From beb6c1bac2b6e3cc53b1bca918547c8a58be4d3d Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Fri, 8 May 2026 16:10:46 +0530 Subject: [PATCH 001/157] dt-bindings: soc: qcom: smd-rpm: Add Shikra rpm-smd compatible Add compatible for the Qualcomm Shikra rpm-smd device. Signed-off-by: Komal Bajaj Signed-off-by: Sneh Mankad Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260508-shikra_mailbox_and_rpm_changes-v3-1-698f8e5fb339@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml index 270bcd079f88..bd1d32898461 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,smd-rpm.yaml @@ -57,6 +57,7 @@ properties: - qcom,rpm-qcm2290 - qcom,rpm-qcs404 - qcom,rpm-sdm660 + - qcom,rpm-shikra - qcom,rpm-sm6115 - qcom,rpm-sm6125 - qcom,rpm-sm6375 From d92f87f3e7c79f58cc6d6edad4bf1c96f795aa7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 16 Jun 2026 16:38:29 +0200 Subject: [PATCH 002/157] tee: qcomtee: Drop unused assignment of platform_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct platform_device_id to zero without relying on that value. Drop this unused assignment. While touching this array unify spacing and usage of commas and use a named initializer for .name for improved readability. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Amirreza Zarrabi Signed-off-by: Jens Wiklander --- drivers/tee/qcomtee/call.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c index 0efc5646242a..4a597eeaf174 100644 --- a/drivers/tee/qcomtee/call.c +++ b/drivers/tee/qcomtee/call.c @@ -798,7 +798,12 @@ static void qcomtee_remove(struct platform_device *pdev) kfree(qcomtee); } -static const struct platform_device_id qcomtee_ids[] = { { "qcomtee", 0 }, {} }; +static const struct platform_device_id qcomtee_ids[] = { + { + .name = "qcomtee", + }, + { } +}; MODULE_DEVICE_TABLE(platform, qcomtee_ids); static struct platform_driver qcomtee_platform_driver = { From fa476d53edd24e8105faace04e881b9c4179738f Mon Sep 17 00:00:00 2001 From: Weigang He Date: Wed, 10 Jun 2026 00:38:52 +1000 Subject: [PATCH 003/157] soc: samsung: exynos-pmu: fix of_node refcount leak in exynos_get_pmu_regmap() exynos_get_pmu_regmap() obtains a device_node via of_find_matching_node() and passes it to exynos_get_pmu_regmap_by_phandle(np, NULL). With propname == NULL the callee uses np directly and does not drop a reference, so the reference taken by of_find_matching_node() is leaked on every call -- including on each -EPROBE_DEFER retry of the only in-tree caller, exynos_retention_init() in the Exynos pinctrl driver. Annotate np with the __free(device_node) cleanup attribute so the reference is released when the function returns. Found by static analysis tool CodeQL. Fixes: 76640b84bd7a ("soc: samsung: pmu: Provide global function to get PMU regmap") Signed-off-by: Weigang He Link: https://patch.msgid.link/20260609143852.1783558-1-geoffreyhe2@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/exynos-pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index d58376c38179..f5fcdde9750e 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -167,8 +167,8 @@ static const struct mfd_cell exynos_pmu_devs[] = { */ struct regmap *exynos_get_pmu_regmap(void) { - struct device_node *np = of_find_matching_node(NULL, - exynos_pmu_of_device_ids); + struct device_node *np __free(device_node) = + of_find_matching_node(NULL, exynos_pmu_of_device_ids); if (np) return exynos_get_pmu_regmap_by_phandle(np, NULL); return ERR_PTR(-ENODEV); From d3ebfcb66147497c1f6a4ceaed9a499cec2b3fe2 Mon Sep 17 00:00:00 2001 From: Sanghoon Bae Date: Wed, 10 Jun 2026 14:03:25 +0900 Subject: [PATCH 004/157] dt-bindings: soc: samsung: exynos-sysreg: Add hsi0 for ExynosAutov920 Add hsi0 compatible on samsung,exynos-sysreg To support ExynosAutov920 PCIe settings: - PCIe PHY power control - PLL settings for PCIe - PCIe device direction (RC/EP) Signed-off-by: Sanghoon Bae Reviewed-by: Peter Griffin Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260610050326.2903402-2-sh86.bae@samsung.com Signed-off-by: Krzysztof Kozlowski --- .../devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml b/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml index 9c63dbcd4d77..fcdb9ffd2644 100644 --- a/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/samsung,exynos-sysreg.yaml @@ -37,6 +37,7 @@ properties: - samsung,exynos8895-peric1-sysreg - samsung,exynos990-peric0-sysreg - samsung,exynos990-peric1-sysreg + - samsung,exynosautov920-hsi0-sysreg - samsung,exynosautov920-hsi2-sysreg - samsung,exynosautov920-peric0-sysreg - samsung,exynosautov920-peric1-sysreg From f52ddaf5e771c753663a719834848fde92b219f8 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 16 Jun 2026 08:47:41 +0800 Subject: [PATCH 005/157] firmware: ti_sci: Undo list publication on populate failure ti_sci_probe() publishes the controller on the global ti_sci_list before creating child devices. If of_platform_populate() fails after creating some children, the probe error path leaves the children around and leaves the failed controller visible through ti_sci_get_handle(). Depopulate any children created by the failed populate call and remove the controller from the global list before returning the probe error. Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260616004741.1726-1-pengpeng@iscas.ac.cn Signed-off-by: Nishanth Menon --- drivers/firmware/ti_sci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 590a464403c5..cc747ab0237f 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -4225,6 +4225,10 @@ static int ti_sci_probe(struct platform_device *pdev) ret = of_platform_populate(dev->of_node, NULL, NULL, dev); if (ret) { dev_err(dev, "platform_populate failed %pe\n", ERR_PTR(ret)); + of_platform_depopulate(dev); + mutex_lock(&ti_sci_list_mutex); + list_del(&info->node); + mutex_unlock(&ti_sci_list_mutex); goto out; } return 0; From 1527acf2295cf2d6e11e3e9b121d63709667e6e7 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Thu, 18 Jun 2026 02:22:02 +0800 Subject: [PATCH 006/157] memory: stm32_omm: initialize ret in stm32_omm_set_amcr stm32_omm_set_amcr() returns ret after checking whether the AMCR value matches the device tree description. On the normal matching path ret is not otherwise assigned, so initialize it to 0 before the checks. Signed-off-by: Ruoyu Wang Reviewed-by: Patrice Chotard Link: https://patch.msgid.link/20260617182202.961843-1-ruoyuw560@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/stm32_omm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/memory/stm32_omm.c b/drivers/memory/stm32_omm.c index 5d06623f3f68..2a1af229d244 100644 --- a/drivers/memory/stm32_omm.c +++ b/drivers/memory/stm32_omm.c @@ -47,7 +47,7 @@ static int stm32_omm_set_amcr(struct device *dev, bool set) struct device_node *node; struct resource res, res1; unsigned int syscon_args[2]; - int ret, idx; + int ret = 0, idx; unsigned int i, amcr, read_amcr; for (i = 0; i < omm->nb_child; i++) { From 9706c38004ca3a760cc6c6baee24648892526875 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 22 Apr 2026 10:33:31 +0200 Subject: [PATCH 007/157] soc: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options: QCOM, Qualcomm and Qualcomm Technologies. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260422083330.84247-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 2caadbbcf830..f6970431d07d 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -27,7 +27,7 @@ config QCOM_COMMAND_DB SoC specific identifier and information for the shared resources. config QCOM_GENI_SE - tristate "QCOM GENI Serial Engine Driver" + tristate "Qualcomm GENI Serial Engine Driver" depends on ARCH_QCOM || COMPILE_TEST help This driver is used to manage Generic Interface (GENI) firmware based @@ -36,7 +36,7 @@ config QCOM_GENI_SE Engines present in the QUP. config QCOM_GSBI - tristate "QCOM General Serial Bus Interface" + tristate "Qualcomm General Serial Bus Interface" depends on ARCH_QCOM || COMPILE_TEST select MFD_SYSCON help @@ -45,7 +45,7 @@ config QCOM_GSBI devices to the output pins. config QCOM_LLCC - tristate "Qualcomm Technologies, Inc. LLCC driver" + tristate "Qualcomm LLCC driver" depends on ARCH_QCOM || COMPILE_TEST select REGMAP_MMIO help @@ -237,7 +237,7 @@ config QCOM_SPM Adaptive Voltage Scaler parameters, where supported. config QCOM_STATS - tristate "Qualcomm Technologies, Inc. (QTI) Sleep stats driver" + tristate "Qualcomm Sleep stats driver" depends on (ARCH_QCOM && DEBUG_FS) || COMPILE_TEST depends on QCOM_SMEM depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n @@ -268,7 +268,7 @@ config QCOM_APR ASM, ADM and AFE modules. config QCOM_ICC_BWMON - tristate "QCOM Interconnect Bandwidth Monitor driver" + tristate "Qualcomm Interconnect Bandwidth Monitor driver" depends on ARCH_QCOM || COMPILE_TEST select PM_OPP select REGMAP_MMIO @@ -287,7 +287,7 @@ config QCOM_INLINE_CRYPTO_ENGINE select QCOM_SCM config QCOM_PBS - tristate "PBS trigger support for Qualcomm Technologies, Inc. PMICS" + tristate "PBS trigger support for Qualcomm PMICs" depends on SPMI help This driver supports configuring software programmable boot sequencer (PBS) From 457462828d08514fbf53a385c0cd39bf597adf63 Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Tue, 21 Apr 2026 16:17:41 +0800 Subject: [PATCH 008/157] dt-bindings: soc: amlogic: clk-measure: Add A1 and T7 compatible Add the Amlogic A1 and T7 compatible for the clk-measurer IP. Acked-by: Conor Dooley Signed-off-by: Jian Hu Link: https://patch.msgid.link/20260421-clkmsr_a1_t7-v3-1-efc00b0f9e6b@amlogic.com Signed-off-by: Neil Armstrong --- .../bindings/soc/amlogic/amlogic,meson-gx-clk-measure.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-clk-measure.yaml b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-clk-measure.yaml index 39d4637c2d08..b1200e6940ac 100644 --- a/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-clk-measure.yaml +++ b/Documentation/devicetree/bindings/soc/amlogic/amlogic,meson-gx-clk-measure.yaml @@ -24,6 +24,8 @@ properties: - amlogic,meson-sm1-clk-measure - amlogic,c3-clk-measure - amlogic,s4-clk-measure + - amlogic,a1-clk-measure + - amlogic,t7-clk-measure reg: maxItems: 1 From 8bfdc0393b3f3d6aaa1203e889ef9e96a50b28a2 Mon Sep 17 00:00:00 2001 From: Jian Hu Date: Tue, 21 Apr 2026 16:17:42 +0800 Subject: [PATCH 009/157] soc: amlogic: clk-measure: Add A1 and T7 support Add support for the A1 and T7 SoC family in amlogic clk measure. Reviewed-by: Neil Armstrong Signed-off-by: Jian Hu Link: https://patch.msgid.link/20260421-clkmsr_a1_t7-v3-2-efc00b0f9e6b@amlogic.com Signed-off-by: Neil Armstrong --- drivers/soc/amlogic/meson-clk-measure.c | 272 ++++++++++++++++++++++++ 1 file changed, 272 insertions(+) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index d862e30a244e..8c4f3cc8c8ab 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -787,6 +787,258 @@ static const struct meson_msr_id clk_msr_s4[] = { }; +static const struct meson_msr_id clk_msr_a1[] = { + CLK_MSR_ID(0, "tdmout_b_sclk"), + CLK_MSR_ID(1, "tdmout_a_sclk"), + CLK_MSR_ID(2, "tdmin_lb_sclk"), + CLK_MSR_ID(3, "tdmin_b_sclk"), + CLK_MSR_ID(4, "tdmin_a_sclk"), + CLK_MSR_ID(5, "vad"), + CLK_MSR_ID(6, "resamplea"), + CLK_MSR_ID(7, "pdm_sysclk"), + CLK_MSR_ID(8, "pdm_dclk"), + CLK_MSR_ID(9, "locker_out"), + CLK_MSR_ID(10, "locker_in"), + CLK_MSR_ID(11, "spdifin"), + CLK_MSR_ID(12, "tdmin_vad"), + CLK_MSR_ID(13, "au_adc"), + CLK_MSR_ID(14, "au_dac"), + CLK_MSR_ID(16, "spicc_a"), + CLK_MSR_ID(17, "spifc"), + CLK_MSR_ID(18, "sd_emmc_a"), + CLK_MSR_ID(19, "dmcx4"), + CLK_MSR_ID(20, "dmc"), + CLK_MSR_ID(21, "psram"), + CLK_MSR_ID(22, "cecb"), + CLK_MSR_ID(23, "ceca"), + CLK_MSR_ID(24, "ts"), + CLK_MSR_ID(25, "pwm_f"), + CLK_MSR_ID(26, "pwm_e"), + CLK_MSR_ID(27, "pwm_d"), + CLK_MSR_ID(28, "pwm_c"), + CLK_MSR_ID(29, "pwm_b"), + CLK_MSR_ID(30, "pwm_a"), + CLK_MSR_ID(31, "saradc"), + CLK_MSR_ID(32, "usb_bus"), + CLK_MSR_ID(33, "dsp_b"), + CLK_MSR_ID(34, "dsp_a"), + CLK_MSR_ID(35, "axi"), + CLK_MSR_ID(36, "sys"), + CLK_MSR_ID(40, "rng_ring_osc0"), + CLK_MSR_ID(41, "rng_ring_osc1"), + CLK_MSR_ID(42, "rng_ring_osc2"), + CLK_MSR_ID(43, "rng_ring_osc3"), + CLK_MSR_ID(44, "dds_out"), + CLK_MSR_ID(45, "cpu_clk_div16"), + CLK_MSR_ID(46, "gpio_msr"), + CLK_MSR_ID(50, "osc_ring_cpu0"), + CLK_MSR_ID(51, "osc_ring_cpu1"), + CLK_MSR_ID(54, "osc_ring_top0"), + CLK_MSR_ID(55, "osc_ring_top1"), + CLK_MSR_ID(56, "osc_ring_ddr"), + CLK_MSR_ID(57, "osc_ring_dmc"), + CLK_MSR_ID(58, "osc_ring_dspa"), + CLK_MSR_ID(59, "osc_ring_dspb"), + CLK_MSR_ID(60, "osc_ring_rama"), + CLK_MSR_ID(61, "osc_ring_ramb"), +}; + +static const struct meson_msr_id clk_msr_t7[] = { + CLK_MSR_ID(0, "sys"), + CLK_MSR_ID(1, "axi"), + CLK_MSR_ID(2, "rtc"), + CLK_MSR_ID(3, "dspa"), + CLK_MSR_ID(4, "dspb"), + CLK_MSR_ID(5, "mali"), + CLK_MSR_ID(6, "sys_cpu_clk_div16"), + CLK_MSR_ID(7, "ceca"), + CLK_MSR_ID(8, "cecb"), + CLK_MSR_ID(10, "fclk_div5"), + CLK_MSR_ID(11, "mpll0"), + CLK_MSR_ID(12, "mpll1"), + CLK_MSR_ID(13, "mpll2"), + CLK_MSR_ID(14, "mpll3"), + CLK_MSR_ID(15, "mpll_50m"), + CLK_MSR_ID(16, "pcie_inp"), + CLK_MSR_ID(17, "pcie_inn"), + CLK_MSR_ID(18, "mpll_test_out"), + CLK_MSR_ID(19, "hifi_pll"), + CLK_MSR_ID(20, "gp0_pll"), + CLK_MSR_ID(21, "gp1_pll"), + CLK_MSR_ID(22, "eth_mppll_50m"), + CLK_MSR_ID(23, "sys_pll_div16"), + CLK_MSR_ID(24, "ddr_dpll_pt"), + CLK_MSR_ID(25, "earcrx_pll"), + CLK_MSR_ID(26, "paie1_clk_inp"), + CLK_MSR_ID(27, "paie1_clk_inn"), + CLK_MSR_ID(28, "amlgdc"), + CLK_MSR_ID(29, "gdc"), + CLK_MSR_ID(30, "mod_eth_phy_ref"), + CLK_MSR_ID(31, "mod_eth_tx"), + CLK_MSR_ID(32, "eth_clk125Mhz"), + CLK_MSR_ID(33, "eth_clk_rmii"), + CLK_MSR_ID(34, "co_clkin_to_mac"), + CLK_MSR_ID(35, "mod_eth_rx_clk_rmii"), + CLK_MSR_ID(36, "co_rx"), + CLK_MSR_ID(37, "co_tx"), + CLK_MSR_ID(38, "eth_phy_rxclk"), + CLK_MSR_ID(39, "eth_phy_plltxclk"), + CLK_MSR_ID(40, "ephy_test"), + CLK_MSR_ID(41, "dsi_b_meas"), + CLK_MSR_ID(42, "hdmirx_apl"), + CLK_MSR_ID(43, "hdmirx_tmds"), + CLK_MSR_ID(44, "hdmirx_cable"), + CLK_MSR_ID(45, "hdmirx_apll_clk_audio"), + CLK_MSR_ID(46, "hdmirx_5m"), + CLK_MSR_ID(47, "hdmirx_2m"), + CLK_MSR_ID(48, "hdmirx_cfg"), + CLK_MSR_ID(49, "hdmirx_hdcp2x_eclk"), + CLK_MSR_ID(50, "vid_pll0_div"), + CLK_MSR_ID(51, "hdmi_vid_pll"), + CLK_MSR_ID(54, "vdac_clk"), + CLK_MSR_ID(55, "vpu_clk_buf"), + CLK_MSR_ID(56, "mod_tcon_clko"), + CLK_MSR_ID(57, "lcd_an_clk_ph2"), + CLK_MSR_ID(58, "lcd_an_clk_ph3"), + CLK_MSR_ID(59, "hdmi_tx_pixel"), + CLK_MSR_ID(60, "vdin_meas"), + CLK_MSR_ID(61, "vpu_clk"), + CLK_MSR_ID(62, "vpu_clkb"), + CLK_MSR_ID(63, "vpu_clkb_tmp"), + CLK_MSR_ID(64, "vpu_clkc"), + CLK_MSR_ID(65, "vid_lock"), + CLK_MSR_ID(66, "vapbclk"), + CLK_MSR_ID(67, "ge2d"), + CLK_MSR_ID(68, "aud_pll"), + CLK_MSR_ID(69, "aud_sck"), + CLK_MSR_ID(70, "dsi_a_meas"), + CLK_MSR_ID(72, "mipi_csi_phy"), + CLK_MSR_ID(73, "mipi_isp"), + CLK_MSR_ID(76, "hdmitx_tmds"), + CLK_MSR_ID(77, "hdmitx_sys"), + CLK_MSR_ID(78, "hdmitx_fe"), + CLK_MSR_ID(80, "hdmitx_prif"), + CLK_MSR_ID(81, "hdmitx_200m"), + CLK_MSR_ID(82, "hdmitx_aud"), + CLK_MSR_ID(83, "hdmitx_pnx"), + CLK_MSR_ID(84, "spicc5"), + CLK_MSR_ID(85, "spicc4"), + CLK_MSR_ID(86, "spicc3"), + CLK_MSR_ID(87, "spicc2"), + CLK_MSR_ID(93, "vdec"), + CLK_MSR_ID(94, "wave521_aclk"), + CLK_MSR_ID(95, "wave521_cclk"), + CLK_MSR_ID(96, "wave521_bclk"), + CLK_MSR_ID(97, "hcodec"), + CLK_MSR_ID(98, "hevcb"), + CLK_MSR_ID(99, "hevcf"), + CLK_MSR_ID(100, "hdmi_aud_pll"), + CLK_MSR_ID(101, "hdmi_acr_ref"), + CLK_MSR_ID(102, "hdmi_meter"), + CLK_MSR_ID(103, "hdmi_vid"), + CLK_MSR_ID(104, "hdmi_aud"), + CLK_MSR_ID(105, "hdmi_dsd"), + CLK_MSR_ID(108, "dsi1_phy"), + CLK_MSR_ID(109, "dsi0_phy"), + CLK_MSR_ID(110, "smartcard"), + CLK_MSR_ID(111, "sar_adc"), + CLK_MSR_ID(113, "sd_emmc_c"), + CLK_MSR_ID(114, "sd_emmc_b"), + CLK_MSR_ID(115, "sd_emmc_a"), + CLK_MSR_ID(116, "gpio_msr"), + CLK_MSR_ID(117, "spicc1"), + CLK_MSR_ID(118, "spicc0"), + CLK_MSR_ID(119, "anakin"), + CLK_MSR_ID(121, "ts_clk(temp sensor)"), + CLK_MSR_ID(122, "ts_a73"), + CLK_MSR_ID(123, "ts_a53"), + CLK_MSR_ID(124, "ts_nna"), + CLK_MSR_ID(130, "audio_vad"), + CLK_MSR_ID(131, "acodec_dac_clk_x128"), + CLK_MSR_ID(132, "audio_locker_in"), + CLK_MSR_ID(133, "audio_locker_out"), + CLK_MSR_ID(134, "audio_tdmout_c_sclk"), + CLK_MSR_ID(135, "audio_tdmout_b_sclk"), + CLK_MSR_ID(136, "audio_tdmout_a_sclk"), + CLK_MSR_ID(137, "audio_tdmin_lb_sclk"), + CLK_MSR_ID(138, "audio_tdmin_c_sclk"), + CLK_MSR_ID(139, "audio_tdmin_b_sclk"), + CLK_MSR_ID(140, "audio_tdmin_a_sclk"), + CLK_MSR_ID(141, "audio_resamplea"), + CLK_MSR_ID(142, "audio_pdm_sysclk"), + CLK_MSR_ID(143, "audio_spdifoutb_mst"), + CLK_MSR_ID(144, "audio_spdifout_mst"), + CLK_MSR_ID(145, "audio_spdifin_mst"), + CLK_MSR_ID(146, "audio_pdm_dclk"), + CLK_MSR_ID(147, "audio_resampleb"), + CLK_MSR_ID(148, "earcrx_pll_dmac"), + CLK_MSR_ID(156, "pwm_ao_h"), + CLK_MSR_ID(157, "pwm_ao_g"), + CLK_MSR_ID(158, "pwm_ao_f"), + CLK_MSR_ID(159, "pwm_ao_e"), + CLK_MSR_ID(160, "pwm_ao_d"), + CLK_MSR_ID(161, "pwm_ao_c"), + CLK_MSR_ID(162, "pwm_ao_b"), + CLK_MSR_ID(163, "pwm_ao_a"), + CLK_MSR_ID(164, "pwm_f"), + CLK_MSR_ID(165, "pwm_e"), + CLK_MSR_ID(166, "pwm_d"), + CLK_MSR_ID(167, "pwm_c"), + CLK_MSR_ID(168, "pwm_b"), + CLK_MSR_ID(169, "pwm_a"), + CLK_MSR_ID(170, "aclkm"), + CLK_MSR_ID(171, "mclk_pll"), + CLK_MSR_ID(172, "a73_sys_pll_div16"), + CLK_MSR_ID(173, "a73_cpu_clk_div16"), + CLK_MSR_ID(176, "rng_ring_0"), + CLK_MSR_ID(177, "rng_ring_1"), + CLK_MSR_ID(178, "rng_ring_2"), + CLK_MSR_ID(179, "rng_ring_3"), + CLK_MSR_ID(180, "am_ring_out0"), + CLK_MSR_ID(181, "am_ring_out1"), + CLK_MSR_ID(182, "am_ring_out2"), + CLK_MSR_ID(183, "am_ring_out3"), + CLK_MSR_ID(184, "am_ring_out4"), + CLK_MSR_ID(185, "am_ring_out5"), + CLK_MSR_ID(186, "am_ring_out6"), + CLK_MSR_ID(187, "am_ring_out7"), + CLK_MSR_ID(188, "am_ring_out8"), + CLK_MSR_ID(189, "am_ring_out9"), + CLK_MSR_ID(190, "am_ring_out10"), + CLK_MSR_ID(191, "am_ring_out11"), + CLK_MSR_ID(192, "am_ring_out12"), + CLK_MSR_ID(193, "am_ring_out13"), + CLK_MSR_ID(194, "am_ring_out14"), + CLK_MSR_ID(195, "am_ring_out15"), + CLK_MSR_ID(196, "am_ring_out16"), + CLK_MSR_ID(197, "am_ring_out17"), + CLK_MSR_ID(198, "am_ring_out18"), + CLK_MSR_ID(199, "am_ring_out19"), + CLK_MSR_ID(200, "mipi_csi_phy0"), + CLK_MSR_ID(201, "mipi_csi_phy1"), + CLK_MSR_ID(202, "mipi_csi_phy2"), + CLK_MSR_ID(203, "mipi_csi_phy3"), + CLK_MSR_ID(204, "vid_pll1_div"), + CLK_MSR_ID(205, "vid_pll2_div"), + CLK_MSR_ID(206, "am_ring_out20"), + CLK_MSR_ID(207, "am_ring_out21"), + CLK_MSR_ID(208, "am_ring_out22"), + CLK_MSR_ID(209, "am_ring_out23"), + CLK_MSR_ID(210, "am_ring_out24"), + CLK_MSR_ID(211, "am_ring_out25"), + CLK_MSR_ID(212, "am_ring_out26"), + CLK_MSR_ID(213, "am_ring_out27"), + CLK_MSR_ID(214, "am_ring_out28"), + CLK_MSR_ID(215, "am_ring_out29"), + CLK_MSR_ID(216, "am_ring_out30"), + CLK_MSR_ID(217, "am_ring_out31"), + CLK_MSR_ID(218, "am_ring_out32"), + CLK_MSR_ID(219, "enc0_if"), + CLK_MSR_ID(220, "enc2"), + CLK_MSR_ID(221, "enc1"), + CLK_MSR_ID(222, "enc0") +}; + static int meson_measure_id(struct meson_msr_id *clk_msr_id, unsigned int duration) { @@ -1026,6 +1278,18 @@ static const struct meson_msr_data clk_msr_s4_data = { .reg = &msr_reg_offset_v2, }; +static const struct meson_msr_data clk_msr_a1_data = { + .msr_table = (void *)clk_msr_a1, + .msr_count = ARRAY_SIZE(clk_msr_a1), + .reg = &msr_reg_offset_v2, +}; + +static const struct meson_msr_data clk_msr_t7_data = { + .msr_table = (void *)clk_msr_t7, + .msr_count = ARRAY_SIZE(clk_msr_t7), + .reg = &msr_reg_offset_v2, +}; + static const struct of_device_id meson_msr_match_table[] = { { .compatible = "amlogic,meson-gx-clk-measure", @@ -1059,6 +1323,14 @@ static const struct of_device_id meson_msr_match_table[] = { .compatible = "amlogic,s4-clk-measure", .data = &clk_msr_s4_data, }, + { + .compatible = "amlogic,a1-clk-measure", + .data = &clk_msr_a1_data, + }, + { + .compatible = "amlogic,t7-clk-measure", + .data = &clk_msr_t7_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, meson_msr_match_table); From 67481152be2ec5de3529664c82054b43d6d4b9f5 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 15 Jun 2026 17:15:31 +0800 Subject: [PATCH 010/157] soc: amlogic: meson-clk-measure: remove debugfs tree meson_msr_probe() creates a debugfs tree with entries that reference devm-managed measurement table entries and the driver's private regmap state. The driver has no remove callback, so unbinding the device can leave those debugfs entries behind after the private data is released. Store the debugfs root and remove the subtree from a new remove callback. Signed-off-by: Pengpeng Hou Reviewed-by: Neil Armstrong Link: https://patch.msgid.link/20260615091531.21964-1-pengpeng@iscas.ac.cn Signed-off-by: Neil Armstrong --- drivers/soc/amlogic/meson-clk-measure.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/soc/amlogic/meson-clk-measure.c b/drivers/soc/amlogic/meson-clk-measure.c index 8c4f3cc8c8ab..8a1a9fed0e74 100644 --- a/drivers/soc/amlogic/meson-clk-measure.c +++ b/drivers/soc/amlogic/meson-clk-measure.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ struct meson_msr_data { struct meson_msr { struct regmap *regmap; struct meson_msr_data data; + struct dentry *debugfs_root; }; #define CLK_MSR_ID(__id, __name) \ @@ -1204,6 +1206,7 @@ static int meson_msr_probe(struct platform_device *pdev) sizeof(struct msr_reg_offset)); root = debugfs_create_dir("meson-clk-msr", NULL); + priv->debugfs_root = root; clks = debugfs_create_dir("clks", root); debugfs_create_file("measure_summary", 0444, root, @@ -1219,9 +1222,19 @@ static int meson_msr_probe(struct platform_device *pdev) &priv->data.msr_table[i], &clk_msr_fops); } + platform_set_drvdata(pdev, priv); + return 0; } +static void meson_msr_remove(struct platform_device *pdev) +{ + struct meson_msr *priv = platform_get_drvdata(pdev); + + if (!IS_ERR_OR_NULL(priv->debugfs_root)) + debugfs_remove_recursive(priv->debugfs_root); +} + static const struct msr_reg_offset msr_reg_offset = { .duty_val = 0x0, .freq_ctrl = 0x4, @@ -1337,6 +1350,7 @@ MODULE_DEVICE_TABLE(of, meson_msr_match_table); static struct platform_driver meson_msr_driver = { .probe = meson_msr_probe, + .remove = meson_msr_remove, .driver = { .name = "meson_msr", .of_match_table = meson_msr_match_table, From 36887b488b253c99660a7cef52bb1202820bfdae Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 16 Jun 2026 08:53:46 +0800 Subject: [PATCH 011/157] soc: apple: sart: require device link for consumers devm_apple_sart_get() obtains the supplier platform device and attempts to create a runtime-PM device link to it, but it ignores device_link_add() failure. A consumer can then continue without the dependency that keeps the SART supplier ordered and runtime-PM reachable. Treat a failed device link as an error and drop the supplier device reference before returning. Signed-off-by: Pengpeng Hou Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260616005346.7862-1-pengpeng@iscas.ac.cn Signed-off-by: Sven Peter --- drivers/soc/apple/sart.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/soc/apple/sart.c b/drivers/soc/apple/sart.c index 9eaf3febb382..66b99955b395 100644 --- a/drivers/soc/apple/sart.c +++ b/drivers/soc/apple/sart.c @@ -218,6 +218,7 @@ struct apple_sart *devm_apple_sart_get(struct device *dev) { struct device_node *sart_node; struct platform_device *sart_pdev; + struct device_link *link; struct apple_sart *sart; sart_node = of_parse_phandle(dev->of_node, "apple,sart", 0); @@ -236,8 +237,12 @@ struct apple_sart *devm_apple_sart_get(struct device *dev) return ERR_PTR(-EPROBE_DEFER); } - device_link_add(dev, &sart_pdev->dev, - DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); + link = device_link_add(dev, &sart_pdev->dev, + DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOREMOVE_SUPPLIER); + if (!link) { + put_device(&sart_pdev->dev); + return ERR_PTR(-ENODEV); + } put_device(&sart_pdev->dev); From 743af1d6a8b4bfa62dd01b5c2d81b1c62ea597f7 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sat, 27 Jun 2026 19:50:16 +0200 Subject: [PATCH 012/157] soc: qcom: socinfo: Add SoC ID for SDM850 Recognize the SDM850 SoC. Signed-off-by: David Heidelberg Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260627-sda850-v2-2-44bf46ade42e@ixit.cz Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index af418adad7aa..1836e226a294 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -425,6 +425,7 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ8078) }, { qcom_board_id(SDM636) }, { qcom_board_id(SDA636) }, + { qcom_board_id(SDM850) }, { qcom_board_id(SDM632) }, { qcom_board_id(SDA632) }, { qcom_board_id(SDA450) }, From 90388101052ec56d24323e35c87ff594abe28e29 Mon Sep 17 00:00:00 2001 From: Mayur Kumar Date: Mon, 11 May 2026 01:08:40 +0530 Subject: [PATCH 013/157] soc: qcom: cmd-db: use C-style SPDX comment for source file checkpatch.pl reports an improper SPDX comment style for this file: WARNING: Improper SPDX comment style for 'drivers/soc/qcom/cmd-db.c', please use '//' instead Per Documentation/process/license-rules.rst, C source files must use the '// SPDX-License-Identifier: ...' form instead of the '/* ... */' form. Fix it. Signed-off-by: Mayur Kumar Reviewed-by: Sneh Mankad Link: https://lore.kernel.org/r/20260510193840.59156-1-kmayur809@gmail.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 84a75d8c4b70..3f1eee784387 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. From e21d3b2e5c4387b243424daa0ff2d45c6fbf73e2 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:29 +0300 Subject: [PATCH 014/157] soc: qcom: ubwc: set min_acc length to 64 for all UBWC 1.0 targets According to the documentation, the MAL should be set for all UBWC 1.0 targets, no matter what is the version of the UBWC decoders are present on the device. The helper comes from DPU / GPU world, where there was no separate bit to control MAL before UBWC 2.0. As the helper is now being used by other drivers too, correct the helper to return the correct MAL value (Iris doesn't support UBWC 1.0 devices for now, so there is no changes of the behaviour). Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-22-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- include/linux/soc/qcom/ubwc.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 83d2c2a7116c..0b5aa9d0343b 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -75,14 +75,9 @@ static inline bool qcom_ubwc_get_ubwc_mode(const struct qcom_ubwc_cfg_data *cfg) return ret; } -/* - * This is the best guess, based on the MDSS driver, which worked so far. - */ static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data *cfg) { - return cfg->ubwc_enc_version == UBWC_1_0 && - (cfg->ubwc_dec_version == UBWC_2_0 || - cfg->ubwc_dec_version == UBWC_3_0); + return cfg->ubwc_enc_version == UBWC_1_0; } static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg) From e7012ac2fc000fe7ae3e58a7449b80fa955b124a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:30 +0300 Subject: [PATCH 015/157] soc: qcom: ubwc: drop ubwc_dec_version The ubwc_dec_version field has been inherited from the MDSS driver and it is equal to the version of the UBWC decoder in the display block only. Other IP Cores can have different UBWC decoders and so the version would vary between blocks. As the value is no longer used as is not relevant to other UBWC database consumers, drop it from the UBWC database. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-23-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 23 ----------------------- include/linux/soc/qcom/ubwc.h | 2 -- 2 files changed, 25 deletions(-) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 3fe47d8f0f63..1344cda0fb75 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -18,7 +18,6 @@ static const struct qcom_ubwc_cfg_data no_ubwc_data = { static const struct qcom_ubwc_cfg_data eliza_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_dec_version = UBWC_5_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -29,7 +28,6 @@ static const struct qcom_ubwc_cfg_data eliza_data = { static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_enc_version = UBWC_6_0, - .ubwc_dec_version = UBWC_6_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -39,7 +37,6 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { static const struct qcom_ubwc_cfg_data msm8937_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_1_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -48,7 +45,6 @@ static const struct qcom_ubwc_cfg_data msm8937_data = { static const struct qcom_ubwc_cfg_data msm8998_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_1_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -62,7 +58,6 @@ static const struct qcom_ubwc_cfg_data qcm2290_data = { static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, .highest_bank_bit = 13, @@ -71,7 +66,6 @@ static const struct qcom_ubwc_cfg_data sa8775p_data = { static const struct qcom_ubwc_cfg_data sar2130p_data = { .ubwc_enc_version = UBWC_3_0, /* 4.0.2 in hw */ - .ubwc_dec_version = UBWC_4_3, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -81,7 +75,6 @@ static const struct qcom_ubwc_cfg_data sar2130p_data = { static const struct qcom_ubwc_cfg_data sc7180_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -90,7 +83,6 @@ static const struct qcom_ubwc_cfg_data sc7180_data = { static const struct qcom_ubwc_cfg_data sc7280_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -100,7 +92,6 @@ static const struct qcom_ubwc_cfg_data sc7280_data = { static const struct qcom_ubwc_cfg_data sc8180x_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_dec_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, @@ -109,7 +100,6 @@ static const struct qcom_ubwc_cfg_data sc8180x_data = { static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -119,7 +109,6 @@ static const struct qcom_ubwc_cfg_data sc8280xp_data = { static const struct qcom_ubwc_cfg_data sdm670_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, @@ -127,7 +116,6 @@ static const struct qcom_ubwc_cfg_data sdm670_data = { static const struct qcom_ubwc_cfg_data sdm845_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, @@ -135,7 +123,6 @@ static const struct qcom_ubwc_cfg_data sdm845_data = { static const struct qcom_ubwc_cfg_data sm6115_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -145,7 +132,6 @@ static const struct qcom_ubwc_cfg_data sm6115_data = { static const struct qcom_ubwc_cfg_data sm6125_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_dec_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, @@ -154,7 +140,6 @@ static const struct qcom_ubwc_cfg_data sm6125_data = { static const struct qcom_ubwc_cfg_data sm6150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, @@ -162,7 +147,6 @@ static const struct qcom_ubwc_cfg_data sm6150_data = { static const struct qcom_ubwc_cfg_data sm6350_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -171,7 +155,6 @@ static const struct qcom_ubwc_cfg_data sm6350_data = { static const struct qcom_ubwc_cfg_data sm7150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_dec_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, @@ -179,7 +162,6 @@ static const struct qcom_ubwc_cfg_data sm7150_data = { static const struct qcom_ubwc_cfg_data sm8150_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_dec_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, @@ -187,7 +169,6 @@ static const struct qcom_ubwc_cfg_data sm8150_data = { static const struct qcom_ubwc_cfg_data sm8250_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -198,7 +179,6 @@ static const struct qcom_ubwc_cfg_data sm8250_data = { static const struct qcom_ubwc_cfg_data sm8350_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -209,7 +189,6 @@ static const struct qcom_ubwc_cfg_data sm8350_data = { static const struct qcom_ubwc_cfg_data sm8550_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_dec_version = UBWC_4_3, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .ubwc_bank_spread = true, @@ -220,7 +199,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_dec_version = UBWC_5_0, .ubwc_swizzle = 6, .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ @@ -230,7 +208,6 @@ static const struct qcom_ubwc_cfg_data sm8750_data = { static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_dec_version = UBWC_5_0, .ubwc_swizzle = 0, .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 0b5aa9d0343b..c3f9efae5db8 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -13,8 +13,6 @@ struct qcom_ubwc_cfg_data { u32 ubwc_enc_version; - /* Can be read from MDSS_BASE + 0x58 */ - u32 ubwc_dec_version; /** * @ubwc_swizzle: Whether to enable level 1, 2 & 3 bank swizzling. From 5a9f4c535ae05facd4f6ef116b18062f9f2ad768 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:31 +0300 Subject: [PATCH 016/157] soc: qcom: ubwc: drop ubwc_bank_spread According to the documentation, UBWC bank spreading should be enabled for all targets. It's just not all targets have separate bit to control it. Drop the bit from the database and make the helper always return true. If we need to change it later, the helper can be adjusted according to the programming guides. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-24-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 14 -------------- include/linux/soc/qcom/ubwc.h | 3 +-- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 1344cda0fb75..35cde4e9a238 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -20,7 +20,6 @@ static const struct qcom_ubwc_cfg_data eliza_data = { .ubwc_enc_version = UBWC_5_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 14 for LP_DDR4 */ .highest_bank_bit = 15, .macrotile_mode = true, @@ -30,7 +29,6 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_enc_version = UBWC_6_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 16, .macrotile_mode = true, }; @@ -59,7 +57,6 @@ static const struct qcom_ubwc_cfg_data qcm2290_data = { static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 13, .macrotile_mode = true, }; @@ -68,7 +65,6 @@ static const struct qcom_ubwc_cfg_data sar2130p_data = { .ubwc_enc_version = UBWC_3_0, /* 4.0.2 in hw */ .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 13, .macrotile_mode = true, }; @@ -77,7 +73,6 @@ static const struct qcom_ubwc_cfg_data sc7180_data = { .ubwc_enc_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, }; @@ -85,7 +80,6 @@ static const struct qcom_ubwc_cfg_data sc7280_data = { .ubwc_enc_version = UBWC_3_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, .macrotile_mode = true, }; @@ -102,7 +96,6 @@ static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 16, .macrotile_mode = true, }; @@ -126,7 +119,6 @@ static const struct qcom_ubwc_cfg_data sm6115_data = { .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, }; @@ -149,7 +141,6 @@ static const struct qcom_ubwc_cfg_data sm6350_data = { .ubwc_enc_version = UBWC_2_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, .highest_bank_bit = 14, }; @@ -171,7 +162,6 @@ static const struct qcom_ubwc_cfg_data sm8250_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -181,7 +171,6 @@ static const struct qcom_ubwc_cfg_data sm8350_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -191,7 +180,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -200,7 +188,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_enc_version = UBWC_5_0, .ubwc_swizzle = 6, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, @@ -209,7 +196,6 @@ static const struct qcom_ubwc_cfg_data sm8750_data = { static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, .ubwc_swizzle = 0, - .ubwc_bank_spread = true, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, .macrotile_mode = true, diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index c3f9efae5db8..254721f5ea3c 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -33,7 +33,6 @@ struct qcom_ubwc_cfg_data { * DDR bank. This should ideally use DRAM type detection. */ int highest_bank_bit; - bool ubwc_bank_spread; /** * @macrotile_mode: Macrotile Mode @@ -85,7 +84,7 @@ static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg) { - return cfg->ubwc_bank_spread; + return true; } static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) From fc54caf87a5a7289502ecbcdc80bb71f7bb5de9c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:32 +0300 Subject: [PATCH 017/157] soc: qcom: ubwc: drop macrotile_mode from the database All the users have been migrated to using qcom_ubwc_macrotile_mode() instead of reading the raw value from the config structure. Drop the field from struct qcom_ubwc_cfg_data and replace it with the calculated value. Split single UBWC_3_0 into UBWC_3_0 (no macrotile mode) and UBWC_3_1 (with macrotile mode). Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-25-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 18 +++--------------- include/linux/soc/qcom/ubwc.h | 18 ++++++++---------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 35cde4e9a238..8dd91d0b3974 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -22,7 +22,6 @@ static const struct qcom_ubwc_cfg_data eliza_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 14 for LP_DDR4 */ .highest_bank_bit = 15, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data kaanapali_data = { @@ -30,7 +29,6 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data msm8937_data = { @@ -58,15 +56,13 @@ static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 13, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sar2130p_data = { - .ubwc_enc_version = UBWC_3_0, /* 4.0.2 in hw */ + .ubwc_enc_version = UBWC_3_1, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 13, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sc7180_data = { @@ -77,19 +73,17 @@ static const struct qcom_ubwc_cfg_data sc7180_data = { }; static const struct qcom_ubwc_cfg_data sc7280_data = { - .ubwc_enc_version = UBWC_3_0, + .ubwc_enc_version = UBWC_3_1, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sc8180x_data = { - .ubwc_enc_version = UBWC_3_0, + .ubwc_enc_version = UBWC_3_1, .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sc8280xp_data = { @@ -97,7 +91,6 @@ static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sdm670_data = { @@ -164,7 +157,6 @@ static const struct qcom_ubwc_cfg_data sm8250_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sm8350_data = { @@ -173,7 +165,6 @@ static const struct qcom_ubwc_cfg_data sm8350_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sm8550_data = { @@ -182,7 +173,6 @@ static const struct qcom_ubwc_cfg_data sm8550_data = { UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data sm8750_data = { @@ -190,7 +180,6 @@ static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_swizzle = 6, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct qcom_ubwc_cfg_data glymur_data = { @@ -198,7 +187,6 @@ static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_swizzle = 0, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, - .macrotile_mode = true, }; static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 254721f5ea3c..fee778360ac2 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -33,15 +33,6 @@ struct qcom_ubwc_cfg_data { * DDR bank. This should ideally use DRAM type detection. */ int highest_bank_bit; - - /** - * @macrotile_mode: Macrotile Mode - * - * Whether to use 4-channel macrotiling mode or the newer - * 8-channel macrotiling mode introduced in UBWC 3.1. 0 is - * 4-channel and 1 is 8-channel. - */ - bool macrotile_mode; }; #define UBWC_1_0 0x10000000 @@ -77,9 +68,16 @@ static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data return cfg->ubwc_enc_version == UBWC_1_0; } +/* + * @qcom_ubwc_macrotile_mode: whether to use 4-channel or 8-channel macrotiling + * + * The 8-channel macrotiling mode was introduced in UBWC 3.1. + * + * Returns: false for the 4-channel and true for 8-channel. + */ static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg) { - return cfg->macrotile_mode; + return cfg->ubwc_enc_version >= UBWC_3_1; } static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg) From 17120525ff070da978effc009877bace3d788698 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:33 +0300 Subject: [PATCH 018/157] soc: qcom: ubwc: use fixed values for UBWC swizzle for UBWC < 4.0 UBWC devices before 4.0 use standard UBWC swizzle levels. As all the drivers now use the qcom_ubwc_swizzle() helper, move those values to the helper, leaving UBWC 4.0+ intact for now. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-26-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 34 ---------------------------------- include/linux/soc/qcom/ubwc.h | 33 ++++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 8dd91d0b3974..7e321389a399 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -33,17 +33,11 @@ static const struct qcom_ubwc_cfg_data kaanapali_data = { static const struct qcom_ubwc_cfg_data msm8937_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data msm8998_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, }; @@ -60,94 +54,66 @@ static const struct qcom_ubwc_cfg_data sa8775p_data = { static const struct qcom_ubwc_cfg_data sar2130p_data = { .ubwc_enc_version = UBWC_3_1, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 13, }; static const struct qcom_ubwc_cfg_data sc7180_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sc7280_data = { .ubwc_enc_version = UBWC_3_1, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sc8180x_data = { .ubwc_enc_version = UBWC_3_1, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sc8280xp_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sdm670_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sdm845_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, }; static const struct qcom_ubwc_cfg_data sm6115_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm6125_data = { .ubwc_enc_version = UBWC_1_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL1 | - UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm6150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm6350_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm7150_data = { .ubwc_enc_version = UBWC_2_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 14, }; static const struct qcom_ubwc_cfg_data sm8150_data = { .ubwc_enc_version = UBWC_3_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 15, }; diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index fee778360ac2..7c9506741001 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -22,9 +22,6 @@ struct qcom_ubwc_cfg_data { * UBWC 4.0 adds the optional ability to disable levels 2 & 3. */ u32 ubwc_swizzle; -#define UBWC_SWIZZLE_ENABLE_LVL1 BIT(0) -#define UBWC_SWIZZLE_ENABLE_LVL2 BIT(1) -#define UBWC_SWIZZLE_ENABLE_LVL3 BIT(2) /** * @highest_bank_bit: Highest Bank Bit @@ -55,12 +52,7 @@ static inline const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void) static inline bool qcom_ubwc_get_ubwc_mode(const struct qcom_ubwc_cfg_data *cfg) { - bool ret = cfg->ubwc_enc_version == UBWC_1_0; - - if (ret && !(cfg->ubwc_swizzle & UBWC_SWIZZLE_ENABLE_LVL1)) - pr_err("UBWC config discrepancy - level 1 swizzling disabled on UBWC 1.0\n"); - - return ret; + return cfg->ubwc_enc_version == UBWC_1_0; } static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data *cfg) @@ -85,8 +77,31 @@ static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg) return true; } +#define UBWC_SWIZZLE_ENABLE_LVL1 BIT(0) +#define UBWC_SWIZZLE_ENABLE_LVL2 BIT(1) +#define UBWC_SWIZZLE_ENABLE_LVL3 BIT(2) + +/** + * @qcom_ubwc_swizzle: Whether to enable level 1, 2 & 3 bank swizzling. + * + * UBWC 1.0 always enables all three levels. + * UBWC 2.0 removes level 1 bank swizzling, leaving levels 2 & 3. + * UBWC 4.0 adds the optional ability to disable levels 2 & 3. + */ static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) { + if (cfg->ubwc_enc_version == 0) + return 0; + + if (cfg->ubwc_enc_version == UBWC_1_0) + return UBWC_SWIZZLE_ENABLE_LVL1 | + UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3; + + if (cfg->ubwc_enc_version < UBWC_4_0) + return UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3; + return cfg->ubwc_swizzle; } From 31e1c3248586dcc9aae42539152ab271dcfd37b6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:34 +0300 Subject: [PATCH 019/157] soc: qcom: ubwc: sort out the rest of the UBWC swizzle settings Sort out the remaining UBWC swizzle values, using flags to control whether level 2 and level 3 swizzling are enabled or not. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-27-72f2749bc807@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 16 +++------------- include/linux/soc/qcom/ubwc.h | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 7e321389a399..f27440d5c06f 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -18,16 +18,12 @@ static const struct qcom_ubwc_cfg_data no_ubwc_data = { static const struct qcom_ubwc_cfg_data eliza_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 14 for LP_DDR4 */ .highest_bank_bit = 15, }; static const struct qcom_ubwc_cfg_data kaanapali_data = { .ubwc_enc_version = UBWC_6_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, .highest_bank_bit = 16, }; @@ -48,7 +44,7 @@ static const struct qcom_ubwc_cfg_data qcm2290_data = { static const struct qcom_ubwc_cfg_data sa8775p_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL3, + .flags = UBWC_FLAG_DISABLE_SWIZZLE_LVL2, .highest_bank_bit = 13, }; @@ -119,38 +115,32 @@ static const struct qcom_ubwc_cfg_data sm8150_data = { static const struct qcom_ubwc_cfg_data sm8250_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sm8350_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sm8550_data = { .ubwc_enc_version = UBWC_4_0, - .ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sm8750_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_swizzle = 6, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, - .ubwc_swizzle = 0, + .flags = UBWC_FLAG_DISABLE_SWIZZLE_LVL2 | + UBWC_FLAG_DISABLE_SWIZZLE_LVL3, /* TODO: highest_bank_bit = 15 for LP_DDR4 */ .highest_bank_bit = 16, }; diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index 7c9506741001..a7372d9c25fb 100644 --- a/include/linux/soc/qcom/ubwc.h +++ b/include/linux/soc/qcom/ubwc.h @@ -14,15 +14,6 @@ struct qcom_ubwc_cfg_data { u32 ubwc_enc_version; - /** - * @ubwc_swizzle: Whether to enable level 1, 2 & 3 bank swizzling. - * - * UBWC 1.0 always enables all three levels. - * UBWC 2.0 removes level 1 bank swizzling, leaving levels 2 & 3. - * UBWC 4.0 adds the optional ability to disable levels 2 & 3. - */ - u32 ubwc_swizzle; - /** * @highest_bank_bit: Highest Bank Bit * @@ -30,6 +21,10 @@ struct qcom_ubwc_cfg_data { * DDR bank. This should ideally use DRAM type detection. */ int highest_bank_bit; + + unsigned int flags; +#define UBWC_FLAG_DISABLE_SWIZZLE_LVL2 BIT(0) +#define UBWC_FLAG_DISABLE_SWIZZLE_LVL3 BIT(1) }; #define UBWC_1_0 0x10000000 @@ -98,11 +93,16 @@ static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg) UBWC_SWIZZLE_ENABLE_LVL2 | UBWC_SWIZZLE_ENABLE_LVL3; - if (cfg->ubwc_enc_version < UBWC_4_0) - return UBWC_SWIZZLE_ENABLE_LVL2 | - UBWC_SWIZZLE_ENABLE_LVL3; + u32 ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3; - return cfg->ubwc_swizzle; + if (cfg->flags & UBWC_FLAG_DISABLE_SWIZZLE_LVL2) + ubwc_swizzle &= ~UBWC_SWIZZLE_ENABLE_LVL2; + + if (cfg->flags & UBWC_FLAG_DISABLE_SWIZZLE_LVL3) + ubwc_swizzle &= ~UBWC_SWIZZLE_ENABLE_LVL3; + + return ubwc_swizzle; } static inline u32 qcom_ubwc_version_tag(const struct qcom_ubwc_cfg_data *cfg) From d39d5ff3e550393dea28da2fa2afe7fe3cee7f42 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 20 May 2026 17:51:35 +0300 Subject: [PATCH 020/157] soc: qcom: ubwc: deduplicate UBWC configuration data After removing all extra entries from the UBWC database it is easy to define generic entries, common for all machine classes. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260520-ubwc-rework-v5-28-72f2749bc807@oss.qualcomm.com [bjorn: Dropped unused "ubwc_3_0_hbb16", per compiler warning] Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 249 +++++++++++++-------------------- 1 file changed, 100 insertions(+), 149 deletions(-) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index f27440d5c06f..31cc56a3c2d7 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -16,30 +16,70 @@ static const struct qcom_ubwc_cfg_data no_ubwc_data = { /* no UBWC, no HBB */ }; -static const struct qcom_ubwc_cfg_data eliza_data = { +static const struct qcom_ubwc_cfg_data ubwc_0_0_hbb15 = { + /* no UBWC */ + .highest_bank_bit = 15, +}; + +static const struct qcom_ubwc_cfg_data ubwc_1_0_hbb14 = { + .ubwc_enc_version = UBWC_1_0, + .highest_bank_bit = 14, +}; + +static const struct qcom_ubwc_cfg_data ubwc_1_0_hbb15 = { + .ubwc_enc_version = UBWC_1_0, + .highest_bank_bit = 15, +}; + +static const struct qcom_ubwc_cfg_data ubwc_2_0_hbb14 = { + .ubwc_enc_version = UBWC_2_0, + .highest_bank_bit = 14, +}; + +static const struct qcom_ubwc_cfg_data ubwc_2_0_hbb15 = { + .ubwc_enc_version = UBWC_2_0, + .highest_bank_bit = 15, +}; + +static const struct qcom_ubwc_cfg_data ubwc_3_0_hbb15 = { + .ubwc_enc_version = UBWC_3_0, + .highest_bank_bit = 15, +}; + +static const struct qcom_ubwc_cfg_data ubwc_3_1_hbb13 = { + .ubwc_enc_version = UBWC_3_1, + .highest_bank_bit = 13, +}; + +static const struct qcom_ubwc_cfg_data ubwc_3_1_hbb14 = { + .ubwc_enc_version = UBWC_3_1, + .highest_bank_bit = 14, +}; + +static const struct qcom_ubwc_cfg_data ubwc_3_1_hbb16 = { + .ubwc_enc_version = UBWC_3_1, + .highest_bank_bit = 16, +}; + +static const struct qcom_ubwc_cfg_data ubwc_4_0_hbb16 = { + .ubwc_enc_version = UBWC_4_0, + .highest_bank_bit = 16, +}; + +static const struct qcom_ubwc_cfg_data ubwc_5_0_hbb15 = { .ubwc_enc_version = UBWC_5_0, /* TODO: highest_bank_bit = 14 for LP_DDR4 */ .highest_bank_bit = 15, }; -static const struct qcom_ubwc_cfg_data kaanapali_data = { - .ubwc_enc_version = UBWC_6_0, +static const struct qcom_ubwc_cfg_data ubwc_5_0_hbb16 = { + .ubwc_enc_version = UBWC_5_0, .highest_bank_bit = 16, }; -static const struct qcom_ubwc_cfg_data msm8937_data = { - .ubwc_enc_version = UBWC_1_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data msm8998_data = { - .ubwc_enc_version = UBWC_1_0, - .highest_bank_bit = 15, -}; - -static const struct qcom_ubwc_cfg_data qcm2290_data = { - /* no UBWC */ - .highest_bank_bit = 15, +static const struct qcom_ubwc_cfg_data ubwc_6_0_hbb16 = { + .ubwc_enc_version = UBWC_6_0, + .highest_bank_bit = 16, }; static const struct qcom_ubwc_cfg_data sa8775p_data = { @@ -48,95 +88,6 @@ static const struct qcom_ubwc_cfg_data sa8775p_data = { .highest_bank_bit = 13, }; -static const struct qcom_ubwc_cfg_data sar2130p_data = { - .ubwc_enc_version = UBWC_3_1, - .highest_bank_bit = 13, -}; - -static const struct qcom_ubwc_cfg_data sc7180_data = { - .ubwc_enc_version = UBWC_2_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sc7280_data = { - .ubwc_enc_version = UBWC_3_1, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sc8180x_data = { - .ubwc_enc_version = UBWC_3_1, - .highest_bank_bit = 16, -}; - -static const struct qcom_ubwc_cfg_data sc8280xp_data = { - .ubwc_enc_version = UBWC_4_0, - .highest_bank_bit = 16, -}; - -static const struct qcom_ubwc_cfg_data sdm670_data = { - .ubwc_enc_version = UBWC_2_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sdm845_data = { - .ubwc_enc_version = UBWC_2_0, - .highest_bank_bit = 15, -}; - -static const struct qcom_ubwc_cfg_data sm6115_data = { - .ubwc_enc_version = UBWC_1_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sm6125_data = { - .ubwc_enc_version = UBWC_1_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sm6150_data = { - .ubwc_enc_version = UBWC_2_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sm6350_data = { - .ubwc_enc_version = UBWC_2_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sm7150_data = { - .ubwc_enc_version = UBWC_2_0, - .highest_bank_bit = 14, -}; - -static const struct qcom_ubwc_cfg_data sm8150_data = { - .ubwc_enc_version = UBWC_3_0, - .highest_bank_bit = 15, -}; - -static const struct qcom_ubwc_cfg_data sm8250_data = { - .ubwc_enc_version = UBWC_4_0, - /* TODO: highest_bank_bit = 15 for LP_DDR4 */ - .highest_bank_bit = 16, -}; - -static const struct qcom_ubwc_cfg_data sm8350_data = { - .ubwc_enc_version = UBWC_4_0, - /* TODO: highest_bank_bit = 15 for LP_DDR4 */ - .highest_bank_bit = 16, -}; - -static const struct qcom_ubwc_cfg_data sm8550_data = { - .ubwc_enc_version = UBWC_4_0, - /* TODO: highest_bank_bit = 15 for LP_DDR4 */ - .highest_bank_bit = 16, -}; - -static const struct qcom_ubwc_cfg_data sm8750_data = { - .ubwc_enc_version = UBWC_5_0, - /* TODO: highest_bank_bit = 15 for LP_DDR4 */ - .highest_bank_bit = 16, -}; - static const struct qcom_ubwc_cfg_data glymur_data = { .ubwc_enc_version = UBWC_5_0, .flags = UBWC_FLAG_DISABLE_SWIZZLE_LVL2 | @@ -149,61 +100,61 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { .compatible = "qcom,apq8016", .data = &no_ubwc_data }, { .compatible = "qcom,apq8026", .data = &no_ubwc_data }, { .compatible = "qcom,apq8074", .data = &no_ubwc_data }, - { .compatible = "qcom,apq8096", .data = &msm8998_data }, - { .compatible = "qcom,eliza", .data = &eliza_data, }, - { .compatible = "qcom,kaanapali", .data = &kaanapali_data, }, + { .compatible = "qcom,apq8096", .data = &ubwc_1_0_hbb15 }, + { .compatible = "qcom,eliza", .data = &ubwc_5_0_hbb15 }, { .compatible = "qcom,glymur", .data = &glymur_data}, + { .compatible = "qcom,kaanapali", .data = &ubwc_6_0_hbb16 }, { .compatible = "qcom,mahua", .data = &glymur_data }, { .compatible = "qcom,msm8226", .data = &no_ubwc_data }, { .compatible = "qcom,msm8916", .data = &no_ubwc_data }, { .compatible = "qcom,msm8917", .data = &no_ubwc_data }, - { .compatible = "qcom,msm8937", .data = &msm8937_data }, + { .compatible = "qcom,msm8937", .data = &ubwc_1_0_hbb14 }, { .compatible = "qcom,msm8929", .data = &no_ubwc_data }, { .compatible = "qcom,msm8939", .data = &no_ubwc_data }, - { .compatible = "qcom,msm8953", .data = &msm8937_data }, + { .compatible = "qcom,msm8953", .data = &ubwc_1_0_hbb14 }, { .compatible = "qcom,msm8956", .data = &no_ubwc_data }, { .compatible = "qcom,msm8974", .data = &no_ubwc_data }, { .compatible = "qcom,msm8976", .data = &no_ubwc_data }, - { .compatible = "qcom,msm8996", .data = &msm8998_data }, - { .compatible = "qcom,msm8998", .data = &msm8998_data }, - { .compatible = "qcom,qcm2290", .data = &qcm2290_data, }, - { .compatible = "qcom,qcm6490", .data = &sc7280_data, }, - { .compatible = "qcom,qcs8300", .data = &sc8280xp_data, }, - { .compatible = "qcom,sa8155p", .data = &sm8150_data, }, - { .compatible = "qcom,sa8540p", .data = &sc8280xp_data, }, + { .compatible = "qcom,msm8996", .data = &ubwc_1_0_hbb15 }, + { .compatible = "qcom,msm8998", .data = &ubwc_1_0_hbb15 }, + { .compatible = "qcom,qcm2290", .data = &ubwc_0_0_hbb15, }, + { .compatible = "qcom,qcm6490", .data = &ubwc_3_1_hbb14, }, + { .compatible = "qcom,qcs8300", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,sa8155p", .data = &ubwc_3_0_hbb15, }, + { .compatible = "qcom,sa8540p", .data = &ubwc_4_0_hbb16, }, { .compatible = "qcom,sa8775p", .data = &sa8775p_data, }, - { .compatible = "qcom,sar2130p", .data = &sar2130p_data }, - { .compatible = "qcom,sc7180", .data = &sc7180_data }, - { .compatible = "qcom,sc7280", .data = &sc7280_data, }, - { .compatible = "qcom,sc8180x", .data = &sc8180x_data, }, - { .compatible = "qcom,sc8280xp", .data = &sc8280xp_data, }, - { .compatible = "qcom,sda660", .data = &msm8937_data }, - { .compatible = "qcom,sdm450", .data = &msm8937_data }, - { .compatible = "qcom,sdm630", .data = &msm8937_data }, - { .compatible = "qcom,sdm632", .data = &msm8937_data }, - { .compatible = "qcom,sdm636", .data = &msm8937_data }, - { .compatible = "qcom,sdm660", .data = &msm8937_data }, - { .compatible = "qcom,sdm670", .data = &sdm670_data, }, - { .compatible = "qcom,sdm845", .data = &sdm845_data, }, - { .compatible = "qcom,sm4250", .data = &sm6115_data, }, - { .compatible = "qcom,sm6115", .data = &sm6115_data, }, - { .compatible = "qcom,sm6125", .data = &sm6125_data, }, - { .compatible = "qcom,sm6150", .data = &sm6150_data, }, - { .compatible = "qcom,sm6350", .data = &sm6350_data, }, - { .compatible = "qcom,sm6375", .data = &sm6350_data, }, - { .compatible = "qcom,sm7125", .data = &sc7180_data }, - { .compatible = "qcom,sm7150", .data = &sm7150_data, }, - { .compatible = "qcom,sm7225", .data = &sm6350_data, }, - { .compatible = "qcom,sm7325", .data = &sc7280_data, }, - { .compatible = "qcom,sm8150", .data = &sm8150_data, }, - { .compatible = "qcom,sm8250", .data = &sm8250_data, }, - { .compatible = "qcom,sm8350", .data = &sm8350_data, }, - { .compatible = "qcom,sm8450", .data = &sm8350_data, }, - { .compatible = "qcom,sm8550", .data = &sm8550_data, }, - { .compatible = "qcom,sm8650", .data = &sm8550_data, }, - { .compatible = "qcom,sm8750", .data = &sm8750_data, }, - { .compatible = "qcom,x1e80100", .data = &sm8550_data, }, - { .compatible = "qcom,x1p42100", .data = &sm8550_data, }, + { .compatible = "qcom,sar2130p", .data = &ubwc_3_1_hbb13 }, + { .compatible = "qcom,sc7180", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sc7280", .data = &ubwc_3_1_hbb14, }, + { .compatible = "qcom,sc8180x", .data = &ubwc_3_1_hbb16, }, + { .compatible = "qcom,sc8280xp", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,sda660", .data = &ubwc_1_0_hbb14 }, + { .compatible = "qcom,sdm450", .data = &ubwc_1_0_hbb14 }, + { .compatible = "qcom,sdm630", .data = &ubwc_1_0_hbb14 }, + { .compatible = "qcom,sdm632", .data = &ubwc_1_0_hbb14 }, + { .compatible = "qcom,sdm636", .data = &ubwc_1_0_hbb14 }, + { .compatible = "qcom,sdm660", .data = &ubwc_1_0_hbb14 }, + { .compatible = "qcom,sdm670", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sdm845", .data = &ubwc_2_0_hbb15, }, + { .compatible = "qcom,sm4250", .data = &ubwc_1_0_hbb14, }, + { .compatible = "qcom,sm6115", .data = &ubwc_1_0_hbb14, }, + { .compatible = "qcom,sm6125", .data = &ubwc_1_0_hbb14, }, + { .compatible = "qcom,sm6150", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sm6350", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sm6375", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sm7125", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sm7150", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sm7225", .data = &ubwc_2_0_hbb14, }, + { .compatible = "qcom,sm7325", .data = &ubwc_3_1_hbb14, }, + { .compatible = "qcom,sm8150", .data = &ubwc_3_0_hbb15, }, + { .compatible = "qcom,sm8250", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,sm8350", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,sm8450", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,sm8550", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,sm8650", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,sm8750", .data = &ubwc_5_0_hbb16, }, + { .compatible = "qcom,x1e80100", .data = &ubwc_4_0_hbb16, }, + { .compatible = "qcom,x1p42100", .data = &ubwc_4_0_hbb16, }, { } }; From 889ee1f4f6633f8300500b529518d30bec282762 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 1 May 2026 09:14:47 +0200 Subject: [PATCH 021/157] soc: qcom: ubwc: Add config for Milos Describe the Universal Bandwidth Compression (UBWC) configuration for the Milos SoC. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20260501-milos-mdss-v3-5-58bfc58c0e13@fairphone.com [bjorn: Rebased on recent refactorings] Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 31cc56a3c2d7..a3cb6d61bb65 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -96,6 +96,14 @@ static const struct qcom_ubwc_cfg_data glymur_data = { .highest_bank_bit = 16, }; +static const struct qcom_ubwc_cfg_data milos_data = { + .ubwc_enc_version = UBWC_4_0, + .flags = UBWC_SWIZZLE_ENABLE_LVL2 | + UBWC_SWIZZLE_ENABLE_LVL3, + /* TODO: highest_bank_bit = 14 for LP_DDR4 */ + .highest_bank_bit = 15, +}; + static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { .compatible = "qcom,apq8016", .data = &no_ubwc_data }, { .compatible = "qcom,apq8026", .data = &no_ubwc_data }, @@ -105,6 +113,7 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { .compatible = "qcom,glymur", .data = &glymur_data}, { .compatible = "qcom,kaanapali", .data = &ubwc_6_0_hbb16 }, { .compatible = "qcom,mahua", .data = &glymur_data }, + { .compatible = "qcom,milos", .data = &milos_data }, { .compatible = "qcom,msm8226", .data = &no_ubwc_data }, { .compatible = "qcom,msm8916", .data = &no_ubwc_data }, { .compatible = "qcom,msm8917", .data = &no_ubwc_data }, From 6342de0aed216b6df460b492ddb532b3e0ed16f1 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 15 Jun 2026 16:05:40 -0400 Subject: [PATCH 022/157] bus: ti-sysc: Fix /chosen node reference leak sysc_init_stdout_path() gets the /chosen node with of_find_node_by_path() to read stdout-path. The function then overwrites the local node pointer with the stdout-path lookup result, or exits on error, without dropping the /chosen reference. Keep the /chosen node in a separate variable and put it after the stdout-path value has been used for the lookup. The successful stdout node lookup remains referenced by the cached stdout_path pointer. Fixes: 3bb37c8e6e6a ("bus: ti-sysc: Handle stdout-path for debug console") Signed-off-by: Yuho Choi Reviewed-by: Andreas Kemnade Link: https://patch.msgid.link/20260615200540.770205-1-dbgh9129@gmail.com Signed-off-by: Kevin Hilman (TI) --- drivers/bus/ti-sysc.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index a5b9507de37c..e118b900c9ac 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -682,6 +682,7 @@ static struct device_node *stdout_path; static void sysc_init_stdout_path(struct sysc *ddata) { + struct device_node *chosen; struct device_node *np = NULL; const char *uart; @@ -691,15 +692,18 @@ static void sysc_init_stdout_path(struct sysc *ddata) if (stdout_path) return; - np = of_find_node_by_path("/chosen"); - if (!np) + chosen = of_find_node_by_path("/chosen"); + if (!chosen) goto err; - uart = of_get_property(np, "stdout-path", NULL); - if (!uart) + uart = of_get_property(chosen, "stdout-path", NULL); + if (!uart) { + of_node_put(chosen); goto err; + } np = of_find_node_by_path(uart); + of_node_put(chosen); if (!np) goto err; From c709c76f4cf1d8646362f5a00ce8da1bd8ed58d7 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 21 May 2026 13:12:29 +0000 Subject: [PATCH 023/157] soc: qcom: ice: Support IP version beyond v3 This driver currently supports Inline Crypto Engine (ICE) IP version 3 and 4. But this driver fully supports v5 and should support upcoming versions as well. So extend the version check to support versions 3 and beyond. Signed-off-by: Manivannan Sadhasivam Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260521131229.11199-1-mani@kernel.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ice.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c index 5f20108aa03e..e256595b9826 100644 --- a/drivers/soc/qcom/ice.c +++ b/drivers/soc/qcom/ice.c @@ -126,8 +126,8 @@ static bool qcom_ice_check_supported(struct qcom_ice *ice) int minor = FIELD_GET(GENMASK(23, 16), regval); int step = FIELD_GET(GENMASK(15, 0), regval); - /* For now this driver only supports ICE version 3 and 4. */ - if (major != 3 && major != 4) { + /* Support ICE version 3 and above. */ + if (major < 3) { dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n", major, minor, step); return false; From ed3233af30b711c2a434e95cc05531244fd07307 Mon Sep 17 00:00:00 2001 From: Sneh Mankad Date: Mon, 15 Jun 2026 14:24:36 +0530 Subject: [PATCH 024/157] soc: qcom: qcom_stats: Add SoCCP and DCP subsystems stats Add SMEM items for SoC Control Processor and Display Co-Processor subsystem stats which are present in Hawi SoC. Signed-off-by: Sneh Mankad Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260615-add_subsystem_lpm_stats-v1-1-d40fc7c755da@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_stats.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index 2e380faf9080..e7122444f12a 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -54,6 +54,8 @@ static const struct subsystem_data subsystems[] = { { "cdsp1", 607, 12 }, { "gpdsp0", 607, 17 }, { "gpdsp1", 607, 18 }, + { "soccp", 607, 19 }, + { "dcp", 607, 22 }, { "slpi", 608, 3 }, { "gpu", 609, 0 }, { "display", 610, 0 }, From 64774dea58969194ea5c27fa639954e551a87024 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Sat, 20 Jun 2026 16:04:06 +0800 Subject: [PATCH 025/157] bus: qcom-ebi2: Fix clock leak on probe failure qcom_ebi2_probe() enables the EBI2X and EBI2 clocks before it walks child nodes and populates child devices. If reading a child node's reg property fails, or if of_platform_default_populate() fails, probe returns without disabling either clock. Route those failure paths through the existing clock cleanup labels so a failed probe does not leave the clocks prepared and enabled. Fixes: 335a12754808 ("bus: qcom: add EBI2 driver") Signed-off-by: Ruoyu Wang Link: https://lore.kernel.org/r/20260620080406.1970447-1-ruoyuw560@gmail.com Signed-off-by: Bjorn Andersson --- drivers/bus/qcom-ebi2.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c index be8166565e7c..ab00c75b9e95 100644 --- a/drivers/bus/qcom-ebi2.c +++ b/drivers/bus/qcom-ebi2.c @@ -353,7 +353,7 @@ static int qcom_ebi2_probe(struct platform_device *pdev) /* Figure out the chipselect */ ret = of_property_read_u32(child, "reg", &csindex); if (ret) - return ret; + goto err_disable_clk; if (csindex > 5) { dev_err(dev, @@ -372,8 +372,12 @@ static int qcom_ebi2_probe(struct platform_device *pdev) have_children = true; } - if (have_children) - return of_platform_default_populate(np, NULL, dev); + if (have_children) { + ret = of_platform_default_populate(np, NULL, dev); + if (ret) + goto err_disable_clk; + } + return 0; err_disable_clk: From c63037c69a5173dd87efde82bfa4c339adefd72e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 1 Jul 2026 12:58:50 +0200 Subject: [PATCH 026/157] dt-bindings: rockchip: pmu: Narrow definition of reboot-mode The 'reboot-mode' child of PMU node must be a syscon-reboot-mode one, so add missing $ref to enforce the validation and disallow other reboot-mode handlers. Defining 'type: object' is on the other hand not necessary when other schema is referenced. This can be further restricted to match hardware/firmware: syscon-reboot-mode schema allows arbitrary "mode-.* properties but only a subset actually makes sense and is valid. Provide negative look-ahead pattern to disallow any modes not supported by the device, which tightens the binding. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260701105849.197086-3-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Heiko Stuebner --- Documentation/devicetree/bindings/arm/rockchip/pmu.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml b/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml index 55b2200d6e75..1cff2328cd01 100644 --- a/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml +++ b/Documentation/devicetree/bindings/arm/rockchip/pmu.yaml @@ -61,7 +61,13 @@ properties: type: object reboot-mode: - type: object + $ref: /schemas/power/reset/syscon-reboot-mode.yaml + unevaluatedProperties: false + + patternProperties: + # Negative look-ahead to disallow unsupported modes. The '$' has to be + # part of lookahead group to work, instead of trailing outside of (). + "^mode-(?!(bootloader$|loader$|normal$|recovery$))": false required: - compatible From 39eace59ac826465fae0788d0a6b304ec94f9944 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 1 Jul 2026 12:58:51 +0200 Subject: [PATCH 027/157] dt-bindings: rockchip: grf: Narrow allowed reboot modes syscon-reboot-mode schema allows arbitrary "mode-.* properties but only a subset actually makes sense and is valid. Provide negative look-ahead pattern to disallow any modes not supported by the device, which tightens the binding. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260701105849.197086-4-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Heiko Stuebner --- Documentation/devicetree/bindings/soc/rockchip/grf.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml index 2cc43742b8e3..7bcb4e2f47ec 100644 --- a/Documentation/devicetree/bindings/soc/rockchip/grf.yaml +++ b/Documentation/devicetree/bindings/soc/rockchip/grf.yaml @@ -239,11 +239,14 @@ allOf: properties: reboot-mode: type: object - $ref: /schemas/power/reset/syscon-reboot-mode.yaml# - unevaluatedProperties: false + patternProperties: + # Negative look-ahead to disallow unsupported modes. The '$' has to be + # part of lookahead group to work, instead of trailing outside of (). + "^mode-(?!(bootloader$|fastboot$|loader$|normal$|recovery$))": false + - if: properties: compatible: From cf14d0f9e75c83a0879c1fcf2e85972b552f26f9 Mon Sep 17 00:00:00 2001 From: Jingyi Wang Date: Thu, 4 Jun 2026 22:45:44 -0700 Subject: [PATCH 028/157] dt-bindings: firmware: qcom,scm: Document SCM on Maili SOC Document SCM compatible for the Qualcomm Maili SoC. Signed-off-by: Jingyi Wang Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260604-maili-soc-binding-v2-1-21b5e9bd1aa5@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index 25f62bacbc91..f344d1390b1b 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -37,6 +37,7 @@ properties: - qcom,scm-ipq9574 - qcom,scm-ipq9650 - qcom,scm-kaanapali + - qcom,scm-maili - qcom,scm-mdm9607 - qcom,scm-milos - qcom,scm-msm8226 @@ -212,6 +213,7 @@ allOf: - qcom,scm-eliza - qcom,scm-hawi - qcom,scm-kaanapali + - qcom,scm-maili - qcom,scm-milos - qcom,scm-sm8450 - qcom,scm-sm8550 From ffc5383f2c180a8541ff05879b39afb19513732e Mon Sep 17 00:00:00 2001 From: Jingyi Wang Date: Thu, 4 Jun 2026 22:45:45 -0700 Subject: [PATCH 029/157] dt-bindings: sram: Document qcom,maili-imem compatible On Qualcomm Maili platform, IMEM is a block of SRAM shared across multiple IP blocks which can fall back to "mmio-sram". Document its compatible. Signed-off-by: Jingyi Wang Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260604-maili-soc-binding-v2-2-21b5e9bd1aa5@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/sram/sram.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml index eb695698a03e..14a537e82a84 100644 --- a/Documentation/devicetree/bindings/sram/sram.yaml +++ b/Documentation/devicetree/bindings/sram/sram.yaml @@ -37,6 +37,7 @@ properties: - qcom,eliza-imem - qcom,hawi-imem - qcom,kaanapali-imem + - qcom,maili-imem - qcom,milos-imem - qcom,rpm-msg-ram - rockchip,rk3288-pmu-sram From 30783d878a26da939f9a6d57a1d7012cc553e340 Mon Sep 17 00:00:00 2001 From: Chunkai Deng Date: Thu, 4 Jun 2026 22:45:46 -0700 Subject: [PATCH 030/157] dt-bindings: soc: qcom,aoss-qmp: Document the Maili AOSS side channel Document the Always-on Subsystem side channel on Qualcomm Maili SoC. Signed-off-by: Chunkai Deng Signed-off-by: Jingyi Wang Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260604-maili-soc-binding-v2-3-21b5e9bd1aa5@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml index 8eaa04431d74..9f9d9bcb66c4 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml @@ -29,6 +29,7 @@ properties: - qcom,glymur-aoss-qmp - qcom,hawi-aoss-qmp - qcom,kaanapali-aoss-qmp + - qcom,maili-aoss-qmp - qcom,milos-aoss-qmp - qcom,qcs615-aoss-qmp - qcom,qcs8300-aoss-qmp From 3467fec5322678463fa5e43556eb0216f6a0ca22 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Sun, 31 May 2026 03:09:01 +0530 Subject: [PATCH 031/157] soc: qcom: llcc: Add configuration data for Shikra SoC Add Last Level Cache table and configs for the Shikra SoC. Signed-off-by: Komal Bajaj Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260531-shikra_llcc_conf-v1-1-fa405f5a2404@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 8948b5fd42d2..22c8099cf6bb 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -2305,6 +2305,20 @@ static const struct llcc_slice_config sdm845_data[] = {{ }, }; +static const struct llcc_slice_config shikra_data[] = { + { + .usecase_id = LLCC_ECC, + .slice_id = 26, + .max_cap = 256, + .priority = 3, + .fixed_size = true, + .bonus_ways = 0x3, + .cache_mode = 0, + .activate_on_init = true, + .vict_prio = true, + }, +}; + static const struct llcc_slice_config sm6350_data[] = { { .usecase_id = LLCC_CPUSS, @@ -4575,6 +4589,15 @@ static const struct qcom_llcc_config sdm845_cfg[] = { }, }; +static const struct qcom_llcc_config shikra_cfg[] = { + { + .sct_data = shikra_data, + .size = ARRAY_SIZE(shikra_data), + .reg_offset = llcc_v2_1_reg_offset, + .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + }, +}; + static const struct qcom_llcc_config sm6350_cfg[] = { { .sct_data = sm6350_data, @@ -4752,6 +4775,11 @@ static const struct qcom_sct_config sdm845_cfgs = { .num_config = ARRAY_SIZE(sdm845_cfg), }; +static const struct qcom_sct_config shikra_cfgs = { + .llcc_config = shikra_cfg, + .num_config = ARRAY_SIZE(shikra_cfg), +}; + static const struct qcom_sct_config sm6350_cfgs = { .llcc_config = sm6350_cfg, .num_config = ARRAY_SIZE(sm6350_cfg), @@ -5632,6 +5660,7 @@ static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sc8280xp-llcc", .data = &sc8280xp_cfgs }, { .compatible = "qcom,sdm670-llcc", .data = &sdm670_cfgs }, { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfgs }, + { .compatible = "qcom,shikra-llcc", .data = &shikra_cfgs }, { .compatible = "qcom,sm6350-llcc", .data = &sm6350_cfgs }, { .compatible = "qcom,sm7150-llcc", .data = &sm7150_cfgs }, { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfgs }, From 63fba7528f2c329595321187a92b2caf16141be0 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 8 Jun 2026 14:11:38 +0530 Subject: [PATCH 032/157] soc: qcom: pd-mapper: Add support for SA8775P Add support for the Qualcomm SA8775P SoC to the protection domain mapper. SA8775P share the same protection domain configuration as SC8280XP with an additional gpdsp domain, except for charger_pd. Add an entry to the kernel, to avoid the need for userspace to provide this service. Signed-off-by: Mohammad Rafi Shaik Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260608084139.1468000-2-mohammad.rafi.shaik@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_pd_mapper.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c index b99718e25f2f..03a5a3a73719 100644 --- a/drivers/soc/qcom/qcom_pd_mapper.c +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -311,6 +311,24 @@ static const struct qcom_pdm_domain_data cdsp_root_pd = { .services = { NULL }, }; +static const struct qcom_pdm_domain_data cdsp1_root_pd = { + .domain = "msm/cdsp1/root_pd", + .instance_id = 125, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data gpdsp_root_pd = { + .domain = "msm/gpdsp/root_pd", + .instance_id = 192, + .services = { NULL }, +}; + +static const struct qcom_pdm_domain_data gpdsp1_root_pd = { + .domain = "msm/gpdsp1/root_pd", + .instance_id = 241, + .services = { NULL }, +}; + static const struct qcom_pdm_domain_data slpi_root_pd = { .domain = "msm/slpi/root_pd", .instance_id = 90, @@ -426,6 +444,16 @@ static const struct qcom_pdm_domain_data *qcs615_domains[] = { NULL, }; +static const struct qcom_pdm_domain_data *sa8775p_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &cdsp1_root_pd, + &gpdsp_root_pd, + &gpdsp1_root_pd, + NULL, +}; + static const struct qcom_pdm_domain_data *sc7180_domains[] = { &adsp_audio_pd, &adsp_root_pd_pdr, @@ -603,6 +631,7 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { { .compatible = "qcom,qcm6490", .data = sc7280_domains, }, { .compatible = "qcom,qcs404", .data = qcs404_domains, }, { .compatible = "qcom,qcs615", .data = qcs615_domains, }, + { .compatible = "qcom,sa8775p", .data = sa8775p_domains, }, { .compatible = "qcom,sc7180", .data = sc7180_domains, }, { .compatible = "qcom,sc7280", .data = sc7280_domains, }, { .compatible = "qcom,sc8180x", .data = sc8180x_domains, }, From 17e26dfa92a502212577c4dbdb55426f1476d521 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 8 Jun 2026 14:11:39 +0530 Subject: [PATCH 033/157] soc: qcom: pd-mapper: Add support for QCS8300 Add support for the Qualcomm QCS8300 SoC to the protection domain mapper. QCS8300 share the same protection domain configuration as SC8280XP, except charger_pd. Add an entry to the kernel, to avoid the need for userspace to provide this service. Signed-off-by: Mohammad Rafi Shaik Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260608084139.1468000-3-mohammad.rafi.shaik@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_pd_mapper.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c index 03a5a3a73719..d98c6df52bea 100644 --- a/drivers/soc/qcom/qcom_pd_mapper.c +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -444,6 +444,14 @@ static const struct qcom_pdm_domain_data *qcs615_domains[] = { NULL, }; +static const struct qcom_pdm_domain_data *qcs8300_domains[] = { + &adsp_audio_pd, + &adsp_root_pd, + &cdsp_root_pd, + &gpdsp_root_pd, + NULL, +}; + static const struct qcom_pdm_domain_data *sa8775p_domains[] = { &adsp_audio_pd, &adsp_root_pd, @@ -631,6 +639,7 @@ static const struct of_device_id qcom_pdm_domains[] __maybe_unused = { { .compatible = "qcom,qcm6490", .data = sc7280_domains, }, { .compatible = "qcom,qcs404", .data = qcs404_domains, }, { .compatible = "qcom,qcs615", .data = qcs615_domains, }, + { .compatible = "qcom,qcs8300", .data = qcs8300_domains, }, { .compatible = "qcom,sa8775p", .data = sa8775p_domains, }, { .compatible = "qcom,sc7180", .data = sc7180_domains, }, { .compatible = "qcom,sc7280", .data = sc7280_domains, }, From 5239692170f1815c0bbc57de432d7eb8b616f8f1 Mon Sep 17 00:00:00 2001 From: Sumit Gupta Date: Fri, 26 Jun 2026 18:44:23 +0530 Subject: [PATCH 034/157] memory: tegra: add multi-socket support to the memory interconnect Add support for representing each memory-controller instance (one per NUMA node / socket) as its own interconnect (ICC) provider, with its own MC client nodes, to match the hardware topology on multi-socket Tegra SoCs. Use the NUMA node ID to make client IDs globally unique across per-socket providers, since the ICC framework allocates node IDs from a single global IDR. Per-socket MC and EMC node names are also derived from dev_name() so they match the corresponding debugfs subdirectory. On single-socket platforms (NUMA_NO_NODE) the existing client IDs and node-name strings are preserved. Each socket's MC and EMC therefore get their own debugfs subdirectory under /sys/kernel/debug/{mc,emc}/. The parent directories are created on first probe. Bandwidth requests from MC clients in a socket are routed to that socket's local BPMP. Signed-off-by: Sumit Gupta Tested-by: Jon Hunter Reviewed-by: Jon Hunter Link: https://patch.msgid.link/20260626131423.3986998-1-sumitg@nvidia.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/mc.c | 40 ++++++++++++---- drivers/memory/tegra/mc.h | 37 +++++++++++++++ drivers/memory/tegra/tegra186-emc.c | 71 ++++++++++++++++++++++------- 3 files changed, 123 insertions(+), 25 deletions(-) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index ec80ea9cc173..51ad0d3e48d4 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -3,6 +3,7 @@ * Copyright (C) 2014-2026 NVIDIA CORPORATION. All rights reserved. */ +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +24,9 @@ #include "mc.h" +static DEFINE_MUTEX(tegra_mc_debugfs_root_lock); +static struct dentry *tegra_mc_debugfs_root; + static const struct of_device_id tegra_mc_of_match[] = { #ifdef CONFIG_ARCH_TEGRA_2x_SOC { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, @@ -778,7 +783,7 @@ struct icc_node *tegra_mc_icc_xlate(const struct of_phandle_args *spec, void *da struct icc_node *node; list_for_each_entry(node, &mc->provider.nodes, node_list) { - if (node->id == spec->args[0]) + if (tegra_mc_client_id_from_node(node) == spec->args[0]) return node; } @@ -834,6 +839,7 @@ const struct tegra_mc_icc_ops tegra_mc_icc_ops = { */ static int tegra_mc_interconnect_setup(struct tegra_mc *mc) { + int node_id = dev_to_node(mc->dev); struct icc_node *node; unsigned int i; int err; @@ -854,31 +860,40 @@ static int tegra_mc_interconnect_setup(struct tegra_mc *mc) icc_provider_init(&mc->provider); /* create Memory Controller node */ - node = icc_node_create(TEGRA_ICC_MC); + node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_MC); if (IS_ERR(node)) return PTR_ERR(node); - node->name = "Memory Controller"; + if (node_id == NUMA_NO_NODE) + node->name = "Memory Controller"; + else + node->name = dev_name(mc->dev); + icc_node_add(node, &mc->provider); /* link Memory Controller to External Memory Controller */ - err = icc_link_create(node, TEGRA_ICC_EMC); + err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_EMC); if (err) goto remove_nodes; for (i = 0; i < mc->soc->num_clients; i++) { /* create MC client node */ - node = icc_node_create(mc->soc->clients[i].id); + node = tegra_mc_icc_node_create(node_id, mc->soc->clients[i].id); if (IS_ERR(node)) { err = PTR_ERR(node); goto remove_nodes; } - node->name = mc->soc->clients[i].name; + if (node_id == NUMA_NO_NODE) + node->name = mc->soc->clients[i].name; + else + node->name = devm_kasprintf(mc->dev, GFP_KERNEL, "%d-%s", + node_id, mc->soc->clients[i].name); + icc_node_add(node, &mc->provider); /* link Memory Client to Memory Controller */ - err = icc_link_create(node, TEGRA_ICC_MC); + err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_MC); if (err) goto remove_nodes; @@ -957,7 +972,16 @@ static int tegra_mc_probe(struct platform_device *pdev) if (IS_ERR(mc->regs)) return PTR_ERR(mc->regs); - mc->debugfs.root = debugfs_create_dir("mc", NULL); + scoped_guard(mutex, &tegra_mc_debugfs_root_lock) { + if (!tegra_mc_debugfs_root) + tegra_mc_debugfs_root = debugfs_create_dir("mc", NULL); + + if (dev_to_node(mc->dev) == NUMA_NO_NODE) + mc->debugfs.root = tegra_mc_debugfs_root; + else + mc->debugfs.root = debugfs_create_dir(dev_name(mc->dev), + tegra_mc_debugfs_root); + } if (mc->soc->ops && mc->soc->ops->probe) { err = mc->soc->ops->probe(mc); diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index e94d265d7b67..01499292dd5f 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -167,6 +168,42 @@ icc_provider_to_tegra_mc(struct icc_provider *provider) return container_of(provider, struct tegra_mc, provider); } +/* + * Compose a globally-unique ICC node ID. On single-socket + * systems (NUMA_NO_NODE), the SoC client ID is returned unchanged. + * On multi-socket systems, the NUMA node ID is encoded in the + * upper bits of the returned ID. + * + * The client ID field is sized to keep composed IDs below + * ICC_DYN_ID_START (the start of the ICC core's dynamic-ID range). + */ +#define TEGRA_MC_CLIENT_ID_BITS 12 +#define TEGRA_MC_CLIENT_ID_MASK ((1U << TEGRA_MC_CLIENT_ID_BITS) - 1) + +static inline u32 tegra_mc_get_client_id(int node_id, int id) +{ + if (node_id == NUMA_NO_NODE) + return id; + + return ((node_id + 1) << TEGRA_MC_CLIENT_ID_BITS) | id; +} + +static inline struct icc_node *tegra_mc_icc_node_create(int node_id, int id) +{ + return icc_node_create(tegra_mc_get_client_id(node_id, id)); +} + +static inline int tegra_mc_icc_link_create(struct icc_node *node, int node_id, int id) +{ + return icc_link_create(node, tegra_mc_get_client_id(node_id, id)); +} + +/* Return the SoC client ID encoded in an ICC node ID. */ +static inline u32 tegra_mc_client_id_from_node(const struct icc_node *node) +{ + return node->id & TEGRA_MC_CLIENT_ID_MASK; +} + static inline u32 mc_ch_readl(const struct tegra_mc *mc, int ch, unsigned long offset) { diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c index f71265b303b9..e25f485e136e 100644 --- a/drivers/memory/tegra/tegra186-emc.c +++ b/drivers/memory/tegra/tegra186-emc.c @@ -3,16 +3,22 @@ * Copyright (C) 2019-2025 NVIDIA CORPORATION. All rights reserved. */ +#include #include #include +#include #include #include +#include #include #include #include #include "mc.h" +static DEFINE_MUTEX(tegra_emc_debugfs_root_lock); +static struct dentry *tegra_emc_debugfs_root; + struct tegra186_emc_dvfs { unsigned long latency; unsigned long rate; @@ -207,7 +213,17 @@ static int tegra186_emc_get_emc_dvfs_latency(struct tegra186_emc *emc) return err; } - emc->debugfs.root = debugfs_create_dir("emc", NULL); + scoped_guard(mutex, &tegra_emc_debugfs_root_lock) { + if (!tegra_emc_debugfs_root) + tegra_emc_debugfs_root = debugfs_create_dir("emc", NULL); + + if (dev_to_node(emc->dev) == NUMA_NO_NODE) + emc->debugfs.root = tegra_emc_debugfs_root; + else + emc->debugfs.root = debugfs_create_dir(dev_name(emc->dev), + tegra_emc_debugfs_root); + } + debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc, &tegra186_emc_debug_available_rates_fops); debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc, @@ -239,7 +255,7 @@ tegra186_emc_of_icc_xlate(const struct of_phandle_args *spec, void *data) /* External Memory is the only possible ICC route */ list_for_each_entry(node, &provider->nodes, node_list) { - if (node->id != TEGRA_ICC_EMEM) + if (tegra_mc_client_id_from_node(node) != TEGRA_ICC_EMEM) continue; return node; @@ -258,6 +274,7 @@ static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *pe static int tegra186_emc_interconnect_init(struct tegra186_emc *emc) { + int node_id = dev_to_node(emc->dev->parent); struct icc_node *node; int err; @@ -271,26 +288,36 @@ static int tegra186_emc_interconnect_init(struct tegra186_emc *emc) icc_provider_init(&emc->provider); /* create External Memory Controller node */ - node = icc_node_create(TEGRA_ICC_EMC); - if (IS_ERR(node)) - return PTR_ERR(node); - - node->name = "External Memory Controller"; - icc_node_add(node, &emc->provider); - - /* link External Memory Controller to External Memory (DRAM) */ - err = icc_link_create(node, TEGRA_ICC_EMEM); - if (err) - goto remove_nodes; - - /* create External Memory node */ - node = icc_node_create(TEGRA_ICC_EMEM); + node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_EMC); if (IS_ERR(node)) { err = PTR_ERR(node); goto remove_nodes; } - node->name = "External Memory (DRAM)"; + if (node_id == NUMA_NO_NODE) + node->name = "External Memory Controller"; + else + node->name = dev_name(emc->dev); + + icc_node_add(node, &emc->provider); + + /* link External Memory Controller to External Memory (DRAM) */ + err = tegra_mc_icc_link_create(node, node_id, TEGRA_ICC_EMEM); + if (err) + goto remove_nodes; + + /* create External Memory node */ + node = tegra_mc_icc_node_create(node_id, TEGRA_ICC_EMEM); + if (IS_ERR(node)) { + err = PTR_ERR(node); + goto remove_nodes; + } + + if (node_id == NUMA_NO_NODE) + node->name = "External Memory (DRAM)"; + else + node->name = devm_kasprintf(emc->dev, GFP_KERNEL, "%d-dram", node_id); + icc_node_add(node, &emc->provider); err = icc_provider_register(&emc->provider); @@ -383,6 +410,16 @@ static void tegra186_emc_remove(struct platform_device *pdev) debugfs_remove_recursive(emc->debugfs.root); + scoped_guard(mutex, &tegra_emc_debugfs_root_lock) { + if (emc->debugfs.root == tegra_emc_debugfs_root) { + tegra_emc_debugfs_root = NULL; + } else if (tegra_emc_debugfs_root && + simple_empty(tegra_emc_debugfs_root)) { + debugfs_remove(tegra_emc_debugfs_root); + tegra_emc_debugfs_root = NULL; + } + } + mc->bpmp = NULL; tegra_bpmp_put(emc->bpmp); } From 8111c7f3723f414b7f655a3880775cbbd0e9de8e Mon Sep 17 00:00:00 2001 From: Ashish Mhetre Date: Fri, 3 Jul 2026 04:56:53 +0000 Subject: [PATCH 035/157] memory: tegra: Guard against NULL mc_regs in IRQ handler The per-error decode in tegra30_mc_handle_irq() dereferences mc->soc->regs unconditionally. This 'regs' structure is optional and is only used for decoding and logging MC error interrupts. The rest of the MC functionality does not depend on it. When adding support for the Tegra238 SoC, the 'regs' structure was initially omitted because it is only used for error logging. We found that this resulted in a NULL pointer dereference in IRQ context, causing a crash when an MC error interrupt fired. Although existing upstream devices will not hit this condition because 'regs' is present, guard against it to improve the robustness of the driver. Skip the decode and just clear the interrupt when mc_regs is NULL. This bypasses the error interrupt logging while keeping the remaining MC functionality intact. Signed-off-by: Ashish Mhetre Link: https://patch.msgid.link/20260703045653.395498-1-amhetre@nvidia.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/mc.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 51ad0d3e48d4..458ce99916ad 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -603,6 +603,13 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) if (!status) return IRQ_NONE; + if (!mc->soc->regs) { + dev_err_ratelimited(mc->dev, + "MC error interrupt 0x%08lx with no error register map, Clearing.\n", + status); + goto clear; + } + for_each_set_bit(bit, &status, 32) { const char *error = tegra_mc_status_names[bit] ?: "unknown"; const char *client = "unknown", *desc; @@ -741,6 +748,7 @@ irqreturn_t tegra30_mc_handle_irq(int irq, void *data) desc, perm); } +clear: /* clear interrupts */ if (mc->soc->num_channels) { mc_ch_writel(mc, channel, status, MC_INTSTATUS); From 3527091de0feb27e437bce175048b54be835221b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 1 Jul 2026 12:57:32 +0200 Subject: [PATCH 036/157] dt-bindings: samsung: exynos-pmu: Restrict children with unevaluatedProperties Each schema for a child node should end with either additionalProperties or unevaluatedProperties, so add missing one making the schema tighter. Defining 'type: object' is on the other hand not necessary when other schema is referenced. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Peter Griffin Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260701105731.196618-3-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Krzysztof Kozlowski --- .../devicetree/bindings/soc/samsung/exynos-pmu.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index 76ce7e98c10f..d124f3ae9fbd 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -94,19 +94,19 @@ properties: reboot-mode: $ref: /schemas/power/reset/syscon-reboot-mode.yaml - type: object + unevaluatedProperties: false description: Reboot mode to alter bootloader behavior for the next boot syscon-poweroff: $ref: /schemas/power/reset/syscon-poweroff.yaml# - type: object + unevaluatedProperties: false description: Node for power off method syscon-reboot: $ref: /schemas/power/reset/syscon-reboot.yaml# - type: object + unevaluatedProperties: false description: Node for reboot method From e131ca83dabc8bfb8c1d455d02bf2d7a7bc1198a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 1 Jul 2026 12:57:33 +0200 Subject: [PATCH 037/157] dt-bindings: samsung: exynos-pmu: Narrow allowed reboot modes syscon-reboot-mode schema allows arbitrary "mode-.* properties but only a subset actually makes sense and is valid. Provide negative look-ahead pattern to disallow any modes not supported by the device, which tightens the binding. Most of Android-based Exynos devices share the bootloader or its parts, thus reboot modes are most likely the same, however only a few upstream DTS define them, so add restrictions only for these. Google GS101 has on the other hand quite different bootloader with many known reboot modes. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Peter Griffin Acked-by: Rob Herring (Arm) Link: https://patch.msgid.link/20260701105731.196618-4-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Krzysztof Kozlowski --- .../bindings/soc/samsung/exynos-pmu.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml index d124f3ae9fbd..5bd39876e3b3 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml +++ b/Documentation/devicetree/bindings/soc/samsung/exynos-pmu.yaml @@ -176,6 +176,20 @@ allOf: properties: dp-phy: false + - if: + properties: + compatible: + contains: + enum: + - samsung,exynos7-pmu + then: + properties: + reboot-mode: + patternProperties: + # Negative look-ahead to disallow unsupported modes. The '$' has to be + # part of lookahead group to work, instead of trailing outside of (). + "^mode-(?!(bootloader$|download$|fastboot$|recovery$))": false + - if: properties: compatible: @@ -183,6 +197,13 @@ allOf: enum: - google,gs101-pmu then: + properties: + reboot-mode: + patternProperties: + # Negative look-ahead to disallow unsupported modes. The '$' has to be + # part of lookahead group to work, instead of trailing outside of (). + "^mode-(?!(bootloader$|charge$|dm-verity-device-corrupted$|fastboot$|reboot-ab-update$|recovery$)|rescue$|shutdown-thermal$|shutdown-thermal-battery$)": false + required: - google,pmu-intr-gen-syscon else: From e4a3c99bbf779d6357adb8101ad18f34c2194306 Mon Sep 17 00:00:00 2001 From: Griffin Kroah-Hartman Date: Thu, 9 Jul 2026 15:24:16 +0200 Subject: [PATCH 038/157] soc: mediatek: mtk-socinfo Add error handling in devm_kasprintf() Add an error check to the devm_kasprintf() function in mtk_socinfo_create_socinfo_node(), returning -ENOMEM if the function failed. Assisted-by: gkh_clanker_t1000 CC: Matthias Brugger CC: AngeloGioacchino Del Regno Signed-off-by: Griffin Kroah-Hartman Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-socinfo.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c index 424a1eb82c20..e294730ba709 100644 --- a/drivers/soc/mediatek/mtk-socinfo.c +++ b/drivers/soc/mediatek/mtk-socinfo.c @@ -76,11 +76,14 @@ static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop) if (!attrs) return -ENOMEM; - if (data->marketing_name != NULL && data->marketing_name[0] != '\0') + if (data->marketing_name != NULL && data->marketing_name[0] != '\0') { attrs->family = devm_kasprintf(mtk_socinfop->dev, GFP_KERNEL, "MediaTek %s", data->marketing_name); - else + if (!attrs->family) + return -ENOMEM; + } else { attrs->family = soc_manufacturer; + } attrs->soc_id = data->soc_name; /* From 3300a285c28529ca1a178f01b4cc046fd3547dbd Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Tue, 7 Jul 2026 16:58:14 +0200 Subject: [PATCH 039/157] soc: fsl: qe_ports_ic: Use generic I/O helper instead of specific powerPC ones Use ioread32be() and iowrite32be() instead of in_be32() and out_be32() to allow build on other platforms than powerPC. Link: https://lore.kernel.org/r/b08f76c1d8ff864774246f1e2c2158c223c001be.1783435914.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 9b0bba64e91e..33ca1ddafe18 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -23,36 +23,39 @@ struct qepic_data { static void qepic_mask(struct irq_data *d) { struct qepic_data *data = irq_data_get_irq_chip_data(d); + u32 val = ioread32be(data->reg + CEPIMR); - clrbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d))); + iowrite32be(val & ~(1 << (31 - irqd_to_hwirq(d))), data->reg + CEPIMR); } static void qepic_unmask(struct irq_data *d) { struct qepic_data *data = irq_data_get_irq_chip_data(d); + u32 val = ioread32be(data->reg + CEPIMR); - setbits32(data->reg + CEPIMR, 1 << (31 - irqd_to_hwirq(d))); + iowrite32be(val | 1 << (31 - irqd_to_hwirq(d)), data->reg + CEPIMR); } static void qepic_end(struct irq_data *d) { struct qepic_data *data = irq_data_get_irq_chip_data(d); - out_be32(data->reg + CEPIER, 1 << (31 - irqd_to_hwirq(d))); + iowrite32be(1 << (31 - irqd_to_hwirq(d)), data->reg + CEPIER); } static int qepic_set_type(struct irq_data *d, unsigned int flow_type) { struct qepic_data *data = irq_data_get_irq_chip_data(d); unsigned int vec = (unsigned int)irqd_to_hwirq(d); + u32 val = ioread32be(data->reg + CEPICR); switch (flow_type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_FALLING: - setbits32(data->reg + CEPICR, 1 << (31 - vec)); + iowrite32be(val | 1 << (31 - vec), data->reg + CEPICR); return 0; case IRQ_TYPE_EDGE_BOTH: case IRQ_TYPE_NONE: - clrbits32(data->reg + CEPICR, 1 << (31 - vec)); + iowrite32be(val & ~(1 << (31 - vec)), data->reg + CEPICR); return 0; } return -EINVAL; @@ -69,7 +72,7 @@ static struct irq_chip qepic = { static int qepic_get_irq(struct irq_desc *desc) { struct qepic_data *data = irq_desc_get_handler_data(desc); - u32 event = in_be32(data->reg + CEPIER); + u32 event = ioread32be(data->reg + CEPIER); if (!event) return -1; From 65486d9827d9aada854573bf57b836aa752fece0 Mon Sep 17 00:00:00 2001 From: Nabige Aala Date: Mon, 8 Jun 2026 12:31:43 +0530 Subject: [PATCH 040/157] soc: qcom: ubwc: Add Shikra UBWC config Add UBWC configuration for the Shikra platform. Shikra shares the same hardware as QCM2290 (Agatti), so reuse qcm2290_data for the UBWC settings Reviewed-by: Dmitry Baryshkov Signed-off-by: Nabige Aala Link: https://lore.kernel.org/r/20260608-shikra-display-v4-2-88a846afdd5d@oss.qualcomm.com [bjorn: Translated to new generic definitions] Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index a3cb6d61bb65..0209a02c390f 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -145,6 +145,7 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { .compatible = "qcom,sdm660", .data = &ubwc_1_0_hbb14 }, { .compatible = "qcom,sdm670", .data = &ubwc_2_0_hbb14, }, { .compatible = "qcom,sdm845", .data = &ubwc_2_0_hbb15, }, + { .compatible = "qcom,shikra", .data = &ubwc_0_0_hbb15, }, { .compatible = "qcom,sm4250", .data = &ubwc_1_0_hbb14, }, { .compatible = "qcom,sm6115", .data = &ubwc_1_0_hbb14, }, { .compatible = "qcom,sm6125", .data = &ubwc_1_0_hbb14, }, From 49bd2cc8c3bd1fd3c9da113690383d674038ffa6 Mon Sep 17 00:00:00 2001 From: Fenglin Wu Date: Wed, 10 Jun 2026 00:10:13 -0700 Subject: [PATCH 041/157] dt-bindings: soc: qcom: qcom,pmic-glink: Add Maili compatible string Maili is a mobile platform that is compatible with Hawi and Kaanapali platform with respect to pmic-glink support. Add Maili compatible string with Kaanapali as a fallback. Signed-off-by: Fenglin Wu Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260610-maili-pmic-glink-v1-1-a6ba02d6deba@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml index 2db4288a8a54..16ad4f5698fc 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,pmic-glink.yaml @@ -35,6 +35,7 @@ properties: - items: - enum: - qcom,hawi-pmic-glink + - qcom,maili-pmic-glink - const: qcom,kaanapali-pmic-glink - const: qcom,pmic-glink - items: From 08314e7c2c38b9ae6a5e01c58ed10a950859404d Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 2 Jul 2026 17:28:17 +0530 Subject: [PATCH 042/157] firmware: qcom: Add a generic PAS service Qcom platforms has the legacy of using non-standard SCM calls splintered over the various kernel drivers. These SCM calls aren't compliant with the standard SMC calling conventions which is a prerequisite to enable migration to the FF-A specifications from Arm. OP-TEE as an alternative trusted OS to Qualcomm TEE (QTEE) can't support these non-standard SCM calls. And even for newer architectures using S-EL2 with Hafnium support, QTEE won't be able to support SCM calls either with FF-A requirements coming in. And with both OP-TEE and QTEE drivers well integrated in the TEE subsystem, it makes further sense to reuse the TEE bus client drivers infrastructure. The added benefit of TEE bus infrastructure is that there is support for discoverable/enumerable services. With that client drivers don't have to manually invoke a special SCM call to know the service status. So enable the generic Peripheral Authentication Service (PAS) provided by the firmware. It acts as the common layer with different TZ backends plugged in whether it's an SCM implementation or a proper TEE bus based PAS service implementation. Reviewed-by: Mukesh Ojha Tested-by: Mukesh Ojha # Lemans Reviewed-by: Harshal Dev Tested-by: Vignesh Viswanathan # IPQ9650 Signed-off-by: Sumit Garg Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260702115835.167602-2-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/Kconfig | 8 + drivers/firmware/qcom/Makefile | 1 + drivers/firmware/qcom/qcom_pas.c | 298 +++++++++++++++++++++++++ drivers/firmware/qcom/qcom_pas.h | 50 +++++ include/linux/firmware/qcom/qcom_pas.h | 43 ++++ 5 files changed, 400 insertions(+) create mode 100644 drivers/firmware/qcom/qcom_pas.c create mode 100644 drivers/firmware/qcom/qcom_pas.h create mode 100644 include/linux/firmware/qcom/qcom_pas.h diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index b477d54b495a..9f66cc774508 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -6,6 +6,14 @@ menu "Qualcomm firmware drivers" +config QCOM_PAS + tristate "Qualcomm generic PAS interface driver" + help + Enable the generic Peripheral Authentication Service (PAS) provided + by the firmware. It acts as the common layer with different TZ + backends plugged in whether it's an SCM implementation or a proper + TEE bus based PAS service implementation. + config QCOM_SCM select QCOM_TZMEM tristate diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile index 0be40a1abc13..dc5ab45f906a 100644 --- a/drivers/firmware/qcom/Makefile +++ b/drivers/firmware/qcom/Makefile @@ -8,3 +8,4 @@ qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o +obj-$(CONFIG_QCOM_PAS) += qcom_pas.o diff --git a/drivers/firmware/qcom/qcom_pas.c b/drivers/firmware/qcom/qcom_pas.c new file mode 100644 index 000000000000..24485dd0fa10 --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "qcom_pas.h" + +static struct qcom_pas_ops *ops_ptr; + +/** + * devm_qcom_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 + * + * Return: The new PAS context, or ERR_PTR() on failure. + */ +struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size) +{ + struct qcom_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_pas_context_alloc); + +/** + * qcom_pas_init_image() - Initialize peripheral authentication service state + * machine for a given peripheral, using the metadata + * @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 + * @size: size of the metadata + * @ctx: optional pas context + * + * Return: 0 on success. + * + * Upon successful return, the PAS metadata context (@ctx) will be used to + * track the metadata allocation, this needs to be released by invoking + * qcom_pas_metadata_release() by the caller. + */ +int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_pas_context *ctx) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->init_image(ops_ptr->dev, pas_id, metadata, size, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_init_image); + +/** + * qcom_pas_metadata_release() - release metadata context + * @ctx: pas context + */ +void qcom_pas_metadata_release(struct qcom_pas_context *ctx) +{ + if (!ops_ptr || !ctx || !ctx->ptr) + return; + + ops_ptr->metadata_release(ops_ptr->dev, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_metadata_release); + +/** + * qcom_pas_mem_setup() - Prepare the memory related to a given peripheral + * for firmware loading + * @pas_id: peripheral authentication service id + * @addr: start address of memory area to prepare + * @size: size of the memory area to prepare + * + * Return: 0 on success. + */ +int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size); +} +EXPORT_SYMBOL_GPL(qcom_pas_mem_setup); + +/** + * qcom_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 PAS service. + * + * 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 + * @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_pas_get_rsc_table(struct qcom_pas_context *ctx, + void *input_rt, + size_t input_rt_size, + size_t *output_rt_size) +{ + if (!ops_ptr) + return ERR_PTR(-ENODEV); + if (!ctx) + return ERR_PTR(-EINVAL); + + return ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt, + input_rt_size, output_rt_size); +} +EXPORT_SYMBOL_GPL(qcom_pas_get_rsc_table); + +/** + * qcom_pas_auth_and_reset() - Authenticate the given peripheral firmware + * and reset the remote processor + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_auth_and_reset(u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->auth_and_reset(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_auth_and_reset); + +/** + * qcom_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the + * remote processor + * + * @ctx: Context saved during call to devm_qcom_pas_context_alloc() + * + * 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_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx) +{ + if (!ops_ptr) + return -ENODEV; + if (!ctx) + return -EINVAL; + + return ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx); +} +EXPORT_SYMBOL_GPL(qcom_pas_prepare_and_auth_reset); + +/** + * qcom_pas_set_remote_state() - Set the remote processor state + * @state: peripheral state + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_set_remote_state(u32 state, u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_set_remote_state); + +/** + * qcom_pas_shutdown() - Shut down the remote processor + * @pas_id: peripheral authentication service id + * + * Return: 0 on success. + */ +int qcom_pas_shutdown(u32 pas_id) +{ + if (!ops_ptr) + return -ENODEV; + + return ops_ptr->shutdown(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_shutdown); + +/** + * qcom_pas_supported() - Check if the peripheral authentication service is + * supported for the given peripheral + * @pas_id: peripheral authentication service id + * + * Return: true if PAS is supported for this peripheral, otherwise false. + */ +bool qcom_pas_supported(u32 pas_id) +{ + if (!ops_ptr) + return false; + + return ops_ptr->supported(ops_ptr->dev, pas_id); +} +EXPORT_SYMBOL_GPL(qcom_pas_supported); + +/** + * qcom_pas_is_available() - Check if the peripheral authentication service is + * available. Note that it is mandatory for any PAS + * client to invoke this API. If it returns true then + * only any other PAS API can be invoked. + * + * Return: true if PAS is available, otherwise false. + */ +bool qcom_pas_is_available(void) +{ + /* + * The barrier for ops_ptr is intended to synchronize the data stores + * for the ops data structure when client drivers are in parallel + * checking for PAS service availability. + * + * Once the PAS backend becomes available, it is allowed for multiple + * threads to enter TZ for parallel bringup of co-processors during + * boot. + */ + return !!smp_load_acquire(&ops_ptr); +} +EXPORT_SYMBOL_GPL(qcom_pas_is_available); + +void qcom_pas_ops_register(struct qcom_pas_ops *ops) +{ + if (!qcom_pas_is_available()) + /* Paired with smp_load_acquire() in qcom_pas_is_available() */ + smp_store_release(&ops_ptr, ops); + else + pr_err("qcom_pas: ops already registered by %s\n", + ops_ptr->drv_name); +} +EXPORT_SYMBOL_GPL(qcom_pas_ops_register); + +void qcom_pas_ops_unregister(void) +{ + /* Paired with smp_load_acquire() in qcom_pas_is_available() */ + smp_store_release(&ops_ptr, NULL); +} +EXPORT_SYMBOL_GPL(qcom_pas_ops_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm generic TZ PAS driver"); diff --git a/drivers/firmware/qcom/qcom_pas.h b/drivers/firmware/qcom/qcom_pas.h new file mode 100644 index 000000000000..8643e2760602 --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_PAS_INT_H +#define __QCOM_PAS_INT_H + +struct device; + +/** + * struct qcom_pas_ops - Qcom Peripheral Authentication Service (PAS) ops + * @drv_name: PAS driver name. + * @dev: PAS device pointer. + * @supported: Peripheral supported callback. + * @init_image: Peripheral image initialization callback. + * @mem_setup: Peripheral memory setup callback. + * @get_rsc_table: Peripheral get resource table callback. + * @prepare_and_auth_reset: Peripheral prepare firmware authentication and + * reset callback. + * @auth_and_reset: Peripheral firmware authentication and reset + * callback. + * @set_remote_state: Peripheral set remote state callback. + * @shutdown: Peripheral shutdown callback. + * @metadata_release: Image metadata release callback. + */ +struct qcom_pas_ops { + const char *drv_name; + struct device *dev; + bool (*supported)(struct device *dev, u32 pas_id); + int (*init_image)(struct device *dev, u32 pas_id, const void *metadata, + size_t size, struct qcom_pas_context *ctx); + int (*mem_setup)(struct device *dev, u32 pas_id, phys_addr_t addr, + phys_addr_t size); + void *(*get_rsc_table)(struct device *dev, struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size); + int (*prepare_and_auth_reset)(struct device *dev, + struct qcom_pas_context *ctx); + int (*auth_and_reset)(struct device *dev, u32 pas_id); + int (*set_remote_state)(struct device *dev, u32 state, u32 pas_id); + int (*shutdown)(struct device *dev, u32 pas_id); + void (*metadata_release)(struct device *dev, + struct qcom_pas_context *ctx); +}; + +void qcom_pas_ops_register(struct qcom_pas_ops *ops); +void qcom_pas_ops_unregister(void); + +#endif /* __QCOM_PAS_INT_H */ diff --git a/include/linux/firmware/qcom/qcom_pas.h b/include/linux/firmware/qcom/qcom_pas.h new file mode 100644 index 000000000000..65b1c9564458 --- /dev/null +++ b/include/linux/firmware/qcom/qcom_pas.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2010-2015, 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (C) 2015 Linaro Ltd. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_PAS_H +#define __QCOM_PAS_H + +#include +#include + +struct qcom_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; + bool use_tzmem; +}; + +bool qcom_pas_is_available(void); +struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev, + u32 pas_id, + phys_addr_t mem_phys, + size_t mem_size); +int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_pas_context *ctx); +struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size); +int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size); +int qcom_pas_auth_and_reset(u32 pas_id); +int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx); +int qcom_pas_set_remote_state(u32 state, u32 pas_id); +int qcom_pas_shutdown(u32 pas_id); +bool qcom_pas_supported(u32 pas_id); +void qcom_pas_metadata_release(struct qcom_pas_context *ctx); + +#endif /* __QCOM_PAS_H */ From 5c1a2975d23c51c01aca51945d0f10a4ee4c9020 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 2 Jul 2026 17:28:18 +0530 Subject: [PATCH 043/157] firmware: qcom_scm: Migrate to generic PAS service With the availability of generic PAS service, let's add SCM calls as a backend to keep supporting legacy QTEE interfaces. The exported qcom_scm* wrappers will get dropped once all the client drivers get migrated as part of future patches. Tested-by: Mukesh Ojha # Lemans Reviewed-by: Harshal Dev Tested-by: Vignesh Viswanathan # IPQ9650 Reviewed-by: Konrad Dybcio Signed-off-by: Sumit Garg Link: https://lore.kernel.org/r/20260702115835.167602-3-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/Kconfig | 4 +- drivers/firmware/qcom/qcom_scm.c | 335 ++++++++++++++----------------- 2 files changed, 157 insertions(+), 182 deletions(-) diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index 9f66cc774508..a5b3344289f7 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -15,8 +15,10 @@ config QCOM_PAS TEE bus based PAS service implementation. config QCOM_SCM + tristate "Qualcomm PAS SCM interface driver" + select QCOM_PAS select QCOM_TZMEM - tristate + default y if ARCH_QCOM config QCOM_TZMEM tristate diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 6b601a4b89db..7933e55803dc 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include +#include "qcom_pas.h" #include "qcom_scm.h" #include "qcom_tzmem.h" @@ -479,25 +481,6 @@ void qcom_scm_cpu_power_down(u32 flags) } EXPORT_SYMBOL_GPL(qcom_scm_cpu_power_down); -int qcom_scm_set_remote_state(u32 state, u32 id) -{ - struct qcom_scm_desc desc = { - .svc = QCOM_SCM_SVC_BOOT, - .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, - .arginfo = QCOM_SCM_ARGS(2), - .args[0] = state, - .args[1] = id, - .owner = ARM_SMCCC_OWNER_SIP, - }; - struct qcom_scm_res res; - int ret; - - ret = qcom_scm_call(__scm->dev, &desc, &res); - - return ret ? : res.result[0]; -} -EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); - static int qcom_scm_disable_sdi(void) { int ret; @@ -570,26 +553,12 @@ 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; + struct qcom_pas_context *ctx; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -600,11 +569,12 @@ struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, ctx->mem_phys = mem_phys; ctx->mem_size = mem_size; - return ctx; + return (struct qcom_scm_pas_context *)ctx; } EXPORT_SYMBOL_GPL(devm_qcom_scm_pas_context_alloc); -static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, +static int __qcom_scm_pas_init_image(struct device *dev, u32 pas_id, + dma_addr_t mdata_phys, struct qcom_scm_res *res) { struct qcom_scm_desc desc = { @@ -626,7 +596,7 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, desc.args[1] = mdata_phys; - ret = qcom_scm_call(__scm->dev, &desc, res); + ret = qcom_scm_call(dev, &desc, res); qcom_scm_bw_disable(); disable_clk: @@ -635,7 +605,8 @@ static int __qcom_scm_pas_init_image(u32 pas_id, dma_addr_t mdata_phys, return ret; } -static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, +static int qcom_scm_pas_prep_and_init_image(struct device *dev, + struct qcom_pas_context *ctx, const void *metadata, size_t size) { struct qcom_scm_res res; @@ -650,7 +621,7 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, memcpy(mdata_buf, metadata, size); mdata_phys = qcom_tzmem_to_phys(mdata_buf); - ret = __qcom_scm_pas_init_image(ctx->pas_id, mdata_phys, &res); + ret = __qcom_scm_pas_init_image(dev, ctx->pas_id, mdata_phys, &res); if (ret < 0) qcom_tzmem_free(mdata_buf); else @@ -659,25 +630,9 @@ static int qcom_scm_pas_prep_and_init_image(struct qcom_scm_pas_context *ctx, return ret ? : res.result[0]; } -/** - * qcom_scm_pas_init_image() - Initialize peripheral authentication service - * state machine for a given peripheral, using the - * metadata - * @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 - * @size: size of the metadata - * @ctx: optional pas context - * - * Return: 0 on success. - * - * Upon successful return, the PAS metadata context (@ctx) will be used to - * 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 pas_id, const void *metadata, size_t size, - struct qcom_scm_pas_context *ctx) +static int __qcom_scm_pas_init_image2(struct device *dev, u32 pas_id, + const void *metadata, size_t size, + struct qcom_pas_context *ctx) { struct qcom_scm_res res; dma_addr_t mdata_phys; @@ -685,7 +640,7 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, int ret; if (ctx && ctx->use_tzmem) - return qcom_scm_pas_prep_and_init_image(ctx, metadata, size); + return qcom_scm_pas_prep_and_init_image(dev, ctx, metadata, size); /* * During the scm call memory protection will be enabled for the meta @@ -699,16 +654,15 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, * If we pass a buffer that is already part of an SHM Bridge to this * call, it will fail. */ - mdata_buf = dma_alloc_coherent(__scm->dev, size, &mdata_phys, - GFP_KERNEL); + mdata_buf = dma_alloc_coherent(dev, size, &mdata_phys, GFP_KERNEL); if (!mdata_buf) return -ENOMEM; memcpy(mdata_buf, metadata, size); - ret = __qcom_scm_pas_init_image(pas_id, mdata_phys, &res); + ret = __qcom_scm_pas_init_image(dev, pas_id, mdata_phys, &res); if (ret < 0 || !ctx) { - dma_free_coherent(__scm->dev, size, mdata_buf, mdata_phys); + dma_free_coherent(dev, size, mdata_buf, mdata_phys); } else if (ctx) { ctx->ptr = mdata_buf; ctx->phys = mdata_phys; @@ -717,36 +671,35 @@ int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, return ret ? : res.result[0]; } + +int qcom_scm_pas_init_image(u32 pas_id, const void *metadata, size_t size, + struct qcom_scm_pas_context *ctx) +{ + return __qcom_scm_pas_init_image2(__scm->dev, pas_id, metadata, size, + (struct qcom_pas_context *)ctx); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_init_image); -/** - * qcom_scm_pas_metadata_release() - release metadata context - * @ctx: pas context - */ -void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) +static void __qcom_scm_pas_metadata_release(struct device *dev, + struct qcom_pas_context *ctx) { - if (!ctx->ptr) - return; - if (ctx->use_tzmem) qcom_tzmem_free(ctx->ptr); else - dma_free_coherent(__scm->dev, ctx->size, ctx->ptr, ctx->phys); + dma_free_coherent(dev, ctx->size, ctx->ptr, ctx->phys); ctx->ptr = NULL; } + +void qcom_scm_pas_metadata_release(struct qcom_scm_pas_context *ctx) +{ + __qcom_scm_pas_metadata_release(__scm->dev, + (struct qcom_pas_context *)ctx); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_metadata_release); -/** - * qcom_scm_pas_mem_setup() - Prepare the memory related to a given peripheral - * for firmware loading - * @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 pas_id, phys_addr_t addr, phys_addr_t size) +static int __qcom_scm_pas_mem_setup(struct device *dev, u32 pas_id, + phys_addr_t addr, phys_addr_t size) { int ret; struct qcom_scm_desc desc = { @@ -768,7 +721,7 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -776,9 +729,15 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) return ret ? : res.result[0]; } + +int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size) +{ + return __qcom_scm_pas_mem_setup(__scm->dev, pas_id, addr, size); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup); -static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, +static void *__qcom_scm_pas_get_rsc_table(struct device *dev, u32 pas_id, + void *input_rt_tzm, size_t input_rt_size, size_t *output_rt_size) { @@ -813,7 +772,7 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm, * 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); + ret = qcom_scm_call(dev, &desc, &res); if (!ret && res.result[2] > SZ_1G) { ret = -E2BIG; goto free_output_rt; @@ -830,51 +789,11 @@ static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_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) +static void *__qcom_scm_pas_get_rsc_table2(struct device *dev, + struct qcom_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; @@ -909,11 +828,12 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c memcpy(input_rt_tzm, input_rt, input_rt_size); - output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm, + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, 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, + output_rt_tzm = __qcom_scm_pas_get_rsc_table(dev, ctx->pas_id, input_rt_tzm, input_rt_size, &size); @@ -943,16 +863,20 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c return ret ? ERR_PTR(ret) : tbl_ptr; } + +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) +{ + return __qcom_scm_pas_get_rsc_table2(__scm->dev, + (struct qcom_pas_context *)ctx, + input_rt, input_rt_size, + output_rt_size); +} 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 - * @pas_id: peripheral authentication service id - * - * Return 0 on success. - */ -int qcom_scm_pas_auth_and_reset(u32 pas_id) +static int __qcom_scm_pas_auth_and_reset(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -972,7 +896,7 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -980,28 +904,15 @@ int qcom_scm_pas_auth_and_reset(u32 pas_id) return ret ? : res.result[0]; } + +int qcom_scm_pas_auth_and_reset(u32 pas_id) +{ + return __qcom_scm_pas_auth_and_reset(__scm->dev, pas_id); +} 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) +static int __qcom_scm_pas_prepare_and_auth_reset(struct device *dev, + struct qcom_pas_context *ctx) { u64 handle; int ret; @@ -1012,7 +923,7 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) * 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); + return __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id); /* * When Linux runs @ EL2 Linux must create the shmbridge itself and then @@ -1022,20 +933,45 @@ int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) if (ret) return ret; - ret = qcom_scm_pas_auth_and_reset(ctx->pas_id); + ret = __qcom_scm_pas_auth_and_reset(dev, ctx->pas_id); qcom_tzmem_shm_bridge_delete(handle); return ret; } + +int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx) +{ + return __qcom_scm_pas_prepare_and_auth_reset(__scm->dev, + (struct qcom_pas_context *)ctx); +} 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 - * - * Returns 0 on success. - */ -int qcom_scm_pas_shutdown(u32 pas_id) +static int __qcom_scm_pas_set_remote_state(struct device *dev, u32 state, + u32 pas_id) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_BOOT, + .cmd = QCOM_SCM_BOOT_SET_REMOTE_STATE, + .arginfo = QCOM_SCM_ARGS(2), + .args[0] = state, + .args[1] = pas_id, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + int ret; + + ret = qcom_scm_call(dev, &desc, &res); + + return ret ? : res.result[0]; +} + +int qcom_scm_set_remote_state(u32 state, u32 id) +{ + return __qcom_scm_pas_set_remote_state(__scm->dev, state, id); +} +EXPORT_SYMBOL_GPL(qcom_scm_set_remote_state); + +static int __qcom_scm_pas_shutdown(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -1055,7 +991,7 @@ int qcom_scm_pas_shutdown(u32 pas_id) if (ret) goto disable_clk; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); qcom_scm_bw_disable(); disable_clk: @@ -1063,16 +999,14 @@ int qcom_scm_pas_shutdown(u32 pas_id) return ret ? : res.result[0]; } + +int qcom_scm_pas_shutdown(u32 pas_id) +{ + return __qcom_scm_pas_shutdown(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_shutdown); -/** - * qcom_scm_pas_supported() - Check if the peripheral authentication service is - * available for the given peripherial - * @pas_id: peripheral authentication service id - * - * Returns true if PAS is supported for this peripheral, otherwise false. - */ -bool qcom_scm_pas_supported(u32 pas_id) +static bool __qcom_scm_pas_supported(struct device *dev, u32 pas_id) { int ret; struct qcom_scm_desc desc = { @@ -1084,16 +1018,49 @@ bool qcom_scm_pas_supported(u32 pas_id) }; struct qcom_scm_res res; - if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + if (!__qcom_scm_is_call_available(dev, QCOM_SCM_SVC_PIL, QCOM_SCM_PIL_PAS_IS_SUPPORTED)) return false; - ret = qcom_scm_call(__scm->dev, &desc, &res); + ret = qcom_scm_call(dev, &desc, &res); return ret ? false : !!res.result[0]; } + +bool qcom_scm_pas_supported(u32 pas_id) +{ + return __qcom_scm_pas_supported(__scm->dev, pas_id); +} EXPORT_SYMBOL_GPL(qcom_scm_pas_supported); +static struct qcom_pas_ops qcom_pas_ops_scm = { + .drv_name = "qcom_scm", + .supported = __qcom_scm_pas_supported, + .init_image = __qcom_scm_pas_init_image2, + .mem_setup = __qcom_scm_pas_mem_setup, + .get_rsc_table = __qcom_scm_pas_get_rsc_table2, + .auth_and_reset = __qcom_scm_pas_auth_and_reset, + .prepare_and_auth_reset = __qcom_scm_pas_prepare_and_auth_reset, + .set_remote_state = __qcom_scm_pas_set_remote_state, + .shutdown = __qcom_scm_pas_shutdown, + .metadata_release = __qcom_scm_pas_metadata_release, +}; + +/** + * qcom_scm_is_pas_available() - Check if the peripheral authentication service + * is available via SCM or not + * + * Returns true if PAS is available, otherwise false. + */ +static bool qcom_scm_is_pas_available(void) +{ + if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL, + QCOM_SCM_PIL_PAS_AUTH_AND_RESET)) + return false; + + return true; +} + static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset) { struct qcom_scm_desc desc = { @@ -2837,6 +2804,11 @@ static int qcom_scm_probe(struct platform_device *pdev) __get_convention(); + if (qcom_scm_is_pas_available()) { + qcom_pas_ops_scm.dev = scm->dev; + qcom_pas_ops_register(&qcom_pas_ops_scm); + } + /* * If "download mode" is requested, from this point on warmboot * will cause the boot stages to enter download mode, unless @@ -2876,6 +2848,7 @@ static void qcom_scm_shutdown(struct platform_device *pdev) { /* Clean shutdown, disable download mode to allow normal restart */ qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP); + qcom_pas_ops_unregister(); } static const struct of_device_id qcom_scm_dt_match[] = { From b6f7978da0c4d26fe465aa6634f5a0b48f900de0 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 2 Jul 2026 17:28:19 +0530 Subject: [PATCH 044/157] firmware: qcom: Add a PAS TEE service Add support for Peripheral Authentication Service (PAS) driver based on TEE bus with OP-TEE providing the backend PAS service implementation. The TEE PAS service ABI is designed to be extensible with additional API as PTA_QCOM_PAS_CAPABILITIES. This allows to accommodate any future extensions of the PAS service needed while still maintaining backwards compatibility. Reviewed-by: Mukesh Ojha Tested-by: Mukesh Ojha # Lemans Reviewed-by: Harshal Dev Tested-by: Vignesh Viswanathan # IPQ9650 Signed-off-by: Sumit Garg Link: https://lore.kernel.org/r/20260702115835.167602-4-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/Kconfig | 10 + drivers/firmware/qcom/Makefile | 1 + drivers/firmware/qcom/qcom_pas_tee.c | 479 +++++++++++++++++++++++++++ 3 files changed, 490 insertions(+) create mode 100644 drivers/firmware/qcom/qcom_pas_tee.c diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig index a5b3344289f7..c7f8413ab996 100644 --- a/drivers/firmware/qcom/Kconfig +++ b/drivers/firmware/qcom/Kconfig @@ -14,6 +14,16 @@ config QCOM_PAS backends plugged in whether it's an SCM implementation or a proper TEE bus based PAS service implementation. +config QCOM_PAS_TEE + tristate "Qualcomm PAS TEE interface driver" + select QCOM_PAS + depends on TEE + depends on !CPU_BIG_ENDIAN + default m if ARCH_QCOM + help + Enable the generic Peripheral Authentication Service (PAS) provided + by the firmware TEE implementation as the backend. + config QCOM_SCM tristate "Qualcomm PAS SCM interface driver" select QCOM_PAS diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile index dc5ab45f906a..48801d18f37b 100644 --- a/drivers/firmware/qcom/Makefile +++ b/drivers/firmware/qcom/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o obj-$(CONFIG_QCOM_PAS) += qcom_pas.o +obj-$(CONFIG_QCOM_PAS_TEE) += qcom_pas_tee.o diff --git a/drivers/firmware/qcom/qcom_pas_tee.c b/drivers/firmware/qcom/qcom_pas_tee.c new file mode 100644 index 000000000000..ac33a00687aa --- /dev/null +++ b/drivers/firmware/qcom/qcom_pas_tee.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcom_pas.h" + +/* + * Peripheral Authentication Service (PAS) supported. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define TA_QCOM_PAS_IS_SUPPORTED 1 + +/* + * PAS capabilities. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [out] params[1].value.a: PAS capability flags + */ +#define TA_QCOM_PAS_CAPABILITIES 2 + +/* + * PAS image initialization. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[1].memref: Loadable firmware metadata + */ +#define TA_QCOM_PAS_INIT_IMAGE 3 + +/* + * PAS memory setup. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Relocatable firmware size + * [in] params[1].value.a: 32bit LSB relocatable firmware memory address + * [in] params[1].value.b: 32bit MSB relocatable firmware memory address + */ +#define TA_QCOM_PAS_MEM_SETUP 4 + +/* + * PAS get resource table. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [inout] params[1].memref: Resource table config + */ +#define TA_QCOM_PAS_GET_RESOURCE_TABLE 5 + +/* + * PAS image authentication and co-processor reset. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Firmware size + * [in] params[1].value.a: 32bit LSB firmware memory address + * [in] params[1].value.b: 32bit MSB firmware memory address + * [in] params[2].memref: Optional fw memory space shared/lent + */ +#define TA_QCOM_PAS_AUTH_AND_RESET 6 + +/* + * PAS co-processor set suspend/resume state. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Co-processor state identifier + */ +#define TA_QCOM_PAS_SET_REMOTE_STATE 7 + +/* + * PAS co-processor shutdown. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + */ +#define TA_QCOM_PAS_SHUTDOWN 8 + +#define TEE_NUM_PARAMS 4 + +/** + * struct qcom_pas_tee_private - PAS service private data + * @dev: PAS service device. + * @ctx: TEE context handler. + * @session_id: PAS TA session identifier. + */ +struct qcom_pas_tee_private { + struct device *dev; + struct tee_context *ctx; + u32 session_id; +}; + +static bool qcom_pas_tee_supported(struct device *dev, u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_IS_SUPPORTED, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS not supported, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return false; + } + + return true; +} + +static int qcom_pas_tee_init_image(struct device *dev, u32 pas_id, + const void *metadata, size_t size, + struct qcom_pas_context *ctx) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_INIT_IMAGE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + } + }; + struct tee_shm *mdata_shm; + u8 *mdata_buf = NULL; + int ret; + + mdata_shm = tee_shm_alloc_kernel_buf(data->ctx, size); + if (IS_ERR(mdata_shm)) { + dev_err(dev, "mdata_shm allocation failed\n"); + return PTR_ERR(mdata_shm); + } + + mdata_buf = tee_shm_get_va(mdata_shm, 0); + if (IS_ERR(mdata_buf)) { + dev_err(dev, "mdata_buf get VA failed\n"); + tee_shm_free(mdata_shm); + return PTR_ERR(mdata_buf); + } + memcpy(mdata_buf, metadata, size); + + param[1].u.memref.shm = mdata_shm; + param[1].u.memref.size = size; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS init image failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + tee_shm_free(mdata_shm); + return ret ?: -EINVAL; + } + + if (ctx) + ctx->ptr = (void *)mdata_shm; + else + tee_shm_free(mdata_shm); + + return ret; +} + +static int qcom_pas_tee_mem_setup(struct device *dev, u32 pas_id, + phys_addr_t addr, phys_addr_t size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_MEM_SETUP, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = size, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = lower_32_bits(addr), + .u.value.b = upper_32_bits(addr), + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS mem setup failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +DEFINE_FREE(shm_free, struct tee_shm *, tee_shm_free(_T)) + +static void *qcom_pas_tee_get_rsc_table(struct device *dev, + struct qcom_pas_context *ctx, + void *input_rt, size_t input_rt_size, + size_t *output_rt_size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_GET_RESOURCE_TABLE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = ctx->pas_id, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT, + .u.memref.size = input_rt_size, + } + }; + void *rt_buf = NULL; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n", + ctx->pas_id, ret, inv_arg.ret); + return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL); + } + + if (param[1].u.memref.size >= input_rt_size) { + struct tee_shm *rt_shm __free(shm_free) = + tee_shm_alloc_kernel_buf(data->ctx, + param[1].u.memref.size); + void *rt_shm_va; + + if (IS_ERR_OR_NULL(rt_shm)) { + dev_err(dev, "rt_shm allocation failed\n"); + rt_shm = NULL; + return ERR_PTR(-ENOMEM); + } + + rt_shm_va = tee_shm_get_va(rt_shm, 0); + if (IS_ERR(rt_shm_va)) { + dev_err(dev, "rt_shm get VA failed\n"); + return ERR_CAST(rt_shm_va); + } + memcpy(rt_shm_va, input_rt, input_rt_size); + + param[1].u.memref.shm = rt_shm; + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS get RT failed, pas_id: %d, ret: %d, err: 0x%x\n", + ctx->pas_id, ret, inv_arg.ret); + return ret ? ERR_PTR(ret) : ERR_PTR(-EINVAL); + } + + if (param[1].u.memref.size) { + *output_rt_size = param[1].u.memref.size; + rt_buf = kmemdup(rt_shm_va, *output_rt_size, GFP_KERNEL); + if (!rt_buf) + return ERR_PTR(-ENOMEM); + } + } else { + *output_rt_size = 0; + } + + return rt_buf; +} + +static int __qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id, + phys_addr_t mem_phys, size_t mem_size) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_AUTH_AND_RESET, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = mem_size, + }, + [1] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = lower_32_bits(mem_phys), + .u.value.b = upper_32_bits(mem_phys), + }, + /* Reserved for fw memory space to be shared or lent */ + [2] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT, + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS auth reset failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static int qcom_pas_tee_auth_and_reset(struct device *dev, u32 pas_id) +{ + return __qcom_pas_tee_auth_and_reset(dev, pas_id, 0, 0); +} + +static int qcom_pas_tee_prepare_and_auth_reset(struct device *dev, + struct qcom_pas_context *ctx) +{ + return __qcom_pas_tee_auth_and_reset(dev, ctx->pas_id, ctx->mem_phys, + ctx->mem_size); +} + +static int qcom_pas_tee_set_remote_state(struct device *dev, u32 state, + u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_SET_REMOTE_STATE, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id, + .u.value.b = state, + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS set remote state failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static int qcom_pas_tee_shutdown(struct device *dev, u32 pas_id) +{ + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + struct tee_ioctl_invoke_arg inv_arg = { + .func = TA_QCOM_PAS_SHUTDOWN, + .session = data->session_id, + .num_params = TEE_NUM_PARAMS + }; + struct tee_param param[4] = { + [0] = { + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT, + .u.value.a = pas_id + } + }; + int ret; + + ret = tee_client_invoke_func(data->ctx, &inv_arg, param); + if (ret < 0 || inv_arg.ret != 0) { + dev_err(dev, "PAS shutdown failed, pas_id: %d, ret: %d, err: 0x%x\n", + pas_id, ret, inv_arg.ret); + return ret ?: -EINVAL; + } + + return ret; +} + +static void qcom_pas_tee_metadata_release(struct device *dev, + struct qcom_pas_context *ctx) +{ + struct tee_shm *mdata_shm = ctx->ptr; + + tee_shm_free(mdata_shm); + ctx->ptr = NULL; +} + +static struct qcom_pas_ops qcom_pas_ops_tee = { + .drv_name = "qcom-pas-tee", + .supported = qcom_pas_tee_supported, + .init_image = qcom_pas_tee_init_image, + .mem_setup = qcom_pas_tee_mem_setup, + .get_rsc_table = qcom_pas_tee_get_rsc_table, + .auth_and_reset = qcom_pas_tee_auth_and_reset, + .prepare_and_auth_reset = qcom_pas_tee_prepare_and_auth_reset, + .set_remote_state = qcom_pas_tee_set_remote_state, + .shutdown = qcom_pas_tee_shutdown, + .metadata_release = qcom_pas_tee_metadata_release, +}; + +static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) +{ + return ver->impl_id == TEE_IMPL_ID_OPTEE; +} + +static int qcom_pas_tee_probe(struct tee_client_device *pas_dev) +{ + struct device *dev = &pas_dev->dev; + struct qcom_pas_tee_private *data; + struct tee_ioctl_open_session_arg sess_arg = { + .clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL + }; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL); + if (IS_ERR(data->ctx)) + return -ENODEV; + + export_uuid(sess_arg.uuid, &pas_dev->id.uuid); + ret = tee_client_open_session(data->ctx, &sess_arg, NULL); + if (ret < 0 || sess_arg.ret != 0) { + dev_err(dev, "tee_client_open_session failed, ret: %d, err: 0x%x\n", + ret, sess_arg.ret); + tee_client_close_context(data->ctx); + return ret ?: -EINVAL; + } + + data->session_id = sess_arg.session; + dev_set_drvdata(dev, data); + qcom_pas_ops_tee.dev = dev; + qcom_pas_ops_register(&qcom_pas_ops_tee); + + return ret; +} + +static void qcom_pas_tee_remove(struct tee_client_device *pas_dev) +{ + struct device *dev = &pas_dev->dev; + struct qcom_pas_tee_private *data = dev_get_drvdata(dev); + + qcom_pas_ops_unregister(); + tee_client_close_session(data->ctx, data->session_id); + tee_client_close_context(data->ctx); +} + +static const struct tee_client_device_id qcom_pas_tee_id_table[] = { + {UUID_INIT(0xcff7d191, 0x7ca0, 0x4784, + 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe)}, + {} +}; +MODULE_DEVICE_TABLE(tee, qcom_pas_tee_id_table); + +static struct tee_client_driver optee_pas_tee_driver = { + .probe = qcom_pas_tee_probe, + .remove = qcom_pas_tee_remove, + .id_table = qcom_pas_tee_id_table, + .driver = { + .name = "qcom-pas-tee", + }, +}; + +module_tee_client_driver(optee_pas_tee_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Qualcomm PAS TEE driver"); From 6701259025d49139131a0eb2257659a066dcca22 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 2 Jul 2026 17:28:30 +0530 Subject: [PATCH 045/157] MAINTAINERS: Add maintainer entry for Qualcomm PAS TZ service Add Sumit Garg as the maintainer for the Qualcomm generic Peripheral Authentication Service (PAS) as well as the PAS TEE backend driver. Signed-off-by: Sumit Garg Link: https://lore.kernel.org/r/20260702115835.167602-15-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..7847b2a98f90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22315,6 +22315,15 @@ F: Documentation/devicetree/bindings/media/*qcom* F: drivers/media/platform/qcom F: include/dt-bindings/media/*qcom* +QUALCOMM PAS TZ SERVICE +M: Sumit Garg +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: drivers/firmware/qcom/qcom_pas.c +F: drivers/firmware/qcom/qcom_pas.h +F: drivers/firmware/qcom/qcom_pas_tee.c +F: include/linux/firmware/qcom/qcom_pas.h + QUALCOMM SMB CHARGER DRIVER M: Casey Connolly L: linux-arm-msm@vger.kernel.org From f2866e6a27f7cc26111ce195ddf00d4050bb27e5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Jul 2026 15:25:01 +0200 Subject: [PATCH 046/157] soc: qcom: Hide all drivers behind selectable menu Switch from a simple menu to menuconfig, so all Qualcomm SoC drivers will be under one selectable option, allowing to disable them all which should make kernel configuration easier when preparing a non-Qualcomm kernel. This has few benefits (functional impact of this commit): 1. Allow compile testing of QCOM_OCMEM, which previously required ARCH_QCOM. 2. Hide behind ARCH_QCOM or COMPILE_TEST drivers specific to Qualcomm which should not be available to other kernel builds: QCOM_PMIC_PDCHARGER_ULOG, QCOM_PMIC_GLINK, QCOM_SPM and QCOM_PBS. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260711-qcom-soc-kconfig-v2-1-4a907e064281@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 82 ++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index f6970431d07d..f3d9c9d776e5 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -2,11 +2,21 @@ # # QCOM Soc drivers # -menu "Qualcomm SoC drivers" +menuconfig QCOM_SOC + bool "Qualcomm SoC drivers" + depends on ARCH_QCOM || COMPILE_TEST + default ARCH_QCOM + help + This collection of drivers is specific to Qualcomm System-on-Chips + and most of them are necessary for a fully functional boot of the + Linux kernel (plus a few debugging drivers). + Drivers can be skipped when building Linux kernel not intended to run + said processors. + +if QCOM_SOC config QCOM_AOSS_QMP tristate "Qualcomm AOSS Driver" - depends on ARCH_QCOM || COMPILE_TEST depends on MAILBOX depends on COMMON_CLK && PM select PM_GENERIC_DOMAINS @@ -18,7 +28,6 @@ config QCOM_AOSS_QMP config QCOM_COMMAND_DB tristate "Qualcomm Command DB" - depends on ARCH_QCOM || COMPILE_TEST depends on OF_RESERVED_MEM help Command DB queries shared memory by key string for shared system @@ -28,7 +37,6 @@ config QCOM_COMMAND_DB config QCOM_GENI_SE tristate "Qualcomm GENI Serial Engine Driver" - depends on ARCH_QCOM || COMPILE_TEST help This driver is used to manage Generic Interface (GENI) firmware based Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This @@ -37,7 +45,6 @@ config QCOM_GENI_SE config QCOM_GSBI tristate "Qualcomm General Serial Bus Interface" - depends on ARCH_QCOM || COMPILE_TEST select MFD_SYSCON help Say y here to enable GSBI support. The GSBI provides control @@ -46,7 +53,6 @@ config QCOM_GSBI config QCOM_LLCC tristate "Qualcomm LLCC driver" - depends on ARCH_QCOM || COMPILE_TEST select REGMAP_MMIO help Qualcomm Technologies, Inc. platform specific @@ -54,17 +60,8 @@ config QCOM_LLCC SDM845. This provides interfaces to clients that use the LLCC. Say yes here to enable LLCC slice driver. -config QCOM_KRYO_L2_ACCESSORS - bool - depends on (ARCH_QCOM || COMPILE_TEST) && ARM64 - -config QCOM_MDT_LOADER - tristate - select QCOM_SCM - config QCOM_OCMEM tristate "Qualcomm On Chip Memory (OCMEM) driver" - depends on ARCH_QCOM select QCOM_SCM help The On Chip Memory (OCMEM) allocator allows various clients to @@ -77,7 +74,7 @@ config QCOM_PD_MAPPER select QCOM_QMI_HELPERS select QCOM_PDR_MSG select AUXILIARY_BUS - depends on NET && QRTR && (ARCH_QCOM || COMPILE_TEST) + depends on NET && QRTR default QCOM_RPROC_COMMON help The Protection Domain Mapper maps registered services to the domains @@ -85,12 +82,6 @@ config QCOM_PD_MAPPER implementation of the service. It is a simpler alternative to the userspace daemon. -config QCOM_PDR_HELPERS - tristate - select QCOM_QMI_HELPERS - select QCOM_PDR_MSG - depends on NET - config QCOM_PDR_MSG tristate @@ -124,13 +115,8 @@ config QCOM_PMIC_GLINK Say yes here to support USB-C and battery status on modern Qualcomm platforms. -config QCOM_QMI_HELPERS - tristate - depends on NET - config QCOM_RAMP_CTRL tristate "Qualcomm Ramp Controller driver" - depends on ARCH_QCOM || COMPILE_TEST help The Ramp Controller is used to program the sequence ID for pulse swallowing, enable sequence and link sequence IDs for the CPU @@ -139,7 +125,6 @@ config QCOM_RAMP_CTRL config QCOM_RMTFS_MEM tristate "Qualcomm Remote Filesystem memory driver" - depends on ARCH_QCOM || COMPILE_TEST select QCOM_SCM help The Qualcomm remote filesystem memory driver is used for allocating @@ -151,7 +136,6 @@ config QCOM_RMTFS_MEM config QCOM_RPM_MASTER_STATS tristate "Qualcomm RPM Master stats" - depends on ARCH_QCOM || COMPILE_TEST help The RPM Master sleep stats driver provides detailed per-subsystem sleep/wake data, read from the RPM message RAM. It can be used to @@ -162,7 +146,6 @@ config QCOM_RPM_MASTER_STATS config QCOM_RPMH tristate "Qualcomm RPM-Hardened (RPMH) Communication" - depends on ARCH_QCOM || COMPILE_TEST depends on (QCOM_COMMAND_DB || !QCOM_COMMAND_DB) help Support for communication with the hardened-RPM blocks in @@ -173,7 +156,6 @@ config QCOM_RPMH config QCOM_SMEM tristate "Qualcomm Shared Memory Manager (SMEM)" - depends on ARCH_QCOM || COMPILE_TEST depends on HWSPINLOCK help Say y here to enable support for the Qualcomm Shared Memory Manager. @@ -182,7 +164,6 @@ config QCOM_SMEM config QCOM_SMD_RPM tristate "Qualcomm Resource Power Manager (RPM) over SMD" - depends on ARCH_QCOM || COMPILE_TEST depends on RPMSG depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n help @@ -229,7 +210,6 @@ config QCOM_SOCINFO config QCOM_SPM tristate "Qualcomm Subsystem Power Manager (SPM)" - depends on ARCH_QCOM || COMPILE_TEST select QCOM_SCM help Enable the support for the Qualcomm Subsystem Power Manager, used @@ -238,7 +218,7 @@ config QCOM_SPM config QCOM_STATS tristate "Qualcomm Sleep stats driver" - depends on (ARCH_QCOM && DEBUG_FS) || COMPILE_TEST + depends on DEBUG_FS || COMPILE_TEST depends on QCOM_SMEM depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n help @@ -249,7 +229,6 @@ config QCOM_STATS config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" - depends on ARCH_QCOM || COMPILE_TEST depends on RPMSG help Client driver for the WCNSS_CTRL SMD channel, used to download nv @@ -257,7 +236,6 @@ config QCOM_WCNSS_CTRL config QCOM_APR tristate "Qualcomm APR/GPR Bus (Asynchronous/Generic Packet Router)" - depends on ARCH_QCOM || COMPILE_TEST depends on RPMSG depends on NET select QCOM_PDR_HELPERS @@ -269,7 +247,6 @@ config QCOM_APR config QCOM_ICC_BWMON tristate "Qualcomm Interconnect Bandwidth Monitor driver" - depends on ARCH_QCOM || COMPILE_TEST select PM_OPP select REGMAP_MMIO help @@ -282,10 +259,6 @@ config QCOM_ICC_BWMON the fixed bandwidth votes from cpufreq (CPU nodes) thus achieve high memory throughput even with lower CPU frequencies. -config QCOM_INLINE_CRYPTO_ENGINE - tristate - select QCOM_SCM - config QCOM_PBS tristate "PBS trigger support for Qualcomm PMICs" depends on SPMI @@ -295,7 +268,32 @@ config QCOM_PBS This module provides the APIs to the client drivers that wants to send the PBS trigger event to the PBS RAM. -endmenu +endif + +# Options selected by other drivers from different subsystems must be outside +# of the menuconfig if-block: + +config QCOM_INLINE_CRYPTO_ENGINE + tristate + select QCOM_SCM + +config QCOM_KRYO_L2_ACCESSORS + bool + depends on ARM64 + +config QCOM_MDT_LOADER + tristate + select QCOM_SCM + +config QCOM_PDR_HELPERS + tristate + select QCOM_QMI_HELPERS + select QCOM_PDR_MSG + depends on NET + +config QCOM_QMI_HELPERS + tristate + depends on NET config QCOM_UBWC_CONFIG tristate From e10b198a94c66f7105dbd535f66631c96579c903 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Jul 2026 15:25:02 +0200 Subject: [PATCH 047/157] soc: qcom: Restrict drivers per ARM/ARM64 There is no point to allow selecting core SoC drivers for Qualcomm ARMv7 SoCs when building ARM64 kernel, and vice versa. This makes kernel configuration more difficult as many do not remember the Qualcomm SoCs model names/numbers and their properties like architecture. No features should be lost because: 1. There won't be a single image for ARMv7 and ARMv8/9 SoCs. 2. Newer ARMv8/9 SoCs won't be running in arm32 emulation mode. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260711-qcom-soc-kconfig-v2-2-4a907e064281@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index f3d9c9d776e5..7207814cac85 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -37,6 +37,7 @@ config QCOM_COMMAND_DB config QCOM_GENI_SE tristate "Qualcomm GENI Serial Engine Driver" + depends on ARM64 || COMPILE_TEST help This driver is used to manage Generic Interface (GENI) firmware based Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This @@ -45,6 +46,7 @@ config QCOM_GENI_SE config QCOM_GSBI tristate "Qualcomm General Serial Bus Interface" + depends on ARM || COMPILE_TEST select MFD_SYSCON help Say y here to enable GSBI support. The GSBI provides control @@ -104,6 +106,7 @@ config QCOM_PMIC_GLINK depends on DRM depends on NET depends on OF + depends on ARM64 || COMPILE_TEST select AUXILIARY_BUS select QCOM_PDR_HELPERS select DRM_AUX_HPD_BRIDGE @@ -247,6 +250,7 @@ config QCOM_APR config QCOM_ICC_BWMON tristate "Qualcomm Interconnect Bandwidth Monitor driver" + depends on ARM64 || COMPILE_TEST select PM_OPP select REGMAP_MMIO help From 9b3daac1d0eed2564e49a08636e5044c0bfac0b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Jul 2026 15:25:03 +0200 Subject: [PATCH 048/157] soc: qcom: Make important drivers default The drivers for Qualcomm SoC components are covering a basic or fundamental SoC blocks. Usually they are required for booting or to achieve basic expected functionality when running Linux. These drivers do not represent any sort of buses visible to the board designers/configurators, thus they should be always enabled, regardless how SoC is used in the final board. Kernel configuration should not ask users choice of drivers when that choice is obvious and known to the developers that answer should be 'yes' or 'module'. Switch most of the Qualcomm SoC drivers to a default 'yes' or 'module' for ARCH_QCOM, to match existing defconfig usage. This has no impact on arm64 defconfig, arm qcom_defconfig and arm multi_v7_defconfig. multi: +#define CONFIG_QCOM_PDR_HELPERS_MODULE 1 +#define CONFIG_QCOM_PBS_MODULE 1 +#define CONFIG_QCOM_AOSS_QMP 1 +#define CONFIG_QCOM_APR_MODULE 1 +#define CONFIG_QCOM_LLCC_MODULE 1 qcom: +#define CONFIG_QCOM_PDR_HELPERS_MODULE 1 +#define CONFIG_QCOM_PBS_MODULE 1 +#define CONFIG_QCOM_AOSS_QMP 1 +#define CONFIG_QCOM_APR_MODULE 1 +#define CONFIG_QCOM_SPM 1 +#define CONFIG_QCOM_LLCC_MODULE 1 The change will however enable by default all drivers for arm or arm64 COMPILE_TEST builds, whenever ARCH_QCOM is selected, which feels logical: if one selects ARCH_QCOM then probably by default wants to build test it entirely. Kernels with COMPILE_TEST are not supposed to be used for booting. Reviewed-by: Konrad Dybcio Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260711-qcom-soc-kconfig-v2-3-4a907e064281@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 7207814cac85..4e6eac2769e5 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -20,6 +20,7 @@ config QCOM_AOSS_QMP depends on MAILBOX depends on COMMON_CLK && PM select PM_GENERIC_DOMAINS + default ARCH_QCOM help This driver provides the means of communicating with and controlling the low-power state for resources related to the remoteproc @@ -29,6 +30,7 @@ config QCOM_AOSS_QMP config QCOM_COMMAND_DB tristate "Qualcomm Command DB" depends on OF_RESERVED_MEM + default ARCH_QCOM help Command DB queries shared memory by key string for shared system resources. Platform drivers that require to set state of a shared @@ -38,6 +40,7 @@ config QCOM_COMMAND_DB config QCOM_GENI_SE tristate "Qualcomm GENI Serial Engine Driver" depends on ARM64 || COMPILE_TEST + default ARCH_QCOM help This driver is used to manage Generic Interface (GENI) firmware based Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This @@ -47,6 +50,7 @@ config QCOM_GENI_SE config QCOM_GSBI tristate "Qualcomm General Serial Bus Interface" depends on ARM || COMPILE_TEST + default ARCH_QCOM select MFD_SYSCON help Say y here to enable GSBI support. The GSBI provides control @@ -56,6 +60,7 @@ config QCOM_GSBI config QCOM_LLCC tristate "Qualcomm LLCC driver" select REGMAP_MMIO + default m if ARCH_QCOM help Qualcomm Technologies, Inc. platform specific Last Level Cache Controller(LLCC) driver for platforms such as, @@ -65,6 +70,7 @@ config QCOM_LLCC config QCOM_OCMEM tristate "Qualcomm On Chip Memory (OCMEM) driver" select QCOM_SCM + default m if ARCH_QCOM help The On Chip Memory (OCMEM) allocator allows various clients to allocate memory from OCMEM based on performance, latency and power @@ -110,6 +116,7 @@ config QCOM_PMIC_GLINK select AUXILIARY_BUS select QCOM_PDR_HELPERS select DRM_AUX_HPD_BRIDGE + default m if ARCH_QCOM help The Qualcomm PMIC GLINK driver provides access, over GLINK, to the USB and battery firmware running on one of the coprocessors in @@ -129,6 +136,7 @@ config QCOM_RAMP_CTRL config QCOM_RMTFS_MEM tristate "Qualcomm Remote Filesystem memory driver" select QCOM_SCM + default m if ARCH_QCOM help The Qualcomm remote filesystem memory driver is used for allocating and exposing regions of shared memory with remote processors for the @@ -150,6 +158,7 @@ config QCOM_RPM_MASTER_STATS config QCOM_RPMH tristate "Qualcomm RPM-Hardened (RPMH) Communication" depends on (QCOM_COMMAND_DB || !QCOM_COMMAND_DB) + default ARCH_QCOM help Support for communication with the hardened-RPM blocks in Qualcomm Technologies Inc (QTI) SoCs. RPMH communication uses an @@ -160,6 +169,7 @@ config QCOM_RPMH config QCOM_SMEM tristate "Qualcomm Shared Memory Manager (SMEM)" depends on HWSPINLOCK + default ARCH_QCOM help Say y here to enable support for the Qualcomm Shared Memory Manager. The driver provides an interface to items in a heap shared among all @@ -169,6 +179,7 @@ config QCOM_SMD_RPM tristate "Qualcomm Resource Power Manager (RPM) over SMD" depends on RPMSG depends on RPMSG_QCOM_SMD || RPMSG_QCOM_SMD=n + default ARCH_QCOM help If you say yes to this option, support will be included for the Resource Power Manager system found in the Qualcomm 8974 based @@ -189,6 +200,7 @@ config QCOM_SMP2P depends on QCOM_SMEM select QCOM_SMEM_STATE select IRQ_DOMAIN + default ARCH_QCOM help Say yes here to support the Qualcomm Shared Memory Point to Point protocol. @@ -199,6 +211,7 @@ config QCOM_SMSM depends on QCOM_SMEM select QCOM_SMEM_STATE select IRQ_DOMAIN + default ARCH_QCOM help Say yes here to support the Qualcomm Shared Memory State Machine. The state machine is represented by bits in shared memory. @@ -207,6 +220,7 @@ config QCOM_SOCINFO tristate "Qualcomm socinfo driver" depends on QCOM_SMEM select SOC_BUS + default m if ARCH_QCOM help Say yes here to support the Qualcomm socinfo driver, providing information about the SoC to user space. @@ -214,6 +228,7 @@ config QCOM_SOCINFO config QCOM_SPM tristate "Qualcomm Subsystem Power Manager (SPM)" select QCOM_SCM + default ARCH_QCOM if ARM help Enable the support for the Qualcomm Subsystem Power Manager, used to manage cores, L2 low power modes and to configure the internal @@ -224,6 +239,7 @@ config QCOM_STATS depends on DEBUG_FS || COMPILE_TEST depends on QCOM_SMEM depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n + default m if ARCH_QCOM help Qualcomm Technologies, Inc. (QTI) Sleep stats driver to read the shared memory exported by the remote processor related to @@ -233,6 +249,7 @@ config QCOM_STATS config QCOM_WCNSS_CTRL tristate "Qualcomm WCNSS control driver" depends on RPMSG + default m if ARCH_QCOM help Client driver for the WCNSS_CTRL SMD channel, used to download nv firmware to a newly booted WCNSS chip. @@ -242,6 +259,7 @@ config QCOM_APR depends on RPMSG depends on NET select QCOM_PDR_HELPERS + default m if ARCH_QCOM help Enable APR IPC protocol support between application processor and QDSP6. APR is @@ -253,6 +271,7 @@ config QCOM_ICC_BWMON depends on ARM64 || COMPILE_TEST select PM_OPP select REGMAP_MMIO + default m if ARCH_QCOM help Sets up driver monitoring bandwidth on various interconnects and based on that voting for interconnect bandwidth, adjusting their @@ -266,6 +285,7 @@ config QCOM_ICC_BWMON config QCOM_PBS tristate "PBS trigger support for Qualcomm PMICs" depends on SPMI + default m if ARCH_QCOM help This driver supports configuring software programmable boot sequencer (PBS) trigger event through PBS RAM on Qualcomm Technologies, Inc. PMICs. From f6540105e0aaf6c1d3fec6070217f199bdc351f7 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Jul 2026 15:25:04 +0200 Subject: [PATCH 049/157] soc: qcom: Avoid SCM and SPM for cpuidle drivers QCOM_SCM and QCOM_SPM are user-selectable drivers, thus ARM_QCOM_SPM_CPUIDLE should rather avoid selecting them but instead depend to avoid any possible unmet dependencies. ARM_QCOM_SPM_CPUIDLE does use symbols from SCM and SPM, and since it cannot be built-in, the dependency must be as built-in. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260711-qcom-soc-kconfig-v2-4-4a907e064281@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/cpuidle/Kconfig.arm | 4 ++-- drivers/soc/qcom/Kconfig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index a1ee475d180d..b88b01aa5829 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -120,11 +120,11 @@ config ARM_QCOM_SPM_CPUIDLE bool "CPU Idle Driver for Qualcomm Subsystem Power Manager (SPM)" depends on (ARCH_QCOM || COMPILE_TEST) && !ARM64 && MMU depends on ARCH_SUSPEND_POSSIBLE + depends on QCOM_SCM=y + depends on QCOM_SPM=y select ARM_CPU_SUSPEND select CPU_IDLE_MULTIPLE_DRIVERS select DT_IDLE_STATES - select QCOM_SCM - select QCOM_SPM help Select this to enable cpuidle for Qualcomm processors. The Subsystem Power Manager (SPM) controls low power modes for the diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 4e6eac2769e5..fd4d4ecd2df0 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -227,7 +227,7 @@ config QCOM_SOCINFO config QCOM_SPM tristate "Qualcomm Subsystem Power Manager (SPM)" - select QCOM_SCM + depends on QCOM_SCM default ARCH_QCOM if ARM help Enable the support for the Qualcomm Subsystem Power Manager, used From f6bb2daa4584229af155c2488b83151999315293 Mon Sep 17 00:00:00 2001 From: Yuvaraj Ranganathan Date: Fri, 22 May 2026 12:08:59 +0530 Subject: [PATCH 050/157] firmware: qcom: scm: add trace events for the SMC call interface The SCM SMC call path is opaque at runtime. Stalls caused by firmware congestion, QCOM_SCM_WAITQ_SLEEP/RESUME cycles, and EBUSY retry loops are invisible without recompiling the kernel with temporary printk statements or attaching a hardware debugger. Add five TRACE_EVENTs covering the complete lifecycle of an SCM call: scm_smc_request Emit before each arm_smccc_smc_quirk() invocation. Records the SMC function ID, decoded service and command identifiers, argument count, and up to six register arguments in hex and decimal. Because the caller loops on QCOM_SCM_INTERRUPTED, this event fires once per physical SMC instruction including inte scm_smc_done Emit after the outer __scm_smc_do() returns, pairing each request with its final outcome. Records the SMC function ID, the kernel error code returned to the caller, and the four firmware result registers a0-a3. scm_waitq_sleep Emit when the firmware returns QCOM_SCM_WAITQ_SLEEP. Records the wait-queue context and the SMC call context handles required to issue the matching WAITQ_RESUME. scm_waitq_resume Emit just before constructing and sending the WAITQ_RESUME follow-up call. Records the SMC call context handle being resumed. scm_waitq_get_wq_ctx Emit after a successful WAITQ_GET_WQ_CTX fast-call. Records the returned wait-queue context, flags, and more_pending indicator. These events let ftrace and perf reconstruct the full sequence of firmware interactions, measure per-call and end-to-end latency, and attribute waitqueue stalls to specific service/command pairs without modifying driver source. Reviewed-by: Konrad Dybcio Signed-off-by: Yuvaraj Ranganathan Link: https://lore.kernel.org/r/20260522-scm-tracepoints-v2-1-e27cdbe0c585@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm_trace.h | 143 +++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 drivers/firmware/qcom/qcom_scm_trace.h diff --git a/drivers/firmware/qcom/qcom_scm_trace.h b/drivers/firmware/qcom/qcom_scm_trace.h new file mode 100644 index 000000000000..6c911124fc56 --- /dev/null +++ b/drivers/firmware/qcom/qcom_scm_trace.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qcom_scm + +#if !defined(_TRACE_SCM_SMC_INTERFACE_H) || defined(TRACE_HEADER_MULTI_READ) + +#define _TRACE_SCM_SMC_INTERFACE_H + +#include + +TRACE_EVENT(scm_smc_request, + + TP_PROTO(unsigned long a0, const struct arm_smccc_args *smc), + + TP_ARGS(a0, smc), + + TP_STRUCT__entry( + __field(u64, smc_id) + __field(u8, svc_id) + __field(u8, cmd_id) + __field(u8, args_cnt) + __dynamic_array(unsigned long, args, + min_t(u8, (smc->args[1] & 0xF), (u8)6)) + ), + + TP_fast_assign( + __entry->smc_id = a0; + __entry->svc_id = (smc->args[0] >> 8) & 0xFF; + __entry->cmd_id = smc->args[0] & 0xFF; + u8 n = min_t(u8, (smc->args[1] & 0xF), (u8)6); + + __entry->args_cnt = n; + + unsigned long *dst = __get_dynamic_array(args); + + for (int i = 0; i < n; i++) + dst[i] = smc->args[2 + i]; + ), + + TP_printk("smc_id:0x%08llx svc_id:0x%02x cmd_id:0x%02x args_cnt:%u args:%s", + __entry->smc_id, __entry->svc_id, __entry->cmd_id, __entry->args_cnt, + __print_dynamic_array(args, sizeof(unsigned long))) +); + +TRACE_EVENT(scm_waitq_sleep, + + TP_PROTO(u32 wq_ctx, u32 smc_ctx), + + TP_ARGS(wq_ctx, smc_ctx), + + TP_STRUCT__entry( + __field(u32, wq_ctx) + __field(u32, smc_call_ctx) + ), + + TP_fast_assign( + __entry->wq_ctx = wq_ctx; + __entry->smc_call_ctx = smc_ctx; + ), + + TP_printk("wq_ctx:%u, smc_call_ctx:%u", __entry->wq_ctx, __entry->smc_call_ctx) +); + +TRACE_EVENT(scm_waitq_resume, + + TP_PROTO(u32 smc_ctx), + + TP_ARGS(smc_ctx), + + TP_STRUCT__entry( + __field(u32, smc_call_ctx) + ), + + TP_fast_assign( + __entry->smc_call_ctx = smc_ctx; + ), + + TP_printk("smc_call_ctx:%u", __entry->smc_call_ctx) +); + +TRACE_EVENT(scm_waitq_get_wq_ctx, + + TP_PROTO(u32 wq_ctx, u32 flags, u32 pending), + + TP_ARGS(wq_ctx, flags, pending), + + TP_STRUCT__entry( + __field(u32, wq_ctx) + __field(u32, flags) + __field(u32, more_pending) + ), + + TP_fast_assign( + __entry->wq_ctx = wq_ctx; + __entry->flags = flags; + __entry->more_pending = pending; + ), + + TP_printk("wq_ctx:%u, flags:%u, more_pending:%u", + __entry->wq_ctx, __entry->flags, __entry->more_pending) +); + +TRACE_EVENT(scm_smc_done, + + TP_PROTO(int ret, u64 smc_id, struct arm_smccc_res *smc_res), + + TP_ARGS(ret, smc_id, smc_res), + + TP_STRUCT__entry( + __field(int, ret) + __field(u64, smc_id) + __field(unsigned long, res) + __field(unsigned long, res0) + __field(unsigned long, res1) + __field(unsigned long, res2) + ), + + TP_fast_assign( + __entry->ret = ret; + __entry->smc_id = smc_id; + __entry->res = smc_res->a0; + __entry->res0 = smc_res->a1; + __entry->res1 = smc_res->a2; + __entry->res2 = smc_res->a3; + ), + + TP_printk("smc_id:0x%08llx, ret:%d res_to_callee:0x%lx res0:0x%lx res1:0x%lx res2:0x%lx", + __entry->smc_id, __entry->ret, __entry->res, + __entry->res0, __entry->res1, __entry->res2) +); + +#endif /* _TRACE_SCM_SMC_INTERFACE_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE qcom_scm_trace + +#include + From 41329e72363c02facfeae063ef304aa7ced68c3b Mon Sep 17 00:00:00 2001 From: Yuvaraj Ranganathan Date: Fri, 22 May 2026 12:09:00 +0530 Subject: [PATCH 051/157] firmware: qcom: scm: instrument SMC call path with tracepoints Wire the five tracepoints defined in qcom_scm_trace.h into the SMC execution path by including the header with CREATE_TRACE_POINTS. Reviewed-by: Konrad Dybcio Signed-off-by: Yuvaraj Ranganathan Link: https://lore.kernel.org/r/20260522-scm-tracepoints-v2-2-e27cdbe0c585@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/Makefile | 1 + drivers/firmware/qcom/qcom_scm-smc.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile index 48801d18f37b..88ce74d74c3e 100644 --- a/drivers/firmware/qcom/Makefile +++ b/drivers/firmware/qcom/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_QCOM_SCM) += qcom-scm.o qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o +CFLAGS_qcom_scm-smc.o := -I$(src) obj-$(CONFIG_QCOM_TZMEM) += qcom_tzmem.o obj-$(CONFIG_QCOM_QSEECOM) += qcom_qseecom.o obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o diff --git a/drivers/firmware/qcom/qcom_scm-smc.c b/drivers/firmware/qcom/qcom_scm-smc.c index 574930729ddd..01999c22659c 100644 --- a/drivers/firmware/qcom/qcom_scm-smc.c +++ b/drivers/firmware/qcom/qcom_scm-smc.c @@ -24,6 +24,9 @@ struct arm_smccc_args { unsigned long args[8]; }; +#define CREATE_TRACE_POINTS +#include "qcom_scm_trace.h" + static DEFINE_MUTEX(qcom_scm_lock); #define QCOM_SCM_EBUSY_WAIT_MS 30 @@ -44,6 +47,7 @@ static void __scm_smc_do_quirk(const struct arm_smccc_args *smc, quirk.state.a6 = 0; do { + trace_scm_smc_request(a0, smc); arm_smccc_smc_quirk(a0, smc->args[1], smc->args[2], smc->args[3], smc->args[4], smc->args[5], quirk.state.a6, smc->args[7], res, &quirk); @@ -83,6 +87,7 @@ int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending) if (ret) return ret; + trace_scm_waitq_get_wq_ctx(get_wq_res.a1, get_wq_res.a2, get_wq_res.a3); *wq_ctx = get_wq_res.a1; *flags = get_wq_res.a2; *more_pending = get_wq_res.a3; @@ -105,10 +110,12 @@ static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_ wq_ctx = res->a1; smc_call_ctx = res->a2; + trace_scm_waitq_sleep(wq_ctx, smc_call_ctx); ret = qcom_scm_wait_for_wq_completion(wq_ctx); if (ret) return ret; + trace_scm_waitq_resume(smc_call_ctx); fill_wq_resume_args(&resume, smc_call_ctx); smc = &resume; } @@ -201,6 +208,9 @@ int __scm_smc_call(struct device *dev, const struct qcom_scm_desc *desc, } ret = __scm_smc_do(dev, &smc, &smc_res, atomic); + + trace_scm_smc_done(ret, smc.args[0], &smc_res); + if (ret) return ret; From cfe9b47ef18bdb737be480f3e539e9561ef74078 Mon Sep 17 00:00:00 2001 From: Konstantin Shabanov Date: Mon, 29 Jun 2026 22:48:12 +0700 Subject: [PATCH 052/157] firmware: qcom: scm: Allow QSEECOM on Honor Magicbook Art 14 Allow particular machine accessing eg. efivars. Signed-off-by: Konstantin Shabanov Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260629154812.9066-5-mail@etehtsea.me Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 7933e55803dc..bfc5e6723eb9 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2262,6 +2262,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "dell,latitude-7455" }, { .compatible = "dell,xps13-9345" }, { .compatible = "ecs,liva-qc710" }, + { .compatible = "honor,magicbook-art-14-snapdragon" }, { .compatible = "hp,elitebook-ultra-g1q" }, { .compatible = "hp,omnibook-x14" }, { .compatible = "huawei,gaokun3" }, From 75e918aa876440d8ad559a11d6ab87bddb1ed79a Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 09:55:01 +0800 Subject: [PATCH 053/157] soc: qcom: rpmh-rsc: manage PM notifiers with devres rpmh_rsc_probe() registers CPU PM or genpd notifiers before populating child devices. If child population fails, the CPU PM notifier path is not unwound and the genpd path needs open-coded cleanup. Use devm_pm_runtime_enable() for the genpd path and devm_add_action_or_reset() for both notifier registrations. This makes probe failure and driver detach use the same cleanup model while keeping devm_of_platform_populate() responsible for child devices. Fixes: 25092e6100ac ("soc: qcom: rpmh-rsc: Attach RSC to cluster PM domain") Signed-off-by: Pengpeng Hou Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260623015501.31129-1-pengpeng@iscas.ac.cn Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index c6f7d5c9c493..66928ca40b9a 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -944,17 +944,30 @@ static int rpmh_rsc_pd_callback(struct notifier_block *nfb, return NOTIFY_OK; } +static void rpmh_rsc_pd_detach(void *data) +{ + dev_pm_genpd_remove_notifier(data); +} + static int rpmh_rsc_pd_attach(struct rsc_drv *drv, struct device *dev) { int ret; - pm_runtime_enable(dev); + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + drv->genpd_nb.notifier_call = rpmh_rsc_pd_callback; ret = dev_pm_genpd_add_notifier(dev, &drv->genpd_nb); if (ret) - pm_runtime_disable(dev); + return ret; - return ret; + return devm_add_action_or_reset(dev, rpmh_rsc_pd_detach, dev); +} + +static void rpmh_rsc_cpu_pm_unregister(void *data) +{ + cpu_pm_unregister_notifier(data); } static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv) @@ -1107,7 +1120,15 @@ static int rpmh_rsc_probe(struct platform_device *pdev) return ret; } else { drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; - cpu_pm_register_notifier(&drv->rsc_pm); + ret = cpu_pm_register_notifier(&drv->rsc_pm); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, + rpmh_rsc_cpu_pm_unregister, + &drv->rsc_pm); + if (ret) + return ret; } } @@ -1122,13 +1143,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, drv); drv->dev = &pdev->dev; - ret = devm_of_platform_populate(&pdev->dev); - if (ret && pdev->dev.pm_domain) { - dev_pm_genpd_remove_notifier(&pdev->dev); - pm_runtime_disable(&pdev->dev); - } - - return ret; + return devm_of_platform_populate(&pdev->dev); } static const struct of_device_id rpmh_drv_match[] = { From d19a46f7ed8eb54fea61e0eaf7db53ff7babb03c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Tue, 23 Jun 2026 09:54:15 +0800 Subject: [PATCH 054/157] bus: qcom-ebi2: use managed resources for clocks and children qcom_ebi2_probe() enables the EBI2 clocks manually and populates child devices manually. Several later failure paths can then return without disabling the clocks or without relying on the driver core to undo child population. Use devm_clk_get_enabled() for both clocks and devm_of_platform_populate() for children. This lets the driver core unwind the resources automatically and removes the hand-written error labels. Fixes: 335a12754808 ("bus: qcom: add EBI2 driver") Signed-off-by: Pengpeng Hou Reviewed-by: Konrad Dybcio Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20260623015415.26975-1-pengpeng@iscas.ac.cn Signed-off-by: Bjorn Andersson --- drivers/bus/qcom-ebi2.c | 50 +++++++++-------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/drivers/bus/qcom-ebi2.c b/drivers/bus/qcom-ebi2.c index ab00c75b9e95..8d2eb955dc92 100644 --- a/drivers/bus/qcom-ebi2.c +++ b/drivers/bus/qcom-ebi2.c @@ -302,41 +302,23 @@ static int qcom_ebi2_probe(struct platform_device *pdev) u32 val; int ret; - ebi2xclk = devm_clk_get(dev, "ebi2x"); + ebi2xclk = devm_clk_get_enabled(dev, "ebi2x"); if (IS_ERR(ebi2xclk)) return PTR_ERR(ebi2xclk); - ret = clk_prepare_enable(ebi2xclk); - if (ret) { - dev_err(dev, "could not enable EBI2X clk (%d)\n", ret); - return ret; - } - - ebi2clk = devm_clk_get(dev, "ebi2"); - if (IS_ERR(ebi2clk)) { - ret = PTR_ERR(ebi2clk); - goto err_disable_2x_clk; - } - - ret = clk_prepare_enable(ebi2clk); - if (ret) { - dev_err(dev, "could not enable EBI2 clk\n"); - goto err_disable_2x_clk; - } + ebi2clk = devm_clk_get_enabled(dev, "ebi2"); + if (IS_ERR(ebi2clk)) + return PTR_ERR(ebi2clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ebi2_base = devm_ioremap_resource(dev, res); - if (IS_ERR(ebi2_base)) { - ret = PTR_ERR(ebi2_base); - goto err_disable_clk; - } + if (IS_ERR(ebi2_base)) + return PTR_ERR(ebi2_base); res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ebi2_xmem = devm_ioremap_resource(dev, res); - if (IS_ERR(ebi2_xmem)) { - ret = PTR_ERR(ebi2_xmem); - goto err_disable_clk; - } + if (IS_ERR(ebi2_xmem)) + return PTR_ERR(ebi2_xmem); /* Allegedly this turns the power save mode off */ writel(0UL, ebi2_xmem + EBI2_XMEM_CFG); @@ -353,7 +335,7 @@ static int qcom_ebi2_probe(struct platform_device *pdev) /* Figure out the chipselect */ ret = of_property_read_u32(child, "reg", &csindex); if (ret) - goto err_disable_clk; + return ret; if (csindex > 5) { dev_err(dev, @@ -372,20 +354,10 @@ static int qcom_ebi2_probe(struct platform_device *pdev) have_children = true; } - if (have_children) { - ret = of_platform_default_populate(np, NULL, dev); - if (ret) - goto err_disable_clk; - } + if (have_children) + return devm_of_platform_populate(dev); return 0; - -err_disable_clk: - clk_disable_unprepare(ebi2clk); -err_disable_2x_clk: - clk_disable_unprepare(ebi2xclk); - - return ret; } static const struct of_device_id qcom_ebi2_of_match[] = { From 9c62bb72a5d02795f767cf9f5e5dd781d8ccf70a Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Tue, 30 Jun 2026 13:47:19 -0700 Subject: [PATCH 055/157] firmware: arm_scmi: Simplify notification allocations Several SCMI notification data structures allocate a descriptor and then allocate a fixed-size array referenced by that descriptor. Use flexible array members for per-protocol registered events and per-event source refcounts so each descriptor and its array can be allocated together. Annotate the flexible arrays with __counted_by() and assign the count fields immediately after allocation, before any array access. The registered_protocols table has a fixed SCMI_MAX_PROTO size, so embed it directly in the notification instance instead of allocating it separately. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260630204719.334211-1-rosenp@gmail.com (sudeep.holla: Updated title and improved change description) Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/notify.c | 43 ++++++++++++------------------ 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 0a192cf2deab..3cf706aacc11 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -209,11 +209,11 @@ struct scmi_registered_events_desc; * @init_work: A work item to perform final initializations of pending handlers * @notify_wq: A reference to the allocated Kernel cmwq * @pending_mtx: A mutex to protect @pending_events_handlers + * @pending_events_handlers: An hashtable containing all pending events' + * handlers descriptors * @registered_protocols: A statically allocated array containing pointers to * all the registered protocol-level specific information * related to events' handling - * @pending_events_handlers: An hashtable containing all pending events' - * handlers descriptors * * Each platform instance, represented by a handle, has its own instance of * the notification subsystem represented by this structure. @@ -225,8 +225,8 @@ struct scmi_notify_instance { struct workqueue_struct *notify_wq; /* lock to protect pending_events_handlers */ struct mutex pending_mtx; - struct scmi_registered_events_desc **registered_protocols; DECLARE_HASHTABLE(pending_events_handlers, SCMI_PENDING_HASH_SZ); + struct scmi_registered_events_desc *registered_protocols[SCMI_MAX_PROTO]; }; /** @@ -276,13 +276,13 @@ struct scmi_registered_event; * @eh_sz: Size of the pre-allocated buffer @eh * @in_flight: A reference to an in flight &struct scmi_registered_event * @num_events: Number of events in @registered_events - * @registered_events: A dynamically allocated array holding all the registered - * events' descriptors, whose fixed-size is determined at - * compile time. * @registered_mtx: A mutex to protect @registered_events_handlers * @ph: SCMI protocol handle reference * @registered_events_handlers: An hashtable containing all events' handlers * descriptors registered for this protocol + * @registered_events: A dynamically allocated array holding all the registered + * events' descriptors, whose fixed-size is determined at + * compile time. * * All protocols that register at least one event have their protocol-specific * information stored here, together with the embedded allocated events_queue. @@ -302,11 +302,11 @@ struct scmi_registered_events_desc { size_t eh_sz; void *in_flight; int num_events; - struct scmi_registered_event **registered_events; /* mutex to protect registered_events_handlers */ struct mutex registered_mtx; const struct scmi_protocol_handle *ph; DECLARE_HASHTABLE(registered_events_handlers, SCMI_REGISTERED_HASH_SZ); + struct scmi_registered_event *registered_events[] __counted_by(num_events); }; /** @@ -338,9 +338,9 @@ struct scmi_registered_event { void *report; u32 num_sources; bool not_supported_by_platform; - refcount_t *sources; /* locking to serialize the access to sources */ struct mutex sources_mtx; + refcount_t sources[] __counted_by(num_sources); }; /** @@ -706,9 +706,13 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni, if (WARN_ON(ni->registered_protocols[proto_id])) return ERR_PTR(-EINVAL); - pd = devm_kzalloc(ni->handle->dev, sizeof(*pd), GFP_KERNEL); + pd = devm_kzalloc(ni->handle->dev, + struct_size(pd, registered_events, num_events), + GFP_KERNEL); if (!pd) return ERR_PTR(-ENOMEM); + + pd->num_events = num_events; pd->id = proto_id; pd->ops = ops; pd->ni = ni; @@ -722,12 +726,6 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni, return ERR_PTR(-ENOMEM); pd->eh_sz = eh_sz; - pd->registered_events = devm_kcalloc(ni->handle->dev, num_events, - sizeof(char *), GFP_KERNEL); - if (!pd->registered_events) - return ERR_PTR(-ENOMEM); - pd->num_events = num_events; - /* Initialize per protocol handlers table */ mutex_init(&pd->registered_mtx); hash_init(pd->registered_events_handlers); @@ -796,18 +794,16 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, int id; struct scmi_registered_event *r_evt; - r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt), + r_evt = devm_kzalloc(ni->handle->dev, + struct_size(r_evt, sources, num_sources), GFP_KERNEL); if (!r_evt) return -ENOMEM; + + r_evt->num_sources = num_sources; r_evt->proto = pd; r_evt->evt = evt; - r_evt->sources = devm_kcalloc(ni->handle->dev, num_sources, - sizeof(refcount_t), GFP_KERNEL); - if (!r_evt->sources) - return -ENOMEM; - r_evt->num_sources = num_sources; mutex_init(&r_evt->sources_mtx); r_evt->report = devm_kzalloc(ni->handle->dev, @@ -1673,11 +1669,6 @@ int scmi_notification_init(struct scmi_handle *handle) ni->gid = gid; ni->handle = handle; - ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO, - sizeof(char *), GFP_KERNEL); - if (!ni->registered_protocols) - goto err; - ni->notify_wq = alloc_workqueue(dev_name(handle->dev), WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS, 0); From 0be72be03ca7de55f22dd4c0622d81ac98aee38a Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 2 Jul 2026 17:28:24 +0530 Subject: [PATCH 056/157] drm/msm: Switch to generic PAS TZ APIs Switch drm/msm client drivers over to generic PAS TZ APIs. Generic PAS TZ service allows to support multiple TZ implementation backends like QTEE based SCM PAS service, OP-TEE based PAS service and any further future TZ backend service. Acked-by: Dmitry Baryshkov Reviewed-by: Mukesh Ojha Tested-by: Mukesh Ojha # Lemans Signed-off-by: Sumit Garg Link: https://lore.kernel.org/r/20260702115835.167602-9-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- drivers/gpu/drm/msm/Kconfig | 1 + drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 4 ++-- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 11 ++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 250246f81ea9..09469d56513b 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -21,6 +21,7 @@ config DRM_MSM select SHMEM select TMPFS select QCOM_SCM + select QCOM_PAS select QCOM_UBWC_CONFIG select WANT_DEV_COREDUMP select SND_SOC_HDMI_CODEC if SND_SOC diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index 2c0bbac43c52..57135acc8e2c 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include #include @@ -653,7 +653,7 @@ static int a5xx_zap_shader_resume(struct msm_gpu *gpu) if (adreno_is_a506(adreno_gpu)) return 0; - ret = qcom_scm_set_remote_state(SCM_GPU_ZAP_SHADER_RESUME, GPU_PAS_ID); + ret = qcom_pas_set_remote_state(SCM_GPU_ZAP_SHADER_RESUME, GPU_PAS_ID); if (ret) DRM_ERROR("%s: zap-shader resume failed: %d\n", gpu->name, ret); diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index c62c45bb0ddb..489462065ea9 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -146,10 +147,10 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname, goto out; /* Send the image to the secure world */ - ret = qcom_scm_pas_auth_and_reset(pasid); + ret = qcom_pas_auth_and_reset(pasid); /* - * If the scm call returns -EOPNOTSUPP we assume that this target + * If the pas call returns -EOPNOTSUPP we assume that this target * doesn't need/support the zap shader so quietly fail */ if (ret == -EOPNOTSUPP) @@ -175,9 +176,9 @@ int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid) if (!zap_available) return -ENODEV; - /* We need SCM to be able to load the firmware */ - if (!qcom_scm_is_available()) { - DRM_DEV_ERROR(&pdev->dev, "SCM is not available\n"); + /* We need PAS to be able to load the firmware */ + if (!qcom_pas_is_available()) { + DRM_DEV_ERROR(&pdev->dev, "PAS is not available\n"); return -EPROBE_DEFER; } From bc3c98b7761f98d2e4aa72259feb2b0902c1e86f Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 25 Jun 2026 00:38:12 +0530 Subject: [PATCH 057/157] dt-bindings: firmware: qcom,scm: Add minidump SRAM property On Qualcomm SoCs that support minidump, a word in always-on SRAM is shared between the Operating System(HLOS) and boot firmware. OS is expected to select the minidump download destination either USB upload to a host PC or save to on-device storage. Boot firmware will reads this word before DDR is initialized on the warm reset following a crash to decide where to deliver the minidump. Add a 'sram' property to the SCM binding to allow the OS to identify this SRAM region via a phandle. Signed-off-by: Mukesh Ojha Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260624190830.3131112-2-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml index f344d1390b1b..24a49613c869 100644 --- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml +++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml @@ -130,6 +130,13 @@ properties: - description: offset of the download mode control register description: TCSR hardware block + sram: + description: + Phandle to a region in always-on SRAM used to store the download + mode value for boot firmware to read before DDR is initialised on + the next warm reset. + maxItems: 1 + allOf: # Clocks - if: From d9603a7790d164657a51997d7265ca845bee78bf Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 25 Jun 2026 00:38:14 +0530 Subject: [PATCH 058/157] firmware: qcom: scm: Fix missing smp_load_acquire() __scm is published in qcom_scm_probe() with smp_store_release() but qcom_scm_set_download_mode() reads it directly without smp_load_acquire(), creating a potential ordering violation where a CPU could observe a stale or partially initialised __scm pointer. Use smp_load_acquire() to acquire __scm in the qcom_scm_set_download_mode() function. Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260624190830.3131112-4-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index bfc5e6723eb9..2abac54ed1f3 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -534,23 +534,23 @@ static int qcom_scm_io_rmw(phys_addr_t addr, unsigned int mask, unsigned int val return qcom_scm_io_writel(addr, new); } -static void qcom_scm_set_download_mode(u32 dload_mode) +static void qcom_scm_set_download_mode(struct qcom_scm *scm, u32 dload_mode) { int ret = 0; - if (__scm->dload_mode_addr) { - ret = qcom_scm_io_rmw(__scm->dload_mode_addr, QCOM_DLOAD_MASK, + if (scm->dload_mode_addr) { + ret = qcom_scm_io_rmw(scm->dload_mode_addr, QCOM_DLOAD_MASK, FIELD_PREP(QCOM_DLOAD_MASK, dload_mode)); - } else if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_BOOT, + } else if (__qcom_scm_is_call_available(scm->dev, QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_SET_DLOAD_MODE)) { - ret = __qcom_scm_set_dload_mode(__scm->dev, !!dload_mode); + ret = __qcom_scm_set_dload_mode(scm->dev, !!dload_mode); } else if (dload_mode) { - dev_err(__scm->dev, + dev_err(scm->dev, "No available mechanism for setting download mode\n"); } if (ret) - dev_err(__scm->dev, "failed to set download mode: %d\n", ret); + dev_err(scm->dev, "failed to set download mode: %d\n", ret); } struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, @@ -2673,6 +2673,7 @@ static int get_download_mode(char *buffer, const struct kernel_param *kp) static int set_download_mode(const char *val, const struct kernel_param *kp) { + struct qcom_scm *scm; bool tmp; int ret; @@ -2688,8 +2689,10 @@ static int set_download_mode(const char *val, const struct kernel_param *kp) } download_mode = ret; - if (__scm) - qcom_scm_set_download_mode(download_mode); + /* Pairs with smp_store_release() in qcom_scm_probe(). */ + scm = smp_load_acquire(&__scm); + if (scm) + qcom_scm_set_download_mode(scm, download_mode); return 0; } @@ -2815,7 +2818,7 @@ static int qcom_scm_probe(struct platform_device *pdev) * will cause the boot stages to enter download mode, unless * disabled below by a clean shutdown/reboot. */ - qcom_scm_set_download_mode(download_mode); + qcom_scm_set_download_mode(scm, download_mode); /* * Disable SDI if indicated by DT that it is enabled by default. @@ -2848,7 +2851,7 @@ static int qcom_scm_probe(struct platform_device *pdev) static void qcom_scm_shutdown(struct platform_device *pdev) { /* Clean shutdown, disable download mode to allow normal restart */ - qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP); + qcom_scm_set_download_mode(__scm, QCOM_DLOAD_NODUMP); qcom_pas_ops_unregister(); } From d0a05db99bd0d76aafc20245922eec28c939a192 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 25 Jun 2026 00:38:15 +0530 Subject: [PATCH 059/157] firmware: qcom: scm: use dev_err_probe() for dload address failure Replace the bare `return ret` after qcom_scm_find_dload_address() with dev_err_probe() to produce a consistent, deferred-probe-aware error message when the download-mode address cannot be resolved. Reviewed-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260624190830.3131112-5-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 2abac54ed1f3..4a0f05c8031d 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2719,7 +2719,8 @@ static int qcom_scm_probe(struct platform_device *pdev) scm->dev = &pdev->dev; ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr); if (ret < 0) - return ret; + return dev_err_probe(&pdev->dev, ret, + "Failed to get download mode address\n"); mutex_init(&scm->scm_bw_lock); From 7bd52a0d456eef0243f508fc712e59c612ee7d45 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 25 Jun 2026 00:38:16 +0530 Subject: [PATCH 060/157] firmware: qcom: scm: Add minidump SRAM support On most Qualcomm SoCs where minidump is supported, a word in always-on SRAM is shared between the operating system (OS) and boot firmware. Before DDR is initialized on the warm reset following a crash, firmware reads this word to decide if minidump is enabled and collect a minidump, and where to deliver it (USB upload to a host, or save to local storage). The OS is expected to select one of the destinations. The SRAM region is described by a 'sram' phandle on the SCM DT node. If the property is absent the feature is silently disabled, keeping existing SoCs unaffected. Expose a 'minidump_dest' module parameter (default: usb) so the user can select the destination. Only the string names "usb" or "storage" are accepted. Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260624190830.3131112-6-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 92 ++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 4a0f05c8031d..f35f2ee39130 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -59,6 +59,7 @@ struct qcom_scm { int scm_vote_count; u64 dload_mode_addr; + void __iomem *minidump_sram; struct qcom_tzmem_pool *mempool; unsigned int wq_cnt; @@ -143,6 +144,20 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = { #define QCOM_DLOAD_MINIDUMP 2 #define QCOM_DLOAD_BOTHDUMP 3 +/* Minidump destination values written to always-on SRAM for boot firmware */ +#define QCOM_MINIDUMP_DEST_USB 0x0 +#define QCOM_MINIDUMP_DEST_STORAGE 0x2 + +static u32 minidump_dest = QCOM_MINIDUMP_DEST_USB; + +static const struct { + const char *name; + u32 val; +} minidump_dest_map[] = { + { "usb", QCOM_MINIDUMP_DEST_USB }, + { "storage", QCOM_MINIDUMP_DEST_STORAGE }, +}; + #define QCOM_SCM_DEFAULT_WAITQ_COUNT 1 static const char * const qcom_scm_convention_names[] = { @@ -551,6 +566,14 @@ static void qcom_scm_set_download_mode(struct qcom_scm *scm, u32 dload_mode) if (ret) dev_err(scm->dev, "failed to set download mode: %d\n", ret); + + /* + * Write the destination into the always-on SRAM so boot firmware + * can read it before DDR is initialised on the next warm reset. + * Only written when minidump is active; + */ + if (scm->minidump_sram && (dload_mode & QCOM_DLOAD_MINIDUMP)) + writel_relaxed(minidump_dest, scm->minidump_sram); } struct qcom_scm_pas_context *devm_qcom_scm_pas_context_alloc(struct device *dev, @@ -2007,6 +2030,29 @@ int qcom_scm_gpu_init_regs(u32 gpu_req) } EXPORT_SYMBOL_GPL(qcom_scm_gpu_init_regs); +static int qcom_scm_map_minidump_sram(struct device *dev, void __iomem **out) +{ + struct device_node *np = dev->of_node; + struct device_node *sram_np; + struct resource res; + int ret; + + sram_np = of_parse_phandle(np, "sram", 0); + if (!sram_np) + return 0; + + ret = of_address_to_resource(sram_np, 0, &res); + of_node_put(sram_np); + if (ret) + return ret; + + *out = devm_ioremap(dev, res.start, resource_size(&res)); + if (!*out) + return -ENOMEM; + + return 0; +} + static int qcom_scm_find_dload_address(struct device *dev, u64 *addr) { struct device_node *tcsr; @@ -2705,6 +2751,47 @@ static const struct kernel_param_ops download_mode_param_ops = { module_param_cb(download_mode, &download_mode_param_ops, NULL, 0644); MODULE_PARM_DESC(download_mode, "download mode: off/0/N for no dump mode, full/on/1/Y for full dump mode, mini for minidump mode and full,mini for both full and minidump mode together are acceptable values"); +static int get_minidump_dest(char *buffer, const struct kernel_param *kp) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(minidump_dest_map); i++) + if (minidump_dest == minidump_dest_map[i].val) + return sysfs_emit(buffer, "%s\n", minidump_dest_map[i].name); + + return sysfs_emit(buffer, "unknown\n"); +} + +static int set_minidump_dest(const char *val, const struct kernel_param *kp) +{ + struct qcom_scm *scm; + int i; + + for (i = 0; i < ARRAY_SIZE(minidump_dest_map); i++) + if (sysfs_streq(val, minidump_dest_map[i].name)) + break; + + if (i >= ARRAY_SIZE(minidump_dest_map)) + return -EINVAL; + + minidump_dest = minidump_dest_map[i].val; + + /* Pairs with smp_store_release() in qcom_scm_probe(). */ + scm = smp_load_acquire(&__scm); + if (scm && scm->minidump_sram && (download_mode & QCOM_DLOAD_MINIDUMP)) + writel_relaxed(minidump_dest, scm->minidump_sram); + + return 0; +} + +static const struct kernel_param_ops minidump_dest_param_ops = { + .get = get_minidump_dest, + .set = set_minidump_dest, +}; + +module_param_cb(minidump_dest, &minidump_dest_param_ops, NULL, 0644); +MODULE_PARM_DESC(minidump_dest, "Minidump SRAM destination: usb (default) or storage"); + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_tzmem_pool_config pool_config; @@ -2722,6 +2809,11 @@ static int qcom_scm_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, ret, "Failed to get download mode address\n"); + ret = qcom_scm_map_minidump_sram(&pdev->dev, &scm->minidump_sram); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to map minidump SRAM\n"); + mutex_init(&scm->scm_bw_lock); scm->path = devm_of_icc_get(&pdev->dev, NULL); From a49c2acde67e73b409947ba36d0eaa0a50fdeb48 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sun, 5 Jul 2026 10:16:30 +0200 Subject: [PATCH 061/157] dt-bindings: soc: apple: Add Apple PMGR misc controls Certain Apple SoCs include additional PMGR power states that are controlled via a different "misc" control block. On existing SoCs, this includes the fabric and memory controller state. Reviewed-by: Conor Dooley Signed-off-by: Sasha Finkelstein Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260705-pmgr-misc-v3-1-51b75fed6f73@chaosmail.tech Signed-off-by: Sven Peter --- .../soc/apple/apple,t6000-pmgr-misc.yaml | 45 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/apple/apple,t6000-pmgr-misc.yaml diff --git a/Documentation/devicetree/bindings/soc/apple/apple,t6000-pmgr-misc.yaml b/Documentation/devicetree/bindings/soc/apple/apple,t6000-pmgr-misc.yaml new file mode 100644 index 000000000000..588100222f89 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/apple/apple,t6000-pmgr-misc.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/apple/apple,t6000-pmgr-misc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple SoC PMGR Misc Power States + +maintainers: + - Sasha Finkelstein + +description: | + Certain Apple SoCs include additional PMGR power states that are controlled + via a different "misc" control block. This includes the fabric and memory + controller states. + +properties: + compatible: + enum: + - apple,t6000-pmgr-misc + - apple,t6020-pmgr-misc + + reg: + maxItems: 2 + + reg-names: + items: + - const: fabric-ps + - const: dcs-ps + +required: + - compatible + - reg + - reg-names + +additionalProperties: false + +examples: + - | + power-management@8e20c000 { + compatible = "apple,t6000-pmgr-misc"; + reg = <0x8e20c000 0x400>, + <0x8e20c800 0x400>; + reg-names = "fabric-ps", "dcs-ps"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..5a29bb86499f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2607,6 +2607,7 @@ F: Documentation/devicetree/bindings/power/apple* F: Documentation/devicetree/bindings/power/reset/apple,smc-reboot.yaml F: Documentation/devicetree/bindings/pwm/apple,s5l-fpwm.yaml F: Documentation/devicetree/bindings/rtc/apple,smc-rtc.yaml +F: Documentation/devicetree/bindings/soc/apple/apple,t6000-pmgr-misc.yaml F: Documentation/devicetree/bindings/spi/apple,spi.yaml F: Documentation/devicetree/bindings/spmi/apple,spmi.yaml F: Documentation/devicetree/bindings/usb/apple,dwc3.yaml From fef9350c314bf089a11a62858261e6576ada867b Mon Sep 17 00:00:00 2001 From: Hector Martin Date: Sun, 5 Jul 2026 10:16:31 +0200 Subject: [PATCH 062/157] soc: apple: Add driver for Apple PMGR misc controls Apple SoCs have PMGR blocks that control a bunch of power-related features. Besides the existing device power state controls (which are very uniform and handled by apple-pmgr-pwrstate), we also need to manage more random registers such as SoC-wide fabric and memory controller power states, which have a different interface. Add a driver for these kitchen sink controls. Right now it implements fabric and memory controller power state switching on system standby/s2idle, which saves about 1W of power or so on t60xx platforms. Signed-off-by: Hector Martin Reviewed-by: Sven Peter Co-developed-by: Sasha Finkelstein Signed-off-by: Sasha Finkelstein Reviewed-by: Joshua Peisach Link: https://patch.msgid.link/20260705-pmgr-misc-v3-2-51b75fed6f73@chaosmail.tech Signed-off-by: Sven Peter --- drivers/soc/apple/Kconfig | 10 ++ drivers/soc/apple/Makefile | 2 + drivers/soc/apple/apple-pmgr-misc.c | 181 ++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 drivers/soc/apple/apple-pmgr-misc.c diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index d0ff32182a2b..db30c0ade948 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -16,6 +16,16 @@ config APPLE_MAILBOX Say Y here if you have an Apple SoC. +config APPLE_PMGR_MISC + tristate "Apple SoC PMGR miscellaneous support" + depends on PM + help + The PMGR block in Apple SoCs provides high-level power state + controls for SoC devices. This driver manages miscellaneous + power controls. + + Say 'y' here if you have a Pro/Max/Ultra Apple SoC. + config APPLE_RTKIT tristate "Apple RTKit co-processor IPC protocol" depends on APPLE_MAILBOX diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 0b85ab61aefe..0481f7f43b06 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -3,6 +3,8 @@ obj-$(CONFIG_APPLE_MAILBOX) += apple-mailbox.o apple-mailbox-y = mailbox.o +obj-$(CONFIG_APPLE_PMGR_MISC) += apple-pmgr-misc.o + obj-$(CONFIG_APPLE_RTKIT) += apple-rtkit.o apple-rtkit-y = rtkit.o rtkit-crashlog.o diff --git a/drivers/soc/apple/apple-pmgr-misc.c b/drivers/soc/apple/apple-pmgr-misc.c new file mode 100644 index 000000000000..7db29f7de329 --- /dev/null +++ b/drivers/soc/apple/apple-pmgr-misc.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SoC PMGR device power state driver + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_CLKGEN_PSTATE 0 +#define APPLE_CLKGEN_PSTATE_DESIRED GENMASK(3, 0) + +#define DCS_DEV_PSTATE_MIN_T600X 7 +#define SYS_DEV_PSTATE_SUSPEND 1 + +enum sys_device { + DEV_FABRIC, + DEV_DCS, + DEV_MAX, +}; + +struct apple_pmgr_sys_device { + void __iomem *base; + u32 active_state; + u32 suspend_state; +}; + +struct apple_pmgr_misc_hw { + u32 dev_min_ps[DEV_MAX]; +}; + +struct apple_pmgr_misc { + struct device *dev; + struct apple_pmgr_sys_device devices[DEV_MAX]; +}; + +static void apple_pmgr_sys_dev_set_pstate(struct apple_pmgr_misc *misc, + enum sys_device dev, bool active) +{ + u32 pstate; + u32 val; + + if (!misc->devices[dev].base) + return; + + if (active) + pstate = misc->devices[dev].active_state; + else + pstate = misc->devices[dev].suspend_state; + + dev_dbg(misc->dev, "set %d ps to pstate %d\n", dev, pstate); + + val = readl_relaxed(misc->devices[dev].base + APPLE_CLKGEN_PSTATE); + FIELD_MODIFY(APPLE_CLKGEN_PSTATE_DESIRED, &val, pstate); + writel_relaxed(val, misc->devices[dev].base + APPLE_CLKGEN_PSTATE); +} + +static int __maybe_unused apple_pmgr_misc_suspend_noirq(struct device *dev) +{ + struct apple_pmgr_misc *misc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < DEV_MAX; i++) + apple_pmgr_sys_dev_set_pstate(misc, i, false); + + return 0; +} + +static int __maybe_unused apple_pmgr_misc_resume_noirq(struct device *dev) +{ + struct apple_pmgr_misc *misc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < DEV_MAX; i++) + apple_pmgr_sys_dev_set_pstate(misc, i, true); + + return 0; +} + +static int apple_pmgr_init_device(struct apple_pmgr_misc *misc, + const struct apple_pmgr_misc_hw *hw, + enum sys_device dev, + const char *device_name) +{ + void __iomem *base; + char name[32]; + u32 val; + + snprintf(name, sizeof(name), "%s-ps", device_name); + + base = devm_platform_ioremap_resource_byname( + to_platform_device(misc->dev), name); + if (IS_ERR(base)) + return PTR_ERR(base); + + val = readl_relaxed(base + APPLE_CLKGEN_PSTATE); + + misc->devices[dev].base = base; + misc->devices[dev].active_state = + FIELD_GET(APPLE_CLKGEN_PSTATE_DESIRED, val); + misc->devices[dev].suspend_state = hw->dev_min_ps[dev]; + + return 0; +} + +static int apple_pmgr_misc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct apple_pmgr_misc_hw *hw; + struct apple_pmgr_misc *misc; + int ret; + + misc = devm_kzalloc(dev, sizeof(*misc), GFP_KERNEL); + if (!misc) + return -ENOMEM; + + misc->dev = dev; + hw = of_device_get_match_data(dev); + if (!hw) + return -EINVAL; + + ret = apple_pmgr_init_device(misc, hw, DEV_FABRIC, "fabric"); + if (ret) + return ret; + + ret = apple_pmgr_init_device(misc, hw, DEV_DCS, "dcs"); + if (ret) + return ret; + + platform_set_drvdata(pdev, misc); + + return 0; +} + +static const struct apple_pmgr_misc_hw apple_pmgr_misc_hw_t600x = { + .dev_min_ps = { + [DEV_FABRIC] = SYS_DEV_PSTATE_SUSPEND, + [DEV_DCS] = DCS_DEV_PSTATE_MIN_T600X, + }, +}; + +static const struct apple_pmgr_misc_hw apple_pmgr_misc_hw_t602x = { + .dev_min_ps = { + [DEV_FABRIC] = SYS_DEV_PSTATE_SUSPEND, + [DEV_DCS] = SYS_DEV_PSTATE_SUSPEND, + }, +}; + +static const struct of_device_id apple_pmgr_misc_of_match[] = { + { .compatible = "apple,t6000-pmgr-misc", .data = &apple_pmgr_misc_hw_t600x }, + { .compatible = "apple,t6020-pmgr-misc", .data = &apple_pmgr_misc_hw_t602x }, + {} +}; + +MODULE_DEVICE_TABLE(of, apple_pmgr_misc_of_match); + +static const struct dev_pm_ops apple_pmgr_misc_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(apple_pmgr_misc_suspend_noirq, + apple_pmgr_misc_resume_noirq) +}; + +static struct platform_driver apple_pmgr_misc_driver = { + .probe = apple_pmgr_misc_probe, + .driver = { + .name = "apple-pmgr-misc", + .of_match_table = apple_pmgr_misc_of_match, + .pm = pm_ptr(&apple_pmgr_misc_pm_ops), + }, +}; + +MODULE_AUTHOR("Hector Martin "); +MODULE_DESCRIPTION("PMGR misc driver for Apple SoCs"); +MODULE_LICENSE("GPL"); + +module_platform_driver(apple_pmgr_misc_driver); From 0314900dcdde044af0208fed212035dbfaa55843 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:20 +0100 Subject: [PATCH 063/157] firmware: arm_scmi: Publish channel state before callbacks Transport setup can enable callbacks before the setup routine returns. mailbox_chan_setup() registers the mailbox client with mbox_request_channel(), and the mailbox controller startup path can enable interrupt delivery before SCMI mailbox channel state has been published. Similarly, smc_chan_setup() requests the optional A2P completion IRQ before the SMC transport has made its cinfo pointer visible. If a pending or spurious callback fires in those windows, the transport RX callback can dereference a NULL transport cinfo pointer. Publishing only the transport-private pointer is not sufficient either: an early callback can enter the SCMI core before scmi_chan_setup() has assigned cinfo->handle. The core derives scmi_info from cinfo->handle in the RX path, so a NULL handle can still fault even when the transport-private cinfo is valid. Assign cinfo->handle before invoking the transport setup callback. Publish the mailbox and SMC transport-private channel state before requesting the mailbox channels or IRQ, and clear the early-published pointers again on setup failure. Also unwind mailbox setup devres resources on failure so an optional RX setup error that is ignored by the core does not leave stale transport state behind. Fixes: 5c8a47a5a91d ("firmware: arm_scmi: Make scmi core independent of the transport type") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-1-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 2 +- drivers/firmware/arm_scmi/transports/mailbox.c | 18 +++++++++++++----- drivers/firmware/arm_scmi/transports/smc.c | 15 +++++++++------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 3e0d975ec94c..1d1f5d25d773 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2782,6 +2782,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, cinfo->id = prot_id; cinfo->dev = &tdev->dev; + cinfo->handle = &info->handle; ret = info->desc->ops->chan_setup(cinfo, info->dev, tx); if (ret) { of_node_put(of_node); @@ -2814,7 +2815,6 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, return ret; } - cinfo->handle = &info->handle; return 0; } diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c index ae0f67e6cc45..b6459fbb8151 100644 --- a/drivers/firmware/arm_scmi/transports/mailbox.c +++ b/drivers/firmware/arm_scmi/transports/mailbox.c @@ -211,13 +211,18 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, cl->tx_block = false; cl->knows_txdone = tx; + cinfo->transport_info = smbox; + smbox->cinfo = cinfo; + mutex_init(&smbox->chan_lock); + smbox->chan = mbox_request_channel(cl, tx ? 0 : p2a_chan); if (IS_ERR(smbox->chan)) { ret = PTR_ERR(smbox->chan); + smbox->chan = NULL; if (ret != -EPROBE_DEFER) dev_err(cdev, "failed to request SCMI %s mailbox\n", desc); - return ret; + goto err_clear_cinfo; } /* Additional unidirectional channel for TX if needed */ @@ -241,11 +246,14 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, } } - cinfo->transport_info = smbox; - smbox->cinfo = cinfo; - mutex_init(&smbox->chan_lock); - return 0; + +err_clear_cinfo: + cinfo->transport_info = NULL; + smbox->cinfo = NULL; + devm_iounmap(dev, smbox->shmem); + devm_kfree(dev, smbox); + return ret; } static int mailbox_chan_free(int id, void *p, void *data) diff --git a/drivers/firmware/arm_scmi/transports/smc.c b/drivers/firmware/arm_scmi/transports/smc.c index 21abb571e4f2..1fce3ccdeb7f 100644 --- a/drivers/firmware/arm_scmi/transports/smc.c +++ b/drivers/firmware/arm_scmi/transports/smc.c @@ -172,6 +172,13 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, scmi_info->param_page = SHMEM_PAGE(res.start); scmi_info->param_offset = SHMEM_OFFSET(res.start); } + + scmi_info->func_id = func_id; + scmi_info->cap_id = cap_id; + scmi_info->cinfo = cinfo; + smc_channel_lock_init(scmi_info); + cinfo->transport_info = scmi_info; + /* * If there is an interrupt named "a2p", then the service and * completion of a message is signaled by an interrupt rather than by @@ -183,18 +190,14 @@ static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, IRQF_NO_SUSPEND, dev_name(dev), scmi_info); if (ret) { dev_err(dev, "failed to setup SCMI smc irq\n"); + cinfo->transport_info = NULL; + scmi_info->cinfo = NULL; return ret; } } else { cinfo->no_completion_irq = true; } - scmi_info->func_id = func_id; - scmi_info->cap_id = cap_id; - scmi_info->cinfo = cinfo; - smc_channel_lock_init(scmi_info); - cinfo->transport_info = scmi_info; - return 0; } From 66a0bbf30cc14140fe13f63cd594a7c1ee352b75 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:21 +0100 Subject: [PATCH 064/157] firmware: arm_scmi: Unregister device notifier before IDR teardown The requested-devices notifier looks up protocol fwnodes from the active_protocols IDR. During remove, unregister the notifier before releasing and destroying active_protocols so no notifier callback can race with the IDR teardown. Keep the bus notifier registered until after the protocol state is torn down, matching the existing remove ordering for SCMI bus users. Fixes: 53b8c25df708 ("firmware: arm_scmi: Add common notifier helpers") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-2-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 1d1f5d25d773..52aff3aded4c 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -3401,6 +3401,9 @@ static void scmi_remove(struct platform_device *pdev) list_del(&info->node); mutex_unlock(&scmi_list_mutex); + blocking_notifier_chain_unregister(&scmi_requested_devices_nh, + &info->dev_req_nb); + scmi_notification_exit(&info->handle); mutex_lock(&info->protocols_mtx); @@ -3411,8 +3414,6 @@ static void scmi_remove(struct platform_device *pdev) of_node_put(child); idr_destroy(&info->active_protocols); - blocking_notifier_chain_unregister(&scmi_requested_devices_nh, - &info->dev_req_nb); bus_unregister_notifier(&scmi_bus_type, &info->bus_nb); /* Safe to free channels since no more users */ From 8e49055d0d495c9c07575ad8e111d9eaf0efb13f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:22 +0100 Subject: [PATCH 065/157] firmware: arm_scmi: Quiesce notifications before teardown scmi_notification_exit() clears and releases the notification instance, but transport callbacks can still deliver incoming notifications until the TX/RX channels are freed. During remove, an RX interrupt in that window can enter scmi_notify() while notification state is being torn down and then dereference freed memory. The same ordering exists on the probe error path after notification initialization. The notification late-init worker has a separate lifetime issue: protocol event registration queues ni->init_work on the system workqueue, so destroying ni->notify_wq does not drain that work. If the devres group is released while init_work is still pending or running, the late-init worker can dereference the freed notification instance. Quiesce the notification core before TX/RX channels are torn down, then clean up the channels before releasing the notification core resources. Use disable_work_sync() so future late-init queueing is rejected and any already queued or running late-init work has completed before channel teardown starts. Fixes: 1e7cbfaa66d3 ("firmware: arm_scmi: Free mailbox channels if probe fails") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-3-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 13 +++++++------ drivers/firmware/arm_scmi/notify.c | 21 +++++++++++++++++++++ drivers/firmware/arm_scmi/notify.h | 1 + 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 52aff3aded4c..ec373595f955 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -3325,7 +3325,7 @@ static int scmi_probe(struct platform_device *pdev) dev_err(dev, "%s", err_str); return 0; } - goto notification_exit; + goto raw_mode_cleanup; } mutex_lock(&scmi_list_mutex); @@ -3367,17 +3367,18 @@ static int scmi_probe(struct platform_device *pdev) return 0; -notification_exit: +raw_mode_cleanup: if (IS_ENABLED(CONFIG_ARM_SCMI_RAW_MODE_SUPPORT)) scmi_raw_mode_cleanup(info->raw); - scmi_notification_exit(&info->handle); clear_dev_req_notifier: blocking_notifier_chain_unregister(&scmi_requested_devices_nh, &info->dev_req_nb); clear_bus_notifier: bus_unregister_notifier(&scmi_bus_type, &info->bus_nb); clear_txrx_setup: + scmi_notification_quiesce(&info->handle); scmi_cleanup_txrx_channels(info); + scmi_notification_exit(&info->handle); clear_ida: ida_free(&scmi_id, info->id); @@ -3404,6 +3405,9 @@ static void scmi_remove(struct platform_device *pdev) blocking_notifier_chain_unregister(&scmi_requested_devices_nh, &info->dev_req_nb); + /* Stop transport callbacks before tearing down notifications. */ + scmi_notification_quiesce(&info->handle); + scmi_cleanup_txrx_channels(info); scmi_notification_exit(&info->handle); mutex_lock(&info->protocols_mtx); @@ -3416,9 +3420,6 @@ static void scmi_remove(struct platform_device *pdev) bus_unregister_notifier(&scmi_bus_type, &info->bus_nb); - /* Safe to free channels since no more users */ - scmi_cleanup_txrx_channels(info); - ida_free(&scmi_id, info->id); } diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 3cf706aacc11..fb60fb07c0ed 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -1697,6 +1697,25 @@ int scmi_notification_init(struct scmi_handle *handle) return -ENOMEM; } +/** + * scmi_notification_quiesce() - Stop notification late initialization + * @handle: The handle identifying the platform instance to quiesce + * + * Prevent new late-init work from being queued and wait for any already queued + * or running late-init work to complete before transport channels are torn + * down. + */ +void scmi_notification_quiesce(struct scmi_handle *handle) +{ + struct scmi_notify_instance *ni; + + ni = scmi_notification_instance_data_get(handle); + if (!ni) + return; + + disable_work_sync(&ni->init_work); +} + /** * scmi_notification_exit() - Shutdown and clean Notification core * @handle: The handle identifying the platform instance to shutdown @@ -1708,6 +1727,8 @@ void scmi_notification_exit(struct scmi_handle *handle) ni = scmi_notification_instance_data_get(handle); if (!ni) return; + + scmi_notification_quiesce(handle); scmi_notification_instance_data_set(handle, NULL); /* Destroy while letting pending work complete */ diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi/notify.h index 76758a736cf4..f18f98c5ab3b 100644 --- a/drivers/firmware/arm_scmi/notify.h +++ b/drivers/firmware/arm_scmi/notify.h @@ -82,6 +82,7 @@ struct scmi_protocol_events { }; int scmi_notification_init(struct scmi_handle *handle); +void scmi_notification_quiesce(struct scmi_handle *handle); void scmi_notification_exit(struct scmi_handle *handle); int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, const struct scmi_protocol_handle *ph, From 687d67be3d87894ef12e8a164434612e0b53cfae Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:23 +0100 Subject: [PATCH 066/157] firmware: arm_scmi: Clean up channels on setup failure scmi_channels_setup() can fail after the common BASE channel or earlier protocol channels have already been registered in the TX/RX IDRs. Route this failure through the existing channel cleanup label so the transport channels, transport devices and IDR state created before the failure are released before the probe error path frees the SCMI instance ID. Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-4-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index ec373595f955..d2af47d03b4b 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -3263,7 +3263,7 @@ static int scmi_probe(struct platform_device *pdev) ret = scmi_channels_setup(info); if (ret) { err_str = "failed to setup channels\n"; - goto clear_ida; + goto clear_txrx_setup; } ret = bus_register_notifier(&scmi_bus_type, &info->bus_nb); @@ -3379,7 +3379,6 @@ static int scmi_probe(struct platform_device *pdev) scmi_notification_quiesce(&info->handle); scmi_cleanup_txrx_channels(info); scmi_notification_exit(&info->handle); -clear_ida: ida_free(&scmi_id, info->id); out_err: From d72e7e5f24687c0490aabf317653caffe0447aeb Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:24 +0100 Subject: [PATCH 067/157] firmware: arm_scmi: Free transport channel on IDR failure If transport channel setup succeeds but the following IDR insertion fails, the error path destroys the transport device and frees the channel info without invoking the transport cleanup callback. Call chan_free() before destroying the device so transport specific resources such as IRQs, mailbox channels and mapped shared memory are released consistently with the normal teardown path. Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-5-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index d2af47d03b4b..6ee2152f6b01 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2808,6 +2808,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, "unable to allocate SCMI idr slot err %d\n", ret); /* Destroy channel and device only if created by this call. */ if (tdev) { + info->desc->ops->chan_free(prot_id, cinfo, idr); of_node_put(of_node); scmi_device_destroy(info->dev, prot_id, name); devm_kfree(info->dev, cinfo); From c38b1e19485aaa820e52cfe162525a8af67563da Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:25 +0100 Subject: [PATCH 068/157] firmware: arm_scmi: Avoid IDR updates while cleaning channels scmi_cleanup_channels() walks the TX/RX channel IDRs with idr_for_each() to free transport resources and destroy the dedicated transport devices before calling idr_destroy(). The destroy callback removed each entry from the same IDR being walked. That is not needed for this cleanup path, and it is unsafe because idr_for_each() has not advanced its radix-tree iterator while the callback is running. Removing the current entry from the callback can invalidate the iterator state. The callback also cannot be protected by rcu_read_lock(), because scmi_device_destroy() may sleep. Leave IDR teardown to the following idr_destroy() call and keep the callback limited to device destruction. Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-6-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 6ee2152f6b01..d17559c897a9 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2885,7 +2885,7 @@ static int scmi_channels_setup(struct scmi_info *info) return 0; } -static int scmi_chan_destroy(int id, void *p, void *idr) +static int scmi_chan_destroy(int id, void *p, void *data) { struct scmi_chan_info *cinfo = p; @@ -2898,8 +2898,6 @@ static int scmi_chan_destroy(int id, void *p, void *idr) cinfo->dev = NULL; } - idr_remove(idr, id); - return 0; } From 59407ccb52130f2c81f4b3cbe4f14114afceb54f Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:26 +0100 Subject: [PATCH 069/157] firmware: arm_scmi: Reject out of range DT protocol IDs SCMI protocol IDs carried in message headers are limited by MSG_PROTOCOL_ID_MASK. The DT parsing paths noticed protocol IDs outside that range, but only logged an error and then kept processing the invalid value. That lets a malformed 32-bit DT reg value reach helpers which take a u8 protocol ID, where it can be truncated and/or treated as a different protocol. For channel setup, two different out-of-range values can also be used as distinct IDR keys while aliasing the generated SCMI protocol identity. Skip DT protocol nodes whose reg value does not fit the SCMI protocol ID field before setting up channels or creating protocol devices. Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-7-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index d17559c897a9..0735c6374269 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2873,9 +2873,11 @@ static int scmi_channels_setup(struct scmi_info *info) if (of_property_read_u32(child, "reg", &prot_id)) continue; - if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) + if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) { dev_err(info->dev, "Out of range protocol %d\n", prot_id); + continue; + } ret = scmi_txrx_setup(info, child, prot_id); if (ret) @@ -3339,8 +3341,10 @@ static int scmi_probe(struct platform_device *pdev) if (of_property_read_u32(child, "reg", &prot_id)) continue; - if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) + if (!FIELD_FIT(MSG_PROTOCOL_ID_MASK, prot_id)) { dev_err(dev, "Out of range protocol %d\n", prot_id); + continue; + } if (!scmi_is_protocol_implemented(handle, prot_id)) { dev_err(dev, "SCMI protocol %d not implemented\n", From a71a3d4d8a6e9e399fd988c0e6da47a6ee21c99e Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:27 +0100 Subject: [PATCH 070/157] firmware: arm_scmi: Use channel ID for transport teardown SCMI protocols can share the BASE transport channel when firmware does not describe a dedicated channel for the protocol. In that case multiple IDR entries can point at the same scmi_chan_info, whose owning transport device was created with cinfo->id. scmi_chan_destroy() used the IDR iterator key when destroying the transport device. If an alias entry is visited before the owning channel entry, the lookup can miss the device because the iterator key does not match the protocol ID used when the transport device was created. The code then clears cinfo->dev, so the later owning entry skips teardown and leaks the transport device. Destroy the transport device using cinfo->id, which is the protocol ID that owns the channel and was used when creating the transport device. Fixes: 05a2801d8b90 ("firmware: arm_scmi: Use dedicated devices to initialize channels") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-8-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 0735c6374269..f607557c04ad 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2896,7 +2896,7 @@ static int scmi_chan_destroy(int id, void *p, void *data) struct scmi_device *sdev = to_scmi_dev(cinfo->dev); of_node_put(cinfo->dev->of_node); - scmi_device_destroy(info->dev, id, sdev->name); + scmi_device_destroy(info->dev, cinfo->id, sdev->name); cinfo->dev = NULL; } From e6a0e7a49d83e4fa4e1db68d74f99282eb97aa49 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:28 +0100 Subject: [PATCH 071/157] firmware: arm_scmi: Protect device request lookup with RCU The SCMI device request notifier looks up protocol OF nodes from the active_protocols IDR. The IDR lookup can run concurrently with protocol activation while probe is still registering protocols and creating their SCMI devices. Wrap the lookup in an RCU read-side critical section as required by the IDR API for lockless readers. Fixes: 53b8c25df708 ("firmware: arm_scmi: Add common notifier helpers") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-9-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index f607557c04ad..a2871dca24dc 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -2958,7 +2959,9 @@ static int scmi_device_request_notifier(struct notifier_block *nb, struct scmi_device_id *id_table = data; struct scmi_info *info = req_nb_to_scmi_info(nb); + rcu_read_lock(); np = idr_find(&info->active_protocols, id_table->protocol_id); + rcu_read_unlock(); if (!np) return NOTIFY_DONE; From e3a5c30d233ca5d3e799a80da806554c703bda13 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:29 +0100 Subject: [PATCH 072/157] firmware: arm_scmi: Drop handle on protocol bind failures The SCMI bus notifier acquires an SCMI handle when the driver core emits BUS_NOTIFY_BIND_DRIVER, before invoking the protocol driver probe callback. The protocol probe path only checks whether sdev->handle is set. If device_link_add() fails after the handle has been acquired, the protocol device can still bind with a valid handle but without the dependency link to the SCMI parent. A concurrent parent unbind can then miss the child and tear down the SCMI instance while the child still holds a handle into it. If the protocol driver probe later fails, for example with -EPROBE_DEFER, the driver core emits BUS_NOTIFY_DRIVER_NOT_BOUND rather than BUS_NOTIFY_UNBOUND_DRIVER. The SCMI notifier only released the handle on BUS_NOTIFY_UNBOUND_DRIVER, so each failed protocol-device bind leaked the SCMI instance users refcount and left sdev->handle set after the failed probe. Make the link helper report failure and drop the acquired handle if the link cannot be created. Also handle BUS_NOTIFY_DRIVER_NOT_BOUND in the same cleanup path used for unbind so failed probes balance the earlier BUS_NOTIFY_BIND_DRIVER acquisition. Fixes: 971fc0665f13 ("firmware: arm_scmi: Move handle get/set helpers") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-10-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 31 ++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index a2871dca24dc..84d1294f269c 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2629,21 +2629,31 @@ static int scmi_handle_put(const struct scmi_handle *handle) return 0; } -static void scmi_device_link_add(struct device *consumer, +static bool scmi_device_link_add(struct device *consumer, struct device *supplier) { struct device_link *link; link = device_link_add(consumer, supplier, DL_FLAG_AUTOREMOVE_CONSUMER); - WARN_ON(!link); + return !WARN_ON(!link); +} + +static void scmi_clear_handle(struct scmi_device *scmi_dev) +{ + if (!scmi_dev->handle) + return; + + scmi_handle_put(scmi_dev->handle); + scmi_dev->handle = NULL; } static void scmi_set_handle(struct scmi_device *scmi_dev) { scmi_dev->handle = scmi_handle_get(&scmi_dev->dev); - if (scmi_dev->handle) - scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev); + if (scmi_dev->handle && + !scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev)) + scmi_clear_handle(scmi_dev); } static int __scmi_xfer_info_init(struct scmi_info *sinfo, @@ -2927,6 +2937,7 @@ static int scmi_bus_notifier(struct notifier_block *nb, { struct scmi_info *info = bus_nb_to_scmi_info(nb); struct scmi_device *sdev = to_scmi_dev(data); + const char *status; /* Skip devices of different SCMI instances */ if (sdev->dev.parent != info->dev) @@ -2936,18 +2947,22 @@ static int scmi_bus_notifier(struct notifier_block *nb, case BUS_NOTIFY_BIND_DRIVER: /* setup handle now as the transport is ready */ scmi_set_handle(sdev); + status = "about to be BOUND."; + break; + case BUS_NOTIFY_DRIVER_NOT_BOUND: + scmi_clear_handle(sdev); + status = "NOT BOUND."; break; case BUS_NOTIFY_UNBOUND_DRIVER: - scmi_handle_put(sdev->handle); - sdev->handle = NULL; + scmi_clear_handle(sdev); + status = "UNBOUND."; break; default: return NOTIFY_DONE; } dev_dbg(info->dev, "Device %s (%s) is now %s\n", dev_name(&sdev->dev), - sdev->name, action == BUS_NOTIFY_BIND_DRIVER ? - "about to be BOUND." : "UNBOUND."); + sdev->name, status); return NOTIFY_OK; } From e4c16ae24ca027d7a72d645b42f976bb3e99b4b0 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:30 +0100 Subject: [PATCH 073/157] firmware: arm_scmi: Clear SystemPower flag on create failure __scmi_device_create() reserves the singleton SystemPower protocol device before registering the SCMI device. If any later step fails, a stale reservation can make a later retry reject SystemPower device creation permanently, for example after probe deferral. A plain global boolean is not enough to track the reservation. A delayed final release of an older SystemPower device could clear the boolean after a newer device has already claimed it, breaking the singleton guarantee for the active device. Track the reservation with the scmi_device pointer itself. Claim it with cmpxchg(NULL, scmi_dev) after allocating the device object, and release it with cmpxchg(scmi_dev, NULL) from the common cleanup helper. This lets the create-failure, explicit destroy and final release paths clear only the reservation owned by the device being cleaned up. Fixes: 2c3e674465e7 ("firmware: arm_scmi: Refactor device create/destroy helpers") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-11-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 61 ++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 793be9eabaed..dcaefc1aa892 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -7,7 +7,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include #include #include #include @@ -33,8 +32,8 @@ struct scmi_requested_dev { struct list_head node; }; -/* Track globally the creation of SCMI SystemPower related devices */ -static atomic_t scmi_syspower_registered = ATOMIC_INIT(0); +/* Track globally the SCMI SystemPower protocol device. */ +static struct scmi_device *scmi_syspower_registered; /** * scmi_protocol_device_request - Helper to request a device @@ -391,10 +390,17 @@ void scmi_driver_unregister(struct scmi_driver *driver) } EXPORT_SYMBOL_GPL(scmi_driver_unregister); +static void scmi_device_release_syspower(struct scmi_device *scmi_dev) +{ + if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM) + cmpxchg(&scmi_syspower_registered, scmi_dev, NULL); +} + static void scmi_device_release(struct device *dev) { struct scmi_device *scmi_dev = to_scmi_dev(dev); + scmi_device_release_syspower(scmi_dev); kfree_const(scmi_dev->name); kfree(scmi_dev); } @@ -406,9 +412,7 @@ static void __scmi_device_destroy(struct scmi_device *scmi_dev) dev_name(&scmi_dev->dev), scmi_dev->protocol_id, scmi_dev->name); - if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM) - atomic_set(&scmi_syspower_registered, 0); - + scmi_device_release_syspower(scmi_dev); ida_free(&scmi_bus_id, scmi_dev->id); device_unregister(&scmi_dev->dev); } @@ -419,6 +423,7 @@ __scmi_device_create(struct device_node *np, struct device *parent, { int id, retval; struct scmi_device *scmi_dev; + bool syspower = (protocol == SCMI_PROTOCOL_SYSTEM); /* * If the same protocol/name device already exist under the same parent @@ -431,39 +436,33 @@ __scmi_device_create(struct device_node *np, struct device *parent, if (scmi_dev) return scmi_dev; - /* - * Ignore any possible subsequent failures while creating the device - * since we are doomed anyway at that point; not using a mutex which - * spans across this whole function to keep things simple and to avoid - * to serialize all the __scmi_device_create calls across possibly - * different SCMI server instances (parent) - */ - if (protocol == SCMI_PROTOCOL_SYSTEM && - atomic_cmpxchg(&scmi_syspower_registered, 0, 1)) { - dev_warn(parent, - "SCMI SystemPower protocol device must be unique !\n"); - return NULL; - } - scmi_dev = kzalloc_obj(*scmi_dev); if (!scmi_dev) return NULL; - scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL); - if (!scmi_dev->name) { + scmi_dev->protocol_id = protocol; + + /* + * Reserve the singleton SystemPower protocol device using the device + * pointer itself, so delayed release of an older device cannot clear + * a reservation owned by a newer device. + */ + if (syspower && cmpxchg(&scmi_syspower_registered, NULL, scmi_dev)) { + dev_warn(parent, + "SCMI SystemPower protocol device must be unique !\n"); kfree(scmi_dev); return NULL; } + scmi_dev->name = kstrdup_const(name ?: "unknown", GFP_KERNEL); + if (!scmi_dev->name) + goto free_dev; + id = ida_alloc_min(&scmi_bus_id, 1, GFP_KERNEL); - if (id < 0) { - kfree_const(scmi_dev->name); - kfree(scmi_dev); - return NULL; - } + if (id < 0) + goto free_name; scmi_dev->id = id; - scmi_dev->protocol_id = protocol; scmi_dev->dev.parent = parent; device_set_node(&scmi_dev->dev, of_fwnode_handle(np)); scmi_dev->dev.bus = &scmi_bus_type; @@ -482,6 +481,12 @@ __scmi_device_create(struct device_node *np, struct device *parent, put_device(&scmi_dev->dev); ida_free(&scmi_bus_id, id); return NULL; +free_name: + kfree_const(scmi_dev->name); +free_dev: + scmi_device_release_syspower(scmi_dev); + kfree(scmi_dev); + return NULL; } static struct scmi_device * From 659705f5eb378cea462214b74bdc6240cdab53fc Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:31 +0100 Subject: [PATCH 074/157] firmware: arm_scmi: Fix OF node reference handling SCMI devices store the DT node in dev.of_node through device_set_node(), but that helper only assigns the fwnode and of_node pointers without taking an OF node reference. Take a reference when assigning the node and release it from the SCMI device release path. With the device owning that reference, remove the separate channel-side get/put pair from the core driver. Fixes: 96da4a99ce50 ("firmware: arm_scmi: Set fwnode for the scmi_device") Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-12-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 3 ++- drivers/firmware/arm_scmi/driver.c | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index dcaefc1aa892..a14df82a9310 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -401,6 +401,7 @@ static void scmi_device_release(struct device *dev) struct scmi_device *scmi_dev = to_scmi_dev(dev); scmi_device_release_syspower(scmi_dev); + of_node_put(dev->of_node); kfree_const(scmi_dev->name); kfree(scmi_dev); } @@ -464,7 +465,7 @@ __scmi_device_create(struct device_node *np, struct device *parent, scmi_dev->id = id; scmi_dev->dev.parent = parent; - device_set_node(&scmi_dev->dev, of_fwnode_handle(np)); + device_set_node(&scmi_dev->dev, of_fwnode_handle(of_node_get(np))); scmi_dev->dev.bus = &scmi_bus_type; scmi_dev->dev.release = scmi_device_release; dev_set_name(&scmi_dev->dev, "scmi_dev.%d", id); diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 84d1294f269c..532f1f65b2ab 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2789,14 +2789,12 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, devm_kfree(info->dev, cinfo); return -EINVAL; } - of_node_get(of_node); cinfo->id = prot_id; cinfo->dev = &tdev->dev; cinfo->handle = &info->handle; ret = info->desc->ops->chan_setup(cinfo, info->dev, tx); if (ret) { - of_node_put(of_node); scmi_device_destroy(info->dev, prot_id, name); devm_kfree(info->dev, cinfo); return ret; @@ -2820,7 +2818,6 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, /* Destroy channel and device only if created by this call. */ if (tdev) { info->desc->ops->chan_free(prot_id, cinfo, idr); - of_node_put(of_node); scmi_device_destroy(info->dev, prot_id, name); devm_kfree(info->dev, cinfo); } @@ -2906,7 +2903,6 @@ static int scmi_chan_destroy(int id, void *p, void *data) struct scmi_info *info = handle_to_scmi_info(cinfo->handle); struct scmi_device *sdev = to_scmi_dev(cinfo->dev); - of_node_put(cinfo->dev->of_node); scmi_device_destroy(info->dev, cinfo->id, sdev->name); cinfo->dev = NULL; } From 6f7c06744d53dc8e047725d411d7f915d9ec35ae Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:32 +0100 Subject: [PATCH 075/157] firmware: arm_scmi: Unwind TX receiver mailbox setup failure mailbox_chan_setup() can request an additional unidirectional TX receiver channel after successfully acquiring the primary channel. If that second request fails, the function returns immediately and leaves the primary channel allocated. Unwind the primary mailbox channel before returning the error so probe deferral or other setup failures do not leave the channel busy for later probe attempts. Fixes: 9f68ff79ec2c ("firmware: arm_scmi: Add support for unidirectional mailbox channels") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-13-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/transports/mailbox.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c index b6459fbb8151..37e3eab529ea 100644 --- a/drivers/firmware/arm_scmi/transports/mailbox.c +++ b/drivers/firmware/arm_scmi/transports/mailbox.c @@ -230,9 +230,10 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, smbox->chan_receiver = mbox_request_channel(cl, a2p_rx_chan); if (IS_ERR(smbox->chan_receiver)) { ret = PTR_ERR(smbox->chan_receiver); + smbox->chan_receiver = NULL; if (ret != -EPROBE_DEFER) dev_err(cdev, "failed to request SCMI Tx Receiver mailbox\n"); - return ret; + goto err_free_chan; } } @@ -248,6 +249,8 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, return 0; +err_free_chan: + mbox_free_channel(smbox->chan); err_clear_cinfo: cinfo->transport_info = NULL; smbox->cinfo = NULL; From f3e3773c4e5e96549d7540d8ddeb4fcd534f6f1d Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:33 +0100 Subject: [PATCH 076/157] firmware: arm_scmi: Unwind P2A receiver mailbox setup failure mailbox_chan_setup() can request an additional P2A receiver channel after successfully acquiring the primary P2A channel. If that later request fails, the function returns immediately and leaves the primary channel allocated. Unwind the primary mailbox channel before returning the error so probe deferral or other setup failures do not leave the channel busy for later probe attempts. Fixes: fa8b28ba22d9 ("firmware: arm_scmi: Add support for platform to agent channel completion") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-14-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/transports/mailbox.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/arm_scmi/transports/mailbox.c b/drivers/firmware/arm_scmi/transports/mailbox.c index 37e3eab529ea..308736c3ead9 100644 --- a/drivers/firmware/arm_scmi/transports/mailbox.c +++ b/drivers/firmware/arm_scmi/transports/mailbox.c @@ -241,9 +241,10 @@ static int mailbox_chan_setup(struct scmi_chan_info *cinfo, struct device *dev, smbox->chan_platform_receiver = mbox_request_channel(cl, p2a_rx_chan); if (IS_ERR(smbox->chan_platform_receiver)) { ret = PTR_ERR(smbox->chan_platform_receiver); + smbox->chan_platform_receiver = NULL; if (ret != -EPROBE_DEFER) dev_err(cdev, "failed to request SCMI P2A Receiver mailbox\n"); - return ret; + goto err_free_chan; } } From 6abe8fe36b29ff51d1a42c2f338972883f4751a5 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:34 +0100 Subject: [PATCH 077/157] firmware: arm_scmi: Fix SCMI device destroy lifetimes scmi_child_dev_find() drops the reference returned by device_find_child() before returning the scmi_device pointer. A concurrent unregister can then release the device while the destroy path is still using the returned pointer. Make the lookup helper return the device_find_child() reference and keep it until scmi_device_destroy() has finished unregistering the child. Also split device_unregister() in __scmi_device_destroy() so the SCMI bus ID is not made reusable until after device_del() has removed the old scmi_dev.N name from sysfs. This avoids a new SCMI device reusing the same ID while the old device is still registered. The final device release callback is also a possible cleanup path when SCMI children are deleted by driver core recursion rather than __scmi_device_destroy(). Release the SCMI bus ID from a common helper used by destroy, register-failure and final-release paths, and clear scmi_dev->id after freeing it so the final release cannot free the same ID again. Fixes: 9ca67840c0dd ("firmware: arm_scmi: Balance device refcount when destroying devices") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-15-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 39 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index a14df82a9310..e1f66c08c81d 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -237,8 +237,9 @@ static int scmi_match_by_id_table(struct device *dev, const void *data) return scmi_dev_match_by_id_table(scmi_dev, id_table); } -static struct scmi_device *scmi_child_dev_find(struct device *parent, - int prot_id, const char *name) +/* Returns a device_find_child() reference which must be dropped by caller. */ +static struct scmi_device * +scmi_child_dev_find_get(struct device *parent, int prot_id, const char *name) { struct scmi_device_id id_table[2] = { 0 }; struct device *dev; @@ -250,9 +251,6 @@ static struct scmi_device *scmi_child_dev_find(struct device *parent, if (!dev) return NULL; - /* Drop the refcnt bumped implicitly by device_find_child */ - put_device(dev); - return to_scmi_dev(dev); } @@ -390,17 +388,22 @@ void scmi_driver_unregister(struct scmi_driver *driver) } EXPORT_SYMBOL_GPL(scmi_driver_unregister); -static void scmi_device_release_syspower(struct scmi_device *scmi_dev) +static void scmi_device_release_resources(struct scmi_device *scmi_dev) { if (scmi_dev->protocol_id == SCMI_PROTOCOL_SYSTEM) cmpxchg(&scmi_syspower_registered, scmi_dev, NULL); + + if (scmi_dev->id) { + ida_free(&scmi_bus_id, scmi_dev->id); + scmi_dev->id = 0; + } } static void scmi_device_release(struct device *dev) { struct scmi_device *scmi_dev = to_scmi_dev(dev); - scmi_device_release_syspower(scmi_dev); + scmi_device_release_resources(scmi_dev); of_node_put(dev->of_node); kfree_const(scmi_dev->name); kfree(scmi_dev); @@ -413,9 +416,9 @@ static void __scmi_device_destroy(struct scmi_device *scmi_dev) dev_name(&scmi_dev->dev), scmi_dev->protocol_id, scmi_dev->name); - scmi_device_release_syspower(scmi_dev); - ida_free(&scmi_bus_id, scmi_dev->id); - device_unregister(&scmi_dev->dev); + device_del(&scmi_dev->dev); + scmi_device_release_resources(scmi_dev); + put_device(&scmi_dev->dev); } static struct scmi_device * @@ -433,9 +436,11 @@ __scmi_device_create(struct device_node *np, struct device *parent, * each DT defined protocol at probe time, and the concurrent * registration of SCMI drivers. */ - scmi_dev = scmi_child_dev_find(parent, protocol, name); - if (scmi_dev) + scmi_dev = scmi_child_dev_find_get(parent, protocol, name); + if (scmi_dev) { + put_device(&scmi_dev->dev); return scmi_dev; + } scmi_dev = kzalloc_obj(*scmi_dev); if (!scmi_dev) @@ -479,13 +484,13 @@ __scmi_device_create(struct device_node *np, struct device *parent, return scmi_dev; put_dev: + scmi_device_release_resources(scmi_dev); put_device(&scmi_dev->dev); - ida_free(&scmi_bus_id, id); return NULL; free_name: kfree_const(scmi_dev->name); free_dev: - scmi_device_release_syspower(scmi_dev); + scmi_device_release_resources(scmi_dev); kfree(scmi_dev); return NULL; } @@ -567,9 +572,11 @@ void scmi_device_destroy(struct device *parent, int protocol, const char *name) { struct scmi_device *scmi_dev; - scmi_dev = scmi_child_dev_find(parent, protocol, name); - if (scmi_dev) + scmi_dev = scmi_child_dev_find_get(parent, protocol, name); + if (scmi_dev) { __scmi_device_destroy(scmi_dev); + put_device(&scmi_dev->dev); + } } EXPORT_SYMBOL_GPL(scmi_device_destroy); From a14dd8fe0a95db638c550ed984cfe2a7428c783d Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Tue, 14 Jul 2026 13:56:35 +0100 Subject: [PATCH 078/157] firmware: arm_scmi: Fix transport device teardown lookup SCMI transport devices are deliberately excluded from normal SCMI bus matching so protocol drivers cannot bind to the internal transport children. However, scmi_device_destroy() uses the same protocol/name lookup to find devices that must be unregistered during channel teardown. Split the match helper so driver matching still skips transport devices, while explicit child lookup can find them for teardown. Use a shared transport-device name prefix macro for both matching and name generation. Since transport-device names are derived from direction and protocol ID, reject duplicate protocol channel setup before creating or finding a transport device. This prevents malformed firmware with duplicate protocol child nodes from reusing an existing transport device and then destroying it when the duplicate IDR insertion fails. Fixes: 9593804c44c2 ("firmware: arm_scmi: Exclude transport devices from bus matching") Reported-by: Sashiko Link: https://patch.msgid.link/20260714-scmi_core_fixes-v6-16-3afe499d46e3@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 22 +++++++++++++++++----- drivers/firmware/arm_scmi/common.h | 2 ++ drivers/firmware/arm_scmi/driver.c | 5 ++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index e1f66c08c81d..7f06d56e4905 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -200,21 +200,33 @@ scmi_protocol_table_unregister(const struct scmi_device_id *id_table) scmi_protocol_device_unrequest(entry); } -static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev, - const struct scmi_device_id *id_table) +static bool scmi_device_is_transport(const struct scmi_device *scmi_dev) +{ + return !strncmp(scmi_dev->name, SCMI_TRANSPORT_DEVNAME_PREFIX, + strlen(SCMI_TRANSPORT_DEVNAME_PREFIX)); +} + +static int __scmi_dev_match_by_id_table(struct scmi_device *scmi_dev, + const struct scmi_device_id *id_table, + bool skip_transport) { if (!id_table || !id_table->name) return 0; - /* Always skip transport devices from matching */ for (; id_table->protocol_id && id_table->name; id_table++) if (id_table->protocol_id == scmi_dev->protocol_id && - strncmp(scmi_dev->name, "__scmi_transport_device", 23) && + !(skip_transport && scmi_device_is_transport(scmi_dev)) && !strcmp(id_table->name, scmi_dev->name)) return 1; return 0; } +static int scmi_dev_match_by_id_table(struct scmi_device *scmi_dev, + const struct scmi_device_id *id_table) +{ + return __scmi_dev_match_by_id_table(scmi_dev, id_table, true); +} + static int scmi_dev_match_id(struct scmi_device *scmi_dev, const struct scmi_driver *scmi_drv) { @@ -234,7 +246,7 @@ static int scmi_match_by_id_table(struct device *dev, const void *data) struct scmi_device *scmi_dev = to_scmi_dev(dev); const struct scmi_device_id *id_table = data; - return scmi_dev_match_by_id_table(scmi_dev, id_table); + return __scmi_dev_match_by_id_table(scmi_dev, id_table, false); } /* Returns a device_find_child() reference which must be dropped by caller. */ diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index b9723c105fc1..fe8c22cfb9f7 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -34,6 +34,8 @@ #define SCMI_SHMEM_MAX_PAYLOAD_SIZE 104 +#define SCMI_TRANSPORT_DEVNAME_PREFIX "__scmi_transport_device" + enum scmi_error_codes { SCMI_SUCCESS = 0, /* Success */ SCMI_ERR_SUPPORT = -1, /* Not supported */ diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 532f1f65b2ab..ef29fd223287 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -2762,6 +2762,9 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, idx = tx ? 0 : 1; idr = tx ? &info->tx_idr : &info->rx_idr; + if (idr_find(idr, prot_id)) + return -EEXIST; + if (!info->desc->ops->chan_available(of_node, idx)) { cinfo = idr_find(idr, SCMI_PROTOCOL_BASE); if (unlikely(!cinfo)) /* Possible only if platform has no Rx */ @@ -2779,7 +2782,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device_node *of_node, cinfo->no_completion_irq = info->desc->no_completion_irq; /* Create a unique name for this transport device */ - snprintf(name, 32, "__scmi_transport_device_%s_%02X", + snprintf(name, sizeof(name), SCMI_TRANSPORT_DEVNAME_PREFIX "_%s_%02X", idx ? "rx" : "tx", prot_id); /* Create a uniquely named, dedicated transport device for this chan */ tdev = scmi_device_create(of_node, info->dev, prot_id, name); From 74a161d20ef60bb8db333a5203284796429ecb99 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Wed, 15 Jul 2026 12:10:37 +0100 Subject: [PATCH 079/157] soc/tegra: pmc: Don't register sys-off handler for multi-socket devices The kernel does not permit a device to register multiple sys-off handlers for non-default priority levels. Hence, for multi-socket Tegra devices, registering the sys-off handler in the Tegra PMC driver (which uses the low priority level) more than once fails. The Tegra PMC sys-off handler is supported for all current generations of the Tegra SoC, however, it is only used as a low priority handler if everything else fails. For example, on Tegra264 the EFI reboot handler is called to restart the device. Therefore, for multi-socket devices, skip the registering of the sys-off handler to because this is not critical. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index f419a5395545..41ed716d5857 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3074,15 +3074,18 @@ static int tegra_pmc_probe(struct platform_device *pdev) } } - err = devm_register_sys_off_handler(&pdev->dev, - SYS_OFF_MODE_RESTART, - SYS_OFF_PRIO_LOW, - tegra_pmc_restart_handler, - pmc); - if (err) { - dev_err(&pdev->dev, "failed to register sys-off handler: %d\n", - err); - return err; + if (dev_to_node(&pdev->dev) == NUMA_NO_NODE) { + err = devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_LOW, + tegra_pmc_restart_handler, + pmc); + if (err) { + dev_err(&pdev->dev, + "failed to register sys-off handler: %d\n", + err); + return err; + } } /* From 85b88ceeeeb7bb93cd0af7ccaf2a4b010d5bc5c6 Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 2 Jul 2026 17:28:27 +0530 Subject: [PATCH 080/157] net: ipa: Switch to generic PAS TZ APIs Switch ipa client driver over to generic PAS TZ APIs. Generic PAS TZ service allows to support multiple TZ implementation backends like QTEE based SCM PAS service, OP-TEE based PAS service and any further future TZ backend service. Reviewed-by: Alex Elder Reviewed-by: Konrad Dybcio Signed-off-by: Sumit Garg Acked-by: Alex Elder Link: https://lore.kernel.org/r/20260702115835.167602-12-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- drivers/net/ipa/Kconfig | 2 +- drivers/net/ipa/ipa_main.c | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/net/ipa/Kconfig b/drivers/net/ipa/Kconfig index 01d219d3760c..a9aff1b7977d 100644 --- a/drivers/net/ipa/Kconfig +++ b/drivers/net/ipa/Kconfig @@ -6,7 +6,7 @@ config QCOM_IPA depends on QCOM_RPROC_COMMON || (QCOM_RPROC_COMMON=n && COMPILE_TEST) depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n select QCOM_MDT_LOADER - select QCOM_SCM + select QCOM_PAS select QCOM_QMI_HELPERS help Choose Y or M here to include support for the Qualcomm diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c index 788dd99af2a4..3cd9e44680e9 100644 --- a/drivers/net/ipa/ipa_main.c +++ b/drivers/net/ipa/ipa_main.c @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include "ipa.h" @@ -624,10 +624,13 @@ static int ipa_firmware_load(struct device *dev) } ret = qcom_mdt_load(dev, fw, path, IPA_PAS_ID, virt, phys, size, NULL); - if (ret) + if (ret) { dev_err(dev, "error %d loading \"%s\"\n", ret, path); - else if ((ret = qcom_scm_pas_auth_and_reset(IPA_PAS_ID))) - dev_err(dev, "error %d authenticating \"%s\"\n", ret, path); + } else { + ret = qcom_pas_auth_and_reset(IPA_PAS_ID); + if (ret) + dev_err(dev, "error %d authenticating \"%s\"\n", ret, path); + } memunmap(virt); out_release_firmware: @@ -758,7 +761,7 @@ static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev) return IPA_LOADER_INVALID; out_self: /* We need Trust Zone to load firmware; make sure it's available */ - if (qcom_scm_is_available()) + if (qcom_pas_is_available()) return IPA_LOADER_SELF; return IPA_LOADER_DEFER; From 07231087d5e24c4d9c578c824b96f8af913f7324 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 9 Jul 2026 12:47:44 +0200 Subject: [PATCH 081/157] soc: renesas: r8a78000: Drop duplicate "default ARCH_RENESAS" The Kconfig entry for ARCH_R8A78000 contains both "default y if ARCH_RENESAS" and "default ARCH_RENESAS", which are sort-of duplicates. Drop the latter, to restore consistency with the other ARM64 entries. Fixes: 5284d0b09d1bdc69 ("soc: renesas: Identify R-Car X5H") Reported-by: Marek Vasut Closes: https://lore.kernel.org/a069d50d-030d-4189-ae9d-37f989829da4@mailbox.org Signed-off-by: Geert Uytterhoeven Reviewed-by: Marek Vasut Reviewed-by: Kuninori Morimoto Reviewed-by: Duy Nguyen Link: https://patch.msgid.link/64de6e95719a6dec7412cf7e917a42749e738b99.1783593775.git.geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 2ab150d04bb1..fdf18ed2dfc2 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -356,7 +356,6 @@ config ARCH_R8A779H0 config ARCH_R8A78000 bool "ARM64 Platform support for R8A78000 (R-Car X5H)" default y if ARCH_RENESAS - default ARCH_RENESAS select ARCH_RCAR_GEN5 help This enables support for the Renesas R-Car X5H SoC. From 26ead60b1a2722627759dc8c279ed336826bb5e1 Mon Sep 17 00:00:00 2001 From: Komal Bajaj Date: Thu, 30 Apr 2026 18:08:04 +0530 Subject: [PATCH 082/157] dt-bindings: sram: Document qcom,shikra-imem compatible Add compatible for Shikra SoC IMEM. Signed-off-by: Komal Bajaj Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260430-shikra-imem-binding-v1-1-c6976239f90f@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/sram/sram.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sram/sram.yaml b/Documentation/devicetree/bindings/sram/sram.yaml index 14a537e82a84..be0c857efd44 100644 --- a/Documentation/devicetree/bindings/sram/sram.yaml +++ b/Documentation/devicetree/bindings/sram/sram.yaml @@ -40,6 +40,7 @@ properties: - qcom,maili-imem - qcom,milos-imem - qcom,rpm-msg-ram + - qcom,shikra-imem - rockchip,rk3288-pmu-sram reg: From 3c817862759913097f11467ed4ed2bbf974dabaf Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Mon, 6 Jul 2026 22:47:06 +0800 Subject: [PATCH 083/157] soc: ti: knav_qmss: Remove debugfs file on teardown knav_queue_probe() creates the global qmss debugfs file whose show callback reads the global knav_qdev state. knav_queue_remove() tears down the queue manager resources but leaves the debugfs file published. Save the debugfs dentry in struct knav_device and remove it during teardown before the resources used by the show callback are released. While touching the debugfs_create_file() call, spell the unchanged read- only file mode as 0444. Fixes: 41f93af900a2 ("soc: ti: add Keystone Navigator QMSS driver") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260706144706.96313-1-pengpeng@iscas.ac.cn Signed-off-by: Nishanth Menon --- drivers/soc/ti/knav_qmss.h | 1 + drivers/soc/ti/knav_qmss_queue.c | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/soc/ti/knav_qmss.h b/drivers/soc/ti/knav_qmss.h index 037dc1b36645..8a624fbda84a 100644 --- a/drivers/soc/ti/knav_qmss.h +++ b/drivers/soc/ti/knav_qmss.h @@ -304,6 +304,7 @@ struct knav_device { struct list_head pools; struct list_head pdsps; struct list_head qmgrs; + struct dentry *debugfs_file; enum qmss_version version; }; diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 7410b63af0e6..3e4041454f69 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -1849,8 +1849,9 @@ static int knav_queue_probe(struct platform_device *pdev) goto err; } - debugfs_create_file("qmss", S_IFREG | S_IRUGO, NULL, NULL, - &knav_queue_debug_fops); + knav_qdev->debugfs_file = + debugfs_create_file("qmss", 0444, NULL, NULL, + &knav_queue_debug_fops); device_ready = true; return 0; @@ -1868,6 +1869,8 @@ static void knav_queue_remove(struct platform_device *pdev) struct knav_device *kdev = platform_get_drvdata(pdev); device_ready = false; + debugfs_remove(kdev->debugfs_file); + kdev->debugfs_file = NULL; knav_queue_stop_pdsps(kdev); knav_queue_free_regions(kdev); knav_free_queue_ranges(kdev); From 43584144a46bccbe90482cd8eede9596d8fc47dc Mon Sep 17 00:00:00 2001 From: Faiyaz Mohammed Date: Mon, 13 Jul 2026 15:28:52 +0530 Subject: [PATCH 084/157] soc: qcom: llcc: Skip ECC interrupt setup on Shikra, pre-configured by DSF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Shikra, the DDR System Firmware (DSF) configures ECC interrupt routing before the kernel driver probes — it enables Tag/Data RAM interrupts and programs error thresholds in the LLCC interrupt-enable registers. Set irq_configured in shikra_cfg so that qcom_llcc_edac_probe() skips calling qcom_llcc_core_setup(), which would otherwise overwrite the firmware-managed register state with redundant writes. Signed-off-by: Faiyaz Mohammed Reviewed-by: Mukesh Ojha Reviewed-by: Komal Bajaj Link: https://lore.kernel.org/r/20260713-shikra-edac-v2-1-22fcb3ca9fb9@qti.qualcomm.com [bjorn: Changed subject prefix] Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 22c8099cf6bb..733999867bbf 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -4595,6 +4595,7 @@ static const struct qcom_llcc_config shikra_cfg[] = { .size = ARRAY_SIZE(shikra_data), .reg_offset = llcc_v2_1_reg_offset, .edac_reg_offset = &llcc_v2_1_edac_reg_offset, + .irq_configured = true, }, }; From 2c4097e6c4aed276c5e9ec2ab331ab397ea780bf Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 22 Jul 2026 10:52:50 +0100 Subject: [PATCH 085/157] firmware: arm_scmi: Fix requested device removal race scmi_protocol_device_unrequest() drops scmi_requested_devices_mtx while notifying listeners but continues to retain the per-protocol list head. When two SCMI drivers for the same protocol unregister concurrently, one thread can remove the final request and free the list head while the other is running its notifier. The latter then dereferences the freed list head after reacquiring the mutex and can free it a second time. Complete the list and IDR updates, including freeing an empty list head, before dropping the mutex. Keep the blocking notifier outside the critical section and retain only the detached request across the callback. Fixes: d3cd7c525fd2 ("firmware: arm_scmi: Refactor protocol device creation") Reported-by: Sashiko Link: https://patch.msgid.link/20260722095250.2011630-1-sudeep.holla@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 7f06d56e4905..cdaea09d9611 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -158,6 +158,7 @@ static int scmi_protocol_table_register(const struct scmi_device_id *id_table) */ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table) { + struct scmi_requested_dev *rdev, *victim = NULL; struct list_head *phead; pr_debug("Unrequesting SCMI device (%s) for protocol %x\n", @@ -166,29 +167,28 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table mutex_lock(&scmi_requested_devices_mtx); phead = idr_find(&scmi_requested_devices, id_table->protocol_id); if (phead) { - struct scmi_requested_dev *victim, *tmp; - - list_for_each_entry_safe(victim, tmp, phead, node) { - if (!strcmp(victim->id_table->name, id_table->name)) { - list_del(&victim->node); - - mutex_unlock(&scmi_requested_devices_mtx); - blocking_notifier_call_chain(&scmi_requested_devices_nh, - SCMI_BUS_NOTIFY_DEVICE_UNREQUEST, - (void *)victim->id_table); - kfree(victim); - mutex_lock(&scmi_requested_devices_mtx); + list_for_each_entry(rdev, phead, node) { + if (!strcmp(rdev->id_table->name, id_table->name)) { + victim = rdev; + list_del(&rdev->node); break; } } - if (list_empty(phead)) { + if (victim && list_empty(phead)) { idr_remove(&scmi_requested_devices, id_table->protocol_id); kfree(phead); } } mutex_unlock(&scmi_requested_devices_mtx); + + if (victim) { + blocking_notifier_call_chain(&scmi_requested_devices_nh, + SCMI_BUS_NOTIFY_DEVICE_UNREQUEST, + (void *)victim->id_table); + kfree(victim); + } } static void From 2224b622260ba590ab56ea1585d6bf7610be25b2 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 22 Jul 2026 18:35:20 +0100 Subject: [PATCH 086/157] firmware: arm_scmi: Roll back partial protocol table registration scmi_protocol_table_register() can leave earlier requests registered when a later entry in the same ID table fails. Each request retains a pointer to the driver's ID table, so a failed module load can leave a dangling pointer after the module storage is released. Unrequest only the successfully registered prefix, in reverse order, before returning the failure. Leave the failed entry and the remaining entries untouched because matching requests can be owned by another driver. Fixes: 2858f6e5f064 ("firmware: arm_scmi: Add multiple protocols registration support") Reported-by: Sashiko Link: https://patch.msgid.link/20260722173521.2184378-1-sudeep.holla@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index cdaea09d9611..d520910eb251 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -135,17 +135,6 @@ static int scmi_protocol_device_request(const struct scmi_device_id *id_table) return ret; } -static int scmi_protocol_table_register(const struct scmi_device_id *id_table) -{ - int ret = 0; - const struct scmi_device_id *entry; - - for (entry = id_table; entry->name && ret == 0; entry++) - ret = scmi_protocol_device_request(entry); - - return ret; -} - /** * scmi_protocol_device_unrequest - Helper to unrequest a device * @@ -191,6 +180,26 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table } } +static int scmi_protocol_table_register(const struct scmi_device_id *id_table) +{ + const struct scmi_device_id *entry; + int ret; + + for (entry = id_table; entry->name; entry++) { + ret = scmi_protocol_device_request(entry); + if (ret) + goto err_unrequest; + } + + return 0; + +err_unrequest: + while (entry != id_table) + scmi_protocol_device_unrequest(--entry); + + return ret; +} + static void scmi_protocol_table_unregister(const struct scmi_device_id *id_table) { From 9f7cd6a62aa754ed6b48cbd5d50de40add1bcc86 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 22 Jul 2026 18:35:21 +0100 Subject: [PATCH 087/157] firmware: arm_scmi: Unrequest devices if driver registration fails scmi_driver_register() requests protocol devices before registering the driver. If driver_register() fails, those requests remain in the global IDR and retain pointers to the module's ID table. Once the failed module load releases that storage, later request matching or SCMI device creation can dereference the stale pointers. Unrequest the complete protocol table before returning the registration failure. At this point table registration succeeded, so every entry is owned by the current registration attempt. Fixes: d3cd7c525fd2 ("firmware: arm_scmi: Refactor protocol device creation") Reported-by: Sashiko Link: https://patch.msgid.link/20260722173521.2184378-2-sudeep.holla@kernel.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index d520910eb251..e060edbe7e83 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -395,10 +395,14 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner, driver->driver.mod_name = mod_name; retval = driver_register(&driver->driver); - if (!retval) - pr_debug("Registered new scmi driver %s\n", driver->name); + if (retval) { + scmi_protocol_table_unregister(driver->id_table); + return retval; + } - return retval; + pr_debug("Registered new scmi driver %s\n", driver->name); + + return 0; } EXPORT_SYMBOL_GPL(scmi_driver_register); From d73e353efd3e35f0e661043b916b9ce93b6c3c72 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Thu, 23 Jul 2026 10:28:54 +0800 Subject: [PATCH 088/157] soc: xilinx: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260722023523.42269-6-panchuang@vivo.com Signed-off-by: Michal Simek --- drivers/soc/xilinx/zynqmp_power.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 370e61ac47d8..54c796afb89e 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -368,11 +368,8 @@ static int zynqmp_pm_probe(struct platform_device *pdev) IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(&pdev->dev), &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "devm_request_threaded_irq '%d' failed with %d\n", - irq, ret); + if (ret) return ret; - } } else { dev_err(&pdev->dev, "Required property not found in DT node\n"); return -ENOENT; From 01479280fa5d4691013bcd64fdfd935117b69769 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:18:51 +0800 Subject: [PATCH 089/157] soc: ixp4xx: qmgr: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Message-ID: <20260704121851.63805-1-pengpeng@iscas.ac.cn> Signed-off-by: Linus Walleij --- drivers/soc/ixp4xx/ixp4xx-qmgr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c index 475e229039e3..e3ad0e0237a7 100644 --- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c +++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c @@ -454,6 +454,7 @@ static const struct of_device_id ixp4xx_qmgr_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, ixp4xx_qmgr_of_match); static struct platform_driver ixp4xx_qmgr_driver = { .driver = { From f0b93c158aa9214f119889668e10c33f00960896 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:23:24 +0800 Subject: [PATCH 090/157] soc: ixp4xx: npe: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Message-ID: <20260704122324.80274-1-pengpeng@iscas.ac.cn> Signed-off-by: Linus Walleij --- drivers/soc/ixp4xx/ixp4xx-npe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/ixp4xx/ixp4xx-npe.c b/drivers/soc/ixp4xx/ixp4xx-npe.c index 33e2e0366f19..0b3cc471ed62 100644 --- a/drivers/soc/ixp4xx/ixp4xx-npe.c +++ b/drivers/soc/ixp4xx/ixp4xx-npe.c @@ -752,6 +752,7 @@ static const struct of_device_id ixp4xx_npe_of_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, ixp4xx_npe_of_match); static struct platform_driver ixp4xx_npe_driver = { .driver = { From 48e54749c1b6446f53f7419c50ebe7c84f81cb0b Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Wed, 22 Jul 2026 10:35:18 +0800 Subject: [PATCH 091/157] soc: ixp4xx: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Message-ID: <20260722023523.42269-2-panchuang@vivo.com> Signed-off-by: Linus Walleij --- drivers/soc/ixp4xx/ixp4xx-qmgr.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c index e3ad0e0237a7..492a31660535 100644 --- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c +++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c @@ -421,19 +421,13 @@ static int ixp4xx_qmgr_probe(struct platform_device *pdev) err = devm_request_irq(dev, irq1, handler1, 0, "IXP4xx Queue Manager", NULL); - if (err) { - dev_err(dev, "failed to request IRQ%i (%i)\n", - irq1, err); + if (err) return err; - } err = devm_request_irq(dev, irq2, handler2, 0, "IXP4xx Queue Manager", NULL); - if (err) { - dev_err(dev, "failed to request IRQ%i (%i)\n", - irq2, err); + if (err) return err; - } used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */ spin_lock_init(&qmgr_lock); From dce7afe3efe485a99200a59dc78738539cbd133f Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Mon, 20 Jul 2026 17:59:12 +0800 Subject: [PATCH 092/157] soc: ti: wkup_m3_ipc: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Hari Prasath Gujulan Elango Link: https://patch.msgid.link/20260720095920.542801-7-panchuang@vivo.com Signed-off-by: Nishanth Menon --- drivers/soc/ti/wkup_m3_ipc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c index 5845fc652adc..f8640cdc2a21 100644 --- a/drivers/soc/ti/wkup_m3_ipc.c +++ b/drivers/soc/ti/wkup_m3_ipc.c @@ -630,10 +630,8 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, wkup_m3_txev_handler, 0, "wkup_m3_txev", m3_ipc); - if (ret) { - dev_err(dev, "request_irq failed\n"); + if (ret) return ret; - } m3_ipc->mbox_client.dev = dev; m3_ipc->mbox_client.tx_done = NULL; From 96aa6902ff2bf81b9b8e0fa228494595dc2374a9 Mon Sep 17 00:00:00 2001 From: Miles Krause Date: Sat, 11 Jul 2026 22:56:14 -0400 Subject: [PATCH 093/157] memory: jz4780-nemc: Use dev_err_probe() for clock error Use dev_err_probe() to simplify clock acquisition error handling and avoid logging probe deferral as an error. No functional change. Signed-off-by: Miles Krause Link: https://patch.msgid.link/20260711-jz4780-nemc-dev-err-probe-v1-1-afb6f6a82752@gmail.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/jz4780-nemc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c index 1a8161514d03..1bf17057cbf5 100644 --- a/drivers/memory/jz4780-nemc.c +++ b/drivers/memory/jz4780-nemc.c @@ -313,10 +313,9 @@ static int jz4780_nemc_probe(struct platform_device *pdev) writel(0, nemc->base + NEMC_NFCSR); nemc->clk = devm_clk_get(dev, NULL); - if (IS_ERR(nemc->clk)) { - dev_err(dev, "failed to get clock\n"); - return PTR_ERR(nemc->clk); - } + if (IS_ERR(nemc->clk)) + return dev_err_probe(dev, PTR_ERR(nemc->clk), + "failed to get clock\n"); ret = clk_prepare_enable(nemc->clk); if (ret) { From 21d055109b2352542ac293872ca9113b42b6c55e Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Mon, 27 Jul 2026 10:45:37 +0200 Subject: [PATCH 094/157] powerpc: Move CONFIG_QE_GPIO to SoC Commit 7aa1aa6ecec2 ("QE: Move QE from arch/powerpc to drivers/soc") moved QE into drivers/soc including gpio.c but left CONFIG_QE_GPIO in powerpc's Kconfig. Move it to SoC as well as it is the only place it is used: drivers/soc/fsl/qe/Makefile:obj-$(CONFIG_QE_GPIO) += gpio.o qe_ports_ic.o include/soc/fsl/qe/qe.h:#ifdef CONFIG_QE_GPIO include/soc/fsl/qe/qe.h:#endif /* CONFIG_QE_GPIO */ There is also a dependency in USB subsystem for CONFIG_USB_FHCI_HCD: drivers/usb/host/Kconfig: depends on QE_GPIO && QUICC_ENGINE Because that USB driver selects CONFIG_FSL_GTM which is specific to powerpc, it has to depend on CONFIG_PPC. Acked-by: Madhavan Srinivasan Link: https://lore.kernel.org/r/9a7131d15a94e2017877e628f0e97f9aa2e0aae9.1785140877.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- arch/powerpc/platforms/Kconfig | 8 -------- drivers/soc/fsl/qe/Kconfig | 8 ++++++++ drivers/usb/host/Kconfig | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig index c4e61843d9d9..2f797ac6f1b3 100644 --- a/arch/powerpc/platforms/Kconfig +++ b/arch/powerpc/platforms/Kconfig @@ -228,14 +228,6 @@ config TAU_AVERAGE If in doubt, say N here. -config QE_GPIO - bool "QE GPIO support" - depends on QUICC_ENGINE - select GPIOLIB - help - Say Y here if you're going to use hardware that connects to the - QE GPIOs. - config CPM2 bool "Enable support for the CPM2 (Communications Processor Module)" depends on (FSL_SOC_BOOKE && PPC32) || PPC_82xx diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index eb03f42ab978..b35a8fd30ebf 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -67,3 +67,11 @@ config QE_USB default y if USB_FSL_QE help QE USB Controller support + +config QE_GPIO + bool "QE GPIO support" + depends on QUICC_ENGINE + select GPIOLIB + help + Say Y here if you're going to use hardware that connects to the + QE GPIOs. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index b3b1ec696bf5..10cff12a3f31 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -611,6 +611,7 @@ config USB_UHCI_ASPEED config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" depends on QE_GPIO && QUICC_ENGINE + depends on PPC select FSL_GTM select QE_USB help From 2fa28edca0f4fa1fd59ae969fca4368e12f68b59 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 1 Jul 2026 14:20:42 +0200 Subject: [PATCH 095/157] soc: mediatek: mtk-mmsys: Rework routes to specify component ID In preparation for a refactoring of multimedia related MediaTek drivers, including mmsys, mutex and mediatek-drm, rework all of the MMSYS routes to specify a hardware component instance number (or "SubID") alongside the hardware component type. This also is one step of preparation towards the removal of the catch-all mtk_ddp_comp_id enumeration and towards the migration from a predefined-coupling static hardware component IDSubID mapping (carrying around a very long enumeration and also some multiple big arrays in mediatek-drm) to a more flexible map of Component ID (Type) decoupled from Component SubID (HW Instance) as then, anyway, techniques to handle components are always the same on a type basis. Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mt8167-mmsys.h | 10 +- drivers/soc/mediatek/mt8173-mmsys.h | 28 ++--- drivers/soc/mediatek/mt8183-mmsys.h | 14 +-- drivers/soc/mediatek/mt8186-mmsys.h | 22 ++-- drivers/soc/mediatek/mt8188-mmsys.h | 78 ++++++------ drivers/soc/mediatek/mt8192-mmsys.h | 20 +-- drivers/soc/mediatek/mt8195-mmsys.h | 181 ++++++++++++++-------------- drivers/soc/mediatek/mt8365-mmsys.h | 20 +-- drivers/soc/mediatek/mtk-mmsys.h | 21 ++-- 9 files changed, 201 insertions(+), 193 deletions(-) diff --git a/drivers/soc/mediatek/mt8167-mmsys.h b/drivers/soc/mediatek/mt8167-mmsys.h index eef14083c47b..001379373507 100644 --- a/drivers/soc/mediatek/mt8167-mmsys.h +++ b/drivers/soc/mediatek/mt8167-mmsys.h @@ -19,19 +19,19 @@ #define MT8167_DSI0_SEL_IN_MASK 0x3 static const struct mtk_mmsys_routes mt8167_mmsys_routing_table[] = { - MMSYS_ROUTE(OVL0, COLOR0, + MMSYS_ROUTE(OVL, 0, COLOR, 0, MT8167_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0, OVL0_MOUT_EN_COLOR0), - MMSYS_ROUTE(DITHER0, RDMA0, + MMSYS_ROUTE(DITHER, 0, RDMA, 0, MT8167_DISP_REG_CONFIG_DISP_DITHER_MOUT_EN, MT8167_DITHER_MOUT_EN_MASK, MT8167_DITHER_MOUT_EN_RDMA0), - MMSYS_ROUTE(OVL0, COLOR0, + MMSYS_ROUTE(OVL, 0, COLOR, 0, MT8167_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0, COLOR0_SEL_IN_OVL0), - MMSYS_ROUTE(RDMA0, DSI0, + MMSYS_ROUTE(RDMA, 0, DSI, 0, MT8167_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8167_DSI0_SEL_IN_MASK, MT8167_DSI0_SEL_IN_RDMA0), - MMSYS_ROUTE(RDMA0, DSI0, + MMSYS_ROUTE(RDMA, 0, DSI, 0, MT8167_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8167_RDMA0_SOUT_MASK, MT8167_RDMA0_SOUT_DSI0), }; diff --git a/drivers/soc/mediatek/mt8173-mmsys.h b/drivers/soc/mediatek/mt8173-mmsys.h index 957876d7c166..af67879ff8b4 100644 --- a/drivers/soc/mediatek/mt8173-mmsys.h +++ b/drivers/soc/mediatek/mt8173-mmsys.h @@ -33,46 +33,46 @@ #define MT8173_RDMA0_SOUT_COLOR0 BIT(0) static const struct mtk_mmsys_routes mt8173_mmsys_routing_table[] = { - MMSYS_ROUTE(OVL0, COLOR0, + MMSYS_ROUTE(OVL, 0, COLOR, 0, MT8173_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, MT8173_OVL0_MOUT_EN_COLOR0, MT8173_OVL0_MOUT_EN_COLOR0), - MMSYS_ROUTE(OD0, RDMA0, + MMSYS_ROUTE(OD, 0, RDMA, 0, MT8173_DISP_REG_CONFIG_DISP_OD_MOUT_EN, MT8173_OD0_MOUT_EN_RDMA0, MT8173_OD0_MOUT_EN_RDMA0), - MMSYS_ROUTE(UFOE, DSI0, + MMSYS_ROUTE(UFOE, 0, DSI, 0, MT8173_DISP_REG_CONFIG_DISP_UFOE_MOUT_EN, MT8173_UFOE_MOUT_EN_DSI0, MT8173_UFOE_MOUT_EN_DSI0), - MMSYS_ROUTE(COLOR0, AAL0, + MMSYS_ROUTE(COLOR, 0, AAL, 0, MT8173_DISP_REG_CONFIG_DISP_COLOR0_SOUT_SEL_IN, MT8173_COLOR0_SOUT_MERGE, 0 /* SOUT to AAL */), - MMSYS_ROUTE(RDMA0, UFOE, + MMSYS_ROUTE(RDMA, 0, UFOE, 0, MT8173_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL_IN, MT8173_RDMA0_SOUT_COLOR0, 0 /* SOUT to UFOE */), - MMSYS_ROUTE(OVL0, COLOR0, + MMSYS_ROUTE(OVL, 0, COLOR, 0, MT8173_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, MT8173_COLOR0_SEL_IN_OVL0, MT8173_COLOR0_SEL_IN_OVL0), - MMSYS_ROUTE(AAL0, COLOR0, + MMSYS_ROUTE(AAL, 0, COLOR, 0, MT8173_DISP_REG_CONFIG_DISP_AAL_SEL_IN, MT8173_AAL_SEL_IN_MERGE, 0 /* SEL_IN from COLOR0 */), - MMSYS_ROUTE(RDMA0, UFOE, + MMSYS_ROUTE(RDMA, 0, UFOE, 0, MT8173_DISP_REG_CONFIG_DISP_UFOE_SEL_IN, MT8173_UFOE_SEL_IN_RDMA0, 0 /* SEL_IN from RDMA0 */), - MMSYS_ROUTE(UFOE, DSI0, + MMSYS_ROUTE(UFOE, 0, DSI, 0, MT8173_DISP_REG_CONFIG_DSI0_SEL_IN, MT8173_DSI0_SEL_IN_UFOE, 0 /* SEL_IN from UFOE */), - MMSYS_ROUTE(OVL1, COLOR1, + MMSYS_ROUTE(OVL, 1, COLOR, 1, MT8173_DISP_REG_CONFIG_DISP_OVL1_MOUT_EN, MT8173_OVL1_MOUT_EN_COLOR1, MT8173_OVL1_MOUT_EN_COLOR1), - MMSYS_ROUTE(GAMMA, RDMA1, + MMSYS_ROUTE(GAMMA, 0, RDMA, 1, MT8173_DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN, MT8173_GAMMA_MOUT_EN_RDMA1, MT8173_GAMMA_MOUT_EN_RDMA1), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8173_DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, RDMA1_SOUT_DPI0), - MMSYS_ROUTE(OVL1, COLOR1, + MMSYS_ROUTE(OVL, 1, COLOR, 1, MT8173_DISP_REG_CONFIG_DISP_COLOR1_SEL_IN, COLOR1_SEL_IN_OVL1, COLOR1_SEL_IN_OVL1), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8173_DISP_REG_CONFIG_DPI_SEL_IN, MT8173_DPI0_SEL_IN_MASK, MT8173_DPI0_SEL_IN_RDMA1), }; diff --git a/drivers/soc/mediatek/mt8183-mmsys.h b/drivers/soc/mediatek/mt8183-mmsys.h index 123384958c4b..cf221ef203d2 100644 --- a/drivers/soc/mediatek/mt8183-mmsys.h +++ b/drivers/soc/mediatek/mt8183-mmsys.h @@ -28,25 +28,25 @@ #define MT8183_MMSYS_SW0_RST_B 0x140 static const struct mtk_mmsys_routes mmsys_mt8183_routing_table[] = { - MMSYS_ROUTE(OVL0, OVL_2L0, + MMSYS_ROUTE(OVL, 0, OVL_2L, 0, MT8183_DISP_OVL0_MOUT_EN, MT8183_OVL0_MOUT_EN_OVL0_2L, MT8183_OVL0_MOUT_EN_OVL0_2L), - MMSYS_ROUTE(OVL_2L0, RDMA0, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 0, MT8183_DISP_OVL0_2L_MOUT_EN, MT8183_OVL0_2L_MOUT_EN_DISP_PATH0, MT8183_OVL0_2L_MOUT_EN_DISP_PATH0), - MMSYS_ROUTE(OVL_2L1, RDMA1, + MMSYS_ROUTE(OVL_2L, 1, RDMA, 1, MT8183_DISP_OVL1_2L_MOUT_EN, MT8183_OVL1_2L_MOUT_EN_RDMA1, MT8183_OVL1_2L_MOUT_EN_RDMA1), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8183_DISP_DITHER0_MOUT_EN, MT8183_DITHER0_MOUT_IN_DSI0, MT8183_DITHER0_MOUT_IN_DSI0), - MMSYS_ROUTE(OVL_2L0, RDMA0, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 0, MT8183_DISP_PATH0_SEL_IN, MT8183_DISP_PATH0_SEL_IN_OVL0_2L, MT8183_DISP_PATH0_SEL_IN_OVL0_2L), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8183_DISP_DPI0_SEL_IN, MT8183_DPI0_SEL_IN_RDMA1, MT8183_DPI0_SEL_IN_RDMA1), - MMSYS_ROUTE(RDMA0, COLOR0, + MMSYS_ROUTE(RDMA, 0, COLOR, 0, MT8183_DISP_RDMA0_SOUT_SEL_IN, MT8183_RDMA0_SOUT_COLOR0, MT8183_RDMA0_SOUT_COLOR0), }; diff --git a/drivers/soc/mediatek/mt8186-mmsys.h b/drivers/soc/mediatek/mt8186-mmsys.h index 354664be72bd..0c6941be6fa5 100644 --- a/drivers/soc/mediatek/mt8186-mmsys.h +++ b/drivers/soc/mediatek/mt8186-mmsys.h @@ -63,37 +63,37 @@ #define MT8186_MMSYS_SW0_RST_B 0x160 static const struct mtk_mmsys_routes mmsys_mt8186_routing_table[] = { - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8186_DISP_OVL0_MOUT_EN, MT8186_OVL0_MOUT_EN_MASK, MT8186_OVL0_MOUT_TO_RDMA0), - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8186_DISP_RDMA0_SEL_IN, MT8186_RDMA0_SEL_IN_MASK, MT8186_RDMA0_FROM_OVL0), - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8186_MMSYS_OVL_CON, MT8186_MMSYS_OVL0_CON_MASK, MT8186_OVL0_GO_BLEND), - MMSYS_ROUTE(RDMA0, COLOR0, + MMSYS_ROUTE(RDMA, 0, COLOR, 0, MT8186_DISP_RDMA0_SOUT_SEL, MT8186_RDMA0_SOUT_SEL_MASK, MT8186_RDMA0_SOUT_TO_COLOR0), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8186_DISP_DITHER0_MOUT_EN, MT8186_DITHER0_MOUT_EN_MASK, MT8186_DITHER0_MOUT_TO_DSI0), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8186_DISP_DSI0_SEL_IN, MT8186_DSI0_SEL_IN_MASK, MT8186_DSI0_FROM_DITHER0), - MMSYS_ROUTE(OVL_2L0, RDMA1, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 1, MT8186_DISP_OVL0_2L_MOUT_EN, MT8186_OVL0_2L_MOUT_EN_MASK, MT8186_OVL0_2L_MOUT_TO_RDMA1), - MMSYS_ROUTE(OVL_2L0, RDMA1, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 1, MT8186_DISP_RDMA1_SEL_IN, MT8186_RDMA1_SEL_IN_MASK, MT8186_RDMA1_FROM_OVL0_2L), - MMSYS_ROUTE(OVL_2L0, RDMA1, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 1, MT8186_MMSYS_OVL_CON, MT8186_MMSYS_OVL0_2L_CON_MASK, MT8186_OVL0_2L_GO_BLEND), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8186_DISP_RDMA1_MOUT_EN, MT8186_RDMA1_MOUT_EN_MASK, MT8186_RDMA1_MOUT_TO_DPI0_SEL), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8186_DISP_DPI0_SEL_IN, MT8186_DPI0_SEL_IN_MASK, MT8186_DPI0_FROM_RDMA1), }; diff --git a/drivers/soc/mediatek/mt8188-mmsys.h b/drivers/soc/mediatek/mt8188-mmsys.h index 99080afead7e..c70c4b462381 100644 --- a/drivers/soc/mediatek/mt8188-mmsys.h +++ b/drivers/soc/mediatek/mt8188-mmsys.h @@ -202,124 +202,124 @@ static const u8 mmsys_mt8188_vdo1_rst_tb[] = { }; static const struct mtk_mmsys_routes mmsys_mt8188_routing_table[] = { - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8188_VDO0_OVL_MOUT_EN, MT8188_MOUT_DISP_OVL0_TO_DISP_RDMA0, MT8188_MOUT_DISP_OVL0_TO_DISP_RDMA0), - MMSYS_ROUTE(OVL0, WDMA0, + MMSYS_ROUTE(OVL, 0, WDMA, 0, MT8188_VDO0_OVL_MOUT_EN, MT8188_MOUT_DISP_OVL0_TO_DISP_WDMA0, MT8188_MOUT_DISP_OVL0_TO_DISP_WDMA0), - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8188_VDO0_DISP_RDMA_SEL, MT8188_SEL_IN_DISP_RDMA0_FROM_MASK, MT8188_SEL_IN_DISP_RDMA0_FROM_DISP_OVL0), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8188_VDO0_DSI0_SEL_IN, MT8188_SEL_IN_DSI0_FROM_MASK, MT8188_SEL_IN_DSI0_FROM_DISP_DITHER0), - MMSYS_ROUTE(DITHER0, MERGE0, + MMSYS_ROUTE(DITHER, 0, MERGE, 0, MT8188_VDO0_VPP_MERGE_SEL, MT8188_SEL_IN_VPP_MERGE_FROM_MASK, MT8188_SEL_IN_DP_INTF0_FROM_DISP_DITHER0), - MMSYS_ROUTE(DITHER0, DSC0, + MMSYS_ROUTE(DITHER, 0, DSC, 0, MT8188_VDO0_DSC_WARP_SEL, MT8188_SEL_IN_DSC_WRAP0C0_IN_FROM_MASK, MT8188_SEL_IN_DSC_WRAP0C0_IN_FROM_DISP_DITHER0), - MMSYS_ROUTE(DITHER0, DP_INTF0, + MMSYS_ROUTE(DITHER, 0, DP_INTF, 0, MT8188_VDO0_DP_INTF0_SEL_IN, MT8188_SEL_IN_DP_INTF0_FROM_MASK, MT8188_SEL_IN_DP_INTF0_FROM_DISP_DITHER0), - MMSYS_ROUTE(DSC0, MERGE0, + MMSYS_ROUTE(DSC, 0, MERGE, 0, MT8188_VDO0_VPP_MERGE_SEL, MT8188_SEL_IN_VPP_MERGE_FROM_MASK, MT8188_SEL_IN_VPP_MERGE_FROM_DSC_WRAP0_OUT), - MMSYS_ROUTE(MERGE0, DP_INTF0, + MMSYS_ROUTE(MERGE, 0, DP_INTF, 0, MT8188_VDO0_DP_INTF0_SEL_IN, MT8188_SEL_IN_DP_INTF0_FROM_MASK, MT8188_SEL_IN_DP_INTF0_FROM_VPP_MERGE), - MMSYS_ROUTE(DSC0, DSI0, + MMSYS_ROUTE(DSC, 0, DSI, 0, MT8188_VDO0_DSI0_SEL_IN, MT8188_SEL_IN_DSI0_FROM_MASK, MT8188_SEL_IN_DSI0_FROM_DSC_WRAP0_OUT), - MMSYS_ROUTE(RDMA0, COLOR0, + MMSYS_ROUTE(RDMA, 0, COLOR, 0, MT8188_VDO0_DISP_RDMA_SEL, GENMASK(1, 0), MT8188_SOUT_DISP_RDMA0_TO_DISP_COLOR0), - MMSYS_ROUTE(DITHER0, DSC0, + MMSYS_ROUTE(DITHER, 0, DSC, 0, MT8188_VDO0_DISP_DITHER0_SEL_OUT, MT8188_SOUT_DISP_DITHER0_TO_MASK, MT8188_SOUT_DISP_DITHER0_TO_DSC_WRAP0_IN), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8188_VDO0_DISP_DITHER0_SEL_OUT, MT8188_SOUT_DISP_DITHER0_TO_MASK, MT8188_SOUT_DISP_DITHER0_TO_DSI0), - MMSYS_ROUTE(DITHER0, MERGE0, + MMSYS_ROUTE(DITHER, 0, MERGE, 0, MT8188_VDO0_DISP_DITHER0_SEL_OUT, MT8188_SOUT_DISP_DITHER0_TO_MASK, MT8188_SOUT_DISP_DITHER0_TO_VPP_MERGE0), - MMSYS_ROUTE(DITHER0, DP_INTF0, + MMSYS_ROUTE(DITHER, 0, DP_INTF, 0, MT8188_VDO0_DISP_DITHER0_SEL_OUT, MT8188_SOUT_DISP_DITHER0_TO_MASK, MT8188_SOUT_DISP_DITHER0_TO_DP_INTF0), - MMSYS_ROUTE(MERGE0, DP_INTF0, + MMSYS_ROUTE(MERGE, 0, DP_INTF, 0, MT8188_VDO0_VPP_MERGE_SEL, MT8188_SOUT_VPP_MERGE_TO_MASK, MT8188_SOUT_VPP_MERGE_TO_DP_INTF0), - MMSYS_ROUTE(MERGE0, DPI0, + MMSYS_ROUTE(MERGE, 0, DPI, 0, MT8188_VDO0_VPP_MERGE_SEL, MT8188_SOUT_VPP_MERGE_TO_MASK, MT8188_SOUT_VPP_MERGE_TO_SINA_VIRTUAL0), - MMSYS_ROUTE(MERGE0, WDMA0, + MMSYS_ROUTE(MERGE, 0, WDMA, 0, MT8188_VDO0_VPP_MERGE_SEL, MT8188_SOUT_VPP_MERGE_TO_MASK, MT8188_SOUT_VPP_MERGE_TO_DISP_WDMA0), - MMSYS_ROUTE(MERGE0, DSC0, + MMSYS_ROUTE(MERGE, 0, DSC, 0, MT8188_VDO0_VPP_MERGE_SEL, MT8188_SOUT_VPP_MERGE_TO_MASK, MT8188_SOUT_VPP_MERGE_TO_DSC_WRAP0_IN), - MMSYS_ROUTE(DSC0, DSI0, + MMSYS_ROUTE(DSC, 0, DSI, 0, MT8188_VDO0_DSC_WARP_SEL, MT8188_SOUT_DSC_WRAP0_OUT_TO_MASK, MT8188_SOUT_DSC_WRAP0_OUT_TO_DSI0), - MMSYS_ROUTE(DSC0, MERGE0, + MMSYS_ROUTE(DSC, 0, MERGE, 0, MT8188_VDO0_DSC_WARP_SEL, MT8188_SOUT_DSC_WRAP0_OUT_TO_MASK, MT8188_SOUT_DSC_WRAP0_OUT_TO_VPP_MERGE), }; static const struct mtk_mmsys_routes mmsys_mt8188_vdo1_routing_table[] = { - MMSYS_ROUTE(MDP_RDMA0, MERGE1, + MMSYS_ROUTE(MDP_RDMA, 0, MERGE, 1, MT8188_VDO1_VPP_MERGE0_P0_SEL_IN, GENMASK(0, 0), MT8188_VPP_MERGE0_P0_SEL_IN_FROM_MDP_RDMA0), - MMSYS_ROUTE(MDP_RDMA1, MERGE1, + MMSYS_ROUTE(MDP_RDMA, 1, MERGE, 1, MT8188_VDO1_VPP_MERGE0_P1_SEL_IN, GENMASK(0, 0), MT8188_VPP_MERGE0_P1_SEL_IN_FROM_MDP_RDMA1), - MMSYS_ROUTE(MDP_RDMA2, MERGE2, + MMSYS_ROUTE(MDP_RDMA, 2, MERGE, 2, MT8188_VDO1_VPP_MERGE1_P0_SEL_IN, GENMASK(0, 0), MT8188_VPP_MERGE1_P0_SEL_IN_FROM_MDP_RDMA2), - MMSYS_ROUTE(MERGE1, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 1, ETHDR_MIXER, 0, MT8188_VDO1_MERGE0_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8188_SOUT_TO_MIXER_IN1_SEL), - MMSYS_ROUTE(MERGE2, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 2, ETHDR_MIXER, 0, MT8188_VDO1_MERGE1_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8188_SOUT_TO_MIXER_IN2_SEL), - MMSYS_ROUTE(MERGE3, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 3, ETHDR_MIXER, 0, MT8188_VDO1_MERGE2_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8188_SOUT_TO_MIXER_IN3_SEL), - MMSYS_ROUTE(MERGE4, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 4, ETHDR_MIXER, 0, MT8188_VDO1_MERGE3_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8188_SOUT_TO_MIXER_IN4_SEL), - MMSYS_ROUTE(ETHDR_MIXER, MERGE5, + MMSYS_ROUTE(ETHDR_MIXER, 0, MERGE, 5, MT8188_VDO1_MIXER_OUT_SOUT_SEL, GENMASK(0, 0), MT8188_MIXER_SOUT_TO_MERGE4_ASYNC_SEL), - MMSYS_ROUTE(MERGE1, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 1, ETHDR_MIXER, 0, MT8188_VDO1_MIXER_IN1_SEL_IN, GENMASK(0, 0), MT8188_MIXER_IN1_SEL_IN_FROM_MERGE0_ASYNC_SOUT), - MMSYS_ROUTE(MERGE2, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 2, ETHDR_MIXER, 0, MT8188_VDO1_MIXER_IN2_SEL_IN, GENMASK(0, 0), MT8188_MIXER_IN2_SEL_IN_FROM_MERGE1_ASYNC_SOUT), - MMSYS_ROUTE(MERGE3, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 3, ETHDR_MIXER, 0, MT8188_VDO1_MIXER_IN3_SEL_IN, GENMASK(0, 0), MT8188_MIXER_IN3_SEL_IN_FROM_MERGE2_ASYNC_SOUT), - MMSYS_ROUTE(MERGE4, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 4, ETHDR_MIXER, 0, MT8188_VDO1_MIXER_IN4_SEL_IN, GENMASK(0, 0), MT8188_MIXER_IN4_SEL_IN_FROM_MERGE3_ASYNC_SOUT), - MMSYS_ROUTE(ETHDR_MIXER, MERGE5, + MMSYS_ROUTE(ETHDR_MIXER, 0, MERGE, 5, MT8188_VDO1_MIXER_SOUT_SEL_IN, GENMASK(2, 0), MT8188_MIXER_SOUT_SEL_IN_FROM_DISP_MIXER), - MMSYS_ROUTE(ETHDR_MIXER, MERGE5, + MMSYS_ROUTE(ETHDR_MIXER, 0, MERGE, 5, MT8188_VDO1_MERGE4_ASYNC_SEL_IN, GENMASK(2, 0), MT8188_MERGE4_ASYNC_SEL_IN_FROM_MIXER_OUT_SOUT), - MMSYS_ROUTE(MERGE5, DPI1, + MMSYS_ROUTE(MERGE, 5, DPI, 1, MT8188_VDO1_DISP_DPI1_SEL_IN, GENMASK(1, 0), MT8188_DISP_DPI1_SEL_IN_FROM_VPP_MERGE4_MOUT), - MMSYS_ROUTE(MERGE5, DPI1, + MMSYS_ROUTE(MERGE, 5, DPI, 1, MT8188_VDO1_MERGE4_SOUT_SEL, GENMASK(3, 0), MT8188_MERGE4_SOUT_TO_DPI1_SEL), - MMSYS_ROUTE(MERGE5, DP_INTF1, + MMSYS_ROUTE(MERGE, 5, DP_INTF, 1, MT8188_VDO1_DISP_DP_INTF0_SEL_IN, GENMASK(1, 0), MT8188_DISP_DP_INTF0_SEL_IN_FROM_VPP_MERGE4_MOUT), - MMSYS_ROUTE(MERGE5, DP_INTF1, + MMSYS_ROUTE(MERGE, 5, DP_INTF, 1, MT8188_VDO1_MERGE4_SOUT_SEL, GENMASK(3, 0), MT8188_MERGE4_SOUT_TO_DP_INTF0_SEL), }; diff --git a/drivers/soc/mediatek/mt8192-mmsys.h b/drivers/soc/mediatek/mt8192-mmsys.h index 7cafa2455fd0..37ced5152ba7 100644 --- a/drivers/soc/mediatek/mt8192-mmsys.h +++ b/drivers/soc/mediatek/mt8192-mmsys.h @@ -31,34 +31,34 @@ #define MT8192_DSI0_SEL_IN_DITHER0 0x1 static const struct mtk_mmsys_routes mmsys_mt8192_routing_table[] = { - MMSYS_ROUTE(OVL_2L0, RDMA0, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 0, MT8192_DISP_OVL0_2L_MOUT_EN, MT8192_OVL0_MOUT_EN_DISP_RDMA0, MT8192_OVL0_MOUT_EN_DISP_RDMA0), - MMSYS_ROUTE(OVL_2L2, RDMA4, + MMSYS_ROUTE(OVL_2L, 2, RDMA, 4, MT8192_DISP_OVL2_2L_MOUT_EN, MT8192_OVL2_2L_MOUT_EN_RDMA4, MT8192_OVL2_2L_MOUT_EN_RDMA4), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8192_DISP_DITHER0_MOUT_EN, MT8192_DITHER0_MOUT_IN_DSI0, MT8192_DITHER0_MOUT_IN_DSI0), - MMSYS_ROUTE(OVL_2L0, RDMA0, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 0, MT8192_DISP_RDMA0_SEL_IN, MT8192_RDMA0_SEL_IN_OVL0_2L, MT8192_RDMA0_SEL_IN_OVL0_2L), - MMSYS_ROUTE(CCORR, AAL0, + MMSYS_ROUTE(CCORR, 0, AAL, 0, MT8192_DISP_AAL0_SEL_IN, MT8192_AAL0_SEL_IN_CCORR0, MT8192_AAL0_SEL_IN_CCORR0), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8192_DISP_DSI0_SEL_IN, MT8192_DSI0_SEL_IN_DITHER0, MT8192_DSI0_SEL_IN_DITHER0), - MMSYS_ROUTE(RDMA0, COLOR0, + MMSYS_ROUTE(RDMA, 0, COLOR, 0, MT8192_DISP_RDMA0_SOUT_SEL, MT8192_RDMA0_SOUT_COLOR0, MT8192_RDMA0_SOUT_COLOR0), - MMSYS_ROUTE(CCORR, AAL0, + MMSYS_ROUTE(CCORR, 0, AAL, 0, MT8192_DISP_CCORR0_SOUT_SEL, MT8192_CCORR0_SOUT_AAL0, MT8192_CCORR0_SOUT_AAL0), - MMSYS_ROUTE(OVL0, OVL_2L0, + MMSYS_ROUTE(OVL, 0, OVL_2L, 0, MT8192_MMSYS_OVL_MOUT_EN, MT8192_DISP_OVL0_GO_BG, MT8192_DISP_OVL0_GO_BG), - MMSYS_ROUTE(OVL_2L0, RDMA0, + MMSYS_ROUTE(OVL_2L, 0, RDMA, 0, MT8192_MMSYS_OVL_MOUT_EN, MT8192_DISP_OVL0_2L_GO_BLEND, MT8192_DISP_OVL0_2L_GO_BLEND), }; diff --git a/drivers/soc/mediatek/mt8195-mmsys.h b/drivers/soc/mediatek/mt8195-mmsys.h index f69929a2a4d4..3a58b9b74282 100644 --- a/drivers/soc/mediatek/mt8195-mmsys.h +++ b/drivers/soc/mediatek/mt8195-mmsys.h @@ -160,278 +160,279 @@ #define MT8195_SVPP3_MDP_RSZ BIT(5) static const struct mtk_mmsys_routes mmsys_mt8195_routing_table[] = { - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8195_VDO0_OVL_MOUT_EN, MT8195_MOUT_DISP_OVL0_TO_DISP_RDMA0, MT8195_MOUT_DISP_OVL0_TO_DISP_RDMA0), - MMSYS_ROUTE(OVL0, WDMA0, + MMSYS_ROUTE(OVL, 0, WDMA, 0, MT8195_VDO0_OVL_MOUT_EN, MT8195_MOUT_DISP_OVL0_TO_DISP_WDMA0, MT8195_MOUT_DISP_OVL0_TO_DISP_WDMA0), - MMSYS_ROUTE(OVL0, OVL1, + MMSYS_ROUTE(OVL, 0, OVL, 1, MT8195_VDO0_OVL_MOUT_EN, MT8195_MOUT_DISP_OVL0_TO_DISP_OVL1, MT8195_MOUT_DISP_OVL0_TO_DISP_OVL1), - MMSYS_ROUTE(OVL1, RDMA1, + MMSYS_ROUTE(OVL, 1, RDMA, 1, MT8195_VDO0_OVL_MOUT_EN, MT8195_MOUT_DISP_OVL1_TO_DISP_RDMA1, MT8195_MOUT_DISP_OVL1_TO_DISP_RDMA1), - MMSYS_ROUTE(OVL1, WDMA1, + MMSYS_ROUTE(OVL, 1, WDMA, 1, MT8195_VDO0_OVL_MOUT_EN, MT8195_MOUT_DISP_OVL1_TO_DISP_WDMA1, MT8195_MOUT_DISP_OVL1_TO_DISP_WDMA1), - MMSYS_ROUTE(OVL1, OVL0, + MMSYS_ROUTE(OVL, 1, OVL, 0, MT8195_VDO0_OVL_MOUT_EN, MT8195_MOUT_DISP_OVL1_TO_DISP_OVL0, MT8195_MOUT_DISP_OVL1_TO_DISP_OVL0), - MMSYS_ROUTE(DSC0, MERGE0, + MMSYS_ROUTE(DSC, 0, MERGE, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_VPP_MERGE_FROM_MASK, MT8195_SEL_IN_VPP_MERGE_FROM_DSC_WRAP0_OUT), - MMSYS_ROUTE(DITHER1, MERGE0, + MMSYS_ROUTE(DITHER, 1, MERGE, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_VPP_MERGE_FROM_MASK, MT8195_SEL_IN_VPP_MERGE_FROM_DISP_DITHER1), - MMSYS_ROUTE(MERGE5, MERGE0, + MMSYS_ROUTE(MERGE, 5, MERGE, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_VPP_MERGE_FROM_MASK, MT8195_SEL_IN_VPP_MERGE_FROM_VDO1_VIRTUAL0), - MMSYS_ROUTE(DITHER0, DSC0, + MMSYS_ROUTE(DITHER, 0, DSC, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP0_IN_FROM_MASK, MT8195_SEL_IN_DSC_WRAP0_IN_FROM_DISP_DITHER0), - MMSYS_ROUTE(MERGE0, DSC0, + MMSYS_ROUTE(MERGE, 0, DSC, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP0_IN_FROM_MASK, MT8195_SEL_IN_DSC_WRAP0_IN_FROM_VPP_MERGE), - MMSYS_ROUTE(DITHER1, DSC1, + MMSYS_ROUTE(DITHER, 1, DSC, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_IN_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_IN_FROM_DISP_DITHER1), - MMSYS_ROUTE(MERGE0, DSC1, + MMSYS_ROUTE(MERGE, 0, DSC, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_IN_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_IN_FROM_VPP_MERGE), - MMSYS_ROUTE(MERGE0, DP_INTF1, + MMSYS_ROUTE(MERGE, 0, DP_INTF, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_VPP_MERGE), - MMSYS_ROUTE(MERGE0, DPI0, + MMSYS_ROUTE(MERGE, 0, DPI, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_VPP_MERGE), - MMSYS_ROUTE(MERGE0, DPI1, + MMSYS_ROUTE(MERGE, 0, DPI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_VPP_MERGE), - MMSYS_ROUTE(DSC1, DP_INTF1, + MMSYS_ROUTE(DSC, 1, DP_INTF, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_DSC_WRAP1_OUT), - MMSYS_ROUTE(DSC1, DPI0, + MMSYS_ROUTE(DSC, 1, DPI, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_DSC_WRAP1_OUT), - MMSYS_ROUTE(DSC1, DPI1, + MMSYS_ROUTE(DSC, 1, DPI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINA_VIRTUAL0_FROM_DSC_WRAP1_OUT), - MMSYS_ROUTE(DSC0, DP_INTF1, + MMSYS_ROUTE(DSC, 0, DP_INTF, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINB_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINB_VIRTUAL0_FROM_DSC_WRAP0_OUT), - MMSYS_ROUTE(DSC0, DPI0, + MMSYS_ROUTE(DSC, 0, DPI, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINB_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINB_VIRTUAL0_FROM_DSC_WRAP0_OUT), - MMSYS_ROUTE(DSC0, DPI1, + MMSYS_ROUTE(DSC, 0, DPI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_SINB_VIRTUAL0_FROM_MASK, MT8195_SEL_IN_SINB_VIRTUAL0_FROM_DSC_WRAP0_OUT), - MMSYS_ROUTE(DSC1, DP_INTF0, + MMSYS_ROUTE(DSC, 1, DP_INTF, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DP_INTF0_FROM_MASK, MT8195_SEL_IN_DP_INTF0_FROM_DSC_WRAP1_OUT), - MMSYS_ROUTE(MERGE0, DP_INTF0, + MMSYS_ROUTE(MERGE, 0, DP_INTF, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DP_INTF0_FROM_MASK, MT8195_SEL_IN_DP_INTF0_FROM_VPP_MERGE), - MMSYS_ROUTE(MERGE5, DP_INTF0, + MMSYS_ROUTE(MERGE, 5, DP_INTF, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DP_INTF0_FROM_MASK, MT8195_SEL_IN_DP_INTF0_FROM_VDO1_VIRTUAL0), - MMSYS_ROUTE(DSC0, DSI0, + MMSYS_ROUTE(DSC, 0, DSI, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSI0_FROM_MASK, MT8195_SEL_IN_DSI0_FROM_DSC_WRAP0_OUT), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSI0_FROM_MASK, MT8195_SEL_IN_DSI0_FROM_DISP_DITHER0), - MMSYS_ROUTE(DSC1, DSI1, + MMSYS_ROUTE(DSC, 1, DSI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSI1_FROM_MASK, MT8195_SEL_IN_DSI1_FROM_DSC_WRAP1_OUT), - MMSYS_ROUTE(MERGE0, DSI1, + MMSYS_ROUTE(MERGE, 0, DSI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSI1_FROM_MASK, MT8195_SEL_IN_DSI1_FROM_VPP_MERGE), - MMSYS_ROUTE(OVL1, WDMA1, + MMSYS_ROUTE(OVL, 1, WDMA, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DISP_WDMA1_FROM_MASK, MT8195_SEL_IN_DISP_WDMA1_FROM_DISP_OVL1), - MMSYS_ROUTE(MERGE0, WDMA1, + MMSYS_ROUTE(MERGE, 0, WDMA, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DISP_WDMA1_FROM_MASK, MT8195_SEL_IN_DISP_WDMA1_FROM_VPP_MERGE), - MMSYS_ROUTE(DSC1, DSI1, + MMSYS_ROUTE(DSC, 1, DSI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DSC_WRAP1_IN), - MMSYS_ROUTE(DSC1, DP_INTF0, + MMSYS_ROUTE(DSC, 1, DP_INTF, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DSC_WRAP1_IN), - MMSYS_ROUTE(DSC1, DP_INTF1, + MMSYS_ROUTE(DSC, 1, DP_INTF, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DSC_WRAP1_IN), - MMSYS_ROUTE(DSC1, DPI0, + MMSYS_ROUTE(DSC, 1, DPI, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DSC_WRAP1_IN), - MMSYS_ROUTE(DSC1, DPI1, + MMSYS_ROUTE(DSC, 1, DPI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DSC_WRAP1_IN), - MMSYS_ROUTE(DSC1, MERGE0, + MMSYS_ROUTE(DSC, 1, MERGE, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DSC_WRAP1_IN), - MMSYS_ROUTE(DITHER1, DSI1, + MMSYS_ROUTE(DITHER, 1, DSI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DISP_DITHER1), - MMSYS_ROUTE(DITHER1, DP_INTF0, + MMSYS_ROUTE(DITHER, 1, DP_INTF, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DISP_DITHER1), - MMSYS_ROUTE(DITHER1, DPI0, + MMSYS_ROUTE(DITHER, 1, DPI, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DISP_DITHER1), - MMSYS_ROUTE(DITHER1, DPI1, + MMSYS_ROUTE(DITHER, 1, DPI, 1, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DSC_WRAP1_FROM_MASK, MT8195_SEL_IN_DSC_WRAP1_OUT_FROM_DISP_DITHER1), - MMSYS_ROUTE(OVL0, WDMA0, + MMSYS_ROUTE(OVL, 0, WDMA, 0, MT8195_VDO0_SEL_IN, MT8195_SEL_IN_DISP_WDMA0_FROM_MASK, MT8195_SEL_IN_DISP_WDMA0_FROM_DISP_OVL0), - MMSYS_ROUTE(DITHER0, DSC0, + MMSYS_ROUTE(DITHER, 0, DSC, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER0_TO_MASK, MT8195_SOUT_DISP_DITHER0_TO_DSC_WRAP0_IN), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER0_TO_MASK, MT8195_SOUT_DISP_DITHER0_TO_DSI0), - MMSYS_ROUTE(DITHER1, DSC1, + MMSYS_ROUTE(DITHER, 1, DSC, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER1_TO_MASK, MT8195_SOUT_DISP_DITHER1_TO_DSC_WRAP1_IN), - MMSYS_ROUTE(DITHER1, MERGE0, + MMSYS_ROUTE(DITHER, 1, MERGE, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER1_TO_MASK, MT8195_SOUT_DISP_DITHER1_TO_VPP_MERGE), - MMSYS_ROUTE(DITHER1, DSI1, + MMSYS_ROUTE(DITHER, 1, DSI, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER1_TO_MASK, MT8195_SOUT_DISP_DITHER1_TO_DSC_WRAP1_OUT), - MMSYS_ROUTE(DITHER1, DP_INTF0, + MMSYS_ROUTE(DITHER, 1, DP_INTF, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER1_TO_MASK, MT8195_SOUT_DISP_DITHER1_TO_DSC_WRAP1_OUT), - MMSYS_ROUTE(DITHER1, DP_INTF1, + MMSYS_ROUTE(DITHER, 1, DP_INTF, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER1_TO_MASK, MT8195_SOUT_DISP_DITHER1_TO_DSC_WRAP1_OUT), - MMSYS_ROUTE(DITHER1, DPI0, + MMSYS_ROUTE(DITHER, 1, DPI, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER1_TO_MASK, MT8195_SOUT_DISP_DITHER1_TO_DSC_WRAP1_OUT), - MMSYS_ROUTE(DITHER1, DPI1, + MMSYS_ROUTE(DITHER, 1, DPI, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DISP_DITHER1_TO_MASK, MT8195_SOUT_DISP_DITHER1_TO_DSC_WRAP1_OUT), - MMSYS_ROUTE(MERGE5, MERGE0, + MMSYS_ROUTE(MERGE, 5, MERGE, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VDO1_VIRTUAL0_TO_MASK, MT8195_SOUT_VDO1_VIRTUAL0_TO_VPP_MERGE), - MMSYS_ROUTE(MERGE5, DP_INTF0, + MMSYS_ROUTE(MERGE, 5, DP_INTF, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VDO1_VIRTUAL0_TO_MASK, MT8195_SOUT_VDO1_VIRTUAL0_TO_DP_INTF0), - MMSYS_ROUTE(MERGE0, DSI1, + MMSYS_ROUTE(MERGE, 0, DSI, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_MASK, MT8195_SOUT_VPP_MERGE_TO_DSI1), - MMSYS_ROUTE(MERGE0, DP_INTF0, + MMSYS_ROUTE(MERGE, 0, DP_INTF, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_MASK, MT8195_SOUT_VPP_MERGE_TO_DP_INTF0), - MMSYS_ROUTE(MERGE0, DP_INTF1, + MMSYS_ROUTE(MERGE, 0, DP_INTF, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_MASK, MT8195_SOUT_VPP_MERGE_TO_SINA_VIRTUAL0), - MMSYS_ROUTE(MERGE0, DPI0, + MMSYS_ROUTE(MERGE, 0, DPI, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_MASK, MT8195_SOUT_VPP_MERGE_TO_SINA_VIRTUAL0), - MMSYS_ROUTE(MERGE0, DPI1, + MMSYS_ROUTE(MERGE, 0, DPI, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_MASK, MT8195_SOUT_VPP_MERGE_TO_SINA_VIRTUAL0), - MMSYS_ROUTE(MERGE0, WDMA1, + MMSYS_ROUTE(MERGE, 0, WDMA, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_MASK, MT8195_SOUT_VPP_MERGE_TO_DISP_WDMA1), - MMSYS_ROUTE(MERGE0, DSC0, + MMSYS_ROUTE(MERGE, 0, DSC, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_MASK, MT8195_SOUT_VPP_MERGE_TO_DSC_WRAP0_IN), - MMSYS_ROUTE(MERGE0, DSC1, + MMSYS_ROUTE(MERGE, 0, DSC, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_VPP_MERGE_TO_DSC_WRAP1_IN_MASK, MT8195_SOUT_VPP_MERGE_TO_DSC_WRAP1_IN), - MMSYS_ROUTE(DSC0, DSI0, + MMSYS_ROUTE(DSC, 0, DSI, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP0_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP0_OUT_TO_DSI0), - MMSYS_ROUTE(DSC0, DP_INTF1, + MMSYS_ROUTE(DSC, 0, DP_INTF, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP0_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP0_OUT_TO_SINB_VIRTUAL0), - MMSYS_ROUTE(DSC0, DPI0, + MMSYS_ROUTE(DSC, 0, DPI, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP0_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP0_OUT_TO_SINB_VIRTUAL0), - MMSYS_ROUTE(DSC0, DPI1, + MMSYS_ROUTE(DSC, 0, DPI, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP0_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP0_OUT_TO_SINB_VIRTUAL0), - MMSYS_ROUTE(DSC0, MERGE0, + MMSYS_ROUTE(DSC, 0, MERGE, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP0_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP0_OUT_TO_VPP_MERGE), - MMSYS_ROUTE(DSC1, DSI1, + MMSYS_ROUTE(DSC, 1, DSI, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP1_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP1_OUT_TO_DSI1), - MMSYS_ROUTE(DSC1, DP_INTF0, + MMSYS_ROUTE(DSC, 1, DP_INTF, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP1_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP1_OUT_TO_DP_INTF0), - MMSYS_ROUTE(DSC1, DP_INTF1, + MMSYS_ROUTE(DSC, 1, DP_INTF, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP1_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP1_OUT_TO_SINA_VIRTUAL0), - MMSYS_ROUTE(DSC1, DPI0, + MMSYS_ROUTE(DSC, 1, DPI, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP1_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP1_OUT_TO_SINA_VIRTUAL0), - MMSYS_ROUTE(DSC1, DPI1, + MMSYS_ROUTE(DSC, 1, DPI, 1, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP1_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP1_OUT_TO_SINA_VIRTUAL0), - MMSYS_ROUTE(DSC1, MERGE0, + MMSYS_ROUTE(DSC, 1, MERGE, 0, MT8195_VDO0_SEL_OUT, MT8195_SOUT_DSC_WRAP1_OUT_TO_MASK, MT8195_SOUT_DSC_WRAP1_OUT_TO_VPP_MERGE), }; static const struct mtk_mmsys_routes mmsys_mt8195_vdo1_routing_table[] = { - MMSYS_ROUTE(MDP_RDMA0, MERGE1, + MMSYS_ROUTE(MDP_RDMA, 0, MERGE, 1, MT8195_VDO1_VPP_MERGE0_P0_SEL_IN, GENMASK(0, 0), MT8195_VPP_MERGE0_P0_SEL_IN_FROM_MDP_RDMA0), - MMSYS_ROUTE(MDP_RDMA1, MERGE1, + MMSYS_ROUTE(MDP_RDMA, 1, MERGE, 1, MT8195_VDO1_VPP_MERGE0_P1_SEL_IN, GENMASK(0, 0), MT8195_VPP_MERGE0_P1_SEL_IN_FROM_MDP_RDMA1), - MMSYS_ROUTE(MDP_RDMA2, MERGE2, + MMSYS_ROUTE(MDP_RDMA, 2, MERGE, 2, MT8195_VDO1_VPP_MERGE1_P0_SEL_IN, GENMASK(0, 0), MT8195_VPP_MERGE1_P0_SEL_IN_FROM_MDP_RDMA2), - MMSYS_ROUTE(MERGE1, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 1, ETHDR_MIXER, 0, MT8195_VDO1_MERGE0_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8195_SOUT_TO_MIXER_IN1_SEL), - MMSYS_ROUTE(MERGE2, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 2, ETHDR_MIXER, 0, MT8195_VDO1_MERGE1_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8195_SOUT_TO_MIXER_IN2_SEL), - MMSYS_ROUTE(MERGE3, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 3, ETHDR_MIXER, 0, MT8195_VDO1_MERGE2_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8195_SOUT_TO_MIXER_IN3_SEL), - MMSYS_ROUTE(MERGE4, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 4, ETHDR_MIXER, 0, MT8195_VDO1_MERGE3_ASYNC_SOUT_SEL, GENMASK(1, 0), MT8195_SOUT_TO_MIXER_IN4_SEL), - MMSYS_ROUTE(ETHDR_MIXER, MERGE5, + MMSYS_ROUTE(ETHDR_MIXER, 0, MERGE, 5, MT8195_VDO1_MIXER_OUT_SOUT_SEL, GENMASK(0, 0), MT8195_MIXER_SOUT_TO_MERGE4_ASYNC_SEL), - MMSYS_ROUTE(MERGE1, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 1, ETHDR_MIXER, 0, MT8195_VDO1_MIXER_IN1_SEL_IN, GENMASK(0, 0), MT8195_MIXER_IN1_SEL_IN_FROM_MERGE0_ASYNC_SOUT), - MMSYS_ROUTE(MERGE2, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 2, ETHDR_MIXER, 0, MT8195_VDO1_MIXER_IN2_SEL_IN, GENMASK(0, 0), MT8195_MIXER_IN2_SEL_IN_FROM_MERGE1_ASYNC_SOUT), - MMSYS_ROUTE(MERGE3, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 3, ETHDR_MIXER, 0, MT8195_VDO1_MIXER_IN3_SEL_IN, GENMASK(0, 0), MT8195_MIXER_IN3_SEL_IN_FROM_MERGE2_ASYNC_SOUT), - MMSYS_ROUTE(MERGE4, ETHDR_MIXER, + MMSYS_ROUTE(MERGE, 4, ETHDR_MIXER, 0, MT8195_VDO1_MIXER_IN4_SEL_IN, GENMASK(0, 0), MT8195_MIXER_IN4_SEL_IN_FROM_MERGE3_ASYNC_SOUT), - MMSYS_ROUTE(ETHDR_MIXER, MERGE5, + MMSYS_ROUTE(ETHDR_MIXER, 0, MERGE, 5, MT8195_VDO1_MIXER_SOUT_SEL_IN, GENMASK(2, 0), MT8195_MIXER_SOUT_SEL_IN_FROM_DISP_MIXER), - MMSYS_ROUTE(ETHDR_MIXER, MERGE5, + MMSYS_ROUTE(ETHDR_MIXER, 0, MERGE, 5, MT8195_VDO1_MERGE4_ASYNC_SEL_IN, GENMASK(2, 0), MT8195_MERGE4_ASYNC_SEL_IN_FROM_MIXER_OUT_SOUT), - MMSYS_ROUTE(MERGE5, DPI1, + MMSYS_ROUTE(MERGE, 5, DPI, 1, MT8195_VDO1_DISP_DPI1_SEL_IN, GENMASK(1, 0), MT8195_DISP_DPI1_SEL_IN_FROM_VPP_MERGE4_MOUT), - MMSYS_ROUTE(MERGE5, DPI1, + MMSYS_ROUTE(MERGE, 5, DPI, 1, MT8195_VDO1_MERGE4_SOUT_SEL, GENMASK(1, 0), MT8195_MERGE4_SOUT_TO_DPI1_SEL), - MMSYS_ROUTE(MERGE5, DP_INTF1, + MMSYS_ROUTE(MERGE, 5, DP_INTF, 1, MT8195_VDO1_DISP_DP_INTF0_SEL_IN, GENMASK(1, 0), MT8195_DISP_DP_INTF0_SEL_IN_FROM_VPP_MERGE4_MOUT), - MMSYS_ROUTE(MERGE5, DP_INTF1, + MMSYS_ROUTE(MERGE, 5, DP_INTF, 1, MT8195_VDO1_MERGE4_SOUT_SEL, GENMASK(1, 0), MT8195_MERGE4_SOUT_TO_DP_INTF0_SEL), }; + #endif /* __SOC_MEDIATEK_MT8195_MMSYS_H */ diff --git a/drivers/soc/mediatek/mt8365-mmsys.h b/drivers/soc/mediatek/mt8365-mmsys.h index 533a3fd0923b..b438ab7ae00b 100644 --- a/drivers/soc/mediatek/mt8365-mmsys.h +++ b/drivers/soc/mediatek/mt8365-mmsys.h @@ -28,35 +28,35 @@ #define MT8365_DPI0_SEL_IN_RDMA1 0x0 static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = { - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8365_DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, MT8365_DISP_MS_IN_OUT_MASK, MT8365_OVL0_MOUT_PATH0_SEL), - MMSYS_ROUTE(OVL0, RDMA0, + MMSYS_ROUTE(OVL, 0, RDMA, 0, MT8365_DISP_REG_CONFIG_DISP_RDMA0_SEL_IN, MT8365_DISP_MS_IN_OUT_MASK, MT8365_RDMA0_SEL_IN_OVL0), - MMSYS_ROUTE(RDMA0, COLOR0, + MMSYS_ROUTE(RDMA, 0, COLOR, 0, MT8365_DISP_REG_CONFIG_DISP_RDMA0_SOUT_SEL, MT8365_DISP_MS_IN_OUT_MASK, MT8365_RDMA0_SOUT_COLOR0), - MMSYS_ROUTE(COLOR0, CCORR, + MMSYS_ROUTE(COLOR, 0, CCORR, 0, MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, MT8365_DISP_MS_IN_OUT_MASK, MT8365_DISP_COLOR_SEL_IN_COLOR0), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8365_DISP_REG_CONFIG_DISP_DITHER0_MOUT_EN, MT8365_DISP_MS_IN_OUT_MASK, MT8365_DITHER_MOUT_EN_DSI0), - MMSYS_ROUTE(DITHER0, DSI0, + MMSYS_ROUTE(DITHER, 0, DSI, 0, MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN, MT8365_DISP_MS_IN_OUT_MASK, MT8365_DSI0_SEL_IN_DITHER), - MMSYS_ROUTE(RDMA0, COLOR0, + MMSYS_ROUTE(RDMA, 0, COLOR, 0, MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN, MT8365_DISP_MS_IN_OUT_MASK, MT8365_RDMA0_RSZ0_SEL_IN_RDMA0), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8365_DISP_REG_CONFIG_DISP_LVDS_SYS_CFG_00, MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK, MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8365_DISP_REG_CONFIG_DISP_DPI0_SEL_IN, MT8365_DISP_MS_IN_OUT_MASK, MT8365_DPI0_SEL_IN_RDMA1), - MMSYS_ROUTE(RDMA1, DPI0, + MMSYS_ROUTE(RDMA, 1, DPI, 0, MT8365_DISP_REG_CONFIG_DISP_RDMA1_SOUT_SEL, MT8365_DISP_MS_IN_OUT_MASK, MT8365_RDMA1_SOUT_DPI0), }; diff --git a/drivers/soc/mediatek/mtk-mmsys.h b/drivers/soc/mediatek/mtk-mmsys.h index fe628d5f5198..a655cf006217 100644 --- a/drivers/soc/mediatek/mtk-mmsys.h +++ b/drivers/soc/mediatek/mtk-mmsys.h @@ -80,18 +80,25 @@ #define MMSYS_RST_NR(bank, bit) (((bank) * 32) + (bit)) +/* Temporary compatibility definitions */ +#define DDP_COMPONENT_BLS0 DDP_COMPONENT_BLS +#define DDP_COMPONENT_CCORR0 DDP_COMPONENT_CCORR +#define DDP_COMPONENT_UFOE0 DDP_COMPONENT_UFOE +#define DDP_COMPONENT_GAMMA0 DDP_COMPONENT_GAMMA +#define DDP_COMPONENT_ETHDR_MIXER0 DDP_COMPONENT_ETHDR_MIXER + /* * This macro adds a compile time check to make sure that the in/out * selection bit(s) fit in the register mask, similar to bitfield * macros, but this does not transform the value. */ -#define MMSYS_ROUTE(from, to, reg_addr, reg_mask, selection) \ - { DDP_COMPONENT_##from, DDP_COMPONENT_##to, reg_addr, reg_mask, \ - (__BUILD_BUG_ON_ZERO_MSG((reg_mask) == 0, "Invalid mask") + \ - __BUILD_BUG_ON_ZERO_MSG(~(reg_mask) & (selection), \ - #selection " does not fit in " \ - #reg_mask) + \ - (selection)) \ +#define MMSYS_ROUTE(from, fsid, to, tsid, reg_addr, reg_mask, selection) \ + { DDP_COMPONENT_##from##fsid, DDP_COMPONENT_##to##tsid, reg_addr, reg_mask, \ + (__BUILD_BUG_ON_ZERO_MSG((reg_mask) == 0, "Invalid mask") + \ + __BUILD_BUG_ON_ZERO_MSG(~(reg_mask) & (selection), \ + #selection " does not fit in " \ + #reg_mask) + \ + (selection)) \ } struct mtk_mmsys_routes { From dd63310b94f3153bf3741698fa10037a42725621 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Wed, 1 Jul 2026 14:20:43 +0200 Subject: [PATCH 096/157] soc: mediatek: mtk-mmsys: Use MMSYS_ROUTE() in default routing table All of the mtk_mmsys_routes tables for all SoCs were converted to use the MMSYS_ROUTE() macro but the default one used for MT2701, MT2712 and SoCs from that generation was not: convert this one as well. This brings no functional change. Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-mmsys.h | 279 +++++++++++++------------------ 1 file changed, 114 insertions(+), 165 deletions(-) diff --git a/drivers/soc/mediatek/mtk-mmsys.h b/drivers/soc/mediatek/mtk-mmsys.h index a655cf006217..c51a5e334e29 100644 --- a/drivers/soc/mediatek/mtk-mmsys.h +++ b/drivers/soc/mediatek/mtk-mmsys.h @@ -158,171 +158,120 @@ struct mtk_mmsys_driver_data { * to an independent table. */ static const struct mtk_mmsys_routes mmsys_default_routing_table[] = { - { - DDP_COMPONENT_BLS, DDP_COMPONENT_DSI0, - DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK, - BLS_TO_DSI_RDMA1_TO_DPI1 - }, { - DDP_COMPONENT_BLS, DDP_COMPONENT_DSI0, - DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK, - DSI_SEL_IN_BLS - }, { - DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK, - BLS_TO_DPI_RDMA1_TO_DSI - }, { - DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK, - DSI_SEL_IN_RDMA - }, { - DDP_COMPONENT_BLS, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_DPI_SEL, DPI_SEL_IN_MASK, - DPI_SEL_IN_BLS - }, { - DDP_COMPONENT_GAMMA, DDP_COMPONENT_RDMA1, - DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN, GAMMA_MOUT_EN_RDMA1, - GAMMA_MOUT_EN_RDMA1 - }, { - DDP_COMPONENT_OD0, DDP_COMPONENT_RDMA0, - DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD_MOUT_EN_RDMA0, - OD_MOUT_EN_RDMA0 - }, { - DDP_COMPONENT_OD1, DDP_COMPONENT_RDMA1, - DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD1_MOUT_EN_RDMA1, - OD1_MOUT_EN_RDMA1 - }, { - DDP_COMPONENT_OVL0, DDP_COMPONENT_COLOR0, - DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0, - OVL0_MOUT_EN_COLOR0 - }, { - DDP_COMPONENT_OVL0, DDP_COMPONENT_COLOR0, - DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0, - COLOR0_SEL_IN_OVL0 - }, { - DDP_COMPONENT_OVL0, DDP_COMPONENT_RDMA0, - DISP_REG_CONFIG_DISP_OVL_MOUT_EN, OVL_MOUT_EN_RDMA, - OVL_MOUT_EN_RDMA - }, { - DDP_COMPONENT_OVL1, DDP_COMPONENT_COLOR1, - DISP_REG_CONFIG_DISP_OVL1_MOUT_EN, OVL1_MOUT_EN_COLOR1, - OVL1_MOUT_EN_COLOR1 - }, { - DDP_COMPONENT_OVL1, DDP_COMPONENT_COLOR1, - DISP_REG_CONFIG_DISP_COLOR1_SEL_IN, COLOR1_SEL_IN_OVL1, - COLOR1_SEL_IN_OVL1 - }, { - DDP_COMPONENT_RDMA0, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, - RDMA0_SOUT_DPI0 - }, { - DDP_COMPONENT_RDMA0, DDP_COMPONENT_DPI1, - DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, - RDMA0_SOUT_DPI1 - }, { - DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI1, - DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, - RDMA0_SOUT_DSI1 - }, { - DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI2, - DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, - RDMA0_SOUT_DSI2 - }, { - DDP_COMPONENT_RDMA0, DDP_COMPONENT_DSI3, - DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, - RDMA0_SOUT_DSI3 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, - RDMA1_SOUT_DPI0 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK, - DPI0_SEL_IN_RDMA1 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI1, - DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, - RDMA1_SOUT_DPI1 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI1, - DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK, - DPI1_SEL_IN_RDMA1 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI0, - DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK, - DSI0_SEL_IN_RDMA1 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI1, - DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, - RDMA1_SOUT_DSI1 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI1, - DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK, - DSI1_SEL_IN_RDMA1 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI2, - DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, - RDMA1_SOUT_DSI2 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI2, - DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK, - DSI2_SEL_IN_RDMA1 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI3, - DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, - RDMA1_SOUT_DSI3 - }, { - DDP_COMPONENT_RDMA1, DDP_COMPONENT_DSI3, - DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_MASK, - DSI3_SEL_IN_RDMA1 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, - RDMA2_SOUT_DPI0 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI0, - DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK, - DPI0_SEL_IN_RDMA2 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI1, - DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, - RDMA2_SOUT_DPI1 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DPI1, - DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK, - DPI1_SEL_IN_RDMA2 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI0, - DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK, - DSI0_SEL_IN_RDMA2 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI1, - DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, - RDMA2_SOUT_DSI1 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI1, - DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK, - DSI1_SEL_IN_RDMA2 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI2, - DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, - RDMA2_SOUT_DSI2 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI2, - DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK, - DSI2_SEL_IN_RDMA2 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI3, - DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, - RDMA2_SOUT_DSI3 - }, { - DDP_COMPONENT_RDMA2, DDP_COMPONENT_DSI3, - DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_MASK, - DSI3_SEL_IN_RDMA2 - }, { - DDP_COMPONENT_UFOE, DDP_COMPONENT_DSI0, - DISP_REG_CONFIG_DISP_UFOE_MOUT_EN, UFOE_MOUT_EN_DSI0, - UFOE_MOUT_EN_DSI0 - } + MMSYS_ROUTE(BLS, 0, DSI, 0, + DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK, + BLS_TO_DSI_RDMA1_TO_DPI1), + MMSYS_ROUTE(BLS, 0, DSI, 0, + DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK, + DSI_SEL_IN_BLS), + MMSYS_ROUTE(BLS, 0, DPI, 0, + DISP_REG_CONFIG_OUT_SEL, BLS_RDMA1_DSI_DPI_MASK, + BLS_TO_DPI_RDMA1_TO_DSI), + MMSYS_ROUTE(BLS, 0, DPI, 0, + DISP_REG_CONFIG_DSI_SEL, DSI_SEL_IN_MASK, + DSI_SEL_IN_RDMA), + MMSYS_ROUTE(BLS, 0, DPI, 0, + DISP_REG_CONFIG_DPI_SEL, DPI_SEL_IN_MASK, + DPI_SEL_IN_BLS), + MMSYS_ROUTE(GAMMA, 0, RDMA, 1, + DISP_REG_CONFIG_DISP_GAMMA_MOUT_EN, GAMMA_MOUT_EN_RDMA1, + GAMMA_MOUT_EN_RDMA1), + MMSYS_ROUTE(OD, 0, RDMA, 0, + DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD_MOUT_EN_RDMA0, + OD_MOUT_EN_RDMA0), + MMSYS_ROUTE(OD, 1, RDMA, 1, + DISP_REG_CONFIG_DISP_OD_MOUT_EN, OD1_MOUT_EN_RDMA1, + OD1_MOUT_EN_RDMA1), + MMSYS_ROUTE(OVL, 0, COLOR, 0, + DISP_REG_CONFIG_DISP_OVL0_MOUT_EN, OVL0_MOUT_EN_COLOR0, + OVL0_MOUT_EN_COLOR0), + MMSYS_ROUTE(OVL, 0, COLOR, 0, + DISP_REG_CONFIG_DISP_COLOR0_SEL_IN, COLOR0_SEL_IN_OVL0, + COLOR0_SEL_IN_OVL0), + MMSYS_ROUTE(OVL, 0, RDMA, 0, + DISP_REG_CONFIG_DISP_OVL_MOUT_EN, OVL_MOUT_EN_RDMA, + OVL_MOUT_EN_RDMA), + MMSYS_ROUTE(OVL, 1, COLOR, 1, + DISP_REG_CONFIG_DISP_OVL1_MOUT_EN, OVL1_MOUT_EN_COLOR1, + OVL1_MOUT_EN_COLOR1), + MMSYS_ROUTE(OVL, 1, COLOR, 1, + DISP_REG_CONFIG_DISP_COLOR1_SEL_IN, COLOR1_SEL_IN_OVL1, + COLOR1_SEL_IN_OVL1), + MMSYS_ROUTE(RDMA, 0, DPI, 0, + DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, + RDMA0_SOUT_DPI0), + MMSYS_ROUTE(RDMA, 0, DPI, 1, + DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, + RDMA0_SOUT_DPI1), + MMSYS_ROUTE(RDMA, 0, DSI, 1, + DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, + RDMA0_SOUT_DSI1), + MMSYS_ROUTE(RDMA, 0, DSI, 2, + DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, + RDMA0_SOUT_DSI2), + MMSYS_ROUTE(RDMA, 0, DSI, 3, + DISP_REG_CONFIG_DISP_RDMA0_SOUT_EN, RDMA0_SOUT_MASK, + RDMA0_SOUT_DSI3), + MMSYS_ROUTE(RDMA, 1, DPI, 0, + DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, + RDMA1_SOUT_DPI0), + MMSYS_ROUTE(RDMA, 1, DPI, 0, + DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK, + DPI0_SEL_IN_RDMA1), + MMSYS_ROUTE(RDMA, 1, DPI, 1, + DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, + RDMA1_SOUT_DPI1), + MMSYS_ROUTE(RDMA, 1, DPI, 1, + DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK, + DPI1_SEL_IN_RDMA1), + MMSYS_ROUTE(RDMA, 1, DSI, 0, + DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK, + DSI0_SEL_IN_RDMA1), + MMSYS_ROUTE(RDMA, 1, DSI, 1, + DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, + RDMA1_SOUT_DSI1), + MMSYS_ROUTE(RDMA, 1, DSI, 1, + DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK, + DSI1_SEL_IN_RDMA1), + MMSYS_ROUTE(RDMA, 1, DSI, 2, + DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, + RDMA1_SOUT_DSI2), + MMSYS_ROUTE(RDMA, 1, DSI, 2, + DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK, + DSI2_SEL_IN_RDMA1), + MMSYS_ROUTE(RDMA, 1, DSI, 3, + DISP_REG_CONFIG_DISP_RDMA1_SOUT_EN, RDMA1_SOUT_MASK, + RDMA1_SOUT_DSI3), + MMSYS_ROUTE(RDMA, 1, DSI, 3, + DISP_REG_CONFIG_DSIO_SEL_IN, DSI3_SEL_IN_MASK, + DSI3_SEL_IN_RDMA1), + MMSYS_ROUTE(RDMA, 2, DPI, 0, + DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, + RDMA2_SOUT_DPI0), + MMSYS_ROUTE(RDMA, 2, DPI, 0, + DISP_REG_CONFIG_DPI_SEL_IN, DPI0_SEL_IN_MASK, + DPI0_SEL_IN_RDMA2), + MMSYS_ROUTE(RDMA, 2, DPI, 1, + DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, + RDMA2_SOUT_DPI1), + MMSYS_ROUTE(RDMA, 2, DPI, 1, + DISP_REG_CONFIG_DPI_SEL_IN, DPI1_SEL_IN_MASK, + DPI1_SEL_IN_RDMA2), + MMSYS_ROUTE(RDMA, 2, DSI, 0, + DISP_REG_CONFIG_DSIE_SEL_IN, DSI0_SEL_IN_MASK, + DSI0_SEL_IN_RDMA2), + MMSYS_ROUTE(RDMA, 2, DSI, 1, + DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, + RDMA2_SOUT_DSI1), + MMSYS_ROUTE(RDMA, 2, DSI, 1, + DISP_REG_CONFIG_DSIO_SEL_IN, DSI1_SEL_IN_MASK, + DSI1_SEL_IN_RDMA2), + MMSYS_ROUTE(RDMA, 2, DSI, 2, + DISP_REG_CONFIG_DISP_RDMA2_SOUT, RDMA2_SOUT_MASK, + RDMA2_SOUT_DSI2), + MMSYS_ROUTE(RDMA, 2, DSI, 2, + DISP_REG_CONFIG_DSIE_SEL_IN, DSI2_SEL_IN_MASK, + DSI2_SEL_IN_RDMA2), }; #endif /* __SOC_MEDIATEK_MTK_MMSYS_H */ From ec762057868d70d474a9816e9c293c2ef801c3c9 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:43:08 +0800 Subject: [PATCH 097/157] soc: mediatek: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-dvfsrc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/mediatek/mtk-dvfsrc.c b/drivers/soc/mediatek/mtk-dvfsrc.c index 548a28f50242..e8cdee8aed35 100644 --- a/drivers/soc/mediatek/mtk-dvfsrc.c +++ b/drivers/soc/mediatek/mtk-dvfsrc.c @@ -862,6 +862,7 @@ static const struct of_device_id mtk_dvfsrc_of_match[] = { { .compatible = "mediatek,mt8196-dvfsrc", .data = &mt8196_data }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mtk_dvfsrc_of_match); static struct platform_driver mtk_dvfsrc_driver = { .probe = mtk_dvfsrc_probe, From 3fd6a04e5bf863a8c02c150d584f42c7b9ae5f8c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 12 Jul 2026 18:22:13 +0200 Subject: [PATCH 098/157] dt-bindings: soc: qcom,wcnss: Drop redundant $ref of firmware-name property The DT schema core defines the type ($ref) of 'firmware-name' property as string-array, so individual schemas do not need to. They also should not redefine it to a single string, but instead just set number of expected firmware names. Signed-off-by: Krzysztof Kozlowski Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260712162212.184592-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml index 4fcae6bedfff..56a4152633e5 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,wcnss.yaml @@ -19,8 +19,8 @@ properties: const: qcom,wcnss firmware-name: - $ref: /schemas/types.yaml#/definitions/string - default: wlan/prima/WCNSS_qcom_wlan_nv.bin + maxItems: 1 + default: [ wlan/prima/WCNSS_qcom_wlan_nv.bin ] description: Relative firmware image path for the WLAN NV blob. From b255c6f4e6b48e09227cd68777a5d2ec1d36dc31 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 17 Jul 2026 20:06:37 +0200 Subject: [PATCH 099/157] soc: qcom: pmic_glink_altmode: Define the TBT extradata properly Before the fixes-referenced commit, there was a trailing 'u32 reserved' after the payload array. That commit gobbled it up into the thunderbolt extradata. Push it back where it belongs. There's no functional change, since the outer struct size remains identical - struct usbc_sc8280x_tbt_data and therefore the union it's a part of made up for the difference and the res bytes were ignored anyway. Fixes: 0539c5a6fdef ("soc: qcom: pmic_glink_altmode: Consume TBT3/USB4 mode notifications") Reported-by: Fenglin Wu Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260717-topic-tbt_extradata_fixup-v1-1-5caa18f1c8d3@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pmic_glink_altmode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index 619bad2c27ee..13c434f8d03a 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -53,7 +53,7 @@ struct usbc_sc8280x_tbt_data { /* This field is NOP on USB4, all cables support rounded rates by spec */ u8 rounded_cable : 1; u8 power_limited : 1; - u8 res[11]; + u8 res[7]; }; struct usbc_notify { @@ -74,6 +74,7 @@ struct usbc_notify { struct usbc_sc8280x_dp_data dp; struct usbc_sc8280x_tbt_data tbt; } extended_data; + u32 reserved; }; struct usbc_sc8180x_notify { From 522bfb4f33c0930b3d14d5c5ee80bc93a883b544 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Thu, 2 Jul 2026 11:12:23 +0530 Subject: [PATCH 100/157] soc: qcom: geni-se: Use HW PROG_RAM_DEPTH to validate firmware size The hardcoded MAX_GENI_CFG_RAMn_CNT limit is not accurate for all SoCs: some targets have less CFG RAM than the constant implies, while others like QCS615 need more entries than the old limit of 455 allowed, causing valid firmware to be rejected at load time. Rather than hardcoding a constant, read PROG_RAM_DEPTH from SE_HW_PARAM_2 at runtime to get the actual CFG RAM depth of the hardware instance and use that as the upper bound for firmware size validation. Fixes: d4bf06592ad6 ("soc: qcom: geni-se: Add support to load QUP SE Firmware via Linux subsystem") Cc: stable@vger.kernel.org Reviewed-by: Konrad Dybcio Signed-off-by: Viken Dadhaniya Link: https://lore.kernel.org/r/20260702-qup-se-increase-ram-cnt-v3-1-80b363373a5b@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom-geni-se.c | 24 +++++++++++++----------- include/linux/soc/qcom/geni-se.h | 4 ++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index 15636a8dc907..107fa2057654 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -154,8 +154,6 @@ struct se_fw_hdr { /*Magic numbers*/ #define SE_MAGIC_NUM 0x57464553 -#define MAX_GENI_CFG_RAMn_CNT 455 - #define MI_PBT_NON_PAGED_SEGMENT 0x0 #define MI_PBT_HASH_SEGMENT 0x2 #define MI_PBT_NOTUSED_SEGMENT 0x3 @@ -1224,24 +1222,27 @@ EXPORT_SYMBOL_GPL(geni_se_resources_init); /** * geni_find_protocol_fw() - Locate and validate SE firmware for a protocol. - * @dev: Pointer to the device structure. + * @se: Pointer to the serial engine structure. * @fw: Pointer to the firmware image. * @protocol: Expected serial engine protocol type. * * Identifies the appropriate firmware image or configuration required for a - * specific communication protocol instance running on a Qualcomm GENI - * controller. + * specific communication protocol instance running on a Qualcomm GENI + * controller. Validates the firmware size against the hardware PROG_RAM_DEPTH + * read from SE_HW_PARAM_2. * * Return: pointer to a valid 'struct se_fw_hdr' if found, or NULL otherwise. */ -static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct firmware *fw, +static struct se_fw_hdr *geni_find_protocol_fw(struct geni_se *se, const struct firmware *fw, enum geni_se_protocol_type protocol) { + struct device *dev = se->dev; const struct elf32_hdr *ehdr; const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; struct se_fw_hdr *sefw; u32 fw_end, cfg_idx_end, cfg_val_end; + u32 prog_ram_depth; u16 fw_size; int i; @@ -1300,10 +1301,11 @@ static struct se_fw_hdr *geni_find_protocol_fw(struct device *dev, const struct sefw->fw_size_in_items = cpu_to_le16(fw_size); } - if (fw_size >= MAX_GENI_CFG_RAMn_CNT) { - dev_err(dev, - "Firmware size (%u) exceeds max allowed RAMn count (%u)\n", - fw_size, MAX_GENI_CFG_RAMn_CNT); + prog_ram_depth = FIELD_GET(PROG_RAM_DEPTH_MSK, + readl_relaxed(se->base + SE_HW_PARAM_2)); + if (fw_size >= prog_ram_depth) { + dev_err(dev, "Firmware size (%u) exceeds RAM size (%u)\n", + fw_size, prog_ram_depth); continue; } @@ -1427,7 +1429,7 @@ static int geni_load_se_fw(struct geni_se *se, const struct firmware *fw, int ret; struct se_fw_hdr *hdr; - hdr = geni_find_protocol_fw(se->dev, fw, protocol); + hdr = geni_find_protocol_fw(se, fw, protocol); if (!hdr) return -EINVAL; diff --git a/include/linux/soc/qcom/geni-se.h b/include/linux/soc/qcom/geni-se.h index c5e6ab85df09..0991ef7ec092 100644 --- a/include/linux/soc/qcom/geni-se.h +++ b/include/linux/soc/qcom/geni-se.h @@ -124,6 +124,7 @@ struct geni_se { #define SE_DMA_RX_FSM_RST 0xd58 #define SE_HW_PARAM_0 0xe24 #define SE_HW_PARAM_1 0xe28 +#define SE_HW_PARAM_2 0xe2c /* GENI_FORCE_DEFAULT_REG fields */ #define FORCE_DEFAULT BIT(0) @@ -291,6 +292,9 @@ struct geni_se { #define RX_FIFO_DEPTH_MSK GENMASK(21, 16) #define RX_FIFO_DEPTH_SHFT 16 +/* SE_HW_PARAM_2 fields */ +#define PROG_RAM_DEPTH_MSK GENMASK(10, 0) + #define HW_VER_MAJOR_MASK GENMASK(31, 28) #define HW_VER_MAJOR_SHFT 28 #define HW_VER_MINOR_MASK GENMASK(27, 16) From ca8c96d3187a11229b3bd44992a9bc3d22caf13a Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Tue, 21 Jul 2026 19:11:28 +0200 Subject: [PATCH 101/157] firmware: qcom: scm: Allow QSEECOM on Asus Zenbook A16 (UX3607OA) In line with other Glymur-based machines, the Asus Zenbook A16 (UX3607OA) exposes EFI variables through the "uefisecapp". Add it to the QSEECOM allowlist to enable this access. Signed-off-by: Konrad Dybcio Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260721-topic-a16_submission-v1-3-8ea213130d05@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index f35f2ee39130..9ec79b445481 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2304,6 +2304,7 @@ static const struct of_device_id qcom_scm_qseecom_allowlist[] __maybe_unused = { { .compatible = "asus,vivobook-s15-x1p4" }, { .compatible = "asus,zenbook-a14-ux3407qa" }, { .compatible = "asus,zenbook-a14-ux3407ra" }, + { .compatible = "asus,zenbook-a16-ux3607oa" }, { .compatible = "dell,inspiron-14-plus-7441" }, { .compatible = "dell,latitude-7455" }, { .compatible = "dell,xps13-9345" }, From 966d23c7e68ea32679275a7e3d2383181002c868 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Fri, 24 Jul 2026 15:19:37 +0530 Subject: [PATCH 102/157] firmware: qcom: scm: Fix NULL dereference in IRQ handler before __scm is published In qcom_scm_probe(), devm_request_threaded_irq() is called before smp_store_release(&__scm, scm). Two paths can dereference __scm before it is published, both causing a NULL pointer dereference. The IRQ handler receives scm via its data argument but passes only wq_ctx to qcom_scm_waitq_wakeup() and qcom_scm_get_completion(), which then dereference __scm directly. Thread scm through both functions so the IRQ handler path never touches __scm. Non-atomic SMC calls made during probe (e.g. from qcom_tzmem_init via qcom_scm_shm_bridge_enable) can return WAITQ_SLEEP, causing qcom_scm_wait_for_wq_completion() to run before __scm is published and dereference it. Add platform_set_drvdata(pdev, scm) early in probe and change qcom_scm_wait_for_wq_completion() to take the device pointer and use dev_get_drvdata() to reach scm, removing any dependency on __scm. Fixes: 6bf325992236 ("firmware: qcom: scm: Add wait-queue handling logic") Reviewed-by: Bartosz Golaszewski Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260724094939.613844-2-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm-smc.c | 2 +- drivers/firmware/qcom/qcom_scm.c | 22 ++++++++++------------ drivers/firmware/qcom/qcom_scm.h | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm-smc.c b/drivers/firmware/qcom/qcom_scm-smc.c index 01999c22659c..127365ab11fc 100644 --- a/drivers/firmware/qcom/qcom_scm-smc.c +++ b/drivers/firmware/qcom/qcom_scm-smc.c @@ -111,7 +111,7 @@ static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_ smc_call_ctx = res->a2; trace_scm_waitq_sleep(wq_ctx, smc_call_ctx); - ret = qcom_scm_wait_for_wq_completion(wq_ctx); + ret = qcom_scm_wait_for_wq_completion(dev, wq_ctx); if (ret) return ret; diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 9ec79b445481..d11026c26cde 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2645,23 +2645,20 @@ static int qcom_scm_get_waitq_irq(struct qcom_scm *scm) return irq_create_fwspec_mapping(&fwspec); } -static struct completion *qcom_scm_get_completion(u32 wq_ctx) +static struct completion *qcom_scm_get_completion(struct qcom_scm *scm, u32 wq_ctx) { - struct completion *wq; - - if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt)) + if (WARN_ON_ONCE(wq_ctx >= scm->wq_cnt)) return ERR_PTR(-EINVAL); - wq = &__scm->waitq_comps[wq_ctx]; - - return wq; + return &scm->waitq_comps[wq_ctx]; } -int qcom_scm_wait_for_wq_completion(u32 wq_ctx) +int qcom_scm_wait_for_wq_completion(struct device *dev, u32 wq_ctx) { + struct qcom_scm *scm = dev_get_drvdata(dev); struct completion *wq; - wq = qcom_scm_get_completion(wq_ctx); + wq = qcom_scm_get_completion(scm, wq_ctx); if (IS_ERR(wq)) return PTR_ERR(wq); @@ -2670,11 +2667,11 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx) return 0; } -static int qcom_scm_waitq_wakeup(unsigned int wq_ctx) +static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx) { struct completion *wq; - wq = qcom_scm_get_completion(wq_ctx); + wq = qcom_scm_get_completion(scm, wq_ctx); if (IS_ERR(wq)) return PTR_ERR(wq); @@ -2701,7 +2698,7 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data) goto out; } - ret = qcom_scm_waitq_wakeup(wq_ctx); + ret = qcom_scm_waitq_wakeup(scm, wq_ctx); if (ret) goto out; } while (more_pending); @@ -2805,6 +2802,7 @@ static int qcom_scm_probe(struct platform_device *pdev) return -ENOMEM; scm->dev = &pdev->dev; + platform_set_drvdata(pdev, scm); ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr); if (ret < 0) return dev_err_probe(&pdev->dev, ret, diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index caab80a73e17..cf90a565fdfb 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -66,7 +66,7 @@ struct qcom_scm_res { u64 result[MAX_QCOM_SCM_RETS]; }; -int qcom_scm_wait_for_wq_completion(u32 wq_ctx); +int qcom_scm_wait_for_wq_completion(struct device *dev, u32 wq_ctx); int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending); #define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF)) From b697b20cea4374d27e2134da4bb7b0ea39f36c8b Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Fri, 24 Jul 2026 15:19:38 +0530 Subject: [PATCH 103/157] firmware: qcom: scm: Fix reserved memory cleanup on probe failure of_reserved_mem_device_init() adds an entry to a global list with no devres counterpart. If qcom_scm_probe() fails after the call the assignment is never cleaned up. A probe retry would add a duplicate entry, leaking the original one permanently. Add an err_rmem label that calls of_reserved_mem_device_release() and route all error paths after of_reserved_mem_device_init() through it. of_reserved_mem_device_release() is safe to call unconditionally as it simply walks an empty list when nothing was assigned. Fixes: a33b2579c8d3 ("firmware: qcom: scm: add support for SHM bridge memory carveout") Reviewed-by: Bartosz Golaszewski Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260724094939.613844-3-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index d11026c26cde..3eaa4c9ccf3c 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2850,9 +2850,11 @@ static int qcom_scm_probe(struct platform_device *pdev) "Failed to setup the reserved memory region for TZ mem\n"); ret = qcom_tzmem_enable(scm->dev); - if (ret) - return dev_err_probe(scm->dev, ret, - "Failed to enable the TrustZone memory allocator\n"); + if (ret) { + ret = dev_err_probe(scm->dev, ret, + "Failed to enable the TrustZone memory allocator\n"); + goto err_rmem; + } memset(&pool_config, 0, sizeof(pool_config)); pool_config.initial_size = 0; @@ -2860,9 +2862,11 @@ static int qcom_scm_probe(struct platform_device *pdev) pool_config.max_size = SZ_256K; scm->mempool = devm_qcom_tzmem_pool_new(scm->dev, &pool_config); - if (IS_ERR(scm->mempool)) - return dev_err_probe(scm->dev, PTR_ERR(scm->mempool), - "Failed to create the SCM memory pool\n"); + if (IS_ERR(scm->mempool)) { + ret = dev_err_probe(scm->dev, PTR_ERR(scm->mempool), + "Failed to create the SCM memory pool\n"); + goto err_rmem; + } ret = qcom_scm_query_waitq_count(scm); scm->wq_cnt = ret < 0 ? QCOM_SCM_DEFAULT_WAITQ_COUNT : ret; @@ -2938,6 +2942,10 @@ static int qcom_scm_probe(struct platform_device *pdev) qcom_scm_gunyah_wdt_init(scm); return 0; + +err_rmem: + of_reserved_mem_device_release(scm->dev); + return ret; } static void qcom_scm_shutdown(struct platform_device *pdev) From 9941fe8a04f3d258e07eb5899db3027252a4190f Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Fri, 24 Jul 2026 15:19:39 +0530 Subject: [PATCH 104/157] firmware: qcom: scm: Fix tzmem state on probe retry qcom_tzmem_enable() returns -EBUSY if called a second time, but this causes probe retries to fail permanently if a later step in qcom_scm_probe() defers after qcom_tzmem_enable() has already succeeded. Use DO_ONCE() to ensure qcom_tzmem_init() runs exactly once across all calls in a thread-safe manner. qcom_tzmem_dev is set on every call since probe retries use the same device pointer. The result of the first initialisation is cached and returned to every subsequent caller. Fixes: 40289e35ca52 ("firmware: qcom: scm: enable the TZ mem allocator") Reviewed-by: Bartosz Golaszewski Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260724094939.613844-4-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_tzmem.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/qcom/qcom_tzmem.c b/drivers/firmware/qcom/qcom_tzmem.c index 0635cbeacfc8..0fd9581275f1 100644 --- a/drivers/firmware/qcom/qcom_tzmem.c +++ b/drivers/firmware/qcom/qcom_tzmem.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -507,14 +508,18 @@ phys_addr_t qcom_tzmem_to_phys(void *vaddr) } EXPORT_SYMBOL_GPL(qcom_tzmem_to_phys); +static void qcom_tzmem_do_init(int *result) +{ + *result = qcom_tzmem_init(); +} + int qcom_tzmem_enable(struct device *dev) { - if (qcom_tzmem_dev) - return -EBUSY; + static int result; qcom_tzmem_dev = dev; - - return qcom_tzmem_init(); + DO_ONCE(qcom_tzmem_do_init, &result); + return result; } EXPORT_SYMBOL_GPL(qcom_tzmem_enable); From 80350cdc5f7e2ddc6739987a78f04b9b7788e593 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 27 Jul 2026 11:59:27 +0200 Subject: [PATCH 105/157] soc: qcom: smem: Use 'unsigned int' instead of 'unsigned' checkpatch.pl reports: WARNING: Prefer 'unsigned int' to bare use of 'unsigned' Convert the SMEM driver to use the less ambiguous type instead. Signed-off-by: Konrad Dybcio Reviewed-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260727-topic-smem_dramc-v5-1-66188b3e338d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smem.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index afb21a778fe7..43b66120fc64 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -281,7 +281,7 @@ struct qcom_smem { struct smem_partition global_partition; struct xarray partitions; - unsigned num_regions; + unsigned int num_regions; struct smem_region regions[] __counted_by(num_regions); }; @@ -399,7 +399,7 @@ EXPORT_SYMBOL_GPL(qcom_smem_is_available); static int qcom_smem_alloc_private(struct qcom_smem *smem, struct smem_partition *part, - unsigned item, + unsigned int item, size_t size) { struct smem_private_entry *hdr, *end; @@ -460,7 +460,7 @@ static int qcom_smem_alloc_private(struct qcom_smem *smem, } static int qcom_smem_alloc_global(struct qcom_smem *smem, - unsigned item, + unsigned int item, size_t size) { struct smem_global_entry *entry; @@ -503,7 +503,7 @@ static int qcom_smem_alloc_global(struct qcom_smem *smem, * * Return: 0 on success, negative errno on failure. */ -int qcom_smem_alloc(unsigned host, unsigned item, size_t size) +int qcom_smem_alloc(unsigned int host, unsigned int item, size_t size) { struct smem_partition *part; unsigned long flags; @@ -544,7 +544,7 @@ int qcom_smem_alloc(unsigned host, unsigned item, size_t size) EXPORT_SYMBOL_GPL(qcom_smem_alloc); static void *qcom_smem_get_global(struct qcom_smem *smem, - unsigned item, + unsigned int item, size_t *size) { struct smem_header *header; @@ -553,7 +553,6 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, u64 entry_offset; u32 e_size; u32 aux_base; - unsigned i; header = smem->regions[0].virt_base; entry = &header->toc[item]; @@ -562,7 +561,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, aux_base = le32_to_cpu(entry->aux_base) & AUX_BASE_MASK; - for (i = 0; i < smem->num_regions; i++) { + for (unsigned int i = 0; i < smem->num_regions; i++) { region = &smem->regions[i]; if ((u32)region->aux_base == aux_base || !aux_base) { @@ -584,7 +583,7 @@ static void *qcom_smem_get_global(struct qcom_smem *smem, static void *qcom_smem_get_private(struct qcom_smem *smem, struct smem_partition *part, - unsigned item, + unsigned int item, size_t *size) { struct smem_private_entry *e, *end; @@ -683,7 +682,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, * * Return: a pointer to an SMEM item on success, ERR_PTR() on failure. */ -void *qcom_smem_get(unsigned host, unsigned item, size_t *size) +void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size) { struct smem_partition *part; void *ptr; @@ -717,12 +716,12 @@ EXPORT_SYMBOL_GPL(qcom_smem_get); * * Return: number of available bytes on success, negative errno on failure. */ -int qcom_smem_get_free_space(unsigned host) +int qcom_smem_get_free_space(unsigned int host) { struct smem_partition *part; struct smem_partition_header *phdr; struct smem_header *header; - unsigned ret; + unsigned int ret; if (IS_ERR(__smem)) return PTR_ERR(__smem); From 1d234eeafc56af8c9af6eee857f41a4310615e78 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 27 Jul 2026 11:59:28 +0200 Subject: [PATCH 106/157] soc: qcom: smem: Expose DDR data from SMEM Most modern Qualcomm platforms (>= SM8150) expose information about the DDR memory present on the system via SMEM. Details from this information is used in various scenarios, such as multimedia drivers configuring the hardware based on the "Highest Bank address Bit" (hbb), or the list of valid frequencies in validation scenarios... Add support for parsing v3-v7 version of the structs. Unforunately, they are not versioned, so some elbow grease is necessary to determine which one is present. See for reference: ver 3: https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commit/1d11897d2cfcc7b85f28ff74c445018dbbecac7a ver 4: https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commit/f6e9aa549260bbc0bdcb156c2b05f48dc5963203 ver 5: https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commit/617d3297abe8b1b8dd3de3d1dd69c3961e6f343f ver 5 with 6regions: https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commit/d770e009f9bae58d56d926f7490bbfb45af8341f ver 6: https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commit/62659b557fdb1551b20fae8073d1d701dfa8a62e ver 7: https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commit/734d95599c5ebb1ca0d4e1639142e65c590532b7 Reviewed-by: Bjorn Andersson Signed-off-by: Konrad Dybcio Link: https://git.codelinaro.org/clo/la/abl/tianocore/edk2/-/commit/1d11897d2cfcc7b85f28ff74c445018dbbecac7a Link: https://lore.kernel.org/r/20260727-topic-smem_dramc-v5-2-66188b3e338d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Makefile | 3 +- drivers/soc/qcom/smem.c | 60 +++-- drivers/soc/qcom/smem.h | 12 + drivers/soc/qcom/smem_dramc.c | 430 ++++++++++++++++++++++++++++++++++ include/linux/soc/qcom/smem.h | 2 + 5 files changed, 485 insertions(+), 22 deletions(-) create mode 100644 drivers/soc/qcom/smem.h create mode 100644 drivers/soc/qcom/smem_dramc.c diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index b7f1d2a57367..798643be3590 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -23,7 +23,8 @@ obj-$(CONFIG_QCOM_RPMH) += qcom_rpmh.o qcom_rpmh-y += rpmh-rsc.o qcom_rpmh-y += rpmh.o obj-$(CONFIG_QCOM_SMD_RPM) += rpm-proc.o smd-rpm.o -obj-$(CONFIG_QCOM_SMEM) += smem.o +qcom_smem-y += smem.o smem_dramc.o +obj-$(CONFIG_QCOM_SMEM) += qcom_smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o CFLAGS_smp2p.o := -I$(src) obj-$(CONFIG_QCOM_SMP2P) += smp2p.o diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 43b66120fc64..b5e7bd8c1512 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -4,6 +4,7 @@ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. */ +#include #include #include #include @@ -16,6 +17,8 @@ #include #include +#include "smem.h" + /* * The Qualcomm shared memory system is a allocate only heap structure that * consists of one of more memory areas that can be accessed by the processors @@ -267,6 +270,7 @@ struct smem_region { * @partitions: list of partitions of current processor/host * @item_count: max accepted item number * @socinfo: platform device pointer + * @debugfs_dir: directory for SMEM-related debugfs attributes * @num_regions: number of @regions * @regions: list of the memory regions defining the shared memory */ @@ -281,6 +285,8 @@ struct qcom_smem { struct smem_partition global_partition; struct xarray partitions; + struct dentry *debugfs_dir; + unsigned int num_regions; struct smem_region regions[] __counted_by(num_regions); }; @@ -671,6 +677,30 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, return ERR_PTR(-EINVAL); } +void *__qcom_smem_get(struct qcom_smem *smem, unsigned int host, unsigned int item, size_t *size) +{ + struct smem_partition *part; + void *ptr; + + if (IS_ERR(smem)) + return smem; + + if (item >= smem->item_count) + return ERR_PTR(-EINVAL); + + part = xa_load(&smem->partitions, host); + if (part) { + ptr = qcom_smem_get_private(smem, part, item, size); + } else if (smem->global_partition.virt_base) { + part = &smem->global_partition; + ptr = qcom_smem_get_private(smem, part, item, size); + } else { + ptr = qcom_smem_get_global(smem, item, size); + } + + return ptr; +} + /** * qcom_smem_get() - resolve ptr of size of a smem item * @host: the remote processor, or -1 @@ -684,26 +714,7 @@ static void *qcom_smem_get_private(struct qcom_smem *smem, */ void *qcom_smem_get(unsigned int host, unsigned int item, size_t *size) { - struct smem_partition *part; - void *ptr; - - if (IS_ERR(__smem)) - return __smem; - - if (item >= __smem->item_count) - return ERR_PTR(-EINVAL); - - part = xa_load(&__smem->partitions, host); - if (part) { - ptr = qcom_smem_get_private(__smem, part, item, size); - } else if (__smem->global_partition.virt_base) { - part = &__smem->global_partition; - ptr = qcom_smem_get_private(__smem, part, item, size); - } else { - ptr = qcom_smem_get_global(__smem, item, size); - } - - return ptr; + return __qcom_smem_get(__smem, host, item, size); } EXPORT_SYMBOL_GPL(qcom_smem_get); @@ -1236,19 +1247,26 @@ static int qcom_smem_probe(struct platform_device *pdev) if (ret < 0 && ret != -ENOENT) return ret; + smem->debugfs_dir = smem_dram_parse(smem, smem->dev); + __smem = smem; smem->socinfo = platform_device_register_data(&pdev->dev, "qcom-socinfo", PLATFORM_DEVID_NONE, NULL, 0); - if (IS_ERR(smem->socinfo)) + if (IS_ERR(smem->socinfo)) { + debugfs_remove_recursive(smem->debugfs_dir); + dev_dbg(&pdev->dev, "failed to register socinfo device\n"); + } return 0; } static void qcom_smem_remove(struct platform_device *pdev) { + debugfs_remove_recursive(__smem->debugfs_dir); + platform_device_unregister(__smem->socinfo); xa_destroy(&__smem->partitions); diff --git a/drivers/soc/qcom/smem.h b/drivers/soc/qcom/smem.h new file mode 100644 index 000000000000..40523eb87231 --- /dev/null +++ b/drivers/soc/qcom/smem.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __QCOM_SMEM_INTERNAL__ +#define __QCOM_SMEM_INTERNAL__ + +#include + +struct qcom_smem; + +struct dentry *smem_dram_parse(struct qcom_smem *smem, struct device *dev); +void *__qcom_smem_get(struct qcom_smem *smem, unsigned int host, unsigned int item, size_t *size); + +#endif diff --git a/drivers/soc/qcom/smem_dramc.c b/drivers/soc/qcom/smem_dramc.c new file mode 100644 index 000000000000..63ebe7f6db59 --- /dev/null +++ b/drivers/soc/qcom/smem_dramc.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smem.h" + +#define SMEM_DDR_INFO_ID 603 + +#define MAX_DDR_FREQ_NUM_V3 13 +#define MAX_DDR_FREQ_NUM_V5 14 + +#define MAX_CHAN_NUM 8 +#define MAX_RANK_NUM 2 + +#define DDR_HBB_MIN 13 +#define DDR_HBB_MAX 19 + +#define MAX_SHUB_ENTRIES 8 + +static struct smem_dram *__dram; + +enum ddr_info_version { + INFO_UNKNOWN, + INFO_V3, + INFO_V3_WITH_14_FREQS, + INFO_V4, + INFO_V5, + INFO_V5_WITH_6_REGIONS, + INFO_V6, /* INFO_V6 seems to only have shipped with 6 DDR regions, unlike V7 */ + INFO_V7, + INFO_V7_WITH_6_REGIONS, +}; + +struct smem_dram { + unsigned long frequencies[MAX_DDR_FREQ_NUM_V5]; + u32 num_frequencies; + u8 hbb; +}; + +enum ddr_type { + DDR_TYPE_NODDR = 0, + DDR_TYPE_LPDDR1 = 1, + DDR_TYPE_LPDDR2 = 2, + DDR_TYPE_PCDDR2 = 3, + DDR_TYPE_PCDDR3 = 4, + DDR_TYPE_LPDDR3 = 5, + DDR_TYPE_LPDDR4 = 6, + DDR_TYPE_LPDDR4X = 7, + DDR_TYPE_LPDDR5 = 8, + DDR_TYPE_LPDDR5X = 9, +}; + +/* The data structures below are NOT __packed on purpose! */ + +/* Structs used across multiple versions */ +struct ddr_part_details { + __le16 revision_id1; + __le16 revision_id2; + __le16 width; + __le16 density; +}; + +struct ddr_freq_table { + __le32 freq_khz; + u8 enabled; +}; + +/* V3 */ +struct ddr_freq_plan_v3 { + struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V3]; + u8 num_ddr_freqs; + phys_addr_t clk_period_address; +}; + +struct ddr_details_v3 { + u8 manufacturer_id; + u8 device_type; + struct ddr_part_details ddr_params[MAX_CHAN_NUM]; + struct ddr_freq_plan_v3 ddr_freq_tbl; + u8 num_channels; +}; + +/* Some V3 structs have an additional frequency level */ +struct ddr_freq_plan_v3_14freqs { + struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V3 + 1]; + u8 num_ddr_freqs; + phys_addr_t clk_period_address; +}; + +struct ddr_details_v3_14freqs { + u8 manufacturer_id; + u8 device_type; + struct ddr_part_details ddr_params[MAX_CHAN_NUM]; + struct ddr_freq_plan_v3_14freqs ddr_freq_tbl; + u8 num_channels; +}; + +/* V4 */ +struct ddr_details_v4 { + u8 manufacturer_id; + u8 device_type; + struct ddr_part_details ddr_params[MAX_CHAN_NUM]; + struct ddr_freq_plan_v3 ddr_freq_tbl; + u8 num_channels; + u8 num_ranks[MAX_CHAN_NUM]; + u8 highest_bank_addr_bit[MAX_CHAN_NUM][MAX_RANK_NUM]; +}; + +/* V5 */ +struct shub_freq_table { + u8 enable; + __le32 freq_khz; +}; + +struct shub_freq_plan_entry { + u8 num_shub_freqs; + struct shub_freq_table shub_freq[MAX_SHUB_ENTRIES]; +}; + +struct ddr_xbl2quantum_smem_data { + phys_addr_t ssr_cookie_addr; + __le32 reserved[10]; +}; + +struct ddr_freq_plan_v5 { + struct ddr_freq_table ddr_freq[MAX_DDR_FREQ_NUM_V5]; + u8 num_ddr_freqs; + phys_addr_t clk_period_address; + __le32 max_nom_ddr_freq; +}; + +struct ddr_region_v5 { + __le64 start_address; + __le64 size; + __le64 mem_controller_address; + __le32 granule_size; /* MiB */ + u8 ddr_rank; +#define DDR_RANK_0 BIT(0) +#define DDR_RANK_1 BIT(1) + u8 segments_start_index; + __le64 segments_start_offset; +}; + +struct ddr_regions_v5 { + __le32 ddr_region_num; /* We expect this to always be 4 or 6 */ + __le64 ddr_rank0_size; + __le64 ddr_rank1_size; + __le64 ddr_cs0_start_addr; + __le64 ddr_cs1_start_addr; + __le32 highest_bank_addr_bit; + struct ddr_region_v5 ddr_region[] __counted_by_le(ddr_region_num); +}; + +struct ddr_details_v5 { + u8 manufacturer_id; + u8 device_type; + struct ddr_part_details ddr_params[MAX_CHAN_NUM]; + struct ddr_freq_plan_v5 ddr_freq_tbl; + u8 num_channels; + u8 _padding; + struct ddr_regions_v5 ddr_regions; +}; + +/* V6 */ +struct ddr_misc_info_v6 { + __le32 dsf_version; + __le32 reserved[10]; +}; + +/* V7 */ +struct ddr_details_v7 { + u8 manufacturer_id; + u8 device_type; + struct ddr_part_details ddr_params[MAX_CHAN_NUM]; + struct ddr_freq_plan_v5 ddr_freq_tbl; + u8 num_channels; + u8 sct_config; + struct ddr_regions_v5 ddr_regions; +}; + +/** + * qcom_smem_dram_get_hbb(): Get the Highest bank address bit + * + * Context: Check qcom_smem_is_available() before calling this function. + * Because __dram * is initialized by smem_dram_parse(), which is in turn + * called from * qcom_smem_probe(), __dram will only be NULL if the data + * couldn't have been found/interpreted correctly. + * + * Return: highest bank bit on success, -ENODATA on failure. + */ +int qcom_smem_dram_get_hbb(void) +{ + if (!__dram || !__dram->hbb) + return -ENODATA; + + if (__dram->hbb < DDR_HBB_MIN || __dram->hbb > DDR_HBB_MAX) + return -ENODATA; + + return __dram->hbb; +} +EXPORT_SYMBOL_GPL(qcom_smem_dram_get_hbb); + +static void smem_dram_parse_v3_data(struct smem_dram *dram, void *data) +{ + struct ddr_details_v3 *details = data; + + for (int i = 0; i < MAX_DDR_FREQ_NUM_V3; i++) { + struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i]; + + if (freq_entry->freq_khz && freq_entry->enabled) { + u32 freq_khz = le32_to_cpu(freq_entry->freq_khz); + dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz; + } + } +} + +static void smem_dram_parse_v3_14freqs_data(struct smem_dram *dram, void *data) +{ + struct ddr_details_v3_14freqs *details = data; + + for (int i = 0; i < MAX_DDR_FREQ_NUM_V3 + 1; i++) { + struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i]; + + if (freq_entry->freq_khz && freq_entry->enabled) + dram->frequencies[dram->num_frequencies++] = 1000 * freq_entry->freq_khz; + } +} + +static void smem_dram_parse_v4_data(struct smem_dram *dram, void *data) +{ + struct ddr_details_v4 *details = data; + + /* Rank 0 channel 0 entry holds the correct value */ + dram->hbb = details->highest_bank_addr_bit[0][0]; + + for (int i = 0; i < MAX_DDR_FREQ_NUM_V3; i++) { + struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i]; + + if (freq_entry->freq_khz && freq_entry->enabled) { + u32 freq_khz = le32_to_cpu(freq_entry->freq_khz); + dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz; + } + } +} + +static void smem_dram_parse_v5_data(struct smem_dram *dram, void *data) +{ + struct ddr_details_v5 *details = data; + struct ddr_regions_v5 *region = &details->ddr_regions; + + dram->hbb = le32_to_cpu(region[0].highest_bank_addr_bit); + + for (int i = 0; i < MAX_DDR_FREQ_NUM_V5; i++) { + struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i]; + + if (freq_entry->freq_khz && freq_entry->enabled) { + u32 freq_khz = le32_to_cpu(freq_entry->freq_khz); + dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz; + } + } +} + +static void smem_dram_parse_v7_data(struct smem_dram *dram, void *data) +{ + struct ddr_details_v7 *details = data; + struct ddr_regions_v5 *region = &details->ddr_regions; + + dram->hbb = le32_to_cpu(region[0].highest_bank_addr_bit); + + for (int i = 0; i < MAX_DDR_FREQ_NUM_V5; i++) { + struct ddr_freq_table *freq_entry = &details->ddr_freq_tbl.ddr_freq[i]; + + if (freq_entry->freq_khz && freq_entry->enabled) { + u32 freq_khz = le32_to_cpu(freq_entry->freq_khz); + dram->frequencies[dram->num_frequencies++] = 1000 * freq_khz; + } + } +} + +/* The structure contains no version field, so we have to perform some guesswork.. */ +static int smem_dram_infer_struct_version(size_t size) +{ + /* Some early versions provided less bytes of less useful data */ + if (size < sizeof(struct ddr_details_v3)) + return -EINVAL; + + if (size == sizeof(struct ddr_details_v3)) + return INFO_V3; + + if (size == sizeof(struct ddr_details_v3_14freqs)) + return INFO_V3_WITH_14_FREQS; + + if (size == sizeof(struct ddr_details_v4)) + return INFO_V4; + + if (size == sizeof(struct ddr_details_v5) + + 4 * sizeof(struct ddr_region_v5)) + return INFO_V5; + + if (size == sizeof(struct ddr_details_v5) + + 4 * sizeof(struct ddr_region_v5) + + sizeof(struct ddr_xbl2quantum_smem_data) + + sizeof(struct shub_freq_plan_entry)) + return INFO_V5; + + if (size == sizeof(struct ddr_details_v5) + + 6 * sizeof(struct ddr_region_v5)) + return INFO_V5_WITH_6_REGIONS; + + if (size == sizeof(struct ddr_details_v5) + + 6 * sizeof(struct ddr_region_v5) + + sizeof(struct ddr_xbl2quantum_smem_data) + + sizeof(struct shub_freq_plan_entry)) + return INFO_V5_WITH_6_REGIONS; + + if (size == sizeof(struct ddr_details_v5) + + 6 * sizeof(struct ddr_region_v5) + + sizeof(struct ddr_misc_info_v6) + + sizeof(struct shub_freq_plan_entry)) + return INFO_V6; + + if (size == sizeof(struct ddr_details_v7) + + 4 * sizeof(struct ddr_region_v5) + + sizeof(struct ddr_misc_info_v6) + + sizeof(struct shub_freq_plan_entry)) + return INFO_V7; + + if (size == sizeof(struct ddr_details_v7) + + 6 * sizeof(struct ddr_region_v5) + + sizeof(struct ddr_misc_info_v6) + + sizeof(struct shub_freq_plan_entry)) + return INFO_V7_WITH_6_REGIONS; + + return INFO_UNKNOWN; +} + +static int smem_dram_frequencies_show(struct seq_file *s, void *unused) +{ + struct smem_dram *dram = s->private; + + for (int i = 0; i < dram->num_frequencies; i++) + seq_printf(s, "%lu\n", dram->frequencies[i]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(smem_dram_frequencies); + +static int smem_hbb_show(struct seq_file *s, void *unused) +{ + struct smem_dram *dram = s->private; + + if (!dram->hbb) + return -EINVAL; + + seq_printf(s, "%d\n", dram->hbb); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(smem_hbb); + +struct dentry *smem_dram_parse(struct qcom_smem *smem, struct device *dev) +{ + struct dentry *debugfs_dir; + enum ddr_info_version ver; + struct smem_dram *dram; + size_t actual_size; + void *data; + + /* No need to check qcom_smem_is_available(), this func is called by the SMEM driver */ + data = __qcom_smem_get(smem, QCOM_SMEM_HOST_ANY, SMEM_DDR_INFO_ID, &actual_size); + if (IS_ERR_OR_NULL(data)) + return ERR_PTR(-ENODATA); + + ver = smem_dram_infer_struct_version(actual_size); + if (ver < 0) { + /* Some SoCs don't provide data that's useful for us */ + return ERR_PTR(-ENODATA); + } else if (ver == INFO_UNKNOWN) { + /* In other cases, we may not have added support for a newer struct revision */ + dev_err(dev, "Found an unknown type of DRAM info struct (size = %zu)\n", + actual_size); + return ERR_PTR(-EINVAL); + } + + dram = devm_kzalloc(dev, sizeof(*dram), GFP_KERNEL); + if (!dram) + return ERR_PTR(-ENOMEM); + + switch (ver) { + case INFO_V3: + smem_dram_parse_v3_data(dram, data); + break; + case INFO_V3_WITH_14_FREQS: + smem_dram_parse_v3_14freqs_data(dram, data); + break; + case INFO_V4: + smem_dram_parse_v4_data(dram, data); + break; + case INFO_V5: + case INFO_V5_WITH_6_REGIONS: + case INFO_V6: + smem_dram_parse_v5_data(dram, data); + break; + case INFO_V7: + case INFO_V7_WITH_6_REGIONS: + smem_dram_parse_v7_data(dram, data); + break; + default: + return ERR_PTR(-EINVAL); + } + + debugfs_dir = debugfs_create_dir("qcom_smem", NULL); + debugfs_create_file("dram_frequencies", 0444, debugfs_dir, dram, + &smem_dram_frequencies_fops); + debugfs_create_file("hbb", 0444, debugfs_dir, dram, &smem_hbb_fops); + + __dram = dram; + + return debugfs_dir; +} diff --git a/include/linux/soc/qcom/smem.h b/include/linux/soc/qcom/smem.h index f946e3beca21..3249cbd2016b 100644 --- a/include/linux/soc/qcom/smem.h +++ b/include/linux/soc/qcom/smem.h @@ -17,4 +17,6 @@ int qcom_smem_get_feature_code(u32 *code); int qcom_smem_bust_hwspin_lock_by_host(unsigned int host); +int qcom_smem_dram_get_hbb(void); + #endif From 1b445022d1d0ec7431b6f8fe0054ae8ccf33d60a Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Mon, 27 Jul 2026 11:59:29 +0200 Subject: [PATCH 107/157] soc: qcom: ubwc: Get HBB from SMEM To make sure the correct settings for a given DRAM configuration get applied, attempt to retrieve that data from SMEM (which happens to be what the BSP kernel does, albeit with through convoluted means of the bootloader altering the DT with this data). Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260727-topic-smem_dramc-v5-3-66188b3e338d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/ubwc_config.c | 40 +++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/ubwc_config.c b/drivers/soc/qcom/ubwc_config.c index 0209a02c390f..23901a4c51f7 100644 --- a/drivers/soc/qcom/ubwc_config.c +++ b/drivers/soc/qcom/ubwc_config.c @@ -11,6 +11,7 @@ #include #include +#include static const struct qcom_ubwc_cfg_data no_ubwc_data = { /* no UBWC, no HBB */ @@ -168,9 +169,25 @@ static const struct of_device_id qcom_ubwc_configs[] __maybe_unused = { { } }; +static struct qcom_ubwc_cfg_data *cfg; +static DEFINE_MUTEX(cfg_mutex); +/** + * qcom_ubwc_config_get_data() - Retrieve UBWC data for the platform. + * + * Return: Pointer to valid struct qcom_ubwc_cfg_data on success, negative + * errno on failure. Note that this may return EPROBE_DEFER. + */ const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void) { const struct qcom_ubwc_cfg_data *data; + int hbb; + + guard(mutex)(&cfg_mutex); + if (cfg) + return cfg; + + if (!qcom_smem_is_available()) + return ERR_PTR(-EPROBE_DEFER); data = of_machine_get_match_data(qcom_ubwc_configs); if (!data) { @@ -178,9 +195,30 @@ const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void) return ERR_PTR(-EINVAL); } - return data; + hbb = qcom_smem_dram_get_hbb(); + if (hbb == -ENODATA) { + /* Lack of HBB data is OK - it was only introduced later */ + return data; + } else if (hbb < 0) { + pr_err("Couldn't get HBB data from SMEM: %d\n", hbb); + return ERR_PTR(hbb); + } + + cfg = kmemdup(data, sizeof(*data), GFP_KERNEL); + if (!cfg) + return ERR_PTR(-ENOMEM); + + cfg->highest_bank_bit = hbb; + + return cfg; } EXPORT_SYMBOL_GPL(qcom_ubwc_config_get_data); +static void __exit ubwc_config_exit(void) +{ + kfree(cfg); +} +module_exit(ubwc_config_exit); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("UBWC config database for QTI SoCs"); From 2d361087e8f662c7846e34451ebe19b42bb86db6 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 30 Jul 2026 14:16:23 +0530 Subject: [PATCH 108/157] soc: qcom: qcom_stats: Remove unused macro definitions Remove macros that are defined but never used, as identified by -Wunused-macros: STAT_TYPE_OFFSET, COUNT_OFFSET, LAST_ENTERED_AT_OFFSET, LAST_EXITED_AT_OFFSET, ACCUMULATED_OFFSET No functional impact. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Maulik Shah Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260730-b4b4-removed-unused-macros-v1-1-2883fecb9e6e@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_stats.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index e7122444f12a..d9c811cb9ef6 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -20,11 +20,6 @@ #define RPM_DYNAMIC_ADDR 0x14 #define RPM_DYNAMIC_ADDR_MASK 0xFFFF -#define STAT_TYPE_OFFSET 0x0 -#define COUNT_OFFSET 0x4 -#define LAST_ENTERED_AT_OFFSET 0x8 -#define LAST_EXITED_AT_OFFSET 0x10 -#define ACCUMULATED_OFFSET 0x18 #define CLIENT_VOTES_OFFSET 0x20 #define DDR_STATS_MAGIC_KEY 0xA1157A75 From 8463421f20f67168132fb1b47571741266d872fb Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 30 Jul 2026 14:16:24 +0530 Subject: [PATCH 109/157] soc: qcom: qcom_stats: Replace CLIENT_VOTES_OFFSET macro with sizeof() CLIENT_VOTES_OFFSET (0x20) equals sizeof(struct sleep_stats), since appended_stats immediately follows sleep_stats in the memory layout. Replace the magic constant with sizeof(struct sleep_stats) to make the layout relationship self-documenting and drop the macro. No functional impact. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Maulik Shah Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260730-b4b4-removed-unused-macros-v1-2-2883fecb9e6e@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_stats.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/qcom/qcom_stats.c b/drivers/soc/qcom/qcom_stats.c index d9c811cb9ef6..06923408bfce 100644 --- a/drivers/soc/qcom/qcom_stats.c +++ b/drivers/soc/qcom/qcom_stats.c @@ -20,8 +20,6 @@ #define RPM_DYNAMIC_ADDR 0x14 #define RPM_DYNAMIC_ADDR_MASK 0xFFFF -#define CLIENT_VOTES_OFFSET 0x20 - #define DDR_STATS_MAGIC_KEY 0xA1157A75 #define DDR_STATS_MAX_NUM_MODES 20 #define DDR_STATS_MAGIC_KEY_ADDR 0x0 @@ -135,7 +133,7 @@ static int qcom_soc_sleep_stats_show(struct seq_file *s, void *unused) if (d->appended_stats_avail) { struct appended_stats votes; - memcpy_fromio(&votes, reg + CLIENT_VOTES_OFFSET, sizeof(votes)); + memcpy_fromio(&votes, reg + sizeof(struct sleep_stats), sizeof(votes)); seq_printf(s, "Client Votes: %#x\n", votes.client_votes); } From a20f7b1fe71167b34b77b748eed6a2476d6206c0 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Thu, 25 Jun 2026 00:38:13 +0530 Subject: [PATCH 110/157] dt-bindings: sram: qcom,imem: Add minidump-sram pattern property The qcom,imem binding describes a single word in always-on SRAM shared between the operating system (OS) and boot firmware. Before DDR is initialized on the warm reset following a crash, firmware reads this word to decide where to deliver the minidump, and the OS is expected to select one of the destinations: either USB upload to a host PC or save to on-device storage. If nothing is selected by the OS, USB is the default. Signed-off-by: Mukesh Ojha Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20260624190830.3131112-3-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/sram/qcom,imem.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/sram/qcom,imem.yaml b/Documentation/devicetree/bindings/sram/qcom,imem.yaml index c63026904061..17adced6d3a4 100644 --- a/Documentation/devicetree/bindings/sram/qcom,imem.yaml +++ b/Documentation/devicetree/bindings/sram/qcom,imem.yaml @@ -67,6 +67,22 @@ properties: $ref: /schemas/power/reset/syscon-reboot-mode.yaml# patternProperties: + "^minidump-sram@[0-9a-f]+$": + type: object + description: + A word in always-on SRAM shared between the kernel and boot firmware. + Before DDR is initialised on the warm reset following a crash, firmware + reads this word to decide where to deliver the minidump (USB or storage). + + properties: + reg: + maxItems: 1 + + required: + - reg + + additionalProperties: false + "^modem-tables@[0-9a-f]+$": type: object description: From a60558ae6562edc95317eae60dc5e989493a278d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 29 Jul 2026 10:35:06 +0800 Subject: [PATCH 111/157] dt-bindings: soc: qcom,aoss-qmp: Document Nord AOSS side channel Document Always-on Subsystem side channel on Qualcomm Nord SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20260729023508.879752-2-shengchao.guo@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml index 9f9d9bcb66c4..d2ae2ae87a95 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml @@ -31,6 +31,7 @@ properties: - qcom,kaanapali-aoss-qmp - qcom,maili-aoss-qmp - qcom,milos-aoss-qmp + - qcom,nord-aoss-qmp - qcom,qcs615-aoss-qmp - qcom,qcs8300-aoss-qmp - qcom,qdu1000-aoss-qmp From b46a43e6c68860ebf843c2138e30553c84083ff7 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Thu, 23 Jul 2026 10:37:58 +0800 Subject: [PATCH 112/157] soc/tegra: cbb: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Mikko Perttunen Signed-off-by: Thierry Reding --- drivers/soc/tegra/cbb/tegra194-cbb.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/soc/tegra/cbb/tegra194-cbb.c b/drivers/soc/tegra/cbb/tegra194-cbb.c index 2f69e104c838..69ef929e04f8 100644 --- a/drivers/soc/tegra/cbb/tegra194-cbb.c +++ b/drivers/soc/tegra/cbb/tegra194-cbb.c @@ -2072,20 +2072,15 @@ static int tegra194_cbb_interrupt_enable(struct tegra_cbb *cbb) if (priv->sec_irq) { err = devm_request_irq(dev, priv->sec_irq, tegra194_cbb_err_isr, 0, dev_name(dev), priv); - if (err) { - dev_err(dev, "failed to register interrupt %u: %d\n", priv->sec_irq, err); + if (err) return err; - } } if (priv->nonsec_irq) { err = devm_request_irq(dev, priv->nonsec_irq, tegra194_cbb_err_isr, 0, dev_name(dev), priv); - if (err) { - dev_err(dev, "failed to register interrupt %u: %d\n", priv->nonsec_irq, - err); + if (err) return err; - } } return 0; From 43e7ae814c981f0ecdc365746a752a094415a8b7 Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Thu, 14 May 2026 10:42:52 +0530 Subject: [PATCH 113/157] soc/tegra: fuse: Do not use fuse data as entropy source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fuse values are static and device-unique but not secret. Using them as an entropy source weakens the overall randomness pool because the data is constant and externally observable over the device’s lifetime. Stop feeding fuse data into the kernel’s randomness subsystem. Signed-off-by: Kartik Rajput Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra30.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index 524fa1b0cd3d..2fad8b13a3b9 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -64,28 +64,6 @@ static u32 tegra30_fuse_read(struct tegra_fuse *fuse, unsigned int offset) return value; } -static void __init tegra30_fuse_add_randomness(void) -{ - u32 randomness[12]; - - randomness[0] = tegra_sku_info.sku_id; - randomness[1] = tegra_read_straps(); - randomness[2] = tegra_read_chipid(); - randomness[3] = tegra_sku_info.cpu_process_id << 16; - randomness[3] |= tegra_sku_info.soc_process_id; - randomness[4] = tegra_sku_info.cpu_speedo_id << 16; - randomness[4] |= tegra_sku_info.soc_speedo_id; - randomness[5] = tegra_fuse_read_early(FUSE_VENDOR_CODE); - randomness[6] = tegra_fuse_read_early(FUSE_FAB_CODE); - randomness[7] = tegra_fuse_read_early(FUSE_LOT_CODE_0); - randomness[8] = tegra_fuse_read_early(FUSE_LOT_CODE_1); - randomness[9] = tegra_fuse_read_early(FUSE_WAFER_ID); - randomness[10] = tegra_fuse_read_early(FUSE_X_COORDINATE); - randomness[11] = tegra_fuse_read_early(FUSE_Y_COORDINATE); - - add_device_randomness(randomness, sizeof(randomness)); -} - static void __init tegra30_fuse_init(struct tegra_fuse *fuse) { fuse->read_early = tegra30_fuse_read_early; @@ -95,8 +73,6 @@ static void __init tegra30_fuse_init(struct tegra_fuse *fuse) if (fuse->soc->speedo_init) fuse->soc->speedo_init(&tegra_sku_info); - - tegra30_fuse_add_randomness(); } #endif From 863515d1eb678c7c17bb4cc19819ba2992400022 Mon Sep 17 00:00:00 2001 From: Kartik Rajput Date: Thu, 14 May 2026 11:05:18 +0530 Subject: [PATCH 114/157] soc/tegra: fuse: Use device-specific instance in suspend/resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid using the default fuse instance in suspend/resume callbacks, as it always runs the suspend/resume callbacks for the default instance rather than the device’s own. Get the correct fuse instance by using `dev_get_drvdata()`. Signed-off-by: Kartik Rajput Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 071cd9620634..77a54124e9f4 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -259,6 +259,7 @@ static int tegra_fuse_probe(struct platform_device *pdev) static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev) { + struct tegra_fuse *fuse = dev_get_drvdata(dev); int err; err = clk_prepare_enable(fuse->clk); @@ -272,6 +273,8 @@ static int __maybe_unused tegra_fuse_runtime_resume(struct device *dev) static int __maybe_unused tegra_fuse_runtime_suspend(struct device *dev) { + struct tegra_fuse *fuse = dev_get_drvdata(dev); + clk_disable_unprepare(fuse->clk); return 0; @@ -279,6 +282,7 @@ static int __maybe_unused tegra_fuse_runtime_suspend(struct device *dev) static int __maybe_unused tegra_fuse_suspend(struct device *dev) { + struct tegra_fuse *fuse = dev_get_drvdata(dev); int ret; /* @@ -295,6 +299,7 @@ static int __maybe_unused tegra_fuse_suspend(struct device *dev) static int __maybe_unused tegra_fuse_resume(struct device *dev) { + struct tegra_fuse *fuse = dev_get_drvdata(dev); int ret = 0; if (fuse->soc->clk_suspend_on) From 97ffe9cb67ccafb4644f820ef68662217b76c224 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Sat, 25 Apr 2026 20:03:04 -0400 Subject: [PATCH 115/157] soc/tegra: Add PM dependency to SOC_TEGRA_PMC Kconfig kconfiglint reports: K006: config SOC_TEGRA_PMC selects PM_GENERIC_DOMAINS which depends on PM, but SOC_TEGRA_PMC does not depend on PM SOC_TEGRA_PMC is an internal bool symbol (no prompt) that is selected by ARCH_TEGRA_*_SOC options to enable the Power Management Controller driver. It was originally introduced in commit 5e7d4c652941 ("soc/tegra: Implement Tegra186 PMC support") as a bare `bool` with no selects of its own. Over time, additional selects were added: - Commit 5098e2b95e8e ("soc/tegra: pmc: Select GENERIC_PINCONF") added select GENERIC_PINCONF - Commit 28dbe8231066 ("soc/tegra: pmc: Select IRQ_DOMAIN_HIERARCHY") added select IRQ_DOMAIN_HIERARCHY - Commit f880ee9e9688 ("soc/tegra: pmc: Add core power domain") added select PM_OPP and select PM_GENERIC_DOMAINS to support core power domain voltage scaling PM_GENERIC_DOMAINS is defined in kernel/power/Kconfig as: config PM_GENERIC_DOMAINS bool depends on PM When commit f880ee9e9688 ("soc/tegra: pmc: Add core power domain") added the select, it did not add a corresponding `depends on PM` to SOC_TEGRA_PMC. This was not a functional problem because all callers of SOC_TEGRA_PMC are inside `if ARCH_TEGRA` blocks, and both the ARM32 definition (arch/arm/mach-tegra/Kconfig) and the ARM64 definition (arch/arm64/Kconfig.platforms) of ARCH_TEGRA select PM. Thus PM is always enabled when SOC_TEGRA_PMC is selected in practice. However, the dependency chain is implicit rather than explicit. Add `depends on PM` to SOC_TEGRA_PMC to make the requirement explicit and prevent a theoretical misconfiguration if SOC_TEGRA_PMC were ever selected outside the ARCH_TEGRA context. Assisted-by: Claude:claude-opus-4-6 kconfiglint Signed-off-by: Sasha Levin Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 073346c1542b..8b6fdb9ad1ea 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -171,6 +171,7 @@ config SOC_TEGRA_FLOWCTRL config SOC_TEGRA_PMC bool + depends on PM select GENERIC_PINCONF select IRQ_DOMAIN_HIERARCHY select PM_OPP From e2414b289c2b68afab361def612ca3791cd70d12 Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Thu, 30 Jul 2026 14:29:36 +0200 Subject: [PATCH 116/157] soc: fsl: qe: properly scan GPIO nodes at startup Before commit 156460811def ("soc: fsl: qe: Change GPIO driver to a proper platform driver") qe_add_gpiochips() was walking the device tree to find all nodes with compatible "fsl,mpc8323-qe-pario-bank". After that commit the discovery is handled by the platform core, therefore it is necessary to call of_platform_default_populate() on the par_io node. Fixes: 156460811def ("soc: fsl: qe: Change GPIO driver to a proper platform driver") Reviewed-by: Herve Codina Link: https://lore.kernel.org/r/a1db12ef75bf881dd5fba893a37db0c8517eca1b.1785414349.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_io.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/soc/fsl/qe/qe_io.c b/drivers/soc/fsl/qe/qe_io.c index a5e2d0e5ab51..150913fce981 100644 --- a/drivers/soc/fsl/qe/qe_io.c +++ b/drivers/soc/fsl/qe/qe_io.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -184,3 +185,17 @@ int par_io_of_config(struct device_node *np) return 0; } EXPORT_SYMBOL(par_io_of_config); + +static int __init par_io_populate(void) +{ + struct device_node *np = of_find_node_by_type(NULL, "par_io"); + + if (!np) + return 0; + + of_platform_default_populate(np, NULL, NULL); + of_node_put(np); + + return 0; +} +arch_initcall(par_io_populate); From 4175a6c0e964dfbe53557b9a74a9ec1005fe5303 Mon Sep 17 00:00:00 2001 From: David Laight Date: Mon, 8 Jun 2026 10:54:56 +0100 Subject: [PATCH 117/157] drivers/bus/fsl-mc: Use strscpy() to copy strings into arrays Replacing strcpy() with strscpy() ensures than overflow of the target buffer cannot happen. Signed-off-by: David Laight Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260608095500.2567-1-david.laight.linux@gmail.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 64d75eed0d34..8c203cd47534 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -908,7 +908,7 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev, int state, err; mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); - strcpy(endpoint1.type, mc_dev->obj_desc.type); + strscpy(endpoint1.type, mc_dev->obj_desc.type); endpoint1.id = mc_dev->obj_desc.id; endpoint1.if_id = if_id; @@ -925,7 +925,7 @@ struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev, return ERR_PTR(err); } - strcpy(endpoint_desc.type, endpoint2.type); + strscpy(endpoint_desc.type, endpoint2.type); endpoint_desc.id = endpoint2.id; endpoint = fsl_mc_device_lookup(&endpoint_desc, mc_bus_dev); if (endpoint) From 20a8c2530247be7ab4b5cb8896a5897e66324d42 Mon Sep 17 00:00:00 2001 From: Roy Pledge Date: Wed, 28 Sep 2022 11:25:29 -0400 Subject: [PATCH 118/157] MAINTAINERS: Update drivers/soc/fsl/dpio maintainer Update the drivers/soc/fsl/dpio maintainer to be Ioana Ciornei Signed-off-by: Roy Pledge Acked-by: Ioana Ciornei Link: https://lore.kernel.org/r/1664378729-24815-1-git-send-email-roy.pledge@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..664d5f956650 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7823,7 +7823,7 @@ F: Documentation/devicetree/bindings/media/i2c/dongwoon,dw9807-vcm.yaml F: drivers/media/i2c/dw9807-vcm.c DPAA2 DATAPATH I/O (DPIO) DRIVER -M: Roy Pledge +M: Ioana Ciornei L: linux-kernel@vger.kernel.org S: Maintained F: drivers/soc/fsl/dpio From 92eed60bf3c4d1beea686f51c10a81293417b8c4 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Thu, 23 Jul 2026 10:47:48 +0800 Subject: [PATCH 119/157] soc: fsl: dpio: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Acked-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260723024748.30105-1-panchuang@vivo.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/dpio/dpio-driver.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/soc/fsl/dpio/dpio-driver.c b/drivers/soc/fsl/dpio/dpio-driver.c index 9e3fddd8f5a9..d5198ffc4455 100644 --- a/drivers/soc/fsl/dpio/dpio-driver.c +++ b/drivers/soc/fsl/dpio/dpio-driver.c @@ -102,12 +102,8 @@ static int register_dpio_irq_handlers(struct fsl_mc_device *dpio_dev, int cpu) 0, dev_name(&dpio_dev->dev), &dpio_dev->dev); - if (error < 0) { - dev_err(&dpio_dev->dev, - "devm_request_irq() failed: %d\n", - error); + if (error < 0) return error; - } /* set the affinity hint */ if (irq_set_affinity_hint(irq->virq, cpumask_of(cpu))) From e460ef309f44b39480209970f1dd462f051d6f30 Mon Sep 17 00:00:00 2001 From: "Christophe Leroy (CS GROUP)" Date: Thu, 30 Jul 2026 12:09:40 +0200 Subject: [PATCH 120/157] soc: fsl: qe: implement get_direction() The lack of get_direction() callback in this driver causes GPIOLIB to emit a warning. Implement it. Fixes: e623c4303ed1 ("gpiolib: sanitize the return value of gpio_chip::get_direction()") Reviewed-by: Bartosz Golaszewski Link: https://lore.kernel.org/r/30b3f278a10b46252783458c81dc438df176f86c.1785405882.git.chleroy@kernel.org Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/gpio.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index 66828f2a3577..6d8f4d549fe2 100644 --- a/drivers/soc/fsl/qe/gpio.c +++ b/drivers/soc/fsl/qe/gpio.c @@ -135,6 +135,30 @@ static int qe_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } +static int qe_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); + struct qe_pio_regs __iomem *regs = qe_gc->regs; + unsigned long flags; + u32 val, mask; + + spin_lock_irqsave(&qe_gc->lock, flags); + + if (gpio < QE_PIO_PINS / 2) + val = ioread32be(®s->cpdir1); + else + val = ioread32be(®s->cpdir2); + + spin_unlock_irqrestore(&qe_gc->lock, flags); + + mask = (u32)QE_PIO_DIR_OUT << (QE_PIO_PINS - 2 - (gpio % (QE_PIO_PINS / 2)) * 2); + + if (val & mask) + return GPIO_LINE_DIRECTION_OUT; + else + return GPIO_LINE_DIRECTION_IN; +} + struct qe_pin { /* * The qe_gpio_chip name is unfortunate, we should change that to @@ -308,6 +332,7 @@ static int qe_gpio_probe(struct platform_device *ofdev) gc->ngpio = QE_PIO_PINS; gc->direction_input = qe_gpio_dir_in; gc->direction_output = qe_gpio_dir_out; + gc->get_direction = qe_gpio_get_direction; gc->get = qe_gpio_get; gc->set = qe_gpio_set; gc->set_multiple = qe_gpio_set_multiple; From 7e47fe56415847732419ca8cef9307bf111dd368 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:14 +0200 Subject: [PATCH 121/157] soc: fsl: qe: Add chained_irq_{enter,exit}() calls in cascade handler Wrap the cascade handler body with chained_irq_{enter,exit}() to properly inform the parent IRQ chip that a chained interrupt is being serviced. Fixes: f0bcd784e1b76 ("soc: fsl: qe: Add an interrupt controller for QUICC Engine Ports") Signed-off-by: Paul Louvel Cc: stable@kernel.org Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-1-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 33ca1ddafe18..c3768c82a58c 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -82,7 +83,13 @@ static int qepic_get_irq(struct irq_desc *desc) static void qepic_cascade(struct irq_desc *desc) { + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + generic_handle_irq(qepic_get_irq(desc)); + + chained_irq_exit(chip, desc); } static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) From 890af4d0464ef10f61a30c69295075f2a6e09a1a Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:15 +0200 Subject: [PATCH 122/157] dt-bindings: soc: fsl: qe: Set #interrupt-cells to 2 to support interrupt type encoding The QUICC Engine port interrupt controller can be configured to generate an interrupt on either a high-to-low transition or any change in the signal state on the related GPIOs. Update the #interrupt-cells property to 2 so consumers can encode interrupt level information. Acked-by: Krzysztof Kozlowski Signed-off-by: Paul Louvel Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-2-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- .../bindings/interrupt-controller/fsl,qe-ports-ic.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml b/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml index 2b8e7b9c6d7a..2b7c6b4f0389 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/fsl,qe-ports-ic.yaml @@ -23,7 +23,7 @@ properties: const: 0 '#interrupt-cells': - const: 1 + const: 2 interrupts: maxItems: 1 @@ -45,7 +45,7 @@ examples: reg = <0xc00 0x18>; interrupt-controller; #address-cells = <0>; - #interrupt-cells = <1>; + #interrupt-cells = <2>; interrupts = <74 0x8>; interrupt-parent = <&ipic>; }; From b6a375b38c279a22149b08426e80a0947baa2eb2 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Wed, 8 Jul 2026 12:15:16 +0200 Subject: [PATCH 123/157] dt-bindings: soc: fsl: qe: Convert QE GPIO to DT schema Convert QE GPIO devicetree binding to DT schema. The old binding uses fsl,-qe-pario-bank because multiple MCP83XX SoC has support for these GPIO banks. The best practice is to list out every instead. Signed-off-by: Christophe Leroy Signed-off-by: Paul Louvel Reviewed-by: Krzysztof Kozlowski Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-3-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- .../gpio/fsl,mpc8323-qe-pario-bank.yaml | 45 +++++++++++++++++++ .../bindings/soc/fsl/cpm_qe/qe/par_io.txt | 26 +---------- 2 files changed, 46 insertions(+), 25 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml diff --git a/Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml b/Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml new file mode 100644 index 000000000000..1af99339ff40 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/fsl,mpc8323-qe-pario-bank.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale QUICC Engine Parallel I/O (QE PARIO) GPIO Bank + +maintainers: + - Christophe Leroy + +properties: + compatible: + oneOf: + - items: + - enum: + - fsl,mpc8360-qe-pario-bank + - fsl,mpc8569-qe-pario-bank + - const: fsl,mpc8323-qe-pario-bank + - const: fsl,mpc8323-qe-pario-bank + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + gpio-controller@1400 { + compatible = "fsl,mpc8360-qe-pario-bank", "fsl,mpc8323-qe-pario-bank"; + reg = <0x1400 0x18>; + gpio-controller; + #gpio-cells = <2>; + }; diff --git a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe/par_io.txt b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe/par_io.txt index 09b1b05fa677..782699c14567 100644 --- a/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe/par_io.txt +++ b/Documentation/devicetree/bindings/soc/fsl/cpm_qe/qe/par_io.txt @@ -24,28 +24,4 @@ par_io@1400 { Note that "par_io" nodes are obsolete, and should not be used for the new device trees. Instead, each Par I/O bank should be represented -via its own gpio-controller node: - -Required properties: -- #gpio-cells : should be "2". -- compatible : should be "fsl,-qe-pario-bank", - "fsl,mpc8323-qe-pario-bank". -- reg : offset to the register set and its length. -- gpio-controller : node to identify gpio controllers. - -Example: - qe_pio_a: gpio-controller@1400 { - #gpio-cells = <2>; - compatible = "fsl,mpc8360-qe-pario-bank", - "fsl,mpc8323-qe-pario-bank"; - reg = <0x1400 0x18>; - gpio-controller; - }; - - qe_pio_e: gpio-controller@1460 { - #gpio-cells = <2>; - compatible = "fsl,mpc8360-qe-pario-bank", - "fsl,mpc8323-qe-pario-bank"; - reg = <0x1460 0x18>; - gpio-controller; - }; +via its own gpio-controller node. From 5ddc1624b3aca9c63712f3a5be578c6c3b222627 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:17 +0200 Subject: [PATCH 124/157] dt-bindings: soc: fsl: qe: Add support of IRQ in QE GPIO Some QE GPIO pins have an associated interrupt line in the QE PIC to signal state changes on the pin. Add the corresponding interrupt-controller / nexus properties to the QE GPIO binding. Because the GPIO controller does not perform any interrupt handling itself, a nexus node (interrupt-map) is used to map each GPIO line supporting IRQ to the parent QE PIC interrupt domain. As the QE PIC can be configured to generate an interrupt on either a high-to-low transition or any change in signal state, three interrupt-map entries are needed per GPIO pin that can yield an interrupt (falling, both, and the "none" case which defaults to both in QE PIC). This overhead is necessary because the interrupt-map-pass-thru property is not part of the DT specification. The interrupt-map property is optional: it is not required for GPIO banks that have no interrupt capable GPIO line (e.g. port D on MPC8323), or when interrupt functionality is not used. Update the example to show a scenario where each bank supports a different numbers of IRQs, or no IRQs at all. Signed-off-by: Paul Louvel Reviewed-by: Krzysztof Kozlowski Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-4-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- .../gpio/fsl,mpc8323-qe-pario-bank.yaml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml b/Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml index 1af99339ff40..1d2ab44fcd3c 100644 --- a/Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl,mpc8323-qe-pario-bank.yaml @@ -27,6 +27,17 @@ properties: "#gpio-cells": const: 2 + "#address-cells": + const: 0 + + "#interrupt-cells": + const: 2 + + interrupt-map: + description: | + Specifies the mapping of GPIO lines to the parent interrupt controller, as the + GPIO controller does not do interrupt handling itself. + required: - compatible - reg @@ -37,9 +48,37 @@ additionalProperties: false examples: - | + #include + gpio-controller@1400 { compatible = "fsl,mpc8360-qe-pario-bank", "fsl,mpc8323-qe-pario-bank"; reg = <0x1400 0x18>; gpio-controller; #gpio-cells = <2>; }; + + gpio-controller@1418 { + compatible = "fsl,mpc8323-qe-pario-bank"; + reg = <0x1418 0x18>; + gpio-controller; + #gpio-cells = <2>; + #address-cells = <0>; + #interrupt-cells = <2>; + interrupt-map = < + 7 IRQ_TYPE_EDGE_FALLING &pic 4 IRQ_TYPE_EDGE_FALLING + 7 IRQ_TYPE_EDGE_BOTH &pic 4 IRQ_TYPE_EDGE_BOTH + 7 0 &pic 4 IRQ_TYPE_NONE + + 9 IRQ_TYPE_EDGE_FALLING &pic 5 IRQ_TYPE_EDGE_FALLING + 9 IRQ_TYPE_EDGE_BOTH &pic 5 IRQ_TYPE_EDGE_BOTH + 9 0 &pic 5 IRQ_TYPE_NONE + + 25 IRQ_TYPE_EDGE_FALLING &pic 6 IRQ_TYPE_EDGE_FALLING + 25 IRQ_TYPE_EDGE_BOTH &pic 6 IRQ_TYPE_EDGE_BOTH + 25 0 &pic 6 IRQ_TYPE_NONE + + 27 IRQ_TYPE_EDGE_FALLING &pic 7 IRQ_TYPE_EDGE_FALLING + 27 IRQ_TYPE_EDGE_BOTH &pic 7 IRQ_TYPE_EDGE_BOTH + 27 0 &pic 7 IRQ_TYPE_NONE + >; + }; From 9727108464115732a9957f268be510a916a13c07 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:18 +0200 Subject: [PATCH 125/157] soc: fsl: qe: Use generic_handle_domain_irq() Replace the irq_find_mapping() + generic_handle_irq() pattern with generic_handle_domain_irq(), which handles the IRQ domain lookup internally. This is less error-prone and more idiomatic. Remove the now-unused irq_find_mapping() call from qepic_get_irq(). Signed-off-by: Paul Louvel Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-5-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index c3768c82a58c..c8fff45e353c 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -78,16 +78,17 @@ static int qepic_get_irq(struct irq_desc *desc) if (!event) return -1; - return irq_find_mapping(data->host, 32 - ffs(event)); + return 32 - ffs(event); } static void qepic_cascade(struct irq_desc *desc) { + struct qepic_data *data = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); chained_irq_enter(chip, desc); - generic_handle_irq(qepic_get_irq(desc)); + generic_handle_domain_irq(data->host, qepic_get_irq(desc)); chained_irq_exit(chip, desc); } From 0887d5f68c9c3413acf4d1e083dd83f1be4205ee Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:19 +0200 Subject: [PATCH 126/157] soc: fsl: qe: Iterate over all pending interrupts in cascade handler Instead of only servicing a single interrupt, the chained handler can handle all IRQs that have their bit set in the event register. This avoid multiple parent IRQ handler being serviced if more than one interrupt are pending on the QE PIC. Remove unused code. Signed-off-by: Paul Louvel Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-6-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index c8fff45e353c..bc8b68e5d1a9 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -5,6 +5,7 @@ * Copyright (c) 2025 Christophe Leroy CS GROUP France (christophe.leroy@csgroup.eu) */ +#include #include #include #include @@ -70,25 +71,17 @@ static struct irq_chip qepic = { .irq_set_type = qepic_set_type, }; -static int qepic_get_irq(struct irq_desc *desc) -{ - struct qepic_data *data = irq_desc_get_handler_data(desc); - u32 event = ioread32be(data->reg + CEPIER); - - if (!event) - return -1; - - return 32 - ffs(event); -} - static void qepic_cascade(struct irq_desc *desc) { struct qepic_data *data = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); + unsigned long event, bit; chained_irq_enter(chip, desc); - generic_handle_domain_irq(data->host, qepic_get_irq(desc)); + event = ioread32be(data->reg + CEPIER); + for_each_set_bit(bit, &event, 32) + generic_handle_domain_irq(data->host, 31 - bit); chained_irq_exit(chip, desc); } From feaa9dc2a792e1d0766960368f911bc6b4feaa20 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:20 +0200 Subject: [PATCH 127/157] soc: fsl: qe: Handle spurious interrupts When no interrupt bits are set in the event register, call handle_bad_irq() to account for the spurious interrupt before exiting the cascade handler. Signed-off-by: Paul Louvel Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-7-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index bc8b68e5d1a9..29f4334db5a0 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -80,9 +80,15 @@ static void qepic_cascade(struct irq_desc *desc) chained_irq_enter(chip, desc); event = ioread32be(data->reg + CEPIER); + if (!event) { + handle_bad_irq(desc); + goto out; + } + for_each_set_bit(bit, &event, 32) generic_handle_domain_irq(data->host, 31 - bit); +out: chained_irq_exit(chip, desc); } From 159dae1b6b76d34b0e55c486a48cfd05ddf82f72 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:21 +0200 Subject: [PATCH 128/157] soc: fsl: qe: Convert to generic IRQ chip The generic IRQ chip framework is available to handle IRQ chips. Using this framework for the QE interrupt controller allows to simplify the driver. Indeed, the framework internally handles operations coded directly in the driver. Signed-off-by: Paul Louvel Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-8-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/Kconfig | 1 + drivers/soc/fsl/qe/qe_ports_ic.c | 118 ++++++++++++++++++++----------- 2 files changed, 77 insertions(+), 42 deletions(-) diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index b35a8fd30ebf..87cd1662e168 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -71,6 +71,7 @@ config QE_USB config QE_GPIO bool "QE GPIO support" depends on QUICC_ENGINE + select GENERIC_IRQ_CHIP select GPIOLIB help Say Y here if you're going to use hardware that connects to the diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 29f4334db5a0..4f6b75439f9f 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -18,107 +18,144 @@ struct qepic_data { void __iomem *reg; - struct irq_domain *host; int irq; }; static void qepic_mask(struct irq_data *d) { - struct qepic_data *data = irq_data_get_irq_chip_data(d); - u32 val = ioread32be(data->reg + CEPIMR); + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + u32 val; - iowrite32be(val & ~(1 << (31 - irqd_to_hwirq(d))), data->reg + CEPIMR); + guard(raw_spinlock)(&gc->lock); + + val = ioread32be(gc->reg_base + CEPIMR); + iowrite32be(val & ~d->mask, gc->reg_base + CEPIMR); } static void qepic_unmask(struct irq_data *d) { - struct qepic_data *data = irq_data_get_irq_chip_data(d); - u32 val = ioread32be(data->reg + CEPIMR); + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + u32 val; - iowrite32be(val | 1 << (31 - irqd_to_hwirq(d)), data->reg + CEPIMR); + guard(raw_spinlock)(&gc->lock); + + val = ioread32be(gc->reg_base + CEPIMR); + iowrite32be(val | d->mask, gc->reg_base + CEPIMR); } static void qepic_end(struct irq_data *d) { - struct qepic_data *data = irq_data_get_irq_chip_data(d); + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - iowrite32be(1 << (31 - irqd_to_hwirq(d)), data->reg + CEPIER); + iowrite32be(d->mask, gc->reg_base + CEPIER); +} + +static void qepic_calc_mask(struct irq_data *d) +{ + d->mask = 1 << (31 - irqd_to_hwirq(d)); } static int qepic_set_type(struct irq_data *d, unsigned int flow_type) { - struct qepic_data *data = irq_data_get_irq_chip_data(d); - unsigned int vec = (unsigned int)irqd_to_hwirq(d); - u32 val = ioread32be(data->reg + CEPICR); + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + u32 val; + guard(raw_spinlock)(&gc->lock); + + val = ioread32be(gc->reg_base + CEPICR); switch (flow_type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_FALLING: - iowrite32be(val | 1 << (31 - vec), data->reg + CEPICR); + iowrite32be(val | d->mask, gc->reg_base + CEPICR); return 0; case IRQ_TYPE_EDGE_BOTH: case IRQ_TYPE_NONE: - iowrite32be(val & ~(1 << (31 - vec)), data->reg + CEPICR); + iowrite32be(val & ~d->mask, gc->reg_base + CEPICR); return 0; } return -EINVAL; } -static struct irq_chip qepic = { - .name = "QEPIC", - .irq_mask = qepic_mask, - .irq_unmask = qepic_unmask, - .irq_eoi = qepic_end, - .irq_set_type = qepic_set_type, -}; - static void qepic_cascade(struct irq_desc *desc) { - struct qepic_data *data = irq_desc_get_handler_data(desc); + struct irq_domain *domain = irq_desc_get_handler_data(desc); + struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); struct irq_chip *chip = irq_desc_get_chip(desc); unsigned long event, bit; chained_irq_enter(chip, desc); - event = ioread32be(data->reg + CEPIER); + event = ioread32be(gc->reg_base + CEPIER); if (!event) { handle_bad_irq(desc); goto out; } for_each_set_bit(bit, &event, 32) - generic_handle_domain_irq(data->host, 31 - bit); + generic_handle_domain_irq(domain, 31 - bit); out: chained_irq_exit(chip, desc); } -static int qepic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) +static int qepic_chip_init(struct irq_chip_generic *gc) { - irq_set_chip_data(virq, h->host_data); - irq_set_chip_and_handler(virq, &qepic, handle_fasteoi_irq); + struct qepic_data *data = gc->domain->host_data; + struct irq_chip_type *ct = gc->chip_types; + + gc->reg_base = data->reg; + + ct->chip.irq_mask = qepic_mask; + ct->chip.irq_unmask = qepic_unmask; + ct->chip.irq_eoi = qepic_end; + ct->chip.irq_set_type = qepic_set_type; + ct->chip.irq_calc_mask = qepic_calc_mask; + return 0; } -static const struct irq_domain_ops qepic_host_ops = { - .map = qepic_host_map, -}; - -static void qepic_remove(void *res) +static int qepic_domain_init(struct irq_domain *d) { - struct qepic_data *data = res; + struct qepic_data *data = d->host_data; + + irq_set_chained_handler_and_data(data->irq, qepic_cascade, d); + + return 0; +} + +static void qepic_domain_exit(struct irq_domain *d) +{ + struct qepic_data *data = d->host_data; irq_set_chained_handler_and_data(data->irq, NULL, NULL); - irq_domain_remove(data->host); } static int qepic_probe(struct platform_device *pdev) { + struct irq_domain_chip_generic_info dgc_info = { + .name = "QEPIC", + .handler = handle_fasteoi_irq, + .irqs_per_chip = 32, + .num_ct = 1, + .init = qepic_chip_init, + }; + struct irq_domain_info d_info = { + .fwnode = of_fwnode_handle(pdev->dev.of_node), + .domain_flags = IRQ_DOMAIN_FLAG_DESTROY_GC, + .size = 32, + .hwirq_max = 32, + .ops = &irq_generic_chip_ops, + .dgc_info = &dgc_info, + .init = qepic_domain_init, + .exit = qepic_domain_exit, + }; struct device *dev = &pdev->dev; + struct irq_domain *domain; struct qepic_data *data; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + d_info.host_data = data; data->reg = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->reg)) @@ -128,14 +165,11 @@ static int qepic_probe(struct platform_device *pdev) if (data->irq < 0) return data->irq; - data->host = irq_domain_create_linear(dev_fwnode(dev), 32, &qepic_host_ops, data); - if (!data->host) - return -ENODEV; - - irq_set_chained_handler_and_data(data->irq, qepic_cascade, data); - - return devm_add_action_or_reset(dev, qepic_remove, data); + domain = devm_irq_domain_instantiate(dev, &d_info); + if (IS_ERR(domain)) + return PTR_ERR(domain); + return 0; } static const struct of_device_id qepic_match[] = { From 17ee1c7e48a36d4e03a24092674abfd07751c0f2 Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:22 +0200 Subject: [PATCH 129/157] soc: fsl: qe: Rename irq variable to parent_irq Rename the local variable holding the platform IRQ to parent_irq, which better describes its role as the upstream/chained interrupt in the hierarchy. Signed-off-by: Paul Louvel Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-9-1972044cfbd1@bootlin.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ports_ic.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ports_ic.c b/drivers/soc/fsl/qe/qe_ports_ic.c index 4f6b75439f9f..7375f92f528b 100644 --- a/drivers/soc/fsl/qe/qe_ports_ic.c +++ b/drivers/soc/fsl/qe/qe_ports_ic.c @@ -18,7 +18,7 @@ struct qepic_data { void __iomem *reg; - int irq; + int parent_irq; }; static void qepic_mask(struct irq_data *d) @@ -117,7 +117,7 @@ static int qepic_domain_init(struct irq_domain *d) { struct qepic_data *data = d->host_data; - irq_set_chained_handler_and_data(data->irq, qepic_cascade, d); + irq_set_chained_handler_and_data(data->parent_irq, qepic_cascade, d); return 0; } @@ -126,7 +126,7 @@ static void qepic_domain_exit(struct irq_domain *d) { struct qepic_data *data = d->host_data; - irq_set_chained_handler_and_data(data->irq, NULL, NULL); + irq_set_chained_handler_and_data(data->parent_irq, NULL, NULL); } static int qepic_probe(struct platform_device *pdev) @@ -161,9 +161,9 @@ static int qepic_probe(struct platform_device *pdev) if (IS_ERR(data->reg)) return PTR_ERR(data->reg); - data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) - return data->irq; + data->parent_irq = platform_get_irq(pdev, 0); + if (data->parent_irq < 0) + return data->parent_irq; domain = devm_irq_domain_instantiate(dev, &d_info); if (IS_ERR(domain)) From 244b7f86d60544c99f6e3774fbf0e1aafd3a30f4 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Sat, 4 Jul 2026 20:24:42 +0800 Subject: [PATCH 130/157] soc: aspeed: add missing MODULE_DEVICE_TABLE() The driver has an OF match table wired to .of_match_table, but does not export the table with MODULE_DEVICE_TABLE(). Add the missing MODULE_DEVICE_TABLE(of, ...) entry so module alias information is generated for OF based module autoloading. This is a source-level fix. It does not claim dynamic hardware reproduction; the evidence is the driver-owned match table, its use by the platform driver, and the missing module alias publication. Signed-off-by: Pengpeng Hou Signed-off-by: Andrew Jeffery --- drivers/soc/aspeed/aspeed-uart-routing.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/aspeed/aspeed-uart-routing.c b/drivers/soc/aspeed/aspeed-uart-routing.c index 0191e36e66e1..b4b521b5ddf1 100644 --- a/drivers/soc/aspeed/aspeed-uart-routing.c +++ b/drivers/soc/aspeed/aspeed-uart-routing.c @@ -582,6 +582,7 @@ static const struct of_device_id aspeed_uart_routing_table[] = { .data = &ast2600_uart_routing_attr_group }, { }, }; +MODULE_DEVICE_TABLE(of, aspeed_uart_routing_table); static struct platform_driver aspeed_uart_routing_driver = { .driver = { From 94d3305d990b055e1561e3b78275344b1f40c86e Mon Sep 17 00:00:00 2001 From: Sumit Garg Date: Thu, 2 Jul 2026 17:28:25 +0530 Subject: [PATCH 131/157] media: qcom: Switch to generic PAS TZ APIs Switch qcom media client drivers over to generic PAS TZ APIs. Generic PAS TZ service allows to support multiple TZ implementation backends like QTEE based SCM PAS service, OP-TEE based PAS service and any further future TZ backend service. Reviewed-by: Mukesh Ojha Tested-by: Mukesh Ojha # Lemans Reviewed-by: Konrad Dybcio Signed-off-by: Sumit Garg Link: https://lore.kernel.org/r/20260702115835.167602-10-sumit.garg@kernel.org Signed-off-by: Bjorn Andersson --- drivers/media/platform/qcom/iris/Kconfig | 27 ++++++++++--------- .../media/platform/qcom/iris/iris_firmware.c | 9 ++++--- drivers/media/platform/qcom/venus/Kconfig | 1 + drivers/media/platform/qcom/venus/firmware.c | 11 ++++---- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig index af78a1775937..388c9bbc8136 100644 --- a/drivers/media/platform/qcom/iris/Kconfig +++ b/drivers/media/platform/qcom/iris/Kconfig @@ -1,14 +1,15 @@ config VIDEO_QCOM_IRIS - tristate "Qualcomm iris V4L2 decoder driver" - depends on VIDEO_DEV - depends on ARCH_QCOM || COMPILE_TEST - select V4L2_MEM2MEM_DEV - select QCOM_MDT_LOADER - select QCOM_SCM - select QCOM_UBWC_CONFIG - select VIDEOBUF2_DMA_CONTIG - help - This is a V4L2 driver for Qualcomm iris video accelerator - hardware. It accelerates decoding operations on various - Qualcomm SoCs. - To compile this driver as a module choose m here. + tristate "Qualcomm iris V4L2 decoder driver" + depends on VIDEO_DEV + depends on ARCH_QCOM || COMPILE_TEST + select V4L2_MEM2MEM_DEV + select QCOM_MDT_LOADER + select QCOM_SCM + select QCOM_PAS + select QCOM_UBWC_CONFIG + select VIDEOBUF2_DMA_CONTIG + help + This is a V4L2 driver for Qualcomm iris video accelerator + hardware. It accelerates decoding operations on various + Qualcomm SoCs. + To compile this driver as a module choose m here. diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c index 1a476146d758..ea9654dd679e 100644 --- a/drivers/media/platform/qcom/iris/iris_firmware.c +++ b/drivers/media/platform/qcom/iris/iris_firmware.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -80,7 +81,7 @@ int iris_fw_load(struct iris_core *core) return -ENOMEM; } - ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID); + ret = qcom_pas_auth_and_reset(IRIS_PAS_ID); if (ret) { dev_err(core->dev, "auth and reset failed: %d\n", ret); return ret; @@ -94,7 +95,7 @@ int iris_fw_load(struct iris_core *core) cp_config->cp_nonpixel_size); if (ret) { dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret); - qcom_scm_pas_shutdown(IRIS_PAS_ID); + qcom_pas_shutdown(IRIS_PAS_ID); return ret; } } @@ -104,10 +105,10 @@ int iris_fw_load(struct iris_core *core) int iris_fw_unload(struct iris_core *core) { - return qcom_scm_pas_shutdown(IRIS_PAS_ID); + return qcom_pas_shutdown(IRIS_PAS_ID); } int iris_set_hw_state(struct iris_core *core, bool resume) { - return qcom_scm_set_remote_state(resume, 0); + return qcom_pas_set_remote_state(resume, 0); } diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig index 63ee8c78dc6d..7997b8aa427a 100644 --- a/drivers/media/platform/qcom/venus/Kconfig +++ b/drivers/media/platform/qcom/venus/Kconfig @@ -6,6 +6,7 @@ config VIDEO_QCOM_VENUS select OF_DYNAMIC if ARCH_QCOM select QCOM_MDT_LOADER select QCOM_SCM + select QCOM_PAS select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 1de7436713ed..3a38ff985822 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -58,7 +59,7 @@ int venus_set_hw_state(struct venus_core *core, bool resume) int ret; if (core->use_tz) { - ret = qcom_scm_set_remote_state(resume, 0); + ret = qcom_pas_set_remote_state(resume, 0); if (resume && ret == -EINVAL) ret = 0; return ret; @@ -218,7 +219,7 @@ int venus_boot(struct venus_core *core) int ret; if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) || - (core->use_tz && !qcom_scm_is_available())) + (core->use_tz && !qcom_pas_is_available())) return -EPROBE_DEFER; ret = of_property_read_string_index(dev->of_node, "firmware-name", 0, @@ -236,7 +237,7 @@ int venus_boot(struct venus_core *core) core->fw.mem_phys = mem_phys; if (core->use_tz) - ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID); + ret = qcom_pas_auth_and_reset(VENUS_PAS_ID); else ret = venus_boot_no_tz(core, mem_phys, mem_size); @@ -259,7 +260,7 @@ int venus_boot(struct venus_core *core) res->cp_nonpixel_start, res->cp_nonpixel_size); if (ret) { - qcom_scm_pas_shutdown(VENUS_PAS_ID); + qcom_pas_shutdown(VENUS_PAS_ID); dev_err(dev, "set virtual address ranges fail (%d)\n", ret); return ret; @@ -274,7 +275,7 @@ int venus_shutdown(struct venus_core *core) int ret; if (core->use_tz) - ret = qcom_scm_pas_shutdown(VENUS_PAS_ID); + ret = qcom_pas_shutdown(VENUS_PAS_ID); else ret = venus_shutdown_no_tz(core); From 1c351103ce64b0b8eb4d1c1b9a2706e28c3486ae Mon Sep 17 00:00:00 2001 From: Paul Louvel Date: Wed, 8 Jul 2026 12:15:23 +0200 Subject: [PATCH 132/157] soc: fsl: qe: Add support of IRQs in QE GPIO Some QE GPIO pins have an associated interrupt line in the QE PIC to signal state changes on the pin. Because the GPIO controller does not perform any interrupt handling itself, a nexus node (interrupt-map) is used to map each GPIO line supporting IRQ to the parent QE PIC interrupt domain. Add the to_irq() method in the corresponding GPIO controller driver, that uses the nexus node to perform the translation. Signed-off-by: Paul Louvel Reviewed-by: Christophe Leroy (CS GROUP) Link: https://lore.kernel.org/r/20260708-qe-pic-gpios-v2-10-1972044cfbd1@bootlin.com [chleroy: Added dependency on !SPARC to fix build on sparc reported by 0-day kernel robot] Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/Kconfig | 1 + drivers/soc/fsl/qe/gpio.c | 28 +++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/soc/fsl/qe/Kconfig b/drivers/soc/fsl/qe/Kconfig index 87cd1662e168..9e2350551bb1 100644 --- a/drivers/soc/fsl/qe/Kconfig +++ b/drivers/soc/fsl/qe/Kconfig @@ -71,6 +71,7 @@ config QE_USB config QE_GPIO bool "QE GPIO support" depends on QUICC_ENGINE + depends on !SPARC # Should be OF_IRQ but it leads to circ. dependency select GENERIC_IRQ_CHIP select GPIOLIB help diff --git a/drivers/soc/fsl/qe/gpio.c b/drivers/soc/fsl/qe/gpio.c index 6d8f4d549fe2..5cd59cfbf22f 100644 --- a/drivers/soc/fsl/qe/gpio.c +++ b/drivers/soc/fsl/qe/gpio.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ #define PIN_MASK(gpio) (1UL << (QE_PIO_PINS - 1 - (gpio))) struct qe_gpio_chip { + struct device_node *np; struct gpio_chip gc; void __iomem *regs; spinlock_t lock; @@ -159,6 +161,29 @@ static int qe_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) return GPIO_LINE_DIRECTION_IN; } +static int qe_gpio_to_irq(struct gpio_chip *gc, unsigned int gpio) +{ + struct qe_gpio_chip *qe_gc = gpiochip_get_data(gc); + struct of_phandle_args oirq; + struct irq_domain *domain; + int ret; + + oirq.np = qe_gc->np; + oirq.args_count = 2; + oirq.args[0] = gpio; + oirq.args[1] = 0; + + ret = of_irq_parse_raw(NULL, &oirq); + if (ret) + return ret; + + domain = irq_find_host(oirq.np); + if (!domain) + return -EPROBE_DEFER; + + return irq_create_of_mapping(&oirq); +} + struct qe_pin { /* * The qe_gpio_chip name is unfortunate, we should change that to @@ -323,7 +348,7 @@ static int qe_gpio_probe(struct platform_device *ofdev) qe_gc = devm_kzalloc(dev, sizeof(*qe_gc), GFP_KERNEL); if (!qe_gc) return -ENOMEM; - + qe_gc->np = np; spin_lock_init(&qe_gc->lock); gc = &qe_gc->gc; @@ -336,6 +361,7 @@ static int qe_gpio_probe(struct platform_device *ofdev) gc->get = qe_gpio_get; gc->set = qe_gpio_set; gc->set_multiple = qe_gpio_set_multiple; + gc->to_irq = qe_gpio_to_irq; gc->parent = dev; gc->owner = THIS_MODULE; From 4b7a470f99bd19b31fc3fdeb3008d8699d05558b Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Fri, 10 Jul 2026 19:09:26 +0800 Subject: [PATCH 133/157] bus: fsl-mc: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_threaded_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260710110930.462109-2-panchuang@vivo.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/dprc-driver.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index a85706826fa0..a81188cb06f2 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -521,12 +521,8 @@ static int register_dprc_irq_handler(struct fsl_mc_device *mc_dev) IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(&mc_dev->dev), &mc_dev->dev); - if (error < 0) { - dev_err(&mc_dev->dev, - "devm_request_threaded_irq() failed: %d\n", - error); + if (error < 0) return error; - } return 0; } From 29a5302f8f3888e41d5fd1ea1838b30d2f3f2eb6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 27 Jul 2026 17:49:24 -0700 Subject: [PATCH 134/157] soc: fsl: fix kernel-doc warnings and typos Correct spelling of "list". Fix a kernel-doc warning by describing the nested structure completely: include/soc/fsl/dpaa2-fd.h:52: warning: Function parameter or member 'simple' not described in 'dpaa2_fd' Signed-off-by: Randy Dunlap Cc: Li Yang Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-arm-kernel@lists.infradead.org Cc: Frank Li Cc: Guanhua Gao Cc: Roy Pledge Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260728004924.904210-1-rdunlap@infradead.org Signed-off-by: Christophe Leroy (CS GROUP) --- include/soc/fsl/dpaa2-fd.h | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/include/soc/fsl/dpaa2-fd.h b/include/soc/fsl/dpaa2-fd.h index bae490cac0aa..0ca0766b2b0d 100644 --- a/include/soc/fsl/dpaa2-fd.h +++ b/include/soc/fsl/dpaa2-fd.h @@ -25,14 +25,15 @@ /** * struct dpaa2_fd - Struct describing FDs - * @words: for easier/faster copying the whole FD structure - * @addr: address in the FD - * @len: length in the FD - * @bpid: buffer pool ID - * @format_offset: format, offset, and short-length fields - * @frc: frame context - * @ctrl: control bits...including dd, sc, va, err, etc - * @flc: flow context address + * @words: for easier/faster copying the whole FD structure + * @simple: struct for the FD fields + * @simple.addr: address in the FD + * @simple.len: length in the FD + * @simple.bpid: buffer pool ID + * @simple.format_offset: format, offset, and short-length fields + * @simple.frc: frame context + * @simple.ctrl: control bits...including dd, sc, va, err, etc + * @simple.flc: flow context address * * This structure represents the basic Frame Descriptor used in the system. */ @@ -497,7 +498,7 @@ static inline void dpaa2_fl_set_addr(struct dpaa2_fl_entry *fle, * dpaa2_fl_get_frc() - Get the frame context in the FLE * @fle: the given frame list entry * - * Return the frame context field in the frame lsit entry. + * Return the frame context field in the frame list entry. */ static inline u32 dpaa2_fl_get_frc(const struct dpaa2_fl_entry *fle) { From a1924557ed24f2e9ef3550364b15fb8257830bb7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 27 Jul 2026 17:49:38 -0700 Subject: [PATCH 135/157] soc: fsl: dpio: fix kernel-doc typos Correct spelling of 2 words. Signed-off-by: Randy Dunlap Cc: Li Yang Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-arm-kernel@lists.infradead.org Cc: Frank Li Cc: Guanhua Gao Cc: Roy Pledge Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260728004938.905415-1-rdunlap@infradead.org Signed-off-by: Christophe Leroy (CS GROUP) --- include/soc/fsl/dpaa2-io.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/soc/fsl/dpaa2-io.h b/include/soc/fsl/dpaa2-io.h index 4bf62de2e00e..2a748fafa5b7 100644 --- a/include/soc/fsl/dpaa2-io.h +++ b/include/soc/fsl/dpaa2-io.h @@ -22,7 +22,7 @@ struct device; * DOC: DPIO Service * * The DPIO service provides APIs for users to interact with the datapath - * by enqueueing and dequeing frame descriptors. + * by enqueueing and dequeueing frame descriptors. * * The following set of APIs can be used to enqueue and dequeue frames * as well as producing notification callbacks when data is available @@ -33,7 +33,7 @@ struct device; /** * struct dpaa2_io_desc - The DPIO descriptor - * @receives_notifications: Use notificaton mode. Non-zero if the DPIO + * @receives_notifications: Use notification mode. Non-zero if the DPIO * has a channel. * @has_8prio: Set to non-zero for channel with 8 priority WQs. Ignored * unless receives_notification is TRUE. From d2fb943cba02142e92236ae7250193af40bae077 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 22 Jul 2026 02:15:55 +0300 Subject: [PATCH 136/157] soc: fsl: guts: perform fsl_guts_init() error teardown in reverse order of setup fsl_guts_init() is about to get much more complicated and the central error handling procedure cannot scale in its current design, unless we add a lot of "if" conditions to detect what has been allocated and what hasn't. Currently the code relies on the fact that kfree(NULL) is safe, but this doesn't scale to the case where "soc_dev_attr" itself is NULL, because this would dereference "soc_dev_attr->family" and friends of a NULL pointer. Convert to the more typical error handling pattern where the teardown is in the strict reverse order of setup, and a teardown step is only called if its corresponding setup step was executed. At the same time, maintain the optionality of soc_dev_attr->serial_number by not checking whether that kasprintf() has returned NULL. In the error path, kfree(NULL) is safe, so we don't need to add an "if" condition for it. Michael Walle has confirmed that ignoring the error was intentional, and we preserve that: https://lore.kernel.org/linux-phy/DK44809N7Y8I.J2Z3U4N32H0Q@kernel.org/ Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260721231603.67865-2-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/guts.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 9bee7baec2b9..453456f31800 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -227,17 +227,23 @@ static int __init fsl_guts_init(void) } else { soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ"); } - if (!soc_dev_attr->family) - goto err_nomem; + if (!soc_dev_attr->family) { + ret = -ENOMEM; + goto err_free_soc_dev_attr; + } soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", svr); - if (!soc_dev_attr->soc_id) - goto err_nomem; + if (!soc_dev_attr->soc_id) { + ret = -ENOMEM; + goto err_free_family; + } soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d", (svr >> 4) & 0xf, svr & 0xf); - if (!soc_dev_attr->revision) - goto err_nomem; + if (!soc_dev_attr->revision) { + ret = -ENOMEM; + goto err_free_soc_id; + } if (soc_data) soc_uid = fsl_guts_get_soc_uid(soc_data->sfp_compat, @@ -249,7 +255,7 @@ static int __init fsl_guts_init(void) soc_dev = soc_device_register(soc_dev_attr); if (IS_ERR(soc_dev)) { ret = PTR_ERR(soc_dev); - goto err; + goto err_free_serial_number; } pr_info("Machine: %s\n", soc_dev_attr->machine); @@ -259,13 +265,14 @@ static int __init fsl_guts_init(void) return 0; -err_nomem: - ret = -ENOMEM; -err: - kfree(soc_dev_attr->family); - kfree(soc_dev_attr->soc_id); - kfree(soc_dev_attr->revision); +err_free_serial_number: kfree(soc_dev_attr->serial_number); + kfree(soc_dev_attr->revision); +err_free_soc_id: + kfree(soc_dev_attr->soc_id); +err_free_family: + kfree(soc_dev_attr->family); +err_free_soc_dev_attr: kfree(soc_dev_attr); return ret; From 450e2f6a5391d7a9dd1433ddacd67fceefdfc99f Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Wed, 22 Jul 2026 02:15:56 +0300 Subject: [PATCH 137/157] soc: fsl: guts: use a macro to encode the DCFG CCSR space Instead of using a hardcoded value when iomapping the DCFG CCSR space, add a new macro for it. The code will be easier to follow this way, especially when we add support for the DCFG DCSR space as well. Signed-off-by: Ioana Ciornei Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20260721231603.67865-3-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/guts.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 453456f31800..b97eb80cea95 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -14,6 +14,8 @@ #include #include +#define DCFG_CCSR 0 + struct fsl_soc_die_attr { char *die; u32 svr; @@ -197,7 +199,7 @@ static int __init fsl_guts_init(void) return 0; soc_data = match->data; - regs = of_iomap(np, 0); + regs = of_iomap(np, DCFG_CCSR); if (!regs) { of_node_put(np); return -ENOMEM; From c559ba052a6f73e921e37a988eb48a40a4ea0947 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Wed, 22 Jul 2026 02:15:57 +0300 Subject: [PATCH 138/157] soc: fsl: guts: add a global structure to hold state Add the fsl_soc_guts structure in order to pass information like base addresses, endianness etc between the init time and the runtime operations (RCW override) which will get added in future patches. There is no point in mapping and unmapping the DCFG CCSR space every time we need to make a read, just map it once and keep its reference in this new global structure. Signed-off-by: Ioana Ciornei Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20260721231603.67865-4-vladimir.oltean@nxp.com [chleroy: fixed typo on 'structure' in commit message] Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/guts.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index b97eb80cea95..bb65b62ed805 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -106,6 +106,11 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { { }, }; +static struct fsl_soc_guts { + struct ccsr_guts __iomem *dcfg_ccsr; + bool little_endian; +} soc; + static const struct fsl_soc_die_attr *fsl_soc_die_match( u32 svr, const struct fsl_soc_die_attr *matches) { @@ -187,9 +192,7 @@ static int __init fsl_guts_init(void) const struct fsl_soc_die_attr *soc_die; const struct fsl_soc_data *soc_data; const struct of_device_id *match; - struct ccsr_guts __iomem *regs; struct device_node *np; - bool little_endian; u64 soc_uid = 0; u32 svr; int ret; @@ -199,24 +202,25 @@ static int __init fsl_guts_init(void) return 0; soc_data = match->data; - regs = of_iomap(np, DCFG_CCSR); - if (!regs) { + soc.dcfg_ccsr = of_iomap(np, DCFG_CCSR); + if (!soc.dcfg_ccsr) { of_node_put(np); return -ENOMEM; } - little_endian = of_property_read_bool(np, "little-endian"); - if (little_endian) - svr = ioread32(®s->svr); + soc.little_endian = of_property_read_bool(np, "little-endian"); + if (soc.little_endian) + svr = ioread32(&soc.dcfg_ccsr->svr); else - svr = ioread32be(®s->svr); - iounmap(regs); + svr = ioread32be(&soc.dcfg_ccsr->svr); of_node_put(np); /* Register soc device */ soc_dev_attr = kzalloc_obj(*soc_dev_attr); - if (!soc_dev_attr) - return -ENOMEM; + if (!soc_dev_attr) { + ret = -ENOMEM; + goto err_unmap_dcfg_ccsr; + } ret = soc_attr_read_machine(soc_dev_attr); if (ret) @@ -276,6 +280,9 @@ static int __init fsl_guts_init(void) kfree(soc_dev_attr->family); err_free_soc_dev_attr: kfree(soc_dev_attr); +err_unmap_dcfg_ccsr: + iounmap(soc.dcfg_ccsr); + soc.dcfg_ccsr = NULL; return ret; } From 35faff569fc02750b6581180b10645d253afa10b Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Wed, 22 Jul 2026 02:15:58 +0300 Subject: [PATCH 139/157] soc: fsl: guts: add a central fsl_guts_read() function Add a central fsl_guts_read() function which will take into account the endianness that was already determined. No point is duplicating the if-else statement each time we need to read a DCFG register. Signed-off-by: Ioana Ciornei Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20260721231603.67865-5-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/guts.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index bb65b62ed805..8e5c3cae811e 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -111,6 +111,14 @@ static struct fsl_soc_guts { bool little_endian; } soc; +static unsigned int fsl_guts_read(const void __iomem *reg) +{ + if (soc.little_endian) + return ioread32(reg); + + return ioread32be(reg); +} + static const struct fsl_soc_die_attr *fsl_soc_die_match( u32 svr, const struct fsl_soc_die_attr *matches) { @@ -209,10 +217,7 @@ static int __init fsl_guts_init(void) } soc.little_endian = of_property_read_bool(np, "little-endian"); - if (soc.little_endian) - svr = ioread32(&soc.dcfg_ccsr->svr); - else - svr = ioread32be(&soc.dcfg_ccsr->svr); + svr = fsl_guts_read(&soc.dcfg_ccsr->svr); of_node_put(np); /* Register soc device */ From 5f331a987fb76fea3afdbf874afb0d7a496b919c Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Wed, 22 Jul 2026 02:15:59 +0300 Subject: [PATCH 140/157] soc: fsl: guts: make it easier to determine on which SoC we are running The guts driver will need to easily determine on which SoC it's running when it will need to perform RCW override at runtime. The guts driver knows this already because fsl_guts_init() reads the QorIQ/Layerscape architectural System Version Register (SVR), but it doesn't save this for later lookups. Add a new qoriq_die enum to be used as an index in the fsl_soc_die array. A new fsl_soc_die_match_one() function is also added so that we can directly determine if the SVR is a match with a specific die. The SVR value read from the DCFG CCSR is also kept in the global soc structure so that it can be accessed when needed. Signed-off-by: Ioana Ciornei Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20260721231603.67865-6-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/guts.c | 47 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 8e5c3cae811e..15674c6734c6 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -27,6 +27,23 @@ struct fsl_soc_data { u32 uid_offset; }; +enum qoriq_die { + DIE_T4240, + DIE_T1040, + DIE_T2080, + DIE_T1024, + DIE_LS1043A, + DIE_LS2080A, + DIE_LS1088A, + DIE_LS1012A, + DIE_LS1046A, + DIE_LS2088A, + DIE_LS1021A, + DIE_LX2160A, + DIE_LS1028A, + DIE_MAX, +}; + /* SoC die attribute definition for QorIQ platform */ static const struct fsl_soc_die_attr fsl_soc_die[] = { /* @@ -34,21 +51,25 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { */ /* Die: T4240, SoC: T4240/T4160/T4080 */ + [DIE_T4240] = { .die = "T4240", .svr = 0x82400000, .mask = 0xfff00000, }, /* Die: T1040, SoC: T1040/T1020/T1042/T1022 */ + [DIE_T1040] = { .die = "T1040", .svr = 0x85200000, .mask = 0xfff00000, }, /* Die: T2080, SoC: T2080/T2081 */ + [DIE_T2080] = { .die = "T2080", .svr = 0x85300000, .mask = 0xfff00000, }, /* Die: T1024, SoC: T1024/T1014/T1023/T1013 */ + [DIE_T1024] = { .die = "T1024", .svr = 0x85400000, .mask = 0xfff00000, @@ -59,46 +80,55 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { */ /* Die: LS1043A, SoC: LS1043A/LS1023A */ + [DIE_LS1043A] = { .die = "LS1043A", .svr = 0x87920000, .mask = 0xffff0000, }, /* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */ + [DIE_LS2080A] = { .die = "LS2080A", .svr = 0x87010000, .mask = 0xff3f0000, }, /* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */ + [DIE_LS1088A] = { .die = "LS1088A", .svr = 0x87030000, .mask = 0xff3f0000, }, /* Die: LS1012A, SoC: LS1012A */ + [DIE_LS1012A] = { .die = "LS1012A", .svr = 0x87040000, .mask = 0xffff0000, }, /* Die: LS1046A, SoC: LS1046A/LS1026A */ + [DIE_LS1046A] = { .die = "LS1046A", .svr = 0x87070000, .mask = 0xffff0000, }, /* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */ + [DIE_LS2088A] = { .die = "LS2088A", .svr = 0x87090000, .mask = 0xff3f0000, }, /* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A */ + [DIE_LS1021A] = { .die = "LS1021A", .svr = 0x87000000, .mask = 0xfff70000, }, /* Die: LX2160A, SoC: LX2160A/LX2120A/LX2080A */ + [DIE_LX2160A] = { .die = "LX2160A", .svr = 0x87360000, .mask = 0xff3f0000, }, /* Die: LS1028A, SoC: LS1028A */ + [DIE_LS1028A] = { .die = "LS1028A", .svr = 0x870b0000, .mask = 0xff3f0000, @@ -109,6 +139,7 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { static struct fsl_soc_guts { struct ccsr_guts __iomem *dcfg_ccsr; bool little_endian; + u32 svr; } soc; static unsigned int fsl_guts_read(const void __iomem *reg) @@ -119,11 +150,16 @@ static unsigned int fsl_guts_read(const void __iomem *reg) return ioread32be(reg); } +static bool fsl_soc_die_match_one(u32 svr, const struct fsl_soc_die_attr *match) +{ + return match->svr == (svr & match->mask); +} + static const struct fsl_soc_die_attr *fsl_soc_die_match( u32 svr, const struct fsl_soc_die_attr *matches) { while (matches->svr) { - if (matches->svr == (svr & matches->mask)) + if (fsl_soc_die_match_one(svr, matches)) return matches; matches++; } @@ -202,7 +238,6 @@ static int __init fsl_guts_init(void) const struct of_device_id *match; struct device_node *np; u64 soc_uid = 0; - u32 svr; int ret; np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match); @@ -217,7 +252,7 @@ static int __init fsl_guts_init(void) } soc.little_endian = of_property_read_bool(np, "little-endian"); - svr = fsl_guts_read(&soc.dcfg_ccsr->svr); + soc.svr = fsl_guts_read(&soc.dcfg_ccsr->svr); of_node_put(np); /* Register soc device */ @@ -231,7 +266,7 @@ static int __init fsl_guts_init(void) if (ret) of_machine_read_compatible(&soc_dev_attr->machine, 0); - soc_die = fsl_soc_die_match(svr, fsl_soc_die); + soc_die = fsl_soc_die_match(soc.svr, fsl_soc_die); if (soc_die) { soc_dev_attr->family = kasprintf(GFP_KERNEL, "QorIQ %s", soc_die->die); @@ -243,14 +278,14 @@ static int __init fsl_guts_init(void) goto err_free_soc_dev_attr; } - soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", svr); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "svr:0x%08x", soc.svr); if (!soc_dev_attr->soc_id) { ret = -ENOMEM; goto err_free_family; } soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d.%d", - (svr >> 4) & 0xf, svr & 0xf); + (soc.svr >> 4) & 0xf, soc.svr & 0xf); if (!soc_dev_attr->revision) { ret = -ENOMEM; goto err_free_soc_id; From 01a658660126b58562906bd4b368a742a2a92390 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 22 Jul 2026 02:16:00 +0300 Subject: [PATCH 141/157] soc: fsl: guts: make fsl_soc_data available after fsl_guts_init() In a future change, struct fsl_soc_data will be extended with methods for performing RCW override. Since this will be performed from a calling context outside fsl_guts_init(), we need to keep track of the soc_data that we determine at fsl_guts_init() time, so we can reference it later. Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260721231603.67865-7-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/guts.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 15674c6734c6..c283d44b68a3 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -138,6 +138,7 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { static struct fsl_soc_guts { struct ccsr_guts __iomem *dcfg_ccsr; + const struct fsl_soc_data *data; bool little_endian; u32 svr; } soc; @@ -231,10 +232,9 @@ static const struct of_device_id fsl_guts_of_match[] = { static int __init fsl_guts_init(void) { - struct soc_device_attribute *soc_dev_attr; + struct soc_device_attribute *soc_dev_attr = NULL; static struct soc_device *soc_dev; const struct fsl_soc_die_attr *soc_die; - const struct fsl_soc_data *soc_data; const struct of_device_id *match; struct device_node *np; u64 soc_uid = 0; @@ -243,12 +243,13 @@ static int __init fsl_guts_init(void) np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match); if (!np) return 0; - soc_data = match->data; + soc.data = match->data; soc.dcfg_ccsr = of_iomap(np, DCFG_CCSR); if (!soc.dcfg_ccsr) { of_node_put(np); - return -ENOMEM; + ret = -ENOMEM; + goto err_clear_soc_data; } soc.little_endian = of_property_read_bool(np, "little-endian"); @@ -291,9 +292,9 @@ static int __init fsl_guts_init(void) goto err_free_soc_id; } - if (soc_data) - soc_uid = fsl_guts_get_soc_uid(soc_data->sfp_compat, - soc_data->uid_offset); + if (soc.data) + soc_uid = fsl_guts_get_soc_uid(soc.data->sfp_compat, + soc.data->uid_offset); if (soc_uid) soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, "%016llX", soc_uid); @@ -323,6 +324,8 @@ static int __init fsl_guts_init(void) err_unmap_dcfg_ccsr: iounmap(soc.dcfg_ccsr); soc.dcfg_ccsr = NULL; +err_clear_soc_data: + soc.data = NULL; return ret; } From d1b710f463fc26081d37c56a85b15dfa25560e36 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 22 Jul 2026 02:16:01 +0300 Subject: [PATCH 142/157] dt-bindings: fsl: layerscape-dcfg: define DCFG_DCSR region In Layerscape (Arm) and QorIQ (PowerPC) devices, hardware peripherals are accessed by the CPU through a portion of the SoC address space called CCSR ("Configuration, Control, and Status Registers"). All hardware IP blocks have their registers mapped here, and the Device Configuration block makes no exception. However, there exists a secondary range of the address space named DCSR ("Debug Control and Status Registers") which, like CCSR, also holds registers of hardware IP blocks, except the DCSR contents is hidden in all public reference manuals. The intention of the CCSR/DCSR split, to the best of my knowledge, was to place the functionality that is too low level for normal use, and which is necessary only for debug, in a completely separate address space which can be hidden. A use case has appeared where networking SerDes lanes need to be reconfigured at runtime for a different protocol (example: 10GBase-R to SGMII), and the architecture of the SoCs does not normally permit that. The Reset Configuration Word (RCW) is a data structure read by the SoC preboot loader (PBL) which contains stuff like pinmuxing and SerDes protocol mapping for each lane. The RCW that the PBL has loaded is visible in the DCFG block's normal status registers (from CCSR), as read only. Turns out, the RCW is also mapped in the DCFG's shadow register map (in DCSR), in a write-only form. Writing to the RCW registers from the DCFG's DCSR space to change what the PBL has loaded is called "RCW override". It has been validated that the RCW override procedure is necessary to reconfigure the networking data path when a SerDes lane performs a major protocol change. It changes some internal muxes which connect the PCS to either the 10G MAC or to the 1G MAC. Defining the DCSR area of the DCFG as a secondary 'reg' array element allows operating systems to perform RCW overrides. Since it is introduced late in the binding's lifetime, it is optional. It can be identified by name, but also by index (first 'reg' is CCSR). Note that while all SoCs should have a DCFG register block in DCSR, we only need to expose it for the SoCs where the RCW override procedure is known to be needed and has been validated. Signed-off-by: Vladimir Oltean Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20260721231603.67865-8-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- .../bindings/soc/fsl/fsl,layerscape-dcfg.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/soc/fsl/fsl,layerscape-dcfg.yaml b/Documentation/devicetree/bindings/soc/fsl/fsl,layerscape-dcfg.yaml index 3fb0534ea597..fc14fd0bf84b 100644 --- a/Documentation/devicetree/bindings/soc/fsl/fsl,layerscape-dcfg.yaml +++ b/Documentation/devicetree/bindings/soc/fsl/fsl,layerscape-dcfg.yaml @@ -36,7 +36,20 @@ properties: - const: simple-mfd reg: - maxItems: 1 + minItems: 1 + items: + - description: + Customer-visible DCFG register map from CCSR address space + (Configuration, Control and Status Registers) + - description: + Customer-hidden DCFG register map from DCSR address space + (Debug Control and Status Registers) + + reg-names: + minItems: 1 + items: + - const: dcfg_ccsr + - const: dcfg_dcsr little-endian: true big-endian: true From 4f6981ea8fdb1916bff5d35e39634d5a5abb631a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 22 Jul 2026 02:16:02 +0300 Subject: [PATCH 143/157] soc: fsl: guts: implement the RCW override procedure Add support for the RCW override procedure which enables runtime reconfiguration of the protocol running on a SerDes lane. The procedure is done through the DCFG DCSR space which now can be defined as the second memory region of the guts DT node. Support is added on the following SoCs: LS1046A, LS1088A, LS2088A. The procedure is exported to the "client" driver - the Lynx10G SerDes PHY driver - through the following functions: - fsl_guts_lane_validate() used to validate that changing the protocol on a specific lane is supported. - fsl_guts_lane_set_mode() which can be used to request the RCW procedure be executed for a specific lane. Since the RCW override procedure is different depending on the SoC, the private fsl_soc_data structure is updated with two new per SoC callbacks (.serdes_get_rcw_override() and .serdes_init_rcwcr()) which get used from the generic fsl_guts_lane_set_mode() function. These two callbacks hide all the SoC specific register offsets, masks and values so that the _set_mode() procedure is straightforward. Signed-off-by: Ioana Ciornei Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260721231603.67865-9-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/guts.c | 399 ++++++++++++++++++++++++++++++++++++++- include/linux/fsl/guts.h | 18 +- 2 files changed, 409 insertions(+), 8 deletions(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index c283d44b68a3..28361c63e26c 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -5,7 +5,10 @@ * Copyright (C) 2016 Freescale Semiconductor, Inc. */ +#include +#include #include +#include #include #include #include @@ -15,6 +18,28 @@ #include #define DCFG_CCSR 0 +#define DCFG_DCSR 1 + +#define RCW_TIMEOUT_US 1 + +#define LS1046A_RCWSR5_SRDS_PRTCL_S1(lane) \ + GENMASK(19 + 4 * (lane), 16 + 4 * (lane)) +#define LS1046A_SRDS_PRTCL_XFI 1 +#define LS1046A_SRDS_PRTCL_100BASEX_SGMII 3 + +#define LS1088A_RCWSR29_SRDS_PRTCL_S1_LNn(lane) \ + GENMASK(19 + 4 * (3 - (lane)), 16 + 4 * (3 - (lane))) +#define LS1088A_RCWSR30_SRDS_PRTCL_S2_LNn(lane) \ + GENMASK(3 + 4 * (3 - (lane)), 4 * (3 - (lane))) +#define LS1088A_SRDS_PRTCL_XFI 1 +#define LS1088A_SRDS_PRTCL_100BASEX_SGMII 3 + +#define LS2088A_RCWSR29_SRDS_PRTCL_S1 GENMASK(23, 16) +#define LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1 BIT(14) +#define LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(lane) BIT(6 + (7 - (lane))) +#define LS2088A_RCWSR30_SRDS_CLK_SEL_MSK GENMASK(13, 6) +#define LS2088A_SRDS_CLK_SEL_XGMII 1 +#define LS2088A_SRDS_CLK_SEL_GMII 0 struct fsl_soc_die_attr { char *die; @@ -22,9 +47,21 @@ struct fsl_soc_die_attr { u32 mask; }; +struct fsl_soc_serdes_rcw_override { + int offset; + int mask; + int val; +}; + struct fsl_soc_data { const char *sfp_compat; u32 uid_offset; + int num_serdes_blocks; + int num_serdes_lanes; + int (*serdes_init_rcwcr)(int index); + int (*serdes_get_rcw_override)(int index, int lane, + enum lynx_lane_mode lane_mode, + struct fsl_soc_serdes_rcw_override *override); }; enum qoriq_die { @@ -44,6 +81,48 @@ enum qoriq_die { DIE_MAX, }; +struct rcw_full_srds_proto { + int srds_proto_val; + unsigned long xgmii_lanes; +}; + +static const struct rcw_full_srds_proto ls2088a_rcw_full_srds1_protos[] = { + { 0x05, 0 }, + { 0x07, 0 }, + { 0x09, 0 }, + { 0x0a, 0 }, + { 0x0c, 0 }, + { 0x0e, 0 }, + { 0x10, 0 }, + { 0x12, 0 }, + { 0x14, 0 }, + { 0x16, 0 }, + { 0x18, 0 }, + { 0x1a, 0 }, + { 0x1c, 0 }, + { 0x1e, 0 }, + { 0x20, 0 }, + { 0x22, 0 }, + { 0x24, 0 }, + { 0x26, GENMASK(7, 6) }, + { 0x28, GENMASK(7, 4) }, + { 0x2a, GENMASK(7, 0) }, + /* 0x2b: unimplemented because of XAUI1 (lanes E-H) */ + /* 0x2d: unimplemented because of XAUI1 (lanes E-H) */ + /* 0x2e: unimplemented because of XAUI1 (lanes E-H) */ + /* 0x30: unimplemented because of XAUI1 (lanes E-H) */ + /* 0x32: unimplemented because of XAUI1 (lanes E-H) and XAUI2 (A-D) */ + /* 0x33: unimplemented because of QSGMII (lanes E-H) */ + /* 0x35: unimplemented because of QSGMII (lanes A-C) */ + /* 0x37: unimplemented because of QSGMII (lanes E-F) */ + { 0x39, 0 }, + { 0x3b, GENMASK(6, 4) | GENMASK(2, 0) }, + { 0x4b, GENMASK(7, 4) }, + { 0x4c, GENMASK(3, 0) }, + { 0x4d, 0 }, + { 0x58, 0 }, +}; + /* SoC die attribute definition for QorIQ platform */ static const struct fsl_soc_die_attr fsl_soc_die[] = { /* @@ -138,9 +217,11 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = { static struct fsl_soc_guts { struct ccsr_guts __iomem *dcfg_ccsr; + struct ccsr_guts __iomem *dcfg_dcsr; const struct fsl_soc_data *data; bool little_endian; u32 svr; + spinlock_t rcwcr_lock; /* serializes concurrent writes to the RCWCR */ } soc; static unsigned int fsl_guts_read(const void __iomem *reg) @@ -151,6 +232,36 @@ static unsigned int fsl_guts_read(const void __iomem *reg) return ioread32be(reg); } +static void fsl_guts_write(void __iomem *reg, u32 val) +{ + if (soc.little_endian) + iowrite32(val, reg); + else + iowrite32be(val, reg); +} + +/* Some fields of the Reset Configuration Word (RCW) can be overridden at + * runtime by writing to the RCWCRn registers contained within the DCSR space + * of the Device Configuration (DCFG) block. The layout of the RCWCRn registers + * is identical with the read-only RCWSRn from the CCSR space. + */ +static int fsl_guts_rcw_rmw(int offset, u32 val, u32 mask) +{ + u32 rcwcr, rcwsr = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[offset]); + + rcwcr = rcwsr & ~mask; + rcwcr |= val; + fsl_guts_write(&soc.dcfg_dcsr->rcwcr[offset], rcwcr); + + pr_debug("RCW override: RCWSR%d 0x%x -> RCWCR%d 0x%x\n", + offset + 1, rcwsr, offset + 1, rcwcr); + + /* Updates to RCWCR should be visible back in RCWSR immediately */ + return read_poll_timeout_atomic(fsl_guts_read, rcwsr, rcwsr == rcwcr, + 0, RCW_TIMEOUT_US, false, + &soc.dcfg_ccsr->rcwsr[offset]); +} + static bool fsl_soc_die_match_one(u32 svr, const struct fsl_soc_die_attr *match) { return match->svr == (svr & match->mask); @@ -167,6 +278,97 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match( return NULL; } +static int +fsl_guts_serdes_get_rcw_override(int serdes_idx, int lane, + enum lynx_lane_mode lane_mode, + struct fsl_soc_serdes_rcw_override *override) +{ + const struct fsl_soc_data *soc_data = soc.data; + + if (!soc_data) + return -ENODEV; + + /* serdes_idx is one-based */ + if (serdes_idx > soc_data->num_serdes_blocks || serdes_idx <= 0) + return -ERANGE; + + if (lane >= soc_data->num_serdes_lanes || lane < 0) + return -ERANGE; + + if (!soc_data->serdes_get_rcw_override) { + pr_debug("RCW override not implemented for SoC\n"); + return -EINVAL; + } + + if (!soc.dcfg_dcsr) { + pr_debug("Device tree does not define DCFG_DCSR region necessary for RCW override\n"); + return -EINVAL; + } + + return soc_data->serdes_get_rcw_override(serdes_idx, lane, lane_mode, + override); +} + +/** + * fsl_guts_lane_validate() - Validate that SerDes protocol is implemented and + * supported on current SoC + * @serdes_idx: one-based SerDes block index + * @lane: zero-based lane index within SerDes + * @lane_mode: requested SerDes protocol + * + * Should be called before actually requesting the RCW override procedure to be + * applied using %fsl_guts_lane_set_mode() + * + * Return: 0 if RCW override to protocol is possible, negative error otherwise + */ +int fsl_guts_lane_validate(int serdes_idx, int lane, enum lynx_lane_mode lane_mode) +{ + struct fsl_soc_serdes_rcw_override override; + + return fsl_guts_serdes_get_rcw_override(serdes_idx, lane, lane_mode, + &override); +} +EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_validate, "FSL_GUTS"); + +/** + * fsl_guts_lane_set_mode() - apply RCW override procedure for SerDes lane + * @serdes_idx: one-based SerDes block index + * @lane: zero-based lane index within SerDes + * @lane_mode: requested SerDes protocol + * + * Return: 0 on success, negative error otherwise + */ +int fsl_guts_lane_set_mode(int serdes_idx, int lane, enum lynx_lane_mode lane_mode) +{ + struct fsl_soc_serdes_rcw_override override; + int err; + + err = fsl_guts_serdes_get_rcw_override(serdes_idx, lane, lane_mode, + &override); + if (err) + return err; + + spin_lock(&soc.rcwcr_lock); + + if (soc.data->serdes_init_rcwcr) { + err = soc.data->serdes_init_rcwcr(serdes_idx); + if (err) + goto out_unlock; + } + + err = fsl_guts_rcw_rmw(override.offset, + override.val << __ffs(override.mask), + override.mask); + if (err) + pr_err("RCW override failed: %pe\n", ERR_PTR(err)); + +out_unlock: + spin_unlock(&soc.rcwcr_lock); + + return err; +} +EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_set_mode, "FSL_GUTS"); + static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset) { struct device_node *np; @@ -193,9 +395,188 @@ static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset) return uid; } +static int ls1046a_serdes_get_rcw_override(int index, int lane, + enum lynx_lane_mode lane_mode, + struct fsl_soc_serdes_rcw_override *override) +{ + /* The RCW override procedure has to write to different registers + * depending on the SerDes block index. + */ + switch (index) { + case 1: + override->offset = 4; + override->mask = LS1046A_RCWSR5_SRDS_PRTCL_S1(lane); + break; + default: + return -EINVAL; + } + + if (lynx_lane_mode_uses_xgmii_mac(lane_mode)) + override->val = LS1046A_SRDS_PRTCL_XFI; + else if (lynx_lane_mode_uses_gmii_mac(lane_mode)) + override->val = LS1046A_SRDS_PRTCL_100BASEX_SGMII; + else + return -EINVAL; + + return 0; +} + +static int ls1088a_serdes_get_rcw_override(int index, int lane, + enum lynx_lane_mode lane_mode, + struct fsl_soc_serdes_rcw_override *override) +{ + /* The RCW override procedure has to write to different registers + * depending on the SerDes block index. + */ + switch (index) { + case 1: + override->offset = 28; + override->mask = LS1088A_RCWSR29_SRDS_PRTCL_S1_LNn(lane); + break; + case 2: + override->offset = 29; + override->mask = LS1088A_RCWSR30_SRDS_PRTCL_S2_LNn(lane); + break; + default: + return -EINVAL; + } + + if (lynx_lane_mode_uses_xgmii_mac(lane_mode)) + override->val = LS1088A_SRDS_PRTCL_XFI; + else if (lynx_lane_mode_uses_gmii_mac(lane_mode)) + override->val = LS1088A_SRDS_PRTCL_100BASEX_SGMII; + else + return -EINVAL; + + return 0; +} + +static const struct rcw_full_srds_proto *ls2088a_get_full_serdes1_proto(void) +{ + u32 rcwsr29 = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[28]); + u32 srds_prtcl_s1 = FIELD_GET(LS2088A_RCWSR29_SRDS_PRTCL_S1, rcwsr29); + + for (int i = 0; i < ARRAY_SIZE(ls2088a_rcw_full_srds1_protos); i++) { + const struct rcw_full_srds_proto *proto; + + proto = &ls2088a_rcw_full_srds1_protos[i]; + if (proto->srds_proto_val == srds_prtcl_s1) + return proto; + } + + return NULL; +} + +static int ls2088a_serdes_get_rcw_override(int index, int lane, + enum lynx_lane_mode lane_mode, + struct fsl_soc_serdes_rcw_override *override) +{ + switch (index) { + case 1: + override->offset = 29; + override->mask = LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(lane); + break; + default: + return -EINVAL; + } + + /* RCW override only supported if we know how to handle the initial + * RCWSR29[SRDS_PRTCL_S1] value and turn it into an override. + */ + if (!ls2088a_get_full_serdes1_proto()) { + u32 rcwsr30 = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[29]); + + /* If a SerDes-level override is already in place (probably + * left there by a previous boot stage), use it. + */ + if (!(rcwsr30 & LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1)) + return -EINVAL; + } + + if (lynx_lane_mode_uses_xgmii_mac(lane_mode)) + override->val = LS2088A_SRDS_CLK_SEL_XGMII; + else if (lynx_lane_mode_uses_gmii_mac(lane_mode)) + override->val = LS2088A_SRDS_CLK_SEL_GMII; + else + return -EINVAL; + + return 0; +} + +static int ls2088a_serdes_init_rcwcr(int serdes_idx) +{ + const struct rcw_full_srds_proto *srds_prtcl_s1; + const struct fsl_soc_data *soc_data = soc.data; + u32 rcwsr30; + int i, err; + + /* SerDes 2 supports only SGMII for networking. There should be + * no need for RCW override + */ + if (serdes_idx != 1) + return -EINVAL; + + /* SRDS_CLK_EN_SEL_XGMII_S1: SerDes Clock Enable Select XGMII Serdes 1: + * Enables to select GMII/XGMII clock according to + * SRDS_CLK_SEL_XGMII_Ln_S1. + * If the GMII/XGMII select override has already been set, use it. + * Otherwise, derive an initial override for all lanes based on the + * full SerDes protocol table. + */ + rcwsr30 = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[29]); + if (rcwsr30 & LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1) { + pr_debug("RCWSR30 = 0x%x, using this.\n", rcwsr30); + return 0; + } + + srds_prtcl_s1 = ls2088a_get_full_serdes1_proto(); + + rcwsr30 = LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1; + + /* We need to configure the initial state of all lanes for + * the SerDes block #1 + */ + for_each_set_bit(i, &srds_prtcl_s1->xgmii_lanes, soc_data->num_serdes_lanes) + rcwsr30 |= LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(i); + + pr_debug("Setting initial RCWSR30 = 0x%x based on SRDS_PRTCL_S1 = 0x%x\n", + rcwsr30, srds_prtcl_s1->srds_proto_val); + + err = fsl_guts_rcw_rmw(29, rcwsr30, + LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1 | + LS2088A_RCWSR30_SRDS_CLK_SEL_MSK); + if (err) { + pr_err("Setting up initial RCWCR failed: %pe\n", ERR_PTR(err)); + return err; + } + + return 0; +} + +static const struct fsl_soc_data ls1088a_data = { + .serdes_get_rcw_override = ls1088a_serdes_get_rcw_override, + .num_serdes_blocks = 2, + .num_serdes_lanes = 4, +}; + +static const struct fsl_soc_data ls1046a_data = { + .serdes_get_rcw_override = ls1046a_serdes_get_rcw_override, + .num_serdes_blocks = 2, + .num_serdes_lanes = 4, +}; + +static const struct fsl_soc_data ls2088a_data = { + .serdes_get_rcw_override = ls2088a_serdes_get_rcw_override, + .serdes_init_rcwcr = ls2088a_serdes_init_rcwcr, + .num_serdes_blocks = 2, + .num_serdes_lanes = 8, +}; + static const struct fsl_soc_data ls1028a_data = { .sfp_compat = "fsl,ls1028a-sfp", .uid_offset = 0x21c, + .num_serdes_blocks = 1, + .num_serdes_lanes = 4, }; /* @@ -221,10 +602,10 @@ static const struct of_device_id fsl_guts_of_match[] = { { .compatible = "fsl,mpc8572-guts", }, { .compatible = "fsl,ls1021a-dcfg", }, { .compatible = "fsl,ls1043a-dcfg", }, - { .compatible = "fsl,ls2080a-dcfg", }, - { .compatible = "fsl,ls1088a-dcfg", }, + { .compatible = "fsl,ls2080a-dcfg", .data = &ls2088a_data}, + { .compatible = "fsl,ls1088a-dcfg", .data = &ls1088a_data}, { .compatible = "fsl,ls1012a-dcfg", }, - { .compatible = "fsl,ls1046a-dcfg", }, + { .compatible = "fsl,ls1046a-dcfg", .data = &ls1046a_data}, { .compatible = "fsl,lx2160a-dcfg", }, { .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data}, {} @@ -240,6 +621,8 @@ static int __init fsl_guts_init(void) u64 soc_uid = 0; int ret; + spin_lock_init(&soc.rcwcr_lock); + np = of_find_matching_node_and_match(NULL, fsl_guts_of_match, &match); if (!np) return 0; @@ -251,6 +634,8 @@ static int __init fsl_guts_init(void) ret = -ENOMEM; goto err_clear_soc_data; } + /* DCFG_DCSR is optional */ + soc.dcfg_dcsr = of_iomap(np, DCFG_DCSR); soc.little_endian = of_property_read_bool(np, "little-endian"); soc.svr = fsl_guts_read(&soc.dcfg_ccsr->svr); @@ -260,7 +645,7 @@ static int __init fsl_guts_init(void) soc_dev_attr = kzalloc_obj(*soc_dev_attr); if (!soc_dev_attr) { ret = -ENOMEM; - goto err_unmap_dcfg_ccsr; + goto err_unmap_dcfg_ccsr_dcsr; } ret = soc_attr_read_machine(soc_dev_attr); @@ -321,7 +706,11 @@ static int __init fsl_guts_init(void) kfree(soc_dev_attr->family); err_free_soc_dev_attr: kfree(soc_dev_attr); -err_unmap_dcfg_ccsr: +err_unmap_dcfg_ccsr_dcsr: + if (soc.dcfg_dcsr) { + iounmap(soc.dcfg_dcsr); + soc.dcfg_dcsr = NULL; + } iounmap(soc.dcfg_ccsr); soc.dcfg_ccsr = NULL; err_clear_soc_data: diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h index fdb55ca47a4f..8d5ffb985fee 100644 --- a/include/linux/fsl/guts.h +++ b/include/linux/fsl/guts.h @@ -13,6 +13,7 @@ #include #include +#include /* * Global Utility Registers. @@ -91,9 +92,15 @@ struct ccsr_guts { u32 iovselsr; /* 0x.00c0 - I/O voltage select status register Called 'elbcvselcr' on 86xx SOCs */ u8 res0c4[0x100 - 0xc4]; - u32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers - There are 16 registers */ - u8 res140[0x224 - 0x140]; + /* 0x.0100 - read-only Reset Configuration Word Status registers in + * CCSR, or write-only Reset Configuration Word Control registers in + * DCSR. In both cases there are 32 registers. + */ + union { + u32 rcwsr[32]; + u32 rcwcr[32]; + }; + u8 res180[0x224 - 0x180]; u32 iodelay1; /* 0x.0224 - IO delay control register 1 */ u32 iodelay2; /* 0x.0228 - IO delay control register 2 */ u8 res22c[0x604 - 0x22c]; @@ -131,6 +138,11 @@ struct ccsr_guts { u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */ } __attribute__ ((packed)); +int fsl_guts_lane_validate(int serdes_idx, int lane, + enum lynx_lane_mode lane_mode); +int fsl_guts_lane_set_mode(int serdes_idx, int lane, + enum lynx_lane_mode lane_mode); + /* Alternate function signal multiplex control */ #define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x)) From ce816ddb047b5551210ef1aa27415cf4ff29d8e4 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 22 Jul 2026 02:16:03 +0300 Subject: [PATCH 144/157] phy: lynx-10g: use RCW override procedure for dynamic protocol change Up until this patch, the only protocol change supported was between 1000Base-X/SGMII and 2500Base-X. The others require an RCW override procedure which was lacking. Since now the guts driver provides the means of applying this procedure, make use of it and remove any comment which mentioned the limitation. Signed-off-by: Vladimir Oltean Acked-by: Vinod Koul Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260721231603.67865-10-vladimir.oltean@nxp.com Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/phy/freescale/Kconfig | 1 + drivers/phy/freescale/phy-fsl-lynx-10g.c | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 5bf3864fbe64..d4e189fffbf8 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -58,6 +58,7 @@ config PHY_FSL_LYNX_10G tristate "Freescale Layerscape Lynx 10G SerDes PHY support" depends on OF depends on ARCH_LAYERSCAPE || COMPILE_TEST + select FSL_GUTS select GENERIC_PHY select PHY_FSL_LYNX_CORE help diff --git a/drivers/phy/freescale/phy-fsl-lynx-10g.c b/drivers/phy/freescale/phy-fsl-lynx-10g.c index 38def160ef1a..60c85d08c33e 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-10g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-10g.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "phy-fsl-lynx-core.h" @@ -1167,14 +1168,7 @@ static bool lynx_10g_lane_mode_needs_rcw_override(struct lynx_lane *lane, /* Major protocol changes, which involve changing the PCS connection to * the GMII MAC with the one to the XGMII MAC, require an RCW override - * procedure to reconfigure an internal mux, as documented here: - * https://lore.kernel.org/linux-phy/20230810102631.bvozjer3t67r67iy@skbuf/ - * This is SoC-specific, and not yet implemented in drivers/soc/fsl/guts.c. - * - * So the supported set of protocols depends on the initial lane mode. - * - * Minor protocol changes (SGMII <-> 1000Base-X <-> 2500Base-X or - * 10GBase-R <-> USXGMII) are supported. + * procedure to reconfigure an internal mux. */ if ((lynx_lane_mode_uses_gmii_mac(curr) && lynx_lane_mode_uses_xgmii_mac(new)) || @@ -1189,6 +1183,7 @@ static int lynx_10g_validate(struct phy *phy, enum phy_mode mode, int submode, union phy_configure_opts *opts) { struct lynx_lane *lane = phy_get_drvdata(phy); + struct lynx_priv *priv = lane->priv; enum lynx_lane_mode lane_mode; int err; @@ -1197,7 +1192,8 @@ static int lynx_10g_validate(struct phy *phy, enum phy_mode mode, int submode, return err; if (lynx_10g_lane_mode_needs_rcw_override(lane, lane_mode)) - return -EINVAL; + return fsl_guts_lane_validate(priv->info->index, lane->id, + lane_mode); return 0; } @@ -1205,6 +1201,7 @@ static int lynx_10g_validate(struct phy *phy, enum phy_mode mode, int submode, static int lynx_10g_set_mode(struct phy *phy, enum phy_mode mode, int submode) { struct lynx_lane *lane = phy_get_drvdata(phy); + struct lynx_priv *priv = lane->priv; bool powered_up = lane->powered_up; enum lynx_lane_mode lane_mode; int err; @@ -1225,6 +1222,13 @@ static int lynx_10g_set_mode(struct phy *phy, enum phy_mode mode, int submode) if (powered_up) lynx_10g_lane_halt(phy); + if (lynx_10g_lane_mode_needs_rcw_override(lane, lane_mode)) { + err = fsl_guts_lane_set_mode(priv->info->index, lane->id, + lane_mode); + if (err) + goto out; + } + err = lynx_10g_lane_disable_pcvt(lane, lane->mode); if (err) goto out; @@ -1314,6 +1318,7 @@ static struct platform_driver lynx_10g_driver = { }; module_platform_driver(lynx_10g_driver); +MODULE_IMPORT_NS("FSL_GUTS"); MODULE_IMPORT_NS("PHY_FSL_LYNX"); MODULE_AUTHOR("Ioana Ciornei "); MODULE_AUTHOR("Vladimir Oltean "); From fbdba2a67fa7c0e7570bcbf9f28b782d6100270d Mon Sep 17 00:00:00 2001 From: Linkai Gong Date: Fri, 31 Jul 2026 17:46:08 +0800 Subject: [PATCH 145/157] soc: fsl: qe: check platform_driver_register() in qe_ic_of_init() qe_ic_of_init() ignored the return value of platform_driver_register() and always returned success. Propagate the error to the initcall. Fixes: be7ecbd240b2 ("soc: fsl: qe: convert QE interrupt controller to platform_device") Signed-off-by: Linkai Gong Reviewed-by: Maxim Kochetkov Link: https://lore.kernel.org/r/20260731094608.1883391-1-gonglinkai@kylinos.cn Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/soc/fsl/qe/qe_ic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/soc/fsl/qe/qe_ic.c b/drivers/soc/fsl/qe/qe_ic.c index 943911053af6..bccc7d612009 100644 --- a/drivers/soc/fsl/qe/qe_ic.c +++ b/drivers/soc/fsl/qe/qe_ic.c @@ -473,7 +473,6 @@ static struct platform_driver qe_ic_driver = static int __init qe_ic_of_init(void) { - platform_driver_register(&qe_ic_driver); - return 0; + return platform_driver_register(&qe_ic_driver); } subsys_initcall(qe_ic_of_init); From e14b8d39388095fb806fe9f98a95b70619e4cabd Mon Sep 17 00:00:00 2001 From: "Pawel Zalewski (The Capable Hub)" Date: Tue, 28 Jul 2026 11:58:56 +0100 Subject: [PATCH 146/157] bus: fsl-mc: drop unused assignment of acpi_device_id::driver_data This module sets the acpi_device_id::driver_data to 0 but the field is not actually used within the module, we can just drop it from the table. While we are at it - use a named initializer for the acpi_device_id::id field as well to make the code more readable. Signed-off-by: Pawel Zalewski (The Capable Hub) Reviewed-by: Ioana Ciornei Link: https://lore.kernel.org/r/20260728-acpi-bus-v1-1-12ff25fdea9b@thegoodpenguin.co.uk Signed-off-by: Christophe Leroy (CS GROUP) --- drivers/bus/fsl-mc/fsl-mc-bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c index 8c203cd47534..66a4fa73c5b8 100644 --- a/drivers/bus/fsl-mc/fsl-mc-bus.c +++ b/drivers/bus/fsl-mc/fsl-mc-bus.c @@ -1202,7 +1202,7 @@ static const struct of_device_id fsl_mc_bus_match_table[] = { MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table); static const struct acpi_device_id fsl_mc_bus_acpi_match_table[] = { - {"NXP0008", 0 }, + { .id = "NXP0008" }, { } }; MODULE_DEVICE_TABLE(acpi, fsl_mc_bus_acpi_match_table); From 1689ca12dd78d61fd2663da795197d820a9d7c48 Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Fri, 24 Jul 2026 02:53:51 -0700 Subject: [PATCH 147/157] firmware: xilinx: Use TF-A feature check for TF-A-specific APIs Currently, TF-A-specific APIs are validated using the firmware PM_FEATURE_CHECK API, even though TF-A provides a dedicated mechanism via PM_API_FEATURES API. Update the feature check logic for TF-A-specific API calls to use PM_API_FEATURES. If this check fails, fall back to the legacy PM_FEATURE_CHECK to support backward compatibility. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260724095352.2890326-2-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index af838b2dc327..79993ac8452a 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -223,28 +223,33 @@ static int __do_feature_check_call(const u32 api_id, u32 *ret_payload) module_id = FIELD_GET(MODULE_ID_MASK, api_id); /* - * Feature check of APIs belonging to PM, XSEM, and TF-A are handled by calling + * Feature check of APIs belonging to PM and XSEM are handled by calling * PM_FEATURE_CHECK API. For other modules, call PM_API_FEATURES API. */ - if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID || module_id == TF_A_MODULE_ID) + if (module_id == PM_MODULE_ID || module_id == XSEM_MODULE_ID) feature_check_api_id = PM_FEATURE_CHECK; else feature_check_api_id = PM_API_FEATURES; - /* - * Feature check of TF-A APIs is done in the TF-A layer and it expects for - * MODULE_ID_MASK bits of SMC's arg[0] to be the same as PM_MODULE_ID. - */ - if (module_id == TF_A_MODULE_ID) { - module_id = PM_MODULE_ID; + if (module_id == TF_A_MODULE_ID) smc_arg[1] = api_id; - } else { + else smc_arg[1] = (api_id & API_ID_MASK); - } smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, module_id) | feature_check_api_id; ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]); + + /* + * For TF-A APIs, if the feature check with PM_API_FEATURES fails, + * retry with the legacy PM_FEATURE_CHECK for backward compatibility. + */ + if (module_id == TF_A_MODULE_ID && ret) { + smc_arg[0] = PM_SIP_SVC | FIELD_PREP(MODULE_ID_MASK, PM_MODULE_ID) | + PM_FEATURE_CHECK; + ret = do_fw_call(ret_payload, 2, smc_arg[0], smc_arg[1]); + } + if (ret) ret = -EOPNOTSUPP; else From 5b8c4b238b02db7af65f349a9d677c2dd60359ac Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Fri, 24 Jul 2026 02:53:52 -0700 Subject: [PATCH 148/157] firmware: xilinx: Propagate actual error from feature check When do_fw_call() fails in __do_feature_check_call(), propagate the actual errno from zynqmp_pm_ret_code() instead of always returning -EOPNOTSUPP. This gives callers more precise error information. Existing callers only test ret < 0 and are unchanged by this. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260724095352.2890326-3-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 79993ac8452a..1e3ade42b69f 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -251,11 +251,9 @@ static int __do_feature_check_call(const u32 api_id, u32 *ret_payload) } if (ret) - ret = -EOPNOTSUPP; - else - ret = ret_payload[1]; + return ret; - return ret; + return ret_payload[1]; } static int do_feature_check_call(const u32 api_id) From 00bee072c8ae394b029ed4ce6fc8def3e5627e1c Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Wed, 29 Jul 2026 05:25:20 -0700 Subject: [PATCH 149/157] firmware: xilinx: Add support to clear EL3 PM state Currently, during a kexec restart, only the kernel is reloaded, while EL3-specific data remain unchanged. This leads to a mismatch between the kernel state and secure firmware state like SGI number and shutdown scope variable. For example, the kernel registers an SGI number with EL3 firmware so that secure firmware can notify the kernel of events via that SGI. EL3 stores this SGI number in its internal state. After a kexec, the newly loaded kernel re-registers and may request a different SGI number, but the stale value programmed in EL3 remains, so event notifications are delivered on the old SGI and are missed by the new kernel. The shutdown scope variable has a similar stale state problem. To resolve this, the TF_A_CLEAR_PM_STATE PM API is introduced to clear EL3 PM subsystem state during kexec. On a graceful reboot, this API is triggered by zynqmp_firmware_shutdown(), while in a crash kernel scenario, it is invoked by zynqmp_firmware_probe() in the reloaded kernel. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260729122522.3732875-2-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 50 +++++++++++++++++++++++++++- include/linux/firmware/xlnx-zynqmp.h | 3 +- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 1e3ade42b69f..e697153ff056 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -3,7 +3,7 @@ * Xilinx Zynq MPSoC Firmware layer * * Copyright (C) 2014-2022 Xilinx, Inc. - * Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2026 Advanced Micro Devices, Inc. * * Michal Simek * Davorin Mista @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -2068,6 +2069,44 @@ static struct attribute *zynqmp_firmware_attrs[] = { ATTRIBUTE_GROUPS(zynqmp_firmware); +/** + * zynqmp_clear_pm_state() - Clear subsystem state + * @dev: Device pointer used for logging + * + * Clears PM specific data in EL3 firmware. + * + * Return: Returns status, either success or error + */ +static int zynqmp_clear_pm_state(struct device *dev) +{ + u32 pm_family_code; + int ret; + + /* Get the Family code of platform */ + ret = zynqmp_pm_get_family_info(&pm_family_code); + if (ret < 0) + return ret; + + /* Supporting on Versal and Versal Net platforms only */ + if (pm_family_code == PM_VERSAL_FAMILY_CODE || + pm_family_code == PM_VERSAL_NET_FAMILY_CODE) { + /* Check if EL3 firmware supports TF_A_CLEAR_PM_STATE */ + ret = do_feature_check_call(TF_A_CLEAR_PM_STATE); + if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_1)) { + /* Clear PM specific data in EL3 firmware */ + ret = zynqmp_pm_invoke_fn(TF_A_CLEAR_PM_STATE, NULL, 0); + if (ret) + dev_err(dev, + "Failed to clear EL3 PM subsystem state: %d\n", ret); + } else { + dev_warn(dev, "TF_A_CLEAR_PM_STATE is not supported by EL3 firmware: %d\n", ret); + ret = 0; + } + } + + return ret; +} + static int zynqmp_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -2121,6 +2160,9 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) if (ret < 0) return ret; + if (is_kdump_kernel()) + zynqmp_clear_pm_state(dev); + /* Check trustzone version number */ ret = zynqmp_pm_get_trustzone_version(&pm_tz_version); if (ret) @@ -2154,6 +2196,11 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) return of_platform_populate(dev->of_node, NULL, NULL, dev); } +static void zynqmp_firmware_shutdown(struct platform_device *pdev) +{ + zynqmp_clear_pm_state(&pdev->dev); +} + static void zynqmp_firmware_remove(struct platform_device *pdev) { struct pm_api_feature_data *feature_data; @@ -2213,5 +2260,6 @@ static struct platform_driver zynqmp_firmware_driver = { }, .probe = zynqmp_firmware_probe, .remove = zynqmp_firmware_remove, + .shutdown = zynqmp_firmware_shutdown, }; module_platform_driver(zynqmp_firmware_driver); diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 7e27b0f7bf7e..baaa88b0b197 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -3,7 +3,7 @@ * Xilinx Zynq MPSoC Firmware layer * * Copyright (C) 2014-2021 Xilinx - * Copyright (C) 2022 - 2025 Advanced Micro Devices, Inc. + * Copyright (C) 2022 - 2026 Advanced Micro Devices, Inc. * * Michal Simek * Davorin Mista @@ -66,6 +66,7 @@ #define FIRMWARE_VERSION_MASK 0xFFFFU /* ATF only commands */ +#define TF_A_CLEAR_PM_STATE 0xa05 #define TF_A_PM_REGISTER_SGI 0xa04 #define PM_GET_TRUSTZONE_VERSION 0xa03 #define PM_SET_SUSPEND_MODE 0xa02 From 84dbfa5519cf6583d09ee0123ac580542b144553 Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Wed, 29 Jul 2026 05:25:21 -0700 Subject: [PATCH 150/157] firmware: xilinx: Release all peripheral devices from firmware During a kexec restart, only the kernel is reloaded while devices allocated in firmware persist, causing state mismatches between the kernel and firmware. Introduce PM_DEV_ALL_PERIPH node ID (0x18224FFFU) to release all peripheral devices during kexec. On graceful restarts, this happens in zynqmp_firmware_shutdown(). On crash kernel restarts, it happens in zynqmp_firmware_probe() of the reloaded kernel. Releasing all peripherals depends on firmware support for the PM_DEV_ALL_PERIPH node ID. On firmware that does not implement it (the feature check reports a version below PM_API_VERSION_3) the release is skipped and a warning such as "Bulk device release is not supported by firmware" is logged, e.g. on Versal NET firmware that predates this API. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260729122522.3732875-3-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 15 ++++++++++++++- include/linux/firmware/xlnx-zynqmp.h | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index e697153ff056..4bf6c9eabebc 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -2073,7 +2073,7 @@ ATTRIBUTE_GROUPS(zynqmp_firmware); * zynqmp_clear_pm_state() - Clear subsystem state * @dev: Device pointer used for logging * - * Clears PM specific data in EL3 firmware. + * Clears PM specific data in EL3 and platform firmware. * * Return: Returns status, either success or error */ @@ -2102,6 +2102,19 @@ static int zynqmp_clear_pm_state(struct device *dev) dev_warn(dev, "TF_A_CLEAR_PM_STATE is not supported by EL3 firmware: %d\n", ret); ret = 0; } + + /* Check if the firmware supports the PM_DEV_ALL_PERIPH node ID */ + ret = do_feature_check_call(PM_RELEASE_NODE); + if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_3)) { + /* Attempt to release all peripheral devices via firmware */ + ret = zynqmp_pm_release_node(PM_DEV_ALL_PERIPH); + if (ret) + dev_err(dev, "Failed to release all peripheral devices: %d\n", ret); + } else { + dev_warn(dev, + "Bulk device release is not supported by firmware: %d\n", ret); + ret = 0; + } } return ret; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index baaa88b0b197..ac39e5492961 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -50,6 +50,7 @@ /* PM API versions */ #define PM_API_VERSION_1 1 #define PM_API_VERSION_2 2 +#define PM_API_VERSION_3 3 #define PM_PINCTRL_PARAM_SET_VERSION 2 @@ -145,6 +146,9 @@ #define XPM_EVENT_ERROR_MASK_NOC_NCR BIT(13) #define XPM_EVENT_ERROR_MASK_NOC_CR BIT(12) +/* Node ID for all peripheral devices */ +#define PM_DEV_ALL_PERIPH 0x18224FFFU + enum pm_module_id { PM_MODULE_ID = 0x0, XPM_MODULE_ID = 0x2, From 0e8860367da261116cd290bcd6a54d2e597a0c5c Mon Sep 17 00:00:00 2001 From: Jay Buddhabhatti Date: Wed, 29 Jul 2026 05:25:22 -0700 Subject: [PATCH 151/157] firmware: xilinx: Clear firmware notifiers across kexec transitions During a kexec restart, only the kernel is reloaded but notifier callbacks in firmware persist, causing state mismatches between kernel and firmware. To address this, introduce PM_ALL_NOTIFIERS node ID to unregister all notifier callbacks during kexec. On a graceful kexec restart, this occurs in zynqmp_firmware_shutdown(). On a crash kernel restart, it happens in zynqmp_firmware_probe() in the reloaded kernel. Unregistering all notifiers depends on firmware support for the PM_ALL_NOTIFIERS node ID. On firmware that does not implement it (the feature check reports a version below PM_API_VERSION_3) the step is skipped and a warning such as "Firmware doesn't support unregister all notifiers at once" is logged, e.g. on Versal NET firmware that predates this API. Signed-off-by: Jay Buddhabhatti Reviewed-by: Radhey Shyam Pandey Reviewed-by: Prasanna Kumar T S M Link: https://patch.msgid.link/20260729122522.3732875-4-jay.buddhabhatti@amd.com Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 14 ++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 4bf6c9eabebc..f44785aee808 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -2115,6 +2115,20 @@ static int zynqmp_clear_pm_state(struct device *dev) "Bulk device release is not supported by firmware: %d\n", ret); ret = 0; } + + /* Check if the firmware supports the PM_ALL_NOTIFIERS node ID */ + ret = do_feature_check_call(PM_REGISTER_NOTIFIER); + if (ret >= 0 && ((ret & FIRMWARE_VERSION_MASK) >= PM_API_VERSION_3)) { + /* Attempt to unregister all notifier callbacks via firmware */ + ret = zynqmp_pm_register_notifier(PM_ALL_NOTIFIERS, 0, 0, 0); + if (ret) + dev_err(dev, "Failed to unregister all notifiers: %d\n", ret); + } else { + dev_warn(dev, + "Firmware doesn't support unregister all notifiers at once: %d\n", + ret); + ret = 0; + } } return ret; diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index ac39e5492961..69a2f74269f3 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -149,6 +149,9 @@ /* Node ID for all peripheral devices */ #define PM_DEV_ALL_PERIPH 0x18224FFFU +/* Node ID for all notifier callbacks */ +#define PM_ALL_NOTIFIERS 0xFFFFFFFFU + enum pm_module_id { PM_MODULE_ID = 0x0, XPM_MODULE_ID = 0x2, From 4e926dc3d6ad35089ca45af6d1cee36bc24a80fb Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sat, 27 Jun 2026 19:50:15 +0200 Subject: [PATCH 152/157] dt-bindings: arm: qcom,ids: Add SoC ID for Snapdragon SDM 850 Add SoC ID for Qualcomm Snapdragon SDM850. Signed-off-by: David Heidelberg Reviewed-by: Konrad Dybcio Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20260627-sda850-v2-1-44bf46ade42e@ixit.cz Signed-off-by: Bjorn Andersson Signed-off-by: Arnd Bergmann --- include/dt-bindings/arm/qcom,ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/arm/qcom,ids.h b/include/dt-bindings/arm/qcom,ids.h index 1af73c0ad41c..2cf433f61d5e 100644 --- a/include/dt-bindings/arm/qcom,ids.h +++ b/include/dt-bindings/arm/qcom,ids.h @@ -184,6 +184,7 @@ #define QCOM_ID_IPQ8078 344 #define QCOM_ID_SDM636 345 #define QCOM_ID_SDA636 346 +#define QCOM_ID_SDM850 348 #define QCOM_ID_SDM632 349 #define QCOM_ID_SDA632 350 #define QCOM_ID_SDA450 351 From a1ee4bb25b3bd97c1f46fe2968263b54f0be3c19 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 17 Aug 2026 15:35:38 +0300 Subject: [PATCH 153/157] drm/msm: Guard the QCOM_UBWC_CONFIG select with QCOM_SMEM QCOM_UBWC_CONFIG is about to depend on QCOM_SMEM. Guard the DRM_MSM select with 'if ARCH_QCOM && QCOM_SMEM', and add 'depends on QCOM_SMEM || QCOM_SMEM=n' so a built-in driver is never selected against a missing or modular SMEM. Fixes: 1b445022d1d0 ("soc: qcom: ubwc: Get HBB from SMEM") Reviewed-by: Konrad Dybcio Acked-by: Dmitry Baryshkov Reviewed-by: Dmitry Baryshkov Signed-off-by: Daniel Baluta Tested-by: Nathan Chancellor # build Signed-off-by: Arnd Bergmann --- drivers/gpu/drm/msm/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 09469d56513b..ed1c4775d1e9 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -11,6 +11,7 @@ config DRM_MSM depends on QCOM_OCMEM || QCOM_OCMEM=n depends on QCOM_LLCC || QCOM_LLCC=n depends on QCOM_COMMAND_DB || QCOM_COMMAND_DB=n + depends on QCOM_SMEM || QCOM_SMEM=n depends on PM select IOMMU_IO_PGTABLE select QCOM_MDT_LOADER if ARCH_QCOM @@ -22,7 +23,7 @@ config DRM_MSM select TMPFS select QCOM_SCM select QCOM_PAS - select QCOM_UBWC_CONFIG + select QCOM_UBWC_CONFIG if ARCH_QCOM && QCOM_SMEM select WANT_DEV_COREDUMP select SND_SOC_HDMI_CODEC if SND_SOC select SYNC_FILE From 8afb928301700aedc9a19e9f42a0be55ee03234a Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 17 Aug 2026 15:35:39 +0300 Subject: [PATCH 154/157] media: iris: Guard the QCOM_UBWC_CONFIG select with QCOM_SMEM QCOM_UBWC_CONFIG is about to depend on QCOM_SMEM. Guard the VIDEO_QCOM_IRIS select with 'if ARCH_QCOM && QCOM_SMEM', and add 'depends on QCOM_SMEM || QCOM_SMEM=n' so a built-in driver is never selected against a missing or modular SMEM. Fixes: c43207553867 ("media: iris: retrieve UBWC platform configuration") Reviewed-by: Konrad Dybcio Signed-off-by: Daniel Baluta Tested-by: Nathan Chancellor # build Reviewed-by: Dmitry Baryshkov Signed-off-by: Arnd Bergmann --- drivers/media/platform/qcom/iris/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig index 388c9bbc8136..0996213e71e7 100644 --- a/drivers/media/platform/qcom/iris/Kconfig +++ b/drivers/media/platform/qcom/iris/Kconfig @@ -2,11 +2,12 @@ config VIDEO_QCOM_IRIS tristate "Qualcomm iris V4L2 decoder driver" depends on VIDEO_DEV depends on ARCH_QCOM || COMPILE_TEST + depends on QCOM_SMEM || QCOM_SMEM=n select V4L2_MEM2MEM_DEV select QCOM_MDT_LOADER select QCOM_SCM select QCOM_PAS - select QCOM_UBWC_CONFIG + select QCOM_UBWC_CONFIG if ARCH_QCOM && QCOM_SMEM select VIDEOBUF2_DMA_CONTIG help This is a V4L2 driver for Qualcomm iris video accelerator From f24cfafd1e11feca224d085bff515f5ba45c55a2 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 17 Aug 2026 15:35:40 +0300 Subject: [PATCH 155/157] soc: qcom: ubwc: Fix link error when QCOM_SMEM=n ubwc_config.c calls qcom_smem_is_available() and qcom_smem_dram_get_hbb() unconditionally, so QCOM_UBWC_CONFIG=y with QCOM_SMEM=n fails to link: arm-linux-gnueabihf-ld: ubwc_config.c:(.text+0x2c): undefined reference to 'qcom_smem_is_available' Add the missing 'depends on QCOM_SMEM'. Fixes: 1b445022d1d0 ("soc: qcom: ubwc: Get HBB from SMEM") Reviewed-by: Konrad Dybcio Signed-off-by: Daniel Baluta Tested-by: Nathan Chancellor # build Reviewed-by: Dmitry Baryshkov Signed-off-by: Arnd Bergmann --- drivers/soc/qcom/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index fd4d4ecd2df0..e0629e9328c8 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -321,6 +321,7 @@ config QCOM_QMI_HELPERS config QCOM_UBWC_CONFIG tristate + depends on QCOM_SMEM help Most Qualcomm SoCs feature a number of Universal Bandwidth Compression (UBWC) engines across various IP blocks, which need to be initialized From c6441becf4d064bb1dcbccbd3e2276833804cff5 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 17 Aug 2026 15:35:41 +0300 Subject: [PATCH 156/157] soc: qcom: ubwc: Fix missing include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CONFIG_QCOM_UBWC_CONFIG=n, compiler needs to know the definition of ERR_PTR otherwise there will be a compilation error: In file included from drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp_v13.c:7: ./include/linux/soc/qcom/ubwc.h: In function ‘qcom_ubwc_config_get_data’: ./include/linux/soc/qcom/ubwc.h:45:16: error: implicit declaration of function ‘ERR_PTR’ [-Wimplicit-function-declaration] Fix this by including Fixes: 1924272b9ce1 ("soc: qcom: Add UBWC config provider") Reviewed-by: Dmitry Baryshkov Reviewed-by: Konrad Dybcio Signed-off-by: Daniel Baluta Tested-by: Nathan Chancellor # build Signed-off-by: Arnd Bergmann --- include/linux/soc/qcom/ubwc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/soc/qcom/ubwc.h b/include/linux/soc/qcom/ubwc.h index a7372d9c25fb..f3a70360b177 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 #include From aed89a05a736d2570a514f00c2ab83388662b271 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 18 Aug 2026 09:47:43 +0200 Subject: [PATCH 157/157] soc: qcom: make QCOM_PDR_MSG selectable Selecting QCOM_PDR_HELPERS from outside of a CONFIG_QCOM_SOC block causes a build warning: WARNING: unmet direct dependencies detected for QCOM_PDR_MSG Depends on [n]: QCOM_SOC [=n] Selected by [y]: - QCOM_PDR_HELPERS [=y] && NET [=y] Avoid this by allowing QCOM_PDR_MSG to be selected as well. Fixes: f2866e6a27f7 ("soc: qcom: Hide all drivers behind selectable menu") Signed-off-by: Arnd Bergmann --- drivers/soc/qcom/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index e0629e9328c8..2b524154d9fb 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -90,9 +90,6 @@ config QCOM_PD_MAPPER implementation of the service. It is a simpler alternative to the userspace daemon. -config QCOM_PDR_MSG - tristate - config QCOM_PMIC_PDCHARGER_ULOG tristate "Qualcomm PMIC PDCharger ULOG driver" depends on RPMSG @@ -315,6 +312,9 @@ config QCOM_PDR_HELPERS select QCOM_PDR_MSG depends on NET +config QCOM_PDR_MSG + tristate + config QCOM_QMI_HELPERS tristate depends on NET