i3c: dw: Add quirk to address OD/PP timing issue on AMD platform
authorShyam Sundar S K <Shyam-sundar.S-k@amd.com>
Thu, 14 Nov 2024 11:02:39 +0000 (16:32 +0530)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Mon, 18 Nov 2024 13:27:29 +0000 (14:27 +0100)
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>
drivers/i3c/master/dw-i3c-master.c
drivers/i3c/master/dw-i3c-master.h

index 1a7c300..5b5c2e4 100644 (file)
 
 #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);
index 219ff81..c5cb695 100644 (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)