Message ID | 20170404120819.6921-2-linus.walleij@linaro.org (mailing list archive) |
---|---|
State | Not Applicable, archived |
Delegated to: | Andy Gross |
Headers | show |
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 --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 */
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