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: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Co-developed-by: Sanket Goswami <Sanket.Goswami@amd.com>
Signed-off-by: Sanket Goswami <Sanket.Goswami@amd.com>
Signed-off-by: Shyam 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: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
Shyam Sundar S K 2024-11-14 16:32:39 +05:30 committed by Alexandre Belloni
parent 0a0d851ce1
commit 473d0cb485
2 changed files with 29 additions and 1 deletions

View File

@ -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);

View File

@ -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)