]> Pileus Git - ~andy/linux/blobdiff - drivers/staging/iio/adc/mxs-lradc.c
staging: Remove OOM message after input_allocate_device
[~andy/linux] / drivers / staging / iio / adc / mxs-lradc.c
index a08c1736458b4f2cfab9af87c408a65e430d8020..aeae76b77be5732eebd2756a8c07353f99d4be0c 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/completion.h>
 #include <linux/delay.h>
 #include <linux/input.h>
+#include <linux/clk.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
@@ -129,11 +130,24 @@ enum mxs_lradc_ts {
        MXS_LRADC_TOUCHSCREEN_5WIRE,
 };
 
+/*
+ * Touchscreen handling
+ */
+enum lradc_ts_plate {
+       LRADC_TOUCH = 0,
+       LRADC_SAMPLE_X,
+       LRADC_SAMPLE_Y,
+       LRADC_SAMPLE_PRESSURE,
+       LRADC_SAMPLE_VALID,
+};
+
 struct mxs_lradc {
        struct device           *dev;
        void __iomem            *base;
        int                     irq[13];
 
+       struct clk              *clk;
+
        uint32_t                *buffer;
        struct iio_trigger      *trig;
 
@@ -169,32 +183,63 @@ struct mxs_lradc {
 #define CHAN_MASK_TOUCHSCREEN_4WIRE    (0xf << 2)
 #define CHAN_MASK_TOUCHSCREEN_5WIRE    (0x1f << 2)
        enum mxs_lradc_ts       use_touchscreen;
-       bool                    stop_touchscreen;
        bool                    use_touchbutton;
 
        struct input_dev        *ts_input;
-       struct work_struct      ts_work;
+
+       enum mxs_lradc_id       soc;
+       enum lradc_ts_plate     cur_plate; /* statemachine */
+       bool                    ts_valid;
+       unsigned                ts_x_pos;
+       unsigned                ts_y_pos;
+       unsigned                ts_pressure;
+
+       /* handle touchscreen's physical behaviour */
+       /* samples per coordinate */
+       unsigned                over_sample_cnt;
+       /* time clocks between samples */
+       unsigned                over_sample_delay;
+       /* time in clocks to wait after the plates where switched */
+       unsigned                settling_delay;
 };
 
 #define        LRADC_CTRL0                             0x00
-#define        LRADC_CTRL0_TOUCH_DETECT_ENABLE         (1 << 23)
-#define        LRADC_CTRL0_TOUCH_SCREEN_TYPE           (1 << 22)
-#define        LRADC_CTRL0_YNNSW       /* YM */        (1 << 21)
-#define        LRADC_CTRL0_YPNSW       /* YP */        (1 << 20)
-#define        LRADC_CTRL0_YPPSW       /* YP */        (1 << 19)
-#define        LRADC_CTRL0_XNNSW       /* XM */        (1 << 18)
-#define        LRADC_CTRL0_XNPSW       /* XM */        (1 << 17)
-#define        LRADC_CTRL0_XPPSW       /* XP */        (1 << 16)
-#define        LRADC_CTRL0_PLATE_MASK                  (0x3f << 16)
+# define LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE  (1 << 23)
+# define LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE    (1 << 22)
+# define LRADC_CTRL0_MX28_YNNSW        /* YM */        (1 << 21)
+# define LRADC_CTRL0_MX28_YPNSW        /* YP */        (1 << 20)
+# define LRADC_CTRL0_MX28_YPPSW        /* YP */        (1 << 19)
+# define LRADC_CTRL0_MX28_XNNSW        /* XM */        (1 << 18)
+# define LRADC_CTRL0_MX28_XNPSW        /* XM */        (1 << 17)
+# define LRADC_CTRL0_MX28_XPPSW        /* XP */        (1 << 16)
+
+# define LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE  (1 << 20)
+# define LRADC_CTRL0_MX23_YM                   (1 << 19)
+# define LRADC_CTRL0_MX23_XM                   (1 << 18)
+# define LRADC_CTRL0_MX23_YP                   (1 << 17)
+# define LRADC_CTRL0_MX23_XP                   (1 << 16)
+
+# define LRADC_CTRL0_MX28_PLATE_MASK \
+               (LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE | \
+               LRADC_CTRL0_MX28_YNNSW | LRADC_CTRL0_MX28_YPNSW | \
+               LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW | \
+               LRADC_CTRL0_MX28_XNPSW | LRADC_CTRL0_MX28_XPPSW)
+
+# define LRADC_CTRL0_MX23_PLATE_MASK \
+               (LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE | \
+               LRADC_CTRL0_MX23_YM | LRADC_CTRL0_MX23_XM | \
+               LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XP)
 
 #define        LRADC_CTRL1                             0x10
 #define        LRADC_CTRL1_TOUCH_DETECT_IRQ_EN         (1 << 24)
 #define        LRADC_CTRL1_LRADC_IRQ_EN(n)             (1 << ((n) + 16))
-#define        LRADC_CTRL1_LRADC_IRQ_EN_MASK           (0x1fff << 16)
+#define        LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK      (0x1fff << 16)
+#define        LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK      (0x01ff << 16)
 #define        LRADC_CTRL1_LRADC_IRQ_EN_OFFSET         16
 #define        LRADC_CTRL1_TOUCH_DETECT_IRQ            (1 << 8)
 #define        LRADC_CTRL1_LRADC_IRQ(n)                (1 << (n))
-#define        LRADC_CTRL1_LRADC_IRQ_MASK              0x1fff
+#define        LRADC_CTRL1_MX28_LRADC_IRQ_MASK         0x1fff
+#define        LRADC_CTRL1_MX23_LRADC_IRQ_MASK         0x01ff
 #define        LRADC_CTRL1_LRADC_IRQ_OFFSET            0
 
 #define        LRADC_CTRL2                             0x20
@@ -207,19 +252,33 @@ struct mxs_lradc {
 #define        LRADC_CH_ACCUMULATE                     (1 << 29)
 #define        LRADC_CH_NUM_SAMPLES_MASK               (0x1f << 24)
 #define        LRADC_CH_NUM_SAMPLES_OFFSET             24
+#define        LRADC_CH_NUM_SAMPLES(x) \
+                               ((x) << LRADC_CH_NUM_SAMPLES_OFFSET)
 #define        LRADC_CH_VALUE_MASK                     0x3ffff
 #define        LRADC_CH_VALUE_OFFSET                   0
 
 #define        LRADC_DELAY(n)                          (0xd0 + (0x10 * (n)))
 #define        LRADC_DELAY_TRIGGER_LRADCS_MASK         (0xff << 24)
 #define        LRADC_DELAY_TRIGGER_LRADCS_OFFSET       24
+#define        LRADC_DELAY_TRIGGER(x) \
+                               (((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \
+                               LRADC_DELAY_TRIGGER_LRADCS_MASK)
 #define        LRADC_DELAY_KICK                        (1 << 20)
 #define        LRADC_DELAY_TRIGGER_DELAYS_MASK         (0xf << 16)
 #define        LRADC_DELAY_TRIGGER_DELAYS_OFFSET       16
+#define        LRADC_DELAY_TRIGGER_DELAYS(x) \
+                               (((x) << LRADC_DELAY_TRIGGER_DELAYS_OFFSET) & \
+                               LRADC_DELAY_TRIGGER_DELAYS_MASK)
 #define        LRADC_DELAY_LOOP_COUNT_MASK             (0x1f << 11)
 #define        LRADC_DELAY_LOOP_COUNT_OFFSET           11
+#define        LRADC_DELAY_LOOP(x) \
+                               (((x) << LRADC_DELAY_LOOP_COUNT_OFFSET) & \
+                               LRADC_DELAY_LOOP_COUNT_MASK)
 #define        LRADC_DELAY_DELAY_MASK                  0x7ff
 #define        LRADC_DELAY_DELAY_OFFSET                0
+#define        LRADC_DELAY_DELAY(x) \
+                               (((x) << LRADC_DELAY_DELAY_OFFSET) & \
+                               LRADC_DELAY_DELAY_MASK)
 
 #define        LRADC_CTRL4                             0x140
 #define        LRADC_CTRL4_LRADCSELECT_MASK(n)         (0xf << ((n) * 4))
@@ -228,6 +287,475 @@ struct mxs_lradc {
 #define LRADC_RESOLUTION                       12
 #define LRADC_SINGLE_SAMPLE_MASK               ((1 << LRADC_RESOLUTION) - 1)
 
+static void mxs_lradc_reg_set(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+       writel(val, lradc->base + reg + STMP_OFFSET_REG_SET);
+}
+
+static void mxs_lradc_reg_clear(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+       writel(val, lradc->base + reg + STMP_OFFSET_REG_CLR);
+}
+
+static void mxs_lradc_reg_wrt(struct mxs_lradc *lradc, u32 val, u32 reg)
+{
+       writel(val, lradc->base + reg);
+}
+
+static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc)
+{
+       if (lradc->soc == IMX23_LRADC)
+               return LRADC_CTRL0_MX23_PLATE_MASK;
+       else
+               return LRADC_CTRL0_MX28_PLATE_MASK;
+}
+
+static u32 mxs_lradc_irq_en_mask(struct mxs_lradc *lradc)
+{
+       if (lradc->soc == IMX23_LRADC)
+               return LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK;
+       else
+               return LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK;
+}
+
+static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc)
+{
+       if (lradc->soc == IMX23_LRADC)
+               return LRADC_CTRL1_MX23_LRADC_IRQ_MASK;
+       else
+               return LRADC_CTRL1_MX28_LRADC_IRQ_MASK;
+}
+
+static u32 mxs_lradc_touch_detect_bit(struct mxs_lradc *lradc)
+{
+       if (lradc->soc == IMX23_LRADC)
+               return LRADC_CTRL0_MX23_TOUCH_DETECT_ENABLE;
+       else
+               return LRADC_CTRL0_MX28_TOUCH_DETECT_ENABLE;
+}
+
+static u32 mxs_lradc_drive_x_plate(struct mxs_lradc *lradc)
+{
+       if (lradc->soc == IMX23_LRADC)
+               return LRADC_CTRL0_MX23_XP | LRADC_CTRL0_MX23_XM;
+       else
+               return LRADC_CTRL0_MX28_XPPSW | LRADC_CTRL0_MX28_XNNSW;
+}
+
+static u32 mxs_lradc_drive_y_plate(struct mxs_lradc *lradc)
+{
+       if (lradc->soc == IMX23_LRADC)
+               return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_YM;
+       else
+               return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_YNNSW;
+}
+
+static u32 mxs_lradc_drive_pressure(struct mxs_lradc *lradc)
+{
+       if (lradc->soc == IMX23_LRADC)
+               return LRADC_CTRL0_MX23_YP | LRADC_CTRL0_MX23_XM;
+       else
+               return LRADC_CTRL0_MX28_YPPSW | LRADC_CTRL0_MX28_XNNSW;
+}
+
+static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
+{
+       return !!(readl(lradc->base + LRADC_STATUS) &
+                                       LRADC_STATUS_TOUCH_DETECT_RAW);
+}
+
+static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
+{
+       /*
+        * prepare for oversampling conversion
+        *
+        * from the datasheet:
+        * "The ACCUMULATE bit in the appropriate channel register
+        * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+        * otherwise, the IRQs will not fire."
+        */
+       mxs_lradc_reg_wrt(lradc, LRADC_CH_ACCUMULATE |
+                       LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1),
+                       LRADC_CH(ch));
+
+       /* from the datasheet:
+        * "Software must clear this register in preparation for a
+        * multi-cycle accumulation.
+        */
+       mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch));
+
+       /* prepare the delay/loop unit according to the oversampling count */
+       mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch) |
+               LRADC_DELAY_TRIGGER_DELAYS(0) |
+               LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) |
+               LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
+                       LRADC_DELAY(3));
+
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) |
+                       LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
+                       LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
+
+       /* wake us again, when the complete conversion is done */
+       mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch), LRADC_CTRL1);
+       /*
+        * after changing the touchscreen plates setting
+        * the signals need some initial time to settle. Start the
+        * SoC's delay unit and start the conversion later
+        * and automatically.
+        */
+       mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
+               LRADC_DELAY_TRIGGER_DELAYS(1 << 3) | /* trigger DELAY unit#3 */
+               LRADC_DELAY_KICK |
+               LRADC_DELAY_DELAY(lradc->settling_delay),
+                       LRADC_DELAY(2));
+}
+
+/*
+ * Pressure detection is special:
+ * We want to do both required measurements for the pressure detection in
+ * one turn. Use the hardware features to chain both conversions and let the
+ * hardware report one interrupt if both conversions are done
+ */
+static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1,
+                                                       unsigned ch2)
+{
+       u32 reg;
+
+       /*
+        * prepare for oversampling conversion
+        *
+        * from the datasheet:
+        * "The ACCUMULATE bit in the appropriate channel register
+        * HW_LRADC_CHn must be set to 1 if NUM_SAMPLES is greater then 0;
+        * otherwise, the IRQs will not fire."
+        */
+       reg = LRADC_CH_ACCUMULATE |
+               LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1);
+       mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
+       mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
+
+       /* from the datasheet:
+        * "Software must clear this register in preparation for a
+        * multi-cycle accumulation.
+        */
+       mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch1));
+       mxs_lradc_reg_clear(lradc, LRADC_CH_VALUE_MASK, LRADC_CH(ch2));
+
+       /* prepare the delay/loop unit according to the oversampling count */
+       mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << ch1) |
+               LRADC_DELAY_TRIGGER(1 << ch2) | /* start both channels */
+               LRADC_DELAY_TRIGGER_DELAYS(0) |
+               LRADC_DELAY_LOOP(lradc->over_sample_cnt - 1) |
+               LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
+                                       LRADC_DELAY(3));
+
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) |
+                       LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
+                       LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
+
+       /* wake us again, when the conversions are done */
+       mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch2), LRADC_CTRL1);
+       /*
+        * after changing the touchscreen plates setting
+        * the signals need some initial time to settle. Start the
+        * SoC's delay unit and start the conversion later
+        * and automatically.
+        */
+       mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(0) | /* don't trigger ADC */
+               LRADC_DELAY_TRIGGER_DELAYS(1 << 3) | /* trigger DELAY unit#3 */
+               LRADC_DELAY_KICK |
+               LRADC_DELAY_DELAY(lradc->settling_delay), LRADC_DELAY(2));
+}
+
+static unsigned mxs_lradc_read_raw_channel(struct mxs_lradc *lradc,
+                                                       unsigned channel)
+{
+       u32 reg;
+       unsigned num_samples, val;
+
+       reg = readl(lradc->base + LRADC_CH(channel));
+       if (reg & LRADC_CH_ACCUMULATE)
+               num_samples = lradc->over_sample_cnt;
+       else
+               num_samples = 1;
+
+       val = (reg & LRADC_CH_VALUE_MASK) >> LRADC_CH_VALUE_OFFSET;
+       return val / num_samples;
+}
+
+static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc,
+                                               unsigned ch1, unsigned ch2)
+{
+       u32 reg, mask;
+       unsigned pressure, m1, m2;
+
+       mask = LRADC_CTRL1_LRADC_IRQ(ch1) | LRADC_CTRL1_LRADC_IRQ(ch2);
+       reg = readl(lradc->base + LRADC_CTRL1) & mask;
+
+       while (reg != mask) {
+               reg = readl(lradc->base + LRADC_CTRL1) & mask;
+               dev_dbg(lradc->dev, "One channel is still busy: %X\n", reg);
+       }
+
+       m1 = mxs_lradc_read_raw_channel(lradc, ch1);
+       m2 = mxs_lradc_read_raw_channel(lradc, ch2);
+
+       if (m2 == 0) {
+               dev_warn(lradc->dev, "Cannot calculate pressure\n");
+               return 1 << (LRADC_RESOLUTION - 1);
+       }
+
+       /* simply scale the value from 0 ... max ADC resolution */
+       pressure = m1;
+       pressure *= (1 << LRADC_RESOLUTION);
+       pressure /= m2;
+
+       dev_dbg(lradc->dev, "Pressure = %u\n", pressure);
+       return pressure;
+}
+
+#define TS_CH_XP 2
+#define TS_CH_YP 3
+#define TS_CH_XM 4
+#define TS_CH_YM 5
+
+static int mxs_lradc_read_ts_channel(struct mxs_lradc *lradc)
+{
+       u32 reg;
+       int val;
+
+       reg = readl(lradc->base + LRADC_CTRL1);
+
+       /* only channels 3 to 5 are of interest here */
+       if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YP)) {
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YP) |
+                       LRADC_CTRL1_LRADC_IRQ(TS_CH_YP), LRADC_CTRL1);
+               val = mxs_lradc_read_raw_channel(lradc, TS_CH_YP);
+       } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_XM)) {
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_XM) |
+                       LRADC_CTRL1_LRADC_IRQ(TS_CH_XM), LRADC_CTRL1);
+               val = mxs_lradc_read_raw_channel(lradc, TS_CH_XM);
+       } else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YM)) {
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YM) |
+                       LRADC_CTRL1_LRADC_IRQ(TS_CH_YM), LRADC_CTRL1);
+               val = mxs_lradc_read_raw_channel(lradc, TS_CH_YM);
+       } else {
+               return -EIO;
+       }
+
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
+
+       return val;
+}
+
+/*
+ * YP(open)--+-------------+
+ *           |             |--+
+ *           |             |  |
+ *    YM(-)--+-------------+  |
+ *             +--------------+
+ *             |              |
+ *         XP(weak+)        XM(open)
+ *
+ * "weak+" means 200k Ohm VDDIO
+ * (-) means GND
+ */
+static void mxs_lradc_setup_touch_detection(struct mxs_lradc *lradc)
+{
+       /*
+        * In order to detect a touch event the 'touch detect enable' bit
+        * enables:
+        *  - a weak pullup to the X+ connector
+        *  - a strong ground at the Y- connector
+        */
+       mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+       mxs_lradc_reg_set(lradc, mxs_lradc_touch_detect_bit(lradc),
+                               LRADC_CTRL0);
+}
+
+/*
+ * YP(meas)--+-------------+
+ *           |             |--+
+ *           |             |  |
+ * YM(open)--+-------------+  |
+ *             +--------------+
+ *             |              |
+ *           XP(+)          XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc)
+{
+       mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+       mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
+
+       lradc->cur_plate = LRADC_SAMPLE_X;
+       mxs_lradc_setup_ts_channel(lradc, TS_CH_YP);
+}
+
+/*
+ *   YP(+)--+-------------+
+ *          |             |--+
+ *          |             |  |
+ *   YM(-)--+-------------+  |
+ *            +--------------+
+ *            |              |
+ *         XP(open)        XM(meas)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc)
+{
+       mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+       mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
+
+       lradc->cur_plate = LRADC_SAMPLE_Y;
+       mxs_lradc_setup_ts_channel(lradc, TS_CH_XM);
+}
+
+/*
+ *    YP(+)--+-------------+
+ *           |             |--+
+ *           |             |  |
+ * YM(meas)--+-------------+  |
+ *             +--------------+
+ *             |              |
+ *          XP(meas)        XM(-)
+ *
+ * (+) means here 1.85 V
+ * (-) means here GND
+ */
+static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc)
+{
+       mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+       mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
+
+       lradc->cur_plate = LRADC_SAMPLE_PRESSURE;
+       mxs_lradc_setup_ts_pressure(lradc, TS_CH_XP, TS_CH_YM);
+}
+
+static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
+{
+       mxs_lradc_setup_touch_detection(lradc);
+
+       lradc->cur_plate = LRADC_TOUCH;
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
+                               LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+       mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+}
+
+static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc)
+{
+       input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos);
+       input_report_abs(lradc->ts_input, ABS_Y, lradc->ts_y_pos);
+       input_report_abs(lradc->ts_input, ABS_PRESSURE, lradc->ts_pressure);
+       input_report_key(lradc->ts_input, BTN_TOUCH, 1);
+       input_sync(lradc->ts_input);
+}
+
+static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc)
+{
+       mxs_lradc_setup_touch_detection(lradc);
+       lradc->cur_plate = LRADC_SAMPLE_VALID;
+       /*
+        * start a dummy conversion to burn time to settle the signals
+        * note: we are not interested in the conversion's value
+        */
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(5));
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
+       mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1);
+       mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << 5) |
+               LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
+                       LRADC_DELAY(2));
+}
+
+/*
+ * in order to avoid false measurements, report only samples where
+ * the surface is still touched after the position measurement
+ */
+static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid)
+{
+       /* if it is still touched, report the sample */
+       if (valid && mxs_lradc_check_touch_event(lradc)) {
+               lradc->ts_valid = true;
+               mxs_lradc_report_ts_event(lradc);
+       }
+
+       /* if it is even still touched, continue with the next measurement */
+       if (mxs_lradc_check_touch_event(lradc)) {
+               mxs_lradc_prepare_y_pos(lradc);
+               return;
+       }
+
+       if (lradc->ts_valid) {
+               /* signal the release */
+               lradc->ts_valid = false;
+               input_report_key(lradc->ts_input, BTN_TOUCH, 0);
+               input_sync(lradc->ts_input);
+       }
+
+       /* if it is released, wait for the next touch via IRQ */
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1);
+       mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
+}
+
+/* touchscreen's state machine */
+static void mxs_lradc_handle_touch(struct mxs_lradc *lradc)
+{
+       int val;
+
+       switch (lradc->cur_plate) {
+       case LRADC_TOUCH:
+               /*
+                * start with the Y-pos, because it uses nearly the same plate
+                * settings like the touch detection
+                */
+               if (mxs_lradc_check_touch_event(lradc)) {
+                       mxs_lradc_reg_clear(lradc,
+                                       LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+                                       LRADC_CTRL1);
+                       mxs_lradc_prepare_y_pos(lradc);
+               }
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
+                                       LRADC_CTRL1);
+               return;
+
+       case LRADC_SAMPLE_Y:
+               val = mxs_lradc_read_ts_channel(lradc);
+               if (val < 0) {
+                       mxs_lradc_enable_touch_detection(lradc); /* re-start */
+                       return;
+               }
+               lradc->ts_y_pos = val;
+               mxs_lradc_prepare_x_pos(lradc);
+               return;
+
+       case LRADC_SAMPLE_X:
+               val = mxs_lradc_read_ts_channel(lradc);
+               if (val < 0) {
+                       mxs_lradc_enable_touch_detection(lradc); /* re-start */
+                       return;
+               }
+               lradc->ts_x_pos = val;
+               mxs_lradc_prepare_pressure(lradc);
+               return;
+
+       case LRADC_SAMPLE_PRESSURE:
+               lradc->ts_pressure =
+                       mxs_lradc_read_ts_pressure(lradc, TS_CH_XP, TS_CH_YM);
+               mxs_lradc_complete_touch_event(lradc);
+               return;
+
+       case LRADC_SAMPLE_VALID:
+               val = mxs_lradc_read_ts_channel(lradc); /* ignore the value */
+               mxs_lradc_finish_touch_event(lradc, 1);
+               break;
+       }
+}
+
 /*
  * Raw I/O operations
  */
@@ -262,21 +790,20 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
         * Virtual channel 0 is always used here as the others are always not
         * used if doing raw sampling.
         */
-       writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
-       writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+       if (lradc->soc == IMX28_LRADC)
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
+                       LRADC_CTRL1);
+       mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
 
        /* Clean the slot's previous content, then set new one. */
-       writel(LRADC_CTRL4_LRADCSELECT_MASK(0),
-               lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
-       writel(chan->channel, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(0), LRADC_CTRL4);
+       mxs_lradc_reg_set(lradc, chan->channel, LRADC_CTRL4);
 
-       writel(0, lradc->base + LRADC_CH(0));
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(0));
 
        /* Enable the IRQ and start sampling the channel. */
-       writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
-       writel(1 << 0, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+       mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
+       mxs_lradc_reg_set(lradc, 1 << 0, LRADC_CTRL0);
 
        /* Wait for completion on the channel, 1 second max. */
        ret = wait_for_completion_killable_timeout(&lradc->completion, HZ);
@@ -290,8 +817,7 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
        ret = IIO_VAL_INT;
 
 err:
-       writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(0), LRADC_CTRL1);
 
        mutex_unlock(&lradc->lock);
 
@@ -303,220 +829,33 @@ static const struct iio_info mxs_lradc_iio_info = {
        .read_raw               = mxs_lradc_read_raw,
 };
 
