blob: bf762c558ef0468931c4adf3401175bc741e5376 [file] [log] [blame]
Tero Kristo0aa29302021-06-11 11:45:13 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Texas Instruments K3 SoC PLL clock driver
4 *
Dave Gerlachd3c56e22021-09-07 17:16:57 -05005 * Copyright (C) 2020-2021 Texas Instruments Incorporated - http://www.ti.com/
Tero Kristo0aa29302021-06-11 11:45:13 +03006 * Tero Kristo <t-kristo@ti.com>
7 */
8
9#include <common.h>
10#include <asm/io.h>
11#include <dm.h>
12#include <div64.h>
13#include <errno.h>
14#include <clk-uclass.h>
15#include <linux/clk-provider.h>
16#include "k3-clk.h"
17#include <linux/rational.h>
18
19/* 16FFT register offsets */
20#define PLL_16FFT_CFG 0x08
21#define PLL_KICK0 0x10
22#define PLL_KICK1 0x14
23#define PLL_16FFT_CTRL 0x20
24#define PLL_16FFT_STAT 0x24
25#define PLL_16FFT_FREQ_CTRL0 0x30
26#define PLL_16FFT_FREQ_CTRL1 0x34
27#define PLL_16FFT_DIV_CTRL 0x38
28
29/* CTRL register bits */
30#define PLL_16FFT_CTRL_BYPASS_EN BIT(31)
31#define PLL_16FFT_CTRL_PLL_EN BIT(15)
32#define PLL_16FFT_CTRL_DSM_EN BIT(1)
33
34/* STAT register bits */
35#define PLL_16FFT_STAT_LOCK BIT(0)
36
37/* FREQ_CTRL0 bits */
38#define PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK 0xfff
39
40/* DIV CTRL register bits */
41#define PLL_16FFT_DIV_CTRL_REF_DIV_MASK 0x3f
42
43#define PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS 24
44#define PLL_16FFT_HSDIV_CTRL_CLKOUT_EN BIT(15)
45
46/* KICK register magic values */
47#define PLL_KICK0_VALUE 0x68ef3490
48#define PLL_KICK1_VALUE 0xd172bc5a
49
50/**
51 * struct ti_pll_clk - TI PLL clock data info structure
52 * @clk: core clock structure
53 * @reg: memory address of the PLL controller
54 */
55struct ti_pll_clk {
56 struct clk clk;
57 void __iomem *reg;
58};
59
60#define to_clk_pll(_clk) container_of(_clk, struct ti_pll_clk, clk)
61
62static int ti_pll_wait_for_lock(struct clk *clk)
63{
64 struct ti_pll_clk *pll = to_clk_pll(clk);
65 u32 stat;
66 int i;
67
68 for (i = 0; i < 100000; i++) {
69 stat = readl(pll->reg + PLL_16FFT_STAT);
70 if (stat & PLL_16FFT_STAT_LOCK)
71 return 0;
72 }
73
74 printf("%s: pll (%s) failed to lock\n", __func__,
75 clk->dev->name);
76
77 return -EBUSY;
78}
79
80static ulong ti_pll_clk_get_rate(struct clk *clk)
81{
82 struct ti_pll_clk *pll = to_clk_pll(clk);
83 u64 current_freq;
84 u64 parent_freq = clk_get_parent_rate(clk);
85 u32 pllm;
86 u32 plld;
87 u32 pllfm;
88 u32 ctrl;
89
90 /* Check if we are in bypass */
91 ctrl = readl(pll->reg + PLL_16FFT_CTRL);
92 if (ctrl & PLL_16FFT_CTRL_BYPASS_EN)
93 return parent_freq;
94
95 pllm = readl(pll->reg + PLL_16FFT_FREQ_CTRL0);
96 pllfm = readl(pll->reg + PLL_16FFT_FREQ_CTRL1);
97
98 plld = readl(pll->reg + PLL_16FFT_DIV_CTRL) &
99 PLL_16FFT_DIV_CTRL_REF_DIV_MASK;
100
101 current_freq = parent_freq * pllm / plld;
102
103 if (pllfm) {
104 u64 tmp;
105
106 tmp = parent_freq * pllfm;
107 do_div(tmp, plld);
108 tmp >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
109 current_freq += tmp;
110 }
111
112 return current_freq;
113}
114
115static ulong ti_pll_clk_set_rate(struct clk *clk, ulong rate)
116{
117 struct ti_pll_clk *pll = to_clk_pll(clk);
118 u64 current_freq;
119 u64 parent_freq = clk_get_parent_rate(clk);
120 int ret;
121 u32 ctrl;
122 unsigned long pllm;
123 u32 pllfm = 0;
124 unsigned long plld;
Dave Gerlachd3c56e22021-09-07 17:16:57 -0500125 u32 div_ctrl;
Tero Kristo0aa29302021-06-11 11:45:13 +0300126 u32 rem;
127 int shift;
128
129 debug("%s(clk=%p, rate=%u)\n", __func__, clk, (u32)rate);
130
131 if (ti_pll_clk_get_rate(clk) == rate)
132 return rate;
133
134 if (rate != parent_freq)
135 /*
136 * Attempt with higher max multiplier value first to give
137 * some space for fractional divider to kick in.
138 */
139 for (shift = 8; shift >= 0; shift -= 8) {
140 rational_best_approximation(rate, parent_freq,
141 ((PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK + 1) << shift) - 1,
142 PLL_16FFT_DIV_CTRL_REF_DIV_MASK, &pllm, &plld);
143 if (pllm / plld <= PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK)
144 break;
145 }
146
147 /* Put PLL to bypass mode */
148 ctrl = readl(pll->reg + PLL_16FFT_CTRL);
149 ctrl |= PLL_16FFT_CTRL_BYPASS_EN;
150 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
151
152 if (rate == parent_freq) {
153 debug("%s: put %s to bypass\n", __func__, clk->dev->name);
154 return rate;
155 }
156
157 debug("%s: pre-frac-calc: rate=%u, parent_freq=%u, plld=%u, pllm=%u\n",
158 __func__, (u32)rate, (u32)parent_freq, (u32)plld, (u32)pllm);
159
160 /* Check if we need fractional config */
161 if (plld > 1) {
162 pllfm = pllm % plld;
163 pllfm <<= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
164 rem = pllfm % plld;
165 pllfm /= plld;
166 if (rem)
167 pllfm++;
168 pllm /= plld;
169 plld = 1;
170 }
171
172 if (pllfm)
173 ctrl |= PLL_16FFT_CTRL_DSM_EN;
174 else
175 ctrl &= ~PLL_16FFT_CTRL_DSM_EN;
176
177 writel(pllm, pll->reg + PLL_16FFT_FREQ_CTRL0);
178 writel(pllfm, pll->reg + PLL_16FFT_FREQ_CTRL1);
Dave Gerlachd3c56e22021-09-07 17:16:57 -0500179
180 /*
181 * div_ctrl register contains other divider values, so rmw
182 * only plld and leave existing values alone
183 */
184 div_ctrl = readl(pll->reg + PLL_16FFT_DIV_CTRL);
185 div_ctrl &= ~PLL_16FFT_DIV_CTRL_REF_DIV_MASK;
186 div_ctrl |= plld;
187 writel(div_ctrl, pll->reg + PLL_16FFT_DIV_CTRL);
Tero Kristo0aa29302021-06-11 11:45:13 +0300188
189 ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN;
190 ctrl |= PLL_16FFT_CTRL_PLL_EN;
191 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
192
193 ret = ti_pll_wait_for_lock(clk);
194 if (ret)
195 return ret;
196
197 debug("%s: pllm=%u, plld=%u, pllfm=%u, parent_freq=%u\n",
198 __func__, (u32)pllm, (u32)plld, (u32)pllfm, (u32)parent_freq);
199
200 current_freq = parent_freq * pllm / plld;
201
202 if (pllfm) {
203 u64 tmp;
204
205 tmp = parent_freq * pllfm;
206 do_div(tmp, plld);
207 tmp >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
208 current_freq += tmp;
209 }
210
211 return current_freq;
212}
213
214static int ti_pll_clk_enable(struct clk *clk)
215{
216 struct ti_pll_clk *pll = to_clk_pll(clk);
217 u32 ctrl;
218
219 ctrl = readl(pll->reg + PLL_16FFT_CTRL);
220 ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN;
221 ctrl |= PLL_16FFT_CTRL_PLL_EN;
222 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
223
224 return ti_pll_wait_for_lock(clk);
225}
226
227static int ti_pll_clk_disable(struct clk *clk)
228{
229 struct ti_pll_clk *pll = to_clk_pll(clk);
230 u32 ctrl;
231
232 ctrl = readl(pll->reg + PLL_16FFT_CTRL);
233 ctrl |= PLL_16FFT_CTRL_BYPASS_EN;
234 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
235
236 return 0;
237}
238
239static const struct clk_ops ti_pll_clk_ops = {
240 .get_rate = ti_pll_clk_get_rate,
241 .set_rate = ti_pll_clk_set_rate,
242 .enable = ti_pll_clk_enable,
243 .disable = ti_pll_clk_disable,
244};
245
246struct clk *clk_register_ti_pll(const char *name, const char *parent_name,
247 void __iomem *reg)
248{
249 struct ti_pll_clk *pll;
250 int ret;
251 int i;
252 u32 cfg, ctrl, hsdiv_presence_bit, hsdiv_ctrl_offs;
253
254 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
255 if (!pll)
256 return ERR_PTR(-ENOMEM);
257
258 pll->reg = reg;
259
260 ret = clk_register(&pll->clk, "ti-pll-clk", name, parent_name);
261 if (ret) {
262 printf("%s: failed to register: %d\n", __func__, ret);
263 kfree(pll);
264 return ERR_PTR(ret);
265 }
266
267 /* Unlock the PLL registers */
268 writel(PLL_KICK0_VALUE, pll->reg + PLL_KICK0);
269 writel(PLL_KICK1_VALUE, pll->reg + PLL_KICK1);
270
271 /* Enable all HSDIV outputs */
272 cfg = readl(pll->reg + PLL_16FFT_CFG);
273 for (i = 0; i < 16; i++) {
274 hsdiv_presence_bit = BIT(16 + i);
275 hsdiv_ctrl_offs = 0x80 + (i * 4);
276 /* Enable HSDIV output if present */
277 if ((hsdiv_presence_bit & cfg) != 0UL) {
278 ctrl = readl(pll->reg + hsdiv_ctrl_offs);
279 ctrl |= PLL_16FFT_HSDIV_CTRL_CLKOUT_EN;
280 writel(ctrl, pll->reg + hsdiv_ctrl_offs);
281 }
282 }
283
284 return &pll->clk;
285}
286
287U_BOOT_DRIVER(ti_pll_clk) = {
288 .name = "ti-pll-clk",
289 .id = UCLASS_CLK,
290 .ops = &ti_pll_clk_ops,
291 .flags = DM_FLAG_PRE_RELOC,
292};