diff mbox series

[V3,3/4] iio: adc: Add support for PMIC7 ADC

Message ID 1587993846-30773-4-git-send-email-jprakash@codeaurora.org (mailing list archive)
State New, archived
Headers show
Series [V3,1/4] iio: adc: Convert the QCOM SPMI ADC bindings to .yaml format | expand

Commit Message

Jishnu Prakash April 27, 2020, 1:24 p.m. UTC
The ADC architecture on PMIC7 is changed as compared to PMIC5. The
major change from PMIC5 is that all SW communication to ADC goes through
PMK8350, which communicates with other PMICs through PBS when the ADC
on PMK8350 works in master mode. The SID register is used to identify the
PMICs with which the PBS needs to communicate. Add support for the same.

Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
---
 drivers/iio/adc/qcom-spmi-adc5.c   | 239 +++++++++++++++++++++++++++++++++-
 drivers/iio/adc/qcom-vadc-common.c | 260 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/qcom-vadc-common.h |  15 +++
 3 files changed, 510 insertions(+), 4 deletions(-)

Comments

Amit Kucheria May 5, 2020, 10:03 a.m. UTC | #1
Hi Jishnu,

It is nice to cc a reviewer from a previous version. Just saw this.

On Mon, Apr 27, 2020 at 6:55 PM Jishnu Prakash <jprakash@codeaurora.org> wrote:
>
> The ADC architecture on PMIC7 is changed as compared to PMIC5. The
> major change from PMIC5 is that all SW communication to ADC goes through
> PMK8350, which communicates with other PMICs through PBS when the ADC
> on PMK8350 works in master mode. The SID register is used to identify the
> PMICs with which the PBS needs to communicate. Add support for the same.
>
> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
> ---
>  drivers/iio/adc/qcom-spmi-adc5.c   | 239 +++++++++++++++++++++++++++++++++-
>  drivers/iio/adc/qcom-vadc-common.c | 260 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/qcom-vadc-common.h |  15 +++
>  3 files changed, 510 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
> index 21fdcde..a66eeb7 100644
> --- a/drivers/iio/adc/qcom-spmi-adc5.c
> +++ b/drivers/iio/adc/qcom-spmi-adc5.c
> @@ -1,6 +1,6 @@
>  // SPDX-License-Identifier: GPL-2.0
>  /*
> - * Copyright (c) 2018, The Linux Foundation. All rights reserved.
> + * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
>   */
>
>  #include <linux/bitops.h>
> @@ -23,6 +23,7 @@
>
>  #define ADC5_USR_REVISION1                     0x0
>  #define ADC5_USR_STATUS1                       0x8
> +#define ADC5_USR_STATUS1_CONV_FAULT            BIT(7)
>  #define ADC5_USR_STATUS1_REQ_STS               BIT(1)
>  #define ADC5_USR_STATUS1_EOC                   BIT(0)
>  #define ADC5_USR_STATUS1_REQ_STS_EOC_MASK      0x3
> @@ -65,6 +66,9 @@
>
>  #define ADC5_USR_IBAT_DATA1                    0x53
>
> +#define ADC_CHANNEL_OFFSET                     0x8
> +#define ADC_CHANNEL_MASK                       GENMASK(7, 0)
> +
>  /*
>   * Conversion time varies based on the decimation, clock rate, fast average
>   * samples and measurements queued across different VADC peripherals.
> @@ -79,6 +83,11 @@
>  #define ADC5_HW_SETTLE_DIFF_MINOR              3
>  #define ADC5_HW_SETTLE_DIFF_MAJOR              5
>
> +/* For PMIC7 */
> +#define ADC_APP_SID                            0x40
> +#define ADC_APP_SID_MASK                       GENMASK(3, 0)
> +#define ADC7_CONV_TIMEOUT                      msecs_to_jiffies(10)
> +
>  enum adc5_cal_method {
>         ADC5_NO_CAL = 0,
>         ADC5_RATIOMETRIC_CAL,
> @@ -96,6 +105,7 @@ enum adc5_cal_val {
>   * @cal_method: calibration method.
>   * @cal_val: calibration value
>   * @decimation: sampling rate supported for the channel.
> + * @sid: slave id of PMIC owning the channel, for PMIC7.
>   * @prescale: channel scaling performed on the input signal.
>   * @hw_settle_time: the time between AMUX being configured and the
>   *     start of conversion.
> @@ -110,6 +120,7 @@ struct adc5_channel_prop {
>         enum adc5_cal_method    cal_method;
>         enum adc5_cal_val       cal_val;
>         unsigned int            decimation;
> +       unsigned int            sid;
>         unsigned int            prescale;
>         unsigned int            hw_settle_time;
>         unsigned int            avg_samples;
> @@ -140,6 +151,7 @@ struct adc5_chip {
>         bool                    poll_eoc;
>         struct completion       complete;
>         struct mutex            lock;
> +       int                     irq_eoc;
>         const struct adc5_data  *data;
>  };
>
> @@ -155,6 +167,8 @@ static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
>         {.num =  1, .den = 16}
>  };
>
> +static const struct adc5_data adc7_data_pmic;
> +
>  static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
>  {
>         return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
> @@ -165,6 +179,11 @@ static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
>         return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
>  }
>
> +static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
> +{
> +       return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
> +}
> +
>  static int adc5_prescaling_from_dt(u32 num, u32 den)
>  {
>         unsigned int pre;
> @@ -314,6 +333,47 @@ static int adc5_configure(struct adc5_chip *adc,
>         return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
>  }
>
> +static int adc7_configure(struct adc5_chip *adc,
> +                       struct adc5_channel_prop *prop)
> +{
> +       int ret;
> +       u8 conv_req = 0, buf[4];
> +
> +       ret = adc5_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid);
> +       if (ret)
> +               return ret;
> +
> +       ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
> +       if (ret < 0)
> +               return ret;
> +
> +       /* Digital param selection */
> +       adc5_update_dig_param(adc, prop, &buf[0]);
> +
> +       /* Update fast average sample value */
> +       buf[1] &= 0xff & ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;

Did you answer Andy's query about this in v2? I couldn't find it.

> +       buf[1] |= prop->avg_samples;
> +
> +       /* Select ADC channel */
> +       buf[2] = prop->channel;
> +
> +       /* Select HW settle delay for channel */
> +       buf[3] &= 0xff & ~ADC5_USR_HW_SETTLE_DELAY_MASK;

Same here.

> +       buf[3] |= prop->hw_settle_time;
> +
> +       /* Select CONV request */
> +       conv_req = ADC5_USR_CONV_REQ_REQ;
> +
> +       if (!adc->poll_eoc)
> +               reinit_completion(&adc->complete);
> +
> +       ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
> +       if (ret)
> +               return ret;
> +
> +       return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1);

Is it required to call adc5_write() twice here in sequence on the
adc7? adc5_configure() above manages just fine by having an extra
element in buf and one call.

> +}
> +
>  static int adc5_do_conversion(struct adc5_chip *adc,
>                         struct adc5_channel_prop *prop,
>                         struct iio_chan_spec const *chan,
> @@ -355,6 +415,43 @@ static int adc5_do_conversion(struct adc5_chip *adc,
>         return ret;
>  }
>
> +static int adc7_do_conversion(struct adc5_chip *adc,
> +                       struct adc5_channel_prop *prop,
> +                       struct iio_chan_spec const *chan,
> +                       u16 *data_volt, u16 *data_cur)
> +{
> +       int ret;
> +       u8 status;
> +
> +       mutex_lock(&adc->lock);
> +
> +       ret = adc7_configure(adc, prop);
> +       if (ret) {
> +               dev_err(adc->dev, "ADC configure failed with %d\n", ret);
> +               goto unlock;
> +       }
> +
> +       /* No support for polling mode at present */
> +       wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT);
> +
> +       ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
> +       if (ret < 0)
> +               goto unlock;
> +
> +       if (status & ADC5_USR_STATUS1_CONV_FAULT) {
> +               dev_err(adc->dev, "Unexpected conversion fault\n");
> +               ret = -EIO;
> +               goto unlock;
> +       }
> +
> +       ret = adc5_read_voltage_data(adc, data_volt);
> +
> +unlock:
> +       mutex_unlock(&adc->lock);
> +
> +       return ret;
> +}
> +
>  static irqreturn_t adc5_isr(int irq, void *dev_id)
>  {
>         struct adc5_chip *adc = dev_id;
> @@ -377,6 +474,56 @@ static int adc5_of_xlate(struct iio_dev *indio_dev,
>         return -EINVAL;
>  }
>
> +static int adc7_of_xlate(struct iio_dev *indio_dev,
> +                               const struct of_phandle_args *iiospec)
> +{
> +       struct adc5_chip *adc = iio_priv(indio_dev);
> +       int i, v_channel;
> +
> +       for (i = 0; i < adc->nchannels; i++) {
> +               v_channel = (adc->chan_props[i].sid << ADC_CHANNEL_OFFSET |
> +                       adc->chan_props[i].channel);
> +               if (v_channel == iiospec->args[0])
> +                       return i;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static int adc7_read_raw(struct iio_dev *indio_dev,
> +                        struct iio_chan_spec const *chan, int *val, int *val2,
> +                        long mask)

Just a nit. Since all your adc7 functions follow your adc5 functions,
please move this below adc5_read_raw for consistency.

> +{
> +       struct adc5_chip *adc = iio_priv(indio_dev);
> +       struct adc5_channel_prop *prop;
> +       u16 adc_code_volt, adc_code_cur;
> +       int ret;
> +
> +       prop = &adc->chan_props[chan->address];
> +
> +       switch (mask) {
> +       case IIO_CHAN_INFO_PROCESSED:
> +               ret = adc7_do_conversion(adc, prop, chan,

Except for this bit (call to adc7_do_conversion instead of
adc5_do_conversion), this function is identical to adc5_read_raw.

How about making adc5_read_raw and adc7_read_raw a shim around a
function adc_read_raw_common() that takes a function pointer?

> +                                       &adc_code_volt, &adc_code_cur);
> +               if (ret)
> +                       return ret;
> +
> +               ret = qcom_adc5_hw_scale(prop->scale_fn_type,
> +                       &adc5_prescale_ratios[prop->prescale],
> +                       adc->data,
> +                       adc_code_volt, val);
> +
> +               if (ret)
> +                       return ret;
> +
> +               return IIO_VAL_INT;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
>  static int adc5_read_raw(struct iio_dev *indio_dev,
>                          struct iio_chan_spec const *chan, int *val, int *val2,
>                          long mask)
> @@ -415,6 +562,11 @@ static const struct iio_info adc5_info = {
>         .of_xlate = adc5_of_xlate,
>  };
>
> +static const struct iio_info adc7_info = {
> +       .read_raw = adc7_read_raw,
> +       .of_xlate = adc7_of_xlate,
> +};
> +
>  struct adc5_channels {
>         const char *datasheet_name;
>         unsigned int prescale_index;
> @@ -477,6 +629,39 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
>                                         SCALE_HW_CALIB_PM5_SMB_TEMP)
>  };
>
> +static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
> +       [ADC7_REF_GND]          = ADC5_CHAN_VOLT("ref_gnd", 0,
> +                                       SCALE_HW_CALIB_DEFAULT)
> +       [ADC7_1P25VREF]         = ADC5_CHAN_VOLT("vref_1p25", 0,
> +                                       SCALE_HW_CALIB_DEFAULT)
> +       [ADC7_VPH_PWR]          = ADC5_CHAN_VOLT("vph_pwr", 1,
> +                                       SCALE_HW_CALIB_DEFAULT)
> +       [ADC7_VBAT_SNS]         = ADC5_CHAN_VOLT("vbat_sns", 3,
> +                                       SCALE_HW_CALIB_DEFAULT)
> +       [ADC7_DIE_TEMP]         = ADC5_CHAN_TEMP("die_temp", 0,
> +                                       SCALE_HW_CALIB_PMIC_THERM_PM7)
> +       [ADC7_AMUX_THM1_100K_PU]        = ADC5_CHAN_TEMP("amux_thm1_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_AMUX_THM2_100K_PU]        = ADC5_CHAN_TEMP("amux_thm2_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_AMUX_THM3_100K_PU]        = ADC5_CHAN_TEMP("amux_thm3_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_AMUX_THM4_100K_PU]        = ADC5_CHAN_TEMP("amux_thm4_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_AMUX_THM5_100K_PU]        = ADC5_CHAN_TEMP("amux_thm5_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_AMUX_THM6_100K_PU]        = ADC5_CHAN_TEMP("amux_thm6_pu2", 0,

Use space instead of tab for indentation in all the THMx channels to
make it look more in line.

> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_GPIO1_100K_PU]    = ADC5_CHAN_TEMP("gpio1_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_GPIO2_100K_PU]    = ADC5_CHAN_TEMP("gpio2_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_GPIO3_100K_PU]    = ADC5_CHAN_TEMP("gpio3_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +       [ADC7_GPIO4_100K_PU]    = ADC5_CHAN_TEMP("gpio4_pu2", 0,
> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
> +};
> +
>  static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
>         [ADC5_REF_GND]          = ADC5_CHAN_VOLT("ref_gnd", 0,
>                                         SCALE_HW_CALIB_DEFAULT)
> @@ -511,6 +696,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>  {
>         const char *name = node->name, *channel_name;
>         u32 chan, value, varr[2];
> +       u32 sid = 0;
>         int ret;
>         struct device *dev = adc->dev;
>
> @@ -520,6 +706,15 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>                 return ret;
>         }
>
> +       /* Value read from "reg" is virtual channel number */
> +
> +       /* virtual channel number = sid << 8 | channel number */
> +
> +       if (adc->data == &adc7_data_pmic) {
> +               sid = chan >> ADC_CHANNEL_OFFSET;
> +               chan = chan & ADC_CHANNEL_MASK;
> +       }
> +
>         if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
>             !data->adc_chans[chan].datasheet_name) {
>                 dev_err(dev, "%s invalid channel number %d\n", name, chan);
> @@ -528,6 +723,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>
>         /* the channel has DT description */
>         prop->channel = chan;
> +       prop->sid = sid;
>
>         channel_name = of_get_property(node,
>                                 "label", NULL) ? : node->name;
> @@ -578,8 +774,9 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>                 pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
>                                                 dig_version[1]);
>                 /* Digital controller >= 5.3 have hw_settle_2 option */
> -               if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
> -                       dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
> +               if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
> +                       dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
> +                       adc->data == &adc7_data_pmic)
>                         ret = adc5_hw_settle_time_from_dt(value,
>                                                         data->hw_settle_2);
>                 else
> @@ -629,6 +826,7 @@ static const struct adc5_data adc5_data_pmic = {
>         .full_scale_code_volt = 0x70e4,
>         .full_scale_code_cur = 0x2710,
>         .adc_chans = adc5_chans_pmic,
> +       .info = &adc5_info,

This belongs in a separate patch along with the change in
adc5_data_pmic_rev2 below

>         .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
>                                 {250, 420, 840},
>         .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
> @@ -639,10 +837,23 @@ static const struct adc5_data adc5_data_pmic = {
>                                 1, 2, 4, 8, 16, 32, 64, 128},
>  };
>
> +static const struct adc5_data adc7_data_pmic = {
> +       .full_scale_code_volt = 0x70e4,
> +       .adc_chans = adc7_chans_pmic,
> +       .info = &adc7_info,
> +       .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
> +                               {85, 340, 1360},
> +       .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
> +                               {15, 100, 200, 300, 400, 500, 600, 700,
> +                               1000, 2000, 4000, 8000, 16000, 32000,
> +                               64000, 128000},
> +};
> +
>  static const struct adc5_data adc5_data_pmic_rev2 = {
>         .full_scale_code_volt = 0x4000,
>         .full_scale_code_cur = 0x1800,
>         .adc_chans = adc5_chans_rev2,
> +       .info = &adc5_info,

Separate patch.

>         .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
>                                 {256, 512, 1024},
>         .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
> @@ -659,6 +870,10 @@ static const struct of_device_id adc5_match_table[] = {
>                 .data = &adc5_data_pmic,
>         },
>         {
> +               .compatible = "qcom,spmi-adc7",
> +               .data = &adc7_data_pmic,
> +       },
> +       {
>                 .compatible = "qcom,spmi-adc-rev2",
>                 .data = &adc5_data_pmic_rev2,
>         },
> @@ -752,6 +967,9 @@ static int adc5_probe(struct platform_device *pdev)
>         adc->regmap = regmap;
>         adc->dev = dev;
>         adc->base = reg;
> +
> +       platform_set_drvdata(pdev, adc);
> +
>         init_completion(&adc->complete);
>         mutex_init(&adc->lock);
>
> @@ -761,6 +979,8 @@ static int adc5_probe(struct platform_device *pdev)
>                 return ret;
>         }
>
> +       indio_dev->info = adc->data->info;
> +
>         irq_eoc = platform_get_irq(pdev, 0);
>         if (irq_eoc < 0) {
>                 if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
> @@ -773,23 +993,34 @@ static int adc5_probe(struct platform_device *pdev)
>                         return ret;
>         }
>
> +       adc->irq_eoc = irq_eoc;
> +

I think this belongs in a separate patch along with the adc5_exit
function below.

>         indio_dev->dev.parent = dev;
>         indio_dev->dev.of_node = node;
>         indio_dev->name = pdev->name;
>         indio_dev->modes = INDIO_DIRECT_MODE;
> -       indio_dev->info = &adc5_info;
>         indio_dev->channels = adc->iio_chans;
>         indio_dev->num_channels = adc->nchannels;
>
>         return devm_iio_device_register(dev, indio_dev);
>  }
>
> +static int adc5_exit(struct platform_device *pdev)
> +{
> +       struct adc5_chip *adc = platform_get_drvdata(pdev);
> +
> +       if (adc->irq_eoc >= 0)
> +               disable_irq(adc->irq_eoc);
> +       return 0;
> +}
> +
>  static struct platform_driver adc5_driver = {
>         .driver = {
>                 .name = "qcom-spmi-adc5.c",
>                 .of_match_table = adc5_match_table,
>         },
>         .probe = adc5_probe,
> +       .remove = adc5_exit,

This really belongs in a separate patch

>  };
>  module_platform_driver(adc5_driver);
>
> diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
> index 2bb78d1..b6915c2 100644
> --- a/drivers/iio/adc/qcom-vadc-common.c
> +++ b/drivers/iio/adc/qcom-vadc-common.c
> @@ -89,6 +89,195 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
>         { 46,   125000 },
>  };
>
> +static const struct vadc_map_pt adcmap7_die_temp[] = {
> +       { 433700, 1967},
> +       { 473100, 1964},
> +       { 512400, 1957},
> +       { 551500, 1949},
> +       { 590500, 1940},
> +       { 629300, 1930},
> +       { 667900, 1921},
> +       { 706400, 1910},
> +       { 744600, 1896},
> +       { 782500, 1878},
> +       { 820100, 1859},
> +       { 857300, 0},
> +};
> +
> +/*
> + * Resistance to temperature table for 100k pull up for NTCG104EF104.
> + */
> +static const struct vadc_map_pt adcmap7_100k[] = {
> +       { 4250657, -40960 },
> +       { 3962085, -39936 },
> +       { 3694875, -38912 },
> +       { 3447322, -37888 },
> +       { 3217867, -36864 },
> +       { 3005082, -35840 },
> +       { 2807660, -34816 },
> +       { 2624405, -33792 },
> +       { 2454218, -32768 },
> +       { 2296094, -31744 },
> +       { 2149108, -30720 },
> +       { 2012414, -29696 },
> +       { 1885232, -28672 },
> +       { 1766846, -27648 },
> +       { 1656598, -26624 },
> +       { 1553884, -25600 },
> +       { 1458147, -24576 },
> +       { 1368873, -23552 },
> +       { 1285590, -22528 },
> +       { 1207863, -21504 },
> +       { 1135290, -20480 },
> +       { 1067501, -19456 },
> +       { 1004155, -18432 },
> +       { 944935, -17408 },
> +       { 889550, -16384 },
> +       { 837731, -15360 },
> +       { 789229, -14336 },
> +       { 743813, -13312 },
> +       { 701271, -12288 },
> +       { 661405, -11264 },
> +       { 624032, -10240 },
> +       { 588982, -9216 },
> +       { 556100, -8192 },
> +       { 525239, -7168 },
> +       { 496264, -6144 },
> +       { 469050, -5120 },
> +       { 443480, -4096 },
> +       { 419448, -3072 },
> +       { 396851, -2048 },
> +       { 375597, -1024 },
> +       { 355598, 0 },
> +       { 336775, 1024 },
> +       { 319052, 2048 },
> +       { 302359, 3072 },
> +       { 286630, 4096 },
> +       { 271806, 5120 },
> +       { 257829, 6144 },
> +       { 244646, 7168 },
> +       { 232209, 8192 },
> +       { 220471, 9216 },
> +       { 209390, 10240 },
> +       { 198926, 11264 },
> +       { 189040, 12288 },
> +       { 179698, 13312 },
> +       { 170868, 14336 },
> +       { 162519, 15360 },
> +       { 154622, 16384 },
> +       { 147150, 17408 },
> +       { 140079, 18432 },
> +       { 133385, 19456 },
> +       { 127046, 20480 },
> +       { 121042, 21504 },
> +       { 115352, 22528 },
> +       { 109960, 23552 },
> +       { 104848, 24576 },
> +       { 100000, 25600 },
> +       { 95402, 26624 },
> +       { 91038, 27648 },
> +       { 86897, 28672 },
> +       { 82965, 29696 },
> +       { 79232, 30720 },
> +       { 75686, 31744 },
> +       { 72316, 32768 },
> +       { 69114, 33792 },
> +       { 66070, 34816 },
> +       { 63176, 35840 },
> +       { 60423, 36864 },
> +       { 57804, 37888 },
> +       { 55312, 38912 },
> +       { 52940, 39936 },
> +       { 50681, 40960 },
> +       { 48531, 41984 },
> +       { 46482, 43008 },
> +       { 44530, 44032 },
> +       { 42670, 45056 },
> +       { 40897, 46080 },
> +       { 39207, 47104 },
> +       { 37595, 48128 },
> +       { 36057, 49152 },
> +       { 34590, 50176 },
> +       { 33190, 51200 },
> +       { 31853, 52224 },
> +       { 30577, 53248 },
> +       { 29358, 54272 },
> +       { 28194, 55296 },
> +       { 27082, 56320 },
> +       { 26020, 57344 },
> +       { 25004, 58368 },
> +       { 24033, 59392 },
> +       { 23104, 60416 },
> +       { 22216, 61440 },
> +       { 21367, 62464 },
> +       { 20554, 63488 },
> +       { 19776, 64512 },
> +       { 19031, 65536 },
> +       { 18318, 66560 },
> +       { 17636, 67584 },
> +       { 16982, 68608 },
> +       { 16355, 69632 },
> +       { 15755, 70656 },
> +       { 15180, 71680 },
> +       { 14628, 72704 },
> +       { 14099, 73728 },
> +       { 13592, 74752 },
> +       { 13106, 75776 },
> +       { 12640, 76800 },
> +       { 12192, 77824 },
> +       { 11762, 78848 },
> +       { 11350, 79872 },
> +       { 10954, 80896 },
> +       { 10574, 81920 },
> +       { 10209, 82944 },
> +       { 9858, 83968 },
> +       { 9521, 84992 },
> +       { 9197, 86016 },
> +       { 8886, 87040 },
> +       { 8587, 88064 },
> +       { 8299, 89088 },
> +       { 8023, 90112 },
> +       { 7757, 91136 },
> +       { 7501, 92160 },
> +       { 7254, 93184 },
> +       { 7017, 94208 },
> +       { 6789, 95232 },
> +       { 6570, 96256 },
> +       { 6358, 97280 },
> +       { 6155, 98304 },
> +       { 5959, 99328 },
> +       { 5770, 100352 },
> +       { 5588, 101376 },
> +       { 5412, 102400 },
> +       { 5243, 103424 },
> +       { 5080, 104448 },
> +       { 4923, 105472 },
> +       { 4771, 106496 },
> +       { 4625, 107520 },
> +       { 4484, 108544 },
> +       { 4348, 109568 },
> +       { 4217, 110592 },
> +       { 4090, 111616 },
> +       { 3968, 112640 },
> +       { 3850, 113664 },
> +       { 3736, 114688 },
> +       { 3626, 115712 },
> +       { 3519, 116736 },
> +       { 3417, 117760 },
> +       { 3317, 118784 },
> +       { 3221, 119808 },
> +       { 3129, 120832 },
> +       { 3039, 121856 },
> +       { 2952, 122880 },
> +       { 2868, 123904 },
> +       { 2787, 124928 },
> +       { 2709, 125952 },
> +       { 2633, 126976 },
> +       { 2560, 128000 },
> +       { 2489, 129024 },
> +       { 2420, 130048 }
> +};
> +
>  static int qcom_vadc_scale_hw_calib_volt(
>                                 const struct vadc_prescale_ratio *prescale,
>                                 const struct adc5_data *data,
> @@ -97,6 +286,10 @@ static int qcom_vadc_scale_hw_calib_therm(
>                                 const struct vadc_prescale_ratio *prescale,
>                                 const struct adc5_data *data,
>                                 u16 adc_code, int *result_mdec);
> +static int qcom_vadc7_scale_hw_calib_therm(
> +                               const struct vadc_prescale_ratio *prescale,
> +                               const struct adc5_data *data,
> +                               u16 adc_code, int *result_mdec);
>  static int qcom_vadc_scale_hw_smb_temp(
>                                 const struct vadc_prescale_ratio *prescale,
>                                 const struct adc5_data *data,
> @@ -109,12 +302,20 @@ static int qcom_vadc_scale_hw_calib_die_temp(
>                                 const struct vadc_prescale_ratio *prescale,
>                                 const struct adc5_data *data,
>                                 u16 adc_code, int *result_mdec);
> +static int qcom_vadc7_scale_hw_calib_die_temp(
> +                               const struct vadc_prescale_ratio *prescale,
> +                               const struct adc5_data *data,
> +                               u16 adc_code, int *result_mdec);
>
>  static struct qcom_adc5_scale_type scale_adc5_fn[] = {
>         [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
>         [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
>         [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
> +       [SCALE_HW_CALIB_THERM_100K_PU_PM7] = {
> +                                       qcom_vadc7_scale_hw_calib_therm},
>         [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
> +       [SCALE_HW_CALIB_PMIC_THERM_PM7] = {
> +                                       qcom_vadc7_scale_hw_calib_die_temp},
>         [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
>         [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
>  };
> @@ -291,6 +492,32 @@ static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
>         return (int) voltage;
>  }
>
> +static int qcom_vadc7_scale_hw_calib_therm(
> +                               const struct vadc_prescale_ratio *prescale,
> +                               const struct adc5_data *data,
> +                               u16 adc_code, int *result_mdec)
> +{
> +       s64 resistance = adc_code;
> +       int ret, result;
> +
> +       if (adc_code >= RATIO_MAX_ADC7)
> +               return -EINVAL;
> +
> +       /* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
> +       resistance *= R_PU_100K;
> +       resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code);
> +
> +       ret = qcom_vadc_map_voltage_temp(adcmap7_100k,
> +                                ARRAY_SIZE(adcmap7_100k),
> +                                resistance, &result);
> +       if (ret)
> +               return ret;
> +
> +       *result_mdec = result;
> +
> +       return 0;
> +}
> +
>  static int qcom_vadc_scale_hw_calib_volt(
>                                 const struct vadc_prescale_ratio *prescale,
>                                 const struct adc5_data *data,
> @@ -330,6 +557,39 @@ static int qcom_vadc_scale_hw_calib_die_temp(
>         return 0;
>  }
>
> +static int qcom_vadc7_scale_hw_calib_die_temp(
> +                               const struct vadc_prescale_ratio *prescale,
> +                               const struct adc5_data *data,
> +                               u16 adc_code, int *result_mdec)
> +{
> +
> +       int voltage, vtemp0, temp, i = ARRAY_SIZE(adcmap7_die_temp) - 1;
> +
> +       voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
> +                               prescale, data, 1);
> +
> +       if (adcmap7_die_temp[0].x > voltage) {
> +               *result_mdec = DIE_TEMP_ADC7_SCALE_1;
> +               return 0;
> +       } else if (adcmap7_die_temp[i].x <= voltage) {
> +               *result_mdec = DIE_TEMP_ADC7_MAX;
> +               return 0;
> +       }
> +
> +       for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++)
> +               if (adcmap7_die_temp[i].x > voltage)
> +                       break;
> +
> +       vtemp0 = adcmap7_die_temp[i - 1].x;
> +       voltage = voltage - vtemp0;
> +       temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
> +               adcmap7_die_temp[i - 1].y);
> +       temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1));
> +       *result_mdec = temp;
> +
> +       return 0;
> +}
> +
>  static int qcom_vadc_scale_hw_smb_temp(
>                                 const struct vadc_prescale_ratio *prescale,
>                                 const struct adc5_data *data,
> diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
> index e074902a..d195e83 100644
> --- a/drivers/iio/adc/qcom-vadc-common.h
> +++ b/drivers/iio/adc/qcom-vadc-common.h
> @@ -49,6 +49,14 @@
>  #define ADC5_FULL_SCALE_CODE                   0x70e4
>  #define ADC5_USR_DATA_CHECK                    0x8000
>
> +#define R_PU_100K                      100000
> +#define RATIO_MAX_ADC7         0x4000
> +
> +#define DIE_TEMP_ADC7_SCALE_1                          -60000
> +#define DIE_TEMP_ADC7_SCALE_2                          20000
> +#define DIE_TEMP_ADC7_SCALE_FACTOR                     1000
> +#define DIE_TEMP_ADC7_MAX                              160000
> +
>  /**
>   * struct vadc_map_pt - Map the graph representation for ADC channel
>   * @x: Represent the ADC digitized code.
> @@ -110,8 +118,12 @@ struct vadc_prescale_ratio {
>   *     lookup table. The hardware applies offset/slope to adc code.
>   * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
>   *     100k pullup. The hardware applies offset/slope to adc code.
> + * SCALE_HW_CALIB_THERM_100K_PU_PM7: Returns temperature in millidegC using
> + *     lookup table for PMIC7. The hardware applies offset/slope to adc code.
>   * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
>   *     The hardware applies offset/slope to adc code.
> + * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
> + *     The hardware applies offset/slope to adc code. This is for PMIC7.
>   * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
>   *     charger temperature.
>   * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
> @@ -126,7 +138,9 @@ enum vadc_scale_fn_type {
>         SCALE_HW_CALIB_DEFAULT,
>         SCALE_HW_CALIB_THERM_100K_PULLUP,
>         SCALE_HW_CALIB_XOTHERM,
> +       SCALE_HW_CALIB_THERM_100K_PU_PM7,
>         SCALE_HW_CALIB_PMIC_THERM,
> +       SCALE_HW_CALIB_PMIC_THERM_PM7,
>         SCALE_HW_CALIB_PM5_CHG_TEMP,
>         SCALE_HW_CALIB_PM5_SMB_TEMP,
>         SCALE_HW_CALIB_INVALID,
> @@ -136,6 +150,7 @@ struct adc5_data {
>         const u32       full_scale_code_volt;
>         const u32       full_scale_code_cur;
>         const struct adc5_channels *adc_chans;
> +       const struct iio_info *info;
>         unsigned int    *decimation;
>         unsigned int    *hw_settle_1;
>         unsigned int    *hw_settle_2;
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
Jishnu Prakash May 13, 2020, 9:20 a.m. UTC | #2
Hi Amit,

On 5/5/2020 3:33 PM, Amit Kucheria wrote:
> Hi Jishnu,
>
> It is nice to cc a reviewer from a previous version. Just saw this.
>
> On Mon, Apr 27, 2020 at 6:55 PM Jishnu Prakash <jprakash@codeaurora.org> wrote:
>> The ADC architecture on PMIC7 is changed as compared to PMIC5. The
>> major change from PMIC5 is that all SW communication to ADC goes through
>> PMK8350, which communicates with other PMICs through PBS when the ADC
>> on PMK8350 works in master mode. The SID register is used to identify the
>> PMICs with which the PBS needs to communicate. Add support for the same.
>>
>> Signed-off-by: Jishnu Prakash <jprakash@codeaurora.org>
>> ---
>>   drivers/iio/adc/qcom-spmi-adc5.c   | 239 +++++++++++++++++++++++++++++++++-
>>   drivers/iio/adc/qcom-vadc-common.c | 260 +++++++++++++++++++++++++++++++++++++
>>   drivers/iio/adc/qcom-vadc-common.h |  15 +++
>>   3 files changed, 510 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
>> index 21fdcde..a66eeb7 100644
>> --- a/drivers/iio/adc/qcom-spmi-adc5.c
>> +++ b/drivers/iio/adc/qcom-spmi-adc5.c
>> @@ -1,6 +1,6 @@
>>   // SPDX-License-Identifier: GPL-2.0
>>   /*
>> - * Copyright (c) 2018, The Linux Foundation. All rights reserved.
>> + * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
>>    */
>>
>>   #include <linux/bitops.h>
>> @@ -23,6 +23,7 @@
>>
>>   #define ADC5_USR_REVISION1                     0x0
>>   #define ADC5_USR_STATUS1                       0x8
>> +#define ADC5_USR_STATUS1_CONV_FAULT            BIT(7)
>>   #define ADC5_USR_STATUS1_REQ_STS               BIT(1)
>>   #define ADC5_USR_STATUS1_EOC                   BIT(0)
>>   #define ADC5_USR_STATUS1_REQ_STS_EOC_MASK      0x3
>> @@ -65,6 +66,9 @@
>>
>>   #define ADC5_USR_IBAT_DATA1                    0x53
>>
>> +#define ADC_CHANNEL_OFFSET                     0x8
>> +#define ADC_CHANNEL_MASK                       GENMASK(7, 0)
>> +
>>   /*
>>    * Conversion time varies based on the decimation, clock rate, fast average
>>    * samples and measurements queued across different VADC peripherals.
>> @@ -79,6 +83,11 @@
>>   #define ADC5_HW_SETTLE_DIFF_MINOR              3
>>   #define ADC5_HW_SETTLE_DIFF_MAJOR              5
>>
>> +/* For PMIC7 */
>> +#define ADC_APP_SID                            0x40
>> +#define ADC_APP_SID_MASK                       GENMASK(3, 0)
>> +#define ADC7_CONV_TIMEOUT                      msecs_to_jiffies(10)
>> +
>>   enum adc5_cal_method {
>>          ADC5_NO_CAL = 0,
>>          ADC5_RATIOMETRIC_CAL,
>> @@ -96,6 +105,7 @@ enum adc5_cal_val {
>>    * @cal_method: calibration method.
>>    * @cal_val: calibration value
>>    * @decimation: sampling rate supported for the channel.
>> + * @sid: slave id of PMIC owning the channel, for PMIC7.
>>    * @prescale: channel scaling performed on the input signal.
>>    * @hw_settle_time: the time between AMUX being configured and the
>>    *     start of conversion.
>> @@ -110,6 +120,7 @@ struct adc5_channel_prop {
>>          enum adc5_cal_method    cal_method;
>>          enum adc5_cal_val       cal_val;
>>          unsigned int            decimation;
>> +       unsigned int            sid;
>>          unsigned int            prescale;
>>          unsigned int            hw_settle_time;
>>          unsigned int            avg_samples;
>> @@ -140,6 +151,7 @@ struct adc5_chip {
>>          bool                    poll_eoc;
>>          struct completion       complete;
>>          struct mutex            lock;
>> +       int                     irq_eoc;
>>          const struct adc5_data  *data;
>>   };
>>
>> @@ -155,6 +167,8 @@ static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
>>          {.num =  1, .den = 16}
>>   };
>>
>> +static const struct adc5_data adc7_data_pmic;
>> +
>>   static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
>>   {
>>          return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
>> @@ -165,6 +179,11 @@ static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
>>          return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
>>   }
>>
>> +static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
>> +{
>> +       return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
>> +}
>> +
>>   static int adc5_prescaling_from_dt(u32 num, u32 den)
>>   {
>>          unsigned int pre;
>> @@ -314,6 +333,47 @@ static int adc5_configure(struct adc5_chip *adc,
>>          return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
>>   }
>>
>> +static int adc7_configure(struct adc5_chip *adc,
>> +                       struct adc5_channel_prop *prop)
>> +{
>> +       int ret;
>> +       u8 conv_req = 0, buf[4];
>> +
>> +       ret = adc5_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
>> +       if (ret < 0)
>> +               return ret;
>> +
>> +       /* Digital param selection */
>> +       adc5_update_dig_param(adc, prop, &buf[0]);
>> +
>> +       /* Update fast average sample value */
>> +       buf[1] &= 0xff & ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
> Did you answer Andy's query about this in v2? I couldn't find it.
I missed it at earlier, I have replied now and will make the change in 
my latest post.
>
>> +       buf[1] |= prop->avg_samples;
>> +
>> +       /* Select ADC channel */
>> +       buf[2] = prop->channel;
>> +
>> +       /* Select HW settle delay for channel */
>> +       buf[3] &= 0xff & ~ADC5_USR_HW_SETTLE_DELAY_MASK;
> Same here.
I missed it at earlier, I have replied now and will make the change in 
my latest post.
>
>> +       buf[3] |= prop->hw_settle_time;
>> +
>> +       /* Select CONV request */
>> +       conv_req = ADC5_USR_CONV_REQ_REQ;
>> +
>> +       if (!adc->poll_eoc)
>> +               reinit_completion(&adc->complete);
>> +
>> +       ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
>> +       if (ret)
>> +               return ret;
>> +
>> +       return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1);
> Is it required to call adc5_write() twice here in sequence on the
> adc7? adc5_configure() above manages just fine by having an extra
> element in buf and one call.
In PMIC5, that write is used to write to registers 0x42-0x47. Register 
0x46 has been removed for PMIC7 ADC, so we use two writes, for 0x42-0x45 
and for 0x47 alone.
>
>> +}
>> +
>>   static int adc5_do_conversion(struct adc5_chip *adc,
>>                          struct adc5_channel_prop *prop,
>>                          struct iio_chan_spec const *chan,
>> @@ -355,6 +415,43 @@ static int adc5_do_conversion(struct adc5_chip *adc,
>>          return ret;
>>   }
>>
>> +static int adc7_do_conversion(struct adc5_chip *adc,
>> +                       struct adc5_channel_prop *prop,
>> +                       struct iio_chan_spec const *chan,
>> +                       u16 *data_volt, u16 *data_cur)
>> +{
>> +       int ret;
>> +       u8 status;
>> +
>> +       mutex_lock(&adc->lock);
>> +
>> +       ret = adc7_configure(adc, prop);
>> +       if (ret) {
>> +               dev_err(adc->dev, "ADC configure failed with %d\n", ret);
>> +               goto unlock;
>> +       }
>> +
>> +       /* No support for polling mode at present */
>> +       wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT);
>> +
>> +       ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
>> +       if (ret < 0)
>> +               goto unlock;
>> +
>> +       if (status & ADC5_USR_STATUS1_CONV_FAULT) {
>> +               dev_err(adc->dev, "Unexpected conversion fault\n");
>> +               ret = -EIO;
>> +               goto unlock;
>> +       }
>> +
>> +       ret = adc5_read_voltage_data(adc, data_volt);
>> +
>> +unlock:
>> +       mutex_unlock(&adc->lock);
>> +
>> +       return ret;
>> +}
>> +
>>   static irqreturn_t adc5_isr(int irq, void *dev_id)
>>   {
>>          struct adc5_chip *adc = dev_id;
>> @@ -377,6 +474,56 @@ static int adc5_of_xlate(struct iio_dev *indio_dev,
>>          return -EINVAL;
>>   }
>>
>> +static int adc7_of_xlate(struct iio_dev *indio_dev,
>> +                               const struct of_phandle_args *iiospec)
>> +{
>> +       struct adc5_chip *adc = iio_priv(indio_dev);
>> +       int i, v_channel;
>> +
>> +       for (i = 0; i < adc->nchannels; i++) {
>> +               v_channel = (adc->chan_props[i].sid << ADC_CHANNEL_OFFSET |
>> +                       adc->chan_props[i].channel);
>> +               if (v_channel == iiospec->args[0])
>> +                       return i;
>> +       }
>> +
>> +       return -EINVAL;
>> +}
>> +
>> +static int adc7_read_raw(struct iio_dev *indio_dev,
>> +                        struct iio_chan_spec const *chan, int *val, int *val2,
>> +                        long mask)
> Just a nit. Since all your adc7 functions follow your adc5 functions,
> please move this below adc5_read_raw for consistency.
I'll do it in the next post.
>
>> +{
>> +       struct adc5_chip *adc = iio_priv(indio_dev);
>> +       struct adc5_channel_prop *prop;
>> +       u16 adc_code_volt, adc_code_cur;
>> +       int ret;
>> +
>> +       prop = &adc->chan_props[chan->address];
>> +
>> +       switch (mask) {
>> +       case IIO_CHAN_INFO_PROCESSED:
>> +               ret = adc7_do_conversion(adc, prop, chan,
> Except for this bit (call to adc7_do_conversion instead of
> adc5_do_conversion), this function is identical to adc5_read_raw.
>
> How about making adc5_read_raw and adc7_read_raw a shim around a
> function adc_read_raw_common() that takes a function pointer?
I'll do this in the next post.
>
>> +                                       &adc_code_volt, &adc_code_cur);
>> +               if (ret)
>> +                       return ret;
>> +
>> +               ret = qcom_adc5_hw_scale(prop->scale_fn_type,
>> +                       &adc5_prescale_ratios[prop->prescale],
>> +                       adc->data,
>> +                       adc_code_volt, val);
>> +
>> +               if (ret)
>> +                       return ret;
>> +
>> +               return IIO_VAL_INT;
>> +       default:
>> +               return -EINVAL;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>>   static int adc5_read_raw(struct iio_dev *indio_dev,
>>                           struct iio_chan_spec const *chan, int *val, int *val2,
>>                           long mask)
>> @@ -415,6 +562,11 @@ static const struct iio_info adc5_info = {
>>          .of_xlate = adc5_of_xlate,
>>   };
>>
>> +static const struct iio_info adc7_info = {
>> +       .read_raw = adc7_read_raw,
>> +       .of_xlate = adc7_of_xlate,
>> +};
>> +
>>   struct adc5_channels {
>>          const char *datasheet_name;
>>          unsigned int prescale_index;
>> @@ -477,6 +629,39 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
>>                                          SCALE_HW_CALIB_PM5_SMB_TEMP)
>>   };
>>
>> +static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
>> +       [ADC7_REF_GND]          = ADC5_CHAN_VOLT("ref_gnd", 0,
>> +                                       SCALE_HW_CALIB_DEFAULT)
>> +       [ADC7_1P25VREF]         = ADC5_CHAN_VOLT("vref_1p25", 0,
>> +                                       SCALE_HW_CALIB_DEFAULT)
>> +       [ADC7_VPH_PWR]          = ADC5_CHAN_VOLT("vph_pwr", 1,
>> +                                       SCALE_HW_CALIB_DEFAULT)
>> +       [ADC7_VBAT_SNS]         = ADC5_CHAN_VOLT("vbat_sns", 3,
>> +                                       SCALE_HW_CALIB_DEFAULT)
>> +       [ADC7_DIE_TEMP]         = ADC5_CHAN_TEMP("die_temp", 0,
>> +                                       SCALE_HW_CALIB_PMIC_THERM_PM7)
>> +       [ADC7_AMUX_THM1_100K_PU]        = ADC5_CHAN_TEMP("amux_thm1_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_AMUX_THM2_100K_PU]        = ADC5_CHAN_TEMP("amux_thm2_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_AMUX_THM3_100K_PU]        = ADC5_CHAN_TEMP("amux_thm3_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_AMUX_THM4_100K_PU]        = ADC5_CHAN_TEMP("amux_thm4_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_AMUX_THM5_100K_PU]        = ADC5_CHAN_TEMP("amux_thm5_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_AMUX_THM6_100K_PU]        = ADC5_CHAN_TEMP("amux_thm6_pu2", 0,
> Use space instead of tab for indentation in all the THMx channels to
> make it look more in line.
I'll do this in the next post.
>
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_GPIO1_100K_PU]    = ADC5_CHAN_TEMP("gpio1_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_GPIO2_100K_PU]    = ADC5_CHAN_TEMP("gpio2_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_GPIO3_100K_PU]    = ADC5_CHAN_TEMP("gpio3_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +       [ADC7_GPIO4_100K_PU]    = ADC5_CHAN_TEMP("gpio4_pu2", 0,
>> +                                       SCALE_HW_CALIB_THERM_100K_PU_PM7)
>> +};
>> +
>>   static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
>>          [ADC5_REF_GND]          = ADC5_CHAN_VOLT("ref_gnd", 0,
>>                                          SCALE_HW_CALIB_DEFAULT)
>> @@ -511,6 +696,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>>   {
>>          const char *name = node->name, *channel_name;
>>          u32 chan, value, varr[2];
>> +       u32 sid = 0;
>>          int ret;
>>          struct device *dev = adc->dev;
>>
>> @@ -520,6 +706,15 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>>                  return ret;
>>          }
>>
>> +       /* Value read from "reg" is virtual channel number */
>> +
>> +       /* virtual channel number = sid << 8 | channel number */
>> +
>> +       if (adc->data == &adc7_data_pmic) {
>> +               sid = chan >> ADC_CHANNEL_OFFSET;
>> +               chan = chan & ADC_CHANNEL_MASK;
>> +       }
>> +
>>          if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
>>              !data->adc_chans[chan].datasheet_name) {
>>                  dev_err(dev, "%s invalid channel number %d\n", name, chan);
>> @@ -528,6 +723,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>>
>>          /* the channel has DT description */
>>          prop->channel = chan;
>> +       prop->sid = sid;
>>
>>          channel_name = of_get_property(node,
>>                                  "label", NULL) ? : node->name;
>> @@ -578,8 +774,9 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
>>                  pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
>>                                                  dig_version[1]);
>>                  /* Digital controller >= 5.3 have hw_settle_2 option */
>> -               if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
>> -                       dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
>> +               if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
>> +                       dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
>> +                       adc->data == &adc7_data_pmic)
>>                          ret = adc5_hw_settle_time_from_dt(value,
>>                                                          data->hw_settle_2);
>>                  else
>> @@ -629,6 +826,7 @@ static const struct adc5_data adc5_data_pmic = {
>>          .full_scale_code_volt = 0x70e4,
>>          .full_scale_code_cur = 0x2710,
>>          .adc_chans = adc5_chans_pmic,
>> +       .info = &adc5_info,
> This belongs in a separate patch along with the change in
> adc5_data_pmic_rev2 below
I'll add a separate patch for this in the next post.
>
>>          .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
>>                                  {250, 420, 840},
>>          .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
>> @@ -639,10 +837,23 @@ static const struct adc5_data adc5_data_pmic = {
>>                                  1, 2, 4, 8, 16, 32, 64, 128},
>>   };
>>
>> +static const struct adc5_data adc7_data_pmic = {
>> +       .full_scale_code_volt = 0x70e4,
>> +       .adc_chans = adc7_chans_pmic,
>> +       .info = &adc7_info,
>> +       .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
>> +                               {85, 340, 1360},
>> +       .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
>> +                               {15, 100, 200, 300, 400, 500, 600, 700,
>> +                               1000, 2000, 4000, 8000, 16000, 32000,
>> +                               64000, 128000},
>> +};
>> +
>>   static const struct adc5_data adc5_data_pmic_rev2 = {
>>          .full_scale_code_volt = 0x4000,
>>          .full_scale_code_cur = 0x1800,
>>          .adc_chans = adc5_chans_rev2,
>> +       .info = &adc5_info,
> Separate patch.
I'll add a separate patch for this in the next post.
>
>>          .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
>>                                  {256, 512, 1024},
>>          .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
>> @@ -659,6 +870,10 @@ static const struct of_device_id adc5_match_table[] = {
>>                  .data = &adc5_data_pmic,
>>          },
>>          {
>> +               .compatible = "qcom,spmi-adc7",
>> +               .data = &adc7_data_pmic,
>> +       },
>> +       {
>>                  .compatible = "qcom,spmi-adc-rev2",
>>                  .data = &adc5_data_pmic_rev2,
>>          },
>> @@ -752,6 +967,9 @@ static int adc5_probe(struct platform_device *pdev)
>>          adc->regmap = regmap;
>>          adc->dev = dev;
>>          adc->base = reg;
>> +
>> +       platform_set_drvdata(pdev, adc);
>> +
>>          init_completion(&adc->complete);
>>          mutex_init(&adc->lock);
>>
>> @@ -761,6 +979,8 @@ static int adc5_probe(struct platform_device *pdev)
>>                  return ret;
>>          }
>>
>> +       indio_dev->info = adc->data->info;
>> +
>>          irq_eoc = platform_get_irq(pdev, 0);
>>          if (irq_eoc < 0) {
>>                  if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
>> @@ -773,23 +993,34 @@ static int adc5_probe(struct platform_device *pdev)
>>                          return ret;
>>          }
>>
>> +       adc->irq_eoc = irq_eoc;
>> +
> I think this belongs in a separate patch along with the adc5_exit
> function below.
I'll add a separate patch for this in the next post.
>
>>          indio_dev->dev.parent = dev;
>>          indio_dev->dev.of_node = node;
>>          indio_dev->name = pdev->name;
>>          indio_dev->modes = INDIO_DIRECT_MODE;
>> -       indio_dev->info = &adc5_info;
>>          indio_dev->channels = adc->iio_chans;
>>          indio_dev->num_channels = adc->nchannels;
>>
>>          return devm_iio_device_register(dev, indio_dev);
>>   }
>>
>> +static int adc5_exit(struct platform_device *pdev)
>> +{
>> +       struct adc5_chip *adc = platform_get_drvdata(pdev);
>> +
>> +       if (adc->irq_eoc >= 0)
>> +               disable_irq(adc->irq_eoc);
>> +       return 0;
>> +}
>> +
>>   static struct platform_driver adc5_driver = {
>>          .driver = {
>>                  .name = "qcom-spmi-adc5.c",
>>                  .of_match_table = adc5_match_table,
>>          },
>>          .probe = adc5_probe,
>> +       .remove = adc5_exit,
> This really belongs in a separate patch
I'll add a separate patch for this and the above changes mentioned in 
the next post.
>
>>   };
>>   module_platform_driver(adc5_driver);
>>
>>
diff mbox series

Patch

diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index 21fdcde..a66eeb7 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -1,6 +1,6 @@ 
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/bitops.h>
@@ -23,6 +23,7 @@ 
 
 #define ADC5_USR_REVISION1			0x0
 #define ADC5_USR_STATUS1			0x8
+#define ADC5_USR_STATUS1_CONV_FAULT		BIT(7)
 #define ADC5_USR_STATUS1_REQ_STS		BIT(1)
 #define ADC5_USR_STATUS1_EOC			BIT(0)
 #define ADC5_USR_STATUS1_REQ_STS_EOC_MASK	0x3
@@ -65,6 +66,9 @@ 
 
 #define ADC5_USR_IBAT_DATA1			0x53
 
+#define ADC_CHANNEL_OFFSET			0x8
+#define ADC_CHANNEL_MASK			GENMASK(7, 0)
+
 /*
  * Conversion time varies based on the decimation, clock rate, fast average
  * samples and measurements queued across different VADC peripherals.
@@ -79,6 +83,11 @@ 
 #define ADC5_HW_SETTLE_DIFF_MINOR		3
 #define ADC5_HW_SETTLE_DIFF_MAJOR		5
 
+/* For PMIC7 */
+#define ADC_APP_SID				0x40
+#define ADC_APP_SID_MASK			GENMASK(3, 0)
+#define ADC7_CONV_TIMEOUT			msecs_to_jiffies(10)
+
 enum adc5_cal_method {
 	ADC5_NO_CAL = 0,
 	ADC5_RATIOMETRIC_CAL,
@@ -96,6 +105,7 @@  enum adc5_cal_val {
  * @cal_method: calibration method.
  * @cal_val: calibration value
  * @decimation: sampling rate supported for the channel.
+ * @sid: slave id of PMIC owning the channel, for PMIC7.
  * @prescale: channel scaling performed on the input signal.
  * @hw_settle_time: the time between AMUX being configured and the
  *	start of conversion.
@@ -110,6 +120,7 @@  struct adc5_channel_prop {
 	enum adc5_cal_method	cal_method;
 	enum adc5_cal_val	cal_val;
 	unsigned int		decimation;
+	unsigned int		sid;
 	unsigned int		prescale;
 	unsigned int		hw_settle_time;
 	unsigned int		avg_samples;
@@ -140,6 +151,7 @@  struct adc5_chip {
 	bool			poll_eoc;
 	struct completion	complete;
 	struct mutex		lock;
+	int			irq_eoc;
 	const struct adc5_data	*data;
 };
 
@@ -155,6 +167,8 @@  static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
 	{.num =  1, .den = 16}
 };
 
+static const struct adc5_data adc7_data_pmic;
+
 static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
 {
 	return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
@@ -165,6 +179,11 @@  static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
 	return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
 }
 
+static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val)
+{
+	return regmap_update_bits(adc->regmap, adc->base + offset, mask, val);
+}
+
 static int adc5_prescaling_from_dt(u32 num, u32 den)
 {
 	unsigned int pre;
@@ -314,6 +333,47 @@  static int adc5_configure(struct adc5_chip *adc,
 	return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
 }
 
+static int adc7_configure(struct adc5_chip *adc,
+			struct adc5_channel_prop *prop)
+{
+	int ret;
+	u8 conv_req = 0, buf[4];
+
+	ret = adc5_masked_write(adc, ADC_APP_SID, ADC_APP_SID_MASK, prop->sid);
+	if (ret)
+		return ret;
+
+	ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+	if (ret < 0)
+		return ret;
+
+	/* Digital param selection */
+	adc5_update_dig_param(adc, prop, &buf[0]);
+
+	/* Update fast average sample value */
+	buf[1] &= 0xff & ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
+	buf[1] |= prop->avg_samples;
+
+	/* Select ADC channel */
+	buf[2] = prop->channel;
+
+	/* Select HW settle delay for channel */
+	buf[3] &= 0xff & ~ADC5_USR_HW_SETTLE_DELAY_MASK;
+	buf[3] |= prop->hw_settle_time;
+
+	/* Select CONV request */
+	conv_req = ADC5_USR_CONV_REQ_REQ;
+
+	if (!adc->poll_eoc)
+		reinit_completion(&adc->complete);
+
+	ret = adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+	if (ret)
+		return ret;
+
+	return adc5_write(adc, ADC5_USR_CONV_REQ, &conv_req, 1);
+}
+
 static int adc5_do_conversion(struct adc5_chip *adc,
 			struct adc5_channel_prop *prop,
 			struct iio_chan_spec const *chan,
@@ -355,6 +415,43 @@  static int adc5_do_conversion(struct adc5_chip *adc,
 	return ret;
 }
 
+static int adc7_do_conversion(struct adc5_chip *adc,
+			struct adc5_channel_prop *prop,
+			struct iio_chan_spec const *chan,
+			u16 *data_volt, u16 *data_cur)
+{
+	int ret;
+	u8 status;
+
+	mutex_lock(&adc->lock);
+
+	ret = adc7_configure(adc, prop);
+	if (ret) {
+		dev_err(adc->dev, "ADC configure failed with %d\n", ret);
+		goto unlock;
+	}
+
+	/* No support for polling mode at present */
+	wait_for_completion_timeout(&adc->complete, ADC7_CONV_TIMEOUT);
+
+	ret = adc5_read(adc, ADC5_USR_STATUS1, &status, 1);
+	if (ret < 0)
+		goto unlock;
+
+	if (status & ADC5_USR_STATUS1_CONV_FAULT) {
+		dev_err(adc->dev, "Unexpected conversion fault\n");
+		ret = -EIO;
+		goto unlock;
+	}
+
+	ret = adc5_read_voltage_data(adc, data_volt);
+
+unlock:
+	mutex_unlock(&adc->lock);
+
+	return ret;
+}
+
 static irqreturn_t adc5_isr(int irq, void *dev_id)
 {
 	struct adc5_chip *adc = dev_id;
@@ -377,6 +474,56 @@  static int adc5_of_xlate(struct iio_dev *indio_dev,
 	return -EINVAL;
 }
 
+static int adc7_of_xlate(struct iio_dev *indio_dev,
+				const struct of_phandle_args *iiospec)
+{
+	struct adc5_chip *adc = iio_priv(indio_dev);
+	int i, v_channel;
+
+	for (i = 0; i < adc->nchannels; i++) {
+		v_channel = (adc->chan_props[i].sid << ADC_CHANNEL_OFFSET |
+			adc->chan_props[i].channel);
+		if (v_channel == iiospec->args[0])
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+static int adc7_read_raw(struct iio_dev *indio_dev,
+			 struct iio_chan_spec const *chan, int *val, int *val2,
+			 long mask)
+{
+	struct adc5_chip *adc = iio_priv(indio_dev);
+	struct adc5_channel_prop *prop;
+	u16 adc_code_volt, adc_code_cur;
+	int ret;
+
+	prop = &adc->chan_props[chan->address];
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		ret = adc7_do_conversion(adc, prop, chan,
+					&adc_code_volt, &adc_code_cur);
+		if (ret)
+			return ret;
+
+		ret = qcom_adc5_hw_scale(prop->scale_fn_type,
+			&adc5_prescale_ratios[prop->prescale],
+			adc->data,
+			adc_code_volt, val);
+
+		if (ret)
+			return ret;
+
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int adc5_read_raw(struct iio_dev *indio_dev,
 			 struct iio_chan_spec const *chan, int *val, int *val2,
 			 long mask)
@@ -415,6 +562,11 @@  static const struct iio_info adc5_info = {
 	.of_xlate = adc5_of_xlate,
 };
 
+static const struct iio_info adc7_info = {
+	.read_raw = adc7_read_raw,
+	.of_xlate = adc7_of_xlate,
+};
+
 struct adc5_channels {
 	const char *datasheet_name;
 	unsigned int prescale_index;
@@ -477,6 +629,39 @@  static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
 					SCALE_HW_CALIB_PM5_SMB_TEMP)
 };
 
+static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
+	[ADC7_REF_GND]		= ADC5_CHAN_VOLT("ref_gnd", 0,
+					SCALE_HW_CALIB_DEFAULT)
+	[ADC7_1P25VREF]		= ADC5_CHAN_VOLT("vref_1p25", 0,
+					SCALE_HW_CALIB_DEFAULT)
+	[ADC7_VPH_PWR]		= ADC5_CHAN_VOLT("vph_pwr", 1,
+					SCALE_HW_CALIB_DEFAULT)
+	[ADC7_VBAT_SNS]		= ADC5_CHAN_VOLT("vbat_sns", 3,
+					SCALE_HW_CALIB_DEFAULT)
+	[ADC7_DIE_TEMP]		= ADC5_CHAN_TEMP("die_temp", 0,
+					SCALE_HW_CALIB_PMIC_THERM_PM7)
+	[ADC7_AMUX_THM1_100K_PU]	= ADC5_CHAN_TEMP("amux_thm1_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_AMUX_THM2_100K_PU]	= ADC5_CHAN_TEMP("amux_thm2_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_AMUX_THM3_100K_PU]	= ADC5_CHAN_TEMP("amux_thm3_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_AMUX_THM4_100K_PU]	= ADC5_CHAN_TEMP("amux_thm4_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_AMUX_THM5_100K_PU]	= ADC5_CHAN_TEMP("amux_thm5_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_AMUX_THM6_100K_PU]	= ADC5_CHAN_TEMP("amux_thm6_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_GPIO1_100K_PU]	= ADC5_CHAN_TEMP("gpio1_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_GPIO2_100K_PU]	= ADC5_CHAN_TEMP("gpio2_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_GPIO3_100K_PU]	= ADC5_CHAN_TEMP("gpio3_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+	[ADC7_GPIO4_100K_PU]	= ADC5_CHAN_TEMP("gpio4_pu2", 0,
+					SCALE_HW_CALIB_THERM_100K_PU_PM7)
+};
+
 static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
 	[ADC5_REF_GND]		= ADC5_CHAN_VOLT("ref_gnd", 0,
 					SCALE_HW_CALIB_DEFAULT)
@@ -511,6 +696,7 @@  static int adc5_get_dt_channel_data(struct adc5_chip *adc,
 {
 	const char *name = node->name, *channel_name;
 	u32 chan, value, varr[2];
+	u32 sid = 0;
 	int ret;
 	struct device *dev = adc->dev;
 
@@ -520,6 +706,15 @@  static int adc5_get_dt_channel_data(struct adc5_chip *adc,
 		return ret;
 	}
 
+	/* Value read from "reg" is virtual channel number */
+
+	/* virtual channel number = sid << 8 | channel number */
+
+	if (adc->data == &adc7_data_pmic) {
+		sid = chan >> ADC_CHANNEL_OFFSET;
+		chan = chan & ADC_CHANNEL_MASK;
+	}
+
 	if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
 	    !data->adc_chans[chan].datasheet_name) {
 		dev_err(dev, "%s invalid channel number %d\n", name, chan);
@@ -528,6 +723,7 @@  static int adc5_get_dt_channel_data(struct adc5_chip *adc,
 
 	/* the channel has DT description */
 	prop->channel = chan;
+	prop->sid = sid;
 
 	channel_name = of_get_property(node,
 				"label", NULL) ? : node->name;
@@ -578,8 +774,9 @@  static int adc5_get_dt_channel_data(struct adc5_chip *adc,
 		pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
 						dig_version[1]);
 		/* Digital controller >= 5.3 have hw_settle_2 option */
-		if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
-			dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
+		if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
+			dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) ||
+			adc->data == &adc7_data_pmic)
 			ret = adc5_hw_settle_time_from_dt(value,
 							data->hw_settle_2);
 		else
@@ -629,6 +826,7 @@  static const struct adc5_data adc5_data_pmic = {
 	.full_scale_code_volt = 0x70e4,
 	.full_scale_code_cur = 0x2710,
 	.adc_chans = adc5_chans_pmic,
+	.info = &adc5_info,
 	.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
 				{250, 420, 840},
 	.hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
@@ -639,10 +837,23 @@  static const struct adc5_data adc5_data_pmic = {
 				1, 2, 4, 8, 16, 32, 64, 128},
 };
 
+static const struct adc5_data adc7_data_pmic = {
+	.full_scale_code_volt = 0x70e4,
+	.adc_chans = adc7_chans_pmic,
+	.info = &adc7_info,
+	.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+				{85, 340, 1360},
+	.hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+				{15, 100, 200, 300, 400, 500, 600, 700,
+				1000, 2000, 4000, 8000, 16000, 32000,
+				64000, 128000},
+};
+
 static const struct adc5_data adc5_data_pmic_rev2 = {
 	.full_scale_code_volt = 0x4000,
 	.full_scale_code_cur = 0x1800,
 	.adc_chans = adc5_chans_rev2,
+	.info = &adc5_info,
 	.decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
 				{256, 512, 1024},
 	.hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
@@ -659,6 +870,10 @@  static const struct of_device_id adc5_match_table[] = {
 		.data = &adc5_data_pmic,
 	},
 	{
+		.compatible = "qcom,spmi-adc7",
+		.data = &adc7_data_pmic,
+	},
+	{
 		.compatible = "qcom,spmi-adc-rev2",
 		.data = &adc5_data_pmic_rev2,
 	},
@@ -752,6 +967,9 @@  static int adc5_probe(struct platform_device *pdev)
 	adc->regmap = regmap;
 	adc->dev = dev;
 	adc->base = reg;
+
+	platform_set_drvdata(pdev, adc);
+
 	init_completion(&adc->complete);
 	mutex_init(&adc->lock);
 
@@ -761,6 +979,8 @@  static int adc5_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	indio_dev->info = adc->data->info;
+
 	irq_eoc = platform_get_irq(pdev, 0);
 	if (irq_eoc < 0) {
 		if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
@@ -773,23 +993,34 @@  static int adc5_probe(struct platform_device *pdev)
 			return ret;
 	}
 
+	adc->irq_eoc = irq_eoc;
+
 	indio_dev->dev.parent = dev;
 	indio_dev->dev.of_node = node;
 	indio_dev->name = pdev->name;
 	indio_dev->modes = INDIO_DIRECT_MODE;
-	indio_dev->info = &adc5_info;
 	indio_dev->channels = adc->iio_chans;
 	indio_dev->num_channels = adc->nchannels;
 
 	return devm_iio_device_register(dev, indio_dev);
 }
 
+static int adc5_exit(struct platform_device *pdev)
+{
+	struct adc5_chip *adc = platform_get_drvdata(pdev);
+
+	if (adc->irq_eoc >= 0)
+		disable_irq(adc->irq_eoc);
+	return 0;
+}
+
 static struct platform_driver adc5_driver = {
 	.driver = {
 		.name = "qcom-spmi-adc5.c",
 		.of_match_table = adc5_match_table,
 	},
 	.probe = adc5_probe,
+	.remove = adc5_exit,
 };
 module_platform_driver(adc5_driver);
 
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
index 2bb78d1..b6915c2 100644
--- a/drivers/iio/adc/qcom-vadc-common.c
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -89,6 +89,195 @@  static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
 	{ 46,	125000 },
 };
 
+static const struct vadc_map_pt adcmap7_die_temp[] = {
+	{ 433700, 1967},
+	{ 473100, 1964},
+	{ 512400, 1957},
+	{ 551500, 1949},
+	{ 590500, 1940},
+	{ 629300, 1930},
+	{ 667900, 1921},
+	{ 706400, 1910},
+	{ 744600, 1896},
+	{ 782500, 1878},
+	{ 820100, 1859},
+	{ 857300, 0},
+};
+
+/*
+ * Resistance to temperature table for 100k pull up for NTCG104EF104.
+ */
+static const struct vadc_map_pt adcmap7_100k[] = {
+	{ 4250657, -40960 },
+	{ 3962085, -39936 },
+	{ 3694875, -38912 },
+	{ 3447322, -37888 },
+	{ 3217867, -36864 },
+	{ 3005082, -35840 },
+	{ 2807660, -34816 },
+	{ 2624405, -33792 },
+	{ 2454218, -32768 },
+	{ 2296094, -31744 },
+	{ 2149108, -30720 },
+	{ 2012414, -29696 },
+	{ 1885232, -28672 },
+	{ 1766846, -27648 },
+	{ 1656598, -26624 },
+	{ 1553884, -25600 },
+	{ 1458147, -24576 },
+	{ 1368873, -23552 },
+	{ 1285590, -22528 },
+	{ 1207863, -21504 },
+	{ 1135290, -20480 },
+	{ 1067501, -19456 },
+	{ 1004155, -18432 },
+	{ 944935, -17408 },
+	{ 889550, -16384 },
+	{ 837731, -15360 },
+	{ 789229, -14336 },
+	{ 743813, -13312 },
+	{ 701271, -12288 },
+	{ 661405, -11264 },
+	{ 624032, -10240 },
+	{ 588982, -9216 },
+	{ 556100, -8192 },
+	{ 525239, -7168 },
+	{ 496264, -6144 },
+	{ 469050, -5120 },
+	{ 443480, -4096 },
+	{ 419448, -3072 },
+	{ 396851, -2048 },
+	{ 375597, -1024 },
+	{ 355598, 0 },
+	{ 336775, 1024 },
+	{ 319052, 2048 },
+	{ 302359, 3072 },
+	{ 286630, 4096 },
+	{ 271806, 5120 },
+	{ 257829, 6144 },
+	{ 244646, 7168 },
+	{ 232209, 8192 },
+	{ 220471, 9216 },
+	{ 209390, 10240 },
+	{ 198926, 11264 },
+	{ 189040, 12288 },
+	{ 179698, 13312 },
+	{ 170868, 14336 },
+	{ 162519, 15360 },
+	{ 154622, 16384 },
+	{ 147150, 17408 },
+	{ 140079, 18432 },
+	{ 133385, 19456 },
+	{ 127046, 20480 },
+	{ 121042, 21504 },
+	{ 115352, 22528 },
+	{ 109960, 23552 },
+	{ 104848, 24576 },
+	{ 100000, 25600 },
+	{ 95402, 26624 },
+	{ 91038, 27648 },
+	{ 86897, 28672 },
+	{ 82965, 29696 },
+	{ 79232, 30720 },
+	{ 75686, 31744 },
+	{ 72316, 32768 },
+	{ 69114, 33792 },
+	{ 66070, 34816 },
+	{ 63176, 35840 },
+	{ 60423, 36864 },
+	{ 57804, 37888 },
+	{ 55312, 38912 },
+	{ 52940, 39936 },
+	{ 50681, 40960 },
+	{ 48531, 41984 },
+	{ 46482, 43008 },
+	{ 44530, 44032 },
+	{ 42670, 45056 },
+	{ 40897, 46080 },
+	{ 39207, 47104 },
+	{ 37595, 48128 },
+	{ 36057, 49152 },
+	{ 34590, 50176 },
+	{ 33190, 51200 },
+	{ 31853, 52224 },
+	{ 30577, 53248 },
+	{ 29358, 54272 },
+	{ 28194, 55296 },
+	{ 27082, 56320 },
+	{ 26020, 57344 },
+	{ 25004, 58368 },
+	{ 24033, 59392 },
+	{ 23104, 60416 },
+	{ 22216, 61440 },
+	{ 21367, 62464 },
+	{ 20554, 63488 },
+	{ 19776, 64512 },
+	{ 19031, 65536 },
+	{ 18318, 66560 },
+	{ 17636, 67584 },
+	{ 16982, 68608 },
+	{ 16355, 69632 },
+	{ 15755, 70656 },
+	{ 15180, 71680 },
+	{ 14628, 72704 },
+	{ 14099, 73728 },
+	{ 13592, 74752 },
+	{ 13106, 75776 },
+	{ 12640, 76800 },
+	{ 12192, 77824 },
+	{ 11762, 78848 },
+	{ 11350, 79872 },
+	{ 10954, 80896 },
+	{ 10574, 81920 },
+	{ 10209, 82944 },
+	{ 9858, 83968 },
+	{ 9521, 84992 },
+	{ 9197, 86016 },
+	{ 8886, 87040 },
+	{ 8587, 88064 },
+	{ 8299, 89088 },
+	{ 8023, 90112 },
+	{ 7757, 91136 },
+	{ 7501, 92160 },
+	{ 7254, 93184 },
+	{ 7017, 94208 },
+	{ 6789, 95232 },
+	{ 6570, 96256 },
+	{ 6358, 97280 },
+	{ 6155, 98304 },
+	{ 5959, 99328 },
+	{ 5770, 100352 },
+	{ 5588, 101376 },
+	{ 5412, 102400 },
+	{ 5243, 103424 },
+	{ 5080, 104448 },
+	{ 4923, 105472 },
+	{ 4771, 106496 },
+	{ 4625, 107520 },
+	{ 4484, 108544 },
+	{ 4348, 109568 },
+	{ 4217, 110592 },
+	{ 4090, 111616 },
+	{ 3968, 112640 },
+	{ 3850, 113664 },
+	{ 3736, 114688 },
+	{ 3626, 115712 },
+	{ 3519, 116736 },
+	{ 3417, 117760 },
+	{ 3317, 118784 },
+	{ 3221, 119808 },
+	{ 3129, 120832 },
+	{ 3039, 121856 },
+	{ 2952, 122880 },
+	{ 2868, 123904 },
+	{ 2787, 124928 },
+	{ 2709, 125952 },
+	{ 2633, 126976 },
+	{ 2560, 128000 },
+	{ 2489, 129024 },
+	{ 2420, 130048 }
+};
+
 static int qcom_vadc_scale_hw_calib_volt(
 				const struct vadc_prescale_ratio *prescale,
 				const struct adc5_data *data,
@@ -97,6 +286,10 @@  static int qcom_vadc_scale_hw_calib_therm(
 				const struct vadc_prescale_ratio *prescale,
 				const struct adc5_data *data,
 				u16 adc_code, int *result_mdec);
+static int qcom_vadc7_scale_hw_calib_therm(
+				const struct vadc_prescale_ratio *prescale,
+				const struct adc5_data *data,
+				u16 adc_code, int *result_mdec);
 static int qcom_vadc_scale_hw_smb_temp(
 				const struct vadc_prescale_ratio *prescale,
 				const struct adc5_data *data,
@@ -109,12 +302,20 @@  static int qcom_vadc_scale_hw_calib_die_temp(
 				const struct vadc_prescale_ratio *prescale,
 				const struct adc5_data *data,
 				u16 adc_code, int *result_mdec);
+static int qcom_vadc7_scale_hw_calib_die_temp(
+				const struct vadc_prescale_ratio *prescale,
+				const struct adc5_data *data,
+				u16 adc_code, int *result_mdec);
 
 static struct qcom_adc5_scale_type scale_adc5_fn[] = {
 	[SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
 	[SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
 	[SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
+	[SCALE_HW_CALIB_THERM_100K_PU_PM7] = {
+					qcom_vadc7_scale_hw_calib_therm},
 	[SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
+	[SCALE_HW_CALIB_PMIC_THERM_PM7] = {
+					qcom_vadc7_scale_hw_calib_die_temp},
 	[SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
 	[SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
 };
@@ -291,6 +492,32 @@  static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
 	return (int) voltage;
 }
 
+static int qcom_vadc7_scale_hw_calib_therm(
+				const struct vadc_prescale_ratio *prescale,
+				const struct adc5_data *data,
+				u16 adc_code, int *result_mdec)
+{
+	s64 resistance = adc_code;
+	int ret, result;
+
+	if (adc_code >= RATIO_MAX_ADC7)
+		return -EINVAL;
+
+	/* (ADC code * R_PULLUP (100Kohm)) / (full_scale_code - ADC code)*/
+	resistance *= R_PU_100K;
+	resistance = div64_s64(resistance, RATIO_MAX_ADC7 - adc_code);
+
+	ret = qcom_vadc_map_voltage_temp(adcmap7_100k,
+				 ARRAY_SIZE(adcmap7_100k),
+				 resistance, &result);
+	if (ret)
+		return ret;
+
+	*result_mdec = result;
+
+	return 0;
+}
+
 static int qcom_vadc_scale_hw_calib_volt(
 				const struct vadc_prescale_ratio *prescale,
 				const struct adc5_data *data,
@@ -330,6 +557,39 @@  static int qcom_vadc_scale_hw_calib_die_temp(
 	return 0;
 }
 
+static int qcom_vadc7_scale_hw_calib_die_temp(
+				const struct vadc_prescale_ratio *prescale,
+				const struct adc5_data *data,
+				u16 adc_code, int *result_mdec)
+{
+
+	int voltage, vtemp0, temp, i = ARRAY_SIZE(adcmap7_die_temp) - 1;
+
+	voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
+				prescale, data, 1);
+
+	if (adcmap7_die_temp[0].x > voltage) {
+		*result_mdec = DIE_TEMP_ADC7_SCALE_1;
+		return 0;
+	} else if (adcmap7_die_temp[i].x <= voltage) {
+		*result_mdec = DIE_TEMP_ADC7_MAX;
+		return 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(adcmap7_die_temp); i++)
+		if (adcmap7_die_temp[i].x > voltage)
+			break;
+
+	vtemp0 = adcmap7_die_temp[i - 1].x;
+	voltage = voltage - vtemp0;
+	temp = div64_s64(voltage * DIE_TEMP_ADC7_SCALE_FACTOR,
+		adcmap7_die_temp[i - 1].y);
+	temp += DIE_TEMP_ADC7_SCALE_1 + (DIE_TEMP_ADC7_SCALE_2 * (i - 1));
+	*result_mdec = temp;
+
+	return 0;
+}
+
 static int qcom_vadc_scale_hw_smb_temp(
 				const struct vadc_prescale_ratio *prescale,
 				const struct adc5_data *data,
diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
index e074902a..d195e83 100644
--- a/drivers/iio/adc/qcom-vadc-common.h
+++ b/drivers/iio/adc/qcom-vadc-common.h
@@ -49,6 +49,14 @@ 
 #define ADC5_FULL_SCALE_CODE			0x70e4
 #define ADC5_USR_DATA_CHECK			0x8000
 
+#define R_PU_100K			100000
+#define RATIO_MAX_ADC7		0x4000
+
+#define DIE_TEMP_ADC7_SCALE_1				-60000
+#define DIE_TEMP_ADC7_SCALE_2				20000
+#define DIE_TEMP_ADC7_SCALE_FACTOR			1000
+#define DIE_TEMP_ADC7_MAX				160000
+
 /**
  * struct vadc_map_pt - Map the graph representation for ADC channel
  * @x: Represent the ADC digitized code.
@@ -110,8 +118,12 @@  struct vadc_prescale_ratio {
  *	lookup table. The hardware applies offset/slope to adc code.
  * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
  *	100k pullup. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_THERM_100K_PU_PM7: Returns temperature in millidegC using
+ *	lookup table for PMIC7. The hardware applies offset/slope to adc code.
  * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
  *	The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
+ *	The hardware applies offset/slope to adc code. This is for PMIC7.
  * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
  *	charger temperature.
  * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
@@ -126,7 +138,9 @@  enum vadc_scale_fn_type {
 	SCALE_HW_CALIB_DEFAULT,
 	SCALE_HW_CALIB_THERM_100K_PULLUP,
 	SCALE_HW_CALIB_XOTHERM,
+	SCALE_HW_CALIB_THERM_100K_PU_PM7,
 	SCALE_HW_CALIB_PMIC_THERM,
+	SCALE_HW_CALIB_PMIC_THERM_PM7,
 	SCALE_HW_CALIB_PM5_CHG_TEMP,
 	SCALE_HW_CALIB_PM5_SMB_TEMP,
 	SCALE_HW_CALIB_INVALID,
@@ -136,6 +150,7 @@  struct adc5_data {
 	const u32	full_scale_code_volt;
 	const u32	full_scale_code_cur;
 	const struct adc5_channels *adc_chans;
+	const struct iio_info *info;
 	unsigned int	*decimation;
 	unsigned int	*hw_settle_1;
 	unsigned int	*hw_settle_2;