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