1*e2ad626fSUlf Hansson // SPDX-License-Identifier: GPL-2.0+ 2*e2ad626fSUlf Hansson /* 3*e2ad626fSUlf Hansson * Copyright (C) 2016 Freescale Semiconductor, Inc. 4*e2ad626fSUlf Hansson * Copyright 2017-2018 NXP 5*e2ad626fSUlf Hansson * Dong Aisheng <aisheng.dong@nxp.com> 6*e2ad626fSUlf Hansson * 7*e2ad626fSUlf Hansson * Implementation of the SCU based Power Domains 8*e2ad626fSUlf Hansson * 9*e2ad626fSUlf Hansson * NOTE: a better implementation suggested by Ulf Hansson is using a 10*e2ad626fSUlf Hansson * single global power domain and implement the ->attach|detach_dev() 11*e2ad626fSUlf Hansson * callback for the genpd and use the regular of_genpd_add_provider_simple(). 12*e2ad626fSUlf Hansson * From within the ->attach_dev(), we could get the OF node for 13*e2ad626fSUlf Hansson * the device that is being attached and then parse the power-domain 14*e2ad626fSUlf Hansson * cell containing the "resource id" and store that in the per device 15*e2ad626fSUlf Hansson * struct generic_pm_domain_data (we have void pointer there for 16*e2ad626fSUlf Hansson * storing these kind of things). 17*e2ad626fSUlf Hansson * 18*e2ad626fSUlf Hansson * Additionally, we need to implement the ->stop() and ->start() 19*e2ad626fSUlf Hansson * callbacks of genpd, which is where you "power on/off" devices, 20*e2ad626fSUlf Hansson * rather than using the above ->power_on|off() callbacks. 21*e2ad626fSUlf Hansson * 22*e2ad626fSUlf Hansson * However, there're two known issues: 23*e2ad626fSUlf Hansson * 1. The ->attach_dev() of power domain infrastructure still does 24*e2ad626fSUlf Hansson * not support multi domains case as the struct device *dev passed 25*e2ad626fSUlf Hansson * in is a virtual PD device, it does not help for parsing the real 26*e2ad626fSUlf Hansson * device resource id from device tree, so it's unware of which 27*e2ad626fSUlf Hansson * real sub power domain of device should be attached. 28*e2ad626fSUlf Hansson * 29*e2ad626fSUlf Hansson * The framework needs some proper extension to support multi power 30*e2ad626fSUlf Hansson * domain cases. 31*e2ad626fSUlf Hansson * 32*e2ad626fSUlf Hansson * Update: Genpd assigns the ->of_node for the virtual device before it 33*e2ad626fSUlf Hansson * invokes ->attach_dev() callback, hence parsing for device resources via 34*e2ad626fSUlf Hansson * DT should work fine. 35*e2ad626fSUlf Hansson * 36*e2ad626fSUlf Hansson * 2. It also breaks most of current drivers as the driver probe sequence 37*e2ad626fSUlf Hansson * behavior changed if removing ->power_on|off() callback and use 38*e2ad626fSUlf Hansson * ->start() and ->stop() instead. genpd_dev_pm_attach will only power 39*e2ad626fSUlf Hansson * up the domain and attach device, but will not call .start() which 40*e2ad626fSUlf Hansson * relies on device runtime pm. That means the device power is still 41*e2ad626fSUlf Hansson * not up before running driver probe function. For SCU enabled 42*e2ad626fSUlf Hansson * platforms, all device drivers accessing registers/clock without power 43*e2ad626fSUlf Hansson * domain enabled will trigger a HW access error. That means we need fix 44*e2ad626fSUlf Hansson * most drivers probe sequence with proper runtime pm. 45*e2ad626fSUlf Hansson * 46*e2ad626fSUlf Hansson * Update: Runtime PM support isn't necessary. Instead, this can easily be 47*e2ad626fSUlf Hansson * fixed in drivers by adding a call to dev_pm_domain_start() during probe. 48*e2ad626fSUlf Hansson * 49*e2ad626fSUlf Hansson * In summary, the second part needs to be addressed via minor updates to the 50*e2ad626fSUlf Hansson * relevant drivers, before the "single global power domain" model can be used. 51*e2ad626fSUlf Hansson * 52*e2ad626fSUlf Hansson */ 53*e2ad626fSUlf Hansson 54*e2ad626fSUlf Hansson #include <dt-bindings/firmware/imx/rsrc.h> 55*e2ad626fSUlf Hansson #include <linux/console.h> 56*e2ad626fSUlf Hansson #include <linux/firmware/imx/sci.h> 57*e2ad626fSUlf Hansson #include <linux/firmware/imx/svc/rm.h> 58*e2ad626fSUlf Hansson #include <linux/io.h> 59*e2ad626fSUlf Hansson #include <linux/module.h> 60*e2ad626fSUlf Hansson #include <linux/of.h> 61*e2ad626fSUlf Hansson #include <linux/of_address.h> 62*e2ad626fSUlf Hansson #include <linux/of_platform.h> 63*e2ad626fSUlf Hansson #include <linux/platform_device.h> 64*e2ad626fSUlf Hansson #include <linux/pm.h> 65*e2ad626fSUlf Hansson #include <linux/pm_domain.h> 66*e2ad626fSUlf Hansson #include <linux/slab.h> 67*e2ad626fSUlf Hansson 68*e2ad626fSUlf Hansson /* SCU Power Mode Protocol definition */ 69*e2ad626fSUlf Hansson struct imx_sc_msg_req_set_resource_power_mode { 70*e2ad626fSUlf Hansson struct imx_sc_rpc_msg hdr; 71*e2ad626fSUlf Hansson u16 resource; 72*e2ad626fSUlf Hansson u8 mode; 73*e2ad626fSUlf Hansson } __packed __aligned(4); 74*e2ad626fSUlf Hansson 75*e2ad626fSUlf Hansson struct req_get_resource_mode { 76*e2ad626fSUlf Hansson u16 resource; 77*e2ad626fSUlf Hansson }; 78*e2ad626fSUlf Hansson 79*e2ad626fSUlf Hansson struct resp_get_resource_mode { 80*e2ad626fSUlf Hansson u8 mode; 81*e2ad626fSUlf Hansson }; 82*e2ad626fSUlf Hansson 83*e2ad626fSUlf Hansson struct imx_sc_msg_req_get_resource_power_mode { 84*e2ad626fSUlf Hansson struct imx_sc_rpc_msg hdr; 85*e2ad626fSUlf Hansson union { 86*e2ad626fSUlf Hansson struct req_get_resource_mode req; 87*e2ad626fSUlf Hansson struct resp_get_resource_mode resp; 88*e2ad626fSUlf Hansson } data; 89*e2ad626fSUlf Hansson } __packed __aligned(4); 90*e2ad626fSUlf Hansson 91*e2ad626fSUlf Hansson #define IMX_SCU_PD_NAME_SIZE 20 92*e2ad626fSUlf Hansson struct imx_sc_pm_domain { 93*e2ad626fSUlf Hansson struct generic_pm_domain pd; 94*e2ad626fSUlf Hansson char name[IMX_SCU_PD_NAME_SIZE]; 95*e2ad626fSUlf Hansson u32 rsrc; 96*e2ad626fSUlf Hansson }; 97*e2ad626fSUlf Hansson 98*e2ad626fSUlf Hansson struct imx_sc_pd_range { 99*e2ad626fSUlf Hansson char *name; 100*e2ad626fSUlf Hansson u32 rsrc; 101*e2ad626fSUlf Hansson u8 num; 102*e2ad626fSUlf Hansson 103*e2ad626fSUlf Hansson /* add domain index */ 104*e2ad626fSUlf Hansson bool postfix; 105*e2ad626fSUlf Hansson u8 start_from; 106*e2ad626fSUlf Hansson }; 107*e2ad626fSUlf Hansson 108*e2ad626fSUlf Hansson struct imx_sc_pd_soc { 109*e2ad626fSUlf Hansson const struct imx_sc_pd_range *pd_ranges; 110*e2ad626fSUlf Hansson u8 num_ranges; 111*e2ad626fSUlf Hansson }; 112*e2ad626fSUlf Hansson 113*e2ad626fSUlf Hansson static int imx_con_rsrc; 114*e2ad626fSUlf Hansson 115*e2ad626fSUlf Hansson /* Align with the IMX_SC_PM_PW_MODE_[OFF,STBY,LP,ON] macros */ 116*e2ad626fSUlf Hansson static const char * const imx_sc_pm_mode[] = { 117*e2ad626fSUlf Hansson "IMX_SC_PM_PW_MODE_OFF", 118*e2ad626fSUlf Hansson "IMX_SC_PM_PW_MODE_STBY", 119*e2ad626fSUlf Hansson "IMX_SC_PM_PW_MODE_LP", 120*e2ad626fSUlf Hansson "IMX_SC_PM_PW_MODE_ON" 121*e2ad626fSUlf Hansson }; 122*e2ad626fSUlf Hansson 123*e2ad626fSUlf Hansson static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { 124*e2ad626fSUlf Hansson /* LSIO SS */ 125*e2ad626fSUlf Hansson { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, 126*e2ad626fSUlf Hansson { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, 127*e2ad626fSUlf Hansson { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, 128*e2ad626fSUlf Hansson { "kpp", IMX_SC_R_KPP, 1, false, 0 }, 129*e2ad626fSUlf Hansson { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, 130*e2ad626fSUlf Hansson { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, 131*e2ad626fSUlf Hansson { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 }, 132*e2ad626fSUlf Hansson 133*e2ad626fSUlf Hansson /* CONN SS */ 134*e2ad626fSUlf Hansson { "usb", IMX_SC_R_USB_0, 2, true, 0 }, 135*e2ad626fSUlf Hansson { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, 136*e2ad626fSUlf Hansson { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0}, 137*e2ad626fSUlf Hansson { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, 138*e2ad626fSUlf Hansson { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, 139*e2ad626fSUlf Hansson { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, 140*e2ad626fSUlf Hansson { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, 141*e2ad626fSUlf Hansson { "nand", IMX_SC_R_NAND, 1, false, 0 }, 142*e2ad626fSUlf Hansson { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, 143*e2ad626fSUlf Hansson 144*e2ad626fSUlf Hansson /* AUDIO SS */ 145*e2ad626fSUlf Hansson { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, 146*e2ad626fSUlf Hansson { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, 147*e2ad626fSUlf Hansson { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, 148*e2ad626fSUlf Hansson { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 }, 149*e2ad626fSUlf Hansson { "mclk-out-0", IMX_SC_R_MCLK_OUT_0, 1, false, 0 }, 150*e2ad626fSUlf Hansson { "mclk-out-1", IMX_SC_R_MCLK_OUT_1, 1, false, 0 }, 151*e2ad626fSUlf Hansson { "dma0-ch", IMX_SC_R_DMA_0_CH0, 32, true, 0 }, 152*e2ad626fSUlf Hansson { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, 153*e2ad626fSUlf Hansson { "dma2-ch", IMX_SC_R_DMA_2_CH0, 32, true, 0 }, 154*e2ad626fSUlf Hansson { "dma3-ch", IMX_SC_R_DMA_3_CH0, 32, true, 0 }, 155*e2ad626fSUlf Hansson { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, 156*e2ad626fSUlf Hansson { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, 157*e2ad626fSUlf Hansson { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, 158*e2ad626fSUlf Hansson { "esai1", IMX_SC_R_ESAI_1, 1, false, 0 }, 159*e2ad626fSUlf Hansson { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, 160*e2ad626fSUlf Hansson { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, 161*e2ad626fSUlf Hansson { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, 162*e2ad626fSUlf Hansson { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, 163*e2ad626fSUlf Hansson { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, 164*e2ad626fSUlf Hansson { "sai5", IMX_SC_R_SAI_5, 1, false, 0 }, 165*e2ad626fSUlf Hansson { "sai6", IMX_SC_R_SAI_6, 1, false, 0 }, 166*e2ad626fSUlf Hansson { "sai7", IMX_SC_R_SAI_7, 1, false, 0 }, 167*e2ad626fSUlf Hansson { "amix", IMX_SC_R_AMIX, 1, false, 0 }, 168*e2ad626fSUlf Hansson { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, 169*e2ad626fSUlf Hansson { "dsp", IMX_SC_R_DSP, 1, false, 0 }, 170*e2ad626fSUlf Hansson { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, 171*e2ad626fSUlf Hansson 172*e2ad626fSUlf Hansson /* DMA SS */ 173*e2ad626fSUlf Hansson { "can", IMX_SC_R_CAN_0, 3, true, 0 }, 174*e2ad626fSUlf Hansson { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, 175*e2ad626fSUlf Hansson { "lpi2c", IMX_SC_R_I2C_0, 5, true, 0 }, 176*e2ad626fSUlf Hansson { "adc", IMX_SC_R_ADC_0, 2, true, 0 }, 177*e2ad626fSUlf Hansson { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, 178*e2ad626fSUlf Hansson { "lcd-pll", IMX_SC_R_ELCDIF_PLL, 1, true, 0 }, 179*e2ad626fSUlf Hansson { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, 180*e2ad626fSUlf Hansson { "lpuart", IMX_SC_R_UART_0, 5, true, 0 }, 181*e2ad626fSUlf Hansson { "sim", IMX_SC_R_EMVSIM_0, 2, true, 0 }, 182*e2ad626fSUlf Hansson { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, 183*e2ad626fSUlf Hansson { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, 184*e2ad626fSUlf Hansson 185*e2ad626fSUlf Hansson /* VPU SS */ 186*e2ad626fSUlf Hansson { "vpu", IMX_SC_R_VPU, 1, false, 0 }, 187*e2ad626fSUlf Hansson { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, 188*e2ad626fSUlf Hansson { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, 189*e2ad626fSUlf Hansson { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, 190*e2ad626fSUlf Hansson { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 }, 191*e2ad626fSUlf Hansson { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 }, 192*e2ad626fSUlf Hansson { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 }, 193*e2ad626fSUlf Hansson { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 }, 194*e2ad626fSUlf Hansson 195*e2ad626fSUlf Hansson /* GPU SS */ 196*e2ad626fSUlf Hansson { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, 197*e2ad626fSUlf Hansson { "gpu1-pid", IMX_SC_R_GPU_1_PID0, 4, true, 0 }, 198*e2ad626fSUlf Hansson 199*e2ad626fSUlf Hansson 200*e2ad626fSUlf Hansson /* HSIO SS */ 201*e2ad626fSUlf Hansson { "pcie-a", IMX_SC_R_PCIE_A, 1, false, 0 }, 202*e2ad626fSUlf Hansson { "serdes-0", IMX_SC_R_SERDES_0, 1, false, 0 }, 203*e2ad626fSUlf Hansson { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, 204*e2ad626fSUlf Hansson { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, 205*e2ad626fSUlf Hansson { "sata-0", IMX_SC_R_SATA_0, 1, false, 0 }, 206*e2ad626fSUlf Hansson { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, 207*e2ad626fSUlf Hansson 208*e2ad626fSUlf Hansson /* MIPI SS */ 209*e2ad626fSUlf Hansson { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, 210*e2ad626fSUlf Hansson { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, 211*e2ad626fSUlf Hansson { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, 212*e2ad626fSUlf Hansson 213*e2ad626fSUlf Hansson { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 }, 214*e2ad626fSUlf Hansson { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 }, 215*e2ad626fSUlf Hansson { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 }, 216*e2ad626fSUlf Hansson 217*e2ad626fSUlf Hansson /* LVDS SS */ 218*e2ad626fSUlf Hansson { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, 219*e2ad626fSUlf Hansson { "lvds0-pwm", IMX_SC_R_LVDS_0_PWM_0, 1, false, 0 }, 220*e2ad626fSUlf Hansson { "lvds0-lpi2c", IMX_SC_R_LVDS_0_I2C_0, 2, true, 0 }, 221*e2ad626fSUlf Hansson { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 }, 222*e2ad626fSUlf Hansson { "lvds1-pwm", IMX_SC_R_LVDS_1_PWM_0, 1, false, 0 }, 223*e2ad626fSUlf Hansson { "lvds1-lpi2c", IMX_SC_R_LVDS_1_I2C_0, 2, true, 0 }, 224*e2ad626fSUlf Hansson 225*e2ad626fSUlf Hansson { "mipi1", IMX_SC_R_MIPI_1, 1, 0 }, 226*e2ad626fSUlf Hansson { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, 0 }, 227*e2ad626fSUlf Hansson { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, 1 }, 228*e2ad626fSUlf Hansson { "lvds1", IMX_SC_R_LVDS_1, 1, 0 }, 229*e2ad626fSUlf Hansson 230*e2ad626fSUlf Hansson /* DC SS */ 231*e2ad626fSUlf Hansson { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, 232*e2ad626fSUlf Hansson { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, 233*e2ad626fSUlf Hansson { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 }, 234*e2ad626fSUlf Hansson 235*e2ad626fSUlf Hansson { "dc1", IMX_SC_R_DC_1, 1, false, 0 }, 236*e2ad626fSUlf Hansson { "dc1-pll", IMX_SC_R_DC_1_PLL_0, 2, true, 0 }, 237*e2ad626fSUlf Hansson { "dc1-video", IMX_SC_R_DC_1_VIDEO0, 2, true, 0 }, 238*e2ad626fSUlf Hansson 239*e2ad626fSUlf Hansson /* CM40 SS */ 240*e2ad626fSUlf Hansson { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 }, 241*e2ad626fSUlf Hansson { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 }, 242*e2ad626fSUlf Hansson { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0}, 243*e2ad626fSUlf Hansson { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0}, 244*e2ad626fSUlf Hansson { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0}, 245*e2ad626fSUlf Hansson 246*e2ad626fSUlf Hansson /* CM41 SS */ 247*e2ad626fSUlf Hansson { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, 248*e2ad626fSUlf Hansson { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, 249*e2ad626fSUlf Hansson { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0}, 250*e2ad626fSUlf Hansson { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0}, 251*e2ad626fSUlf Hansson { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0}, 252*e2ad626fSUlf Hansson 253*e2ad626fSUlf Hansson /* CM41 SS */ 254*e2ad626fSUlf Hansson { "cm41_i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, 255*e2ad626fSUlf Hansson { "cm41_intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, 256*e2ad626fSUlf Hansson 257*e2ad626fSUlf Hansson /* DB SS */ 258*e2ad626fSUlf Hansson { "perf", IMX_SC_R_PERF, 1, false, 0}, 259*e2ad626fSUlf Hansson 260*e2ad626fSUlf Hansson /* IMAGE SS */ 261*e2ad626fSUlf Hansson { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 }, 262*e2ad626fSUlf Hansson { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 }, 263*e2ad626fSUlf Hansson { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 }, 264*e2ad626fSUlf Hansson { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 }, 265*e2ad626fSUlf Hansson 266*e2ad626fSUlf Hansson /* SECO SS */ 267*e2ad626fSUlf Hansson { "seco_mu", IMX_SC_R_SECO_MU_2, 3, true, 2}, 268*e2ad626fSUlf Hansson 269*e2ad626fSUlf Hansson /* V2X SS */ 270*e2ad626fSUlf Hansson { "v2x_mu", IMX_SC_R_V2X_MU_0, 2, true, 0}, 271*e2ad626fSUlf Hansson { "v2x_mu", IMX_SC_R_V2X_MU_2, 1, true, 2}, 272*e2ad626fSUlf Hansson { "v2x_mu", IMX_SC_R_V2X_MU_3, 2, true, 3}, 273*e2ad626fSUlf Hansson { "img-pdma", IMX_SC_R_ISI_CH0, 8, true, 0 }, 274*e2ad626fSUlf Hansson { "img-csi0", IMX_SC_R_CSI_0, 1, false, 0 }, 275*e2ad626fSUlf Hansson { "img-csi0-i2c0", IMX_SC_R_CSI_0_I2C_0, 1, false, 0 }, 276*e2ad626fSUlf Hansson { "img-csi0-pwm0", IMX_SC_R_CSI_0_PWM_0, 1, false, 0 }, 277*e2ad626fSUlf Hansson { "img-csi1", IMX_SC_R_CSI_1, 1, false, 0 }, 278*e2ad626fSUlf Hansson { "img-csi1-i2c0", IMX_SC_R_CSI_1_I2C_0, 1, false, 0 }, 279*e2ad626fSUlf Hansson { "img-csi1-pwm0", IMX_SC_R_CSI_1_PWM_0, 1, false, 0 }, 280*e2ad626fSUlf Hansson { "img-parallel", IMX_SC_R_PI_0, 1, false, 0 }, 281*e2ad626fSUlf Hansson { "img-parallel-i2c0", IMX_SC_R_PI_0_I2C_0, 1, false, 0 }, 282*e2ad626fSUlf Hansson { "img-parallel-pwm0", IMX_SC_R_PI_0_PWM_0, 2, true, 0 }, 283*e2ad626fSUlf Hansson { "img-parallel-pll", IMX_SC_R_PI_0_PLL, 1, false, 0 }, 284*e2ad626fSUlf Hansson 285*e2ad626fSUlf Hansson /* HDMI TX SS */ 286*e2ad626fSUlf Hansson { "hdmi-tx", IMX_SC_R_HDMI, 1, false, 0}, 287*e2ad626fSUlf Hansson { "hdmi-tx-i2s", IMX_SC_R_HDMI_I2S, 1, false, 0}, 288*e2ad626fSUlf Hansson { "hdmi-tx-i2c0", IMX_SC_R_HDMI_I2C_0, 1, false, 0}, 289*e2ad626fSUlf Hansson { "hdmi-tx-pll0", IMX_SC_R_HDMI_PLL_0, 1, false, 0}, 290*e2ad626fSUlf Hansson { "hdmi-tx-pll1", IMX_SC_R_HDMI_PLL_1, 1, false, 0}, 291*e2ad626fSUlf Hansson 292*e2ad626fSUlf Hansson /* HDMI RX SS */ 293*e2ad626fSUlf Hansson { "hdmi-rx", IMX_SC_R_HDMI_RX, 1, false, 0}, 294*e2ad626fSUlf Hansson { "hdmi-rx-pwm", IMX_SC_R_HDMI_RX_PWM_0, 1, false, 0}, 295*e2ad626fSUlf Hansson { "hdmi-rx-i2c0", IMX_SC_R_HDMI_RX_I2C_0, 1, false, 0}, 296*e2ad626fSUlf Hansson { "hdmi-rx-bypass", IMX_SC_R_HDMI_RX_BYPASS, 1, false, 0}, 297*e2ad626fSUlf Hansson 298*e2ad626fSUlf Hansson /* SECURITY SS */ 299*e2ad626fSUlf Hansson { "sec-jr", IMX_SC_R_CAAM_JR2, 2, true, 2}, 300*e2ad626fSUlf Hansson 301*e2ad626fSUlf Hansson /* BOARD SS */ 302*e2ad626fSUlf Hansson { "board", IMX_SC_R_BOARD_R0, 8, true, 0}, 303*e2ad626fSUlf Hansson }; 304*e2ad626fSUlf Hansson 305*e2ad626fSUlf Hansson static const struct imx_sc_pd_soc imx8qxp_scu_pd = { 306*e2ad626fSUlf Hansson .pd_ranges = imx8qxp_scu_pd_ranges, 307*e2ad626fSUlf Hansson .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), 308*e2ad626fSUlf Hansson }; 309*e2ad626fSUlf Hansson 310*e2ad626fSUlf Hansson static struct imx_sc_ipc *pm_ipc_handle; 311*e2ad626fSUlf Hansson 312*e2ad626fSUlf Hansson static inline struct imx_sc_pm_domain * 313*e2ad626fSUlf Hansson to_imx_sc_pd(struct generic_pm_domain *genpd) 314*e2ad626fSUlf Hansson { 315*e2ad626fSUlf Hansson return container_of(genpd, struct imx_sc_pm_domain, pd); 316*e2ad626fSUlf Hansson } 317*e2ad626fSUlf Hansson 318*e2ad626fSUlf Hansson static void imx_sc_pd_get_console_rsrc(void) 319*e2ad626fSUlf Hansson { 320*e2ad626fSUlf Hansson struct of_phandle_args specs; 321*e2ad626fSUlf Hansson int ret; 322*e2ad626fSUlf Hansson 323*e2ad626fSUlf Hansson if (!of_stdout) 324*e2ad626fSUlf Hansson return; 325*e2ad626fSUlf Hansson 326*e2ad626fSUlf Hansson ret = of_parse_phandle_with_args(of_stdout, "power-domains", 327*e2ad626fSUlf Hansson "#power-domain-cells", 328*e2ad626fSUlf Hansson 0, &specs); 329*e2ad626fSUlf Hansson if (ret) 330*e2ad626fSUlf Hansson return; 331*e2ad626fSUlf Hansson 332*e2ad626fSUlf Hansson imx_con_rsrc = specs.args[0]; 333*e2ad626fSUlf Hansson } 334*e2ad626fSUlf Hansson 335*e2ad626fSUlf Hansson static int imx_sc_get_pd_power(struct device *dev, u32 rsrc) 336*e2ad626fSUlf Hansson { 337*e2ad626fSUlf Hansson struct imx_sc_msg_req_get_resource_power_mode msg; 338*e2ad626fSUlf Hansson struct imx_sc_rpc_msg *hdr = &msg.hdr; 339*e2ad626fSUlf Hansson int ret; 340*e2ad626fSUlf Hansson 341*e2ad626fSUlf Hansson hdr->ver = IMX_SC_RPC_VERSION; 342*e2ad626fSUlf Hansson hdr->svc = IMX_SC_RPC_SVC_PM; 343*e2ad626fSUlf Hansson hdr->func = IMX_SC_PM_FUNC_GET_RESOURCE_POWER_MODE; 344*e2ad626fSUlf Hansson hdr->size = 2; 345*e2ad626fSUlf Hansson 346*e2ad626fSUlf Hansson msg.data.req.resource = rsrc; 347*e2ad626fSUlf Hansson 348*e2ad626fSUlf Hansson ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); 349*e2ad626fSUlf Hansson if (ret) 350*e2ad626fSUlf Hansson dev_err(dev, "failed to get power resource %d mode, ret %d\n", 351*e2ad626fSUlf Hansson rsrc, ret); 352*e2ad626fSUlf Hansson 353*e2ad626fSUlf Hansson return msg.data.resp.mode; 354*e2ad626fSUlf Hansson } 355*e2ad626fSUlf Hansson 356*e2ad626fSUlf Hansson static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) 357*e2ad626fSUlf Hansson { 358*e2ad626fSUlf Hansson struct imx_sc_msg_req_set_resource_power_mode msg; 359*e2ad626fSUlf Hansson struct imx_sc_rpc_msg *hdr = &msg.hdr; 360*e2ad626fSUlf Hansson struct imx_sc_pm_domain *pd; 361*e2ad626fSUlf Hansson int ret; 362*e2ad626fSUlf Hansson 363*e2ad626fSUlf Hansson pd = to_imx_sc_pd(domain); 364*e2ad626fSUlf Hansson 365*e2ad626fSUlf Hansson hdr->ver = IMX_SC_RPC_VERSION; 366*e2ad626fSUlf Hansson hdr->svc = IMX_SC_RPC_SVC_PM; 367*e2ad626fSUlf Hansson hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; 368*e2ad626fSUlf Hansson hdr->size = 2; 369*e2ad626fSUlf Hansson 370*e2ad626fSUlf Hansson msg.resource = pd->rsrc; 371*e2ad626fSUlf Hansson msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; 372*e2ad626fSUlf Hansson 373*e2ad626fSUlf Hansson /* keep uart console power on for no_console_suspend */ 374*e2ad626fSUlf Hansson if (imx_con_rsrc == pd->rsrc && !console_suspend_enabled && !power_on) 375*e2ad626fSUlf Hansson return -EBUSY; 376*e2ad626fSUlf Hansson 377*e2ad626fSUlf Hansson ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); 378*e2ad626fSUlf Hansson if (ret) 379*e2ad626fSUlf Hansson dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", 380*e2ad626fSUlf Hansson power_on ? "up" : "off", pd->rsrc, ret); 381*e2ad626fSUlf Hansson 382*e2ad626fSUlf Hansson return ret; 383*e2ad626fSUlf Hansson } 384*e2ad626fSUlf Hansson 385*e2ad626fSUlf Hansson static int imx_sc_pd_power_on(struct generic_pm_domain *domain) 386*e2ad626fSUlf Hansson { 387*e2ad626fSUlf Hansson return imx_sc_pd_power(domain, true); 388*e2ad626fSUlf Hansson } 389*e2ad626fSUlf Hansson 390*e2ad626fSUlf Hansson static int imx_sc_pd_power_off(struct generic_pm_domain *domain) 391*e2ad626fSUlf Hansson { 392*e2ad626fSUlf Hansson return imx_sc_pd_power(domain, false); 393*e2ad626fSUlf Hansson } 394*e2ad626fSUlf Hansson 395*e2ad626fSUlf Hansson static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, 396*e2ad626fSUlf Hansson void *data) 397*e2ad626fSUlf Hansson { 398*e2ad626fSUlf Hansson struct generic_pm_domain *domain = ERR_PTR(-ENOENT); 399*e2ad626fSUlf Hansson struct genpd_onecell_data *pd_data = data; 400*e2ad626fSUlf Hansson unsigned int i; 401*e2ad626fSUlf Hansson 402*e2ad626fSUlf Hansson for (i = 0; i < pd_data->num_domains; i++) { 403*e2ad626fSUlf Hansson struct imx_sc_pm_domain *sc_pd; 404*e2ad626fSUlf Hansson 405*e2ad626fSUlf Hansson sc_pd = to_imx_sc_pd(pd_data->domains[i]); 406*e2ad626fSUlf Hansson if (sc_pd->rsrc == spec->args[0]) { 407*e2ad626fSUlf Hansson domain = &sc_pd->pd; 408*e2ad626fSUlf Hansson break; 409*e2ad626fSUlf Hansson } 410*e2ad626fSUlf Hansson } 411*e2ad626fSUlf Hansson 412*e2ad626fSUlf Hansson return domain; 413*e2ad626fSUlf Hansson } 414*e2ad626fSUlf Hansson 415*e2ad626fSUlf Hansson static struct imx_sc_pm_domain * 416*e2ad626fSUlf Hansson imx_scu_add_pm_domain(struct device *dev, int idx, 417*e2ad626fSUlf Hansson const struct imx_sc_pd_range *pd_ranges) 418*e2ad626fSUlf Hansson { 419*e2ad626fSUlf Hansson struct imx_sc_pm_domain *sc_pd; 420*e2ad626fSUlf Hansson bool is_off; 421*e2ad626fSUlf Hansson int mode, ret; 422*e2ad626fSUlf Hansson 423*e2ad626fSUlf Hansson if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) 424*e2ad626fSUlf Hansson return NULL; 425*e2ad626fSUlf Hansson 426*e2ad626fSUlf Hansson sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); 427*e2ad626fSUlf Hansson if (!sc_pd) 428*e2ad626fSUlf Hansson return ERR_PTR(-ENOMEM); 429*e2ad626fSUlf Hansson 430*e2ad626fSUlf Hansson sc_pd->rsrc = pd_ranges->rsrc + idx; 431*e2ad626fSUlf Hansson sc_pd->pd.power_off = imx_sc_pd_power_off; 432*e2ad626fSUlf Hansson sc_pd->pd.power_on = imx_sc_pd_power_on; 433*e2ad626fSUlf Hansson 434*e2ad626fSUlf Hansson if (pd_ranges->postfix) 435*e2ad626fSUlf Hansson snprintf(sc_pd->name, sizeof(sc_pd->name), 436*e2ad626fSUlf Hansson "%s%i", pd_ranges->name, pd_ranges->start_from + idx); 437*e2ad626fSUlf Hansson else 438*e2ad626fSUlf Hansson snprintf(sc_pd->name, sizeof(sc_pd->name), 439*e2ad626fSUlf Hansson "%s", pd_ranges->name); 440*e2ad626fSUlf Hansson 441*e2ad626fSUlf Hansson sc_pd->pd.name = sc_pd->name; 442*e2ad626fSUlf Hansson if (imx_con_rsrc == sc_pd->rsrc) 443*e2ad626fSUlf Hansson sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON; 444*e2ad626fSUlf Hansson 445*e2ad626fSUlf Hansson mode = imx_sc_get_pd_power(dev, pd_ranges->rsrc + idx); 446*e2ad626fSUlf Hansson if (mode == IMX_SC_PM_PW_MODE_ON) 447*e2ad626fSUlf Hansson is_off = false; 448*e2ad626fSUlf Hansson else 449*e2ad626fSUlf Hansson is_off = true; 450*e2ad626fSUlf Hansson 451*e2ad626fSUlf Hansson dev_dbg(dev, "%s : %s\n", sc_pd->name, imx_sc_pm_mode[mode]); 452*e2ad626fSUlf Hansson 453*e2ad626fSUlf Hansson if (sc_pd->rsrc >= IMX_SC_R_LAST) { 454*e2ad626fSUlf Hansson dev_warn(dev, "invalid pd %s rsrc id %d found", 455*e2ad626fSUlf Hansson sc_pd->name, sc_pd->rsrc); 456*e2ad626fSUlf Hansson 457*e2ad626fSUlf Hansson devm_kfree(dev, sc_pd); 458*e2ad626fSUlf Hansson return NULL; 459*e2ad626fSUlf Hansson } 460*e2ad626fSUlf Hansson 461*e2ad626fSUlf Hansson ret = pm_genpd_init(&sc_pd->pd, NULL, is_off); 462*e2ad626fSUlf Hansson if (ret) { 463*e2ad626fSUlf Hansson dev_warn(dev, "failed to init pd %s rsrc id %d", 464*e2ad626fSUlf Hansson sc_pd->name, sc_pd->rsrc); 465*e2ad626fSUlf Hansson devm_kfree(dev, sc_pd); 466*e2ad626fSUlf Hansson return NULL; 467*e2ad626fSUlf Hansson } 468*e2ad626fSUlf Hansson 469*e2ad626fSUlf Hansson return sc_pd; 470*e2ad626fSUlf Hansson } 471*e2ad626fSUlf Hansson 472*e2ad626fSUlf Hansson static int imx_scu_init_pm_domains(struct device *dev, 473*e2ad626fSUlf Hansson const struct imx_sc_pd_soc *pd_soc) 474*e2ad626fSUlf Hansson { 475*e2ad626fSUlf Hansson const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; 476*e2ad626fSUlf Hansson struct generic_pm_domain **domains; 477*e2ad626fSUlf Hansson struct genpd_onecell_data *pd_data; 478*e2ad626fSUlf Hansson struct imx_sc_pm_domain *sc_pd; 479*e2ad626fSUlf Hansson u32 count = 0; 480*e2ad626fSUlf Hansson int i, j; 481*e2ad626fSUlf Hansson 482*e2ad626fSUlf Hansson for (i = 0; i < pd_soc->num_ranges; i++) 483*e2ad626fSUlf Hansson count += pd_ranges[i].num; 484*e2ad626fSUlf Hansson 485*e2ad626fSUlf Hansson domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); 486*e2ad626fSUlf Hansson if (!domains) 487*e2ad626fSUlf Hansson return -ENOMEM; 488*e2ad626fSUlf Hansson 489*e2ad626fSUlf Hansson pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); 490*e2ad626fSUlf Hansson if (!pd_data) 491*e2ad626fSUlf Hansson return -ENOMEM; 492*e2ad626fSUlf Hansson 493*e2ad626fSUlf Hansson count = 0; 494*e2ad626fSUlf Hansson for (i = 0; i < pd_soc->num_ranges; i++) { 495*e2ad626fSUlf Hansson for (j = 0; j < pd_ranges[i].num; j++) { 496*e2ad626fSUlf Hansson sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); 497*e2ad626fSUlf Hansson if (IS_ERR_OR_NULL(sc_pd)) 498*e2ad626fSUlf Hansson continue; 499*e2ad626fSUlf Hansson 500*e2ad626fSUlf Hansson domains[count++] = &sc_pd->pd; 501*e2ad626fSUlf Hansson dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); 502*e2ad626fSUlf Hansson } 503*e2ad626fSUlf Hansson } 504*e2ad626fSUlf Hansson 505*e2ad626fSUlf Hansson pd_data->domains = domains; 506*e2ad626fSUlf Hansson pd_data->num_domains = count; 507*e2ad626fSUlf Hansson pd_data->xlate = imx_scu_pd_xlate; 508*e2ad626fSUlf Hansson 509*e2ad626fSUlf Hansson of_genpd_add_provider_onecell(dev->of_node, pd_data); 510*e2ad626fSUlf Hansson 511*e2ad626fSUlf Hansson return 0; 512*e2ad626fSUlf Hansson } 513*e2ad626fSUlf Hansson 514*e2ad626fSUlf Hansson static int imx_sc_pd_probe(struct platform_device *pdev) 515*e2ad626fSUlf Hansson { 516*e2ad626fSUlf Hansson const struct imx_sc_pd_soc *pd_soc; 517*e2ad626fSUlf Hansson int ret; 518*e2ad626fSUlf Hansson 519*e2ad626fSUlf Hansson ret = imx_scu_get_handle(&pm_ipc_handle); 520*e2ad626fSUlf Hansson if (ret) 521*e2ad626fSUlf Hansson return ret; 522*e2ad626fSUlf Hansson 523*e2ad626fSUlf Hansson pd_soc = of_device_get_match_data(&pdev->dev); 524*e2ad626fSUlf Hansson if (!pd_soc) 525*e2ad626fSUlf Hansson return -ENODEV; 526*e2ad626fSUlf Hansson 527*e2ad626fSUlf Hansson imx_sc_pd_get_console_rsrc(); 528*e2ad626fSUlf Hansson 529*e2ad626fSUlf Hansson return imx_scu_init_pm_domains(&pdev->dev, pd_soc); 530*e2ad626fSUlf Hansson } 531*e2ad626fSUlf Hansson 532*e2ad626fSUlf Hansson static const struct of_device_id imx_sc_pd_match[] = { 533*e2ad626fSUlf Hansson { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, 534*e2ad626fSUlf Hansson { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, 535*e2ad626fSUlf Hansson { /* sentinel */ } 536*e2ad626fSUlf Hansson }; 537*e2ad626fSUlf Hansson 538*e2ad626fSUlf Hansson static struct platform_driver imx_sc_pd_driver = { 539*e2ad626fSUlf Hansson .driver = { 540*e2ad626fSUlf Hansson .name = "imx-scu-pd", 541*e2ad626fSUlf Hansson .of_match_table = imx_sc_pd_match, 542*e2ad626fSUlf Hansson .suppress_bind_attrs = true, 543*e2ad626fSUlf Hansson }, 544*e2ad626fSUlf Hansson .probe = imx_sc_pd_probe, 545*e2ad626fSUlf Hansson }; 546*e2ad626fSUlf Hansson builtin_platform_driver(imx_sc_pd_driver); 547*e2ad626fSUlf Hansson 548*e2ad626fSUlf Hansson MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); 549*e2ad626fSUlf Hansson MODULE_DESCRIPTION("IMX SCU Power Domain driver"); 550*e2ad626fSUlf Hansson MODULE_LICENSE("GPL v2"); 551