* [PATCH] KVM: arm64: vgic: Don't reset cpuif/redist addresses at finalize time
From: Marc Zyngier @ 2026-03-23 17:47 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu
Although we are OK with rewriting idregs at finalize time, resetting
the guest's cpuif (GICv3) or redistributor (GICv3) addresses once
we start running the guest is a pretty bad idea.
Move back this initialisation to vgic creation time.
Fixes: a258a383b9177 ("KVM: arm64: gic-v5: Sanitize ID_AA64PFR2_EL1.GCIE")
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
arch/arm64/kvm/vgic/vgic-init.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 47169604100f2..fd872079f2a24 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -147,6 +147,15 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
kvm->arch.vgic.implementation_rev = KVM_VGIC_IMP_REV_LATEST;
kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
+ switch (type) {
+ case KVM_DEV_TYPE_ARM_VGIC_V2:
+ kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
+ break;
+ case KVM_DEV_TYPE_ARM_VGIC_V3:
+ INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
+ break;
+ }
+
/*
* We've now created the GIC. Update the system register state
* to accurately reflect what we've created.
@@ -684,10 +693,8 @@ void kvm_vgic_finalize_idregs(struct kvm *kvm)
switch (type) {
case KVM_DEV_TYPE_ARM_VGIC_V2:
- kvm->arch.vgic.vgic_cpu_base = VGIC_ADDR_UNDEF;
break;
case KVM_DEV_TYPE_ARM_VGIC_V3:
- INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
aa64pfr0 |= SYS_FIELD_PREP_ENUM(ID_AA64PFR0_EL1, GIC, IMP);
pfr1 |= SYS_FIELD_PREP_ENUM(ID_PFR1_EL1, GIC, GICv3);
break;
--
2.47.3
^ permalink raw reply related
* [PATCH] KVM: arm64: set_id_regs: Allow GICv3 support to be set at runtime
From: Marc Zyngier @ 2026-03-23 17:46 UTC (permalink / raw)
To: kvmarm, linux-arm-kernel
Cc: Joey Gouly, Suzuki K Poulose, Oliver Upton, Zenghui Yu,
Mark Brown
set_id_regs creates a GIC3 guest when possible, and then proceeds
to write the ID registers as if they were not affected by the presence
of a GIC. As it turns out, ID_AA64PFR1_EL1 is the proof of the
contrary.
KVM now makes a point in exposing the GIC support to the guest,
no matter what userspace says (userspace such as QEMU is known to
write silly things at times).
Accommodate for this level of nonsense by teaching set_id_regs about
fields that are mutable, and only compare registers that have been
re-sanitised first.
Reported-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
---
.../testing/selftests/kvm/arm64/set_id_regs.c | 52 ++++++++++++++++---
1 file changed, 45 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/kvm/arm64/set_id_regs.c b/tools/testing/selftests/kvm/arm64/set_id_regs.c
index 73de5be58bab0..24f70312b3557 100644
--- a/tools/testing/selftests/kvm/arm64/set_id_regs.c
+++ b/tools/testing/selftests/kvm/arm64/set_id_regs.c
@@ -37,6 +37,9 @@ struct reg_ftr_bits {
* For FTR_LOWER_SAFE, safe_val is used as the minimal safe value.
*/
int64_t safe_val;
+
+ /* Allowed to be changed by the host after run */
+ bool mutable;
};
struct test_feature_reg {
@@ -44,7 +47,7 @@ struct test_feature_reg {
const struct reg_ftr_bits *ftr_bits;
};
-#define __REG_FTR_BITS(NAME, SIGNED, TYPE, SHIFT, MASK, SAFE_VAL) \
+#define __REG_FTR_BITS(NAME, SIGNED, TYPE, SHIFT, MASK, SAFE_VAL, MUT) \
{ \
.name = #NAME, \
.sign = SIGNED, \
@@ -52,15 +55,20 @@ struct test_feature_reg {
.shift = SHIFT, \
.mask = MASK, \
.safe_val = SAFE_VAL, \
+ .mutable = MUT, \
}
#define REG_FTR_BITS(type, reg, field, safe_val) \
__REG_FTR_BITS(reg##_##field, FTR_UNSIGNED, type, reg##_##field##_SHIFT, \
- reg##_##field##_MASK, safe_val)
+ reg##_##field##_MASK, safe_val, false)
+
+#define REG_FTR_BITS_MUTABLE(type, reg, field, safe_val) \
+ __REG_FTR_BITS(reg##_##field, FTR_UNSIGNED, type, reg##_##field##_SHIFT, \
+ reg##_##field##_MASK, safe_val, true)
#define S_REG_FTR_BITS(type, reg, field, safe_val) \
__REG_FTR_BITS(reg##_##field, FTR_SIGNED, type, reg##_##field##_SHIFT, \
- reg##_##field##_MASK, safe_val)
+ reg##_##field##_MASK, safe_val, false)
#define REG_FTR_END \
{ \
@@ -134,7 +142,8 @@ static const struct reg_ftr_bits ftr_id_aa64pfr0_el1[] = {
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, CSV2, 0),
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, DIT, 0),
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, SEL2, 0),
- REG_FTR_BITS(FTR_EXACT, ID_AA64PFR0_EL1, GIC, 0),
+ /* GICv3 support will be forced at run time if available */
+ REG_FTR_BITS_MUTABLE(FTR_EXACT, ID_AA64PFR0_EL1, GIC, 0),
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL3, 1),
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL2, 1),
REG_FTR_BITS(FTR_LOWER_SAFE, ID_AA64PFR0_EL1, EL1, 1),
@@ -634,12 +643,38 @@ static void test_user_set_mte_reg(struct kvm_vcpu *vcpu)
ksft_test_result_pass("ID_AA64PFR1_EL1.MTE_frac no longer 0xF\n");
}
+static uint64_t reset_mutable_bits(uint32_t id, uint64_t val)
+{
+ struct test_feature_reg *reg = NULL;
+
+ for (int i = 0; i < ARRAY_SIZE(test_regs); i++) {
+ if (test_regs[i].reg == id) {
+ reg = &test_regs[i];
+ break;
+ }
+ }
+
+ if (!reg)
+ return val;
+
+ for (const struct reg_ftr_bits *bits = reg->ftr_bits; bits->type != FTR_END; bits++) {
+ if (bits->mutable) {
+ val &= ~bits->mask;
+ val |= bits->safe_val << bits->shift;
+ }
+ }
+
+ return val;
+}
+
static void test_guest_reg_read(struct kvm_vcpu *vcpu)
{
bool done = false;
struct ucall uc;
while (!done) {
+ uint64_t val;
+
vcpu_run(vcpu);
switch (get_ucall(vcpu, &uc)) {
@@ -647,9 +682,11 @@ static void test_guest_reg_read(struct kvm_vcpu *vcpu)
REPORT_GUEST_ASSERT(uc);
break;
case UCALL_SYNC:
+ val = test_reg_vals[encoding_to_range_idx(uc.args[2])];
+ val = reset_mutable_bits(uc.args[2], val);
+
/* Make sure the written values are seen by guest */
- TEST_ASSERT_EQ(test_reg_vals[encoding_to_range_idx(uc.args[2])],
- uc.args[3]);
+ TEST_ASSERT_EQ(val, reset_mutable_bits(uc.args[2], uc.args[3]));
break;
case UCALL_DONE:
done = true;
@@ -740,7 +777,8 @@ static void test_assert_id_reg_unchanged(struct kvm_vcpu *vcpu, uint32_t encodin
uint64_t observed;
observed = vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(encoding));
- TEST_ASSERT_EQ(test_reg_vals[idx], observed);
+ TEST_ASSERT_EQ(reset_mutable_bits(test_reg_vals[idx], encoding),
+ reset_mutable_bits(observed, encoding));
}
static void test_reset_preserves_id_regs(struct kvm_vcpu *vcpu)
--
2.47.3
^ permalink raw reply related
* [PATCH RESEND v4 4/4] drm/rockchip: dw_hdmi_qp: Do not send HPD events for all connectors
From: Cristian Ciocaltea @ 2026-03-23 17:45 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip,
Diederik de Haas, Maud Spierings
In-Reply-To: <20260323-dw-hdmi-qp-scramb-v4-0-1d99df84883e@collabora.com>
In order to optimize the HPD event handling and run the detect cycle on
the affected connector only, make use of
drm_connector_helper_hpd_irq_event() instead of
drm_helper_hpd_irq_event().
Additionally, move devm_request_threaded_irq() after bridge connector
initialization.
Tested-by: Diederik de Haas <diederik@cknow-tech.com>
Tested-by: Maud Spierings <maud_spierings@hotmail.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 44 ++++++++++++--------------
1 file changed, 20 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index 1a09bcc96c3e..65dfaee15178 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -92,6 +92,7 @@ struct rockchip_hdmi_qp {
struct regmap *regmap;
struct regmap *vo_regmap;
struct rockchip_encoder encoder;
+ struct drm_connector *connector;
struct dw_hdmi_qp *hdmi;
struct phy *phy;
struct gpio_desc *frl_enable_gpio;
@@ -251,14 +252,10 @@ static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
struct rockchip_hdmi_qp *hdmi = container_of(work,
struct rockchip_hdmi_qp,
hpd_work.work);
- struct drm_device *drm = hdmi->encoder.encoder.dev;
- bool changed;
+ bool changed = drm_connector_helper_hpd_irq_event(hdmi->connector);
- if (drm) {
- changed = drm_helper_hpd_irq_event(drm);
- if (changed)
- dev_dbg(hdmi->dev, "connector status changed\n");
- }
+ if (changed)
+ dev_dbg(hdmi->dev, "connector status changed\n");
}
static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id)
@@ -466,13 +463,12 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
struct dw_hdmi_qp_plat_data plat_data = {};
const struct rockchip_hdmi_qp_cfg *cfg;
struct drm_device *drm = data;
- struct drm_connector *connector;
struct drm_encoder *encoder;
struct rockchip_hdmi_qp *hdmi;
struct resource *res;
struct clk_bulk_data *clks;
struct clk *ref_clk;
- int ret, irq, i;
+ int ret, hpd_irq, i;
if (!pdev->dev.of_node)
return -ENODEV;
@@ -573,17 +569,9 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
if (plat_data.cec_irq < 0)
return plat_data.cec_irq;
- irq = platform_get_irq_byname(pdev, "hpd");
- if (irq < 0)
- return irq;
-
- ret = devm_request_threaded_irq(hdmi->dev, irq,
- cfg->ctrl_ops->hardirq_callback,
- cfg->ctrl_ops->irq_callback,
- IRQF_SHARED, "dw-hdmi-qp-hpd",
- hdmi);
- if (ret)
- return ret;
+ hpd_irq = platform_get_irq_byname(pdev, "hpd");
+ if (hpd_irq < 0)
+ return hpd_irq;
drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
@@ -597,12 +585,20 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
"Failed to bind dw-hdmi-qp");
}
- connector = drm_bridge_connector_init(drm, encoder);
- if (IS_ERR(connector))
- return dev_err_probe(hdmi->dev, PTR_ERR(connector),
+ hdmi->connector = drm_bridge_connector_init(drm, encoder);
+ if (IS_ERR(hdmi->connector))
+ return dev_err_probe(hdmi->dev, PTR_ERR(hdmi->connector),
"Failed to init bridge connector\n");
- return drm_connector_attach_encoder(connector, encoder);
+ ret = drm_connector_attach_encoder(hdmi->connector, encoder);
+ if (ret)
+ return ret;
+
+ return devm_request_threaded_irq(hdmi->dev, hpd_irq,
+ cfg->ctrl_ops->hardirq_callback,
+ cfg->ctrl_ops->irq_callback,
+ IRQF_SHARED, "dw-hdmi-qp-hpd",
+ hdmi);
}
static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
--
2.52.0
^ permalink raw reply related
* [PATCH RESEND v4 3/4] drm/bridge: dw-hdmi-qp: Add high TMDS clock ratio and scrambling support
From: Cristian Ciocaltea @ 2026-03-23 17:45 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip,
Diederik de Haas, Maud Spierings
In-Reply-To: <20260323-dw-hdmi-qp-scramb-v4-0-1d99df84883e@collabora.com>
Add support for HDMI 2.0 display modes, e.g. 4K@60Hz, by permitting TMDS
character rates above the 340 MHz limit of HDMI 1.4b.
Hence, provide the required SCDC management, including the high TMDS
clock ratio and scrambling setup, and filter out the HDMI 2.1 modes.
Tested-by: Diederik de Haas <diederik@cknow-tech.com>
Tested-by: Maud Spierings <maud_spierings@hotmail.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 167 ++++++++++++++++++++++++---
1 file changed, 150 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index d649a1cf07f5..e40f16a364ed 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -2,6 +2,7 @@
/*
* Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
* Copyright (c) 2024 Collabora Ltd.
+ * Copyright (c) 2025 Amazon.com, Inc. or its affiliates.
*
* Author: Algea Cao <algea.cao@rock-chips.com>
* Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
@@ -21,9 +22,11 @@
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_cec_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
+#include <drm/display/drm_scdc_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_helper.h>
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_modes.h>
@@ -39,8 +42,10 @@
#define DDC_SEGMENT_ADDR 0x30
#define HDMI14_MAX_TMDSCLK 340000000
+#define HDMI20_MAX_TMDSRATE 600000000
-#define SCRAMB_POLL_DELAY_MS 3000
+#define SCDC_MIN_SOURCE_VERSION 0x1
+#define SCRAMB_POLL_DELAY_MS 5000
/*
* Unless otherwise noted, entries in this table are 100% optimization.
@@ -164,6 +169,11 @@ struct dw_hdmi_qp {
} phy;
unsigned long ref_clk_rate;
+
+ struct drm_connector *curr_conn;
+ struct delayed_work scramb_work;
+ bool scramb_enabled;
+
struct regmap *regm;
int main_irq;
@@ -749,28 +759,98 @@ static struct i2c_adapter *dw_hdmi_qp_i2c_adapter(struct dw_hdmi_qp *hdmi)
return adap;
}
+static bool dw_hdmi_qp_supports_scrambling(struct drm_display_info *display)
+{
+ if (!display->is_hdmi)
+ return false;
+
+ return display->hdmi.scdc.supported &&
+ display->hdmi.scdc.scrambling.supported;
+}
+
+static void dw_hdmi_qp_set_scramb(struct dw_hdmi_qp *hdmi)
+{
+ dev_dbg(hdmi->dev, "set scrambling\n");
+
+ drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, true);
+ drm_scdc_set_scrambling(hdmi->curr_conn, true);
+
+ schedule_delayed_work(&hdmi->scramb_work,
+ msecs_to_jiffies(SCRAMB_POLL_DELAY_MS));
+}
+
+static void dw_hdmi_qp_scramb_work(struct work_struct *work)
+{
+ struct dw_hdmi_qp *hdmi = container_of(to_delayed_work(work),
+ struct dw_hdmi_qp,
+ scramb_work);
+ if (!drm_scdc_get_scrambling_status(hdmi->curr_conn))
+ dw_hdmi_qp_set_scramb(hdmi);
+}
+
+static void dw_hdmi_qp_enable_scramb(struct dw_hdmi_qp *hdmi)
+{
+ u8 ver;
+
+ if (!dw_hdmi_qp_supports_scrambling(&hdmi->curr_conn->display_info))
+ return;
+
+ drm_scdc_readb(hdmi->bridge.ddc, SCDC_SINK_VERSION, &ver);
+ drm_scdc_writeb(hdmi->bridge.ddc, SCDC_SOURCE_VERSION,
+ min_t(u8, ver, SCDC_MIN_SOURCE_VERSION));
+
+ dw_hdmi_qp_set_scramb(hdmi);
+ dw_hdmi_qp_write(hdmi, 1, SCRAMB_CONFIG0);
+
+ hdmi->scramb_enabled = true;
+
+ /* Wait at least 1 ms before resuming TMDS transmission */
+ usleep_range(1000, 5000);
+}
+
+static void dw_hdmi_qp_disable_scramb(struct dw_hdmi_qp *hdmi)
+{
+ if (!hdmi->scramb_enabled)
+ return;
+
+ dev_dbg(hdmi->dev, "disable scrambling\n");
+
+ hdmi->scramb_enabled = false;
+ cancel_delayed_work_sync(&hdmi->scramb_work);
+
+ dw_hdmi_qp_write(hdmi, 0, SCRAMB_CONFIG0);
+
+ if (hdmi->curr_conn->status == connector_status_connected) {
+ drm_scdc_set_scrambling(hdmi->curr_conn, false);
+ drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, false);
+ }
+}
+
static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
struct drm_connector_state *conn_state;
- struct drm_connector *connector;
unsigned int op_mode;
- connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
- if (WARN_ON(!connector))
+ hdmi->curr_conn = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
+ if (WARN_ON(!hdmi->curr_conn))
return;
- conn_state = drm_atomic_get_new_connector_state(state, connector);
+ conn_state = drm_atomic_get_new_connector_state(state, hdmi->curr_conn);
if (WARN_ON(!conn_state))
return;
- if (connector->display_info.is_hdmi) {
+ if (hdmi->curr_conn->display_info.is_hdmi) {
dev_dbg(hdmi->dev, "%s mode=HDMI %s rate=%llu bpc=%u\n", __func__,
drm_hdmi_connector_get_output_format_name(conn_state->hdmi.output_format),
conn_state->hdmi.tmds_char_rate, conn_state->hdmi.output_bpc);
op_mode = 0;
hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
+
+ if (conn_state->hdmi.tmds_char_rate > HDMI14_MAX_TMDSCLK)
+ dw_hdmi_qp_enable_scramb(hdmi);
} else {
dev_dbg(hdmi->dev, "%s mode=DVI\n", __func__);
op_mode = OPMODE_DVI;
@@ -781,7 +861,7 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge,
dw_hdmi_qp_mod(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
dw_hdmi_qp_mod(hdmi, op_mode, OPMODE_DVI, LINK_CONFIG0);
- drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+ drm_atomic_helper_connector_hdmi_update_infoframes(hdmi->curr_conn, state);
}
static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
@@ -791,13 +871,56 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
hdmi->tmds_char_rate = 0;
+ dw_hdmi_qp_disable_scramb(hdmi);
+
+ hdmi->curr_conn = NULL;
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
}
-static enum drm_connector_status
-dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
+static int dw_hdmi_qp_reset_crtc(struct dw_hdmi_qp *hdmi,
+ struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ u8 config;
+ int ret;
+
+ ret = drm_scdc_readb(hdmi->bridge.ddc, SCDC_TMDS_CONFIG, &config);
+ if (ret < 0) {
+ dev_err(hdmi->dev, "Failed to read TMDS config: %d\n", ret);
+ return ret;
+ }
+
+ if (!!(config & SCDC_SCRAMBLING_ENABLE) == hdmi->scramb_enabled)
+ return 0;
+
+ drm_atomic_helper_connector_hdmi_hotplug(connector,
+ connector_status_connected);
+ /*
+ * Conform to HDMI 2.0 spec by ensuring scrambled data is not sent
+ * before configuring the sink scrambling, as well as suspending any
+ * TMDS transmission while changing the TMDS clock rate in the sink.
+ */
+
+ dev_dbg(hdmi->dev, "resetting crtc\n");
+
+retry:
+ ret = drm_bridge_helper_reset_crtc(&hdmi->bridge, ctx);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(ctx);
+ goto retry;
+ } else if (ret) {
+ dev_err(hdmi->dev, "Failed to reset crtc: %d\n", ret);
+ }
+
+ return ret;
+}
+
+static int dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx)
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ enum drm_connector_status status;
const struct drm_edid *drm_edid;
if (hdmi->no_hpd) {
@@ -808,7 +931,15 @@ dw_hdmi_qp_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connec
return connector_status_disconnected;
}
- return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+ status = hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
+
+ dev_dbg(hdmi->dev, "%s status=%d scramb=%d\n", __func__,
+ status, hdmi->scramb_enabled);
+
+ if (status == connector_status_connected && hdmi->scramb_enabled)
+ dw_hdmi_qp_reset_crtc(hdmi, connector, ctx);
+
+ return status;
}
static const struct drm_edid *
@@ -832,12 +963,12 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
- /*
- * TODO: when hdmi->no_hpd is 1 we must not support modes that
- * require scrambling, including every mode with a clock above
- * HDMI14_MAX_TMDSCLK.
- */
- if (rate > HDMI14_MAX_TMDSCLK) {
+ if (hdmi->no_hpd && rate > HDMI14_MAX_TMDSCLK) {
+ dev_dbg(hdmi->dev, "Unsupported TMDS char rate in no_hpd mode: %lld\n", rate);
+ return MODE_CLOCK_HIGH;
+ }
+
+ if (rate > HDMI20_MAX_TMDSRATE) {
dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate);
return MODE_CLOCK_HIGH;
}
@@ -1197,7 +1328,7 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
.atomic_enable = dw_hdmi_qp_bridge_atomic_enable,
.atomic_disable = dw_hdmi_qp_bridge_atomic_disable,
- .detect = dw_hdmi_qp_bridge_detect,
+ .detect_ctx = dw_hdmi_qp_bridge_detect,
.edid_read = dw_hdmi_qp_bridge_edid_read,
.hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid,
.hdmi_clear_avi_infoframe = dw_hdmi_qp_bridge_clear_avi_infoframe,
@@ -1287,6 +1418,8 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
if (IS_ERR(hdmi))
return ERR_CAST(hdmi);
+ INIT_DELAYED_WORK(&hdmi->scramb_work, dw_hdmi_qp_scramb_work);
+
hdmi->dev = dev;
regs = devm_platform_ioremap_resource(pdev, 0);
--
2.52.0
^ permalink raw reply related
* [PATCH RESEND v4 2/4] drm/bridge-connector: Switch to using ->detect_ctx hook
From: Cristian Ciocaltea @ 2026-03-23 17:45 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip,
Diederik de Haas, Maud Spierings
In-Reply-To: <20260323-dw-hdmi-qp-scramb-v4-0-1d99df84883e@collabora.com>
In preparation to allow bridge drivers relying on the HDMI connector
framework to provide HDMI 2.0 support, make use of the atomic version of
drm_connector_funcs.detect() hook and invoke the newly introduced
drm_bridge_detect_ctx() helper.
In particular, this is going to be used for triggering an empty modeset
in drm_bridge_funcs.detect_ctx() callback, in order to manage SCDC
status lost on sink disconnects.
Tested-by: Diederik de Haas <diederik@cknow-tech.com>
Tested-by: Maud Spierings <maud_spierings@hotmail.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 73 ++++++++++++++------------
1 file changed, 38 insertions(+), 35 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index f686aa5c0ed9..626f15aba5d5 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -208,39 +208,6 @@ static void drm_bridge_connector_disable_hpd(struct drm_connector *connector)
* Bridge Connector Functions
*/
-static enum drm_connector_status
-drm_bridge_connector_detect(struct drm_connector *connector, bool force)
-{
- struct drm_bridge_connector *bridge_connector =
- to_drm_bridge_connector(connector);
- struct drm_bridge *detect = bridge_connector->bridge_detect;
- struct drm_bridge *hdmi = bridge_connector->bridge_hdmi;
- enum drm_connector_status status;
-
- if (detect) {
- status = detect->funcs->detect(detect, connector);
-
- if (hdmi)
- drm_atomic_helper_connector_hdmi_hotplug(connector, status);
-
- drm_bridge_connector_hpd_notify(connector, status);
- } else {
- switch (connector->connector_type) {
- case DRM_MODE_CONNECTOR_DPI:
- case DRM_MODE_CONNECTOR_LVDS:
- case DRM_MODE_CONNECTOR_DSI:
- case DRM_MODE_CONNECTOR_eDP:
- status = connector_status_connected;
- break;
- default:
- status = connector_status_unknown;
- break;
- }
- }
-
- return status;
-}
-
static void drm_bridge_connector_force(struct drm_connector *connector)
{
struct drm_bridge_connector *bridge_connector =
@@ -278,7 +245,6 @@ static void drm_bridge_connector_reset(struct drm_connector *connector)
static const struct drm_connector_funcs drm_bridge_connector_funcs = {
.reset = drm_bridge_connector_reset,
- .detect = drm_bridge_connector_detect,
.force = drm_bridge_connector_force,
.fill_modes = drm_helper_probe_single_connector_modes,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -291,6 +257,42 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = {
* Bridge Connector Helper Functions
*/
+static int drm_bridge_connector_detect_ctx(struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool force)
+{
+ struct drm_bridge_connector *bridge_connector =
+ to_drm_bridge_connector(connector);
+ struct drm_bridge *detect = bridge_connector->bridge_detect;
+ struct drm_bridge *hdmi = bridge_connector->bridge_hdmi;
+ int ret;
+
+ if (detect) {
+ ret = drm_bridge_detect_ctx(detect, connector, ctx);
+ if (ret < 0)
+ return ret;
+
+ if (hdmi)
+ drm_atomic_helper_connector_hdmi_hotplug(connector, ret);
+
+ drm_bridge_connector_hpd_notify(connector, ret);
+ } else {
+ switch (connector->connector_type) {
+ case DRM_MODE_CONNECTOR_DPI:
+ case DRM_MODE_CONNECTOR_LVDS:
+ case DRM_MODE_CONNECTOR_DSI:
+ case DRM_MODE_CONNECTOR_eDP:
+ ret = connector_status_connected;
+ break;
+ default:
+ ret = connector_status_unknown;
+ break;
+ }
+ }
+
+ return ret;
+}
+
static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector,
struct drm_bridge *bridge)
{
@@ -298,7 +300,7 @@ static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector,
const struct drm_edid *drm_edid;
int n;
- status = drm_bridge_connector_detect(connector, false);
+ status = drm_bridge_connector_detect_ctx(connector, NULL, false);
if (status != connector_status_connected)
goto no_edid;
@@ -384,6 +386,7 @@ static int drm_bridge_connector_atomic_check(struct drm_connector *connector,
static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = {
.get_modes = drm_bridge_connector_get_modes,
+ .detect_ctx = drm_bridge_connector_detect_ctx,
.mode_valid = drm_bridge_connector_mode_valid,
.enable_hpd = drm_bridge_connector_enable_hpd,
.disable_hpd = drm_bridge_connector_disable_hpd,
--
2.52.0
^ permalink raw reply related
* [PATCH RESEND v4 0/4] Add HDMI 2.0 support to DW HDMI QP TX
From: Cristian Ciocaltea @ 2026-03-23 17:45 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip,
Diederik de Haas, Maud Spierings
This patch series provides the missing support for high TMDS clock ratio
and scrambling to DW HDMI QP TX library, required for handling HDMI 2.0
display modes on RK3576 & RK3588 SoCs.
In order to allow addressing the SCDC status lost on sink disconnects,
it adds an atomic variant of the drm_bridge_funcs.detect() callback and
a new drm_bridge_detect_ctx() helper, which is further used in
drm_bridge_connector to switch to using the ->detect_ctx() hook.
Furthermore, it optimizes HPD event handling in dw_hdmi_qp Rockchip
platform driver to run the detect cycle on the affected connector only.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
Changes in v4:
- Fixed conflicts while rebasing onto latest drm-misc-next
- Link to v3: https://lore.kernel.org/r/20260119-dw-hdmi-qp-scramb-v3-0-bd8611730fc1@collabora.com
Changes in v3:
- Used drm_bridge_helper_reset_crtc() helper to reset the display
pipeline and got rid of some boilerplate code (Maxime)
- Rebased onto latest drm-misc-next
- Link to v2: https://lore.kernel.org/r/20260113-dw-hdmi-qp-scramb-v2-0-ae7b2c58d24d@collabora.com
Changes in v2:
- Collected Tested-by tags from Diederik and Maud
- Rebased onto latest drm-misc-next
- Ensured the recently introduced 'no-hpd' support for dealing with
unconnected/repurposed/broken HPD pin is limited to HDMI 1.4 rates
- Link to v1: https://lore.kernel.org/r/20251203-dw-hdmi-qp-scramb-v1-0-836fe7401a69@collabora.com
---
Cristian Ciocaltea (4):
drm/bridge: Add ->detect_ctx hook and drm_bridge_detect_ctx()
drm/bridge-connector: Switch to using ->detect_ctx hook
drm/bridge: dw-hdmi-qp: Add high TMDS clock ratio and scrambling support
drm/rockchip: dw_hdmi_qp: Do not send HPD events for all connectors
drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 167 ++++++++++++++++++++++---
drivers/gpu/drm/display/drm_bridge_connector.c | 73 +++++------
drivers/gpu/drm/drm_bridge.c | 58 +++++++++
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 44 +++----
include/drm/drm_bridge.h | 30 +++++
5 files changed, 296 insertions(+), 76 deletions(-)
---
base-commit: 0dd7a23da214279cfb77b0eb03c38614867524d7
change-id: 20251203-dw-hdmi-qp-scramb-cdbd8b57ccf9
--
Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
^ permalink raw reply
* [PATCH RESEND v4 1/4] drm/bridge: Add ->detect_ctx hook and drm_bridge_detect_ctx()
From: Cristian Ciocaltea @ 2026-03-23 17:45 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Sandy Huang,
Heiko Stübner, Andy Yan
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip,
Diederik de Haas, Maud Spierings
In-Reply-To: <20260323-dw-hdmi-qp-scramb-v4-0-1d99df84883e@collabora.com>
Add an atomic variant of the ->detect callback and a new helper to call
the hook while passing an optional drm_modeset_acquire_ctx reference.
When both ->detect_ctx and ->detect are defined, the latter is ignored.
If acquire_ctx is unset, the function takes care of the locking,
while also handling EDEADLK.
Tested-by: Diederik de Haas <diederik@cknow-tech.com>
Tested-by: Maud Spierings <maud_spierings@hotmail.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/drm_bridge.c | 58 ++++++++++++++++++++++++++++++++++++++++++++
include/drm/drm_bridge.h | 30 +++++++++++++++++++++++
2 files changed, 88 insertions(+)
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index f8b0333a0a3b..1247573c2100 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -1345,6 +1345,64 @@ drm_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
}
EXPORT_SYMBOL_GPL(drm_bridge_detect);
+/**
+ * drm_bridge_detect_ctx - check if anything is attached to the bridge output
+ * @bridge: bridge control structure
+ * @connector: attached connector
+ * @ctx: acquire_ctx, or NULL to let this function handle locking
+ *
+ * If the bridge supports output detection, as reported by the
+ * DRM_BRIDGE_OP_DETECT bridge ops flag, call &drm_bridge_funcs.detect_ctx
+ * or &drm_bridge_funcs.detect for the bridge and return the connection status.
+ * Otherwise return connector_status_unknown.
+ *
+ * When both @ctx and &drm_bridge_funcs.detect_ctx are not set, this helper
+ * function is equivalent to drm_bridge_detect() above.
+ *
+ * RETURNS:
+ * The detection status on success, or connector_status_unknown if the bridge
+ * doesn't support output detection.
+ * If @ctx is set, it might also return -EDEADLK.
+ */
+int drm_bridge_detect_ctx(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx)
+{
+ if (!(bridge->ops & DRM_BRIDGE_OP_DETECT))
+ return connector_status_unknown;
+
+ if (bridge->funcs->detect_ctx) {
+ struct drm_modeset_acquire_ctx br_ctx;
+ int ret;
+
+ if (ctx)
+ return bridge->funcs->detect_ctx(bridge, connector, ctx);
+
+ drm_modeset_acquire_init(&br_ctx, 0);
+retry:
+ ret = drm_modeset_lock(&connector->dev->mode_config.connection_mutex,
+ &br_ctx);
+ if (!ret)
+ ret = bridge->funcs->detect_ctx(bridge, connector, &br_ctx);
+
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&br_ctx);
+ goto retry;
+ }
+
+ if (ret < 0)
+ ret = connector_status_unknown;
+
+ drm_modeset_drop_locks(&br_ctx);
+ drm_modeset_acquire_fini(&br_ctx);
+
+ return ret;
+ }
+
+ return bridge->funcs->detect(bridge, connector);
+}
+EXPORT_SYMBOL_GPL(drm_bridge_detect_ctx);
+
/**
* drm_bridge_get_modes - fill all modes currently valid for the sink into the
* @connector
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 4f19f7064ee3..31ef1496fe63 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -547,6 +547,33 @@ struct drm_bridge_funcs {
enum drm_connector_status (*detect)(struct drm_bridge *bridge,
struct drm_connector *connector);
+ /**
+ * @detect_ctx:
+ *
+ * Check if anything is attached to the bridge output.
+ *
+ * This callback is optional, if not implemented the bridge will be
+ * considered as always having a component attached to its output.
+ * Bridges that implement this callback shall set the
+ * DRM_BRIDGE_OP_DETECT flag in their &drm_bridge->ops.
+ *
+ * This is the atomic version of &drm_bridge_funcs.detect.
+ *
+ * To avoid races against concurrent connector state updates, the
+ * helper libraries always call this with ctx set to a valid context,
+ * and &drm_mode_config.connection_mutex will always be locked with
+ * the ctx parameter set to this ctx. This allows taking additional
+ * locks as required.
+ *
+ * RETURNS:
+ *
+ * &drm_connector_status indicating the bridge output status,
+ * or the error code returned by drm_modeset_lock(), -EDEADLK.
+ */
+ int (*detect_ctx)(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx);
+
/**
* @get_modes:
*
@@ -1542,6 +1569,9 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge,
enum drm_connector_status
drm_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector);
+int drm_bridge_detect_ctx(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct drm_modeset_acquire_ctx *ctx);
int drm_bridge_get_modes(struct drm_bridge *bridge,
struct drm_connector *connector);
const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge,
--
2.52.0
^ permalink raw reply related
* Re: [PATCH 5/5] pinctrl: sunxi: a523: add missing IRQ bank (plus old DT workaround)
From: Chen-Yu Tsai @ 2026-03-23 17:41 UTC (permalink / raw)
To: Andre Przywara
Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jernej Skrabec, Samuel Holland, Michal Piekos, linux-gpio,
devicetree, linux-arm-kernel, linux-sunxi, linux-kernel
In-Reply-To: <20260323110151.2352832-6-andre.przywara@arm.com>
On Mon, Mar 23, 2026 at 7:02 PM Andre Przywara <andre.przywara@arm.com> wrote:
>
> The Allwinner A532 SoC implements 10 GPIO banks, each of which is
> interrupt capable. However the first bank (PortA) is skipped, so the
> indicies of those banks range from 1 to 10, not 0 to 9.
> We described the skipped bank correctly, but missed that for the IRQ
> banks, where we rely on the IRQ bank index to be aligned with the MMIO
> register offset, starting at 0x200.
>
> Correct that by increasing the number of IRQ banks to 11, to cover both
> the first skipped one, but also the last one (PortK). This fixes a bug
> where the interrupt numbers would be off-by-one, due to that
> mis-enumeration.
> The big caveat is that now old DTs break the kernel, since they only
> provide 10 interrupts, and the driver bails out entirely due to the last
> missing one. So add a workaround for this particular case, where we
> detect the requirement for 11 banks, but only 10 interrupts provided,
> and continue with 10 IRQs, albeit emitting a warning about a DT update.
> This would still be broken in terms of interrupt assignment, but it was
> broken the whole time before, so it's not a regression.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> ---
> drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c | 2 +-
> drivers/pinctrl/sunxi/pinctrl-sunxi.c | 22 +++++++++++++--------
> 2 files changed, 15 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c
> index b6f78f1f30ac..a1d157de53d2 100644
> --- a/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c
> +++ b/drivers/pinctrl/sunxi/pinctrl-sun55i-a523.c
> @@ -17,7 +17,7 @@ static const u8 a523_nr_bank_pins[SUNXI_PINCTRL_MAX_BANKS] =
> /* PA PB PC PD PE PF PG PH PI PJ PK */
> { 0, 15, 17, 24, 16, 7, 15, 20, 17, 28, 24 };
>
> -static const unsigned int a523_irq_bank_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
> +static const unsigned int a523_irq_bank_map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Actually you don't even need this, since this is a linear mapping.
From sunxi_irq_hw_bank_num():
if (!desc->irq_bank_map)
return bank;
else
return desc->irq_bank_map[bank];
> static const u8 a523_irq_bank_muxes[SUNXI_PINCTRL_MAX_BANKS] =
> /* PA PB PC PD PE PF PG PH PI PJ PK */
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> index 6a86b7989b25..ffee79397590 100644
> --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> @@ -19,6 +19,7 @@
> #include <linux/irqdomain.h>
> #include <linux/of.h>
> #include <linux/of_clk.h>
> +#include <linux/of_irq.h>
> #include <linux/platform_device.h>
> #include <linux/regulator/consumer.h>
> #include <linux/slab.h>
> @@ -1582,6 +1583,7 @@ int sunxi_pinctrl_init_with_flags(struct platform_device *pdev,
> struct sunxi_pinctrl *pctl;
> struct pinmux_ops *pmxops;
> int i, ret, last_pin, pin_idx;
> + int num_irq_banks;
> struct clk *clk;
>
> pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
> @@ -1715,16 +1717,20 @@ int sunxi_pinctrl_init_with_flags(struct platform_device *pdev,
> goto gpiochip_error;
> }
>
> - pctl->irq = devm_kcalloc(&pdev->dev,
> - pctl->desc->irq_banks,
> - sizeof(*pctl->irq),
> - GFP_KERNEL);
> + num_irq_banks = pctl->desc->irq_banks;
> + /* Workaround for old A523 DT, exposing one less interrupt. */
> + if (num_irq_banks == 11 && of_irq_count(node) < 11) {
> + num_irq_banks = 10;
> + pr_warn("Not enough PIO interrupts, please update your DT!\n");
> + }
I would probably make the check universal, and also use dev_warn().
num_irq_banks = of_irq_count(node);
if (num_irq_banks != pctrl->desc->irq_banks) {
dev_warn(&pdev->dev, "Incorrect number of PIO interrupts,
please update your DT!\n");
num_irq_banks = min(num_irq_banks, pctrl->desc->irq_banks);
}
Otherwise,
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
> + pctl->irq = devm_kcalloc(&pdev->dev, num_irq_banks,
> + sizeof(*pctl->irq), GFP_KERNEL);
> if (!pctl->irq) {
> ret = -ENOMEM;
> goto gpiochip_error;
> }
>
> - for (i = 0; i < pctl->desc->irq_banks; i++) {
> + for (i = 0; i < num_irq_banks; i++) {
> pctl->irq[i] = platform_get_irq(pdev, i);
> if (pctl->irq[i] < 0) {
> ret = pctl->irq[i];
> @@ -1733,7 +1739,7 @@ int sunxi_pinctrl_init_with_flags(struct platform_device *pdev,
> }
>
> pctl->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev),
> - pctl->desc->irq_banks * IRQ_PER_BANK,
> + num_irq_banks * IRQ_PER_BANK,
> &sunxi_pinctrl_irq_domain_ops, pctl);
> if (!pctl->domain) {
> dev_err(&pdev->dev, "Couldn't register IRQ domain\n");
> @@ -1741,7 +1747,7 @@ int sunxi_pinctrl_init_with_flags(struct platform_device *pdev,
> goto gpiochip_error;
> }
>
> - for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) {
> + for (i = 0; i < (num_irq_banks * IRQ_PER_BANK); i++) {
> int irqno = irq_create_mapping(pctl->domain, i);
>
> irq_set_lockdep_class(irqno, &sunxi_pinctrl_irq_lock_class,
> @@ -1751,7 +1757,7 @@ int sunxi_pinctrl_init_with_flags(struct platform_device *pdev,
> irq_set_chip_data(irqno, pctl);
> }
>
> - for (i = 0; i < pctl->desc->irq_banks; i++) {
> + for (i = 0; i < num_irq_banks; i++) {
> /* Mask and clear all IRQs before registering a handler */
> writel(0, pctl->membase +
> sunxi_irq_ctrl_reg_from_bank(pctl->desc, i));
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v1 1/1] scsi: ufs: rockchip: Drop unused include
From: Bart Van Assche @ 2026-03-23 17:31 UTC (permalink / raw)
To: Andy Shevchenko, Shawn Lin, linux-scsi, linux-arm-kernel,
linux-rockchip, linux-kernel
Cc: James E.J. Bottomley, Martin K. Petersen, Heiko Stuebner
In-Reply-To: <20260320215606.3236516-1-andriy.shevchenko@linux.intel.com>
On 3/20/26 2:56 PM, Andy Shevchenko wrote:
> This driver includes the legacy header <linux/gpio.h> but does
> not use any symbols from it. Drop the inclusion.
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
^ permalink raw reply
* Re: [PATCH v1 2/3] arm64: mm: Handle invalid large leaf mappings correctly
From: Ryan Roberts @ 2026-03-23 17:25 UTC (permalink / raw)
To: Kevin Brodsky, Catalin Marinas, Will Deacon,
David Hildenbrand (Arm), Dev Jain, Yang Shi, Suzuki K Poulose,
Jinjiang Tu
Cc: linux-arm-kernel, linux-kernel, stable
In-Reply-To: <588b2b4f-9cf6-43e5-b0e5-55820c74cbbb@arm.com>
On 23/03/2026 16:52, Kevin Brodsky wrote:
> On 23/03/2026 14:03, Ryan Roberts wrote:
>> [...]
>>
>> diff --git a/arch/arm64/mm/pageattr.c b/arch/arm64/mm/pageattr.c
>> index 358d1dc9a576f..87dfe4c82fa92 100644
>> --- a/arch/arm64/mm/pageattr.c
>> +++ b/arch/arm64/mm/pageattr.c
>> @@ -25,6 +25,11 @@ static ptdesc_t set_pageattr_masks(ptdesc_t val, struct mm_walk *walk)
>> {
>> struct page_change_data *masks = walk->private;
>>
>> + /*
>> + * Some users clear and set bits which alias eachother (e.g. PTE_NG and
>
> Nit: "each other"
>
>> + * PTE_PRESENT_INVALID). It is therefore important that we always clear
>> + * first then set.
>> + */
>> val &= ~(pgprot_val(masks->clear_mask));
>> val |= (pgprot_val(masks->set_mask));
>>
>> @@ -36,7 +41,7 @@ static int pageattr_pud_entry(pud_t *pud, unsigned long addr,
>> {
>> pud_t val = pudp_get(pud);
>>
>> - if (pud_sect(val)) {
>> + if (pud_leaf(val)) {
>> if (WARN_ON_ONCE((next - addr) != PUD_SIZE))
>> return -EINVAL;
>> val = __pud(set_pageattr_masks(pud_val(val), walk));
>> @@ -52,7 +57,7 @@ static int pageattr_pmd_entry(pmd_t *pmd, unsigned long addr,
>> {
>> pmd_t val = pmdp_get(pmd);
>>
>> - if (pmd_sect(val)) {
>> + if (pmd_leaf(val)) {
>> if (WARN_ON_ONCE((next - addr) != PMD_SIZE))
>> return -EINVAL;
>> val = __pmd(set_pageattr_masks(pmd_val(val), walk));
>> @@ -132,11 +137,12 @@ static int __change_memory_common(unsigned long start, unsigned long size,
>> ret = update_range_prot(start, size, set_mask, clear_mask);
>>
>> /*
>> - * If the memory is being made valid without changing any other bits
>> - * then a TLBI isn't required as a non-valid entry cannot be cached in
>> - * the TLB.
>> + * If the memory is being switched from present-invalid to valid without
>> + * changing any other bits then a TLBI isn't required as a non-valid
>> + * entry cannot be cached in the TLB.
>> */
>> - if (pgprot_val(set_mask) != PTE_VALID || pgprot_val(clear_mask))
>> + if (pgprot_val(set_mask) != (PTE_MAYBE_NG | PTE_VALID) ||
>
> It isn't obvious to understand where all those PTE_MAYBE_NG come from if
> one hasn't realised that PTE_PRESENT_INVALID overlays PTE_NG.
>
> Since for this purpose we always set/clear both PTE_VALID and
> PTE_MAYBE_NG, maybe we could define some macro as PTE_VALID |
> PTE_MAYBE_NG, as a counterpart to PTE_PRESENT_INVALID?
How about:
#define PTE_PRESENT_VALID_KERNEL (PTE_VALID | PTE_MAYBE_NG)
The user space equivalent has NG clear, so important to clarify that this is the
kernel value, I think.
Thanks,
Ryan
>
> - Kevin
>
>> [...]
^ permalink raw reply
* [PATCH] ASoC: sun4i-spdif: Make reset control non-optional and check for all errors
From: Chen-Yu Tsai @ 2026-03-23 17:24 UTC (permalink / raw)
To: Philipp Zabel, Mark Brown, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland
Cc: Chen-Yu Tsai, Jaroslav Kysela, Takashi Iwai, linux-sound,
linux-sunxi, linux-arm-kernel, linux-kernel
From: Chen-Yu Tsai <wens@csie.org>
The logic around the reset control is weird. The driver already has
quirks describing which compatible strings require a reset control, but
the reset control is acquired using the "optional" API. Then any errors
other than deferred probe are ignored.
Instead, switch to the non-optional variant. Also use the deassered
variant to simplify the code. This also fixes a missing reset assert
upon removal.
Last, clean up the error message printing with dev_err_probe().
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
sound/soc/sunxi/sun4i-spdif.c | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 65de03ca3ad2..c2ec19437cd7 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -712,15 +712,10 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
if (quirks->has_reset) {
- host->rst = devm_reset_control_get_optional_exclusive(&pdev->dev,
- NULL);
- if (PTR_ERR(host->rst) == -EPROBE_DEFER) {
- ret = -EPROBE_DEFER;
- dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
- return ret;
- }
- if (!IS_ERR(host->rst))
- reset_control_deassert(host->rst);
+ host->rst = devm_reset_control_get_exclusive_deasserted(&pdev->dev, NULL);
+ if (IS_ERR(host->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->rst),
+ "Failed to get reset\n");
}
ret = devm_snd_soc_register_component(&pdev->dev,
--
2.47.3
^ permalink raw reply related
* Re: [PATCH] PCI: mediatek-gen3: select PCI_PWRCTRL_GENERIC correctly
From: Bjorn Helgaas @ 2026-03-23 17:24 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: Bjorn Helgaas, Arnd Bergmann, Lorenzo Pieralisi,
Krzysztof Wilczyński, Matthias Brugger,
AngeloGioacchino Del Regno, Neil Armstrong, Bartosz Golaszewski,
Chen-Yu Tsai, Arnd Bergmann, Rob Herring, Thomas Gleixner,
Nam Cao, Marc Zyngier, linux-pci, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <wlu2o3hx6yvpzs54mxsf24gmqwwudkixx7aylv4dkey744yify@eufscwyqiyhk>
On Mon, Mar 23, 2026 at 05:26:47PM +0530, Manivannan Sadhasivam wrote:
> On Mon, Mar 23, 2026 at 11:55:06AM +0100, Arnd Bergmann wrote:
> > From: Arnd Bergmann <arnd@arndb.de>
> >
> > The PCI_PWRCTRL_SLOT option was renamed to PCI_PWRCTRL_GENERIC, but a new
> > user showed up using the old name, which caused a build failure when
> > both conflicting patches are merged:
> >
> > aarch64-linux-ld: drivers/pci/controller/pcie-mediatek-gen3.o: in function `mtk_pcie_remove':
> > pcie-mediatek-gen3.c:(.text+0x137c): undefined reference to `pci_pwrctrl_power_off_devices'
> >
> > Use the correct symbol as intended originally.
> >
> > Fixes: ff124bbbca1d ("PCI/pwrctrl: generic: Rename pci-pwrctrl-slot as generic")
> > Fixes: 073a7e1b7bcc ("PCI: mediatek-gen3: Integrate new pwrctrl API")
> > Signed-off-by: Arnd Bergmann <arnd@arndb.de>
>
> Bjorn, should I squash this fix with 073a7e1b7bcc so that pci/next will build
> properly?
If you squash this into 073a7e1b7bcc, I think the 0-day bot will
comlain about the resulting pci/controller/mediatek-gen3 branch
because it doesn't include ff124bbbca1d.
I can resolve it by hand when merging pci/controller/mediatek-gen3
into pci/next.
> > ---
> > drivers/pci/controller/Kconfig | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> > index e72ac6934379..686349e09cd3 100644
> > --- a/drivers/pci/controller/Kconfig
> > +++ b/drivers/pci/controller/Kconfig
> > @@ -222,7 +222,7 @@ config PCIE_MEDIATEK_GEN3
> > depends on ARCH_AIROHA || ARCH_MEDIATEK || COMPILE_TEST
> > depends on PCI_MSI
> > select IRQ_MSI_LIB
> > - select PCI_PWRCTRL_SLOT
> > + select PCI_PWRCTRL_GENERIC
> > help
> > Adds support for PCIe Gen3 MAC controller for MediaTek SoCs.
> > This PCIe controller is compatible with Gen3, Gen2 and Gen1 speed,
> > --
> > 2.39.5
> >
>
> --
> மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH 1/2] arm64/entry: Fix involuntary preemption exception masking
From: Mark Rutland @ 2026-03-23 17:21 UTC (permalink / raw)
To: Thomas Gleixner
Cc: vladimir.murzin, Peter Zijlstra, catalin.marinas, ruanjinjie,
linux-kernel, luto, will, linux-arm-kernel
In-Reply-To: <875x6qjyac.ffs@tglx>
On Fri, Mar 20, 2026 at 04:50:03PM +0100, Thomas Gleixner wrote:
> On Fri, Mar 20 2026 at 14:57, Mark Rutland wrote:
> > On Fri, Mar 20, 2026 at 03:11:20PM +0100, Thomas Gleixner wrote:
> >> Yes. It's not an optimization. It's a correctness issue.
> >>
> >> If the interrupted context is RCU idle then you have to carefully go
> >> back to that context. So that the context can tell RCU it is done with
> >> the idle state and RCU has to pay attention again. Otherwise all of this
> >> becomes imbalanced.
> >>
> >> This is about context-level nesting:
> >>
> >> ...
> >> L1.A ct_cpuidle_enter();
> >>
> >> -> interrupt
> >> L2.A ct_irq_enter();
> >> ... // Set NEED_RESCHED
> >> L2.B ct_irq_exit();
> >>
> >> ...
> >> L1.B ct_cpuidle_exit();
> >>
> >> Scheduling between #L2.B and #L1.B makes RCU rightfully upset.
> >
> > I suspect I'm missing something obvious here:
> >
> > * Regardless of nesting, I see that scheduling between L2.B and L1.B is
> > broken because RCU isn't watching.
> >
> > * I'm not sure whether there's a problem with scheduling between L2.A
> > and L2.B, which is what arm64 used to do, and what arm64 would do
> > after this patch.
>
> The only reason why it "works" is that the idle task has preemption
> permanently disabled, so it won't really schedule even if need_resched()
> is set. So it "works" by chance and not by design.
Ah, I see.
Thanks -- that relieves my fear that we'd have to backport a fix to
stable kernels. Since that's safe by accident, I think we can leave
stable kernels as-is.
> Apply the patch below and watch the show.
Thanks for this too; I hadn't spotted rcu_irq_exit_check_preempt().
Info dump below, but this is just agreeing with what you said above. :)
Since rcu_irq_exit_check_preempt() doesn't dump the actual values, I
hacked up something similar and tested arm64's old logic (from v6.17).
CT_NESTING_IRQ_NONIDLE would be 0x4000000000000001, so that would
be off-by-one if we were to preempt. However, as you say, preemption is
disabled, and that happens to save us.
Thanks again!
Mark.
| ------------[ cut here ]------------
| HARK: arm64_preempt_schedule_irq() called with:
| CT nesting: 0x0000000000000001
| CT NMI nesting: 0x4000000000000002
| RCU watching: yes
| preempt_count: 0x00000001
| WARNING: CPU: 0 PID: 0 at arch/arm64/kernel/entry-common.c:286 el1_interrupt+0xf8/0x100
| Modules linked in:
| CPU: 0 UID: 0 PID: 0 Comm: swapper/0 Not tainted 6.17.0-00001-gc02e86492f52-dirty #8 PREEMPT
| Hardware name: linux,dummy-virt (DT)
| pstate: 600000c9 (nZCv daIF -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
| pc : el1_interrupt+0xf8/0x100
| lr : el1_interrupt+0xf8/0x100
| sp : ffffa1efd4333be0
| x29: ffffa1efd4333be0 x28: ffffa1efd434d280 x27: ffffa1efd4342360
| x26: ffffa1efd4345000 x25: 0000000000000000 x24: ffffa1efd434d280
| x23: 0000000060000009 x22: ffffa1efd31f0154 x21: ffffa1efd4333d70
| x20: 0000000000000000 x19: ffffa1efd4333c20 x18: 000000000000000a
| x17: 72702020200a7365 x16: 79203a676e696863 x15: 7461772055435220
| x14: 2020200a32303030 x13: 3130303030303030 x12: 7830203a746e756f
| x11: 0000000000000058 x10: 0000000000000018 x9 : fff000003c7e5000
| x8 : 00000000000affa8 x7 : 0000000000000084 x6 : fff000003fc7b6c0
| x5 : fff000003fc7b6c0 x4 : 0000000000000000 x3 : 0000000000000000
| x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffffa1efd434d280
| Call trace:
| el1_interrupt+0xf8/0x100 (P)
| el1h_64_irq_handler+0x18/0x24
| el1h_64_irq+0x6c/0x70
| default_idle_call+0xb4/0x2a0 (P)
| do_idle+0x210/0x270
| cpu_startup_entry+0x34/0x40
| rest_init+0x174/0x180
| console_on_rootfs+0x0/0x6c
| __primary_switched+0x88/0x90
| irq event stamp: 848
| hardirqs last enabled at (846): [<ffffa1efd1fb0da8>] rcu_core+0xc88/0x1048
| hardirqs last disabled at (847): [<ffffa1efd1ee2444>] handle_softirqs+0x434/0x4a0
| softirqs last enabled at (848): [<ffffa1efd1ee245c>] handle_softirqs+0x44c/0x4a0
| softirqs last disabled at (841): [<ffffa1efd1e10794>] __do_softirq+0x14/0x20
| ---[ end trace 0000000000000000 ]---
^ permalink raw reply
* Re: [PATCH v1 1/3] arm64: mm: Fix rodata=full block mapping support for realm guests
From: Ryan Roberts @ 2026-03-23 17:20 UTC (permalink / raw)
To: Kevin Brodsky, Catalin Marinas, Will Deacon,
David Hildenbrand (Arm), Dev Jain, Yang Shi, Suzuki K Poulose,
Jinjiang Tu
Cc: linux-arm-kernel, linux-kernel, stable
In-Reply-To: <71261065-7895-492f-8457-998901391530@arm.com>
On 23/03/2026 16:52, Kevin Brodsky wrote:
> On 23/03/2026 14:03, Ryan Roberts wrote:
>> [...]
>>
>> diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
>> index 96711b8578fd0..b9b248d24fd10 100644
>> --- a/arch/arm64/mm/init.c
>> +++ b/arch/arm64/mm/init.c
>> @@ -350,7 +350,6 @@ void __init arch_mm_preinit(void)
>> }
>>
>> swiotlb_init(swiotlb, flags);
>> - swiotlb_update_mem_attributes();
>>
>> /*
>> * Check boundaries twice: Some fundamental inconsistencies can be
>> @@ -377,6 +376,14 @@ void __init arch_mm_preinit(void)
>> }
>> }
>>
>> +bool page_alloc_available __ro_after_init;
>> +
>> +void __init mem_init(void)
>> +{
>> + page_alloc_available = true;
>> + swiotlb_update_mem_attributes();
>
> The move seems reasonable, x86 calls this function even later (from
> arch_cpu_finalize_init()).
>
>> +}
>> +
>> void free_initmem(void)
>> {
>> void *lm_init_begin = lm_alias(__init_begin);
>> diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
>> index a6a00accf4f93..5b6a8d53e64b7 100644
>> --- a/arch/arm64/mm/mmu.c
>> +++ b/arch/arm64/mm/mmu.c
>> @@ -773,14 +773,33 @@ int split_kernel_leaf_mapping(unsigned long start, unsigned long end)
>> {
>> int ret;
>>
>> - /*
>> - * !BBML2_NOABORT systems should not be trying to change permissions on
>> - * anything that is not pte-mapped in the first place. Just return early
>> - * and let the permission change code raise a warning if not already
>> - * pte-mapped.
>> - */
>> - if (!system_supports_bbml2_noabort())
>> - return 0;
>> + if (!system_supports_bbml2_noabort()) {
>> + /*
>> + * !BBML2_NOABORT systems should not be trying to change
>> + * permissions on anything that is not pte-mapped in the first
>> + * place. Just return early and let the permission change code
>> + * raise a warning if not already pte-mapped.
>> + */
>> + if (system_capabilities_finalized() ||
>> + !cpu_supports_bbml2_noabort())
>> + return 0;
>> +
>> + /*
>> + * Boot-time: split_kernel_leaf_mapping_locked() allocates from
>> + * page allocator. Can't split until it's available.
>> + */
>> + extern bool page_alloc_available;
>
> Could we at least have the declaration in say <asm/mmu.h>? x86 defines a
> similar global so we could eventually have a generic global (defined
> before mem_init() is called).
Yeah, fair enough. I was being lazy. I'll move it to the header for v2.
>
> Looks good otherwise:
>
> Reviewed-by: Kevin Brodsky <kevin.brodsky@arm.com>
>
>> + if (WARN_ON(!page_alloc_available))
>> + return -EBUSY;
>> +
>> + /*
>> + * Boot-time: Started secondary cpus but don't know if they
>> + * support BBML2_NOABORT yet. Can't allow splitting in this
>> + * window in case they don't.
>> + */
>> + if (WARN_ON(num_online_cpus() > 1))
>> + return -EBUSY;
>> + }
>>
>> /*
>> * If the region is within a pte-mapped area, there is no need to try to
^ permalink raw reply
* [PATCH] arm64: dts: allwinner: sun55i: Fix r-spi DMA
From: Chen-Yu Tsai @ 2026-03-23 17:19 UTC (permalink / raw)
To: Chen-Yu Tsai, Jernej Skrabec, Samuel Holland
Cc: linux-sunxi, devicetree, linux-arm-kernel, linux-kernel
r-spi has DRQs for both the main and MCU DMA controllers on the A523 SoC
family, however it seems it that it is mainly routed to the MCU DMA
controller, with no obvious way to change it.
Change the DMA channels of r-spi to the MCU so that it works properly.
Fixes: 1bec3bd1f839 ("arm64: dts: allwinner: sun55i: Add SPI controllers")
Signed-off-by: Chen-Yu Tsai <wens@kernel.org>
---
arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi
index 56d4e5862ff4..34d7547a6004 100644
--- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi
@@ -1018,7 +1018,7 @@ r_spi0: spi@7092000 {
interrupts = <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&r_ccu CLK_BUS_R_SPI>, <&r_ccu CLK_R_SPI>;
clock-names = "ahb", "mod";
- dmas = <&dma 53>, <&dma 53>;
+ dmas = <&mcu_dma 13>, <&mcu_dma 13>;
dma-names = "rx", "tx";
resets = <&r_ccu RST_BUS_R_SPI>;
status = "disabled";
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v2 4/4] iommu: Get DT/ACPI parsing into the proper probe path
From: Tudor Ambarus @ 2026-03-23 17:18 UTC (permalink / raw)
To: Robin Murphy, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Rafael J. Wysocki, Len Brown, Russell King, Greg Kroah-Hartman,
Danilo Krummrich, Stuart Yoder, Laurentiu Tudor, Nipun Gupta,
Nikhil Agarwal, Joerg Roedel, Will Deacon, Rob Herring,
Bjorn Helgaas
Cc: linux-acpi, linux-arm-kernel, linux-kernel, iommu, devicetree,
linux-pci, Charan Teja Kalla, Peter Griffin, André Draszik,
Juan Yescas, kernel-team
In-Reply-To: <e3b191e6fd6ca9a1e84c5e5e40044faf97abb874.1740753261.git.robin.murphy@arm.com>
Hi, Robin,
On 2/28/25 5:46 PM, Robin Murphy wrote:
> diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
> index a3b45b84f42b..1cec7074367a 100644
> --- a/drivers/iommu/iommu.c
> +++ b/drivers/iommu/iommu.c
> @@ -414,9 +414,21 @@ static int iommu_init_device(struct device *dev)
> if (!dev_iommu_get(dev))
> return -ENOMEM;
> /*
> - * For FDT-based systems and ACPI IORT/VIOT, drivers register IOMMU
> - * instances with non-NULL fwnodes, and client devices should have been
> - * identified with a fwspec by this point. Otherwise, we can currently
> + * For FDT-based systems and ACPI IORT/VIOT, the common firmware parsing
> + * is buried in the bus dma_configure path. Properly unpicking that is
> + * still a big job, so for now just invoke the whole thing. The device
> + * already having a driver bound means dma_configure has already run and
> + * either found no IOMMU to wait for, or we're in its replay call right
> + * now, so either way there's no point calling it again.
> + */
> + if (!dev->driver && dev->bus->dma_configure) {
> + mutex_unlock(&iommu_probe_device_lock);
> + dev->bus->dma_configure(dev);
> + mutex_lock(&iommu_probe_device_lock);
> + }
I was chasing the "something fishy" dev_WARN on a 6.19+ downstream
android kernel and while looking at the IOMMU code I couldn't help
myself and ask whether we shall prevent concurrent execution of
dma_configure().
It seems to me that while the IOMMU subsystem is executing
dma_configure(), the deferred probe workqueue can concurrently pick up
the same device, enter really_probe(), set dev->driver, and execute
dma_configure(). Is it worth protecting against this?
I can try to prove it if needed, using a downstream iommu driver (sigh).
Thanks!
ta
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index e61927b4d41f..5f0c1a8064b5 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -461,9 +461,19 @@ static int iommu_init_device(struct device *dev)
* already having a driver bound means dma_configure has already run and
* found no IOMMU to wait for, so there's no point calling it again.
*/
- if (!dev->iommu->fwspec && !dev->driver && dev->bus->dma_configure) {
+ if (!dev->iommu->fwspec && !READ_ONCE(dev->driver) &&
+ dev->bus->dma_configure) {
mutex_unlock(&iommu_probe_device_lock);
- dev->bus->dma_configure(dev);
+
+ /*
+ * Serialize with really_probe(). Recheck dev->driver in case a
+ * driver bound while we were waiting for the lock.
+ */
+ device_lock(dev);
+ if (!dev->driver)
+ dev->bus->dma_configure(dev);
+ device_unlock(dev);
+
mutex_lock(&iommu_probe_device_lock);
/* If another instance finished the job for us, skip it */
if (!dev->iommu || dev->iommu_group)
(END)
^ permalink raw reply related
* Re: [PATCH 3/5] dt-bindings: pinctrl: sun55i-a523: increase IRQ bank number
From: Chen-Yu Tsai @ 2026-03-23 17:10 UTC (permalink / raw)
To: Andre Przywara
Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jernej Skrabec, Samuel Holland, Michal Piekos, linux-gpio,
devicetree, linux-arm-kernel, linux-sunxi, linux-kernel
In-Reply-To: <20260323110151.2352832-4-andre.przywara@arm.com>
On Mon, Mar 23, 2026 at 7:02 PM Andre Przywara <andre.przywara@arm.com> wrote:
>
> The Allwinner A523 SoC implements 10 GPIO banks in the first pinctrl
> instance, but it skips the first bank (PortA), so their index goes from
> 1 to 10. The same is actually true for the IRQ banks: there are registers
> for 11 banks, though the first bank is not implemented (RAZ/WI).
> In contrast to previous SoCs, the count of the IRQ banks starts with this
> first unimplemented bank, so we need to provide an interrupt for it.
> And indeed the A523 user manual lists an interrupt number for PortA, so we
> need to increase the maximum number of interrupts per pin controller to 11,
> to be able to assign the correct interrupt number for each bank.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
^ permalink raw reply
* Re: [PATCH 4/5] arm64: dts: allwinner: a523: Add missing GPIO interrupt
From: Chen-Yu Tsai @ 2026-03-23 17:08 UTC (permalink / raw)
To: Andre Przywara
Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jernej Skrabec, Samuel Holland, Michal Piekos, linux-gpio,
devicetree, linux-arm-kernel, linux-sunxi, linux-kernel
In-Reply-To: <20260323110151.2352832-5-andre.przywara@arm.com>
On Mon, Mar 23, 2026 at 7:02 PM Andre Przywara <andre.przywara@arm.com> wrote:
>
> Even though the Allwinner A523 SoC implements 10 GPIO banks, it has
> actually registers for 11 IRQ banks, and even an interrupt assigned to
> the first, non-implemented IRQ bank.
> Add that first interrupt to the list of GPIO interrupts, to correct the
> association between IRQs and GPIO banks.
>
> This fixes GPIO IRQ operation on boards with A523 SoCs, as seen by
> broken SD card detect functionality, for instance.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> Fixes: 35ac96f79664 ("arm64: dts: allwinner: Add Allwinner A523 .dtsi file")
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
^ permalink raw reply
* Re: [PATCH 2/5] pinctrl: sunxi: Remove unneeded IRQ remuxing for some SoCs
From: Chen-Yu Tsai @ 2026-03-23 17:07 UTC (permalink / raw)
To: Andre Przywara
Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jernej Skrabec, Samuel Holland, Michal Piekos, linux-gpio,
devicetree, linux-arm-kernel, linux-sunxi, linux-kernel
In-Reply-To: <20260323110151.2352832-3-andre.przywara@arm.com>
On Mon, Mar 23, 2026 at 7:02 PM Andre Przywara <andre.przywara@arm.com> wrote:
>
> The Allwinner A10 and H3 SoCs cannot read the state of a GPIO line when
> that line is muxed for IRQ triggering (muxval 6), but only if it's
> explicitly muxed for GPIO input (muxval 0). Other SoCs do not show this
> behaviour, so we added a optional workaround, triggered by a quirk bit,
> which triggers remuxing the pin when it's configured for IRQ, while we
> need to read its value.
>
> For some reasons this quirk flag was copied over to newer SoCs, even
> though they don't show this behaviour, and the GPIO data register
> reflects the true GPIO state even with a pin configured to muxval 6
> (IRQ). The workaround is just more costly, but doesn't break otherwise,
> so this was probably never noticed by anyone.
> Experiments confirm that the H5, H6, H616 and A523 do not need this
> workaround, they show the GPIO line value with both muxval 0 and 6.
>
> Remove the unneeded quirk from those SoC's pinctrl driver description.
> This should have no obvious effect on the H5, H6, H616 (other than
> being more efficient), but the workaround is broken for the A523, so
> it fixes (one part of the) interrupt operation there.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
> Fixes: b8a51e95b376 ("pinctrl: sunxi: Add support for the secondary A523 GPIO ports")
Acked-by: Chen-Yu Tsai <wens@kernel.org>
^ permalink raw reply
* Re: [PATCH 1/5] pinctrl: sunxi: Rework IRQ remuxing to avoid fixed mux value
From: Chen-Yu Tsai @ 2026-03-23 17:04 UTC (permalink / raw)
To: Andre Przywara
Cc: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Jernej Skrabec, Samuel Holland, Michal Piekos, linux-gpio,
devicetree, linux-arm-kernel, linux-sunxi, linux-kernel
In-Reply-To: <20260323110151.2352832-2-andre.przywara@arm.com>
On Mon, Mar 23, 2026 at 7:02 PM Andre Przywara <andre.przywara@arm.com> wrote:
>
> Some Allwinner SoCs cannot read the state of a GPIO line when the pin is
> muxed to the IRQ function. To access that state anyway, we temporarily
> mux that pin back to GPIO input, then return it to the IRQ mux
> afterwards. This code assumes that the IRQ mux value is 0x6, even though
> newer SoCs (D1/T113/A523/...) encode the IRQ mux with 0xe.
>
> Avoid hardcoding the different IRQ mux values by saving the programmed
> value before switching to GPIO input, then restoring the saved value
> afterwards. This makes the code robust against future changes of the IRQ
> mux value. This also avoids calling the sunxi_pmx_set() function twice,
> each of which does a read/modify/write operation, fenced in by the pctl
> lock. The new code takes the lock around the whole operation, which is
> also safer since it avoids (probably theoretical) races against other
> code touching the mux register meanwhile.
>
> Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Reviewed-by: Chen-Yu Tsai <wens@kernel.org>
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: watchdog: Convert TS-4800 to DT schema
From: Frank Li @ 2026-03-23 16:59 UTC (permalink / raw)
To: Eduard Bostina
Cc: daniel.baluta, simona.toaca, d-gole, m-chawdhry, Wim Van Sebroeck,
Guenter Roeck, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Mark Brown,
linux-watchdog, devicetree, linux-kernel, imx, linux-arm-kernel
In-Reply-To: <20260323084616.10469-2-egbostina@gmail.com>
On Mon, Mar 23, 2026 at 10:46:12AM +0200, Eduard Bostina wrote:
> Convert the Technologic Systems TS-4800 watchdog timer bindings
> to DT schema.
>
> Signed-off-by: Eduard Bostina <egbostina@gmail.com>
> ---
> Note:
> * This patch is part of the GSoC2026 application process for device tree bindings conversions
> * https://github.com/LinuxFoundationGSoC/ProjectIdeas/wiki/GSoC-2026-Device-Tree-Bindings
>
> .../watchdog/technologic,ts4800-wdt.yaml | 46 +++++++++++++++++++
> .../bindings/watchdog/ts4800-wdt.txt | 25 ----------
> 2 files changed, 46 insertions(+), 25 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml
> delete mode 100644 Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt
>
> diff --git a/Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml b/Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml
> new file mode 100644
> index 000000000..cb2066b4b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/watchdog/technologic,ts4800-wdt.yaml
> @@ -0,0 +1,46 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/watchdog/technologic,ts4800-wdt.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Technologic Systems TS-4800 Watchdog
> +
> +maintainers:
> + - Eduard Bostina <egbostina@gmail.com>
> +
> +allOf:
> + - $ref: watchdog.yaml#
Move allOf after required incase add if-else branch later.
> +
> +properties:
> + compatible:
> + const: technologic,ts4800-wdt
> +
> + syscon:
> + $ref: /schemas/types.yaml#/definitions/phandle-array
> + items:
> + - items:
> + - description: Phandle to the FPGA's syscon
> + - description: Offset to the watchdog register
> + description: Phandle / integers array that points to the syscon node which
> + describes the FPGA's syscon registers.
> +
> +required:
> + - compatible
> + - syscon
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + syscon: syscon@b0010000 {
> + compatible = "syscon", "simple-mfd";
> + reg = <0xb0010000 0x3d>;
> + reg-io-width = <2>;
> +
Needn't this part. just keep below watchdog node should be enough.
Frank
> + watchdog {
> + compatible = "technologic,ts4800-wdt";
> + syscon = <&syscon 0xe>;
> + timeout-sec = <10>;
> + };
> + };
> diff --git a/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt b/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt
> deleted file mode 100644
> index 8f6caad42..000000000
> --- a/Documentation/devicetree/bindings/watchdog/ts4800-wdt.txt
> +++ /dev/null
> @@ -1,25 +0,0 @@
> -Technologic Systems Watchdog
> -
> -Required properties:
> -- compatible: must be "technologic,ts4800-wdt"
> -- syscon: phandle / integer array that points to the syscon node which
> - describes the FPGA's syscon registers.
> - - phandle to FPGA's syscon
> - - offset to the watchdog register
> -
> -Optional property:
> -- timeout-sec: contains the watchdog timeout in seconds.
> -
> -Example:
> -
> -syscon: syscon@b0010000 {
> - compatible = "syscon", "simple-mfd";
> - reg = <0xb0010000 0x3d>;
> - reg-io-width = <2>;
> -
> - wdt@e {
> - compatible = "technologic,ts4800-wdt";
> - syscon = <&syscon 0xe>;
> - timeout-sec = <10>;
> - };
> -}
> --
> 2.53.0
>
^ permalink raw reply
* [PATCH 2/3] firmware: arm_scmi: Fix bound iterators returning too many items
From: Geert Uytterhoeven @ 2026-03-23 16:56 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Marek Vasut
Cc: arm-scmi, linux-arm-kernel, linux-renesas-soc, linux-kernel,
Geert Uytterhoeven
In-Reply-To: <cover.1774283748.git.geert+renesas@glider.be>
When using a bound-iterator with an upper bound, commands are sent, and
responses are received, until the upper bound is reached. However, it
is up to the SCMI provider implementation to decide how many rates are
returned in response to a single CLOCK_DESCRIBE_RATES command. If the
last response contains rates beyond the specified upper bound, they are
still passed up for further processing. This may lead to buffer
overflows in unprepared callsites.
While the imprecise bound handling may have been intentional (it was
mentioned in the commit message introducing the code), it is still
confusing for users, and may cause hard to debug crashes. Fix this by
strictly enforcing the upper bound.
Note that this may cause an increase in the number of
CLOCK_DESCRIBE_RATES commands issued, as retrieving the last rate may no
longer be done inadvertentently, but require its own command.
Fixes: 13289addf5a52e1f ("firmware: arm_scmi: Add bound iterators support")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
This caused random "kernel BUG at drivers/base/devres.c:135!" crashes
during boot on R-Car X5H, as lazy clock rate discovery does not handle
correctly receiving more rates than expected.
Example for a clock with 8 rates, which are all returned in response to
a single CLOCK_DESCRIBE_RATES command:
scmi_clock_describe_rates_get_lazy: Grabbing rates 0..2
iter_clk_describe_update_state: Returned 8 remaining 0
iter_clk_describe_update_state: Allocating 8 rates
iter_clk_describe_process_response: rates[0] = 33333333
iter_clk_describe_process_response: rates[1] = 66666666
iter_clk_describe_process_response: rates[2] = 133333333
iter_clk_describe_process_response: rates[3] = 266666666
iter_clk_describe_process_response: rates[4] = 355555555
iter_clk_describe_process_response: rates[5] = 533333333
iter_clk_describe_process_response: rates[6] = 711111111
iter_clk_describe_process_response: rates[7] = 1066666666
^^^^^^^^^^
Rates [3] to [7] are received, despite being outside the bound.
scmi_clock_describe_rates_get_lazy: Grabbing rates 7..7
iter_clk_describe_update_state: Returned 1 remaining 0
iter_clk_describe_process_response: rates[8] = 1066666666
^ ^^^^^^^^^^
Out of bounds access! ------------------------+ |
Same value as [7] ---------------------------------+
---
drivers/firmware/arm_scmi/driver.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 2a9183686203b4e7..03fd7caa8b42a12c 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1820,6 +1820,7 @@ static int __scmi_iterator_run(void *iter, unsigned int *start, unsigned int *en
const struct scmi_protocol_handle *ph;
struct scmi_iterator_state *st;
struct scmi_iterator *i;
+ unsigned int n;
if (!iter)
return -EINVAL;
@@ -1852,13 +1853,17 @@ static int __scmi_iterator_run(void *iter, unsigned int *start, unsigned int *en
return -EINVAL;
}
- for (st->loop_idx = 0; st->loop_idx < st->num_returned; st->loop_idx++) {
+ if (end)
+ n = min(st->num_returned, *end - st->desc_index + 1);
+ else
+ n = st->num_returned;
+ for (st->loop_idx = 0; st->loop_idx < n; st->loop_idx++) {
ret = iops->process_response(ph, i->resp, st, i->priv);
if (ret)
return ret;
}
- st->desc_index += st->num_returned;
+ st->desc_index += n;
ph->xops->reset_rx_to_maxsz(ph, i->t);
/*
* check for both returned and remaining to avoid infinite
--
2.43.0
^ permalink raw reply related
* [PATCH 3/3] firmware: arm_scmi: Use proper iter_response_bound_cleanup() name
From: Geert Uytterhoeven @ 2026-03-23 16:56 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Marek Vasut
Cc: arm-scmi, linux-arm-kernel, linux-renesas-soc, linux-kernel,
Geert Uytterhoeven
In-Reply-To: <cover.1774283748.git.geert+renesas@glider.be>
The documentation speaks of the "iter_response_bound_cleanup()" protocol
helper, while the actual helper is called "iter_response_cleanup()".
Settle on the former name, because the helper is only needed when using
bound-iterators.
Fixes: 13289addf5a52e1f ("firmware: arm_scmi: Add bound iterators support")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
drivers/firmware/arm_scmi/clock.c | 2 +-
drivers/firmware/arm_scmi/driver.c | 6 +++---
drivers/firmware/arm_scmi/protocols.h | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 623dbc2f1e09303d..0205b37d219a88ed 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -604,7 +604,7 @@ scmi_clock_describe_rates_get_lazy(const struct scmi_protocol_handle *ph,
}
out:
- ph->hops->iter_response_cleanup(iter);
+ ph->hops->iter_response_bound_cleanup(iter);
return ret;
}
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index 03fd7caa8b42a12c..bfcc3e3c931dc9cc 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -1875,7 +1875,7 @@ static int __scmi_iterator_run(void *iter, unsigned int *start, unsigned int *en
return 0;
}
-static void scmi_iterator_cleanup(void *iter)
+static void scmi_iterator_bound_cleanup(void *iter)
{
struct scmi_iterator *i = iter;
@@ -1888,7 +1888,7 @@ static int scmi_iterator_run(void *iter)
int ret;
ret = __scmi_iterator_run(iter, NULL, NULL);
- scmi_iterator_cleanup(iter);
+ scmi_iterator_bound_cleanup(iter);
return ret;
}
@@ -2078,7 +2078,7 @@ static const struct scmi_proto_helpers_ops helpers_ops = {
.iter_response_init = scmi_iterator_init,
.iter_response_run = scmi_iterator_run,
.iter_response_run_bound = scmi_iterator_run_bound,
- .iter_response_cleanup = scmi_iterator_cleanup,
+ .iter_response_bound_cleanup = scmi_iterator_bound_cleanup,
.protocol_msg_check = scmi_protocol_msg_check,
.fastchannel_init = scmi_common_fastchannel_init,
.fastchannel_db_ring = scmi_common_fastchannel_db_ring,
diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h
index e2ef604c16ef6771..15ad5162e37a90ec 100644
--- a/drivers/firmware/arm_scmi/protocols.h
+++ b/drivers/firmware/arm_scmi/protocols.h
@@ -286,7 +286,7 @@ struct scmi_proto_helpers_ops {
int (*iter_response_run)(void *iter);
int (*iter_response_run_bound)(void *iter,
unsigned int *start, unsigned int *end);
- void (*iter_response_cleanup)(void *iter);
+ void (*iter_response_bound_cleanup)(void *iter);
int (*protocol_msg_check)(const struct scmi_protocol_handle *ph,
u32 message_id, u32 *attributes);
void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
--
2.43.0
^ permalink raw reply related
* [PATCH 1/3] firmware: arm_scmi: Fix OOB in scmi_clock_describe_rates_get_lazy()
From: Geert Uytterhoeven @ 2026-03-23 16:56 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Marek Vasut
Cc: arm-scmi, linux-arm-kernel, linux-renesas-soc, linux-kernel,
Geert Uytterhoeven
In-Reply-To: <cover.1774283748.git.geert+renesas@glider.be>
Lazy discovery of discrete rates works as follows:
A. Grab the first three rates,
B. Grab the last rate, if there are more than three rates.
It is up to the SCMI provider implementation to decide how many rates
are returned in response to a single CLOCK_DESCRIBE_RATES command. Each
rate received is stored in the scmi_clock_rates.rates[] array, and
.num_rates is updated accordingly.
When more than 3 rates have been received after step A, the last rate
may have been received already, and stored in scmi_clock_rates.rates[]
(which has space for scmi_clock_desc.tot_rates entries). Hence grabbing
the last rate again will store it a second time, beyond the end of the
array.
Fix this by only grabbing the last rate when we don't already have it.
Fixes: a78da552c6f3bff5 ("firmware: arm_scmi: Use bound iterators to minimize discovered rates")
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
This bug caused random "kernel BUG at drivers/base/devres.c:135!"
crashes during boot on R-Car X5H.
Example for a clock with 8 rates, which are all returned in response to
a single CLOCK_DESCRIBE_RATES command:
scmi_clock_describe_rates_get_lazy: Grabbing rates 0..2
iter_clk_describe_update_state: Returned 8 remaining 0
iter_clk_describe_update_state: Allocating 8 rates
iter_clk_describe_process_response: rates[0] = 33333333
iter_clk_describe_process_response: rates[1] = 66666666
iter_clk_describe_process_response: rates[2] = 133333333
iter_clk_describe_process_response: rates[3] = 266666666
iter_clk_describe_process_response: rates[4] = 355555555
iter_clk_describe_process_response: rates[5] = 533333333
iter_clk_describe_process_response: rates[6] = 711111111
iter_clk_describe_process_response: rates[7] = 1066666666
^^^^^^^^^^
scmi_clock_describe_rates_get_lazy: Grabbing rates 7..7
iter_clk_describe_update_state: Returned 1 remaining 0
iter_clk_describe_process_response: rates[8] = 1066666666
^ ^^^^^^^^^^
Out of bounds access! ------------------------+ |
Same value as [7] ---------------------------------+
drivers/firmware/arm_scmi/clock.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c
index 0e7e341171aad829..623dbc2f1e09303d 100644
--- a/drivers/firmware/arm_scmi/clock.c
+++ b/drivers/firmware/arm_scmi/clock.c
@@ -593,8 +593,11 @@ scmi_clock_describe_rates_get_lazy(const struct scmi_protocol_handle *ph,
if (ret)
goto out;
- /* If discrete grab the last value, which should be the max */
- if (clkd->r.rate_discrete && clkd->tot_rates > 3) {
+ /*
+ * If discrete and we don't already have it, grab the last value, which
+ * should be the max
+ */
+ if (clkd->r.rate_discrete && clkd->tot_rates > clkd->r.num_rates) {
first = clkd->tot_rates - 1;
last = clkd->tot_rates - 1;
ret = ph->hops->iter_response_run_bound(iter, &first, &last);
--
2.43.0
^ permalink raw reply related
* [PATCH 0/3] firmware: arm_scmi: Lazy clock rates and bound iterator fixes
From: Geert Uytterhoeven @ 2026-03-23 16:56 UTC (permalink / raw)
To: Sudeep Holla, Cristian Marussi, Marek Vasut
Cc: arm-scmi, linux-arm-kernel, linux-renesas-soc, linux-kernel,
Geert Uytterhoeven
Hi all,
This patch series:
- Fixes an out-of-bound access in lazy clock rate handling,
- Synchronizes bound-iterator cleanup naming between documentation and
code.
Note that the first two patches fix the same symptom; either or both can
be applied.
Thanks for your comments!
Geert Uytterhoeven (3):
firmware: arm_scmi: Fix OOB in scmi_clock_describe_rates_get_lazy()
firmware: arm_scmi: Fix bound iterators returning too many items
firmware: arm_scmi: Use proper iter_response_bound_cleanup() name
drivers/firmware/arm_scmi/clock.c | 9 ++++++---
drivers/firmware/arm_scmi/driver.c | 15 ++++++++++-----
drivers/firmware/arm_scmi/protocols.h | 2 +-
3 files changed, 17 insertions(+), 9 deletions(-)
--
2.43.0
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox