From fdb89f8839df0bed76ef2d48ff84c77282041bf8 Mon Sep 17 00:00:00 2001 From: Xinyu Chen Date: Fri, 10 Feb 2012 15:50:27 +0800 Subject: [PATCH] ENGR00174024 mma8451: update 3-axis accelerometer driver 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 --- drivers/hwmon/mxc_mma8451.c | 378 ++++++++++++++++++++++++------------ 1 file changed, 252 insertions(+), 126 deletions(-) diff --git a/drivers/hwmon/mxc_mma8451.c b/drivers/hwmon/mxc_mma8451.c index 44817614b7a5..110ff3485e3f 100644 --- a/drivers/hwmon/mxc_mma8451.c +++ b/drivers/hwmon/mxc_mma8451.c @@ -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 #include #include +#include #include #include #include @@ -32,25 +33,21 @@ #include #include -/* - * 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); } -- 2.39.5