/* Copyright (c) 2014-2018, 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. */ /* NVS = NVidia Sensor framework */ /* See nvs_iio.c and nvs.h for documentation */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_TRACE #include #endif // ENABLE_TRACE #include "nvi.h" #define NVI_DRIVER_VERSION (345) #define NVI_VENDOR "Invensense" #define NVI_NAME "mpu6xxx" #define NVI_NAME_MPU6050 "mpu6050" #define NVI_NAME_MPU6500 "mpu6500" #define NVI_NAME_MPU6515 "mpu6515" #define NVI_NAME_MPU9150 "mpu9150" #define NVI_NAME_MPU9250 "mpu9250" #define NVI_NAME_MPU9350 "mpu9350" #define NVI_NAME_ICM20628 "icm20628" #define NVI_NAME_ICM20630 "icm20630" #define NVI_NAME_ICM20632 "icm20632" #define NVI_HW_ID_AUTO (0xFF) #define NVI_HW_ID_MPU6050 (0x68) #define NVI_HW_ID_MPU6500 (0x70) #define NVI_HW_ID_MPU6515 (0x74) #define NVI_HW_ID_MPU9150 (0x68) #define NVI_HW_ID_MPU9250 (0x71) #define NVI_HW_ID_MPU9350 (0x72) #define NVI_HW_ID_ICM20628 (0xA2) #define NVI_HW_ID_ICM20630 (0xAB) #define NVI_HW_ID_ICM20632 (0xAD) /* NVI_FW_CRC_CHECK used only during development to confirm valid FW */ #define NVI_FW_CRC_CHECK (0) struct nvi_pdata { struct nvi_state st; struct work_struct fw_load_work; const struct i2c_device_id *i2c_dev_id; }; struct nvi_id_hal { u8 hw_id; const char *name; const struct nvi_hal *hal; }; /* ARRAY_SIZE(nvi_id_hals) must match ARRAY_SIZE(nvi_i2c_device_id) - 1 */ enum NVI_NDX { NVI_NDX_AUTO = 0, NVI_NDX_MPU6050, NVI_NDX_MPU6500, NVI_NDX_MPU6515, NVI_NDX_MPU9150, NVI_NDX_MPU9250, NVI_NDX_MPU9350, NVI_NDX_ICM20628, NVI_NDX_ICM20630, NVI_NDX_ICM20632, NVI_NDX_N, }; /* enum NVI_NDX_N must match ARRAY_SIZE(nvi_i2c_device_id) - 1 */ static struct i2c_device_id nvi_i2c_device_id[] = { { NVI_NAME, NVI_NDX_AUTO }, { NVI_NAME_MPU6050, NVI_NDX_MPU6050 }, { NVI_NAME_MPU6500, NVI_NDX_MPU6500 }, { NVI_NAME_MPU6515, NVI_NDX_MPU6515 }, { NVI_NAME_MPU9150, NVI_NDX_MPU9150 }, { NVI_NAME_MPU9250, NVI_NDX_MPU9250 }, { NVI_NAME_MPU9350, NVI_NDX_MPU9350 }, { NVI_NAME_ICM20628, NVI_NDX_ICM20628 }, { NVI_NAME_ICM20630, NVI_NDX_ICM20630 }, { NVI_NAME_ICM20632, NVI_NDX_ICM20632 }, {} }; enum NVI_INFO { NVI_INFO_VER = 0, NVI_INFO_DBG, NVI_INFO_DBG_SPEW, NVI_INFO_AUX_SPEW, NVI_INFO_FIFO_SPEW, NVI_INFO_TS_SPEW, NVI_INFO_SNSR_SPEW, NVI_INFO_REG_WR = 0xC6, /* use 0xD0 on cmd line */ NVI_INFO_MEM_RD, NVI_INFO_MEM_WR, NVI_INFO_DMP_FW, NVI_INFO_DMP_EN_MSK, NVI_INFO_FN_INIT }; /* regulator names in order of powering on */ static char *nvi_vregs[] = { "vdd", "vlogic", }; #ifdef ENABLE_TRACE static int snsr_ids[] = { [DEV_ACC] = SENSOR_TYPE_ACCELEROMETER, [DEV_GYR] = SENSOR_TYPE_GYROSCOPE, [DEV_SM] = SENSOR_TYPE_SIGNIFICANT_MOTION, [DEV_GMR] = SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR, [DEV_GYU] = SENSOR_TYPE_GYROSCOPE_UNCALIBRATED }; #endif // ENABLE_TRACE static struct nvi_state *nvi_state_local; static int nvi_dmp_fw(struct nvi_state *st); static int nvi_aux_bypass_enable(struct nvi_state *st, bool enable); static int nvi_read(struct nvi_state *st, bool flush); static int nvi_nb_vreg(struct nvi_state *st, unsigned long event, unsigned int i) { if (event & REGULATOR_EVENT_POST_ENABLE) st->ts_vreg_en[i] = nvs_timestamp(); else if (event & (REGULATOR_EVENT_DISABLE | REGULATOR_EVENT_FORCE_DISABLE)) st->ts_vreg_en[i] = 0; if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s %s event=0x%x ts=%lld\n", __func__, st->vreg[i].supply, (unsigned int)event, st->ts_vreg_en[i]); return NOTIFY_OK; } static int nvi_nb_vreg_vdd(struct notifier_block *nb, unsigned long event, void *ignored) { struct nvi_state *st = container_of(nb, struct nvi_state, nb_vreg[0]); return nvi_nb_vreg(st, event, 0); } static int nvi_nb_vreg_vlogic(struct notifier_block *nb, unsigned long event, void *ignored) { struct nvi_state *st = container_of(nb, struct nvi_state, nb_vreg[1]); return nvi_nb_vreg(st, event, 1); } static int (* const nvi_nb_vreg_pf[])(struct notifier_block *nb, unsigned long event, void *ignored) = { nvi_nb_vreg_vdd, nvi_nb_vreg_vlogic, }; void nvi_err(struct nvi_state *st) { st->errs++; if (!st->errs) st->errs--; } static void nvi_mutex_lock(struct nvi_state *st) { unsigned int i; if (st->nvs) { for (i = 0; i < DEV_N; i++) st->nvs->nvs_mutex_lock(st->snsr[i].nvs_st); } } static void nvi_mutex_unlock(struct nvi_state *st) { unsigned int i; if (st->nvs) { for (i = 0; i < DEV_N; i++) st->nvs->nvs_mutex_unlock(st->snsr[i].nvs_st); } } static void nvi_disable_irq(struct nvi_state *st) { if (st->i2c->irq && !st->irq_dis) { disable_irq_nosync(st->i2c->irq); st->irq_dis = true; if (st->sts & NVS_STS_SPEW_MSG) dev_info(&st->i2c->dev, "%s IRQ disabled\n", __func__); } } static void nvi_enable_irq(struct nvi_state *st) { if (st->i2c->irq && st->irq_dis) { enable_irq(st->i2c->irq); st->irq_dis = false; if (st->sts & NVS_STS_SPEW_MSG) dev_info(&st->i2c->dev, "%s IRQ enabled\n", __func__); } } static void nvi_rc_clr(struct nvi_state *st, const char *fn) { unsigned int i; for (i = 0; i < ARRAY_SIZE(st->rc_msk); i++) st->rc_msk[i] = 0; if (st->sts & NVI_DBG_SPEW_MSG) dev_info(&st->i2c->dev, "%s-%s\n", __func__, fn); } static int nvi_i2c_w(struct nvi_state *st, u16 len, u8 *buf) { struct i2c_msg msg; msg.addr = st->i2c->addr; msg.flags = 0; msg.len = len; msg.buf = buf; if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) { nvi_err(st); return -EIO; } return 0; } static int nvi_wr_reg_bank_sel(struct nvi_state *st, u8 reg_bank) { u8 buf[2]; bool wr = true; int ret = 0; if (!st->hal->reg->reg_bank.reg) return 0; reg_bank <<= 4; if (st->rc_msk[NVI_RC_BANK_REG_BANK] & NVI_RC_MSK_REG_BANK) { if (reg_bank == st->rc.reg_bank) wr = false; } if (wr) { buf[0] = st->hal->reg->reg_bank.reg; buf[1] = reg_bank; ret = nvi_i2c_w(st, sizeof(buf), buf); if (ret) { dev_err(&st->i2c->dev, "%s 0x%x!->0x%x ERR=%d\n", __func__, st->rc.reg_bank, reg_bank, ret); st->rc_msk[NVI_RC_BANK_REG_BANK] &= ~NVI_RC_MSK_REG_BANK; } else { if (st->sts & NVI_DBG_SPEW_MSG) dev_info(&st->i2c->dev, "%s 0x%x->0x%x\n", __func__, st->rc.reg_bank, reg_bank); st->rc.reg_bank = reg_bank; st->rc_msk[NVI_RC_BANK_REG_BANK] |= NVI_RC_MSK_REG_BANK; } } return ret; } static int nvi_i2c_write(struct nvi_state *st, u8 bank, u16 len, u8 *buf) { int ret; ret = nvi_wr_reg_bank_sel(st, bank); if (!ret) ret = nvi_i2c_w(st, len, buf); return ret; } static int nvi_i2c_write_be(struct nvi_state *st, const struct nvi_br *br, u16 len, u32 val) { u8 buf[5]; unsigned int i; buf[0] = br->reg; for (i = len; i > 0; i--) buf[i] = (u8)(val >> (8 * (len - i))); return nvi_i2c_write(st, br->bank, len + 1, buf); } static int nvi_i2c_write_le(struct nvi_state *st, const struct nvi_br *br, u16 len, u32 val) { u8 buf[5]; unsigned int i; buf[0] = br->reg; for (i = 0; i < len; i++) buf[i + 1] = (u8)(val >> (8 * i)); return nvi_i2c_write(st, br->bank, len + 1, buf); } int nvi_i2c_write_rc(struct nvi_state *st, const struct nvi_br *br, u32 val, const char *fn, u8 *rc, bool be) { u16 len; u64 rc_msk; bool wr; unsigned int rc_bank; unsigned int i; int ret = 0; len = br->len; if (!len) len++; rc_bank = br->bank; rc_bank <<= 7; /* registers only go to 0x7F */ rc_bank |= br->reg; rc_msk = ((1ULL << len) - 1) << (rc_bank % 64); rc_bank /= 64; val |= br->dflt; wr = st->rc_dis; if (!wr) { if (rc) { if ((st->rc_msk[rc_bank] & rc_msk) == rc_msk) { /* register is cached */ for (i = 0; i < len; i++) { if (*(rc + i) != (u8)(val >> (8 * i))) { /* register data changed */ wr = true; break; } } } else { /* register not cached */ wr = true; } } else { wr = true; } } if (wr) { if (be) ret = nvi_i2c_write_be(st, br, len, val); else ret = nvi_i2c_write_le(st, br, len, val); if (ret) { if (fn == NULL) fn = __func__; dev_err(&st->i2c->dev, "%s 0x%08x!=>0x%01x%02x ERR=%d\n", fn, val, br->bank, br->reg, ret); st->rc_msk[rc_bank] &= ~rc_msk; } else { if (st->sts & NVI_DBG_SPEW_MSG && fn) dev_info(&st->i2c->dev, "%s 0x%08x=>0x%01x%02x\n", fn, val, br->bank, br->reg); if (rc) { for (i = 0; i < len; i++) *(rc + i) = (u8)(val >> (8 * i)); st->rc_msk[rc_bank] |= rc_msk; } else { /* register data not cached */ st->rc_msk[rc_bank] &= ~rc_msk; } } } return ret; } int nvi_i2c_wr(struct nvi_state *st, const struct nvi_br *br, u8 val, const char *fn) { u8 buf[2]; int ret; buf[0] = br->reg; buf[1] = val | br->dflt; ret = nvi_wr_reg_bank_sel(st, br->bank); if (!ret) { ret = nvi_i2c_w(st, sizeof(buf), buf); if (ret) { if (fn == NULL) fn = __func__; dev_err(&st->i2c->dev, "%s 0x%02x!=>0x%01x%02x ERR=%d\n", fn, val, br->bank, br->reg, ret); } else { if (st->sts & NVI_DBG_SPEW_MSG && fn) dev_info(&st->i2c->dev, "%s 0x%02x=>0x%01x%02x\n", fn, val, br->bank, br->reg); } } return ret; } int nvi_i2c_wr_rc(struct nvi_state *st, const struct nvi_br *br, u8 val, const char *fn, u8 *rc) { u64 rc_msk; bool wr; unsigned int rc_bank; int ret = 0; val |= br->dflt; rc_bank = br->bank; rc_bank <<= 7; /* registers only go to 0x7F */ rc_bank |= br->reg; rc_msk = 1ULL << (rc_bank % 64); rc_bank /= 64; wr = st->rc_dis; if (!wr) { if (rc) { if (st->rc_msk[rc_bank] & rc_msk) { /* register is cached */ if (val != *rc) /* register data changed */ wr = true; } else { /* register not cached */ wr = true; } } else { wr = true; } } if (wr) { ret = nvi_i2c_wr(st, br, val, fn); if (ret) { st->rc_msk[rc_bank] &= ~rc_msk; } else { if (rc) { *rc = val; st->rc_msk[rc_bank] |= rc_msk; } else { st->rc_msk[rc_bank] &= ~rc_msk; } } } return ret; } int nvi_i2c_r(struct nvi_state *st, u8 bank, u8 reg, u16 len, u8 *buf) { struct i2c_msg msg[2]; int ret; ret = nvi_wr_reg_bank_sel(st, bank); if (ret) return ret; if (!len) len++; msg[0].addr = st->i2c->addr; msg[0].flags = 0; msg[0].len = 1; msg[0].buf = ® msg[1].addr = st->i2c->addr; msg[1].flags = I2C_M_RD; msg[1].len = len; msg[1].buf = buf; if (i2c_transfer(st->i2c->adapter, msg, 2) != 2) { nvi_err(st); return -EIO; } return 0; } int nvi_i2c_rd(struct nvi_state *st, const struct nvi_br *br, u8 *buf) { u16 len = br->len; if (!len) len = 1; return nvi_i2c_r(st, br->bank, br->reg, len, buf); } int nvi_mem_wr(struct nvi_state *st, u16 addr, u16 len, u8 *data, bool validate) { struct i2c_msg msg[6]; u8 buf_bank[2]; u8 buf_addr[2]; u8 buf_data[257]; u16 bank_len; u16 data_len; unsigned int data_i; int ret; ret = nvi_wr_reg_bank_sel(st, st->hal->reg->mem_bank.bank); if (ret) return ret; buf_bank[0] = st->hal->reg->mem_bank.reg; buf_bank[1] = addr >> 8; buf_addr[0] = st->hal->reg->mem_addr.reg; buf_addr[1] = addr & 0xFF; buf_data[0] = st->hal->reg->mem_rw.reg; msg[0].addr = st->i2c->addr; msg[0].flags = 0; msg[0].len = sizeof(buf_bank); msg[0].buf = buf_bank; msg[1].addr = st->i2c->addr; msg[1].flags = 0; msg[1].len = sizeof(buf_addr); msg[1].buf = buf_addr; msg[2].addr = st->i2c->addr; msg[2].flags = 0; msg[2].buf = buf_data; msg[3].addr = st->i2c->addr; msg[3].flags = 0; msg[3].len = sizeof(buf_addr); msg[3].buf = buf_addr; msg[4].addr = st->i2c->addr; msg[4].flags = 0; msg[4].len = 1; msg[4].buf = buf_data; msg[5].addr = st->i2c->addr; msg[5].flags = I2C_M_RD; msg[5].buf = &buf_data[1]; data_i = 0; bank_len = (addr + len - 1) >> 8; for (; buf_bank[1] <= bank_len; buf_bank[1]++) { if (buf_bank[1] == bank_len) data_len = len - data_i; else data_len = 0x0100 - buf_addr[1]; msg[2].len = data_len + 1; memcpy(&buf_data[1], data + data_i, data_len); if (i2c_transfer(st->i2c->adapter, msg, 3) != 3) { nvi_err(st); return -EIO; } if (validate) { msg[5].len = data_len; if (i2c_transfer(st->i2c->adapter, &msg[3], 3) != 3) { nvi_err(st); return -EIO; } ret = memcmp(&buf_data[1], data + data_i, data_len); if (ret) return ret; } data_i += data_len; buf_addr[1] = 0; } return 0; } int nvi_mem_wr_be(struct nvi_state *st, u16 addr, u16 len, u32 val) { u8 buf[4]; unsigned int i; int ret; for (i = 0; i < len; i++) buf[i] = (u8)(val >> (8 * (len - (i + 1)))); ret = nvi_mem_wr(st, addr, len, buf, false); if (st->sts & NVI_DBG_SPEW_MSG) dev_info(&st->i2c->dev, "%s 0x%08x=>0x%04hx err=%d\n", __func__, val, addr, ret); return ret; } int nvi_mem_wr_be_mc(struct nvi_state *st, u16 addr, u16 len, u32 val, u32 *mc) { int ret = 0; if (val != *mc || st->mc_dis) { ret = nvi_mem_wr_be(st, addr, len, val); if (!ret) *mc = val; } return ret; } int nvi_mem_rd(struct nvi_state *st, u16 addr, u16 len, u8 *data) { struct i2c_msg msg[4]; u8 buf_bank[2]; u8 buf_addr[2]; u16 bank_len; u16 data_len; unsigned int data_i; int ret; ret = nvi_wr_reg_bank_sel(st, st->hal->reg->mem_bank.bank); if (ret) return ret; buf_bank[0] = st->hal->reg->mem_bank.reg; buf_bank[1] = addr >> 8; buf_addr[0] = st->hal->reg->mem_addr.reg; buf_addr[1] = addr & 0xFF; msg[0].addr = st->i2c->addr; msg[0].flags = 0; msg[0].len = sizeof(buf_bank); msg[0].buf = buf_bank; msg[1].addr = st->i2c->addr; msg[1].flags = 0; msg[1].len = sizeof(buf_addr); msg[1].buf = buf_addr; msg[2].addr = st->i2c->addr; msg[2].flags = 0; msg[2].len = 1; msg[2].buf = (u8 *)&st->hal->reg->mem_rw.reg; msg[3].addr = st->i2c->addr; msg[3].flags = I2C_M_RD; data_i = 0; bank_len = (addr + len - 1) >> 8; for (; buf_bank[1] <= bank_len; buf_bank[1]++) { if (buf_bank[1] == bank_len) data_len = len - data_i; else data_len = 0x0100 - buf_addr[1]; msg[3].len = data_len; msg[3].buf = data + data_i; if (i2c_transfer(st->i2c->adapter, msg, 4) != 4) { nvi_err(st); return -EIO; } data_i += data_len; buf_addr[1] = 0; } return 0; } int nvi_mem_rd_le(struct nvi_state *st, u16 addr, u16 len, u32 *val) { u32 buf_le = 0; u8 buf_rd[4]; unsigned int i; int ret; ret = nvi_mem_rd(st, addr, len, buf_rd); if (!ret) { /* convert to little endian */ for (i = 0; i < len; i++) { buf_le <<= 8; buf_le |= buf_rd[i]; } *val = buf_le; } return ret; } static int nvi_rd_accel_offset(struct nvi_state *st) { u8 buf[2]; unsigned int i; int ret; for (i = 0; i < AXIS_N; i++) { ret = nvi_i2c_rd(st, &st->hal->reg->a_offset_h[i], buf); if (!ret) st->rc.accel_offset[i] = be16_to_cpup((__be16 *)buf); } return ret; } int nvi_wr_accel_offset(struct nvi_state *st, unsigned int axis, u16 offset) { return nvi_i2c_write_rc(st, &st->hal->reg->a_offset_h[axis], offset, __func__, (u8 *)&st->rc.accel_offset[axis], true); } static int nvi_rd_gyro_offset(struct nvi_state *st) { u8 buf[2]; unsigned int i; int ret; for (i = 0; i < AXIS_N; i++) { ret = nvi_i2c_rd(st, &st->hal->reg->g_offset_h[i], buf); if (!ret) st->rc.gyro_offset[i] = be16_to_cpup((__be16 *)buf); } return ret; } int nvi_wr_gyro_offset(struct nvi_state *st, unsigned int axis, u16 offset) { return nvi_i2c_write_rc(st, &st->hal->reg->g_offset_h[axis], offset, __func__, (u8 *)&st->rc.gyro_offset[axis], true); } int nvi_wr_fifo_cfg(struct nvi_state *st, int fifo) { u8 fifo_cfg; if (!st->hal->reg->fifo_cfg.reg) return 0; if (fifo >= 0) fifo_cfg = (fifo << 2) | 0x01; else fifo_cfg = 0; return nvi_i2c_wr_rc(st, &st->hal->reg->fifo_cfg, fifo_cfg, NULL, &st->rc.fifo_cfg); } static int nvi_wr_i2c_slv4_ctrl(struct nvi_state *st, bool slv4_en) { u8 val; val = st->aux.delay_hw; val |= (st->aux.port[AUX_PORT_IO].nmp.ctrl & BIT_I2C_SLV_REG_DIS); if (slv4_en) val |= BIT_SLV_EN; return nvi_i2c_wr_rc(st, &st->hal->reg->i2c_slv4_ctrl, val, __func__, &st->rc.i2c_slv4_ctrl); } static int nvi_rd_int_sts_dmp(struct nvi_state *st) { int ret; ret = nvi_i2c_rd(st, &st->hal->reg->int_dmp, &st->rc.int_dmp); if (ret) dev_err(&st->i2c->dev, "%s %x=ERR %d\n", __func__, st->hal->reg->int_dmp.reg, ret); return ret; } static int nvi_rd_int_status(struct nvi_state *st) { u8 buf[4] = {0, 0, 0, 0}; unsigned int i; unsigned int n; int ret; ret = nvi_i2c_rd(st, &st->hal->reg->int_status, buf); if (ret) { dev_err(&st->i2c->dev, "%s %x=ERR %d\n", __func__, st->hal->reg->int_status.reg, ret); } else { /* convert to little endian */ st->rc.int_status = 0; n = st->hal->reg->int_status.len; if (!n) n++; for (i = 0; i < n; i++) { st->rc.int_status <<= 8; st->rc.int_status |= buf[i]; } if (st->rc.int_status & (1 << st->hal->bit->int_dmp)) ret = nvi_rd_int_sts_dmp(st); } return ret; } int nvi_int_able(struct nvi_state *st, const char *fn, bool en) { u32 int_en = 0; u32 int_msk; unsigned int fifo; int dev; int ret; if (en) { if (st->en_msk & (1 << DEV_DMP)) { int_en |= 1 << st->hal->bit->int_dmp; } else if (st->en_msk & MSK_DEV_ALL) { int_msk = 1 << st->hal->bit->int_data_rdy_0; if (st->rc.fifo_cfg & 0x01) { /* multi FIFO enabled */ fifo = 0; for (; fifo < st->hal->fifo_n; fifo++) { dev = st->hal->fifo_dev[fifo]; if (dev < 0) continue; if (st->rc.fifo_en & st->hal-> dev[dev]->fifo_en_msk) int_en |= int_msk << fifo; } } else { int_en |= int_msk; } } } ret = nvi_i2c_write_rc(st, &st->hal->reg->int_enable, int_en, __func__, (u8 *)&st->rc.int_enable, false); if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s-%s en=%x int_en=%x err=%d\n", __func__, fn, en, int_en, ret); return ret; } static void nvi_flush_aux(struct nvi_state *st, int port) { struct aux_port *ap = &st->aux.port[port]; if (ap->nmp.handler) ap->nmp.handler(NULL, 0, 0, ap->nmp.ext_driver); } static void nvi_flush_push(struct nvi_state *st) { struct aux_port *ap; unsigned int i; int ret; for (i = 0; i < DEV_N; i++) { if (st->snsr[i].flush) { ret = st->nvs->handler(st->snsr[i].nvs_st, NULL, 0LL); if (ret >= 0) st->snsr[i].flush = false; } } for (i = 0; i < AUX_PORT_IO; i++) { ap = &st->aux.port[i]; if (ap->flush) nvi_flush_aux(st, i); ap->flush = false; } } static int nvi_user_ctrl_rst(struct nvi_state *st, u8 user_ctrl) { u8 fifo_rst; unsigned int msk; unsigned int n; int i; int ret = 0; int ret_t = 0; if (user_ctrl & BIT_SIG_COND_RST) user_ctrl = BITS_USER_CTRL_RST; if (user_ctrl & BIT_DMP_RST) user_ctrl |= BIT_FIFO_RST; if (user_ctrl & BIT_FIFO_RST) { st->buf_i = 0; if (st->hal->reg->fifo_rst.reg) { /* ICM part */ if (st->en_msk & (1 << DEV_DMP)) { ret = nvi_wr_fifo_cfg(st, st->hal->dmp->fifo_mode); } else { n = 0; for (i = 0; i < DEV_AXIS_N; i++) { if (st->hal->dev[i]->fifo_en_msk && st->snsr[i].enable) n++; } msk = st->snsr[DEV_AUX].enable; msk |= st->aux.dmp_en_msk; if (st->hal->dev[DEV_AUX]->fifo_en_msk && msk) n++; if (n > 1) ret = nvi_wr_fifo_cfg(st, 0); else ret = nvi_wr_fifo_cfg(st, -1); } if (st->icm_fifo_off) { ret |= nvi_i2c_wr(st, &st->hal->reg->fifo_rst, 0x1F, __func__); ret |= nvi_i2c_wr(st, &st->hal->reg->fifo_rst, 0, __func__); st->icm_fifo_off = false; } if (st->en_msk & (1 << DEV_DMP)) fifo_rst = 0x1E; else fifo_rst = 0; ret |= nvi_i2c_wr(st, &st->hal->reg->fifo_rst, 0x1F, __func__); ret |= nvi_i2c_wr(st, &st->hal->reg->fifo_rst, fifo_rst, __func__); if (ret) ret_t |= ret; else nvi_flush_push(st); if (user_ctrl == BIT_FIFO_RST) /* then done */ return ret_t; user_ctrl &= ~BIT_FIFO_RST; } } ret = nvi_i2c_wr(st, &st->hal->reg->user_ctrl, user_ctrl, __func__); if (ret) { ret_t |= ret; } else { if (user_ctrl & BIT_FIFO_RST) nvi_flush_push(st); for (i = 0; i < POWER_UP_TIME; i++) { user_ctrl = -1; ret = nvi_i2c_rd(st, &st->hal->reg->user_ctrl, &user_ctrl); if (!(user_ctrl & BITS_USER_CTRL_RST)) break; mdelay(1); } ret_t |= ret; st->rc.user_ctrl = user_ctrl; if (user_ctrl & BIT_DMP_RST && st->hal->dmp) { if (st->hal->dmp->dmp_reset_delay_ms) msleep(st->hal->dmp->dmp_reset_delay_ms); } } return ret_t; } int nvi_user_ctrl_en(struct nvi_state *st, const char *fn, bool en_dmp, bool en_fifo, bool en_i2c, bool en_irq) { struct aux_port *ap; int i; int ret = 0; u32 val = 0; if (en_dmp) { if (!(st->en_msk & (1 << DEV_DMP))) en_dmp = false; } if (en_fifo && !en_dmp) { for (i = 0; i < st->hal->src_n; i++) st->src[i].fifo_data_n = 0; for (i = 0; i < DEV_MPU_N; i++) { if (st->snsr[i].enable && st->hal->dev[i]->fifo_en_msk) { val |= st->hal->dev[i]->fifo_en_msk; st->src[st->hal->dev[i]->src].fifo_data_n += st->hal->dev[i]->fifo_data_n; st->fifo_src = st->hal->dev[i]->src; } } if (st->hal->dev[DEV_AUX]->fifo_en_msk && st->snsr[DEV_AUX].enable) { st->src[st->hal->dev[DEV_AUX]->src].fifo_data_n += st->aux.ext_data_n; st->fifo_src = st->hal->dev[DEV_AUX]->src; for (i = 0; i < AUX_PORT_IO; i++) { ap = &st->aux.port[i]; if (st->snsr[DEV_AUX].enable & (1 << i) && ap->nmp.addr & BIT_I2C_READ) val |= (1 << st->hal->bit->slv_fifo_en[i]); } } if (!val) en_fifo = false; } ret |= nvi_i2c_write_rc(st, &st->hal->reg->fifo_en, val, __func__, (u8 *)&st->rc.fifo_en, false); if (!ret) { val = 0; if (en_dmp) val |= BIT_DMP_EN; if (en_fifo) val |= BIT_FIFO_EN; if (en_i2c && (st->en_msk & (1 << DEV_AUX))) val |= BIT_I2C_MST_EN; else en_i2c = false; if (en_irq && val) ret = nvi_int_able(st, __func__, true); else en_irq = false; ret |= nvi_i2c_wr_rc(st, &st->hal->reg->user_ctrl, val, __func__, &st->rc.user_ctrl); } if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s-%s DMP=%x FIFO=%x I2C=%x IRQ=%x err=%d\n", __func__, fn, en_dmp, en_fifo, en_i2c, en_irq, ret); return ret; } int nvi_wr_pm1(struct nvi_state *st, const char *fn, u8 pm1) { u8 pm1_rd; unsigned int i; int ret = 0; if (pm1 & BIT_H_RESET) { /* must make sure FIFO is off or IRQ storm will occur */ ret = nvi_int_able(st, __func__, false); ret |= nvi_user_ctrl_en(st, __func__, false, false, false, false); if (!ret) { nvi_user_ctrl_rst(st, BITS_USER_CTRL_RST); ret = nvi_i2c_wr(st, &st->hal->reg->pm1, BIT_H_RESET, __func__); } } else { ret = nvi_i2c_wr_rc(st, &st->hal->reg->pm1, pm1, __func__, &st->rc.pm1); } st->pm = NVI_PM_ERR; if (pm1 & BIT_H_RESET && !ret) { st->en_msk &= MSK_RST; nvi_rc_clr(st, __func__); st->rc_dis = false; for (i = 0; i < st->hal->src_n; i++) st->src[i].period_us_req = 0; for (i = 0; i < (POWER_UP_TIME / REG_UP_TIME); i++) { mdelay(REG_UP_TIME); pm1_rd = -1; ret = nvi_i2c_rd(st, &st->hal->reg->pm1, &pm1_rd); if ((!ret) && (!(pm1_rd & BIT_H_RESET))) break; } msleep(POR_MS); nvi_rd_accel_offset(st); nvi_rd_gyro_offset(st); nvi_dmp_fw(st); } if (st->sts & NVI_DBG_SPEW_MSG) dev_info(&st->i2c->dev, "%s-%s pm1=%x err=%d\n", __func__, fn, pm1, ret); return ret; } static int nvi_pm_w(struct nvi_state *st, u8 pm1, u8 pm2, u8 lp) { s64 por_ns; unsigned int delay_ms; unsigned int i; int ret; ret = nvs_vregs_enable(&st->i2c->dev, st->vreg, ARRAY_SIZE(nvi_vregs)); if (ret) { delay_ms = 0; for (i = 0; i < ARRAY_SIZE(nvi_vregs); i++) { por_ns = nvs_timestamp() - st->ts_vreg_en[i]; if ((por_ns < 0) || (!st->ts_vreg_en[i])) { delay_ms = (POR_MS * 1000000); break; } if (por_ns < (POR_MS * 1000000)) { por_ns = (POR_MS * 1000000) - por_ns; if (por_ns > delay_ms) delay_ms = (unsigned int)por_ns; } } delay_ms /= 1000000; if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s %ums delay\n", __func__, delay_ms); if (delay_ms) msleep(delay_ms); ret = nvi_wr_pm1(st, __func__, BIT_H_RESET); } ret |= st->hal->fn->pm(st, pm1, pm2, lp); return ret; } int nvi_pm_wr(struct nvi_state *st, const char *fn, u8 pm1, u8 pm2, u8 lp) { int ret; ret = nvi_pm_w(st, pm1, pm2, lp); if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s-%s PM1=%x PM2=%x LPA=%x err=%d\n", __func__, fn, pm1, pm2, lp, ret); st->pm = NVI_PM_ERR; /* lost st->pm status: nvi_pm is being bypassed */ return ret; } /** * @param st * @param pm_req: call with one of the following: * NVI_PM_OFF_FORCE = force off state * NVI_PM_ON = minimum power for device access * NVI_PM_ON_FULL = power for gyro * NVI_PM_AUTO = automatically sets power after * configuration. * Typical use is to set needed power for configuration and * then call with NVI_PM_AUTO when done. All other NVI_PM_ * levels are handled automatically and are for internal * use. * @return int: returns 0 for success or error code */ static int nvi_pm(struct nvi_state *st, const char *fn, int pm_req) { u8 pm1; u8 pm2; u8 lp; int i; int pm; int ret = 0; lp = st->rc.lp_config; if (pm_req == NVI_PM_AUTO) { pm2 = 0; if (!(st->en_msk & MSK_PM_ACC_EN)) pm2 |= BIT_PWR_ACCEL_STBY; if (!(st->en_msk & MSK_PM_GYR_EN)) pm2 |= BIT_PWR_GYRO_STBY; if (st->en_msk & MSK_PM_ON_FULL) { pm = NVI_PM_ON_FULL; } else if (st->en_msk & MSK_PM_ON) { pm = NVI_PM_ON; } else if ((st->en_msk & ((1 << EN_LP) | MSK_DEV_ALL)) == MSK_PM_LP) { if (st->snsr[DEV_ACC].period_us_req >= st->snsr[DEV_ACC].cfg.thresh_hi) { for (lp = 0; lp < st->hal->lp_tbl_n; lp++) { if (st->snsr[DEV_ACC].period_us_req >= st->hal->lp_tbl[lp]) break; } pm = NVI_PM_ON_CYCLE; } else { pm = NVI_PM_ON; } } else if (st->en_msk & MSK_PM_LP) { pm = NVI_PM_ON; } else if (st->en_msk & MSK_PM_STDBY || st->aux.bypass_lock) { pm = NVI_PM_STDBY; } else { pm = NVI_PM_OFF; } } else { pm2 = st->rc.pm2; if ((pm_req > NVI_PM_STDBY) && (pm_req < st->pm)) pm = st->pm; else pm = pm_req; } if (pm == NVI_PM_OFF) { for (i = 0; i < AUX_PORT_IO; i++) { if (st->aux.port[i].nmp.shutdown_bypass) { nvi_aux_bypass_enable(st, true); pm = NVI_PM_STDBY; break; } } if (st->en_msk & (1 << FW_LOADED)) pm = NVI_PM_STDBY; } switch (pm) { case NVI_PM_OFF_FORCE: case NVI_PM_OFF: pm = NVI_PM_OFF; case NVI_PM_STDBY: pm1 = BIT_SLEEP; pm2 = (BIT_PWR_ACCEL_STBY | BIT_PWR_GYRO_STBY); break; case NVI_PM_ON_CYCLE: pm1 = BIT_CYCLE; pm2 &= ~BIT_PWR_ACCEL_STBY; break; case NVI_PM_ON: pm1 = INV_CLK_INTERNAL; if (pm2 & BIT_PWR_ACCEL_STBY) { for (i = 0; i < DEV_N_AUX; i++) { if (MSK_PM_ACC_EN & (1 << i)) { if (st->snsr[i].enable) { pm2 &= ~BIT_PWR_ACCEL_STBY; break; } } } } break; case NVI_PM_ON_FULL: pm1 = INV_CLK_PLL; /* gyro must be turned on before going to PLL clock */ pm2 &= ~BIT_PWR_GYRO_STBY; if (pm2 & BIT_PWR_ACCEL_STBY) { for (i = 0; i < DEV_N_AUX; i++) { if (MSK_PM_ACC_EN & (1 << i)) { if (st->snsr[i].enable) { pm2 &= ~BIT_PWR_ACCEL_STBY; break; } } } } break; default: dev_err(&st->i2c->dev, "%s %d=>%d ERR=EINVAL\n", __func__, st->pm, pm); return -EINVAL; } if (pm != st->pm || lp != st->rc.lp_config || pm2 != (st->rc.pm2 & (BIT_PWR_ACCEL_STBY | BIT_PWR_GYRO_STBY))) { if (pm == NVI_PM_OFF) { if (st->pm > NVI_PM_OFF || st->pm == NVI_PM_ERR) ret |= nvi_wr_pm1(st, __func__, BIT_H_RESET); ret |= nvi_pm_w(st, pm1, pm2, lp); ret |= nvs_vregs_disable(&st->i2c->dev, st->vreg, ARRAY_SIZE(nvi_vregs)); } else { if (pm == NVI_PM_ON_CYCLE) /* last chance to write to regs before cycle */ ret |= nvi_int_able(st, __func__, true); ret |= nvi_pm_w(st, pm1, pm2, lp); if (pm > NVI_PM_STDBY) mdelay(REG_UP_TIME); } if (ret < 0) { dev_err(&st->i2c->dev, "%s PM %d=>%d ERR=%d\n", __func__, st->pm, pm, ret); pm = NVI_PM_ERR; } if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s-%s PM %d=>%d PM1=%x PM2=%x LP=%x\n", __func__, fn, st->pm, pm, pm1, pm2, lp); st->pm = pm; if (ret > 0) ret = 0; } return ret; } static void nvi_pm_exit(struct nvi_state *st) { if (st->hal) nvi_pm(st, __func__, NVI_PM_OFF_FORCE); nvs_vregs_exit(&st->i2c->dev, st->vreg, ARRAY_SIZE(nvi_vregs)); } static int nvi_pm_init(struct nvi_state *st) { int ret; ret = nvs_vregs_init(&st->i2c->dev, st->vreg, ARRAY_SIZE(nvi_vregs), nvi_vregs); st->pm = NVI_PM_ERR; return ret; } static int nvi_dmp_fw(struct nvi_state *st) { #if NVI_FW_CRC_CHECK u32 crc32; #endif /* NVI_FW_CRC_CHECK */ int ret; st->icm_dmp_war = false; if (!st->hal->dmp) return -EINVAL; #if NVI_FW_CRC_CHECK crc32 = crc32(0, st->hal->dmp->fw, st->hal->dmp->fw_len); if (crc32 != st->hal->dmp->fw_crc32) { dev_err(&st->i2c->dev, "%s FW CRC FAIL %x != %x\n", __func__, crc32, st->hal->dmp->fw_crc32); return -EINVAL; } #endif /* NVI_FW_CRC_CHECK */ ret = nvi_user_ctrl_en(st, __func__, false, false, false, false); if (ret) return ret; ret = nvi_mem_wr(st, st->hal->dmp->fw_mem_addr, st->hal->dmp->fw_len, (u8 *)st->hal->dmp->fw, true); if (ret) { if (st->sts & NVI_STS_PART_ID_VALID) dev_err(&st->i2c->dev, "%s ERR: nvi_mem_wr\n", __func__); return ret; } ret = nvi_i2c_write_rc(st, &st->hal->reg->fw_start, st->hal->dmp->fw_start, __func__, NULL, true); if (ret) return ret; ret = st->hal->dmp->fn_init(st); /* nvi_dmp_init */ if (ret) { dev_err(&st->i2c->dev, "%s ERR: nvi_dmp_init\n", __func__); return ret; } nvi_user_ctrl_en(st, __func__, false, false, false, false); st->en_msk |= (1 << FW_LOADED); return 0; } void nvi_push_delay(struct nvi_state *st) { unsigned int i; for (i = 0; i < DEV_MPU_N; i++) { if (st->snsr[i].enable) { if (st->snsr[i].push_delay_ns && !st->snsr[i].ts_push_delay) st->snsr[i].ts_push_delay = nvs_timestamp() + st->snsr[i].push_delay_ns; } else { st->snsr[i].ts_push_delay = 0; } } } int nvi_aux_delay(struct nvi_state *st, const char *fn) { u8 val; unsigned int msk_en; unsigned int src_us; unsigned int delay; unsigned int i; int ret; /* determine valid delays by ports enabled */ delay = 0; msk_en = st->snsr[DEV_AUX].enable | st->aux.dmp_en_msk; for (i = 0; msk_en; i++) { if (msk_en & (1 << i)) { msk_en &= ~(1 << i); if (delay < st->aux.port[i].nmp.delay_ms) delay = st->aux.port[i].nmp.delay_ms; } } src_us = st->src[st->hal->dev[DEV_AUX]->src].period_us_src; if (src_us) { delay *= 1000; /* ms => us */ if (delay % src_us) { delay /= src_us; } else { delay /= src_us; if (delay) delay--; } } else { delay = 0; } if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s-%s aux.delay_hw=%u=>%u\n", __func__, fn, st->aux.delay_hw, delay); st->aux.delay_hw = delay; ret = nvi_wr_i2c_slv4_ctrl(st, (bool) (st->rc.i2c_slv4_ctrl & BIT_SLV_EN)); /* HW port delay enable */ val = BIT_DELAY_ES_SHADOW; for (i = 0; i < AUX_PORT_MAX; i++) { if (st->aux.port[i].nmp.delay_ms) val |= (1 << i); } ret |= nvi_i2c_wr_rc(st, &st->hal->reg->i2c_mst_delay_ctrl, val, __func__, &st->rc.i2c_mst_delay_ctrl); return ret; } static int nvi_timeout(struct nvi_state *st) { bool disabled = true; unsigned int timeout_us = -1; unsigned int i; /* find the fastest batch timeout of all the enabled devices */ for (i = 0; i < DEV_N_AUX; i++) { if (st->snsr[i].enable) { if (st->snsr[i].timeout_us < timeout_us) timeout_us = st->snsr[i].timeout_us; disabled = false; } } disabled = true; /* batch mode is currently disabled */ if (disabled) timeout_us = 0; /* batch mode disabled */ if (timeout_us != st->bm_timeout_us) { st->bm_timeout_us = timeout_us; return 1; } return 0; } static int nvi_period_src(struct nvi_state *st, int src) { bool enabled = false; unsigned int period_us = -1; unsigned int dev_msk; unsigned int i; if (src < 0) return 0; /* find the fastest period of all the enabled devices */ dev_msk = st->hal->src[src].dev_msk; for (i = 0; dev_msk; i++) { if (dev_msk & (1 << i)) { dev_msk &= ~(1 << i); if (st->snsr[i].enable && st->snsr[i].period_us_req) { if (st->snsr[i].period_us_req < period_us) period_us = st->snsr[i].period_us_req; enabled = true; } } } if (enabled) { if (period_us < st->src[src].period_us_min) period_us = st->src[src].period_us_min; if (period_us > st->src[src].period_us_max) period_us = st->src[src].period_us_max; if (period_us != st->src[src].period_us_req) { st->src[src].period_us_req = period_us; return 1; } } return 0; } int nvi_period_aux(struct nvi_state *st) { bool enabled = false; unsigned int period_us = -1; unsigned int timeout_us = -1; unsigned int msk_en; unsigned int i; int ret = 0; msk_en = st->snsr[DEV_AUX].enable | st->aux.dmp_en_msk; for (i = 0; msk_en; i++) { if (msk_en & (1 << i)) { msk_en &= ~(1 << i); if (st->aux.port[i].period_us_req) { if (st->aux.port[i].period_us_req < period_us) period_us = st->aux.port[i].period_us_req; if (st->aux.port[i].timeout_us < timeout_us) timeout_us = st->aux.port[i].timeout_us; enabled = true; } } } if (enabled) { st->snsr[DEV_AUX].timeout_us = timeout_us; if (st->snsr[DEV_AUX].period_us_req != period_us) { st->snsr[DEV_AUX].period_us_req = period_us; ret |= 1; /* flag something changed */ } } ret |= nvi_period_src(st, st->hal->dev[DEV_AUX]->src); ret |= nvi_timeout(st); return ret; } static int nvi_period_all(struct nvi_state *st) { unsigned int src; int ret = 0; for (src = 0; src < st->hal->src_n; src++) { if (st->hal->src[src].dev_msk & (1 << DEV_AUX)) continue; /* run nvi_period_aux last for timeout */ else ret |= nvi_period_src(st, src); } ret |= nvi_period_aux(st); return ret; } static int nvi_en(struct nvi_state *st) { bool dmp_en = false; unsigned int i; unsigned int us; int src; int ret; int ret_t = 0; while (1) { if (st->snsr[DEV_GYR].enable) { ret_t = nvi_pm(st, __func__, NVI_PM_ON_FULL); break; } for (i = 0; i < DEV_N_AUX; i++) { if (st->snsr[i].enable) { ret_t = nvi_pm(st, __func__, NVI_PM_ON); break; } } if (i < DEV_N_AUX) break; ret_t = nvi_pm(st, __func__, NVI_PM_AUTO); if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s AUTO en_msk=%x err=%d\n", __func__, st->en_msk, ret_t); return ret_t; } ret_t |= nvi_int_able(st, __func__, false); ret_t |= nvi_user_ctrl_en(st, __func__, false, false, false, false); if (ret_t) { if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_err(&st->i2c->dev, "%s DIS_ERR en_msk=%x ERR=%d\n", __func__, st->en_msk, ret_t); return ret_t; } if (st->en_msk & (1 << FW_LOADED)) { /* test if batch is needed or more specifically that an * enabled sensor doesn't support batch. The DMP can't * do batch and non-batch at the same time. */ if (st->bm_timeout_us) { dmp_en = true; } else { /* batch disabled - test if a DMP sensor is enabled */ for (i = 0; i < DEV_N_AUX; i++) { if (st->dmp_en_msk & (1 << i)) { if (st->snsr[i].enable) { dmp_en = true; break; } } } } if (dmp_en) { ret_t |= st->hal->dmp->fn_en(st); /* nvi_dmp_en */ st->en_msk |= (1 << DEV_DMP); if (ret_t) { /* reprogram for non-DMP mode below */ dmp_en = false; if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_err(&st->i2c->dev, "%s DMP ERR=%d\n", __func__, ret_t); } else { if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s DMP enabled\n", __func__); } } } if (!dmp_en) { if (st->en_msk & (1 << DEV_DMP)) { st->en_msk &= ~(MSK_DEV_SNSR | (1 << DEV_DMP)); if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s DMP disabled\n", __func__); if (st->aux.dmp_en_msk) { st->aux.dmp_en_msk = 0; nvi_aux_enable(st, __func__, true, true); } for (i = 0; i < DEV_N_AUX; i++) st->snsr[i].odr = 0; for (i = 0; i < AUX_PORT_MAX; i++) st->aux.port[i].odr = 0; } ret = 0; for (i = 0; i < st->hal->src_n; i++) ret |= st->hal->src[i].fn_period(st); if (ret) { ret_t |= ret; } else { for (i = 0; i < DEV_N; i++) { src = st->hal->dev[i]->src; if (src < 0 || src >= SRC_N) continue; us = st->src[src].period_us_src; st->snsr[i].period_us_rd = us; } us = st->src[st->hal->dev[DEV_AUX]->src].period_us_src; for (i = 0; i < AUX_PORT_IO; i++) st->aux.port[i].period_us_rd = us; } if (st->snsr[DEV_ACC].enable) { ret = st->hal->fn->en_acc(st); if (ret) { ret_t |= ret; st->en_msk &= ~(1 << DEV_ACC); } else { st->en_msk |= (1 << DEV_ACC); } } if (st->snsr[DEV_GYR].enable) { ret = st->hal->fn->en_gyr(st); if (ret) { ret_t |= ret; st->en_msk &= ~(1 << DEV_GYR); } else { st->en_msk |= (1 << DEV_GYR); } } nvi_push_delay(st); /* NVI_PM_AUTO to go to NVI_PM_ON_CYCLE if need be */ /* this also restores correct PM mode if error */ ret_t |= nvi_pm(st, __func__, NVI_PM_AUTO); if (st->pm > NVI_PM_ON_CYCLE) ret_t |= nvi_reset(st, __func__, true, false, true); } if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG)) dev_info(&st->i2c->dev, "%s EXIT en_msk=%x err=%d\n", __func__, st->en_msk, ret_t); return ret_t; } static void nvi_aux_dbg(struct nvi_state *st, char *tag, int val) { struct nvi_mpu_port *n; struct aux_port *p; struct aux_ports *a; u8 data[4]; unsigned int i; int ret; if (!(st->sts & NVI_DBG_SPEW_AUX)) return; dev_info(&st->i2c->dev, "%s %s %d\n", __func__, tag, val); a = &st->aux; for (i = 0; i < AUX_PORT_IO; i++) { ret = nvi_i2c_rd(st, &st->hal->reg->i2c_slv_addr[i], &data[0]); ret |= nvi_i2c_rd(st, &st->hal->reg->i2c_slv_reg[i], &data[1]); ret |= nvi_i2c_rd(st, &st->hal->reg->i2c_slv_ctrl[i], &data[2]); ret |= nvi_i2c_rd(st, &st->hal->reg->i2c_slv_do[i], &data[3]); /* HW = hardware */ if (ret) pr_info("HW: ERR=%d\n", ret); else pr_info("HW: P%d AD=%x RG=%x CL=%x DO=%x\n", i, data[0], data[1], data[2], data[3]); /* RC = hardware register cache */ pr_info("HC: P%d AD=%x RG=%x CL=%x DO=%x\n", i, st->rc.i2c_slv_addr[i], st->rc.i2c_slv_reg[i], st->rc.i2c_slv_ctrl[i], st->rc.i2c_slv_do[i]); n = &st->aux.port[i].nmp; /* NS = nmp structure */ pr_info("NS: P%d AD=%x RG=%x CL=%x DO=%x MS=%u US=%u SB=%x\n", i, n->addr, n->reg, n->ctrl, n->data_out, n->delay_ms, st->aux.port[i].period_us_rd, n->shutdown_bypass); p = &st->aux.port[i]; /* PS = port structure */ pr_info("PS: P%d EDO=%u ODR=%u DMP_CTRL=%x EN=%x HWDOUT=%x\n", i, p->ext_data_offset, p->odr, !!(a->dmp_ctrl_msk & (1 << i)), !!(st->snsr[DEV_AUX].enable & (1 << i)), p->hw_do); } pr_info("AUX: EN=%x MEN=%x DEN=%x DLY=%x SRC=%u DN=%u BEN=%x BLK=%d\n", !!(st->en_msk & (1 << DEV_AUX)), !!(st->rc.user_ctrl & BIT_I2C_MST_EN), st->aux.dmp_en_msk, (st->rc.i2c_slv4_ctrl & BITS_I2C_MST_DLY), st->src[st->hal->dev[DEV_AUX]->src].period_us_src, a->ext_data_n, (st->rc.int_pin_cfg & BIT_BYPASS_EN), a->bypass_lock); } static void nvi_aux_ext_data_offset(struct nvi_state *st) { unsigned int i; unsigned int offset = 0; for (i = 0; i < AUX_PORT_IO; i++) { if (st->aux.port[i].nmp.addr & BIT_I2C_READ) { st->aux.port[i].ext_data_offset = offset; offset += (st->rc.i2c_slv_ctrl[i] & BITS_I2C_SLV_CTRL_LEN); } } if (offset > AUX_EXT_DATA_REG_MAX) { offset = AUX_EXT_DATA_REG_MAX; dev_err(&st->i2c->dev, "%s ERR MPU slaves exceed data storage\n", __func__); } st->aux.ext_data_n = offset; return; } static int nvi_aux_port_data_out(struct nvi_state *st, int port, u8 data_out) { int ret; ret = nvi_i2c_wr_rc(st, &st->hal->reg->i2c_slv_do[port], data_out, NULL, &st->rc.i2c_slv_do[port]); if (!ret) { st->aux.port[port].nmp.data_out = data_out; st->aux.port[port].hw_do = true; } else { st->aux.port[port].hw_do = false; } return ret; } static int nvi_aux_port_wr(struct nvi_state *st, int port) { struct aux_port *ap; int ret; ap = &st->aux.port[port]; ret = nvi_i2c_wr_rc(st, &st->hal->reg->i2c_slv_addr[port], ap->nmp.addr, __func__, &st->rc.i2c_slv_addr[port]); ret |= nvi_i2c_wr_rc(st, &st->hal->reg->i2c_slv_reg[port], ap->nmp.reg, __func__, &st->rc.i2c_slv_reg[port]); ret |= nvi_i2c_wr_rc(st, &st->hal->reg->i2c_slv_do[port], ap->nmp.data_out, __func__, &st->rc.i2c_slv_do[port]); return ret; } static int nvi_aux_port_en(struct nvi_state *st, int port, bool en) { struct aux_port *ap; u8 slv_ctrl; u8 ctrl; u8 reg; unsigned int dmp_ctrl_msk; int ret = 0; ap = &st->aux.port[port]; if (en && ap->nmp.addr != st->rc.i2c_slv_addr[port]) { ret = nvi_aux_port_wr(st, port); if (!ret) ap->hw_do = true; } if (en && !ap->hw_do) nvi_aux_port_data_out(st, port, ap->nmp.data_out); if (port == AUX_PORT_IO) { ret = nvi_wr_i2c_slv4_ctrl(st, en); } else { slv_ctrl = st->rc.i2c_slv_ctrl[port]; if (en) { dmp_ctrl_msk = st->aux.dmp_ctrl_msk; reg = ap->nmp.reg; ctrl = ap->nmp.ctrl; if (ap->dd && st->en_msk & (1 << DEV_DMP)) { reg = ap->dd->dmp_rd_reg; if (ctrl != ap->dd->dmp_rd_ctrl) { ctrl = ap->dd->dmp_rd_ctrl; st->aux.dmp_ctrl_msk |= (1 << port); } } else { st->aux.dmp_ctrl_msk &= ~(1 << port); } if (dmp_ctrl_msk != st->aux.dmp_ctrl_msk) /* AUX HW needs to be reset if slv_ctrl values * change other than enable bit. */ st->aux.reset_i2c = true; ret = nvi_i2c_wr_rc(st, &st->hal->reg->i2c_slv_reg[port], reg, __func__, &st->rc.i2c_slv_reg[port]); ctrl |= BIT_SLV_EN; } else { ctrl = 0; st->aux.dmp_ctrl_msk &= ~(1 << port); } ret |= nvi_i2c_wr_rc(st, &st->hal->reg->i2c_slv_ctrl[port], ctrl, __func__, &st->rc.i2c_slv_ctrl[port]); if (slv_ctrl != st->rc.i2c_slv_ctrl[port]) nvi_aux_ext_data_offset(st); } return ret; } int nvi_aux_enable(struct nvi_state *st, const char *fn, bool en_req, bool force) { bool enable = en_req; bool enabled = false; bool en; unsigned int msk_en; unsigned int i; int ret = 0; if (st->rc.int_pin_cfg & BIT_BYPASS_EN) enable = false; /* global enable is honored only if a port is enabled */ msk_en = st->snsr[DEV_AUX].enable | st->aux.dmp_en_msk; if (!msk_en) enable = false; if (st->en_msk & (1 << DEV_AUX)) enabled = true; if (force || enable != enabled) { if (enable) { st->en_msk |= (1 << DEV_AUX); for (i = 0; i < AUX_PORT_MAX; i++) { if (msk_en & (1 << i)) en = true; else en = false; ret |= nvi_aux_port_en(st, i, en); } } else { st->en_msk &= ~(1 << DEV_AUX); for (i = 0; i < AUX_PORT_MAX; i++) { if (st->rc.i2c_slv_addr[i]) nvi_aux_port_en(st, i, false); } } if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG | NVI_DBG_SPEW_AUX)) dev_info(&st->i2c->dev, "%s-%s en_req=%x enabled: %x->%x err=%d\n", __func__, fn, en_req, enabled, enable, ret); } return ret; } static int nvi_aux_port_enable(struct nvi_state *st, unsigned int port_mask, bool en) { unsigned int enabled; unsigned int i; int ret; enabled = st->snsr[DEV_AUX].enable; if (en) st->snsr[DEV_AUX].enable |= port_mask; else st->snsr[DEV_AUX].enable &= ~port_mask; if (enabled == st->snsr[DEV_AUX].enable) return 0; if (st->hal->dev[DEV_AUX]->fifo_en_msk) { /* AUX uses FIFO */ for (i = 0; i < AUX_PORT_IO; i++) { if (port_mask & (1 << i)) { if (st->aux.port[i].nmp.addr & BIT_I2C_READ) st->aux.reset_fifo = true; } } } if (en && (st->rc.int_pin_cfg & BIT_BYPASS_EN)) return 0; ret = 0; for (i = 0; i < AUX_PORT_MAX; i++) { if (port_mask & (1 << i)) ret |= nvi_aux_port_en(st, i, en); } ret |= nvi_aux_enable(st, __func__, true, false); nvi_period_aux(st); if (port_mask & ((1 << AUX_PORT_IO) - 1)) ret |= nvi_en(st); return ret; } static int nvi_aux_port_free(struct nvi_state *st, int port) { memset(&st->aux.port[port], 0, sizeof(struct aux_port)); st->snsr[DEV_AUX].enable &= ~(1 << port); st->aux.dmp_en_msk &= ~(1 << port); if (st->rc.i2c_slv_addr[port]) { nvi_aux_port_wr(st, port); nvi_aux_port_en(st, port, false); nvi_aux_enable(st, __func__, false, false); nvi_user_ctrl_en(st, __func__, false, false, false, false); nvi_aux_enable(st, __func__, true, false); if (port != AUX_PORT_IO) st->aux.reset_i2c = true; nvi_period_aux(st); nvi_en(st); } return 0; } static int nvi_aux_port_alloc(struct nvi_state *st, struct nvi_mpu_port *nmp, int port) { struct nvi_aux_port_dmp_dev *dd = NULL; struct nvi_dmp_aux_port *ap; unsigned int i; unsigned int j; if (st->aux.reset_i2c) nvi_reset(st, __func__, false, true, true); if (port < 0) { for (i = 0; i < AUX_PORT_IO; i++) { if (!st->aux.port[i].nmp.addr) /* port available */ break; } if (i < AUX_PORT_IO) port = i; else return -ENODEV; } else { if (st->aux.port[port].nmp.addr) /* already taken */ return -ENODEV; } /* override port setting if DMP used */ if (st->hal->dmp && port < AUX_PORT_IO) { for (i = 0; i < st->hal->dmp->ap_n; i++) { ap = &st->hal->dmp->ap[i]; if (nmp->type == ap->type && ap->port_rd == (bool)(nmp->addr & BIT_I2C_READ)) { if (ap->dd) { for (j = 0; j < ap->dd_n; j++) { if (nmp->id == ap->dd[j].dev) { dd = &ap->dd[j]; break; } } if (j >= ap->dd_n) /* device not supported */ return -ENODEV; } break; } } if (i < st->hal->dmp->ap_n && !st->aux.port[ap->port].nmp.addr) port = ap->port; else return -ENODEV; } memset(&st->aux.port[port], 0, sizeof(struct aux_port)); memcpy(&st->aux.port[port].nmp, nmp, sizeof(struct nvi_mpu_port)); st->aux.port[port].dd = dd; st->aux.port[port].nmp.ctrl &= ~BIT_SLV_EN; st->aux.port[port].period_us_req = st->aux.port[port].nmp.period_us; return port; } static int nvi_aux_bypass_enable(struct nvi_state *st, bool en) { u8 val; int ret; if (en && (st->rc.int_pin_cfg & BIT_BYPASS_EN)) return 0; val = st->rc.int_pin_cfg; if (en) { ret = nvi_aux_enable(st, __func__, false, false); ret |= nvi_user_ctrl_en(st, __func__, false, false, false, false); if (!ret) { val |= BIT_BYPASS_EN; ret = nvi_i2c_wr_rc(st, &st->hal->reg->int_pin_cfg, val, __func__, &st->rc.int_pin_cfg); } } else { val &= ~BIT_BYPASS_EN; ret = nvi_i2c_wr_rc(st, &st->hal->reg->int_pin_cfg, val, __func__, &st->rc.int_pin_cfg); if (!ret) nvi_aux_enable(st, __func__, true, false); } nvi_period_aux(st); nvi_en(st); return ret; } static int nvi_aux_bypass_request(struct nvi_state *st, bool enable) { s64 ns; s64 to; int ret = 0; if ((bool)(st->rc.int_pin_cfg & BIT_BYPASS_EN) == enable) { st->aux.bypass_timeout_ns = nvs_timestamp(); st->aux.bypass_lock++; if (!st->aux.bypass_lock) dev_err(&st->i2c->dev, "%s rollover ERR\n", __func__); } else { if (st->aux.bypass_lock) { ns = nvs_timestamp() - st->aux.bypass_timeout_ns; to = st->bypass_timeout_ms; to *= 1000000; if (ns > to) st->aux.bypass_lock = 0; else ret = -EBUSY; } if (!st->aux.bypass_lock) { ret = nvi_aux_bypass_enable(st, enable); if (ret) dev_err(&st->i2c->dev, "%s ERR=%d\n", __func__, ret); else st->aux.bypass_lock++; } } return ret; } static int nvi_aux_bypass_release(struct nvi_state *st) { int ret = 0; if (st->aux.bypass_lock) st->aux.bypass_lock--; if (!st->aux.bypass_lock) { ret = nvi_aux_bypass_enable(st, false); if (ret) dev_err(&st->i2c->dev, "%s ERR=%d\n", __func__, ret); } return ret; } static int nvi_aux_dev_valid(struct nvi_state *st, struct nvi_mpu_port *nmp, u8 *data) { u8 val; int i; int ret; /* turn off bypass */ ret = nvi_aux_bypass_request(st, false); if (ret) return -EBUSY; /* grab the special port */ ret = nvi_aux_port_alloc(st, nmp, AUX_PORT_IO); if (ret != AUX_PORT_IO) { nvi_aux_bypass_release(st); return -EBUSY; } /* enable it at fastest speed */ st->aux.port[AUX_PORT_IO].nmp.delay_ms = 0; st->aux.port[AUX_PORT_IO].period_us_req = st->src[st->hal->dev[DEV_AUX]->src].period_us_min; ret = nvi_user_ctrl_en(st, __func__, false, false, false, false); ret |= nvi_aux_port_enable(st, 1 << AUX_PORT_IO, true); ret |= nvi_user_ctrl_en(st, __func__, false, false, true, false); if (ret) { nvi_aux_port_free(st, AUX_PORT_IO); nvi_aux_bypass_release(st); return -EBUSY; } /* now turn off all the other ports for fastest response */ for (i = 0; i < AUX_PORT_IO; i++) { if (st->rc.i2c_slv_addr[i]) nvi_aux_port_en(st, i, false); } /* start reading the results */ for (i = 0; i < AUX_DEV_VALID_READ_LOOP_MAX; i++) { mdelay(AUX_DEV_VALID_READ_DELAY_MS); val = 0; ret = nvi_i2c_rd(st, &st->hal->reg->i2c_mst_status, &val); if (ret) continue; if (val & 0x50) break; } /* these will restore all previously disabled ports */ nvi_aux_bypass_release(st); nvi_aux_port_free(st, AUX_PORT_IO); if (i >= AUX_DEV_VALID_READ_LOOP_MAX) return -ENODEV; if (val & 0x10) /* NACK */ return -EIO; if (nmp->addr & BIT_I2C_READ) { ret = nvi_i2c_rd(st, &st->hal->reg->i2c_slv4_di, &val); if (ret) return -EBUSY; *data = (u8)val; dev_info(&st->i2c->dev, "%s MPU read 0x%x from device 0x%x\n", __func__, val, (nmp->addr & ~BIT_I2C_READ)); } else { dev_info(&st->i2c->dev, "%s MPU found device 0x%x\n", __func__, (nmp->addr & ~BIT_I2C_READ)); } return 0; } static int nvi_aux_mpu_call_pre(struct nvi_state *st, int port) { if ((port < 0) || (port >= AUX_PORT_IO)) return -EINVAL; if (st->sts & (NVS_STS_SHUTDOWN | NVS_STS_SUSPEND)) return -EPERM; if (!st->aux.port[port].nmp.addr) return -EINVAL; return 0; } static int nvi_aux_mpu_call_post(struct nvi_state *st, char *tag, int ret) { if (ret < 0) ret = -EBUSY; nvi_aux_dbg(st, tag, ret); return ret; } /* See the mpu.h file for details on the nvi_mpu_ calls. */ int nvi_mpu_dev_valid(struct nvi_mpu_port *nmp, u8 *data) { struct nvi_state *st = nvi_state_local; int ret = -EPERM; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s\n", __func__); } else { pr_debug("%s ERR -EAGAIN\n", __func__); return -EAGAIN; } if (nmp == NULL) return -EINVAL; if ((nmp->addr & BIT_I2C_READ) && (data == NULL)) return -EINVAL; nvi_mutex_lock(st); if (!(st->sts & (NVS_STS_SHUTDOWN | NVS_STS_SUSPEND))) { nvi_pm(st, __func__, NVI_PM_ON); ret = nvi_aux_dev_valid(st, nmp, data); nvi_pm(st, __func__, NVI_PM_AUTO); nvi_aux_dbg(st, "nvi_mpu_dev_valid=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_dev_valid); int nvi_mpu_port_alloc(struct nvi_mpu_port *nmp) { struct nvi_state *st = nvi_state_local; int ret = -EPERM; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s\n", __func__); } else { pr_debug("%s ERR -EAGAIN\n", __func__); return -EAGAIN; } if (nmp == NULL || !(nmp->ctrl & BITS_I2C_SLV_CTRL_LEN)) return -EINVAL; if (nmp->addr & BIT_I2C_READ && !nmp->handler) return -EINVAL; nvi_mutex_lock(st); if (!(st->sts & (NVS_STS_SHUTDOWN | NVS_STS_SUSPEND))) { nvi_pm(st, __func__, NVI_PM_ON); ret = nvi_aux_port_alloc(st, nmp, -1); if (ret >= 0 && st->hal->dmp) /* need to reinitialize DMP for new device */ st->hal->dmp->fn_init(st); nvi_pm(st, __func__, NVI_PM_AUTO); ret = nvi_aux_mpu_call_post(st, "nvi_mpu_port_alloc=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_port_alloc); int nvi_mpu_port_free(int port) { struct nvi_state *st = nvi_state_local; int ret; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s port %d\n", __func__, port); } else { pr_debug("%s port %d ERR -EAGAIN\n", __func__, port); return -EAGAIN; } nvi_mutex_lock(st); ret = nvi_aux_mpu_call_pre(st, port); if (!ret) { nvi_pm(st, __func__, NVI_PM_ON); ret = nvi_aux_port_free(st, port); nvi_pm(st, __func__, NVI_PM_AUTO); ret = nvi_aux_mpu_call_post(st, "nvi_mpu_port_free=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_port_free); int nvi_mpu_enable(unsigned int port_mask, bool enable) { struct nvi_state *st = nvi_state_local; unsigned int i; int ret; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s port_mask 0x%x: %x\n", __func__, port_mask, enable); } else { pr_debug("%s port_mask 0x%x: %x ERR -EAGAIN\n", __func__, port_mask, enable); return -EAGAIN; } if (port_mask >= (1 << AUX_PORT_IO) || !port_mask) return -EINVAL; for (i = 0; i < AUX_PORT_IO; i++) { if (port_mask & (1 << i)) { if (!st->aux.port[i].nmp.addr) return -EINVAL; } } nvi_mutex_lock(st); if (st->sts & (NVS_STS_SHUTDOWN | NVS_STS_SUSPEND)) { ret = -EPERM; } else { nvi_pm(st, __func__, NVI_PM_ON); ret = nvi_aux_port_enable(st, port_mask, enable); ret = nvi_aux_mpu_call_post(st, "nvi_mpu_enable=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_enable); int nvi_mpu_delay_ms(int port, u8 delay_ms) { struct nvi_state *st = nvi_state_local; int ret; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s port %d: %u\n", __func__, port, delay_ms); } else { pr_debug("%s port %d: %u ERR -EAGAIN\n", __func__, port, delay_ms); return -EAGAIN; } nvi_mutex_lock(st); ret = nvi_aux_mpu_call_pre(st, port); if (!ret) { st->aux.port[port].nmp.delay_ms = delay_ms; if (st->rc.i2c_slv_ctrl[port] & BIT_SLV_EN) ret = nvi_aux_delay(st, __func__); ret = nvi_aux_mpu_call_post(st, "nvi_mpu_delay_ms=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_delay_ms); int nvi_mpu_data_out(int port, u8 data_out) { struct nvi_state *st = nvi_state_local; int ret; if (st == NULL) return -EAGAIN; ret = nvi_aux_mpu_call_pre(st, port); if (!ret) { if (st->rc.i2c_slv_ctrl[port] & BIT_SLV_EN) { ret = nvi_aux_port_data_out(st, port, data_out); } else { st->aux.port[port].nmp.data_out = data_out; st->aux.port[port].hw_do = false; } if (ret < 0) ret = -EBUSY; } return ret; } EXPORT_SYMBOL(nvi_mpu_data_out); int nvi_mpu_batch(int port, unsigned int period_us, unsigned int timeout_us) { struct nvi_state *st = nvi_state_local; int ret; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s port %d: p=%u t=%u\n", __func__, port, period_us, timeout_us); } else { pr_debug("%s port %d: p=%u t=%u ERR -EAGAIN\n", __func__, port, period_us, timeout_us); return -EAGAIN; } nvi_mutex_lock(st); ret = nvi_aux_mpu_call_pre(st, port); if (!ret) { if (timeout_us && ((st->aux.port[port].nmp.id == ID_INVALID) || (st->aux.port[port].nmp.id >= ID_INVALID_END))) { /* sensor not supported by DMP */ ret = -EINVAL; } else { st->aux.port[port].period_us_req = period_us; st->aux.port[port].timeout_us = timeout_us; ret = nvi_period_aux(st); if (st->en_msk & (1 << DEV_DMP) && st->hal->dmp->fn_dev_batch) { /* batch can be done real-time with DMP on */ /* nvi_dd_batch */ ret = st->hal->dmp->fn_dev_batch(st, DEV_AUX, port); } else { if (ret > 0) /* timings changed */ ret = nvi_en(st); } ret = nvi_aux_mpu_call_post(st, "nvi_mpu_batch=", ret); } } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_batch); int nvi_mpu_batch_read(int port, unsigned int *period_us, unsigned int *timeout_us) { struct nvi_state *st = nvi_state_local; int ret; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s port %d: p=%p t=%p\n", __func__, port, period_us, timeout_us); } else { pr_debug("%s port %d: p=%p t=%p ERR -EAGAIN\n", __func__, port, period_us, timeout_us); return -EAGAIN; } nvi_mutex_lock(st); ret = nvi_aux_mpu_call_pre(st, port); if (!ret) { if (timeout_us) *timeout_us = 0; if (period_us) *period_us = st->aux.port[port].period_us_rd; ret = nvi_aux_mpu_call_post(st, "nvi_mpu_batch_read=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_batch_read); int nvi_mpu_flush(int port) { struct nvi_state *st = nvi_state_local; int ret; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s port %d\n", __func__, port); } else { pr_debug("%s port %d ERR -EAGAIN\n", __func__, port); return -EAGAIN; } nvi_mutex_lock(st); ret = nvi_aux_mpu_call_pre(st, port); if (!ret) { if (st->hal->dev[DEV_AUX]->fifo_en_msk) { /* HW flush only when FIFO is used for AUX */ st->aux.port[port].flush = true; ret = nvi_read(st, true); } else { nvi_flush_aux(st, port); } ret = nvi_aux_mpu_call_post(st, "nvi_mpu_flush=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_flush); int nvi_mpu_info(int read_port, struct nvi_mpu_inf *inf) { struct nvi_state *st = nvi_state_local; struct nvi_aux_port_dmp_dev *dd; unsigned int i; int ret; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s port %d\n", __func__, read_port); } else { pr_debug("%s port %d ERR -EAGAIN\n", __func__, read_port); return -EAGAIN; } if (!inf) return -EINVAL; nvi_mutex_lock(st); ret = nvi_aux_mpu_call_pre(st, read_port); if (!ret) { i = st->hal->dev[DEV_AUX]->src; inf->period_us_min = st->src[i].period_us_min; inf->period_us_max = st->src[i].period_us_max; /* batch not supported at this time */ inf->fifo_reserve = 0; inf->fifo_max = 0; dd = st->aux.port[read_port].dd; if (dd) { inf->dmp_rd_len_sts = dd->dmp_rd_len_sts; inf->dmp_rd_len_data = dd->dmp_rd_len_data; inf->dmp_rd_be_sts = dd->dmp_rd_be_sts; inf->dmp_rd_be_data = dd->dmp_rd_be_data; } else { inf->dmp_rd_len_sts = 0; inf->dmp_rd_len_data = 0; inf->dmp_rd_be_sts = false; inf->dmp_rd_be_data = false; } ret = nvi_aux_mpu_call_post(st, "nvi_mpu_info=", 0); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_info); int nvi_mpu_bypass_request(bool enable) { struct nvi_state *st = nvi_state_local; int ret = -EPERM; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s enable=%x\n", __func__, enable); } else { pr_debug("%s ERR -EAGAIN\n", __func__); return -EAGAIN; } nvi_mutex_lock(st); if (!(st->sts & (NVS_STS_SHUTDOWN | NVS_STS_SUSPEND))) { nvi_pm(st, __func__, NVI_PM_ON); ret = nvi_aux_bypass_request(st, enable); nvi_pm(st, __func__, NVI_PM_AUTO); ret = nvi_aux_mpu_call_post(st, "nvi_mpu_bypass_request=", ret); } nvi_mutex_unlock(st); return ret; } EXPORT_SYMBOL(nvi_mpu_bypass_request); int nvi_mpu_bypass_release(void) { struct nvi_state *st = nvi_state_local; if (st != NULL) { if (st->sts & NVI_DBG_SPEW_AUX) pr_info("%s\n", __func__); } else { pr_debug("%s\n", __func__); return 0; } nvi_mutex_lock(st); if (!(st->sts & (NVS_STS_SHUTDOWN | NVS_STS_SUSPEND))) { nvi_pm(st, __func__, NVI_PM_ON); nvi_aux_bypass_release(st); nvi_pm(st, __func__, NVI_PM_AUTO); nvi_aux_mpu_call_post(st, "nvi_mpu_bypass_release", 0); } nvi_mutex_unlock(st); return 0; } EXPORT_SYMBOL(nvi_mpu_bypass_release); int nvi_reset(struct nvi_state *st, const char *fn, bool rst_fifo, bool rst_i2c, bool en_irq) { s64 ts; u8 val; bool rst_dmp = false; unsigned int i; int ret; ret = nvi_int_able(st, __func__, false); val = 0; if (rst_i2c || st->aux.reset_i2c) { st->aux.reset_i2c = false; rst_i2c = true; ret |= nvi_aux_enable(st, __func__, false, false); val |= BIT_I2C_MST_RST; } if (rst_fifo) { st->aux.reset_fifo = false; val |= BIT_FIFO_RST; if (st->en_msk & (1 << DEV_DMP)) { val |= BIT_DMP_RST; rst_dmp = true; ret |= nvi_aux_enable(st, __func__, false, false); } } ret |= nvi_user_ctrl_en(st, __func__, !rst_fifo, !rst_fifo, !rst_i2c, false); val |= st->rc.user_ctrl; ret |= nvi_user_ctrl_rst(st, val); if (rst_i2c || rst_dmp) ret |= nvi_aux_enable(st, __func__, true, false); ts = nvs_timestamp(); if (rst_fifo) { for (i = 0; i < st->hal->src_n; i++) { st->src[i].ts_reset = true; st->src[i].ts_1st = ts; st->src[i].ts_end = ts; st->src[i].ts_period = st->src[i].period_us_src * 1000; } for (i = 0; i < DEV_N_AUX; i++) { st->snsr[i].ts_reset = true; st->snsr[i].ts_last = ts; st->snsr[i].ts_n = 0; } for (i = 0; i < AUX_PORT_MAX; i++) { st->aux.port[i].ts_reset = true; st->aux.port[i].ts_last = ts; } if (st->hal->dmp) { /* nvi_dmp_clk_n */ ret |= st->hal->dmp->fn_clk_n(st, &st->dmp_clk_n); st->src[SRC_DMP].ts_reset = true; st->src[SRC_DMP].ts_1st = ts; st->src[SRC_DMP].ts_end = ts; st->src[SRC_DMP].ts_period = st->src[SRC_DMP].period_us_src * 1000; } } ret |= nvi_user_ctrl_en(st, __func__, true, true, true, en_irq); if (st->sts & (NVS_STS_SPEW_MSG | NVI_DBG_SPEW_MSG | NVI_DBG_SPEW_FIFO | NVI_DBG_SPEW_TS)) dev_info(&st->i2c->dev, "%s-%s DMP=%x FIFO=%x I2C=%x ts=%lld err=%d\n", __func__, fn, rst_dmp, rst_fifo, rst_i2c, ts, ret); return ret; } s64 nvi_ts_dev(struct nvi_state *st, s64 ts_now, unsigned int dev, unsigned int aux_port) { s64 ts; int src; if (ts_now) { if (st->en_msk & (1 << DEV_DMP)) src = SRC_DMP; else src = st->hal->dev[dev]->src; } else { src = -1; } if (src < 0) { ts = nvs_timestamp(); } else { if (dev == DEV_AUX && aux_port < AUX_PORT_MAX) { if (st->aux.port[aux_port].ts_reset) { st->aux.port[aux_port].ts_reset = false; ts = st->src[src].ts_1st; } else { ts = st->src[src].ts_period; if (st->aux.port[aux_port].odr) ts *= (st->aux.port[aux_port].odr + 1); ts += st->aux.port[aux_port].ts_last; } } else { if (st->snsr[dev].ts_reset) { st->snsr[dev].ts_reset = false; ts = st->src[src].ts_1st; } else { ts = st->src[src].ts_period; if (st->snsr[dev].odr) ts *= (st->snsr[dev].odr + 1); ts += st->snsr[dev].ts_last; } } if (ts > ts_now) { if (st->sts & (NVI_DBG_SPEW_FIFO | NVI_DBG_SPEW_TS)) dev_info(&st->i2c->dev, "%s ts > ts_now (%lld > %lld)\n", __func__, ts, ts_now); ts = ts_now; } } if (dev == DEV_AUX && aux_port < AUX_PORT_MAX) { if (ts < st->aux.port[aux_port].ts_last) ts = -1; else st->aux.port[aux_port].ts_last = ts; } else { if (ts < st->snsr[dev].ts_last) ts = -1; else st->snsr[dev].ts_last = ts; } if (ts < st->snsr[dev].ts_push_delay) ts = -1; if (st->sts & NVI_DBG_SPEW_FIFO && src >= 0) dev_info(&st->i2c->dev, "src[%d] ts_period=%lld ts_end=%lld %s ts[%u]=%lld\n", src, st->src[src].ts_period, st->src[src].ts_end, st->snsr[dev].cfg.name, st->snsr[dev].ts_n, ts); st->snsr[dev].ts_n++; return ts; } static void nvi_aux_rd(struct nvi_state *st) { s64 ts; u8 *p; struct aux_port *ap; unsigned int len; unsigned int i; int ret; if ((!st->aux.ext_data_n) || (!(st->rc.user_ctrl & BIT_I2C_MST_EN))) return; ret = nvi_i2c_r(st, st->hal->reg->ext_sens_data_00.bank, st->hal->reg->ext_sens_data_00.reg, st->aux.ext_data_n, (u8 *)&st->aux.ext_data); if (ret) return; ts = nvi_ts_dev(st, 0, DEV_AUX, -1); for (i = 0; i < AUX_PORT_IO; i++) { ap = &st->aux.port[i]; if ((st->rc.i2c_slv_ctrl[i] & BIT_SLV_EN) && ap->nmp.addr & BIT_I2C_READ) { p = &st->aux.ext_data[ap->ext_data_offset]; len = ap->nmp.ctrl & BITS_I2C_SLV_CTRL_LEN; ap->nmp.handler(p, len, ts, ap->nmp.ext_driver); } } } static s32 nvi_matrix(struct nvi_state *st, signed char *matrix, s32 x, s32 y, s32 z, unsigned int axis) { return ((matrix[0 + axis] == 1 ? x : (matrix[0 + axis] == -1 ? -x : 0)) + (matrix[3 + axis] == 1 ? y : (matrix[3 + axis] == -1 ? -y : 0)) + (matrix[6 + axis] == 1 ? z : (matrix[6 + axis] == -1 ? -z : 0))); } int nvi_push(struct nvi_state *st, unsigned int dev, u8 *buf, s64 ts) { u8 buf_le[20]; s32 val_le[4]; s32 val[AXIS_N]; u32 u_val; unsigned int sts; unsigned int buf_le_i; unsigned int ch; unsigned int ch_sz; unsigned int m; unsigned int n; int i; #ifdef ENABLE_TRACE int cookie = 0; u64 ts_irq = atomic64_read(&st->ts_irq); cookie = COOKIE(SENSOR_TYPE_ACCELEROMETER, ts_irq); trace_async_atrace_begin(__func__, TRACE_SENSOR_ID, cookie); trace_async_atrace_end(__func__, TRACE_SENSOR_ID, cookie); cookie = COOKIE(snsr_ids[dev], ts); trace_async_atrace_begin(__func__, TRACE_SENSOR_ID, cookie); #endif // ENABLE_TRACE ch_sz = abs(st->snsr[dev].cfg.ch_sz); m = 0; if (st->snsr[dev].buf_n) { n = st->snsr[dev].buf_n / st->snsr[dev].cfg.ch_n; m = st->snsr[dev].buf_n % st->snsr[dev].cfg.ch_n; if (m) n++; } else { n = ch_sz; } /* convert big endian byte stream to little endian channel data */ for (ch = 0; ch < st->snsr[dev].cfg.ch_n; ch++) { val_le[ch] = 0; if (st->snsr[dev].enable & (1 << ch)) { if (m && ch == (st->snsr[dev].cfg.ch_n - 1)) { /* handle last channel misalignment */ for (i = 0; i < m; i++) { val_le[ch] <<= 8; val_le[ch] |= (u8)*buf++; } /* extend sign bit */ i = (sizeof(val_le[ch]) - m) * 8; val_le[ch] <<= i; val_le[ch] >>= i; } else { for (i = 0; i < n; i++) { val_le[ch] <<= 8; val_le[ch] |= (u8)*buf++; } /* extend sign bit */ i = (sizeof(val_le[ch]) - n) * 8; if (i) { val_le[ch] <<= i; val_le[ch] >>= i; } } } } /* shift HW data size to channel size if needed */ if (st->snsr[dev].buf_shft) { if (st->snsr[dev].buf_shft < 0) { n = abs(st->snsr[dev].buf_shft); for (ch = 0; ch < st->snsr[dev].cfg.ch_n; ch++) val_le[ch] >>= n; } else { for (ch = 0; ch < st->snsr[dev].cfg.ch_n; ch++) val_le[ch] <<= st->snsr[dev].buf_shft; } } /* apply matrix if needed */ if (st->snsr[dev].matrix) { for (ch = 0; ch < AXIS_N; ch++) val[ch] = val_le[ch]; for (ch = 0; ch < AXIS_N; ch++) val_le[ch] = nvi_matrix(st, st->snsr[dev].cfg.matrix, val[AXIS_X], val[AXIS_Y], val[AXIS_Z], ch); } /* convert little endian channel data to little endian byte stream */ buf_le_i = 0; for (ch = 0; ch < st->snsr[dev].cfg.ch_n; ch++) { u_val = (u32)val_le[ch]; for (i = 0; i < ch_sz; i++) { buf_le[buf_le_i + i] = (u8)(u_val & 0xFF); u_val >>= 8; } buf_le_i += ch_sz; } /* add status if needed (no endian conversion) */ if (buf_le_i < st->snsr[dev].cfg.snsr_data_n) { n = st->snsr[dev].cfg.snsr_data_n - buf_le_i; if (n > sizeof(u_val)) n = sizeof(u_val); u_val = st->snsr[dev].sts; for (i = 0; i < n; i++) { buf_le[buf_le_i + i] = (u8)(u_val & 0xFF); u_val >>= 8; } } if (ts >= 0) { if (st->sts & (NVI_DBG_SPEW_SNSR << dev)) { sts = st->sts; st->sts |= NVS_STS_SPEW_DATA; st->nvs->handler(st->snsr[dev].nvs_st, buf_le, ts); if (!(sts & NVS_STS_SPEW_DATA)) st->sts &= ~NVS_STS_SPEW_DATA; } else { st->nvs->handler(st->snsr[dev].nvs_st, buf_le, ts); } } #ifdef ENABLE_TRACE trace_async_atrace_end(__func__, TRACE_SENSOR_ID, cookie); #endif // ENABLE_TRACE return buf_le_i; } static int nvi_push_event(struct nvi_state *st, unsigned int dev) { s64 ts = nvs_timestamp(); u8 val = 1; unsigned int sts; int ret; #ifdef ENABLE_TRACE int cookie = 0; u64 ts_irq = atomic64_read(&st->ts_irq); cookie = COOKIE(SENSOR_TYPE_ACCELEROMETER, ts_irq); trace_async_atrace_begin(__func__, TRACE_SENSOR_ID, cookie); trace_async_atrace_end(__func__, TRACE_SENSOR_ID, cookie); cookie = COOKIE(snsr_ids[dev], ts); trace_async_atrace_begin(__func__, TRACE_SENSOR_ID, cookie); #endif // ENABLE_TRACE if (st->sts & (NVI_DBG_SPEW_SNSR << dev)) { sts = st->sts; st->sts |= NVS_STS_SPEW_DATA; ret = st->nvs->handler(st->snsr[dev].nvs_st, &val, ts); if (!(sts & NVS_STS_SPEW_DATA)) st->sts &= ~NVS_STS_SPEW_DATA; } else { ret = st->nvs->handler(st->snsr[dev].nvs_st, &val, ts); } #ifdef ENABLE_TRACE trace_async_atrace_end(__func__, TRACE_SENSOR_ID, cookie); #endif // ENABLE_TRACE return ret; } static int nvi_push_oneshot(struct nvi_state *st, unsigned int dev) { /* disable now to avoid reinitialization on handler's disable */ st->snsr[dev].enable = 0; st->en_msk &= ~(1 << dev); return nvi_push_event(st, dev); } static int nvi_dev_rd(struct nvi_state *st, unsigned int dev) { u8 buf[AXIS_N * 2]; u16 len; int ret; if (!st->snsr[dev].enable) return 0; len = st->snsr[dev].cfg.ch_n << 1; ret = nvi_i2c_r(st, st->hal->reg->out_h[dev].bank, st->hal->reg->out_h[dev].reg, len, buf); if (!ret) ret = nvi_push(st, dev, buf, nvi_ts_dev(st, 0, dev, 0)); return ret; } static int nvi_fifo_aux(struct nvi_state *st, s64 ts, unsigned int n) { struct aux_port *ap; unsigned int fifo_data_n; unsigned int port; ts = nvi_ts_dev(st, ts, DEV_AUX, -1); for (port = 0; port < AUX_PORT_IO; port++) { ap = &st->aux.port[port]; if (st->rc.fifo_en & (1 << st->hal->bit->slv_fifo_en[port])) { fifo_data_n = ap->nmp.ctrl & BITS_I2C_SLV_CTRL_LEN; if (fifo_data_n > n) return 0; ap->nmp.handler(&st->buf[st->buf_i], fifo_data_n, ts, ap->nmp.ext_driver); st->buf_i += fifo_data_n; n -= fifo_data_n; } if (st->sts & (NVS_STS_SUSPEND | NVS_STS_SHUTDOWN)) return -1; } return 1; } static int nvi_fifo_dev_rd(struct nvi_state *st, s64 ts, unsigned int n, unsigned int dev) { if (st->sts & (NVS_STS_SUSPEND | NVS_STS_SHUTDOWN)) return -1; if (st->hal->dev[dev]->fifo_data_n > n) return 0; nvi_push(st, dev, &st->buf[st->buf_i], nvi_ts_dev(st, ts, dev, 0)); st->buf_i += st->hal->dev[dev]->fifo_data_n; return 1; } static int nvi_fifo_dev(struct nvi_state *st, s64 ts, unsigned int n) { unsigned int dev; int ret; dev = st->hal->fifo_dev[(st->rc.fifo_cfg >> 2) & 0x07]; if (dev == DEV_AUX) ret = nvi_fifo_aux(st, ts, n); else ret = nvi_fifo_dev_rd(st, ts, n, dev); return ret; } static int nvi_fifo_devs(struct nvi_state *st, s64 ts, unsigned int n) { unsigned int dev; int ret = 0; for (dev = 0; dev < DEV_MPU_N; dev++) { if (st->rc.fifo_en & st->hal->dev[dev]->fifo_en_msk) { ret = nvi_fifo_dev_rd(st, ts, n, dev); if (ret <= 0) return ret; } } if (st->rc.fifo_en & st->hal->dev[DEV_AUX]->fifo_en_msk) ret = nvi_fifo_aux(st, ts, n); return ret; } /* fifo_n_max can be used if we want to round-robin FIFOs */ static int nvi_fifo_rd(struct nvi_state *st, int src, unsigned int fifo_n_max, int (*fn)(struct nvi_state *st, s64 ts, unsigned int n)) { u16 fifo_count; u32 dmp_clk_n = 0; s64 ts_period; s64 ts_now; s64 ts_end; bool sync; unsigned int ts_n; unsigned int fifo_n; unsigned int buf_n; int ret = 0; ts_end = nvs_timestamp(); if (src < 0) /* nvi_dmp_clk_n */ ret = st->hal->dmp->fn_clk_n(st, &dmp_clk_n); ret |= nvi_i2c_rd(st, &st->hal->reg->fifo_count_h, (u8 *)&fifo_count); if (ret || !fifo_count) return 0; ts_now = nvs_timestamp(); if (ts_now < (ts_end + 5000000)) sync = true; else sync = false; ts_end = atomic64_read(&st->ts_irq); fifo_n = (unsigned int)be16_to_cpu(fifo_count); if (st->sts & NVS_STS_SPEW_IRQ) dev_info(&st->i2c->dev, "src=%d sync=%x fifo_n=%u ts_clk_n=%u ts_diff=%lld\n", src, sync, fifo_n, dmp_clk_n, ts_now - st->ts_now); st->ts_now = ts_now; if (src < 0) { /* DMP timing */ if (dmp_clk_n > st->dmp_clk_n) ts_n = dmp_clk_n - st->dmp_clk_n; else /* counter rolled over */ ts_n = (~st->dmp_clk_n + 1) + dmp_clk_n; /* ts_n is the number of DMP clock ticks since last time */ st->dmp_clk_n = dmp_clk_n; src = SRC_DMP; fifo_n_max = 0; /* DMP disables round-robin FIFOs */ } else { /* FIFO timing */ ts_n = fifo_n / st->src[src].fifo_data_n; /* TS's needed */ if ((fifo_n % st->src[src].fifo_data_n) || !ts_n) /* reset FIFO if doesn't divide cleanly */ return -1; } if (ts_n) { ts_period = st->src[src].period_us_src; ts_period *= 1000; if (sync && ts_end > st->src[src].ts_end && ts_end < ts_now && ts_end > (ts_now - (ts_period >> 2))) /* ts_irq is within the rate so sync to IRQ */ ts_now = ts_end; if (st->src[src].ts_reset) { st->src[src].ts_reset = false; ts_end = st->src[src].ts_period * (ts_n - 1); if (sync) { st->src[src].ts_1st = ts_now - ts_end; st->src[src].ts_end = st->src[src].ts_1st; } } else { ts_end = st->src[src].ts_period * ts_n; } ts_end += st->src[src].ts_end; /* ts_now will be sent to nvi_ts_dev where the timestamp is * prevented from going into the future which allows some * tolerance here for ts_end being a little more than ts_now. * The more tolerance we have the less recalculating the period * to avoid swing around the true period. Plus, the clamp on * ts_now in nvi_ts_dev has the benefit of "syncing" with the * current calculations per device. */ if (ts_end > (ts_now + (ts_period >> 3)) || (sync && (ts_end < (ts_now - (ts_period >> 1))))) { if (st->sts & (NVI_DBG_SPEW_FIFO | NVI_DBG_SPEW_TS)) { dev_info(&st->i2c->dev, "sync=%x now=%lld end=%lld ts_n=%u\n", sync, ts_now, ts_end, ts_n); dev_info(&st->i2c->dev, "src=%d old period=%lld end=%lld\n", src, st->src[src].ts_period, st->src[src].ts_end); } /* st->src[src].ts_period needs to be adjusted */ ts_period = ts_now - st->src[src].ts_end; do_div(ts_period, ts_n); st->src[src].ts_period = ts_period; ts_end = ts_period * ts_n; ts_end += st->src[src].ts_end; if (st->sts & (NVI_DBG_SPEW_FIFO | NVI_DBG_SPEW_TS)) dev_info(&st->i2c->dev, "src=%d new period=%lld end=%lld\n", src, ts_period, ts_end); } if (fifo_n_max) { /* would only apply to FIFO timing (non-DMP) */ if (fifo_n_max < fifo_n) { fifo_n = fifo_n_max; ts_n = fifo_n / st->src[src].fifo_data_n; ts_end = st->src[src].ts_period * ts_n; ts_end += st->src[src].ts_end; } } st->src[src].ts_end = ts_end; } else { /* wasn't able to calculate TS */ ts_now = 0; } while (fifo_n) { buf_n = sizeof(st->buf) - st->buf_i; if (buf_n > fifo_n) buf_n = fifo_n; ret = nvi_i2c_r(st, st->hal->reg->fifo_rw.bank, st->hal->reg->fifo_rw.reg, buf_n, &st->buf[st->buf_i]); if (ret) return 0; fifo_n -= buf_n; buf_n += st->buf_i; st->buf_i = 0; /* fn updates st->buf_i */ while (st->buf_i < buf_n) { ret = fn(st, ts_now, buf_n - st->buf_i); /* ret < 0: error to exit * ret = 0: not enough data to process * ret > 0: all done processing data */ if (ret <= 0) break; } buf_n -= st->buf_i; if (buf_n) { memcpy(st->buf, &st->buf[st->buf_i], buf_n); st->buf_i = buf_n; } else { st->buf_i = 0; } if (ret < 0) break; } return ret; } static int nvi_rd(struct nvi_state *st) { u8 val; u32 int_msk; unsigned int fifo; int src; int ret; if (st->en_msk & (1 << DEV_DMP)) { if (st->en_msk & ((1 << DEV_SM) | (1 << DEV_STP))) { ret = nvi_i2c_rd(st, &st->hal->reg->int_dmp, &val); if (val & (1 << st->hal->bit->dmp_int_sm)) nvi_push_oneshot(st, DEV_SM); if (val & (1 << st->hal->bit->dmp_int_stp)) nvi_push_event(st, DEV_STP); } if (st->en_msk & st->dmp_en_msk) /* nvi_dmp_rd */ return nvi_fifo_rd(st, -1, 0, st->hal->dmp->fn_rd); nvi_en(st); return 0; } if (st->pm == NVI_PM_ON_CYCLE) { /* only low power accelerometer data */ nvi_pm(st, __func__, NVI_PM_ON); ret = nvi_dev_rd(st, DEV_ACC); nvi_pm(st, __func__, NVI_PM_AUTO); return 0; } nvi_dev_rd(st, DEV_TMP); if (!(st->rc.fifo_en & st->hal->dev[DEV_AUX]->fifo_en_msk)) nvi_aux_rd(st); /* handle FIFO enabled data */ if (st->rc.fifo_cfg & 0x01) { /* multi FIFO enabled */ int_msk = 1 << st->hal->bit->int_data_rdy_0; for (fifo = 0; fifo < st->hal->fifo_n; fifo++) { if (st->rc.int_enable & (int_msk << fifo)) { ret = nvi_wr_fifo_cfg(st, fifo); if (ret) return 0; src = st->hal->dev[st->hal-> fifo_dev[fifo]]->src; ret = nvi_fifo_rd(st, src, 0, nvi_fifo_dev); if (st->buf_i || (ret < 0)) { /* HW FIFO misalignment - reset */ nvi_err(st); return -1; } } } } else { /* st->fifo_src is either SRC_MPU or the source for the single * device enabled for the single FIFO in ICM. */ ret = nvi_fifo_rd(st, st->fifo_src, 0, nvi_fifo_devs); if (st->buf_i || (ret < 0)) { /* HW FIFO misalignment - reset */ nvi_err(st); return -1; } } return 0; } static int nvi_read(struct nvi_state *st, bool flush) { int ret; if (st->irq_dis && !(st->sts & NVS_STS_SHUTDOWN)) { dev_err(&st->i2c->dev, "%s ERR: IRQ storm reset. n=%u\n", __func__, st->irq_storm_n); st->irq_storm_n = 0; nvi_pm(st, __func__, NVI_PM_ON); nvi_wr_pm1(st, __func__, BIT_H_RESET); nvi_enable_irq(st); nvi_en(st); } else if (!(st->sts & (NVS_STS_SUSPEND | NVS_STS_SHUTDOWN))) { ret = nvi_rd(st); if (ret < 0) nvi_en(st); /* a little harder reset for ICM DMP */ else if (flush) nvi_reset(st, __func__, true, false, true); } else if (flush) { nvi_flush_push(st); } return 0; } static irqreturn_t nvi_thread(int irq, void *dev_id) { struct nvi_state *st = (struct nvi_state *)dev_id; nvi_mutex_lock(st); nvi_read(st, false); nvi_mutex_unlock(st); return IRQ_HANDLED; } static irqreturn_t nvi_handler(int irq, void *dev_id) { u64 ts_old; u64 ts_diff; #ifdef ENABLE_TRACE int cookie = 0; #endif // ENABLE_TRACE struct nvi_state *st = (struct nvi_state *)dev_id; u64 ts = nvs_timestamp(); #ifdef ENABLE_TRACE cookie = COOKIE(SENSOR_TYPE_ACCELEROMETER, ts); trace_async_atrace_begin(__func__, TRACE_SENSOR_ID, cookie); #endif // ENABLE_TRACE ts_old = atomic64_xchg(&st->ts_irq, ts); ts_diff = ts - ts_old; /* test for MPU IRQ storm problem */ if (ts_diff < NVI_IRQ_STORM_MIN_NS) { st->irq_storm_n++; if (st->irq_storm_n > NVI_IRQ_STORM_MAX_N) nvi_disable_irq(st); } else { st->irq_storm_n = 0; } if (st->sts & NVS_STS_SPEW_IRQ) dev_info(&st->i2c->dev, "%s ts=%llu ts_diff=%llu irq_dis=%x\n", __func__, ts, ts_diff, st->irq_dis); #ifdef ENABLE_TRACE trace_async_atrace_end(__func__, TRACE_SENSOR_ID, cookie); #endif // ENABLE_TRACE return IRQ_WAKE_THREAD; } static int nvi_enable(void *client, int snsr_id, int enable) { struct nvi_state *st = (struct nvi_state *)client; if (enable < 0) /* return current enable request status */ return st->snsr[snsr_id].enable; if (st->snsr[snsr_id].enable == enable) /* nothing has changed with enable request */ return 0; st->snsr[snsr_id].enable = enable; if (!enable) /* officially flagged as off here */ st->en_msk &= ~(1 << snsr_id); if (st->sts & NVS_STS_SUSPEND) /* speed up suspend/resume by not doing nvi_en for every dev */ return 0; if (snsr_id == DEV_TMP) /* this is a static sensor that will be read when gyro is on */ return 0; if (st->en_msk & (1 << DEV_DMP)) { /* DMP is currently on */ if (!(st->en_msk & st->dmp_en_msk)) /* DMP may get turned off (may stay on due to batch) so * we update timings that may have changed while DMP * was on. */ nvi_period_all(st); } else { nvi_period_src(st, st->hal->dev[snsr_id]->src); nvi_timeout(st); } return nvi_en(st); } static int nvi_batch(void *client, int snsr_id, int flags, unsigned int period_us, unsigned int timeout_us) { struct nvi_state *st = (struct nvi_state *)client; int ret = 0; /* We use batch to set parameters in realtime for one-shot sensors * (that normally doesn't use batch) */ if (SENSOR_FLAG_ONE_SHOT_MODE == (st->snsr[snsr_id].cfg.flags & REPORTING_MODE_MASK)) { st->snsr[snsr_id].cfg.delay_us_min = period_us; st->snsr[snsr_id].cfg.delay_us_max = timeout_us; st->snsr[snsr_id].cfg.report_n = flags; if (st->en_msk & (1 << DEV_DMP)) ret = st->hal->dmp->fn_dev_init(st, snsr_id); return ret; } if (timeout_us && !st->snsr[snsr_id].cfg.fifo_max_evnt_cnt) return -EINVAL; if (snsr_id == DEV_TMP) return 0; if (period_us == st->snsr[snsr_id].period_us_req && timeout_us == st->snsr[snsr_id].timeout_us) return 0; st->snsr[snsr_id].period_us_req = period_us; st->snsr[snsr_id].timeout_us = timeout_us; if (!st->snsr[snsr_id].enable) return 0; ret = nvi_timeout(st); if (st->en_msk & (1 << DEV_DMP)) { if (st->hal->dmp->fn_dev_batch) /* batch can be done in real-time with the DMP on */ /* nvi_dd_batch */ ret = st->hal->dmp->fn_dev_batch(st, snsr_id, -1); else ret = nvi_en(st); } else { ret |= nvi_period_src(st, st->hal->dev[snsr_id]->src); if (ret > 0) ret = nvi_en(st); } return ret; } static int nvi_batch_read(void *client, int snsr_id, unsigned int *period_us, unsigned int *timeout_us) { struct nvi_state *st = (struct nvi_state *)client; if (timeout_us) *timeout_us = 0; if (period_us) *period_us = st->snsr[snsr_id].period_us_rd; return 0; } static int nvi_flush(void *client, int snsr_id) { struct nvi_state *st = (struct nvi_state *)client; int ret = -EINVAL; if (st->snsr[snsr_id].enable) { st->snsr[snsr_id].flush = true; ret = nvi_read(st, true); } return ret; } static int nvi_max_range(void *client, int snsr_id, int max_range) { struct nvi_state *st = (struct nvi_state *)client; int ret = 0; unsigned int i = max_range; unsigned int ch; if (snsr_id < 0 || snsr_id >= DEV_N) return -EINVAL; if (st->snsr[snsr_id].enable) /* can't change settings on the fly (disable device first) */ return -EPERM; if (i > st->hal->dev[snsr_id]->rr_0n) /* clamp to highest setting */ i = st->hal->dev[snsr_id]->rr_0n; st->snsr[snsr_id].usr_cfg = i; st->snsr[snsr_id].cfg.resolution.ival = st->hal->dev[snsr_id]->rr[i].resolution.ival; st->snsr[snsr_id].cfg.resolution.fval = st->hal->dev[snsr_id]->rr[i].resolution.fval; st->snsr[snsr_id].cfg.max_range.ival = st->hal->dev[snsr_id]->rr[i].max_range.ival; st->snsr[snsr_id].cfg.max_range.fval = st->hal->dev[snsr_id]->rr[i].max_range.fval; st->snsr[snsr_id].cfg.offset.ival = st->hal->dev[snsr_id]->offset.ival; st->snsr[snsr_id].cfg.offset.fval = st->hal->dev[snsr_id]->offset.fval; st->snsr[snsr_id].cfg.scale.ival = st->hal->dev[snsr_id]->scale.ival; st->snsr[snsr_id].cfg.scale.fval = st->hal->dev[snsr_id]->scale.fval; /* AXIS sensors need resolution put in the scales */ if (st->snsr[snsr_id].cfg.ch_n_max) { for (ch = 0; ch < st->snsr[snsr_id].cfg.ch_n_max; ch++) { st->snsr[snsr_id].cfg.scales[ch].ival = st->snsr[snsr_id].cfg.resolution.ival; st->snsr[snsr_id].cfg.scales[ch].fval = st->snsr[snsr_id].cfg.resolution.fval; } } if (st->en_msk & (1 << DEV_DMP)) ret = nvi_en(st); return ret; } static int nvi_offset(void *client, int snsr_id, int channel, int offset) { struct nvi_state *st = (struct nvi_state *)client; int old; int ret; if (snsr_id >= DEV_AXIS_N || channel >= AXIS_N) return -EINVAL; old = st->dev_offset[snsr_id][channel]; st->dev_offset[snsr_id][channel] = offset; if (st->en_msk & (1 << snsr_id)) { ret = nvi_en(st); if (ret) { st->dev_offset[snsr_id][channel] = old; return -EINVAL; } } return 0; } static int nvi_thresh_lo(void *client, int snsr_id, int thresh_lo) { struct nvi_state *st = (struct nvi_state *)client; int ret = 1; switch (snsr_id) { case DEV_ACC: return 0; case DEV_SM: st->snsr[DEV_SM].cfg.thresh_lo = thresh_lo; if (st->en_msk & (1 << DEV_DMP)) ret = st->hal->dmp->fn_dev_init(st, snsr_id); return ret; default: return -EINVAL; } return ret; } static int nvi_thresh_hi(void *client, int snsr_id, int thresh_hi) { struct nvi_state *st = (struct nvi_state *)client; int ret = 1; switch (snsr_id) { case DEV_ACC: if (thresh_hi > 0) st->en_msk |= (1 << EN_LP); else st->en_msk &= ~(1 << EN_LP); return 1; case DEV_SM: st->snsr[DEV_SM].cfg.thresh_hi = thresh_hi; if (st->en_msk & (1 << DEV_DMP)) ret = st->hal->dmp->fn_dev_init(st, snsr_id); return ret; default: return -EINVAL; } return ret; } static int nvi_reset_dev(void *client, int snsr_id) { struct nvi_state *st = (struct nvi_state *)client; int ret; ret = nvi_pm(st, __func__, NVI_PM_ON); ret |= nvi_wr_pm1(st, __func__, BIT_H_RESET); nvi_period_all(st); ret |= nvi_en(st); return ret; } static int nvi_self_test(void *client, int snsr_id, char *buf) { struct nvi_state *st = (struct nvi_state *)client; int ret; nvi_pm(st, __func__, NVI_PM_ON); nvi_aux_enable(st, __func__, false, false); nvi_user_ctrl_en(st, __func__, false, false, false, false); if (snsr_id == DEV_ACC) ret = st->hal->fn->st_acc(st); else if (snsr_id == DEV_GYR) ret = st->hal->fn->st_gyr(st); else ret = 0; nvi_aux_enable(st, __func__, true, false); nvi_period_all(st); nvi_en(st); if (ret) return snprintf(buf, PAGE_SIZE, "%d FAIL\n", ret); return snprintf(buf, PAGE_SIZE, "%d PASS\n", ret); } static int nvi_regs(void *client, int snsr_id, char *buf) { struct nvi_state *st = (struct nvi_state *)client; ssize_t t; u8 data; unsigned int i; unsigned int j; int ret; t = snprintf(buf, PAGE_SIZE, "registers: (only data != 0 shown)\n"); for (j = 0; j < st->hal->reg_bank_n; j++) { t += snprintf(buf + t, PAGE_SIZE - t, "bank %u:\n", j); for (i = 0; i < st->hal->regs_n; i++) { if ((j == st->hal->reg->fifo_rw.bank) && (i == st->hal->reg->fifo_rw.reg)) continue; ret = nvi_i2c_r(st, j, i, 1, &data); if (ret) t += snprintf(buf + t, PAGE_SIZE - t, "0x%02x=ERR\n", i); else if (data) t += snprintf(buf + t, PAGE_SIZE - t, "0x%02x=0x%02x\n", i, data); } } return t; } static int nvi_nvs_write(void *client, int snsr_id, unsigned int nvs) { struct nvi_state *st = (struct nvi_state *)client; switch (nvs & 0xFF) { case NVI_INFO_VER: case NVI_INFO_DBG: case NVI_INFO_REG_WR: case NVI_INFO_MEM_RD: case NVI_INFO_MEM_WR: case NVI_INFO_DMP_FW: case NVI_INFO_DMP_EN_MSK: case NVI_INFO_FN_INIT: break; case NVI_INFO_DBG_SPEW: st->sts ^= NVI_DBG_SPEW_MSG; break; case NVI_INFO_AUX_SPEW: st->sts ^= NVI_DBG_SPEW_AUX; nvi_aux_dbg(st, "SNAPSHOT", 0); break; case NVI_INFO_FIFO_SPEW: st->sts ^= NVI_DBG_SPEW_FIFO; break; case NVI_INFO_TS_SPEW: st->sts ^= NVI_DBG_SPEW_TS; break; default: if (nvs < (NVI_INFO_SNSR_SPEW + DEV_N)) st->sts ^= (NVI_DBG_SPEW_SNSR << (nvs - NVI_INFO_SNSR_SPEW)); else return -EINVAL; } st->info = nvs; return 0; } static int nvi_nvs_read(void *client, int snsr_id, char *buf) { struct nvi_state *st = (struct nvi_state *)client; u8 buf_rw[256]; unsigned int n; unsigned int i; unsigned int info; int ret; ssize_t t; info = st->info; st->info = NVI_INFO_VER; switch (info & 0xFF) { case NVI_INFO_VER: t = snprintf(buf, PAGE_SIZE, "NVI driver v. %u\n", NVI_DRIVER_VERSION); if (st->en_msk & (1 << FW_LOADED)) { t += snprintf(buf + t, PAGE_SIZE - t, "DMP FW v. %u\n", st->hal->dmp->fw_ver); t += snprintf(buf + t, PAGE_SIZE - t, "DMP enabled=%u\n", !!(st->en_msk & (1 << DEV_DMP))); } t += snprintf(buf + t, PAGE_SIZE - t, "standby_en=%x\n", !!(st->en_msk & (1 << EN_STDBY))); t += snprintf(buf + t, PAGE_SIZE - t, "bypass_timeout_ms=%u\n", st->bypass_timeout_ms); for (i = 0; i < DEV_N_AUX; i++) { if (st->snsr[i].push_delay_ns) t += snprintf(buf + t, PAGE_SIZE - t, "%s_push_delay_ns=%lld\n", st->snsr[i].cfg.name, st->snsr[i].push_delay_ns); } for (i = 0; i < DEV_N_AUX; i++) { if ((st->dmp_dev_msk | MSK_DEV_MPU_AUX) & (1 << i)) { if (st->dmp_en_msk & (1 << i)) t += snprintf(buf + t, PAGE_SIZE - t, "%s_dmp_en=1\n", st->snsr[i].cfg.name); else t += snprintf(buf + t, PAGE_SIZE - t, "%s_dmp_en=0\n", st->snsr[i].cfg.name); } } return t; case NVI_INFO_DBG: t = snprintf(buf, PAGE_SIZE, "en_msk=%x\n", st->en_msk); t += snprintf(buf + t, PAGE_SIZE - t, "sts=%x\n", st->sts); t += snprintf(buf + t, PAGE_SIZE - t, "pm=%d\n", st->pm); t += snprintf(buf + t, PAGE_SIZE - t, "bm_timeout_us=%u\n", st->bm_timeout_us); t += snprintf(buf + t, PAGE_SIZE - t, "fifo_src=%d\n", st->fifo_src); for (i = 0; i < DEV_N_AUX; i++) { t += snprintf(buf + t, PAGE_SIZE - t, "snsr[%u] %s:\n", i, st->snsr[i].cfg.name); t += snprintf(buf + t, PAGE_SIZE - t, "usr_cfg=%x\n", st->snsr[i].usr_cfg); t += snprintf(buf + t, PAGE_SIZE - t, "enable=%x\n", st->snsr[i].enable); t += snprintf(buf + t, PAGE_SIZE - t, "timeout_us=%u\n", st->snsr[i].timeout_us); t += snprintf(buf + t, PAGE_SIZE - t, "period_us_req=%u\n", st->snsr[i].period_us_req); t += snprintf(buf + t, PAGE_SIZE - t, "period_us_rd=%u\n", st->snsr[i].period_us_rd); t += snprintf(buf + t, PAGE_SIZE - t, "odr=%u\n", st->snsr[i].odr); t += snprintf(buf + t, PAGE_SIZE - t, "ts_last=%lld\n", st->snsr[i].ts_last); t += snprintf(buf + t, PAGE_SIZE - t, "ts_reset=%x\n", st->snsr[i].ts_reset); t += snprintf(buf + t, PAGE_SIZE - t, "flush=%x\n", st->snsr[i].flush); t += snprintf(buf + t, PAGE_SIZE - t, "matrix=%x\n", st->snsr[i].matrix); t += snprintf(buf + t, PAGE_SIZE - t, "buf_shft=%d\n", st->snsr[i].buf_shft); t += snprintf(buf + t, PAGE_SIZE - t, "buf_n=%u\n", st->snsr[i].buf_n); } if (st->hal->dmp) { /* nvi_dmp_clk_n */ st->hal->dmp->fn_clk_n(st, &n); t += snprintf(buf + t, PAGE_SIZE - t, "nvi_dmp_clk_n=%u\n", n); t += snprintf(buf + t, PAGE_SIZE - t, "st->dmp_clk_n=%u\n", st->dmp_clk_n); n = SRC_DMP; } else { n = 0; } for (i = 0; i < SRC_N; i++) { if (i >= st->hal->src_n && i != SRC_DMP) continue; t += snprintf(buf + t, PAGE_SIZE - t, "src[%u]:\n", i); t += snprintf(buf + t, PAGE_SIZE - t, "ts_reset=%x\n", st->src[i].ts_reset); t += snprintf(buf + t, PAGE_SIZE - t, "ts_end=%lld\n", st->src[i].ts_end); t += snprintf(buf + t, PAGE_SIZE - t, "ts_period=%lld\n", st->src[i].ts_period); t += snprintf(buf + t, PAGE_SIZE - t, "period_us_src=%u\n", st->src[i].period_us_src); t += snprintf(buf + t, PAGE_SIZE - t, "period_us_req=%u\n", st->src[i].period_us_req); t += snprintf(buf + t, PAGE_SIZE - t, "period_us_min=%u\n", st->src[i].period_us_min); t += snprintf(buf + t, PAGE_SIZE - t, "period_us_max=%u\n", st->src[i].period_us_max); t += snprintf(buf + t, PAGE_SIZE - t, "fifo_data_n=%u\n", st->src[i].fifo_data_n); t += snprintf(buf + t, PAGE_SIZE - t, "base_t=%u\n", st->src[i].base_t); } return t; case NVI_INFO_DBG_SPEW: return snprintf(buf, PAGE_SIZE, "DBG spew=%x\n", !!(st->sts & NVI_DBG_SPEW_MSG)); case NVI_INFO_AUX_SPEW: return snprintf(buf, PAGE_SIZE, "AUX spew=%x\n", !!(st->sts & NVI_DBG_SPEW_AUX)); case NVI_INFO_FIFO_SPEW: return snprintf(buf, PAGE_SIZE, "FIFO spew=%x\n", !!(st->sts & NVI_DBG_SPEW_FIFO)); case NVI_INFO_TS_SPEW: return snprintf(buf, PAGE_SIZE, "TS spew=%x\n", !!(st->sts & NVI_DBG_SPEW_TS)); case NVI_INFO_REG_WR: st->rc_dis = true; buf_rw[0] = (u8)(info >> 16); buf_rw[1] = (u8)(info >> 8); ret = nvi_i2c_write(st, info >> 24, 2, buf_rw); return snprintf(buf, PAGE_SIZE, "REG WR: b=%02x r=%02x d=%02x ERR=%d\n", info >> 24, buf_rw[0], buf_rw[1], ret); case NVI_INFO_MEM_RD: n = (info >> 8) & 0xFF; if (!n) n = sizeof(buf_rw); ret = nvi_mem_rd(st, info >> 16, n, buf_rw); if (ret) return snprintf(buf, PAGE_SIZE, "MEM RD: ERR=%d\n", ret); t = snprintf(buf, PAGE_SIZE, "MEM RD:\n"); for (i = 0; i < n; i++) { if (!(i % 8)) t += snprintf(buf + t, PAGE_SIZE - t, "%04x: ", (info >> 16) + i); t += snprintf(buf + t, PAGE_SIZE - t, "%02x ", buf_rw[i]); if (!((i + 1) % 8)) t += snprintf(buf + t, PAGE_SIZE - t, "\n"); } t += snprintf(buf + t, PAGE_SIZE - t, "\n"); return t; case NVI_INFO_MEM_WR: st->mc_dis = true; buf_rw[0] = (u8)(info >> 8); ret = nvi_mem_wr(st, info >> 16, 1, buf_rw, true); return snprintf(buf, PAGE_SIZE, "MEM WR: a=%04x d=%02x ERR=%d\n", info >> 16, buf_rw[0], ret); case NVI_INFO_DMP_FW: ret = nvi_dmp_fw(st); return snprintf(buf, PAGE_SIZE, "DMP FW: ERR=%d\n", ret); case NVI_INFO_DMP_EN_MSK: st->dmp_en_msk = (info >> 8) & MSK_DEV_ALL; return snprintf(buf, PAGE_SIZE, "st->dmp_en_msk=%x\n", st->dmp_en_msk); case NVI_INFO_FN_INIT: if (st->hal->fn->init) { ret = st->hal->fn->init(st); return snprintf(buf, PAGE_SIZE, "hal->fn->init() ret=%d\n", ret); } else { return snprintf(buf, PAGE_SIZE, "no hal->fn->init()\n"); } default: i = info - NVI_INFO_SNSR_SPEW; if (i < DEV_N) return snprintf(buf, PAGE_SIZE, "%s spew=%x\n", st->snsr[i].cfg.name, !!(st->sts & (NVI_DBG_SPEW_SNSR << i))); break; } return -EINVAL; } static struct nvs_fn_dev nvi_nvs_fn = { .enable = nvi_enable, .batch = nvi_batch, .batch_read = nvi_batch_read, .flush = nvi_flush, .max_range = nvi_max_range, .offset = nvi_offset, .thresh_lo = nvi_thresh_lo, .thresh_hi = nvi_thresh_hi, .reset = nvi_reset_dev, .self_test = nvi_self_test, .regs = nvi_regs, .nvs_write = nvi_nvs_write, .nvs_read = nvi_nvs_read, }; static int nvi_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct nvi_state *st = i2c_get_clientdata(client); unsigned int i; int ret; int ret_t = 0; s64 ts = 0; /* = 0 to fix compile */ if (st->sts & NVS_STS_SPEW_MSG) ts = nvs_timestamp(); st->sts |= NVS_STS_SUSPEND; if (st->nvs) { for (i = 0; i < DEV_N; i++) ret_t |= st->nvs->suspend(st->snsr[i].nvs_st); } nvi_mutex_lock(st); ret_t |= nvi_en(st); for (i = 0; i < DEV_N; i++) { if (st->snsr[i].enable && (st->snsr[i].cfg.flags & SENSOR_FLAG_WAKE_UP)) { ret = irq_set_irq_wake(st->i2c->irq, 1); if (!ret) { st->irq_set_irq_wake = true; break; } } } if (st->sts & NVS_STS_SPEW_MSG) dev_info(&client->dev, "%s WAKE_ON=%x elapsed_t=%lldns err=%d\n", __func__, st->irq_set_irq_wake, nvs_timestamp() - ts, ret_t); nvi_mutex_unlock(st); return ret_t; } static int nvi_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct nvi_state *st = i2c_get_clientdata(client); s64 ts = 0; /* = 0 to fix compile */ unsigned int i; int ret; if (st->sts & NVS_STS_SPEW_MSG) ts = nvs_timestamp(); nvi_mutex_lock(st); if (st->irq_set_irq_wake) { /* determine if wake source */ ret = nvi_rd_int_status(st); if (ret) { dev_err(&client->dev, "%s IRQ STS ERR=%d\n", __func__, ret); } else { if (st->sts & NVS_STS_SPEW_MSG) dev_info(&client->dev, "%s IRQ STS=%#x DMP=%#x\n", __func__, st->rc.int_status, st->rc.int_dmp); if (st->rc.int_status & (1 << st->hal->bit->int_dmp)) { if (st->rc.int_dmp & (1 << st->hal->bit->dmp_int_sm)) nvi_push_oneshot(st, DEV_SM); } } ret = irq_set_irq_wake(st->i2c->irq, 0); if (!ret) st->irq_set_irq_wake = false; } nvi_mutex_unlock(st); ret = 0; if (st->nvs) { for (i = 0; i < DEV_N; i++) ret |= st->nvs->resume(st->snsr[i].nvs_st); } nvi_mutex_lock(st); for (i = 0; i < AUX_PORT_MAX; i++) { if (st->aux.port[i].nmp.shutdown_bypass) break; } if (i < AUX_PORT_MAX) { nvi_pm(st, __func__, NVI_PM_ON); nvi_aux_bypass_enable(st, false); } st->sts &= ~NVS_STS_SUSPEND; nvi_period_all(st); ret = nvi_en(st); if (st->sts & NVS_STS_SPEW_MSG) dev_info(&client->dev, "%s elapsed_t=%lldns err=%d\n", __func__, nvs_timestamp() - ts, ret); nvi_mutex_unlock(st); return ret; } static const struct dev_pm_ops nvi_pm_ops = { .suspend = nvi_suspend, .resume = nvi_resume, }; static void nvi_shutdown(struct i2c_client *client) { struct nvi_state *st = i2c_get_clientdata(client); unsigned int i; st->sts |= NVS_STS_SHUTDOWN; if (st->nvs) { for (i = 0; i < DEV_N; i++) st->nvs->shutdown(st->snsr[i].nvs_st); } nvi_disable_irq(st); if (st->hal) { nvi_user_ctrl_en(st, __func__, false, false, false, false); nvi_pm(st, __func__, NVI_PM_OFF); } if (st->sts & NVS_STS_SPEW_MSG) dev_info(&client->dev, "%s\n", __func__); } static int nvi_remove(struct i2c_client *client) { struct nvi_state *st = i2c_get_clientdata(client); unsigned int i; if (st != NULL) { if (st->i2c->irq > 0) free_irq(st->i2c->irq, st); nvi_shutdown(client); if (st->nvs) { for (i = 0; i < DEV_N; i++) st->nvs->remove(st->snsr[i].nvs_st); } nvi_pm_exit(st); } dev_info(&client->dev, "%s\n", __func__); return 0; } static struct nvi_id_hal nvi_id_hals[] = { { NVI_HW_ID_AUTO, NVI_NAME, &nvi_hal_6050 }, { NVI_HW_ID_MPU6050, NVI_NAME_MPU6050, &nvi_hal_6050 }, { NVI_HW_ID_MPU6500, NVI_NAME_MPU6500, &nvi_hal_6500 }, { NVI_HW_ID_MPU6515, NVI_NAME_MPU6515, &nvi_hal_6515 }, { NVI_HW_ID_MPU9150, NVI_NAME_MPU9150, &nvi_hal_6050 }, { NVI_HW_ID_MPU9250, NVI_NAME_MPU9250, &nvi_hal_6500 }, { NVI_HW_ID_MPU9350, NVI_NAME_MPU9350, &nvi_hal_6515 }, { NVI_HW_ID_ICM20628, NVI_NAME_ICM20628, &nvi_hal_20628 }, { NVI_HW_ID_ICM20630, NVI_NAME_ICM20630, &nvi_hal_20628 }, { NVI_HW_ID_ICM20632, NVI_NAME_ICM20632, &nvi_hal_20628 }, }; static int nvi_id2hal(struct nvi_state *st, u8 hw_id) { int i; for (i = 1; i < (int)ARRAY_SIZE(nvi_id_hals); i++) { if (nvi_id_hals[i].hw_id == hw_id) { st->hal = nvi_id_hals[i].hal; return i; } } return -ENODEV; } static int nvi_id_dev(struct nvi_state *st, const struct i2c_device_id *i2c_dev_id) { u8 hw_id = NVI_HW_ID_AUTO; unsigned int i = i2c_dev_id->driver_data; unsigned int dev; int src; int ret; BUG_ON(NVI_NDX_N != ARRAY_SIZE(nvi_i2c_device_id) - 1); BUG_ON(NVI_NDX_N != ARRAY_SIZE(nvi_id_hals)); st->hal = nvi_id_hals[i].hal; if (i == NVI_NDX_AUTO) { nvi_pm_wr(st, __func__, 0, 0, 0); ret = nvi_i2c_rd(st, &st->hal->reg->who_am_i, &hw_id); if (ret) { dev_err(&st->i2c->dev, "%s AUTO ID FAILED\n", __func__); return -ENODEV; } ret = nvi_id2hal(st, hw_id); if (ret < 0) { st->hal = &nvi_hal_20628; /* cause a master reset by disabling regulators */ nvs_vregs_disable(&st->i2c->dev, st->vreg, ARRAY_SIZE(nvi_vregs)); ret = nvi_pm_wr(st, __func__, 0, 0, 0); ret = nvi_i2c_rd(st, &st->hal->reg->who_am_i, &hw_id); if (ret) { dev_err(&st->i2c->dev, "%s AUTO ID FAILED\n", __func__); return -ENODEV; } ret = nvi_id2hal(st, hw_id); if (ret < 0) { dev_err(&st->i2c->dev, "%s hw_id=%x AUTO ID FAILED\n", __func__, hw_id); return -ENODEV; } } i = ret; } else { /* cause a master reset by disabling regulators */ nvs_vregs_disable(&st->i2c->dev, st->vreg, ARRAY_SIZE(nvi_vregs)); nvi_pm_wr(st, __func__, 0, 0, 0); } /* populate the rest of st->snsr[dev].cfg */ for (dev = 0; dev < DEV_N; dev++) { st->snsr[dev].cfg.part = nvi_id_hals[i].name; st->snsr[dev].cfg.version = st->hal->dev[dev]->version; st->snsr[dev].cfg.milliamp.ival = st->hal->dev[dev]->milliamp.ival; st->snsr[dev].cfg.milliamp.fval = st->hal->dev[dev]->milliamp.fval; } ret = nvs_vregs_sts(st->vreg, ARRAY_SIZE(nvi_vregs)); if (ret < 0) /* regulators aren't supported so manually do master reset */ nvi_wr_pm1(st, __func__, BIT_H_RESET); for (i = 0; i < AXIS_N; i++) { st->rom_offset[DEV_ACC][i] = (s16)st->rc.accel_offset[i]; st->rom_offset[DEV_GYR][i] = (s16)st->rc.gyro_offset[i]; st->dev_offset[DEV_ACC][i] = 0; st->dev_offset[DEV_GYR][i] = 0; } BUG_ON(SRC_N < st->hal->src_n); if (st->hal->fn->init) ret = st->hal->fn->init(st); else ret = 0; /* set sensor period limits after st->hal->fn->init executes */ for (dev = 0; dev < DEV_N; dev++) { src = st->hal->dev[dev]->src; if (src < 0) continue; if (SENSOR_FLAG_ONE_SHOT_MODE == (st->snsr[dev].cfg.flags & REPORTING_MODE_MASK)) continue; BUG_ON(src >= st->hal->src_n); st->snsr[dev].cfg.delay_us_min = st->src[src].period_us_min; st->snsr[dev].cfg.delay_us_max = st->src[src].period_us_max; } if (hw_id == NVI_HW_ID_AUTO) dev_info(&st->i2c->dev, "%s: USING DEVICE TREE: %s\n", __func__, i2c_dev_id->name); else dev_info(&st->i2c->dev, "%s: FOUND HW ID=0x%X USING: %s\n", __func__, hw_id, st->snsr[0].cfg.part); return ret; } static struct sensor_cfg nvi_cfg_dflt[] = { { .name = "accelerometer", .snsr_id = DEV_ACC, .kbuf_sz = KBUF_SZ, .snsr_data_n = 20, /* 64-bit status for integer */ .ch_n = AXIS_N, .ch_sz = -4, .vendor = NVI_VENDOR, .float_significance = NVS_FLOAT_NANO, .ch_n_max = AXIS_N, .thresh_hi = -1, /* LP */ }, { .name = "gyroscope", .snsr_id = DEV_GYR, .kbuf_sz = KBUF_SZ, .snsr_data_n = 20, /* 64-bit status for integer */ .ch_n = AXIS_N, .ch_sz = -4, .vendor = NVI_VENDOR, .max_range = { .ival = 3, }, .float_significance = NVS_FLOAT_NANO, .ch_n_max = AXIS_N, }, { .name = "gyro_temp", .snsr_id = SENSOR_TYPE_TEMPERATURE, .ch_n = 1, .ch_sz = -2, .vendor = NVI_VENDOR, .flags = SENSOR_FLAG_ON_CHANGE_MODE, .float_significance = NVS_FLOAT_NANO, }, { .name = "significant_motion", .snsr_id = DEV_SM, .ch_n = 1, .ch_sz = 1, .vendor = NVI_VENDOR, .delay_us_min = -1, /* delay_us_max is ignored by NVS since this is a one-shot * sensor so we use it as a third threshold parameter */ .delay_us_max = 200, /* SMD_DELAY2_THLD */ .flags = SENSOR_FLAG_ONE_SHOT_MODE | SENSOR_FLAG_WAKE_UP, .thresh_lo = 1500, /* SMD_MOT_THLD */ .thresh_hi = 600, /* SMD_DELAY_THLD */ }, { .name = "step_detector", .snsr_id = DEV_STP, .ch_n = 1, .ch_sz = 1, .vendor = NVI_VENDOR, .delay_us_min = -1, .flags = SENSOR_FLAG_ONE_SHOT_MODE, }, { .name = "quaternion", .snsr_id = SENSOR_TYPE_ORIENTATION, .kbuf_sz = KBUF_SZ, .ch_n = AXIS_N, .ch_sz = -4, .vendor = NVI_VENDOR, .delay_us_min = 10000, .delay_us_max = 255000, }, { .name = "geomagnetic_rotation_vector", .snsr_id = DEV_GMR, .kbuf_sz = KBUF_SZ, .ch_n = 4, .ch_sz = -4, .vendor = NVI_VENDOR, .delay_us_min = 10000, .delay_us_max = 255000, }, { .name = "gyroscope_uncalibrated", .snsr_id = DEV_GYU, .kbuf_sz = KBUF_SZ, .ch_n = AXIS_N, .ch_sz = -2, .vendor = NVI_VENDOR, .max_range = { .ival = 3, }, .delay_us_min = 10000, .delay_us_max = 255000, .float_significance = NVS_FLOAT_NANO, .ch_n_max = AXIS_N, }, }; /* device tree parameters before HAL initialized */ static int nvi_of_dt_pre(struct nvi_state *st, struct device_node *dn) { u32 tmp; char str[64]; unsigned int i; for (i = 0; i < ARRAY_SIZE(nvi_cfg_dflt); i++) memcpy(&st->snsr[i].cfg, &nvi_cfg_dflt[i], sizeof(st->snsr[i].cfg)); st->snsr[DEV_AUX].cfg.name = "auxiliary"; st->en_msk = (1 << EN_STDBY); st->bypass_timeout_ms = NVI_BYPASS_TIMEOUT_MS; if (!dn) return -EINVAL; /* driver specific parameters */ if (!of_property_read_u32(dn, "standby_en", &tmp)) { if (tmp) st->en_msk |= (1 << EN_STDBY); else st->en_msk &= ~(1 << EN_STDBY); } of_property_read_u32(dn, "bypass_timeout_ms", &st->bypass_timeout_ms); for (i = 0; i < DEV_N_AUX; i++) { snprintf(str, sizeof(str), "%s_push_delay_ns", st->snsr[i].cfg.name); if (!of_property_read_u32(dn, str, &tmp)) st->snsr[i].push_delay_ns = (s64)tmp; } return 0; } /* device tree parameters after HAL initialized */ static void nvi_of_dt_post(struct nvi_state *st, struct device_node *dn) { u32 tmp; char str[64]; unsigned int msk; unsigned int i; unsigned int j; /* sensor specific parameters */ for (i = 0; i < DEV_N; i++) nvs_of_dt(dn, &st->snsr[i].cfg, NULL); /* nvs_of_dt doesn't allow REPORTING_MODE_MASK bits to change so we * allow for it here so that we can configure whether batch sysfs nodes * are populated to be used to configure significant motion parameters * in realtime. */ if (!of_property_read_u32(dn, "significant_motion_flags", &tmp)) { msk = SENSOR_FLAG_READONLY_MASK & ~REPORTING_MODE_MASK; tmp &= ~msk; st->snsr[DEV_SM].cfg.flags &= msk; st->snsr[DEV_SM].cfg.flags |= tmp; } for (i = 0; i < DEV_N; i++) { tmp = 0; for (j = 0; j < 9; j++) tmp |= st->snsr[i].cfg.matrix[j]; if (tmp) { /* sensor has a matrix */ snprintf(str, sizeof(str), "%s_matrix_enable", st->snsr[i].cfg.name); if (!of_property_read_u32(dn, str, &tmp)) { /* matrix override */ if (tmp) /* apply matrix within kernel */ st->snsr[i].matrix = true; else /* HAL/fusion will handle matrix */ st->snsr[i].matrix = false; } } } /* sensor overrides that enable the DMP. * If the sensor is specific to the DMP and this override is * disable, then the virtual sensor is removed. */ if (st->hal->dmp) { st->dmp_dev_msk = st->hal->dmp->dev_msk; st->dmp_en_msk = st->hal->dmp->en_msk; for (i = 0; i < DEV_N_AUX; i++) { snprintf(str, sizeof(str), "%s_dmp_en", st->snsr[i].cfg.name); if (!of_property_read_u32(dn, str, &tmp)) { if (tmp) { msk = 1 << i; if (MSK_DEV_DMP & msk) st->dmp_dev_msk |= msk; st->dmp_en_msk |= msk; } else { msk = ~(1 << i); if (MSK_DEV_DMP & (1 << i)) st->dmp_dev_msk &= msk; st->dmp_en_msk &= msk; } } } } } static int nvi_init(struct nvi_state *st, const struct i2c_device_id *i2c_dev_id) { struct mpu_platform_data *pdata; signed char matrix[9]; unsigned int i; unsigned int n; int ret; nvi_of_dt_pre(st, st->i2c->dev.of_node); nvi_pm_init(st); ret = nvi_id_dev(st, i2c_dev_id); if (ret) return ret; st->sts |= NVI_STS_PART_ID_VALID; if (st->i2c->dev.of_node) { nvi_of_dt_post(st, st->i2c->dev.of_node); } else { pdata = dev_get_platdata(&st->i2c->dev); if (pdata) { memcpy(&st->snsr[DEV_ACC].cfg.matrix, &pdata->orientation, sizeof(st->snsr[DEV_ACC].cfg.matrix)); memcpy(&st->snsr[DEV_GYR].cfg.matrix, &pdata->orientation, sizeof(st->snsr[DEV_GYR].cfg.matrix)); } else { dev_err(&st->i2c->dev, "%s dev_get_platdata ERR\n", __func__); return -ENODEV; } } if (st->en_msk & (1 << FW_LOADED)) ret = 0; else ret = nvi_dmp_fw(st); if (ret) { /* remove DMP dependent sensors */ n = MSK_DEV_DMP; } else { dev_info(&st->i2c->dev, "%s DMP FW loaded\n", __func__); /* remove DMP dependent sensors not supported by this DMP */ n = MSK_DEV_DMP ^ st->dmp_dev_msk; } if (n) { for (i = 0; i < DEV_N; i++) { if (n & (1 << i)) st->snsr[i].cfg.snsr_id = -1; } } nvi_nvs_fn.sts = &st->sts; nvi_nvs_fn.errs = &st->errs; st->nvs = nvs_auto(NVS_CFG_KIF); if (st->nvs == NULL) return -ENODEV; n = 0; for (i = 0; i < DEV_N; i++) { if (st->snsr[i].matrix) { /* matrix handled at kernel so remove from NVS */ memcpy(matrix, st->snsr[i].cfg.matrix, sizeof(matrix)); memset(st->snsr[i].cfg.matrix, 0, sizeof(st->snsr[i].cfg.matrix)); } ret = st->nvs->probe(&st->snsr[i].nvs_st, st, &st->i2c->dev, &nvi_nvs_fn, &st->snsr[i].cfg); if (!ret) { st->snsr[i].cfg.snsr_id = i; if (st->snsr[i].matrix) memcpy(st->snsr[i].cfg.matrix, matrix, sizeof(st->snsr[i].cfg.matrix)); nvi_max_range(st, i, st->snsr[i].cfg.max_range.ival); n++; } } if (!n) return -ENODEV; /* restore SENSOR_FLAG_ONE_SHOT_MODE for significant motion in case it * was cleared to allow realtime calibration with batch. */ st->snsr[DEV_SM].cfg.flags &= ~REPORTING_MODE_MASK; st->snsr[DEV_SM].cfg.flags |= SENSOR_FLAG_ONE_SHOT_MODE; ret = request_threaded_irq(st->i2c->irq, nvi_handler, nvi_thread, IRQF_TRIGGER_RISING, NVI_NAME, st); if (ret) { dev_err(&st->i2c->dev, "%s req_threaded_irq ERR %d\n", __func__, ret); return -ENOMEM; } nvi_pm(st, __func__, NVI_PM_AUTO); nvi_rc_clr(st, __func__); st->rc_dis = false; /* enable register cache after initialization */ nvi_state_local = st; return 0; } static void nvi_dmp_fw_load_worker(struct work_struct *work) { struct nvi_pdata *pd = container_of(work, struct nvi_pdata, fw_load_work); struct nvi_state *st = &pd->st; int ret; ret = nvi_init(st, pd->i2c_dev_id); if (ret) { dev_err(&st->i2c->dev, "%s ERR %d\n", __func__, ret); nvi_remove(st->i2c); } dev_info(&st->i2c->dev, "%s done\n", __func__); #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) device_resource_registered(); #else device_unblock_probing(); #endif } static int nvi_probe(struct i2c_client *client, const struct i2c_device_id *i2c_dev_id) { struct nvi_pdata *pd; struct nvi_state *st; int ret; dev_info(&client->dev, "%s %s\n", __func__, i2c_dev_id->name); if (!client->irq) { dev_err(&client->dev, "%s ERR: no interrupt\n", __func__); return -ENODEV; } /* just test if global disable */ ret = nvs_of_dt(client->dev.of_node, NULL, NULL); if (ret == -ENODEV) { dev_info(&client->dev, "%s DT disabled\n", __func__); return -ENODEV; } pd = devm_kzalloc(&client->dev, sizeof(*pd), GFP_KERNEL); if (pd == NULL) return -ENOMEM; st = &pd->st; i2c_set_clientdata(client, pd); st->rc_dis = true; /* disable register cache during initialization */ st->i2c = client; pd->i2c_dev_id = i2c_dev_id; /* Init fw load worker thread */ INIT_WORK(&pd->fw_load_work, nvi_dmp_fw_load_worker); schedule_work(&pd->fw_load_work); return 0; } MODULE_DEVICE_TABLE(i2c, nvi_i2c_device_id); static const struct of_device_id nvi_of_match[] = { { .compatible = "invensense,mpu6xxx", }, { .compatible = "invensense,mpu6050", }, { .compatible = "invensense,mpu6500", }, { .compatible = "invensense,mpu6515", }, { .compatible = "invensense,mpu9150", }, { .compatible = "invensense,mpu9250", }, { .compatible = "invensense,mpu9350", }, { .compatible = "invensense,icm20628", }, { .compatible = "invensense,icm20630", }, { .compatible = "invensense,icm20632", }, {} }; MODULE_DEVICE_TABLE(of, nvi_of_match); static struct i2c_driver nvi_i2c_driver = { .class = I2C_CLASS_HWMON, .probe = nvi_probe, .remove = nvi_remove, .shutdown = nvi_shutdown, .driver = { .name = NVI_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(nvi_of_match), .pm = &nvi_pm_ops, }, .id_table = nvi_i2c_device_id, }; module_i2c_driver(nvi_i2c_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("NVidiaInvensense driver"); MODULE_AUTHOR("NVIDIA Corporation");