ccu-sun8i-de2.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2017 Icenowy Zheng <[email protected]>
  4. */
  5. #include <linux/clk.h>
  6. #include <linux/clk-provider.h>
  7. #include <linux/module.h>
  8. #include <linux/of_device.h>
  9. #include <linux/platform_device.h>
  10. #include <linux/reset.h>
  11. #include "ccu_common.h"
  12. #include "ccu_div.h"
  13. #include "ccu_gate.h"
  14. #include "ccu_reset.h"
  15. #include "ccu-sun8i-de2.h"
  16. static SUNXI_CCU_GATE(bus_mixer0_clk, "bus-mixer0", "bus-de",
  17. 0x04, BIT(0), 0);
  18. static SUNXI_CCU_GATE(bus_mixer1_clk, "bus-mixer1", "bus-de",
  19. 0x04, BIT(1), 0);
  20. static SUNXI_CCU_GATE(bus_wb_clk, "bus-wb", "bus-de",
  21. 0x04, BIT(2), 0);
  22. static SUNXI_CCU_GATE(bus_rot_clk, "bus-rot", "bus-de",
  23. 0x04, BIT(3), 0);
  24. static SUNXI_CCU_GATE(mixer0_clk, "mixer0", "mixer0-div",
  25. 0x00, BIT(0), CLK_SET_RATE_PARENT);
  26. static SUNXI_CCU_GATE(mixer1_clk, "mixer1", "mixer1-div",
  27. 0x00, BIT(1), CLK_SET_RATE_PARENT);
  28. static SUNXI_CCU_GATE(wb_clk, "wb", "wb-div",
  29. 0x00, BIT(2), CLK_SET_RATE_PARENT);
  30. static SUNXI_CCU_GATE(rot_clk, "rot", "rot-div",
  31. 0x00, BIT(3), CLK_SET_RATE_PARENT);
  32. static SUNXI_CCU_M(mixer0_div_clk, "mixer0-div", "de", 0x0c, 0, 4,
  33. CLK_SET_RATE_PARENT);
  34. static SUNXI_CCU_M(mixer1_div_clk, "mixer1-div", "de", 0x0c, 4, 4,
  35. CLK_SET_RATE_PARENT);
  36. static SUNXI_CCU_M(wb_div_clk, "wb-div", "de", 0x0c, 8, 4,
  37. CLK_SET_RATE_PARENT);
  38. static SUNXI_CCU_M(rot_div_clk, "rot-div", "de", 0x0c, 0x0c, 4,
  39. CLK_SET_RATE_PARENT);
  40. static SUNXI_CCU_M(mixer0_div_a83_clk, "mixer0-div", "pll-de", 0x0c, 0, 4,
  41. CLK_SET_RATE_PARENT);
  42. static SUNXI_CCU_M(mixer1_div_a83_clk, "mixer1-div", "pll-de", 0x0c, 4, 4,
  43. CLK_SET_RATE_PARENT);
  44. static SUNXI_CCU_M(wb_div_a83_clk, "wb-div", "pll-de", 0x0c, 8, 4,
  45. CLK_SET_RATE_PARENT);
  46. static SUNXI_CCU_M(rot_div_a83_clk, "rot-div", "pll-de", 0x0c, 0x0c, 4,
  47. CLK_SET_RATE_PARENT);
  48. static struct ccu_common *sun8i_de2_ccu_clks[] = {
  49. &mixer0_clk.common,
  50. &mixer1_clk.common,
  51. &wb_clk.common,
  52. &rot_clk.common,
  53. &bus_mixer0_clk.common,
  54. &bus_mixer1_clk.common,
  55. &bus_wb_clk.common,
  56. &bus_rot_clk.common,
  57. &mixer0_div_clk.common,
  58. &mixer1_div_clk.common,
  59. &wb_div_clk.common,
  60. &rot_div_clk.common,
  61. &mixer0_div_a83_clk.common,
  62. &mixer1_div_a83_clk.common,
  63. &wb_div_a83_clk.common,
  64. &rot_div_a83_clk.common,
  65. };
  66. static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = {
  67. .hws = {
  68. [CLK_MIXER0] = &mixer0_clk.common.hw,
  69. [CLK_MIXER1] = &mixer1_clk.common.hw,
  70. [CLK_WB] = &wb_clk.common.hw,
  71. [CLK_ROT] = &rot_clk.common.hw,
  72. [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw,
  73. [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw,
  74. [CLK_BUS_WB] = &bus_wb_clk.common.hw,
  75. [CLK_BUS_ROT] = &bus_rot_clk.common.hw,
  76. [CLK_MIXER0_DIV] = &mixer0_div_a83_clk.common.hw,
  77. [CLK_MIXER1_DIV] = &mixer1_div_a83_clk.common.hw,
  78. [CLK_WB_DIV] = &wb_div_a83_clk.common.hw,
  79. [CLK_ROT_DIV] = &rot_div_a83_clk.common.hw,
  80. },
  81. .num = CLK_NUMBER_WITH_ROT,
  82. };
  83. static struct clk_hw_onecell_data sun8i_h3_de2_hw_clks = {
  84. .hws = {
  85. [CLK_MIXER0] = &mixer0_clk.common.hw,
  86. [CLK_MIXER1] = &mixer1_clk.common.hw,
  87. [CLK_WB] = &wb_clk.common.hw,
  88. [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw,
  89. [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw,
  90. [CLK_BUS_WB] = &bus_wb_clk.common.hw,
  91. [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw,
  92. [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw,
  93. [CLK_WB_DIV] = &wb_div_clk.common.hw,
  94. },
  95. .num = CLK_NUMBER_WITHOUT_ROT,
  96. };
  97. static struct clk_hw_onecell_data sun8i_v3s_de2_hw_clks = {
  98. .hws = {
  99. [CLK_MIXER0] = &mixer0_clk.common.hw,
  100. [CLK_WB] = &wb_clk.common.hw,
  101. [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw,
  102. [CLK_BUS_WB] = &bus_wb_clk.common.hw,
  103. [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw,
  104. [CLK_WB_DIV] = &wb_div_clk.common.hw,
  105. },
  106. .num = CLK_NUMBER_WITHOUT_ROT,
  107. };
  108. static struct clk_hw_onecell_data sun50i_a64_de2_hw_clks = {
  109. .hws = {
  110. [CLK_MIXER0] = &mixer0_clk.common.hw,
  111. [CLK_MIXER1] = &mixer1_clk.common.hw,
  112. [CLK_WB] = &wb_clk.common.hw,
  113. [CLK_ROT] = &rot_clk.common.hw,
  114. [CLK_BUS_MIXER0] = &bus_mixer0_clk.common.hw,
  115. [CLK_BUS_MIXER1] = &bus_mixer1_clk.common.hw,
  116. [CLK_BUS_WB] = &bus_wb_clk.common.hw,
  117. [CLK_BUS_ROT] = &bus_rot_clk.common.hw,
  118. [CLK_MIXER0_DIV] = &mixer0_div_clk.common.hw,
  119. [CLK_MIXER1_DIV] = &mixer1_div_clk.common.hw,
  120. [CLK_WB_DIV] = &wb_div_clk.common.hw,
  121. [CLK_ROT_DIV] = &rot_div_clk.common.hw,
  122. },
  123. .num = CLK_NUMBER_WITH_ROT,
  124. };
  125. static struct ccu_reset_map sun8i_a83t_de2_resets[] = {
  126. [RST_MIXER0] = { 0x08, BIT(0) },
  127. /*
  128. * Mixer1 reset line is shared with wb, so only RST_WB is
  129. * exported here.
  130. */
  131. [RST_WB] = { 0x08, BIT(2) },
  132. [RST_ROT] = { 0x08, BIT(3) },
  133. };
  134. static struct ccu_reset_map sun8i_h3_de2_resets[] = {
  135. [RST_MIXER0] = { 0x08, BIT(0) },
  136. /*
  137. * Mixer1 reset line is shared with wb, so only RST_WB is
  138. * exported here.
  139. * V3s doesn't have mixer1, so it also shares this struct.
  140. */
  141. [RST_WB] = { 0x08, BIT(2) },
  142. };
  143. static struct ccu_reset_map sun50i_a64_de2_resets[] = {
  144. [RST_MIXER0] = { 0x08, BIT(0) },
  145. [RST_MIXER1] = { 0x08, BIT(1) },
  146. [RST_WB] = { 0x08, BIT(2) },
  147. [RST_ROT] = { 0x08, BIT(3) },
  148. };
  149. static struct ccu_reset_map sun50i_h5_de2_resets[] = {
  150. [RST_MIXER0] = { 0x08, BIT(0) },
  151. [RST_MIXER1] = { 0x08, BIT(1) },
  152. [RST_WB] = { 0x08, BIT(2) },
  153. };
  154. static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = {
  155. .ccu_clks = sun8i_de2_ccu_clks,
  156. .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks),
  157. .hw_clks = &sun8i_a83t_de2_hw_clks,
  158. .resets = sun8i_a83t_de2_resets,
  159. .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets),
  160. };
  161. static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc = {
  162. .ccu_clks = sun8i_de2_ccu_clks,
  163. .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks),
  164. .hw_clks = &sun8i_h3_de2_hw_clks,
  165. .resets = sun8i_h3_de2_resets,
  166. .num_resets = ARRAY_SIZE(sun8i_h3_de2_resets),
  167. };
  168. static const struct sunxi_ccu_desc sun8i_r40_de2_clk_desc = {
  169. .ccu_clks = sun8i_de2_ccu_clks,
  170. .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks),
  171. .hw_clks = &sun50i_a64_de2_hw_clks,
  172. .resets = sun8i_a83t_de2_resets,
  173. .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets),
  174. };
  175. static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = {
  176. .ccu_clks = sun8i_de2_ccu_clks,
  177. .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks),
  178. .hw_clks = &sun8i_v3s_de2_hw_clks,
  179. .resets = sun8i_a83t_de2_resets,
  180. .num_resets = ARRAY_SIZE(sun8i_a83t_de2_resets),
  181. };
  182. static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = {
  183. .ccu_clks = sun8i_de2_ccu_clks,
  184. .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks),
  185. .hw_clks = &sun50i_a64_de2_hw_clks,
  186. .resets = sun50i_a64_de2_resets,
  187. .num_resets = ARRAY_SIZE(sun50i_a64_de2_resets),
  188. };
  189. static const struct sunxi_ccu_desc sun50i_h5_de2_clk_desc = {
  190. .ccu_clks = sun8i_de2_ccu_clks,
  191. .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks),
  192. .hw_clks = &sun8i_h3_de2_hw_clks,
  193. .resets = sun50i_h5_de2_resets,
  194. .num_resets = ARRAY_SIZE(sun50i_h5_de2_resets),
  195. };
  196. static int sunxi_de2_clk_probe(struct platform_device *pdev)
  197. {
  198. struct clk *bus_clk, *mod_clk;
  199. struct reset_control *rstc;
  200. void __iomem *reg;
  201. const struct sunxi_ccu_desc *ccu_desc;
  202. int ret;
  203. ccu_desc = of_device_get_match_data(&pdev->dev);
  204. if (!ccu_desc)
  205. return -EINVAL;
  206. reg = devm_platform_ioremap_resource(pdev, 0);
  207. if (IS_ERR(reg))
  208. return PTR_ERR(reg);
  209. bus_clk = devm_clk_get(&pdev->dev, "bus");
  210. if (IS_ERR(bus_clk))
  211. return dev_err_probe(&pdev->dev, PTR_ERR(bus_clk),
  212. "Couldn't get bus clk\n");
  213. mod_clk = devm_clk_get(&pdev->dev, "mod");
  214. if (IS_ERR(mod_clk))
  215. return dev_err_probe(&pdev->dev, PTR_ERR(mod_clk),
  216. "Couldn't get mod clk\n");
  217. rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
  218. if (IS_ERR(rstc))
  219. return dev_err_probe(&pdev->dev, PTR_ERR(rstc),
  220. "Couldn't get reset control\n");
  221. /* The clocks need to be enabled for us to access the registers */
  222. ret = clk_prepare_enable(bus_clk);
  223. if (ret) {
  224. dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n", ret);
  225. return ret;
  226. }
  227. ret = clk_prepare_enable(mod_clk);
  228. if (ret) {
  229. dev_err(&pdev->dev, "Couldn't enable mod clk: %d\n", ret);
  230. goto err_disable_bus_clk;
  231. }
  232. /* The reset control needs to be asserted for the controls to work */
  233. ret = reset_control_deassert(rstc);
  234. if (ret) {
  235. dev_err(&pdev->dev,
  236. "Couldn't deassert reset control: %d\n", ret);
  237. goto err_disable_mod_clk;
  238. }
  239. ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc);
  240. if (ret)
  241. goto err_assert_reset;
  242. return 0;
  243. err_assert_reset:
  244. reset_control_assert(rstc);
  245. err_disable_mod_clk:
  246. clk_disable_unprepare(mod_clk);
  247. err_disable_bus_clk:
  248. clk_disable_unprepare(bus_clk);
  249. return ret;
  250. }
  251. static const struct of_device_id sunxi_de2_clk_ids[] = {
  252. {
  253. .compatible = "allwinner,sun8i-a83t-de2-clk",
  254. .data = &sun8i_a83t_de2_clk_desc,
  255. },
  256. {
  257. .compatible = "allwinner,sun8i-h3-de2-clk",
  258. .data = &sun8i_h3_de2_clk_desc,
  259. },
  260. {
  261. .compatible = "allwinner,sun8i-r40-de2-clk",
  262. .data = &sun8i_r40_de2_clk_desc,
  263. },
  264. {
  265. .compatible = "allwinner,sun8i-v3s-de2-clk",
  266. .data = &sun8i_v3s_de2_clk_desc,
  267. },
  268. {
  269. .compatible = "allwinner,sun50i-a64-de2-clk",
  270. .data = &sun50i_a64_de2_clk_desc,
  271. },
  272. {
  273. .compatible = "allwinner,sun50i-h5-de2-clk",
  274. .data = &sun50i_h5_de2_clk_desc,
  275. },
  276. {
  277. .compatible = "allwinner,sun50i-h6-de3-clk",
  278. .data = &sun50i_h5_de2_clk_desc,
  279. },
  280. { }
  281. };
  282. static struct platform_driver sunxi_de2_clk_driver = {
  283. .probe = sunxi_de2_clk_probe,
  284. .driver = {
  285. .name = "sunxi-de2-clks",
  286. .of_match_table = sunxi_de2_clk_ids,
  287. },
  288. };
  289. module_platform_driver(sunxi_de2_clk_driver);
  290. MODULE_IMPORT_NS(SUNXI_CCU);
  291. MODULE_LICENSE("GPL");