[PATCH] motion-sensors-irq-spi.patch

Wolfgang Spraul wolfgang at openmoko.com
Mon Jan 28 14:16:20 CET 2008


Andy, Werner -

today Tony and I wondered whether Motion Sensor activity can wakeup  
the CPU from deep sleep.
Right now we believe wakeup from deep sleep CPU can be caused by:

1. wifi
2. GSM
3. usb

Anything else? We think bluetooth, motion sensors cannot.
Wolfgang

On Jan 28, 2008, at 8:14 PM, Andy Green wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Folks with this patch motion sensors are visibly working via hexdump  
> and
> the work queue is discarded for a private IRQ-safe bitbang
> implementation in the ISR.
>
> Werner this would be good to have if you don't see a serious problem,
> since the current tree is slowed to a crawl by the existing level
> sensitive interrupt stuff.
>
> - -Andy
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.7 (GNU/Linux)
> Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org
>
> iD8DBQFHnccpOjLpvpq7dMoRAu1uAJ9HIqgxWp+Q11xGOCIPdygUuBiLNACfbLSs
> WKbKhPpNkGZR69GXvwEKhW4=
> =AYjf
> -----END PGP SIGNATURE-----
> motion-sensors-irq-spi.patch
>
> From: Andy Green <andy at openmoko.com>
>
> This patch removes the workqueue method originally implemented for
> motion sensor service and replaces with with a platform-defined
> callback which is used in the ISR.
>
> An implementation for the callback is also provided which performs
> IRQ-safe SPI bitbang at high speed to service the motion sensor
> and insert the x y z data into the input queue.
>
> Interrupts are changed to edge trigger from level removing the
> sluggish behaviour.
>
> Interrupts are serviced in typ 75 - 100us without involving the
> scheduler now.
>
> The input event interface was broken, this also fixes it.
>
> # cat /proc/bus/input/devices
> ...
> I: Bus=0018 Vendor=0000 Product=0000 Version=0000
> N: Name="lis302-1 (top)"
> P: Phys=
> S: Sysfs=/devices/virtual/input/input2
> U: Uniq=
> H: Handlers=event2
> B: EV=5
> B: REL=7
>
> I: Bus=0018 Vendor=0000 Product=0000 Version=0000
> N: Name="lis302-2 (bottom)"
> P: Phys=
> S: Sysfs=/devices/virtual/input/input3
> U: Uniq=
> H: Handlers=event3
> B: EV=5
> B: REL=7
>
> Data is scaled to be report in mG, so 1000 = 1G.
>
> Data should be reported at 100 sps per axis.
> You can sample the X Y Z data easily through hexdump.
> Hexdump on a serial terminal can't keep up with the
> data, so it is not shown in order.
>
> # hexdump /dev/input/event2
> ...
> 00009c0 55d7 478e 70bd 000d 0002 0000 ffdc ffff
> 00009d0 55d7 478e 712d 000d 0002 0002 03de 0000
> 00009e0 55d7 478e 714b 000d 0000 0000 0000 0000
> 00009f0 55d7 478e 976b 000d 0002 0000 ffee ffff
> 0000a00 55d7 478e 97c9 000d 0002 0001 0012 0000
> 0000a10 55d7 478e 97f2 000d 0002 0002 03de 0000
> 0000a20 55d7 478e 980f 000d 0000 0000 0000 0000
>
> The first 8 bytes are timestamp info.
> The next 16-bit word is EV_REL to show it is relative
> coordinate data.
> The next word is 0000=X, 0001=Y, 0002=Z
> The last 32 bits on each line is the sample data, in
> signed milli-G.
>
> You can single out just one coordinate for rough testing
> like this
>
> # hexdump /dev/input/event3 | grep ".* .* .* .* .* 0002 0002 .* .*$"  
> | cut -d' ' -f8
> 03a8
> 03ba
> 03a8
> 03a8
> 03ba
> 03a8
> 03a8
> 03a8
> 03ba
> ...
>
> Due to tolerances 1G is reported as ~0x3ba milli-G == 954mG
> on the sensor I looked at.  Or it could be the alien space
> drive I mounted on my roof.
>
> Signed-off-by: Andy Green <andy at openmoko.com>
> ---
>
> arch/arm/mach-s3c2440/mach-gta02.c |   82 ++++++++++++
> drivers/input/misc/lis302dl.c      |  246 ++++++++ 
> +---------------------------
> include/linux/lis302dl.h           |  100 +++++++++++++++
> 3 files changed, 242 insertions(+), 186 deletions(-)
>
>
> diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach- 
> s3c2440/mach-gta02.c
> index 1b682ec..c952a76 100644
> --- a/arch/arm/mach-s3c2440/mach-gta02.c
> +++ b/arch/arm/mach-s3c2440/mach-gta02.c
> @@ -525,13 +525,93 @@ static struct platform_device gta01_led_dev = {
>
> /* SPI: Accelerometers attached to SPI of s3c244x */
>
> -static const struct lis302dl_platform_data lis302_pdata[] = {
> +/*
> + * Situation is that Linux SPI can't work in an interrupt context,  
> so we
> + * implement our own bitbang here.  Arbitration is needed because  
> not only
> + * can this interrupt happen at any time even if foreground wants  
> to use
> + * the bitbang API from Linux, but multiple motion sensors can be  
> on the
> + * same SPI bus, and multiple interrupts can happen.
> + *
> + * Foreground / interrupt arbitration is okay because the  
> interrupts are
> + * disabled around all the foreground SPI code.
> + *
> + * Interrupt / Interrupt arbitration is okay because the interrupts  
> are
> + * at the same priority (I think)
> + *
> + * Servicing is typ 75 - 100us at 400MHz.
> + */
> +
> +/* #define DEBUG_SPEW_MS */
> +#define MG_PER_SAMPLE 18
> +
> +void gat02_lis302dl_bitbang_read(struct lis302dl_info *lis)
> +{
> +	struct lis302dl_platform_data *pdata = lis->pdata;
> +	u8 shifter = 0xc0 | LIS302DL_REG_OUT_X; /* read, autoincrement */
> +	int n, n1;
> +#ifdef DEBUG_SPEW_MS
> +	s8 x, y, z;
> +#endif
> +
> +	s3c2410_gpio_setpin(pdata->pin_chip_select, 0);
> +	for (n = 0; n < 8; n++) { /* write the r/w, inc and address */
> +		s3c2410_gpio_setpin(pdata->pin_clk, 0);
> +		s3c2410_gpio_setpin(pdata->pin_mosi, (shifter >> 7) & 1);
> +		s3c2410_gpio_setpin(pdata->pin_clk, 1);
> +		shifter <<= 1;
> +	}
> +	for (n = 0; n < 5; n++) { /* 5 consequetive registers */
> +		for (n1 = 0; n1 < 8; n1++) { /* 8 bits each */
> +			s3c2410_gpio_setpin(pdata->pin_clk, 0);
> +			s3c2410_gpio_setpin(pdata->pin_clk, 1);
> +			shifter <<= 1;
> +			if (s3c2410_gpio_getpin(pdata->pin_miso))
> +				shifter |= 1;
> +		}
> +		switch (n) {
> +		case 0:
> +#ifdef DEBUG_SPEW_MS
> +			x = shifter;
> +#endif
> +			input_report_rel(lis->input_dev, REL_X, MG_PER_SAMPLE *  
> (s8)shifter);
> +			break;
> +		case 2:
> +#ifdef DEBUG_SPEW_MS
> +			y = shifter;
> +#endif
> +			input_report_rel(lis->input_dev, REL_Y, MG_PER_SAMPLE *  
> (s8)shifter);
> +			break;
> +		case 4:
> +#ifdef DEBUG_SPEW_MS
> +			z = shifter;
> +#endif
> +			input_report_rel(lis->input_dev, REL_Z, MG_PER_SAMPLE *  
> (s8)shifter);
> +			break;
> +		}
> +	}
> +	s3c2410_gpio_setpin(pdata->pin_chip_select, 1);
> +	input_sync(lis->input_dev);
> +#ifdef DEBUG_SPEW_MS
> +	printk("%s: %d %d %d\n", pdata->name, x, y, z);
> +#endif
> +}
> +
> +
> +const struct lis302dl_platform_data lis302_pdata[] = {
> 	{
> 		.name		= "lis302-1 (top)",
> 		.pin_chip_select= S3C2410_GPD12,
> +		.pin_clk	= S3C2410_GPG7,
> +		.pin_mosi	= S3C2410_GPG6,
> +		.pin_miso	= S3C2410_GPG5,
> +		.lis302dl_bitbang_read = gat02_lis302dl_bitbang_read,
> 	}, {
> 		.name		= "lis302-2 (bottom)",
> 		.pin_chip_select= S3C2410_GPD13,
> +		.pin_clk	= S3C2410_GPG7,
> +		.pin_mosi	= S3C2410_GPG6,
> +		.pin_miso	= S3C2410_GPG5,
> +		.lis302dl_bitbang_read = gat02_lis302dl_bitbang_read,
> 	},
> };
>
> diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/ 
> lis302dl.c
> index 2cb2921..4ab66c4 100644
> --- a/drivers/input/misc/lis302dl.c
> +++ b/drivers/input/misc/lis302dl.c
> @@ -33,110 +33,17 @@
> #include <linux/device.h>
> #include <linux/platform_device.h>
> #include <linux/delay.h>
> -#include <linux/input.h>
> #include <linux/irq.h>
> #include <linux/interrupt.h>
> #include <linux/sysfs.h>
>
> #include <linux/lis302dl.h>
>
> -#include <linux/spi/spi.h>
> -
> -#define LIS302DL_WHO_AM_I_MAGIC		0x3b
> -
> -enum lis302dl_reg {
> -	LIS302DL_REG_WHO_AM_I		= 0x0f,
> -	LIS302DL_REG_CTRL1		= 0x20,
> -	LIS302DL_REG_CTRL2		= 0x21,
> -	LIS302DL_REG_CTRL3		= 0x22,
> -	LIS302DL_REG_HP_FILTER_RESET	= 0x23,
> -	LIS302DL_REG_STATUS		= 0x27,
> -	LIS302DL_REG_OUT_X		= 0x29,
> -	LIS302DL_REG_OUT_Y		= 0x2b,
> -	LIS302DL_REG_OUT_Z		= 0x2d,
> -	LIS302DL_REG_FF_WU_CFG_1	= 0x30,
> -	LIS302DL_REG_FF_WU_SRC_1	= 0x31,
> -	LIS302DL_REG_FF_WU_THS_1	= 0x32,
> -	LIS302DL_REG_FF_WU_DURATION_1	= 0x33,
> -	LIS302DL_REG_FF_WU_CFG_2	= 0x34,
> -	LIS302DL_REG_FF_WU_SRC_2	= 0x35,
> -	LIS302DL_REG_FF_WU_THS_2	= 0x36,
> -	LIS302DL_REG_FF_WU_DURATION_2	= 0x37,
> -	LIS302DL_REG_CLICK_CFG		= 0x38,
> -	LIS302DL_REG_CLICK_SRC		= 0x39,
> -	LIS302DL_REG_CLICK_THSY_X	= 0x3b,
> -	LIS302DL_REG_CLICK_THSZ		= 0x3c,
> -	LIS302DL_REG_CLICK_TIME_LIMIT	= 0x3d,
> -	LIS302DL_REG_CLICK_LATENCY	= 0x3e,
> -	LIS302DL_REG_CLICK_WINDOW	= 0x3f,
> -};
> -
> -enum lis302dl_reg_ctrl1 {
> -	LIS302DL_CTRL1_Xen		= 0x01,
> -	LIS302DL_CTRL1_Yen		= 0x02,
> -	LIS302DL_CTRL1_Zen		= 0x04,
> -	LIS302DL_CTRL1_STM		= 0x08,
> -	LIS302DL_CTRL1_STP		= 0x10,
> -	LIS302DL_CTRL1_FS		= 0x20,
> -	LIS302DL_CTRL1_PD		= 0x40,
> -	LIS302DL_CTRL1_DR		= 0x80,
> -};
> -
> -enum lis302dl_reg_ctrl3 {
> -	LIS302DL_CTRL3_PP_OD		= 0x40,
> -	LIS302DL_CTRL3_IHL		= 0x80,
> -};
> -
> -enum lis302dl_reg_status {
> -	LIS302DL_STATUS_XDA		= 0x01,
> -	LIS302DL_STATUS_YDA		= 0x02,
> -	LIS302DL_STATUS_ZDA		= 0x04,
> -	LIS302DL_STATUS_XYZDA		= 0x08,
> -	LIS302DL_STATUS_XOR		= 0x10,
> -	LIS302DL_STATUS_YOR		= 0x20,
> -	LIS302DL_STATUS_ZOR		= 0x40,
> -	LIS302DL_STATUS_XYZOR		= 0x80,
> -};
> -
> -enum lis302dl_reg_ffwusrc1 {
> -	LIS302DL_FFWUSRC1_XL		= 0x01,
> -	LIS302DL_FFWUSRC1_XH		= 0x02,
> -	LIS302DL_FFWUSRC1_YL		= 0x04,
> -	LIS302DL_FFWUSRC1_YH		= 0x08,
> -	LIS302DL_FFWUSRC1_ZL		= 0x10,
> -	LIS302DL_FFWUSRC1_ZH		= 0x20,
> -	LIS302DL_FFWUSRC1_IA		= 0x40,
> -};
> -
> -enum lis302dl_reg_cloik_src {
> -	LIS302DL_CLICKSRC_SINGLE_X	= 0x01,
> -	LIS302DL_CLICKSRC_DOUBLE_X	= 0x02,
> -	LIS302DL_CLICKSRC_SINGLE_Y	= 0x04,
> -	LIS302DL_CLICKSRC_DOUBLE_Y	= 0x08,
> -	LIS302DL_CLICKSRC_SINGLE_Z	= 0x10,
> -	LIS302DL_CLICKSRC_DOUBLE_Z	= 0x20,
> -	LIS302DL_CLICKSRC_IA		= 0x40,
> -};
> -
> -struct lis302dl_info {
> -	struct spi_device *spi_dev;
> -	struct input_dev *input_dev;
> -	struct mutex lock;
> -	struct work_struct work;
> -	unsigned int flags;
> -	unsigned int working;
> -	u_int8_t regs[0x40];
> -};
> -
> -#define LIS302DL_F_WUP_FF		0x0001	/* wake up from free fall */
> -#define LIS302DL_F_WUP_CLICK		0x0002
> -#define LIS302DL_F_POWER		0x0010
> -#define LIS302DL_F_FS			0x0020 	/* ADC full scale */
> -
> /* lowlevel register access functions */
>
> -#define READ_BIT	0x80
> -#define	ADDR_MASK	0x3f
> +#define READ_BIT		0x80
> +#define READ_BIT_INC_ADS	0xc0
> +#define	ADDR_MASK		0x3f
>
> static u_int8_t __reg_read(struct lis302dl_info *lis, u_int8_t reg)
> {
> @@ -233,7 +140,7 @@ static void lis302dl_int_mode(struct spi_device  
> *spi, int int_pin,
> 		BUG();
> 	}
> }
> -
> +#if 0
> static void _report_btn_single(struct input_dev *inp, int btn)
> {
> 	input_report_key(inp, btn, 1);
> @@ -251,84 +158,14 @@ static void _report_btn_double(struct  
> input_dev *inp, int btn)
> 	input_sync(inp);
> 	input_report_key(inp, btn, 0);
> }
> +#endif
>
> -static void lis302dl_work(struct work_struct *work)
> -{
> -	struct lis302dl_info *lis =
> -			container_of(work, struct lis302dl_info, work);
> -
> -	u_int8_t status, ff_wu_src_1, ff_wu_src_2, click_src;
> -	u_int8_t val_x, val_y, val_z;
> -
> -	lis->working = 1;
> -
> -	status = reg_read(lis, LIS302DL_REG_STATUS);
> -	ff_wu_src_1 = reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
> -	ff_wu_src_2 = reg_read(lis, LIS302DL_REG_FF_WU_SRC_2);
> -	click_src = reg_read(lis, LIS302DL_REG_CLICK_SRC);
> -
> -	if (status & LIS302DL_STATUS_XYZDA) {
> -		val_x = reg_read(lis, LIS302DL_REG_OUT_X);
> -		if (lis->flags & LIS302DL_F_FS)
> -			val_x = val_x << 2;
> -		input_report_rel(lis->input_dev, REL_X, val_x);
> -		val_y = reg_read(lis, LIS302DL_REG_OUT_Y);
> -		if (lis->flags & LIS302DL_F_FS)
> -			val_y = val_y << 2;
> -		input_report_rel(lis->input_dev, REL_Y, val_y);
> -		val_z = reg_read(lis, LIS302DL_REG_OUT_Z);
> -		if (lis->flags & LIS302DL_F_FS)
> -			val_z = val_z << 2;
> -		input_report_rel(lis->input_dev, REL_Z, val_z);
> -//		printk("%p: x=%d, y=%d, z=%d\n", lis, val_x, val_y, val_z);
> -	}
> -	if (status & 0xf0)
> -		dev_dbg(&lis->spi_dev->dev, "overrun!\n");
> -
> -	/* FIXME: implement overrun statistics */
> -
> -	if (ff_wu_src_1 & LIS302DL_FFWUSRC1_IA) {
> -		/* FIXME: free fall interrupt handling */
> -	}
> -
> -	if (click_src & LIS302DL_CLICKSRC_IA) {
> -		if (click_src & LIS302DL_CLICKSRC_SINGLE_X)
> -			_report_btn_single(lis->input_dev, BTN_X);
> -		if (click_src & LIS302DL_CLICKSRC_DOUBLE_X)
> -			_report_btn_double(lis->input_dev, BTN_X);
> -
> -		if (click_src & LIS302DL_CLICKSRC_SINGLE_Y)
> -			_report_btn_single(lis->input_dev, BTN_Y);
> -		if (click_src & LIS302DL_CLICKSRC_DOUBLE_Y)
> -			_report_btn_double(lis->input_dev, BTN_Y);
> -
> -		if (click_src & LIS302DL_CLICKSRC_SINGLE_Z)
> -			_report_btn_single(lis->input_dev, BTN_Z);
> -		if (click_src & LIS302DL_CLICKSRC_DOUBLE_Z)
> -			_report_btn_double(lis->input_dev, BTN_Z);
> -	}
> -
> -	lis->working = 0;
> -	input_sync(lis->input_dev);
> -	put_device(&lis->spi_dev->dev);
> -
> -	enable_irq(lis->spi_dev->irq);
> -}
>
> static irqreturn_t lis302dl_interrupt(int irq, void *_lis)
> {
> 	struct lis302dl_info *lis = _lis;
>
> -	get_device(&lis->spi_dev->dev);
> -
> -	if (!schedule_work(&lis->work))
> -		if (!lis->working)
> -			dev_dbg(&lis->spi_dev->dev, "work item may be lost\n");
> -
> -	/* Disable any further interrupts until we have processed
> -	 * the current one */
> -	disable_irq(lis->spi_dev->irq);
> -
> +	(lis->pdata->lis302dl_bitbang_read)(lis);
> 	return IRQ_HANDLED;
> }
>
> @@ -402,9 +239,16 @@ static int lis302dl_input_open(struct input_dev  
> *inp)
> 	struct lis302dl_info *lis = inp->private;
> 	u_int8_t ctrl1 = LIS302DL_CTRL1_PD | LIS302DL_CTRL1_Xen |
> 			 LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen;
> +	unsigned long flags;
>
> +	local_save_flags(flags);
> 	/* make sure we're powered up and generate data ready */
> 	reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1);
> +	local_irq_restore(flags);
> +
> +	/* kick it off -- since we are edge triggered, if we missed the edge
> +	 * permanent low interrupt is death for us */
> +	(lis->pdata->lis302dl_bitbang_read)(lis);
>
> 	return 0;
> }
> @@ -414,6 +258,9 @@ static void lis302dl_input_close(struct  
> input_dev *inp)
> 	struct lis302dl_info *lis = inp->private;
> 	u_int8_t ctrl1 = LIS302DL_CTRL1_Xen | LIS302DL_CTRL1_Yen |
> 			 LIS302DL_CTRL1_Zen;
> +	unsigned long flags;
> +
> +	local_save_flags(flags);
>
> 	/* since the input core already serializes access and makes sure we
> 	 * only see close() for the close of the last user, we can safely
> @@ -426,6 +273,7 @@ static void lis302dl_input_close(struct  
> input_dev *inp)
> 		reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD,
> 				 0x00);
> 	}
> +	local_irq_restore(flags);
> }
>
> static int __devinit lis302dl_probe(struct spi_device *spi)
> @@ -433,17 +281,22 @@ static int __devinit lis302dl_probe(struct  
> spi_device *spi)
> 	int rc;
> 	struct lis302dl_info *lis;
> 	u_int8_t wai;
> +	unsigned long flags;
> +	struct lis302dl_platform_data *pdata;
>
> 	lis = kzalloc(sizeof(*lis), GFP_KERNEL);
> 	if (!lis)
> 		return -ENOMEM;
>
> +	local_save_flags(flags);
> +
> 	mutex_init(&lis->lock);
> -	INIT_WORK(&lis->work, lis302dl_work);
> 	lis->spi_dev = spi;
>
> 	spi_set_drvdata(spi, lis);
>
> +	pdata = spi->dev.platform_data;
> +
> 	rc = spi_setup(spi);
> 	if (rc < 0) {
> 		dev_err(&spi->dev, "error during spi_setup\n");
> @@ -459,18 +312,10 @@ static int __devinit lis302dl_probe(struct  
> spi_device *spi)
> 		goto bail_free_lis;
> 	}
>
> -	rc = request_irq(lis->spi_dev->irq, lis302dl_interrupt,
> -			 IRQF_TRIGGER_LOW, "lis302dl", lis);
> -	if (rc < 0) {
> -		dev_err(&spi->dev, "error requesting IRQ %d\n",
> -			lis->spi_dev->irq);
> -		goto bail_free_lis;
> -	}
> -
> 	rc = sysfs_create_group(&spi->dev.kobj, &lis302dl_attr_group);
> 	if (rc) {
> 		dev_err(&spi->dev, "error creating sysfs group\n");
> -		goto bail_irq;
> +		goto bail_free_lis;
> 	}
>
> 	/* initialize input layer details */
> @@ -481,14 +326,18 @@ static int __devinit lis302dl_probe(struct  
> spi_device *spi)
> 	}
>
> 	set_bit(EV_REL, lis->input_dev->evbit);
> -	set_bit(EV_KEY, 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->input_dev->private = lis;
> -	lis->input_dev->name = "lis302dl"; /* FIXME: platform data */
> -	lis->input_dev->id.bustype = BUS_I2C; /* FIXME: SPI Bus */
> +	lis->input_dev->name = pdata->name;
> +	 /* SPI Bus not defined as a valid bus for input subsystem*/
> +	lis->input_dev->id.bustype = BUS_I2C; /* lie about it */
> 	lis->input_dev->open = lis302dl_input_open;
> 	lis->input_dev->close = lis302dl_input_close;
>
> @@ -519,25 +368,42 @@ static int __devinit lis302dl_probe(struct  
> spi_device *spi)
> 	reg_read(lis, LIS302DL_REG_FF_WU_SRC_2);
> 	reg_read(lis, LIS302DL_REG_CLICK_SRC);
>
> +	dev_info(&spi->dev, "Found %s\n", pdata->name);
> +
> +	lis->pdata = pdata;
> +
> +	rc = request_irq(lis->spi_dev->irq, lis302dl_interrupt,
> +			 IRQF_TRIGGER_FALLING, "lis302dl", lis);
> +	if (rc < 0) {
> +		dev_err(&spi->dev, "error requesting IRQ %d\n",
> +			lis->spi_dev->irq);
> +		goto bail_inp_reg;
> +	}
> +	local_irq_restore(flags);
> 	return 0;
>
> +bail_inp_reg:
> +	input_unregister_device(lis->input_dev);
> bail_inp_dev:
> 	input_free_device(lis->input_dev);
> bail_sysfs:
> 	sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group);
> -bail_irq:
> -	free_irq(lis->spi_dev->irq, NULL);
> bail_free_lis:
> 	kfree(lis);
> +	local_irq_restore(flags);
> 	return rc;
> }
>
> static int __devexit lis302dl_remove(struct spi_device *spi)
> {
> 	struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
> +	unsigned long flags;
>
> 	/* power down the device */
> +	local_save_flags(flags);
> 	reg_write(lis, LIS302DL_REG_CTRL1, 0x00);
> +	local_irq_restore(flags);
> +
> 	sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group);
> 	input_unregister_device(lis->input_dev);
> 	if (lis->input_dev)
> @@ -552,6 +418,9 @@ static int __devexit lis302dl_remove(struct  
> spi_device *spi)
> static int lis302dl_suspend(struct spi_device *spi, pm_message_t  
> state)
> {
> 	struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
> +	unsigned long flags;
> +
> +	local_save_flags(flags);
>
> 	/* save registers */
> 	lis->regs[LIS302DL_REG_CTRL1] = reg_read(lis, LIS302DL_REG_CTRL1);
> @@ -592,12 +461,17 @@ static int lis302dl_suspend(struct spi_device  
> *spi, pm_message_t state)
> 		reg_write(lis, LIS302DL_REG_CTRL1, tmp);
> 	}
>
> +	local_irq_restore(flags);
> +
> 	return 0;
> }
>
> static int lis302dl_resume(struct spi_device *spi)
> {
> 	struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
> +	unsigned long flags;
> +
> +	local_save_flags(flags);
>
> 	/* restore registers after resume */
> 	reg_write(lis, LIS302DL_REG_CTRL1, lis->regs[LIS302DL_REG_CTRL1]);
> @@ -628,6 +502,8 @@ static int lis302dl_resume(struct spi_device *spi)
> 	reg_write(lis, LIS302DL_REG_CLICK_WINDOW,
> 		  lis->regs[LIS302DL_REG_CLICK_WINDOW]);
>
> +	local_irq_restore(flags);
> +
> 	return 0;
> }
> #else
> diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h
> index e45083a..3612683 100644
> --- a/include/linux/lis302dl.h
> +++ b/include/linux/lis302dl.h
> @@ -2,11 +2,111 @@
> #define _LINUX_LIS302DL_H
>
> #include <linux/types.h>
> +#include <linux/spi/spi.h>
> +#include <linux/input.h>
> +
> +
> +struct lis302dl_info;
>
> struct lis302dl_platform_data {
> 	char *name;
> 	unsigned long pin_chip_select;
> +	unsigned long pin_clk;
> +	unsigned long pin_mosi;
> +	unsigned long pin_miso;
> +	void (*lis302dl_bitbang_read)(struct lis302dl_info *);
> +};
> +
> +struct lis302dl_info {
> +	struct lis302dl_platform_data *pdata;
> +	struct spi_device *spi_dev;
> +	struct input_dev *input_dev;
> +	struct mutex lock;
> +	unsigned int flags;
> +	u_int8_t regs[0x40];
> +};
> +
> +enum lis302dl_reg {
> +	LIS302DL_REG_WHO_AM_I		= 0x0f,
> +	LIS302DL_REG_CTRL1		= 0x20,
> +	LIS302DL_REG_CTRL2		= 0x21,
> +	LIS302DL_REG_CTRL3		= 0x22,
> +	LIS302DL_REG_HP_FILTER_RESET	= 0x23,
> +	LIS302DL_REG_STATUS		= 0x27,
> +	LIS302DL_REG_OUT_X		= 0x29,
> +	LIS302DL_REG_OUT_Y		= 0x2b,
> +	LIS302DL_REG_OUT_Z		= 0x2d,
> +	LIS302DL_REG_FF_WU_CFG_1	= 0x30,
> +	LIS302DL_REG_FF_WU_SRC_1	= 0x31,
> +	LIS302DL_REG_FF_WU_THS_1	= 0x32,
> +	LIS302DL_REG_FF_WU_DURATION_1	= 0x33,
> +	LIS302DL_REG_FF_WU_CFG_2	= 0x34,
> +	LIS302DL_REG_FF_WU_SRC_2	= 0x35,
> +	LIS302DL_REG_FF_WU_THS_2	= 0x36,
> +	LIS302DL_REG_FF_WU_DURATION_2	= 0x37,
> +	LIS302DL_REG_CLICK_CFG		= 0x38,
> +	LIS302DL_REG_CLICK_SRC		= 0x39,
> +	LIS302DL_REG_CLICK_THSY_X	= 0x3b,
> +	LIS302DL_REG_CLICK_THSZ		= 0x3c,
> +	LIS302DL_REG_CLICK_TIME_LIMIT	= 0x3d,
> +	LIS302DL_REG_CLICK_LATENCY	= 0x3e,
> +	LIS302DL_REG_CLICK_WINDOW	= 0x3f,
> +};
> +
> +enum lis302dl_reg_ctrl1 {
> +	LIS302DL_CTRL1_Xen		= 0x01,
> +	LIS302DL_CTRL1_Yen		= 0x02,
> +	LIS302DL_CTRL1_Zen		= 0x04,
> +	LIS302DL_CTRL1_STM		= 0x08,
> +	LIS302DL_CTRL1_STP		= 0x10,
> +	LIS302DL_CTRL1_FS		= 0x20,
> +	LIS302DL_CTRL1_PD		= 0x40,
> +	LIS302DL_CTRL1_DR		= 0x80,
> +};
> +
> +enum lis302dl_reg_ctrl3 {
> +	LIS302DL_CTRL3_PP_OD		= 0x40,
> +	LIS302DL_CTRL3_IHL		= 0x80,
> };
>
> +enum lis302dl_reg_status {
> +	LIS302DL_STATUS_XDA		= 0x01,
> +	LIS302DL_STATUS_YDA		= 0x02,
> +	LIS302DL_STATUS_ZDA		= 0x04,
> +	LIS302DL_STATUS_XYZDA		= 0x08,
> +	LIS302DL_STATUS_XOR		= 0x10,
> +	LIS302DL_STATUS_YOR		= 0x20,
> +	LIS302DL_STATUS_ZOR		= 0x40,
> +	LIS302DL_STATUS_XYZOR		= 0x80,
> +};
> +
> +enum lis302dl_reg_ffwusrc1 {
> +	LIS302DL_FFWUSRC1_XL		= 0x01,
> +	LIS302DL_FFWUSRC1_XH		= 0x02,
> +	LIS302DL_FFWUSRC1_YL		= 0x04,
> +	LIS302DL_FFWUSRC1_YH		= 0x08,
> +	LIS302DL_FFWUSRC1_ZL		= 0x10,
> +	LIS302DL_FFWUSRC1_ZH		= 0x20,
> +	LIS302DL_FFWUSRC1_IA		= 0x40,
> +};
> +
> +enum lis302dl_reg_cloik_src {
> +	LIS302DL_CLICKSRC_SINGLE_X	= 0x01,
> +	LIS302DL_CLICKSRC_DOUBLE_X	= 0x02,
> +	LIS302DL_CLICKSRC_SINGLE_Y	= 0x04,
> +	LIS302DL_CLICKSRC_DOUBLE_Y	= 0x08,
> +	LIS302DL_CLICKSRC_SINGLE_Z	= 0x10,
> +	LIS302DL_CLICKSRC_DOUBLE_Z	= 0x20,
> +	LIS302DL_CLICKSRC_IA		= 0x40,
> +};
> +
> +#define LIS302DL_WHO_AM_I_MAGIC		0x3b
> +
> +#define LIS302DL_F_WUP_FF		0x0001	/* wake up from free fall */
> +#define LIS302DL_F_WUP_CLICK		0x0002
> +#define LIS302DL_F_POWER		0x0010
> +#define LIS302DL_F_FS			0x0020 	/* ADC full scale */
> +
> +
> #endif /* _LINUX_LIS302DL_H */
>





More information about the openmoko-kernel mailing list