i3c: dw: Add Altera SoCFPGA runtime PM disable quirk
Subsystems (3)
v2 cover i3c: dw: Add Altera SoCFPGA runtime PM disable quirk
From: Adrian Ng Ho Yin <[email protected]>
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,socfpga-dw-i3c-master", is introduced to identify the SoCFPGA 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:v1->v2:
*Add new compatible string in dw i3c dt binding.
*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
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 SoCFPGA
.../bindings/i3c/snps,dw-i3c-master.yaml | 6 +- .../arm64/boot/dts/intel/socfpga_agilex5.dtsi | 6 +- drivers/i3c/master/dw-i3c-master.c | 176 +++++++++++------- 3 files changed, 115 insertions(+), 73 deletions(-)
Signature · 1 line
2.49.GIT
RFC PATCH v2 1/3 dt-bindings: i3c: snps: Add Altera SoCFPGA compatible
From: Adrian Ng Ho Yin <[email protected]>
Add the "altr,socfpga-dw-i3c-master" compatible string to the Synopsys DesignWare I3C master binding. This allows SoCFPGA platforms such as Agilex5 to use the generic DW I3C master controller while applying any required platform-specific quirks.
--- .../devicetree/bindings/i3c/snps,dw-i3c-master.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml +5 -1
@@ -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,socfpga-dw-i3c-master + - const: snps,dw-i3c-master-1.00a reg: maxItems: 1
Signature · 1 line
2.49.GIT
Re: [RFC PATCH-v2 1/3] dt-bindings: i3c: snps: Add Altera SoCFPGA compatible
Quoted 17 lines · On 31/10/2025 09:27, [email protected] wrote:
From: Adrian Ng Ho Yin <[email protected]>
Add the "altr,socfpga-dw-i3c-master" compatible string to the Synopsys DesignWare I3C master binding. This allows SoCFPGA platforms such as Agilex5 to use the generic DW I3C master controller while applying any required platform-specific quirks.
Signed-off-by Adrian Ng Ho Yin <[email protected]>--- .../devicetree/bindings/i3c/snps,dw-i3c-master.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml +0
-0
Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml allOf: · line 20
Quoted 8 lines
Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml +4
-1@@ -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,socfpga-dw-i3c-master
You need soc specific compatible (see writing bindings). There is no such SoC as socfpga. Otherwise please point me to the line in the *existing* bindings describing this SoC.
Best regards,
Krzysztof
RFC PATCH v2 2/3 arm64: dts: intel: agilex5: Add Altera compatible for I3C controllers
In reply to [email protected]From: Adrian Ng Ho Yin <[email protected]>
Add the "altr,socfpga-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.
--- arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi +4 -2
@@ -203,7 +203,8 @@ i2c4: i2c@10c02c00 { }; i3c0: i3c@10da0000 { - compatible = "snps,dw-i3c-master-1.00a"; + compatible = "altr,socfpga-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,socfpga-dw-i3c-master", + "snps,dw-i3c-master-1.00a"; reg = <0x10da1000 0x1000>; #address-cells = <3>; #size-cells = <0>;
Signature · 1 line
2.49.GIT
RFC PATCH v2 3/3 i3c: dw: Add runtime PM disable quirk for Altera SoCFPGA
In reply to [email protected]From: Adrian Ng Ho Yin <[email protected]>
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 some Altera SoCFPGA systems, where the controller may enter a suspended state while a slave device issues an In-Band Interrupt (IBI). 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. Disabling runtime PM ensures the controller remains active, allowing IBI transactions to complete correctly.
A new compatible string, "altr,socfpga-dw-i3c-master", is added to represent the SoCFPGA-specific variant of the DesignWare I3C controller, which requires runtime PM to remain disabled.
--- drivers/i3c/master/dw-i3c-master.c | 176 +++++++++++++++++------------ 1 file changed, 106 insertions(+), 70 deletions(-)
drivers/i3c/master/dw-i3c-master.c +106 -70
@@ -227,7 +227,8 @@#define AMD_I3C_PP_TIMING 0x8001A/* List of quirks */ -#define AMD_I3C_OD_PP_TIMING BIT(1) +#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,16 @@ 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 ="snps,dw-i3c-master-1.00a" + }, + { .compatible ="altr,socfpga-dw-i3c-master" , + .data = (void *)DW_I3C_DISABLE_RUNTIME_PM_QUIRK , + }, {}, };MODULE_DEVICE_TABLE (of, dw_i3c_master_of_match);
Signature · 1 line
2.49.GIT
Quoted 21 lines · On 31/10/2025 09:27, [email protected] wrote:
From: Adrian Ng Ho Yin <[email protected]>
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,socfpga-dw-i3c-master", is introduced to identify the SoCFPGA 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.
Quoted 8 lines
---
changelog:v1->v2:
*Add new compatible string in dw i3c dt binding.
*Added new compatible string for altr socfpga platform. *RemoveKconfigthat disables runtime PM added in v1. *Update implementation to disable runtime PM via compatible string match
Why is this still RFC? Please always provide reason in changelog and be aware that some maintainers - including me - skip or just look briefly through RFC patches since they are simply not ready.
Also remember that versioning *continuous* when dropping non-RFC.
Best regards,
Krzysztof
Quoted 32 lines · Krzysztof Kozlowski
Quoted 21 lines · On 31/10/2025 09:27, [email protected] wrote:
From: Adrian Ng Ho Yin <[email protected]>
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,socfpga-dw-i3c-master", is introduced to identify the SoCFPGA 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.Quoted 8 lines
---
changelog:v1->v2:
*Add new compatible string in dw i3c dt binding.
*Added new compatible string for altr socfpga platform. *RemoveKconfigthat disables runtime PM added in v1. *Update implementation to disable runtime PM via compatible string match
Quoted 6 lines
Why is this still RFC? Please always provide reason in changelog and be aware that some maintainers - including me - skip or just look briefly through RFC patches since they are simply not ready.
Also remember that versioning *continuous* when dropping non-RFC.
Noted. Will resend and drop the RFC tag and update reason in the changelog.>
Quoted 2 lines
Best regards,
Krzysztof
Thank You,
Adrian