Commit fba0e56e authored by Adrian Ng Ho Yin's avatar Adrian Ng Ho Yin Committed by Alexandre Belloni
Browse files

i3c: dw: Disable runtime PM on Agilex5 to avoid bus hang on IBI



When running compliance tests on the Altera Agilex5 SoCFPGA platform,
the I3C bus can hang when a slave issues an IBI after the DAA process
completes. The DesignWare I3C master enters runtime suspend once DAA
finishes and stops driving SCL, preventing the IBI transfer from
completing and leaving SDA stuck low.

Add a new compatible string, "altr,agilex5-dw-i3c-master" and apply a quirk
that keep runtime PM always active on this platform by calling
pm_runtime_get_noresume() during probe.

Prevent bus hangs triggered by IBIs on Agilex5 while maintaining keep the
same behavior on other platforms.

Signed-off-by: default avatarAdrian Ng Ho Yin <adrianhoyin.ng@altera.com>
Reviewed-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/482d540722a98c2809d8275445aaa544b565bf85.1762237922.git.adrianhoyin.ng@altera.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 8d1d2c40
Loading
Loading
Loading
Loading
+30 −1
Original line number Diff line number Diff line
@@ -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;
@@ -252,6 +253,10 @@ struct dw_i3c_i2c_dev_data {
	struct i3c_generic_ibi_pool *ibi_pool;
};

struct dw_i3c_drvdata {
	u32 flags;
};

static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
					   const struct i3c_ccc_cmd *cmd)
{
@@ -1535,6 +1540,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
			struct platform_device *pdev)
{
	int ret, irq;
	const struct dw_i3c_drvdata *drvdata;
	unsigned long quirks = 0;

	if (!master->platform_ops)
		master->platform_ops = &dw_i3c_platform_ops_default;
@@ -1590,7 +1597,18 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
	master->maxdevs = ret >> 16;
	master->free_pos = GENMASK(master->maxdevs - 1, 0);

	master->quirks = (unsigned long)device_get_match_data(&pdev->dev);
	if (has_acpi_companion(&pdev->dev)) {
		quirks = (unsigned long)device_get_match_data(&pdev->dev);
	} else if (pdev->dev.of_node) {
		drvdata = device_get_match_data(&pdev->dev);
		if (drvdata)
			quirks = drvdata->flags;
	}
	master->quirks = quirks;

	/* Keep controller enabled by preventing runtime suspend */
	if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
		pm_runtime_get_noresume(&pdev->dev);

	INIT_WORK(&master->hj_work, dw_i3c_hj_work);
	ret = i3c_master_register(&master->base, &pdev->dev,
@@ -1617,6 +1635,10 @@ void dw_i3c_common_remove(struct dw_i3c_master *master)
	cancel_work_sync(&master->hj_work);
	i3c_master_unregister(&master->base);

	/* Balance pm_runtime_get_noresume() from probe() */
	if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK)
		pm_runtime_put_noidle(master->dev);

	pm_runtime_disable(master->dev);
	pm_runtime_set_suspended(master->dev);
	pm_runtime_dont_use_autosuspend(master->dev);
@@ -1759,8 +1781,15 @@ static void dw_i3c_shutdown(struct platform_device *pdev)
	pm_runtime_put_autosuspend(master->dev);
}

static const struct dw_i3c_drvdata altr_agilex5_drvdata = {
	.flags = DW_I3C_DISABLE_RUNTIME_PM_QUIRK,
};

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 = &altr_agilex5_drvdata,
	},
	{},
};
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);