Re: [PATCH v8] i2c: designware: fix master is holding SCL low while ENABLE bit is disabled

From: Andy Shevchenko
Date: Tue Sep 10 2024 - 05:04:43 EST


On Tue, Sep 10, 2024 at 02:13:09PM +0800, Kimriver Liu wrote:
> It was observed issuing ABORT bit(IC_ENABLE[1]) will not work when

"...observed that issuing..."
...bit (..."


> IC_ENABLE is already disabled.
>
> Check if ENABLE bit(IC_ENABLE[0]) is disabled when the master is

"...bit (..."
master --> controller

> holding SCL low. If ENABLE bit is disabled, the software need
> enable it before trying to issue ABORT bit. otherwise,
> the controller ignores any write to ABORT bit.

Fixes tag?

...

> abort_needed = raw_intr_stats & DW_IC_INTR_MST_ON_HOLD;
> if (abort_needed) {
> + if (!(enable & DW_IC_ENABLE_ENABLE)) {

> + regmap_write(dev->map, DW_IC_ENABLE, DW_IC_ENABLE_ENABLE);

This call might also need a one line comment.

> + enable |= DW_IC_ENABLE_ENABLE;

More natural is to put this after the fsleep() call. The rationale is that it
will be easier to see what exactly is going to be written back to the
register.

> + /*
> + * Wait 10 times the signaling period of the highest I2C
> + * transfer supported by the driver (for 400KHz this is
> + * 25us) to ensure the I2C ENABLE bit is already set
> + * as described in the DesignWare I2C databook.
> + */
> + fsleep(DIV_ROUND_CLOSEST_ULL(10 * MICRO, t->bus_freq_hz));

...somewhere here...

> + }
> +
> regmap_write(dev->map, DW_IC_ENABLE, enable | DW_IC_ENABLE_ABORT);

...

> +static bool i2c_dw_is_master_idling(struct dw_i2c_dev *dev)

Sorry if I made a mistake, but again, looking at the usage you have again
negation here and there...

i2c_dw_is_controller_active

(note new terminology, dunno if it makes sense start using it in function
names, as we have more of them following old style)

> +{
> + u32 status;
> +
> + regmap_read(dev->map, DW_IC_STATUS, &status);
> + if (!(status & DW_IC_STATUS_MASTER_ACTIVITY))
> + return true;

return false;

.,,

> + return !regmap_read_poll_timeout(dev->map, DW_IC_STATUS, status,
> + !(status & DW_IC_STATUS_MASTER_ACTIVITY),
> + 1100, 20000);

...and drop !.

> +}

...

> + /*
> + * This happens rarely and is hard to reproduce. Debug trace

Rarely how? Perhaps put a ration in the parentheses, like

"...rarely (~1:100)..."

> + * showed that IC_STATUS had value of 0x23 when STOP_DET occurred,
> + * if disable IC_ENABLE.ENABLE immediately that can result in
> + * IC_RAW_INTR_STAT.MASTER_ON_HOLD holding SCL low.
> + */
> + if (!i2c_dw_is_master_idling(dev))

...and here

if (i2c_dw_is_controller_active(dev))

But please double check that I haven't made any mistakes in all this logic.

> + dev_err(dev->dev, "I2C master not idling\n");

--
With Best Regards,
Andy Shevchenko