From e04dddf46dcd77ac6e299dac506f4db1a745a2b8 Mon Sep 17 00:00:00 2001 From: Kumar Anurag Date: Tue, 14 Apr 2026 03:56:25 -0700 Subject: [PATCH 01/30] FROMLIST: arm64: dts: qcom: lemans: Enable audio over DisplayPort Add dailinks for DISPLAY-PORT to enable audio functionality on edp0. Link: https://lore.kernel.org/all/ce8ca265-24df-41e5-a763-f9475de2fa9b@oss.qualcomm.com/ Signed-off-by: Kumar Anurag --- arch/arm64/boot/dts/qcom/lemans-evk.dts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/lemans-evk.dts b/arch/arm64/boot/dts/qcom/lemans-evk.dts index ed8b5b3fa2dc2..42917d5335a56 100644 --- a/arch/arm64/boot/dts/qcom/lemans-evk.dts +++ b/arch/arm64/boot/dts/qcom/lemans-evk.dts @@ -189,6 +189,22 @@ sound-dai = <&q6apm>; }; }; + + dp0-dai-link { + link-name = "DisplayPort0 Playback"; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + codec { + sound-dai = <&mdss0_dp0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; }; vbus_supply_regulator_0: regulator-vbus-supply-0 { From 3412472bc74f7940dc4b682863c2646052c8350e Mon Sep 17 00:00:00 2001 From: Mohd Ayaan Anwar Date: Wed, 15 Apr 2026 10:08:10 +0530 Subject: [PATCH 02/30] QCLINUX: prune.config: Enable Realtek PHY config for Rb3Gen2 Industrial board The Rb3Gen2 Industrial board has the Realtek RTL8221 PHY which fails to function correctly without the Realtek PHY driver. Remove CONFIG_REALTEK_PHY from prune.config to properly enable it. Signed-off-by: Mohd Ayaan Anwar --- arch/arm64/configs/prune.config | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm64/configs/prune.config b/arch/arm64/configs/prune.config index f32d997bdd66e..7f505d6bcab3b 100644 --- a/arch/arm64/configs/prune.config +++ b/arch/arm64/configs/prune.config @@ -92,7 +92,6 @@ # CONFIG_BROADCOM_PHY is not set # CONFIG_BCM54140_PHY is not set # CONFIG_MICROSEMI_PHY is not set -# CONFIG_REALTEK_PHY is not set # CONFIG_ROCKCHIP_PHY is not set # CONFIG_DP83867_PHY is not set # CONFIG_DP83TD510_PHY is not set From 80c7d74ecee6d3fc6f62e8148509c57441a90401 Mon Sep 17 00:00:00 2001 From: Karthik S Date: Thu, 16 Apr 2026 11:34:44 +0530 Subject: [PATCH 03/30] FROMLIST: ASoC: qcom: q6apm-lpass-dai: move graph start to trigger Start the graph at trigger callback. Staring the graph at prepare does not make sense as there is no data transfer at this point. Moving this to trigger will also help cope situation where pipewire is not happy if display port is not connected during start. Link: https://lore.kernel.org/all/20260402081118.348071-12-srinivas.kandagatla@oss.qualcomm.com/ Signed-off-by: Srinivas Kandagatla Signed-off-by: Karthik S --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 40 +++++++++++++++++-------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329f..1cd8774f245ca 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -159,6 +159,31 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct } } +static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!dai_data->is_port_started[dai->id]) { + ret = q6apm_graph_start(dai_data->graph[dai->id]); + if (ret < 0) + dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); + else + dai_data->is_port_started[dai->id] = true; + } + break; + default: + break; + } + + return ret; +} + static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -171,10 +196,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - q6apm_graph_close(dai_data->graph[dai->id]); - dai_data->graph[dai->id] = NULL; - } } /** @@ -203,14 +224,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s dev_err(dai->dev, "Failed to prepare Graph %d\n", rc); goto err; } - - rc = q6apm_graph_start(dai_data->graph[dai->id]); - if (rc < 0) { - dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); - goto err; - } - dai_data->is_port_started[dai->id] = true; - return 0; err: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -254,6 +267,7 @@ static const struct snd_soc_dai_ops q6dma_ops = { .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6i2s_ops = { @@ -263,6 +277,7 @@ static const struct snd_soc_dai_ops q6i2s_ops = { .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -271,6 +286,7 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .shutdown = q6apm_lpass_dai_shutdown, .hw_params = q6hdmi_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = { From a66dbfa33b1d0658f1fd9cda7d1b37cdc6aaf7f5 Mon Sep 17 00:00:00 2001 From: Karthik S Date: Thu, 16 Apr 2026 12:40:26 +0530 Subject: [PATCH 04/30] FROMLIST: arm64: dts: qcom: Enable secondary mi2s Enable secondary mi2s to support HDMI audio. Link: https://lore.kernel.org/all/20260413091937.134469-2-kumar.singh@oss.qualcomm.com/ Signed-off-by: Kumar Anurag Signed-off-by: Karthik S --- arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts | 43 ++++++++++++++++++++ arch/arm64/boot/dts/qcom/sc7280.dtsi | 5 +++ 2 files changed, 48 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts index d36937248a310..eec6f92d3fe94 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts @@ -775,6 +775,7 @@ lt9611_codec: hdmi-bridge@2b { compatible = "lontium,lt9611uxc"; reg = <0x2b>; + #sound-dai-cells = <1>; interrupts-extended = <&tlmm 24 IRQ_TYPE_EDGE_FALLING>; reset-gpios = <&pm7250b_gpios 2 GPIO_ACTIVE_HIGH>; @@ -1331,6 +1332,9 @@ compatible = "qcom,qcs6490-rb3gen2-sndcard"; model = "QCS6490-RB3Gen2"; + pinctrl-0 = <&mi2s1_data0>, <&mi2s1_mclk>, <&mi2s1_sclk>, <&mi2s1_ws>; + pinctrl-names = "default"; + audio-routing = "SpkrLeft IN", "WSA_SPK1 OUT", "SpkrRight IN", "WSA_SPK2 OUT", "VA DMIC0", "vdd-micb", @@ -1370,6 +1374,22 @@ sound-dai = <&q6apm>; }; }; + + mi2s1-playback-dai-link { + link-name = "Secondary MI2S Playback"; + + codec { + sound-dai = <<9611_codec 0>; + }; + + cpu { + sound-dai = <&q6apmbedai SECONDARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; }; &spi3 { @@ -1747,3 +1767,26 @@ compatible = "qcom,qcm6490-lpassaudiocc"; /delete-property/ power-domains; }; + +&mi2s1_data0 { + drive-strength = <8>; + bias-disable; +}; + +&mi2s1_mclk { + drive-strength = <8>; + bias-disable; + output-high; +}; + +&mi2s1_sclk { + drive-strength = <8>; + bias-disable; + output-high; +}; + +&mi2s1_ws { + drive-strength = <8>; + bias-disable; + output-high; +}; diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index edafc6595d152..7c659c0ba451a 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -5880,6 +5880,11 @@ function = "mi2s1_ws"; }; + mi2s1_mclk: mi2s1-mclk-state { + pins = "gpio105"; + function = "sec_mi2s"; + }; + pcie0_clkreq_n: pcie0-clkreq-n-state { pins = "gpio88"; function = "pcie0_clkreqn"; From 03c1eb9402b6e69f97a65b133c42f4f77f0bc371 Mon Sep 17 00:00:00 2001 From: Karthik S Date: Thu, 16 Apr 2026 12:46:10 +0530 Subject: [PATCH 05/30] FROMLIST: arm64: dts: qcom: qcs6490: Enable DP audio Add new dai link to enable DP audio. Link: https://lore.kernel.org/all/20260413091937.134469-3-kumar.singh@oss.qualcomm.com/ Signed-off-by: Kumar Anurag Signed-off-by: Karthik S --- arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts index eec6f92d3fe94..3926253cc53e8 100644 --- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts @@ -1390,6 +1390,22 @@ sound-dai = <&q6apm>; }; }; + + dp-dai-link { + link-name = "DisplayPort0 Playback"; + + codec { + sound-dai = <&mdss_dp>; + }; + + cpu { + sound-dai = <&q6apmbedai DISPLAY_PORT_RX_0>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; }; &spi3 { From a2c83eb3a3b900fb03dd03ce03508f4675b09088 Mon Sep 17 00:00:00 2001 From: Karthik S Date: Thu, 16 Apr 2026 12:49:39 +0530 Subject: [PATCH 06/30] FROMLIST: ASoC: qcom: q6dsp-lpass-ports: Extend q6dsp-lpass-ports driver to support additional sampling rates Expand the existing constraints in the q6dsp-lpass-ports driver to allow a wider range of sampling rates, from 8000 Hz to 192000 Hz. This change improves compatibility with diverse audio hardware and provides greater flexibility for audio stream configurations. Signed-off-by: Ravi Hothi Signed-off-by: Karthik S Link: https://lore.kernel.org/all/20251210065157.2775514-2-ravi.hothi@oss.qualcomm.com/ --- sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 73 +++++++++--------------- 1 file changed, 27 insertions(+), 46 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index 4eed54b071a5c..76421adb5afa2 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -10,16 +10,14 @@ #define Q6AFE_TDM_PB_DAI(pre, num, did) { \ .playback = { \ .stream_name = pre" TDM"#num" Playback", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ .formats = SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ .channels_min = 1, \ .channels_max = 8, \ .rate_min = 8000, \ - .rate_max = 176400, \ + .rate_max = 192000, \ }, \ .name = #did, \ .id = did, \ @@ -28,16 +26,14 @@ #define Q6AFE_TDM_CAP_DAI(pre, num, did) { \ .capture = { \ .stream_name = pre" TDM"#num" Capture", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ .formats = SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ .channels_min = 1, \ .channels_max = 8, \ .rate_min = 8000, \ - .rate_max = 176400, \ + .rate_max = 192000, \ }, \ .name = #did, \ .id = did, \ @@ -46,16 +42,14 @@ #define Q6AFE_CDC_DMA_RX_DAI(did) { \ .playback = { \ .stream_name = #did" Playback", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ .formats = SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ .channels_min = 1, \ .channels_max = 8, \ .rate_min = 8000, \ - .rate_max = 176400, \ + .rate_max = 192000, \ }, \ .name = #did, \ .id = did, \ @@ -64,16 +58,14 @@ #define Q6AFE_CDC_DMA_TX_DAI(did) { \ .capture = { \ .stream_name = #did" Capture", \ - .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ - SNDRV_PCM_RATE_176400, \ + .rates = SNDRV_PCM_RATE_8000_192000, \ .formats = SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE, \ .channels_min = 1, \ .channels_max = 8, \ .rate_min = 8000, \ - .rate_max = 176400, \ + .rate_max = 192000, \ }, \ .name = #did, \ .id = did, \ @@ -350,118 +342,108 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { }, { .playback = { .stream_name = "Primary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .id = PRIMARY_MI2S_RX, .name = "PRI_MI2S_RX", }, { .capture = { .stream_name = "Primary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .id = PRIMARY_MI2S_TX, .name = "PRI_MI2S_TX", }, { .playback = { .stream_name = "Secondary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .name = "SEC_MI2S_RX", .id = SECONDARY_MI2S_RX, }, { .capture = { .stream_name = "Secondary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .id = SECONDARY_MI2S_TX, .name = "SEC_MI2S_TX", }, { .playback = { .stream_name = "Tertiary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .name = "TERT_MI2S_RX", .id = TERTIARY_MI2S_RX, }, { .capture = { .stream_name = "Tertiary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .id = TERTIARY_MI2S_TX, .name = "TERT_MI2S_TX", }, { .playback = { .stream_name = "Quaternary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .name = "QUAT_MI2S_RX", .id = QUATERNARY_MI2S_RX, }, { .capture = { .stream_name = "Quaternary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .id = QUATERNARY_MI2S_TX, .name = "QUAT_MI2S_TX", }, { .playback = { .stream_name = "Quinary MI2S Playback", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 | - SNDRV_PCM_RATE_192000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 1, .channels_max = 8, @@ -473,13 +455,12 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { }, { .capture = { .stream_name = "Quinary MI2S Capture", - .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | - SNDRV_PCM_RATE_16000, + .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .id = QUINARY_MI2S_TX, .name = "QUIN_MI2S_TX", From d14d31749fb966cf512aa2bb82bf08d643d98ec5 Mon Sep 17 00:00:00 2001 From: Karthik S Date: Thu, 16 Apr 2026 12:51:31 +0530 Subject: [PATCH 07/30] FROMLIST: ASoC: qcom: q6dsp-lpass-ports: Update constraints to support 32-bit PCM format Expand the existing constraints by introducing support for the 32-bit PCM format (SNDRV_PCM_FMTBIT_S32_LE) alongside the existing 16-bit and 24-bit formats. This enhancement enables handling of high-resolution audio streams and improves audio quality for supported hardware. Signed-off-by: Ravi Hothi Signed-off-by: Karthik S Link: https://lore.kernel.org/all/20251210065157.2775514-3-ravi.hothi@oss.qualcomm.com/ --- sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 35 +++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c index 76421adb5afa2..8542e620ee02d 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c @@ -344,7 +344,8 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .stream_name = "Primary MI2S Playback", .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -357,7 +358,8 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .stream_name = "Primary MI2S Capture", .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -369,7 +371,9 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .playback = { .stream_name = "Secondary MI2S Playback", .rates = SNDRV_PCM_RATE_8000_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -382,7 +386,8 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .stream_name = "Secondary MI2S Capture", .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -394,7 +399,9 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .playback = { .stream_name = "Tertiary MI2S Playback", .rates = SNDRV_PCM_RATE_8000_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -407,7 +414,8 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .stream_name = "Tertiary MI2S Capture", .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -419,7 +427,9 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .playback = { .stream_name = "Quaternary MI2S Playback", .rates = SNDRV_PCM_RATE_8000_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -432,7 +442,8 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .stream_name = "Quaternary MI2S Capture", .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -444,7 +455,9 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .playback = { .stream_name = "Quinary MI2S Playback", .rates = SNDRV_PCM_RATE_8000_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, @@ -456,7 +469,9 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = { .capture = { .stream_name = "Quinary MI2S Capture", .rates = SNDRV_PCM_RATE_8000_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, .channels_min = 1, .channels_max = 8, .rate_min = 8000, From 498870f9b1ed4252135b280a19ed062996310bda Mon Sep 17 00:00:00 2001 From: Mohd Ayaan Anwar Date: Sun, 19 Apr 2026 20:35:15 +0530 Subject: [PATCH 08/30] PENDING: arm64: dts: qcom: rb3gen2-industrial-mezzanine: add overlay for QPS615 Add an overlay devicetree to enable Rb3Gen2 industrial board's 4 ethernet ports provided by two QPS615 PCIe Ethernet switches. Signed-off-by: Mohd Ayaan Anwar --- arch/arm64/boot/dts/qcom/Makefile | 2 + ...-rb3gen2-industrial-mezzanine-staging.dtso | 119 ++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-staging.dtso diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index bbac93687e38f..dc753ac765966 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -474,6 +474,8 @@ dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-vision-mezzanine-camx.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-staging.dtbo +dtb-$(CONFIG_ARCH_QCOM) += qcs6490-rb3gen2-industrial-mezzanine-staging.dtbo + qcs8300-ride-camx-dtbs:= qcs8300-ride.dtb qcs8300-ride-camx.dtbo dtb-$(CONFIG_ARCH_QCOM) += qcs8300-ride-camx.dtb diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-staging.dtso b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-staging.dtso new file mode 100644 index 0000000000000..edb284558b5a6 --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2-industrial-mezzanine-staging.dtso @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/dts-v1/; +/plugin/; + +#include +#include + +&{/} { + qep_vreg: qep_vreg { + compatible = "regulator-fixed"; + regulator-name = "qep_vreg"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + gpio = <&pm7325_gpios 8 0>; + enable-active-high; + }; + + aqr_vreg: aqr_vreg { + compatible = "regulator-fixed"; + regulator-name = "aqr_vreg"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + gpio = <&pm7250b_gpios 4 0>; + enable-active-high; + }; +}; + +&pcie1_port0 { + pcie@0,0 { + pcie@3,0 { + qps615: pci@0,0 { + compatible = "pci1179,0220"; + interrupts-extended = <&tlmm 141 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "wol_irq"; + pinctrl-names = "default"; + pinctrl-0 = <&aqr_intn_wol_sig>; + phy-reset-gpios = <&qps615 0 GPIO_ACTIVE_LOW>; + phy-supply = <&aqr_vreg>; + reset-deassert-us = <221000>; + + gpio-controller; + #gpio-cells = <2>; + }; + + pci@0,1 { + compatible = "pci1179,0220"; + interrupts-extended = <&tlmm 101 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "wol_irq"; + pinctrl-names = "default"; + pinctrl-0 = <&napa_intn_wol_sig>; + phy-reset-gpios = <&qps615 1 GPIO_ACTIVE_LOW>; + phy-supply = <&qep_vreg>; + reset-deassert-us = <20000>; + }; + }; + }; +}; + +&pcie0_port { + pcie@0,0 { + pcie@3,0 { + pci@0,0 { + interrupts-extended = <&tlmm 136 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "wol_irq"; + pinctrl-names = "default"; + pinctrl-0 = <&rtl_rc0_intn_wol_sig>; + phy-mode = "sgmii"; + phy-reset-gpios = <&tlmm 90 GPIO_ACTIVE_LOW>; + reset-deassert-us = <75000>; + }; + + pci@0,1 { + interrupts-extended = <&tlmm 142 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "wol_irq"; + pinctrl-names = "default"; + pinctrl-0 = <&napa_rc0_intn_wol_sig>; + phy-reset-gpios = <&tlmm 25 GPIO_ACTIVE_LOW>; + phy-supply = <&qep_vreg>; + reset-deassert-us = <20000>; + }; + }; + }; +}; + +&tlmm { + qps615_intn_wol { + aqr_intn_wol_sig: aqr_intn_wol_sig { + pins = "gpio141"; + function = "gpio"; + input-enable; + bias-disable; + }; + + napa_intn_wol_sig: napa_intn_wol_sig { + pins = "gpio101"; + function = "gpio"; + input-enable; + bias-disable; + }; + + rtl_rc0_intn_wol_sig: rtl_rc0_intn_wol_sig { + pins = "gpio136"; + function = "gpio"; + input-enable; + bias-disable; + }; + + napa_rc0_intn_wol_sig: napa_rc0_intn_wol_sig { + pins = "gpio142"; + function = "gpio"; + input-enable; + bias-disable; + }; + }; +}; From d782578fdbda4e8144078704b692461a725b8af0 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Tue, 13 May 2025 09:58:23 +0530 Subject: [PATCH 09/30] FROMLIST: misc: fastrpc: Fix initial memory allocation for Audio PD memory pool The initial buffer allocated for the Audio PD memory pool is never added to the pool because pageslen is set to 0. As a result, the buffer is not registered with Audio PD and is never used, causing a memory leak. Audio PD immediately falls back to allocating memory from the remote heap since the pool starts out empty. Fix this by setting pageslen to 1 so that the initially allocated buffer is correctly registered and becomes part of the Audio PD memory pool. Link: https://lore.kernel.org/all/20260407100508.1027-2-jianping.li@oss.qualcomm.com/ Fixes: 0871561055e66 ("misc: fastrpc: Add support for audiopd") Cc: stable@kernel.org Co-developed-by: Ekansh Gupta Signed-off-by: Ekansh Gupta Signed-off-by: Jianping Li --- drivers/misc/fastrpc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index c4019db14ac4e..7c5bc88b3d12e 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1423,7 +1423,9 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, err = PTR_ERR(name); goto err; } - + inbuf.client_id = fl->client_id; + inbuf.namelen = init.namelen; + inbuf.pageslen = 0; if (!fl->cctx->remote_heap) { err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen, &fl->cctx->remote_heap); @@ -1444,12 +1446,10 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, goto err_map; } scm_done = true; + inbuf.pageslen = 1; } } - inbuf.client_id = fl->client_id; - inbuf.namelen = init.namelen; - inbuf.pageslen = 0; fl->pd = USER_PD; args[0].ptr = (u64)(uintptr_t)&inbuf; From 3ca00e4f7b860349e4c4ef1771405368be15faa6 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Tue, 13 May 2025 09:58:24 +0530 Subject: [PATCH 10/30] FROMLIST: misc: fastrpc: Remove buffer from list prior to unmap operation fastrpc_req_munmap_impl() is called to unmap any buffer. The buffer is getting removed from the list after it is unmapped from DSP. This can create potential race conditions if any other thread removes the entry from list while unmap operation is ongoing. Remove the entry before calling unmap operation. Link: https://lore.kernel.org/all/20260409062617.1182-3-jianping.li@oss.qualcomm.com/ Fixes: 2419e55e532de ("misc: fastrpc: add mmap/unmap support") Cc: stable@kernel.org Co-developed-by: Ekansh Gupta Signed-off-by: Ekansh Gupta Signed-off-by: Jianping Li --- drivers/misc/fastrpc.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 7c5bc88b3d12e..ee3db5e20098e 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -1958,9 +1958,6 @@ static int fastrpc_req_munmap_impl(struct fastrpc_user *fl, struct fastrpc_buf * &args[0]); if (!err) { dev_dbg(dev, "unmmap\tpt 0x%09lx OK\n", buf->raddr); - spin_lock(&fl->lock); - list_del(&buf->node); - spin_unlock(&fl->lock); fastrpc_buf_free(buf); } else { dev_err(dev, "unmmap\tpt 0x%09lx ERROR\n", buf->raddr); @@ -1974,6 +1971,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp) struct fastrpc_buf *buf = NULL, *iter, *b; struct fastrpc_req_munmap req; struct device *dev = fl->sctx->dev; + int err; if (copy_from_user(&req, argp, sizeof(req))) return -EFAULT; @@ -1981,6 +1979,7 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp) spin_lock(&fl->lock); list_for_each_entry_safe(iter, b, &fl->mmaps, node) { if ((iter->raddr == req.vaddrout) && (iter->size == req.size)) { + list_del(&iter->node); buf = iter; break; } @@ -1993,7 +1992,14 @@ static int fastrpc_req_munmap(struct fastrpc_user *fl, char __user *argp) return -EINVAL; } - return fastrpc_req_munmap_impl(fl, buf); + err = fastrpc_req_munmap_impl(fl, buf); + if (err) { + spin_lock(&fl->lock); + list_add_tail(&buf->node, &fl->mmaps); + spin_unlock(&fl->lock); + } + + return err; } static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp) @@ -2083,14 +2089,17 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp) if (copy_to_user((void __user *)argp, &req, sizeof(req))) { err = -EFAULT; - goto err_assign; + goto err_copy; } dev_dbg(dev, "mmap\t\tpt 0x%09lx OK [len 0x%08llx]\n", buf->raddr, buf->size); return 0; - +err_copy: + spin_lock(&fl->lock); + list_del(&buf->node); + spin_unlock(&fl->lock); err_assign: fastrpc_req_munmap_impl(fl, buf); From ba1bd9d4712e17fe412a41b0193de58c2548017d Mon Sep 17 00:00:00 2001 From: Jianping Li Date: Tue, 23 Dec 2025 15:50:59 +0800 Subject: [PATCH 11/30] FROMLIST: misc: fastrpc: Allocate entire reserved memory for Audio PD in probe Allocating and freeing Audio PD memory from userspace is unsafe because the kernel cannot reliably determine when the DSP has finished using the memory. Userspace may free buffers while they are still in use by the DSP, and remote free requests cannot be safely trusted. Allocate the entire Audio PD reserved-memory region upfront during rpmsg probe and tie its lifetime to the rpmsg channel. This avoids userspace- controlled alloc/free and ensures memory is reclaimed only when the DSP shuts down. Link: https://lore.kernel.org/all/20260409062617.1182-4-jianping.li@oss.qualcomm.com/ Signed-off-by: Jianping Li --- drivers/misc/fastrpc.c | 101 +++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 50 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index ee3db5e20098e..8bb848366708c 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -294,6 +294,8 @@ struct fastrpc_channel_ctx { struct kref refcount; /* Flag if dsp attributes are cached */ bool valid_attributes; + /* Flag if audio PD init mem was allocated */ + bool audio_init_mem; u32 dsp_attributes[FASTRPC_MAX_DSP_ATTRIBUTES]; struct fastrpc_device *secure_fdevice; struct fastrpc_device *fdevice; @@ -1394,15 +1396,16 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, struct fastrpc_init_create_static init; struct fastrpc_invoke_args *args; struct fastrpc_phy_page pages[1]; + struct fastrpc_channel_ctx *cctx = fl->cctx; char *name; int err; - bool scm_done = false; struct { int client_id; u32 namelen; u32 pageslen; } inbuf; u32 sc; + unsigned long flags; args = kcalloc(FASTRPC_CREATE_STATIC_PROCESS_NARGS, sizeof(*args), GFP_KERNEL); if (!args) @@ -1426,29 +1429,6 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, inbuf.client_id = fl->client_id; inbuf.namelen = init.namelen; inbuf.pageslen = 0; - if (!fl->cctx->remote_heap) { - err = fastrpc_remote_heap_alloc(fl, fl->sctx->dev, init.memlen, - &fl->cctx->remote_heap); - if (err) - goto err_name; - - /* Map if we have any heap VMIDs associated with this ADSP Static Process. */ - if (fl->cctx->vmcount) { - u64 src_perms = BIT(QCOM_SCM_VMID_HLOS); - - err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys, - (u64)fl->cctx->remote_heap->size, - &src_perms, - fl->cctx->vmperms, fl->cctx->vmcount); - if (err) { - dev_err(fl->sctx->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d\n", - fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err); - goto err_map; - } - scm_done = true; - inbuf.pageslen = 1; - } - } fl->pd = USER_PD; @@ -1460,8 +1440,25 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, args[1].length = inbuf.namelen; args[1].fd = -1; - pages[0].addr = fl->cctx->remote_heap->phys; - pages[0].size = fl->cctx->remote_heap->size; + spin_lock_irqsave(&cctx->lock, flags); + if (!fl->cctx->audio_init_mem) { + if (!fl->cctx->remote_heap || + !fl->cctx->remote_heap->phys || + !fl->cctx->remote_heap->size) { + spin_unlock_irqrestore(&cctx->lock, flags); + err = -ENOMEM; + goto err; + } + + pages[0].addr = fl->cctx->remote_heap->phys; + pages[0].size = fl->cctx->remote_heap->size; + fl->cctx->audio_init_mem = true; + inbuf.pageslen = 1; + } else { + pages[0].addr = 0; + pages[0].size = 0; + } + spin_unlock_irqrestore(&cctx->lock, flags); args[2].ptr = (u64)(uintptr_t) pages; args[2].length = sizeof(*pages); @@ -1479,26 +1476,7 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl, return 0; err_invoke: - if (fl->cctx->vmcount && scm_done) { - u64 src_perms = 0; - struct qcom_scm_vmperm dst_perms; - u32 i; - - for (i = 0; i < fl->cctx->vmcount; i++) - src_perms |= BIT(fl->cctx->vmperms[i].vmid); - - dst_perms.vmid = QCOM_SCM_VMID_HLOS; - dst_perms.perm = QCOM_SCM_PERM_RWX; - err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys, - (u64)fl->cctx->remote_heap->size, - &src_perms, &dst_perms, 1); - if (err) - dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d\n", - fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err); - } -err_map: - fastrpc_buf_free(fl->cctx->remote_heap); -err_name: + fl->cctx->audio_init_mem = false; kfree(name); err: kfree(args); @@ -2465,7 +2443,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) } } - if (domain_id == SDSP_DOMAIN_ID) { + if (domain_id == SDSP_DOMAIN_ID || domain_id == ADSP_DOMAIN_ID) { struct resource res; u64 src_perms; @@ -2476,7 +2454,15 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) qcom_scm_assign_mem(res.start, resource_size(&res), &src_perms, data->vmperms, data->vmcount); } - + if (domain_id == ADSP_DOMAIN_ID) { + data->remote_heap = + kzalloc(sizeof(*data->remote_heap), GFP_KERNEL); + if (!data->remote_heap) + return -ENOMEM; + + data->remote_heap->phys = res.start; + data->remote_heap->size = resource_size(&res); + } } secure_dsp = !(of_property_read_bool(rdev->of_node, "qcom,non-secure-domain")); @@ -2556,6 +2542,7 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev) struct fastrpc_buf *buf, *b; struct fastrpc_user *user; unsigned long flags; + int err; /* No invocations past this point */ spin_lock_irqsave(&cctx->lock, flags); @@ -2573,8 +2560,22 @@ static void fastrpc_rpmsg_remove(struct rpmsg_device *rpdev) list_for_each_entry_safe(buf, b, &cctx->invoke_interrupted_mmaps, node) list_del(&buf->node); - if (cctx->remote_heap) - fastrpc_buf_free(cctx->remote_heap); + if (cctx->remote_heap && cctx->vmcount) { + u64 src_perms = 0; + struct qcom_scm_vmperm dst_perms; + + for (u32 i = 0; i < cctx->vmcount; i++) + src_perms |= BIT(cctx->vmperms[i].vmid); + + dst_perms.vmid = QCOM_SCM_VMID_HLOS; + dst_perms.perm = QCOM_SCM_PERM_RWX; + + err = qcom_scm_assign_mem(cctx->remote_heap->phys, + cctx->remote_heap->size, &src_perms, + &dst_perms, 1); + if (!err) + fastrpc_buf_free(cctx->remote_heap); + } of_platform_depopulate(&rpdev->dev); From 0aef3f9745233d4f40710a31d0581bfb27994e92 Mon Sep 17 00:00:00 2001 From: Ekansh Gupta Date: Tue, 13 May 2025 09:58:21 +0530 Subject: [PATCH 12/30] FROMLIST: misc: fastrpc: Allow fastrpc_buf_free() to accept NULL Make fastrpc_buf_free() a no-op when passed a NULL pointer, allowing callers to avoid open-coded NULL checks. Link: https://lore.kernel.org/all/20260409062617.1182-5-jianping.li@oss.qualcomm.com/ Co-developed-by: Ekansh Gupta Signed-off-by: Ekansh Gupta Signed-off-by: Jianping Li --- drivers/misc/fastrpc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 8bb848366708c..2ee90822d826a 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -416,6 +416,9 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd, static void fastrpc_buf_free(struct fastrpc_buf *buf) { + if (!buf) + return; + dma_free_coherent(buf->dev, buf->size, buf->virt, FASTRPC_PHYS(buf->phys)); kfree(buf); @@ -533,8 +536,7 @@ static void fastrpc_context_free(struct kref *ref) for (i = 0; i < ctx->nbufs; i++) fastrpc_map_put(ctx->maps[i]); - if (ctx->buf) - fastrpc_buf_free(ctx->buf); + fastrpc_buf_free(ctx->buf); spin_lock_irqsave(&cctx->lock, flags); idr_remove(&cctx->ctx_idr, ctx->ctxid >> 8); @@ -1664,8 +1666,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) list_del(&fl->user); spin_unlock_irqrestore(&cctx->lock, flags); - if (fl->init_mem) - fastrpc_buf_free(fl->init_mem); + fastrpc_buf_free(fl->init_mem); list_for_each_entry_safe(ctx, n, &fl->pending, node) { list_del(&ctx->node); From 43b75d7566a4b15dc4884519fd66203e6e8dd40e Mon Sep 17 00:00:00 2001 From: Mohd Ayaan Anwar Date: Tue, 21 Apr 2026 23:34:28 +0530 Subject: [PATCH 13/30] PENDING: arm64: dts: qcom: monaco-evk: fix overlay stacking and PHY reset polarity Fix issues with the Monaco EVK IFP Mezz staging overlay for QPS615: 1) Setting the VendorDtbOverlays EFI variable to "staging" causes UEFI to fail the overlay application entirely. The staging overlay references &eeprom1, a label introduced by the ifp-mezzanine overlay. When overlays are stacked, ufdt does not propagate __symbols__ from previously applied overlays into the working DTB, so &eeprom1 cannot be resolved as a fixup target and the firmware aborts with FDT_ERR_NOTFOUND. Fix this by defining the eeprom node inline under &i2c15, which is present in the base DTB __symbols__. 2) The QCA8081 port does not come out of reset and is therefore never detected. The PHY reset line is declared GPIO_ACTIVE_HIGH, but the QCA8081 has an active-low reset input. With GPIO_ACTIVE_HIGH the driver's deassert call maps logical 0 to physical LOW, holding RESETN asserted throughout probe. Correct it to GPIO_ACTIVE_LOW. Signed-off-by: Mohd Ayaan Anwar --- .../boot/dts/qcom/monaco-evk-staging.dtso | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/monaco-evk-staging.dtso b/arch/arm64/boot/dts/qcom/monaco-evk-staging.dtso index a646b28f6f9d0..75cf03300c27b 100644 --- a/arch/arm64/boot/dts/qcom/monaco-evk-staging.dtso +++ b/arch/arm64/boot/dts/qcom/monaco-evk-staging.dtso @@ -9,14 +9,16 @@ #include #include -&eeprom1 { - nvmem-layout { - mac_addr1: mac-addr@0 { - reg = <0x0 0x6>; - }; +&i2c15 { + eeprom1: eeprom@52 { + nvmem-layout { + mac_addr1: mac-addr@0 { + reg = <0x0 0x6>; + }; - mac_addr2: mac-addr@6 { - reg = <0x6 0x6>; + mac_addr2: mac-addr@6 { + reg = <0x6 0x6>; + }; }; }; }; @@ -42,7 +44,7 @@ nvmem-cell-names = "mac-address"; pinctrl-names = "default"; pinctrl-0 = <&napa_intn_wol_sig>; - phy-reset-gpios = <&expander5 0 GPIO_ACTIVE_HIGH>; + phy-reset-gpios = <&expander5 0 GPIO_ACTIVE_LOW>; reset-deassert-us = <20000>; }; }; From ceaa5702d83aeb96b64d256ce855270c00ae7c15 Mon Sep 17 00:00:00 2001 From: Jianping Li Date: Tue, 21 Apr 2026 18:30:12 +0800 Subject: [PATCH 14/30] FROMLIST: arm64: dts: qcom: sc7280: avoid EFI overlap for ADSP remote heap On KODIAK platforms boot can fail when the DT "adsp-rpc-remote-heap" reserved-memory region overlaps with firmware allocations (UEFI/EFI runtime). The kernel then reports failure to reserve the region and subsequent EFI runtime activity may trigger aborts. The remote heap node was described as a fixed "no-map" region, which turns it into a hard carveout. Replace it with a "shared-dma-pool" reserved memory region with reusable CMA-backed allocation, specifying alignment and size. This avoids hard carveouts and reduces the chance of conflicting with firmware memory maps while keeping an explicit pool for ADSP remote heap usage. Link: https://lore.kernel.org/all/20260423063502.484-1-jianping.li@oss.qualcomm.com/ Fixes: 90a58ffa9c55 ("arm64: dts: qcom: kodiak: Add memory region for audiopd") Cc: stable@kernel.org Signed-off-by: Jianping Li --- arch/arm64/boot/dts/qcom/sc7280.dtsi | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc7280.dtsi b/arch/arm64/boot/dts/qcom/sc7280.dtsi index edafc6595d152..27371dd9588d7 100644 --- a/arch/arm64/boot/dts/qcom/sc7280.dtsi +++ b/arch/arm64/boot/dts/qcom/sc7280.dtsi @@ -191,9 +191,12 @@ qcom,vmid = ; }; - adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap@9cb80000 { - reg = <0x0 0x9cb80000 0x0 0x800000>; - no-map; + adsp_rpc_remote_heap_mem: adsp-rpc-remote-heap { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; }; }; From ac9e3868566a3946cef80bcddbd7e8ac5134e318 Mon Sep 17 00:00:00 2001 From: Jagadeesh Pagadala Date: Thu, 23 Apr 2026 22:13:52 +0530 Subject: [PATCH 15/30] QCLINUX: arm64: configs: qcom.config: Enable CONNECTOR, PROC_EVENTS Enable CONFIG_CONNECTOR and CONFIG_PROC_EVENTS to allow userspace to receive process lifecycle events (fork/exec/exit) via the proc connector over netlink. Signed-off-by: Jagadeesh Pagadala --- arch/arm64/configs/qcom.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/qcom.config b/arch/arm64/configs/qcom.config index 31e1e4f5cf51b..567bc0c34a784 100644 --- a/arch/arm64/configs/qcom.config +++ b/arch/arm64/configs/qcom.config @@ -11,6 +11,7 @@ CONFIG_BT_BNEP_PROTO_FILTER=y CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y CONFIG_CFG80211_CERTIFICATION_ONUS=y +CONFIG_CONNECTOR=y CONFIG_CORESIGHT_CORESIGHT_TNOC=m CONFIG_CORESIGHT_CTCU=m CONFIG_CORESIGHT_DUMMY=m @@ -42,6 +43,7 @@ CONFIG_PM_WAKELOCKS_LIMIT=0 # CONFIG_PM_WAKELOCKS_GC is not set CONFIG_PM=y CONFIG_POWERCAP=y +CONFIG_PROC_EVENTS=y CONFIG_QCA808X_PHY=m CONFIG_QCOM_QMI_COOLING=y CONFIG_REMOTEPROC_THERMAL=y From fbedeef392e7251602b69db9cc17056e89fa6f06 Mon Sep 17 00:00:00 2001 From: Vijayanand Jitta Date: Fri, 24 Apr 2026 11:46:00 +0530 Subject: [PATCH 16/30] Revert "FROMLIST: of: Respect #{iommu,msi}-cells in maps" This reverts commit 374c98dee2f6258537100d95bc71b281c2b9f7d4. The v13 version of this patch series introduced a regression in of_msi_xlate(). The "Factor arguments passed to of_map_id() into a struct" patch changed the call from passing &np to passing *msi_np, collapsing the "pointer-but-no-filter-yet" case into "no filter at all". This causes of_map_id() to return 0 (pass-through) instead of -ENODEV when a node has no msi-map property, terminating the walk prematurely and leaving *msi_np NULL so no MSI domain is associated with the device. Additionally, fsl_mc_get_msi_id() passes msi_np == NULL directly to of_msi_xlate(), causing a NULL pointer dereference. Revert the v13 series to pick up v14 which has the above fixes. Signed-off-by: Vijayanand Jitta --- drivers/of/base.c | 157 +++++++++++---------------------------------- include/linux/of.h | 6 +- 2 files changed, 38 insertions(+), 125 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 43e8a91566ab8..1ba2e538caed8 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2049,48 +2049,18 @@ int of_find_last_cache_level(unsigned int cpu) return cache_level; } -/* - * Some DTs have an iommu-map targeting a 2-cell IOMMU node while - * specifying only 1 cell. Fortunately they all consist of value '1' - * as the 2nd cell entry with the same target, so check for that pattern. - * - * Example: - * IOMMU node: - * #iommu-cells = <2>; - * - * Device node: - * iommu-map = <0x0000 &smmu 0x0000 0x1>, - * <0x0100 &smmu 0x0100 0x1>; - */ -static bool of_check_bad_map(const __be32 *map, int len) -{ - __be32 phandle = map[1]; - - if (len % 4) - return false; - for (int i = 0; i < len; i += 4) { - if (map[i + 1] != phandle || map[i + 3] != cpu_to_be32(1)) - return false; - } - return true; -} - /** * of_map_id - Translate an ID through a downstream mapping. * @np: root complex device node. * @id: device ID to map. * @map_name: property name of the map to use. - * @cells_name: property name of target specifier cells. * @map_mask_name: optional property name of the mask to use. * @filter_np: optional device node to filter matches by, or NULL to match any. * If non-NULL, only map entries targeting this node will be matched. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args_count will be set to the number of output specifier cells - * as defined by @cells_name in the target node, and - * @arg->args[0..args_count-1] will contain the translated output - * specifier values. If a map entry was matched, @arg->np will be set - * to the target node with a reference held that the caller must release - * with of_node_put(). + * @arg->args[0] will contain the translated ID. If a map entry was + * matched, @arg->np will be set to the target node with a reference + * held that the caller must release with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that @@ -2099,19 +2069,17 @@ static bool of_check_bad_map(const __be32 *map, int len) * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *cells_name, - const char *map_mask_name, + const char *map_name, const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; - int map_bytes, map_len, offset = 0; - bool bad_map = false; + int map_len; const __be32 *map = NULL; if (!np || !map_name || !arg) return -EINVAL; - map = of_get_property(np, map_name, &map_bytes); + map = of_get_property(np, map_name, &map_len); if (!map) { if (filter_np) return -ENODEV; @@ -2121,9 +2089,11 @@ int of_map_id(const struct device_node *np, u32 id, return 0; } - if (map_bytes % sizeof(*map)) - goto err_map_len; - map_len = map_bytes / sizeof(*map); + if (!map_len || map_len % (4 * sizeof(*map))) { + pr_err("%pOF: Error: Bad %s length: %d\n", np, + map_name, map_len); + return -EINVAL; + } /* The default is to select all bits. */ map_mask = 0xffffffff; @@ -2136,84 +2106,39 @@ int of_map_id(const struct device_node *np, u32 id, of_property_read_u32(np, map_mask_name, &map_mask); masked_id = map_mask & id; - - while (offset < map_len) { + for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { struct device_node *phandle_node; - u32 id_base, phandle, id_len, id_off, cells = 0; - const __be32 *out_base; - - if (map_len - offset < 2) - goto err_map_len; - - id_base = be32_to_cpup(map + offset); + u32 id_base = be32_to_cpup(map + 0); + u32 phandle = be32_to_cpup(map + 1); + u32 out_base = be32_to_cpup(map + 2); + u32 id_len = be32_to_cpup(map + 3); if (id_base & ~map_mask) { - pr_err("%pOF: Invalid %s translation - %s (0x%x) ignores id-base (0x%x)\n", - np, map_name, map_mask_name, map_mask, id_base); + pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n", + np, map_name, map_name, + map_mask, id_base); return -EFAULT; } - phandle = be32_to_cpup(map + offset + 1); + if (masked_id < id_base || masked_id >= id_base + id_len) + continue; + phandle_node = of_find_node_by_phandle(phandle); if (!phandle_node) return -ENODEV; - if (bad_map) { - cells = 1; - } else if (of_property_read_u32(phandle_node, cells_name, &cells)) { - pr_err("%pOF: missing %s property\n", phandle_node, cells_name); - of_node_put(phandle_node); - return -EINVAL; - } - - if (map_len - offset < 3 + cells) { - of_node_put(phandle_node); - goto err_map_len; - } - - if (offset == 0 && cells == 2) { - bad_map = of_check_bad_map(map, map_len); - if (bad_map) { - pr_warn_once("%pOF: %s mismatches target %s, assuming extra cell of 0\n", - np, map_name, cells_name); - cells = 1; - } - } - - out_base = map + offset + 2; - offset += 3 + cells; - - id_len = be32_to_cpup(map + offset - 1); - if (id_len > 1 && cells > 1) { - /* - * With 1 output cell we reasonably assume its value - * has a linear relationship to the input; with more, - * we'd need help from the provider to know what to do. - */ - pr_err("%pOF: Unsupported %s - cannot handle %d-ID range with %d-cell output specifier\n", - np, map_name, id_len, cells); - of_node_put(phandle_node); - return -EINVAL; - } - id_off = masked_id - id_base; - if (masked_id < id_base || id_off >= id_len) { - of_node_put(phandle_node); - continue; - } - if (filter_np && filter_np != phandle_node) { of_node_put(phandle_node); continue; } arg->np = phandle_node; - for (int i = 0; i < cells; i++) - arg->args[i] = id_off + be32_to_cpu(out_base[i]); - arg->args_count = cells; + arg->args[0] = masked_id - id_base + out_base; + arg->args_count = 1; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", - np, map_name, map_mask, id_base, be32_to_cpup(out_base), - id_len, id, id_off + be32_to_cpup(out_base)); + np, map_name, map_mask, id_base, out_base, + id_len, id, masked_id - id_base + out_base); return 0; } @@ -2224,10 +2149,6 @@ int of_map_id(const struct device_node *np, u32 id, arg->args[0] = id; arg->args_count = 1; return 0; - -err_map_len: - pr_err("%pOF: Error: Bad %s length: %d\n", np, map_name, map_bytes); - return -EINVAL; } EXPORT_SYMBOL_GPL(of_map_id); @@ -2237,21 +2158,18 @@ EXPORT_SYMBOL_GPL(of_map_id); * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args_count will be set to the number of output specifier cells - * and @arg->args[0..args_count-1] will contain the translated output - * specifier values. If a map entry was matched, @arg->np holds a - * reference to the target node that the caller must release with - * of_node_put(). + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * - * Convenience wrapper around of_map_id() using "iommu-map", "#iommu-cells", - * and "iommu-map-mask". + * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, struct of_phandle_args *arg) { - return of_map_id(np, id, "iommu-map", "#iommu-cells", "iommu-map-mask", NULL, arg); + return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -2264,20 +2182,17 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * to match any. If non-NULL, only map entries targeting this node will * be matched. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args_count will be set to the number of output specifier cells - * and @arg->args[0..args_count-1] will contain the translated output - * specifier values. If a map entry was matched, @arg->np holds a - * reference to the target node that the caller must release with - * of_node_put(). + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * - * Convenience wrapper around of_map_id() using "msi-map", "#msi-cells", - * and "msi-map-mask". + * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, const struct device_node *filter_np, struct of_phandle_args *arg) { - return of_map_id(np, id, "msi-map", "#msi-cells", "msi-map-mask", filter_np, arg); + return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/include/linux/of.h b/include/linux/of.h index aab51acfbbfae..93907d6258786 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -457,8 +457,7 @@ const char *of_prop_next_string(const struct property *prop, const char *cur); bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *cells_name, - const char *map_mask_name, + const char *map_name, const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg); int of_map_iommu_id(const struct device_node *np, u32 id, @@ -913,8 +912,7 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag } static inline int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *cells_name, - const char *map_mask_name, + const char *map_name, const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg) { From 2417ba3dd4dacb33aa7c08c4a30b69d9c5c44943 Mon Sep 17 00:00:00 2001 From: Vijayanand Jitta Date: Fri, 24 Apr 2026 11:46:00 +0530 Subject: [PATCH 17/30] Revert "FROMLIST: of: Factor arguments passed to of_map_id() into a struct" This reverts commit 62babf235a046b2085adad86aa97adba196f5765. The v13 version of this patch series introduced a regression in of_msi_xlate(). This patch changed the call from passing &np to passing *msi_np, collapsing the "pointer-but-no-filter-yet" case into "no filter at all". This causes of_map_id() to return 0 (pass-through) instead of -ENODEV when a node has no msi-map property, terminating the walk prematurely and leaving *msi_np NULL so no MSI domain is associated with the device. Additionally, fsl_mc_get_msi_id() passes msi_np == NULL directly to of_msi_xlate(), causing a NULL pointer dereference. Revert the v13 series to pick up v14 which has the above fixes. Signed-off-by: Vijayanand Jitta --- drivers/cdx/cdx_msi.c | 7 ++- drivers/iommu/of_iommu.c | 4 +- drivers/irqchip/irq-gic-its-msi-parent.c | 21 ++++---- drivers/of/base.c | 68 +++++++++++------------- drivers/of/irq.c | 10 +--- drivers/pci/controller/dwc/pci-imx6.c | 32 +++++------ drivers/pci/controller/pcie-apple.c | 5 +- drivers/xen/grant-dma-ops.c | 4 +- include/linux/of.h | 14 +++-- 9 files changed, 72 insertions(+), 93 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index 6924e07c75283..63b3544ec9978 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -121,23 +121,22 @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, struct device *dev, int nvec, msi_alloc_info_t *info) { - struct of_phandle_args msi_spec = {}; struct cdx_device *cdx_dev = to_cdx_device(dev); struct device *parent = cdx_dev->cdx->dev; struct msi_domain_info *msi_info; + u32 dev_id; int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &msi_spec); + ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); if (ret) { dev_err(dev, "of_map_id failed for MSI: %d\n", ret); return ret; } - of_node_put(msi_spec.np); #ifdef GENERIC_MSI_DOMAIN_OPS /* Set the device Id to be passed to the GIC-ITS */ - info->scratchpad[0].ul = msi_spec.args[0]; + info->scratchpad[0].ul = dev_id; #endif msi_info = msi_get_domain_info(msi_domain->parent); diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index a18bb60f6f3df..a511ecf21fcdf 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -45,10 +45,10 @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct device *dev, const u32 *id) { - struct of_phandle_args iommu_spec = {}; + struct of_phandle_args iommu_spec = { .args_count = 1 }; int err; - err = of_map_iommu_id(master_np, *id, &iommu_spec); + err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index 429e146ab3338..0884c4cbd2452 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -164,13 +164,11 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, } while (!ret); if (ret) { - struct of_phandle_args msi_spec = {}; + struct device_node *np = NULL; - ret = of_map_msi_id(dev->of_node, dev->id, NULL, &msi_spec); - if (!ret) { - of_node_put(msi_spec.np); - *dev_id = msi_spec.args[0]; - } + ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); + if (np) + of_node_put(np); } return ret; @@ -211,13 +209,12 @@ static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev } while (!ret); if (ret) { - struct of_phandle_args msi_spec = {}; + struct device_node *np = NULL; - ret = of_map_msi_id(dev->of_node, dev->id, NULL, &msi_spec); - if (!ret) { - *dev_id = msi_spec.args[0]; - ret = its_translate_frame_address(msi_spec.np, pa); - of_node_put(msi_spec.np); + ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); + if (np) { + ret = its_translate_frame_address(np, pa); + of_node_put(np); } } diff --git a/drivers/of/base.c b/drivers/of/base.c index 1ba2e538caed8..f54b481fa889c 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2055,37 +2055,36 @@ int of_find_last_cache_level(unsigned int cpu) * @id: device ID to map. * @map_name: property name of the map to use. * @map_mask_name: optional property name of the mask to use. - * @filter_np: optional device node to filter matches by, or NULL to match any. - * If non-NULL, only map entries targeting this node will be matched. - * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] will contain the translated ID. If a map entry was - * matched, @arg->np will be set to the target node with a reference - * held that the caller must release with of_node_put(). + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that - * ID, as per the "iommu-map" and "msi-map" bindings. + * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or + * @id_out may be NULL if only the other is required. If @target points to + * a non-NULL device node pointer, only entries targeting that node will be + * matched; if it points to a NULL value, it will receive the device node of + * the first matching target phandle, with a reference held. * * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - const struct device_node *filter_np, struct of_phandle_args *arg) + struct device_node **target, u32 *id_out) { u32 map_mask, masked_id; int map_len; const __be32 *map = NULL; - if (!np || !map_name || !arg) + if (!np || !map_name || (!target && !id_out)) return -EINVAL; map = of_get_property(np, map_name, &map_len); if (!map) { - if (filter_np) + if (target) return -ENODEV; /* Otherwise, no map implies no translation */ - arg->args[0] = id; - arg->args_count = 1; + *id_out = id; return 0; } @@ -2127,14 +2126,18 @@ int of_map_id(const struct device_node *np, u32 id, if (!phandle_node) return -ENODEV; - if (filter_np && filter_np != phandle_node) { - of_node_put(phandle_node); - continue; + if (target) { + if (*target) + of_node_put(phandle_node); + else + *target = phandle_node; + + if (*target != phandle_node) + continue; } - arg->np = phandle_node; - arg->args[0] = masked_id - id_base + out_base; - arg->args_count = 1; + if (id_out) + *id_out = masked_id - id_base + out_base; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", np, map_name, map_mask, id_base, out_base, @@ -2143,11 +2146,11 @@ int of_map_id(const struct device_node *np, u32 id, } pr_info("%pOF: no %s translation for id 0x%x on %pOF\n", np, map_name, - id, filter_np); + id, target && *target ? *target : NULL); /* Bypasses translation */ - arg->args[0] = id; - arg->args_count = 1; + if (id_out) + *id_out = id; return 0; } EXPORT_SYMBOL_GPL(of_map_id); @@ -2157,19 +2160,17 @@ EXPORT_SYMBOL_GPL(of_map_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. - * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. * * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, - struct of_phandle_args *arg) + struct device_node **target, u32 *id_out) { - return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); + return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -2178,21 +2179,16 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the msi-map table. - * @filter_np: optional MSI controller node to filter matches by, or NULL - * to match any. If non-NULL, only map entries targeting this node will - * be matched. - * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. * * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, - const struct device_node *filter_np, struct of_phandle_args *arg) + struct device_node **target, u32 *id_out) { - return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); + return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 58d30f5b98c28..9549dda8f9d66 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -725,16 +725,8 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - struct of_phandle_args msi_spec = {}; - - if (!of_map_msi_id(parent_dev->of_node, id_in, *msi_np, &msi_spec)) { - id_out = msi_spec.args[0]; - if (!*msi_np) - *msi_np = msi_spec.np; - else - of_node_put(msi_spec.np); + if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) break; - } if (!of_check_msi_parent(parent_dev->of_node, msi_np)) break; } diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index b876b08b08aea..f8857890395db 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1118,32 +1118,30 @@ static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 rid) static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) { - struct of_phandle_args iommu_spec = {}; - struct of_phandle_args msi_spec = {}; struct device *dev = imx_pcie->pci->dev; + struct device_node *target; u32 sid_i, sid_m; int err_i, err_m; u32 sid = 0; - err_i = of_map_iommu_id(dev->of_node, rid, &iommu_spec); - if (!err_i) - sid_i = iommu_spec.args[0]; - of_node_put(iommu_spec.np); - if (!err_i && !iommu_spec.np) { + target = NULL; + err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); + if (target) { + of_node_put(target); + } else { /* - * "iommu_spec.np == NULL && err_i == 0" means RID out of map - * range. Use 1:1 map RID to streamID. Hardware can't support - * this because the streamID is only 6 bits. + * "target == NULL && err_i == 0" means RID out of map range. + * Use 1:1 map RID to streamID. Hardware can't support this + * because the streamID is only 6 bits */ err_i = -EINVAL; } - err_m = of_map_msi_id(dev->of_node, rid, NULL, &msi_spec); - if (!err_m) - sid_m = msi_spec.args[0]; - of_node_put(msi_spec.np); + target = NULL; + err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); + /* - * err_m msi_spec.np + * err_m target * 0 NULL RID out of range. Use 1:1 map RID to * streamID, Current hardware can't * support it, so return -EINVAL. @@ -1151,8 +1149,10 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) * 0 != NULL Get correct streamID from RID * != 0 != NULL Invalid combination */ - if (!err_m && !msi_spec.np) + if (!err_m && !target) return -EINVAL; + else if (target) + of_node_put(target); /* Find streamID map entry for RID in msi-map */ /* * msi-map iommu-map diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 8d4a554681969..ce21728d6e51b 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -782,7 +782,6 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d { u32 sid, rid = pci_dev_id(pdev); struct apple_pcie_port *port; - struct of_phandle_args iommu_spec = {}; int idx, err; port = apple_pcie_get_port(pdev); @@ -792,12 +791,10 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_iommu_id(port->pcie->dev->of_node, rid, &iommu_spec); + err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); if (err) return err; - of_node_put(iommu_spec.np); - sid = iommu_spec.args[0]; mutex_lock(&port->pcie->lock); idx = bitmap_find_free_region(port->sid_map, port->sid_map_sz, 0); diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index f3b7529461172..f143ea8cd1103 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -315,13 +315,13 @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct device_node *np, domid_t *backend_domid) { - struct of_phandle_args iommu_spec = {}; + struct of_phandle_args iommu_spec = { .args_count = 1 }; if (dev_is_pci(dev)) { struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_iommu_id(np, rid, &iommu_spec)) { + if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index 93907d6258786..df3a89ec76b5a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -458,13 +458,13 @@ bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - const struct device_node *filter_np, struct of_phandle_args *arg); + struct device_node **target, u32 *id_out); int of_map_iommu_id(const struct device_node *np, u32 id, - struct of_phandle_args *arg); + struct device_node **target, u32 *id_out); int of_map_msi_id(const struct device_node *np, u32 id, - const struct device_node *filter_np, struct of_phandle_args *arg); + struct device_node **target, u32 *id_out); phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); @@ -913,21 +913,19 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag static inline int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - const struct device_node *filter_np, - struct of_phandle_args *arg) + struct device_node **target, u32 *id_out) { return -EINVAL; } static inline int of_map_iommu_id(const struct device_node *np, u32 id, - struct of_phandle_args *arg) + struct device_node **target, u32 *id_out) { return -EINVAL; } static inline int of_map_msi_id(const struct device_node *np, u32 id, - const struct device_node *filter_np, - struct of_phandle_args *arg) + struct device_node **target, u32 *id_out) { return -EINVAL; } From da661235ba2dd2942fe1deb44d16425884a3b426 Mon Sep 17 00:00:00 2001 From: Vijayanand Jitta Date: Fri, 24 Apr 2026 11:46:00 +0530 Subject: [PATCH 18/30] Revert "FROMLIST: of: Add convenience wrappers for of_map_id()" This reverts commit 7a3860c20379c4c6d6cd62d07783adc3ca851e7a. The v13 version of this patch series introduced a regression in of_msi_xlate(). The "Factor arguments passed to of_map_id() into a struct" patch changed the call from passing &np to passing *msi_np, collapsing the "pointer-but-no-filter-yet" case into "no filter at all". This causes of_map_id() to return 0 (pass-through) instead of -ENODEV when a node has no msi-map property, terminating the walk prematurely and leaving *msi_np NULL so no MSI domain is associated with the device. Additionally, fsl_mc_get_msi_id() passes msi_np == NULL directly to of_msi_xlate(), causing a NULL pointer dereference. Revert the v13 series to pick up v14 which has the above fixes. Signed-off-by: Vijayanand Jitta --- drivers/cdx/cdx_msi.c | 3 +- drivers/iommu/of_iommu.c | 4 ++- drivers/irqchip/irq-gic-its-msi-parent.c | 4 +-- drivers/of/base.c | 38 ------------------------ drivers/of/irq.c | 3 +- drivers/pci/controller/dwc/pci-imx6.c | 6 ++-- drivers/pci/controller/pcie-apple.c | 3 +- drivers/xen/grant-dma-ops.c | 3 +- include/linux/of.h | 18 ----------- 9 files changed, 17 insertions(+), 65 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index 63b3544ec9978..91b95422b2634 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -128,7 +128,8 @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); + ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask", + NULL, &dev_id); if (ret) { dev_err(dev, "of_map_id failed for MSI: %d\n", ret); return ret; diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index a511ecf21fcdf..6b989a62def20 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -48,7 +48,9 @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct of_phandle_args iommu_spec = { .args_count = 1 }; int err; - err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); + err = of_map_id(master_np, *id, "iommu-map", + "iommu-map-mask", &iommu_spec.np, + iommu_spec.args); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index 0884c4cbd2452..eb1473f1448ac 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -166,7 +166,7 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, if (ret) { struct device_node *np = NULL; - ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); + ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id); if (np) of_node_put(np); } @@ -211,7 +211,7 @@ static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev if (ret) { struct device_node *np = NULL; - ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); + ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id); if (np) { ret = its_translate_frame_address(np, pa); of_node_put(np); diff --git a/drivers/of/base.c b/drivers/of/base.c index f54b481fa889c..2fd27ea0310c5 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2154,41 +2154,3 @@ int of_map_id(const struct device_node *np, u32 id, return 0; } EXPORT_SYMBOL_GPL(of_map_id); - -/** - * of_map_iommu_id - Translate an ID using "iommu-map" bindings. - * @np: root complex device node. - * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform - * stream/device ID) used as the lookup key in the iommu-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. - * - * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". - * - * Return: 0 on success or a standard error code on failure. - */ -int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) -{ - return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); -} -EXPORT_SYMBOL_GPL(of_map_iommu_id); - -/** - * of_map_msi_id - Translate an ID using "msi-map" bindings. - * @np: root complex device node. - * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform - * stream/device ID) used as the lookup key in the msi-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. - * - * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". - * - * Return: 0 on success or a standard error code on failure. - */ -int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) -{ - return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); -} -EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 9549dda8f9d66..1cd93549d0939 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -725,7 +725,8 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) + if (!of_map_id(parent_dev->of_node, id_in, "msi-map", + "msi-map-mask", msi_np, &id_out)) break; if (!of_check_msi_parent(parent_dev->of_node, msi_np)) break; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index f8857890395db..a42164c870548 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1125,7 +1125,8 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) u32 sid = 0; target = NULL; - err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); + err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", + &target, &sid_i); if (target) { of_node_put(target); } else { @@ -1138,7 +1139,8 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) } target = NULL; - err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); + err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", + &target, &sid_m); /* * err_m target diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index ce21728d6e51b..0380d300adca6 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -791,7 +791,8 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); + err = of_map_id(port->pcie->dev->of_node, rid, "iommu-map", + "iommu-map-mask", NULL, &sid); if (err) return err; diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index f143ea8cd1103..43a918c498c6c 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -321,7 +321,8 @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { + if (of_map_id(np, rid, "iommu-map", "iommu-map-mask", &iommu_spec.np, + iommu_spec.args)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index df3a89ec76b5a..121a288ca92df 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -460,12 +460,6 @@ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, struct device_node **target, u32 *id_out); -int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); - -int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); - phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); struct kimage; @@ -918,18 +912,6 @@ static inline int of_map_id(const struct device_node *np, u32 id, return -EINVAL; } -static inline int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) -{ - return -EINVAL; -} - -static inline int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) -{ - return -EINVAL; -} - static inline phys_addr_t of_dma_get_max_cpu_address(struct device_node *np) { return PHYS_ADDR_MAX; From d47bb88b0cf5c143459694240a71ff31a14783ab Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Tue, 14 Oct 2025 17:49:02 +0100 Subject: [PATCH 19/30] FROMLIST: of: Add convenience wrappers for of_map_id() Since we now have quite a few users parsing "iommu-map" and "msi-map" properties, give them some wrappers to conveniently encapsulate the appropriate sets of property names. This will also make it easier to then change of_map_id() to correctly account for specifier cells. Link: https://lore.kernel.org/lkml/20260424-parse_iommu_cells-v14-1-fd02f11b6c38@oss.qualcomm.com/ Reviewed-by: Rob Herring (Arm) Reviewed-by: Frank Li Acked-by: Marc Zyngier Acked-by: Bjorn Helgaas Signed-off-by: Robin Murphy [Conflict: irq-gic-its-msi-parent.c was refactored to split of_pmsi_get_msi_info() into of_pmsi_get_dev_id() and of_v5_pmsi_get_msi_info(); updated both of_map_id() calls.] Signed-off-by: Vijayanand Jitta --- drivers/cdx/cdx_msi.c | 3 +- drivers/iommu/of_iommu.c | 4 +-- drivers/irqchip/irq-gic-its-msi-parent.c | 4 +-- drivers/of/base.c | 38 ++++++++++++++++++++++++ drivers/of/irq.c | 3 +- drivers/pci/controller/dwc/pci-imx6.c | 6 ++-- drivers/pci/controller/pcie-apple.c | 3 +- drivers/xen/grant-dma-ops.c | 3 +- include/linux/of.h | 18 +++++++++++ 9 files changed, 65 insertions(+), 17 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index 91b95422b2634..63b3544ec9978 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -128,8 +128,7 @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask", - NULL, &dev_id); + ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); if (ret) { dev_err(dev, "of_map_id failed for MSI: %d\n", ret); return ret; diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 6b989a62def20..a511ecf21fcdf 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -48,9 +48,7 @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct of_phandle_args iommu_spec = { .args_count = 1 }; int err; - err = of_map_id(master_np, *id, "iommu-map", - "iommu-map-mask", &iommu_spec.np, - iommu_spec.args); + err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index eb1473f1448ac..0884c4cbd2452 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -166,7 +166,7 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, if (ret) { struct device_node *np = NULL; - ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id); + ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); if (np) of_node_put(np); } @@ -211,7 +211,7 @@ static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev if (ret) { struct device_node *np = NULL; - ret = of_map_id(dev->of_node, dev->id, "msi-map", "msi-map-mask", &np, dev_id); + ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); if (np) { ret = its_translate_frame_address(np, pa); of_node_put(np); diff --git a/drivers/of/base.c b/drivers/of/base.c index 2fd27ea0310c5..f54b481fa889c 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2154,3 +2154,41 @@ int of_map_id(const struct device_node *np, u32 id, return 0; } EXPORT_SYMBOL_GPL(of_map_id); + +/** + * of_map_iommu_id - Translate an ID using "iommu-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the iommu-map table. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); +} +EXPORT_SYMBOL_GPL(of_map_iommu_id); + +/** + * of_map_msi_id - Translate an ID using "msi-map" bindings. + * @np: root complex device node. + * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform + * stream/device ID) used as the lookup key in the msi-map table. + * @target: optional pointer to a target device node. + * @id_out: optional pointer to receive the translated ID. + * + * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". + * + * Return: 0 on success or a standard error code on failure. + */ +int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); +} +EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 1cd93549d0939..9549dda8f9d66 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -725,8 +725,7 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_id(parent_dev->of_node, id_in, "msi-map", - "msi-map-mask", msi_np, &id_out)) + if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) break; if (!of_check_msi_parent(parent_dev->of_node, msi_np)) break; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index a42164c870548..f8857890395db 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1125,8 +1125,7 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) u32 sid = 0; target = NULL; - err_i = of_map_id(dev->of_node, rid, "iommu-map", "iommu-map-mask", - &target, &sid_i); + err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); if (target) { of_node_put(target); } else { @@ -1139,8 +1138,7 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) } target = NULL; - err_m = of_map_id(dev->of_node, rid, "msi-map", "msi-map-mask", - &target, &sid_m); + err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); /* * err_m target diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index 0380d300adca6..ce21728d6e51b 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -791,8 +791,7 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_id(port->pcie->dev->of_node, rid, "iommu-map", - "iommu-map-mask", NULL, &sid); + err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); if (err) return err; diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index 43a918c498c6c..f143ea8cd1103 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -321,8 +321,7 @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_id(np, rid, "iommu-map", "iommu-map-mask", &iommu_spec.np, - iommu_spec.args)) { + if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index 121a288ca92df..df3a89ec76b5a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -460,6 +460,12 @@ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, struct device_node **target, u32 *id_out); +int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out); + +int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out); + phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); struct kimage; @@ -912,6 +918,18 @@ static inline int of_map_id(const struct device_node *np, u32 id, return -EINVAL; } +static inline int of_map_iommu_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return -EINVAL; +} + +static inline int of_map_msi_id(const struct device_node *np, u32 id, + struct device_node **target, u32 *id_out) +{ + return -EINVAL; +} + static inline phys_addr_t of_dma_get_max_cpu_address(struct device_node *np) { return PHYS_ADDR_MAX; From 1e6e503857314661607590d2bbcd782340d377fa Mon Sep 17 00:00:00 2001 From: Charan Teja Kalla Date: Sat, 29 Nov 2025 08:48:34 +0530 Subject: [PATCH 20/30] FROMLIST: of: Factor arguments passed to of_map_id() into a struct Change of_map_id() to take a pointer to struct of_phandle_args instead of passing target device node and translated IDs separately. Update all callers accordingly. Add an explicit filter_np parameter to of_map_id() and of_map_msi_id() to separate the filter input from the output. Previously, the target parameter served dual purpose: as an input filter (if non-NULL, only match entries targeting that node) and as an output (receiving the matched node with a reference held). Now filter_np is the explicit input filter and arg->np is the pure output. Previously, of_map_id() would call of_node_put() on the matched node when a filter was provided, making reference ownership inconsistent. Remove this internal of_node_put() call so that of_map_id() now always transfers ownership of the matched node reference to the caller via arg->np. Callers are now consistently responsible for releasing this reference with of_node_put(arg->np) when done. Link: https://lore.kernel.org/lkml/20260424-parse_iommu_cells-v14-2-fd02f11b6c38@oss.qualcomm.com/ Acked-by: Frank Li Suggested-by: Rob Herring (Arm) Suggested-by: Dmitry Baryshkov Signed-off-by: Charan Teja Kalla [Conflict: irq-gic-its-msi-parent.c was refactored to split of_pmsi_get_msi_info() into of_pmsi_get_dev_id() and of_v5_pmsi_get_msi_info(); updated both of_map_id() calls.] Signed-off-by: Vijayanand Jitta --- drivers/cdx/cdx_msi.c | 7 +-- drivers/iommu/of_iommu.c | 4 +- drivers/irqchip/irq-gic-its-msi-parent.c | 21 ++++---- drivers/of/base.c | 68 +++++++++++++----------- drivers/of/irq.c | 30 ++++++++--- drivers/pci/controller/dwc/pci-imx6.c | 32 +++++------ drivers/pci/controller/pcie-apple.c | 5 +- drivers/xen/grant-dma-ops.c | 4 +- include/linux/of.h | 14 ++--- 9 files changed, 108 insertions(+), 77 deletions(-) diff --git a/drivers/cdx/cdx_msi.c b/drivers/cdx/cdx_msi.c index 63b3544ec9978..6924e07c75283 100644 --- a/drivers/cdx/cdx_msi.c +++ b/drivers/cdx/cdx_msi.c @@ -121,22 +121,23 @@ static int cdx_msi_prepare(struct irq_domain *msi_domain, struct device *dev, int nvec, msi_alloc_info_t *info) { + struct of_phandle_args msi_spec = {}; struct cdx_device *cdx_dev = to_cdx_device(dev); struct device *parent = cdx_dev->cdx->dev; struct msi_domain_info *msi_info; - u32 dev_id; int ret; /* Retrieve device ID from requestor ID using parent device */ - ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &dev_id); + ret = of_map_msi_id(parent->of_node, cdx_dev->msi_dev_id, NULL, &msi_spec); if (ret) { dev_err(dev, "of_map_id failed for MSI: %d\n", ret); return ret; } + of_node_put(msi_spec.np); #ifdef GENERIC_MSI_DOMAIN_OPS /* Set the device Id to be passed to the GIC-ITS */ - info->scratchpad[0].ul = dev_id; + info->scratchpad[0].ul = msi_spec.args[0]; #endif msi_info = msi_get_domain_info(msi_domain->parent); diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index a511ecf21fcdf..a18bb60f6f3df 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -45,10 +45,10 @@ static int of_iommu_configure_dev_id(struct device_node *master_np, struct device *dev, const u32 *id) { - struct of_phandle_args iommu_spec = { .args_count = 1 }; + struct of_phandle_args iommu_spec = {}; int err; - err = of_map_iommu_id(master_np, *id, &iommu_spec.np, iommu_spec.args); + err = of_map_iommu_id(master_np, *id, &iommu_spec); if (err) return err; diff --git a/drivers/irqchip/irq-gic-its-msi-parent.c b/drivers/irqchip/irq-gic-its-msi-parent.c index 0884c4cbd2452..429e146ab3338 100644 --- a/drivers/irqchip/irq-gic-its-msi-parent.c +++ b/drivers/irqchip/irq-gic-its-msi-parent.c @@ -164,11 +164,13 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, } while (!ret); if (ret) { - struct device_node *np = NULL; + struct of_phandle_args msi_spec = {}; - ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); - if (np) - of_node_put(np); + ret = of_map_msi_id(dev->of_node, dev->id, NULL, &msi_spec); + if (!ret) { + of_node_put(msi_spec.np); + *dev_id = msi_spec.args[0]; + } } return ret; @@ -209,12 +211,13 @@ static int of_v5_pmsi_get_msi_info(struct irq_domain *domain, struct device *dev } while (!ret); if (ret) { - struct device_node *np = NULL; + struct of_phandle_args msi_spec = {}; - ret = of_map_msi_id(dev->of_node, dev->id, &np, dev_id); - if (np) { - ret = its_translate_frame_address(np, pa); - of_node_put(np); + ret = of_map_msi_id(dev->of_node, dev->id, NULL, &msi_spec); + if (!ret) { + *dev_id = msi_spec.args[0]; + ret = its_translate_frame_address(msi_spec.np, pa); + of_node_put(msi_spec.np); } } diff --git a/drivers/of/base.c b/drivers/of/base.c index f54b481fa889c..1ba2e538caed8 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2055,36 +2055,37 @@ int of_find_last_cache_level(unsigned int cpu) * @id: device ID to map. * @map_name: property name of the map to use. * @map_mask_name: optional property name of the mask to use. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @filter_np: optional device node to filter matches by, or NULL to match any. + * If non-NULL, only map entries targeting this node will be matched. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] will contain the translated ID. If a map entry was + * matched, @arg->np will be set to the target node with a reference + * held that the caller must release with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that - * ID, as per the "iommu-map" and "msi-map" bindings. Either of @target or - * @id_out may be NULL if only the other is required. If @target points to - * a non-NULL device node pointer, only entries targeting that node will be - * matched; if it points to a NULL value, it will receive the device node of - * the first matching target phandle, with a reference held. + * ID, as per the "iommu-map" and "msi-map" bindings. * * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; int map_len; const __be32 *map = NULL; - if (!np || !map_name || (!target && !id_out)) + if (!np || !map_name || !arg) return -EINVAL; map = of_get_property(np, map_name, &map_len); if (!map) { - if (target) + if (filter_np) return -ENODEV; /* Otherwise, no map implies no translation */ - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; } @@ -2126,18 +2127,14 @@ int of_map_id(const struct device_node *np, u32 id, if (!phandle_node) return -ENODEV; - if (target) { - if (*target) - of_node_put(phandle_node); - else - *target = phandle_node; - - if (*target != phandle_node) - continue; + if (filter_np && filter_np != phandle_node) { + of_node_put(phandle_node); + continue; } - if (id_out) - *id_out = masked_id - id_base + out_base; + arg->np = phandle_node; + arg->args[0] = masked_id - id_base + out_base; + arg->args_count = 1; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", np, map_name, map_mask, id_base, out_base, @@ -2146,11 +2143,11 @@ int of_map_id(const struct device_node *np, u32 id, } pr_info("%pOF: no %s translation for id 0x%x on %pOF\n", np, map_name, - id, target && *target ? *target : NULL); + id, filter_np); /* Bypasses translation */ - if (id_out) - *id_out = id; + arg->args[0] = id; + arg->args_count = 1; return 0; } EXPORT_SYMBOL_GPL(of_map_id); @@ -2160,17 +2157,19 @@ EXPORT_SYMBOL_GPL(of_map_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct of_phandle_args *arg) { - return of_map_id(np, id, "iommu-map", "iommu-map-mask", target, id_out); + return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -2179,16 +2178,21 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * @np: root complex device node. * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the msi-map table. - * @target: optional pointer to a target device node. - * @id_out: optional pointer to receive the translated ID. + * @filter_np: optional MSI controller node to filter matches by, or NULL + * to match any. If non-NULL, only map entries targeting this node will + * be matched. + * @arg: pointer to a &struct of_phandle_args for the result. On success, + * @arg->args[0] contains the translated ID. If a map entry was matched, + * @arg->np holds a reference to the target node that the caller must + * release with of_node_put(). * * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, struct of_phandle_args *arg) { - return of_map_id(np, id, "msi-map", "msi-map-mask", target, id_out); + return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 9549dda8f9d66..70d000a530e1c 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -704,19 +704,22 @@ static int of_check_msi_parent(struct device_node *dev_node, struct device_node /** * of_msi_xlate - map a MSI ID and find relevant MSI controller node * @dev: device for which the mapping is to be done. - * @msi_np: Pointer to target MSI controller node + * @msi_np: Pointer to target MSI controller node, or NULL if the caller + * only needs the translated ID without receiving the controller node. + * If non-NULL and pointing to a non-NULL node, only entries targeting + * that node will be matched. If non-NULL and pointing to NULL, it will + * receive the first matching target node with a reference held. * @id_in: Device ID. * * Walk up the device hierarchy looking for devices with a "msi-map" * or "msi-parent" property. If found, apply the mapping to @id_in. - * If @msi_np points to a non-NULL device node pointer, only entries targeting - * that node will be matched; if it points to a NULL value, it will receive the - * device node of the first matching target phandle, with a reference held. * * Returns: The mapped MSI id. */ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) { + struct device_node *local_np = NULL; + struct device_node **np = msi_np ?: &local_np; struct device *parent_dev; u32 id_out = id_in; @@ -725,11 +728,26 @@ u32 of_msi_xlate(struct device *dev, struct device_node **msi_np, u32 id_in) * "msi-map" or an "msi-parent" property. */ for (parent_dev = dev; parent_dev; parent_dev = parent_dev->parent) { - if (!of_map_msi_id(parent_dev->of_node, id_in, msi_np, &id_out)) + struct of_phandle_args msi_spec = {}; + + if (!of_map_msi_id(parent_dev->of_node, id_in, *np, &msi_spec)) { + /* + * Pass-through result: no msi-map on this node (or no + * matching entry). Keep walking up the hierarchy. + */ + if (!msi_spec.np) + continue; + id_out = msi_spec.args[0]; + if (!*np) + *np = msi_spec.np; + else + of_node_put(msi_spec.np); break; - if (!of_check_msi_parent(parent_dev->of_node, msi_np)) + } + if (!of_check_msi_parent(parent_dev->of_node, np)) break; } + of_node_put(local_np); return id_out; } EXPORT_SYMBOL_GPL(of_msi_xlate); diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index f8857890395db..b876b08b08aea 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1118,30 +1118,32 @@ static void imx_pcie_remove_lut(struct imx_pcie *imx_pcie, u16 rid) static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) { + struct of_phandle_args iommu_spec = {}; + struct of_phandle_args msi_spec = {}; struct device *dev = imx_pcie->pci->dev; - struct device_node *target; u32 sid_i, sid_m; int err_i, err_m; u32 sid = 0; - target = NULL; - err_i = of_map_iommu_id(dev->of_node, rid, &target, &sid_i); - if (target) { - of_node_put(target); - } else { + err_i = of_map_iommu_id(dev->of_node, rid, &iommu_spec); + if (!err_i) + sid_i = iommu_spec.args[0]; + of_node_put(iommu_spec.np); + if (!err_i && !iommu_spec.np) { /* - * "target == NULL && err_i == 0" means RID out of map range. - * Use 1:1 map RID to streamID. Hardware can't support this - * because the streamID is only 6 bits + * "iommu_spec.np == NULL && err_i == 0" means RID out of map + * range. Use 1:1 map RID to streamID. Hardware can't support + * this because the streamID is only 6 bits. */ err_i = -EINVAL; } - target = NULL; - err_m = of_map_msi_id(dev->of_node, rid, &target, &sid_m); - + err_m = of_map_msi_id(dev->of_node, rid, NULL, &msi_spec); + if (!err_m) + sid_m = msi_spec.args[0]; + of_node_put(msi_spec.np); /* - * err_m target + * err_m msi_spec.np * 0 NULL RID out of range. Use 1:1 map RID to * streamID, Current hardware can't * support it, so return -EINVAL. @@ -1149,10 +1151,8 @@ static int imx_pcie_add_lut_by_rid(struct imx_pcie *imx_pcie, u32 rid) * 0 != NULL Get correct streamID from RID * != 0 != NULL Invalid combination */ - if (!err_m && !target) + if (!err_m && !msi_spec.np) return -EINVAL; - else if (target) - of_node_put(target); /* Find streamID map entry for RID in msi-map */ /* * msi-map iommu-map diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c index ce21728d6e51b..8d4a554681969 100644 --- a/drivers/pci/controller/pcie-apple.c +++ b/drivers/pci/controller/pcie-apple.c @@ -782,6 +782,7 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d { u32 sid, rid = pci_dev_id(pdev); struct apple_pcie_port *port; + struct of_phandle_args iommu_spec = {}; int idx, err; port = apple_pcie_get_port(pdev); @@ -791,10 +792,12 @@ static int apple_pcie_enable_device(struct pci_host_bridge *bridge, struct pci_d dev_dbg(&pdev->dev, "added to bus %s, index %d\n", pci_name(pdev->bus->self), port->idx); - err = of_map_iommu_id(port->pcie->dev->of_node, rid, NULL, &sid); + err = of_map_iommu_id(port->pcie->dev->of_node, rid, &iommu_spec); if (err) return err; + of_node_put(iommu_spec.np); + sid = iommu_spec.args[0]; mutex_lock(&port->pcie->lock); idx = bitmap_find_free_region(port->sid_map, port->sid_map_sz, 0); diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index f143ea8cd1103..f3b7529461172 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -315,13 +315,13 @@ static int xen_dt_grant_init_backend_domid(struct device *dev, struct device_node *np, domid_t *backend_domid) { - struct of_phandle_args iommu_spec = { .args_count = 1 }; + struct of_phandle_args iommu_spec = {}; if (dev_is_pci(dev)) { struct pci_dev *pdev = to_pci_dev(dev); u32 rid = PCI_DEVID(pdev->bus->number, pdev->devfn); - if (of_map_iommu_id(np, rid, &iommu_spec.np, iommu_spec.args)) { + if (of_map_iommu_id(np, rid, &iommu_spec)) { dev_dbg(dev, "Cannot translate ID\n"); return -ESRCH; } diff --git a/include/linux/of.h b/include/linux/of.h index df3a89ec76b5a..93907d6258786 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -458,13 +458,13 @@ bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out); + const struct device_node *filter_np, struct of_phandle_args *arg); int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); + struct of_phandle_args *arg); int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out); + const struct device_node *filter_np, struct of_phandle_args *arg); phys_addr_t of_dma_get_max_cpu_address(struct device_node *np); @@ -913,19 +913,21 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag static inline int of_map_id(const struct device_node *np, u32 id, const char *map_name, const char *map_mask_name, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, + struct of_phandle_args *arg) { return -EINVAL; } static inline int of_map_iommu_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + struct of_phandle_args *arg) { return -EINVAL; } static inline int of_map_msi_id(const struct device_node *np, u32 id, - struct device_node **target, u32 *id_out) + const struct device_node *filter_np, + struct of_phandle_args *arg) { return -EINVAL; } From 00e90ec96c5f70faf09eea2efa0ec58f549ce983 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Wed, 25 Mar 2026 12:19:57 +0530 Subject: [PATCH 21/30] FROMLIST: of: Respect #{iommu,msi}-cells in maps So far our parsing of {iommu,msi}-map properties has always blindly assumed that the output specifiers will always have exactly 1 cell. This typically does happen to be the case, but is not actually enforced (and the PCI msi-map binding even explicitly states support for 0 or 1 cells) - as a result we've now ended up with dodgy DTs out in the field which depend on this behaviour to map a 1-cell specifier for a 2-cell provider, despite that being bogus per the bindings themselves. Since there is some potential use in being able to map at least single input IDs to multi-cell output specifiers (and properly support 0-cell outputs as well), add support for properly parsing and using the target nodes' #cells values, albeit with the unfortunate complication of still having to work around expectations of the old behaviour too. Since there are multi-cell output specifiers, the callers of of_map_id() may need to get the exact cell output value for further processing. Update of_map_id() to set args_count in the output to reflect the actual number of output specifier cells. Link: https://lore.kernel.org/lkml/20260424-parse_iommu_cells-v14-3-fd02f11b6c38@oss.qualcomm.com/ Signed-off-by: Robin Murphy Signed-off-by: Charan Teja Kalla Signed-off-by: Vijayanand Jitta --- drivers/of/base.c | 157 ++++++++++++++++++++++++++++++++++----------- include/linux/of.h | 6 +- 2 files changed, 125 insertions(+), 38 deletions(-) diff --git a/drivers/of/base.c b/drivers/of/base.c index 1ba2e538caed8..43e8a91566ab8 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2049,18 +2049,48 @@ int of_find_last_cache_level(unsigned int cpu) return cache_level; } +/* + * Some DTs have an iommu-map targeting a 2-cell IOMMU node while + * specifying only 1 cell. Fortunately they all consist of value '1' + * as the 2nd cell entry with the same target, so check for that pattern. + * + * Example: + * IOMMU node: + * #iommu-cells = <2>; + * + * Device node: + * iommu-map = <0x0000 &smmu 0x0000 0x1>, + * <0x0100 &smmu 0x0100 0x1>; + */ +static bool of_check_bad_map(const __be32 *map, int len) +{ + __be32 phandle = map[1]; + + if (len % 4) + return false; + for (int i = 0; i < len; i += 4) { + if (map[i + 1] != phandle || map[i + 3] != cpu_to_be32(1)) + return false; + } + return true; +} + /** * of_map_id - Translate an ID through a downstream mapping. * @np: root complex device node. * @id: device ID to map. * @map_name: property name of the map to use. + * @cells_name: property name of target specifier cells. * @map_mask_name: optional property name of the mask to use. * @filter_np: optional device node to filter matches by, or NULL to match any. * If non-NULL, only map entries targeting this node will be matched. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] will contain the translated ID. If a map entry was - * matched, @arg->np will be set to the target node with a reference - * held that the caller must release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * as defined by @cells_name in the target node, and + * @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np will be set + * to the target node with a reference held that the caller must release + * with of_node_put(). * * Given a device ID, look up the appropriate implementation-defined * platform ID and/or the target device which receives transactions on that @@ -2069,17 +2099,19 @@ int of_find_last_cache_level(unsigned int cpu) * Return: 0 on success or a standard error code on failure. */ int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg) { u32 map_mask, masked_id; - int map_len; + int map_bytes, map_len, offset = 0; + bool bad_map = false; const __be32 *map = NULL; if (!np || !map_name || !arg) return -EINVAL; - map = of_get_property(np, map_name, &map_len); + map = of_get_property(np, map_name, &map_bytes); if (!map) { if (filter_np) return -ENODEV; @@ -2089,11 +2121,9 @@ int of_map_id(const struct device_node *np, u32 id, return 0; } - if (!map_len || map_len % (4 * sizeof(*map))) { - pr_err("%pOF: Error: Bad %s length: %d\n", np, - map_name, map_len); - return -EINVAL; - } + if (map_bytes % sizeof(*map)) + goto err_map_len; + map_len = map_bytes / sizeof(*map); /* The default is to select all bits. */ map_mask = 0xffffffff; @@ -2106,39 +2136,84 @@ int of_map_id(const struct device_node *np, u32 id, of_property_read_u32(np, map_mask_name, &map_mask); masked_id = map_mask & id; - for ( ; map_len > 0; map_len -= 4 * sizeof(*map), map += 4) { + + while (offset < map_len) { struct device_node *phandle_node; - u32 id_base = be32_to_cpup(map + 0); - u32 phandle = be32_to_cpup(map + 1); - u32 out_base = be32_to_cpup(map + 2); - u32 id_len = be32_to_cpup(map + 3); + u32 id_base, phandle, id_len, id_off, cells = 0; + const __be32 *out_base; + + if (map_len - offset < 2) + goto err_map_len; + + id_base = be32_to_cpup(map + offset); if (id_base & ~map_mask) { - pr_err("%pOF: Invalid %s translation - %s-mask (0x%x) ignores id-base (0x%x)\n", - np, map_name, map_name, - map_mask, id_base); + pr_err("%pOF: Invalid %s translation - %s (0x%x) ignores id-base (0x%x)\n", + np, map_name, map_mask_name, map_mask, id_base); return -EFAULT; } - if (masked_id < id_base || masked_id >= id_base + id_len) - continue; - + phandle = be32_to_cpup(map + offset + 1); phandle_node = of_find_node_by_phandle(phandle); if (!phandle_node) return -ENODEV; + if (bad_map) { + cells = 1; + } else if (of_property_read_u32(phandle_node, cells_name, &cells)) { + pr_err("%pOF: missing %s property\n", phandle_node, cells_name); + of_node_put(phandle_node); + return -EINVAL; + } + + if (map_len - offset < 3 + cells) { + of_node_put(phandle_node); + goto err_map_len; + } + + if (offset == 0 && cells == 2) { + bad_map = of_check_bad_map(map, map_len); + if (bad_map) { + pr_warn_once("%pOF: %s mismatches target %s, assuming extra cell of 0\n", + np, map_name, cells_name); + cells = 1; + } + } + + out_base = map + offset + 2; + offset += 3 + cells; + + id_len = be32_to_cpup(map + offset - 1); + if (id_len > 1 && cells > 1) { + /* + * With 1 output cell we reasonably assume its value + * has a linear relationship to the input; with more, + * we'd need help from the provider to know what to do. + */ + pr_err("%pOF: Unsupported %s - cannot handle %d-ID range with %d-cell output specifier\n", + np, map_name, id_len, cells); + of_node_put(phandle_node); + return -EINVAL; + } + id_off = masked_id - id_base; + if (masked_id < id_base || id_off >= id_len) { + of_node_put(phandle_node); + continue; + } + if (filter_np && filter_np != phandle_node) { of_node_put(phandle_node); continue; } arg->np = phandle_node; - arg->args[0] = masked_id - id_base + out_base; - arg->args_count = 1; + for (int i = 0; i < cells; i++) + arg->args[i] = id_off + be32_to_cpu(out_base[i]); + arg->args_count = cells; pr_debug("%pOF: %s, using mask %08x, id-base: %08x, out-base: %08x, length: %08x, id: %08x -> %08x\n", - np, map_name, map_mask, id_base, out_base, - id_len, id, masked_id - id_base + out_base); + np, map_name, map_mask, id_base, be32_to_cpup(out_base), + id_len, id, id_off + be32_to_cpup(out_base)); return 0; } @@ -2149,6 +2224,10 @@ int of_map_id(const struct device_node *np, u32 id, arg->args[0] = id; arg->args_count = 1; return 0; + +err_map_len: + pr_err("%pOF: Error: Bad %s length: %d\n", np, map_name, map_bytes); + return -EINVAL; } EXPORT_SYMBOL_GPL(of_map_id); @@ -2158,18 +2237,21 @@ EXPORT_SYMBOL_GPL(of_map_id); * @id: Requester ID of the device (e.g. PCI RID/BDF or a platform * stream/device ID) used as the lookup key in the iommu-map table. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). * - * Convenience wrapper around of_map_id() using "iommu-map" and "iommu-map-mask". + * Convenience wrapper around of_map_id() using "iommu-map", "#iommu-cells", + * and "iommu-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_iommu_id(const struct device_node *np, u32 id, struct of_phandle_args *arg) { - return of_map_id(np, id, "iommu-map", "iommu-map-mask", NULL, arg); + return of_map_id(np, id, "iommu-map", "#iommu-cells", "iommu-map-mask", NULL, arg); } EXPORT_SYMBOL_GPL(of_map_iommu_id); @@ -2182,17 +2264,20 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * to match any. If non-NULL, only map entries targeting this node will * be matched. * @arg: pointer to a &struct of_phandle_args for the result. On success, - * @arg->args[0] contains the translated ID. If a map entry was matched, - * @arg->np holds a reference to the target node that the caller must - * release with of_node_put(). + * @arg->args_count will be set to the number of output specifier cells + * and @arg->args[0..args_count-1] will contain the translated output + * specifier values. If a map entry was matched, @arg->np holds a + * reference to the target node that the caller must release with + * of_node_put(). * - * Convenience wrapper around of_map_id() using "msi-map" and "msi-map-mask". + * Convenience wrapper around of_map_id() using "msi-map", "#msi-cells", + * and "msi-map-mask". * * Return: 0 on success or a standard error code on failure. */ int of_map_msi_id(const struct device_node *np, u32 id, const struct device_node *filter_np, struct of_phandle_args *arg) { - return of_map_id(np, id, "msi-map", "msi-map-mask", filter_np, arg); + return of_map_id(np, id, "msi-map", "#msi-cells", "msi-map-mask", filter_np, arg); } EXPORT_SYMBOL_GPL(of_map_msi_id); diff --git a/include/linux/of.h b/include/linux/of.h index 93907d6258786..aab51acfbbfae 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -457,7 +457,8 @@ const char *of_prop_next_string(const struct property *prop, const char *cur); bool of_console_check(const struct device_node *dn, char *name, int index); int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg); int of_map_iommu_id(const struct device_node *np, u32 id, @@ -912,7 +913,8 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag } static inline int of_map_id(const struct device_node *np, u32 id, - const char *map_name, const char *map_mask_name, + const char *map_name, const char *cells_name, + const char *map_mask_name, const struct device_node *filter_np, struct of_phandle_args *arg) { From 45cb3060c5bbcd83f630b27b391c331eb27e076d Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Chundru Date: Thu, 23 Apr 2026 09:06:18 +0530 Subject: [PATCH 22/30] Revert "Revert "FROMLIST: PCI: dwc: Remove MSI/MSIX capability if iMSI-RX is used as MSI controller"" Commit bf62c12d1523 ("Revert "FROMLIST: PCI: dwc: Remove MSI/MSIX capability if iMSI-RX is used as MSI controller"") was applied due to excessive AER logging and functional breakage after INTx fallback. The AER issue is now mitigated by disabling L0s, so restore the original behavior and remove MSI/MSI-X capabilities from Root Ports using iMSI-RX. This reverts commit bf62c12d1523 ("Revert "FROMLIST: PCI: dwc: Remove MSI/MSIX capability if iMSI-RX is used as MSI controller""). Signed-off-by: Krishna Chaitanya Chundru --- drivers/pci/controller/dwc/pcie-designware-host.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 60620cc0dedcf..29cbb9ed26a59 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1106,6 +1106,16 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp) dw_pcie_dbi_ro_wr_dis(pci); + /* + * If iMSI-RX module is used as the MSI controller, remove MSI and + * MSI-X capabilities from PCIe Root Ports to ensure fallback to INTx + * interrupt handling. + */ + if (pp->has_msi_ctrl) { + dw_pcie_remove_capability(pci, PCI_CAP_ID_MSI); + dw_pcie_remove_capability(pci, PCI_CAP_ID_MSIX); + } + return 0; } EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); From 3bb5cae9c6e7a7f467ea3bc6a88f4dac3d7fd868 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 19 Apr 2026 17:39:34 +0800 Subject: [PATCH 23/30] FROMLIST: PCI: qcom: Disable ASPM L0s for SA8775P Commit f5cd8a929c82 ("PCI: dwc: Remove MSI/MSIX capability for Root Port if iMSI-RX is used as MSI controller") removed MSI/MSI-X capabilities from the Root Port on platforms using iMSI-RX (including SA8775P, which has no msi-parent/msi-map in DT). This causes PME and AER service drivers to fall back from MSI to INTx. There are lot of AER's seen after this change, the reason for this AER's can be board specific, and recently discovered refgen voting required by phy driver. [ 13.069528] pcieport 0000:00:00.0: PME: Signaling with IRQ 332 [ 13.082436] pcieport 0000:00:00.0: AER: enabled with IRQ 332 [ 13.082447] pcieport 0000:00:00.0: AER: Correctable error message received from 0000:01:00.0 [ 13.101347] pci 0000:01:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) [ 13.111281] pci 0000:01:00.0: device [17cb:1103] error status/mask=00001000/0000e000 [ 13.111284] pci 0000:01:00.0: [12] Timeout [ 13.111313] pcieport 0000:00:00.0: AER: Multiple Correctable error message received from 0000:01:00.0 [ 13.130512] pcieport 0000:00:00.0: PCIe Bus Error: severity=Correctable, type=Data Link Layer, (Transmitter ID) [ 13.130514] pcieport 0000:00:00.0: device [17cb:0115] error status/mask=00001000/0000e000 [ 13.130516] pcieport 0000:00:00.0: [12] Timeout Fix this temporarly on SA8775P/Lemans platform by adding no_l0s = true to cfg_1_34_0 for SA8775P, so that PCI_EXP_LNKCAP_ASPM_L0S is cleared from the Root Port and ASPM L0s is prevented from being negotiated. Fixes: f5cd8a929c82 ("PCI: dwc: Remove MSI/MSIX capability for Root Port if iMSI-RX is used as MSI controller") Assisted-by: Claude:claude-4-6-sonnet Link: https://lore.kernel.org/all/20260419093934.1223027-1-shengchao.guo@oss.qualcomm.com/ Signed-off-by: Shawn Guo --- drivers/pci/controller/dwc/pcie-qcom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 83e973623f55b..6aec2fce916a1 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1483,6 +1483,7 @@ static const struct qcom_pcie_cfg cfg_1_9_0 = { static const struct qcom_pcie_cfg cfg_1_34_0 = { .ops = &ops_1_9_0, .override_no_snoop = true, + .no_l0s = true, }; static const struct qcom_pcie_cfg cfg_2_1_0 = { From 424ee7be72f777dce7986f52e1e98e59dc711b11 Mon Sep 17 00:00:00 2001 From: Sushrut Shree Trivedi Date: Thu, 30 Apr 2026 10:12:18 +0530 Subject: [PATCH 24/30] FROMLIST: pci: quirks: Advertise D3cold capability for UPD720201 PCIe-to-USB bridge UPD720201 does not advertise D3cold support until firmware is loaded post pci enumeration. This results in upd blocking D3cold entry during system suspend and causing overall failure to enter XO shutdown. Hence, add a quirk to advertise D3cold PME capability since the HW actually supports and advertises it post firmware loading. Link: https://lore.kernel.org/all/20260430-d3cold_support-v1-1-6734f280c481@oss.qualcomm.com/ Signed-off-by: Sushrut Shree Trivedi --- drivers/pci/quirks.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 77689f8505e73..4c4899deabad5 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6429,3 +6429,13 @@ static void pci_mask_replay_timer_timeout(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9750, pci_mask_replay_timer_timeout); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_GLI, 0x9755, pci_mask_replay_timer_timeout); #endif + +/* + * Renesas PCIe-to-USB bridge UPD720201 does not advertise D3cold + * capability by default until firmware is loaded post-enumeration. + */ +static void quirk_enable_d3cold(struct pci_dev *dev) +{ + dev->pme_support = dev->pme_support | (1 << PCI_D3cold); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_RENESAS, 0x0014, quirk_enable_d3cold); From a3950d49717b615cb5899d63f0cc215229a2e415 Mon Sep 17 00:00:00 2001 From: Viken Dadhaniya Date: Tue, 31 Mar 2026 17:47:12 +0530 Subject: [PATCH 25/30] WORKAROUND: i2c: qcom-geni: Fix -EACCES error during system resume When other drivers attempt I2C transfers during early resume phase, the I2C controller is still runtime suspended, causing pm_runtime_get_sync() to fail with -EACCES (-13): [ 101.914202] geni_i2c 980000.i2c: error turning SE resources:-13 The PM runtime core returns -EACCES when runtime PM is disabled (dev->power.disable_depth > 0). This occurs because: 1. During suspend_noirq, I2C driver calls geni_i2c_runtime_suspend() and then pm_runtime_disable() 2. I2C driver's noirq_resume only marks adapter as resumed but doesn't re-enable runtime PM or power up the hardware 3. Other drivers resuming later attempt I2C transfers 4. pm_runtime_get_sync() returns -EACCES because runtime PM is still disabled Fix this by calling pm_runtime_force_resume() in geni_i2c_resume_noirq() to properly resume the hardware and re-enable runtime PM during the noirq phase. This ensures the I2C controller is powered and ready for use when other drivers need it during resume. Upstream-Status: Pending Signed-off-by: Viken Dadhaniya --- drivers/i2c/busses/i2c-qcom-geni.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index bfb352b04902c..d2021520bded8 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -831,8 +831,14 @@ static int geni_i2c_probe(struct platform_device *pdev) spin_lock_init(&gi2c->lock); platform_set_drvdata(pdev, gi2c); - /* Keep interrupts disabled initially to allow for low-power modes */ - ret = devm_request_irq(dev, gi2c->irq, geni_i2c_irq, IRQF_NO_AUTOEN, + /* + * Keep interrupts disabled initially to allow for low-power modes. + * IRQF_NO_SUSPEND: Keep IRQ enabled during suspend to handle I2C transfers + * in noirq phase (e.g., from PCIe driver's noirq_resume). + * IRQF_EARLY_RESUME: Enable IRQ early during resume sequence. + */ + ret = devm_request_irq(dev, gi2c->irq, geni_i2c_irq, + IRQF_NO_AUTOEN | IRQF_NO_SUSPEND | IRQF_EARLY_RESUME, dev_name(dev), gi2c); if (ret) return dev_err_probe(dev, ret, @@ -1044,6 +1050,20 @@ static int __maybe_unused geni_i2c_suspend_noirq(struct device *dev) static int __maybe_unused geni_i2c_resume_noirq(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret = 0; + + /* + * Resume hardware to handle I2C transfers from other drivers' + * noirq_resume callbacks (e.g., PCIe driver). + * pm_runtime_force_resume() properly handles PM state and usage_count. + */ + if (gi2c->suspended) { + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "Failed to resume I2C during noirq: %d\n", ret); + return ret; + } + } i2c_mark_adapter_resumed(&gi2c->adap); return 0; From 6b2f39d7f3a821ca081f1e72be6fa21e3a0f532a Mon Sep 17 00:00:00 2001 From: Ziyue Zhang Date: Tue, 14 Apr 2026 18:14:13 +0800 Subject: [PATCH 26/30] FROMLIST: PCI: qcom: Add D3cold support Add support for transitioning PCIe endpoints under host bridge into D3cold by integrating with the DWC core suspend/resume helpers. Implement PME_TurnOff message generation via ELBI_SYS_CTRL and hook it into the DWC host operations so the controller follows the standard PME_TurnOff-based power-down sequence before entering D3cold. When the device is suspended into D3cold, fully tear down interconnect bandwidth, OPP votes. If D3cold is not entered, retain existing behavior by keeping the required interconnect and OPP votes. Use dw_pcie::skip_pwrctrl_off to avoid powering off devices during suspend to preseve wakeup capability of the devices and also not to power on the devices in the init path. Drop the qcom_pcie::suspended flag and rely on the existing dw_pcie::suspended state, which now drives both the power-management flow and the interconnect/OPP handling Link: https://lore.kernel.org/all/20260407-d3cold-v4-5-bb171f75b465@oss.qualcomm.com/ Signed-off-by: Krishna Chaitanya Chundru Signed-off-by: Ziyue Zhang --- drivers/pci/controller/dwc/Kconfig | 2 +- .../pci/controller/dwc/pcie-designware-host.c | 3 ++ drivers/pci/controller/dwc/pcie-designware.h | 1 + drivers/pci/controller/dwc/pcie-qcom.c | 46 +++++++++++++------ 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 349d4657393c9..57be07198094c 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -299,7 +299,7 @@ config PCIE_QCOM select CRC8 select PCIE_QCOM_COMMON select PCI_HOST_COMMON - select PCI_PWRCTRL_SLOT + select PCI_PWRCTRL_GENERIC help Say Y here to enable PCIe controller support on Qualcomm SoCs. The PCIe controller uses the DesignWare core plus Qualcomm-specific diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 29cbb9ed26a59..57293c180ad89 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -1167,6 +1167,8 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci) if (!pci_host_common_can_enter_d3cold(pci->pp.bridge)) return 0; + pci->pp.skip_pwrctrl_off = true; + if (pci->pp.ops->pme_turn_off) { pci->pp.ops->pme_turn_off(&pci->pp); } else { @@ -1222,6 +1224,7 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci) return 0; pci->suspended = false; + pci->pp.skip_pwrctrl_off = false; if (pci->pp.ops->init) { ret = pci->pp.ops->init(&pci->pp); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 57ca6b8de55bc..bc34858c0851f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -436,6 +436,7 @@ struct dw_pcie_rp { bool ecam_enabled; bool native_ecam; bool skip_l23_ready; + bool skip_pwrctrl_off; }; struct dw_pcie_ep_ops { diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 6aec2fce916a1..4deed3ed5089a 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -1308,13 +1308,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_deinit; - ret = pci_pwrctrl_create_devices(pci->dev); - if (ret) - goto err_disable_phy; + if (!pci->suspended) { + ret = pci_pwrctrl_create_devices(pci->dev); + if (ret) + goto err_disable_phy; + } - ret = pci_pwrctrl_power_on_devices(pci->dev); - if (ret) - goto err_pwrctrl_destroy; + if (!pp->skip_pwrctrl_off) { + ret = pci_pwrctrl_power_on_devices(pci->dev); + if (ret) + goto err_pwrctrl_destroy; + } if (pcie->cfg->ops->post_init) { ret = pcie->cfg->ops->post_init(pcie); @@ -1356,11 +1360,14 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) qcom_pcie_perst_assert(pcie); - /* - * No need to destroy pwrctrl devices as this function only gets called - * during system suspend as of now. - */ - pci_pwrctrl_power_off_devices(pci->dev); + if (!pci->pp.skip_pwrctrl_off) { + /* + * No need to destroy pwrctrl devices as this function only gets called + * during system suspend as of now. + */ + pci_pwrctrl_power_off_devices(pci->dev); + } + qcom_pcie_phy_power_off(pcie); pcie->cfg->ops->deinit(pcie); } @@ -2051,11 +2058,16 @@ static int qcom_pcie_resume_noirq(struct device *dev) ret = icc_enable(pcie->icc_mem); if (ret) { dev_err(dev, "Failed to enable PCIe-MEM interconnect path: %d\n", ret); - return ret; + goto disable_icc_cpu; } + + /* + * Ignore -ENODEV & -EIO here since it is expected when no endpoint is + * connected to the PCIe link. + */ ret = dw_pcie_resume_noirq(pcie->pci); - if (ret && (ret != -ETIMEDOUT)) - return ret; + if (ret && ret != -ENODEV && ret != -EIO) + goto disable_icc_mem; } else { if (pm_suspend_target_state != PM_SUSPEND_MEM) { ret = icc_enable(pcie->icc_cpu); @@ -2070,6 +2082,12 @@ static int qcom_pcie_resume_noirq(struct device *dev) qcom_pcie_icc_opp_update(pcie); return 0; +disable_icc_mem: + icc_disable(pcie->icc_mem); +disable_icc_cpu: + icc_disable(pcie->icc_cpu); + + return ret; } static const struct of_device_id qcom_pcie_match[] = { From d549eaff6ecc5ce27cb517546adb12990108b651 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Tue, 21 Apr 2026 16:11:01 +0530 Subject: [PATCH 27/30] FROMLIST: PCI/pwrctrl: Do not try to power on/off devices that don't need pwrctrl pci_pwrctrl_is_required() is used to detect whether a device really needs the PCI pwrctrl support or not. It is currently used in pci_pwrctrl_create_device(), but not in pci_pwrctrl_power_{on/off}_device() APIs. This leads to pwrctrl core trying to power on/off the incompatible devices like USB hub downstream ports defined in DT. Hence, add this check to prevent pwrctrl core from poking at wrong devices. For this purpose, move the pci_pwrctrl_is_required() helper definition to the top. Link: https://lore.kernel.org/linux-pci/20260421104102.12322-1-manivannan.sadhasivam@oss.qualcomm.com/ Fixes: b35cf3b6aa1e ("PCI/pwrctrl: Add APIs to power on/off pwrctrl devices") Reported-by: Krishna Chaitanya Chundru Signed-off-by: Manivannan Sadhasivam Signed-off-by: Ziyue Zhang --- drivers/pci/pwrctrl/core.c | 49 +++++++++++++++++++++++++++++++++++++ include/linux/pci-pwrctrl.h | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c index 1b91375738a08..47a8771257615 100644 --- a/drivers/pci/pwrctrl/core.c +++ b/drivers/pci/pwrctrl/core.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -138,6 +139,48 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready); +/* + * Check whether the pwrctrl device really needs to be created or not. The + * pwrctrl device will only be created if the node satisfies below requirements: + * + * 1. Presence of compatible property with "pci" prefix to match against the + * pwrctrl driver (AND) + * 2. At least one of the power supplies defined in the devicetree node of the + * device (OR) in the remote endpoint parent node to indicate pwrctrl + * requirement. + */ +static bool pci_pwrctrl_is_required(struct device_node *np) +{ + struct device_node *endpoint; + const char *compat; + int ret; + + ret = of_property_read_string(np, "compatible", &compat); + if (ret < 0) + return false; + + if (!strstarts(compat, "pci")) + return false; + + if (of_pci_supply_present(np)) + return true; + + if (of_graph_is_present(np)) { + for_each_endpoint_of_node(np, endpoint) { + struct device_node *remote __free(device_node) = + of_graph_get_remote_port_parent(endpoint); + if (remote) { + if (of_pci_supply_present(remote)) { + of_node_put(endpoint); + return true; + } + } + } + } + + return false; +} + static int __pci_pwrctrl_power_off_device(struct device *dev) { struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev); @@ -156,6 +199,9 @@ static void pci_pwrctrl_power_off_device(struct device_node *np) for_each_available_child_of_node_scoped(np, child) pci_pwrctrl_power_off_device(child); + if (!pci_pwrctrl_is_required(np)) + return; + pdev = of_find_device_by_node(np); if (!pdev) return; @@ -212,6 +258,9 @@ static int pci_pwrctrl_power_on_device(struct device_node *np) return ret; } + if (!pci_pwrctrl_is_required(np)) + return 0; + pdev = of_find_device_by_node(np); if (!pdev) return 0; diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h index 1192a2527521d..d2b83f5b4961b 100644 --- a/include/linux/pci-pwrctrl.h +++ b/include/linux/pci-pwrctrl.h @@ -54,7 +54,7 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl); void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl); int devm_pci_pwrctrl_device_set_ready(struct device *dev, struct pci_pwrctrl *pwrctrl); -#if IS_ENABLED(CONFIG_PCI_PWRCTRL) +#if IS_REACHABLE(CONFIG_PCI_PWRCTRL) int pci_pwrctrl_create_devices(struct device *parent); void pci_pwrctrl_destroy_devices(struct device *parent); int pci_pwrctrl_power_on_devices(struct device *parent); From 6cb29a7eafb3e3bdd4ea27c27091e188181c1cf7 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 9 Feb 2026 16:59:46 +0530 Subject: [PATCH 28/30] FROMLIST: 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. Link: https://lore.kernel.org/all/20260209112947.930853-2-mohammad.rafi.shaik@oss.qualcomm.com/ Signed-off-by: Mohammad Rafi Shaik --- drivers/soc/qcom/qcom_pd_mapper.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c index 0357be8bf3ad5..bfb48a10fb111 100644 --- a/drivers/soc/qcom/qcom_pd_mapper.c +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -305,6 +305,18 @@ static const struct qcom_pdm_domain_data cdsp_root_pd = { .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, @@ -402,6 +414,15 @@ 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, + &gpdsp_root_pd, + &gpdsp1_root_pd, + NULL, +}; + static const struct qcom_pdm_domain_data *sc7180_domains[] = { &adsp_audio_pd, &adsp_root_pd_pdr, @@ -573,6 +594,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 83c37b4f97e094c8056cb026d28f4e433bf72b67 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 9 Feb 2026 16:59:47 +0530 Subject: [PATCH 29/30] FROMLIST: 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. Link: https://lore.kernel.org/all/20260209112947.930853-3-mohammad.rafi.shaik@oss.qualcomm.com/ Signed-off-by: Mohammad Rafi Shaik --- drivers/soc/qcom/qcom_pd_mapper.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/soc/qcom/qcom_pd_mapper.c b/drivers/soc/qcom/qcom_pd_mapper.c index bfb48a10fb111..524e933c388c8 100644 --- a/drivers/soc/qcom/qcom_pd_mapper.c +++ b/drivers/soc/qcom/qcom_pd_mapper.c @@ -414,6 +414,13 @@ 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, + NULL, +}; + static const struct qcom_pdm_domain_data *sa8775p_domains[] = { &adsp_audio_pd, &adsp_root_pd, @@ -594,6 +601,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 a6304208667a3fb8e0a4ebf0dc898801e812bdfd Mon Sep 17 00:00:00 2001 From: Hasibur Rahman Mohammed Date: Wed, 6 May 2026 14:55:16 +0530 Subject: [PATCH 30/30] Update README test Signed-off-by: Hasibur Rahman Mohammed --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index fd903645e6de0..f95233b6b666c 100644 --- a/README +++ b/README @@ -16,3 +16,5 @@ several of them using the reStructuredText markup notation. Please read the Documentation/process/changes.rst file, as it contains the requirements for building and running the kernel, and information about the problems which may result by upgrading your kernel. + +