923 lines
21 KiB
C
923 lines
21 KiB
C
/* Copyright (c) 2015 - 2017, 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.
|
|
*/
|
|
|
|
/* The NVS = NVidia Sensor framework */
|
|
/* See nvs_iio.c and nvs.h for documentation */
|
|
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/err.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/of.h>
|
|
#include <linux/nvs.h>
|
|
|
|
#define STM_NAME "ais328dq"
|
|
#define STM_VENDOR "STMicroelectronics"
|
|
#define STM_VERSION (1)
|
|
#define STM_KBUF_SIZE (32)
|
|
#define STM_DELAY_US_MAX (255000)
|
|
#define STM_HW_DELAY_POR_MS (50)
|
|
#define STM_HW_DELAY_US (100)
|
|
#define STM_POLL_DELAY_MS_DFLT (200)
|
|
#define STM_ERR_CNT_MAX (20)
|
|
/* HW registers */
|
|
#define STM_REG_WHO_AM_I (0x0F)
|
|
#define STM_REG_WHO_AM_I_ID (0x32)
|
|
#define STM_REG_CTRL1 (0x20)
|
|
#define STM_REG_CTRL1_XEN (0)
|
|
#define STM_REG_CTRL1_YEN (1)
|
|
#define STM_REG_CTRL1_ZEN (2)
|
|
#define STM_REG_CTRL1_DR (3)
|
|
#define STM_REG_CTRL1_PM (5)
|
|
#define STM_REG_CTRL2 (0x21)
|
|
#define STM_REG_CTRL2_HPCF (0)
|
|
#define STM_REG_CTRL2_HPEN1 (2)
|
|
#define STM_REG_CTRL2_HPEN2 (3)
|
|
#define STM_REG_CTRL2_FDS (4)
|
|
#define STM_REG_CTRL2_HPM (5)
|
|
#define STM_REG_CTRL2_BOOT (7)
|
|
#define STM_REG_CTRL3 (0x22)
|
|
#define STM_REG_CTRL3_I1_CFG (0)
|
|
#define STM_REG_CTRL3_LIR1 (2)
|
|
#define STM_REG_CTRL3_I2_CFG (3)
|
|
#define STM_REG_CTRL3_LIR2 (5)
|
|
#define STM_REG_CTRL3_PP_OD (6)
|
|
#define STM_REG_CTRL3_IHL (7)
|
|
#define STM_REG_CTRL4 (0x23)
|
|
#define STM_REG_CTRL4_SIM (0)
|
|
#define STM_REG_CTRL4_ST (1)
|
|
#define STM_REG_CTRL4_ST_SIGN (3)
|
|
#define STM_REG_CTRL4_FS (4)
|
|
#define STM_REG_CTRL4_FS_MASK (0x30)
|
|
#define STM_REG_CTRL4_BLE (6)
|
|
#define STM_REG_CTRL4_BDU (7)
|
|
#define STM_REG_CTRL5 (0x24)
|
|
#define STM_REG_CTRL5_TURNON0 (0)
|
|
#define STM_REG_CTRL5_TURNON1 (1)
|
|
#define STM_REG_HP_FILTER_RESET (0x25)
|
|
#define STM_REG_REFERENCE (0x26)
|
|
#define STM_REG_STATUS (0x27)
|
|
#define STM_REG_STATUS_XDA (0)
|
|
#define STM_REG_STATUS_YDA (1)
|
|
#define STM_REG_STATUS_ZDA (2)
|
|
#define STM_REG_STATUS_ZYXDA (3)
|
|
#define STM_REG_STATUS_DA_MASK (0x0F)
|
|
#define STM_REG_STATUS_XOR (4)
|
|
#define STM_REG_STATUS_YOR (5)
|
|
#define STM_REG_STATUS_ZOR (6)
|
|
#define STM_REG_STATUS_ZYXOR (7)
|
|
#define STM_REG_OUT_X_L (0x28)
|
|
#define STM_REG_OUT_X_H (0x29)
|
|
#define STM_REG_OUT_Y_L (0x2A)
|
|
#define STM_REG_OUT_Y_H (0x2B)
|
|
#define STM_REG_OUT_Z_L (0x2C)
|
|
#define STM_REG_OUT_Z_H (0x2D)
|
|
#define STM_REG_INT1_CFG (0x30)
|
|
#define STM_REG_INT1_SRC (0x31)
|
|
#define STM_REG_INT1_THS (0x32)
|
|
#define STM_REG_INT1_DURATION (0x33)
|
|
#define STM_REG_INT2_CFG (0x34)
|
|
#define STM_REG_INT2_SRC (0x35)
|
|
#define STM_REG_INT2_THS (0x36)
|
|
#define STM_REG_INT2_DURATION (0x37)
|
|
|
|
#define STM_REG_INT_CFG_XLIE (0)
|
|
#define STM_REG_INT_CFG_XHIE (1)
|
|
#define STM_REG_INT_CFG_YLIE (2)
|
|
#define STM_REG_INT_CFG_YHIE (3)
|
|
#define STM_REG_INT_CFG_ZLIE (4)
|
|
#define STM_REG_INT_CFG_ZHIE (5)
|
|
#define STM_REG_INT_CFG_6D (6)
|
|
#define STM_REG_INT_CFG_AOI (7)
|
|
#define STM_REG_INT_SRC_XL (0)
|
|
#define STM_REG_INT_SRC_XH (1)
|
|
#define STM_REG_INT_SRC_YL (2)
|
|
#define STM_REG_INT_SRC_YH (3)
|
|
#define STM_REG_INT_SRC_ZL (4)
|
|
#define STM_REG_INT_SRC_ZH (5)
|
|
#define STM_REG_INT_SRC_IA (6)
|
|
|
|
#define STM_I2C_AUTO_INC_AD (0x80)
|
|
|
|
#define AXIS_X (0)
|
|
#define AXIS_Y (1)
|
|
#define AXIS_Z (2)
|
|
#define AXIS_N (3)
|
|
|
|
#define STM_ODR_OVERRIDE_CELLS (3)
|
|
|
|
/* regulator names in order of powering on */
|
|
static char *stm_vregs[] = {
|
|
"vdd",
|
|
"vdd_IO",
|
|
};
|
|
|
|
static struct sensor_cfg stm_cfg_dflt = {
|
|
.name = "accelerometer",
|
|
.kbuf_sz = STM_KBUF_SIZE,
|
|
.ch_n = AXIS_N,
|
|
.ch_sz = -2,
|
|
.part = STM_NAME,
|
|
.vendor = STM_VENDOR,
|
|
.version = STM_VERSION,
|
|
/* milliamp is dynamic based on delay */
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 400000000,
|
|
},
|
|
.delay_us_min = 1000,
|
|
.delay_us_max = 2000000,
|
|
/* default matrix to get the attribute */
|
|
.matrix[0] = 1,
|
|
.matrix[4] = 1,
|
|
.matrix[8] = 1,
|
|
.float_significance = NVS_FLOAT_NANO
|
|
};
|
|
|
|
static unsigned short stm_i2c_addrs[] = {
|
|
0x18,
|
|
0x19,
|
|
};
|
|
|
|
struct stm_state {
|
|
struct i2c_client *i2c;
|
|
struct nvs_fn_if *nvs;
|
|
void *nvs_st;
|
|
struct sensor_cfg cfg;
|
|
struct regulator_bulk_data vreg[ARRAY_SIZE(stm_vregs)];
|
|
struct workqueue_struct *stm_work_queue;
|
|
struct work_struct dw;
|
|
u32* odr_override_tbl; /* Sampling freq override table */
|
|
unsigned int sts; /* status flags */
|
|
unsigned int errs; /* error count */
|
|
unsigned int enabled; /* enable status */
|
|
unsigned int delay_us; /* requested sampling delay (us) */
|
|
u16 i2c_addr; /* I2C address */
|
|
bool irq_dis; /* interrupt host disable flag */
|
|
|
|
u8 ru_ctrl2; /* register user CTRL_REG2 */
|
|
u8 ru_ctrl3; /* register user CTRL_REG3 */
|
|
u8 ru_ctrl4; /* register user CTRL_REG3 */
|
|
u8 ru_ctrl5; /* register user CTRL_REG3 */
|
|
u8 buf[7]; /* data buffer + status */
|
|
};
|
|
|
|
struct stm_rr {
|
|
struct nvs_float max_range;
|
|
struct nvs_float resolution;
|
|
struct nvs_float scale;
|
|
};
|
|
|
|
static struct stm_rr stm_rr_tbl[] = {
|
|
/* all accelerometer values are in g's fval = NVS_FLOAT_NANO */
|
|
{
|
|
.max_range = {
|
|
.ival = 19,
|
|
.fval = 613300000,
|
|
},
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 598550,
|
|
},
|
|
.scale = {
|
|
.ival = 0,
|
|
.fval = 598550,
|
|
},
|
|
},
|
|
{
|
|
.max_range = {
|
|
.ival = 39,
|
|
.fval = 226600000,
|
|
},
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 1197101,
|
|
},
|
|
.scale = {
|
|
.ival = 0,
|
|
.fval = 1197101,
|
|
},
|
|
},
|
|
{
|
|
.max_range = {
|
|
.ival = 78,
|
|
.fval = 453200000,
|
|
},
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 2394202,
|
|
},
|
|
.scale = {
|
|
.ival = 0,
|
|
.fval = 2394202,
|
|
},
|
|
},
|
|
{
|
|
.max_range = {
|
|
.ival = 78,
|
|
.fval = 453200000,
|
|
},
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 2394202,
|
|
},
|
|
.scale = {
|
|
.ival = 0,
|
|
.fval = 2394202,
|
|
},
|
|
},
|
|
};
|
|
|
|
struct stm_odr {
|
|
unsigned int us;
|
|
u8 hw;
|
|
};
|
|
|
|
|
|
static struct stm_odr stm_odr_tbl[] = {
|
|
{ 2000000, 0x58, },
|
|
{ 1000000, 0x78, },
|
|
{ 500000, 0x98, },
|
|
{ 200000, 0xB8, },
|
|
{ 100000, 0xD8, },
|
|
{ 20000, 0x20, },
|
|
{ 10000, 0x28, },
|
|
{ 2500, 0x30, },
|
|
{ 1000, 0x38, },
|
|
};
|
|
|
|
|
|
static void stm_err(struct stm_state *st)
|
|
{
|
|
st->errs++;
|
|
if (!st->errs)
|
|
st->errs--;
|
|
}
|
|
|
|
static int stm_i2c_rd(struct stm_state *st, u8 reg, u16 len, u8 *val)
|
|
{
|
|
struct i2c_msg msg[2];
|
|
|
|
if (len > 1)
|
|
reg |= STM_I2C_AUTO_INC_AD;
|
|
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 = val;
|
|
if (i2c_transfer(st->i2c->adapter, msg, 2) != 2) {
|
|
stm_err(st);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm_i2c_wr(struct stm_state *st, u8 reg, u8 val)
|
|
{
|
|
struct i2c_msg msg;
|
|
u8 buf[2];
|
|
|
|
if (st->i2c_addr) {
|
|
buf[0] = reg;
|
|
buf[1] = val;
|
|
msg.addr = st->i2c_addr;
|
|
msg.flags = 0;
|
|
msg.len = 2;
|
|
msg.buf = buf;
|
|
if (i2c_transfer(st->i2c->adapter, &msg, 1) != 1) {
|
|
stm_err(st);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int stm_cmd(struct stm_state *st, int enable)
|
|
{
|
|
u8 ctrl1 = enable;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
if (enable) {
|
|
for (i = 0; i < ARRAY_SIZE(stm_odr_tbl) - 1; i++) {
|
|
if (st->delay_us >= stm_odr_tbl[i].us)
|
|
break;
|
|
}
|
|
ctrl1 |= stm_odr_tbl[i].hw;
|
|
ret |= stm_i2c_wr(st, STM_REG_CTRL2, st->ru_ctrl2);
|
|
ret |= stm_i2c_wr(st, STM_REG_CTRL3, st->ru_ctrl3);
|
|
ret |= stm_i2c_wr(st, STM_REG_CTRL4, st->ru_ctrl4);
|
|
ret |= stm_i2c_wr(st, STM_REG_CTRL5, st->ru_ctrl5);
|
|
}
|
|
ret |= stm_i2c_wr(st, STM_REG_CTRL1, ctrl1);
|
|
return ret;
|
|
}
|
|
|
|
static int stm_pm(struct stm_state *st, bool enable)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (enable) {
|
|
ret = nvs_vregs_enable(&st->i2c->dev, st->vreg,
|
|
ARRAY_SIZE(stm_vregs));
|
|
if (ret > 0)
|
|
mdelay(STM_HW_DELAY_POR_MS);
|
|
} else {
|
|
if (st->i2c->irq) {
|
|
ret = nvs_vregs_sts(st->vreg, ARRAY_SIZE(stm_vregs));
|
|
if ((ret < 0) || (ret == ARRAY_SIZE(stm_vregs))) {
|
|
ret = stm_i2c_wr(st, STM_REG_CTRL1, 0);
|
|
} else if (ret > 0) {
|
|
ret = nvs_vregs_enable(&st->i2c->dev, st->vreg,
|
|
ARRAY_SIZE(stm_vregs));
|
|
mdelay(STM_HW_DELAY_POR_MS);
|
|
ret = stm_i2c_wr(st, STM_REG_CTRL1, 0);
|
|
}
|
|
}
|
|
ret |= nvs_vregs_disable(&st->i2c->dev, st->vreg,
|
|
ARRAY_SIZE(stm_vregs));
|
|
}
|
|
if (ret > 0)
|
|
ret = 0;
|
|
if (ret) {
|
|
dev_err(&st->i2c->dev, "%s pwr=%x ERR=%d\n",
|
|
__func__, enable, ret);
|
|
} else {
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&st->i2c->dev, "%s pwr=%x\n",
|
|
__func__, enable);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void stm_pm_exit(struct stm_state *st)
|
|
{
|
|
stm_pm(st, false);
|
|
nvs_vregs_exit(&st->i2c->dev, st->vreg, ARRAY_SIZE(stm_vregs));
|
|
}
|
|
|
|
static int stm_pm_init(struct stm_state *st)
|
|
{
|
|
int ret;
|
|
|
|
st->delay_us = STM_DELAY_US_MAX;
|
|
nvs_vregs_init(&st->i2c->dev,
|
|
st->vreg, ARRAY_SIZE(stm_vregs), stm_vregs);
|
|
ret = stm_pm(st, true);
|
|
return ret;
|
|
}
|
|
|
|
static int stm_rd(struct stm_state *st, s64 ts)
|
|
{
|
|
int ret;
|
|
|
|
ret = stm_i2c_rd(st, STM_REG_STATUS, sizeof(st->buf), st->buf);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (st->buf[0] & STM_REG_STATUS_DA_MASK) {
|
|
st->nvs->handler(st->nvs_st, &st->buf[1], ts);
|
|
} else {
|
|
ret = -EAGAIN;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void stm_read(struct stm_state *st, s64 ts)
|
|
{
|
|
st->nvs->nvs_mutex_lock(st->nvs_st);
|
|
if (st->enabled)
|
|
stm_rd(st, ts);
|
|
st->nvs->nvs_mutex_unlock(st->nvs_st);
|
|
}
|
|
|
|
static void stm_work(struct work_struct *ws)
|
|
{
|
|
struct stm_state *st = container_of((struct work_struct *)ws,
|
|
struct stm_state, dw);
|
|
s64 ts1=0, ts2=0;
|
|
unsigned long delay_value = 0;
|
|
u64 ts_diff = 0;
|
|
|
|
usleep_range(st->delay_us, st->delay_us);
|
|
while (st->enabled) {
|
|
ts1 = nvs_timestamp();
|
|
stm_read(st, ts1);
|
|
ts2 = nvs_timestamp();
|
|
ts_diff = (ts2 - ts1)/1000;
|
|
if (st->delay_us > (unsigned int)ts_diff) {
|
|
delay_value = st->delay_us - (unsigned int)ts_diff;
|
|
usleep_range(delay_value, delay_value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static irqreturn_t stm_irq_thread(int irq, void *dev_id)
|
|
{
|
|
s64 ts = nvs_timestamp();
|
|
struct stm_state *st = (struct stm_state *)dev_id;
|
|
|
|
if (st->sts & NVS_STS_SPEW_IRQ)
|
|
dev_info(&st->i2c->dev, "%s\n", __func__);
|
|
stm_read(st, ts);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static void stm_disable_irq(struct stm_state *st)
|
|
{
|
|
if (!st->irq_dis) {
|
|
disable_irq_nosync(st->i2c->irq);
|
|
st->irq_dis = true;
|
|
}
|
|
}
|
|
|
|
static void stm_enable_irq(struct stm_state *st)
|
|
{
|
|
if (st->irq_dis) {
|
|
enable_irq(st->i2c->irq);
|
|
st->irq_dis = false;
|
|
}
|
|
}
|
|
|
|
static int stm_dis(struct stm_state *st)
|
|
{
|
|
if (st->i2c->irq)
|
|
stm_disable_irq(st);
|
|
st->enabled = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int stm_disable(struct stm_state *st)
|
|
{
|
|
int ret;
|
|
|
|
ret = stm_dis(st);
|
|
if (!ret)
|
|
stm_pm(st, false);
|
|
return ret;
|
|
}
|
|
|
|
static int stm_enable(void *client, int snsr_id, int enable)
|
|
{
|
|
struct stm_state *st = (struct stm_state *)client;
|
|
int ret;
|
|
|
|
if (enable < 0)
|
|
/* just return enable status */
|
|
return st->enabled;
|
|
|
|
if (enable) {
|
|
ret = stm_pm(st, true);
|
|
if (!ret) {
|
|
ret = stm_cmd(st, enable);
|
|
if (ret) {
|
|
stm_disable(st);
|
|
} else {
|
|
st->enabled = enable;
|
|
if (st->i2c->irq)
|
|
stm_enable_irq(st);
|
|
else
|
|
queue_work(st->stm_work_queue, &st->dw);
|
|
}
|
|
}
|
|
} else {
|
|
ret = stm_disable(st);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int stm_batch(void *client, int snsr_id, int flags,
|
|
unsigned int period_us, unsigned int timeout_us)
|
|
{
|
|
struct stm_state *st = (struct stm_state *)client;
|
|
int ret = 0;
|
|
|
|
if (timeout_us)
|
|
/* timeout not supported (no HW FIFO) */
|
|
return -EINVAL;
|
|
|
|
if (period_us < st->cfg.delay_us_min)
|
|
period_us = st->cfg.delay_us_min;
|
|
if (period_us != st->delay_us) {
|
|
st->delay_us = period_us;
|
|
if (st->enabled)
|
|
ret = stm_cmd(st, st->enabled);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int stm_max_range(void *client, int snsr_id, int max_range)
|
|
{
|
|
struct stm_state *st = (struct stm_state *)client;
|
|
|
|
if (max_range < 0 || max_range >= ARRAY_SIZE(stm_rr_tbl))
|
|
return -EINVAL;
|
|
|
|
st->cfg.max_range.ival = stm_rr_tbl[max_range].max_range.ival;
|
|
st->cfg.max_range.fval = stm_rr_tbl[max_range].max_range.fval;
|
|
st->cfg.resolution.ival = stm_rr_tbl[max_range].resolution.ival;
|
|
st->cfg.resolution.fval = stm_rr_tbl[max_range].resolution.fval;
|
|
st->cfg.scale.ival = stm_rr_tbl[max_range].scale.ival;
|
|
st->cfg.scale.fval = stm_rr_tbl[max_range].scale.fval;
|
|
if (max_range == 2)
|
|
max_range = 3;
|
|
st->ru_ctrl4 &= ~STM_REG_CTRL4_FS_MASK;
|
|
st->ru_ctrl4 |= max_range << STM_REG_CTRL4_FS;
|
|
if (st->enabled)
|
|
stm_enable(st, snsr_id, st->enabled);
|
|
return 0;
|
|
}
|
|
|
|
static int stm_reset(void *client, int snsr_id)
|
|
{
|
|
struct stm_state *st = (struct stm_state *)client;
|
|
unsigned int enabled = st->enabled;
|
|
int ret;
|
|
|
|
stm_dis(st);
|
|
stm_pm(st, true);
|
|
ret = stm_i2c_wr(st, STM_REG_CTRL2, 1 << STM_REG_CTRL2_BOOT);
|
|
stm_enable(st, snsr_id, enabled);
|
|
return ret;
|
|
}
|
|
|
|
static int stm_selftest(void *client, int snsr_id, char *buf)
|
|
{
|
|
struct stm_state *st = (struct stm_state *)client;
|
|
unsigned int enabled = st->enabled;
|
|
ssize_t t;
|
|
int ret;
|
|
|
|
stm_dis(st);
|
|
/* set self-test bit when enabled */
|
|
st->ru_ctrl4 |= (1 << STM_REG_CTRL4_ST);
|
|
/* enable */
|
|
stm_enable(st, 0, 7);
|
|
/* need to put a data acquire delay loop here */
|
|
ret = stm_rd(st, nvs_timestamp());
|
|
/* disable */
|
|
stm_dis(st);
|
|
st->ru_ctrl4 &= ~(1 << STM_REG_CTRL4_ST);
|
|
if (buf) {
|
|
if (ret < 0) {
|
|
t = sprintf(buf, "ERR: %d\n", ret);
|
|
} else {
|
|
if (ret > 0)
|
|
t = sprintf(buf, "%d FAIL", ret);
|
|
else
|
|
t = sprintf(buf, "%d PASS", ret);
|
|
t += sprintf(buf + t, " xyz: %hd %hd %hd\n",
|
|
st->buf[1],
|
|
st->buf[3],
|
|
st->buf[5]);
|
|
}
|
|
}
|
|
/* restore */
|
|
stm_enable(st, 0, enabled);
|
|
if (buf)
|
|
return t;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stm_regs(void *client, int snsr_id, char *buf)
|
|
{
|
|
struct stm_state *st = (struct stm_state *)client;
|
|
ssize_t t;
|
|
u8 val[2];
|
|
u8 i;
|
|
int ret;
|
|
|
|
t = sprintf(buf, "registers:\n");
|
|
ret = stm_i2c_rd(st, STM_REG_WHO_AM_I, 1, val);
|
|
if (ret)
|
|
t += sprintf(buf + t, "0x%hhx=ERR\n", STM_REG_WHO_AM_I);
|
|
else
|
|
t += sprintf(buf + t, "0x%hhx=0x%hhx\n",
|
|
STM_REG_WHO_AM_I, val[0]);
|
|
for (i = STM_REG_CTRL1; i < STM_REG_HP_FILTER_RESET; i++) {
|
|
ret = stm_i2c_rd(st, i, 1, val);
|
|
if (ret)
|
|
t += sprintf(buf + t, "0x%hhx=ERR\n", i);
|
|
else
|
|
t += sprintf(buf + t, "0x%hhx=0x%hhx\n", i, val[0]);
|
|
}
|
|
for (i = STM_REG_REFERENCE; i <= STM_REG_STATUS; i++) {
|
|
ret = stm_i2c_rd(st, i, 1, val);
|
|
if (ret)
|
|
t += sprintf(buf + t, "0x%hhx=ERR\n", i);
|
|
else
|
|
t += sprintf(buf + t, "0x%hhx=0x%hhx\n", i, val[0]);
|
|
}
|
|
for (i = STM_REG_OUT_X_L; i < STM_REG_OUT_Z_H; i += 2) {
|
|
ret = stm_i2c_rd(st, i, 2, val);
|
|
if (ret)
|
|
t += sprintf(buf + t, "0x%hhx:0x%hhx=ERR\n",
|
|
i, i + 1);
|
|
else
|
|
t += sprintf(buf + t, "0x%hhx:0x%hhx=0x%hx\n",
|
|
i, i + 1, *((u16 *)val));
|
|
}
|
|
for (i = STM_REG_INT1_CFG; i <= STM_REG_INT2_DURATION; i++) {
|
|
ret = stm_i2c_rd(st, i, 1, val);
|
|
if (ret)
|
|
t += sprintf(buf + t, "0x%hhx=ERR\n", i);
|
|
else
|
|
t += sprintf(buf + t, "0x%hhx=0x%hhx\n", i, val[0]);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
|
|
static struct nvs_fn_dev stm_fn_dev = {
|
|
.enable = stm_enable,
|
|
.batch = stm_batch,
|
|
.max_range = stm_max_range,
|
|
.reset = stm_reset,
|
|
.self_test = stm_selftest,
|
|
.regs = stm_regs,
|
|
};
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int stm_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct stm_state *st = i2c_get_clientdata(client);
|
|
int ret = 0;
|
|
|
|
st->sts |= NVS_STS_SUSPEND;
|
|
if (st->nvs && st->nvs_st)
|
|
ret = st->nvs->suspend(st->nvs_st);
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
static int stm_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct stm_state *st = i2c_get_clientdata(client);
|
|
int ret = 0;
|
|
|
|
if (st->nvs && st->nvs_st)
|
|
ret = st->nvs->resume(st->nvs_st);
|
|
st->sts &= ~NVS_STS_SUSPEND;
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_PM_SLEEP */
|
|
static SIMPLE_DEV_PM_OPS(stm_pm_ops, stm_suspend, stm_resume);
|
|
|
|
static void stm_shutdown(struct i2c_client *client)
|
|
{
|
|
struct stm_state *st = i2c_get_clientdata(client);
|
|
|
|
st->sts |= NVS_STS_SHUTDOWN;
|
|
if (st->nvs && st->nvs_st)
|
|
st->nvs->shutdown(st->nvs_st);
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
}
|
|
|
|
static int stm_remove(struct i2c_client *client)
|
|
{
|
|
struct stm_state *st = i2c_get_clientdata(client);
|
|
|
|
if (st != NULL) {
|
|
stm_shutdown(client);
|
|
if (st->nvs && st->nvs_st)
|
|
st->nvs->remove(st->nvs_st);
|
|
stm_pm_exit(st);
|
|
destroy_workqueue(st->stm_work_queue);
|
|
}
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int stm_id_dev(struct stm_state *st, const char *name)
|
|
{
|
|
u8 val;
|
|
int ret = 0;
|
|
|
|
ret = stm_i2c_rd(st, STM_REG_WHO_AM_I, 1, &val);
|
|
if (!ret) {
|
|
if (val == STM_REG_WHO_AM_I_ID)
|
|
dev_info(&st->i2c->dev, "%s %s found\n",
|
|
__func__, name);
|
|
else
|
|
dev_info(&st->i2c->dev, "%s %hhx response @ I2C=%x\n",
|
|
__func__, val, st->i2c->addr);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int stm_id_i2c(struct stm_state *st,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(stm_i2c_addrs); i++) {
|
|
if (st->i2c->addr == stm_i2c_addrs[i])
|
|
break;
|
|
}
|
|
|
|
if (i < ARRAY_SIZE(stm_i2c_addrs)) {
|
|
st->i2c_addr = st->i2c->addr;
|
|
ret = stm_id_dev(st, id->name);
|
|
} else {
|
|
for (i = 0; i < ARRAY_SIZE(stm_i2c_addrs); i++) {
|
|
st->i2c_addr = stm_i2c_addrs[i];
|
|
ret = stm_id_dev(st, id->name);
|
|
if (!ret)
|
|
break;
|
|
}
|
|
}
|
|
if (ret)
|
|
st->i2c_addr = 0;
|
|
return ret;
|
|
}
|
|
|
|
static int stm_of_dt(struct stm_state *st, struct device_node *dn)
|
|
{
|
|
int count;
|
|
int i;
|
|
int j;
|
|
|
|
memcpy(&st->cfg, &stm_cfg_dflt, sizeof(st->cfg));
|
|
if (dn) {
|
|
/* device specific parameters */
|
|
of_property_read_u8(dn, "CTRL_REG2", &st->ru_ctrl2);
|
|
of_property_read_u8(dn, "CTRL_REG3", &st->ru_ctrl3);
|
|
of_property_read_u8(dn, "CTRL_REG4", &st->ru_ctrl4);
|
|
of_property_read_u8(dn, "CTRL_REG5", &st->ru_ctrl5);
|
|
count = of_property_count_elems_of_size(dn, "stm_odr_override",
|
|
sizeof(u32));
|
|
if (count > 0) {
|
|
if ((count % STM_ODR_OVERRIDE_CELLS) != 0) {
|
|
dev_err(&st->i2c->dev,
|
|
"%s: Invalid ODR override table length\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
st->odr_override_tbl = devm_kzalloc(&st->i2c->dev,
|
|
sizeof(u32) * count, GFP_KERNEL);
|
|
if (IS_ERR_OR_NULL(st->odr_override_tbl))
|
|
return -ENOMEM;
|
|
if (of_property_read_u32_array(dn, "stm_odr_override",
|
|
st->odr_override_tbl, count)) {
|
|
dev_err(&st->i2c->dev,
|
|
" %s Fetching odr override table failed\n",
|
|
__func__);
|
|
return -EINVAL;
|
|
}
|
|
for (i = 0; i < count; i += STM_ODR_OVERRIDE_CELLS) {
|
|
for (j = 0; j < ARRAY_SIZE(stm_odr_tbl); j++) {
|
|
if (st->odr_override_tbl[i] == stm_odr_tbl[j].us) {
|
|
stm_odr_tbl[j].us = st->odr_override_tbl[i+1];
|
|
stm_odr_tbl[j].hw = (u8)st->odr_override_tbl[i+2];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
dev_info(&st->i2c->dev, "%s dev.of_node=NULL\n", __func__);
|
|
}
|
|
stm_max_range(st, 0, (st->ru_ctrl4 & STM_REG_CTRL4_FS_MASK) >>
|
|
STM_REG_CTRL4_FS);
|
|
return 0;
|
|
}
|
|
|
|
static int stm_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct stm_state *st;
|
|
unsigned long irqflags;
|
|
int ret;
|
|
|
|
st = devm_kzalloc(&client->dev, sizeof(*st), GFP_KERNEL);
|
|
if (st == NULL) {
|
|
dev_err(&client->dev, "%s devm_kzalloc ERR\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
i2c_set_clientdata(client, st);
|
|
st->i2c = client;
|
|
ret = stm_of_dt(st, client->dev.of_node);
|
|
if (ret) {
|
|
dev_err(&client->dev, "%s _of_dt ERR\n", __func__);
|
|
goto stm_probe_exit;
|
|
}
|
|
|
|
stm_pm_init(st);
|
|
ret = stm_id_i2c(st, id);
|
|
if (ret) {
|
|
dev_err(&client->dev, "%s _id_i2c ERR\n", __func__);
|
|
ret = -ENODEV;
|
|
goto stm_probe_exit;
|
|
}
|
|
|
|
stm_pm(st, false);
|
|
ret = nvs_of_dt(client->dev.of_node, &st->cfg, NULL);
|
|
if (ret < 0)
|
|
dev_info(&client->dev, "%s nvs_of_dt ERR\n", __func__);
|
|
stm_fn_dev.errs = &st->errs;
|
|
stm_fn_dev.sts = &st->sts;
|
|
st->nvs = nvs_iio();
|
|
if (st->nvs == NULL) {
|
|
ret = -ENODEV;
|
|
goto stm_probe_exit;
|
|
}
|
|
|
|
ret = st->nvs->probe(&st->nvs_st, st, &client->dev,
|
|
&stm_fn_dev, &st->cfg);
|
|
if (ret) {
|
|
dev_err(&client->dev, "%s nvs_probe ERR\n", __func__);
|
|
ret = -ENODEV;
|
|
goto stm_probe_exit;
|
|
}
|
|
|
|
if (client->irq) {
|
|
if (st->ru_ctrl3 & (1 << STM_REG_CTRL3_IHL))
|
|
irqflags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT;
|
|
else
|
|
irqflags = IRQF_TRIGGER_RISING | IRQF_ONESHOT;
|
|
ret = request_threaded_irq(client->irq, NULL, stm_irq_thread,
|
|
irqflags, STM_NAME, st);
|
|
if (ret) {
|
|
dev_err(&client->dev, "%s req_threaded_irq ERR %d\n",
|
|
__func__, ret);
|
|
ret = -ENOMEM;
|
|
goto stm_probe_exit;
|
|
}
|
|
} else {
|
|
st->stm_work_queue = create_workqueue("stm_poll");
|
|
if (!st->stm_work_queue)
|
|
return -ENODEV;
|
|
INIT_WORK(&st->dw, stm_work);
|
|
}
|
|
|
|
dev_info(&client->dev, "%s done\n", __func__);
|
|
return 0;
|
|
|
|
stm_probe_exit:
|
|
stm_remove(client);
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_device_id stm_i2c_device_id[] = {
|
|
{ STM_NAME, 0 },
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, stm_i2c_device_id);
|
|
|
|
static const struct of_device_id stm_of_match[] = {
|
|
{ .compatible = "stm,ais328dq", },
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, stm_of_match);
|
|
|
|
static struct i2c_driver stm_driver = {
|
|
.class = I2C_CLASS_HWMON,
|
|
.probe = stm_probe,
|
|
.remove = stm_remove,
|
|
.shutdown = stm_shutdown,
|
|
.driver = {
|
|
.name = STM_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(stm_of_match),
|
|
.pm = &stm_pm_ops,
|
|
},
|
|
.id_table = stm_i2c_device_id,
|
|
};
|
|
|
|
module_i2c_driver(stm_driver);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("AIS328DQ driver");
|
|
MODULE_AUTHOR("NVIDIA Corporation");
|
|
|