-/*
- * Touchscreen handling
- */
-enum lradc_ts_plate {
-       LRADC_SAMPLE_X,
-       LRADC_SAMPLE_Y,
-       LRADC_SAMPLE_PRESSURE,
-};
-
-static int mxs_lradc_ts_touched(struct mxs_lradc *lradc)
-{
-       uint32_t reg;
-
-       /* Enable touch detection. */
-       writel(LRADC_CTRL0_PLATE_MASK,
-               lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
-       writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
-               lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
-
-       msleep(LRADC_TS_SAMPLE_DELAY_MS);
-
-       reg = readl(lradc->base + LRADC_STATUS);
-
-       return reg & LRADC_STATUS_TOUCH_DETECT_RAW;
-}
-
-static int32_t mxs_lradc_ts_sample(struct mxs_lradc *lradc,
-                               enum lradc_ts_plate plate, int change)
-{
-       unsigned long delay, jiff;
-       uint32_t reg, ctrl0 = 0, chan = 0;
-       /* The touchscreen always uses CTRL4 slot #7. */
-       const uint8_t slot = 7;
-       uint32_t val;
-
-       /*
-        * There are three correct configurations of the controller sampling
-        * the touchscreen, each of these configuration provides different
-        * information from the touchscreen.
-        *
-        * The following table describes the sampling configurations:
-        * +-------------+-------+-------+-------+
-        * | Wire \ Axis |   X   |   Y   |   Z   |
-        * +---------------------+-------+-------+
-        * |   X+ (CH2)  |   HI  |   TS  |   TS  |
-        * +-------------+-------+-------+-------+
-        * |   X- (CH4)  |   LO  |   SH  |   HI  |
-        * +-------------+-------+-------+-------+
-        * |   Y+ (CH3)  |   SH  |   HI  |   HI  |
-        * +-------------+-------+-------+-------+
-        * |   Y- (CH5)  |   TS  |   LO  |   SH  |
-        * +-------------+-------+-------+-------+
-        *
-        * HI ... strong '1'  ; LO ... strong '0'
-        * SH ... sample here ; TS ... tri-state
-        *
-        * There are a few other ways of obtaining the Z coordinate
-        * (aka. pressure), but the one in the table seems to be the
-        * most reliable one.
-        */
-       switch (plate) {
-       case LRADC_SAMPLE_X:
-               ctrl0 = LRADC_CTRL0_XPPSW | LRADC_CTRL0_XNNSW;
-               chan = 3;
-               break;
-       case LRADC_SAMPLE_Y:
-               ctrl0 = LRADC_CTRL0_YPPSW | LRADC_CTRL0_YNNSW;
-               chan = 4;
-               break;
-       case LRADC_SAMPLE_PRESSURE:
-               ctrl0 = LRADC_CTRL0_YPPSW | LRADC_CTRL0_XNNSW;
-               chan = 5;
-               break;
-       }
-
-       if (change) {
-               writel(LRADC_CTRL0_PLATE_MASK,
-                       lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
-               writel(ctrl0, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
-
-               writel(LRADC_CTRL4_LRADCSELECT_MASK(slot),
-                       lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
-               writel(chan << LRADC_CTRL4_LRADCSELECT_OFFSET(slot),
-                       lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
-       }
-
-       writel(0xffffffff, lradc->base + LRADC_CH(slot) + STMP_OFFSET_REG_CLR);
-       writel(1 << slot, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
-
-       delay = jiffies + msecs_to_jiffies(LRADC_TS_SAMPLE_DELAY_MS);
-       do {
-               jiff = jiffies;
-               reg = readl_relaxed(lradc->base + LRADC_CTRL1);
-               if (reg & LRADC_CTRL1_LRADC_IRQ(slot))
-                       break;
-       } while (time_before(jiff, delay));
-
-       writel(LRADC_CTRL1_LRADC_IRQ(slot),
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
-
-       if (time_after_eq(jiff, delay))
-               return -ETIMEDOUT;
-
-       val = readl(lradc->base + LRADC_CH(slot));
-       val &= LRADC_CH_VALUE_MASK;
-
-       return val;
-}
-
-static int32_t mxs_lradc_ts_sample_filter(struct mxs_lradc *lradc,
-                               enum lradc_ts_plate plate)
-{
-       int32_t val, tot = 0;
-       int i;
-
-       val = mxs_lradc_ts_sample(lradc, plate, 1);
-
-       /* Delay a bit so the touchscreen is stable. */
-       mdelay(2);
-
-       for (i = 0; i < LRADC_TS_SAMPLE_AMOUNT; i++) {
-               val = mxs_lradc_ts_sample(lradc, plate, 0);
-               tot += val;
-       }
-
-       return tot / LRADC_TS_SAMPLE_AMOUNT;
-}
-
-static void mxs_lradc_ts_work(struct work_struct *ts_work)
-{
-       struct mxs_lradc *lradc = container_of(ts_work,
-                               struct mxs_lradc, ts_work);
-       int val_x, val_y, val_p;
-       bool valid = false;
-
-       while (mxs_lradc_ts_touched(lradc)) {
-               /* Disable touch detector so we can sample the touchscreen. */
-               writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
-                       lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
-
-               if (likely(valid)) {
-                       input_report_abs(lradc->ts_input, ABS_X, val_x);
-                       input_report_abs(lradc->ts_input, ABS_Y, val_y);
-                       input_report_abs(lradc->ts_input, ABS_PRESSURE, val_p);
-                       input_report_key(lradc->ts_input, BTN_TOUCH, 1);
-                       input_sync(lradc->ts_input);
-               }
-
-               valid = false;
-
-               val_x = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_X);
-               if (val_x < 0)
-                       continue;
-               val_y = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_Y);
-               if (val_y < 0)
-                       continue;
-               val_p = mxs_lradc_ts_sample_filter(lradc, LRADC_SAMPLE_PRESSURE);
-               if (val_p < 0)
-                       continue;
-
-               valid = true;
-       }
-
-       input_report_abs(lradc->ts_input, ABS_PRESSURE, 0);
-       input_report_key(lradc->ts_input, BTN_TOUCH, 0);
-       input_sync(lradc->ts_input);
-
-       /* Do not restart the TS IRQ if the driver is shutting down. */
-       if (lradc->stop_touchscreen)
-               return;
-
-       /* Restart the touchscreen interrupts. */
-       writel(LRADC_CTRL1_TOUCH_DETECT_IRQ,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
-       writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
-}
-
 static int mxs_lradc_ts_open(struct input_dev *dev)
 {
        struct mxs_lradc *lradc = input_get_drvdata(dev);
 
-       /* The touchscreen is starting. */
-       lradc->stop_touchscreen = false;
-
        /* Enable the touch-detect circuitry. */
-       writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
-               lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
-
-       /* Enable the touch-detect IRQ. */
-       writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+       mxs_lradc_enable_touch_detection(lradc);
 
        return 0;
 }
 
-static void mxs_lradc_ts_close(struct input_dev *dev)
+static void mxs_lradc_disable_ts(struct mxs_lradc *lradc)
 {
-       struct mxs_lradc *lradc = input_get_drvdata(dev);
+       /* stop all interrupts from firing */
+       mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
+               LRADC_CTRL1_LRADC_IRQ_EN(2) | LRADC_CTRL1_LRADC_IRQ_EN(3) |
+               LRADC_CTRL1_LRADC_IRQ_EN(4) | LRADC_CTRL1_LRADC_IRQ_EN(5),
+               LRADC_CTRL1);
 
-       /* Indicate the touchscreen is stopping. */
-       lradc->stop_touchscreen = true;
-       mb();
-
-       /* Wait until touchscreen thread finishes any possible remnants. */
-       cancel_work_sync(&lradc->ts_work);
+       /* Power-down touchscreen touch-detect circuitry. */
+       mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
+}
 
-       /* Disable touchscreen touch-detect IRQ. */
-       writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+static void mxs_lradc_ts_close(struct input_dev *dev)
+{
+       struct mxs_lradc *lradc = input_get_drvdata(dev);
 
-       /* Power-down touchscreen touch-detect circuitry. */
-       writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
-               lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+       mxs_lradc_disable_ts(lradc);
 }
 
 static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
@@ -529,10 +868,8 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
                return 0;
 
        input = input_allocate_device();
-       if (!input) {
-               dev_err(dev, "Failed to allocate TS device!\n");
+       if (!input)
                return -ENOMEM;
-       }
 
        input->name = DRIVER_NAME;
        input->id.bustype = BUS_HOST;
@@ -562,8 +899,7 @@ static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc)
        if (!lradc->use_touchscreen)
                return;
 
-       cancel_work_sync(&lradc->ts_work);
-
+       mxs_lradc_disable_ts(lradc);
        input_unregister_device(lradc->ts_input);
 }
 
@@ -576,31 +912,24 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
        struct mxs_lradc *lradc = iio_priv(iio);
        unsigned long reg = readl(lradc->base + LRADC_CTRL1);
        const uint32_t ts_irq_mask =
-               LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
-               LRADC_CTRL1_TOUCH_DETECT_IRQ;
+               LRADC_CTRL1_TOUCH_DETECT_IRQ |
+               LRADC_CTRL1_LRADC_IRQ(2) |
+               LRADC_CTRL1_LRADC_IRQ(3) |
+               LRADC_CTRL1_LRADC_IRQ(4) |
+               LRADC_CTRL1_LRADC_IRQ(5);
 
-       if (!(reg & LRADC_CTRL1_LRADC_IRQ_MASK))
+       if (!(reg & mxs_lradc_irq_mask(lradc)))
                return IRQ_NONE;
 
-       /*
-        * Touchscreen IRQ handling code has priority and therefore
-        * is placed here. In case touchscreen IRQ arrives, disable
-        * it ASAP
-        */
-       if (reg & LRADC_CTRL1_TOUCH_DETECT_IRQ) {
-               writel(ts_irq_mask,
-                       lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
-               if (!lradc->stop_touchscreen)
-                       schedule_work(&lradc->ts_work);
-       }
+       if (lradc->use_touchscreen && (reg & ts_irq_mask))
+               mxs_lradc_handle_touch(lradc);
 
        if (iio_buffer_enabled(iio))
                iio_trigger_poll(iio->trig, iio_get_time_ns());
        else if (reg & LRADC_CTRL1_LRADC_IRQ(0))
                complete(&lradc->completion);
 
-       writel(reg & LRADC_CTRL1_LRADC_IRQ_MASK,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+       mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc), LRADC_CTRL1);
 
        return IRQ_HANDLED;
 }
@@ -619,19 +948,13 @@ static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p)
 
        for_each_set_bit(i, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
                lradc->buffer[j] = readl(lradc->base + LRADC_CH(j));
-               writel(chan_value, lradc->base + LRADC_CH(j));
+               mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(j));
                lradc->buffer[j] &= LRADC_CH_VALUE_MASK;
                lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
                j++;
        }
 
