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