forked from rrcarlosr/Jetpack
2164 lines
48 KiB
C
2164 lines
48 KiB
C
/* 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.
|
|
*/
|
|
|
|
/* Device mapping is done via three parameters:
|
|
* 1. If BMP_NVI_MPU_SUPPORT (defined below) is set, the code is included to
|
|
* support the device behind an Invensense MPU running an NVI (NVidia/
|
|
* Invensense) driver.
|
|
* If BMP_NVI_MPU_SUPPORT is 0 then this driver is only for the device in a
|
|
* stand-alone configuration without any dependancies on an Invensense MPU.
|
|
* 2. Device tree platform configuration nvi_config:
|
|
* - auto = automatically detect if connected to host or MPU
|
|
* - mpu = connected to MPU
|
|
* - host = connected to host
|
|
* This is only available if BMP_NVI_MPU_SUPPORT is set.
|
|
* 3. device in board file:
|
|
* - bmpX80 = automatically detect the device
|
|
* - force the device for:
|
|
* - bmp180
|
|
* - bmp280
|
|
* If you have no clue what the device is and don't know how it is
|
|
* connected then use auto and bmpX80. The auto-detect mechanisms are for
|
|
* platforms that have multiple possible configurations but takes longer to
|
|
* initialize. No device identification and connect testing is done for
|
|
* specific configurations.
|
|
*/
|
|
/* The NVS = NVidia Sensor framework */
|
|
/* NVI = NVidia/Invensense */
|
|
/* See Nvs.cpp in the HAL for the NVS implementation of batch/flush. */
|
|
/* See NvsIio.cpp in the HAL for the IIO enable/disable extension mechanism. */
|
|
|
|
|
|
#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/of.h>
|
|
#include <linux/nvs.h>
|
|
|
|
#ifndef BMP_NVI_MPU_SUPPORT
|
|
#define BMP_NVI_MPU_SUPPORT 0
|
|
#endif
|
|
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
#include <linux/mpu_iio.h>
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
|
|
#define BMP_DRIVER_VERSION (336)
|
|
#define BMP_VENDOR "Bosch"
|
|
#define BMP_NAME "bmpX80"
|
|
#define BMP180_NAME "bmp180"
|
|
#define BMP280_NAME "bmp280"
|
|
#define BMP_KBUF_SIZE (32)
|
|
#define BMP_PRES_MAX_RANGE_IVAL (1100)
|
|
#define BMP_PRES_MAX_RANGE_MICRO (0)
|
|
#define BMP_TEMP_MAX_RANGE_IVAL (125)
|
|
#define BMP_TEMP_MAX_RANGE_MICRO (0)
|
|
#define BMP_HW_DELAY_POR_MS (10)
|
|
#define BMP_POLL_DELAY_MS_DFLT (200)
|
|
#define BMP_MPU_RETRY_COUNT (50)
|
|
#define BMP_MPU_RETRY_DELAY_MS (20)
|
|
/* HW registers */
|
|
#define BMP_REG_ID (0xD0)
|
|
#define BMP_REG_ID_BMP180 (0x55)
|
|
#define BMP_REG_ID_BMP280 (0x56)
|
|
#define BMP_REG_RESET (0xE0)
|
|
#define BMP_REG_RESET_VAL (0xB6)
|
|
#define BMP_REG_CTRL (0xF4)
|
|
#define BMP_REG_CTRL_MODE_SLEEP (0)
|
|
#define BMP180_REG_CTRL_MODE_MASK (0x1F)
|
|
#define BMP180_REG_CTRL_MODE_PRES (0x34)
|
|
#define BMP180_REG_CTRL_MODE_TEMP (0x2E)
|
|
#define BMP180_REG_CTRL_SCO (5)
|
|
#define BMP180_REG_CTRL_OSS (6)
|
|
#define BMP280_REG_CTRL_MODE_MASK (0x03)
|
|
#define BMP280_REG_CTRL_MODE_FORCED1 (1)
|
|
#define BMP280_REG_CTRL_MODE_FORCED2 (2)
|
|
#define BMP280_REG_CTRL_MODE_NORMAL (3)
|
|
#define BMP280_REG_CTRL_OSRS_P (2)
|
|
#define BMP280_REG_CTRL_OSRS_T (5)
|
|
#define BMP180_REG_OUT_MSB (0xF6)
|
|
#define BMP180_REG_OUT_LSB (0xF7)
|
|
#define BMP180_REG_OUT_XLSB (0xF8)
|
|
#define BMP280_REG_STATUS (0xF3)
|
|
#define BMP280_REG_STATUS_MEASURING (3)
|
|
#define BMP280_REG_STATUS_IM_UPDATE (0)
|
|
#define BMP280_REG_CONFIG (0xF5)
|
|
#define BMP280_REG_CONFIG_T_SB (5)
|
|
#define BMP280_REG_PRESS_MSB (0xF7)
|
|
#define BMP280_REG_PRESS_LSB (0xF8)
|
|
#define BMP280_REG_PRESS_XLSB (0xF9)
|
|
#define BMP280_REG_TEMP_MSB (0xFA)
|
|
#define BMP280_REG_TEMP_LSB (0xFB)
|
|
#define BMP280_REG_TEMP_XLSB (0xFC)
|
|
/* ROM registers */
|
|
#define BMP180_REG_AC1 (0xAA)
|
|
#define BMP180_REG_AC2 (0xAC)
|
|
#define BMP180_REG_AC3 (0xAE)
|
|
#define BMP180_REG_AC4 (0xB0)
|
|
#define BMP180_REG_AC5 (0xB2)
|
|
#define BMP180_REG_AC6 (0xB4)
|
|
#define BMP180_REG_B1 (0xB6)
|
|
#define BMP180_REG_B2 (0xB8)
|
|
#define BMP180_REG_MB (0xBA)
|
|
#define BMP180_REG_MC (0xBC)
|
|
#define BMP180_REG_MD (0xBE)
|
|
#define BMP280_REG_CWORD00 (0x88)
|
|
#define BMP280_REG_CWORD01 (0x8A)
|
|
#define BMP280_REG_CWORD02 (0x8C)
|
|
#define BMP280_REG_CWORD03 (0x8E)
|
|
#define BMP280_REG_CWORD04 (0x90)
|
|
#define BMP280_REG_CWORD05 (0x92)
|
|
#define BMP280_REG_CWORD06 (0x94)
|
|
#define BMP280_REG_CWORD07 (0x96)
|
|
#define BMP280_REG_CWORD08 (0x98)
|
|
#define BMP280_REG_CWORD09 (0x9A)
|
|
#define BMP280_REG_CWORD10 (0x9C)
|
|
#define BMP280_REG_CWORD11 (0x9E)
|
|
#define BMP280_REG_CWORD12 (0xA0)
|
|
|
|
#define BATCH_DRY_RUN (0x1)
|
|
#define BATCH_WAKE_UPON_FIFO_FULL (0x2)
|
|
#define WR (0)
|
|
#define RD (1)
|
|
#define PORT_N (2)
|
|
/* _buf_push expects this scan order */
|
|
#define BMP_DEV_PRS (0)
|
|
#define BMP_DEV_TMP (1)
|
|
#define BMP_DEV_N (2)
|
|
|
|
/* regulator names in order of powering on */
|
|
static char *bmp_vregs[] = {
|
|
"vdd",
|
|
"vddio",
|
|
};
|
|
|
|
static char *bmp_configs[] = {
|
|
"auto",
|
|
"mpu",
|
|
"host",
|
|
};
|
|
|
|
static u8 bmp_ids[] = {
|
|
BMP_REG_ID_BMP180,
|
|
BMP_REG_ID_BMP280,
|
|
0x57,
|
|
0x58,
|
|
};
|
|
|
|
static unsigned short bmp_i2c_addrs[] = {
|
|
0x76,
|
|
0x77,
|
|
};
|
|
|
|
struct bmp_scale {
|
|
unsigned int delay_ms;
|
|
u8 os; /* oversampling */
|
|
struct nvs_float resolution;
|
|
struct nvs_float milliamp;
|
|
};
|
|
|
|
struct bmp_hal_dev {
|
|
int version;
|
|
unsigned int scale_n;
|
|
struct bmp_scale *scale;
|
|
struct nvs_float scale_float;
|
|
};
|
|
|
|
union bmp_rom {
|
|
struct bmp180_rom {
|
|
s16 ac1;
|
|
s16 ac2;
|
|
s16 ac3;
|
|
u16 ac4;
|
|
u16 ac5;
|
|
u16 ac6;
|
|
s16 b1;
|
|
s16 b2;
|
|
s16 mb;
|
|
s16 mc;
|
|
s16 md;
|
|
} bmp180;
|
|
struct bmp280_rom {
|
|
u16 dig_T1;
|
|
s16 dig_T2;
|
|
s16 dig_T3;
|
|
u16 dig_P1;
|
|
s16 dig_P2;
|
|
s16 dig_P3;
|
|
s16 dig_P4;
|
|
s16 dig_P5;
|
|
s16 dig_P6;
|
|
s16 dig_P7;
|
|
s16 dig_P8;
|
|
s16 dig_P9;
|
|
s16 reserved;
|
|
} bmp280;
|
|
} rom;
|
|
|
|
struct bmp_cmode {
|
|
unsigned int t_us;
|
|
u8 t_sb;
|
|
};
|
|
|
|
struct bmp_state {
|
|
struct i2c_client *i2c;
|
|
struct nvs_fn_if *nvs;
|
|
void *nvs_st[BMP_DEV_N];
|
|
struct sensor_cfg cfg[BMP_DEV_N];
|
|
struct regulator_bulk_data vreg[ARRAY_SIZE(bmp_vregs)];
|
|
struct workqueue_struct *wq;
|
|
struct work_struct ws;
|
|
struct bmp_hal *hal; /* Hardware Abstaction Layer */
|
|
union bmp_rom rom; /* calibration data */
|
|
unsigned int sts; /* status flags */
|
|
unsigned int errs; /* error count */
|
|
unsigned int enabled; /* enable status */
|
|
unsigned int period_us_min; /* minimum period from MPU */
|
|
unsigned int period_us; /* global sampling period */
|
|
unsigned int delay_us[BMP_DEV_N]; /* sampling delay */
|
|
unsigned int timeout_us[BMP_DEV_N]; /* batch timeout */
|
|
unsigned int scale_i; /* oversampling index */
|
|
unsigned int scale_user; /* user oversampling index */
|
|
unsigned int mpu_bypass_n; /* allow single bypass call */
|
|
unsigned int dmp_rd_len_sts; /* status length from DMP */
|
|
unsigned int dmp_rd_len_data; /* data length from DMP */
|
|
u16 i2c_addr; /* I2C address */
|
|
u8 dev_id; /* device ID */
|
|
bool initd; /* set if initialized */
|
|
bool cmode; /* mode normal (continuous) */
|
|
bool mpu_en; /* if device behind MPU */
|
|
bool port_en[PORT_N]; /* enable status of MPU write port */
|
|
int port_id[PORT_N]; /* MPU port ID */
|
|
u8 data_out; /* write value to mode register */
|
|
s32 ut; /* uncompensated temperature */
|
|
s32 up; /* uncompensated pressure */
|
|
s32 t_fine; /* temperature used in pressure calc */
|
|
s32 tmp; /* true temperature */
|
|
u32 prs; /* true pressure hPa/100 Pa/1 mBar */
|
|
u8 nvi_config; /* NVI configuration */
|
|
};
|
|
|
|
struct bmp_hal {
|
|
struct bmp_hal_dev *dev;
|
|
const char *part;
|
|
u8 rom_addr_start;
|
|
u8 rom_size;
|
|
bool rom_big_endian;
|
|
u8 mode_mask;
|
|
struct bmp_cmode *cmode_tbl;
|
|
int (*bmp_read)(struct bmp_state *st, s64 ts);
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
unsigned int mpu_id;
|
|
u8 port_rd_reg;
|
|
u8 port_rd_ctrl;
|
|
void (*port_rd_handler)(u8 *data, unsigned int len,
|
|
long long timestamp, void *ext_driver);
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
};
|
|
|
|
|
|
static void bmp_err(struct bmp_state *st)
|
|
{
|
|
st->errs++;
|
|
if (!st->errs)
|
|
st->errs--;
|
|
}
|
|
|
|
static int bmp_i2c_rd(struct bmp_state *st, u8 reg, u16 len, u8 *val)
|
|
{
|
|
struct i2c_msg msg[2];
|
|
|
|
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) {
|
|
bmp_err(st);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bmp_i2c_wr(struct bmp_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) {
|
|
bmp_err(st);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bmp_nvi_mpu_bypass_request(struct bmp_state *st)
|
|
{
|
|
int ret = 0;
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
int i;
|
|
|
|
if (st->mpu_en) {
|
|
if (!st->mpu_bypass_n) {
|
|
for (i = 0; i < BMP_MPU_RETRY_COUNT; i++) {
|
|
ret = nvi_mpu_bypass_request(true);
|
|
if ((!ret) || (ret == -EPERM))
|
|
break;
|
|
|
|
msleep(BMP_MPU_RETRY_DELAY_MS);
|
|
}
|
|
|
|
if (ret) {
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_err(&st->i2c->dev, "%s err=%d\n",
|
|
__func__, ret);
|
|
if (ret == -EPERM)
|
|
ret = 0;
|
|
} else {
|
|
st->mpu_bypass_n++;
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&st->i2c->dev, "%s\n",
|
|
__func__);
|
|
}
|
|
} else {
|
|
st->mpu_bypass_n++;
|
|
}
|
|
}
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_nvi_mpu_bypass_release(struct bmp_state *st)
|
|
{
|
|
int ret = 0;
|
|
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
if (st->mpu_en) {
|
|
if (st->mpu_bypass_n == 1) {
|
|
ret = nvi_mpu_bypass_release();
|
|
if (ret) {
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_err(&st->i2c->dev, "%s err=%d\n",
|
|
__func__, ret);
|
|
} else {
|
|
st->mpu_bypass_n--;
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&st->i2c->dev, "%s\n",
|
|
__func__);
|
|
}
|
|
} else {
|
|
st->mpu_bypass_n--;
|
|
}
|
|
}
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_mode_wr(struct bmp_state *st, u8 mode)
|
|
{
|
|
int ret;
|
|
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
if (st->mpu_en && !st->cmode) {
|
|
ret = nvi_mpu_data_out(st->port_id[WR], mode);
|
|
} else {
|
|
ret = bmp_nvi_mpu_bypass_request(st);
|
|
if (!ret) {
|
|
if (st->cmode) {
|
|
ret = bmp_i2c_wr(st, BMP_REG_CTRL,
|
|
BMP_REG_CTRL_MODE_SLEEP);
|
|
if (mode & st->hal->mode_mask) {
|
|
udelay(BMP_HW_DELAY_POR_MS);
|
|
ret |= bmp_i2c_wr(st, BMP_REG_CTRL,
|
|
mode);
|
|
}
|
|
} else {
|
|
ret = bmp_i2c_wr(st, BMP_REG_CTRL, mode);
|
|
}
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&st->i2c->dev, "%s mode=%x err=%d\n",
|
|
__func__, mode, ret);
|
|
bmp_nvi_mpu_bypass_release(st);
|
|
}
|
|
}
|
|
#else /* BMP_NVI_MPU_SUPPORT */
|
|
ret = bmp_i2c_wr(st, BMP_REG_CTRL, mode);
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&st->i2c->dev, "%s mode=%x err=%d\n",
|
|
__func__, mode, ret);
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
if (!ret)
|
|
st->data_out = mode;
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_pm(struct bmp_state *st, bool enable)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (enable) {
|
|
ret = nvs_vregs_enable(&st->i2c->dev, st->vreg,
|
|
ARRAY_SIZE(bmp_vregs));
|
|
if (ret)
|
|
mdelay(BMP_HW_DELAY_POR_MS);
|
|
} else {
|
|
if (st->cmode) {
|
|
ret = nvs_vregs_sts(st->vreg, ARRAY_SIZE(bmp_vregs));
|
|
if ((ret < 0) || (ret == ARRAY_SIZE(bmp_vregs))) {
|
|
ret = bmp_mode_wr(st, BMP_REG_CTRL_MODE_SLEEP);
|
|
} else if (ret > 0) {
|
|
nvs_vregs_enable(&st->i2c->dev, st->vreg,
|
|
ARRAY_SIZE(bmp_vregs));
|
|
mdelay(BMP_HW_DELAY_POR_MS);
|
|
ret = bmp_mode_wr(st, BMP_REG_CTRL_MODE_SLEEP);
|
|
}
|
|
}
|
|
ret |= nvs_vregs_disable(&st->i2c->dev, st->vreg,
|
|
ARRAY_SIZE(bmp_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;
|
|
}
|
|
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
static int bmp_port_free(struct bmp_state *st, int port)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (st->port_id[port] >= 0) {
|
|
ret = nvi_mpu_port_free(st->port_id[port]);
|
|
if (!ret)
|
|
st->port_id[port] = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_ports_free(struct bmp_state *st)
|
|
{
|
|
int ret;
|
|
|
|
ret = bmp_port_free(st, WR);
|
|
ret |= bmp_port_free(st, RD);
|
|
return ret;
|
|
}
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
|
|
static void bmp_pm_exit(struct bmp_state *st)
|
|
{
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
bmp_ports_free(st);
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
bmp_pm(st, false);
|
|
nvs_vregs_exit(&st->i2c->dev, st->vreg, ARRAY_SIZE(bmp_vregs));
|
|
}
|
|
|
|
static int bmp_pm_init(struct bmp_state *st)
|
|
{
|
|
int ret;
|
|
|
|
st->delay_us[BMP_DEV_PRS] = (BMP_POLL_DELAY_MS_DFLT * 1000);
|
|
st->delay_us[BMP_DEV_TMP] = (BMP_POLL_DELAY_MS_DFLT * 1000);
|
|
st->period_us = (BMP_POLL_DELAY_MS_DFLT * 1000);
|
|
st->port_id[WR] = -1;
|
|
st->port_id[RD] = -1;
|
|
nvs_vregs_init(&st->i2c->dev,
|
|
st->vreg, ARRAY_SIZE(bmp_vregs), bmp_vregs);
|
|
ret = bmp_pm(st, true);
|
|
return ret;
|
|
}
|
|
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
static int bmp_ports_enable(struct bmp_state *st, bool enable)
|
|
{
|
|
int ret = 0;
|
|
unsigned int port_mask = 0;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < PORT_N; i++) {
|
|
if (enable != st->port_en[i] && st->port_id[i] >= 0)
|
|
port_mask |= (1 << st->port_id[i]);
|
|
}
|
|
|
|
if (port_mask) {
|
|
ret = nvi_mpu_enable(port_mask, enable);
|
|
if (!ret) {
|
|
for (i = 0; i < PORT_N; i++) {
|
|
if (st->port_id[i] >= 0 &&
|
|
port_mask & (1 << st->port_id[i]))
|
|
st->port_en[i] = enable;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
|
|
static int bmp_wr(struct bmp_state *st, u8 reg, u8 val)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = bmp_nvi_mpu_bypass_request(st);
|
|
if (!ret) {
|
|
ret = bmp_i2c_wr(st, reg, val);
|
|
bmp_nvi_mpu_bypass_release(st);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int bmp_cmode_t(struct bmp_state *st, u8 *t_sb,
|
|
unsigned int period_us, unsigned int scale_i)
|
|
{
|
|
unsigned int us = 0;
|
|
unsigned int i = 0;
|
|
|
|
|
|
while (st->hal->cmode_tbl[i].t_us) {
|
|
*t_sb = st->hal->cmode_tbl[i].t_sb;
|
|
us = st->hal->cmode_tbl[i].t_us;
|
|
us += st->hal->dev[BMP_DEV_PRS].scale[scale_i].delay_ms * 1000;
|
|
if (period_us >= us)
|
|
return us;
|
|
|
|
i++;
|
|
if ((!st->mpu_en) && st->hal->cmode_tbl[i].t_us) {
|
|
us -= st->hal->cmode_tbl[i].t_us;
|
|
us >>= 1;
|
|
us += st->hal->cmode_tbl[i].t_us;
|
|
us += st->hal->dev[BMP_DEV_PRS].scale[scale_i].
|
|
delay_ms * 1000;
|
|
if (period_us > us)
|
|
return us;
|
|
}
|
|
}
|
|
|
|
return us;
|
|
}
|
|
|
|
static int bmp_mode(struct bmp_state *st, unsigned int period_us,
|
|
unsigned int scale_user, unsigned int enable)
|
|
{
|
|
u8 mode;
|
|
u8 t_sb;
|
|
unsigned int i;
|
|
unsigned int scale_i;
|
|
unsigned int us = 0;
|
|
int ret = 0;
|
|
|
|
i = BMP_DEV_PRS;
|
|
if (st->cmode) {
|
|
if (scale_user) {
|
|
/* static scale mode */
|
|
scale_i = scale_user - 1;
|
|
us = bmp_cmode_t(st, &t_sb, period_us, scale_i);
|
|
} else {
|
|
/* auto scale mode - matching scale to the period */
|
|
scale_i = 0;
|
|
for (; scale_i < st->hal->dev[i].scale_n; scale_i++) {
|
|
us = bmp_cmode_t(st, &t_sb,
|
|
period_us, scale_i);
|
|
if (period_us >= us)
|
|
/* HW (us) is fast enough */
|
|
break;
|
|
}
|
|
|
|
if (scale_i >= st->hal->dev[i].scale_n)
|
|
scale_i = st->hal->dev[i].scale_n - 1;
|
|
}
|
|
|
|
if (period_us < us)
|
|
period_us = us;
|
|
mode = BMP280_REG_CTRL_MODE_NORMAL;
|
|
mode |= st->hal->dev[BMP_DEV_TMP].scale[scale_i].os <<
|
|
BMP280_REG_CTRL_OSRS_T;
|
|
if (enable & (1 << BMP_DEV_PRS))
|
|
mode |= st->hal->dev[BMP_DEV_PRS].scale[scale_i].os <<
|
|
BMP280_REG_CTRL_OSRS_P;
|
|
/* else presure will be disabled */
|
|
t_sb <<= BMP280_REG_CONFIG_T_SB;
|
|
ret = bmp_nvi_mpu_bypass_request(st);
|
|
if (!ret) {
|
|
ret = bmp_wr(st, BMP280_REG_CONFIG, t_sb);
|
|
if (ret)
|
|
mode = BMP_REG_CTRL_MODE_SLEEP;
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&st->i2c->dev, "%s cfg=%x err=%d\n",
|
|
__func__, t_sb, ret);
|
|
ret |= bmp_mode_wr(st, mode);
|
|
bmp_nvi_mpu_bypass_release(st);
|
|
}
|
|
} else {
|
|
if (scale_user) {
|
|
scale_i = scale_user - 1;
|
|
us = st->hal->dev[i].scale[scale_i].delay_ms * 1000;
|
|
} else {
|
|
/* scale is automatic based on period */
|
|
scale_i = 0;
|
|
for (; scale_i < st->hal->dev[i].scale_n; scale_i++) {
|
|
us = st->hal->dev[i].scale[scale_i].delay_ms;
|
|
us *= 1000;
|
|
if (period_us >= us)
|
|
/* HW (us) is fast enough */
|
|
break;
|
|
}
|
|
|
|
if (scale_i >= st->hal->dev[i].scale_n)
|
|
scale_i = st->hal->dev[i].scale_n - 1;
|
|
}
|
|
|
|
if (period_us < us)
|
|
period_us = us;
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
if (scale_i != st->scale_i && st->mpu_en &&
|
|
st->port_id[WR] >= 0)
|
|
ret = nvi_mpu_delay_ms(st->port_id[WR],
|
|
st->hal->dev[i].scale[scale_i].delay_ms);
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
if (st->dev_id == BMP_REG_ID_BMP180) {
|
|
mode = st->hal->dev[i].scale[scale_i].os <<
|
|
BMP180_REG_CTRL_OSS;
|
|
mode |= BMP180_REG_CTRL_MODE_TEMP;
|
|
} else {
|
|
mode = BMP280_REG_CTRL_MODE_FORCED1;
|
|
mode |= st->hal->dev[BMP_DEV_TMP].scale[scale_i].os <<
|
|
BMP280_REG_CTRL_OSRS_T;
|
|
if (enable & (1 << BMP_DEV_PRS))
|
|
mode |= st->hal->dev[i].scale[scale_i].os <<
|
|
BMP280_REG_CTRL_OSRS_P;
|
|
}
|
|
ret |= bmp_mode_wr(st, mode);
|
|
}
|
|
if (!ret) {
|
|
if (period_us != st->period_us) {
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&st->i2c->dev, "%s: period_us=%u\n",
|
|
__func__, period_us);
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
if (st->mpu_en) {
|
|
us = -1;
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if ((enable & (1 << i)) &&
|
|
st->timeout_us[i] < us)
|
|
us = st->timeout_us[i];
|
|
}
|
|
ret = nvi_mpu_batch(st->port_id[RD],
|
|
period_us, us);
|
|
}
|
|
if (!ret)
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
st->period_us = period_us;
|
|
}
|
|
if (!ret)
|
|
st->scale_i = scale_i;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void bmp_calc_180(struct bmp_state *st)
|
|
{
|
|
long X1, X2, X3, B3, B5, B6, p;
|
|
unsigned long B4, B7;
|
|
long pressure;
|
|
u8 oss = st->hal->dev[BMP_DEV_PRS].scale[st->scale_i].os;
|
|
|
|
X1 = ((st->ut - st->rom.bmp180.ac6) * st->rom.bmp180.ac5) >> 15;
|
|
X2 = st->rom.bmp180.mc * (1 << 11) / (X1 + st->rom.bmp180.md);
|
|
B5 = X1 + X2;
|
|
st->tmp = (B5 + 8) >> 4;
|
|
B6 = B5 - 4000;
|
|
X1 = (st->rom.bmp180.b2 * ((B6 * B6) >> 12)) >> 11;
|
|
X2 = (st->rom.bmp180.ac2 * B6) >> 11;
|
|
X3 = X1 + X2;
|
|
B3 = ((((st->rom.bmp180.ac1 << 2) + X3) << oss) + 2) >> 2;
|
|
X1 = (st->rom.bmp180.ac3 * B6) >> 13;
|
|
X2 = (st->rom.bmp180.b1 * ((B6 * B6) >> 12)) >> 16;
|
|
X3 = ((X1 + X2) + 2) >> 2;
|
|
B4 = (st->rom.bmp180.ac4 * (unsigned long)(X3 + 32768)) >> 15;
|
|
B7 = ((unsigned long)st->up - B3) * (50000 >> oss);
|
|
if (B7 < 0x80000000)
|
|
p = (B7 << 1) / B4;
|
|
else
|
|
p = (B7 / B4) << 1;
|
|
X1 = (p >> 8) * (p >> 8);
|
|
X1 = (X1 * 3038) >> 16;
|
|
X2 = (-7357 * p) >> 16;
|
|
pressure = p + ((X1 + X2 + 3791) >> 4);
|
|
st->prs = (int)pressure;
|
|
}
|
|
|
|
static int bmp_read_sts_180(struct bmp_state *st, u8 *data, s64 ts)
|
|
{
|
|
u8 oss;
|
|
s32 val;
|
|
int ret = 0;
|
|
|
|
/* BMP180_REG_CTRL_SCO is 0 when data is ready */
|
|
if (!(data[0] & (1 << BMP180_REG_CTRL_SCO))) {
|
|
oss = st->hal->dev[BMP_DEV_PRS].scale[st->scale_i].os;
|
|
ret = -1;
|
|
if (data[0] == 0x0A) { /* temperature */
|
|
st->ut = ((data[2] << 8) + data[3]);
|
|
st->data_out = BMP180_REG_CTRL_MODE_PRES |
|
|
(oss << BMP180_REG_CTRL_OSS);
|
|
} else { /* pressure */
|
|
val = ((data[2] << 16) + (data[3] << 8) + data[4]);
|
|
val >>= (8 - oss);
|
|
st->data_out = BMP180_REG_CTRL_MODE_TEMP;
|
|
st->up = val;
|
|
bmp_calc_180(st);
|
|
ret = 1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_read_180(struct bmp_state *st, s64 ts)
|
|
{
|
|
u8 data[5];
|
|
int ret;
|
|
|
|
ret = bmp_i2c_rd(st, BMP_REG_CTRL, 5, data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = bmp_read_sts_180(st, data, ts);
|
|
if (ret > 0) {
|
|
if (st->enabled & (1 << BMP_DEV_PRS))
|
|
st->nvs->handler(st->nvs_st[BMP_DEV_PRS],
|
|
&st->prs, ts);
|
|
if (st->enabled & (1 << BMP_DEV_TMP))
|
|
/* nvs handles on-change sensor timing & same values */
|
|
st->nvs->handler(st->nvs_st[BMP_DEV_TMP],
|
|
&st->tmp, ts);
|
|
bmp_i2c_wr(st, BMP_REG_CTRL, st->data_out);
|
|
} else if (ret < 0) {
|
|
bmp_i2c_wr(st, BMP_REG_CTRL, st->data_out);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void bmp_calc_temp_280(struct bmp_state *st)
|
|
{
|
|
s32 var1;
|
|
s32 var2;
|
|
|
|
var1 = st->ut;
|
|
var1 >>= 3;
|
|
var2 = st->rom.bmp280.dig_T1;
|
|
var2 <<= 1;
|
|
var1 -= var2;
|
|
var1 *= st->rom.bmp280.dig_T2;
|
|
var1 >>= 11;
|
|
var2 = st->ut;
|
|
var2 >>= 4;
|
|
var2 -= (s32)st->rom.bmp280.dig_T1;
|
|
var2 *= var2;
|
|
var2 >>= 12;
|
|
var2 *= st->rom.bmp280.dig_T3;
|
|
var2 >>= 14;
|
|
st->t_fine = var1 + var2;
|
|
st->tmp = (st->t_fine * 5 + 128) >> 8;
|
|
}
|
|
|
|
static int bmp_calc_pres_280(struct bmp_state *st)
|
|
{
|
|
s64 var1;
|
|
s64 var2;
|
|
s64 var3;
|
|
s64 p;
|
|
|
|
var1 = st->t_fine;
|
|
var1 -= 128000;
|
|
var2 = var1 * var1;
|
|
var2 *= st->rom.bmp280.dig_P6;
|
|
var3 = st->rom.bmp280.dig_P5;
|
|
var3 *= var1;
|
|
var3 <<= 17;
|
|
var2 += var3;
|
|
var3 = st->rom.bmp280.dig_P4;
|
|
var3 <<= 35;
|
|
var2 += var3;
|
|
var3 = var1;
|
|
var3 *= st->rom.bmp280.dig_P2;
|
|
var3 <<= 12;
|
|
var1 *= var1;
|
|
var1 *= st->rom.bmp280.dig_P3;
|
|
var1 >>= 8;
|
|
var1 += var3;
|
|
var3 = 1;
|
|
var3 <<= 47;
|
|
var1 += var3;
|
|
var1 *= st->rom.bmp280.dig_P1;
|
|
var1 >>= 33;
|
|
if (!var1)
|
|
return -1;
|
|
|
|
p = 1048576 - st->up;
|
|
p <<= 31;
|
|
p -= var2;
|
|
p *= 3125;
|
|
p = div64_s64(p, var1);
|
|
var1 = p;
|
|
var1 >>= 13;
|
|
var1 *= var1;
|
|
var1 *= st->rom.bmp280.dig_P9;
|
|
var1 >>= 25;
|
|
var2 = st->rom.bmp280.dig_P8;
|
|
var2 *= p;
|
|
var2 >>= 19;
|
|
var3 = st->rom.bmp280.dig_P7;
|
|
var3 <<= 4;
|
|
p += var1;
|
|
p += var2;
|
|
p >>= 8;
|
|
p += var3;
|
|
p >>= 8;
|
|
st->prs = (u32)p;
|
|
return 1;
|
|
}
|
|
|
|
static int bmp_push_280(struct bmp_state *st, u8 *data, s64 ts)
|
|
{
|
|
s32 val;
|
|
int ret;
|
|
|
|
val = (data[0] << 16) | (data[1] << 8) | data[2];
|
|
val = le32_to_cpup(&val);
|
|
val >>= 4;
|
|
st->up = val;
|
|
val = (data[3] << 16) | (data[4] << 8) | data[5];
|
|
val = le32_to_cpup(&val);
|
|
val >>= 4;
|
|
st->ut = val;
|
|
bmp_calc_temp_280(st);
|
|
if (st->enabled & (1 << BMP_DEV_PRS)) {
|
|
ret = bmp_calc_pres_280(st);
|
|
if (ret > 0)
|
|
st->nvs->handler(st->nvs_st[BMP_DEV_PRS],
|
|
&st->prs, ts);
|
|
} else {
|
|
ret = 1;
|
|
}
|
|
if (st->enabled & (1 << BMP_DEV_TMP))
|
|
/* nvs handles on-change sensor timing & same values */
|
|
st->nvs->handler(st->nvs_st[BMP_DEV_TMP], &st->tmp, ts);
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_read_sts_280(struct bmp_state *st, u8 *data)
|
|
{
|
|
if (data[0] & (1 << BMP280_REG_STATUS_IM_UPDATE))
|
|
/* registers are updating */
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int bmp_read_280(struct bmp_state *st, s64 ts)
|
|
{
|
|
u8 data[10];
|
|
int ret;
|
|
|
|
ret = bmp_i2c_rd(st, BMP280_REG_STATUS, 10, data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = bmp_read_sts_280(st, data);
|
|
if (ret > 0) {
|
|
ret = bmp_push_280(st, &data[4], ts);
|
|
if (ret > 0)
|
|
bmp_i2c_wr(st, BMP_REG_CTRL, st->data_out);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
static void bmp_mpu_handler_280(u8 *data, unsigned int len, s64 ts, void *p_val)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)p_val;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
if (ts < 0 || !data)
|
|
/* error - just drop */
|
|
return;
|
|
|
|
if (!ts) {
|
|
/* no timestamp means flush done */
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if (st->nvs_st[i])
|
|
st->nvs->handler(st->nvs_st[i], NULL, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (st->enabled) {
|
|
if (len == st->dmp_rd_len_sts)
|
|
/* status data from the DMP not supported */
|
|
return;
|
|
|
|
if (len == st->dmp_rd_len_data) {
|
|
/* this data is from the DMP */
|
|
i = 0;
|
|
ret = 1;
|
|
} else {
|
|
i = 4;
|
|
ret = bmp_read_sts_280(st, data);
|
|
}
|
|
if (ret > 0)
|
|
bmp_push_280(st, &data[i], ts);
|
|
}
|
|
}
|
|
|
|
static void bmp_mpu_handler_180(u8 *data, unsigned int len, s64 ts, void *p_val)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)p_val;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
if (ts < 0)
|
|
/* error - just drop */
|
|
return;
|
|
|
|
if (!ts) {
|
|
/* no timestamp means flush done */
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if (st->nvs_st[i])
|
|
st->nvs->handler(st->nvs_st[i], NULL, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
ret = bmp_read_sts_180(st, data, ts);
|
|
if (ret > 0) {
|
|
if (st->enabled & (1 << BMP_DEV_PRS))
|
|
st->nvs->handler(st->nvs_st[BMP_DEV_PRS],
|
|
&st->prs, ts);
|
|
if (st->enabled & (1 << BMP_DEV_TMP))
|
|
/* nvs handles on-change sensor timing & same values */
|
|
st->nvs->handler(st->nvs_st[BMP_DEV_TMP],
|
|
&st->tmp, ts);
|
|
nvi_mpu_data_out(st->port_id[WR], st->data_out);
|
|
} else if (ret < 0) {
|
|
nvi_mpu_data_out(st->port_id[WR], st->data_out);
|
|
}
|
|
}
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
|
|
static void bmp_work(struct work_struct *ws)
|
|
{
|
|
struct bmp_state *st = container_of((struct work_struct *)ws,
|
|
struct bmp_state, ws);
|
|
s64 ts1;
|
|
s64 ts2;
|
|
unsigned int ts_diff;
|
|
unsigned int i;
|
|
unsigned long delay_us;
|
|
|
|
while (1) {
|
|
for (i = 0; i < BMP_DEV_N; i++)
|
|
st->nvs->nvs_mutex_lock(st->nvs_st[i]);
|
|
if (st->enabled) {
|
|
ts1 = nvs_timestamp();
|
|
st->hal->bmp_read(st, ts1);
|
|
ts2 = nvs_timestamp();
|
|
ts_diff = (ts2 - ts1) / 1000; /* ns => us */
|
|
if (st->period_us > ts_diff)
|
|
delay_us = st->period_us - ts_diff;
|
|
else
|
|
delay_us = 0;
|
|
for (i = 0; i < BMP_DEV_N; i++)
|
|
st->nvs->nvs_mutex_unlock(st->nvs_st[i]);
|
|
} else {
|
|
for (i = 0; i < BMP_DEV_N; i++)
|
|
st->nvs->nvs_mutex_unlock(st->nvs_st[i]);
|
|
break;
|
|
}
|
|
|
|
if (delay_us) {
|
|
if (st->period_us <= st->cfg[BMP_DEV_PRS].thresh_lo)
|
|
usleep_range(delay_us, delay_us);
|
|
else
|
|
usleep_range(delay_us, st->period_us);
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsigned int bmp_poll_delay(struct bmp_state *st, unsigned int en_msk)
|
|
{
|
|
unsigned int i;
|
|
unsigned int delay_us = -1;
|
|
bool valid = false;
|
|
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if (en_msk & (1 << i)) {
|
|
if (st->delay_us[i] && st->delay_us[i] < delay_us) {
|
|
delay_us = st->delay_us[i];
|
|
valid = true;
|
|
}
|
|
}
|
|
}
|
|
if (!valid)
|
|
delay_us = (BMP_POLL_DELAY_MS_DFLT * 1000);
|
|
return delay_us;
|
|
}
|
|
|
|
static int bmp_init_hw(struct bmp_state *st)
|
|
{
|
|
u8 *p_rom8;
|
|
u16 *p_rom16;
|
|
int i;
|
|
int ret = 0;
|
|
|
|
st->ut = 0;
|
|
st->up = 0;
|
|
st->tmp = 0;
|
|
st->prs = 0;
|
|
p_rom8 = (u8 *)&st->rom;
|
|
ret = bmp_nvi_mpu_bypass_request(st);
|
|
if (!ret) {
|
|
ret = bmp_i2c_rd(st, st->hal->rom_addr_start,
|
|
st->hal->rom_size, p_rom8);
|
|
bmp_nvi_mpu_bypass_release(st);
|
|
}
|
|
if (ret)
|
|
return ret;
|
|
|
|
p_rom16 = (u16 *)&st->rom;
|
|
for (i = 0; i < (st->hal->rom_size >> 1); i++) {
|
|
if (st->hal->rom_big_endian)
|
|
*p_rom16 = be16_to_cpup(p_rom16);
|
|
else
|
|
*p_rom16 = le16_to_cpup(p_rom16);
|
|
p_rom16++;
|
|
}
|
|
st->initd = true;
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_dis(struct bmp_state *st)
|
|
{
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
int ret = 0;
|
|
|
|
if (st->mpu_en)
|
|
ret = bmp_ports_enable(st, false);
|
|
if (!ret)
|
|
st->enabled = 0;
|
|
return ret;
|
|
#else /* BMP_NVI_MPU_SUPPORT */
|
|
st->enabled = 0;
|
|
return 0;
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
}
|
|
|
|
static int bmp_disable(struct bmp_state *st, int snsr_id)
|
|
{
|
|
bool disable = true;
|
|
int ret = 0;
|
|
|
|
if (snsr_id >= 0) {
|
|
if (st->enabled & ~(1 << snsr_id)) {
|
|
st->enabled &= ~(1 << snsr_id);
|
|
disable = false;
|
|
}
|
|
}
|
|
if (disable) {
|
|
ret = bmp_dis(st);
|
|
if (!ret)
|
|
bmp_pm(st, false);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_en(struct bmp_state *st)
|
|
{
|
|
int ret = 0;
|
|
|
|
bmp_pm(st, true);
|
|
if (!st->initd)
|
|
ret = bmp_init_hw(st);
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_enable(void *client, int snsr_id, int enable)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
int ret;
|
|
|
|
if (enable < 0)
|
|
return st->enabled & (1 << snsr_id);
|
|
|
|
if (enable) {
|
|
enable = st->enabled | (1 << snsr_id);
|
|
ret = bmp_en(st);
|
|
if (!ret) {
|
|
ret = bmp_mode(st, bmp_poll_delay(st, enable),
|
|
st->scale_user, enable);
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
if (st->mpu_en)
|
|
ret |= bmp_ports_enable(st, true);
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
if (ret) {
|
|
bmp_disable(st, snsr_id);
|
|
} else {
|
|
st->enabled = enable;
|
|
if (!st->mpu_en) {
|
|
cancel_work_sync(&st->ws);
|
|
queue_work(st->wq, &st->ws);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ret = bmp_disable(st, snsr_id);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_batch(void *client, int snsr_id, int flags,
|
|
unsigned int period, unsigned int timeout)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
int ret = 0;
|
|
unsigned int old_period;
|
|
unsigned int old_timeout;
|
|
|
|
if (timeout && !st->mpu_en)
|
|
/* timeout not supported (no HW FIFO) */
|
|
return -EINVAL;
|
|
|
|
old_period = st->delay_us[snsr_id];
|
|
old_timeout = st->timeout_us[snsr_id];
|
|
st->delay_us[snsr_id] = period;
|
|
st->timeout_us[snsr_id] = timeout;
|
|
if (st->enabled) {
|
|
ret = bmp_mode(st, bmp_poll_delay(st, st->enabled),
|
|
st->scale_user, st->enabled);
|
|
if (ret) {
|
|
st->delay_us[snsr_id] = old_period;
|
|
st->timeout_us[snsr_id] = old_timeout;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_batch_read(void *client, int snsr_id,
|
|
unsigned int *period_us, unsigned int *timeout_us)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
int ret = 0;
|
|
|
|
if (period_us)
|
|
*period_us = st->period_us;
|
|
if (timeout_us)
|
|
*timeout_us = 0;
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
if (st->mpu_en)
|
|
ret = nvi_mpu_batch_read(st->port_id[RD],
|
|
period_us, timeout_us);
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_flush(void *client, int snsr_id)
|
|
{
|
|
int ret = -EINVAL;
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
|
|
if (st->mpu_en)
|
|
ret = nvi_mpu_flush(st->port_id[RD]);
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_resolution(void *client, int snsr_id, int resolution)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
unsigned int us;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
if (snsr_id != BMP_DEV_PRS)
|
|
return -EINVAL;
|
|
|
|
if (resolution < 0)
|
|
resolution = 0;
|
|
if (resolution > st->hal->dev[BMP_DEV_PRS].scale_n)
|
|
resolution = st->hal->dev[BMP_DEV_PRS].scale_n;
|
|
if (st->enabled) {
|
|
ret = bmp_mode(st, st->period_us, resolution, st->enabled);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
if (resolution)
|
|
st->scale_i = resolution - 1;
|
|
else
|
|
st->scale_i = 0;
|
|
}
|
|
|
|
st->scale_user = resolution;
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
st->cfg[i].resolution.ival =
|
|
st->hal->dev[i].scale[st->scale_i].resolution.ival;
|
|
st->cfg[i].resolution.fval =
|
|
st->hal->dev[i].scale[st->scale_i].resolution.fval;
|
|
st->cfg[i].milliamp.ival =
|
|
st->hal->dev[i].scale[st->scale_i].milliamp.ival;
|
|
st->cfg[i].milliamp.fval =
|
|
st->hal->dev[i].scale[st->scale_i].milliamp.fval;
|
|
us = st->hal->dev[i].scale[st->scale_i].delay_ms * 1000;
|
|
if (us < st->period_us_min)
|
|
us = st->period_us_min;
|
|
st->cfg[i].delay_us_min = us;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bmp_reset(void *client, int snsr_id)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
unsigned int enabled = st->enabled;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
bmp_dis(st);
|
|
bmp_en(st);
|
|
ret = bmp_wr(st, BMP_REG_RESET, BMP_REG_RESET_VAL);
|
|
if (!ret)
|
|
mdelay(BMP_HW_DELAY_POR_MS);
|
|
for (i = 0; i < BMP_DEV_N; i++)
|
|
bmp_enable(st, i, enabled & (1 << i));
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_regs(void *client, int snsr_id, char *buf)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
ssize_t t;
|
|
u8 data[11];
|
|
u16 *cal;
|
|
unsigned int i;
|
|
int ret;
|
|
|
|
if (!st->initd) {
|
|
t = snprintf(buf, PAGE_SIZE, "calibration: NEED ENABLE\n");
|
|
} else {
|
|
t = snprintf(buf, PAGE_SIZE, "calibration:\n");
|
|
cal = &st->rom.bmp280.dig_T1;
|
|
for (i = 0; i < st->hal->rom_size; i = i + 2)
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "%#2x=%#2x\n",
|
|
st->hal->rom_addr_start + i, *cal++);
|
|
}
|
|
ret = bmp_nvi_mpu_bypass_request(st);
|
|
if (!ret) {
|
|
ret = bmp_i2c_rd(st, BMP_REG_ID, 1, data);
|
|
ret |= bmp_i2c_rd(st, BMP280_REG_STATUS,
|
|
10, &data[1]);
|
|
bmp_nvi_mpu_bypass_release(st);
|
|
}
|
|
if (ret) {
|
|
t += snprintf(buf + t, PAGE_SIZE - t,
|
|
"registers: ERR %d\n", ret);
|
|
} else {
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "registers:\n");
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "%#2x=%#2x\n",
|
|
BMP_REG_ID, data[0]);
|
|
for (i = 0; i < 10; i++)
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "%#2x=%#2x\n",
|
|
BMP280_REG_STATUS + i, data[i + 1]);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
static int bmp_nvs_read(void *client, int snsr_id, char *buf)
|
|
{
|
|
struct bmp_state *st = (struct bmp_state *)client;
|
|
ssize_t t;
|
|
|
|
t = snprintf(buf, PAGE_SIZE, "driver v.%u\n", BMP_DRIVER_VERSION);
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "mpu_en=%x\n", st->mpu_en);
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "nvi_config=%hhu\n",
|
|
st->nvi_config);
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "cmode_enable=%x\n", st->cmode);
|
|
if (!st->scale_user)
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "scale_user=auto\n");
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "scale_i=%x\n", st->scale_i);
|
|
return t;
|
|
}
|
|
|
|
static struct nvs_fn_dev bmp_fn_dev = {
|
|
.enable = bmp_enable,
|
|
.batch = bmp_batch,
|
|
.batch_read = bmp_batch_read,
|
|
.flush = bmp_flush,
|
|
.resolution = bmp_resolution,
|
|
.reset = bmp_reset,
|
|
.regs = bmp_regs,
|
|
.nvs_read = bmp_nvs_read,
|
|
};
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int bmp_suspend(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct bmp_state *st = i2c_get_clientdata(client);
|
|
unsigned int i;
|
|
int ret = 0;
|
|
|
|
st->sts |= NVS_STS_SUSPEND;
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if (st->nvs && st->nvs_st[i])
|
|
ret |= st->nvs->suspend(st->nvs_st[i]);
|
|
}
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_resume(struct device *dev)
|
|
{
|
|
struct i2c_client *client = to_i2c_client(dev);
|
|
struct bmp_state *st = i2c_get_clientdata(client);
|
|
unsigned int i;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if (st->nvs && st->nvs_st[i])
|
|
ret |= st->nvs->resume(st->nvs_st[i]);
|
|
}
|
|
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(bmp_pm_ops, bmp_suspend, bmp_resume);
|
|
|
|
static void bmp_shutdown(struct i2c_client *client)
|
|
{
|
|
struct bmp_state *st = i2c_get_clientdata(client);
|
|
unsigned int i;
|
|
|
|
st->sts |= NVS_STS_SHUTDOWN;
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if (st->nvs && st->nvs_st[i])
|
|
st->nvs->shutdown(st->nvs_st[i]);
|
|
}
|
|
if (st->sts & NVS_STS_SPEW_MSG)
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
}
|
|
|
|
static int bmp_remove(struct i2c_client *client)
|
|
{
|
|
struct bmp_state *st = i2c_get_clientdata(client);
|
|
unsigned int i;
|
|
|
|
if (st != NULL) {
|
|
bmp_shutdown(client);
|
|
if (st->nvs) {
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
if (st->nvs_st[i]) {
|
|
st->nvs->remove(st->nvs_st[i]);
|
|
st->nvs_st[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
bmp_pm_exit(st);
|
|
if (st->wq) {
|
|
destroy_workqueue(st->wq);
|
|
st->wq = NULL;
|
|
}
|
|
}
|
|
dev_info(&client->dev, "%s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
struct sensor_cfg bmp_cfg_dflt[] = {
|
|
{
|
|
.name = "pressure",
|
|
.snsr_id = BMP_DEV_PRS,
|
|
.kbuf_sz = BMP_KBUF_SIZE,
|
|
.ch_n = 1,
|
|
.ch_sz = -4,
|
|
.part = BMP_NAME,
|
|
.vendor = BMP_VENDOR,
|
|
.version = 0,
|
|
.max_range = {
|
|
.ival = BMP_PRES_MAX_RANGE_IVAL,
|
|
.fval = BMP_PRES_MAX_RANGE_MICRO,
|
|
},
|
|
},
|
|
{
|
|
.name = "ambient_temperature",
|
|
.snsr_id = BMP_DEV_TMP,
|
|
.ch_n = 1,
|
|
.ch_sz = -4,
|
|
.part = BMP_NAME,
|
|
.vendor = BMP_VENDOR,
|
|
.version = 0,
|
|
.max_range = {
|
|
.ival = BMP_TEMP_MAX_RANGE_IVAL,
|
|
.fval = BMP_TEMP_MAX_RANGE_MICRO,
|
|
},
|
|
.flags = SENSOR_FLAG_ON_CHANGE_MODE,
|
|
},
|
|
};
|
|
|
|
static struct bmp_scale bmp_scale_prs_180[] = {
|
|
{
|
|
.delay_ms = 26,
|
|
.os = 0x03,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 10000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 12000,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 14,
|
|
.os = 0x02,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 10000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 7000,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 8,
|
|
.os = 0x01,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 10000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 5000,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 5,
|
|
.os = 0x00,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 10000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 3000,
|
|
},
|
|
},
|
|
};
|
|
|
|
static struct bmp_scale bmp_scale_tmp_180[] = {
|
|
{
|
|
.delay_ms = 26,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 100000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 12000,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 14,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 100000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 7000,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 8,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 100000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 5000,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 5,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 100000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 3000,
|
|
},
|
|
},
|
|
};
|
|
|
|
static struct bmp_hal_dev bmp_hal_dev_180[] = {
|
|
{
|
|
.version = 1,
|
|
.scale_n = ARRAY_SIZE(bmp_scale_prs_180),
|
|
.scale = bmp_scale_prs_180,
|
|
.scale_float = {
|
|
.ival = 0,
|
|
.fval = 10000,
|
|
},
|
|
},
|
|
{
|
|
.version = 1,
|
|
.scale_n = ARRAY_SIZE(bmp_scale_tmp_180),
|
|
.scale = bmp_scale_tmp_180,
|
|
.scale_float = {
|
|
.ival = 0,
|
|
.fval = 100000,
|
|
}
|
|
},
|
|
};
|
|
|
|
static struct bmp_hal bmp_hal_180 = {
|
|
.dev = bmp_hal_dev_180,
|
|
.part = BMP180_NAME,
|
|
.rom_addr_start = BMP180_REG_AC1,
|
|
.rom_size = 22,
|
|
.rom_big_endian = true,
|
|
.mode_mask = BMP180_REG_CTRL_MODE_MASK,
|
|
.cmode_tbl = NULL,
|
|
.bmp_read = &bmp_read_180,
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
.mpu_id = 0,
|
|
.port_rd_reg = BMP_REG_CTRL,
|
|
.port_rd_ctrl = 6,
|
|
.port_rd_handler = &bmp_mpu_handler_180,
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
};
|
|
|
|
static struct bmp_scale bmp_scale_prs_280[] = {
|
|
{
|
|
.delay_ms = 44,
|
|
.os = 0x05,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 1800,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 24800,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 23,
|
|
.os = 0x04,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 3600,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 12700,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 14,
|
|
.os = 0x03,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 7200,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 7020,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 9,
|
|
.os = 0x02,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 14300,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 4170,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 7,
|
|
.os = 0x01,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 28700,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 2740,
|
|
},
|
|
},
|
|
};
|
|
|
|
static struct bmp_scale bmp_scale_tmp_280[] = {
|
|
/* os oversampling is useless > 2x and only used for pressure @ 16x */
|
|
{
|
|
.delay_ms = 44,
|
|
.os = 0x02,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 300,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 24800,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 23,
|
|
.os = 0x01,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 600,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 12700,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 14,
|
|
.os = 0x01,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 1200,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 7020,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 9,
|
|
.os = 0x01,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 2500,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 4170,
|
|
},
|
|
},
|
|
{
|
|
.delay_ms = 7,
|
|
.os = 0x01,
|
|
.resolution = {
|
|
.ival = 0,
|
|
.fval = 5000,
|
|
},
|
|
.milliamp = {
|
|
.ival = 0,
|
|
.fval = 2740,
|
|
},
|
|
},
|
|
};
|
|
|
|
static struct bmp_hal_dev bmp_hal_dev_280[] = {
|
|
{
|
|
.version = 1,
|
|
.scale_n = ARRAY_SIZE(bmp_scale_prs_280),
|
|
.scale = bmp_scale_prs_280,
|
|
.scale_float = {
|
|
.ival = 0,
|
|
.fval = 10000,
|
|
}
|
|
},
|
|
{
|
|
.version = 1,
|
|
.scale_n = ARRAY_SIZE(bmp_scale_tmp_280),
|
|
.scale = bmp_scale_tmp_280,
|
|
.scale_float = {
|
|
.ival = 0,
|
|
.fval = 10000,
|
|
},
|
|
},
|
|
};
|
|
|
|
static struct bmp_cmode bmp_cmode_280[] = {
|
|
{
|
|
.t_us = 4000000,
|
|
.t_sb = 0x07,
|
|
},
|
|
{
|
|
.t_us = 2000000,
|
|
.t_sb = 0x06,
|
|
},
|
|
{
|
|
.t_us = 1000000,
|
|
.t_sb = 0x05,
|
|
},
|
|
{
|
|
.t_us = 500000,
|
|
.t_sb = 0x04,
|
|
},
|
|
{
|
|
.t_us = 250000,
|
|
.t_sb = 0x03,
|
|
},
|
|
{
|
|
.t_us = 125000,
|
|
.t_sb = 0x02,
|
|
},
|
|
{
|
|
.t_us = 62500,
|
|
.t_sb = 0x01,
|
|
},
|
|
{
|
|
.t_us = 500,
|
|
.t_sb = 0x00,
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct bmp_hal bmp_hal_280 = {
|
|
.dev = bmp_hal_dev_280,
|
|
.part = BMP280_NAME,
|
|
.rom_addr_start = BMP280_REG_CWORD00,
|
|
.rom_size = 26,
|
|
.rom_big_endian = false,
|
|
.mode_mask = BMP280_REG_CTRL_MODE_MASK,
|
|
.cmode_tbl = bmp_cmode_280,
|
|
.bmp_read = &bmp_read_280,
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
.mpu_id = PRESSURE_ID_BMP280,
|
|
.port_rd_reg = BMP280_REG_STATUS,
|
|
.port_rd_ctrl = 10,
|
|
.port_rd_handler = &bmp_mpu_handler_280,
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
};
|
|
|
|
static int bmp_id_hal(struct bmp_state *st)
|
|
{
|
|
unsigned int i;
|
|
int ret = 0;
|
|
|
|
switch (st->dev_id) {
|
|
case BMP_REG_ID_BMP280:
|
|
st->hal = &bmp_hal_280;
|
|
break;
|
|
|
|
case BMP_REG_ID_BMP180:
|
|
st->hal = &bmp_hal_180;
|
|
break;
|
|
|
|
default:
|
|
dev_err(&st->i2c->dev, "%s ERR: Unknown device\n", __func__);
|
|
st->hal = &bmp_hal_280; /* to prevent NULL pointers */
|
|
ret = -ENODEV;
|
|
break;
|
|
}
|
|
|
|
/* default scale to fastest and least power usage */
|
|
st->scale_user = st->hal->dev[BMP_DEV_PRS].scale_n;
|
|
st->scale_i = st->scale_user - 1;
|
|
if (st->hal->cmode_tbl == NULL)
|
|
st->cmode = false;
|
|
for (i = 0; i < BMP_DEV_N; i++)
|
|
memcpy(&st->cfg[i], &bmp_cfg_dflt[i],
|
|
sizeof(struct sensor_cfg));
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
st->cfg[i].part = st->hal->part;
|
|
st->cfg[i].version = st->hal->dev[i].version;
|
|
st->cfg[i].scale.ival = st->hal->dev[i].scale_float.ival;
|
|
st->cfg[i].scale.fval = st->hal->dev[i].scale_float.fval;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_id_compare(struct bmp_state *st, u8 val, const char *name)
|
|
{
|
|
unsigned int i;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bmp_ids); i++) {
|
|
if (val == bmp_ids[i]) {
|
|
if ((st->dev_id == BMP_REG_ID_BMP180) &&
|
|
(st->dev_id != val))
|
|
dev_err(&st->i2c->dev, "%s ERR: %x != %s\n",
|
|
__func__, st->dev_id, name);
|
|
if (val != BMP_REG_ID_BMP180)
|
|
/* BMP280 may have more ID's than 0x56 */
|
|
val = BMP_REG_ID_BMP280;
|
|
st->dev_id = val;
|
|
break;
|
|
}
|
|
}
|
|
if (!st->dev_id) {
|
|
ret = -ENODEV;
|
|
dev_err(&st->i2c->dev, "%s ERR: ID %x != %s\n",
|
|
__func__, val, name);
|
|
} else {
|
|
dev_dbg(&st->i2c->dev, "%s using ID %x for %s\n",
|
|
__func__, st->dev_id, name);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_id_dev(struct bmp_state *st, const char *name)
|
|
{
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
struct nvi_mpu_port nmp;
|
|
struct nvi_mpu_inf inf;
|
|
unsigned int i;
|
|
u8 config_boot;
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
u8 val = 0;
|
|
int ret;
|
|
|
|
if (!strcmp(name, BMP180_NAME))
|
|
st->dev_id = BMP_REG_ID_BMP180;
|
|
else if (!strcmp(name, BMP280_NAME))
|
|
st->dev_id = BMP_REG_ID_BMP280;
|
|
#if BMP_NVI_MPU_SUPPORT
|
|
config_boot = st->nvi_config & NVI_CONFIG_BOOT_MASK;
|
|
if ((config_boot == NVI_CONFIG_BOOT_MPU) && (!st->dev_id)) {
|
|
dev_err(&st->i2c->dev, "%s ERR: NVI_CONFIG_BOOT_MPU && %s\n",
|
|
__func__, name);
|
|
config_boot = NVI_CONFIG_BOOT_AUTO;
|
|
}
|
|
if (config_boot == NVI_CONFIG_BOOT_AUTO) {
|
|
nmp.addr = st->i2c_addr | 0x80;
|
|
nmp.reg = BMP_REG_ID;
|
|
nmp.ctrl = 1;
|
|
ret = nvi_mpu_dev_valid(&nmp, &val);
|
|
dev_info(&st->i2c->dev, "%s AUTO ID=%x ret=%d\n",
|
|
__func__, val, ret);
|
|
/* see mpu_iio.h for possible return values */
|
|
if ((ret == -EAGAIN) || (ret == -EBUSY))
|
|
return -EAGAIN;
|
|
|
|
if (!ret)
|
|
ret = bmp_id_compare(st, val, name);
|
|
if ((!ret) || ((ret == -EIO) && (st->dev_id)))
|
|
config_boot = NVI_CONFIG_BOOT_MPU;
|
|
}
|
|
if (config_boot == NVI_CONFIG_BOOT_MPU) {
|
|
st->mpu_en = true;
|
|
bmp_id_hal(st);
|
|
nmp.type = SECONDARY_SLAVE_TYPE_PRESSURE;
|
|
nmp.id = st->hal->mpu_id;
|
|
nmp.addr = st->i2c_addr; /* write port */
|
|
nmp.reg = BMP_REG_CTRL;
|
|
nmp.ctrl = 1;
|
|
nmp.data_out = st->data_out;
|
|
nmp.delay_ms = st->hal->dev[BMP_DEV_PRS].
|
|
scale[st->scale_i].delay_ms;
|
|
nmp.period_us = 0;
|
|
nmp.shutdown_bypass = false;
|
|
nmp.handler = NULL;
|
|
nmp.ext_driver = NULL;
|
|
ret = nvi_mpu_port_alloc(&nmp);
|
|
dev_dbg(&st->i2c->dev, "%s MPU port/ret=%d\n",
|
|
__func__, ret);
|
|
/* By requesting the write port first it allows us to
|
|
* automatically determine if the DMP requires a single
|
|
* port, in which case this port request will fail.
|
|
* If this part does not support continuous mode
|
|
* required for single port operation, then this device
|
|
* population fails.
|
|
*/
|
|
if (ret < 0) {
|
|
if (st->hal->cmode_tbl)
|
|
st->cmode = true;
|
|
else
|
|
return ret;
|
|
} else {
|
|
st->cmode = false;
|
|
st->port_id[WR] = ret;
|
|
}
|
|
|
|
nmp.addr = st->i2c_addr | 0x80; /* read port */
|
|
nmp.data_out = 0;
|
|
nmp.delay_ms = 0;
|
|
nmp.period_us = st->period_us;
|
|
if (st->cmode)
|
|
nmp.shutdown_bypass = true;
|
|
nmp.ext_driver = (void *)st;
|
|
nmp.reg = st->hal->port_rd_reg;
|
|
nmp.ctrl = st->hal->port_rd_ctrl;
|
|
nmp.handler = st->hal->port_rd_handler;
|
|
ret = nvi_mpu_port_alloc(&nmp);
|
|
dev_dbg(&st->i2c->dev, "%s MPU port/ret=%d\n",
|
|
__func__, ret);
|
|
if (ret < 0) {
|
|
bmp_ports_free(st);
|
|
dev_err(&st->i2c->dev, "%s ERR %d\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
st->port_id[RD] = ret;
|
|
ret = nvi_mpu_info(st->port_id[RD], &inf);
|
|
if (ret)
|
|
return ret;
|
|
|
|
st->period_us_min = inf.period_us_min;
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
st->cfg[i].fifo_rsrv_evnt_cnt = inf.fifo_reserve;
|
|
st->cfg[i].fifo_max_evnt_cnt = inf.fifo_max;
|
|
st->cfg[i].delay_us_max = inf.period_us_max;
|
|
}
|
|
st->dmp_rd_len_sts = inf.dmp_rd_len_sts;
|
|
st->dmp_rd_len_data = inf.dmp_rd_len_data;
|
|
return 0;
|
|
}
|
|
#endif /* BMP_NVI_MPU_SUPPORT */
|
|
/* NVI_CONFIG_BOOT_HOST */
|
|
st->mpu_en = false;
|
|
if (!st->dev_id) {
|
|
ret = bmp_i2c_rd(st, BMP_REG_ID, 1, &val);
|
|
dev_dbg(&st->i2c->dev, "%s Host read ID=%x ret=%d\n",
|
|
__func__, val, ret);
|
|
if (!ret)
|
|
bmp_id_compare(st, val, name);
|
|
}
|
|
ret = bmp_id_hal(st);
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_id_i2c(struct bmp_state *st,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
int i;
|
|
int ret;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(bmp_i2c_addrs); i++) {
|
|
if (st->i2c->addr == bmp_i2c_addrs[i])
|
|
break;
|
|
}
|
|
|
|
if (i < ARRAY_SIZE(bmp_i2c_addrs)) {
|
|
st->i2c_addr = st->i2c->addr;
|
|
ret = bmp_id_dev(st, id->name);
|
|
} else {
|
|
for (i = 0; i < ARRAY_SIZE(bmp_i2c_addrs); i++) {
|
|
st->i2c_addr = bmp_i2c_addrs[i];
|
|
ret = bmp_id_dev(st, BMP_NAME);
|
|
if ((ret == -EAGAIN) || (!ret))
|
|
break;
|
|
}
|
|
}
|
|
if (ret)
|
|
st->i2c_addr = 0;
|
|
return ret;
|
|
}
|
|
|
|
static int bmp_of_dt(struct bmp_state *st, struct device_node *dn)
|
|
{
|
|
char const *pchar;
|
|
u8 cfg;
|
|
int ret;
|
|
|
|
if (dn) {
|
|
/* just test if global disable */
|
|
ret = nvs_of_dt(dn, NULL, NULL);
|
|
if (ret == -ENODEV)
|
|
return -ENODEV;
|
|
|
|
/* this device supports these programmable parameters */
|
|
if (!of_property_read_string(dn, "nvi_config", &pchar)) {
|
|
for (cfg = 0; cfg < ARRAY_SIZE(bmp_configs); cfg++) {
|
|
if (!strcasecmp(pchar, bmp_configs[cfg])) {
|
|
st->nvi_config = cfg;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!of_property_read_u8(dn, "cmode_enable", &cfg)) {
|
|
if (cfg)
|
|
st->cmode = true;
|
|
else
|
|
st->cmode = false;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int bmp_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct bmp_state *st;
|
|
unsigned int i;
|
|
unsigned int n;
|
|
int ret;
|
|
|
|
dev_info(&client->dev, "%s %s\n", id->name, __func__);
|
|
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 = bmp_of_dt(st, client->dev.of_node);
|
|
if (ret) {
|
|
if (ret == -ENODEV) {
|
|
dev_info(&client->dev, "%s DT disabled\n", __func__);
|
|
} else {
|
|
dev_err(&client->dev, "%s _of_dt ERR\n", __func__);
|
|
ret = -ENODEV;
|
|
}
|
|
goto bmp_probe_err;
|
|
}
|
|
|
|
bmp_pm_init(st);
|
|
ret = bmp_id_i2c(st, id);
|
|
if (ret == -EAGAIN) {
|
|
ret = -EPROBE_DEFER;
|
|
goto bmp_probe_exit;
|
|
} else if (ret) {
|
|
goto bmp_probe_err;
|
|
}
|
|
|
|
bmp_init_hw(st);
|
|
bmp_pm(st, false);
|
|
bmp_fn_dev.errs = &st->errs;
|
|
bmp_fn_dev.sts = &st->sts;
|
|
st->nvs = nvs_iio();
|
|
if (st->nvs == NULL) {
|
|
ret = -ENODEV;
|
|
goto bmp_probe_err;
|
|
}
|
|
|
|
st->cfg[BMP_DEV_PRS].resolution.ival = st->scale_user;
|
|
nvs_of_dt(client->dev.of_node, &st->cfg[BMP_DEV_PRS], NULL);
|
|
bmp_resolution(st, BMP_DEV_PRS, st->cfg[BMP_DEV_PRS].resolution.ival);
|
|
nvs_of_dt(client->dev.of_node, &st->cfg[BMP_DEV_TMP], NULL);
|
|
if (st->cfg[BMP_DEV_PRS].snsr_id < 0)
|
|
/* pressure disabled so use temperature settings */
|
|
bmp_resolution(st, BMP_DEV_PRS,
|
|
st->cfg[BMP_DEV_TMP].resolution.ival);
|
|
n = 0;
|
|
for (i = 0; i < BMP_DEV_N; i++) {
|
|
ret = st->nvs->probe(&st->nvs_st[i], st, &client->dev,
|
|
&bmp_fn_dev, &st->cfg[i]);
|
|
if (!ret)
|
|
n++;
|
|
}
|
|
if (!n) {
|
|
ret = -ENODEV;
|
|
goto bmp_probe_exit;
|
|
}
|
|
|
|
if (!st->mpu_en) {
|
|
st->wq = create_workqueue(BMP_NAME);
|
|
if (!st->wq) {
|
|
ret = -ENOMEM;
|
|
goto bmp_probe_err;
|
|
}
|
|
|
|
INIT_WORK(&st->ws, bmp_work);
|
|
}
|
|
dev_info(&client->dev, "%s done\n", __func__);
|
|
return 0;
|
|
|
|
bmp_probe_err:
|
|
dev_err(&client->dev, "%s ERR %d\n", __func__, ret);
|
|
bmp_probe_exit:
|
|
bmp_remove(client);
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_device_id bmp_i2c_device_id[] = {
|
|
{ BMP_NAME, 0 },
|
|
{ BMP180_NAME, 0 },
|
|
{ BMP280_NAME, 0 },
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, bmp_i2c_device_id);
|
|
|
|
static const struct of_device_id bmp_of_match[] = {
|
|
{ .compatible = "bmp,bmpX80", },
|
|
{ .compatible = "bmp,bmp180", },
|
|
{ .compatible = "bmp,bmp280", },
|
|
{},
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, bmp_of_match);
|
|
|
|
static struct i2c_driver bmp_driver = {
|
|
.class = I2C_CLASS_HWMON,
|
|
.probe = bmp_probe,
|
|
.remove = bmp_remove,
|
|
.shutdown = bmp_shutdown,
|
|
.driver = {
|
|
.name = BMP_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(bmp_of_match),
|
|
.pm = &bmp_pm_ops,
|
|
},
|
|
.id_table = bmp_i2c_device_id,
|
|
};
|
|
|
|
static int __init bmp_init(void)
|
|
{
|
|
return i2c_add_driver(&bmp_driver);
|
|
}
|
|
|
|
static void __exit bmp_exit(void)
|
|
{
|
|
i2c_del_driver(&bmp_driver);
|
|
}
|
|
|
|
late_initcall(bmp_init);
|
|
module_exit(bmp_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("BMPX80 driver");
|
|
MODULE_AUTHOR("NVIDIA Corporation");
|
|
|