Input: add support for mma8450 accelerometer
authorEric Miao <eric.miao@linaro.org>
Wed, 22 Jun 2011 08:02:50 +0000 (01:02 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 22 Jun 2011 08:27:25 +0000 (01:27 -0700)
Signed-off-by: Sammy He <r62914@freescale.com>
Signed-off-by: Eric Miao <eric.miao@linaro.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/mma8450.c [new file with mode: 0644]

index 45dc6aa62ba4fce3715c20b504da7f02745997e5..0f22918ad9ce06c4aade9a3f4ce0b0dc28101ff6 100644 (file)
@@ -100,6 +100,17 @@ config INPUT_MAX8925_ONKEY
          To compile this driver as a module, choose M here: the module
          will be called max8925_onkey.
 
+config INPUT_MMA8450
+       tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
+       depends on I2C
+       select INPUT_POLLDEV
+       help
+         Say Y here if you want to support Freescale's MMA8450 Accelerometer
+         through I2C interface.
+
+         To compile this driver as a module, choose M here: the
+         module will be called mma8450.
+
 config INPUT_APANEL
        tristate "Fujitsu Lifebook Application Panel buttons"
        depends on X86 && I2C && LEDS_CLASS
index 38efb2cb182b90646e7e4e7e41288e0222fca641..99953c3c442ce71b4fbf90489f30396b92a120c2 100644 (file)
@@ -27,6 +27,7 @@ obj-$(CONFIG_INPUT_IXP4XX_BEEPER)     += ixp4xx-beeper.o
 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)     += keyspan_remote.o
 obj-$(CONFIG_INPUT_M68K_BEEP)          += m68kspkr.o
 obj-$(CONFIG_INPUT_MAX8925_ONKEY)      += max8925_onkey.o
+obj-$(CONFIG_INPUT_MMA8450)            += mma8450.o
 obj-$(CONFIG_INPUT_PCAP)               += pcap_keys.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
 obj-$(CONFIG_INPUT_PCF8574)            += pcf8574_keypad.o
@@ -45,4 +46,3 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS)      += wistron_btns.o
 obj-$(CONFIG_INPUT_WM831X_ON)          += wm831x-on.o
 obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND)        += xen-kbdfront.o
 obj-$(CONFIG_INPUT_YEALINK)            += yealink.o
