diff mbox

[2/3,v5] iio: adc: break out common code from SPMI VADC

Message ID 20170404120819.6921-2-linus.walleij@linaro.org (mailing list archive)
State Not Applicable, archived
Delegated to: Andy Gross
Headers show

Commit Message

Linus Walleij April 4, 2017, 12:08 p.m. UTC
The SPMI VADC and the earlier XOADC share a subset of
common code, so to be able to use the same code in both
drivers, we break out a separate file with the common code,
prefix exported functions that are no longer static with
qcom_* and bake an object qcom-spmi-vadc.o that contains both
files: qcom-vadc-common.o and qcom-spmi-vadc-core.o.

As we need to follow the procedure for making a kernel module
or compiled in object from several files, but still want to
produce the same module name, rename the qcom-spmi-vadc.c
file to qcom-spmi-vadc-core.c so we can bake the two objects
into qcom-spmi-vadc.o

Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-arm-msm@vger.kernel.org
Cc: Ivan T. Ivanov <iivanov.xz@gmail.com>
Cc: Andy Gross <andy.gross@linaro.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: Stephen Boyd <sboyd@codeaurora.org>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Rama Krishna Phani A <rphani@codeaurora.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
ChangeLog v4->v5:
- Fix kernel build again. Give up and create a helper module
  with EXPORT_SYMBOL() functions. It works for allyes and allmod,
  finally. YES I TESTED IT THOROUGHLY:

  x86_64 allmodconfig:
  CC [M]  kernel/configs.o
  CC [M]  drivers/iio/adc/qcom-vadc-common.o
  CC [M]  drivers/iio/adc/qcom-spmi-vadc.o
  CC [M]  drivers/iio/adc/qcom-pm8xxx-xoadc.o
  Building modules, stage 2.
  MODPOST 6247 modules
  CC      drivers/iio/adc/qcom-pm8xxx-xoadc.mod.o
  CC      drivers/iio/adc/qcom-vadc-common.mod.o
  CC      drivers/iio/adc/qcom-spmi-vadc.mod.o
  CC      kernel/configs.mod.o
  LD [M]  drivers/iio/adc/qcom-pm8xxx-xoadc.ko
  LD [M]  drivers/iio/adc/qcom-spmi-vadc.ko
  LD [M]  drivers/iio/adc/qcom-vadc-common.ko
  LD [M]  kernel/configs.ko

  x86_64 allyesconfig:
  CC      drivers/iio/adc/qcom-vadc-common.o
  CC      drivers/iio/adc/qcom-spmi-vadc.o
  CC      drivers/iio/adc/qcom-pm8xxx-xoadc.o
  LD      drivers/iio/adc/built-in.o
  LD      drivers/iio/built-in.o
  LD      vmlinux.o
  MODPOST vmlinux.o
  KSYM    .tmp_kallsyms1.o
  KSYM    .tmp_kallsyms2.o
  LD      vmlinux

  So help me God. And sorry for wasting so much of your time with
  these build issues. :( :( :(

- Missing inclusion guard in the .h file.
ChangeLog v3->v4:
- Fix up the kernel build, tested with allyes and allmod.
ChangeLog v2->v3:
- Rewrite on top of Rama Krishna's changes. Now we use the
  generic channel properties, calibration graph and prescale
  settings in all VADC drivers. I did away with the vtable
  indirection which I just don't see the point of, it's
  better to just pass the type of conversion function around
  and have a switch case select the conversion. It was done like
  that in the vendor tree but it's not a good idea.
ChangeLog v1->v2:
- No changes just reposting
---
 drivers/iio/adc/Kconfig            |   4 +
 drivers/iio/adc/Makefile           |   1 +
 drivers/iio/adc/qcom-spmi-vadc.c   | 325 ++-----------------------------------
 drivers/iio/adc/qcom-vadc-common.c | 230 ++++++++++++++++++++++++++
 drivers/iio/adc/qcom-vadc-common.h | 108 ++++++++++++
 5 files changed, 358 insertions(+), 310 deletions(-)
 create mode 100644 drivers/iio/adc/qcom-vadc-common.c
 create mode 100644 drivers/iio/adc/qcom-vadc-common.h

Comments

Jonathan Cameron April 8, 2017, 4:41 p.m. UTC | #1
On 04/04/17 13:08, Linus Walleij wrote:
> The SPMI VADC and the earlier XOADC share a subset of
> common code, so to be able to use the same code in both
> drivers, we break out a separate file with the common code,
> prefix exported functions that are no longer static with
> qcom_* and bake an object qcom-spmi-vadc.o that contains both
> files: qcom-vadc-common.o and qcom-spmi-vadc-core.o.
> 
> As we need to follow the procedure for making a kernel module
> or compiled in object from several files, but still want to
> produce the same module name, rename the qcom-spmi-vadc.c
> file to qcom-spmi-vadc-core.c so we can bake the two objects
> into qcom-spmi-vadc.o
> 
> Cc: linux-arm-kernel@lists.infradead.org
> Cc: linux-arm-msm@vger.kernel.org
> Cc: Ivan T. Ivanov <iivanov.xz@gmail.com>
> Cc: Andy Gross <andy.gross@linaro.org>
> Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
> Cc: Rama Krishna Phani A <rphani@codeaurora.org>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Applied to the togreg branhc of iio.git and pushed out as
testing for the autobuilders to play with it.

Thanks,

Jonathan
> ---
> ChangeLog v4->v5:
> - Fix kernel build again. Give up and create a helper module
>   with EXPORT_SYMBOL() functions. It works for allyes and allmod,
>   finally. YES I TESTED IT THOROUGHLY:
> 
>   x86_64 allmodconfig:
>   CC [M]  kernel/configs.o
>   CC [M]  drivers/iio/adc/qcom-vadc-common.o
>   CC [M]  drivers/iio/adc/qcom-spmi-vadc.o
>   CC [M]  drivers/iio/adc/qcom-pm8xxx-xoadc.o
>   Building modules, stage 2.
>   MODPOST 6247 modules
>   CC      drivers/iio/adc/qcom-pm8xxx-xoadc.mod.o
>   CC      drivers/iio/adc/qcom-vadc-common.mod.o
>   CC      drivers/iio/adc/qcom-spmi-vadc.mod.o
>   CC      kernel/configs.mod.o
>   LD [M]  drivers/iio/adc/qcom-pm8xxx-xoadc.ko
>   LD [M]  drivers/iio/adc/qcom-spmi-vadc.ko
>   LD [M]  drivers/iio/adc/qcom-vadc-common.ko
>   LD [M]  kernel/configs.ko
> 
>   x86_64 allyesconfig:
>   CC      drivers/iio/adc/qcom-vadc-common.o
>   CC      drivers/iio/adc/qcom-spmi-vadc.o
>   CC      drivers/iio/adc/qcom-pm8xxx-xoadc.o
>   LD      drivers/iio/adc/built-in.o
>   LD      drivers/iio/built-in.o
>   LD      vmlinux.o
>   MODPOST vmlinux.o
>   KSYM    .tmp_kallsyms1.o
>   KSYM    .tmp_kallsyms2.o
>   LD      vmlinux
> 
>   So help me God. And sorry for wasting so much of your time with
>   these build issues. :( :( :(
> 
> - Missing inclusion guard in the .h file.
> ChangeLog v3->v4:
> - Fix up the kernel build, tested with allyes and allmod.
> ChangeLog v2->v3:
> - Rewrite on top of Rama Krishna's changes. Now we use the
>   generic channel properties, calibration graph and prescale
>   settings in all VADC drivers. I did away with the vtable
>   indirection which I just don't see the point of, it's
>   better to just pass the type of conversion function around
>   and have a switch case select the conversion. It was done like
>   that in the vendor tree but it's not a good idea.
> ChangeLog v1->v2:
> - No changes just reposting
> ---
>  drivers/iio/adc/Kconfig            |   4 +
>  drivers/iio/adc/Makefile           |   1 +
>  drivers/iio/adc/qcom-spmi-vadc.c   | 325 ++-----------------------------------
>  drivers/iio/adc/qcom-vadc-common.c | 230 ++++++++++++++++++++++++++
>  drivers/iio/adc/qcom-vadc-common.h | 108 ++++++++++++
>  5 files changed, 358 insertions(+), 310 deletions(-)
>  create mode 100644 drivers/iio/adc/qcom-vadc-common.c
>  create mode 100644 drivers/iio/adc/qcom-vadc-common.h
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index dedae7adbce9..8720e1c706fe 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -442,6 +442,9 @@ config PALMAS_GPADC
>  	  is used in smartphones and tablets and supports a 16 channel
>  	  general purpose ADC.
>  
> +config QCOM_VADC_COMMON
> +	tristate
> +
>  config QCOM_SPMI_IADC
>  	tristate "Qualcomm SPMI PMIC current ADC"
>  	depends on SPMI
> @@ -460,6 +463,7 @@ config QCOM_SPMI_VADC
>  	tristate "Qualcomm SPMI PMIC voltage ADC"
>  	depends on SPMI
>  	select REGMAP_SPMI
> +	select QCOM_VADC_COMMON
>  	help
>  	  This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
>  
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index d0012620cd1c..8c2e294042ae 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
>  obj-$(CONFIG_NAU7802) += nau7802.o
>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>  obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
> +obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
>  obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>  obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
>  obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
> diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
> index 0a19761d656c..9e600bfd1765 100644
> --- a/drivers/iio/adc/qcom-spmi-vadc.c
> +++ b/drivers/iio/adc/qcom-spmi-vadc.c
> @@ -28,6 +28,8 @@
>  
>  #include <dt-bindings/iio/qcom,spmi-vadc.h>
>  
> +#include "qcom-vadc-common.h"
> +
>  /* VADC register and bit definitions */
>  #define VADC_REVISION2				0x1
>  #define VADC_REVISION2_SUPPORTED_VADC		1
> @@ -75,84 +77,10 @@
>  
>  #define VADC_DATA				0x60	/* 16 bits */
>  
> -#define VADC_CONV_TIME_MIN_US			2000
> -#define VADC_CONV_TIME_MAX_US			2100
> -
> -/* Min ADC code represents 0V */
> -#define VADC_MIN_ADC_CODE			0x6000
> -/* Max ADC code represents full-scale range of 1.8V */
> -#define VADC_MAX_ADC_CODE			0xa800
> -
> -#define VADC_ABSOLUTE_RANGE_UV			625000
> -#define VADC_RATIOMETRIC_RANGE			1800
> -
> -#define VADC_DEF_PRESCALING			0 /* 1:1 */
> -#define VADC_DEF_DECIMATION			0 /* 512 */
> -#define VADC_DEF_HW_SETTLE_TIME			0 /* 0 us */
> -#define VADC_DEF_AVG_SAMPLES			0 /* 1 sample */
> -#define VADC_DEF_CALIB_TYPE			VADC_CALIB_ABSOLUTE
> -
> -#define VADC_DECIMATION_MIN			512
> -#define VADC_DECIMATION_MAX			4096
> -
> -#define VADC_HW_SETTLE_DELAY_MAX		10000
> -#define VADC_AVG_SAMPLES_MAX			512
> -
> -#define KELVINMIL_CELSIUSMIL			273150
> -
> -#define PMI_CHG_SCALE_1				-138890
> -#define PMI_CHG_SCALE_2				391750000000LL
> -
>  #define VADC_CHAN_MIN			VADC_USBIN
>  #define VADC_CHAN_MAX			VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
>  
>  /**
> - * struct vadc_map_pt - Map the graph representation for ADC channel
> - * @x: Represent the ADC digitized code.
> - * @y: Represent the physical data which can be temperature, voltage,
> - *     resistance.
> - */
> -struct vadc_map_pt {
> -	s32 x;
> -	s32 y;
> -};
> -
> -/*
> - * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
> - * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
> - * calibration.
> - */
> -enum vadc_calibration {
> -	VADC_CALIB_ABSOLUTE = 0,
> -	VADC_CALIB_RATIOMETRIC
> -};
> -
> -/**
> - * struct vadc_linear_graph - Represent ADC characteristics.
> - * @dy: numerator slope to calculate the gain.
> - * @dx: denominator slope to calculate the gain.
> - * @gnd: A/D word of the ground reference used for the channel.
> - *
> - * Each ADC device has different offset and gain parameters which are
> - * computed to calibrate the device.
> - */
> -struct vadc_linear_graph {
> -	s32 dy;
> -	s32 dx;
> -	s32 gnd;
> -};
> -
> -/**
> - * struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
> - * @num: the inverse numerator of the gain applied to the input channel.
> - * @den: the inverse denominator of the gain applied to the input channel.
> - */
> -struct vadc_prescale_ratio {
> -	u32 num;
> -	u32 den;
> -};
> -
> -/**
>   * struct vadc_channel_prop - VADC channel property.
>   * @channel: channel number, refer to the channel list.
>   * @calibration: calibration type.
> @@ -162,9 +90,8 @@ struct vadc_prescale_ratio {
>   *	start of conversion.
>   * @avg_samples: ability to provide single result from the ADC
>   *	that is an average of multiple measurements.
> - * @scale_fn: Represents the scaling function to convert voltage
> + * @scale_fn_type: Represents the scaling function to convert voltage
>   *	physical units desired by the client for the channel.
> - *	Referenced from enum vadc_scale_fn_type.
>   */
>  struct vadc_channel_prop {
>  	unsigned int channel;
> @@ -173,7 +100,7 @@ struct vadc_channel_prop {
>  	unsigned int prescale;
>  	unsigned int hw_settle_time;
>  	unsigned int avg_samples;
> -	unsigned int scale_fn;
> +	enum vadc_scale_fn_type scale_fn_type;
>  };
>  
>  /**
> @@ -204,35 +131,6 @@ struct vadc_priv {
>  	struct mutex		 lock;
>  };
>  
> -/**
> - * struct vadc_scale_fn - Scaling function prototype
> - * @scale: Function pointer to one of the scaling functions
> - *	which takes the adc properties, channel properties,
> - *	and returns the physical result.
> - */
> -struct vadc_scale_fn {
> -	int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *,
> -		     u16, int *);
> -};
> -
> -/**
> - * enum vadc_scale_fn_type - Scaling function to convert ADC code to
> - *				physical scaled units for the channel.
> - * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
> - * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
> - *				 Uses a mapping table with 100K pullup.
> - * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
> - * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
> - * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
> - */
> -enum vadc_scale_fn_type {
> -	SCALE_DEFAULT = 0,
> -	SCALE_THERM_100K_PULLUP,
> -	SCALE_PMIC_THERM,
> -	SCALE_XOTHERM,
> -	SCALE_PMI_CHG_TEMP,
> -};
> -
>  static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
>  	{.num =  1, .den =  1},
>  	{.num =  1, .den =  3},
> @@ -244,44 +142,6 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
>  	{.num =  1, .den = 10}
>  };
>  
> -/* Voltage to temperature */
> -static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
> -	{1758,	-40},
> -	{1742,	-35},
> -	{1719,	-30},
> -	{1691,	-25},
> -	{1654,	-20},
> -	{1608,	-15},
> -	{1551,	-10},
> -	{1483,	-5},
> -	{1404,	0},
> -	{1315,	5},
> -	{1218,	10},
> -	{1114,	15},
> -	{1007,	20},
> -	{900,	25},
> -	{795,	30},
> -	{696,	35},
> -	{605,	40},
> -	{522,	45},
> -	{448,	50},
> -	{383,	55},
> -	{327,	60},
> -	{278,	65},
> -	{237,	70},
> -	{202,	75},
> -	{172,	80},
> -	{146,	85},
> -	{125,	90},
> -	{107,	95},
> -	{92,	100},
> -	{79,	105},
> -	{68,	110},
> -	{59,	115},
> -	{51,	120},
> -	{44,	125}
> -};
> -
>  static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
>  {
>  	return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
> @@ -553,159 +413,6 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
>  	return ret;
>  }
>  
> -static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
> -				 u32 tablesize, s32 input, s64 *output)
> -{
> -	bool descending = 1;
> -	u32 i = 0;
> -
> -	if (!pts)
> -		return -EINVAL;
> -
> -	/* Check if table is descending or ascending */
> -	if (tablesize > 1) {
> -		if (pts[0].x < pts[1].x)
> -			descending = 0;
> -	}
> -
> -	while (i < tablesize) {
> -		if ((descending) && (pts[i].x < input)) {
> -			/* table entry is less than measured*/
> -			 /* value and table is descending, stop */
> -			break;
> -		} else if ((!descending) &&
> -				(pts[i].x > input)) {
> -			/* table entry is greater than measured*/
> -			/*value and table is ascending, stop */
> -			break;
> -		}
> -		i++;
> -	}
> -
> -	if (i == 0) {
> -		*output = pts[0].y;
> -	} else if (i == tablesize) {
> -		*output = pts[tablesize - 1].y;
> -	} else {
> -		/* result is between search_index and search_index-1 */
> -		/* interpolate linearly */
> -		*output = (((s32)((pts[i].y - pts[i - 1].y) *
> -			(input - pts[i - 1].x)) /
> -			(pts[i].x - pts[i - 1].x)) +
> -			pts[i - 1].y);
> -	}
> -
> -	return 0;
> -}
> -
> -static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
> -			     const struct vadc_channel_prop *prop,
> -			     s64 *scale_voltage)
> -{
> -	*scale_voltage = (adc_code -
> -		vadc->graph[prop->calibration].gnd);
> -	*scale_voltage *= vadc->graph[prop->calibration].dx;
> -	*scale_voltage = div64_s64(*scale_voltage,
> -		vadc->graph[prop->calibration].dy);
> -	if (prop->calibration == VADC_CALIB_ABSOLUTE)
> -		*scale_voltage +=
> -		vadc->graph[prop->calibration].dx;
> -
> -	if (*scale_voltage < 0)
> -		*scale_voltage = 0;
> -}
> -
> -static int vadc_scale_volt(struct vadc_priv *vadc,
> -			   const struct vadc_channel_prop *prop, u16 adc_code,
> -			   int *result_uv)
> -{
> -	const struct vadc_prescale_ratio *prescale;
> -	s64 voltage = 0, result = 0;
> -
> -	vadc_scale_calib(vadc, adc_code, prop, &voltage);
> -
> -	prescale = &vadc_prescale_ratios[prop->prescale];
> -	voltage = voltage * prescale->den;
> -	result = div64_s64(voltage, prescale->num);
> -	*result_uv = result;
> -
> -	return 0;
> -}
> -
> -static int vadc_scale_therm(struct vadc_priv *vadc,
> -			    const struct vadc_channel_prop *prop, u16 adc_code,
> -			    int *result_mdec)
> -{
> -	s64 voltage = 0, result = 0;
> -
> -	vadc_scale_calib(vadc, adc_code, prop, &voltage);
> -
> -	if (prop->calibration == VADC_CALIB_ABSOLUTE)
> -		voltage = div64_s64(voltage, 1000);
> -
> -	vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
> -			      ARRAY_SIZE(adcmap_100k_104ef_104fb),
> -			      voltage, &result);
> -	result *= 1000;
> -	*result_mdec = result;
> -
> -	return 0;
> -}
> -
> -static int vadc_scale_die_temp(struct vadc_priv *vadc,
> -			       const struct vadc_channel_prop *prop,
> -			       u16 adc_code, int *result_mdec)
> -{
> -	const struct vadc_prescale_ratio *prescale;
> -	s64 voltage = 0;
> -	u64 temp; /* Temporary variable for do_div */
> -
> -	vadc_scale_calib(vadc, adc_code, prop, &voltage);
> -
> -	if (voltage > 0) {
> -		prescale = &vadc_prescale_ratios[prop->prescale];
> -		temp = voltage * prescale->den;
> -		do_div(temp, prescale->num * 2);
> -		voltage = temp;
> -	} else {
> -		voltage = 0;
> -	}
> -
> -	voltage -= KELVINMIL_CELSIUSMIL;
> -	*result_mdec = voltage;
> -
> -	return 0;
> -}
> -
> -static int vadc_scale_chg_temp(struct vadc_priv *vadc,
> -			       const struct vadc_channel_prop *prop,
> -			       u16 adc_code, int *result_mdec)
> -{
> -	const struct vadc_prescale_ratio *prescale;
> -	s64 voltage = 0, result = 0;
> -
> -	vadc_scale_calib(vadc, adc_code, prop, &voltage);
> -
> -	prescale = &vadc_prescale_ratios[prop->prescale];
> -	voltage = voltage * prescale->den;
> -	voltage = div64_s64(voltage, prescale->num);
> -	voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
> -	voltage = (voltage + PMI_CHG_SCALE_2);
> -	result =  div64_s64(voltage, 1000000);
> -	*result_mdec = result;
> -
> -	return 0;
> -}
> -
> -static int vadc_decimation_from_dt(u32 value)
> -{
> -	if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
> -	    value > VADC_DECIMATION_MAX)
> -		return -EINVAL;
> -
> -	return __ffs64(value / VADC_DECIMATION_MIN);
> -}
> -
>  static int vadc_prescaling_from_dt(u32 num, u32 den)
>  {
>  	unsigned int pre;
> @@ -742,14 +449,6 @@ static int vadc_avg_samples_from_dt(u32 value)
>  	return __ffs64(value);
>  }
>  
> -static struct vadc_scale_fn scale_fn[] = {
> -	[SCALE_DEFAULT] = {vadc_scale_volt},
> -	[SCALE_THERM_100K_PULLUP] = {vadc_scale_therm},
> -	[SCALE_PMIC_THERM] = {vadc_scale_die_temp},
> -	[SCALE_XOTHERM] = {vadc_scale_therm},
> -	[SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp},
> -};
> -
>  static int vadc_read_raw(struct iio_dev *indio_dev,
>  			 struct iio_chan_spec const *chan, int *val, int *val2,
>  			 long mask)
> @@ -766,7 +465,13 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
>  		if (ret)
>  			break;
>  
> -		scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
> +		ret = qcom_vadc_scale(prop->scale_fn_type,
> +				&vadc->graph[prop->calibration],
> +				&vadc_prescale_ratios[prop->prescale],
> +				(prop->calibration == VADC_CALIB_ABSOLUTE),
> +				adc_code, val);
> +		if (ret)
> +			break;
>  
>  		return IIO_VAL_INT;
>  	case IIO_CHAN_INFO_RAW:
> @@ -809,7 +514,7 @@ struct vadc_channels {
>  	unsigned int prescale_index;
>  	enum iio_chan_type type;
>  	long info_mask;
> -	unsigned int scale_fn;
> +	enum vadc_scale_fn_type scale_fn_type;
>  };
>  
>  #define VADC_CHAN(_dname, _type, _mask, _pre, _scale)			\
> @@ -818,7 +523,7 @@ struct vadc_channels {
>  		.prescale_index = _pre,					\
>  		.type = _type,						\
>  		.info_mask = _mask,					\
> -		.scale_fn = _scale					\
> +		.scale_fn_type = _scale					\
>  	},								\
>  
>  #define VADC_NO_CHAN(_dname, _type, _mask, _pre)			\
> @@ -976,7 +681,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
>  
>  	ret = of_property_read_u32(node, "qcom,decimation", &value);
>  	if (!ret) {
> -		ret = vadc_decimation_from_dt(value);
> +		ret = qcom_vadc_decimation_from_dt(value);
>  		if (ret < 0) {
>  			dev_err(dev, "%02x invalid decimation %d\n",
>  				chan, value);
> @@ -1068,7 +773,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
>  			return ret;
>  		}
>  
> -		prop.scale_fn = vadc_chans[prop.channel].scale_fn;
> +		prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
>  		vadc->chan_props[index] = prop;
>  
>  		vadc_chan = &vadc_chans[prop.channel];
> diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
> new file mode 100644
> index 000000000000..102fc51b10aa
> --- /dev/null
> +++ b/drivers/iio/adc/qcom-vadc-common.c
> @@ -0,0 +1,230 @@
> +#include <linux/bug.h>
> +#include <linux/kernel.h>
> +#include <linux/bitops.h>
> +#include <linux/math64.h>
> +#include <linux/log2.h>
> +#include <linux/err.h>
> +
> +#include "qcom-vadc-common.h"
> +
> +/* Voltage to temperature */
> +static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
> +	{1758,	-40},
> +	{1742,	-35},
> +	{1719,	-30},
> +	{1691,	-25},
> +	{1654,	-20},
> +	{1608,	-15},
> +	{1551,	-10},
> +	{1483,	-5},
> +	{1404,	0},
> +	{1315,	5},
> +	{1218,	10},
> +	{1114,	15},
> +	{1007,	20},
> +	{900,	25},
> +	{795,	30},
> +	{696,	35},
> +	{605,	40},
> +	{522,	45},
> +	{448,	50},
> +	{383,	55},
> +	{327,	60},
> +	{278,	65},
> +	{237,	70},
> +	{202,	75},
> +	{172,	80},
> +	{146,	85},
> +	{125,	90},
> +	{107,	95},
> +	{92,	100},
> +	{79,	105},
> +	{68,	110},
> +	{59,	115},
> +	{51,	120},
> +	{44,	125}
> +};
> +
> +static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
> +				      u32 tablesize, s32 input, s64 *output)
> +{
> +	bool descending = 1;
> +	u32 i = 0;
> +
> +	if (!pts)
> +		return -EINVAL;
> +
> +	/* Check if table is descending or ascending */
> +	if (tablesize > 1) {
> +		if (pts[0].x < pts[1].x)
> +			descending = 0;
> +	}
> +
> +	while (i < tablesize) {
> +		if ((descending) && (pts[i].x < input)) {
> +			/* table entry is less than measured*/
> +			 /* value and table is descending, stop */
> +			break;
> +		} else if ((!descending) &&
> +				(pts[i].x > input)) {
> +			/* table entry is greater than measured*/
> +			/*value and table is ascending, stop */
> +			break;
> +		}
> +		i++;
> +	}
> +
> +	if (i == 0) {
> +		*output = pts[0].y;
> +	} else if (i == tablesize) {
> +		*output = pts[tablesize - 1].y;
> +	} else {
> +		/* result is between search_index and search_index-1 */
> +		/* interpolate linearly */
> +		*output = (((s32)((pts[i].y - pts[i - 1].y) *
> +			(input - pts[i - 1].x)) /
> +			(pts[i].x - pts[i - 1].x)) +
> +			pts[i - 1].y);
> +	}
> +
> +	return 0;
> +}
> +
> +static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
> +				  u16 adc_code,
> +				  bool absolute,
> +				  s64 *scale_voltage)
> +{
> +	*scale_voltage = (adc_code - calib_graph->gnd);
> +	*scale_voltage *= calib_graph->dx;
> +	*scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
> +	if (absolute)
> +		*scale_voltage += calib_graph->dx;
> +
> +	if (*scale_voltage < 0)
> +		*scale_voltage = 0;
> +}
> +
> +static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
> +				const struct vadc_prescale_ratio *prescale,
> +				bool absolute, u16 adc_code,
> +				int *result_uv)
> +{
> +	s64 voltage = 0, result = 0;
> +
> +	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
> +
> +	voltage = voltage * prescale->den;
> +	result = div64_s64(voltage, prescale->num);
> +	*result_uv = result;
> +
> +	return 0;
> +}
> +
> +static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
> +				 const struct vadc_prescale_ratio *prescale,
> +				 bool absolute, u16 adc_code,
> +				 int *result_mdec)
> +{
> +	s64 voltage = 0, result = 0;
> +	int ret;
> +
> +	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
> +
> +	if (absolute)
> +		voltage = div64_s64(voltage, 1000);
> +
> +	ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
> +					 ARRAY_SIZE(adcmap_100k_104ef_104fb),
> +					 voltage, &result);
> +	if (ret)
> +		return ret;
> +
> +	result *= 1000;
> +	*result_mdec = result;
> +
> +	return 0;
> +}
> +
> +static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
> +				    const struct vadc_prescale_ratio *prescale,
> +				    bool absolute,
> +				    u16 adc_code, int *result_mdec)
> +{
> +	s64 voltage = 0;
> +	u64 temp; /* Temporary variable for do_div */
> +
> +	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
> +
> +	if (voltage > 0) {
> +		temp = voltage * prescale->den;
> +		do_div(temp, prescale->num * 2);
> +		voltage = temp;
> +	} else {
> +		voltage = 0;
> +	}
> +
> +	voltage -= KELVINMIL_CELSIUSMIL;
> +	*result_mdec = voltage;
> +
> +	return 0;
> +}
> +
> +static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
> +				    const struct vadc_prescale_ratio *prescale,
> +				    bool absolute,
> +				    u16 adc_code, int *result_mdec)
> +{
> +	s64 voltage = 0, result = 0;
> +
> +	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
> +
> +	voltage = voltage * prescale->den;
> +	voltage = div64_s64(voltage, prescale->num);
> +	voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
> +	voltage = (voltage + PMI_CHG_SCALE_2);
> +	result =  div64_s64(voltage, 1000000);
> +	*result_mdec = result;
> +
> +	return 0;
> +}
> +
> +int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
> +		    const struct vadc_linear_graph *calib_graph,
> +		    const struct vadc_prescale_ratio *prescale,
> +		    bool absolute,
> +		    u16 adc_code, int *result)
> +{
> +	switch (scaletype) {
> +	case SCALE_DEFAULT:
> +		return qcom_vadc_scale_volt(calib_graph, prescale,
> +					    absolute, adc_code,
> +					    result);
> +	case SCALE_THERM_100K_PULLUP:
> +	case SCALE_XOTHERM:
> +		return qcom_vadc_scale_therm(calib_graph, prescale,
> +					     absolute, adc_code,
> +					     result);
> +	case SCALE_PMIC_THERM:
> +		return qcom_vadc_scale_die_temp(calib_graph, prescale,
> +						absolute, adc_code,
> +						result);
> +	case SCALE_PMI_CHG_TEMP:
> +		return qcom_vadc_scale_chg_temp(calib_graph, prescale,
> +						absolute, adc_code,
> +						result);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +EXPORT_SYMBOL(qcom_vadc_scale);
> +
> +int qcom_vadc_decimation_from_dt(u32 value)
> +{
> +	if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
> +	    value > VADC_DECIMATION_MAX)
> +		return -EINVAL;
> +
> +	return __ffs64(value / VADC_DECIMATION_MIN);
> +}
> +EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
> diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
> new file mode 100644
> index 000000000000..63c872a70adc
> --- /dev/null
> +++ b/drivers/iio/adc/qcom-vadc-common.h
> @@ -0,0 +1,108 @@
> +/*
> + * Code shared between the different Qualcomm PMIC voltage ADCs
> + */
> +
> +#ifndef QCOM_VADC_COMMON_H
> +#define QCOM_VADC_COMMON_H
> +
> +#define VADC_CONV_TIME_MIN_US			2000
> +#define VADC_CONV_TIME_MAX_US			2100
> +
> +/* Min ADC code represents 0V */
> +#define VADC_MIN_ADC_CODE			0x6000
> +/* Max ADC code represents full-scale range of 1.8V */
> +#define VADC_MAX_ADC_CODE			0xa800
> +
> +#define VADC_ABSOLUTE_RANGE_UV			625000
> +#define VADC_RATIOMETRIC_RANGE			1800
> +
> +#define VADC_DEF_PRESCALING			0 /* 1:1 */
> +#define VADC_DEF_DECIMATION			0 /* 512 */
> +#define VADC_DEF_HW_SETTLE_TIME			0 /* 0 us */
> +#define VADC_DEF_AVG_SAMPLES			0 /* 1 sample */
> +#define VADC_DEF_CALIB_TYPE			VADC_CALIB_ABSOLUTE
> +
> +#define VADC_DECIMATION_MIN			512
> +#define VADC_DECIMATION_MAX			4096
> +
> +#define VADC_HW_SETTLE_DELAY_MAX		10000
> +#define VADC_AVG_SAMPLES_MAX			512
> +
> +#define KELVINMIL_CELSIUSMIL			273150
> +
> +#define PMI_CHG_SCALE_1				-138890
> +#define PMI_CHG_SCALE_2				391750000000LL
> +
> +/**
> + * struct vadc_map_pt - Map the graph representation for ADC channel
> + * @x: Represent the ADC digitized code.
> + * @y: Represent the physical data which can be temperature, voltage,
> + *     resistance.
> + */
> +struct vadc_map_pt {
> +	s32 x;
> +	s32 y;
> +};
> +
> +/*
> + * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
> + * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
> + * calibration.
> + */
> +enum vadc_calibration {
> +	VADC_CALIB_ABSOLUTE = 0,
> +	VADC_CALIB_RATIOMETRIC
> +};
> +
> +/**
> + * struct vadc_linear_graph - Represent ADC characteristics.
> + * @dy: numerator slope to calculate the gain.
> + * @dx: denominator slope to calculate the gain.
> + * @gnd: A/D word of the ground reference used for the channel.
> + *
> + * Each ADC device has different offset and gain parameters which are
> + * computed to calibrate the device.
> + */
> +struct vadc_linear_graph {
> +	s32 dy;
> +	s32 dx;
> +	s32 gnd;
> +};
> +
> +/**
> + * struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
> + * @num: the inverse numerator of the gain applied to the input channel.
> + * @den: the inverse denominator of the gain applied to the input channel.
> + */
> +struct vadc_prescale_ratio {
> +	u32 num;
> +	u32 den;
> +};
> +
> +/**
> + * enum vadc_scale_fn_type - Scaling function to convert ADC code to
> + *				physical scaled units for the channel.
> + * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
> + * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
> + *				 Uses a mapping table with 100K pullup.
> + * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
> + * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
> + * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
> + */
> +enum vadc_scale_fn_type {
> +	SCALE_DEFAULT = 0,
> +	SCALE_THERM_100K_PULLUP,
> +	SCALE_PMIC_THERM,
> +	SCALE_XOTHERM,
> +	SCALE_PMI_CHG_TEMP,
> +};
> +
> +int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
> +		    const struct vadc_linear_graph *calib_graph,
> +		    const struct vadc_prescale_ratio *prescale,
> +		    bool absolute,
> +		    u16 adc_code, int *result_mdec);
> +
> +int qcom_vadc_decimation_from_dt(u32 value);
> +
> +#endif /* QCOM_VADC_COMMON_H */
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index dedae7adbce9..8720e1c706fe 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -442,6 +442,9 @@  config PALMAS_GPADC
 	  is used in smartphones and tablets and supports a 16 channel
 	  general purpose ADC.
 
