From 65c5d85b4cf89969d2e2e981c018bf0ef1c03a2a Mon Sep 17 00:00:00 2001 From: mokopatches 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 --- 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 #include +#include #include #include @@ -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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#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 "); +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 + +struct lis302dl_platform_data { + char *name; +}; + +#endif /* _LINUX_LIS302DL_H */ + -- 1.5.6.3