[PATCH] i3c: master: dw-i3c-master: fix OD timing for first broadcast

NG, TZE YEE tze.yee.ng at altera.com
Tue Jun 30 22:46:09 PDT 2026


On 24/6/2026 2:44 pm, Guntupalli, Manikanta wrote:
> Public
>
> Hi Tze Yee Ng,
>
> We will check this on our end and get back to you shortly with an update.
>
> Thanks,
> Manikanta
>
>

Hi Manikanta,

Gentle follow-up on the AMD quirk question below. It has been about a
week since your 24 June reply, and I have not seen an update yet.

For AMDI0015 with AMD_I3C_OD_PP_TIMING, I still need your guidance on:
- what OD timing ->set_speed(NORMAL) should restore,
- how ->set_speed(SLOW) should be programmed, and
- whether send_ccc_cmd() should keep forcing AMD_I3C_OD_TIMING before
every CCC.

Once I have your input, I can send an AMD-specific follow-up patch.

Thanks,
Tze Yee Ng

>> -----Original Message-----
>> From: NG, TZE YEE <tze.yee.ng at altera.com>
>> Sent: Friday, June 19, 2026 1:49 PM
>> To: Guntupalli, Manikanta <manikanta.guntupalli at amd.com>
>> Cc: linux-i3c at lists.infradead.org; Frank.Li at kernel.org; Frank Li
>> <Frank.li at oss.nxp.com>
>> Subject: Re: [PATCH] i3c: master: dw-i3c-master: fix OD timing for first broadcast
>>
>> On 12/6/2026 3:58 pm, Ng, Tze Yee wrote:
>>> On 12/6/2026 12:21 am, Frank Li wrote:
>>>> [You don't often get email from frank.li at oss.nxp.com. Learn why this
>>>> is important at https://aka.ms/LearnAboutSenderIdentification ]
>>>>
>>>> On Wed, Jun 10, 2026 at 08:36:55AM +0000, sashiko-bot at kernel.org wrote:
>>>>> Thank you for your contribution! Sashiko AI review found 5 potential
>>>>> issue(s) to consider:
>>>>>
>>>>> New issues:
>>>>> - [High] The newly introduced `dw_i3c_master_set_speed()`
>>>>> permanently overwrites hardware-specific AMD timings with generic timings.
>>>>> - [High] The `od_hcnt` calculation for `I3C_OPEN_DRAIN_SLOW_SPEED`
>>>>> violates the `tHIGH_INIT` minimum requirement.
>>>>>
>>>>> Pre-existing issues:
>>>>> - [High] The IRQ handler accesses hardware registers without Runtime
>>>>> PM protection.
>>>>> - [High] Use-After-Free and invalid `queue_work()` due to improper
>>>>> teardown ordering in `dw_i3c_common_remove()`.
>>>>> - [High] `dw_i3c_master_set_dev_nack_retry` modifies hardware
>>>>> registers without resuming the device via Runtime PM.
>>>>> --
>>>>>
>>>>> commit bd5322e3d6d6cd3a6a8155f24cc89a12fe61be00
>>>>> Author: Tze Yee Ng <tze.yee.ng at altera.com>
>>>>>
>>>>> i3c: master: dw-i3c-master: fix OD timing for first broadcast
>>>>>
>>>>> This commit implements the set_speed() callback to switch open-drain
>>>>> timing for the first broadcast address to slow speed. It aims to fix
>>>>> I2C devices with spike filters not being detected on mixed buses by
>>>>> dynamically switching between slow and normal open drain speeds.
>>>>>
>>>>>> diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/
>>>>>> master/dw-i3c-master.c index a7593d6efac5a..6cf7071d3d06b 100644
>>>>>> --- a/drivers/i3c/master/dw-i3c-master.c
>>>>>> +++ b/drivers/i3c/master/dw-i3c-master.c
>>>>> [ ... ]
>>>>>> @@ -1484,6 +1485,57 @@ static irqreturn_t
>>>>>> dw_i3c_master_irq_handler(int irq, void *dev_id)
>>>>>>       return IRQ_HANDLED;
>>>>>>    }
>>>>>
>>>>> [Severity: High]
>>>>> This is a pre-existing issue, but does the IRQ handler access
>>>>> hardware registers without Runtime PM protection?
>>>>>
>>>>> The interrupt handler directly reads hardware registers before
>>>>> checking the device's PM active state:
>>>>>
>>>>> drivers/i3c/master/dw-i3c-master.c:dw_i3c_master_irq_handler() {
>>>>>       ...
>>>>>       status = readl(master->regs + INTR_STATUS);
>>>>>       ...
>>>>> }
>>>>>
>>>>> Since the driver does not synchronize IRQs in its runtime_suspend
>>>>> callback, if an interrupt arrives right as the device enters runtime
>>>>> suspend (or if a spurious interrupt occurs), will this raw register
>>>>> read access powered-down hardware and cause a synchronous external
>>>>> abort?
>>>>>
>>>>>> +static int dw_i3c_master_set_speed(struct i3c_master_controller
>>>>>> +*m,
>>>>>> +                              enum i3c_open_drain_speed speed) {
>>>>> [ ... ]
>>>>>> +   switch (speed) {
>>>>>> +   case I3C_OPEN_DRAIN_SLOW_SPEED:
>>>>> [ ... ]
>>>>>> +           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);
>>>>>
>>>>> [Severity: High]
>>>>> Does this precision loss guarantee the resulting pulse is shorter
>>>>> than the specification minimum for certain clock frequencies?
>>>>>
>>>>> If core_period is calculated by rounding up 1000000000 / core_rate,
>>>>> using this overestimated integer in the division
>>>>> DIV_ROUND_UP(I3C_BUS_THIGH_INIT_OD_MIN_NS, core_period) - 1
>>>>> underestimates the necessary cycle count.
>>>>>
>>>>> For example, at a 133.3 MHz core rate, the exact period is 7.5 ns
>>>>> but core_period evaluates to 8 ns. The calculated cycle count
>>>>> evaluates to 25 cycles, which produces a 187.5 ns pulse.
>>>>>
>>>>> Does this violate the strict 200 ns minimum for tHIGH_INIT required
>>>>> by the I3C specification?
>>>>
>>>> Is it true?
>>>>
>>>>>
>>>>>> +           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);
>>>>>
>>>>> [Severity: High]
>>>>> Does unconditionally restoring master->i3c_od_timing_normal here
>>>>> permanently overwrite hardware-specific AMD timings with generic
>>>>> timings?
>>>>>
>>>>> If amd_configure_od_pp_quirk() is not updated to modify
>>>>> master->i3c_od_timing_normal, restoring it after a broadcast
>>>>> master->destroys
>>>>> the
>>>>> required AMD-specific quirk timings.
>>>>>
>>>>> Additionally, does dw_i3c_master_send_ccc_cmd() unconditionally
>>>>> overwrite the OD timing with AMD normal timing right before sending
>>>>> a broadcast CCC, bypassing the I3C_OPEN_DRAIN_SLOW_SPEED state
>>>>> expected by the I3C core?
>>>>>
>>>>>> +           master->i3c_od_timing = master->i3c_od_timing_normal;
>>>>>> +           break;
>>>>
>>>> Please check this also?
>>>>
>>>> Frank
>>>>
>>>>> [ ... ]
>>>>>> +static int dw_i3c_master_set_dev_nack_retry(struct
>>>>>> i3c_master_controller *m,
>>>>>> +                                       unsigned long
>>>>>> dev_nack_retry_cnt)
>>>>>> +{
>>>>> [ ... ]
>>>>>> +   for (i = 0; i < master->maxdevs; i++) {
>>>>>> +           /* Skip free/empty slots */
>>>>>> +           if (master->free_pos & BIT(i))
>>>>>> +                   continue;
>>>>>> +
>>>>>> +           reg = readl(master->regs +
>>>>>> +
>>>>>> +DEV_ADDR_TABLE_LOC(master->datstartaddr,
>>>>>> i));
>>>>>
>>>>> [Severity: High]
>>>>> This is a pre-existing issue, but does this function modify hardware
>>>>> registers without resuming the device via Runtime PM?
>>>>>
>>>>> The sysfs attribute dev_nack_retry_count directly invokes
>>>>> master->ops->set_dev_nack_retry(). Since sysfs callbacks can be
>>>>> triggered
>>>>> from userspace at any time, executing this while the device is
>>>>> runtime suspended will access powered-down hardware.
>>>>>
>>>>> On ARM SoCs, can this trigger a kernel panic (external abort) and
>>>>> allow userspace to trivially crash the system?
>>>>>
>>>>> [ ... ]
>>>>>
>>>>> [Severity: High]
>>>>> This is a pre-existing issue, but is there a use-after-free or
>>>>> invalid
>>>>> queue_work() due to improper teardown ordering in
>>>>> dw_i3c_common_remove()?
>>>>>
>>>>> dw_i3c_common_remove() calls cancel_work_sync(&master->hj_work)
>>>>> before the
>>>>> hardware interrupt is disabled and freed by devres:
>>>>>
>>>>> drivers/i3c/master/dw-i3c-master.c:dw_i3c_common_remove() {
>>>>>       ...
>>>>>       cancel_work_sync(&master->hj_work);
>>>>>       i3c_master_unregister(&master->base);
>>>>>       ...
>>>>> }
>>>>>
>>>>> Because the IRQ remains active throughout the function and during
>>>>> early devres teardown, can an incoming Hot Join interrupt trigger
>>>>> the IRQ handler, which will call queue_work() to re-queue hj_work?
>>>>>
>>>>> Does this either cause queue_work() to execute on a destroyed
>>>>> workqueue or result in a use-after-free when the workqueue
>>>>> subsequently processes the work using the master object that has
>>>>> already been freed by devres?
>>>>>
>>>>> --
>>>>> Sashiko AI review · https://nam10.safelinks.protection.outlook.com/?
>>>>>
>> url=https%3A%2F%2Fsashiko.dev%2F%23%2Fpatchset%2Febddb8b62eae92de0a
>> 7
>>>>> eeda93cb18213c677ae96.1781077653.git.tze.yee.ng%40altera.com%3Fpart%
>>>>>
>> 3D1&data=05%7C02%7Ctze.yee.ng%40altera.com%7C8e2758995cb645fd357608
>> d
>>>>>
>> ec7d58fc1%7Cfbd72e03d4a54110adce614d51f2077a%7C0%7C0%7C6391679172
>> 502
>>>>>
>> 16401%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLj
>> AuMD
>>>>>
>> AwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%
>> 7C&
>>>>>
>> sdata=by5%2FiOLcg8kdN%2FkGUwg3B988Zkk3%2FjQJA%2FOqMllYOY0%3D&r
>> eserve
>>>>> d=0
>>>>>
>>>>> --
>>>>> linux-i3c mailing list
>>>>> linux-i3c at lists.infradead.org
>>>>> https://nam10.safelinks.protection.outlook.com/?
>>>>> url=http%3A%2F%2Flists.infradead.org%2Fmailman%2Flistinfo%2Flinux-
>>>>>
>> i3c&data=05%7C02%7Ctze.yee.ng%40altera.com%7C8e2758995cb645fd357608d
>>>>>
>> ec7d58fc1%7Cfbd72e03d4a54110adce614d51f2077a%7C0%7C0%7C6391679172
>> 502
>>>>>
>> 61515%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLj
>> AuMD
>>>>>
>> AwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%
>> 7C&
>>>>> sdata=yRfffqH10vlfqPSccrkt3XJkPqEDr7Ecoe08uXGamcQ%3D&reserved=0
>>>
>>> Hi Frank,
>>>
>>> Sashiko is correct that the initial core_period-based calculation
>>> could undershoot the 200 ns tHIGH_INIT minimum at some core clock
>>> rates. I checked the Zephyr DesignWare I3C driver, which uses a
>>> core_rate-based calculation aligned with the DWC timing model. v2 will
>>> adopt the same
>>> approach:
>>>
>>> od_hcnt = DIV_ROUND_UP((u64)I3C_BUS_THIGH_INIT_OD_MIN_NS *
>> core_rate,
>>>                          1000000000ULL) - 1;
>>>
>>> still using the existing - 1 and SCL_I3C_TIMING_CNT_MIN convention
>>> from dw_i3c_clk_cfg().
>>>
>>> Reference: https://github.com/zephyrproject-rtos/zephyr/
>>> commit/4d6673bc693920bdacf96d7cd3cfeb71d421e0ae
>>> (drivers/i3c/i3c_dw.c,
>>> dw_i3c_init_scl_timing())
>>>
>>> For AMD specific timings,  I left existing AMD behavior unchanged in v1.
>>> @Manikanta Guntupalli, for AMDI0015, should ->set_speed() use
>>> AMD_I3C_OD_TIMING or generic timing as the normal OD baseline at slow/
>>> normal speed? I can add AMD-specific handling in v2 once that is clarified.
>>>
>>> Thanks,
>>> Tze Yee
>>
>> Hi Manikanta,
>>
>> Just checking whether you had a chance to look at the AMD dw-i3c-master question
>> below.
>>
>> For AMDI0015, I need to know whether ->set_speed() should use
>> AMD_I3C_OD_TIMING or generic OD timing as the normal baseline, and whether
>> send_ccc_cmd() should stop overwriting OD timing before RSTDAA.
>>
>> Once I have your guidance, I can send an AMD-specific follow-up patch.
>>
>> Thanks,
>> Tze Yee Ng
>



More information about the linux-i3c mailing list