blob: 2e0e7bbe68ffaa5fa4b67d7ea52bb0912d168d8c [file] [log] [blame]
Liviu Dudauba024e62018-09-17 17:50:00 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2018 Arm Ltd
4 * Author: Liviu Dudau <liviu.dudau@foss.arm.com>
5 *
6 */
7#define DEBUG
Liviu Dudauba024e62018-09-17 17:50:00 +01008#include <clk-uclass.h>
9#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070011#include <dm/device_compat.h>
Liviu Dudauba024e62018-09-17 17:50:00 +010012#include <dm/lists.h>
13#include <errno.h>
14#include <misc.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060015#include <linux/bitops.h>
Liviu Dudauba024e62018-09-17 17:50:00 +010016
17#define CLK_FUNCTION BIT(20)
18
19struct vexpress_osc_clk_priv {
20 u8 osc;
21 ulong rate_min;
22 ulong rate_max;
23};
24
25static ulong vexpress_osc_clk_get_rate(struct clk *clk)
26{
27 int err;
28 u32 data;
29 struct udevice *vexpress_cfg = dev_get_parent(clk->dev);
30 struct vexpress_osc_clk_priv *priv = dev_get_priv(clk->dev);
31
32 data = CLK_FUNCTION | priv->osc;
33 err = misc_read(vexpress_cfg, 0, &data, sizeof(data));
Simon Glass587dc402018-11-06 15:21:39 -070034 if (err < 0)
Liviu Dudauba024e62018-09-17 17:50:00 +010035 return err;
36
37 return data;
38}
39
40#ifndef CONFIG_SPL_BUILD
41static ulong vexpress_osc_clk_set_rate(struct clk *clk, ulong rate)
42{
43 int err;
44 u32 buffer[2];
45 struct udevice *vexpress_cfg = dev_get_parent(clk->dev);
46 struct vexpress_osc_clk_priv *priv = dev_get_priv(clk->dev);
47
48 if (rate < priv->rate_min || rate > priv->rate_max)
49 return -EINVAL;
50
51 /*
52 * we are sending the parent the info about the oscillator
53 * and the value we want to set
54 */
55 buffer[0] = CLK_FUNCTION | priv->osc;
56 buffer[1] = rate;
57 err = misc_write(vexpress_cfg, 0, buffer, 2 * sizeof(u32));
Simon Glass587dc402018-11-06 15:21:39 -070058 if (err < 0)
Liviu Dudauba024e62018-09-17 17:50:00 +010059 return err;
60
61 return rate;
62}
63#endif
64
65static struct clk_ops vexpress_osc_clk_ops = {
66 .get_rate = vexpress_osc_clk_get_rate,
67#ifndef CONFIG_SPL_BUILD
68 .set_rate = vexpress_osc_clk_set_rate,
69#endif
70};
71
72static int vexpress_osc_clk_probe(struct udevice *dev)
73{
74 struct vexpress_osc_clk_priv *priv = dev_get_priv(dev);
75 u32 values[2];
76 int err;
77
78 err = dev_read_u32_array(dev, "freq-range", values, 2);
79 if (err)
80 return err;
81 priv->rate_min = values[0];
82 priv->rate_max = values[1];
83
84 err = dev_read_u32_array(dev, "arm,vexpress-sysreg,func", values, 2);
85 if (err)
86 return err;
87
88 if (values[0] != 1) {
89 dev_err(dev, "Invalid VExpress function for clock, must be '1'");
90 return -EINVAL;
91 }
92 priv->osc = values[1];
93 debug("clk \"%s%d\", min freq %luHz, max freq %luHz\n", dev->name,
94 priv->osc, priv->rate_min, priv->rate_max);
95
96 return 0;
97}
98
99static const struct udevice_id vexpress_osc_clk_ids[] = {
100 { .compatible = "arm,vexpress-osc", },
101 {}
102};
103
104U_BOOT_DRIVER(vexpress_osc_clk) = {
105 .name = "vexpress_osc_clk",
106 .id = UCLASS_CLK,
107 .of_match = vexpress_osc_clk_ids,
108 .ops = &vexpress_osc_clk_ops,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700109 .priv_auto = sizeof(struct vexpress_osc_clk_priv),
Liviu Dudauba024e62018-09-17 17:50:00 +0100110 .probe = vexpress_osc_clk_probe,
111};