]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
48264d9b | 2 | /* |
d6c7ee7d | 3 | * Copyright (C) 2016-2017 Socionext Inc. |
4e3d8406 | 4 | * Author: Masahiro Yamada <yamada.masahiro@socionext.com> |
48264d9b MY |
5 | */ |
6 | ||
d678a59d | 7 | #include <common.h> |
805dc44c | 8 | #include <clk-uclass.h> |
9d922450 | 9 | #include <dm.h> |
336d4615 | 10 | #include <dm/device_compat.h> |
48264d9b | 11 | #include <linux/bitops.h> |
eb41d8a1 | 12 | #include <linux/bug.h> |
48264d9b | 13 | #include <linux/io.h> |
45a3b1fd | 14 | #include <linux/sizes.h> |
48264d9b MY |
15 | |
16 | #include "clk-uniphier.h" | |
17 | ||
102e3187 MY |
18 | /** |
19 | * struct uniphier_clk_priv - private data for UniPhier clock driver | |
20 | * | |
21 | * @base: base address of the clock provider | |
805dc44c | 22 | * @data: SoC specific data |
102e3187 MY |
23 | */ |
24 | struct uniphier_clk_priv { | |
d6c7ee7d | 25 | struct udevice *dev; |
102e3187 | 26 | void __iomem *base; |
805dc44c | 27 | const struct uniphier_clk_data *data; |
102e3187 MY |
28 | }; |
29 | ||
d6c7ee7d MY |
30 | static void uniphier_clk_gate_enable(struct uniphier_clk_priv *priv, |
31 | const struct uniphier_clk_gate_data *gate) | |
102e3187 | 32 | { |
d6c7ee7d | 33 | u32 val; |
102e3187 | 34 | |
d6c7ee7d MY |
35 | val = readl(priv->base + gate->reg); |
36 | val |= BIT(gate->bit); | |
37 | writel(val, priv->base + gate->reg); | |
38 | } | |
102e3187 | 39 | |
d6c7ee7d MY |
40 | static void uniphier_clk_mux_set_parent(struct uniphier_clk_priv *priv, |
41 | const struct uniphier_clk_mux_data *mux, | |
42 | u8 id) | |
43 | { | |
44 | u32 val; | |
45 | int i; | |
102e3187 | 46 | |
d6c7ee7d MY |
47 | for (i = 0; i < mux->num_parents; i++) { |
48 | if (mux->parent_ids[i] != id) | |
49 | continue; | |
102e3187 | 50 | |
d6c7ee7d MY |
51 | val = readl(priv->base + mux->reg); |
52 | val &= ~mux->masks[i]; | |
53 | val |= mux->vals[i]; | |
54 | writel(val, priv->base + mux->reg); | |
55 | return; | |
805dc44c MY |
56 | } |
57 | ||
d6c7ee7d | 58 | WARN_ON(1); |
102e3187 MY |
59 | } |
60 | ||
d6c7ee7d MY |
61 | static u8 uniphier_clk_mux_get_parent(struct uniphier_clk_priv *priv, |
62 | const struct uniphier_clk_mux_data *mux) | |
48264d9b | 63 | { |
d6c7ee7d MY |
64 | u32 val; |
65 | int i; | |
48264d9b | 66 | |
d6c7ee7d MY |
67 | val = readl(priv->base + mux->reg); |
68 | ||
69 | for (i = 0; i < mux->num_parents; i++) | |
70 | if ((mux->masks[i] & val) == mux->vals[i]) | |
71 | return mux->parent_ids[i]; | |
72 | ||
73 | dev_err(priv->dev, "invalid mux setting\n"); | |
74 | ||
75 | return UNIPHIER_CLK_ID_INVALID; | |
76 | } | |
77 | ||
78 | static const struct uniphier_clk_data *uniphier_clk_get_data( | |
79 | struct uniphier_clk_priv *priv, u8 id) | |
80 | { | |
81 | const struct uniphier_clk_data *data; | |
82 | ||
83 | for (data = priv->data; data->type != UNIPHIER_CLK_TYPE_END; data++) | |
84 | if (data->id == id) | |
85 | return data; | |
86 | ||
87 | dev_err(priv->dev, "id=%u not found\n", id); | |
48264d9b | 88 | |
805dc44c | 89 | return NULL; |
48264d9b MY |
90 | } |
91 | ||
d6c7ee7d MY |
92 | static const struct uniphier_clk_data *uniphier_clk_get_parent_data( |
93 | struct uniphier_clk_priv *priv, | |
94 | const struct uniphier_clk_data *data) | |
48264d9b | 95 | { |
d6c7ee7d MY |
96 | const struct uniphier_clk_data *parent_data; |
97 | u8 parent_id = UNIPHIER_CLK_ID_INVALID; | |
98 | ||
99 | switch (data->type) { | |
100 | case UNIPHIER_CLK_TYPE_GATE: | |
101 | parent_id = data->data.gate.parent_id; | |
102 | break; | |
103 | case UNIPHIER_CLK_TYPE_MUX: | |
104 | parent_id = uniphier_clk_mux_get_parent(priv, &data->data.mux); | |
105 | break; | |
106 | default: | |
107 | break; | |
108 | } | |
48264d9b | 109 | |
d6c7ee7d MY |
110 | if (parent_id == UNIPHIER_CLK_ID_INVALID) |
111 | return NULL; | |
48264d9b | 112 | |
d6c7ee7d | 113 | parent_data = uniphier_clk_get_data(priv, parent_id); |
805dc44c | 114 | |
d6c7ee7d | 115 | WARN_ON(!parent_data); |
48264d9b | 116 | |
d6c7ee7d MY |
117 | return parent_data; |
118 | } | |
119 | ||
120 | static void __uniphier_clk_enable(struct uniphier_clk_priv *priv, | |
121 | const struct uniphier_clk_data *data) | |
122 | { | |
123 | const struct uniphier_clk_data *parent_data; | |
48264d9b | 124 | |
d6c7ee7d MY |
125 | if (data->type == UNIPHIER_CLK_TYPE_GATE) |
126 | uniphier_clk_gate_enable(priv, &data->data.gate); | |
127 | ||
128 | parent_data = uniphier_clk_get_parent_data(priv, data); | |
129 | if (!parent_data) | |
130 | return; | |
131 | ||
132 | return __uniphier_clk_enable(priv, parent_data); | |
48264d9b MY |
133 | } |
134 | ||
d6c7ee7d | 135 | static int uniphier_clk_enable(struct clk *clk) |
48264d9b | 136 | { |
135aa950 | 137 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); |
d6c7ee7d MY |
138 | const struct uniphier_clk_data *data; |
139 | ||
140 | data = uniphier_clk_get_data(priv, clk->id); | |
141 | if (!data) | |
142 | return -ENODEV; | |
143 | ||
144 | __uniphier_clk_enable(priv, data); | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | static unsigned long __uniphier_clk_get_rate( | |
150 | struct uniphier_clk_priv *priv, | |
151 | const struct uniphier_clk_data *data) | |
152 | { | |
153 | const struct uniphier_clk_data *parent_data; | |
48264d9b | 154 | |
d6c7ee7d MY |
155 | if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE) |
156 | return data->data.rate.fixed_rate; | |
157 | ||
158 | parent_data = uniphier_clk_get_parent_data(priv, data); | |
159 | if (!parent_data) | |
805dc44c | 160 | return 0; |
48264d9b | 161 | |
d6c7ee7d MY |
162 | return __uniphier_clk_get_rate(priv, parent_data); |
163 | } | |
48264d9b | 164 | |
d6c7ee7d MY |
165 | static unsigned long uniphier_clk_get_rate(struct clk *clk) |
166 | { | |
167 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); | |
168 | const struct uniphier_clk_data *data; | |
169 | ||
170 | data = uniphier_clk_get_data(priv, clk->id); | |
171 | if (!data) | |
172 | return -ENODEV; | |
173 | ||
174 | return __uniphier_clk_get_rate(priv, data); | |
175 | } | |
176 | ||
177 | static unsigned long __uniphier_clk_set_rate( | |
178 | struct uniphier_clk_priv *priv, | |
179 | const struct uniphier_clk_data *data, | |
180 | unsigned long rate, bool set) | |
181 | { | |
182 | const struct uniphier_clk_data *best_parent_data = NULL; | |
183 | const struct uniphier_clk_data *parent_data; | |
184 | unsigned long best_rate = 0; | |
185 | unsigned long parent_rate; | |
186 | u8 parent_id; | |
187 | int i; | |
188 | ||
189 | if (data->type == UNIPHIER_CLK_TYPE_FIXED_RATE) | |
190 | return data->data.rate.fixed_rate; | |
191 | ||
192 | if (data->type == UNIPHIER_CLK_TYPE_GATE) { | |
193 | parent_data = uniphier_clk_get_parent_data(priv, data); | |
194 | if (!parent_data) | |
195 | return 0; | |
196 | ||
197 | return __uniphier_clk_set_rate(priv, parent_data, rate, set); | |
198 | } | |
199 | ||
200 | if (WARN_ON(data->type != UNIPHIER_CLK_TYPE_MUX)) | |
201 | return -EINVAL; | |
202 | ||
203 | for (i = 0; i < data->data.mux.num_parents; i++) { | |
204 | parent_id = data->data.mux.parent_ids[i]; | |
205 | parent_data = uniphier_clk_get_data(priv, parent_id); | |
206 | if (WARN_ON(!parent_data)) | |
207 | return -EINVAL; | |
208 | ||
209 | parent_rate = __uniphier_clk_set_rate(priv, parent_data, rate, | |
210 | false); | |
211 | ||
212 | if (parent_rate <= rate && best_rate < parent_rate) { | |
213 | best_rate = parent_rate; | |
214 | best_parent_data = parent_data; | |
805dc44c | 215 | } |
48264d9b MY |
216 | } |
217 | ||
d6c7ee7d MY |
218 | dev_dbg(priv->dev, "id=%u, best_rate=%lu\n", data->id, best_rate); |
219 | ||
220 | if (!best_parent_data) | |
805dc44c MY |
221 | return -EINVAL; |
222 | ||
d6c7ee7d MY |
223 | if (!set) |
224 | return best_rate; | |
225 | ||
226 | uniphier_clk_mux_set_parent(priv, &data->data.mux, | |
227 | best_parent_data->id); | |
228 | ||
229 | return best_rate = __uniphier_clk_set_rate(priv, best_parent_data, | |
230 | rate, true); | |
231 | } | |
232 | ||
233 | static unsigned long uniphier_clk_set_rate(struct clk *clk, ulong rate) | |
234 | { | |
235 | struct uniphier_clk_priv *priv = dev_get_priv(clk->dev); | |
236 | const struct uniphier_clk_data *data; | |
48264d9b | 237 | |
d6c7ee7d MY |
238 | data = uniphier_clk_get_data(priv, clk->id); |
239 | if (!data) | |
240 | return -ENODEV; | |
48264d9b | 241 | |
d6c7ee7d | 242 | return __uniphier_clk_set_rate(priv, data, rate, true); |
48264d9b MY |
243 | } |
244 | ||
1d21e1b9 | 245 | static const struct clk_ops uniphier_clk_ops = { |
48264d9b | 246 | .enable = uniphier_clk_enable, |
135aa950 SW |
247 | .get_rate = uniphier_clk_get_rate, |
248 | .set_rate = uniphier_clk_set_rate, | |
48264d9b MY |
249 | }; |
250 | ||
805dc44c MY |
251 | static int uniphier_clk_probe(struct udevice *dev) |
252 | { | |
253 | struct uniphier_clk_priv *priv = dev_get_priv(dev); | |
254 | fdt_addr_t addr; | |
255 | ||
2548493a | 256 | addr = dev_read_addr(dev->parent); |
805dc44c MY |
257 | if (addr == FDT_ADDR_T_NONE) |
258 | return -EINVAL; | |
259 | ||
260 | priv->base = devm_ioremap(dev, addr, SZ_4K); | |
261 | if (!priv->base) | |
262 | return -ENOMEM; | |
263 | ||
d6c7ee7d | 264 | priv->dev = dev; |
805dc44c MY |
265 | priv->data = (void *)dev_get_driver_data(dev); |
266 | ||
267 | return 0; | |
268 | } | |
269 | ||
102e3187 | 270 | static const struct udevice_id uniphier_clk_match[] = { |
d7505752 MY |
271 | /* System clock */ |
272 | { | |
273 | .compatible = "socionext,uniphier-ld4-clock", | |
d6c7ee7d | 274 | .data = (ulong)uniphier_pxs2_sys_clk_data, |
d7505752 MY |
275 | }, |
276 | { | |
277 | .compatible = "socionext,uniphier-pro4-clock", | |
d6c7ee7d | 278 | .data = (ulong)uniphier_pxs2_sys_clk_data, |
d7505752 MY |
279 | }, |
280 | { | |
281 | .compatible = "socionext,uniphier-sld8-clock", | |
d6c7ee7d | 282 | .data = (ulong)uniphier_pxs2_sys_clk_data, |
d7505752 MY |
283 | }, |
284 | { | |
285 | .compatible = "socionext,uniphier-pro5-clock", | |
d6c7ee7d | 286 | .data = (ulong)uniphier_pxs2_sys_clk_data, |
d7505752 MY |
287 | }, |
288 | { | |
289 | .compatible = "socionext,uniphier-pxs2-clock", | |
d6c7ee7d | 290 | .data = (ulong)uniphier_pxs2_sys_clk_data, |
d7505752 MY |
291 | }, |
292 | { | |
293 | .compatible = "socionext,uniphier-ld11-clock", | |
d6c7ee7d | 294 | .data = (ulong)uniphier_ld20_sys_clk_data, |
d7505752 MY |
295 | }, |
296 | { | |
297 | .compatible = "socionext,uniphier-ld20-clock", | |
d6c7ee7d | 298 | .data = (ulong)uniphier_ld20_sys_clk_data, |
d7505752 | 299 | }, |
ab05406e MY |
300 | { |
301 | .compatible = "socionext,uniphier-pxs3-clock", | |
302 | .data = (ulong)uniphier_pxs3_sys_clk_data, | |
303 | }, | |
d7505752 | 304 | /* Media I/O clock */ |
102e3187 | 305 | { |
6dc5b6b1 | 306 | .compatible = "socionext,uniphier-ld4-mio-clock", |
d6c7ee7d | 307 | .data = (ulong)uniphier_mio_clk_data, |
102e3187 MY |
308 | }, |
309 | { | |
6dc5b6b1 | 310 | .compatible = "socionext,uniphier-pro4-mio-clock", |
d6c7ee7d | 311 | .data = (ulong)uniphier_mio_clk_data, |
102e3187 MY |
312 | }, |
313 | { | |
6dc5b6b1 | 314 | .compatible = "socionext,uniphier-sld8-mio-clock", |
d6c7ee7d | 315 | .data = (ulong)uniphier_mio_clk_data, |
102e3187 MY |
316 | }, |
317 | { | |
4c642e68 | 318 | .compatible = "socionext,uniphier-pro5-sd-clock", |
d6c7ee7d | 319 | .data = (ulong)uniphier_mio_clk_data, |
102e3187 MY |
320 | }, |
321 | { | |
4c642e68 | 322 | .compatible = "socionext,uniphier-pxs2-sd-clock", |
d6c7ee7d | 323 | .data = (ulong)uniphier_mio_clk_data, |
102e3187 MY |
324 | }, |
325 | { | |
6dc5b6b1 | 326 | .compatible = "socionext,uniphier-ld11-mio-clock", |
d6c7ee7d | 327 | .data = (ulong)uniphier_mio_clk_data, |
102e3187 MY |
328 | }, |
329 | { | |
4c642e68 | 330 | .compatible = "socionext,uniphier-ld20-sd-clock", |
d6c7ee7d | 331 | .data = (ulong)uniphier_mio_clk_data, |
102e3187 | 332 | }, |
ab05406e MY |
333 | { |
334 | .compatible = "socionext,uniphier-pxs3-sd-clock", | |
335 | .data = (ulong)uniphier_mio_clk_data, | |
336 | }, | |
102e3187 MY |
337 | { /* sentinel */ } |
338 | }; | |
48264d9b | 339 | |
102e3187 MY |
340 | U_BOOT_DRIVER(uniphier_clk) = { |
341 | .name = "uniphier-clk", | |
342 | .id = UCLASS_CLK, | |
343 | .of_match = uniphier_clk_match, | |
344 | .probe = uniphier_clk_probe, | |
41575d8e | 345 | .priv_auto = sizeof(struct uniphier_clk_priv), |
102e3187 MY |
346 | .ops = &uniphier_clk_ops, |
347 | }; |