Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Shyam Sundar S K and committed by
Alexandre Belloni
473d0cb4 0a0d851c

+29 -1
+28 -1
drivers/i3c/master/dw-i3c-master.c
··· 220 220 221 221 #define XFER_TIMEOUT (msecs_to_jiffies(1000)) 222 222 #define RPM_AUTOSUSPEND_TIMEOUT 1000 /* ms */ 223 + 224 + /* Timing values to configure 12.5MHz frequency */ 225 + #define AMD_I3C_OD_TIMING 0x4C007C 226 + #define AMD_I3C_PP_TIMING 0x8001A 227 + 228 + /* List of quirks */ 229 + #define AMD_I3C_OD_PP_TIMING BIT(1) 230 + 223 231 struct dw_i3c_cmd { 224 232 u32 cmd_lo; 225 233 u32 cmd_hi; ··· 802 794 return ret; 803 795 } 804 796 797 + static void amd_configure_od_pp_quirk(struct dw_i3c_master *master) 798 + { 799 + master->i3c_od_timing = AMD_I3C_OD_TIMING; 800 + master->i3c_pp_timing = AMD_I3C_PP_TIMING; 801 + } 802 + 805 803 static int dw_i3c_master_send_ccc_cmd(struct i3c_master_controller *m, 806 804 struct i3c_ccc_cmd *ccc) 807 805 { ··· 816 802 817 803 if (ccc->id == I3C_CCC_ENTDAA) 818 804 return -EINVAL; 805 + 806 + /* AMD platform specific OD and PP timings */ 807 + if (master->quirks & AMD_I3C_OD_PP_TIMING) { 808 + amd_configure_od_pp_quirk(master); 809 + writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING); 810 + writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); 811 + } 819 812 820 813 ret = pm_runtime_resume_and_get(master->dev); 821 814 if (ret < 0) { ··· 1623 1602 master->maxdevs = ret >> 16; 1624 1603 master->free_pos = GENMASK(master->maxdevs - 1, 0); 1625 1604 1605 + master->quirks = (unsigned long)device_get_match_data(&pdev->dev); 1606 + 1626 1607 INIT_WORK(&master->hj_work, dw_i3c_hj_work); 1627 1608 ret = i3c_master_register(&master->base, &pdev->dev, 1628 1609 &dw_mipi_i3c_ops, false); ··· 1698 1675 1699 1676 static void dw_i3c_master_restore_timing_regs(struct dw_i3c_master *master) 1700 1677 { 1678 + /* AMD platform specific OD and PP timings */ 1679 + if (master->quirks & AMD_I3C_OD_PP_TIMING) 1680 + amd_configure_od_pp_quirk(master); 1681 + 1701 1682 writel(master->i3c_pp_timing, master->regs + SCL_I3C_PP_TIMING); 1702 1683 writel(master->bus_free_timing, master->regs + BUS_FREE_TIMING); 1703 1684 writel(master->i3c_od_timing, master->regs + SCL_I3C_OD_TIMING); ··· 1776 1749 MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match); 1777 1750 1778 1751 static const struct acpi_device_id amd_i3c_device_match[] = { 1779 - { "AMDI0015" }, 1752 + { "AMDI0015", AMD_I3C_OD_PP_TIMING }, 1780 1753 { } 1781 1754 }; 1782 1755 MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match);
+1
drivers/i3c/master/dw-i3c-master.h
··· 50 50 u32 bus_free_timing; 51 51 u32 i2c_fm_timing; 52 52 u32 i2c_fmp_timing; 53 + u32 quirks; 53 54 /* 54 55 * Per-device hardware data, used to manage the device address table 55 56 * (DAT)