[Letux-kernel] [PATCH 2/5] w1: omap-hdq: fix interrupt handling which did show spurious timeouts

H. Nikolaus Schaller hns at goldelico.com
Sat May 9 18:26:24 CEST 2020

Hi Andreas,

> Am 08.05.2020 um 22:53 schrieb Andreas Kemnade <andreas at kemnade.info>:
> Hi,
> On Fri,  8 May 2020 16:28:17 +0200
> "H. Nikolaus Schaller" <hns at goldelico.com> wrote:
>> after
>> commit 27d13da8782a ("w1: omap-hdq: Simplify driver with PM runtime autosuspend")
>> was applied, we did see timeouts and wrong values when
>> reading a bq27000 connected to hdq of the omap3. This occurred
>> mainly after boot and sometimes settled down after several reads
>> indicating ignored interrupts.
>> root at letux:~# time cat /sys/class/power_supply/bq27000-battery/uevent
>> POWER_SUPPLY_NAME=bq27000-battery
>> real    0m15.761s
>> user    0m0.001s
>> sys     0m0.025s
>> root at letux:~#
>> Sometimes the effect did disappear after trying multiple
>> times and speed went up and results became correct.
>> Enabling debugging revealed that there were tx and
>> rx timeouts, i.e. the driver does not always respond
>> properly to interrupts.
>> This patch improves interrupt handling to avoid races
>> and loss of interrupt flags.
>> The ideas are:
>> * only the hdq_isr() sets bits in hdq_status
>> * and does wake_up()
>> * bits are only reset by the read/write/break functions
>>  if they were waited for
>> * rx/tx/timeout bits are completely decoupled from each
>>  other (and not reset all after waiting for one of them)
> how should that get mixed up? Don't we have a
> break-write-read or break-write-write sequence everywhere which
> must be somehow atomic, like on the i2c bus a
> start-chipaddress-data-data...-stop must be atomic?
> Maybe we are fixing symptoms here? Is the real problem just a layer up?

The isr reads the register for all 3 bits and stores the
state in hdq_status. This then resets all 3 bits in the interrupt
status register of the hdq controller.

If there is a second interrupt (for a different bit) before all
bits in hdq_status have been processed, the original bit
is no longer found in hdq_status.

This is why I make the hdq_status set only.

And the hdq_read/write processing resets only those bits that
have been processed.

>> * which bits to reset is specified by a new parameter
>>  to hdq_reset_irqstatus()
>> * hdq_reset_irqstatus() also returns the state before
>>  resetting so that we can encapsulate the spinlock
>> * this should now handle the case that the write and read
>>  are both already finished quickly before the hdq_write_byte()
>>  ends. Old code may have reset all status bits making
>>  the next hdq_read_byte() timeout
>> * the spinlock protects the reset of bits in function
>>  hdq_reset_irqstatus() which could be a read-write-modify
>>  problem if the interrupt handler tries to read-modify-write
>>  exactly at the same moment
>> * add mutex protection also for hdq_write_byte() just to
>>  be safe not to disturb a hdq_read_byte() triggered by
>>  some other thread/process.
> And again, this looks like some locking needs to be done one layer up?

There is locking. hqd_read/write/break are locked by a mutex.
And modifications of the hdq_status are now spin-locked.

Indeed some locking was missing but should now be complete.

> In summary: I do not think this worsens anything, so at least it should
> be a starting point for discussion.
> Regards,
> Andreas

BR and thanks,

More information about the Letux-kernel mailing list