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