^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0+
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2016 Freescale Semiconductor, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright 2017-2018 NXP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Dong Aisheng <aisheng.dong@nxp.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Implementation of the SCU based Power Domains
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * NOTE: a better implementation suggested by Ulf Hansson is using a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * single global power domain and implement the ->attach|detach_dev()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * callback for the genpd and use the regular of_genpd_add_provider_simple().
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * From within the ->attach_dev(), we could get the OF node for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) * the device that is being attached and then parse the power-domain
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) * cell containing the "resource id" and store that in the per device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) * struct generic_pm_domain_data (we have void pointer there for
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) * storing these kind of things).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * Additionally, we need to implement the ->stop() and ->start()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * callbacks of genpd, which is where you "power on/off" devices,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * rather than using the above ->power_on|off() callbacks.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * However, there're two known issues:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * 1. The ->attach_dev() of power domain infrastructure still does
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * not support multi domains case as the struct device *dev passed
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * in is a virtual PD device, it does not help for parsing the real
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * device resource id from device tree, so it's unware of which
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * real sub power domain of device should be attached.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * The framework needs some proper extension to support multi power
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * domain cases.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) * 2. It also breaks most of current drivers as the driver probe sequence
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * behavior changed if removing ->power_on|off() callback and use
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * ->start() and ->stop() instead. genpd_dev_pm_attach will only power
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) * up the domain and attach device, but will not call .start() which
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) * relies on device runtime pm. That means the device power is still
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * not up before running driver probe function. For SCU enabled
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) * platforms, all device drivers accessing registers/clock without power
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) * domain enabled will trigger a HW access error. That means we need fix
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) * most drivers probe sequence with proper runtime pm.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) * In summary, we need fix above two issue before being able to switch to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) * the "single global power domain" way.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) #include <dt-bindings/firmware/imx/rsrc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) #include <linux/firmware/imx/sci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) #include <linux/firmware/imx/svc/rm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) #include <linux/of.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) #include <linux/pm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) #include <linux/pm_domain.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) /* SCU Power Mode Protocol definition */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) struct imx_sc_msg_req_set_resource_power_mode {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) struct imx_sc_rpc_msg hdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) u16 resource;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) u8 mode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) } __packed __aligned(4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) #define IMX_SCU_PD_NAME_SIZE 20
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) struct imx_sc_pm_domain {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) struct generic_pm_domain pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) char name[IMX_SCU_PD_NAME_SIZE];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) u32 rsrc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) struct imx_sc_pd_range {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) u32 rsrc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) u8 num;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) /* add domain index */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) bool postfix;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) u8 start_from;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) struct imx_sc_pd_soc {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) const struct imx_sc_pd_range *pd_ranges;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) u8 num_ranges;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) /* LSIO SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) { "pwm", IMX_SC_R_PWM_0, 8, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) { "gpt", IMX_SC_R_GPT_0, 5, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) { "kpp", IMX_SC_R_KPP, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) /* CONN SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) { "usb", IMX_SC_R_USB_0, 2, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) { "usb2", IMX_SC_R_USB_2, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) { "enet", IMX_SC_R_ENET_0, 2, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) { "nand", IMX_SC_R_NAND, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) { "mlb", IMX_SC_R_MLB_0, 1, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /* AUDIO SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) { "sai", IMX_SC_R_SAI_0, 3, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) { "sai3", IMX_SC_R_SAI_3, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) { "sai4", IMX_SC_R_SAI_4, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) { "sai5", IMX_SC_R_SAI_5, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) { "sai6", IMX_SC_R_SAI_6, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) { "sai7", IMX_SC_R_SAI_7, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) { "amix", IMX_SC_R_AMIX, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) { "dsp", IMX_SC_R_DSP, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) /* DMA SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) { "can", IMX_SC_R_CAN_0, 3, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) { "ftm", IMX_SC_R_FTM_0, 2, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) { "adc", IMX_SC_R_ADC_0, 1, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) { "lcd", IMX_SC_R_LCD_0, 1, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) { "lpuart", IMX_SC_R_UART_0, 4, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) /* VPU SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) { "vpu", IMX_SC_R_VPU, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) /* GPU SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) /* HSIO SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) /* MIPI SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) /* LVDS SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) /* DC SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) { "dc0", IMX_SC_R_DC_0, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) /* CM40 SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) /* CM41 SS */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) static const struct imx_sc_pd_soc imx8qxp_scu_pd = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) .pd_ranges = imx8qxp_scu_pd_ranges,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) static struct imx_sc_ipc *pm_ipc_handle;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) static inline struct imx_sc_pm_domain *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) to_imx_sc_pd(struct generic_pm_domain *genpd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) return container_of(genpd, struct imx_sc_pm_domain, pd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) struct imx_sc_msg_req_set_resource_power_mode msg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) struct imx_sc_rpc_msg *hdr = &msg.hdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) struct imx_sc_pm_domain *pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) pd = to_imx_sc_pd(domain);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) hdr->ver = IMX_SC_RPC_VERSION;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) hdr->svc = IMX_SC_RPC_SVC_PM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) hdr->size = 2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) msg.resource = pd->rsrc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) dev_err(&domain->dev, "failed to power %s resource %d ret %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) power_on ? "up" : "off", pd->rsrc, ret);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) static int imx_sc_pd_power_on(struct generic_pm_domain *domain)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) return imx_sc_pd_power(domain, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) static int imx_sc_pd_power_off(struct generic_pm_domain *domain)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) return imx_sc_pd_power(domain, false);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) void *data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) struct genpd_onecell_data *pd_data = data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) unsigned int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) for (i = 0; i < pd_data->num_domains; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) struct imx_sc_pm_domain *sc_pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) sc_pd = to_imx_sc_pd(pd_data->domains[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) if (sc_pd->rsrc == spec->args[0]) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) domain = &sc_pd->pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) return domain;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) static struct imx_sc_pm_domain *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) imx_scu_add_pm_domain(struct device *dev, int idx,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) const struct imx_sc_pd_range *pd_ranges)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) struct imx_sc_pm_domain *sc_pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) if (!sc_pd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) return ERR_PTR(-ENOMEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) sc_pd->rsrc = pd_ranges->rsrc + idx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) sc_pd->pd.power_off = imx_sc_pd_power_off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) sc_pd->pd.power_on = imx_sc_pd_power_on;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) if (pd_ranges->postfix)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) snprintf(sc_pd->name, sizeof(sc_pd->name),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) "%s%i", pd_ranges->name, pd_ranges->start_from + idx);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) snprintf(sc_pd->name, sizeof(sc_pd->name),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) "%s", pd_ranges->name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) sc_pd->pd.name = sc_pd->name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) if (sc_pd->rsrc >= IMX_SC_R_LAST) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) dev_warn(dev, "invalid pd %s rsrc id %d found",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) sc_pd->name, sc_pd->rsrc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) devm_kfree(dev, sc_pd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) ret = pm_genpd_init(&sc_pd->pd, NULL, true);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) dev_warn(dev, "failed to init pd %s rsrc id %d",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) sc_pd->name, sc_pd->rsrc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) devm_kfree(dev, sc_pd);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) return sc_pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) static int imx_scu_init_pm_domains(struct device *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) const struct imx_sc_pd_soc *pd_soc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) struct generic_pm_domain **domains;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) struct genpd_onecell_data *pd_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) struct imx_sc_pm_domain *sc_pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) u32 count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) int i, j;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) for (i = 0; i < pd_soc->num_ranges; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) count += pd_ranges[i].num;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) if (!domains)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) if (!pd_data)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) for (i = 0; i < pd_soc->num_ranges; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) for (j = 0; j < pd_ranges[i].num; j++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) if (IS_ERR_OR_NULL(sc_pd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) domains[count++] = &sc_pd->pd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) pd_data->domains = domains;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333) pd_data->num_domains = count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) pd_data->xlate = imx_scu_pd_xlate;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) of_genpd_add_provider_onecell(dev->of_node, pd_data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) static int imx_sc_pd_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) const struct imx_sc_pd_soc *pd_soc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) ret = imx_scu_get_handle(&pm_ipc_handle);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) pd_soc = of_device_get_match_data(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) if (!pd_soc)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) return imx_scu_init_pm_domains(&pdev->dev, pd_soc);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) static const struct of_device_id imx_sc_pd_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) { /* sentinel */ }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) static struct platform_driver imx_sc_pd_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 364) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 365) .name = "imx-scu-pd",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 366) .of_match_table = imx_sc_pd_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 367) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 368) .probe = imx_sc_pd_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 369) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 370) builtin_platform_driver(imx_sc_pd_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 371)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 372) MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 373) MODULE_DESCRIPTION("IMX SCU Power Domain driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 374) MODULE_LICENSE("GPL v2");