| From 73e3e3fc50de50cfd68e945d85679c983ed31bd9 Mon Sep 17 00:00:00 2001 |
| From: Akinobu Mita <akinobu.mita@gmail.com> |
| Date: Fri, 21 Jul 2017 00:24:20 +0900 |
| Subject: iio: adc: ti-ads1015: avoid getting stale result after runtime resume |
| |
| From: Akinobu Mita <akinobu.mita@gmail.com> |
| |
| commit 73e3e3fc50de50cfd68e945d85679c983ed31bd9 upstream. |
| |
| This driver assumes that the device is operating in the continuous |
| conversion mode which performs the conversion continuously. So this driver |
| doesn't insert a wait time before reading the conversion register if the |
| configuration is not changed from a previous request. |
| |
| This assumption is broken if the device is runtime suspended and entered |
| a power-down state. The forthcoming request causes reading a stale result |
| from the conversion register as the device is runtime resumed just before. |
| |
| Fix it by adding a flag to detect that condition and insert a necessary |
| wait time. |
| |
| Cc: Daniel Baluta <daniel.baluta@gmail.com> |
| Signed-off-by: Akinobu Mita <akinobu.mita@gmail.com> |
| Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/iio/adc/ti-ads1015.c | 18 ++++++++++++++++-- |
| 1 file changed, 16 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/iio/adc/ti-ads1015.c |
| +++ b/drivers/iio/adc/ti-ads1015.c |
| @@ -176,6 +176,12 @@ struct ads1015_data { |
| struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; |
| |
| unsigned int *data_rate; |
| + /* |
| + * Set to true when the ADC is switched to the continuous-conversion |
| + * mode and exits from a power-down state. This flag is used to avoid |
| + * getting the stale result from the conversion register. |
| + */ |
| + bool conv_invalid; |
| }; |
| |
| static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg) |
| @@ -254,9 +260,10 @@ int ads1015_get_adc_result(struct ads101 |
| if (ret < 0) |
| return ret; |
| |
| - if (change) { |
| + if (change || data->conv_invalid) { |
| conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]); |
| usleep_range(conv_time, conv_time + 1); |
| + data->conv_invalid = false; |
| } |
| |
| return regmap_read(data->regmap, ADS1015_CONV_REG, val); |
| @@ -624,6 +631,8 @@ static int ads1015_probe(struct i2c_clie |
| if (ret) |
| return ret; |
| |
| + data->conv_invalid = true; |
| + |
| ret = pm_runtime_set_active(&client->dev); |
| if (ret) |
| goto err_buffer_cleanup; |
| @@ -679,10 +688,15 @@ static int ads1015_runtime_resume(struct |
| { |
| struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); |
| struct ads1015_data *data = iio_priv(indio_dev); |
| + int ret; |
| |
| - return regmap_update_bits(data->regmap, ADS1015_CFG_REG, |
| + ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG, |
| ADS1015_CFG_MOD_MASK, |
| ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT); |
| + if (!ret) |
| + data->conv_invalid = true; |
| + |
| + return ret; |
| } |
| #endif |
| |