]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00174024 mma8451: update 3-axis accelerometer driver
authorXinyu Chen <b03824@freescale.com>
Fri, 10 Feb 2012 07:50:27 +0000 (15:50 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:33:59 +0000 (08:33 +0200)
Merge mma8451 drivers from sensor team.
The drivers are updated with chip position configure
in driver, export enable and position interface to userspace.

Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
drivers/hwmon/mxc_mma8451.c

index 44817614b7a5c4c5a438a2d77a305b72a6c41163..110ff3485e3ff5c1b82ca305de4646e6df9ce984 100644 (file)
@@ -2,7 +2,7 @@
  *  mma8451.c - Linux kernel modules for 3-Axis Orientation/Motion
  *  Detection Sensor
  *
- *  Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *  Copyright (C) 2010-2012 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
@@ -23,6 +23,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/i2c.h>
+#include <linux/pm.h>
 #include <linux/mutex.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/hwmon.h>
 #include <linux/input-polldev.h>
 
-/*
- * Defines
- */
-#define assert(expr)\
-       if (!(expr)) {\
-               printk(KERN_ERR "Assertion failed! %s,%d,%s,%s\n",\
-                       __FILE__, __LINE__, __func__, #expr);\
-       }
-
-#define MMA8451_DRV_NAME       "mma8451"
 #define MMA8451_I2C_ADDR       0x1C
 #define MMA8451_ID             0x1A
+#define MMA8452_ID             0x2A
+#define MMA8453_ID             0x3A
 
+#define POLL_INTERVAL_MIN      1
 #define POLL_INTERVAL_MAX      500
-#define POLL_INTERVAL          100
+#define POLL_INTERVAL          100     /* msecs */
 #define INPUT_FUZZ             32
 #define INPUT_FLAT             32
 #define MODE_CHANGE_DELAY_MS   100
 
+#define MMA8451_STATUS_ZYXDR   0x08
+#define MMA8451_BUF_SIZE       7
+
 /* register enum for mma8451 registers */
 enum {
        MMA8451_STATUS = 0x00,
@@ -108,92 +105,107 @@ enum {
        MMA8451_REG_END,
 };
 
+/* The sensitivity is represented in counts/g. In 2g mode the
+sensitivity is 1024 counts/g. In 4g mode the sensitivity is 512
+counts/g and in 8g mode the sensitivity is 256 counts/g.
+ */
 enum {
        MODE_2G = 0,
        MODE_4G,
        MODE_8G,
 };
 
+enum {
+       MMA_STANDBY = 0,
+       MMA_ACTIVED,
+};
+
 /* mma8451 status */
 struct mma8451_status {
        u8 mode;
        u8 ctl_reg1;
+       int active;
+       int position;
 };
 
-static struct mma8451_status mma_status = {
-       .mode = 0,
-       .ctl_reg1 = 0
-};
-
+static struct mma8451_status mma_status;
 static struct input_polled_dev *mma8451_idev;
 static struct device *hwmon_dev;
 static struct i2c_client *mma8451_i2c_client;
 
-/***************************************************************
- *
- * Initialization function
- *
- **************************************************************/
-static int mma8451_init_client(struct i2c_client *client)
-{
-       int result;
-
-       mma_status.ctl_reg1 = 0x00;
-       result =
-           i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
-                                     mma_status.ctl_reg1);
-       assert(result == 0);
+static int senstive_mode = MODE_2G;
+static int ACCHAL[8][3][3] = {
+       { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} },
+       { {-1, 0, 0}, {0, -1, 0}, {0, 0, 1} },
+       { {0, 1, 0}, {-1, 0, 0}, {0, 0, 1} },
+       { {1, 0, 0}, {0, 1, 0}, {0, 0, 1} },
+
+       { {0, -1, 0}, {-1, 0, 0}, {0, 0, -1} },
+       { {-1, 0, 0}, {0, 1, 0}, {0, 0, -1} },
+       { {0, 1, 0}, {1, 0, 0}, {0, 0, -1} },
+       { {1, 0, 0}, {0, -1, 0}, {0, 0, -1} },
+};
 
-       mma_status.mode = MODE_2G;
-       result =
-           i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG,
-                                     mma_status.mode);
-       assert(result == 0);
+static DEFINE_MUTEX(mma8451_lock);
+static int mma8451_adjust_position(short *x, short *y, short *z)
+{
+       short rawdata[3], data[3];
+       int i, j;
+       int position = mma_status.position;
+       if (position < 0 || position > 7)
+               position = 0;
+       rawdata[0] = *x;
+       rawdata[1] = *y;
+       rawdata[2] = *z;
+       for (i = 0; i < 3; i++) {
+               data[i] = 0;
+               for (j = 0; j < 3; j++)
+                       data[i] += rawdata[j] * ACCHAL[position][i][j];
+       }
+       *x = data[0];
+       *y = data[1];
+       *z = data[2];
+       return 0;
+}
 
-       mma_status.ctl_reg1 |= 0x01;
-       result =
-           i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
-                                     mma_status.ctl_reg1);
-       assert(result == 0);
+static int mma8451_change_mode(struct i2c_client *client, int mode)
+{
+       int result;
 
+       mma_status.ctl_reg1 = 0;
+       result = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1, 0);
+       if (result < 0)
+               goto out;
+
+       mma_status.mode = mode;
+       result = i2c_smbus_write_byte_data(client, MMA8451_XYZ_DATA_CFG,
+                                          mma_status.mode);
+       if (result < 0)
+               goto out;
+       mma_status.active = MMA_STANDBY;
        mdelay(MODE_CHANGE_DELAY_MS);
 
+       return 0;
+out:
+       dev_err(&client->dev, "error when init mma8451:(%d)", result);
        return result;
 }
 
-/***************************************************************
-*
-* read sensor data from mma8451
-*
-***************************************************************/
 static int mma8451_read_data(short *x, short *y, short *z)
 {
-       u8 tmp_data[7];
+       u8 tmp_data[MMA8451_BUF_SIZE];
+       int ret;
 
-       if (i2c_smbus_read_i2c_block_data
-           (mma8451_i2c_client, MMA8451_OUT_X_MSB, 7, tmp_data) < 7) {
+       ret = i2c_smbus_read_i2c_block_data(mma8451_i2c_client,
+                                           MMA8451_OUT_X_MSB, 7, tmp_data);
+       if (ret < MMA8451_BUF_SIZE) {
                dev_err(&mma8451_i2c_client->dev, "i2c block read failed\n");
-               return -3;
+               return -EIO;
        }
 
        *x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
        *y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
        *z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
-
-       *x = (short)(*x) >> 2;
-       *y = (short)(*y) >> 2;
-       *z = (short)(*z) >> 2;
-
-       if (mma_status.mode == MODE_4G) {
-               (*x) = (*x) << 1;
-               (*y) = (*y) << 1;
-               (*z) = (*z) << 1;
-       } else if (mma_status.mode == MODE_8G) {
-               (*x) = (*x) << 2;
-               (*y) = (*y) << 2;
-               (*z) = (*z) << 2;
-       }
-
        return 0;
 }
 
@@ -201,20 +213,29 @@ static void report_abs(void)
 {
        short x, y, z;
        int result;
+       int retry = 3;
 
+       mutex_lock(&mma8451_lock);
+       if (mma_status.active == MMA_STANDBY)
+               goto out;
+       /* wait for the data ready */
        do {
-               result =
-                   i2c_smbus_read_byte_data(mma8451_i2c_client,
-                                            MMA8451_STATUS);
-       } while (!(result & 0x08));     /* wait for new data */
-
+               result = i2c_smbus_read_byte_data(mma8451_i2c_client,
+                                                 MMA8451_STATUS);
+               retry--;
+               msleep(1);
+       } while (!(result & MMA8451_STATUS_ZYXDR) && retry > 0);
+       if (retry == 0)
+               goto out;
        if (mma8451_read_data(&x, &y, &z) != 0)
-               return;
-
+               goto out;
+       mma8451_adjust_position(&x, &y, &z);
        input_report_abs(mma8451_idev->input, ABS_X, x);
        input_report_abs(mma8451_idev->input, ABS_Y, y);
        input_report_abs(mma8451_idev->input, ABS_Z, z);
        input_sync(mma8451_idev->input);
+out:
+       mutex_unlock(&mma8451_lock);
 }
 
 static void mma8451_dev_poll(struct input_polled_dev *dev)
@@ -222,10 +243,98 @@ static void mma8451_dev_poll(struct input_polled_dev *dev)
        report_abs();
 }
 
+static ssize_t mma8451_enable_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct i2c_client *client;
+       u8 val;
+       int enable;
+
+       mutex_lock(&mma8451_lock);
+       client = mma8451_i2c_client;
+       val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
+       if ((val & 0x01) && mma_status.active == MMA_ACTIVED)
+               enable = 1;
+       else
+               enable = 0;
+       mutex_unlock(&mma8451_lock);
+       return sprintf(buf, "%d\n", enable);
+}
+
+static ssize_t mma8451_enable_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct i2c_client *client;
+       int ret;
+       unsigned long enable;
+       u8 val = 0;
+       enable = simple_strtoul(buf, NULL, 10);
+       mutex_lock(&mma8451_lock);
+       client = mma8451_i2c_client;
+       enable = (enable > 0) ? 1 : 0;
+       if (enable && mma_status.active == MMA_STANDBY) {
+               val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
+               ret =
+                   i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
+                                             val | 0x01);
+               if (!ret) {
+                       mma_status.active = MMA_ACTIVED;
+               }
+       } else if (enable == 0 && mma_status.active == MMA_ACTIVED) {
+               val = i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
+               ret =
+                   i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
+                                             val & 0xFE);
+               if (!ret) {
+                       mma_status.active = MMA_STANDBY;
+               }
+       }
+       mutex_unlock(&mma8451_lock);
+       return count;
+}
+
+static ssize_t mma8451_position_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       int position = 0;
+       mutex_lock(&mma8451_lock);
+       position = mma_status.position;
+       mutex_unlock(&mma8451_lock);
+       return sprintf(buf, "%d\n", position);
+}
+
+static ssize_t mma8451_position_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       int position;
+       position = simple_strtoul(buf, NULL, 10);
+       mutex_lock(&mma8451_lock);
+       mma_status.position = position;
+       mutex_unlock(&mma8451_lock);
+       return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+                  mma8451_enable_show, mma8451_enable_store);
+static DEVICE_ATTR(position, S_IWUSR | S_IRUGO,
+                  mma8451_position_show, mma8451_position_store);
+
+static struct attribute *mma8451_attributes[] = {
+       &dev_attr_enable.attr,
+       &dev_attr_position.attr,
+       NULL
+};
+
+static const struct attribute_group mma8451_attr_group = {
+       .attrs = mma8451_attributes,
+};
+
 static int __devinit mma8451_probe(struct i2c_client *client,
                                   const struct i2c_device_id *id)
 {
-       int result;
+       int result, client_id;
        struct input_dev *idev;
        struct i2c_adapter *adapter;
 
@@ -234,109 +343,129 @@ static int __devinit mma8451_probe(struct i2c_client *client,
        result = i2c_check_functionality(adapter,
                                         I2C_FUNC_SMBUS_BYTE |
                                         I2C_FUNC_SMBUS_BYTE_DATA);
-       assert(result);
+       if (!result)
+               goto err_out;
 
-       printk(KERN_INFO "check mma8451 chip ID\n");
-       result = i2c_smbus_read_byte_data(client, MMA8451_WHO_AM_I);
+       client_id = i2c_smbus_read_byte_data(client, MMA8451_WHO_AM_I);
 
-       if (MMA8451_ID != result) {
+       if (client_id != MMA8451_ID && client_id != MMA8452_ID
+           && client_id != MMA8453_ID) {
                dev_err(&client->dev,
-                       "read chip ID 0x%x is not equal to 0x%x!\n", result,
-                       MMA8451_ID);
-               printk(KERN_INFO "read chip ID failed\n");
+                       "read chip ID 0x%x is not equal to 0x%x or 0x%x!\n",
+                       result, MMA8451_ID, MMA8452_ID);
                result = -EINVAL;
-               goto err_detach_client;
+               goto err_out;
        }
 
        /* Initialize the MMA8451 chip */
-       result = mma8451_init_client(client);
-       assert(result == 0);
+       result = mma8451_change_mode(client, senstive_mode);
+       if (result) {
+               dev_err(&client->dev,
+                       "error when init mma8451 chip:(%d)\n", result);
+               goto err_out;
+       }
 
        hwmon_dev = hwmon_device_register(&client->dev);
-       assert(!(IS_ERR(hwmon_dev)));
-
-       dev_info(&client->dev, "build time %s %s\n", __DATE__, __TIME__);
+       if (!hwmon_dev) {
+               result = -ENOMEM;
+               dev_err(&client->dev, "error when register hwmon device\n");
+               goto err_out;
+       }
 
-       /*input poll device register */
        mma8451_idev = input_allocate_polled_device();
        if (!mma8451_idev) {
-               dev_err(&client->dev, "alloc poll device failed!\n");
                result = -ENOMEM;
-               return result;
+               dev_err(&client->dev, "alloc poll device failed!\n");
+               goto err_alloc_poll_device;
        }
        mma8451_idev->poll = mma8451_dev_poll;
        mma8451_idev->poll_interval = POLL_INTERVAL;
+       mma8451_idev->poll_interval_min = POLL_INTERVAL_MIN;
        mma8451_idev->poll_interval_max = POLL_INTERVAL_MAX;
        idev = mma8451_idev->input;
-       idev->name = MMA8451_DRV_NAME;
+       idev->name = "mma845x";
        idev->id.bustype = BUS_I2C;
        idev->evbit[0] = BIT_MASK(EV_ABS);
 
        input_set_abs_params(idev, ABS_X, -8192, 8191, INPUT_FUZZ, INPUT_FLAT);
        input_set_abs_params(idev, ABS_Y, -8192, 8191, INPUT_FUZZ, INPUT_FLAT);
        input_set_abs_params(idev, ABS_Z, -8192, 8191, INPUT_FUZZ, INPUT_FLAT);
+
        result = input_register_polled_device(mma8451_idev);
        if (result) {
                dev_err(&client->dev, "register poll device failed!\n");
-               return result;
+               goto err_register_polled_device;
        }
+       result = sysfs_create_group(&idev->dev.kobj, &mma8451_attr_group);
+       if (result) {
+               dev_err(&client->dev, "create device file failed!\n");
+               result = -EINVAL;
+               goto err_register_polled_device;
+       }
+       mma_status.position = *(int *)client->dev.platform_data;
+       return 0;
+err_register_polled_device:
+       input_free_polled_device(mma8451_idev);
+err_alloc_poll_device:
+       hwmon_device_unregister(&client->dev);
+err_out:
        return result;
+}
 
-err_detach_client:
-       return result;
+static int mma8451_stop_chip(struct i2c_client *client)
+{
+       int ret = 0;
+       if (mma_status.active == MMA_ACTIVED) {
+               mma_status.ctl_reg1 = i2c_smbus_read_byte_data(client,
+                                                              MMA8451_CTRL_REG1);
+               ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
+                                               mma_status.ctl_reg1 & 0xFE);
+       }
+       return ret;
 }
 
 static int __devexit mma8451_remove(struct i2c_client *client)
 {
-       int result;
-       mma_status.ctl_reg1 =
-           i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
-       result =
-           i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
-                                     mma_status.ctl_reg1 & 0xFE);
-       assert(result == 0);
-
+       int ret;
+       ret = mma8451_stop_chip(client);
        hwmon_device_unregister(hwmon_dev);
 
-       return result;
+       return ret;
 }
 
-static int mma8451_suspend(struct i2c_client *client, pm_message_t mesg)
+#ifdef CONFIG_PM_SLEEP
+static int mma8451_suspend(struct device *dev)
 {
-       int result;
-       mma_status.ctl_reg1 =
-           i2c_smbus_read_byte_data(client, MMA8451_CTRL_REG1);
-       result =
-           i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
-                                     mma_status.ctl_reg1 & 0xFE);
-       assert(result == 0);
-       return result;
+       struct i2c_client *client = to_i2c_client(dev);
+
+       return mma8451_stop_chip(client);
 }
 
-static int mma8451_resume(struct i2c_client *client)
+static int mma8451_resume(struct device *dev)
 {
-       int result;
-       result =
-           i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
-                                     mma_status.ctl_reg1);
-       assert(result == 0);
-       return result;
+       int ret = 0;
+       struct i2c_client *client = to_i2c_client(dev);
+       if (mma_status.active == MMA_ACTIVED)
+               ret = i2c_smbus_write_byte_data(client, MMA8451_CTRL_REG1,
+                                               mma_status.ctl_reg1);
+       return ret;
+
 }
+#endif
 
 static const struct i2c_device_id mma8451_id[] = {
-       {MMA8451_DRV_NAME, 0},
-       {},
+       {"mma8451", 0},
 };
 
 MODULE_DEVICE_TABLE(i2c, mma8451_id);
 
+static SIMPLE_DEV_PM_OPS(mma8451_pm_ops, mma8451_suspend, mma8451_resume);
 static struct i2c_driver mma8451_driver = {
        .driver = {
-                  .name = MMA8451_DRV_NAME,
+                  .name = "mma8451",
                   .owner = THIS_MODULE,
+                  .pm = &mma8451_pm_ops,
                   },
-       .suspend = mma8451_suspend,
-       .resume = mma8451_resume,
        .probe = mma8451_probe,
        .remove = __devexit_p(mma8451_remove),
        .id_table = mma8451_id,
@@ -352,14 +481,11 @@ static int __init mma8451_init(void)
                printk(KERN_INFO "add mma8451 i2c driver failed\n");
                return -ENODEV;
        }
-       printk(KERN_INFO "add mma8451 i2c driver\n");
-
        return res;
 }
 
 static void __exit mma8451_exit(void)
 {
-       printk(KERN_INFO "remove mma8451 i2c driver.\n");
        i2c_del_driver(&mma8451_driver);
 }