+config QCOM_VADC_COMMON
+	tristate
+
 config QCOM_SPMI_IADC
 	tristate "Qualcomm SPMI PMIC current ADC"
 	depends on SPMI
@@ -460,6 +463,7 @@  config QCOM_SPMI_VADC
 	tristate "Qualcomm SPMI PMIC voltage ADC"
 	depends on SPMI
 	select REGMAP_SPMI
+	select QCOM_VADC_COMMON
 	help
 	  This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
 
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index d0012620cd1c..8c2e294042ae 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -43,6 +43,7 @@  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
 obj-$(CONFIG_NAU7802) += nau7802.o
 obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
 obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
+obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
 obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
 obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
 obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 0a19761d656c..9e600bfd1765 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -28,6 +28,8 @@ 
 
 #include <dt-bindings/iio/qcom,spmi-vadc.h>
 
+#include "qcom-vadc-common.h"
+
 /* VADC register and bit definitions */
 #define VADC_REVISION2				0x1
 #define VADC_REVISION2_SUPPORTED_VADC		1
@@ -75,84 +77,10 @@ 
 
 #define VADC_DATA				0x60	/* 16 bits */
 
-#define VADC_CONV_TIME_MIN_US			2000
-#define VADC_CONV_TIME_MAX_US			2100
-
-/* Min ADC code represents 0V */
-#define VADC_MIN_ADC_CODE			0x6000
-/* Max ADC code represents full-scale range of 1.8V */
-#define VADC_MAX_ADC_CODE			0xa800
-
-#define VADC_ABSOLUTE_RANGE_UV			625000
-#define VADC_RATIOMETRIC_RANGE			1800
-
-#define VADC_DEF_PRESCALING			0 /* 1:1 */
-#define VADC_DEF_DECIMATION			0 /* 512 */
-#define VADC_DEF_HW_SETTLE_TIME			0 /* 0 us */
-#define VADC_DEF_AVG_SAMPLES			0 /* 1 sample */
-#define VADC_DEF_CALIB_TYPE			VADC_CALIB_ABSOLUTE
-
-#define VADC_DECIMATION_MIN			512
-#define VADC_DECIMATION_MAX			4096
-
-#define VADC_HW_SETTLE_DELAY_MAX		10000
-#define VADC_AVG_SAMPLES_MAX			512
-
-#define KELVINMIL_CELSIUSMIL			273150
-
-#define PMI_CHG_SCALE_1				-138890
-#define PMI_CHG_SCALE_2				391750000000LL
-
 #define VADC_CHAN_MIN			VADC_USBIN
 #define VADC_CHAN_MAX			VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
 
 /**
- * struct vadc_map_pt - Map the graph representation for ADC channel
- * @x: Represent the ADC digitized code.
- * @y: Represent the physical data which can be temperature, voltage,
- *     resistance.
- */
-struct vadc_map_pt {
-	s32 x;
-	s32 y;
-};
-
-/*
- * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
- * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
- * calibration.
- */
-enum vadc_calibration {
-	VADC_CALIB_ABSOLUTE = 0,
-	VADC_CALIB_RATIOMETRIC
-};
-
-/**
- * struct vadc_linear_graph - Represent ADC characteristics.
- * @dy: numerator slope to calculate the gain.
- * @dx: denominator slope to calculate the gain.
- * @gnd: A/D word of the ground reference used for the channel.
- *
- * Each ADC device has different offset and gain parameters which are
- * computed to calibrate the device.
- */
-struct vadc_linear_graph {
-	s32 dy;
-	s32 dx;
-	s32 gnd;
-};
-
-/**
- * struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
- * @num: the inverse numerator of the gain applied to the input channel.
- * @den: the inverse denominator of the gain applied to the input channel.
- */
-struct vadc_prescale_ratio {
-	u32 num;
-	u32 den;
-};
-
-/**
  * struct vadc_channel_prop - VADC channel property.
  * @channel: channel number, refer to the channel list.
  * @calibration: calibration type.
@@ -162,9 +90,8 @@  struct vadc_prescale_ratio {
  *	start of conversion.
  * @avg_samples: ability to provide single result from the ADC
  *	that is an average of multiple measurements.
- * @scale_fn: Represents the scaling function to convert voltage
+ * @scale_fn_type: Represents the scaling function to convert voltage
  *	physical units desired by the client for the channel.
- *	Referenced from enum vadc_scale_fn_type.
  */
 struct vadc_channel_prop {
 	unsigned int channel;
@@ -173,7 +100,7 @@  struct vadc_channel_prop {
 	unsigned int prescale;
 	unsigned int hw_settle_time;
 	unsigned int avg_samples;
-	unsigned int scale_fn;
+	enum vadc_scale_fn_type scale_fn_type;
 };
 
 /**
@@ -204,35 +131,6 @@  struct vadc_priv {
 	struct mutex		 lock;
 };
 
-/**
- * struct vadc_scale_fn - Scaling function prototype
- * @scale: Function pointer to one of the scaling functions
- *	which takes the adc properties, channel properties,
- *	and returns the physical result.
- */
-struct vadc_scale_fn {
-	int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *,
-		     u16, int *);
-};
-
-/**
- * enum vadc_scale_fn_type - Scaling function to convert ADC code to
- *				physical scaled units for the channel.
- * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
- * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
- *				 Uses a mapping table with 100K pullup.
- * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
- * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
- * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
- */
-enum vadc_scale_fn_type {
-	SCALE_DEFAULT = 0,
-	SCALE_THERM_100K_PULLUP,
-	SCALE_PMIC_THERM,
-	SCALE_XOTHERM,
-	SCALE_PMI_CHG_TEMP,
-};
-
 static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
 	{.num =  1, .den =  1},
 	{.num =  1, .den =  3},
