From 54bb0bfc6105dc7842d31ab67bd6afb6de0a6a28 Mon Sep 17 00:00:00 2001
From: mokopatches <mokopatches@openmoko.org>
Date: Fri, 4 Apr 2008 11:36:01 +0100
Subject: [PATCH] gta02-acc.patch

---
 arch/arm/mach-s3c2410/mach-gta01.c      |    2 +-
 arch/arm/mach-s3c2410/mach-qt2410.c     |    2 +-
 arch/arm/mach-s3c2440/mach-gta02.c      |  181 +++++++++++++++--
 drivers/input/misc/lis302dl.c           |  350 ++++++++++++-------------------
 drivers/spi/spi_s3c24xx_gpio.c          |   13 +-
 include/asm-arm/arch-s3c2410/spi-gpio.h |    3 +-
 include/linux/lis302dl.h                |  102 +++++++++
 7 files changed, 415 insertions(+), 238 deletions(-)

diff --git a/arch/arm/mach-s3c2410/mach-gta01.c b/arch/arm/mach-s3c2410/mach-gta01.c
index 87bb189..0543daa 100644
--- a/arch/arm/mach-s3c2410/mach-gta01.c
+++ b/arch/arm/mach-s3c2410/mach-gta01.c
@@ -510,7 +510,7 @@ static struct spi_board_info gta01_spi_board_info[] = {
 	},
 };
 
