scu-pd.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2016 Freescale Semiconductor, Inc.
  4. * Copyright 2017-2018 NXP
  5. * Dong Aisheng <[email protected]>
  6. *
  7. * Implementation of the SCU based Power Domains
  8. *
  9. * NOTE: a better implementation suggested by Ulf Hansson is using a
  10. * single global power domain and implement the ->attach|detach_dev()
  11. * callback for the genpd and use the regular of_genpd_add_provider_simple().
  12. * From within the ->attach_dev(), we could get the OF node for
  13. * the device that is being attached and then parse the power-domain
  14. * cell containing the "resource id" and store that in the per device
  15. * struct generic_pm_domain_data (we have void pointer there for
  16. * storing these kind of things).
  17. *
  18. * Additionally, we need to implement the ->stop() and ->start()
  19. * callbacks of genpd, which is where you "power on/off" devices,
  20. * rather than using the above ->power_on|off() callbacks.
  21. *
  22. * However, there're two known issues:
  23. * 1. The ->attach_dev() of power domain infrastructure still does
  24. * not support multi domains case as the struct device *dev passed
  25. * in is a virtual PD device, it does not help for parsing the real
  26. * device resource id from device tree, so it's unware of which
  27. * real sub power domain of device should be attached.
  28. *
  29. * The framework needs some proper extension to support multi power
  30. * domain cases.
  31. *
  32. * Update: Genpd assigns the ->of_node for the virtual device before it
  33. * invokes ->attach_dev() callback, hence parsing for device resources via
  34. * DT should work fine.
  35. *
  36. * 2. It also breaks most of current drivers as the driver probe sequence
  37. * behavior changed if removing ->power_on|off() callback and use
  38. * ->start() and ->stop() instead. genpd_dev_pm_attach will only power
  39. * up the domain and attach device, but will not call .start() which
  40. * relies on device runtime pm. That means the device power is still
  41. * not up before running driver probe function. For SCU enabled
  42. * platforms, all device drivers accessing registers/clock without power
  43. * domain enabled will trigger a HW access error. That means we need fix
  44. * most drivers probe sequence with proper runtime pm.
  45. *
  46. * Update: Runtime PM support isn't necessary. Instead, this can easily be
  47. * fixed in drivers by adding a call to dev_pm_domain_start() during probe.
  48. *
  49. * In summary, the second part needs to be addressed via minor updates to the
  50. * relevant drivers, before the "single global power domain" model can be used.
  51. *
  52. */
  53. #include <dt-bindings/firmware/imx/rsrc.h>
  54. #include <linux/firmware/imx/sci.h>
  55. #include <linux/firmware/imx/svc/rm.h>
  56. #include <linux/io.h>
  57. #include <linux/module.h>
  58. #include <linux/of.h>
  59. #include <linux/of_address.h>
  60. #include <linux/of_platform.h>
  61. #include <linux/platform_device.h>
  62. #include <linux/pm.h>
  63. #include <linux/pm_domain.h>
  64. #include <linux/slab.h>
  65. /* SCU Power Mode Protocol definition */
  66. struct imx_sc_msg_req_set_resource_power_mode {
  67. struct imx_sc_rpc_msg hdr;
  68. u16 resource;
  69. u8 mode;
  70. } __packed __aligned(4);
  71. #define IMX_SCU_PD_NAME_SIZE 20
  72. struct imx_sc_pm_domain {
  73. struct generic_pm_domain pd;
  74. char name[IMX_SCU_PD_NAME_SIZE];
  75. u32 rsrc;
  76. };
  77. struct imx_sc_pd_range {
  78. char *name;
  79. u32 rsrc;
  80. u8 num;
  81. /* add domain index */
  82. bool postfix;
  83. u8 start_from;
  84. };
  85. struct imx_sc_pd_soc {
  86. const struct imx_sc_pd_range *pd_ranges;
  87. u8 num_ranges;
  88. };
  89. static int imx_con_rsrc;
  90. static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
  91. /* LSIO SS */
  92. { "pwm", IMX_SC_R_PWM_0, 8, true, 0 },
  93. { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 },
  94. { "gpt", IMX_SC_R_GPT_0, 5, true, 0 },
  95. { "kpp", IMX_SC_R_KPP, 1, false, 0 },
  96. { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 },
  97. { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 },
  98. { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 },
  99. /* CONN SS */
  100. { "usb", IMX_SC_R_USB_0, 2, true, 0 },
  101. { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 },
  102. { "usb2", IMX_SC_R_USB_2, 1, false, 0 },
  103. { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 },
  104. { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 },
  105. { "enet", IMX_SC_R_ENET_0, 2, true, 0 },
  106. { "nand", IMX_SC_R_NAND, 1, false, 0 },
  107. { "mlb", IMX_SC_R_MLB_0, 1, true, 0 },
  108. /* AUDIO SS */
  109. { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 },
  110. { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 },
  111. { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 },
  112. { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 },
  113. { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 },
  114. { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 },
  115. { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 },
  116. { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 },
  117. { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 },
  118. { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 },
  119. { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 },
  120. { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 },
  121. { "sai", IMX_SC_R_SAI_0, 3, true, 0 },
  122. { "sai3", IMX_SC_R_SAI_3, 1, false, 0 },
  123. { "sai4", IMX_SC_R_SAI_4, 1, false, 0 },
  124. { "sai5", IMX_SC_R_SAI_5, 1, false, 0 },
  125. { "sai6", IMX_SC_R_SAI_6, 1, false, 0 },
  126. { "sai7", IMX_SC_R_SAI_7, 1, false, 0 },
  127. { "amix", IMX_SC_R_AMIX, 1, false, 0 },
  128. { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 },
  129. { "dsp", IMX_SC_R_DSP, 1, false, 0 },
  130. { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 },
  131. /* DMA SS */
  132. { "can", IMX_SC_R_CAN_0, 3, true, 0 },
  133. { "ftm", IMX_SC_R_FTM_0, 2, true, 0 },
  134. { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 },
  135. { "adc", IMX_SC_R_ADC_0, 2, true, 0 },
  136. { "lcd", IMX_SC_R_LCD_0, 1, true, 0 },
  137. { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 },
  138. { "lpuart", IMX_SC_R_UART_0, 4, true, 0 },
  139. { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 },
  140. { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
  141. /* VPU SS */
  142. { "vpu", IMX_SC_R_VPU, 1, false, 0 },
  143. { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 },
  144. { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 },
  145. { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 },
  146. { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 },
  147. { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 },
  148. { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 },
  149. { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 },
  150. /* GPU SS */
  151. { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 },
  152. /* HSIO SS */
  153. { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 },
  154. { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 },
  155. { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 },
  156. /* MIPI SS */
  157. { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 },
  158. { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 },
  159. { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 },
  160. { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 },
  161. { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 },
  162. { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 },
  163. /* LVDS SS */
  164. { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 },
  165. { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 },
  166. /* DC SS */
  167. { "dc0", IMX_SC_R_DC_0, 1, false, 0 },
  168. { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 },
  169. { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 },
  170. /* CM40 SS */
  171. { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 },
  172. { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 },
  173. { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0},
  174. { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0},
  175. { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0},
  176. /* CM41 SS */
  177. { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 },
  178. { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 },
  179. { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0},
  180. { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0},
  181. { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0},
  182. /* IMAGE SS */
  183. { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 },
  184. { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 },
  185. { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 },
  186. { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 },
  187. };
  188. static const struct imx_sc_pd_soc imx8qxp_scu_pd = {
  189. .pd_ranges = imx8qxp_scu_pd_ranges,
  190. .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges),
  191. };
  192. static struct imx_sc_ipc *pm_ipc_handle;
  193. static inline struct imx_sc_pm_domain *
  194. to_imx_sc_pd(struct generic_pm_domain *genpd)
  195. {
  196. return container_of(genpd, struct imx_sc_pm_domain, pd);
  197. }
  198. static void imx_sc_pd_get_console_rsrc(void)
  199. {
  200. struct of_phandle_args specs;
  201. int ret;
  202. if (!of_stdout)
  203. return;
  204. ret = of_parse_phandle_with_args(of_stdout, "power-domains",
  205. "#power-domain-cells",
  206. 0, &specs);
  207. if (ret)
  208. return;
  209. imx_con_rsrc = specs.args[0];
  210. }
  211. static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
  212. {
  213. struct imx_sc_msg_req_set_resource_power_mode msg;
  214. struct imx_sc_rpc_msg *hdr = &msg.hdr;
  215. struct imx_sc_pm_domain *pd;
  216. int ret;
  217. pd = to_imx_sc_pd(domain);
  218. hdr->ver = IMX_SC_RPC_VERSION;
  219. hdr->svc = IMX_SC_RPC_SVC_PM;
  220. hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE;
  221. hdr->size = 2;
  222. msg.resource = pd->rsrc;
  223. msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP;
  224. ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true);
  225. if (ret)
  226. dev_err(&domain->dev, "failed to power %s resource %d ret %d\n",
  227. power_on ? "up" : "off", pd->rsrc, ret);
  228. return ret;
  229. }
  230. static int imx_sc_pd_power_on(struct generic_pm_domain *domain)
  231. {
  232. return imx_sc_pd_power(domain, true);
  233. }
  234. static int imx_sc_pd_power_off(struct generic_pm_domain *domain)
  235. {
  236. return imx_sc_pd_power(domain, false);
  237. }
  238. static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec,
  239. void *data)
  240. {
  241. struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
  242. struct genpd_onecell_data *pd_data = data;
  243. unsigned int i;
  244. for (i = 0; i < pd_data->num_domains; i++) {
  245. struct imx_sc_pm_domain *sc_pd;
  246. sc_pd = to_imx_sc_pd(pd_data->domains[i]);
  247. if (sc_pd->rsrc == spec->args[0]) {
  248. domain = &sc_pd->pd;
  249. break;
  250. }
  251. }
  252. return domain;
  253. }
  254. static struct imx_sc_pm_domain *
  255. imx_scu_add_pm_domain(struct device *dev, int idx,
  256. const struct imx_sc_pd_range *pd_ranges)
  257. {
  258. struct imx_sc_pm_domain *sc_pd;
  259. bool is_off = true;
  260. int ret;
  261. if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx))
  262. return NULL;
  263. sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL);
  264. if (!sc_pd)
  265. return ERR_PTR(-ENOMEM);
  266. sc_pd->rsrc = pd_ranges->rsrc + idx;
  267. sc_pd->pd.power_off = imx_sc_pd_power_off;
  268. sc_pd->pd.power_on = imx_sc_pd_power_on;
  269. if (pd_ranges->postfix)
  270. snprintf(sc_pd->name, sizeof(sc_pd->name),
  271. "%s%i", pd_ranges->name, pd_ranges->start_from + idx);
  272. else
  273. snprintf(sc_pd->name, sizeof(sc_pd->name),
  274. "%s", pd_ranges->name);
  275. sc_pd->pd.name = sc_pd->name;
  276. if (imx_con_rsrc == sc_pd->rsrc) {
  277. sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON;
  278. is_off = false;
  279. }
  280. if (sc_pd->rsrc >= IMX_SC_R_LAST) {
  281. dev_warn(dev, "invalid pd %s rsrc id %d found",
  282. sc_pd->name, sc_pd->rsrc);
  283. devm_kfree(dev, sc_pd);
  284. return NULL;
  285. }
  286. ret = pm_genpd_init(&sc_pd->pd, NULL, is_off);
  287. if (ret) {
  288. dev_warn(dev, "failed to init pd %s rsrc id %d",
  289. sc_pd->name, sc_pd->rsrc);
  290. devm_kfree(dev, sc_pd);
  291. return NULL;
  292. }
  293. return sc_pd;
  294. }
  295. static int imx_scu_init_pm_domains(struct device *dev,
  296. const struct imx_sc_pd_soc *pd_soc)
  297. {
  298. const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges;
  299. struct generic_pm_domain **domains;
  300. struct genpd_onecell_data *pd_data;
  301. struct imx_sc_pm_domain *sc_pd;
  302. u32 count = 0;
  303. int i, j;
  304. for (i = 0; i < pd_soc->num_ranges; i++)
  305. count += pd_ranges[i].num;
  306. domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL);
  307. if (!domains)
  308. return -ENOMEM;
  309. pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL);
  310. if (!pd_data)
  311. return -ENOMEM;
  312. count = 0;
  313. for (i = 0; i < pd_soc->num_ranges; i++) {
  314. for (j = 0; j < pd_ranges[i].num; j++) {
  315. sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]);
  316. if (IS_ERR_OR_NULL(sc_pd))
  317. continue;
  318. domains[count++] = &sc_pd->pd;
  319. dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name);
  320. }
  321. }
  322. pd_data->domains = domains;
  323. pd_data->num_domains = count;
  324. pd_data->xlate = imx_scu_pd_xlate;
  325. of_genpd_add_provider_onecell(dev->of_node, pd_data);
  326. return 0;
  327. }
  328. static int imx_sc_pd_probe(struct platform_device *pdev)
  329. {
  330. const struct imx_sc_pd_soc *pd_soc;
  331. int ret;
  332. ret = imx_scu_get_handle(&pm_ipc_handle);
  333. if (ret)
  334. return ret;
  335. pd_soc = of_device_get_match_data(&pdev->dev);
  336. if (!pd_soc)
  337. return -ENODEV;
  338. imx_sc_pd_get_console_rsrc();
  339. return imx_scu_init_pm_domains(&pdev->dev, pd_soc);
  340. }
  341. static const struct of_device_id imx_sc_pd_match[] = {
  342. { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd},
  343. { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd},
  344. { /* sentinel */ }
  345. };
  346. static struct platform_driver imx_sc_pd_driver = {
  347. .driver = {
  348. .name = "imx-scu-pd",
  349. .of_match_table = imx_sc_pd_match,
  350. },
  351. .probe = imx_sc_pd_probe,
  352. };
  353. builtin_platform_driver(imx_sc_pd_driver);
  354. MODULE_AUTHOR("Dong Aisheng <[email protected]>");
  355. MODULE_DESCRIPTION("IMX SCU Power Domain driver");
  356. MODULE_LICENSE("GPL v2");