-
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
new file mode 100644 (file)
index 0000000..20f8f92
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ *  Driver for Freescale's 3-Axis Accelerometer MMA8450
+ *
+ *  Copyright (C) 2011 Freescale Semiconductor, Inc. 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+
+#define MMA8450_DRV_NAME       "mma8450"
+
+#define MODE_CHANGE_DELAY_MS   100
+#define POLL_INTERVAL          100
+#define POLL_INTERVAL_MAX      500
+
+/* register definitions */
+#define MMA8450_STATUS         0x00
+#define MMA8450_STATUS_ZXYDR   0x08
+
+#define MMA8450_OUT_X8         0x01
+#define MMA8450_OUT_Y8         0x02
+#define MMA8450_OUT_Z8         0x03
+
+#define MMA8450_OUT_X_LSB      0x05
+#define MMA8450_OUT_X_MSB      0x06
+#define MMA8450_OUT_Y_LSB      0x07
+#define MMA8450_OUT_Y_MSB      0x08
+#define MMA8450_OUT_Z_LSB      0x09
+#define MMA8450_OUT_Z_MSB      0x0a
+
+#define MMA8450_XYZ_DATA_CFG   0x16
+
+#define MMA8450_CTRL_REG1      0x38
+#define MMA8450_CTRL_REG2      0x39
+
+/* mma8450 status */
+struct mma8450 {
+       struct i2c_client       *client;
+       struct input_polled_dev *idev;
+};
+
+static int mma8450_read(struct mma8450 *m, unsigned off)
+{
+       struct i2c_client *c = m->client;
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(c, off);
+       if (ret < 0)
+               dev_err(&c->dev,
+                       "failed to read register 0x%02x, error %d\n",
+                       off, ret);
+
+       return ret;
+}
+
+static int mma8450_write(struct mma8450 *m, unsigned off, u8 v)
+{
+       struct i2c_client *c = m->client;
+       int error;
+
+       error = i2c_smbus_write_byte_data(c, off, v);
+       if (error < 0) {
+               dev_err(&c->dev,
+                       "failed to write to register 0x%02x, error %d\n",
+                       off, error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int mma8450_read_xyz(struct mma8450 *m, int *x, int *y, int *z)
+{
+       struct i2c_client *c = m->client;
+       u8 buff[6];
+       int err;
+
+       err = i2c_smbus_read_i2c_block_data(c, MMA8450_OUT_X_LSB, 6, buff);
+       if (err < 0) {
+               dev_err(&c->dev,
+                       "failed to read block data at 0x%02x, error %d\n",
+                       MMA8450_OUT_X_LSB, err);
+               return err;
+       }
+
+       *x = ((buff[1] << 4) & 0xff0) | (buff[0] & 0xf);
+       *y = ((buff[3] << 4) & 0xff0) | (buff[2] & 0xf);
+       *z = ((buff[5] << 4) & 0xff0) | (buff[4] & 0xf);
+
+       return 0;
+}
+
+static void mma8450_poll(struct input_polled_dev *dev)
+{
+       struct mma8450 *m = dev->private;
+       int x, y, z;
+       int ret;
+       int err;
+
+       ret = mma8450_read(m, MMA8450_STATUS);
+       if (ret < 0)
+               return;
+
+       if (!(ret & MMA8450_STATUS_ZXYDR))
+               return;
+
+       err = mma8450_read_xyz(m, &x, &y, &z);
+       if (err)
+               return;
+
+       input_report_abs(dev->input, ABS_X, x);
+       input_report_abs(dev->input, ABS_Y, y);
+       input_report_abs(dev->input, ABS_Z, z);
+       input_sync(dev->input);
+}
+
+/* Initialize the MMA8450 chip */
+static void mma8450_open(struct input_polled_dev *dev)
+{
+       struct mma8450 *m = dev->private;
+       int err;
+
+       /* enable all events from X/Y/Z, no FIFO */
+       err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07);
+       if (err)
+               return;
+
+       /*
+        * Sleep mode poll rate - 50Hz
+        * System output data rate - 400Hz
+        * Full scale selection - Active, +/- 2G
+        */
+       err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01);
+       if (err < 0)
+               return;
+
+       msleep(MODE_CHANGE_DELAY_MS);
+}
+
+static void mma8450_close(struct input_polled_dev *dev)
+{
+       struct mma8450 *m = dev->private;
+
+       mma8450_write(m, MMA8450_CTRL_REG1, 0x00);
+       mma8450_write(m, MMA8450_CTRL_REG2, 0x01);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int __devinit mma8450_probe(struct i2c_client *c,
+                                  const struct i2c_device_id *id)
+{
+       struct input_polled_dev *idev;
+       struct mma8450 *m;
+       int err;
+
+       m = kzalloc(sizeof(struct mma8450), GFP_KERNEL);
+       idev = input_allocate_polled_device();
+       if (!m || !idev) {
+               err = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       m->client = c;
+       m->idev = idev;
+
+       idev->private           = m;
+       idev->input->name       = MMA8450_DRV_NAME;
+       idev->input->id.bustype = BUS_I2C;
+       idev->poll              = mma8450_poll;
+       idev->poll_interval     = POLL_INTERVAL;
+       idev->poll_interval_max = POLL_INTERVAL_MAX;
+       idev->open              = mma8450_open;
+       idev->close             = mma8450_close;
+
+       __set_bit(EV_ABS, idev->input->evbit);
+       input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32);
+       input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32);
+       input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32);
+
+       err = input_register_polled_device(idev);
+       if (err) {
+               dev_err(&c->dev, "failed to register polled input device\n");
+               goto err_free_mem;
+       }
+
+       return 0;
+
+err_free_mem:
+       input_free_polled_device(idev);
+       kfree(m);
+       return err;
+}
+
+static int __devexit mma8450_remove(struct i2c_client *c)
+{
+       struct mma8450 *m = i2c_get_clientdata(c);
+       struct input_polled_dev *idev = m->idev;
+
+       input_unregister_polled_device(idev);
+       input_free_polled_device(idev);
+       kfree(m);
+
+       return 0;
+}
+
+static const struct i2c_device_id mma8450_id[] = {
+       { MMA8450_DRV_NAME, 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, mma8450_id);
+
+static struct i2c_driver mma8450_driver = {
+       .driver = {
+               .name   = MMA8450_DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = mma8450_probe,
+       .remove         = __devexit_p(mma8450_remove),
+       .id_table       = mma8450_id,
+};
+
+static int __init mma8450_init(void)
+{
+       return i2c_add_driver(&mma8450_driver);
+}
+module_init(mma8450_init);
+
+static void __exit mma8450_exit(void)
+{
+       i2c_del_driver(&mma8450_driver);
+}
+module_exit(mma8450_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver");
+MODULE_LICENSE("GPL");