diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.26/0031-lis302dl.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches-2.6.26/0031-lis302dl.patch.patch | 781 |
1 files changed, 781 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.26/0031-lis302dl.patch.patch b/target/linux/s3c24xx/patches-2.6.26/0031-lis302dl.patch.patch new file mode 100755 index 0000000000..ea1b231e36 --- /dev/null +++ b/target/linux/s3c24xx/patches-2.6.26/0031-lis302dl.patch.patch @@ -0,0 +1,781 @@ +From 65c5d85b4cf89969d2e2e981c018bf0ef1c03a2a Mon Sep 17 00:00:00 2001 +From: mokopatches <mokopatches@openmoko.org> +Date: Wed, 16 Jul 2008 14:46:56 +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 | 2 + + drivers/input/misc/lis302dl.c | 633 ++++++++++++++++++++++++++++++++++++ + include/linux/lis302dl.h | 11 + + 5 files changed, 700 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 432699d..ac8bcf4 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -197,4 +197,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 ebd39f2..0d223ba 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -20,3 +20,4 @@ + obj-$(CONFIG_INPUT_UINPUT) += uinput.o + obj-$(CONFIG_INPUT_APANEL) += apanel.o + obj-$(CONFIG_INPUT_GPIO_BUTTONS) += gpio_buttons.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.3 + |