* [PATCH v3 0/3] i3c: dw: Add Altera Agilex5 runtime PM disable quirk
@ 2025-10-31 9:05 adrianhoyin.ng
2025-10-31 9:05 ` [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible adrianhoyin.ng
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: adrianhoyin.ng @ 2025-10-31 9:05 UTC (permalink / raw)
To: alexandre.belloni, Frank.Li, wsa+renesas, robh, krzk+dt, conor+dt,
dinguyen, linux-i3c, devicetree, linux-kernel
Cc: adrianhoyin.ng
From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com>
This patchset adds support for an Altera SoCFPGA-specific quirk
in the Synopsys DesignWare I3C master driver.
While running the I3C compliance test suite on the Altera Agilex5 SoCFPGA,
the I3C bus was observed to hang when a slave device issued an IBI after
the Dynamic Address Assignment (DAA) process completed.
This issue occurs because the controller enters a suspended state after
DAA due to runtime PM being enabled. When suspended, the controller stops
driving the SCL line. As a result, an IBI transfer cannot complete, leaving
the SDA line stuck low and the bus in a hung state.
To address this issue, a new compatible string,
"altr,agilex5-dw-i3c-master", is introduced to identify the
SoC variant. When this compatible string is matched, a new
quirk (DW_I3C_DISABLE_RUNTIME_PM_QUIRK) disables all runtime PM
operations in the driver, ensuring the controller remains active
to handle IBI transactions reliably.
---
changelog:
v2->v3:
*Dropped RFC tag.
*Update compatible string to "altr,agilex5-dw-i3c-master" to match actual SoC.
*Update commit message to describe changes correctly.
v1->v2:
*Add new compatible string in dw i3c dt binding to validate against
newly added compatible string.
*Added new compatible string for altr socfpga platform.
*Remove Kconfig that disables runtime PM added in v1.
*Update implementation to disable runtime PM via compatible string
match so that the implementation can be tied to a specific compatible
string so that it does impact the existing behavior for other users.
See previous patch series at:
https://lore.kernel.org/all/22286d459959f2a153ac59d7da46794c0f495c77.1760579799.git.adrianhoyin.ng@altera.com/
---
Adrian Ng Ho Yin (3):
dt-bindings: i3c: snps: Add Altera SoCFPGA compatible
arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers
i3c: dw: Add runtime PM disable quirk for Altera Agilex5
.../bindings/i3c/snps,dw-i3c-master.yaml | 6 +-
.../arm64/boot/dts/intel/socfpga_agilex5.dtsi | 6 +-
drivers/i3c/master/dw-i3c-master.c | 171 +++++++++++-------
3 files changed, 112 insertions(+), 71 deletions(-)
--
2.49.GIT
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible 2025-10-31 9:05 [PATCH v3 0/3] i3c: dw: Add Altera Agilex5 runtime PM disable quirk adrianhoyin.ng @ 2025-10-31 9:05 ` adrianhoyin.ng 2025-10-31 9:26 ` Krzysztof Kozlowski 2025-10-31 18:36 ` Frank Li 2025-10-31 9:05 ` [PATCH v3 2/3] arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers adrianhoyin.ng 2025-10-31 9:05 ` [PATCH v3 3/3] i3c: dw: Add runtime PM disable quirk for Altera Agilex5 adrianhoyin.ng 2 siblings, 2 replies; 8+ messages in thread From: adrianhoyin.ng @ 2025-10-31 9:05 UTC (permalink / raw) To: alexandre.belloni, Frank.Li, wsa+renesas, robh, krzk+dt, conor+dt, dinguyen, linux-i3c, devicetree, linux-kernel Cc: adrianhoyin.ng From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> Add the "altr,agilex5-dw-i3c-master" compatible string to the Synopsys DesignWare I3C master binding. This allow Agilex5 to use the generic DW I3C master controller while applying any required platform-specific quirks. Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> --- .../devicetree/bindings/i3c/snps,dw-i3c-master.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml b/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml index 5f6467375811..e803457d3f55 100644 --- a/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml +++ b/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml @@ -14,7 +14,11 @@ allOf: properties: compatible: - const: snps,dw-i3c-master-1.00a + oneOf: + - const: snps,dw-i3c-master-1.00a + - items: + - const: altr,agilex5-dw-i3c-master + - const: snps,dw-i3c-master-1.00a reg: maxItems: 1 -- 2.49.GIT ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible 2025-10-31 9:05 ` [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible adrianhoyin.ng @ 2025-10-31 9:26 ` Krzysztof Kozlowski 2025-10-31 18:36 ` Frank Li 1 sibling, 0 replies; 8+ messages in thread From: Krzysztof Kozlowski @ 2025-10-31 9:26 UTC (permalink / raw) To: adrianhoyin.ng, alexandre.belloni, Frank.Li, wsa+renesas, robh, krzk+dt, conor+dt, dinguyen, linux-i3c, devicetree, linux-kernel On 31/10/2025 10:05, adrianhoyin.ng@altera.com wrote: > From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> > > Add the "altr,agilex5-dw-i3c-master" compatible string to the > Synopsys DesignWare I3C master binding. This allow Agilex5 to > use the generic DW I3C master controller while applying any > required platform-specific quirks. > > Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org> Best regards, Krzysztof ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible 2025-10-31 9:05 ` [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible adrianhoyin.ng 2025-10-31 9:26 ` Krzysztof Kozlowski @ 2025-10-31 18:36 ` Frank Li 1 sibling, 0 replies; 8+ messages in thread From: Frank Li @ 2025-10-31 18:36 UTC (permalink / raw) To: adrianhoyin.ng Cc: alexandre.belloni, wsa+renesas, robh, krzk+dt, conor+dt, dinguyen, linux-i3c, devicetree, linux-kernel On Fri, Oct 31, 2025 at 05:05:56PM +0800, adrianhoyin.ng@altera.com wrote: > From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> > > Add the "altr,agilex5-dw-i3c-master" compatible string to the > Synopsys DesignWare I3C master binding. This allow Agilex5 to > use the generic DW I3C master controller while applying any > required platform-specific quirks. > > Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> > --- Reviewed-by: Frank Li <Frank.Li@nxp.com> > .../devicetree/bindings/i3c/snps,dw-i3c-master.yaml | 6 +++++- > 1 file changed, 5 insertions(+), 1 deletion(-) > > diff --git a/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml b/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml > index 5f6467375811..e803457d3f55 100644 > --- a/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml > +++ b/Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml > @@ -14,7 +14,11 @@ allOf: > > properties: > compatible: > - const: snps,dw-i3c-master-1.00a > + oneOf: > + - const: snps,dw-i3c-master-1.00a > + - items: > + - const: altr,agilex5-dw-i3c-master > + - const: snps,dw-i3c-master-1.00a > > reg: > maxItems: 1 > -- > 2.49.GIT > > > -- > linux-i3c mailing list > linux-i3c@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-i3c ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 2/3] arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers 2025-10-31 9:05 [PATCH v3 0/3] i3c: dw: Add Altera Agilex5 runtime PM disable quirk adrianhoyin.ng 2025-10-31 9:05 ` [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible adrianhoyin.ng @ 2025-10-31 9:05 ` adrianhoyin.ng 2025-10-31 18:44 ` Frank Li 2025-10-31 9:05 ` [PATCH v3 3/3] i3c: dw: Add runtime PM disable quirk for Altera Agilex5 adrianhoyin.ng 2 siblings, 1 reply; 8+ messages in thread From: adrianhoyin.ng @ 2025-10-31 9:05 UTC (permalink / raw) To: alexandre.belloni, Frank.Li, wsa+renesas, robh, krzk+dt, conor+dt, dinguyen, linux-i3c, devicetree, linux-kernel Cc: adrianhoyin.ng From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> Add the "altr,agilex5-dw-i3c-master" compatible string to the I3C controller nodes on the Agilex5 SoCFPGA platform. This allows the platform to use the generic Synopsys DW I3C master driver while enabling platform-specific quirks or configurations associated with Altera SoCFPGA devices. Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> --- arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi index 04e99cd7e74b..c494b3bbb5e9 100644 --- a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi +++ b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi @@ -203,7 +203,8 @@ i2c4: i2c@10c02c00 { }; i3c0: i3c@10da0000 { - compatible = "snps,dw-i3c-master-1.00a"; + compatible = "altr,agilex5-dw-i3c-master", + "snps,dw-i3c-master-1.00a"; reg = <0x10da0000 0x1000>; #address-cells = <3>; #size-cells = <0>; @@ -213,7 +214,8 @@ i3c0: i3c@10da0000 { }; i3c1: i3c@10da1000 { - compatible = "snps,dw-i3c-master-1.00a"; + compatible = "altr,agilex5-dw-i3c-master", + "snps,dw-i3c-master-1.00a"; reg = <0x10da1000 0x1000>; #address-cells = <3>; #size-cells = <0>; -- 2.49.GIT ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 2/3] arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers 2025-10-31 9:05 ` [PATCH v3 2/3] arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers adrianhoyin.ng @ 2025-10-31 18:44 ` Frank Li 0 siblings, 0 replies; 8+ messages in thread From: Frank Li @ 2025-10-31 18:44 UTC (permalink / raw) To: adrianhoyin.ng Cc: alexandre.belloni, wsa+renesas, robh, krzk+dt, conor+dt, dinguyen, linux-i3c, devicetree, linux-kernel On Fri, Oct 31, 2025 at 05:05:57PM +0800, adrianhoyin.ng@altera.com wrote: > From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> > > Add the "altr,agilex5-dw-i3c-master" compatible string to the > I3C controller nodes on the Agilex5 SoCFPGA platform. Suppose this should be enough. the below context is common sense. > This allows > the platform to use the generic Synopsys DW I3C master driver while > enabling platform-specific quirks or configurations associated with > Altera SoCFPGA devices. > > Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> > --- > arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi | 6 ++++-- > 1 file changed, 4 insertions(+), 2 deletions(-) > > diff --git a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi > index 04e99cd7e74b..c494b3bbb5e9 100644 > --- a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi > +++ b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi > @@ -203,7 +203,8 @@ i2c4: i2c@10c02c00 { > }; > > i3c0: i3c@10da0000 { > - compatible = "snps,dw-i3c-master-1.00a"; > + compatible = "altr,agilex5-dw-i3c-master", > + "snps,dw-i3c-master-1.00a"; Need align to previous line Frank > reg = <0x10da0000 0x1000>; > #address-cells = <3>; > #size-cells = <0>; > @@ -213,7 +214,8 @@ i3c0: i3c@10da0000 { > }; > > i3c1: i3c@10da1000 { > - compatible = "snps,dw-i3c-master-1.00a"; > + compatible = "altr,agilex5-dw-i3c-master", > + "snps,dw-i3c-master-1.00a"; > reg = <0x10da1000 0x1000>; > #address-cells = <3>; > #size-cells = <0>; > -- > 2.49.GIT > ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v3 3/3] i3c: dw: Add runtime PM disable quirk for Altera Agilex5 2025-10-31 9:05 [PATCH v3 0/3] i3c: dw: Add Altera Agilex5 runtime PM disable quirk adrianhoyin.ng 2025-10-31 9:05 ` [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible adrianhoyin.ng 2025-10-31 9:05 ` [PATCH v3 2/3] arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers adrianhoyin.ng @ 2025-10-31 9:05 ` adrianhoyin.ng 2025-10-31 18:43 ` Frank Li 2 siblings, 1 reply; 8+ messages in thread From: adrianhoyin.ng @ 2025-10-31 9:05 UTC (permalink / raw) To: alexandre.belloni, Frank.Li, wsa+renesas, robh, krzk+dt, conor+dt, dinguyen, linux-i3c, devicetree, linux-kernel Cc: adrianhoyin.ng From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> Add support for disabling runtime power management (PM) in the Synopsys DesignWare I3C master driver through a new quirk flag, DW_I3C_DISABLE_RUNTIME_PM_QUIRK. When this quirk is active, the driver skips all runtime PM operations (e.g. pm_runtime_enable(), pm_runtime_get(), and pm_runtime_put()), keeping the controller continuously powered. This change addresses a reliability issue observed on Altera Agilex5 where the i3c bus enters a hung state when a slave device tries to send an IBI while the controller is in a suspended state which happens after the DAA procedures. Once suspended, the controller no longer drives the SCL line, causing the SDA line to remain low and leaving the I3C bus in a hung state when a slave device tries to send an IBI. Disabling runtime PM ensures the controller remains active, allowing IBI transactions to complete correctly. A new compatible string, "altr,agilex5-dw-i3c-master", is added to represent the SoC-specific variant of the DesignWare I3C controller, which requires runtime PM to remain disabled. Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> --- drivers/i3c/master/dw-i3c-master.c | 171 +++++++++++++++++------------ 1 file changed, 103 insertions(+), 68 deletions(-) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 9ceedf09c3b6..97da2b17b5aa 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -228,6 +228,7 @@ /* List of quirks */ #define AMD_I3C_OD_PP_TIMING BIT(1) +#define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2) struct dw_i3c_cmd { u32 cmd_lo; @@ -635,12 +636,14 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) struct i3c_device_info info = { }; int ret; - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, ret); - return ret; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return ret; + } } ret = master->platform_ops->init(master); @@ -682,7 +685,8 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) dw_i3c_master_enable(master); rpm_out: - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); return ret; } @@ -798,12 +802,14 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); } - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, ret); - return ret; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return ret; + } } if (ccc->rnw) @@ -811,7 +817,8 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, else ret = dw_i3c_ccc_set(master, ccc); - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); return ret; } @@ -824,12 +831,14 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) u8 last_addr = 0; int ret, pos; - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, ret); - return ret; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return ret; + } } olddevs = ~(master->free_pos); @@ -893,7 +902,8 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) dw_i3c_master_free_xfer(xfer); rpm_out: - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); return ret; } @@ -929,12 +939,14 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev, if (!xfer) return -ENOMEM; - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, ret); - return ret; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return ret; + } } for (i = 0; i < i3c_nxfers; i++) { @@ -978,7 +990,8 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev, ret = xfer->ret; dw_i3c_master_free_xfer(xfer); - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); return ret; } @@ -1089,12 +1102,14 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, if (!xfer) return -ENOMEM; - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, ret); - return ret; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return ret; + } } for (i = 0; i < i2c_nxfers; i++) { @@ -1127,7 +1142,8 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, ret = xfer->ret; dw_i3c_master_free_xfer(xfer); - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); return ret; } @@ -1272,12 +1288,14 @@ static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m) struct dw_i3c_master *master = to_dw_i3c_master(m); int ret; - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, ret); - return ret; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return ret; + } } dw_i3c_master_enable_sir_signal(master, true); @@ -1294,7 +1312,8 @@ static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m) writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK, master->regs + DEVICE_CTRL); - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); return 0; } @@ -1305,12 +1324,14 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev) struct dw_i3c_master *master = to_dw_i3c_master(m); int rc; - rc = pm_runtime_resume_and_get(master->dev); - if (rc < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, rc); - return rc; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + rc = pm_runtime_resume_and_get(master->dev); + if (rc < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, rc); + return rc; + } } dw_i3c_master_set_sir_enabled(master, dev, data->index, true); @@ -1319,7 +1340,8 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev) if (rc) { dw_i3c_master_set_sir_enabled(master, dev, data->index, false); - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); } return rc; @@ -1338,7 +1360,8 @@ static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev) dw_i3c_master_set_sir_enabled(master, dev, data->index, false); - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); return 0; } @@ -1573,11 +1596,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, platform_set_drvdata(pdev, master); - pm_runtime_set_autosuspend_delay(&pdev->dev, RPM_AUTOSUSPEND_TIMEOUT); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); - /* Information regarding the FIFOs/QUEUEs depth */ ret = readl(master->regs + QUEUE_STATUS_LEVEL); master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret); @@ -1592,6 +1610,13 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->quirks = (unsigned long)device_get_match_data(&pdev->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + pm_runtime_set_autosuspend_delay(&pdev->dev, RPM_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + } + INIT_WORK(&master->hj_work, dw_i3c_hj_work); ret = i3c_master_register(&master->base, &pdev->dev, &dw_mipi_i3c_ops, false); @@ -1601,9 +1626,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, return 0; err_disable_pm: - pm_runtime_disable(&pdev->dev); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_dont_use_autosuspend(&pdev->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_dont_use_autosuspend(&pdev->dev); + } err_assert_rst: reset_control_assert(master->core_rst); @@ -1617,9 +1644,11 @@ void dw_i3c_common_remove(struct dw_i3c_master *master) cancel_work_sync(&master->hj_work); i3c_master_unregister(&master->base); - pm_runtime_disable(master->dev); - pm_runtime_set_suspended(master->dev); - pm_runtime_dont_use_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + pm_runtime_disable(master->dev); + pm_runtime_set_suspended(master->dev); + pm_runtime_dont_use_autosuspend(master->dev); + } } EXPORT_SYMBOL_GPL(dw_i3c_common_remove); @@ -1742,12 +1771,14 @@ static void dw_i3c_shutdown(struct platform_device *pdev) struct dw_i3c_master *master = platform_get_drvdata(pdev); int ret; - ret = pm_runtime_resume_and_get(master->dev); - if (ret < 0) { - dev_err(master->dev, - "<%s> cannot resume i3c bus master, err: %d\n", - __func__, ret); - return; + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return; + } } cancel_work_sync(&master->hj_work); @@ -1756,11 +1787,15 @@ static void dw_i3c_shutdown(struct platform_device *pdev) writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); writel((u32)~INTR_ALL, master->regs + INTR_SIGNAL_EN); - pm_runtime_put_autosuspend(master->dev); + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) + pm_runtime_put_autosuspend(master->dev); } static const struct of_device_id dw_i3c_master_of_match[] = { { .compatible = "snps,dw-i3c-master-1.00a", }, + { .compatible = "altr,agilex5-dw-i3c-master", + .data = (void *)DW_I3C_DISABLE_RUNTIME_PM_QUIRK, + }, {}, }; MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match); -- 2.49.GIT ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v3 3/3] i3c: dw: Add runtime PM disable quirk for Altera Agilex5 2025-10-31 9:05 ` [PATCH v3 3/3] i3c: dw: Add runtime PM disable quirk for Altera Agilex5 adrianhoyin.ng @ 2025-10-31 18:43 ` Frank Li 0 siblings, 0 replies; 8+ messages in thread From: Frank Li @ 2025-10-31 18:43 UTC (permalink / raw) To: adrianhoyin.ng Cc: alexandre.belloni, wsa+renesas, robh, krzk+dt, conor+dt, dinguyen, linux-i3c, devicetree, linux-kernel On Fri, Oct 31, 2025 at 05:05:58PM +0800, adrianhoyin.ng@altera.com wrote: > From: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> > > Add support for disabling runtime power management (PM) in the > Synopsys DesignWare I3C master driver through a new quirk flag, > DW_I3C_DISABLE_RUNTIME_PM_QUIRK. When this quirk is active, > the driver skips all runtime PM operations (e.g. pm_runtime_enable(), > pm_runtime_get(), and pm_runtime_put()), keeping the controller > continuously powered. > > This change addresses a reliability issue observed on Altera Agilex5 > where the i3c bus enters a hung state when a slave device tries to send > an IBI while the controller is in a suspended state which happens after > the DAA procedures. Once suspended, the controller no longer drives the > SCL line, causing the SDA line to remain low and leaving the I3C bus in > a hung state when a slave device tries to send an IBI. Disabling runtime > PM ensures the controller remains active, allowing IBI transactions to > complete correctly. > > A new compatible string, "altr,agilex5-dw-i3c-master", is added to > represent the SoC-specific variant of the DesignWare I3C controller, > which requires runtime PM to remain disabled. > > Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com> > --- > drivers/i3c/master/dw-i3c-master.c | 171 +++++++++++++++++------------ > 1 file changed, 103 insertions(+), 68 deletions(-) > > diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c > index 9ceedf09c3b6..97da2b17b5aa 100644 > --- a/drivers/i3c/master/dw-i3c-master.c > +++ b/drivers/i3c/master/dw-i3c-master.c > @@ -228,6 +228,7 @@ > > /* List of quirks */ > #define AMD_I3C_OD_PP_TIMING BIT(1) > +#define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2) > > struct dw_i3c_cmd { > u32 cmd_lo; > @@ -635,12 +636,14 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) > struct i3c_device_info info = { }; > int ret; > > - ret = pm_runtime_resume_and_get(master->dev); > - if (ret < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, ret); > - return ret; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + ret = pm_runtime_resume_and_get(master->dev); > + if (ret < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, ret); > + return ret; > + } > } > > ret = master->platform_ops->init(master); > @@ -682,7 +685,8 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) > dw_i3c_master_enable(master); > > rpm_out: > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > return ret; > } > > @@ -798,12 +802,14 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, > writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); > } > > - ret = pm_runtime_resume_and_get(master->dev); > - if (ret < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, ret); > - return ret; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + ret = pm_runtime_resume_and_get(master->dev); > + if (ret < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, ret); > + return ret; > + } > } > > if (ccc->rnw) > @@ -811,7 +817,8 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, > else > ret = dw_i3c_ccc_set(master, ccc); > > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > return ret; > } > > @@ -824,12 +831,14 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) > u8 last_addr = 0; > int ret, pos; > > - ret = pm_runtime_resume_and_get(master->dev); > - if (ret < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, ret); > - return ret; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + ret = pm_runtime_resume_and_get(master->dev); > + if (ret < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, ret); > + return ret; > + } > } > > olddevs = ~(master->free_pos); > @@ -893,7 +902,8 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) > dw_i3c_master_free_xfer(xfer); > > rpm_out: > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > return ret; > } > > @@ -929,12 +939,14 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev, > if (!xfer) > return -ENOMEM; > > - ret = pm_runtime_resume_and_get(master->dev); > - if (ret < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, ret); > - return ret; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + ret = pm_runtime_resume_and_get(master->dev); > + if (ret < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, ret); > + return ret; > + } > } > > for (i = 0; i < i3c_nxfers; i++) { > @@ -978,7 +990,8 @@ static int dw_i3c_master_priv_xfers(struct i3c_dev_desc *dev, > ret = xfer->ret; > dw_i3c_master_free_xfer(xfer); > > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > return ret; > } > > @@ -1089,12 +1102,14 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, > if (!xfer) > return -ENOMEM; > > - ret = pm_runtime_resume_and_get(master->dev); > - if (ret < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, ret); > - return ret; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + ret = pm_runtime_resume_and_get(master->dev); > + if (ret < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, ret); > + return ret; > + } > } > > for (i = 0; i < i2c_nxfers; i++) { > @@ -1127,7 +1142,8 @@ static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev, > ret = xfer->ret; > dw_i3c_master_free_xfer(xfer); > > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > return ret; > } > > @@ -1272,12 +1288,14 @@ static int dw_i3c_master_enable_hotjoin(struct i3c_master_controller *m) > struct dw_i3c_master *master = to_dw_i3c_master(m); > int ret; > > - ret = pm_runtime_resume_and_get(master->dev); > - if (ret < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, ret); > - return ret; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + ret = pm_runtime_resume_and_get(master->dev); > + if (ret < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, ret); > + return ret; > + } > } > > dw_i3c_master_enable_sir_signal(master, true); > @@ -1294,7 +1312,8 @@ static int dw_i3c_master_disable_hotjoin(struct i3c_master_controller *m) > writel(readl(master->regs + DEVICE_CTRL) | DEV_CTRL_HOT_JOIN_NACK, > master->regs + DEVICE_CTRL); > > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > return 0; > } > > @@ -1305,12 +1324,14 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev) > struct dw_i3c_master *master = to_dw_i3c_master(m); > int rc; > > - rc = pm_runtime_resume_and_get(master->dev); > - if (rc < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, rc); > - return rc; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + rc = pm_runtime_resume_and_get(master->dev); > + if (rc < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, rc); > + return rc; > + } > } > > dw_i3c_master_set_sir_enabled(master, dev, data->index, true); > @@ -1319,7 +1340,8 @@ static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev) > > if (rc) { > dw_i3c_master_set_sir_enabled(master, dev, data->index, false); > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > } > > return rc; > @@ -1338,7 +1360,8 @@ static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev) > > dw_i3c_master_set_sir_enabled(master, dev, data->index, false); > > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > return 0; > } > > @@ -1573,11 +1596,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, > > platform_set_drvdata(pdev, master); > > - pm_runtime_set_autosuspend_delay(&pdev->dev, RPM_AUTOSUSPEND_TIMEOUT); > - pm_runtime_use_autosuspend(&pdev->dev); > - pm_runtime_set_active(&pdev->dev); > - pm_runtime_enable(&pdev->dev); > - > /* Information regarding the FIFOs/QUEUEs depth */ > ret = readl(master->regs + QUEUE_STATUS_LEVEL); > master->caps.cmdfifodepth = QUEUE_STATUS_LEVEL_CMD(ret); > @@ -1592,6 +1610,13 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, > > master->quirks = (unsigned long)device_get_match_data(&pdev->dev); > > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + pm_runtime_set_autosuspend_delay(&pdev->dev, RPM_AUTOSUSPEND_TIMEOUT); > + pm_runtime_use_autosuspend(&pdev->dev); > + pm_runtime_set_active(&pdev->dev); > + pm_runtime_enable(&pdev->dev); > + } > + I think you just need increase runtime pm refer counter should be enough. if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) pm_runtime_get_noresume(dev); So all runtime_pm function will no be called because ref counter always bigger than 0. Frank > INIT_WORK(&master->hj_work, dw_i3c_hj_work); > ret = i3c_master_register(&master->base, &pdev->dev, > &dw_mipi_i3c_ops, false); > @@ -1601,9 +1626,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, > return 0; > > err_disable_pm: > - pm_runtime_disable(&pdev->dev); > - pm_runtime_set_suspended(&pdev->dev); > - pm_runtime_dont_use_autosuspend(&pdev->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + pm_runtime_disable(&pdev->dev); > + pm_runtime_set_suspended(&pdev->dev); > + pm_runtime_dont_use_autosuspend(&pdev->dev); > + } > > err_assert_rst: > reset_control_assert(master->core_rst); > @@ -1617,9 +1644,11 @@ void dw_i3c_common_remove(struct dw_i3c_master *master) > cancel_work_sync(&master->hj_work); > i3c_master_unregister(&master->base); > > - pm_runtime_disable(master->dev); > - pm_runtime_set_suspended(master->dev); > - pm_runtime_dont_use_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + pm_runtime_disable(master->dev); > + pm_runtime_set_suspended(master->dev); > + pm_runtime_dont_use_autosuspend(master->dev); > + } > } > EXPORT_SYMBOL_GPL(dw_i3c_common_remove); > > @@ -1742,12 +1771,14 @@ static void dw_i3c_shutdown(struct platform_device *pdev) > struct dw_i3c_master *master = platform_get_drvdata(pdev); > int ret; > > - ret = pm_runtime_resume_and_get(master->dev); > - if (ret < 0) { > - dev_err(master->dev, > - "<%s> cannot resume i3c bus master, err: %d\n", > - __func__, ret); > - return; > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) { > + ret = pm_runtime_resume_and_get(master->dev); > + if (ret < 0) { > + dev_err(master->dev, > + "<%s> cannot resume i3c bus master, err: %d\n", > + __func__, ret); > + return; > + } > } > > cancel_work_sync(&master->hj_work); > @@ -1756,11 +1787,15 @@ static void dw_i3c_shutdown(struct platform_device *pdev) > writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); > writel((u32)~INTR_ALL, master->regs + INTR_SIGNAL_EN); > > - pm_runtime_put_autosuspend(master->dev); > + if (!(master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)) > + pm_runtime_put_autosuspend(master->dev); > } > > static const struct of_device_id dw_i3c_master_of_match[] = { > { .compatible = "snps,dw-i3c-master-1.00a", }, > + { .compatible = "altr,agilex5-dw-i3c-master", > + .data = (void *)DW_I3C_DISABLE_RUNTIME_PM_QUIRK, > + }, > {}, > }; > MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match); > -- > 2.49.GIT > > > -- > linux-i3c mailing list > linux-i3c@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-i3c ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-10-31 18:45 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-10-31 9:05 [PATCH v3 0/3] i3c: dw: Add Altera Agilex5 runtime PM disable quirk adrianhoyin.ng 2025-10-31 9:05 ` [PATCH v3 1/3] dt-bindings: i3c: snps: Add Altera Agilex5 SoC compatible adrianhoyin.ng 2025-10-31 9:26 ` Krzysztof Kozlowski 2025-10-31 18:36 ` Frank Li 2025-10-31 9:05 ` [PATCH v3 2/3] arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers adrianhoyin.ng 2025-10-31 18:44 ` Frank Li 2025-10-31 9:05 ` [PATCH v3 3/3] i3c: dw: Add runtime PM disable quirk for Altera Agilex5 adrianhoyin.ng 2025-10-31 18:43 ` Frank Li
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).