Commit 473d0cb4 authored by Shyam Sundar S K's avatar Shyam Sundar S K Committed by Alexandre Belloni
Browse files

i3c: dw: Add quirk to address OD/PP timing issue on AMD platform



The AMD Legacy I3C is having a problem with its IP, specifically with the
push-pull and open-drain pull-up registers. These registers need to be
manually programmed for every CCC submission to align with the duty cycle.
Therefore, add a quirk to address this issue.

Reviewed-by: default avatarJarkko Nikula <jarkko.nikula@linux.intel.com>
Co-developed-by: default avatarSanket Goswami <Sanket.Goswami@amd.com>
Signed-off-by: default avatarSanket Goswami <Sanket.Goswami@amd.com>
Signed-off-by: default avatarShyam Sundar S K <Shyam-sundar.S-k@amd.com>
Link: https://lore.kernel.org/r/20241114110239.660551-3-Shyam-sundar.S-k@amd.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 0a0d851c
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -220,6 +220,14 @@

#define XFER_TIMEOUT (msecs_to_jiffies(1000))
#define RPM_AUTOSUSPEND_TIMEOUT 1000 /* ms */

/* Timing values to configure 12.5MHz frequency */
#define AMD_I3C_OD_TIMING          0x4C007C
#define AMD_I3C_PP_TIMING          0x8001A

/* List of quirks */
#define AMD_I3C_OD_PP_TIMING		BIT(1)

struct dw_i3c_cmd {
	u32 cmd_lo;
	u32 cmd_hi;
@@ -794,6 +802,12 @@ static int dw_i3c_ccc_get(struct dw_i3c_master *master, struct i3c_ccc_cmd *ccc)
	return ret;
}

static void amd_configure_od_pp_quirk(struct dw_i3c_master *master)
{
	master->i3c_od_timing = AMD_I3C_OD_TIMING;
	master->i3c_pp_timing = AMD_I3C_PP_TIMING;
}

static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
				      struct i3c_ccc_cmd *ccc)
{
@@ -803,6 +817,13 @@ static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m,
	if (ccc->id == I3C_CCC_ENTDAA)
		return -EINVAL;

	/* AMD platform specific OD and PP timings */
	if (master->quirks & AMD_I3C_OD_PP_TIMING) {
		amd_configure_od_pp_quirk(master);
		writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
		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,
@@ -1602,6 +1623,8 @@ 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);

	INIT_WORK(&master->hj_work, dw_i3c_hj_work);
	ret = i3c_master_register(&master->base, &pdev->dev,
				  &dw_mipi_i3c_ops, false);
@@ -1675,6 +1698,10 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)

static void dw_i3c_master_restore_timing_regs(struct dw_i3c_master *master)
{
	/* AMD platform specific OD and PP timings */
	if (master->quirks & AMD_I3C_OD_PP_TIMING)
		amd_configure_od_pp_quirk(master);

	writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING);
	writel(master->bus_free_timing, master->regs + BUS_FREE_TIMING);
	writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING);
@@ -1749,7 +1776,7 @@ static const struct of_device_id dw_i3c_master_of_match[] = {
MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match);

static const struct acpi_device_id amd_i3c_device_match[] = {
	{ "AMDI0015" },
	{ "AMDI0015", AMD_I3C_OD_PP_TIMING },
	{ }
};
MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match);
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ struct dw_i3c_master {
	u32 bus_free_timing;
	u32 i2c_fm_timing;
	u32 i2c_fmp_timing;
	u32 quirks;
	/*
	 * Per-device hardware data, used to manage the device address table
	 * (DAT)