summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorGeorg Ottinger <g.ottinger@abatec.at>2019-01-30 14:42:02 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-04-27 09:30:30 +0200
commitfd10344b7c0a572d895456506bf8d9d5caa0ecf5 (patch)
tree78850cda7416f194452f915be3e723d77ee8d43a /drivers
parent9d4f298b95e4a5fd550fc258834b846af64815c1 (diff)
downloadlinux-stable-fd10344b7c0a572d895456506bf8d9d5caa0ecf5.tar.gz
linux-stable-fd10344b7c0a572d895456506bf8d9d5caa0ecf5.tar.bz2
linux-stable-fd10344b7c0a572d895456506bf8d9d5caa0ecf5.zip
iio: adc: at91: disable adc channel interrupt in timeout case
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/iio/adc/at91_adc.c28
1 files changed, 17 insertions, 11 deletions
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index dc438141b75b..a4bb40f1e44e 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -703,23 +703,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;