@@ -244,44 +142,6 @@  static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
 	{.num =  1, .den = 10}
 };
 
-/* Voltage to temperature */
-static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
-	{1758,	-40},
-	{1742,	-35},
-	{1719,	-30},
-	{1691,	-25},
-	{1654,	-20},
-	{1608,	-15},
-	{1551,	-10},
-	{1483,	-5},
-	{1404,	0},
-	{1315,	5},
-	{1218,	10},
-	{1114,	15},
-	{1007,	20},
-	{900,	25},
-	{795,	30},
-	{696,	35},
-	{605,	40},
-	{522,	45},
-	{448,	50},
-	{383,	55},
-	{327,	60},
-	{278,	65},
-	{237,	70},
-	{202,	75},
-	{172,	80},
-	{146,	85},
-	{125,	90},
-	{107,	95},
-	{92,	100},
-	{79,	105},
-	{68,	110},
-	{59,	115},
-	{51,	120},
-	{44,	125}
-};
-
 static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
 {
 	return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
@@ -553,159 +413,6 @@  static int vadc_measure_ref_points(struct vadc_priv *vadc)
 	return ret;
 }
 
-static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
-				 u32 tablesize, s32 input, s64 *output)
-{
-	bool descending = 1;
-	u32 i = 0;
-
-	if (!pts)
-		return -EINVAL;
-
-	/* Check if table is descending or ascending */
-	if (tablesize > 1) {
-		if (pts[0].x < pts[1].x)
-			descending = 0;
-	}
-
-	while (i < tablesize) {
-		if ((descending) && (pts[i].x < input)) {
-			/* table entry is less than measured*/
-			 /* value and table is descending, stop */
-			break;
-		} else if ((!descending) &&
-				(pts[i].x > input)) {
-			/* table entry is greater than measured*/
-			/*value and table is ascending, stop */
-			break;
-		}
-		i++;
-	}
-
-	if (i == 0) {
-		*output = pts[0].y;
-	} else if (i == tablesize) {
-		*output = pts[tablesize - 1].y;
-	} else {
-		/* result is between search_index and search_index-1 */
-		/* interpolate linearly */
-		*output = (((s32)((pts[i].y - pts[i - 1].y) *
-			(input - pts[i - 1].x)) /
-			(pts[i].x - pts[i - 1].x)) +
-			pts[i - 1].y);
-	}
-
-	return 0;
-}
-
-static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
-			     const struct vadc_channel_prop *prop,
-			     s64 *scale_voltage)
-{
-	*scale_voltage = (adc_code -
-		vadc->graph[prop->calibration].gnd);
-	*scale_voltage *= vadc->graph[prop->calibration].dx;
-	*scale_voltage = div64_s64(*scale_voltage,
-		vadc->graph[prop->calibration].dy);
-	if (prop->calibration == VADC_CALIB_ABSOLUTE)
-		*scale_voltage +=
-		vadc->graph[prop->calibration].dx;
-
-	if (*scale_voltage < 0)
-		*scale_voltage = 0;
-}
-
-static int vadc_scale_volt(struct vadc_priv *vadc,
-			   const struct vadc_channel_prop *prop, u16 adc_code,
-			   int *result_uv)
-{
-	const struct vadc_prescale_ratio *prescale;
-	s64 voltage = 0, result = 0;
-
-	vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
-	prescale = &vadc_prescale_ratios[prop->prescale];
-	voltage = voltage * prescale->den;
-	result = div64_s64(voltage, prescale->num);
-	*result_uv = result;
-
-	return 0;
-}
-
-static int vadc_scale_therm(struct vadc_priv *vadc,
-			    const struct vadc_channel_prop *prop, u16 adc_code,
-			    int *result_mdec)
-{
-	s64 voltage = 0, result = 0;
-
-	vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
-	if (prop->calibration == VADC_CALIB_ABSOLUTE)
-		voltage = div64_s64(voltage, 1000);
-
-	vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
-			      ARRAY_SIZE(adcmap_100k_104ef_104fb),
-			      voltage, &result);
-	result *= 1000;
-	*result_mdec = result;
-
-	return 0;
-}
-
-static int vadc_scale_die_temp(struct vadc_priv *vadc,
-			       const struct vadc_channel_prop *prop,
-			       u16 adc_code, int *result_mdec)
-{
-	const struct vadc_prescale_ratio *prescale;
-	s64 voltage = 0;
-	u64 temp; /* Temporary variable for do_div */
-
-	vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
-	if (voltage > 0) {
-		prescale = &vadc_prescale_ratios[prop->prescale];
-		temp = voltage * prescale->den;
-		do_div(temp, prescale->num * 2);
-		voltage = temp;
-	} else {
-		voltage = 0;
-	}
-
-	voltage -= KELVINMIL_CELSIUSMIL;
-	*result_mdec = voltage;
-
-	return 0;
-}
-
-static int vadc_scale_chg_temp(struct vadc_priv *vadc,
-			       const struct vadc_channel_prop *prop,
-			       u16 adc_code, int *result_mdec)
-{
-	const struct vadc_prescale_ratio *prescale;
-	s64 voltage = 0, result = 0;
-
-	vadc_scale_calib(vadc, adc_code, prop, &voltage);
-
-	prescale = &vadc_prescale_ratios[prop->prescale];
-	voltage = voltage * prescale->den;
-	voltage = div64_s64(voltage, prescale->num);
-	voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
-	voltage = (voltage + PMI_CHG_SCALE_2);
-	result =  div64_s64(voltage, 1000000);
-	*result_mdec = result;
-
-	return 0;
-}
-
-static int vadc_decimation_from_dt(u32 value)
-{
-	if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
-	    value > VADC_DECIMATION_MAX)
-		return -EINVAL;
-
-	return __ffs64(value / VADC_DECIMATION_MIN);
-}
-
 static int vadc_prescaling_from_dt(u32 num, u32 den)
 {
 	unsigned int pre;
@@ -742,14 +449,6 @@  static int vadc_avg_samples_from_dt(u32 value)
 	return __ffs64(value);
 }
 
