summaryrefslogtreecommitdiff
path: root/drivers/hwmon/mma8x5x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/mma8x5x.c')
-rw-r--r--drivers/hwmon/mma8x5x.c907
1 files changed, 907 insertions, 0 deletions
diff --git a/drivers/hwmon/mma8x5x.c b/drivers/hwmon/mma8x5x.c
new file mode 100644
index 000000000000..4b13a2521f86
--- /dev/null
+++ b/drivers/hwmon/mma8x5x.c
@@ -0,0 +1,907 @@
+/*
+ * mma8x5x.c - Linux kernel modules for 3-Axis Orientation/Motion
+ * Detection Sensor MMA8451/MMA8452/MMA8453/MMA8652/MMA8653
+ *
+ * Copyright (C) 2012-2014 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/module.h>
+#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/irq.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/poll.h>
+#include <linux/hwmon.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+
+#define MMA8X5X_I2C_ADDR 0x1D
+#define MMA8451_ID 0x1A
+#define MMA8452_ID 0x2A
+#define MMA8453_ID 0x3A
+#define MMA8652_ID 0x4A
+#define MMA8653_ID 0x5A
+
+
+#define POLL_INTERVAL_MIN 1
+#define POLL_INTERVAL_MAX 500
+#define POLL_INTERVAL 100 /* msecs */
+
+/* if sensor is standby ,set POLL_STOP_TIME to slow down the poll */
+#define POLL_STOP_TIME 200
+#define INPUT_FUZZ 32
+#define INPUT_FLAT 32
+#define MODE_CHANGE_DELAY_MS 100
+
+#define MMA8X5X_STATUS_ZYXDR 0x08
+#define MMA8X5X_BUF_SIZE 6
+
+#define MMA8X5X_FIFO_SIZE 32
+/* register enum for mma8x5x registers */
+enum {
+ MMA8X5X_STATUS = 0x00,
+ MMA8X5X_OUT_X_MSB,
+ MMA8X5X_OUT_X_LSB,
+ MMA8X5X_OUT_Y_MSB,
+ MMA8X5X_OUT_Y_LSB,
+ MMA8X5X_OUT_Z_MSB,
+ MMA8X5X_OUT_Z_LSB,
+
+ MMA8X5X_F_SETUP = 0x09,
+ MMA8X5X_TRIG_CFG,
+ MMA8X5X_SYSMOD,
+ MMA8X5X_INT_SOURCE,
+ MMA8X5X_WHO_AM_I,
+ MMA8X5X_XYZ_DATA_CFG,
+ MMA8X5X_HP_FILTER_CUTOFF,
+
+ MMA8X5X_PL_STATUS,
+ MMA8X5X_PL_CFG,
+ MMA8X5X_PL_COUNT,
+ MMA8X5X_PL_BF_ZCOMP,
+ MMA8X5X_P_L_THS_REG,
+
+ MMA8X5X_FF_MT_CFG,
+ MMA8X5X_FF_MT_SRC,
+ MMA8X5X_FF_MT_THS,
+ MMA8X5X_FF_MT_COUNT,
+
+ MMA8X5X_TRANSIENT_CFG = 0x1D,
+ MMA8X5X_TRANSIENT_SRC,
+ MMA8X5X_TRANSIENT_THS,
+ MMA8X5X_TRANSIENT_COUNT,
+
+ MMA8X5X_PULSE_CFG,
+ MMA8X5X_PULSE_SRC,
+ MMA8X5X_PULSE_THSX,
+ MMA8X5X_PULSE_THSY,
+ MMA8X5X_PULSE_THSZ,
+ MMA8X5X_PULSE_TMLT,
+ MMA8X5X_PULSE_LTCY,
+ MMA8X5X_PULSE_WIND,
+
+ MMA8X5X_ASLP_COUNT,
+ MMA8X5X_CTRL_REG1,
+ MMA8X5X_CTRL_REG2,
+ MMA8X5X_CTRL_REG3,
+ MMA8X5X_CTRL_REG4,
+ MMA8X5X_CTRL_REG5,
+
+ MMA8X5X_OFF_X,
+ MMA8X5X_OFF_Y,
+ MMA8X5X_OFF_Z,
+
+ MMA8X5X_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,
+};
+#pragma pack(1)
+struct mma8x5x_data_axis {
+ short x;
+ short y;
+ short z;
+};
+struct mma8x5x_fifo{
+ int count;
+ s64 period;
+ s64 timestamp;
+ struct mma8x5x_data_axis fifo_data[MMA8X5X_FIFO_SIZE];
+};
+#pragma pack()
+
+struct mma8x5x_data {
+ struct i2c_client *client;
+ struct input_dev *idev;
+ struct delayed_work work;
+ struct mutex data_lock;
+ struct mma8x5x_fifo fifo;
+ wait_queue_head_t fifo_wq;
+ atomic_t fifo_ready;
+ int active;
+ int delay;
+ int position;
+ u8 chip_id;
+ int mode;
+ int awaken; // is just awake from suspend
+ s64 period_rel;
+ int fifo_wakeup;
+ int fifo_timeout;
+};
+static struct mma8x5x_data * p_mma8x5x_data = NULL;
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x1c, 0x1d, I2C_CLIENT_END };
+
+static int mma8x5x_chip_id[] = {
+ MMA8451_ID,
+ MMA8452_ID,
+ MMA8453_ID,
+ MMA8652_ID,
+ MMA8653_ID,
+};
+static char *mma8x5x_names[] = {
+ "mma8451",
+ "mma8452",
+ "mma8453",
+ "mma8652",
+ "mma8653",
+};
+static int mma8x5x_position_setting[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 int mma8x5x_data_convert(struct mma8x5x_data *pdata,
+ struct mma8x5x_data_axis *axis_data)
+{
+ short rawdata[3], data[3];
+ int i, j;
+ int position = pdata->position;
+
+ if (position < 0 || position > 7)
+ position = 0;
+ rawdata[0] = axis_data->x;
+ rawdata[1] = axis_data->y;
+ rawdata[2] = axis_data->z;
+ for (i = 0; i < 3; i++) {
+ data[i] = 0;
+ for (j = 0; j < 3; j++)
+ data[i] += rawdata[j] * mma8x5x_position_setting[position][i][j];
+ }
+ axis_data->x = data[0];
+ axis_data->y = data[1];
+ axis_data->z = data[2];
+ return 0;
+}
+static int mma8x5x_check_id(int id)
+{
+ int i = 0;
+
+ for (i = 0; i < sizeof(mma8x5x_chip_id) / sizeof(mma8x5x_chip_id[0]); i++)
+ if (id == mma8x5x_chip_id[i])
+ return 1;
+ return 0;
+}
+static char *mma8x5x_id2name(u8 id)
+{
+ return mma8x5x_names[(id >> 4) - 1];
+}
+
+static int mma8x5x_i2c_read_fifo(struct i2c_client *client,u8 reg, char * buf, int len)
+{
+ char send_buf[] = {reg};
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = send_buf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ },
+ };
+ if (i2c_transfer(client->adapter, msgs, 2) < 0) {
+ printk(KERN_ERR "mma8x5x: transfer error\n");
+ return -EIO;
+ } else
+ return len;
+}
+
+/*period is ms, return the real period per event*/
+static s64 mma8x5x_odr_set(struct i2c_client * client, int period){
+ u8 odr;
+ u8 val;
+ s64 period_rel;
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1,val &(~0x01)); //standby
+ val &= ~(0x07 << 3);
+ if(period >= 640){ /*1.56HZ*/
+ odr = 0x7;
+ period_rel = 640 * NSEC_PER_MSEC;
+ }
+ else if(period >= 160){ /*6.25HZ*/
+ odr = 0x06;
+ period_rel = 160 * NSEC_PER_MSEC;
+ }
+ else if(period >= 80){ /*12.5HZ*/
+ odr = 0x05;
+ period_rel = 80 * NSEC_PER_MSEC;
+ }else if(period >= 20){ /*50HZ*/
+ odr = 0x04;
+ period_rel = 20 * NSEC_PER_MSEC;
+ }else if(period >= 10){ /*100HZ*/
+ odr = 0x03;
+ period_rel = 10 * NSEC_PER_MSEC;
+ }else if(period >= 5){ /*200HZ*/
+ odr = 0x02;
+ period_rel = 5 * NSEC_PER_MSEC;
+ }else if((period * 2) >= 5){ /*400HZ*/
+ odr = 0x01;
+ period_rel = 2500 * NSEC_PER_USEC;
+ }else{ /*800HZ*/
+ odr = 0x00;
+ period_rel = 1250 * NSEC_PER_USEC;
+ }
+ val |= (odr << 3);
+ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1,val); //standby
+ return period_rel;
+}
+static int mma8x5x_device_init(struct i2c_client *client)
+{
+ int result;
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+
+ result = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, 0);
+ if (result < 0)
+ goto out;
+
+ result = i2c_smbus_write_byte_data(client, MMA8X5X_XYZ_DATA_CFG,
+ pdata->mode);
+ if (result < 0)
+ goto out;
+ pdata->active = MMA_STANDBY;
+ msleep(MODE_CHANGE_DELAY_MS);
+ return 0;
+out:
+ dev_err(&client->dev, "error when init mma8x5x:(%d)", result);
+ return result;
+}
+static int mma8x5x_device_stop(struct i2c_client *client)
+{
+ u8 val;
+
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & 0xfe);
+ return 0;
+}
+static int mma8x5x_read_data(struct i2c_client *client,
+ struct mma8x5x_data_axis *data)
+{
+ u8 tmp_data[MMA8X5X_BUF_SIZE];
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client,
+ MMA8X5X_OUT_X_MSB,
+ MMA8X5X_BUF_SIZE, tmp_data);
+ if (ret < MMA8X5X_BUF_SIZE) {
+ dev_err(&client->dev, "i2c block read failed\n");
+ return -EIO;
+ }
+ data->x = ((tmp_data[0] << 8) & 0xff00) | tmp_data[1];
+ data->y = ((tmp_data[2] << 8) & 0xff00) | tmp_data[3];
+ data->z = ((tmp_data[4] << 8) & 0xff00) | tmp_data[5];
+ return 0;
+}
+static int mma8x5x_fifo_interrupt(struct i2c_client *client,int enable)
+{
+ u8 val,sys_mode;
+ sys_mode = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG1);
+ i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,(sys_mode & (~0x01))); //standby
+ val = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG4);
+ val &= ~(0x01 << 6);
+ if(enable)
+ val |= (0x01 << 6);
+ i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG4,val);
+ i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,sys_mode);
+ return 0;
+}
+static int mma8x5x_fifo_setting(struct mma8x5x_data *pdata,int time_out,int is_overwrite)
+{
+ u8 val,sys_mode,pin_cfg;
+ struct i2c_client *client = pdata->client;
+ sys_mode = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG1);
+ i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,(sys_mode & (~0x01))); //standby
+ pin_cfg = i2c_smbus_read_byte_data(client,MMA8X5X_CTRL_REG5);
+ val = i2c_smbus_read_byte_data(client,MMA8X5X_F_SETUP);
+ val &= ~(0x03 << 6);
+ if(time_out > 0){
+ if(is_overwrite)
+ val |= (0x01 << 6);
+ else
+ val |= (0x02 << 6);
+ }
+ i2c_smbus_write_byte_data(client,MMA8X5X_F_SETUP,val);
+ i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG5,pin_cfg |(0x01 << 6));//route to pin 1
+ i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1,sys_mode);
+ if(time_out > 0){
+ pdata->period_rel = mma8x5x_odr_set(client,time_out/32); //fifo len is 32
+ }
+ return 0;
+}
+static int mma8x5x_read_fifo_data(struct mma8x5x_data *pdata){
+ int count,cnt;
+ u8 buf[256],val;
+ int i,index;
+ struct i2c_client *client = pdata->client;
+ struct mma8x5x_fifo *pfifo = &pdata->fifo;
+ struct timespec ts;
+ val = i2c_smbus_read_byte_data(client,MMA8X5X_STATUS);
+ if(val & (0x01 << 7)) //fifo overflow
+ {
+ cnt = (val & 0x3f);
+ count = mma8x5x_i2c_read_fifo(client,MMA8X5X_OUT_X_MSB,buf,MMA8X5X_BUF_SIZE * cnt);
+ if(count > 0){
+ ktime_get_ts(&ts);
+ for(i = 0; i < count/MMA8X5X_BUF_SIZE ;i++){
+ index = MMA8X5X_BUF_SIZE * i;
+ pfifo->fifo_data[i].x = ((buf[index] << 8) & 0xff00) | buf[index + 1];
+ pfifo->fifo_data[i].y = ((buf[index + 2] << 8) & 0xff00) | buf[index + 3];
+ pfifo->fifo_data[i].z = ((buf[index + 4] << 8) & 0xff00) | buf[index + 5];
+ mma8x5x_data_convert(pdata, &pfifo->fifo_data[i]);
+ }
+ pfifo->period = pdata->period_rel;
+ pfifo->count = count / MMA8X5X_BUF_SIZE;
+ pfifo->timestamp = ((s64)ts.tv_sec) * NSEC_PER_SEC + ts.tv_nsec;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static void mma8x5x_report_data(struct mma8x5x_data *pdata)
+{
+ struct mma8x5x_data_axis data;
+ int ret;
+ ret = mma8x5x_read_data(pdata->client, &data);
+ if(!ret){
+ mma8x5x_data_convert(pdata, &data);
+ input_report_abs(pdata->idev, ABS_X, data.x);
+ input_report_abs(pdata->idev, ABS_Y, data.y);
+ input_report_abs(pdata->idev, ABS_Z, data.z);
+ input_sync(pdata->idev);
+ }
+}
+static void mma8x5x_work(struct mma8x5x_data * pdata){
+ int delay;
+ if(pdata->active == MMA_ACTIVED){
+ delay = msecs_to_jiffies(pdata->delay);
+ if (delay >= HZ)
+ delay = round_jiffies_relative(delay);
+ schedule_delayed_work(&pdata->work, delay);
+ }
+}
+static void mma8x5x_dev_poll(struct work_struct *work)
+{
+ struct mma8x5x_data *pdata = container_of(work, struct mma8x5x_data,work.work);
+ mma8x5x_report_data(pdata);
+ mma8x5x_work(pdata);
+}
+static irqreturn_t mma8x5x_irq_handler(int irq, void *dev)
+{
+ int ret;
+ u8 int_src;
+ struct mma8x5x_data *pdata = (struct mma8x5x_data *)dev;
+ int_src = i2c_smbus_read_byte_data(pdata->client,MMA8X5X_INT_SOURCE);
+ if(int_src & (0x01 << 6)){
+ ret = mma8x5x_read_fifo_data(pdata);
+ if(!ret){
+ atomic_set(&pdata->fifo_ready, 1);
+ wake_up(&pdata->fifo_wq);
+ }
+ if(pdata->awaken) /*is just awken from suspend*/
+ {
+ mma8x5x_fifo_setting(pdata,pdata->fifo_timeout,0); //10s timeout
+ mma8x5x_fifo_interrupt(pdata->client,1);
+ pdata->awaken = 0;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+
+static ssize_t mma8x5x_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ struct i2c_client *client = pdata->client;
+ u8 val;
+ int enable;
+
+ mutex_lock(&pdata->data_lock);
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ if ((val & 0x01) && pdata->active == MMA_ACTIVED)
+ enable = 1;
+ else
+ enable = 0;
+ mutex_unlock(&pdata->data_lock);
+ return sprintf(buf, "%d\n", enable);
+}
+
+static ssize_t mma8x5x_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ struct i2c_client *client = pdata->client;
+ int ret;
+ unsigned long enable;
+ u8 val = 0;
+
+ enable = simple_strtoul(buf, NULL, 10);
+ mutex_lock(&pdata->data_lock);
+ enable = (enable > 0) ? 1 : 0;
+ if (enable && pdata->active == MMA_STANDBY) {
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ ret = i2c_smbus_write_byte_data(client,MMA8X5X_CTRL_REG1, val | 0x01);
+ if (!ret) {
+ pdata->active = MMA_ACTIVED;
+ if(pdata->fifo_timeout <= 0) //continuous mode
+ mma8x5x_work(pdata);
+ else{ /*fifo mode*/
+ mma8x5x_fifo_setting(pdata,pdata->fifo_timeout,0); //no overwirte fifo
+ mma8x5x_fifo_interrupt(client,1);
+ }
+ printk(KERN_INFO"mma enable setting active \n");
+ }
+ } else if (enable == 0 && pdata->active == MMA_ACTIVED) {
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ ret = i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & 0xFE);
+ if (!ret) {
+ pdata->active = MMA_STANDBY;
+ if(pdata->fifo_timeout <= 0) //continuous mode
+ cancel_delayed_work_sync(&pdata->work);
+ else{ /*fifo mode*/
+ mma8x5x_fifo_setting(pdata,0,0); //no overwirte fifo
+ mma8x5x_fifo_interrupt(client,0);
+ }
+ printk(KERN_INFO"mma enable setting inactive \n");
+ }
+ }
+ mutex_unlock(&pdata->data_lock);
+ return count;
+}
+
+static ssize_t mma8x5x_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ int delay;
+ mutex_lock(&pdata->data_lock);
+ delay = pdata->delay;
+ mutex_unlock(&pdata->data_lock);
+ return sprintf(buf, "%d\n", delay);
+}
+
+static ssize_t mma8x5x_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ struct i2c_client * client = pdata->client;
+ int delay;
+ delay = simple_strtoul(buf, NULL, 10);
+ mutex_lock(&pdata->data_lock);
+ cancel_delayed_work_sync(&pdata->work);
+ pdata->delay = delay;
+ if(pdata->active == MMA_ACTIVED && pdata->fifo_timeout <= 0){
+ mma8x5x_odr_set(client,delay);
+ mma8x5x_work(pdata);
+ }
+ mutex_unlock(&pdata->data_lock);
+ return count;
+}
+
+static ssize_t mma8x5x_fifo_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count = 0;
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ mutex_lock(&pdata->data_lock);
+ count = sprintf(&buf[count], "period poll :%d ms\n", pdata->delay);
+ count += sprintf(&buf[count],"period fifo :%lld ns\n",pdata->period_rel);
+ count += sprintf(&buf[count],"timeout :%d ms\n", pdata->fifo_timeout);
+ count += sprintf(&buf[count],"interrupt wake up: %s\n", (pdata->fifo_wakeup ? "yes" : "no")); /*is the interrupt enable*/
+ mutex_unlock(&pdata->data_lock);
+ return count;
+}
+
+static ssize_t mma8x5x_fifo_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ struct i2c_client * client = pdata->client;
+ int period,timeout,wakeup;
+ sscanf(buf,"%d,%d,%d",&period,&timeout,&wakeup);
+ printk("period %d ,timeout is %d, wake up is :%d\n",period,timeout,wakeup);
+ if(timeout > 0){
+ mutex_lock(&pdata->data_lock);
+ cancel_delayed_work_sync(&pdata->work);
+ pdata->delay = period;
+ mutex_unlock(&pdata->data_lock);
+ mma8x5x_fifo_setting(pdata,timeout,0); //no overwirte fifo
+ mma8x5x_fifo_interrupt(client,1);
+ pdata->fifo_timeout = timeout;
+ pdata->fifo_wakeup = wakeup;
+ }else{
+ mma8x5x_fifo_setting(pdata,timeout,0); //no overwirte fifo
+ mma8x5x_fifo_interrupt(client,0);
+ pdata->fifo_timeout = timeout;
+ pdata->fifo_wakeup = wakeup;
+ mutex_lock(&pdata->data_lock);
+ pdata->delay = period;
+ if(pdata->active == MMA_ACTIVED)
+ mma8x5x_work(pdata);
+ mutex_unlock(&pdata->data_lock);
+ }
+ return count;
+}
+
+static ssize_t mma8x5x_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ int position = 0;
+ mutex_lock(&pdata->data_lock);
+ position = pdata->position;
+ mutex_unlock(&pdata->data_lock);
+ return sprintf(buf, "%d\n", position);
+}
+
+static ssize_t mma8x5x_position_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mma8x5x_data *pdata = dev_get_drvdata(dev);
+ int position;
+ position = simple_strtoul(buf, NULL, 10);
+ mutex_lock(&pdata->data_lock);
+ pdata->position = position;
+ mutex_unlock(&pdata->data_lock);
+ return count;
+}
+
+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
+ mma8x5x_enable_show, mma8x5x_enable_store);
+static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO,
+ mma8x5x_delay_show, mma8x5x_delay_store);
+
+static DEVICE_ATTR(fifo, S_IWUSR | S_IRUGO,
+ mma8x5x_fifo_show, mma8x5x_fifo_store);
+static DEVICE_ATTR(position, S_IWUSR | S_IRUGO,
+ mma8x5x_position_show, mma8x5x_position_store);
+
+static struct attribute *mma8x5x_attributes[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_poll_delay.attr,
+ &dev_attr_fifo.attr,
+ &dev_attr_position.attr,
+ NULL
+};
+
+static const struct attribute_group mma8x5x_attr_group = {
+ .attrs = mma8x5x_attributes,
+};
+static int mma8x5x_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int chip_id;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+ chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I);
+ if (!mma8x5x_check_id(chip_id))
+ return -ENODEV;
+ printk(KERN_INFO "check %s i2c address 0x%x \n",
+ mma8x5x_id2name(chip_id), client->addr);
+ strlcpy(info->type, "mma8x5x", I2C_NAME_SIZE);
+ return 0;
+}
+static int mma8x5x_open(struct inode *inode, struct file *file){
+ int err;
+ err = nonseekable_open(inode, file);
+ if (err)
+ return err;
+ file->private_data = p_mma8x5x_data;
+ return 0;
+}
+static ssize_t mma8x5x_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){
+ struct mma8x5x_data *pdata = file->private_data;
+ int ret = 0;
+ if (!(file->f_flags & O_NONBLOCK)) {
+ ret = wait_event_interruptible(pdata->fifo_wq, (atomic_read(&pdata->fifo_ready) != 0));
+ if (ret)
+ return ret;
+ }
+ if (!atomic_read(&pdata->fifo_ready))
+ return -ENODEV;
+ if(size < sizeof(struct mma8x5x_fifo)){
+ printk(KERN_ERR "the buffer leght less than need\n");
+ return -ENOMEM;
+ }
+ if(!copy_to_user(buf,&pdata->fifo,sizeof(struct mma8x5x_fifo))){
+ atomic_set(&pdata->fifo_ready,0);
+ return size;
+ }
+ return -ENOMEM ;
+}
+static unsigned int mma8x5x_poll(struct file * file, struct poll_table_struct * wait){
+ struct mma8x5x_data *pdata = file->private_data;
+ poll_wait(file, &pdata->fifo_wq, wait);
+ if (atomic_read(&pdata->fifo_ready))
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static const struct file_operations mma8x5x_fops = {
+ .owner = THIS_MODULE,
+ .open = mma8x5x_open,
+ .read = mma8x5x_read,
+ .poll = mma8x5x_poll,
+};
+
+static struct miscdevice mma8x5x_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mma8x5x",
+ .fops = &mma8x5x_fops,
+};
+
+static int mma8x5x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int result, chip_id;
+ struct input_dev *idev;
+ struct mma8x5x_data *pdata;
+ struct i2c_adapter *adapter;
+ struct device_node *of_node = client->dev.of_node;
+ u32 pos = 0;
+ adapter = to_i2c_adapter(client->dev.parent);
+ result = i2c_check_functionality(adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!result)
+ goto err_out;
+
+ printk(KERN_ERR"%s here 7", __func__);
+ chip_id = i2c_smbus_read_byte_data(client, MMA8X5X_WHO_AM_I);
+
+ if (!mma8x5x_check_id(chip_id)) {
+ dev_err(&client->dev,
+ "read chip ID 0x%x is not equal to 0x%x,0x%x,0x%x,0x%x,0x%x!\n",
+ chip_id, MMA8451_ID, MMA8452_ID,
+ MMA8453_ID, MMA8652_ID, MMA8653_ID);
+ result = -EINVAL;
+ goto err_out;
+ }
+ pdata = kzalloc(sizeof(struct mma8x5x_data), GFP_KERNEL);
+ if (!pdata) {
+ result = -ENOMEM;
+ dev_err(&client->dev, "alloc data memory error!\n");
+ goto err_out;
+ }
+
+ printk(KERN_ERR"%s here 6", __func__);
+ /* Initialize the MMA8X5X chip */
+ memset(pdata,0,sizeof(struct mma8x5x_data));
+ pdata->client = client;
+ pdata->chip_id = chip_id;
+ pdata->mode = MODE_2G;
+ pdata->fifo_wakeup = 0;
+ pdata->fifo_timeout = 0;
+
+ printk(KERN_ERR"%s here 0", __func__);
+ result = of_property_read_u32(of_node, "position",&pos );
+
+ printk(KERN_ERR"%s here result=%d pos=%d", __func__, result, pos);
+ if (result)
+ pos = 1;
+ pdata->position = (int)pos;
+ p_mma8x5x_data = pdata;
+ mutex_init(&pdata->data_lock);
+ i2c_set_clientdata(client, pdata);
+
+ printk(KERN_ERR"%s here 12", __func__);
+ mma8x5x_device_init(client);
+
+ printk(KERN_ERR"%s here 13", __func__);
+ idev = input_allocate_device();
+ if (!idev) {
+ result = -ENOMEM;
+ dev_err(&client->dev, "alloc input device failed!\n");
+ goto err_alloc_input_device;
+ }
+ printk(KERN_ERR"%s here 1", __func__);
+ idev->name = "FreescaleAccelerometer";
+ idev->uniq = mma8x5x_id2name(pdata->chip_id);
+ idev->id.bustype = BUS_I2C;
+ idev->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(idev, ABS_X, -0x7fff, 0x7fff, 0, 0);
+ input_set_abs_params(idev, ABS_Y, -0x7fff, 0x7fff, 0, 0);
+ input_set_abs_params(idev, ABS_Z, -0x7fff, 0x7fff, 0, 0);
+ dev_set_drvdata(&idev->dev,pdata);
+ pdata->idev= idev ;
+ printk(KERN_ERR"%s here 2", __func__);
+ result = input_register_device(pdata->idev);
+ if (result) {
+ dev_err(&client->dev, "register input device failed!\n");
+ goto err_register_input_device;
+ }
+ pdata->delay = POLL_INTERVAL;
+ INIT_DELAYED_WORK(&pdata->work, mma8x5x_dev_poll);
+ result = sysfs_create_group(&idev->dev.kobj, &mma8x5x_attr_group);
+ if (result) {
+ dev_err(&client->dev, "create device file failed!\n");
+ result = -EINVAL;
+ goto err_create_sysfs;
+ }
+ init_waitqueue_head(&pdata->fifo_wq);
+ if(client->irq){
+ printk(KERN_ERR"%s here 3", __func__);
+ result= request_threaded_irq(client->irq,NULL, mma8x5x_irq_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->dev.driver->name, pdata);
+ if (result < 0) {
+ dev_err(&client->dev, "failed to register MMA8x5x irq %d!\n",
+ client->irq);
+ goto err_register_irq;
+ }else{
+ result = misc_register(&mma8x5x_dev);
+ if (result) {
+ dev_err(&client->dev,"register fifo device error\n");
+ goto err_reigster_dev;
+ }
+ }
+ }
+ printk(KERN_INFO"mma8x5x device driver probe successfully\n");
+ return 0;
+err_reigster_dev:
+ free_irq(client->irq,pdata);
+err_register_irq:
+ sysfs_remove_group(&idev->dev.kobj,&mma8x5x_attr_group);
+err_create_sysfs:
+ input_unregister_device(pdata->idev);
+err_register_input_device:
+ input_free_device(idev);
+err_alloc_input_device:
+ kfree(pdata);
+err_out:
+ return result;
+}
+static int mma8x5x_remove(struct i2c_client *client)
+{
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+ struct input_dev *idev = pdata->idev;
+ mma8x5x_device_stop(client);
+ if (pdata) {
+ sysfs_remove_group(&idev->dev.kobj,&mma8x5x_attr_group);
+ input_unregister_device(pdata->idev);
+ input_free_device(pdata->idev);
+ kfree(pdata);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mma8x5x_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+ if(pdata->fifo_timeout <= 0){
+ if (pdata->active == MMA_ACTIVED)
+ mma8x5x_device_stop(client);
+ }else{
+ if (pdata->active == MMA_ACTIVED){
+ if (pdata->fifo_wakeup){
+ mma8x5x_fifo_setting(pdata,10000,0); //10s timeout , overwrite
+ mma8x5x_fifo_interrupt(client,1);
+ }else{
+ mma8x5x_fifo_interrupt(client,0);
+ mma8x5x_fifo_setting(pdata,10000,1); //10s timeout , overwrite
+ }
+ }
+ }
+ return 0;
+}
+
+static int mma8x5x_resume(struct device *dev)
+{
+ int val = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8x5x_data *pdata = i2c_get_clientdata(client);
+ if(pdata->fifo_timeout <= 0){
+ if (pdata->active == MMA_ACTIVED) {
+ val = i2c_smbus_read_byte_data(client, MMA8X5X_CTRL_REG1);
+ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val | 0x01);
+ }
+ }else{
+ if (pdata->active == MMA_ACTIVED) {
+ mma8x5x_fifo_interrupt(client,1);
+ pdata->awaken = 1; //awake from suspend
+ }
+ }
+ return 0;
+
+}
+#endif
+
+static const struct i2c_device_id mma8x5x_id[] = {
+ { "mma8x5x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mma8x5x_id);
+
+static SIMPLE_DEV_PM_OPS(mma8x5x_pm_ops, mma8x5x_suspend, mma8x5x_resume);
+static struct i2c_driver mma8x5x_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "mma8x5x",
+ .owner = THIS_MODULE,
+ .pm = &mma8x5x_pm_ops,
+ },
+ .probe = mma8x5x_probe,
+ .remove = mma8x5x_remove,
+ .id_table = mma8x5x_id,
+ .detect = mma8x5x_detect,
+ .address_list = normal_i2c,
+};
+
+
+module_i2c_driver(mma8x5x_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA8X5X 3-Axis Orientation/Motion Detection Sensor driver");
+MODULE_LICENSE("GPL");
+