diff options
author | Siddartha Mohanadoss <smohanad@codeaurora.org> | 2018-08-02 18:43:53 -0700 |
---|---|---|
committer | Jonathan Cameron <Jonathan.Cameron@huawei.com> | 2018-08-18 18:11:43 +0100 |
commit | e13d757279bbc59776c8435fb94e54b5a58bdd0b (patch) | |
tree | 82a059a180b0dae280fe34239114a15ee4f6be18 /drivers/iio/adc/qcom-vadc-common.c | |
parent | 2fca585502716c25c52ad4fe54207f80762bc7b4 (diff) | |
download | linux-e13d757279bbc59776c8435fb94e54b5a58bdd0b.tar.bz2 |
iio: adc: Add QCOM SPMI PMIC5 ADC driver
This patch adds support for QCOM SPMI PMIC5 family
of ADC driver that supports hardware based offset and
gain compensation. The ADC peripheral can measure both
voltage and current channels whose input signal is
connected to the PMIC ADC AMUX.
The register set and configuration has been refreshed
compared to the prior QCOM PMIC ADC family. Register
ADC5 as part of the IIO framework.
Signed-off-by: Siddartha Mohanadoss <smohanad@codeaurora.org>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Diffstat (limited to 'drivers/iio/adc/qcom-vadc-common.c')
-rw-r--r-- | drivers/iio/adc/qcom-vadc-common.c | 189 |
1 files changed, 184 insertions, 5 deletions
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index fe3d7826783c..dcd7fb5b9fb2 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -47,8 +47,79 @@ static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = { {44, 125} }; +/* + * Voltage to temperature table for 100k pull up for NTCG104EF104 with + * 1.875V reference. + */ +static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = { + { 1831, -40000 }, + { 1814, -35000 }, + { 1791, -30000 }, + { 1761, -25000 }, + { 1723, -20000 }, + { 1675, -15000 }, + { 1616, -10000 }, + { 1545, -5000 }, + { 1463, 0 }, + { 1370, 5000 }, + { 1268, 10000 }, + { 1160, 15000 }, + { 1049, 20000 }, + { 937, 25000 }, + { 828, 30000 }, + { 726, 35000 }, + { 630, 40000 }, + { 544, 45000 }, + { 467, 50000 }, + { 399, 55000 }, + { 340, 60000 }, + { 290, 65000 }, + { 247, 70000 }, + { 209, 75000 }, + { 179, 80000 }, + { 153, 85000 }, + { 130, 90000 }, + { 112, 95000 }, + { 96, 100000 }, + { 82, 105000 }, + { 71, 110000 }, + { 62, 115000 }, + { 53, 120000 }, + { 46, 125000 }, +}; + +static int qcom_vadc_scale_hw_calib_volt( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv); +static int qcom_vadc_scale_hw_calib_therm( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_smb_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_chg5_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); +static int qcom_vadc_scale_hw_calib_die_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec); + +static struct qcom_adc5_scale_type scale_adc5_fn[] = { + [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt}, + [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm}, + [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp}, + [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp}, + [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp}, +}; + static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts, - u32 tablesize, s32 input, s64 *output) + u32 tablesize, s32 input, int *output) { bool descending = 1; u32 i = 0; @@ -128,7 +199,7 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, bool absolute, u16 adc_code, int *result_mdec) { - s64 voltage = 0, result = 0; + s64 voltage = 0; int ret; qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage); @@ -138,12 +209,11 @@ static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph, ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb, ARRAY_SIZE(adcmap_100k_104ef_104fb), - voltage, &result); + voltage, result_mdec); if (ret) return ret; - result *= 1000; - *result_mdec = result; + *result_mdec *= 1000; return 0; } @@ -191,6 +261,99 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, return 0; } +static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + unsigned int factor) +{ + s64 voltage, temp, adc_vdd_ref_mv = 1875; + + /* + * The normal data range is between 0V to 1.875V. On cases where + * we read low voltage values, the ADC code can go beyond the + * range and the scale result is incorrect so we clamp the values + * for the cases where the code represents a value below 0V + */ + if (adc_code > VADC5_MAX_CODE) + adc_code = 0; + + /* (ADC code * vref_vadc (1.875V)) / full_scale_code */ + voltage = (s64) adc_code * adc_vdd_ref_mv * 1000; + voltage = div64_s64(voltage, data->full_scale_code_volt); + if (voltage > 0) { + voltage *= prescale->den; + temp = prescale->num * factor; + voltage = div64_s64(voltage, temp); + } else { + voltage = 0; + } + + return (int) voltage; +} + +static int qcom_vadc_scale_hw_calib_volt( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_uv) +{ + *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1); + + return 0; +} + +static int qcom_vadc_scale_hw_calib_therm( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + int voltage; + + voltage = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 1000); + + /* Map voltage to temperature from look-up table */ + return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + voltage, result_mdec); +} + +static int qcom_vadc_scale_hw_calib_die_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 2); + *result_mdec -= KELVINMIL_CELSIUSMIL; + + return 0; +} + +static int qcom_vadc_scale_hw_smb_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100, + prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR); + *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec; + + return 0; +} + +static int qcom_vadc_scale_hw_chg5_temp( + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result_mdec) +{ + *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code, + prescale, data, 4); + *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec; + + 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, @@ -221,6 +384,22 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, } EXPORT_SYMBOL(qcom_vadc_scale); +int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, + const struct vadc_prescale_ratio *prescale, + const struct adc5_data *data, + u16 adc_code, int *result) +{ + if (!(scaletype >= SCALE_HW_CALIB_DEFAULT && + scaletype < SCALE_HW_CALIB_INVALID)) { + pr_err("Invalid scale type %d\n", scaletype); + return -EINVAL; + } + + return scale_adc5_fn[scaletype].scale_fn(prescale, data, + adc_code, result); +} +EXPORT_SYMBOL(qcom_adc5_hw_scale); + int qcom_vadc_decimation_from_dt(u32 value) { if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN || |