clk-imx8-acm.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. // SPDX-License-Identifier: GPL-2.0+
  2. //
  3. // Copyright 2023 NXP
  4. //
  5. #include <dt-bindings/clock/imx8-clock.h>
  6. #include <linux/clk-provider.h>
  7. #include <linux/device.h>
  8. #include <linux/err.h>
  9. #include <linux/io.h>
  10. #include <linux/module.h>
  11. #include <linux/of.h>
  12. #include <linux/of_device.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/pm_domain.h>
  15. #include <linux/pm_runtime.h>
  16. #include <linux/slab.h>
  17. #include "clk.h"
  18. /**
  19. * struct clk_imx_acm_pm_domains - structure for multi power domain
  20. * @pd_dev: power domain device
  21. * @pd_dev_link: power domain device link
  22. * @num_domains: power domain nummber
  23. */
  24. struct clk_imx_acm_pm_domains {
  25. struct device **pd_dev;
  26. struct device_link **pd_dev_link;
  27. int num_domains;
  28. };
  29. /**
  30. * struct clk_imx8_acm_sel - for clock mux
  31. * @name: clock name
  32. * @clkid: clock id
  33. * @parents: clock parents
  34. * @num_parents: clock parents number
  35. * @reg: register offset
  36. * @shift: bit shift in register
  37. * @width: bits width
  38. */
  39. struct clk_imx8_acm_sel {
  40. const char *name;
  41. int clkid;
  42. const struct clk_parent_data *parents; /* For mux */
  43. int num_parents;
  44. u32 reg;
  45. u8 shift;
  46. u8 width;
  47. };
  48. /**
  49. * struct imx8_acm_soc_data - soc specific data
  50. * @sels: pointer to struct clk_imx8_acm_sel
  51. * @num_sels: numbers of items
  52. * @mclk_sels: pointer to imx8qm/qxp/dxl_mclk_sels
  53. */
  54. struct imx8_acm_soc_data {
  55. struct clk_imx8_acm_sel *sels;
  56. unsigned int num_sels;
  57. struct clk_parent_data *mclk_sels;
  58. };
  59. /**
  60. * struct imx8_acm_priv - private structure
  61. * @dev_pm: multi power domain
  62. * @soc_data: pointer to soc data
  63. * @reg: base address of registers
  64. * @regs: save registers for suspend
  65. */
  66. struct imx8_acm_priv {
  67. struct clk_imx_acm_pm_domains dev_pm;
  68. const struct imx8_acm_soc_data *soc_data;
  69. void __iomem *reg;
  70. u32 regs[IMX_ADMA_ACM_CLK_END];
  71. };
  72. static const struct clk_parent_data imx8qm_aud_clk_sels[] = {
  73. { .fw_name = "aud_rec_clk0_lpcg_clk" },
  74. { .fw_name = "aud_rec_clk1_lpcg_clk" },
  75. { .fw_name = "dummy" },
  76. { .fw_name = "hdmi_rx_mclk" },
  77. { .fw_name = "ext_aud_mclk0" },
  78. { .fw_name = "ext_aud_mclk1" },
  79. { .fw_name = "esai0_rx_clk" },
  80. { .fw_name = "esai0_rx_hf_clk" },
  81. { .fw_name = "esai0_tx_clk" },
  82. { .fw_name = "esai0_tx_hf_clk" },
  83. { .fw_name = "esai1_rx_clk" },
  84. { .fw_name = "esai1_rx_hf_clk" },
  85. { .fw_name = "esai1_tx_clk" },
  86. { .fw_name = "esai1_tx_hf_clk" },
  87. { .fw_name = "spdif0_rx" },
  88. { .fw_name = "spdif1_rx" },
  89. { .fw_name = "sai0_rx_bclk" },
  90. { .fw_name = "sai0_tx_bclk" },
  91. { .fw_name = "sai1_rx_bclk" },
  92. { .fw_name = "sai1_tx_bclk" },
  93. { .fw_name = "sai2_rx_bclk" },
  94. { .fw_name = "sai3_rx_bclk" },
  95. { .fw_name = "sai4_rx_bclk" },
  96. };
  97. static const struct clk_parent_data imx8qm_mclk_out_sels[] = {
  98. { .fw_name = "aud_rec_clk0_lpcg_clk" },
  99. { .fw_name = "aud_rec_clk1_lpcg_clk" },
  100. { .fw_name = "dummy" },
  101. { .fw_name = "hdmi_rx_mclk" },
  102. { .fw_name = "spdif0_rx" },
  103. { .fw_name = "spdif1_rx" },
  104. { .fw_name = "sai4_rx_bclk" },
  105. { .fw_name = "sai6_rx_bclk" },
  106. };
  107. #define ACM_AUD_CLK0_SEL_INDEX 2
  108. #define ACM_AUD_CLK1_SEL_INDEX 3
  109. static struct clk_parent_data imx8qm_mclk_sels[] = {
  110. { .fw_name = "aud_pll_div_clk0_lpcg_clk" },
  111. { .fw_name = "aud_pll_div_clk1_lpcg_clk" },
  112. { }, /* clk_hw pointer of "acm_aud_clk0_sel" */
  113. { }, /* clk_hw pointer of "acm_aud_clk1_sel" */
  114. };
  115. static const struct clk_parent_data imx8qm_asrc_mux_clk_sels[] = {
  116. { .fw_name = "sai4_rx_bclk" },
  117. { .fw_name = "sai5_tx_bclk" },
  118. { .index = -1 },
  119. { .fw_name = "dummy" },
  120. };
  121. static struct clk_imx8_acm_sel imx8qm_sels[] = {
  122. { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8qm_aud_clk_sels, ARRAY_SIZE(imx8qm_aud_clk_sels), 0x000000, 0, 5 },
  123. { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8qm_aud_clk_sels, ARRAY_SIZE(imx8qm_aud_clk_sels), 0x010000, 0, 5 },
  124. { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8qm_mclk_out_sels, ARRAY_SIZE(imx8qm_mclk_out_sels), 0x020000, 0, 3 },
  125. { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8qm_mclk_out_sels, ARRAY_SIZE(imx8qm_mclk_out_sels), 0x030000, 0, 3 },
  126. { "acm_asrc0_mclk_sel", IMX_ADMA_ACM_ASRC0_MUX_CLK_SEL, imx8qm_asrc_mux_clk_sels, ARRAY_SIZE(imx8qm_asrc_mux_clk_sels), 0x040000, 0, 2 },
  127. { "acm_esai0_mclk_sel", IMX_ADMA_ACM_ESAI0_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x060000, 0, 2 },
  128. { "acm_esai1_mclk_sel", IMX_ADMA_ACM_ESAI1_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x070000, 0, 2 },
  129. { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x0E0000, 0, 2 },
  130. { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x0F0000, 0, 2 },
  131. { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x100000, 0, 2 },
  132. { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x110000, 0, 2 },
  133. { "acm_sai4_mclk_sel", IMX_ADMA_ACM_SAI4_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x120000, 0, 2 },
  134. { "acm_sai5_mclk_sel", IMX_ADMA_ACM_SAI5_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x130000, 0, 2 },
  135. { "acm_sai6_mclk_sel", IMX_ADMA_ACM_SAI6_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x140000, 0, 2 },
  136. { "acm_sai7_mclk_sel", IMX_ADMA_ACM_SAI7_MCLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x150000, 0, 2 },
  137. { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1A0000, 0, 2 },
  138. { "acm_spdif1_mclk_sel", IMX_ADMA_ACM_SPDIF1_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1B0000, 0, 2 },
  139. { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8qm_mclk_sels, ARRAY_SIZE(imx8qm_mclk_sels), 0x1C0000, 0, 2 },
  140. };
  141. static const struct clk_parent_data imx8qxp_aud_clk_sels[] = {
  142. { .fw_name = "aud_rec_clk0_lpcg_clk" },
  143. { .fw_name = "aud_rec_clk1_lpcg_clk" },
  144. { .fw_name = "ext_aud_mclk0" },
  145. { .fw_name = "ext_aud_mclk1" },
  146. { .fw_name = "esai0_rx_clk" },
  147. { .fw_name = "esai0_rx_hf_clk" },
  148. { .fw_name = "esai0_tx_clk" },
  149. { .fw_name = "esai0_tx_hf_clk" },
  150. { .fw_name = "spdif0_rx" },
  151. { .fw_name = "sai0_rx_bclk" },
  152. { .fw_name = "sai0_tx_bclk" },
  153. { .fw_name = "sai1_rx_bclk" },
  154. { .fw_name = "sai1_tx_bclk" },
  155. { .fw_name = "sai2_rx_bclk" },
  156. { .fw_name = "sai3_rx_bclk" },
  157. };
  158. static const struct clk_parent_data imx8qxp_mclk_out_sels[] = {
  159. { .fw_name = "aud_rec_clk0_lpcg_clk" },
  160. { .fw_name = "aud_rec_clk1_lpcg_clk" },
  161. { .index = -1 },
  162. { .index = -1 },
  163. { .fw_name = "spdif0_rx" },
  164. { .index = -1 },
  165. { .index = -1 },
  166. { .fw_name = "sai4_rx_bclk" },
  167. };
  168. static struct clk_parent_data imx8qxp_mclk_sels[] = {
  169. { .fw_name = "aud_pll_div_clk0_lpcg_clk" },
  170. { .fw_name = "aud_pll_div_clk1_lpcg_clk" },
  171. { }, /* clk_hw pointer of "acm_aud_clk0_sel" */
  172. { }, /* clk_hw pointer of "acm_aud_clk1_sel" */
  173. };
  174. static struct clk_imx8_acm_sel imx8qxp_sels[] = {
  175. { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8qxp_aud_clk_sels, ARRAY_SIZE(imx8qxp_aud_clk_sels), 0x000000, 0, 5 },
  176. { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8qxp_aud_clk_sels, ARRAY_SIZE(imx8qxp_aud_clk_sels), 0x010000, 0, 5 },
  177. { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8qxp_mclk_out_sels, ARRAY_SIZE(imx8qxp_mclk_out_sels), 0x020000, 0, 3 },
  178. { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8qxp_mclk_out_sels, ARRAY_SIZE(imx8qxp_mclk_out_sels), 0x030000, 0, 3 },
  179. { "acm_esai0_mclk_sel", IMX_ADMA_ACM_ESAI0_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x060000, 0, 2 },
  180. { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x0E0000, 0, 2 },
  181. { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x0F0000, 0, 2 },
  182. { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x100000, 0, 2 },
  183. { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x110000, 0, 2 },
  184. { "acm_sai4_mclk_sel", IMX_ADMA_ACM_SAI4_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x140000, 0, 2 },
  185. { "acm_sai5_mclk_sel", IMX_ADMA_ACM_SAI5_MCLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x150000, 0, 2 },
  186. { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x1A0000, 0, 2 },
  187. { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8qxp_mclk_sels, ARRAY_SIZE(imx8qxp_mclk_sels), 0x1C0000, 0, 2 },
  188. };
  189. static const struct clk_parent_data imx8dxl_aud_clk_sels[] = {
  190. { .fw_name = "aud_rec_clk0_lpcg_clk" },
  191. { .fw_name = "aud_rec_clk1_lpcg_clk" },
  192. { .fw_name = "ext_aud_mclk0" },
  193. { .fw_name = "ext_aud_mclk1" },
  194. { .index = -1 },
  195. { .index = -1 },
  196. { .index = -1 },
  197. { .index = -1 },
  198. { .fw_name = "spdif0_rx" },
  199. { .fw_name = "sai0_rx_bclk" },
  200. { .fw_name = "sai0_tx_bclk" },
  201. { .fw_name = "sai1_rx_bclk" },
  202. { .fw_name = "sai1_tx_bclk" },
  203. { .fw_name = "sai2_rx_bclk" },
  204. { .fw_name = "sai3_rx_bclk" },
  205. };
  206. static const struct clk_parent_data imx8dxl_mclk_out_sels[] = {
  207. { .fw_name = "aud_rec_clk0_lpcg_clk" },
  208. { .fw_name = "aud_rec_clk1_lpcg_clk" },
  209. { .index = -1 },
  210. { .index = -1 },
  211. { .fw_name = "spdif0_rx" },
  212. { .index = -1 },
  213. { .index = -1 },
  214. { .index = -1 },
  215. };
  216. static struct clk_parent_data imx8dxl_mclk_sels[] = {
  217. { .fw_name = "aud_pll_div_clk0_lpcg_clk" },
  218. { .fw_name = "aud_pll_div_clk1_lpcg_clk" },
  219. { }, /* clk_hw pointer of "acm_aud_clk0_sel" */
  220. { }, /* clk_hw pointer of "acm_aud_clk1_sel" */
  221. };
  222. static struct clk_imx8_acm_sel imx8dxl_sels[] = {
  223. { "acm_aud_clk0_sel", IMX_ADMA_ACM_AUD_CLK0_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x000000, 0, 5 },
  224. { "acm_aud_clk1_sel", IMX_ADMA_ACM_AUD_CLK1_SEL, imx8dxl_aud_clk_sels, ARRAY_SIZE(imx8dxl_aud_clk_sels), 0x010000, 0, 5 },
  225. { "acm_mclkout0_sel", IMX_ADMA_ACM_MCLKOUT0_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x020000, 0, 3 },
  226. { "acm_mclkout1_sel", IMX_ADMA_ACM_MCLKOUT1_SEL, imx8dxl_mclk_out_sels, ARRAY_SIZE(imx8dxl_mclk_out_sels), 0x030000, 0, 3 },
  227. { "acm_sai0_mclk_sel", IMX_ADMA_ACM_SAI0_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0E0000, 0, 2 },
  228. { "acm_sai1_mclk_sel", IMX_ADMA_ACM_SAI1_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x0F0000, 0, 2 },
  229. { "acm_sai2_mclk_sel", IMX_ADMA_ACM_SAI2_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x100000, 0, 2 },
  230. { "acm_sai3_mclk_sel", IMX_ADMA_ACM_SAI3_MCLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x110000, 0, 2 },
  231. { "acm_spdif0_mclk_sel", IMX_ADMA_ACM_SPDIF0_TX_CLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x1A0000, 0, 2 },
  232. { "acm_mqs_mclk_sel", IMX_ADMA_ACM_MQS_TX_CLK_SEL, imx8dxl_mclk_sels, ARRAY_SIZE(imx8dxl_mclk_sels), 0x1C0000, 0, 2 },
  233. };
  234. /**
  235. * clk_imx_acm_attach_pm_domains: attach multi power domains
  236. * @dev: device pointer
  237. * @dev_pm: power domains for device
  238. */
  239. static int clk_imx_acm_attach_pm_domains(struct device *dev,
  240. struct clk_imx_acm_pm_domains *dev_pm)
  241. {
  242. int ret;
  243. int i;
  244. dev_pm->num_domains = of_count_phandle_with_args(dev->of_node, "power-domains",
  245. "#power-domain-cells");
  246. if (dev_pm->num_domains <= 1)
  247. return 0;
  248. dev_pm->pd_dev = devm_kmalloc_array(dev, dev_pm->num_domains,
  249. sizeof(*dev_pm->pd_dev),
  250. GFP_KERNEL);
  251. if (!dev_pm->pd_dev)
  252. return -ENOMEM;
  253. dev_pm->pd_dev_link = devm_kmalloc_array(dev,
  254. dev_pm->num_domains,
  255. sizeof(*dev_pm->pd_dev_link),
  256. GFP_KERNEL);
  257. if (!dev_pm->pd_dev_link)
  258. return -ENOMEM;
  259. for (i = 0; i < dev_pm->num_domains; i++) {
  260. dev_pm->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
  261. if (IS_ERR(dev_pm->pd_dev[i])) {
  262. ret = PTR_ERR(dev_pm->pd_dev[i]);
  263. goto detach_pm;
  264. }
  265. dev_pm->pd_dev_link[i] = device_link_add(dev,
  266. dev_pm->pd_dev[i],
  267. DL_FLAG_STATELESS |
  268. DL_FLAG_PM_RUNTIME |
  269. DL_FLAG_RPM_ACTIVE);
  270. if (!dev_pm->pd_dev_link[i]) {
  271. dev_pm_domain_detach(dev_pm->pd_dev[i], false);
  272. ret = -EINVAL;
  273. goto detach_pm;
  274. }
  275. }
  276. return 0;
  277. detach_pm:
  278. while (--i >= 0) {
  279. device_link_del(dev_pm->pd_dev_link[i]);
  280. dev_pm_domain_detach(dev_pm->pd_dev[i], false);
  281. }
  282. return ret;
  283. }
  284. /**
  285. * clk_imx_acm_detach_pm_domains: detach multi power domains
  286. * @dev: deivice pointer
  287. * @dev_pm: multi power domain for device
  288. */
  289. static void clk_imx_acm_detach_pm_domains(struct device *dev,
  290. struct clk_imx_acm_pm_domains *dev_pm)
  291. {
  292. int i;
  293. if (dev_pm->num_domains <= 1)
  294. return;
  295. for (i = 0; i < dev_pm->num_domains; i++) {
  296. device_link_del(dev_pm->pd_dev_link[i]);
  297. dev_pm_domain_detach(dev_pm->pd_dev[i], false);
  298. }
  299. }
  300. static int imx8_acm_clk_probe(struct platform_device *pdev)
  301. {
  302. struct clk_hw_onecell_data *clk_hw_data;
  303. struct device *dev = &pdev->dev;
  304. struct clk_imx8_acm_sel *sels;
  305. struct imx8_acm_priv *priv;
  306. struct clk_hw **hws;
  307. void __iomem *base;
  308. int ret;
  309. int i;
  310. base = devm_of_iomap(dev, dev->of_node, 0, NULL);
  311. if (WARN_ON(IS_ERR(base)))
  312. return PTR_ERR(base);
  313. priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
  314. if (!priv)
  315. return -ENOMEM;
  316. priv->reg = base;
  317. priv->soc_data = of_device_get_match_data(dev);
  318. platform_set_drvdata(pdev, priv);
  319. clk_hw_data = devm_kzalloc(&pdev->dev, struct_size(clk_hw_data, hws, IMX_ADMA_ACM_CLK_END),
  320. GFP_KERNEL);
  321. if (!clk_hw_data)
  322. return -ENOMEM;
  323. clk_hw_data->num = IMX_ADMA_ACM_CLK_END;
  324. hws = clk_hw_data->hws;
  325. ret = clk_imx_acm_attach_pm_domains(&pdev->dev, &priv->dev_pm);
  326. if (ret)
  327. return ret;
  328. pm_runtime_enable(&pdev->dev);
  329. pm_runtime_get_sync(&pdev->dev);
  330. sels = priv->soc_data->sels;
  331. for (i = 0; i < priv->soc_data->num_sels; i++) {
  332. hws[sels[i].clkid] = devm_clk_hw_register_mux_parent_data_table(dev,
  333. sels[i].name, sels[i].parents,
  334. sels[i].num_parents, 0,
  335. base + sels[i].reg,
  336. sels[i].shift, sels[i].width,
  337. 0, NULL, NULL);
  338. if (IS_ERR(hws[sels[i].clkid])) {
  339. ret = PTR_ERR(hws[sels[i].clkid]);
  340. imx_check_clk_hws(hws, IMX_ADMA_ACM_CLK_END);
  341. goto err_clk_register;
  342. }
  343. /*
  344. * The IMX_ADMA_ACM_AUD_CLK0_SEL and IMX_ADMA_ACM_AUD_CLK1_SEL are
  345. * registered first. After registration, update the clk_hw pointer
  346. * to imx8qm/qxp/dxl_mclk_sels structures.
  347. */
  348. if (sels[i].clkid == IMX_ADMA_ACM_AUD_CLK0_SEL)
  349. priv->soc_data->mclk_sels[ACM_AUD_CLK0_SEL_INDEX].hw =
  350. hws[IMX_ADMA_ACM_AUD_CLK0_SEL];
  351. if (sels[i].clkid == IMX_ADMA_ACM_AUD_CLK1_SEL)
  352. priv->soc_data->mclk_sels[ACM_AUD_CLK1_SEL_INDEX].hw =
  353. hws[IMX_ADMA_ACM_AUD_CLK1_SEL];
  354. }
  355. ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_hw_data);
  356. if (ret < 0) {
  357. dev_err(dev, "failed to register hws for ACM\n");
  358. goto err_clk_register;
  359. }
  360. pm_runtime_put_sync(&pdev->dev);
  361. return 0;
  362. err_clk_register:
  363. pm_runtime_put_sync(&pdev->dev);
  364. pm_runtime_disable(&pdev->dev);
  365. clk_imx_acm_detach_pm_domains(&pdev->dev, &priv->dev_pm);
  366. return ret;
  367. }
  368. static void imx8_acm_clk_remove(struct platform_device *pdev)
  369. {
  370. struct imx8_acm_priv *priv = dev_get_drvdata(&pdev->dev);
  371. pm_runtime_disable(&pdev->dev);
  372. clk_imx_acm_detach_pm_domains(&pdev->dev, &priv->dev_pm);
  373. }
  374. static const struct imx8_acm_soc_data imx8qm_acm_data = {
  375. .sels = imx8qm_sels,
  376. .num_sels = ARRAY_SIZE(imx8qm_sels),
  377. .mclk_sels = imx8qm_mclk_sels,
  378. };
  379. static const struct imx8_acm_soc_data imx8qxp_acm_data = {
  380. .sels = imx8qxp_sels,
  381. .num_sels = ARRAY_SIZE(imx8qxp_sels),
  382. .mclk_sels = imx8qxp_mclk_sels,
  383. };
  384. static const struct imx8_acm_soc_data imx8dxl_acm_data = {
  385. .sels = imx8dxl_sels,
  386. .num_sels = ARRAY_SIZE(imx8dxl_sels),
  387. .mclk_sels = imx8dxl_mclk_sels,
  388. };
  389. static const struct of_device_id imx8_acm_match[] = {
  390. { .compatible = "fsl,imx8qm-acm", .data = &imx8qm_acm_data },
  391. { .compatible = "fsl,imx8qxp-acm", .data = &imx8qxp_acm_data },
  392. { .compatible = "fsl,imx8dxl-acm", .data = &imx8dxl_acm_data },
  393. { /* sentinel */ }
  394. };
  395. MODULE_DEVICE_TABLE(of, imx8_acm_match);
  396. static int __maybe_unused imx8_acm_runtime_suspend(struct device *dev)
  397. {
  398. struct imx8_acm_priv *priv = dev_get_drvdata(dev);
  399. struct clk_imx8_acm_sel *sels;
  400. int i;
  401. sels = priv->soc_data->sels;
  402. for (i = 0; i < priv->soc_data->num_sels; i++)
  403. priv->regs[i] = readl_relaxed(priv->reg + sels[i].reg);
  404. return 0;
  405. }
  406. static int __maybe_unused imx8_acm_runtime_resume(struct device *dev)
  407. {
  408. struct imx8_acm_priv *priv = dev_get_drvdata(dev);
  409. struct clk_imx8_acm_sel *sels;
  410. int i;
  411. sels = priv->soc_data->sels;
  412. for (i = 0; i < priv->soc_data->num_sels; i++)
  413. writel_relaxed(priv->regs[i], priv->reg + sels[i].reg);
  414. return 0;
  415. }
  416. static const struct dev_pm_ops imx8_acm_pm_ops = {
  417. SET_RUNTIME_PM_OPS(imx8_acm_runtime_suspend,
  418. imx8_acm_runtime_resume, NULL)
  419. SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
  420. pm_runtime_force_resume)
  421. };
  422. static struct platform_driver imx8_acm_clk_driver = {
  423. .driver = {
  424. .name = "imx8-acm",
  425. .of_match_table = imx8_acm_match,
  426. .pm = &imx8_acm_pm_ops,
  427. },
  428. .probe = imx8_acm_clk_probe,
  429. .remove = imx8_acm_clk_remove,
  430. };
  431. module_platform_driver(imx8_acm_clk_driver);
  432. MODULE_AUTHOR("Shengjiu Wang <shengjiu.wang@nxp.com>");
  433. MODULE_DESCRIPTION("Freescale i.MX8 Audio Clock Mux driver");
  434. MODULE_LICENSE("GPL");