Re: [PATCH] i3c: master: dw-i3c-master: fix OD timing for first broadcast
"NG, TZE YEE" <[email protected]>
| Newsgroups | org.infradead.lists.linux-i3c,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
On 12/6/2026 12:19 am, Frank Li wrote: > [You don't often get email from [email protected]. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] > > On Wed, Jun 10, 2026 at 01:14:26AM -0700, [email protected] wrote: >> From: Tze Yee Ng <[email protected]> >> >> Implement ->set_speed() so the I3C core can switch open-drain timing for >> the first broadcast address per spec: I3C_OPEN_DRAIN_SLOW_SPEED programs >> tHIGH_INIT (200 ns) before RSTDAA, and I3C_OPEN_DRAIN_NORMAL_SPEED restores >> normal OD timing afterward. Cache the normal OD register value during bus >> init and use a separate od_hcnt for the slow path so SDR extended timing >> remains derived from the normal PP hcnt. >> >> Fixes I2C devices with spike filters not being detected on mixed buses. >> >> Signed-off-by: Tze Yee Ng <[email protected]> >> --- >> drivers/i3c/master/dw-i3c-master.c | 53 ++++++++++++++++++++++++++++++ >> drivers/i3c/master/dw-i3c-master.h | 1 + >> include/linux/i3c/master.h | 1 + >> 3 files changed, 55 insertions(+) >> >> diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c >> index 655693a2187e..dbb801910b3d 100644 >> --- a/drivers/i3c/master/dw-i3c-master.c >> +++ b/drivers/i3c/master/dw-i3c-master.c >> @@ -592,6 +592,7 @@ static int dw_i3c_clk_cfg(struct dw_i3c_master *master) >> scl_timing = SCL_I3C_TIMING_HCNT(hcnt) | SCL_I3C_TIMING_LCNT(lcnt); >> writel(scl_timing, master->regs + SCL_I3C_OD_TIMING); >> master->i3c_od_timing = scl_timing; >> + master->i3c_od_timing_normal = scl_timing; >> >> lcnt = DIV_ROUND_UP(core_rate, I3C_BUS_SDR1_SCL_RATE) - hcnt; >> scl_timing = SCL_EXT_LCNT_1(lcnt); >> @@ -1480,6 +1481,57 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id) >> return IRQ_HANDLED; >> } >> >> +static int dw_i3c_master_set_speed(struct i3c_master_controller *m, >> + enum i3c_open_drain_speed speed) >> +{ >> + struct dw_i3c_master *master = to_dw_i3c_master(m); >> + unsigned long core_rate, core_period; >> + u32 scl_timing; >> + u8 od_hcnt, lcnt; >> + int ret; >> + >> + ret = pm_runtime_resume_and_get(master->dev); >> + if (ret < 0) { >> + dev_err(master->dev, >> + "<%s> cannot resume i3c bus master, err: %d\n", >> + __func__, ret); >> + return ret; >> + } > > PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev->dev, pm); > if (PM_RUNTIME_ACQUIRE_ERR(&pm)) > return -ENXIO; > > Use modem QCQUIRE macro > >> + >> + switch (speed) { >> + case I3C_OPEN_DRAIN_SLOW_SPEED: >> + core_rate = clk_get_rate(master->core_clk); >> + if (!core_rate) { >> + ret = -EINVAL; >> + break; > > you can return ret; here after use ACQUIRE macro. > > Frank >> + } >> + >> + core_period = DIV_ROUND_UP(1000000000, core_rate); >> + lcnt = SCL_I3C_TIMING_LCNT(master->i3c_od_timing_normal); >> + od_hcnt = max_t(u8, SCL_I3C_TIMING_CNT_MIN, >> + DIV_ROUND_UP(I3C_BUS_THIGH_INIT_OD_MIN_NS, >> + core_period) - 1); >> + scl_timing = SCL_I3C_TIMING_HCNT(od_hcnt) | >> + SCL_I3C_TIMING_LCNT(lcnt); >> + writel(scl_timing, master->regs + SCL_I3C_OD_TIMING); >> + master->i3c_od_timing = scl_timing; >> + break; >> + >> + case I3C_OPEN_DRAIN_NORMAL_SPEED: >> + writel(master->i3c_od_timing_normal, >> + master->regs + SCL_I3C_OD_TIMING); >> + master->i3c_od_timing = master->i3c_od_timing_normal; >> + break; >> + >> + default: >> + ret = -EINVAL; >> + break; >> + } >> + pm_runtime_put_autosuspend(master->dev); >> + >> + return ret; >> +} >> + >> static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m, >> unsigned long dev_nack_retry_cnt) >> { >> @@ -1534,6 +1586,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = { >> .recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot, >> .enable_hotjoin = dw_i3c_master_enable_hotjoin, >> .disable_hotjoin = dw_i3c_master_disable_hotjoin, >> + .set_speed = dw_i3c_master_set_speed, >> .set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry, >> }; >> >> diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h >> index c5cb695c16ab..c814087a6f1f 100644 >> --- a/drivers/i3c/master/dw-i3c-master.h >> +++ b/drivers/i3c/master/dw-i3c-master.h >> @@ -46,6 +46,7 @@ struct dw_i3c_master { >> u32 dev_addr; >> u32 i3c_pp_timing; >> u32 i3c_od_timing; >> + u32 i3c_od_timing_normal; >> u32 ext_lcnt_timing; >> u32 bus_free_timing; >> u32 i2c_fm_timing; >> diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h >> index 592b646f6134..c7d7448c4cd7 100644 >> --- a/include/linux/i3c/master.h >> +++ b/include/linux/i3c/master.h >> @@ -259,6 +259,7 @@ struct i3c_device { >> #define I3C_BUS_THIGH_MIXED_MAX_NS 41 >> #define I3C_BUS_TIDLE_MIN_NS 200000 >> #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.43.7 >> Hi Frank, Thanks for review. I will change to use PM_RUNTIME_ACQUIRE in v2. Thanks, Tze Yee -- linux-i3c mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-i3c