blob: 18cb9cddbf38f4f68f9d035079ff147a4409878e [file] [log] [blame]
Jim Liu25688562022-04-19 13:32:20 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2022 Nuvoton Technology Corp.
4 *
5 * Formula for calculating clock rate:
6 * Fout = ((Fin / PRE_DIV) / div) / POST_DIV
7 */
8
9#include <div64.h>
10#include <dm.h>
11#include <asm/io.h>
12#include <linux/bitfield.h>
13#include <linux/log2.h>
14#include "clk_npcm.h"
15
16static int clkid_to_clksel(struct npcm_clk_select *selector, int id)
17{
18 int i;
19
20 for (i = 0; i < selector->num_parents; i++) {
21 if (selector->parents[i].id == id)
22 return selector->parents[i].clksel;
23 }
24
25 return -EINVAL;
26}
27
28static int clksel_to_clkid(struct npcm_clk_select *selector, int clksel)
29{
30 int i;
31
32 for (i = 0; i < selector->num_parents; i++) {
33 if (selector->parents[i].clksel == clksel)
34 return selector->parents[i].id;
35 }
36
37 return -EINVAL;
38}
39
40static struct npcm_clk_pll *npcm_clk_pll_get(struct npcm_clk_data *clk_data, int id)
41{
42 struct npcm_clk_pll *pll = clk_data->clk_plls;
43 int i;
44
45 for (i = 0; i < clk_data->num_plls; i++) {
46 if (pll->id == id)
47 return pll;
48 pll++;
49 }
50
51 return NULL;
52}
53
54static struct npcm_clk_select *npcm_clk_selector_get(struct npcm_clk_data *clk_data,
55 int id)
56{
57 struct npcm_clk_select *selector = clk_data->clk_selectors;
58 int i;
59
60 for (i = 0; i < clk_data->num_selectors; i++) {
61 if (selector->id == id)
62 return selector;
63 selector++;
64 }
65
66 return NULL;
67}
68
69static struct npcm_clk_div *npcm_clk_divider_get(struct npcm_clk_data *clk_data,
70 int id)
71{
72 struct npcm_clk_div *divider = clk_data->clk_dividers;
73 int i;
74
75 for (i = 0; i < clk_data->num_dividers; i++) {
76 if (divider->id == id)
77 return divider;
78 divider++;
79 }
80
81 return NULL;
82}
83
84static ulong npcm_clk_get_fin(struct clk *clk)
85{
86 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
87 struct npcm_clk_select *selector;
88 struct clk parent;
89 ulong parent_rate;
90 u32 val, clksel;
91 int ret;
92
93 selector = npcm_clk_selector_get(priv->clk_data, clk->id);
94 if (!selector)
95 return 0;
96
97 if (selector->flags & FIXED_PARENT) {
98 clksel = 0;
99 } else {
100 val = readl(priv->base + selector->reg);
101 clksel = (val & selector->mask) >> (ffs(selector->mask) - 1);
102 }
103 parent.id = clksel_to_clkid(selector, clksel);
104
105 ret = clk_request(clk->dev, &parent);
106 if (ret)
107 return 0;
108
109 parent_rate = clk_get_rate(&parent);
110
111 debug("fin of clk%lu = %lu\n", clk->id, parent_rate);
112 return parent_rate;
113}
114
115static u32 npcm_clk_get_div(struct clk *clk)
116{
117 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
118 struct npcm_clk_div *divider;
119 u32 val, div;
120
121 divider = npcm_clk_divider_get(priv->clk_data, clk->id);
122 if (!divider)
123 return 0;
124
125 val = readl(priv->base + divider->reg);
126 div = (val & divider->mask) >> (ffs(divider->mask) - 1);
127 if (divider->flags & DIV_TYPE1)
128 div = div + 1;
129 else
130 div = 1 << div;
131
132 if (divider->flags & PRE_DIV2)
133 div = div << 1;
134
135 return div;
136}
137
Jim Liuf071d552023-11-14 17:00:04 +0800138static int npcm_clk_set_div(struct clk *clk, u32 div)
Jim Liu25688562022-04-19 13:32:20 +0800139{
140 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
141 struct npcm_clk_div *divider;
142 u32 val, clkdiv;
143
144 divider = npcm_clk_divider_get(priv->clk_data, clk->id);
145 if (!divider)
146 return -EINVAL;
147
Jim Liuf071d552023-11-14 17:00:04 +0800148 if (divider->flags & DIV_RO)
149 return 0;
150
Jim Liu25688562022-04-19 13:32:20 +0800151 if (divider->flags & PRE_DIV2)
152 div = div >> 1;
153
154 if (divider->flags & DIV_TYPE1)
155 clkdiv = div - 1;
156 else
157 clkdiv = ilog2(div);
158
Jim Liuf071d552023-11-14 17:00:04 +0800159 if (clkdiv > (divider->mask >> (ffs(divider->mask) - 1))) {
160 printf("clkdiv(%d) for clk(%ld) is over limit\n",
161 clkdiv, clk->id);
162 return -EINVAL;
163 }
164
Jim Liu25688562022-04-19 13:32:20 +0800165 val = readl(priv->base + divider->reg);
166 val &= ~divider->mask;
167 val |= (clkdiv << (ffs(divider->mask) - 1)) & divider->mask;
168 writel(val, priv->base + divider->reg);
169
170 return 0;
171}
172
173static ulong npcm_clk_get_fout(struct clk *clk)
174{
175 ulong parent_rate;
176 u32 div;
177
178 parent_rate = npcm_clk_get_fin(clk);
179 if (!parent_rate)
180 return -EINVAL;
181
182 div = npcm_clk_get_div(clk);
183 if (!div)
184 return -EINVAL;
185
186 debug("fout of clk%lu = (%lu / %u)\n", clk->id, parent_rate, div);
187 return (parent_rate / div);
188}
189
190static ulong npcm_clk_get_pll_fout(struct clk *clk)
191{
192 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
193 struct npcm_clk_pll *pll;
194 struct clk parent;
195 ulong parent_rate;
196 ulong fbdv, indv, otdv1, otdv2;
197 u32 val;
198 u64 ret;
199
200 pll = npcm_clk_pll_get(priv->clk_data, clk->id);
201 if (!pll)
202 return -ENODEV;
203
204 parent.id = pll->parent_id;
205 ret = clk_request(clk->dev, &parent);
206 if (ret)
207 return ret;
208
209 parent_rate = clk_get_rate(&parent);
210
211 val = readl(priv->base + pll->reg);
212 indv = FIELD_GET(PLLCON_INDV, val);
213 fbdv = FIELD_GET(PLLCON_FBDV, val);
214 otdv1 = FIELD_GET(PLLCON_OTDV1, val);
215 otdv2 = FIELD_GET(PLLCON_OTDV2, val);
216
217 ret = (u64)parent_rate * fbdv;
218 do_div(ret, indv * otdv1 * otdv2);
219 if (pll->flags & POST_DIV2)
220 do_div(ret, 2);
221
222 debug("fout of pll(id %lu) = %llu\n", clk->id, ret);
223 return ret;
224}
225
226static ulong npcm_clk_get_rate(struct clk *clk)
227{
228 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
229 struct npcm_clk_data *clk_data = priv->clk_data;
230 struct clk refclk;
231 int ret;
232
233 debug("%s: id %lu\n", __func__, clk->id);
234 if (clk->id == clk_data->refclk_id) {
235 ret = clk_get_by_name(clk->dev, "refclk", &refclk);
236 if (!ret)
237 return clk_get_rate(&refclk);
238 else
239 return ret;
240 }
241
242 if (clk->id >= clk_data->pll0_id &&
243 clk->id < clk_data->pll0_id + clk_data->num_plls)
244 return npcm_clk_get_pll_fout(clk);
245 else
246 return npcm_clk_get_fout(clk);
247}
248
249static ulong npcm_clk_set_rate(struct clk *clk, ulong rate)
250{
251 ulong parent_rate;
252 u32 div;
253 int ret;
254
255 debug("%s: id %lu, rate %lu\n", __func__, clk->id, rate);
256 parent_rate = npcm_clk_get_fin(clk);
257 if (!parent_rate)
258 return -EINVAL;
259
260 div = DIV_ROUND_UP(parent_rate, rate);
261 ret = npcm_clk_set_div(clk, div);
262 if (ret)
263 return ret;
264
Jim Liuf071d552023-11-14 17:00:04 +0800265 debug("%s: rate %lu, new rate %lu\n", __func__, rate, npcm_clk_get_rate(clk));
266 return npcm_clk_get_rate(clk);
Jim Liu25688562022-04-19 13:32:20 +0800267}
268
269static int npcm_clk_set_parent(struct clk *clk, struct clk *parent)
270{
271 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
272 struct npcm_clk_select *selector;
273 int clksel;
274 u32 val;
275
276 debug("%s: id %lu, parent %lu\n", __func__, clk->id, parent->id);
277 selector = npcm_clk_selector_get(priv->clk_data, clk->id);
278 if (!selector)
279 return -EINVAL;
280
281 clksel = clkid_to_clksel(selector, parent->id);
282 if (clksel < 0)
283 return -EINVAL;
284
285 val = readl(priv->base + selector->reg);
286 val &= ~selector->mask;
287 val |= clksel << (ffs(selector->mask) - 1);
288 writel(val, priv->base + selector->reg);
289
290 return 0;
291}
292
293static int npcm_clk_request(struct clk *clk)
294{
295 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
296
297 if (clk->id >= priv->num_clks)
298 return -EINVAL;
299
300 return 0;
301}
302
303const struct clk_ops npcm_clk_ops = {
304 .get_rate = npcm_clk_get_rate,
305 .set_rate = npcm_clk_set_rate,
306 .set_parent = npcm_clk_set_parent,
307 .request = npcm_clk_request,
308};