aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kagstrom <simon.kagstrom@gmail.com>2008-11-19 17:11:25 +0000
committerAndy Green <agreen@pads.home.warmcat.com>2008-11-19 17:11:25 +0000
commit107b2462fc4872eec18e7fa4a8f498f6f5c5e75d (patch)
treeae861741cb94aa5e69b68ec1d220fc76015a238d
parentc298e74a6c0f6c94f57e40767b5a8be3f2ec314f (diff)
: lis302dl-port-work-from-stable.patch
Merge in the accelerometer functionality from stable From: Simon Kagstrom <simon.kagstrom@gmail.com> 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 <simon.kagstrom@gmail.com>
-rw-r--r--arch/arm/mach-s3c2440/mach-gta02.c35
-rw-r--r--drivers/input/misc/lis302dl.c633
-rw-r--r--include/linux/lis302dl.h10
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 a959f67dc12..ac2e5b615ef 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 489651fe7b0..5a8f362f2af 100644
--- a/drivers/input/misc/lis302dl.c
+++ b/drivers/input/misc/lis302dl.c
@@ -4,6 +4,8 @@
* Author: Harald Welte <laforge@openmoko.org>
* converted to private bitbang by:
* Andy Green <andy@openmoko.com>
+ * ability to set acceleration threshold added by:
+ * Simon Kagstrom <simon.kagstrom@gmail.com>
* 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 4578db4ad6d..662ac60f607 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 */