blob: 8d71f2a24b82f27531e8972edb60109fa8cb487b [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
138static u32 npcm_clk_set_div(struct clk *clk, u32 div)
139{
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
148 if (divider->flags & PRE_DIV2)
149 div = div >> 1;
150
151 if (divider->flags & DIV_TYPE1)
152 clkdiv = div - 1;
153 else
154 clkdiv = ilog2(div);
155
156 val = readl(priv->base + divider->reg);
157 val &= ~divider->mask;
158 val |= (clkdiv << (ffs(divider->mask) - 1)) & divider->mask;
159 writel(val, priv->base + divider->reg);
160
161 return 0;
162}
163
164static ulong npcm_clk_get_fout(struct clk *clk)
165{
166 ulong parent_rate;
167 u32 div;
168
169 parent_rate = npcm_clk_get_fin(clk);
170 if (!parent_rate)
171 return -EINVAL;
172
173 div = npcm_clk_get_div(clk);
174 if (!div)
175 return -EINVAL;
176
177 debug("fout of clk%lu = (%lu / %u)\n", clk->id, parent_rate, div);
178 return (parent_rate / div);
179}
180
181static ulong npcm_clk_get_pll_fout(struct clk *clk)
182{
183 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
184 struct npcm_clk_pll *pll;
185 struct clk parent;
186 ulong parent_rate;
187 ulong fbdv, indv, otdv1, otdv2;
188 u32 val;
189 u64 ret;
190
191 pll = npcm_clk_pll_get(priv->clk_data, clk->id);
192 if (!pll)
193 return -ENODEV;
194
195 parent.id = pll->parent_id;
196 ret = clk_request(clk->dev, &parent);
197 if (ret)
198 return ret;
199
200 parent_rate = clk_get_rate(&parent);
201
202 val = readl(priv->base + pll->reg);
203 indv = FIELD_GET(PLLCON_INDV, val);
204 fbdv = FIELD_GET(PLLCON_FBDV, val);
205 otdv1 = FIELD_GET(PLLCON_OTDV1, val);
206 otdv2 = FIELD_GET(PLLCON_OTDV2, val);
207
208 ret = (u64)parent_rate * fbdv;
209 do_div(ret, indv * otdv1 * otdv2);
210 if (pll->flags & POST_DIV2)
211 do_div(ret, 2);
212
213 debug("fout of pll(id %lu) = %llu\n", clk->id, ret);
214 return ret;
215}
216
217static ulong npcm_clk_get_rate(struct clk *clk)
218{
219 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
220 struct npcm_clk_data *clk_data = priv->clk_data;
221 struct clk refclk;
222 int ret;
223
224 debug("%s: id %lu\n", __func__, clk->id);
225 if (clk->id == clk_data->refclk_id) {
226 ret = clk_get_by_name(clk->dev, "refclk", &refclk);
227 if (!ret)
228 return clk_get_rate(&refclk);
229 else
230 return ret;
231 }
232
233 if (clk->id >= clk_data->pll0_id &&
234 clk->id < clk_data->pll0_id + clk_data->num_plls)
235 return npcm_clk_get_pll_fout(clk);
236 else
237 return npcm_clk_get_fout(clk);
238}
239
240static ulong npcm_clk_set_rate(struct clk *clk, ulong rate)
241{
242 ulong parent_rate;
243 u32 div;
244 int ret;
245
246 debug("%s: id %lu, rate %lu\n", __func__, clk->id, rate);
247 parent_rate = npcm_clk_get_fin(clk);
248 if (!parent_rate)
249 return -EINVAL;
250
251 div = DIV_ROUND_UP(parent_rate, rate);
252 ret = npcm_clk_set_div(clk, div);
253 if (ret)
254 return ret;
255
256 debug("%s: rate %lu, new rate (%lu / %u)\n", __func__, rate, parent_rate, div);
257 return (parent_rate / div);
258}
259
260static int npcm_clk_set_parent(struct clk *clk, struct clk *parent)
261{
262 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
263 struct npcm_clk_select *selector;
264 int clksel;
265 u32 val;
266
267 debug("%s: id %lu, parent %lu\n", __func__, clk->id, parent->id);
268 selector = npcm_clk_selector_get(priv->clk_data, clk->id);
269 if (!selector)
270 return -EINVAL;
271
272 clksel = clkid_to_clksel(selector, parent->id);
273 if (clksel < 0)
274 return -EINVAL;
275
276 val = readl(priv->base + selector->reg);
277 val &= ~selector->mask;
278 val |= clksel << (ffs(selector->mask) - 1);
279 writel(val, priv->base + selector->reg);
280
281 return 0;
282}
283
284static int npcm_clk_request(struct clk *clk)
285{
286 struct npcm_clk_priv *priv = dev_get_priv(clk->dev);
287
288 if (clk->id >= priv->num_clks)
289 return -EINVAL;
290
291 return 0;
292}
293
294const struct clk_ops npcm_clk_ops = {
295 .get_rate = npcm_clk_get_rate,
296 .set_rate = npcm_clk_set_rate,
297 .set_parent = npcm_clk_set_parent,
298 .request = npcm_clk_request,
299};