diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.24/1038-lis302dl.patch.patch')
-rw-r--r-- | target/linux/s3c24xx/patches-2.6.24/1038-lis302dl.patch.patch | 781 |
1 files changed, 0 insertions, 781 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.24/1038-lis302dl.patch.patch b/target/linux/s3c24xx/patches-2.6.24/1038-lis302dl.patch.patch deleted file mode 100644 index 6cd803c..0000000 --- a/target/linux/s3c24xx/patches-2.6.24/1038-lis302dl.patch.patch +++ /dev/null @@ -1,781 +0,0 @@ -From e31bab42367386736c07638683acb4da3c775eaf Mon Sep 17 00:00:00 2001 -From: mokopatches <mokopatches@openmoko.org> -Date: Fri, 4 Apr 2008 11:35:42 +0100 -Subject: [PATCH] lis302dl.patch - This is a Linux driver for the STmicro LIS302DL 3-axis accelerometer. - -Signed-off-by: Harald Welte <laforge@openmoko.org> ---- - arch/arm/mach-s3c2440/mach-gta02.c | 46 +++- - drivers/input/misc/Kconfig | 9 + - drivers/input/misc/Makefile | 1 + - drivers/input/misc/lis302dl.c | 633 ++++++++++++++++++++++++++++++++++++ - include/linux/lis302dl.h | 11 + - 5 files changed, 699 insertions(+), 1 deletions(-) - create mode 100644 drivers/input/misc/lis302dl.c - create mode 100644 include/linux/lis302dl.h - -diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c -index f72a5ae..d11da10 100644 ---- a/arch/arm/mach-s3c2440/mach-gta02.c -+++ b/arch/arm/mach-s3c2440/mach-gta02.c -@@ -46,6 +46,7 @@ - #include <linux/mtd/physmap.h> - - #include <linux/pcf50633.h> -+#include <linux/lis302dl.h> - - #include <asm/mach/arch.h> - #include <asm/mach/map.h> -@@ -463,7 +464,7 @@ static struct s3c2410_ts_mach_info gta02_ts_cfg = { - .oversampling_shift = 5, - }; - --/* SPI */ -+/* SPI: LCM control interface attached to Glamo3362 */ - - static struct spi_board_info gta02_spi_board_info[] = { - { -@@ -504,6 +505,48 @@ static struct platform_device gta01_led_dev = { - .resource = gta01_led_resources, - }; - -+/* SPI: Accelerometers attached to SPI of s3c244x */ -+ -+static void gta02_spi_acc_set_cs(struct s3c2410_spi_info *spi, int cs, int pol) -+{ -+ s3c2410_gpio_setpin(cs, pol); -+} -+ -+static const struct lis302dl_platform_data lis302_pdata[] = { -+ { -+ .name = "lis302-1 (top)" -+ }, { -+ .name = "lis302-2 (bottom)" -+ }, -+}; -+ -+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, -+ .bus_num = 1, -+ .chip_select = S3C2410_GPD12, -+ .mode = SPI_MODE_3, -+ }, -+ { -+ .modalias = "lis302dl", -+ .platform_data = &lis302_pdata[1], -+ .irq = GTA02_IRQ_GSENSOR_2, -+ .max_speed_hz = 400 * 1000, -+ .bus_num = 1, -+ .chip_select = S3C2410_GPD13, -+ .mode = SPI_MODE_3, -+ }, -+}; -+ -+static struct s3c2410_spi_info gta02_spi_acc_cfg = { -+ .set_cs = gta02_spi_acc_set_cs, -+ .board_size = ARRAY_SIZE(gta02_spi_acc_bdinfo), -+ .board_info = gta02_spi_acc_bdinfo, -+}; -+ - static struct resource gta02_led_resources[] = { - { - .name = "gta02-power:orange", -@@ -746,6 +789,7 @@ static void __init gta02_machine_init(void) - s3c_device_usb.dev.platform_data = >a02_usb_info; - s3c_device_nand.dev.platform_data = >a02_nand_info; - s3c_device_sdi.dev.platform_data = >a02_mmc_cfg; -+ s3c_device_spi1.dev.platform_data = >a02_spi_acc_cfg; - - /* Only GTA02v1 has a SD_DETECT GPIO. Since the slot is not - * hot-pluggable, this is not required anyway */ -diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig -index 8f5c7b9..5a15edd 100644 ---- a/drivers/input/misc/Kconfig -+++ b/drivers/input/misc/Kconfig -@@ -183,4 +183,13 @@ config HP_SDC_RTC - Say Y here if you want to support the built-in real time clock - of the HP SDC controller. - -+config INPUT_LIS302DL -+ tristate "STmicro LIS302DL 3-axis accelerometer" -+ depends on SPI_MASTER -+ help -+ SPI driver for the STmicro LIS302DL 3-axis accelerometer. -+ -+ The userspece interface is a 3-axis (X/Y/Z) relative movement -+ Linux input device, reporting REL_[XYZ] events. -+ - endif -diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile -index 3585b50..f197a67 100644 ---- a/drivers/input/misc/Makefile -+++ b/drivers/input/misc/Makefile -@@ -18,3 +18,4 @@ obj-$(CONFIG_INPUT_POWERMATE) += powermate.o - obj-$(CONFIG_INPUT_YEALINK) += yealink.o - obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o - obj-$(CONFIG_INPUT_UINPUT) += uinput.o -+obj-$(CONFIG_INPUT_LIS302DL) += lis302dl.o -diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c -new file mode 100644 -index 0000000..45c41c8 ---- /dev/null -+++ b/drivers/input/misc/lis302dl.c -@@ -0,0 +1,633 @@ -+/* Linux kernel driver for the ST LIS302D 3-axis accelerometer -+ * -+ * Copyright (C) 2007 by OpenMoko, Inc. -+ * Author: Harald Welte <laforge@openmoko.org> -+ * All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, -+ * MA 02111-1307 USA -+ * -+ * TODO -+ * * statistics for overflow events -+ * * configuration interface (sysfs) for -+ * * enable/disable x/y/z axis data ready -+ * * enable/disable resume from freee fall / click -+ * * free fall / click parameters -+ * * high pass filter parameters -+ */ -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/module.h> -+#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 -+ -+static inline 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; -+ -+ rc = spi_w8r8(lis->spi_dev, cmd); -+ -+ return rc; -+} -+ -+static u_int8_t reg_read(struct lis302dl_info *lis, u_int8_t reg) -+{ -+ u_int8_t ret; -+ -+ mutex_lock(&lis->lock); -+ ret = __reg_read(lis, reg); -+ mutex_unlock(&lis->lock); -+ -+ return ret; -+} -+ -+static inline 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); -+ buf[1] = val; -+ -+ return spi_write(lis->spi_dev, buf, sizeof(buf)); -+} -+ -+static int reg_write(struct lis302dl_info *lis, u_int8_t reg, u_int8_t val) -+{ -+ int ret; -+ -+ mutex_lock(&lis->lock); -+ ret = __reg_write(lis, reg, val); -+ mutex_unlock(&lis->lock); -+ -+ return ret; -+} -+ -+static int reg_set_bit_mask(struct lis302dl_info *lis, -+ u_int8_t reg, u_int8_t mask, u_int8_t val) -+{ -+ int ret; -+ u_int8_t tmp; -+ -+ val &= mask; -+ -+ mutex_lock(&lis->lock); -+ -+ tmp = __reg_read(lis, reg); -+ tmp &= ~mask; -+ tmp |= val; -+ ret = __reg_write(lis, reg, tmp); -+ -+ mutex_unlock(&lis->lock); -+ -+ return ret; -+} -+ -+/* interrupt handling related */ -+ -+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, -+}; -+ -+static void lis302dl_int_mode(struct spi_device *spi, int int_pin, -+ enum lis302dl_intmode mode) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); -+ -+ if (int_pin == 1) -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode); -+ else if (int_pin == 2) -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3); -+} -+ -+static void _report_btn_single(struct input_dev *inp, int btn) -+{ -+ input_report_key(inp, btn, 1); -+ input_sync(inp); -+ input_report_key(inp, btn, 0); -+} -+ -+static void _report_btn_double(struct input_dev *inp, int btn) -+{ -+ input_report_key(inp, btn, 1); -+ input_sync(inp); -+ input_report_key(inp, btn, 0); -+ input_sync(inp); -+ input_report_key(inp, btn, 1); -+ input_sync(inp); -+ input_report_key(inp, btn, 0); -+} -+ -+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); -+ -+ return IRQ_HANDLED; -+} -+ -+/* sysfs */ -+ -+static ssize_t show_rate(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(dev); -+ u_int8_t ctrl1 = reg_read(lis, LIS302DL_REG_CTRL1); -+ -+ return sprintf(buf, "%d\n", ctrl1 & LIS302DL_CTRL1_DR ? 400 : 100); -+} -+ -+static ssize_t set_rate(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(dev); -+ -+ if (!strcmp(buf, "400\n")) -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR, -+ LIS302DL_CTRL1_DR); -+ else -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR, 0); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(sample_rate, S_IRUGO | S_IWUSR, show_rate, set_rate); -+ -+static ssize_t show_scale(struct device *dev, struct device_attribute *attr, -+ char *buf) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(dev); -+ u_int8_t ctrl1 = reg_read(lis, LIS302DL_REG_CTRL1); -+ -+ return sprintf(buf, "%s\n", ctrl1 & LIS302DL_CTRL1_FS ? "9.2" : "2.3"); -+} -+ -+static ssize_t set_scale(struct device *dev, struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(dev); -+ -+ if (!strcmp(buf, "9.2\n")) -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS, -+ LIS302DL_CTRL1_FS); -+ else -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS, 0); -+ -+ return count; -+} -+ -+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, -+}; -+ -+static struct attribute_group lis302dl_attr_group = { -+ .name = NULL, -+ .attrs = lis302dl_sysfs_entries, -+}; -+ -+/* input device handling and driver core interaction */ -+ -+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; -+ -+ /* make sure we're powered up and generate data ready */ -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1); -+ -+ return 0; -+} -+ -+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; -+ -+ /* since the input core already serializes access and makes sure we -+ * only see close() for the close of the lastre user, we can safely -+ * disable the data ready events */ -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, 0x00); -+ -+ /* however, don't power down the whole device if still needed */ -+ if (!(lis->flags & LIS302DL_F_WUP_FF || -+ lis->flags & LIS302DL_F_WUP_CLICK)) { -+ reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD, -+ 0x00); -+ } -+} -+ -+static int __devinit lis302dl_probe(struct spi_device *spi) -+{ -+ int rc; -+ struct lis302dl_info *lis; -+ u_int8_t wai; -+ -+ lis = kzalloc(sizeof(*lis), GFP_KERNEL); -+ if (!lis) -+ return -ENOMEM; -+ -+ mutex_init(&lis->lock); -+ INIT_WORK(&lis->work, lis302dl_work); -+ lis->spi_dev = spi; -+ -+ spi_set_drvdata(spi, lis); -+ -+ rc = spi_setup(spi); -+ if (rc < 0) { -+ printk(KERN_ERR "error durign spi_setup of lis302dl driver\n"); -+ dev_set_drvdata(&spi->dev, NULL); -+ kfree(lis); -+ return rc; -+ } -+ -+ 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_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 = sysfs_create_group(&spi->dev.kobj, &lis302dl_attr_group); -+ if (rc) { -+ dev_err(&spi->dev, "error creating sysfs group\n"); -+ /* FIXME */ -+ return rc; -+ } -+ -+ /* 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 */ -+ } -+ -+ set_bit(EV_REL, lis->input_dev->evbit); -+ 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->open = lis302dl_input_open; -+ lis->input_dev->close = lis302dl_input_close; -+ -+ input_register_device(lis->input_dev); -+ -+ return 0; -+} -+ -+static int __devexit lis302dl_remove(struct spi_device *spi) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); -+ -+ /* power down the device */ -+ reg_write(lis, LIS302DL_REG_CTRL1, 0x00); -+ sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group); -+ input_unregister_device(lis->input_dev); -+ dev_set_drvdata(&spi->dev, NULL); -+ kfree(lis); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int lis302dl_suspend(struct spi_device *spi, pm_message_t state) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); -+ -+ /* save registers */ -+ lis->regs[LIS302DL_REG_CTRL1] = reg_read(lis, LIS302DL_REG_CTRL1); -+ lis->regs[LIS302DL_REG_CTRL2] = reg_read(lis, LIS302DL_REG_CTRL2); -+ lis->regs[LIS302DL_REG_CTRL3] = reg_read(lis, LIS302DL_REG_CTRL3); -+ lis->regs[LIS302DL_REG_FF_WU_CFG_1] = -+ reg_read(lis, LIS302DL_REG_FF_WU_CFG_1); -+ lis->regs[LIS302DL_REG_FF_WU_THS_1] = -+ reg_read(lis, LIS302DL_REG_FF_WU_THS_1); -+ lis->regs[LIS302DL_REG_FF_WU_DURATION_1] = -+ reg_read(lis, LIS302DL_REG_FF_WU_DURATION_1); -+ lis->regs[LIS302DL_REG_FF_WU_CFG_2] = -+ reg_read(lis, LIS302DL_REG_FF_WU_CFG_2); -+ lis->regs[LIS302DL_REG_FF_WU_THS_2] = -+ reg_read(lis, LIS302DL_REG_FF_WU_THS_2); -+ lis->regs[LIS302DL_REG_FF_WU_DURATION_2] = -+ reg_read(lis, LIS302DL_REG_FF_WU_DURATION_2); -+ lis->regs[LIS302DL_REG_CLICK_CFG] = -+ reg_read(lis, LIS302DL_REG_CLICK_CFG); -+ lis->regs[LIS302DL_REG_CLICK_THSY_X] = -+ reg_read(lis, LIS302DL_REG_CLICK_THSY_X); -+ lis->regs[LIS302DL_REG_CLICK_THSZ] = -+ reg_read(lis, LIS302DL_REG_CLICK_THSZ); -+ lis->regs[LIS302DL_REG_CLICK_TIME_LIMIT] = -+ reg_read(lis, LIS302DL_REG_CLICK_TIME_LIMIT); -+ lis->regs[LIS302DL_REG_CLICK_LATENCY] = -+ reg_read(lis, LIS302DL_REG_CLICK_LATENCY); -+ 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); -+ } -+ -+ return 0; -+} -+ -+static int lis302dl_resume(struct spi_device *spi) -+{ -+ struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); -+ -+ /* restore registers after resume */ -+ reg_write(lis, LIS302DL_REG_CTRL1, lis->regs[LIS302DL_REG_CTRL1]); -+ reg_write(lis, LIS302DL_REG_CTRL2, lis->regs[LIS302DL_REG_CTRL2]); -+ reg_write(lis, LIS302DL_REG_CTRL3, lis->regs[LIS302DL_REG_CTRL3]); -+ reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, -+ lis->regs[LIS302DL_REG_FF_WU_CFG_1]); -+ reg_write(lis, LIS302DL_REG_FF_WU_THS_1, -+ lis->regs[LIS302DL_REG_FF_WU_THS_1]); -+ reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, -+ lis->regs[LIS302DL_REG_FF_WU_DURATION_1]); -+ reg_write(lis, LIS302DL_REG_FF_WU_CFG_2, -+ lis->regs[LIS302DL_REG_FF_WU_CFG_2]); -+ reg_write(lis, LIS302DL_REG_FF_WU_THS_2, -+ lis->regs[LIS302DL_REG_FF_WU_THS_2]); -+ reg_write(lis, LIS302DL_REG_FF_WU_DURATION_2, -+ lis->regs[LIS302DL_REG_FF_WU_DURATION_2]); -+ reg_write(lis, LIS302DL_REG_CLICK_CFG, -+ lis->regs[LIS302DL_REG_CLICK_CFG]); -+ reg_write(lis, LIS302DL_REG_CLICK_THSY_X, -+ lis->regs[LIS302DL_REG_CLICK_THSY_X]); -+ reg_write(lis, LIS302DL_REG_CLICK_THSZ, -+ lis->regs[LIS302DL_REG_CLICK_THSZ]); -+ reg_write(lis, LIS302DL_REG_CLICK_TIME_LIMIT, -+ lis->regs[LIS302DL_REG_CLICK_TIME_LIMIT]); -+ reg_write(lis, LIS302DL_REG_CLICK_LATENCY, -+ lis->regs[LIS302DL_REG_CLICK_LATENCY]); -+ reg_write(lis, LIS302DL_REG_CLICK_WINDOW, -+ lis->regs[LIS302DL_REG_CLICK_WINDOW]); -+ -+ return 0; -+} -+#else -+#define lis302dl_suspend NULL -+#define lis302dl_resume NULL -+#endif -+ -+static struct spi_driver lis302dl_driver = { -+ .driver = { -+ .name = "lis302dl", -+ .owner = THIS_MODULE, -+ }, -+ -+ .probe = lis302dl_probe, -+ .remove = __devexit_p(lis302dl_remove), -+ .suspend = lis302dl_suspend, -+ .resume = lis302dl_resume, -+}; -+ -+static int __init lis302dl_init(void) -+{ -+ return spi_register_driver(&lis302dl_driver); -+} -+ -+static void __exit lis302dl_exit(void) -+{ -+ spi_unregister_driver(&lis302dl_driver); -+} -+ -+MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); -+MODULE_LICENSE("GPL"); -+ -+module_init(lis302dl_init); -+module_exit(lis302dl_exit); -diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h -new file mode 100644 -index 0000000..d0f31be ---- /dev/null -+++ b/include/linux/lis302dl.h -@@ -0,0 +1,11 @@ -+#ifndef _LINUX_LIS302DL_H -+#define _LINUX_LIS302DL_H -+ -+#include <linux/types.h> -+ -+struct lis302dl_platform_data { -+ char *name; -+}; -+ -+#endif /* _LINUX_LIS302DL_H */ -+ --- -1.5.6.5 - |