Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
19struct 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
51static inline struct visconti_pll *to_visconti_pll(struct clk_hw *hw)
52{
53 return container_of(hw, struct visconti_pll, hw);
54}
55
56static 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
75static 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
88static 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
103static int visconti_pll_determine_rate(struct clk_hw *hw,
104 struct clk_rate_request *req)
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 /* Assuming rate_table is in descending order */
111 for (i = 0; i < pll->rate_count; i++)
112 if (req->rate >= rate_table[i].rate) {
113 req->rate = rate_table[i].rate;
114
115 return 0;
116 }
117
118 /* return minimum supported value */
119 req->rate = rate_table[i - 1].rate;
120
121 return 0;
122}
123
124static unsigned long visconti_pll_recalc_rate(struct clk_hw *hw,
125 unsigned long parent_rate)
126{
127 struct visconti_pll *pll = to_visconti_pll(hw);
128 struct visconti_pll_rate_table rate_table;
129
130 memset(&rate_table, 0, sizeof(rate_table));
131 visconti_pll_get_params(pll, &rate_table);
132
133 return visconti_get_pll_rate_from_data(pll, &rate_table);
134}
135
136static int visconti_pll_set_params(struct visconti_pll *pll,
137 const struct visconti_pll_rate_table *rate_table)
138{
139 writel(PLL_CREATE_FRACMODE(rate_table), pll->pll_base + PLL_FRACMODE_REG);
140 writel(PLL_CREATE_OSTDIV(rate_table), pll->pll_base + PLL_POSTDIV_REG);
141 writel(rate_table->intin, pll->pll_base + PLL_INTIN_REG);
142 writel(rate_table->fracin, pll->pll_base + PLL_FRACIN_REG);
143 writel(rate_table->refdiv, pll->pll_base + PLL_REFDIV_REG);
144
145 return 0;
146}
147
148static int visconti_pll_set_rate(struct clk_hw *hw, unsigned long rate,
149 unsigned long parent_rate)
150{
151 struct visconti_pll *pll = to_visconti_pll(hw);
152 const struct visconti_pll_rate_table *rate_table;
153
154 rate_table = visconti_get_pll_settings(pll, rate);
155 if (!rate_table)
156 return -EINVAL;
157
158 return visconti_pll_set_params(pll, rate_table);
159}
160
161static int visconti_pll_is_enabled(struct clk_hw *hw)
162{
163 struct visconti_pll *pll = to_visconti_pll(hw);
164 u32 reg;
165
166 reg = readl(pll->pll_base + PLL_CTRL_REG);
167
168 return (reg & PLL_PLLEN);
169}
170
171static int visconti_pll_enable(struct clk_hw *hw)
172{
173 struct visconti_pll *pll = to_visconti_pll(hw);
174 const struct visconti_pll_rate_table *rate_table = pll->rate_table;
175 unsigned long flags;
176 u32 reg;
177
178 if (visconti_pll_is_enabled(hw))
179 return 0;
180
181 spin_lock_irqsave(pll->lock, flags);
182
183 writel(PLL_CONFIG_SEL, pll->pll_base + PLL_CONF_REG);
184
185 reg = readl(pll->pll_base + PLL_CTRL_REG);
186 reg |= PLL_BYPASS;
187 writel(reg, pll->pll_base + PLL_CTRL_REG);
188
189 visconti_pll_set_params(pll, &rate_table[0]);
190
191 reg = readl(pll->pll_base + PLL_CTRL_REG);
192 reg &= ~PLL_PLLEN;
193 writel(reg, pll->pll_base + PLL_CTRL_REG);
194
195 udelay(1);
196
197 reg = readl(pll->pll_base + PLL_CTRL_REG);
198 reg |= PLL_PLLEN;
199 writel(reg, pll->pll_base + PLL_CTRL_REG);
200
201 udelay(40);
202
203 reg = readl(pll->pll_base + PLL_CTRL_REG);
204 reg &= ~PLL_BYPASS;
205 writel(reg, pll->pll_base + PLL_CTRL_REG);
206
207 spin_unlock_irqrestore(pll->lock, flags);
208
209 return 0;
210}
211
212static void visconti_pll_disable(struct clk_hw *hw)
213{
214 struct visconti_pll *pll = to_visconti_pll(hw);
215 unsigned long flags;
216 u32 reg;
217
218 if (!visconti_pll_is_enabled(hw))
219 return;
220
221 spin_lock_irqsave(pll->lock, flags);
222
223 writel(PLL_CONFIG_SEL, pll->pll_base + PLL_CONF_REG);
224
225 reg = readl(pll->pll_base + PLL_CTRL_REG);
226 reg |= PLL_BYPASS;
227 writel(reg, pll->pll_base + PLL_CTRL_REG);
228
229 reg = readl(pll->pll_base + PLL_CTRL_REG);
230 reg &= ~PLL_PLLEN;
231 writel(reg, pll->pll_base + PLL_CTRL_REG);
232
233 spin_unlock_irqrestore(pll->lock, flags);
234}
235
236static const struct clk_ops visconti_pll_ops = {
237 .enable = visconti_pll_enable,
238 .disable = visconti_pll_disable,
239 .is_enabled = visconti_pll_is_enabled,
240 .determine_rate = visconti_pll_determine_rate,
241 .recalc_rate = visconti_pll_recalc_rate,
242 .set_rate = visconti_pll_set_rate,
243};
244
245static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx,
246 const char *name,
247 const char *parent_name,
248 int offset,
249 const struct visconti_pll_rate_table *rate_table,
250 spinlock_t *lock)
251{
252 struct clk_init_data init = {};
253 struct visconti_pll *pll;
254 struct clk_hw *pll_hw_clk;
255 size_t len;
256 int ret;
257
258 pll = kzalloc_obj(*pll);
259 if (!pll)
260 return ERR_PTR(-ENOMEM);
261
262 init.name = name;
263 init.flags = CLK_IGNORE_UNUSED;
264 init.parent_names = &parent_name;
265 init.num_parents = 1;
266
267 for (len = 0; rate_table[len].rate != 0; )
268 len++;
269 pll->rate_count = len;
270 pll->rate_table = kmemdup_array(rate_table,
271 pll->rate_count, sizeof(*pll->rate_table),
272 GFP_KERNEL);
273 WARN(!pll->rate_table, "%s: could not allocate rate table for %s\n", __func__, name);
274
275 init.ops = &visconti_pll_ops;
276 pll->hw.init = &init;
277 pll->pll_base = ctx->reg_base + offset;
278 pll->lock = lock;
279 pll->ctx = ctx;
280
281 pll_hw_clk = &pll->hw;
282 ret = clk_hw_register(NULL, &pll->hw);
283 if (ret) {
284 pr_err("failed to register pll clock %s : %d\n", name, ret);
285 kfree(pll->rate_table);
286 kfree(pll);
287 pll_hw_clk = ERR_PTR(ret);
288 }
289
290 return pll_hw_clk;
291}
292
293static void visconti_pll_add_lookup(struct visconti_pll_provider *ctx,
294 struct clk_hw *hw_clk,
295 unsigned int id)
296{
297 if (id)
298 ctx->clk_data.hws[id] = hw_clk;
299}
300
301void __init visconti_register_plls(struct visconti_pll_provider *ctx,
302 const struct visconti_pll_info *list,
303 unsigned int nr_plls,
304 spinlock_t *lock)
305{
306 int idx;
307
308 for (idx = 0; idx < nr_plls; idx++, list++) {
309 struct clk_hw *clk;
310
311 clk = visconti_register_pll(ctx,
312 list->name,
313 list->parent,
314 list->base_reg,
315 list->rate_table,
316 lock);
317 if (IS_ERR(clk)) {
318 pr_err("failed to register clock %s\n", list->name);
319 continue;
320 }
321
322 visconti_pll_add_lookup(ctx, clk, list->id);
323 }
324}
325
326struct visconti_pll_provider * __init visconti_init_pll(struct device_node *np,
327 void __iomem *base,
328 unsigned long nr_plls)
329{
330 struct visconti_pll_provider *ctx;
331 int i;
332
333 ctx = kzalloc_flex(*ctx, clk_data.hws, nr_plls);
334 if (!ctx)
335 return ERR_PTR(-ENOMEM);
336
337 ctx->node = np;
338 ctx->reg_base = base;
339 ctx->clk_data.num = nr_plls;
340
341 for (i = 0; i < nr_plls; ++i)
342 ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
343
344 return ctx;
345}