Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b3bd859 authored by Himanshu Jha's avatar Himanshu Jha Committed by Jonathan Cameron
Browse files

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



Note from Jonathan: The compensation functions are 'interesting' and
could do with a tidy up in future.  However, they work so we can leave that
for another day.

Cc: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: default avatarHimanshu Jha <himanshujha199640@gmail.com>
Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 64319757
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -21,6 +21,29 @@ 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 I2C && BME680
	select REGMAP_I2C

config BME680_SPI
	tristate
	depends on SPI && BME680
	select REGMAP_SPI

config CCS811
	tristate "AMS CCS811 VOC sensor"
	depends on I2C
+3 −0
Original line number Diff line number Diff line
@@ -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
+96 −0
Original line number Diff line number Diff line
/* 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_I2C		0xE0
#define BME680_REG_SOFT_RESET_SPI		0x60
#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_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			1
#define BME680_MODE_SLEEP			0

#define BME680_REG_CONFIG			0x75
#define   BME680_FILTER_MASK			GENMASK(4, 2)
#define     BME680_FILTER_COEFF_VAL		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		BIT(4)
#define     BME680_NB_CONV_0_VAL		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_ */
+959 −0

File added.

Preview size limit exceeded, changes collapsed.

+85 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * BME680 - I2C Driver
 *
 * Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
 *
 * 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_I2C,
			   BME680_CMD_SOFTRESET);
	if (ret < 0) {
		dev_err(&client->dev, "Failed to reset chip\n");
		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 <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("BME680 I2C driver");
MODULE_LICENSE("GPL v2");
Loading