-       if (iio->scan_timestamp) {
-               s64 *timestamp = (s64 *)((u8 *)lradc->buffer +
-                                       ALIGN(j, sizeof(s64)));
-               *timestamp = pf->timestamp;
-       }
-
-       iio_push_to_buffers(iio, (u8 *)lradc->buffer);
+       iio_push_to_buffers_with_timestamp(iio, lradc->buffer, pf->timestamp);
 
        iio_trigger_notify_done(iio->trig);
 
@@ -644,7 +967,7 @@ static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state)
        struct mxs_lradc *lradc = iio_priv(iio);
        const uint32_t st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
 
-       writel(LRADC_DELAY_KICK, lradc->base + LRADC_DELAY(0) + st);
+       mxs_lradc_reg_wrt(lradc, LRADC_DELAY_KICK, LRADC_DELAY(0) + st);
 
        return 0;
 }
@@ -716,38 +1039,30 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
                goto err_mem;
        }
 
-       ret = iio_sw_buffer_preenable(iio);
-       if (ret < 0)
-               goto err_buf;
-
-       writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
-       writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+       if (lradc->soc == IMX28_LRADC)
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
+                                                       LRADC_CTRL1);
+       mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
 
        for_each_set_bit(chan, iio->active_scan_mask, LRADC_MAX_TOTAL_CHANS) {
                ctrl4_set |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
                ctrl4_clr |= LRADC_CTRL4_LRADCSELECT_MASK(ofs);
                ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
-               writel(chan_value, lradc->base + LRADC_CH(ofs));
+               mxs_lradc_reg_wrt(lradc, chan_value, LRADC_CH(ofs));
                bitmap_set(&enable, ofs, 1);
                ofs++;
        }
 
