clk-loongson2.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Author: Yinbo Zhu <zhuyinbo@loongson.cn>
  4. * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
  5. */
  6. #include <linux/err.h>
  7. #include <linux/init.h>
  8. #include <linux/clk-provider.h>
  9. #include <linux/slab.h>
  10. #include <linux/module.h>
  11. #include <linux/platform_device.h>
  12. #include <linux/io-64-nonatomic-lo-hi.h>
  13. #include <dt-bindings/clock/loongson,ls2k-clk.h>
  14. enum loongson2_clk_type {
  15. CLK_TYPE_PLL,
  16. CLK_TYPE_SCALE,
  17. CLK_TYPE_DIVIDER,
  18. CLK_TYPE_GATE,
  19. CLK_TYPE_FIXED,
  20. CLK_TYPE_NONE,
  21. };
  22. struct loongson2_clk_provider {
  23. void __iomem *base;
  24. struct device *dev;
  25. spinlock_t clk_lock; /* protect access to DIV registers */
  26. /* Must be last --ends in a flexible-array member. */
  27. struct clk_hw_onecell_data clk_data;
  28. };
  29. struct loongson2_clk_data {
  30. struct clk_hw hw;
  31. void __iomem *reg;
  32. u8 div_shift;
  33. u8 div_width;
  34. u8 mult_shift;
  35. u8 mult_width;
  36. u8 bit_idx;
  37. };
  38. struct loongson2_clk_board_info {
  39. u8 id;
  40. enum loongson2_clk_type type;
  41. const char *name;
  42. const char *parent_name;
  43. unsigned long fixed_rate;
  44. unsigned long flags;
  45. u8 reg_offset;
  46. u8 div_shift;
  47. u8 div_width;
  48. u8 mult_shift;
  49. u8 mult_width;
  50. u8 bit_idx;
  51. };
  52. #define CLK_DIV(_id, _name, _pname, _offset, _dshift, _dwidth) \
  53. { \
  54. .id = _id, \
  55. .type = CLK_TYPE_DIVIDER, \
  56. .name = _name, \
  57. .parent_name = _pname, \
  58. .reg_offset = _offset, \
  59. .div_shift = _dshift, \
  60. .div_width = _dwidth, \
  61. }
  62. #define CLK_PLL(_id, _name, _offset, _mshift, _mwidth, \
  63. _dshift, _dwidth) \
  64. { \
  65. .id = _id, \
  66. .type = CLK_TYPE_PLL, \
  67. .name = _name, \
  68. .parent_name = NULL, \
  69. .reg_offset = _offset, \
  70. .mult_shift = _mshift, \
  71. .mult_width = _mwidth, \
  72. .div_shift = _dshift, \
  73. .div_width = _dwidth, \
  74. }
  75. #define CLK_SCALE(_id, _name, _pname, _offset, \
  76. _dshift, _dwidth) \
  77. { \
  78. .id = _id, \
  79. .type = CLK_TYPE_SCALE, \
  80. .name = _name, \
  81. .parent_name = _pname, \
  82. .reg_offset = _offset, \
  83. .div_shift = _dshift, \
  84. .div_width = _dwidth, \
  85. }
  86. #define CLK_SCALE_MODE(_id, _name, _pname, _offset, \
  87. _dshift, _dwidth, _midx) \
  88. { \
  89. .id = _id, \
  90. .type = CLK_TYPE_SCALE, \
  91. .name = _name, \
  92. .parent_name = _pname, \
  93. .reg_offset = _offset, \
  94. .div_shift = _dshift, \
  95. .div_width = _dwidth, \
  96. .bit_idx = _midx + 1, \
  97. }
  98. #define CLK_GATE(_id, _name, _pname, _offset, _bidx) \
  99. { \
  100. .id = _id, \
  101. .type = CLK_TYPE_GATE, \
  102. .name = _name, \
  103. .parent_name = _pname, \
  104. .reg_offset = _offset, \
  105. .bit_idx = _bidx, \
  106. }
  107. #define CLK_GATE_FLAGS(_id, _name, _pname, _offset, _bidx, \
  108. _flags) \
  109. { \
  110. .id = _id, \
  111. .type = CLK_TYPE_GATE, \
  112. .name = _name, \
  113. .parent_name = _pname, \
  114. .reg_offset = _offset, \
  115. .bit_idx = _bidx, \
  116. .flags = _flags \
  117. }
  118. #define CLK_FIXED(_id, _name, _pname, _rate) \
  119. { \
  120. .id = _id, \
  121. .type = CLK_TYPE_FIXED, \
  122. .name = _name, \
  123. .parent_name = _pname, \
  124. .fixed_rate = _rate, \
  125. }
  126. static const struct loongson2_clk_board_info ls2k0300_clks[] = {
  127. /* Reference Clock */
  128. CLK_PLL(LS2K0300_NODE_PLL, "pll_node", 0x00, 15, 9, 8, 7),
  129. CLK_PLL(LS2K0300_DDR_PLL, "pll_ddr", 0x08, 15, 9, 8, 7),
  130. CLK_PLL(LS2K0300_PIX_PLL, "pll_pix", 0x10, 15, 9, 8, 7),
  131. CLK_FIXED(LS2K0300_CLK_STABLE, "clk_stable", NULL, 100000000),
  132. CLK_FIXED(LS2K0300_CLK_THSENS, "clk_thsens", NULL, 10000000),
  133. /* Node PLL */
  134. CLK_DIV(LS2K0300_CLK_NODE_DIV, "clk_node_div", "pll_node", 0x00, 24, 7),
  135. CLK_DIV(LS2K0300_CLK_GMAC_DIV, "clk_gmac_div", "pll_node", 0x04, 0, 7),
  136. CLK_DIV(LS2K0300_CLK_I2S_DIV, "clk_i2s_div", "pll_node", 0x04, 8, 7),
  137. CLK_GATE(LS2K0300_CLK_NODE_PLL_GATE, "clk_node_pll_gate", "clk_node_div", 0x00, 0),
  138. CLK_GATE(LS2K0300_CLK_GMAC_GATE, "clk_gmac_gate", "clk_gmac_div", 0x00, 1),
  139. CLK_GATE(LS2K0300_CLK_I2S_GATE, "clk_i2s_gate", "clk_i2s_div", 0x00, 2),
  140. CLK_GATE_FLAGS(LS2K0300_CLK_NODE_GATE, "clk_node_gate", "clk_node_scale", 0x24, 0,
  141. CLK_IS_CRITICAL),
  142. CLK_SCALE_MODE(LS2K0300_CLK_NODE_SCALE, "clk_node_scale", "clk_node_pll_gate", 0x20, 0, 3,
  143. 3),
  144. /* DDR PLL */
  145. CLK_DIV(LS2K0300_CLK_DDR_DIV, "clk_ddr_div", "pll_ddr", 0x08, 24, 7),
  146. CLK_DIV(LS2K0300_CLK_NET_DIV, "clk_net_div", "pll_ddr", 0x0c, 0, 7),
  147. CLK_DIV(LS2K0300_CLK_DEV_DIV, "clk_dev_div", "pll_ddr", 0x0c, 8, 7),
  148. CLK_GATE(LS2K0300_CLK_NET_GATE, "clk_net_gate", "clk_net_div", 0x08, 1),
  149. CLK_GATE(LS2K0300_CLK_DEV_GATE, "clk_dev_gate", "clk_dev_div", 0x08, 2),
  150. CLK_GATE_FLAGS(LS2K0300_CLK_DDR_GATE, "clk_ddr_gate", "clk_ddr_div", 0x08, 0,
  151. CLK_IS_CRITICAL),
  152. /* PIX PLL */
  153. CLK_DIV(LS2K0300_CLK_PIX_DIV, "clk_pix_div", "pll_pix", 0x10, 24, 7),
  154. CLK_DIV(LS2K0300_CLK_GMACBP_DIV, "clk_gmacbp_div", "pll_pix", 0x14, 0, 7),
  155. CLK_GATE(LS2K0300_CLK_PIX_PLL_GATE, "clk_pix_pll_gate", "clk_pix_div", 0x10, 0),
  156. CLK_GATE(LS2K0300_CLK_PIX_GATE, "clk_pix_gate", "clk_pix_scale", 0x24, 6),
  157. CLK_GATE(LS2K0300_CLK_GMACBP_GATE, "clk_gmacbp_gate", "clk_gmacbp_div", 0x10, 1),
  158. CLK_SCALE_MODE(LS2K0300_CLK_PIX_SCALE, "clk_pix_scale", "clk_pix_pll_gate", 0x20, 4, 3, 7),
  159. /* clk_dev_gate */
  160. CLK_DIV(LS2K0300_CLK_SDIO_SCALE, "clk_sdio_scale", "clk_dev_gate", 0x20, 24, 4),
  161. CLK_GATE(LS2K0300_CLK_USB_GATE, "clk_usb_gate", "clk_usb_scale", 0x24, 2),
  162. CLK_GATE(LS2K0300_CLK_SDIO_GATE, "clk_sdio_gate", "clk_sdio_scale", 0x24, 4),
  163. CLK_GATE(LS2K0300_CLK_APB_GATE, "clk_apb_gate", "clk_apb_scale", 0x24, 3),
  164. CLK_GATE_FLAGS(LS2K0300_CLK_BOOT_GATE, "clk_boot_gate", "clk_boot_scale", 0x24, 1,
  165. CLK_IS_CRITICAL),
  166. CLK_SCALE_MODE(LS2K0300_CLK_USB_SCALE, "clk_usb_scale", "clk_dev_gate", 0x20, 12, 3, 15),
  167. CLK_SCALE_MODE(LS2K0300_CLK_APB_SCALE, "clk_apb_scale", "clk_dev_gate", 0x20, 16, 3, 19),
  168. CLK_SCALE_MODE(LS2K0300_CLK_BOOT_SCALE, "clk_boot_scale", "clk_dev_gate", 0x20, 8, 3, 11),
  169. };
  170. static const struct loongson2_clk_board_info ls2k0500_clks[] = {
  171. CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 16, 8, 8, 6),
  172. CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x8, 16, 8, 8, 6),
  173. CLK_PLL(LOONGSON2_DC_PLL, "pll_soc", 0x10, 16, 8, 8, 6),
  174. CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x18, 16, 8, 8, 6),
  175. CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x20, 16, 8, 8, 6),
  176. CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0, 24, 6),
  177. CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x8, 24, 6),
  178. CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0xc, 8, 6),
  179. CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_soc", 0x10, 24, 6),
  180. CLK_DIV(LOONGSON2_DC_CLK, "clk_sb", "pll_soc", 0x14, 0, 6),
  181. CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_soc", 0x14, 8, 6),
  182. CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x18, 24, 6),
  183. CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x20, 24, 6),
  184. CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", "clk_sb", 0x28, 8, 3),
  185. CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_sb", 0x28, 12, 3),
  186. CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_sb", 0x28, 16, 3),
  187. CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_sb", 0x28, 20, 3),
  188. { /* Sentinel */ },
  189. };
  190. static const struct loongson2_clk_board_info ls2k1000_clks[] = {
  191. CLK_PLL(LOONGSON2_NODE_PLL, "pll_node", 0, 32, 10, 26, 6),
  192. CLK_PLL(LOONGSON2_DDR_PLL, "pll_ddr", 0x10, 32, 10, 26, 6),
  193. CLK_PLL(LOONGSON2_DC_PLL, "pll_dc", 0x20, 32, 10, 26, 6),
  194. CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 32, 10, 26, 6),
  195. CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 32, 10, 26, 6),
  196. CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "pll_node", 0x8, 0, 6),
  197. CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "pll_ddr", 0x18, 0, 6),
  198. CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "pll_ddr", 0x18, 22, 6),
  199. /*
  200. * The hda clk divisor in the upper 32bits and the clk-prodiver
  201. * layer code doesn't support 64bit io operation thus a conversion
  202. * is required that subtract shift by 32 and add 4byte to the hda
  203. * address
  204. */
  205. CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "pll_ddr", 0x22, 12, 7),
  206. CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "pll_dc", 0x28, 0, 6),
  207. CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "pll_dc", 0x28, 22, 6),
  208. CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x38, 0, 6),
  209. CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x38, 0, 6),
  210. CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 8, 3),
  211. CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_gmac", 0x50, 12, 3),
  212. CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_gmac", 0x50, 16, 3),
  213. CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_gmac", 0x50, 20, 3),
  214. { /* Sentinel */ },
  215. };
  216. static const struct loongson2_clk_board_info ls2k2000_clks[] = {
  217. CLK_PLL(LOONGSON2_DC_PLL, "pll_0", 0, 21, 9, 32, 6),
  218. CLK_PLL(LOONGSON2_DDR_PLL, "pll_1", 0x10, 21, 9, 32, 6),
  219. CLK_PLL(LOONGSON2_NODE_PLL, "pll_2", 0x20, 21, 9, 32, 6),
  220. CLK_PLL(LOONGSON2_PIX0_PLL, "pll_pix0", 0x30, 21, 9, 32, 6),
  221. CLK_PLL(LOONGSON2_PIX1_PLL, "pll_pix1", 0x40, 21, 9, 32, 6),
  222. CLK_GATE(LOONGSON2_OUT0_GATE, "out0_gate", "pll_0", 0, 40),
  223. CLK_GATE(LOONGSON2_GMAC_GATE, "gmac_gate", "pll_0", 0, 41),
  224. CLK_GATE(LOONGSON2_RIO_GATE, "rio_gate", "pll_0", 0, 42),
  225. CLK_GATE(LOONGSON2_DC_GATE, "dc_gate", "pll_1", 0x10, 40),
  226. CLK_GATE(LOONGSON2_DDR_GATE, "ddr_gate", "pll_1", 0x10, 41),
  227. CLK_GATE(LOONGSON2_GPU_GATE, "gpu_gate", "pll_1", 0x10, 42),
  228. CLK_GATE(LOONGSON2_HDA_GATE, "hda_gate", "pll_2", 0x20, 40),
  229. CLK_GATE(LOONGSON2_NODE_GATE, "node_gate", "pll_2", 0x20, 41),
  230. CLK_GATE(LOONGSON2_EMMC_GATE, "emmc_gate", "pll_2", 0x20, 42),
  231. CLK_GATE(LOONGSON2_PIX0_GATE, "pix0_gate", "pll_pix0", 0x30, 40),
  232. CLK_GATE(LOONGSON2_PIX1_GATE, "pix1_gate", "pll_pix1", 0x40, 40),
  233. CLK_DIV(LOONGSON2_OUT0_CLK, "clk_out0", "out0_gate", 0, 0, 6),
  234. CLK_DIV(LOONGSON2_GMAC_CLK, "clk_gmac", "gmac_gate", 0, 7, 6),
  235. CLK_DIV(LOONGSON2_RIO_CLK, "clk_rio", "rio_gate", 0, 14, 6),
  236. CLK_DIV(LOONGSON2_DC_CLK, "clk_dc", "dc_gate", 0x10, 0, 6),
  237. CLK_DIV(LOONGSON2_GPU_CLK, "clk_gpu", "gpu_gate", 0x10, 7, 6),
  238. CLK_DIV(LOONGSON2_DDR_CLK, "clk_ddr", "ddr_gate", 0x10, 14, 6),
  239. CLK_DIV(LOONGSON2_HDA_CLK, "clk_hda", "hda_gate", 0x20, 0, 6),
  240. CLK_DIV(LOONGSON2_NODE_CLK, "clk_node", "node_gate", 0x20, 7, 6),
  241. CLK_DIV(LOONGSON2_EMMC_CLK, "clk_emmc", "emmc_gate", 0x20, 14, 6),
  242. CLK_DIV(LOONGSON2_PIX0_CLK, "clk_pix0", "pll_pix0", 0x30, 0, 6),
  243. CLK_DIV(LOONGSON2_PIX1_CLK, "clk_pix1", "pll_pix1", 0x40, 0, 6),
  244. CLK_SCALE(LOONGSON2_SATA_CLK, "clk_sata", "clk_out0", 0x50, 12, 3),
  245. CLK_SCALE(LOONGSON2_USB_CLK, "clk_usb", "clk_out0", 0x50, 16, 3),
  246. CLK_SCALE(LOONGSON2_APB_CLK, "clk_apb", "clk_node", 0x50, 20, 3),
  247. CLK_SCALE(LOONGSON2_BOOT_CLK, "clk_boot", NULL, 0x50, 23, 3),
  248. CLK_SCALE(LOONGSON2_DES_CLK, "clk_des", "clk_node", 0x50, 40, 3),
  249. CLK_SCALE(LOONGSON2_I2S_CLK, "clk_i2s", "clk_node", 0x50, 44, 3),
  250. CLK_FIXED(LOONGSON2_MISC_CLK, "clk_misc", NULL, 50000000),
  251. { /* Sentinel */ },
  252. };
  253. static inline struct loongson2_clk_data *to_loongson2_clk(struct clk_hw *hw)
  254. {
  255. return container_of(hw, struct loongson2_clk_data, hw);
  256. }
  257. static inline unsigned long loongson2_rate_part(u64 val, u8 shift, u8 width)
  258. {
  259. return (val & GENMASK(shift + width - 1, shift)) >> shift;
  260. }
  261. static unsigned long loongson2_pll_recalc_rate(struct clk_hw *hw,
  262. unsigned long parent_rate)
  263. {
  264. u64 val, mult, div;
  265. struct loongson2_clk_data *clk = to_loongson2_clk(hw);
  266. val = readq(clk->reg);
  267. mult = loongson2_rate_part(val, clk->mult_shift, clk->mult_width);
  268. div = loongson2_rate_part(val, clk->div_shift, clk->div_width);
  269. return div_u64((u64)parent_rate * mult, div);
  270. }
  271. static const struct clk_ops loongson2_pll_recalc_ops = {
  272. .recalc_rate = loongson2_pll_recalc_rate,
  273. };
  274. static unsigned long loongson2_freqscale_recalc_rate(struct clk_hw *hw,
  275. unsigned long parent_rate)
  276. {
  277. u64 val, scale;
  278. u32 mode = 0;
  279. struct loongson2_clk_data *clk = to_loongson2_clk(hw);
  280. val = readq(clk->reg);
  281. scale = loongson2_rate_part(val, clk->div_shift, clk->div_width) + 1;
  282. if (clk->bit_idx)
  283. mode = val & BIT(clk->bit_idx - 1);
  284. return mode == 0 ? div_u64((u64)parent_rate * scale, 8) :
  285. div_u64((u64)parent_rate, scale);
  286. }
  287. static const struct clk_ops loongson2_freqscale_recalc_ops = {
  288. .recalc_rate = loongson2_freqscale_recalc_rate,
  289. };
  290. static struct clk_hw *loongson2_clk_register(const char *parent,
  291. struct loongson2_clk_provider *clp,
  292. const struct loongson2_clk_board_info *cld,
  293. const struct clk_ops *ops)
  294. {
  295. int ret;
  296. struct clk_hw *hw;
  297. struct loongson2_clk_data *clk;
  298. struct clk_init_data init = { };
  299. clk = devm_kzalloc(clp->dev, sizeof(*clk), GFP_KERNEL);
  300. if (!clk)
  301. return ERR_PTR(-ENOMEM);
  302. init.name = cld->name;
  303. init.ops = ops;
  304. init.flags = 0;
  305. init.num_parents = 1;
  306. init.parent_names = &parent;
  307. clk->reg = clp->base + cld->reg_offset;
  308. clk->div_shift = cld->div_shift;
  309. clk->div_width = cld->div_width;
  310. clk->mult_shift = cld->mult_shift;
  311. clk->mult_width = cld->mult_width;
  312. clk->bit_idx = cld->bit_idx;
  313. clk->hw.init = &init;
  314. hw = &clk->hw;
  315. ret = devm_clk_hw_register(clp->dev, hw);
  316. if (ret)
  317. clk = ERR_PTR(ret);
  318. return hw;
  319. }
  320. static int loongson2_clk_probe(struct platform_device *pdev)
  321. {
  322. int i, clks_num = 0;
  323. struct clk_hw *hw;
  324. struct device *dev = &pdev->dev;
  325. struct loongson2_clk_provider *clp;
  326. const struct loongson2_clk_board_info *p, *data;
  327. const char *refclk_name, *parent_name;
  328. data = device_get_match_data(dev);
  329. if (!data)
  330. return -EINVAL;
  331. refclk_name = of_clk_get_parent_name(dev->of_node, 0);
  332. if (IS_ERR(refclk_name))
  333. return dev_err_probe(dev, PTR_ERR(refclk_name),
  334. "failed to get refclk name\n");
  335. for (p = data; p->name; p++)
  336. clks_num = max(clks_num, p->id + 1);
  337. clp = devm_kzalloc(dev, struct_size(clp, clk_data.hws, clks_num),
  338. GFP_KERNEL);
  339. if (!clp)
  340. return -ENOMEM;
  341. clp->base = devm_platform_ioremap_resource(pdev, 0);
  342. if (IS_ERR(clp->base))
  343. return PTR_ERR(clp->base);
  344. spin_lock_init(&clp->clk_lock);
  345. clp->clk_data.num = clks_num;
  346. clp->dev = dev;
  347. /* Avoid returning NULL for unused id */
  348. memset_p((void **)clp->clk_data.hws, ERR_PTR(-ENOENT), clks_num);
  349. for (i = 0; i < clks_num; i++) {
  350. p = &data[i];
  351. parent_name = p->parent_name ? p->parent_name : refclk_name;
  352. switch (p->type) {
  353. case CLK_TYPE_PLL:
  354. hw = loongson2_clk_register(parent_name, clp, p,
  355. &loongson2_pll_recalc_ops);
  356. break;
  357. case CLK_TYPE_SCALE:
  358. hw = loongson2_clk_register(parent_name, clp, p,
  359. &loongson2_freqscale_recalc_ops);
  360. break;
  361. case CLK_TYPE_DIVIDER:
  362. hw = devm_clk_hw_register_divider(dev, p->name,
  363. parent_name, 0,
  364. clp->base + p->reg_offset,
  365. p->div_shift, p->div_width,
  366. CLK_DIVIDER_ONE_BASED |
  367. CLK_DIVIDER_ALLOW_ZERO,
  368. &clp->clk_lock);
  369. break;
  370. case CLK_TYPE_GATE:
  371. hw = devm_clk_hw_register_gate(dev, p->name, parent_name,
  372. p->flags,
  373. clp->base + p->reg_offset,
  374. p->bit_idx, 0,
  375. &clp->clk_lock);
  376. break;
  377. case CLK_TYPE_FIXED:
  378. hw = devm_clk_hw_register_fixed_rate(dev, p->name, parent_name,
  379. 0, p->fixed_rate);
  380. break;
  381. default:
  382. return dev_err_probe(dev, -EINVAL, "Invalid clk type\n");
  383. }
  384. if (IS_ERR(hw))
  385. return dev_err_probe(dev, PTR_ERR(hw),
  386. "Register clk: %s, type: %u failed!\n",
  387. p->name, p->type);
  388. clp->clk_data.hws[p->id] = hw;
  389. }
  390. return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, &clp->clk_data);
  391. }
  392. static const struct of_device_id loongson2_clk_match_table[] = {
  393. { .compatible = "loongson,ls2k0300-clk", .data = &ls2k0300_clks },
  394. { .compatible = "loongson,ls2k0500-clk", .data = &ls2k0500_clks },
  395. { .compatible = "loongson,ls2k-clk", .data = &ls2k1000_clks },
  396. { .compatible = "loongson,ls2k2000-clk", .data = &ls2k2000_clks },
  397. { }
  398. };
  399. MODULE_DEVICE_TABLE(of, loongson2_clk_match_table);
  400. static struct platform_driver loongson2_clk_driver = {
  401. .probe = loongson2_clk_probe,
  402. .driver = {
  403. .name = "loongson2-clk",
  404. .of_match_table = loongson2_clk_match_table,
  405. },
  406. };
  407. module_platform_driver(loongson2_clk_driver);
  408. MODULE_DESCRIPTION("Loongson2 clock driver");
  409. MODULE_AUTHOR("Loongson Technology Corporation Limited");
  410. MODULE_LICENSE("GPL");