Commit | Line | Data |
---|---|---|
b4cbe606 NI |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Toshiba Visconti PLL driver | |
4 | * | |
5 | * Copyright (c) 2021 TOSHIBA CORPORATION | |
6 | * Copyright (c) 2021 Toshiba Electronic Devices & Storage Corporation | |
7 | * | |
8 | * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> | |
9 | */ | |
10 | ||
11 | #include <linux/bitfield.h> | |
12 | #include <linux/clk-provider.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/slab.h> | |
15 | #include <linux/io.h> | |
16 | ||
17 | #include "pll.h" | |
18 | ||
19 | struct visconti_pll { | |
20 | struct clk_hw hw; | |
21 | void __iomem *pll_base; | |
22 | spinlock_t *lock; | |
23 | unsigned long flags; | |
24 | const struct visconti_pll_rate_table *rate_table; | |
25 | size_t rate_count; | |
26 | struct visconti_pll_provider *ctx; | |
27 | }; | |
28 | ||
29 | #define PLL_CONF_REG 0x0000 | |
30 | #define PLL_CTRL_REG 0x0004 | |
31 | #define PLL_FRACMODE_REG 0x0010 | |
32 | #define PLL_INTIN_REG 0x0014 | |
33 | #define PLL_FRACIN_REG 0x0018 | |
34 | #define PLL_REFDIV_REG 0x001c | |
35 | #define PLL_POSTDIV_REG 0x0020 | |
36 | ||
37 | #define PLL_CONFIG_SEL BIT(0) | |
38 | #define PLL_PLLEN BIT(4) | |
39 | #define PLL_BYPASS BIT(16) | |
40 | #define PLL_INTIN_MASK GENMASK(11, 0) | |
41 | #define PLL_FRACIN_MASK GENMASK(23, 0) | |
42 | #define PLL_REFDIV_MASK GENMASK(5, 0) | |
43 | #define PLL_POSTDIV_MASK GENMASK(2, 0) | |
44 | ||
45 | #define PLL0_FRACMODE_DACEN BIT(4) | |
46 | #define PLL0_FRACMODE_DSMEN BIT(0) | |
47 | ||
48 | #define PLL_CREATE_FRACMODE(table) (table->dacen << 4 | table->dsmen) | |
49 | #define PLL_CREATE_OSTDIV(table) (table->postdiv2 << 4 | table->postdiv1) | |
50 | ||
51 | static inline struct visconti_pll *to_visconti_pll(struct clk_hw *hw) | |
52 | { | |
53 | return container_of(hw, struct visconti_pll, hw); | |
54 | } | |
55 | ||
56 | static void visconti_pll_get_params(struct visconti_pll *pll, | |
57 | struct visconti_pll_rate_table *rate_table) | |
58 | { | |
59 | u32 postdiv, val; | |
60 | ||
61 | val = readl(pll->pll_base + PLL_FRACMODE_REG); | |
62 | ||
63 | rate_table->dacen = FIELD_GET(PLL0_FRACMODE_DACEN, val); | |
64 | rate_table->dsmen = FIELD_GET(PLL0_FRACMODE_DSMEN, val); | |
65 | ||
66 | rate_table->fracin = readl(pll->pll_base + PLL_FRACIN_REG) & PLL_FRACIN_MASK; | |
67 | rate_table->intin = readl(pll->pll_base + PLL_INTIN_REG) & PLL_INTIN_MASK; | |
68 | rate_table->refdiv = readl(pll->pll_base + PLL_REFDIV_REG) & PLL_REFDIV_MASK; | |
69 | ||
70 | postdiv = readl(pll->pll_base + PLL_POSTDIV_REG); | |
71 | rate_table->postdiv1 = postdiv & PLL_POSTDIV_MASK; | |
72 | rate_table->postdiv2 = (postdiv >> 4) & PLL_POSTDIV_MASK; | |
73 | } | |
74 | ||
75 | static const struct visconti_pll_rate_table *visconti_get_pll_settings(struct visconti_pll *pll, | |
76 | unsigned long rate) | |
77 | { | |
78 | const struct visconti_pll_rate_table *rate_table = pll->rate_table; | |
79 | int i; | |
80 | ||
81 | for (i = 0; i < pll->rate_count; i++) | |
82 | if (rate == rate_table[i].rate) | |
83 | return &rate_table[i]; | |
84 | ||
85 | return NULL; | |
86 | } | |
87 | ||
88 | static unsigned long visconti_get_pll_rate_from_data(struct visconti_pll *pll, | |
89 | const struct visconti_pll_rate_table *rate) | |
90 | { | |
91 | const struct visconti_pll_rate_table *rate_table = pll->rate_table; | |
92 | int i; | |
93 | ||
94 | for (i = 0; i < pll->rate_count; i++) | |
95 | if (memcmp(&rate_table[i].dacen, &rate->dacen, | |
96 | sizeof(*rate) - sizeof(unsigned long)) == 0) | |
97 | return rate_table[i].rate; | |
98 | ||
99 | /* set default */ | |
100 | return rate_table[0].rate; | |
101 | } | |
102 | ||
103 | static long visconti_pll_round_rate(struct clk_hw *hw, | |
104 | unsigned long rate, unsigned long *prate) | |
105 | { | |
106 | struct visconti_pll *pll = to_visconti_pll(hw); | |
107 | const struct visconti_pll_rate_table *rate_table = pll->rate_table; | |
108 | int i; | |
109 | ||
110 | /* Assumming rate_table is in descending order */ | |
111 | for (i = 0; i < pll->rate_count; i++) | |
112 | if (rate >= rate_table[i].rate) | |
113 | return rate_table[i].rate; | |
114 | ||
115 | /* return minimum supported value */ | |
116 | return rate_table[i - 1].rate; | |
117 | } | |
118 | ||
119 | static unsigned long visconti_pll_recalc_rate(struct clk_hw *hw, | |
120 | unsigned long parent_rate) | |
121 | { | |
122 | struct visconti_pll *pll = to_visconti_pll(hw); | |
123 | struct visconti_pll_rate_table rate_table; | |
124 | ||
125 | memset(&rate_table, 0, sizeof(rate_table)); | |
126 | visconti_pll_get_params(pll, &rate_table); | |
127 | ||
128 | return visconti_get_pll_rate_from_data(pll, &rate_table); | |
129 | } | |
130 | ||
131 | static int visconti_pll_set_params(struct visconti_pll *pll, | |
132 | const struct visconti_pll_rate_table *rate_table) | |
133 | { | |
134 | writel(PLL_CREATE_FRACMODE(rate_table), pll->pll_base + PLL_FRACMODE_REG); | |
135 | writel(PLL_CREATE_OSTDIV(rate_table), pll->pll_base + PLL_POSTDIV_REG); | |
136 | writel(rate_table->intin, pll->pll_base + PLL_INTIN_REG); | |
137 | writel(rate_table->fracin, pll->pll_base + PLL_FRACIN_REG); | |
138 | writel(rate_table->refdiv, pll->pll_base + PLL_REFDIV_REG); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | static int visconti_pll_set_rate(struct clk_hw *hw, unsigned long rate, | |
144 | unsigned long parent_rate) | |
145 | { | |
146 | struct visconti_pll *pll = to_visconti_pll(hw); | |
147 | const struct visconti_pll_rate_table *rate_table; | |
148 | ||
149 | rate_table = visconti_get_pll_settings(pll, rate); | |
150 | if (!rate_table) | |
151 | return -EINVAL; | |
152 | ||
153 | return visconti_pll_set_params(pll, rate_table); | |
154 | } | |
155 | ||
156 | static int visconti_pll_is_enabled(struct clk_hw *hw) | |
157 | { | |
158 | struct visconti_pll *pll = to_visconti_pll(hw); | |
159 | u32 reg; | |
160 | ||
161 | reg = readl(pll->pll_base + PLL_CTRL_REG); | |
162 | ||
163 | return (reg & PLL_PLLEN); | |
164 | } | |
165 | ||
166 | static int visconti_pll_enable(struct clk_hw *hw) | |
167 | { | |
168 | struct visconti_pll *pll = to_visconti_pll(hw); | |
169 | const struct visconti_pll_rate_table *rate_table = pll->rate_table; | |
170 | unsigned long flags; | |
171 | u32 reg; | |
172 | ||
173 | if (visconti_pll_is_enabled(hw)) | |
174 | return 0; | |
175 | ||
176 | spin_lock_irqsave(pll->lock, flags); | |
177 | ||
178 | writel(PLL_CONFIG_SEL, pll->pll_base + PLL_CONF_REG); | |
179 | ||
180 | reg = readl(pll->pll_base + PLL_CTRL_REG); | |
181 | reg |= PLL_BYPASS; | |
182 | writel(reg, pll->pll_base + PLL_CTRL_REG); | |
183 | ||
184 | visconti_pll_set_params(pll, &rate_table[0]); | |
185 | ||
186 | reg = readl(pll->pll_base + PLL_CTRL_REG); | |
187 | reg &= ~PLL_PLLEN; | |
188 | writel(reg, pll->pll_base + PLL_CTRL_REG); | |
189 | ||
190 | udelay(1); | |
191 | ||
192 | reg = readl(pll->pll_base + PLL_CTRL_REG); | |
193 | reg |= PLL_PLLEN; | |
194 | writel(reg, pll->pll_base + PLL_CTRL_REG); | |
195 | ||
196 | udelay(40); | |
197 | ||
198 | reg = readl(pll->pll_base + PLL_CTRL_REG); | |
199 | reg &= ~PLL_BYPASS; | |
200 | writel(reg, pll->pll_base + PLL_CTRL_REG); | |
201 | ||
202 | spin_unlock_irqrestore(pll->lock, flags); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static void visconti_pll_disable(struct clk_hw *hw) | |
208 | { | |
209 | struct visconti_pll *pll = to_visconti_pll(hw); | |
210 | unsigned long flags; | |
211 | u32 reg; | |
212 | ||
213 | if (!visconti_pll_is_enabled(hw)) | |
214 | return; | |
215 | ||
216 | spin_lock_irqsave(pll->lock, flags); | |
217 | ||
218 | writel(PLL_CONFIG_SEL, pll->pll_base + PLL_CONF_REG); | |
219 | ||
220 | reg = readl(pll->pll_base + PLL_CTRL_REG); | |
221 | reg |= PLL_BYPASS; | |
222 | writel(reg, pll->pll_base + PLL_CTRL_REG); | |
223 | ||
224 | reg = readl(pll->pll_base + PLL_CTRL_REG); | |
225 | reg &= ~PLL_PLLEN; | |
226 | writel(reg, pll->pll_base + PLL_CTRL_REG); | |
227 | ||
228 | spin_unlock_irqrestore(pll->lock, flags); | |
229 | } | |
230 | ||
231 | static const struct clk_ops visconti_pll_ops = { | |
232 | .enable = visconti_pll_enable, | |
233 | .disable = visconti_pll_disable, | |
234 | .is_enabled = visconti_pll_is_enabled, | |
235 | .round_rate = visconti_pll_round_rate, | |
236 | .recalc_rate = visconti_pll_recalc_rate, | |
237 | .set_rate = visconti_pll_set_rate, | |
238 | }; | |
239 | ||
240 | static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, | |
241 | const char *name, | |
242 | const char *parent_name, | |
243 | int offset, | |
244 | const struct visconti_pll_rate_table *rate_table, | |
245 | spinlock_t *lock) | |
246 | { | |
247 | struct clk_init_data init; | |
248 | struct visconti_pll *pll; | |
b4cbe606 NI |
249 | struct clk_hw *pll_hw_clk; |
250 | size_t len; | |
251 | int ret; | |
252 | ||
253 | pll = kzalloc(sizeof(*pll), GFP_KERNEL); | |
254 | if (!pll) | |
255 | return ERR_PTR(-ENOMEM); | |
256 | ||
257 | init.name = name; | |
258 | init.flags = CLK_IGNORE_UNUSED; | |
259 | init.parent_names = &parent_name; | |
260 | init.num_parents = 1; | |
261 | ||
262 | for (len = 0; rate_table[len].rate != 0; ) | |
263 | len++; | |
264 | pll->rate_count = len; | |
265 | pll->rate_table = kmemdup(rate_table, | |
266 | pll->rate_count * sizeof(struct visconti_pll_rate_table), | |
267 | GFP_KERNEL); | |
268 | WARN(!pll->rate_table, "%s: could not allocate rate table for %s\n", __func__, name); | |
269 | ||
270 | init.ops = &visconti_pll_ops; | |
271 | pll->hw.init = &init; | |
272 | pll->pll_base = ctx->reg_base + offset; | |
273 | pll->lock = lock; | |
274 | pll->ctx = ctx; | |
275 | ||
276 | pll_hw_clk = &pll->hw; | |
277 | ret = clk_hw_register(NULL, &pll->hw); | |
278 | if (ret) { | |
818d9150 | 279 | pr_err("failed to register pll clock %s : %d\n", name, ret); |
b4cbe606 NI |
280 | kfree(pll); |
281 | pll_hw_clk = ERR_PTR(ret); | |
282 | } | |
283 | ||
284 | return pll_hw_clk; | |
285 | } | |
286 | ||
287 | static void visconti_pll_add_lookup(struct visconti_pll_provider *ctx, | |
288 | struct clk_hw *hw_clk, | |
289 | unsigned int id) | |
290 | { | |
4e31bfa3 | 291 | if (id) |
b4cbe606 NI |
292 | ctx->clk_data.hws[id] = hw_clk; |
293 | } | |
294 | ||
295 | void __init visconti_register_plls(struct visconti_pll_provider *ctx, | |
296 | const struct visconti_pll_info *list, | |
297 | unsigned int nr_plls, | |
298 | spinlock_t *lock) | |
299 | { | |
300 | int idx; | |
301 | ||
302 | for (idx = 0; idx < nr_plls; idx++, list++) { | |
303 | struct clk_hw *clk; | |
304 | ||
305 | clk = visconti_register_pll(ctx, | |
306 | list->name, | |
307 | list->parent, | |
308 | list->base_reg, | |
309 | list->rate_table, | |
310 | lock); | |
311 | if (IS_ERR(clk)) { | |
312 | pr_err("failed to register clock %s\n", list->name); | |
313 | continue; | |
314 | } | |
315 | ||
316 | visconti_pll_add_lookup(ctx, clk, list->id); | |
317 | } | |
318 | } | |
319 | ||
320 | struct visconti_pll_provider * __init visconti_init_pll(struct device_node *np, | |
321 | void __iomem *base, | |
322 | unsigned long nr_plls) | |
323 | { | |
324 | struct visconti_pll_provider *ctx; | |
325 | int i; | |
326 | ||
327 | ctx = kzalloc(struct_size(ctx, clk_data.hws, nr_plls), GFP_KERNEL); | |
328 | if (!ctx) | |
329 | return ERR_PTR(-ENOMEM); | |
330 | ||
331 | for (i = 0; i < nr_plls; ++i) | |
332 | ctx->clk_data.hws[i] = ERR_PTR(-ENOENT); | |
333 | ||
334 | ctx->node = np; | |
335 | ctx->reg_base = base; | |
336 | ctx->clk_data.num = nr_plls; | |
337 | ||
338 | return ctx; | |
339 | } |