blob: 4b0028d04bf6c30692e926e6f7e9b08d098e0350 [file] [log] [blame]
Neil Armstrongf6eb68b2018-09-07 17:25:13 +02001// 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-axg.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/axg-clkc.h>
17#include "clk_meson.h"
Simon Glass61b29b82020-02-03 07:36:15 -070018#include <linux/err.h>
Neil Armstrongf6eb68b2018-09-07 17:25:13 +020019
20#define XTAL_RATE 24000000
21
22struct meson_clk {
23 struct regmap *map;
24};
25
26static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id);
27
28static struct meson_gate gates[] = {
29 /* Everything Else (EE) domain gates */
30 MESON_GATE(CLKID_SPICC0, HHI_GCLK_MPEG0, 8),
31 MESON_GATE(CLKID_I2C, HHI_GCLK_MPEG0, 9),
32 MESON_GATE(CLKID_UART0, HHI_GCLK_MPEG0, 13),
33 MESON_GATE(CLKID_SPICC1, HHI_GCLK_MPEG0, 15),
34 MESON_GATE(CLKID_SD_EMMC_B, HHI_GCLK_MPEG0, 25),
35 MESON_GATE(CLKID_SD_EMMC_C, HHI_GCLK_MPEG0, 26),
36 MESON_GATE(CLKID_ETH, HHI_GCLK_MPEG1, 3),
37 MESON_GATE(CLKID_UART1, HHI_GCLK_MPEG1, 16),
38
39 /* Always On (AO) domain gates */
40 MESON_GATE(CLKID_AO_I2C, HHI_GCLK_AO, 4),
41
42 /* PLL Gates */
43 /* CLKID_FCLK_DIV2 is critical for the SCPI Processor */
44 MESON_GATE(CLKID_MPLL2, HHI_MPLL_CNTL9, 14),
45 /* CLKID_CLK81 is critical for the system */
46
47 /* Peripheral Gates */
48 MESON_GATE(CLKID_SD_EMMC_B_CLK0, HHI_SD_EMMC_CLK_CNTL, 23),
49 MESON_GATE(CLKID_SD_EMMC_C_CLK0, HHI_NAND_CLK_CNTL, 7),
50};
51
52static int meson_set_gate(struct clk *clk, bool on)
53{
54 struct meson_clk *priv = dev_get_priv(clk->dev);
55 struct meson_gate *gate;
56
57 if (clk->id >= ARRAY_SIZE(gates))
58 return -ENOENT;
59
60 gate = &gates[clk->id];
61
62 if (gate->reg == 0)
63 return 0;
64
65 regmap_update_bits(priv->map, gate->reg,
66 BIT(gate->bit), on ? BIT(gate->bit) : 0);
67
68 return 0;
69}
70
71static int meson_clk_enable(struct clk *clk)
72{
73 return meson_set_gate(clk, true);
74}
75
76static int meson_clk_disable(struct clk *clk)
77{
78 return meson_set_gate(clk, false);
79}
80
81static unsigned long meson_clk81_get_rate(struct clk *clk)
82{
83 struct meson_clk *priv = dev_get_priv(clk->dev);
84 unsigned long parent_rate;
85 uint reg;
86 int parents[] = {
87 -1,
88 -1,
89 CLKID_FCLK_DIV7,
90 CLKID_MPLL1,
91 CLKID_MPLL2,
92 CLKID_FCLK_DIV4,
93 CLKID_FCLK_DIV3,
94 CLKID_FCLK_DIV5
95 };
96
97 /* mux */
98 regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
99 reg = (reg >> 12) & 7;
100
101 switch (reg) {
102 case 0:
103 parent_rate = XTAL_RATE;
104 break;
105 case 1:
106 return -ENOENT;
107 default:
108 parent_rate = meson_clk_get_rate_by_id(clk, parents[reg]);
109 }
110
111 /* divider */
112 regmap_read(priv->map, HHI_MPEG_CLK_CNTL, &reg);
113 reg = reg & ((1 << 7) - 1);
114
115 return parent_rate / reg;
116}
117
118static long mpll_rate_from_params(unsigned long parent_rate,
119 unsigned long sdm,
120 unsigned long n2)
121{
122 unsigned long divisor = (SDM_DEN * n2) + sdm;
123
124 if (n2 < N2_MIN)
125 return -EINVAL;
126
127 return DIV_ROUND_UP_ULL((u64)parent_rate * SDM_DEN, divisor);
128}
129
130static struct parm meson_mpll0_parm[3] = {
131 {HHI_MPLL_CNTL7, 0, 14}, /* psdm */
132 {HHI_MPLL_CNTL7, 16, 9}, /* pn2 */
133};
134
135static struct parm meson_mpll1_parm[3] = {
136 {HHI_MPLL_CNTL8, 0, 14}, /* psdm */
137 {HHI_MPLL_CNTL8, 16, 9}, /* pn2 */
138};
139
140static struct parm meson_mpll2_parm[3] = {
141 {HHI_MPLL_CNTL9, 0, 14}, /* psdm */
142 {HHI_MPLL_CNTL9, 16, 9}, /* pn2 */
143};
144
145/*
146 * MultiPhase Locked Loops are outputs from a PLL with additional frequency
147 * scaling capabilities. MPLL rates are calculated as:
148 *
149 * f(N2_integer, SDM_IN ) = 2.0G/(N2_integer + SDM_IN/16384)
150 */
151static ulong meson_mpll_get_rate(struct clk *clk, unsigned long id)
152{
153 struct meson_clk *priv = dev_get_priv(clk->dev);
154 struct parm *psdm, *pn2;
155 unsigned long sdm, n2;
156 unsigned long parent_rate;
157 uint reg;
158
159 switch (id) {
160 case CLKID_MPLL0:
161 psdm = &meson_mpll0_parm[0];
162 pn2 = &meson_mpll0_parm[1];
163 break;
164 case CLKID_MPLL1:
165 psdm = &meson_mpll1_parm[0];
166 pn2 = &meson_mpll1_parm[1];
167 break;
168 case CLKID_MPLL2:
169 psdm = &meson_mpll2_parm[0];
170 pn2 = &meson_mpll2_parm[1];
171 break;
172 default:
173 return -ENOENT;
174 }
175
176 parent_rate = meson_clk_get_rate_by_id(clk, CLKID_FIXED_PLL);
177 if (IS_ERR_VALUE(parent_rate))
178 return parent_rate;
179
180 regmap_read(priv->map, psdm->reg_off, &reg);
181 sdm = PARM_GET(psdm->width, psdm->shift, reg);
182
183 regmap_read(priv->map, pn2->reg_off, &reg);
184 n2 = PARM_GET(pn2->width, pn2->shift, reg);
185
186 return mpll_rate_from_params(parent_rate, sdm, n2);
187}
188
189static struct parm meson_fixed_pll_parm[3] = {
190 {HHI_MPLL_CNTL, 0, 9}, /* pm */
191 {HHI_MPLL_CNTL, 9, 5}, /* pn */
192 {HHI_MPLL_CNTL, 16, 2}, /* pod */
193};
194
195static struct parm meson_sys_pll_parm[3] = {
196 {HHI_SYS_PLL_CNTL, 0, 9}, /* pm */
197 {HHI_SYS_PLL_CNTL, 9, 5}, /* pn */
198 {HHI_SYS_PLL_CNTL, 16, 2}, /* pod */
199};
200
201static ulong meson_pll_get_rate(struct clk *clk, unsigned long id)
202{
203 struct meson_clk *priv = dev_get_priv(clk->dev);
204 struct parm *pm, *pn, *pod;
205 unsigned long parent_rate_mhz = XTAL_RATE / 1000000;
206 u16 n, m, od;
207 uint reg;
208
209 switch (id) {
210 case CLKID_FIXED_PLL:
211 pm = &meson_fixed_pll_parm[0];
212 pn = &meson_fixed_pll_parm[1];
213 pod = &meson_fixed_pll_parm[2];
214 break;
215 case CLKID_SYS_PLL:
216 pm = &meson_sys_pll_parm[0];
217 pn = &meson_sys_pll_parm[1];
218 pod = &meson_sys_pll_parm[2];
219 break;
220 default:
221 return -ENOENT;
222 }
223
224 regmap_read(priv->map, pn->reg_off, &reg);
225 n = PARM_GET(pn->width, pn->shift, reg);
226
227 regmap_read(priv->map, pm->reg_off, &reg);
228 m = PARM_GET(pm->width, pm->shift, reg);
229
230 regmap_read(priv->map, pod->reg_off, &reg);
231 od = PARM_GET(pod->width, pod->shift, reg);
232
233 return ((parent_rate_mhz * m / n) >> od) * 1000000;
234}
235
236static ulong meson_clk_get_rate_by_id(struct clk *clk, unsigned long id)
237{
238 ulong rate;
239
240 switch (id) {
241 case CLKID_FIXED_PLL:
242 case CLKID_SYS_PLL:
243 rate = meson_pll_get_rate(clk, id);
244 break;
245 case CLKID_FCLK_DIV2:
246 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 2;
247 break;
248 case CLKID_FCLK_DIV3:
249 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 3;
250 break;
251 case CLKID_FCLK_DIV4:
252 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 4;
253 break;
254 case CLKID_FCLK_DIV5:
255 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 5;
256 break;
257 case CLKID_FCLK_DIV7:
258 rate = meson_pll_get_rate(clk, CLKID_FIXED_PLL) / 7;
259 break;
260 case CLKID_MPLL0:
261 case CLKID_MPLL1:
262 case CLKID_MPLL2:
263 rate = meson_mpll_get_rate(clk, id);
264 break;
265 case CLKID_CLK81:
266 rate = meson_clk81_get_rate(clk);
267 break;
268 default:
269 if (gates[id].reg != 0) {
270 /* a clock gate */
271 rate = meson_clk81_get_rate(clk);
272 break;
273 }
274 return -ENOENT;
275 }
276
277 debug("clock %lu has rate %lu\n", id, rate);
278 return rate;
279}
280
281static ulong meson_clk_get_rate(struct clk *clk)
282{
283 return meson_clk_get_rate_by_id(clk, clk->id);
284}
285
286static int meson_clk_probe(struct udevice *dev)
287{
288 struct meson_clk *priv = dev_get_priv(dev);
289
290 priv->map = syscon_node_to_regmap(dev_get_parent(dev)->node);
291 if (IS_ERR(priv->map))
292 return PTR_ERR(priv->map);
293
Jerome Brunetb3d69aa2020-03-05 12:12:37 +0100294 /*
295 * Depending on the boot src, the state of the MMC clock might
296 * be different. Reset it to make sure we won't get stuck
297 */
298 regmap_write(priv->map, HHI_NAND_CLK_CNTL, 0);
299 regmap_write(priv->map, HHI_SD_EMMC_CLK_CNTL, 0);
300
Neil Armstrongf6eb68b2018-09-07 17:25:13 +0200301 debug("meson-clk-axg: probed\n");
302
303 return 0;
304}
305
306static struct clk_ops meson_clk_ops = {
307 .disable = meson_clk_disable,
308 .enable = meson_clk_enable,
309 .get_rate = meson_clk_get_rate,
310};
311
312static const struct udevice_id meson_clk_ids[] = {
313 { .compatible = "amlogic,axg-clkc" },
314 { }
315};
316
317U_BOOT_DRIVER(meson_clk_axg) = {
318 .name = "meson_clk_axg",
319 .id = UCLASS_CLK,
320 .of_match = meson_clk_ids,
321 .priv_auto_alloc_size = sizeof(struct meson_clk),
322 .ops = &meson_clk_ops,
323 .probe = meson_clk_probe,
324};