[PATCH 2/2]: lis302dl-port-work-from-stable.patch

Simon Kagstrom simon.kagstrom at gmail.com
Sat Nov 15 17:25:26 CET 2008


Merge in the accelerometer functionality from stable

From: Simon Kagstrom <simon.kagstrom at 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 at gmail.com>
---

 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 <laforge at openmoko.org>
  *         converted to private bitbang by:
  *         Andy Green <andy at openmoko.com>
+ *         ability to set acceleration threshold added by:
+ *         Simon Kagstrom <simon.kagstrom at 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 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 */



More information about the openmoko-kernel mailing list