blob: 1e22a50910ca7a849ef0cb04abf8432f385252da [file] [log] [blame]
Dario Binacchid09f0632020-12-30 00:06:32 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * TI multiplexer clock support
4 *
5 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
6 *
7 * Based on Linux kernel drivers/clk/ti/mux.c
8 */
9
10#include <common.h>
11#include <dm.h>
12#include <dm/device_compat.h>
13#include <clk-uclass.h>
14#include <asm/io.h>
15#include <linux/clk-provider.h>
16
17struct clk_ti_mux_priv {
18 struct clk_bulk parents;
19 fdt_addr_t reg;
20 u32 flags;
21 u32 mux_flags;
22 u32 mask;
23 u32 shift;
24 s32 latch;
25};
26
27static void clk_ti_mux_rmw(u32 val, u32 mask, fdt_addr_t reg)
28{
29 u32 v;
30
31 v = readl(reg);
32 v &= ~mask;
33 v |= val;
34 writel(v, reg);
35}
36
37static void clk_ti_mux_latch(fdt_addr_t reg, s8 shift)
38{
39 u32 latch;
40
41 if (shift < 0)
42 return;
43
44 latch = 1 << shift;
45
46 clk_ti_mux_rmw(latch, latch, reg);
47 clk_ti_mux_rmw(0, latch, reg);
48 readl(reg); /* OCP barrier */
49}
50
51static struct clk *clk_ti_mux_get_parent_by_index(struct clk_bulk *parents,
52 int index)
53{
54 if (index < 0 || !parents)
55 return ERR_PTR(-EINVAL);
56
57 if (index >= parents->count)
58 return ERR_PTR(-ENODEV);
59
60 return &parents->clks[index];
61}
62
63static int clk_ti_mux_get_parent_index(struct clk_bulk *parents,
64 struct clk *parent)
65{
66 int i;
67
68 if (!parents || !parent)
69 return -EINVAL;
70
71 for (i = 0; i < parents->count; i++) {
72 if (parents->clks[i].dev == parent->dev)
73 return i;
74 }
75
76 return -ENODEV;
77}
78
79static int clk_ti_mux_get_index(struct clk *clk)
80{
81 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
82 u32 val;
83
84 val = readl(priv->reg);
85 val >>= priv->shift;
86 val &= priv->mask;
87
88 if (val && (priv->flags & CLK_MUX_INDEX_BIT))
89 val = ffs(val) - 1;
90
91 if (val && (priv->flags & CLK_MUX_INDEX_ONE))
92 val--;
93
94 if (val >= priv->parents.count)
95 return -EINVAL;
96
97 return val;
98}
99
100static int clk_ti_mux_set_parent(struct clk *clk, struct clk *parent)
101{
102 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
103 int index;
104 u32 val;
105
106 index = clk_ti_mux_get_parent_index(&priv->parents, parent);
107 if (index < 0) {
108 dev_err(clk->dev, "failed to get parent clock\n");
109 return index;
110 }
111
112 index = clk_mux_index_to_val(NULL, priv->flags, index);
113
114 if (priv->flags & CLK_MUX_HIWORD_MASK) {
115 val = priv->mask << (priv->shift + 16);
116 } else {
117 val = readl(priv->reg);
118 val &= ~(priv->mask << priv->shift);
119 }
120
121 val |= index << priv->shift;
122 writel(val, priv->reg);
123 clk_ti_mux_latch(priv->reg, priv->latch);
124 return 0;
125}
126
127static ulong clk_ti_mux_set_rate(struct clk *clk, ulong rate)
128{
129 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
130 struct clk *parent;
131 int index;
132
133 if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
134 return -ENOSYS;
135
136 index = clk_ti_mux_get_index(clk);
137 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
138 if (IS_ERR(parent))
139 return PTR_ERR(parent);
140
141 rate = clk_set_rate(parent, rate);
142 dev_dbg(clk->dev, "rate=%ld\n", rate);
143 return rate;
144}
145
146static ulong clk_ti_mux_get_rate(struct clk *clk)
147{
148 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
149 int index;
150 struct clk *parent;
151 ulong rate;
152
153 index = clk_ti_mux_get_index(clk);
154 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
155 if (IS_ERR(parent))
156 return PTR_ERR(parent);
157
158 rate = clk_get_rate(parent);
159 dev_dbg(clk->dev, "rate=%ld\n", rate);
160 return rate;
161}
162
163static ulong clk_ti_mux_round_rate(struct clk *clk, ulong rate)
164{
165 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
166 struct clk *parent;
167 int index;
168
169 if ((clk->flags & CLK_SET_RATE_PARENT) == 0)
170 return -ENOSYS;
171
172 index = clk_ti_mux_get_index(clk);
173 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
174 if (IS_ERR(parent))
175 return PTR_ERR(parent);
176
177 rate = clk_round_rate(parent, rate);
178 dev_dbg(clk->dev, "rate=%ld\n", rate);
179 return rate;
180}
181
182static int clk_ti_mux_request(struct clk *clk)
183{
184 struct clk_ti_mux_priv *priv = dev_get_priv(clk->dev);
185 struct clk *parent;
186 int index;
187
188 clk->flags = priv->flags;
189
190 index = clk_ti_mux_get_index(clk);
191 parent = clk_ti_mux_get_parent_by_index(&priv->parents, index);
192 if (IS_ERR(parent))
193 return PTR_ERR(parent);
194
195 return clk_ti_mux_set_parent(clk, parent);
196}
197
198static struct clk_ops clk_ti_mux_ops = {
199 .request = clk_ti_mux_request,
200 .round_rate = clk_ti_mux_round_rate,
201 .get_rate = clk_ti_mux_get_rate,
202 .set_rate = clk_ti_mux_set_rate,
203 .set_parent = clk_ti_mux_set_parent,
204};
205
206static int clk_ti_mux_remove(struct udevice *dev)
207{
208 struct clk_ti_mux_priv *priv = dev_get_priv(dev);
209 int err;
210
211 err = clk_release_all(priv->parents.clks, priv->parents.count);
212 if (err)
213 dev_dbg(dev, "could not release all parents' clocks\n");
214
215 return err;
216}
217
218static int clk_ti_mux_probe(struct udevice *dev)
219{
220 struct clk_ti_mux_priv *priv = dev_get_priv(dev);
221 int err;
222
223 err = clk_get_bulk(dev, &priv->parents);
224 if (err || priv->parents.count < 2) {
225 dev_err(dev, "mux-clock must have parents\n");
226 return err ? err : -EFAULT;
227 }
228
229 /* Generate bit-mask based on parents info */
230 priv->mask = priv->parents.count;
231 if (!(priv->mux_flags & CLK_MUX_INDEX_ONE))
232 priv->mask--;
233
234 priv->mask = (1 << fls(priv->mask)) - 1;
235 return 0;
236}
237
238static int clk_ti_mux_of_to_plat(struct udevice *dev)
239{
240 struct clk_ti_mux_priv *priv = dev_get_priv(dev);
241
242 priv->reg = dev_read_addr(dev);
243 if (priv->reg == FDT_ADDR_T_NONE) {
244 dev_err(dev, "failed to get register\n");
245 return -EINVAL;
246 }
247
248 dev_dbg(dev, "reg=0x%08lx\n", priv->reg);
249 priv->shift = dev_read_u32_default(dev, "ti,bit-shift", 0);
250 priv->latch = dev_read_s32_default(dev, "ti,latch-bit", -EINVAL);
251
252 priv->flags = CLK_SET_RATE_NO_REPARENT;
253 if (dev_read_bool(dev, "ti,set-rate-parent"))
254 priv->flags |= CLK_SET_RATE_PARENT;
255
256 if (dev_read_bool(dev, "ti,index-starts-at-one"))
257 priv->mux_flags |= CLK_MUX_INDEX_ONE;
258
259 return 0;
260}
261
262static const struct udevice_id clk_ti_mux_of_match[] = {
263 {.compatible = "ti,mux-clock"},
264 {},
265};
266
267U_BOOT_DRIVER(clk_ti_mux) = {
268 .name = "ti_mux_clock",
269 .id = UCLASS_CLK,
270 .of_match = clk_ti_mux_of_match,
271 .ofdata_to_platdata = clk_ti_mux_of_to_plat,
272 .probe = clk_ti_mux_probe,
273 .remove = clk_ti_mux_remove,
274 .priv_auto = sizeof(struct clk_ti_mux_priv),
275 .ops = &clk_ti_mux_ops,
276};