]>
Commit | Line | Data |
---|---|---|
a71e907c LD |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Copyright (C) 2018 Arm Ltd | |
4 | * Author: Liviu Dudau <[email protected]> | |
5 | * | |
6 | */ | |
7 | #define DEBUG | |
a71e907c LD |
8 | #include <clk-uclass.h> |
9 | #include <dm.h> | |
f7ae49fc | 10 | #include <log.h> |
336d4615 | 11 | #include <dm/device_compat.h> |
a71e907c LD |
12 | #include <dm/lists.h> |
13 | #include <errno.h> | |
14 | #include <misc.h> | |
cd93d625 | 15 | #include <linux/bitops.h> |
a71e907c LD |
16 | |
17 | #define CLK_FUNCTION BIT(20) | |
18 | ||
19 | struct vexpress_osc_clk_priv { | |
20 | u8 osc; | |
21 | ulong rate_min; | |
22 | ulong rate_max; | |
23 | }; | |
24 | ||
25 | static 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)); | |
8729b1ae | 34 | if (err < 0) |
a71e907c LD |
35 | return err; |
36 | ||
37 | return data; | |
38 | } | |
39 | ||
371dc068 | 40 | #ifndef CONFIG_XPL_BUILD |
a71e907c LD |
41 | static 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)); | |
8729b1ae | 58 | if (err < 0) |
a71e907c LD |
59 | return err; |
60 | ||
61 | return rate; | |
62 | } | |
63 | #endif | |
64 | ||
65 | static struct clk_ops vexpress_osc_clk_ops = { | |
66 | .get_rate = vexpress_osc_clk_get_rate, | |
371dc068 | 67 | #ifndef CONFIG_XPL_BUILD |
a71e907c LD |
68 | .set_rate = vexpress_osc_clk_set_rate, |
69 | #endif | |
70 | }; | |
71 | ||
72 | static 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 | ||
99 | static const struct udevice_id vexpress_osc_clk_ids[] = { | |
100 | { .compatible = "arm,vexpress-osc", }, | |
101 | {} | |
102 | }; | |
103 | ||
104 | U_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, | |
41575d8e | 109 | .priv_auto = sizeof(struct vexpress_osc_clk_priv), |
a71e907c LD |
110 | .probe = vexpress_osc_clk_probe, |
111 | }; |