1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2026 UltraRISC Technology (Shanghai) Co., Ltd.
4 */
5
6 #include <linux/bitfield.h>
7 #include <linux/clk-provider.h>
8 #include <linux/io.h>
9 #include <linux/math64.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12
13 #include "clk-ultrarisc.h"
14
15 struct ultrarisc_pll_clk {
16 struct clk_hw hw;
17 void __iomem *base;
18 const struct ultrarisc_pll_layout *layout;
19 };
20
21 struct ultrarisc_divider_clk {
22 struct clk_divider divider;
23 struct clk_gate gate;
24 u32 load_mask;
25 };
26
27 #define to_ultrarisc_pll_clk(_hw) \
28 container_of(_hw, struct ultrarisc_pll_clk, hw)
29
to_ultrarisc_divider_clk(struct clk_hw * hw)30 static inline struct ultrarisc_divider_clk *to_ultrarisc_divider_clk(struct clk_hw *hw)
31 {
32 struct clk_divider *divider = to_clk_divider(hw);
33
34 return container_of(divider, struct ultrarisc_divider_clk, divider);
35 }
36
ultrarisc_pll_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)37 static unsigned long ultrarisc_pll_recalc_rate(struct clk_hw *hw,
38 unsigned long parent_rate)
39 {
40 struct ultrarisc_pll_clk *pll = to_ultrarisc_pll_clk(hw);
41 const struct ultrarisc_pll_layout *layout = pll->layout;
42 u32 oddiv1_div, oddiv2_div;
43 u64 mult, rate, den;
44 u32 frac, m, n;
45 u32 cfg1, cfg2;
46
47 cfg1 = readl_relaxed(pll->base + layout->cfg1_offset);
48 cfg2 = readl_relaxed(pll->base + layout->cfg2_offset);
49
50 frac = field_get(layout->frac_mask, cfg1);
51 m = field_get(layout->m_mask, cfg2);
52 n = field_get(layout->n_mask, cfg2);
53 if (!n)
54 return 0;
55
56 oddiv1_div = 1U << field_get(layout->oddiv1_mask, cfg2);
57 oddiv2_div = 1U << field_get(layout->oddiv2_mask, cfg2);
58
59 /*
60 * The output frequency is calculated as:
61 * fvco = parent * (m + frac / 2^24) / n
62 * fout = fvco / (2^oddiv1_raw * 2^oddiv2_raw)
63 *
64 * The output divider values are derived from the raw register field values as:
65 * oddivX_div = 1 << oddivX_raw
66 */
67 mult = ((u64)m << 24) + frac;
68 rate = (u64)parent_rate * mult;
69 den = ((u64)n << 24) * oddiv1_div * oddiv2_div;
70
71 return div64_u64(rate + (den >> 1), den);
72 }
73
74 static const struct clk_ops ultrarisc_pll_ro_ops = {
75 .recalc_rate = ultrarisc_pll_recalc_rate,
76 };
77
ultrarisc_divider_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)78 static unsigned long ultrarisc_divider_recalc_rate(struct clk_hw *hw,
79 unsigned long parent_rate)
80 {
81 struct clk_divider *divider = to_clk_divider(hw);
82 u32 val;
83
84 val = readl_relaxed(divider->reg) >> divider->shift;
85 val &= clk_div_mask(divider->width);
86
87 return divider_recalc_rate(hw, parent_rate, val, divider->table,
88 divider->flags, divider->width);
89 }
90
ultrarisc_divider_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)91 static int ultrarisc_divider_determine_rate(struct clk_hw *hw,
92 struct clk_rate_request *req)
93 {
94 struct clk_divider *divider = to_clk_divider(hw);
95
96 return divider_determine_rate(hw, req, divider->table, divider->width,
97 divider->flags);
98 }
99
ultrarisc_divider_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)100 static int ultrarisc_divider_set_rate(struct clk_hw *hw, unsigned long rate,
101 unsigned long parent_rate)
102 {
103 struct ultrarisc_divider_clk *divider_clk = to_ultrarisc_divider_clk(hw);
104 struct clk_divider *divider = ÷r_clk->divider;
105 int value;
106 u32 val;
107
108 value = divider_get_val(rate, parent_rate, divider->table,
109 divider->width, divider->flags);
110 if (value < 0)
111 return value;
112
113 scoped_guard(spinlock_irqsave, divider->lock) {
114 val = readl_relaxed(divider->reg);
115 val &= ~(clk_div_mask(divider->width) << divider->shift);
116 val |= value << divider->shift;
117 writel_relaxed(val, divider->reg);
118
119 if (divider_clk->load_mask) {
120 /*
121 * Program the new divider field, then write 1 to the
122 * load bit to trigger the update. The load bit is
123 * write-triggered and reads back as 0 on this hardware.
124 */
125 writel_relaxed(val | divider_clk->load_mask, divider->reg);
126 }
127 }
128
129 return 0;
130 }
131
132 static const struct clk_ops ultrarisc_divider_ops = {
133 .recalc_rate = ultrarisc_divider_recalc_rate,
134 .determine_rate = ultrarisc_divider_determine_rate,
135 .set_rate = ultrarisc_divider_set_rate,
136 };
137
ultrarisc_clk_register_pll(struct device * dev,const struct ultrarisc_pll_desc * desc,const struct ultrarisc_pll_layout * layout,void __iomem * base)138 static struct clk_hw *ultrarisc_clk_register_pll(struct device *dev,
139 const struct ultrarisc_pll_desc *desc,
140 const struct ultrarisc_pll_layout *layout,
141 void __iomem *base)
142 {
143 struct clk_parent_data pdata = { .index = 0 };
144 struct ultrarisc_pll_clk *pll;
145 struct clk_init_data init = {
146 .name = desc->name,
147 .ops = &ultrarisc_pll_ro_ops,
148 .parent_data = &pdata,
149 .num_parents = 1,
150 .flags = CLK_GET_RATE_NOCACHE,
151 };
152 int ret;
153
154 pll = devm_kzalloc(dev, sizeof(*pll), GFP_KERNEL);
155 if (!pll)
156 return ERR_PTR(-ENOMEM);
157
158 pll->base = base;
159 pll->layout = layout;
160 pll->hw.init = &init;
161
162 ret = devm_clk_hw_register(dev, &pll->hw);
163 if (ret)
164 return ERR_PTR(ret);
165
166 return &pll->hw;
167 }
168
169 static struct clk_hw *
ultrarisc_clk_register_divider(struct device * dev,const struct ultrarisc_divider_desc * desc,struct clk_hw * parent_hw,void __iomem * base,spinlock_t * lock)170 ultrarisc_clk_register_divider(struct device *dev,
171 const struct ultrarisc_divider_desc *desc,
172 struct clk_hw *parent_hw, void __iomem *base,
173 spinlock_t *lock)
174 {
175 const struct clk_parent_data pdata = { .hw = parent_hw };
176 void __iomem *reg = base + desc->offset;
177 struct ultrarisc_divider_clk *divider;
178
179 if (!desc->div_width)
180 return ERR_PTR(-EINVAL);
181
182 if (!lock)
183 return ERR_PTR(-EINVAL);
184
185 divider = devm_kzalloc(dev, sizeof(*divider), GFP_KERNEL);
186 if (!divider)
187 return ERR_PTR(-ENOMEM);
188
189 divider->divider.reg = reg;
190 divider->divider.shift = desc->div_shift;
191 divider->divider.width = desc->div_width;
192 divider->divider.flags = desc->divider_flags;
193 divider->divider.lock = lock;
194 divider->load_mask = desc->load_mask;
195 divider->gate.reg = reg;
196 divider->gate.bit_idx = desc->gate_bit;
197 divider->gate.flags = desc->gate_flags;
198 divider->gate.lock = lock;
199
200 return devm_clk_hw_register_composite_pdata(dev, desc->name,
201 &pdata, 1, NULL, NULL,
202 ÷r->divider.hw,
203 &ultrarisc_divider_ops,
204 ÷r->gate.hw,
205 &clk_gate_ops, 0);
206 }
207
ultrarisc_clk_register_fixed_factors(struct device * dev,struct clk_hw_onecell_data * clk_data,const struct ultrarisc_clk_soc_data * soc_data)208 static int ultrarisc_clk_register_fixed_factors(struct device *dev,
209 struct clk_hw_onecell_data *clk_data,
210 const struct ultrarisc_clk_soc_data *soc_data)
211 {
212 u32 i;
213
214 for (i = 0; i < soc_data->num_fixed_factors; i++) {
215 const struct ultrarisc_fixed_factor_desc *desc;
216 struct clk_hw *parent_hw;
217 struct clk_hw *hw;
218
219 desc = &soc_data->fixed_factors[i];
220 if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num)
221 return -EINVAL;
222
223 parent_hw = clk_data->hws[desc->parent_id];
224 if (!parent_hw)
225 return -EINVAL;
226
227 hw = devm_clk_hw_register_fixed_factor_parent_hw(dev, desc->name,
228 parent_hw, 0,
229 desc->mult,
230 desc->div);
231 if (IS_ERR(hw))
232 return PTR_ERR(hw);
233
234 clk_data->hws[desc->id] = hw;
235 }
236
237 return 0;
238 }
239
ultrarisc_clk_register_plls(struct platform_device * pdev,struct clk_hw_onecell_data * clk_data,const struct ultrarisc_clk_soc_data * soc_data,void __iomem * base)240 static int ultrarisc_clk_register_plls(struct platform_device *pdev,
241 struct clk_hw_onecell_data *clk_data,
242 const struct ultrarisc_clk_soc_data *soc_data,
243 void __iomem *base)
244 {
245 struct device *dev = &pdev->dev;
246 u32 i;
247
248 for (i = 0; i < soc_data->num_plls; i++) {
249 const struct ultrarisc_pll_desc *desc = &soc_data->plls[i];
250 struct clk_hw *hw;
251
252 if (desc->id >= clk_data->num) {
253 dev_err(dev, "%s invalid clock ID %u >= %u\n",
254 desc->name, desc->id, clk_data->num);
255 return -EINVAL;
256 }
257
258 hw = ultrarisc_clk_register_pll(dev, desc, soc_data->pll_layout, base);
259 if (IS_ERR(hw))
260 return PTR_ERR(hw);
261
262 clk_data->hws[desc->id] = hw;
263 }
264
265 return 0;
266 }
267
ultrarisc_clk_register_dividers(struct platform_device * pdev,struct clk_hw_onecell_data * clk_data,const struct ultrarisc_clk_soc_data * soc_data,void __iomem * base,spinlock_t * lock)268 static int ultrarisc_clk_register_dividers(struct platform_device *pdev,
269 struct clk_hw_onecell_data *clk_data,
270 const struct ultrarisc_clk_soc_data *soc_data,
271 void __iomem *base,
272 spinlock_t *lock)
273 {
274 struct device *dev = &pdev->dev;
275 u32 i;
276
277 for (i = 0; i < soc_data->num_dividers; i++) {
278 const struct ultrarisc_divider_desc *desc;
279 struct clk_hw *parent_hw;
280 struct clk_hw *hw;
281
282 desc = &soc_data->dividers[i];
283 if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num)
284 return -EINVAL;
285
286 parent_hw = clk_data->hws[desc->parent_id];
287 if (!parent_hw)
288 return -EINVAL;
289
290 hw = ultrarisc_clk_register_divider(dev, desc, parent_hw, base,
291 lock);
292 if (IS_ERR(hw))
293 return PTR_ERR(hw);
294
295 if (desc->max_rate)
296 clk_hw_set_rate_range(hw, 0, desc->max_rate);
297
298 clk_data->hws[desc->id] = hw;
299 }
300
301 return 0;
302 }
303
ultrarisc_clk_register_gates(struct platform_device * pdev,struct clk_hw_onecell_data * clk_data,const struct ultrarisc_clk_soc_data * soc_data,void __iomem * base,spinlock_t * lock)304 static int ultrarisc_clk_register_gates(struct platform_device *pdev,
305 struct clk_hw_onecell_data *clk_data,
306 const struct ultrarisc_clk_soc_data *soc_data,
307 void __iomem *base,
308 spinlock_t *lock)
309 {
310 struct device *dev = &pdev->dev;
311 u32 i;
312
313 for (i = 0; i < soc_data->num_gates; i++) {
314 const struct ultrarisc_gate_desc *desc;
315 struct clk_hw *parent_hw;
316 struct clk_hw *hw;
317
318 desc = &soc_data->gates[i];
319 if (desc->id >= clk_data->num || desc->parent_id >= clk_data->num)
320 return -EINVAL;
321
322 parent_hw = clk_data->hws[desc->parent_id];
323 if (!parent_hw)
324 return -EINVAL;
325
326 hw = devm_clk_hw_register_gate_parent_hw(dev, desc->name,
327 parent_hw, 0,
328 base + desc->offset,
329 desc->gate_bit,
330 desc->gate_flags,
331 lock);
332 if (IS_ERR(hw))
333 return PTR_ERR(hw);
334
335 clk_data->hws[desc->id] = hw;
336 }
337
338 return 0;
339 }
340
ultrarisc_clk_probe(struct platform_device * pdev,const struct ultrarisc_clk_soc_data * soc_data)341 int ultrarisc_clk_probe(struct platform_device *pdev,
342 const struct ultrarisc_clk_soc_data *soc_data)
343 {
344 struct clk_hw_onecell_data *clk_data;
345 struct device *dev = &pdev->dev;
346 void __iomem *base;
347 spinlock_t *lock;
348 int ret;
349
350 if (!soc_data)
351 return -EINVAL;
352
353 lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
354 if (!lock)
355 return -ENOMEM;
356
357 spin_lock_init(lock);
358
359 clk_data = devm_kzalloc(dev, struct_size(clk_data, hws,
360 soc_data->num_clks),
361 GFP_KERNEL);
362 if (!clk_data)
363 return -ENOMEM;
364
365 clk_data->num = soc_data->num_clks;
366
367 base = devm_platform_ioremap_resource(pdev, 0);
368 if (IS_ERR(base))
369 return PTR_ERR(base);
370
371 ret = ultrarisc_clk_register_plls(pdev, clk_data, soc_data, base);
372 if (ret)
373 return ret;
374
375 ret = ultrarisc_clk_register_fixed_factors(dev, clk_data, soc_data);
376 if (ret)
377 return ret;
378
379 ret = ultrarisc_clk_register_dividers(pdev, clk_data, soc_data, base, lock);
380 if (ret)
381 return ret;
382
383 ret = ultrarisc_clk_register_gates(pdev, clk_data, soc_data, base, lock);
384 if (ret)
385 return ret;
386
387 for (int i = 0; i < clk_data->num; i++) {
388 if (!clk_data->hws[i]) {
389 dev_err(dev, "missing clock ID %u\n", i);
390 return -EINVAL;
391 }
392 }
393
394 return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data);
395 }
396 EXPORT_SYMBOL_NS_GPL(ultrarisc_clk_probe, "CLK_ULTRARISC");
397
398 MODULE_DESCRIPTION("UltraRISC clock core driver");
399 MODULE_LICENSE("GPL");
400