2018-07-26 04:35:10 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Bosch BME680 - Temperature, Pressure, Humidity & Gas Sensor
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
|
|
|
|
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
|
|
|
|
*
|
|
|
|
* Datasheet:
|
|
|
|
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
|
|
|
|
*/
|
|
|
|
#include <linux/bitfield.h>
|
2024-06-09 16:38:12 -07:00
|
|
|
#include <linux/cleanup.h>
|
2024-06-06 14:22:56 -07:00
|
|
|
#include <linux/delay.h>
|
2018-07-26 04:35:10 -07:00
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/log2.h>
|
2024-06-09 16:38:16 -07:00
|
|
|
#include <linux/module.h>
|
2018-07-26 04:35:10 -07:00
|
|
|
#include <linux/regmap.h>
|
2024-06-09 16:38:16 -07:00
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
#include <linux/iio/iio.h>
|
|
|
|
#include <linux/iio/sysfs.h>
|
|
|
|
|
2024-10-01 12:35:57 -07:00
|
|
|
#include <linux/unaligned.h>
|
2024-06-09 16:38:18 -07:00
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
#include "bme680.h"
|
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
/* 1st set of calibration data */
|
|
|
|
enum {
|
|
|
|
/* Temperature calib indexes */
|
|
|
|
T2_LSB = 0,
|
|
|
|
T3 = 2,
|
|
|
|
/* Pressure calib indexes */
|
|
|
|
P1_LSB = 4,
|
|
|
|
P2_LSB = 6,
|
|
|
|
P3 = 8,
|
|
|
|
P4_LSB = 10,
|
|
|
|
P5_LSB = 12,
|
|
|
|
P7 = 14,
|
|
|
|
P6 = 15,
|
|
|
|
P8_LSB = 18,
|
|
|
|
P9_LSB = 20,
|
|
|
|
P10 = 22,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 2nd set of calibration data */
|
|
|
|
enum {
|
|
|
|
/* Humidity calib indexes */
|
|
|
|
H2_MSB = 0,
|
|
|
|
H1_LSB = 1,
|
|
|
|
H3 = 3,
|
|
|
|
H4 = 4,
|
|
|
|
H5 = 5,
|
|
|
|
H6 = 6,
|
|
|
|
H7 = 7,
|
|
|
|
/* Stray T1 calib index */
|
|
|
|
T1_LSB = 8,
|
|
|
|
/* Gas heater calib indexes */
|
|
|
|
GH2_LSB = 10,
|
|
|
|
GH1 = 12,
|
|
|
|
GH3 = 13,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* 3rd set of calibration data */
|
|
|
|
enum {
|
|
|
|
RES_HEAT_VAL = 0,
|
|
|
|
RES_HEAT_RANGE = 2,
|
|
|
|
RANGE_SW_ERR = 4,
|
|
|
|
};
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
struct bme680_calib {
|
|
|
|
u16 par_t1;
|
|
|
|
s16 par_t2;
|
|
|
|
s8 par_t3;
|
|
|
|
u16 par_p1;
|
|
|
|
s16 par_p2;
|
|
|
|
s8 par_p3;
|
|
|
|
s16 par_p4;
|
|
|
|
s16 par_p5;
|
|
|
|
s8 par_p6;
|
|
|
|
s8 par_p7;
|
|
|
|
s16 par_p8;
|
|
|
|
s16 par_p9;
|
|
|
|
u8 par_p10;
|
|
|
|
u16 par_h1;
|
|
|
|
u16 par_h2;
|
|
|
|
s8 par_h3;
|
|
|
|
s8 par_h4;
|
|
|
|
s8 par_h5;
|
2024-06-06 14:22:54 -07:00
|
|
|
u8 par_h6;
|
2018-07-26 04:35:10 -07:00
|
|
|
s8 par_h7;
|
|
|
|
s8 par_gh1;
|
|
|
|
s16 par_gh2;
|
|
|
|
s8 par_gh3;
|
|
|
|
u8 res_heat_range;
|
|
|
|
s8 res_heat_val;
|
|
|
|
s8 range_sw_err;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct bme680_data {
|
|
|
|
struct regmap *regmap;
|
|
|
|
struct bme680_calib bme680;
|
2024-06-09 16:38:12 -07:00
|
|
|
struct mutex lock; /* Protect multiple serial R/W ops to device. */
|
2018-07-26 04:35:10 -07:00
|
|
|
u8 oversampling_temp;
|
|
|
|
u8 oversampling_press;
|
|
|
|
u8 oversampling_humid;
|
|
|
|
u16 heater_dur;
|
|
|
|
u16 heater_temp;
|
2024-06-09 16:38:18 -07:00
|
|
|
|
|
|
|
union {
|
2024-06-09 16:38:20 -07:00
|
|
|
u8 buf[3];
|
|
|
|
unsigned int check;
|
|
|
|
__be16 be16;
|
2024-06-09 16:38:18 -07:00
|
|
|
u8 bme680_cal_buf_1[BME680_CALIB_RANGE_1_LEN];
|
|
|
|
u8 bme680_cal_buf_2[BME680_CALIB_RANGE_2_LEN];
|
|
|
|
u8 bme680_cal_buf_3[BME680_CALIB_RANGE_3_LEN];
|
|
|
|
};
|
2018-07-26 04:35:10 -07:00
|
|
|
};
|
|
|
|
|
2019-03-06 00:31:48 -07:00
|
|
|
static const struct regmap_range bme680_volatile_ranges[] = {
|
|
|
|
regmap_reg_range(BME680_REG_MEAS_STAT_0, BME680_REG_GAS_R_LSB),
|
|
|
|
regmap_reg_range(BME680_REG_STATUS, BME680_REG_STATUS),
|
|
|
|
regmap_reg_range(BME680_T2_LSB_REG, BME680_GH3_REG),
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct regmap_access_table bme680_volatile_table = {
|
|
|
|
.yes_ranges = bme680_volatile_ranges,
|
|
|
|
.n_yes_ranges = ARRAY_SIZE(bme680_volatile_ranges),
|
|
|
|
};
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
const struct regmap_config bme680_regmap_config = {
|
|
|
|
.reg_bits = 8,
|
|
|
|
.val_bits = 8,
|
2019-03-06 00:31:48 -07:00
|
|
|
.max_register = 0xef,
|
|
|
|
.volatile_table = &bme680_volatile_table,
|
|
|
|
.cache_type = REGCACHE_RBTREE,
|
2018-07-26 04:35:10 -07:00
|
|
|
};
|
2022-01-30 13:57:01 -07:00
|
|
|
EXPORT_SYMBOL_NS(bme680_regmap_config, IIO_BME680);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
static const struct iio_chan_spec bme680_channels[] = {
|
|
|
|
{
|
|
|
|
.type = IIO_TEMP,
|
|
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
|
|
|
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.type = IIO_PRESSURE,
|
|
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
|
|
|
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.type = IIO_HUMIDITYRELATIVE,
|
|
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
|
|
|
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.type = IIO_RESISTANCE,
|
|
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int bme680_read_calib(struct bme680_data *data,
|
|
|
|
struct bme680_calib *calib)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
2024-06-09 16:38:18 -07:00
|
|
|
unsigned int tmp_msb, tmp_lsb;
|
2018-07-26 04:35:10 -07:00
|
|
|
int ret;
|
|
|
|
|
2020-04-05 11:03:18 -07:00
|
|
|
ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG,
|
2024-06-09 16:38:18 -07:00
|
|
|
data->bme680_cal_buf_1,
|
|
|
|
sizeof(data->bme680_cal_buf_1));
|
2018-07-26 04:35:10 -07:00
|
|
|
if (ret < 0) {
|
2024-06-09 16:38:18 -07:00
|
|
|
dev_err(dev, "failed to read 1st set of calib data;\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
calib->par_t2 = get_unaligned_le16(&data->bme680_cal_buf_1[T2_LSB]);
|
|
|
|
calib->par_t3 = data->bme680_cal_buf_1[T3];
|
|
|
|
calib->par_p1 = get_unaligned_le16(&data->bme680_cal_buf_1[P1_LSB]);
|
|
|
|
calib->par_p2 = get_unaligned_le16(&data->bme680_cal_buf_1[P2_LSB]);
|
|
|
|
calib->par_p3 = data->bme680_cal_buf_1[P3];
|
|
|
|
calib->par_p4 = get_unaligned_le16(&data->bme680_cal_buf_1[P4_LSB]);
|
|
|
|
calib->par_p5 = get_unaligned_le16(&data->bme680_cal_buf_1[P5_LSB]);
|
|
|
|
calib->par_p7 = data->bme680_cal_buf_1[P7];
|
|
|
|
calib->par_p6 = data->bme680_cal_buf_1[P6];
|
|
|
|
calib->par_p8 = get_unaligned_le16(&data->bme680_cal_buf_1[P8_LSB]);
|
|
|
|
calib->par_p9 = get_unaligned_le16(&data->bme680_cal_buf_1[P9_LSB]);
|
|
|
|
calib->par_p10 = data->bme680_cal_buf_1[P10];
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
ret = regmap_bulk_read(data->regmap, BME680_H2_MSB_REG,
|
|
|
|
data->bme680_cal_buf_2,
|
|
|
|
sizeof(data->bme680_cal_buf_2));
|
2018-07-26 04:35:10 -07:00
|
|
|
if (ret < 0) {
|
2024-06-09 16:38:18 -07:00
|
|
|
dev_err(dev, "failed to read 2nd set of calib data;\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
tmp_lsb = data->bme680_cal_buf_2[H1_LSB];
|
|
|
|
tmp_msb = data->bme680_cal_buf_2[H1_LSB + 1];
|
2018-07-26 04:35:10 -07:00
|
|
|
calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
2018-08-17 12:03:16 -07:00
|
|
|
(tmp_lsb & BME680_BIT_H1_DATA_MASK);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
tmp_msb = data->bme680_cal_buf_2[H2_MSB];
|
|
|
|
tmp_lsb = data->bme680_cal_buf_2[H2_MSB + 1];
|
2018-07-26 04:35:10 -07:00
|
|
|
calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
|
2018-08-17 12:03:14 -07:00
|
|
|
(tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
calib->par_h3 = data->bme680_cal_buf_2[H3];
|
|
|
|
calib->par_h4 = data->bme680_cal_buf_2[H4];
|
|
|
|
calib->par_h5 = data->bme680_cal_buf_2[H5];
|
|
|
|
calib->par_h6 = data->bme680_cal_buf_2[H6];
|
|
|
|
calib->par_h7 = data->bme680_cal_buf_2[H7];
|
|
|
|
calib->par_t1 = get_unaligned_le16(&data->bme680_cal_buf_2[T1_LSB]);
|
|
|
|
calib->par_gh2 = get_unaligned_le16(&data->bme680_cal_buf_2[GH2_LSB]);
|
|
|
|
calib->par_gh1 = data->bme680_cal_buf_2[GH1];
|
|
|
|
calib->par_gh3 = data->bme680_cal_buf_2[GH3];
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
ret = regmap_bulk_read(data->regmap, BME680_REG_RES_HEAT_VAL,
|
|
|
|
data->bme680_cal_buf_3,
|
|
|
|
sizeof(data->bme680_cal_buf_3));
|
2018-07-26 04:35:10 -07:00
|
|
|
if (ret < 0) {
|
2024-06-09 16:38:18 -07:00
|
|
|
dev_err(dev, "failed to read 3rd set of calib data;\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
calib->res_heat_val = data->bme680_cal_buf_3[RES_HEAT_VAL];
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK,
|
|
|
|
data->bme680_cal_buf_3[RES_HEAT_RANGE]);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:18 -07:00
|
|
|
calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK,
|
|
|
|
data->bme680_cal_buf_3[RANGE_SW_ERR]);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
static int bme680_read_temp_adc(struct bme680_data *data, u32 *adc_temp)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
u32 value_temp;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
|
|
|
|
data->buf, BME680_TEMP_NUM_BYTES);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "failed to read temperature\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
value_temp = FIELD_GET(BME680_MEAS_TRIM_MASK,
|
|
|
|
get_unaligned_be24(data->buf));
|
|
|
|
if (value_temp == BME680_MEAS_SKIPPED) {
|
|
|
|
/* reading was skipped */
|
|
|
|
dev_err(dev, "reading temperature skipped\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
*adc_temp = value_temp;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
/*
|
|
|
|
* Taken from Bosch BME680 API:
|
|
|
|
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L876
|
|
|
|
*
|
|
|
|
* Returns temperature measurement in DegC, resolutions is 0.01 DegC. Therefore,
|
|
|
|
* output value of "3233" represents 32.33 DegC.
|
|
|
|
*/
|
2024-06-09 16:38:26 -07:00
|
|
|
static s32 bme680_calc_t_fine(struct bme680_data *data, u32 adc_temp)
|
2018-07-26 04:35:10 -07:00
|
|
|
{
|
|
|
|
struct bme680_calib *calib = &data->bme680;
|
|
|
|
s64 var1, var2, var3;
|
|
|
|
|
2019-03-06 00:31:48 -07:00
|
|
|
/* If the calibration is invalid, attempt to reload it */
|
|
|
|
if (!calib->par_t2)
|
|
|
|
bme680_read_calib(data, calib);
|
|
|
|
|
2024-06-09 16:38:14 -07:00
|
|
|
var1 = ((s32)adc_temp >> 3) - ((s32)calib->par_t1 << 1);
|
2018-07-26 04:35:10 -07:00
|
|
|
var2 = (var1 * calib->par_t2) >> 11;
|
|
|
|
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
|
2024-06-06 14:22:55 -07:00
|
|
|
var3 = (var3 * ((s32)calib->par_t3 << 4)) >> 14;
|
2024-06-09 16:38:26 -07:00
|
|
|
return var2 + var3; /* t_fine = var2 + var3 */
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_get_t_fine(struct bme680_data *data, s32 *t_fine)
|
|
|
|
{
|
|
|
|
u32 adc_temp;
|
|
|
|
int ret;
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
ret = bme680_read_temp_adc(data, &adc_temp);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*t_fine = bme680_calc_t_fine(data, adc_temp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static s16 bme680_compensate_temp(struct bme680_data *data,
|
|
|
|
u32 adc_temp)
|
|
|
|
{
|
|
|
|
return (bme680_calc_t_fine(data, adc_temp) * 5 + 128) / 256;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_read_press_adc(struct bme680_data *data, u32 *adc_press)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
u32 value_press;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
|
|
|
|
data->buf, BME680_PRESS_NUM_BYTES);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "failed to read pressure\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
value_press = FIELD_GET(BME680_MEAS_TRIM_MASK,
|
|
|
|
get_unaligned_be24(data->buf));
|
|
|
|
if (value_press == BME680_MEAS_SKIPPED) {
|
|
|
|
/* reading was skipped */
|
|
|
|
dev_err(dev, "reading pressure skipped\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
*adc_press = value_press;
|
|
|
|
|
|
|
|
return 0;
|
2018-07-26 04:35:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Taken from Bosch BME680 API:
|
|
|
|
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L896
|
|
|
|
*
|
|
|
|
* Returns pressure measurement in Pa. Output value of "97356" represents
|
|
|
|
* 97356 Pa = 973.56 hPa.
|
|
|
|
*/
|
|
|
|
static u32 bme680_compensate_press(struct bme680_data *data,
|
2024-06-09 16:38:26 -07:00
|
|
|
u32 adc_press, s32 t_fine)
|
2018-07-26 04:35:10 -07:00
|
|
|
{
|
|
|
|
struct bme680_calib *calib = &data->bme680;
|
|
|
|
s32 var1, var2, var3, press_comp;
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
var1 = (t_fine >> 1) - 64000;
|
2018-07-26 04:35:10 -07:00
|
|
|
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2;
|
|
|
|
var2 = var2 + (var1 * calib->par_p5 << 1);
|
2024-06-06 14:22:55 -07:00
|
|
|
var2 = (var2 >> 2) + ((s32)calib->par_p4 << 16);
|
2018-07-26 04:35:10 -07:00
|
|
|
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
|
2024-06-06 14:22:55 -07:00
|
|
|
((s32)calib->par_p3 << 5)) >> 3) +
|
2018-07-26 04:35:10 -07:00
|
|
|
((calib->par_p2 * var1) >> 1);
|
|
|
|
var1 = var1 >> 18;
|
|
|
|
var1 = ((32768 + var1) * calib->par_p1) >> 15;
|
|
|
|
press_comp = 1048576 - adc_press;
|
|
|
|
press_comp = ((press_comp - (var2 >> 12)) * 3125);
|
|
|
|
|
|
|
|
if (press_comp >= BME680_MAX_OVERFLOW_VAL)
|
|
|
|
press_comp = ((press_comp / (u32)var1) << 1);
|
|
|
|
else
|
|
|
|
press_comp = ((press_comp << 1) / (u32)var1);
|
|
|
|
|
|
|
|
var1 = (calib->par_p9 * (((press_comp >> 3) *
|
|
|
|
(press_comp >> 3)) >> 13)) >> 12;
|
|
|
|
var2 = ((press_comp >> 2) * calib->par_p8) >> 13;
|
|
|
|
var3 = ((press_comp >> 8) * (press_comp >> 8) *
|
|
|
|
(press_comp >> 8) * calib->par_p10) >> 17;
|
|
|
|
|
2024-06-06 14:22:55 -07:00
|
|
|
press_comp += (var1 + var2 + var3 + ((s32)calib->par_p7 << 7)) >> 4;
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
return press_comp;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
static int bme680_read_humid_adc(struct bme680_data *data, u32 *adc_humidity)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
u32 value_humidity;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = regmap_bulk_read(data->regmap, BME680_REG_HUMIDITY_MSB,
|
|
|
|
&data->be16, BME680_HUMID_NUM_BYTES);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "failed to read humidity\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
value_humidity = be16_to_cpu(data->be16);
|
|
|
|
if (value_humidity == BME680_MEAS_SKIPPED) {
|
|
|
|
/* reading was skipped */
|
|
|
|
dev_err(dev, "reading humidity skipped\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
*adc_humidity = value_humidity;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
/*
|
|
|
|
* Taken from Bosch BME680 API:
|
|
|
|
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L937
|
|
|
|
*
|
|
|
|
* Returns humidity measurement in percent, resolution is 0.001 percent. Output
|
|
|
|
* value of "43215" represents 43.215 %rH.
|
|
|
|
*/
|
|
|
|
static u32 bme680_compensate_humid(struct bme680_data *data,
|
2024-06-09 16:38:26 -07:00
|
|
|
u16 adc_humid, s32 t_fine)
|
2018-07-26 04:35:10 -07:00
|
|
|
{
|
|
|
|
struct bme680_calib *calib = &data->bme680;
|
|
|
|
s32 var1, var2, var3, var4, var5, var6, temp_scaled, calc_hum;
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
temp_scaled = (t_fine * 5 + 128) >> 8;
|
2024-06-09 16:38:14 -07:00
|
|
|
var1 = (adc_humid - (((s32)calib->par_h1 * 16))) -
|
|
|
|
(((temp_scaled * calib->par_h3) / 100) >> 1);
|
|
|
|
var2 = (calib->par_h2 *
|
2018-07-26 04:35:10 -07:00
|
|
|
(((temp_scaled * calib->par_h4) / 100) +
|
|
|
|
(((temp_scaled * ((temp_scaled * calib->par_h5) / 100))
|
|
|
|
>> 6) / 100) + (1 << 14))) >> 10;
|
|
|
|
var3 = var1 * var2;
|
2024-06-06 14:22:55 -07:00
|
|
|
var4 = (s32)calib->par_h6 << 7;
|
2018-07-26 04:35:10 -07:00
|
|
|
var4 = (var4 + ((temp_scaled * calib->par_h7) / 100)) >> 4;
|
|
|
|
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
|
|
|
|
var6 = (var4 * var5) >> 1;
|
|
|
|
calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
|
|
|
|
|
2018-08-17 12:03:13 -07:00
|
|
|
calc_hum = clamp(calc_hum, 0, 100000); /* clamp between 0-100 %rH */
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
return calc_hum;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Taken from Bosch BME680 API:
|
|
|
|
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L973
|
|
|
|
*
|
|
|
|
* Returns gas measurement in Ohm. Output value of "82986" represent 82986 ohms.
|
|
|
|
*/
|
|
|
|
static u32 bme680_compensate_gas(struct bme680_data *data, u16 gas_res_adc,
|
|
|
|
u8 gas_range)
|
|
|
|
{
|
|
|
|
struct bme680_calib *calib = &data->bme680;
|
|
|
|
s64 var1;
|
|
|
|
u64 var2;
|
|
|
|
s64 var3;
|
|
|
|
u32 calc_gas_res;
|
|
|
|
|
|
|
|
/* Look up table for the possible gas range values */
|
2024-07-25 16:18:18 -07:00
|
|
|
static const u32 lookupTable[16] = {2147483647u, 2147483647u,
|
2018-07-26 04:35:10 -07:00
|
|
|
2147483647u, 2147483647u, 2147483647u,
|
|
|
|
2126008810u, 2147483647u, 2130303777u,
|
|
|
|
2147483647u, 2147483647u, 2143188679u,
|
|
|
|
2136746228u, 2147483647u, 2126008810u,
|
|
|
|
2147483647u, 2147483647u};
|
|
|
|
|
|
|
|
var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
|
|
|
|
((s64) lookupTable[gas_range])) >> 16;
|
|
|
|
var2 = ((gas_res_adc << 15) - 16777216) + var1;
|
|
|
|
var3 = ((125000 << (15 - gas_range)) * var1) >> 9;
|
|
|
|
var3 += (var2 >> 1);
|
|
|
|
calc_gas_res = div64_s64(var3, (s64) var2);
|
|
|
|
|
|
|
|
return calc_gas_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Taken from Bosch BME680 API:
|
|
|
|
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1002
|
|
|
|
*/
|
|
|
|
static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
|
|
|
|
{
|
|
|
|
struct bme680_calib *calib = &data->bme680;
|
|
|
|
s32 var1, var2, var3, var4, var5, heatr_res_x100;
|
|
|
|
u8 heatr_res;
|
|
|
|
|
|
|
|
if (temp > 400) /* Cap temperature */
|
|
|
|
temp = 400;
|
|
|
|
|
|
|
|
var1 = (((s32) BME680_AMB_TEMP * calib->par_gh3) / 1000) * 256;
|
|
|
|
var2 = (calib->par_gh1 + 784) * (((((calib->par_gh2 + 154009) *
|
|
|
|
temp * 5) / 100)
|
|
|
|
+ 3276800) / 10);
|
|
|
|
var3 = var1 + (var2 / 2);
|
|
|
|
var4 = (var3 / (calib->res_heat_range + 4));
|
|
|
|
var5 = 131 * calib->res_heat_val + 65536;
|
|
|
|
heatr_res_x100 = ((var4 / var5) - 250) * 34;
|
2020-12-27 10:11:25 -07:00
|
|
|
heatr_res = DIV_ROUND_CLOSEST(heatr_res_x100, 100);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
return heatr_res;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Taken from Bosch BME680 API:
|
|
|
|
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1188
|
|
|
|
*/
|
|
|
|
static u8 bme680_calc_heater_dur(u16 dur)
|
|
|
|
{
|
|
|
|
u8 durval, factor = 0;
|
|
|
|
|
|
|
|
if (dur >= 0xfc0) {
|
|
|
|
durval = 0xff; /* Max duration */
|
|
|
|
} else {
|
|
|
|
while (dur > 0x3F) {
|
|
|
|
dur = dur / 4;
|
|
|
|
factor += 1;
|
|
|
|
}
|
|
|
|
durval = dur + (factor * 64);
|
|
|
|
}
|
|
|
|
|
|
|
|
return durval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_set_mode(struct bme680_data *data, bool mode)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (mode) {
|
|
|
|
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
|
|
|
BME680_MODE_MASK, BME680_MODE_FORCED);
|
|
|
|
if (ret < 0)
|
|
|
|
dev_err(dev, "failed to set forced mode\n");
|
|
|
|
|
|
|
|
} else {
|
|
|
|
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
|
|
|
BME680_MODE_MASK, BME680_MODE_SLEEP);
|
|
|
|
if (ret < 0)
|
|
|
|
dev_err(dev, "failed to set sleep mode\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-08-17 12:03:19 -07:00
|
|
|
static u8 bme680_oversampling_to_reg(u8 val)
|
|
|
|
{
|
|
|
|
return ilog2(val) + 1;
|
|
|
|
}
|
|
|
|
|
2024-06-06 14:22:56 -07:00
|
|
|
/*
|
|
|
|
* Taken from Bosch BME680 API:
|
|
|
|
* https://github.com/boschsensortec/BME68x_SensorAPI/blob/v4.4.8/bme68x.c#L490
|
|
|
|
*/
|
|
|
|
static int bme680_wait_for_eoc(struct bme680_data *data)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
int ret;
|
|
|
|
/*
|
|
|
|
* (Sum of oversampling ratios * time per oversampling) +
|
|
|
|
* TPH measurement + gas measurement + wait transition from forced mode
|
|
|
|
* + heater duration
|
|
|
|
*/
|
|
|
|
int wait_eoc_us = ((data->oversampling_temp + data->oversampling_press +
|
|
|
|
data->oversampling_humid) * 1936) + (477 * 4) +
|
|
|
|
(477 * 5) + 1000 + (data->heater_dur * 1000);
|
|
|
|
|
|
|
|
usleep_range(wait_eoc_us, wait_eoc_us + 100);
|
|
|
|
|
2024-06-09 16:38:20 -07:00
|
|
|
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &data->check);
|
2024-06-06 14:22:56 -07:00
|
|
|
if (ret) {
|
|
|
|
dev_err(dev, "failed to read measurement status register.\n");
|
|
|
|
return ret;
|
|
|
|
}
|
2024-06-09 16:38:20 -07:00
|
|
|
if (data->check & BME680_MEAS_BIT) {
|
2024-06-06 14:22:56 -07:00
|
|
|
dev_err(dev, "Device measurement cycle incomplete.\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2024-06-09 16:38:20 -07:00
|
|
|
if (!(data->check & BME680_NEW_DATA_BIT)) {
|
2024-06-06 14:22:56 -07:00
|
|
|
dev_err(dev, "No new data available from the device.\n");
|
|
|
|
return -ENODATA;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
static int bme680_chip_config(struct bme680_data *data)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
int ret;
|
2018-08-17 12:03:19 -07:00
|
|
|
u8 osrs;
|
|
|
|
|
|
|
|
osrs = FIELD_PREP(
|
|
|
|
BME680_OSRS_HUMIDITY_MASK,
|
|
|
|
bme680_oversampling_to_reg(data->oversampling_humid));
|
2018-07-26 04:35:10 -07:00
|
|
|
/*
|
|
|
|
* Highly recommended to set oversampling of humidity before
|
|
|
|
* temperature/pressure oversampling.
|
|
|
|
*/
|
|
|
|
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_HUMIDITY,
|
|
|
|
BME680_OSRS_HUMIDITY_MASK, osrs);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "failed to write ctrl_hum register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IIR filter settings */
|
|
|
|
ret = regmap_update_bits(data->regmap, BME680_REG_CONFIG,
|
|
|
|
BME680_FILTER_MASK,
|
|
|
|
BME680_FILTER_COEFF_VAL);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "failed to write config register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-08-17 12:03:19 -07:00
|
|
|
osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK,
|
|
|
|
bme680_oversampling_to_reg(data->oversampling_temp)) |
|
|
|
|
FIELD_PREP(BME680_OSRS_PRESS_MASK,
|
|
|
|
bme680_oversampling_to_reg(data->oversampling_press));
|
2018-07-26 04:35:10 -07:00
|
|
|
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
|
2018-08-17 12:03:19 -07:00
|
|
|
BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK,
|
2018-07-26 04:35:10 -07:00
|
|
|
osrs);
|
2024-06-09 16:38:21 -07:00
|
|
|
if (ret < 0) {
|
2018-07-26 04:35:10 -07:00
|
|
|
dev_err(dev, "failed to write ctrl_meas register\n");
|
2024-06-09 16:38:21 -07:00
|
|
|
return ret;
|
|
|
|
}
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:21 -07:00
|
|
|
return 0;
|
2018-07-26 04:35:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_gas_config(struct bme680_data *data)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
int ret;
|
|
|
|
u8 heatr_res, heatr_dur;
|
|
|
|
|
2024-06-09 16:38:22 -07:00
|
|
|
/* Go to sleep */
|
|
|
|
ret = bme680_set_mode(data, false);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
heatr_res = bme680_calc_heater_res(data, data->heater_temp);
|
|
|
|
|
|
|
|
/* set target heater temperature */
|
|
|
|
ret = regmap_write(data->regmap, BME680_REG_RES_HEAT_0, heatr_res);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev, "failed to write res_heat_0 register\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
heatr_dur = bme680_calc_heater_dur(data->heater_dur);
|
|
|
|
|
|
|
|
/* set target heating duration */
|
|
|
|
ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur);
|
|
|
|
if (ret < 0) {
|
2018-08-03 04:55:51 -07:00
|
|
|
dev_err(dev, "failed to write gas_wait_0 register\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-08-20 10:39:59 -07:00
|
|
|
/* Enable the gas sensor and select heater profile set-point 0 */
|
2018-07-26 04:35:10 -07:00
|
|
|
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
|
|
|
|
BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
|
2018-08-20 10:39:59 -07:00
|
|
|
FIELD_PREP(BME680_RUN_GAS_MASK, 1) |
|
|
|
|
FIELD_PREP(BME680_NB_CONV_MASK, 0));
|
2018-07-26 04:35:10 -07:00
|
|
|
if (ret < 0)
|
|
|
|
dev_err(dev, "failed to write ctrl_gas_1 register\n");
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-06 00:31:47 -07:00
|
|
|
static int bme680_read_temp(struct bme680_data *data, int *val)
|
2018-07-26 04:35:10 -07:00
|
|
|
{
|
|
|
|
int ret;
|
2024-06-09 16:38:14 -07:00
|
|
|
u32 adc_temp;
|
2018-07-26 04:35:10 -07:00
|
|
|
s16 comp_temp;
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
ret = bme680_read_temp_adc(data, &adc_temp);
|
|
|
|
if (ret)
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
comp_temp = bme680_compensate_temp(data, adc_temp);
|
2024-06-09 16:38:26 -07:00
|
|
|
*val = comp_temp * 10; /* Centidegrees to millidegrees */
|
|
|
|
return IIO_VAL_INT;
|
2018-07-26 04:35:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_read_press(struct bme680_data *data,
|
|
|
|
int *val, int *val2)
|
|
|
|
{
|
|
|
|
int ret;
|
2024-06-09 16:38:14 -07:00
|
|
|
u32 adc_press;
|
2024-06-09 16:38:26 -07:00
|
|
|
s32 t_fine;
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
ret = bme680_get_t_fine(data, &t_fine);
|
|
|
|
if (ret)
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
ret = bme680_read_press_adc(data, &adc_press);
|
|
|
|
if (ret)
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
*val = bme680_compensate_press(data, adc_press, t_fine);
|
2024-06-06 14:22:53 -07:00
|
|
|
*val2 = 1000;
|
2018-07-26 04:35:10 -07:00
|
|
|
return IIO_VAL_FRACTIONAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_read_humid(struct bme680_data *data,
|
|
|
|
int *val, int *val2)
|
|
|
|
{
|
|
|
|
int ret;
|
2024-06-09 16:38:26 -07:00
|
|
|
u32 adc_humidity, comp_humidity;
|
|
|
|
s32 t_fine;
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
ret = bme680_get_t_fine(data, &t_fine);
|
|
|
|
if (ret)
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
ret = bme680_read_humid_adc(data, &adc_humidity);
|
|
|
|
if (ret)
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
|
2024-06-09 16:38:26 -07:00
|
|
|
comp_humidity = bme680_compensate_humid(data, adc_humidity, t_fine);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
*val = comp_humidity;
|
|
|
|
*val2 = 1000;
|
|
|
|
return IIO_VAL_FRACTIONAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_read_gas(struct bme680_data *data,
|
|
|
|
int *val)
|
|
|
|
{
|
|
|
|
struct device *dev = regmap_get_device(data->regmap);
|
|
|
|
int ret;
|
2024-06-09 16:38:17 -07:00
|
|
|
u16 adc_gas_res, gas_regs_val;
|
2018-07-26 04:35:10 -07:00
|
|
|
u8 gas_range;
|
|
|
|
|
2024-06-09 16:38:20 -07:00
|
|
|
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &data->check);
|
|
|
|
if (data->check & BME680_GAS_MEAS_BIT) {
|
2018-07-26 04:35:10 -07:00
|
|
|
dev_err(dev, "gas measurement incomplete\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:17 -07:00
|
|
|
ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
|
2024-06-09 16:38:20 -07:00
|
|
|
&data->be16, BME680_GAS_NUM_BYTES);
|
2018-07-26 04:35:10 -07:00
|
|
|
if (ret < 0) {
|
2024-06-09 16:38:17 -07:00
|
|
|
dev_err(dev, "failed to read gas resistance\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
return ret;
|
|
|
|
}
|
2024-06-09 16:38:20 -07:00
|
|
|
|
|
|
|
gas_regs_val = be16_to_cpu(data->be16);
|
2024-06-09 16:38:17 -07:00
|
|
|
adc_gas_res = FIELD_GET(BME680_ADC_GAS_RES, gas_regs_val);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* occurs if either the gas heating duration was insuffient
|
|
|
|
* to reach the target heater temperature or the target
|
|
|
|
* heater temperature was too high for the heater sink to
|
|
|
|
* reach.
|
|
|
|
*/
|
2024-06-09 16:38:17 -07:00
|
|
|
if ((gas_regs_val & BME680_GAS_STAB_BIT) == 0) {
|
2018-07-26 04:35:10 -07:00
|
|
|
dev_err(dev, "heater failed to reach the target temperature\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:17 -07:00
|
|
|
gas_range = FIELD_GET(BME680_GAS_RANGE_MASK, gas_regs_val);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
*val = bme680_compensate_gas(data, adc_gas_res, gas_range);
|
|
|
|
return IIO_VAL_INT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_read_raw(struct iio_dev *indio_dev,
|
|
|
|
struct iio_chan_spec const *chan,
|
|
|
|
int *val, int *val2, long mask)
|
|
|
|
{
|
|
|
|
struct bme680_data *data = iio_priv(indio_dev);
|
2024-06-09 16:38:25 -07:00
|
|
|
int ret;
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:12 -07:00
|
|
|
guard(mutex)(&data->lock);
|
|
|
|
|
2024-06-09 16:38:25 -07:00
|
|
|
/* set forced mode to trigger measurement */
|
|
|
|
ret = bme680_set_mode(data, true);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = bme680_wait_for_eoc(data);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
switch (mask) {
|
|
|
|
case IIO_CHAN_INFO_PROCESSED:
|
|
|
|
switch (chan->type) {
|
|
|
|
case IIO_TEMP:
|
2019-03-06 00:31:47 -07:00
|
|
|
return bme680_read_temp(data, val);
|
2018-07-26 04:35:10 -07:00
|
|
|
case IIO_PRESSURE:
|
|
|
|
return bme680_read_press(data, val, val2);
|
|
|
|
case IIO_HUMIDITYRELATIVE:
|
|
|
|
return bme680_read_humid(data, val, val2);
|
|
|
|
case IIO_RESISTANCE:
|
|
|
|
return bme680_read_gas(data, val);
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
|
|
|
switch (chan->type) {
|
|
|
|
case IIO_TEMP:
|
2018-08-17 12:03:19 -07:00
|
|
|
*val = data->oversampling_temp;
|
2018-07-26 04:35:10 -07:00
|
|
|
return IIO_VAL_INT;
|
|
|
|
case IIO_PRESSURE:
|
2018-08-17 12:03:19 -07:00
|
|
|
*val = data->oversampling_press;
|
2018-07-26 04:35:10 -07:00
|
|
|
return IIO_VAL_INT;
|
|
|
|
case IIO_HUMIDITYRELATIVE:
|
2018-08-17 12:03:19 -07:00
|
|
|
*val = data->oversampling_humid;
|
2018-07-26 04:35:10 -07:00
|
|
|
return IIO_VAL_INT;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-17 12:03:19 -07:00
|
|
|
static bool bme680_is_valid_oversampling(int rate)
|
2018-07-26 04:35:10 -07:00
|
|
|
{
|
2018-08-17 12:03:19 -07:00
|
|
|
return (rate > 0 && rate <= 16 && is_power_of_2(rate));
|
2018-07-26 04:35:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bme680_write_raw(struct iio_dev *indio_dev,
|
|
|
|
struct iio_chan_spec const *chan,
|
|
|
|
int val, int val2, long mask)
|
|
|
|
{
|
|
|
|
struct bme680_data *data = iio_priv(indio_dev);
|
|
|
|
|
2024-06-09 16:38:12 -07:00
|
|
|
guard(mutex)(&data->lock);
|
|
|
|
|
2018-08-11 03:26:36 -07:00
|
|
|
if (val2 != 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
switch (mask) {
|
|
|
|
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
2018-08-17 12:03:19 -07:00
|
|
|
{
|
|
|
|
if (!bme680_is_valid_oversampling(val))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2018-07-26 04:35:10 -07:00
|
|
|
switch (chan->type) {
|
|
|
|
case IIO_TEMP:
|
2018-08-17 12:03:19 -07:00
|
|
|
data->oversampling_temp = val;
|
|
|
|
break;
|
2018-07-26 04:35:10 -07:00
|
|
|
case IIO_PRESSURE:
|
2018-08-17 12:03:19 -07:00
|
|
|
data->oversampling_press = val;
|
|
|
|
break;
|
2018-07-26 04:35:10 -07:00
|
|
|
case IIO_HUMIDITYRELATIVE:
|
2018-08-17 12:03:19 -07:00
|
|
|
data->oversampling_humid = val;
|
|
|
|
break;
|
2018-07-26 04:35:10 -07:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-08-17 12:03:19 -07:00
|
|
|
|
|
|
|
return bme680_chip_config(data);
|
|
|
|
}
|
2018-07-26 04:35:10 -07:00
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char bme680_oversampling_ratio_show[] = "1 2 4 8 16";
|
|
|
|
|
|
|
|
static IIO_CONST_ATTR(oversampling_ratio_available,
|
|
|
|
bme680_oversampling_ratio_show);
|
|
|
|
|
|
|
|
static struct attribute *bme680_attributes[] = {
|
|
|
|
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct attribute_group bme680_attribute_group = {
|
|
|
|
.attrs = bme680_attributes,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct iio_info bme680_info = {
|
|
|
|
.read_raw = &bme680_read_raw,
|
|
|
|
.write_raw = &bme680_write_raw,
|
|
|
|
.attrs = &bme680_attribute_group,
|
|
|
|
};
|
|
|
|
|
|
|
|
int bme680_core_probe(struct device *dev, struct regmap *regmap,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct iio_dev *indio_dev;
|
|
|
|
struct bme680_data *data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
|
|
|
if (!indio_dev)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
data = iio_priv(indio_dev);
|
2024-06-09 16:38:12 -07:00
|
|
|
mutex_init(&data->lock);
|
2018-07-26 04:35:10 -07:00
|
|
|
dev_set_drvdata(dev, indio_dev);
|
|
|
|
data->regmap = regmap;
|
|
|
|
indio_dev->name = name;
|
|
|
|
indio_dev->channels = bme680_channels;
|
|
|
|
indio_dev->num_channels = ARRAY_SIZE(bme680_channels);
|
|
|
|
indio_dev->info = &bme680_info;
|
|
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
|
|
|
|
|
|
/* default values for the sensor */
|
2018-08-17 12:03:19 -07:00
|
|
|
data->oversampling_humid = 2; /* 2X oversampling rate */
|
|
|
|
data->oversampling_press = 4; /* 4X oversampling rate */
|
|
|
|
data->oversampling_temp = 8; /* 8X oversampling rate */
|
2018-07-26 04:35:10 -07:00
|
|
|
data->heater_temp = 320; /* degree Celsius */
|
|
|
|
data->heater_dur = 150; /* milliseconds */
|
|
|
|
|
2024-06-09 16:38:19 -07:00
|
|
|
ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
|
|
|
|
BME680_CMD_SOFTRESET);
|
2024-06-09 16:38:23 -07:00
|
|
|
if (ret < 0)
|
|
|
|
return dev_err_probe(dev, ret, "Failed to reset chip\n");
|
2024-06-09 16:38:19 -07:00
|
|
|
|
2024-06-09 16:38:22 -07:00
|
|
|
usleep_range(BME680_STARTUP_TIME_US, BME680_STARTUP_TIME_US + 1000);
|
|
|
|
|
2024-06-09 16:38:20 -07:00
|
|
|
ret = regmap_read(regmap, BME680_REG_CHIP_ID, &data->check);
|
2024-06-09 16:38:23 -07:00
|
|
|
if (ret < 0)
|
|
|
|
return dev_err_probe(dev, ret, "Error reading chip ID\n");
|
2024-06-09 16:38:19 -07:00
|
|
|
|
2024-06-09 16:38:20 -07:00
|
|
|
if (data->check != BME680_CHIP_ID_VAL) {
|
2024-06-09 16:38:19 -07:00
|
|
|
dev_err(dev, "Wrong chip ID, got %x expected %x\n",
|
2024-06-09 16:38:20 -07:00
|
|
|
data->check, BME680_CHIP_ID_VAL);
|
2024-06-09 16:38:19 -07:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:22 -07:00
|
|
|
ret = bme680_read_calib(data, &data->bme680);
|
2018-07-26 04:35:10 -07:00
|
|
|
if (ret < 0) {
|
2024-06-09 16:38:23 -07:00
|
|
|
return dev_err_probe(dev, ret,
|
2024-06-09 16:38:22 -07:00
|
|
|
"failed to read calibration coefficients at probe\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
}
|
|
|
|
|
2024-06-09 16:38:22 -07:00
|
|
|
ret = bme680_chip_config(data);
|
2024-06-09 16:38:23 -07:00
|
|
|
if (ret < 0)
|
|
|
|
return dev_err_probe(dev, ret,
|
|
|
|
"failed to set chip_config data\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
|
2024-06-09 16:38:22 -07:00
|
|
|
ret = bme680_gas_config(data);
|
2024-06-09 16:38:23 -07:00
|
|
|
if (ret < 0)
|
|
|
|
return dev_err_probe(dev, ret,
|
|
|
|
"failed to set gas config data\n");
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
return devm_iio_device_register(dev, indio_dev);
|
|
|
|
}
|
2022-01-30 13:57:01 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(bme680_core_probe, IIO_BME680);
|
2018-07-26 04:35:10 -07:00
|
|
|
|
|
|
|
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
|
|
|
|
MODULE_DESCRIPTION("Bosch BME680 Driver");
|
|
|
|
MODULE_LICENSE("GPL v2");
|