[PATCH 2/3 v5] iio: adc: break out common code from SPMI VADC
Jonathan Cameron
jic23 at kernel.org
Sat Apr 8 09:41:20 PDT 2017
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 at lists.infradead.org
> Cc: linux-arm-msm at vger.kernel.org
> Cc: Ivan T. Ivanov <iivanov.xz at gmail.com>
> Cc: Andy Gross <andy.gross at linaro.org>
> Cc: Bjorn Andersson <bjorn.andersson at linaro.org>
> Cc: Stephen Boyd <sboyd at codeaurora.org>
> Cc: Srinivas Kandagatla <srinivas.kandagatla at linaro.org>
> Cc: Rama Krishna Phani A <rphani at codeaurora.org>
> Signed-off-by: Linus Walleij <linus.walleij at 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 */
>
More information about the linux-arm-kernel
mailing list