From 0418592550c6a370b2b8a5cbebd53fb7dd63d837 Mon Sep 17 00:00:00 2001 From: Pankaj Patil Date: Thu, 11 Dec 2025 14:32:36 +0530 Subject: soc: qcom: llcc-qcom: Add support for Glymur Add system cache table(SCT) and configs for Glymur SoC Updated the list of usecase id's to enable additional clients for Glymur Reviewed-by: Konrad Dybcio Signed-off-by: Pankaj Patil Link: https://lore.kernel.org/r/20251211-glymur_llcc_enablement-v3-2-43457b354b0d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/llcc-qcom.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 0287f9182c4d..8243ab3a12a8 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -74,13 +74,17 @@ #define LLCC_CAMSRTIP 73 #define LLCC_CAMRTRF 74 #define LLCC_CAMSRTRF 75 +#define LLCC_OOBM_NS 81 +#define LLCC_OOBM_S 82 #define LLCC_VIDEO_APV 83 #define LLCC_COMPUTE1 87 #define LLCC_CPUSS_OPP 88 #define LLCC_CPUSSMPAM 89 +#define LLCC_VIDSC_VSP1 91 #define LLCC_CAM_IPE_STROV 92 #define LLCC_CAM_OFE_STROV 93 #define LLCC_CPUSS_HEU 94 +#define LLCC_PCIE_TCU 97 #define LLCC_MDM_PNG_FIXED 100 /** -- cgit v1.2.3 From a707eda330b932bcf698be9460e54e2f389e24b7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 15 Dec 2025 15:16:31 +0100 Subject: tee: Add some helpers to reduce boilerplate for tee client drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to platform drivers (and others) create dedicated register and unregister functions and a macro to simplify modules that only need to handle driver registration in their init and exit handlers. Reviewed-by: Sumit Garg Signed-off-by: Uwe Kleine-König Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 16 ++++++++++++++++ include/linux/tee_drv.h | 9 +++++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index d65d47cc154e..51379f7fc5d5 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1405,6 +1405,22 @@ const struct bus_type tee_bus_type = { }; EXPORT_SYMBOL_GPL(tee_bus_type); +int __tee_client_driver_register(struct tee_client_driver *tee_driver, + struct module *owner) +{ + tee_driver->driver.owner = owner; + tee_driver->driver.bus = &tee_bus_type; + + return driver_register(&tee_driver->driver); +} +EXPORT_SYMBOL_GPL(__tee_client_driver_register); + +void tee_client_driver_unregister(struct tee_client_driver *tee_driver) +{ + driver_unregister(&tee_driver->driver); +} +EXPORT_SYMBOL_GPL(tee_client_driver_unregister); + static int __init tee_init(void) { int rc; diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 88a6f9697c89..850c03b2cdea 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -322,4 +322,13 @@ struct tee_client_driver { #define to_tee_client_driver(d) \ container_of_const(d, struct tee_client_driver, driver) +#define tee_client_driver_register(drv) \ + __tee_client_driver_register(drv, THIS_MODULE) +int __tee_client_driver_register(struct tee_client_driver *, struct module *); +void tee_client_driver_unregister(struct tee_client_driver *); + +#define module_tee_client_driver(__tee_client_driver) \ + module_driver(__tee_client_driver, tee_client_driver_register, \ + tee_client_driver_unregister) + #endif /*__TEE_DRV_H*/ -- cgit v1.2.3 From 71a33465726e4125f5ef33705ce5bc7ec1bf5b1f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 15 Dec 2025 15:16:32 +0100 Subject: tee: Add probe, remove and shutdown bus callbacks to tee_client_driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a bus specific probe, remove and shutdown function. For now this only allows to get rid of a cast of the generic device to a tee_client device in the drivers and changes the remove prototype to return void---a non-zero return value is ignored anyhow. The objective is to get rid of users of struct device_driver callbacks .probe(), .remove() and .shutdown() to eventually remove these. Until all tee_client drivers are converted this results in a runtime warning about the drivers needing an update because there is a bus probe function and a driver probe function. Signed-off-by: Uwe Kleine-König Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/tee_drv.h | 3 +++ 2 files changed, 71 insertions(+) (limited to 'include') diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 51379f7fc5d5..a015730664c7 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1398,19 +1398,87 @@ static int tee_client_device_uevent(const struct device *dev, return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id); } +static int tee_client_device_probe(struct device *dev) +{ + struct tee_client_device *tcdev = to_tee_client_device(dev); + struct tee_client_driver *drv = to_tee_client_driver(dev->driver); + + if (drv->probe) + return drv->probe(tcdev); + else + return 0; +} + +static void tee_client_device_remove(struct device *dev) +{ + struct tee_client_device *tcdev = to_tee_client_device(dev); + struct tee_client_driver *drv = to_tee_client_driver(dev->driver); + + if (drv->remove) + drv->remove(tcdev); +} + +static void tee_client_device_shutdown(struct device *dev) +{ + struct tee_client_device *tcdev = to_tee_client_device(dev); + struct tee_client_driver *drv = to_tee_client_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(tcdev); +} + const struct bus_type tee_bus_type = { .name = "tee", .match = tee_client_device_match, .uevent = tee_client_device_uevent, + .probe = tee_client_device_probe, + .remove = tee_client_device_remove, + .shutdown = tee_client_device_shutdown, }; EXPORT_SYMBOL_GPL(tee_bus_type); +static int tee_client_device_probe_legacy(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + struct device_driver *driver = dev->driver; + + return driver->probe(dev); +} + +static void tee_client_device_remove_legacy(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + struct device_driver *driver = dev->driver; + + driver->remove(dev); +} + +static void tee_client_device_shutdown_legacy(struct tee_client_device *tcdev) +{ + struct device *dev = &tcdev->dev; + struct device_driver *driver = dev->driver; + + driver->shutdown(dev); +} + int __tee_client_driver_register(struct tee_client_driver *tee_driver, struct module *owner) { tee_driver->driver.owner = owner; tee_driver->driver.bus = &tee_bus_type; + /* + * Drivers that have callbacks set for tee_driver->driver need updating + * to use the callbacks in tee_driver instead. driver_register() warns + * about that, so no need to warn here, too. + */ + if (!tee_driver->probe && tee_driver->driver.probe) + tee_driver->probe = tee_client_device_probe_legacy; + if (!tee_driver->remove && tee_driver->driver.remove) + tee_driver->remove = tee_client_device_remove_legacy; + if (!tee_driver->shutdown && tee_driver->driver.probe) + tee_driver->shutdown = tee_client_device_shutdown_legacy; + return driver_register(&tee_driver->driver); } EXPORT_SYMBOL_GPL(__tee_client_driver_register); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 850c03b2cdea..e561a26f537a 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -315,6 +315,9 @@ struct tee_client_device { * @driver: driver structure */ struct tee_client_driver { + int (*probe)(struct tee_client_device *); + void (*remove)(struct tee_client_device *); + void (*shutdown)(struct tee_client_device *); const struct tee_client_device_id *id_table; struct device_driver driver; }; -- cgit v1.2.3 From 2b7a02c322922a37cc5fc15d055b794cc2193062 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Fri, 19 Dec 2025 21:52:08 +0800 Subject: clk: spacemit: prepare common ccu header In order to prepare adding clock driver for new K3 SoC, extract generic code to a separate common ccu header file, so they are not defined in K1 SoC-specific file, and then can be shared by all clock drivers. Link: https://lore.kernel.org/r/20260108-06-k1-clk-common-v4-1-badf635993d3@gentoo.org Reviewed-by: Alex Elder Signed-off-by: Yixun Lan --- include/soc/spacemit/ccu.h | 21 +++++++++++++++++++++ include/soc/spacemit/k1-syscon.h | 12 +----------- 2 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 include/soc/spacemit/ccu.h (limited to 'include') diff --git a/include/soc/spacemit/ccu.h b/include/soc/spacemit/ccu.h new file mode 100644 index 000000000000..84dcdecccc05 --- /dev/null +++ b/include/soc/spacemit/ccu.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_SPACEMIT_CCU_H__ +#define __SOC_SPACEMIT_CCU_H__ + +#include +#include + +/* Auxiliary device used to represent a CCU reset controller */ +struct spacemit_ccu_adev { + struct auxiliary_device adev; + struct regmap *regmap; +}; + +static inline struct spacemit_ccu_adev * +to_spacemit_ccu_adev(struct auxiliary_device *adev) +{ + return container_of(adev, struct spacemit_ccu_adev, adev); +} + +#endif /* __SOC_SPACEMIT_CCU_H__ */ diff --git a/include/soc/spacemit/k1-syscon.h b/include/soc/spacemit/k1-syscon.h index 354751562c55..0be7a2e8d445 100644 --- a/include/soc/spacemit/k1-syscon.h +++ b/include/soc/spacemit/k1-syscon.h @@ -5,17 +5,7 @@ #ifndef __SOC_K1_SYSCON_H__ #define __SOC_K1_SYSCON_H__ -/* Auxiliary device used to represent a CCU reset controller */ -struct spacemit_ccu_adev { - struct auxiliary_device adev; - struct regmap *regmap; -}; - -static inline struct spacemit_ccu_adev * -to_spacemit_ccu_adev(struct auxiliary_device *adev) -{ - return container_of(adev, struct spacemit_ccu_adev, adev); -} +#include "ccu.h" /* APBS register offset */ #define APBS_PLL1_SWCR1 0x100 -- cgit v1.2.3 From 091d19cc24018f2bd783e932fb2403cb7a2bdb3c Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sat, 20 Dec 2025 21:28:15 +0800 Subject: clk: spacemit: k3: extract common header Extracting common header file, which will be shared by clock and reset drivers. So will make it easy to add reset driver for K3 SoC later. Link: https://lore.kernel.org/r/20260108-k3-clk-v5-4-42a11b74ad58@gentoo.org Signed-off-by: Yixun Lan --- include/soc/spacemit/k3-syscon.h | 273 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 include/soc/spacemit/k3-syscon.h (limited to 'include') diff --git a/include/soc/spacemit/k3-syscon.h b/include/soc/spacemit/k3-syscon.h new file mode 100644 index 000000000000..0299bea065a0 --- /dev/null +++ b/include/soc/spacemit/k3-syscon.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* SpacemiT clock and reset driver definitions for the K3 SoC */ + +#ifndef __SOC_K3_SYSCON_H__ +#define __SOC_K3_SYSCON_H__ + +#include "ccu.h" + +/* APBS register offset */ +#define APBS_PLL1_SWCR1 0x100 +#define APBS_PLL1_SWCR2 0x104 +#define APBS_PLL1_SWCR3 0x108 +#define APBS_PLL2_SWCR1 0x118 +#define APBS_PLL2_SWCR2 0x11c +#define APBS_PLL2_SWCR3 0x120 +#define APBS_PLL3_SWCR1 0x124 +#define APBS_PLL3_SWCR2 0x128 +#define APBS_PLL3_SWCR3 0x12c +#define APBS_PLL4_SWCR1 0x130 +#define APBS_PLL4_SWCR2 0x134 +#define APBS_PLL4_SWCR3 0x138 +#define APBS_PLL5_SWCR1 0x13c +#define APBS_PLL5_SWCR2 0x140 +#define APBS_PLL5_SWCR3 0x144 +#define APBS_PLL6_SWCR1 0x148 +#define APBS_PLL6_SWCR2 0x14c +#define APBS_PLL6_SWCR3 0x150 +#define APBS_PLL7_SWCR1 0x158 +#define APBS_PLL7_SWCR2 0x15c +#define APBS_PLL7_SWCR3 0x160 +#define APBS_PLL8_SWCR1 0x180 +#define APBS_PLL8_SWCR2 0x184 +#define APBS_PLL8_SWCR3 0x188 + +/* MPMU register offset */ +#define MPMU_FCCR 0x0008 +#define MPMU_POSR 0x0010 +#define POSR_PLL1_LOCK BIT(24) +#define POSR_PLL2_LOCK BIT(25) +#define POSR_PLL3_LOCK BIT(26) +#define POSR_PLL4_LOCK BIT(27) +#define POSR_PLL5_LOCK BIT(28) +#define POSR_PLL6_LOCK BIT(29) +#define POSR_PLL7_LOCK BIT(30) +#define POSR_PLL8_LOCK BIT(31) +#define MPMU_SUCCR 0x0014 +#define MPMU_ISCCR 0x0044 +#define MPMU_WDTPCR 0x0200 +#define MPMU_RIPCCR 0x0210 +#define MPMU_ACGR 0x1024 +#define MPMU_APBCSCR 0x1050 +#define MPMU_SUCCR_1 0x10b0 + +#define MPMU_I2S0_SYSCLK 0x1100 +#define MPMU_I2S2_SYSCLK 0x1104 +#define MPMU_I2S3_SYSCLK 0x1108 +#define MPMU_I2S4_SYSCLK 0x110c +#define MPMU_I2S5_SYSCLK 0x1110 +#define MPMU_I2S_SYSCLK_CTRL 0x1114 + +/* APBC register offset */ +#define APBC_UART0_CLK_RST 0x00 +#define APBC_UART2_CLK_RST 0x04 +#define APBC_GPIO_CLK_RST 0x08 +#define APBC_PWM0_CLK_RST 0x0c +#define APBC_PWM1_CLK_RST 0x10 +#define APBC_PWM2_CLK_RST 0x14 +#define APBC_PWM3_CLK_RST 0x18 +#define APBC_TWSI8_CLK_RST 0x20 +#define APBC_UART3_CLK_RST 0x24 +#define APBC_RTC_CLK_RST 0x28 +#define APBC_TWSI0_CLK_RST 0x2c +#define APBC_TWSI1_CLK_RST 0x30 +#define APBC_TIMERS0_CLK_RST 0x34 +#define APBC_TWSI2_CLK_RST 0x38 +#define APBC_AIB_CLK_RST 0x3c +#define APBC_TWSI4_CLK_RST 0x40 +#define APBC_TIMERS1_CLK_RST 0x44 +#define APBC_ONEWIRE_CLK_RST 0x48 +#define APBC_TWSI5_CLK_RST 0x4c +#define APBC_DRO_CLK_RST 0x58 +#define APBC_IR0_CLK_RST 0x5c +#define APBC_IR1_CLK_RST 0x1c +#define APBC_TWSI6_CLK_RST 0x60 +#define APBC_COUNTER_CLK_SEL 0x64 +#define APBC_TSEN_CLK_RST 0x6c +#define APBC_UART4_CLK_RST 0x70 +#define APBC_UART5_CLK_RST 0x74 +#define APBC_UART6_CLK_RST 0x78 +#define APBC_SSP3_CLK_RST 0x7c +#define APBC_SSPA0_CLK_RST 0x80 +#define APBC_SSPA1_CLK_RST 0x84 +#define APBC_SSPA2_CLK_RST 0x88 +#define APBC_SSPA3_CLK_RST 0x8c +#define APBC_IPC_AP2AUD_CLK_RST 0x90 +#define APBC_UART7_CLK_RST 0x94 +#define APBC_UART8_CLK_RST 0x98 +#define APBC_UART9_CLK_RST 0x9c +#define APBC_CAN0_CLK_RST 0xa0 +#define APBC_CAN1_CLK_RST 0xa4 +#define APBC_PWM4_CLK_RST 0xa8 +#define APBC_PWM5_CLK_RST 0xac +#define APBC_PWM6_CLK_RST 0xb0 +#define APBC_PWM7_CLK_RST 0xb4 +#define APBC_PWM8_CLK_RST 0xb8 +#define APBC_PWM9_CLK_RST 0xbc +#define APBC_PWM10_CLK_RST 0xc0 +#define APBC_PWM11_CLK_RST 0xc4 +#define APBC_PWM12_CLK_RST 0xc8 +#define APBC_PWM13_CLK_RST 0xcc +#define APBC_PWM14_CLK_RST 0xd0 +#define APBC_PWM15_CLK_RST 0xd4 +#define APBC_PWM16_CLK_RST 0xd8 +#define APBC_PWM17_CLK_RST 0xdc +#define APBC_PWM18_CLK_RST 0xe0 +#define APBC_PWM19_CLK_RST 0xe4 +#define APBC_TIMERS2_CLK_RST 0x11c +#define APBC_TIMERS3_CLK_RST 0x120 +#define APBC_TIMERS4_CLK_RST 0x124 +#define APBC_TIMERS5_CLK_RST 0x128 +#define APBC_TIMERS6_CLK_RST 0x12c +#define APBC_TIMERS7_CLK_RST 0x130 + +#define APBC_CAN2_CLK_RST 0x148 +#define APBC_CAN3_CLK_RST 0x14c +#define APBC_CAN4_CLK_RST 0x150 +#define APBC_UART10_CLK_RST 0x154 +#define APBC_SSP0_CLK_RST 0x158 +#define APBC_SSP1_CLK_RST 0x15c +#define APBC_SSPA4_CLK_RST 0x160 +#define APBC_SSPA5_CLK_RST 0x164 + +/* APMU register offset */ +#define APMU_CSI_CCIC2_CLK_RES_CTRL 0x024 +#define APMU_ISP_CLK_RES_CTRL 0x038 +#define APMU_PMU_CLK_GATE_CTRL 0x040 +#define APMU_LCD_CLK_RES_CTRL1 0x044 +#define APMU_LCD_SPI_CLK_RES_CTRL 0x048 +#define APMU_LCD_CLK_RES_CTRL2 0x04c +#define APMU_CCIC_CLK_RES_CTRL 0x050 +#define APMU_SDH0_CLK_RES_CTRL 0x054 +#define APMU_SDH1_CLK_RES_CTRL 0x058 +#define APMU_USB_CLK_RES_CTRL 0x05c +#define APMU_QSPI_CLK_RES_CTRL 0x060 +#define APMU_DMA_CLK_RES_CTRL 0x064 +#define APMU_AES_CLK_RES_CTRL 0x068 +#define APMU_MCB_CLK_RES_CTRL 0x06c +#define APMU_VPU_CLK_RES_CTRL 0x0a4 +#define APMU_DTC_CLK_RES_CTRL 0x0ac +#define APMU_GPU_CLK_RES_CTRL 0x0cc +#define APMU_SDH2_CLK_RES_CTRL 0x0e0 +#define APMU_PMUA_MC_CTRL 0x0e8 +#define APMU_PMU_CC2_AP 0x100 +#define APMU_PMUA_EM_CLK_RES_CTRL 0x104 +#define APMU_UCIE_CTRL 0x11c +#define APMU_RCPU_CLK_RES_CTRL 0x14c +#define APMU_TOP_DCLK_CTRL 0x158 +#define APMU_LCD_EDP_CTRL 0x23c +#define APMU_UFS_CLK_RES_CTRL 0x268 +#define APMU_LCD_CLK_RES_CTRL3 0x26c +#define APMU_LCD_CLK_RES_CTRL4 0x270 +#define APMU_LCD_CLK_RES_CTRL5 0x274 +#define APMU_CCI550_CLK_CTRL 0x300 +#define APMU_ACLK_CLK_CTRL 0x388 +#define APMU_CPU_C0_CLK_CTRL 0x38C +#define APMU_CPU_C1_CLK_CTRL 0x390 +#define APMU_CPU_C2_CLK_CTRL 0x394 +#define APMU_CPU_C3_CLK_CTRL 0x208 +#define APMU_PCIE_CLK_RES_CTRL_A 0x1f0 +#define APMU_PCIE_CLK_RES_CTRL_B 0x1c8 +#define APMU_PCIE_CLK_RES_CTRL_C 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_D 0x1e0 +#define APMU_PCIE_CLK_RES_CTRL_E 0x1e8 +#define APMU_EMAC0_CLK_RES_CTRL 0x3e4 +#define APMU_EMAC1_CLK_RES_CTRL 0x3ec +#define APMU_EMAC2_CLK_RES_CTRL 0x248 +#define APMU_ESPI_CLK_RES_CTRL 0x240 +#define APMU_SNR_ISIM_VCLK_CTRL 0x3f8 + +/* DCIU register offsets */ +#define DCIU_DMASYS_CLK_EN 0x234 +#define DCIU_DMASYS_SDMA_CLK_EN 0x238 +#define DCIU_C2_TCM_PIPE_CLK 0x244 +#define DCIU_C3_TCM_PIPE_CLK 0x248 + +#define DCIU_DMASYS_S0_RSTN 0x204 +#define DCIU_DMASYS_S1_RSTN 0x208 +#define DCIU_DMASYS_A0_RSTN 0x20C +#define DCIU_DMASYS_A1_RSTN 0x210 +#define DCIU_DMASYS_A2_RSTN 0x214 +#define DCIU_DMASYS_A3_RSTN 0x218 +#define DCIU_DMASYS_A4_RSTN 0x21C +#define DCIU_DMASYS_A5_RSTN 0x220 +#define DCIU_DMASYS_A6_RSTN 0x224 +#define DCIU_DMASYS_A7_RSTN 0x228 +#define DCIU_DMASYS_RSTN 0x22C +#define DCIU_DMASYS_SDMA_RSTN 0x230 + +/* RCPU SYSCTRL register offsets */ +#define RCPU_CAN_CLK_RST 0x4c +#define RCPU_CAN1_CLK_RST 0xF0 +#define RCPU_CAN2_CLK_RST 0xF4 +#define RCPU_CAN3_CLK_RST 0xF8 +#define RCPU_CAN4_CLK_RST 0xFC +#define RCPU_IRC_CLK_RST 0x48 +#define RCPU_IRC1_CLK_RST 0xEC +#define RCPU_GMAC_CLK_RST 0xE4 +#define RCPU_ESPI_CLK_RST 0xDC +#define RCPU_AUDIO_I2S0_SYS_CLK_CTRL 0x70 +#define RCPU_AUDIO_I2S1_SYS_CLK_CTRL 0x44 + +/* RCPU UARTCTRL register offsets */ +#define RCPU1_UART0_CLK_RST 0x00 +#define RCPU1_UART1_CLK_RST 0x04 +#define RCPU1_UART2_CLK_RST 0x08 +#define RCPU1_UART3_CLK_RST 0x0c +#define RCPU1_UART4_CLK_RST 0x10 +#define RCPU1_UART5_CLK_RST 0x14 + +/* RCPU I2SCTRL register offsets */ +#define RCPU2_AUDIO_I2S0_TX_RX_CLK_CTRL 0x60 +#define RCPU2_AUDIO_I2S1_TX_RX_CLK_CTRL 0x64 +#define RCPU2_AUDIO_I2S2_TX_RX_CLK_CTRL 0x68 +#define RCPU2_AUDIO_I2S3_TX_RX_CLK_CTRL 0x6C + +#define RCPU2_AUDIO_I2S2_SYS_CLK_CTRL 0x44 +#define RCPU2_AUDIO_I2S3_SYS_CLK_CTRL 0x54 + +/* RCPU SPICTRL register offsets */ +#define RCPU3_SSP0_CLK_RST 0x00 +#define RCPU3_SSP1_CLK_RST 0x04 +#define RCPU3_PWR_SSP_CLK_RST 0x08 + +/* RCPU I2CCTRL register offsets */ +#define RCPU4_I2C0_CLK_RST 0x00 +#define RCPU4_I2C1_CLK_RST 0x04 +#define RCPU4_PWR_I2C_CLK_RST 0x08 + +/* RPMU register offsets */ +#define RCPU5_AON_PER_CLK_RST_CTRL 0x2C +#define RCPU5_TIMER1_CLK_RST 0x4C +#define RCPU5_TIMER2_CLK_RST 0x70 +#define RCPU5_TIMER3_CLK_RST 0x78 +#define RCPU5_TIMER4_CLK_RST 0x7C +#define RCPU5_GPIO_AND_EDGE_CLK_RST 0x74 +#define RCPU5_RCPU_BUS_CLK_CTRL 0xC0 +#define RCPU5_RT24_CORE0_CLK_CTRL 0xC4 +#define RCPU5_RT24_CORE1_CLK_CTRL 0xC8 +#define RCPU5_RT24_CORE0_SW_RESET 0xCC +#define RCPU5_RT24_CORE1_SW_RESET 0xD0 + +/* RCPU PWMCTRL register offsets */ +#define RCPU6_PWM0_CLK_RST 0x00 +#define RCPU6_PWM1_CLK_RST 0x04 +#define RCPU6_PWM2_CLK_RST 0x08 +#define RCPU6_PWM3_CLK_RST 0x0c +#define RCPU6_PWM4_CLK_RST 0x10 +#define RCPU6_PWM5_CLK_RST 0x14 +#define RCPU6_PWM6_CLK_RST 0x18 +#define RCPU6_PWM7_CLK_RST 0x1c +#define RCPU6_PWM8_CLK_RST 0x20 +#define RCPU6_PWM9_CLK_RST 0x24 + +/* APBC2 SEC register offsets */ +#define APBC2_UART1_CLK_RST 0x00 +#define APBC2_SSP2_CLK_RST 0x04 +#define APBC2_TWSI3_CLK_RST 0x08 +#define APBC2_RTC_CLK_RST 0x0c +#define APBC2_TIMERS_CLK_RST 0x10 +#define APBC2_GPIO_CLK_RST 0x1c + +#endif /* __SOC_K3_SYSCON_H__ */ -- cgit v1.2.3 From 69054348cc1c2d87acad90aec5e6e0d191012aff Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:51 +0530 Subject: firmware: qcom_scm: Rename peripheral as pas_id Peripheral and pas_id refers to unique id for a subsystem and used only when peripheral authentication service from secure world is utilized. Lets rename peripheral to pas_id to reflect closer to its meaning. Reviewed-by: Bryan O'Donoghue Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-3-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 30 +++++++++++++++--------------- include/linux/firmware/qcom/qcom_scm.h | 10 +++++----- 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 6461408c58a3..1e1057638e98 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -562,7 +562,7 @@ static void qcom_scm_set_download_mode(u32 dload_mode) * qcom_scm_pas_init_image() - Initialize peripheral authentication service * state machine for a given peripheral, using the * metadata - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * @metadata: pointer to memory containing ELF header, program header table * and optional blob of data used for authenticating the metadata * and the rest of the firmware @@ -575,7 +575,7 @@ static void qcom_scm_set_download_mode(u32 dload_mode) * track the metadata allocation, this needs to be released by invoking * qcom_scm_pas_metadata_release() by the caller. */ -int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, struct qcom_scm_pas_metadata *ctx) { dma_addr_t mdata_phys; @@ -585,7 +585,7 @@ int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_INIT_IMAGE, .arginfo = QCOM_SCM_ARGS(2, QCOM_SCM_VAL, QCOM_SCM_RW), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; @@ -656,20 +656,20 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); /** * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral * for firmware loading - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * @addr: start address of memory area to prepare * @size: size of the memory area to prepare * * Returns 0 on success. */ -int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size) +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_MEM_SETUP, .arginfo = QCOM_SCM_ARGS(3), - .args[0] = peripheral, + .args[0] = pas_id, .args[1] = addr, .args[2] = size, .owner = ARM_SMCCC_OWNER_SIP, @@ -697,18 +697,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); /** * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware * and reset the remote processor - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * * Return 0 on success. */ -int qcom_scm_pas_auth_and_reset(u32 peripheral) +int qcom_scm_pas_auth_and_reset(u32 pas_id) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_AUTH_AND_RESET, .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; @@ -733,18 +733,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); /** * qcom_scm_pas_shutdown() - Shut down the remote processor - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * * Returns 0 on success. */ -int qcom_scm_pas_shutdown(u32 peripheral) +int qcom_scm_pas_shutdown(u32 pas_id) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_SHUTDOWN, .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; @@ -770,18 +770,18 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); /** * qcom_scm_pas_supported() - Check if the peripheral authentication service is * available for the given peripherial - * @peripheral: peripheral id + * @pas_id: peripheral authentication service id * * Returns true if PAS is supported for this peripheral, otherwise false. */ -bool qcom_scm_pas_supported(u32 peripheral) +bool qcom_scm_pas_supported(u32 pas_id) { int ret; struct qcom_scm_desc desc = { .svc = QCOM_SCM_SVC_PIL, .cmd = QCOM_SCM_PIL_PAS_IS_SUPPORTED, .arginfo = QCOM_SCM_ARGS(1), - .args[0] = peripheral, + .args[0] = pas_id, .owner = ARM_SMCCC_OWNER_SIP, }; struct qcom_scm_res res; diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index a55ca771286b..a13f703b16cd 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -72,13 +72,13 @@ struct qcom_scm_pas_metadata { ssize_t size; }; -int qcom_scm_pas_init_image(u32 peripheral, const void *metadata, size_t size, +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, struct qcom_scm_pas_metadata *ctx); void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); -int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); -int qcom_scm_pas_auth_and_reset(u32 peripheral); -int qcom_scm_pas_shutdown(u32 peripheral); -bool qcom_scm_pas_supported(u32 peripheral); +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); +int qcom_scm_pas_auth_and_reset(u32 pas_id); +int qcom_scm_pas_shutdown(u32 pas_id); +bool qcom_scm_pas_supported(u32 pas_id); int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); -- cgit v1.2.3 From ccb7bde5f7cc794dee0cd66fd451cb0e0715712d Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:52 +0530 Subject: firmware: qcom_scm: Introduce PAS context allocator helper function When the Peripheral Authentication Service (PAS) method runs on a SoC where Linux operates at EL2 (i.e., without the Gunyah hypervisor), the reset sequences are handled by TrustZone. In such cases, Linux must perform additional steps before invoking PAS SMC calls, such as creating a SHM bridge. Therefore, PAS SMC calls require awareness and handling of these additional steps when Linux runs at EL2. To support this, there is a need for a data structure that can be initialized prior to invoking any SMC or MDT functions. This structure allows those functions to determine whether they are operating in the presence or absence of the Gunyah hypervisor and behave accordingly. Currently, remoteproc and non-remoteproc subsystems use different variants of the MDT loader helper API, primarily due to differences in metadata context handling. Remoteproc subsystems retain the metadata context until authentication and reset are completed, while non-remoteproc subsystems (e.g., video, graphics, IPA, etc.) do not retain the metadata context and can free it within the qcom_scm_pas_init() call by passing a NULL context parameter and due to these differences, it is not possible to extend metadata context handling to support remoteproc and non remoteproc subsystem use PAS operations, when Linux operates at EL2. Add PAS context data structure allocator helper function. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-4-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/firmware/qcom/qcom_scm.h | 14 ++++++++++++++ 2 files changed, 48 insertions(+) (limited to 'include') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 1e1057638e98..5162c02f5f88 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -558,6 +558,40 @@ static void qcom_scm_set_download_mode(u32 dload_mode) dev_err(__scm->dev, "failed to set download mode: %d\n", ret); } +/** + * devm_qcom_scm_pas_context_alloc() - Allocate peripheral authentication service + * context for a given peripheral + * + * PAS context is device-resource managed, so the caller does not need + * to worry about freeing the context memory. + * + * @dev: PAS firmware device + * @pas_id: peripheral authentication service id + * @mem_phys: Subsystem reserve memory start address + * @mem_size: Subsystem reserve memory size + * + * Returns: The new PAS context, or ERR_PTR() on failure. + */ +struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size) +{ + struct qcom_scm_pas_context *ctx; + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return ERR_PTR(-ENOMEM); + + ctx->dev = dev; + ctx->pas_id = pas_id; + ctx->mem_phys = mem_phys; + ctx->mem_size = mem_size; + + return ctx; +} +EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); + /** * qcom_scm_pas_init_image() - Initialize peripheral authentication service * state machine for a given peripheral, using the diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index a13f703b16cd..5045f8fe876d 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -72,6 +72,20 @@ struct qcom_scm_pas_metadata { ssize_t size; }; +struct qcom_scm_pas_context { + struct device *dev; + u32 pas_id; + phys_addr_t mem_phys; + size_t mem_size; + void *ptr; + dma_addr_t phys; + ssize_t size; +}; + +struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size); int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, struct qcom_scm_pas_metadata *ctx); void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); -- cgit v1.2.3 From b13d8baf56016e7eec29395b52d18b91df081d48 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:53 +0530 Subject: remoteproc: pas: Replace metadata context with PAS context structure As a superset of the existing metadata context, the PAS context structure enables both remoteproc and non-remoteproc subsystems to better support scenarios where the SoC runs with or without the Gunyah hypervisor. To reflect this, relevant SCM and metadata functions are updated to incorporate PAS context awareness and remove metadata context data structure completely. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-5-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 8 +++---- drivers/remoteproc/qcom_q6v5_pas.c | 38 ++++++++++++++++++++++++---------- drivers/soc/qcom/mdt_loader.c | 4 ++-- include/linux/firmware/qcom/qcom_scm.h | 10 ++------- include/linux/soc/qcom/mdt_loader.h | 6 +++--- 5 files changed, 38 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 5162c02f5f88..4edd475ef848 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -601,7 +601,7 @@ EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); * and optional blob of data used for authenticating the metadata * and the rest of the firmware * @size: size of the metadata - * @ctx: optional metadata context + * @ctx: optional pas context * * Return: 0 on success. * @@ -610,7 +610,7 @@ EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); * qcom_scm_pas_metadata_release() by the caller. */ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, - struct qcom_scm_pas_metadata *ctx) + struct qcom_scm_pas_context *ctx) { dma_addr_t mdata_phys; void *mdata_buf; @@ -674,9 +674,9 @@ EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); /** * qcom_scm_pas_metadata_release() - release metadata context - * @ctx: metadata context + * @ctx: pas context */ -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx) +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) { if (!ctx->ptr) return; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 52680ac99589..bfcb65aed008 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -117,8 +117,8 @@ struct qcom_pas { struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; - struct qcom_scm_pas_metadata pas_metadata; - struct qcom_scm_pas_metadata dtb_pas_metadata; + struct qcom_scm_pas_context *pas_ctx; + struct qcom_scm_pas_context *dtb_pas_ctx; }; static void qcom_pas_segment_dump(struct rproc *rproc, @@ -211,9 +211,9 @@ static int qcom_pas_unprepare(struct rproc *rproc) * auth_and_reset() was successful, but in other cases clean it up * here. */ - qcom_scm_pas_metadata_release(&pas->pas_metadata); + qcom_scm_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); return 0; } @@ -241,7 +241,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, pas->dtb_pas_id, pas->dtb_mem_phys, - &pas->dtb_pas_metadata); + pas->dtb_pas_ctx); if (ret) goto release_dtb_firmware; @@ -255,7 +255,7 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) return 0; release_dtb_metadata: - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); release_dtb_firmware: release_firmware(pas->dtb_firmware); @@ -306,7 +306,7 @@ static int qcom_pas_start(struct rproc *rproc) } ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id, - pas->mem_phys, &pas->pas_metadata); + pas->mem_phys, pas->pas_ctx); if (ret) goto disable_px_supply; @@ -332,9 +332,9 @@ static int qcom_pas_start(struct rproc *rproc) goto release_pas_metadata; } - qcom_scm_pas_metadata_release(&pas->pas_metadata); + qcom_scm_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); /* firmware is used to pass reference from qcom_pas_start(), drop it now */ pas->firmware = NULL; @@ -342,9 +342,9 @@ static int qcom_pas_start(struct rproc *rproc) return 0; release_pas_metadata: - qcom_scm_pas_metadata_release(&pas->pas_metadata); + qcom_scm_pas_metadata_release(pas->pas_ctx); if (pas->dtb_pas_id) - qcom_scm_pas_metadata_release(&pas->dtb_pas_metadata); + qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); disable_px_supply: if (pas->px_supply) regulator_disable(pas->px_supply); @@ -760,6 +760,22 @@ static int qcom_pas_probe(struct platform_device *pdev) } qcom_add_ssr_subdev(rproc, &pas->ssr_subdev, desc->ssr_name); + + pas->pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->pas_id, + pas->mem_phys, pas->mem_size); + if (IS_ERR(pas->pas_ctx)) { + ret = PTR_ERR(pas->pas_ctx); + goto remove_ssr_sysmon; + } + + pas->dtb_pas_ctx = devm_qcom_scm_pas_context_alloc(pas->dev, pas->dtb_pas_id, + pas->dtb_mem_phys, + pas->dtb_mem_size); + if (IS_ERR(pas->dtb_pas_ctx)) { + ret = PTR_ERR(pas->dtb_pas_ctx); + goto remove_ssr_sysmon; + } + ret = rproc_add(rproc); if (ret) goto remove_ssr_sysmon; diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index c239107cb930..b125140100db 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -234,13 +234,13 @@ EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); * @fw_name: name of the firmware, for construction of segment file names * @pas_id: PAS identifier * @mem_phys: physical address of allocated memory region - * @ctx: PAS metadata context, to be released by caller + * @ctx: PAS context, ctx->metadata to be released by caller * * Returns 0 on success, negative errno otherwise. */ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *ctx) + struct qcom_scm_pas_context *ctx) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index 5045f8fe876d..ad69b51fe6fc 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -66,12 +66,6 @@ int qcom_scm_set_warm_boot_addr(void *entry); void qcom_scm_cpu_power_down(u32 flags); int qcom_scm_set_remote_state(u32 state, u32 id); -struct qcom_scm_pas_metadata { - void *ptr; - dma_addr_t phys; - ssize_t size; -}; - struct qcom_scm_pas_context { struct device *dev; u32 pas_id; @@ -87,8 +81,8 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, phys_addr_t mem_phys, size_t mem_size); int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, - struct qcom_scm_pas_metadata *ctx); -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_metadata *ctx); + struct qcom_scm_pas_context *ctx); +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx); int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); int qcom_scm_pas_auth_and_reset(u32 pas_id); int qcom_scm_pas_shutdown(u32 pas_id); diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 8ea8230579a2..07c278841816 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -10,14 +10,14 @@ struct device; struct firmware; -struct qcom_scm_pas_metadata; +struct qcom_scm_pas_context; #if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ssize_t qcom_mdt_get_size(const struct firmware *fw); int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *pas_metadata_ctx); + struct qcom_scm_pas_context *pas_ctx); int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, @@ -39,7 +39,7 @@ static inline ssize_t qcom_mdt_get_size(const struct firmware *fw) static inline int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *pas_metadata_ctx) + struct qcom_scm_pas_context *pas_ctx) { return -ENODEV; } -- cgit v1.2.3 From 8a4fcffde6c860c4e9164cf3530c9d97972781dc Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:54 +0530 Subject: soc: qcom: mdtloader: Add PAS context aware qcom_mdt_pas_load() function Introduce a new PAS context-aware function, qcom_mdt_pas_load(), for remote processor drivers. This function utilizes the PAS context pointer returned from qcom_scm_pas_ctx_init() to perform firmware metadata verification and memory setup via SMC calls. The qcom_mdt_pas_load() and qcom_mdt_load() functions are largely similar, but the former is designed for clients using the PAS context-based data structure. Over time, all users of qcom_mdt_load() can be migrated to use qcom_mdt_pas_load() for consistency and improved abstraction. As the remoteproc PAS driver (qcom_q6v5_pas) has already adopted the PAS context-based approach, update it to use qcom_mdt_pas_load(). Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-6-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/remoteproc/qcom_q6v5_pas.c | 24 +++++------------------- drivers/soc/qcom/mdt_loader.c | 31 +++++++++++++++++++++++++++++++ include/linux/soc/qcom/mdt_loader.h | 10 ++++++++++ 3 files changed, 46 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index bfcb65aed008..f746d2f39a1d 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -239,15 +239,9 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) return ret; } - ret = qcom_mdt_pas_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, - pas->dtb_pas_id, pas->dtb_mem_phys, - pas->dtb_pas_ctx); - if (ret) - goto release_dtb_firmware; - - ret = qcom_mdt_load_no_init(pas->dev, pas->dtb_firmware, pas->dtb_firmware_name, - pas->dtb_mem_region, pas->dtb_mem_phys, - pas->dtb_mem_size, &pas->dtb_mem_reloc); + ret = qcom_mdt_pas_load(pas->dtb_pas_ctx, pas->dtb_firmware, + pas->dtb_firmware_name, pas->dtb_mem_region, + &pas->dtb_mem_reloc); if (ret) goto release_dtb_metadata; } @@ -256,8 +250,6 @@ static int qcom_pas_load(struct rproc *rproc, const struct firmware *fw) release_dtb_metadata: qcom_scm_pas_metadata_release(pas->dtb_pas_ctx); - -release_dtb_firmware: release_firmware(pas->dtb_firmware); return ret; @@ -305,14 +297,8 @@ static int qcom_pas_start(struct rproc *rproc) } } - ret = qcom_mdt_pas_init(pas->dev, pas->firmware, rproc->firmware, pas->pas_id, - pas->mem_phys, pas->pas_ctx); - if (ret) - goto disable_px_supply; - - ret = qcom_mdt_load_no_init(pas->dev, pas->firmware, rproc->firmware, - pas->mem_region, pas->mem_phys, pas->mem_size, - &pas->mem_reloc); + ret = qcom_mdt_pas_load(pas->pas_ctx, pas->firmware, rproc->firmware, + pas->mem_region, &pas->mem_reloc); if (ret) goto release_pas_metadata; diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index b125140100db..50c6a3c6b2a3 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -478,5 +478,36 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, } EXPORT_SYMBOL_GPL(qcom_mdt_load); +/** + * qcom_mdt_pas_load - Loads and authenticates the metadata of the firmware + * (typically contained in the .mdt file), followed by loading the actual + * firmware segments (e.g., .bXX files). Authentication of the segments done + * by a separate call. + * + * The PAS context must be initialized using qcom_scm_pas_context_init() + * prior to invoking this function. + * + * @ctx: Pointer to the PAS (Peripheral Authentication Service) context + * @fw: Firmware object representing the .mdt file + * @firmware: Name of the firmware used to construct segment file names + * @mem_region: Memory region allocated for loading the firmware + * @reloc_base: Physical address adjusted after relocation + * + * Return: 0 on success or a negative error code on failure. + */ +int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, + const char *firmware, void *mem_region, phys_addr_t *reloc_base) +{ + int ret; + + ret = qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx); + if (ret) + return ret; + + return qcom_mdt_load_no_init(ctx->dev, fw, firmware, mem_region, ctx->mem_phys, + ctx->mem_size, reloc_base); +} +EXPORT_SYMBOL_GPL(qcom_mdt_pas_load); + MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 07c278841816..7d57746fbbfa 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -23,6 +23,9 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base); +int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *fw, + const char *firmware, void *mem_region, phys_addr_t *reloc_base); + int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, const char *fw_name, void *mem_region, phys_addr_t mem_phys, size_t mem_size, @@ -52,6 +55,13 @@ static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, return -ENODEV; } +static inline int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, + const struct firmware *fw, const char *firmware, + void *mem_region, phys_addr_t *reloc_base) +{ + return -ENODEV; +} + static inline int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, const char *fw_name, void *mem_region, -- cgit v1.2.3 From 928dbaaa9d89363d79e309ec00c5527ddfbe47c8 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:55 +0530 Subject: soc: qcom: mdtloader: Remove qcom_mdt_pas_init() from exported symbols qcom_mdt_pas_init() was previously used only by the remoteproc driver (drivers/remoteproc/qcom_q6v5_pas.c). Since that driver has now transitioned to using PAS context-based qcom_mdt_pas_load() function, making qcom_mdt_pas_init() obsolete for external use. Removes qcom_mdt_pas_init() from the list of exported symbols and make it static to limit its scope to internal use within mdtloader. Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-7-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/mdt_loader.c | 22 +++++----------------- include/linux/soc/qcom/mdt_loader.h | 10 ---------- 2 files changed, 5 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 50c6a3c6b2a3..c004d444d698 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -227,20 +227,9 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, } EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); -/** - * qcom_mdt_pas_init() - initialize PAS region for firmware loading - * @dev: device handle to associate resources with - * @fw: firmware object for the mdt file - * @fw_name: name of the firmware, for construction of segment file names - * @pas_id: PAS identifier - * @mem_phys: physical address of allocated memory region - * @ctx: PAS context, ctx->metadata to be released by caller - * - * Returns 0 on success, negative errno otherwise. - */ -int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *ctx) +static int __qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, + const char *fw_name, int pas_id, phys_addr_t mem_phys, + struct qcom_scm_pas_context *ctx) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; @@ -302,7 +291,6 @@ int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, out: return ret; } -EXPORT_SYMBOL_GPL(qcom_mdt_pas_init); static bool qcom_mdt_bins_are_split(const struct firmware *fw) { @@ -469,7 +457,7 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, { int ret; - ret = qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL); + ret = __qcom_mdt_pas_init(dev, fw, fw_name, pas_id, mem_phys, NULL); if (ret) return ret; @@ -500,7 +488,7 @@ int qcom_mdt_pas_load(struct qcom_scm_pas_context *ctx, const struct firmware *f { int ret; - ret = qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx); + ret = __qcom_mdt_pas_init(ctx->dev, fw, firmware, ctx->pas_id, ctx->mem_phys, ctx); if (ret) return ret; diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 7d57746fbbfa..82372e0db0a1 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -15,9 +15,6 @@ struct qcom_scm_pas_context; #if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ssize_t qcom_mdt_get_size(const struct firmware *fw); -int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *pas_ctx); int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, @@ -40,13 +37,6 @@ static inline ssize_t qcom_mdt_get_size(const struct firmware *fw) return -ENODEV; } -static inline int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_context *pas_ctx) -{ - return -ENODEV; -} - static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, -- cgit v1.2.3 From 4a7d6a78fbc6527fb1b61944aab00d9cdd1d4f01 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:56 +0530 Subject: firmware: qcom_scm: Add a prep version of auth_and_reset function For memory passed to TrustZone (TZ), it must either be part of a pool registered with TZ or explicitly registered via SHMbridge SMC calls. When Gunyah hypervisor is present, PAS SMC calls from Linux running at EL1 are trapped by Gunyah running @ EL2, which handles SHMbridge creation for both metadata and remoteproc carveout memory before invoking the calls to TZ. On SoCs running with a non-Gunyah-based hypervisor, Linux must take responsibility for creating the SHM bridge before invoking PAS SMC calls. For the auth_and_reset() call, the remoteproc carveout memory must first be registered with TZ via a SHMbridge SMC call and once authentication and reset are complete, the SHMbridge memory can be deregistered. Introduce qcom_scm_pas_prepare_and_auth_reset(), which sets up the SHM bridge over the remoteproc carveout memory when Linux operates at EL2. This behavior is indicated by a new field added to the PAS context data structure. The function then invokes the auth_and_reset SMC call. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-8-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 47 ++++++++++++++++++++++++++++++++++ include/linux/firmware/qcom/qcom_scm.h | 2 ++ 2 files changed, 49 insertions(+) (limited to 'include') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 4edd475ef848..d3783166fea1 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -765,6 +765,53 @@ disable_clk: } EXPORT_SYMBOL_GPL(qcom_scm_pas_auth_and_reset); +/** + * qcom_scm_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the + * remote processor + * + * @ctx: Context saved during call to qcom_scm_pas_context_init() + * + * This function performs the necessary steps to prepare a PAS subsystem, + * authenticate it using the provided metadata, and initiate a reset sequence. + * + * It should be used when Linux is in control setting up the IOMMU hardware + * for remote subsystem during secure firmware loading processes. The preparation + * step sets up a shmbridge over the firmware memory before TrustZone accesses the + * firmware memory region for authentication. The authentication step verifies + * the integrity and authenticity of the firmware or configuration using secure + * metadata. Finally, the reset step ensures the subsystem starts in a clean and + * sane state. + * + * Return: 0 on success, negative errno on failure. + */ +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) +{ + u64 handle; + int ret; + + /* + * When Linux running @ EL1, Gunyah hypervisor running @ EL2 traps the + * auth_and_reset call and create an shmbridge on the remote subsystem + * memory region and then invokes a call to TrustZone to authenticate. + */ + if (!ctx->use_tzmem) + return qcom_scm_pas_auth_and_reset(ctx->pas_id); + + /* + * When Linux runs @ EL2 Linux must create the shmbridge itself and then + * subsequently call TrustZone for authenticate and reset. + */ + ret = qcom_tzmem_shm_bridge_create(ctx->mem_phys, ctx->mem_size, &handle); + if (ret) + return ret; + + ret = qcom_scm_pas_auth_and_reset(ctx->pas_id); + qcom_tzmem_shm_bridge_delete(handle); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_prepare_and_auth_reset); + /** * qcom_scm_pas_shutdown() - Shut down the remote processor * @pas_id: peripheral authentication service id diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index ad69b51fe6fc..d6d83888bb75 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -74,6 +74,7 @@ struct qcom_scm_pas_context { void *ptr; dma_addr_t phys; ssize_t size; + bool use_tzmem; }; struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, @@ -87,6 +88,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); int qcom_scm_pas_auth_and_reset(u32 pas_id); int qcom_scm_pas_shutdown(u32 pas_id); bool qcom_scm_pas_supported(u32 pas_id); +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx); int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); -- cgit v1.2.3 From 8b9d2050cfa0c22c05622df103e366933fc045ed Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Mon, 5 Jan 2026 18:52:59 +0530 Subject: firmware: qcom_scm: Add qcom_scm_pas_get_rsc_table() to get resource table Qualcomm remote processor may rely on Static and Dynamic resources for it to be functional. Static resources are fixed like for example, memory-mapped addresses required by the subsystem and dynamic resources, such as shared memory in DDR etc., are determined at runtime during the boot process. For most of the Qualcomm SoCs, when run with Gunyah or older QHEE hypervisor, all the resources whether it is static or dynamic, is managed by the hypervisor. Dynamic resources if it is present for a remote processor will always be coming from secure world via SMC call while static resources may be present in remote processor firmware binary or it may be coming qcom_scm_pas_get_rsc_table() SMC call along with dynamic resources. Some of the remote processor drivers, such as video, GPU, IPA, etc., do not check whether resources are present in their remote processor firmware binary. In such cases, the caller of this function should set input_rt and input_rt_size as NULL and zero respectively. Remoteproc framework has method to check whether firmware binary contain resources or not and they should be pass resource table pointer to input_rt and resource table size to input_rt_size and this will be forwarded to TrustZone for authentication. TrustZone will then append the dynamic resources and return the complete resource table in the passed output buffer. More about documentation on resource table format can be found in include/linux/remoteproc.h Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-11-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 171 +++++++++++++++++++++++++++++++++ drivers/firmware/qcom/qcom_scm.h | 1 + include/linux/firmware/qcom/qcom_scm.h | 4 + 3 files changed, 176 insertions(+) (limited to 'include') diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 4ce892d8fb25..918613d5a151 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -111,6 +112,8 @@ enum qcom_scm_qseecom_tz_cmd_info { QSEECOM_TZ_CMD_INFO_VERSION = 3, }; +#define RSCTABLE_BUFFER_NOT_SUFFICIENT 20 + #define QSEECOM_MAX_APP_NAME_SIZE 64 #define SHMBRIDGE_RESULT_NOTSUPP 4 @@ -766,6 +769,174 @@ disable_clk: } EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); +static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, + size_t input_rt_size, + size_t *output_rt_size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_PIL, + .cmd = QCOM_SCM_PIL_PAS_GET_RSCTABLE, + .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RW, QCOM_SCM_VAL), + .args[0] = pas_id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + void *output_rt_tzm; + int ret; + + output_rt_tzm = qcom_tzmem_alloc(__scm->mempool, *output_rt_size, GFP_KERNEL); + if (!output_rt_tzm) + return ERR_PTR(-ENOMEM); + + desc.args[1] = qcom_tzmem_to_phys(input_rt_tzm); + desc.args[2] = input_rt_size; + desc.args[3] = qcom_tzmem_to_phys(output_rt_tzm); + desc.args[4] = *output_rt_size; + + /* + * Whether SMC fail or pass, res.result[2] will hold actual resource table + * size. + * + * If passed 'output_rt_size' buffer size is not sufficient to hold the + * resource table TrustZone sends, response code in res.result[1] as + * RSCTABLE_BUFFER_NOT_SUFFICIENT so that caller can retry this SMC call + * with output_rt_tzm buffer with res.result[2] size however, It should not + * be of unresonable size. + */ + ret = qcom_scm_call(__scm->dev, &desc, &res); + if (!ret && res.result[2] > SZ_1G) { + ret = -E2BIG; + goto free_output_rt; + } + + *output_rt_size = res.result[2]; + if (ret && res.result[1] == RSCTABLE_BUFFER_NOT_SUFFICIENT) + ret = -EOVERFLOW; + +free_output_rt: + if (ret) + qcom_tzmem_free(output_rt_tzm); + + return ret ? ERR_PTR(ret) : output_rt_tzm; +} + +/** + * qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer + * for a given peripheral. + * + * Qualcomm remote processor may rely on both static and dynamic resources for + * its functionality. Static resources typically refer to memory-mapped addresses + * required by the subsystem and are often embedded within the firmware binary + * and dynamic resources, such as shared memory in DDR etc., are determined at + * runtime during the boot process. + * + * On Qualcomm Technologies devices, it's possible that static resources are not + * embedded in the firmware binary and instead are provided by TrustZone However, + * dynamic resources are always expected to come from TrustZone. This indicates + * that for Qualcomm devices, all resources (static and dynamic) will be provided + * by TrustZone via the SMC call. + * + * If the remote processor firmware binary does contain static resources, they + * should be passed in input_rt. These will be forwarded to TrustZone for + * authentication. TrustZone will then append the dynamic resources and return + * the complete resource table in output_rt_tzm. + * + * If the remote processor firmware binary does not include a resource table, + * the caller of this function should set input_rt as NULL and input_rt_size + * as zero respectively. + * + * More about documentation on resource table data structures can be found in + * include/linux/remoteproc.h + * + * @ctx: PAS context + * @pas_id: peripheral authentication service id + * @input_rt: resource table buffer which is present in firmware binary + * @input_rt_size: size of the resource table present in firmware binary + * @output_rt_size: TrustZone expects caller should pass worst case size for + * the output_rt_tzm. + * + * Return: + * On success, returns a pointer to the allocated buffer containing the final + * resource table and output_rt_size will have actual resource table size from + * TrustZone. The caller is responsible for freeing the buffer. On failure, + * returns ERR_PTR(-errno). + */ +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) +{ + struct resource_table empty_rsc = {}; + size_t size = SZ_16K; + void *output_rt_tzm; + void *input_rt_tzm; + void *tbl_ptr; + int ret; + + ret = qcom_scm_clk_enable(); + if (ret) + return ERR_PTR(ret); + + ret = qcom_scm_bw_enable(); + if (ret) + goto disable_clk; + + /* + * TrustZone can not accept buffer as NULL value as argument hence, + * we need to pass a input buffer indicating that subsystem firmware + * does not have resource table by filling resource table structure. + */ + if (!input_rt) { + input_rt = &empty_rsc; + input_rt_size = sizeof(empty_rsc); + } + + input_rt_tzm = qcom_tzmem_alloc(__scm->mempool, input_rt_size, GFP_KERNEL); + if (!input_rt_tzm) { + ret = -ENOMEM; + goto disable_scm_bw; + } + + memcpy(input_rt_tzm, input_rt, input_rt_size); + + output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm, + input_rt_size, &size); + if (PTR_ERR(output_rt_tzm) == -EOVERFLOW) + /* Try again with the size requested by the TZ */ + output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, + input_rt_tzm, + input_rt_size, + &size); + if (IS_ERR(output_rt_tzm)) { + ret = PTR_ERR(output_rt_tzm); + goto free_input_rt; + } + + tbl_ptr = kzalloc(size, GFP_KERNEL); + if (!tbl_ptr) { + qcom_tzmem_free(output_rt_tzm); + ret = -ENOMEM; + goto free_input_rt; + } + + memcpy(tbl_ptr, output_rt_tzm, size); + *output_rt_size = size; + qcom_tzmem_free(output_rt_tzm); + +free_input_rt: + qcom_tzmem_free(input_rt_tzm); + +disable_scm_bw: + qcom_scm_bw_disable(); + +disable_clk: + qcom_scm_clk_disable(); + + return ret ? ERR_PTR(ret) : tbl_ptr; +} +EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table); + /** * qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware * and reset the remote processor diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index a56c8212cc0c..50d87c628d78 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev); #define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06 #define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07 #define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a +#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21 #define QCOM_SCM_SVC_IO 0x05 #define QCOM_SCM_IO_READ 0x01 diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h index d6d83888bb75..5747bd191bf1 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -88,6 +88,10 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); int qcom_scm_pas_auth_and_reset(u32 pas_id); int qcom_scm_pas_shutdown(u32 pas_id); bool qcom_scm_pas_supported(u32 pas_id); +struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size); + int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx); int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); -- cgit v1.2.3 From 900131320bc9a9ec1d84702b2694b813c11c91b7 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Mon, 12 Jan 2026 23:15:54 +0200 Subject: ARM: s3c: remove a leftover hwmon-s3c.h header file The last user of defined structures s3c_hwmon_pdata and s3c_hwmon_chcfg was removed in commit 0d297df03890 ("ARM: s3c: simplify platform code"), thus the platform data header file itself can be removed also. Signed-off-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20260112211554.3755188-1-vz@mleia.com Signed-off-by: Krzysztof Kozlowski --- include/linux/platform_data/hwmon-s3c.h | 36 --------------------------------- 1 file changed, 36 deletions(-) delete mode 100644 include/linux/platform_data/hwmon-s3c.h (limited to 'include') diff --git a/include/linux/platform_data/hwmon-s3c.h b/include/linux/platform_data/hwmon-s3c.h deleted file mode 100644 index 7d21e0c41037..000000000000 --- a/include/linux/platform_data/hwmon-s3c.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2005 Simtec Electronics - * Ben Dooks - * http://armlinux.simtec.co.uk/ - * - * S3C - HWMon interface for ADC -*/ - -#ifndef __HWMON_S3C_H__ -#define __HWMON_S3C_H__ - -/** - * s3c_hwmon_chcfg - channel configuration - * @name: The name to give this channel. - * @mult: Multiply the ADC value read by this. - * @div: Divide the value from the ADC by this. - * - * The value read from the ADC is converted to a value that - * hwmon expects (mV) by result = (value_read * @mult) / @div. - */ -struct s3c_hwmon_chcfg { - const char *name; - unsigned int mult; - unsigned int div; -}; - -/** - * s3c_hwmon_pdata - HWMON platform data - * @in: One configuration for each possible channel used. - */ -struct s3c_hwmon_pdata { - struct s3c_hwmon_chcfg *in[8]; -}; - -#endif /* __HWMON_S3C_H__ */ -- cgit v1.2.3 From 241bdf7253502c56251ef8b25ab9cad5b6547422 Mon Sep 17 00:00:00 2001 From: Aristo Chen Date: Mon, 12 Jan 2026 23:48:29 +0800 Subject: tee: add revision sysfs attribute Add a generic TEE revision sysfs attribute backed by a new optional get_tee_revision() callback. The revision string is diagnostic-only and must not be used to infer feature support. Signed-off-by: Aristo Chen Reviewed-by: Sumit Garg Signed-off-by: Jens Wiklander --- Documentation/ABI/testing/sysfs-class-tee | 10 ++++++ drivers/tee/tee_core.c | 51 ++++++++++++++++++++++++++++++- include/linux/tee_core.h | 9 ++++++ 3 files changed, 69 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee index c9144d16003e..1a0a3050aaa9 100644 --- a/Documentation/ABI/testing/sysfs-class-tee +++ b/Documentation/ABI/testing/sysfs-class-tee @@ -13,3 +13,13 @@ Description: space if the variable is absent. The primary purpose of this variable is to let systemd know whether tee-supplicant is needed in the early boot with initramfs. + +What: /sys/class/tee/tee{,priv}X/revision +Date: Jan 2026 +KernelVersion: 6.19 +Contact: op-tee@lists.trustedfirmware.org +Description: + Read-only revision string reported by the TEE driver. This is + for diagnostics only and must not be used to infer feature + support. Use TEE_IOC_VERSION for capability and compatibility + checks. diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index d65d47cc154e..0a00499811c1 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1146,7 +1146,56 @@ static struct attribute *tee_dev_attrs[] = { NULL }; -ATTRIBUTE_GROUPS(tee_dev); +static const struct attribute_group tee_dev_group = { + .attrs = tee_dev_attrs, +}; + +static ssize_t revision_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + char version[TEE_REVISION_STR_SIZE]; + int ret; + + if (!teedev->desc->ops->get_tee_revision) + return -ENODEV; + + ret = teedev->desc->ops->get_tee_revision(teedev, version, + sizeof(version)); + if (ret) + return ret; + + return sysfs_emit(buf, "%s\n", version); +} +static DEVICE_ATTR_RO(revision); + +static struct attribute *tee_revision_attrs[] = { + &dev_attr_revision.attr, + NULL +}; + +static umode_t tee_revision_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct tee_device *teedev = container_of(dev, struct tee_device, dev); + + if (teedev->desc->ops->get_tee_revision) + return attr->mode; + + return 0; +} + +static const struct attribute_group tee_revision_group = { + .attrs = tee_revision_attrs, + .is_visible = tee_revision_attr_is_visible, +}; + +static const struct attribute_group *tee_dev_groups[] = { + &tee_dev_group, + &tee_revision_group, + NULL +}; static const struct class tee_class = { .name = "tee", diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index 1f3e5dad6d0d..ee5f0bd41f43 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -76,6 +76,9 @@ struct tee_device { /** * struct tee_driver_ops - driver operations vtable * @get_version: returns version of driver + * @get_tee_revision: returns revision string (diagnostic only); + * do not infer feature support from this, use + * TEE_IOC_VERSION instead * @open: called for a context when the device file is opened * @close_context: called when the device file is closed * @release: called to release the context @@ -95,9 +98,12 @@ struct tee_device { * client closes the device file, even if there are existing references to the * context. The TEE driver can use @close_context to start cleaning up. */ + struct tee_driver_ops { void (*get_version)(struct tee_device *teedev, struct tee_ioctl_version_data *vers); + int (*get_tee_revision)(struct tee_device *teedev, + char *buf, size_t len); int (*open)(struct tee_context *ctx); void (*close_context)(struct tee_context *ctx); void (*release)(struct tee_context *ctx); @@ -123,6 +129,9 @@ struct tee_driver_ops { int (*shm_unregister)(struct tee_context *ctx, struct tee_shm *shm); }; +/* Size for TEE revision string buffer used by get_tee_revision(). */ +#define TEE_REVISION_STR_SIZE 128 + /** * struct tee_desc - Describes the TEE driver to the subsystem * @name: name of driver -- cgit v1.2.3 From ccef4b2703ff5b0de0b1bda30a0de3026d52eb19 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 10 Jan 2026 21:37:53 +0200 Subject: soc: qcom: ubwc: add missing include The header has a function which calls pr_err(). Don't require users of the header to include and include it here. Fixes: 87cfc79dcd60 ("drm/msm/a6xx: Resolve the meaning of UBWC_MODE") Signed-off-by: Dmitry Baryshkov Reviewed-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20260110-iris-ubwc-v1-1-dd70494dcd7b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/ubwc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 0a4edfe3d96d..f052e241736c 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -8,6 +8,7 @@ #define __QCOM_UBWC_H__ #include +#include #include struct qcom_ubwc_cfg_data { -- cgit v1.2.3 From 49d2cda7ca2e8e287617e7a5b7fae523eaece955 Mon Sep 17 00:00:00 2001 From: "Herve Codina (Schneider Electric)" Date: Wed, 14 Jan 2026 10:39:30 +0100 Subject: of/irq: Introduce for_each_of_imap_item for_each_of_imap_item is an iterator designed to help a driver to parse an interrupt-map property. Indeed some drivers need to know details about the interrupt mapping described in the device-tree in order to set internal registers accordingly. Signed-off-by: Herve Codina (Schneider Electric) Tested-by: Wolfram Sang Reviewed-by: Rob Herring (Arm) Reviewed-by: Linus Walleij Link: https://patch.msgid.link/20260114093938.1089936-2-herve.codina@bootlin.com Signed-off-by: Geert Uytterhoeven --- drivers/of/irq.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/of_irq.h | 41 ++++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/of/irq.c b/drivers/of/irq.c index e3816819dbfe..f374d8b212b8 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -157,6 +157,76 @@ const __be32 *of_irq_parse_imap_parent(const __be32 *imap, int len, struct of_ph return imap; } +int of_imap_parser_init(struct of_imap_parser *parser, struct device_node *node, + struct of_imap_item *item) +{ + int imaplen; + u32 tmp; + int ret; + + /* + * parent_offset is the offset where the parent part is starting. + * In other words, the offset where the parent interrupt controller + * phandle is present. + * + * Compute this offset (child #interrupt-cells + child #address-cells) + */ + parser->parent_offset = of_bus_n_addr_cells(node); + + ret = of_property_read_u32(node, "#interrupt-cells", &tmp); + if (ret) + return ret; + + parser->parent_offset += tmp; + + if (WARN(parser->parent_offset > ARRAY_SIZE(item->child_imap), + "child part size = %u, cannot fit in array of %zu items", + parser->parent_offset, ARRAY_SIZE(item->child_imap))) + return -EINVAL; + + parser->imap = of_get_property(node, "interrupt-map", &imaplen); + if (!parser->imap) + return -ENOENT; + + imaplen /= sizeof(*parser->imap); + parser->imap_end = parser->imap + imaplen; + + memset(item, 0, sizeof(*item)); + item->child_imap_count = parser->parent_offset; + + return 0; +} +EXPORT_SYMBOL_GPL(of_imap_parser_init); + +struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser, + struct of_imap_item *item) +{ + const __be32 *imap_parent, *imap_next; + int i; + + /* Release previously get parent node */ + of_node_put(item->parent_args.np); + + if (parser->imap + parser->parent_offset + 1 >= parser->imap_end) + return NULL; + + imap_parent = parser->imap + parser->parent_offset; + + imap_next = of_irq_parse_imap_parent(imap_parent, + parser->imap_end - imap_parent, + &item->parent_args); + if (!imap_next) + return NULL; + + for (i = 0; i < parser->parent_offset; i++) + item->child_imap[i] = be32_to_cpu(*(parser->imap + i)); + + parser->imap = imap_next; + + return item; +} +EXPORT_SYMBOL_GPL(of_imap_parser_one); + /** * of_irq_parse_raw - Low level interrupt tree parsing * @addr: address specifier (start of "reg" property of the device) in be32 format diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h index 1c2bc0281807..2a64d8cecaae 100644 --- a/include/linux/of_irq.h +++ b/include/linux/of_irq.h @@ -11,6 +11,30 @@ typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *); +struct of_imap_parser { + struct device_node *node; + const __be32 *imap; + const __be32 *imap_end; + u32 parent_offset; +}; + +struct of_imap_item { + struct of_phandle_args parent_args; + u32 child_imap_count; + u32 child_imap[16]; /* Arbitrary size. + * Should be #address-cells + #interrupt-cells but + * avoid using allocation and so, expect that 16 + * should be enough + */ +}; + +/* + * If the iterator is exited prematurely (break, goto, return) of_node_put() has + * to be called on item.parent_args.np + */ +#define for_each_of_imap_item(parser, item) \ + for (; of_imap_parser_one(parser, item);) + /* * Workarounds only applied to 32bit powermac machines */ @@ -49,6 +73,11 @@ extern int of_irq_get_byname(struct device_node *dev, const char *name); extern int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int nr_irqs); extern struct device_node *of_irq_find_parent(struct device_node *child); +extern int of_imap_parser_init(struct of_imap_parser *parser, + struct device_node *node, + struct of_imap_item *item); +extern struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser, + struct of_imap_item *item); extern struct irq_domain *of_msi_get_domain(struct device *dev, const struct device_node *np, enum irq_domain_bus_token token); @@ -92,7 +121,17 @@ static inline void *of_irq_find_parent(struct device_node *child) { return NULL; } - +static inline int of_imap_parser_init(struct of_imap_parser *parser, + struct device_node *node, + struct of_imap_item *item) +{ + return -ENOSYS; +} +static inline struct of_imap_item *of_imap_parser_one(struct of_imap_parser *parser, + struct of_imap_item *item) +{ + return NULL; +} static inline struct irq_domain *of_msi_get_domain(struct device *dev, struct device_node *np, enum irq_domain_bus_token token) -- cgit v1.2.3 From 4fa62e80c7dba7ff419dea23f10bcb125e38bde3 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 29 Oct 2025 20:07:01 +0800 Subject: firmware: arm_scmi: imx: Support getting syslog of MISC protocol MISC protocol supports getting system log regarding system sleep latency, wakeup interrupt and etc. Add the API for user to retrieve the information from SM. Signed-off-by: Peng Fan Acked-by: Sudeep Holla Signed-off-by: Shawn Guo --- .../firmware/arm_scmi/vendors/imx/imx-sm-misc.c | 83 ++++++++++++++++++++++ include/linux/scmi_imx_protocol.h | 2 + 2 files changed, 85 insertions(+) (limited to 'include') diff --git a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c index 700a3f24f4ef..eae0b0562f6c 100644 --- a/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c +++ b/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c @@ -28,6 +28,7 @@ enum scmi_imx_misc_protocol_cmd { SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6, SCMI_IMX_MISC_CTRL_NOTIFY = 0x8, SCMI_IMX_MISC_CFG_INFO_GET = 0xC, + SCMI_IMX_MISC_SYSLOG_GET = 0xD, SCMI_IMX_MISC_BOARD_INFO = 0xE, }; @@ -89,6 +90,19 @@ struct scmi_imx_misc_cfg_info_out { u8 cfgname[MISC_MAX_CFGNAME]; }; +struct scmi_imx_misc_syslog_in { + __le32 flags; + __le32 index; +}; + +#define REMAINING(x) le32_get_bits((x), GENMASK(31, 20)) +#define RETURNED(x) le32_get_bits((x), GENMASK(11, 0)) + +struct scmi_imx_misc_syslog_out { + __le32 numlogflags; + __le32 syslog[]; +}; + static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph, struct scmi_imx_misc_info *mi) { @@ -371,10 +385,79 @@ static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph) return ret; } +struct scmi_imx_misc_syslog_ipriv { + u32 *array; + u16 *size; +}; + +static void iter_misc_syslog_prepare_message(void *message, u32 desc_index, + const void *priv) +{ + struct scmi_imx_misc_syslog_in *msg = message; + + msg->flags = cpu_to_le32(0); + msg->index = cpu_to_le32(desc_index); +} + +static int iter_misc_syslog_update_state(struct scmi_iterator_state *st, + const void *response, void *priv) +{ + const struct scmi_imx_misc_syslog_out *r = response; + struct scmi_imx_misc_syslog_ipriv *p = priv; + + st->num_returned = RETURNED(r->numlogflags); + st->num_remaining = REMAINING(r->numlogflags); + *p->size = st->num_returned + st->num_remaining; + + return 0; +} + +static int +iter_misc_syslog_process_response(const struct scmi_protocol_handle *ph, + const void *response, + struct scmi_iterator_state *st, void *priv) +{ + const struct scmi_imx_misc_syslog_out *r = response; + struct scmi_imx_misc_syslog_ipriv *p = priv; + + p->array[st->desc_index + st->loop_idx] = + le32_to_cpu(r->syslog[st->loop_idx]); + + return 0; +} + +static int scmi_imx_misc_syslog_get(const struct scmi_protocol_handle *ph, u16 *size, + void *array) +{ + struct scmi_iterator_ops ops = { + .prepare_message = iter_misc_syslog_prepare_message, + .update_state = iter_misc_syslog_update_state, + .process_response = iter_misc_syslog_process_response, + }; + struct scmi_imx_misc_syslog_ipriv ipriv = { + .array = array, + .size = size, + }; + void *iter; + + if (!array || !size || !*size) + return -EINVAL; + + iter = ph->hops->iter_response_init(ph, &ops, *size, SCMI_IMX_MISC_SYSLOG_GET, + sizeof(struct scmi_imx_misc_syslog_in), + &ipriv); + if (IS_ERR(iter)) + return PTR_ERR(iter); + + /* If firmware return NOT SUPPORTED, propagate value to caller */ + return ph->hops->iter_response_run(iter); +} + static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = { .misc_ctrl_set = scmi_imx_misc_ctrl_set, .misc_ctrl_get = scmi_imx_misc_ctrl_get, .misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify, + .misc_syslog = scmi_imx_misc_syslog_get, }; static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph) diff --git a/include/linux/scmi_imx_protocol.h b/include/linux/scmi_imx_protocol.h index 27bd372cbfb1..2407d7693b6b 100644 --- a/include/linux/scmi_imx_protocol.h +++ b/include/linux/scmi_imx_protocol.h @@ -59,6 +59,8 @@ struct scmi_imx_misc_proto_ops { u32 *num, u32 *val); int (*misc_ctrl_req_notify)(const struct scmi_protocol_handle *ph, u32 ctrl_id, u32 evt_id, u32 flags); + int (*misc_syslog)(const struct scmi_protocol_handle *ph, u16 *size, + void *array); }; /* See LMM_ATTRIBUTES in imx95.rst */ -- cgit v1.2.3 From 70f752ebb08c85a5ea19471a5aaf26263e53dcb0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 3 Feb 2025 16:22:58 +0100 Subject: soc/tegra: pmc: Add PMC contextual functions Add implementations that take as argument a struct tegra_pmc * for most public APIs, as well as a function to obtain the PMC for any given device. This will allow transitioning away users from relying on a global variable storing the PMC context. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 174 ++++++++++++++++++++++++++++++++++++++++++++---- include/soc/tegra/pmc.h | 60 ++++++++++++++++- 2 files changed, 219 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 750fe29f4cf5..9cdbd8ba94be 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -957,30 +957,122 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain) return err; } +static void tegra_pmc_put_device(void *data) +{ + struct tegra_pmc *pmc = data; + + put_device(pmc->dev); +} + +static const struct of_device_id tegra_pmc_match[]; + +static struct tegra_pmc *tegra_pmc_get(struct device *dev) +{ + struct platform_device *pdev; + struct device_node *np; + struct tegra_pmc *pmc; + + np = of_parse_phandle(dev->of_node, "nvidia,pmc", 0); + if (!np) { + struct device_node *parent = of_node_get(dev->of_node); + + while ((parent = of_get_next_parent(parent)) != NULL) { + np = of_find_matching_node(parent, tegra_pmc_match); + if (np) + break; + } + + of_node_put(parent); + + if (!np) + return ERR_PTR(-ENODEV); + } + + pdev = of_find_device_by_node(np); + of_node_put(np); + + if (!pdev) + return ERR_PTR(-ENODEV); + + pmc = platform_get_drvdata(pdev); + if (!pmc) { + put_device(&pdev->dev); + return ERR_PTR(-EPROBE_DEFER); + } + + return pmc; +} + /** - * tegra_powergate_power_on() - power on partition + * tegra_pmc_get() - find the PMC for a given device + * @dev: device for which to find the PMC + * + * Returns a pointer to the PMC on success or an ERR_PTR()-encoded error code + * otherwise. + */ +struct tegra_pmc *devm_tegra_pmc_get(struct device *dev) +{ + struct tegra_pmc *pmc; + int err; + + pmc = tegra_pmc_get(dev); + if (IS_ERR(pmc)) + return pmc; + + err = devm_add_action_or_reset(dev, tegra_pmc_put_device, pmc); + if (err < 0) + return ERR_PTR(err); + + return pmc; +} +EXPORT_SYMBOL(devm_tegra_pmc_get); + +/** + * tegra_pmc_powergate_power_on() - power on partition + * @pmc: power management controller * @id: partition ID */ -int tegra_powergate_power_on(unsigned int id) +int tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id) { if (!tegra_powergate_is_available(pmc, id)) return -EINVAL; return tegra_powergate_set(pmc, id, true); } +EXPORT_SYMBOL(tegra_pmc_powergate_power_on); + +/** + * tegra_powergate_power_on() - power on partition + * @id: partition ID + */ +int tegra_powergate_power_on(unsigned int id) +{ + return tegra_pmc_powergate_power_on(pmc, id); +} EXPORT_SYMBOL(tegra_powergate_power_on); /** - * tegra_powergate_power_off() - power off partition + * tegra_pmc_powergate_power_off() - power off partition + * @pmc: power management controller * @id: partition ID */ -int tegra_powergate_power_off(unsigned int id) +int tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id) { if (!tegra_powergate_is_available(pmc, id)) return -EINVAL; return tegra_powergate_set(pmc, id, false); } +EXPORT_SYMBOL(tegra_pmc_powergate_power_off); + +/** + * tegra_powergate_power_off() - power off partition + * @id: partition ID + */ +int tegra_powergate_power_off(unsigned int id) +{ + return tegra_pmc_powergate_power_off(pmc, id); +} EXPORT_SYMBOL(tegra_powergate_power_off); /** @@ -997,28 +1089,41 @@ static int tegra_powergate_is_powered(struct tegra_pmc *pmc, unsigned int id) } /** - * tegra_powergate_remove_clamping() - remove power clamps for partition + * tegra_pmc_powergate_remove_clamping() - remove power clamps for partition + * @pmc: power management controller * @id: partition ID */ -int tegra_powergate_remove_clamping(unsigned int id) +int tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id) { if (!tegra_powergate_is_available(pmc, id)) return -EINVAL; return __tegra_powergate_remove_clamping(pmc, id); } +EXPORT_SYMBOL(tegra_pmc_powergate_remove_clamping); + +/** + * tegra_powergate_remove_clamping() - remove power clamps for partition + * @id: partition ID + */ +int tegra_powergate_remove_clamping(unsigned int id) +{ + return tegra_pmc_powergate_remove_clamping(pmc, id); +} EXPORT_SYMBOL(tegra_powergate_remove_clamping); /** - * tegra_powergate_sequence_power_up() - power up partition + * tegra_pmc_powergate_sequence_power_up() - power up partition + * @pmc: power management controller * @id: partition ID * @clk: clock for partition * @rst: reset for partition * * Must be called with clk disabled, and returns with clk enabled. */ -int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, - struct reset_control *rst) +int tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc, + unsigned int id, struct clk *clk, + struct reset_control *rst) { struct tegra_powergate *pg; int err; @@ -1052,6 +1157,21 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, return err; } +EXPORT_SYMBOL(tegra_pmc_powergate_sequence_power_up); + +/** + * tegra_powergate_sequence_power_up() - power up partition + * @id: partition ID + * @clk: clock for partition + * @rst: reset for partition + * + * Must be called with clk disabled, and returns with clk enabled. + */ +int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, + struct reset_control *rst) +{ + return tegra_pmc_powergate_sequence_power_up(pmc, id, clk, rst); +} EXPORT_SYMBOL(tegra_powergate_sequence_power_up); /** @@ -1627,11 +1747,12 @@ static void tegra_io_pad_unprepare(struct tegra_pmc *pmc) /** * tegra_io_pad_power_enable() - enable power to I/O pad + * @pmc: power management controller * @id: Tegra I/O pad ID for which to enable power * * Returns: 0 on success or a negative error code on failure. */ -int tegra_io_pad_power_enable(enum tegra_io_pad id) +int tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id) { const struct tegra_io_pad_soc *pad; unsigned long request, status; @@ -1666,15 +1787,28 @@ unlock: mutex_unlock(&pmc->powergates_lock); return err; } +EXPORT_SYMBOL(tegra_pmc_io_pad_power_enable); + +/** + * tegra_io_pad_power_enable() - enable power to I/O pad + * @id: Tegra I/O pad ID for which to enable power + * + * Returns: 0 on success or a negative error code on failure. + */ +int tegra_io_pad_power_enable(enum tegra_io_pad id) +{ + return tegra_pmc_io_pad_power_enable(pmc, id); +} EXPORT_SYMBOL(tegra_io_pad_power_enable); /** - * tegra_io_pad_power_disable() - disable power to I/O pad + * tegra_pmc_io_pad_power_disable() - disable power to I/O pad + * @pmc: power management controller * @id: Tegra I/O pad ID for which to disable power * * Returns: 0 on success or a negative error code on failure. */ -int tegra_io_pad_power_disable(enum tegra_io_pad id) +int tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id) { const struct tegra_io_pad_soc *pad; unsigned long request, status; @@ -1709,6 +1843,18 @@ unlock: mutex_unlock(&pmc->powergates_lock); return err; } +EXPORT_SYMBOL(tegra_pmc_io_pad_power_disable); + +/** + * tegra_io_pad_power_disable() - disable power to I/O pad + * @id: Tegra I/O pad ID for which to disable power + * + * Returns: 0 on success or a negative error code on failure. + */ +int tegra_io_pad_power_disable(enum tegra_io_pad id) +{ + return tegra_pmc_io_pad_power_disable(pmc, id); +} EXPORT_SYMBOL(tegra_io_pad_power_disable); static int tegra_io_pad_is_powered(struct tegra_pmc *pmc, enum tegra_io_pad id) @@ -2184,9 +2330,9 @@ static int tegra_io_pad_pinconf_set(struct pinctrl_dev *pctl_dev, switch (param) { case PIN_CONFIG_MODE_LOW_POWER: if (arg) - err = tegra_io_pad_power_disable(pad->id); + err = tegra_pmc_io_pad_power_disable(pmc, pad->id); else - err = tegra_io_pad_power_enable(pad->id); + err = tegra_pmc_io_pad_power_enable(pmc, pad->id); if (err) return err; break; diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index c545875d0ff1..1fd21be02577 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -16,6 +16,7 @@ struct clk; struct reset_control; +struct tegra_pmc; bool tegra_pmc_cpu_is_powered(unsigned int cpuid); int tegra_pmc_cpu_power_on(unsigned int cpuid); @@ -149,11 +150,24 @@ enum tegra_io_pad { }; #ifdef CONFIG_SOC_TEGRA_PMC +struct tegra_pmc *devm_tegra_pmc_get(struct device *dev); + +int tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id); +int tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id); +int tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id); + +/* Must be called with clk disabled, and returns with clk enabled */ +int tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc, + unsigned int id, struct clk *clk, + struct reset_control *rst); +int tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id); +int tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id); + +/* legacy */ int tegra_powergate_power_on(unsigned int id); int tegra_powergate_power_off(unsigned int id); int tegra_powergate_remove_clamping(unsigned int id); -/* Must be called with clk disabled, and returns with clk enabled */ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, struct reset_control *rst); @@ -166,6 +180,50 @@ void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); bool tegra_pmc_core_domain_state_synced(void); #else +static inline struct tegra_pmc *devm_tegra_pmc_get(struct device *dev) +{ + return ERR_PTR(-ENOSYS); +} + +static inline int +tegra_pmc_powergate_power_on(struct tegra_pmc *pmc, unsigned int id) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_powergate_power_off(struct tegra_pmc *pmc, unsigned int id) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_powergate_remove_clamping(struct tegra_pmc *pmc, unsigned int id) +{ + return -ENOSYS; +} + +/* Must be called with clk disabled, and returns with clk enabled */ +static inline int +tegra_pmc_powergate_sequence_power_up(struct tegra_pmc *pmc, unsigned int id, + struct clk *clk, + struct reset_control *rst) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_io_pad_power_enable(struct tegra_pmc *pmc, enum tegra_io_pad id) +{ + return -ENOSYS; +} + +static inline int +tegra_pmc_io_pad_power_disable(struct tegra_pmc *pmc, enum tegra_io_pad id) +{ + return -ENOSYS; +} + static inline int tegra_powergate_power_on(unsigned int id) { return -ENOSYS; -- cgit v1.2.3 From ba99035bf16ef0d4a7f6acd56fc9292c0bd0d42e Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sat, 17 Jan 2026 18:59:10 +0100 Subject: soc: apple: rtkit: Add function to poweroff Add a function to put a co-processor into the lowest possible power state from which recovery usually isn't possible without a full SoC reset. This is required for the USB4/Thunderbolt co-processors which can be restarted since the entire USB4 root complex can be completely reset independently of the rest of the SoC. Reviewed-by: Janne Grunau Link: https://patch.msgid.link/20260117-apple-rtkit-poweroff-v2-1-b882a180e44d@kernel.org Signed-off-by: Sven Peter --- drivers/soc/apple/rtkit.c | 16 ++++++++++++++++ include/linux/soc/apple/rtkit.h | 7 +++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/soc/apple/rtkit.c b/drivers/soc/apple/rtkit.c index b8d4da147d23..4ad4f964fde7 100644 --- a/drivers/soc/apple/rtkit.c +++ b/drivers/soc/apple/rtkit.c @@ -851,6 +851,22 @@ int apple_rtkit_shutdown(struct apple_rtkit *rtk) } EXPORT_SYMBOL_GPL(apple_rtkit_shutdown); +int apple_rtkit_poweroff(struct apple_rtkit *rtk) +{ + int ret; + + ret = apple_rtkit_set_ap_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF); + if (ret) + return ret; + + ret = apple_rtkit_set_iop_power_state(rtk, APPLE_RTKIT_PWR_STATE_OFF); + if (ret) + return ret; + + return apple_rtkit_reinit(rtk); +} +EXPORT_SYMBOL_GPL(apple_rtkit_poweroff); + int apple_rtkit_idle(struct apple_rtkit *rtk) { int ret; diff --git a/include/linux/soc/apple/rtkit.h b/include/linux/soc/apple/rtkit.h index 736f53018017..bda3c528b515 100644 --- a/include/linux/soc/apple/rtkit.h +++ b/include/linux/soc/apple/rtkit.h @@ -125,6 +125,13 @@ int apple_rtkit_wake(struct apple_rtkit *rtk); */ int apple_rtkit_shutdown(struct apple_rtkit *rtk); +/* + * Put the co-processor into the lowest power state. Note that it usually + * is not possible to recover from this state without a full SoC reset. + */ + +int apple_rtkit_poweroff(struct apple_rtkit *rtk); + /* * Put the co-processor into idle mode */ -- cgit v1.2.3 From 266f35701b6f7ddd9521310eb5add01001d4a614 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:30 +0800 Subject: mailbox: mtk-cmdq: Add cmdq private data to cmdq_pkt for generating instruction Add the cmdq_mbox_priv structure to store the private data of GCE, such as the shift bits of the physical address. Then, include the cmdq_mbox_priv structure within the cmdq_pkt structure. This allows CMDQ users to utilize the private data in cmdq_pkt to generate GCE instructions when needed. Additionally, having cmdq_mbox_priv makes it easier to expand and reference other GCE private data in the future. Add cmdq_get_mbox_priv() for CMDQ users to get all the private data into the cmdq_mbox_priv of the cmdq_pkt. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/mailbox/mtk-cmdq-mailbox.c | 8 ++++++++ include/linux/mailbox/mtk-cmdq-mailbox.h | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 5791f80f995a..95e8a5331b7c 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -104,6 +104,14 @@ static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *p return (dma_addr_t)addr << pdata->shift; } +void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) +{ + struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); + + priv->shift_pa = cmdq->pdata->shift; +} +EXPORT_SYMBOL(cmdq_get_mbox_priv); + u8 cmdq_get_shift_pa(struct mbox_chan *chan) { struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h index e1555e06e7e5..73b70be4a8a7 100644 --- a/include/linux/mailbox/mtk-cmdq-mailbox.h +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h @@ -70,13 +70,31 @@ struct cmdq_cb_data { struct cmdq_pkt *pkt; }; +struct cmdq_mbox_priv { + u8 shift_pa; +}; + struct cmdq_pkt { void *va_base; dma_addr_t pa_base; size_t cmd_buf_size; /* command occupied size */ size_t buf_size; /* real buffer size */ + struct cmdq_mbox_priv priv; /* for generating instruction */ }; +/** + * cmdq_get_mbox_priv() - get the private data of mailbox channel + * @chan: mailbox channel + * @priv: pointer to store the private data of mailbox channel + * + * While generating the GCE instruction to command buffer, the private data + * of GCE hardware may need to be referenced, such as the shift bits of + * physical address. + * + * This function should be called before generating the GCE instruction. + */ +void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv); + /** * cmdq_get_shift_pa() - get the shift bits of physical address * @chan: mailbox channel -- cgit v1.2.3 From 1c1874843bc43d9f333d441af00f61ece2373e5d Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:32 +0800 Subject: mailbox: mtk-cmdq: Add mminfra_offset configuration for DRAM transaction The GCE in MT8196 is placed in MMINFRA and requires all addresses in GCE instructions for DRAM transactions to be IOVA. Due to MMIO, if the GCE needs to access a hardware register at 0x1000_0000, but the SMMU is also mapping a DRAM block at 0x1000_0000, the MMINFRA will not know whether to write to the hardware register or the DRAM. To solve this, MMINFRA treats addresses greater than 2G as data paths and those less than 2G as config paths because the DRAM start address is currently at 2G (0x8000_0000). On the data path, MMINFRA remaps DRAM addresses by subtracting 2G, allowing SMMU to map DRAM addresses less than 2G. For example, if the DRAM start address 0x8000_0000 is mapped to IOVA=0x0, when GCE accesses IOVA=0x0, it must add a 2G offset to the address in the GCE instruction. MMINFRA will then see it as a data path (IOVA >= 2G) and subtract 2G, allowing GCE to access IOVA=0x0. Since the MMINFRA remap subtracting 2G is done in hardware and cannot be configured by software, the address of DRAM in GCE instruction must always add 2G to ensure proper access. After that, the shift functions do more than just shift addresses, so the APIs were renamed to cmdq_convert_gce_addr() and cmdq_revert_gce_addr(). This 2G adjustment is referred to as mminfra_offset in the CMDQ driver. CMDQ helper can get the mminfra_offset from the cmdq_mbox_priv of cmdq_pkt and add the mminfra_offset to the DRAM address in GCE instructions. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/mailbox/mtk-cmdq-mailbox.c | 6 ++++-- include/linux/mailbox/mtk-cmdq-mailbox.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index a544108ddae7..a9c06e4bbad4 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -94,6 +94,7 @@ struct cmdq { struct gce_plat { u32 thread_nr; u8 shift; + dma_addr_t mminfra_offset; bool control_by_sw; bool sw_ddr_en; bool gce_vm; @@ -103,13 +104,13 @@ struct gce_plat { static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata) { /* Convert DMA addr (PA or IOVA) to GCE readable addr */ - return addr >> pdata->shift; + return (addr + pdata->mminfra_offset) >> pdata->shift; } static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata) { /* Revert GCE readable addr to DMA addr (PA or IOVA) */ - return (dma_addr_t)addr << pdata->shift; + return ((dma_addr_t)addr << pdata->shift) - pdata->mminfra_offset; } void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) @@ -117,6 +118,7 @@ void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); priv->shift_pa = cmdq->pdata->shift; + priv->mminfra_offset = cmdq->pdata->mminfra_offset; } EXPORT_SYMBOL(cmdq_get_mbox_priv); diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h index 73b70be4a8a7..07c1bfbdb8c4 100644 --- a/include/linux/mailbox/mtk-cmdq-mailbox.h +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h @@ -72,6 +72,7 @@ struct cmdq_cb_data { struct cmdq_mbox_priv { u8 shift_pa; + dma_addr_t mminfra_offset; }; struct cmdq_pkt { -- cgit v1.2.3 From 4bf783d8415cc397334b375a05f0b2321fc6c319 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:35 +0800 Subject: soc: mediatek: mtk-cmdq: Add pa_base parsing for hardware without subsys ID support When GCE executes instructions, it typically locates the corresponding hardware register using the subsys ID. For hardware that does not support subsys ID, the subsys ID is set to an invalid value, and the physical address must be used to generate GCE instructions. The main advantage of using subsys ID is to reduce the number of instructions. Without subsys ID, an additional `ASSIGN` instruction is needed to assign the high bytes of the physical address, which can impact performance if too many instructions are required. However, if the hardware does not support subsys ID, using the physical address is the only option to achieve the same functionality. This commit adds a pa_base parsing flow to the cmdq_client_reg structure to handle hardware without subsys ID support. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-cmdq-helper.c | 16 ++++++++++++++-- include/linux/soc/mediatek/mtk-cmdq.h | 3 +++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 8feeaa320359..80806fbeba91 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #define CMDQ_WRITE_ENABLE_MASK BIT(0) @@ -60,20 +61,31 @@ int cmdq_dev_get_client_reg(struct device *dev, struct cmdq_client_reg *client_reg, int idx) { struct of_phandle_args spec; + struct resource res; int err; if (!client_reg) return -ENOENT; + err = of_address_to_resource(dev->of_node, 0, &res); + if (err) { + dev_err(dev, "Missing reg in %s node\n", dev->of_node->full_name); + return -EINVAL; + } + client_reg->pa_base = res.start; + err = of_parse_phandle_with_fixed_args(dev->of_node, "mediatek,gce-client-reg", 3, idx, &spec); if (err < 0) { - dev_warn(dev, + dev_dbg(dev, "error %d can't parse gce-client-reg property (%d)", err, idx); - return err; + /* make subsys invalid */ + client_reg->subsys = CMDQ_SUBSYS_INVALID; + + return 0; } client_reg->subsys = (u8)spec.args[0]; diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h index 0c3906e8ad19..3578cc9200e9 100644 --- a/include/linux/soc/mediatek/mtk-cmdq.h +++ b/include/linux/soc/mediatek/mtk-cmdq.h @@ -23,6 +23,8 @@ #define CMDQ_THR_SPR_IDX2 (2) #define CMDQ_THR_SPR_IDX3 (3) +#define CMDQ_SUBSYS_INVALID (U8_MAX) + struct cmdq_pkt; enum cmdq_logic_op { @@ -52,6 +54,7 @@ struct cmdq_operand { struct cmdq_client_reg { u8 subsys; + phys_addr_t pa_base; u16 offset; u16 size; }; -- cgit v1.2.3 From 40dc5bbad63b5f60dd2e69a32def1a2673cba09e Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:36 +0800 Subject: soc: mediatek: mtk-cmdq: Extend cmdq_pkt_write API for SoCs without subsys ID This patch extends the cmdq_pkt_write API to support SoCs that do not have subsys ID mapping by introducing new register write APIs: - cmdq_pkt_write_pa() and cmdq_pkt_write_subsys() replace cmdq_pkt_write() - cmdq_pkt_write_mask_pa() and cmdq_pkt_write_mask_subsys() replace cmdq_pkt_write_mask() To ensure consistent function pointer interfaces, both cmdq_pkt_write_pa() and cmdq_pkt_write_subsys() provide subsys and pa_base parameters. This unifies how register writes are invoked, regardless of whether subsys ID is supported by the device. All GCEs support writing registers by PA (with mask) without subsys, but this requires extra GCE instructions to convert the PA into a GCE readable format, reducing performance compared to using subsys directly. Therefore, subsys is preferred for register writes when available. API documentation and function pointer declarations in cmdq_client_reg have been updated. The original write APIs will be removed after all CMDQ users transition to the new interfaces. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Acked-by: Jassi Brar Acked-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-cmdq-helper.c | 54 ++++++++++++++++++++ include/linux/soc/mediatek/mtk-cmdq.h | 90 ++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) (limited to 'include') diff --git a/drivers/soc/mediatek/mtk-cmdq-helper.c b/drivers/soc/mediatek/mtk-cmdq-helper.c index 80806fbeba91..b30715ef0d6f 100644 --- a/drivers/soc/mediatek/mtk-cmdq-helper.c +++ b/drivers/soc/mediatek/mtk-cmdq-helper.c @@ -85,6 +85,16 @@ int cmdq_dev_get_client_reg(struct device *dev, /* make subsys invalid */ client_reg->subsys = CMDQ_SUBSYS_INVALID; + /* + * All GCEs support writing register PA with mask without subsys, + * but this requires extra GCE instructions to convert the PA into + * a format that GCE can handle, which is less performance than + * directly using subsys. Therefore, when subsys is available, + * we prefer to use subsys for writing register PA. + */ + client_reg->pkt_write = cmdq_pkt_write_pa; + client_reg->pkt_write_mask = cmdq_pkt_write_mask_pa; + return 0; } @@ -93,6 +103,9 @@ int cmdq_dev_get_client_reg(struct device *dev, client_reg->size = (u16)spec.args[2]; of_node_put(spec.np); + client_reg->pkt_write = cmdq_pkt_write_subsys; + client_reg->pkt_write_mask = cmdq_pkt_write_mask_subsys; + return 0; } EXPORT_SYMBOL(cmdq_dev_get_client_reg); @@ -214,6 +227,26 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value) } EXPORT_SYMBOL(cmdq_pkt_write); +int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base, + u16 offset, u32 value) +{ + int err; + + err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base)); + if (err < 0) + return err; + + return cmdq_pkt_write_s_value(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_LOW(offset), value); +} +EXPORT_SYMBOL(cmdq_pkt_write_pa); + +int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/, + u16 offset, u32 value) +{ + return cmdq_pkt_write(pkt, subsys, offset, value); +} +EXPORT_SYMBOL(cmdq_pkt_write_subsys); + int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value, u32 mask) { @@ -231,6 +264,27 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, } EXPORT_SYMBOL(cmdq_pkt_write_mask); +int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base, + u16 offset, u32 value, u32 mask) +{ + int err; + + err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base)); + if (err < 0) + return err; + + return cmdq_pkt_write_s_mask_value(pkt, CMDQ_THR_SPR_IDX0, + CMDQ_ADDR_LOW(offset), value, mask); +} +EXPORT_SYMBOL(cmdq_pkt_write_mask_pa); + +int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/, + u16 offset, u32 value, u32 mask) +{ + return cmdq_pkt_write_mask(pkt, subsys, offset, value, mask); +} +EXPORT_SYMBOL(cmdq_pkt_write_mask_subsys); + int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low, u16 reg_idx) { diff --git a/include/linux/soc/mediatek/mtk-cmdq.h b/include/linux/soc/mediatek/mtk-cmdq.h index 3578cc9200e9..a06b5a61f337 100644 --- a/include/linux/soc/mediatek/mtk-cmdq.h +++ b/include/linux/soc/mediatek/mtk-cmdq.h @@ -57,6 +57,17 @@ struct cmdq_client_reg { phys_addr_t pa_base; u16 offset; u16 size; + + /* + * Client only uses these functions for MMIO access, + * so doesn't need to handle the mminfra_offset. + * The mminfra_offset is used for DRAM access and + * is handled internally by CMDQ APIs. + */ + int (*pkt_write)(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base, + u16 offset, u32 value); + int (*pkt_write_mask)(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base, + u16 offset, u32 value, u32 mask); }; struct cmdq_client { @@ -124,6 +135,32 @@ void cmdq_pkt_destroy(struct cmdq_client *client, struct cmdq_pkt *pkt); */ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value); +/** + * cmdq_pkt_write_pa() - append write command to the CMDQ packet with pa_base + * @pkt: the CMDQ packet + * @subsys: unused parameter + * @pa_base: the physical address base of the hardware register + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value); + +/** + * cmdq_pkt_write_subsys() - append write command to the CMDQ packet with subsys + * @pkt: the CMDQ packet + * @subsys: the CMDQ sub system code + * @pa_base: unused parameter + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, u32 value); + /** * cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet * @pkt: the CMDQ packet @@ -137,6 +174,34 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value); int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value, u32 mask); +/** + * cmdq_pkt_write_mask_pa() - append write command with mask to the CMDQ packet with pa + * @pkt: the CMDQ packet + * @subsys: unused parameter + * @pa_base: the physical address base of the hardware register + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * @mask: the specified target register mask + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value, u32 mask); + +/** + * cmdq_pkt_write_mask_subsys() - append write command with mask to the CMDQ packet with subsys + * @pkt: the CMDQ packet + * @subsys: the CMDQ sub system code + * @pa_base: unused parameter + * @offset: register offset from CMDQ sub system + * @value: the specified target register value + * @mask: the specified target register mask + * + * Return: 0 for success; else the error code is returned + */ +int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, u32 value, u32 mask); + /* * cmdq_pkt_read_s() - append read_s command to the CMDQ packet * @pkt: the CMDQ packet @@ -421,12 +486,37 @@ static inline int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u3 return -ENOENT; } +static inline int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value) +{ + return -ENOENT; +} + +static inline int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, u32 value) +{ + return -ENOENT; +} + static inline int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value, u32 mask) { return -ENOENT; } +static inline int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, + u32 pa_base, u16 offset, u32 value, u32 mask) +{ + return -ENOENT; +} + +static inline int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, + u32 pa_base /*unused*/, u16 offset, + u32 value, u32 mask) +{ + return -ENOENT; +} + static inline int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low, u16 reg_idx) { -- cgit v1.2.3 From 216e0a5e98e5f0f02a818884e8acf340892cecae Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Tue, 20 Jan 2026 19:10:49 +0800 Subject: dt-bindings: soc: spacemit: Add K3 reset support and IDs Update the spacemit,k1-syscon.yaml binding to document K3 SoC reset support. K3 reset devices are registered at runtime as auxiliary devices by the K3 CCU driver. Since K3 reuses the K1 syscon binding, there is no separate YAML binding file for K3 resets. Update #reset-cells description to document where reset IDs are defined. Acked-by: Alex Elder Acked-by: Krzysztof Kozlowski Signed-off-by: Guodong Xu Reviewed-by: Yixun Lan Link: https://lore.kernel.org/spacemit/20260114092742-GYC7933267@gentoo.org/ [1] Signed-off-by: Philipp Zabel --- .../bindings/soc/spacemit/spacemit,k1-syscon.yaml | 8 +- include/dt-bindings/reset/spacemit,k3-resets.h | 171 +++++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/reset/spacemit,k3-resets.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml b/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml index 133a391ee68c..f0c6feb99be3 100644 --- a/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml +++ b/Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml @@ -10,7 +10,7 @@ maintainers: - Haylen Chu description: - System controllers found on SpacemiT K1 SoC, which are capable of + System controllers found on SpacemiT K1/K3 SoC, which are capable of clock, reset and power-management functions. properties: @@ -46,6 +46,12 @@ properties: "#reset-cells": const: 1 + description: | + ID of the reset controller line. Valid IDs are defined in corresponding + files: + + For SpacemiT K1, see include/dt-bindings/clock/spacemit,k1-syscon.h + For SpacemiT K3, see include/dt-bindings/reset/spacemit,k3-resets.h required: - compatible diff --git a/include/dt-bindings/reset/spacemit,k3-resets.h b/include/dt-bindings/reset/spacemit,k3-resets.h new file mode 100644 index 000000000000..79ac1c22b7b5 --- /dev/null +++ b/include/dt-bindings/reset/spacemit,k3-resets.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2025 SpacemiT Technology Co. Ltd + */ + +#ifndef _DT_BINDINGS_RESET_SPACEMIT_K3_RESETS_H_ +#define _DT_BINDINGS_RESET_SPACEMIT_K3_RESETS_H_ + +/* MPMU resets */ +#define RESET_MPMU_WDT 0 +#define RESET_MPMU_RIPC 1 + +/* APBC resets */ +#define RESET_APBC_UART0 0 +#define RESET_APBC_UART2 1 +#define RESET_APBC_UART3 2 +#define RESET_APBC_UART4 3 +#define RESET_APBC_UART5 4 +#define RESET_APBC_UART6 5 +#define RESET_APBC_UART7 6 +#define RESET_APBC_UART8 7 +#define RESET_APBC_UART9 8 +#define RESET_APBC_UART10 9 +#define RESET_APBC_GPIO 10 +#define RESET_APBC_PWM0 11 +#define RESET_APBC_PWM1 12 +#define RESET_APBC_PWM2 13 +#define RESET_APBC_PWM3 14 +#define RESET_APBC_PWM4 15 +#define RESET_APBC_PWM5 16 +#define RESET_APBC_PWM6 17 +#define RESET_APBC_PWM7 18 +#define RESET_APBC_PWM8 19 +#define RESET_APBC_PWM9 20 +#define RESET_APBC_PWM10 21 +#define RESET_APBC_PWM11 22 +#define RESET_APBC_PWM12 23 +#define RESET_APBC_PWM13 24 +#define RESET_APBC_PWM14 25 +#define RESET_APBC_PWM15 26 +#define RESET_APBC_PWM16 27 +#define RESET_APBC_PWM17 28 +#define RESET_APBC_PWM18 29 +#define RESET_APBC_PWM19 30 +#define RESET_APBC_SPI0 31 +#define RESET_APBC_SPI1 32 +#define RESET_APBC_SPI3 33 +#define RESET_APBC_RTC 34 +#define RESET_APBC_TWSI0 35 +#define RESET_APBC_TWSI1 36 +#define RESET_APBC_TWSI2 37 +#define RESET_APBC_TWSI4 38 +#define RESET_APBC_TWSI5 39 +#define RESET_APBC_TWSI6 40 +#define RESET_APBC_TWSI8 41 +#define RESET_APBC_TIMERS0 42 +#define RESET_APBC_TIMERS1 43 +#define RESET_APBC_TIMERS2 44 +#define RESET_APBC_TIMERS3 45 +#define RESET_APBC_TIMERS4 46 +#define RESET_APBC_TIMERS5 47 +#define RESET_APBC_TIMERS6 48 +#define RESET_APBC_TIMERS7 49 +#define RESET_APBC_AIB 50 +#define RESET_APBC_ONEWIRE 51 +#define RESET_APBC_I2S0 52 +#define RESET_APBC_I2S1 53 +#define RESET_APBC_I2S2 54 +#define RESET_APBC_I2S3 55 +#define RESET_APBC_I2S4 56 +#define RESET_APBC_I2S5 57 +#define RESET_APBC_DRO 58 +#define RESET_APBC_IR0 59 +#define RESET_APBC_IR1 60 +#define RESET_APBC_TSEN 61 +#define RESET_IPC_AP2AUD 62 +#define RESET_APBC_CAN0 63 +#define RESET_APBC_CAN1 64 +#define RESET_APBC_CAN2 65 +#define RESET_APBC_CAN3 66 +#define RESET_APBC_CAN4 67 + +/* APMU resets */ +#define RESET_APMU_CSI 0 +#define RESET_APMU_CCIC2PHY 1 +#define RESET_APMU_CCIC3PHY 2 +#define RESET_APMU_ISP_CIBUS 3 +#define RESET_APMU_DSI_ESC 4 +#define RESET_APMU_LCD 5 +#define RESET_APMU_V2D 6 +#define RESET_APMU_LCD_MCLK 7 +#define RESET_APMU_LCD_DSCCLK 8 +#define RESET_APMU_SC2_HCLK 9 +#define RESET_APMU_CCIC_4X 10 +#define RESET_APMU_CCIC1_PHY 11 +#define RESET_APMU_SDH_AXI 12 +#define RESET_APMU_SDH0 13 +#define RESET_APMU_SDH1 14 +#define RESET_APMU_SDH2 15 +#define RESET_APMU_USB2 16 +#define RESET_APMU_USB3_PORTA 17 +#define RESET_APMU_USB3_PORTB 18 +#define RESET_APMU_USB3_PORTC 19 +#define RESET_APMU_USB3_PORTD 20 +#define RESET_APMU_QSPI 21 +#define RESET_APMU_QSPI_BUS 22 +#define RESET_APMU_DMA 23 +#define RESET_APMU_AES_WTM 24 +#define RESET_APMU_MCB_DCLK 25 +#define RESET_APMU_MCB_ACLK 26 +#define RESET_APMU_VPU 27 +#define RESET_APMU_DTC 28 +#define RESET_APMU_GPU 29 +#define RESET_APMU_ALZO 30 +#define RESET_APMU_MC 31 +#define RESET_APMU_CPU0_POP 32 +#define RESET_APMU_CPU0_SW 33 +#define RESET_APMU_CPU1_POP 34 +#define RESET_APMU_CPU1_SW 35 +#define RESET_APMU_CPU2_POP 36 +#define RESET_APMU_CPU2_SW 37 +#define RESET_APMU_CPU3_POP 38 +#define RESET_APMU_CPU3_SW 39 +#define RESET_APMU_C0_MPSUB_SW 40 +#define RESET_APMU_CPU4_POP 41 +#define RESET_APMU_CPU4_SW 42 +#define RESET_APMU_CPU5_POP 43 +#define RESET_APMU_CPU5_SW 44 +#define RESET_APMU_CPU6_POP 45 +#define RESET_APMU_CPU6_SW 46 +#define RESET_APMU_CPU7_POP 47 +#define RESET_APMU_CPU7_SW 48 +#define RESET_APMU_C1_MPSUB_SW 49 +#define RESET_APMU_MPSUB_DBG 50 +#define RESET_APMU_UCIE 51 +#define RESET_APMU_RCPU 52 +#define RESET_APMU_DSI4LN2_ESCCLK 53 +#define RESET_APMU_DSI4LN2_LCD_SW 54 +#define RESET_APMU_DSI4LN2_LCD_MCLK 55 +#define RESET_APMU_DSI4LN2_LCD_DSCCLK 56 +#define RESET_APMU_DSI4LN2_DPU_ACLK 57 +#define RESET_APMU_DPU_ACLK 58 +#define RESET_APMU_UFS_ACLK 59 +#define RESET_APMU_EDP0 60 +#define RESET_APMU_EDP1 61 +#define RESET_APMU_PCIE_PORTA 62 +#define RESET_APMU_PCIE_PORTB 63 +#define RESET_APMU_PCIE_PORTC 64 +#define RESET_APMU_PCIE_PORTD 65 +#define RESET_APMU_PCIE_PORTE 66 +#define RESET_APMU_EMAC0 67 +#define RESET_APMU_EMAC1 68 +#define RESET_APMU_EMAC2 69 +#define RESET_APMU_ESPI_MCLK 70 +#define RESET_APMU_ESPI_SCLK 71 + +/* DCIU resets*/ +#define RESET_DCIU_HDMA 0 +#define RESET_DCIU_DMA350 1 +#define RESET_DCIU_DMA350_0 2 +#define RESET_DCIU_DMA350_1 3 +#define RESET_DCIU_AXIDMA0 4 +#define RESET_DCIU_AXIDMA1 5 +#define RESET_DCIU_AXIDMA2 6 +#define RESET_DCIU_AXIDMA3 7 +#define RESET_DCIU_AXIDMA4 8 +#define RESET_DCIU_AXIDMA5 9 +#define RESET_DCIU_AXIDMA6 10 +#define RESET_DCIU_AXIDMA7 11 + +#endif /* _DT_BINDINGS_RESET_SPACEMIT_K3_H_ */ -- cgit v1.2.3