2018-07-11 15:06:27

by Himanshu Jha

[permalink] [raw]
Subject: [PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

Bosch BME680 is a 4-in-1 sensor with temperature, pressure, humidity
and gas sensing capability. It supports both I2C and SPI communication
protocol for effective data communication.

The device supports two modes:

1. Sleep mode
2. Forced mode

The measurements only takes place when forced mode is triggered and a
single TPHG cycle is performed by the sensor. The sensor automatically
goes to sleep after afterwards.

The device has various calibration constants/parameters programmed into
devices' non-volatile memory(NVM) during production and can't be altered
by the user. These constants are used in the compensation functions to
get the required compensated readings along with the raw data. The
compensation functions/algorithms are provided by Bosch Sensortec GmbH
via their API[1]. As these don't change during the measurement cycle,
therefore we read and store them at the probe. The default configs
supplied by Bosch are also set at probe.

0-day tested with build success.

GSoC-2018: https://summerofcode.withgoogle.com/projects/#6691473790074880
Mentor: Daniel Baluta
[1] https://github.com/BoschSensortec/BME680_driver
Datasheet:
https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf

Cc: Daniel Baluta <[email protected]>
Signed-off-by: Himanshu Jha <[email protected]>
---

v3:
-moved files to chemical directory instead of a dedicated directory.
-read calibration parameters serially with endian conversions.
-drop some return ret.
-removed few unnecessary casts safely.
-added 'u' suffix to explicitly specify unsigned for large values
and thereby fixing comiler warning.
-left aligned all comments.
-added a comment explaining heater stability failure.

v2:
-Used devm_add_action() to add a generic remove method for
both I2C & SPI driver.
-Introduction of compensation functions.
-chip initialisation routines moved to respective I2C and SPI
driver.
-Introduction of gas sensing rountines.
-Simplified Kconfig to reduce various options.

drivers/iio/chemical/Kconfig | 25 +
drivers/iio/chemical/Makefile | 3 +
drivers/iio/chemical/bme680.h | 99 ++++
drivers/iio/chemical/bme680_core.c | 946 +++++++++++++++++++++++++++++++++++++
drivers/iio/chemical/bme680_i2c.c | 83 ++++
drivers/iio/chemical/bme680_spi.c | 123 +++++
6 files changed, 1279 insertions(+)
create mode 100644 drivers/iio/chemical/bme680.h
create mode 100644 drivers/iio/chemical/bme680_core.c
create mode 100644 drivers/iio/chemical/bme680_i2c.c
create mode 100644 drivers/iio/chemical/bme680_spi.c

diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 5cb5be7..24790a8 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -21,6 +21,31 @@ config ATLAS_PH_SENSOR
To compile this driver as module, choose M here: the
module will be called atlas-ph-sensor.

+config BME680
+ tristate "Bosch Sensortec BME680 sensor driver"
+ depends on (I2C || SPI)
+ select REGMAP
+ select BME680_I2C if (I2C)
+ select BME680_SPI if (SPI)
+ help
+ Say yes here to build support for Bosch Sensortec BME680 sensor with
+ temperature, pressure, humidity and gas sensing capability.
+
+ This driver can also be built as a module. If so, the module for I2C
+ would be called bme680_i2c and bme680_spi for SPI support.
+
+config BME680_I2C
+ tristate
+ depends on BME680
+ depends on I2C
+ select REGMAP_I2C
+
+config BME680_SPI
+ tristate
+ depends on BME680
+ depends on SPI
+ select REGMAP_SPI
+
config CCS811
tristate "AMS CCS811 VOC sensor"
depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index a629b29..2f4c4ba 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -4,6 +4,9 @@

# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
+obj-$(CONFIG_BME680) += bme680_core.o
+obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
+obj-$(CONFIG_BME680_SPI) += bme680_spi.o
obj-$(CONFIG_CCS811) += ccs811.o
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h
new file mode 100644
index 0000000..80c4190
--- /dev/null
+++ b/drivers/iio/chemical/bme680.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef BME680_H_
+#define BME680_H_
+
+#define BME680_REG_CHIP_I2C_ID 0xD0
+#define BME680_REG_CHIP_SPI_ID 0x50
+#define BME680_CHIP_ID_VAL 0x61
+#define BME680_REG_SOFT_RESET 0xE0
+#define BME680_CMD_SOFTRESET 0xB6
+#define BME680_REG_STATUS 0x73
+#define BME680_SPI_MEM_PAGE_BIT BIT(4)
+#define BME680_SPI_MEM_PAGE_1_VAL 1
+
+#define BME680_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
+#define BME680_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2)
+#define BME680_OSRS_HUMID_X(osrs_h) ((osrs_h) << 0)
+
+#define BME680_REG_TEMP_MSB 0x22
+#define BME680_REG_PRESS_MSB 0x1F
+#define BM6880_REG_HUMIDITY_MSB 0x25
+#define BME680_REG_GAS_MSB 0x2A
+#define BME680_REG_GAS_R_LSB 0x2B
+#define BME680_GAS_STAB_BIT BIT(4)
+
+#define BME680_REG_CTRL_HUMIDITY 0x72
+#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
+
+#define BME680_REG_CTRL_MEAS 0x74
+#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
+#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
+#define BME680_MODE_MASK GENMASK(1, 0)
+
+#define BME680_MODE_FORCED BIT(0)
+#define BME680_MODE_SLEEP 0
+
+#define BME680_REG_CONFIG 0x75
+#define BME680_FILTER_MASK GENMASK(4, 2)
+#define BME680_FILTER_COEFF BIT(1)
+
+/* TEMP/PRESS/HUMID reading skipped */
+#define BME680_MEAS_SKIPPED 0x8000
+
+#define BME680_MAX_OVERFLOW_VAL 0x40000000
+#define BME680_HUM_REG_SHIFT_VAL 4
+#define BME680_BIT_H1_DATA_MSK 0x0F
+
+#define BME680_REG_RES_HEAT_RANGE 0x02
+#define BME680_RHRANGE_MSK 0x30
+#define BME680_REG_RES_HEAT_VAL 0x00
+#define BME680_REG_RANGE_SW_ERR 0x04
+#define BME680_RSERROR_MSK 0xF0
+#define BME680_REG_RES_HEAT_0 0x5A
+#define BME680_REG_GAS_WAIT_0 0x64
+#define BME680_GAS_RANGE_MASK 0x0F
+#define BME680_ADC_GAS_RES_SHIFT 6
+#define BME680_AMB_TEMP 25
+
+#define BME680_REG_CTRL_GAS_1 0x71
+#define BME680_RUN_GAS_MASK BIT(4)
+#define BME680_NB_CONV_MASK GENMASK(3, 0)
+#define BME680_RUN_GAS_EN BIT(4)
+#define BME680_NB_CONV_0 0
+
+#define BME680_REG_MEAS_STAT_0 0x1D
+#define BME680_GAS_MEAS_BIT BIT(6)
+
+/* Calibration Parameters */
+#define BME680_T2_LSB_REG 0x8A
+#define BME680_T3_REG 0x8C
+#define BME680_P1_LSB_REG 0x8E
+#define BME680_P2_LSB_REG 0x90
+#define BME680_P3_REG 0x92
+#define BME680_P4_LSB_REG 0x94
+#define BME680_P5_LSB_REG 0x96
+#define BME680_P7_REG 0x98
+#define BME680_P6_REG 0x99
+#define BME680_P8_LSB_REG 0x9C
+#define BME680_P9_LSB_REG 0x9E
+#define BME680_P10_REG 0xA0
+#define BME680_H2_LSB_REG 0xE2
+#define BME680_H2_MSB_REG 0xE1
+#define BME680_H1_MSB_REG 0xE3
+#define BME680_H1_LSB_REG 0xE2
+#define BME680_H3_REG 0xE4
+#define BME680_H4_REG 0xE5
+#define BME680_H5_REG 0xE6
+#define BME680_H6_REG 0xE7
+#define BME680_H7_REG 0xE8
+#define BME680_T1_LSB_REG 0xE9
+#define BME680_GH2_LSB_REG 0xEB
+#define BME680_GH1_REG 0xED
+#define BME680_GH3_REG 0xEE
+
+extern const struct regmap_config bme680_regmap_config;
+
+int bme680_core_probe(struct device *dev, struct regmap *regmap,
+ const char *name);
+
+#endif /* BME680_H_ */
diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c
new file mode 100644
index 0000000..8dd789e
--- /dev/null
+++ b/drivers/iio/chemical/bme680_core.c
@@ -0,0 +1,946 @@
+// 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 <[email protected]>
+ */
+#include <linux/acpi.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;
+};
+
+const struct regmap_config bme680_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+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 const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
+
+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,
+ (u8 *) &buf, 2);
+ 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,
+ (u8 *) &buf, 2);
+ 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,
+ (u8 *) &buf, 2);
+ 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,
+ (u8 *) &buf, 2);
+ 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,
+ (u8 *) &buf, 2);
+ 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,
+ (u8 *) &buf, 2);
+ 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,
+ (u8 *) &buf, 2);
+ 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,
+ (u8 *) &buf, 2);
+ 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_MSK);
+
+ 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,
+ (u8 *) &buf, 2);
+ 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 = (tmp & BME680_RHRANGE_MSK) / 16;
+
+ 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 = (tmp & BME680_RSERROR_MSK) / 16;
+
+ return 0;
+}
+
+/* Taken from Bosch BME680 API */
+static s32 bme680_compensate_temp(struct bme680_data *data,
+ s32 adc_temp)
+{
+ struct bme680_calib *calib = &data->bme680;
+ s64 var1, var2, var3, calc_temp;
+
+ var1 = ((s32) adc_temp >> 3) - ((s32) calib->par_t1 << 1);
+ var2 = (var1 * (s32) calib->par_t2) >> 11;
+ var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
+ var3 = ((var3) * ((s32) calib->par_t3 << 4)) >> 14;
+ data->t_fine = (s32) (var2 + var3);
+ calc_temp = (s16) (((data->t_fine * 5) + 128) >> 8);
+
+ return calc_temp;
+}
+
+/* Taken from Bosch BME680 API */
+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 = (((s32)data->t_fine) >> 1) - 64000;
+ var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * (s32)calib->par_p6) >> 2;
+ var2 = var2 + ((var1 * (s32)calib->par_p5) << 1);
+ var2 = (var2 >> 2) + ((s32)calib->par_p4 << 16);
+ var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
+ ((s32)calib->par_p3 << 5)) >> 3) +
+ (((s32)calib->par_p2 * var1) >> 1);
+ var1 = var1 >> 18;
+ var1 = ((32768 + var1) * (s32)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 = ((s32)calib->par_p9 * (((press_comp >> 3) *
+ (press_comp >> 3)) >> 13)) >> 12;
+ var2 = ((press_comp >> 2) * (s32)calib->par_p8) >> 13;
+ var3 = ((press_comp >> 8) * (press_comp >> 8) *
+ (press_comp >> 8) * calib->par_p10) >> 17;
+
+ press_comp += ((var1 + var2 + var3 + ((s32)calib->par_p7 << 7)) >> 4);
+
+ return press_comp;
+}
+
+/* Taken from Bosch BME680 API */
+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 = (((s32) 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 * (s32) calib->par_h4) /
+ ((s32) 100)) + (((temp_scaled * ((temp_scaled *
+ (s32) calib->par_h5) / 100)) >> 6) / 100) +
+ (s32) (1 << 14))) >> 10;
+ var3 = var1 * var2;
+ var4 = (s32) calib->par_h6 << 7;
+ var4 = (var4 + ((temp_scaled * (s32) calib->par_h7) / 100)) >> 4;
+ var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
+ var6 = (var4 * var5) >> 1;
+ calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
+
+ if (calc_hum > 100000) /* Cap at 100%rH */
+ calc_hum = 100000;
+ else if (calc_hum < 0)
+ calc_hum = 0;
+
+ return calc_hum;
+}
+
+/* Taken from Bosch BME680 API */
+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 1 for the possible gas range values */
+ u32 lookupTable1[16] = {2147483647u, 2147483647u, 2147483647u,
+ 2147483647u, 2147483647u, 2126008810u,
+ 2147483647u, 2130303777u, 2147483647u,
+ 2147483647u, 2143188679u, 2136746228u,
+ 2147483647u, 2126008810u, 2147483647u,
+ 2147483647u};
+ /* Look up table 2 for the possible gas range values */
+ u32 lookupTable2[16] = {4096000000u, 2048000000u, 1024000000u,
+ 512000000u, 255744255u, 127110228u, 64000000u,
+ 32258064u, 16016016u, 8000000u, 4000000u,
+ 2000000u, 1000000u, 500000u, 250000u, 125000u};
+
+ var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
+ ((s64) lookupTable1[gas_range])) >> 16;
+ var2 = (((s64) ((s64) gas_res_adc << 15) - 16777216) + var1);
+ var3 = (((s64) lookupTable2[gas_range] * (s64) var1) >> 9);
+ calc_gas_res = (u32) ((var3 + ((s64) var2 >> 1)) / (s64) var2);
+
+ return calc_gas_res;
+}
+
+/* Taken from Bosch BME680 API */
+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 = (heatr_res_x100 + 50) / 100;
+
+ return heatr_res;
+}
+
+/* Taken from Bosch BME680 API */
+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 int bme680_chip_config(struct bme680_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+ u8 osrs = BME680_OSRS_HUMID_X(data->oversampling_humid + 1);
+ /*
+ * 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);
+ if (ret < 0) {
+ dev_err(dev, "failed to write config register\n");
+ return ret;
+ }
+
+ osrs = BME680_OSRS_TEMP_X(data->oversampling_temp + 1) |
+ BME680_OSRS_PRESS_X(data->oversampling_press + 1);
+
+ 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, "failted to write gas_wait_0 register\n");
+ return ret;
+ }
+
+ /* Selecting the runGas and NB conversion settings for the sensor */
+ ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
+ BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
+ BME680_RUN_GAS_EN | BME680_NB_CONV_0);
+ if (ret < 0)
+ dev_err(dev, "failed to write ctrl_gas_1 register\n");
+
+ return ret;
+}
+
+/* Outputs temperature measurement in degC */
+static int bme680_read_temp(struct bme680_data *data,
+ int *val, int *val2)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret = 0;
+ __be32 tmp = 0;
+ s32 adc_temp, 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,
+ (u8 *) &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 && val2) {
+ *val = comp_temp;
+ *val2 = 100;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return ret;
+}
+
+/* Outputs pressure measurement in hPa */
+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, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
+ (u8 *) &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;
+}
+
+/* Outputs humidity measurement in %r.H */
+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 so we get a reading of t_fine */
+ ret = bme680_read_temp(data, NULL, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
+ (u8 *) &tmp, 2);
+ 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;
+}
+
+/* Outputs gas measurement in ohm */
+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 a single 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;
+ }
+
+ if ((check & BME680_GAS_STAB_BIT) == 0) {
+ /*
+ * 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.
+ */
+ dev_err(dev, "heater failed to reach the target temperature\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
+ (u8 *) &tmp, 2);
+ 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, val2);
+ 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 = 1 << data->oversampling_temp;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = 1 << data->oversampling_press;
+ return IIO_VAL_INT;
+ case IIO_HUMIDITYRELATIVE:
+ *val = 1 << data->oversampling_humid;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
+ int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
+ if (bme680_oversampling_avail[i] == val) {
+ data->oversampling_temp = ilog2(val);
+
+ return bme680_chip_config(data);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
+ int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
+ if (bme680_oversampling_avail[i] == val) {
+ data->oversampling_press = ilog2(val);
+
+ return bme680_chip_config(data);
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
+ int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
+ if (bme680_oversampling_avail[i] == val) {
+ data->oversampling_humid = ilog2(val);
+
+ return bme680_chip_config(data);
+ }
+ }
+
+ return -EINVAL;
+}
+
+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);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_TEMP:
+ return bme680_write_oversampling_ratio_temp(data, val);
+ case IIO_PRESSURE:
+ return bme680_write_oversampling_ratio_press(data, val);
+ case IIO_HUMIDITYRELATIVE:
+ return bme680_write_oversampling_ratio_humid(data, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ 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);
+}
+
+static void bme680_core_remove(void *arg)
+{
+ iio_device_unregister(arg);
+}
+
+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;
+
+ ret = devm_add_action(dev, bme680_core_remove, indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "failed to register remove action\n");
+ return ret;
+ }
+
+ 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->dev.parent = dev;
+ 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 = ilog2(2); /* 2X oversampling rate */
+ data->oversampling_press = ilog2(4); /* 4X oversampling rate */
+ data->oversampling_temp = ilog2(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 iio_device_register(indio_dev);
+}
+EXPORT_SYMBOL_GPL(bme680_core_probe);
+
+MODULE_AUTHOR("Himanshu Jha <[email protected]>");
+MODULE_DESCRIPTION("Bosch BME680 Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c
new file mode 100644
index 0000000..a3a77cf
--- /dev/null
+++ b/drivers/iio/chemical/bme680_i2c.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BME680 - I2C Driver
+ *
+ * Copyright (C) 2018 Himanshu Jha <[email protected]>
+ *
+ * 7-Bit I2C slave address is:
+ * - 0x76 if SDO is pulled to GND
+ * - 0x77 if SDO is pulled to VDDIO
+ *
+ * Note: SDO pin cannot be left floating otherwise I2C address
+ * will be undefined.
+ */
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "bme680.h"
+
+static int bme680_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ const char *name = NULL;
+ unsigned int val;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
+ BME680_CMD_SOFTRESET);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(regmap, BME680_REG_CHIP_I2C_ID, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading I2C chip ID\n");
+ return ret;
+ }
+
+ if (val != BME680_CHIP_ID_VAL) {
+ dev_err(&client->dev, "Wrong chip ID, got %x expected %x\n",
+ val, BME680_CHIP_ID_VAL);
+ return -ENODEV;
+ }
+
+ if (id)
+ name = id->name;
+
+ return bme680_core_probe(&client->dev, regmap, name);
+}
+
+static const struct i2c_device_id bme680_i2c_id[] = {
+ {"bme680", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
+
+static const struct acpi_device_id bme680_acpi_match[] = {
+ {"BME0680", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
+
+static struct i2c_driver bme680_i2c_driver = {
+ .driver = {
+ .name = "bme680_i2c",
+ .acpi_match_table = ACPI_PTR(bme680_acpi_match),
+ },
+ .probe = bme680_i2c_probe,
+ .id_table = bme680_i2c_id,
+};
+module_i2c_driver(bme680_i2c_driver);
+
+MODULE_AUTHOR("Himanshu Jha <[email protected]>");
+MODULE_DESCRIPTION("BME680 I2C driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c
new file mode 100644
index 0000000..1fb3466
--- /dev/null
+++ b/drivers/iio/chemical/bme680_spi.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BME680 - SPI Driver
+ *
+ * Copyright (C) 2018 Himanshu Jha <[email protected]>
+ */
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "bme680.h"
+
+static int bme680_regmap_spi_write(void *context, const void *data,
+ size_t count)
+{
+ struct spi_device *spi = context;
+ u8 buf[2];
+
+ memcpy(buf, data, 2);
+ /*
+ * The SPI register address (= full register address without bit 7)
+ * and the write command (bit7 = RW = '0')
+ */
+ buf[0] &= ~0x80;
+
+ return spi_write_then_read(spi, buf, 2, NULL, 0);
+}
+
+static int bme680_regmap_spi_read(void *context, const void *reg,
+ size_t reg_size, void *val, size_t val_size)
+{
+ struct spi_device *spi = context;
+
+ return spi_write_then_read(spi, reg, reg_size, val, val_size);
+}
+
+static struct regmap_bus bme680_regmap_bus = {
+ .write = bme680_regmap_spi_write,
+ .read = bme680_regmap_spi_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static int bme680_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap *regmap;
+ unsigned int val;
+ int ret;
+
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "spi_setup failed!\n");
+ return ret;
+ }
+
+ regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
+ &spi->dev, &bme680_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Failed to register spi regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
+ BME680_CMD_SOFTRESET);
+ if (ret < 0)
+ return ret;
+
+ /* after power-on reset, Page 0(0x80-0xFF) of spi_mem_page is active */
+ ret = regmap_read(regmap, BME680_REG_CHIP_SPI_ID, &val);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Error reading SPI chip ID\n");
+ return ret;
+ }
+
+ if (val != BME680_CHIP_ID_VAL) {
+ dev_err(&spi->dev, "Wrong chip ID, got %x expected %x\n",
+ val, BME680_CHIP_ID_VAL);
+ return -ENODEV;
+ }
+ /*
+ * select Page 1 of spi_mem_page to enable access to
+ * to registers from address 0x00 to 0x7F.
+ */
+ ret = regmap_write_bits(regmap, BME680_REG_STATUS,
+ BME680_SPI_MEM_PAGE_BIT,
+ BME680_SPI_MEM_PAGE_1_VAL);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to set page 1 of spi_mem_page\n");
+ return ret;
+ }
+
+ return bme680_core_probe(&spi->dev, regmap, id->name);
+}
+
+static const struct spi_device_id bme680_spi_id[] = {
+ {"bme680", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(spi, bme680_spi_id);
+
+static const struct acpi_device_id bme680_acpi_match[] = {
+ {"BME0680", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
+
+static struct spi_driver bme680_spi_driver = {
+ .driver = {
+ .name = "bme680_spi",
+ .acpi_match_table = ACPI_PTR(bme680_acpi_match),
+ },
+ .probe = bme680_spi_probe,
+ .id_table = bme680_spi_id,
+};
+module_spi_driver(bme680_spi_driver);
+
+MODULE_AUTHOR("Himanshu Jha <[email protected]>");
+MODULE_DESCRIPTION("Bosch BME680 SPI driver");
+MODULE_LICENSE("GPL v2");
--
2.7.4



2018-07-12 03:10:13

by Matt Ranostay

[permalink] [raw]
Subject: Re: [PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

On Wed, Jul 11, 2018 at 5:13 AM, Himanshu Jha
<[email protected]> wrote:
> Bosch BME680 is a 4-in-1 sensor with temperature, pressure, humidity
> and gas sensing capability. It supports both I2C and SPI communication
> protocol for effective data communication.
>
> The device supports two modes:
>
> 1. Sleep mode
> 2. Forced mode
>
> The measurements only takes place when forced mode is triggered and a
> single TPHG cycle is performed by the sensor. The sensor automatically
> goes to sleep after afterwards.
>
> The device has various calibration constants/parameters programmed into
> devices' non-volatile memory(NVM) during production and can't be altered
> by the user. These constants are used in the compensation functions to
> get the required compensated readings along with the raw data. The
> compensation functions/algorithms are provided by Bosch Sensortec GmbH
> via their API[1]. As these don't change during the measurement cycle,
> therefore we read and store them at the probe. The default configs
> supplied by Bosch are also set at probe.
>
> 0-day tested with build success.
>
> GSoC-2018: https://summerofcode.withgoogle.com/projects/#6691473790074880
> Mentor: Daniel Baluta
> [1] https://github.com/BoschSensortec/BME680_driver
> Datasheet:
> https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
>
> Cc: Daniel Baluta <[email protected]>
> Signed-off-by: Himanshu Jha <[email protected]>
> ---
>
> v3:
> -moved files to chemical directory instead of a dedicated directory.
> -read calibration parameters serially with endian conversions.
> -drop some return ret.
> -removed few unnecessary casts safely.
> -added 'u' suffix to explicitly specify unsigned for large values
> and thereby fixing comiler warning.
> -left aligned all comments.
> -added a comment explaining heater stability failure.
>
> v2:
> -Used devm_add_action() to add a generic remove method for
> both I2C & SPI driver.
> -Introduction of compensation functions.
> -chip initialisation routines moved to respective I2C and SPI
> driver.
> -Introduction of gas sensing rountines.
> -Simplified Kconfig to reduce various options.
>
> drivers/iio/chemical/Kconfig | 25 +
> drivers/iio/chemical/Makefile | 3 +
> drivers/iio/chemical/bme680.h | 99 ++++
> drivers/iio/chemical/bme680_core.c | 946 +++++++++++++++++++++++++++++++++++++
> drivers/iio/chemical/bme680_i2c.c | 83 ++++
> drivers/iio/chemical/bme680_spi.c | 123 +++++
> 6 files changed, 1279 insertions(+)
> create mode 100644 drivers/iio/chemical/bme680.h
> create mode 100644 drivers/iio/chemical/bme680_core.c
> create mode 100644 drivers/iio/chemical/bme680_i2c.c
> create mode 100644 drivers/iio/chemical/bme680_spi.c
>
> diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
> index 5cb5be7..24790a8 100644
> --- a/drivers/iio/chemical/Kconfig
> +++ b/drivers/iio/chemical/Kconfig
> @@ -21,6 +21,31 @@ config ATLAS_PH_SENSOR
> To compile this driver as module, choose M here: the
> module will be called atlas-ph-sensor.
>
> +config BME680
> + tristate "Bosch Sensortec BME680 sensor driver"
> + depends on (I2C || SPI)
> + select REGMAP
> + select BME680_I2C if (I2C)
> + select BME680_SPI if (SPI)

Don't think you actually need parentheses around any of these, but of
course then again doesn't hurt

> + help
> + Say yes here to build support for Bosch Sensortec BME680 sensor with
> + temperature, pressure, humidity and gas sensing capability.
> +
> + This driver can also be built as a module. If so, the module for I2C
> + would be called bme680_i2c and bme680_spi for SPI support.
> +
> +config BME680_I2C
> + tristate
> + depends on BME680
> + depends on I2C

Wouldn't "depends on I2C && BME680" be cleaner? Maybe someone else
here can tell me if I'm too nit-picky :)

> + select REGMAP_I2C
> +
> +config BME680_SPI
> + tristate
> + depends on BME680
> + depends on SPI

Same only with SPI

> + select REGMAP_SPI
> +
> config CCS811
> tristate "AMS CCS811 VOC sensor"
> depends on I2C
> diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
> index a629b29..2f4c4ba 100644
> --- a/drivers/iio/chemical/Makefile
> +++ b/drivers/iio/chemical/Makefile
> @@ -4,6 +4,9 @@
>
> # When adding new entries keep the list in alphabetical order
> obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
> +obj-$(CONFIG_BME680) += bme680_core.o
> +obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
> +obj-$(CONFIG_BME680_SPI) += bme680_spi.o
> obj-$(CONFIG_CCS811) += ccs811.o
> obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
> obj-$(CONFIG_VZ89X) += vz89x.o
> diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h
> new file mode 100644
> index 0000000..80c4190
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680.h
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef BME680_H_
> +#define BME680_H_
> +
> +#define BME680_REG_CHIP_I2C_ID 0xD0
> +#define BME680_REG_CHIP_SPI_ID 0x50
> +#define BME680_CHIP_ID_VAL 0x61
> +#define BME680_REG_SOFT_RESET 0xE0
> +#define BME680_CMD_SOFTRESET 0xB6
> +#define BME680_REG_STATUS 0x73
> +#define BME680_SPI_MEM_PAGE_BIT BIT(4)
> +#define BME680_SPI_MEM_PAGE_1_VAL 1
> +
> +#define BME680_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
> +#define BME680_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2)
> +#define BME680_OSRS_HUMID_X(osrs_h) ((osrs_h) << 0)
> +
> +#define BME680_REG_TEMP_MSB 0x22
> +#define BME680_REG_PRESS_MSB 0x1F
> +#define BM6880_REG_HUMIDITY_MSB 0x25
> +#define BME680_REG_GAS_MSB 0x2A
> +#define BME680_REG_GAS_R_LSB 0x2B
> +#define BME680_GAS_STAB_BIT BIT(4)
> +
> +#define BME680_REG_CTRL_HUMIDITY 0x72
> +#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
> +
> +#define BME680_REG_CTRL_MEAS 0x74
> +#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
> +#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
> +#define BME680_MODE_MASK GENMASK(1, 0)
> +
> +#define BME680_MODE_FORCED BIT(0)
> +#define BME680_MODE_SLEEP 0
> +
> +#define BME680_REG_CONFIG 0x75
> +#define BME680_FILTER_MASK GENMASK(4, 2)
> +#define BME680_FILTER_COEFF BIT(1)
> +
> +/* TEMP/PRESS/HUMID reading skipped */
> +#define BME680_MEAS_SKIPPED 0x8000
> +
> +#define BME680_MAX_OVERFLOW_VAL 0x40000000
> +#define BME680_HUM_REG_SHIFT_VAL 4
> +#define BME680_BIT_H1_DATA_MSK 0x0F
> +
> +#define BME680_REG_RES_HEAT_RANGE 0x02
> +#define BME680_RHRANGE_MSK 0x30
> +#define BME680_REG_RES_HEAT_VAL 0x00
> +#define BME680_REG_RANGE_SW_ERR 0x04
> +#define BME680_RSERROR_MSK 0xF0
> +#define BME680_REG_RES_HEAT_0 0x5A
> +#define BME680_REG_GAS_WAIT_0 0x64
> +#define BME680_GAS_RANGE_MASK 0x0F
> +#define BME680_ADC_GAS_RES_SHIFT 6
> +#define BME680_AMB_TEMP 25
> +
> +#define BME680_REG_CTRL_GAS_1 0x71
> +#define BME680_RUN_GAS_MASK BIT(4)
> +#define BME680_NB_CONV_MASK GENMASK(3, 0)
> +#define BME680_RUN_GAS_EN BIT(4)
> +#define BME680_NB_CONV_0 0
> +
> +#define BME680_REG_MEAS_STAT_0 0x1D
> +#define BME680_GAS_MEAS_BIT BIT(6)
> +
> +/* Calibration Parameters */
> +#define BME680_T2_LSB_REG 0x8A
> +#define BME680_T3_REG 0x8C
> +#define BME680_P1_LSB_REG 0x8E
> +#define BME680_P2_LSB_REG 0x90
> +#define BME680_P3_REG 0x92
> +#define BME680_P4_LSB_REG 0x94
> +#define BME680_P5_LSB_REG 0x96
> +#define BME680_P7_REG 0x98
> +#define BME680_P6_REG 0x99
> +#define BME680_P8_LSB_REG 0x9C
> +#define BME680_P9_LSB_REG 0x9E
> +#define BME680_P10_REG 0xA0
> +#define BME680_H2_LSB_REG 0xE2
> +#define BME680_H2_MSB_REG 0xE1
> +#define BME680_H1_MSB_REG 0xE3
> +#define BME680_H1_LSB_REG 0xE2
> +#define BME680_H3_REG 0xE4
> +#define BME680_H4_REG 0xE5
> +#define BME680_H5_REG 0xE6
> +#define BME680_H6_REG 0xE7
> +#define BME680_H7_REG 0xE8
> +#define BME680_T1_LSB_REG 0xE9
> +#define BME680_GH2_LSB_REG 0xEB
> +#define BME680_GH1_REG 0xED
> +#define BME680_GH3_REG 0xEE
> +
> +extern const struct regmap_config bme680_regmap_config;
> +
> +int bme680_core_probe(struct device *dev, struct regmap *regmap,
> + const char *name);
> +
> +#endif /* BME680_H_ */
> diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c
> new file mode 100644
> index 0000000..8dd789e
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680_core.c
> @@ -0,0 +1,946 @@
> +// 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 <[email protected]>
> + */
> +#include <linux/acpi.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;
> +};
> +
> +const struct regmap_config bme680_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +};
> +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 const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
> +
> +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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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_MSK);
> +
> + 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,
> + (u8 *) &buf, 2);
> + 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 = (tmp & BME680_RHRANGE_MSK) / 16;
> +
> + 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 = (tmp & BME680_RSERROR_MSK) / 16;
> +
> + return 0;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +static s32 bme680_compensate_temp(struct bme680_data *data,
> + s32 adc_temp)
> +{
> + struct bme680_calib *calib = &data->bme680;
> + s64 var1, var2, var3, calc_temp;
> +
> + var1 = ((s32) adc_temp >> 3) - ((s32) calib->par_t1 << 1);
> + var2 = (var1 * (s32) calib->par_t2) >> 11;
> + var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
> + var3 = ((var3) * ((s32) calib->par_t3 << 4)) >> 14;
> + data->t_fine = (s32) (var2 + var3);
> + calc_temp = (s16) (((data->t_fine * 5) + 128) >> 8);
> +
> + return calc_temp;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 = (((s32)data->t_fine) >> 1) - 64000;
> + var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * (s32)calib->par_p6) >> 2;
> + var2 = var2 + ((var1 * (s32)calib->par_p5) << 1);
> + var2 = (var2 >> 2) + ((s32)calib->par_p4 << 16);
> + var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
> + ((s32)calib->par_p3 << 5)) >> 3) +
> + (((s32)calib->par_p2 * var1) >> 1);
> + var1 = var1 >> 18;
> + var1 = ((32768 + var1) * (s32)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 = ((s32)calib->par_p9 * (((press_comp >> 3) *
> + (press_comp >> 3)) >> 13)) >> 12;
> + var2 = ((press_comp >> 2) * (s32)calib->par_p8) >> 13;
> + var3 = ((press_comp >> 8) * (press_comp >> 8) *
> + (press_comp >> 8) * calib->par_p10) >> 17;
> +
> + press_comp += ((var1 + var2 + var3 + ((s32)calib->par_p7 << 7)) >> 4);
> +
> + return press_comp;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 = (((s32) 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 * (s32) calib->par_h4) /
> + ((s32) 100)) + (((temp_scaled * ((temp_scaled *
> + (s32) calib->par_h5) / 100)) >> 6) / 100) +
> + (s32) (1 << 14))) >> 10;
> + var3 = var1 * var2;
> + var4 = (s32) calib->par_h6 << 7;
> + var4 = (var4 + ((temp_scaled * (s32) calib->par_h7) / 100)) >> 4;
> + var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
> + var6 = (var4 * var5) >> 1;
> + calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
> +
> + if (calc_hum > 100000) /* Cap at 100%rH */
> + calc_hum = 100000;
> + else if (calc_hum < 0)
> + calc_hum = 0;
> +
> + return calc_hum;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 1 for the possible gas range values */
> + u32 lookupTable1[16] = {2147483647u, 2147483647u, 2147483647u,
> + 2147483647u, 2147483647u, 2126008810u,
> + 2147483647u, 2130303777u, 2147483647u,
> + 2147483647u, 2143188679u, 2136746228u,
> + 2147483647u, 2126008810u, 2147483647u,
> + 2147483647u};
> + /* Look up table 2 for the possible gas range values */
> + u32 lookupTable2[16] = {4096000000u, 2048000000u, 1024000000u,
> + 512000000u, 255744255u, 127110228u, 64000000u,
> + 32258064u, 16016016u, 8000000u, 4000000u,
> + 2000000u, 1000000u, 500000u, 250000u, 125000u};
> +
> + var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
> + ((s64) lookupTable1[gas_range])) >> 16;
> + var2 = (((s64) ((s64) gas_res_adc << 15) - 16777216) + var1);
> + var3 = (((s64) lookupTable2[gas_range] * (s64) var1) >> 9);
> + calc_gas_res = (u32) ((var3 + ((s64) var2 >> 1)) / (s64) var2);
> +
> + return calc_gas_res;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 = (heatr_res_x100 + 50) / 100;
> +
> + return heatr_res;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 int bme680_chip_config(struct bme680_data *data)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret;
> + u8 osrs = BME680_OSRS_HUMID_X(data->oversampling_humid + 1);
> + /*
> + * 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);
> + if (ret < 0) {
> + dev_err(dev, "failed to write config register\n");
> + return ret;
> + }
> +
> + osrs = BME680_OSRS_TEMP_X(data->oversampling_temp + 1) |
> + BME680_OSRS_PRESS_X(data->oversampling_press + 1);
> +
> + 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, "failted to write gas_wait_0 register\n");
> + return ret;
> + }
> +
> + /* Selecting the runGas and NB conversion settings for the sensor */
> + ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
> + BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
> + BME680_RUN_GAS_EN | BME680_NB_CONV_0);
> + if (ret < 0)
> + dev_err(dev, "failed to write ctrl_gas_1 register\n");
> +
> + return ret;
> +}
> +
> +/* Outputs temperature measurement in degC */
> +static int bme680_read_temp(struct bme680_data *data,
> + int *val, int *val2)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret = 0;
> + __be32 tmp = 0;
> + s32 adc_temp, 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,
> + (u8 *) &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 && val2) {
> + *val = comp_temp;
> + *val2 = 100;
> + return IIO_VAL_FRACTIONAL;
> + }
> +
> + return ret;
> +}
> +
> +/* Outputs pressure measurement in hPa */
> +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, NULL);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
> + (u8 *) &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;
> +}
> +
> +/* Outputs humidity measurement in %r.H */
> +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 so we get a reading of t_fine */
> + ret = bme680_read_temp(data, NULL, NULL);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
> + (u8 *) &tmp, 2);
> + 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;
> +}
> +
> +/* Outputs gas measurement in ohm */
> +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 a single 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;
> + }
> +
> + if ((check & BME680_GAS_STAB_BIT) == 0) {
> + /*
> + * 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.
> + */
> + dev_err(dev, "heater failed to reach the target temperature\n");
> + return -EINVAL;
> + }
> +
> + ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
> + (u8 *) &tmp, 2);
> + 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, val2);
> + 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 = 1 << data->oversampling_temp;
> + return IIO_VAL_INT;
> + case IIO_PRESSURE:
> + *val = 1 << data->oversampling_press;
> + return IIO_VAL_INT;
> + case IIO_HUMIDITYRELATIVE:
> + *val = 1 << data->oversampling_humid;
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
> + int val)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
> + if (bme680_oversampling_avail[i] == val) {
> + data->oversampling_temp = ilog2(val);
> +
> + return bme680_chip_config(data);
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
> + int val)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
> + if (bme680_oversampling_avail[i] == val) {
> + data->oversampling_press = ilog2(val);
> +
> + return bme680_chip_config(data);
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
> + int val)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
> + if (bme680_oversampling_avail[i] == val) {
> + data->oversampling_humid = ilog2(val);
> +
> + return bme680_chip_config(data);
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +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);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + switch (chan->type) {
> + case IIO_TEMP:
> + return bme680_write_oversampling_ratio_temp(data, val);
> + case IIO_PRESSURE:
> + return bme680_write_oversampling_ratio_press(data, val);
> + case IIO_HUMIDITYRELATIVE:
> + return bme680_write_oversampling_ratio_humid(data, val);
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +
> + 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);
> +}
> +
> +static void bme680_core_remove(void *arg)
> +{
> + iio_device_unregister(arg);
> +}
> +
> +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;
> +
> + ret = devm_add_action(dev, bme680_core_remove, indio_dev);
> + if (ret < 0) {
> + dev_err(dev, "failed to register remove action\n");
> + return ret;
> + }
> +
> + 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->dev.parent = dev;
> + 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 = ilog2(2); /* 2X oversampling rate */
> + data->oversampling_press = ilog2(4); /* 4X oversampling rate */
> + data->oversampling_temp = ilog2(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 iio_device_register(indio_dev);
> +}
> +EXPORT_SYMBOL_GPL(bme680_core_probe);
> +
> +MODULE_AUTHOR("Himanshu Jha <[email protected]>");
> +MODULE_DESCRIPTION("Bosch BME680 Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c
> new file mode 100644
> index 0000000..a3a77cf
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680_i2c.c
> @@ -0,0 +1,83 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * BME680 - I2C Driver
> + *
> + * Copyright (C) 2018 Himanshu Jha <[email protected]>
> + *
> + * 7-Bit I2C slave address is:
> + * - 0x76 if SDO is pulled to GND
> + * - 0x77 if SDO is pulled to VDDIO
> + *
> + * Note: SDO pin cannot be left floating otherwise I2C address
> + * will be undefined.
> + */
> +#include <linux/acpi.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#include "bme680.h"
> +
> +static int bme680_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct regmap *regmap;
> + const char *name = NULL;
> + unsigned int val;
> + int ret;
> +
> + regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(&client->dev, "Failed to register i2c regmap %d\n",
> + (int)PTR_ERR(regmap));
> + return PTR_ERR(regmap);
> + }
> +
> + ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
> + BME680_CMD_SOFTRESET);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_read(regmap, BME680_REG_CHIP_I2C_ID, &val);
> + if (ret < 0) {
> + dev_err(&client->dev, "Error reading I2C chip ID\n");
> + return ret;
> + }
> +
> + if (val != BME680_CHIP_ID_VAL) {
> + dev_err(&client->dev, "Wrong chip ID, got %x expected %x\n",
> + val, BME680_CHIP_ID_VAL);
> + return -ENODEV;
> + }
> +
> + if (id)
> + name = id->name;
> +
> + return bme680_core_probe(&client->dev, regmap, name);
> +}
> +
> +static const struct i2c_device_id bme680_i2c_id[] = {
> + {"bme680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
> +
> +static const struct acpi_device_id bme680_acpi_match[] = {
> + {"BME0680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
> +
> +static struct i2c_driver bme680_i2c_driver = {
> + .driver = {
> + .name = "bme680_i2c",
> + .acpi_match_table = ACPI_PTR(bme680_acpi_match),
> + },
> + .probe = bme680_i2c_probe,
> + .id_table = bme680_i2c_id,
> +};
> +module_i2c_driver(bme680_i2c_driver);
> +
> +MODULE_AUTHOR("Himanshu Jha <[email protected]>");
> +MODULE_DESCRIPTION("BME680 I2C driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c
> new file mode 100644
> index 0000000..1fb3466
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680_spi.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * BME680 - SPI Driver
> + *
> + * Copyright (C) 2018 Himanshu Jha <[email protected]>
> + */
> +#include <linux/acpi.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/spi/spi.h>
> +
> +#include "bme680.h"
> +
> +static int bme680_regmap_spi_write(void *context, const void *data,
> + size_t count)
> +{
> + struct spi_device *spi = context;
> + u8 buf[2];
> +
> + memcpy(buf, data, 2);
> + /*
> + * The SPI register address (= full register address without bit 7)
> + * and the write command (bit7 = RW = '0')
> + */
> + buf[0] &= ~0x80;
> +
> + return spi_write_then_read(spi, buf, 2, NULL, 0);
> +}
> +
> +static int bme680_regmap_spi_read(void *context, const void *reg,
> + size_t reg_size, void *val, size_t val_size)
> +{
> + struct spi_device *spi = context;
> +
> + return spi_write_then_read(spi, reg, reg_size, val, val_size);
> +}
> +
> +static struct regmap_bus bme680_regmap_bus = {
> + .write = bme680_regmap_spi_write,
> + .read = bme680_regmap_spi_read,
> + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> + .val_format_endian_default = REGMAP_ENDIAN_BIG,
> +};
> +
> +static int bme680_spi_probe(struct spi_device *spi)
> +{
> + const struct spi_device_id *id = spi_get_device_id(spi);
> + struct regmap *regmap;
> + unsigned int val;
> + int ret;
> +
> + spi->bits_per_word = 8;
> + ret = spi_setup(spi);
> + if (ret < 0) {
> + dev_err(&spi->dev, "spi_setup failed!\n");
> + return ret;
> + }
> +
> + regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
> + &spi->dev, &bme680_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(&spi->dev, "Failed to register spi regmap %d\n",
> + (int)PTR_ERR(regmap));
> + return PTR_ERR(regmap);
> + }
> +
> + ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
> + BME680_CMD_SOFTRESET);
> + if (ret < 0)
> + return ret;
> +
> + /* after power-on reset, Page 0(0x80-0xFF) of spi_mem_page is active */
> + ret = regmap_read(regmap, BME680_REG_CHIP_SPI_ID, &val);
> + if (ret < 0) {
> + dev_err(&spi->dev, "Error reading SPI chip ID\n");
> + return ret;
> + }
> +
> + if (val != BME680_CHIP_ID_VAL) {
> + dev_err(&spi->dev, "Wrong chip ID, got %x expected %x\n",
> + val, BME680_CHIP_ID_VAL);
> + return -ENODEV;
> + }
> + /*
> + * select Page 1 of spi_mem_page to enable access to
> + * to registers from address 0x00 to 0x7F.
> + */
> + ret = regmap_write_bits(regmap, BME680_REG_STATUS,
> + BME680_SPI_MEM_PAGE_BIT,
> + BME680_SPI_MEM_PAGE_1_VAL);
> + if (ret < 0) {
> + dev_err(&spi->dev, "failed to set page 1 of spi_mem_page\n");
> + return ret;
> + }
> +
> + return bme680_core_probe(&spi->dev, regmap, id->name);
> +}
> +
> +static const struct spi_device_id bme680_spi_id[] = {
> + {"bme680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(spi, bme680_spi_id);
> +
> +static const struct acpi_device_id bme680_acpi_match[] = {
> + {"BME0680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
> +
> +static struct spi_driver bme680_spi_driver = {
> + .driver = {
> + .name = "bme680_spi",
> + .acpi_match_table = ACPI_PTR(bme680_acpi_match),
> + },
> + .probe = bme680_spi_probe,
> + .id_table = bme680_spi_id,
> +};
> +module_spi_driver(bme680_spi_driver);
> +
> +MODULE_AUTHOR("Himanshu Jha <[email protected]>");
> +MODULE_DESCRIPTION("Bosch BME680 SPI driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2018-07-12 13:31:07

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

On Wed, 11 Jul 2018 17:40:07 -0700
Matt Ranostay <[email protected]> wrote:

> On Wed, Jul 11, 2018 at 5:13 AM, Himanshu Jha
> <[email protected]> wrote:
> > Bosch BME680 is a 4-in-1 sensor with temperature, pressure, humidity
> > and gas sensing capability. It supports both I2C and SPI communication
> > protocol for effective data communication.
> >
> > The device supports two modes:
> >
> > 1. Sleep mode
> > 2. Forced mode
> >
> > The measurements only takes place when forced mode is triggered and a
> > single TPHG cycle is performed by the sensor. The sensor automatically
> > goes to sleep after afterwards.
> >
> > The device has various calibration constants/parameters programmed into
> > devices' non-volatile memory(NVM) during production and can't be altered
> > by the user. These constants are used in the compensation functions to
> > get the required compensated readings along with the raw data. The
> > compensation functions/algorithms are provided by Bosch Sensortec GmbH
> > via their API[1]. As these don't change during the measurement cycle,
> > therefore we read and store them at the probe. The default configs
> > supplied by Bosch are also set at probe.
> >
> > 0-day tested with build success.
> >
> > GSoC-2018: https://summerofcode.withgoogle.com/projects/#6691473790074880
> > Mentor: Daniel Baluta
> > [1] https://github.com/BoschSensortec/BME680_driver
> > Datasheet:
> > https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
> >
> > Cc: Daniel Baluta <[email protected]>
> > Signed-off-by: Himanshu Jha <[email protected]>
> > ---
> >
> > v3:
> > -moved files to chemical directory instead of a dedicated directory.
> > -read calibration parameters serially with endian conversions.
> > -drop some return ret.
> > -removed few unnecessary casts safely.
> > -added 'u' suffix to explicitly specify unsigned for large values
> > and thereby fixing comiler warning.
> > -left aligned all comments.
> > -added a comment explaining heater stability failure.
> >
> > v2:
> > -Used devm_add_action() to add a generic remove method for
> > both I2C & SPI driver.
> > -Introduction of compensation functions.
> > -chip initialisation routines moved to respective I2C and SPI
> > driver.
> > -Introduction of gas sensing rountines.
> > -Simplified Kconfig to reduce various options.
> >
> > drivers/iio/chemical/Kconfig | 25 +
> > drivers/iio/chemical/Makefile | 3 +
> > drivers/iio/chemical/bme680.h | 99 ++++
> > drivers/iio/chemical/bme680_core.c | 946 +++++++++++++++++++++++++++++++++++++
> > drivers/iio/chemical/bme680_i2c.c | 83 ++++
> > drivers/iio/chemical/bme680_spi.c | 123 +++++
> > 6 files changed, 1279 insertions(+)
> > create mode 100644 drivers/iio/chemical/bme680.h
> > create mode 100644 drivers/iio/chemical/bme680_core.c
> > create mode 100644 drivers/iio/chemical/bme680_i2c.c
> > create mode 100644 drivers/iio/chemical/bme680_spi.c
> >
> > diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
> > index 5cb5be7..24790a8 100644
> > --- a/drivers/iio/chemical/Kconfig
> > +++ b/drivers/iio/chemical/Kconfig
> > @@ -21,6 +21,31 @@ config ATLAS_PH_SENSOR
> > To compile this driver as module, choose M here: the
> > module will be called atlas-ph-sensor.
> >
> > +config BME680
> > + tristate "Bosch Sensortec BME680 sensor driver"
> > + depends on (I2C || SPI)
> > + select REGMAP
> > + select BME680_I2C if (I2C)
> > + select BME680_SPI if (SPI)
>
> Don't think you actually need parentheses around any of these, but of
> course then again doesn't hurt

Nice to tidy this one up, though if it is all there is I can do that whilst
applying. So don't bother sending a v4 for this until other reviews are in.

>
> > + help
> > + Say yes here to build support for Bosch Sensortec BME680 sensor with
> > + temperature, pressure, humidity and gas sensing capability.
> > +
> > + This driver can also be built as a module. If so, the module for I2C
> > + would be called bme680_i2c and bme680_spi for SPI support.
> > +
> > +config BME680_I2C
> > + tristate
> > + depends on BME680
> > + depends on I2C
>
> Wouldn't "depends on I2C && BME680" be cleaner? Maybe someone else
> here can tell me if I'm too nit-picky :)

You said it ;) Can't say I care either way on these.

>
> > + select REGMAP_I2C
> > +
> > +config BME680_SPI
> > + tristate
> > + depends on BME680
> > + depends on SPI
>
> Same only with SPI
>
> > + select REGMAP_SPI
> > +
> > config CCS811
> > tristate "AMS CCS811 VOC sensor"
> > depends on I2C
...

Matt, please give us indication if you don't have anything else to say :)
Saves on a lot of scrolling and wondering if we missed something (which
of course I might have done!)



2018-07-13 21:00:18

by David Frey

[permalink] [raw]
Subject: Re: [PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

Hi Himanshu Jha,

First a bit of background. I'm working on a device which will contain a
bme680 sensor. A colleague of mine started work on a Linux kernel
driver for the chip a little while ago. The (WIP) driver can be found
here:
https://github.com/mangOH/mangOH/tree/master/linux_kernel_modules/bme680

This driver is written targeting an older kernel (3.18.x) because that's
the kernel we're stuck on for now. Rather than writing the driver from
scratch, what we did was write the Linux kernel driver as a wrapper
around the Bosch code. My theory at the time was that Bosch made the
chip, so they probably know what they're doing when it comes to writing
a driver library. After having looked at the code in more detail, I'm
less confident that our approach was the best one. I'm not attempting
to upstream the driver built by my colleague and I'm not trying to
request review of this code either. I simply want to make you aware of
it so that you can look at it to get some ideas.

I have included a number of comments on your driver below. Keep up the
good work!



On 7/11/2018 5:13 AM, Himanshu Jha wrote:
> Bosch BME680 is a 4-in-1 sensor with temperature, pressure, humidity
> and gas sensing capability. It supports both I2C and SPI communication
> protocol for effective data communication.
>
> The device supports two modes:
>
> 1. Sleep mode
> 2. Forced mode
>
> The measurements only takes place when forced mode is triggered and a
> single TPHG cycle is performed by the sensor. The sensor automatically
> goes to sleep after afterwards.
>
> The device has various calibration constants/parameters programmed into
> devices' non-volatile memory(NVM) during production and can't be altered
> by the user. These constants are used in the compensation functions to
> get the required compensated readings along with the raw data. The
> compensation functions/algorithms are provided by Bosch Sensortec GmbH
> via their API[1]. As these don't change during the measurement cycle,
> therefore we read and store them at the probe. The default configs
> supplied by Bosch are also set at probe.
>
> 0-day tested with build success.
>
> GSoC-2018: https://summerofcode.withgoogle.com/projects/#6691473790074880
> Mentor: Daniel Baluta
> [1] https://github.com/BoschSensortec/BME680_driver
> Datasheet:
> https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
>
> Cc: Daniel Baluta <[email protected]>
> Signed-off-by: Himanshu Jha <[email protected]>
> ---
>
> v3:
> -moved files to chemical directory instead of a dedicated directory.
> -read calibration parameters serially with endian conversions.
> -drop some return ret.
> -removed few unnecessary casts safely.
> -added 'u' suffix to explicitly specify unsigned for large values
> and thereby fixing comiler warning.
> -left aligned all comments.
> -added a comment explaining heater stability failure.
>
> v2:
> -Used devm_add_action() to add a generic remove method for
> both I2C & SPI driver.
> -Introduction of compensation functions.
> -chip initialisation routines moved to respective I2C and SPI
> driver.
> -Introduction of gas sensing rountines.
> -Simplified Kconfig to reduce various options.
>
> drivers/iio/chemical/Kconfig | 25 +
> drivers/iio/chemical/Makefile | 3 +
> drivers/iio/chemical/bme680.h | 99 ++++
> drivers/iio/chemical/bme680_core.c | 946 +++++++++++++++++++++++++++++++++++++
> drivers/iio/chemical/bme680_i2c.c | 83 ++++
> drivers/iio/chemical/bme680_spi.c | 123 +++++
> 6 files changed, 1279 insertions(+)
> create mode 100644 drivers/iio/chemical/bme680.h
> create mode 100644 drivers/iio/chemical/bme680_core.c
> create mode 100644 drivers/iio/chemical/bme680_i2c.c
> create mode 100644 drivers/iio/chemical/bme680_spi.c
>
> diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
> index 5cb5be7..24790a8 100644
> --- a/drivers/iio/chemical/Kconfig
> +++ b/drivers/iio/chemical/Kconfig
> @@ -21,6 +21,31 @@ config ATLAS_PH_SENSOR
> To compile this driver as module, choose M here: the
> module will be called atlas-ph-sensor.
>
> +config BME680
> + tristate "Bosch Sensortec BME680 sensor driver"
> + depends on (I2C || SPI)
> + select REGMAP
> + select BME680_I2C if (I2C)
> + select BME680_SPI if (SPI)
> + help
> + Say yes here to build support for Bosch Sensortec BME680 sensor with
> + temperature, pressure, humidity and gas sensing capability.
> +
> + This driver can also be built as a module. If so, the module for I2C
> + would be called bme680_i2c and bme680_spi for SPI support.
> +
> +config BME680_I2C
> + tristate
> + depends on BME680
> + depends on I2C
> + select REGMAP_I2C
> +
> +config BME680_SPI
> + tristate
> + depends on BME680
> + depends on SPI
> + select REGMAP_SPI
> +
> config CCS811
> tristate "AMS CCS811 VOC sensor"
> depends on I2C
> diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
> index a629b29..2f4c4ba 100644
> --- a/drivers/iio/chemical/Makefile
> +++ b/drivers/iio/chemical/Makefile
> @@ -4,6 +4,9 @@
>
> # When adding new entries keep the list in alphabetical order
> obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-ph-sensor.o
> +obj-$(CONFIG_BME680) += bme680_core.o
> +obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
> +obj-$(CONFIG_BME680_SPI) += bme680_spi.o
> obj-$(CONFIG_CCS811) += ccs811.o
> obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
> obj-$(CONFIG_VZ89X) += vz89x.o
> diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h
> new file mode 100644
> index 0000000..80c4190
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680.h
> @@ -0,0 +1,99 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef BME680_H_
> +#define BME680_H_
> +
> +#define BME680_REG_CHIP_I2C_ID 0xD0
> +#define BME680_REG_CHIP_SPI_ID 0x50
> +#define BME680_CHIP_ID_VAL 0x61
Try to be consistent with the indenting of the defines. I think this
would be clearest:
#define BME680_REG_X 0x00
#define BME680_X_FOO_EN_MASK BIT(0)
#define BME680_X_BAR_MASK GENMASK(3, 1)
#define BME680_BAR_VAL1 3
#define BME680_BAR_VAL2 7

This way the register, field definition and field values are all
visually distinctive.

> +#define BME680_REG_SOFT_RESET 0xE0
The datasheet says that the soft reset register differs for I2C and SPI.
For I2C it is 0xE0 and for SPI it is 0x60 when page 0 is selected.

> +#define BME680_CMD_SOFTRESET 0xB6
> +#define BME680_REG_STATUS 0x73
> +#define BME680_SPI_MEM_PAGE_BIT BIT(4)
> +#define BME680_SPI_MEM_PAGE_1_VAL 1
> +
> +#define BME680_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
> +#define BME680_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2)
> +#define BME680_OSRS_HUMID_X(osrs_h) ((osrs_h) << 0)
You could use the FIELD_PREP macro from <linux/bitfield.h> to eliminate
the need for these macros. For example:
ctrl_meas_reg = FIELD_PREP(BME680_OSRS_TEMP_MASK, temp_val) |
FIELD_PREP(BME680_OSRS_PRESS_MASK, press_val) |
FIELD_PREP(BME880_MODE_MASK, mode_val);

> +
> +#define BME680_REG_TEMP_MSB 0x22
> +#define BME680_REG_PRESS_MSB 0x1F
> +#define BM6880_REG_HUMIDITY_MSB 0x25
> +#define BME680_REG_GAS_MSB 0x2A
> +#define BME680_REG_GAS_R_LSB 0x2B
> +#define BME680_GAS_STAB_BIT BIT(4)
> +
> +#define BME680_REG_CTRL_HUMIDITY 0x72
> +#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
> +
> +#define BME680_REG_CTRL_MEAS 0x74
> +#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
> +#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
> +#define BME680_MODE_MASK GENMASK(1, 0)
> +
> +#define BME680_MODE_FORCED BIT(0)
> +#define BME680_MODE_SLEEP 0
This should be:
#define BME680_MODE_SLEEP 0
#define BME680_MODE_FORCED 1

> +
> +#define BME680_REG_CONFIG 0x75
> +#define BME680_FILTER_MASK GENMASK(4, 2)
> +#define BME680_FILTER_COEFF BIT(1)
> +
> +/* TEMP/PRESS/HUMID reading skipped */
> +#define BME680_MEAS_SKIPPED 0x8000
> +
> +#define BME680_MAX_OVERFLOW_VAL 0x40000000
> +#define BME680_HUM_REG_SHIFT_VAL 4
> +#define BME680_BIT_H1_DATA_MSK 0x0F
> +
> +#define BME680_REG_RES_HEAT_RANGE 0x02
> +#define BME680_RHRANGE_MSK 0x30
> +#define BME680_REG_RES_HEAT_VAL 0x00
> +#define BME680_REG_RANGE_SW_ERR 0x04
> +#define BME680_RSERROR_MSK 0xF0
> +#define BME680_REG_RES_HEAT_0 0x5A
> +#define BME680_REG_GAS_WAIT_0 0x64
> +#define BME680_GAS_RANGE_MASK 0x0F
> +#define BME680_ADC_GAS_RES_SHIFT 6
> +#define BME680_AMB_TEMP 25
> +
> +#define BME680_REG_CTRL_GAS_1 0x71
> +#define BME680_RUN_GAS_MASK BIT(4)
> +#define BME680_NB_CONV_MASK GENMASK(3, 0)
> +#define BME680_RUN_GAS_EN BIT(4)
> +#define BME680_NB_CONV_0 0
> +
> +#define BME680_REG_MEAS_STAT_0 0x1D
> +#define BME680_GAS_MEAS_BIT BIT(6)
> +
> +/* Calibration Parameters */
> +#define BME680_T2_LSB_REG 0x8A
> +#define BME680_T3_REG 0x8C
> +#define BME680_P1_LSB_REG 0x8E
> +#define BME680_P2_LSB_REG 0x90
> +#define BME680_P3_REG 0x92
> +#define BME680_P4_LSB_REG 0x94
> +#define BME680_P5_LSB_REG 0x96
> +#define BME680_P7_REG 0x98
> +#define BME680_P6_REG 0x99
> +#define BME680_P8_LSB_REG 0x9C
> +#define BME680_P9_LSB_REG 0x9E
> +#define BME680_P10_REG 0xA0
> +#define BME680_H2_LSB_REG 0xE2
> +#define BME680_H2_MSB_REG 0xE1
> +#define BME680_H1_MSB_REG 0xE3
> +#define BME680_H1_LSB_REG 0xE2
> +#define BME680_H3_REG 0xE4
> +#define BME680_H4_REG 0xE5
> +#define BME680_H5_REG 0xE6
> +#define BME680_H6_REG 0xE7
> +#define BME680_H7_REG 0xE8
> +#define BME680_T1_LSB_REG 0xE9
> +#define BME680_GH2_LSB_REG 0xEB
> +#define BME680_GH1_REG 0xED
> +#define BME680_GH3_REG 0xEE
> +
> +extern const struct regmap_config bme680_regmap_config;
> +
> +int bme680_core_probe(struct device *dev, struct regmap *regmap,
> + const char *name);
> +
> +#endif /* BME680_H_ */
> diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c
> new file mode 100644
> index 0000000..8dd789e
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680_core.c
> @@ -0,0 +1,946 @@
> +// 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 <[email protected]>
> + */
> +#include <linux/acpi.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;
> +};
> +
> +const struct regmap_config bme680_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> +};
> +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 const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
> + > +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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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,
> + (u8 *) &buf, 2);
> + 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_MSK);
> +
> + 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,
> + (u8 *) &buf, 2);
> + 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 = (tmp & BME680_RHRANGE_MSK) / 16;
> +
> + 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 = (tmp & BME680_RSERROR_MSK) / 16;
> +
> + return 0;
> +}
> +
> +/* Taken from Bosch BME680 API */

I think there should be a link to the Bosch code
(https://github.com/BoschSensortec/BME680_driver/) somewhere within the
comments of this file. Maybe it belongs at the top of this file?

> +static s32 bme680_compensate_temp(struct bme680_data *data,
> + s32 adc_temp)
> +{
> + struct bme680_calib *calib = &data->bme680;
> + s64 var1, var2, var3, calc_temp;
> +
> + var1 = ((s32) adc_temp >> 3) - ((s32) calib->par_t1 << 1);
> + var2 = (var1 * (s32) calib->par_t2) >> 11;
> + var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
> + var3 = ((var3) * ((s32) calib->par_t3 << 4)) >> 14;
> + data->t_fine = (s32) (var2 + var3);
> + calc_temp = (s16) (((data->t_fine * 5) + 128) >> 8);
> +
> + return calc_temp;
I think the meaning of the s32 returned should be documented. Based on
code elsewhere in the driver I'm guessing it's degrees celcius * 100.
The same comment applies to the other compensate functions.

> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 = (((s32)data->t_fine) >> 1) - 64000;
> + var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * (s32)calib->par_p6) >> 2;
> + var2 = var2 + ((var1 * (s32)calib->par_p5) << 1);
> + var2 = (var2 >> 2) + ((s32)calib->par_p4 << 16);
> + var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
> + ((s32)calib->par_p3 << 5)) >> 3) +
> + (((s32)calib->par_p2 * var1) >> 1);
> + var1 = var1 >> 18;
> + var1 = ((32768 + var1) * (s32)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 = ((s32)calib->par_p9 * (((press_comp >> 3) *
> + (press_comp >> 3)) >> 13)) >> 12;
> + var2 = ((press_comp >> 2) * (s32)calib->par_p8) >> 13;
> + var3 = ((press_comp >> 8) * (press_comp >> 8) *
> + (press_comp >> 8) * calib->par_p10) >> 17;
> +
> + press_comp += ((var1 + var2 + var3 + ((s32)calib->par_p7 << 7)) >> 4);
> +
> + return press_comp;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 = (((s32) 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 * (s32) calib->par_h4) /
> + ((s32) 100)) + (((temp_scaled * ((temp_scaled *
> + (s32) calib->par_h5) / 100)) >> 6) / 100) +
> + (s32) (1 << 14))) >> 10;
> + var3 = var1 * var2;
> + var4 = (s32) calib->par_h6 << 7;
> + var4 = (var4 + ((temp_scaled * (s32) calib->par_h7) / 100)) >> 4;
> + var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
> + var6 = (var4 * var5) >> 1;
> + calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
> +
> + if (calc_hum > 100000) /* Cap at 100%rH */
> + calc_hum = 100000;
> + else if (calc_hum < 0)
> + calc_hum = 0;
> +
> + return calc_hum;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 1 for the possible gas range values */
> + u32 lookupTable1[16] = {2147483647u, 2147483647u, 2147483647u,
> + 2147483647u, 2147483647u, 2126008810u,
> + 2147483647u, 2130303777u, 2147483647u,
> + 2147483647u, 2143188679u, 2136746228u,
> + 2147483647u, 2126008810u, 2147483647u,
> + 2147483647u};
> + /* Look up table 2 for the possible gas range values */
> + u32 lookupTable2[16] = {4096000000u, 2048000000u, 1024000000u,
> + 512000000u, 255744255u, 127110228u, 64000000u,
> + 32258064u, 16016016u, 8000000u, 4000000u,
> + 2000000u, 1000000u, 500000u, 250000u, 125000u};
> +
> + var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
> + ((s64) lookupTable1[gas_range])) >> 16;
> + var2 = (((s64) ((s64) gas_res_adc << 15) - 16777216) + var1);
> + var3 = (((s64) lookupTable2[gas_range] * (s64) var1) >> 9);
> + calc_gas_res = (u32) ((var3 + ((s64) var2 >> 1)) / (s64) var2);
> +
> + return calc_gas_res;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 = (heatr_res_x100 + 50) / 100;
> +
> + return heatr_res;
> +}
> +
> +/* Taken from Bosch BME680 API */
> +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 int bme680_chip_config(struct bme680_data *data)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret;
> + u8 osrs = BME680_OSRS_HUMID_X(data->oversampling_humid + 1);
> + /*
> + * Highly recommended to set oversampling of humidity before
> + * temperature/pressure oversampling.
> + */
I think you are referring to this snippet from the datasheet: "It is
highly recommended to set first osrs_h<2:0> followed by osrs_t<2:0> and
osrs_p<2:0> in one write command (see Section 3.3)." My interpretation
of this is that they are saying that you should do one bulk write rather
than writing the fields individually. Maybe they're just recommending
this for efficiency reasons. I'm not really sure though. I read
through section 3.3 and I couldn't find any justification to backup this
rather prominent suggestion.

> + 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);
> + if (ret < 0) {
> + dev_err(dev, "failed to write config register\n");
> + return ret;
> + }
> +
> + osrs = BME680_OSRS_TEMP_X(data->oversampling_temp + 1) |
> + BME680_OSRS_PRESS_X(data->oversampling_press + 1);
> +
> + 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, "failted to write gas_wait_0 register\n");
> + return ret;
> + }
> +
> + /* Selecting the runGas and NB conversion settings for the sensor */
> + ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
> + BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
> + BME680_RUN_GAS_EN | BME680_NB_CONV_0);
> + if (ret < 0)
> + dev_err(dev, "failed to write ctrl_gas_1 register\n");
> +
> + return ret;
> +}
> +
> +/* Outputs temperature measurement in degC */
> +static int bme680_read_temp(struct bme680_data *data,
> + int *val, int *val2)
> +{
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret = 0;
> + __be32 tmp = 0;
> + s32 adc_temp, 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,
> + (u8 *) &tmp, 3);
> + if (ret < 0) {
> + dev_err(dev, "failed to read temperature\n");
> + return ret;
> + }
I think the value you're reading from the register may actually be from
a previous request. I think you need to poll meas_status_0 (0x1D) field
new_data_0 (bit 7) to wait for new data after setting the mode to
forced. You can see that's what Bosch's code does:
https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L1227

