162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci * Copyright 2017-2018 NXP 562306a36Sopenharmony_ci * Dong Aisheng <aisheng.dong@nxp.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Implementation of the SCU based Power Domains 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * NOTE: a better implementation suggested by Ulf Hansson is using a 1062306a36Sopenharmony_ci * single global power domain and implement the ->attach|detach_dev() 1162306a36Sopenharmony_ci * callback for the genpd and use the regular of_genpd_add_provider_simple(). 1262306a36Sopenharmony_ci * From within the ->attach_dev(), we could get the OF node for 1362306a36Sopenharmony_ci * the device that is being attached and then parse the power-domain 1462306a36Sopenharmony_ci * cell containing the "resource id" and store that in the per device 1562306a36Sopenharmony_ci * struct generic_pm_domain_data (we have void pointer there for 1662306a36Sopenharmony_ci * storing these kind of things). 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * Additionally, we need to implement the ->stop() and ->start() 1962306a36Sopenharmony_ci * callbacks of genpd, which is where you "power on/off" devices, 2062306a36Sopenharmony_ci * rather than using the above ->power_on|off() callbacks. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * However, there're two known issues: 2362306a36Sopenharmony_ci * 1. The ->attach_dev() of power domain infrastructure still does 2462306a36Sopenharmony_ci * not support multi domains case as the struct device *dev passed 2562306a36Sopenharmony_ci * in is a virtual PD device, it does not help for parsing the real 2662306a36Sopenharmony_ci * device resource id from device tree, so it's unware of which 2762306a36Sopenharmony_ci * real sub power domain of device should be attached. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * The framework needs some proper extension to support multi power 3062306a36Sopenharmony_ci * domain cases. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * Update: Genpd assigns the ->of_node for the virtual device before it 3362306a36Sopenharmony_ci * invokes ->attach_dev() callback, hence parsing for device resources via 3462306a36Sopenharmony_ci * DT should work fine. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * 2. It also breaks most of current drivers as the driver probe sequence 3762306a36Sopenharmony_ci * behavior changed if removing ->power_on|off() callback and use 3862306a36Sopenharmony_ci * ->start() and ->stop() instead. genpd_dev_pm_attach will only power 3962306a36Sopenharmony_ci * up the domain and attach device, but will not call .start() which 4062306a36Sopenharmony_ci * relies on device runtime pm. That means the device power is still 4162306a36Sopenharmony_ci * not up before running driver probe function. For SCU enabled 4262306a36Sopenharmony_ci * platforms, all device drivers accessing registers/clock without power 4362306a36Sopenharmony_ci * domain enabled will trigger a HW access error. That means we need fix 4462306a36Sopenharmony_ci * most drivers probe sequence with proper runtime pm. 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * Update: Runtime PM support isn't necessary. Instead, this can easily be 4762306a36Sopenharmony_ci * fixed in drivers by adding a call to dev_pm_domain_start() during probe. 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * In summary, the second part needs to be addressed via minor updates to the 5062306a36Sopenharmony_ci * relevant drivers, before the "single global power domain" model can be used. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#include <dt-bindings/firmware/imx/rsrc.h> 5562306a36Sopenharmony_ci#include <linux/console.h> 5662306a36Sopenharmony_ci#include <linux/firmware/imx/sci.h> 5762306a36Sopenharmony_ci#include <linux/firmware/imx/svc/rm.h> 5862306a36Sopenharmony_ci#include <linux/io.h> 5962306a36Sopenharmony_ci#include <linux/module.h> 6062306a36Sopenharmony_ci#include <linux/of.h> 6162306a36Sopenharmony_ci#include <linux/of_address.h> 6262306a36Sopenharmony_ci#include <linux/of_platform.h> 6362306a36Sopenharmony_ci#include <linux/platform_device.h> 6462306a36Sopenharmony_ci#include <linux/pm.h> 6562306a36Sopenharmony_ci#include <linux/pm_domain.h> 6662306a36Sopenharmony_ci#include <linux/slab.h> 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* SCU Power Mode Protocol definition */ 6962306a36Sopenharmony_cistruct imx_sc_msg_req_set_resource_power_mode { 7062306a36Sopenharmony_ci struct imx_sc_rpc_msg hdr; 7162306a36Sopenharmony_ci u16 resource; 7262306a36Sopenharmony_ci u8 mode; 7362306a36Sopenharmony_ci} __packed __aligned(4); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistruct req_get_resource_mode { 7662306a36Sopenharmony_ci u16 resource; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct resp_get_resource_mode { 8062306a36Sopenharmony_ci u8 mode; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct imx_sc_msg_req_get_resource_power_mode { 8462306a36Sopenharmony_ci struct imx_sc_rpc_msg hdr; 8562306a36Sopenharmony_ci union { 8662306a36Sopenharmony_ci struct req_get_resource_mode req; 8762306a36Sopenharmony_ci struct resp_get_resource_mode resp; 8862306a36Sopenharmony_ci } data; 8962306a36Sopenharmony_ci} __packed __aligned(4); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci#define IMX_SCU_PD_NAME_SIZE 20 9262306a36Sopenharmony_cistruct imx_sc_pm_domain { 9362306a36Sopenharmony_ci struct generic_pm_domain pd; 9462306a36Sopenharmony_ci char name[IMX_SCU_PD_NAME_SIZE]; 9562306a36Sopenharmony_ci u32 rsrc; 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistruct imx_sc_pd_range { 9962306a36Sopenharmony_ci char *name; 10062306a36Sopenharmony_ci u32 rsrc; 10162306a36Sopenharmony_ci u8 num; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* add domain index */ 10462306a36Sopenharmony_ci bool postfix; 10562306a36Sopenharmony_ci u8 start_from; 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistruct imx_sc_pd_soc { 10962306a36Sopenharmony_ci const struct imx_sc_pd_range *pd_ranges; 11062306a36Sopenharmony_ci u8 num_ranges; 11162306a36Sopenharmony_ci}; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int imx_con_rsrc; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Align with the IMX_SC_PM_PW_MODE_[OFF,STBY,LP,ON] macros */ 11662306a36Sopenharmony_cistatic const char * const imx_sc_pm_mode[] = { 11762306a36Sopenharmony_ci "IMX_SC_PM_PW_MODE_OFF", 11862306a36Sopenharmony_ci "IMX_SC_PM_PW_MODE_STBY", 11962306a36Sopenharmony_ci "IMX_SC_PM_PW_MODE_LP", 12062306a36Sopenharmony_ci "IMX_SC_PM_PW_MODE_ON" 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { 12462306a36Sopenharmony_ci /* LSIO SS */ 12562306a36Sopenharmony_ci { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, 12662306a36Sopenharmony_ci { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, 12762306a36Sopenharmony_ci { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, 12862306a36Sopenharmony_ci { "kpp", IMX_SC_R_KPP, 1, false, 0 }, 12962306a36Sopenharmony_ci { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, 13062306a36Sopenharmony_ci { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, 13162306a36Sopenharmony_ci { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 }, 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* CONN SS */ 13462306a36Sopenharmony_ci { "usb", IMX_SC_R_USB_0, 2, true, 0 }, 13562306a36Sopenharmony_ci { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, 13662306a36Sopenharmony_ci { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0}, 13762306a36Sopenharmony_ci { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, 13862306a36Sopenharmony_ci { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, 13962306a36Sopenharmony_ci { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, 14062306a36Sopenharmony_ci { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, 14162306a36Sopenharmony_ci { "nand", IMX_SC_R_NAND, 1, false, 0 }, 14262306a36Sopenharmony_ci { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* AUDIO SS */ 14562306a36Sopenharmony_ci { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, 14662306a36Sopenharmony_ci { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, 14762306a36Sopenharmony_ci { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, 14862306a36Sopenharmony_ci { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 }, 14962306a36Sopenharmony_ci { "mclk-out-0", IMX_SC_R_MCLK_OUT_0, 1, false, 0 }, 15062306a36Sopenharmony_ci { "mclk-out-1", IMX_SC_R_MCLK_OUT_1, 1, false, 0 }, 15162306a36Sopenharmony_ci { "dma0-ch", IMX_SC_R_DMA_0_CH0, 32, true, 0 }, 15262306a36Sopenharmony_ci { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, 15362306a36Sopenharmony_ci { "dma2-ch-0", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, 15462306a36Sopenharmony_ci { "dma2-ch-1", IMX_SC_R_DMA_2_CH5, 27, true, 0 }, 15562306a36Sopenharmony_ci { "dma3-ch", IMX_SC_R_DMA_3_CH0, 32, true, 0 }, 15662306a36Sopenharmony_ci { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, 15762306a36Sopenharmony_ci { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, 15862306a36Sopenharmony_ci { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, 15962306a36Sopenharmony_ci { "esai1", IMX_SC_R_ESAI_1, 1, false, 0 }, 16062306a36Sopenharmony_ci { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, 16162306a36Sopenharmony_ci { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, 16262306a36Sopenharmony_ci { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, 16362306a36Sopenharmony_ci { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, 16462306a36Sopenharmony_ci { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, 16562306a36Sopenharmony_ci { "sai5", IMX_SC_R_SAI_5, 1, false, 0 }, 16662306a36Sopenharmony_ci { "sai6", IMX_SC_R_SAI_6, 1, false, 0 }, 16762306a36Sopenharmony_ci { "sai7", IMX_SC_R_SAI_7, 1, false, 0 }, 16862306a36Sopenharmony_ci { "amix", IMX_SC_R_AMIX, 1, false, 0 }, 16962306a36Sopenharmony_ci { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, 17062306a36Sopenharmony_ci { "dsp", IMX_SC_R_DSP, 1, false, 0 }, 17162306a36Sopenharmony_ci { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* DMA SS */ 17462306a36Sopenharmony_ci { "can", IMX_SC_R_CAN_0, 3, true, 0 }, 17562306a36Sopenharmony_ci { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, 17662306a36Sopenharmony_ci { "lpi2c", IMX_SC_R_I2C_0, 5, true, 0 }, 17762306a36Sopenharmony_ci { "adc", IMX_SC_R_ADC_0, 2, true, 0 }, 17862306a36Sopenharmony_ci { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, 17962306a36Sopenharmony_ci { "lcd-pll", IMX_SC_R_ELCDIF_PLL, 1, true, 0 }, 18062306a36Sopenharmony_ci { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, 18162306a36Sopenharmony_ci { "lpuart", IMX_SC_R_UART_0, 5, true, 0 }, 18262306a36Sopenharmony_ci { "sim", IMX_SC_R_EMVSIM_0, 2, true, 0 }, 18362306a36Sopenharmony_ci { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, 18462306a36Sopenharmony_ci { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* VPU SS */ 18762306a36Sopenharmony_ci { "vpu", IMX_SC_R_VPU, 1, false, 0 }, 18862306a36Sopenharmony_ci { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, 18962306a36Sopenharmony_ci { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, 19062306a36Sopenharmony_ci { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, 19162306a36Sopenharmony_ci { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 }, 19262306a36Sopenharmony_ci { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 }, 19362306a36Sopenharmony_ci { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 }, 19462306a36Sopenharmony_ci { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 }, 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* GPU SS */ 19762306a36Sopenharmony_ci { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, 19862306a36Sopenharmony_ci { "gpu1-pid", IMX_SC_R_GPU_1_PID0, 4, true, 0 }, 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* HSIO SS */ 20262306a36Sopenharmony_ci { "pcie-a", IMX_SC_R_PCIE_A, 1, false, 0 }, 20362306a36Sopenharmony_ci { "serdes-0", IMX_SC_R_SERDES_0, 1, false, 0 }, 20462306a36Sopenharmony_ci { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, 20562306a36Sopenharmony_ci { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, 20662306a36Sopenharmony_ci { "sata-0", IMX_SC_R_SATA_0, 1, false, 0 }, 20762306a36Sopenharmony_ci { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* MIPI SS */ 21062306a36Sopenharmony_ci { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, 21162306a36Sopenharmony_ci { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, 21262306a36Sopenharmony_ci { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 }, 21562306a36Sopenharmony_ci { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 }, 21662306a36Sopenharmony_ci { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 }, 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* LVDS SS */ 21962306a36Sopenharmony_ci { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, 22062306a36Sopenharmony_ci { "lvds0-pwm", IMX_SC_R_LVDS_0_PWM_0, 1, false, 0 }, 22162306a36Sopenharmony_ci { "lvds0-lpi2c", IMX_SC_R_LVDS_0_I2C_0, 2, true, 0 }, 22262306a36Sopenharmony_ci { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 }, 22362306a36Sopenharmony_ci { "lvds1-pwm", IMX_SC_R_LVDS_1_PWM_0, 1, false, 0 }, 22462306a36Sopenharmony_ci { "lvds1-lpi2c", IMX_SC_R_LVDS_1_I2C_0, 2, true, 0 }, 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci { "mipi1", IMX_SC_R_MIPI_1, 1, 0 }, 22762306a36Sopenharmony_ci { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, 0 }, 22862306a36Sopenharmony_ci { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, 1 }, 22962306a36Sopenharmony_ci { "lvds1", IMX_SC_R_LVDS_1, 1, 0 }, 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* DC SS */ 23262306a36Sopenharmony_ci { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, 23362306a36Sopenharmony_ci { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, 23462306a36Sopenharmony_ci { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 }, 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci { "dc1", IMX_SC_R_DC_1, 1, false, 0 }, 23762306a36Sopenharmony_ci { "dc1-pll", IMX_SC_R_DC_1_PLL_0, 2, true, 0 }, 23862306a36Sopenharmony_ci { "dc1-video", IMX_SC_R_DC_1_VIDEO0, 2, true, 0 }, 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* CM40 SS */ 24162306a36Sopenharmony_ci { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 }, 24262306a36Sopenharmony_ci { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 }, 24362306a36Sopenharmony_ci { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0}, 24462306a36Sopenharmony_ci { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0}, 24562306a36Sopenharmony_ci { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0}, 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* CM41 SS */ 24862306a36Sopenharmony_ci { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, 24962306a36Sopenharmony_ci { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, 25062306a36Sopenharmony_ci { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0}, 25162306a36Sopenharmony_ci { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0}, 25262306a36Sopenharmony_ci { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0}, 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* CM41 SS */ 25562306a36Sopenharmony_ci { "cm41_i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, 25662306a36Sopenharmony_ci { "cm41_intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* DB SS */ 25962306a36Sopenharmony_ci { "perf", IMX_SC_R_PERF, 1, false, 0}, 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* IMAGE SS */ 26262306a36Sopenharmony_ci { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 }, 26362306a36Sopenharmony_ci { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 }, 26462306a36Sopenharmony_ci { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 }, 26562306a36Sopenharmony_ci { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 }, 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* SECO SS */ 26862306a36Sopenharmony_ci { "seco_mu", IMX_SC_R_SECO_MU_2, 3, true, 2}, 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* V2X SS */ 27162306a36Sopenharmony_ci { "v2x_mu", IMX_SC_R_V2X_MU_0, 2, true, 0}, 27262306a36Sopenharmony_ci { "v2x_mu", IMX_SC_R_V2X_MU_2, 1, true, 2}, 27362306a36Sopenharmony_ci { "v2x_mu", IMX_SC_R_V2X_MU_3, 2, true, 3}, 27462306a36Sopenharmony_ci { "img-pdma", IMX_SC_R_ISI_CH0, 8, true, 0 }, 27562306a36Sopenharmony_ci { "img-csi0", IMX_SC_R_CSI_0, 1, false, 0 }, 27662306a36Sopenharmony_ci { "img-csi0-i2c0", IMX_SC_R_CSI_0_I2C_0, 1, false, 0 }, 27762306a36Sopenharmony_ci { "img-csi0-pwm0", IMX_SC_R_CSI_0_PWM_0, 1, false, 0 }, 27862306a36Sopenharmony_ci { "img-csi1", IMX_SC_R_CSI_1, 1, false, 0 }, 27962306a36Sopenharmony_ci { "img-csi1-i2c0", IMX_SC_R_CSI_1_I2C_0, 1, false, 0 }, 28062306a36Sopenharmony_ci { "img-csi1-pwm0", IMX_SC_R_CSI_1_PWM_0, 1, false, 0 }, 28162306a36Sopenharmony_ci { "img-parallel", IMX_SC_R_PI_0, 1, false, 0 }, 28262306a36Sopenharmony_ci { "img-parallel-i2c0", IMX_SC_R_PI_0_I2C_0, 1, false, 0 }, 28362306a36Sopenharmony_ci { "img-parallel-pwm0", IMX_SC_R_PI_0_PWM_0, 2, true, 0 }, 28462306a36Sopenharmony_ci { "img-parallel-pll", IMX_SC_R_PI_0_PLL, 1, false, 0 }, 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* HDMI TX SS */ 28762306a36Sopenharmony_ci { "hdmi-tx", IMX_SC_R_HDMI, 1, false, 0}, 28862306a36Sopenharmony_ci { "hdmi-tx-i2s", IMX_SC_R_HDMI_I2S, 1, false, 0}, 28962306a36Sopenharmony_ci { "hdmi-tx-i2c0", IMX_SC_R_HDMI_I2C_0, 1, false, 0}, 29062306a36Sopenharmony_ci { "hdmi-tx-pll0", IMX_SC_R_HDMI_PLL_0, 1, false, 0}, 29162306a36Sopenharmony_ci { "hdmi-tx-pll1", IMX_SC_R_HDMI_PLL_1, 1, false, 0}, 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* HDMI RX SS */ 29462306a36Sopenharmony_ci { "hdmi-rx", IMX_SC_R_HDMI_RX, 1, false, 0}, 29562306a36Sopenharmony_ci { "hdmi-rx-pwm", IMX_SC_R_HDMI_RX_PWM_0, 1, false, 0}, 29662306a36Sopenharmony_ci { "hdmi-rx-i2c0", IMX_SC_R_HDMI_RX_I2C_0, 1, false, 0}, 29762306a36Sopenharmony_ci { "hdmi-rx-bypass", IMX_SC_R_HDMI_RX_BYPASS, 1, false, 0}, 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* SECURITY SS */ 30062306a36Sopenharmony_ci { "sec-jr", IMX_SC_R_CAAM_JR2, 2, true, 2}, 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* BOARD SS */ 30362306a36Sopenharmony_ci { "board", IMX_SC_R_BOARD_R0, 8, true, 0}, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic const struct imx_sc_pd_soc imx8qxp_scu_pd = { 30762306a36Sopenharmony_ci .pd_ranges = imx8qxp_scu_pd_ranges, 30862306a36Sopenharmony_ci .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), 30962306a36Sopenharmony_ci}; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic struct imx_sc_ipc *pm_ipc_handle; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic inline struct imx_sc_pm_domain * 31462306a36Sopenharmony_cito_imx_sc_pd(struct generic_pm_domain *genpd) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci return container_of(genpd, struct imx_sc_pm_domain, pd); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void imx_sc_pd_get_console_rsrc(void) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct of_phandle_args specs; 32262306a36Sopenharmony_ci int ret; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!of_stdout) 32562306a36Sopenharmony_ci return; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci ret = of_parse_phandle_with_args(of_stdout, "power-domains", 32862306a36Sopenharmony_ci "#power-domain-cells", 32962306a36Sopenharmony_ci 0, &specs); 33062306a36Sopenharmony_ci if (ret) 33162306a36Sopenharmony_ci return; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci imx_con_rsrc = specs.args[0]; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int imx_sc_get_pd_power(struct device *dev, u32 rsrc) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct imx_sc_msg_req_get_resource_power_mode msg; 33962306a36Sopenharmony_ci struct imx_sc_rpc_msg *hdr = &msg.hdr; 34062306a36Sopenharmony_ci int ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci hdr->ver = IMX_SC_RPC_VERSION; 34362306a36Sopenharmony_ci hdr->svc = IMX_SC_RPC_SVC_PM; 34462306a36Sopenharmony_ci hdr->func = IMX_SC_PM_FUNC_GET_RESOURCE_POWER_MODE; 34562306a36Sopenharmony_ci hdr->size = 2; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci msg.data.req.resource = rsrc; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); 35062306a36Sopenharmony_ci if (ret) 35162306a36Sopenharmony_ci dev_err(dev, "failed to get power resource %d mode, ret %d\n", 35262306a36Sopenharmony_ci rsrc, ret); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return msg.data.resp.mode; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct imx_sc_msg_req_set_resource_power_mode msg; 36062306a36Sopenharmony_ci struct imx_sc_rpc_msg *hdr = &msg.hdr; 36162306a36Sopenharmony_ci struct imx_sc_pm_domain *pd; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci pd = to_imx_sc_pd(domain); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci hdr->ver = IMX_SC_RPC_VERSION; 36762306a36Sopenharmony_ci hdr->svc = IMX_SC_RPC_SVC_PM; 36862306a36Sopenharmony_ci hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; 36962306a36Sopenharmony_ci hdr->size = 2; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci msg.resource = pd->rsrc; 37262306a36Sopenharmony_ci msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* keep uart console power on for no_console_suspend */ 37562306a36Sopenharmony_ci if (imx_con_rsrc == pd->rsrc && !console_suspend_enabled && !power_on) 37662306a36Sopenharmony_ci return -EBUSY; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); 37962306a36Sopenharmony_ci if (ret) 38062306a36Sopenharmony_ci dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", 38162306a36Sopenharmony_ci power_on ? "up" : "off", pd->rsrc, ret); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int imx_sc_pd_power_on(struct generic_pm_domain *domain) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci return imx_sc_pd_power(domain, true); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int imx_sc_pd_power_off(struct generic_pm_domain *domain) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci return imx_sc_pd_power(domain, false); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_cistatic struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, 39762306a36Sopenharmony_ci void *data) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct generic_pm_domain *domain = ERR_PTR(-ENOENT); 40062306a36Sopenharmony_ci struct genpd_onecell_data *pd_data = data; 40162306a36Sopenharmony_ci unsigned int i; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci for (i = 0; i < pd_data->num_domains; i++) { 40462306a36Sopenharmony_ci struct imx_sc_pm_domain *sc_pd; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci sc_pd = to_imx_sc_pd(pd_data->domains[i]); 40762306a36Sopenharmony_ci if (sc_pd->rsrc == spec->args[0]) { 40862306a36Sopenharmony_ci domain = &sc_pd->pd; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci return domain; 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_cistatic struct imx_sc_pm_domain * 41762306a36Sopenharmony_ciimx_scu_add_pm_domain(struct device *dev, int idx, 41862306a36Sopenharmony_ci const struct imx_sc_pd_range *pd_ranges) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct imx_sc_pm_domain *sc_pd; 42162306a36Sopenharmony_ci bool is_off; 42262306a36Sopenharmony_ci int mode, ret; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) 42562306a36Sopenharmony_ci return NULL; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); 42862306a36Sopenharmony_ci if (!sc_pd) 42962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci sc_pd->rsrc = pd_ranges->rsrc + idx; 43262306a36Sopenharmony_ci sc_pd->pd.power_off = imx_sc_pd_power_off; 43362306a36Sopenharmony_ci sc_pd->pd.power_on = imx_sc_pd_power_on; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (pd_ranges->postfix) 43662306a36Sopenharmony_ci snprintf(sc_pd->name, sizeof(sc_pd->name), 43762306a36Sopenharmony_ci "%s%i", pd_ranges->name, pd_ranges->start_from + idx); 43862306a36Sopenharmony_ci else 43962306a36Sopenharmony_ci snprintf(sc_pd->name, sizeof(sc_pd->name), 44062306a36Sopenharmony_ci "%s", pd_ranges->name); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci sc_pd->pd.name = sc_pd->name; 44362306a36Sopenharmony_ci if (imx_con_rsrc == sc_pd->rsrc) 44462306a36Sopenharmony_ci sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci mode = imx_sc_get_pd_power(dev, pd_ranges->rsrc + idx); 44762306a36Sopenharmony_ci if (mode == IMX_SC_PM_PW_MODE_ON) 44862306a36Sopenharmony_ci is_off = false; 44962306a36Sopenharmony_ci else 45062306a36Sopenharmony_ci is_off = true; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci dev_dbg(dev, "%s : %s\n", sc_pd->name, imx_sc_pm_mode[mode]); 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (sc_pd->rsrc >= IMX_SC_R_LAST) { 45562306a36Sopenharmony_ci dev_warn(dev, "invalid pd %s rsrc id %d found", 45662306a36Sopenharmony_ci sc_pd->name, sc_pd->rsrc); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci devm_kfree(dev, sc_pd); 45962306a36Sopenharmony_ci return NULL; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ret = pm_genpd_init(&sc_pd->pd, NULL, is_off); 46362306a36Sopenharmony_ci if (ret) { 46462306a36Sopenharmony_ci dev_warn(dev, "failed to init pd %s rsrc id %d", 46562306a36Sopenharmony_ci sc_pd->name, sc_pd->rsrc); 46662306a36Sopenharmony_ci devm_kfree(dev, sc_pd); 46762306a36Sopenharmony_ci return NULL; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return sc_pd; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int imx_scu_init_pm_domains(struct device *dev, 47462306a36Sopenharmony_ci const struct imx_sc_pd_soc *pd_soc) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; 47762306a36Sopenharmony_ci struct generic_pm_domain **domains; 47862306a36Sopenharmony_ci struct genpd_onecell_data *pd_data; 47962306a36Sopenharmony_ci struct imx_sc_pm_domain *sc_pd; 48062306a36Sopenharmony_ci u32 count = 0; 48162306a36Sopenharmony_ci int i, j; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci for (i = 0; i < pd_soc->num_ranges; i++) 48462306a36Sopenharmony_ci count += pd_ranges[i].num; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); 48762306a36Sopenharmony_ci if (!domains) 48862306a36Sopenharmony_ci return -ENOMEM; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); 49162306a36Sopenharmony_ci if (!pd_data) 49262306a36Sopenharmony_ci return -ENOMEM; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci count = 0; 49562306a36Sopenharmony_ci for (i = 0; i < pd_soc->num_ranges; i++) { 49662306a36Sopenharmony_ci for (j = 0; j < pd_ranges[i].num; j++) { 49762306a36Sopenharmony_ci sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); 49862306a36Sopenharmony_ci if (IS_ERR_OR_NULL(sc_pd)) 49962306a36Sopenharmony_ci continue; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci domains[count++] = &sc_pd->pd; 50262306a36Sopenharmony_ci dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci pd_data->domains = domains; 50762306a36Sopenharmony_ci pd_data->num_domains = count; 50862306a36Sopenharmony_ci pd_data->xlate = imx_scu_pd_xlate; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci of_genpd_add_provider_onecell(dev->of_node, pd_data); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return 0; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int imx_sc_pd_probe(struct platform_device *pdev) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci const struct imx_sc_pd_soc *pd_soc; 51862306a36Sopenharmony_ci int ret; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ret = imx_scu_get_handle(&pm_ipc_handle); 52162306a36Sopenharmony_ci if (ret) 52262306a36Sopenharmony_ci return ret; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci pd_soc = of_device_get_match_data(&pdev->dev); 52562306a36Sopenharmony_ci if (!pd_soc) 52662306a36Sopenharmony_ci return -ENODEV; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci imx_sc_pd_get_console_rsrc(); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return imx_scu_init_pm_domains(&pdev->dev, pd_soc); 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic const struct of_device_id imx_sc_pd_match[] = { 53462306a36Sopenharmony_ci { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, 53562306a36Sopenharmony_ci { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, 53662306a36Sopenharmony_ci { /* sentinel */ } 53762306a36Sopenharmony_ci}; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic struct platform_driver imx_sc_pd_driver = { 54062306a36Sopenharmony_ci .driver = { 54162306a36Sopenharmony_ci .name = "imx-scu-pd", 54262306a36Sopenharmony_ci .of_match_table = imx_sc_pd_match, 54362306a36Sopenharmony_ci .suppress_bind_attrs = true, 54462306a36Sopenharmony_ci }, 54562306a36Sopenharmony_ci .probe = imx_sc_pd_probe, 54662306a36Sopenharmony_ci}; 54762306a36Sopenharmony_cibuiltin_platform_driver(imx_sc_pd_driver); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ciMODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>"); 55062306a36Sopenharmony_ciMODULE_DESCRIPTION("IMX SCU Power Domain driver"); 55162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 552