summaryrefslogtreecommitdiff
path: root/drivers/input/misc
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2010-08-02 18:35:17 -0700
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2010-08-02 18:35:17 -0700
commitd01d0756f75e7a5b4b43764ad45b83c4340f11d6 (patch)
tree90db2ff7ccb35a8fdcf98366e6404afe1f845bc4 /drivers/input/misc
parentb326b853dca2f410b254198ee89abad71a2f4668 (diff)
parent0d87c7228a49e8342d60dd552892e470e0b291fa (diff)
Merge branch 'next' into for-linus
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/Kconfig48
-rw-r--r--drivers/input/misc/Makefile4
-rw-r--r--drivers/input/misc/adxl34x-i2c.c163
-rw-r--r--drivers/input/misc/adxl34x-spi.c145
-rw-r--r--drivers/input/misc/adxl34x.c915
-rw-r--r--drivers/input/misc/adxl34x.h30
-rw-r--r--drivers/input/misc/atlas_btns.c38
-rw-r--r--drivers/input/misc/pwm-beeper.c199
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c12
-rw-r--r--drivers/input/misc/wistron_btns.c4
10 files changed, 1528 insertions, 30 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index c44b9eafc556..b49e23379723 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -327,6 +327,17 @@ config INPUT_PCF8574
To compile this driver as a module, choose M here: the
module will be called pcf8574_keypad.
+config INPUT_PWM_BEEPER
+ tristate "PWM beeper support"
+ depends on HAVE_PWM
+ help
+ Say Y here to get support for PWM based beeper devices.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pwm-beeper.
+
config INPUT_GPIO_ROTARY_ENCODER
tristate "Rotary encoders connected to GPIO pins"
depends on GPIOLIB && GENERIC_GPIO
@@ -390,4 +401,41 @@ config INPUT_PCAP
To compile this driver as a module, choose M here: the
module will be called pcap_keys.
+config INPUT_ADXL34X
+ tristate "Analog Devices ADXL34x Three-Axis Digital Accelerometer"
+ default n
+ help
+ Say Y here if you have a Accelerometer interface using the
+ ADXL345/6 controller, and your board-specific initialization
+ code includes that in its table of devices.
+
+ This driver can use either I2C or SPI communication to the
+ ADXL345/6 controller. Select the appropriate method for
+ your system.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called adxl34x.
+
+config INPUT_ADXL34X_I2C
+ tristate "support I2C bus connection"
+ depends on INPUT_ADXL34X && I2C
+ default y
+ help
+ Say Y here if you have ADXL345/6 hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adxl34x-i2c.
+
+config INPUT_ADXL34X_SPI
+ tristate "support SPI bus connection"
+ depends on INPUT_ADXL34X && SPI
+ default y
+ help
+ Say Y here if you have ADXL345/6 hooked to a SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adxl34x-spi.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 71fe57d8023f..19ccca78fa76 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -8,6 +8,9 @@ obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o
+obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
+obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
+obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
@@ -26,6 +29,7 @@ obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
+obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
new file mode 100644
index 000000000000..0779724af7e7
--- /dev/null
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -0,0 +1,163 @@
+/*
+ * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include "adxl34x.h"
+
+static int adxl34x_smbus_read(struct device *dev, unsigned char reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int adxl34x_smbus_write(struct device *dev,
+ unsigned char reg, unsigned char val)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adxl34x_smbus_read_block(struct device *dev,
+ unsigned char reg, int count,
+ void *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_read_i2c_block_data(client, reg, count, buf);
+}
+
+static int adxl34x_i2c_read_block(struct device *dev,
+ unsigned char reg, int count,
+ void *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_master_send(client, &reg, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_master_recv(client, buf, count);
+ if (ret < 0)
+ return ret;
+
+ if (ret != count)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct adxl34x_bus_ops adxl34x_smbus_bops = {
+ .bustype = BUS_I2C,
+ .write = adxl34x_smbus_write,
+ .read = adxl34x_smbus_read,
+ .read_block = adxl34x_smbus_read_block,
+};
+
+static const struct adxl34x_bus_ops adxl34x_i2c_bops = {
+ .bustype = BUS_I2C,
+ .write = adxl34x_smbus_write,
+ .read = adxl34x_smbus_read,
+ .read_block = adxl34x_i2c_read_block,
+};
+
+static int __devinit adxl34x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adxl34x *ac;
+ int error;
+
+ error = i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!error) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ ac = adxl34x_probe(&client->dev, client->irq, false,
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK) ?
+ &adxl34x_smbus_bops : &adxl34x_i2c_bops);
+ if (IS_ERR(ac))
+ return PTR_ERR(ac);
+
+ i2c_set_clientdata(client, ac);
+
+ return 0;
+}
+
+static int __devexit adxl34x_i2c_remove(struct i2c_client *client)
+{
+ struct adxl34x *ac = i2c_get_clientdata(client);
+
+ return adxl34x_remove(ac);
+}
+
+#ifdef CONFIG_PM
+static int adxl34x_i2c_suspend(struct i2c_client *client, pm_message_t message)
+{
+ struct adxl34x *ac = i2c_get_clientdata(client);
+
+ adxl34x_suspend(ac);
+
+ return 0;
+}
+
+static int adxl34x_i2c_resume(struct i2c_client *client)
+{
+ struct adxl34x *ac = i2c_get_clientdata(client);
+
+ adxl34x_resume(ac);
+
+ return 0;
+}
+#else
+# define adxl34x_i2c_suspend NULL
+# define adxl34x_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id adxl34x_id[] = {
+ { "adxl34x", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, adxl34x_id);
+
+static struct i2c_driver adxl34x_driver = {
+ .driver = {
+ .name = "adxl34x",
+ .owner = THIS_MODULE,
+ },
+ .probe = adxl34x_i2c_probe,
+ .remove = __devexit_p(adxl34x_i2c_remove),
+ .suspend = adxl34x_i2c_suspend,
+ .resume = adxl34x_i2c_resume,
+ .id_table = adxl34x_id,
+};
+
+static int __init adxl34x_i2c_init(void)
+{
+ return i2c_add_driver(&adxl34x_driver);
+}
+module_init(adxl34x_i2c_init);
+
+static void __exit adxl34x_i2c_exit(void)
+{
+ i2c_del_driver(&adxl34x_driver);
+}
+module_exit(adxl34x_i2c_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c
new file mode 100644
index 000000000000..782de9e89828
--- /dev/null
+++ b/drivers/input/misc/adxl34x-spi.c
@@ -0,0 +1,145 @@
+/*
+ * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_SPI */
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include "adxl34x.h"
+
+#define MAX_SPI_FREQ_HZ 5000000
+#define MAX_FREQ_NO_FIFODELAY 1500000
+#define ADXL34X_CMD_MULTB (1 << 6)
+#define ADXL34X_CMD_READ (1 << 7)
+#define ADXL34X_WRITECMD(reg) (reg & 0x3F)
+#define ADXL34X_READCMD(reg) (ADXL34X_CMD_READ | (reg & 0x3F))
+#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \
+ | (reg & 0x3F))
+
+static int adxl34x_spi_read(struct device *dev, unsigned char reg)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char cmd;
+
+ cmd = ADXL34X_READCMD(reg);
+
+ return spi_w8r8(spi, cmd);
+}
+
+static int adxl34x_spi_write(struct device *dev,
+ unsigned char reg, unsigned char val)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ buf[0] = ADXL34X_WRITECMD(reg);
+ buf[1] = val;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int adxl34x_spi_read_block(struct device *dev,
+ unsigned char reg, int count,
+ void *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ ssize_t status;
+
+ reg = ADXL34X_READMB_CMD(reg);
+ status = spi_write_then_read(spi, &reg, 1, buf, count);
+
+ return (status < 0) ? status : 0;
+}
+
+static const struct adxl34x_bus_ops adx134x_spi_bops = {
+ .bustype = BUS_SPI,
+ .write = adxl34x_spi_write,
+ .read = adxl34x_spi_read,
+ .read_block = adxl34x_spi_read_block,
+};
+
+static int __devinit adxl34x_spi_probe(struct spi_device *spi)
+{
+ struct adxl34x *ac;
+
+ /* don't exceed max specified SPI CLK frequency */
+ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+ dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ ac = adxl34x_probe(&spi->dev, spi->irq,
+ spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY,
+ &adx134x_spi_bops);
+
+ if (IS_ERR(ac))
+ return PTR_ERR(ac);
+
+ spi_set_drvdata(spi, ac);
+
+ return 0;
+}
+
+static int __devexit adxl34x_spi_remove(struct spi_device *spi)
+{
+ struct adxl34x *ac = dev_get_drvdata(&spi->dev);
+
+ return adxl34x_remove(ac);
+}
+
+#ifdef CONFIG_PM
+static int adxl34x_spi_suspend(struct spi_device *spi, pm_message_t message)
+{
+ struct adxl34x *ac = dev_get_drvdata(&spi->dev);
+
+ adxl34x_suspend(ac);
+
+ return 0;
+}
+
+static int adxl34x_spi_resume(struct spi_device *spi)
+{
+ struct adxl34x *ac = dev_get_drvdata(&spi->dev);
+
+ adxl34x_resume(ac);
+
+ return 0;
+}
+#else
+# define adxl34x_spi_suspend NULL
+# define adxl34x_spi_resume NULL
+#endif
+
+static struct spi_driver adxl34x_driver = {
+ .driver = {
+ .name = "adxl34x",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = adxl34x_spi_probe,
+ .remove = __devexit_p(adxl34x_spi_remove),
+ .suspend = adxl34x_spi_suspend,
+ .resume = adxl34x_spi_resume,
+};
+
+static int __init adxl34x_spi_init(void)
+{
+ return spi_register_driver(&adxl34x_driver);
+}
+module_init(adxl34x_spi_init);
+
+static void __exit adxl34x_spi_exit(void)
+{
+ spi_unregister_driver(&adxl34x_driver);
+}
+module_exit(adxl34x_spi_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c
new file mode 100644
index 000000000000..e2ca01708080
--- /dev/null
+++ b/drivers/input/misc/adxl34x.c
@@ -0,0 +1,915 @@
+/*
+ * ADXL345/346 Three-Axis Digital Accelerometers
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/input/adxl34x.h>
+
+#include "adxl34x.h"
+
+/* ADXL345/6 Register Map */
+#define DEVID 0x00 /* R Device ID */
+#define THRESH_TAP 0x1D /* R/W Tap threshold */
+#define OFSX 0x1E /* R/W X-axis offset */
+#define OFSY 0x1F /* R/W Y-axis offset */
+#define OFSZ 0x20 /* R/W Z-axis offset */
+#define DUR 0x21 /* R/W Tap duration */
+#define LATENT 0x22 /* R/W Tap latency */
+#define WINDOW 0x23 /* R/W Tap window */
+#define THRESH_ACT 0x24 /* R/W Activity threshold */
+#define THRESH_INACT 0x25 /* R/W Inactivity threshold */
+#define TIME_INACT 0x26 /* R/W Inactivity time */
+#define ACT_INACT_CTL 0x27 /* R/W Axis enable control for activity and */
+ /* inactivity detection */
+#define THRESH_FF 0x28 /* R/W Free-fall threshold */
+#define TIME_FF 0x29 /* R/W Free-fall time */
+#define TAP_AXES 0x2A /* R/W Axis control for tap/double tap */
+#define ACT_TAP_STATUS 0x2B /* R Source of tap/double tap */
+#define BW_RATE 0x2C /* R/W Data rate and power mode control */
+#define POWER_CTL 0x2D /* R/W Power saving features control */
+#define INT_ENABLE 0x2E /* R/W Interrupt enable control */
+#define INT_MAP 0x2F /* R/W Interrupt mapping control */
+#define INT_SOURCE 0x30 /* R Source of interrupts */
+#define DATA_FORMAT 0x31 /* R/W Data format control */
+#define DATAX0 0x32 /* R X-Axis Data 0 */
+#define DATAX1 0x33 /* R X-Axis Data 1 */
+#define DATAY0 0x34 /* R Y-Axis Data 0 */
+#define DATAY1 0x35 /* R Y-Axis Data 1 */
+#define DATAZ0 0x36 /* R Z-Axis Data 0 */
+#define DATAZ1 0x37 /* R Z-Axis Data 1 */
+#define FIFO_CTL 0x38 /* R/W FIFO control */
+#define FIFO_STATUS 0x39 /* R FIFO status */
+#define TAP_SIGN 0x3A /* R Sign and source for tap/double tap */
+/* Orientation ADXL346 only */
+#define ORIENT_CONF 0x3B /* R/W Orientation configuration */
+#define ORIENT 0x3C /* R Orientation status */
+
+/* DEVIDs */
+#define ID_ADXL345 0xE5
+#define ID_ADXL346 0xE6
+
+/* INT_ENABLE/INT_MAP/INT_SOURCE Bits */
+#define DATA_READY (1 << 7)
+#define SINGLE_TAP (1 << 6)
+#define DOUBLE_TAP (1 << 5)
+#define ACTIVITY (1 << 4)
+#define INACTIVITY (1 << 3)
+#define FREE_FALL (1 << 2)
+#define WATERMARK (1 << 1)
+#define OVERRUN (1 << 0)
+
+/* ACT_INACT_CONTROL Bits */
+#define ACT_ACDC (1 << 7)
+#define ACT_X_EN (1 << 6)
+#define ACT_Y_EN (1 << 5)
+#define ACT_Z_EN (1 << 4)
+#define INACT_ACDC (1 << 3)
+#define INACT_X_EN (1 << 2)
+#define INACT_Y_EN (1 << 1)
+#define INACT_Z_EN (1 << 0)
+
+/* TAP_AXES Bits */
+#define SUPPRESS (1 << 3)
+#define TAP_X_EN (1 << 2)
+#define TAP_Y_EN (1 << 1)
+#define TAP_Z_EN (1 << 0)
+
+/* ACT_TAP_STATUS Bits */
+#define ACT_X_SRC (1 << 6)
+#define ACT_Y_SRC (1 << 5)
+#define ACT_Z_SRC (1 << 4)
+#define ASLEEP (1 << 3)
+#define TAP_X_SRC (1 << 2)
+#define TAP_Y_SRC (1 << 1)
+#define TAP_Z_SRC (1 << 0)
+
+/* BW_RATE Bits */
+#define LOW_POWER (1 << 4)
+#define RATE(x) ((x) & 0xF)
+
+/* POWER_CTL Bits */
+#define PCTL_LINK (1 << 5)
+#define PCTL_AUTO_SLEEP (1 << 4)
+#define PCTL_MEASURE (1 << 3)
+#define PCTL_SLEEP (1 << 2)
+#define PCTL_WAKEUP(x) ((x) & 0x3)
+
+/* DATA_FORMAT Bits */
+#define SELF_TEST (1 << 7)
+#define SPI (1 << 6)
+#define INT_INVERT (1 << 5)
+#define FULL_RES (1 << 3)
+#define JUSTIFY (1 << 2)
+#define RANGE(x) ((x) & 0x3)
+#define RANGE_PM_2g 0
+#define RANGE_PM_4g 1
+#define RANGE_PM_8g 2
+#define RANGE_PM_16g 3
+
+/*
+ * Maximum value our axis may get in full res mode for the input device
+ * (signed 13 bits)
+ */
+#define ADXL_FULLRES_MAX_VAL 4096
+
+/*
+ * Maximum value our axis may get in fixed res mode for the input device
+ * (signed 10 bits)
+ */
+#define ADXL_FIXEDRES_MAX_VAL 512
+
+/* FIFO_CTL Bits */
+#define FIFO_MODE(x) (((x) & 0x3) << 6)
+#define FIFO_BYPASS 0
+#define FIFO_FIFO 1
+#define FIFO_STREAM 2
+#define FIFO_TRIGGER 3
+#define TRIGGER (1 << 5)
+#define SAMPLES(x) ((x) & 0x1F)
+
+/* FIFO_STATUS Bits */
+#define FIFO_TRIG (1 << 7)
+#define ENTRIES(x) ((x) & 0x3F)
+
+/* TAP_SIGN Bits ADXL346 only */
+#define XSIGN (1 << 6)
+#define YSIGN (1 << 5)
+#define ZSIGN (1 << 4)
+#define XTAP (1 << 3)
+#define YTAP (1 << 2)
+#define ZTAP (1 << 1)
+
+/* ORIENT_CONF ADXL346 only */
+#define ORIENT_DEADZONE(x) (((x) & 0x7) << 4)
+#define ORIENT_DIVISOR(x) ((x) & 0x7)
+
+/* ORIENT ADXL346 only */
+#define ADXL346_2D_VALID (1 << 6)
+#define ADXL346_2D_ORIENT(x) (((x) & 0x3) >> 4)
+#define ADXL346_3D_VALID (1 << 3)
+#define ADXL346_3D_ORIENT(x) ((x) & 0x7)
+#define ADXL346_2D_PORTRAIT_POS 0 /* +X */
+#define ADXL346_2D_PORTRAIT_NEG 1 /* -X */
+#define ADXL346_2D_LANDSCAPE_POS 2 /* +Y */
+#define ADXL346_2D_LANDSCAPE_NEG 3 /* -Y */
+
+#define ADXL346_3D_FRONT 3 /* +X */
+#define ADXL346_3D_BACK 4 /* -X */
+#define ADXL346_3D_RIGHT 2 /* +Y */
+#define ADXL346_3D_LEFT 5 /* -Y */
+#define ADXL346_3D_TOP 1 /* +Z */
+#define ADXL346_3D_BOTTOM 6 /* -Z */
+
+#undef ADXL_DEBUG
+
+#define ADXL_X_AXIS 0
+#define ADXL_Y_AXIS 1
+#define ADXL_Z_AXIS 2
+
+#define AC_READ(ac, reg) ((ac)->bops->read((ac)->dev, reg))
+#define AC_WRITE(ac, reg, val) ((ac)->bops->write((ac)->dev, reg, val))
+
+struct axis_triple {
+ int x;
+ int y;
+ int z;
+};
+
+struct adxl34x {
+ struct device *dev;
+ struct input_dev *input;
+ struct mutex mutex; /* reentrant protection for struct */
+ struct adxl34x_platform_data pdata;
+ struct axis_triple swcal;
+ struct axis_triple hwcal;
+ struct axis_triple saved;
+ char phys[32];
+ unsigned orient2d_saved;
+ unsigned orient3d_saved;
+ bool disabled; /* P: mutex */
+ bool opened; /* P: mutex */
+ bool suspended; /* P: mutex */
+ bool fifo_delay;
+ int irq;
+ unsigned model;
+ unsigned int_mask;
+
+ const struct adxl34x_bus_ops *bops;
+};
+
+static const struct adxl34x_platform_data adxl34x_default_init = {
+ .tap_threshold = 35,
+ .tap_duration = 3,
+ .tap_latency = 20,
+ .tap_window = 20,
+ .tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN,
+ .act_axis_control = 0xFF,
+ .activity_threshold = 6,
+ .inactivity_threshold = 4,
+ .inactivity_time = 3,
+ .free_fall_threshold = 8,
+ .free_fall_time = 0x20,
+ .data_rate = 8,
+ .data_range = ADXL_FULL_RES,
+
+ .ev_type = EV_ABS,
+ .ev_code_x = ABS_X, /* EV_REL */
+ .ev_code_y = ABS_Y, /* EV_REL */
+ .ev_code_z = ABS_Z, /* EV_REL */
+
+ .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
+ .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
+ .fifo_mode = FIFO_STREAM,
+ .watermark = 0,
+};
+
+static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis)
+{
+ short buf[3];
+
+ ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf);
+
+ mutex_lock(&ac->mutex);
+ ac->saved.x = (s16) le16_to_cpu(buf[0]);
+ axis->x = ac->saved.x;
+
+ ac->saved.y = (s16) le16_to_cpu(buf[1]);
+ axis->y = ac->saved.y;
+
+ ac->saved.z = (s16) le16_to_cpu(buf[2]);
+ axis->z = ac->saved.z;
+ mutex_unlock(&ac->mutex);
+}
+
+static void adxl34x_service_ev_fifo(struct adxl34x *ac)
+{
+ struct adxl34x_platform_data *pdata = &ac->pdata;
+ struct axis_triple axis;
+
+ adxl34x_get_triple(ac, &axis);
+
+ input_event(ac->input, pdata->ev_type, pdata->ev_code_x,
+ axis.x - ac->swcal.x);
+ input_event(ac->input, pdata->ev_type, pdata->ev_code_y,
+ axis.y - ac->swcal.y);
+ input_event(ac->input, pdata->ev_type, pdata->ev_code_z,
+ axis.z - ac->swcal.z);
+}
+
+static void adxl34x_report_key_single(struct input_dev *input, int key)
+{
+ input_report_key(input, key, true);
+ input_sync(input);
+ input_report_key(input, key, false);
+}
+
+static void adxl34x_send_key_events(struct adxl34x *ac,
+ struct adxl34x_platform_data *pdata, int status, int press)
+{
+ int i;
+
+ for (i = ADXL_X_AXIS; i <= ADXL_Z_AXIS; i++) {
+ if (status & (1 << (ADXL_Z_AXIS - i)))
+ input_report_key(ac->input,
+ pdata->ev_code_tap[i], press);
+ }
+}
+
+static void adxl34x_do_tap(struct adxl34x *ac,
+ struct adxl34x_platform_data *pdata, int status)
+{
+ adxl34x_send_key_events(ac, pdata, status, true);
+ input_sync(ac->input);
+ adxl34x_send_key_events(ac, pdata, status, false);
+}
+
+static irqreturn_t adxl34x_irq(int irq, void *handle)
+{
+ struct adxl34x *ac = handle;
+ struct adxl34x_platform_data *pdata = &ac->pdata;
+ int int_stat, tap_stat, samples, orient, orient_code;
+
+ /*
+ * ACT_TAP_STATUS should be read before clearing the interrupt
+ * Avoid reading ACT_TAP_STATUS in case TAP detection is disabled
+ */
+
+ if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN))
+ tap_stat = AC_READ(ac, ACT_TAP_STATUS);
+ else
+ tap_stat = 0;
+
+ int_stat = AC_READ(ac, INT_SOURCE);
+
+ if (int_stat & FREE_FALL)
+ adxl34x_report_key_single(ac->input, pdata->ev_code_ff);
+
+ if (int_stat & OVERRUN)
+ dev_dbg(ac->dev, "OVERRUN\n");
+
+ if (int_stat & (SINGLE_TAP | DOUBLE_TAP)) {
+ adxl34x_do_tap(ac, pdata, tap_stat);
+
+ if (int_stat & DOUBLE_TAP)
+ adxl34x_do_tap(ac, pdata, tap_stat);
+ }
+
+ if (pdata->ev_code_act_inactivity) {
+ if (int_stat & ACTIVITY)
+ input_report_key(ac->input,
+ pdata->ev_code_act_inactivity, 1);
+ if (int_stat & INACTIVITY)
+ input_report_key(ac->input,
+ pdata->ev_code_act_inactivity, 0);
+ }
+
+ /*
+ * ORIENTATION SENSING ADXL346 only
+ */
+ if (pdata->orientation_enable) {
+ orient = AC_READ(ac, ORIENT);
+ if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) &&
+ (orient & ADXL346_2D_VALID)) {
+
+ orient_code = ADXL346_2D_ORIENT(orient);
+ /* Report orientation only when it changes */
+ if (ac->orient2d_saved != orient_code) {
+ ac->orient2d_saved = orient_code;
+ adxl34x_report_key_single(ac->input,
+ pdata->ev_codes_orient_2d[orient_code]);
+ }
+ }
+
+ if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) &&
+ (orient & ADXL346_3D_VALID)) {
+
+ orient_code = ADXL346_3D_ORIENT(orient) - 1;
+ /* Report orientation only when it changes */
+ if (ac->orient3d_saved != orient_code) {
+ ac->orient3d_saved = orient_code;
+ adxl34x_report_key_single(ac->input,
+ pdata->ev_codes_orient_3d[orient_code]);
+ }
+ }
+ }
+
+ if (int_stat & (DATA_READY | WATERMARK)) {
+
+ if (pdata->fifo_mode)
+ samples = ENTRIES(AC_READ(ac, FIFO_STATUS)) + 1;
+ else
+ samples = 1;
+
+ for (; samples > 0; samples--) {
+ adxl34x_service_ev_fifo(ac);
+ /*
+ * To ensure that the FIFO has
+ * completely popped, there must be at least 5 us between
+ * the end of reading the data registers, signified by the
+ * transition to register 0x38 from 0x37 or the CS pin
+ * going high, and the start of new reads of the FIFO or
+ * reading the FIFO_STATUS register. For SPI operation at
+ * 1.5 MHz or lower, the register addressing portion of the
+ * transmission is sufficient delay to ensure the FIFO has
+ * completely popped. It is necessary for SPI operation
+ * greater than 1.5 MHz to de-assert the CS pin to ensure a
+ * total of 5 us, which is at most 3.4 us at 5 MHz
+ * operation.
+ */
+ if (ac->fifo_delay && (samples > 1))
+ udelay(3);
+ }
+ }
+
+ input_sync(ac->input);
+
+ return IRQ_HANDLED;
+}
+
+static void __adxl34x_disable(struct adxl34x *ac)
+{
+ /*
+ * A '0' places the ADXL34x into standby mode
+ * with minimum power consumption.
+ */
+ AC_WRITE(ac, POWER_CTL, 0);
+}
+
+static void __adxl34x_enable(struct adxl34x *ac)
+{
+ AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
+}
+
+void adxl34x_suspend(struct adxl34x *ac)
+{
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && !ac->disabled && ac->opened)
+ __adxl34x_disable(ac);
+
+ ac->suspended = true;
+
+ mutex_unlock(&ac->mutex);
+}
+EXPORT_SYMBOL_GPL(adxl34x_suspend);
+
+void adxl34x_resume(struct adxl34x *ac)
+{
+ mutex_lock(&ac->mutex);
+
+ if (ac->suspended && !ac->disabled && ac->opened)
+ __adxl34x_enable(ac);
+
+ ac->suspended = false;
+
+ mutex_unlock(&ac->mutex);
+}
+EXPORT_SYMBOL_GPL(adxl34x_resume);
+
+static ssize_t adxl34x_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ac->disabled);
+}
+
+static ssize_t adxl34x_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && ac->opened) {
+ if (val) {
+ if (!ac->disabled)
+ __adxl34x_disable(ac);
+ } else {
+ if (ac->disabled)
+ __adxl34x_enable(ac);
+ }
+ }
+
+ ac->disabled = !!val;
+
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, adxl34x_disable_show, adxl34x_disable_store);
+
+static ssize_t adxl34x_calibrate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ ssize_t count;
+
+ mutex_lock(&ac->mutex);
+ count = sprintf(buf, "%d,%d,%d\n",
+ ac->hwcal.x * 4 + ac->swcal.x,
+ ac->hwcal.y * 4 + ac->swcal.y,
+ ac->hwcal.z * 4 + ac->swcal.z);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static ssize_t adxl34x_calibrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ /*
+ * Hardware offset calibration has a resolution of 15.6 mg/LSB.
+ * We use HW calibration and handle the remaining bits in SW. (4mg/LSB)
+ */
+
+ mutex_lock(&ac->mutex);
+ ac->hwcal.x -= (ac->saved.x / 4);
+ ac->swcal.x = ac->saved.x % 4;
+
+ ac->hwcal.y -= (ac->saved.y / 4);
+ ac->swcal.y = ac->saved.y % 4;
+
+ ac->hwcal.z -= (ac->saved.z / 4);
+ ac->swcal.z = ac->saved.z % 4;
+
+ AC_WRITE(ac, OFSX, (s8) ac->hwcal.x);
+ AC_WRITE(ac, OFSY, (s8) ac->hwcal.y);
+ AC_WRITE(ac, OFSZ, (s8) ac->hwcal.z);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(calibrate, 0664,
+ adxl34x_calibrate_show, adxl34x_calibrate_store);
+
+static ssize_t adxl34x_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", RATE(ac->pdata.data_rate));
+}
+
+static ssize_t adxl34x_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+
+ ac->pdata.data_rate = RATE(val);
+ AC_WRITE(ac, BW_RATE,
+ ac->pdata.data_rate |
+ (ac->pdata.low_power_mode ? LOW_POWER : 0));
+
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(rate, 0664, adxl34x_rate_show, adxl34x_rate_store);
+
+static ssize_t adxl34x_autosleep_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n",
+ ac->pdata.power_mode & (PCTL_AUTO_SLEEP | PCTL_LINK) ? 1 : 0);
+}
+
+static ssize_t adxl34x_autosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+
+ if (val)
+ ac->pdata.power_mode |= (PCTL_AUTO_SLEEP | PCTL_LINK);
+ else
+ ac->pdata.power_mode &= ~(PCTL_AUTO_SLEEP | PCTL_LINK);
+
+ if (!ac->disabled && !ac->suspended && ac->opened)
+ AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
+
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(autosleep, 0664,
+ adxl34x_autosleep_show, adxl34x_autosleep_store);
+
+static ssize_t adxl34x_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ ssize_t count;
+
+ mutex_lock(&ac->mutex);
+ count = sprintf(buf, "(%d, %d, %d)\n",
+ ac->saved.x, ac->saved.y, ac->saved.z);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(position, S_IRUGO, adxl34x_position_show, NULL);
+
+#ifdef ADXL_DEBUG
+static ssize_t adxl34x_write_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned long val;
+ int error;
+
+ /*
+ * This allows basic ADXL register write access for debug purposes.
+ */
+ error = strict_strtoul(buf, 16, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+ AC_WRITE(ac, val >> 8, val & 0xFF);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(write, 0664, NULL, adxl34x_write_store);
+#endif
+
+static struct attribute *adxl34x_attributes[] = {
+ &dev_attr_disable.attr,
+ &dev_attr_calibrate.attr,
+ &dev_attr_rate.attr,
+ &dev_attr_autosleep.attr,
+ &dev_attr_position.attr,
+#ifdef ADXL_DEBUG
+ &dev_attr_write.attr,
+#endif
+ NULL
+};
+
+static const struct attribute_group adxl34x_attr_group = {
+ .attrs = adxl34x_attributes,
+};
+
+static int adxl34x_input_open(struct input_dev *input)
+{
+ struct adxl34x *ac = input_get_drvdata(input);
+
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && !ac->disabled)
+ __adxl34x_enable(ac);
+
+ ac->opened = true;
+
+ mutex_unlock(&ac->mutex);
+
+ return 0;
+}
+
+static void adxl34x_input_close(struct input_dev *input)
+{
+ struct adxl34x *ac = input_get_drvdata(input);
+
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && !ac->disabled)
+ __adxl34x_disable(ac);
+
+ ac->opened = false;
+
+ mutex_unlock(&ac->mutex);
+}
+
+struct adxl34x *adxl34x_probe(struct device *dev, int irq,
+ bool fifo_delay_default,
+ const struct adxl34x_bus_ops *bops)
+{
+ struct adxl34x *ac;
+ struct input_dev *input_dev;
+ const struct adxl34x_platform_data *pdata;
+ int err, range, i;
+ unsigned char revid;
+
+ if (!irq) {
+ dev_err(dev, "no IRQ?\n");
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ac || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ac->fifo_delay = fifo_delay_default;
+
+ pdata = dev->platform_data;
+ if (!pdata) {
+ dev_dbg(dev,
+ "No platfrom data: Using default initialization\n");
+ pdata = &adxl34x_default_init;
+ }
+
+ ac->pdata = *pdata;
+ pdata = &ac->pdata;
+
+ ac->input = input_dev;
+ ac->disabled = true;
+ ac->dev = dev;
+ ac->irq = irq;
+ ac->bops = bops;
+
+ mutex_init(&ac->mutex);
+
+ input_dev->name = "ADXL34x accelerometer";
+ revid = ac->bops->read(dev, DEVID);
+
+ switch (revid) {
+ case ID_ADXL345:
+ ac->model = 345;
+ break;
+ case ID_ADXL346:
+ ac->model = 346;
+ break;
+ default:
+ dev_err(dev, "Failed to probe %s\n", input_dev->name);
+ err = -ENODEV;
+ goto err_free_mem;
+ }
+
+ snprintf(ac->phys, sizeof(ac->phys), "%s/input0", dev_name(dev));
+
+ input_dev->phys = ac->phys;
+ input_dev->dev.parent = dev;
+ input_dev->id.product = ac->model;
+ input_dev->id.bustype = bops->bustype;
+ input_dev->open = adxl34x_input_open;
+ input_dev->close = adxl34x_input_close;
+
+ input_set_drvdata(input_dev, ac);
+
+ __set_bit(ac->pdata.ev_type, input_dev->evbit);
+
+ if (ac->pdata.ev_type == EV_REL) {
+ __set_bit(REL_X, input_dev->relbit);
+ __set_bit(REL_Y, input_dev->relbit);
+ __set_bit(REL_Z, input_dev->relbit);
+ } else {
+ /* EV_ABS */
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_Z, input_dev->absbit);
+
+ if (pdata->data_range & FULL_RES)
+ range = ADXL_FULLRES_MAX_VAL; /* Signed 13-bit */
+ else
+ range = ADXL_FIXEDRES_MAX_VAL; /* Signed 10-bit */
+
+ input_set_abs_params(input_dev, ABS_X, -range, range, 3, 3);
+ input_set_abs_params(input_dev, ABS_Y, -range, range, 3, 3);
+ input_set_abs_params(input_dev, ABS_Z, -range, range, 3, 3);
+ }
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(pdata->ev_code_tap[ADXL_X_AXIS], input_dev->keybit);
+ __set_bit(pdata->ev_code_tap[ADXL_Y_AXIS], input_dev->keybit);
+ __set_bit(pdata->ev_code_tap[ADXL_Z_AXIS], input_dev->keybit);
+
+ if (pdata->ev_code_ff) {
+ ac->int_mask = FREE_FALL;
+ __set_bit(pdata->ev_code_ff, input_dev->keybit);
+ }
+
+ if (pdata->ev_code_act_inactivity)
+ __set_bit(pdata->ev_code_act_inactivity, input_dev->keybit);
+
+ ac->int_mask |= ACTIVITY | INACTIVITY;
+
+ if (pdata->watermark) {
+ ac->int_mask |= WATERMARK;
+ if (!FIFO_MODE(pdata->fifo_mode))
+ ac->pdata.fifo_mode |= FIFO_STREAM;
+ } else {
+ ac->int_mask |= DATA_READY;
+ }
+
+ if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN))
+ ac->int_mask |= SINGLE_TAP | DOUBLE_TAP;
+
+ if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
+ ac->fifo_delay = false;
+
+ ac->bops->write(dev, POWER_CTL, 0);
+
+ err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ dev_name(dev), ac);
+ if (err) {
+ dev_err(dev, "irq %d busy?\n", ac->irq);
+ goto err_free_mem;
+ }
+
+ err = sysfs_create_group(&dev->kobj, &adxl34x_attr_group);
+ if (err)
+ goto err_free_irq;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_attr;
+
+ AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
+ AC_WRITE(ac, OFSX, pdata->x_axis_offset);
+ ac->hwcal.x = pdata->x_axis_offset;
+ AC_WRITE(ac, OFSY, pdata->y_axis_offset);
+ ac->hwcal.y = pdata->y_axis_offset;
+ AC_WRITE(ac, OFSZ, pdata->z_axis_offset);
+ ac->hwcal.z = pdata->z_axis_offset;
+ AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
+ AC_WRITE(ac, DUR, pdata->tap_duration);
+ AC_WRITE(ac, LATENT, pdata->tap_latency);
+ AC_WRITE(ac, WINDOW, pdata->tap_window);
+ AC_WRITE(ac, THRESH_ACT, pdata->activity_threshold);
+ AC_WRITE(ac, THRESH_INACT, pdata->inactivity_threshold);
+ AC_WRITE(ac, TIME_INACT, pdata->inactivity_time);
+ AC_WRITE(ac, THRESH_FF, pdata->free_fall_threshold);
+ AC_WRITE(ac, TIME_FF, pdata->free_fall_time);
+ AC_WRITE(ac, TAP_AXES, pdata->tap_axis_control);
+ AC_WRITE(ac, ACT_INACT_CTL, pdata->act_axis_control);
+ AC_WRITE(ac, BW_RATE, RATE(ac->pdata.data_rate) |
+ (pdata->low_power_mode ? LOW_POWER : 0));
+ AC_WRITE(ac, DATA_FORMAT, pdata->data_range);
+ AC_WRITE(ac, FIFO_CTL, FIFO_MODE(pdata->fifo_mode) |
+ SAMPLES(pdata->watermark));
+
+ if (pdata->use_int2) {
+ /* Map all INTs to INT2 */
+ AC_WRITE(ac, INT_MAP, ac->int_mask | OVERRUN);
+ } else {
+ /* Map all INTs to INT1 */
+ AC_WRITE(ac, INT_MAP, 0);
+ }
+
+ if (ac->model == 346 && ac->pdata.orientation_enable) {
+ AC_WRITE(ac, ORIENT_CONF,
+ ORIENT_DEADZONE(ac->pdata.deadzone_angle) |
+ ORIENT_DIVISOR(ac->pdata.divisor_length));
+
+ ac->orient2d_saved = 1234;
+ ac->orient3d_saved = 1234;
+
+ if (pdata->orientation_enable & ADXL_EN_ORIENTATION_3D)
+ for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_3d); i++)
+ __set_bit(pdata->ev_codes_orient_3d[i],
+ input_dev->keybit);
+
+ if (pdata->orientation_enable & ADXL_EN_ORIENTATION_2D)
+ for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_2d); i++)
+ __set_bit(pdata->ev_codes_orient_2d[i],
+ input_dev->keybit);
+ } else {
+ ac->pdata.orientation_enable = 0;
+ }
+
+ AC_WRITE(ac, INT_ENABLE, ac->int_mask | OVERRUN);
+
+ ac->pdata.power_mode &= (PCTL_AUTO_SLEEP | PCTL_LINK);
+
+ return ac;
+
+ err_remove_attr:
+ sysfs_remove_group(&dev->kobj, &adxl34x_attr_group);
+ err_free_irq:
+ free_irq(ac->irq, ac);
+ err_free_mem:
+ input_free_device(input_dev);
+ kfree(ac);
+ err_out:
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(adxl34x_probe);
+
+int adxl34x_remove(struct adxl34x *ac)
+{
+ sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group);
+ free_irq(ac->irq, ac);
+ input_unregister_device(ac->input);
+ dev_dbg(ac->dev, "unregistered accelerometer\n");
+ kfree(ac);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adxl34x_remove);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x.h b/drivers/input/misc/adxl34x.h
new file mode 100644
index 000000000000..bbbc80fda164
--- /dev/null
+++ b/drivers/input/misc/adxl34x.h
@@ -0,0 +1,30 @@
+/*
+ * ADXL345/346 Three-Axis Digital Accelerometers (I2C/SPI Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADXL34X_H_
+#define _ADXL34X_H_
+
+struct device;
+struct adxl34x;
+
+struct adxl34x_bus_ops {
+ u16 bustype;
+ int (*read)(struct device *, unsigned char);
+ int (*read_block)(struct device *, unsigned char, int, void *);
+ int (*write)(struct device *, unsigned char, unsigned char);
+};
+
+void adxl34x_suspend(struct adxl34x *ac);
+void adxl34x_resume(struct adxl34x *ac);
+struct adxl34x *adxl34x_probe(struct device *dev, int irq,
+ bool fifo_delay_default,
+ const struct adxl34x_bus_ops *bops);
+int adxl34x_remove(struct adxl34x *ac);
+
+#endif
diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c
index dfaa9a045ed8..601f7372f9c4 100644
--- a/drivers/input/misc/atlas_btns.c
+++ b/drivers/input/misc/atlas_btns.c
@@ -21,6 +21,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
@@ -60,12 +62,11 @@ static acpi_status acpi_atlas_button_handler(u32 function,
input_report_key(input_dev, atlas_keymap[code], key_down);
input_sync(input_dev);
- status = 0;
+ status = AE_OK;
} else {
- printk(KERN_WARNING "atlas: shrugged on unexpected function"
- ":function=%x,address=%lx,value=%x\n",
+ pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n",
function, (unsigned long)address, (u32)*value);
- status = -EINVAL;
+ status = AE_BAD_PARAMETER;
}
return status;
@@ -79,7 +80,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)
input_dev = input_allocate_device();
if (!input_dev) {
- printk(KERN_ERR "atlas: unable to allocate input device\n");
+ pr_err("unable to allocate input device\n");
return -ENOMEM;
}
@@ -102,7 +103,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)
err = input_register_device(input_dev);
if (err) {
- printk(KERN_ERR "atlas: couldn't register input device\n");
+ pr_err("couldn't register input device\n");
input_free_device(input_dev);
return err;
}
@@ -112,12 +113,12 @@ static int atlas_acpi_button_add(struct acpi_device *device)
0x81, &acpi_atlas_button_handler,
&acpi_atlas_button_setup, device);
if (ACPI_FAILURE(status)) {
- printk(KERN_ERR "Atlas: Error installing addr spc handler\n");
+ pr_err("error installing addr spc handler\n");
input_unregister_device(input_dev);
- status = -EINVAL;
+ err = -EINVAL;
}
- return status;
+ return err;
}
static int atlas_acpi_button_remove(struct acpi_device *device, int type)
@@ -126,14 +127,12 @@ static int atlas_acpi_button_remove(struct acpi_device *device, int type)
status = acpi_remove_address_space_handler(device->handle,
0x81, &acpi_atlas_button_handler);
- if (ACPI_FAILURE(status)) {
- printk(KERN_ERR "Atlas: Error removing addr spc handler\n");
- status = -EINVAL;
- }
+ if (ACPI_FAILURE(status))
+ pr_err("error removing addr spc handler\n");
input_unregister_device(input_dev);
- return status;
+ return 0;
}
static const struct acpi_device_id atlas_device_ids[] = {
@@ -145,6 +144,7 @@ MODULE_DEVICE_TABLE(acpi, atlas_device_ids);
static struct acpi_driver atlas_acpi_driver = {
.name = ACPI_ATLAS_NAME,
.class = ACPI_ATLAS_CLASS,
+ .owner = THIS_MODULE,
.ids = atlas_device_ids,
.ops = {
.add = atlas_acpi_button_add,
@@ -154,18 +154,10 @@ static struct acpi_driver atlas_acpi_driver = {
static int __init atlas_acpi_init(void)
{
- int result;
-
if (acpi_disabled)
return -ENODEV;
- result = acpi_bus_register_driver(&atlas_acpi_driver);
- if (result < 0) {
- printk(KERN_ERR "Atlas ACPI: Unable to register driver\n");
- return -ENODEV;
- }
-
- return 0;
+ return acpi_bus_register_driver(&atlas_acpi_driver);
}
static void __exit atlas_acpi_exit(void)
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
new file mode 100644
index 000000000000..57c294f07198
--- /dev/null
+++ b/drivers/input/misc/pwm-beeper.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ * PWM beeper driver
+ *
+ * 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.
+ *
+ * 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.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+struct pwm_beeper {
+ struct input_dev *input;
+ struct pwm_device *pwm;
+ unsigned long period;
+};
+
+#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
+
+static int pwm_beeper_event(struct input_dev *input,
+ unsigned int type, unsigned int code, int value)
+{
+ int ret = 0;
+ struct pwm_beeper *beeper = input_get_drvdata(input);
+ unsigned long period;
+
+ if (type != EV_SND || value < 0)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_BELL:
+ value = value ? 1000 : 0;
+ break;
+ case SND_TONE:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (value == 0) {
+ pwm_config(beeper->pwm, 0, 0);
+ pwm_disable(beeper->pwm);
+ } else {
+ period = HZ_TO_NANOSECONDS(value);
+ ret = pwm_config(beeper->pwm, period / 2, period);
+ if (ret)
+ return ret;
+ ret = pwm_enable(beeper->pwm);
+ if (ret)
+ return ret;
+ beeper->period = period;
+ }
+
+ return 0;
+}
+
+static int __devinit pwm_beeper_probe(struct platform_device *pdev)
+{
+ unsigned long pwm_id = (unsigned long)pdev->dev.platform_data;
+ struct pwm_beeper *beeper;
+ int error;
+
+ beeper = kzalloc(sizeof(*beeper), GFP_KERNEL);
+ if (!beeper)
+ return -ENOMEM;
+
+ beeper->pwm = pwm_request(pwm_id, "pwm beeper");
+
+ if (IS_ERR(beeper->pwm)) {
+ error = PTR_ERR(beeper->pwm);
+ dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error);
+ goto err_free;
+ }
+
+ beeper->input = input_allocate_device();
+ if (!beeper->input) {
+ dev_err(&pdev->dev, "Failed to allocate input device\n");
+ error = -ENOMEM;
+ goto err_pwm_free;
+ }
+ beeper->input->dev.parent = &pdev->dev;
+
+ beeper->input->name = "pwm-beeper";
+ beeper->input->phys = "pwm/input0";
+ beeper->input->id.bustype = BUS_HOST;
+ beeper->input->id.vendor = 0x001f;
+ beeper->input->id.product = 0x0001;
+ beeper->input->id.version = 0x0100;
+
+ beeper->input->evbit[0] = BIT(EV_SND);
+ beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL);
+
+ beeper->input->event = pwm_beeper_event;
+
+ input_set_drvdata(beeper->input, beeper);
+
+ error = input_register_device(beeper->input);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
+ goto err_input_free;
+ }
+
+ platform_set_drvdata(pdev, beeper);
+
+ return 0;
+
+err_input_free:
+ input_free_device(beeper->input);
+err_pwm_free:
+ pwm_free(beeper->pwm);
+err_free:
+ kfree(beeper);
+
+ return error;
+}
+
+static int __devexit pwm_beeper_remove(struct platform_device *pdev)
+{
+ struct pwm_beeper *beeper = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ input_unregister_device(beeper->input);
+
+ pwm_disable(beeper->pwm);
+ pwm_free(beeper->pwm);
+
+ kfree(beeper);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pwm_beeper_suspend(struct device *dev)
+{
+ struct pwm_beeper *beeper = dev_get_drvdata(dev);
+
+ if (beeper->period)
+ pwm_disable(beeper->pwm);
+
+ return 0;
+}
+
+static int pwm_beeper_resume(struct device *dev)
+{
+ struct pwm_beeper *beeper = dev_get_drvdata(dev);
+
+ if (beeper->period) {
+ pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
+ pwm_enable(beeper->pwm);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
+ pwm_beeper_suspend, pwm_beeper_resume);
+
+#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
+#else
+#define PWM_BEEPER_PM_OPS NULL
+#endif
+
+static struct platform_driver pwm_beeper_driver = {
+ .probe = pwm_beeper_probe,
+ .remove = __devexit_p(pwm_beeper_remove),
+ .driver = {
+ .name = "pwm-beeper",
+ .owner = THIS_MODULE,
+ .pm = PWM_BEEPER_PM_OPS,
+ },
+};
+
+static int __init pwm_beeper_init(void)
+{
+ return platform_driver_register(&pwm_beeper_driver);
+}
+module_init(pwm_beeper_init);
+
+static void __exit pwm_beeper_exit(void)
+{
+ platform_driver_unregister(&pwm_beeper_driver);
+}
+module_exit(pwm_beeper_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("PWM beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pwm-beeper");
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index e9069b87fde2..f16972bddca4 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -52,7 +52,7 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr)
return IRQ_HANDLED;
}
-static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev)
+static int __init twl4030_pwrbutton_probe(struct platform_device *pdev)
{
struct input_dev *pwr;
int irq = platform_get_irq(pdev, 0);
@@ -95,7 +95,7 @@ free_input_dev:
return err;
}
-static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev)
+static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev)
{
struct input_dev *pwr = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
@@ -106,9 +106,8 @@ static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev)
return 0;
}
-struct platform_driver twl4030_pwrbutton_driver = {
- .probe = twl4030_pwrbutton_probe,
- .remove = __devexit_p(twl4030_pwrbutton_remove),
+static struct platform_driver twl4030_pwrbutton_driver = {
+ .remove = __exit_p(twl4030_pwrbutton_remove),
.driver = {
.name = "twl4030_pwrbutton",
.owner = THIS_MODULE,
@@ -117,7 +116,8 @@ struct platform_driver twl4030_pwrbutton_driver = {
static int __init twl4030_pwrbutton_init(void)
{
- return platform_driver_register(&twl4030_pwrbutton_driver);
+ return platform_driver_probe(&twl4030_pwrbutton_driver,
+ twl4030_pwrbutton_probe);
}
module_init(twl4030_pwrbutton_init);
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index 4dac8b79fcd4..12501de0c5cd 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -1347,7 +1347,7 @@ static int __init wb_module_init(void)
err = map_bios();
if (err)
- return err;
+ goto err_free_keymap;
err = platform_driver_register(&wistron_driver);
if (err)
@@ -1371,6 +1371,8 @@ static int __init wb_module_init(void)
platform_driver_unregister(&wistron_driver);
err_unmap_bios:
unmap_bios();
+ err_free_keymap:
+ kfree(keymap);
return err;
}