> +
> + 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 && val2) {
> + *val = comp_temp;
> + *val2 = 100;
> + return IIO_VAL_FRACTIONAL;
> + }
> +
> + return ret;
> +}
> +
> +/* Outputs pressure measurement in hPa */
> +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, NULL);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
> + (u8 *) &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;
> +}
> +
> +/* Outputs humidity measurement in %r.H */
> +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 so we get a reading of t_fine */
> + ret = bme680_read_temp(data, NULL, NULL);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
> + (u8 *) &tmp, 2);
> + 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;
> +}
> +
> +/* Outputs gas measurement in ohm */
> +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 a single 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;
> + }
> +
> + if ((check & BME680_GAS_STAB_BIT) == 0) {
> + /*
> + * 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.
> + */
> + dev_err(dev, "heater failed to reach the target temperature\n");
> + return -EINVAL;
> + }
> +
> + ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
> + (u8 *) &tmp, 2);
> + 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, val2);
> + 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 = 1 << data->oversampling_temp;
> + return IIO_VAL_INT;
> + case IIO_PRESSURE:
> + *val = 1 << data->oversampling_press;
> + return IIO_VAL_INT;
> + case IIO_HUMIDITYRELATIVE:
> + *val = 1 << data->oversampling_humid;
> + return IIO_VAL_INT;
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
> + int val)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
> + if (bme680_oversampling_avail[i] == val) {
> + data->oversampling_temp = ilog2(val);
> +
> + return bme680_chip_config(data);
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
> + int val)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
> + if (bme680_oversampling_avail[i] == val) {
> + data->oversampling_press = ilog2(val);
> +
> + return bme680_chip_config(data);
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
> + int val)
> +{
> + int i;
> +
> + for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); ++i) {
> + if (bme680_oversampling_avail[i] == val) {
> + data->oversampling_humid = ilog2(val);
> +
> + return bme680_chip_config(data);
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> +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);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + switch (chan->type) {
> + case IIO_TEMP:
> + return bme680_write_oversampling_ratio_temp(data, val);
> + case IIO_PRESSURE:
> + return bme680_write_oversampling_ratio_press(data, val);
> + case IIO_HUMIDITYRELATIVE:
> + return bme680_write_oversampling_ratio_humid(data, val);
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +
> + 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);
> +}
> +
> +static void bme680_core_remove(void *arg)
> +{
> + iio_device_unregister(arg);
> +}
> +
> +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;
> +
> + ret = devm_add_action(dev, bme680_core_remove, indio_dev);
> + if (ret < 0) {
> + dev_err(dev, "failed to register remove action\n");
> + return ret;
> + }
> +
> + 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->dev.parent = dev;
> + 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 = ilog2(2); /* 2X oversampling rate */
> + data->oversampling_press = ilog2(4); /* 4X oversampling rate */
> + data->oversampling_temp = ilog2(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 iio_device_register(indio_dev);
> +}
> +EXPORT_SYMBOL_GPL(bme680_core_probe);
> +
> +MODULE_AUTHOR("Himanshu Jha <[email protected]>");
> +MODULE_DESCRIPTION("Bosch BME680 Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c
> new file mode 100644
> index 0000000..a3a77cf
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680_i2c.c
> @@ -0,0 +1,83 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * BME680 - I2C Driver
> + *
> + * Copyright (C) 2018 Himanshu Jha <[email protected]>
> + *
> + * 7-Bit I2C slave address is:
> + * - 0x76 if SDO is pulled to GND
> + * - 0x77 if SDO is pulled to VDDIO
> + *
> + * Note: SDO pin cannot be left floating otherwise I2C address
> + * will be undefined.
> + */
> +#include <linux/acpi.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#include "bme680.h"
> +
> +static int bme680_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct regmap *regmap;
> + const char *name = NULL;
> + unsigned int val;
> + int ret;
> +
> + regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(&client->dev, "Failed to register i2c regmap %d\n",
> + (int)PTR_ERR(regmap));
> + return PTR_ERR(regmap);
> + }
> +
> + ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
> + BME680_CMD_SOFTRESET);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_read(regmap, BME680_REG_CHIP_I2C_ID, &val);
> + if (ret < 0) {
> + dev_err(&client->dev, "Error reading I2C chip ID\n");
> + return ret;
> + }
> +
> + if (val != BME680_CHIP_ID_VAL) {
> + dev_err(&client->dev, "Wrong chip ID, got %x expected %x\n",
> + val, BME680_CHIP_ID_VAL);
> + return -ENODEV;
> + }
> +
> + if (id)
> + name = id->name;
> +
> + return bme680_core_probe(&client->dev, regmap, name);
> +}
> +
> +static const struct i2c_device_id bme680_i2c_id[] = {
> + {"bme680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
> +
> +static const struct acpi_device_id bme680_acpi_match[] = {
> + {"BME0680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
> +
> +static struct i2c_driver bme680_i2c_driver = {
> + .driver = {
> + .name = "bme680_i2c",
> + .acpi_match_table = ACPI_PTR(bme680_acpi_match),
> + },
> + .probe = bme680_i2c_probe,
> + .id_table = bme680_i2c_id,
> +};
> +module_i2c_driver(bme680_i2c_driver);
> +
> +MODULE_AUTHOR("Himanshu Jha <[email protected]>");
> +MODULE_DESCRIPTION("BME680 I2C driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c
> new file mode 100644
> index 0000000..1fb3466
> --- /dev/null
> +++ b/drivers/iio/chemical/bme680_spi.c
> @@ -0,0 +1,123 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * BME680 - SPI Driver
> + *
> + * Copyright (C) 2018 Himanshu Jha <[email protected]>
> + */
> +#include <linux/acpi.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/spi/spi.h>
> +
> +#include "bme680.h"
> +
> +static int bme680_regmap_spi_write(void *context, const void *data,
> + size_t count)
> +{
> + struct spi_device *spi = context;
> + u8 buf[2];
> +
> + memcpy(buf, data, 2);
> + /*
> + * The SPI register address (= full register address without bit 7)
> + * and the write command (bit7 = RW = '0')
> + */
> + buf[0] &= ~0x80;
> +
> + return spi_write_then_read(spi, buf, 2, NULL, 0);
> +}
> +
> +static int bme680_regmap_spi_read(void *context, const void *reg,
> + size_t reg_size, void *val, size_t val_size)
> +{
> + struct spi_device *spi = context;
> +
> + return spi_write_then_read(spi, reg, reg_size, val, val_size);
> +}
> +
> +static struct regmap_bus bme680_regmap_bus = {
> + .write = bme680_regmap_spi_write,
> + .read = bme680_regmap_spi_read,
> + .reg_format_endian_default = REGMAP_ENDIAN_BIG,
> + .val_format_endian_default = REGMAP_ENDIAN_BIG,
> +};
> +
> +static int bme680_spi_probe(struct spi_device *spi)
> +{
> + const struct spi_device_id *id = spi_get_device_id(spi);
> + struct regmap *regmap;
> + unsigned int val;
> + int ret;
> +
> + spi->bits_per_word = 8;
> + ret = spi_setup(spi);
> + if (ret < 0) {
> + dev_err(&spi->dev, "spi_setup failed!\n");
> + return ret;
> + }
> +
> + regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
> + &spi->dev, &bme680_regmap_config);
> + if (IS_ERR(regmap)) {
> + dev_err(&spi->dev, "Failed to register spi regmap %d\n",
> + (int)PTR_ERR(regmap));
> + return PTR_ERR(regmap);
> + }
> +
> + ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
> + BME680_CMD_SOFTRESET);
> + if (ret < 0)
> + return ret;
> +
> + /* after power-on reset, Page 0(0x80-0xFF) of spi_mem_page is active */
> + ret = regmap_read(regmap, BME680_REG_CHIP_SPI_ID, &val);
> + if (ret < 0) {
> + dev_err(&spi->dev, "Error reading SPI chip ID\n");
> + return ret;
> + }
> +
> + if (val != BME680_CHIP_ID_VAL) {
> + dev_err(&spi->dev, "Wrong chip ID, got %x expected %x\n",
> + val, BME680_CHIP_ID_VAL);
> + return -ENODEV;
> + }
> + /*
> + * select Page 1 of spi_mem_page to enable access to
> + * to registers from address 0x00 to 0x7F.
> + */
> + ret = regmap_write_bits(regmap, BME680_REG_STATUS,
> + BME680_SPI_MEM_PAGE_BIT,
> + BME680_SPI_MEM_PAGE_1_VAL);
> + if (ret < 0) {
> + dev_err(&spi->dev, "failed to set page 1 of spi_mem_page\n");
> + return ret;
> + }
> +
> + return bme680_core_probe(&spi->dev, regmap, id->name);
> +}
> +
> +static const struct spi_device_id bme680_spi_id[] = {
> + {"bme680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(spi, bme680_spi_id);
> +
> +static const struct acpi_device_id bme680_acpi_match[] = {
> + {"BME0680", 0},
> + {},
> +};
> +MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
> +
> +static struct spi_driver bme680_spi_driver = {
> + .driver = {
> + .name = "bme680_spi",
> + .acpi_match_table = ACPI_PTR(bme680_acpi_match),
> + },
> + .probe = bme680_spi_probe,
> + .id_table = bme680_spi_id,
> +};
> +module_spi_driver(bme680_spi_driver);
> +
> +MODULE_AUTHOR("Himanshu Jha <[email protected]>");
> +MODULE_DESCRIPTION("Bosch BME680 SPI driver");
> +MODULE_LICENSE("GPL v2");
>


2018-07-14 07:34:41

by Himanshu Jha

[permalink] [raw]
Subject: Re: [PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

Hi David,

On Fri, Jul 13, 2018 at 01:42:35PM -0700, David Frey wrote:
> Hi Himanshu Jha,
>
> First a bit of background. I'm working on a device which will contain a
> bme680 sensor. A colleague of mine started work on a Linux kernel driver
> for the chip a little while ago. The (WIP) driver can be found here:
> https://github.com/mangOH/mangOH/tree/master/linux_kernel_modules/bme680

Great!

> This driver is written targeting an older kernel (3.18.x) because that's the
> kernel we're stuck on for now. Rather than writing the driver from scratch,
> what we did was write the Linux kernel driver as a wrapper around the Bosch
> code. My theory at the time was that Bosch made the chip, so they probably
> know what they're doing when it comes to writing a driver library. After
> having looked at the code in more detail, I'm less confident that our
> approach was the best one. I'm not attempting to upstream the driver built
> by my colleague and I'm not trying to request review of this code either. I
> simply want to make you aware of it so that you can look at it to get some
> ideas.

Thanks for taking your time to review.

> I have included a number of comments on your driver below. Keep up the good
> work!
>
> >+++ b/drivers/iio/chemical/bme680.h
> >@@ -0,0 +1,99 @@
> >+/* SPDX-License-Identifier: GPL-2.0 */
> >+#ifndef BME680_H_
> >+#define BME680_H_
> >+
> >+#define BME680_REG_CHIP_I2C_ID 0xD0
> >+#define BME680_REG_CHIP_SPI_ID 0x50
> >+#define BME680_CHIP_ID_VAL 0x61
> Try to be consistent with the indenting of the defines. I think this would
> be clearest:
> #define BME680_REG_X 0x00
> #define BME680_X_FOO_EN_MASK BIT(0)
> #define BME680_X_BAR_MASK GENMASK(3, 1)
> #define BME680_BAR_VAL1 3
> #define BME680_BAR_VAL2 7
>
> This way the register, field definition and field values are all visually
> distinctive.

I have used this pattern everywhere where applicable. But not applied
for *_VAL, would definitely follow this up.

> >+#define BME680_REG_SOFT_RESET 0xE0
> The datasheet says that the soft reset register differs for I2C and SPI.
> For I2C it is 0xE0 and for SPI it is 0x60 when page 0 is selected.

That's really a stupid mistake :(
I have exported these individual initialization code in the I2C & SPI
drivers respectively but it slipped my mind somehow. This device has
peculiar characteristics in register addressing.

I will correct this in next version.

> >+#define BME680_CMD_SOFTRESET 0xB6
> >+#define BME680_REG_STATUS 0x73
> >+#define BME680_SPI_MEM_PAGE_BIT BIT(4)
> >+#define BME680_SPI_MEM_PAGE_1_VAL 1
> >+
> >+#define BME680_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
> >+#define BME680_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2)
> >+#define BME680_OSRS_HUMID_X(osrs_h) ((osrs_h) << 0)
> You could use the FIELD_PREP macro from <linux/bitfield.h> to eliminate the
> need for these macros. For example:
> ctrl_meas_reg = FIELD_PREP(BME680_OSRS_TEMP_MASK, temp_val) |
> FIELD_PREP(BME680_OSRS_PRESS_MASK, press_val) |
> FIELD_PREP(BME880_MODE_MASK, mode_val);

Ah, yes! I didn't knew about these magic macros. It will remove some
log2() computation hacks from my code.

> >+
> >+#define BME680_REG_TEMP_MSB 0x22
> >+#define BME680_REG_PRESS_MSB 0x1F
> >+#define BM6880_REG_HUMIDITY_MSB 0x25
> >+#define BME680_REG_GAS_MSB 0x2A
> >+#define BME680_REG_GAS_R_LSB 0x2B
> >+#define BME680_GAS_STAB_BIT BIT(4)
> >+
> >+#define BME680_REG_CTRL_HUMIDITY 0x72
> >+#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
> >+
> >+#define BME680_REG_CTRL_MEAS 0x74
> >+#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
> >+#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
> >+#define BME680_MODE_MASK GENMASK(1, 0)
> >+
> >+#define BME680_MODE_FORCED BIT(0)
> >+#define BME680_MODE_SLEEP 0
> This should be:
> #define BME680_MODE_SLEEP 0
> #define BME680_MODE_FORCED 1

Yes, this is much clearer and removes ambiguity.

> >+/* Taken from Bosch BME680 API */
>
> I think there should be a link to the Bosch code
> (https://github.com/BoschSensortec/BME680_driver/) somewhere within the
> comments of this file. Maybe it belongs at the top of this file?

I planned to add:
https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L876
to here and likewise to other compensate functions.
But these links may change(if somehow they plan to migrate to Gitlab),
long lines are not welcomed.

You could also notice that I haven't included datasheet link at the top
of this file. Well, most of the companies change the links when releasing
the new Revision(Rev. A,B,...) so it is likely that the link would be
dead/old lying at the top of source.

Therefore, I added both links to Bosch API and datasheet only in the
commmit log(also suggested by my mentor).

> >+static s32 bme680_compensate_temp(struct bme680_data *data,
> >+ s32 adc_temp)
> >+{
> >+ struct bme680_calib *calib = &data->bme680;
> >+ s64 var1, var2, var3, calc_temp;
> >+
> >+ var1 = ((s32) adc_temp >> 3) - ((s32) calib->par_t1 << 1);
> >+ var2 = (var1 * (s32) calib->par_t2) >> 11;
> >+ var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
> >+ var3 = ((var3) * ((s32) calib->par_t3 << 4)) >> 14;
> >+ data->t_fine = (s32) (var2 + var3);
> >+ calc_temp = (s16) (((data->t_fine * 5) + 128) >> 8);
> >+
> >+ return calc_temp;
> I think the meaning of the s32 returned should be documented. Based on code
> elsewhere in the driver I'm guessing it's degrees celcius * 100. The same
> comment applies to the other compensate functions.

Yes, this function returns values like 3253(32.53degC), which I divide
in the read_temp() at last few lines:

*val = bme680_compensate_temp();
*val2 = 100;
return IIO_VAL_FRACTIONAL;

You can only document when you have "official" references, I mean these
*should* have been documented in datasheet. And then we could say:

"Temperature has 100 degC .... Pg 21"

But these resolution information is what I got from Bosch when I
contacted them and also evident from README.md

https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/README.md#example-for-reading-all-sensor-data

"printf("T: %.2f degC, P: %.2f hPa, H %.2f %%rH ",
data.temperature / 100.0f,
data.pressure / 100.0f,
data.humidity / 1000.0f );"

And I am pretty sure they will add these information in the coming
revision of datasheet.

I have looked at the various family of sensors from Bosch and they even
mention the optimal oversampling ratio, filter coefficient, etc., for
say "using in weather stations"(Take a look at bme280 datasheet)

[ market policy is what I heard somewhere ;) ]

> >+static int bme680_chip_config(struct bme680_data *data)
> >+{
> >+ struct device *dev = regmap_get_device(data->regmap);
> >+ int ret;
> >+ u8 osrs = BME680_OSRS_HUMID_X(data->oversampling_humid + 1);
> >+ /*
> >+ * Highly recommended to set oversampling of humidity before
> >+ * temperature/pressure oversampling.
> >+ */
> I think you are referring to this snippet from the datasheet: "It is highly
> recommended to set first osrs_h<2:0> followed by osrs_t<2:0> and osrs_p<2:0>
> in one write command (see Section 3.3)." My interpretation of this is that
> they are saying that you should do one bulk write rather than writing the
> fields individually. Maybe they're just recommending this for efficiency
> reasons. I'm not really sure though. I read through section 3.3 and I
> couldn't find any justification to backup this rather prominent suggestion.

Yes, I referred to the same segment of article from datasheet.
But how do you do a bulk_write() for *two different* registers ?

We have humidity bit(osrs_h<2:0) at Ctrl_hum(0x72) register and
temperature(osrs_t), pressure bit(osrs_p) at Ctrl_meas(0x74) register.

So, I think it was setting humidity first perhaps and then doing the
rest.

Even Bosch is doing the reverse of what they say in the datasheet:
https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L464

They are setting Temperature & Pressure _before_ humidity oversampling
setting. Weird!?

> >+static int bme680_read_temp(struct bme680_data *data,
> >+ int *val, int *val2)
> >+{
> >+ struct device *dev = regmap_get_device(data->regmap);
> >+ int ret = 0;
> >+ __be32 tmp = 0;
> >+ s32 adc_temp, 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,
> >+ (u8 *) &tmp, 3);
> >+ if (ret < 0) {
> >+ dev_err(dev, "failed to read temperature\n");
> >+ return ret;
> >+ }
> I think the value you're reading from the register may actually be from a
> previous request. I think you need to poll meas_status_0 (0x1D) field
> new_data_0 (bit 7) to wait for new data after setting the mode to forced.
> You can see that's what Bosch's code does:
> https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L1227

Yes, we can do that but, for a while, I am following my GSoC proposal
timeline. And I have planned these check_bits function later, but since
this check_bits function was imperative in gas sensing, therefore I
included them now.

My plan in incremental changes and this patch is kindof minimal. I have
tested the sensor several times and found no errors in readings for
T P H G readings so far.

The problem arises that this sensor is made to work in a T->P->H->G
fashion and every channel is mostly dependent on the other. And IIO
driver design pattern isn't the best choice it. You can't just take a
single channel readings by running Bosch Code because it is not designed
like that.

For instance: we need t_fine values for pressure/humidity compensation
functions which we only get when reading temperature. So, you need to
run the temperaure cycle if you need pressure/humidity values. And this
is what I did by doing a dummy read_temp(data, NULL, NULL) to get the t_fine
value.

I had also planned to add heater_profile_duration:
https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L647

But what if I only read gas channel ?
Then this function needs to be tweaked accordingly.

But without these functions my code works fine, but I will surely add
them later.

There are also function that can be added for eg. to add a delay during
startup:

Start-up time -- tstartup -- Time to first communication -- 2ms

This delay should be added after power-on reset.

I am currently considering only the core functionality for now, later
incrementally add these function.

I am currenlty supplying default values for heater temperature & duration
at probe and planned to add syfs attributes for user to specify these
values. But somehow it got complicated and my mentor advised to just to
the needful now and later we can add various support interfaces. We can
also add interface for turning on/off optional IIR filter.

The another problem also arise is that each you want a reading, you need
to set the FORCED mode. But Bosch Code doesn't support individual
channel reading as we do in IIO, they don;t need to care about this
information.

But on other side in IIO, if you want pressure reading you do:

# cat in_pressure_humidityrelative
..
and then behind the scenes FORCED mode is set and you get the reading.

But then again, if you want temp & pressure both:

# cat in_temp_input in_pressure_humidityrelative
..

and here we set FORCED mode *twice* for every measurement.
And then datasheet say:

At Page 14/50
------------

3. Sensor usage
3.1 Sensor modes

"It is important to note that, further mode change commands or other
write commands to the control registers are ignored until
the mode change command has been executed. All control registers should
be set to the desired values before writing to the
mode register."

So, we need to take care of these stuff too!

And then there are a lot of things to discuss....

Thanks you so much for the feedback, David! :)

And if at some point I have said something stupid, then please forgive me.

I am a 3rd year undergrad student and started with IIO few months back, and
not a Bosch driver developer ;)

--
Himanshu Jha
Undergraduate Student
Department of Electronics & Communication
Guru Tegh Bahadur Institute of Technology

2018-07-15 09:11:28

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

On Sat, 14 Jul 2018 13:03:42 +0530
Himanshu Jha <[email protected]> wrote:

> Hi David,
>
> On Fri, Jul 13, 2018 at 01:42:35PM -0700, David Frey wrote:
> > Hi Himanshu Jha,
> >
> > First a bit of background. I'm working on a device which will contain a
> > bme680 sensor. A colleague of mine started work on a Linux kernel driver
> > for the chip a little while ago. The (WIP) driver can be found here:
> > https://github.com/mangOH/mangOH/tree/master/linux_kernel_modules/bme680
>
> Great!
>
> > This driver is written targeting an older kernel (3.18.x) because that's the
> > kernel we're stuck on for now. Rather than writing the driver from scratch,
> > what we did was write the Linux kernel driver as a wrapper around the Bosch
> > code. My theory at the time was that Bosch made the chip, so they probably
> > know what they're doing when it comes to writing a driver library. After
> > having looked at the code in more detail, I'm less confident that our
> > approach was the best one. I'm not attempting to upstream the driver built
> > by my colleague and I'm not trying to request review of this code either. I
> > simply want to make you aware of it so that you can look at it to get some
> > ideas.
>
> Thanks for taking your time to review.
>
> > I have included a number of comments on your driver below. Keep up the good
> > work!
> >
> > >+++ b/drivers/iio/chemical/bme680.h
> > >@@ -0,0 +1,99 @@
> > >+/* SPDX-License-Identifier: GPL-2.0 */
> > >+#ifndef BME680_H_
> > >+#define BME680_H_
> > >+
> > >+#define BME680_REG_CHIP_I2C_ID 0xD0
> > >+#define BME680_REG_CHIP_SPI_ID 0x50
> > >+#define BME680_CHIP_ID_VAL 0x61
> > Try to be consistent with the indenting of the defines. I think this would
> > be clearest:
> > #define BME680_REG_X 0x00
> > #define BME680_X_FOO_EN_MASK BIT(0)
> > #define BME680_X_BAR_MASK GENMASK(3, 1)
> > #define BME680_BAR_VAL1 3
> > #define BME680_BAR_VAL2 7
> >
> > This way the register, field definition and field values are all visually
> > distinctive.
>
> I have used this pattern everywhere where applicable. But not applied
> for *_VAL, would definitely follow this up.
>
> > >+#define BME680_REG_SOFT_RESET 0xE0
> > The datasheet says that the soft reset register differs for I2C and SPI.
> > For I2C it is 0xE0 and for SPI it is 0x60 when page 0 is selected.
>
> That's really a stupid mistake :(
> I have exported these individual initialization code in the I2C & SPI
> drivers respectively but it slipped my mind somehow. This device has
> peculiar characteristics in register addressing.
>
> I will correct this in next version.
>
> > >+#define BME680_CMD_SOFTRESET 0xB6
> > >+#define BME680_REG_STATUS 0x73
> > >+#define BME680_SPI_MEM_PAGE_BIT BIT(4)
> > >+#define BME680_SPI_MEM_PAGE_1_VAL 1
> > >+
> > >+#define BME680_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
> > >+#define BME680_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2)
> > >+#define BME680_OSRS_HUMID_X(osrs_h) ((osrs_h) << 0)
> > You could use the FIELD_PREP macro from <linux/bitfield.h> to eliminate the
> > need for these macros. For example:
> > ctrl_meas_reg = FIELD_PREP(BME680_OSRS_TEMP_MASK, temp_val) |
> > FIELD_PREP(BME680_OSRS_PRESS_MASK, press_val) |
> > FIELD_PREP(BME880_MODE_MASK, mode_val);
>
> Ah, yes! I didn't knew about these magic macros. It will remove some
> log2() computation hacks from my code.
>
> > >+
> > >+#define BME680_REG_TEMP_MSB 0x22
> > >+#define BME680_REG_PRESS_MSB 0x1F
> > >+#define BM6880_REG_HUMIDITY_MSB 0x25
> > >+#define BME680_REG_GAS_MSB 0x2A
> > >+#define BME680_REG_GAS_R_LSB 0x2B
> > >+#define BME680_GAS_STAB_BIT BIT(4)
> > >+
> > >+#define BME680_REG_CTRL_HUMIDITY 0x72
> > >+#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
> > >+
> > >+#define BME680_REG_CTRL_MEAS 0x74
> > >+#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
> > >+#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
> > >+#define BME680_MODE_MASK GENMASK(1, 0)
> > >+
> > >+#define BME680_MODE_FORCED BIT(0)
> > >+#define BME680_MODE_SLEEP 0
> > This should be:
> > #define BME680_MODE_SLEEP 0
> > #define BME680_MODE_FORCED 1
>
> Yes, this is much clearer and removes ambiguity.
>
> > >+/* Taken from Bosch BME680 API */
> >
> > I think there should be a link to the Bosch code
> > (https://github.com/BoschSensortec/BME680_driver/) somewhere within the
> > comments of this file. Maybe it belongs at the top of this file?
>
> I planned to add:
> https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L876
> to here and likewise to other compensate functions.
> But these links may change(if somehow they plan to migrate to Gitlab),
> long lines are not welcomed.

Looks like github does the same trick kernel.org does in allowing shortened hashes.

I think

https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L876
Is the same thing and under 80 chars (just) :)


>
> You could also notice that I haven't included datasheet link at the top
> of this file. Well, most of the companies change the links when releasing
> the new Revision(Rev. A,B,...) so it is likely that the link would be
> dead/old lying at the top of source.

Even though this happens, it's better to have something than nothing
perhaps.

>
> Therefore, I added both links to Bosch API and datasheet only in the
> commmit log(also suggested by my mentor).
>
> > >+static s32 bme680_compensate_temp(struct bme680_data *data,
> > >+ s32 adc_temp)
> > >+{
> > >+ struct bme680_calib *calib = &data->bme680;
> > >+ s64 var1, var2, var3, calc_temp;
> > >+
> > >+ var1 = ((s32) adc_temp >> 3) - ((s32) calib->par_t1 << 1);
> > >+ var2 = (var1 * (s32) calib->par_t2) >> 11;
> > >+ var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
> > >+ var3 = ((var3) * ((s32) calib->par_t3 << 4)) >> 14;
> > >+ data->t_fine = (s32) (var2 + var3);
> > >+ calc_temp = (s16) (((data->t_fine * 5) + 128) >> 8);
> > >+
> > >+ return calc_temp;
> > I think the meaning of the s32 returned should be documented. Based on code
> > elsewhere in the driver I'm guessing it's degrees celcius * 100. The same
> > comment applies to the other compensate functions.
>
> Yes, this function returns values like 3253(32.53degC), which I divide
> in the read_temp() at last few lines:
>
> *val = bme680_compensate_temp();
> *val2 = 100;
> return IIO_VAL_FRACTIONAL;
>
> You can only document when you have "official" references, I mean these
> *should* have been documented in datasheet. And then we could say:
>
> "Temperature has 100 degC .... Pg 21"
>
> But these resolution information is what I got from Bosch when I
> contacted them and also evident from README.md
>
> https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/README.md#example-for-reading-all-sensor-data
>
> "printf("T: %.2f degC, P: %.2f hPa, H %.2f %%rH ",
> data.temperature / 100.0f,
> data.pressure / 100.0f,
> data.humidity / 1000.0f );"
>
> And I am pretty sure they will add these information in the coming
> revision of datasheet.
>
> I have looked at the various family of sensors from Bosch and they even
> mention the optimal oversampling ratio, filter coefficient, etc., for
> say "using in weather stations"(Take a look at bme280 datasheet)
>
> [ market policy is what I heard somewhere ;) ]
>
> > >+static int bme680_chip_config(struct bme680_data *data)
> > >+{
> > >+ struct device *dev = regmap_get_device(data->regmap);
> > >+ int ret;
> > >+ u8 osrs = BME680_OSRS_HUMID_X(data->oversampling_humid + 1);
> > >+ /*
> > >+ * Highly recommended to set oversampling of humidity before
> > >+ * temperature/pressure oversampling.
> > >+ */
> > I think you are referring to this snippet from the datasheet: "It is highly
> > recommended to set first osrs_h<2:0> followed by osrs_t<2:0> and osrs_p<2:0>
> > in one write command (see Section 3.3)." My interpretation of this is that
> > they are saying that you should do one bulk write rather than writing the
> > fields individually. Maybe they're just recommending this for efficiency
> > reasons. I'm not really sure though. I read through section 3.3 and I
> > couldn't find any justification to backup this rather prominent suggestion.
>
> Yes, I referred to the same segment of article from datasheet.
> But how do you do a bulk_write() for *two different* registers ?
>
> We have humidity bit(osrs_h<2:0) at Ctrl_hum(0x72) register and
> temperature(osrs_t), pressure bit(osrs_p) at Ctrl_meas(0x74) register.
>
> So, I think it was setting humidity first perhaps and then doing the
> rest.
>
> Even Bosch is doing the reverse of what they say in the datasheet:
> https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L464
>
> They are setting Temperature & Pressure _before_ humidity oversampling
> setting. Weird!?
>
> > >+static int bme680_read_temp(struct bme680_data *data,
> > >+ int *val, int *val2)
> > >+{
> > >+ struct device *dev = regmap_get_device(data->regmap);
> > >+ int ret = 0;
> > >+ __be32 tmp = 0;
> > >+ s32 adc_temp, 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,
> > >+ (u8 *) &tmp, 3);
> > >+ if (ret < 0) {
> > >+ dev_err(dev, "failed to read temperature\n");
> > >+ return ret;
> > >+ }
> > I think the value you're reading from the register may actually be from a
> > previous request. I think you need to poll meas_status_0 (0x1D) field
> > new_data_0 (bit 7) to wait for new data after setting the mode to forced.
> > You can see that's what Bosch's code does:
> > https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L1227
>
> Yes, we can do that but, for a while, I am following my GSoC proposal
> timeline. And I have planned these check_bits function later, but since
> this check_bits function was imperative in gas sensing, therefore I
> included them now.
>
> My plan in incremental changes and this patch is kindof minimal. I have
> tested the sensor several times and found no errors in readings for
> T P H G readings so far.
>
> The problem arises that this sensor is made to work in a T->P->H->G
> fashion and every channel is mostly dependent on the other. And IIO
> driver design pattern isn't the best choice it. You can't just take a
> single channel readings by running Bosch Code because it is not designed
> like that.
>
> For instance: we need t_fine values for pressure/humidity compensation
> functions which we only get when reading temperature. So, you need to
> run the temperaure cycle if you need pressure/humidity values. And this
> is what I did by doing a dummy read_temp(data, NULL, NULL) to get the t_fine
> value.

This pattern isn't that unusual in devices. Normally you do it pretty
much the way you have. The sysfs interface in IIO is just interested
in data presentation, we don't care what you have to do to get it ;)

This will fit much better when doing buffered interfaces anyway which
naturally grab sets of channels.

>
> I had also planned to add heater_profile_duration:
> https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L647
>
> But what if I only read gas channel ?
> Then this function needs to be tweaked accordingly.
>
> But without these functions my code works fine, but I will surely add
> them later.
>
> There are also function that can be added for eg. to add a delay during
> startup:
>
> Start-up time -- tstartup -- Time to first communication -- 2ms
>
> This delay should be added after power-on reset.
>
> I am currently considering only the core functionality for now, later
> incrementally add these function.
>
> I am currenlty supplying default values for heater temperature & duration
> at probe and planned to add syfs attributes for user to specify these
> values. But somehow it got complicated and my mentor advised to just to
> the needful now and later we can add various support interfaces. We can
> also add interface for turning on/off optional IIR filter.
>
> The another problem also arise is that each you want a reading, you need
> to set the FORCED mode. But Bosch Code doesn't support individual
> channel reading as we do in IIO, they don;t need to care about this
> information.
>
> But on other side in IIO, if you want pressure reading you do:
>
> # cat in_pressure_humidityrelative
> ..
> and then behind the scenes FORCED mode is set and you get the reading.
>
> But then again, if you want temp & pressure both:
>
> # cat in_temp_input in_pressure_humidityrelative
> ..
>
> and here we set FORCED mode *twice* for every measurement.
> And then datasheet say:
>
> At Page 14/50
> ------------
>
> 3. Sensor usage
> 3.1 Sensor modes
>
> "It is important to note that, further mode change commands or other
> write commands to the control registers are ignored until
> the mode change command has been executed. All control registers should
> be set to the desired values before writing to the
> mode register."
>
> So, we need to take care of these stuff too!
>
> And then there are a lot of things to discuss....
>
> Thanks you so much for the feedback, David! :)
>
> And if at some point I have said something stupid, then please forgive me.
>
> I am a 3rd year undergrad student and started with IIO few months back, and
> not a Bosch driver developer ;)
>
Going well so far ;)

Jonathan

2018-07-15 09:45:32

by Himanshu Jha

[permalink] [raw]
Subject: Re: [PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

Hi Jonathan,

On Sun, Jul 15, 2018 at 10:10:36AM +0100, Jonathan Cameron wrote:
> On Sat, 14 Jul 2018 13:03:42 +0530
> Himanshu Jha <[email protected]> wrote:
>
> > Hi David,
> >
> > On Fri, Jul 13, 2018 at 01:42:35PM -0700, David Frey wrote:
> > > Hi Himanshu Jha,
> > >
> > > First a bit of background. I'm working on a device which will contain a
> > > bme680 sensor. A colleague of mine started work on a Linux kernel driver
> > > for the chip a little while ago. The (WIP) driver can be found here:
> > > https://github.com/mangOH/mangOH/tree/master/linux_kernel_modules/bme680
> >
> > Great!
> >
> > > This driver is written targeting an older kernel (3.18.x) because that's the
> > > kernel we're stuck on for now. Rather than writing the driver from scratch,
> > > what we did was write the Linux kernel driver as a wrapper around the Bosch
> > > code. My theory at the time was that Bosch made the chip, so they probably
> > > know what they're doing when it comes to writing a driver library. After
> > > having looked at the code in more detail, I'm less confident that our
> > > approach was the best one. I'm not attempting to upstream the driver built
> > > by my colleague and I'm not trying to request review of this code either. I
> > > simply want to make you aware of it so that you can look at it to get some
> > > ideas.
> >
> > Thanks for taking your time to review.
> >
> > > I have included a number of comments on your driver below. Keep up the good
> > > work!
> > >
> > > >+++ b/drivers/iio/chemical/bme680.h
> > > >@@ -0,0 +1,99 @@
> > > >+/* SPDX-License-Identifier: GPL-2.0 */
> > > >+#ifndef BME680_H_
> > > >+#define BME680_H_
> > > >+
> > > >+#define BME680_REG_CHIP_I2C_ID 0xD0
> > > >+#define BME680_REG_CHIP_SPI_ID 0x50
> > > >+#define BME680_CHIP_ID_VAL 0x61
> > > Try to be consistent with the indenting of the defines. I think this would
> > > be clearest:
> > > #define BME680_REG_X 0x00
> > > #define BME680_X_FOO_EN_MASK BIT(0)
> > > #define BME680_X_BAR_MASK GENMASK(3, 1)
> > > #define BME680_BAR_VAL1 3
> > > #define BME680_BAR_VAL2 7
> > >
> > > This way the register, field definition and field values are all visually
> > > distinctive.
> >
> > I have used this pattern everywhere where applicable. But not applied
> > for *_VAL, would definitely follow this up.
> >
> > > >+#define BME680_REG_SOFT_RESET 0xE0
> > > The datasheet says that the soft reset register differs for I2C and SPI.
> > > For I2C it is 0xE0 and for SPI it is 0x60 when page 0 is selected.
> >
> > That's really a stupid mistake :(
> > I have exported these individual initialization code in the I2C & SPI
> > drivers respectively but it slipped my mind somehow. This device has
> > peculiar characteristics in register addressing.
> >
> > I will correct this in next version.
> >
> > > >+#define BME680_CMD_SOFTRESET 0xB6
> > > >+#define BME680_REG_STATUS 0x73
> > > >+#define BME680_SPI_MEM_PAGE_BIT BIT(4)
> > > >+#define BME680_SPI_MEM_PAGE_1_VAL 1
> > > >+
> > > >+#define BME680_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
> > > >+#define BME680_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2)
> > > >+#define BME680_OSRS_HUMID_X(osrs_h) ((osrs_h) << 0)
> > > You could use the FIELD_PREP macro from <linux/bitfield.h> to eliminate the
> > > need for these macros. For example:
> > > ctrl_meas_reg = FIELD_PREP(BME680_OSRS_TEMP_MASK, temp_val) |
> > > FIELD_PREP(BME680_OSRS_PRESS_MASK, press_val) |
> > > FIELD_PREP(BME880_MODE_MASK, mode_val);
> >
> > Ah, yes! I didn't knew about these magic macros. It will remove some
> > log2() computation hacks from my code.
> >
> > > >+
> > > >+#define BME680_REG_TEMP_MSB 0x22
> > > >+#define BME680_REG_PRESS_MSB 0x1F
> > > >+#define BM6880_REG_HUMIDITY_MSB 0x25
> > > >+#define BME680_REG_GAS_MSB 0x2A
> > > >+#define BME680_REG_GAS_R_LSB 0x2B
> > > >+#define BME680_GAS_STAB_BIT BIT(4)
> > > >+
> > > >+#define BME680_REG_CTRL_HUMIDITY 0x72
> > > >+#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
> > > >+
> > > >+#define BME680_REG_CTRL_MEAS 0x74
> > > >+#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
> > > >+#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
> > > >+#define BME680_MODE_MASK GENMASK(1, 0)
> > > >+
> > > >+#define BME680_MODE_FORCED BIT(0)
> > > >+#define BME680_MODE_SLEEP 0
> > > This should be:
> > > #define BME680_MODE_SLEEP 0
> > > #define BME680_MODE_FORCED 1
> >
> > Yes, this is much clearer and removes ambiguity.
> >
> > > >+/* Taken from Bosch BME680 API */
> > >
> > > I think there should be a link to the Bosch code
> > > (https://github.com/BoschSensortec/BME680_driver/) somewhere within the
> > > comments of this file. Maybe it belongs at the top of this file?
> >
> > I planned to add:
> > https://github.com/BoschSensortec/BME680_driver/blob/63bb5336db4659519860832be2738c685133aa33/bme680.c#L876
> > to here and likewise to other compensate functions.
> > But these links may change(if somehow they plan to migrate to Gitlab),
> > long lines are not welcomed.
>
> Looks like github does the same trick kernel.org does in allowing shortened hashes.

$ git log --abbrev-commit ...

> I think
>
> https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L876
> Is the same thing and under 80 chars (just) :)

Ah, yes :)
Perfect!

> > You could also notice that I haven't included datasheet link at the top
> > of this file. Well, most of the companies change the links when releasing
> > the new Revision(Rev. A,B,...) so it is likely that the link would be
> > dead/old lying at the top of source.
>
> Even though this happens, it's better to have something than nothing
> perhaps.

Sure! Will add both links in next version.

> > Yes, we can do that but, for a while, I am following my GSoC proposal
> > timeline. And I have planned these check_bits function later, but since
> > this check_bits function was imperative in gas sensing, therefore I
> > included them now.
> >
> > My plan in incremental changes and this patch is kindof minimal. I have
> > tested the sensor several times and found no errors in readings for
> > T P H G readings so far.
> >
> > The problem arises that this sensor is made to work in a T->P->H->G
> > fashion and every channel is mostly dependent on the other. And IIO
> > driver design pattern isn't the best choice it. You can't just take a
> > single channel readings by running Bosch Code because it is not designed
> > like that.
> >
> > For instance: we need t_fine values for pressure/humidity compensation
> > functions which we only get when reading temperature. So, you need to
> > run the temperaure cycle if you need pressure/humidity values. And this
> > is what I did by doing a dummy read_temp(data, NULL, NULL) to get the t_fine
> > value.
>
> This pattern isn't that unusual in devices. Normally you do it pretty
> much the way you have. The sysfs interface in IIO is just interested
> in data presentation, we don't care what you have to do to get it ;)

OK. I agree, but will create a new thread for some
clarification/rectification of IIO core.

> This will fit much better when doing buffered interfaces anyway which
> naturally grab sets of channels.

Yes, I think so it work better in that way.
Also, for industrial use I think buffered interface is a better option.

> > Thanks you so much for the feedback, David! :)
> >
> > And if at some point I have said something stupid, then please forgive me.
> >
> > I am a 3rd year undergrad student and started with IIO few months back, and
> > not a Bosch driver developer ;)
> >
> Going well so far ;)

Thanks Jonathan again :)

Well it is better to hint developers that you're a student, else I had a
discussion in the past where some senior developer was explaining me
about CPU profiling (I don't remember exactly) and it didn't even hit me
for the next two weeks ;)

But I would like David to review the patches in the next version too.

--
Himanshu Jha
Undergraduate Student
Department of Electronics & Communication
Guru Tegh Bahadur Institute of Technology