| From 09c6bdee51183a575bf7546890c8c137a75a2b44 Mon Sep 17 00:00:00 2001 |
| From: Georg Ottinger <g.ottinger@abatec.at> |
| Date: Wed, 30 Jan 2019 14:42:02 +0100 |
| Subject: iio: adc: at91: disable adc channel interrupt in timeout case |
| |
| From: Georg Ottinger <g.ottinger@abatec.at> |
| |
| commit 09c6bdee51183a575bf7546890c8c137a75a2b44 upstream. |
| |
| 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@abatec.at> |
| Acked-by: Ludovic Desroches <ludovic.desroches@microchip.com> |
| Cc: <Stable@vger.kernel.org> |
| Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/iio/adc/at91_adc.c | 28 +++++++++++++++++----------- |
| 1 file changed, 17 insertions(+), 11 deletions(-) |
| |
| --- 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_ |
| 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; |