Merge tag 'pull-24apr19' of git://git.denx.de/u-boot-dm
[oweals/u-boot.git] / drivers / clk / meson / g12a.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2018 - Beniamino Galvani <b.galvani@gmail.com>
4  * (C) Copyright 2018 - BayLibre, SAS
5  * Author: Neil Armstrong <narmstrong@baylibre.com>
6  */
7
8 #include <common.h>
9 #include <asm/arch/clock-g12a.h>
10 #include <asm/io.h>
11 #include <clk-uclass.h>
12 #include <dm.h>
13 #include <regmap.h>
14 #include <syscon.h>
15 #include <div64.h>
16 #include <dt-bindings/clock/g12a-clkc.h>
17 #include "clk_meson.h"
18
19 #define XTAL_RATE 24000000
20
21 struct meson_clk {
22         struct regmap *map;
23 };
24
25 static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id);
26
27 #define NUM_CLKS 178
28
29 static struct meson_gate gates[NUM_CLKS] = {
30         /* Everything Else (EE) domain gates */
31         MESON_GATE(CLKID_SPICC0, HHI_GCLK_MPEG0, 8),
32         MESON_GATE(CLKID_I2C, HHI_GCLK_MPEG0, 9),
33         MESON_GATE(CLKID_UART0, HHI_GCLK_MPEG0, 13),
34         MESON_GATE(CLKID_SPICC1, HHI_GCLK_MPEG0, 14),
35         MESON_GATE(CLKID_SD_EMMC_B, HHI_GCLK_MPEG0, 25),
36         MESON_GATE(CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26),
37         MESON_GATE(CLKID_ETH, HHI_GCLK_MPEG1, 3),
38         MESON_GATE(CLKID_UART1, HHI_GCLK_MPEG1, 16),
39
40         /* Peripheral Gates */
41         MESON_GATE(CLKID_SD_EMMC_B_CLK0, HHI_SD_EMMC_CLK_CNTL, 23),
42         MESON_GATE(CLKID_SD_EMMC_C_CLK0, HHI_NAND_CLK_CNTL, 7),
43 };
44
45 static int meson_set_gate(struct clk *clk, bool on)
46 {
47         struct meson_clk *priv = dev_get_priv(clk->dev);
48         struct meson_gate *gate;
49
50         if (clk->id >= ARRAY_SIZE(gates))
51                 return -ENOENT;
52
53         gate = &gates[clk->id];
54
55         if (gate->reg == 0)
56                 return 0;
57
58         regmap_update_bits(priv->map, gate->reg,
59                            BIT(gate->bit), on ? BIT(gate->bit) : 0);
60
61         return 0;
62 }
63
64 static int meson_clk_enable(struct clk *clk)
65 {
66         return meson_set_gate(clk, true);
67 }
68
69 static int meson_clk_disable(struct clk *clk)
70 {
71         return meson_set_gate(clk, false);
72 }
73
74 static unsigned long meson_clk81_get_rate(struct clk *clk)
75 {
76         struct meson_clk *priv = dev_get_priv(clk->dev);
77         unsigned long parent_rate;
78         uint reg;
79         int parents[] = {
80                 -1,
81                 -1,
82                 CLKID_FCLK_DIV7,
83                 CLKID_MPLL1,
84                 CLKID_MPLL2,
85                 CLKID_FCLK_DIV4,
86                 CLKID_FCLK_DIV3,
87                 CLKID_FCLK_DIV5
88         };
89
90         /* mux */
91         regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
92         reg = (reg >> 12) & 7;
93
94         switch (reg) {
95         case 0:
96                 parent_rate = XTAL_RATE;
97                 break;
98         case 1:
99                 return -ENOENT;
100         default:
101                 parent_rate = meson_clk_get_rate_by_id(clk, parents[reg]);
102         }
103
104         /* divider */
105         regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
106         reg = reg & ((1 << 7) - 1);
107
108         return parent_rate / reg;
109 }
110
111 static long mpll_rate_from_params(unsigned long parent_rate,
112                                   unsigned long sdm,
113                                   unsigned long n2)
114 {
115         unsigned long divisor = (SDM_DEN * n2) + sdm;
116
117         if (n2 < N2_MIN)
118                 return -EINVAL;
119
120         return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor);
121 }
122
123 static struct parm meson_mpll0_parm[2] = {
124         {HHI_MPLL_CNTL1, 0, 14}, /* psdm */
125         {HHI_MPLL_CNTL1, 20, 9}, /* pn2 */
126 };
127
128 static struct parm meson_mpll1_parm[2] = {
129         {HHI_MPLL_CNTL3, 0, 14}, /* psdm */
130         {HHI_MPLL_CNTL3, 20, 9}, /* pn2 */
131 };
132
133 static struct parm meson_mpll2_parm[2] = {
134         {HHI_MPLL_CNTL5, 0, 14}, /* psdm */
135         {HHI_MPLL_CNTL5, 20, 9}, /* pn2 */
136 };
137
138 /*
139  * MultiPhase Locked Loops are outputs from a PLL with additional frequency
140  * scaling capabilities. MPLL rates are calculated as:
141  *
142  * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384)
143  */
144 static ulong meson_mpll_get_rate(struct clk *clk, unsigned long id)
145 {
146         struct meson_clk *priv = dev_get_priv(clk->dev);
147         struct parm *psdm, *pn2;
148         unsigned long sdm, n2;
149         unsigned long parent_rate;
150         uint reg;
151
152         switch (id) {
153         case CLKID_MPLL0:
154                 psdm = &meson_mpll0_parm[0];
155                 pn2 = &meson_mpll0_parm[1];
156                 break;
157         case CLKID_MPLL1:
158                 psdm = &meson_mpll1_parm[0];
159                 pn2 = &meson_mpll1_parm[1];
160                 break;
161         case CLKID_MPLL2:
162                 psdm = &meson_mpll2_parm[0];
163                 pn2 = &meson_mpll2_parm[1];
164                 break;
165         default:
166                 return -ENOENT;
167         }
168
169         parent_rate = meson_clk_get_rate_by_id(clk, CLKID_FIXED_PLL);
170         if (IS_ERR_VALUE(parent_rate))
171                 return parent_rate;
172
173         regmap_read(priv->map, psdm->reg_off, &reg);
174         sdm = PARM_GET(psdm->width, psdm->shift, reg);
175
176         regmap_read(priv->map, pn2->reg_off, &reg);
177         n2 = PARM_GET(pn2->width, pn2->shift, reg);
178
179         return mpll_rate_from_params(parent_rate, sdm, n2);
180 }
181
182 static struct parm meson_fixed_pll_parm[3] = {
183         {HHI_FIX_PLL_CNTL0, 0, 8}, /* pm */
184         {HHI_FIX_PLL_CNTL0, 10, 5}, /* pn */
185         {HHI_FIX_PLL_CNTL0, 16, 2}, /* pod */
186 };
187
188 static struct parm meson_sys_pll_parm[3] = {
189         {HHI_SYS_PLL_CNTL0, 0, 8}, /* pm */
190         {HHI_SYS_PLL_CNTL0, 10, 5}, /* pn */
191         {HHI_SYS_PLL_CNTL0, 16, 2}, /* pod */
192 };
193
194 static ulong meson_pll_get_rate(struct clk *clk, unsigned long id)
195 {
196         struct meson_clk *priv = dev_get_priv(clk->dev);
197         struct parm *pm, *pn, *pod;
198         unsigned long parent_rate_mhz = XTAL_RATE / 1000000;
199         u16 n, m, od;
200         uint reg;
201
202         /*
203          * FIXME: Between the unit conversion and the missing frac, we know
204          * rate will be slightly off ...
205         */
206
207         switch (id) {
208         case CLKID_FIXED_PLL:
209                 pm = &meson_fixed_pll_parm[0];
210                 pn = &meson_fixed_pll_parm[1];
211                 pod = &meson_fixed_pll_parm[2];
212                 break;
213         case CLKID_SYS_PLL:
214                 pm = &meson_sys_pll_parm[0];
215                 pn = &meson_sys_pll_parm[1];
216                 pod = &meson_sys_pll_parm[2];
217                 break;
218         default:
219                 return -ENOENT;
220         }
221
222         regmap_read(priv->map, pn->reg_off, &reg);
223         n = PARM_GET(pn->width, pn->shift, reg);
224
225         regmap_read(priv->map, pm->reg_off, &reg);
226         m = PARM_GET(pm->width, pm->shift, reg);
227
228         regmap_read(priv->map, pod->reg_off, &reg);
229         od = PARM_GET(pod->width, pod->shift, reg);
230
231         return ((parent_rate_mhz * m / n) >> od) * 1000000;
232 }
233
234 static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id)
235 {
236         ulong rate;
237
238         switch (id) {
239         case CLKID_FIXED_PLL:
240         case CLKID_SYS_PLL:
241                 rate = meson_pll_get_rate(clk, id);
242                 break;
243         case CLKID_FCLK_DIV2:
244                 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 2;
245                 break;
246         case CLKID_FCLK_DIV3:
247                 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 3;
248                 break;
249         case CLKID_FCLK_DIV4:
250                 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 4;
251                 break;
252         case CLKID_FCLK_DIV5:
253                 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 5;
254                 break;
255         case CLKID_FCLK_DIV7:
256                 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 7;
257                 break;
258         case CLKID_MPLL0:
259         case CLKID_MPLL1:
260         case CLKID_MPLL2:
261                 rate = meson_mpll_get_rate(clk, id);
262                 break;
263         case CLKID_CLK81:
264                 rate = meson_clk81_get_rate(clk);
265                 break;
266         default:
267                 if (gates[id].reg != 0) {
268                         /* a clock gate */
269                         rate = meson_clk81_get_rate(clk);
270                         break;
271                 }
272                 return -ENOENT;
273         }
274
275         debug("clock %lu has rate %lu\n", id, rate);
276         return rate;
277 }
278
279 static ulong meson_clk_get_rate(struct clk *clk)
280 {
281         return meson_clk_get_rate_by_id(clk, clk->id);
282 }
283
284 static int meson_clk_probe(struct udevice *dev)
285 {
286         struct meson_clk *priv = dev_get_priv(dev);
287
288         priv->map = syscon_node_to_regmap(dev_get_parent(dev)->node);
289         if (IS_ERR(priv->map))
290                 return PTR_ERR(priv->map);
291
292         debug("meson-clk-g12a: probed\n");
293
294         return 0;
295 }
296
297 static struct clk_ops meson_clk_ops = {
298         .disable        = meson_clk_disable,
299         .enable         = meson_clk_enable,
300         .get_rate       = meson_clk_get_rate,
301 };
302
303 static const struct udevice_id meson_clk_ids[] = {
304         { .compatible = "amlogic,g12a-clkc" },
305         { }
306 };
307
308 U_BOOT_DRIVER(meson_clk_g12a) = {
309         .name           = "meson_clk_g12a",
310         .id             = UCLASS_CLK,
311         .of_match       = meson_clk_ids,
312         .priv_auto_alloc_size = sizeof(struct meson_clk),
313         .ops            = &meson_clk_ops,
314         .probe          = meson_clk_probe,
315 };