blob: fad306aeed232156016a1a1536956b96991dd49d [file] [log] [blame]
Lukasz Majewski4de44bb2019-06-24 15:50:45 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2019 DENX Software Engineering
4 * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
5 */
6
7#include <common.h>
8#include <asm/io.h>
Giulio Benetti6713a012020-01-10 15:46:59 +01009#include <div64.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020010#include <malloc.h>
11#include <clk-uclass.h>
12#include <dm/device.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070013#include <dm/devres.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020014#include <dm/uclass.h>
15#include <clk.h>
16#include "clk.h"
Simon Glassd66c5f72020-02-03 07:36:15 -070017#include <linux/err.h>
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020018
Giulio Benettiff331fa2020-01-10 15:46:53 +010019#define UBOOT_DM_CLK_IMX_PLLV3_GENERIC "imx_clk_pllv3_generic"
Giulio Benetti05bf7fd2020-01-10 15:46:58 +010020#define UBOOT_DM_CLK_IMX_PLLV3_SYS "imx_clk_pllv3_sys"
Giulio Benettiff331fa2020-01-10 15:46:53 +010021#define UBOOT_DM_CLK_IMX_PLLV3_USB "imx_clk_pllv3_usb"
Giulio Benetti6713a012020-01-10 15:46:59 +010022#define UBOOT_DM_CLK_IMX_PLLV3_AV "imx_clk_pllv3_av"
Lukasz Majewskib0f278b2020-02-24 14:55:25 +010023#define UBOOT_DM_CLK_IMX_PLLV3_ENET "imx_clk_pllv3_enet"
Jesse Taube4303cd12022-07-26 01:43:42 -040024#define UBOOT_DM_CLK_IMX_PLLV3_GENV2 "imx_clk_pllv3_genericv2"
Giulio Benetti6713a012020-01-10 15:46:59 +010025
26#define PLL_NUM_OFFSET 0x10
27#define PLL_DENOM_OFFSET 0x20
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020028
Giulio Benetti7dad7a32020-01-10 15:46:55 +010029#define BM_PLL_POWER (0x1 << 12)
Jesse Taube4303cd12022-07-26 01:43:42 -040030#define BM_PLL_POWER_V2 (0x1 << 21)
Giulio Benettieeef2742020-04-08 17:10:07 +020031#define BM_PLL_ENABLE (0x1 << 13)
Giulio Benetti8c25d1e2020-01-10 15:46:57 +010032#define BM_PLL_LOCK (0x1 << 31)
Jesse Taube4303cd12022-07-26 01:43:42 -040033#define BM_PLL_LOCK_V2 (0x1 << 29)
Giulio Benetti7dad7a32020-01-10 15:46:55 +010034
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020035struct clk_pllv3 {
36 struct clk clk;
37 void __iomem *base;
Giulio Benetti7dad7a32020-01-10 15:46:55 +010038 u32 power_bit;
39 bool powerup_set;
Jesse Taube4303cd12022-07-26 01:43:42 -040040 u32 lock_bit;
Giulio Benettieeef2742020-04-08 17:10:07 +020041 u32 enable_bit;
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020042 u32 div_mask;
43 u32 div_shift;
Lukasz Majewskib0f278b2020-02-24 14:55:25 +010044 unsigned long ref_clock;
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020045};
46
47#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
48
Jesse Taube4303cd12022-07-26 01:43:42 -040049static ulong clk_pllv3_genericv2_get_rate(struct clk *clk)
50{
51 struct clk_pllv3 *pll = to_clk_pllv3(dev_get_clk_ptr(clk->dev));
52 unsigned long parent_rate = clk_get_parent_rate(clk);
53
54 u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
55
56 return (div == 0) ? parent_rate * 22 : parent_rate * 20;
57}
58
59static ulong clk_pllv3_genericv2_set_rate(struct clk *clk, ulong rate)
60{
61 struct clk_pllv3 *pll = to_clk_pllv3(clk);
62 unsigned long parent_rate = clk_get_parent_rate(clk);
63
64 u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
65 u32 val = (div == 0) ? parent_rate * 22 : parent_rate * 20;
66
67 if (rate == val)
68 return 0;
69
70 return -EINVAL;
71}
72
Giulio Benettiff331fa2020-01-10 15:46:53 +010073static ulong clk_pllv3_generic_get_rate(struct clk *clk)
Lukasz Majewski4de44bb2019-06-24 15:50:45 +020074{
75 struct clk_pllv3 *pll = to_clk_pllv3(dev_get_clk_ptr(clk->dev));
76 unsigned long parent_rate = clk_get_parent_rate(clk);
77
78 u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
79
80 return (div == 1) ? parent_rate * 22 : parent_rate * 20;
81}
82
Giulio Benetti8c25d1e2020-01-10 15:46:57 +010083static ulong clk_pllv3_generic_set_rate(struct clk *clk, ulong rate)
84{
85 struct clk_pllv3 *pll = to_clk_pllv3(clk);
86 unsigned long parent_rate = clk_get_parent_rate(clk);
87 u32 val, div;
88
89 if (rate == parent_rate * 22)
90 div = 1;
91 else if (rate == parent_rate * 20)
92 div = 0;
93 else
94 return -EINVAL;
95
96 val = readl(pll->base);
97 val &= ~(pll->div_mask << pll->div_shift);
98 val |= (div << pll->div_shift);
99 writel(val, pll->base);
100
101 /* Wait for PLL to lock */
Jesse Taube4303cd12022-07-26 01:43:42 -0400102 while (!(readl(pll->base) & pll->lock_bit))
Giulio Benetti8c25d1e2020-01-10 15:46:57 +0100103 ;
104
105 return 0;
106}
107
Giulio Benetti7dad7a32020-01-10 15:46:55 +0100108static int clk_pllv3_generic_enable(struct clk *clk)
109{
110 struct clk_pllv3 *pll = to_clk_pllv3(clk);
111 u32 val;
112
113 val = readl(pll->base);
114 if (pll->powerup_set)
115 val |= pll->power_bit;
116 else
117 val &= ~pll->power_bit;
Giulio Benettieeef2742020-04-08 17:10:07 +0200118
119 val |= pll->enable_bit;
120
Giulio Benetti7dad7a32020-01-10 15:46:55 +0100121 writel(val, pll->base);
122
123 return 0;
124}
125
Giulio Benetti47e15642020-01-10 15:46:56 +0100126static int clk_pllv3_generic_disable(struct clk *clk)
127{
128 struct clk_pllv3 *pll = to_clk_pllv3(clk);
129 u32 val;
130
131 val = readl(pll->base);
132 if (pll->powerup_set)
133 val &= ~pll->power_bit;
134 else
135 val |= pll->power_bit;
Giulio Benettieeef2742020-04-08 17:10:07 +0200136
137 val &= ~pll->enable_bit;
138
Giulio Benetti47e15642020-01-10 15:46:56 +0100139 writel(val, pll->base);
140
141 return 0;
142}
143
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200144static const struct clk_ops clk_pllv3_generic_ops = {
Giulio Benettiff331fa2020-01-10 15:46:53 +0100145 .get_rate = clk_pllv3_generic_get_rate,
Giulio Benetti7dad7a32020-01-10 15:46:55 +0100146 .enable = clk_pllv3_generic_enable,
Giulio Benetti47e15642020-01-10 15:46:56 +0100147 .disable = clk_pllv3_generic_disable,
Giulio Benetti8c25d1e2020-01-10 15:46:57 +0100148 .set_rate = clk_pllv3_generic_set_rate,
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200149};
150
Jesse Taube4303cd12022-07-26 01:43:42 -0400151static const struct clk_ops clk_pllv3_genericv2_ops = {
152 .get_rate = clk_pllv3_genericv2_get_rate,
153 .enable = clk_pllv3_generic_enable,
154 .disable = clk_pllv3_generic_disable,
155 .set_rate = clk_pllv3_genericv2_set_rate,
156};
157
Giulio Benetti05bf7fd2020-01-10 15:46:58 +0100158static ulong clk_pllv3_sys_get_rate(struct clk *clk)
159{
160 struct clk_pllv3 *pll = to_clk_pllv3(clk);
161 unsigned long parent_rate = clk_get_parent_rate(clk);
162 u32 div = readl(pll->base) & pll->div_mask;
163
164 return parent_rate * div / 2;
165}
166
167static ulong clk_pllv3_sys_set_rate(struct clk *clk, ulong rate)
168{
169 struct clk_pllv3 *pll = to_clk_pllv3(clk);
170 unsigned long parent_rate = clk_get_parent_rate(clk);
Giulio Benetticf4c04a2020-01-17 13:06:40 +0100171 unsigned long min_rate;
172 unsigned long max_rate;
Giulio Benetti05bf7fd2020-01-10 15:46:58 +0100173 u32 val, div;
174
Giulio Benetticf4c04a2020-01-17 13:06:40 +0100175 if (parent_rate == 0)
176 return -EINVAL;
177
178 min_rate = parent_rate * 54 / 2;
179 max_rate = parent_rate * 108 / 2;
180
Giulio Benetti05bf7fd2020-01-10 15:46:58 +0100181 if (rate < min_rate || rate > max_rate)
182 return -EINVAL;
183
184 div = rate * 2 / parent_rate;
185 val = readl(pll->base);
186 val &= ~pll->div_mask;
187 val |= div;
188 writel(val, pll->base);
189
190 /* Wait for PLL to lock */
Jesse Taube4303cd12022-07-26 01:43:42 -0400191 while (!(readl(pll->base) & pll->lock_bit))
Giulio Benetti05bf7fd2020-01-10 15:46:58 +0100192 ;
193
194 return 0;
195}
196
197static const struct clk_ops clk_pllv3_sys_ops = {
Wolfgang Denk62fb2b42021-09-27 17:42:39 +0200198 .enable = clk_pllv3_generic_enable,
Giulio Benetti05bf7fd2020-01-10 15:46:58 +0100199 .disable = clk_pllv3_generic_disable,
200 .get_rate = clk_pllv3_sys_get_rate,
201 .set_rate = clk_pllv3_sys_set_rate,
202};
203
Giulio Benetti6713a012020-01-10 15:46:59 +0100204static ulong clk_pllv3_av_get_rate(struct clk *clk)
205{
206 struct clk_pllv3 *pll = to_clk_pllv3(clk);
207 unsigned long parent_rate = clk_get_parent_rate(clk);
208 u32 mfn = readl(pll->base + PLL_NUM_OFFSET);
209 u32 mfd = readl(pll->base + PLL_DENOM_OFFSET);
210 u32 div = readl(pll->base) & pll->div_mask;
211 u64 temp64 = (u64)parent_rate;
212
Giulio Benetti2af750e2020-01-17 13:06:41 +0100213 if (mfd == 0)
214 return -EIO;
215
Giulio Benetti6713a012020-01-10 15:46:59 +0100216 temp64 *= mfn;
217 do_div(temp64, mfd);
218
219 return parent_rate * div + (unsigned long)temp64;
220}
221
222static ulong clk_pllv3_av_set_rate(struct clk *clk, ulong rate)
223{
224 struct clk_pllv3 *pll = to_clk_pllv3(clk);
225 unsigned long parent_rate = clk_get_parent_rate(clk);
Giulio Benettie4a55582020-01-17 13:06:42 +0100226 unsigned long min_rate;
227 unsigned long max_rate;
Giulio Benetti6713a012020-01-10 15:46:59 +0100228 u32 val, div;
229 u32 mfn, mfd = 1000000;
230 u32 max_mfd = 0x3FFFFFFF;
231 u64 temp64;
232
Giulio Benettie4a55582020-01-17 13:06:42 +0100233 if (parent_rate == 0)
234 return -EINVAL;
235
236 min_rate = parent_rate * 27;
237 max_rate = parent_rate * 54;
238
Giulio Benetti6713a012020-01-10 15:46:59 +0100239 if (rate < min_rate || rate > max_rate)
240 return -EINVAL;
241
242 if (parent_rate <= max_mfd)
243 mfd = parent_rate;
244
245 div = rate / parent_rate;
246 temp64 = (u64)(rate - div * parent_rate);
247 temp64 *= mfd;
248 do_div(temp64, parent_rate);
249 mfn = temp64;
250
251 val = readl(pll->base);
252 val &= ~pll->div_mask;
253 val |= div;
254 writel(val, pll->base);
255 writel(mfn, pll->base + PLL_NUM_OFFSET);
256 writel(mfd, pll->base + PLL_DENOM_OFFSET);
257
258 /* Wait for PLL to lock */
Jesse Taube4303cd12022-07-26 01:43:42 -0400259 while (!(readl(pll->base) & pll->lock_bit))
Giulio Benetti6713a012020-01-10 15:46:59 +0100260 ;
261
262 return 0;
263}
264
265static const struct clk_ops clk_pllv3_av_ops = {
266 .enable = clk_pllv3_generic_enable,
267 .disable = clk_pllv3_generic_disable,
268 .get_rate = clk_pllv3_av_get_rate,
269 .set_rate = clk_pllv3_av_set_rate,
270};
271
Lukasz Majewskib0f278b2020-02-24 14:55:25 +0100272static ulong clk_pllv3_enet_get_rate(struct clk *clk)
273{
274 struct clk_pllv3 *pll = to_clk_pllv3(clk);
275
276 return pll->ref_clock;
277}
278
279static const struct clk_ops clk_pllv3_enet_ops = {
280 .enable = clk_pllv3_generic_enable,
281 .disable = clk_pllv3_generic_disable,
282 .get_rate = clk_pllv3_enet_get_rate,
283};
284
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200285struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
286 const char *parent_name, void __iomem *base,
287 u32 div_mask)
288{
289 struct clk_pllv3 *pll;
290 struct clk *clk;
291 char *drv_name;
292 int ret;
293
294 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
295 if (!pll)
296 return ERR_PTR(-ENOMEM);
297
Giulio Benetti7dad7a32020-01-10 15:46:55 +0100298 pll->power_bit = BM_PLL_POWER;
Giulio Benettieeef2742020-04-08 17:10:07 +0200299 pll->enable_bit = BM_PLL_ENABLE;
Jesse Taube4303cd12022-07-26 01:43:42 -0400300 pll->lock_bit = BM_PLL_LOCK;
Giulio Benetti7dad7a32020-01-10 15:46:55 +0100301
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200302 switch (type) {
303 case IMX_PLLV3_GENERIC:
Giulio Benettiff331fa2020-01-10 15:46:53 +0100304 drv_name = UBOOT_DM_CLK_IMX_PLLV3_GENERIC;
Giulio Benetti670e5702020-01-10 15:46:54 +0100305 pll->div_shift = 0;
Giulio Benetti7dad7a32020-01-10 15:46:55 +0100306 pll->powerup_set = false;
Giulio Benettiff331fa2020-01-10 15:46:53 +0100307 break;
Jesse Taube4303cd12022-07-26 01:43:42 -0400308 case IMX_PLLV3_GENERICV2:
309 pll->power_bit = BM_PLL_POWER_V2;
310 pll->lock_bit = BM_PLL_LOCK_V2;
311 drv_name = UBOOT_DM_CLK_IMX_PLLV3_GENV2;
312 pll->div_shift = 0;
313 pll->powerup_set = false;
314 break;
Giulio Benetti05bf7fd2020-01-10 15:46:58 +0100315 case IMX_PLLV3_SYS:
316 drv_name = UBOOT_DM_CLK_IMX_PLLV3_SYS;
317 pll->div_shift = 0;
318 pll->powerup_set = false;
319 break;
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200320 case IMX_PLLV3_USB:
Giulio Benettiff331fa2020-01-10 15:46:53 +0100321 drv_name = UBOOT_DM_CLK_IMX_PLLV3_USB;
Giulio Benetti670e5702020-01-10 15:46:54 +0100322 pll->div_shift = 1;
Giulio Benetti7dad7a32020-01-10 15:46:55 +0100323 pll->powerup_set = true;
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200324 break;
Giulio Benetti6713a012020-01-10 15:46:59 +0100325 case IMX_PLLV3_AV:
326 drv_name = UBOOT_DM_CLK_IMX_PLLV3_AV;
327 pll->div_shift = 0;
328 pll->powerup_set = false;
329 break;
Lukasz Majewskib0f278b2020-02-24 14:55:25 +0100330 case IMX_PLLV3_ENET:
331 drv_name = UBOOT_DM_CLK_IMX_PLLV3_ENET;
332 pll->ref_clock = 500000000;
333 break;
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200334 default:
335 kfree(pll);
Simon Glass29ff16a2021-03-25 10:26:08 +1300336 return ERR_PTR(-EINVAL);
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200337 }
338
339 pll->base = base;
340 pll->div_mask = div_mask;
341 clk = &pll->clk;
342
343 ret = clk_register(clk, drv_name, name, parent_name);
344 if (ret) {
345 kfree(pll);
346 return ERR_PTR(ret);
347 }
348
349 return clk;
350}
351
352U_BOOT_DRIVER(clk_pllv3_generic) = {
Giulio Benettiff331fa2020-01-10 15:46:53 +0100353 .name = UBOOT_DM_CLK_IMX_PLLV3_GENERIC,
354 .id = UCLASS_CLK,
355 .ops = &clk_pllv3_generic_ops,
356 .flags = DM_FLAG_PRE_RELOC,
357};
358
Jesse Taube4303cd12022-07-26 01:43:42 -0400359U_BOOT_DRIVER(clk_pllv3_genericv2) = {
360 .name = UBOOT_DM_CLK_IMX_PLLV3_GENV2,
361 .id = UCLASS_CLK,
362 .ops = &clk_pllv3_genericv2_ops,
363 .flags = DM_FLAG_PRE_RELOC,
364};
365
Giulio Benetti05bf7fd2020-01-10 15:46:58 +0100366U_BOOT_DRIVER(clk_pllv3_sys) = {
367 .name = UBOOT_DM_CLK_IMX_PLLV3_SYS,
368 .id = UCLASS_CLK,
369 .ops = &clk_pllv3_sys_ops,
370 .flags = DM_FLAG_PRE_RELOC,
371};
372
Giulio Benettiff331fa2020-01-10 15:46:53 +0100373U_BOOT_DRIVER(clk_pllv3_usb) = {
374 .name = UBOOT_DM_CLK_IMX_PLLV3_USB,
Lukasz Majewski4de44bb2019-06-24 15:50:45 +0200375 .id = UCLASS_CLK,
376 .ops = &clk_pllv3_generic_ops,
377 .flags = DM_FLAG_PRE_RELOC,
378};
Giulio Benetti6713a012020-01-10 15:46:59 +0100379
380U_BOOT_DRIVER(clk_pllv3_av) = {
381 .name = UBOOT_DM_CLK_IMX_PLLV3_AV,
382 .id = UCLASS_CLK,
383 .ops = &clk_pllv3_av_ops,
384 .flags = DM_FLAG_PRE_RELOC,
385};
Lukasz Majewskib0f278b2020-02-24 14:55:25 +0100386
387U_BOOT_DRIVER(clk_pllv3_enet) = {
388 .name = UBOOT_DM_CLK_IMX_PLLV3_ENET,
389 .id = UCLASS_CLK,
390 .ops = &clk_pllv3_enet_ops,
391};