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:
parent
0a0d851ce1
commit
473d0cb485
@ -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);
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user