0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054 #include <dt-bindings/firmware/imx/rsrc.h>
0055 #include <linux/firmware/imx/sci.h>
0056 #include <linux/firmware/imx/svc/rm.h>
0057 #include <linux/io.h>
0058 #include <linux/module.h>
0059 #include <linux/of.h>
0060 #include <linux/of_address.h>
0061 #include <linux/of_platform.h>
0062 #include <linux/platform_device.h>
0063 #include <linux/pm.h>
0064 #include <linux/pm_domain.h>
0065 #include <linux/slab.h>
0066
0067
0068 struct imx_sc_msg_req_set_resource_power_mode {
0069 struct imx_sc_rpc_msg hdr;
0070 u16 resource;
0071 u8 mode;
0072 } __packed __aligned(4);
0073
0074 #define IMX_SCU_PD_NAME_SIZE 20
0075 struct imx_sc_pm_domain {
0076 struct generic_pm_domain pd;
0077 char name[IMX_SCU_PD_NAME_SIZE];
0078 u32 rsrc;
0079 };
0080
0081 struct imx_sc_pd_range {
0082 char *name;
0083 u32 rsrc;
0084 u8 num;
0085
0086
0087 bool postfix;
0088 u8 start_from;
0089 };
0090
0091 struct imx_sc_pd_soc {
0092 const struct imx_sc_pd_range *pd_ranges;
0093 u8 num_ranges;
0094 };
0095
0096 static int imx_con_rsrc;
0097
0098 static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
0099
0100 { "pwm", IMX_SC_R_PWM_0, 8, true, 0 },
0101 { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 },
0102 { "gpt", IMX_SC_R_GPT_0, 5, true, 0 },
0103 { "kpp", IMX_SC_R_KPP, 1, false, 0 },
0104 { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 },
0105 { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 },
0106 { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 },
0107
0108
0109 { "usb", IMX_SC_R_USB_0, 2, true, 0 },
0110 { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 },
0111 { "usb2", IMX_SC_R_USB_2, 1, false, 0 },
0112 { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 },
0113 { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 },
0114 { "enet", IMX_SC_R_ENET_0, 2, true, 0 },
0115 { "nand", IMX_SC_R_NAND, 1, false, 0 },
0116 { "mlb", IMX_SC_R_MLB_0, 1, true, 0 },
0117
0118
0119 { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 },
0120 { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 },
0121 { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 },
0122 { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 },
0123 { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 },
0124 { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 },
0125 { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 },
0126 { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 },
0127 { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 },
0128 { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 },
0129 { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 },
0130 { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 },
0131 { "sai", IMX_SC_R_SAI_0, 3, true, 0 },
0132 { "sai3", IMX_SC_R_SAI_3, 1, false, 0 },
0133 { "sai4", IMX_SC_R_SAI_4, 1, false, 0 },
0134 { "sai5", IMX_SC_R_SAI_5, 1, false, 0 },
0135 { "sai6", IMX_SC_R_SAI_6, 1, false, 0 },
0136 { "sai7", IMX_SC_R_SAI_7, 1, false, 0 },
0137 { "amix", IMX_SC_R_AMIX, 1, false, 0 },
0138 { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 },
0139 { "dsp", IMX_SC_R_DSP, 1, false, 0 },
0140 { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 },
0141
0142
0143 { "can", IMX_SC_R_CAN_0, 3, true, 0 },
0144 { "ftm", IMX_SC_R_FTM_0, 2, true, 0 },
0145 { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 },
0146 { "adc", IMX_SC_R_ADC_0, 2, true, 0 },
0147 { "lcd", IMX_SC_R_LCD_0, 1, true, 0 },
0148 { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 },
0149 { "lpuart", IMX_SC_R_UART_0, 4, true, 0 },
0150 { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 },
0151 { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
0152
0153
0154 { "vpu", IMX_SC_R_VPU, 1, false, 0 },
0155 { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 },
0156 { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 },
0157 { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 },
0158 { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 },
0159 { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 },
0160 { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 },
0161 { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 },
0162
0163
0164 { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 },
0165
0166
0167 { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 },
0168 { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 },
0169 { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 },
0170
0171
0172 { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 },
0173 { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 },
0174 { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 },
0175
0176 { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 },
0177 { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 },
0178 { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 },
0179
0180
0181 { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 },
0182 { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 },
0183
0184
0185 { "dc0", IMX_SC_R_DC_0, 1, false, 0 },
0186 { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 },
0187 { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 },
0188
0189
0190 { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 },
0191 { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 },
0192 { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0},
0193 { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0},
0194 { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0},
0195
0196
0197 { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 },
0198 { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 },
0199 { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0},
0200 { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0},
0201 { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0},
0202
0203
0204 { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 },
0205 { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 },
0206 { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 },
0207 { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 },
0208 };
0209
0210 static const struct imx_sc_pd_soc imx8qxp_scu_pd = {
0211 .pd_ranges = imx8qxp_scu_pd_ranges,
0212 .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges),
0213 };
0214
0215 static struct imx_sc_ipc *pm_ipc_handle;
0216
0217 static inline struct imx_sc_pm_domain *
0218 to_imx_sc_pd(struct generic_pm_domain *genpd)
0219 {
0220 return container_of(genpd, struct imx_sc_pm_domain, pd);
0221 }
0222
0223 static void imx_sc_pd_get_console_rsrc(void)
0224 {
0225 struct of_phandle_args specs;
0226 int ret;
0227
0228 if (!of_stdout)
0229 return;
0230
0231 ret = of_parse_phandle_with_args(of_stdout, "power-domains",
0232 "#power-domain-cells",
0233 0, &specs);
0234 if (ret)
0235 return;
0236
0237 imx_con_rsrc = specs.args[0];
0238 }
0239
0240 static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
0241 {
0242 struct imx_sc_msg_req_set_resource_power_mode msg;
0243 struct imx_sc_rpc_msg *hdr = &msg.hdr;
0244 struct imx_sc_pm_domain *pd;
0245 int ret;
0246
0247 pd = to_imx_sc_pd(domain);
0248
0249 hdr->ver = IMX_SC_RPC_VERSION;
0250 hdr->svc = IMX_SC_RPC_SVC_PM;
0251 hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE;
0252 hdr->size = 2;
0253
0254 msg.resource = pd->rsrc;
0255 msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP;
0256
0257 ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true);
0258 if (ret)
0259 dev_err(&domain->dev, "failed to power %s resource %d ret %d\n",
0260 power_on ? "up" : "off", pd->rsrc, ret);
0261
0262 return ret;
0263 }
0264
0265 static int imx_sc_pd_power_on(struct generic_pm_domain *domain)
0266 {
0267 return imx_sc_pd_power(domain, true);
0268 }
0269
0270 static int imx_sc_pd_power_off(struct generic_pm_domain *domain)
0271 {
0272 return imx_sc_pd_power(domain, false);
0273 }
0274
0275 static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec,
0276 void *data)
0277 {
0278 struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
0279 struct genpd_onecell_data *pd_data = data;
0280 unsigned int i;
0281
0282 for (i = 0; i < pd_data->num_domains; i++) {
0283 struct imx_sc_pm_domain *sc_pd;
0284
0285 sc_pd = to_imx_sc_pd(pd_data->domains[i]);
0286 if (sc_pd->rsrc == spec->args[0]) {
0287 domain = &sc_pd->pd;
0288 break;
0289 }
0290 }
0291
0292 return domain;
0293 }
0294
0295 static struct imx_sc_pm_domain *
0296 imx_scu_add_pm_domain(struct device *dev, int idx,
0297 const struct imx_sc_pd_range *pd_ranges)
0298 {
0299 struct imx_sc_pm_domain *sc_pd;
0300 bool is_off = true;
0301 int ret;
0302
0303 if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx))
0304 return NULL;
0305
0306 sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL);
0307 if (!sc_pd)
0308 return ERR_PTR(-ENOMEM);
0309
0310 sc_pd->rsrc = pd_ranges->rsrc + idx;
0311 sc_pd->pd.power_off = imx_sc_pd_power_off;
0312 sc_pd->pd.power_on = imx_sc_pd_power_on;
0313
0314 if (pd_ranges->postfix)
0315 snprintf(sc_pd->name, sizeof(sc_pd->name),
0316 "%s%i", pd_ranges->name, pd_ranges->start_from + idx);
0317 else
0318 snprintf(sc_pd->name, sizeof(sc_pd->name),
0319 "%s", pd_ranges->name);
0320
0321 sc_pd->pd.name = sc_pd->name;
0322 if (imx_con_rsrc == sc_pd->rsrc) {
0323 sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON;
0324 is_off = false;
0325 }
0326
0327 if (sc_pd->rsrc >= IMX_SC_R_LAST) {
0328 dev_warn(dev, "invalid pd %s rsrc id %d found",
0329 sc_pd->name, sc_pd->rsrc);
0330
0331 devm_kfree(dev, sc_pd);
0332 return NULL;
0333 }
0334
0335 ret = pm_genpd_init(&sc_pd->pd, NULL, is_off);
0336 if (ret) {
0337 dev_warn(dev, "failed to init pd %s rsrc id %d",
0338 sc_pd->name, sc_pd->rsrc);
0339 devm_kfree(dev, sc_pd);
0340 return NULL;
0341 }
0342
0343 return sc_pd;
0344 }
0345
0346 static int imx_scu_init_pm_domains(struct device *dev,
0347 const struct imx_sc_pd_soc *pd_soc)
0348 {
0349 const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges;
0350 struct generic_pm_domain **domains;
0351 struct genpd_onecell_data *pd_data;
0352 struct imx_sc_pm_domain *sc_pd;
0353 u32 count = 0;
0354 int i, j;
0355
0356 for (i = 0; i < pd_soc->num_ranges; i++)
0357 count += pd_ranges[i].num;
0358
0359 domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL);
0360 if (!domains)
0361 return -ENOMEM;
0362
0363 pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL);
0364 if (!pd_data)
0365 return -ENOMEM;
0366
0367 count = 0;
0368 for (i = 0; i < pd_soc->num_ranges; i++) {
0369 for (j = 0; j < pd_ranges[i].num; j++) {
0370 sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]);
0371 if (IS_ERR_OR_NULL(sc_pd))
0372 continue;
0373
0374 domains[count++] = &sc_pd->pd;
0375 dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name);
0376 }
0377 }
0378
0379 pd_data->domains = domains;
0380 pd_data->num_domains = count;
0381 pd_data->xlate = imx_scu_pd_xlate;
0382
0383 of_genpd_add_provider_onecell(dev->of_node, pd_data);
0384
0385 return 0;
0386 }
0387
0388 static int imx_sc_pd_probe(struct platform_device *pdev)
0389 {
0390 const struct imx_sc_pd_soc *pd_soc;
0391 int ret;
0392
0393 ret = imx_scu_get_handle(&pm_ipc_handle);
0394 if (ret)
0395 return ret;
0396
0397 pd_soc = of_device_get_match_data(&pdev->dev);
0398 if (!pd_soc)
0399 return -ENODEV;
0400
0401 imx_sc_pd_get_console_rsrc();
0402
0403 return imx_scu_init_pm_domains(&pdev->dev, pd_soc);
0404 }
0405
0406 static const struct of_device_id imx_sc_pd_match[] = {
0407 { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd},
0408 { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd},
0409 { }
0410 };
0411
0412 static struct platform_driver imx_sc_pd_driver = {
0413 .driver = {
0414 .name = "imx-scu-pd",
0415 .of_match_table = imx_sc_pd_match,
0416 },
0417 .probe = imx_sc_pd_probe,
0418 };
0419 builtin_platform_driver(imx_sc_pd_driver);
0420
0421 MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
0422 MODULE_DESCRIPTION("IMX SCU Power Domain driver");
0423 MODULE_LICENSE("GPL v2");