A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (C) 2016 Freescale Semiconductor, Inc. |
| 4 | * Copyright 2017-2018 NXP |
| 5 | * Dong Aisheng <aisheng.dong@nxp.com> |
| 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 | * |
Ulf Hansson | 7c6b089 | 2021-03-17 10:31:17 +0100 | [diff] [blame] | 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 | * |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 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 | * |
Ulf Hansson | 7c6b089 | 2021-03-17 10:31:17 +0100 | [diff] [blame] | 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. |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 51 | * |
| 52 | */ |
| 53 | |
| 54 | #include <dt-bindings/firmware/imx/rsrc.h> |
| 55 | #include <linux/firmware/imx/sci.h> |
Peng Fan | e2314cf | 2020-09-08 18:07:01 +0800 | [diff] [blame] | 56 | #include <linux/firmware/imx/svc/rm.h> |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 57 | #include <linux/io.h> |
| 58 | #include <linux/module.h> |
| 59 | #include <linux/of.h> |
| 60 | #include <linux/of_address.h> |
| 61 | #include <linux/of_platform.h> |
| 62 | #include <linux/platform_device.h> |
| 63 | #include <linux/pm.h> |
| 64 | #include <linux/pm_domain.h> |
| 65 | #include <linux/slab.h> |
| 66 | |
| 67 | /* SCU Power Mode Protocol definition */ |
| 68 | struct imx_sc_msg_req_set_resource_power_mode { |
| 69 | struct imx_sc_rpc_msg hdr; |
| 70 | u16 resource; |
| 71 | u8 mode; |
Leonard Crestez | 7c1a1c8 | 2020-02-20 18:29:35 +0200 | [diff] [blame] | 72 | } __packed __aligned(4); |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 73 | |
| 74 | #define IMX_SCU_PD_NAME_SIZE 20 |
| 75 | struct imx_sc_pm_domain { |
| 76 | struct generic_pm_domain pd; |
| 77 | char name[IMX_SCU_PD_NAME_SIZE]; |
| 78 | u32 rsrc; |
| 79 | }; |
| 80 | |
| 81 | struct imx_sc_pd_range { |
| 82 | char *name; |
| 83 | u32 rsrc; |
| 84 | u8 num; |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 85 | |
| 86 | /* add domain index */ |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 87 | bool postfix; |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 88 | u8 start_from; |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 89 | }; |
| 90 | |
| 91 | struct imx_sc_pd_soc { |
| 92 | const struct imx_sc_pd_range *pd_ranges; |
| 93 | u8 num_ranges; |
| 94 | }; |
| 95 | |
Dong Aisheng | 893cfb9 | 2021-03-05 21:17:48 +0800 | [diff] [blame] | 96 | static int imx_con_rsrc; |
| 97 | |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 98 | static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { |
| 99 | /* LSIO SS */ |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 100 | { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, |
| 101 | { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, |
| 102 | { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, |
| 103 | { "kpp", IMX_SC_R_KPP, 1, false, 0 }, |
| 104 | { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, |
Daniel Baluta | 6d9d2171 | 2019-07-18 13:25:17 +0300 | [diff] [blame] | 105 | { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, |
Sebastien Fagard | e391b24 | 2020-01-27 14:27:32 +0000 | [diff] [blame] | 106 | { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 107 | |
| 108 | /* CONN SS */ |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 109 | { "usb", IMX_SC_R_USB_0, 2, true, 0 }, |
| 110 | { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, |
| 111 | { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, |
| 112 | { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, |
| 113 | { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, |
| 114 | { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, |
| 115 | { "nand", IMX_SC_R_NAND, 1, false, 0 }, |
| 116 | { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 117 | |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 118 | /* AUDIO SS */ |
| 119 | { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, |
| 120 | { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, |
| 121 | { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, |
Daniel Baluta | 0a7696b | 2020-01-27 14:27:31 +0000 | [diff] [blame] | 122 | { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 }, |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 123 | { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, |
| 124 | { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, |
| 125 | { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, |
| 126 | { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, |
| 127 | { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, |
| 128 | { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, |
| 129 | { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, |
Daniel Baluta | 0a7696b | 2020-01-27 14:27:31 +0000 | [diff] [blame] | 130 | { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 131 | { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, |
Daniel Baluta | 0a7696b | 2020-01-27 14:27:31 +0000 | [diff] [blame] | 132 | { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, |
| 133 | { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, |
| 134 | { "sai5", IMX_SC_R_SAI_5, 1, false, 0 }, |
| 135 | { "sai6", IMX_SC_R_SAI_6, 1, false, 0 }, |
| 136 | { "sai7", IMX_SC_R_SAI_7, 1, false, 0 }, |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 137 | { "amix", IMX_SC_R_AMIX, 1, false, 0 }, |
| 138 | { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, |
| 139 | { "dsp", IMX_SC_R_DSP, 1, false, 0 }, |
| 140 | { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 141 | |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 142 | /* DMA SS */ |
| 143 | { "can", IMX_SC_R_CAN_0, 3, true, 0 }, |
| 144 | { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, |
| 145 | { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, |
Frank Li | f63af5f | 2021-03-19 16:23:52 -0500 | [diff] [blame] | 146 | { "adc", IMX_SC_R_ADC_0, 2, true, 0 }, |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 147 | { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, |
| 148 | { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, |
| 149 | { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, |
| 150 | { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, |
Daniel Baluta | d43dc52 | 2019-07-18 13:25:19 +0300 | [diff] [blame] | 151 | { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 152 | |
| 153 | /* VPU SS */ |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 154 | { "vpu", IMX_SC_R_VPU, 1, false, 0 }, |
| 155 | { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, |
| 156 | { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, |
| 157 | { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, |
Ming Qian | 16dcfe9 | 2022-01-26 11:09:31 +0800 | [diff] [blame] | 158 | { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 }, |
| 159 | { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 }, |
| 160 | { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 }, |
| 161 | { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 162 | |
| 163 | /* GPU SS */ |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 164 | { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 165 | |
| 166 | /* HSIO SS */ |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 167 | { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, |
| 168 | { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 169 | { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 170 | |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 171 | /* MIPI SS */ |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 172 | { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, |
| 173 | { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, |
| 174 | { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 175 | |
Liu Ying | e00e70a | 2020-11-18 15:31:23 +0800 | [diff] [blame] | 176 | { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 }, |
| 177 | { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 }, |
| 178 | { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 }, |
| 179 | |
Aisheng Dong | 32654da | 2019-02-20 14:38:36 +0000 | [diff] [blame] | 180 | /* LVDS SS */ |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 181 | { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, |
Liu Ying | f57afc6f | 2020-11-18 15:31:22 +0800 | [diff] [blame] | 182 | { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 183 | |
| 184 | /* DC SS */ |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 185 | { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, |
| 186 | { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, |
Liu Ying | 90ad8e0 | 2020-11-18 15:31:21 +0800 | [diff] [blame] | 187 | { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 }, |
Joakim Zhang | 705dcca | 2020-02-17 11:19:15 +0800 | [diff] [blame] | 188 | |
| 189 | /* CM40 SS */ |
Peng Fan | cfda066 | 2020-06-05 09:59:32 +0800 | [diff] [blame] | 190 | { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 }, |
| 191 | { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 }, |
| 192 | { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0}, |
| 193 | { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0}, |
| 194 | { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0}, |
| 195 | |
| 196 | /* CM41 SS */ |
| 197 | { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, |
| 198 | { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, |
| 199 | { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0}, |
| 200 | { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0}, |
| 201 | { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0}, |
Mirela Rabulea | 0b7a8e5 | 2020-11-12 05:05:49 +0200 | [diff] [blame] | 202 | |
| 203 | /* IMAGE SS */ |
| 204 | { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 }, |
| 205 | { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 }, |
| 206 | { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 }, |
| 207 | { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 }, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 208 | }; |
| 209 | |
| 210 | static const struct imx_sc_pd_soc imx8qxp_scu_pd = { |
| 211 | .pd_ranges = imx8qxp_scu_pd_ranges, |
| 212 | .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), |
| 213 | }; |
| 214 | |
| 215 | static struct imx_sc_ipc *pm_ipc_handle; |
| 216 | |
| 217 | static inline struct imx_sc_pm_domain * |
| 218 | to_imx_sc_pd(struct generic_pm_domain *genpd) |
| 219 | { |
| 220 | return container_of(genpd, struct imx_sc_pm_domain, pd); |
| 221 | } |
| 222 | |
Dong Aisheng | 893cfb9 | 2021-03-05 21:17:48 +0800 | [diff] [blame] | 223 | static void imx_sc_pd_get_console_rsrc(void) |
| 224 | { |
| 225 | struct of_phandle_args specs; |
| 226 | int ret; |
| 227 | |
| 228 | if (!of_stdout) |
| 229 | return; |
| 230 | |
| 231 | ret = of_parse_phandle_with_args(of_stdout, "power-domains", |
| 232 | "#power-domain-cells", |
| 233 | 0, &specs); |
| 234 | if (ret) |
| 235 | return; |
| 236 | |
| 237 | imx_con_rsrc = specs.args[0]; |
| 238 | } |
| 239 | |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 240 | static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) |
| 241 | { |
| 242 | struct imx_sc_msg_req_set_resource_power_mode msg; |
| 243 | struct imx_sc_rpc_msg *hdr = &msg.hdr; |
| 244 | struct imx_sc_pm_domain *pd; |
| 245 | int ret; |
| 246 | |
| 247 | pd = to_imx_sc_pd(domain); |
| 248 | |
| 249 | hdr->ver = IMX_SC_RPC_VERSION; |
| 250 | hdr->svc = IMX_SC_RPC_SVC_PM; |
| 251 | hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; |
| 252 | hdr->size = 2; |
| 253 | |
| 254 | msg.resource = pd->rsrc; |
| 255 | msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; |
| 256 | |
| 257 | ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); |
| 258 | if (ret) |
| 259 | dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", |
| 260 | power_on ? "up" : "off", pd->rsrc, ret); |
| 261 | |
| 262 | return ret; |
| 263 | } |
| 264 | |
| 265 | static int imx_sc_pd_power_on(struct generic_pm_domain *domain) |
| 266 | { |
| 267 | return imx_sc_pd_power(domain, true); |
| 268 | } |
| 269 | |
| 270 | static int imx_sc_pd_power_off(struct generic_pm_domain *domain) |
| 271 | { |
| 272 | return imx_sc_pd_power(domain, false); |
| 273 | } |
| 274 | |
| 275 | static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, |
| 276 | void *data) |
| 277 | { |
| 278 | struct generic_pm_domain *domain = ERR_PTR(-ENOENT); |
| 279 | struct genpd_onecell_data *pd_data = data; |
| 280 | unsigned int i; |
| 281 | |
| 282 | for (i = 0; i < pd_data->num_domains; i++) { |
| 283 | struct imx_sc_pm_domain *sc_pd; |
| 284 | |
| 285 | sc_pd = to_imx_sc_pd(pd_data->domains[i]); |
| 286 | if (sc_pd->rsrc == spec->args[0]) { |
| 287 | domain = &sc_pd->pd; |
| 288 | break; |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | return domain; |
| 293 | } |
| 294 | |
| 295 | static struct imx_sc_pm_domain * |
| 296 | imx_scu_add_pm_domain(struct device *dev, int idx, |
| 297 | const struct imx_sc_pd_range *pd_ranges) |
| 298 | { |
| 299 | struct imx_sc_pm_domain *sc_pd; |
Dong Aisheng | 893cfb9 | 2021-03-05 21:17:48 +0800 | [diff] [blame] | 300 | bool is_off = true; |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 301 | int ret; |
| 302 | |
Peng Fan | e2314cf | 2020-09-08 18:07:01 +0800 | [diff] [blame] | 303 | if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) |
| 304 | return NULL; |
| 305 | |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 306 | sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); |
| 307 | if (!sc_pd) |
| 308 | return ERR_PTR(-ENOMEM); |
| 309 | |
| 310 | sc_pd->rsrc = pd_ranges->rsrc + idx; |
| 311 | sc_pd->pd.power_off = imx_sc_pd_power_off; |
| 312 | sc_pd->pd.power_on = imx_sc_pd_power_on; |
| 313 | |
| 314 | if (pd_ranges->postfix) |
| 315 | snprintf(sc_pd->name, sizeof(sc_pd->name), |
Aisheng Dong | ad8cc07 | 2019-02-20 14:38:32 +0000 | [diff] [blame] | 316 | "%s%i", pd_ranges->name, pd_ranges->start_from + idx); |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 317 | else |
| 318 | snprintf(sc_pd->name, sizeof(sc_pd->name), |
| 319 | "%s", pd_ranges->name); |
| 320 | |
| 321 | sc_pd->pd.name = sc_pd->name; |
Dong Aisheng | 893cfb9 | 2021-03-05 21:17:48 +0800 | [diff] [blame] | 322 | if (imx_con_rsrc == sc_pd->rsrc) { |
| 323 | sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON; |
| 324 | is_off = false; |
| 325 | } |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 326 | |
| 327 | if (sc_pd->rsrc >= IMX_SC_R_LAST) { |
| 328 | dev_warn(dev, "invalid pd %s rsrc id %d found", |
| 329 | sc_pd->name, sc_pd->rsrc); |
| 330 | |
| 331 | devm_kfree(dev, sc_pd); |
| 332 | return NULL; |
| 333 | } |
| 334 | |
Dong Aisheng | 893cfb9 | 2021-03-05 21:17:48 +0800 | [diff] [blame] | 335 | ret = pm_genpd_init(&sc_pd->pd, NULL, is_off); |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 336 | if (ret) { |
| 337 | dev_warn(dev, "failed to init pd %s rsrc id %d", |
| 338 | sc_pd->name, sc_pd->rsrc); |
| 339 | devm_kfree(dev, sc_pd); |
| 340 | return NULL; |
| 341 | } |
| 342 | |
| 343 | return sc_pd; |
| 344 | } |
| 345 | |
| 346 | static int imx_scu_init_pm_domains(struct device *dev, |
| 347 | const struct imx_sc_pd_soc *pd_soc) |
| 348 | { |
| 349 | const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; |
| 350 | struct generic_pm_domain **domains; |
| 351 | struct genpd_onecell_data *pd_data; |
| 352 | struct imx_sc_pm_domain *sc_pd; |
| 353 | u32 count = 0; |
| 354 | int i, j; |
| 355 | |
| 356 | for (i = 0; i < pd_soc->num_ranges; i++) |
| 357 | count += pd_ranges[i].num; |
| 358 | |
| 359 | domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); |
| 360 | if (!domains) |
| 361 | return -ENOMEM; |
| 362 | |
| 363 | pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); |
| 364 | if (!pd_data) |
| 365 | return -ENOMEM; |
| 366 | |
| 367 | count = 0; |
| 368 | for (i = 0; i < pd_soc->num_ranges; i++) { |
| 369 | for (j = 0; j < pd_ranges[i].num; j++) { |
| 370 | sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); |
| 371 | if (IS_ERR_OR_NULL(sc_pd)) |
| 372 | continue; |
| 373 | |
| 374 | domains[count++] = &sc_pd->pd; |
| 375 | dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); |
| 376 | } |
| 377 | } |
| 378 | |
| 379 | pd_data->domains = domains; |
| 380 | pd_data->num_domains = count; |
| 381 | pd_data->xlate = imx_scu_pd_xlate; |
| 382 | |
| 383 | of_genpd_add_provider_onecell(dev->of_node, pd_data); |
| 384 | |
| 385 | return 0; |
| 386 | } |
| 387 | |
| 388 | static int imx_sc_pd_probe(struct platform_device *pdev) |
| 389 | { |
| 390 | const struct imx_sc_pd_soc *pd_soc; |
| 391 | int ret; |
| 392 | |
| 393 | ret = imx_scu_get_handle(&pm_ipc_handle); |
| 394 | if (ret) |
| 395 | return ret; |
| 396 | |
| 397 | pd_soc = of_device_get_match_data(&pdev->dev); |
| 398 | if (!pd_soc) |
| 399 | return -ENODEV; |
| 400 | |
Dong Aisheng | 893cfb9 | 2021-03-05 21:17:48 +0800 | [diff] [blame] | 401 | imx_sc_pd_get_console_rsrc(); |
| 402 | |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 403 | return imx_scu_init_pm_domains(&pdev->dev, pd_soc); |
| 404 | } |
| 405 | |
| 406 | static const struct of_device_id imx_sc_pd_match[] = { |
| 407 | { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, |
Aisheng Dong | e59e59b | 2018-12-18 16:01:29 +0000 | [diff] [blame] | 408 | { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, |
A.s. Dong | c800cd7 | 2018-11-01 15:20:13 +0000 | [diff] [blame] | 409 | { /* sentinel */ } |
| 410 | }; |
| 411 | |
| 412 | static struct platform_driver imx_sc_pd_driver = { |
| 413 | .driver = { |
| 414 | .name = "imx-scu-pd", |
| 415 | .of_match_table = imx_sc_pd_match, |
| 416 | }, |
| 417 | .probe = imx_sc_pd_probe, |
| 418 | }; |
| 419 | builtin_platform_driver(imx_sc_pd_driver); |
| 420 | |
| 421 | MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); |
| 422 | MODULE_DESCRIPTION("IMX SCU Power Domain driver"); |
| 423 | MODULE_LICENSE("GPL v2"); |