]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/input/touchscreen/ads7846.c
[PATCH 3/6] ads7846: replace spin_lock_irqsave
[linux-2.6-omap-h63xx.git] / drivers / input / touchscreen / ads7846.c
index 71fbcc4c58b6e122f7f793a7065995e078055e99..8200a03ee2d7f7927be9b4c3ff70abeeec30a73f 100644 (file)
@@ -2,7 +2,8 @@
  * ADS7846 based touchscreen and sensor driver
  *
  * Copyright (c) 2005 David Brownell
- * Copyright (c) 2006 Imre Deak <imre.deak@nokia.com>
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
  *
  * Using code from:
  *  - corgi_ts.c
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  */
-#include <linux/device.h>
+#include <linux/hwmon.h>
 #include <linux/init.h>
+#include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
+#include <asm/irq.h>
 
 #ifdef CONFIG_ARM
 #include <asm/mach-types.h>
 
 /*
  * This code has been heavily tested on a Nokia 770, and lightly
- * tested on an other ads7846 device.
+ * tested on other ads7846 devices (OSK/Mistral, Lubbock).
  * Support for ads7843 and ads7845 has only been stubbed in.
  *
- * Not yet done:  How accurate are the temperature and voltage
- * readings? (System-specific calibration should support
- * accuracy of 0.3 degrees C; otherwise it's 2.0 degrees.)
- *
  * IRQ handling needs a workaround because of a shortcoming in handling
  * edge triggered IRQs on some platforms like the OMAP1/2. These
  * platforms don't handle the ARM lazy IRQ disabling properly, thus we
  * have to maintain our own SW IRQ disabled status. This should be
- * removed as soon as the affected platforms' IRQ handling is fixed.
+ * removed as soon as the affected platform's IRQ handling is fixed.
  *
  * app note sbaa036 talks in more detail about accurate sampling...
  * that ought to help in situations like LCDs inducing noise (which
@@ -77,17 +76,14 @@ struct ads7846 {
        char                    phys[32];
 
        struct spi_device       *spi;
+       struct class_device     *hwmon;
        u16                     model;
        u16                     vref_delay_usecs;
        u16                     x_plate_ohms;
-       u16                     pressure_max;
 
        u8                      read_x, read_y, read_z1, read_z2, pwrdown;
        u16                     dummy;          /* for the pwrdown read */
        struct ts_event         tc;
-       u16                     last_x;
-       u16                     last_y;
-       u16                     last_pressure;
 
        struct spi_transfer     xfer[10];
        struct spi_message      msg[5];