-static struct vadc_scale_fn scale_fn[] = {
-	[SCALE_DEFAULT] = {vadc_scale_volt},
-	[SCALE_THERM_100K_PULLUP] = {vadc_scale_therm},
-	[SCALE_PMIC_THERM] = {vadc_scale_die_temp},
-	[SCALE_XOTHERM] = {vadc_scale_therm},
-	[SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp},
-};
-
 static int vadc_read_raw(struct iio_dev *indio_dev,
 			 struct iio_chan_spec const *chan, int *val, int *val2,
 			 long mask)
@@ -766,7 +465,13 @@  static int vadc_read_raw(struct iio_dev *indio_dev,
 		if (ret)
 			break;
 
-		scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
+		ret = qcom_vadc_scale(prop->scale_fn_type,
+				&vadc->graph[prop->calibration],
+				&vadc_prescale_ratios[prop->prescale],
+				(prop->calibration == VADC_CALIB_ABSOLUTE),
+				adc_code, val);
+		if (ret)
+			break;
 
 		return IIO_VAL_INT;
 	case IIO_CHAN_INFO_RAW:
@@ -809,7 +514,7 @@  struct vadc_channels {
 	unsigned int prescale_index;
 	enum iio_chan_type type;
 	long info_mask;
-	unsigned int scale_fn;
+	enum vadc_scale_fn_type scale_fn_type;
 };
 
 #define VADC_CHAN(_dname, _type, _mask, _pre, _scale)			\
@@ -818,7 +523,7 @@  struct vadc_channels {
 		.prescale_index = _pre,					\
 		.type = _type,						\
 		.info_mask = _mask,					\
-		.scale_fn = _scale					\
+		.scale_fn_type = _scale					\
 	},								\
 
 #define VADC_NO_CHAN(_dname, _type, _mask, _pre)			\
@@ -976,7 +681,7 @@  static int vadc_get_dt_channel_data(struct device *dev,
 
 	ret = of_property_read_u32(node, "qcom,decimation", &value);
 	if (!ret) {
-		ret = vadc_decimation_from_dt(value);
+		ret = qcom_vadc_decimation_from_dt(value);
 		if (ret < 0) {
 			dev_err(dev, "%02x invalid decimation %d\n",
 				chan, value);
@@ -1068,7 +773,7 @@  static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
 			return ret;
 		}
 
-		prop.scale_fn = vadc_chans[prop.channel].scale_fn;
+		prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
 		vadc->chan_props[index] = prop;
 
 		vadc_chan = &vadc_chans[prop.channel];
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
new file mode 100644
index 000000000000..102fc51b10aa
--- /dev/null
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -0,0 +1,230 @@ 
+#include <linux/bug.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/math64.h>
+#include <linux/log2.h>
+#include <linux/err.h>
+
+#include "qcom-vadc-common.h"
+
+/* Voltage to temperature */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
+	{1758,	-40},
+	{1742,	-35},
+	{1719,	-30},
+	{1691,	-25},
+	{1654,	-20},
+	{1608,	-15},
+	{1551,	-10},
+	{1483,	-5},
+	{1404,	0},
+	{1315,	5},
+	{1218,	10},
+	{1114,	15},
+	{1007,	20},
+	{900,	25},
+	{795,	30},
+	{696,	35},
+	{605,	40},
+	{522,	45},
+	{448,	50},
+	{383,	55},
+	{327,	60},
+	{278,	65},
+	{237,	70},
+	{202,	75},
+	{172,	80},
+	{146,	85},
+	{125,	90},
+	{107,	95},
+	{92,	100},
+	{79,	105},
+	{68,	110},
+	{59,	115},
+	{51,	120},
+	{44,	125}
+};
+
+static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
+				      u32 tablesize, s32 input, s64 *output)
+{
+	bool descending = 1;
+	u32 i = 0;
+
+	if (!pts)
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].x < pts[1].x)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending) && (pts[i].x < input)) {
+			/* table entry is less than measured*/
+			 /* value and table is descending, stop */
+			break;
+		} else if ((!descending) &&
+				(pts[i].x > input)) {
+			/* table entry is greater than measured*/
+			/*value and table is ascending, stop */
+			break;
+		}
+		i++;
+	}
+
+	if (i == 0) {
+		*output = pts[0].y;
+	} else if (i == tablesize) {
+		*output = pts[tablesize - 1].y;
+	} else {
+		/* result is between search_index and search_index-1 */
+		/* interpolate linearly */
+		*output = (((s32)((pts[i].y - pts[i - 1].y) *
+			(input - pts[i - 1].x)) /
+			(pts[i].x - pts[i - 1].x)) +
+			pts[i - 1].y);
+	}
+
+	return 0;
+}
+
+static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
+				  u16 adc_code,
+				  bool absolute,
+				  s64 *scale_voltage)
+{
+	*scale_voltage = (adc_code - calib_graph->gnd);
+	*scale_voltage *= calib_graph->dx;
+	*scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
+	if (absolute)
+		*scale_voltage += calib_graph->dx;
+
+	if (*scale_voltage < 0)
+		*scale_voltage = 0;
+}
+
+static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
+				const struct vadc_prescale_ratio *prescale,
+				bool absolute, u16 adc_code,
+				int *result_uv)
+{
+	s64 voltage = 0, result = 0;
+
+	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+	voltage = voltage * prescale->den;
+	result = div64_s64(voltage, prescale->num);
+	*result_uv = result;
+
+	return 0;
+}
+
+static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
+				 const struct vadc_prescale_ratio *prescale,
+				 bool absolute, u16 adc_code,
+				 int *result_mdec)
+{
+	s64 voltage = 0, result = 0;
+	int ret;
+
+	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+	if (absolute)
+		voltage = div64_s64(voltage, 1000);
+
+	ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
+					 ARRAY_SIZE(adcmap_100k_104ef_104fb),
+					 voltage, &result);
+	if (ret)
+		return ret;
+
+	result *= 1000;
+	*result_mdec = result;
+
+	return 0;
+}
+
+static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
+				    const struct vadc_prescale_ratio *prescale,
+				    bool absolute,
+				    u16 adc_code, int *result_mdec)
+{
+	s64 voltage = 0;
+	u64 temp; /* Temporary variable for do_div */
+
+	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+	if (voltage > 0) {
+		temp = voltage * prescale->den;
+		do_div(temp, prescale->num * 2);
+		voltage = temp;
+	} else {
+		voltage = 0;
+	}
+
+	voltage -= KELVINMIL_CELSIUSMIL;
+	*result_mdec = voltage;
+
+	return 0;
+}
+
+static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
+				    const struct vadc_prescale_ratio *prescale,
+				    bool absolute,
+				    u16 adc_code, int *result_mdec)
+{
+	s64 voltage = 0, result = 0;
+
+	qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
+
+	voltage = voltage * prescale->den;
+	voltage = div64_s64(voltage, prescale->num);
+	voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
+	voltage = (voltage + PMI_CHG_SCALE_2);
+	result =  div64_s64(voltage, 1000000);
+	*result_mdec = result;
+
+	return 0;
+}
+
+int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
+		    const struct vadc_linear_graph *calib_graph,
+		    const struct vadc_prescale_ratio *prescale,
+		    bool absolute,
+		    u16 adc_code, int *result)
+{
+	switch (scaletype) {
+	case SCALE_DEFAULT:
+		return qcom_vadc_scale_volt(calib_graph, prescale,
+					    absolute, adc_code,
+					    result);
+	case SCALE_THERM_100K_PULLUP:
+	case SCALE_XOTHERM:
+		return qcom_vadc_scale_therm(calib_graph, prescale,
+					     absolute, adc_code,
+					     result);
+	case SCALE_PMIC_THERM:
+		return qcom_vadc_scale_die_temp(calib_graph, prescale,
+						absolute, adc_code,
+						result);
+	case SCALE_PMI_CHG_TEMP:
+		return qcom_vadc_scale_chg_temp(calib_graph, prescale,
+						absolute, adc_code,
+						result);
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(qcom_vadc_scale);
+
+int qcom_vadc_decimation_from_dt(u32 value)
+{
+	if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
+	    value > VADC_DECIMATION_MAX)
+		return -EINVAL;
+
+	return __ffs64(value / VADC_DECIMATION_MIN);
+}
+EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
new file mode 100644
index 000000000000..63c872a70adc
--- /dev/null
+++ b/drivers/iio/adc/qcom-vadc-common.h
@@ -0,0 +1,108 @@ 
+/*
+ * Code shared between the different Qualcomm PMIC voltage ADCs
+ */
+
+#ifndef QCOM_VADC_COMMON_H
+#define QCOM_VADC_COMMON_H
+
+#define VADC_CONV_TIME_MIN_US			2000
+#define VADC_CONV_TIME_MAX_US			2100
+
+/* Min ADC code represents 0V */
+#define VADC_MIN_ADC_CODE			0x6000
+/* Max ADC code represents full-scale range of 1.8V */
+#define VADC_MAX_ADC_CODE			0xa800
+
+#define VADC_ABSOLUTE_RANGE_UV			625000
+#define VADC_RATIOMETRIC_RANGE			1800
+
+#define VADC_DEF_PRESCALING			0 /* 1:1 */
+#define VADC_DEF_DECIMATION			0 /* 512 */
+#define VADC_DEF_HW_SETTLE_TIME			0 /* 0 us */
+#define VADC_DEF_AVG_SAMPLES			0 /* 1 sample */
+#define VADC_DEF_CALIB_TYPE			VADC_CALIB_ABSOLUTE
+
+#define VADC_DECIMATION_MIN			512
+#define VADC_DECIMATION_MAX			4096
+
+#define VADC_HW_SETTLE_DELAY_MAX		10000
+#define VADC_AVG_SAMPLES_MAX			512
+
+#define KELVINMIL_CELSIUSMIL			273150
+
+#define PMI_CHG_SCALE_1				-138890
+#define PMI_CHG_SCALE_2				391750000000LL
+
+/**
+ * struct vadc_map_pt - Map the graph representation for ADC channel
+ * @x: Represent the ADC digitized code.
+ * @y: Represent the physical data which can be temperature, voltage,
+ *     resistance.
+ */
+struct vadc_map_pt {
+	s32 x;
+	s32 y;
+};
+
+/*
+ * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
+ * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
+ * calibration.
+ */
+enum vadc_calibration {
+	VADC_CALIB_ABSOLUTE = 0,
+	VADC_CALIB_RATIOMETRIC
+};
+
+/**
+ * struct vadc_linear_graph - Represent ADC characteristics.
+ * @dy: numerator slope to calculate the gain.
+ * @dx: denominator slope to calculate the gain.
+ * @gnd: A/D word of the ground reference used for the channel.
+ *
+ * Each ADC device has different offset and gain parameters which are
+ * computed to calibrate the device.
+ */
+struct vadc_linear_graph {
+	s32 dy;
+	s32 dx;
+	s32 gnd;
+};
+
+/**
+ * struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
+ * @num: the inverse numerator of the gain applied to the input channel.
+ * @den: the inverse denominator of the gain applied to the input channel.
+ */
+struct vadc_prescale_ratio {
+	u32 num;
+	u32 den;
+};
+
+/**
+ * enum vadc_scale_fn_type - Scaling function to convert ADC code to
+ *				physical scaled units for the channel.
+ * SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
+ * SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
+ *				 Uses a mapping table with 100K pullup.
+ * SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
+ * SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
+ */
+enum vadc_scale_fn_type {
+	SCALE_DEFAULT = 0,
+	SCALE_THERM_100K_PULLUP,
+	SCALE_PMIC_THERM,
+	SCALE_XOTHERM,
+	SCALE_PMI_CHG_TEMP,
+};
+
+int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
+		    const struct vadc_linear_graph *calib_graph,
+		    const struct vadc_prescale_ratio *prescale,
+		    bool absolute,
+		    u16 adc_code, int *result_mdec);
+
+int qcom_vadc_decimation_from_dt(u32 value);
+
+#endif /* QCOM_VADC_COMMON_H */