-       writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
-               lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
-
-       writel(ctrl4_clr, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_CLR);
-       writel(ctrl4_set, lradc->base + LRADC_CTRL4 + STMP_OFFSET_REG_SET);
-
-       writel(ctrl1_irq, lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
-
-       writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
-               lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
+       mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
+                                       LRADC_DELAY_KICK, LRADC_DELAY(0));
+       mxs_lradc_reg_clear(lradc, ctrl4_clr, LRADC_CTRL4);
+       mxs_lradc_reg_set(lradc, ctrl4_set, LRADC_CTRL4);
+       mxs_lradc_reg_set(lradc, ctrl1_irq, LRADC_CTRL1);
+       mxs_lradc_reg_set(lradc, enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
+                                       LRADC_DELAY(0));
 
        return 0;
 
-err_buf:
-       kfree(lradc->buffer);
 err_mem:
        mutex_unlock(&lradc->lock);
        return ret;
@@ -757,12 +1072,13 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
 {
        struct mxs_lradc *lradc = iio_priv(iio);
 
-       writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
-               lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
+       mxs_lradc_reg_clear(lradc, LRADC_DELAY_TRIGGER_LRADCS_MASK |
+                                       LRADC_DELAY_KICK, LRADC_DELAY(0));
 
-       writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
-       writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+       mxs_lradc_reg_clear(lradc, 0xff, LRADC_CTRL0);
+       if (lradc->soc == IMX28_LRADC)
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK,
+                                       LRADC_CTRL1);
 
        kfree(lradc->buffer);
        mutex_unlock(&lradc->lock);
