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