On Wed, 30 Jan 2019 14:42:02 +0100 <g.ottinger@xxxxxxxxx> wrote: > From: Georg Ottinger <g.ottinger@xxxxxxxxx> > > Having a brief look at at91_adc_read_raw() it is obvious that in the case > of a timeout the setting of AT91_ADC_CHDR and AT91_ADC_IDR registers is > omitted. If 2 different channels are queried we can end up with a > situation where two interrupts are enabled, but only one interrupt is > cleared in the interrupt handler. Resulting in a interrupt loop and a > system hang. > > Signed-off-by: Georg Ottinger <g.ottinger@xxxxxxxxx> Whilst I agree this looks like a correct change, I would like Maxime to take a look as he is obviously much more familiar with the driver than I am. I suspect you can only actually get there in the event of a hardware failure as that isn't actually a timeout you should ever see. Could be wrong though! Thanks, Jonathan > --- > drivers/iio/adc/at91_adc.c | 28 +++++++++++++++++----------- > 1 file changed, 17 insertions(+), 11 deletions(-) > > diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c > index 75d2f7358..596841a3c 100644 > --- a/drivers/iio/adc/at91_adc.c > +++ b/drivers/iio/adc/at91_adc.c > @@ -704,23 +704,29 @@ static int at91_adc_read_raw(struct iio_dev *idev, > ret = wait_event_interruptible_timeout(st->wq_data_avail, > st->done, > msecs_to_jiffies(1000)); > - if (ret == 0) > - ret = -ETIMEDOUT; > - if (ret < 0) { > - mutex_unlock(&st->lock); > - return ret; > - } > - > - *val = st->last_value; > > + /* Disable interrupts, regardless if adc conversion was > + * successful or not > + */ > at91_adc_writel(st, AT91_ADC_CHDR, > AT91_ADC_CH(chan->channel)); > at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel)); > > - st->last_value = 0; > - st->done = false; > + if (ret > 0) { > + /* a valid conversion took place */ > + *val = st->last_value; > + st->last_value = 0; > + st->done = false; > + ret = IIO_VAL_INT; > + } else if (ret == 0) { > + /* conversion timeout */ > + dev_err(&idev->dev, "ADC Channel %d timeout.\n", > + chan->channel); > + ret = -ETIMEDOUT; > + } > + > mutex_unlock(&st->lock); > - return IIO_VAL_INT; > + return ret; > > case IIO_CHAN_INFO_SCALE: > *val = st->vref_mv;