BigW Consortium Gitlab

Commit 69c659ed by Ashish Syal

Replace bme680 driver with mainline kernel v4.19.1

parent 3933b454
......@@ -44,9 +44,6 @@
[submodule "apps/LocationTriangulation"]
path = apps/LocationTriangulation
url = https://github.com/mangOH/LocationTriangulation
[submodule "linux_kernel_modules/bme680/BME680_driver"]
path = linux_kernel_modules/bme680/BME680_driver
url = https://github.com/BoschSensortec/BME680_driver.git
[submodule "linux_kernel_modules/bmi088/BMI08x-Sensor-API"]
path = linux_kernel_modules/bmi088/BMI08x-Sensor-API
url = https://github.com/BoschSensortec/BMI08x-Sensor-API.git
......
Subproject commit b619094dfe507034e1034884f4638a78dae7646c
/*
* Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _LINUX_BITFIELD_H
#define _LINUX_BITFIELD_H
#include <asm/byteorder.h>
/*
* Bitfield access macros
*
* FIELD_{GET,PREP} macros take as first parameter shifted mask
* from which they extract the base mask and shift amount.
* Mask must be a compilation time constant.
*
* Example:
*
* #define REG_FIELD_A GENMASK(6, 0)
* #define REG_FIELD_B BIT(7)
* #define REG_FIELD_C GENMASK(15, 8)
* #define REG_FIELD_D GENMASK(31, 16)
*
* Get:
* a = FIELD_GET(REG_FIELD_A, reg);
* b = FIELD_GET(REG_FIELD_B, reg);
*
* Set:
* reg = FIELD_PREP(REG_FIELD_A, 1) |
* FIELD_PREP(REG_FIELD_B, 0) |
* FIELD_PREP(REG_FIELD_C, c) |
* FIELD_PREP(REG_FIELD_D, 0x40);
*
* Modify:
* reg &= ~REG_FIELD_C;
* reg |= FIELD_PREP(REG_FIELD_C, c);
*/
#define __bf_shf(x) (__builtin_ffsll(x) - 1)
/**
* FIELD_PREP() - prepare a bitfield element
* @_mask: shifted mask defining the field's length and position
* @_val: value to put in the field
*
* FIELD_PREP() masks and shifts up the value. The result should
* be combined with other fields of the bitfield using logical OR.
*/
#define FIELD_PREP(_mask, _val) \
({ \
((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
})
#endif
#include <linux/device.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/version.h>
#include <linux/delay.h>
#include "bme680.h"
#include "bme680_defs.h"
extern const struct regmap_config bme680_regmap_config;
struct bme680_data {
struct mutex lock;
struct bme680_dev bme680_dev;
struct bme680_field_data field_data;
struct regmap *regmap;
};
static const struct iio_chan_spec bme680_channels[] = {
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
},
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
},
{
.type = IIO_HUMIDITYRELATIVE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
},
{
.type = IIO_RESISTANCE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
},
};
static void bme680_delay_ms(u32 period)
{
/*
* Return control or wait,
* for a period amount of milliseconds
*/
msleep_interruptible(period);
}
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);
int ret;
mutex_lock(&data->lock);
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
{
uint16_t meas_period;
u8 set_required_settings;
/* Select the power mode */
/* Must be set before writing the sensor configuration */
data->bme680_dev.power_mode = BME680_FORCED_MODE;
/* Set the required sensor settings needed */
set_required_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_FILTER_SEL
| BME680_GAS_SENSOR_SEL;
/* Set the desired sensor configuration */
ret = bme680_set_sensor_settings(set_required_settings, &data->bme680_dev);
if (ret) {
dev_err(&indio_dev->dev, "%s(): bme680_set_sensor_settings() failed(%d)\n", __func__, ret);
goto exit;
}
/* Set the power mode */
ret = bme680_set_sensor_mode(&data->bme680_dev);
if (ret) {
dev_err(&indio_dev->dev, "%s(): bme680_set_sensor_settings() failed(%d)\n", __func__, ret);
goto exit;
}
bme680_get_profile_dur(&meas_period, &data->bme680_dev);
/* Delay till the measurement is ready */
bme680_delay_ms(meas_period * 2);
ret = bme680_get_sensor_data(&data->field_data, &data->bme680_dev);
if (ret) {
if (ret != BME680_W_NO_NEW_DATA) {
dev_err(&indio_dev->dev, "%s(): bme680_get_sensor_data() failed(%d)\n",
__func__, ret);
}
else {
dev_warn(&indio_dev->dev, "%s(): no new data\n", __func__);
ret = BME680_OK;
}
goto exit;
}
switch (chan->type) {
case IIO_RESISTANCE:
if (data->field_data.status & BME680_GASM_VALID_MSK) {
dev_dbg(&indio_dev->dev, "%s(): gas resistance(%d ohms)\n",
__func__, data->field_data.gas_resistance);
*val = data->field_data.gas_resistance;
ret = IIO_VAL_INT;
}
else {
dev_warn(&indio_dev->dev, "%s(): unstable heating setup\n",
__func__);
ret = -EAGAIN;
goto exit;
}
break;
case IIO_PRESSURE:
dev_dbg(&indio_dev->dev, "%s(): pressure(%d.%u hPa)\n",
__func__, data->field_data.pressure / 100,
data->field_data.pressure % 100);
*val = data->field_data.pressure;
ret = IIO_VAL_INT;
break;
case IIO_HUMIDITYRELATIVE:
dev_dbg(&indio_dev->dev, "%s(): humidity(%d.%u %%rH)\n",
__func__, data->field_data.humidity / 1000,
data->field_data.humidity % 1000);
*val = data->field_data.humidity;
*val2 = 1000;
ret = IIO_VAL_FRACTIONAL;
break;
case IIO_TEMP:
dev_dbg(&indio_dev->dev, "%s(): temperature(%d.%u degC)\n",
__func__, data->field_data.temperature / 100,
data->field_data.temperature % 100);
*val = data->field_data.temperature;
*val2 = 100;
ret = IIO_VAL_FRACTIONAL;
break;
default:
dev_err(&indio_dev->dev, "%s(): unsupported channel type(%d)\n",
__func__, chan->type);
ret = -EINVAL;
goto exit;
}
break;
}
default:
dev_err(&indio_dev->dev, "%s(): unsupported mask(%ld)\n", __func__, mask);
ret = -EINVAL;
goto exit;
}
exit:
mutex_unlock(&data->lock);
return ret;
}
static int bme680_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
dev_err(&indio_dev->dev, "%s(): unsupported\n", __func__);
return -EINVAL;
}
static const struct iio_info bme680_info = {
.driver_module = THIS_MODULE,
.read_raw = &bme680_read_raw,
.write_raw = &bme680_write_raw,
};
static int match(struct device *dev, void *data)
{
struct i2c_client *client = to_i2c_client(dev);
return (client->addr == BME680_I2C_ADDR_PRIMARY);
}
static struct i2c_client *bme680_i2c_get_client(void)
{
struct device *dev = bus_find_device(&i2c_bus_type, NULL, NULL, match);
if (!dev)
return NULL;
return to_i2c_client(dev);
}
static s8 bme680_i2c_read(u8 dev_id, u8 reg_addr, u8 *reg_data, u16 len)
{
static struct i2c_client *client = NULL;
struct iio_dev *indio_dev;
struct bme680_data *data;
int ret = 0;
if (!client) {
client = bme680_i2c_get_client();
if (IS_ERR(client)) {
ret = PTR_ERR(client);
goto exit;
}
}
indio_dev = dev_get_drvdata(&client->dev);
data = iio_priv(indio_dev);
ret = regmap_bulk_read(data->regmap, reg_addr, reg_data, len);
if (ret < 0) {
dev_err(&client->dev, "%s(): regmap_bulk_read() failed(%d)\n", __func__, ret);
goto exit;
}
exit:
return ret;
}
static s8 bme680_i2c_write(u8 dev_id, u8 reg_addr, u8 *reg_data, u16 len)
{
static struct i2c_client *client = NULL;
struct iio_dev *indio_dev;
struct bme680_data *data;
int ret = 0;
if (!client) {
client = bme680_i2c_get_client();
if (IS_ERR(client)) {
ret = PTR_ERR(client);
goto exit;
}
}
indio_dev = dev_get_drvdata(&client->dev);
data = iio_priv(indio_dev);
ret = regmap_bulk_write(data->regmap, reg_addr, reg_data, len);
if (ret < 0) {
dev_err(&client->dev, "%s(): regmap_bulk_write() failed(%d)\n", __func__, ret);
goto exit;
}
exit:
return ret;
}
static int bme680_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct bme680_data *data;
dev_info(&client->dev, "%s(): probe addr(0x%04x) chip ID(0x%lx)\n",
__func__, client->addr, id->driver_data);
if (id->driver_data != BME680_CHIP_ID) {
dev_err(&client->dev, "%s(): invalid chip ID(0x%lx)\n", __func__, id->driver_data);
ret = -EINVAL;
goto exit;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (IS_ERR(indio_dev)) {
dev_err(&client->dev, "%s(): devm_iio_device_alloc() failed(%d)\n", __func__, ret);
ret = PTR_ERR(indio_dev);
goto exit;
}
data = iio_priv(indio_dev);
mutex_init(&data->lock);
data->regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "%s(): devm_regmap_init_i2c() failed(%d)\n", __func__, ret);
ret = PTR_ERR(data->regmap);
goto exit;
}
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
indio_dev->channels = bme680_channels;
indio_dev->info = &bme680_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = 4;
dev_set_drvdata(&client->dev, indio_dev);
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&client->dev, "%s(): iio_device_register() failed(%d)\n", __func__, ret);
goto exit;
}
data->bme680_dev.dev_id = BME680_I2C_ADDR_PRIMARY;
data->bme680_dev.intf = BME680_I2C_INTF;
data->bme680_dev.read = bme680_i2c_read;
data->bme680_dev.write = bme680_i2c_write;
data->bme680_dev.delay_ms = bme680_delay_ms;
ret = bme680_init(&data->bme680_dev);
if (ret) {
dev_err(&client->dev, "%s(): bme680_init() failed(%d)\n", __func__, ret);
goto exit;
}
/* Set the temperature, pressure and humidity settings */
data->bme680_dev.tph_sett.os_hum = BME680_OS_2X;
data->bme680_dev.tph_sett.os_pres = BME680_OS_4X;
data->bme680_dev.tph_sett.os_temp = BME680_OS_8X;
data->bme680_dev.tph_sett.filter = BME680_FILTER_SIZE_3;
/* Set the remaining gas sensor settings and link the heating profile */
data->bme680_dev.gas_sett.run_gas = BME680_ENABLE_GAS_MEAS;
/* Create a ramp heat waveform in 3 steps */
data->bme680_dev.gas_sett.heatr_temp = 320; /* degree Celsius */
data->bme680_dev.gas_sett.heatr_dur = 150; /* milliseconds */
/* Select the power mode */
/* Must be set before writing the sensor configuration */
data->bme680_dev.power_mode = BME680_FORCED_MODE;
exit:
return ret;
}
static int bme680_i2c_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
dev_info(&client->dev, "%s(): remove\n", __func__);
iio_device_unregister(indio_dev);
iio_device_free(indio_dev);
return 0;
}
static const struct acpi_device_id bme680_acpi_i2c_match[] = {
{"BME680", BME680_CHIP_ID },
{ },
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_i2c_match);
#ifdef CONFIG_OF
static const struct of_device_id bme680_of_i2c_match[] = {
{ .compatible = "bosch,bme680", .data = (void *)BME680_CHIP_ID },
{ },
};
MODULE_DEVICE_TABLE(of, bme680_of_i2c_match);
#else
#define bme680_of_i2c_match NULL
#endif
static const struct i2c_device_id bme680_i2c_id[] = {
{"bme680", BME680_CHIP_ID },
{ },
};
MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
static struct i2c_driver bme680_i2c_driver = {
.driver = {
.name = "bme680",
.acpi_match_table = ACPI_PTR(bme680_acpi_i2c_match),
.of_match_table = of_match_ptr(bme680_of_i2c_match),
},
.probe = bme680_i2c_probe,
.remove = bme680_i2c_remove,
.id_table = bme680_i2c_id,
};
module_i2c_driver(bme680_i2c_driver);
MODULE_AUTHOR("David Clark <dclark@sierrawireless.com>");
MODULE_DESCRIPTION("Driver for Bosch Sensortec BME680 gas, humidity, pressure and temperature sensor");
MODULE_LICENSE("GPL v2");
......@@ -2,16 +2,17 @@ cflags:
{
// This driver depends on IIO
-DCONFIG_IIO
-DCONFIG_IIO_BUFFER
-DCONFIG_IIO_TRIGGERRED_BUFFER
-DDEBUG
-DSIERRA
-I$MANGOH_ROOT/linux_kernel_modules/bme680/BME680_driver
-DREGMAP
-DREGMAP_I2C
}
sources:
{
BME680_driver/bme680.c
bme680-i2c.c
bme680-regmap.c
bme680_i2c.c
bme680_core.c
}
requires:
......
#include <linux/device.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "bme680_defs.h"
static bool bme680_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case BME680_CONF_HEAT_CTRL_ADDR:
case BME680_CONF_ODR_RUN_GAS_NBC_ADDR:
case BME680_CONF_OS_H_ADDR:
case BME680_CONF_T_P_MODE_ADDR:
case BME680_SOFT_RESET_ADDR:
case BME680_GAS_WAIT0_ADDR:
return true;
default:
if (((reg >= BME680_CONF_ODR_FILT_ADDR) &&
(reg <= BME680_CONF_ODR_FILT_ADDR + BME680_FILTER_SIZE_127)) ||
((reg >= BME680_ADDR_SENS_CONF_START) &&
(reg <= BME680_ADDR_SENS_CONF_START + BME680_FILTER_SIZE_7)))
return true;
dev_err(dev, "%s(): Error register(0x%04x) not writable\n", __func__, reg);
return false;
};
}
static bool bme680_is_volatile_reg(struct device *dev, unsigned int reg)
{
if (((reg >= BME680_ADDR_GAS_CONF_START) && (reg <= BME680_ADDR_GAS_CONF_START + 9)) ||
((reg >= BME680_ADDR_SENS_CONF_START) && (reg <= BME680_ADDR_SENS_CONF_START + 9)))
return true;
return false;
}
const struct regmap_config bme680_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = BME680_COEFF_ADDR2 + BME680_COEFF_ADDR2_LEN,
.cache_type = REGCACHE_NONE,
.writeable_reg = bme680_is_writeable_reg,
.volatile_reg = bme680_is_volatile_reg,
};
EXPORT_SYMBOL(bme680_regmap_config);
/* 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_ */
diff --git a/bme680.c b/bme680.c
index 5b6bfd7..8f82bf5 100644
--- a/bme680.c
+++ b/bme680.c
@@ -358,13 +358,20 @@ int8_t bme680_set_regs(const uint8_t *reg_addr, const uint8_t *reg_data, uint8_t
tmp_buff[(2 * index)] = reg_addr[index];
}
tmp_buff[(2 * index) + 1] = reg_data[index];
+#ifdef SIERRA
+ dev->com_rslt = dev->write(dev->dev_id, tmp_buff[2 * index], &tmp_buff[(2 * index) + 1], 1);
+ if (dev->com_rslt != 0)
+ rslt = BME680_E_COM_FAIL;
+#endif
}
+#ifndef SIERRA
/* Write the interleaved array */
if (rslt == BME680_OK) {
dev->com_rslt = dev->write(dev->dev_id, tmp_buff[0], &tmp_buff[1], (2 * len) - 1);
if (dev->com_rslt != 0)
rslt = BME680_E_COM_FAIL;
}
+#endif
} else {
rslt = BME680_E_INVALID_LENGTH;
}
@@ -770,6 +777,7 @@ static int8_t get_calib_data(struct bme680_dev *dev)
| (coeff_array[BME680_H1_LSB_REG] & BME680_BIT_H1_DATA_MSK));
dev->calib.par_h2 = (uint16_t) (((uint16_t) coeff_array[BME680_H2_MSB_REG] << BME680_HUM_REG_SHIFT_VAL)
| ((coeff_array[BME680_H2_LSB_REG]) >> BME680_HUM_REG_SHIFT_VAL));
+
dev->calib.par_h3 = (int8_t) coeff_array[BME680_H3_REG];
dev->calib.par_h4 = (int8_t) coeff_array[BME680_H4_REG];
dev->calib.par_h5 = (int8_t) coeff_array[BME680_H5_REG];
@@ -991,7 +999,13 @@ static uint32_t calc_gas_resistance(uint16_t gas_res_adc, uint8_t gas_range, con
((int64_t) lookupTable1[gas_range])) >> 16;
var2 = (((int64_t) ((int64_t) gas_res_adc << 15) - (int64_t) (16777216)) + var1);
var3 = (((int64_t) lookupTable2[gas_range] * (int64_t) var1) >> 9);
+#ifdef SIERRA
+ var3 += (int64_t) var2 >> 1;
+ do_div(var3, (int64_t) var2);
+ calc_gas_res = (uint32_t) var3;
+#else
calc_gas_res = (uint32_t) ((var3 + ((int64_t) var2 >> 1)) / (int64_t) var2);
+#endif
return calc_gas_res;
}
// SPDX-License-Identifier: GPL-2.0
/*
* Bosch BME680 - Temperature, Pressure, Humidity & Gas Sensor
*
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*
* Datasheet:
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
*/
#include <linux/acpi.h>
#include "bitfield.h"
#include <linux/device.h>
#include <linux/module.h>
#include <linux/log2.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/version.h>
#include "bme680.h"
#define regmap_write_bits regmap_update_bits
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)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
#endif
},
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
#endif
},
{
.type = IIO_HUMIDITYRELATIVE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
#endif
},
{
.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:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L876
*
* Returns temperature measurement in DegC, resolutions is 0.01 DegC. Therefore,
* output value of "3233" represents 32.33 DegC.
*/
static s16 bme680_compensate_temp(struct bme680_data *data,
s32 adc_temp)
{
struct bme680_calib *calib = &data->bme680;
s64 var1, var2, var3;
s16 calc_temp;
var1 = (adc_temp >> 3) - (calib->par_t1 << 1);
var2 = (var1 * calib->par_t2) >> 11;
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
var3 = (var3 * (calib->par_t3 << 4)) >> 14;
data->t_fine = var2 + var3;
calc_temp = (data->t_fine * 5 + 128) >> 8;
return calc_temp;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L896
*
* Returns pressure measurement in Pa. Output value of "97356" represents
* 97356 Pa = 973.56 hPa.
*/
static u32 bme680_compensate_press(struct bme680_data *data,
u32 adc_press)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, press_comp;
var1 = (data->t_fine >> 1) - 64000;
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2;
var2 = var2 + (var1 * calib->par_p5 << 1);
var2 = (var2 >> 2) + (calib->par_p4 << 16);
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
(calib->par_p3 << 5)) >> 3) +
((calib->par_p2 * var1) >> 1);
var1 = var1 >> 18;
var1 = ((32768 + var1) * calib->par_p1) >> 15;
press_comp = 1048576 - adc_press;
press_comp = ((press_comp - (var2 >> 12)) * 3125);
if (press_comp >= BME680_MAX_OVERFLOW_VAL)
press_comp = ((press_comp / (u32)var1) << 1);
else
press_comp = ((press_comp << 1) / (u32)var1);
var1 = (calib->par_p9 * (((press_comp >> 3) *
(press_comp >> 3)) >> 13)) >> 12;
var2 = ((press_comp >> 2) * calib->par_p8) >> 13;
var3 = ((press_comp >> 8) * (press_comp >> 8) *
(press_comp >> 8) * calib->par_p10) >> 17;
press_comp += (var1 + var2 + var3 + (calib->par_p7 << 7)) >> 4;
return press_comp;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L937
*
* Returns humidity measurement in percent, resolution is 0.001 percent. Output
* value of "43215" represents 43.215 %rH.
*/
static u32 bme680_compensate_humid(struct bme680_data *data,
u16 adc_humid)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, var4, var5, var6, temp_scaled, calc_hum;
temp_scaled = (data->t_fine * 5 + 128) >> 8;
var1 = (adc_humid - ((s32) ((s32) calib->par_h1 * 16))) -
(((temp_scaled * (s32) calib->par_h3) / 100) >> 1);
var2 = ((s32) calib->par_h2 *
(((temp_scaled * calib->par_h4) / 100) +
(((temp_scaled * ((temp_scaled * calib->par_h5) / 100))
>> 6) / 100) + (1 << 14))) >> 10;
var3 = var1 * var2;
var4 = calib->par_h6 << 7;
var4 = (var4 + ((temp_scaled * calib->par_h7) / 100)) >> 4;
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
var6 = (var4 * var5) >> 1;
calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
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:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L973
*
* Returns gas measurement in Ohm. Output value of "82986" represent 82986 ohms.
*/
#if 1
static u32 bme680_compensate_gas(struct bme680_data *data, u16 gas_res_adc,
u8 gas_range)
{
struct bme680_calib *calib = &data->bme680;
s64 var1;
u64 var2;
s64 var3;
u32 calc_gas_res;
/* Look up table for the possible gas range values */
const u32 lookupTable[16] = {2147483647u, 2147483647u,
2147483647u, 2147483647u, 2147483647u,
2126008810u, 2147483647u, 2130303777u,
2147483647u, 2147483647u, 2143188679u,
2136746228u, 2147483647u, 2126008810u,
2147483647u, 2147483647u};
var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
((s64) lookupTable[gas_range])) >> 16;
var2 = ((gas_res_adc << 15) - 16777216) + var1;
var3 = ((125000 << (15 - gas_range)) * var1) >> 9;
var3 += (var2 >> 1);
calc_gas_res = div64_s64(var3, (s64) var2);
return calc_gas_res;
}
#endif
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1002
*/
static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, var4, var5, heatr_res_x100;
u8 heatr_res;
if (temp > 400) /* Cap temperature */
temp = 400;
var1 = (((s32) BME680_AMB_TEMP * calib->par_gh3) / 1000) * 256;
var2 = (calib->par_gh1 + 784) * (((((calib->par_gh2 + 154009) *
temp * 5) / 100)
+ 3276800) / 10);
var3 = var1 + (var2 / 2);
var4 = (var3 / (calib->res_heat_range + 4));
var5 = 131 * calib->res_heat_val + 65536;
heatr_res_x100 = ((var4 / var5) - 250) * 34;
heatr_res = (heatr_res_x100 + 50) / 100;
return heatr_res;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1188
*/
static u8 bme680_calc_heater_dur(u16 dur)
{
u8 durval, factor = 0;
if (dur >= 0xfc0) {
durval = 0xff; /* Max duration */
} else {
while (dur > 0x3F) {
dur = dur / 4;
factor += 1;
}
durval = dur + (factor * 64);
}
return durval;
}
static int bme680_set_mode(struct bme680_data *data, bool mode)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
if (mode) {
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
BME680_MODE_MASK, BME680_MODE_FORCED);
if (ret < 0)
dev_err(dev, "failed to set forced mode\n");
} else {
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
BME680_MODE_MASK, BME680_MODE_SLEEP);
if (ret < 0)
dev_err(dev, "failed to set sleep mode\n");
}
return ret;
}
static int bme680_chip_config(struct bme680_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
u8 osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK,
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_VAL);
if (ret < 0) {
dev_err(dev, "failed to write config register\n");
return ret;
}
osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, data->oversampling_temp + 1) |
FIELD_PREP(BME680_OSRS_PRESS_MASK, 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_BIT | BME680_NB_CONV_0_VAL);
if (ret < 0)
dev_err(dev, "failed to write ctrl_gas_1 register\n");
return ret;
}
static int bme680_read_temp(struct bme680_data *data,
int *val, int *val2)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
__be32 tmp = 0;
s32 adc_temp;
s16 comp_temp;
/* set forced mode to trigger measurement */
ret = bme680_set_mode(data, true);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
(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;
}
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;
}
static int bme680_read_humid(struct bme680_data *data,
int *val, int *val2)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
__be16 tmp = 0;
s32 adc_humidity;
u32 comp_humidity;
/* Read and compensate temperature to get a reading of t_fine */
ret = bme680_read_temp(data, NULL, 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;
}
#if 1
static int bme680_read_gas(struct bme680_data *data,
int *val)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
__be16 tmp = 0;
unsigned int check;
u16 adc_gas_res;
u8 gas_range;
/* Set heater settings */
ret = bme680_gas_config(data);
if (ret < 0) {
dev_err(dev, "failed to set gas config\n");
return ret;
}
/* set forced mode to trigger measurement */
ret = bme680_set_mode(data, true);
if (ret < 0)
return ret;
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check);
if (check & BME680_GAS_MEAS_BIT) {
dev_err(dev, "gas measurement incomplete\n");
return -EBUSY;
}
ret = regmap_read(data->regmap, BME680_REG_GAS_R_LSB, &check);
if (ret < 0) {
dev_err(dev, "failed to read gas_r_lsb register\n");
return ret;
}
/*
* occurs if either the gas heating duration was insuffient
* to reach the target heater temperature or the target
* heater temperature was too high for the heater sink to
* reach.
*/
if ((check & BME680_GAS_STAB_BIT) == 0) {
dev_err(dev, "heater failed to reach the target temperature\n");
return -EINVAL;
}
ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
(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;
}
#endif
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;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
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;
}
#endif
default:
return -EINVAL;
}
}
#if 0
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) {
//#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
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;
}
//#endif
default:
return -EINVAL;
}
}
#endif
static const char bme680_oversampling_ratio_show[] = "1 2 4 8 16";
static IIO_CONST_ATTR(oversampling_ratio_available,
bme680_oversampling_ratio_show);
static struct attribute *bme680_attributes[] = {
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
NULL,
};
static const struct attribute_group bme680_attribute_group = {
.attrs = bme680_attributes,
};
static const struct iio_info bme680_info = {
.read_raw = &bme680_read_raw,
//.write_raw = &bme680_write_raw,
.attrs = &bme680_attribute_group,
};
static const char *bme680_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
return dev_name(dev);
}
int bme680_core_probe(struct device *dev, struct regmap *regmap,
const char *name)
{
struct iio_dev *indio_dev;
struct bme680_data *data;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
if (!name && ACPI_HANDLE(dev))
name = bme680_match_acpi_device(dev);
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
indio_dev->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 devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_GPL(bme680_core_probe);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 Driver");
MODULE_LICENSE("GPL v2");
// 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");
// SPDX-License-Identifier: GPL-2.0
/*
* BME680 - SPI Driver
*
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*/
#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_SPI,
BME680_CMD_SOFTRESET);
if (ret < 0) {
dev_err(&spi->dev, "Failed to reset chip\n");
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 <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 SPI driver");
MODULE_LICENSE("GPL v2");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment