Merge in the accelerometer functionality from stable

From: Simon Kagstrom <[EMAIL PROTECTED]>

This giant patch merges the functionality from stable. It also changes
the default threshold to 0 (generate interrupts on data). As the
level-triggered interrupts cause too frequent interruptions on low
thresholds, also set a lower limit to 36mg to avoid slowing down the
phone too much.

Also rearrange the probe function to disable interrupts only over device
I/O. This fixes the kernel errors when inserting the module.


Andy was right: In stable-tracking, no magic workaround is needed to
make suspend/resume work. I've tested the accelerometers under heavy
load (making the phone basically unusable) without getting lockups.

Signed-off-by: Simon Kagstrom <[EMAIL PROTECTED]>
---

 arch/arm/mach-s3c2440/mach-gta02.c |   35 --
 drivers/input/misc/lis302dl.c      |  633 ++++++++++++++++++++----------------
 include/linux/lis302dl.h           |   10 +
 3 files changed, 361 insertions(+), 317 deletions(-)

diff --git a/arch/arm/mach-s3c2440/mach-gta02.c 
b/arch/arm/mach-s3c2440/mach-gta02.c
index a959f67..ac2e5b6 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -1049,13 +1049,10 @@ static void __gta02_lis302dl_bitbang(struct 
lis302dl_info *lis, u8 *tx,
                                             int tx_bytes, u8 *rx, int rx_bytes)
 {
        struct lis302dl_platform_data *pdata = lis->pdata;
-       u8 shifter = 0;
        int n;
-       unsigned long flags;
+       u8 shifter = 0;
        unsigned long other_cs;
 
-       local_irq_save(flags);
-
        /*
         * Huh... "quirk"... CS on this device is not really "CS" like you can
         * expect.
@@ -1105,8 +1102,6 @@ static void __gta02_lis302dl_bitbang(struct lis302dl_info 
*lis, u8 *tx,
        }
        s3c2410_gpio_setpin(pdata->pin_chip_select, 1);
        s3c2410_gpio_setpin(other_cs, 1);
-
-       local_irq_restore(flags);
 }
 
 
@@ -1139,30 +1134,6 @@ static void gta02_lis302dl_bitbang_write_reg(struct 
lis302dl_info *lis, u8 reg,
 }
 
 
-static void gta02_lis302dl_bitbang_sample(struct lis302dl_info *lis)
-{
-       u8 data = 0xc0 | LIS302DL_REG_OUT_X; /* read, autoincrement */
-       u8 read[5];
-       unsigned long flags;
-
-       local_irq_save(flags);
-
-       __gta02_lis302dl_bitbang(lis, &data, 1, &read[0], 5);
-
-       local_irq_restore(flags);
-
-       input_report_rel(lis->input_dev, REL_X, MG_PER_SAMPLE * (s8)read[0]);
-       input_report_rel(lis->input_dev, REL_Y, MG_PER_SAMPLE * (s8)read[2]);
-       input_report_rel(lis->input_dev, REL_Z, MG_PER_SAMPLE * (s8)read[4]);
-
-       input_sync(lis->input_dev);
-#ifdef DEBUG_SPEW_MS
-       printk(KERN_INFO "%s: %d %d %d\n", pdata->name, read[0], read[2],
-                                                                      read[4]);
-#endif
-}
-
-
 void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume)
 {
        struct lis302dl_platform_data *pdata = lis->pdata;
@@ -1203,7 +1174,7 @@ struct lis302dl_platform_data lis302_pdata_top = {
                .pin_miso       = S3C2410_GPG5,
                .interrupt      = GTA02_IRQ_GSENSOR_1,
                .open_drain     = 1, /* altered at runtime by PCB rev */
-               .lis302dl_bitbang_read_sample = gta02_lis302dl_bitbang_sample,
+               .lis302dl_bitbang = __gta02_lis302dl_bitbang,
                .lis302dl_bitbang_reg_read = gta02_lis302dl_bitbang_read_reg,
                .lis302dl_bitbang_reg_write = gta02_lis302dl_bitbang_write_reg,
                .lis302dl_suspend_io = gta02_lis302dl_suspend_io,
@@ -1217,7 +1188,7 @@ struct lis302dl_platform_data lis302_pdata_bottom = {
                .pin_miso       = S3C2410_GPG5,
                .interrupt      = GTA02_IRQ_GSENSOR_2,
                .open_drain     = 1, /* altered at runtime by PCB rev */
-               .lis302dl_bitbang_read_sample = gta02_lis302dl_bitbang_sample,
+               .lis302dl_bitbang = __gta02_lis302dl_bitbang,
                .lis302dl_bitbang_reg_read = gta02_lis302dl_bitbang_read_reg,
                .lis302dl_bitbang_reg_write = gta02_lis302dl_bitbang_write_reg,
                .lis302dl_suspend_io = gta02_lis302dl_suspend_io,
diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
index 489651f..5a8f362 100644
--- a/drivers/input/misc/lis302dl.c
+++ b/drivers/input/misc/lis302dl.c
@@ -4,6 +4,8 @@
  * Author: Harald Welte <[EMAIL PROTECTED]>
  *         converted to private bitbang by:
  *         Andy Green <[EMAIL PROTECTED]>
+ *         ability to set acceleration threshold added by:
+ *         Simon Kagstrom <[EMAIL PROTECTED]>
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or
@@ -41,19 +43,64 @@
 
 #include <linux/lis302dl.h>
 
+/* Utility functions */
+static u8 __reg_read(struct lis302dl_info *lis, u8 reg)
+{
+       return (lis->pdata->lis302dl_bitbang_reg_read)(lis, reg);
+}
 
+static void __reg_write(struct lis302dl_info *lis, u8 reg, u8 val)
+{
+       (lis->pdata->lis302dl_bitbang_reg_write)(lis, reg, val);
+}
 
 static void __reg_set_bit_mask(struct lis302dl_info *lis, u8 reg, u8 mask,
-                                                                        u8 val)
+               u8 val)
 {
        u_int8_t tmp;
 
        val &= mask;
 
-       tmp = (lis->pdata->lis302dl_bitbang_reg_read)(lis, reg);
+       tmp = __reg_read(lis, reg);
        tmp &= ~mask;
        tmp |= val;
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, reg, tmp);
+       __reg_write(lis, reg, tmp);
+}
+
+static int __ms_to_duration(struct lis302dl_info *lis, int ms)
+{
+       /* If we have 400 ms sampling rate, the stepping is 2.5 ms,
+        * on 100 ms the stepping is 10ms */
+       if (lis->flags & LIS302DL_F_DR)
+               return min((ms * 10) / 25, 637);
+
+       return min(ms / 10, 2550);
+}
+
+static int __duration_to_ms(struct lis302dl_info *lis, int duration)
+{
+       if (lis->flags & LIS302DL_F_DR)
+               return (duration * 25) / 10;
+
+       return duration * 10;
+}
+
+static u8 __mg_to_threshold(struct lis302dl_info *lis, int mg)
+{
+       /* If FS is set each bit is 71mg, otherwise 18mg. The THS register
+        * has 7 bits for the threshold value */
+       if (lis->flags & LIS302DL_F_FS)
+               return min(mg / 71, 127);
+
+       return min(mg / 18, 127);
+}
+
+static int __threshold_to_mg(struct lis302dl_info *lis, u8 threshold)
+{
+       if (lis->flags & LIS302DL_F_FS)
+               return threshold * 71;
+
+       return threshold * 18;
 }
 
 /* interrupt handling related */
@@ -83,6 +130,65 @@ static void __lis302dl_int_mode(struct device *dev, int 
int_pin,
                BUG();
        }
 }
+
+static void __enable_wakeup(struct lis302dl_info *lis)
+{
+       __reg_write(lis, LIS302DL_REG_CTRL1, 0);
+
+       /* First zero to get to a known state */
+       __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, LIS302DL_FFWUCFG_XHIE |
+                       LIS302DL_FFWUCFG_YHIE | LIS302DL_FFWUCFG_ZHIE);
+       __reg_write(lis, LIS302DL_REG_FF_WU_THS_1,
+                       __mg_to_threshold(lis, lis->wakeup.threshold));
+       __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1,
+                       __ms_to_duration(lis, lis->wakeup.duration));
+
+       /* Route the interrupt for wakeup */
+       __lis302dl_int_mode(lis->dev, 1,
+                       LIS302DL_INTMODE_FF_WU_1);
+
+       __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET);
+       __reg_read(lis, LIS302DL_REG_OUT_X);
+       __reg_read(lis, LIS302DL_REG_OUT_Y);
+       __reg_read(lis, LIS302DL_REG_OUT_Z);
+       __reg_read(lis, LIS302DL_REG_STATUS);
+       __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
+       __reg_read(lis, LIS302DL_REG_FF_WU_SRC_2);
+       __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | 7);
+}
+
+static void __enable_data_collection(struct lis302dl_info *lis)
+{
+       u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen |
+                        LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen;
+
+       /* make sure we're powered up and generate data ready */
+       __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1);
+
+       /* If the threshold is zero, let the device generated an interrupt
+        * on each datum */
+       if (lis->threshold == 0) {
+               __reg_write(lis, LIS302DL_REG_CTRL2, 0);
+               __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_DATA_READY);
+               __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_DATA_READY);
+       } else {
+               __reg_write(lis, LIS302DL_REG_CTRL2,
+                               LIS302DL_CTRL2_HPFF1);
+               __reg_write(lis, LIS302DL_REG_FF_WU_THS_1,
+                               __mg_to_threshold(lis, lis->threshold));
+               __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1,
+                               __ms_to_duration(lis, lis->duration));
+
+               /* Clear the HP filter "starting point" */
+               __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET);
+               __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1,
+                               LIS302DL_FFWUCFG_XHIE | LIS302DL_FFWUCFG_YHIE |
+                               LIS302DL_FFWUCFG_ZHIE);
+               __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_FF_WU_12);
+               __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_FF_WU_12);
+       }
+}
+
 #if 0
 static void _report_btn_single(struct input_dev *inp, int btn)
 {
@@ -104,11 +210,36 @@ static void _report_btn_double(struct input_dev *inp, int 
btn)
 #endif
 
 
+static void lis302dl_bitbang_read_sample(struct lis302dl_info *lis)
+{
+       u8 data = 0xc0 | LIS302DL_REG_OUT_X; /* read, autoincrement */
+       u8 read[5];
+       unsigned long flags;
+       int mg_per_sample;
+       int n;
+
+       local_irq_save(flags);
+       mg_per_sample = __threshold_to_mg(lis, 1);
+
+       (lis->pdata->lis302dl_bitbang)(lis, &data, 1, &read[0], 5);
+
+       local_irq_restore(flags);
+
+       input_report_rel(lis->input_dev, REL_X, mg_per_sample * (s8)read[0]);
+       input_report_rel(lis->input_dev, REL_Y, mg_per_sample * (s8)read[2]);
+       input_report_rel(lis->input_dev, REL_Z, mg_per_sample * (s8)read[4]);
+
+       input_sync(lis->input_dev);
+
+       /* Reset the HP filter */
+       __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET);
+}
+
 static irqreturn_t lis302dl_interrupt(int irq, void *_lis)
 {
        struct lis302dl_info *lis = _lis;
 
-       (lis->pdata->lis302dl_bitbang_read_sample)(lis);
+       lis302dl_bitbang_read_sample(lis);
        return IRQ_HANDLED;
 }
 
@@ -122,8 +253,7 @@ static ssize_t show_rate(struct device *dev, struct 
device_attribute *attr,
        unsigned long flags;
 
        local_irq_save(flags);
-       ctrl1 = (lis->pdata->lis302dl_bitbang_reg_read)
-                                                     (lis, LIS302DL_REG_CTRL1);
+       ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1);
        local_irq_restore(flags);
 
        return sprintf(buf, "%d\n", ctrl1 & LIS302DL_CTRL1_DR ? 400 : 100);
@@ -137,12 +267,15 @@ static ssize_t set_rate(struct device *dev, struct 
device_attribute *attr,
 
        local_irq_save(flags);
 
-       if (!strcmp(buf, "400\n"))
+       if (!strcmp(buf, "400\n")) {
                __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR,
                                 LIS302DL_CTRL1_DR);
-       else
+               lis->flags |= LIS302DL_F_DR;
+       } else {
                __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR,
-                                                                            0);
+                               0);
+               lis->flags &= ~LIS302DL_F_DR;
+       }
        local_irq_restore(flags);
 
        return count;
@@ -158,8 +291,7 @@ static ssize_t show_scale(struct device *dev, struct 
device_attribute *attr,
        unsigned long flags;
 
        local_irq_save(flags);
-       ctrl1 = (lis->pdata->lis302dl_bitbang_reg_read)(lis,
-                                                           LIS302DL_REG_CTRL1);
+       ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1);
        local_irq_restore(flags);
 
        return sprintf(buf, "%s\n", ctrl1 & LIS302DL_CTRL1_FS ? "9.2" : "2.3");
@@ -173,12 +305,18 @@ static ssize_t set_scale(struct device *dev, struct 
device_attribute *attr,
 
        local_irq_save(flags);
 
-       if (!strcmp(buf, "9.2\n"))
+       if (!strcmp(buf, "9.2\n")) {
                __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS,
                                 LIS302DL_CTRL1_FS);
-       else
+               lis->flags |= LIS302DL_F_FS;
+       } else {
                __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS,
-                                                                            0);
+                               0);
+               lis->flags &= ~LIS302DL_F_FS;
+       }
+
+       if (lis->flags & LIS302DL_F_INPUT_OPEN)
+               __enable_data_collection(lis);
 
        local_irq_restore(flags);
 
@@ -187,6 +325,77 @@ static ssize_t set_scale(struct device *dev, struct 
device_attribute *attr,
 
 static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale);
 
+static ssize_t show_threshold(struct device *dev, struct device_attribute 
*attr,
+                char *buf)
+{
+       struct lis302dl_info *lis = dev_get_drvdata(dev);
+
+       /* Display the device view of the threshold setting */
+       return sprintf(buf, "%d\n", __threshold_to_mg(lis,
+                       __mg_to_threshold(lis, lis->threshold)));
+}
+
+static ssize_t set_threshold(struct device *dev, struct device_attribute *attr,
+                const char *buf, size_t count)
+{
+       struct lis302dl_info *lis = dev_get_drvdata(dev);
+       unsigned int val;
+
+       if (sscanf(buf, "%u\n", &val) != 1)
+               return -EINVAL;
+       /* 8g is the maximum if FS is 1 */
+       if (val > 8000)
+               return -ERANGE;
+       /* Lower than 36 overloads the system with interrupts */
+       if (val < 36)
+               val = 36;
+
+       /* Set the threshold and write it out if the device is used */
+       lis->threshold = val;
+
+       if (lis->flags & LIS302DL_F_INPUT_OPEN) {
+               unsigned long flags;
+
+               local_irq_save(flags);
+               __enable_data_collection(lis);
+               local_irq_restore(flags);
+       }
+
+       return count;
+}
+
+static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR, show_threshold, 
set_threshold);
+
+static ssize_t show_duration(struct device *dev, struct device_attribute *attr,
+                char *buf)
+{
+       struct lis302dl_info *lis = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n", __duration_to_ms(lis,
+                       __ms_to_duration(lis, lis->duration)));
+}
+
+static ssize_t set_duration(struct device *dev, struct device_attribute *attr,
+                const char *buf, size_t count)
+{
+       struct lis302dl_info *lis = dev_get_drvdata(dev);
+       unsigned int val;
+
+       if (sscanf(buf, "%u\n", &val) != 1)
+               return -EINVAL;
+       if (val > 2550)
+               return -ERANGE;
+
+       lis->duration = val;
+       if (lis->flags & LIS302DL_F_INPUT_OPEN)
+               __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1,
+                               __ms_to_duration(lis, lis->duration));
+
+       return count;
+}
+
+static DEVICE_ATTR(duration, S_IRUGO | S_IWUSR, show_duration, set_duration);
+
 static ssize_t lis302dl_dump(struct device *dev, struct device_attribute *attr,
                                                                      char *buf)
 {
@@ -199,7 +408,7 @@ static ssize_t lis302dl_dump(struct device *dev, struct 
device_attribute *attr,
        local_irq_save(flags);
 
        for (n = 0; n < sizeof(reg); n++)
-               reg[n] = (lis->pdata->lis302dl_bitbang_reg_read)(lis, n);
+               reg[n] = __reg_read(lis, n);
 
        local_irq_restore(flags);
 
@@ -214,231 +423,92 @@ static ssize_t lis302dl_dump(struct device *dev, struct 
device_attribute *attr,
 }
 static DEVICE_ATTR(dump, S_IRUGO, lis302dl_dump, NULL);
 
-static int __freefall_ms_to_duration(struct lis302dl_info *lis, int ms)
-{
-       u8 r = (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_CTRL1);
-
-       /* If we have 400 ms sampling rate, the stepping is 2.5 ms,
-        * on 100 ms the stepping is 10ms */
-       if (r & LIS302DL_CTRL1_DR) {
-               /* Too large */
-               if (ms > 637)
-                       return -1;
-
-               return (ms * 10) / 25;
-       }
-
-       /* Too large value */
-       if (ms > 2550)
-                       return -1;
-       return ms / 10;
-}
-
-static int __freefall_duration_to_ms(struct lis302dl_info *lis, int duration)
-{
-       u8 r = (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_CTRL1);
-
-       if (r & LIS302DL_CTRL1_DR)
-               return (duration * 25) / 10;
-
-       return duration * 10;
-}
-
 /* Configure freefall/wakeup interrupts */
-static ssize_t set_freefall_common(int which, struct device *dev,
-                  struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t set_wakeup_threshold(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
 {
        struct lis302dl_info *lis = dev_get_drvdata(dev);
-       u_int8_t x_lo, y_lo, z_lo;
-       u_int8_t x_hi, y_hi, z_hi;
-       int duration;
-       int threshold;
-       int and_events;
-       int r_ths = LIS302DL_REG_FF_WU_THS_1; /* registers, assume first pin */
-       int r_duration = LIS302DL_REG_FF_WU_DURATION_1;
-       int r_cfg = LIS302DL_REG_FF_WU_CFG_1;
-       int flag_mask = LIS302DL_F_WUP_FF_1;
-       int intmode = LIS302DL_INTMODE_FF_WU_1;
-       int x, y, z;
-       int ms;
-       unsigned long flags;
+       unsigned int threshold;
 
-       /* Configure for second freefall/wakeup pin */
-       if (which == 2) {
-               r_ths = LIS302DL_REG_FF_WU_THS_2;
-               r_duration = LIS302DL_REG_FF_WU_DURATION_2;
-               r_cfg = LIS302DL_REG_FF_WU_CFG_2;
-               flag_mask = LIS302DL_F_WUP_FF_2;
-               intmode = LIS302DL_INTMODE_FF_WU_2;
+       if (sscanf(buf, "%u\n", &threshold) != 1)
+               return -EINVAL;
 
-               printk(KERN_WARNING
-                           "Configuring second freefall / wakeup interrupt\n");
-       }
+       if (threshold > 8000)
+               return -ERANGE;
+       if (threshold < 36)
+               threshold = 36;
 
-       /* Parse the input */
-       if (strcmp(buf, "0\n") == 0) {
-               /* Turn off the interrupt */
-               local_irq_save(flags);
-               if (lis->flags & LIS302DL_F_IRQ_WAKE)
+       /* Zero turns the feature off */
+       if (threshold == 0) {
+               if (lis->flags & LIS302DL_F_IRQ_WAKE) {
                        disable_irq_wake(lis->pdata->interrupt);
-               __lis302dl_int_mode(lis->dev, which,
-                                                  LIS302DL_INTMODE_DATA_READY);
-               lis->flags &= ~(flag_mask | LIS302DL_F_IRQ_WAKE);
-
-               (lis->pdata->lis302dl_bitbang_reg_write)(lis, r_cfg, 0);
-               (lis->pdata->lis302dl_bitbang_reg_write)(lis, r_ths, 0);
-               (lis->pdata->lis302dl_bitbang_reg_write)(lis, r_duration, 0);
-
-               /* Power off unless the input subsystem is using the device */
-               if (!(lis->flags & LIS302DL_F_INPUT_OPEN))
-                       __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1,
-                                                         LIS302DL_CTRL1_PD, 0);
-
-               local_irq_restore(flags);
+                       lis->flags &= ~LIS302DL_F_IRQ_WAKE;
+               }
 
                return count;
        }
 
-       if (sscanf(buf, "%d %d %d %d %d %d", &x, &y, &z, &threshold, &ms,
-                                                             &and_events) != 6)
-               return -EINVAL;
-
-       local_irq_save(flags);
-       duration = __freefall_ms_to_duration(lis, ms);
-       local_irq_save(flags);
-
-       if (duration < 0)
-               return -ERANGE;
-
-       /* 7 bits */
-       if (threshold < 0 || threshold > 127)
-               return -ERANGE;
-
-       /* Interrupt flags */
-       x_lo = x < 0 ? LIS302DL_FFWUCFG_XLIE : 0;
-       y_lo = y < 0 ? LIS302DL_FFWUCFG_YLIE : 0;
-       z_lo = z < 0 ? LIS302DL_FFWUCFG_ZLIE : 0;
-       x_hi = x > 0 ? LIS302DL_FFWUCFG_XHIE : 0;
-       y_hi = y > 0 ? LIS302DL_FFWUCFG_YHIE : 0;
-       z_hi = z > 0 ? LIS302DL_FFWUCFG_ZHIE : 0;
-
-       /* Setup the configuration registers */
-       local_irq_save(flags);
-       /* First zero to get to a known state */
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, r_cfg, 0);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, r_cfg,
-               (and_events ? LIS302DL_FFWUCFG_AOI : 0) |
-               x_lo | x_hi | y_lo | y_hi | z_lo | z_hi);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, r_ths,
-                                           threshold & ~LIS302DL_FFWUTHS_DCRM);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, r_duration, duration);
-
-       /* Route the interrupt for wakeup */
-       __lis302dl_int_mode(lis->dev, which, intmode);
+       lis->wakeup.threshold = threshold;
 
-       /* Power up the device and note that we want to wake up from
-        * this interrupt */
-       if (!(lis->flags & LIS302DL_F_IRQ_WAKE))
+       if (!(lis->flags & LIS302DL_F_IRQ_WAKE)) {
                enable_irq_wake(lis->pdata->interrupt);
-
-       lis->flags |= flag_mask | LIS302DL_F_IRQ_WAKE;
-       __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD,
-                       LIS302DL_CTRL1_PD);
-       local_irq_restore(flags);
+               lis->flags |= LIS302DL_F_IRQ_WAKE;
+       }
 
        return count;
 }
 
-static ssize_t set_freefall_1(struct device *dev, struct device_attribute 
*attr,
-                        const char *buf, size_t count)
-{
-       return set_freefall_common(1, dev, attr, buf, count);
-}
-static ssize_t set_freefall_2(struct device *dev, struct device_attribute 
*attr,
-                        const char *buf, size_t count)
+static ssize_t show_wakeup_threshold(struct device *dev,
+               struct device_attribute *attr, char *buf)
 {
-       return set_freefall_common(2, dev, attr, buf, count);
+       struct lis302dl_info *lis = dev_get_drvdata(dev);
+
+       /* All events off? */
+       if (lis->wakeup.threshold == 0)
+               return sprintf(buf, "off\n");
+
+       return sprintf(buf, "%u\n", lis->wakeup.threshold);
 }
 
+static DEVICE_ATTR(wakeup_threshold, S_IRUGO | S_IWUSR, show_wakeup_threshold,
+               set_wakeup_threshold);
 
-static ssize_t show_freefall_common(int which, struct device *dev,
-               struct device_attribute *attr, char *buf)
+static ssize_t set_wakeup_duration(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
 {
        struct lis302dl_info *lis = dev_get_drvdata(dev);
-       u_int8_t duration;
-       u_int8_t threshold;
-       u_int8_t config;
-       u_int8_t r4;
-       u_int8_t r5;
-       int r_ths = LIS302DL_REG_FF_WU_THS_1; /* registers, assume first pin */
-       int r_duration = LIS302DL_REG_FF_WU_DURATION_1;
-       int r_cfg = LIS302DL_REG_FF_WU_CFG_1;
-       int r_src = LIS302DL_REG_FF_WU_SRC_1;
-       unsigned long flags;
-       int ms;
-
-       /* Configure second freefall/wakeup pin */
-       if (which == 2) {
-               r_ths = LIS302DL_REG_FF_WU_THS_2;
-               r_duration = LIS302DL_REG_FF_WU_DURATION_2;
-               r_cfg = LIS302DL_REG_FF_WU_CFG_2;
-               r_src = LIS302DL_REG_FF_WU_SRC_2;
-       }
+       unsigned int duration;
 
-       local_irq_save(flags);
-       config = (lis->pdata->lis302dl_bitbang_reg_read)(lis, r_cfg);
-       threshold = (lis->pdata->lis302dl_bitbang_reg_read)(lis, r_ths);
-       duration = (lis->pdata->lis302dl_bitbang_reg_read)(lis, r_duration);
-       r4 = (lis->pdata->lis302dl_bitbang_reg_read)(lis, r_src);
-       r5 = (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_CTRL3);
-       ms = __freefall_duration_to_ms(lis, duration);
-       local_irq_restore(flags);
+       if (sscanf(buf, "%u\n", &duration) != 1)
+               return -EINVAL;
 
-       /* All events off? */
-       if ((config & (LIS302DL_FFWUCFG_XLIE | LIS302DL_FFWUCFG_XHIE |
-                       LIS302DL_FFWUCFG_YLIE | LIS302DL_FFWUCFG_YHIE |
-                       LIS302DL_FFWUCFG_ZLIE | LIS302DL_FFWUCFG_ZHIE)) == 0)
-               return sprintf(buf, "off\n");
+       if (duration > 2550)
+               return -ERANGE;
 
+       lis->wakeup.duration = duration;
 
-       return sprintf(buf,
-                       "%s events, %s interrupt, duration %d, threshold %d, "
-                       "enabled: %s %s %s %s %s %s\n",
-                       (config & LIS302DL_FFWUCFG_AOI) == 0 ? "or" : "and",
-                       (config & LIS302DL_FFWUCFG_LIR) == 0 ?
-                                                       "don't latch" : "latch",
-                       ms, threshold,
-                       (config & LIS302DL_FFWUCFG_XLIE) == 0 ? "---" : "xlo",
-                       (config & LIS302DL_FFWUCFG_XHIE) == 0 ? "---" : "xhi",
-                       (config & LIS302DL_FFWUCFG_YLIE) == 0 ? "---" : "ylo",
-                       (config & LIS302DL_FFWUCFG_YHIE) == 0 ? "---" : "yhi",
-                       (config & LIS302DL_FFWUCFG_ZLIE) == 0 ? "---" : "zlo",
-                       (config & LIS302DL_FFWUCFG_ZHIE) == 0 ? "---" : "zhi");
+       return count;
 }
 
-static ssize_t show_freefall_1(struct device *dev,
+static ssize_t show_wakeup_duration(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
-       return show_freefall_common(1, dev, attr, buf);
-}
+       struct lis302dl_info *lis = dev_get_drvdata(dev);
 
-static ssize_t show_freefall_2(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       return show_freefall_common(2, dev, attr, buf);
+       return sprintf(buf, "%u\n", lis->wakeup.duration);
 }
 
-static DEVICE_ATTR(freefall_wakeup_1, S_IRUGO | S_IWUSR, show_freefall_1,
-                                                               set_freefall_1);
-static DEVICE_ATTR(freefall_wakeup_2, S_IRUGO | S_IWUSR, show_freefall_2,
-                                                               set_freefall_2);
+static DEVICE_ATTR(wakeup_duration, S_IRUGO | S_IWUSR, show_wakeup_duration,
+               set_wakeup_duration);
 
 static struct attribute *lis302dl_sysfs_entries[] = {
        &dev_attr_sample_rate.attr,
        &dev_attr_full_scale.attr,
+       &dev_attr_threshold.attr,
+       &dev_attr_duration.attr,
        &dev_attr_dump.attr,
-       &dev_attr_freefall_wakeup_1.attr,
-       &dev_attr_freefall_wakeup_2.attr,
+       &dev_attr_wakeup_threshold.attr,
+       &dev_attr_wakeup_duration.attr,
        NULL
 };
 
@@ -452,14 +522,11 @@ static struct attribute_group lis302dl_attr_group = {
 static int lis302dl_input_open(struct input_dev *inp)
 {
        struct lis302dl_info *lis = input_get_drvdata(inp);
-       u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen |
-                        LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen;
        unsigned long flags;
 
        local_irq_save(flags);
-       /* make sure we're powered up and generate data ready */
-       __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1);
 
+       __enable_data_collection(lis);
        lis->flags |= LIS302DL_F_INPUT_OPEN;
 
        local_irq_restore(flags);
@@ -499,11 +566,11 @@ static int __lis302dl_reset_device(struct lis302dl_info 
*lis)
 {
        int timeout = 10;
 
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL2,
-                                     LIS302DL_CTRL2_BOOT | LIS302DL_CTRL2_FDS);
+       __reg_write(lis, LIS302DL_REG_CTRL2,
+                       LIS302DL_CTRL2_BOOT | LIS302DL_CTRL2_FDS);
 
-       while (((lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_CTRL2)
-                                         & LIS302DL_CTRL2_BOOT) && (timeout--))
+       while ((__reg_read(lis, LIS302DL_REG_CTRL2)
+                       & LIS302DL_CTRL2_BOOT) && (timeout--))
                mdelay(1);
 
        return !!(timeout < 0);
@@ -521,26 +588,12 @@ static int __devinit lis302dl_probe(struct 
platform_device *pdev)
        if (!lis)
                return -ENOMEM;
 
-       local_irq_save(flags);
-
        lis->dev = &pdev->dev;
 
        dev_set_drvdata(lis->dev, lis);
 
        lis->pdata = pdata;
 
-       /* Configure our IO */
-       (lis->pdata->lis302dl_suspend_io)(lis, 1);
-
-       wai = (lis->pdata->lis302dl_bitbang_reg_read)(lis,
-                                                        LIS302DL_REG_WHO_AM_I);
-       if (wai != LIS302DL_WHO_AM_I_MAGIC) {
-               dev_err(lis->dev, "unknown who_am_i signature 0x%02x\n", wai);
-               dev_set_drvdata(lis->dev, NULL);
-               rc = -ENODEV;
-               goto bail_free_lis;
-       }
-
        rc = sysfs_create_group(&lis->dev->kobj, &lis302dl_attr_group);
        if (rc) {
                dev_err(lis->dev, "error creating sysfs group\n");
@@ -554,15 +607,6 @@ static int __devinit lis302dl_probe(struct platform_device 
*pdev)
                goto bail_sysfs;
        }
 
-       set_bit(EV_REL, lis->input_dev->evbit);
-       set_bit(REL_X, lis->input_dev->relbit);
-       set_bit(REL_Y, lis->input_dev->relbit);
-       set_bit(REL_Z, lis->input_dev->relbit);
-/*     set_bit(EV_KEY, lis->input_dev->evbit);
-       set_bit(BTN_X, lis->input_dev->keybit);
-       set_bit(BTN_Y, lis->input_dev->keybit);
-       set_bit(BTN_Z, lis->input_dev->keybit);
-*/
        input_set_drvdata(lis->input_dev, lis);
        lis->input_dev->name = pdata->name;
         /* SPI Bus not defined as a valid bus for input subsystem*/
@@ -576,50 +620,68 @@ static int __devinit lis302dl_probe(struct 
platform_device *pdev)
                goto bail_inp_dev;
        }
 
+       local_irq_save(flags);
+       /* Configure our IO */
+       (lis->pdata->lis302dl_suspend_io)(lis, 1);
+
+       wai = __reg_read(lis, LIS302DL_REG_WHO_AM_I);
+       if (wai != LIS302DL_WHO_AM_I_MAGIC) {
+               dev_err(lis->dev, "unknown who_am_i signature 0x%02x\n", wai);
+               dev_set_drvdata(lis->dev, NULL);
+               rc = -ENODEV;
+               goto bail_sysfs;
+       }
+
+       set_bit(EV_REL, lis->input_dev->evbit);
+       set_bit(REL_X, lis->input_dev->relbit);
+       set_bit(REL_Y, lis->input_dev->relbit);
+       set_bit(REL_Z, lis->input_dev->relbit);
+/*     set_bit(EV_KEY, lis->input_dev->evbit);
+       set_bit(BTN_X, lis->input_dev->keybit);
+       set_bit(BTN_Y, lis->input_dev->keybit);
+       set_bit(BTN_Z, lis->input_dev->keybit);
+*/
+       lis->threshold = 0;
+       lis->duration = 0;
+       memset(&lis->wakeup, 0, sizeof(lis->wakeup));
+
        if (__lis302dl_reset_device(lis))
                dev_err(lis->dev, "device BOOT reload failed\n");
 
        /* force us powered */
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL1,
-                                                     LIS302DL_CTRL1_PD |
-                                                     LIS302DL_CTRL1_Xen |
-                                                     LIS302DL_CTRL1_Yen |
-                                                     LIS302DL_CTRL1_Zen);
+       __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD |
+                       LIS302DL_CTRL1_Xen |
+                       LIS302DL_CTRL1_Yen |
+                       LIS302DL_CTRL1_Zen);
        mdelay(1);
 
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL2, 0);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL3,
-                                    LIS302DL_CTRL3_PP_OD | LIS302DL_CTRL3_IHL);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis,
-                                                LIS302DL_REG_FF_WU_THS_1, 
0x14);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis,
-                                          LIS302DL_REG_FF_WU_DURATION_1, 0x00);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis,
-                                                LIS302DL_REG_FF_WU_CFG_1, 0x0);
+       __reg_write(lis, LIS302DL_REG_CTRL2, 0);
+       __reg_write(lis, LIS302DL_REG_CTRL3,
+                       LIS302DL_CTRL3_PP_OD | LIS302DL_CTRL3_IHL);
+       __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, 0x0);
+       __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, 0x00);
+       __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, 0x0);
 
        /* start off in powered down mode; we power up when someone opens us */
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL1,
-                                                       LIS302DL_CTRL1_Xen |
-                                                       LIS302DL_CTRL1_Yen |
-                                                       LIS302DL_CTRL1_Zen);
+       __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_Xen |
+                       LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen);
 
        if (pdata->open_drain)
                /* switch interrupt to open collector, active-low */
-               (lis->pdata->lis302dl_bitbang_reg_write)(lis,
-                       LIS302DL_REG_CTRL3, LIS302DL_CTRL3_PP_OD |
-                                           LIS302DL_CTRL3_IHL);
+               __reg_write(lis, LIS302DL_REG_CTRL3,
+                               LIS302DL_CTRL3_PP_OD | LIS302DL_CTRL3_IHL);
        else
                /* push-pull, active-low */
-               (lis->pdata->lis302dl_bitbang_reg_write)(lis,
-                                       LIS302DL_REG_CTRL3, LIS302DL_CTRL3_IHL);
+               __reg_write(lis, LIS302DL_REG_CTRL3, LIS302DL_CTRL3_IHL);
 
-       __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_DATA_READY);
-       __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_DATA_READY);
+       __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_GND);
+       __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_GND);
 
-       (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_STATUS);
-       (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_FF_WU_SRC_1);
-       (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_FF_WU_SRC_2);
-       (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_CLICK_SRC);
+       __reg_read(lis, LIS302DL_REG_STATUS);
+       __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
+       __reg_read(lis, LIS302DL_REG_FF_WU_SRC_2);
+       __reg_read(lis, LIS302DL_REG_CLICK_SRC);
+       local_irq_restore(flags);
 
        dev_info(lis->dev, "Found %s\n", pdata->name);
 
@@ -635,7 +697,6 @@ static int __devinit lis302dl_probe(struct platform_device 
*pdev)
                        lis->pdata->interrupt);
                goto bail_inp_reg;
        }
-       local_irq_restore(flags);
        return 0;
 
 bail_inp_reg:
@@ -655,15 +716,19 @@ static int __devexit lis302dl_remove(struct 
platform_device *pdev)
        struct lis302dl_info *lis = dev_get_drvdata(&pdev->dev);
        unsigned long flags;
 
+       /* Disable interrupts */
+       if (lis->flags & LIS302DL_F_IRQ_WAKE)
+               disable_irq_wake(lis->pdata->interrupt);
+       free_irq(lis->pdata->interrupt, lis);
+
        /* Reset and power down the device */
        local_irq_save(flags);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL3, 0x00);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL2, 0x00);
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL1, 0x00);
+       __reg_write(lis, LIS302DL_REG_CTRL3, 0x00);
+       __reg_write(lis, LIS302DL_REG_CTRL2, 0x00);
+       __reg_write(lis, LIS302DL_REG_CTRL1, 0x00);
        local_irq_restore(flags);
 
        /* Cleanup resources */
-       free_irq(lis->pdata->interrupt, lis);
        sysfs_remove_group(&pdev->dev.kobj, &lis302dl_attr_group);
        input_unregister_device(lis->input_dev);
        if (lis->input_dev)
@@ -703,8 +768,7 @@ static int lis302dl_suspend(struct platform_device *pdev, 
pm_message_t state)
        int n;
 
        /* determine if we want to wake up from the accel. */
-       if (lis->flags & LIS302DL_F_WUP_FF ||
-               lis->flags & LIS302DL_F_WUP_CLICK)
+       if (lis->flags & LIS302DL_F_WUP_CLICK)
                return 0;
 
        disable_irq(lis->pdata->interrupt);
@@ -722,13 +786,16 @@ static int lis302dl_suspend(struct platform_device *pdev, 
pm_message_t state)
        /* save registers */
        for (n = 0; n < ARRAY_SIZE(regs_to_save); n++)
                lis->regs[regs_to_save[n]] =
-                       (lis->pdata->lis302dl_bitbang_reg_read)(lis,
-                                                               
regs_to_save[n]);
+                       __reg_read(lis, regs_to_save[n]);
+
+       /* power down or enable wakeup */
 
-       /* power down */
-       tmp = (lis->pdata->lis302dl_bitbang_reg_read)(lis, LIS302DL_REG_CTRL1);
-       tmp &= ~LIS302DL_CTRL1_PD;
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL1, tmp);
+       if (lis->wakeup.threshold == 0) {
+               tmp = __reg_read(lis, LIS302DL_REG_CTRL1);
+               tmp &= ~LIS302DL_CTRL1_PD;
+               __reg_write(lis, LIS302DL_REG_CTRL1, tmp);
+       } else
+               __enable_wakeup(lis);
 
        /* place our IO to the device in sleep-compatible states */
        (lis->pdata->lis302dl_suspend_io)(lis, 0);
@@ -744,8 +811,7 @@ static int lis302dl_resume(struct platform_device *pdev)
        unsigned long flags;
        int n;
 
-       if (lis->flags & LIS302DL_F_WUP_FF ||
-               lis->flags & LIS302DL_F_WUP_CLICK)
+       if (lis->flags & LIS302DL_F_WUP_CLICK)
                return 0;
 
        local_irq_save(flags);
@@ -754,25 +820,24 @@ static int lis302dl_resume(struct platform_device *pdev)
        (lis->pdata->lis302dl_suspend_io)(lis, 1);
 
        /* resume from powerdown first! */
-       (lis->pdata->lis302dl_bitbang_reg_write)(lis, LIS302DL_REG_CTRL1,
-                                                     LIS302DL_CTRL1_PD |
-                                                     LIS302DL_CTRL1_Xen |
-                                                     LIS302DL_CTRL1_Yen |
-                                                     LIS302DL_CTRL1_Zen);
+       __reg_write(lis, LIS302DL_REG_CTRL1,
+                       LIS302DL_CTRL1_PD |
+                       LIS302DL_CTRL1_Xen |
+                       LIS302DL_CTRL1_Yen |
+                       LIS302DL_CTRL1_Zen);
        mdelay(1);
 
        if (__lis302dl_reset_device(lis))
                dev_err(&pdev->dev, "device BOOT reload failed\n");
 
-       lis->regs[LIS302DL_REG_CTRL1] |=        LIS302DL_CTRL1_PD |
+       lis->regs[LIS302DL_REG_CTRL1] |=        LIS302DL_CTRL1_PD |
                                                LIS302DL_CTRL1_Xen |
                                                LIS302DL_CTRL1_Yen |
                                                LIS302DL_CTRL1_Zen;
 
        /* restore registers after resume */
        for (n = 0; n < ARRAY_SIZE(regs_to_save); n++)
-               (lis->pdata->lis302dl_bitbang_reg_write)(lis,
-                               regs_to_save[n], lis->regs[regs_to_save[n]]);
+               __reg_write(lis, regs_to_save[n], lis->regs[regs_to_save[n]]);
 
        local_irq_restore(flags);
        enable_irq(lis->pdata->interrupt);
diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h
index 4578db4..662ac60 100644
--- a/include/linux/lis302dl.h
+++ b/include/linux/lis302dl.h
@@ -16,7 +16,8 @@ struct lis302dl_platform_data {
        unsigned long pin_miso;
        int open_drain;
        int interrupt;
-       void (*lis302dl_bitbang_read_sample)(struct lis302dl_info *);
+       void (*lis302dl_bitbang)(struct lis302dl_info *lis, u8 *tx,
+                       int tx_bytes, u8 *rx, int rx_bytes);
        void (*lis302dl_suspend_io)(struct lis302dl_info *, int resuming);
        int (*lis302dl_bitbang_reg_read)(struct lis302dl_info *, u8 reg);
        void (*lis302dl_bitbang_reg_write)(struct lis302dl_info *, u8 reg,
@@ -28,6 +29,12 @@ struct lis302dl_info {
        struct device *dev;
        struct input_dev *input_dev;
        unsigned int flags;
+       unsigned int threshold;
+       unsigned int duration;
+       struct {
+               unsigned int threshold; /* mg */
+               unsigned int duration;  /* ms */
+       } wakeup;
        u_int8_t regs[0x40];
 };
 
@@ -140,6 +147,7 @@ enum lis302dl_reg_cloik_src {
 #define LIS302DL_F_FS                  0x0020  /* ADC full scale */
 #define LIS302DL_F_INPUT_OPEN  0x0040  /* Set if input device is opened */
 #define LIS302DL_F_IRQ_WAKE    0x0080  /* IRQ is setup in wake mode */
+#define LIS302DL_F_DR                  0x0100  /* Data rate, 400Hz/100Hz */
 
 
 #endif /* _LINUX_LIS302DL_H */

Reply via email to