aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/misc/lis302dl.c
diff options
context:
space:
mode:
authormokopatches <mokopatches@openmoko.org>2008-11-19 17:03:16 +0000
committerwarmcat <andy@warmcat.com>2008-11-19 17:03:16 +0000
commitc8a8421359011e536c4152ddae84ea04316f436f (patch)
tree9f93018841167facc01188f242348ef488074efc /drivers/input/misc/lis302dl.c
parent5915ea88b0b6ada7fc5234239feec5d4960887df (diff)
lis302dl.patch
This is a Linux driver for the STmicro LIS302DL 3-axis accelerometer. Signed-off-by: Harald Welte <laforge@openmoko.org>
Diffstat (limited to 'drivers/input/misc/lis302dl.c')
-rw-r--r--drivers/input/misc/lis302dl.c633
1 files changed, 633 insertions, 0 deletions
diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
new file mode 100644
index 00000000000..45c41c81719
--- /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);