1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2022-23 StarFive Technology Co., Ltd.
4  *
5  * Author:	Yanhong Wang <yanhong.wang@starfivetech.com>
6  */
7 
8 #include <common.h>
9 #include <asm/io.h>
10 #include <malloc.h>
11 #include <clk-uclass.h>
12 #include <div64.h>
13 #include <dm/device.h>
14 #include <linux/bitops.h>
15 #include <linux/clk-provider.h>
16 #include <linux/delay.h>
17 #include <linux/err.h>
18 
19 #include "clk.h"
20 
21 #define UBOOT_DM_CLK_JH7110_PLLX "jh7110_clk_pllx"
22 
23 #define PLL_PD_OFF		1
24 #define PLL_PD_ON		0
25 
26 #define CLK_DDR_BUS_MASK	GENMASK(29, 24)
27 #define CLK_DDR_BUS_OFFSET	0xAC
28 #define CLK_DDR_BUS_OSC_DIV2	0
29 #define CLK_DDR_BUS_PLL1_DIV2	1
30 #define CLK_DDR_BUS_PLL1_DIV4	2
31 #define CLK_DDR_BUS_PLL1_DIV8	3
32 
33 struct clk_jh7110_pllx {
34 	struct clk		clk;
35 	void __iomem	*base;
36 	void __iomem	*sysreg;
37 	enum starfive_pll_type	type;
38 	const struct starfive_pllx_offset *offset;
39 	const struct starfive_pllx_rate *rate_table;
40 	int rate_count;
41 };
42 
43 #define getbits_le32(addr, mask) ((in_le32(addr) & (mask)) >> __ffs((mask)))
44 
45 #define PLLX_SET(offset, mask, val) do {\
46 		reg = readl((ulong *)((ulong)pll->base + (offset))); \
47 		reg &= ~(mask); \
48 		reg |= (mask) & ((val) << __ffs(mask)); \
49 		writel(reg, (ulong *)((ulong)pll->base + (offset))); \
50 	} while (0)
51 
52 #define PLLX_RATE(_rate, _pd, _fd)	\
53 	{						\
54 		.rate		= (_rate),	\
55 		.prediv		= (_pd),	\
56 		.fbdiv		= (_fd),	\
57 	}
58 
59 #define to_clk_pllx(_clk) container_of(_clk, struct clk_jh7110_pllx, clk)
60 
61 static const struct starfive_pllx_rate jh7110_pll0_tbl[] = {
62 	PLLX_RATE(375000000UL, 8, 125),
63 	PLLX_RATE(500000000UL, 6, 125),
64 	PLLX_RATE(625000000UL, 24, 625),
65 	PLLX_RATE(750000000UL, 4, 125),
66 	PLLX_RATE(875000000UL, 24, 875),
67 	PLLX_RATE(1000000000UL, 3, 125),
68 	PLLX_RATE(1250000000UL, 12, 625),
69 	PLLX_RATE(1375000000UL, 24, 1375),
70 	PLLX_RATE(1500000000UL, 2, 125),
71 	PLLX_RATE(1625000000UL, 24, 1625),
72 	PLLX_RATE(1750000000UL, 12, 875),
73 	PLLX_RATE(1800000000UL, 3, 225),
74 };
75 
76 static const struct starfive_pllx_rate jh7110_pll1_tbl[] = {
77 	PLLX_RATE(1066000000UL, 12, 533),
78 	PLLX_RATE(1200000000UL, 1, 50),
79 	PLLX_RATE(1400000000UL, 6, 350),
80 	PLLX_RATE(1600000000UL, 3, 200),
81 };
82 
83 static const struct starfive_pllx_rate jh7110_pll2_tbl[] = {
84 	PLLX_RATE(1228800000UL, 15, 768),
85 	PLLX_RATE(1188000000UL, 2, 99),
86 };
87 
88 static const struct starfive_pllx_offset jh7110_pll0_offset = {
89 	.pd = 0x20,
90 	.prediv = 0x24,
91 	.fbdiv = 0x1c,
92 	.frac = 0x20,
93 	.postdiv1 = 0x20,
94 	.dacpd = 0x18,
95 	.dsmpd = 0x18,
96 	.pd_mask = BIT(27),
97 	.prediv_mask = GENMASK(5, 0),
98 	.fbdiv_mask = GENMASK(11, 0),
99 	.frac_mask = GENMASK(23, 0),
100 	.postdiv1_mask = GENMASK(29, 28),
101 	.dacpd_mask = BIT(24),
102 	.dsmpd_mask = BIT(25)
103 };
104 
105 static const struct starfive_pllx_offset jh7110_pll1_offset = {
106 	.pd = 0x28,
107 	.prediv = 0x2c,
108 	.fbdiv = 0x24,
109 	.frac = 0x28,
110 	.postdiv1 = 0x28,
111 	.dacpd = 0x24,
112 	.dsmpd = 0x24,
113 	.pd_mask = BIT(27),
114 	.prediv_mask = GENMASK(5, 0),
115 	.fbdiv_mask = GENMASK(28, 17),
116 	.frac_mask = GENMASK(23, 0),
117 	.postdiv1_mask = GENMASK(29, 28),
118 	.dacpd_mask = BIT(15),
119 	.dsmpd_mask = BIT(16)
120 };
121 
122 static const struct starfive_pllx_offset jh7110_pll2_offset = {
123 	.pd = 0x30,
124 	.prediv = 0x34,
125 	.fbdiv = 0x2c,
126 	.frac = 0x30,
127 	.postdiv1 = 0x30,
128 	.dacpd = 0x2c,
129 	.dsmpd = 0x2c,
130 	.pd_mask = BIT(27),
131 	.prediv_mask = GENMASK(5, 0),
132 	.fbdiv_mask = GENMASK(28, 17),
133 	.frac_mask = GENMASK(23, 0),
134 	.postdiv1_mask = GENMASK(29, 28),
135 	.dacpd_mask = BIT(15),
136 	.dsmpd_mask = BIT(16)
137 };
138 
139 struct starfive_pllx_clk starfive_jh7110_pll0 __initdata = {
140 	.type = PLL0,
141 	.offset = &jh7110_pll0_offset,
142 	.rate_table = jh7110_pll0_tbl,
143 	.rate_count = ARRAY_SIZE(jh7110_pll0_tbl),
144 };
145 
146 struct starfive_pllx_clk starfive_jh7110_pll1 __initdata = {
147 	.type = PLL1,
148 	.offset = &jh7110_pll1_offset,
149 	.rate_table = jh7110_pll1_tbl,
150 	.rate_count = ARRAY_SIZE(jh7110_pll1_tbl),
151 };
152 
153 struct starfive_pllx_clk starfive_jh7110_pll2 __initdata = {
154 	.type = PLL2,
155 	.offset = &jh7110_pll2_offset,
156 	.rate_table = jh7110_pll2_tbl,
157 	.rate_count = ARRAY_SIZE(jh7110_pll2_tbl),
158 };
159 
160 static const struct starfive_pllx_rate *
jh7110_get_pll_settings(struct clk_jh7110_pllx * pll,unsigned long rate)161 jh7110_get_pll_settings(struct clk_jh7110_pllx *pll, unsigned long rate)
162 {
163 	for (int i = 0; i < pll->rate_count; i++)
164 		if (rate == pll->rate_table[i].rate)
165 			return &pll->rate_table[i];
166 
167 	return NULL;
168 }
169 
jh7110_pll_set_rate(struct clk_jh7110_pllx * pll,const struct starfive_pllx_rate * rate)170 static void jh7110_pll_set_rate(struct clk_jh7110_pllx *pll,
171 				const struct starfive_pllx_rate *rate)
172 {
173 	u32 reg;
174 	bool set = (pll->type == PLL1) ? true : false;
175 
176 	if (set) {
177 		reg = readl((ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
178 		reg &= ~CLK_DDR_BUS_MASK;
179 		reg |= CLK_DDR_BUS_OSC_DIV2 << __ffs(CLK_DDR_BUS_MASK);
180 		writel(reg, (ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
181 	}
182 
183 	PLLX_SET(pll->offset->pd, pll->offset->pd_mask, PLL_PD_OFF);
184 	PLLX_SET(pll->offset->dacpd, pll->offset->dacpd_mask, 1);
185 	PLLX_SET(pll->offset->dsmpd, pll->offset->dsmpd_mask, 1);
186 	PLLX_SET(pll->offset->prediv, pll->offset->prediv_mask, rate->prediv);
187 	PLLX_SET(pll->offset->fbdiv, pll->offset->fbdiv_mask, rate->fbdiv);
188 	PLLX_SET(pll->offset->postdiv1, pll->offset->postdiv1, 0);
189 	PLLX_SET(pll->offset->pd, pll->offset->pd_mask, PLL_PD_ON);
190 
191 	if (set) {
192 		udelay(100);
193 		reg = readl((ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
194 		reg &= ~CLK_DDR_BUS_MASK;
195 		reg |= CLK_DDR_BUS_PLL1_DIV2 << __ffs(CLK_DDR_BUS_MASK);
196 		writel(reg, (ulong *)((ulong)pll->sysreg + CLK_DDR_BUS_OFFSET));
197 	}
198 }
199 
jh7110_pllx_recalc_rate(struct clk * clk)200 static ulong jh7110_pllx_recalc_rate(struct clk *clk)
201 {
202 	struct clk_jh7110_pllx *pll = to_clk_pllx(dev_get_clk_ptr(clk->dev));
203 	u64 refclk = clk_get_parent_rate(clk);
204 	u32 dacpd, dsmpd;
205 	u32 prediv, fbdiv, postdiv1;
206 	u64 frac;
207 
208 	dacpd = getbits_le32((ulong)pll->base + pll->offset->dacpd,
209 			     pll->offset->dacpd_mask);
210 	dsmpd = getbits_le32((ulong)pll->base + pll->offset->dsmpd,
211 			     pll->offset->dsmpd_mask);
212 	prediv = getbits_le32((ulong)pll->base + pll->offset->prediv,
213 			      pll->offset->prediv_mask);
214 	fbdiv = getbits_le32((ulong)pll->base + pll->offset->fbdiv,
215 			     pll->offset->fbdiv_mask);
216 	postdiv1 = 1 << getbits_le32((ulong)pll->base + pll->offset->postdiv1,
217 			pll->offset->postdiv1_mask);
218 	frac = (u64)getbits_le32((ulong)pll->base + pll->offset->frac,
219 			pll->offset->frac_mask);
220 
221 	/* Integer Multiple Mode
222 	 * Both dacpd and dsmpd should be set as 1 while integer multiple mode.
223 	 *
224 	 * The frequency of outputs can be figured out as below.
225 	 *
226      *       Fvco = Fref*Nl/M
227 	 * NI is integer frequency dividing ratio of feedback divider, set by fbdiv1[11:0] ,
228 	 *    NI = 8, 9, 10, 12.13....4095
229 	 * M is frequency dividing ratio of pre-divider, set by prediv[5:0],M = 1,2...63
230 	 *
231      *      Fclko1 = Fvco/Q1
232 	 * Q1 is frequency dividing ratio of post divider, set by postdiv1[1:0],Q1= 1,2,4,8
233 	 *
234 	 * Fraction Multiple Mode
235 	 *
236 	 *  Both dacpd and dsmpd should be set as 0 while integer multiple mode.
237 	 *
238      *      Fvco = Fref*(NI+NF)/M
239 	 * NI is integer frequency dividing ratio of feedback divider, set by fbdiv[11:0] ,
240 	 *     NI = 8, 9, 10, 12.13....4095
241 	 * NF is fractional frequency dividing ratio, set by frac[23:0],  NF =frac[23:0]/2^24= 0~0.99999994
242 	 * M is frequency dividing ratio of pre-divider, set by prediv[5:0],M = 1,2...63
243 	 *
244      *     Fclko1 = Fvco/Q1
245 	 * Q1 is frequency dividing ratio of post divider, set by postdivl[1:0],Q1= 1,2,4,8
246 	 */
247 	if (dacpd == 1 && dsmpd == 1)
248 		frac = 0;
249 	else if (dacpd == 0 && dsmpd == 0)
250 		do_div(frac, 1 << 24);
251 	else
252 		return -EINVAL;
253 
254 	refclk *= (fbdiv + frac);
255 	do_div(refclk, prediv * postdiv1);
256 
257 	return refclk;
258 }
259 
jh7110_pllx_set_rate(struct clk * clk,ulong drate)260 static ulong jh7110_pllx_set_rate(struct clk *clk, ulong drate)
261 {
262 	struct clk_jh7110_pllx *pll = to_clk_pllx(dev_get_clk_ptr(clk->dev));
263 	const struct starfive_pllx_rate *rate;
264 
265 	rate = jh7110_get_pll_settings(pll, drate);
266 	if (!rate)
267 		return -EINVAL;
268 
269 	jh7110_pll_set_rate(pll, rate);
270 
271 	return jh7110_pllx_recalc_rate(clk);
272 }
273 
274 static const struct clk_ops clk_jh7110_ops = {
275 	.set_rate	= jh7110_pllx_set_rate,
276 	.get_rate	= jh7110_pllx_recalc_rate,
277 };
278 
starfive_jh7110_pll(const char * name,const char * parent_name,void __iomem * base,void __iomem * sysreg,const struct starfive_pllx_clk * pll_clk)279 struct clk *starfive_jh7110_pll(const char *name, const char *parent_name,
280 				void __iomem *base, void __iomem *sysreg,
281 				const struct starfive_pllx_clk *pll_clk)
282 {
283 	struct clk_jh7110_pllx *pll;
284 	struct clk *clk;
285 	int ret;
286 
287 	if (!pll_clk || !base || !sysreg)
288 		return ERR_PTR(-EINVAL);
289 
290 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
291 	if (!pll)
292 		return ERR_PTR(-ENOMEM);
293 
294 	pll->base = base;
295 	pll->sysreg = sysreg;
296 	pll->type = pll_clk->type;
297 	pll->offset = pll_clk->offset;
298 	pll->rate_table = pll_clk->rate_table;
299 	pll->rate_count = pll_clk->rate_count;
300 
301 	clk = &pll->clk;
302 	ret = clk_register(clk, UBOOT_DM_CLK_JH7110_PLLX, name, parent_name);
303 	if (ret) {
304 		kfree(pll);
305 		return ERR_PTR(ret);
306 	}
307 
308 	if (IS_ENABLED(CONFIG_SPL_BUILD) && pll->type == PLL0)
309 		jh7110_pllx_set_rate(clk, 1000000000);
310 
311 	if (IS_ENABLED(CONFIG_SPL_BUILD) && pll->type == PLL2)
312 		jh7110_pllx_set_rate(clk, 1188000000);
313 
314 	return clk;
315 }
316 
317 U_BOOT_DRIVER(jh7110_clk_pllx) = {
318 	.name	= UBOOT_DM_CLK_JH7110_PLLX,
319 	.id	= UCLASS_CLK,
320 	.ops	= &clk_jh7110_ops,
321 };
322