summaryrefslogtreecommitdiff
path: root/drivers/staging/iio/adc/mxs-lradc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 12:11:44 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-21 12:11:44 -0800
commitb5c78e04dd061b776978dad61dd85357081147b0 (patch)
tree2416b2dc61c452c3aeb2a32bcedf15e6257be638 /drivers/staging/iio/adc/mxs-lradc.c
parent06991c28f37ad68e5c03777f5c3b679b56e3dac1 (diff)
parent951348b377385475aa256c27e1c9e2564c9ec160 (diff)
Merge tag 'staging-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging tree update from Greg Kroah-Hartman: "Here's the big staging tree merge for 3.9-rc1 Lots of cleanups and updates for drivers all through the staging tree. We are pretty much "code neutral" here, adding just about as many lines as we removed. All of these have been in linux-next for a while." * tag 'staging-3.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (804 commits) staging: comedi: vmk80xx: wait for URBs to complete staging: comedi: drivers: addi-data: hwdrv_apci3200.c: Add a missing semicolon staging: et131x: Update TODO list staging: et131x: Remove assignment of skb->dev staging: wlan-ng: hfa384x.h: fix for error reported by smatch staging/zache checkpatch ERROR: spaces prohibited around that staging/ozwpan: Mark read only parameters and structs as const staging/ozwpan: Remove empty and unused function oz_cdev_heartbeat staging/ozwpan: Mark local functions as static (fix sparse warnings) staging/ozwpan: Add missing header includes staging/usbip: Mark local functions as static (fix sparse warnings) staging/xgifb: Remove duplicated code in loops. staging/xgifb: Consolidate return paths staging/xgifb: Remove code without effect staging/xgifb: Remove unnecessary casts staging/xgifb: Consolidate if/else if with identical code branches staging: vt6656: replaced custom TRUE definition with true staging: vt6656: replaced custom FALSE definition with false staging: vt6656: replace custom BOOL definition with bool staging/rtl8187se: Mark functions as static to silence sparse ...
Diffstat (limited to 'drivers/staging/iio/adc/mxs-lradc.c')
-rw-r--r--drivers/staging/iio/adc/mxs-lradc.c525
1 files changed, 490 insertions, 35 deletions
diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c
index 3d562da039db..55a459b61907 100644
--- a/drivers/staging/iio/adc/mxs-lradc.c
+++ b/drivers/staging/iio/adc/mxs-lradc.c
@@ -33,6 +33,8 @@
#include <linux/stmp_device.h>
#include <linux/bitops.h>
#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/input.h>
#include <mach/mxs.h>
#include <mach/common.h>
@@ -60,7 +62,39 @@
#define LRADC_DELAY_TIMER_PER 200
#define LRADC_DELAY_TIMER_LOOP 5
-static const char * const mxs_lradc_irq_name[] = {
+/*
+ * Once the pen touches the touchscreen, the touchscreen switches from
+ * IRQ-driven mode to polling mode to prevent interrupt storm. The polling
+ * is realized by worker thread, which is called every 20 or so milliseconds.
+ * This gives the touchscreen enough fluence and does not strain the system
+ * too much.
+ */
+#define LRADC_TS_SAMPLE_DELAY_MS 5
+
+/*
+ * The LRADC reads the following amount of samples from each touchscreen
+ * channel and the driver then computes avarage of these.
+ */
+#define LRADC_TS_SAMPLE_AMOUNT 4
+
+enum mxs_lradc_id {
+ IMX23_LRADC,
+ IMX28_LRADC,
+};
+
+static const char * const mx23_lradc_irq_names[] = {
+ "mxs-lradc-touchscreen",
+ "mxs-lradc-channel0",
+ "mxs-lradc-channel1",
+ "mxs-lradc-channel2",
+ "mxs-lradc-channel3",
+ "mxs-lradc-channel4",
+ "mxs-lradc-channel5",
+ "mxs-lradc-channel6",
+ "mxs-lradc-channel7",
+};
+
+static const char * const mx28_lradc_irq_names[] = {
"mxs-lradc-touchscreen",
"mxs-lradc-thresh0",
"mxs-lradc-thresh1",
@@ -76,9 +110,26 @@ static const char * const mxs_lradc_irq_name[] = {
"mxs-lradc-button1",
};
-struct mxs_lradc_chan {
- uint8_t slot;
- uint8_t flags;
+struct mxs_lradc_of_config {
+ const int irq_count;
+ const char * const *irq_name;
+};
+
+static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
+ [IMX23_LRADC] = {
+ .irq_count = ARRAY_SIZE(mx23_lradc_irq_names),
+ .irq_name = mx23_lradc_irq_names,
+ },
+ [IMX28_LRADC] = {
+ .irq_count = ARRAY_SIZE(mx28_lradc_irq_names),
+ .irq_name = mx28_lradc_irq_names,
+ },
+};
+
+enum mxs_lradc_ts {
+ MXS_LRADC_TOUCHSCREEN_NONE = 0,
+ MXS_LRADC_TOUCHSCREEN_4WIRE,
+ MXS_LRADC_TOUCHSCREEN_5WIRE,
};
struct mxs_lradc {
@@ -91,24 +142,70 @@ struct mxs_lradc {
struct mutex lock;
- uint8_t enable;
-
struct completion completion;
+
+ /*
+ * Touchscreen LRADC channels receives a private slot in the CTRL4
+ * register, the slot #7. Therefore only 7 slots instead of 8 in the
+ * CTRL4 register can be mapped to LRADC channels when using the
+ * touchscreen.
+ *
+ * Furthermore, certain LRADC channels are shared between touchscreen
+ * and/or touch-buttons and generic LRADC block. Therefore when using
+ * either of these, these channels are not available for the regular
+ * sampling. The shared channels are as follows:
+ *
+ * CH0 -- Touch button #0
+ * CH1 -- Touch button #1
+ * CH2 -- Touch screen XPUL
+ * CH3 -- Touch screen YPLL
+ * CH4 -- Touch screen XNUL
+ * CH5 -- Touch screen YNLR
+ * CH6 -- Touch screen WIPER (5-wire only)
+ *
+ * The bitfields below represents which parts of the LRADC block are
+ * switched into special mode of operation. These channels can not
+ * be sampled as regular LRADC channels. The driver will refuse any
+ * attempt to sample these channels.
+ */
+#define CHAN_MASK_TOUCHBUTTON (0x3 << 0)
+#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;
};
#define LRADC_CTRL0 0x00
-#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23)
-#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22)
+#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_CTRL1 0x10
-#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
-#define LRADC_CTRL1_LRADC_IRQ_MASK 0x1fff
+#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_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_LRADC_IRQ_OFFSET 0
#define LRADC_CTRL2 0x20
#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15)
+#define LRADC_STATUS 0x40
+#define LRADC_STATUS_TOUCH_DETECT_RAW (1 << 0)
+
#define LRADC_CH(n) (0x50 + (0x10 * (n)))
#define LRADC_CH_ACCUMULATE (1 << 29)
#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
@@ -140,6 +237,7 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
{
struct mxs_lradc *lradc = iio_priv(iio_dev);
int ret;
+ unsigned long mask;
if (m != IIO_CHAN_INFO_RAW)
return -EINVAL;
@@ -148,6 +246,12 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
if (chan->channel > LRADC_MAX_TOTAL_CHANS)
return -EINVAL;
+ /* Validate the channel if it doesn't intersect with reserved chans. */
+ bitmap_set(&mask, chan->channel, 1);
+ ret = iio_validate_scan_mask_onehot(iio_dev, &mask);
+ if (ret)
+ return -EINVAL;
+
/*
* See if there is no buffered operation in progess. If there is, simply
* bail out. This can be improved to support both buffered and raw IO at
@@ -169,7 +273,11 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
- writel(chan->channel, lradc->base + LRADC_CTRL4);
+ /* 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);
+
writel(0, lradc->base + LRADC_CH(0));
/* Enable the IRQ and start sampling the channel. */
@@ -203,6 +311,269 @@ static const struct iio_info mxs_lradc_iio_info = {
};
/*
+ * 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);
+
+ return 0;
+}
+
+static void mxs_lradc_ts_close(struct input_dev *dev)
+{
+ struct mxs_lradc *lradc = input_get_drvdata(dev);
+
+ /* Indicate the touchscreen is stopping. */
+ lradc->stop_touchscreen = true;
+ mb();
+
+ /* Wait until touchscreen thread finishes any possible remnants. */
+ cancel_work_sync(&lradc->ts_work);
+
+ /* Disable touchscreen touch-detect IRQ. */
+ writel(LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ /* Power-down touchscreen touch-detect circuitry. */
+ writel(LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+}
+
+static int mxs_lradc_ts_register(struct mxs_lradc *lradc)
+{
+ struct input_dev *input;
+ struct device *dev = lradc->dev;
+ int ret;
+
+ if (!lradc->use_touchscreen)
+ return 0;
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(dev, "Failed to allocate TS device!\n");
+ return -ENOMEM;
+ }
+
+ input->name = DRIVER_NAME;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = dev;
+ input->open = mxs_lradc_ts_open;
+ input->close = mxs_lradc_ts_close;
+
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ input_set_abs_params(input, ABS_X, 0, LRADC_CH_VALUE_MASK, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, LRADC_CH_VALUE_MASK, 0, 0);
+ input_set_abs_params(input, ABS_PRESSURE, 0, LRADC_CH_VALUE_MASK, 0, 0);
+
+ lradc->ts_input = input;
+ input_set_drvdata(input, lradc);
+ ret = input_register_device(input);
+ if (ret)
+ input_free_device(lradc->ts_input);
+
+ return ret;
+}
+
+static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc)
+{
+ if (!lradc->use_touchscreen)
+ return;
+
+ cancel_work_sync(&lradc->ts_work);
+
+ input_unregister_device(lradc->ts_input);
+}
+
+/*
* IRQ Handling
*/
static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
@@ -210,14 +581,24 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
struct iio_dev *iio = 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;
if (!(reg & LRADC_CTRL1_LRADC_IRQ_MASK))
return IRQ_NONE;
/*
- * Touchscreen IRQ handling code shall probably have priority
- * and therefore shall be placed here.
+ * 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 (iio_buffer_enabled(iio))
iio_trigger_poll(iio->trig, iio_get_time_ns());
@@ -313,8 +694,10 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
{
struct mxs_lradc *lradc = iio_priv(iio);
struct iio_buffer *buffer = iio->buffer;
- int ret = 0, chan, ofs = 0, enable = 0;
- uint32_t ctrl4 = 0;
+ int ret = 0, chan, ofs = 0;
+ unsigned long enable = 0;
+ uint32_t ctrl4_set = 0;
+ uint32_t ctrl4_clr = 0;
uint32_t ctrl1_irq = 0;
const uint32_t chan_value = LRADC_CH_ACCUMULATE |
((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
@@ -346,17 +729,20 @@ static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
for_each_set_bit(chan, buffer->scan_mask, LRADC_MAX_TOTAL_CHANS) {
- ctrl4 |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
+ 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));
- enable |= 1 << 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, lradc->base + LRADC_CTRL4);
+ 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,
@@ -391,9 +777,33 @@ static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
const unsigned long *mask)
{
- const int mw = bitmap_weight(mask, iio->masklength);
-
- return mw <= LRADC_MAX_MAPPED_CHANS;
+ struct mxs_lradc *lradc = iio_priv(iio);
+ const int len = iio->masklength;
+ const int map_chans = bitmap_weight(mask, len);
+ int rsvd_chans = 0;
+ unsigned long rsvd_mask = 0;
+
+ if (lradc->use_touchbutton)
+ rsvd_mask |= CHAN_MASK_TOUCHBUTTON;
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_4WIRE)
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_4WIRE;
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE)
+ rsvd_mask |= CHAN_MASK_TOUCHSCREEN_5WIRE;
+
+ if (lradc->use_touchbutton)
+ rsvd_chans++;
+ if (lradc->use_touchscreen)
+ rsvd_chans++;
+
+ /* Test for attempts to map channels with special mode of operation. */
+ if (bitmap_intersects(mask, &rsvd_mask, len))
+ return false;
+
+ /* Test for attempts to map more channels then available slots. */
+ if (map_chans + rsvd_chans > LRADC_MAX_MAPPED_CHANS)
+ return false;
+
+ return true;
}
static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
@@ -442,15 +852,29 @@ static const struct iio_chan_spec mxs_lradc_chan_spec[] = {
static void mxs_lradc_hw_init(struct mxs_lradc *lradc)
{
- int i;
- const uint32_t cfg =
+ /* The ADC always uses DELAY CHANNEL 0. */
+ const uint32_t adc_cfg =
+ (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + 0)) |
(LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
stmp_reset_block(lradc->base);
- for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
- writel(cfg | (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + i)),
- lradc->base + LRADC_DELAY(i));
+ /* Configure DELAY CHANNEL 0 for generic ADC sampling. */
+ writel(adc_cfg, lradc->base + 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));
+
+ /* Configure the touchscreen type */
+ writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+
+ if (lradc->use_touchscreen == MXS_LRADC_TOUCHSCREEN_5WIRE) {
+ writel(LRADC_CTRL0_TOUCH_SCREEN_TYPE,
+ lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+ }
/* Start internal temperature sensing. */
writel(0, lradc->base + LRADC_CTRL2);
@@ -467,12 +891,25 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
writel(0, lradc->base + LRADC_DELAY(i));
}
+static const struct of_device_id mxs_lradc_dt_ids[] = {
+ { .compatible = "fsl,imx23-lradc", .data = (void *)IMX23_LRADC, },
+ { .compatible = "fsl,imx28-lradc", .data = (void *)IMX28_LRADC, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
+
static int mxs_lradc_probe(struct platform_device *pdev)
{
+ const struct of_device_id *of_id =
+ of_match_device(mxs_lradc_dt_ids, &pdev->dev);
+ const struct mxs_lradc_of_config *of_cfg =
+ &mxs_lradc_of_config[(enum mxs_lradc_id)of_id->data];
struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
struct mxs_lradc *lradc;
struct iio_dev *iio;
struct resource *iores;
+ uint32_t ts_wires = 0;
int ret = 0;
int i;
@@ -494,8 +931,23 @@ static int mxs_lradc_probe(struct platform_device *pdev)
goto err_addr;
}
+ INIT_WORK(&lradc->ts_work, mxs_lradc_ts_work);
+
+ /* 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);
+
/* Grab all IRQ sources */
- for (i = 0; i < 13; i++) {
+ for (i = 0; i < of_cfg->irq_count; i++) {
lradc->irq[i] = platform_get_irq(pdev, i);
if (lradc->irq[i] < 0) {
ret = -EINVAL;
@@ -504,7 +956,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, lradc->irq[i],
mxs_lradc_handle_irq, 0,
- mxs_lradc_irq_name[i], iio);
+ of_cfg->irq_name[i], iio);
if (ret)
goto err_addr;
}
@@ -531,11 +983,16 @@ static int mxs_lradc_probe(struct platform_device *pdev)
if (ret)
goto err_trig;
+ /* Register the touchscreen input device. */
+ ret = mxs_lradc_ts_register(lradc);
+ if (ret)
+ goto err_dev;
+
/* Register IIO device. */
ret = iio_device_register(iio);
if (ret) {
dev_err(dev, "Failed to register IIO device\n");
- goto err_dev;
+ goto err_ts;
}
/* Configure the hardware. */
@@ -543,6 +1000,8 @@ static int mxs_lradc_probe(struct platform_device *pdev)
return 0;
+err_ts:
+ mxs_lradc_ts_unregister(lradc);
err_dev:
mxs_lradc_trigger_remove(iio);
err_trig:
@@ -557,6 +1016,8 @@ static int mxs_lradc_remove(struct platform_device *pdev)
struct iio_dev *iio = platform_get_drvdata(pdev);
struct mxs_lradc *lradc = iio_priv(iio);
+ mxs_lradc_ts_unregister(lradc);
+
mxs_lradc_hw_stop(lradc);
iio_device_unregister(iio);
@@ -567,12 +1028,6 @@ static int mxs_lradc_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id mxs_lradc_dt_ids[] = {
- { .compatible = "fsl,imx28-lradc", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
-
static struct platform_driver mxs_lradc_driver = {
.driver = {
.name = DRIVER_NAME,