* Re: [PATCH v2 2/3] pmdomain: core: add support for power-domains-child-ids
From: Kevin Hilman @ 2026-04-20 22:13 UTC (permalink / raw)
To: Ulf Hansson
Cc: Rob Herring, Geert Uytterhoeven, linux-pm, devicetree,
linux-kernel, arm-scmi, linux-arm-kernel
In-Reply-To: <CAPDyKFrbk8SZs+-WMkbKnh12+OTBtoZ2bwtuASGvAsZwJEd4vQ@mail.gmail.com>
Ulf Hansson <ulf.hansson@linaro.org> writes:
> On Sat, 11 Apr 2026 at 01:44, Kevin Hilman (TI) <khilman@baylibre.com> wrote:
>>
>> Currently, PM domains can only support hierarchy for simple
>> providers (e.g. ones with #power-domain-cells = 0).
>>
>> Add support for oncell providers as well by adding a new property
>> `power-domains-child-ids` to describe the parent/child relationship.
>>
>> For example, an SCMI PM domain provider has multiple domains, each of
>> which might be a child of diffeent parent domains. In this example,
>> the parent domains are MAIN_PD and WKUP_PD:
>>
>> scmi_pds: protocol@11 {
>> reg = <0x11>;
>> #power-domain-cells = <1>;
>> power-domains = <&MAIN_PD>, <&WKUP_PD>;
>> power-domains-child-ids = <15>, <19>;
>> };
>>
>> With this example using the new property, SCMI PM domain 15 becomes a
>> child domain of MAIN_PD, and SCMI domain 19 becomes a child domain of
>> WKUP_PD.
>>
>> To support this feature, add two new core functions
>>
>> - of_genpd_add_child_ids()
>> - of_genpd_remove_child_ids()
>>
>> which can be called by pmdomain providers to add/remove child domains
>> if they support the new property power-domains-child-ids.
>>
>> The add function is "all or nothing". If it cannot add all of the
>> child domains in the list, it will unwind any additions already made
>> and report a failure.
>>
>> Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
>> ---
>> drivers/pmdomain/core.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/pm_domain.h | 16 ++++++++++++++++
>> 2 files changed, 182 insertions(+)
>>
>> diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
>> index 61c2277c9ce3..f978477dd546 100644
>> --- a/drivers/pmdomain/core.c
>> +++ b/drivers/pmdomain/core.c
>> @@ -2909,6 +2909,172 @@ static struct generic_pm_domain *genpd_get_from_provider(
>> return genpd;
>> }
>>
>> +/**
>> + * of_genpd_add_child_ids() - Parse power-domains-child-ids property
>> + * @np: Device node pointer associated with the PM domain provider.
>> + * @data: Pointer to the onecell data associated with the PM domain provider.
>> + *
>> + * Parse the power-domains and power-domains-child-ids properties to establish
>> + * parent-child relationships for PM domains. The power-domains property lists
>> + * parent domains, and power-domains-child-ids lists which child domain IDs
>> + * should be associated with each parent.
>> + *
>> + * Uses "all or nothing" semantics: either all relationships are established
>> + * successfully, or none are (any partially-added relationships are unwound
>> + * on error).
>> + *
>> + * Returns 0 on success, -ENOENT if properties don't exist, or negative error code.
>> + */
>
> As I mentioned in my earlier reply for the previous version, returning
> a specific error code when the property doesn't exist will complicate
> handling for the caller. Moreover, we also need to make sure we don't
> returning the same error code (-ENOENT) for a different error further
> down the execution path in of_genpd_add_child_ids(). Otherwise it
> would the caller treat the error code in the wrong way.
>
> To me, there are two better ways to address this. For both options,
> of_genpd_add_child_ids() should return 0 when
> "power-domains-child-ids" is missing.
>
> 1) Add another helper function that checks if
> "power-domains-child-ids" exists. The caller can then use this to
> pre-parse the property and decide whether to treat it as an error.
>
> 2) As I suggested earlier, let of_genpd_add_child_ids() return the
> number of assigned parents/children, while still using the all or
> nothing approach, of course.
OK, I like (2) better. I'll respin with that approach.
Kevin
^ permalink raw reply
* Re: [PATCH v11 12/14] cpuidle/poll_state: Wait for need-resched via tif_need_resched_relaxed_wait()
From: Christoph Lameter (Ampere) @ 2026-04-20 22:12 UTC (permalink / raw)
To: Okanovic, Haris
Cc: ankur.a.arora@oracle.com, joao.m.martins@oracle.com,
xueshuai@linux.alibaba.com, david.laight.linux@gmail.com,
boris.ostrovsky@oracle.com, memxor@gmail.com, ashok.bhat@arm.com,
zhenglifeng1@huawei.com, konrad.wilk@oracle.com,
akpm@linux-foundation.org, linux-kernel@vger.kernel.org,
catalin.marinas@arm.com, ast@kernel.org, rdunlap@infradead.org,
daniel.lezcano@linaro.org, arnd@arndb.de,
linux-arch@vger.kernel.org, will@kernel.org, mark.rutland@arm.com,
peterz@infradead.org, bpf@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, rafael@kernel.org,
linux-pm@vger.kernel.org
In-Reply-To: <a374b23f8b03f850a874d46bc78411fb99483ca2.camel@amazon.com>
On Mon, 20 Apr 2026, Okanovic, Haris wrote:
> I hope this series can merge soon. It's been stuck in review for more
> than 2 years.
3 years I believe
Lets move this forward.
^ permalink raw reply
* [PATCH] phy: apple: atc: Fix typec switch/mux leak and UAF on unbind
From: David Carlier @ 2026-04-20 23:10 UTC (permalink / raw)
To: Sven Peter, Janne Grunau, Neal Gompa, Vinod Koul, Neil Armstrong,
Hector Martin, Philipp Zabel
Cc: David Carlier, asahi, linux-arm-kernel, linux-phy, linux-kernel
atcphy_probe_switch() and atcphy_probe_mux() discard the pointers
returned by typec_switch_register() and typec_mux_register(). The
platform driver has no .remove callback, so the registered switch
and mux devices outlive the apple_atcphy struct, which is released
by devm cleanup on driver unbind.
A subsequent typec event (cable orientation change, alt-mode
transition) then invokes the registered atcphy_sw_set() or
atcphy_mux_set() callback, which retrieves the freed apple_atcphy
and dereferences it. Unbind followed by a cable replug or alt-mode
change is enough to trigger the use-after-free.
Save the registered switch and mux and unregister them through
devm_add_action_or_reset() so the framework references disappear in
step with the driver's devm-allocated state. Drop the unused struct
apple_atcphy::sw and ::mux fields, which were declared with the
wrong consumer-side types and never assigned.
Fixes: 8e98ca1e74db ("phy: apple: Add Apple Type-C PHY")
Signed-off-by: David Carlier <devnexen@gmail.com>
---
drivers/phy/apple/atc.c | 27 ++++++++++++++++++++++-----
1 file changed, 22 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c
index e9d106f135c5..4156fabad742 100644
--- a/drivers/phy/apple/atc.c
+++ b/drivers/phy/apple/atc.c
@@ -628,9 +628,6 @@ struct apple_atcphy {
struct reset_controller_dev rcdev;
- struct typec_switch *sw;
- struct typec_mux *mux;
-
struct mutex lock;
};
@@ -2066,15 +2063,25 @@ static int atcphy_sw_set(struct typec_switch_dev *sw, enum typec_orientation ori
return 0;
}
+static void atcphy_typec_switch_unregister(void *data)
+{
+ typec_switch_unregister(data);
+}
+
static int atcphy_probe_switch(struct apple_atcphy *atcphy)
{
+ struct typec_switch_dev *sw;
struct typec_switch_desc sw_desc = {
.drvdata = atcphy,
.fwnode = atcphy->dev->fwnode,
.set = atcphy_sw_set,
};
- return PTR_ERR_OR_ZERO(typec_switch_register(atcphy->dev, &sw_desc));
+ sw = typec_switch_register(atcphy->dev, &sw_desc);
+ if (IS_ERR(sw))
+ return PTR_ERR(sw);
+
+ return devm_add_action_or_reset(atcphy->dev, atcphy_typec_switch_unregister, sw);
}
static int atcphy_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
@@ -2146,15 +2153,25 @@ static int atcphy_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *sta
return atcphy_configure(atcphy, target_mode);
}
+static void atcphy_typec_mux_unregister(void *data)
+{
+ typec_mux_unregister(data);
+}
+
static int atcphy_probe_mux(struct apple_atcphy *atcphy)
{
+ struct typec_mux_dev *mux;
struct typec_mux_desc mux_desc = {
.drvdata = atcphy,
.fwnode = atcphy->dev->fwnode,
.set = atcphy_mux_set,
};
- return PTR_ERR_OR_ZERO(typec_mux_register(atcphy->dev, &mux_desc));
+ mux = typec_mux_register(atcphy->dev, &mux_desc);
+ if (IS_ERR(mux))
+ return PTR_ERR(mux);
+
+ return devm_add_action_or_reset(atcphy->dev, atcphy_typec_mux_unregister, mux);
}
static int atcphy_load_tunables(struct apple_atcphy *atcphy)
--
2.53.0
^ permalink raw reply related
* [PATCH v3 0/6] drm: handle IRQ_HPD events correctly
From: Dmitry Baryshkov @ 2026-04-20 23:33 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Heikki Krogerus, Greg Kroah-Hartman, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Adrien Grassein, Jani Nikula, Rodrigo Vivi,
Joonas Lahtinen, Tvrtko Ursulin, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, Tomi Valkeinen,
Bjorn Andersson, Konrad Dybcio, Pengyu Luo, Nikita Travkin,
Yongxing Mou
Cc: dri-devel, linux-kernel, linux-usb, intel-gfx, intel-xe,
linux-amlogic, linux-arm-kernel, linux-arm-msm, freedreno
Both DisplayPort and HDMI standards define a way for the Sink / display
to notify the Source / host about some kinds of events. In case of HDMI
it's as simple as singnalling changes to the EDID. In case of
DisplayPort it's more complicated and requires actual checking of the
DPCD registers.
Currently USB-C drivers don't have a way to deliver the IRQ_HPD
notifications, leading to missing MST notifications. Provide necessary
plumbing to let IRQ_HPD events be passed to the DisplayPort drivers.
Note: the Yoga C630 UCSI driver and Acer Aspire1 EC driver are not yet
enabled to send the IRQ_HPD events. Both of them would need some more
reverse engineering to find out how the event is being reported by the
EC.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
Changes in v3:
- Fixed build error if aux bridges are disabled (Intel GFX CI)
- Link to v2: https://patch.msgid.link/20260420-hpd-irq-events-v2-0-402ffe27e9e9@oss.qualcomm.com
Changes in v2:
- Change irq_hpd arg to be an enum, possibly desribing other uses (Toni)
- Account for that, chaning the API accordingly (with_irq -> extra,
etc.)
- Wire up AUX bridge notifications
- Link to v1: https://patch.msgid.link/20260416-hpd-irq-events-v1-0-1ab1f1cfb2b2@oss.qualcomm.com
---
Dmitry Baryshkov (6):
drm/connector: report IRQ_HPD events to drm_connector_oob_hotplug_event()
drm/bridge: pass down IRQ_HPD to the drivers
drm/bridge: aux-hpd: let drivers pass IRQ_HPD events
drm/msm: dp: handle the IRQ_HPD events reported by USB-C
soc: qcom: pmic-glink-altmode: pass down HPD_IRQ events
usb: typec: ucsi: huawei-gaokun: pass down HPD_IRQ events
drivers/gpu/drm/bridge/aux-hpd-bridge.c | 11 +++++----
drivers/gpu/drm/bridge/chrontel-ch7033.c | 3 ++-
drivers/gpu/drm/bridge/lontium-lt8912b.c | 3 ++-
drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 3 ++-
drivers/gpu/drm/bridge/ti-tfp410.c | 4 ++--
drivers/gpu/drm/display/drm_bridge_connector.c | 22 ++++++++++--------
drivers/gpu/drm/drm_bridge.c | 20 ++++++++++-------
drivers/gpu/drm/drm_connector.c | 7 ++++--
drivers/gpu/drm/i915/display/intel_dp.c | 3 ++-
drivers/gpu/drm/meson/meson_encoder_hdmi.c | 3 ++-
drivers/gpu/drm/msm/dp/dp_display.c | 6 ++++-
drivers/gpu/drm/msm/dp/dp_drm.h | 3 ++-
drivers/gpu/drm/omapdrm/dss/hdmi4.c | 3 ++-
drivers/soc/qcom/pmic_glink_altmode.c | 6 ++++-
drivers/usb/typec/altmodes/displayport.c | 15 +++++++++----
drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 11 +++++----
include/drm/bridge/aux-bridge.h | 13 +++++++++--
include/drm/drm_bridge.h | 31 +++++++++++++++++++++-----
include/drm/drm_connector.h | 22 ++++++++++++++++--
19 files changed, 138 insertions(+), 51 deletions(-)
---
base-commit: c7275b05bc428c7373d97aa2da02d3a7fa6b9f66
change-id: 20260414-hpd-irq-events-e72bc076a5f1
Best regards,
--
With best wishes
Dmitry
^ permalink raw reply
* [PATCH v3 1/6] drm/connector: report IRQ_HPD events to drm_connector_oob_hotplug_event()
From: Dmitry Baryshkov @ 2026-04-20 23:33 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Heikki Krogerus, Greg Kroah-Hartman, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Adrien Grassein, Jani Nikula, Rodrigo Vivi,
Joonas Lahtinen, Tvrtko Ursulin, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, Tomi Valkeinen,
Bjorn Andersson, Konrad Dybcio, Pengyu Luo, Nikita Travkin,
Yongxing Mou
Cc: dri-devel, linux-kernel, linux-usb, intel-gfx, intel-xe,
linux-amlogic, linux-arm-kernel, linux-arm-msm, freedreno
In-Reply-To: <20260421-hpd-irq-events-v3-0-44d2bf40dfc2@oss.qualcomm.com>
The DisplayPort standard defines a special kind of events called IRQ.
These events are used to notify DP Source about the events on the Sink
side. It is extremely important for DP MST handling, where the MST
events are reported through this IRQ.
In case of the USB-C DP AltMode there is no actual HPD pulse, but the
events are ported through the bits in the AltMode VDOs.
Extend the drm_connector_oob_hotplug_event() interface and report IRQ
events to the DisplayPort Sink drivers.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/drm_connector.c | 5 ++++-
drivers/usb/typec/altmodes/displayport.c | 15 +++++++++++----
include/drm/drm_connector.h | 19 ++++++++++++++++++-
3 files changed, 33 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 47dc53c4a738..edee9daccd51 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -3510,6 +3510,8 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
* drm_connector_oob_hotplug_event - Report out-of-band hotplug event to connector
* @connector_fwnode: fwnode_handle to report the event on
* @status: hot plug detect logical state
+ * @extra_status: additional information provided by the sink without changing
+ * the HPD state (or in addition to such a change).
*
* On some hardware a hotplug event notification may come from outside the display
* driver / device. An example of this is some USB Type-C setups where the hardware
@@ -3520,7 +3522,8 @@ struct drm_connector *drm_connector_find_by_fwnode(struct fwnode_handle *fwnode)
* a drm_connector reference through calling drm_connector_find_by_fwnode().
*/
void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct drm_connector *connector;
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 35d9c3086990..7182a8e2e710 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -189,7 +189,9 @@ static int dp_altmode_status_update(struct dp_altmode *dp)
} else {
drm_connector_oob_hotplug_event(dp->connector_fwnode,
hpd ? connector_status_connected :
- connector_status_disconnected);
+ connector_status_disconnected,
+ (hpd && irq_hpd) ? DRM_CONNECTOR_DP_IRQ_HPD :
+ DRM_CONNECTOR_NO_EXTRA_STATUS);
dp->hpd = hpd;
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
if (hpd && irq_hpd) {
@@ -212,7 +214,10 @@ static int dp_altmode_configured(struct dp_altmode *dp)
*/
if (dp->pending_hpd) {
drm_connector_oob_hotplug_event(dp->connector_fwnode,
- connector_status_connected);
+ connector_status_connected,
+ dp->pending_irq_hpd ?
+ DRM_CONNECTOR_DP_IRQ_HPD :
+ DRM_CONNECTOR_NO_EXTRA_STATUS);
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
dp->pending_hpd = false;
if (dp->pending_irq_hpd) {
@@ -397,7 +402,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
dp->data.conf = 0;
if (dp->hpd) {
drm_connector_oob_hotplug_event(dp->connector_fwnode,
- connector_status_disconnected);
+ connector_status_disconnected,
+ DRM_CONNECTOR_NO_EXTRA_STATUS);
dp->hpd = false;
sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd");
}
@@ -827,7 +833,8 @@ void dp_altmode_remove(struct typec_altmode *alt)
if (dp->connector_fwnode) {
drm_connector_oob_hotplug_event(dp->connector_fwnode,
- connector_status_disconnected);
+ connector_status_disconnected,
+ DRM_CONNECTOR_NO_EXTRA_STATUS);
fwnode_handle_put(dp->connector_fwnode);
}
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index f83f28cae207..e05197e970d3 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -91,6 +91,22 @@ enum drm_connector_status {
connector_status_unknown = 3,
};
+/**
+ * enum drm_connector_status_extra - additional events sent by the sink /
+ * display together or in replacement of the HPD status changes.
+ */
+enum drm_connector_status_extra {
+ /**
+ * @DRM_CONNECTOR_NO_EXTRA_STATUS: No additional status reported.
+ */
+ DRM_CONNECTOR_NO_EXTRA_STATUS,
+ /**
+ * @DRM_CONNECTOR_DP_IRQ_HPD: DisplayPort Sink has sent the
+ * IRQ_HPD (either by the HPD short pulse or via the AltMode event).
+ */
+ DRM_CONNECTOR_DP_IRQ_HPD,
+};
+
/**
* enum drm_connector_registration_state - userspace registration status for
* a &drm_connector
@@ -2521,7 +2537,8 @@ drm_connector_is_unregistered(struct drm_connector *connector)
}
void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
- enum drm_connector_status status);
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status);
const char *drm_get_connector_type_name(unsigned int connector_type);
const char *drm_get_connector_status_name(enum drm_connector_status status);
const char *drm_get_subpixel_order_name(enum subpixel_order order);
--
2.47.3
^ permalink raw reply related
* [PATCH v3 2/6] drm/bridge: pass down IRQ_HPD to the drivers
From: Dmitry Baryshkov @ 2026-04-20 23:33 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Heikki Krogerus, Greg Kroah-Hartman, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Adrien Grassein, Jani Nikula, Rodrigo Vivi,
Joonas Lahtinen, Tvrtko Ursulin, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, Tomi Valkeinen,
Bjorn Andersson, Konrad Dybcio, Pengyu Luo, Nikita Travkin,
Yongxing Mou
Cc: dri-devel, linux-kernel, linux-usb, intel-gfx, intel-xe,
linux-amlogic, linux-arm-kernel, linux-arm-msm, freedreno
In-Reply-To: <20260421-hpd-irq-events-v3-0-44d2bf40dfc2@oss.qualcomm.com>
Pass down the notifications about the IRQ_HPD events down to the
individual drivers, letting them handle those as required.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/bridge/chrontel-ch7033.c | 3 ++-
drivers/gpu/drm/bridge/lontium-lt8912b.c | 3 ++-
drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 3 ++-
drivers/gpu/drm/bridge/ti-tfp410.c | 4 ++--
drivers/gpu/drm/display/drm_bridge_connector.c | 22 +++++++++++++---------
drivers/gpu/drm/drm_bridge.c | 5 +++--
drivers/gpu/drm/drm_connector.c | 2 +-
drivers/gpu/drm/i915/display/intel_dp.c | 3 ++-
drivers/gpu/drm/meson/meson_encoder_hdmi.c | 3 ++-
drivers/gpu/drm/msm/dp/dp_display.c | 3 ++-
drivers/gpu/drm/msm/dp/dp_drm.h | 3 ++-
drivers/gpu/drm/omapdrm/dss/hdmi4.c | 3 ++-
include/drm/drm_bridge.h | 9 ++++++---
include/drm/drm_connector.h | 3 ++-
14 files changed, 43 insertions(+), 26 deletions(-)
diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c
index 54d49d4882c8..04e6b4c00a28 100644
--- a/drivers/gpu/drm/bridge/chrontel-ch7033.c
+++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c
@@ -259,7 +259,8 @@ static const struct drm_connector_helper_funcs ch7033_connector_helper_funcs = {
.best_encoder = ch7033_connector_best_encoder,
};
-static void ch7033_hpd_event(void *arg, enum drm_connector_status status)
+static void ch7033_hpd_event(void *arg, enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct ch7033_priv *priv = arg;
diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c
index 8a0b48efca58..b404f0cbf60d 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8912b.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c
@@ -504,7 +504,8 @@ static int lt8912_attach_dsi(struct lt8912 *lt)
return 0;
}
-static void lt8912_bridge_hpd_cb(void *data, enum drm_connector_status status)
+static void lt8912_bridge_hpd_cb(void *data, enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct lt8912 *lt = data;
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
index 11aab07d88df..ca41ebe9f26f 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
@@ -430,7 +430,8 @@ static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *brid
static void lt9611uxc_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
const struct drm_edid *drm_edid;
diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c
index 3b6b0e92cf89..199916662895 100644
--- a/drivers/gpu/drm/bridge/ti-tfp410.c
+++ b/drivers/gpu/drm/bridge/ti-tfp410.c
@@ -39,7 +39,6 @@ drm_bridge_to_tfp410(struct drm_bridge *bridge)
{
return container_of(bridge, struct tfp410, bridge);
}
-
static inline struct tfp410 *
drm_connector_to_tfp410(struct drm_connector *connector)
{
@@ -110,7 +109,8 @@ static void tfp410_hpd_work_func(struct work_struct *work)
drm_helper_hpd_irq_event(dvi->bridge.dev);
}
-static void tfp410_hpd_callback(void *arg, enum drm_connector_status status)
+static void tfp410_hpd_callback(void *arg, enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct tfp410 *dvi = arg;
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 39cc18f78eda..5fdb1a231cec 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -141,7 +141,8 @@ struct drm_bridge_connector {
*/
static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
@@ -149,12 +150,13 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
/* Notify all bridges in the pipeline of hotplug events. */
drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) {
if (bridge->funcs->hpd_notify)
- bridge->funcs->hpd_notify(bridge, connector, status);
+ bridge->funcs->hpd_notify(bridge, connector, status, extra_status);
}
}
static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bridge_connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct drm_connector *connector = &drm_bridge_connector->base;
struct drm_device *dev = connector->dev;
@@ -163,24 +165,26 @@ static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bri
connector->status = status;
mutex_unlock(&dev->mode_config.mutex);
- drm_bridge_connector_hpd_notify(connector, status);
+ drm_bridge_connector_hpd_notify(connector, status, extra_status);
drm_kms_helper_connector_hotplug_event(connector);
}
static void drm_bridge_connector_hpd_cb(void *cb_data,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
- drm_bridge_connector_handle_hpd(cb_data, status);
+ drm_bridge_connector_handle_hpd(cb_data, status, extra_status);
}
static void drm_bridge_connector_oob_hotplug_event(struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
- drm_bridge_connector_handle_hpd(bridge_connector, status);
+ drm_bridge_connector_handle_hpd(bridge_connector, status, extra_status);
}
static void drm_bridge_connector_enable_hpd(struct drm_connector *connector)
@@ -223,7 +227,7 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
if (hdmi)
drm_atomic_helper_connector_hdmi_hotplug(connector, status);
- drm_bridge_connector_hpd_notify(connector, status);
+ drm_bridge_connector_hpd_notify(connector, status, DRM_CONNECTOR_NO_EXTRA_STATUS);
} else {
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_DPI:
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index d6f512b73389..c8c3301cd936 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -1444,7 +1444,8 @@ EXPORT_SYMBOL_GPL(drm_bridge_edid_read);
*/
void drm_bridge_hpd_enable(struct drm_bridge *bridge,
void (*cb)(void *data,
- enum drm_connector_status status),
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status),
void *data)
{
if (!(bridge->ops & DRM_BRIDGE_OP_HPD))
@@ -1509,7 +1510,7 @@ void drm_bridge_hpd_notify(struct drm_bridge *bridge,
{
mutex_lock(&bridge->hpd_mutex);
if (bridge->hpd_cb)
- bridge->hpd_cb(bridge->hpd_data, status);
+ bridge->hpd_cb(bridge->hpd_data, status, DRM_CONNECTOR_NO_EXTRA_STATUS);
mutex_unlock(&bridge->hpd_mutex);
}
EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index edee9daccd51..415eb834808c 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -3532,7 +3532,7 @@ void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode,
return;
if (connector->funcs->oob_hotplug_event)
- connector->funcs->oob_hotplug_event(connector, status);
+ connector->funcs->oob_hotplug_event(connector, status, extra_status);
drm_connector_put(connector);
}
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 4955bd8b11d7..98bbcab2067b 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -6779,7 +6779,8 @@ static int intel_dp_connector_atomic_check(struct drm_connector *_connector,
}
static void intel_dp_oob_hotplug_event(struct drm_connector *_connector,
- enum drm_connector_status hpd_state)
+ enum drm_connector_status hpd_state,
+ enum drm_connector_status_extra extra_status)
{
struct intel_connector *connector = to_intel_connector(_connector);
struct intel_display *display = to_intel_display(connector);
diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
index 1abb0572bb5f..691b9996c8a4 100644
--- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
@@ -323,7 +323,8 @@ static int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge,
static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index d2124d625485..7a0623fdbd8e 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1785,7 +1785,8 @@ void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge)
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge);
struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display;
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 9eb3431dd93a..74da3ef6b625 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -41,6 +41,7 @@ void msm_dp_bridge_hpd_enable(struct drm_bridge *bridge);
void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge);
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
- enum drm_connector_status status);
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status);
#endif /* _DP_DRM_H_ */
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index 29b2dfb90b5f..a7288791b2a5 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -429,7 +429,8 @@ static void hdmi4_bridge_disable(struct drm_bridge *bridge,
static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index a8d67bd9ee50..3e4672fbd7a8 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -615,7 +615,8 @@ struct drm_bridge_funcs {
*/
void (*hpd_notify)(struct drm_bridge *bridge,
struct drm_connector *connector,
- enum drm_connector_status status);
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status);
/**
* @hpd_enable:
@@ -1260,7 +1261,8 @@ struct drm_bridge {
* @hpd_cb: Hot plug detection callback, registered with
* drm_bridge_hpd_enable().
*/
- void (*hpd_cb)(void *data, enum drm_connector_status status);
+ void (*hpd_cb)(void *data, enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status);
/**
* @hpd_data: Private data passed to the Hot plug detection callback
* @hpd_cb.
@@ -1550,7 +1552,8 @@ const struct drm_edid *drm_bridge_edid_read(struct drm_bridge *bridge,
struct drm_connector *connector);
void drm_bridge_hpd_enable(struct drm_bridge *bridge,
void (*cb)(void *data,
- enum drm_connector_status status),
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status),
void *data);
void drm_bridge_hpd_disable(struct drm_bridge *bridge);
void drm_bridge_hpd_notify(struct drm_bridge *bridge,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index e05197e970d3..5ac5a64f83d9 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1720,7 +1720,8 @@ struct drm_connector_funcs {
* has been received from a source outside the display driver / device.
*/
void (*oob_hotplug_event)(struct drm_connector *connector,
- enum drm_connector_status status);
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status);
/**
* @debugfs_init:
--
2.47.3
^ permalink raw reply related
* [PATCH v3 3/6] drm/bridge: aux-hpd: let drivers pass IRQ_HPD events
From: Dmitry Baryshkov @ 2026-04-20 23:33 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Heikki Krogerus, Greg Kroah-Hartman, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Adrien Grassein, Jani Nikula, Rodrigo Vivi,
Joonas Lahtinen, Tvrtko Ursulin, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, Tomi Valkeinen,
Bjorn Andersson, Konrad Dybcio, Pengyu Luo, Nikita Travkin,
Yongxing Mou
Cc: dri-devel, linux-kernel, linux-usb, intel-gfx, intel-xe,
linux-amlogic, linux-arm-kernel, linux-arm-msm, freedreno
In-Reply-To: <20260421-hpd-irq-events-v3-0-44d2bf40dfc2@oss.qualcomm.com>
Let users of aux-hpd, the UCSI and PMIC GLINK drivers pass the IRQ_HPD
events to the DisplayPort drivers.
The drm_aux_hpd_bridge_notify() is keps to ease merging of the series,
preventing extra cross-tree merges. It will be removed once all
drivers are converted. The drm_bridge_hpd_notify() function is kept for
the driver which only care about the connector status and will always
pass false as the irq_hpd event.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/bridge/aux-hpd-bridge.c | 11 +++++++----
drivers/gpu/drm/drm_bridge.c | 17 ++++++++++-------
include/drm/bridge/aux-bridge.h | 13 +++++++++++--
include/drm/drm_bridge.h | 22 ++++++++++++++++++++--
4 files changed, 48 insertions(+), 15 deletions(-)
diff --git a/drivers/gpu/drm/bridge/aux-hpd-bridge.c b/drivers/gpu/drm/bridge/aux-hpd-bridge.c
index f02a38a2638a..0e2f0b046121 100644
--- a/drivers/gpu/drm/bridge/aux-hpd-bridge.c
+++ b/drivers/gpu/drm/bridge/aux-hpd-bridge.c
@@ -136,16 +136,19 @@ struct device *drm_dp_hpd_bridge_register(struct device *parent, struct device_n
EXPORT_SYMBOL_GPL(drm_dp_hpd_bridge_register);
/**
- * drm_aux_hpd_bridge_notify - notify hot plug detection events
+ * drm_aux_hpd_bridge_notify_extra - notify hot plug detection events
* @dev: device created for the HPD bridge
* @status: output connection status
+ * @extra_status: extra status bits like DRM_CONNECTOR_DP_IRQ_HPD
*
* A wrapper around drm_bridge_hpd_notify() that is used to report hot plug
* detection events for bridges created via drm_dp_hpd_bridge_register().
*
* This function shall be called in a context that can sleep.
*/
-void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status status)
+void drm_aux_hpd_bridge_notify_extra(struct device *dev,
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct auxiliary_device *adev = to_auxiliary_dev(dev);
struct drm_aux_hpd_bridge_data *data = auxiliary_get_drvdata(adev);
@@ -153,9 +156,9 @@ void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status sta
if (!data)
return;
- drm_bridge_hpd_notify(&data->bridge, status);
+ drm_bridge_hpd_notify_extra(&data->bridge, status, extra_status);
}
-EXPORT_SYMBOL_GPL(drm_aux_hpd_bridge_notify);
+EXPORT_SYMBOL_GPL(drm_aux_hpd_bridge_notify_extra);
static int drm_aux_hpd_bridge_attach(struct drm_bridge *bridge,
struct drm_encoder *encoder,
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c8c3301cd936..09c3f5954ade 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -1495,25 +1495,28 @@ void drm_bridge_hpd_disable(struct drm_bridge *bridge)
EXPORT_SYMBOL_GPL(drm_bridge_hpd_disable);
/**
- * drm_bridge_hpd_notify - notify hot plug detection events
+ * drm_bridge_hpd_notify_extra - notify hot plug detection and sink IRQ events
* @bridge: bridge control structure
* @status: output connection status
+ * @extra_status: additional status recorded by the sink
*
* Bridge drivers shall call this function to report hot plug events when they
- * detect a change in the output status, when hot plug detection has been
- * enabled by drm_bridge_hpd_enable().
+ * detect a change in the output status or when the sink has reported extra HPD
+ * status events (like the IRQ_HPD in case of the DisplayPort), when hot plug
+ * detection has been enabled by drm_bridge_hpd_enable().
*
* This function shall be called in a context that can sleep.
*/
-void drm_bridge_hpd_notify(struct drm_bridge *bridge,
- enum drm_connector_status status)
+void drm_bridge_hpd_notify_extra(struct drm_bridge *bridge,
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
mutex_lock(&bridge->hpd_mutex);
if (bridge->hpd_cb)
- bridge->hpd_cb(bridge->hpd_data, status, DRM_CONNECTOR_NO_EXTRA_STATUS);
+ bridge->hpd_cb(bridge->hpd_data, status, extra_status);
mutex_unlock(&bridge->hpd_mutex);
}
-EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify);
+EXPORT_SYMBOL_GPL(drm_bridge_hpd_notify_extra);
#ifdef CONFIG_OF
/**
diff --git a/include/drm/bridge/aux-bridge.h b/include/drm/bridge/aux-bridge.h
index c2f5a855512f..f9a86886b0df 100644
--- a/include/drm/bridge/aux-bridge.h
+++ b/include/drm/bridge/aux-bridge.h
@@ -25,7 +25,9 @@ struct auxiliary_device *devm_drm_dp_hpd_bridge_alloc(struct device *parent, str
int devm_drm_dp_hpd_bridge_add(struct device *dev, struct auxiliary_device *adev);
struct device *drm_dp_hpd_bridge_register(struct device *parent,
struct device_node *np);
-void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status status);
+void drm_aux_hpd_bridge_notify_extra(struct device *dev,
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status);
#else
static inline struct auxiliary_device *devm_drm_dp_hpd_bridge_alloc(struct device *parent,
struct device_node *np)
@@ -44,9 +46,16 @@ static inline struct device *drm_dp_hpd_bridge_register(struct device *parent,
return NULL;
}
-static inline void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status status)
+static inline void drm_aux_hpd_bridge_notify_extra(struct device *dev,
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
}
#endif
+static inline void drm_aux_hpd_bridge_notify(struct device *dev, enum drm_connector_status status)
+{
+ drm_aux_hpd_bridge_notify_extra(dev, status, DRM_CONNECTOR_NO_EXTRA_STATUS);
+}
+
#endif
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 3e4672fbd7a8..2cf604cf02db 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -1556,8 +1556,26 @@ void drm_bridge_hpd_enable(struct drm_bridge *bridge,
enum drm_connector_status_extra extra_status),
void *data);
void drm_bridge_hpd_disable(struct drm_bridge *bridge);
-void drm_bridge_hpd_notify(struct drm_bridge *bridge,
- enum drm_connector_status status);
+void drm_bridge_hpd_notify_extra(struct drm_bridge *bridge,
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status);
+
+/**
+ * drm_bridge_hpd_notify - notify hot plug detection events
+ * @bridge: bridge control structure
+ * @status: output connection status
+ *
+ * Bridge drivers shall call this function to report hot plug events when they
+ * detect a change in the output status, when hot plug detection has been
+ * enabled by drm_bridge_hpd_enable().
+ *
+ * This function shall be called in a context that can sleep.
+ */
+static inline void drm_bridge_hpd_notify(struct drm_bridge *bridge,
+ enum drm_connector_status status)
+{
+ drm_bridge_hpd_notify_extra(bridge, status, DRM_CONNECTOR_NO_EXTRA_STATUS);
+}
#ifdef CONFIG_DRM_PANEL_BRIDGE
bool drm_bridge_is_panel(const struct drm_bridge *bridge);
--
2.47.3
^ permalink raw reply related
* [PATCH v3 4/6] drm/msm: dp: handle the IRQ_HPD events reported by USB-C
From: Dmitry Baryshkov @ 2026-04-20 23:33 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Heikki Krogerus, Greg Kroah-Hartman, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Adrien Grassein, Jani Nikula, Rodrigo Vivi,
Joonas Lahtinen, Tvrtko Ursulin, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, Tomi Valkeinen,
Bjorn Andersson, Konrad Dybcio, Pengyu Luo, Nikita Travkin,
Yongxing Mou
Cc: dri-devel, linux-kernel, linux-usb, intel-gfx, intel-xe,
linux-amlogic, linux-arm-kernel, linux-arm-msm, freedreno
In-Reply-To: <20260421-hpd-irq-events-v3-0-44d2bf40dfc2@oss.qualcomm.com>
Let the MSM DisplayPort driver properly track and handle IRQ_HPD
delivered over the OOB events (e.g. from the USB-C AltMode handler).
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 7a0623fdbd8e..8df579bb320a 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1800,4 +1800,7 @@ void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
msm_dp_add_event(dp, EV_HPD_PLUG_INT, 0, 0);
else if (msm_dp_display->link_ready && status == connector_status_disconnected)
msm_dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0);
+
+ if (extra_status == DRM_CONNECTOR_DP_IRQ_HPD)
+ msm_dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0);
}
--
2.47.3
^ permalink raw reply related
* [PATCH v3 5/6] soc: qcom: pmic-glink-altmode: pass down HPD_IRQ events
From: Dmitry Baryshkov @ 2026-04-20 23:33 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Heikki Krogerus, Greg Kroah-Hartman, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Adrien Grassein, Jani Nikula, Rodrigo Vivi,
Joonas Lahtinen, Tvrtko Ursulin, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, Tomi Valkeinen,
Bjorn Andersson, Konrad Dybcio, Pengyu Luo, Nikita Travkin,
Yongxing Mou
Cc: dri-devel, linux-kernel, linux-usb, intel-gfx, intel-xe,
linux-amlogic, linux-arm-kernel, linux-arm-msm, freedreno
In-Reply-To: <20260421-hpd-irq-events-v3-0-44d2bf40dfc2@oss.qualcomm.com>
Pass IRQ_HPD events to the HPD bridge, letting those to be delivered to
the DisplayPort driver.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/soc/qcom/pmic_glink_altmode.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
index 619bad2c27ee..946eb20b8f83 100644
--- a/drivers/soc/qcom/pmic_glink_altmode.c
+++ b/drivers/soc/qcom/pmic_glink_altmode.c
@@ -373,7 +373,11 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
else
conn_status = connector_status_disconnected;
- drm_aux_hpd_bridge_notify(&alt_port->bridge->dev, conn_status);
+ drm_aux_hpd_bridge_notify_extra(&alt_port->bridge->dev,
+ conn_status,
+ alt_port->hpd_irq ?
+ DRM_CONNECTOR_DP_IRQ_HPD :
+ DRM_CONNECTOR_NO_EXTRA_STATUS);
} else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
if (alt_port->svid == USB_TYPEC_TBT_SID)
pmic_glink_altmode_enable_tbt(altmode, alt_port);
--
2.47.3
^ permalink raw reply related
* [PATCH v3 6/6] usb: typec: ucsi: huawei-gaokun: pass down HPD_IRQ events
From: Dmitry Baryshkov @ 2026-04-20 23:33 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Heikki Krogerus, Greg Kroah-Hartman, Andrzej Hajda,
Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
Jernej Skrabec, Adrien Grassein, Jani Nikula, Rodrigo Vivi,
Joonas Lahtinen, Tvrtko Ursulin, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Rob Clark, Dmitry Baryshkov, Abhinav Kumar,
Jessica Zhang, Sean Paul, Marijn Suijten, Tomi Valkeinen,
Bjorn Andersson, Konrad Dybcio, Pengyu Luo, Nikita Travkin,
Yongxing Mou
Cc: dri-devel, linux-kernel, linux-usb, intel-gfx, intel-xe,
linux-amlogic, linux-arm-kernel, linux-arm-msm, freedreno
In-Reply-To: <20260421-hpd-irq-events-v3-0-44d2bf40dfc2@oss.qualcomm.com>
Pass IRQ_HPD events to the HPD bridge, letting those to be delivered to
the DisplayPort driver.
Reviewed-by: Pengyu Luo <mitltlatltl@gmail.com>
Acked-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
index ca749fde49bd..5e3f887ecbd8 100644
--- a/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
+++ b/drivers/usb/typec/ucsi/ucsi_huawei_gaokun.c
@@ -299,10 +299,13 @@ static void gaokun_ucsi_handle_altmode(struct gaokun_ucsi_port *port)
/* UCSI callback .connector_status() have set orientation */
if (port->bridge)
- drm_aux_hpd_bridge_notify(&port->bridge->dev,
- port->hpd_state ?
- connector_status_connected :
- connector_status_disconnected);
+ drm_aux_hpd_bridge_notify_extra(&port->bridge->dev,
+ port->hpd_state ?
+ connector_status_connected :
+ connector_status_disconnected,
+ port->hpd_irq ?
+ DRM_CONNECTOR_DP_IRQ_HPD :
+ DRM_CONNECTOR_NO_EXTRA_STATUS);
gaokun_ec_ucsi_pan_ack(uec->ec, port->idx);
}
--
2.47.3
^ permalink raw reply related
* [PATCH v3 0/3] pmdomain: core: add support for domain hierarchies in DT
From: Kevin Hilman (TI) @ 2026-04-20 23:51 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring
Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
linux-arm-kernel
Currently, PM domains can only support hierarchy for simple
providers (e.g. ones with #power-domain-cells = 0).
Add support for oncell providers as well by adding a new property
`power-domains-child-ids` to describe the parent/child relationship.
Also adds the first user of the new API: the Arm SCMI PM domain driver.
Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
Changes in v3:
- cleanup return codes for add_child_ids()
- < 0: error
- zero means no child domains present
- > 0: number of child domains added
- arm_scmi: adopt to new return codes (only <0 is an error)
- Link to v2: https://patch.msgid.link/20260410-topic-lpm-pmdomain-child-ids-v2-0-83396e4b5f8b@baylibre.com
Changes compared to initial RFC[2]
- dropped RFC
- rewrote the parse/add function to use iterators/helpers from of.h
- add a remove function for cleanup
- use child domain language instead of subdomain
This idea was previously discussed on the arm-scmi mailing list[1]
where this approach was proposed by Ulf, and then an initial RFC[2]
implementation was made. From there, it was suggested by Rob[3] to
use a nexus node map instead, which led to several more versions
attempting to implement that, culminating in v5[4], where Rob and
Geert then had second thoughts about the power-domain-map approach.
Therefore, I've gone back to the approach in the initial RFC[2] to use
the child-ids approach.
[1] https://lore.kernel.org/arm-scmi/CAPDyKFo_P129sVirHHYjOQT+QUmpymcRJme9obzKJeRgO7B-1A@mail.gmail.com/
[2] https://lore.kernel.org/all/20250528-pmdomain-hierarchy-onecell-v1-1-851780700c68@baylibre.com/
[3] https://lore.kernel.org/all/20250528203532.GA704342-robh@kernel.org/
[4] https://lore.kernel.org/r/20260122-pmdomain-hierarchy-onecell-v5-0-76855ec856bd@baylibre.com
Changes in v2:
- dt-bindings: fix warinings from make dt_binding_check
- scmi_pm_domain: switch to dev_err()
- pmdomain: core: fix locking around add/remove domains
- pmdomain: error unwind if any children fail to be added
- pmdomain: fix node reference leak
- pmdomain: ensure power-domains and child-ids properties are same
length before iterating
- Link to v1: https://patch.msgid.link/20260310-topic-lpm-pmdomain-child-ids-v1-0-5361687a18ff@baylibre.com
---
Kevin Hilman (TI) (3):
dt-bindings: power: Add power-domains-child-ids property
pmdomain: core: add support for power-domains-child-ids
pmdomain: arm_scmi: add support for domain hierarchies
Documentation/devicetree/bindings/power/power-domain.yaml | 34 ++++++++++++++++++++++++++++++++++
drivers/pmdomain/arm/scmi_pm_domain.c | 14 +++++++++++++-
drivers/pmdomain/core.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pm_domain.h | 16 ++++++++++++++++
4 files changed, 230 insertions(+), 1 deletion(-)
---
base-commit: f7b88edb52c8dd01b7e576390d658ae6eef0e134
change-id: 20260310-topic-lpm-pmdomain-child-ids-e3d57ae57040
Best regards,
--
Kevin Hilman (TI) <khilman@baylibre.com>
^ permalink raw reply
* [PATCH v3 2/3] pmdomain: core: add support for power-domains-child-ids
From: Kevin Hilman (TI) @ 2026-04-20 23:51 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring
Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
linux-arm-kernel
In-Reply-To: <20260420-topic-lpm-pmdomain-child-ids-v3-0-c2c40bef238c@baylibre.com>
Currently, PM domains can only support hierarchy for simple
providers (e.g. ones with #power-domain-cells = 0).
Add support for oncell providers as well by adding a new property
`power-domains-child-ids` to describe the parent/child relationship.
For example, an SCMI PM domain provider has multiple domains, each of
which might be a child of diffeent parent domains. In this example,
the parent domains are MAIN_PD and WKUP_PD:
scmi_pds: protocol@11 {
reg = <0x11>;
#power-domain-cells = <1>;
power-domains = <&MAIN_PD>, <&WKUP_PD>;
power-domains-child-ids = <15>, <19>;
};
With this example using the new property, SCMI PM domain 15 becomes a
child domain of MAIN_PD, and SCMI domain 19 becomes a child domain of
WKUP_PD.
To support this feature, add two new core functions
- of_genpd_add_child_ids()
- of_genpd_remove_child_ids()
which can be called by pmdomain providers to add/remove child domains
if they support the new property power-domains-child-ids.
The add function is "all or nothing". If it cannot add all of the
child domains in the list, it will unwind any additions already made
and report a failure.
Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
drivers/pmdomain/core.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/pm_domain.h | 16 ++++++++++++++++
2 files changed, 183 insertions(+)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 61c2277c9ce3..97498a37336d 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -2909,6 +2909,173 @@ static struct generic_pm_domain *genpd_get_from_provider(
return genpd;
}
+/**
+ * of_genpd_add_child_ids() - Parse power-domains-child-ids property
+ * @np: Device node pointer associated with the PM domain provider.
+ * @data: Pointer to the onecell data associated with the PM domain provider.
+ *
+ * Parse the power-domains and power-domains-child-ids properties to establish
+ * parent-child relationships for PM domains. The power-domains property lists
+ * parent domains, and power-domains-child-ids lists which child domain IDs
+ * should be associated with each parent.
+ *
+ * Uses "all or nothing" semantics: either all relationships are established
+ * successfully, or none are (any partially-added relationships are unwound
+ * on error).
+ *
+ * Returns the number of parent-child relationships established on success,
+ * 0 if the properties don't exist, or a negative error code on failure.
+ */
+int of_genpd_add_child_ids(struct device_node *np,
+ struct genpd_onecell_data *data)
+{
+ struct of_phandle_args parent_args;
+ struct generic_pm_domain *parent_genpd, *child_genpd;
+ struct generic_pm_domain **pairs; /* pairs[2*i]=parent, pairs[2*i+1]=child */
+ u32 child_id;
+ int i, ret, count, child_count, added = 0;
+
+ /* Check if both properties exist */
+ count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells");
+ if (count <= 0)
+ return 0;
+
+ child_count = of_property_count_u32_elems(np, "power-domains-child-ids");
+ if (child_count < 0)
+ return 0;
+ if (child_count != count)
+ return -EINVAL;
+
+ /* Allocate tracking array for error unwind (parent/child pairs) */
+ pairs = kmalloc_array(count * 2, sizeof(*pairs), GFP_KERNEL);
+ if (!pairs)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ ret = of_property_read_u32_index(np, "power-domains-child-ids",
+ i, &child_id);
+ if (ret)
+ goto err_unwind;
+
+ /* Validate child ID is within bounds */
+ if (child_id >= data->num_domains) {
+ pr_err("Child ID %u out of bounds (max %u) for %pOF\n",
+ child_id, data->num_domains - 1, np);
+ ret = -EINVAL;
+ goto err_unwind;
+ }
+
+ /* Get the child domain */
+ child_genpd = data->domains[child_id];
+ if (!child_genpd) {
+ pr_err("Child domain %u is NULL for %pOF\n", child_id, np);
+ ret = -EINVAL;
+ goto err_unwind;
+ }
+
+ ret = of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", i,
+ &parent_args);
+ if (ret)
+ goto err_unwind;
+
+ /* Get the parent domain */
+ parent_genpd = genpd_get_from_provider(&parent_args);
+ of_node_put(parent_args.np);
+ if (IS_ERR(parent_genpd)) {
+ pr_err("Failed to get parent domain for %pOF: %ld\n",
+ np, PTR_ERR(parent_genpd));
+ ret = PTR_ERR(parent_genpd);
+ goto err_unwind;
+ }
+
+ /* Establish parent-child relationship */
+ ret = pm_genpd_add_subdomain(parent_genpd, child_genpd);
+ if (ret) {
+ pr_err("Failed to add child domain %u to parent in %pOF: %d\n",
+ child_id, np, ret);
+ goto err_unwind;
+ }
+
+ /* Track for potential unwind */
+ pairs[2 * added] = parent_genpd;
+ pairs[2 * added + 1] = child_genpd;
+ added++;
+
+ pr_debug("Added child domain %u (%s) to parent %s for %pOF\n",
+ child_id, child_genpd->name, parent_genpd->name, np);
+ }
+
+ kfree(pairs);
+ return count;
+
+err_unwind:
+ /* Reverse all previously established relationships */
+ while (added-- > 0)
+ pm_genpd_remove_subdomain(pairs[2 * added], pairs[2 * added + 1]);
+ kfree(pairs);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(of_genpd_add_child_ids);
+
+/**
+ * of_genpd_remove_child_ids() - Remove parent-child PM domain relationships
+ * @np: Device node pointer associated with the PM domain provider.
+ * @data: Pointer to the onecell data associated with the PM domain provider.
+ *
+ * Reverses the effect of of_genpd_add_child_ids() by parsing the same
+ * power-domains and power-domains-child-ids properties and calling
+ * pm_genpd_remove_subdomain() for each established relationship.
+ *
+ * Returns 0 on success, -ENOENT if properties don't exist, or negative error
+ * code on failure.
+ */
+int of_genpd_remove_child_ids(struct device_node *np,
+ struct genpd_onecell_data *data)
+{
+ struct of_phandle_args parent_args;
+ struct generic_pm_domain *parent_genpd, *child_genpd;
+ u32 child_id;
+ int i, ret, count, child_count;
+
+ /* Check if both properties exist */
+ count = of_count_phandle_with_args(np, "power-domains", "#power-domain-cells");
+ if (count <= 0)
+ return -ENOENT;
+
+ child_count = of_property_count_u32_elems(np, "power-domains-child-ids");
+ if (child_count < 0)
+ return -ENOENT;
+ if (child_count != count)
+ return -EINVAL;
+
+ for (i = 0; i < count; i++) {
+ if (of_property_read_u32_index(np, "power-domains-child-ids",
+ i, &child_id))
+ continue;
+
+ if (child_id >= data->num_domains || !data->domains[child_id])
+ continue;
+
+ ret = of_parse_phandle_with_args(np, "power-domains",
+ "#power-domain-cells", i,
+ &parent_args);
+ if (ret)
+ continue;
+
+ parent_genpd = genpd_get_from_provider(&parent_args);
+ of_node_put(parent_args.np);
+ if (IS_ERR(parent_genpd))
+ continue;
+
+ child_genpd = data->domains[child_id];
+ pm_genpd_remove_subdomain(parent_genpd, child_genpd);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_genpd_remove_child_ids);
+
/**
* of_genpd_add_device() - Add a device to an I/O PM domain
* @genpdspec: OF phandle args to use for look-up PM domain
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index f67a2cb7d781..b44615d79af6 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -465,6 +465,10 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np);
int of_genpd_parse_idle_states(struct device_node *dn,
struct genpd_power_state **states, int *n);
void of_genpd_sync_state(struct device_node *np);
+int of_genpd_add_child_ids(struct device_node *np,
+ struct genpd_onecell_data *data);
+int of_genpd_remove_child_ids(struct device_node *np,
+ struct genpd_onecell_data *data);
int genpd_dev_pm_attach(struct device *dev);
struct device *genpd_dev_pm_attach_by_id(struct device *dev,
@@ -534,6 +538,18 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
{
return ERR_PTR(-EOPNOTSUPP);
}
+
+static inline int of_genpd_add_child_ids(struct device_node *np,
+ struct genpd_onecell_data *data)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int of_genpd_remove_child_ids(struct device_node *np,
+ struct genpd_onecell_data *data)
+{
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_PM_GENERIC_DOMAINS_OF */
#ifdef CONFIG_PM
--
2.51.0
^ permalink raw reply related
* [PATCH v3 3/3] pmdomain: arm_scmi: add support for domain hierarchies
From: Kevin Hilman (TI) @ 2026-04-20 23:51 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring
Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
linux-arm-kernel
In-Reply-To: <20260420-topic-lpm-pmdomain-child-ids-v3-0-c2c40bef238c@baylibre.com>
After primary SCMI pmdomain is created, use new of_genpd helper which
checks for child domain mappings defined in power-domains-child-ids.
Also remove any child domain mappings when SCMI domain is removed.
Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
drivers/pmdomain/arm/scmi_pm_domain.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/pmdomain/arm/scmi_pm_domain.c b/drivers/pmdomain/arm/scmi_pm_domain.c
index b5e2ffd5ea64..66dcf2e687a8 100644
--- a/drivers/pmdomain/arm/scmi_pm_domain.c
+++ b/drivers/pmdomain/arm/scmi_pm_domain.c
@@ -114,6 +114,14 @@ static int scmi_pm_domain_probe(struct scmi_device *sdev)
dev_set_drvdata(dev, scmi_pd_data);
+ /*
+ * Parse (optional) power-domains-child-ids property to
+ * establish parent-child relationships
+ */
+ ret = of_genpd_add_child_ids(np, scmi_pd_data);
+ if (ret < 0)
+ dev_err(dev, "Failed to add child domain hierarchy: %d\n", ret);
+
return 0;
err_rm_genpds:
for (i = num_domains - 1; i >= 0; i--)
@@ -129,9 +137,13 @@ static void scmi_pm_domain_remove(struct scmi_device *sdev)
struct device *dev = &sdev->dev;
struct device_node *np = dev->of_node;
+ scmi_pd_data = dev_get_drvdata(dev);
+
+ /* Remove any parent-child relationships established at probe time */
+ of_genpd_remove_child_ids(np, scmi_pd_data);
+
of_genpd_del_provider(np);
- scmi_pd_data = dev_get_drvdata(dev);
for (i = 0; i < scmi_pd_data->num_domains; i++) {
if (!scmi_pd_data->domains[i])
continue;
--
2.51.0
^ permalink raw reply related
* [PATCH v3 1/3] dt-bindings: power: Add power-domains-child-ids property
From: Kevin Hilman (TI) @ 2026-04-20 23:51 UTC (permalink / raw)
To: Ulf Hansson, Rob Herring
Cc: Geert Uytterhoeven, linux-pm, devicetree, linux-kernel, arm-scmi,
linux-arm-kernel
In-Reply-To: <20260420-topic-lpm-pmdomain-child-ids-v3-0-c2c40bef238c@baylibre.com>
Add binding documentation for the new power-domains-child-ids property,
which works in conjunction with the existing power-domains property to
establish parent-child relationships between a multi-domain power domain
provider and external parent domains.
Each element in the uint32 array identifies the child domain
ID (index) within the provider that should be made a child domain of
the corresponding phandle entry in power-domains. The two arrays must
have the same number of elements.
Signed-off-by: Kevin Hilman (TI) <khilman@baylibre.com>
---
Documentation/devicetree/bindings/power/power-domain.yaml | 34 ++++++++++++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/Documentation/devicetree/bindings/power/power-domain.yaml b/Documentation/devicetree/bindings/power/power-domain.yaml
index b1147dbf2e73..163b0af158fd 100644
--- a/Documentation/devicetree/bindings/power/power-domain.yaml
+++ b/Documentation/devicetree/bindings/power/power-domain.yaml
@@ -68,6 +68,21 @@ properties:
by the given provider should be subdomains of the domain specified
by this binding.
+ power-domains-child-ids:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ description:
+ An array of child domain IDs that correspond to the power-domains
+ property. This property is only applicable to power domain providers
+ with "#power-domain-cells" > 0 (i.e., providers that supply multiple
+ power domains). It specifies which of the provider's child domains
+ should be associated with each parent domain listed in the power-domains
+ property. The number of elements in this array must match the number of
+ phandles in the power-domains property. Each element specifies the child
+ domain ID (index) that should be made a child domain of the corresponding
+ parent domain. This enables hierarchical power domain structures where
+ different child domains from the same provider can have different
+ parent domains.
+
required:
- "#power-domain-cells"
@@ -133,3 +148,22 @@ examples:
min-residency-us = <7000>;
};
};
+
+ - |
+ // Example: SCMI domain 15 -> MAIN_PD, SCMI domain 19 -> WKUP_PD
+ MAIN_PD: power-controller-main {
+ compatible = "foo,power-controller";
+ #power-domain-cells = <0>;
+ };
+
+ WKUP_PD: power-controller-wkup {
+ compatible = "foo,power-controller";
+ #power-domain-cells = <0>;
+ };
+
+ scmi_pds: power-controller-scmi {
+ compatible = "foo,power-controller";
+ #power-domain-cells = <1>;
+ power-domains = <&MAIN_PD>, <&WKUP_PD>;
+ power-domains-child-ids = <15>, <19>;
+ };
--
2.51.0
^ permalink raw reply related
* Re: [PATCH v3 3/6] soc: mediatek: mtk-devapc: Add support for MT8189 DEVAPC
From: CK Hu (胡俊光) @ 2026-04-21 0:14 UTC (permalink / raw)
To: robh@kernel.org, Xiaoshun Xu (徐晓顺),
krzk+dt@kernel.org, conor+dt@kernel.org, matthias.bgg@gmail.com,
AngeloGioacchino Del Regno
Cc: linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org,
Sirius Wang (王皓昱),
Project_Global_Chrome_Upstream_Group,
Vince-WL Liu (劉文龍)
In-Reply-To: <20260416031231.2932493-4-xiaoshun.xu@mediatek.com>
On Thu, 2026-04-16 at 11:12 +0800, Xiaoshun Xu wrote:
> Add support for MT8189 DEVAPC, DEVAPC debug registers have new version,
> so refine the structure of devapc_regs_ofs_xxxx to devapc_regs_ofs_verX,
> and rename the infra_base to base in mtk_devapc_context because devapc
> not only access the infra_base to dump debug information when violation
> happens
This patch does many modification.
It's better to separate these modification so we could more clearly understand what does each do.
I suggest break this patch into these patches:
1. lower case for hex number.
2. get vio_idx_num from device tree.
3. rename infra_base.
4. add version 1 register definition.
5. add version 2 register definition.
6. add version 3 register definition.
7. add mt8189 devapc
Regards,
CK
>
> Signed-off-by: Xiaoshun Xu <xiaoshun.xu@mediatek.com>
> ---
> drivers/soc/mediatek/mtk-devapc.c | 171 +++++++++++++++++++++++-------
> 1 file changed, 134 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/soc/mediatek/mtk-devapc.c b/drivers/soc/mediatek/mtk-devapc.c
> index f54e310791e5..824b49613c5a 100644
> --- a/drivers/soc/mediatek/mtk-devapc.c
> +++ b/drivers/soc/mediatek/mtk-devapc.c
> @@ -27,9 +27,19 @@ struct mtk_devapc_vio_dbgs {
> u32 addr_h:4;
> u32 resv:4;
> } dbg0_bits;
> +
> + struct {
> + u32 dmnid:6;
> + u32 vio_w:1;
> + u32 vio_r:1;
> + u32 addr_h:4;
> + u32 resv:20;
> + } dbg0_bits_ver2;
> };
>
> u32 vio_dbg1;
> + u32 vio_dbg2;
> + u32 vio_dbg3;
> };
>
> struct mtk_devapc_regs_ofs {
> @@ -38,6 +48,8 @@ struct mtk_devapc_regs_ofs {
> u32 vio_sta_offset;
> u32 vio_dbg0_offset;
> u32 vio_dbg1_offset;
> + u32 vio_dbg2_offset;
> + u32 vio_dbg3_offset;
> u32 apc_con_offset;
> u32 vio_shift_sta_offset;
> u32 vio_shift_sel_offset;
> @@ -45,16 +57,20 @@ struct mtk_devapc_regs_ofs {
> };
>
> struct mtk_devapc_data {
> - /* numbers of violation index */
> - u32 vio_idx_num;
> + u32 version;
> + /* Default numbers of violation index */
> + u32 default_vio_idx_num;
> const struct mtk_devapc_regs_ofs *regs_ofs;
> };
>
> struct mtk_devapc_context {
> struct device *dev;
> - void __iomem *infra_base;
> + void __iomem *base;
> struct clk *infra_clk;
> const struct mtk_devapc_data *data;
> +
> + /* numbers of violation index */
> + u32 vio_idx_num;
> };
>
> static void clear_vio_status(struct mtk_devapc_context *ctx)
> @@ -62,12 +78,12 @@ static void clear_vio_status(struct mtk_devapc_context *ctx)
> void __iomem *reg;
> int i;
>
> - reg = ctx->infra_base + ctx->data->regs_ofs->vio_sta_offset;
> + reg = ctx->base + ctx->data->regs_ofs->vio_sta_offset;
>
> - for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
> + for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->vio_idx_num - 1); i++)
> writel(GENMASK(31, 0), reg + 4 * i);
>
> - writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1, 0),
> + writel(GENMASK(VIO_MOD_TO_REG_OFF(ctx->vio_idx_num - 1), 0),
> reg + 4 * i);
> }
>
> @@ -77,22 +93,22 @@ static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask)
> u32 val;
> int i;
>
> - reg = ctx->infra_base + ctx->data->regs_ofs->vio_mask_offset;
> + reg = ctx->base + ctx->data->regs_ofs->vio_mask_offset;
>
> if (mask)
> val = GENMASK(31, 0);
> else
> val = 0;
>
> - for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
> + for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->vio_idx_num - 1); i++)
> writel(val, reg + 4 * i);
>
> val = readl(reg + 4 * i);
> if (mask)
> - val |= GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
> + val |= GENMASK(VIO_MOD_TO_REG_OFF(ctx->vio_idx_num - 1),
> 0);
> else
> - val &= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx->data->vio_idx_num) - 1,
> + val &= ~GENMASK(VIO_MOD_TO_REG_OFF(ctx->vio_idx_num - 1),
> 0);
>
> writel(val, reg + 4 * i);
> @@ -119,11 +135,11 @@ static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
> int ret;
> u32 val;
>
> - pd_vio_shift_sta_reg = ctx->infra_base +
> + pd_vio_shift_sta_reg = ctx->base +
> ctx->data->regs_ofs->vio_shift_sta_offset;
> - pd_vio_shift_sel_reg = ctx->infra_base +
> + pd_vio_shift_sel_reg = ctx->base +
> ctx->data->regs_ofs->vio_shift_sel_offset;
> - pd_vio_shift_con_reg = ctx->infra_base +
> + pd_vio_shift_con_reg = ctx->base +
> ctx->data->regs_ofs->vio_shift_con_offset;
>
> /* Find the minimum shift group which has violation */
> @@ -134,7 +150,7 @@ static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
> min_shift_group = __ffs(val);
>
> /* Assign the group to sync */
> - writel(0x1 << min_shift_group, pd_vio_shift_sel_reg);
> + writel(BIT(min_shift_group), pd_vio_shift_sel_reg);
>
> /* Start syncing */
> writel(0x1, pd_vio_shift_con_reg);
> @@ -150,7 +166,7 @@ static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
> writel(0x0, pd_vio_shift_con_reg);
>
> /* Write clear */
> - writel(0x1 << min_shift_group, pd_vio_shift_sta_reg);
> + writel(BIT(min_shift_group), pd_vio_shift_sta_reg);
>
> return true;
> }
> @@ -164,22 +180,52 @@ static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
> struct mtk_devapc_vio_dbgs vio_dbgs;
> void __iomem *vio_dbg0_reg;
> void __iomem *vio_dbg1_reg;
> + void __iomem *vio_dbg2_reg;
> + void __iomem *vio_dbg3_reg;
> + u32 vio_addr_l, vio_addr_h, bus_id, domain_id;
> + u32 vio_w, vio_r;
> + u64 vio_addr;
>
> - vio_dbg0_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg0_offset;
> - vio_dbg1_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg1_offset;
> + vio_dbg0_reg = ctx->base + ctx->data->regs_ofs->vio_dbg0_offset;
> + vio_dbg1_reg = ctx->base + ctx->data->regs_ofs->vio_dbg1_offset;
> + vio_dbg2_reg = ctx->base + ctx->data->regs_ofs->vio_dbg2_offset;
> + vio_dbg3_reg = ctx->base + ctx->data->regs_ofs->vio_dbg3_offset;
>
> vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg);
> vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg);
> + if (ctx->data->version >= 2U)
> + vio_dbgs.vio_dbg2 = readl(vio_dbg2_reg);
> + if (ctx->data->version == 3U)
> + vio_dbgs.vio_dbg3 = readl(vio_dbg3_reg);
> +
> + if (ctx->data->version == 1U) {
> + /* arch version 1 */
> + bus_id = vio_dbgs.dbg0_bits.mstid;
> + vio_addr = vio_dbgs.vio_dbg1;
> + domain_id = vio_dbgs.dbg0_bits.dmnid;
> + vio_w = vio_dbgs.dbg0_bits.vio_w;
> + vio_r = vio_dbgs.dbg0_bits.vio_r;
> + } else {
> + /* arch version 2 & 3 */
> + bus_id = vio_dbgs.vio_dbg1;
> +
> + vio_addr_l = vio_dbgs.vio_dbg2;
> + vio_addr_h = ctx->data->version == 2U ? vio_dbgs.dbg0_bits_ver2.addr_h :
> + vio_dbgs.vio_dbg3;
> + vio_addr = ((u64)vio_addr_h << 32) + vio_addr_l;
> + domain_id = vio_dbgs.dbg0_bits_ver2.dmnid;
> + vio_w = vio_dbgs.dbg0_bits_ver2.vio_w;
> + vio_r = vio_dbgs.dbg0_bits_ver2.vio_r;
> + }
>
> /* Print violation information */
> - if (vio_dbgs.dbg0_bits.vio_w)
> + if (vio_w)
> dev_info(ctx->dev, "Write Violation\n");
> - else if (vio_dbgs.dbg0_bits.vio_r)
> + else if (vio_r)
> dev_info(ctx->dev, "Read Violation\n");
>
> - dev_info(ctx->dev, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%x\n",
> - vio_dbgs.dbg0_bits.mstid, vio_dbgs.dbg0_bits.dmnid,
> - vio_dbgs.vio_dbg1);
> + dev_info(ctx->dev, "Bus ID:0x%x, Dom ID:0x%x, Vio Addr:0x%llx\n",
> + bus_id, domain_id, vio_addr);
> }
>
> /*
> @@ -209,7 +255,8 @@ static irqreturn_t devapc_violation_irq(int irq_number, void *data)
> */
> static void start_devapc(struct mtk_devapc_context *ctx)
> {
> - writel(BIT(31), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
> +
> + writel(BIT(31), ctx->base + ctx->data->regs_ofs->apc_con_offset);
>
> mask_module_irq(ctx, false);
> }
> @@ -221,28 +268,60 @@ static void stop_devapc(struct mtk_devapc_context *ctx)
> {
> mask_module_irq(ctx, true);
>
> - writel(BIT(2), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
> + writel(BIT(2), ctx->base + ctx->data->regs_ofs->apc_con_offset);
> }
>
> -static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt6779 = {
> +static const struct mtk_devapc_regs_ofs devapc_regs_ofs_ver1 = {
> + .vio_mask_offset = 0x0,
> + .vio_sta_offset = 0x400,
> + .vio_dbg0_offset = 0x900,
> + .vio_dbg1_offset = 0x904,
> + .apc_con_offset = 0xf00,
> + .vio_shift_sta_offset = 0xf10,
> + .vio_shift_sel_offset = 0xf14,
> + .vio_shift_con_offset = 0xf20,
> +};
> +
> +static const struct mtk_devapc_regs_ofs devapc_regs_ofs_ver2 = {
> .vio_mask_offset = 0x0,
> .vio_sta_offset = 0x400,
> .vio_dbg0_offset = 0x900,
> .vio_dbg1_offset = 0x904,
> - .apc_con_offset = 0xF00,
> - .vio_shift_sta_offset = 0xF10,
> - .vio_shift_sel_offset = 0xF14,
> - .vio_shift_con_offset = 0xF20,
> + .vio_dbg2_offset = 0x908,
> + .apc_con_offset = 0xf00,
> + .vio_shift_sta_offset = 0xf20,
> + .vio_shift_sel_offset = 0xf30,
> + .vio_shift_con_offset = 0xf10,
> +};
> +
> +static const struct mtk_devapc_regs_ofs devapc_regs_ofs_ver3 = {
> + .vio_mask_offset = 0x0,
> + .vio_sta_offset = 0x400,
> + .vio_dbg0_offset = 0x900,
> + .vio_dbg1_offset = 0x904,
> + .vio_dbg2_offset = 0x908,
> + .vio_dbg3_offset = 0x90c,
> + .apc_con_offset = 0xf00,
> + .vio_shift_sta_offset = 0xf20,
> + .vio_shift_sel_offset = 0xf30,
> + .vio_shift_con_offset = 0xf10,
> };
>
> static const struct mtk_devapc_data devapc_mt6779 = {
> - .vio_idx_num = 511,
> - .regs_ofs = &devapc_regs_ofs_mt6779,
> + .version = 1,
> + .default_vio_idx_num = 511,
> + .regs_ofs = &devapc_regs_ofs_ver1,
> };
>
> static const struct mtk_devapc_data devapc_mt8186 = {
> - .vio_idx_num = 519,
> - .regs_ofs = &devapc_regs_ofs_mt6779,
> + .version = 1,
> + .default_vio_idx_num = 519,
> + .regs_ofs = &devapc_regs_ofs_ver1,
> +};
> +
> +static const struct mtk_devapc_data devapc_mt8189 = {
> + .version = 3,
> + .regs_ofs = &devapc_regs_ofs_ver3,
> };
>
> static const struct of_device_id mtk_devapc_dt_match[] = {
> @@ -252,6 +331,9 @@ static const struct of_device_id mtk_devapc_dt_match[] = {
> }, {
> .compatible = "mediatek,mt8186-devapc",
> .data = &devapc_mt8186,
> + }, {
> + .compatible = "mediatek,mt8189-devapc",
> + .data = &devapc_mt8189,
> }, {
> },
> };
> @@ -274,9 +356,24 @@ static int mtk_devapc_probe(struct platform_device *pdev)
> ctx->data = of_device_get_match_data(&pdev->dev);
> ctx->dev = &pdev->dev;
>
> - ctx->infra_base = of_iomap(node, 0);
> - if (!ctx->infra_base)
> + ctx->base = of_iomap(node, 0);
> + if (!ctx->base) {
> + dev_err(ctx->dev, "Failed to map devapc registers\n");
> return -EINVAL;
> + }
> +
> + /*
> + * Set effective vio_idx_num from default value.
> + * If vio_idx_num is 0, get the info from DT.
> + */
> + ctx->vio_idx_num = ctx->data->default_vio_idx_num;
> + if (ctx->vio_idx_num == 0)
> + if (of_property_read_u32(node,
> + "vio-idx-num",
> + &ctx->vio_idx_num)) {
> + ret = -EINVAL;
> + goto err;
> + }
>
> devapc_irq = irq_of_parse_and_map(node, 0);
> if (!devapc_irq) {
> @@ -314,7 +411,7 @@ static int mtk_devapc_probe(struct platform_device *pdev)
> return 0;
>
> err:
> - iounmap(ctx->infra_base);
> + iounmap(ctx->base);
> return ret;
> }
>
> @@ -326,7 +423,7 @@ static void mtk_devapc_remove(struct platform_device *pdev)
>
> clk_disable_unprepare(ctx->infra_clk);
>
> - iounmap(ctx->infra_base);
> + iounmap(ctx->base);
> }
>
> static struct platform_driver mtk_devapc_driver = {
^ permalink raw reply
* Re: [GIT PULL] arm64 updates for 7.1-rc1 (second round)
From: pr-tracker-bot @ 2026-04-21 0:31 UTC (permalink / raw)
To: Catalin Marinas
Cc: Linus Torvalds, Will Deacon, James Morse, Ben Horgan,
linux-arm-kernel, linux-kernel
In-Reply-To: <aeaDFH0-2zhOQTxy@arm.com>
The pull request you sent on Mon, 20 Apr 2026 20:48:36 +0100:
> git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux tags/arm64-upstream
has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/13f24586a292e35c9cc71e649dc4e4ea1895c5e5
Thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/prtracker.html
^ permalink raw reply
* Re: [PATCH v3 7/8] sframe: Introduce in-kernel SFRAME_VALIDATION.
From: Dylan Hatch @ 2026-04-21 1:29 UTC (permalink / raw)
To: Jens Remus
Cc: Indu Bhagat, Roman Gushchin, Weinan Liu, Will Deacon,
Josh Poimboeuf, Indu Bhagat, Peter Zijlstra, Steven Rostedt,
Catalin Marinas, Jiri Kosina, Mark Rutland, Prasanna Kumar T S M,
Puranjay Mohan, Song Liu, joe.lawrence, linux-toolchains,
linux-kernel, live-patching, linux-arm-kernel, Heiko Carstens
In-Reply-To: <dde1daa9-724c-4186-aaf6-caff6b47c5a9@linux.ibm.com>
On Mon, Apr 20, 2026 at 5:31 AM Jens Remus <jremus@linux.ibm.com> wrote:
>
> On 4/20/2026 7:02 AM, Dylan Hatch wrote:
> > On Thu, Apr 16, 2026 at 8:04 AM Jens Remus <jremus@linux.ibm.com> wrote:
> >> On 4/6/2026 8:49 PM, Dylan Hatch wrote:
>
> >>> Generalize the __safe* helpers to support a non-user-access code path.
> >>> Allow for kernel FDE read failures due to the presence of .rodata.text.
> >>> This section contains code that can't be executed by the kernel
> >>> direclty, and thus lies ouside the normal kernel-text bounds.
> >>
> >> Nits: s/direclty/directly/ s/ouside/outside/
> >>
> >> Could you please explain the issue? How/why does .sframe for
> >> .rodata.text pose an issue for .sframe verification?
> >
> > __read_fde checks that the fde_addr it extracts is within the bounds
> > of sec->text_start and sec->text_end. In the case of the vmlinux
>
> Looking at the existing check in __read_fde(), do you agree that it is
> wrong, as sec->text_end IIUC points behind .text and thus the check
> should be:
>
> if (func_addr < sec->text_start || func_addr >= sec->text_end)
> return -EINVAL;
I agree this is correct. Is this a fix that would be folded into your
previous patch series?
>
> > .sframe section, this is _stext and _etext. However on arm64, there is
> > an .rodata.text section that lies outside this range. From
> > arch/arm64/kernel/vmlinux.lds.S:
> >
> > /* code sections that are never executed via the kernel mapping */
> > .rodata.text : {
> > TRAMP_TEXT
> > HIBERNATE_TEXT
> > KEXEC_TEXT
> > IDMAP_TEXT
> > . = ALIGN(PAGE_SIZE);
> > }
> >
> > So __read_fde fails for functions in this section. Under normal SFrame
> > usage for unwinding, we should never need to look up a PC value in
> > these functions because they will never be executed by the kernel.
> > However, we still hit this error when validating all FDEs.
>
> Thanks for the explanation! Could you please improve the commit
> message, for instance as follows:
>
> __read_fde() checks that the extracted FDE function start address is
> within the bounds of the .text section start and end. In case of
> vmlinux this is _stext and _etext. However on arm64, .rodata.text
> resides outside this range, causing __read_fde() to fail.
>
> > I think ideally we might prevent sframe data from being generated for
> > this code (maybe from the linker script somehow?), but I don't know of
> > a simple way to do this.
>
> I dont't know of any way to exclude a single function or a whole section
> from .sframe generation. The GNU linker would discard SFrame FDEs and
> its FREs for discarded functions. But in this case the function itself
> is not discarded. As .sframe is not generated separately per section it
> is also not possible to discard e.g. .sframe.rodata.
>
> > Alternatively, we can check for FDEs located
> > in .rodata.text during validation, but this seems to only be present
> > in arm64, so maybe we would need an arch-specific hook to do this? I'm
> > open to suggestions.
>
> Maybe that is better than ignoring __read_fde() failures? I first
> thought this would get nasty, but maybe it would not be too bad.
> Following is what I came up with (note tabs replaced by spaces due to
> copy&paste from terminal):
Thanks for the suggestion! I'll look into incorporating this.
>
> diff --git a/arch/arm64/include/asm/sections.h b/arch/arm64/include/asm/sections.h
> @@ -23,6 +23,7 @@ extern char __irqentry_text_start[], __irqentry_text_end[];
> extern char __mmuoff_data_start[], __mmuoff_data_end[];
> extern char __entry_tramp_text_start[], __entry_tramp_text_end[];
> extern char __relocate_new_kernel_start[], __relocate_new_kernel_end[];
> +extern char _srodatatext[], _erodatatext[];
>
> static inline size_t entry_tramp_text_size(void)
> {
> diff --git a/arch/arm64/include/asm/unwind_sframe.h b/arch/arm64/include/asm/unwind_sframe.h
> @@ -2,11 +2,28 @@
> #ifndef _ASM_ARM64_UNWIND_SFRAME_H
> #define _ASM_ARM64_UNWIND_SFRAME_H
>
> +#include <linux/sframe.h>
> +
> #ifdef CONFIG_ARM64
>
> #define SFRAME_REG_SP 31
> #define SFRAME_REG_FP 29
>
> +static inline bool sframe_func_start_addr_valid(struct sframe_section *sec,
> + unsigned long func_addr)
> +{
> + return (sec->text_start >= func_addr && func_addr < sec->text_end) ||
> + (sec->rodatatext_start >= func_addr && func_addr < sec->rodatatext_end);
> +}
> +#define sframe_func_start_addr_valid sframe_func_start_addr_valid
> +
> +static void arch_init_sframe_table(struct sframe_section *kernel_sfsec)
> +{
> + kernel_sfsec->rodatatext_start = (unsigned long)_srodatatext;
> + kernel_sfsec->rodatatext_end = (unsigned long)_erodatatext;
> +}
> +#define arch_init_sframe_table arch_init_sframe_table
> +
> #endif
>
> #endif /* _ASM_ARM64_UNWIND_SFRAME_H */
> diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
> @@ -213,12 +213,14 @@ SECTIONS
>
> /* code sections that are never executed via the kernel mapping */
> .rodata.text : {
> + _srodatatext = .;
> TRAMP_TEXT
> HIBERNATE_TEXT
> KEXEC_TEXT
> IDMAP_TEXT
> . = ALIGN(PAGE_SIZE);
> }
> + _erodatatext = .;
>
> idmap_pg_dir = .;
> . += PAGE_SIZE;
> diff --git a/include/linux/sframe.h b/include/linux/sframe.h
> @@ -63,6 +63,10 @@ struct sframe_section {
> unsigned long sframe_end;
> unsigned long text_start;
> unsigned long text_end;
> +#if defined(CONFIG_SFRAME_UNWINDER) && defined(CONFIG_ARM64)
> + unsigned long rodatatext_start;
> + unsigned long rodatatext_end;
> +#endif
It looks to me like .rodata.text only exists for vmlinux. I wonder if
in sframe_func_start_addr_valid we can just use the global
_srodatatext and _erodatatext after identifying if an sframe_section
corresponds to vmlinux (kernel_sfsec)? That way we don't need to add
these extra fields.
>
> bool fdes_sorted;
> unsigned long fdes_start;
> diff --git a/kernel/unwind/sframe.c b/kernel/unwind/sframe.c
> @@ -20,11 +20,23 @@
> #include "sframe.h"
> #include "sframe_debug.h"
>
> +#ifndef sframe_func_start_addr_valid
> +static inline bool sframe_func_start_addr_valid(struct sframe_section *sec,
> + unsigned long func_addr)
> +{
> + return (sec->text_start <= func_addr && func_addr < sec->text_end);
> +}
> +#endif
> +
> #ifdef CONFIG_SFRAME_UNWINDER
>
> static bool sframe_init __ro_after_init;
> static struct sframe_section kernel_sfsec __ro_after_init;
>
> +#ifndef arch_init_sframe_table
> +static void arch_init_sframe_table(struct sframe_section *kernel_sfsec) {}
> +#endif
> +
> #endif /* CONFIG_SFRAME_UNWINDER */
>
> struct sframe_fde_internal {
> @@ -152,7 +164,7 @@ static __always_inline int __read_fde(struct sframe_section *sec,
> sizeof(struct sframe_fde_v3), Efault);
>
> func_addr = fde_addr + _fde.func_start_off;
> - if (func_addr < sec->text_start || func_addr > sec->text_end)
> + if (!sframe_func_start_addr_valid(sec, func_addr))
> return -EINVAL;
>
> fda_addr = sec->fres_start + _fde.fres_off;
> @@ -696,13 +708,6 @@ static int sframe_validate_section(struct sframe_section *sec)
> int ret;
>
> ret = safe_read_fde(sec, i, &fde);
> - /*
> - * Code in .rodata.text is not considered part of normal kernel
> - * text, but there is no easy way to prevent sframe data from
> - * being generated for it.
> - */
> - if (ret && sec->sec_type == SFRAME_KERNEL)
> - continue;
> if (ret)
> return ret;
>
> @@ -1031,6 +1036,8 @@ void __init init_sframe_table(void)
> if (WARN_ON(sframe_validate_section(&kernel_sfsec)))
> return;
>
> + arch_init_sframe_table(&kernel_sfsec);
> +
> sframe_init = true;
> }
>
> Regards,
> Jens
> --
> Jens Remus
> Linux on Z Development (D3303)
> jremus@de.ibm.com / jremus@linux.ibm.com
>
> IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
> IBM Data Privacy Statement: https://www.ibm.com/privacy/
>
Thanks,
Dylan
^ permalink raw reply
* RE: [RFC PATCH v5 1/9] media: v4l2-common: Add YUV24 format info
From: Nas Chung @ 2026-04-21 1:45 UTC (permalink / raw)
To: Nicolas Dufresne, mchehab@kernel.org, hverkuil@xs4all.nl,
robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org,
shawnguo@kernel.org, s.hauer@pengutronix.de
Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-imx@nxp.com,
linux-arm-kernel@lists.infradead.org, marek.vasut@mailbox.org,
ming.qian@oss.nxp.com
In-Reply-To: <90e206fad7bef6052fcf38314889e7ff525d3201.camel@collabora.com>
Hi, Nicolas.
>-----Original Message-----
>From: Nicolas Dufresne <nicolas.dufresne@collabora.com>
>Sent: Tuesday, April 21, 2026 12:40 AM
>To: Nas Chung <nas.chung@chipsnmedia.com>; mchehab@kernel.org;
>hverkuil@xs4all.nl; robh@kernel.org; krzk+dt@kernel.org;
>conor+dt@kernel.org; shawnguo@kernel.org; s.hauer@pengutronix.de
>Cc: linux-media@vger.kernel.org; devicetree@vger.kernel.org; linux-
>kernel@vger.kernel.org; linux-imx@nxp.com; linux-arm-
>kernel@lists.infradead.org; marek.vasut@mailbox.org; ming.qian@oss.nxp.com
>Subject: Re: [RFC PATCH v5 1/9] media: v4l2-common: Add YUV24 format info
>
>Le mercredi 15 avril 2026 à 18:25 +0900, Nas Chung a écrit :
>> The YUV24 format is missing an entry in the v4l2_format_info().
>> The YUV24 format is the packed YUV 4:4:4 formats with 8 bits
>> per component.
>>
>> Fixes: 0376a51fbe5e ("media: v4l: Add packed YUV444 24bpp pixel format")
>> Signed-off-by: Nas Chung <nas.chung@chipsnmedia.com>
>> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
>
>Unless you disagree, I might cherry-pick this one. Would it be ok with you ?
Sure, that's fine with me.
Thanks.
Nas.
>
>Nicolas
>
>> ---
>> drivers/media/v4l2-core/v4l2-common.c | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-
>core/v4l2-common.c
>> index 554c591e1113..55bcd5975d9f 100644
>> --- a/drivers/media/v4l2-core/v4l2-common.c
>> +++ b/drivers/media/v4l2-core/v4l2-common.c
>> @@ -281,6 +281,7 @@ const struct v4l2_format_info *v4l2_format_info(u32
>format)
>> { .format = V4L2_PIX_FMT_Y212, .pixel_enc =
>V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0,
>0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
>> { .format = V4L2_PIX_FMT_Y216, .pixel_enc =
>V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0,
>0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
>> { .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc =
>V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0,
>0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
>> + { .format = V4L2_PIX_FMT_YUV24, .pixel_enc =
>V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0,
>0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
>> { .format = V4L2_PIX_FMT_MT2110T, .pixel_enc =
>V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0,
>0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
>> .block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
>> { .format = V4L2_PIX_FMT_MT2110R, .pixel_enc =
>V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0,
>0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
^ permalink raw reply
* Re: [PATCH] arm64: KVM: Initialize vGIC before preempt-disabled section in kvm_reset_vcpu()
From: Deepanshu Kartikey @ 2026-04-21 1:48 UTC (permalink / raw)
To: Marc Zyngier
Cc: oupton, joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas,
will, drjones, christoffer.dall, linux-arm-kernel, kvmarm,
linux-kernel, syzbot+12b178b7c756664d2518
In-Reply-To: <865x5r2dik.wl-maz@kernel.org>
On Thu, Apr 16, 2026 at 7:50 PM Marc Zyngier <maz@kernel.org> wrote:
>
> On Sun, 12 Apr 2026 09:04:37 +0100,
> Deepanshu Kartikey <kartikey406@gmail.com> wrote:
> >
> > kvm_reset_vcpu() calls kvm_timer_vcpu_reset() inside a preempt-disabled
> > section to avoid races with preempt notifiers that also call vcpu put/load.
> >
> > However, kvm_timer_vcpu_reset() eventually calls kvm_vgic_inject_irq()
> > which triggers vgic_lazy_init() if the vGIC has not been initialized yet.
> > vgic_lazy_init() acquires a mutex and calls vgic_init() which invokes
> > synchronize_srcu_expedited() -- both of which may sleep. Sleeping inside
> > a preempt-disabled section is illegal and causes:
> >
> > BUG: scheduling while atomic: syz.1.49/3699/0x00000002
> >
> > Fix this by calling vgic_lazy_init() before preempt_disable(). On the
> > second call inside kvm_vgic_inject_irq(), vgic_initialized() will return
> > true and vgic_lazy_init() will return immediately without sleeping.
> >
>
> I think this really goes in the wrong direction. Forcing the vgic (a
> global resource) to initialise when the vcpu's timer (a local
> resource) is reset feels at best bizarre. Now you are promoting it to
> be forced at vcpu reset. This makes things worse.
>
> You probably want to take a step back and look at *why* we end-up
> here. The core reason seems to be that the timer emulation caches the
> level in a per-timer structure, and tries hard not call into the vgic
> unless the level changes. Which means that unless the vgic is
> initialised and is able to latch that state, the initial pending state
> will not be propagated to the guest.
>
> But do we need this optimisation? I don't think so. Other emulated
> devices don't require it. We can let the vgic know the state of the
> timer at every vcpu entry, just like we do for other virtual
> interrupts that the kernel injects (PMU, vgic MI).
>
> Once you remove the this cache and the need for the vgic to buffer
> things outside of normal execution, you can also drop the magic init
> from the interrupt injection path, because the injection will happen
> on the run path, just like any other PPI.
>
> That'd be a much better approach IMO.
>
> Thanks,
>
> M.
>
> --
> Without deviation from the norm, progress is not possible.
Hi Marc,
Thank you for the detailed feedback! I apologize for the delayed
response — I was away on holiday.
I understand your point. My fix addresses the symptom rather than the
root cause. Forcing vGIC (a global resource) to initialize during
timer (a local resource) reset is not the right approach.
I will take your suggestion and work on:
I will send a v2 once I have something ready.
Thanks again for the guidance!
Best regards,
Deeanshu Kartikey
^ permalink raw reply
* Re: [PATCH] serial: fsl_lpuart: fix rx buffer and DMA map leaks in start_rx_dma
From: Frank Li @ 2026-04-21 2:33 UTC (permalink / raw)
To: Shitalkumar Gandhi
Cc: gregkh, jirislaby, bhuvanchandra.dv, peng.fan, sherry.sun,
linux-serial, imx, linux-arm-kernel, linux-kernel, stable,
Shitalkumar Gandhi
In-Reply-To: <20260420135903.2062024-1-shitalkumar.gandhi@cambiumnetworks.com>
On Mon, Apr 20, 2026 at 07:29:03PM +0530, Shitalkumar Gandhi wrote:
> lpuart_start_rx_dma() allocates sport->rx_ring.buf with kzalloc() and
> then maps a scatterlist via dma_map_sg(). On three subsequent error
> paths the function returns directly without releasing those resources:
>
> - when dma_map_sg() returns 0 (-EINVAL):
> ring->buf is leaked.
> - when dmaengine_slave_config() fails:
> ring->buf and the DMA mapping are leaked.
> - when dmaengine_prep_dma_cyclic() returns NULL:
> ring->buf and the DMA mapping are leaked.
>
> The sole cleanup path, lpuart_dma_rx_free(), is only reached when
> lpuart_dma_rx_use is set, and the caller lpuart_rx_dma_startup() clears
> that flag on failure of lpuart_start_rx_dma(). So these resources are
> permanently leaked on every failure in this function. Repeated port
> open/close or termios changes under error conditions will slowly consume
> memory and leave stale streaming DMA mappings behind.
>
> Fix it by introducing two error labels that unmap the scatterlist and
> free the ring buffer as appropriate. While here, replace the misleading
> -EFAULT (bad userspace pointer) returned when dmaengine_prep_dma_cyclic()
> fails with the more accurate -ENOMEM, matching how other dmaengine users
> in the tree treat this failure.
>
> No functional change on the success path.
>
> Fixes: 5887ad43ee02 ("tty: serial: fsl_lpuart: Use cyclic DMA for Rx")
> Cc: stable@vger.kernel.org
>
> Signed-off-by: Shitalkumar Gandhi <shitalkumar.gandhi@cambiumnetworks.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/tty/serial/fsl_lpuart.c | 15 ++++++++++++---
> 1 file changed, 12 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
> index f36d50fe056f..296a096be351 100644
> --- a/drivers/tty/serial/fsl_lpuart.c
> +++ b/drivers/tty/serial/fsl_lpuart.c
> @@ -1376,7 +1376,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
>
> if (!nent) {
> dev_err(sport->port.dev, "DMA Rx mapping error\n");
> - return -EINVAL;
> + ret = -EINVAL;
> + goto err_free_buf;
> }
>
> dma_rx_sconfig.src_addr = lpuart_dma_datareg_addr(sport);
> @@ -1388,7 +1389,7 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
> if (ret < 0) {
> dev_err(sport->port.dev,
> "DMA Rx slave config failed, err = %d\n", ret);
> - return ret;
> + goto err_unmap_sg;
> }
>
> sport->dma_rx_desc = dmaengine_prep_dma_cyclic(chan,
> @@ -1399,7 +1400,8 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
> DMA_PREP_INTERRUPT);
> if (!sport->dma_rx_desc) {
> dev_err(sport->port.dev, "Cannot prepare cyclic DMA\n");
> - return -EFAULT;
> + ret = -ENOMEM;
> + goto err_unmap_sg;
> }
>
> sport->dma_rx_desc->callback = lpuart_dma_rx_complete;
> @@ -1423,6 +1425,13 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
> }
>
> return 0;
> +
> +err_unmap_sg:
> + dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE);
> +err_free_buf:
> + kfree(ring->buf);
> + ring->buf = NULL;
> + return ret;
> }
>
> static void lpuart_dma_rx_free(struct uart_port *port)
> --
> 2.25.1
>
^ permalink raw reply
* Re: [PATCH V3] dmaengine: imx-sdma: Fix SPBA bus detection on multi-SPBA platforms
From: Frank Li @ 2026-04-21 2:37 UTC (permalink / raw)
To: Shengjiu Wang
Cc: vkoul, Frank.Li, s.hauer, kernel, festevam, dmaengine, imx,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260420100854.2095549-1-shengjiu.wang@nxp.com>
On Mon, Apr 20, 2026 at 06:08:54PM +0800, Shengjiu Wang wrote:
> i.MX8M platforms have multiple SPBA buses under different AIPS buses.
> The current code searches the entire device tree and returns the first
> SPBA bus found, which may not be under the same AIPS bus as the SDMA
> controller.
>
> This breaks SDMA P2P transfers because the SDMA script needs to know
> if peripherals are on SPBA or AIPS to configure watermark levels
> correctly. Using the wrong SPBA bus causes DMA timeouts and transfer
> failures.
>
> Fix by searching for the SPBA bus under the SDMA's parent node (AIPS)
> first, then falling back to a global search for backward compatibility.
>
> Example device tree showing the issue:
> aips1 {
> spba1 { sai@...; }; /* Correct SPBA for sdma1 */
> sdma1@...;
> };
> aips2 {
> spba2 { uart@...; }; /* Wrong SPBA - found first by old code */
> };
>
> Fixes: 8391ecf465ec ("dmaengine: imx-sdma: Add device to device support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> changs in v3:
> - add fallback to a global search for backward compatibility, which is
> to address comments from sashiko.dev
> - update commit subject and commit message
> - add comments in code.
> - add Cc stable tag
> - Don't add Frank's RB on v2 as there are several other changes.
>
> changes in v2:
> - add fixes tag
> - use __free(device_node) for auto release.
>
> drivers/dma/imx-sdma.c | 13 ++++++++++++-
> 1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
> index 3d527883776b..592705af2319 100644
> --- a/drivers/dma/imx-sdma.c
> +++ b/drivers/dma/imx-sdma.c
> @@ -2364,7 +2364,18 @@ static int sdma_probe(struct platform_device *pdev)
> return dev_err_probe(&pdev->dev, ret,
> "failed to register controller\n");
>
> - spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus");
> + /*
> + * On i.MX8M platforms with multiple SPBA buses, we need to find
> + * the SPBA bus that's under the same AIPS bus as this SDMA controller.
> + * First check the SDMA's parent (AIPS bus) for a child SPBA bus.
> + * If not found, fall back to searching the entire device tree for
> + * backward compatibility with older platforms.
> + */
> + struct device_node *sdma_parent_np __free(device_node) = of_get_parent(np);
> +
> + spba_bus = of_get_compatible_child(sdma_parent_np, "fsl,spba-bus");
> + if (!spba_bus)
> + spba_bus = of_find_compatible_node(NULL, NULL, "fsl,spba-bus");
> ret = of_address_to_resource(spba_bus, 0, &spba_res);
> if (!ret) {
> sdma->spba_start_addr = spba_res.start;
> --
> 2.34.1
>
^ permalink raw reply
* [PATCH 6.12.y] wifi: mac80211: always free skb on ieee80211_tx_prepare_skb() failure
From: Li hongliang @ 2026-04-21 2:42 UTC (permalink / raw)
To: gregkh, stable, nbd
Cc: patches, linux-kernel, toke, kvalo, johannes, matthias.bgg,
angelogioacchino.delregno, nbd, linux-wireless, linux-arm-kernel,
linux-mediatek, johannes.berg
From: Felix Fietkau <nbd@nbd.name>
[ Upstream commit d5ad6ab61cbd89afdb60881f6274f74328af3ee9 ]
ieee80211_tx_prepare_skb() has three error paths, but only two of them
free the skb. The first error path (ieee80211_tx_prepare() returning
TX_DROP) does not free it, while invoke_tx_handlers() failure and the
fragmentation check both do.
Add kfree_skb() to the first error path so all three are consistent,
and remove the now-redundant frees in callers (ath9k, mt76,
mac80211_hwsim) to avoid double-free.
Document the skb ownership guarantee in the function's kdoc.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Link: https://patch.msgid.link/20260314065455.2462900-1-nbd@nbd.name
Fixes: 06be6b149f7e ("mac80211: add ieee80211_tx_prepare_skb() helper function")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
[ Exclude changes to drivers/net/wireless/mediatek/mt76/scan.c as this file is first
introduced by commit 31083e38548f("wifi: mt76: add code for emulating hardware scanning")
after linux-6.14.]
Signed-off-by: Li hongliang <1468888505@139.com>
---
drivers/net/wireless/ath/ath9k/channel.c | 6 ++----
drivers/net/wireless/virtual/mac80211_hwsim.c | 1 -
include/net/mac80211.h | 4 +++-
net/mac80211/tx.c | 4 +++-
4 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 571062f2e82a..ba8ec5112afe 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -1011,7 +1011,7 @@ static void ath_scan_send_probe(struct ath_softc *sc,
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
- goto error;
+ return;
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
if (ath_tx_start(sc->hw, skb, &txctl))
@@ -1124,10 +1124,8 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
skb->priority = 7;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
- if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
- dev_kfree_skb_any(skb);
+ if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta))
return false;
- }
break;
default:
return false;
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 8b4fd5fd11b0..e992e59b5918 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -2977,7 +2977,6 @@ static void hw_scan_work(struct work_struct *work)
hwsim->tmp_chan->band,
NULL)) {
rcu_read_unlock();
- kfree_skb(probe);
continue;
}
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 80259a37e724..7d71a4149cdf 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7208,7 +7208,9 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
* @band: the band to transmit on
* @sta: optional pointer to get the station to send the frame to
*
- * Return: %true if the skb was prepared, %false otherwise
+ * Return: %true if the skb was prepared, %false otherwise.
+ * On failure, the skb is freed by this function; callers must not
+ * free it again.
*
* Note: must be called under RCU lock
*/
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 9142d748a6a7..0458cbba232e 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1897,8 +1897,10 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_tx_data tx;
struct sk_buff *skb2;
- if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
+ if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) {
+ kfree_skb(skb);
return false;
+ }
info->band = band;
info->control.vif = vif;
--
2.34.1
^ permalink raw reply related
* [PATCH 6.6.y] wifi: mac80211: always free skb on ieee80211_tx_prepare_skb() failure
From: Li hongliang @ 2026-04-21 2:43 UTC (permalink / raw)
To: gregkh, stable, nbd
Cc: patches, linux-kernel, toke, kvalo, johannes, matthias.bgg,
angelogioacchino.delregno, nbd, linux-wireless, linux-arm-kernel,
linux-mediatek, johannes.berg
From: Felix Fietkau <nbd@nbd.name>
[ Upstream commit d5ad6ab61cbd89afdb60881f6274f74328af3ee9 ]
ieee80211_tx_prepare_skb() has three error paths, but only two of them
free the skb. The first error path (ieee80211_tx_prepare() returning
TX_DROP) does not free it, while invoke_tx_handlers() failure and the
fragmentation check both do.
Add kfree_skb() to the first error path so all three are consistent,
and remove the now-redundant frees in callers (ath9k, mt76,
mac80211_hwsim) to avoid double-free.
Document the skb ownership guarantee in the function's kdoc.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Link: https://patch.msgid.link/20260314065455.2462900-1-nbd@nbd.name
Fixes: 06be6b149f7e ("mac80211: add ieee80211_tx_prepare_skb() helper function")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
[ Exclude changes to drivers/net/wireless/mediatek/mt76/scan.c as this file is first
introduced by commit 31083e38548f("wifi: mt76: add code for emulating hardware scanning")
after linux-6.14.]
Signed-off-by: Li hongliang <1468888505@139.com>
---
drivers/net/wireless/ath/ath9k/channel.c | 6 ++----
drivers/net/wireless/virtual/mac80211_hwsim.c | 1 -
include/net/mac80211.h | 4 ++++
net/mac80211/tx.c | 4 +++-
4 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 571062f2e82a..ba8ec5112afe 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -1011,7 +1011,7 @@ static void ath_scan_send_probe(struct ath_softc *sc,
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
- goto error;
+ return;
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
if (ath_tx_start(sc->hw, skb, &txctl))
@@ -1124,10 +1124,8 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
skb->priority = 7;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
- if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
- dev_kfree_skb_any(skb);
+ if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta))
return false;
- }
break;
default:
return false;
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 1214e7dcc812..bf12ff0ab06a 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -2892,7 +2892,6 @@ static void hw_scan_work(struct work_struct *work)
hwsim->tmp_chan->band,
NULL)) {
rcu_read_unlock();
- kfree_skb(probe);
continue;
}
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index adaa1b2323d2..85d785060e76 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7032,6 +7032,10 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
* @band: the band to transmit on
* @sta: optional pointer to get the station to send the frame to
*
+ * Return: %true if the skb was prepared, %false otherwise.
+ * On failure, the skb is freed by this function; callers must not
+ * free it again.
+ *
* Note: must be called under RCU lock
*/
bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 7eddcb6f9645..2a708132320c 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1911,8 +1911,10 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_tx_data tx;
struct sk_buff *skb2;
- if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
+ if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) {
+ kfree_skb(skb);
return false;
+ }
info->band = band;
info->control.vif = vif;
--
2.34.1
^ permalink raw reply related
* [PATCH 6.1.y] wifi: mac80211: always free skb on ieee80211_tx_prepare_skb() failure
From: Li hongliang @ 2026-04-21 2:43 UTC (permalink / raw)
To: gregkh, stable, nbd
Cc: patches, linux-kernel, toke, kvalo, johannes, matthias.bgg,
angelogioacchino.delregno, nbd, linux-wireless, linux-arm-kernel,
linux-mediatek, johannes.berg
From: Felix Fietkau <nbd@nbd.name>
[ Upstream commit d5ad6ab61cbd89afdb60881f6274f74328af3ee9 ]
ieee80211_tx_prepare_skb() has three error paths, but only two of them
free the skb. The first error path (ieee80211_tx_prepare() returning
TX_DROP) does not free it, while invoke_tx_handlers() failure and the
fragmentation check both do.
Add kfree_skb() to the first error path so all three are consistent,
and remove the now-redundant frees in callers (ath9k, mt76,
mac80211_hwsim) to avoid double-free.
Document the skb ownership guarantee in the function's kdoc.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Link: https://patch.msgid.link/20260314065455.2462900-1-nbd@nbd.name
Fixes: 06be6b149f7e ("mac80211: add ieee80211_tx_prepare_skb() helper function")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
[ Exclude changes to drivers/net/wireless/mediatek/mt76/scan.c as this file is first
introduced by commit 31083e38548f("wifi: mt76: add code for emulating hardware scanning")
after linux-6.14.]
Signed-off-by: Li hongliang <1468888505@139.com>
---
drivers/net/wireless/ath/ath9k/channel.c | 6 ++----
drivers/net/wireless/mac80211_hwsim.c | 1 -
include/net/mac80211.h | 4 ++++
net/mac80211/tx.c | 4 +++-
4 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 571062f2e82a..ba8ec5112afe 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -1011,7 +1011,7 @@ static void ath_scan_send_probe(struct ath_softc *sc,
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
- goto error;
+ return;
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
if (ath_tx_start(sc->hw, skb, &txctl))
@@ -1124,10 +1124,8 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
skb->priority = 7;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
- if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
- dev_kfree_skb_any(skb);
+ if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta))
return false;
- }
break;
default:
return false;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 80a2a668cfb9..316b5f56b6e5 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2743,7 +2743,6 @@ static void hw_scan_work(struct work_struct *work)
hwsim->tmp_chan->band,
NULL)) {
rcu_read_unlock();
- kfree_skb(probe);
continue;
}
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 62e0847d3793..1769d03e6b1d 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -6874,6 +6874,10 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
* @band: the band to transmit on
* @sta: optional pointer to get the station to send the frame to
*
+ * Return: %true if the skb was prepared, %false otherwise.
+ * On failure, the skb is freed by this function; callers must not
+ * free it again.
+ *
* Note: must be called under RCU lock
*/
bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 7333e43dfc35..2e99a1063e93 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1934,8 +1934,10 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_tx_data tx;
struct sk_buff *skb2;
- if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
+ if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) {
+ kfree_skb(skb);
return false;
+ }
info->band = band;
info->control.vif = vif;
--
2.34.1
^ permalink raw reply related
* [PATCH 5.15.y] wifi: mac80211: always free skb on ieee80211_tx_prepare_skb() failure
From: Li hongliang @ 2026-04-21 2:44 UTC (permalink / raw)
To: gregkh, stable, nbd
Cc: patches, linux-kernel, toke, kvalo, johannes, matthias.bgg,
angelogioacchino.delregno, nbd, linux-wireless, linux-arm-kernel,
linux-mediatek, johannes.berg
From: Felix Fietkau <nbd@nbd.name>
[ Upstream commit d5ad6ab61cbd89afdb60881f6274f74328af3ee9 ]
ieee80211_tx_prepare_skb() has three error paths, but only two of them
free the skb. The first error path (ieee80211_tx_prepare() returning
TX_DROP) does not free it, while invoke_tx_handlers() failure and the
fragmentation check both do.
Add kfree_skb() to the first error path so all three are consistent,
and remove the now-redundant frees in callers (ath9k, mt76,
mac80211_hwsim) to avoid double-free.
Document the skb ownership guarantee in the function's kdoc.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Link: https://patch.msgid.link/20260314065455.2462900-1-nbd@nbd.name
Fixes: 06be6b149f7e ("mac80211: add ieee80211_tx_prepare_skb() helper function")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
[ Exclude changes to drivers/net/wireless/mediatek/mt76/scan.c as this file is first
introduced by commit 31083e38548f("wifi: mt76: add code for emulating hardware scanning")
after linux-6.14.]
Signed-off-by: Li hongliang <1468888505@139.com>
---
drivers/net/wireless/ath/ath9k/channel.c | 6 ++----
drivers/net/wireless/mac80211_hwsim.c | 1 -
include/net/mac80211.h | 4 ++++
net/mac80211/tx.c | 4 +++-
4 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
index 6cf087522157..31b7921bf34f 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -1011,7 +1011,7 @@ static void ath_scan_send_probe(struct ath_softc *sc,
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
- goto error;
+ return;
txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
if (ath_tx_start(sc->hw, skb, &txctl))
@@ -1124,10 +1124,8 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
skb->priority = 7;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
- if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
- dev_kfree_skb_any(skb);
+ if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta))
return false;
- }
break;
default:
return false;
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 7d7350258683..ed4d83775fe7 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -2347,7 +2347,6 @@ static void hw_scan_work(struct work_struct *work)
hwsim->tmp_chan->band,
NULL)) {
rcu_read_unlock();
- kfree_skb(probe);
continue;
}
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index f101ef4a1fd6..a4ef9f93a53c 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -6454,6 +6454,10 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif,
* @band: the band to transmit on
* @sta: optional pointer to get the station to send the frame to
*
+ * Return: %true if the skb was prepared, %false otherwise.
+ * On failure, the skb is freed by this function; callers must not
+ * free it again.
+ *
* Note: must be called under RCU lock
*/
bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index a5be5fe5c6b4..054493161376 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1882,8 +1882,10 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
struct ieee80211_tx_data tx;
struct sk_buff *skb2;
- if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP)
+ if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) {
+ kfree_skb(skb);
return false;
+ }
info->band = band;
info->control.vif = vif;
--
2.34.1
^ permalink raw reply related
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