diff options
Diffstat (limited to 'recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch')
-rw-r--r-- | recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch | 1759 |
1 files changed, 0 insertions, 1759 deletions
diff --git a/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch b/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch deleted file mode 100644 index 4365e9312a..0000000000 --- a/recipes/linux/linux-openmoko-2.6.34/0001-accels.patch.patch +++ /dev/null @@ -1,1759 +0,0 @@ -From 1b8efb3ae35ba6a3dfcf03b28bab9ce945a9b294 Mon Sep 17 00:00:00 2001 -From: Radek Polak <psonek2@seznam.cz> -Date: Fri, 9 Apr 2010 09:15:40 +0200 -Subject: [PATCH 11/17] accels.patch - -adds support for accelerometers. You will need include/linux/lis302dl.h and -drivers/input/misc/lis302dl.c from andy-tracking. The patch needs -spi_bitbang_transfer_sync() and bitbang_work() to be ported correctly (some -fixes from original 2.6.32 are missing). - -Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com> ---- - arch/arm/mach-s3c2410/include/mach/spi-gpio.h | 3 +- - arch/arm/mach-s3c2440/mach-gta02.c | 157 ++++ - drivers/input/misc/Kconfig | 9 + - drivers/input/misc/Makefile | 1 + - drivers/input/misc/lis302dl.c | 952 +++++++++++++++++++++++++ - drivers/spi/spi_bitbang.c | 231 ++++--- - drivers/spi/spi_s3c24xx_gpio.c | 7 +- - include/linux/lis302dl.h | 152 ++++ - include/linux/spi/spi.h | 30 + - include/linux/spi/spi_bitbang.h | 5 + - 10 files changed, 1433 insertions(+), 114 deletions(-) - create mode 100644 drivers/input/misc/lis302dl.c - create mode 100644 include/linux/lis302dl.h - -diff --git a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h -index dcef228..8eedc9c 100644 ---- a/arch/arm/mach-s3c2410/include/mach/spi-gpio.h -+++ b/arch/arm/mach-s3c2410/include/mach/spi-gpio.h -@@ -21,7 +21,8 @@ struct s3c2410_spigpio_info { - int num_chipselect; - int bus_num; - -- void (*chip_select)(struct s3c2410_spigpio_info *spi, int cs); -+ int non_blocking_transfer; -+ void (*chip_select)(struct s3c2410_spigpio_info *spi, int csid, int cs); - }; - - -diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c -index 9a9461d..1fa93b4 100644 ---- a/arch/arm/mach-s3c2440/mach-gta02.c -+++ b/arch/arm/mach-s3c2440/mach-gta02.c -@@ -62,6 +62,7 @@ - - #include <linux/input.h> - #include <linux/gpio_keys.h> -+#include <linux/lis302dl.h> - - #include <linux/leds.h> - #include <linux/leds_pwm.h> -@@ -111,6 +112,22 @@ - #include <linux/glamofb.h> - #include <linux/mfd/glamo.h> - -+#define S3C2410_GPIONO(bank,offset) ((bank) + (offset)) -+ -+#define S3C2410_GPIO_BANKD (32*3) -+#define S3C2410_GPIO_BANKG (32*6) -+ -+#define S3C2410_GPG5 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 5) -+#define S3C2410_GPG6 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 6) -+#define S3C2410_GPG7 S3C2410_GPIONO(S3C2410_GPIO_BANKG, 7) -+#define S3C2410_GPD12 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 12) -+#define S3C2410_GPD13 S3C2410_GPIONO(S3C2410_GPIO_BANKD, 13) -+ -+#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */ -+#define BITBANG_CS_INACTIVE 0 -+ -+#define S3C_SYSTEM_REV_ATAG GTA02v6_SYSTEM_REV -+ - static struct pcf50633 *gta02_pcf; - - /* -@@ -322,6 +339,60 @@ const static struct jbt6k74_platform_data jbt6k74_pdata = { - .gpio_reset = GTA02_GPIO_GLAMO(4), - }; - -+/*----------- SPI: Accelerometers attached to SPI of s3c244x ----------------- */ -+ -+void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume) -+{ -+ struct lis302dl_platform_data *pdata = lis->pdata; -+ -+ if (!resume) { -+ /* -+ * we don't want to power them with a high level -+ * because GSENSOR_3V3 is not up during suspend -+ */ -+ s3c2410_gpio_setpin(pdata->pin_chip_select, 0); -+ s3c2410_gpio_setpin(pdata->pin_clk, 0); -+ s3c2410_gpio_setpin(pdata->pin_mosi, 0); -+ /* misnomer: it is a pullDOWN in 2442 */ -+ s3c2410_gpio_pullup(pdata->pin_miso, 1); -+ return; -+ } -+ -+ /* back to normal */ -+ s3c2410_gpio_setpin(pdata->pin_chip_select, 1); -+ s3c2410_gpio_setpin(pdata->pin_clk, 1); -+ /* misnomer: it is a pullDOWN in 2442 */ -+ s3c2410_gpio_pullup(pdata->pin_miso, 0); -+ -+ s3c2410_gpio_cfgpin(pdata->pin_chip_select, S3C2410_GPIO_OUTPUT); -+ s3c2410_gpio_cfgpin(pdata->pin_clk, S3C2410_GPIO_OUTPUT); -+ s3c2410_gpio_cfgpin(pdata->pin_mosi, S3C2410_GPIO_OUTPUT); -+ s3c2410_gpio_cfgpin(pdata->pin_miso, S3C2410_GPIO_INPUT); -+ -+} -+ -+struct lis302dl_platform_data lis302_pdata_top = { -+ .name = "lis302-1 (top)", -+ .pin_chip_select= S3C2410_GPD12, -+ .pin_clk = S3C2410_GPG7, -+ .pin_mosi = S3C2410_GPG6, -+ .pin_miso = S3C2410_GPG5, -+ .interrupt = GTA02_IRQ_GSENSOR_1, -+ .open_drain = 1, /* altered at runtime by PCB rev */ -+ .lis302dl_suspend_io = gta02_lis302dl_suspend_io, -+}; -+ -+struct lis302dl_platform_data lis302_pdata_bottom = { -+ .name = "lis302-2 (bottom)", -+ .pin_chip_select= S3C2410_GPD13, -+ .pin_clk = S3C2410_GPG7, -+ .pin_mosi = S3C2410_GPG6, -+ .pin_miso = S3C2410_GPG5, -+ .interrupt = GTA02_IRQ_GSENSOR_2, -+ .open_drain = 1, /* altered at runtime by PCB rev */ -+ .lis302dl_suspend_io = gta02_lis302dl_suspend_io, -+}; -+ - static struct spi_board_info gta02_spi_board_info[] = { - { - .modalias = "jbt6k74", -@@ -332,6 +403,81 @@ static struct spi_board_info gta02_spi_board_info[] = { - .bus_num = 2, - .chip_select = 0 - }, -+ { -+ .modalias = "lis302dl", -+ /* platform_data */ -+ .platform_data = &lis302_pdata_top, -+ /* controller_data */ -+ /* irq */ -+ .max_speed_hz = 100 * 1000, -+ .bus_num = 3, -+ .chip_select = 0, -+ }, -+ { -+ .modalias = "lis302dl", -+ /* platform_data */ -+ .platform_data = &lis302_pdata_bottom, -+ /* controller_data */ -+ /* irq */ -+ .max_speed_hz = 100 * 1000, -+ .bus_num = 3, -+ .chip_select = 1, -+ }, -+}; -+ -+static void gta02_lis302_chip_select(struct s3c2410_spigpio_info *info, int csid, int cs) -+{ -+ -+ /* -+ * Huh... "quirk"... CS on this device is not really "CS" like you can -+ * expect. -+ * -+ * When it is 0 it selects SPI interface mode. -+ * When it is 1 it selects I2C interface mode. -+ * -+ * Because we have 2 devices on one interface we have to make sure -+ * that the "disabled" device (actually in I2C mode) don't think we're -+ * talking to it. -+ * -+ * When we talk to the "enabled" device, the "disabled" device sees -+ * the clocks as I2C clocks, creating havoc. -+ * -+ * I2C sees MOSI going LOW while CLK HIGH as a START action, thus we -+ * must ensure this is never issued. -+ */ -+ -+ int cs_gpio, other_cs_gpio; -+ -+ cs_gpio = csid ? S3C2410_GPD13 : S3C2410_GPD12; -+ other_cs_gpio = (1 - csid) ? S3C2410_GPD13 : S3C2410_GPD12; -+ -+ -+ if (cs == BITBANG_CS_ACTIVE) { -+ s3c2410_gpio_setpin(other_cs_gpio, 1); -+ s3c2410_gpio_setpin(cs_gpio, 1); -+ s3c2410_gpio_setpin(info->pin_clk, 1); -+ s3c2410_gpio_setpin(cs_gpio, 0); -+ } else { -+ s3c2410_gpio_setpin(cs_gpio, 1); -+ s3c2410_gpio_setpin(other_cs_gpio, 1); -+ } -+} -+ -+static struct s3c2410_spigpio_info gta02_spigpio_cfg = { -+ .pin_clk = S3C2410_GPG7, -+ .pin_mosi = S3C2410_GPG6, -+ .pin_miso = S3C2410_GPG5, -+ .bus_num = 3, -+ .num_chipselect = 2, -+ .chip_select = gta02_lis302_chip_select, -+ .non_blocking_transfer = 1, -+}; -+ -+static struct platform_device gta02_spi_gpio_dev = { -+ .name = "spi_s3c24xx_gpio", -+ .dev = { -+ .platform_data = >a02_spigpio_cfg, -+ }, - }; - - static struct resource gta02_glamo_resources[] = { -@@ -1091,6 +1237,7 @@ static struct platform_device *gta02_devices[] __initdata = { - >a02_pm_bt_dev, - >a02_pm_wlan_dev, - >a02_glamo_dev, -+ >a02_spi_gpio_dev, - &s3c_device_adc, - &s3c_device_ts, - }; -@@ -1302,6 +1449,16 @@ static void __init gta02_machine_init(void) - /* Set the panic callback to make AUX LED blink at ~5Hz. */ - panic_blink = gta02_panic_blink; - -+ switch (S3C_SYSTEM_REV_ATAG) { -+ case GTA02v6_SYSTEM_REV: -+ /* we need push-pull interrupt from motion sensors */ -+ lis302_pdata_top.open_drain = 0; -+ lis302_pdata_bottom.open_drain = 0; -+ break; -+ default: -+ break; -+ } -+ - bus_register_notifier(&platform_bus_type, >a02_device_register_notifier); - bus_register_notifier(&spi_bus_type, >a02_device_register_notifier); - -diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig -index 23140a3..d8d0932 100644 ---- a/drivers/input/misc/Kconfig -+++ b/drivers/input/misc/Kconfig -@@ -340,4 +340,13 @@ config INPUT_PCAP - To compile this driver as a module, choose M here: the - module will be called pcap_keys. - -+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 7e95a5d..5b810db 100644 ---- a/drivers/input/misc/Makefile -+++ b/drivers/input/misc/Makefile -@@ -32,4 +32,5 @@ obj-$(CONFIG_INPUT_WINBOND_CIR) += winbond-cir.o - obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o - obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o - obj-$(CONFIG_INPUT_YEALINK) += yealink.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..d345bfb ---- /dev/null -+++ b/drivers/input/misc/lis302dl.c -@@ -0,0 +1,952 @@ -+/* 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_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, -+ LIS302DL_REG_CTRL1, -+}; -+ -+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"); -+ -+ /* 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); -diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c -index 5265330..24c61a6 100644 ---- a/drivers/spi/spi_bitbang.c -+++ b/drivers/spi/spi_bitbang.c -@@ -254,134 +254,139 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t) - * Drivers can provide word-at-a-time i/o primitives, or provide - * transfer-at-a-time ones to leverage dma or fifo hardware. - */ --static void bitbang_work(struct work_struct *work) -+/* Synchronous non blocking transfer */ -+int -+spi_bitbang_transfer_sync(struct spi_device *spi, struct spi_message *m) - { -- struct spi_bitbang *bitbang = -- container_of(work, struct spi_bitbang, work); -- unsigned long flags; -- int do_setup = -1; -- int (*setup_transfer)(struct spi_device *, -- struct spi_transfer *); -+ struct spi_bitbang *bitbang = spi_master_get_devdata(spi->master); -+ struct spi_transfer *t; -+ unsigned long flags; -+ int cs_change = 1; -+ int status; -+ int nsecs; -+ int (*setup_transfer)(struct spi_device *, struct spi_transfer *); -+ -+ /* FIXME this is made-up ... the correct value is known to -+ * word-at-a-time bitbang code, and presumably chipselect() -+ * should enforce these requirements too? -+ */ -+ nsecs = 100; -+ cs_change = 1; -+ status = 0; -+ setup_transfer = NULL; -+ -+ local_irq_save(flags); -+ list_for_each_entry (t, &m->transfers, transfer_list) { -+ /* override or restore speed and wordsize */ -+ if (t->speed_hz || t->bits_per_word) { -+ setup_transfer = bitbang->setup_transfer; -+ if (!setup_transfer) { -+ status = -ENOPROTOOPT; -+ break; -+ } -+ } -+ if (setup_transfer) { -+ status = setup_transfer(spi, t); -+ if (status < 0) -+ break; -+ } - -- setup_transfer = bitbang->setup_transfer; -+ /* set up default clock polarity, and activate chip; -+ * this implicitly updates clock and spi modes as -+ * previously recorded for this device via setup(). -+ * (and also deselects any other chip that might be -+ * selected ...) -+ */ - -- spin_lock_irqsave(&bitbang->lock, flags); -- bitbang->busy = 1; -- while (!list_empty(&bitbang->queue)) { -- struct spi_message *m; -- struct spi_device *spi; -- unsigned nsecs; -- struct spi_transfer *t = NULL; -- unsigned tmp; -- unsigned cs_change; -- int status; -+ if (cs_change) { -+ bitbang->chipselect(spi, BITBANG_CS_ACTIVE); -+ ndelay(nsecs); -+ } - -- m = container_of(bitbang->queue.next, struct spi_message, -- queue); -- list_del_init(&m->queue); -- spin_unlock_irqrestore(&bitbang->lock, flags); -+ cs_change = t->cs_change; -+ if (!t->tx_buf && !t->rx_buf && t->len) { -+ status = -EINVAL; -+ break; -+ } - -- /* FIXME this is made-up ... the correct value is known to -- * word-at-a-time bitbang code, and presumably chipselect() -- * should enforce these requirements too? -+ /* transfer data. the lower level code handles any -+ * new dma mappings it needs. our caller always gave -+ * us dma-safe buffers. - */ -- nsecs = 100; -+ if (t->len) { -+ /* REVISIT dma API still needs a designated -+ * DMA_ADDR_INVALID; ~0 might be better. -+ */ -+ if (!m->is_dma_mapped) -+ t->rx_dma = t->tx_dma = 0; -+ status = bitbang->txrx_bufs(spi, t); -+ } - -- spi = m->spi; -- tmp = 0; -- cs_change = 1; -+ if (status > 0) -+ m->actual_length += status; -+ if (status != t->len) { -+ /* always report some kind of error */ -+ if (status >= 0) -+ status = -EREMOTEIO; -+ break; -+ } - status = 0; -+ /* protocol tweaks before next transfer */ -+ if (t->delay_usecs) -+ udelay(t->delay_usecs); -+ if (!cs_change) -+ continue; -+ if (t->transfer_list.next == &m->transfers) -+ break; -+ /* sometimes a short mid-message deselect of the chip -+ * may be needed to terminate a mode or command -+ */ -+ ndelay(nsecs); -+ bitbang->chipselect(spi, BITBANG_CS_INACTIVE); -+ ndelay(nsecs); -+ } - -- list_for_each_entry (t, &m->transfers, transfer_list) { -- -- /* override speed or wordsize? */ -- if (t->speed_hz || t->bits_per_word) -- do_setup = 1; -- -- /* init (-1) or override (1) transfer params */ -- if (do_setup != 0) { -- if (!setup_transfer) { -- status = -ENOPROTOOPT; -- break; -- } -- status = setup_transfer(spi, t); -- if (status < 0) -- break; -- } -+ m->status = status; -+ if (m->complete) -+ m->complete(m->context); - -- /* set up default clock polarity, and activate chip; -- * this implicitly updates clock and spi modes as -- * previously recorded for this device via setup(). -- * (and also deselects any other chip that might be -- * selected ...) -- */ -- if (cs_change) { -- bitbang->chipselect(spi, BITBANG_CS_ACTIVE); -- ndelay(nsecs); -- } -- cs_change = t->cs_change; -- if (!t->tx_buf && !t->rx_buf && t->len) { -- status = -EINVAL; -- break; -- } -+ /* restore speed and wordsize */ -+ if (setup_transfer) -+ setup_transfer(spi, NULL); - -- /* transfer data. the lower level code handles any -- * new dma mappings it needs. our caller always gave -- * us dma-safe buffers. -- */ -- if (t->len) { -- /* REVISIT dma API still needs a designated -- * DMA_ADDR_INVALID; ~0 might be better. -- */ -- if (!m->is_dma_mapped) -- t->rx_dma = t->tx_dma = 0; -- status = bitbang->txrx_bufs(spi, t); -- } -- if (status > 0) -- m->actual_length += status; -- if (status != t->len) { -- /* always report some kind of error */ -- if (status >= 0) -- status = -EREMOTEIO; -- break; -- } -- status = 0; -- -- /* protocol tweaks before next transfer */ -- if (t->delay_usecs) -- udelay(t->delay_usecs); -+ /* normally deactivate chipselect ... unless no error and -+ * cs_change has hinted that the next message will probably -+ * be for this chip too. -+ */ -+ if (!(status == 0 && cs_change)) { -+ ndelay(nsecs); -+ bitbang->chipselect(spi, BITBANG_CS_INACTIVE); -+ ndelay(nsecs); -+ } - -- if (!cs_change) -- continue; -- if (t->transfer_list.next == &m->transfers) -- break; -+ local_irq_restore(flags); - -- /* sometimes a short mid-message deselect of the chip -- * may be needed to terminate a mode or command -- */ -- ndelay(nsecs); -- bitbang->chipselect(spi, BITBANG_CS_INACTIVE); -- ndelay(nsecs); -- } -+ return status; -+} -+EXPORT_SYMBOL_GPL(spi_bitbang_transfer_sync); - -- m->status = status; -- m->complete(m->context); -+static void bitbang_work(struct work_struct *work) -+{ -+ struct spi_bitbang *bitbang = -+ container_of(work, struct spi_bitbang, work); -+ unsigned long flags; - -- /* restore speed and wordsize if it was overridden */ -- if (do_setup == 1) -- setup_transfer(spi, NULL); -- do_setup = 0; -+ spin_lock_irqsave(&bitbang->lock, flags); -+ bitbang->busy = 1; -+ while (!list_empty(&bitbang->queue)) { -+ struct spi_message *m; - -- /* normally deactivate chipselect ... unless no error and -- * cs_change has hinted that the next message will probably -- * be for this chip too. -- */ -- if (!(status == 0 && cs_change)) { -- ndelay(nsecs); -- bitbang->chipselect(spi, BITBANG_CS_INACTIVE); -- ndelay(nsecs); -- } -+ m = container_of(bitbang->queue.next, struct spi_message, -+ queue); -+ list_del_init(&m->queue); - -+ spin_unlock_irqrestore(&bitbang->lock, flags); -+ spi_bitbang_transfer_sync(m->spi, m); - spin_lock_irqsave(&bitbang->lock, flags); - } - bitbang->busy = 0; -@@ -456,6 +461,10 @@ int spi_bitbang_start(struct spi_bitbang *bitbang) - - if (!bitbang->master->transfer) - bitbang->master->transfer = spi_bitbang_transfer; -+ -+ if (!bitbang->master->transfer_sync && bitbang->non_blocking_transfer) -+ bitbang->master->transfer_sync = spi_bitbang_transfer_sync; -+ - if (!bitbang->txrx_bufs) { - bitbang->use_dma = 0; - bitbang->txrx_bufs = spi_bitbang_bufs; -diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c -index bbf9371..5685b78 100644 ---- a/drivers/spi/spi_s3c24xx_gpio.c -+++ b/drivers/spi/spi_s3c24xx_gpio.c -@@ -92,7 +92,7 @@ static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value) - struct s3c2410_spigpio *sg = spidev_to_sg(dev); - - if (sg->info && sg->info->chip_select) -- (sg->info->chip_select)(sg->info, value); -+ (sg->info->chip_select)(sg->info, dev->chip_select, value); - } - - static int s3c2410_spigpio_probe(struct platform_device *dev) -@@ -113,14 +113,17 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) - - platform_set_drvdata(dev, sp); - -- /* copy in the plkatform data */ -+ /* copy in the platform data */ - info = sp->info = dev->dev.platform_data; - -+ master->num_chipselect = info->num_chipselect; -+ - /* setup spi bitbang adaptor */ - sp->bitbang.master = spi_master_get(master); - sp->bitbang.master->bus_num = info->bus_num; - sp->bitbang.master->num_chipselect = info->num_chipselect; - sp->bitbang.chipselect = s3c2410_spigpio_chipselect; -+ sp->bitbang.non_blocking_transfer = info->non_blocking_transfer; - - sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0; - sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1; -diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h -new file mode 100644 -index 0000000..0c1fc30 ---- /dev/null -+++ b/include/linux/lis302dl.h -@@ -0,0 +1,152 @@ -+#ifndef _LINUX_LIS302DL_H -+#define _LINUX_LIS302DL_H -+ -+#include <linux/types.h> -+#include <linux/spi/spi.h> -+#include <linux/input.h> -+#include <linux/workqueue.h> -+ -+struct lis302dl_info; -+ -+struct lis302dl_platform_data { -+ char *name; -+ unsigned long pin_chip_select; -+ unsigned long pin_clk; -+ unsigned long pin_mosi; -+ unsigned long pin_miso; -+ int open_drain; -+ int interrupt; -+ void (*lis302dl_suspend_io)(struct lis302dl_info *, int resuming); -+}; -+ -+struct lis302dl_info { -+ struct lis302dl_platform_data *pdata; -+ struct device *dev; -+ struct input_dev *input_dev; -+ unsigned int flags; -+ unsigned int threshold; -+ unsigned int duration; -+ u32 overruns; -+ struct { -+ unsigned int threshold; /* mg */ -+ unsigned int duration; /* ms */ -+ } wakeup; -+ -+ struct spi_device *spi; -+ u_int8_t regs[0x40]; -+}; -+ -+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_ctrl2 { -+ LIS302DL_CTRL2_HPC1 = 0x01, -+ LIS302DL_CTRL2_HPC2 = 0x02, -+ LIS302DL_CTRL2_HPFF1 = 0x04, -+ LIS302DL_CTRL2_HPFF2 = 0x08, -+ LIS302DL_CTRL2_FDS = 0x10, -+ LIS302DL_CTRL2_BOOT = 0x40, -+ LIS302DL_CTRL2_SIM = 0x80, -+}; -+enum lis302dl_reg_ctrl3 { -+ LIS302DL_CTRL3_PP_OD = 0x40, -+ LIS302DL_CTRL3_IHL = 0x80, -+}; -+ -+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, -+}; -+ -+/* Wakeup/freefall interrupt defs */ -+enum lis302dl_reg_ffwucfg { -+ LIS302DL_FFWUCFG_XLIE = 0x01, -+ LIS302DL_FFWUCFG_XHIE = 0x02, -+ LIS302DL_FFWUCFG_YLIE = 0x04, -+ LIS302DL_FFWUCFG_YHIE = 0x08, -+ LIS302DL_FFWUCFG_ZLIE = 0x10, -+ LIS302DL_FFWUCFG_ZHIE = 0x20, -+ LIS302DL_FFWUCFG_LIR = 0x40, -+ LIS302DL_FFWUCFG_AOI = 0x80, -+}; -+ -+enum lis302dl_reg_ffwuths { -+ LIS302DL_FFWUTHS_DCRM = 0x80, -+}; -+ -+enum lis302dl_reg_ffwusrc { -+ LIS302DL_FFWUSRC_XL = 0x01, -+ LIS302DL_FFWUSRC_XH = 0x02, -+ LIS302DL_FFWUSRC_YL = 0x04, -+ LIS302DL_FFWUSRC_YH = 0x08, -+ LIS302DL_FFWUSRC_ZL = 0x10, -+ LIS302DL_FFWUSRC_ZH = 0x20, -+ LIS302DL_FFWUSRC_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, -+}; -+ -+#define LIS302DL_WHO_AM_I_MAGIC 0x3b -+ -+#define LIS302DL_F_WUP_FF_1 0x0001 /* wake up from free fall */ -+#define LIS302DL_F_WUP_FF_2 0x0002 -+#define LIS302DL_F_WUP_FF 0x0003 -+#define LIS302DL_F_WUP_CLICK 0x0004 -+#define LIS302DL_F_POWER 0x0010 -+#define LIS302DL_F_FS 0x0020 /* ADC full scale */ -+#define LIS302DL_F_INPUT_OPEN 0x0040 /* Set if input device is opened */ -+#define LIS302DL_F_IRQ_WAKE 0x0080 /* IRQ is setup in wake mode */ -+#define LIS302DL_F_DR 0x0100 /* Data rate, 400Hz/100Hz */ -+ -+ -+#endif /* _LINUX_LIS302DL_H */ -+ -diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h -index af56071..83ad05d 100644 ---- a/include/linux/spi/spi.h -+++ b/include/linux/spi/spi.h -@@ -292,6 +292,13 @@ struct spi_master { - int (*transfer)(struct spi_device *spi, - struct spi_message *mesg); - -+ /* -+ * Synchronous non blocking transfer function. Should guarantee -+ * data availability when it returns -+ */ -+ int (*transfer_sync)(struct spi_device *spi, -+ struct spi_message *mesg); -+ - /* called on release() to free memory provided by spi_master */ - void (*cleanup)(struct spi_device *spi); - }; -@@ -543,6 +550,29 @@ static inline void spi_message_free(struct spi_message *m) - extern int spi_setup(struct spi_device *spi); - extern int spi_async(struct spi_device *spi, struct spi_message *message); - -+/** -+ * spi_non_blocking_transfer - Synchronous, non blocking transfer -+ * @spi: device with which data will be exchanged -+ * @message: describes the data transfers with optional completion handlers -+ * Context: any (irqs may be blocked, etc) -+ * -+ * Data is guaranteed to be written or read when this function returns. -+ * -+ * Note : This may not be supported by all spi masters. -+ */ -+ -+static inline int -+spi_non_blocking_transfer(struct spi_device *spi, struct spi_message *message) -+{ -+ if (unlikely(!spi->master->transfer_sync)) { -+ dev_err(&spi->master->dev, -+ "non-blocking transfers not supported\n"); -+ return -EIO; -+ } -+ -+ return spi->master->transfer_sync(spi, message); -+} -+ - /*---------------------------------------------------------------------------*/ - - /* All these synchronous SPI transfer routines are utilities layered -diff --git a/include/linux/spi/spi_bitbang.h b/include/linux/spi/spi_bitbang.h -index 3274c50..6dc9b8b 100644 ---- a/include/linux/spi/spi_bitbang.h -+++ b/include/linux/spi/spi_bitbang.h -@@ -31,6 +31,9 @@ struct spi_bitbang { - u8 use_dma; - u8 flags; /* extra spi->mode support */ - -+ /* Support for synchronous non blocking transfers */ -+ int non_blocking_transfer; -+ - struct spi_master *master; - - /* setup_transfer() changes clock and/or wordsize to match settings -@@ -62,6 +65,8 @@ extern void spi_bitbang_cleanup(struct spi_device *spi); - extern int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m); - extern int spi_bitbang_setup_transfer(struct spi_device *spi, - struct spi_transfer *t); -+extern int spi_bitbang_transfer_sync(struct spi_device *spi, -+ struct spi_message *m); - - /* start or stop queue processing */ - extern int spi_bitbang_start(struct spi_bitbang *spi); --- -1.7.3 - |