* [PATCH v8 16/29] phy: rockchip: usbdp: Cleanup DP lane selection function
From: Sebastian Reichel @ 2026-06-26 20:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com>
Use FIELD_PREP_WM16() helpers to simplify the DP lane selection
logic.
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 28 +++++++---------------------
1 file changed, 7 insertions(+), 21 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index d8978cd22707..e243d92483e0 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -553,30 +553,16 @@ static void rk_udphy_usb_bvalid_enable(struct rk_udphy *udphy, u8 enable)
static void rk_udphy_dp_lane_select(struct rk_udphy *udphy)
{
const struct rk_udphy_cfg *cfg = udphy->cfgs;
- u32 value = 0;
-
- switch (udphy->dp_lanes) {
- case 4:
- value |= 3 << udphy->dp_lane_sel[3] * 2;
- value |= 2 << udphy->dp_lane_sel[2] * 2;
- fallthrough;
-
- case 2:
- value |= 1 << udphy->dp_lane_sel[1] * 2;
- fallthrough;
+ u32 value = FIELD_PREP_WM16(DP_LANE_SEL_ALL, 0);
+ int i;
- case 1:
- value |= 0 << udphy->dp_lane_sel[0] * 2;
- break;
+ for (i = 0; i < udphy->dp_lanes; i++)
+ value |= field_prep(DP_LANE_SEL_N(udphy->dp_lane_sel[i]), i);
- default:
- break;
- }
+ value |= FIELD_PREP_WM16(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel);
+ value |= FIELD_PREP_WM16(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel);
- regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg,
- ((DP_AUX_DIN_SEL | DP_AUX_DOUT_SEL | DP_LANE_SEL_ALL) << 16) |
- FIELD_PREP(DP_AUX_DIN_SEL, udphy->dp_aux_din_sel) |
- FIELD_PREP(DP_AUX_DOUT_SEL, udphy->dp_aux_dout_sel) | value);
+ regmap_write(udphy->vogrf, cfg->vogrfcfg[udphy->id].dp_lane_reg, value);
}
static void rk_udphy_dp_lane_enable(struct rk_udphy *udphy, int dp_lanes)
--
2.53.0
^ permalink raw reply related
* [PATCH v8 25/29] phy: rockchip: usbdp: Clear USB status on PHY exit
From: Sebastian Reichel @ 2026-06-26 20:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel, Sashiko
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com>
Ensure the USB status flag is cleared when the USB3 PHY is
exited while the system is in DP-only mode. This can happen
if the USB3 controller device is unbound while a DP-only
adapter is plugged into the USB-C port.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260625-rockchip-usbdp-cleanup-v7-24-38eb3cf654fd%40collabora.com
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 4566822d70c4..15c18c6e3093 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1322,8 +1322,10 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy)
udphy->usb_in_use = false;
/* DP only or high-speed */
- if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
+ if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
+ udphy->status &= ~UDPHY_MODE_USB;
return 0;
+ }
rk_udphy_power_off(udphy, UDPHY_MODE_USB);
--
2.53.0
^ permalink raw reply related
* [PATCH v8 19/29] phy: rockchip: usbdp: Rename mode_change to phy_needs_reinit
From: Sebastian Reichel @ 2026-06-26 20:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com>
Right now the mode_change property is set whenever the mode changes
between USB-only, DP-only and USB-DP. It is needed, because on any
mode change the PHY needs to be re-initialized. Apparently at least
DP also requires a re-init when the cable orientation is changed,
which is currently not being done (except when the orientation switch
also involves a mode change). Prepare for this by renaming mode_change
to phy_needs_reinit.
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 731e487db57e..1bb22fc18c9f 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -172,7 +172,7 @@ struct rk_udphy {
/* PHY status management */
bool flip;
- bool mode_change;
+ bool phy_needs_reinit;
u8 mode;
u8 status;
@@ -584,7 +584,7 @@ static void rk_udphy_mode_set(struct rk_udphy *udphy, u8 mode)
if (udphy->mode == mode)
return;
- udphy->mode_change = true;
+ udphy->phy_needs_reinit = true;
udphy->mode = mode;
}
@@ -985,15 +985,15 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
if (udphy->mode & UDPHY_MODE_USB)
rk_udphy_u3_port_disable(udphy, false);
- udphy->mode_change = false;
- } else if (udphy->mode_change) {
+ udphy->phy_needs_reinit = false;
+ } else if (udphy->phy_needs_reinit) {
if (udphy->mode == UDPHY_MODE_DP)
rk_udphy_u3_port_disable(udphy, true);
ret = rk_udphy_init(udphy);
if (ret)
return ret;
- udphy->mode_change = false;
+ udphy->phy_needs_reinit = false;
}
udphy->status |= mode;
--
2.53.0
^ permalink raw reply related
* [PATCH v8 22/29] phy: rockchip: usbdp: Properly handle TYPEC_STATE_SAFE and TYPEC_STATE_USB
From: Sebastian Reichel @ 2026-06-26 20:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel, Sashiko
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com>
Handle TYPEC_STATE_SAFE and TYPEC_STATE_USB Type-C state events,
so that the muxing is properly updated when exiting DP AltMode.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260619155020.CC7361F000E9%40smtp.kernel.org
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index 7c3b0d45d938..63fb41ad0199 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1327,17 +1327,26 @@ static const struct phy_ops rk_udphy_usb3_phy_ops = {
.owner = THIS_MODULE,
};
+static bool rk_udphy_is_supported_mode(struct typec_mux_state *state)
+{
+ /* Handle Safe State and USB State */
+ if (state->mode < TYPEC_STATE_MODAL)
+ return true;
+
+ /* Handle DP AltMode */
+ if (state->alt && state->alt->svid == USB_TYPEC_DP_SID)
+ return true;
+
+ return false;
+}
+
static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
struct rk_udphy *udphy = typec_mux_get_drvdata(mux);
- /*
- * Ignore mux events not involving DP AltMode, because
- * the mode field is being reused, e.g. state->mode == 4
- * could be either TYPEC_MODE_USB4 or TYPEC_DP_STATE_C.
- */
- if (!state->alt || state->alt->svid != USB_TYPEC_DP_SID)
+ /* Ignore mux events not involving USB or DP */
+ if (!rk_udphy_is_supported_mode(state))
return 0;
mutex_lock(&udphy->mutex);
--
2.53.0
^ permalink raw reply related
* [PATCH v8 28/29] phy: rockchip: usbdp: Avoid xHCI SErrors
From: Sebastian Reichel @ 2026-06-26 20:55 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Frank Wang,
Rob Herring, Krzysztof Kozlowski, Conor Dooley
Cc: Andy Yan, Dmitry Baryshkov, Yubing Zhang, Alexey Charkov,
linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel, kernel,
devicetree, Sebastian Reichel
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com>
The USBDP PHY provides the PIPE clock to the USB3 controller, which
means the PHY must be fully running when anything tries to access
the xHCI registers.
When switching between USB3-only, USB3 + DP and DP-only mode, the
PHY must be re-initialized resulting in a short period of the PHY
being disabled. If the DWC3 driver decides to access the xHCI at
this point the system will fail with an SError.
This patch avoids the problems by disabling the USB3 port before
re-initializing it. This does a couple of things:
- forces phystatus to 0 from GRF (not from PHY)
- switches PIPE clock source from PHY to UTMI (safe fallback clock)
- num_u3_port=0
The last part will be ignored, as DWC3 already probed, but the
clock re-routing will avoid the SError. There is a small delay
afterwards to make sure the mux happened. The datasheet gives
no hints how long it takes, so delay time is a guess.
Fixes: 2f70bbddeb45 ("phy: rockchip: add usbdp combo phy driver")
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-usbdp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
index dc166392ba19..7c8b9eaaf352 100644
--- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -1033,8 +1033,8 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
rk_udphy_u3_port_disable(udphy, false);
udphy->phy_needs_reinit = false;
} else if (udphy->phy_needs_reinit) {
- if (udphy->mode == UDPHY_MODE_DP)
- rk_udphy_u3_port_disable(udphy, true);
+ rk_udphy_u3_port_disable(udphy, true);
+ udelay(10);
ret = rk_udphy_init(udphy);
if (ret)
--
2.53.0
^ permalink raw reply related
* Re: [PATCH v8 07/29] phy: rockchip: usbdp: Handle rk_udphy_reset_deassert_all errors in init check
From: sashiko-bot @ 2026-06-26 21:11 UTC (permalink / raw)
To: Sebastian Reichel
Cc: vkoul, neil.armstrong, conor+dt, olteanv, robh, linux-phy,
devicetree
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-7-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [Medium] The return value of `regmap_read` is ignored, leading to the use of an uninitialized stack variable.
- [Medium] The patch fails to address identical reset deassertion vulnerabilities in `rk_udphy_init`, leaving the driver susceptible to the exact same SError it claims to fix.
--
commit af7a83cb1b1746054cd095d5743210c65397d6b1
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Handle rk_udphy_reset_deassert_all errors in init check
This commit fixes a theoretical SError issue by ensuring that the return
value of rk_udphy_reset_deassert_all is checked. It bails out early if
deasserting the reset lines fails before attempting further register access.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/message/20260619154903.2225A1F000E9%40smtp.kernel.org
> Fix theoretical issue, that failure to deassert the reset lines would
> result in an SError in follow-up register access. Note, that this cannot
> happen with current code, since the Rockchip reset driver never returns
> any errors for (de)asserting reset lines.
[Severity: Medium]
This is a pre-existing issue, but does this same vulnerability still exist in
rk_udphy_init() where reset deassertions are also unchecked?
For example:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_init() {
...
rk_udphy_reset_deassert(udphy, "pma_apb");
rk_udphy_reset_deassert(udphy, "pcs_apb");
/* Step 2: set init sequence and phy refclk */
ret = regmap_multi_reg_write(udphy->pma_regmap, ...
...
}
If rk_udphy_reset_deassert() fails here, wouldn't the follow-up
regmap_multi_reg_write() trigger the exact same SError?
Should the reset deassertions in rk_udphy_init() also be checked?
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index f7ff55f03b7cf..b8de0a47a6165 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -936,7 +936,9 @@ static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
> return ret;
> }
>
> - rk_udphy_reset_deassert_all(udphy);
> + ret = rk_udphy_reset_deassert_all(udphy);
> + if (ret)
> + goto exit;
>
> regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
> if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) {
[Severity: Medium]
This is a pre-existing issue, but does this code evaluate an uninitialized
variable if the read fails?
If regmap_read() fails and returns an error, value is not initialized, and
the subsequent FIELD_GET() checks will operate on uninitialized stack memory.
Should the return value of regmap_read() be checked here?
> dev_info(udphy->dev, "Started with DP PHY pre-enabled; seamless takeover unsupported\n");
> rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, false);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=7
^ permalink raw reply
* Re: [PATCH v8 05/29] phy: rockchip: usbdp: Handle missing clock-names DT property gracefully
From: sashiko-bot @ 2026-06-26 21:12 UTC (permalink / raw)
To: Sebastian Reichel
Cc: neil.armstrong, linux-phy, conor+dt, devicetree, vkoul, olteanv,
robh
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-5-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Incomplete fix: NULL pointer dereference on missing `clock-names` device tree property remains unpatched in sibling rockchip PHY drivers.
--
commit 76423f2a5c05c96f96a6a0ac328646bd389ab49c
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Handle missing clock-names DT property gracefully
This commit fixes a NULL pointer dereference in the rockchip usbdp PHY driver
that occurs when the device tree specifies clocks but omits clock-names. It
adds a check to ensure the clock ID string is set before passing it to
strncmp().
Closes: https://sashiko.dev/#/message/20260619154349.071321F000E9%40smtp.kernel.org
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 2845a012eafcc..3fc8222fcaec2 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -432,6 +432,9 @@ static int rk_udphy_clk_init(struct rk_udphy *udphy, struct device *dev)
>
> /* used for configure phy reference clock frequency */
> for (i = 0; i < udphy->num_clks; i++) {
> + if (!udphy->clks[i].id)
> + continue;
> +
[Severity: High]
This is a pre-existing issue, but does this same missing clock-names
vulnerability need to be addressed in sibling rockchip PHY drivers?
Looking at phy-rockchip-inno-usb2.c in rockchip_usb2phy_clk480m_register(),
the same pattern appears:
for (i = 0; i < rphy->num_clks; i++) {
if (!strncmp(rphy->clks[i].id, "phyclk", 6)) {
...
Similarly, in phy-rockchip-naneng-combphy.c in rockchip_combphy_parse_dt():
for (i = 0; i < priv->num_clks; i++) {
if (!strncmp(priv->clks[i].id, "ref", 3)) {
...
If the device tree omits clock-names for these drivers, won't
devm_clk_bulk_get_all() leave the id field as NULL and cause a NULL pointer
dereference during strncmp() just like it did here?
> if (!strncmp(udphy->clks[i].id, "refclk", 6)) {
> udphy->refclk = udphy->clks[i].clk;
> break;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=5
^ permalink raw reply
* Re: [PATCH v8 01/29] dt-bindings: phy: rockchip-usbdp: add improved ports scheme
From: sashiko-bot @ 2026-06-26 21:13 UTC (permalink / raw)
To: Sebastian Reichel
Cc: neil.armstrong, devicetree, olteanv, linux-phy, conor+dt, vkoul,
robh
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-1-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Missing mutual exclusion between `port` and `ports` in device tree schema
--
commit bef609fa8f96018c59387544e50ed1e5818faa7a
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
dt-bindings: phy: rockchip-usbdp: add improved ports scheme
This commit introduces a documented port scheme for the Rockchip USBDP PHY,
modeled after the Qualcomm QMP USB4-USB3-DP PHY controller binding. It adds
a top-level ports node with specific endpoints for USB, DisplayPort, and
SBU connections to properly support USB-C DP AltMode.
> diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
> --- a/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
> +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-usbdp.yaml
> @@ -114,6 +114,29 @@ properties:
> A port node to link the PHY to a TypeC controller for the purpose of
> handling orientation switching.
>
> + ports:
> + $ref: /schemas/graph.yaml#/properties/ports
[Severity: Medium]
Should there be a mutual exclusion constraint between the newly added
ports property and the pre-existing port property?
If a device tree node defines both a top-level port and a ports node
simultaneously, it currently passes schema validation. However, standard
OF graph parsing functions like of_graph_get_next_endpoint() will silently
ignore the top-level port if a ports node is present, creating a shadowing
conflict.
Could this be prevented by adding a oneOf mutual exclusion constraint?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=1
^ permalink raw reply
* Re: [PATCH v8 17/29] phy: rockchip: usbdp: Register DP aux bridge
From: sashiko-bot @ 2026-06-26 21:14 UTC (permalink / raw)
To: Sebastian Reichel
Cc: vkoul, linux-phy, devicetree, conor+dt, robh, olteanv,
neil.armstrong
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-17-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm_aux_bridge_register() is incompatible with the rockchip-usbdp PHY topology, breaking the DP bridge chain when USB and DP ports are split.
--
commit 70d2ba94c5da680bd420eb74b5cffb26d6fad15d
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Register DP aux bridge
This patch adds support to register a DRM auxiliary bridge if the DP
aux channel is connected. This enables DP altmode helper code on
devicetree based platforms.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index e243d92483e09..a204699619b80 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -1505,6 +1507,18 @@ static int rk_udphy_probe(struct platform_device *pdev)
> return ret;
> }
>
> + /*
> + * Only register the DRM bridge, if the DP aux channel is connected.
> + * Some boards use the USBDP PHY only for its USB3 capabilities.
> + */
> + dp_aux_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 3, 0, 0);
> + if (dp_aux_ep) {
> + ret = drm_aux_bridge_register(dev);
[Severity: High]
Since the PHY detects the DP connector on port 3, is the call to
drm_aux_bridge_register() compatible with this topology?
Looking at how the auxiliary bridge driver probes in
drivers/gpu/drm/bridge/aux-bridge.c, it hardcodes the downstream bridge
lookup to port 0:
drm_aux_bridge_probe()
data->next_bridge = devm_drm_of_get_bridge(..., 0, 0);
If port 0 (the USB output) is unconnected or connected to a non-DRM
component, will devm_drm_of_get_bridge() return an error like -ENODEV?
This looks like it would cause the aux bridge probe to fail, breaking the
DP chain and leaving the DP controller waiting indefinitely with
-EPROBE_DEFER, unless both port 0 and port 3 happen to point to the exact
same connector node.
> + fwnode_handle_put(dp_aux_ep);
> + if (ret)
> + return ret;
> + }
> +
> udphy->phy_u3 = devm_phy_create(dev, dev->of_node, &rk_udphy_usb3_phy_ops);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=17
^ permalink raw reply
* Re: [PATCH v8 03/29] phy: rockchip: usbdp: Do not lose USB3 PHY status
From: sashiko-bot @ 2026-06-26 21:16 UTC (permalink / raw)
To: Sebastian Reichel
Cc: robh, conor+dt, linux-phy, devicetree, vkoul, neil.armstrong,
olteanv
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-3-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] USB3 reverse orientation changes are never programmed into the PHY hardware because `rk_udphy_orien_sw_set` updates the software structs but fails to trigger a hardware re-initialization.
--
commit 2e0448207186d0a4562665690cfdad4abd051059
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Do not lose USB3 PHY status
This commit fixes an issue where the PHY status is lost during a DisplayPort
mode change. It ensures that the tracking information for USB3 is preserved
so the PHY isn't inadvertently disabled when DP is toggled off.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index a5ffdf7a6e76b..12dfdd470a782 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -1008,7 +1008,6 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
> rk_udphy_u3_port_disable(udphy, false);
> udphy->mode_change = false;
> } else if (udphy->mode_change) {
> - udphy->status = UDPHY_MODE_NONE;
[Severity: High]
This isn't a bug introduced by this patch, but does keeping the USB mode bit
in the status flag prevent USB3 reverse orientation changes from being
programmed into the PHY hardware?
When a USB-C cable is flipped, the orientation switch handler
rk_udphy_orien_sw_set() updates the software state and requests a re-init:
udphy->phy_needs_reinit = true;
udphy->flip = flipped;
rk_udphy_set_typec_default_mapping(udphy);
Later, rk_udphy_typec_mux_set() evaluates whether to apply this to the
hardware. However, it only calls rk_udphy_power_on() if the USB mode is not
already set in the status flag:
if (... && !(udphy->status & UDPHY_MODE_USB) && ...)
return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
Since DWC3 keeps the PHY enabled permanently and the status flag retains
the USB mode, it appears rk_udphy_power_on() is never invoked to apply the
new lane mapping.
Could this cause the USB3 port to silently fail when plugged in reverse
orientation, since the hardware is never reconfigured?
> if (udphy->mode == UDPHY_MODE_DP)
> rk_udphy_u3_port_disable(udphy, true);
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=3
^ permalink raw reply
* Re: [PATCH v8 06/29] phy: rockchip: usbdp: Drop seamless DP takeover
From: sashiko-bot @ 2026-06-26 21:16 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, olteanv, robh, conor+dt, vkoul, neil.armstrong,
linux-phy
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-6-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Unconditionally disabling the active DP PHY while the upstream display controller is still streaming data causes an AXI bus stall and system hang on Rockchip SoCs.
- [High] Forcing DP HPD to disconnected during probe permanently breaks fixed eDP/DP displays.
--
commit 31addd0a9ff6667916e1a40ca73d061ababa8583
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Drop seamless DP takeover
This commit drops support for seamless DP takeover from the bootloader. If a
pre-enabled DP PHY is detected, it logs a warning, forces HPD low, and
unconditionally disables the PHY.
Link: https://lore.kernel.org/linux-phy/20260612164107.C7DB21F000E9@smtp.kernel.org/
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 3fc8222fcaec2..f7ff55f03b7cf 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -938,10 +939,12 @@ static int rk_udphy_get_initial_status(struct rk_udphy *udphy)
> rk_udphy_reset_deassert_all(udphy);
>
> regmap_read(udphy->pma_regmap, CMN_LANE_MUX_AND_EN_OFFSET, &value);
> - if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value))
> - udphy->status = UDPHY_MODE_DP;
> - else
> - rk_udphy_disable(udphy);
> + if (FIELD_GET(CMN_DP_LANE_MUX_ALL, value) && FIELD_GET(CMN_DP_LANE_EN_ALL, value)) {
> + dev_info(udphy->dev, "Started with DP PHY pre-enabled; seamless takeover unsupported\n");
> + rk_udphy_grfreg_write(udphy->vogrf, &cfg->vogrfcfg[udphy->id].hpd_trigger, false);
[Severity: High]
Will forcing HPD low here permanently break fixed eDP or DP displays?
While this pulls HPD low to signal a disconnect, fixed eDP/DP boards do not
use Type-C muxes and will never trigger a re-assertion via Type-C events.
Since the upstream DP controller driver does not know about the VO GRF
override, the HPD signal remains permanently forced low, and the display
will be seen as disconnected by the kernel.
> + }
> +
> + rk_udphy_disable(udphy);
[Severity: High]
Could calling rk_udphy_disable() synchronously right after forcing HPD low
trigger an AXI bus stall?
If the DP PHY was left enabled by the bootloader, the upstream display
controller (VOP) might still be actively issuing AXI/AHB transactions to the
PHY.
The HPD state change via GRF is asynchronous, but this disables the PHY clocks
immediately on the very next line. Removing the clock from an active AXI slave
often causes the bus to stall indefinitely, resulting in a hard system freeze.
>
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=6
^ permalink raw reply
* Re: [PATCH v8 25/29] phy: rockchip: usbdp: Clear USB status on PHY exit
From: sashiko-bot @ 2026-06-26 21:17 UTC (permalink / raw)
To: Sebastian Reichel
Cc: robh, olteanv, conor+dt, devicetree, vkoul, neil.armstrong,
linux-phy
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-25-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Clearing `udphy->status` and returning early in `rk_udphy_usb3_phy_exit` bypasses hardware disablement when the status drops to 0, leading to a permanent clock enable count leak and keeping the PHY hardware incorrectly powered on.
--
commit 0522ec66e66f7228817b6bf6fcd2ff4639f6af01
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Clear USB status on PHY exit
This commit modifies the USB3 PHY exit routine to ensure the USB status
flag is cleared when the system is in DP-only mode. This addresses a
case where the USB3 controller is unbound while a DP-only adapter is
plugged in.
Closes: https://sashiko.dev/#/message/20260625-rockchip-usbdp-cleanup-v7-24-38eb3cf654fd%40collabora.com
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 4566822d70c4a..15c18c6e30935 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -1322,8 +1322,10 @@ static int rk_udphy_usb3_phy_exit(struct phy *phy)
> udphy->usb_in_use = false;
>
> /* DP only or high-speed */
> - if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs)
> + if (!(udphy->mode & UDPHY_MODE_USB) || udphy->hs) {
> + udphy->status &= ~UDPHY_MODE_USB;
> return 0;
[Severity: Medium]
If udphy->status drops to 0 after this bitwise clear, does returning early
bypass hardware disablement?
It appears this might lead to a permanent clock enable count leak and keep
the PHY hardware incorrectly powered on when a USB3 connection is unbound
or exited while the PHY is in a DP-only mode.
> + }
>
> rk_udphy_power_off(udphy, UDPHY_MODE_USB);
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=25
^ permalink raw reply
* Re: [PATCH v8 13/29] phy: rockchip: usbdp: Limit DP lane count to muxed lanes
From: sashiko-bot @ 2026-06-26 21:17 UTC (permalink / raw)
To: Sebastian Reichel
Cc: olteanv, robh, conor+dt, devicetree, vkoul, neil.armstrong,
linux-phy
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-13-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but the patch's fix is incomplete. The lane count validation added to prevent DP configurations from corrupting USB3 PHY state is bypassed during voltage-only updates. If the Type-C state dynamically reduces the available DP lanes, `udphy->dp_lanes` is updated but the cached `udphy->lanes` remains stale. A subsequent voltage-only update will iterate up to the stale `udphy->lanes` and write DP voltage configurations to the physically reassigned USB3 lanes, corrupting the USB3 PHY state.
--
commit e11a3cd1b181c4fdc9e18a16f2893d3ae7465cb5
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Limit DP lane count to muxed lanes
This commit limits the DisplayPort lane count to 2 if the PHY is
restricted due to USB3 usage. It aims to prevent USB3 state corruption
during voltage configuration.
Closes: https://lore.kernel.org/linux-phy/20260612165546.98E1F1F000E9@smtp.kernel.org/
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 5c38da0dadd22..6de8ac3f91441 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -1132,6 +1132,9 @@ static int rk_udphy_dp_phy_verify_link_rate(struct rk_udphy *udphy,
> static int rk_udphy_dp_phy_verify_lanes(struct rk_udphy *udphy,
> struct phy_configure_opts_dp *dp)
> {
> + if (dp->lanes > udphy->dp_lanes)
> + return -EINVAL;
> +
[Severity: High]
This isn't a bug introduced by this patch, but is the fix incomplete for
voltage-only updates?
If a Type-C event renegotiates the connection and restricts udphy->dp_lanes
to 2, udphy->lanes could still hold a stale value (like 4).
Looking at rk_udphy_dp_phy_configure(), the lane verification is skipped
if dp->set_lanes is false:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_dp_phy_configure() {
...
if (dp->set_lanes) {
ret = rk_udphy_dp_phy_verify_lanes(udphy, dp);
if (ret)
return ret;
}
...
}
If the driver then issues a phy_configure() with set_lanes=false and
set_voltages=true, the voltage loop will iterate using the stale
udphy->lanes value:
drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_dp_phy_configure() {
...
if (dp->set_voltages) {
for (i = 0; i < udphy->lanes; i++) {
lane = udphy->dp_lane_sel[i];
...
}
Could this still result in writing DP voltage configurations to reassigned
USB3 lanes, corrupting the USB3 PHY state?
> switch (dp->lanes) {
> case 1:
> case 2:
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=13
^ permalink raw reply
* Re: [PATCH v8 24/29] phy: rockchip: usbdp: Support going from DP-only mode to USB mode
From: sashiko-bot @ 2026-06-26 21:17 UTC (permalink / raw)
To: Sebastian Reichel
Cc: vkoul, conor+dt, neil.armstrong, robh, olteanv, devicetree,
linux-phy
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-24-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The logic introduced to restore USB functionality fails on subsequent adapter replugs because `udphy->status` permanently retains the `UDPHY_MODE_USB` flag.
--
commit b83098c186ec9041e5338df8dff07f7fa436a60c
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Support going from DP-only mode to USB mode
When a USB-C adapter, which maps all Superspeed lanes to DP is plugged
in, the USB support is disabled in the PHY. When the adapter is
unplugged and a different adapter with USB functionality is plugged in
afterwards, USB functionality is not restored as the USB controller
keeps the PHY enabled for the entire time.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 837a4cb3e4b60..4566822d70c4a 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -1347,6 +1362,17 @@ static int rk_udphy_typec_mux_set(struct typec_mux_dev *mux,
>
> rk_udphy_set_typec_state(udphy, state->mode);
>
> + /*
> + * If the new mode includes USB, but it has not yet been powered
> + * (because the previous mode was DP-only) and the USB PHY was
> + * already initialized by the USB controller, we need to power on
> + * the USB side now since no subsequent phy_init call will come
> + * from the controller.
> + */
> + if ((udphy->mode & UDPHY_MODE_USB) && !(udphy->status & UDPHY_MODE_USB) &&
> + udphy->usb_in_use && !udphy->hs)
> + return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
[Severity: High]
Does this logic handle subsequent replugs correctly if a USB device is plugged
in first?
Since the USB controller keeps the PHY enabled during hotplug (as noted in the
commit message), the UDPHY_MODE_USB flag in udphy->status is not cleared.
If the user then plugs in a DP-only adapter, the PHY is reinitialized for DP
and physically disables the USB3 port.
If the user later unplugs the DP-only adapter and plugs in a USB-only adapter,
won't !(udphy->status & UDPHY_MODE_USB) evaluate to false because the flag was
never cleared? It seems this would skip the call to rk_udphy_power_on() and
leave the USB3 port disabled.
> +
> return 0;
> }
>
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=24
^ permalink raw reply
* Re: [PATCH v8 20/29] phy: rockchip: usbdp: Re-init the PHY on orientation change
From: sashiko-bot @ 2026-06-26 21:18 UTC (permalink / raw)
To: Sebastian Reichel
Cc: linux-phy, vkoul, neil.armstrong, robh, conor+dt, devicetree,
olteanv
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-20-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Hardware lane muxing is never actively updated for USB-only devices upon replug or orientation change, breaking USB SuperSpeed in reverse orientation on ports that do not power-cycle the PHY.
--
commit 52b656ef41c03fa9932c56f88505027225e05d96
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Re-init the PHY on orientation change
This commit updates the driver to record cable orientation changes and set the
phy_needs_reinit flag. It aims to fix DP functionality breaking when the cable
is re-plugged with a swapped orientation.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index 1bb22fc18c9f9..3d222768dc3c9 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -626,6 +626,7 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
> enum typec_orientation orien)
> {
> struct rk_udphy *udphy = typec_switch_get_drvdata(sw);
> + bool flipped = orien == TYPEC_ORIENTATION_REVERSE;
>
> mutex_lock(&udphy->mutex);
>
> @@ -637,7 +638,10 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw,
> goto unlock_ret;
> }
>
> - udphy->flip = orien == TYPEC_ORIENTATION_REVERSE;
> + if (udphy->flip != flipped)
> + udphy->phy_needs_reinit = true;
> +
> + udphy->flip = flipped;
[Severity: High]
This is a pre-existing issue, but does this actually flush the re-initialization
to the hardware if the USB PHY is already running?
When a user replugs a USB-only Type-C device in reverse orientation on a port
where the USB controller does not power-cycle the PHY on unplug, for example in
host mode, rk_udphy_orien_sw_set() notes the orientation change and sets
udphy->phy_needs_reinit = true.
However, because the USB controller keeps the PHY powered on,
udphy->status & UDPHY_MODE_USB remains true.
When rk_udphy_typec_mux_set() is called later to handle the mux event, it
appears to skip actively re-initializing the PHY if the USB mode is already
active:
if ((udphy->mode & UDPHY_MODE_USB) && !(udphy->status & UDPHY_MODE_USB) &&
udphy->usb_in_use && !udphy->hs)
return rk_udphy_power_on(udphy, UDPHY_MODE_USB);
return 0;
Could this leave the hardware stuck in the old orientation state and break USB
SuperSpeed, causing it to fall back to USB 2.0 upon reverse replug?
> rk_udphy_set_typec_default_mapping(udphy);
> rk_udphy_usb_bvalid_enable(udphy, true);
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=20
^ permalink raw reply
* Re: [PATCH v8 12/29] phy: rockchip: usbdp: Support single-lane DP
From: sashiko-bot @ 2026-06-26 21:19 UTC (permalink / raw)
To: Sebastian Reichel
Cc: neil.armstrong, vkoul, robh, conor+dt, devicetree, olteanv,
linux-phy
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-12-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Unclocked PHY register access caused by returning 0 on unsupported mode in `rk_udphy_power_on`.
--
commit 835e45901918a9b24aa9d7ccd7b8538db264146b
Author: Zhang Yubing <yubing.zhang@rock-chips.com>
phy: rockchip: usbdp: Support single-lane DP
Implement support for using just a single DisplayPort line.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
[ ... ]
> @@ -1083,18 +1072,17 @@ static int rk_udphy_dp_phy_exit(struct phy *phy)
> static int rk_udphy_dp_phy_power_on(struct phy *phy)
> {
[ ... ]
> ret = rk_udphy_power_on(udphy, UDPHY_MODE_DP);
> if (ret)
> goto unlock;
>
> - rk_udphy_dplane_enable(udphy, dp_lanes);
> + rk_udphy_dplane_enable(udphy, udphy->dp_lanes);
>
> rk_udphy_dplane_select(udphy);
[Severity: High]
This is a pre-existing issue, but I noticed a potential problem in how
we handle the return value of rk_udphy_power_on() here.
If the DP controller powers on the DP PHY when DP mode is not currently
enabled, such as before Type-C negotiation completes or when forced by
userspace bypassing HPD checks, rk_udphy_power_on() returns 0 for
unsupported modes.
Because the return value is 0, execution proceeds to rk_udphy_dplane_enable()
and rk_udphy_dplane_select(). Can this cause a kernel crash or SError
resulting from an MMIO access to unclocked UDPHY_PMA registers?
Should rk_udphy_power_on() return an error code like -EOPNOTSUPP instead of
0 when the mode is not supported, so the check here can properly abort?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=12
^ permalink raw reply
* Re: [PATCH v8 28/29] phy: rockchip: usbdp: Avoid xHCI SErrors
From: sashiko-bot @ 2026-06-26 21:24 UTC (permalink / raw)
To: Sebastian Reichel
Cc: neil.armstrong, conor+dt, devicetree, robh, linux-phy, olteanv,
vkoul
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-28-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Unconditionally re-enabling the USB3 port during PHY re-initialization overrides the High-Speed only (hs) configuration, potentially leading to an SError on subsequent PHY power off.
--
commit 9d1b08c311cabde1792e9f8ef67ab6b7a5f76005
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
phy: rockchip: usbdp: Avoid xHCI SErrors
This patch avoids xHCI SErrors by disabling the USB3 port before
re-initializing it when switching between USB3-only, USB3 + DP and DP-only
modes.
> diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> index dc166392ba199..7c8b9eaaf3526 100644
> --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c
> +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c
> @@ -1033,8 +1033,8 @@ static int rk_udphy_power_on(struct rk_udphy *udphy, u8 mode)
> rk_udphy_u3_port_disable(udphy, false);
[Severity: High]
This is a pre-existing issue, but does this unconditionally re-enable the
USB3 port and override the High-Speed only (hs) configuration?
If the system is configured with a high-speed only USB device, the PHY
initialization via rk_udphy_usb3_phy_init() correctly disables the USB3 port
to safely route the xHCI PIPE clock to the UTMI fallback.
When a DisplayPort cable is plugged in, rk_udphy_power_on() is called. Since
udphy->mode still contains UDPHY_MODE_USB for combo modes, this code
switches the clock source back to the PHY, overriding the disablement.
When the DP cable is disconnected, rk_udphy_dp_phy_power_off() drops the
status to UDPHY_MODE_NONE and powers off the PHY. At this point, the active
xHCI controller runs without a clock, causing an immediate SError on its
next register access.
Should the re-enable conditions here, and similarly after the PHY
re-initialization further down in this function, check if high-speed only
is disabled?
For example:
if ((udphy->mode & UDPHY_MODE_USB) && !udphy->hs)
> udphy->phy_needs_reinit = false;
> } else if (udphy->phy_needs_reinit) {
> - if (udphy->mode == UDPHY_MODE_DP)
> - rk_udphy_u3_port_disable(udphy, true);
> + rk_udphy_u3_port_disable(udphy, true);
> + udelay(10);
>
> ret = rk_udphy_init(udphy);
> if (ret)
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=28
^ permalink raw reply
* Re: [PATCH v8 29/29] phy: rockchip: usbdp: Disable USB3 on probe
From: sashiko-bot @ 2026-06-26 21:24 UTC (permalink / raw)
To: Sebastian Reichel
Cc: devicetree, linux-phy, vkoul, robh, neil.armstrong, olteanv,
conor+dt
In-Reply-To: <20260626-rockchip-usbdp-cleanup-v8-29-47f682987895@collabora.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but missing USB3 port disable in the power-off path recreates the U-Boot failure state on subsequent initialization, which will now permanently fail due to the removal of the -EPROBE_DEFER retry mechanism.
--
Note: The format of this report has been altered to use a free-form summary
due to strict anti-recitation policies preventing direct quoting of the patch diff.
commit 8c774704e1cc3fa0c1dd6e73117e4641a08efb29
Author: Sebastian Reichel <sebastian.reichel@collabora.com>
Subject: phy: rockchip: usbdp: Disable USB3 on probe
This commit disables USB3 support during probe to start with a clean state,
and removes the -EPROBE_DEFER retry mechanism for cmn ana lcpll lock timeouts
since they should only happen on the first try if U-Boot had enabled USB.
[Severity: High]
This isn't a bug introduced by this patch, but removing the -EPROBE_DEFER
retry mechanism makes a pre-existing power-off issue permanently fatal.
In drivers/phy/rockchip/phy-rockchip-usbdp.c:rk_udphy_power_off(), when the
PHY transitions to UDPHY_MODE_NONE, the code disables the PHY but does not
disable the USB3 port in the GRF:
rk_udphy_power_off()
udphy->status &= ~mode;
if (udphy->status == UDPHY_MODE_NONE)
rk_udphy_disable(udphy);
When the USB subsystem resumes from suspend or driver unbind/bind, it calls
rk_udphy_power_on(). Since udphy->status is UDPHY_MODE_NONE, it calls
rk_udphy_setup(), which executes the PLL lock:
rk_udphy_power_on()
if (udphy->status == UDPHY_MODE_NONE) {
ret = rk_udphy_setup(udphy);
Because the USB3 port was never disabled in the GRF during power-off, this
recreates the exact unclean state that caused the U-Boot lock timeout. Without
the -EPROBE_DEFER retry mechanism removed by this patch, the PLL lock will
time out and result in a permanent failure to resume.
Should rk_udphy_power_off() also disable the USB3 port by calling
rk_udphy_u3_port_disable(udphy, true) when the status reaches UDPHY_MODE_NONE,
so the next power-on starts with a clean state?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626-rockchip-usbdp-cleanup-v8-0-47f682987895@collabora.com?part=29
^ permalink raw reply
* Re: [PATCH 1/1] dt-bindings: mfd: st,stmpe: document st,stmpe610 compatible
From: Linus Walleij @ 2026-06-26 23:05 UTC (permalink / raw)
To: Frank.Li
Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Coquelin, Alexandre Torgue,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:ARM/STM32 ARCHITECTURE,
moderated list:ARM/STM32 ARCHITECTURE, open list, imx, Frank Li
In-Reply-To: <20260626183140.3499179-1-Frank.Li@oss.nxp.com>
Hi Frank,
thanks for your patch!
On Fri, Jun 26, 2026 at 8:31 PM <Frank.Li@oss.nxp.com> wrote:
> From: Frank Li <Frank.Li@nxp.com>
>
> The STMPE610 has long been supported by the STMPE I2C MFD driver, but its
> compatible string was never documented.
>
> Add the missing st,stmpe610 compatible string to fix below CHECK_DTBS
> warnings:
> imx53-m53evk.dtb: /soc/bus@60000000/i2c@63fc4000/touchscreen@41: failed to match any schema with compatible: ['st,stmpe610']
>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
(...)
> compatible:
> enum:
> - st,stmpe601
> + - st,stmpe610
There is no STMPE601.
It's a mis-spelled STMPE610, so please delete the row above and
edit the committ message accordingly.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH v2 2/2] software node: Fix software_node_get_reference_args() with index -1
From: Zijun Hu @ 2026-06-26 23:50 UTC (permalink / raw)
To: Alban Bedel, driver-core, devicetree
Cc: Andy Shevchenko, Daniel Scally, Heikki Krogerus, Sakari Ailus,
Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Rob Herring, Saravana Kannan, linux-kernel, Sashiko
In-Reply-To: <20260618152035.1600436-2-alban.bedel@lht.dlh.de>
On 6/18/2026 11:20 PM, Alban Bedel wrote:
> The bounds check for the index passed to
> software_node_get_reference_args() was failing when passed UINT_MAX,
> this in turn would lead to an out of bound access in the property
> array. Fix the bound check to also cover the UINT_MAX case.
>
> Fixes: 31e4e12e0e960 ("software node: Correct a OOB check in software_node_get_reference_args()")
i think the fix tag may not be right.
for original express before the fix tag: if (index * sizeof(*ref) > prop->length)
for UINT_MAX, multiplication overflow?
> Reported-by: Sashiko <sashiko-bot@kernel.org>
> Closes: https://lore.kernel.org/linux-devicetree/20260611103904.7CB131F00893@smtp.kernel.org/
> Signed-off-by: Alban Bedel <alban.bedel@lht.dlh.de>
> --
> v2: No changes. Only submit this patch along with the patch that
> triggered the Sashiko report, to hopefully avoid another useless
> report.
> ---
> drivers/base/swnode.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
> index 869228a65cb36..2bc76f01eb77d 100644
> --- a/drivers/base/swnode.c
> +++ b/drivers/base/swnode.c
> @@ -537,7 +537,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode,
> if (prop->is_inline)
> return -EINVAL;
>
> - if ((index + 1) * sizeof(*ref) > prop->length)
> + if (index >= prop->length / sizeof(*ref))
> return -ENOENT;
>
who will use UINT_MAX ?
This function is a interface function. the best fix should check
input parameter @index and return -EINVAL if it is not expected?
^ permalink raw reply
* (no subject)
From: Nikhil Gautam @ 2026-06-27 0:58 UTC (permalink / raw)
To: linux-iio
Cc: jic23, dlechner, nuno.sa, andy, u.kleine-koenig, robh, krzk+dt,
conor+dt, devicetree, linux-kernel, Nikhil Gautam
Subject: [PATCH v3 0/2] iio: magnetometer: add support for Melexis MLX90393
Hi,
This series adds initial Industrial I/O subsystem support for the
Melexis MLX90393 3-axis magnetometer and temperature sensor.
The MLX90393 supports both I2C and SPI interfaces. This series
implements support for the I2C interface while keeping the driver
structure transport-independent to simplify future SPI support.
The device uses a command-based communication protocol rather than a
conventional register-addressed interface. A small transport abstraction
layer is therefore used instead of regmap to share the common sensor
logic between the current I2C implementation and future SPI support.
Currently supported features:
* Raw magnetic field measurements for X/Y/Z axes
* Raw temperature measurements
* Configurable gain/scale selection
* Configurable oversampling ratio
* Direct mode operation through the IIO subsystem
* I2C interface support
The driver has been tested on Raspberry Pi 5 hardware using an
MLX90393 sensor connected over I2C. Magnetic field and temperature
measurements were verified through the IIO sysfs interface.
Datasheet:
https://www.melexis.com/en/documents/documentation/datasheets/datasheet-mlx90393
Previous submission:
Link: https://lore.kernel.org/linux-iio/20260510191010.155380-1-nikhilgtr@gmail.com/
Changes in v3:
[DT]
* Made vdd-supply and vddio-supply required properties.
* Moved the MAINTAINERS entry to the correct alphabetical position.
[IIO]
* Added the publicly available MLX90393 datasheet reference.
* Clarified the motivation for introducing a dedicated MLX90393 driver.
* Added the I2C device ID table and switched to designated initializers.
* Added a forward declaration for struct device to reduce unnecessary
header dependencies.
* Added symbol namespaces.
* Improved include usage.
* Improved status validation and consistency of conditional checks.
* Added comments documenting protocol encoding, initialization delays,
and temperature conversion constants derived from the datasheet.
* Improved naming, formatting, indentation and general coding style.
* Addressed all review comments from Andy Shevchenko,
Uwe Kleine-König and Krzysztof Kozlowski.
Changes in v2:
[DT]
* Extended the DT binding to document power supply regulators and
optional interrupt and trigger GPIOs.
[IIO]
* Removed the RFC tag based on reviewer feedback.
* Added a MAINTAINERS entry as part of the initial submission and
expanded it in the driver patch.
* Reworked the scale availability implementation to simplify the
data layout and eliminate the need for constructing a temporary
table, avoiding potential race conditions.
* Replaced usleep_range() with fsleep() where appropriate and
documented initialization delays.
* Simplified helper functions and improved error handling by
returning directly where appropriate.
* Reduced unnecessary local variables and line wrapping to improve
readability and align with kernel coding style.
* Added comments for lock protection and command definitions to
improve code clarity.
* Switched to devm_mutex_init() and cleaned up include usage in
accordance with the "include what you use" principle.
* Improved consistency across the driver, including conditional
handling, switch statements, formatting and general code style.
* Addressed all review comments from Jonathan Cameron.
Many thanks to Jonathan Cameron, Andy Shevchenko,
Uwe Kleine-König and Krzysztof Kozlowski for their detailed reviews
and valuable feedback, which significantly improved this series.
Further review and comments are greatly appreciated.
Thanks,
Nikhil Gautam
Nikhil Gautam (2):
dt-bindings: iio: magnetometer: add Melexis MLX90393
iio: magnetometer: add support for Melexis MLX90393
.../iio/magnetometer/melexis,mlx90393.yaml | 54 ++
MAINTAINERS | 7 +
drivers/iio/magnetometer/Kconfig | 10 +
drivers/iio/magnetometer/Makefile | 2 +
drivers/iio/magnetometer/mlx90393.h | 73 ++
drivers/iio/magnetometer/mlx90393_core.c | 701 ++++++++++++++++++
drivers/iio/magnetometer/mlx90393_i2c.c | 76 ++
7 files changed, 923 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
create mode 100644 drivers/iio/magnetometer/mlx90393.h
create mode 100644 drivers/iio/magnetometer/mlx90393_core.c
create mode 100644 drivers/iio/magnetometer/mlx90393_i2c.c
--
2.39.5
^ permalink raw reply
* [PATCH v3 1/2] dt-bindings: iio: magnetometer: add Melexis MLX90393
From: Nikhil Gautam @ 2026-06-27 0:58 UTC (permalink / raw)
To: linux-iio
Cc: jic23, dlechner, nuno.sa, andy, u.kleine-koenig, robh, krzk+dt,
conor+dt, devicetree, linux-kernel, Nikhil Gautam
In-Reply-To: <20260627005843.7786-1-nikhilgtr@gmail.com>
Add devicetree bindings for the Melexis MLX90393
3-axis magnetometer and temperature sensor.
The device supports magnetic field and temperature
measurements over I2C and SPI interfaces.
This initial binding documents the I2C interface.
Signed-off-by: Nikhil Gautam <nikhilgtr@gmail.com>
---
.../iio/magnetometer/melexis,mlx90393.yaml | 54 +++++++++++++++++++
MAINTAINERS | 6 +++
2 files changed, 60 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
diff --git a/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml b/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
new file mode 100644
index 000000000000..33ab0be66345
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/magnetometer/melexis,mlx90393.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Melexis MLX90393 magnetometer sensor
+
+maintainers:
+ - Nikhil Gautam <nikhilgtr@gmail.com>
+
+description:
+ Melexis MLX90393 3-axis magnetometer and temperature sensor.
+
+properties:
+ compatible:
+ const: melexis,mlx90393
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ trigger-gpios:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - vdd-supply
+ - vddio-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ magnetometer@c {
+ compatible = "melexis,mlx90393";
+ reg = <0x0c>;
+
+ interrupt-parent = <&gpio>;
+ interrupts = <17 IRQ_TYPE_EDGE_RISING>;
+
+ trigger-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index a92290fffa16..469ae3f9aa0f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15501,6 +15501,12 @@ F: Documentation/scsi/megaraid.rst
F: drivers/scsi/megaraid.*
F: drivers/scsi/megaraid/
+MELEXIS MLX90393 MAGNETOMETER DRIVER
+M: Nikhil Gautam <nikhilgtr@gmail.com>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
+
MELEXIS MLX90614 DRIVER
M: Crt Mori <cmo@melexis.com>
L: linux-iio@vger.kernel.org
--
2.39.5
^ permalink raw reply related
* [PATCH v3 2/2] iio: magnetometer: add support for Melexis MLX90393
From: Nikhil Gautam @ 2026-06-27 0:58 UTC (permalink / raw)
To: linux-iio
Cc: jic23, dlechner, nuno.sa, andy, u.kleine-koenig, robh, krzk+dt,
conor+dt, devicetree, linux-kernel, Nikhil Gautam
In-Reply-To: <20260627005843.7786-1-nikhilgtr@gmail.com>
Add Industrial I/O subsystem support for the Melexis
MLX90393 3-axis magnetometer and temperature sensor.
The driver currently supports:
raw magnetic field measurements
raw temperature measurements
configurable gain/scale selection
configurable oversampling ratio
direct mode operation
The MLX90393 supports both I2C and SPI interfaces. This
initial implementation adds support for the I2C interface.
The device uses a command-based communication protocol
rather than a conventional register-addressed interface.
A small transport abstraction layer is therefore used
instead of regmap to share the common sensor logic
between the current I2C implementation and future SPI
support without duplicating code.
Signed-off-by: Nikhil Gautam <nikhilgtr@gmail.com>
---
MAINTAINERS | 1 +
drivers/iio/magnetometer/Kconfig | 10 +
drivers/iio/magnetometer/Makefile | 2 +
drivers/iio/magnetometer/mlx90393.h | 73 +++
drivers/iio/magnetometer/mlx90393_core.c | 701 +++++++++++++++++++++++
drivers/iio/magnetometer/mlx90393_i2c.c | 76 +++
6 files changed, 863 insertions(+)
create mode 100644 drivers/iio/magnetometer/mlx90393.h
create mode 100644 drivers/iio/magnetometer/mlx90393_core.c
create mode 100644 drivers/iio/magnetometer/mlx90393_i2c.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 469ae3f9aa0f..177dda261801 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15506,6 +15506,7 @@ M: Nikhil Gautam <nikhilgtr@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
+F: drivers/iio/magnetometer/mlx90393*
MELEXIS MLX90614 DRIVER
M: Crt Mori <cmo@melexis.com>
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 3debf1320ad1..e6b74e7e3317 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -128,6 +128,16 @@ config HID_SENSOR_MAGNETOMETER_3D
Say yes here to build support for the HID SENSOR
Magnetometer 3D.
+config MLX90393
+ tristate "MELEXIS MLX90393 3-axis magnetometer sensor"
+ depends on I2C
+ help
+ Say yes here to build support for the MELEXIS MLX90393 3-axis
+ magnetometer.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mlx90393.
+
config MMC35240
tristate "MEMSIC MMC35240 3-axis magnetic sensor"
select REGMAP_I2C
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 9297723a97d8..542c89d38a59 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -14,6 +14,8 @@ obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
+obj-$(CONFIG_MLX90393) += mlx90393_core.o
+obj-$(CONFIG_MLX90393) += mlx90393_i2c.o
obj-$(CONFIG_MMC35240) += mmc35240.o
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
diff --git a/drivers/iio/magnetometer/mlx90393.h b/drivers/iio/magnetometer/mlx90393.h
new file mode 100644
index 000000000000..6a646ac05198
--- /dev/null
+++ b/drivers/iio/magnetometer/mlx90393.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * MLX90393 magnetometer & temperature sensor driver
+ *
+ * Copyright (c) 2026 Nikhil Gautam <nikhilgtr@gmail.com>
+ */
+
+#ifndef MLX90393_H
+#define MLX90393_H
+
+#include <linux/bits.h>
+#include <linux/types.h>
+
+#define MLX90393_AXIS_MAX 2
+#define MLX90393_GAIN_MAX 8
+#define MLX90393_RES_MAX 4
+#define MLX90393_OSR2_MAX 4
+#define MLX90393_OSR_MAX 4
+
+#define MLX90393_CMD_MASK GENMASK(7, 4)
+
+/* Commands (datasheet, Table 11 - Command List) */
+#define MLX90393_CMD_SB 0x10 /* Start Burst Mode */
+#define MLX90393_CMD_SW 0x20 /* Start Wake-up on Change Mode */
+#define MLX90393_CMD_SM 0x30 /* Start Single Measurement Mode */
+#define MLX90393_CMD_RM 0x40 /* Read Measurement */
+#define MLX90393_CMD_RR 0x50 /* Read Register */
+#define MLX90393_CMD_WR 0x60 /* Write Register */
+#define MLX90393_CMD_EX 0x80 /* Exit Mode */
+#define MLX90393_CMD_HR 0xD0 /* Memory Recall */
+#define MLX90393_CMD_HS 0xE0 /* Memory Store */
+#define MLX90393_CMD_RT 0xF0 /* Reset Device */
+
+#define MLX90393_MEASURE_Z BIT(0)
+#define MLX90393_MEASURE_Y BIT(1)
+#define MLX90393_MEASURE_X BIT(2)
+#define MLX90393_MEASURE_TEMP BIT(3)
+
+#define MLX90393_MEASURE_ALL (MLX90393_MEASURE_TEMP | \
+ MLX90393_MEASURE_X | MLX90393_MEASURE_Y | MLX90393_MEASURE_Z)
+
+#define MLX90393_NUM_CHANNELS 4
+
+#define MLX90393_STATUS_RESP GENMASK(1, 0)
+#define MLX90393_STATUS_RT BIT(2)
+#define MLX90393_STATUS_ERROR BIT(4)
+
+#define MLX90393_REG_CONF1 0x00
+#define MLX90393_REG_CONF2 0x01
+#define MLX90393_REG_CONF3 0x02
+#define MLX90393_REG_CONF4 0x03
+
+#define MLX90393_CONF1_GAIN_SEL GENMASK(6, 4)
+#define MLX90393_CONF1_HALLCONF GENMASK(3, 0)
+
+#define MLX90393_CONF3_OSR GENMASK(1, 0)
+#define MLX90393_CONF3_DIG_FILT GENMASK(4, 2)
+#define MLX90393_CONF3_RES_X GENMASK(6, 5)
+#define MLX90393_CONF3_RES_Y GENMASK(8, 7)
+#define MLX90393_CONF3_RES_Z GENMASK(10, 9)
+#define MLX90393_CONF3_OSR2 GENMASK(12, 11)
+
+struct device;
+
+struct mlx90393_transfer_ops {
+ int (*xfer)(void *context, const u8 *tx, int tx_len, u8 *rx, int rx_len);
+};
+
+int mlx90393_core_probe(struct device *dev,
+ const struct mlx90393_transfer_ops *ops,
+ void *context);
+
+#endif
diff --git a/drivers/iio/magnetometer/mlx90393_core.c b/drivers/iio/magnetometer/mlx90393_core.c
new file mode 100644
index 000000000000..78fcab8a2b56
--- /dev/null
+++ b/drivers/iio/magnetometer/mlx90393_core.c
@@ -0,0 +1,701 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MLX90393 magnetometer & temperature sensor driver
+ *
+ * Copyright (c) 2026 Nikhil Gautam <nikhilgtr@gmail.com>
+ *
+ * Datasheet: https://media.melexis.com/-/media/files/documents/datasheets/mlx90393-datasheet-melexis.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/time64.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+#include "mlx90393.h"
+
+struct mlx90393_data {
+ /* Protects sensor configuration and measurement operations */
+ struct mutex lock;
+ struct device *dev;
+ void *bus_context;
+ const struct mlx90393_transfer_ops *ops;
+ u8 gain_sel;
+ u8 hallconf;
+
+ u8 res_xy;
+ u8 res_z;
+
+ u8 dig_filt;
+ u8 osr;
+ u8 osr2;
+};
+
+enum mlx90393_channels {
+ MLX90393_CHAN_X,
+ MLX90393_CHAN_Y,
+ MLX90393_CHAN_Z,
+ MLX90393_CHAN_TEMP,
+};
+
+enum mlx90393_axis_type {
+ MLX90393_AXIS_TYPE_XY,
+ MLX90393_AXIS_TYPE_Z,
+};
+
+/* Datasheet: Table no.17 */
+static const int mlx90393_scale_table[MLX90393_AXIS_MAX][MLX90393_GAIN_MAX]
+ [MLX90393_RES_MAX] = {
+ /* XY axis */
+ {
+ { 751, 1502, 3004, 6009},
+ { 601, 1202, 2403, 4840},
+ { 451, 901, 1803, 3605},
+ { 376, 751, 1502, 3004},
+ { 300, 601, 1202, 2403},
+ { 250, 501, 1001, 2003},
+ { 200, 401, 801, 1602},
+ { 150, 300, 601, 1202},
+ },
+ /* Z axis */
+ {
+ { 1210, 2420, 4840, 9680},
+ { 968, 1936, 3872, 7744},
+ { 726, 1452, 2904, 5808},
+ { 605, 1210, 2420, 4840},
+ { 484, 968, 1936, 3872},
+ { 403, 807, 1613, 3227},
+ { 323, 645, 1291, 2581},
+ { 242, 484, 968, 1936},
+ }
+};
+
+static const int mlx90393_osr2_avail[MLX90393_OSR2_MAX] = {
+ 0, 1, 2, 3,
+};
+
+static const int mlx90393_osr_avail[MLX90393_OSR_MAX] = {
+ 1, 2, 4, 8,
+};
+
+#define MLX90393_CHAN(idx, axis, addr) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel = idx, \
+ .address = addr, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),\
+ .info_mask_separate_available = \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+}
+
+static const struct iio_chan_spec mlx90393_channels[] = {
+ MLX90393_CHAN(0, X, MLX90393_CHAN_X),
+ MLX90393_CHAN(1, Y, MLX90393_CHAN_Y),
+ MLX90393_CHAN(2, Z, MLX90393_CHAN_Z),
+ {
+ .type = IIO_TEMP,
+ .address = MLX90393_CHAN_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+/*
+ * Calculate total conversion time in microseconds.
+ *
+ * Formula derived from datasheet timing equations.
+ * Datasheet: Table 8, Page no. 12
+ *
+ * Abbreviations:
+ * TCONVM -> Single Magnetic axis conversion time
+ * TCONVT -> Temperature conversion time
+ * TSTBY -> Time from IDLE to STANDBY
+ * TACTIVE -> Time from STBY to ACTIVE
+ * TCONV_END -> Time to end analog active mode
+ */
+static int mlx90393_get_tconv_us(struct mlx90393_data *data)
+{
+ const int osr = data->osr;
+ const int osr2 = data->osr2;
+ const int df = data->dig_filt;
+
+ int tconvm;
+ int tconvt;
+
+ int m = 3; /* X,Y,Z */
+
+ /*
+ * TCONVM = 67 + 64 * 2^OSR * (2 + 2^DIG_FILT)
+ */
+ tconvm = 67 + (64 * BIT(osr) * (2 + BIT(df)));
+
+ /*
+ * TCONVT = 67 + 192 * 2^OSR2
+ */
+ tconvt = 67 + (192 * BIT(osr2));
+ /*
+ * Total conversion time:
+ * TSTBY + TACTIVE + m * TCONVM + TCONVT + TCONV_END
+ */
+ return 220 + 360 + (m * tconvm) + tconvt + 120;
+}
+
+static int mlx90393_xfer(struct mlx90393_data *data,
+ const u8 *tx, int tx_len,
+ u8 *rx, int rx_len)
+{
+ return data->ops->xfer(data->bus_context, tx, tx_len, rx, rx_len);
+}
+
+static int mlx90393_check_status(u8 cmd, u8 status)
+{
+ /* Datasheet: Table 12: Status byte definition */
+
+ /* Always validate error bit */
+ if (status & MLX90393_STATUS_ERROR)
+ return -EIO;
+
+ switch (cmd & MLX90393_CMD_MASK) {
+ case MLX90393_CMD_RM:
+ /*
+ * D1:D0 indicates response availability
+ * 00 means invalid/no measurement
+ */
+ if ((status & MLX90393_STATUS_RESP) == 0)
+ return -EIO;
+ return 0;
+ case MLX90393_CMD_RT:
+ /* Reset acknowledge */
+ if ((status & MLX90393_STATUS_RT) == 0)
+ return -EIO;
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static int mlx90393_write_cmd(struct mlx90393_data *data, u8 cmd)
+{
+ u8 status;
+ int ret;
+
+ ret = mlx90393_xfer(data, &cmd, 1, &status, 1);
+ if (ret)
+ return ret;
+
+ return mlx90393_check_status(cmd, status);
+}
+
+static int mlx90393_read_cmd(struct mlx90393_data *data, u8 cmd, u8 *rx,
+ int rx_len)
+{
+ int ret;
+
+ ret = mlx90393_xfer(data, &cmd, 1, rx, rx_len);
+ if (ret)
+ return ret;
+
+ return mlx90393_check_status(cmd, rx[0]);
+}
+
+static int mlx90393_read_reg(struct mlx90393_data *data, u8 reg, u16 *val)
+{
+ u8 tx[2];
+ u8 rx[3];
+ int ret;
+
+ tx[0] = MLX90393_CMD_RR;
+ /* Register address is encoded in bits [7:2] */
+ tx[1] = reg << 2;
+
+ ret = mlx90393_xfer(data, tx, sizeof(tx), rx, sizeof(rx));
+ if (ret)
+ return ret;
+
+ ret = mlx90393_check_status(tx[0], rx[0]);
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_be16(&rx[1]);
+
+ return 0;
+}
+
+static int mlx90393_write_reg(struct mlx90393_data *data, u8 reg, u16 val)
+{
+ u8 tx[4];
+ u8 status;
+ int ret;
+
+ tx[0] = MLX90393_CMD_WR;
+ put_unaligned_be16(val, &tx[1]);
+ /* Register address is encoded in bits [7:2] */
+ tx[3] = reg << 2;
+
+ ret = mlx90393_xfer(data, tx, sizeof(tx), &status, 1);
+ if (ret)
+ return ret;
+
+ return mlx90393_check_status(tx[0], status);
+}
+
+static int mlx90393_update_bits(struct mlx90393_data *data, u8 reg,
+ u16 mask, u16 val)
+{
+ u16 reg_val;
+ int ret;
+
+ ret = mlx90393_read_reg(data, reg, ®_val);
+ if (ret)
+ return ret;
+
+ reg_val &= ~mask;
+ reg_val |= (val << __ffs(mask)) & mask;
+
+ return mlx90393_write_reg(data, reg, reg_val);
+}
+
+static int mlx90393_read_measurement(struct mlx90393_data *data,
+ enum mlx90393_channels chan, int *val)
+{
+ u8 rx[9];
+ int ret;
+
+ /* Start measurement */
+ ret = mlx90393_write_cmd(data, MLX90393_CMD_SM | MLX90393_MEASURE_ALL);
+ if (ret)
+ return ret;
+
+ /* Wait conversion */
+ fsleep(mlx90393_get_tconv_us(data));
+
+ /* Read measurement */
+ ret = mlx90393_read_cmd(data, MLX90393_CMD_RM | MLX90393_MEASURE_ALL,
+ rx, sizeof(rx));
+ if (ret)
+ return ret;
+ /*
+ * Measurement response layout:
+ * [status][temp][x][y][z]
+ */
+
+ switch (chan) {
+ case MLX90393_CHAN_TEMP:
+ *val = get_unaligned_be16(&rx[1]);
+ return 0;
+
+ case MLX90393_CHAN_X:
+ *val = sign_extend32(get_unaligned_be16(&rx[3]), 15);
+ return 0;
+
+ case MLX90393_CHAN_Y:
+ *val = sign_extend32(get_unaligned_be16(&rx[5]), 15);
+ return 0;
+
+ case MLX90393_CHAN_Z:
+ *val = sign_extend32(get_unaligned_be16(&rx[7]), 15);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mlx90393_get_scale(struct mlx90393_data *data,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2)
+{
+ enum mlx90393_axis_type axis;
+ u8 res;
+
+ if (chan->channel2 == IIO_MOD_Z) {
+ axis = MLX90393_AXIS_TYPE_Z;
+ res = data->res_z;
+ } else {
+ axis = MLX90393_AXIS_TYPE_XY;
+ res = data->res_xy;
+ }
+
+ *val = 0;
+ *val2 = mlx90393_scale_table[axis][data->gain_sel][res];
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int mlx90393_find_scale(struct mlx90393_data *data, bool z_axis,
+ int val, int val2,
+ int *gain)
+{
+ u8 res;
+ enum mlx90393_axis_type axis;
+
+ if (z_axis) {
+ axis = MLX90393_AXIS_TYPE_Z;
+ res = data->res_z;
+ } else {
+ axis = MLX90393_AXIS_TYPE_XY;
+ res = data->res_xy;
+ }
+
+ if (val != 0)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(mlx90393_scale_table[0]); i++) {
+ if (mlx90393_scale_table[axis][i][res] == val2) {
+ *gain = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mlx90393_set_scale(struct mlx90393_data *data,
+ const struct iio_chan_spec *chan,
+ int val, int val2)
+{
+ bool z_axis;
+ int gain;
+ int ret;
+
+ z_axis = chan->channel2 == IIO_MOD_Z;
+
+ ret = mlx90393_find_scale(data, z_axis, val, val2, &gain);
+ if (ret)
+ return ret;
+
+ ret = mlx90393_update_bits(data, MLX90393_REG_CONF1, MLX90393_CONF1_GAIN_SEL,
+ gain);
+ if (ret)
+ return ret;
+
+ data->gain_sel = gain;
+ return 0;
+}
+
+static int mlx90393_get_osr(struct mlx90393_data *data, int *val)
+{
+ *val = mlx90393_osr_avail[data->osr];
+ return IIO_VAL_INT;
+}
+
+static int mlx90393_find_osr(int val, int *osr)
+{
+ for (unsigned int i = 0; i < MLX90393_OSR_MAX; i++) {
+ if (mlx90393_osr_avail[i] == val) {
+ *osr = i;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mlx90393_get_temp_osr2(struct mlx90393_data *data, int *val)
+{
+ *val = mlx90393_osr2_avail[data->osr2];
+ return IIO_VAL_INT;
+}
+
+static int mlx90393_set_osr(struct mlx90393_data *data, int val)
+{
+ int osr;
+ int ret;
+
+ ret = mlx90393_find_osr(val, &osr);
+ if (ret)
+ return ret;
+
+ if (osr == data->osr)
+ return 0;
+
+ ret = mlx90393_update_bits(data, MLX90393_REG_CONF3, MLX90393_CONF3_OSR,
+ osr);
+ if (ret)
+ return ret;
+
+ data->osr = osr;
+ return 0;
+}
+
+static int mlx90393_set_temp_osr2(struct mlx90393_data *data, int val)
+{
+ int ret;
+
+ if (val < 0 || val >= MLX90393_OSR2_MAX)
+ return -EINVAL;
+
+ if (val == data->osr2)
+ return 0;
+
+ ret = mlx90393_update_bits(data, MLX90393_REG_CONF3, MLX90393_CONF3_OSR2,
+ val);
+ if (ret)
+ return ret;
+
+ data->osr2 = val;
+
+ return 0;
+}
+
+static int mlx90393_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mlx90393_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2,
+ long mask)
+{
+ struct mlx90393_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&data->lock);
+ return mlx90393_set_scale(data, chan, val, val2);
+ }
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ guard(mutex)(&data->lock);
+ switch (chan->type) {
+ case IIO_TEMP:
+ return mlx90393_set_temp_osr2(data, val);
+
+ case IIO_MAGN:
+ return mlx90393_set_osr(data, val);
+
+ default:
+ return -EINVAL;
+ }
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mlx90393_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct mlx90393_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ guard(mutex)(&data->lock);
+ ret = mlx90393_read_measurement(data, chan->address, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_MAGN:
+ return mlx90393_get_scale(data, chan, val, val2);
+
+ case IIO_TEMP:
+ /*
+ * Datasheet Table 7: Thermal Specification
+ */
+ *val = 0;
+ *val2 = 22124;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+ /*
+ * Datasheet Table 7: Thermal Specification
+ */
+
+ *val = -45114;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_TEMP:
+ return mlx90393_get_temp_osr2(data, val);
+ case IIO_MAGN:
+ return mlx90393_get_osr(data, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mlx90393_read_avail(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ const int **vals,
+ int *type,
+ int *length,
+ long mask)
+{
+ struct mlx90393_data *data = iio_priv(indio_dev);
+ static int scale_avail[MLX90393_GAIN_MAX][MLX90393_AXIS_MAX];
+ enum mlx90393_axis_type axis;
+ u8 res;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE: {
+ guard(mutex)(&data->lock);
+ axis = chan->channel2 == IIO_MOD_Z;
+ res = axis ? data->res_z : data->res_xy;
+
+ for (unsigned int i = 0; i < MLX90393_GAIN_MAX; i++) {
+ scale_avail[i][0] = 0;
+ scale_avail[i][1] =
+ mlx90393_scale_table[axis][i][res];
+ }
+
+ *vals = &scale_avail[0][0];
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = MLX90393_GAIN_MAX * MLX90393_AXIS_MAX;
+ return IIO_AVAIL_LIST;
+ }
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (chan->type == IIO_TEMP) {
+ *vals = mlx90393_osr2_avail;
+ *type = IIO_VAL_INT;
+ *length = MLX90393_OSR2_MAX;
+ } else {
+ *vals = mlx90393_osr_avail;
+ *type = IIO_VAL_INT;
+ *length = MLX90393_OSR_MAX;
+ }
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mlx90393_info = {
+ .read_raw = mlx90393_read_raw,
+ .write_raw = mlx90393_write_raw,
+ .read_avail = mlx90393_read_avail,
+ .write_raw_get_fmt = mlx90393_write_raw_get_fmt,
+};
+
+static int mlx90393_init(struct mlx90393_data *data)
+{
+ int ret;
+ u16 reg;
+
+ /* Exit mode */
+ ret = mlx90393_write_cmd(data, MLX90393_CMD_EX);
+ if (ret)
+ return ret;
+
+ /*
+ * Datasheet section 15.4.1.2 (RT command), Figure 16:
+ * Wait 1 ms after EX command before issuing RT.
+ */
+ fsleep(1 * USEC_PER_MSEC);
+
+ /* Reset device */
+ ret = mlx90393_write_cmd(data, MLX90393_CMD_RT);
+ if (ret)
+ return ret;
+
+ /*
+ * Datasheet section 15.4.1.2 (RT command), Figure 16:
+ * Wait 1.5 ms for the start-up sequence to complete.
+ */
+ fsleep(1.5 * USEC_PER_MSEC);
+
+ ret = mlx90393_read_reg(data, MLX90393_REG_CONF1, ®);
+ if (ret)
+ return ret;
+
+ data->gain_sel = FIELD_GET(MLX90393_CONF1_GAIN_SEL, reg);
+ data->hallconf = FIELD_GET(MLX90393_CONF1_HALLCONF, reg);
+
+ ret = mlx90393_read_reg(data, MLX90393_REG_CONF3, ®);
+ if (ret)
+ return ret;
+
+ data->res_xy = FIELD_GET(MLX90393_CONF3_RES_X, reg);
+ data->res_z = FIELD_GET(MLX90393_CONF3_RES_Z, reg);
+ data->dig_filt = FIELD_GET(MLX90393_CONF3_DIG_FILT, reg);
+ data->osr = FIELD_GET(MLX90393_CONF3_OSR, reg);
+ data->osr2 = FIELD_GET(MLX90393_CONF3_OSR2, reg);
+
+ return 0;
+}
+
+int mlx90393_core_probe(struct device *dev,
+ const struct mlx90393_transfer_ops *ops,
+ void *context)
+{
+ struct iio_dev *indio_dev;
+ struct mlx90393_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ data->dev = dev;
+ data->ops = ops;
+ data->bus_context = context;
+
+ indio_dev->name = "mlx90393";
+ indio_dev->info = &mlx90393_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mlx90393_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mlx90393_channels);
+
+ ret = mlx90393_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to initialize device\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(mlx90393_core_probe, "IIO_MLX90393");
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nikhil Gautam <nikhilgtr@gmail.com>");
+MODULE_DESCRIPTION("MLX90393 magnetometer sensor driver");
diff --git a/drivers/iio/magnetometer/mlx90393_i2c.c b/drivers/iio/magnetometer/mlx90393_i2c.c
new file mode 100644
index 000000000000..a9f0a40d15e8
--- /dev/null
+++ b/drivers/iio/magnetometer/mlx90393_i2c.c
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/array_size.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+
+#include "mlx90393.h"
+
+/*
+ * MLX90393 commands use repeated-start transfers where
+ * every command is followed by a status/data response.
+ */
+static int mlx90393_i2c_xfer(void *context,
+ const u8 *tx, int tx_len,
+ u8 *rx, int rx_len)
+{
+ struct i2c_client *client = context;
+ int ret;
+ struct i2c_msg msgs[2] = {
+ [0] = {
+ .addr = client->addr,
+ .len = tx_len,
+ .buf = (u8 *)tx,
+ },
+ [1] = {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = rx_len,
+ .buf = rx,
+ },
+ };
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+ return ret < 0 ? ret : -EIO;
+
+ return 0;
+}
+
+static const struct mlx90393_transfer_ops mlx90393_i2c_ops = {
+ .xfer = mlx90393_i2c_xfer,
+};
+
+static int mlx90393_i2c_probe(struct i2c_client *client)
+{
+ return mlx90393_core_probe(&client->dev, &mlx90393_i2c_ops, client);
+}
+
+static const struct i2c_device_id mlx90393_id[] = {
+ { .name = "mlx90393" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mlx90393_id);
+
+static const struct of_device_id mlx90393_of_match[] = {
+ { .compatible = "melexis,mlx90393" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mlx90393_of_match);
+
+static struct i2c_driver mlx90393_i2c_driver = {
+ .driver = {
+ .name = "mlx90393",
+ .of_match_table = mlx90393_of_match,
+ },
+ .probe = mlx90393_i2c_probe,
+ .id_table = mlx90393_id,
+};
+
+module_i2c_driver(mlx90393_i2c_driver);
+
+MODULE_IMPORT_NS("IIO_MLX90393");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nikhil Gautam <nikhilgtr@gmail.com>");
+MODULE_DESCRIPTION("MLX90393 magnetometer sensor driver");
--
2.39.5
^ permalink raw reply related
* Re: [PATCH v3 1/2] dt-bindings: iio: magnetometer: add Melexis MLX90393
From: sashiko-bot @ 2026-06-27 1:04 UTC (permalink / raw)
To: Nikhil Gautam; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260627005843.7786-2-nikhilgtr@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The required properties `vdd-supply` and `vddio-supply` are omitted from the `properties:` block and missing from the `examples:` node, causing schema validation failures.
--
commit c1fe5f8c39a4b8113c50e154a6674ac296637f70
Author: Nikhil Gautam <nikhilgtr@gmail.com>
dt-bindings: iio: magnetometer: add Melexis MLX90393
Add devicetree bindings for the Melexis MLX90393 3-axis magnetometer and
temperature sensor.
> diff --git a/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml b/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
> new file mode 100644
> index 0000000000000..33ab0be663450
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.yaml
[ ... ]
> +properties:
> + compatible:
> + const: melexis,mlx90393
> +
> + reg:
> + maxItems: 1
> +
> + interrupts:
> + maxItems: 1
> +
> + trigger-gpios:
> + maxItems: 1
> +
> +required:
> + - compatible
> + - reg
> + - vdd-supply
> + - vddio-supply
> +
> +additionalProperties: false
[Severity: Low]
Will this binding fail schema validation when running dt_binding_check?
The vdd-supply and vddio-supply properties are listed as required, but
they are not defined in the properties block.
Because additionalProperties is set to false, wouldn't the schema reject
these supply properties during validation?
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> + #include <dt-bindings/interrupt-controller/irq.h>
> +
> + i2c {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + magnetometer@c {
> + compatible = "melexis,mlx90393";
> + reg = <0x0c>;
> +
> + interrupt-parent = <&gpio>;
> + interrupts = <17 IRQ_TYPE_EDGE_RISING>;
> +
> + trigger-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>;
> + };
> + };
[Severity: Low]
Does this example node need to include the vdd-supply and vddio-supply
properties to pass validation, since they are marked as required above?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260627005843.7786-1-nikhilgtr@gmail.com?part=1
^ permalink raw reply
* Re: [PATCH v3 2/2] iio: magnetometer: add support for Melexis MLX90393
From: sashiko-bot @ 2026-06-27 1:10 UTC (permalink / raw)
To: Nikhil Gautam; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260627005843.7786-3-nikhilgtr@gmail.com>
Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [High] TOCTOU data race in `mlx90393_read_avail` due to static array.
- [High] Stack-allocated buffers passed to I2C transport violate DMA rules.
- [High] `mlx90393_check_status` rejects valid responses for 4-axis reads.
- [Medium] Writing to `in_temp_scale` silently corrupts magnetometer gain configuration.
- [Medium] Incorrect physical units exposed for IIO scales (ABI violation).
- [Medium] Temperature oversampling ratio exposes internal register indices (ABI violation).
- [Low] Use of floating-point literal in `fsleep` duration.
- [Low] Misuse of `MLX90393_AXIS_MAX` for scale array length.
- [Low] Data race on configuration variables in `mlx90393_read_raw`.
--
commit 615b644e2712a4949015c117e64d449d197438d8
Author: Nikhil Gautam <nikhilgtr@gmail.com>
iio: magnetometer: add support for Melexis MLX90393
Add Industrial I/O subsystem support for the Melexis MLX90393 3-axis
magnetometer and temperature sensor.
> diff --git a/drivers/iio/magnetometer/mlx90393_core.c b/drivers/iio/magnetometer/mlx90393_core.c
> index 0000000000000..78fcab8a2b566 100644
> --- /dev/null
> +++ b/drivers/iio/magnetometer/mlx90393_core.c
[ ... ]
> +static const int mlx90393_osr2_avail[MLX90393_OSR2_MAX] = {
> + 0, 1, 2, 3,
> +};
[Severity: Medium]
Should this array expose the physical oversampling ratios (e.g., 1, 2, 4, 8)
rather than the internal register values, to comply with IIO ABI standards?
[ ... ]
> +static int mlx90393_check_status(u8 cmd, u8 status)
> +{
> + /* Datasheet: Table 12: Status byte definition */
> +
> + /* Always validate error bit */
> + if (status & MLX90393_STATUS_ERROR)
> + return -EIO;
> +
> + switch (cmd & MLX90393_CMD_MASK) {
> + case MLX90393_CMD_RM:
> + /*
> + * D1:D0 indicates response availability
> + * 00 means invalid/no measurement
> + */
> + if ((status & MLX90393_STATUS_RESP) == 0)
> + return -EIO;
[Severity: High]
Does this validation break 4-axis reads?
If MLX90393_MEASURE_ALL requests 4 words, could the 2-bit counter overflow
to 00, causing the driver to incorrectly reject valid responses?
[ ... ]
> +static int mlx90393_write_reg(struct mlx90393_data *data, u8 reg, u16 val)
> +{
> + u8 tx[4];
> + u8 status;
> + int ret;
> +
> + tx[0] = MLX90393_CMD_WR;
> + put_unaligned_be16(val, &tx[1]);
> + /* Register address is encoded in bits [7:2] */
> + tx[3] = reg << 2;
> +
> + ret = mlx90393_xfer(data, tx, sizeof(tx), &status, 1);
[Severity: High]
Can passing stack-allocated buffers to the I2C transport cause memory
corruption or a kernel panic?
If the underlying I2C controller uses DMA, mapping stack memory (which might
be CONFIG_VMAP_STACK) is generally not allowed. This also applies to the rx
buffer in mlx90393_read_measurement().
[ ... ]
> +static int mlx90393_get_scale(struct mlx90393_data *data,
> + const struct iio_chan_spec *chan,
> + int *val, int *val2)
> +{
> + enum mlx90393_axis_type axis;
> + u8 res;
> +
> + if (chan->channel2 == IIO_MOD_Z) {
> + axis = MLX90393_AXIS_TYPE_Z;
> + res = data->res_z;
> + } else {
> + axis = MLX90393_AXIS_TYPE_XY;
> + res = data->res_xy;
> + }
> +
> + *val = 0;
> + *val2 = mlx90393_scale_table[axis][data->gain_sel][res];
> +
> + return IIO_VAL_INT_PLUS_NANO;
[Severity: Medium]
Do these return values violate the IIO ABI?
Is the magnetometer scale exposing Tesla (nano scale) instead of Gauss?
[ ... ]
> +static int mlx90393_write_raw(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + int val, int val2,
> + long mask)
> +{
> + struct mlx90393_data *data = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE: {
> + guard(mutex)(&data->lock);
> + return mlx90393_set_scale(data, chan, val, val2);
> + }
[Severity: Medium]
Does this lack of chan->type checking mean writes to the temperature scale
sysfs attribute will fall through to mlx90393_set_scale()?
Could this unintentionally alter the magnetometer gain configuration?
[ ... ]
> +static int mlx90393_read_raw(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + int *val, int *val2, long mask)
> +{
> + struct mlx90393_data *data = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW: {
> + guard(mutex)(&data->lock);
> + ret = mlx90393_read_measurement(data, chan->address, val);
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + }
> + case IIO_CHAN_INFO_SCALE:
> + switch (chan->type) {
> + case IIO_MAGN:
> + return mlx90393_get_scale(data, chan, val, val2);
[Severity: Low]
Does reading driver configuration fields like data->gain_sel in
mlx90393_get_scale() without holding data->lock introduce a data race with
concurrent sysfs writes in mlx90393_write_raw()?
> +
> + case IIO_TEMP:
> + /*
> + * Datasheet Table 7: Thermal Specification
> + */
> + *val = 0;
> + *val2 = 22124;
> + return IIO_VAL_INT_PLUS_MICRO;
[Severity: Medium]
Does this violate the IIO ABI by exposing Celsius instead of milli-Celsius?
[ ... ]
> +static int mlx90393_read_avail(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + const int **vals,
> + int *type,
> + int *length,
> + long mask)
> +{
> + struct mlx90393_data *data = iio_priv(indio_dev);
> + static int scale_avail[MLX90393_GAIN_MAX][MLX90393_AXIS_MAX];
[Severity: High]
Is it safe to populate and return a pointer to a static array here?
Since the IIO core reads this array outside the driver's lock, couldn't a
concurrent sysfs read from another thread or device overwrite the array
contents while it's being read?
[ ... ]
> + *vals = &scale_avail[0][0];
> + *type = IIO_VAL_INT_PLUS_NANO;
> + *length = MLX90393_GAIN_MAX * MLX90393_AXIS_MAX;
[Severity: Low]
Is MLX90393_AXIS_MAX being misused here to calculate the length?
While it evaluates to 2, shouldn't length represent the number of values
(entries * 2 elements per IIO value pair) rather than axis count?
[ ... ]
> +static int mlx90393_init(struct mlx90393_data *data)
> +{
> + int ret;
> + u16 reg;
> +
> + /* Exit mode */
> + ret = mlx90393_write_cmd(data, MLX90393_CMD_EX);
> + if (ret)
> + return ret;
> +
> + /*
> + * Datasheet section 15.4.1.2 (RT command), Figure 16:
> + * Wait 1 ms after EX command before issuing RT.
> + */
> + fsleep(1 * USEC_PER_MSEC);
> +
> + /* Reset device */
> + ret = mlx90393_write_cmd(data, MLX90393_CMD_RT);
> + if (ret)
> + return ret;
> +
> + /*
> + * Datasheet section 15.4.1.2 (RT command), Figure 16:
> + * Wait 1.5 ms for the start-up sequence to complete.
> + */
> + fsleep(1.5 * USEC_PER_MSEC);
[Severity: Low]
Could using a floating-point literal (1.5) cause build or linkage errors
on architectures where hardware floating-point arithmetic is not supported
in the kernel?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260627005843.7786-1-nikhilgtr@gmail.com?part=2
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox