diff options
author | Xinyu Chen <b03824@freescale.com> | 2012-02-10 08:48:10 +0800 |
---|---|---|
committer | Xinyu Chen <b03824@freescale.com> | 2012-02-10 12:51:47 +0800 |
commit | 4e2b99b9b80d45ffbd2016f7723d8022186cd472 (patch) | |
tree | f49e2972865e48c0b1a338ef012b62bf4dfdcb0e /drivers/hwmon | |
parent | 25c46eaf509334a5b9f604251e7c39226086a11a (diff) |
ENGR00174127-1 hwmon: merge the mag3110 and mma8451 driver
Merge mag3110 and mma8451 drivers from sensor team.
The drivers are updated with chip position configure
in driver, export set delay interface to userspace and
use polling mode in mag3110 instead of interrupt mode.
Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
Diffstat (limited to 'drivers/hwmon')
-rwxr-xr-x | drivers/hwmon/Kconfig | 10 | ||||
-rwxr-xr-x | drivers/hwmon/Makefile | 1 | ||||
-rwxr-xr-x | drivers/hwmon/mag3110.c | 570 | ||||
-rw-r--r-- | drivers/hwmon/mxc_mma8451.c | 378 |
4 files changed, 833 insertions, 126 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index fdea3c01ad45..2e7757394acc 100755 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1408,6 +1408,16 @@ config SENSORS_ATK0110 endif # ACPI +config SENSORS_MAG3110 + tristate "Freescale MAG3110 e-compass sensor" + depends on I2C && SYSFS + help + If you say yes here you get support for the Freescale MAG3110 + e-compass sensor. + + This driver can also be built as a module. If so, the module + will be called mag3110. + config MXC_MMA8450 tristate "MMA8450 device driver" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 32bb2da23954..fc6e6b43a287 100755 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o +obj-$(CONFIG_SENSORS_MAG3110) += mag3110.o obj-$(CONFIG_MXC_MMA8450) += mxc_mma8450.o obj-$(CONFIG_MXC_MMA8451) += mxc_mma8451.o obj-$(CONFIG_SENSORS_DA9052) += da9052-adc.o diff --git a/drivers/hwmon/mag3110.c b/drivers/hwmon/mag3110.c new file mode 100755 index 000000000000..55f2114869cd --- /dev/null +++ b/drivers/hwmon/mag3110.c @@ -0,0 +1,570 @@ +/* + * + * Copyright (C) 2011-2012 Freescale Semiconductor, Inc. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/input-polldev.h> +#include <linux/hwmon.h> +#include <linux/input.h> +#include <linux/wait.h> +#include <linux/workqueue.h> + +#define MAG3110_DRV_NAME "mag3110" +#define MAG3110_ID 0xC4 +#define MAG3110_XYZ_DATA_LEN 6 +#define MAG3110_STATUS_ZYXDR 0x08 + +#define MAG3110_AC_MASK (0x01) +#define MAG3110_AC_OFFSET 0 +#define MAG3110_DR_MODE_MASK (0x7 << 5) +#define MAG3110_DR_MODE_OFFSET 5 +#define MAG3110_IRQ_USED 0 + +#define POLL_INTERVAL_MAX 500 +#define POLL_INTERVAL 100 +#define INT_TIMEOUT 1000 +/* register enum for mag3110 registers */ +enum { + MAG3110_DR_STATUS = 0x00, + MAG3110_OUT_X_MSB, + MAG3110_OUT_X_LSB, + MAG3110_OUT_Y_MSB, + MAG3110_OUT_Y_LSB, + MAG3110_OUT_Z_MSB, + MAG3110_OUT_Z_LSB, + MAG3110_WHO_AM_I, + + MAG3110_OFF_X_MSB, + MAG3110_OFF_X_LSB, + MAG3110_OFF_Y_MSB, + MAG3110_OFF_Y_LSB, + MAG3110_OFF_Z_MSB, + MAG3110_OFF_Z_LSB, + + MAG3110_DIE_TEMP, + + MAG3110_CTRL_REG1 = 0x10, + MAG3110_CTRL_REG2, +}; +enum { + MAG_STANDBY, + MAG_ACTIVED +}; +struct mag3110_data { + struct i2c_client *client; + struct input_polled_dev *poll_dev; + struct device *hwmon_dev; + wait_queue_head_t waitq; + bool data_ready; + u8 ctl_reg1; + int active; + int position; +}; +static short MAGHAL[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} }, +}; + +static struct mag3110_data *mag3110_pdata; +/*! + * This function do one mag3110 register read. + */ +static DEFINE_MUTEX(mag3110_lock); +static int mag3110_adjust_position(short *x, short *y, short *z) +{ + short rawdata[3], data[3]; + int i, j; + int position = mag3110_pdata->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] * MAGHAL[position][i][j]; + } + *x = data[0]; + *y = data[1]; + *z = data[2]; + return 0; +} + +static int mag3110_read_reg(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/*! + * This function do one mag3110 register write. + */ +static int mag3110_write_reg(struct i2c_client *client, u8 reg, char value) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, reg, value); + if (ret < 0) + dev_err(&client->dev, "i2c write failed\n"); + return ret; +} + +/*! + * This function do multiple mag3110 registers read. + */ +static int mag3110_read_block_data(struct i2c_client *client, u8 reg, + int count, u8 *addr) +{ + if (i2c_smbus_read_i2c_block_data(client, reg, count, addr) < count) { + dev_err(&client->dev, "i2c block read failed\n"); + return -1; + } + + return count; +} + +/* + * Initialization function + */ +static int mag3110_init_client(struct i2c_client *client) +{ + int val, ret; + + /* enable automatic resets */ + val = 0x80; + ret = mag3110_write_reg(client, MAG3110_CTRL_REG2, val); + + /* set default data rate to 10HZ */ + val = mag3110_read_reg(client, MAG3110_CTRL_REG1); + val |= (0x0 << MAG3110_DR_MODE_OFFSET); + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, val); + + return ret; +} + +/*************************************************************** +* +* read sensor data from mag3110 +* +***************************************************************/ +static int mag3110_read_data(short *x, short *y, short *z) +{ + struct mag3110_data *data; + int retry = 3; + u8 tmp_data[MAG3110_XYZ_DATA_LEN]; + int result; + if (!mag3110_pdata || mag3110_pdata->active == MAG_STANDBY) + return -EINVAL; + + data = mag3110_pdata; +#if MAG3110_IRQ_USED + if (!wait_event_interruptible_timeout + (data->waitq, data->data_ready != 0, + msecs_to_jiffies(INT_TIMEOUT))) { + dev_dbg(&data->client->dev, "interrupt not received\n"); + return -ETIME; + } +#else + do { + msleep(1); + result = i2c_smbus_read_byte_data(data->client, + MAG3110_DR_STATUS); + retry--; + } while (!(result & MAG3110_STATUS_ZYXDR) && retry > 0); + /* Clear data_ready flag after data is read out */ + if (retry == 0) { + return -EINVAL; + } +#endif + + data->data_ready = 0; + + if (mag3110_read_block_data(data->client, + MAG3110_OUT_X_MSB, MAG3110_XYZ_DATA_LEN, + tmp_data) < 0) + return -1; + + *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]; + + return 0; +} + +static void report_abs(void) +{ + struct input_dev *idev; + short x, y, z; + + mutex_lock(&mag3110_lock); + if (mag3110_read_data(&x, &y, &z) != 0) + goto out; + mag3110_adjust_position(&x, &y, &z); + idev = mag3110_pdata->poll_dev->input; + input_report_abs(idev, ABS_X, x); + input_report_abs(idev, ABS_Y, y); + input_report_abs(idev, ABS_Z, z); + input_sync(idev); +out: + mutex_unlock(&mag3110_lock); +} + +static void mag3110_dev_poll(struct input_polled_dev *dev) +{ + report_abs(); +} + +#if MAG3110_IRQ_USED +static irqreturn_t mag3110_irq_handler(int irq, void *dev_id) +{ + mag3110_pdata->data_ready = 1; + wake_up_interruptible(&mag3110_pdata->waitq); + + return IRQ_HANDLED; +} +#endif +static ssize_t mag3110_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + int val; + mutex_lock(&mag3110_lock); + client = mag3110_pdata->client; + val = mag3110_read_reg(client, MAG3110_CTRL_REG1) & MAG3110_AC_MASK; + + mutex_unlock(&mag3110_lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t mag3110_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + int reg, ret, enable; + u8 tmp_data[MAG3110_XYZ_DATA_LEN]; + + enable = simple_strtoul(buf, NULL, 10); + mutex_lock(&mag3110_lock); + client = mag3110_pdata->client; + reg = mag3110_read_reg(client, MAG3110_CTRL_REG1); + if (enable && mag3110_pdata->active == MAG_STANDBY) { + reg |= MAG3110_AC_MASK; + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); + if (!ret) + mag3110_pdata->active = MAG_ACTIVED; + } else if (!enable && mag3110_pdata->active == MAG_ACTIVED) { + reg &= ~MAG3110_AC_MASK; + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); + if (!ret) + mag3110_pdata->active = MAG_STANDBY; + } + + if (mag3110_pdata->active == MAG_ACTIVED) { + msleep(100); + /* Read out MSB data to clear interrupt flag automatically */ + mag3110_read_block_data(client, MAG3110_OUT_X_MSB, + MAG3110_XYZ_DATA_LEN, tmp_data); + } + mutex_unlock(&mag3110_lock); + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + mag3110_enable_show, mag3110_enable_store); + +static ssize_t mag3110_dr_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + int val; + + client = mag3110_pdata->client; + val = (mag3110_read_reg(client, MAG3110_CTRL_REG1) + & MAG3110_DR_MODE_MASK) >> MAG3110_DR_MODE_OFFSET; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t mag3110_dr_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client; + int reg, ret; + unsigned long val; + + /* This must be done when mag3110 is disabled */ + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 7)) + return -EINVAL; + + client = mag3110_pdata->client; + reg = mag3110_read_reg(client, MAG3110_CTRL_REG1) & + ~MAG3110_DR_MODE_MASK; + reg |= (val << MAG3110_DR_MODE_OFFSET); + /* MAG3110_CTRL_REG1 bit 5-7: data rate mode */ + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, reg); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(dr_mode, S_IWUSR | S_IRUGO, + mag3110_dr_mode_show, mag3110_dr_mode_store); + +static ssize_t mag3110_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + mutex_lock(&mag3110_lock); + val = mag3110_pdata->position; + mutex_unlock(&mag3110_lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t mag3110_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(&mag3110_lock); + mag3110_pdata->position = position; + mutex_unlock(&mag3110_lock); + return count; +} + +static DEVICE_ATTR(position, S_IWUSR | S_IRUGO, + mag3110_position_show, mag3110_position_store); + +static struct attribute *mag3110_attributes[] = { + &dev_attr_enable.attr, + &dev_attr_dr_mode.attr, + &dev_attr_position.attr, + NULL +}; + +static const struct attribute_group mag3110_attr_group = { + .attrs = mag3110_attributes, +}; + +static int __devinit mag3110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter; + struct input_dev *idev; + struct mag3110_data *data; + int ret = 0; + + adapter = to_i2c_adapter(client->dev.parent); + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + dev_info(&client->dev, "check mag3110 chip ID\n"); + ret = mag3110_read_reg(client, MAG3110_WHO_AM_I); + + if (MAG3110_ID != ret) { + dev_err(&client->dev, + "read chip ID 0x%x is not equal to 0x%x!\n", ret, + MAG3110_ID); + return -EINVAL; + } + data = kzalloc(sizeof(struct mag3110_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->client = client; + i2c_set_clientdata(client, data); + /* Init queue */ + init_waitqueue_head(&data->waitq); + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + dev_err(&client->dev, "hwmon register failed!\n"); + ret = PTR_ERR(data->hwmon_dev); + goto error_rm_dev_sysfs; + } + + /*input poll device register */ + data->poll_dev = input_allocate_polled_device(); + if (!data->poll_dev) { + dev_err(&client->dev, "alloc poll device failed!\n"); + ret = -ENOMEM; + goto error_rm_hwmon_dev; + } + data->poll_dev->poll = mag3110_dev_poll; + data->poll_dev->poll_interval = POLL_INTERVAL; + data->poll_dev->poll_interval_max = POLL_INTERVAL_MAX; + idev = data->poll_dev->input; + idev->name = MAG3110_DRV_NAME; + idev->id.bustype = BUS_I2C; + idev->evbit[0] = BIT_MASK(EV_ABS); + input_set_abs_params(idev, ABS_X, -15000, 15000, 0, 0); + input_set_abs_params(idev, ABS_Y, -15000, 15000, 0, 0); + input_set_abs_params(idev, ABS_Z, -15000, 15000, 0, 0); + ret = input_register_polled_device(data->poll_dev); + if (ret) { + dev_err(&client->dev, "register poll device failed!\n"); + goto error_free_poll_dev; + } + + /*create device group in sysfs as user interface */ + ret = sysfs_create_group(&idev->dev.kobj, &mag3110_attr_group); + if (ret) { + dev_err(&client->dev, "create device file failed!\n"); + ret = -EINVAL; + goto error_rm_poll_dev; + } + /* set irq type to edge rising */ +#if MAG3110_IRQ_USED + ret = request_irq(client->irq, mag3110_irq_handler, + IRQF_TRIGGER_RISING, client->dev.driver->name, idev); + if (ret < 0) { + dev_err(&client->dev, "failed to register irq %d!\n", + client->irq); + goto error_rm_dev_sysfs; + } +#endif + /* Initialize mag3110 chip */ + mag3110_init_client(client); + mag3110_pdata = data; + mag3110_pdata->active = MAG_STANDBY; + mag3110_pdata->position = *(int *)client->dev.platform_data; + dev_info(&client->dev, "mag3110 is probed\n"); + return 0; +error_rm_dev_sysfs: + sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group); +error_rm_poll_dev: + input_unregister_polled_device(data->poll_dev); +error_free_poll_dev: + input_free_polled_device(data->poll_dev); +error_rm_hwmon_dev: + hwmon_device_unregister(data->hwmon_dev); + + kfree(data); + mag3110_pdata = NULL; + + return ret; +} + +static int __devexit mag3110_remove(struct i2c_client *client) +{ + struct mag3110_data *data; + int ret; + + data = i2c_get_clientdata(client); + + data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1); + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, + data->ctl_reg1 & ~MAG3110_AC_MASK); + + free_irq(client->irq, data); + input_unregister_polled_device(data->poll_dev); + input_free_polled_device(data->poll_dev); + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &mag3110_attr_group); + kfree(data); + mag3110_pdata = NULL; + + return ret; +} + +#ifdef CONFIG_PM +static int mag3110_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret = 0; + struct mag3110_data *data = i2c_get_clientdata(client); + if (data->active == MAG_ACTIVED) { + data->ctl_reg1 = mag3110_read_reg(client, MAG3110_CTRL_REG1); + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, + data->ctl_reg1 & ~MAG3110_AC_MASK); + } + return ret; +} + +static int mag3110_resume(struct i2c_client *client) +{ + int ret = 0; + u8 tmp_data[MAG3110_XYZ_DATA_LEN]; + struct mag3110_data *data = i2c_get_clientdata(client); + if (data->active == MAG_ACTIVED) { + ret = mag3110_write_reg(client, MAG3110_CTRL_REG1, + data->ctl_reg1); + + if (data->ctl_reg1 & MAG3110_AC_MASK) { + /* Read out MSB data to clear interrupt flag automatically */ + mag3110_read_block_data(client, MAG3110_OUT_X_MSB, + MAG3110_XYZ_DATA_LEN, tmp_data); + } + } + return ret; +} + +#else +#define mag3110_suspend NULL +#define mag3110_resume NULL +#endif /* CONFIG_PM */ + +static const struct i2c_device_id mag3110_id[] = { + {MAG3110_DRV_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mag3110_id); +static struct i2c_driver mag3110_driver = { + .driver = {.name = MAG3110_DRV_NAME, + .owner = THIS_MODULE,}, + .suspend = mag3110_suspend, + .resume = mag3110_resume, + .probe = mag3110_probe, + .remove = __devexit_p(mag3110_remove), + .id_table = mag3110_id, +}; + +static int __init mag3110_init(void) +{ + return i2c_add_driver(&mag3110_driver); +} + +static void __exit mag3110_exit(void) +{ + i2c_del_driver(&mag3110_driver); +} + +module_init(mag3110_init); +module_exit(mag3110_exit); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Freescale mag3110 3-axis magnetometer driver"); +MODULE_LICENSE("GPL"); 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 <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> @@ -32,25 +33,21 @@ #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); } |