Dear all, I'd like to submit the invensense mpu6050/mpu9150 driver, a reduced version of driver code yet still pretty big. This is the first file and biggest file. Welcome any comments. Thanks. Best regards, Ge GAO +++ inv_mpu_core.c 2012-05-15 10:33:36.752543619 -0700 @@ -0,0 +1,2211 @@ +/* +* Copyright (C) 2012 Invensense, Inc. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* 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. +* +*/ + +/** + * @addtogroup DRIVERS + * @brief Hardware drivers. + * + * @{ + * @file inv_gyro.c + * @brief A sysfs device driver for Invensense devices + * @details This driver currently works for the MPU6050, MPU9150 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/jiffies.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kfifo.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include "inv_mpu_iio.h" +#include "../../sysfs.h" +static void inv_setup_reg(struct inv_reg_map_s *reg) +{ + reg->who_am_i = 0x75; + reg->sample_rate_div = 0x19; + reg->lpf = 0x1A; + reg->product_id = 0x0C; + reg->bank_sel = 0x6D; + reg->user_ctrl = 0x6A; + reg->fifo_en = 0x23; + reg->gyro_config = 0x1B; + reg->accl_config = 0x1C; + reg->fifo_count_h = 0x72; + reg->fifo_r_w = 0x74; + reg->raw_gyro = 0x43; + reg->raw_accl = 0x3B; + reg->temperature = 0x41; + reg->int_enable = 0x38; + reg->int_status = 0x3A; + reg->pwr_mgmt_1 = 0x6B; + reg->pwr_mgmt_2 = 0x6C; + reg->mem_start_addr = 0x6E; + reg->mem_r_w = 0x6F; + reg->prgm_strt_addrh = 0x70; +}; +static const struct inv_hw_s hw_info[INV_NUM_PARTS] = { + {117, "MPU6050"}, + {118, "MPU9150"} +}; +/** + * inv_i2c_read() - Read one or more bytes from the device registers. + * @st: Device driver instance. + * @reg: First device register to be read from. + * @length: Number of bytes to read. + * @data: Data read from device. + * NOTE: The slave register will not increment when reading from the FIFO. + */ +int inv_i2c_read_base(struct inv_gyro_state_s *st, unsigned short i2c_addr, + unsigned char reg, unsigned short length, unsigned char *data) +{ + struct i2c_msg msgs[2]; + int res; + + if (!data) + return -EINVAL; + + msgs[0].addr = i2c_addr; + msgs[0].flags = 0; /* write */ + msgs[0].buf = ® + msgs[0].len = 1; + + msgs[1].addr = i2c_addr; + msgs[1].flags = I2C_M_RD; + msgs[1].buf = data; + msgs[1].len = length; + + res = i2c_transfer(st->sl_handle, msgs, 2); + if (res < 2) { + if (res >= 0) + res = -EIO; + return res; + } else + return 0; +} + +/** + * inv_i2c_single_write() - Write a byte to a device register. + * @st: Device driver instance. + * @reg: Device register to be written to. + * @data: Byte to write to device. + */ +int inv_i2c_single_write_base(struct inv_gyro_state_s *st, + unsigned short i2c_addr, unsigned char reg, unsigned char data) +{ + unsigned char tmp[2]; + struct i2c_msg msg; + int res; + + tmp[0] = reg; + tmp[1] = data; + + msg.addr = i2c_addr; + msg.flags = 0; /* write */ + msg.buf = tmp; + msg.len = 2; + + /*printk(KERN_ERR "WS%02X%02X%02X\n", i2c_addr, reg, data);*/ + res = i2c_transfer(st->sl_handle, &msg, 1); + if (res < 1) { + if (res == 0) + res = -EIO; + return res; + } else + return 0; +} +int inv_set_power_state(struct inv_gyro_state_s *st, + unsigned char power_on) +{ + struct inv_reg_map_s *reg; + unsigned char data; + int result; + + reg = &st->reg; + if (power_on) + data = 0; + else + data = BIT_SLEEP; + if (st->chip_config.lpa_mode) + data |= BIT_CYCLE; + if (st->chip_config.gyro_enable) { + result = inv_i2c_single_write(st, + reg->pwr_mgmt_1, data | INV_CLK_PLL); + if (result) + return result; + st->chip_config.clk_src = INV_CLK_PLL; + } else { + result = inv_i2c_single_write(st, + reg->pwr_mgmt_1, data | INV_CLK_INTERNAL); + if (result) + return result; + st->chip_config.clk_src = INV_CLK_INTERNAL; + } + + if (power_on) { + msleep(POWER_UP_TIME); + data = 0; + if (0 == st->chip_config.accl_enable) + data |= BIT_PWR_ACCL_STBY; + if (0 == st->chip_config.gyro_enable) + data |= BIT_PWR_GYRO_STBY; + data |= (st->chip_config.lpa_freq << LPA_FREQ_SHIFT); + + result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data); + if (result) + return result; + msleep(POWER_UP_TIME); + st->chip_config.is_asleep = 0; + } else + st->chip_config.is_asleep = 1; + return 0; +} + +/** + * inv_init_config() - Initialize hardware, disable FIFO. + * @indio_dev: Device driver instance. + * Initial configuration: + * FSR: +/- 2000DPS + * DLPF: 42Hz + * FIFO rate: 50Hz + * Clock source: Gyro PLL + */ +static int inv_init_config(struct iio_dev *indio_dev) +{ + struct inv_reg_map_s *reg; + int result; + struct inv_gyro_state_s *st = iio_priv(indio_dev); + + if (st->chip_config.is_asleep) + return -EPERM; + reg = &st->reg; + result = set_inv_enable(indio_dev, 0); + if (result) + return result; + + result = inv_i2c_single_write(st, reg->gyro_config, + INV_FSR_2000DPS << GYRO_CONFIG_FSR_SHIFT); + if (result) + return result; + st->chip_config.fsr = INV_FSR_2000DPS; + + result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ); + if (result) + return result; + st->chip_config.lpf = INV_FILTER_42HZ; + + result = inv_i2c_single_write(st, reg->sample_rate_div, + ONE_K_HZ/INIT_FIFO_RATE - 1); + if (result) + return result; + st->chip_config.fifo_rate = INIT_FIFO_RATE; + st->irq_dur_us = INIT_DUR_TIME; + st->chip_config.enable = 0; + st->chip_config.dmp_on = 0; + st->compass_divider = 0; + st->compass_counter = 0; + st->self_test_run_once = 0; + st->chip_config.compass_enable = 0; + st->chip_config.firmware_loaded = 0; + st->chip_config.prog_start_addr = DMP_START_ADDR; + st->chip_config.gyro_enable = 1; + st->chip_config.gyro_fifo_enable = 1; + st->chip_config.accl_enable = 1; + st->chip_config.accl_fifo_enable = 1; + st->chip_config.accl_fs = INV_FS_02G; + result = inv_i2c_single_write(st, reg->accl_config, + (INV_FS_02G << ACCL_CONFIG_FSR_SHIFT)); + if (result) + return result; + st->tap.on = 0; + st->tap.time = INIT_TAP_TIME; + st->tap.thresh = INIT_TAP_THRESHOLD; + st->tap.min_count = INIT_TAP_MIN_COUNT; + return 0; +} +/** + * inv_compass_scale_show() - show compass scale. + */ +static int inv_compass_scale_show(struct inv_gyro_state_s *st, int *scale) +{ + if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) + *scale = DATA_AKM8975_SCALE; + else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) + *scale = DATA_AKM8972_SCALE; + else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) + if (st->compass_scale) + *scale = DATA_AKM8963_SCALE1; + else + *scale = DATA_AKM8963_SCALE0; + else + return -EINVAL; + *scale *= (1L << 15); + return IIO_VAL_INT; +} + +/** + * mpu_read_raw() - read raw method. + */ +static int mpu_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) { + struct inv_gyro_state_s *st = iio_priv(indio_dev); + int result; + if (st->chip_config.is_asleep) + return -EINVAL; + switch (mask) { + case 0: + if (chan->type == IIO_ANGL_VEL) { + *val = st->raw_gyro[chan->channel2 - IIO_MOD_X]; + return IIO_VAL_INT; + } + if (chan->type == IIO_ACCEL) { + *val = st->raw_accel[chan->channel2 - IIO_MOD_X]; + return IIO_VAL_INT; + } + if (chan->type == IIO_MAGN) { + *val = st->raw_compass[chan->channel2 - IIO_MOD_X]; + return IIO_VAL_INT; + } + return -EINVAL; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_ANGL_VEL) { + *val = (1 << st->chip_config.fsr)*GYRO_DPS_SCALE; + return IIO_VAL_INT; + } + if (chan->type == IIO_ACCEL) { + *val = (2 << st->chip_config.accl_fs); + return IIO_VAL_INT; + } + if (chan->type == IIO_MAGN) + return inv_compass_scale_show(st, val); + return -EINVAL; + case IIO_CHAN_INFO_CALIBBIAS: + if (st->self_test_run_once == 0) { + result = inv_do_test(st, 0, st->gyro_bias, + st->accel_bias); + if (result) + return result; + st->self_test_run_once = 1; + } + + if (chan->type == IIO_ANGL_VEL) { + *val = st->gyro_bias[chan->channel2 - IIO_MOD_X]; + return IIO_VAL_INT; + } + if (chan->type == IIO_ACCEL) { + *val = st->accel_bias[chan->channel2 - IIO_MOD_X]; + return IIO_VAL_INT; + } + return -EINVAL; + default: + return -EINVAL; + } +} + +/** + * inv_write_fsr() - Configure the gyro's scale range. + */ +static int inv_write_fsr(struct inv_gyro_state_s *st, int fsr) +{ + struct inv_reg_map_s *reg; + int result; + reg = &st->reg; + if ((fsr < 0) || (fsr > MAX_GYRO_FS_PARAM)) + return -EINVAL; + if (fsr == st->chip_config.fsr) + return 0; + + result = inv_i2c_single_write(st, reg->gyro_config, + fsr << GYRO_CONFIG_FSR_SHIFT); + if (result) + return result; + st->chip_config.fsr = fsr; + return 0; +} + +/** + * inv_write_accel_fs() - Configure the accelerometer's scale range. + */ +static int inv_write_accel_fs(struct inv_gyro_state_s *st, int fs) +{ + int result; + struct inv_reg_map_s *reg; + reg = &st->reg; + + if (fs < 0 || fs > MAX_ACCL_FS_PARAM) + return -EINVAL; + if (fs == st->chip_config.accl_fs) + return 0; + result = inv_i2c_single_write(st, reg->accl_config, + (fs << ACCL_CONFIG_FSR_SHIFT)); + if (result) + return result; + /* reset fifo because the data could be mixed with old bad data */ + st->chip_config.accl_fs = fs; + return 0; +} +/** + * inv_write_compass_scale() - Configure the compass's scale range. + */ +static int inv_write_compass_scale(struct inv_gyro_state_s *st, int data) +{ + char d, en; + int result; + if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id) + return 0; + if (data) + en = 1; + else + en = 0; + if (st->compass_scale == en) + return 0; + d = (1 | (st->compass_scale << AKM8963_SCALE_SHIFT)); + result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, d); + if (result) + return result; + st->compass_scale = en; + return 0; + +} + +/** + * mpu_write_raw() - write raw method. + */ +static int mpu_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) { + struct inv_gyro_state_s *st = iio_priv(indio_dev); + int result; + if (st->chip_config.is_asleep) + return -EPERM; + switch (mask) { + case IIO_CHAN_INFO_SCALE: + result = -EINVAL; + if (chan->type == IIO_ANGL_VEL) + result = inv_write_fsr(st, val); + if (chan->type == IIO_ACCEL) + result = inv_write_accel_fs(st, val); + if (chan->type == IIO_MAGN) + result = inv_write_compass_scale(st, val); + return result; + default: + return -EINVAL; + } + return 0; +} + +/** + * inv_set_lpf() - set low pass filer based on fifo rate. + */ +static int inv_set_lpf(struct inv_gyro_state_s *st, int rate) +{ + const short hz[] = {188, 98, 42, 20, 10, 5}; + const int d[] = {INV_FILTER_188HZ, INV_FILTER_98HZ, + INV_FILTER_42HZ, INV_FILTER_20HZ, + INV_FILTER_10HZ, INV_FILTER_5HZ}; + int i, h, data, result; + struct inv_reg_map_s *reg; + reg = &st->reg; + h = (rate >> 1); + i = 0; + while ((h < hz[i]) && (i < ARRAY_SIZE(d))) + i++; + if (i == ARRAY_SIZE(d)) + i -= 1; + data = d[i]; + result = inv_i2c_single_write(st, reg->lpf, data); + if (result) + return result; + st->chip_config.lpf = data; + return 0; +} + +/** + * inv_fifo_rate_store() - Set fifo rate. + */ +static ssize_t inv_fifo_rate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long fifo_rate; + unsigned char data; + int result; + struct inv_gyro_state_s *st; + struct inv_reg_map_s *reg; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + reg = &st->reg; + + if (st->chip_config.is_asleep) + return -EPERM; + if (kstrtoul(buf, 10, &fifo_rate)) + return -EINVAL; + if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE)) + return -EINVAL; + if (fifo_rate == st->chip_config.fifo_rate) + return count; + if (st->has_compass) { + data = COMPASS_RATE_SCALE*fifo_rate/ONE_K_HZ; + if (data > 0) + data -= 1; + st->compass_divider = data; + st->compass_counter = 0; + /* I2C_MST_DLY is set according to sample rate, + AKM cannot be read or set at sample rate higher than 100Hz*/ + result = inv_i2c_single_write(st, REG_I2C_SLV4_CTRL, data); + if (result) + return result; + } + data = ONE_K_HZ / fifo_rate - 1; + result = inv_i2c_single_write(st, reg->sample_rate_div, data); + if (result) + return result; + st->chip_config.fifo_rate = fifo_rate; + result = inv_set_lpf(st, fifo_rate); + if (result) + return result; + st->irq_dur_us = (data + 1) * ONE_K_HZ; + st->last_isr_time = iio_get_time_ns(); + return count; +} +/** + * inv_fifo_rate_show() - Get the current sampling rate. + */ +static ssize_t inv_fifo_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->chip_config.fifo_rate); +} + +/** + * inv_power_state_store() - Turn device on/off. + */ +static ssize_t inv_power_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int result; + unsigned long power_state; + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + if (kstrtoul(buf, 10, &power_state)) + return -EINVAL; + if (!power_state == st->chip_config.is_asleep) + return count; + result = inv_set_power_state(st, power_state); + return count; +} + +/** + * inv_power_state_show() - Check if the device is on or in sleep mode. + */ +static ssize_t inv_power_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return sprintf(buf, "0\n"); + else + return sprintf(buf, "1\n"); +} + +/** + * inv_firmware_loaded_store() - calling this function will change + * firmware load + */ +static ssize_t inv_firmware_loaded_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned long data, result; + result = kstrtoul(buf, 10, &data); + if (result) + return result; + if (data != 0) + return -EINVAL; + st->chip_config.firmware_loaded = 0; + st->chip_config.dmp_on = 0; + return count; +} +/** + * inv_firmware_loaded_show() - calling this function will show current + * firmware load status + */ +static ssize_t inv_firmware_loaded_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->chip_config.firmware_loaded); +} + +/** + * inv_lpa_mode_store() - store current low power settings + */ +static ssize_t inv_lpa_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned long result, lpa_mode; + unsigned char d; + struct inv_reg_map_s *reg; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, &lpa_mode); + if (result) + return result; + + reg = &st->reg; + result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d); + if (result) + return result; + d &= ~BIT_CYCLE; + if (lpa_mode) + d |= BIT_CYCLE; + result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d); + if (result) + return result; + st->chip_config.lpa_mode = lpa_mode; + return count; +} +/** + * inv_lpa_mode_show() - show current low power settings + */ +static ssize_t inv_lpa_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->chip_config.lpa_mode); +} + +/** + * inv_lpa_freq_store() - store current low power frequency setting. + */ +static ssize_t inv_lpa_freq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned long result, lpa_freq; + unsigned char d; + struct inv_reg_map_s *reg; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, &lpa_freq); + if (result) + return result; + if (lpa_freq > MAX_LPA_FREQ_PARAM) + return -EINVAL; + reg = &st->reg; + result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &d); + if (result) + return result; + d &= ~BIT_LPA_FREQ; + d |= (unsigned char)(lpa_freq << LPA_FREQ_SHIFT); + result = inv_i2c_single_write(st, reg->pwr_mgmt_2, d); + if (result) + return result; + st->chip_config.lpa_freq = lpa_freq; + return count; +} +/** + * inv_lpa_freq_show() - show current low power frequency setting + */ +static ssize_t inv_lpa_freq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + switch (st->chip_config.lpa_freq) { + case 0: + return sprintf(buf, "1.25\n"); + case 1: + return sprintf(buf, "5\n"); + case 2: + return sprintf(buf, "20\n"); + case 3: + return sprintf(buf, "40\n"); + default: + return sprintf(buf, "0\n"); + } +} +/** + * inv_dmp_on_store() - calling this function will store current dmp on + */ +static ssize_t inv_dmp_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int result, data; + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + + if (st->chip_config.is_asleep) + return -EPERM; + if (0 == st->chip_config.firmware_loaded) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + if (data) + st->chip_config.dmp_on = 1; + else + st->chip_config.dmp_on = 0; + + return count; +} +/** + * inv_dmp_on_show() - calling this function will show current dmp_on + */ +static ssize_t inv_dmp_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->chip_config.dmp_on); +} +/** + * inv_orientation_on_store() - calling this function will store + * current orientation on + */ +static ssize_t inv_orientation_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int result, data, en; + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + + if (st->chip_config.is_asleep) + return -EPERM; + if (0 == st->chip_config.firmware_loaded) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + if (data) + en = 1; + else + en = 0; + result = inv_enable_orientation_dmp(st, en); + if (result) + return result; + st->chip_config.orientation_on = en; + return count; +} +/** + * inv_orientation_on_show() - calling this function will show + * current orientation_on + */ +static ssize_t inv_orientation_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->chip_config.orientation_on); +} + +/** + * inv_tap_on_store() - calling this function will store current tap on + */ +static ssize_t inv_tap_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int result, data; + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + if (0 == st->chip_config.firmware_loaded) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + if (data) + st->tap.on = 1; + else + st->tap.on = 0; + result = inv_enable_tap_dmp(st, st->tap.on); + return count; +} +/** + * inv_tap_on_show() - calling this function will show current tap_on + */ +static ssize_t inv_tap_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->tap.on); +} +/** + * inv_tap_time_store() - calling this function will store current tap time + */ +static ssize_t inv_tap_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int result, data; + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + if (0 == st->chip_config.firmware_loaded) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + result = inv_set_tap_time_dmp(st, data); + if (result) + return result; + st->tap.time = data; + return count; +} +/** + * inv_tap_time_show() - calling this function will show current tap time + */ +static ssize_t inv_tap_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->tap.time); +} + +/** + * inv_tap_min_count_store() - calling this function will store tap count + */ +static ssize_t inv_tap_min_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int result, data; + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + if (0 == st->chip_config.firmware_loaded) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + result = inv_set_min_taps_dmp(st, data); + if (result) + return result; + st->tap.min_count = data; + return count; +} +/** + * inv_tap_min_count_show() - calling this function show minimum count + */ +static ssize_t inv_tap_min_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->tap.min_count); +} + +/** + * inv_tap_threshold_store() - calling this function will store tap threshold + */ +static ssize_t inv_tap_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int result, data; + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + if (0 == st->chip_config.firmware_loaded) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_X, data); + if (result) + return result; + result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Y, data); + if (result) + return result; + result = inv_set_tap_threshold_dmp(st, INV_TAP_AXIS_Z, data); + if (result) + return result; + + st->tap.thresh = data; + return count; +} +/** + * inv_tap_thresh_show() - calling this function show current tap threshold + */ +static ssize_t inv_tap_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->tap.thresh); +} +/** + * inv_clk_src_show() - Show the device's clock source. + */ +static ssize_t inv_clk_src_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + + switch (st->chip_config.clk_src) { + case INV_CLK_INTERNAL: + return sprintf(buf, "INTERNAL\n"); + case INV_CLK_PLL: + return sprintf(buf, "Gyro PLL\n"); + default: + return -EPERM; + } +} +/** + * inv_reg_dump_show() - Register dump for testing. + * TODO: Only for testing. + */ +static ssize_t inv_reg_dump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ii; + char data; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + ssize_t bytes_printed = 0; + struct inv_gyro_state_s *st = iio_priv(indio_dev); + + for (ii = 0; ii < st->hw->num_reg; ii++) { + inv_i2c_read(st, ii, 1, &data); + bytes_printed += sprintf(buf+bytes_printed, "%#2x: %#2x\n", + ii, data); + } + return bytes_printed; +} + +/** + * inv_self_test_show() - self test result. 0 for fail; 1 for success. + * calling this function will trigger self test + * and return test result. + */ +static ssize_t inv_self_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int result; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + result = inv_hw_self_test(st); + return sprintf(buf, "%d\n", result); +} +/** + * inv_key_show() - calling this function will show the key + * + */ +static ssize_t inv_key_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + unsigned char *key; + struct inv_gyro_state_s *st = iio_priv(indio_dev); + key = st->plat_data.key; + return sprintf(buf, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + key[0], key[1], key[2], key[3], + key[4], key[5], key[6], key[7], + key[8], key[9], key[10], key[11], + key[12], key[13], key[14], key[15]); +} +/** + * inv_gyro_matrix_show() - show orientation matrix + */ +static ssize_t inv_gyro_matrix_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + signed char *m; + struct inv_gyro_state_s *st = iio_priv(indio_dev); + m = st->plat_data.orientation; + return sprintf(buf, + "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +} +/** + * inv_accl_matrix_show() - show orientation matrix + */ +static ssize_t inv_accl_matrix_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + signed char *m; + struct inv_gyro_state_s *st = iio_priv(indio_dev); + if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_ACCEL) + m = st->plat_data.secondary_orientation; + else + m = st->plat_data.orientation; + return sprintf(buf, + "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +} +/** + * inv_compass_matrix_show() - show orientation matrix + */ +static ssize_t inv_compass_matrix_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + signed char *m; + struct inv_gyro_state_s *st = iio_priv(indio_dev); + if (st->plat_data.sec_slave_type == SECONDARY_SLAVE_TYPE_COMPASS) + m = st->plat_data.secondary_orientation; + else + return -1; + return sprintf(buf, + "%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); +} + +/** + * inv_flick_lower_store() - calling this function will store current + * flick lower bound + */ +static ssize_t inv_flick_lower_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + int result, data, out; + unsigned char *p; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtol(buf, 10, (long unsigned int *)&data); + if (result) + return result; + out = cpu_to_be32p(&data); + p = (unsigned char *)&out; + + result = mem_w_key(KEY_FLICK_LOWER, 4, p); + if (result) + return result; + st->flick.lower = data; + return count; +} + +/** + * inv_flick_lower_show() - calling this function will show current + * flick lower bound + */ +static ssize_t inv_flick_lower_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->flick.lower); +} +/** + * inv_flick_upper_store() - calling this function will store current + * flick upper bound + */ +static ssize_t inv_flick_upper_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned int result, data, out; + unsigned char *p; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + out = cpu_to_be32p(&data); + p = (unsigned char *)&out; + result = mem_w_key(KEY_FLICK_UPPER, 4, p); + if (result) + return result; + st->flick.upper = data; + return count; +} + +/** + * inv_flick_upper_show() - calling this function will show current + * flick upper bound + */ +static ssize_t inv_flick_upper_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->flick.upper); +} +/** + * inv_flick_counter_store() - calling this function will store current + * flick counter value + */ +static ssize_t inv_flick_counter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned int result, data, out; + unsigned char *p; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + out = cpu_to_be32p(&data); + p = (unsigned char *)&out; + result = mem_w_key(KEY_FLICK_COUNTER, 4, p); + if (result) + return result; + st->flick.counter = data; + + return count; +} + +/** + * inv_flick_counter_show() - calling this function will show current + * flick counter value + */ +static ssize_t inv_flick_counter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->flick.counter); +} + +/** + * inv_flick_int_on_store() - calling this function will store current + * flick interrupt on value + */ +static ssize_t inv_flick_int_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long result, data; + unsigned char d[4]; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, &data); + if (result) + return result; + if (data) + /* Use interrupt to signal when gesture was observed */ + d[0] = DIND40+4; + else + d[0] = DINAA0+8; + result = mem_w_key(KEY_CGNOTICE_INTR, 1, d); + if (result) + return result; + st->flick.int_on = data; + return count; +} + +/** + * inv_flick_int_on_show() - calling this function will show current + * flick interrupt on value + */ +static ssize_t inv_flick_int_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->flick.int_on); +} +/** + * inv_flick_axis_store() - calling this function will store current + * flick axis value + */ +static ssize_t inv_flick_axis_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long result, data; + unsigned char d[4]; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, &data); + if (result) + return result; + + if (data == 0) + d[0] = DINBC2; + else if (data == 2) + d[2] = DINBC6; + else + d[0] = DINBC4; + result = mem_w_key(KEY_CFG_FLICK_IN, 1, d); + if (result) + return result; + st->flick.axis = data; + + return count; +} + +/** + * inv_flick_axis_show() - calling this function will show current + * flick axis value + */ +static ssize_t inv_flick_axis_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->flick.axis); +} +/** + * inv_flick_msg_on_store() - calling this function will store current + * flick message on value + */ +static ssize_t inv_flick_msg_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned int result, data, out; + unsigned char *p; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + if (data) + data = DATA_MSG_ON; + out = cpu_to_be32p(&data); + p = (unsigned char *)&out; + result = mem_w_key(KEY_FLICK_MSG, 4, p); + if (result) + return result; + st->flick.msg_on = data; + + return count; +} + +/** + * inv_flick_msg_on_show() - calling this function will show current + * flick message on value + */ +static ssize_t inv_flick_msg_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->flick.msg_on); +} + +/** + * inv_pedometer_steps_store() - calling this function will store current + * pedometer steps into MPU memory + */ +static ssize_t inv_pedometer_steps_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned int result, data, out; + unsigned char *p; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + + out = cpu_to_be32p(&data); + p = (unsigned char *)&out; + result = mem_w_key(KEY_D_PEDSTD_STEPCTR, 4, p); + if (result) + return result; + + return count; +} + +/** + * inv_pedometer_steps_show() - calling this function will store current + * pedometer steps into MPU memory + */ +static ssize_t inv_pedometer_steps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int result, data; + unsigned char d[4]; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + result = mpu_memory_read(st->sl_handle, st->i2c_addr, + inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), 4, d); + if (result) + return result; + data = be32_to_cpup((int *)d); + return sprintf(buf, "%d\n", data); +} +/** + * inv_pedometer_time_store() - calling this function will store current + * pedometer time into MPU memory + */ +static ssize_t inv_pedometer_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + unsigned int result, data, out; + unsigned char *p; + if (st->chip_config.is_asleep) + return -EPERM; + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; + + out = cpu_to_be32p(&data); + p = (unsigned char *)&out; + result = mem_w_key(KEY_D_PEDSTD_TIMECTR, 4, p); + if (result) + return result; + return count; +} +/** + * inv_pedometer_time_show() - calling this function will store current + * pedometer steps into MPU memory + */ +static ssize_t inv_pedometer_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int result, data; + unsigned char d[4]; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + if (st->chip_config.is_asleep) + return -EPERM; + result = mpu_memory_read(st->sl_handle, st->i2c_addr, + inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), 4, d); + if (result) + return result; + data = be32_to_cpup((int *)d); + return sprintf(buf, "%d\n", data); +} + +/** + * inv_dmp_flick_show() - calling this function will show flick event. + * This event must use poll. + */ +static ssize_t inv_dmp_flick_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "1\n"); +} +/** + * inv_dmp_orient_show() - calling this function will show orientation + * This event must use poll. + */ +static ssize_t inv_dmp_orient_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->orient_data); +} +/** + * inv_dmp_tap_show() - calling this function will show tap + * This event must use poll. + */ +static ssize_t inv_dmp_tap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->tap_data); +} +/** + * inv_temperature_show() - Read temperature data directly from registers. + */ +static ssize_t inv_temperature_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + struct inv_reg_map_s *reg; + int result; + short temp; + long scale_t; + unsigned char data[2]; + reg = &st->reg; + + if (st->chip_config.is_asleep) + return -EPERM; + result = inv_i2c_read(st, reg->temperature, 2, data); + if (result) { + printk(KERN_ERR "Could not read temperature register.\n"); + return result; + } + temp = (signed short)(be16_to_cpup((short *)&data[0])); + + scale_t = MPU6050_TEMP_OFFSET + + inv_q30_mult((long)temp << MPU_TEMP_SHIFT, + MPU6050_TEMP_SCALE); + return sprintf(buf, "%ld %lld\n", scale_t, iio_get_time_ns()); +} +static int inv_switch_gyro_engine(struct inv_gyro_state_s *st, int en) +{ + struct inv_reg_map_s *reg; + unsigned char data; + int result; + reg = &st->reg; + + result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data); + if (result) + return result; + if (en) + data &= (~BIT_PWR_GYRO_STBY); + else + data |= BIT_PWR_GYRO_STBY; + result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data); + if (result) + return result; + msleep(SENSOR_UP_TIME); + + if (en) + st->chip_config.clk_src = INV_CLK_PLL; + else + st->chip_config.clk_src = INV_CLK_INTERNAL; + + return 0; +} +static int inv_switch_accl_engine(struct inv_gyro_state_s *st, int en) +{ + struct inv_reg_map_s *reg; + unsigned char data; + int result; + reg = &st->reg; + result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &data); + if (result) + return result; + if (en) + data &= (~BIT_PWR_ACCL_STBY); + else + data |= BIT_PWR_ACCL_STBY; + result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data); + if (result) + return result; + msleep(SENSOR_UP_TIME); + return 0; +} + +/** + * inv_gyro_enable_store() - Enable/disable gyro. + */ +static ssize_t inv_gyro_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long data, en; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + struct iio_buffer *ring = indio_dev->buffer; + int result; + + if (st->chip_config.is_asleep) + return -EPERM; + if (st->chip_config.enable) + return -EPERM; + + result = kstrtoul(buf, 10, &data); + if (result) + return -EINVAL; + if (data) + en = 1; + else + en = 0; + if (en == st->chip_config.gyro_enable) + return count; + result = inv_switch_gyro_engine(st, en); + if (result) + return result; + + if (0 == en) { + st->chip_config.gyro_fifo_enable = 0; + clear_bit(INV_MPU_SCAN_GYRO_X, ring->scan_mask); + clear_bit(INV_MPU_SCAN_GYRO_Y, ring->scan_mask); + clear_bit(INV_MPU_SCAN_GYRO_Z, ring->scan_mask); + } + st->chip_config.gyro_enable = en; + return count; +} +/** + * inv_gyro_enable_show() - Check if the FIFO and ring buffer are enabled. + */ +static ssize_t inv_gyro_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->chip_config.gyro_enable); +} + +/** + * inv_accl_enable_store() - Enable/disable accl. + */ +static ssize_t inv_accl_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long en, data; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + struct iio_buffer *ring = indio_dev->buffer; + int result; + + if (st->chip_config.is_asleep) + return -EPERM; + if (st->chip_config.enable) + return -EPERM; + result = kstrtoul(buf, 10, &data); + if (result) + return -EINVAL; + if (data) + en = 1; + else + en = 0; + if (en == st->chip_config.accl_enable) + return count; + result = inv_switch_accl_engine(st, en); + if (result) + return result; + st->chip_config.accl_enable = en; + if (0 == en) { + st->chip_config.accl_fifo_enable = 0; + clear_bit(INV_MPU_SCAN_ACCL_X, ring->scan_mask); + clear_bit(INV_MPU_SCAN_ACCL_Y, ring->scan_mask); + clear_bit(INV_MPU_SCAN_ACCL_Z, ring->scan_mask); + } + return count; +} +/** + * inv_accl_enable_show() - Check if the FIFO and ring buffer are enabled. + */ +static ssize_t inv_accl_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + return sprintf(buf, "%d\n", st->chip_config.accl_enable); +} + +/** + * inv_compass_en_store() - calling this function will store compass + * enable + */ +static ssize_t inv_compass_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long data, result, en; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + struct iio_buffer *ring = indio_dev->buffer; + if (st->chip_config.is_asleep) + return -EPERM; + if (st->chip_config.enable) + return -EPERM; + result = kstrtoul(buf, 10, &data); + if (result) + return result; + if (data) + en = 1; + else + en = 0; + if (en == st->chip_config.compass_enable) + return count; + st->chip_config.compass_enable = en; + if (0 == en) { + st->chip_config.compass_fifo_enable = 0; + clear_bit(INV_MPU_SCAN_MAGN_X, ring->scan_mask); + clear_bit(INV_MPU_SCAN_MAGN_Y, ring->scan_mask); + clear_bit(INV_MPU_SCAN_MAGN_Z, ring->scan_mask); + } + + return count; +} +/** + * inv_compass_en_show() - calling this function will show compass + * enable status + */ +static ssize_t inv_compass_en_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", st->chip_config.compass_enable); +} + +static const struct iio_chan_spec gyro_accel_channels[] = { + { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_GYRO_X, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_GYRO_Y, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_GYRO_Z, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_ACCL_X, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_ACCL_Y, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_ACCL_Z, + .scan_type = IIO_ST('s', 16, 16, 0) + }, + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP) +}; +static const struct iio_chan_spec gyro_accel_compass_channels[] = { + { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_GYRO_X, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_GYRO_Y, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ANGL_VEL, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_GYRO_Z, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_ACCL_X, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_ACCL_Y, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_ACCEL, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT | + IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_ACCL_Z, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_MAGN, + .modified = 1, + .channel2 = IIO_MOD_X, + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_MAGN_X, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_MAGN, + .modified = 1, + .channel2 = IIO_MOD_Y, + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_MAGN_Y, + .scan_type = IIO_ST('s', 16, 16, 0) + }, { + .type = IIO_MAGN, + .modified = 1, + .channel2 = IIO_MOD_Z, + .info_mask = IIO_CHAN_INFO_SCALE_SHARED_BIT, + .scan_index = INV_MPU_SCAN_MAGN_Z, + .scan_type = IIO_ST('s', 16, 16, 0) + }, + + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU_SCAN_TIMESTAMP) +}; + +static struct inv_chip_chan_info chip_channel_info[] = { + { + .channels = gyro_accel_channels, + .num_channels = ARRAY_SIZE(gyro_accel_channels), + .default_scan_mask = (1 << INV_MPU_SCAN_GYRO_X) | + (1 << INV_MPU_SCAN_GYRO_Y) | (1 << INV_MPU_SCAN_GYRO_Z) | + (1 << INV_MPU_SCAN_ACCL_X) | (1 << INV_MPU_SCAN_ACCL_Y) | + (1 << INV_MPU_SCAN_ACCL_Z) | (1 << INV_MPU_SCAN_TIMESTAMP) + }, + { + .channels = gyro_accel_compass_channels, + .num_channels = ARRAY_SIZE(gyro_accel_compass_channels), + .default_scan_mask = (1 << INV_MPU_SCAN_GYRO_X) | + (1 << INV_MPU_SCAN_GYRO_Y) | (1 << INV_MPU_SCAN_GYRO_Z) | + (1 << INV_MPU_SCAN_ACCL_X) | (1 << INV_MPU_SCAN_ACCL_Y) | + (1 << INV_MPU_SCAN_ACCL_Z) | (1 << INV_MPU_SCAN_MAGN_X) | + (1 << INV_MPU_SCAN_MAGN_Y) | (1 << INV_MPU_SCAN_MAGN_Z) | + (1 << INV_MPU_SCAN_TIMESTAMP) + } +}; + +/*constant IIO attribute */ +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 50 100 200 500"); +static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, inv_fifo_rate_show, + inv_fifo_rate_store); +static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL); +static DEVICE_ATTR(clock_source, S_IRUGO, inv_clk_src_show, NULL); +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, inv_power_state_show, + inv_power_state_store); +static DEVICE_ATTR(firmware_loaded, S_IRUGO | S_IWUSR, + inv_firmware_loaded_show, inv_firmware_loaded_store); +static DEVICE_ATTR(lpa_mode, S_IRUGO | S_IWUSR, inv_lpa_mode_show, + inv_lpa_mode_store); +static DEVICE_ATTR(lpa_freq, S_IRUGO | S_IWUSR, inv_lpa_freq_show, + inv_lpa_freq_store); +static DEVICE_ATTR(reg_dump, S_IRUGO, inv_reg_dump_show, NULL); +static DEVICE_ATTR(self_test, S_IRUGO, inv_self_test_show, NULL); +static DEVICE_ATTR(key, S_IRUGO, inv_key_show, NULL); +static DEVICE_ATTR(gyro_matrix, S_IRUGO, inv_gyro_matrix_show, NULL); +static DEVICE_ATTR(accl_matrix, S_IRUGO, inv_accl_matrix_show, NULL); +static DEVICE_ATTR(compass_matrix, S_IRUGO, inv_compass_matrix_show, NULL); +static DEVICE_ATTR(flick_lower, S_IRUGO | S_IWUSR, inv_flick_lower_show, + inv_flick_lower_store); +static DEVICE_ATTR(flick_upper, S_IRUGO | S_IWUSR, inv_flick_upper_show, + inv_flick_upper_store); +static DEVICE_ATTR(flick_counter, S_IRUGO | S_IWUSR, inv_flick_counter_show, + inv_flick_counter_store); +static DEVICE_ATTR(flick_message_on, S_IRUGO | S_IWUSR, inv_flick_msg_on_show, + inv_flick_msg_on_store); +static DEVICE_ATTR(flick_int_on, S_IRUGO | S_IWUSR, inv_flick_int_on_show, + inv_flick_int_on_store); +static DEVICE_ATTR(flick_axis, S_IRUGO | S_IWUSR, inv_flick_axis_show, + inv_flick_axis_store); +static DEVICE_ATTR(dmp_on, S_IRUGO | S_IWUSR, inv_dmp_on_show, + inv_dmp_on_store); +static DEVICE_ATTR(orientation_on, S_IRUGO | S_IWUSR, + inv_orientation_on_show, inv_orientation_on_store); + +static DEVICE_ATTR(tap_on, S_IRUGO | S_IWUSR, inv_tap_on_show, + inv_tap_on_store); +static DEVICE_ATTR(tap_time, S_IRUGO | S_IWUSR, inv_tap_time_show, + inv_tap_time_store); +static DEVICE_ATTR(tap_min_count, S_IRUGO | S_IWUSR, inv_tap_min_count_show, + inv_tap_min_count_store); +static DEVICE_ATTR(tap_threshold, S_IRUGO | S_IWUSR, inv_tap_threshold_show, + inv_tap_threshold_store); +static DEVICE_ATTR(pedometer_time, S_IRUGO | S_IWUSR, inv_pedometer_time_show, + inv_pedometer_time_store); +static DEVICE_ATTR(pedometer_steps, S_IRUGO | S_IWUSR, + inv_pedometer_steps_show, inv_pedometer_steps_store); +static DEVICE_ATTR(event_flick, S_IRUGO, inv_dmp_flick_show, NULL); +static DEVICE_ATTR(event_orientation, S_IRUGO, inv_dmp_orient_show, NULL); +static DEVICE_ATTR(event_tap, S_IRUGO, inv_dmp_tap_show, NULL); +static DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_gyro_enable_show, + inv_gyro_enable_store); +static DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_accl_enable_show, + inv_accl_enable_store); +static DEVICE_ATTR(compass_enable, S_IRUGO | S_IWUSR, inv_compass_en_show, + inv_compass_en_store); + +static const struct attribute *inv_mpu6050_attributes[] = { + &dev_attr_gyro_enable.attr, + &dev_attr_temperature.attr, + &dev_attr_clock_source.attr, + &dev_attr_power_state.attr, + &dev_attr_reg_dump.attr, + &dev_attr_self_test.attr, + &dev_attr_key.attr, + &dev_attr_gyro_matrix.attr, + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &dev_attr_accl_enable.attr, + &dev_attr_accl_matrix.attr, + &dev_attr_firmware_loaded.attr, + &dev_attr_lpa_mode.attr, + &dev_attr_lpa_freq.attr, + &dev_attr_flick_lower.attr, + &dev_attr_flick_upper.attr, + &dev_attr_flick_counter.attr, + &dev_attr_flick_message_on.attr, + &dev_attr_flick_int_on.attr, + &dev_attr_flick_axis.attr, + &dev_attr_dmp_on.attr, + &dev_attr_orientation_on.attr, + &dev_attr_tap_on.attr, + &dev_attr_tap_time.attr, + &dev_attr_tap_min_count.attr, + &dev_attr_tap_threshold.attr, + &dev_attr_pedometer_time.attr, + &dev_attr_pedometer_steps.attr, + &dev_attr_event_flick.attr, + &dev_attr_event_orientation.attr, + &dev_attr_event_tap.attr, +}; + +static const struct attribute *inv_compass_attributes[] = { + &dev_attr_compass_matrix.attr, + &dev_attr_compass_enable.attr, +}; + +static struct attribute *inv_attributes[ARRAY_SIZE(inv_mpu6050_attributes) + + ARRAY_SIZE(inv_compass_attributes) + 1]; +static const struct attribute_group inv_attribute_group = { + .name = "mpu", + .attrs = inv_attributes +}; + +static const struct iio_info mpu_info = { + .driver_module = THIS_MODULE, + .read_raw = &mpu_read_raw, + .write_raw = &mpu_write_raw, + .attrs = &inv_attribute_group, +}; + +/** + * inv_setup_compass() - Configure compass. + */ +static int inv_setup_compass(struct inv_gyro_state_s *st) +{ + int result; + unsigned char data[4]; + + result = inv_i2c_read(st, REG_YGOFFS_TC, 1, data); + if (result) + return result; + data[0] &= ~BIT_I2C_MST_VDDIO; + if (st->plat_data.level_shifter) + data[0] |= BIT_I2C_MST_VDDIO; + /*set up VDDIO register */ + result = inv_i2c_single_write(st, REG_YGOFFS_TC, data[0]); + if (result) + return result; + /* set to bypass mode */ + result = inv_i2c_single_write(st, REG_INT_PIN_CFG, BIT_BYPASS_EN); + if (result) + return result; + /*read secondary i2c ID register */ + result = inv_secondary_read(REG_AKM_ID, 1, data); + if (result) + return result; + if (data[0] != DATA_AKM_ID) + return -ENXIO; + /*set AKM to Fuse ROM access mode */ + result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_FR); + if (result) + return result; + result = inv_secondary_read(REG_AKM_SENSITIVITY, THREE_AXIS, + st->chip_info.compass_sens); + if (result) + return result; + /*revert to power down mode */ + result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_DN); + if (result) + return result; + pr_err("senx=%d, seny=%d,senz=%d\n", + st->chip_info.compass_sens[0], + st->chip_info.compass_sens[1], + st->chip_info.compass_sens[2]); + /*restore to non-bypass mode */ + result = inv_i2c_single_write(st, REG_INT_PIN_CFG, 0); + if (result) + return result; + + /*setup master mode and master clock and ES bit*/ + result = inv_i2c_single_write(st, REG_I2C_MST_CTRL, BIT_WAIT_FOR_ES); + if (result) + return result; + /* slave 0 is used to read data from compass */ + /*read mode */ + result = inv_i2c_single_write(st, REG_I2C_SLV0_ADDR, BIT_I2C_READ| + st->plat_data.secondary_i2c_addr); + if (result) + return result; + /* AKM status register address is 2 */ + result = inv_i2c_single_write(st, REG_I2C_SLV0_REG, REG_AKM_STATUS); + if (result) + return result; + /* slave 0 is enabled at the beginning, read 8 bytes from here */ + result = inv_i2c_single_write(st, REG_I2C_SLV0_CTRL, BIT_SLV_EN | + NUM_BYTES_COMPASS_SLAVE); + if (result) + return result; + /*slave 1 is used for AKM mode change only*/ + result = inv_i2c_single_write(st, REG_I2C_SLV1_ADDR, + st->plat_data.secondary_i2c_addr); + if (result) + return result; + /* AKM mode register address is 0x0A */ + result = inv_i2c_single_write(st, REG_I2C_SLV1_REG, REG_AKM_MODE); + if (result) + return result; + /* slave 1 is enabled, byte length is 1 */ + result = inv_i2c_single_write(st, REG_I2C_SLV1_CTRL, BIT_SLV_EN | 1); + if (result) + return result; + /* output data for slave 1 is fixed, single measure mode*/ + st->compass_scale = 1; + data[0] = 1; + if (COMPASS_ID_AK8975 == st->plat_data.sec_slave_id) { + st->compass_st_upper[0] = DATA_AKM8975_ST_X_UP; + st->compass_st_upper[1] = DATA_AKM8975_ST_Y_UP; + st->compass_st_upper[2] = DATA_AKM8975_ST_Z_UP; + st->compass_st_lower[0] = DATA_AKM8975_ST_X_LW; + st->compass_st_lower[1] = DATA_AKM8975_ST_Y_LW; + st->compass_st_lower[2] = DATA_AKM8975_ST_Z_LW; + } else if (COMPASS_ID_AK8972 == st->plat_data.sec_slave_id) { + st->compass_st_upper[0] = DATA_AKM8972_ST_X_UP; + st->compass_st_upper[1] = DATA_AKM8972_ST_Y_UP; + st->compass_st_upper[2] = DATA_AKM8972_ST_Z_UP; + st->compass_st_lower[0] = DATA_AKM8972_ST_X_LW; + st->compass_st_lower[1] = DATA_AKM8972_ST_Y_LW; + st->compass_st_lower[2] = DATA_AKM8972_ST_Z_LW; + } else if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) { + st->compass_st_upper[0] = DATA_AKM8963_ST_X_UP; + st->compass_st_upper[1] = DATA_AKM8963_ST_Y_UP; + st->compass_st_upper[2] = DATA_AKM8963_ST_Z_UP; + st->compass_st_lower[0] = DATA_AKM8963_ST_X_LW; + st->compass_st_lower[1] = DATA_AKM8963_ST_Y_LW; + st->compass_st_lower[2] = DATA_AKM8963_ST_Z_LW; + data[0] |= (st->compass_scale << AKM8963_SCALE_SHIFT); + } + result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, data[0]); + if (result) + return result; + /* slave 0 and 1 timer action is enabled every sample*/ + result = inv_i2c_single_write(st, REG_I2C_MST_DELAY_CTRL, + BIT_SLV0_DLY_EN | BIT_SLV1_DLY_EN); + return result; +} + +/** + * inv_check_chip_type() - check and setup chip type. + */ +static int inv_check_chip_type(struct inv_gyro_state_s *st, + const struct i2c_device_id *id) +{ + struct inv_reg_map_s *reg; + int result, chan_index; + int t_ind; + if (!strcmp(id->name, "mpu6050")) + st->chip_type = INV_MPU6050; + else if (!strcmp(id->name, "mpu9150")) + st->chip_type = INV_MPU9150; + else + return -EPERM; + st->hw = (struct inv_hw_s *)(hw_info + st->chip_type); + st->mpu_slave = NULL; + chan_index = CHAN_INDEX_GYRO_ACCL; + if (INV_MPU9150 == st->chip_type) { + st->plat_data.sec_slave_type = SECONDARY_SLAVE_TYPE_COMPASS; + st->plat_data.sec_slave_id = COMPASS_ID_AK8975; + st->has_compass = 1; + chan_index = CHAN_INDEX_GYRO_ACCL_MAGN; + } + if (SECONDARY_SLAVE_TYPE_COMPASS == st->plat_data.sec_slave_type) + st->has_compass = 1; + else + st->has_compass = 0; + if (INV_MPU6050 == st->chip_type) { + if (st->has_compass) + chan_index = CHAN_INDEX_GYRO_ACCL_MAGN; + else + chan_index = CHAN_INDEX_GYRO_ACCL; + } + st->chan_info = &chip_channel_info[chan_index]; + reg = &st->reg; + inv_setup_reg(reg); + st->chip_config.gyro_enable = 1; + result = inv_set_power_state(st, 1); + if (result) + return result; + + result = inv_get_silicon_rev_mpu6050(st); + if (result) { + inv_i2c_single_write(st, reg->pwr_mgmt_1, + BIT_SLEEP | INV_CLK_PLL); + return result; + } + if (st->has_compass) { + result = inv_setup_compass(st); + if (result) { + inv_i2c_single_write(st, reg->pwr_mgmt_1, + BIT_SLEEP | INV_CLK_PLL); + return result; + } + } + + t_ind = 0; + memcpy(&inv_attributes[t_ind], inv_mpu6050_attributes, + sizeof(inv_mpu6050_attributes)); + t_ind += ARRAY_SIZE(inv_mpu6050_attributes); + + if (chan_index > CHAN_INDEX_GYRO_ACCL) { + memcpy(&inv_attributes[t_ind], inv_compass_attributes, + sizeof(inv_compass_attributes)); + t_ind += ARRAY_SIZE(inv_compass_attributes); + } + inv_attributes[t_ind] = NULL; + return 0; +} + +/** + * inv_create_dmp_sysfs() - create binary sysfs dmp entry. + */ +static const struct bin_attribute dmp_firmware = { + .attr = { + .name = "dmp_firmware", + .mode = S_IRUGO | S_IWUSR + }, + .size = 4096, + .read = inv_dmp_firmware_read, + .write = inv_dmp_firmware_write, +}; + +static int inv_create_dmp_sysfs(struct iio_dev *ind) +{ + int result; + result = sysfs_create_bin_file(&ind->dev.kobj, &dmp_firmware); + return result; +} + +/** + * inv_mpu_probe() - probe function. + */ +static int inv_mpu_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct inv_gyro_state_s *st; + struct iio_dev *indio_dev; + int result, reg_done; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + result = -ENODEV; + goto out_no_free; + } + indio_dev = iio_allocate_device(sizeof(*st)); + if (indio_dev == NULL) { + result = -ENOMEM; + goto out_no_free; + } + reg_done = 0; + st = iio_priv(indio_dev); + st->i2c = client; + st->sl_handle = client->adapter; + st->i2c_addr = client->addr; + st->plat_data = + *(struct mpu_platform_data *)dev_get_platdata(&client->dev); + /* power is turned on inside check chip type*/ + result = inv_check_chip_type(st, id); + if (result) + goto out_free; + result = inv_init_config(indio_dev); + if (result) { + dev_err(&client->adapter->dev, + "Could not initialize device.\n"); + goto out_free; + } + result = inv_set_power_state(st, 1); + if (result) { + dev_err(&client->adapter->dev, + "%s could not be turned off.\n", st->hw->name); + goto out_free; + } + + /* Make state variables available to all _show and _store functions. */ + i2c_set_clientdata(client, indio_dev); + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->channels = st->chan_info->channels; + indio_dev->num_channels = st->chan_info->num_channels; + indio_dev->info = &mpu_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->currentmode = INDIO_DIRECT_MODE; + + result = inv_mpu_configure_ring(indio_dev); + if (result) + goto out_free; + result = iio_buffer_register(indio_dev, st->chan_info->channels, + st->chan_info->num_channels); + if (result) + goto out_unreg_ring; + st->irq = client->irq; + result = inv_mpu_probe_trigger(indio_dev); + if (result) + goto out_remove_ring; + + result = iio_device_register(indio_dev); + if (result) + goto out_remove_trigger; + result = inv_create_dmp_sysfs(indio_dev); + if (result) + goto out_unreg_iio; + + INIT_KFIFO(st->timestamps); + spin_lock_init(&st->time_stamp_lock); + pr_info("%s: Probe name %s\n", __func__, id->name); + dev_info(&client->adapter->dev, "%s is ready to go!\n", st->hw->name); + return 0; +out_unreg_iio: + iio_device_unregister(indio_dev); +out_remove_trigger: + if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) + inv_mpu_remove_trigger(indio_dev); +out_remove_ring: + iio_buffer_unregister(indio_dev); +out_unreg_ring: + inv_mpu_unconfigure_ring(indio_dev); +out_free: + iio_free_device(indio_dev); +out_no_free: + dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); + return -EIO; +} + +/** + * inv_mpu_remove() - remove function. + */ +static int inv_mpu_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct inv_gyro_state_s *st = iio_priv(indio_dev); + kfifo_free(&st->timestamps); + iio_device_unregister(indio_dev); + inv_mpu_remove_trigger(indio_dev); + iio_buffer_unregister(indio_dev); + inv_mpu_unconfigure_ring(indio_dev); + iio_free_device(indio_dev); + + dev_info(&client->adapter->dev, "inv-mpu module removed.\n"); + return 0; +} +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; +/* device id table is used to identify what device can be + * supported by this driver + */ +static const struct i2c_device_id inv_mpu_id[] = { + {"mpu6050", INV_MPU6050}, + {"mpu9150", INV_MPU9150}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, inv_mpu_id); + +static struct i2c_driver inv_mpu_driver = { + .class = I2C_CLASS_HWMON, + .probe = inv_mpu_probe, + .remove = inv_mpu_remove, + .id_table = inv_mpu_id, + .driver = { + .owner = THIS_MODULE, + .name = "inv-mpu6050-iio", + }, + .address_list = normal_i2c, +}; + +static int __init inv_mpu_init(void) +{ + int result = i2c_add_driver(&inv_mpu_driver); + if (result) { + pr_err("%s failed\n", __func__); + return result; + } + return 0; +} + +static void __exit inv_mpu_exit(void) +{ + i2c_del_driver(&inv_mpu_driver); +} + +module_init(inv_mpu_init); +module_exit(inv_mpu_exit); + +MODULE_AUTHOR("Invensense Corporation"); +MODULE_DESCRIPTION("Invensense device driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("inv-mpu6050-iio"); +/** + * @} + */ + -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html