@@ -857,24 +1173,25 @@ static int mxs_lradc_hw_init(struct mxs_lradc *lradc)
                return ret;
 
        /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
-       writel(adc_cfg, lradc->base + LRADC_DELAY(0));
+       mxs_lradc_reg_wrt(lradc, adc_cfg, LRADC_DELAY(0));
 
        /* Disable remaining DELAY CHANNELs */
-       writel(0, lradc->base + LRADC_DELAY(1));
-       writel(0, lradc->base + LRADC_DELAY(2));
-       writel(0, lradc->base + LRADC_DELAY(3));
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(1));
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
 
        /* Configure the touchscreen type */
-       writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
-               lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+       if (lradc->soc == IMX28_LRADC) {
+               mxs_lradc_reg_clear(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+                                                       LRADC_CTRL0);
 
-       if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) {
-               writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
-                       lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+       if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
+               mxs_lradc_reg_set(lradc, LRADC_CTRL0_MX28_TOUCH_SCREEN_TYPE,
+                               LRADC_CTRL0);
        }
 
        /* Start internal temperature sensing. */
-       writel(0, lradc->base + LRADC_CTRL2);
+       mxs_lradc_reg_wrt(lradc, 0, LRADC_CTRL2);
 
        return 0;
 }
@@ -883,11 +1200,10 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
 {
        int i;
 
-       writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
-               lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+       mxs_lradc_reg_clear(lradc, mxs_lradc_irq_en_mask(lradc), LRADC_CTRL1);
 
        for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
-               writel(0, lradc->base + LRADC_DELAY(i));
+               mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i));
 }
 
 static const struct of_device_id mxs_lradc_dt_ids[] = {
@@ -897,6 +1213,52 @@ static const struct of_device_id mxs_lradc_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
 
+static int mxs_lradc_probe_touchscreen(struct mxs_lradc *lradc,
+                                               struct device_node *lradc_node)
+{
+       int ret;
+       u32 ts_wires = 0, adapt;
+
+       ret = of_property_read_u32(lradc_node, "fsl,lradc-touchscreen-wires",
+                               &ts_wires);
+       if (ret)
+               return -ENODEV; /* touchscreen feature disabled */
+
+       switch (ts_wires) {
+       case 4:
+               lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
+               break;
+       case 5:
+               if (lradc->soc == IMX28_LRADC) {
+                       lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
+                       break;
+               }
+               /* fall through an error message for i.MX23 */
+       default:
+               dev_err(lradc->dev,
+                       "Unsupported number of touchscreen wires (%d)\n",
+                       ts_wires);
+               return -EINVAL;
+       }
+
+       lradc->over_sample_cnt = 4;
+       ret = of_property_read_u32(lradc_node, "fsl,ave-ctrl", &adapt);
+       if (ret == 0)
+               lradc->over_sample_cnt = adapt;
+
+       lradc->over_sample_delay = 2;
+       ret = of_property_read_u32(lradc_node, "fsl,ave-delay", &adapt);
+       if (ret == 0)
+               lradc->over_sample_delay = adapt;
+
+       lradc->settling_delay = 10;
+       ret = of_property_read_u32(lradc_node, "fsl,settling", &adapt);
+       if (ret == 0)
+               lradc->settling_delay = adapt;
+
+       return 0;
+}
+
 static int mxs_lradc_probe(struct platform_device *pdev)
 {
        const struct of_device_id *of_id =
@@ -908,8 +1270,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
        struct mxs_lradc *lradc;
        struct iio_dev *iio;
        struct resource *iores;
-       uint32_t ts_wires = 0;
-       int ret = 0;
+       int ret = 0, touch_ret;
        int i;
 
        /* Allocate the IIO device. */
@@ -920,6 +1281,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
        }
 
        lradc = iio_priv(iio);
+       lradc->soc = (enum mxs_lradc_id)of_id->data;
 
        /* Grab the memory area */
        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -928,20 +1290,18 @@ static int mxs_lradc_probe(struct platform_device *pdev)
        if (IS_ERR(lradc->base))
                return PTR_ERR(lradc->base);
 
-       INIT_WORK(&lradc->ts_work, mxs_lradc_ts_work);
+       lradc->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(lradc->clk)) {
+               dev_err(dev, "Failed to get the delay unit clock\n");
+               return PTR_ERR(lradc->clk);
+       }
+       ret = clk_prepare_enable(lradc->clk);
+       if (ret != 0) {
+               dev_err(dev, "Failed to enable the delay unit clock\n");
+               return ret;
+       }
 
