diff options
-rw-r--r-- | drivers/input/misc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/compass/ak8975_input.c | 1619 | ||||
-rw-r--r-- | drivers/input/misc/mpu/inv_gyro.c | 4210 | ||||
-rw-r--r-- | drivers/input/misc/mpu/inv_gyro.h | 272 | ||||
-rw-r--r-- | drivers/input/misc/mpu/inv_gyro_misc.c | 129 | ||||
-rw-r--r-- | drivers/input/misc/mpu/inv_mpu3050.c | 756 | ||||
-rw-r--r-- | drivers/input/misc/pressure/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/misc/pressure/Makefile | 7 | ||||
-rw-r--r-- | drivers/input/misc/pressure/bmp180.c | 1080 | ||||
-rw-r--r-- | include/linux/mpu.h | 298 |
11 files changed, 5621 insertions, 2764 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 85844a2ce7d7..c2a302983440 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -645,5 +645,6 @@ config INPUT_CAPELLA_CM3218 source "drivers/input/misc/compass/Kconfig" source "drivers/input/misc/mpu/Kconfig" +source "drivers/input/misc/pressure/Kconfig" endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index b1086162e95f..a4e6d7d636e3 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -63,3 +63,4 @@ obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-y += compass/ obj-y += mpu/ +obj-y += pressure/ diff --git a/drivers/input/misc/compass/ak8975_input.c b/drivers/input/misc/compass/ak8975_input.c index 29754bc25046..e89455434d73 100644 --- a/drivers/input/misc/compass/ak8975_input.c +++ b/drivers/input/misc/compass/ak8975_input.c @@ -1,670 +1,1215 @@ -/* -* 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. +/* Copyright (C) 2012 Invensense, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * - * @{ - * @file ak8975_input.c - * @brief A sysfs device driver for Invensense devices - * @details This driver currently works for the AK8975 + * 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. */ #include <linux/i2c.h> +#include <linux/input.h> #include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/kernel.h> -#include <linux/errno.h> +#include <linux/miscdevice.h> #include <linux/slab.h> -#include <linux/module.h> #include <linux/delay.h> -#include <linux/input.h> #include <linux/workqueue.h> - +#include <linux/regulator/consumer.h> #include <linux/mpu.h> -#define AK8975_DEBUG_IF 0 -#define AK8975_DEBUG_DATA 0 - -#define AK8975_I2C_NAME "ak8975" - -#define SENSOR_DATA_SIZE 8 -#define YPR_DATA_SIZE 12 -#define RWBUF_SIZE 16 - -#define ACC_DATA_FLAG 0 -#define MAG_DATA_FLAG 1 -#define ORI_DATA_FLAG 2 -#define AKM_NUM_SENSORS 3 - -#define ACC_DATA_READY (1 << (ACC_DATA_FLAG)) -#define MAG_DATA_READY (1 << (MAG_DATA_FLAG)) -#define ORI_DATA_READY (1 << (ORI_DATA_FLAG)) - -/*! \name AK8975 constant definition - \anchor AK8975_Def - Constant definitions of the AK8975.*/ -#define AK8975_MEASUREMENT_TIME_US 10000 - -/*! \name AK8975 operation mode - \anchor AK8975_Mode - Defines an operation mode of the AK8975.*/ -/*! @{*/ -#define AK8975_CNTL_MODE_SNG_MEASURE 0x01 -#define AK8975_CNTL_MODE_SELF_TEST 0x08 -#define AK8975_CNTL_MODE_FUSE_ACCESS 0x0F -#define AK8975_CNTL_MODE_POWER_DOWN 0x00 -/*! @}*/ - -/*! \name AK8975 register address -\anchor AK8975_REG -Defines a register address of the AK8975.*/ -/*! @{*/ -#define AK8975_REG_WIA 0x00 -#define AK8975_REG_INFO 0x01 -#define AK8975_REG_ST1 0x02 -#define AK8975_REG_HXL 0x03 -#define AK8975_REG_HXH 0x04 -#define AK8975_REG_HYL 0x05 -#define AK8975_REG_HYH 0x06 -#define AK8975_REG_HZL 0x07 -#define AK8975_REG_HZH 0x08 -#define AK8975_REG_ST2 0x09 -#define AK8975_REG_CNTL 0x0A -#define AK8975_REG_RSV 0x0B -#define AK8975_REG_ASTC 0x0C -#define AK8975_REG_TS1 0x0D -#define AK8975_REG_TS2 0x0E -#define AK8975_REG_I2CDIS 0x0F -/*! @}*/ - -/*! \name AK8975 fuse-rom address -\anchor AK8975_FUSE -Defines a read-only address of the fuse ROM of the AK8975.*/ -/*! @{*/ -#define AK8975_FUSE_ASAX 0x10 -#define AK8975_FUSE_ASAY 0x11 -#define AK8975_FUSE_ASAZ 0x12 -/*! @}*/ - -#define AK8975_MAX_DELAY (100) -#define AK8975_MIN_DELAY (10) - -/** - * struct inv_gyro_state_s - Driver state variables. - * @dev: Represents read-only node for accessing buffered data. - * @idev: Handle to input device. - * @sl_handle: Handle to I2C port. - */ -struct inv_compass_state { + +#define AKM_NAME "akm89xx" +#define AKM_HW_DELAY_POR_MS (50) +#define AKM_HW_DELAY_TSM_MS (10) /* Time Single Measurement */ +#define AKM_HW_DELAY_US (100) +#define AKM_HW_DELAY_ROM_ACCESS_US (200) +#define AKM_POLL_DELAY_MS_DFLT (200) +#define AKM_MPU_RETRY_COUNT (20) +#define AKM_MPU_RETRY_DELAY_MS (20) +#define AKM_ERR_CNT_MAX (20) + +#define AKM_INPUT_RESOLUTION (1) +#define AKM_INPUT_DIVISOR (1) +#define AKM_INPUT_DELAY_MS_MIN (AKM_HW_DELAY_TSM_MS) +#define AKM_INPUT_POWER_UA (10000) +#define AKM_INPUT_RANGE (4912) + +#define AKM_REG_WIA (0x00) +#define AKM_WIA_ID (0x48) +#define AKM_REG_INFO (0x01) +#define AKM_REG_ST1 (0x02) +#define AKM_ST1_DRDY (0x01) +#define AKM_ST1_DOR (0x02) +#define AKM_REG_HXL (0x03) +#define AKM_REG_HXH (0x04) +#define AKM_REG_HYL (0x05) +#define AKM_REG_HYH (0x06) +#define AKM_REG_HZL (0x07) +#define AKM_REG_HZH (0x08) +#define AKM_REG_ST2 (0x09) +#define AKM_ST2_DERR (0x04) +#define AKM_ST2_HOFL (0x08) +#define AKM_ST2_BITM (0x10) +#define AKM_REG_CNTL1 (0x0A) +#define AKM_CNTL1_MODE_MASK (0x0F) +#define AKM_CNTL1_MODE_POWERDOWN (0x00) +#define AKM_CNTL1_MODE_SINGLE (0x01) +#define AKM_CNTL1_MODE_CONT1 (0x02) +#define AKM_CNTL1_MODE_CONT2 (0x06) +#define AKM_CNTL1_MODE_SELFTEST (0x08) +#define AKM_CNTL1_MODE_ROM_ACCESS (0x0F) +#define AKM_CNTL1_OUTPUT_BIT16 (0x10) +#define AKM_REG_CNTL2 (0x0B) +#define AKM_CNTL2_SRST (0x01) +#define AKM_REG_ASTC (0x0C) +#define AKM_REG_TS1 (0x0D) +#define AKM_REG_TS2 (0x0E) +#define AKM_REG_I2CDIS (0x0F) +#define AKM_REG_ASAX (0x10) +#define AKM_REG_ASAY (0x11) +#define AKM_REG_ASAZ (0x12) + +#define AKM8963_SCALE14 (19661) +#define AKM8963_SCALE16 (4915) +#define AKM8972_SCALE (19661) +#define AKM8975_SCALE (9830) +#define AKM8975_RANGE_X_HI (100) +#define AKM8975_RANGE_X_LO (-100) +#define AKM8975_RANGE_Y_HI (100) +#define AKM8975_RANGE_Y_LO (-100) +#define AKM8975_RANGE_Z_HI (-300) +#define AKM8975_RANGE_Z_LO (-1000) +#define AKM8972_RANGE_X_HI (50) +#define AKM8972_RANGE_X_LO (-50) +#define AKM8972_RANGE_Y_HI (50) +#define AKM8972_RANGE_Y_LO (-50) +#define AKM8972_RANGE_Z_HI (-100) +#define AKM8972_RANGE_Z_LO (-500) +#define AKM8963_RANGE_X_HI (200) +#define AKM8963_RANGE_X_LO (-200) +#define AKM8963_RANGE_Y_HI (200) +#define AKM8963_RANGE_Y_LO (-200) +#define AKM8963_RANGE_Z_HI (-800) +#define AKM8963_RANGE_Z_LO (-3200) +#define AXIS_X (0) +#define AXIS_Y (1) +#define AXIS_Z (2) +#define WR (0) +#define RD (1) + + +/* regulator names in order of powering on */ +static char *akm_vregs[] = { + "vdd", + "vid", +}; + + +struct akm_asa { + u8 asa[3]; /* axis sensitivity adjustment */ + s16 range_lo[3]; + s16 range_hi[3]; +}; + +struct akm_inf { struct i2c_client *i2c; - atomic_t enable; - atomic_t delay; - struct mpu_platform_data plat_data; - short i2c_addr; - void *sl_handle; - struct device *inv_dev; + struct mutex mutex_data; struct input_dev *idev; - struct delayed_work work; - - struct mutex value_mutex; - struct mutex enable_mutex; - short value[3]; - char asa[3]; /* axis sensitivity adjustment */ + struct workqueue_struct *wq; + struct delayed_work dw; + struct regulator_bulk_data vreg[ARRAY_SIZE(akm_vregs)]; + struct mpu_platform_data pdata; + struct akm_asa asa; /* data for calibration */ + unsigned int compass_id; /* compass id */ + unsigned long poll_delay_us; /* requested sampling delay (us) */ + unsigned int resolution; /* report when new data outside this */ + bool use_mpu; /* if device behind MPU */ + bool initd; /* set if initialized */ + bool enable; /* enable status */ + bool port_en[2]; /* enable status of MPU write port */ + int port_id[2]; /* MPU port ID */ + u8 data_out; /* write value to trigger a sample */ + u8 range_index; /* max_range index */ + int xyz[3]; /* sample data */ + bool cycle; /* cycle MPU en/dis for high speed */ + unsigned long cycle_delay_us; /* cycle off time (us) */ + s64 cycle_ts; /* cycle MPU disable timestamp */ }; -/* -------------------------------------------------------------------------- */ -/** - * inv_serial_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. - */ -static int inv_serial_read(struct inv_compass_state *st, - unsigned char reg, unsigned short length, unsigned char *data) -{ - struct i2c_msg msgs[2]; - int res; - if (!data || !st->sl_handle) - return -EINVAL; +static int akm_i2c_rd(struct akm_inf *inf, u8 reg, u16 len, u8 *val) +{ + struct i2c_msg msg[2]; + + msg[0].addr = inf->i2c->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ® + msg[1].addr = inf->i2c->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = val; + if (i2c_transfer(inf->i2c->adapter, msg, 2) != 2) + return -EIO; - msgs[0].addr = st->i2c_addr; - msgs[0].flags = 0; /* write */ - msgs[0].buf = ® - msgs[0].len = 1; - - msgs[1].addr = st->i2c_addr; - msgs[1].flags = I2C_M_RD; - msgs[1].buf = data; - msgs[1].len = length; - - pr_debug("%s RD%02X%02X%02X\n", - st->idev->name, st->i2c_addr, reg, length); - res = i2c_transfer(st->sl_handle, msgs, 2); - if (res < 2) { - if (res >= 0) - res = -EIO; - return res; - } else - return 0; + return 0; } -/** - * inv_serial_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. - */ -static int inv_serial_single_write(struct inv_compass_state *st, - unsigned char reg, unsigned char data) +static int akm_i2c_wr(struct akm_inf *inf, u8 reg, u8 val) { - unsigned char tmp[2]; struct i2c_msg msg; - int res; + u8 buf[2]; - if (!st->sl_handle) - return -EINVAL; + buf[0] = reg; + buf[1] = val; + msg.addr = inf->i2c->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + if (i2c_transfer(inf->i2c->adapter, &msg, 1) != 1) + return -EIO; - tmp[0] = reg; - tmp[1] = data; + return 0; +} - msg.addr = st->i2c_addr; - msg.flags = 0; /* write */ - msg.buf = tmp; - msg.len = 2; +static int akm_vreg_dis(struct akm_inf *inf, int i) +{ + int err = 0; + + if (inf->vreg[i].ret && (inf->vreg[i].consumer != NULL)) { + err = regulator_disable(inf->vreg[i].consumer); + if (err) + dev_err(&inf->i2c->dev, "%s %s ERR\n", + __func__, inf->vreg[i].supply); + else + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } + inf->vreg[i].ret = 0; + return err; +} - pr_debug("%s WS%02X%02X%02X\n", - st->idev->name, st->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; +static int akm_vreg_dis_all(struct akm_inf *inf) +{ + unsigned int i; + int err = 0; + + for (i = ARRAY_SIZE(akm_vregs); i > 0; i--) + err |= akm_vreg_dis(inf, (i - 1)); + return err; } -static int ak8975_init(struct inv_compass_state *st) +static int akm_vreg_en(struct akm_inf *inf, int i) { - int result = 0; - unsigned char serial_data[3]; + int err = 0; + + if ((!inf->vreg[i].ret) && (inf->vreg[i].consumer != NULL)) { + err = regulator_enable(inf->vreg[i].consumer); + if (!err) { + inf->vreg[i].ret = 1; + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + err = 1; /* flag regulator state change */ + } else { + dev_err(&inf->i2c->dev, "%s %s ERR\n", + __func__, inf->vreg[i].supply); + } + } + return err; +} + +static int akm_vreg_en_all(struct akm_inf *inf) +{ + int i; + int err = 0; - result = inv_serial_single_write(st, AK8975_REG_CNTL, - AK8975_CNTL_MODE_POWER_DOWN); - if (result) { - pr_err("%s, line=%d\n", __func__, __LINE__); - return result; + for (i = 0; i < ARRAY_SIZE(akm_vregs); i++) + err |= akm_vreg_en(inf, i); + return err; +} + +static void akm_vreg_exit(struct akm_inf *inf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(akm_vregs); i++) { + if (inf->vreg[i].consumer != NULL) { + devm_regulator_put(inf->vreg[i].consumer); + inf->vreg[i].consumer = NULL; + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } } - /* Wait at least 100us */ - udelay(100); - - result = inv_serial_single_write(st, AK8975_REG_CNTL, - AK8975_CNTL_MODE_FUSE_ACCESS); - if (result) { - pr_err("%s, line=%d\n", __func__, __LINE__); - return result; +} + +static int akm_vreg_init(struct akm_inf *inf) +{ + unsigned int i; + int err = 0; + + for (i = 0; i < ARRAY_SIZE(akm_vregs); i++) { + inf->vreg[i].supply = akm_vregs[i]; + inf->vreg[i].ret = 0; + inf->vreg[i].consumer = devm_regulator_get(&inf->i2c->dev, + inf->vreg[i].supply); + if (IS_ERR(inf->vreg[i].consumer)) { + err |= PTR_ERR(inf->vreg[i].consumer); + dev_err(&inf->i2c->dev, "%s err %d for %s\n", + __func__, err, inf->vreg[i].supply); + inf->vreg[i].consumer = NULL; + } else { + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } } + return err; +} - /* Wait at least 200us */ - udelay(200); +static int akm_pm(struct akm_inf *inf, bool enable) +{ + int err; - result = inv_serial_read(st, AK8975_FUSE_ASAX, 3, serial_data); - if (result) { - pr_err("%s, line=%d\n", __func__, __LINE__); - return result; + if (enable) { + err = akm_vreg_en_all(inf); + if (err) + mdelay(AKM_HW_DELAY_POR_MS); + } else { + err = akm_vreg_dis_all(inf); } + if (err > 0) + err = 0; + if (err) + dev_err(&inf->i2c->dev, "%s enable=%x ERR=%d\n", + __func__, enable, err); + else + dev_dbg(&inf->i2c->dev, "%s enable=%x\n", + __func__, enable); + return err; +} - st->asa[0] = serial_data[0]; - st->asa[1] = serial_data[1]; - st->asa[2] = serial_data[2]; +static int akm_port_free(struct akm_inf *inf, int port) +{ + int err = 0; - result = inv_serial_single_write(st, AK8975_REG_CNTL, - AK8975_CNTL_MODE_POWER_DOWN); - if (result) { - pr_err("%s, line=%d\n", __func__, __LINE__); - return result; + if ((inf->use_mpu) && (inf->port_id[port] >= 0)) { + err = nvi_mpu_port_free(inf->port_id[port]); + if (!err) + inf->port_id[port] = -1; } - udelay(100); + return err; +} - return result; +static int akm_ports_free(struct akm_inf *inf) +{ + int err; + + err = akm_port_free(inf, WR); + err |= akm_port_free(inf, RD); + return err; +} + +static void akm_pm_exit(struct akm_inf *inf) +{ + akm_ports_free(inf); + akm_pm(inf, false); + akm_vreg_exit(inf); +} + +static int akm_pm_init(struct akm_inf *inf) +{ + int err; + + inf->initd = false; + inf->enable = false; + inf->port_en[WR] = false; + inf->port_en[RD] = false; + inf->port_id[WR] = -1; + inf->port_id[RD] = -1; + inf->poll_delay_us = (AKM_POLL_DELAY_MS_DFLT * 1000); + inf->cycle_delay_us = (AKM_INPUT_DELAY_MS_MIN * 1000); + akm_vreg_init(inf); + err = akm_pm(inf, true); + return err; } -static int ak8975_read(struct inv_compass_state *st, short rawfixed[3]) +static int akm_nvi_mpu_bypass_request(struct akm_inf *inf) { - unsigned char regs[8]; - unsigned char *stat = ®s[0]; - unsigned char *stat2 = ®s[7]; - int result = 0; - int status = 0; + int i; + int err = 0; - result = inv_serial_read(st, AK8975_REG_ST1, 8, regs); - if (result) { - pr_err("%s, line=%d\n", __func__, __LINE__); - return result; + if (inf->use_mpu) { + for (i = 0; i < AKM_MPU_RETRY_COUNT; i++) { + err = nvi_mpu_bypass_request(true); + if ((!err) || (err == -EPERM)) + break; + + mdelay(AKM_MPU_RETRY_DELAY_MS); + } + if (err == -EPERM) + err = 0; } + return err; +} - rawfixed[0] = (short)((regs[2]<<8) | regs[1]); - rawfixed[1] = (short)((regs[4]<<8) | regs[3]); - rawfixed[2] = (short)((regs[6]<<8) | regs[5]); +static int akm_nvi_mpu_bypass_release(struct akm_inf *inf) +{ + int err = 0; - /* - * ST : data ready - - * Measurement has been completed and data is ready to be read. - */ - if (*stat & 0x01) - status = 0; - - /* - * ST2 : data error - - * occurs when data read is started outside of a readable period; - * data read would not be correct. - * Valid in continuous measurement mode only. - * In single measurement mode this error should not occour but we - * stil account for it and return an error, since the data would be - * corrupted. - * DERR bit is self-clearing when ST2 register is read. - */ - if (*stat2 & 0x04) - status = 0x04; - /* - * ST2 : overflow - - * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT. - * This is likely to happen in presence of an external magnetic - * disturbance; it indicates, the sensor data is incorrect and should - * be ignored. - * An error is returned. - * HOFL bit clears when a new measurement starts. - */ - if (*stat2 & 0x08) - status = 0x08; - /* - * ST : overrun - - * the previous sample was not fetched and lost. - * Valid in continuous measurement mode only. - * In single measurement mode this error should not occour and we - * don't consider this condition an error. - * DOR bit is self-clearing when ST2 or any meas. data register is - * read. - */ - if (*stat & 0x02) { - /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */ - status = 0; + if (inf->use_mpu) + err = nvi_mpu_bypass_release(); + return err; +} + +static int akm_wr(struct akm_inf *inf, u8 reg, u8 val) +{ + int err = 0; + + err = akm_nvi_mpu_bypass_request(inf); + if (!err) { + err = akm_i2c_wr(inf, AKM_REG_CNTL1, AKM_CNTL1_MODE_POWERDOWN); + udelay(AKM_HW_DELAY_US); + err |= akm_i2c_wr(inf, reg, val); + akm_nvi_mpu_bypass_release(inf); } + return err; +} - /* - * trigger next measurement if: - * - stat is non zero; - * - if stat is zero and stat2 is non zero. - * Won't trigger if data is not ready and there was no error. - */ - if (*stat != 0x00 || *stat2 != 0x00) { - result = inv_serial_single_write(st, AK8975_REG_CNTL, - AK8975_CNTL_MODE_SNG_MEASURE); - if (result) { - pr_err("%s, line=%d\n", __func__, __LINE__); - return result; +static int akm_port_enable(struct akm_inf *inf, int port, bool enable) +{ + int err = 0; + + if (enable != inf->port_en[port]) { + err = nvi_mpu_enable(inf->port_id[port], enable, false); + if (!err) + inf->port_en[port] = enable; + } + return err; +} + +static int akm_ports_enable(struct akm_inf *inf, bool enable) +{ + int err; + + err = akm_port_enable(inf, RD, enable); + err |= akm_port_enable(inf, WR, enable); + return err; +} + +static int akm_mode_wr(struct akm_inf *inf, bool reset, u8 range, u8 mode) +{ + u8 mode_old; + u8 mode_new; + u8 val; + int i; + int err = 0; + + mode_new = mode; + if (range) + mode |= AKM_CNTL1_OUTPUT_BIT16; + if ((mode == inf->data_out) && (!reset)) + return err; + + mode_old = inf->data_out & AKM_CNTL1_MODE_MASK; + if (inf->use_mpu) + err = akm_ports_enable(inf, false); + else + cancel_delayed_work_sync(&inf->dw); + if (err) + return err; + + if (reset) { + if (inf->compass_id == COMPASS_ID_AK8963) { + err = akm_nvi_mpu_bypass_request(inf); + if (!err) { + err = akm_wr(inf, AKM_REG_CNTL2, + AKM_CNTL2_SRST); + for (i = 0; i < AKM_HW_DELAY_POR_MS; i++) { + mdelay(1); + err = akm_i2c_rd(inf, AKM_REG_CNTL2, + 1, &val); + if (err) + continue; + + if (!(val & AKM_CNTL2_SRST)) + break; + } + akm_nvi_mpu_bypass_release(inf); + } } } + inf->range_index = range; + inf->data_out = mode; + if (inf->use_mpu) { + if ((mode_old > AKM_CNTL1_MODE_SINGLE) || + (mode_new > AKM_CNTL1_MODE_SINGLE)) + err = akm_wr(inf, AKM_REG_CNTL1, mode); + if (mode_new <= AKM_CNTL1_MODE_SINGLE) { + err |= nvi_mpu_data_out(inf->port_id[WR], mode); + if (mode_new) + err |= akm_ports_enable(inf, true); + } else { + err |= akm_port_enable(inf, RD, true); + } + } else { + err = akm_wr(inf, AKM_REG_CNTL1, mode); + if (mode_new) + queue_delayed_work(inf->wq, &inf->dw, + usecs_to_jiffies(inf->poll_delay_us)); + } + return err; +} + +static int akm_delay(struct akm_inf *inf, unsigned long delay_us) +{ + u8 mode; + int err = 0; + + if (inf->use_mpu) + err |= nvi_mpu_delay_us(inf->port_id[RD], delay_us); + if (!err) { + if (inf->compass_id == COMPASS_ID_AK8963) { + if (delay_us == (AKM_INPUT_DELAY_MS_MIN * 1000)) + mode = AKM_CNTL1_MODE_CONT2; + else + mode = AKM_CNTL1_MODE_SINGLE; + err = akm_mode_wr(inf, false, inf->range_index, mode); + } else { + if ((delay_us == (AKM_INPUT_DELAY_MS_MIN * 1000)) && + inf->cycle_delay_us) + inf->cycle = true; + else + inf->cycle = false; + } + } + return err; +} + +static int akm_init_hw(struct akm_inf *inf) +{ + u8 val[8]; + int err; + int err_t; + + err_t = akm_nvi_mpu_bypass_request(inf); + if (!err_t) { + err_t = akm_wr(inf, AKM_REG_CNTL1, AKM_CNTL1_MODE_ROM_ACCESS); + udelay(AKM_HW_DELAY_ROM_ACCESS_US); + err_t |= akm_i2c_rd(inf, AKM_REG_ASAX, 3, inf->asa.asa); + /* we can autodetect AK8963 with BITM */ + inf->compass_id = 0; + err = akm_wr(inf, AKM_REG_CNTL1, (AKM_CNTL1_MODE_SINGLE | + AKM_CNTL1_OUTPUT_BIT16)); + if (!err) { + mdelay(AKM_HW_DELAY_TSM_MS); + err = akm_i2c_rd(inf, AKM_REG_ST2, 1, val); + if ((!err) && (val[0] & AKM_CNTL1_OUTPUT_BIT16)) + inf->compass_id = COMPASS_ID_AK8963; + } + akm_nvi_mpu_bypass_release(inf); + if (!inf->compass_id) + inf->compass_id = inf->pdata.sec_slave_id; + if (inf->compass_id == COMPASS_ID_AK8963) { + inf->asa.range_lo[AXIS_X] = AKM8963_RANGE_X_LO; + inf->asa.range_hi[AXIS_X] = AKM8963_RANGE_X_HI; + inf->asa.range_lo[AXIS_Y] = AKM8963_RANGE_Y_LO; + inf->asa.range_hi[AXIS_Y] = AKM8963_RANGE_Y_HI; + inf->asa.range_lo[AXIS_Z] = AKM8963_RANGE_Z_LO; + inf->asa.range_hi[AXIS_Z] = AKM8963_RANGE_Z_HI; + inf->range_index = 1; + } else if (inf->compass_id == COMPASS_ID_AK8972) { + inf->asa.range_lo[AXIS_X] = AKM8972_RANGE_X_LO; + inf->asa.range_hi[AXIS_X] = AKM8972_RANGE_X_HI; + inf->asa.range_lo[AXIS_Y] = AKM8972_RANGE_Y_LO; + inf->asa.range_hi[AXIS_Y] = AKM8972_RANGE_Y_HI; + inf->asa.range_lo[AXIS_Z] = AKM8972_RANGE_Z_LO; + inf->asa.range_hi[AXIS_Z] = AKM8972_RANGE_Z_HI; + inf->range_index = 0; + } else { /* default COMPASS_ID_AK8975 */ + inf->compass_id = COMPASS_ID_AK8975; + inf->asa.range_lo[AXIS_X] = AKM8975_RANGE_X_LO; + inf->asa.range_hi[AXIS_X] = AKM8975_RANGE_X_HI; + inf->asa.range_lo[AXIS_Y] = AKM8975_RANGE_Y_LO; + inf->asa.range_hi[AXIS_Y] = AKM8975_RANGE_Y_HI; + inf->asa.range_lo[AXIS_Z] = AKM8975_RANGE_Z_LO; + inf->asa.range_hi[AXIS_Z] = AKM8975_RANGE_Z_HI; + inf->range_index = 0; + } + } + if (!err_t) + inf->initd = true; + else + dev_err(&inf->i2c->dev, "%s ERR %d", __func__, err_t); + return err_t; +} + +static void akm_calc(struct akm_inf *inf, u8 *data) +{ + int x; + int y; + int z; + + /* data[1] = AKM_REG_HXL + * data[2] = AKM_REG_HXH + * data[3] = AKM_REG_HYL + * data[4] = AKM_REG_HYH + * data[5] = AKM_REG_HZL + * data[6] = AKM_REG_HZH + */ + x = (int)((data[2] << 8) | data[1]); + y = (int)((data[4] << 8) | data[3]); + z = (int)((data[6] << 8) | data[5]); + x = ((x * (inf->asa.asa[AXIS_X] + 128)) >> 8); + y = ((y * (inf->asa.asa[AXIS_Y] + 128)) >> 8); + z = ((z * (inf->asa.asa[AXIS_Z] + 128)) >> 8); + mutex_lock(&inf->mutex_data); + inf->xyz[AXIS_X] = x; + inf->xyz[AXIS_Y] = y; + inf->xyz[AXIS_Z] = z; + mutex_unlock(&inf->mutex_data); +} + +static void akm_report(struct akm_inf *inf, u8 *data, s64 ts) +{ + akm_calc(inf, data); + input_report_rel(inf->idev, REL_X, inf->xyz[AXIS_X]); + input_report_rel(inf->idev, REL_Y, inf->xyz[AXIS_Y]); + input_report_rel(inf->idev, REL_Z, inf->xyz[AXIS_Z]); + input_report_rel(inf->idev, REL_MISC, (unsigned int)(ts >> 32)); + input_report_rel(inf->idev, REL_WHEEL, + (unsigned int)(ts & 0xffffffff)); + input_sync(inf->idev); +} - if (status) - pr_err("%s, line=%d, status=%d\n", __func__, __LINE__, status); +static int akm_read_sts(struct akm_inf *inf, u8 *data) +{ + int err = -1; /* assume something wrong */ + /* data[0] = AKM_REG_ST1 + * data[7] = AKM_REG_ST2 + * data[8] = AKM_REG_CNTL1 + */ + if ((data[0] & AKM_ST1_DRDY) && (!(data[7] & + (AKM_ST2_HOFL | AKM_ST2_DERR)))) + err = 1; /* data ready to be reported */ + else if ((data[8] & AKM_CNTL1_MODE_MASK) == (inf->data_out & + AKM_CNTL1_MODE_MASK)) + err = 0; /* still processing */ + return err; +} - return status; +static s64 akm_timestamp_ns(void) +{ + struct timespec ts; + s64 ns; + + ktime_get_ts(&ts); + ns = timespec_to_ns(&ts); + return ns; +} + +static int akm_read(struct akm_inf *inf) +{ + long long timestamp1; + long long timestamp2; + u8 data[9]; + int err; + + timestamp1 = akm_timestamp_ns(); + err = akm_i2c_rd(inf, AKM_REG_ST1, 9, data); + timestamp2 = akm_timestamp_ns(); + if (err) + return err; + + err = akm_read_sts(inf, data); + if (err > 0) { + timestamp2 = (timestamp2 - timestamp1) / 2; + timestamp1 += timestamp2; + akm_report(inf, data, timestamp1); + if ((inf->data_out & AKM_CNTL1_MODE_MASK) == + AKM_CNTL1_MODE_SINGLE) + akm_i2c_wr(inf, AKM_REG_CNTL1, inf->data_out); + } else if (err < 0) { + dev_err(&inf->i2c->dev, "%s ERR\n", __func__); + akm_mode_wr(inf, true, inf->range_index, + inf->data_out & AKM_CNTL1_MODE_MASK); + } + return err; +} + +static void akm_mpu_handler(u8 *data, unsigned int len, s64 ts, void *p_val) +{ + struct akm_inf *inf; + int err; + + inf = (struct akm_inf *)p_val; + if (inf->enable) { + if (inf->cycle && (!inf->port_en[WR])) { + ts -= inf->cycle_ts; + if (ts > (inf->cycle_delay_us * 1000)) + akm_port_enable(inf, WR, true); + return; + } + err = akm_read_sts(inf, data); + if (err > 0) { + akm_report(inf, data, ts); + if (inf->cycle) { + akm_port_enable(inf, WR, false); + inf->cycle_ts = ts; + } + } + } } -static void ak8975_work_func(struct work_struct *work) +static int akm_id(struct akm_inf *inf) { - struct inv_compass_state *st = - container_of((struct delayed_work *)work, - struct inv_compass_state, work); - unsigned long delay = msecs_to_jiffies(atomic_read(&st->delay)); - short c[3]; - c[0] = c[1] = c[2] = 0; - if (0 == ak8975_read(st, c)) { - c[0] = ((c[0] * (st->asa[0] + 128)) >> 8); - c[1] = ((c[1] * (st->asa[1] + 128)) >> 8); - c[2] = ((c[2] * (st->asa[2] + 128)) >> 8); - input_report_rel(st->idev, REL_X, c[0]); - input_report_rel(st->idev, REL_Y, c[1]); - input_report_rel(st->idev, REL_Z, c[2]); - input_sync(st->idev); + struct nvi_mpu_port nmp; + u8 config_boot; + u8 val = 0; + int err; + + config_boot = inf->pdata.config & NVI_CONFIG_BOOT_MASK; + if (config_boot == NVI_CONFIG_BOOT_AUTO) { + nmp.addr = inf->i2c->addr | 0x80; + nmp.reg = AKM_REG_WIA; + nmp.ctrl = 1; + err = nvi_mpu_dev_valid(&nmp, &val); + /* see mpu.h for possible return values */ + if ((err == -EAGAIN) || (err == -EBUSY)) + return -EAGAIN; + + if (((!err) && (val == AKM_WIA_ID)) || (err == -EIO)) + config_boot = NVI_CONFIG_BOOT_MPU; } + if (config_boot == NVI_CONFIG_BOOT_MPU) { + inf->use_mpu = true; + nmp.addr = inf->i2c->addr | 0x80; + nmp.reg = AKM_REG_ST1; + nmp.ctrl = 9; + nmp.data_out = 0; + nmp.delay_ms = 0; + nmp.delay_us = inf->poll_delay_us; + nmp.shutdown_bypass = false; + nmp.handler = &akm_mpu_handler; + nmp.ext_driver = (void *)inf; + err = nvi_mpu_port_alloc(&nmp); + if (err < 0) + return err; + + inf->port_id[RD] = err; + nmp.addr = inf->i2c->addr; + nmp.reg = AKM_REG_CNTL1; + nmp.ctrl = 1; + nmp.data_out = inf->data_out; + nmp.delay_ms = AKM_HW_DELAY_TSM_MS; + nmp.delay_us = 0; + nmp.shutdown_bypass = false; + nmp.handler = NULL; + nmp.ext_driver = NULL; + err = nvi_mpu_port_alloc(&nmp); + if (err < 0) { + akm_ports_free(inf); + } else { + inf->port_id[WR] = err; + err = 0; + } + return err; + } + + /* NVI_CONFIG_BOOT_EXTERNAL */ + inf->use_mpu = false; + err = akm_i2c_rd(inf, AKM_REG_WIA, 1, &val); + if ((!err) && (val == AKM_WIA_ID)) + return 0; - mutex_lock(&st->value_mutex); - st->value[0] = c[0]; - st->value[1] = c[1]; - st->value[2] = c[2]; - mutex_unlock(&st->value_mutex); - schedule_delayed_work(&st->work, delay); + return -ENODEV; } -static ssize_t ak8975_value_show(struct device *dev, - struct device_attribute *attr, char *buf) +static void akm_work(struct work_struct *ws) { - struct inv_compass_state *st = dev_get_drvdata(dev); - short c[3]; + struct akm_inf *inf; - mutex_lock(&st->value_mutex); - c[0] = st->value[0]; - c[1] = st->value[1]; - c[2] = st->value[2]; - mutex_unlock(&st->value_mutex); - return sprintf(buf, "%d, %d, %d\n", c[0], c[1], c[2]); + inf = container_of(ws, struct akm_inf, dw.work); + akm_read(inf); + queue_delayed_work(inf->wq, &inf->dw, + usecs_to_jiffies(inf->poll_delay_us)); } -static ssize_t ak8975_scale_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int akm_enable(struct akm_inf *inf, bool enable) { - return sprintf(buf, "%ld\n", 9830L * 32768L); + int err = 0; + + akm_pm(inf, true); + if (!inf->initd) + err = akm_init_hw(inf); + if (!err) { + if (enable) { + inf->data_out = AKM_CNTL1_MODE_SINGLE; + err = akm_delay(inf, inf->poll_delay_us); + err |= akm_mode_wr(inf, true, inf->range_index, + inf->data_out & AKM_CNTL1_MODE_MASK); + if (!err) + inf->enable = true; + } else { + inf->enable = false; + err = akm_mode_wr(inf, false, inf->range_index, + AKM_CNTL1_MODE_POWERDOWN); + if (!err) + akm_pm(inf, false); + } + } + return err; +} + +static ssize_t akm_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct akm_inf *inf; + unsigned int enable; + bool en; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtouint(buf, 10, &enable); + if (err) + return -EINVAL; + + if (enable) + en = true; + else + en = false; + dev_dbg(&inf->i2c->dev, "%s: %x", __func__, en); + err = akm_enable(inf, en); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d", __func__, en, err); + return err; + } + + return count; } -static ssize_t ak8975_reset_show(struct device *dev, - struct device_attribute *attr, char *buf) + +static ssize_t akm_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_compass_state *st = dev_get_drvdata(dev); - int result; - result = ak8975_init(st); - return sprintf(buf, "%d\n", result); + struct akm_inf *inf; + unsigned int enable = 0; + + inf = dev_get_drvdata(dev); + if (inf->enable) + enable = 1; + return sprintf(buf, "%u\n", enable); } -static ssize_t ak8975_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t akm_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_compass_state *st = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", atomic_read(&st->enable)); + struct akm_inf *inf; + unsigned long delay_us; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &delay_us); + if (err) + return -EINVAL; + + dev_dbg(&inf->i2c->dev, "%s: %lu\n", __func__, delay_us); + if (delay_us < (AKM_INPUT_DELAY_MS_MIN * 1000)) + delay_us = (AKM_INPUT_DELAY_MS_MIN * 1000); + if ((inf->enable) && (delay_us != inf->poll_delay_us)) + err = akm_delay(inf, delay_us); + if (!err) { + inf->poll_delay_us = delay_us; + } else { + dev_err(&inf->i2c->dev, "%s: %lu ERR=%d\n", + __func__, delay_us, err); + return err; + } + + return count; } -static ssize_t ak8975_rate_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t akm_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_compass_state *st = dev_get_drvdata(dev); - /* transform delay in ms to rate */ - return sprintf(buf, "%d\n", 1000 / atomic_read(&st->delay)); + struct akm_inf *inf; + + inf = dev_get_drvdata(dev); + if (inf->enable) + return sprintf(buf, "%lu\n", inf->poll_delay_us); + + return sprintf(buf, "%u\n", (AKM_INPUT_DELAY_MS_MIN * 1000)); } -static ssize_t akm8975_matrix_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t akm_resolution_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_compass_state *st = dev_get_drvdata(dev); - signed char *m; - 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]); -} - -static ssize_t ak8975_rate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - unsigned long data; - int error; - struct inv_compass_state *st = dev_get_drvdata(dev); - - error = kstrtoul(buf, 10, &data); - if (error) - return error; - /* transform rate to delay in ms */ - data = 1000 / data; - if (data > AK8975_MAX_DELAY) - data = AK8975_MAX_DELAY; - if (data < AK8975_MIN_DELAY) - data = AK8975_MIN_DELAY; - atomic_set(&st->delay, (unsigned int) data); + struct akm_inf *inf; + unsigned int resolution; + + inf = dev_get_drvdata(dev); + if (kstrtouint(buf, 10, &resolution)) + return -EINVAL; + + dev_dbg(&inf->i2c->dev, "%s %u", __func__, resolution); + inf->resolution = resolution; return count; } -static void ak8975_set_enable(struct device *dev, int enable) +static ssize_t akm_resolution_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_compass_state *st = dev_get_drvdata(dev); - int result = 0; - int pre_enable = atomic_read(&st->enable); + struct akm_inf *inf; + unsigned int resolution; + + inf = dev_get_drvdata(dev); + if (inf->enable) + resolution = inf->resolution; + else + resolution = AKM_INPUT_RESOLUTION; + return sprintf(buf, "%u\n", resolution); +} - mutex_lock(&st->enable_mutex); - if (enable) { - if (pre_enable == 0) { - result = inv_serial_single_write(st, AK8975_REG_CNTL, - AK8975_CNTL_MODE_SNG_MEASURE); - if (result) - pr_err("%s, line=%d\n", __func__, __LINE__); - schedule_delayed_work(&st->work, - msecs_to_jiffies(atomic_read(&st->delay))); - atomic_set(&st->enable, 1); +static ssize_t akm_max_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct akm_inf *inf; + u8 range_index; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &range_index); + if (err) + return -EINVAL; + + dev_dbg(&inf->i2c->dev, "%s %u", __func__, range_index); + if (inf->compass_id == COMPASS_ID_AK8963) { + if (range_index > 1) + return -EINVAL; + + if (inf->enable) { + err = akm_mode_wr(inf, false, range_index, + inf->data_out & AKM_CNTL1_MODE_MASK); + if (err) + return err; + } else { + inf->range_index = range_index; } + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t akm_max_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct akm_inf *inf; + unsigned int range; + inf = dev_get_drvdata(dev); + if (inf->enable) { + range = inf->range_index; } else { - if (pre_enable == 1) { - cancel_delayed_work_sync(&st->work); - atomic_set(&st->enable, 0); - result = inv_serial_single_write(st, AK8975_REG_CNTL, - AK8975_CNTL_MODE_POWER_DOWN); - if (result) - pr_err("%s, line=%d\n", __func__, __LINE__); - mdelay(1); /* wait at least 100us */ + if (inf->compass_id == COMPASS_ID_AK8963) { + if (inf->range_index) + range = AKM8963_SCALE16; + else + range = AKM8963_SCALE14; + } else if (inf->compass_id == COMPASS_ID_AK8972) { + range = AKM8972_SCALE; + } else { + range = AKM8975_SCALE; } } - mutex_unlock(&st->enable_mutex); + return sprintf(buf, "%u\n", range); } -static ssize_t ak8975_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t akm_cycle_delay_us_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - unsigned long data; - int error; + struct akm_inf *inf; + unsigned long cycle_delay_us; + int err; - error = kstrtoul(buf, 10, &data); - if (error) - return error; - if (data == 0 || data == 1) - ak8975_set_enable(dev, data); + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &cycle_delay_us); + if (err) + return -EINVAL; + dev_dbg(&inf->i2c->dev, "%s %lu", __func__, cycle_delay_us); + inf->cycle_delay_us = cycle_delay_us; return count; } -static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, - ak8975_enable_show, ak8975_enable_store); -static DEVICE_ATTR(value, S_IRUGO, ak8975_value_show, NULL); -static DEVICE_ATTR(scale, S_IRUGO, ak8975_scale_show, NULL); -static DEVICE_ATTR(reset, S_IRUGO, ak8975_reset_show, NULL); -static DEVICE_ATTR(rate, S_IRUGO | S_IWUSR, ak8975_rate_show, - ak8975_rate_store); -static DEVICE_ATTR(compass_matrix, S_IRUGO, akm8975_matrix_show, NULL); +static ssize_t akm_cycle_delay_us_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct akm_inf *inf = dev_get_drvdata(dev); + + return sprintf(buf, "%lu\n", inf->cycle_delay_us); +} + +static ssize_t akm_divisor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct akm_inf *inf; + + inf = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", AKM_INPUT_DIVISOR); +} + +static ssize_t akm_microamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct akm_inf *inf; -static struct attribute *ak8975_attributes[] = { + inf = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", AKM_INPUT_POWER_UA); +} + +static ssize_t akm_magnetic_field_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct akm_inf *inf; + int x; + int y; + int z; + + inf = dev_get_drvdata(dev); + if (inf->enable) { + mutex_lock(&inf->mutex_data); + x = inf->xyz[AXIS_X]; + y = inf->xyz[AXIS_Y]; + z = inf->xyz[AXIS_Z]; + mutex_unlock(&inf->mutex_data); + return sprintf(buf, "%d, %d, %d\n", x, y, z); + } + + return -EPERM; +} + +static ssize_t akm_orientation_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct akm_inf *inf; + signed char *m; + + inf = dev_get_drvdata(dev); + m = inf->pdata.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]); +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWOTH, + akm_enable_show, akm_enable_store); +static DEVICE_ATTR(delay, S_IRUGO | S_IWUSR | S_IWOTH, + akm_delay_show, akm_delay_store); +static DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR | S_IWOTH, + akm_resolution_show, akm_resolution_store); +static DEVICE_ATTR(max_range, S_IRUGO | S_IWUSR | S_IWOTH, + akm_max_range_show, akm_max_range_store); +static DEVICE_ATTR(cycle_delay_us, S_IRUGO | S_IWUSR | S_IWOTH, + akm_cycle_delay_us_show, akm_cycle_delay_us_store); +static DEVICE_ATTR(divisor, S_IRUGO, + akm_divisor_show, NULL); +static DEVICE_ATTR(microamp, S_IRUGO, + akm_microamp_show, NULL); +static DEVICE_ATTR(magnetic_field, S_IRUGO, + akm_magnetic_field_show, NULL); +static DEVICE_ATTR(orientation, S_IRUGO, + akm_orientation_show, NULL); + +static struct attribute *akm_attrs[] = { &dev_attr_enable.attr, - &dev_attr_value.attr, - &dev_attr_scale.attr, - &dev_attr_reset.attr, - &dev_attr_rate.attr, - &dev_attr_compass_matrix.attr, + &dev_attr_delay.attr, + &dev_attr_resolution.attr, + &dev_attr_max_range.attr, + &dev_attr_divisor.attr, + &dev_attr_microamp.attr, + &dev_attr_magnetic_field.attr, + &dev_attr_orientation.attr, + &dev_attr_cycle_delay_us.attr, NULL }; -static struct attribute_group ak8975_attribute_group = { - .name = "ak8975", - .attrs = ak8975_attributes +static struct attribute_group akm_attr_group = { + .name = AKM_NAME, + .attrs = akm_attrs }; +static int akm_sysfs_create(struct akm_inf *inf) +{ + int err; -/** - * inv_setup_input() - internal setup input device. - * @st: Device driver instance. - * @**idev_in pointer to input device - * @*client i2c client - * @*name name of the input device. - */ -static int inv_setup_input(struct inv_compass_state *st, - struct input_dev **idev_in, struct i2c_client *client, - unsigned char *name) { - int result; - struct input_dev *idev; - idev = input_allocate_device(); - if (!idev) { - result = -ENOMEM; - return result; - } - /* Setup input device. */ - idev->name = name; + err = sysfs_create_group(&inf->idev->dev.kobj, &akm_attr_group); + return err; +} - idev->id.bustype = BUS_I2C; - idev->id.product = 'S'; - idev->id.vendor = ('I'<<8) | 'S'; - idev->id.version = 1; - idev->dev.parent = &client->dev; - /* Open and close method. */ - idev->open = NULL; - idev->close = NULL; +static void akm_input_close(struct input_dev *idev) +{ + struct akm_inf *inf; - __set_bit(EV_REL, idev->evbit); - input_set_capability(idev, EV_REL, REL_X); - input_set_capability(idev, EV_REL, REL_Y); - input_set_capability(idev, EV_REL, REL_Z); + inf = input_get_drvdata(idev); + if (inf != NULL) + akm_enable(inf, false); +} - input_set_capability(idev, EV_REL, REL_MISC); - input_set_capability(idev, EV_REL, REL_WHEEL); +static int akm_input_create(struct akm_inf *inf) +{ + int err; - input_set_drvdata(idev, st); - result = input_register_device(idev); - if (result) - input_free_device(idev); + inf->idev = input_allocate_device(); + if (!inf->idev) { + err = -ENOMEM; + dev_err(&inf->i2c->dev, "%s ERR %d\n", __func__, err); + return err; + } - *idev_in = idev; - return result; + inf->idev->name = AKM_NAME; + inf->idev->dev.parent = &inf->i2c->dev; + inf->idev->close = akm_input_close; + input_set_drvdata(inf->idev, inf); + input_set_capability(inf->idev, EV_REL, REL_X); + input_set_capability(inf->idev, EV_REL, REL_Y); + input_set_capability(inf->idev, EV_REL, REL_Z); + input_set_capability(inf->idev, EV_REL, REL_MISC); + input_set_capability(inf->idev, EV_REL, REL_WHEEL); + err = input_register_device(inf->idev); + if (err) { + input_free_device(inf->idev); + inf->idev = NULL; + } + return err; } -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -static int ak8975_mod_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int akm_remove(struct i2c_client *client) { - struct mpu_platform_data *pdata; - struct inv_compass_state *st; - struct input_dev *idev; - int result = 0; - - dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name); + struct akm_inf *inf; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - result = -ENODEV; - goto out_no_free; + inf = i2c_get_clientdata(client); + if (inf != NULL) { + if (inf->idev) { + input_unregister_device(inf->idev); + input_free_device(inf->idev); + } + if (inf->wq) + destroy_workqueue(inf->wq); + akm_pm_exit(inf); + if (&inf->mutex_data) + mutex_destroy(&inf->mutex_data); + kfree(inf); } + dev_info(&client->dev, "%s\n", __func__); + return 0; +} + +static void akm_shutdown(struct i2c_client *client) +{ + akm_remove(client); +} - pdata = (struct mpu_platform_data *)dev_get_platdata(&client->dev); - if (!pdata) { - dev_err(&client->adapter->dev, - "Missing platform data for slave %s\n", devid->name); - result = -EFAULT; - goto out_no_free; +static int akm_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct akm_inf *inf; + struct mpu_platform_data *pd; + int err; + + dev_info(&client->dev, "%s\n", __func__); + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (IS_ERR_OR_NULL(inf)) { + dev_err(&client->dev, "%s kzalloc ERR\n", __func__); + return -ENOMEM; } - st = kzalloc(sizeof(*st), GFP_KERNEL); - if (!st) { - result = -ENOMEM; - goto out_no_free; + inf->i2c = client; + i2c_set_clientdata(client, inf); + pd = (struct mpu_platform_data *)dev_get_platdata(&client->dev); + if (pd == NULL) + dev_err(&client->dev, "%s No %s platform data ERR\n", + __func__, devid->name); + else + inf->pdata = *pd; + akm_pm_init(inf); + err = akm_id(inf); + akm_pm(inf, false); + if (err == -EAGAIN) + goto akm_probe_again; + else if (err) + goto akm_probe_err; + + mutex_init(&inf->mutex_data); + err = akm_input_create(inf); + if (err) + goto akm_probe_err; + + inf->wq = create_singlethread_workqueue(AKM_NAME); + if (!inf->wq) { + dev_err(&client->dev, "%s workqueue ERR\n", __func__); + err = -ENOMEM; + goto akm_probe_err; } - i2c_set_clientdata(client, st); - st->i2c = client; - mutex_init(&st->value_mutex); - mutex_init(&st->enable_mutex); - atomic_set(&st->delay, 100); - st->sl_handle = client->adapter; - st->plat_data = *pdata; - st->i2c_addr = client->addr; - INIT_DELAYED_WORK(&st->work, ak8975_work_func); - result = inv_setup_input(st, &idev, client, "INV_AK8975"); - if (result) - goto out_free_memory; - st->idev = idev; - result = sysfs_create_group(&st->idev->dev.kobj, - &ak8975_attribute_group); - if (result < 0) - goto error_sysfs; - - result = ak8975_init(st); - if (result < 0) - goto error_sysfs; - - return result; -error_sysfs: - input_unregister_device(st->idev); -out_free_memory: - kfree(st); -out_no_free: - dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result); - return result; - -} - -static int ak8975_mod_remove(struct i2c_client *client) -{ - struct inv_compass_state *st = - i2c_get_clientdata(client); - - dev_dbg(&client->adapter->dev, "%s\n", __func__); - ak8975_set_enable(&st->idev->dev, 0); - sysfs_remove_group(&st->idev->dev.kobj, &ak8975_attribute_group); - input_unregister_device(st->idev); - kfree(st); + INIT_DELAYED_WORK(&inf->dw, akm_work); + err = akm_sysfs_create(inf); + if (err) + goto akm_probe_err; + return 0; + +akm_probe_err: + dev_err(&client->dev, "%s ERR %d\n", __func__, err); +akm_probe_again: + akm_remove(client); + return err; } -static const struct i2c_device_id ak8975_mod_id[] = { - {"ak8975", COMPASS_ID_AK8975}, +static const struct i2c_device_id akm_i2c_device_id[] = { + {AKM_NAME, 0}, + {"ak8963", 0}, + {"ak8972", 0}, + {"ak8975", 0}, {} }; -MODULE_DEVICE_TABLE(i2c, ak8975_mod_id); +MODULE_DEVICE_TABLE(i2c, akm_i2c_device_id); -static struct i2c_driver ak8975_mod_driver = { - .class = I2C_CLASS_HWMON, - .probe = ak8975_mod_probe, - .remove = ak8975_mod_remove, - .id_table = ak8975_mod_id, +static struct i2c_driver akm_driver = { + .class = I2C_CLASS_HWMON, + .probe = akm_probe, + .remove = akm_remove, .driver = { - .owner = THIS_MODULE, - .name = "ak8975_mod", - }, - .address_list = normal_i2c, + .name = AKM_NAME, + .owner = THIS_MODULE, + }, + .id_table = akm_i2c_device_id, + .shutdown = akm_shutdown, }; -static int __init ak8975_mod_init(void) +static int __init akm_init(void) { - int res = i2c_add_driver(&ak8975_mod_driver); - pr_info("%s: Probe name %s\n", __func__, "ak8975_mod"); - if (res) - pr_err("%s failed\n", __func__); - return res; + return i2c_add_driver(&akm_driver); } -static void __exit ak8975_mod_exit(void) +static void __exit akm_exit(void) { - pr_info("%s\n", __func__); - i2c_del_driver(&ak8975_mod_driver); + i2c_del_driver(&akm_driver); } -module_init(ak8975_mod_init); -module_exit(ak8975_mod_exit); +module_init(akm_init); +module_exit(akm_exit); -MODULE_AUTHOR("Invensense Corporation"); -MODULE_DESCRIPTION("Driver for AK8975 sensor with input subsystem"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("ak8975_mod"); +MODULE_DESCRIPTION("AKM driver"); -/** - * @} - */ diff --git a/drivers/input/misc/mpu/inv_gyro.c b/drivers/input/misc/mpu/inv_gyro.c index edee7ab4b47c..cfa76ab46865 100644 --- a/drivers/input/misc/mpu/inv_gyro.c +++ b/drivers/input/misc/mpu/inv_gyro.c @@ -35,13 +35,22 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/kfifo.h> +#include <linux/regulator/consumer.h> #include <linux/byteorder/generic.h> #include <linux/poll.h> #include <linux/miscdevice.h> #include <linux/spinlock.h> +#include <linux/mpu.h> #include "inv_gyro.h" + +/* regulator names in order of powering on */ +static char *nvi_vregs[] = { + "vdd", + "vlogic", +}; + static struct inv_reg_map_s chip_reg = { .who_am_i = 0x75, .sample_rate_div = 0x19, @@ -65,6 +74,7 @@ static struct inv_reg_map_s chip_reg = { .mem_r_w = 0x6F, .prgm_strt_addrh = 0x70 }; + static const struct inv_hw_s hw_info[INV_NUM_PARTS] = { {119, "ITG3500"}, { 63, "MPU3050"}, @@ -72,6 +82,25 @@ static const struct inv_hw_s hw_info[INV_NUM_PARTS] = { {118, "MPU9150"} }; +static unsigned long nvi_lpf_us_tbl[] = { + 3906, /* 256Hz */ + 5319, /* 188Hz */ + 10204, /* 98Hz */ + 23810, /* 42Hz */ + 50000, /* 20Hz */ + 100000, /* 10Hz */ + /* 200000, 5Hz */ +}; + +static unsigned long nvi_lpa_delay_us_tbl[] = { + 800000, + 200000, + 50000, + /* 25000, */ +}; + +static struct inv_gyro_state_s *inf_local; + s64 get_time_ns(void) { struct timespec ts; @@ -94,12 +123,12 @@ int inv_i2c_read_base(struct inv_gyro_state_s *st, unsigned short i2c_addr, struct i2c_msg msgs[2]; int res; + if (st->shutdown) + return 0; + if (!data) return -EINVAL; - if (st->i2c_shutdown) - return -ENODEV; - msgs[0].addr = i2c_addr; msgs[0].flags = 0; /* write */ msgs[0].buf = ® @@ -110,8 +139,9 @@ int inv_i2c_read_base(struct inv_gyro_state_s *st, unsigned short i2c_addr, msgs[1].buf = data; msgs[1].len = length; - pr_debug("%s RD%02X%02X%02X\n", st->hw->name, i2c_addr, reg, length); res = i2c_transfer(st->sl_handle, msgs, 2); + pr_debug("%s RD%02X%02X%02X res=%d\n", + st->hw_s->name, i2c_addr, reg, length, res); if (res < 2) { if (res >= 0) res = -EIO; @@ -135,8 +165,8 @@ int inv_i2c_single_write_base(struct inv_gyro_state_s *st, struct i2c_msg msg; int res; - if (st->i2c_shutdown) - return -ENODEV; + if (st->shutdown) + return 0; tmp[0] = reg; tmp[1] = data; @@ -146,8 +176,9 @@ int inv_i2c_single_write_base(struct inv_gyro_state_s *st, msg.buf = tmp; msg.len = 2; - pr_debug("%s WS%02X%02X%02X\n", st->hw->name, i2c_addr, reg, data); res = i2c_transfer(st->sl_handle, &msg, 1); + pr_debug("%s WS%02X%02X%02X res=%d\n", + st->hw_s->name, i2c_addr, reg, data, res); if (res < 1) { if (res == 0) res = -EIO; @@ -156,1410 +187,2221 @@ int inv_i2c_single_write_base(struct inv_gyro_state_s *st, return 0; } -/** - * inv_clear_kfifo() - clear time stamp fifo - * @st: Device driver instance. - */ -void inv_clear_kfifo(struct inv_gyro_state_s *st) + + +/* Register SMPLRT_DIV (0x19) */ +static int nvi_smplrt_div_wr(struct inv_gyro_state_s *inf, + unsigned char smplrt_div) { - unsigned long flags; - spin_lock_irqsave(&st->time_stamp_lock, flags); - kfifo_reset(&st->trigger.timestamps); - spin_unlock_irqrestore(&st->time_stamp_lock, flags); + int err = 0; + + if (smplrt_div != inf->hw.smplrt_div) { + err = inv_i2c_single_write(inf, inf->reg->sample_rate_div, + smplrt_div); + if (!err) + inf->hw.smplrt_div = smplrt_div; + } + return err; } -static int set_power_itg(struct inv_gyro_state_s *st, unsigned char power_on) +/* Register CONFIG (0x1A) */ +static int nvi_config_wr(struct inv_gyro_state_s *inf, unsigned char lpf) { - struct inv_reg_map_s *reg; - unsigned char data; - int result; + int err = 0; - reg = st->reg; - if (power_on) - data = 0; - else - data = BIT_SLEEP; - data |= (st->chip_config.lpa_mode << 5); - if (st->chip_config.gyro_enable) { - result = inv_i2c_single_write(st, - reg->pwr_mgmt_1, data | INV_CLK_PLL); - if (result) - return result; + if (lpf != inf->hw.config) { + err = inv_i2c_single_write(inf, inf->reg->lpf, lpf); + if (!err) + inf->hw.config = lpf; + } + return err; +} - 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; +/* Register GYRO_CONFIG (0x1B) */ +static int nvi_gyro_config_wr(struct inv_gyro_state_s *inf, unsigned char fsr) +{ + unsigned char val; + int err = 0; - st->chip_config.clk_src = INV_CLK_INTERNAL; + if (fsr != inf->hw.gyro_config) { + val = (fsr << 3); + err = inv_i2c_single_write(inf, inf->reg->gyro_config, val); + if (!err) { + inf->hw.gyro_config = val; + err = 1; /* flag change made */ + } } + return err; +} - if (power_on) { - mdelay(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 << 6); - result = inv_i2c_single_write(st, reg->pwr_mgmt_2, data); - if (result) - return result; +/* Register ACCEL_CONFIG (0x1C) */ +static int nvi_accel_config_wr(struct inv_gyro_state_s *inf, + unsigned char fsr, unsigned char hpf) +{ + unsigned char val; + int err = 0; - mdelay(POWER_UP_TIME); - st->chip_config.is_asleep = 0; - } else { - st->chip_config.is_asleep = 1; + val = (fsr << 3) | hpf; + if (val != inf->hw.accl_config) { + err = inv_i2c_single_write(inf, inf->reg->accl_config, + val); + if (!err) { + inf->hw.accl_config = val; + err = 1; /* flag change made */ + if (hpf != 7) + inf->mot_enable = false; + } } - return 0; + return err; } -/** - * inv_set_power_state() - Turn device on/off. - * @st: Device driver instance. - * @power_on: 1 to turn on, 0 to suspend. - */ -int inv_set_power_state(struct inv_gyro_state_s *st, unsigned char power_on) +/* Register MOT_THR (0x1F) */ +static int nvi_mot_thr_wr(struct inv_gyro_state_s *inf, unsigned char mot_thr) { - int ret; - if (!power_on) - disable_irq(st->trigger.irq); + int err = 0; - if (INV_MPU3050 == st->chip_type) - ret = set_power_mpu3050(st, power_on); - else - ret = set_power_itg(st, power_on); + if (mot_thr != inf->hw.mot_thr) { + err = inv_i2c_single_write(inf, REG_MOT_THR, mot_thr); + if (!err) + inf->hw.mot_thr = mot_thr; + } + return err; +} - if (power_on) - enable_irq(st->trigger.irq); - return ret; +/* Register MOT_DUR (0x20) */ +static int nvi_mot_dur_wr(struct inv_gyro_state_s *inf, unsigned char mot_dur) +{ + int err = 0; + + if (mot_dur != inf->hw.mot_dur) { + err = inv_i2c_single_write(inf, REG_MOT_DUR, mot_dur); + if (!err) + inf->hw.mot_dur = mot_dur; + } + return err; } -/** - * reset_fifo_itg() - Reset FIFO related registers. - * @st: Device driver instance. - */ -static int reset_fifo_itg(struct inv_gyro_state_s *st) +/* Register FIFO_EN (0x23) */ +static int nvi_fifo_en_wr(struct inv_gyro_state_s *inf, unsigned char fifo_en) +{ + int err = 0; + + if (fifo_en != inf->hw.fifo_en) { + err = inv_i2c_single_write(inf, inf->reg->fifo_en, fifo_en); + if (!err) + inf->hw.fifo_en = fifo_en; + } + return err; +} + +/* Register I2C_MST_CTRL (0x24) */ +static int nvi_i2c_mst_ctrl_wr(struct inv_gyro_state_s *inf, + bool port3_fifo_en) { - struct inv_reg_map_s *reg; - int result; unsigned char val; + int err = 0; - reg = st->reg; - /* disable interrupt */ - result = inv_i2c_single_write(st, reg->int_enable, 0); - if (result) { - pr_err("%s failed\n", __func__); - return result; + val = inf->aux.clock_i2c; + val |= BIT_WAIT_FOR_ES | BIT_I2C_MST_P_NSR; + if (port3_fifo_en) + val |= BIT_SLV3_FIFO_EN; + if (val != inf->hw.i2c_mst_ctrl) { + err = inv_i2c_single_write(inf, REG_I2C_MST_CTRL, val); + if (!err) + inf->hw.i2c_mst_ctrl = val; } + return err; +} - /* disable the sensor output to FIFO */ - result = inv_i2c_single_write(st, reg->fifo_en, 0); - if (result) - goto reset_fifo_fail; +/* Register I2C_SLV4_CTRL (0x34) */ +static int nvi_i2c_slv4_ctrl_wr(struct inv_gyro_state_s *inf, bool slv4_en) +{ + unsigned char val; + int err = 0; - /* disable fifo reading */ - result = inv_i2c_single_write(st, reg->user_ctrl, 0); - if (result) - goto reset_fifo_fail; + val = inf->aux.delay_hw; + val |= (inf->aux.port[AUX_PORT_SPECIAL].nmp.ctrl & + BITS_I2C_SLV_REG_DIS); + if (slv4_en) + val |= BIT_SLV_EN; + if (val != inf->hw.i2c_slv4_ctrl) { + err = inv_i2c_single_write(inf, REG_I2C_SLV4_CTRL, val); + if (!err) + inf->hw.i2c_slv4_ctrl = val; + } + return err; +} - if (st->chip_config.dmp_on) { - val = (BIT_FIFO_RST | BIT_DMP_RST); - if (st->chip_config.compass_enable) - val |= BIT_I2C_MST_RST; - result = inv_i2c_single_write(st, reg->user_ctrl, val); - if (result) - goto reset_fifo_fail; +/* Register INT_PIN_CFG (0x37) */ +static int nvi_int_pin_cfg_wr(struct inv_gyro_state_s *inf, unsigned char val) +{ + int err = 0; - mdelay(POWER_UP_TIME); - st->last_isr_time = get_time_ns(); - result = inv_i2c_single_write(st, reg->int_enable, - BIT_DMP_INT_EN); - if (result) - return result; + if (val != inf->hw.int_pin_cfg) { + err = inv_i2c_single_write(inf, REG_INT_PIN_CFG, val); + if (!err) + inf->hw.int_pin_cfg = val; + } + return err; +} - val = (BIT_DMP_EN | BIT_FIFO_EN); - if (st->chip_config.compass_enable) - val |= BIT_I2C_MST_EN; - result = inv_i2c_single_write(st, reg->user_ctrl, val); - if (result) - goto reset_fifo_fail; +/* Register INT_ENABLE (0x38) */ +static int nvi_int_enable_wr(struct inv_gyro_state_s *inf, bool enable) +{ + unsigned char val = 0; + int err = 0; - } else { - /* reset FIFO and possibly reset I2C*/ - val = BIT_FIFO_RST; - if (st->chip_config.compass_enable) - val |= BIT_I2C_MST_RST; - result = inv_i2c_single_write(st, reg->user_ctrl, val); - if (result) - goto reset_fifo_fail; - - mdelay(POWER_UP_TIME); - st->last_isr_time = get_time_ns(); - /* enable interrupt */ - if (st->chip_config.accl_fifo_enable || - st->chip_config.gyro_fifo_enable || - st->chip_config.compass_enable){ - result = inv_i2c_single_write(st, reg->int_enable, - BIT_DATA_RDY_EN); - if (result) - return result; + if (enable) { + if ((inf->hw.user_ctrl & BIT_I2C_MST_EN) || + inf->chip_config.gyro_enable) { + val = BIT_DATA_RDY_EN; + } else if (inf->chip_config.accl_enable) { + if (inf->mot_enable && (!inf->mot_cnt)) { + val = BIT_MOT_EN; + if (inf->mot_dbg) + pr_info("%s motion detect on", + __func__); + } else { + val = BIT_DATA_RDY_EN; + } } + } + if ((val != inf->hw.int_enable) && (inf->pm > NVI_PM_OFF)) { + err = inv_i2c_single_write(inf, inf->reg->int_enable, val); + if (!err) + inf->hw.int_enable = val; + } + return err; +} - /* enable FIFO reading and I2C master interface*/ - val = BIT_FIFO_EN; - if (st->chip_config.compass_enable) - val |= BIT_I2C_MST_EN; - result = inv_i2c_single_write(st, reg->user_ctrl, val); - if (result) - goto reset_fifo_fail; +/* Register I2C_MST_DELAY_CTRL (0x67) */ +static int nvi_i2c_mst_delay_ctrl_wr(struct inv_gyro_state_s *inf, + unsigned char i2c_mst_delay_ctrl) +{ + int err = 0; - /* enable sensor output to FIFO */ - val = 0; - if (st->chip_config.gyro_fifo_enable) - val |= BITS_GYRO_OUT; - if (st->chip_config.accl_fifo_enable) - val |= BIT_ACCEL_OUT; - result = inv_i2c_single_write(st, reg->fifo_en, val); - if (result) - goto reset_fifo_fail; + if (i2c_mst_delay_ctrl != inf->hw.i2c_mst_delay_ctrl) { + err = inv_i2c_single_write(inf, REG_I2C_MST_DELAY_CTRL, + i2c_mst_delay_ctrl); + if (!err) + inf->hw.i2c_mst_delay_ctrl = i2c_mst_delay_ctrl; } + return err; +} - return 0; +/* Register MOT_DETECT_CTRL (0x69) */ +static int nvi_mot_detect_ctrl_wr(struct inv_gyro_state_s *inf, + unsigned char val) +{ + int err = 0; -reset_fifo_fail: - if (st->chip_config.dmp_on) - val = BIT_DMP_INT_EN; - else - val = BIT_DATA_RDY_EN; - inv_i2c_single_write(st, reg->int_enable, val); - pr_err("%s failed\n", __func__); - return result; + if (val != inf->hw.mot_detect_ctrl) { + err = inv_i2c_single_write(inf, REG_MOT_DETECT_CTRL, val); + if (!err) + inf->hw.mot_detect_ctrl = val; + } + return err; } -/** - * inv_reset_fifo() - Reset FIFO related registers. - * @st: Device driver instance. - */ -int inv_reset_fifo(struct inv_gyro_state_s *st) +/* Register USER_CTRL (0x6A) */ +static int nvi_user_ctrl_reset_wr(struct inv_gyro_state_s *inf, + unsigned char val) { - if (INV_MPU3050 == st->chip_type) - return reset_fifo_mpu3050(st); - else - return reset_fifo_itg(st); + int i; + int err; + int err_t; + + err_t = inv_i2c_single_write(inf, inf->reg->user_ctrl, val); + for (i = 0; i < POWER_UP_TIME; i++) { + val = -1; + err = inv_i2c_read(inf, inf->reg->user_ctrl, 1, &val); + if (!(val & (BIT_FIFO_RST | BIT_I2C_MST_RST))) + break; + + mdelay(1); + } + err_t |= err; + inf->hw.user_ctrl = val; + return err_t; } -/** - * set_inv_enable() - Reset FIFO related registers. - * @st: Device driver instance. - * @fifo_enable: enable/disable - */ -int set_inv_enable(struct inv_gyro_state_s *st, unsigned long enable) +/* Register USER_CTRL (0x6A) */ +static int nvi_user_ctrl_en_wr(struct inv_gyro_state_s *inf, + bool fifo_enable, bool i2c_enable) { - struct inv_reg_map_s *reg; - int result; + unsigned char val; + bool en; + int i; + int err = 0; - if (st->chip_config.is_asleep) - return -EINVAL; + if (inf->lpa_enable) + fifo_enable = false; + val = 0; + if (fifo_enable) { + for (i = 0; i < (AUX_PORT_SPECIAL - 1); i++) { + if (inf->aux.port[i].fifo_en && inf->aux.port[i].hw_en) + val |= (1 << i); + } + if (inf->chip_config.gyro_fifo_enable) + val |= (inf->chip_config.gyro_enable << 4); + if (inf->chip_config.accl_fifo_enable) + val |= BIT_ACCEL_OUT; + if (inf->chip_config.temp_fifo_enable) + val |= BIT_TEMP_FIFO_EN; + if (inf->aux.port[3].fifo_en && inf->aux.port[3].hw_en) + en = true; + else + en = false; + err |= nvi_i2c_mst_ctrl_wr(inf, en); + if (val || en) + en = true; + else + en = false; + } else { + err |= nvi_i2c_mst_ctrl_wr(inf, false); + en = false; + } + err |= nvi_fifo_en_wr(inf, val); + val = 0; + if (fifo_enable && en) + val |= BIT_FIFO_EN; + if (i2c_enable && inf->aux.enable) + val |= BIT_I2C_MST_EN; + if (val != inf->hw.user_ctrl) { + err |= inv_i2c_single_write(inf, inf->reg->user_ctrl, val); + if (!err) + inf->hw.user_ctrl = val; + } + return err; +} - reg = st->reg; - if (enable) { - result = inv_reset_fifo(st); - if (result) - return result; +/* Register PWR_MGMT_1 (0x6B) */ +static int nvi_pwr_mgmt_1_wr(struct inv_gyro_state_s *inf, unsigned char pm1) +{ + unsigned char val; + int i; + int err; + + for (i = 0; i < POWER_UP_TIME; i++) { + inv_i2c_single_write(inf, inf->reg->pwr_mgmt_1, pm1); + val = -1; + err = inv_i2c_read(inf, inf->reg->pwr_mgmt_1, 1, &val); + if (!val) + break; + + mdelay(1); + } + inf->hw.pwr_mgmt_1 = val; + return err; +} - inv_clear_kfifo(st); - st->chip_config.enable = 1; +static bool nvi_lpa_able(struct inv_gyro_state_s *inf) +{ + bool lpa_enable; + int i; + if (inf->mot_enable) { + lpa_enable = true; + } else if (inf->chip_config.lpa_delay_us) { + if (inf->chip_config.accl_delay_us < + inf->chip_config.lpa_delay_us) + lpa_enable = false; + else + lpa_enable = true; } else { - result = inv_i2c_single_write(st, reg->fifo_en, 0); - if (result) - return result; + lpa_enable = false; + } + for (i = 0; i < ARRAY_SIZE(nvi_lpa_delay_us_tbl); i++) { + if (inf->chip_config.accl_delay_us >= nvi_lpa_delay_us_tbl[i]) + break; + } + inf->lpa_hw = i; + return lpa_enable; +} - result = inv_i2c_single_write(st, reg->int_enable, 0); - if (result) - return result; +static int nvi_vreg_dis(struct inv_gyro_state_s *inf, unsigned int i) +{ + int err = 0; - /* disable fifo reading */ - if (INV_MPU3050 != st->chip_type) { - result = inv_i2c_single_write(st, reg->user_ctrl, 0); - if (result) - return result; + if (inf->vreg[i].ret && (inf->vreg[i].consumer != NULL)) { + err = regulator_disable(inf->vreg[i].consumer); + if (!err) { + inf->vreg[i].ret = 0; + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } else { + dev_err(&inf->i2c->dev, "%s %s ERR\n", + __func__, inf->vreg[i].supply); } + } + return err; +} + +static int nvi_vreg_dis_all(struct inv_gyro_state_s *inf) +{ + unsigned int i; + int err = 0; + + for (i = ARRAY_SIZE(nvi_vregs); i > 0; i--) + err |= nvi_vreg_dis(inf, (i - 1)); + return err; +} + +static int nvi_vreg_en(struct inv_gyro_state_s *inf, unsigned int i) +{ + int err = 0; - st->chip_config.enable = 0; + if ((!inf->vreg[i].ret) && (inf->vreg[i].consumer != NULL)) { + err = regulator_enable(inf->vreg[i].consumer); + if (!err) { + inf->vreg[i].ret = 1; + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + err = 1; /* flag regulator state change */ + } else { + dev_err(&inf->i2c->dev, "%s %s ERR\n", + __func__, inf->vreg[i].supply); + } } + return err; +} - return 0; +static int nvi_vreg_en_all(struct inv_gyro_state_s *inf) +{ + unsigned i; + int err = 0; + + for (i = 0; i < ARRAY_SIZE(nvi_vregs); i++) + err |= nvi_vreg_en(inf, i); + return err; } -static void inv_input_close(struct input_dev *d) +static void nvi_vreg_exit(struct inv_gyro_state_s *inf) { - struct inv_gyro_state_s *st; + int i; - st = input_get_drvdata(d); - set_inv_enable(st, 0); - inv_set_power_state(st, 0); + for (i = 0; i < ARRAY_SIZE(nvi_vregs); i++) { + if (inf->vreg[i].consumer != NULL) { + devm_regulator_put(inf->vreg[i].consumer); + inf->vreg[i].consumer = NULL; + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } + } } -/** - * inv_setup_input() - internal setup input device. - * @st: Device driver instance. - * @**idev_in pointer to input device - * @*client i2c client - * @*name name of the input device. - */ -static int inv_setup_input(struct inv_gyro_state_s *st, - struct input_dev **idev_in, - struct i2c_client *client, unsigned char *name) +static int nvi_vreg_init(struct inv_gyro_state_s *inf) { - int result; - struct input_dev *idev; + unsigned int i; + int err = 0; - idev = input_allocate_device(); - if (!idev) { - result = -ENOMEM; - return result; + for (i = 0; i < ARRAY_SIZE(nvi_vregs); i++) { + inf->vreg[i].supply = nvi_vregs[i]; + inf->vreg[i].ret = 0; + inf->vreg[i].consumer = devm_regulator_get(&inf->i2c->dev, + inf->vreg[i].supply); + if (IS_ERR(inf->vreg[i].consumer)) { + err = PTR_ERR(inf->vreg[i].consumer); + dev_err(&inf->i2c->dev, "%s ERR %d for %s\n", + __func__, err, inf->vreg[i].supply); + inf->vreg[i].consumer = NULL; + } else { + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } } + return err; +} - /* Setup input device. */ - idev->name = name; - idev->id.bustype = BUS_I2C; - idev->id.product = 'S'; - idev->id.vendor = ('I'<<8) | 'S'; - idev->id.version = 1; - idev->dev.parent = &client->dev; - /* Open and close method. */ - if (strcmp(name, "INV_DMP") && strcmp(name, "INV_COMPASS")) - idev->close = inv_input_close; - input_set_capability(idev, EV_REL, REL_X); - input_set_capability(idev, EV_REL, REL_Y); - input_set_capability(idev, EV_REL, REL_Z); - input_set_capability(idev, EV_REL, REL_RX); - input_set_capability(idev, EV_REL, REL_RY); - input_set_capability(idev, EV_REL, REL_RZ); - input_set_capability(idev, EV_REL, REL_MISC); - input_set_capability(idev, EV_REL, REL_WHEEL); - input_set_drvdata(idev, st); - result = input_register_device(idev); - if (result) - input_free_device(idev); - *idev_in = idev; - return result; +static int nvi_pm_wr_on(struct inv_gyro_state_s *inf, u8 pm1, u8 pm2) +{ + unsigned char val; + int i; + int err; + int err_t = 0; + + err = nvi_vreg_en_all(inf); + if (err) { + err_t |= nvi_pwr_mgmt_1_wr(inf, 0); + err_t |= inv_i2c_single_write(inf, inf->reg->pwr_mgmt_1, + BIT_RESET); + for (i = 0; i < POWER_UP_TIME; i++) { + val = -1; + err = inv_i2c_read(inf, inf->reg->pwr_mgmt_1, 1, &val); + if (!(val & BIT_RESET)) + break; + + mdelay(1); + } + err_t |= err; + err_t |= nvi_pwr_mgmt_1_wr(inf, 0); + err_t |= nvi_user_ctrl_reset_wr(inf, (BIT_FIFO_RST | + BIT_I2C_MST_RST)); + for (i = 0; i < AUX_PORT_MAX; i++) { + inf->aux.port[i].hw_valid = false; + inf->aux.port[i].hw_en = false; + } + memset(&inf->hw, 0, sizeof(struct nvi_hw)); + inf->sample_delay_us = 0; + inf->mot_enable = false; + } else { + err_t |= nvi_pwr_mgmt_1_wr(inf, 0); + } + if (pm2 != inf->hw.pwr_mgmt_2) { + err = inv_i2c_single_write(inf, inf->reg->pwr_mgmt_2, pm2); + if (err) + err_t |= err; + else + inf->hw.pwr_mgmt_2 = pm2; + } + if (pm1 != inf->hw.pwr_mgmt_1) { + err = inv_i2c_single_write(inf, inf->reg->pwr_mgmt_1, pm1); + if (err) + err_t |= err; + else + inf->hw.pwr_mgmt_1 = pm1; + } + return err_t; } -/** - * inv_init_config() - Initialize hardware, disable FIFO. - * @st: Device driver instance. - * Initial configuration: - * FSR: +/- 2000DPS - * DLPF: 42Hz - * FIFO rate: 50Hz - * Clock source: Gyro PLL - */ -static int inv_init_config(struct inv_gyro_state_s *st) +static int nvi_pm_wr(struct inv_gyro_state_s *inf, int pm, int stby) { - struct inv_reg_map_s *reg; - int result; + int err = 0; - if (st->chip_config.is_asleep) - return -EPERM; + if ((pm == inf->pm) && (stby == inf->stby)) + return err; - reg = st->reg; - result = set_inv_enable(st, 0); - if (result) - return result; + switch (pm) { + case NVI_PM_OFF_FORCE: + case NVI_PM_OFF: + err = nvi_pm_wr_on(inf, BIT_SLEEP, stby); + err |= nvi_vreg_dis_all(inf); + break; - result = inv_i2c_single_write(st, reg->gyro_config, - INV_FSR_2000DPS << 3); - if (result) - return result; + case NVI_PM_STDBY: + err = nvi_pm_wr_on(inf, BIT_SLEEP, stby); + break; - st->chip_config.fsr = INV_FSR_2000DPS; - result = inv_i2c_single_write(st, reg->lpf, INV_FILTER_42HZ); - if (result) - return result; + case NVI_PM_ON_CYCLE: + err = nvi_pm_wr_on(inf, BIT_CYCLE, stby); + break; - st->chip_config.lpf = INV_FILTER_42HZ; - result = inv_i2c_single_write(st, reg->sample_rate_div, 19); - if (result) - return result; + case NVI_PM_ON: + err = nvi_pm_wr_on(inf, INV_CLK_INTERNAL, stby); + break; - st->chip_config.fifo_rate = 50; - st->irq_dur_us = 20*1000; - st->chip_config.enable = 0; - st->chip_config.dmp_on = 0; - st->compass_divider = 0; - st->compass_counter = 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; - if (INV_ITG3500 != st->chip_type) { - 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 << 3)); - if (result) - return result; + case NVI_PM_ON_FULL: + err = nvi_pm_wr_on(inf, INV_CLK_PLL, stby); + break; - } else { - st->chip_config.accl_enable = 0; - st->chip_config.accl_fifo_enable = 0; + default: + err = -EINVAL; + break; } - return 0; + if (err < 0) { + dev_err(&inf->i2c->dev, "%s requested pm=%d ERR=%d\n", + __func__, pm, err); + pm = NVI_PM_ERR; + } else { + inf->stby = stby; + } + inf->pm = pm; + dev_dbg(&inf->i2c->dev, "%s pm=%d stby=%x\n", __func__, pm, stby); + if (err > 0) + err = 0; + return err; } -/** - * inv_raw_gyro_show() - Read gyro data directly from registers. - */ -static ssize_t inv_raw_gyro_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int nvi_pm(struct inv_gyro_state_s *inf, int pm_req) { - struct inv_gyro_state_s *st; - struct inv_reg_map_s *reg; - int result; - unsigned char data[6]; - - st = dev_get_drvdata(dev); - reg = st->reg; - if (st->chip_config.is_asleep) - return -EPERM; - - if (0 == st->chip_config.gyro_enable) - return -EPERM; + bool irq; + int stby; + int pm; + int err; - result = inv_i2c_read(st, reg->raw_gyro, 6, data); - if (result) { - printk(KERN_ERR "Could not read raw registers.\n"); - return result; + nvi_int_enable_wr(inf, false); + inf->lpa_enable = false; + if ((pm_req == NVI_PM_OFF_FORCE) || (pm_req == NVI_PM_OFF)) { + stby = 0x3F; + pm = NVI_PM_OFF_FORCE; + } else { + stby = ((~inf->chip_config.accl_enable) & 0x07) << 3; + stby |= (~inf->chip_config.gyro_enable) & 0x07; + if (inf->chip_config.gyro_enable || + (inf->hw.user_ctrl & BIT_I2C_MST_EN)) { + if (inf->chip_config.gyro_enable) + pm = NVI_PM_ON_FULL; + else + pm = NVI_PM_ON; + } else if (inf->chip_config.accl_enable) { + if (nvi_lpa_able(inf)) { + inf->lpa_enable = true; + stby |= (inf->lpa_hw << 6); + pm = NVI_PM_ON_CYCLE; + } else { + pm = NVI_PM_ON; + } + } else if (inf->chip_config.enable || inf->aux.bypass_lock) { + pm = NVI_PM_STDBY; + } else { + pm = NVI_PM_OFF; + } + } + if (pm_req > pm) + pm = pm_req; + err = nvi_pm_wr(inf, pm, stby); + if (pm_req == NVI_PM_AUTO) { + nvi_user_ctrl_en_wr(inf, true, true); + irq = true; + } else { + irq = false; } + nvi_int_enable_wr(inf, irq); + return err; +} - return sprintf(buf, "%d %d %d %lld\n", - (signed short)(be16_to_cpup((short *)&data[0])), - (signed short)(be16_to_cpup((short *)&data[2])), - (signed short)(be16_to_cpup((short *)&data[4])), - get_time_ns()); +static void nvi_pm_exit(struct inv_gyro_state_s *inf) +{ + nvi_pm(inf, NVI_PM_OFF_FORCE); + nvi_vreg_exit(inf); } -/** - * inv_raw_accl_show() - Read accel data directly from registers. - */ -static ssize_t inv_raw_accl_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int nvi_pm_init(struct inv_gyro_state_s *inf) { - struct inv_gyro_state_s *st; - struct inv_reg_map_s *reg; - int result; - unsigned char data[6]; + int err = 0; - st = dev_get_drvdata(dev); - reg = st->reg; - if (st->chip_config.is_asleep) - return -EPERM; + nvi_vreg_init(inf); + inf->pm = NVI_PM_ERR; + inf->stby = 0; + err = nvi_pm(inf, NVI_PM_ON_FULL); + return err; +} - if (0 == st->chip_config.accl_enable) - return -EPERM; +static int nvi_motion_detect_enable(struct inv_gyro_state_s *inf, u8 mot_thr) +{ + int err; + int err_t = 0; + + if (mot_thr) { + err = nvi_accel_config_wr(inf, inf->chip_config.accl_fsr, 0); + if (err < 0) + err_t |= err; + err_t |= nvi_config_wr(inf, 0); + if (!inf->hw.mot_dur) + err_t |= nvi_mot_dur_wr(inf, 1); + err_t |= nvi_mot_detect_ctrl_wr(inf, inf->chip_config.mot_ctrl); + err_t |= nvi_mot_thr_wr(inf, mot_thr); + mdelay(5); + err = nvi_accel_config_wr(inf, inf->chip_config.accl_fsr, 7); + if (err < 0) + err_t |= err; + if (!err_t) + inf->mot_enable = true; + } else { + err = nvi_accel_config_wr(inf, inf->chip_config.accl_fsr, 0); + if (err < 0) + err_t |= err; + } + return err_t; +} - result = inv_i2c_read(st, reg->raw_accl, 6, data); - if (result) { - printk(KERN_ERR "Could not read raw registers.\n"); - return result; +static int nvi_aux_delay(struct inv_gyro_state_s *inf, + int port, unsigned int delay_ms) +{ + unsigned char val; + unsigned char i; + unsigned int ms; + unsigned int delay_new; + int delay_rtn; + int err; + + if (port != AUX_PORT_BYPASS) + inf->aux.port[port].nmp.delay_ms = delay_ms; + /* determine valid delays by ports enabled */ + delay_new = 0; + delay_rtn = 0; + for (i = 0; i < AUX_PORT_MAX; i++) { + if (delay_rtn < inf->aux.port[i].nmp.delay_ms) + delay_rtn = inf->aux.port[i].nmp.delay_ms; + if (inf->aux.port[i].hw_en) { + if (delay_new < inf->aux.port[i].nmp.delay_ms) + delay_new = inf->aux.port[i].nmp.delay_ms; + } + } + if (!(inf->hw.user_ctrl & BIT_I2C_MST_EN)) { + /* delay will execute when re-enabled */ + if (delay_ms) + return delay_rtn; + else + return 0; } - return sprintf(buf, "%d %d %d %lld\n", - ((signed short)(be16_to_cpup((short *)&data[0]))* - st->chip_info.multi), - ((signed short)(be16_to_cpup((short *)&data[2]))* - st->chip_info.multi), - ((signed short)(be16_to_cpup((short *)&data[4]))* - st->chip_info.multi), - get_time_ns()); + /* HW global delay */ + for (i = 1; i < (BITS_I2C_MST_DLY - 1); i++) { + ms = (inf->sample_delay_us - (inf->sample_delay_us / + (i + 1))) * 1000; + if (ms >= delay_new) + break; + } + inf->aux.delay_hw = i; + err = nvi_i2c_slv4_ctrl_wr(inf, inf->aux.port[AUX_PORT_SPECIAL].hw_en); + /* HW port delay enable */ + val = BIT_DELAY_ES_SHADOW; + for (i = 0; i < AUX_PORT_MAX; i++) { + if (inf->aux.port[i].nmp.delay_ms) + val |= (1 << i); + } + nvi_i2c_mst_delay_ctrl_wr(inf, val); + if (delay_ms) + return delay_rtn; + else + return 0; } -/** - * inv_temperature_show() - Read temperature data directly from registers. - */ -static ssize_t inv_temperature_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int nvi_global_delay(struct inv_gyro_state_s *inf) { - struct inv_gyro_state_s *st; - struct inv_reg_map_s *reg; - int result; - unsigned char data[2]; + unsigned long delay_us; + unsigned long delay_min; + unsigned char val; + int i; + int err = 0; - st = dev_get_drvdata(dev); - reg = st->reg; - if (st->chip_config.is_asleep) - return -EPERM; + /* find the fastest polling of all the devices */ + delay_us = -1; + for (i = 0; i < AUX_PORT_MAX; i++) { + if (inf->aux.port[i].enable && inf->aux.port[i].nmp.delay_us) { + if (inf->aux.port[i].nmp.delay_us < delay_us) + delay_us = inf->aux.port[i].nmp.delay_us; + } + } + if (inf->chip_config.gyro_enable && inf->chip_config.gyro_delay_us) { + if (inf->chip_config.gyro_delay_us < delay_us) + delay_us = inf->chip_config.gyro_delay_us; + } + if (inf->chip_config.accl_enable && inf->chip_config.accl_delay_us) { + if (inf->chip_config.accl_delay_us < delay_us) + delay_us = inf->chip_config.accl_delay_us; + } + if (delay_us == -1) + delay_us = NVI_DELAY_DEFAULT; /* default if nothing found */ + /* find what the fastest the external devices will let MPU go */ + delay_min = 0; + for (i = 0; i < AUX_PORT_MAX; i++) { + if (inf->aux.port[i].enable) { + if (inf->aux.port[i].nmp.delay_ms > delay_min) + delay_min = inf->aux.port[i].nmp.delay_ms; + } + } + if (!delay_min) + /* default if nothing found */ + delay_min = inf->chip_config.min_delay_us; + else + delay_min *= 1000L; /* ms => us */ + /* set the limits */ + if (delay_us < delay_min) + delay_us = delay_min; + if (delay_us > MAX_FIFO_RATE) + delay_us = MAX_FIFO_RATE; + /* program if new value */ + if (delay_us != inf->sample_delay_us) { + dev_dbg(&inf->i2c->dev, "%s %lu\n", __func__, delay_us); + inf->sample_delay_us = delay_us; + inf->irq_dur_us = delay_us; + delay_us <<= 1; + for (val = 1; val < ARRAY_SIZE(nvi_lpf_us_tbl); val++) { + if (delay_us < nvi_lpf_us_tbl[val]) + break; + } + err |= nvi_config_wr(inf, val); + if (val) + delay_min = 1000; + else + delay_min = 8000; + val = inf->sample_delay_us / delay_min - 1; + err |= nvi_smplrt_div_wr(inf, val); + inf->last_isr_time = get_time_ns(); + } + nvi_aux_delay(inf, AUX_PORT_BYPASS, 0); + return err; +} - result = inv_i2c_read(st, reg->temperature, 2, data); - if (result) { - printk(KERN_ERR "Could not read temperature register.\n"); - return result; +static void nvi_aux_dbg(struct inv_gyro_state_s *inf, char *tag, int val) +{ + struct nvi_mpu_port *n; + struct aux_port *p; + struct aux_ports *a; + unsigned char data[4]; + int i; + + if (!inf->aux.dbg) + return; + + pr_info("%s %s %d\n", __func__, tag, val); + for (i = 0; i < AUX_PORT_MAX; i++) { + inv_i2c_read(inf, (REG_I2C_SLV0_ADDR + (i * 3)), 3, data); + inv_i2c_read(inf, (REG_I2C_SLV0_DO + i), 1, &data[3]); + pr_info("PT=%d AD=%x RG=%x CL=%x DO=%x\n", + i, data[0], data[1], data[2], data[3]); + n = &inf->aux.port[i].nmp; + pr_info("PT=%d AD=%x RG=%x CL=%x DO=%x MS=%u US=%lu SB=%x\n", + i, n->addr, n->reg, n->ctrl, n->data_out, n->delay_ms, + n->delay_us, n->shutdown_bypass); + p = &inf->aux.port[i]; + pr_info("PT=%d OF=%u EN=%x FE=%x HE=%x HD=%x HV=%x NS=%lld\n", + i, p->ext_data_offset, p->enable, p->fifo_en, p->hw_en, + p->hw_do, p->hw_valid, p->delay_ns); + } + a = &inf->aux; + pr_info("EN=%x GE=%x MD=%x GD=%lu DN=%u BE=%x BL=%d SB=%d\n", + a->enable, (inf->hw.user_ctrl & BIT_I2C_MST_EN), + (inf->hw.i2c_slv4_ctrl & BITS_I2C_MST_DLY), + inf->sample_delay_us, a->ext_data_n, + (inf->hw.int_pin_cfg & BIT_BYPASS_EN), a->bypass_lock, + atomic_read(&inf->mutex.count)); +} + +static void nvi_aux_read(struct inv_gyro_state_s *inf) +{ + struct aux_port *ap; + long long timestamp1; + long long timestamp2; + unsigned int i; + unsigned int len; + u8 *p; + int err; + + if ((inf->aux.ext_data_n == 0) || + (!(inf->hw.user_ctrl & BIT_I2C_MST_EN))) + return; + + timestamp1 = get_time_ns(); + err = inv_i2c_read(inf, REG_EXT_SENS_DATA_00, + inf->aux.ext_data_n, + (unsigned char *)&inf->aux.ext_data); + if (err) + return; + + timestamp2 = get_time_ns(); + timestamp1 = timestamp1 + ((timestamp2 - timestamp1) / 2); + for (i = 0; i < AUX_PORT_SPECIAL; i++) { + ap = &inf->aux.port[i]; + if ((ap->nmp.addr & BIT_I2C_READ) && + (ap->nmp.handler != NULL)) { + if ((unsigned long)(timestamp2 - ap->delay_ns) + >= (ap->nmp.delay_us * 1000)) { + ap->delay_ns = timestamp2; + p = &inf->aux.ext_data[ap->ext_data_offset]; + len = ap->nmp.ctrl & BITS_I2C_SLV_CTRL_LEN; + ap->nmp.handler(p, len, timestamp1, + ap->nmp.ext_driver); + } + } } +} - return sprintf(buf, "%d %lld\n", - (signed short)(be16_to_cpup((short *)&data[0])), - get_time_ns()); +static void nvi_aux_ext_data_offset(struct inv_gyro_state_s *inf) +{ + int i; + unsigned short offset; + + offset = 0; + for (i = 0; i < AUX_PORT_SPECIAL; i++) { + if ((inf->aux.port[i].hw_en) && (inf->aux.port[i].nmp.addr & + BIT_I2C_READ)) { + inf->aux.port[i].ext_data_offset = offset; + offset += (inf->aux.port[i].nmp.ctrl & + BITS_I2C_SLV_CTRL_LEN); + } + } + if (offset > AUX_EXT_DATA_REG_MAX) { + offset = AUX_EXT_DATA_REG_MAX; + dev_err(&inf->i2c->dev, + "%s ERR MPU slaves exceed data storage\n", __func__); + } + inf->aux.ext_data_n = offset; + return; } -static int inv_set_lpf(struct inv_gyro_state_s *st, int rate) +static int nvi_aux_port_do(struct inv_gyro_state_s *inf, + int port, u8 data_out) { - short hz[6] = {188, 98, 42, 20, 10, 5}; - int d[6] = {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; + unsigned char reg; + int err; - reg = st->reg; - h = (rate >> 1); - i = 0; - while ((h < hz[i]) && (i < 6)) - i++; - data = d[i]; - if (INV_MPU3050 == st->chip_type) { - if (st->mpu_slave != NULL) { - result = st->mpu_slave->set_lpf(st, rate); - if (result) - return result; - } + if (port == AUX_PORT_SPECIAL) + reg = REG_I2C_SLV4_DO; + else + reg = (REG_I2C_SLV0_DO + port); + err = inv_i2c_single_write(inf, reg, data_out); + return err; +} - result = inv_i2c_single_write(st, reg->lpf, - data | (st->chip_config.fsr << 3)); +static int nvi_aux_port_data_out(struct inv_gyro_state_s *inf, + int port, u8 data_out) +{ + int err; + err = nvi_aux_port_do(inf, port, data_out); + if (!err) { + inf->aux.port[port].nmp.data_out = data_out; + inf->aux.port[port].hw_do = true; } else { - result = inv_i2c_single_write(st, reg->lpf, data); + inf->aux.port[port].hw_do = false; } + return err; +} - if (result) - return result; +static int nvi_aux_port_wr(struct inv_gyro_state_s *inf, int port) +{ + struct aux_port *ap; + int err; - st->chip_config.lpf = data; - return 0; + ap = &inf->aux.port[port]; + err = inv_i2c_single_write(inf, (REG_I2C_SLV0_ADDR + (port * 3)), + ap->nmp.addr); + err |= inv_i2c_single_write(inf, (REG_I2C_SLV0_REG + (port * 3)), + ap->nmp.reg); + err |= nvi_aux_port_do(inf, port, ap->nmp.data_out); + return err; } -/** - * 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) +static int nvi_aux_port_en(struct inv_gyro_state_s *inf, + int port, bool en) { - unsigned long fifo_rate; - unsigned char data; - int result; - struct inv_gyro_state_s *st; - struct inv_reg_map_s *reg; + struct aux_port *ap; + unsigned char reg; + unsigned char val; + int err = 0; - st = dev_get_drvdata(dev); - reg = st->reg; - if (st->chip_config.is_asleep) - return -EPERM; + inf->aux.ext_data_n = 0; + ap = &inf->aux.port[port]; + if ((!ap->hw_valid) && en) { + err = nvi_aux_port_wr(inf, port); + if (!err) { + ap->hw_valid = true; + ap->hw_do = true; + } + } + if ((!ap->hw_do) && en) + nvi_aux_port_data_out(inf, port, ap->nmp.data_out); + if (port == AUX_PORT_SPECIAL) { + err = nvi_i2c_slv4_ctrl_wr(inf, en); + } else { + reg = (REG_I2C_SLV0_CTRL + (port * 3)); + if (en) + val = (ap->nmp.ctrl | BIT_SLV_EN); + else + val = 0; + err = inv_i2c_single_write(inf, reg, val); + } + if (!err) { + ap->hw_en = en; + nvi_aux_ext_data_offset(inf); + } + return err; +} - if (kstrtoul(buf, 10, &fifo_rate)) - return -EINVAL; +static int nvi_aux_enable(struct inv_gyro_state_s *inf, bool enable) +{ + bool en; + unsigned int i; + int err; - if ((fifo_rate < MIN_FIFO_RATE) || (fifo_rate > MAX_FIFO_RATE)) - return -EINVAL; + if (inf->hw.int_pin_cfg & BIT_BYPASS_EN) + enable = false; - if (fifo_rate == st->chip_config.fifo_rate) - return count; - - if (st->has_compass) { - data = 10*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; + en = false; + if (enable) { + /* global enable is honored only if a port is enabled */ + for (i = 0; i < AUX_PORT_MAX; i++) { + if (inf->aux.port[i].enable) { + en = true; + break; + } + } + if (en == (inf->hw.user_ctrl & BIT_I2C_MST_EN)) { + /* if already on then just update delays */ + nvi_global_delay(inf); + } } + inf->aux.enable = en; + if ((inf->hw.user_ctrl & BIT_I2C_MST_EN) == en) + return 0; - 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 = get_time_ns(); - return count; + if (en) { + for (i = 0; i < AUX_PORT_MAX; i++) { + if (inf->aux.port[i].enable) + err |= nvi_aux_port_en(inf, i, true); + } + nvi_motion_detect_enable(inf, 0); + } else { + for (i = 0; i < AUX_PORT_MAX; i++) { + if (inf->aux.port[i].hw_valid) + nvi_aux_port_en(inf, i, false); + } + } + err = nvi_global_delay(inf); + err |= nvi_user_ctrl_en_wr(inf, true, en); + return err; } -/** - * 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) +static int nvi_aux_port_enable(struct inv_gyro_state_s *inf, + int port, bool enable, bool fifo_enable) { - int result; - unsigned long power_state; - struct inv_gyro_state_s *st; + struct aux_port *ap; + int err; - st = dev_get_drvdata(dev); - if (kstrtoul(buf, 10, &power_state)) - return -EINVAL; - - if (!power_state == st->chip_config.is_asleep) - return count; + ap = &inf->aux.port[port]; + ap->enable = enable; + ap->fifo_en = false; + if (enable && (inf->hw.int_pin_cfg & BIT_BYPASS_EN)) + return 0; - result = inv_set_power_state(st, power_state); - return count; + err = nvi_aux_port_en(inf, port, enable); + err |= nvi_aux_enable(inf, true); + return err; } -/** - * inv_enable_store() - Enable/disable chip operation. - */ -static ssize_t inv_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int nvi_reset(struct inv_gyro_state_s *inf, + bool reset_fifo, bool reset_i2c) { - unsigned long enable; - struct inv_gyro_state_s *st; - int result; + unsigned long flags; + unsigned char val; + int i; + int err; + + err = nvi_int_enable_wr(inf, false); + val = 0; + if (reset_i2c) { + /* nvi_aux_bypass_enable(inf, false)? */ + err |= nvi_aux_enable(inf, false); + for (i = 0; i < AUX_PORT_MAX; i++) { + inf->aux.port[i].hw_valid = false; + inf->aux.port[i].hw_en = false; + } + inf->aux.need_reset = false; + val |= BIT_I2C_MST_RST; + } + if (reset_fifo) + val |= BIT_FIFO_RST; + err |= nvi_user_ctrl_en_wr(inf, ~reset_fifo, ~reset_i2c); + val |= inf->hw.user_ctrl; + err |= nvi_user_ctrl_reset_wr(inf, val); + if (reset_fifo) { + spin_lock_irqsave(&inf->time_stamp_lock, flags); + kfifo_reset(&inf->trigger.timestamps); + spin_unlock_irqrestore(&inf->time_stamp_lock, flags); + inf->last_isr_time = get_time_ns(); + } + if (reset_i2c) + err |= nvi_aux_enable(inf, true); + else + err |= nvi_user_ctrl_en_wr(inf, true, true); + err |= nvi_int_enable_wr(inf, true); + return err; +} - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; +static int nvi_aux_port_free(struct inv_gyro_state_s *inf, int port) +{ + bool hw_valid; + int err = 0; - if (kstrtoul(buf, 10, &enable)) - return -EINVAL; + hw_valid = inf->aux.port[port].hw_valid; + memset(&inf->aux.port[port], 0, sizeof(struct aux_port)); + if (hw_valid) { + nvi_aux_port_wr(inf, port); + nvi_aux_port_en(inf, port, false); + nvi_aux_enable(inf, false); + nvi_aux_enable(inf, true); + if (port != AUX_PORT_SPECIAL) + inf->aux.need_reset = true; + } + return err; +} - if (!enable == !st->chip_config.enable) - return count; +static int nvi_aux_port_alloc(struct inv_gyro_state_s *inf, + struct nvi_mpu_port *nmp, int port) +{ + int i; - result = set_inv_enable(st, enable); - if (result) - return result; + if (inf->aux.need_reset) + nvi_reset(inf, false, true); + if (port < 0) { + for (i = 0; i < AUX_PORT_SPECIAL; i++) { + if (inf->aux.port[i].nmp.addr == 0) + break; + } + if (i == AUX_PORT_SPECIAL) + return -ENODEV; + } else { + if (inf->aux.port[port].nmp.addr == 0) + i = port; + else + return -ENODEV; + } - return count; + memset(&inf->aux.port[i], 0, sizeof(struct aux_port)); + memcpy(&inf->aux.port[i].nmp, nmp, sizeof(struct nvi_mpu_port)); + return i; } -/** - * inv_accl_fifo_enable_store() - Enable/disable accl fifo output. - */ -static ssize_t inv_accl_fifo_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int nvi_aux_bypass_enable(struct inv_gyro_state_s *inf, bool enable) { - unsigned long data, en; - int result; - struct inv_gyro_state_s *st; + unsigned char val; + int err; - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + if ((bool)(inf->hw.int_pin_cfg & BIT_BYPASS_EN) == enable) + return 0; - if (st->chip_config.enable) - return -EPERM; + val = inf->hw.int_pin_cfg; + if (enable) { + err = nvi_aux_enable(inf, false); + if (!err) { + val |= BIT_BYPASS_EN; + err = nvi_int_pin_cfg_wr(inf, val); + } + } else { + val &= ~BIT_BYPASS_EN; + err = nvi_int_pin_cfg_wr(inf, val); + if (!err) + nvi_aux_enable(inf, true); + } + return err; +} - result = kstrtoul(buf, 10, &data); - if (result) - return -EINVAL; +static int nvi_aux_bypass_request(struct inv_gyro_state_s *inf, bool enable) +{ + int err = 0; - if (data) - en = 1; - else - en = 0; - if (en == st->chip_config.accl_fifo_enable) - return count; + if ((bool)(inf->hw.int_pin_cfg & BIT_BYPASS_EN) == enable) { + inf->aux.bypass_lock++; + } else { + if (inf->aux.bypass_lock) { + err = -EBUSY; + } else { + err = nvi_aux_bypass_enable(inf, enable); + if (err) + dev_err(&inf->i2c->dev, "%s ERR=%d\n", + __func__, err); + else + inf->aux.bypass_lock++; + } + } + return err; +} - if (en && (0 == st->chip_config.accl_enable)) { - result = inv_set_power_state(st, 0); - if (result) - return result; +static int nvi_aux_bypass_release(struct inv_gyro_state_s *inf) +{ + int err; - st->chip_config.accl_enable = en; - result = inv_set_power_state(st, 1); - if (result) - return result; + if (inf->aux.bypass_lock) + inf->aux.bypass_lock--; + if (!inf->aux.bypass_lock) { + err = nvi_aux_bypass_enable(inf, false); + if (err) + dev_err(&inf->i2c->dev, "%s ERR=%d\n", __func__, err); } - - st->chip_config.accl_fifo_enable = en; - return count; + return 0; } -/** - * inv_gyro_fifo_enable_store() - Enable/disable gyro fifo output. - */ -ssize_t inv_gyro_fifo_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int nvi_aux_dev_valid(struct inv_gyro_state_s *inf, + struct nvi_mpu_port *nmp, u8 *data) { - unsigned long data, en; - int result; - struct inv_gyro_state_s *st; + unsigned char val; + int i; + int err; + + /* turn off bypass */ + err = nvi_aux_bypass_request(inf, false); + if (err) + return -EBUSY; + + /* grab the special port */ + err = nvi_aux_port_alloc(inf, nmp, AUX_PORT_SPECIAL); + if (err != AUX_PORT_SPECIAL) { + nvi_aux_bypass_release(inf); + return -EBUSY; + } - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + /* enable it */ + inf->aux.port[AUX_PORT_SPECIAL].nmp.delay_ms = 0; + err = nvi_aux_port_enable(inf, AUX_PORT_SPECIAL, true, false); + if (err) { + nvi_aux_port_free(inf, AUX_PORT_SPECIAL); + nvi_aux_bypass_release(inf); + return -EBUSY; + } - if (st->chip_config.enable) - return -EPERM; + /* now turn off all the other ports for fastest response */ + for (i = 0; i < AUX_PORT_SPECIAL; i++) { + if (inf->aux.port[i].hw_valid) + nvi_aux_port_en(inf, i, false); + } + /* start reading the results */ + for (i = 0; i < AUX_DEV_VALID_READ_MAX; i++) { + mdelay(1); + val = 0; + err = inv_i2c_read(inf, REG_I2C_MST_STATUS, 1, &val); + if (err) + continue; - result = kstrtoul(buf, 10, &data); - if (result) - return -EINVAL; + if (val & 0x50) + break; + } + /* these will restore all previously disabled ports */ + nvi_aux_bypass_release(inf); + nvi_aux_port_free(inf, AUX_PORT_SPECIAL); + if (i == AUX_DEV_VALID_READ_MAX) + return -ENODEV; - if (data) - en = 1; - else - en = 0; - if (en == st->chip_config.gyro_fifo_enable) - return count; + if (val & 0x10) /* NACK */ + return -EIO; - if (en && (0 == st->chip_config.gyro_enable)) { - result = inv_set_power_state(st, 0); - if (result) - return result; + if (nmp->addr & BIT_I2C_READ) { + err = inv_i2c_read(inf, REG_I2C_SLV4_DI, 1, &val); + if (err) + return -EBUSY; - st->chip_config.gyro_enable = en; - result = inv_set_power_state(st, 1); - if (result) - return result; + *data = (u8)val; + dev_info(&inf->i2c->dev, "%s MPU read 0x%x from device 0x%x\n", + __func__, val, (nmp->addr & ~BIT_I2C_READ)); + } else { + dev_info(&inf->i2c->dev, "%s MPU found device 0x%x\n", + __func__, (nmp->addr & ~BIT_I2C_READ)); } - - st->chip_config.gyro_fifo_enable = en; - return count; + return 0; } -/** - * inv_gyro_enable_store() - Enable/disable gyro. - */ -ssize_t inv_gyro_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int nvi_aux_mpu_call_pre(struct inv_gyro_state_s *inf, int port) { - unsigned long data, en; - struct inv_gyro_state_s *st; - int result; - - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + if ((port < 0) || (port >= AUX_PORT_SPECIAL)) + return -EINVAL; - if (st->chip_config.enable) + if (inf->shutdown) return -EPERM; - result = kstrtoul(buf, 10, &data); - if (result) + if (!inf->aux.port[port].nmp.addr) return -EINVAL; - if (data) - en = 1; - else - en = 0; - if (en == st->chip_config.gyro_enable) - return count; - - if (0 == en) - st->chip_config.gyro_fifo_enable = 0; - result = inv_set_power_state(st, 0); - if (result) - return result; - - st->chip_config.gyro_enable = en; - result = inv_set_power_state(st, 1); - if (result) { - st->chip_config.gyro_enable ^= 1; - return result; - } + mutex_lock(&inf->mutex); + return 0; +} - return count; +static int nvi_aux_mpu_call_post(struct inv_gyro_state_s *inf, + char *tag, int err) +{ + if (err < 0) + err = -EBUSY; + mutex_unlock(&inf->mutex); + nvi_aux_dbg(inf, tag, err); + return err; } -/** - * inv_accl_enable_store() - Enable/disable accl. +/* See the mpu.h file for details on the nvi_mpu_ calls. */ -static ssize_t inv_accl_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +int nvi_mpu_dev_valid(struct nvi_mpu_port *nmp, u8 *data) { - unsigned long data, en; - struct inv_gyro_state_s *st; - int result; - - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + struct inv_gyro_state_s *inf; + int err; - if (st->chip_config.enable) - return -EPERM; + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s\n", __func__); + } else { + pr_debug("%s\n", __func__); + return -EAGAIN; + } - result = kstrtoul(buf, 10, &data); - if (result) + if (nmp == NULL) return -EINVAL; - if (data) - en = 1; - else - en = 0; - if (en == st->chip_config.accl_enable) - return count; - - if (0 == en) - st->chip_config.accl_fifo_enable = 0; - result = inv_set_power_state(st, 0); - if (result) - return result; + if ((nmp->addr & BIT_I2C_READ) && (data == NULL)) + return -EINVAL; - st->chip_config.accl_enable = en; - result = inv_set_power_state(st, 1); - if (result) { - st->chip_config.accl_enable ^= 1; - return result; - } + if (inf->shutdown) + return -EPERM; - return count; + mutex_lock(&inf->mutex); + nvi_pm(inf, NVI_PM_ON); + err = nvi_aux_dev_valid(inf, nmp, data); + nvi_pm(inf, NVI_PM_AUTO); + mutex_unlock(&inf->mutex); + nvi_aux_dbg(inf, "nvi_mpu_dev_valid err: ", err); + return err; } +EXPORT_SYMBOL(nvi_mpu_dev_valid); -/** - * inv_gyro_fs_store() - Change the gyro full-scale range (and scale factor). - */ -static ssize_t inv_gyro_fs_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +int nvi_mpu_port_alloc(struct nvi_mpu_port *nmp) { - unsigned long fsr; - int result; - struct inv_gyro_state_s *st; - struct inv_reg_map_s *reg; + struct inv_gyro_state_s *inf; + int err; - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s\n", __func__); + } else { + pr_debug("%s\n", __func__); + return -EAGAIN; + } - result = kstrtoul(buf, 10, &fsr); - if (result) + if (nmp == NULL) return -EINVAL; - if (fsr > 3) + if (!(nmp->ctrl & BITS_I2C_SLV_CTRL_LEN)) return -EINVAL; - if (fsr == st->chip_config.fsr) - return count; + if (inf->shutdown) + return -EPERM; - reg = st->reg; - if (INV_MPU3050 == st->chip_type) - result = inv_i2c_single_write(st, reg->lpf, - (fsr << 3) | st->chip_config.lpf); - else - result = inv_i2c_single_write(st, reg->gyro_config, - fsr << 3); - if (result) - return result; + mutex_lock(&inf->mutex); + nvi_pm(inf, NVI_PM_ON); + err = nvi_aux_port_alloc(inf, nmp, -1); + nvi_pm(inf, NVI_PM_AUTO); + err = nvi_aux_mpu_call_post(inf, "nvi_mpu_port_alloc err/port: ", err); + return err; +} +EXPORT_SYMBOL(nvi_mpu_port_alloc); - st->chip_config.fsr = fsr; - return count; +int nvi_mpu_port_free(int port) +{ + struct inv_gyro_state_s *inf; + int err; + + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s port %d\n", __func__, port); + } else { + pr_debug("%s port %d\n", __func__, port); + return -EAGAIN; + } + + err = nvi_aux_mpu_call_pre(inf, port); + if (err) + return err; + + nvi_pm(inf, NVI_PM_ON); + err = nvi_aux_port_free(inf, port); + nvi_pm(inf, NVI_PM_AUTO); + err = nvi_aux_mpu_call_post(inf, "nvi_mpu_port_free err: ", err); + return err; } +EXPORT_SYMBOL(nvi_mpu_port_free); -/** - * inv_accl_fs_store() - Configure the accelerometer's scale range. - */ -ssize_t inv_accl_fs_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +int nvi_mpu_enable(int port, bool enable, bool fifo_enable) { - unsigned long fs; - int result; - struct inv_gyro_state_s *st; - struct inv_reg_map_s *reg; + struct inv_gyro_state_s *inf; + int err; - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s port %d: %x\n", __func__, port, enable); + } else { + pr_debug("%s port %d: %x\n", __func__, port, enable); + return -EAGAIN; + } - if (kstrtoul(buf, 10, &fs)) - return -EINVAL; + err = nvi_aux_mpu_call_pre(inf, port); + if (err) + return err; - if (fs > 3) - return -EINVAL; + nvi_pm(inf, NVI_PM_ON); + err = nvi_aux_port_enable(inf, port, enable, fifo_enable); + nvi_pm(inf, NVI_PM_AUTO); + err = nvi_aux_mpu_call_post(inf, "nvi_mpu_enable err: ", err); + return err; +} +EXPORT_SYMBOL(nvi_mpu_enable); - if (fs == st->chip_config.accl_fs) - return count; +int nvi_mpu_delay_ms(int port, u8 delay_ms) +{ + struct inv_gyro_state_s *inf; + int err; - reg = st->reg; - if ((INV_MPU3050 == st->chip_type) && (st->mpu_slave != NULL)) - result = st->mpu_slave->set_fs(st, fs); - else - result = inv_i2c_single_write(st, reg->accl_config, (fs << 3)); - if (result) - return result; + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s port %d: %u\n", __func__, port, delay_ms); + } else { + pr_debug("%s port %d: %u\n", __func__, port, delay_ms); + return -EAGAIN; + } - /* reset fifo because the data could be mixed with old bad data */ - st->chip_config.accl_fs = fs; - return count; + err = nvi_aux_mpu_call_pre(inf, port); + if (err) + return err; + + if (inf->aux.port[port].hw_en) { + err = nvi_aux_delay(inf, port, delay_ms); + nvi_global_delay(inf); + } else { + inf->aux.port[port].nmp.delay_ms = delay_ms; + } + err = nvi_aux_mpu_call_post(inf, "nvi_mpu_delay_ms err: ", err); + return err; } +EXPORT_SYMBOL(nvi_mpu_delay_ms); -/** - * 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) +int nvi_mpu_delay_us(int port, unsigned long delay_us) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned long data, result; + struct inv_gyro_state_s *inf; + int err; - result = kstrtoul(buf, 10, &data); - if (result) - return result; + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s port %d: %lu\n", __func__, port, delay_us); + } else { + pr_debug("%s port %d: %lu\n", __func__, port, delay_us); + return -EAGAIN; + } - if (data != 0) - return -EINVAL; + err = nvi_aux_mpu_call_pre(inf, port); + if (err) + return err; - st->chip_config.firmware_loaded = 0; - st->chip_config.dmp_on = 0; - return count; + inf->aux.port[port].nmp.delay_us = delay_us; + if (inf->aux.port[port].hw_en) + err = nvi_global_delay(inf); + err = nvi_aux_mpu_call_post(inf, "nvi_mpu_delay_us err: ", err); + return err; } +EXPORT_SYMBOL(nvi_mpu_delay_us); -/** - * 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) +int nvi_mpu_data_out(int port, u8 data_out) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned long result, lpa_mode; - unsigned char d; - struct inv_reg_map_s *reg; + struct inv_gyro_state_s *inf; + int err = 0; - if (st->chip_config.is_asleep) - return -EPERM; + inf = inf_local; + if (inf == NULL) + return -EAGAIN; - result = kstrtoul(buf, 10, &lpa_mode); - if (result) - return result; + err = nvi_aux_mpu_call_pre(inf, port); + if (err) + return err; - reg = st->reg; - result = inv_i2c_read(st, reg->pwr_mgmt_1, 1, &d); - if (result) - return result; + if (inf->aux.port[port].hw_en) { + err = nvi_aux_port_data_out(inf, port, data_out); + } else { + inf->aux.port[port].nmp.data_out = data_out; + inf->aux.port[port].hw_do = false; + } + if (err < 0) + err = -EBUSY; + mutex_unlock(&inf->mutex); + return err; +} +EXPORT_SYMBOL(nvi_mpu_data_out); - d &= ~BIT_CYCLE; - if (lpa_mode) - d |= BIT_CYCLE; - result = inv_i2c_single_write(st, reg->pwr_mgmt_1, d); - if (result) - return result; +int nvi_mpu_bypass_request(bool enable) +{ + struct inv_gyro_state_s *inf; + int err; - st->chip_config.lpa_mode = lpa_mode; - return count; + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s\n", __func__); + } else { + pr_debug("%s\n", __func__); + return -EAGAIN; + } + + if (inf->shutdown) + return -EPERM; + + mutex_lock(&inf->mutex); + nvi_pm(inf, NVI_PM_ON); + err = nvi_aux_bypass_request(inf, enable); + nvi_pm(inf, NVI_PM_AUTO); + err = nvi_aux_mpu_call_post(inf, "nvi_mpu_bypass_request err: ", err); + return err; } +EXPORT_SYMBOL(nvi_mpu_bypass_request); -/** - * 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) +int nvi_mpu_bypass_release(void) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned long result, lpa_freq; - unsigned char d; - struct inv_reg_map_s *reg; + struct inv_gyro_state_s *inf; + int err; - if (st->chip_config.is_asleep) + inf = inf_local; + if (inf != NULL) { + if (inf->aux.dbg) + pr_info("%s\n", __func__); + } else { + pr_debug("%s\n", __func__); + return -EAGAIN; + } + + if (inf->shutdown) return -EPERM; - result = kstrtoul(buf, 10, &lpa_freq); - if (result) - return result; + mutex_lock(&inf->mutex); + nvi_pm(inf, NVI_PM_ON); + err = nvi_aux_bypass_release(inf); + nvi_pm(inf, NVI_PM_AUTO); + err = nvi_aux_mpu_call_post(inf, "nvi_mpu_bypass_release err: ", err); + return err; +} +EXPORT_SYMBOL(nvi_mpu_bypass_release); + + +static int nvi_gyro_enable(struct inv_gyro_state_s *inf, + unsigned char enable, unsigned char fifo_enable) +{ + unsigned char enable_old; + unsigned char fifo_enable_old; + int err; + int err_t; + + enable_old = inf->chip_config.gyro_enable; + fifo_enable_old = inf->chip_config.gyro_fifo_enable; + inf->chip_config.gyro_fifo_enable = fifo_enable; + inf->chip_config.gyro_enable = enable; + err_t = nvi_pm(inf, NVI_PM_ON_FULL); + if (enable != enable_old) { + if (enable) { + err = nvi_gyro_config_wr(inf, + inf->chip_config.gyro_fsr); + if (err < 0) + err_t |= err; + nvi_motion_detect_enable(inf, 0); + } + nvi_global_delay(inf); + } + if (fifo_enable_old != fifo_enable) + err_t = nvi_reset(inf, true, false); + if (err_t) { + inf->chip_config.gyro_enable = enable_old; + inf->chip_config.gyro_fifo_enable = fifo_enable_old; + } + err_t |= nvi_pm(inf, NVI_PM_AUTO); + return err_t; +} + +static int nvi_accl_enable(struct inv_gyro_state_s *inf, + unsigned char enable, unsigned char fifo_enable) +{ + unsigned char enable_old; + unsigned char fifo_enable_old; + int err; + int err_t; + + enable_old = inf->chip_config.accl_enable; + fifo_enable_old = inf->chip_config.accl_fifo_enable; + inf->chip_config.accl_fifo_enable = fifo_enable; + inf->chip_config.accl_enable = enable; + err_t = nvi_pm(inf, NVI_PM_ON); + if (enable != enable_old) { + if (enable) { + err = nvi_accel_config_wr(inf, + inf->chip_config.accl_fsr, 0); + if (err < 0) + err_t |= err; + } + nvi_global_delay(inf); + } + if (fifo_enable_old != fifo_enable) + err_t = nvi_reset(inf, true, false); + if (err_t) { + inf->chip_config.accl_enable = enable_old; + inf->chip_config.accl_fifo_enable = fifo_enable_old; + } + err_t |= nvi_pm(inf, NVI_PM_AUTO); + return err_t; +} - if (lpa_freq > 3) + +static ssize_t nvi_gyro_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char enable; + unsigned char fifo_enable; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &enable); + if (err) return -EINVAL; - reg = st->reg; - result = inv_i2c_read(st, reg->pwr_mgmt_2, 1, &d); - if (result) - return result; + if (enable > 7) + return -EINVAL; - d &= ~BIT_LPA_FREQ; - d |= (unsigned char)(lpa_freq << 6); - result = inv_i2c_single_write(st, reg->pwr_mgmt_2, d); - if (result) - return result; + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, enable); + if (enable != inf->chip_config.gyro_enable) { + if (enable) + fifo_enable = inf->chip_config.gyro_fifo_enable; + else + fifo_enable = 0; + err = nvi_gyro_enable(inf, enable, fifo_enable); + } + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", + __func__, enable, err); + return err; + } - st->chip_config.lpa_freq = lpa_freq; return count; } -/** - * 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) +ssize_t nvi_gyro_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st; - unsigned long data, result, en; + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + return sprintf(buf, "%u\n", inf->chip_config.gyro_enable); +} - if (st->chip_config.enable) - return -EPERM; +ssize_t nvi_gyro_fifo_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char fifo_enable; + unsigned char enable; + int err; - result = kstrtoul(buf, 10, &data); - if (result) - return result; + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &fifo_enable); + if (err) + return -EINVAL; - if (data) - en = 1; - else - en = 0; - if (en == st->chip_config.compass_enable) - return count; + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, fifo_enable); + enable = inf->chip_config.gyro_enable; + if (fifo_enable) { + fifo_enable = 1; + if (!enable) + enable = 7; + } + if (fifo_enable != inf->chip_config.gyro_fifo_enable) + err = nvi_gyro_enable(inf, enable, fifo_enable); + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", + __func__, fifo_enable, err); + return err; + } - st->chip_config.compass_enable = en; return count; } -/** - * inv_compass_scale_store() - show current compass scale settings - */ -static ssize_t inv_compass_scale_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +ssize_t nvi_gyro_fifo_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st; - unsigned long data, result, en; - char d; + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - st = dev_get_drvdata(dev); - if (COMPASS_ID_AK8963 != st->plat_data.sec_slave_id) - return count; + return sprintf(buf, "%u\n", inf->chip_config.gyro_fifo_enable); +} - if (st->chip_config.is_asleep) - return -EPERM; +static ssize_t inv_gyro_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned long gyro_delay_us; + unsigned long gyro_delay_us_old; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &gyro_delay_us); + if (err) + return err; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %lu\n", __func__, gyro_delay_us); + if (gyro_delay_us < NVI_INPUT_GYRO_DELAY_US_MIN) + gyro_delay_us = NVI_INPUT_GYRO_DELAY_US_MIN; + if (gyro_delay_us != inf->chip_config.gyro_delay_us) { + gyro_delay_us_old = inf->chip_config.gyro_delay_us; + inf->chip_config.gyro_delay_us = gyro_delay_us; + if (inf->chip_config.gyro_enable) { + err = nvi_global_delay(inf); + if (err) + inf->chip_config.gyro_delay_us = + gyro_delay_us_old; + } + } + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %lu ERR=%d\n", + __func__, gyro_delay_us, err); + return err; + } - result = kstrtoul(buf, 10, &data); - if (result) - return result; + return count; +} - if (data) - en = 1; - else - en = 0; - if (st->compass_scale == en) - return count; +static ssize_t nvi_gyro_resolution_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned int resolution; - st->compass_scale = en; - d = (1 | (st->compass_scale << 4)); - result = inv_i2c_single_write(st, REG_I2C_SLV1_DO, d); - if (result) - return result; + inf = dev_get_drvdata(dev); + if (kstrtouint(buf, 10, &resolution)) + return -EINVAL; + dev_dbg(&inf->i2c->dev, "%s %u", __func__, resolution); + inf->chip_config.gyro_resolution = resolution; return count; } -/** - * 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) +static ssize_t nvi_gyro_resolution_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - int result, data, out; - unsigned char *p; + struct inv_gyro_state_s *inf; + unsigned int resolution; - if (st->chip_config.is_asleep) - return -EPERM; + inf = dev_get_drvdata(dev); + if (inf->chip_config.gyro_enable) + resolution = inf->chip_config.gyro_resolution; + else + resolution = GYRO_INPUT_RESOLUTION; + return sprintf(buf, "%u\n", resolution); +} - result = kstrtol(buf, 10, (long unsigned int *)&data); - if (result) - return result; +static ssize_t nvi_gyro_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *inf; - out = cpu_to_be32p(&data); - p = (unsigned char *)&out; - result = mem_w_key(KEY_FLICK_LOWER, 4, p); - if (result) - return result; + inf = dev_get_drvdata(dev); + if (inf->chip_config.gyro_enable) + return sprintf(buf, "%lu\n", inf->sample_delay_us); - st->flick.lower = data; - return count; + return sprintf(buf, "%d\n", NVI_INPUT_GYRO_DELAY_US_MIN); } -/** - * 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) +static ssize_t nvi_gyro_max_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned int result, data, out; - unsigned char *p; + struct inv_gyro_state_s *inf; + unsigned char fsr; + int err; - if (st->chip_config.is_asleep) - return -EPERM; + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &fsr); + if (err) + return -EINVAL; - result = kstrtoul(buf, 10, (long unsigned int *)&data); - if (result) - return result; + if (fsr > 3) + return -EINVAL; - out = cpu_to_be32p(&data); - p = (unsigned char *)&out; - result = mem_w_key(KEY_FLICK_UPPER, 4, p); - if (result) - return result; + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, fsr); + if (fsr != inf->chip_config.gyro_fsr) { + if (inf->chip_config.gyro_enable) { + err = nvi_gyro_config_wr(inf, fsr); + if (err > 0) + /* reset fifo to purge old data */ + nvi_reset(inf, true, false); + } + if (err >= 0) + inf->chip_config.gyro_fsr = fsr; + } + mutex_unlock(&inf->mutex); + if (err < 0) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", __func__, fsr, err); + return err; + } - st->flick.upper = data; return count; } -/** - * 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) +ssize_t nvi_gyro_max_range_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned int result, data, out; - unsigned char *p; + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); + unsigned int range; - if (st->chip_config.is_asleep) - return -EPERM; + if (inf->chip_config.gyro_enable) + range = inf->chip_config.gyro_fsr; + else + range = (1 << inf->chip_config.gyro_fsr) * 250; + return sprintf(buf, "%u\n", range); +} - result = kstrtoul(buf, 10, (long unsigned int *)&data); - if (result) - return result; +static ssize_t nvi_accl_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char enable; + unsigned char fifo_enable; + int err; - out = cpu_to_be32p(&data); - p = (unsigned char *)&out; - result = mem_w_key(KEY_FLICK_COUNTER, 4, p); - if (result) - return result; + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &enable); + if (err) + return -EINVAL; + + if (enable > 7) + return -EINVAL; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, enable); + if (enable != inf->chip_config.accl_enable) { + if (enable) + fifo_enable = inf->chip_config.accl_fifo_enable; + else + fifo_enable = 0; + err = nvi_accl_enable(inf, enable, fifo_enable); + } + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", + __func__, enable, err); + return err; + } - st->flick.counter = data; return count; } -/** - * 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) +ssize_t nvi_accl_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned long result, data; - unsigned char d[4]; + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; + return sprintf(buf, "%u\n", inf->chip_config.accl_enable); +} - result = kstrtoul(buf, 10, &data); - if (result) - return result; +static ssize_t nvi_accl_fifo_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char fifo_enable; + unsigned char enable; + int err; - 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; + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &fifo_enable); + if (err) + return -EINVAL; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, fifo_enable); + enable = inf->chip_config.accl_enable; + if (fifo_enable) { + fifo_enable = 1; + if (!enable) + enable = 7; + } + if (fifo_enable != inf->chip_config.accl_fifo_enable) + err = nvi_accl_enable(inf, enable, fifo_enable); + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", + __func__, fifo_enable, err); + return err; + } - st->flick.int_on = data; return count; } -/** - * 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) +ssize_t nvi_accl_fifo_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned long result, data; - unsigned char d[4]; + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EPERM; - - result = kstrtoul(buf, 10, &data); - if (result) - return result; + return sprintf(buf, "%u\n", inf->chip_config.accl_fifo_enable); +} - 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; +static ssize_t nvi_accl_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned long accl_delay_us; + unsigned long accl_delay_us_old; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &accl_delay_us); + if (err) + return err; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %lu\n", __func__, accl_delay_us); + if (accl_delay_us < NVI_INPUT_ACCL_DELAY_US_MIN) + accl_delay_us = NVI_INPUT_ACCL_DELAY_US_MIN; + if (accl_delay_us != inf->chip_config.accl_delay_us) { + accl_delay_us_old = inf->chip_config.accl_delay_us; + inf->chip_config.accl_delay_us = accl_delay_us; + if (inf->chip_config.accl_enable) { + err = nvi_global_delay(inf); + if (err) + inf->chip_config.accl_delay_us = + accl_delay_us_old; + else + nvi_pm(inf, NVI_PM_AUTO); + } + } + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %lu ERR=%d\n", + __func__, accl_delay_us, err); + return err; + } - st->flick.axis = data; return count; } -/** - * 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) +ssize_t nvi_accl_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(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; + struct inv_gyro_state_s *inf; - 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; + inf = dev_get_drvdata(dev); + if (inf->chip_config.accl_enable) + return sprintf(buf, "%lu\n", inf->sample_delay_us); - st->flick.msg_on = data; - return count; + return sprintf(buf, "%d\n", NVI_INPUT_ACCL_DELAY_US_MIN); } -/** - * 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, +static ssize_t nvi_accl_resolution_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned int result, data, out; - unsigned char *p; + struct inv_gyro_state_s *inf; + unsigned int resolution; - if (st->chip_config.is_asleep) - return -EPERM; + inf = dev_get_drvdata(dev); + if (kstrtouint(buf, 10, &resolution)) + return -EINVAL; - result = kstrtoul(buf, 10, (long unsigned int *)&data); - if (result) - return result; + dev_dbg(&inf->i2c->dev, "%s %u", __func__, resolution); + inf->chip_config.accl_resolution = resolution; + return count; +} - out = cpu_to_be32p(&data); - p = (unsigned char *)&out; - result = mem_w_key(KEY_D_PEDSTD_STEPCTR, 4, p); - if (result) - return result; +static ssize_t nvi_accl_resolution_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct inv_gyro_state_s *inf; + unsigned int resolution; - return count; + inf = dev_get_drvdata(dev); + if (inf->chip_config.accl_enable) + resolution = inf->chip_config.accl_resolution; + else + resolution = ACCL_INPUT_RESOLUTION; + return sprintf(buf, "%u\n", resolution); } -/** - * 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, +static ssize_t nvi_accl_max_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned int result, data, out; - unsigned char *p; + struct inv_gyro_state_s *inf; + unsigned char fsr; + int err; - if (st->chip_config.is_asleep) - return -EPERM; + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &fsr); + if (err) + return -EINVAL; - result = kstrtoul(buf, 10, (long unsigned int *)&data); - if (result) - return result; + if (fsr > 3) + return -EINVAL; - out = cpu_to_be32p(&data); - p = (unsigned char *)&out; - result = mem_w_key(KEY_D_PEDSTD_TIMECTR, 4, p); - if (result) - return result; + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, fsr); + if (fsr != inf->chip_config.accl_fsr) { + if (inf->chip_config.accl_enable) { + err = nvi_accel_config_wr(inf, fsr, 0); + if (err > 0) + /* reset fifo to purge old data */ + nvi_reset(inf, true, false); + nvi_pm(inf, NVI_PM_AUTO); + } + if (err >= 0) + inf->chip_config.accl_fsr = fsr; + } + mutex_unlock(&inf->mutex); + if (err < 0) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", __func__, fsr, err); + return err; + } return count; } -/** - * inv_key_store() - calling this function will store authenticate key - */ -static ssize_t inv_key_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +ssize_t nvi_accl_max_range_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - unsigned int result, data, out; - unsigned char *p, d[4]; + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); + unsigned int range; - if (st->chip_config.is_asleep) - return -EPERM; + if (inf->chip_config.accl_enable) + range = inf->chip_config.accl_fsr; + else + range = 0x4000 >> inf->chip_config.accl_fsr; + return sprintf(buf, "%u\n", range); +} - result = kstrtoul(buf, 10, (long unsigned int *)&data); - if (result) - return result; +static ssize_t nvi_lpa_delay_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned long lpa_delay_us; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &lpa_delay_us); + if (err) + return err; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %lu\n", __func__, lpa_delay_us); + inf->chip_config.lpa_delay_us = lpa_delay_us; + err = nvi_pm(inf, NVI_PM_AUTO); + mutex_unlock(&inf->mutex); + if (err) + dev_err(&inf->i2c->dev, "%s: %lu ERR=%d\n", + __func__, lpa_delay_us, err); + return count; +} - out = cpu_to_be32p(&data); - p = (unsigned char *)&out; - result = mem_w(D_AUTH_IN, 4, p); - if (result) - return result; +static ssize_t nvi_lpa_delay_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); + + return sprintf(buf, "%lu\n", inf->chip_config.lpa_delay_us); +} + +static ssize_t nvi_motion_thr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char mot_thr; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &mot_thr); + if (err) + return -EINVAL; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %u\n", __func__, mot_thr); + nvi_pm(inf, NVI_PM_ON); + err = nvi_motion_detect_enable(inf, mot_thr); + err |= nvi_pm(inf, NVI_PM_AUTO); + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %u ERR=%d\n", + __func__, mot_thr, err); + return err; + } - result = mpu_memory_read(st->sl_handle, st->i2c_addr, - D_AUTH_IN, 4, d); return count; } -/** - * inv_gyro_fs_show() - Get the current gyro full-scale range. - */ -static ssize_t inv_gyro_fs_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t nvi_motion_thr_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned char mot_thr; - return sprintf(buf, "%d\n", (1 << st->chip_config.fsr)*250); + inf = dev_get_drvdata(dev); + if (inf->mot_enable) + mot_thr = inf->hw.mot_thr; + else + mot_thr = 0; + return sprintf(buf, "%u\n", mot_thr); } -/** - * inv_accl_fs_show() - Get the current gyro full-scale range. - */ -ssize_t inv_accl_fs_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t nvi_motion_dur_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned char mot_dur; + int err; - return sprintf(buf, "%d\n", 2 << st->chip_config.accl_fs); + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &mot_dur); + if (err) + return -EINVAL; + + dev_dbg(&inf->i2c->dev, "%s: %u\n", __func__, mot_dur); + inf->chip_config.mot_dur = mot_dur; + return count; } -/** - * 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) +static ssize_t nvi_motion_dur_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", inf->chip_config.mot_dur); +} - switch (st->chip_config.clk_src) { - case INV_CLK_INTERNAL: - return sprintf(buf, "INTERNAL\n"); +static ssize_t nvi_motion_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char mot_ctrl; + int err; - case INV_CLK_PLL: - return sprintf(buf, "Gyro PLL\n"); + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &mot_ctrl); + if (err) + return -EINVAL; - default: - return sprintf(buf, "Oh no!\n"); - } + dev_dbg(&inf->i2c->dev, "%s: %u\n", __func__, mot_ctrl); + inf->chip_config.mot_ctrl = mot_ctrl; + 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) +static ssize_t nvi_motion_ctrl_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", st->chip_config.fifo_rate); + return sprintf(buf, "%u\n", inf->chip_config.mot_ctrl); } -/** - * inv_enable_show() - Check if the chip are enabled. - */ -static ssize_t inv_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t nvi_motion_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned int mot_cnt; + int err; - return sprintf(buf, "%d\n", st->chip_config.enable); + inf = dev_get_drvdata(dev); + err = kstrtouint(buf, 10, &mot_cnt); + if (err) + return -EINVAL; + + dev_dbg(&inf->i2c->dev, "%s: %u\n", __func__, mot_cnt); + inf->chip_config.mot_cnt = mot_cnt; + return count; } -/** - * inv_gyro_fifo_enable_show() - Check if gyro FIFO are enabled. - */ -ssize_t inv_gyro_fifo_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t nvi_motion_count_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", st->chip_config.gyro_fifo_enable); + return sprintf(buf, "%u\n", inf->chip_config.mot_cnt); } -/** - * inv_accl_fifo_enable_show() - Check if accl FIFO are enabled. - */ -ssize_t inv_accl_fifo_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t nvi_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned char enable; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &enable); + if (err) + return -EINVAL; - return sprintf(buf, "%d\n", st->chip_config.accl_fifo_enable); + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %u\n", __func__, enable); + if (enable) + enable = 1; + if (enable != inf->chip_config.enable) { + inf->chip_config.enable = enable; + err = nvi_pm(inf, NVI_PM_AUTO); + } + mutex_unlock(&inf->mutex); + if (err) + dev_err(&inf->i2c->dev, "%s: %u ERR=%d\n", + __func__, enable, err); + return count; } -/** - * inv_gyro_enable_show() - Check if the FIFO and ring buffer are enabled. - */ -ssize_t inv_gyro_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +ssize_t nvi_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", st->chip_config.gyro_enable); + return sprintf(buf, "%u\n", inf->chip_config.enable); } /** - * inv_accl_enable_show() - Check if the FIFO and ring buffer are enabled. + * inv_raw_gyro_show() - Read gyro data directly from registers. */ -ssize_t inv_accl_enable_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t inv_raw_gyro_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *st; + struct inv_reg_map_s *reg; + int result; + unsigned char data[6]; + + st = dev_get_drvdata(dev); + reg = st->reg; + if (0 == st->chip_config.gyro_enable) + return -EPERM; + + result = inv_i2c_read(st, reg->raw_gyro, 6, data); + if (result) { + printk(KERN_ERR "Could not read raw registers.\n"); + return result; + } - return sprintf(buf, "%d\n", st->chip_config.accl_enable); + return sprintf(buf, "%d %d %d %lld\n", + (signed short)(be16_to_cpup((short *)&data[0])), + (signed short)(be16_to_cpup((short *)&data[2])), + (signed short)(be16_to_cpup((short *)&data[4])), + get_time_ns()); } /** - * inv_power_state_show() - Check if the device is on or in sleep mode. + * inv_raw_accl_show() - Read accel data directly from registers. */ -static ssize_t inv_power_state_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t inv_raw_accl_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *st; + struct inv_reg_map_s *reg; + int result; + unsigned char data[6]; - if (st->chip_config.is_asleep) - return sprintf(buf, "0\n"); + st = dev_get_drvdata(dev); + reg = st->reg; + if (0 == st->chip_config.accl_enable) + return -EPERM; - else - return sprintf(buf, "1\n"); + result = inv_i2c_read(st, reg->raw_accl, 6, data); + if (result) { + printk(KERN_ERR "Could not read raw registers.\n"); + return result; + } + + return sprintf(buf, "%d %d %d %lld\n", + ((signed short)(be16_to_cpup((short *)&data[0]))* + st->chip_info.multi), + ((signed short)(be16_to_cpup((short *)&data[2]))* + st->chip_info.multi), + ((signed short)(be16_to_cpup((short *)&data[4]))* + st->chip_info.multi), + get_time_ns()); } /** @@ -1572,7 +2414,6 @@ static ssize_t inv_temp_scale_show(struct device *dev, if (INV_MPU3050 == st->chip_type) return sprintf(buf, "280\n"); - else return sprintf(buf, "340\n"); } @@ -1587,71 +2428,50 @@ static ssize_t inv_temp_offset_show(struct device *dev, if (INV_MPU3050 == st->chip_type) return sprintf(buf, "-13200\n"); - else return sprintf(buf, "-521\n"); } -/** - * 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) +static ssize_t inv_temperature_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + ssize_t rtn; - return sprintf(buf, "%d\n", st->chip_config.lpa_mode); + inf = dev_get_drvdata(dev); + mutex_lock(&inf->mutex_temp); + rtn = sprintf(buf, "%d %lld\n", inf->temp_val, inf->temp_ts); + mutex_unlock(&inf->mutex_temp); + return rtn; } /** - * inv_lpa_freq_show() - show current low power frequency setting + * inv_key_store() - calling this function will store authenticate key */ -static ssize_t inv_lpa_freq_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t inv_key_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct inv_gyro_state_s *st = dev_get_drvdata(dev); + unsigned int result, data, out; + unsigned char *p, d[4]; - 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"); + if (st->chip_config.enable) + return -EPERM; - default: - return sprintf(buf, "0\n"); - } -} + result = kstrtoul(buf, 10, (long unsigned int *)&data); + if (result) + return result; -/** - * inv_compass_scale_show() - show current compass scale settings - */ -static ssize_t inv_compass_scale_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - long 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; + out = cpu_to_be32p(&data); + p = (unsigned char *)&out; + result = mem_w(D_AUTH_IN, 4, p); + if (result) + return result; - scale *= (1L << 15); - return sprintf(buf, "%ld\n", scale); + result = mpu_memory_read(st->sl_handle, st->i2c_addr, + D_AUTH_IN, 4, d); + return count; } /** @@ -1666,7 +2486,7 @@ static ssize_t inv_reg_dump_show(struct device *dev, ssize_t bytes_printed = 0; struct inv_gyro_state_s *st = dev_get_drvdata(dev); - for (ii = 0; ii < st->hw->num_reg; ii++) { + for (ii = 0; ii < st->hw_s->num_reg; ii++) { /* don't read fifo r/w register */ if (ii == st->reg->fifo_r_w) data = 0; @@ -1679,49 +2499,11 @@ static ssize_t inv_reg_dump_show(struct device *dev, } /** - * inv_self_test_show() - self test result. 0 for fail; 1 for success. - * calling this function will trigger self test - * and return test result. + * inv_gyro_orientation_show() - show orientation matrix */ -static ssize_t inv_self_test_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int result; - int bias[3]; - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - - if (INV_MPU3050 == st->chip_type) { - bias[0] = bias[1] = bias[2] = 0; - result = 0; - } else { - result = inv_hw_self_test(st, bias); - } - return sprintf(buf, "%d, %d, %d, %d\n", - bias[0], bias[1], bias[2], result); -} - -/** - * inv_get_accl_bias_show() - show accl bias value - */ -static ssize_t inv_get_accl_bias_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int result; - int bias[3]; - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - - result = inv_get_accl_bias(st, bias); - if (result) - return -EINVAL; - - return sprintf(buf, "%d, %d, %d\n", bias[0], bias[1], bias[2]); -} - -/** - * inv_gyro_matrix_show() - show orientation matrix - */ -static ssize_t inv_gyro_matrix_show(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t inv_gyro_orientation_show(struct device *dev, + struct device_attribute *attr, + char *buf) { struct inv_gyro_state_s *st = dev_get_drvdata(dev); signed char *m; @@ -1749,25 +2531,6 @@ ssize_t inv_accl_matrix_show(struct device *dev, } /** - * inv_compass_matrix_show() - show orientation matrix - */ -static ssize_t inv_compass_matrix_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - signed char *m; - - 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_key_show() - calling this function will show the key * */ @@ -1779,159 +2542,203 @@ static ssize_t inv_key_show(struct device *dev, struct device_attribute *attr, 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]); + "%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_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) +#if DEBUG_SYSFS_INTERFACE +static ssize_t nvi_dbg_i2c_addr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned char dbg_i2c_addr; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 16, &dbg_i2c_addr); + if (err) + return -EINVAL; - return sprintf(buf, "%d\n", st->chip_config.firmware_loaded); + inf->dbg_i2c_addr = dbg_i2c_addr; + 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) +static ssize_t nvi_dbg_i2c_addr_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct inv_gyro_state_s *st = dev_get_drvdata(dev); + ssize_t bytes_printed = 0; - return sprintf(buf, "%d\n", st->chip_config.compass_enable); + bytes_printed += sprintf(buf + bytes_printed, + "%#2x\n", st->dbg_i2c_addr); + return bytes_printed; } -/** - * 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) +static ssize_t nvi_dbg_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned char dbg_reg; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 16, &dbg_reg); + if (err) + return -EINVAL; - return sprintf(buf, "%d\n", st->flick.lower); + inf->dbg_reg = dbg_reg; + 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) +static ssize_t nvi_dbg_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct inv_gyro_state_s *st = dev_get_drvdata(dev); + ssize_t bytes_printed = 0; - return sprintf(buf, "%d\n", st->flick.upper); + bytes_printed += sprintf(buf + bytes_printed, "%#2x\n", st->dbg_reg); + return bytes_printed; } -/** - * 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) +static ssize_t nvi_dbg_dat_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned short dbg_i2c_addr; + unsigned char dbg_dat; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 16, &dbg_dat); + if (err) + return -EINVAL; - return sprintf(buf, "%d\n", st->flick.counter); + if (inf->dbg_i2c_addr) { + err = inv_i2c_single_write_base(inf, inf->dbg_i2c_addr, + inf->dbg_reg, dbg_dat); + dbg_i2c_addr = inf->dbg_i2c_addr; + } else { + err = inv_i2c_single_write(inf, inf->dbg_reg, dbg_dat); + dbg_i2c_addr = inf->i2c->addr; + } + pr_info("%s dev=%x reg=%x data=%x err=%d\n", + __func__, dbg_i2c_addr, inf->dbg_reg, dbg_dat, err); + 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) +static ssize_t nvi_dbg_dat_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned short dbg_i2c_addr; + unsigned char data; + ssize_t bytes_printed = 0; - return sprintf(buf, "%d\n", st->flick.int_on); + inf = dev_get_drvdata(dev); + if (inf->dbg_i2c_addr) { + inv_i2c_read_base(inf, inf->dbg_i2c_addr, + inf->dbg_reg, 1, &data); + dbg_i2c_addr = inf->dbg_i2c_addr; + } else { + inv_i2c_read(inf, inf->dbg_reg, 1, &data); + dbg_i2c_addr = inf->i2c->addr; + } + bytes_printed += sprintf(buf + bytes_printed, "%#2x:%#2x=%#2x\n", + dbg_i2c_addr, inf->dbg_reg, data); + return bytes_printed; } -/** - * 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) +static ssize_t nvi_aux_dbg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf; + unsigned int enable; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtouint(buf, 10, &enable); + if (err) + return err; - return sprintf(buf, "%d\n", st->flick.axis); + inf->aux.dbg = true; + nvi_aux_dbg(inf, "SNAPSHOT", 0); + if (enable) + inf->aux.dbg = true; + else + inf->aux.dbg = false; + 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) +static ssize_t nvi_aux_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", st->flick.msg_on); + return sprintf(buf, "%x\n", inf->aux.dbg); } -/** - * 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) +static ssize_t nvi_mot_dbg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - int result, data; - unsigned char d[4]; + struct inv_gyro_state_s *inf; + unsigned int mot_dbg; + int err; - if (st->chip_config.is_asleep) - return -EPERM; + inf = dev_get_drvdata(dev); + err = kstrtouint(buf, 10, &mot_dbg); + if (err) + return err; - result = mpu_memory_read(st->sl_handle, st->i2c_addr, - inv_dmp_get_address(KEY_D_PEDSTD_STEPCTR), - 4, d); - if (result) - return result; + if (mot_dbg) + inf->mot_dbg = true; + else + inf->mot_dbg = false; + return count; +} + +static ssize_t nvi_mot_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - data = be32_to_cpup((int *)d); - return sprintf(buf, "%d\n", data); + return sprintf(buf, "%x\n", inf->mot_dbg); } +#endif /* DEBUG_SYSFS_INTERFACE */ -/** - * 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) +static ssize_t nvi_min_delay_us_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct inv_gyro_state_s *st = dev_get_drvdata(dev); - int result, data; - unsigned char d[4]; + struct inv_gyro_state_s *inf; + unsigned long min_delay_us; + int err; - if (st->chip_config.is_asleep) - return -EPERM; + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &min_delay_us); + if (err) + return -EINVAL; - result = mpu_memory_read(st->sl_handle, st->i2c_addr, - inv_dmp_get_address(KEY_D_PEDSTD_TIMECTR), - 4, d); - if (result) - return result; + dev_dbg(&inf->i2c->dev, "%s: %lu\n", __func__, min_delay_us); + inf->chip_config.min_delay_us = min_delay_us; + return count; +} + +static ssize_t nvi_min_delay_us_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct inv_gyro_state_s *inf = dev_get_drvdata(dev); - data = be32_to_cpup((int *)d); - return sprintf(buf, "%d\n", data); + return sprintf(buf, "%lu\n", inf->chip_config.min_delay_us); } + static void inv_report_gyro_accl(struct inv_gyro_state_s *st, s64 t, unsigned char *data) { @@ -1941,7 +2748,7 @@ static void inv_report_gyro_accl(struct inv_gyro_state_s *st, s64 t, conf = &st->chip_config; ind = 0; - if (conf->accl_fifo_enable | conf->dmp_on) { + if (conf->accl_fifo_enable) { x = ((data[ind] << 8)|data[ind + 1])*st->chip_info.multi; y = ((data[ind + 2] << 8)|data[ind + 3])*st->chip_info.multi; z = ((data[ind + 4] << 8)|data[ind + 5])*st->chip_info.multi; @@ -1953,7 +2760,7 @@ static void inv_report_gyro_accl(struct inv_gyro_state_s *st, s64 t, } ind += 6; } - if (conf->gyro_fifo_enable | conf->dmp_on) { + if (conf->gyro_fifo_enable) { x = (data[ind] << 8) | data[ind + 1]; y = (data[ind + 2] << 8) | data[ind + 3]; z = (data[ind + 4] << 8) | data[ind + 5]; @@ -1965,18 +2772,6 @@ static void inv_report_gyro_accl(struct inv_gyro_state_s *st, s64 t, } ind += 6; } - if (conf->dmp_on) { - /* report tap information */ - if (data[ind + 1] & 1) { - input_report_rel(st->idev_dmp, REL_RX, data[ind+3]); - input_sync(st->idev_dmp); - } - /* report orientation information */ - if (data[ind + 1] & 2) { - input_report_rel(st->idev_dmp, REL_RY, data[ind+2]); - input_sync(st->idev_dmp); - } - } if (conf->accl_fifo_enable | conf->gyro_fifo_enable) { input_report_rel(st->idev, REL_MISC, (unsigned int)(t >> 32)); input_report_rel(st->idev, REL_WHEEL, @@ -1985,55 +2780,6 @@ static void inv_report_gyro_accl(struct inv_gyro_state_s *st, s64 t, } } -static int inv_report_compass(struct inv_gyro_state_s *st, s64 t) -{ - short x, y, z; - int result; - unsigned char data[8]; - - /*mpu_memory_read(st->sl_handle, - st->i2c_addr, - 14, - 10, data);*/ - /*divider and counter is used to decrease the speed of read in - high frequency sample rate*/ - if (st->compass_divider == st->compass_counter) { - /*read from external sensor data register */ - result = inv_i2c_read(st, REG_EXT_SENS_DATA_00, 8, data); - if (result) - return result; - - /* data[7] is status 2 register */ - /*for AKM8975, bit 2 and 3 should be all be zero*/ - /* for AMK8963, bit 3 should be zero*/ - if ((DATA_AKM_DRDY == data[0]) && - (0 == (data[7] & DATA_AKM_STAT_MASK))) { - unsigned char *sens; - sens = st->chip_info.compass_sens; - x = (short)((data[2] << 8) | data[1]); - y = (short)((data[4] << 8) | data[3]); - z = (short)((data[6] << 8) | data[5]); - x = ((x * (sens[0] + 128)) >> 8); - y = ((y * (sens[1] + 128)) >> 8); - z = ((z * (sens[2] + 128)) >> 8); - input_report_rel(st->idev_compass, REL_X, x); - input_report_rel(st->idev_compass, REL_Y, y); - input_report_rel(st->idev_compass, REL_Z, z); - input_report_rel(st->idev_compass, REL_MISC, - (unsigned int)(t >> 32)); - input_report_rel(st->idev_compass, REL_WHEEL, - (unsigned int)(t & 0xffffffff)); - input_sync(st->idev_compass); - } - st->compass_counter = 0; - - } else if (st->compass_divider != 0) { - st->compass_counter++; - } - - return 0; -} - /** * inv_read_fifo() - Transfer data from FIFO to ring buffer. */ @@ -2051,40 +2797,42 @@ static irqreturn_t inv_read_fifo(int irq, void *dev_id) st = (struct inv_gyro_state_s *)dev_id; reg = st->reg; - if (st->chip_config.is_asleep) - goto end_session; - if (!(st->chip_config.enable)) - goto end_session; - - if (!(st->chip_config.accl_fifo_enable | - st->chip_config.gyro_fifo_enable | - st->chip_config.dmp_on | - st->chip_config.compass_enable)) - goto end_session; - - if (st->chip_config.dmp_on && st->flick.int_on) { - /*dmp interrupt status */ - inv_i2c_read(st, REG_DMP_INT_STATUS, 2, data); - if (data[0] & 8) { - input_report_rel(st->idev_dmp, REL_RZ, data[0]); - input_sync(st->idev_dmp); + timestamp = get_time_ns(); + if ((!(st->hw.fifo_en & BIT_TEMP_FIFO_EN)) && + st->chip_config.gyro_enable) { + result = inv_i2c_read(st, st->reg->temperature, 2, data); + if (!result) { + mutex_lock(&st->mutex_temp); + st->temp_val = (data[0] << 8) | data[1]; + st->temp_ts = timestamp; + mutex_unlock(&st->mutex_temp); } } - if (st->chip_config.lpa_mode) { + if (st->mot_cnt) + st->mot_cnt--; + if (st->lpa_enable || (st->hw.int_enable & BIT_MOT_EN)) { + if (st->hw.int_enable & BIT_MOT_EN) { + st->mot_cnt = st->chip_config.mot_cnt; + st->mot_enable = false; + nvi_int_enable_wr(st, true); + if (st->mot_dbg) + pr_info("%s motion detect off", __func__); + } result = inv_i2c_read(st, reg->raw_accl, 6, data); if (result) goto end_session; - inv_report_gyro_accl(st, get_time_ns(), data); + inv_report_gyro_accl(st, timestamp, data); + if (st->mot_enable && st->mot_dbg) + pr_info("%s SENDING MOTION DETECT DATA", __func__); + if (st->mot_enable && (!st->mot_cnt)) + nvi_int_enable_wr(st, true); goto end_session; } - if (st->chip_config.dmp_on) - bytes_per_datum = BYTES_FOR_DMP; - else - bytes_per_datum = (st->chip_config.accl_fifo_enable + - st->chip_config.gyro_fifo_enable)*BYTES_PER_SENSOR; + bytes_per_datum = (st->chip_config.accl_fifo_enable + + st->chip_config.gyro_fifo_enable)*BYTES_PER_SENSOR; fifo_count = 0; if (bytes_per_datum != 0) { result = inv_i2c_read(st, reg->fifo_count_h, 2, data); @@ -2102,22 +2850,14 @@ static irqreturn_t inv_read_fifo(int irq, void *dev_id) goto flush_fifo; /* Timestamp mismatch. */ - if (kfifo_len(&st->trigger.timestamps) < - fifo_count / bytes_per_datum) + if (kfifo_len(&st->trigger.timestamps) < (fifo_count / + bytes_per_datum)) goto flush_fifo; - if (kfifo_len(&st->trigger.timestamps) > - fifo_count / bytes_per_datum + TIME_STAMP_TOR) { - if (st->chip_config.dmp_on) { - result = kfifo_to_user(&st->trigger.timestamps, - ×tamp, sizeof(timestamp), &copied); - if (result) - goto flush_fifo; - - } else { - goto flush_fifo; - } - } + if (kfifo_len(&st->trigger.timestamps) > (fifo_count / + bytes_per_datum + + TIME_STAMP_TOR)) + goto flush_fifo; } if (bytes_per_datum == 0) { @@ -2142,16 +2882,14 @@ static irqreturn_t inv_read_fifo(int irq, void *dev_id) fifo_count -= bytes_per_datum; } - if (st->chip_config.compass_enable) - inv_report_compass(st, timestamp); + nvi_aux_read(st); end_session: return IRQ_HANDLED; flush_fifo: /* Flush HW and SW FIFOs. */ - inv_reset_fifo(st); - inv_clear_kfifo(st); + nvi_reset(st, true, false); return IRQ_HANDLED; } @@ -2172,7 +2910,7 @@ static irqreturn_t inv_irq_handler(int irq, void *dev_id) spin_lock(&st->time_stamp_lock); catch_up = 0; while ((time_since_last_irq > st->irq_dur_us*2) && - (catch_up < MAX_CATCH_UP) && (0 == st->chip_config.lpa_mode)) { + (catch_up < MAX_CATCH_UP) && (!st->lpa_enable)) { st->last_isr_time += st->irq_dur_us * ONE_K_HZ; result = kfifo_in(&st->trigger.timestamps, &st->last_isr_time, 1); @@ -2186,25 +2924,31 @@ static irqreturn_t inv_irq_handler(int irq, void *dev_id) return IRQ_WAKE_THREAD; } +static int inv_pm(struct inv_gyro_state_s *inf, int pm_req) +{ + int err; + + if (inf->nvi) { + err = nvi_pm(inf, pm_req); + } else { + if ((pm_req > NVI_PM_OFF) || (pm_req == NVI_PM_AUTO)) + err = set_power_mpu3050(inf, 1); + else + err = set_power_mpu3050(inf, 0); + } + return err; +} + #ifdef CONFIG_PM static int inv_suspend(struct device *dev) { int result; struct inv_gyro_state_s *st = dev_get_drvdata(dev); - if (inv_set_power_state(st, 0)) - dev_err(dev, "inv_set_power_state in inv_suspend failed\n"); - /* vdd is used to gate the vlogic. Disable vlogic first. */ - if (st->inv_regulator.regulator_vdd && - st->inv_regulator.regulator_vlogic) { - result = regulator_disable(st->inv_regulator.regulator_vlogic); - if (result < 0) - dev_err(dev, "regulator_disable for vlogic failed\n"); - result = regulator_disable(st->inv_regulator.regulator_vdd); - if (result < 0) - dev_err(dev, "regulator_disable for vdd failed\n"); - } - return 0; + result = inv_pm(st, NVI_PM_OFF_FORCE); + if (result) + dev_err(dev, "%s ERR\n", __func__); + return result; } static int inv_resume(struct device *dev) @@ -2212,19 +2956,10 @@ static int inv_resume(struct device *dev) int result; struct inv_gyro_state_s *st = dev_get_drvdata(dev); - /* vdd is used to gate the vlogic. Enable vdd first. */ - if (st->inv_regulator.regulator_vdd && - st->inv_regulator.regulator_vlogic) { - result = regulator_enable(st->inv_regulator.regulator_vdd); - if (result < 0) - dev_err(dev, "regulator_enable for vdd failed\n"); - result = regulator_enable(st->inv_regulator.regulator_vlogic); - if (result < 0) - dev_err(dev, "regulator_enable for vlogic failed\n"); - } - if (inv_set_power_state(st, 1)) - dev_err(dev, "inv_set_power_state in inv_resume failed\n"); - return 0; + result = inv_pm(st, NVI_PM_AUTO); + if (result) + dev_err(dev, "%s ERR\n", __func__); + return result; } static const struct dev_pm_ops inv_pm_ops = { @@ -2233,343 +2968,235 @@ static const struct dev_pm_ops inv_pm_ops = { }; #endif -static DEVICE_ATTR(raw_gyro, S_IRUGO, inv_raw_gyro_show, NULL); -static DEVICE_ATTR(raw_accl, S_IRUGO, inv_raw_accl_show, NULL); -static DEVICE_ATTR(temperature, S_IRUGO, inv_temperature_show, NULL); -static DEVICE_ATTR(fifo_rate, S_IRUGO | S_IWUSR, inv_fifo_rate_show, - inv_fifo_rate_store); -static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, inv_enable_show, - inv_enable_store); -static DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR, - inv_gyro_fifo_enable_show, inv_gyro_fifo_enable_store); -static DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_gyro_enable_show, - inv_gyro_enable_store); +static DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_gyro_enable_show, nvi_gyro_enable_store); +static DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_gyro_fifo_enable_show, nvi_gyro_fifo_enable_store); +static DEVICE_ATTR(gyro_delay, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_gyro_delay_show, inv_gyro_delay_store); +static DEVICE_ATTR(gyro_resolution, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_gyro_resolution_show, nvi_gyro_resolution_store); +static DEVICE_ATTR(gyro_max_range, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_gyro_max_range_show, nvi_gyro_max_range_store); +static DEVICE_ATTR(gyro_orientation, S_IRUGO, + inv_gyro_orientation_show, NULL); +static DEVICE_ATTR(raw_gyro, S_IRUGO, + inv_raw_gyro_show, NULL); +static DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_accl_enable_show, nvi_accl_enable_store); static DEVICE_ATTR(accl_fifo_enable, S_IRUGO | S_IWUSR, - inv_accl_fifo_enable_show, inv_accl_fifo_enable_store); -static DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_accl_enable_show, - inv_accl_enable_store); -static DEVICE_ATTR(accl_fs, S_IRUGO | S_IWUSR, inv_accl_fs_show, - inv_accl_fs_store); -static DEVICE_ATTR(gyro_fs, S_IRUGO | S_IWUSR, inv_gyro_fs_show, - inv_gyro_fs_store); -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(compass_enable, S_IRUGO | S_IWUSR, inv_compass_en_show, - inv_compass_en_store); -static DEVICE_ATTR(compass_scale, S_IRUGO | S_IWUSR, inv_compass_scale_show, - inv_compass_scale_store); -static DEVICE_ATTR(temp_scale, S_IRUGO, inv_temp_scale_show, NULL); -static DEVICE_ATTR(temp_offset, S_IRUGO, inv_temp_offset_show, NULL); -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 | S_IWUSR, inv_key_show, inv_key_store); -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(accl_bias, S_IRUGO, inv_get_accl_bias_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(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); + nvi_accl_fifo_enable_show, nvi_accl_fifo_enable_store); +static DEVICE_ATTR(accl_delay, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_accl_delay_show, nvi_accl_delay_store); +static DEVICE_ATTR(accl_resolution, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_accl_resolution_show, nvi_accl_resolution_store); +static DEVICE_ATTR(accl_max_range, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_accl_max_range_show, nvi_accl_max_range_store); +static DEVICE_ATTR(accl_orientation, S_IRUGO, + inv_accl_matrix_show, NULL); +static DEVICE_ATTR(raw_accl, S_IRUGO, + inv_raw_accl_show, NULL); +static DEVICE_ATTR(lpa_delay, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_lpa_delay_enable_show, nvi_lpa_delay_enable_store); +static DEVICE_ATTR(motion_threshold, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_motion_thr_show, nvi_motion_thr_store); +static DEVICE_ATTR(motion_duration, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_motion_dur_show, nvi_motion_dur_store); +static DEVICE_ATTR(motion_count, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_motion_count_show, nvi_motion_count_store); +static DEVICE_ATTR(motion_control, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_motion_ctrl_show, nvi_motion_ctrl_store); +static DEVICE_ATTR(temp_scale, S_IRUGO, + inv_temp_scale_show, NULL); +static DEVICE_ATTR(temp_offset, S_IRUGO, + inv_temp_offset_show, NULL); +static DEVICE_ATTR(temperature, S_IRUGO, + inv_temperature_show, NULL); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_enable_show, nvi_enable_store); +static DEVICE_ATTR(dbg_reg, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_dbg_reg_show, nvi_dbg_reg_store); +static DEVICE_ATTR(dbg_dat, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_dbg_dat_show, nvi_dbg_dat_store); +static DEVICE_ATTR(dbg_i2c_addr, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_dbg_i2c_addr_show, nvi_dbg_i2c_addr_store); +static DEVICE_ATTR(aux_dbg, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_aux_dbg_show, nvi_aux_dbg_store); +static DEVICE_ATTR(mot_dbg, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_mot_dbg_show, nvi_mot_dbg_store); +static DEVICE_ATTR(key, S_IRUGO | S_IWUSR, + inv_key_show, inv_key_store); +static DEVICE_ATTR(reg_dump, S_IRUGO, + inv_reg_dump_show, NULL); +static DEVICE_ATTR(min_delay_us, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_min_delay_us_show, nvi_min_delay_us_store); static struct device_attribute *inv_attributes[] = { + &dev_attr_gyro_delay, + &dev_attr_gyro_orientation, &dev_attr_raw_gyro, - &dev_attr_temperature, - &dev_attr_fifo_rate, - &dev_attr_enable, - &dev_attr_clock_source, - &dev_attr_power_state, - &dev_attr_gyro_fs, &dev_attr_temp_scale, &dev_attr_temp_offset, + &dev_attr_temperature, &dev_attr_reg_dump, - &dev_attr_self_test, &dev_attr_key, - &dev_attr_gyro_matrix, +#if DEBUG_SYSFS_INTERFACE + &dev_attr_dbg_reg, + &dev_attr_dbg_dat, + &dev_attr_dbg_i2c_addr, +#endif /* DEBUG_SYSFS_INTERFACE */ NULL }; static struct device_attribute *inv_mpu6050_attributes[] = { - &dev_attr_gyro_fifo_enable, &dev_attr_gyro_enable, - &dev_attr_accl_fifo_enable, + &dev_attr_gyro_fifo_enable, + &dev_attr_gyro_max_range, + &dev_attr_gyro_resolution, &dev_attr_accl_enable, - &dev_attr_accl_fs, - &dev_attr_accl_bias, + &dev_attr_accl_fifo_enable, + &dev_attr_accl_delay, + &dev_attr_accl_max_range, + &dev_attr_accl_resolution, + &dev_attr_accl_orientation, &dev_attr_raw_accl, - &dev_attr_accl_matrix, - &dev_attr_firmware_loaded, - &dev_attr_lpa_mode, - &dev_attr_lpa_freq, - &dev_attr_flick_lower, - &dev_attr_flick_upper, - &dev_attr_flick_counter, - &dev_attr_flick_message_on, - &dev_attr_flick_int_on, - &dev_attr_flick_axis, - &dev_attr_pedometer_time, - &dev_attr_pedometer_steps, - NULL -}; - -static struct device_attribute *inv_compass_attributes[] = { - &dev_attr_compass_enable, - &dev_attr_compass_scale, - &dev_attr_compass_matrix, + &dev_attr_lpa_delay, + &dev_attr_motion_threshold, + &dev_attr_motion_duration, + &dev_attr_motion_count, + &dev_attr_motion_control, + &dev_attr_enable, +#if DEBUG_SYSFS_INTERFACE + &dev_attr_min_delay_us, + &dev_attr_aux_dbg, + &dev_attr_mot_dbg, +#endif /* DEBUG_SYSFS_INTERFACE */ NULL }; -static void inv_init_regulator(struct inv_gyro_state_s *st, - struct i2c_client *client) -{ - int result; - - /* Power regulator registration*/ - st->inv_regulator.regulator_vdd = - devm_regulator_get(&client->dev, "vdd"); - if (IS_ERR(st->inv_regulator.regulator_vdd)) { - dev_info(&client->adapter->dev, - "regulator_get for vdd failed: %s\n", - dev_name(&client->dev)); - dev_info(&client->adapter->dev, - "Error code for vdd: %d", - PTR_ERR(st->inv_regulator.regulator_vdd)); - goto err_null_regulator; - } - - st->inv_regulator.regulator_vlogic = - devm_regulator_get(&client->dev, "vlogic"); - if (IS_ERR(st->inv_regulator.regulator_vlogic)) { - dev_info(&client->adapter->dev, - "regulator_get for vlogic failed: %s\n", - dev_name(&client->dev)); - dev_info(&client->adapter->dev, - "Error code for vlogic: %d\n", - PTR_ERR(st->inv_regulator.regulator_vlogic)); - goto err_put_regulator; - } - - dev_info(&client->adapter->dev, - "regulator_get for vdd and vlogic succeeded: %s\n", - dev_name(&client->dev)); - - result = regulator_enable(st->inv_regulator.regulator_vdd); - if (result < 0) - dev_err(&client->adapter->dev, - "regulator_enable for vdd failed: %d\n", result); - - result = regulator_enable(st->inv_regulator.regulator_vlogic); - if (result < 0) - dev_err(&client->adapter->dev, - "regulator_enable for vlogic failed: %d\n", result); - - return; - -err_put_regulator: - devm_regulator_put(st->inv_regulator.regulator_vdd); -err_null_regulator: - st->inv_regulator.regulator_vdd = NULL; - st->inv_regulator.regulator_vlogic = NULL; -} - -static int inv_setup_compass(struct inv_gyro_state_s *st) -{ - int result; - unsigned char data[4]; - - result = inv_i2c_read(st, 1, 1, data); - if (result) - return result; - - data[0] &= ~(0x80); - data[0] |= (st->plat_data.level_shifter << 7); - /*set up VDDIO register */ - result = inv_i2c_single_write(st, 1, 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, 2, 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, 3, - 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; - - printk(KERN_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 | 8); - if (result) - return result; - - /*slave 1 is used for AKM mode change only*/ - /*write mode, slave address 0x0E */ - 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; - 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; - data[0] = 1; - } 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; - data[0] = 1; - } 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] = (1 | (st->compass_scale << 4)); - } - 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; -} - 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; + int result = 0; reg = st->reg; st->mpu_slave = NULL; - if (!strcmp(id->name, "itg3500")) + if (!strcmp(id->name, "itg3500")) { st->chip_type = INV_ITG3500; - else if (!strcmp(id->name, "mpu3050")) { + st->nvi = false; + } else if (!strcmp(id->name, "mpu3050")) { st->chip_type = INV_MPU3050; inv_setup_reg_mpu3050(reg); - } else if (!strcmp(id->name, "mpu6050")) + st->nvi = false; + } else if (!strcmp(id->name, "mpu6050")) { st->chip_type = INV_MPU6050; - else if (!strcmp(id->name, "mpu9150")) + st->nvi = true; + } else if (!strcmp(id->name, "mpu9150")) { st->chip_type = INV_MPU9150; - 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; - /*is MPU9150 i2c address hard coded? */ - /*st->plat_data.secondary_i2c_addr = 0xE;*/ - st->has_compass = 1; + st->nvi = true; } if (SECONDARY_SLAVE_TYPE_ACCEL == st->plat_data.sec_slave_type) { if (st->plat_data.sec_slave_id == ACCEL_ID_KXTF9) inv_register_kxtf9_slave(st); } - if (SECONDARY_SLAVE_TYPE_COMPASS == st->plat_data.sec_slave_type) - st->has_compass = 1; - else - st->has_compass = 0; - st->chip_config.gyro_enable = 1; - /*reset register to power up default*/ - result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_RESET); - if (result) - return result; + if (st->nvi) { + nvi_pm_init(st); + } else { + nvi_vreg_init(st); + /*reset register to power up default*/ + result = inv_i2c_single_write(st, reg->pwr_mgmt_1, 0); + result = inv_i2c_single_write(st, reg->pwr_mgmt_1, BIT_RESET); + if (!result) + mdelay(POWER_UP_TIME); + } + return result; +} - mdelay(POWER_UP_TIME); - result = inv_set_power_state(st, 1); - if (result) - return result; +/** + * inv_init_config() - Initialize hardware, disable FIFO. + * @st: Device driver instance. + * Initial configuration: + * FSR: +/- 2000DPS + * DLPF: 42Hz + * FIFO rate: 50Hz + * Clock source: Gyro PLL + */ +static int inv_init_config(struct inv_gyro_state_s *st) +{ + st->chip_config.min_delay_us = MIN_FIFO_RATE; + st->chip_config.lpf = INV_FILTER_42HZ; + st->chip_config.gyro_enable = 0; + st->chip_config.gyro_fifo_enable = 0; + st->chip_config.gyro_fsr = INV_FSR_2000DPS; + st->chip_config.accl_enable = 0; + st->chip_config.accl_fifo_enable = 0; + st->chip_config.accl_fsr = INV_FS_02G; + st->chip_config.mot_dur = 1; + st->chip_config.mot_ctrl = 1; + st->chip_config.mot_cnt = 10; + st->irq_dur_us = 20 * ONE_K_HZ; + st->chip_config.fifo_rate = 50; + st->chip_config.enable = 0; + st->chip_config.dmp_on = 0; + st->chip_config.firmware_loaded = 0; + st->chip_config.prog_start_addr = DMP_START_ADDR; + return 0; +} - if (st->has_compass) { - result = inv_setup_compass(st); - if (result) - return result; +static void inv_input_close(struct input_dev *d) +{ + struct inv_gyro_state_s *st; + + st = input_get_drvdata(d); + inv_pm(st, NVI_PM_OFF_FORCE); + nvi_vreg_exit(st); +} + +/** + * inv_setup_input() - internal setup input device. + * @st: Device driver instance. + * @**idev_in pointer to input device + * @*client i2c client + * @*name name of the input device. + */ +static int inv_setup_input(struct inv_gyro_state_s *st, + struct input_dev **idev_in, + struct i2c_client *client, unsigned char *name) +{ + int result; + struct input_dev *idev; + + idev = input_allocate_device(); + if (!idev) { + result = -ENOMEM; + return result; } - return 0; + /* Setup input device. */ + idev->name = name; + idev->id.bustype = BUS_I2C; + idev->id.product = 'S'; + idev->id.vendor = ('I'<<8) | 'S'; + idev->id.version = 1; + idev->dev.parent = &client->dev; + /* Open and close method. */ + if (strcmp(name, "INV_DMP")) + idev->close = inv_input_close; + input_set_capability(idev, EV_REL, REL_X); + input_set_capability(idev, EV_REL, REL_Y); + input_set_capability(idev, EV_REL, REL_Z); + input_set_capability(idev, EV_REL, REL_RX); + input_set_capability(idev, EV_REL, REL_RY); + input_set_capability(idev, EV_REL, REL_RZ); + input_set_capability(idev, EV_REL, REL_MISC); + input_set_capability(idev, EV_REL, REL_WHEEL); + input_set_drvdata(idev, st); + result = input_register_device(idev); + if (result) + input_free_device(idev); + *idev_in = idev; + return result; } static int inv_create_input(struct inv_gyro_state_s *st, @@ -2578,7 +3205,7 @@ static int inv_create_input(struct inv_gyro_state_s *st, int result; idev = NULL; - result = inv_setup_input(st, &idev, client, st->hw->name); + result = inv_setup_input(st, &idev, client, st->hw_s->name); if (result) return result; @@ -2587,26 +3214,10 @@ static int inv_create_input(struct inv_gyro_state_s *st, return 0; result = inv_setup_input(st, &idev, client, "INV_DMP"); - if (result) { + if (result) input_unregister_device(st->idev); - return result; - } - - st->idev_dmp = idev; - if (!st->has_compass) - return 0; - - if (st->has_compass) { - result = inv_setup_input(st, &idev, client, "INV_COMPASS"); - if (result) { - input_unregister_device(st->idev); - input_unregister_device(st->idev_dmp); - return result; - } - - st->idev_compass = idev; - } - + else + st->idev_dmp = idev; return 0; } @@ -2691,17 +3302,8 @@ static int create_sysfs_interfaces(struct inv_gyro_state_s *st) if (result < 0) goto exit_remove_bin_file; - if (!st->has_compass) - return 0; - - result = create_device_attributes(st->inv_dev, inv_compass_attributes); - if (result < 0) - goto exit_remove_6050_attributes; - return 0; -exit_remove_6050_attributes: - remove_device_attributes(st->inv_dev, inv_mpu6050_attributes); exit_remove_bin_file: sysfs_remove_bin_file(&st->inv_dev->kobj, &dmp_firmware); exit_remove_device_attributes: @@ -2725,20 +3327,65 @@ static void remove_sysfs_interfaces(struct inv_gyro_state_s *st) remove_device_attributes(st->inv_dev, inv_mpu6050_attributes); if (INV_MPU3050 == st->chip_type) inv_mpu3050_remove_sysfs(st); - if (st->has_compass) - remove_device_attributes(st->inv_dev, inv_compass_attributes); device_destroy(st->inv_class, inv_device_dev_t); class_destroy(st->inv_class); st->inv_dev = NULL; st->inv_class = NULL; } -static int inv_mod_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static void nvi_shutdown(struct i2c_client *client) +{ + struct inv_gyro_state_s *inf; + int i; + + inf = i2c_get_clientdata(client); + if (inf != NULL) { + if (inf->nvi) { + for (i = 0; i < AUX_PORT_SPECIAL; i++) { + if (inf->aux.port[i].nmp.shutdown_bypass) { + nvi_aux_bypass_enable(inf, true); + break; + } + } + } + inf->shutdown = true; + if (inf->inv_dev) + remove_sysfs_interfaces(inf); + kfifo_free(&inf->trigger.timestamps); + free_irq(client->irq, inf); + if (inf->idev) + input_unregister_device(inf->idev); + if ((INV_ITG3500 != inf->chip_type) && (inf->idev_dmp)) + input_unregister_device(inf->idev_dmp); + } +} + +static int nvi_remove(struct i2c_client *client) +{ + struct inv_gyro_state_s *inf; + + nvi_shutdown(client); + inf = i2c_get_clientdata(client); + if (inf != NULL) { + if (inf->nvi) { + nvi_pm_exit(inf); + } else { + inv_pm(inf, NVI_PM_OFF_FORCE); + nvi_vreg_exit(inf); + } + kfree(inf); + } + dev_info(&client->dev, "Gyro module removed.\n"); + return 0; +} + +static int nvi_probe(struct i2c_client *client, + const struct i2c_device_id *id) { struct inv_gyro_state_s *st; int result; + pr_info("%s: Probe name %s\n", __func__, id->name); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { result = -ENODEV; goto out_no_free; @@ -2750,24 +3397,22 @@ static int inv_mod_probe(struct i2c_client *client, goto out_no_free; } - inv_init_regulator(st, client); - /* Make state variables available to all _show and _store functions. */ i2c_set_clientdata(client, st); st->i2c = client; st->sl_handle = client->adapter; st->reg = (struct inv_reg_map_s *)&chip_reg ; - st->hw = (struct inv_hw_s *)hw_info; + st->hw_s = (struct inv_hw_s *)hw_info; st->i2c_addr = client->addr; st->plat_data = - *(struct mpu_platform_data *)dev_get_platdata(&client->dev); + *(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; - st->hw = (struct inv_hw_s *)(hw_info + st->chip_type); + st->hw_s = (struct inv_hw_s *)(hw_info + st->chip_type); if (INV_MPU3050 == st->chip_type) result = inv_init_config_mpu3050(st); else @@ -2782,18 +3427,20 @@ static int inv_mod_probe(struct i2c_client *client, result = inv_get_silicon_rev_mpu6050(st); if (result) { dev_err(&client->adapter->dev, - "%s get silicon error.\n", st->hw->name); + "%s get silicon error.\n", st->hw_s->name); goto out_free; } } - result = inv_set_power_state(st, 0); + result = inv_pm(st, NVI_PM_OFF); if (result) { dev_err(&client->adapter->dev, - "%s could not be turned off.\n", st->hw->name); + "%s could not be turned off.\n", st->hw_s->name); goto out_free; } + mutex_init(&st->mutex); + mutex_init(&st->mutex_temp); INIT_KFIFO(st->trigger.timestamps); result = create_sysfs_interfaces(st); if (result) @@ -2826,8 +3473,8 @@ static int inv_mod_probe(struct i2c_client *client, goto out_close_sysfs; } - pr_info("%s: Probe name %s\n", __func__, id->name); - dev_info(&client->adapter->dev, "%s is ready to go!\n", st->hw->name); + inf_local = st; + dev_info(&client->adapter->dev, "%s is ready to go!\n", st->hw_s->name); return 0; out_close_sysfs: @@ -2835,11 +3482,11 @@ out_close_sysfs: out_free_kfifo: kfifo_free(&st->trigger.timestamps); out_free: - result = inv_i2c_single_write(st, st->reg->pwr_mgmt_1, BIT_RESET); - if (st->inv_regulator.regulator_vlogic && - st->inv_regulator.regulator_vdd) { - regulator_disable(st->inv_regulator.regulator_vlogic); - regulator_disable(st->inv_regulator.regulator_vdd); + if (st->nvi) { + nvi_pm_exit(st); + } else { + inv_pm(st, NVI_PM_OFF_FORCE); + nvi_vreg_exit(st); } kfree(st); out_no_free: @@ -2847,45 +3494,6 @@ out_no_free: return -EIO; } -static int inv_mod_remove(struct i2c_client *client) -{ - int result; - struct inv_gyro_state_s *st = i2c_get_clientdata(client); - - result = inv_set_power_state(st, 0); - if (result) - dev_err(&client->adapter->dev, "%s could not be turned off.\n", - st->hw->name); - remove_sysfs_interfaces(st); - result = inv_i2c_single_write(st, st->reg->pwr_mgmt_1, BIT_RESET); - kfifo_free(&st->trigger.timestamps); - free_irq(client->irq, st); - input_unregister_device(st->idev); - if (INV_ITG3500 != st->chip_type) - input_unregister_device(st->idev_dmp); - if (st->has_compass) - input_unregister_device(st->idev_compass); - if (st->inv_regulator.regulator_vlogic && - st->inv_regulator.regulator_vdd) { - regulator_disable(st->inv_regulator.regulator_vlogic); - regulator_disable(st->inv_regulator.regulator_vdd); - } - kfree(st); - dev_info(&client->adapter->dev, "Gyro module removed.\n"); - return 0; -} - -static void inv_i2c_shutdown(struct i2c_client *client) -{ - struct inv_gyro_state_s *st = i2c_get_clientdata(client); - - if (client->irq) - disable_irq(client->irq); - set_inv_enable(st, 0); - inv_set_power_state(st, 0); - st->i2c_shutdown = true; -} - static unsigned short normal_i2c[] = { I2C_CLIENT_END }; /* device id table is used to identify what device can be @@ -2903,8 +3511,8 @@ MODULE_DEVICE_TABLE(i2c, inv_mod_id); static struct i2c_driver inv_mod_driver = { .class = I2C_CLASS_HWMON, - .probe = inv_mod_probe, - .remove = inv_mod_remove, + .probe = nvi_probe, + .remove = nvi_remove, .id_table = inv_mod_id, .driver = { .owner = THIS_MODULE, @@ -2914,7 +3522,7 @@ static struct i2c_driver inv_mod_driver = { #endif }, .address_list = normal_i2c, - .shutdown = inv_i2c_shutdown, + .shutdown = nvi_shutdown, }; static int __init inv_mod_init(void) diff --git a/drivers/input/misc/mpu/inv_gyro.h b/drivers/input/misc/mpu/inv_gyro.h index 8fca85f50dac..9852064f93d6 100644 --- a/drivers/input/misc/mpu/inv_gyro.h +++ b/drivers/input/misc/mpu/inv_gyro.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Invensense, Inc. +* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -33,6 +34,11 @@ #include <linux/regulator/consumer.h> #include "dmpKey.h" + +#define GYRO_INPUT_RESOLUTION (1) +#define ACCL_INPUT_RESOLUTION (1) + + /** * struct inv_reg_map_s - Notable slave registers. * @who_am_i: Upper 6 bits of the device's slave address. @@ -94,16 +100,16 @@ enum inv_devices { * @sample_rate: sensitity for gyro. * @sample_rate: sample rate, i.e, fifo rate. * @lpf: low pass filter. - * @fsr: full scale range. - * @accl_fs: accel full scale range. + * @gyro_fsr: full scale range. + * @accl_fsr: accel full scale range. * @accl_sens: accel sensitivity */ struct test_setup_t { int gyro_sens; int sample_rate; int lpf; - int fsr; - int accl_fs; + int gyro_fsr; + int accl_fsr; unsigned int accl_sens[3]; }; @@ -119,10 +125,10 @@ struct inv_hw_s { /** * struct inv_chip_config_s - Cached chip configuration data. - * @fsr: Full scale range. + * @gyro_fsr: Full scale range. * @lpf: Digital low pass filter frequency. * @clk_src: Clock source. - * @accl_fs: accel full scale range. + * @accl_fsr: accel full scale range. * @fifo_rate: FIFO update rate. * @enable: master enable to enable output * @accl_enable: enable accel functionality @@ -138,23 +144,29 @@ struct inv_hw_s { * @prog_start_addr: firmware program start address */ struct inv_chip_config_s { - unsigned char fsr; unsigned char lpf; unsigned char clk_src; - unsigned char accl_fs; - unsigned short fifo_rate; unsigned char enable; - unsigned char accl_enable; - unsigned char accl_fifo_enable; unsigned char gyro_enable; unsigned char gyro_fifo_enable; - unsigned char compass_enable; - unsigned char is_asleep; + unsigned long gyro_delay_us; + unsigned int gyro_resolution; + unsigned char gyro_fsr; + unsigned char accl_enable; + unsigned char accl_fifo_enable; + unsigned long accl_delay_us; + unsigned int accl_resolution; + unsigned char accl_fsr; + unsigned long lpa_delay_us; + unsigned char temp_fifo_enable; unsigned char dmp_on; unsigned char firmware_loaded; - unsigned char lpa_mode; - unsigned char lpa_freq; + unsigned char mot_dur; + unsigned char mot_ctrl; + unsigned int mot_cnt; + unsigned char fifo_rate; unsigned int prog_start_addr; + unsigned long min_delay_us; }; /** @@ -230,6 +242,67 @@ struct inv_regulator_s { struct regulator *regulator_vdd; }; +struct nvi_hw { + unsigned char aux_vddio; + unsigned char smplrt_div; + unsigned char config; + unsigned char gyro_config; + unsigned char accl_config; + unsigned char mot_thr; + unsigned char mot_dur; + unsigned char zrmot_thr; + unsigned char zrmot_dur; + unsigned char fifo_en; + unsigned char i2c_mst_ctrl; + unsigned char i2c_slv4_ctrl; + unsigned char int_pin_cfg; + unsigned char int_enable; + unsigned char i2c_mst_delay_ctrl; + unsigned char mot_detect_ctrl; + unsigned char user_ctrl; + unsigned char pwr_mgmt_1; + unsigned char pwr_mgmt_2; +}; + +#define AUX_PORT_MAX (5) +#define AUX_PORT_SPECIAL (4) +#define AUX_PORT_BYPASS (-1) +#define AUX_EXT_DATA_REG_MAX (24) +#define AUX_DEV_VALID_READ_MAX (10) + +struct aux_port { + struct nvi_mpu_port nmp; + long long delay_ns; + unsigned short ext_data_offset; + bool hw_valid; + bool hw_en; + bool hw_do; + bool enable; + bool fifo_en; +}; + +struct aux_ports { + struct aux_port port[AUX_PORT_MAX]; + int bypass_lock; + unsigned char delay_hw; + unsigned short ext_data_n; + unsigned char ext_data[AUX_EXT_DATA_REG_MAX]; + unsigned char clock_i2c; + bool need_reset; + bool enable; + bool dbg; +}; + +#define NVI_PM_ERR 0 +#define NVI_PM_AUTO 1 +#define NVI_PM_OFF_FORCE 2 +#define NVI_PM_OFF 3 +#define NVI_PM_STDBY 4 +#define NVI_PM_ON_CYCLE 5 +#define NVI_PM_ON 6 +#define NVI_PM_ON_FULL 7 + + struct inv_mpu_slave; /** * struct inv_gyro_state_s - Driver state variables. @@ -267,35 +340,47 @@ struct inv_gyro_state_s { struct inv_chip_config_s chip_config; struct inv_chip_info_s chip_info; struct inv_flick_s flick; - struct inv_tap_s tap; + struct inv_tap_s tap; struct inv_reg_map_s *reg; - struct inv_hw_s *hw; + struct inv_hw_s *hw_s; struct inv_trigger_s trigger; struct input_dev *idev; struct input_dev *idev_dmp; - struct input_dev *idev_compass; enum inv_devices chip_type; spinlock_t time_stamp_lock; struct class *inv_class; - struct device *inv_dev; + struct device *inv_dev; struct i2c_client *i2c; struct mpu_platform_data plat_data; struct inv_mpu_slave *mpu_slave; - short compass_st_upper[3]; - short compass_st_lower[3]; + struct regulator_bulk_data vreg[2]; unsigned char fifo_counter; - unsigned char has_compass; - unsigned char compass_scale; unsigned char i2c_addr; - unsigned char compass_divider; - unsigned char compass_counter; unsigned char sample_divider; unsigned char fifo_divider; void *sl_handle; unsigned int irq_dur_us; long long last_isr_time; - struct inv_regulator_s inv_regulator; - bool i2c_shutdown; + struct mutex mutex; + struct mutex mutex_temp; + struct nvi_hw hw; + struct aux_ports aux; + int pm; + int stby; + int lpa_hw; + unsigned long sample_delay_us; + bool shutdown; + bool nvi; + bool lpa_enable; + bool mot_enable; + bool mot_dbg; + unsigned int mot_cnt; + short temp_val; + s64 temp_ts; +#if DEBUG_SYSFS_INTERFACE + unsigned short dbg_i2c_addr; + unsigned char dbg_reg; +#endif /* DEBUG_SYSFS_INTERFACE */ }; /* produces an unique identifier for each device based on the @@ -325,52 +410,6 @@ struct inv_mpu_slave { int (*set_lpf)(struct inv_gyro_state_s *, int rate); int (*set_fs)(struct inv_gyro_state_s *, int fs); }; -/* AKM definitions */ -#define REG_AKM_ID (0x00) -#define REG_AKM_STATUS (0x02) -#define REG_AKM_MEASURE_DATA (0x03) -#define REG_AKM_MODE (0x0A) -#define REG_AKM_ST_CTRL (0x0C) -#define REG_AKM_SENSITIVITY (0x10) -#define REG_AKM8963_CNTL1 (0x0A) - -#define DATA_AKM_ID (0x48) -#define DATA_AKM_MODE_PW_DN (0x00) -#define DATA_AKM_MODE_PW_SM (0x01) -#define DATA_AKM_MODE_PW_ST (0x08) -#define DATA_AKM_MODE_PW_FR (0x0F) -#define DATA_AKM_SELF_TEST (0x40) -#define DATA_AKM_DRDY (0x01) -#define DATA_AKM8963_BIT (0x10) -#define DATA_AKM_STAT_MASK (0x0C) - -#define DATA_AKM8975_SCALE (9830) -#define DATA_AKM8972_SCALE (19661) -#define DATA_AKM8963_SCALE0 (19661) -#define DATA_AKM8963_SCALE1 (4915) - -#define DATA_AKM8975_ST_X_UP (100) -#define DATA_AKM8975_ST_X_LW (-100) -#define DATA_AKM8975_ST_Y_UP (100) -#define DATA_AKM8975_ST_Y_LW (-100) -#define DATA_AKM8975_ST_Z_UP (-300) -#define DATA_AKM8975_ST_Z_LW (-1000) - -/*need to verify the range for 8972 */ -#define DATA_AKM8972_ST_X_UP (50) -#define DATA_AKM8972_ST_X_LW (-50) -#define DATA_AKM8972_ST_Y_UP (50) -#define DATA_AKM8972_ST_Y_LW (-50) -#define DATA_AKM8972_ST_Z_UP (-100) -#define DATA_AKM8972_ST_Z_LW (-500) - -#define DATA_AKM8963_ST_X_UP (200) -#define DATA_AKM8963_ST_X_LW (-200) -#define DATA_AKM8963_ST_Y_UP (200) -#define DATA_AKM8963_ST_Y_LW (-200) -#define DATA_AKM8963_ST_Z_UP (-800) -#define DATA_AKM8963_ST_Z_LW (-3200) - /* register definition*/ #define REG_3050_AUX_VDDIO (0x13) @@ -379,22 +418,34 @@ struct inv_mpu_slave { #define REG_3050_AUX_XOUT_H (0x23) #define REG_3500_OTP (0x00) - +#define REG_AUX_VDDIO (0x01) #define REG_ST_GCT_X (0x0D) +#define REG_MOT_THR (0x1F) +#define REG_MOT_DUR (0x20) +#define REG_ZMOT_THR (0x21) +#define REG_ZMOT_DUR (0x22) #define REG_I2C_MST_CTRL (0x24) +#define BIT_SLV3_FIFO_EN (0x20) #define REG_I2C_SLV0_ADDR (0x25) #define REG_I2C_SLV0_REG (0x26) #define REG_I2C_SLV0_CTRL (0x27) -#define REG_I2C_SLV1_ADDR (0x28) -#define REG_I2C_SLV1_REG (0x29) -#define REG_I2C_SLV1_CTRL (0x2A) - +#define BITS_I2C_SLV_CTRL_LEN (0x0F) +#define BITS_I2C_SLV_REG_DIS (0x10) +#define REG_I2C_SLV4_ADDR (0x31) +#define REG_I2C_SLV4_REG (0x32) +#define REG_I2C_SLV4_DO (0x33) #define REG_I2C_SLV4_CTRL (0x34) +#define REG_I2C_SLV4_DI (0x35) +#define REG_I2C_MST_STATUS (0x36) +#define REG_I2C_SLV0_DO (0x63) +#define REG_FIFO_EN (0x23) + +#define BITS_I2C_MST_DLY (0x1F) #define REG_INT_PIN_CFG (0x37) #define REG_DMP_INT_STATUS (0x39) #define REG_EXT_SENS_DATA_00 (0x49) -#define REG_I2C_SLV1_DO (0x64) #define REG_I2C_MST_DELAY_CTRL (0x67) +#define REG_MOT_DETECT_CTRL (0x69) #define REG_BANK_SEL (0x6D) #define REG_MEM_START (0x6E) #define REG_MEM_RW (0x6F) @@ -407,27 +458,32 @@ struct inv_mpu_slave { #define BIT_BYPASS_EN (0x2) #define BIT_WAIT_FOR_ES (0x40) +#define BIT_I2C_MST_P_NSR (0x10) #define BIT_I2C_READ (0x80) #define BIT_SLV_EN (0x80) #define BIT_DMP_EN (0x80) -#define BIT_FIFO_EN (0x40) +#define BIT_FIFO_EN (0x40) #define BIT_I2C_MST_EN (0x20) #define BIT_DMP_RST (0x08) -#define BIT_FIFO_RST (0x04) +#define BIT_FIFO_RST (0x04) #define BIT_I2C_MST_RST (0x02) #define BIT_SLV0_DLY_EN (0x01) #define BIT_SLV1_DLY_EN (0x02) +#define BIT_DELAY_ES_SHADOW (0x80) -#define BIT_FIFO_OVERFLOW (0x10) -#define BIT_DATA_RDY_EN (0x01) +#define BIT_MOT_EN (0x40) +#define BIT_ZMOT_EN (0x20) +#define BIT_FIFO_OVERFLOW (0x10) +#define BIT_DATA_RDY_EN (0x01) #define BIT_DMP_INT_EN (0x02) #define BIT_PWR_ACCL_STBY (0x38) #define BIT_PWR_GYRO_STBY (0x07) -#define BIT_GYRO_XOUT (0x40) +#define BIT_TEMP_FIFO_EN (0x80) +#define BIT_GYRO_XOUT (0x40) #define BIT_GYRO_YOUT (0x20) #define BIT_GYRO_ZOUT (0x10) #define BIT_ACCEL_OUT (0x08) @@ -464,9 +520,15 @@ struct inv_mpu_slave { #define TIME_STAMP_TOR (5) #define MAX_CATCH_UP (5) #define DEFAULT_ACCL_TRIM (16384) -#define MAX_FIFO_RATE (1000) -#define MIN_FIFO_RATE (4) +#define MAX_FIFO_RATE (1000000) +#define MIN_FIFO_RATE (4000) #define ONE_K_HZ (1000) +#define NVI_DELAY_US_MAX (256000) +#define NVI_DELAY_US_MIN (125) +#define NVI_DELAY_DEFAULT (50000) +#define NVI_INPUT_GYRO_DELAY_US_MIN (125) +#define NVI_INPUT_ACCL_DELAY_US_MIN (1000) + /* authenticate key */ #define D_AUTH_OUT (32) @@ -606,8 +668,6 @@ int mpu_memory_read(struct i2c_adapter *i2c_adap, void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg); int inv_init_config_mpu3050(struct inv_gyro_state_s *st); irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id); -int inv_reset_fifo(struct inv_gyro_state_s *st); -void inv_clear_kfifo(struct inv_gyro_state_s *st); int inv_setup_mpu3050(struct inv_gyro_state_s *st); int inv_register_kxtf9_slave(struct inv_gyro_state_s *st); int create_device_attributes(struct device *dev, @@ -620,28 +680,28 @@ int inv_mpu3050_create_sysfs(struct inv_gyro_state_s *st); int inv_mpu3050_remove_sysfs(struct inv_gyro_state_s *st); int inv_get_accl_bias(struct inv_gyro_state_s *st, int *accl_bias_regular); int set_power_mpu3050(struct inv_gyro_state_s *st, unsigned char power_on); -int reset_fifo_mpu3050(struct inv_gyro_state_s *st); int inv_enable_tap_dmp(struct inv_gyro_state_s *st, unsigned char on); int inv_enable_orientation_dmp(struct inv_gyro_state_s *st); unsigned short inv_dmp_get_address(unsigned short key); -ssize_t inv_gyro_fifo_enable_show(struct device *dev, - struct device_attribute *attr, char *buf); -ssize_t inv_gyro_fifo_enable_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count); -ssize_t inv_gyro_enable_show(struct device *dev, - struct device_attribute *attr, char *buf); -ssize_t inv_gyro_enable_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count); -ssize_t inv_accl_enable_show(struct device *dev, - struct device_attribute *attr, char *buf); -ssize_t inv_accl_fifo_enable_show(struct device *dev, - struct device_attribute *attr, char *buf); -ssize_t inv_accl_fs_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count); -ssize_t inv_accl_fs_show(struct device *dev, - struct device_attribute *attr, char *buf); +ssize_t nvi_gyro_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t nvi_gyro_fifo_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t nvi_gyro_max_range_show(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t nvi_accl_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t nvi_accl_fifo_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t nvi_accl_delay_show(struct device *dev, + struct device_attribute *attr, char *buf); +ssize_t nvi_accl_max_range_show(struct device *dev, + struct device_attribute *attr, char *buf); ssize_t inv_accl_matrix_show(struct device *dev, - struct device_attribute *attr, char *buf); + struct device_attribute *attr, char *buf); +ssize_t nvi_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); + ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t size); diff --git a/drivers/input/misc/mpu/inv_gyro_misc.c b/drivers/input/misc/mpu/inv_gyro_misc.c index 80bc3ca86411..0a058dea9b3a 100644 --- a/drivers/input/misc/mpu/inv_gyro_misc.c +++ b/drivers/input/misc/mpu/inv_gyro_misc.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Invensense, Inc. +* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -76,11 +77,11 @@ #define DEF_GYRO_CT_SHIFT_MAX (105) static struct test_setup_t test_setup = { - .gyro_sens = 32768 / 250, - .sample_rate = DEF_SELFTEST_SAMPLE_RATE, - .lpf = DEF_SELFTEST_LPF_PARA, - .fsr = DEF_SELFTEST_GYRO_FULL_SCALE, - .accl_fs = DEF_SELFTEST_ACCL_FULL_SCALE + .gyro_sens = 32768 / 250, + .sample_rate = DEF_SELFTEST_SAMPLE_RATE, + .lpf = DEF_SELFTEST_LPF_PARA, + .gyro_fsr = DEF_SELFTEST_GYRO_FULL_SCALE, + .accl_fsr = DEF_SELFTEST_ACCL_FULL_SCALE }; /* NOTE: product entries are in chronological order */ @@ -623,13 +624,13 @@ static int inv_do_test(struct inv_gyro_state_s *st, int self_test_flag, return result; result = inv_i2c_single_write(st, reg->gyro_config, - self_test_flag | test_setup.fsr); + self_test_flag | test_setup.gyro_fsr); if (result) return result; if (has_accl) { result = inv_i2c_single_write(st, reg->accl_config, - self_test_flag | test_setup.accl_fs); + self_test_flag | test_setup.accl_fsr); if (result) return result; } @@ -717,111 +718,16 @@ static void inv_recover_setting(struct inv_gyro_state_s *st) reg = st->reg; set_inv_enable(st, st->chip_config.enable); - inv_i2c_single_write(st, reg->gyro_config, st->chip_config.fsr<<3); + inv_i2c_single_write(st, reg->gyro_config, + st->chip_config.gyro_fsr<<3); inv_i2c_single_write(st, reg->lpf, st->chip_config.lpf); data = ONE_K_HZ/st->chip_config.fifo_rate - 1; inv_i2c_single_write(st, reg->sample_rate_div, data); if (INV_ITG3500 != st->chip_type) { inv_i2c_single_write(st, reg->accl_config, - (st->chip_config.accl_fs << 3)|0); + (st->chip_config.accl_fsr << 3)|0); } - if (st->chip_config.is_asleep) - inv_set_power_state(st, 0); - else - inv_set_power_state(st, 1); -} - -static int inv_check_compass_self_test(struct inv_gyro_state_s *st) -{ - int result; - unsigned char data[6]; - unsigned char counter, cntl; - short x, y, z; - unsigned char *sens; - - sens = st->chip_info.compass_sens; - /*set to bypass mode */ - result = inv_i2c_single_write(st, REG_INT_PIN_CFG, BIT_BYPASS_EN); - if (result) { - inv_i2c_single_write(st, REG_INT_PIN_CFG, 0x0); - return result; - } - - /*set to power down mode */ - result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_DN); - if (result) - goto AKM_fail; - - /*write 1 to ASTC register */ - result = inv_secondary_write(REG_AKM_ST_CTRL, DATA_AKM_SELF_TEST); - if (result) - goto AKM_fail; - - /*set self test mode */ - result = inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_ST); - if (result) - goto AKM_fail; - - counter = 10; - while (counter > 0) { - mdelay(10); - result = inv_secondary_read(REG_AKM_STATUS, 1, data); - if (result) - goto AKM_fail; - - if (data[0] != DATA_AKM_DRDY) - counter--; - else - counter = 0; - } - - if (data[0] != DATA_AKM_DRDY) { - result = -1; - goto AKM_fail; - } - - result = inv_secondary_read(REG_AKM_MEASURE_DATA, 6, data); - if (result) - goto AKM_fail; - - x = (short)((data[1]<<8) | data[0]); - y = (short)((data[3]<<8) | data[2]); - z = (short)((data[5]<<8) | data[4]); - x = ((x * (sens[0] + 128)) >> 8); - y = ((y * (sens[1] + 128)) >> 8); - z = ((z * (sens[2] + 128)) >> 8); - if (COMPASS_ID_AK8963 == st->plat_data.sec_slave_id) { - result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl); - if (result) - goto AKM_fail; - - if (0 == (cntl & DATA_AKM8963_BIT)) { - x <<= 2; - y <<= 2; - z <<= 2; - } - } - - result = 1; - result = inv_secondary_read(REG_AKM8963_CNTL1, 1, &cntl); - if (x > st->compass_st_upper[0] || x < st->compass_st_lower[0]) - goto AKM_fail; - - if (y > st->compass_st_upper[1] || y < st->compass_st_lower[1]) - goto AKM_fail; - - if (z > st->compass_st_upper[2] || z < st->compass_st_lower[2]) - goto AKM_fail; - - result = 0; -AKM_fail: - /*write 0 to ASTC register */ - result |= inv_secondary_write(REG_AKM_ST_CTRL, 0); - /*set to power down mode */ - result |= inv_secondary_write(REG_AKM_MODE, DATA_AKM_MODE_PW_DN); - /*restore to non-bypass mode */ - result |= inv_i2c_single_write(st, REG_INT_PIN_CFG, 0x0); - return result; + inv_set_power_state(st, 1); } /** @@ -834,9 +740,9 @@ int inv_hw_self_test(struct inv_gyro_state_s *st, int gyro_bias_st[3]; int accl_bias_st[3], accl_bias_regular[3]; int test_times; - char compass_result, accel_result, gyro_result; + char accel_result, gyro_result; - compass_result = accel_result = gyro_result = 0; + accel_result = gyro_result = 0; test_times = 2; while (test_times > 0) { result = inv_do_test(st, 0, gyro_bias_regular, @@ -865,8 +771,6 @@ int inv_hw_self_test(struct inv_gyro_state_s *st, gyro_result = !inv_check_3500_gyro_self_test(st, gyro_bias_regular, gyro_bias_st); } else { - if (st->has_compass) - compass_result = !inv_check_compass_self_test(st); accel_result = !inv_check_accl_self_test(st, accl_bias_regular, accl_bias_st); gyro_result = !inv_check_6050_gyro_self_test(st, @@ -874,7 +778,7 @@ int inv_hw_self_test(struct inv_gyro_state_s *st, } test_fail: inv_recover_setting(st); - return (compass_result<<2) | (accel_result<<1) | gyro_result; + return (accel_result<<1) | gyro_result; } /** @@ -1517,9 +1421,6 @@ ssize_t inv_dmp_firmware_write(struct file *fp, struct kobject *kobj, struct inv_reg_map_s *reg; st = dev_get_drvdata(container_of(kobj, struct device, kobj)); - if (st->chip_config.is_asleep) - return -EPERM; - if (1 == st->chip_config.firmware_loaded) return -EINVAL; diff --git a/drivers/input/misc/mpu/inv_mpu3050.c b/drivers/input/misc/mpu/inv_mpu3050.c index 96a6e22d5665..205bf456f974 100644 --- a/drivers/input/misc/mpu/inv_mpu3050.c +++ b/drivers/input/misc/mpu/inv_mpu3050.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Invensense, Inc. +* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -38,77 +39,292 @@ #include <linux/spinlock.h> #include "inv_gyro.h" -int set_3050_bypass(struct inv_gyro_state_s *st, int enable) + +static unsigned long nvi_lpf_us_tbl[] = { + 0, /* N/A */ + 5319, /* 188Hz */ + 10204, /* 98Hz */ + 23810, /* 42Hz */ + 50000, /* 20Hz */ + 100000, /* 10Hz */ + /* 200000, 5Hz */ +}; + + +/** + * inv_clear_kfifo() - clear time stamp fifo + * @st: Device driver instance. + */ +static void inv_clear_kfifo(struct inv_gyro_state_s *st) +{ + unsigned long flags; + spin_lock_irqsave(&st->time_stamp_lock, flags); + kfifo_reset(&st->trigger.timestamps); + spin_unlock_irqrestore(&st->time_stamp_lock, flags); +} + +/** + * mpu3050_fifo_reset() - Reset FIFO related registers + * @st: Device driver instance. + */ +static int mpu3050_fifo_reset(struct inv_gyro_state_s *st) { struct inv_reg_map_s *reg; int result; - unsigned char b; + unsigned char val, user_ctrl; reg = st->reg; - result = inv_i2c_read(st, reg->user_ctrl, 1, &b); + /* disable interrupt */ + result = inv_i2c_single_write(st, reg->int_enable, 0); if (result) return result; - if (((b & BIT_3050_AUX_IF_EN) == 0) && enable) - return 0; - if ((b & BIT_3050_AUX_IF_EN) && (enable == 0)) - return 0; - b &= ~BIT_3050_AUX_IF_EN; - if (!enable) { - b |= BIT_3050_AUX_IF_EN; - result = inv_i2c_single_write(st, reg->user_ctrl, b); - return result; + + inv_clear_kfifo(st); + /* disable the sensor output to FIFO */ + result = inv_i2c_single_write(st, reg->fifo_en, 0); + if (result) + goto reset_fifo_fail; + result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl); + if (result) + goto reset_fifo_fail; + /* disable fifo reading */ + user_ctrl &= ~BIT_FIFO_EN; + st->fifo_counter = 0; + /* reset fifo */ + val = (BIT_3050_FIFO_RST | user_ctrl); + result = inv_i2c_single_write(st, reg->user_ctrl, val); + if (result) + goto reset_fifo_fail; + mdelay(POWER_UP_TIME); + st->last_isr_time = get_time_ns(); + if (st->chip_config.dmp_on) { + /* enable interrupt when DMP is done */ + result = inv_i2c_single_write(st, reg->int_enable, + BIT_DMP_INT_EN); + if (result) + return result; + + result = inv_i2c_single_write(st, reg->user_ctrl, + BIT_FIFO_EN|user_ctrl); + if (result) + return result; } else { - /* Coming out of I2C is tricky due to several erratta. Do not - * modify this algorithm - */ - /* - * 1) wait for the right time and send the command to change - * the aux i2c slave address to an invalid address that will - * get nack'ed - * - * 0x00 is broadcast. 0x7F is unlikely to be used by any aux. - */ - result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, - 0x7F); + /* enable interrupt */ + if (st->chip_config.accl_fifo_enable || + st->chip_config.gyro_fifo_enable){ + result = inv_i2c_single_write(st, reg->int_enable, + BIT_DATA_RDY_EN); + if (result) + return result; + } + /* enable FIFO reading and I2C master interface*/ + result = inv_i2c_single_write(st, reg->user_ctrl, + BIT_FIFO_EN | user_ctrl); if (result) return result; - /* - * 2) wait enough time for a nack to occur, then go into - * bypass mode: - */ - mdelay(2); - result = inv_i2c_single_write(st, reg->user_ctrl, b); + /* enable sensor output to FIFO and FIFO footer*/ + val = 1; + if (st->chip_config.accl_fifo_enable) + val |= BITS_3050_ACCL_OUT; + if (st->chip_config.gyro_fifo_enable) + val |= BITS_GYRO_OUT; + result = inv_i2c_single_write(st, reg->fifo_en, val); if (result) return result; - /* - * 3) wait for up to one MPU cycle then restore the slave - * address - */ - mdelay(20); - result = inv_i2c_single_write(st, REG_3050_SLAVE_REG, - st->plat_data.secondary_read_reg); + } + + return 0; +reset_fifo_fail: + if (st->chip_config.dmp_on) + val = BIT_DMP_INT_EN; + else + val = BIT_DATA_RDY_EN; + inv_i2c_single_write(st, reg->int_enable, val); + pr_err("%s failed\n", __func__); + return result; +} + +/** + * set_power_mpu3050() - set power of mpu3050. + * @st: Device driver instance. + * @power_on: on/off + */ +int set_power_mpu3050(struct inv_gyro_state_s *st, unsigned char power_on) +{ + struct inv_reg_map_s *reg; + unsigned char data, p; + int result; + reg = st->reg; + if (power_on) { + data = 0; + } else { + if (st->mpu_slave) { + result = st->mpu_slave->suspend(st); + if (result) + return result; + } + + data = BIT_SLEEP; + } + if (st->chip_config.gyro_enable) { + p = (BITS_3050_POWER1 | INV_CLK_PLL); + result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); if (result) return result; - result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, - st->plat_data.secondary_i2c_addr); + p = (BITS_3050_POWER2 | INV_CLK_PLL); + result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); if (result) return result; - result = inv_i2c_single_write(st, reg->user_ctrl, - (b | BIT_3050_AUX_IF_RST)); + p = INV_CLK_PLL; + result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); if (result) return result; - mdelay(2); + st->chip_config.clk_src = INV_CLK_PLL; + } else { + data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL); + result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data); + if (result) + return result; + st->chip_config.clk_src = INV_CLK_INTERNAL; } + if (power_on) { + mdelay(POWER_UP_TIME); + if (st->mpu_slave) { + result = st->mpu_slave->resume(st); + if (result) + return result; + } + } + return 0; } /** + * inv_set_power_state() - Turn device on/off. + * @st: Device driver instance. + * @power_on: 1 to turn on, 0 to suspend. + */ +int inv_set_power_state(struct inv_gyro_state_s *st, unsigned char power_on) +{ + return set_power_mpu3050(st, power_on); +} + +/** + * set_inv_enable() - Reset FIFO related registers. + * @st: Device driver instance. + * @fifo_enable: enable/disable + */ +int set_inv_enable(struct inv_gyro_state_s *st, unsigned long enable) +{ + struct inv_reg_map_s *reg; + int result; + + reg = st->reg; + if (enable) { + result = mpu3050_fifo_reset(st); + if (result) + return result; + + st->chip_config.enable = 1; + } else { + result = inv_i2c_single_write(st, reg->fifo_en, 0); + if (result) + return result; + + result = inv_i2c_single_write(st, reg->int_enable, 0); + if (result) + return result; + + st->chip_config.enable = 0; + } + + return 0; +} + +static int mpu3050_global_delay(struct inv_gyro_state_s *inf) +{ + unsigned long delay_us; + unsigned char val; + int rate; + int err = 0; + + /* find the fastest polling of all the devices */ + delay_us = -1; + if (inf->chip_config.gyro_enable && inf->chip_config.gyro_delay_us) { + if (inf->chip_config.gyro_delay_us < delay_us) + delay_us = inf->chip_config.gyro_delay_us; + } + if (inf->chip_config.accl_enable && inf->chip_config.accl_delay_us) { + if (inf->chip_config.accl_delay_us < delay_us) + delay_us = inf->chip_config.accl_delay_us; + } + if (delay_us == -1) + delay_us = NVI_DELAY_DEFAULT; /* default if nothing found */ + if (delay_us < MIN_FIFO_RATE) + delay_us = MIN_FIFO_RATE; + if (delay_us > MAX_FIFO_RATE) + delay_us = MAX_FIFO_RATE; + /* program if new value */ + if (delay_us != inf->sample_delay_us) { + dev_dbg(&inf->i2c->dev, "%s %lu\n", __func__, delay_us); + inf->sample_delay_us = delay_us; + val = delay_us / ONE_K_HZ - 1; + err = inv_i2c_single_write(inf, inf->reg->sample_rate_div, + val); + rate = 1000000 / delay_us; + inf->irq_dur_us = delay_us; + delay_us <<= 1; + for (val = 1; val < ARRAY_SIZE(nvi_lpf_us_tbl); val++) { + if (delay_us < nvi_lpf_us_tbl[val]) + break; + } + if (inf->mpu_slave != NULL) + err |= inf->mpu_slave->set_lpf(inf, rate); + err |= inv_i2c_single_write(inf, inf->reg->lpf, val | + (inf->chip_config.gyro_fsr << 3)); + inf->last_isr_time = get_time_ns(); + } + return err; +} + +static int mpu3050_gyro_enable(struct inv_gyro_state_s *inf, + unsigned char enable, unsigned char fifo_enable) +{ + unsigned char enable_old; + unsigned char fifo_enable_old; + unsigned char val; + int err = 0; + + enable_old = inf->chip_config.gyro_enable; + fifo_enable_old = inf->chip_config.gyro_fifo_enable; + inf->chip_config.gyro_fifo_enable = fifo_enable; + inf->chip_config.gyro_enable = enable; + set_power_mpu3050(inf, 1); + if (enable != enable_old) { + if (enable) { + val = (inf->chip_config.gyro_fsr << 3); + val |= inf->chip_config.lpf; + err = inv_i2c_single_write(inf, inf->reg->lpf, val); + } + mpu3050_global_delay(inf); + } + if (fifo_enable_old != fifo_enable) + mpu3050_fifo_reset(inf); + if (err) { + inf->chip_config.gyro_enable = enable_old; + inf->chip_config.gyro_fifo_enable = fifo_enable_old; + } + set_power_mpu3050(inf, 1); + return err; +} + +/** * inv_raw_accl_show() - Read accel data directly from registers. */ -static ssize_t inv_raw_accl_mpu3050_show(struct device *dev, +static ssize_t mpu3050_raw_accl_show(struct device *dev, struct device_attribute *attr, char *buf) { unsigned char data[6]; @@ -132,14 +348,12 @@ static ssize_t inv_raw_accl_mpu3050_show(struct device *dev, /** * inv_accl_fifo_enable_store() - Enable/disable accl fifo output. */ -static ssize_t inv_accl_fifo_mpu3050_enable_store(struct device *dev, +static ssize_t mpu3050_accl_fifo_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long en; struct inv_gyro_state_s *st; st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EINVAL; if (kstrtoul(buf, 10, &en)) return -EINVAL; if (en == !(!(st->chip_config.accl_fifo_enable))) @@ -147,22 +361,19 @@ static ssize_t inv_accl_fifo_mpu3050_enable_store(struct device *dev, st->chip_config.accl_fifo_enable = en; if (en && (0 == st->chip_config.accl_enable)) { st->chip_config.accl_enable = en; - if (st->mpu_slave != NULL) - st->mpu_slave->resume(st); + set_power_mpu3050(st, 1); } return count; } /** - * inv_accl_mpu3050_enable_store() - Enable/disable accl. + * mpu3050_accl_enable_store() - Enable/disable accl. */ -static ssize_t inv_accl_mpu3050_enable_store(struct device *dev, +static ssize_t mpu3050_accl_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { unsigned long en; struct inv_gyro_state_s *st; st = dev_get_drvdata(dev); - if (st->chip_config.is_asleep) - return -EINVAL; if (kstrtoul(buf, 10, &en)) return -EINVAL; if (en == !(!(st->chip_config.accl_enable))) @@ -172,36 +383,245 @@ static ssize_t inv_accl_mpu3050_enable_store(struct device *dev, st->chip_config.accl_fifo_enable = 0; if (st->mpu_slave != NULL) { if (en) - st->mpu_slave->resume(st); + set_power_mpu3050(st, 1); + else + set_power_mpu3050(st, 0); + } + return count; +} + +static ssize_t mpu3050_accl_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned long accl_delay_us; + unsigned long accl_delay_us_old; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &accl_delay_us); + if (err) + return err; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %lu\n", __func__, accl_delay_us); + if (accl_delay_us < NVI_INPUT_ACCL_DELAY_US_MIN) + accl_delay_us = NVI_INPUT_ACCL_DELAY_US_MIN; + if (accl_delay_us != inf->chip_config.accl_delay_us) { + accl_delay_us_old = inf->chip_config.accl_delay_us; + inf->chip_config.accl_delay_us = accl_delay_us; + if (inf->chip_config.accl_enable) { + err = mpu3050_global_delay(inf); + if (!err) + inf->chip_config.accl_delay_us = + accl_delay_us_old; + } + } + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %lu ERR=%d\n", + __func__, accl_delay_us, err); + return err; + } + + return count; +} + +static ssize_t mpu3050_accl_max_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char fsr; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &fsr); + if (err) + return -EINVAL; + + if (fsr > 3) + return -EINVAL; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, fsr); + if (fsr != inf->chip_config.accl_fsr) { + inf->chip_config.accl_fsr = fsr; + if (inf->chip_config.accl_enable) { + if ((inf->chip_type == INV_MPU3050) && + (inf->mpu_slave != NULL)) { + err = inf->mpu_slave->set_fs(inf, fsr); + /* reset fifo to purge old data */ + mpu3050_fifo_reset(inf); + } + } + } + mutex_unlock(&inf->mutex); + if (err < 0) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", __func__, fsr, err); + return err; + } + + return count; +} + +static ssize_t mpu3050_gyro_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char enable; + unsigned char fifo_enable; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &enable); + if (err) + return -EINVAL; + + if (enable > 7) + return -EINVAL; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, enable); + if (enable != inf->chip_config.gyro_enable) { + if (enable) + fifo_enable = inf->chip_config.gyro_fifo_enable; else - st->mpu_slave->suspend(st); + fifo_enable = 0; + err = mpu3050_gyro_enable(inf, enable, fifo_enable); + } + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", + __func__, enable, err); + return err; } + return count; } +static ssize_t mpu3050_gyro_fifo_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char fifo_enable; + unsigned char enable; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &fifo_enable); + if (err) + return -EINVAL; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, fifo_enable); + enable = inf->chip_config.gyro_enable; + if (fifo_enable) { + fifo_enable = 1; + if (!enable) + enable = 7; + } + if (fifo_enable != inf->chip_config.gyro_fifo_enable) + err = mpu3050_gyro_enable(inf, enable, fifo_enable); + mutex_unlock(&inf->mutex); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", + __func__, fifo_enable, err); + return err; + } + + return count; +} + +static ssize_t mpu3050_gyro_max_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char fsr; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &fsr); + if (err) + return -EINVAL; + + if (fsr > 3) + return -EINVAL; + + mutex_lock(&inf->mutex); + dev_dbg(&inf->i2c->dev, "%s: %x\n", __func__, fsr); + if (fsr != inf->chip_config.gyro_fsr) { + inf->chip_config.gyro_fsr = fsr; + if (inf->chip_config.gyro_enable) { + fsr = (fsr << 3) | inf->chip_config.lpf; + err = inv_i2c_single_write(inf, inf->reg->lpf, fsr); + mpu3050_fifo_reset(inf); + } + } + mutex_unlock(&inf->mutex); + if (err < 0) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d\n", __func__, fsr, err); + return err; + } + + return count; +} + +static ssize_t mpu3050_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct inv_gyro_state_s *inf; + unsigned char enable; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &enable); + if (err) + return -EINVAL; + + if (enable) + enable = 1; + inf->chip_config.enable = enable; + return count; +} + +static DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, + nvi_gyro_enable_show, mpu3050_gyro_enable_store); static DEVICE_ATTR(gyro_fifo_enable, S_IRUGO | S_IWUSR, - inv_gyro_fifo_enable_show, - inv_gyro_fifo_enable_store); -static DEVICE_ATTR(gyro_enable, S_IRUGO | S_IWUSR, inv_gyro_enable_show, - inv_gyro_enable_store); -static DEVICE_ATTR(raw_accl, S_IRUGO, inv_raw_accl_mpu3050_show, NULL); + nvi_gyro_fifo_enable_show, mpu3050_gyro_fifo_enable_store); +static DEVICE_ATTR(gyro_max_range, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_gyro_max_range_show, mpu3050_gyro_max_range_store); +static DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, + nvi_accl_enable_show, mpu3050_accl_enable_store); static DEVICE_ATTR(accl_fifo_enable, S_IRUGO | S_IWUSR, - inv_accl_fifo_enable_show, - inv_accl_fifo_mpu3050_enable_store); -static DEVICE_ATTR(accl_enable, S_IRUGO | S_IWUSR, inv_accl_enable_show, - inv_accl_mpu3050_enable_store); -static DEVICE_ATTR(accl_matrix, S_IRUGO, inv_accl_matrix_show, NULL); -static DEVICE_ATTR(accl_fs, S_IRUGO | S_IWUSR, inv_accl_fs_show, - inv_accl_fs_store); + nvi_accl_fifo_enable_show, mpu3050_accl_fifo_enable_store); +static DEVICE_ATTR(accl_delay, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_accl_delay_show, mpu3050_accl_delay_store); +static DEVICE_ATTR(accl_max_range, S_IRUGO | S_IWUSR, + nvi_accl_max_range_show, mpu3050_accl_max_range_store); +static DEVICE_ATTR(accl_orientation, S_IRUGO, + inv_accl_matrix_show, NULL); +static DEVICE_ATTR(raw_accl, S_IRUGO, + mpu3050_raw_accl_show, NULL); +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWOTH, + nvi_enable_show, mpu3050_enable_store); static struct device_attribute *inv_mpu3050_attributes[] = { - &dev_attr_gyro_fifo_enable, &dev_attr_gyro_enable, - &dev_attr_accl_fifo_enable, + &dev_attr_gyro_fifo_enable, + &dev_attr_gyro_max_range, &dev_attr_accl_enable, + &dev_attr_accl_fifo_enable, + &dev_attr_accl_delay, + &dev_attr_accl_max_range, + &dev_attr_accl_orientation, &dev_attr_raw_accl, - &dev_attr_accl_matrix, - &dev_attr_accl_fs, + &dev_attr_enable, NULL }; @@ -219,8 +639,8 @@ int inv_mpu3050_remove_sysfs(struct inv_gyro_state_s *st) return 0; } -static void inv_report_data_3050(struct inv_gyro_state_s *st, s64 t, - int counter, unsigned char *data) +static void mpu3050_report(struct inv_gyro_state_s *st, s64 t, + int counter, unsigned char *data) { short x = 0, y = 0, z = 0; int ind; @@ -278,6 +698,7 @@ static void inv_report_data_3050(struct inv_gyro_state_s *st, s64 t, input_sync(st->idev); } } + /** * inv_read_fifo() - Transfer data from FIFO to ring buffer. */ @@ -296,15 +717,19 @@ irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id) st = (struct inv_gyro_state_s *)dev_id; reg = st->reg; - if (st->chip_config.is_asleep) - goto end_session; - if (!(st->chip_config.enable)) - goto end_session; if (!(st->chip_config.accl_fifo_enable | st->chip_config.gyro_fifo_enable | - st->chip_config.dmp_on | - st->chip_config.compass_enable)) + st->chip_config.dmp_on)) goto end_session; + + result = inv_i2c_read(st, reg->temperature, 2, data); + if (!result) { + mutex_lock(&st->mutex_temp); + st->temp_val = (data[0] << 8) | data[1]; + st->temp_ts = timestamp; + mutex_unlock(&st->mutex_temp); + } + if (st->chip_config.dmp_on) bytes_per_datum = BYTES_FOR_DMP; else @@ -353,7 +778,7 @@ irqreturn_t inv_read_fifo_mpu3050(int irq, void *dev_id) ×tamp, sizeof(timestamp), &copied); if (result) goto flush_fifo; - inv_report_data_3050(st, timestamp, st->fifo_counter, data); + mpu3050_report(st, timestamp, st->fifo_counter, data); fifo_count -= byte_read; if (st->fifo_counter == 0) { st->fifo_counter = 1; @@ -364,8 +789,7 @@ end_session: return IRQ_HANDLED; flush_fifo: /* Flush HW and SW FIFOs. */ - inv_reset_fifo(st); - inv_clear_kfifo(st); + mpu3050_fifo_reset(st); return IRQ_HANDLED; } void inv_setup_reg_mpu3050(struct inv_reg_map_s *reg) @@ -399,9 +823,6 @@ int inv_init_config_mpu3050(struct inv_gyro_state_s *st) int result; unsigned char data; - if (st->chip_config.is_asleep) - return -EPERM; - /*reading AUX VDDIO register */ result = inv_i2c_read(st, REG_3050_AUX_VDDIO, 1, &data); if (result) @@ -421,7 +842,7 @@ int inv_init_config_mpu3050(struct inv_gyro_state_s *st) | INV_FILTER_42HZ); if (result) return result; - st->chip_config.fsr = INV_FSR_2000DPS; + st->chip_config.gyro_fsr = INV_FSR_2000DPS; st->chip_config.lpf = INV_FILTER_42HZ; result = inv_i2c_single_write(st, reg->sample_rate_div, 19); if (result) @@ -430,9 +851,6 @@ int inv_init_config_mpu3050(struct inv_gyro_state_s *st) st->irq_dur_us = 20*1000; st->chip_config.enable = 0; st->chip_config.dmp_on = 0; - st->compass_divider = 0; - st->compass_counter = 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; @@ -460,142 +878,72 @@ int inv_init_config_mpu3050(struct inv_gyro_state_s *st) } return 0; } -/** - * set_power_mpu3050() - set power of mpu3050. - * @st: Device driver instance. - * @power_on: on/off - */ -int set_power_mpu3050(struct inv_gyro_state_s *st, - unsigned char power_on) + +int set_3050_bypass(struct inv_gyro_state_s *st, int enable) { struct inv_reg_map_s *reg; - unsigned char data, p; int result; + unsigned char b; + reg = st->reg; - if (power_on) { - data = 0; + result = inv_i2c_read(st, reg->user_ctrl, 1, &b); + if (result) + return result; + if (((b & BIT_3050_AUX_IF_EN) == 0) && enable) + return 0; + if ((b & BIT_3050_AUX_IF_EN) && (enable == 0)) + return 0; + b &= ~BIT_3050_AUX_IF_EN; + if (!enable) { + b |= BIT_3050_AUX_IF_EN; + result = inv_i2c_single_write(st, reg->user_ctrl, b); + return result; } else { - if (st->mpu_slave) { - result = st->mpu_slave->suspend(st); - if (result) - return result; - } - - data = BIT_SLEEP; - } - if (st->chip_config.gyro_enable) { - p = (BITS_3050_POWER1 | INV_CLK_PLL); - result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); - if (result) - return result; - - p = (BITS_3050_POWER2 | INV_CLK_PLL); - result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); + /* Coming out of I2C is tricky due to several erratta. Do not + * modify this algorithm + */ + /* + * 1) wait for the right time and send the command to change + * the aux i2c slave address to an invalid address that will + * get nack'ed + * + * 0x00 is broadcast. 0x7F is unlikely to be used by any aux. + */ + result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, + 0x7F); if (result) return result; - - p = INV_CLK_PLL; - result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data | p); + /* + * 2) wait enough time for a nack to occur, then go into + * bypass mode: + */ + mdelay(2); + result = inv_i2c_single_write(st, reg->user_ctrl, b); if (result) return result; - - st->chip_config.clk_src = INV_CLK_PLL; - } else { - data |= (BITS_3050_GYRO_STANDBY | INV_CLK_INTERNAL); - result = inv_i2c_single_write(st, reg->pwr_mgmt_1, data); + /* + * 3) wait for up to one MPU cycle then restore the slave + * address + */ + mdelay(20); + result = inv_i2c_single_write(st, REG_3050_SLAVE_REG, + st->plat_data.secondary_read_reg); if (result) return result; - st->chip_config.clk_src = INV_CLK_INTERNAL; - } - if (power_on) { - mdelay(POWER_UP_TIME); - if (st->mpu_slave) { - result = st->mpu_slave->resume(st); - if (result) - return result; - } - st->chip_config.is_asleep = 0; - } else - st->chip_config.is_asleep = 1; - return 0; -} -/** - * reset_fifo_mpu3050() - Reset FIFO related registers - * @st: Device driver instance. - */ -int reset_fifo_mpu3050(struct inv_gyro_state_s *st) -{ - struct inv_reg_map_s *reg; - int result; - unsigned char val, user_ctrl; - reg = st->reg; - /* disable interrupt */ - result = inv_i2c_single_write(st, reg->int_enable, 0); - if (result) - return result; - /* disable the sensor output to FIFO */ - result = inv_i2c_single_write(st, reg->fifo_en, 0); - if (result) - goto reset_fifo_fail; - result = inv_i2c_read(st, reg->user_ctrl, 1, &user_ctrl); - if (result) - goto reset_fifo_fail; - /* disable fifo reading */ - user_ctrl &= ~BIT_FIFO_EN; - st->fifo_counter = 0; - /* reset fifo */ - val = (BIT_3050_FIFO_RST | user_ctrl); - result = inv_i2c_single_write(st, reg->user_ctrl, val); - if (result) - goto reset_fifo_fail; - mdelay(POWER_UP_TIME); - st->last_isr_time = get_time_ns(); - if (st->chip_config.dmp_on) { - /* enable interrupt when DMP is done */ - result = inv_i2c_single_write(st, reg->int_enable, - BIT_DMP_INT_EN); + result = inv_i2c_single_write(st, REG_3050_SLAVE_ADDR, + st->plat_data.secondary_i2c_addr); if (result) return result; result = inv_i2c_single_write(st, reg->user_ctrl, - BIT_FIFO_EN|user_ctrl); - if (result) - return result; - } else { - /* enable interrupt */ - if (st->chip_config.accl_fifo_enable || - st->chip_config.gyro_fifo_enable){ - result = inv_i2c_single_write(st, reg->int_enable, - BIT_DATA_RDY_EN); - if (result) - return result; - } - /* enable FIFO reading and I2C master interface*/ - result = inv_i2c_single_write(st, reg->user_ctrl, - BIT_FIFO_EN | user_ctrl); - if (result) - return result; - /* enable sensor output to FIFO and FIFO footer*/ - val = 1; - if (st->chip_config.accl_fifo_enable) - val |= BITS_3050_ACCL_OUT; - if (st->chip_config.gyro_fifo_enable) - val |= BITS_GYRO_OUT; - result = inv_i2c_single_write(st, reg->fifo_en, val); + (b | BIT_3050_AUX_IF_RST)); if (result) return result; - } + mdelay(2); + } return 0; -reset_fifo_fail: - if (st->chip_config.dmp_on) - val = BIT_DMP_INT_EN; - else - val = BIT_DATA_RDY_EN; - inv_i2c_single_write(st, reg->int_enable, val); - pr_err("%s failed\n", __func__); - return result; } /** * @} diff --git a/drivers/input/misc/pressure/Kconfig b/drivers/input/misc/pressure/Kconfig new file mode 100644 index 000000000000..adacb4ab7868 --- /dev/null +++ b/drivers/input/misc/pressure/Kconfig @@ -0,0 +1,12 @@ +# +# Kconfig for stand-alone pressure input device drivers +# + +config INV_BMP180 + tristate "Bosch BMP180 pressure input device driver" + depends on I2C && SYSFS && INPUT && INPUT_EVDEV + default n + help + This driver supports the Bosch BMP180 pressure sensor. + This driver can be built as a module. The module will be called + inv-bmp180. diff --git a/drivers/input/misc/pressure/Makefile b/drivers/input/misc/pressure/Makefile new file mode 100644 index 000000000000..6831383c3c92 --- /dev/null +++ b/drivers/input/misc/pressure/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for stand-alone pressure input device drivers +# + +obj-$(CONFIG_INV_BMP180) += inv-bmp180.o +inv-bmp180-objs += bmp180.o + diff --git a/drivers/input/misc/pressure/bmp180.c b/drivers/input/misc/pressure/bmp180.c new file mode 100644 index 000000000000..25306d157698 --- /dev/null +++ b/drivers/input/misc/pressure/bmp180.c @@ -0,0 +1,1080 @@ +/* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/regulator/consumer.h> +#include <linux/mpu.h> + + +#define BMP180_NAME "bmp180" +#define BMP180_I2C_ADDR (0x77) +#define BMP180_HW_DELAY_MS (10) +#define BMP180_POLL_DELAY_MS_DFLT (200) +#define BMP180_MPU_RETRY_COUNT (20) +#define BMP180_MPU_RETRY_DELAY_MS (20) +#define BMP180_ERR_CNT_MAX (20) +/* sampling delays */ +#define BMP180_DELAY_ULP (5) +#define BMP180_DELAY_ST (8) +#define BMP180_DELAY_HIGH_RES (14) +#define BMP180_DELAY_UHIGH_RES (26) +/* input poll values*/ +#define BMP180_INPUT_RESOLUTION (1) +#define BMP180_INPUT_DIVISOR (100) +#define BMP180_INPUT_DELAY_MS_MIN (BMP180_DELAY_UHIGH_RES) +#define BMP180_INPUT_POWER_UA (12) +#define BMP180_PRESSURE_MIN (30000) +#define BMP180_PRESSURE_MAX (110000) +#define BMP180_PRESSURE_FUZZ (5) +#define BMP180_PRESSURE_FLAT (5) +/* BMP180 registers */ +#define BMP180_REG_ID (0xD0) +#define BMP180_REG_ID_VAL (0x55) +#define BMP180_REG_RESET (0xE0) +#define BMP180_REG_RESET_VAL (0xB6) +#define BMP180_REG_CTRL (0xF4) +#define BMP180_REG_CTRL_MODE_MASK (0x1F) +#define BMP180_REG_CTRL_MODE_PRES (0x34) +#define BMP180_REG_CTRL_MODE_TEMP (0x2E) +#define BMP180_REG_CTRL_OSS (6) +#define BMP180_REG_CTRL_SCO (5) +#define BMP180_REG_OUT_MSB (0xF6) +#define BMP180_REG_OUT_LSB (0xF7) +#define BMP180_REG_OUT_XLSB (0xF8) +/* ROM registers */ +#define BMP180_REG_AC1 (0xAA) +#define BMP180_REG_AC2 (0xAC) +#define BMP180_REG_AC3 (0xAE) +#define BMP180_REG_AC4 (0xB0) +#define BMP180_REG_AC5 (0xB2) +#define BMP180_REG_AC6 (0xB4) +#define BMP180_REG_B1 (0xB6) +#define BMP180_REG_B2 (0xB8) +#define BMP180_REG_MB (0xBA) +#define BMP180_REG_MC (0xBC) +#define BMP180_REG_MD (0xBE) + +#define WR (0) +#define RD (1) + + +/* regulator names in order of powering on */ +static char *bmp180_vregs[] = { + "vdd", + "vddio", +}; + +static unsigned long bmp180_delay_ms_tbl[] = { + BMP180_DELAY_ULP, + BMP180_DELAY_ST, + BMP180_DELAY_HIGH_RES, + BMP180_DELAY_UHIGH_RES, +}; + + +struct bmp180_rom { + s16 ac1; + s16 ac2; + s16 ac3; + u16 ac4; + u16 ac5; + u16 ac6; + s16 b1; + s16 b2; + s16 mb; + s16 mc; + s16 md; +}; + +struct bmp180_inf { + struct i2c_client *i2c; + struct input_dev *idev; + struct workqueue_struct *wq; + struct delayed_work dw; + struct regulator_bulk_data vreg[ARRAY_SIZE(bmp180_vregs)]; + struct mpu_platform_data pdata; + struct bmp180_rom rom; /* data for calibration */ + unsigned long poll_delay_us; /* requested sampling delay (us) */ + unsigned int resolution; /* report when new data outside this */ + bool use_mpu; /* if device behind MPU */ + bool initd; /* set if initialized */ + bool enable; /* requested enable value */ + bool report; /* used to report first valid sample */ + bool port_en[2]; /* enable status of MPU write port */ + int port_id[2]; /* MPU port ID */ + u8 data_out; /* write value to trigger a sample */ + u8 range_index; /* oversampling value */ + long UT; /* uncompensated temperature */ + long UP; /* uncompensated pressure */ + long temperature; /* true temperature */ + int pressure; /* true pressure hPa/100 Pa/1 mBar */ +}; + + +static int bmp180_i2c_rd(struct bmp180_inf *inf, u8 reg, u16 len, u8 *val) +{ + struct i2c_msg msg[2]; + + msg[0].addr = BMP180_I2C_ADDR; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = ® + msg[1].addr = BMP180_I2C_ADDR; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = val; + if (i2c_transfer(inf->i2c->adapter, msg, 2) != 2) + return -EIO; + + return 0; +} + +static int bmp180_i2c_wr(struct bmp180_inf *inf, u8 reg, u8 val) +{ + struct i2c_msg msg; + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + msg.addr = BMP180_I2C_ADDR; + msg.flags = 0; + msg.len = 2; + msg.buf = buf; + if (i2c_transfer(inf->i2c->adapter, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static int bmp180_vreg_dis(struct bmp180_inf *inf, int i) +{ + int err = 0; + + if (inf->vreg[i].ret && (inf->vreg[i].consumer != NULL)) { + err = regulator_disable(inf->vreg[i].consumer); + if (err) + dev_err(&inf->i2c->dev, "%s %s ERR\n", + __func__, inf->vreg[i].supply); + else + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } + inf->vreg[i].ret = 0; + return err; +} + +static int bmp180_vreg_dis_all(struct bmp180_inf *inf) +{ + unsigned int i; + int err = 0; + + for (i = ARRAY_SIZE(bmp180_vregs); i > 0; i--) + err |= bmp180_vreg_dis(inf, (i - 1)); + return err; +} + +static int bmp180_vreg_en(struct bmp180_inf *inf, int i) +{ + int err = 0; + + if ((!inf->vreg[i].ret) && (inf->vreg[i].consumer != NULL)) { + err = regulator_enable(inf->vreg[i].consumer); + if (!err) { + inf->vreg[i].ret = 1; + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + err = 1; /* flag regulator state change */ + } else { + dev_err(&inf->i2c->dev, "%s %s ERR\n", + __func__, inf->vreg[i].supply); + } + } + return err; +} + +static int bmp180_vreg_en_all(struct bmp180_inf *inf) +{ + int i; + int err = 0; + + for (i = 0; i < ARRAY_SIZE(bmp180_vregs); i++) + err |= bmp180_vreg_en(inf, i); + return err; +} + +static void bmp180_vreg_exit(struct bmp180_inf *inf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(bmp180_vregs); i++) { + if (inf->vreg[i].consumer != NULL) { + devm_regulator_put(inf->vreg[i].consumer); + inf->vreg[i].consumer = NULL; + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } + } +} + +static int bmp180_vreg_init(struct bmp180_inf *inf) +{ + unsigned int i; + int err = 0; + + for (i = 0; i < ARRAY_SIZE(bmp180_vregs); i++) { + inf->vreg[i].supply = bmp180_vregs[i]; + inf->vreg[i].ret = 0; + inf->vreg[i].consumer = devm_regulator_get(&inf->i2c->dev, + inf->vreg[i].supply); + if (IS_ERR(inf->vreg[i].consumer)) { + err |= PTR_ERR(inf->vreg[i].consumer); + dev_err(&inf->i2c->dev, "%s err %d for %s\n", + __func__, err, inf->vreg[i].supply); + inf->vreg[i].consumer = NULL; + } else { + dev_dbg(&inf->i2c->dev, "%s %s\n", + __func__, inf->vreg[i].supply); + } + } + return err; +} + +static int bmp180_pm(struct bmp180_inf *inf, bool enable) +{ + int err; + + if (enable) { + err = bmp180_vreg_en_all(inf); + if (err) + mdelay(BMP180_HW_DELAY_MS); + } else { + err = bmp180_vreg_dis_all(inf); + } + if (err > 0) + err = 0; + if (err) + dev_err(&inf->i2c->dev, "%s enable=%x ERR=%d\n", + __func__, enable, err); + else + dev_dbg(&inf->i2c->dev, "%s enable=%x\n", + __func__, enable); + return err; +} + +static int bmp180_port_free(struct bmp180_inf *inf, int port) +{ + int err = 0; + + if ((inf->use_mpu) && (inf->port_id[port] >= 0)) { + err = nvi_mpu_port_free(inf->port_id[port]); + if (!err) + inf->port_id[port] = -1; + } + return err; +} + +static int bmp180_ports_free(struct bmp180_inf *inf) +{ + int err; + + err = bmp180_port_free(inf, WR); + err |= bmp180_port_free(inf, RD); + return err; +} + +static void bmp180_pm_exit(struct bmp180_inf *inf) +{ + bmp180_ports_free(inf); + bmp180_pm(inf, false); + bmp180_vreg_exit(inf); +} + +static int bmp180_pm_init(struct bmp180_inf *inf) +{ + int err; + + inf->initd = false; + inf->enable = false; + inf->port_en[WR] = false; + inf->port_en[RD] = false; + inf->port_id[WR] = -1; + inf->port_id[RD] = -1; + inf->resolution = 0; + inf->range_index = 0; + inf->poll_delay_us = (BMP180_POLL_DELAY_MS_DFLT * 1000); + bmp180_vreg_init(inf); + err = bmp180_pm(inf, true); + return err; +} + +static int bmp180_nvi_mpu_bypass_request(struct bmp180_inf *inf) +{ + int i; + int err = 0; + + if (inf->use_mpu) { + for (i = 0; i < BMP180_MPU_RETRY_COUNT; i++) { + err = nvi_mpu_bypass_request(true); + if ((!err) || (err == -EPERM)) + break; + + mdelay(BMP180_MPU_RETRY_DELAY_MS); + } + if (err == -EPERM) + err = 0; + } + return err; +} + +static int bmp180_nvi_mpu_bypass_release(struct bmp180_inf *inf) +{ + int err = 0; + + if (inf->use_mpu) + err = nvi_mpu_bypass_release(); + return err; +} + +static int bmp180_wr(struct bmp180_inf *inf, u8 reg, u8 val) +{ + int err = 0; + + err = bmp180_nvi_mpu_bypass_request(inf); + if (!err) { + err = bmp180_i2c_wr(inf, reg, val); + bmp180_nvi_mpu_bypass_release(inf); + } + return err; +} + +static int bmp180_port_enable(struct bmp180_inf *inf, int port, bool enable) +{ + int err = 0; + + if (enable != inf->port_en[port]) { + err = nvi_mpu_enable(inf->port_id[port], enable, false); + if (!err) + inf->port_en[port] = enable; + } + return err; +} + +static int bmp180_ports_enable(struct bmp180_inf *inf, bool enable) +{ + int err; + + err = bmp180_port_enable(inf, WR, enable); + err |= bmp180_port_enable(inf, RD, enable); + return err; +} + +static int bmp180_reset(struct bmp180_inf *inf) +{ + int err = 0; + + if (inf->use_mpu) + err = bmp180_ports_enable(inf, false); + else + cancel_delayed_work_sync(&inf->dw); + if (err) + return err; + + err = bmp180_wr(inf, BMP180_REG_RESET, BMP180_REG_RESET_VAL); + if (!err) + mdelay(BMP180_HW_DELAY_MS); + if (inf->use_mpu) { + err |= nvi_mpu_data_out(inf->port_id[WR], + BMP180_REG_CTRL_MODE_TEMP); + err |= bmp180_ports_enable(inf, true); + } else { + err = bmp180_wr(inf, BMP180_REG_CTRL, + BMP180_REG_CTRL_MODE_TEMP); + queue_delayed_work(inf->wq, &inf->dw, + usecs_to_jiffies(inf->poll_delay_us)); + } + return err; +} + +static int bmp180_delay(struct bmp180_inf *inf, unsigned long delay_us) +{ + int err = 0; + + if (inf->use_mpu) + err = nvi_mpu_delay_us(inf->port_id[RD], delay_us); + return err; +} + +static int bmp180_init_hw(struct bmp180_inf *inf) +{ + u8 *p_rom1; + u8 *p_rom2; + u8 tmp; + int i; + int err = 0; + + inf->UT = 0; + inf->UP = 0; + inf->temperature = 0; + inf->pressure = 0; + p_rom1 = (u8 *)&inf->rom.ac1; + err = bmp180_nvi_mpu_bypass_request(inf); + if (!err) { + err = bmp180_i2c_rd(inf, BMP180_REG_AC1, 22, p_rom1); + bmp180_nvi_mpu_bypass_release(inf); + } + if (err) + return err; + + for (i = 0; i < 11; i++) { + p_rom2 = p_rom1; + tmp = *p_rom1; + *p_rom1++ = *++p_rom2; + *p_rom2 = tmp; + p_rom1++; + } + inf->initd = true; + return err; +} + +static void bmp180_calc(struct bmp180_inf *inf) +{ + long X1, X2, X3, B3, B5, B6, p; + unsigned long B4, B7; + long pressure; + + X1 = ((inf->UT - inf->rom.ac6) * inf->rom.ac5) >> 15; + X2 = inf->rom.mc * (1 << 11) / (X1 + inf->rom.md); + B5 = X1 + X2; + inf->temperature = (B5 + 8) >> 4; + B6 = B5 - 4000; + X1 = (inf->rom.b2 * ((B6 * B6) >> 12)) >> 11; + X2 = (inf->rom.ac2 * B6) >> 11; + X3 = X1 + X2; + B3 = ((((inf->rom.ac1 << 2) + X3) << inf->range_index) + 2) >> 2; + X1 = (inf->rom.ac3 * B6) >> 13; + X2 = (inf->rom.b1 * ((B6 * B6) >> 12)) >> 16; + X3 = ((X1 + X2) + 2) >> 2; + B4 = (inf->rom.ac4 * (unsigned long)(X3 + 32768)) >> 15; + B7 = ((unsigned long)inf->UP - B3) * (50000 >> inf->range_index); + if (B7 < 0x80000000) + p = (B7 << 1) / B4; + else + p = (B7 / B4) << 1; + X1 = (p >> 8) * (p >> 8); + X1 = (X1 * 3038) >> 16; + X2 = (-7357 * p) >> 16; + pressure = p + ((X1 + X2 + 3791) >> 4); + inf->pressure = (int)pressure; +} + +static void bmp180_report(struct bmp180_inf *inf, u8 *data, s64 ts) +{ + input_report_abs(inf->idev, ABS_PRESSURE, inf->pressure); + input_sync(inf->idev); +} + +static int bmp180_read_sts(struct bmp180_inf *inf, u8 *data) +{ + long val; + int limit_lo; + int limit_hi; + int pres; + bool report; + int err = 0; + + /* BMP180_REG_CTRL_SCO is 0 when data is ready */ + val = data[0] & (1 << BMP180_REG_CTRL_SCO); + if (!val) { + err = -1; + if (data[0] == 0x0A) { /* temperature */ + inf->UT = ((data[2] << 8) + data[3]); + inf->data_out = BMP180_REG_CTRL_MODE_PRES | + (inf->range_index << BMP180_REG_CTRL_OSS); + } else { /* pressure */ + val = ((data[2] << 16) + (data[3] << 8) + data[4]) >> + (8 - inf->range_index); + inf->data_out = BMP180_REG_CTRL_MODE_TEMP; + if (inf->resolution && (!inf->report)) { + if (inf->UP == val) + return err; + } + + inf->UP = val; + bmp180_calc(inf); + pres = inf->pressure; + if (inf->resolution) { + limit_lo = pres; + limit_hi = pres; + limit_lo -= (inf->resolution / 2); + limit_hi += (inf->resolution / 2); + if (limit_lo < 0) + limit_lo = 0; + if ((pres < limit_lo) || (pres > limit_hi)) + report = true; + else + report = false; + } else { + report = true; + } + if (report || inf->report) { + inf->report = false; + err = 1; + } + } + } + return err; +} + +static s64 bmp180_timestamp_ns(void) +{ + struct timespec ts; + s64 ns; + + ktime_get_ts(&ts); + ns = timespec_to_ns(&ts); + return ns; +} + +static int bmp180_read(struct bmp180_inf *inf) +{ + long long timestamp1; + long long timestamp2; + u8 data[5]; + int err; + + timestamp1 = bmp180_timestamp_ns(); + err = bmp180_i2c_rd(inf, BMP180_REG_CTRL, 5, data); + timestamp2 = bmp180_timestamp_ns(); + if (err) + return err; + + err = bmp180_read_sts(inf, data); + if (err > 0) { + timestamp2 = (timestamp2 - timestamp1) / 2; + timestamp1 += timestamp2; + bmp180_report(inf, data, timestamp1); + bmp180_i2c_wr(inf, BMP180_REG_CTRL, inf->data_out); + } else if (err < 0) { + bmp180_i2c_wr(inf, BMP180_REG_CTRL, inf->data_out); + } + return err; +} + +static void bmp180_mpu_handler(u8 *data, unsigned int len, s64 ts, void *p_val) +{ + struct bmp180_inf *inf; + int err; + + inf = (struct bmp180_inf *)p_val; + if (inf->enable) { + err = bmp180_read_sts(inf, data); + if (err > 0) { + bmp180_report(inf, data, ts); + nvi_mpu_data_out(inf->port_id[WR], inf->data_out); + } else if (err < 0) { + nvi_mpu_data_out(inf->port_id[WR], inf->data_out); + } + } +} + +static int bmp180_id(struct bmp180_inf *inf) +{ + struct nvi_mpu_port nmp; + u8 config_boot; + u8 val = 0; + int err; + + config_boot = inf->pdata.config & NVI_CONFIG_BOOT_MASK; + if (config_boot == NVI_CONFIG_BOOT_AUTO) { + nmp.addr = BMP180_I2C_ADDR | 0x80; + nmp.reg = BMP180_REG_ID; + nmp.ctrl = 1; + err = nvi_mpu_dev_valid(&nmp, &val); + /* see mpu.h for possible return values */ + if ((err == -EAGAIN) || (err == -EBUSY)) + return -EAGAIN; + + if (((!err) && (val == BMP180_REG_ID_VAL)) || (err == -EIO)) + config_boot = NVI_CONFIG_BOOT_MPU; + } + if (config_boot == NVI_CONFIG_BOOT_MPU) { + inf->use_mpu = true; + nmp.addr = BMP180_I2C_ADDR | 0x80; + nmp.reg = BMP180_REG_CTRL; + nmp.ctrl = 5; + nmp.data_out = 0; + nmp.delay_ms = 0; + nmp.delay_us = inf->poll_delay_us; + nmp.shutdown_bypass = false; + nmp.handler = &bmp180_mpu_handler; + nmp.ext_driver = (void *)inf; + err = nvi_mpu_port_alloc(&nmp); + if (err < 0) + return err; + + inf->port_id[RD] = err; + nmp.addr = BMP180_I2C_ADDR; + nmp.reg = BMP180_REG_CTRL; + nmp.ctrl = 1; + nmp.data_out = BMP180_REG_CTRL_MODE_TEMP; + nmp.delay_ms = bmp180_delay_ms_tbl[inf->range_index]; + nmp.delay_us = 0; + nmp.shutdown_bypass = false; + nmp.handler = NULL; + nmp.ext_driver = NULL; + err = nvi_mpu_port_alloc(&nmp); + if (err < 0) { + bmp180_ports_free(inf); + dev_err(&inf->i2c->dev, "%s ERR %d", __func__, err); + } else { + inf->port_id[WR] = err; + err = 0; + } + return err; + } + + /* NVI_CONFIG_BOOT_EXTERNAL */ + inf->use_mpu = false; + err = bmp180_i2c_rd(inf, BMP180_REG_ID, 1, &val); + if ((!err) && (val == BMP180_REG_ID_VAL)) + return 0; + + return -ENODEV; +} + +static void bmp180_work(struct work_struct *ws) +{ + struct bmp180_inf *inf; + + inf = container_of(ws, struct bmp180_inf, dw.work); + bmp180_read(inf); + queue_delayed_work(inf->wq, &inf->dw, + usecs_to_jiffies(inf->poll_delay_us)); +} + +static int bmp180_enable(struct bmp180_inf *inf, bool enable) +{ + int err = 0; + + bmp180_pm(inf, true); + if (!inf->initd) + err = bmp180_init_hw(inf); + if (enable) { + inf->report = true; + err |= bmp180_delay(inf, inf->poll_delay_us); + err |= bmp180_reset(inf); + if (!err) + inf->enable = true; + } else { + inf->enable = false; + if (inf->use_mpu) + err = bmp180_ports_enable(inf, false); + else + cancel_delayed_work_sync(&inf->dw); + if (!err) + bmp180_pm(inf, false); + } + return err; +} + +static ssize_t bmp180_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bmp180_inf *inf; + unsigned int enable; + bool en; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtouint(buf, 10, &enable); + if (err) + return -EINVAL; + + if (enable) + en = true; + else + en = false; + dev_dbg(&inf->i2c->dev, "%s: %x", __func__, en); + err = bmp180_enable(inf, en); + if (err) { + dev_err(&inf->i2c->dev, "%s: %x ERR=%d", __func__, en, err); + return err; + } + + return count; +} + +static ssize_t bmp180_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp180_inf *inf; + unsigned int enable = 0; + + inf = dev_get_drvdata(dev); + if (inf->enable) + enable = 1; + return sprintf(buf, "%u\n", enable); +} + +static ssize_t bmp180_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bmp180_inf *inf; + unsigned long delay_us; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtoul(buf, 10, &delay_us); + if (err) + return -EINVAL; + + dev_dbg(&inf->i2c->dev, "%s: %lu\n", __func__, delay_us); + /* since we rotate between acquiring data for pressure and temperature + * we need to go twice as fast. + */ + delay_us >>= 1; + if (delay_us < (bmp180_delay_ms_tbl[inf->range_index] * 1000)) + delay_us = (bmp180_delay_ms_tbl[inf->range_index] * 1000); + if (inf->enable && (delay_us != inf->poll_delay_us)) + err = bmp180_delay(inf, delay_us); + if (!err) { + inf->poll_delay_us = delay_us; + } else { + dev_err(&inf->i2c->dev, "%s: %lu ERR=%d\n", + __func__, delay_us, err); + return err; + } + + return count; +} + +static ssize_t bmp180_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp180_inf *inf; + unsigned long delay_us; + + inf = dev_get_drvdata(dev); + if (inf->enable) + delay_us = inf->poll_delay_us; + else + delay_us = bmp180_delay_ms_tbl[inf->range_index] * 1000; + return sprintf(buf, "%lu\n", delay_us); +} + +static ssize_t bmp180_resolution_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bmp180_inf *inf; + unsigned int resolution; + + inf = dev_get_drvdata(dev); + if (kstrtouint(buf, 10, &resolution)) + return -EINVAL; + + inf->resolution = resolution; + dev_dbg(&inf->i2c->dev, "%s %u", __func__, resolution); + return count; +} + +static ssize_t bmp180_resolution_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp180_inf *inf; + unsigned int resolution; + + inf = dev_get_drvdata(dev); + if (inf->enable) + resolution = inf->resolution; + else + resolution = BMP180_INPUT_RESOLUTION; + return sprintf(buf, "%u\n", resolution); +} + +static ssize_t bmp180_max_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bmp180_inf *inf; + u8 range_index; + int err; + + inf = dev_get_drvdata(dev); + err = kstrtou8(buf, 10, &range_index); + if (err) + return -EINVAL; + + if (range_index > 3) + return -EINVAL; + + dev_dbg(&inf->i2c->dev, "%s %u", __func__, range_index); + inf->range_index = range_index; + nvi_mpu_delay_ms(inf->port_id[WR], bmp180_delay_ms_tbl[range_index]); + return count; +} + +static ssize_t bmp180_max_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp180_inf *inf; + unsigned int range; + + inf = dev_get_drvdata(dev); + if (inf->enable) + range = inf->range_index; + else + range = (BMP180_PRESSURE_MAX * BMP180_INPUT_DIVISOR); + return sprintf(buf, "%u\n", range); +} + +static ssize_t bmp180_divisor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp180_inf *inf; + + inf = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", BMP180_INPUT_DIVISOR); +} + +static ssize_t bmp180_microamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp180_inf *inf; + + inf = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", BMP180_INPUT_POWER_UA); +} + +static ssize_t bmp180_pressure_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bmp180_inf *inf; + + inf = dev_get_drvdata(dev); + if (inf->enable) + return sprintf(buf, "%d\n", inf->pressure); + + return -EPERM; +} + +static ssize_t bmp180_temperature_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct bmp180_inf *inf; + + inf = dev_get_drvdata(dev); + if (inf->enable) + return sprintf(buf, "%ld\n", inf->temperature); + + return -EPERM; +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWOTH, + bmp180_enable_show, bmp180_enable_store); +static DEVICE_ATTR(delay, S_IRUGO | S_IWUSR | S_IWOTH, + bmp180_delay_show, bmp180_delay_store); +static DEVICE_ATTR(resolution, S_IRUGO | S_IWUSR | S_IWOTH, + bmp180_resolution_show, bmp180_resolution_store); +static DEVICE_ATTR(max_range, S_IRUGO | S_IWUSR | S_IWOTH, + bmp180_max_range_show, bmp180_max_range_store); +static DEVICE_ATTR(divisor, S_IRUGO, + bmp180_divisor_show, NULL); +static DEVICE_ATTR(microamp, S_IRUGO, + bmp180_microamp_show, NULL); +static DEVICE_ATTR(pressure, S_IRUGO, + bmp180_pressure_show, NULL); +static DEVICE_ATTR(temperature, S_IRUGO, + bmp180_temperature_show, NULL); + +static struct attribute *bmp180_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_delay.attr, + &dev_attr_resolution.attr, + &dev_attr_max_range.attr, + &dev_attr_divisor.attr, + &dev_attr_microamp.attr, + &dev_attr_pressure.attr, + &dev_attr_temperature.attr, + NULL +}; + +static struct attribute_group bmp180_attr_group = { + .name = BMP180_NAME, + .attrs = bmp180_attrs +}; + +static int bmp180_sysfs_create(struct bmp180_inf *inf) +{ + int err; + + err = sysfs_create_group(&inf->idev->dev.kobj, &bmp180_attr_group); + return err; +} + +static void bmp180_input_close(struct input_dev *idev) +{ + struct bmp180_inf *inf; + + inf = input_get_drvdata(idev); + if (inf != NULL) + bmp180_enable(inf, false); +} + +static int bmp180_input_create(struct bmp180_inf *inf) +{ + int err; + + inf->idev = input_allocate_device(); + if (!inf->idev) { + err = -ENOMEM; + dev_err(&inf->i2c->dev, "%s ERR %d\n", __func__, err); + return err; + } + + inf->idev->name = BMP180_NAME; + inf->idev->dev.parent = &inf->i2c->dev; + inf->idev->close = bmp180_input_close; + input_set_drvdata(inf->idev, inf); + input_set_capability(inf->idev, EV_ABS, ABS_PRESSURE); + input_set_abs_params(inf->idev, ABS_PRESSURE, + BMP180_PRESSURE_MIN, BMP180_PRESSURE_MAX, + BMP180_PRESSURE_FUZZ, BMP180_PRESSURE_FLAT); + err = input_register_device(inf->idev); + if (err) { + input_free_device(inf->idev); + inf->idev = NULL; + } + return err; +} + +static int bmp180_remove(struct i2c_client *client) +{ + struct bmp180_inf *inf; + + inf = i2c_get_clientdata(client); + if (inf != NULL) { + if (inf->idev) { + input_unregister_device(inf->idev); + input_free_device(inf->idev); + } + if (inf->wq) + destroy_workqueue(inf->wq); + bmp180_pm_exit(inf); + kfree(inf); + } + dev_info(&client->dev, "%s\n", __func__); + return 0; +} + +static void bmp180_shutdown(struct i2c_client *client) +{ + bmp180_remove(client); +} + +static int bmp180_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bmp180_inf *inf; + struct mpu_platform_data *pd; + int err; + + dev_info(&client->dev, "%s\n", __func__); + inf = kzalloc(sizeof(*inf), GFP_KERNEL); + if (IS_ERR_OR_NULL(inf)) { + dev_err(&client->dev, "%s kzalloc ERR\n", __func__); + return -ENOMEM; + } + + inf->i2c = client; + i2c_set_clientdata(client, inf); + pd = (struct mpu_platform_data *)dev_get_platdata(&client->dev); + if (pd != NULL) + inf->pdata = *pd; + bmp180_pm_init(inf); + err = bmp180_id(inf); + bmp180_pm(inf, false); + if (err == -EAGAIN) + goto bmp180_probe_again; + else if (err) + goto bmp180_probe_err; + + err = bmp180_input_create(inf); + if (err) + goto bmp180_probe_err; + + inf->wq = create_singlethread_workqueue(BMP180_NAME); + if (!inf->wq) { + dev_err(&client->dev, "%s workqueue ERR\n", __func__); + err = -ENOMEM; + goto bmp180_probe_err; + } + + INIT_DELAYED_WORK(&inf->dw, bmp180_work); + err = bmp180_sysfs_create(inf); + if (err) + goto bmp180_probe_err; + + return 0; + +bmp180_probe_err: + dev_err(&client->dev, "%s ERR %d\n", __func__, err); +bmp180_probe_again: + bmp180_remove(client); + return err; +} + +static const struct i2c_device_id bmp180_i2c_device_id[] = { + {BMP180_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, bmp180_i2c_device_id); + +static struct i2c_driver bmp180_driver = { + .class = I2C_CLASS_HWMON, + .probe = bmp180_probe, + .remove = bmp180_remove, + .driver = { + .name = BMP180_NAME, + .owner = THIS_MODULE, + }, + .id_table = bmp180_i2c_device_id, + .shutdown = bmp180_shutdown, +}; + +static int __init bmp180_init(void) +{ + return i2c_add_driver(&bmp180_driver); +} + +static void __exit bmp180_exit(void) +{ + i2c_del_driver(&bmp180_driver); +} + +module_init(bmp180_init); +module_exit(bmp180_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("BMP180 driver"); +MODULE_AUTHOR("NVIDIA Corp"); + diff --git a/include/linux/mpu.h b/include/linux/mpu.h index 1f78833c30f5..3f303da8266a 100644 --- a/include/linux/mpu.h +++ b/include/linux/mpu.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Invensense, Inc. +* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -47,8 +48,19 @@ * data defined by dbg_dat * - A read to dbg_dat initiates an I2C read transaction * to the device register defined by dbg_reg. +* dbg_i2c_addr = I2C device address +* - if set to 0 (default) the MPU I2C address is used. +* - other devices can be accessed by setting this to the +* that device I2C address. When used after enabling +* bypass mode, devices behind the MPU can be accessed. +* aux_dbg = write 1 to spew auxiliary port register dumps after +* after each external driver call. +* Write 0 to disable the spew. +* Writing anything takes a snapshot of the registers. +* Therefore, a write of 0 can take snapshots whenever +* without the external driver call spew. **********************************************************************/ -#define DEBUG_SYSFS_INTERFACE 0 +#define DEBUG_SYSFS_INTERFACE 1 /* Mount maxtices for mount orientation. * MTMAT_XXX_CCW_YYY @@ -251,7 +263,6 @@ enum ext_slave_bus { EXT_SLAVE_BUS_SECONDARY = 1 }; - /** * struct ext_slave_platform_data - Platform data for mpu3050 and mpu6050 * slave devices @@ -358,6 +369,12 @@ struct ext_slave_descr { struct ext_slave_read_trigger *trigger; }; + +#define NVI_CONFIG_BOOT_AUTO (0) /* auto detect connection to MPU */ +#define NVI_CONFIG_BOOT_MPU (1) /* connected to MPU */ +#define NVI_CONFIG_BOOT_EXTERNAL (2) /* connected to host */ +#define NVI_CONFIG_BOOT_MASK (0x03) + /** * struct mpu_platform_data - Platform data for the mpu driver * @int_config: Bits [7:3] of the int config register. @@ -367,6 +384,8 @@ struct ext_slave_descr { * @sec_slave_id: id of the secondary slave device * @secondary_i2c_address: secondary device's i2c address * @secondary_orientation: secondary device's orientation matrix + * @config: the selection determines the device behavior. + * Select from the NVI_CONFIG_BOOT_ defines. * * Contains platform specific information on how to configure the MPU3050 to * work on this platform. The orientation matricies are 3x3 rotation matricies @@ -384,6 +403,281 @@ struct mpu_platform_data { __u8 secondary_read_reg; __s8 secondary_orientation[9]; __u8 key[16]; + __u8 config; +}; + +/** + * struct nvi_mpu_port: Allows an external driver to use the MPU + * auxiliary I2C master by providing the details for the I2C + * polling of a device connected to the MPU. + * - addr: The 6:0 I2C address of the device connected to + * the MPU. + * 7:7 = 0 if the port is to do write transactions. + * = 1 if the port is to do read transactions. + * - reg: The device register the I2C transaction will + * use. + * - ctrl: The number of consecutive registers to read in + * 3:0. If the port is to do write transactions then this + * value must be 1. See MPU documentation for the other + * bits in I2C_SLVx_CTRL that can be applied by this byte. + * - data_out: The data byte written if the port is configured + * to do writes (addr 7:7 = 0). + * - delay_ms: The polling delay time between I2C transactions + * in ms. Note that the MPU HW only supports one delay + * time so the longest delay of all the MPU ports enabled + * is used. + * - delay_us: The delay at which the read data is reported. + * - shutdown_bypass: set if a connection to the host is needed + * when the system is shutdown. The MPU API will be + * disabled as part of its shutdown but it will enable the + * bypass if this is true. + * - *handler: The pointer to the function called when the data + * is available. This can be NULL if the port is + * configured for write transactions. + * The function is called with the following parameters: + * - *data: The pointer to the data to read. + * - length: The number of bytes to be read (same value as + * length above). + * - timestamp: The timestamp of when the data was polled. + * - *pointer: A generic pointer defined next below. Note + * that this can be NULL if this will be a write I2C + * transaction. + * - *ext_driver: A generic pointer that can be used by the + * external driver. Note that this is specifically for the + * external driver and not used by the MPU. + */ +struct nvi_mpu_port { + u8 addr; + u8 reg; + u8 ctrl; + u8 data_out; + unsigned int delay_ms; + unsigned long delay_us; + bool shutdown_bypass; + void (*handler)(u8 *data, unsigned int len, + long long timestamp, void *ext_driver); + void *ext_driver; }; +/** + * Expected use of the nvi_mpu_ routines are as follows: + * - nvi_mpu_dev_valid: Use to validate whether a device is + * connected to the MPU. + * - nvi_mpu_port_alloc: Request a connection to the device. If + * successful, a port number will be returned to identify + * the connection. The port number is then used for all + * further communication with the connection. + * - nvi_mpu_port_free: Use to close the port connection. + * - nvi_mpu_enable: Use to enable/disable a port. + * The enable and FIFO enable is disabled by default so + * this will be required after a port is assigned. + * - nvi_mpu_delay_us: Use to set the sampling rate in + * microseconds. The fastest rate of all the enabled MPU + * devices will be used that does not exceed the + * nvi_mpu_delay_ms setting of an enabled device. + * - nvi_mpu_delay_ms: Use to change the port polling delay at + * runtime. There is only one HW delay so the delay used + * will be the longest delay of all the enabled ports. + * This is separate from the sampling rate + * (nvi_mpu_delay_us). See function notes below. + * - nvi_mpu_data_out: Use to change the data written at runtime + * for ports that are configured as I2C write transactions. + * - nvi_mpu_bypass request/release: Use to connect/disconnect + * the MPU host from the device. When bypass is enabled, + * the connection from the device to the MPU will then be + * connected to the host (that the MPU is connected to). + * This is a global connection switch affecting all ports + * so a mechanism is in place of whether the request is + * honored or not. See the funtion notes for + * nvi_mpu_bypass_request. + */ + +/** + * Use to validate a device connected to the MPU I2C master. + * The function works by doing a single byte read or write to + * the device and detecting a NACK. Typically, the call would + * be set up to read a byte ID of the device. + * @param struct nvi_mpu_port *nmp + * Only the following is needed in nmp: + * - addr + * - reg + * - ctrl + * - data_out if a write transaction + * @param *val: pointer for read data. Can be NULL if write. + * @return int error + * Possible return value or errors are: + * - 0: device is connected to MPU. + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + * - -ENODEV: The device is not connected to the MPU. + * - -EINVAL: Problem with input parameters. + * - -EIO: The device is connected but responded with + * a NACK. + */ +int nvi_mpu_dev_valid(struct nvi_mpu_port *nmp, u8 *data); + +/** + * Request a port. + * @param struct nvi_mpu_port *nmp + * - addr: device I2C address 6:0. + * 7:7 = 0 if the port is to do writes. + * 7:7 = 1 if the port is to do reads. + * - reg: the starting register to write or read. + * - ctrl: number of bytes to read. Use 1 if port + * is configured to do writes. + * - data_out: only valid if port is configured to do + * writes. + * - delay: polling delay + * - handler: function to call when data is read. This + * should be NULL if the port is configured to do + * writes. + * - ext_driver: this pointer is passed in handler for + * use by external driver. This should be NULL + * if the port is configured for writes. + * @return int error/port id + * if return >= 0 then this is the port ID. The ID + * will have a value of 0 to 3 (HW has 4 ports). + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + * - -ENODEV: A port is not available. The only way + * to resolve this error is for a port to be + * freed. + * - -EINVAL: Problem with input parameters. + */ +int nvi_mpu_port_alloc(struct nvi_mpu_port *nmp); + +/** + * Remove a port. + * @param port + * @return int error + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + * - -EINVAL: Problem with input parameters. + */ +int nvi_mpu_port_free(int port); + +/** + * Enable/disable a port. + * @param port + * @param enable + * @return int error + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + * - -EINVAL: Problem with input parameters. + */ +int nvi_mpu_enable(int port, bool enable, bool fifo_enable); + +/** + * Use to change the ports sampling delay in microseconds. The + * hardware only supports one sampling rate so the shortest time + * is used among all enabled ports, accelerometer, and gyro. If + * the requested rate is longer than the actual rate and the + * port is configured for reads, the data will be reported at + * the requested rate skipping the data polled at the faster + * rate. Setting this to zero causes other enabled devices to + * determine the sampling rate. If there are no other enabled + * devices, then the MPU default rate is used. + * @param port + * @param delay_us + * @return int error + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + * - -EINVAL: Problem with input parameters. + */ +int nvi_mpu_delay_us(int port, unsigned long delay_us); + +/** + * Use to change the ports polling delay in milliseconds. + * A delay value of 0 disables the delay for that port. The + * hardware only supports one delay value so the largest request + * of all the enabled ports is used. The polling delay is in + * addition to the sampling delay (nvi_mpu_delay_us). This is + * typically used to guarantee a delay after an I2C write to a + * device to allow the device to process the request and be read + * by another port before another write at the sampling delay. + * + * @param port + * @param delay_ms + * @return int error/delay used or 0 if request is to disable + * and is successful. + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + * - -EINVAL: Problem with input parameters. + */ +int nvi_mpu_delay_ms(int port, u8 delay_ms); + +/** + * Use to change the data written to the sensor. + * @param port + * @param data_out is the new data to be written + * @return int error + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + * - -EINVAL: Problem with input parameters. + */ +int nvi_mpu_data_out(int port, u8 data_out); + +/** + * Enable/disable the MPU bypass mode. When enabled, the MPU + * will connect its auxiliary I2C ports to the host. This is + * typically used to initialize a device that requires more I2C + * transactions than the automated port polling can offer. + * EVERY nvi_mpu_bypass_request call must be balanced with a + * nvi_mpu_bypass_release call! + * A bypass request does not need a following ~enable call. The + * release call will automatically handle the correct bypass + * enable setting. The request locks the bypass setting if + * successful. The release unlocks and restores the setting if + * need be. Although odd, the purpose of the request call with + * the enable cleared to false is to allow an external driver to + * access its device that would normally conflict with a device + * behind the MPU. Note that this call must not be a permanent + * solution, i.e. delayed or no release call. + * When the MPU is in a shutdown state the return error will be + * -EPERM and bypass will be enabled to allow access from the + * host to the devices connected to the MPU for their own + * shutdown needs. + * @param enable + * @return int error: calls that return with an error must not + * be balanced with a release call. + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + */ +int nvi_mpu_bypass_request(bool enable); + +/** + * See the nvi_mpu_bypass_request notes. + * @return int error: calls that return with an error must be + * tried again. + * Possible errors are: + * - -EAGAIN: MPU is not initialized yet. + * - -EPERM: MPU is shutdown. MPU API won't be + * available until a system restart. + * - -EBUSY: MPU is busy with another request. + */ +int nvi_mpu_bypass_release(void); + #endif /* __MPU_H_ */ |