From: Dinesh Maniyam <dinesh.mani...@intel.com> Fix the I3C device with spike filter unable to detect issue by setting tHIGH_INIT to 200ns for first broadcast address. This is according to MIPI SPEC 1.1.1 for first broadcast address which is already part of linux upstreamed patch.
Signed-off-by: Dinesh Maniyam <dinesh.mani...@intel.com> --- drivers/i3c/master/dw-i3c-master.c | 23 +++++++++++++++++++++++ include/dw-i3c.h | 1 + include/linux/i3c/master.h | 1 + 3 files changed, 25 insertions(+) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index b3c5bf210c..af6e472f23 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -330,6 +330,14 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master) if (hcnt < SCL_I3C_TIMING_CNT_MIN) hcnt = SCL_I3C_TIMING_CNT_MIN; + /* set back to THIGH_MAX_NS, after disable spike filter */ + if (!master->first_broadcast) { + lcnt = SCL_I3C_TIMING_LCNT(readl(master->regs + SCL_I3C_OD_TIMING)); + scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | lcnt; + writel(scl_timing, master->regs + SCL_I3C_OD_TIMING); + return 0; + } + lcnt = DIV_ROUND_UP(core_rate, master->base.bus.scl_rate.i3c) - hcnt; if (lcnt < SCL_I3C_TIMING_CNT_MIN) lcnt = SCL_I3C_TIMING_CNT_MIN; @@ -347,6 +355,9 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master) lcnt = max_t(u8, DIV_ROUND_UP(I3C_BUS_TLOW_OD_MIN_NS, core_period), lcnt); + /* first broadcast thigh to 200ns, to disable spike filter */ + hcnt = DIV_ROUND_UP(I3C_BUS_THIGH_INIT_OD_MIN_NS, core_period); + scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt); writel(scl_timing, master->regs + SCL_I3C_OD_TIMING); @@ -402,6 +413,9 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) u32 thld_ctrl; int ret; + /* first broadcast to disable spike filter */ + master->first_broadcast = true; + switch (bus->mode) { case I3C_BUS_MODE_MIXED_FAST: case I3C_BUS_MODE_MIXED_LIMITED: @@ -481,6 +495,15 @@ static void dw_i3c_master_irq_handler(struct dw_i3c_master *master) if (status & INTR_TRANSFER_ERR_STAT) writel(INTR_TRANSFER_ERR_STAT, master->regs + INTR_STATUS); + /* set back to THIGH_MAX_NS, after disable spike filter */ + if (master->first_broadcast) { + master->first_broadcast = false; + int ret = dw_i3c_clk_cfg(master); + + if (ret) + pr_err("Failed to set clk cfg\n"); + } + spin_unlock(&master->xferqueue.lock); } diff --git a/include/dw-i3c.h b/include/dw-i3c.h index 920f18bccb..42c37d6dfa 100644 --- a/include/dw-i3c.h +++ b/include/dw-i3c.h @@ -240,6 +240,7 @@ struct dw_i3c_master { char version[5]; char type[5]; u8 addrs[MAX_DEVS]; + bool first_broadcast; }; struct dw_i3c_i2c_dev_data { diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 759e689e2c..aefe593fc2 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -280,6 +280,7 @@ struct i3c_device { #define I3C_BUS_I2C_FM_PLUS_SCL_RATE 1000000 #define I3C_BUS_I2C_FM_SCL_RATE 400000 #define I3C_BUS_TLOW_OD_MIN_NS 200 +#define I3C_BUS_THIGH_INIT_OD_MIN_NS 200 /** * enum i3c_bus_mode - I3C bus mode -- 2.26.2