diff options
Diffstat (limited to 'target/linux/s3c24xx/files-2.6.30/drivers/input/misc/lis302dl.c')
-rw-r--r-- | target/linux/s3c24xx/files-2.6.30/drivers/input/misc/lis302dl.c | 957 |
1 files changed, 0 insertions, 957 deletions
diff --git a/target/linux/s3c24xx/files-2.6.30/drivers/input/misc/lis302dl.c b/target/linux/s3c24xx/files-2.6.30/drivers/input/misc/lis302dl.c deleted file mode 100644 index dc6602a8ea..0000000000 --- a/target/linux/s3c24xx/files-2.6.30/drivers/input/misc/lis302dl.c +++ /dev/null @@ -1,957 +0,0 @@ -/* Linux kernel driver for the ST LIS302D 3-axis accelerometer - * - * Copyright (C) 2007-2008 by Openmoko, Inc. - * Author: Harald Welte <laforge@openmoko.org> - * converted to private bitbang by: - * Andy Green <andy@openmoko.com> - * ability to set acceleration threshold added by: - * Simon Kagstrom <simon.kagstrom@gmail.com> - * 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/irq.h> -#include <linux/interrupt.h> -#include <linux/sysfs.h> -#include <linux/spi/spi.h> - -#include <linux/lis302dl.h> - -/* Utility functions */ -static u8 __reg_read(struct lis302dl_info *lis, u8 reg) -{ - struct spi_message msg; - struct spi_transfer t; - u8 data[2] = {0xc0 | reg}; - int rc; - - spi_message_init(&msg); - memset(&t, 0, sizeof t); - t.len = 2; - spi_message_add_tail(&t, &msg); - t.tx_buf = &data[0]; - t.rx_buf = &data[0]; - - /* Should complete without blocking */ - rc = spi_non_blocking_transfer(lis->spi, &msg); - if (rc < 0) { - dev_err(lis->dev, "Error reading register\n"); - return rc; - } - - return data[1]; -} - -static void __reg_write(struct lis302dl_info *lis, u8 reg, u8 val) -{ - struct spi_message msg; - struct spi_transfer t; - u8 data[2] = {reg, val}; - - spi_message_init(&msg); - memset(&t, 0, sizeof t); - t.len = 2; - spi_message_add_tail(&t, &msg); - t.tx_buf = &data[0]; - t.rx_buf = &data[0]; - - /* Completes without blocking */ - if (spi_non_blocking_transfer(lis->spi, &msg) < 0) - dev_err(lis->dev, "Error writing register\n"); -} - -static void __reg_set_bit_mask(struct lis302dl_info *lis, u8 reg, u8 mask, - u8 val) -{ - u_int8_t tmp; - - val &= mask; - - tmp = __reg_read(lis, reg); - tmp &= ~mask; - tmp |= val; - __reg_write(lis, reg, tmp); -} - -static int __ms_to_duration(struct lis302dl_info *lis, int ms) -{ - /* If we have 400 ms sampling rate, the stepping is 2.5 ms, - * on 100 ms the stepping is 10ms */ - if (lis->flags & LIS302DL_F_DR) - return min((ms * 10) / 25, 637); - - return min(ms / 10, 2550); -} - -static int __duration_to_ms(struct lis302dl_info *lis, int duration) -{ - if (lis->flags & LIS302DL_F_DR) - return (duration * 25) / 10; - - return duration * 10; -} - -static u8 __mg_to_threshold(struct lis302dl_info *lis, int mg) -{ - /* If FS is set each bit is 71mg, otherwise 18mg. The THS register - * has 7 bits for the threshold value */ - if (lis->flags & LIS302DL_F_FS) - return min(mg / 71, 127); - - return min(mg / 18, 127); -} - -static int __threshold_to_mg(struct lis302dl_info *lis, u8 threshold) -{ - if (lis->flags & LIS302DL_F_FS) - return threshold * 71; - - return threshold * 18; -} - -/* interrupt handling related */ - -enum lis302dl_intmode { - LIS302DL_INTMODE_GND = 0x00, - LIS302DL_INTMODE_FF_WU_1 = 0x01, - 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 device *dev, int int_pin, - enum lis302dl_intmode mode) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - - switch (int_pin) { - case 1: - __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x07, mode); - break; - case 2: - __reg_set_bit_mask(lis, LIS302DL_REG_CTRL3, 0x38, mode << 3); - break; - default: - BUG(); - } -} - -static void __enable_wakeup(struct lis302dl_info *lis) -{ - __reg_write(lis, LIS302DL_REG_CTRL1, 0); - - /* First zero to get to a known state */ - __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, LIS302DL_FFWUCFG_XHIE | - LIS302DL_FFWUCFG_YHIE | LIS302DL_FFWUCFG_ZHIE | - LIS302DL_FFWUCFG_LIR); - __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, - __mg_to_threshold(lis, lis->wakeup.threshold)); - __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, - __ms_to_duration(lis, lis->wakeup.duration)); - - /* Route the interrupt for wakeup */ - __lis302dl_int_mode(lis->dev, 1, - LIS302DL_INTMODE_FF_WU_1); - - __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET); - __reg_read(lis, LIS302DL_REG_OUT_X); - __reg_read(lis, LIS302DL_REG_OUT_Y); - __reg_read(lis, LIS302DL_REG_OUT_Z); - __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_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | 7); -} - -static void __enable_data_collection(struct lis302dl_info *lis) -{ - 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); - - /* If the threshold is zero, let the device generated an interrupt - * on each datum */ - if (lis->threshold == 0) { - __reg_write(lis, LIS302DL_REG_CTRL2, 0); - __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_DATA_READY); - __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_DATA_READY); - } else { - __reg_write(lis, LIS302DL_REG_CTRL2, - LIS302DL_CTRL2_HPFF1); - __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, - __mg_to_threshold(lis, lis->threshold)); - __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, - __ms_to_duration(lis, lis->duration)); - - /* Clear the HP filter "starting point" */ - __reg_read(lis, LIS302DL_REG_HP_FILTER_RESET); - __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, - LIS302DL_FFWUCFG_XHIE | LIS302DL_FFWUCFG_YHIE | - LIS302DL_FFWUCFG_ZHIE | LIS302DL_FFWUCFG_LIR); - __lis302dl_int_mode(lis->dev, 1, LIS302DL_INTMODE_FF_WU_12); - __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_FF_WU_12); - } -} - -#if 0 -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); -} -#endif - - -static void lis302dl_bitbang_read_sample(struct lis302dl_info *lis) -{ - u8 data[(LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS) + 2] = {0xC0 | LIS302DL_REG_STATUS}; - u8 *read = data + 1; - unsigned long flags; - int mg_per_sample = __threshold_to_mg(lis, 1); - struct spi_message msg; - struct spi_transfer t; - - spi_message_init(&msg); - memset(&t, 0, sizeof t); - t.len = sizeof(data); - spi_message_add_tail(&t, &msg); - t.tx_buf = &data[0]; - t.rx_buf = &data[0]; - - /* grab the set of register containing status and XYZ data */ - - local_irq_save(flags); - - /* Should complete without blocking */ - if (spi_non_blocking_transfer(lis->spi, &msg) < 0) - dev_err(lis->dev, "Error reading registers\n"); - - local_irq_restore(flags); - - /* - * at the minute the test below fails 50% of the time due to - * a problem with level interrupts causing ISRs to get called twice. - * This is a workaround for that, but actually this test is still - * valid and the information can be used for overrrun stats. - */ - - /* has any kind of overrun been observed by the lis302dl? */ - if (read[0] & (LIS302DL_STATUS_XOR | - LIS302DL_STATUS_YOR | - LIS302DL_STATUS_ZOR)) - lis->overruns++; - - /* we have a valid sample set? */ - if (read[0] & LIS302DL_STATUS_XYZDA) { - input_report_abs(lis->input_dev, ABS_X, mg_per_sample * - (s8)read[LIS302DL_REG_OUT_X - LIS302DL_REG_STATUS]); - input_report_abs(lis->input_dev, ABS_Y, mg_per_sample * - (s8)read[LIS302DL_REG_OUT_Y - LIS302DL_REG_STATUS]); - input_report_abs(lis->input_dev, ABS_Z, mg_per_sample * - (s8)read[LIS302DL_REG_OUT_Z - LIS302DL_REG_STATUS]); - - input_sync(lis->input_dev); - } - - if (lis->threshold) - /* acknowledge the wakeup source */ - __reg_read(lis, LIS302DL_REG_FF_WU_SRC_1); -} - -static irqreturn_t lis302dl_interrupt(int irq, void *_lis) -{ - struct lis302dl_info *lis = _lis; - - lis302dl_bitbang_read_sample(lis); - return IRQ_HANDLED; -} - -/* sysfs */ - -static ssize_t show_overruns(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", lis->overruns); -} - -static DEVICE_ATTR(overruns, S_IRUGO, show_overruns, NULL); - -static ssize_t show_rate(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - u8 ctrl1; - unsigned long flags; - - local_irq_save(flags); - ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1); - local_irq_restore(flags); - - 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); - unsigned long flags; - - local_irq_save(flags); - - if (!strcmp(buf, "400\n")) { - __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR, - LIS302DL_CTRL1_DR); - lis->flags |= LIS302DL_F_DR; - } else { - __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_DR, - 0); - lis->flags &= ~LIS302DL_F_DR; - } - local_irq_restore(flags); - - 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; - unsigned long flags; - - local_irq_save(flags); - ctrl1 = __reg_read(lis, LIS302DL_REG_CTRL1); - local_irq_restore(flags); - - 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); - unsigned long flags; - - local_irq_save(flags); - - if (!strcmp(buf, "9.2\n")) { - __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS, - LIS302DL_CTRL1_FS); - lis->flags |= LIS302DL_F_FS; - } else { - __reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_FS, - 0); - lis->flags &= ~LIS302DL_F_FS; - } - - if (lis->flags & LIS302DL_F_INPUT_OPEN) - __enable_data_collection(lis); - - local_irq_restore(flags); - - return count; -} - -static DEVICE_ATTR(full_scale, S_IRUGO | S_IWUSR, show_scale, set_scale); - -static ssize_t show_threshold(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - - /* Display the device view of the threshold setting */ - return sprintf(buf, "%d\n", __threshold_to_mg(lis, - __mg_to_threshold(lis, lis->threshold))); -} - -static ssize_t set_threshold(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - unsigned int val; - - if (sscanf(buf, "%u\n", &val) != 1) - return -EINVAL; - /* 8g is the maximum if FS is 1 */ - if (val > 8000) - return -ERANGE; - - /* Set the threshold and write it out if the device is used */ - lis->threshold = val; - - if (lis->flags & LIS302DL_F_INPUT_OPEN) { - unsigned long flags; - - local_irq_save(flags); - __enable_data_collection(lis); - local_irq_restore(flags); - } - - return count; -} - -static DEVICE_ATTR(threshold, S_IRUGO | S_IWUSR, show_threshold, set_threshold); - -static ssize_t show_duration(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", __duration_to_ms(lis, - __ms_to_duration(lis, lis->duration))); -} - -static ssize_t set_duration(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - unsigned int val; - - if (sscanf(buf, "%u\n", &val) != 1) - return -EINVAL; - if (val > 2550) - return -ERANGE; - - lis->duration = val; - if (lis->flags & LIS302DL_F_INPUT_OPEN) - __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, - __ms_to_duration(lis, lis->duration)); - - return count; -} - -static DEVICE_ATTR(duration, S_IRUGO | S_IWUSR, show_duration, set_duration); - -static ssize_t lis302dl_dump(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - int n = 0; - u8 reg[0x40]; - char *end = buf; - unsigned long flags; - - local_irq_save(flags); - - for (n = 0; n < sizeof(reg); n++) - reg[n] = __reg_read(lis, n); - - local_irq_restore(flags); - - for (n = 0; n < sizeof(reg); n += 16) { - hex_dump_to_buffer(reg + n, 16, 16, 1, end, 128, 0); - end += strlen(end); - *end++ = '\n'; - *end++ = '\0'; - } - - return end - buf; -} -static DEVICE_ATTR(dump, S_IRUGO, lis302dl_dump, NULL); - -/* Configure freefall/wakeup interrupts */ -static ssize_t set_wakeup_threshold(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - unsigned int threshold; - - if (sscanf(buf, "%u\n", &threshold) != 1) - return -EINVAL; - - if (threshold > 8000) - return -ERANGE; - - /* Zero turns the feature off */ - if (threshold == 0) { - if (lis->flags & LIS302DL_F_IRQ_WAKE) { - disable_irq_wake(lis->pdata->interrupt); - lis->flags &= ~LIS302DL_F_IRQ_WAKE; - } - - return count; - } - - lis->wakeup.threshold = threshold; - - if (!(lis->flags & LIS302DL_F_IRQ_WAKE)) { - enable_irq_wake(lis->pdata->interrupt); - lis->flags |= LIS302DL_F_IRQ_WAKE; - } - - return count; -} - -static ssize_t show_wakeup_threshold(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - - /* All events off? */ - if (lis->wakeup.threshold == 0) - return sprintf(buf, "off\n"); - - return sprintf(buf, "%u\n", lis->wakeup.threshold); -} - -static DEVICE_ATTR(wakeup_threshold, S_IRUGO | S_IWUSR, show_wakeup_threshold, - set_wakeup_threshold); - -static ssize_t set_wakeup_duration(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - unsigned int duration; - - if (sscanf(buf, "%u\n", &duration) != 1) - return -EINVAL; - - if (duration > 2550) - return -ERANGE; - - lis->wakeup.duration = duration; - - return count; -} - -static ssize_t show_wakeup_duration(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct lis302dl_info *lis = dev_get_drvdata(dev); - - return sprintf(buf, "%u\n", lis->wakeup.duration); -} - -static DEVICE_ATTR(wakeup_duration, S_IRUGO | S_IWUSR, show_wakeup_duration, - set_wakeup_duration); - -static struct attribute *lis302dl_sysfs_entries[] = { - &dev_attr_sample_rate.attr, - &dev_attr_full_scale.attr, - &dev_attr_threshold.attr, - &dev_attr_duration.attr, - &dev_attr_dump.attr, - &dev_attr_wakeup_threshold.attr, - &dev_attr_wakeup_duration.attr, - &dev_attr_overruns.attr, - NULL -}; - -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 = input_get_drvdata(inp); - unsigned long flags; - - local_irq_save(flags); - - __enable_data_collection(lis); - lis->flags |= LIS302DL_F_INPUT_OPEN; - - local_irq_restore(flags); - - return 0; -} - -static void lis302dl_input_close(struct input_dev *inp) -{ - struct lis302dl_info *lis = input_get_drvdata(inp); - u_int8_t ctrl1 = LIS302DL_CTRL1_Xen | LIS302DL_CTRL1_Yen | - LIS302DL_CTRL1_Zen; - unsigned long flags; - - local_irq_save(flags); - - /* since the input core already serializes access and makes sure we - * 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); - lis->flags &= ~LIS302DL_F_INPUT_OPEN; - - /* 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); - } - local_irq_restore(flags); -} - -/* get the device to reload its coefficients from EEPROM and wait for it - * to complete - */ - -static int __lis302dl_reset_device(struct lis302dl_info *lis) -{ - int timeout = 10; - - __reg_write(lis, LIS302DL_REG_CTRL2, - LIS302DL_CTRL2_BOOT | LIS302DL_CTRL2_FDS); - - while ((__reg_read(lis, LIS302DL_REG_CTRL2) - & LIS302DL_CTRL2_BOOT) && (timeout--)) - mdelay(1); - - return !!(timeout < 0); -} - -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 = spi->dev.platform_data; - - spi->mode = SPI_MODE_3; - rc = spi_setup(spi); - if (rc < 0) { - dev_err(&spi->dev, "spi_setup failed\n"); - return rc; - } - - lis = kzalloc(sizeof(*lis), GFP_KERNEL); - if (!lis) - return -ENOMEM; - - lis->dev = &spi->dev; - lis->spi = spi; - - dev_set_drvdata(lis->dev, lis); - - lis->pdata = pdata; - - rc = sysfs_create_group(&lis->dev->kobj, &lis302dl_attr_group); - if (rc) { - dev_err(lis->dev, "error creating sysfs group\n"); - goto bail_free_lis; - } - - /* initialize input layer details */ - lis->input_dev = input_allocate_device(); - if (!lis->input_dev) { - dev_err(lis->dev, "Unable to allocate input device\n"); - goto bail_sysfs; - } - - input_set_drvdata(lis->input_dev, lis); - 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; - - rc = input_register_device(lis->input_dev); - if (rc) { - dev_err(lis->dev, "error %d registering input device\n", rc); - goto bail_inp_dev; - } - - local_irq_save(flags); - /* Configure our IO */ - (lis->pdata->lis302dl_suspend_io)(lis, 1); - - wai = __reg_read(lis, LIS302DL_REG_WHO_AM_I); - if (wai != LIS302DL_WHO_AM_I_MAGIC) { - dev_err(lis->dev, "unknown who_am_i signature 0x%02x\n", wai); - dev_set_drvdata(lis->dev, NULL); - rc = -ENODEV; - local_irq_restore(flags); - goto bail_inp_reg; - } - - set_bit(EV_ABS, lis->input_dev->evbit); - input_set_abs_params(lis->input_dev, ABS_X, 0, 0, 0, 0); - input_set_abs_params(lis->input_dev, ABS_Y, 0, 0, 0, 0); - input_set_abs_params(lis->input_dev, ABS_Z, 0, 0, 0, 0); - - lis->threshold = 0; - lis->duration = 0; - memset(&lis->wakeup, 0, sizeof(lis->wakeup)); - - if (__lis302dl_reset_device(lis)) - dev_err(lis->dev, "device BOOT reload failed\n"); - - /* force us powered */ - __reg_write(lis, LIS302DL_REG_CTRL1, LIS302DL_CTRL1_PD | - LIS302DL_CTRL1_Xen | - LIS302DL_CTRL1_Yen | - LIS302DL_CTRL1_Zen); - mdelay(1); - - __reg_write(lis, LIS302DL_REG_CTRL2, 0); - __reg_write(lis, LIS302DL_REG_CTRL3, - LIS302DL_CTRL3_PP_OD | LIS302DL_CTRL3_IHL); - __reg_write(lis, LIS302DL_REG_FF_WU_THS_1, 0x0); - __reg_write(lis, LIS302DL_REG_FF_WU_DURATION_1, 0x00); - __reg_write(lis, LIS302DL_REG_FF_WU_CFG_1, 0x0); - - /* start off in powered down mode; we power up when someone opens us */ - __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(lis->dev, 1, LIS302DL_INTMODE_GND); - __lis302dl_int_mode(lis->dev, 2, LIS302DL_INTMODE_GND); - - __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); - local_irq_restore(flags); - - dev_info(lis->dev, "Found %s\n", pdata->name); - - lis->pdata = pdata; - - set_irq_handler(lis->pdata->interrupt, handle_level_irq); - - rc = request_irq(lis->pdata->interrupt, lis302dl_interrupt, - IRQF_TRIGGER_LOW, "lis302dl", lis); - - if (rc < 0) { - dev_err(lis->dev, "error requesting IRQ %d\n", - lis->pdata->interrupt); - goto bail_inp_reg; - } - 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(&lis->dev->kobj, &lis302dl_attr_group); -bail_free_lis: - kfree(lis); - return rc; -} - -static int __devexit lis302dl_remove(struct spi_device *spi) -{ - struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); - unsigned long flags; - - /* Disable interrupts */ - if (lis->flags & LIS302DL_F_IRQ_WAKE) - disable_irq_wake(lis->pdata->interrupt); - free_irq(lis->pdata->interrupt, lis); - - /* Reset and power down the device */ - local_irq_save(flags); - __reg_write(lis, LIS302DL_REG_CTRL3, 0x00); - __reg_write(lis, LIS302DL_REG_CTRL2, 0x00); - __reg_write(lis, LIS302DL_REG_CTRL1, 0x00); - local_irq_restore(flags); - - /* Cleanup resources */ - 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(lis->dev, NULL); - kfree(lis); - - return 0; -} - -#ifdef CONFIG_PM - -static u8 regs_to_save[] = { - LIS302DL_REG_CTRL1, - LIS302DL_REG_CTRL2, - LIS302DL_REG_CTRL3, - LIS302DL_REG_FF_WU_CFG_1, - LIS302DL_REG_FF_WU_THS_1, - LIS302DL_REG_FF_WU_DURATION_1, - LIS302DL_REG_FF_WU_CFG_2, - LIS302DL_REG_FF_WU_THS_2, - LIS302DL_REG_FF_WU_DURATION_2, - LIS302DL_REG_CLICK_CFG, - LIS302DL_REG_CLICK_THSY_X, - LIS302DL_REG_CLICK_THSZ, - LIS302DL_REG_CLICK_TIME_LIMIT, - LIS302DL_REG_CLICK_LATENCY, - LIS302DL_REG_CLICK_WINDOW, - -}; - -static int lis302dl_suspend(struct spi_device *spi, pm_message_t state) -{ - struct lis302dl_info *lis = dev_get_drvdata(&spi->dev); - unsigned long flags; - u_int8_t tmp; - int n; - - /* determine if we want to wake up from the accel. */ - if (lis->flags & LIS302DL_F_WUP_CLICK) - return 0; - - disable_irq(lis->pdata->interrupt); - local_irq_save(flags); - - /* - * When we share SPI over multiple sensors, there is a race here - * that one or more sensors will lose. In that case, the shared - * SPI bus GPIO will be in sleep mode and partially pulled down. So - * we explicitly put our IO into "wake" mode here before the final - * traffic to the sensor. - */ - (lis->pdata->lis302dl_suspend_io)(lis, 1); - - /* save registers */ - for (n = 0; n < ARRAY_SIZE(regs_to_save); n++) - lis->regs[regs_to_save[n]] = - __reg_read(lis, regs_to_save[n]); - - /* power down or enable wakeup */ - - if (lis->wakeup.threshold == 0) { - tmp = __reg_read(lis, LIS302DL_REG_CTRL1); - tmp &= ~LIS302DL_CTRL1_PD; - __reg_write(lis, LIS302DL_REG_CTRL1, tmp); - } else - __enable_wakeup(lis); - - /* place our IO to the device in sleep-compatible states */ - (lis->pdata->lis302dl_suspend_io)(lis, 0); - - 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; - int n; - - if (lis->flags & LIS302DL_F_WUP_CLICK) - return 0; - - local_irq_save(flags); - - /* get our IO to the device back in operational states */ - (lis->pdata->lis302dl_suspend_io)(lis, 1); - - /* resume from powerdown first! */ - __reg_write(lis, LIS302DL_REG_CTRL1, - LIS302DL_CTRL1_PD | - LIS302DL_CTRL1_Xen | - LIS302DL_CTRL1_Yen | - LIS302DL_CTRL1_Zen); - mdelay(1); - - if (__lis302dl_reset_device(lis)) - dev_err(&spi->dev, "device BOOT reload failed\n"); - - lis->regs[LIS302DL_REG_CTRL1] |= LIS302DL_CTRL1_PD | - LIS302DL_CTRL1_Xen | - LIS302DL_CTRL1_Yen | - LIS302DL_CTRL1_Zen; - - /* restore registers after resume */ - for (n = 0; n < ARRAY_SIZE(regs_to_save); n++) - __reg_write(lis, regs_to_save[n], lis->regs[regs_to_save[n]]); - - /* if someone had us open, reset the non-wake threshold stuff */ - if (lis->flags & LIS302DL_F_INPUT_OPEN) - __enable_data_collection(lis); - - local_irq_restore(flags); - enable_irq(lis->pdata->interrupt); - - return 0; -} -#else -#define lis302dl_suspend NULL -#define lis302dl_resume NULL -#endif - -static struct spi_driver lis302dl_spi_driver = { - .driver = { - .name = "lis302dl", - .owner = THIS_MODULE, - }, - - .probe = lis302dl_probe, - .remove = __devexit_p(lis302dl_remove), - .suspend = lis302dl_suspend, - .resume = lis302dl_resume, -}; - -static int __devinit lis302dl_init(void) -{ - return spi_register_driver(&lis302dl_spi_driver); -} - -static void __exit lis302dl_exit(void) -{ - spi_unregister_driver(&lis302dl_spi_driver); -} - -MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>"); -MODULE_LICENSE("GPL"); - -module_init(lis302dl_init); -module_exit(lis302dl_exit); |