xref: /openbmc/linux/drivers/iio/adc/mxs-lradc-adc.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26dd112b9SKsenija Stanojevic /*
36dd112b9SKsenija Stanojevic  * Freescale MXS LRADC ADC driver
46dd112b9SKsenija Stanojevic  *
56dd112b9SKsenija Stanojevic  * Copyright (c) 2012 DENX Software Engineering, GmbH.
66dd112b9SKsenija Stanojevic  * Copyright (c) 2017 Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
76dd112b9SKsenija Stanojevic  *
86dd112b9SKsenija Stanojevic  * Authors:
96dd112b9SKsenija Stanojevic  *  Marek Vasut <marex@denx.de>
106dd112b9SKsenija Stanojevic  *  Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
116dd112b9SKsenija Stanojevic  */
126dd112b9SKsenija Stanojevic 
136dd112b9SKsenija Stanojevic #include <linux/completion.h>
146dd112b9SKsenija Stanojevic #include <linux/device.h>
156dd112b9SKsenija Stanojevic #include <linux/err.h>
166dd112b9SKsenija Stanojevic #include <linux/interrupt.h>
176dd112b9SKsenija Stanojevic #include <linux/mfd/core.h>
186dd112b9SKsenija Stanojevic #include <linux/mfd/mxs-lradc.h>
196dd112b9SKsenija Stanojevic #include <linux/module.h>
206dd112b9SKsenija Stanojevic #include <linux/of_irq.h>
216dd112b9SKsenija Stanojevic #include <linux/platform_device.h>
226dd112b9SKsenija Stanojevic #include <linux/sysfs.h>
236dd112b9SKsenija Stanojevic 
246dd112b9SKsenija Stanojevic #include <linux/iio/buffer.h>
256dd112b9SKsenija Stanojevic #include <linux/iio/iio.h>
266dd112b9SKsenija Stanojevic #include <linux/iio/trigger.h>
276dd112b9SKsenija Stanojevic #include <linux/iio/trigger_consumer.h>
286dd112b9SKsenija Stanojevic #include <linux/iio/triggered_buffer.h>
296dd112b9SKsenija Stanojevic #include <linux/iio/sysfs.h>
306dd112b9SKsenija Stanojevic 
316dd112b9SKsenija Stanojevic /*
326dd112b9SKsenija Stanojevic  * Make this runtime configurable if necessary. Currently, if the buffered mode
336dd112b9SKsenija Stanojevic  * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
346dd112b9SKsenija Stanojevic  * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
356dd112b9SKsenija Stanojevic  * seconds. The result is that the samples arrive every 500mS.
366dd112b9SKsenija Stanojevic  */
376dd112b9SKsenija Stanojevic #define LRADC_DELAY_TIMER_PER	200
386dd112b9SKsenija Stanojevic #define LRADC_DELAY_TIMER_LOOP	5
396dd112b9SKsenija Stanojevic 
406dd112b9SKsenija Stanojevic #define VREF_MV_BASE 1850
416dd112b9SKsenija Stanojevic 
42f4f93bf7SPaolo Cretaro static const char *mx23_lradc_adc_irq_names[] = {
436dd112b9SKsenija Stanojevic 	"mxs-lradc-channel0",
446dd112b9SKsenija Stanojevic 	"mxs-lradc-channel1",
456dd112b9SKsenija Stanojevic 	"mxs-lradc-channel2",
466dd112b9SKsenija Stanojevic 	"mxs-lradc-channel3",
476dd112b9SKsenija Stanojevic 	"mxs-lradc-channel4",
486dd112b9SKsenija Stanojevic 	"mxs-lradc-channel5",
496dd112b9SKsenija Stanojevic };
506dd112b9SKsenija Stanojevic 
51f4f93bf7SPaolo Cretaro static const char *mx28_lradc_adc_irq_names[] = {
526dd112b9SKsenija Stanojevic 	"mxs-lradc-thresh0",
536dd112b9SKsenija Stanojevic 	"mxs-lradc-thresh1",
546dd112b9SKsenija Stanojevic 	"mxs-lradc-channel0",
556dd112b9SKsenija Stanojevic 	"mxs-lradc-channel1",
566dd112b9SKsenija Stanojevic 	"mxs-lradc-channel2",
576dd112b9SKsenija Stanojevic 	"mxs-lradc-channel3",
586dd112b9SKsenija Stanojevic 	"mxs-lradc-channel4",
596dd112b9SKsenija Stanojevic 	"mxs-lradc-channel5",
606dd112b9SKsenija Stanojevic 	"mxs-lradc-button0",
616dd112b9SKsenija Stanojevic 	"mxs-lradc-button1",
626dd112b9SKsenija Stanojevic };
636dd112b9SKsenija Stanojevic 
646dd112b9SKsenija Stanojevic static const u32 mxs_lradc_adc_vref_mv[][LRADC_MAX_TOTAL_CHANS] = {
656dd112b9SKsenija Stanojevic 	[IMX23_LRADC] = {
666dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH0 */
676dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH1 */
686dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH2 */
696dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH3 */
706dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH4 */
716dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH5 */
726dd112b9SKsenija Stanojevic 		VREF_MV_BASE * 2,	/* CH6 VDDIO */
736dd112b9SKsenija Stanojevic 		VREF_MV_BASE * 4,	/* CH7 VBATT */
746dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH8 Temp sense 0 */
756dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH9 Temp sense 1 */
766dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH10 */
776dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH11 */
786dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH12 USB_DP */
796dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH13 USB_DN */
806dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH14 VBG */
816dd112b9SKsenija Stanojevic 		VREF_MV_BASE * 4,	/* CH15 VDD5V */
826dd112b9SKsenija Stanojevic 	},
836dd112b9SKsenija Stanojevic 	[IMX28_LRADC] = {
846dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH0 */
856dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH1 */
866dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH2 */
876dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH3 */
886dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH4 */
896dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH5 */
906dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH6 */
916dd112b9SKsenija Stanojevic 		VREF_MV_BASE * 4,	/* CH7 VBATT */
926dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH8 Temp sense 0 */
936dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH9 Temp sense 1 */
946dd112b9SKsenija Stanojevic 		VREF_MV_BASE * 2,	/* CH10 VDDIO */
956dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH11 VTH */
966dd112b9SKsenija Stanojevic 		VREF_MV_BASE * 2,	/* CH12 VDDA */
976dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH13 VDDD */
986dd112b9SKsenija Stanojevic 		VREF_MV_BASE,		/* CH14 VBG */
996dd112b9SKsenija Stanojevic 		VREF_MV_BASE * 4,	/* CH15 VDD5V */
1006dd112b9SKsenija Stanojevic 	},
1016dd112b9SKsenija Stanojevic };
1026dd112b9SKsenija Stanojevic 
1036dd112b9SKsenija Stanojevic enum mxs_lradc_divbytwo {
1046dd112b9SKsenija Stanojevic 	MXS_LRADC_DIV_DISABLED = 0,
1056dd112b9SKsenija Stanojevic 	MXS_LRADC_DIV_ENABLED,
1066dd112b9SKsenija Stanojevic };
1076dd112b9SKsenija Stanojevic 
1086dd112b9SKsenija Stanojevic struct mxs_lradc_scale {
1096dd112b9SKsenija Stanojevic 	unsigned int		integer;
1106dd112b9SKsenija Stanojevic 	unsigned int		nano;
1116dd112b9SKsenija Stanojevic };
1126dd112b9SKsenija Stanojevic 
1136dd112b9SKsenija Stanojevic struct mxs_lradc_adc {
1146dd112b9SKsenija Stanojevic 	struct mxs_lradc	*lradc;
1156dd112b9SKsenija Stanojevic 	struct device		*dev;
1166dd112b9SKsenija Stanojevic 
1176dd112b9SKsenija Stanojevic 	void __iomem		*base;
1186a6be221SJonathan Cameron 	/* Maximum of 8 channels + 8 byte ts */
1196a6be221SJonathan Cameron 	u32			buffer[10] __aligned(8);
1206dd112b9SKsenija Stanojevic 	struct iio_trigger	*trig;
1216dd112b9SKsenija Stanojevic 	struct completion	completion;
1226dd112b9SKsenija Stanojevic 	spinlock_t		lock;
1236dd112b9SKsenija Stanojevic 
1246dd112b9SKsenija Stanojevic 	const u32		*vref_mv;
1256dd112b9SKsenija Stanojevic 	struct mxs_lradc_scale	scale_avail[LRADC_MAX_TOTAL_CHANS][2];
1266dd112b9SKsenija Stanojevic 	unsigned long		is_divided;
1276dd112b9SKsenija Stanojevic };
1286dd112b9SKsenija Stanojevic 
1296dd112b9SKsenija Stanojevic 
1306dd112b9SKsenija Stanojevic /* Raw I/O operations */
mxs_lradc_adc_read_single(struct iio_dev * iio_dev,int chan,int * val)1316dd112b9SKsenija Stanojevic static int mxs_lradc_adc_read_single(struct iio_dev *iio_dev, int chan,
1326dd112b9SKsenija Stanojevic 				     int *val)
1336dd112b9SKsenija Stanojevic {
1346dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
1356dd112b9SKsenija Stanojevic 	struct mxs_lradc *lradc = adc->lradc;
1366dd112b9SKsenija Stanojevic 	int ret;
1376dd112b9SKsenija Stanojevic 
1386dd112b9SKsenija Stanojevic 	/*
1396dd112b9SKsenija Stanojevic 	 * See if there is no buffered operation in progress. If there is simply
1406dd112b9SKsenija Stanojevic 	 * bail out. This can be improved to support both buffered and raw IO at
1416dd112b9SKsenija Stanojevic 	 * the same time, yet the code becomes horribly complicated. Therefore I
1426dd112b9SKsenija Stanojevic 	 * applied KISS principle here.
1436dd112b9SKsenija Stanojevic 	 */
1446dd112b9SKsenija Stanojevic 	ret = iio_device_claim_direct_mode(iio_dev);
1456dd112b9SKsenija Stanojevic 	if (ret)
1466dd112b9SKsenija Stanojevic 		return ret;
1476dd112b9SKsenija Stanojevic 
1486dd112b9SKsenija Stanojevic 	reinit_completion(&adc->completion);
1496dd112b9SKsenija Stanojevic 
1506dd112b9SKsenija Stanojevic 	/*
1516dd112b9SKsenija Stanojevic 	 * No buffered operation in progress, map the channel and trigger it.
1526dd112b9SKsenija Stanojevic 	 * Virtual channel 0 is always used here as the others are always not
1536dd112b9SKsenija Stanojevic 	 * used if doing raw sampling.
1546dd112b9SKsenija Stanojevic 	 */
1556dd112b9SKsenija Stanojevic 	if (lradc->soc == IMX28_LRADC)
1566dd112b9SKsenija Stanojevic 		writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
1576dd112b9SKsenija Stanojevic 		       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
1586dd112b9SKsenija Stanojevic 	writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
1596dd112b9SKsenija Stanojevic 
1606dd112b9SKsenija Stanojevic 	/* Enable / disable the divider per requirement */
1616dd112b9SKsenija Stanojevic 	if (test_bit(chan, &adc->is_divided))
1626dd112b9SKsenija Stanojevic 		writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
1636dd112b9SKsenija Stanojevic 		       adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET);
1646dd112b9SKsenija Stanojevic 	else
1656dd112b9SKsenija Stanojevic 		writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
1666dd112b9SKsenija Stanojevic 		       adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);
1676dd112b9SKsenija Stanojevic 
1686dd112b9SKsenija Stanojevic 	/* Clean the slot's previous content, then set new one. */
1696dd112b9SKsenija Stanojevic 	writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
1706dd112b9SKsenija Stanojevic 	       adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
1716dd112b9SKsenija Stanojevic 	writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
1726dd112b9SKsenija Stanojevic 
1736dd112b9SKsenija Stanojevic 	writel(0, adc->base + LRADC_CH(0));
1746dd112b9SKsenija Stanojevic 
1756dd112b9SKsenija Stanojevic 	/* Enable the IRQ and start sampling the channel. */
1766dd112b9SKsenija Stanojevic 	writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
1776dd112b9SKsenija Stanojevic 	       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
1786dd112b9SKsenija Stanojevic 	writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
1796dd112b9SKsenija Stanojevic 
1806dd112b9SKsenija Stanojevic 	/* Wait for completion on the channel, 1 second max. */
1816dd112b9SKsenija Stanojevic 	ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
1826dd112b9SKsenija Stanojevic 	if (!ret)
1836dd112b9SKsenija Stanojevic 		ret = -ETIMEDOUT;
1846dd112b9SKsenija Stanojevic 	if (ret < 0)
1856dd112b9SKsenija Stanojevic 		goto err;
1866dd112b9SKsenija Stanojevic 
1876dd112b9SKsenija Stanojevic 	/* Read the data. */
1886dd112b9SKsenija Stanojevic 	*val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
1896dd112b9SKsenija Stanojevic 	ret = IIO_VAL_INT;
1906dd112b9SKsenija Stanojevic 
1916dd112b9SKsenija Stanojevic err:
1926dd112b9SKsenija Stanojevic 	writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
1936dd112b9SKsenija Stanojevic 	       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
1946dd112b9SKsenija Stanojevic 
1956dd112b9SKsenija Stanojevic 	iio_device_release_direct_mode(iio_dev);
1966dd112b9SKsenija Stanojevic 
1976dd112b9SKsenija Stanojevic 	return ret;
1986dd112b9SKsenija Stanojevic }
1996dd112b9SKsenija Stanojevic 
mxs_lradc_adc_read_temp(struct iio_dev * iio_dev,int * val)2006dd112b9SKsenija Stanojevic static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
2016dd112b9SKsenija Stanojevic {
2026dd112b9SKsenija Stanojevic 	int ret, min, max;
2036dd112b9SKsenija Stanojevic 
2046dd112b9SKsenija Stanojevic 	ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
2056dd112b9SKsenija Stanojevic 	if (ret != IIO_VAL_INT)
2066dd112b9SKsenija Stanojevic 		return ret;
2076dd112b9SKsenija Stanojevic 
2086dd112b9SKsenija Stanojevic 	ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
2096dd112b9SKsenija Stanojevic 	if (ret != IIO_VAL_INT)
2106dd112b9SKsenija Stanojevic 		return ret;
2116dd112b9SKsenija Stanojevic 
2126dd112b9SKsenija Stanojevic 	*val = max - min;
2136dd112b9SKsenija Stanojevic 
2146dd112b9SKsenija Stanojevic 	return IIO_VAL_INT;
2156dd112b9SKsenija Stanojevic }
2166dd112b9SKsenija Stanojevic 
mxs_lradc_adc_read_raw(struct iio_dev * iio_dev,const struct iio_chan_spec * chan,int * val,int * val2,long m)2176dd112b9SKsenija Stanojevic static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
2186dd112b9SKsenija Stanojevic 			      const struct iio_chan_spec *chan,
2196dd112b9SKsenija Stanojevic 			      int *val, int *val2, long m)
2206dd112b9SKsenija Stanojevic {
2216dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
2226dd112b9SKsenija Stanojevic 
2236dd112b9SKsenija Stanojevic 	switch (m) {
2246dd112b9SKsenija Stanojevic 	case IIO_CHAN_INFO_RAW:
2256dd112b9SKsenija Stanojevic 		if (chan->type == IIO_TEMP)
2266dd112b9SKsenija Stanojevic 			return mxs_lradc_adc_read_temp(iio_dev, val);
2276dd112b9SKsenija Stanojevic 
2286dd112b9SKsenija Stanojevic 		return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
2296dd112b9SKsenija Stanojevic 
2306dd112b9SKsenija Stanojevic 	case IIO_CHAN_INFO_SCALE:
2316dd112b9SKsenija Stanojevic 		if (chan->type == IIO_TEMP) {
2326dd112b9SKsenija Stanojevic 			/*
2336dd112b9SKsenija Stanojevic 			 * From the datasheet, we have to multiply by 1.012 and
2346dd112b9SKsenija Stanojevic 			 * divide by 4
2356dd112b9SKsenija Stanojevic 			 */
2366dd112b9SKsenija Stanojevic 			*val = 0;
2376dd112b9SKsenija Stanojevic 			*val2 = 253000;
2386dd112b9SKsenija Stanojevic 			return IIO_VAL_INT_PLUS_MICRO;
2396dd112b9SKsenija Stanojevic 		}
2406dd112b9SKsenija Stanojevic 
2416dd112b9SKsenija Stanojevic 		*val = adc->vref_mv[chan->channel];
2426dd112b9SKsenija Stanojevic 		*val2 = chan->scan_type.realbits -
2436dd112b9SKsenija Stanojevic 			test_bit(chan->channel, &adc->is_divided);
2446dd112b9SKsenija Stanojevic 		return IIO_VAL_FRACTIONAL_LOG2;
2456dd112b9SKsenija Stanojevic 
2466dd112b9SKsenija Stanojevic 	case IIO_CHAN_INFO_OFFSET:
2476dd112b9SKsenija Stanojevic 		if (chan->type == IIO_TEMP) {
2486dd112b9SKsenija Stanojevic 			/*
2496dd112b9SKsenija Stanojevic 			 * The calculated value from the ADC is in Kelvin, we
2506dd112b9SKsenija Stanojevic 			 * want Celsius for hwmon so the offset is -273.15
2516dd112b9SKsenija Stanojevic 			 * The offset is applied before scaling so it is
2526dd112b9SKsenija Stanojevic 			 * actually -213.15 * 4 / 1.012 = -1079.644268
2536dd112b9SKsenija Stanojevic 			 */
2546dd112b9SKsenija Stanojevic 			*val = -1079;
2556dd112b9SKsenija Stanojevic 			*val2 = 644268;
2566dd112b9SKsenija Stanojevic 
2576dd112b9SKsenija Stanojevic 			return IIO_VAL_INT_PLUS_MICRO;
2586dd112b9SKsenija Stanojevic 		}
2596dd112b9SKsenija Stanojevic 
2606dd112b9SKsenija Stanojevic 		return -EINVAL;
2616dd112b9SKsenija Stanojevic 
2626dd112b9SKsenija Stanojevic 	default:
2636dd112b9SKsenija Stanojevic 		break;
2646dd112b9SKsenija Stanojevic 	}
2656dd112b9SKsenija Stanojevic 
2666dd112b9SKsenija Stanojevic 	return -EINVAL;
2676dd112b9SKsenija Stanojevic }
2686dd112b9SKsenija Stanojevic 
mxs_lradc_adc_write_raw(struct iio_dev * iio_dev,const struct iio_chan_spec * chan,int val,int val2,long m)2696dd112b9SKsenija Stanojevic static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
2706dd112b9SKsenija Stanojevic 				   const struct iio_chan_spec *chan,
2716dd112b9SKsenija Stanojevic 				   int val, int val2, long m)
2726dd112b9SKsenija Stanojevic {
2736dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio_dev);
2746dd112b9SKsenija Stanojevic 	struct mxs_lradc_scale *scale_avail =
2756dd112b9SKsenija Stanojevic 			adc->scale_avail[chan->channel];
2766dd112b9SKsenija Stanojevic 	int ret;
2776dd112b9SKsenija Stanojevic 
2786dd112b9SKsenija Stanojevic 	ret = iio_device_claim_direct_mode(iio_dev);
2796dd112b9SKsenija Stanojevic 	if (ret)
2806dd112b9SKsenija Stanojevic 		return ret;
2816dd112b9SKsenija Stanojevic 
2826dd112b9SKsenija Stanojevic 	switch (m) {
2836dd112b9SKsenija Stanojevic 	case IIO_CHAN_INFO_SCALE:
2846dd112b9SKsenija Stanojevic 		ret = -EINVAL;
2856dd112b9SKsenija Stanojevic 		if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
2866dd112b9SKsenija Stanojevic 		    val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
2876dd112b9SKsenija Stanojevic 			/* divider by two disabled */
2886dd112b9SKsenija Stanojevic 			clear_bit(chan->channel, &adc->is_divided);
2896dd112b9SKsenija Stanojevic 			ret = 0;
2906dd112b9SKsenija Stanojevic 		} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
2916dd112b9SKsenija Stanojevic 			   val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
2926dd112b9SKsenija Stanojevic 			/* divider by two enabled */
2936dd112b9SKsenija Stanojevic 			set_bit(chan->channel, &adc->is_divided);
2946dd112b9SKsenija Stanojevic 			ret = 0;
2956dd112b9SKsenija Stanojevic 		}
2966dd112b9SKsenija Stanojevic 
2976dd112b9SKsenija Stanojevic 		break;
2986dd112b9SKsenija Stanojevic 	default:
2996dd112b9SKsenija Stanojevic 		ret = -EINVAL;
3006dd112b9SKsenija Stanojevic 		break;
3016dd112b9SKsenija Stanojevic 	}
3026dd112b9SKsenija Stanojevic 
3036dd112b9SKsenija Stanojevic 	iio_device_release_direct_mode(iio_dev);
3046dd112b9SKsenija Stanojevic 
3056dd112b9SKsenija Stanojevic 	return ret;
3066dd112b9SKsenija Stanojevic }
3076dd112b9SKsenija Stanojevic 
mxs_lradc_adc_write_raw_get_fmt(struct iio_dev * iio_dev,const struct iio_chan_spec * chan,long m)3086dd112b9SKsenija Stanojevic static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
3096dd112b9SKsenija Stanojevic 					   const struct iio_chan_spec *chan,
3106dd112b9SKsenija Stanojevic 					   long m)
3116dd112b9SKsenija Stanojevic {
3126dd112b9SKsenija Stanojevic 	return IIO_VAL_INT_PLUS_NANO;
3136dd112b9SKsenija Stanojevic }
3146dd112b9SKsenija Stanojevic 
mxs_lradc_adc_show_scale_avail(struct device * dev,struct device_attribute * attr,char * buf)3156dd112b9SKsenija Stanojevic static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
3166dd112b9SKsenija Stanojevic 						 struct device_attribute *attr,
3176dd112b9SKsenija Stanojevic 						 char *buf)
3186dd112b9SKsenija Stanojevic {
3196dd112b9SKsenija Stanojevic 	struct iio_dev *iio = dev_to_iio_dev(dev);
3206dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
3216dd112b9SKsenija Stanojevic 	struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
3226dd112b9SKsenija Stanojevic 	int i, ch, len = 0;
3236dd112b9SKsenija Stanojevic 
3246dd112b9SKsenija Stanojevic 	ch = iio_attr->address;
3256dd112b9SKsenija Stanojevic 	for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
3266dd112b9SKsenija Stanojevic 		len += sprintf(buf + len, "%u.%09u ",
3276dd112b9SKsenija Stanojevic 			       adc->scale_avail[ch][i].integer,
3286dd112b9SKsenija Stanojevic 			       adc->scale_avail[ch][i].nano);
3296dd112b9SKsenija Stanojevic 
3306dd112b9SKsenija Stanojevic 	len += sprintf(buf + len, "\n");
3316dd112b9SKsenija Stanojevic 
3326dd112b9SKsenija Stanojevic 	return len;
3336dd112b9SKsenija Stanojevic }
3346dd112b9SKsenija Stanojevic 
3356dd112b9SKsenija Stanojevic #define SHOW_SCALE_AVAILABLE_ATTR(ch)\
3366dd112b9SKsenija Stanojevic 	IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\
3376dd112b9SKsenija Stanojevic 			mxs_lradc_adc_show_scale_avail, NULL, ch)
3386dd112b9SKsenija Stanojevic 
339f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(0);
340f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(1);
341f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(2);
342f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(3);
343f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(4);
344f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(5);
345f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(6);
346f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(7);
347f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(10);
348f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(11);
349f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(12);
350f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(13);
351f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(14);
352f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(15);
3536dd112b9SKsenija Stanojevic 
3546dd112b9SKsenija Stanojevic static struct attribute *mxs_lradc_adc_attributes[] = {
3556dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
3566dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
3576dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
3586dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
3596dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
3606dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
3616dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
3626dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
3636dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
3646dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
3656dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
3666dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
3676dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
3686dd112b9SKsenija Stanojevic 	&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
3696dd112b9SKsenija Stanojevic 	NULL
3706dd112b9SKsenija Stanojevic };
3716dd112b9SKsenija Stanojevic 
3726dd112b9SKsenija Stanojevic static const struct attribute_group mxs_lradc_adc_attribute_group = {
3736dd112b9SKsenija Stanojevic 	.attrs = mxs_lradc_adc_attributes,
3746dd112b9SKsenija Stanojevic };
3756dd112b9SKsenija Stanojevic 
3766dd112b9SKsenija Stanojevic static const struct iio_info mxs_lradc_adc_iio_info = {
3776dd112b9SKsenija Stanojevic 	.read_raw		= mxs_lradc_adc_read_raw,
3786dd112b9SKsenija Stanojevic 	.write_raw		= mxs_lradc_adc_write_raw,
3796dd112b9SKsenija Stanojevic 	.write_raw_get_fmt	= mxs_lradc_adc_write_raw_get_fmt,
3806dd112b9SKsenija Stanojevic 	.attrs			= &mxs_lradc_adc_attribute_group,
3816dd112b9SKsenija Stanojevic };
3826dd112b9SKsenija Stanojevic 
3836dd112b9SKsenija Stanojevic /* IRQ Handling */
mxs_lradc_adc_handle_irq(int irq,void * data)3846dd112b9SKsenija Stanojevic static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
3856dd112b9SKsenija Stanojevic {
3866dd112b9SKsenija Stanojevic 	struct iio_dev *iio = data;
3876dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
3886dd112b9SKsenija Stanojevic 	struct mxs_lradc *lradc = adc->lradc;
3896dd112b9SKsenija Stanojevic 	unsigned long reg = readl(adc->base + LRADC_CTRL1);
3906dd112b9SKsenija Stanojevic 	unsigned long flags;
3916dd112b9SKsenija Stanojevic 
3926dd112b9SKsenija Stanojevic 	if (!(reg & mxs_lradc_irq_mask(lradc)))
3936dd112b9SKsenija Stanojevic 		return IRQ_NONE;
3946dd112b9SKsenija Stanojevic 
3956dd112b9SKsenija Stanojevic 	if (iio_buffer_enabled(iio)) {
3966dd112b9SKsenija Stanojevic 		if (reg & lradc->buffer_vchans) {
3976dd112b9SKsenija Stanojevic 			spin_lock_irqsave(&adc->lock, flags);
3986dd112b9SKsenija Stanojevic 			iio_trigger_poll(iio->trig);
3996dd112b9SKsenija Stanojevic 			spin_unlock_irqrestore(&adc->lock, flags);
4006dd112b9SKsenija Stanojevic 		}
4016dd112b9SKsenija Stanojevic 	} else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
4026dd112b9SKsenija Stanojevic 		complete(&adc->completion);
4036dd112b9SKsenija Stanojevic 	}
4046dd112b9SKsenija Stanojevic 
4056dd112b9SKsenija Stanojevic 	writel(reg & mxs_lradc_irq_mask(lradc),
4066dd112b9SKsenija Stanojevic 	       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
4076dd112b9SKsenija Stanojevic 
4086dd112b9SKsenija Stanojevic 	return IRQ_HANDLED;
4096dd112b9SKsenija Stanojevic }
4106dd112b9SKsenija Stanojevic 
4116dd112b9SKsenija Stanojevic 
4126dd112b9SKsenija Stanojevic /* Trigger handling */
mxs_lradc_adc_trigger_handler(int irq,void * p)4136dd112b9SKsenija Stanojevic static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
4146dd112b9SKsenija Stanojevic {
4156dd112b9SKsenija Stanojevic 	struct iio_poll_func *pf = p;
4166dd112b9SKsenija Stanojevic 	struct iio_dev *iio = pf->indio_dev;
4176dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
4186dd112b9SKsenija Stanojevic 	const u32 chan_value = LRADC_CH_ACCUMULATE |
4196dd112b9SKsenija Stanojevic 		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
4206dd112b9SKsenija Stanojevic 	unsigned int i, j = 0;
4216dd112b9SKsenija Stanojevic 
4226dd112b9SKsenija Stanojevic 	for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
4236dd112b9SKsenija Stanojevic 		adc->buffer[j] = readl(adc->base + LRADC_CH(j));
4246dd112b9SKsenija Stanojevic 		writel(chan_value, adc->base + LRADC_CH(j));
4256dd112b9SKsenija Stanojevic 		adc->buffer[j] &= LRADC_CH_VALUE_MASK;
4266dd112b9SKsenija Stanojevic 		adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
4276dd112b9SKsenija Stanojevic 		j++;
4286dd112b9SKsenija Stanojevic 	}
4296dd112b9SKsenija Stanojevic 
4306dd112b9SKsenija Stanojevic 	iio_push_to_buffers_with_timestamp(iio, adc->buffer, pf->timestamp);
4316dd112b9SKsenija Stanojevic 
4326dd112b9SKsenija Stanojevic 	iio_trigger_notify_done(iio->trig);
4336dd112b9SKsenija Stanojevic 
4346dd112b9SKsenija Stanojevic 	return IRQ_HANDLED;
4356dd112b9SKsenija Stanojevic }
4366dd112b9SKsenija Stanojevic 
mxs_lradc_adc_configure_trigger(struct iio_trigger * trig,bool state)4376dd112b9SKsenija Stanojevic static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
4386dd112b9SKsenija Stanojevic {
4396dd112b9SKsenija Stanojevic 	struct iio_dev *iio = iio_trigger_get_drvdata(trig);
4406dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
4416dd112b9SKsenija Stanojevic 	const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
4426dd112b9SKsenija Stanojevic 
4436dd112b9SKsenija Stanojevic 	writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st));
4446dd112b9SKsenija Stanojevic 
4456dd112b9SKsenija Stanojevic 	return 0;
4466dd112b9SKsenija Stanojevic }
4476dd112b9SKsenija Stanojevic 
4486dd112b9SKsenija Stanojevic static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
4496dd112b9SKsenija Stanojevic 	.set_trigger_state = &mxs_lradc_adc_configure_trigger,
4506dd112b9SKsenija Stanojevic };
4516dd112b9SKsenija Stanojevic 
mxs_lradc_adc_trigger_init(struct iio_dev * iio)4526dd112b9SKsenija Stanojevic static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
4536dd112b9SKsenija Stanojevic {
4546dd112b9SKsenija Stanojevic 	int ret;
4556dd112b9SKsenija Stanojevic 	struct iio_trigger *trig;
4566dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
4576dd112b9SKsenija Stanojevic 
4586dd112b9SKsenija Stanojevic 	trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
45915ea2878SJonathan Cameron 				      iio_device_id(iio));
46013814627SKangjie Lu 	if (!trig)
46113814627SKangjie Lu 		return -ENOMEM;
4626dd112b9SKsenija Stanojevic 
4636dd112b9SKsenija Stanojevic 	trig->dev.parent = adc->dev;
4646dd112b9SKsenija Stanojevic 	iio_trigger_set_drvdata(trig, iio);
4656dd112b9SKsenija Stanojevic 	trig->ops = &mxs_lradc_adc_trigger_ops;
4666dd112b9SKsenija Stanojevic 
4676dd112b9SKsenija Stanojevic 	ret = iio_trigger_register(trig);
4686dd112b9SKsenija Stanojevic 	if (ret)
4696dd112b9SKsenija Stanojevic 		return ret;
4706dd112b9SKsenija Stanojevic 
4716dd112b9SKsenija Stanojevic 	adc->trig = trig;
4726dd112b9SKsenija Stanojevic 
4736dd112b9SKsenija Stanojevic 	return 0;
4746dd112b9SKsenija Stanojevic }
4756dd112b9SKsenija Stanojevic 
mxs_lradc_adc_trigger_remove(struct iio_dev * iio)4766dd112b9SKsenija Stanojevic static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
4776dd112b9SKsenija Stanojevic {
4786dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
4796dd112b9SKsenija Stanojevic 
4806dd112b9SKsenija Stanojevic 	iio_trigger_unregister(adc->trig);
4816dd112b9SKsenija Stanojevic }
4826dd112b9SKsenija Stanojevic 
mxs_lradc_adc_buffer_preenable(struct iio_dev * iio)4836dd112b9SKsenija Stanojevic static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
4846dd112b9SKsenija Stanojevic {
4856dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
4866dd112b9SKsenija Stanojevic 	struct mxs_lradc *lradc = adc->lradc;
4876dd112b9SKsenija Stanojevic 	int chan, ofs = 0;
4886dd112b9SKsenija Stanojevic 	unsigned long enable = 0;
4896dd112b9SKsenija Stanojevic 	u32 ctrl4_set = 0;
4906dd112b9SKsenija Stanojevic 	u32 ctrl4_clr = 0;
4916dd112b9SKsenija Stanojevic 	u32 ctrl1_irq = 0;
4926dd112b9SKsenija Stanojevic 	const u32 chan_value = LRADC_CH_ACCUMULATE |
4936dd112b9SKsenija Stanojevic 		((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
4946dd112b9SKsenija Stanojevic 
4956dd112b9SKsenija Stanojevic 	if (lradc->soc == IMX28_LRADC)
4966dd112b9SKsenija Stanojevic 		writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
4976dd112b9SKsenija Stanojevic 		       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
4986dd112b9SKsenija Stanojevic 	writel(lradc->buffer_vchans,
4996dd112b9SKsenija Stanojevic 	       adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
5006dd112b9SKsenija Stanojevic 
5016dd112b9SKsenija Stanojevic 	for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
5026dd112b9SKsenija Stanojevic 		ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
5036dd112b9SKsenija Stanojevic 		ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
5046dd112b9SKsenija Stanojevic 		ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
5056dd112b9SKsenija Stanojevic 		writel(chan_value, adc->base + LRADC_CH(ofs));
5066dd112b9SKsenija Stanojevic 		bitmap_set(&enable, ofs, 1);
5076dd112b9SKsenija Stanojevic 		ofs++;
5086dd112b9SKsenija Stanojevic 	}
5096dd112b9SKsenija Stanojevic 
5106dd112b9SKsenija Stanojevic 	writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
5116dd112b9SKsenija Stanojevic 	       adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
5126dd112b9SKsenija Stanojevic 	writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
5136dd112b9SKsenija Stanojevic 	writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
5146dd112b9SKsenija Stanojevic 	writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
5156dd112b9SKsenija Stanojevic 	writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
5166dd112b9SKsenija Stanojevic 	       adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
5176dd112b9SKsenija Stanojevic 
5186dd112b9SKsenija Stanojevic 	return 0;
5196dd112b9SKsenija Stanojevic }
5206dd112b9SKsenija Stanojevic 
mxs_lradc_adc_buffer_postdisable(struct iio_dev * iio)5216dd112b9SKsenija Stanojevic static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
5226dd112b9SKsenija Stanojevic {
5236dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
5246dd112b9SKsenija Stanojevic 	struct mxs_lradc *lradc = adc->lradc;
5256dd112b9SKsenija Stanojevic 
5266dd112b9SKsenija Stanojevic 	writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
5276dd112b9SKsenija Stanojevic 	       adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
5286dd112b9SKsenija Stanojevic 
5296dd112b9SKsenija Stanojevic 	writel(lradc->buffer_vchans,
5306dd112b9SKsenija Stanojevic 	       adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
5316dd112b9SKsenija Stanojevic 	if (lradc->soc == IMX28_LRADC)
5326dd112b9SKsenija Stanojevic 		writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
5336dd112b9SKsenija Stanojevic 		       adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
5346dd112b9SKsenija Stanojevic 
5356dd112b9SKsenija Stanojevic 	return 0;
5366dd112b9SKsenija Stanojevic }
5376dd112b9SKsenija Stanojevic 
mxs_lradc_adc_validate_scan_mask(struct iio_dev * iio,const unsigned long * mask)5386dd112b9SKsenija Stanojevic static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
5396dd112b9SKsenija Stanojevic 					     const unsigned long *mask)
5406dd112b9SKsenija Stanojevic {
5416dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
5426dd112b9SKsenija Stanojevic 	struct mxs_lradc *lradc = adc->lradc;
5436dd112b9SKsenija Stanojevic 	const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
5446dd112b9SKsenija Stanojevic 	int rsvd_chans = 0;
5456dd112b9SKsenija Stanojevic 	unsigned long rsvd_mask = 0;
5466dd112b9SKsenija Stanojevic 
5476dd112b9SKsenija Stanojevic 	if (lradc->use_touchbutton)
5486dd112b9SKsenija Stanojevic 		rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
5496dd112b9SKsenija Stanojevic 	if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE)
5506dd112b9SKsenija Stanojevic 		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
5516dd112b9SKsenija Stanojevic 	if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
5526dd112b9SKsenija Stanojevic 		rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
5536dd112b9SKsenija Stanojevic 
5546dd112b9SKsenija Stanojevic 	if (lradc->use_touchbutton)
5556dd112b9SKsenija Stanojevic 		rsvd_chans++;
5566dd112b9SKsenija Stanojevic 	if (lradc->touchscreen_wire)
5576dd112b9SKsenija Stanojevic 		rsvd_chans += 2;
5586dd112b9SKsenija Stanojevic 
5596dd112b9SKsenija Stanojevic 	/* Test for attempts to map channels with special mode of operation. */
5606dd112b9SKsenija Stanojevic 	if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
5616dd112b9SKsenija Stanojevic 		return false;
5626dd112b9SKsenija Stanojevic 
5636dd112b9SKsenija Stanojevic 	/* Test for attempts to map more channels then available slots. */
5646dd112b9SKsenija Stanojevic 	if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
5656dd112b9SKsenija Stanojevic 		return false;
5666dd112b9SKsenija Stanojevic 
5676dd112b9SKsenija Stanojevic 	return true;
5686dd112b9SKsenija Stanojevic }
5696dd112b9SKsenija Stanojevic 
5706dd112b9SKsenija Stanojevic static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
5716dd112b9SKsenija Stanojevic 	.preenable = &mxs_lradc_adc_buffer_preenable,
5726dd112b9SKsenija Stanojevic 	.postdisable = &mxs_lradc_adc_buffer_postdisable,
5736dd112b9SKsenija Stanojevic 	.validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
5746dd112b9SKsenija Stanojevic };
5756dd112b9SKsenija Stanojevic 
5766dd112b9SKsenija Stanojevic /* Driver initialization */
5776dd112b9SKsenija Stanojevic #define MXS_ADC_CHAN(idx, chan_type, name) {			\
5786dd112b9SKsenija Stanojevic 	.type = (chan_type),					\
5796dd112b9SKsenija Stanojevic 	.indexed = 1,						\
5806dd112b9SKsenija Stanojevic 	.scan_index = (idx),					\
5816dd112b9SKsenija Stanojevic 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
5826dd112b9SKsenija Stanojevic 			      BIT(IIO_CHAN_INFO_SCALE),		\
5836dd112b9SKsenija Stanojevic 	.channel = (idx),					\
5846dd112b9SKsenija Stanojevic 	.address = (idx),					\
5856dd112b9SKsenija Stanojevic 	.scan_type = {						\
5866dd112b9SKsenija Stanojevic 		.sign = 'u',					\
5876dd112b9SKsenija Stanojevic 		.realbits = LRADC_RESOLUTION,			\
5886dd112b9SKsenija Stanojevic 		.storagebits = 32,				\
5896dd112b9SKsenija Stanojevic 	},							\
5906dd112b9SKsenija Stanojevic 	.datasheet_name = (name),				\
5916dd112b9SKsenija Stanojevic }
5926dd112b9SKsenija Stanojevic 
5936dd112b9SKsenija Stanojevic static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
5946dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
5956dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
5966dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
5976dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
5986dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
5996dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
6006dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
6016dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
6026dd112b9SKsenija Stanojevic 	/* Combined Temperature sensors */
6036dd112b9SKsenija Stanojevic 	{
6046dd112b9SKsenija Stanojevic 		.type = IIO_TEMP,
6056dd112b9SKsenija Stanojevic 		.indexed = 1,
6066dd112b9SKsenija Stanojevic 		.scan_index = 8,
6076dd112b9SKsenija Stanojevic 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
6086dd112b9SKsenija Stanojevic 				      BIT(IIO_CHAN_INFO_OFFSET) |
6096dd112b9SKsenija Stanojevic 				      BIT(IIO_CHAN_INFO_SCALE),
6106dd112b9SKsenija Stanojevic 		.channel = 8,
6116dd112b9SKsenija Stanojevic 		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
6126dd112b9SKsenija Stanojevic 		.datasheet_name = "TEMP_DIE",
6136dd112b9SKsenija Stanojevic 	},
6146dd112b9SKsenija Stanojevic 	/* Hidden channel to keep indexes */
6156dd112b9SKsenija Stanojevic 	{
6166dd112b9SKsenija Stanojevic 		.type = IIO_TEMP,
6176dd112b9SKsenija Stanojevic 		.indexed = 1,
6186dd112b9SKsenija Stanojevic 		.scan_index = -1,
6196dd112b9SKsenija Stanojevic 		.channel = 9,
6206dd112b9SKsenija Stanojevic 	},
6216dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
6226dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
6236dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
6246dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
6256dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
6266dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
6276dd112b9SKsenija Stanojevic };
6286dd112b9SKsenija Stanojevic 
6296dd112b9SKsenija Stanojevic static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
6306dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
6316dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
6326dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
6336dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
6346dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
6356dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
6366dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
6376dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
6386dd112b9SKsenija Stanojevic 	/* Combined Temperature sensors */
6396dd112b9SKsenija Stanojevic 	{
6406dd112b9SKsenija Stanojevic 		.type = IIO_TEMP,
6416dd112b9SKsenija Stanojevic 		.indexed = 1,
6426dd112b9SKsenija Stanojevic 		.scan_index = 8,
6436dd112b9SKsenija Stanojevic 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
6446dd112b9SKsenija Stanojevic 				      BIT(IIO_CHAN_INFO_OFFSET) |
6456dd112b9SKsenija Stanojevic 				      BIT(IIO_CHAN_INFO_SCALE),
6466dd112b9SKsenija Stanojevic 		.channel = 8,
6476dd112b9SKsenija Stanojevic 		.scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
6486dd112b9SKsenija Stanojevic 		.datasheet_name = "TEMP_DIE",
6496dd112b9SKsenija Stanojevic 	},
6506dd112b9SKsenija Stanojevic 	/* Hidden channel to keep indexes */
6516dd112b9SKsenija Stanojevic 	{
6526dd112b9SKsenija Stanojevic 		.type = IIO_TEMP,
6536dd112b9SKsenija Stanojevic 		.indexed = 1,
6546dd112b9SKsenija Stanojevic 		.scan_index = -1,
6556dd112b9SKsenija Stanojevic 		.channel = 9,
6566dd112b9SKsenija Stanojevic 	},
6576dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
6586dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
6596dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
6606dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
6616dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
6626dd112b9SKsenija Stanojevic 	MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
6636dd112b9SKsenija Stanojevic };
6646dd112b9SKsenija Stanojevic 
mxs_lradc_adc_hw_init(struct mxs_lradc_adc * adc)6656dd112b9SKsenija Stanojevic static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
6666dd112b9SKsenija Stanojevic {
6676dd112b9SKsenija Stanojevic 	/* The ADC always uses DELAY CHANNEL 0. */
6686dd112b9SKsenija Stanojevic 	const u32 adc_cfg =
6696dd112b9SKsenija Stanojevic 		(1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
6706dd112b9SKsenija Stanojevic 		(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
6716dd112b9SKsenija Stanojevic 
6726dd112b9SKsenija Stanojevic 	/* Configure DELAY CHANNEL 0 for generic ADC sampling. */
6736dd112b9SKsenija Stanojevic 	writel(adc_cfg, adc->base + LRADC_DELAY(0));
6746dd112b9SKsenija Stanojevic 
6756dd112b9SKsenija Stanojevic 	/*
6766dd112b9SKsenija Stanojevic 	 * Start internal temperature sensing by clearing bit
6776dd112b9SKsenija Stanojevic 	 * HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared
6786dd112b9SKsenija Stanojevic 	 * after power up.
6796dd112b9SKsenija Stanojevic 	 */
6806dd112b9SKsenija Stanojevic 	writel(0, adc->base + LRADC_CTRL2);
6816dd112b9SKsenija Stanojevic }
6826dd112b9SKsenija Stanojevic 
mxs_lradc_adc_hw_stop(struct mxs_lradc_adc * adc)6836dd112b9SKsenija Stanojevic static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
6846dd112b9SKsenija Stanojevic {
6856dd112b9SKsenija Stanojevic 	writel(0, adc->base + LRADC_DELAY(0));
6866dd112b9SKsenija Stanojevic }
6876dd112b9SKsenija Stanojevic 
mxs_lradc_adc_probe(struct platform_device * pdev)6886dd112b9SKsenija Stanojevic static int mxs_lradc_adc_probe(struct platform_device *pdev)
6896dd112b9SKsenija Stanojevic {
6906dd112b9SKsenija Stanojevic 	struct device *dev = &pdev->dev;
6916dd112b9SKsenija Stanojevic 	struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
6926dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc;
6936dd112b9SKsenija Stanojevic 	struct iio_dev *iio;
6946dd112b9SKsenija Stanojevic 	struct resource *iores;
6956dd112b9SKsenija Stanojevic 	int ret, irq, virq, i, s, n;
6966dd112b9SKsenija Stanojevic 	u64 scale_uv;
6976dd112b9SKsenija Stanojevic 	const char **irq_name;
6986dd112b9SKsenija Stanojevic 
6996dd112b9SKsenija Stanojevic 	/* Allocate the IIO device. */
7006dd112b9SKsenija Stanojevic 	iio = devm_iio_device_alloc(dev, sizeof(*adc));
7016dd112b9SKsenija Stanojevic 	if (!iio) {
7026dd112b9SKsenija Stanojevic 		dev_err(dev, "Failed to allocate IIO device\n");
7036dd112b9SKsenija Stanojevic 		return -ENOMEM;
7046dd112b9SKsenija Stanojevic 	}
7056dd112b9SKsenija Stanojevic 
7066dd112b9SKsenija Stanojevic 	adc = iio_priv(iio);
7076dd112b9SKsenija Stanojevic 	adc->lradc = lradc;
7086dd112b9SKsenija Stanojevic 	adc->dev = dev;
7096dd112b9SKsenija Stanojevic 
7106dd112b9SKsenija Stanojevic 	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7111454e15bSWei Yongjun 	if (!iores)
7121454e15bSWei Yongjun 		return -EINVAL;
7131454e15bSWei Yongjun 
7146dd112b9SKsenija Stanojevic 	adc->base = devm_ioremap(dev, iores->start, resource_size(iores));
7151454e15bSWei Yongjun 	if (!adc->base)
7161454e15bSWei Yongjun 		return -ENOMEM;
7176dd112b9SKsenija Stanojevic 
7186dd112b9SKsenija Stanojevic 	init_completion(&adc->completion);
7196dd112b9SKsenija Stanojevic 	spin_lock_init(&adc->lock);
7206dd112b9SKsenija Stanojevic 
7216dd112b9SKsenija Stanojevic 	platform_set_drvdata(pdev, iio);
7226dd112b9SKsenija Stanojevic 
7236dd112b9SKsenija Stanojevic 	iio->name = pdev->name;
7246dd112b9SKsenija Stanojevic 	iio->dev.of_node = dev->parent->of_node;
7256dd112b9SKsenija Stanojevic 	iio->info = &mxs_lradc_adc_iio_info;
7266dd112b9SKsenija Stanojevic 	iio->modes = INDIO_DIRECT_MODE;
7276dd112b9SKsenija Stanojevic 	iio->masklength = LRADC_MAX_TOTAL_CHANS;
7286dd112b9SKsenija Stanojevic 
7296dd112b9SKsenija Stanojevic 	if (lradc->soc == IMX23_LRADC) {
7306dd112b9SKsenija Stanojevic 		iio->channels = mx23_lradc_chan_spec;
7316dd112b9SKsenija Stanojevic 		iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
7326dd112b9SKsenija Stanojevic 		irq_name = mx23_lradc_adc_irq_names;
7336dd112b9SKsenija Stanojevic 		n = ARRAY_SIZE(mx23_lradc_adc_irq_names);
7346dd112b9SKsenija Stanojevic 	} else {
7356dd112b9SKsenija Stanojevic 		iio->channels = mx28_lradc_chan_spec;
7366dd112b9SKsenija Stanojevic 		iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
7376dd112b9SKsenija Stanojevic 		irq_name = mx28_lradc_adc_irq_names;
7386dd112b9SKsenija Stanojevic 		n = ARRAY_SIZE(mx28_lradc_adc_irq_names);
7396dd112b9SKsenija Stanojevic 	}
7406dd112b9SKsenija Stanojevic 
7416dd112b9SKsenija Stanojevic 	ret = stmp_reset_block(adc->base);
7426dd112b9SKsenija Stanojevic 	if (ret)
7436dd112b9SKsenija Stanojevic 		return ret;
7446dd112b9SKsenija Stanojevic 
7456dd112b9SKsenija Stanojevic 	for (i = 0; i < n; i++) {
7466dd112b9SKsenija Stanojevic 		irq = platform_get_irq_byname(pdev, irq_name[i]);
7476dd112b9SKsenija Stanojevic 		if (irq < 0)
7486dd112b9SKsenija Stanojevic 			return irq;
7496dd112b9SKsenija Stanojevic 
7506dd112b9SKsenija Stanojevic 		virq = irq_of_parse_and_map(dev->parent->of_node, irq);
7516dd112b9SKsenija Stanojevic 
7526dd112b9SKsenija Stanojevic 		ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq,
7536dd112b9SKsenija Stanojevic 				       0, irq_name[i], iio);
7546dd112b9SKsenija Stanojevic 		if (ret)
7556dd112b9SKsenija Stanojevic 			return ret;
7566dd112b9SKsenija Stanojevic 	}
7576dd112b9SKsenija Stanojevic 
7586dd112b9SKsenija Stanojevic 	ret = mxs_lradc_adc_trigger_init(iio);
7596dd112b9SKsenija Stanojevic 	if (ret)
760*27b2ed5bSJiakai Luo 		return ret;
7616dd112b9SKsenija Stanojevic 
7626dd112b9SKsenija Stanojevic 	ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
7636dd112b9SKsenija Stanojevic 					 &mxs_lradc_adc_trigger_handler,
7646dd112b9SKsenija Stanojevic 					 &mxs_lradc_adc_buffer_ops);
7656dd112b9SKsenija Stanojevic 	if (ret)
766*27b2ed5bSJiakai Luo 		goto err_trig;
7676dd112b9SKsenija Stanojevic 
7686dd112b9SKsenija Stanojevic 	adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
7696dd112b9SKsenija Stanojevic 
7706dd112b9SKsenija Stanojevic 	/* Populate available ADC input ranges */
7716dd112b9SKsenija Stanojevic 	for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
7726dd112b9SKsenija Stanojevic 		for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
7736dd112b9SKsenija Stanojevic 			/*
7746dd112b9SKsenija Stanojevic 			 * [s=0] = optional divider by two disabled (default)
7756dd112b9SKsenija Stanojevic 			 * [s=1] = optional divider by two enabled
7766dd112b9SKsenija Stanojevic 			 *
7776dd112b9SKsenija Stanojevic 			 * The scale is calculated by doing:
7786dd112b9SKsenija Stanojevic 			 *   Vref >> (realbits - s)
7796dd112b9SKsenija Stanojevic 			 * which multiplies by two on the second component
7806dd112b9SKsenija Stanojevic 			 * of the array.
7816dd112b9SKsenija Stanojevic 			 */
7826dd112b9SKsenija Stanojevic 			scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
7836dd112b9SKsenija Stanojevic 				   (LRADC_RESOLUTION - s);
7846dd112b9SKsenija Stanojevic 			adc->scale_avail[i][s].nano =
7856dd112b9SKsenija Stanojevic 					do_div(scale_uv, 100000000) * 10;
7866dd112b9SKsenija Stanojevic 			adc->scale_avail[i][s].integer = scale_uv;
7876dd112b9SKsenija Stanojevic 		}
7886dd112b9SKsenija Stanojevic 	}
7896dd112b9SKsenija Stanojevic 
7906dd112b9SKsenija Stanojevic 	/* Configure the hardware. */
7916dd112b9SKsenija Stanojevic 	mxs_lradc_adc_hw_init(adc);
7926dd112b9SKsenija Stanojevic 
7936dd112b9SKsenija Stanojevic 	/* Register IIO device. */
7946dd112b9SKsenija Stanojevic 	ret = iio_device_register(iio);
7956dd112b9SKsenija Stanojevic 	if (ret) {
7966dd112b9SKsenija Stanojevic 		dev_err(dev, "Failed to register IIO device\n");
7976dd112b9SKsenija Stanojevic 		goto err_dev;
7986dd112b9SKsenija Stanojevic 	}
7996dd112b9SKsenija Stanojevic 
8006dd112b9SKsenija Stanojevic 	return 0;
8016dd112b9SKsenija Stanojevic 
8026dd112b9SKsenija Stanojevic err_dev:
8036dd112b9SKsenija Stanojevic 	mxs_lradc_adc_hw_stop(adc);
8046dd112b9SKsenija Stanojevic 	iio_triggered_buffer_cleanup(iio);
805*27b2ed5bSJiakai Luo err_trig:
806*27b2ed5bSJiakai Luo 	mxs_lradc_adc_trigger_remove(iio);
8076dd112b9SKsenija Stanojevic 	return ret;
8086dd112b9SKsenija Stanojevic }
8096dd112b9SKsenija Stanojevic 
mxs_lradc_adc_remove(struct platform_device * pdev)8106dd112b9SKsenija Stanojevic static int mxs_lradc_adc_remove(struct platform_device *pdev)
8116dd112b9SKsenija Stanojevic {
8126dd112b9SKsenija Stanojevic 	struct iio_dev *iio = platform_get_drvdata(pdev);
8136dd112b9SKsenija Stanojevic 	struct mxs_lradc_adc *adc = iio_priv(iio);
8146dd112b9SKsenija Stanojevic 
8156dd112b9SKsenija Stanojevic 	iio_device_unregister(iio);
8166dd112b9SKsenija Stanojevic 	mxs_lradc_adc_hw_stop(adc);
8176dd112b9SKsenija Stanojevic 	iio_triggered_buffer_cleanup(iio);
818*27b2ed5bSJiakai Luo 	mxs_lradc_adc_trigger_remove(iio);
8196dd112b9SKsenija Stanojevic 
8206dd112b9SKsenija Stanojevic 	return 0;
8216dd112b9SKsenija Stanojevic }
8226dd112b9SKsenija Stanojevic 
8236dd112b9SKsenija Stanojevic static struct platform_driver mxs_lradc_adc_driver = {
8246dd112b9SKsenija Stanojevic 	.driver = {
8256dd112b9SKsenija Stanojevic 		.name	= "mxs-lradc-adc",
8266dd112b9SKsenija Stanojevic 	},
8276dd112b9SKsenija Stanojevic 	.probe	= mxs_lradc_adc_probe,
8286dd112b9SKsenija Stanojevic 	.remove = mxs_lradc_adc_remove,
8296dd112b9SKsenija Stanojevic };
8306dd112b9SKsenija Stanojevic module_platform_driver(mxs_lradc_adc_driver);
8316dd112b9SKsenija Stanojevic 
8326dd112b9SKsenija Stanojevic MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
8336dd112b9SKsenija Stanojevic MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
8346dd112b9SKsenija Stanojevic MODULE_LICENSE("GPL");
8356dd112b9SKsenija Stanojevic MODULE_ALIAS("platform:mxs-lradc-adc");
836