-       /* Check if touchscreen is enabled in DT. */
-       ret = of_property_read_u32(node, "fsl,lradc-touchscreen-wires",
-                               &ts_wires);
-       if (ret)
-               dev_info(dev, "Touchscreen not enabled.\n");
-       else if (ts_wires == 4)
-               lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_4WIRE;
-       else if (ts_wires == 5)
-               lradc->use_touchscreen = MXS_LRADC_TOUCHSCREEN_5WIRE;
-       else
-               dev_warn(dev, "Unsupported number of touchscreen wires (%d)\n",
-                               ts_wires);
+       touch_ret = mxs_lradc_probe_touchscreen(lradc, node);
 
        /* Grab all IRQ sources */
        for (i = 0; i < of_cfg->irq_count; i++) {
@@ -985,9 +1345,11 @@ static int mxs_lradc_probe(struct platform_device *pdev)
                goto err_dev;
 
        /* Register the touchscreen input device. */
-       ret = mxs_lradc_ts_register(lradc);
-       if (ret)
-               goto err_dev;
+       if (touch_ret == 0) {
+               ret = mxs_lradc_ts_register(lradc);
+               if (ret)
+                       goto err_ts_register;
+       }
 
        /* Register IIO device. */
        ret = iio_device_register(iio);
@@ -1000,6 +1362,8 @@ static int mxs_lradc_probe(struct platform_device *pdev)
 
 err_ts:
        mxs_lradc_ts_unregister(lradc);
+err_ts_register:
+       mxs_lradc_hw_stop(lradc);
 err_dev:
        mxs_lradc_trigger_remove(iio);
 err_trig:
@@ -1012,14 +1376,13 @@ static int mxs_lradc_remove(struct platform_device *pdev)
        struct iio_dev *iio = platform_get_drvdata(pdev);
        struct mxs_lradc *lradc = iio_priv(iio);
 
+       iio_device_unregister(iio);
        mxs_lradc_ts_unregister(lradc);
-
        mxs_lradc_hw_stop(lradc);
-
-       iio_device_unregister(iio);
-       iio_triggered_buffer_cleanup(iio);
        mxs_lradc_trigger_remove(iio);
+       iio_triggered_buffer_cleanup(iio);
 
+       clk_disable_unprepare(lradc->clk);
        return 0;
 }
 
@@ -1038,3 +1401,4 @@ module_platform_driver(mxs_lradc_driver);
 MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
 MODULE_DESCRIPTION("Freescale i.MX28 LRADC driver");
 MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);