-static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs)
+static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int csidx, int cs)
 {
 	switch (cs) {
 	case BITBANG_CS_ACTIVE:
diff --git a/arch/arm/mach-s3c2410/mach-qt2410.c b/arch/arm/mach-s3c2410/mach-qt2410.c
index a1caf4b..6f7b56d 100644
--- a/arch/arm/mach-s3c2410/mach-qt2410.c
+++ b/arch/arm/mach-s3c2410/mach-qt2410.c
@@ -214,7 +214,7 @@ static struct platform_device qt2410_led = {
 
 /* SPI */
 
-static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int cs)
+static void spi_gpio_cs(struct s3c2410_spigpio_info *spi, int csidx, int cs)
 {
 	switch (cs) {
 	case BITBANG_CS_ACTIVE:
diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
index d11da10..3fbb131 100644
--- a/arch/arm/mach-s3c2440/mach-gta02.c
+++ b/arch/arm/mach-s3c2440/mach-gta02.c
@@ -78,6 +78,9 @@
 
 #include <linux/glamofb.h>
 
+/* arbitrates which sensor IRQ owns the shared SPI bus */
+static spinlock_t motion_irq_lock;
+
 static struct map_desc gta02_iodesc[] __initdata = {
 	{
 		.virtual	= 0xe0000000,
@@ -377,8 +380,6 @@ static struct platform_device *gta02_devices[] __initdata = {
 	&s3c_device_usbgadget,
 	&s3c_device_nand,
 	&s3c_device_ts,
-	&s3c_device_spi0,
-	&s3c_device_spi1,
 	&gta02_nor_flash,
 };
 
@@ -478,10 +479,12 @@ static struct spi_board_info gta02_spi_board_info[] = {
 	},
 };
 
+#if 0 /* currently this is not used and we use gpio spi */
 static struct glamo_spi_info glamo_spi_cfg = {
 	.board_size	= ARRAY_SIZE(gta02_spi_board_info),
 	.board_info	= gta02_spi_board_info,
 };
+#endif /* 0 */
 
 static struct glamo_spigpio_info glamo_spigpio_cfg = {
 	.pin_clk	= GLAMO_GPIO10_OUTPUT,
@@ -507,16 +510,99 @@ static struct platform_device gta01_led_dev = {
 
 /* SPI: Accelerometers attached to SPI of s3c244x */
 
-static void gta02_spi_acc_set_cs(struct s3c2410_spi_info *spi, int cs, int pol)
+/*
+ * 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 evidently needed, otherwise we
+ * lose edge-triggered service after a while due to the two sensors sharing
+ * the SPI bus having irqs at the same time eventually.
+ *
+ * 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)
 {
-	s3c2410_gpio_setpin(cs, pol);
+	struct lis302dl_platform_data *pdata = lis->pdata;
+	u8 shifter = 0xc0 | LIS302DL_REG_OUT_X; /* read, autoincrement */
+	int n, n1;
+	unsigned long flags;
+#ifdef DEBUG_SPEW_MS
+	s8 x, y, z;
+#endif
+
+	spin_lock_irqsave(&motion_irq_lock, flags);
+	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);
+	spin_unlock_irqrestore(&motion_irq_lock, flags);
+	input_sync(lis->input_dev);
+#ifdef DEBUG_SPEW_MS
+	printk("%s: %d %d %d\n", pdata->name, x, y, z);
+#endif
 }
 
-static const struct lis302dl_platform_data lis302_pdata[] = {
+
+struct lis302dl_platform_data lis302_pdata[] = {
 	{
-		.name		= "lis302-1 (top)"
+		.name		= "lis302-1 (top)",
+		.pin_chip_select= S3C2410_GPD12,
+		.pin_clk	= S3C2410_GPG7,
+		.pin_mosi	= S3C2410_GPG6,
+		.pin_miso	= S3C2410_GPG5,
+		.open_drain	= 1, /* altered at runtime by PCB rev */
+		.lis302dl_bitbang_read = gat02_lis302dl_bitbang_read,
 	}, {
-		.name		= "lis302-2 (bottom)"
+		.name		= "lis302-2 (bottom)",
+		.pin_chip_select= S3C2410_GPD13,
+		.pin_clk	= S3C2410_GPG7,
+		.pin_mosi	= S3C2410_GPG6,
+		.pin_miso	= S3C2410_GPG5,
+		.open_drain	= 1, /* altered at runtime by PCB rev */
+		.lis302dl_bitbang_read = gat02_lis302dl_bitbang_read,
 	},
 };
 
@@ -525,26 +611,75 @@ static struct spi_board_info gta02_spi_acc_bdinfo[] = {
 		.modalias	= "lis302dl",
 		.platform_data	= &lis302_pdata[0],
 		.irq		= GTA02_IRQ_GSENSOR_1,
-		.max_speed_hz	= 400 * 1000,
+		.max_speed_hz	= 10 * 1000 * 1000,
 		.bus_num	= 1,
-		.chip_select	= S3C2410_GPD12,
+		.chip_select	= 0,
 		.mode		= SPI_MODE_3,
 	},
 	{
 		.modalias	= "lis302dl",
 		.platform_data	= &lis302_pdata[1],
 		.irq		= GTA02_IRQ_GSENSOR_2,
-		.max_speed_hz	= 400 * 1000,
+		.max_speed_hz	= 10 * 1000 * 1000,
 		.bus_num	= 1,
-		.chip_select	= S3C2410_GPD13,
+		.chip_select	= 1,
 		.mode		= SPI_MODE_3,
 	},
 };
 
-static struct s3c2410_spi_info gta02_spi_acc_cfg = {
-	.set_cs		= gta02_spi_acc_set_cs,
+static void spi_acc_cs(struct s3c2410_spigpio_info *spigpio_info,
+		       int csid, int cs)
+{
+	struct lis302dl_platform_data * plat_data =
+				(struct lis302dl_platform_data *)spigpio_info->
+						     board_info->platform_data;
+	switch (cs) {
+	case BITBANG_CS_ACTIVE:
+		s3c2410_gpio_setpin(plat_data[csid].pin_chip_select, 0);
+		break;
+	case BITBANG_CS_INACTIVE:
+		s3c2410_gpio_setpin(plat_data[csid].pin_chip_select, 1);
+		break;
+	}
+}
+
+static struct s3c2410_spigpio_info spi_gpio_cfg = {
+	.pin_clk	= S3C2410_GPG7,
+	.pin_mosi	= S3C2410_GPG6,
+	.pin_miso	= S3C2410_GPG5,
 	.board_size	= ARRAY_SIZE(gta02_spi_acc_bdinfo),
 	.board_info	= gta02_spi_acc_bdinfo,
+	.chip_select	= &spi_acc_cs,
+	.num_chipselect = 2,
+};
+
+static struct resource s3c_spi_acc_resource[] = {
+	[0] = {
+		.start = S3C2410_GPG3,
+		.end   = S3C2410_GPG3,
+	},
+	[1] = {
+		.start = S3C2410_GPG5,
+		.end   = S3C2410_GPG5,
+	},
+	[2] = {
+		.start = S3C2410_GPG6,
+		.end   = S3C2410_GPG6,
+	},
+	[3] = {
+		.start = S3C2410_GPG7,
+		.end   = S3C2410_GPG7,
+	},
+};
+
+static struct platform_device s3c_device_spi_acc = {
+	.name		  = "spi_s3c24xx_gpio",
+	.id		  = 1,
+	.num_resources	  = ARRAY_SIZE(s3c_spi_acc_resource),
+	.resource	  = s3c_spi_acc_resource,
+	.dev = {
+		.platform_data = &spi_gpio_cfg,
+	},
 };
 
 static struct resource gta02_led_resources[] = {
@@ -786,10 +921,21 @@ static void __init gta02_machine_init(void)
 {
 	int rc;
 
+	switch (system_rev) {
+	case GTA02v6_SYSTEM_REV:
+		/* we need push-pull interrupt from motion sensors */
+		lis302_pdata[0].open_drain = 0;
+		lis302_pdata[1].open_drain = 0;
+		break;
+	default:
+		break;
+	}
+
+	spin_lock_init(&motion_irq_lock);
+
 	s3c_device_usb.dev.platform_data = &gta02_usb_info;
 	s3c_device_nand.dev.platform_data = &gta02_nand_info;
 	s3c_device_sdi.dev.platform_data = &gta02_mmc_cfg;
-	s3c_device_spi1.dev.platform_data = &gta02_spi_acc_cfg;
 
 	/* Only GTA02v1 has a SD_DETECT GPIO.  Since the slot is not
 	 * hot-pluggable, this is not required anyway */
@@ -801,6 +947,12 @@ static void __init gta02_machine_init(void)
 		break;
 	}
 
+	/* acc sensor chip selects */
+	s3c2410_gpio_setpin(S3C2410_GPD12, 1);
+	s3c2410_gpio_cfgpin(S3C2410_GPD12, S3C2410_GPIO_OUTPUT);
+	s3c2410_gpio_setpin(S3C2410_GPD13, 1);
+	s3c2410_gpio_cfgpin(S3C2410_GPD13, S3C2410_GPIO_OUTPUT);
+
 	INIT_WORK(&gta02_udc_vbus_drawer.work, __gta02_udc_vbus_draw);
 	s3c24xx_udc_set_platdata(&gta02_udc_cfg);
 	set_s3c2410ts_info(&gta02_ts_cfg);
@@ -829,6 +981,7 @@ static void __init gta02_machine_init(void)
 		break;
 	}
 
+	platform_device_register(&s3c_device_spi_acc);
 	platform_device_register(&gta01_button_dev);
 	platform_device_register(&gta01_pm_gsm_dev);
 
diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
index 45c41c8..36bdcf7 100644
--- a/drivers/input/misc/lis302dl.c
+++ b/drivers/input/misc/lis302dl.c
@@ -33,117 +33,26 @@
 #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,
-};
-
-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	0x01
-#define MS_BIT		0x02
-#define ADDR_SHIFT	2
+#define READ_BIT		0x80
+#define READ_BIT_INC_ADS	0xc0
+#define	ADDR_MASK		0x3f
 
-static inline u_int8_t __reg_read(struct lis302dl_info *lis, u_int8_t reg)
+static u_int8_t __reg_read(struct lis302dl_info *lis, u_int8_t reg)
 {
 	int rc;
 	u_int8_t cmd;
 
-	cmd = (reg << ADDR_SHIFT) | READ_BIT;
+	BUG_ON(reg & ~ADDR_MASK);
+
+	cmd = reg | READ_BIT;
 
 	rc = spi_w8r8(lis->spi_dev, cmd);
 
@@ -161,11 +70,13 @@ static u_int8_t reg_read(struct lis302dl_info *lis, u_int8_t reg)
 	return ret;
 }
 
-static inline int __reg_write(struct lis302dl_info *lis, u_int8_t reg, u_int8_t val)
+static int __reg_write(struct lis302dl_info *lis, u_int8_t reg, u_int8_t val)
 {
 	u_int8_t buf[2];
 
-	buf[0] = (reg << ADDR_SHIFT);
+	BUG_ON(reg & ~ADDR_MASK);
+
+	buf[0] = reg;
 	buf[1] = val;
 
 	return spi_write(lis->spi_dev, buf, sizeof(buf));
@@ -207,10 +118,10 @@ static int reg_set_bit_mask(struct lis302dl_info *lis,
 enum lis302dl_intmode {
 	LIS302DL_INTMODE_GND		= 0x00,
 	LIS302DL_INTMODE_FF_WU_1	= 0x01,
-	LIX302DL_INTMODE_FF_WU_2	= 0x02,
-	LIX302DL_INTMODE_FF_WU_12	= 0x03,
-	LIX302DL_INTMODE_DATA_READY	= 0x04,
-	LIX302DL_INTMODE_CLICK		= 0x07,
+	LIS302DL_INTMODE_FF_WU_2	= 0x02,
+	LIS302DL_INTMODE_FF_WU_12	= 0x03,
+	LIS302DL_INTMODE_DATA_READY	= 0x04,
+	LIS302DL_INTMODE_CLICK		= 0x07,
 };
 
 static void lis302dl_int_mode(struct spi_device *spi, int int_pin,
@@ -218,12 +129,18 @@ static void lis302dl_int_mode(struct spi_device *spi, int int_pin,
 {
 	struct lis302dl_info *lis = dev_get_drvdata(&spi->dev);
 
-	if (int_pin == 1)
+	switch (int_pin) {
+	case 1:
 		reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode);
-	else if (int_pin == 2)
+		break;
+	case 2:
 		reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3);
+		break;
+	default:
+		BUG();
+	}
 }
-
+#if 0
 static void _report_btn_single(struct input_dev *inp, int btn)
 {
 	input_report_key(inp, btn, 1);
@@ -241,95 +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, click_src;
-	u_int8_t val;
-
-	lis->working = 1;
-
- 	status = reg_read(lis, LIS302DL_REG_STATUS);
-	ff_wu_src_1 = reg_read(lis, LIS302DL_REG_FF_WU_SRC_1);
-	click_src = reg_read(lis, LIS302DL_REG_CLICK_SRC);
-
-	if (status & LIS302DL_STATUS_XDA) {
-		val = reg_read(lis, LIS302DL_REG_OUT_X);
-		if (lis->flags & LIS302DL_F_FS)
-			val = val << 2;
-		input_report_rel(lis->input_dev, REL_X, val);
-	}
-
-	if (status & LIS302DL_STATUS_YDA) {
-		val = reg_read(lis, LIS302DL_REG_OUT_Y);
-		if (lis->flags & LIS302DL_F_FS)
-			val = val << 2;
-		input_report_rel(lis->input_dev, REL_Y, val);
-	}
-
-	if (status & LIS302DL_STATUS_ZDA) {
-		val = reg_read(lis, LIS302DL_REG_OUT_Z);
-		if (lis->flags & LIS302DL_F_FS)
-			val = val << 2;
-		input_report_rel(lis->input_dev, REL_Z, val);
-	}
-
-	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 void lis302dl_schedule_work(struct lis302dl_info *lis)
-{
-	int status;
-
-	get_device(&lis->spi_dev->dev);
-	status = schedule_work(&lis->work);
-	if (!status && !lis->working)
-		dev_dbg(&lis->spi_dev->dev, "work item may be lost\n");
-}
 
 static irqreturn_t lis302dl_interrupt(int irq, void *_lis)
 {
 	struct lis302dl_info *lis = _lis;
 
-	lis302dl_schedule_work(lis);
-
-	/* 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;
 }
 
@@ -388,6 +224,7 @@ static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale);
 static struct attribute *lis302dl_sysfs_entries[] = {
 	&dev_attr_sample_rate.attr,
 	&dev_attr_full_scale.attr,
+	NULL
 };
 
 static struct attribute_group lis302dl_attr_group = {
@@ -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,9 +258,12 @@ 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 lastre user, we can safely
+	 * only see close() for the close of the last user, we can safely
 	 * disable the data ready events */
 	reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, 0x00);
 
@@ -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,84 +281,138 @@ 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) {
-		printk(KERN_ERR "error durign spi_setup of lis302dl driver\n");
+		dev_err(&spi->dev, "error during spi_setup\n");
 		dev_set_drvdata(&spi->dev, NULL);
-		kfree(lis);
-		return rc;
+		goto bail_free_lis;
 	}
 
 	wai = reg_read(lis, LIS302DL_REG_WHO_AM_I);
 	if (wai != LIS302DL_WHO_AM_I_MAGIC) {
-		printk(KERN_ERR "unknown who_am_i signature 0x%02x\n", wai);
+		dev_err(&spi->dev, "unknown who_am_i signature 0x%02x\n", wai);
 		dev_set_drvdata(&spi->dev, NULL);
-		kfree(lis);
-		return -ENODEV;
-	}
-
-	/* switch interrupt to open collector */
-	reg_write(lis, LIS302DL_CTRL3_PP_OD, 0x7c);
-
-	rc = request_irq(lis->spi_dev->irq, lis302dl_interrupt, IRQF_DISABLED,
-			 "lis302dl", NULL);
-	if (rc < 0) {
-		dev_err(&spi->dev, "error requesting IRQ %d\n",
-			lis->spi_dev->irq);
-		/* FIXME */
-		return rc;
+		rc = -ENODEV;
+		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");
-		/* FIXME */
-		return rc;
+		goto bail_free_lis;
 	}
 
 	/* initialize input layer details */
 	lis->input_dev = input_allocate_device();
 	if (!lis->input_dev) {
 		dev_err(&spi->dev, "Unable to allocate input device\n");
-		/* FIXME */
+		goto bail_sysfs;
 	}
 
 	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;
 
-	input_register_device(lis->input_dev);
+	rc = input_register_device(lis->input_dev);
+	if (rc) {
+		dev_err(&spi->dev, "error %d registering input device\n", rc);
+		goto bail_inp_dev;
+	}
+
+	reg_write(lis, LIS302DL_REG_CTRL1, 0x47);
+	reg_write(lis, LIS302DL_REG_CTRL3, 0xc0);
+	reg_write(lis, LIS302DL_REG_FF_WU_THS_1, 0x14);
+	reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, 0x00);
+	reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, 0x95);
+
+	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 */
+		reg_write(lis, LIS302DL_REG_CTRL3, LIS302DL_CTRL3_PP_OD |
+						   LIS302DL_CTRL3_IHL);
+	else
+		/* push-pull, active-low */
+		reg_write(lis, LIS302DL_REG_CTRL3, LIS302DL_CTRL3_IHL);
+
+	lis302dl_int_mode(spi, 1, LIS302DL_INTMODE_DATA_READY);
+	lis302dl_int_mode(spi, 2, LIS302DL_INTMODE_DATA_READY);
+
+	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);
 
+	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_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)
+		input_free_device(lis->input_dev);
 	dev_set_drvdata(&spi->dev, NULL);
 	kfree(lis);
 
@@ -521,6 +423,10 @@ 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;
+
+	disable_irq(lis->spi_dev->irq);
+	local_save_flags(flags);
 
 	/* save registers */
 	lis->regs[LIS302DL_REG_CTRL1] = reg_read(lis, LIS302DL_REG_CTRL1);
@@ -561,12 +467,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]);
@@ -597,6 +508,9 @@ 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);
+	enable_irq(lis->spi_dev->irq);
+
 	return 0;
 }
 #else
diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c
index 109d82c..03b73dd 100644
--- a/drivers/spi/spi_s3c24xx_gpio.c
+++ b/drivers/spi/spi_s3c24xx_gpio.c
@@ -91,7 +91,7 @@ static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value)
 	struct s3c2410_spigpio *sg = spidev_to_sg(dev);
 
 	if (sg->info && sg->info->chip_select)
-		(sg->info->chip_select)(sg->info, value);
+		(sg->info->chip_select)(sg->info, dev->chip_select, value);
 }
 
 static int s3c2410_spigpio_probe(struct platform_device *dev)
@@ -113,9 +113,11 @@ static int s3c2410_spigpio_probe(struct platform_device *dev)
 
 	platform_set_drvdata(dev, sp);
 
-	/* copy in the plkatform data */
+	/* copy in the platform data */
 	info = sp->info = dev->dev.platform_data;
 
+	master->num_chipselect = info->num_chipselect;
+
 	/* setup spi bitbang adaptor */
 	sp->bitbang.master = spi_master_get(master);
 	sp->bitbang.master->bus_num = info->bus_num;
@@ -146,12 +148,17 @@ static int s3c2410_spigpio_probe(struct platform_device *dev)
 	/* register the chips to go with the board */
 
 	for (i = 0; i < sp->info->board_size; i++) {
+		struct spi_device *spidev;
+
 		dev_info(&dev->dev, "registering %p: %s\n",
 			 &sp->info->board_info[i],
 			 sp->info->board_info[i].modalias);
 
 		sp->info->board_info[i].controller_data = sp;
-		spi_new_device(master, sp->info->board_info + i);
+		spidev = spi_new_device(master, sp->info->board_info + i);
+		if (spidev)
+			spidev->max_speed_hz =
+					  sp->info->board_info[i].max_speed_hz;
 	}
 
 	return 0;
diff --git a/include/asm-arm/arch-s3c2410/spi-gpio.h b/include/asm-arm/arch-s3c2410/spi-gpio.h
index ba1dca8..03dea09 100644
--- a/include/asm-arm/arch-s3c2410/spi-gpio.h
+++ b/include/asm-arm/arch-s3c2410/spi-gpio.h
@@ -22,11 +22,12 @@ struct s3c2410_spigpio_info {
 	unsigned long		 pin_miso;
 
 	int			 bus_num;
+	int			 num_chipselect;
 
 	unsigned long		 board_size;
 	struct spi_board_info	*board_info;
 
-	void (*chip_select)(struct s3c2410_spigpio_info *spi, int cs);
+	void (*chip_select)(struct s3c2410_spigpio_info *spi, int csid, int cs);
 };
 
 
diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h
index d0f31be..2dea813 100644
--- a/include/linux/lis302dl.h
+++ b/include/linux/lis302dl.h
@@ -2,10 +2,112 @@
 #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;
+	int open_drain;
+	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 */
 
-- 
1.5.6.5