[RFC, PATCH 4/4]: Configuring accelerometer wakeups

Simon Kagstrom simon.kagstrom at gmail.com
Mon Aug 11 11:44:33 CEST 2008


lis302dl-configure-wakeup-interrupts.patch

From: Simon Kagstrom <simon.kagstrom at gmail.com>

Add configuration of wakeup/freefall interrupts through a sysfs
interface. Configuration is done through echoing a value of the
form

   X Y Z THRESHOLD DURATION SPEC

to freefall_wakeup_1/2. X, Y and Z are threshold values, given as a
value > 0, < 0 or 0 to specify if an interrupt should be generated for
high or low thresholds or neither (off). THRESHOLD specifies the
threshold that must be exceeded. DURATION specifies the time in
milliseconds for which the acceleration should be measured. SPEC is
either '1' or '0' and specifies if the thresholds should be taken all
together or one at a time ('and' or 'or' mode).

Echoing '0' to the file turns off the interrupts.

Example:

  echo "1 1 1 60 60 0" > freefall_wakeup_1   # Turn on x,y,z, 60ms/60 threshold, or-mode
  echo "0" > freefall_wakeup_1 # Turn off interrupt

Signed-off-by: Simon Kagstrom <simon.kagstrom at gmail.com>
---

 arch/arm/mach-s3c2440/mach-gta02.c |    6 +
 drivers/input/misc/lis302dl.c      |  226 ++++++++++++++++++++++++++++++++++--
 2 files changed, 221 insertions(+), 11 deletions(-)

diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index 75d38f0..055f3e3 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -1099,7 +1099,9 @@ void gta02_lis302dl_bitbang_read(struct lis302dl_info *lis)
 		shifter <<= 1;
 	}
 
