lpass-gfm-sm8250.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * LPASS Audio CC and Always ON CC Glitch Free Mux clock driver
  4. *
  5. * Copyright (c) 2020 Linaro Ltd.
  6. * Author: Srinivas Kandagatla <[email protected]>
  7. */
  8. #include <linux/kernel.h>
  9. #include <linux/module.h>
  10. #include <linux/clk-provider.h>
  11. #include <linux/io.h>
  12. #include <linux/slab.h>
  13. #include <linux/err.h>
  14. #include <linux/pm_clock.h>
  15. #include <linux/pm_runtime.h>
  16. #include <linux/device.h>
  17. #include <linux/platform_device.h>
  18. #include <linux/of_device.h>
  19. #include <dt-bindings/clock/qcom,sm8250-lpass-audiocc.h>
  20. #include <dt-bindings/clock/qcom,sm8250-lpass-aoncc.h>
  21. struct lpass_gfm {
  22. struct device *dev;
  23. void __iomem *base;
  24. };
  25. struct clk_gfm {
  26. unsigned int mux_reg;
  27. unsigned int mux_mask;
  28. struct clk_hw hw;
  29. struct lpass_gfm *priv;
  30. void __iomem *gfm_mux;
  31. };
  32. #define to_clk_gfm(_hw) container_of(_hw, struct clk_gfm, hw)
  33. static u8 clk_gfm_get_parent(struct clk_hw *hw)
  34. {
  35. struct clk_gfm *clk = to_clk_gfm(hw);
  36. return readl(clk->gfm_mux) & clk->mux_mask;
  37. }
  38. static int clk_gfm_set_parent(struct clk_hw *hw, u8 index)
  39. {
  40. struct clk_gfm *clk = to_clk_gfm(hw);
  41. unsigned int val;
  42. val = readl(clk->gfm_mux);
  43. if (index)
  44. val |= clk->mux_mask;
  45. else
  46. val &= ~clk->mux_mask;
  47. writel(val, clk->gfm_mux);
  48. return 0;
  49. }
  50. static const struct clk_ops clk_gfm_ops = {
  51. .get_parent = clk_gfm_get_parent,
  52. .set_parent = clk_gfm_set_parent,
  53. .determine_rate = __clk_mux_determine_rate,
  54. };
  55. static struct clk_gfm lpass_gfm_va_mclk = {
  56. .mux_reg = 0x20000,
  57. .mux_mask = BIT(0),
  58. .hw.init = &(struct clk_init_data) {
  59. .name = "VA_MCLK",
  60. .ops = &clk_gfm_ops,
  61. .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
  62. .num_parents = 2,
  63. .parent_data = (const struct clk_parent_data[]){
  64. {
  65. .index = 0,
  66. .fw_name = "LPASS_CLK_ID_TX_CORE_MCLK",
  67. }, {
  68. .index = 1,
  69. .fw_name = "LPASS_CLK_ID_VA_CORE_MCLK",
  70. },
  71. },
  72. },
  73. };
  74. static struct clk_gfm lpass_gfm_tx_npl = {
  75. .mux_reg = 0x20000,
  76. .mux_mask = BIT(0),
  77. .hw.init = &(struct clk_init_data) {
  78. .name = "TX_NPL",
  79. .ops = &clk_gfm_ops,
  80. .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
  81. .parent_data = (const struct clk_parent_data[]){
  82. {
  83. .index = 0,
  84. .fw_name = "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
  85. }, {
  86. .index = 1,
  87. .fw_name = "LPASS_CLK_ID_VA_CORE_2X_MCLK",
  88. },
  89. },
  90. .num_parents = 2,
  91. },
  92. };
  93. static struct clk_gfm lpass_gfm_wsa_mclk = {
  94. .mux_reg = 0x220d8,
  95. .mux_mask = BIT(0),
  96. .hw.init = &(struct clk_init_data) {
  97. .name = "WSA_MCLK",
  98. .ops = &clk_gfm_ops,
  99. .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
  100. .parent_data = (const struct clk_parent_data[]){
  101. {
  102. .index = 0,
  103. .fw_name = "LPASS_CLK_ID_TX_CORE_MCLK",
  104. }, {
  105. .index = 1,
  106. .fw_name = "LPASS_CLK_ID_WSA_CORE_MCLK",
  107. },
  108. },
  109. .num_parents = 2,
  110. },
  111. };
  112. static struct clk_gfm lpass_gfm_wsa_npl = {
  113. .mux_reg = 0x220d8,
  114. .mux_mask = BIT(0),
  115. .hw.init = &(struct clk_init_data) {
  116. .name = "WSA_NPL",
  117. .ops = &clk_gfm_ops,
  118. .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
  119. .parent_data = (const struct clk_parent_data[]){
  120. {
  121. .index = 0,
  122. .fw_name = "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
  123. }, {
  124. .index = 1,
  125. .fw_name = "LPASS_CLK_ID_WSA_CORE_NPL_MCLK",
  126. },
  127. },
  128. .num_parents = 2,
  129. },
  130. };
  131. static struct clk_gfm lpass_gfm_rx_mclk_mclk2 = {
  132. .mux_reg = 0x240d8,
  133. .mux_mask = BIT(0),
  134. .hw.init = &(struct clk_init_data) {
  135. .name = "RX_MCLK_MCLK2",
  136. .ops = &clk_gfm_ops,
  137. .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
  138. .parent_data = (const struct clk_parent_data[]){
  139. {
  140. .index = 0,
  141. .fw_name = "LPASS_CLK_ID_TX_CORE_MCLK",
  142. }, {
  143. .index = 1,
  144. .fw_name = "LPASS_CLK_ID_RX_CORE_MCLK",
  145. },
  146. },
  147. .num_parents = 2,
  148. },
  149. };
  150. static struct clk_gfm lpass_gfm_rx_npl = {
  151. .mux_reg = 0x240d8,
  152. .mux_mask = BIT(0),
  153. .hw.init = &(struct clk_init_data) {
  154. .name = "RX_NPL",
  155. .ops = &clk_gfm_ops,
  156. .flags = CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE,
  157. .parent_data = (const struct clk_parent_data[]){
  158. {
  159. .index = 0,
  160. .fw_name = "LPASS_CLK_ID_TX_CORE_NPL_MCLK",
  161. }, {
  162. .index = 1,
  163. .fw_name = "LPASS_CLK_ID_RX_CORE_NPL_MCLK",
  164. },
  165. },
  166. .num_parents = 2,
  167. },
  168. };
  169. static struct clk_gfm *aoncc_gfm_clks[] = {
  170. [LPASS_CDC_VA_MCLK] = &lpass_gfm_va_mclk,
  171. [LPASS_CDC_TX_NPL] = &lpass_gfm_tx_npl,
  172. };
  173. static struct clk_hw_onecell_data aoncc_hw_onecell_data = {
  174. .hws = {
  175. [LPASS_CDC_VA_MCLK] = &lpass_gfm_va_mclk.hw,
  176. [LPASS_CDC_TX_NPL] = &lpass_gfm_tx_npl.hw,
  177. },
  178. .num = ARRAY_SIZE(aoncc_gfm_clks),
  179. };
  180. static struct clk_gfm *audiocc_gfm_clks[] = {
  181. [LPASS_CDC_WSA_NPL] = &lpass_gfm_wsa_npl,
  182. [LPASS_CDC_WSA_MCLK] = &lpass_gfm_wsa_mclk,
  183. [LPASS_CDC_RX_NPL] = &lpass_gfm_rx_npl,
  184. [LPASS_CDC_RX_MCLK_MCLK2] = &lpass_gfm_rx_mclk_mclk2,
  185. };
  186. static struct clk_hw_onecell_data audiocc_hw_onecell_data = {
  187. .hws = {
  188. [LPASS_CDC_WSA_NPL] = &lpass_gfm_wsa_npl.hw,
  189. [LPASS_CDC_WSA_MCLK] = &lpass_gfm_wsa_mclk.hw,
  190. [LPASS_CDC_RX_NPL] = &lpass_gfm_rx_npl.hw,
  191. [LPASS_CDC_RX_MCLK_MCLK2] = &lpass_gfm_rx_mclk_mclk2.hw,
  192. },
  193. .num = ARRAY_SIZE(audiocc_gfm_clks),
  194. };
  195. struct lpass_gfm_data {
  196. struct clk_hw_onecell_data *onecell_data;
  197. struct clk_gfm **gfm_clks;
  198. };
  199. static struct lpass_gfm_data audiocc_data = {
  200. .onecell_data = &audiocc_hw_onecell_data,
  201. .gfm_clks = audiocc_gfm_clks,
  202. };
  203. static struct lpass_gfm_data aoncc_data = {
  204. .onecell_data = &aoncc_hw_onecell_data,
  205. .gfm_clks = aoncc_gfm_clks,
  206. };
  207. static int lpass_gfm_clk_driver_probe(struct platform_device *pdev)
  208. {
  209. const struct lpass_gfm_data *data;
  210. struct device *dev = &pdev->dev;
  211. struct clk_gfm *gfm;
  212. struct lpass_gfm *cc;
  213. int err, i;
  214. data = of_device_get_match_data(dev);
  215. if (!data)
  216. return -EINVAL;
  217. cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL);
  218. if (!cc)
  219. return -ENOMEM;
  220. cc->base = devm_platform_ioremap_resource(pdev, 0);
  221. if (IS_ERR(cc->base))
  222. return PTR_ERR(cc->base);
  223. err = devm_pm_runtime_enable(dev);
  224. if (err)
  225. return err;
  226. err = devm_pm_clk_create(dev);
  227. if (err)
  228. return err;
  229. err = of_pm_clk_add_clks(dev);
  230. if (err < 0) {
  231. dev_dbg(dev, "Failed to get lpass core voting clocks\n");
  232. return err;
  233. }
  234. for (i = 0; i < data->onecell_data->num; i++) {
  235. if (!data->gfm_clks[i])
  236. continue;
  237. gfm = data->gfm_clks[i];
  238. gfm->priv = cc;
  239. gfm->gfm_mux = cc->base;
  240. gfm->gfm_mux = gfm->gfm_mux + data->gfm_clks[i]->mux_reg;
  241. err = devm_clk_hw_register(dev, &data->gfm_clks[i]->hw);
  242. if (err)
  243. return err;
  244. }
  245. err = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
  246. data->onecell_data);
  247. if (err)
  248. return err;
  249. return 0;
  250. }
  251. static const struct of_device_id lpass_gfm_clk_match_table[] = {
  252. {
  253. .compatible = "qcom,sm8250-lpass-aoncc",
  254. .data = &aoncc_data,
  255. },
  256. {
  257. .compatible = "qcom,sm8250-lpass-audiocc",
  258. .data = &audiocc_data,
  259. },
  260. { }
  261. };
  262. MODULE_DEVICE_TABLE(of, lpass_gfm_clk_match_table);
  263. static const struct dev_pm_ops lpass_gfm_pm_ops = {
  264. SET_RUNTIME_PM_OPS(pm_clk_suspend, pm_clk_resume, NULL)
  265. };
  266. static struct platform_driver lpass_gfm_clk_driver = {
  267. .probe = lpass_gfm_clk_driver_probe,
  268. .driver = {
  269. .name = "lpass-gfm-clk",
  270. .of_match_table = lpass_gfm_clk_match_table,
  271. .pm = &lpass_gfm_pm_ops,
  272. },
  273. };
  274. module_platform_driver(lpass_gfm_clk_driver);
  275. MODULE_LICENSE("GPL v2");