linux/drivers/iio/chemical/bme680_core.c
Lars-Peter Clausen 166549bb1e iio: bme680: Use DIV_ROUND_CLOSEST() instead of open-coding it
Use DIV_ROUND_CLOSEST() instead of open-coding it. This documents intent
and makes it more clear what is going on for the casual reviewer.

Generated using the following the Coccinelle semantic patch.

// <smpl>
@r1@
expression x;
constant C1;
constant C2;
@@
 ((x) + C1) / C2

@script:python@
C1 << r1.C1;
C2 << r1.C2;
@@
try:
	if int(C1) * 2 != int(C2):
		cocci.include_match(False)
except:
	cocci.include_match(False)

@@
expression r1.x;
constant r1.C1;
constant r1.C2;
@@
-(((x) + C1) / C2)
+DIV_ROUND_CLOSEST(x, C2)
// </smpl>

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Link: https://lore.kernel.org/r/20201227171126.28216-2-lars@metafoo.de
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
2021-01-09 21:52:45 +00:00

965 lines
24 KiB
C

// 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/acpi.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/log2.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include "bme680.h"
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;
s8 par_h6;
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;
u8 oversampling_temp;
u8 oversampling_press;
u8 oversampling_humid;
u16 heater_dur;
u16 heater_temp;
/*
* Carryover value from temperature conversion, used in pressure
* and humidity compensation calculations.
*/
s32 t_fine;
};
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),
};
const struct regmap_config bme680_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xef,
.volatile_table = &bme680_volatile_table,
.cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL(bme680_regmap_config);
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);
unsigned int tmp, tmp_msb, tmp_lsb;
int ret;
__le16 buf;
/* Temperature related coefficients */
ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_T1_LSB_REG\n");
return ret;
}
calib->par_t1 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_T2_LSB_REG\n");
return ret;
}
calib->par_t2 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_T3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_T3_REG\n");
return ret;
}
calib->par_t3 = tmp;
/* Pressure related coefficients */
ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_P1_LSB_REG\n");
return ret;
}
calib->par_p1 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_P2_LSB_REG\n");
return ret;
}
calib->par_p2 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_P3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P3_REG\n");
return ret;
}
calib->par_p3 = tmp;
ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_P4_LSB_REG\n");
return ret;
}
calib->par_p4 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_P5_LSB_REG\n");
return ret;
}
calib->par_p5 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_P6_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P6_REG\n");
return ret;
}
calib->par_p6 = tmp;
ret = regmap_read(data->regmap, BME680_P7_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P7_REG\n");
return ret;
}
calib->par_p7 = tmp;
ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_P8_LSB_REG\n");
return ret;
}
calib->par_p8 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_P9_LSB_REG\n");
return ret;
}
calib->par_p9 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_P10_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P10_REG\n");
return ret;
}
calib->par_p10 = tmp;
/* Humidity related coefficients */
ret = regmap_read(data->regmap, BME680_H1_MSB_REG, &tmp_msb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H1_MSB_REG\n");
return ret;
}
ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H1_LSB_REG\n");
return ret;
}
calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
(tmp_lsb & BME680_BIT_H1_DATA_MASK);
ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H2_MSB_REG\n");
return ret;
}
ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H2_LSB_REG\n");
return ret;
}
calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
(tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
ret = regmap_read(data->regmap, BME680_H3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H3_REG\n");
return ret;
}
calib->par_h3 = tmp;
ret = regmap_read(data->regmap, BME680_H4_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H4_REG\n");
return ret;
}
calib->par_h4 = tmp;
ret = regmap_read(data->regmap, BME680_H5_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H5_REG\n");
return ret;
}
calib->par_h5 = tmp;
ret = regmap_read(data->regmap, BME680_H6_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H6_REG\n");
return ret;
}
calib->par_h6 = tmp;
ret = regmap_read(data->regmap, BME680_H7_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H7_REG\n");
return ret;
}
calib->par_h7 = tmp;
/* Gas heater related coefficients */
ret = regmap_read(data->regmap, BME680_GH1_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_GH1_REG\n");
return ret;
}
calib->par_gh1 = tmp;
ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG,
&buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "failed to read BME680_GH2_LSB_REG\n");
return ret;
}
calib->par_gh2 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_GH3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_GH3_REG\n");
return ret;
}
calib->par_gh3 = tmp;
/* Other coefficients */
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_RANGE, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read resistance heat range\n");
return ret;
}
calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, tmp);
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read resistance heat value\n");
return ret;
}
calib->res_heat_val = tmp;
ret = regmap_read(data->regmap, BME680_REG_RANGE_SW_ERR, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read range software error\n");
return ret;
}
calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, tmp);
return 0;
}
/*
* 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.
*/
static s16 bme680_compensate_temp(struct bme680_data *data,
s32 adc_temp)
{
struct bme680_calib *calib = &data->bme680;
s64 var1, var2, var3;
s16 calc_temp;
/* If the calibration is invalid, attempt to reload it */
if (!calib->par_t2)
bme680_read_calib(data, calib);
var1 = (adc_temp >> 3) - (calib->par_t1 << 1);
var2 = (var1 * calib->par_t2) >> 11;
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
var3 = (var3 * (calib->par_t3 << 4)) >> 14;
data->t_fine = var2 + var3;
calc_temp = (data->t_fine * 5 + 128) >> 8;
return calc_temp;
}
/*
* 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,
u32 adc_press)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, press_comp;
var1 = (data->t_fine >> 1) - 64000;
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2;
var2 = var2 + (var1 * calib->par_p5 << 1);
var2 = (var2 >> 2) + (calib->par_p4 << 16);
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
(calib->par_p3 << 5)) >> 3) +
((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;
press_comp += (var1 + var2 + var3 + (calib->par_p7 << 7)) >> 4;
return press_comp;
}
/*
* 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,
u16 adc_humid)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, var4, var5, var6, temp_scaled, calc_hum;
temp_scaled = (data->t_fine * 5 + 128) >> 8;
var1 = (adc_humid - ((s32) ((s32) calib->par_h1 * 16))) -
(((temp_scaled * (s32) calib->par_h3) / 100) >> 1);
var2 = ((s32) calib->par_h2 *
(((temp_scaled * calib->par_h4) / 100) +
(((temp_scaled * ((temp_scaled * calib->par_h5) / 100))
>> 6) / 100) + (1 << 14))) >> 10;
var3 = var1 * var2;
var4 = calib->par_h6 << 7;
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;
calc_hum = clamp(calc_hum, 0, 100000); /* clamp between 0-100 %rH */
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 */
const u32 lookupTable[16] = {2147483647u, 2147483647u,
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;
heatr_res = DIV_ROUND_CLOSEST(heatr_res_x100, 100);
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;
}
static u8 bme680_oversampling_to_reg(u8 val)
{
return ilog2(val) + 1;
}
static int bme680_chip_config(struct bme680_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
u8 osrs;
osrs = FIELD_PREP(
BME680_OSRS_HUMIDITY_MASK,
bme680_oversampling_to_reg(data->oversampling_humid));
/*
* 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;
}
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));
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK,
osrs);
if (ret < 0)
dev_err(dev, "failed to write ctrl_meas register\n");
return ret;
}
static int bme680_gas_config(struct bme680_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
u8 heatr_res, heatr_dur;
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) {
dev_err(dev, "failed to write gas_wait_0 register\n");
return ret;
}
/* Enable the gas sensor and select heater profile set-point 0 */
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
FIELD_PREP(BME680_RUN_GAS_MASK, 1) |
FIELD_PREP(BME680_NB_CONV_MASK, 0));
if (ret < 0)
dev_err(dev, "failed to write ctrl_gas_1 register\n");
return ret;
}
static int bme680_read_temp(struct bme680_data *data, int *val)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
__be32 tmp = 0;
s32 adc_temp;
s16 comp_temp;
/* set forced mode to trigger measurement */
ret = bme680_set_mode(data, true);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
&tmp, 3);
if (ret < 0) {
dev_err(dev, "failed to read temperature\n");
return ret;
}
adc_temp = be32_to_cpu(tmp) >> 12;
if (adc_temp == BME680_MEAS_SKIPPED) {
/* reading was skipped */
dev_err(dev, "reading temperature skipped\n");
return -EINVAL;
}
comp_temp = bme680_compensate_temp(data, adc_temp);
/*
* val might be NULL if we're called by the read_press/read_humid
* routine which is callled to get t_fine value used in
* compensate_press/compensate_humid to get compensated
* pressure/humidity readings.
*/
if (val) {
*val = comp_temp * 10; /* Centidegrees to millidegrees */
return IIO_VAL_INT;
}
return ret;
}
static int bme680_read_press(struct bme680_data *data,
int *val, int *val2)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
__be32 tmp = 0;
s32 adc_press;
/* Read and compensate temperature to get a reading of t_fine */
ret = bme680_read_temp(data, NULL);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
&tmp, 3);
if (ret < 0) {
dev_err(dev, "failed to read pressure\n");
return ret;
}
adc_press = be32_to_cpu(tmp) >> 12;
if (adc_press == BME680_MEAS_SKIPPED) {
/* reading was skipped */
dev_err(dev, "reading pressure skipped\n");
return -EINVAL;
}
*val = bme680_compensate_press(data, adc_press);
*val2 = 100;
return IIO_VAL_FRACTIONAL;
}
static int bme680_read_humid(struct bme680_data *data,
int *val, int *val2)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
__be16 tmp = 0;
s32 adc_humidity;
u32 comp_humidity;
/* Read and compensate temperature to get a reading of t_fine */
ret = bme680_read_temp(data, NULL);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
&tmp, sizeof(tmp));
if (ret < 0) {
dev_err(dev, "failed to read humidity\n");
return ret;
}
adc_humidity = be16_to_cpu(tmp);
if (adc_humidity == BME680_MEAS_SKIPPED) {
/* reading was skipped */
dev_err(dev, "reading humidity skipped\n");
return -EINVAL;
}
comp_humidity = bme680_compensate_humid(data, adc_humidity);
*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;
__be16 tmp = 0;
unsigned int check;
u16 adc_gas_res;
u8 gas_range;
/* Set heater settings */
ret = bme680_gas_config(data);
if (ret < 0) {
dev_err(dev, "failed to set gas config\n");
return ret;
}
/* set forced mode to trigger measurement */
ret = bme680_set_mode(data, true);
if (ret < 0)
return ret;
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check);
if (check & BME680_GAS_MEAS_BIT) {
dev_err(dev, "gas measurement incomplete\n");
return -EBUSY;
}
ret = regmap_read(data->regmap, BME680_REG_GAS_R_LSB, &check);
if (ret < 0) {
dev_err(dev, "failed to read gas_r_lsb register\n");
return ret;
}
/*
* 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.
*/
if ((check & BME680_GAS_STAB_BIT) == 0) {
dev_err(dev, "heater failed to reach the target temperature\n");
return -EINVAL;
}
ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
&tmp, sizeof(tmp));
if (ret < 0) {
dev_err(dev, "failed to read gas resistance\n");
return ret;
}
gas_range = check & BME680_GAS_RANGE_MASK;
adc_gas_res = be16_to_cpu(tmp) >> BME680_ADC_GAS_RES_SHIFT;
*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);
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_TEMP:
return bme680_read_temp(data, val);
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:
*val = data->oversampling_temp;
return IIO_VAL_INT;
case IIO_PRESSURE:
*val = data->oversampling_press;
return IIO_VAL_INT;
case IIO_HUMIDITYRELATIVE:
*val = data->oversampling_humid;
return IIO_VAL_INT;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static bool bme680_is_valid_oversampling(int rate)
{
return (rate > 0 && rate <= 16 && is_power_of_2(rate));
}
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);
if (val2 != 0)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
{
if (!bme680_is_valid_oversampling(val))
return -EINVAL;
switch (chan->type) {
case IIO_TEMP:
data->oversampling_temp = val;
break;
case IIO_PRESSURE:
data->oversampling_press = val;
break;
case IIO_HUMIDITYRELATIVE:
data->oversampling_humid = val;
break;
default:
return -EINVAL;
}
return bme680_chip_config(data);
}
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,
};
static const char *bme680_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
return dev_name(dev);
}
int bme680_core_probe(struct device *dev, struct regmap *regmap,
const char *name)
{
struct iio_dev *indio_dev;
struct bme680_data *data;
unsigned int val;
int ret;
ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
BME680_CMD_SOFTRESET);
if (ret < 0) {
dev_err(dev, "Failed to reset chip\n");
return ret;
}
ret = regmap_read(regmap, BME680_REG_CHIP_ID, &val);
if (ret < 0) {
dev_err(dev, "Error reading chip ID\n");
return ret;
}
if (val != BME680_CHIP_ID_VAL) {
dev_err(dev, "Wrong chip ID, got %x expected %x\n",
val, BME680_CHIP_ID_VAL);
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
if (!name && ACPI_HANDLE(dev))
name = bme680_match_acpi_device(dev);
data = iio_priv(indio_dev);
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 */
data->oversampling_humid = 2; /* 2X oversampling rate */
data->oversampling_press = 4; /* 4X oversampling rate */
data->oversampling_temp = 8; /* 8X oversampling rate */
data->heater_temp = 320; /* degree Celsius */
data->heater_dur = 150; /* milliseconds */
ret = bme680_chip_config(data);
if (ret < 0) {
dev_err(dev, "failed to set chip_config data\n");
return ret;
}
ret = bme680_gas_config(data);
if (ret < 0) {
dev_err(dev, "failed to set gas config data\n");
return ret;
}
ret = bme680_read_calib(data, &data->bme680);
if (ret < 0) {
dev_err(dev,
"failed to read calibration coefficients at probe\n");
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_GPL(bme680_core_probe);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 Driver");
MODULE_LICENSE("GPL v2");