blob: bf2407a020a3cd0823c9a058ffe6699f8d775121 [file] [log] [blame]
Tero Kristo81744b72021-06-11 11:45:13 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Texas Instruments K3 SoC PLL clock driver
4 *
5 * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
6 * 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;
125 u32 rem;
126 int shift;
127
128 debug("%s(clk=%p, rate=%u)\n", __func__, clk, (u32)rate);
129
130 if (ti_pll_clk_get_rate(clk) == rate)
131 return rate;
132
133 if (rate != parent_freq)
134 /*
135 * Attempt with higher max multiplier value first to give
136 * some space for fractional divider to kick in.
137 */
138 for (shift = 8; shift >= 0; shift -= 8) {
139 rational_best_approximation(rate, parent_freq,
140 ((PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK + 1) << shift) - 1,
141 PLL_16FFT_DIV_CTRL_REF_DIV_MASK, &pllm, &plld);
142 if (pllm / plld <= PLL_16FFT_FREQ_CTRL0_FB_DIV_INT_MASK)
143 break;
144 }
145
146 /* Put PLL to bypass mode */
147 ctrl = readl(pll->reg + PLL_16FFT_CTRL);
148 ctrl |= PLL_16FFT_CTRL_BYPASS_EN;
149 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
150
151 if (rate == parent_freq) {
152 debug("%s: put %s to bypass\n", __func__, clk->dev->name);
153 return rate;
154 }
155
156 debug("%s: pre-frac-calc: rate=%u, parent_freq=%u, plld=%u, pllm=%u\n",
157 __func__, (u32)rate, (u32)parent_freq, (u32)plld, (u32)pllm);
158
159 /* Check if we need fractional config */
160 if (plld > 1) {
161 pllfm = pllm % plld;
162 pllfm <<= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
163 rem = pllfm % plld;
164 pllfm /= plld;
165 if (rem)
166 pllfm++;
167 pllm /= plld;
168 plld = 1;
169 }
170
171 if (pllfm)
172 ctrl |= PLL_16FFT_CTRL_DSM_EN;
173 else
174 ctrl &= ~PLL_16FFT_CTRL_DSM_EN;
175
176 writel(pllm, pll->reg + PLL_16FFT_FREQ_CTRL0);
177 writel(pllfm, pll->reg + PLL_16FFT_FREQ_CTRL1);
178 writel(plld, pll->reg + PLL_16FFT_DIV_CTRL);
179
180 ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN;
181 ctrl |= PLL_16FFT_CTRL_PLL_EN;
182 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
183
184 ret = ti_pll_wait_for_lock(clk);
185 if (ret)
186 return ret;
187
188 debug("%s: pllm=%u, plld=%u, pllfm=%u, parent_freq=%u\n",
189 __func__, (u32)pllm, (u32)plld, (u32)pllfm, (u32)parent_freq);
190
191 current_freq = parent_freq * pllm / plld;
192
193 if (pllfm) {
194 u64 tmp;
195
196 tmp = parent_freq * pllfm;
197 do_div(tmp, plld);
198 tmp >>= PLL_16FFT_FREQ_CTRL1_FB_DIV_FRAC_BITS;
199 current_freq += tmp;
200 }
201
202 return current_freq;
203}
204
205static int ti_pll_clk_enable(struct clk *clk)
206{
207 struct ti_pll_clk *pll = to_clk_pll(clk);
208 u32 ctrl;
209
210 ctrl = readl(pll->reg + PLL_16FFT_CTRL);
211 ctrl &= ~PLL_16FFT_CTRL_BYPASS_EN;
212 ctrl |= PLL_16FFT_CTRL_PLL_EN;
213 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
214
215 return ti_pll_wait_for_lock(clk);
216}
217
218static int ti_pll_clk_disable(struct clk *clk)
219{
220 struct ti_pll_clk *pll = to_clk_pll(clk);
221 u32 ctrl;
222
223 ctrl = readl(pll->reg + PLL_16FFT_CTRL);
224 ctrl |= PLL_16FFT_CTRL_BYPASS_EN;
225 writel(ctrl, pll->reg + PLL_16FFT_CTRL);
226
227 return 0;
228}
229
230static const struct clk_ops ti_pll_clk_ops = {
231 .get_rate = ti_pll_clk_get_rate,
232 .set_rate = ti_pll_clk_set_rate,
233 .enable = ti_pll_clk_enable,
234 .disable = ti_pll_clk_disable,
235};
236
237struct clk *clk_register_ti_pll(const char *name, const char *parent_name,
238 void __iomem *reg)
239{
240 struct ti_pll_clk *pll;
241 int ret;
242 int i;
243 u32 cfg, ctrl, hsdiv_presence_bit, hsdiv_ctrl_offs;
244
245 pll = kzalloc(sizeof(*pll), GFP_KERNEL);
246 if (!pll)
247 return ERR_PTR(-ENOMEM);
248
249 pll->reg = reg;
250
251 ret = clk_register(&pll->clk, "ti-pll-clk", name, parent_name);
252 if (ret) {
253 printf("%s: failed to register: %d\n", __func__, ret);
254 kfree(pll);
255 return ERR_PTR(ret);
256 }
257
258 /* Unlock the PLL registers */
259 writel(PLL_KICK0_VALUE, pll->reg + PLL_KICK0);
260 writel(PLL_KICK1_VALUE, pll->reg + PLL_KICK1);
261
262 /* Enable all HSDIV outputs */
263 cfg = readl(pll->reg + PLL_16FFT_CFG);
264 for (i = 0; i < 16; i++) {
265 hsdiv_presence_bit = BIT(16 + i);
266 hsdiv_ctrl_offs = 0x80 + (i * 4);
267 /* Enable HSDIV output if present */
268 if ((hsdiv_presence_bit & cfg) != 0UL) {
269 ctrl = readl(pll->reg + hsdiv_ctrl_offs);
270 ctrl |= PLL_16FFT_HSDIV_CTRL_CLKOUT_EN;
271 writel(ctrl, pll->reg + hsdiv_ctrl_offs);
272 }
273 }
274
275 return &pll->clk;
276}
277
278U_BOOT_DRIVER(ti_pll_clk) = {
279 .name = "ti-pll-clk",
280 .id = UCLASS_CLK,
281 .ops = &ti_pll_clk_ops,
282 .flags = DM_FLAG_PRE_RELOC,
283};