Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (C) 2016 Freescale Semiconductor, Inc.
0004  * Copyright 2017-2018 NXP
0005  *  Dong Aisheng <aisheng.dong@nxp.com>
0006  *
0007  * Implementation of the SCU based Power Domains
0008  *
0009  * NOTE: a better implementation suggested by Ulf Hansson is using a
0010  * single global power domain and implement the ->attach|detach_dev()
0011  * callback for the genpd and use the regular of_genpd_add_provider_simple().
0012  * From within the ->attach_dev(), we could get the OF node for
0013  * the device that is being attached and then parse the power-domain
0014  * cell containing the "resource id" and store that in the per device
0015  * struct generic_pm_domain_data (we have void pointer there for
0016  * storing these kind of things).
0017  *
0018  * Additionally, we need to implement the ->stop() and ->start()
0019  * callbacks of genpd, which is where you "power on/off" devices,
0020  * rather than using the above ->power_on|off() callbacks.
0021  *
0022  * However, there're two known issues:
0023  * 1. The ->attach_dev() of power domain infrastructure still does
0024  *    not support multi domains case as the struct device *dev passed
0025  *    in is a virtual PD device, it does not help for parsing the real
0026  *    device resource id from device tree, so it's unware of which
0027  *    real sub power domain of device should be attached.
0028  *
0029  *    The framework needs some proper extension to support multi power
0030  *    domain cases.
0031  *
0032  *    Update: Genpd assigns the ->of_node for the virtual device before it
0033  *    invokes ->attach_dev() callback, hence parsing for device resources via
0034  *    DT should work fine.
0035  *
0036  * 2. It also breaks most of current drivers as the driver probe sequence
0037  *    behavior changed if removing ->power_on|off() callback and use
0038  *    ->start() and ->stop() instead. genpd_dev_pm_attach will only power
0039  *    up the domain and attach device, but will not call .start() which
0040  *    relies on device runtime pm. That means the device power is still
0041  *    not up before running driver probe function. For SCU enabled
0042  *    platforms, all device drivers accessing registers/clock without power
0043  *    domain enabled will trigger a HW access error. That means we need fix
0044  *    most drivers probe sequence with proper runtime pm.
0045  *
0046  *    Update: Runtime PM support isn't necessary. Instead, this can easily be
0047  *    fixed in drivers by adding a call to dev_pm_domain_start() during probe.
0048  *
0049  * In summary, the second part needs to be addressed via minor updates to the
0050  * relevant drivers, before the "single global power domain" model can be used.
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 /* SCU Power Mode Protocol definition */
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     /* add domain index */
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     /* LSIO SS */
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     /* CONN SS */
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     /* AUDIO SS */
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     /* DMA SS */
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     /* VPU SS */
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     /* GPU SS */
0164     { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 },
0165 
0166     /* HSIO SS */
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     /* MIPI SS */
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     /* LVDS SS */
0181     { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 },
0182     { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 },
0183 
0184     /* DC SS */
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     /* CM40 SS */
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     /* CM41 SS */
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     /* IMAGE SS */
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     { /* sentinel */ }
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");