clk: starfive: Add funtions of saving and restoring data about SYS, AON and STG
[platform/kernel/linux-starfive.git] / drivers / clk / starfive / clk-starfive-jh7110-gen.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * StarFive JH7110 Clock Generator Driver
4  *
5  * Copyright (C) 2022 Xingyu Wu <xingyu.wu@starfivetech.com>
6  */
7
8 #include <linux/bits.h>
9 #include <linux/clk.h>
10 #include <linux/clk-provider.h>
11 #include <linux/device.h>
12 #include <linux/init.h>
13 #include <linux/io.h>
14 #include <linux/kernel.h>
15 #include <linux/mod_devicetable.h>
16 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/of_device.h>
19 #include <linux/pm_runtime.h>
20
21 #include <dt-bindings/clock/starfive-jh7110-clkgen.h>
22 #include "clk-starfive-jh7110.h"
23 #include "clk-starfive-jh7110-pll.h"
24
25 static struct jh7110_clk * __init jh7110_clk_from(struct clk_hw *hw)
26 {
27         return container_of(hw, struct jh7110_clk, hw);
28 }
29
30 static struct jh7110_clk_priv *jh7110_priv_from(struct jh7110_clk *clk)
31 {
32         return container_of(clk, struct jh7110_clk_priv, reg[clk->idx]);
33 }
34
35 void __iomem *jh7110_clk_reg_addr_get(struct jh7110_clk *clk)
36 {
37         void __iomem *reg;
38         struct jh7110_clk_priv *priv = jh7110_priv_from(clk);
39
40         if (clk->reg_flags == JH7110_CLK_SYS_FLAG)
41                 reg = priv->sys_base + 4 * clk->idx;
42         else if (clk->reg_flags == JH7110_CLK_STG_FLAG)
43                 reg = priv->stg_base + 4 * (clk->idx - JH7110_CLK_SYS_REG_END);
44         else if (clk->reg_flags == JH7110_CLK_AON_FLAG)
45                 reg = priv->aon_base + 4 * (clk->idx - JH7110_CLK_STG_REG_END);
46         else if (clk->reg_flags == JH7110_CLK_VOUT_FLAG)
47                 reg = priv->vout_base + 4 * clk->idx;
48         else if (clk->reg_flags == JH7110_CLK_ISP_FLAG)
49                 reg = priv->isp_base + 4 * clk->idx;
50
51         return reg;
52 }
53
54 static u32 jh7110_clk_reg_get(struct jh7110_clk *clk)
55 {
56         void __iomem *reg = jh7110_clk_reg_addr_get(clk);
57
58         if ((clk->reg_flags == JH7110_CLK_ISP_FLAG) || (clk->reg_flags == JH7110_CLK_VOUT_FLAG)) {
59                 int ret;
60                 struct jh7110_clk_priv *priv = jh7110_priv_from(clk);
61
62                 if (pm_runtime_suspended(priv->dev)) {
63                         ret = pm_runtime_get_sync(priv->dev);
64                         if (ret < 0) {
65                                 dev_err(priv->dev, "cannot resume device :%d.\n", ret);
66                                 return 0;
67                         }
68                         pm_runtime_put(priv->dev);
69                 }
70         }
71
72         return readl_relaxed(reg);
73 }
74
75 static void jh7110_clk_reg_rmw(struct jh7110_clk *clk, u32 mask, u32 value)
76 {
77         struct jh7110_clk_priv *priv = jh7110_priv_from(clk);
78         void __iomem *reg = jh7110_clk_reg_addr_get(clk);
79         unsigned long flags;
80
81         spin_lock_irqsave(&priv->rmw_lock, flags);
82         if ((clk->idx == JH7110_UART3_CLK_CORE
83                 || clk->idx == JH7110_UART4_CLK_CORE
84                 || clk->idx == JH7110_UART5_CLK_CORE)
85                 && (value != JH7110_CLK_ENABLE))
86                 value  <<= 8;
87         value |= jh7110_clk_reg_get(clk) & ~mask;
88         writel_relaxed(value, reg);
89         spin_unlock_irqrestore(&priv->rmw_lock, flags);
90 }
91
92 static int jh7110_clk_enable(struct clk_hw *hw)
93 {
94         struct jh7110_clk *clk = jh7110_clk_from(hw);
95
96         jh7110_clk_reg_rmw(clk, JH7110_CLK_ENABLE, JH7110_CLK_ENABLE);
97         return 0;
98 }
99
100 static void jh7110_clk_disable(struct clk_hw *hw)
101 {
102         struct jh7110_clk *clk = jh7110_clk_from(hw);
103
104         jh7110_clk_reg_rmw(clk, JH7110_CLK_ENABLE, 0);
105 }
106
107 static int jh7110_clk_is_enabled(struct clk_hw *hw)
108 {
109         struct jh7110_clk *clk = jh7110_clk_from(hw);
110
111         return !!(jh7110_clk_reg_get(clk) & JH7110_CLK_ENABLE);
112 }
113
114 static unsigned long jh7110_clk_recalc_rate(struct clk_hw *hw,
115                                                 unsigned long parent_rate)
116 {
117         struct jh7110_clk *clk = jh7110_clk_from(hw);
118         u32 div = jh7110_clk_reg_get(clk) & JH7110_CLK_DIV_MASK;
119
120         if (clk->idx == JH7110_UART3_CLK_CORE
121                 || clk->idx == JH7110_UART4_CLK_CORE
122                 || clk->idx == JH7110_UART5_CLK_CORE)
123                 div = div >> 8;
124
125         return div ? parent_rate / div : 0;
126 }
127
128 static int jh7110_clk_determine_rate(struct clk_hw *hw,
129                                         struct clk_rate_request *req)
130 {
131         struct jh7110_clk *clk = jh7110_clk_from(hw);
132         unsigned long parent = req->best_parent_rate;
133         unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate);
134         unsigned long div = min_t(unsigned long,
135                                 DIV_ROUND_UP(parent, rate), clk->max_div);
136         unsigned long result = parent / div;
137
138         /*
139          * we want the result clamped by min_rate and max_rate if possible:
140          * case 1: div hits the max divider value, which means it's less than
141          * parent / rate, so the result is greater than rate and min_rate in
142          * particular. we can't do anything about result > max_rate because the
143          * divider doesn't go any further.
144          * case 2: div = DIV_ROUND_UP(parent, rate) which means the result is
145          * always lower or equal to rate and max_rate. however the result may
146          * turn out lower than min_rate, but then the next higher rate is fine:
147          *      div - 1 = ceil(parent / rate) - 1 < parent / rate
148          * and thus
149          *      min_rate <= rate < parent / (div - 1)
150          */
151         if (result < req->min_rate && div > 1)
152                 result = parent / (div - 1);
153
154         req->rate = result;
155         return 0;
156 }
157
158 static int jh7110_clk_set_rate(struct clk_hw *hw,
159                                 unsigned long rate,
160                                 unsigned long parent_rate)
161 {
162         struct jh7110_clk *clk = jh7110_clk_from(hw);
163         unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate),
164                                         1UL, (unsigned long)clk->max_div);
165
166         jh7110_clk_reg_rmw(clk, JH7110_CLK_DIV_MASK, div);
167         return 0;
168 }
169
170 static u8 jh7110_clk_get_parent(struct clk_hw *hw)
171 {
172         struct jh7110_clk *clk = jh7110_clk_from(hw);
173         u32 value = jh7110_clk_reg_get(clk);
174
175         return (value & JH7110_CLK_MUX_MASK) >> JH7110_CLK_MUX_SHIFT;
176 }
177
178 static int jh7110_clk_set_parent(struct clk_hw *hw, u8 index)
179 {
180         struct jh7110_clk *clk = jh7110_clk_from(hw);
181         u32 value = (u32)index << JH7110_CLK_MUX_SHIFT;
182
183         jh7110_clk_reg_rmw(clk, JH7110_CLK_MUX_MASK, value);
184         return 0;
185 }
186
187 static int jh7110_clk_mux_determine_rate(struct clk_hw *hw,
188                                          struct clk_rate_request *req)
189 {
190         return clk_mux_determine_rate_flags(hw, req, 0);
191 }
192
193 static int jh7110_clk_get_phase(struct clk_hw *hw)
194 {
195         struct jh7110_clk *clk = jh7110_clk_from(hw);
196         u32 value = jh7110_clk_reg_get(clk);
197
198         return (value & JH7110_CLK_INVERT) ? 180 : 0;
199 }
200
201 static int jh7110_clk_set_phase(struct clk_hw *hw, int degrees)
202 {
203         struct jh7110_clk *clk = jh7110_clk_from(hw);
204         u32 value;
205
206         if (degrees == 0)
207                 value = 0;
208         else if (degrees == 180)
209                 value = JH7110_CLK_INVERT;
210         else
211                 return -EINVAL;
212
213         jh7110_clk_reg_rmw(clk, JH7110_CLK_INVERT, value);
214         return 0;
215 }
216
217 #ifdef CONFIG_DEBUG_FS
218 static void jh7110_clk_debug_init(struct clk_hw *hw, struct dentry *dentry)
219 {
220         static const struct debugfs_reg32 jh7110_clk_reg = {
221                 .name = "CTRL",
222                 .offset = 0,
223         };
224         struct jh7110_clk *clk = jh7110_clk_from(hw);
225         struct jh7110_clk_priv *priv = jh7110_priv_from(clk);
226         struct debugfs_regset32 *regset;
227
228         regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL);
229         if (!regset)
230                 return;
231
232         regset->regs = &jh7110_clk_reg;
233         regset->nregs = 1;
234         regset->base = jh7110_clk_reg_addr_get(clk);
235
236         debugfs_create_regset32("registers", 0400, dentry, regset);
237 }
238 #else
239 #define jh7110_clk_debug_init NULL
240 #endif
241
242 #ifdef CONFIG_PM_SLEEP
243 static int jh7110_clk_save_context(struct clk_hw *hw)
244 {
245         struct jh7110_clk *clk = jh7110_clk_from(hw);
246         void __iomem *reg = jh7110_clk_reg_addr_get(clk);
247         struct jh7110_clk_priv *priv = jh7110_priv_from(clk);
248
249         if (!clk || !priv)
250                 return 0;
251
252         if ((clk->reg_flags == JH7110_CLK_ISP_FLAG) || (clk->reg_flags == JH7110_CLK_VOUT_FLAG))
253                 return 0;
254
255         if (clk->idx >= JH7110_CLK_REG_END)
256                 return 0;
257
258         spin_lock(&priv->rmw_lock);
259         clk->saved_reg_value = readl_relaxed(reg);
260         spin_unlock(&priv->rmw_lock);
261
262         return 0;
263 }
264
265 static void jh7110_clk_gate_restore_context(struct clk_hw *hw)
266 {
267         struct jh7110_clk *clk = jh7110_clk_from(hw);
268
269         if (!clk)
270                 return;
271
272         if ((clk->reg_flags == JH7110_CLK_ISP_FLAG) || (clk->reg_flags == JH7110_CLK_VOUT_FLAG))
273                 return;
274
275         if (clk->idx >= JH7110_CLK_REG_END)
276                 return;
277
278         jh7110_clk_reg_rmw(clk, JH7110_CLK_ENABLE, clk->saved_reg_value);
279
280         return;
281 }
282
283 static void jh7110_clk_div_restore_context(struct clk_hw *hw)
284 {
285         struct jh7110_clk *clk = jh7110_clk_from(hw);
286
287         if (!clk)
288                 return;
289
290         if ((clk->reg_flags == JH7110_CLK_ISP_FLAG) || (clk->reg_flags == JH7110_CLK_VOUT_FLAG))
291                 return;
292
293         if (clk->idx >= JH7110_CLK_REG_END)
294                 return;
295
296         jh7110_clk_reg_rmw(clk, JH7110_CLK_DIV_MASK, clk->saved_reg_value);
297
298         return;
299 }
300
301 static void jh7110_clk_mux_restore_context(struct clk_hw *hw)
302 {
303         struct jh7110_clk *clk = jh7110_clk_from(hw);
304
305         if (!clk)
306                 return;
307
308         if ((clk->reg_flags == JH7110_CLK_ISP_FLAG) || (clk->reg_flags == JH7110_CLK_VOUT_FLAG))
309                 return;
310
311         if (clk->idx >= JH7110_CLK_REG_END)
312                 return;
313
314         jh7110_clk_reg_rmw(clk, JH7110_CLK_MUX_MASK, clk->saved_reg_value);
315
316         return;
317 }
318
319 static void jh7110_clk_inv_restore_context(struct clk_hw *hw)
320 {
321         struct jh7110_clk *clk = jh7110_clk_from(hw);
322
323         if (!clk)
324                 return;
325
326         if ((clk->reg_flags == JH7110_CLK_ISP_FLAG) || (clk->reg_flags == JH7110_CLK_VOUT_FLAG))
327                 return;
328
329         if (clk->idx >= JH7110_CLK_REG_END)
330                 return;
331
332         jh7110_clk_reg_rmw(clk, JH7110_CLK_INVERT, clk->saved_reg_value);
333
334         return;
335 }
336
337 static void jh7110_clk_gdiv_restore_context(struct clk_hw *hw)
338 {
339         jh7110_clk_div_restore_context(hw);
340         jh7110_clk_gate_restore_context(hw);
341
342         return;
343 }
344
345 static void jh7110_clk_gmux_restore_context(struct clk_hw *hw)
346 {
347         jh7110_clk_mux_restore_context(hw);
348         jh7110_clk_gate_restore_context(hw);
349
350         return;
351 }
352
353 static void jh7110_clk_mdiv_restore_context(struct clk_hw *hw)
354 {
355         jh7110_clk_mux_restore_context(hw);
356         jh7110_clk_div_restore_context(hw);
357
358         return;
359 }
360
361 static void jh7110_clk_gmd_restore_context(struct clk_hw *hw)
362 {
363         jh7110_clk_mux_restore_context(hw);
364         jh7110_clk_div_restore_context(hw);
365         jh7110_clk_gate_restore_context(hw);
366
367         return;
368 }
369
370 #endif
371
372 static const struct clk_ops jh7110_clk_gate_ops = {
373         .enable = jh7110_clk_enable,
374         .disable = jh7110_clk_disable,
375         .is_enabled = jh7110_clk_is_enabled,
376         .debug_init = jh7110_clk_debug_init,
377 #ifdef CONFIG_PM_SLEEP
378         .save_context = jh7110_clk_save_context,
379         .restore_context = jh7110_clk_gate_restore_context,
380 #endif
381 };
382
383 static const struct clk_ops jh7110_clk_div_ops = {
384         .recalc_rate = jh7110_clk_recalc_rate,
385         .determine_rate = jh7110_clk_determine_rate,
386         .set_rate = jh7110_clk_set_rate,
387         .debug_init = jh7110_clk_debug_init,
388 #ifdef CONFIG_PM_SLEEP
389         .save_context = jh7110_clk_save_context,
390         .restore_context = jh7110_clk_div_restore_context,
391 #endif
392 };
393
394 static const struct clk_ops jh7110_clk_gdiv_ops = {
395         .enable = jh7110_clk_enable,
396         .disable = jh7110_clk_disable,
397         .is_enabled = jh7110_clk_is_enabled,
398         .recalc_rate = jh7110_clk_recalc_rate,
399         .determine_rate = jh7110_clk_determine_rate,
400         .set_rate = jh7110_clk_set_rate,
401         .debug_init = jh7110_clk_debug_init,
402 #ifdef CONFIG_PM_SLEEP
403         .save_context = jh7110_clk_save_context,
404         .restore_context = jh7110_clk_gdiv_restore_context,
405 #endif
406 };
407
408 static const struct clk_ops jh7110_clk_mux_ops = {
409         .determine_rate = jh7110_clk_mux_determine_rate,
410         .set_parent = jh7110_clk_set_parent,
411         .get_parent = jh7110_clk_get_parent,
412         .debug_init = jh7110_clk_debug_init,
413 #ifdef CONFIG_PM_SLEEP
414         .save_context = jh7110_clk_save_context,
415         .restore_context = jh7110_clk_mux_restore_context,
416 #endif
417 };
418
419 static const struct clk_ops jh7110_clk_gmux_ops = {
420         .enable = jh7110_clk_enable,
421         .disable = jh7110_clk_disable,
422         .is_enabled = jh7110_clk_is_enabled,
423         .determine_rate = jh7110_clk_mux_determine_rate,
424         .set_parent = jh7110_clk_set_parent,
425         .get_parent = jh7110_clk_get_parent,
426         .debug_init = jh7110_clk_debug_init,
427 #ifdef CONFIG_PM_SLEEP
428         .save_context = jh7110_clk_save_context,
429         .restore_context = jh7110_clk_gmux_restore_context,
430 #endif
431 };
432
433 static const struct clk_ops jh7110_clk_mdiv_ops = {
434         .recalc_rate = jh7110_clk_recalc_rate,
435         .determine_rate = jh7110_clk_determine_rate,
436         .get_parent = jh7110_clk_get_parent,
437         .set_parent = jh7110_clk_set_parent,
438         .set_rate = jh7110_clk_set_rate,
439         .debug_init = jh7110_clk_debug_init,
440 #ifdef CONFIG_PM_SLEEP
441         .save_context = jh7110_clk_save_context,
442         .restore_context = jh7110_clk_mdiv_restore_context,
443 #endif
444 };
445
446 static const struct clk_ops jh7110_clk_gmd_ops = {
447         .enable = jh7110_clk_enable,
448         .disable = jh7110_clk_disable,
449         .is_enabled = jh7110_clk_is_enabled,
450         .recalc_rate = jh7110_clk_recalc_rate,
451         .determine_rate = jh7110_clk_determine_rate,
452         .get_parent = jh7110_clk_get_parent,
453         .set_parent = jh7110_clk_set_parent,
454         .set_rate = jh7110_clk_set_rate,
455         .debug_init = jh7110_clk_debug_init,
456 #ifdef CONFIG_PM_SLEEP
457         .save_context = jh7110_clk_save_context,
458         .restore_context = jh7110_clk_gmd_restore_context,
459 #endif
460 };
461
462 static const struct clk_ops jh7110_clk_inv_ops = {
463         .get_phase = jh7110_clk_get_phase,
464         .set_phase = jh7110_clk_set_phase,
465         .debug_init = jh7110_clk_debug_init,
466 #ifdef CONFIG_PM_SLEEP
467         .save_context = jh7110_clk_save_context,
468         .restore_context = jh7110_clk_inv_restore_context,
469 #endif
470 };
471
472 const struct clk_ops *starfive_jh7110_clk_ops(u32 max)
473 {
474         const struct clk_ops *ops;
475
476         if (max & JH7110_CLK_DIV_MASK) {
477                 if (max & JH7110_CLK_MUX_MASK) {
478                         if (max & JH7110_CLK_ENABLE)
479                                 ops = &jh7110_clk_gmd_ops;
480                         else
481                                 ops = &jh7110_clk_mdiv_ops;
482                 } else if (max & JH7110_CLK_ENABLE)
483                         ops = &jh7110_clk_gdiv_ops;
484                 else
485                         ops = &jh7110_clk_div_ops;
486         } else if (max & JH7110_CLK_MUX_MASK) {
487                 if (max & JH7110_CLK_ENABLE)
488                         ops = &jh7110_clk_gmux_ops;
489                 else
490                         ops = &jh7110_clk_mux_ops;
491         } else if (max & JH7110_CLK_ENABLE)
492                 ops = &jh7110_clk_gate_ops;
493         else
494                 ops = &jh7110_clk_inv_ops;
495
496         return ops;
497 }
498 EXPORT_SYMBOL_GPL(starfive_jh7110_clk_ops);
499
500 #ifdef CONFIG_PM_SLEEP
501 static int clk_starfive_jh7110_gen_system_suspend(struct device *dev)
502 {
503         return clk_save_context();
504 }
505
506 static int clk_starfive_jh7110_gen_system_resume(struct device *dev)
507 {
508         clk_restore_context();
509
510         return 0;
511 }
512 #endif
513
514 static const struct dev_pm_ops clk_starfive_jh7110_gen_pm_ops = {
515         SET_LATE_SYSTEM_SLEEP_PM_OPS(clk_starfive_jh7110_gen_system_suspend,
516                                      clk_starfive_jh7110_gen_system_resume)
517 };
518
519 static struct clk_hw *jh7110_clk_get(struct of_phandle_args *clkspec,
520                                                 void *data)
521 {
522         struct jh7110_clk_priv *priv = data;
523         unsigned int idx = clkspec->args[0];
524
525         if (idx < JH7110_PLL0_OUT)
526                 return &priv->reg[idx].hw;
527
528         if (idx < JH7110_CLK_END) {
529 #ifdef CONFIG_CLK_STARFIVE_JH7110_PLL
530                 if ((idx == JH7110_PLL0_OUT) || (idx == JH7110_PLL2_OUT))
531                         return &priv->pll_priv[PLL_OF(idx)].hw;
532 #endif
533                 return priv->pll[PLL_OF(idx)];
534         }
535
536         return ERR_PTR(-EINVAL);
537 }
538
539
540 static int __init clk_starfive_jh7110_probe(struct platform_device *pdev)
541 {
542         struct jh7110_clk_priv *priv;
543         int ret = 0;
544
545         priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7110_PLL0_OUT),
546                                         GFP_KERNEL);
547         if (!priv)
548                 return -ENOMEM;
549
550         spin_lock_init(&priv->rmw_lock);
551         priv->dev = &pdev->dev;
552
553         pm_runtime_enable(priv->dev);
554
555 #ifdef CONFIG_CLK_STARFIVE_JH7110_PLL
556         ret = clk_starfive_jh7110_pll_init(pdev, priv->pll_priv);
557         if (ret)
558                 return ret;
559 #endif
560
561         ret = clk_starfive_jh7110_sys_init(pdev, priv);
562         if (ret)
563                 return ret;
564
565 /* set PLL0 default rate */
566 #ifdef CONFIG_CLK_STARFIVE_JH7110_PLL
567         if (PLL0_DEFAULT_FREQ) {
568                 struct clk *pll0_clk = priv->pll_priv[PLL0_INDEX].hw.clk;
569                 struct clk *cpu_root = priv->reg[JH7110_CPU_ROOT].hw.clk;
570                 struct clk *osc_clk = clk_get(&pdev->dev, "osc");
571
572                 if (IS_ERR(osc_clk))
573                         dev_err(&pdev->dev, "get osc_clk failed\n");
574
575                 if (PLL0_DEFAULT_FREQ >= PLL0_FREQ_1500_VALUE) {
576                         struct clk *cpu_core = priv->reg[JH7110_CPU_CORE].hw.clk;
577
578                         if (clk_set_rate(cpu_core, clk_get_rate(pll0_clk) / 2)) {
579                                 dev_err(&pdev->dev, "set cpu_core rate failed\n");
580                                 goto failed_set;
581                         }
582                 }
583
584                 if (clk_set_parent(cpu_root, osc_clk)) {
585                         dev_err(&pdev->dev, "set parent to osc_clk failed\n");
586                         goto failed_set;
587                 }
588
589                 if (clk_set_rate(pll0_clk, PLL0_DEFAULT_FREQ))
590                         dev_err(&pdev->dev, "set pll0 rate failed\n");
591
592                 if (clk_set_parent(cpu_root, pll0_clk))
593                         dev_err(&pdev->dev, "set parent to pll0_clk failed\n");
594
595 failed_set:
596                 clk_put(osc_clk);
597         }
598 #endif
599
600         ret = clk_starfive_jh7110_stg_init(pdev, priv);
601         if (ret)
602                 return ret;
603
604         ret = clk_starfive_jh7110_aon_init(pdev, priv);
605         if (ret)
606                 return ret;
607
608         ret = devm_of_clk_add_hw_provider(priv->dev, jh7110_clk_get, priv);
609         if (ret)
610                 return ret;
611
612         dev_info(&pdev->dev, "starfive JH7110 clkgen init successfully.");
613         return 0;
614 }
615
616 static const struct of_device_id clk_starfive_jh7110_match[] = {
617         {.compatible = "starfive,jh7110-clkgen"},
618         { /* sentinel */ }
619 };
620
621 static struct platform_driver clk_starfive_jh7110_driver = {
622         .driver = {
623                 .name = "clk-starfive-jh7110",
624                 .of_match_table = clk_starfive_jh7110_match,
625                 .pm = &clk_starfive_jh7110_gen_pm_ops,
626         },
627 };
628 builtin_platform_driver_probe(clk_starfive_jh7110_driver,
629                         clk_starfive_jh7110_probe);
630
631 MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
632 MODULE_DESCRIPTION("StarFive JH7110 sysgen clock driver");
633 MODULE_LICENSE("GPL");