blob: 02e6d9000e292dfc71ab4e474abdea08baa7da20 [file] [log] [blame]
Yanhong Wangc13fe7c2023-03-29 11:42:13 +08001// 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
33struct 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
61static 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
76static 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
83static const struct starfive_pllx_rate jh7110_pll2_tbl[] = {
84 PLLX_RATE(1228800000UL, 15, 768),
85 PLLX_RATE(1188000000UL, 2, 99),
86};
87
88static 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
105static 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
122static 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
139struct 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
146struct 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
153struct 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
160static const struct starfive_pllx_rate *
161jh7110_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
170static 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
200static 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
260static 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
274static const struct clk_ops clk_jh7110_ops = {
275 .set_rate = jh7110_pllx_set_rate,
276 .get_rate = jh7110_pllx_recalc_rate,
277};
278
279struct 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
317U_BOOT_DRIVER(jh7110_clk_pllx) = {
318 .name = UBOOT_DM_CLK_JH7110_PLLX,
319 .id = UCLASS_CLK,
320 .ops = &clk_jh7110_ops,
321};