197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20917de94SStanimir Varbanov /*
3ba71704aSRama Krishna Phani A * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
40917de94SStanimir Varbanov */
50917de94SStanimir Varbanov
60917de94SStanimir Varbanov #include <linux/bitops.h>
70917de94SStanimir Varbanov #include <linux/completion.h>
80917de94SStanimir Varbanov #include <linux/delay.h>
90917de94SStanimir Varbanov #include <linux/err.h>
10ec82edb2SDmitry Baryshkov #include <linux/iio/adc/qcom-vadc-common.h>
110917de94SStanimir Varbanov #include <linux/iio/iio.h>
120917de94SStanimir Varbanov #include <linux/interrupt.h>
130917de94SStanimir Varbanov #include <linux/kernel.h>
14937125acSIvan T. Ivanov #include <linux/math64.h>
150917de94SStanimir Varbanov #include <linux/module.h>
16e7c672d0SNuno Sá #include <linux/mod_devicetable.h>
170917de94SStanimir Varbanov #include <linux/platform_device.h>
18e7c672d0SNuno Sá #include <linux/property.h>
190917de94SStanimir Varbanov #include <linux/regmap.h>
200917de94SStanimir Varbanov #include <linux/slab.h>
210917de94SStanimir Varbanov #include <linux/log2.h>
220917de94SStanimir Varbanov
230917de94SStanimir Varbanov #include <dt-bindings/iio/qcom,spmi-vadc.h>
240917de94SStanimir Varbanov
250917de94SStanimir Varbanov /* VADC register and bit definitions */
260917de94SStanimir Varbanov #define VADC_REVISION2 0x1
270917de94SStanimir Varbanov #define VADC_REVISION2_SUPPORTED_VADC 1
280917de94SStanimir Varbanov
290917de94SStanimir Varbanov #define VADC_PERPH_TYPE 0x4
300917de94SStanimir Varbanov #define VADC_PERPH_TYPE_ADC 8
310917de94SStanimir Varbanov
320917de94SStanimir Varbanov #define VADC_PERPH_SUBTYPE 0x5
330917de94SStanimir Varbanov #define VADC_PERPH_SUBTYPE_VADC 1
340917de94SStanimir Varbanov
350917de94SStanimir Varbanov #define VADC_STATUS1 0x8
360917de94SStanimir Varbanov #define VADC_STATUS1_OP_MODE 4
370917de94SStanimir Varbanov #define VADC_STATUS1_REQ_STS BIT(1)
380917de94SStanimir Varbanov #define VADC_STATUS1_EOC BIT(0)
390917de94SStanimir Varbanov #define VADC_STATUS1_REQ_STS_EOC_MASK 0x3
400917de94SStanimir Varbanov
410917de94SStanimir Varbanov #define VADC_MODE_CTL 0x40
420917de94SStanimir Varbanov #define VADC_OP_MODE_SHIFT 3
430917de94SStanimir Varbanov #define VADC_OP_MODE_NORMAL 0
440917de94SStanimir Varbanov #define VADC_AMUX_TRIM_EN BIT(1)
450917de94SStanimir Varbanov #define VADC_ADC_TRIM_EN BIT(0)
460917de94SStanimir Varbanov
470917de94SStanimir Varbanov #define VADC_EN_CTL1 0x46
480917de94SStanimir Varbanov #define VADC_EN_CTL1_SET BIT(7)
490917de94SStanimir Varbanov
500917de94SStanimir Varbanov #define VADC_ADC_CH_SEL_CTL 0x48
510917de94SStanimir Varbanov
520917de94SStanimir Varbanov #define VADC_ADC_DIG_PARAM 0x50
530917de94SStanimir Varbanov #define VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 2
540917de94SStanimir Varbanov
550917de94SStanimir Varbanov #define VADC_HW_SETTLE_DELAY 0x51
560917de94SStanimir Varbanov
570917de94SStanimir Varbanov #define VADC_CONV_REQ 0x52
580917de94SStanimir Varbanov #define VADC_CONV_REQ_SET BIT(7)
590917de94SStanimir Varbanov
600917de94SStanimir Varbanov #define VADC_FAST_AVG_CTL 0x5a
610917de94SStanimir Varbanov #define VADC_FAST_AVG_EN 0x5b
620917de94SStanimir Varbanov #define VADC_FAST_AVG_EN_SET BIT(7)
630917de94SStanimir Varbanov
640917de94SStanimir Varbanov #define VADC_ACCESS 0xd0
650917de94SStanimir Varbanov #define VADC_ACCESS_DATA 0xa5
660917de94SStanimir Varbanov
670917de94SStanimir Varbanov #define VADC_PERH_RESET_CTL3 0xda
680917de94SStanimir Varbanov #define VADC_FOLLOW_WARM_RB BIT(2)
690917de94SStanimir Varbanov
700917de94SStanimir Varbanov #define VADC_DATA 0x60 /* 16 bits */
710917de94SStanimir Varbanov
720917de94SStanimir Varbanov #define VADC_CHAN_MIN VADC_USBIN
730917de94SStanimir Varbanov #define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
740917de94SStanimir Varbanov
757c271eeaSRama Krishna Phani A /**
760917de94SStanimir Varbanov * struct vadc_channel_prop - VADC channel property.
770917de94SStanimir Varbanov * @channel: channel number, refer to the channel list.
780917de94SStanimir Varbanov * @calibration: calibration type.
790917de94SStanimir Varbanov * @decimation: sampling rate supported for the channel.
800917de94SStanimir Varbanov * @prescale: channel scaling performed on the input signal.
810917de94SStanimir Varbanov * @hw_settle_time: the time between AMUX being configured and the
820917de94SStanimir Varbanov * start of conversion.
830917de94SStanimir Varbanov * @avg_samples: ability to provide single result from the ADC
840917de94SStanimir Varbanov * that is an average of multiple measurements.
85e932d4f0SLinus Walleij * @scale_fn_type: Represents the scaling function to convert voltage
867c271eeaSRama Krishna Phani A * physical units desired by the client for the channel.
87*3d0b1260SMarijn Suijten * @channel_name: Channel name used in device tree.
880917de94SStanimir Varbanov */
890917de94SStanimir Varbanov struct vadc_channel_prop {
900917de94SStanimir Varbanov unsigned int channel;
910917de94SStanimir Varbanov enum vadc_calibration calibration;
920917de94SStanimir Varbanov unsigned int decimation;
930917de94SStanimir Varbanov unsigned int prescale;
940917de94SStanimir Varbanov unsigned int hw_settle_time;
950917de94SStanimir Varbanov unsigned int avg_samples;
96e932d4f0SLinus Walleij enum vadc_scale_fn_type scale_fn_type;
97*3d0b1260SMarijn Suijten const char *channel_name;
980917de94SStanimir Varbanov };
990917de94SStanimir Varbanov
1000917de94SStanimir Varbanov /**
1010917de94SStanimir Varbanov * struct vadc_priv - VADC private structure.
1020917de94SStanimir Varbanov * @regmap: pointer to struct regmap.
1030917de94SStanimir Varbanov * @dev: pointer to struct device.
1040917de94SStanimir Varbanov * @base: base address for the ADC peripheral.
1050917de94SStanimir Varbanov * @nchannels: number of VADC channels.
1060917de94SStanimir Varbanov * @chan_props: array of VADC channel properties.
1070917de94SStanimir Varbanov * @iio_chans: array of IIO channels specification.
1080917de94SStanimir Varbanov * @are_ref_measured: are reference points measured.
1090917de94SStanimir Varbanov * @poll_eoc: use polling instead of interrupt.
1100917de94SStanimir Varbanov * @complete: VADC result notification after interrupt is received.
1110917de94SStanimir Varbanov * @graph: store parameters for calibration.
1120917de94SStanimir Varbanov * @lock: ADC lock for access to the peripheral.
1130917de94SStanimir Varbanov */
1140917de94SStanimir Varbanov struct vadc_priv {
1150917de94SStanimir Varbanov struct regmap *regmap;
1160917de94SStanimir Varbanov struct device *dev;
1170917de94SStanimir Varbanov u16 base;
1180917de94SStanimir Varbanov unsigned int nchannels;
1190917de94SStanimir Varbanov struct vadc_channel_prop *chan_props;
1200917de94SStanimir Varbanov struct iio_chan_spec *iio_chans;
1210917de94SStanimir Varbanov bool are_ref_measured;
1220917de94SStanimir Varbanov bool poll_eoc;
1230917de94SStanimir Varbanov struct completion complete;
1240917de94SStanimir Varbanov struct vadc_linear_graph graph[2];
1250917de94SStanimir Varbanov struct mutex lock;
1260917de94SStanimir Varbanov };
1270917de94SStanimir Varbanov
128a5e9b2ddSAndy Shevchenko static const struct u32_fract vadc_prescale_ratios[] = {
129a5e9b2ddSAndy Shevchenko { .numerator = 1, .denominator = 1 },
130a5e9b2ddSAndy Shevchenko { .numerator = 1, .denominator = 3 },
131a5e9b2ddSAndy Shevchenko { .numerator = 1, .denominator = 4 },
132a5e9b2ddSAndy Shevchenko { .numerator = 1, .denominator = 6 },
133a5e9b2ddSAndy Shevchenko { .numerator = 1, .denominator = 20 },
134a5e9b2ddSAndy Shevchenko { .numerator = 1, .denominator = 8 },
135a5e9b2ddSAndy Shevchenko { .numerator = 10, .denominator = 81 },
136a5e9b2ddSAndy Shevchenko { .numerator = 1, .denominator = 10 },
1370917de94SStanimir Varbanov };
1380917de94SStanimir Varbanov
vadc_read(struct vadc_priv * vadc,u16 offset,u8 * data)1390917de94SStanimir Varbanov static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
1400917de94SStanimir Varbanov {
1410917de94SStanimir Varbanov return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
1420917de94SStanimir Varbanov }
1430917de94SStanimir Varbanov
vadc_write(struct vadc_priv * vadc,u16 offset,u8 data)1440917de94SStanimir Varbanov static int vadc_write(struct vadc_priv *vadc, u16 offset, u8 data)
1450917de94SStanimir Varbanov {
1460917de94SStanimir Varbanov return regmap_write(vadc->regmap, vadc->base + offset, data);
1470917de94SStanimir Varbanov }
1480917de94SStanimir Varbanov
vadc_reset(struct vadc_priv * vadc)1490917de94SStanimir Varbanov static int vadc_reset(struct vadc_priv *vadc)
1500917de94SStanimir Varbanov {
1510917de94SStanimir Varbanov u8 data;
1520917de94SStanimir Varbanov int ret;
1530917de94SStanimir Varbanov
1540917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
1550917de94SStanimir Varbanov if (ret)
1560917de94SStanimir Varbanov return ret;
1570917de94SStanimir Varbanov
1580917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_PERH_RESET_CTL3, &data);
1590917de94SStanimir Varbanov if (ret)
1600917de94SStanimir Varbanov return ret;
1610917de94SStanimir Varbanov
1620917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
1630917de94SStanimir Varbanov if (ret)
1640917de94SStanimir Varbanov return ret;
1650917de94SStanimir Varbanov
1660917de94SStanimir Varbanov data |= VADC_FOLLOW_WARM_RB;
1670917de94SStanimir Varbanov
1680917de94SStanimir Varbanov return vadc_write(vadc, VADC_PERH_RESET_CTL3, data);
1690917de94SStanimir Varbanov }
1700917de94SStanimir Varbanov
vadc_set_state(struct vadc_priv * vadc,bool state)1710917de94SStanimir Varbanov static int vadc_set_state(struct vadc_priv *vadc, bool state)
1720917de94SStanimir Varbanov {
1730917de94SStanimir Varbanov return vadc_write(vadc, VADC_EN_CTL1, state ? VADC_EN_CTL1_SET : 0);
1740917de94SStanimir Varbanov }
1750917de94SStanimir Varbanov
vadc_show_status(struct vadc_priv * vadc)1760917de94SStanimir Varbanov static void vadc_show_status(struct vadc_priv *vadc)
1770917de94SStanimir Varbanov {
1780917de94SStanimir Varbanov u8 mode, sta1, chan, dig, en, req;
1790917de94SStanimir Varbanov int ret;
1800917de94SStanimir Varbanov
1810917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_MODE_CTL, &mode);
1820917de94SStanimir Varbanov if (ret)
1830917de94SStanimir Varbanov return;
1840917de94SStanimir Varbanov
1850917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_ADC_DIG_PARAM, &dig);
1860917de94SStanimir Varbanov if (ret)
1870917de94SStanimir Varbanov return;
1880917de94SStanimir Varbanov
1890917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_ADC_CH_SEL_CTL, &chan);
1900917de94SStanimir Varbanov if (ret)
1910917de94SStanimir Varbanov return;
1920917de94SStanimir Varbanov
1930917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_CONV_REQ, &req);
1940917de94SStanimir Varbanov if (ret)
1950917de94SStanimir Varbanov return;
1960917de94SStanimir Varbanov
1970917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_STATUS1, &sta1);
1980917de94SStanimir Varbanov if (ret)
1990917de94SStanimir Varbanov return;
2000917de94SStanimir Varbanov
2010917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_EN_CTL1, &en);
2020917de94SStanimir Varbanov if (ret)
2030917de94SStanimir Varbanov return;
2040917de94SStanimir Varbanov
2050917de94SStanimir Varbanov dev_err(vadc->dev,
2060917de94SStanimir Varbanov "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
2070917de94SStanimir Varbanov mode, en, chan, dig, req, sta1);
2080917de94SStanimir Varbanov }
2090917de94SStanimir Varbanov
vadc_configure(struct vadc_priv * vadc,struct vadc_channel_prop * prop)2100917de94SStanimir Varbanov static int vadc_configure(struct vadc_priv *vadc,
2110917de94SStanimir Varbanov struct vadc_channel_prop *prop)
2120917de94SStanimir Varbanov {
2130917de94SStanimir Varbanov u8 decimation, mode_ctrl;
2140917de94SStanimir Varbanov int ret;
2150917de94SStanimir Varbanov
2160917de94SStanimir Varbanov /* Mode selection */
2170917de94SStanimir Varbanov mode_ctrl = (VADC_OP_MODE_NORMAL << VADC_OP_MODE_SHIFT) |
2180917de94SStanimir Varbanov VADC_ADC_TRIM_EN | VADC_AMUX_TRIM_EN;
2190917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_MODE_CTL, mode_ctrl);
2200917de94SStanimir Varbanov if (ret)
2210917de94SStanimir Varbanov return ret;
2220917de94SStanimir Varbanov
2230917de94SStanimir Varbanov /* Channel selection */
2240917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_ADC_CH_SEL_CTL, prop->channel);
2250917de94SStanimir Varbanov if (ret)
2260917de94SStanimir Varbanov return ret;
2270917de94SStanimir Varbanov
2280917de94SStanimir Varbanov /* Digital parameter setup */
2290917de94SStanimir Varbanov decimation = prop->decimation << VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT;
2300917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_ADC_DIG_PARAM, decimation);
2310917de94SStanimir Varbanov if (ret)
2320917de94SStanimir Varbanov return ret;
2330917de94SStanimir Varbanov
2340917de94SStanimir Varbanov /* HW settle time delay */
2350917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_HW_SETTLE_DELAY, prop->hw_settle_time);
2360917de94SStanimir Varbanov if (ret)
2370917de94SStanimir Varbanov return ret;
2380917de94SStanimir Varbanov
2390917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_FAST_AVG_CTL, prop->avg_samples);
2400917de94SStanimir Varbanov if (ret)
2410917de94SStanimir Varbanov return ret;
2420917de94SStanimir Varbanov
2430917de94SStanimir Varbanov if (prop->avg_samples)
2440917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_FAST_AVG_EN, VADC_FAST_AVG_EN_SET);
2450917de94SStanimir Varbanov else
2460917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_FAST_AVG_EN, 0);
2470917de94SStanimir Varbanov
2480917de94SStanimir Varbanov return ret;
2490917de94SStanimir Varbanov }
2500917de94SStanimir Varbanov
vadc_poll_wait_eoc(struct vadc_priv * vadc,unsigned int interval_us)2510917de94SStanimir Varbanov static int vadc_poll_wait_eoc(struct vadc_priv *vadc, unsigned int interval_us)
2520917de94SStanimir Varbanov {
2530917de94SStanimir Varbanov unsigned int count, retry;
2540917de94SStanimir Varbanov u8 sta1;
2550917de94SStanimir Varbanov int ret;
2560917de94SStanimir Varbanov
2570917de94SStanimir Varbanov retry = interval_us / VADC_CONV_TIME_MIN_US;
2580917de94SStanimir Varbanov
2590917de94SStanimir Varbanov for (count = 0; count < retry; count++) {
2600917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_STATUS1, &sta1);
2610917de94SStanimir Varbanov if (ret)
2620917de94SStanimir Varbanov return ret;
2630917de94SStanimir Varbanov
2640917de94SStanimir Varbanov sta1 &= VADC_STATUS1_REQ_STS_EOC_MASK;
2650917de94SStanimir Varbanov if (sta1 == VADC_STATUS1_EOC)
2660917de94SStanimir Varbanov return 0;
2670917de94SStanimir Varbanov
2680917de94SStanimir Varbanov usleep_range(VADC_CONV_TIME_MIN_US, VADC_CONV_TIME_MAX_US);
2690917de94SStanimir Varbanov }
2700917de94SStanimir Varbanov
2710917de94SStanimir Varbanov vadc_show_status(vadc);
2720917de94SStanimir Varbanov
2730917de94SStanimir Varbanov return -ETIMEDOUT;
2740917de94SStanimir Varbanov }
2750917de94SStanimir Varbanov
vadc_read_result(struct vadc_priv * vadc,u16 * data)2760917de94SStanimir Varbanov static int vadc_read_result(struct vadc_priv *vadc, u16 *data)
2770917de94SStanimir Varbanov {
2780917de94SStanimir Varbanov int ret;
2790917de94SStanimir Varbanov
2800917de94SStanimir Varbanov ret = regmap_bulk_read(vadc->regmap, vadc->base + VADC_DATA, data, 2);
2810917de94SStanimir Varbanov if (ret)
2820917de94SStanimir Varbanov return ret;
2830917de94SStanimir Varbanov
2840917de94SStanimir Varbanov *data = clamp_t(u16, *data, VADC_MIN_ADC_CODE, VADC_MAX_ADC_CODE);
2850917de94SStanimir Varbanov
2860917de94SStanimir Varbanov return 0;
2870917de94SStanimir Varbanov }
2880917de94SStanimir Varbanov
vadc_get_channel(struct vadc_priv * vadc,unsigned int num)2890917de94SStanimir Varbanov static struct vadc_channel_prop *vadc_get_channel(struct vadc_priv *vadc,
2900917de94SStanimir Varbanov unsigned int num)
2910917de94SStanimir Varbanov {
2920917de94SStanimir Varbanov unsigned int i;
2930917de94SStanimir Varbanov
2940917de94SStanimir Varbanov for (i = 0; i < vadc->nchannels; i++)
2950917de94SStanimir Varbanov if (vadc->chan_props[i].channel == num)
2960917de94SStanimir Varbanov return &vadc->chan_props[i];
2970917de94SStanimir Varbanov
2980917de94SStanimir Varbanov dev_dbg(vadc->dev, "no such channel %02x\n", num);
2990917de94SStanimir Varbanov
3000917de94SStanimir Varbanov return NULL;
3010917de94SStanimir Varbanov }
3020917de94SStanimir Varbanov
vadc_do_conversion(struct vadc_priv * vadc,struct vadc_channel_prop * prop,u16 * data)3030917de94SStanimir Varbanov static int vadc_do_conversion(struct vadc_priv *vadc,
3040917de94SStanimir Varbanov struct vadc_channel_prop *prop, u16 *data)
3050917de94SStanimir Varbanov {
3060917de94SStanimir Varbanov unsigned int timeout;
3070917de94SStanimir Varbanov int ret;
3080917de94SStanimir Varbanov
3090917de94SStanimir Varbanov mutex_lock(&vadc->lock);
3100917de94SStanimir Varbanov
3110917de94SStanimir Varbanov ret = vadc_configure(vadc, prop);
3120917de94SStanimir Varbanov if (ret)
3130917de94SStanimir Varbanov goto unlock;
3140917de94SStanimir Varbanov
3150917de94SStanimir Varbanov if (!vadc->poll_eoc)
3160917de94SStanimir Varbanov reinit_completion(&vadc->complete);
3170917de94SStanimir Varbanov
3180917de94SStanimir Varbanov ret = vadc_set_state(vadc, true);
3190917de94SStanimir Varbanov if (ret)
3200917de94SStanimir Varbanov goto unlock;
3210917de94SStanimir Varbanov
3220917de94SStanimir Varbanov ret = vadc_write(vadc, VADC_CONV_REQ, VADC_CONV_REQ_SET);
3230917de94SStanimir Varbanov if (ret)
3240917de94SStanimir Varbanov goto err_disable;
3250917de94SStanimir Varbanov
3260917de94SStanimir Varbanov timeout = BIT(prop->avg_samples) * VADC_CONV_TIME_MIN_US * 2;
3270917de94SStanimir Varbanov
3280917de94SStanimir Varbanov if (vadc->poll_eoc) {
3290917de94SStanimir Varbanov ret = vadc_poll_wait_eoc(vadc, timeout);
3300917de94SStanimir Varbanov } else {
3310917de94SStanimir Varbanov ret = wait_for_completion_timeout(&vadc->complete, timeout);
3320917de94SStanimir Varbanov if (!ret) {
3330917de94SStanimir Varbanov ret = -ETIMEDOUT;
3340917de94SStanimir Varbanov goto err_disable;
3350917de94SStanimir Varbanov }
3360917de94SStanimir Varbanov
3370917de94SStanimir Varbanov /* Double check conversion status */
3380917de94SStanimir Varbanov ret = vadc_poll_wait_eoc(vadc, VADC_CONV_TIME_MIN_US);
3390917de94SStanimir Varbanov if (ret)
3400917de94SStanimir Varbanov goto err_disable;
3410917de94SStanimir Varbanov }
3420917de94SStanimir Varbanov
3430917de94SStanimir Varbanov ret = vadc_read_result(vadc, data);
3440917de94SStanimir Varbanov
3450917de94SStanimir Varbanov err_disable:
3460917de94SStanimir Varbanov vadc_set_state(vadc, false);
3470917de94SStanimir Varbanov if (ret)
3480917de94SStanimir Varbanov dev_err(vadc->dev, "conversion failed\n");
3490917de94SStanimir Varbanov unlock:
3500917de94SStanimir Varbanov mutex_unlock(&vadc->lock);
3510917de94SStanimir Varbanov return ret;
3520917de94SStanimir Varbanov }
3530917de94SStanimir Varbanov
vadc_measure_ref_points(struct vadc_priv * vadc)3540917de94SStanimir Varbanov static int vadc_measure_ref_points(struct vadc_priv *vadc)
3550917de94SStanimir Varbanov {
3560917de94SStanimir Varbanov struct vadc_channel_prop *prop;
3570917de94SStanimir Varbanov u16 read_1, read_2;
3580917de94SStanimir Varbanov int ret;
3590917de94SStanimir Varbanov
3607c271eeaSRama Krishna Phani A vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
3610917de94SStanimir Varbanov vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
3620917de94SStanimir Varbanov
3630917de94SStanimir Varbanov prop = vadc_get_channel(vadc, VADC_REF_1250MV);
3640917de94SStanimir Varbanov ret = vadc_do_conversion(vadc, prop, &read_1);
3650917de94SStanimir Varbanov if (ret)
3660917de94SStanimir Varbanov goto err;
3670917de94SStanimir Varbanov
3680917de94SStanimir Varbanov /* Try with buffered 625mV channel first */
3690917de94SStanimir Varbanov prop = vadc_get_channel(vadc, VADC_SPARE1);
3700917de94SStanimir Varbanov if (!prop)
3710917de94SStanimir Varbanov prop = vadc_get_channel(vadc, VADC_REF_625MV);
3720917de94SStanimir Varbanov
3730917de94SStanimir Varbanov ret = vadc_do_conversion(vadc, prop, &read_2);
3740917de94SStanimir Varbanov if (ret)
3750917de94SStanimir Varbanov goto err;
3760917de94SStanimir Varbanov
3770917de94SStanimir Varbanov if (read_1 == read_2) {
3780917de94SStanimir Varbanov ret = -EINVAL;
3790917de94SStanimir Varbanov goto err;
3800917de94SStanimir Varbanov }
3810917de94SStanimir Varbanov
3820917de94SStanimir Varbanov vadc->graph[VADC_CALIB_ABSOLUTE].dy = read_1 - read_2;
3830917de94SStanimir Varbanov vadc->graph[VADC_CALIB_ABSOLUTE].gnd = read_2;
3840917de94SStanimir Varbanov
3850917de94SStanimir Varbanov /* Ratiometric calibration */
3860917de94SStanimir Varbanov prop = vadc_get_channel(vadc, VADC_VDD_VADC);
3870917de94SStanimir Varbanov ret = vadc_do_conversion(vadc, prop, &read_1);
3880917de94SStanimir Varbanov if (ret)
3890917de94SStanimir Varbanov goto err;
3900917de94SStanimir Varbanov
3910917de94SStanimir Varbanov prop = vadc_get_channel(vadc, VADC_GND_REF);
3920917de94SStanimir Varbanov ret = vadc_do_conversion(vadc, prop, &read_2);
3930917de94SStanimir Varbanov if (ret)
3940917de94SStanimir Varbanov goto err;
3950917de94SStanimir Varbanov
3960917de94SStanimir Varbanov if (read_1 == read_2) {
3970917de94SStanimir Varbanov ret = -EINVAL;
3980917de94SStanimir Varbanov goto err;
3990917de94SStanimir Varbanov }
4000917de94SStanimir Varbanov
4010917de94SStanimir Varbanov vadc->graph[VADC_CALIB_RATIOMETRIC].dy = read_1 - read_2;
4020917de94SStanimir Varbanov vadc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_2;
4030917de94SStanimir Varbanov err:
4040917de94SStanimir Varbanov if (ret)
4050917de94SStanimir Varbanov dev_err(vadc->dev, "measure reference points failed\n");
4060917de94SStanimir Varbanov
4070917de94SStanimir Varbanov return ret;
4080917de94SStanimir Varbanov }
4090917de94SStanimir Varbanov
vadc_prescaling_from_dt(u32 numerator,u32 denominator)410a5e9b2ddSAndy Shevchenko static int vadc_prescaling_from_dt(u32 numerator, u32 denominator)
4110917de94SStanimir Varbanov {
4120917de94SStanimir Varbanov unsigned int pre;
4130917de94SStanimir Varbanov
4140917de94SStanimir Varbanov for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++)
415a5e9b2ddSAndy Shevchenko if (vadc_prescale_ratios[pre].numerator == numerator &&
416a5e9b2ddSAndy Shevchenko vadc_prescale_ratios[pre].denominator == denominator)
4170917de94SStanimir Varbanov break;
4180917de94SStanimir Varbanov
4190917de94SStanimir Varbanov if (pre == ARRAY_SIZE(vadc_prescale_ratios))
4200917de94SStanimir Varbanov return -EINVAL;
4210917de94SStanimir Varbanov
4220917de94SStanimir Varbanov return pre;
4230917de94SStanimir Varbanov }
4240917de94SStanimir Varbanov
vadc_hw_settle_time_from_dt(u32 value)4250917de94SStanimir Varbanov static int vadc_hw_settle_time_from_dt(u32 value)
4260917de94SStanimir Varbanov {
4270917de94SStanimir Varbanov if ((value <= 1000 && value % 100) || (value > 1000 && value % 2000))
4280917de94SStanimir Varbanov return -EINVAL;
4290917de94SStanimir Varbanov
4300917de94SStanimir Varbanov if (value <= 1000)
4310917de94SStanimir Varbanov value /= 100;
4320917de94SStanimir Varbanov else
4330917de94SStanimir Varbanov value = value / 2000 + 10;
4340917de94SStanimir Varbanov
4350917de94SStanimir Varbanov return value;
4360917de94SStanimir Varbanov }
4370917de94SStanimir Varbanov
vadc_avg_samples_from_dt(u32 value)4380917de94SStanimir Varbanov static int vadc_avg_samples_from_dt(u32 value)
4390917de94SStanimir Varbanov {
4400917de94SStanimir Varbanov if (!is_power_of_2(value) || value > VADC_AVG_SAMPLES_MAX)
4410917de94SStanimir Varbanov return -EINVAL;
4420917de94SStanimir Varbanov
4430917de94SStanimir Varbanov return __ffs64(value);
4440917de94SStanimir Varbanov }
4450917de94SStanimir Varbanov
vadc_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)4460917de94SStanimir Varbanov static int vadc_read_raw(struct iio_dev *indio_dev,
4470917de94SStanimir Varbanov struct iio_chan_spec const *chan, int *val, int *val2,
4480917de94SStanimir Varbanov long mask)
4490917de94SStanimir Varbanov {
4500917de94SStanimir Varbanov struct vadc_priv *vadc = iio_priv(indio_dev);
4510917de94SStanimir Varbanov struct vadc_channel_prop *prop;
4520917de94SStanimir Varbanov u16 adc_code;
4530917de94SStanimir Varbanov int ret;
4540917de94SStanimir Varbanov
4550917de94SStanimir Varbanov switch (mask) {
4560917de94SStanimir Varbanov case IIO_CHAN_INFO_PROCESSED:
4570917de94SStanimir Varbanov prop = &vadc->chan_props[chan->address];
4580917de94SStanimir Varbanov ret = vadc_do_conversion(vadc, prop, &adc_code);
4590917de94SStanimir Varbanov if (ret)
4600917de94SStanimir Varbanov break;
4610917de94SStanimir Varbanov
462e932d4f0SLinus Walleij ret = qcom_vadc_scale(prop->scale_fn_type,
463e932d4f0SLinus Walleij &vadc->graph[prop->calibration],
464e932d4f0SLinus Walleij &vadc_prescale_ratios[prop->prescale],
465e932d4f0SLinus Walleij (prop->calibration == VADC_CALIB_ABSOLUTE),
466e932d4f0SLinus Walleij adc_code, val);
467e932d4f0SLinus Walleij if (ret)
468e932d4f0SLinus Walleij break;
4690917de94SStanimir Varbanov
4700917de94SStanimir Varbanov return IIO_VAL_INT;
4710917de94SStanimir Varbanov case IIO_CHAN_INFO_RAW:
4720917de94SStanimir Varbanov prop = &vadc->chan_props[chan->address];
4730917de94SStanimir Varbanov ret = vadc_do_conversion(vadc, prop, &adc_code);
4740917de94SStanimir Varbanov if (ret)
4750917de94SStanimir Varbanov break;
4760917de94SStanimir Varbanov
477ba71704aSRama Krishna Phani A *val = (int)adc_code;
4780917de94SStanimir Varbanov return IIO_VAL_INT;
4790917de94SStanimir Varbanov default:
4800917de94SStanimir Varbanov ret = -EINVAL;
4810917de94SStanimir Varbanov break;
4820917de94SStanimir Varbanov }
4830917de94SStanimir Varbanov
4840917de94SStanimir Varbanov return ret;
4850917de94SStanimir Varbanov }
4860917de94SStanimir Varbanov
vadc_fwnode_xlate(struct iio_dev * indio_dev,const struct fwnode_reference_args * iiospec)487e7c672d0SNuno Sá static int vadc_fwnode_xlate(struct iio_dev *indio_dev,
488e7c672d0SNuno Sá const struct fwnode_reference_args *iiospec)
4890917de94SStanimir Varbanov {
4900917de94SStanimir Varbanov struct vadc_priv *vadc = iio_priv(indio_dev);
4910917de94SStanimir Varbanov unsigned int i;
4920917de94SStanimir Varbanov
4930917de94SStanimir Varbanov for (i = 0; i < vadc->nchannels; i++)
4940917de94SStanimir Varbanov if (vadc->iio_chans[i].channel == iiospec->args[0])
4950917de94SStanimir Varbanov return i;
4960917de94SStanimir Varbanov
4970917de94SStanimir Varbanov return -EINVAL;
4980917de94SStanimir Varbanov }
4990917de94SStanimir Varbanov
vadc_read_label(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,char * label)500*3d0b1260SMarijn Suijten static int vadc_read_label(struct iio_dev *indio_dev,
501*3d0b1260SMarijn Suijten struct iio_chan_spec const *chan, char *label)
502*3d0b1260SMarijn Suijten {
503*3d0b1260SMarijn Suijten struct vadc_priv *vadc = iio_priv(indio_dev);
504*3d0b1260SMarijn Suijten const char *name = vadc->chan_props[chan->address].channel_name;
505*3d0b1260SMarijn Suijten
506*3d0b1260SMarijn Suijten return sysfs_emit(label, "%s\n", name);
507*3d0b1260SMarijn Suijten }
508*3d0b1260SMarijn Suijten
5090917de94SStanimir Varbanov static const struct iio_info vadc_info = {
5100917de94SStanimir Varbanov .read_raw = vadc_read_raw,
511*3d0b1260SMarijn Suijten .read_label = vadc_read_label,
512e7c672d0SNuno Sá .fwnode_xlate = vadc_fwnode_xlate,
5130917de94SStanimir Varbanov };
5140917de94SStanimir Varbanov
5150917de94SStanimir Varbanov struct vadc_channels {
5160917de94SStanimir Varbanov const char *datasheet_name;
5170917de94SStanimir Varbanov unsigned int prescale_index;
5180917de94SStanimir Varbanov enum iio_chan_type type;
5190917de94SStanimir Varbanov long info_mask;
520e932d4f0SLinus Walleij enum vadc_scale_fn_type scale_fn_type;
5210917de94SStanimir Varbanov };
5220917de94SStanimir Varbanov
5237c271eeaSRama Krishna Phani A #define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
5247c271eeaSRama Krishna Phani A [VADC_##_dname] = { \
5257c271eeaSRama Krishna Phani A .datasheet_name = __stringify(_dname), \
5267c271eeaSRama Krishna Phani A .prescale_index = _pre, \
5277c271eeaSRama Krishna Phani A .type = _type, \
5287c271eeaSRama Krishna Phani A .info_mask = _mask, \
529e932d4f0SLinus Walleij .scale_fn_type = _scale \
5307c271eeaSRama Krishna Phani A }, \
5317c271eeaSRama Krishna Phani A
5327c271eeaSRama Krishna Phani A #define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
5330917de94SStanimir Varbanov [VADC_##_dname] = { \
5340917de94SStanimir Varbanov .datasheet_name = __stringify(_dname), \
5350917de94SStanimir Varbanov .prescale_index = _pre, \
5360917de94SStanimir Varbanov .type = _type, \
5370917de94SStanimir Varbanov .info_mask = _mask \
5387c271eeaSRama Krishna Phani A },
5390917de94SStanimir Varbanov
5407c271eeaSRama Krishna Phani A #define VADC_CHAN_TEMP(_dname, _pre, _scale) \
5417c271eeaSRama Krishna Phani A VADC_CHAN(_dname, IIO_TEMP, \
5427c271eeaSRama Krishna Phani A BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
5437c271eeaSRama Krishna Phani A _pre, _scale) \
5440917de94SStanimir Varbanov
5457c271eeaSRama Krishna Phani A #define VADC_CHAN_VOLT(_dname, _pre, _scale) \
5460917de94SStanimir Varbanov VADC_CHAN(_dname, IIO_VOLTAGE, \
547ba71704aSRama Krishna Phani A BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
5487c271eeaSRama Krishna Phani A _pre, _scale) \
5497c271eeaSRama Krishna Phani A
5507c271eeaSRama Krishna Phani A #define VADC_CHAN_NO_SCALE(_dname, _pre) \
5517c271eeaSRama Krishna Phani A VADC_NO_CHAN(_dname, IIO_VOLTAGE, \
5527c271eeaSRama Krishna Phani A BIT(IIO_CHAN_INFO_RAW), \
5530917de94SStanimir Varbanov _pre) \
5540917de94SStanimir Varbanov
5550917de94SStanimir Varbanov /*
5560917de94SStanimir Varbanov * The array represents all possible ADC channels found in the supported PMICs.
5570917de94SStanimir Varbanov * Every index in the array is equal to the channel number per datasheet. The
5580917de94SStanimir Varbanov * gaps in the array should be treated as reserved channels.
5590917de94SStanimir Varbanov */
5600917de94SStanimir Varbanov static const struct vadc_channels vadc_chans[] = {
5617c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(USBIN, 4, SCALE_DEFAULT)
5627c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(DCIN, 4, SCALE_DEFAULT)
5637c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(VCHG_SNS, 3)
5647c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(SPARE1_03, 1)
5657c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(USB_ID_MV, 1)
5667c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT)
5677c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(VBAT_SNS, 1)
5687c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT)
5697c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM)
5707c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT)
5717c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(REF_1250MV, 0, SCALE_DEFAULT)
5727c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(CHG_TEMP, 0)
5737c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(SPARE1, 0)
5747c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(SPARE2, 0, SCALE_PMI_CHG_TEMP)
5757c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(GND_REF, 0, SCALE_DEFAULT)
5767c271eeaSRama Krishna Phani A VADC_CHAN_VOLT(VDD_VADC, 0, SCALE_DEFAULT)
5770917de94SStanimir Varbanov
5787c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX1_1_1, 0)
5797c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX2_1_1, 0)
5807c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX3_1_1, 0)
5817c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX4_1_1, 0)
5827c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX5_1_1, 0)
5837c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX6_1_1, 0)
5847c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX7_1_1, 0)
5857c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX8_1_1, 0)
5867c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX9_1_1, 0)
5877c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX10_1_1, 0)
5887c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX11_1_1, 0)
5897c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX12_1_1, 0)
5907c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX13_1_1, 0)
5917c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX14_1_1, 0)
5927c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX15_1_1, 0)
5937c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX16_1_1, 0)
5940917de94SStanimir Varbanov
5957c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX1_1_3, 1)
5967c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX2_1_3, 1)
5977c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX3_1_3, 1)
5987c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX4_1_3, 1)
5997c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX5_1_3, 1)
6007c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX6_1_3, 1)
6017c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX7_1_3, 1)
6027c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX8_1_3, 1)
6037c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX9_1_3, 1)
6047c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX10_1_3, 1)
6057c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX11_1_3, 1)
6067c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX12_1_3, 1)
6077c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX13_1_3, 1)
6087c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX14_1_3, 1)
6097c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX15_1_3, 1)
6107c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(P_MUX16_1_3, 1)
6110917de94SStanimir Varbanov
6127c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX1_BAT_THERM, 0)
6137d200b28SJonathan Albrieux VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0, SCALE_DEFAULT)
6147c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX3_XO_THERM, 0)
6157c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX4_AMUX_THM1, 0)
6167c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX5_AMUX_THM2, 0)
6177c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX6_AMUX_THM3, 0)
6187c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX7_HW_ID, 0)
6197c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX8_AMUX_THM4, 0)
6207c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX9_AMUX_THM5, 0)
6217c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX10_USB_ID, 0)
6227c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(AMUX_PU1, 0)
6237c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(AMUX_PU2, 0)
6247c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX3_BUF_XO_THERM, 0)
6250917de94SStanimir Varbanov
6267c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX1_PU1_BAT_THERM, 0)
6277c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX2_PU1_BAT_ID, 0)
6287c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX3_PU1_XO_THERM, 0)
6297c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0, SCALE_THERM_100K_PULLUP)
6307c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0, SCALE_THERM_100K_PULLUP)
6317c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0, SCALE_THERM_100K_PULLUP)
6327c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX7_PU1_AMUX_HW_ID, 0)
6337c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0, SCALE_THERM_100K_PULLUP)
6347c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0, SCALE_THERM_100K_PULLUP)
6357c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX10_PU1_AMUX_USB_ID, 0)
6367c271eeaSRama Krishna Phani A VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0, SCALE_XOTHERM)
6370917de94SStanimir Varbanov
6387c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX1_PU2_BAT_THERM, 0)
6397c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX2_PU2_BAT_ID, 0)
6407c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX3_PU2_XO_THERM, 0)
6417c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX4_PU2_AMUX_THM1, 0)
6427c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX5_PU2_AMUX_THM2, 0)
6437c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX6_PU2_AMUX_THM3, 0)
6447c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX7_PU2_AMUX_HW_ID, 0)
6457c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX8_PU2_AMUX_THM4, 0)
6467c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX9_PU2_AMUX_THM5, 0)
6477c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX10_PU2_AMUX_USB_ID, 0)
6487c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU2_XO_THERM, 0)
6490917de94SStanimir Varbanov
6507c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX1_PU1_PU2_BAT_THERM, 0)
6517c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX2_PU1_PU2_BAT_ID, 0)
6527c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX3_PU1_PU2_XO_THERM, 0)
6537c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
6547c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
6557c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
6567c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
6577c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
6587c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
6597c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
6607c271eeaSRama Krishna Phani A VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
6610917de94SStanimir Varbanov };
6620917de94SStanimir Varbanov
vadc_get_fw_channel_data(struct device * dev,struct vadc_channel_prop * prop,struct fwnode_handle * fwnode)663e7c672d0SNuno Sá static int vadc_get_fw_channel_data(struct device *dev,
6640917de94SStanimir Varbanov struct vadc_channel_prop *prop,
665e7c672d0SNuno Sá struct fwnode_handle *fwnode)
6660917de94SStanimir Varbanov {
667*3d0b1260SMarijn Suijten const char *name = fwnode_get_name(fwnode), *label;
6680917de94SStanimir Varbanov u32 chan, value, varr[2];
6690917de94SStanimir Varbanov int ret;
6700917de94SStanimir Varbanov
671e7c672d0SNuno Sá ret = fwnode_property_read_u32(fwnode, "reg", &chan);
6720917de94SStanimir Varbanov if (ret) {
6730917de94SStanimir Varbanov dev_err(dev, "invalid channel number %s\n", name);
6740917de94SStanimir Varbanov return ret;
6750917de94SStanimir Varbanov }
6760917de94SStanimir Varbanov
6770917de94SStanimir Varbanov if (chan > VADC_CHAN_MAX || chan < VADC_CHAN_MIN) {
6780917de94SStanimir Varbanov dev_err(dev, "%s invalid channel number %d\n", name, chan);
6790917de94SStanimir Varbanov return -EINVAL;
6800917de94SStanimir Varbanov }
6810917de94SStanimir Varbanov
682*3d0b1260SMarijn Suijten ret = fwnode_property_read_string(fwnode, "label", &label);
683*3d0b1260SMarijn Suijten if (ret)
684*3d0b1260SMarijn Suijten label = vadc_chans[chan].datasheet_name;
685*3d0b1260SMarijn Suijten prop->channel_name = label;
686*3d0b1260SMarijn Suijten
6870917de94SStanimir Varbanov /* the channel has DT description */
6880917de94SStanimir Varbanov prop->channel = chan;
6890917de94SStanimir Varbanov
690e7c672d0SNuno Sá ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value);
6910917de94SStanimir Varbanov if (!ret) {
692e932d4f0SLinus Walleij ret = qcom_vadc_decimation_from_dt(value);
6930917de94SStanimir Varbanov if (ret < 0) {
6940917de94SStanimir Varbanov dev_err(dev, "%02x invalid decimation %d\n",
6950917de94SStanimir Varbanov chan, value);
6960917de94SStanimir Varbanov return ret;
6970917de94SStanimir Varbanov }
6980917de94SStanimir Varbanov prop->decimation = ret;
6990917de94SStanimir Varbanov } else {
7000917de94SStanimir Varbanov prop->decimation = VADC_DEF_DECIMATION;
7010917de94SStanimir Varbanov }
7020917de94SStanimir Varbanov
703e7c672d0SNuno Sá ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2);
7040917de94SStanimir Varbanov if (!ret) {
7050917de94SStanimir Varbanov ret = vadc_prescaling_from_dt(varr[0], varr[1]);
7060917de94SStanimir Varbanov if (ret < 0) {
7070917de94SStanimir Varbanov dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
7080917de94SStanimir Varbanov chan, varr[0], varr[1]);
7090917de94SStanimir Varbanov return ret;
7100917de94SStanimir Varbanov }
7110917de94SStanimir Varbanov prop->prescale = ret;
7120917de94SStanimir Varbanov } else {
7130917de94SStanimir Varbanov prop->prescale = vadc_chans[prop->channel].prescale_index;
7140917de94SStanimir Varbanov }
7150917de94SStanimir Varbanov
716e7c672d0SNuno Sá ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value);
7170917de94SStanimir Varbanov if (!ret) {
7180917de94SStanimir Varbanov ret = vadc_hw_settle_time_from_dt(value);
7190917de94SStanimir Varbanov if (ret < 0) {
7200917de94SStanimir Varbanov dev_err(dev, "%02x invalid hw-settle-time %d us\n",
7210917de94SStanimir Varbanov chan, value);
7220917de94SStanimir Varbanov return ret;
7230917de94SStanimir Varbanov }
7240917de94SStanimir Varbanov prop->hw_settle_time = ret;
7250917de94SStanimir Varbanov } else {
7260917de94SStanimir Varbanov prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
7270917de94SStanimir Varbanov }
7280917de94SStanimir Varbanov
729e7c672d0SNuno Sá ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value);
7300917de94SStanimir Varbanov if (!ret) {
7310917de94SStanimir Varbanov ret = vadc_avg_samples_from_dt(value);
7320917de94SStanimir Varbanov if (ret < 0) {
7330917de94SStanimir Varbanov dev_err(dev, "%02x invalid avg-samples %d\n",
7340917de94SStanimir Varbanov chan, value);
7350917de94SStanimir Varbanov return ret;
7360917de94SStanimir Varbanov }
7370917de94SStanimir Varbanov prop->avg_samples = ret;
7380917de94SStanimir Varbanov } else {
7390917de94SStanimir Varbanov prop->avg_samples = VADC_DEF_AVG_SAMPLES;
7400917de94SStanimir Varbanov }
7410917de94SStanimir Varbanov
742e7c672d0SNuno Sá if (fwnode_property_read_bool(fwnode, "qcom,ratiometric"))
7430917de94SStanimir Varbanov prop->calibration = VADC_CALIB_RATIOMETRIC;
7440917de94SStanimir Varbanov else
7450917de94SStanimir Varbanov prop->calibration = VADC_CALIB_ABSOLUTE;
7460917de94SStanimir Varbanov
7470917de94SStanimir Varbanov dev_dbg(dev, "%02x name %s\n", chan, name);
7480917de94SStanimir Varbanov
7490917de94SStanimir Varbanov return 0;
7500917de94SStanimir Varbanov }
7510917de94SStanimir Varbanov
vadc_get_fw_data(struct vadc_priv * vadc)752e7c672d0SNuno Sá static int vadc_get_fw_data(struct vadc_priv *vadc)
7530917de94SStanimir Varbanov {
7540917de94SStanimir Varbanov const struct vadc_channels *vadc_chan;
7550917de94SStanimir Varbanov struct iio_chan_spec *iio_chan;
7560917de94SStanimir Varbanov struct vadc_channel_prop prop;
757e7c672d0SNuno Sá struct fwnode_handle *child;
7580917de94SStanimir Varbanov unsigned int index = 0;
7590917de94SStanimir Varbanov int ret;
7600917de94SStanimir Varbanov
761e7c672d0SNuno Sá vadc->nchannels = device_get_child_node_count(vadc->dev);
7620917de94SStanimir Varbanov if (!vadc->nchannels)
7630917de94SStanimir Varbanov return -EINVAL;
7640917de94SStanimir Varbanov
7650917de94SStanimir Varbanov vadc->iio_chans = devm_kcalloc(vadc->dev, vadc->nchannels,
7660917de94SStanimir Varbanov sizeof(*vadc->iio_chans), GFP_KERNEL);
7670917de94SStanimir Varbanov if (!vadc->iio_chans)
7680917de94SStanimir Varbanov return -ENOMEM;
7690917de94SStanimir Varbanov
7700917de94SStanimir Varbanov vadc->chan_props = devm_kcalloc(vadc->dev, vadc->nchannels,
7710917de94SStanimir Varbanov sizeof(*vadc->chan_props), GFP_KERNEL);
7720917de94SStanimir Varbanov if (!vadc->chan_props)
7730917de94SStanimir Varbanov return -ENOMEM;
7740917de94SStanimir Varbanov
7750917de94SStanimir Varbanov iio_chan = vadc->iio_chans;
7760917de94SStanimir Varbanov
777e7c672d0SNuno Sá device_for_each_child_node(vadc->dev, child) {
778e7c672d0SNuno Sá ret = vadc_get_fw_channel_data(vadc->dev, &prop, child);
779d4c65fe4SJulia Lawall if (ret) {
780e7c672d0SNuno Sá fwnode_handle_put(child);
7810917de94SStanimir Varbanov return ret;
782d4c65fe4SJulia Lawall }
7830917de94SStanimir Varbanov
784e932d4f0SLinus Walleij prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
7850917de94SStanimir Varbanov vadc->chan_props[index] = prop;
7860917de94SStanimir Varbanov
7870917de94SStanimir Varbanov vadc_chan = &vadc_chans[prop.channel];
7880917de94SStanimir Varbanov
7890917de94SStanimir Varbanov iio_chan->channel = prop.channel;
7900917de94SStanimir Varbanov iio_chan->datasheet_name = vadc_chan->datasheet_name;
7910917de94SStanimir Varbanov iio_chan->info_mask_separate = vadc_chan->info_mask;
7920917de94SStanimir Varbanov iio_chan->type = vadc_chan->type;
7930917de94SStanimir Varbanov iio_chan->indexed = 1;
7940917de94SStanimir Varbanov iio_chan->address = index++;
7950917de94SStanimir Varbanov
7960917de94SStanimir Varbanov iio_chan++;
7970917de94SStanimir Varbanov }
7980917de94SStanimir Varbanov
7990917de94SStanimir Varbanov /* These channels are mandatory, they are used as reference points */
8000917de94SStanimir Varbanov if (!vadc_get_channel(vadc, VADC_REF_1250MV)) {
8010917de94SStanimir Varbanov dev_err(vadc->dev, "Please define 1.25V channel\n");
8020917de94SStanimir Varbanov return -ENODEV;
8030917de94SStanimir Varbanov }
8040917de94SStanimir Varbanov
8050917de94SStanimir Varbanov if (!vadc_get_channel(vadc, VADC_REF_625MV)) {
8060917de94SStanimir Varbanov dev_err(vadc->dev, "Please define 0.625V channel\n");
8070917de94SStanimir Varbanov return -ENODEV;
8080917de94SStanimir Varbanov }
8090917de94SStanimir Varbanov
8100917de94SStanimir Varbanov if (!vadc_get_channel(vadc, VADC_VDD_VADC)) {
8110917de94SStanimir Varbanov dev_err(vadc->dev, "Please define VDD channel\n");
8120917de94SStanimir Varbanov return -ENODEV;
8130917de94SStanimir Varbanov }
8140917de94SStanimir Varbanov
8150917de94SStanimir Varbanov if (!vadc_get_channel(vadc, VADC_GND_REF)) {
8160917de94SStanimir Varbanov dev_err(vadc->dev, "Please define GND channel\n");
8170917de94SStanimir Varbanov return -ENODEV;
8180917de94SStanimir Varbanov }
8190917de94SStanimir Varbanov
8200917de94SStanimir Varbanov return 0;
8210917de94SStanimir Varbanov }
8220917de94SStanimir Varbanov
vadc_isr(int irq,void * dev_id)8230917de94SStanimir Varbanov static irqreturn_t vadc_isr(int irq, void *dev_id)
8240917de94SStanimir Varbanov {
8250917de94SStanimir Varbanov struct vadc_priv *vadc = dev_id;
8260917de94SStanimir Varbanov
8270917de94SStanimir Varbanov complete(&vadc->complete);
8280917de94SStanimir Varbanov
8290917de94SStanimir Varbanov return IRQ_HANDLED;
8300917de94SStanimir Varbanov }
8310917de94SStanimir Varbanov
vadc_check_revision(struct vadc_priv * vadc)8320917de94SStanimir Varbanov static int vadc_check_revision(struct vadc_priv *vadc)
8330917de94SStanimir Varbanov {
8340917de94SStanimir Varbanov u8 val;
8350917de94SStanimir Varbanov int ret;
8360917de94SStanimir Varbanov
8370917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_PERPH_TYPE, &val);
8380917de94SStanimir Varbanov if (ret)
8390917de94SStanimir Varbanov return ret;
8400917de94SStanimir Varbanov
8410917de94SStanimir Varbanov if (val < VADC_PERPH_TYPE_ADC) {
8420917de94SStanimir Varbanov dev_err(vadc->dev, "%d is not ADC\n", val);
8430917de94SStanimir Varbanov return -ENODEV;
8440917de94SStanimir Varbanov }
8450917de94SStanimir Varbanov
8460917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_PERPH_SUBTYPE, &val);
8470917de94SStanimir Varbanov if (ret)
8480917de94SStanimir Varbanov return ret;
8490917de94SStanimir Varbanov
8500917de94SStanimir Varbanov if (val < VADC_PERPH_SUBTYPE_VADC) {
8510917de94SStanimir Varbanov dev_err(vadc->dev, "%d is not VADC\n", val);
8520917de94SStanimir Varbanov return -ENODEV;
8530917de94SStanimir Varbanov }
8540917de94SStanimir Varbanov
8550917de94SStanimir Varbanov ret = vadc_read(vadc, VADC_REVISION2, &val);
8560917de94SStanimir Varbanov if (ret)
8570917de94SStanimir Varbanov return ret;
8580917de94SStanimir Varbanov
8590917de94SStanimir Varbanov if (val < VADC_REVISION2_SUPPORTED_VADC) {
8600917de94SStanimir Varbanov dev_err(vadc->dev, "revision %d not supported\n", val);
8610917de94SStanimir Varbanov return -ENODEV;
8620917de94SStanimir Varbanov }
8630917de94SStanimir Varbanov
8640917de94SStanimir Varbanov return 0;
8650917de94SStanimir Varbanov }
8660917de94SStanimir Varbanov
vadc_probe(struct platform_device * pdev)8670917de94SStanimir Varbanov static int vadc_probe(struct platform_device *pdev)
8680917de94SStanimir Varbanov {
8690917de94SStanimir Varbanov struct device *dev = &pdev->dev;
8700917de94SStanimir Varbanov struct iio_dev *indio_dev;
8710917de94SStanimir Varbanov struct vadc_priv *vadc;
8720917de94SStanimir Varbanov struct regmap *regmap;
8730917de94SStanimir Varbanov int ret, irq_eoc;
8740917de94SStanimir Varbanov u32 reg;
8750917de94SStanimir Varbanov
8760917de94SStanimir Varbanov regmap = dev_get_regmap(dev->parent, NULL);
8770917de94SStanimir Varbanov if (!regmap)
8780917de94SStanimir Varbanov return -ENODEV;
8790917de94SStanimir Varbanov
880e7c672d0SNuno Sá ret = device_property_read_u32(dev, "reg", ®);
8810917de94SStanimir Varbanov if (ret < 0)
8820917de94SStanimir Varbanov return ret;
8830917de94SStanimir Varbanov
8840917de94SStanimir Varbanov indio_dev = devm_iio_device_alloc(dev, sizeof(*vadc));
8850917de94SStanimir Varbanov if (!indio_dev)
8860917de94SStanimir Varbanov return -ENOMEM;
8870917de94SStanimir Varbanov
8880917de94SStanimir Varbanov vadc = iio_priv(indio_dev);
8890917de94SStanimir Varbanov vadc->regmap = regmap;
8900917de94SStanimir Varbanov vadc->dev = dev;
8910917de94SStanimir Varbanov vadc->base = reg;
8920917de94SStanimir Varbanov vadc->are_ref_measured = false;
8930917de94SStanimir Varbanov init_completion(&vadc->complete);
8940917de94SStanimir Varbanov mutex_init(&vadc->lock);
8950917de94SStanimir Varbanov
8960917de94SStanimir Varbanov ret = vadc_check_revision(vadc);
8970917de94SStanimir Varbanov if (ret)
8980917de94SStanimir Varbanov return ret;
8990917de94SStanimir Varbanov
900e7c672d0SNuno Sá ret = vadc_get_fw_data(vadc);
9010917de94SStanimir Varbanov if (ret)
9020917de94SStanimir Varbanov return ret;
9030917de94SStanimir Varbanov
9040917de94SStanimir Varbanov irq_eoc = platform_get_irq(pdev, 0);
9050917de94SStanimir Varbanov if (irq_eoc < 0) {
9060917de94SStanimir Varbanov if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
9070917de94SStanimir Varbanov return irq_eoc;
9080917de94SStanimir Varbanov vadc->poll_eoc = true;
9090917de94SStanimir Varbanov } else {
9100917de94SStanimir Varbanov ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
9110917de94SStanimir Varbanov "spmi-vadc", vadc);
9120917de94SStanimir Varbanov if (ret)
9130917de94SStanimir Varbanov return ret;
9140917de94SStanimir Varbanov }
9150917de94SStanimir Varbanov
9160917de94SStanimir Varbanov ret = vadc_reset(vadc);
9170917de94SStanimir Varbanov if (ret) {
9180917de94SStanimir Varbanov dev_err(dev, "reset failed\n");
9190917de94SStanimir Varbanov return ret;
9200917de94SStanimir Varbanov }
9210917de94SStanimir Varbanov
9220917de94SStanimir Varbanov ret = vadc_measure_ref_points(vadc);
9230917de94SStanimir Varbanov if (ret)
9240917de94SStanimir Varbanov return ret;
9250917de94SStanimir Varbanov
9260917de94SStanimir Varbanov indio_dev->name = pdev->name;
9270917de94SStanimir Varbanov indio_dev->modes = INDIO_DIRECT_MODE;
9280917de94SStanimir Varbanov indio_dev->info = &vadc_info;
9290917de94SStanimir Varbanov indio_dev->channels = vadc->iio_chans;
9300917de94SStanimir Varbanov indio_dev->num_channels = vadc->nchannels;
9310917de94SStanimir Varbanov
9320917de94SStanimir Varbanov return devm_iio_device_register(dev, indio_dev);
9330917de94SStanimir Varbanov }
9340917de94SStanimir Varbanov
9350917de94SStanimir Varbanov static const struct of_device_id vadc_match_table[] = {
9360917de94SStanimir Varbanov { .compatible = "qcom,spmi-vadc" },
9370917de94SStanimir Varbanov { }
9380917de94SStanimir Varbanov };
9390917de94SStanimir Varbanov MODULE_DEVICE_TABLE(of, vadc_match_table);
9400917de94SStanimir Varbanov
9410917de94SStanimir Varbanov static struct platform_driver vadc_driver = {
9420917de94SStanimir Varbanov .driver = {
9430917de94SStanimir Varbanov .name = "qcom-spmi-vadc",
9440917de94SStanimir Varbanov .of_match_table = vadc_match_table,
9450917de94SStanimir Varbanov },
9460917de94SStanimir Varbanov .probe = vadc_probe,
9470917de94SStanimir Varbanov };
9480917de94SStanimir Varbanov module_platform_driver(vadc_driver);
9490917de94SStanimir Varbanov
9500917de94SStanimir Varbanov MODULE_ALIAS("platform:qcom-spmi-vadc");
9510917de94SStanimir Varbanov MODULE_DESCRIPTION("Qualcomm SPMI PMIC voltage ADC driver");
9520917de94SStanimir Varbanov MODULE_LICENSE("GPL v2");
9530917de94SStanimir Varbanov MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
9540917de94SStanimir Varbanov MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
955