-	for (n = 0; n < 5; n++) { /* 5 consequetive registers */
+	/* Read registers OUT_X, OUT_Y and OUT_Z as well
+	 * as FF_WU_SRC_1 and _2 (ack interrupts for freefall) */
+	for (n = 0; n < 13; n++) {
 		for (n1 = 0; n1 < 8; n1++) { /* 8 bits each */
 			s3c2410_gpio_setpin(pdata->pin_clk, 0);
 			s3c2410_gpio_setpin(pdata->pin_clk, 0);
@@ -1128,6 +1130,8 @@ void gta02_lis302dl_bitbang_read(struct lis302dl_info *lis)
 #endif
 			input_report_rel(lis->input_dev, REL_Z, MG_PER_SAMPLE * (s8)shifter);
 			break;
+			/* Do nothing for SRC_1 and SRC_2 - these just need to
+			 * be read to ack the interrupt */
 		}
 	}
 	s3c2410_gpio_setpin(pdata->pin_chip_select, 1);
diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
index 1e9ef36..b7e8784 100644
--- a/drivers/input/misc/lis302dl.c
+++ b/drivers/input/misc/lis302dl.c
@@ -164,7 +164,10 @@ static void _report_btn_double(struct input_dev *inp, int btn)
 static irqreturn_t lis302dl_interrupt(int irq, void *_lis)
 {
 	struct lis302dl_info *lis = _lis;
+	static int cnt;
 
+	printk("Interrupt %d\n", cnt);
+	cnt++;
 	(lis->pdata->lis302dl_bitbang_read)(lis);
 	return IRQ_HANDLED;
 }
@@ -248,10 +251,204 @@ static ssize_t lis302dl_dump(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR(dump, S_IRUGO, lis302dl_dump, NULL);
 
+#define CONFIG_PM
+static int freefall_ms_to_duration(struct lis302dl_info *lis, int ms)
+{
+	u_int8_t r = 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)
+{
+	u_int8_t r = 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 ) {
+
+		return (duration * 25) / 10;
+	}
+	
+	/* Too large value */
+	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)
+{
+	struct lis302dl_info *lis = dev_get_drvdata(dev);
+	int x, y, z;
+	int ms;
+	int threshold;
+	u_int8_t x_lo, y_lo, z_lo;
+	u_int8_t x_hi, y_hi, z_hi;
+	int duration;
+	int and_events;
+	int r_ths, r_duration, r_cfg; /* registers */
+
+	/* First or second freefall/wakeup pin */
+	if (which == 1) {
+		r_ths = LIS302DL_REG_FF_WU_THS_1;
+		r_duration = LIS302DL_REG_FF_WU_DURATION_1;
+		r_cfg = LIS302DL_REG_FF_WU_CFG_1;
+	} else {
+		r_ths = LIS302DL_REG_FF_WU_THS_2;
+		r_duration = LIS302DL_REG_FF_WU_DURATION_2;
+		r_cfg = LIS302DL_REG_FF_WU_CFG_2;
+	}
+
+	/* Parse the input */
+	if (strcmp(buf, "0\n") == 0)
+	{
+		/* Turn off the interrupt */
+		lis->flags &= ~LIS302DL_F_WUP_FF;
+		reg_write(lis, r_ths, 0);
+		reg_write(lis, r_duration, 0);
+		reg_write(lis, r_cfg, 0);
+
+		return count;
+	}
+	
+	if (sscanf(buf, "%d %d %d %d %d %d", &x, &y, &z, &threshold, &ms, &and_events) != 6)
+		return -EINVAL;
+	
+	duration = freefall_ms_to_duration(lis, ms);
+	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 */
+	reg_write(lis, r_cfg, 0); /* First zero to get to a known state */ 
+	reg_write(lis, r_cfg, 
+		(and_events ? LIS302DL_FFWUCFG_AOI : 0) | LIS302DL_FFWUCFG_LIR |
+		x_lo | x_hi | y_lo | y_hi | z_lo | z_hi);
+	reg_write(lis, r_ths, threshold & ~LIS302DL_FFWUTHS_DCRM);
+	reg_write(lis, r_duration, duration);
+
+	printk(KERN_ERR "Setting up %d:%d:%d:%d:%d %d:%d\n",
+			x, y, z, threshold & ~LIS302DL_FFWUTHS_DCRM, duration, x_lo, x_hi);
+
+	/* Route the interrupt for FFWU1 */
+	lis302dl_int_mode(lis->spi_dev, 1, LIS302DL_INTMODE_FF_WU_12);
+	lis302dl_int_mode(lis->spi_dev, 2, LIS302DL_INTMODE_FF_WU_12);
+
+	/* Power up the device and note that we want to wake up from
+	 * this interrupt */
+	lis->flags |= LIS302DL_F_WUP_FF;
+	reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD,
+			LIS302DL_CTRL1_PD);
+
+	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)
+{
+	return set_freefall_common(2, dev, attr, buf, count);
+}
+
+
+static ssize_t show_freefall_common(int which, struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	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, r_cfg, r_duration, r_src;
+
+	/* First or second freefall/wakeup pin */
+	if (which == 1) {
+		r_ths = LIS302DL_REG_FF_WU_THS_1;
+		r_duration = LIS302DL_REG_FF_WU_DURATION_1;
+		r_cfg = LIS302DL_REG_FF_WU_CFG_1;
+		r_src = LIS302DL_REG_FF_WU_SRC_1;
+	} else {
+		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;
+	}
+	config = reg_read(lis, r_cfg);
+	threshold = reg_read(lis, r_ths);
+	duration = reg_read(lis, r_duration);
+	r4 = reg_read(lis, r_src);
+	r5 = reg_read(lis, LIS302DL_REG_CTRL3);
+
+	/* 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");
+
+	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",
+			freefall_duration_to_ms(lis, duration), 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");	
+}
+
+static ssize_t show_freefall_1(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return show_freefall_common(1, dev, attr, buf);
+}
+
+static ssize_t show_freefall_2(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return show_freefall_common(2, dev, attr, buf);
+}
+
+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 struct attribute *lis302dl_sysfs_entries[] = {
 	&dev_attr_sample_rate.attr,
 	&dev_attr_full_scale.attr,
 	&dev_attr_dump.attr,
+	&dev_attr_freefall_wakeup_1.attr,
+	&dev_attr_freefall_wakeup_2.attr,
 	NULL
 };
 
@@ -488,7 +685,15 @@ static int lis302dl_suspend(struct spi_device *spi, pm_message_t state)
 {
 	struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
 	unsigned long flags;
-
+	u_int8_t tmp;
+	
+	/* determine if we want to wake up from the accel. */
+	if (lis->flags & LIS302DL_F_WUP_FF ||
+		lis->flags & LIS302DL_F_WUP_CLICK) {
+		printk(KERN_ERR "Not powering down lis302dl!\n");
+		return 0;
+	}
+	
 	disable_irq(lis->spi_dev->irq);
 	local_save_flags(flags);
 
@@ -530,15 +735,11 @@ static int lis302dl_suspend(struct spi_device *spi, pm_message_t state)
 	lis->regs[LIS302DL_REG_CLICK_WINDOW] =
 				reg_read(lis, LIS302DL_REG_CLICK_WINDOW);
 
-	/* determine if we want to wake up from the accel. */
-	if (!(lis->flags & LIS302DL_F_WUP_FF ||
-	      lis->flags & LIS302DL_F_WUP_CLICK)) {
-		/* power down */
-		u_int8_t tmp;
-		tmp = reg_read(lis, LIS302DL_REG_CTRL1);
-		tmp &= ~LIS302DL_CTRL1_PD;
-		reg_write(lis, LIS302DL_REG_CTRL1, tmp);
-	}
+	/* power down */
+	printk(KERN_ERR "Powering down lis302dl\n");
+	tmp = reg_read(lis, LIS302DL_REG_CTRL1);
+	tmp &= ~LIS302DL_CTRL1_PD;
+	reg_write(lis, LIS302DL_REG_CTRL1, tmp);
 
 	/* place our IO to the device in sleep-compatible states */
 	(lis->pdata->lis302dl_suspend_io)(lis, 0);
@@ -553,6 +754,11 @@ static int lis302dl_resume(struct spi_device *spi)
 	struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
 	unsigned long flags;
 
+	if (lis->flags & LIS302DL_F_WUP_FF ||
+		lis->flags & LIS302DL_F_WUP_CLICK) {
+		printk(KERN_ERR "Not powering up lis302dl!\n");
+		return 0;
+	}
 	local_save_flags(flags);
 
 	/* get our IO to the device back in operational states */




More information about the openmoko-kernel mailing list