From e64cce5256a3c48e950cb53ee38bb36c607ada3e Mon Sep 17 00:00:00 2001 From: Siddartha Mohanadoss Date: Tue, 19 Jul 2016 21:24:30 -0700 Subject: [PATCH] iio: adc: rradc: Update RRADC reads and scaling A check is needed to ensure the LSB and MSB are coherent when reading the result registers for the RRADC channels. The driver performs another round of read request and compares the result registers to ensure the data read back is coherent. Since there are no channel specific properties remove the dependency to add the channel nodes in the device tree and instead statically register the channels within the driver. Update the scaling functions to report in the units desired for the final scaled value based on the channel. The resistance channel reports the units for batt_id in ohms, the voltage channels such as DCIN, USBIN report the value in units of microvolts, the current channels in units of microamps and the temperature channels such as thermistors are reported in units of millidegC. Change-Id: I56e6bff28be2cc23f00622d5c37e42564c51a72f Signed-off-by: Siddartha Mohanadoss --- .../bindings/iio/adc/qcom-rradc.txt | 16 +- drivers/iio/adc/qcom-rradc.c | 237 +++++++++++------- 2 files changed, 144 insertions(+), 109 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt index 8a64ccbd66c0..721a4f72563e 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/qcom-rradc.txt @@ -36,13 +36,10 @@ Main node properties: Definition: Must be one. For details about IIO bindings see: Documentation/devicetree/bindings/iio/iio-bindings.txt -Channel subnode properties: - -- channel: - Usage: required - Value type: - Definition: ADC channel number. - See drivers/iio/adc/qcom-rradc.c for channels within rradc_channel_id +IIO client nodes need to specify the RRADC channel number while requesting ADC reads. +The channel list supported by the RRADC driver is available in the enum rradc_channel_id +located at at drivers/iio/adc/qcom-rradc.c. Clients can use this index from the enum +as the channel number while requesting ADC reads. Example: @@ -53,11 +50,6 @@ Example: #address-cells = <1>; #size-cells = <0>; #io-channel-cells = <1>; - - /* Channel node */ - batt_id { - channel = <0>; - }; }; /* IIO client node */ diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c index 116e0c73cb12..17c7d630a2cb 100644 --- a/drivers/iio/adc/qcom-rradc.c +++ b/drivers/iio/adc/qcom-rradc.c @@ -162,6 +162,9 @@ #define FG_ADC_KELVINMIL_CELSIUSMIL 273150 #define FG_ADC_RR_GPIO_FS_RANGE 5000 +#define FG_RR_ADC_COHERENT_CHECK_RETRY 5 +#define FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN 16 +#define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3 /* * The channel number is not a physical index in hardware, @@ -171,21 +174,16 @@ * the RR ADC before RR_ADC_MAX. */ enum rradc_channel_id { - RR_ADC_BATT_ID_5 = 0, - RR_ADC_BATT_ID_15, - RR_ADC_BATT_ID_150, - RR_ADC_BATT_ID, + RR_ADC_BATT_ID = 0, RR_ADC_BATT_THERM, RR_ADC_SKIN_TEMP, - RR_ADC_USBIN_V, RR_ADC_USBIN_I, - RR_ADC_DCIN_V, + RR_ADC_USBIN_V, RR_ADC_DCIN_I, + RR_ADC_DCIN_V, RR_ADC_DIE_TEMP, RR_ADC_CHG_TEMP, RR_ADC_GPIO, - RR_ADC_ATEST, - RR_ADC_TM_ADC, RR_ADC_MAX }; @@ -205,51 +203,75 @@ struct rradc_channels { long info_mask; u8 lsb; u8 msb; + u8 sts; int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result); }; struct rradc_chan_prop { enum rradc_channel_id channel; + uint32_t channel_data; int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, int *result); }; static int rradc_read(struct rradc_chip *rr_adc, u16 offset, u8 *data, int len) { - int rc = 0; + int rc = 0, retry_cnt = 0, i = 0; + u8 data_check[FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN]; + bool coherent_err = false; - rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, data, len); - if (rc < 0) - pr_err("rr adc read reg %d failed with %d\n", offset, rc); + if (len > FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN) { + pr_err("Increase the buffer length\n"); + return -EINVAL; + } + + while (retry_cnt < FG_RR_ADC_COHERENT_CHECK_RETRY) { + rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, + data, len); + if (rc < 0) { + pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc); + return rc; + } + + rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset, + data_check, len); + if (rc < 0) { + pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc); + return rc; + } + + for (i = 0; i < len; i++) { + if (data[i] != data_check[i]) + coherent_err = true; + } + + if (coherent_err) { + retry_cnt++; + coherent_err = false; + pr_debug("retry_cnt:%d\n", retry_cnt); + } else { + break; + } + } + + if (retry_cnt == FG_RR_ADC_COHERENT_CHECK_RETRY) + pr_err("Retry exceeded for coherrency check\n"); return rc; } static int rradc_post_process_batt_id(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_mohms) + int *result_ohms) { uint32_t current_value; int64_t r_id; - switch (prop->channel) { - case RR_ADC_BATT_ID_5: - current_value = FG_ADC_RR_BATT_ID_5_MA; - break; - case RR_ADC_BATT_ID_15: - current_value = FG_ADC_RR_BATT_ID_15_MA; - break; - case RR_ADC_BATT_ID_150: - current_value = FG_ADC_RR_BATT_ID_150_MA; - break; - default: - return -EINVAL; - } - + current_value = prop->channel_data; r_id = ((int64_t)adc_code * FG_ADC_RR_FS_VOLTAGE_MV); r_id = div64_s64(r_id, (FG_MAX_ADC_READINGS * current_value)); - *result_mohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR); + *result_ohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR); return 0; } @@ -270,30 +292,30 @@ static int rradc_post_process_therm(struct rradc_chip *chip, static int rradc_post_process_volt(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_mv) + int *result_uv) { - int64_t mv = 0; + int64_t uv = 0; /* 8x input attenuation; 2.5V ADC full scale */ - mv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR); - mv *= FG_ADC_RR_FS_VOLTAGE_MV; - mv = div64_s64(mv, FG_MAX_ADC_READINGS); - *result_mv = mv; + uv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR); + uv *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR); + uv = div64_s64(uv, FG_MAX_ADC_READINGS); + *result_uv = uv; return 0; } static int rradc_post_process_curr(struct rradc_chip *chip, struct rradc_chan_prop *prop, u16 adc_code, - int *result_ma) + int *result_ua) { - int64_t ma = 0; + int64_t ua = 0; /* 0.5 V/A; 2.5V ADC full scale */ - ma = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR); - ma *= FG_ADC_RR_FS_VOLTAGE_MV; - ma = div64_s64(ma, FG_MAX_ADC_READINGS); - *result_ma = ma; + ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR); + ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR); + ua = div64_s64(ua, FG_MAX_ADC_READINGS); + *result_ua = ua; return 0; } @@ -346,63 +368,68 @@ static int rradc_post_process_gpio(struct rradc_chip *chip, return 0; } -#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb, _sts) \ { \ - .datasheet_name = __stringify(_dname), \ + .datasheet_name = (_dname), \ .type = _type, \ .info_mask = _mask, \ .scale = _scale, \ .lsb = _lsb, \ .msb = _msb, \ + .sts = _sts, \ }, \ -#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_TEMP(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_TEMP, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_VOLTAGE, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_CURRENT, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ -#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb) \ +#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb, _sts) \ RR_ADC_CHAN(_dname, IIO_RESISTANCE, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\ - _scale, _lsb, _msb) \ + _scale, _lsb, _msb, _sts) \ static const struct rradc_channels rradc_chans[] = { - RR_ADC_CHAN_RESISTANCE("batt_id_5", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB) - RR_ADC_CHAN_RESISTANCE("batt_id_15", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_15_LSB, FG_ADC_RR_BATT_ID_15_MSB) - RR_ADC_CHAN_RESISTANCE("batt_id_150", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_150_LSB, FG_ADC_RR_BATT_ID_150_MSB) RR_ADC_CHAN_RESISTANCE("batt_id", rradc_post_process_batt_id, - FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB) + FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB, + FG_ADC_RR_BATT_ID_STS) RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm, - FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB) + FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB, + FG_ADC_RR_BATT_THERM_STS) RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm, - FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB) + FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB, + FG_ADC_RR_AUX_THERM_STS) RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr, - FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB) + FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB, + FG_ADC_RR_USB_IN_I_STS) RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt, - FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB) + FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB, + FG_ADC_RR_USB_IN_V_STS) RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr, - FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB) + FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB, + FG_ADC_RR_DC_IN_I_STS) RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt, - FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB) + FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB, + FG_ADC_RR_DC_IN_V_STS) RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp, - FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB) + FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB, + FG_ADC_RR_PMI_DIE_TEMP_STS) RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp, - FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB) + FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB, + FG_ADC_RR_CHARGER_TEMP_STS) RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio, - FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB) + FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB, + FG_ADC_RR_GPIO_STS) }; static int rradc_do_conversion(struct rradc_chip *chip, @@ -411,15 +438,35 @@ static int rradc_do_conversion(struct rradc_chip *chip, int rc = 0, bytes_to_read = 0; u8 buf[6]; u16 offset = 0, batt_id_5 = 0, batt_id_15 = 0, batt_id_150 = 0; + u16 status = 0; mutex_lock(&chip->lock); + if (prop->channel != RR_ADC_BATT_ID) { + /* BATT_ID STS bit does not get set initially */ + status = rradc_chans[prop->channel].sts; + rc = rradc_read(chip, status, buf, 1); + if (rc < 0) { + pr_err("status read failed:%d\n", rc); + goto fail; + } + + buf[0] &= FG_RR_ADC_STS_CHANNEL_READING_MASK; + if (buf[0] != FG_RR_ADC_STS_CHANNEL_READING_MASK) { + pr_warn("%s is not ready; nothing to read\n", + rradc_chans[prop->channel].datasheet_name); + rc = -ENODATA; + goto fail; + } + } + offset = rradc_chans[prop->channel].lsb; if (prop->channel == RR_ADC_BATT_ID) bytes_to_read = 6; else bytes_to_read = 2; + buf[0] = 0; rc = rradc_read(chip, offset, buf, bytes_to_read); if (rc) { pr_err("read data failed\n"); @@ -427,18 +474,27 @@ static int rradc_do_conversion(struct rradc_chip *chip, } if (prop->channel == RR_ADC_BATT_ID) { - batt_id_150 = (buf[4] << 8) | buf[5]; - batt_id_15 = (buf[2] << 8) | buf[3]; - batt_id_5 = (buf[0] << 8) | buf[1]; + batt_id_150 = (buf[5] << 8) | buf[4]; + batt_id_15 = (buf[3] << 8) | buf[2]; + batt_id_5 = (buf[1] << 8) | buf[0]; + if ((!batt_id_150) && (!batt_id_15) && (!batt_id_5)) { + pr_err("Invalid batt_id values with all zeros\n"); + rc = -EINVAL; + goto fail; + } + if (batt_id_150 <= FG_ADC_RR_BATT_ID_RANGE) { pr_debug("Batt_id_150 is chosen\n"); *data = batt_id_150; + prop->channel_data = FG_ADC_RR_BATT_ID_150_MA; } else if (batt_id_15 <= FG_ADC_RR_BATT_ID_RANGE) { pr_debug("Batt_id_15 is chosen\n"); *data = batt_id_15; + prop->channel_data = FG_ADC_RR_BATT_ID_15_MA; } else { pr_debug("Batt_id_5 is chosen\n"); *data = batt_id_5; + prop->channel_data = FG_ADC_RR_BATT_ID_5_MA; } } else { *data = (buf[1] << 8) | buf[0]; @@ -458,6 +514,11 @@ static int rradc_read_raw(struct iio_dev *indio_dev, u16 adc_code; int rc = 0; + if (chan->address >= RR_ADC_MAX) { + pr_err("Invalid channel index:%ld\n", chan->address); + return -EINVAL; + } + switch (mask) { case IIO_CHAN_INFO_PROCESSED: prop = &chip->chan_props[chan->address]; @@ -477,10 +538,6 @@ static int rradc_read_raw(struct iio_dev *indio_dev, *val = (int) adc_code; return IIO_VAL_INT; - case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = 1000; - return IIO_VAL_INT_PLUS_MICRO; default: rc = -EINVAL; break; @@ -498,15 +555,11 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) { const struct rradc_channels *rradc_chan; struct iio_chan_spec *iio_chan; - struct device_node *child; - unsigned int index = 0, chan, base; + unsigned int i = 0, base; int rc = 0; struct rradc_chan_prop prop; - chip->nchannels = of_get_available_child_count(node); - if (!chip->nchannels || (chip->nchannels >= RR_ADC_MAX)) - return -EINVAL; - + chip->nchannels = RR_ADC_MAX; chip->iio_chans = devm_kcalloc(chip->dev, chip->nchannels, sizeof(*chip->iio_chans), GFP_KERNEL); if (!chip->iio_chans) @@ -529,31 +582,21 @@ static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node) chip->base = base; iio_chan = chip->iio_chans; - for_each_available_child_of_node(node, child) { - rc = of_property_read_u32(child, "channel", &chan); - if (rc) { - dev_err(chip->dev, "invalid channel number %d\n", chan); - return rc; - } + for (i = 0; i < RR_ADC_MAX; i++) { + prop.channel = i; + prop.scale = rradc_chans[i].scale; + /* Private channel data used for selecting batt_id */ + prop.channel_data = 0; + chip->chan_props[i] = prop; - if (chan > RR_ADC_MAX || chan < RR_ADC_BATT_ID_5) { - dev_err(chip->dev, "invalid channel number %d\n", chan); - return -EINVAL; - } - - prop.channel = chan; - prop.scale = rradc_chans[chan].scale; - chip->chan_props[index] = prop; - - rradc_chan = &rradc_chans[chan]; + rradc_chan = &rradc_chans[i]; iio_chan->channel = prop.channel; iio_chan->datasheet_name = rradc_chan->datasheet_name; iio_chan->extend_name = rradc_chan->datasheet_name; iio_chan->info_mask_separate = rradc_chan->info_mask; iio_chan->type = rradc_chan->type; - iio_chan->indexed = 1; - iio_chan->address = index++; + iio_chan->address = i; iio_chan++; }