@@ -163,7 +159,12 @@ struct ads7846 {
 
 /*
  * Non-touchscreen sensors only use single-ended conversions.
+ * The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
+ * ads7846 lets that pin be unconnected, to use internal vREF.
+ *
+ * FIXME make external vREF_mV be a module option, and use that as needed...
  */
+static const unsigned vREF_mV = 2500;
 
 struct ser_req {
        u8                      ref_on;
@@ -185,50 +186,55 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
        struct ser_req          *req = kzalloc(sizeof *req, SLAB_KERNEL);
        int                     status;
        int                     sample;
-       int                     i;
+       int                     use_internal;
 
        if (!req)
                return -ENOMEM;
 
        spi_message_init(&req->msg);
 
-       /* activate reference, so it has time to settle; */
-       req->ref_on = REF_ON;
-       req->xfer[0].tx_buf = &req->ref_on;
-       req->xfer[0].len = 1;
-       req->xfer[1].rx_buf = &req->scratch;
-       req->xfer[1].len = 2;
-
-       /*
-        * for external VREF, 0 usec (and assume it's always on);
-        * for 1uF, use 800 usec;
-        * no cap, 100 usec.
-        */
-       req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+       /* FIXME boards with ads7846 might use external vref instead ... */
+       use_internal = (ts->model == 7846);
+
+       /* maybe turn on internal vREF, and let it settle */
+       if (use_internal) {
+               req->ref_on = REF_ON;
+               req->xfer[0].tx_buf = &req->ref_on;
+               req->xfer[0].len = 1;
+               spi_message_add_tail(&req->xfer[0], &req->msg);
+
+               req->xfer[1].rx_buf = &req->scratch;
+               req->xfer[1].len = 2;
+
+               /* for 1uF, settle for 800 usec; no cap, 100 usec.  */
+               req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+               spi_message_add_tail(&req->xfer[1], &req->msg);
+       }
 
        /* take sample */
        req->command = (u8) command;
        req->xfer[2].tx_buf = &req->command;
        req->xfer[2].len = 1;
+       spi_message_add_tail(&req->xfer[2], &req->msg);
+
        req->xfer[3].rx_buf = &req->sample;
        req->xfer[3].len = 2;
+       spi_message_add_tail(&req->xfer[3], &req->msg);
 
        /* REVISIT:  take a few more samples, and compare ... */
 
-       /* turn off reference */
-       req->ref_off = REF_OFF;
-       req->xfer[4].tx_buf = &req->ref_off;
-       req->xfer[4].len = 1;
-       req->xfer[5].rx_buf = &req->scratch;
-       req->xfer[5].len = 2;
-
-       CS_CHANGE(req->xfer[5]);
-
-       /* group all the transfers together, so we can't interfere with
-        * reading touchscreen state; disable penirq while sampling
-        */
-       for (i = 0; i < 6; i++)
-               spi_message_add_tail(&req->xfer[i], &req->msg);
+       /* maybe off internal vREF */
+       if (use_internal) {
+               req->ref_off = REF_OFF;
+               req->xfer[4].tx_buf = &req->ref_off;
+               req->xfer[4].len = 1;
+               spi_message_add_tail(&req->xfer[4], &req->msg);
+
+               req->xfer[5].rx_buf = &req->scratch;
+               req->xfer[5].len = 2;
+               CS_CHANGE(req->xfer[5]);
+               spi_message_add_tail(&req->xfer[5], &req->msg);
+       }
 
        disable_irq(spi->irq);
        status = spi_sync(spi, &req->msg);
@@ -236,28 +242,70 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
 
        if (req->msg.status)
                status = req->msg.status;
+
+       /* on-wire is a must-ignore bit, a BE12 value, then padding */
        sample = be16_to_cpu(req->sample);
-       sample = sample >> 4;
-       kfree(req);
+       sample = sample >> 3;
+       sample &= 0x0fff;
 
+       kfree(req);
        return status ? status : sample;
 }
 
-#define SHOW(name) static ssize_t \
+#define SHOW(name,var,adjust) static ssize_t \
 name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
 { \
+       struct ads7846 *ts = dev_get_drvdata(dev); \
        ssize_t v = ads7846_read12_ser(dev, \
-                       READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \
+                       READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \
        if (v < 0) \
                return v; \
-       return sprintf(buf, "%u\n", (unsigned) v); \
+       return sprintf(buf, "%u\n", adjust(ts, v)); \
 } \
 static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
 
-SHOW(temp0)
-SHOW(temp1)
-SHOW(vaux)
-SHOW(vbatt)
+
+/* Sysfs conventions report temperatures in millidegrees Celcius.
+ * We could use the low-accuracy two-sample scheme, but can't do the high
+ * accuracy scheme without calibration data.  For now we won't try either;
+ * userspace sees raw sensor values, and must scale appropriately.
+ */
+static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v)
+{
+       return v;
+}
+
+SHOW(temp0, temp0, null_adjust)                // temp1_input
+SHOW(temp1, temp1, null_adjust)                // temp2_input
+
+
+/* sysfs conventions report voltages in millivolts.  We can convert voltages
+ * if we know vREF.  userspace may need to scale vAUX to match the board's
+ * external resistors; we assume that vBATT only uses the internal ones.
+ */
+static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
+{
+       unsigned retval = v;
+
+       /* external resistors may scale vAUX into 0..vREF */
+       retval *= vREF_mV;
+       retval = retval >> 12;
+       return retval;
+}
+
+static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
+{
+       unsigned retval = vaux_adjust(ts, v);
+
+       /* ads7846 has a resistor ladder to scale this signal down */
+       if (ts->model == 7846)
+               retval *= 4;
+       return retval;
+}
+
+SHOW(in0_input, vaux, vaux_adjust)
+SHOW(in1_input, vbatt, vbatt_adjust)
+
 
 static int is_pen_down(struct device *dev)
 {
@@ -287,19 +335,18 @@ static ssize_t ads7846_disable_store(struct device *dev,
                                     const char *buf, size_t count)
 {
        struct ads7846 *ts = dev_get_drvdata(dev);
-       unsigned long flags;
        char *endp;
        int i;
 
        i = simple_strtoul(buf, &endp, 10);
-       spin_lock_irqsave(&ts->lock, flags);
+       spin_lock_irq(&ts->lock);
 
        if (i)
                ads7846_disable(ts);
        else
                ads7846_enable(ts);
 
-       spin_unlock_irqrestore(&ts->lock, flags);
+       spin_unlock_irq(&ts->lock);
 
        return count;
 }
@@ -325,13 +372,13 @@ static void ads7846_rx(void *ads)
        u16                     x, y, z1, z2;
        unsigned long           flags;
 
-       /* adjust:  12 bit samples (left aligned), built from
-        * two 8 bit values writen msb-first.
+       /* adjust:  on-wire is a must-ignore bit, a BE12 value, then padding;
+        * built from two 8 bit values written msb-first.
         */
-       x = ts->tc.x >> 3;
-       y = ts->tc.y >> 3;
-       z1 = ts->tc.z1 >> 3;
-       z2 = ts->tc.z2 >> 3;
+       x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff;
+       y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff;
+       z1 = (be16_to_cpu(ts->tc.z1) >> 3) & 0x0fff;
+       z2 = (be16_to_cpu(ts->tc.z2) >> 3) & 0x0fff;
 
        /* range filtering */
        if (x == MAX_12BIT)
@@ -348,18 +395,6 @@ static void ads7846_rx(void *ads)
        } else
                Rt = 0;
 
-       if (Rt > ts->pressure_max) {
-               if (ts->last_pressure) {
-                       x = ts->last_x;
-                       y = ts->last_y;
-               }
-               Rt = ts->pressure_max;
-       }
-
-       ts->last_x = x;
-       ts->last_y = y;
-       ts->last_pressure = Rt;
-
        /* NOTE:  "pendown" is inferred from pressure; we don't rely on
         * being able to check nPENIRQ status, or "friendly" trigger modes
         * (both-edges is much better than just-falling or low-level).
@@ -423,10 +458,11 @@ static void ads7846_debounce(void *ads)
        t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
        val = (*(u16 *)t->rx_buf) >> 3;
 
-       if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol &&
-                             ts->read_cnt < ts->debounce_max)) {
+       if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol
+                               && ts->read_cnt < ts->debounce_max)) {
                /* Repeat it, if this was the first read or the read wasn't
-                * consistent enough */
+                * consistent enough
+                */
                ts->read_cnt++;
                ts->last_read = val;
        } else {
@@ -460,10 +496,14 @@ static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs)
        spin_lock_irqsave(&ts->lock, flags);
        if (likely(!ts->irq_disabled && !ts->disabled)) {
                if (!ts->irq_disabled) {
+                       /* REVISIT irq logic for many ARM chips has cloned a
+                        * bug wherein disabling an irq in its handler won't
+                        * work;(it's disabled lazily, and too late to work.
+                        * until all their irq logic is fixed, we must shadow
+                        * that state here.
+                        */
                        ts->irq_disabled = 1;
-                       /* The following has at the moment no effect whatsoever
-                        * on OMAP, that's why we maintain the disabled
-                        * state ourselves */
+
                        disable_irq(ts->spi->irq);
                }
                if (!ts->pending) {
@@ -491,8 +531,6 @@ static void ads7846_disable(struct ads7846 *ts)
                        disable_irq(ts->spi->irq);
                }
        } else {
-               unsigned long flags;
-
                /* polling; force a final SPI completion;
                 * that will clean things up neatly
                 */
@@ -500,9 +538,9 @@ static void ads7846_disable(struct ads7846 *ts)
                        mod_timer(&ts->timer, jiffies);
 
                while (ts->pendown || ts->pending) {
-                       spin_unlock_irqrestore(&ts->lock, flags);
+                       spin_unlock_irq(&ts->lock);
                        msleep(1);
-                       spin_lock_irqsave(&ts->lock, flags);
+                       spin_lock_irq(&ts->lock);
                }
        }
 
