/* * mma8x5x.c - Linux kernel modules for 3-Axis Orientation/Motion * Detection Sensor MMA8451/MMA8452/MMA8453/MMA8652/MMA8653 * * Copyright (C) 2012-2015 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; s64 period_rel; int fifo_wakeup; int fifo_timeout; u32 int_pin; }; static struct mma8x5x_data *p_mma8x5x_data; /* 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); /*Standby*/ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val & (~0x01)); 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); /*Standby*/ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, val); 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); /*standby*/ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, (sys_mode & (~0x01))); 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); /*standby*/ i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, (sys_mode & (~0x01))); 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); /*route to pin 1*/ if (pdata->int_pin == 1) i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG5, pin_cfg | (0x01 << 6)); i2c_smbus_write_byte_data(client, MMA8X5X_CTRL_REG1, sys_mode); if (time_out > 0) { /*fifo len is 32*/ pdata->period_rel = mma8x5x_odr_set(client, time_out/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); /*FIFO overflow*/ if (val & (0x01 << 7)) { 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); } /*is just awken from suspend*/ if (pdata->awaken) { /*10s timeout*/ mma8x5x_fifo_setting(pdata, pdata->fifo_timeout, 0); 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; ret = strict_strtol(buf, 10, &enable); if (ret) { dev_err(dev, "string to long error\n"); return ret; } 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; /*continuous mode*/ if (pdata->fifo_timeout <= 0) mma8x5x_work(pdata); else { /*fifo mode*/ mma8x5x_fifo_setting(pdata, pdata->fifo_timeout, 0); 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); 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 ret; long delay; ret = strict_strtol(buf, 10, &delay); if (ret) { dev_err(dev, "string to long error\n"); return ret; } mutex_lock(&pdata->data_lock); cancel_delayed_work_sync(&pdata->work); pdata->delay = (int)delay; if (pdata->active == MMA_ACTIVED && pdata->fifo_timeout <= 0) { mma8x5x_odr_set(client, (int)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); /*is the interrupt enable*/ count += sprintf(&buf[count], "interrupt wake up: %s\n", (pdata->fifo_wakeup ? "yes" : "no")); 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(KERN_INFO"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); /*no overwirte fifo*/ mma8x5x_fifo_setting(pdata, timeout, 0); mma8x5x_fifo_interrupt(client, 1); pdata->fifo_timeout = timeout; pdata->fifo_wakeup = wakeup; } else { /*no overwirte fifo*/ mma8x5x_fifo_setting(pdata, timeout, 0); 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 ret; long position; ret = strict_strtol(buf, 10, &position); if (ret) { dev_err(dev, "string to long error\n"); return ret; } mutex_lock(&pdata->data_lock); pdata->position = (int)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; struct regulator *vdd, *vdd_io; u32 irq_flag; struct irq_data *irq_data; vdd = devm_regulator_get(&client->dev, "vdd"); if (!IS_ERR(vdd)) { result = regulator_enable(vdd); if (result) { dev_err(&client->dev, "vdd set voltage error\n"); return result; } } vdd_io = devm_regulator_get(&client->dev, "vddio"); if (!IS_ERR(vdd_io)) { result = regulator_enable(vdd_io); if (result) { dev_err(&client->dev, "vddio set voltage error\n"); return result; } } 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; 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; } /* 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; result = of_property_read_u32(of_node, "position", &pos); if (result) pos = 1; pdata->position = (int)pos; p_mma8x5x_data = pdata; mutex_init(&pdata->data_lock); i2c_set_clientdata(client, pdata); mma8x5x_device_init(client); idev = input_allocate_device(); if (!idev) { result = -ENOMEM; dev_err(&client->dev, "alloc input device failed!\n"); goto err_alloc_input_device; } 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; 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) { irq_data = irq_get_irq_data(client->irq); irq_flag = irqd_get_trigger_type(irq_data); irq_flag |= IRQF_ONESHOT; result = request_threaded_irq(client->irq, NULL, mma8x5x_irq_handler, irq_flag, 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; } } result = of_property_read_u32(of_node, "interrupt-route", &pdata->int_pin); if (result) { result = -EINVAL; dev_err(&client->dev, "Can't find interrupt-pin value\n"); goto err_reigster_dev; } if (pdata->int_pin == 0 || pdata->int_pin > 2) { result = -EINVAL; dev_err(&client->dev, "The interrupt-pin value is invalid\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) { /*10s timeout , overwrite*/ mma8x5x_fifo_setting(pdata, 10000, 0); mma8x5x_fifo_interrupt(client, 1); } else { mma8x5x_fifo_interrupt(client, 0); /*10s timeout , overwrite*/ mma8x5x_fifo_setting(pdata, 10000, 1); } } } 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); /*Awake from suspend*/ pdata->awaken = 1; } } return 0; } #endif static const struct i2c_device_id mma8x5x_id[] = { {"mma8451", 0}, {"mma8452", 0}, {"mma8453", 0}, {"mma8652", 0}, {"mma8653", 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");