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 */
144e6d364b4SJonathan Cameron if (!iio_device_claim_direct(iio_dev))
145e6d364b4SJonathan Cameron return -EBUSY;
1466dd112b9SKsenija Stanojevic
1476dd112b9SKsenija Stanojevic reinit_completion(&adc->completion);
1486dd112b9SKsenija Stanojevic
1496dd112b9SKsenija Stanojevic /*
1506dd112b9SKsenija Stanojevic * No buffered operation in progress, map the channel and trigger it.
1516dd112b9SKsenija Stanojevic * Virtual channel 0 is always used here as the others are always not
1526dd112b9SKsenija Stanojevic * used if doing raw sampling.
1536dd112b9SKsenija Stanojevic */
1546dd112b9SKsenija Stanojevic if (lradc->soc == IMX28_LRADC)
1556dd112b9SKsenija Stanojevic writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
1566dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
1576dd112b9SKsenija Stanojevic writel(0x1, adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
1586dd112b9SKsenija Stanojevic
1596dd112b9SKsenija Stanojevic /* Enable / disable the divider per requirement */
1606dd112b9SKsenija Stanojevic if (test_bit(chan, &adc->is_divided))
1616dd112b9SKsenija Stanojevic writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
1626dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET);
1636dd112b9SKsenija Stanojevic else
1646dd112b9SKsenija Stanojevic writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
1656dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);
1666dd112b9SKsenija Stanojevic
1676dd112b9SKsenija Stanojevic /* Clean the slot's previous content, then set new one. */
1686dd112b9SKsenija Stanojevic writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
1696dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
1706dd112b9SKsenija Stanojevic writel(chan, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
1716dd112b9SKsenija Stanojevic
1726dd112b9SKsenija Stanojevic writel(0, adc->base + LRADC_CH(0));
1736dd112b9SKsenija Stanojevic
1746dd112b9SKsenija Stanojevic /* Enable the IRQ and start sampling the channel. */
1756dd112b9SKsenija Stanojevic writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
1766dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
1776dd112b9SKsenija Stanojevic writel(BIT(0), adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
1786dd112b9SKsenija Stanojevic
1796dd112b9SKsenija Stanojevic /* Wait for completion on the channel, 1 second max. */
1806dd112b9SKsenija Stanojevic ret = wait_for_completion_killable_timeout(&adc->completion, HZ);
1816dd112b9SKsenija Stanojevic if (!ret)
1826dd112b9SKsenija Stanojevic ret = -ETIMEDOUT;
1836dd112b9SKsenija Stanojevic if (ret < 0)
1846dd112b9SKsenija Stanojevic goto err;
1856dd112b9SKsenija Stanojevic
1866dd112b9SKsenija Stanojevic /* Read the data. */
1876dd112b9SKsenija Stanojevic *val = readl(adc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
1886dd112b9SKsenija Stanojevic ret = IIO_VAL_INT;
1896dd112b9SKsenija Stanojevic
1906dd112b9SKsenija Stanojevic err:
1916dd112b9SKsenija Stanojevic writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
1926dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
1936dd112b9SKsenija Stanojevic
194e6d364b4SJonathan Cameron iio_device_release_direct(iio_dev);
1956dd112b9SKsenija Stanojevic
1966dd112b9SKsenija Stanojevic return ret;
1976dd112b9SKsenija Stanojevic }
1986dd112b9SKsenija Stanojevic
mxs_lradc_adc_read_temp(struct iio_dev * iio_dev,int * val)1996dd112b9SKsenija Stanojevic static int mxs_lradc_adc_read_temp(struct iio_dev *iio_dev, int *val)
2006dd112b9SKsenija Stanojevic {
2016dd112b9SKsenija Stanojevic int ret, min, max;
2026dd112b9SKsenija Stanojevic
2036dd112b9SKsenija Stanojevic ret = mxs_lradc_adc_read_single(iio_dev, 8, &min);
2046dd112b9SKsenija Stanojevic if (ret != IIO_VAL_INT)
2056dd112b9SKsenija Stanojevic return ret;
2066dd112b9SKsenija Stanojevic
2076dd112b9SKsenija Stanojevic ret = mxs_lradc_adc_read_single(iio_dev, 9, &max);
2086dd112b9SKsenija Stanojevic if (ret != IIO_VAL_INT)
2096dd112b9SKsenija Stanojevic return ret;
2106dd112b9SKsenija Stanojevic
2116dd112b9SKsenija Stanojevic *val = max - min;
2126dd112b9SKsenija Stanojevic
2136dd112b9SKsenija Stanojevic return IIO_VAL_INT;
2146dd112b9SKsenija Stanojevic }
2156dd112b9SKsenija Stanojevic
mxs_lradc_adc_read_raw(struct iio_dev * iio_dev,const struct iio_chan_spec * chan,int * val,int * val2,long m)2166dd112b9SKsenija Stanojevic static int mxs_lradc_adc_read_raw(struct iio_dev *iio_dev,
2176dd112b9SKsenija Stanojevic const struct iio_chan_spec *chan,
2186dd112b9SKsenija Stanojevic int *val, int *val2, long m)
2196dd112b9SKsenija Stanojevic {
2206dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio_dev);
2216dd112b9SKsenija Stanojevic
2226dd112b9SKsenija Stanojevic switch (m) {
2236dd112b9SKsenija Stanojevic case IIO_CHAN_INFO_RAW:
2246dd112b9SKsenija Stanojevic if (chan->type == IIO_TEMP)
2256dd112b9SKsenija Stanojevic return mxs_lradc_adc_read_temp(iio_dev, val);
2266dd112b9SKsenija Stanojevic
2276dd112b9SKsenija Stanojevic return mxs_lradc_adc_read_single(iio_dev, chan->channel, val);
2286dd112b9SKsenija Stanojevic
2296dd112b9SKsenija Stanojevic case IIO_CHAN_INFO_SCALE:
2306dd112b9SKsenija Stanojevic if (chan->type == IIO_TEMP) {
2316dd112b9SKsenija Stanojevic /*
2326dd112b9SKsenija Stanojevic * From the datasheet, we have to multiply by 1.012 and
2336dd112b9SKsenija Stanojevic * divide by 4
2346dd112b9SKsenija Stanojevic */
2356dd112b9SKsenija Stanojevic *val = 0;
2366dd112b9SKsenija Stanojevic *val2 = 253000;
2376dd112b9SKsenija Stanojevic return IIO_VAL_INT_PLUS_MICRO;
2386dd112b9SKsenija Stanojevic }
2396dd112b9SKsenija Stanojevic
2406dd112b9SKsenija Stanojevic *val = adc->vref_mv[chan->channel];
2416dd112b9SKsenija Stanojevic *val2 = chan->scan_type.realbits -
2426dd112b9SKsenija Stanojevic test_bit(chan->channel, &adc->is_divided);
2436dd112b9SKsenija Stanojevic return IIO_VAL_FRACTIONAL_LOG2;
2446dd112b9SKsenija Stanojevic
2456dd112b9SKsenija Stanojevic case IIO_CHAN_INFO_OFFSET:
2466dd112b9SKsenija Stanojevic if (chan->type == IIO_TEMP) {
2476dd112b9SKsenija Stanojevic /*
2486dd112b9SKsenija Stanojevic * The calculated value from the ADC is in Kelvin, we
2496dd112b9SKsenija Stanojevic * want Celsius for hwmon so the offset is -273.15
2506dd112b9SKsenija Stanojevic * The offset is applied before scaling so it is
2516dd112b9SKsenija Stanojevic * actually -213.15 * 4 / 1.012 = -1079.644268
2526dd112b9SKsenija Stanojevic */
2536dd112b9SKsenija Stanojevic *val = -1079;
2546dd112b9SKsenija Stanojevic *val2 = 644268;
2556dd112b9SKsenija Stanojevic
2566dd112b9SKsenija Stanojevic return IIO_VAL_INT_PLUS_MICRO;
2576dd112b9SKsenija Stanojevic }
2586dd112b9SKsenija Stanojevic
2596dd112b9SKsenija Stanojevic return -EINVAL;
2606dd112b9SKsenija Stanojevic
2616dd112b9SKsenija Stanojevic default:
2626dd112b9SKsenija Stanojevic break;
2636dd112b9SKsenija Stanojevic }
2646dd112b9SKsenija Stanojevic
2656dd112b9SKsenija Stanojevic return -EINVAL;
2666dd112b9SKsenija Stanojevic }
2676dd112b9SKsenija Stanojevic
mxs_lradc_adc_write_raw(struct iio_dev * iio_dev,const struct iio_chan_spec * chan,int val,int val2,long m)2686dd112b9SKsenija Stanojevic static int mxs_lradc_adc_write_raw(struct iio_dev *iio_dev,
2696dd112b9SKsenija Stanojevic const struct iio_chan_spec *chan,
2706dd112b9SKsenija Stanojevic int val, int val2, long m)
2716dd112b9SKsenija Stanojevic {
2726dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio_dev);
2736dd112b9SKsenija Stanojevic struct mxs_lradc_scale *scale_avail =
2746dd112b9SKsenija Stanojevic adc->scale_avail[chan->channel];
2756dd112b9SKsenija Stanojevic int ret;
2766dd112b9SKsenija Stanojevic
277e6d364b4SJonathan Cameron if (!iio_device_claim_direct(iio_dev))
278e6d364b4SJonathan Cameron return -EBUSY;
2796dd112b9SKsenija Stanojevic
2806dd112b9SKsenija Stanojevic switch (m) {
2816dd112b9SKsenija Stanojevic case IIO_CHAN_INFO_SCALE:
2826dd112b9SKsenija Stanojevic ret = -EINVAL;
2836dd112b9SKsenija Stanojevic if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
2846dd112b9SKsenija Stanojevic val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
2856dd112b9SKsenija Stanojevic /* divider by two disabled */
2866dd112b9SKsenija Stanojevic clear_bit(chan->channel, &adc->is_divided);
2876dd112b9SKsenija Stanojevic ret = 0;
2886dd112b9SKsenija Stanojevic } else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
2896dd112b9SKsenija Stanojevic val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
2906dd112b9SKsenija Stanojevic /* divider by two enabled */
2916dd112b9SKsenija Stanojevic set_bit(chan->channel, &adc->is_divided);
2926dd112b9SKsenija Stanojevic ret = 0;
2936dd112b9SKsenija Stanojevic }
2946dd112b9SKsenija Stanojevic
2956dd112b9SKsenija Stanojevic break;
2966dd112b9SKsenija Stanojevic default:
2976dd112b9SKsenija Stanojevic ret = -EINVAL;
2986dd112b9SKsenija Stanojevic break;
2996dd112b9SKsenija Stanojevic }
3006dd112b9SKsenija Stanojevic
301e6d364b4SJonathan Cameron iio_device_release_direct(iio_dev);
3026dd112b9SKsenija Stanojevic
3036dd112b9SKsenija Stanojevic return ret;
3046dd112b9SKsenija Stanojevic }
3056dd112b9SKsenija Stanojevic
mxs_lradc_adc_write_raw_get_fmt(struct iio_dev * iio_dev,const struct iio_chan_spec * chan,long m)3066dd112b9SKsenija Stanojevic static int mxs_lradc_adc_write_raw_get_fmt(struct iio_dev *iio_dev,
3076dd112b9SKsenija Stanojevic const struct iio_chan_spec *chan,
3086dd112b9SKsenija Stanojevic long m)
3096dd112b9SKsenija Stanojevic {
3106dd112b9SKsenija Stanojevic return IIO_VAL_INT_PLUS_NANO;
3116dd112b9SKsenija Stanojevic }
3126dd112b9SKsenija Stanojevic
mxs_lradc_adc_show_scale_avail(struct device * dev,struct device_attribute * attr,char * buf)3136dd112b9SKsenija Stanojevic static ssize_t mxs_lradc_adc_show_scale_avail(struct device *dev,
3146dd112b9SKsenija Stanojevic struct device_attribute *attr,
3156dd112b9SKsenija Stanojevic char *buf)
3166dd112b9SKsenija Stanojevic {
3176dd112b9SKsenija Stanojevic struct iio_dev *iio = dev_to_iio_dev(dev);
3186dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
3196dd112b9SKsenija Stanojevic struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
3206dd112b9SKsenija Stanojevic int i, ch, len = 0;
3216dd112b9SKsenija Stanojevic
3226dd112b9SKsenija Stanojevic ch = iio_attr->address;
3236dd112b9SKsenija Stanojevic for (i = 0; i < ARRAY_SIZE(adc->scale_avail[ch]); i++)
3246dd112b9SKsenija Stanojevic len += sprintf(buf + len, "%u.%09u ",
3256dd112b9SKsenija Stanojevic adc->scale_avail[ch][i].integer,
3266dd112b9SKsenija Stanojevic adc->scale_avail[ch][i].nano);
3276dd112b9SKsenija Stanojevic
3286dd112b9SKsenija Stanojevic len += sprintf(buf + len, "\n");
3296dd112b9SKsenija Stanojevic
3306dd112b9SKsenija Stanojevic return len;
3316dd112b9SKsenija Stanojevic }
3326dd112b9SKsenija Stanojevic
3336dd112b9SKsenija Stanojevic #define SHOW_SCALE_AVAILABLE_ATTR(ch)\
3346dd112b9SKsenija Stanojevic IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, 0444,\
3356dd112b9SKsenija Stanojevic mxs_lradc_adc_show_scale_avail, NULL, ch)
3366dd112b9SKsenija Stanojevic
337f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(0);
338f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(1);
339f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(2);
340f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(3);
341f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(4);
342f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(5);
343f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(6);
344f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(7);
345f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(10);
346f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(11);
347f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(12);
348f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(13);
349f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(14);
350f4f93bf7SPaolo Cretaro static SHOW_SCALE_AVAILABLE_ATTR(15);
3516dd112b9SKsenija Stanojevic
3526dd112b9SKsenija Stanojevic static struct attribute *mxs_lradc_adc_attributes[] = {
3536dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
3546dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
3556dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
3566dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
3576dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
3586dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
3596dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
3606dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
3616dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
3626dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
3636dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
3646dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
3656dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
3666dd112b9SKsenija Stanojevic &iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
3676dd112b9SKsenija Stanojevic NULL
3686dd112b9SKsenija Stanojevic };
3696dd112b9SKsenija Stanojevic
3706dd112b9SKsenija Stanojevic static const struct attribute_group mxs_lradc_adc_attribute_group = {
3716dd112b9SKsenija Stanojevic .attrs = mxs_lradc_adc_attributes,
3726dd112b9SKsenija Stanojevic };
3736dd112b9SKsenija Stanojevic
3746dd112b9SKsenija Stanojevic static const struct iio_info mxs_lradc_adc_iio_info = {
3756dd112b9SKsenija Stanojevic .read_raw = mxs_lradc_adc_read_raw,
3766dd112b9SKsenija Stanojevic .write_raw = mxs_lradc_adc_write_raw,
3776dd112b9SKsenija Stanojevic .write_raw_get_fmt = mxs_lradc_adc_write_raw_get_fmt,
3786dd112b9SKsenija Stanojevic .attrs = &mxs_lradc_adc_attribute_group,
3796dd112b9SKsenija Stanojevic };
3806dd112b9SKsenija Stanojevic
3816dd112b9SKsenija Stanojevic /* IRQ Handling */
mxs_lradc_adc_handle_irq(int irq,void * data)3826dd112b9SKsenija Stanojevic static irqreturn_t mxs_lradc_adc_handle_irq(int irq, void *data)
3836dd112b9SKsenija Stanojevic {
3846dd112b9SKsenija Stanojevic struct iio_dev *iio = data;
3856dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
3866dd112b9SKsenija Stanojevic struct mxs_lradc *lradc = adc->lradc;
3876dd112b9SKsenija Stanojevic unsigned long reg = readl(adc->base + LRADC_CTRL1);
3886dd112b9SKsenija Stanojevic unsigned long flags;
3896dd112b9SKsenija Stanojevic
3906dd112b9SKsenija Stanojevic if (!(reg & mxs_lradc_irq_mask(lradc)))
3916dd112b9SKsenija Stanojevic return IRQ_NONE;
3926dd112b9SKsenija Stanojevic
3936dd112b9SKsenija Stanojevic if (iio_buffer_enabled(iio)) {
3946dd112b9SKsenija Stanojevic if (reg & lradc->buffer_vchans) {
3956dd112b9SKsenija Stanojevic spin_lock_irqsave(&adc->lock, flags);
3966dd112b9SKsenija Stanojevic iio_trigger_poll(iio->trig);
3976dd112b9SKsenija Stanojevic spin_unlock_irqrestore(&adc->lock, flags);
3986dd112b9SKsenija Stanojevic }
3996dd112b9SKsenija Stanojevic } else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) {
4006dd112b9SKsenija Stanojevic complete(&adc->completion);
4016dd112b9SKsenija Stanojevic }
4026dd112b9SKsenija Stanojevic
4036dd112b9SKsenija Stanojevic writel(reg & mxs_lradc_irq_mask(lradc),
4046dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
4056dd112b9SKsenija Stanojevic
4066dd112b9SKsenija Stanojevic return IRQ_HANDLED;
4076dd112b9SKsenija Stanojevic }
4086dd112b9SKsenija Stanojevic
4096dd112b9SKsenija Stanojevic
4106dd112b9SKsenija Stanojevic /* Trigger handling */
mxs_lradc_adc_trigger_handler(int irq,void * p)4116dd112b9SKsenija Stanojevic static irqreturn_t mxs_lradc_adc_trigger_handler(int irq, void *p)
4126dd112b9SKsenija Stanojevic {
4136dd112b9SKsenija Stanojevic struct iio_poll_func *pf = p;
4146dd112b9SKsenija Stanojevic struct iio_dev *iio = pf->indio_dev;
4156dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
4166dd112b9SKsenija Stanojevic const u32 chan_value = LRADC_CH_ACCUMULATE |
4176dd112b9SKsenija Stanojevic ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
4186dd112b9SKsenija Stanojevic unsigned int i, j = 0;
4196dd112b9SKsenija Stanojevic
4206dd112b9SKsenija Stanojevic for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
4216dd112b9SKsenija Stanojevic adc->buffer[j] = readl(adc->base + LRADC_CH(j));
4226dd112b9SKsenija Stanojevic writel(chan_value, adc->base + LRADC_CH(j));
4236dd112b9SKsenija Stanojevic adc->buffer[j] &= LRADC_CH_VALUE_MASK;
4246dd112b9SKsenija Stanojevic adc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
4256dd112b9SKsenija Stanojevic j++;
4266dd112b9SKsenija Stanojevic }
4276dd112b9SKsenija Stanojevic
428*c65d3f3fSJonathan Cameron iio_push_to_buffers_with_ts(iio, adc->buffer, sizeof(adc->buffer),
429*c65d3f3fSJonathan Cameron pf->timestamp);
4306dd112b9SKsenija Stanojevic
4316dd112b9SKsenija Stanojevic iio_trigger_notify_done(iio->trig);
4326dd112b9SKsenija Stanojevic
4336dd112b9SKsenija Stanojevic return IRQ_HANDLED;
4346dd112b9SKsenija Stanojevic }
4356dd112b9SKsenija Stanojevic
mxs_lradc_adc_configure_trigger(struct iio_trigger * trig,bool state)4366dd112b9SKsenija Stanojevic static int mxs_lradc_adc_configure_trigger(struct iio_trigger *trig, bool state)
4376dd112b9SKsenija Stanojevic {
4386dd112b9SKsenija Stanojevic struct iio_dev *iio = iio_trigger_get_drvdata(trig);
4396dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
4406dd112b9SKsenija Stanojevic const u32 st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
4416dd112b9SKsenija Stanojevic
4426dd112b9SKsenija Stanojevic writel(LRADC_DELAY_KICK, adc->base + (LRADC_DELAY(0) + st));
4436dd112b9SKsenija Stanojevic
4446dd112b9SKsenija Stanojevic return 0;
4456dd112b9SKsenija Stanojevic }
4466dd112b9SKsenija Stanojevic
4476dd112b9SKsenija Stanojevic static const struct iio_trigger_ops mxs_lradc_adc_trigger_ops = {
4486dd112b9SKsenija Stanojevic .set_trigger_state = &mxs_lradc_adc_configure_trigger,
4496dd112b9SKsenija Stanojevic };
4506dd112b9SKsenija Stanojevic
mxs_lradc_adc_trigger_init(struct iio_dev * iio)4516dd112b9SKsenija Stanojevic static int mxs_lradc_adc_trigger_init(struct iio_dev *iio)
4526dd112b9SKsenija Stanojevic {
4536dd112b9SKsenija Stanojevic int ret;
4546dd112b9SKsenija Stanojevic struct iio_trigger *trig;
4556dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
4566dd112b9SKsenija Stanojevic
4576dd112b9SKsenija Stanojevic trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
45815ea2878SJonathan Cameron iio_device_id(iio));
45913814627SKangjie Lu if (!trig)
46013814627SKangjie Lu return -ENOMEM;
4616dd112b9SKsenija Stanojevic
4626dd112b9SKsenija Stanojevic trig->dev.parent = adc->dev;
4636dd112b9SKsenija Stanojevic iio_trigger_set_drvdata(trig, iio);
4646dd112b9SKsenija Stanojevic trig->ops = &mxs_lradc_adc_trigger_ops;
4656dd112b9SKsenija Stanojevic
4666dd112b9SKsenija Stanojevic ret = iio_trigger_register(trig);
4676dd112b9SKsenija Stanojevic if (ret)
4686dd112b9SKsenija Stanojevic return ret;
4696dd112b9SKsenija Stanojevic
4706dd112b9SKsenija Stanojevic adc->trig = trig;
4716dd112b9SKsenija Stanojevic
4726dd112b9SKsenija Stanojevic return 0;
4736dd112b9SKsenija Stanojevic }
4746dd112b9SKsenija Stanojevic
mxs_lradc_adc_trigger_remove(struct iio_dev * iio)4756dd112b9SKsenija Stanojevic static void mxs_lradc_adc_trigger_remove(struct iio_dev *iio)
4766dd112b9SKsenija Stanojevic {
4776dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
4786dd112b9SKsenija Stanojevic
4796dd112b9SKsenija Stanojevic iio_trigger_unregister(adc->trig);
4806dd112b9SKsenija Stanojevic }
4816dd112b9SKsenija Stanojevic
mxs_lradc_adc_buffer_preenable(struct iio_dev * iio)4826dd112b9SKsenija Stanojevic static int mxs_lradc_adc_buffer_preenable(struct iio_dev *iio)
4836dd112b9SKsenija Stanojevic {
4846dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
4856dd112b9SKsenija Stanojevic struct mxs_lradc *lradc = adc->lradc;
4866dd112b9SKsenija Stanojevic int chan, ofs = 0;
4876dd112b9SKsenija Stanojevic unsigned long enable = 0;
4886dd112b9SKsenija Stanojevic u32 ctrl4_set = 0;
4896dd112b9SKsenija Stanojevic u32 ctrl4_clr = 0;
4906dd112b9SKsenija Stanojevic u32 ctrl1_irq = 0;
4916dd112b9SKsenija Stanojevic const u32 chan_value = LRADC_CH_ACCUMULATE |
4926dd112b9SKsenija Stanojevic ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
4936dd112b9SKsenija Stanojevic
4946dd112b9SKsenija Stanojevic if (lradc->soc == IMX28_LRADC)
4956dd112b9SKsenija Stanojevic writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
4966dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
4976dd112b9SKsenija Stanojevic writel(lradc->buffer_vchans,
4986dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
4996dd112b9SKsenija Stanojevic
5006dd112b9SKsenija Stanojevic for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
5016dd112b9SKsenija Stanojevic ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
5026dd112b9SKsenija Stanojevic ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
5036dd112b9SKsenija Stanojevic ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
5046dd112b9SKsenija Stanojevic writel(chan_value, adc->base + LRADC_CH(ofs));
5056dd112b9SKsenija Stanojevic bitmap_set(&enable, ofs, 1);
5066dd112b9SKsenija Stanojevic ofs++;
5076dd112b9SKsenija Stanojevic }
5086dd112b9SKsenija Stanojevic
5096dd112b9SKsenija Stanojevic writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
5106dd112b9SKsenija Stanojevic adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
5116dd112b9SKsenija Stanojevic writel(ctrl4_clr, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
5126dd112b9SKsenija Stanojevic writel(ctrl4_set, adc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
5136dd112b9SKsenija Stanojevic writel(ctrl1_irq, adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
5146dd112b9SKsenija Stanojevic writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
5156dd112b9SKsenija Stanojevic adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
5166dd112b9SKsenija Stanojevic
5176dd112b9SKsenija Stanojevic return 0;
5186dd112b9SKsenija Stanojevic }
5196dd112b9SKsenija Stanojevic
mxs_lradc_adc_buffer_postdisable(struct iio_dev * iio)5206dd112b9SKsenija Stanojevic static int mxs_lradc_adc_buffer_postdisable(struct iio_dev *iio)
5216dd112b9SKsenija Stanojevic {
5226dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
5236dd112b9SKsenija Stanojevic struct mxs_lradc *lradc = adc->lradc;
5246dd112b9SKsenija Stanojevic
5256dd112b9SKsenija Stanojevic writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
5266dd112b9SKsenija Stanojevic adc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
5276dd112b9SKsenija Stanojevic
5286dd112b9SKsenija Stanojevic writel(lradc->buffer_vchans,
5296dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
5306dd112b9SKsenija Stanojevic if (lradc->soc == IMX28_LRADC)
5316dd112b9SKsenija Stanojevic writel(lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET,
5326dd112b9SKsenija Stanojevic adc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
5336dd112b9SKsenija Stanojevic
5346dd112b9SKsenija Stanojevic return 0;
5356dd112b9SKsenija Stanojevic }
5366dd112b9SKsenija Stanojevic
mxs_lradc_adc_validate_scan_mask(struct iio_dev * iio,const unsigned long * mask)5376dd112b9SKsenija Stanojevic static bool mxs_lradc_adc_validate_scan_mask(struct iio_dev *iio,
5386dd112b9SKsenija Stanojevic const unsigned long *mask)
5396dd112b9SKsenija Stanojevic {
5406dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
5416dd112b9SKsenija Stanojevic struct mxs_lradc *lradc = adc->lradc;
5426dd112b9SKsenija Stanojevic const int map_chans = bitmap_weight(mask, LRADC_MAX_TOTAL_CHANS);
5436dd112b9SKsenija Stanojevic int rsvd_chans = 0;
5446dd112b9SKsenija Stanojevic unsigned long rsvd_mask = 0;
5456dd112b9SKsenija Stanojevic
5466dd112b9SKsenija Stanojevic if (lradc->use_touchbutton)
5476dd112b9SKsenija Stanojevic rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
5486dd112b9SKsenija Stanojevic if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_4WIRE)
5496dd112b9SKsenija Stanojevic rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
5506dd112b9SKsenija Stanojevic if (lradc->touchscreen_wire == MXS_LRADC_TOUCHSCREEN_5WIRE)
5516dd112b9SKsenija Stanojevic rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
5526dd112b9SKsenija Stanojevic
5536dd112b9SKsenija Stanojevic if (lradc->use_touchbutton)
5546dd112b9SKsenija Stanojevic rsvd_chans++;
5556dd112b9SKsenija Stanojevic if (lradc->touchscreen_wire)
5566dd112b9SKsenija Stanojevic rsvd_chans += 2;
5576dd112b9SKsenija Stanojevic
5586dd112b9SKsenija Stanojevic /* Test for attempts to map channels with special mode of operation. */
5596dd112b9SKsenija Stanojevic if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
5606dd112b9SKsenija Stanojevic return false;
5616dd112b9SKsenija Stanojevic
5626dd112b9SKsenija Stanojevic /* Test for attempts to map more channels then available slots. */
5636dd112b9SKsenija Stanojevic if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
5646dd112b9SKsenija Stanojevic return false;
5656dd112b9SKsenija Stanojevic
5666dd112b9SKsenija Stanojevic return true;
5676dd112b9SKsenija Stanojevic }
5686dd112b9SKsenija Stanojevic
5696dd112b9SKsenija Stanojevic static const struct iio_buffer_setup_ops mxs_lradc_adc_buffer_ops = {
5706dd112b9SKsenija Stanojevic .preenable = &mxs_lradc_adc_buffer_preenable,
5716dd112b9SKsenija Stanojevic .postdisable = &mxs_lradc_adc_buffer_postdisable,
5726dd112b9SKsenija Stanojevic .validate_scan_mask = &mxs_lradc_adc_validate_scan_mask,
5736dd112b9SKsenija Stanojevic };
5746dd112b9SKsenija Stanojevic
5756dd112b9SKsenija Stanojevic /* Driver initialization */
5766dd112b9SKsenija Stanojevic #define MXS_ADC_CHAN(idx, chan_type, name) { \
5776dd112b9SKsenija Stanojevic .type = (chan_type), \
5786dd112b9SKsenija Stanojevic .indexed = 1, \
5796dd112b9SKsenija Stanojevic .scan_index = (idx), \
5806dd112b9SKsenija Stanojevic .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
5816dd112b9SKsenija Stanojevic BIT(IIO_CHAN_INFO_SCALE), \
5826dd112b9SKsenija Stanojevic .channel = (idx), \
5836dd112b9SKsenija Stanojevic .address = (idx), \
5846dd112b9SKsenija Stanojevic .scan_type = { \
5856dd112b9SKsenija Stanojevic .sign = 'u', \
5866dd112b9SKsenija Stanojevic .realbits = LRADC_RESOLUTION, \
5876dd112b9SKsenija Stanojevic .storagebits = 32, \
5886dd112b9SKsenija Stanojevic }, \
5896dd112b9SKsenija Stanojevic .datasheet_name = (name), \
5906dd112b9SKsenija Stanojevic }
5916dd112b9SKsenija Stanojevic
5926dd112b9SKsenija Stanojevic static const struct iio_chan_spec mx23_lradc_chan_spec[] = {
5936dd112b9SKsenija Stanojevic MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
5946dd112b9SKsenija Stanojevic MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
5956dd112b9SKsenija Stanojevic MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
5966dd112b9SKsenija Stanojevic MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
5976dd112b9SKsenija Stanojevic MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
5986dd112b9SKsenija Stanojevic MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
5996dd112b9SKsenija Stanojevic MXS_ADC_CHAN(6, IIO_VOLTAGE, "VDDIO"),
6006dd112b9SKsenija Stanojevic MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
6016dd112b9SKsenija Stanojevic /* Combined Temperature sensors */
6026dd112b9SKsenija Stanojevic {
6036dd112b9SKsenija Stanojevic .type = IIO_TEMP,
6046dd112b9SKsenija Stanojevic .indexed = 1,
6056dd112b9SKsenija Stanojevic .scan_index = 8,
6066dd112b9SKsenija Stanojevic .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
6076dd112b9SKsenija Stanojevic BIT(IIO_CHAN_INFO_OFFSET) |
6086dd112b9SKsenija Stanojevic BIT(IIO_CHAN_INFO_SCALE),
6096dd112b9SKsenija Stanojevic .channel = 8,
6106dd112b9SKsenija Stanojevic .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
6116dd112b9SKsenija Stanojevic .datasheet_name = "TEMP_DIE",
6126dd112b9SKsenija Stanojevic },
6136dd112b9SKsenija Stanojevic /* Hidden channel to keep indexes */
6146dd112b9SKsenija Stanojevic {
6156dd112b9SKsenija Stanojevic .type = IIO_TEMP,
6166dd112b9SKsenija Stanojevic .indexed = 1,
6176dd112b9SKsenija Stanojevic .scan_index = -1,
6186dd112b9SKsenija Stanojevic .channel = 9,
6196dd112b9SKsenija Stanojevic },
6206dd112b9SKsenija Stanojevic MXS_ADC_CHAN(10, IIO_VOLTAGE, NULL),
6216dd112b9SKsenija Stanojevic MXS_ADC_CHAN(11, IIO_VOLTAGE, NULL),
6226dd112b9SKsenija Stanojevic MXS_ADC_CHAN(12, IIO_VOLTAGE, "USB_DP"),
6236dd112b9SKsenija Stanojevic MXS_ADC_CHAN(13, IIO_VOLTAGE, "USB_DN"),
6246dd112b9SKsenija Stanojevic MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
6256dd112b9SKsenija Stanojevic MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
6266dd112b9SKsenija Stanojevic };
6276dd112b9SKsenija Stanojevic
6286dd112b9SKsenija Stanojevic static const struct iio_chan_spec mx28_lradc_chan_spec[] = {
6296dd112b9SKsenija Stanojevic MXS_ADC_CHAN(0, IIO_VOLTAGE, "LRADC0"),
6306dd112b9SKsenija Stanojevic MXS_ADC_CHAN(1, IIO_VOLTAGE, "LRADC1"),
6316dd112b9SKsenija Stanojevic MXS_ADC_CHAN(2, IIO_VOLTAGE, "LRADC2"),
6326dd112b9SKsenija Stanojevic MXS_ADC_CHAN(3, IIO_VOLTAGE, "LRADC3"),
6336dd112b9SKsenija Stanojevic MXS_ADC_CHAN(4, IIO_VOLTAGE, "LRADC4"),
6346dd112b9SKsenija Stanojevic MXS_ADC_CHAN(5, IIO_VOLTAGE, "LRADC5"),
6356dd112b9SKsenija Stanojevic MXS_ADC_CHAN(6, IIO_VOLTAGE, "LRADC6"),
6366dd112b9SKsenija Stanojevic MXS_ADC_CHAN(7, IIO_VOLTAGE, "VBATT"),
6376dd112b9SKsenija Stanojevic /* Combined Temperature sensors */
6386dd112b9SKsenija Stanojevic {
6396dd112b9SKsenija Stanojevic .type = IIO_TEMP,
6406dd112b9SKsenija Stanojevic .indexed = 1,
6416dd112b9SKsenija Stanojevic .scan_index = 8,
6426dd112b9SKsenija Stanojevic .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
6436dd112b9SKsenija Stanojevic BIT(IIO_CHAN_INFO_OFFSET) |
6446dd112b9SKsenija Stanojevic BIT(IIO_CHAN_INFO_SCALE),
6456dd112b9SKsenija Stanojevic .channel = 8,
6466dd112b9SKsenija Stanojevic .scan_type = {.sign = 'u', .realbits = 18, .storagebits = 32,},
6476dd112b9SKsenija Stanojevic .datasheet_name = "TEMP_DIE",
6486dd112b9SKsenija Stanojevic },
6496dd112b9SKsenija Stanojevic /* Hidden channel to keep indexes */
6506dd112b9SKsenija Stanojevic {
6516dd112b9SKsenija Stanojevic .type = IIO_TEMP,
6526dd112b9SKsenija Stanojevic .indexed = 1,
6536dd112b9SKsenija Stanojevic .scan_index = -1,
6546dd112b9SKsenija Stanojevic .channel = 9,
6556dd112b9SKsenija Stanojevic },
6566dd112b9SKsenija Stanojevic MXS_ADC_CHAN(10, IIO_VOLTAGE, "VDDIO"),
6576dd112b9SKsenija Stanojevic MXS_ADC_CHAN(11, IIO_VOLTAGE, "VTH"),
6586dd112b9SKsenija Stanojevic MXS_ADC_CHAN(12, IIO_VOLTAGE, "VDDA"),
6596dd112b9SKsenija Stanojevic MXS_ADC_CHAN(13, IIO_VOLTAGE, "VDDD"),
6606dd112b9SKsenija Stanojevic MXS_ADC_CHAN(14, IIO_VOLTAGE, "VBG"),
6616dd112b9SKsenija Stanojevic MXS_ADC_CHAN(15, IIO_VOLTAGE, "VDD5V"),
6626dd112b9SKsenija Stanojevic };
6636dd112b9SKsenija Stanojevic
mxs_lradc_adc_hw_init(struct mxs_lradc_adc * adc)6646dd112b9SKsenija Stanojevic static void mxs_lradc_adc_hw_init(struct mxs_lradc_adc *adc)
6656dd112b9SKsenija Stanojevic {
6666dd112b9SKsenija Stanojevic /* The ADC always uses DELAY CHANNEL 0. */
6676dd112b9SKsenija Stanojevic const u32 adc_cfg =
6686dd112b9SKsenija Stanojevic (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
6696dd112b9SKsenija Stanojevic (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
6706dd112b9SKsenija Stanojevic
6716dd112b9SKsenija Stanojevic /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
6726dd112b9SKsenija Stanojevic writel(adc_cfg, adc->base + LRADC_DELAY(0));
6736dd112b9SKsenija Stanojevic
6746dd112b9SKsenija Stanojevic /*
6756dd112b9SKsenija Stanojevic * Start internal temperature sensing by clearing bit
6766dd112b9SKsenija Stanojevic * HW_LRADC_CTRL2_TEMPSENSE_PWD. This bit can be left cleared
6776dd112b9SKsenija Stanojevic * after power up.
6786dd112b9SKsenija Stanojevic */
6796dd112b9SKsenija Stanojevic writel(0, adc->base + LRADC_CTRL2);
6806dd112b9SKsenija Stanojevic }
6816dd112b9SKsenija Stanojevic
mxs_lradc_adc_hw_stop(struct mxs_lradc_adc * adc)6826dd112b9SKsenija Stanojevic static void mxs_lradc_adc_hw_stop(struct mxs_lradc_adc *adc)
6836dd112b9SKsenija Stanojevic {
6846dd112b9SKsenija Stanojevic writel(0, adc->base + LRADC_DELAY(0));
6856dd112b9SKsenija Stanojevic }
6866dd112b9SKsenija Stanojevic
mxs_lradc_adc_probe(struct platform_device * pdev)6876dd112b9SKsenija Stanojevic static int mxs_lradc_adc_probe(struct platform_device *pdev)
6886dd112b9SKsenija Stanojevic {
6896dd112b9SKsenija Stanojevic struct device *dev = &pdev->dev;
6906dd112b9SKsenija Stanojevic struct mxs_lradc *lradc = dev_get_drvdata(dev->parent);
6916dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc;
6926dd112b9SKsenija Stanojevic struct iio_dev *iio;
6936dd112b9SKsenija Stanojevic struct resource *iores;
6946dd112b9SKsenija Stanojevic int ret, irq, virq, i, s, n;
6956dd112b9SKsenija Stanojevic u64 scale_uv;
6966dd112b9SKsenija Stanojevic const char **irq_name;
6976dd112b9SKsenija Stanojevic
6986dd112b9SKsenija Stanojevic /* Allocate the IIO device. */
6996dd112b9SKsenija Stanojevic iio = devm_iio_device_alloc(dev, sizeof(*adc));
7006dd112b9SKsenija Stanojevic if (!iio) {
7016dd112b9SKsenija Stanojevic dev_err(dev, "Failed to allocate IIO device\n");
7026dd112b9SKsenija Stanojevic return -ENOMEM;
7036dd112b9SKsenija Stanojevic }
7046dd112b9SKsenija Stanojevic
7056dd112b9SKsenija Stanojevic adc = iio_priv(iio);
7066dd112b9SKsenija Stanojevic adc->lradc = lradc;
7076dd112b9SKsenija Stanojevic adc->dev = dev;
7086dd112b9SKsenija Stanojevic
7096dd112b9SKsenija Stanojevic iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
7101454e15bSWei Yongjun if (!iores)
7111454e15bSWei Yongjun return -EINVAL;
7121454e15bSWei Yongjun
7136dd112b9SKsenija Stanojevic adc->base = devm_ioremap(dev, iores->start, resource_size(iores));
7141454e15bSWei Yongjun if (!adc->base)
7151454e15bSWei Yongjun return -ENOMEM;
7166dd112b9SKsenija Stanojevic
7176dd112b9SKsenija Stanojevic init_completion(&adc->completion);
7186dd112b9SKsenija Stanojevic spin_lock_init(&adc->lock);
7196dd112b9SKsenija Stanojevic
7206dd112b9SKsenija Stanojevic platform_set_drvdata(pdev, iio);
7216dd112b9SKsenija Stanojevic
7226dd112b9SKsenija Stanojevic iio->name = pdev->name;
7236dd112b9SKsenija Stanojevic iio->dev.of_node = dev->parent->of_node;
7246dd112b9SKsenija Stanojevic iio->info = &mxs_lradc_adc_iio_info;
7256dd112b9SKsenija Stanojevic iio->modes = INDIO_DIRECT_MODE;
7266dd112b9SKsenija Stanojevic
7276dd112b9SKsenija Stanojevic if (lradc->soc == IMX23_LRADC) {
7286dd112b9SKsenija Stanojevic iio->channels = mx23_lradc_chan_spec;
7296dd112b9SKsenija Stanojevic iio->num_channels = ARRAY_SIZE(mx23_lradc_chan_spec);
7306dd112b9SKsenija Stanojevic irq_name = mx23_lradc_adc_irq_names;
7316dd112b9SKsenija Stanojevic n = ARRAY_SIZE(mx23_lradc_adc_irq_names);
7326dd112b9SKsenija Stanojevic } else {
7336dd112b9SKsenija Stanojevic iio->channels = mx28_lradc_chan_spec;
7346dd112b9SKsenija Stanojevic iio->num_channels = ARRAY_SIZE(mx28_lradc_chan_spec);
7356dd112b9SKsenija Stanojevic irq_name = mx28_lradc_adc_irq_names;
7366dd112b9SKsenija Stanojevic n = ARRAY_SIZE(mx28_lradc_adc_irq_names);
7376dd112b9SKsenija Stanojevic }
7386dd112b9SKsenija Stanojevic
7396dd112b9SKsenija Stanojevic ret = stmp_reset_block(adc->base);
7406dd112b9SKsenija Stanojevic if (ret)
7416dd112b9SKsenija Stanojevic return ret;
7426dd112b9SKsenija Stanojevic
7436dd112b9SKsenija Stanojevic for (i = 0; i < n; i++) {
7446dd112b9SKsenija Stanojevic irq = platform_get_irq_byname(pdev, irq_name[i]);
7456dd112b9SKsenija Stanojevic if (irq < 0)
7466dd112b9SKsenija Stanojevic return irq;
7476dd112b9SKsenija Stanojevic
7486dd112b9SKsenija Stanojevic virq = irq_of_parse_and_map(dev->parent->of_node, irq);
7496dd112b9SKsenija Stanojevic
7506dd112b9SKsenija Stanojevic ret = devm_request_irq(dev, virq, mxs_lradc_adc_handle_irq,
7516dd112b9SKsenija Stanojevic 0, irq_name[i], iio);
7526dd112b9SKsenija Stanojevic if (ret)
7536dd112b9SKsenija Stanojevic return ret;
7546dd112b9SKsenija Stanojevic }
7556dd112b9SKsenija Stanojevic
7566dd112b9SKsenija Stanojevic ret = mxs_lradc_adc_trigger_init(iio);
7576dd112b9SKsenija Stanojevic if (ret)
75827b2ed5bSJiakai Luo return ret;
7596dd112b9SKsenija Stanojevic
7606dd112b9SKsenija Stanojevic ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
7616dd112b9SKsenija Stanojevic &mxs_lradc_adc_trigger_handler,
7626dd112b9SKsenija Stanojevic &mxs_lradc_adc_buffer_ops);
7636dd112b9SKsenija Stanojevic if (ret)
76427b2ed5bSJiakai Luo goto err_trig;
7656dd112b9SKsenija Stanojevic
7666dd112b9SKsenija Stanojevic adc->vref_mv = mxs_lradc_adc_vref_mv[lradc->soc];
7676dd112b9SKsenija Stanojevic
7686dd112b9SKsenija Stanojevic /* Populate available ADC input ranges */
7696dd112b9SKsenija Stanojevic for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
7706dd112b9SKsenija Stanojevic for (s = 0; s < ARRAY_SIZE(adc->scale_avail[i]); s++) {
7716dd112b9SKsenija Stanojevic /*
7726dd112b9SKsenija Stanojevic * [s=0] = optional divider by two disabled (default)
7736dd112b9SKsenija Stanojevic * [s=1] = optional divider by two enabled
7746dd112b9SKsenija Stanojevic *
7756dd112b9SKsenija Stanojevic * The scale is calculated by doing:
7766dd112b9SKsenija Stanojevic * Vref >> (realbits - s)
7776dd112b9SKsenija Stanojevic * which multiplies by two on the second component
7786dd112b9SKsenija Stanojevic * of the array.
7796dd112b9SKsenija Stanojevic */
7806dd112b9SKsenija Stanojevic scale_uv = ((u64)adc->vref_mv[i] * 100000000) >>
7816dd112b9SKsenija Stanojevic (LRADC_RESOLUTION - s);
7826dd112b9SKsenija Stanojevic adc->scale_avail[i][s].nano =
7836dd112b9SKsenija Stanojevic do_div(scale_uv, 100000000) * 10;
7846dd112b9SKsenija Stanojevic adc->scale_avail[i][s].integer = scale_uv;
7856dd112b9SKsenija Stanojevic }
7866dd112b9SKsenija Stanojevic }
7876dd112b9SKsenija Stanojevic
7886dd112b9SKsenija Stanojevic /* Configure the hardware. */
7896dd112b9SKsenija Stanojevic mxs_lradc_adc_hw_init(adc);
7906dd112b9SKsenija Stanojevic
7916dd112b9SKsenija Stanojevic /* Register IIO device. */
7926dd112b9SKsenija Stanojevic ret = iio_device_register(iio);
7936dd112b9SKsenija Stanojevic if (ret) {
7946dd112b9SKsenija Stanojevic dev_err(dev, "Failed to register IIO device\n");
7956dd112b9SKsenija Stanojevic goto err_dev;
7966dd112b9SKsenija Stanojevic }
7976dd112b9SKsenija Stanojevic
7986dd112b9SKsenija Stanojevic return 0;
7996dd112b9SKsenija Stanojevic
8006dd112b9SKsenija Stanojevic err_dev:
8016dd112b9SKsenija Stanojevic mxs_lradc_adc_hw_stop(adc);
8026dd112b9SKsenija Stanojevic iio_triggered_buffer_cleanup(iio);
80327b2ed5bSJiakai Luo err_trig:
80427b2ed5bSJiakai Luo mxs_lradc_adc_trigger_remove(iio);
8056dd112b9SKsenija Stanojevic return ret;
8066dd112b9SKsenija Stanojevic }
8076dd112b9SKsenija Stanojevic
mxs_lradc_adc_remove(struct platform_device * pdev)808a72e156fSUwe Kleine-König static void mxs_lradc_adc_remove(struct platform_device *pdev)
8096dd112b9SKsenija Stanojevic {
8106dd112b9SKsenija Stanojevic struct iio_dev *iio = platform_get_drvdata(pdev);
8116dd112b9SKsenija Stanojevic struct mxs_lradc_adc *adc = iio_priv(iio);
8126dd112b9SKsenija Stanojevic
8136dd112b9SKsenija Stanojevic iio_device_unregister(iio);
8146dd112b9SKsenija Stanojevic mxs_lradc_adc_hw_stop(adc);
8156dd112b9SKsenija Stanojevic iio_triggered_buffer_cleanup(iio);
81627b2ed5bSJiakai Luo mxs_lradc_adc_trigger_remove(iio);
8176dd112b9SKsenija Stanojevic }
8186dd112b9SKsenija Stanojevic
8196dd112b9SKsenija Stanojevic static struct platform_driver mxs_lradc_adc_driver = {
8206dd112b9SKsenija Stanojevic .driver = {
8216dd112b9SKsenija Stanojevic .name = "mxs-lradc-adc",
8226dd112b9SKsenija Stanojevic },
8236dd112b9SKsenija Stanojevic .probe = mxs_lradc_adc_probe,
8246a9262edSUwe Kleine-König .remove = mxs_lradc_adc_remove,
8256dd112b9SKsenija Stanojevic };
8266dd112b9SKsenija Stanojevic module_platform_driver(mxs_lradc_adc_driver);
8276dd112b9SKsenija Stanojevic
8286dd112b9SKsenija Stanojevic MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
8296dd112b9SKsenija Stanojevic MODULE_DESCRIPTION("Freescale MXS LRADC driver general purpose ADC driver");
8306dd112b9SKsenija Stanojevic MODULE_LICENSE("GPL");
8316dd112b9SKsenija Stanojevic MODULE_ALIAS("platform:mxs-lradc-adc");
832