@@ -527,14 +565,13 @@ static void ads7846_enable(struct ads7846 *ts)
 static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
 {
        struct ads7846 *ts = dev_get_drvdata(&spi->dev);
-       unsigned long   flags;
 
-       spin_lock_irqsave(&ts->lock, flags);
+       spin_lock_irq(&ts->lock);
 
        spi->dev.power.power_state = message;
        ads7846_disable(ts);
 
-       spin_unlock_irqrestore(&ts->lock, flags);
+       spin_unlock_irq(&ts->lock);
 
        return 0;
 
@@ -543,14 +580,13 @@ static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
 static int ads7846_resume(struct spi_device *spi)
 {
        struct ads7846 *ts = dev_get_drvdata(&spi->dev);
-       unsigned long flags;
 
-       spin_lock_irqsave(&ts->lock, flags);
+       spin_lock_irq(&ts->lock);
 
        spi->dev.power.power_state = PMSG_ON;
        ads7846_enable(ts);
 
-       spin_unlock_irqrestore(&ts->lock, flags);
+       spin_unlock_irq(&ts->lock);
 
        return 0;
 }
@@ -559,6 +595,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 {
        struct ads7846                  *ts;
        struct input_dev                *input_dev;
+       struct class_device             *hwmon = ERR_PTR(-ENOMEM);
        struct ads7846_platform_data    *pdata = spi->dev.platform_data;
        struct spi_message              *m;
        struct spi_transfer             *x;
@@ -584,12 +621,14 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        /* We'd set the wordsize to 12 bits ... except that some controllers
         * will then treat the 8 bit command words as 12 bits (and drop the
         * four MSBs of the 12 bit result).  Result: inputs must be shifted
-        * to discard the four garbage LSBs.
+        * to discard the four garbage LSBs.  (Also, not all controllers can
+        * support 12 bit words.)
         */
 
        ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
        input_dev = input_allocate_device();
-       if (!ts || !input_dev) {
+       hwmon = hwmon_device_register(&spi->dev);
+       if (!ts || !input_dev || IS_ERR(hwmon)) {
                err = -ENOMEM;
                goto err_free_mem;
        }
@@ -599,6 +638,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 
        ts->spi = spi;
        ts->input = input_dev;
+       ts->hwmon = hwmon;
 
        init_timer(&ts->timer);
        ts->timer.data = (unsigned long) ts;
@@ -609,7 +649,6 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        ts->model = pdata->model ? : 7846;
        ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
        ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
-       ts->pressure_max = pdata->pressure_max ? : ~0;
        ts->debounce_max = pdata->debounce_max ? : 1;
        ts->debounce_tol = pdata->debounce_tol ? : 10;
 
@@ -736,27 +775,29 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                goto err_free_mem;
        }
 
-       dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
+       dev_info(&spi->dev, "touchscreen + hwmon, irq %d\n", spi->irq);
 
-       /* take a first sample, leaving nPENIRQ active; avoid
+       /* take a first sample, leaving nPENIRQ active and vREF off; avoid
         * the touchscreen, in case it's not connected.
         */
        (void) ads7846_read12_ser(&spi->dev,
                          READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
 
        /* ads7843/7845 don't have temperature sensors, and
-        * use the other sensors a bit differently too
+        * use the other ADC lines a bit differently too
         */
        if (ts->model == 7846) {
                device_create_file(&spi->dev, &dev_attr_temp0);
                device_create_file(&spi->dev, &dev_attr_temp1);
        }
+       /* in1 == vBAT (7846), or a non-scaled ADC input */
        if (ts->model != 7845)
-               device_create_file(&spi->dev, &dev_attr_vbatt);
-       device_create_file(&spi->dev, &dev_attr_vaux);
+               device_create_file(&spi->dev, &dev_attr_in1_input);
+       /* in0 == a non-scaled ADC input */
+       device_create_file(&spi->dev, &dev_attr_in0_input);
 
+       /* non-hwmon device attributes */
        device_create_file(&spi->dev, &dev_attr_pen_down);
-
        device_create_file(&spi->dev, &dev_attr_disable);
 
        err = input_register_device(input_dev);
@@ -773,11 +814,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                device_remove_file(&spi->dev, &dev_attr_temp0);
        }
        if (ts->model != 7845)
-               device_remove_file(&spi->dev, &dev_attr_vbatt);
-       device_remove_file(&spi->dev, &dev_attr_vaux);
+               device_remove_file(&spi->dev, &dev_attr_in1_input);
+       device_remove_file(&spi->dev, &dev_attr_in0_input);
 
        free_irq(spi->irq, ts);
  err_free_mem:
+       if (!IS_ERR(hwmon))
+               hwmon_device_unregister(hwmon);
        input_free_device(input_dev);
        kfree(ts);
        return err;
@@ -787,6 +830,7 @@ static int __devexit ads7846_remove(struct spi_device *spi)
 {
        struct ads7846          *ts = dev_get_drvdata(&spi->dev);
 
+       hwmon_device_unregister(ts->hwmon);
        input_unregister_device(ts->input);
 
        ads7846_suspend(spi, PMSG_SUSPEND);
@@ -798,8 +842,8 @@ static int __devexit ads7846_remove(struct spi_device *spi)
                device_remove_file(&spi->dev, &dev_attr_temp0);
        }
        if (ts->model != 7845)
-               device_remove_file(&spi->dev, &dev_attr_vbatt);
-       device_remove_file(&spi->dev, &dev_attr_vaux);
+               device_remove_file(&spi->dev, &dev_attr_in1_input);
+       device_remove_file(&spi->dev, &dev_attr_in0_input);
 
        free_irq(ts->spi->irq, ts);
        if (ts->irq_disabled)
@@ -852,7 +896,7 @@ static int __init ads7846_init(void)
 
        return spi_register_driver(&ads7846_driver);
 }
-module_init(ads7846_init);
+device_initcall(ads7846_init);
 
 static void __exit ads7846_exit(void)
 {