clk-sg2042-pll.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Sophgo SG2042 PLL clock Driver
  4. *
  5. * Copyright (C) 2024 Sophgo Technology Inc.
  6. * Copyright (C) 2024 Chen Wang <unicorn_wang@outlook.com>
  7. */
  8. #include <linux/array_size.h>
  9. #include <linux/bitfield.h>
  10. #include <linux/bits.h>
  11. #include <linux/clk-provider.h>
  12. #include <linux/io.h>
  13. #include <linux/iopoll.h>
  14. #include <linux/platform_device.h>
  15. #include <asm/div64.h>
  16. #include <dt-bindings/clock/sophgo,sg2042-pll.h>
  17. #include "clk-sg2042.h"
  18. /* Registers defined in SYS_CTRL */
  19. #define R_PLL_BEGIN 0xC0
  20. #define R_PLL_STAT (0xC0 - R_PLL_BEGIN)
  21. #define R_PLL_CLKEN_CONTROL (0xC4 - R_PLL_BEGIN)
  22. #define R_MPLL_CONTROL (0xE8 - R_PLL_BEGIN)
  23. #define R_FPLL_CONTROL (0xF4 - R_PLL_BEGIN)
  24. #define R_DPLL0_CONTROL (0xF8 - R_PLL_BEGIN)
  25. #define R_DPLL1_CONTROL (0xFC - R_PLL_BEGIN)
  26. /**
  27. * struct sg2042_pll_clock - PLL clock
  28. * @hw: clk_hw for initialization
  29. * @id: used to map clk_onecell_data
  30. * @base: used for readl/writel.
  31. * **NOTE**: PLL registers are all in SYS_CTRL!
  32. * @lock: spinlock to protect register access, modification
  33. * of frequency can only be served one at the time.
  34. * @offset_ctrl: offset of pll control registers
  35. * @shift_status_lock: shift of XXX_LOCK in pll status register
  36. * @shift_status_updating: shift of UPDATING_XXX in pll status register
  37. * @shift_enable: shift of XXX_CLK_EN in pll enable register
  38. */
  39. struct sg2042_pll_clock {
  40. struct clk_hw hw;
  41. unsigned int id;
  42. void __iomem *base;
  43. /* protect register access */
  44. spinlock_t *lock;
  45. u32 offset_ctrl;
  46. u8 shift_status_lock;
  47. u8 shift_status_updating;
  48. u8 shift_enable;
  49. };
  50. #define to_sg2042_pll_clk(_hw) container_of(_hw, struct sg2042_pll_clock, hw)
  51. #define KHZ 1000UL
  52. #define MHZ (KHZ * KHZ)
  53. #define REFDIV_MIN 1
  54. #define REFDIV_MAX 63
  55. #define FBDIV_MIN 16
  56. #define FBDIV_MAX 320
  57. #define PLL_FREF_SG2042 (25 * MHZ)
  58. #define PLL_FOUTPOSTDIV_MIN (16 * MHZ)
  59. #define PLL_FOUTPOSTDIV_MAX (3200 * MHZ)
  60. #define PLL_FOUTVCO_MIN (800 * MHZ)
  61. #define PLL_FOUTVCO_MAX (3200 * MHZ)
  62. struct sg2042_pll_ctrl {
  63. unsigned long freq;
  64. unsigned int fbdiv;
  65. unsigned int postdiv1;
  66. unsigned int postdiv2;
  67. unsigned int refdiv;
  68. };
  69. #define PLLCTRL_FBDIV_MASK GENMASK(27, 16)
  70. #define PLLCTRL_POSTDIV2_MASK GENMASK(14, 12)
  71. #define PLLCTRL_POSTDIV1_MASK GENMASK(10, 8)
  72. #define PLLCTRL_REFDIV_MASK GENMASK(5, 0)
  73. static inline u32 sg2042_pll_ctrl_encode(struct sg2042_pll_ctrl *ctrl)
  74. {
  75. return FIELD_PREP(PLLCTRL_FBDIV_MASK, ctrl->fbdiv) |
  76. FIELD_PREP(PLLCTRL_POSTDIV2_MASK, ctrl->postdiv2) |
  77. FIELD_PREP(PLLCTRL_POSTDIV1_MASK, ctrl->postdiv1) |
  78. FIELD_PREP(PLLCTRL_REFDIV_MASK, ctrl->refdiv);
  79. }
  80. static inline void sg2042_pll_ctrl_decode(unsigned int reg_value,
  81. struct sg2042_pll_ctrl *ctrl)
  82. {
  83. ctrl->fbdiv = FIELD_GET(PLLCTRL_FBDIV_MASK, reg_value);
  84. ctrl->refdiv = FIELD_GET(PLLCTRL_REFDIV_MASK, reg_value);
  85. ctrl->postdiv1 = FIELD_GET(PLLCTRL_POSTDIV1_MASK, reg_value);
  86. ctrl->postdiv2 = FIELD_GET(PLLCTRL_POSTDIV2_MASK, reg_value);
  87. }
  88. static inline void sg2042_pll_enable(struct sg2042_pll_clock *pll, bool en)
  89. {
  90. u32 value;
  91. if (en) {
  92. /* wait pll lock */
  93. if (readl_poll_timeout_atomic(pll->base + R_PLL_STAT,
  94. value,
  95. ((value >> pll->shift_status_lock) & 0x1),
  96. 0,
  97. 100000))
  98. pr_warn("%s not locked\n", pll->hw.init->name);
  99. /* wait pll updating */
  100. if (readl_poll_timeout_atomic(pll->base + R_PLL_STAT,
  101. value,
  102. !((value >> pll->shift_status_updating) & 0x1),
  103. 0,
  104. 100000))
  105. pr_warn("%s still updating\n", pll->hw.init->name);
  106. /* enable pll */
  107. value = readl(pll->base + R_PLL_CLKEN_CONTROL);
  108. writel(value | (1 << pll->shift_enable), pll->base + R_PLL_CLKEN_CONTROL);
  109. } else {
  110. /* disable pll */
  111. value = readl(pll->base + R_PLL_CLKEN_CONTROL);
  112. writel(value & (~(1 << pll->shift_enable)), pll->base + R_PLL_CLKEN_CONTROL);
  113. }
  114. }
  115. /**
  116. * sg2042_pll_recalc_rate() - Calculate rate for plls
  117. * @reg_value: current register value
  118. * @parent_rate: parent frequency
  119. *
  120. * This function is used to calculate below "rate" in equation
  121. * rate = (parent_rate/REFDIV) x FBDIV/POSTDIV1/POSTDIV2
  122. * = (parent_rate x FBDIV) / (REFDIV x POSTDIV1 x POSTDIV2)
  123. *
  124. * Return: The rate calculated.
  125. */
  126. static unsigned long sg2042_pll_recalc_rate(unsigned int reg_value,
  127. unsigned long parent_rate)
  128. {
  129. struct sg2042_pll_ctrl ctrl_table;
  130. u64 numerator, denominator;
  131. sg2042_pll_ctrl_decode(reg_value, &ctrl_table);
  132. numerator = (u64)parent_rate * ctrl_table.fbdiv;
  133. denominator = ctrl_table.refdiv * ctrl_table.postdiv1 * ctrl_table.postdiv2;
  134. do_div(numerator, denominator);
  135. return numerator;
  136. }
  137. /**
  138. * sg2042_pll_get_postdiv_1_2() - Based on input rate/prate/fbdiv/refdiv,
  139. * look up the postdiv1_2 table to get the closest postdiiv combination.
  140. * @rate: FOUTPOSTDIV
  141. * @prate: parent rate, i.e. FREF
  142. * @fbdiv: FBDIV
  143. * @refdiv: REFDIV
  144. * @postdiv1: POSTDIV1, output
  145. * @postdiv2: POSTDIV2, output
  146. *
  147. * postdiv1_2 contains all the possible combination lists of POSTDIV1 and POSTDIV2
  148. * for example:
  149. * postdiv1_2[0] = {2, 4, 8}, where div1 = 2, div2 = 4 , div1 * div2 = 8
  150. *
  151. * See TRM:
  152. * FOUTPOSTDIV = FREF * FBDIV / REFDIV / (POSTDIV1 * POSTDIV2)
  153. * So we get following formula to get POSTDIV1 and POSTDIV2:
  154. * POSTDIV = (prate/REFDIV) x FBDIV/rate
  155. * above POSTDIV = POSTDIV1*POSTDIV2
  156. *
  157. * Return:
  158. * %0 - OK
  159. * %-EINVAL - invalid argument, which means Failed to get the postdivs.
  160. */
  161. static int sg2042_pll_get_postdiv_1_2(unsigned long rate,
  162. unsigned long prate,
  163. unsigned int fbdiv,
  164. unsigned int refdiv,
  165. unsigned int *postdiv1,
  166. unsigned int *postdiv2)
  167. {
  168. int index;
  169. u64 tmp0;
  170. /* POSTDIV_RESULT_INDEX point to 3rd element in the array postdiv1_2 */
  171. #define POSTDIV_RESULT_INDEX 2
  172. static const int postdiv1_2[][3] = {
  173. {2, 4, 8}, {3, 3, 9}, {2, 5, 10}, {2, 6, 12},
  174. {2, 7, 14}, {3, 5, 15}, {4, 4, 16}, {3, 6, 18},
  175. {4, 5, 20}, {3, 7, 21}, {4, 6, 24}, {5, 5, 25},
  176. {4, 7, 28}, {5, 6, 30}, {5, 7, 35}, {6, 6, 36},
  177. {6, 7, 42}, {7, 7, 49}
  178. };
  179. /* prate/REFDIV and result save to tmp0 */
  180. tmp0 = prate;
  181. do_div(tmp0, refdiv);
  182. /* ((prate/REFDIV) x FBDIV) and result save to tmp0 */
  183. tmp0 *= fbdiv;
  184. /* ((prate/REFDIV) x FBDIV)/rate and result save to tmp0 */
  185. do_div(tmp0, rate);
  186. /* tmp0 is POSTDIV1*POSTDIV2, now we calculate div1 and div2 value */
  187. if (tmp0 <= 7) {
  188. /* (div1 * div2) <= 7, no need to use array search */
  189. *postdiv1 = tmp0;
  190. *postdiv2 = 1;
  191. return 0;
  192. }
  193. /* (div1 * div2) > 7, use array search */
  194. for (index = 0; index < ARRAY_SIZE(postdiv1_2); index++) {
  195. if (tmp0 > postdiv1_2[index][POSTDIV_RESULT_INDEX]) {
  196. continue;
  197. } else {
  198. /* found it */
  199. *postdiv1 = postdiv1_2[index][1];
  200. *postdiv2 = postdiv1_2[index][0];
  201. return 0;
  202. }
  203. }
  204. pr_warn("%s can not find in postdiv array!\n", __func__);
  205. return -EINVAL;
  206. }
  207. /**
  208. * sg2042_get_pll_ctl_setting() - Based on the given FOUTPISTDIV and the input
  209. * FREF to calculate the REFDIV/FBDIV/PSTDIV1/POSTDIV2 combination for pllctrl
  210. * register.
  211. * @req_rate: expected output clock rate, i.e. FOUTPISTDIV
  212. * @parent_rate: input parent clock rate, i.e. FREF
  213. * @best: output to hold calculated combination of REFDIV/FBDIV/PSTDIV1/POSTDIV2
  214. *
  215. * Return:
  216. * %0 - OK
  217. * %-EINVAL - invalid argument
  218. */
  219. static int sg2042_get_pll_ctl_setting(struct sg2042_pll_ctrl *best,
  220. unsigned long req_rate,
  221. unsigned long parent_rate)
  222. {
  223. unsigned int fbdiv, refdiv, postdiv1, postdiv2;
  224. unsigned long foutpostdiv;
  225. u64 foutvco;
  226. int ret;
  227. u64 tmp;
  228. if (parent_rate != PLL_FREF_SG2042) {
  229. pr_err("INVALID FREF: %ld\n", parent_rate);
  230. return -EINVAL;
  231. }
  232. if (req_rate < PLL_FOUTPOSTDIV_MIN || req_rate > PLL_FOUTPOSTDIV_MAX) {
  233. pr_alert("INVALID FOUTPOSTDIV: %ld\n", req_rate);
  234. return -EINVAL;
  235. }
  236. memset(best, 0, sizeof(struct sg2042_pll_ctrl));
  237. for (refdiv = REFDIV_MIN; refdiv < REFDIV_MAX + 1; refdiv++) {
  238. /* required by hardware: FREF/REFDIV must > 10 */
  239. tmp = parent_rate;
  240. do_div(tmp, refdiv);
  241. if (tmp <= 10)
  242. continue;
  243. for (fbdiv = FBDIV_MIN; fbdiv < FBDIV_MAX + 1; fbdiv++) {
  244. /*
  245. * FOUTVCO = FREF*FBDIV/REFDIV validation
  246. * required by hardware, FOUTVCO must [800MHz, 3200MHz]
  247. */
  248. foutvco = parent_rate * fbdiv;
  249. do_div(foutvco, refdiv);
  250. if (foutvco < PLL_FOUTVCO_MIN || foutvco > PLL_FOUTVCO_MAX)
  251. continue;
  252. ret = sg2042_pll_get_postdiv_1_2(req_rate, parent_rate,
  253. fbdiv, refdiv,
  254. &postdiv1, &postdiv2);
  255. if (ret)
  256. continue;
  257. /*
  258. * FOUTPOSTDIV = FREF*FBDIV/REFDIV/(POSTDIV1*POSTDIV2)
  259. * = FOUTVCO/(POSTDIV1*POSTDIV2)
  260. */
  261. tmp = foutvco;
  262. do_div(tmp, (postdiv1 * postdiv2));
  263. foutpostdiv = (unsigned long)tmp;
  264. /* Iterative to approach the expected value */
  265. if (abs_diff(foutpostdiv, req_rate) < abs_diff(best->freq, req_rate)) {
  266. best->freq = foutpostdiv;
  267. best->refdiv = refdiv;
  268. best->fbdiv = fbdiv;
  269. best->postdiv1 = postdiv1;
  270. best->postdiv2 = postdiv2;
  271. if (foutpostdiv == req_rate)
  272. return 0;
  273. }
  274. continue;
  275. }
  276. }
  277. if (best->freq == 0)
  278. return -EINVAL;
  279. else
  280. return 0;
  281. }
  282. /**
  283. * sg2042_clk_pll_recalc_rate() - recalc_rate callback for pll clks
  284. * @hw: ccf use to hook get sg2042_pll_clock
  285. * @parent_rate: parent rate
  286. *
  287. * The is function will be called through clk_get_rate
  288. * and return current rate after decoding reg value
  289. *
  290. * Return: Current rate recalculated.
  291. */
  292. static unsigned long sg2042_clk_pll_recalc_rate(struct clk_hw *hw,
  293. unsigned long parent_rate)
  294. {
  295. struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
  296. unsigned long rate;
  297. u32 value;
  298. value = readl(pll->base + pll->offset_ctrl);
  299. rate = sg2042_pll_recalc_rate(value, parent_rate);
  300. pr_debug("--> %s: pll_recalc_rate: val = %ld\n",
  301. clk_hw_get_name(hw), rate);
  302. return rate;
  303. }
  304. static long sg2042_clk_pll_round_rate(struct clk_hw *hw,
  305. unsigned long req_rate,
  306. unsigned long *prate)
  307. {
  308. struct sg2042_pll_ctrl pctrl_table;
  309. unsigned int value;
  310. long proper_rate;
  311. int ret;
  312. ret = sg2042_get_pll_ctl_setting(&pctrl_table, req_rate, *prate);
  313. if (ret) {
  314. proper_rate = 0;
  315. goto out;
  316. }
  317. value = sg2042_pll_ctrl_encode(&pctrl_table);
  318. proper_rate = (long)sg2042_pll_recalc_rate(value, *prate);
  319. out:
  320. pr_debug("--> %s: pll_round_rate: val = %ld\n",
  321. clk_hw_get_name(hw), proper_rate);
  322. return proper_rate;
  323. }
  324. static int sg2042_clk_pll_determine_rate(struct clk_hw *hw,
  325. struct clk_rate_request *req)
  326. {
  327. req->rate = sg2042_clk_pll_round_rate(hw, min(req->rate, req->max_rate),
  328. &req->best_parent_rate);
  329. pr_debug("--> %s: pll_determine_rate: val = %ld\n",
  330. clk_hw_get_name(hw), req->rate);
  331. return 0;
  332. }
  333. static int sg2042_clk_pll_set_rate(struct clk_hw *hw,
  334. unsigned long rate,
  335. unsigned long parent_rate)
  336. {
  337. struct sg2042_pll_clock *pll = to_sg2042_pll_clk(hw);
  338. struct sg2042_pll_ctrl pctrl_table;
  339. unsigned long flags;
  340. u32 value = 0;
  341. int ret;
  342. spin_lock_irqsave(pll->lock, flags);
  343. sg2042_pll_enable(pll, 0);
  344. ret = sg2042_get_pll_ctl_setting(&pctrl_table, rate, parent_rate);
  345. if (ret) {
  346. pr_warn("%s: Can't find a proper pll setting\n", pll->hw.init->name);
  347. goto out;
  348. }
  349. value = sg2042_pll_ctrl_encode(&pctrl_table);
  350. /* write the value to top register */
  351. writel(value, pll->base + pll->offset_ctrl);
  352. out:
  353. sg2042_pll_enable(pll, 1);
  354. spin_unlock_irqrestore(pll->lock, flags);
  355. pr_debug("--> %s: pll_set_rate: val = 0x%x\n",
  356. clk_hw_get_name(hw), value);
  357. return ret;
  358. }
  359. static const struct clk_ops sg2042_clk_pll_ops = {
  360. .recalc_rate = sg2042_clk_pll_recalc_rate,
  361. .round_rate = sg2042_clk_pll_round_rate,
  362. .determine_rate = sg2042_clk_pll_determine_rate,
  363. .set_rate = sg2042_clk_pll_set_rate,
  364. };
  365. static const struct clk_ops sg2042_clk_pll_ro_ops = {
  366. .recalc_rate = sg2042_clk_pll_recalc_rate,
  367. .round_rate = sg2042_clk_pll_round_rate,
  368. };
  369. /*
  370. * Clock initialization macro naming rules:
  371. * FW: use CLK_HW_INIT_FW_NAME
  372. * RO: means Read-Only
  373. */
  374. #define SG2042_PLL_FW(_id, _name, _parent, _r_ctrl, _shift) \
  375. { \
  376. .id = _id, \
  377. .hw.init = CLK_HW_INIT_FW_NAME( \
  378. _name, \
  379. _parent, \
  380. &sg2042_clk_pll_ops, \
  381. CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
  382. .offset_ctrl = _r_ctrl, \
  383. .shift_status_lock = 8 + (_shift), \
  384. .shift_status_updating = _shift, \
  385. .shift_enable = _shift, \
  386. }
  387. #define SG2042_PLL_FW_RO(_id, _name, _parent, _r_ctrl, _shift) \
  388. { \
  389. .id = _id, \
  390. .hw.init = CLK_HW_INIT_FW_NAME( \
  391. _name, \
  392. _parent, \
  393. &sg2042_clk_pll_ro_ops, \
  394. CLK_GET_RATE_NOCACHE | CLK_GET_ACCURACY_NOCACHE),\
  395. .offset_ctrl = _r_ctrl, \
  396. .shift_status_lock = 8 + (_shift), \
  397. .shift_status_updating = _shift, \
  398. .shift_enable = _shift, \
  399. }
  400. static struct sg2042_pll_clock sg2042_pll_clks[] = {
  401. SG2042_PLL_FW(MPLL_CLK, "mpll_clock", "cgi_main", R_MPLL_CONTROL, 0),
  402. SG2042_PLL_FW_RO(FPLL_CLK, "fpll_clock", "cgi_main", R_FPLL_CONTROL, 3),
  403. SG2042_PLL_FW_RO(DPLL0_CLK, "dpll0_clock", "cgi_dpll0", R_DPLL0_CONTROL, 4),
  404. SG2042_PLL_FW_RO(DPLL1_CLK, "dpll1_clock", "cgi_dpll1", R_DPLL1_CONTROL, 5),
  405. };
  406. static DEFINE_SPINLOCK(sg2042_clk_lock);
  407. static int sg2042_clk_register_plls(struct device *dev,
  408. struct sg2042_clk_data *clk_data,
  409. struct sg2042_pll_clock pll_clks[],
  410. int num_pll_clks)
  411. {
  412. struct sg2042_pll_clock *pll;
  413. struct clk_hw *hw;
  414. int i, ret = 0;
  415. for (i = 0; i < num_pll_clks; i++) {
  416. pll = &pll_clks[i];
  417. /* assign these for ops usage during registration */
  418. pll->base = clk_data->iobase;
  419. pll->lock = &sg2042_clk_lock;
  420. hw = &pll->hw;
  421. ret = devm_clk_hw_register(dev, hw);
  422. if (ret) {
  423. pr_err("failed to register clock %s\n", pll->hw.init->name);
  424. break;
  425. }
  426. clk_data->onecell_data.hws[pll->id] = hw;
  427. }
  428. return ret;
  429. }
  430. static int sg2042_init_clkdata(struct platform_device *pdev,
  431. int num_clks,
  432. struct sg2042_clk_data **pp_clk_data)
  433. {
  434. struct sg2042_clk_data *clk_data;
  435. clk_data = devm_kzalloc(&pdev->dev,
  436. struct_size(clk_data, onecell_data.hws, num_clks),
  437. GFP_KERNEL);
  438. if (!clk_data)
  439. return -ENOMEM;
  440. clk_data->iobase = devm_platform_ioremap_resource(pdev, 0);
  441. if (WARN_ON(IS_ERR(clk_data->iobase)))
  442. return PTR_ERR(clk_data->iobase);
  443. clk_data->onecell_data.num = num_clks;
  444. *pp_clk_data = clk_data;
  445. return 0;
  446. }
  447. static int sg2042_pll_probe(struct platform_device *pdev)
  448. {
  449. struct sg2042_clk_data *clk_data = NULL;
  450. int num_clks;
  451. int ret;
  452. num_clks = ARRAY_SIZE(sg2042_pll_clks);
  453. ret = sg2042_init_clkdata(pdev, num_clks, &clk_data);
  454. if (ret)
  455. goto error_out;
  456. ret = sg2042_clk_register_plls(&pdev->dev, clk_data, sg2042_pll_clks,
  457. num_clks);
  458. if (ret)
  459. goto error_out;
  460. return devm_of_clk_add_hw_provider(&pdev->dev,
  461. of_clk_hw_onecell_get,
  462. &clk_data->onecell_data);
  463. error_out:
  464. pr_err("%s failed error number %d\n", __func__, ret);
  465. return ret;
  466. }
  467. static const struct of_device_id sg2042_pll_match[] = {
  468. { .compatible = "sophgo,sg2042-pll" },
  469. { /* sentinel */ }
  470. };
  471. MODULE_DEVICE_TABLE(of, sg2042_pll_match);
  472. static struct platform_driver sg2042_pll_driver = {
  473. .probe = sg2042_pll_probe,
  474. .driver = {
  475. .name = "clk-sophgo-sg2042-pll",
  476. .of_match_table = sg2042_pll_match,
  477. .suppress_bind_attrs = true,
  478. },
  479. };
  480. module_platform_driver(sg2042_pll_driver);
  481. MODULE_AUTHOR("Chen Wang");
  482. MODULE_DESCRIPTION("Sophgo SG2042 pll clock driver");
  483. MODULE_LICENSE("GPL");