Skip to content

Instantly share code, notes, and snippets.

@himanshujha199640
Created June 4, 2018 12:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save himanshujha199640/5c2b9b5f39afa4c75cc3ad59ea4d1d17 to your computer and use it in GitHub Desktop.
Save himanshujha199640/5c2b9b5f39afa4c75cc3ad59ea4d1d17 to your computer and use it in GitHub Desktop.
Temperature channel
/*
* Bosch BME680 - Temperature, Pressure, Humidity & Gas Sensor
*
* IIO core driver - I2C & SPI bus support
*/
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/log2.h>
#include <linux/iio/sysfs.h>
#define BME680_REG_CHIP_I2C_ID 0xD0
#define BME680_REG_CHIP_SPI_ID 0x50
#define BME680_CHIP_ID_VAL 0x61
#define BME680_SOFT_RESET 0xE0
#define BME680_CMD_SOFTRESET 0xB6
#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_CTRL_HUMIDITY 0x72
#define BME680_OSRS_HUMIDITY_MASK (BIT(2) | BIT(1) | BIT(0))
#define BME680_REG_CTRL_MEAS 0x74
#define BME680_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5))
#define BME680_OSRS_PRESS_MASK (BIT(4) | BIT(3) | BIT(2))
#define BME680_MODE_MASK (BIT(1) | BIT(0))
#define BME680_MODE_FORCED BIT(0)
#define BME680_REG_CONFIG 0x75
#define BME680_FILTER_MASK (BIT(4) | BIT(3) | BIT(2))
#define BME680_FILTER_4X BIT(3)
#define BME680_TEMP_SKIPPED 0x80000
#define BME680_REG_COMP_TEMP_START 0x89
#define BME680_COMP_TEMP_REG_COUNT 5
#define BME680_COEFF_ADDR1 0x89
#define BME680_COEFF_ADDR1_LEN 25
#define BME680_COEFF_ADDR2 0xE1
#define BME680_COEFF_ADDR2_LEN 16
#define BME680_COEFF_SIZE 41
/* Macro to combine two 8 bit data's to form a 16 bit data */
#define BME680_CONCAT_BYTES(msb, lsb) (((uint16_t)msb << 8) | (uint16_t)lsb)
/* Array Index to Field data mapping for Calibration Data*/
#define BME680_T2_LSB_REG 1
#define BME680_T2_MSB_REG 2
#define BME680_T3_REG 3
#define BME680_P1_LSB_REG 5
#define BME680_P1_MSB_REG 6
#define BME680_P2_LSB_REG 7
#define BME680_P2_MSB_REG 8
#define BME680_P3_REG 9
#define BME680_P4_LSB_REG 11
#define BME680_P4_MSB_REG 12
#define BME680_P5_LSB_REG 13
#define BME680_P5_MSB_REG 14
#define BME680_P7_REG 15
#define BME680_P6_REG 16
#define BME680_P8_LSB_REG 19
#define BME680_P8_MSB_REG 20
#define BME680_P9_LSB_REG 21
#define BME680_P9_MSB_REG 22
#define BME680_P10_REG 23
#define BME680_H2_MSB_REG 25
#define BME680_H2_LSB_REG 26
#define BME680_H1_LSB_REG 26
#define BME680_H1_MSB_REG 27
#define BME680_H3_REG 28
#define BME680_H4_REG 29
#define BME680_H5_REG 30
#define BME680_H6_REG 31
#define BME680_H7_REG 32
#define BME680_T1_LSB_REG 33
#define BME680_T1_MSB_REG 34
#define BME680_GH2_LSB_REG 35
#define BME680_GH2_MSB_REG 36
#define BME680_GH1_REG 37
#define BME680_GH3_REG 38
struct bme680_calib {
u16 par_t1;
s16 par_t2;
s8 par_t3;
};
enum { par_t1, par_t2, par_t3 };
struct bme680_data {
struct regmap *regmap;
struct device *dev;
const struct bme680_chip_info *chip_info;
struct bme680_calib bme680;
u8 oversampling_temp;
u8 oversampling_press;
u8 oversampling_humid;
s32 t_fine;
};
struct bme680_chip_info {
const int *oversampling_temp_avail;
int num_oversampling_temp_avail;
const int *oversampling_press_avail;
int num_oversampling_press_avail;
const int *oversampling_humid_avail;
int num_oversampling_humid_avail;
int (*chip_config)(struct bme680_data *);
int (*read_temp)(struct bme680_data *, int *);
};
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),
},
};
static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
static int bme680_read_calib(struct bme680_data *data,
struct bme680_calib *calib)
{
int ret;
u8 t_buf[BME680_COEFF_SIZE] = {0};
/* Read temperature calibration values. */
ret = regmap_bulk_read(data->regmap, BME680_COEFF_ADDR1,
t_buf, BME680_COEFF_ADDR1_LEN);
/* append the remaing parameters to the same array */
ret = regmap_bulk_read(data->regmap, BME680_COEFF_ADDR2,
&t_buf[BME680_COEFF_ADDR1_LEN],
BME680_COEFF_ADDR2_LEN);
if (ret < 0) {
dev_err(data->dev,
"failed to read temperature calibration parameters\n");
return ret;
}
/* Temperature related coefficients */
calib->par_t1 = (uint16_t) (BME680_CONCAT_BYTES(t_buf[BME680_T1_MSB_REG],
t_buf[BME680_T1_LSB_REG]));
calib->par_t2 = (int16_t) (BME680_CONCAT_BYTES(t_buf[BME680_T2_MSB_REG],
t_buf[BME680_T2_LSB_REG]));
calib->par_t3 = (int8_t) (t_buf[BME680_T3_REG]);
return 0;
}
static s32 bme680_compensate_temp(struct bme680_data *data,
s32 adc_temp)
{
s64 var1, var2, var3, calc_temp;
struct bme680_calib *calib = &data->bme680;
var1 = ((int32_t) adc_temp >> 3) - ((int32_t) calib->par_t1 << 1);
var2 = (var1 * (int32_t) calib->par_t2) >> 11;
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
var3 = ((var3) * ((int32_t) calib->par_t3 << 4)) >> 14;
data->t_fine = (int32_t) (var2 + var3);
calc_temp = (int16_t) (((data->t_fine * 5) + 128) >> 8);
return calc_temp;
}
static int bme680_read_temp(struct bme680_data *data,
int *val)
{
int ret;
__be32 tmp = 0;
s32 adc_temp, comp_temp;
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
(u8 *) &tmp, 3);
if (ret < 0) {
dev_err(data->dev, "failed to read temperature\n");
return ret;
}
adc_temp = be32_to_cpu(tmp) >> 12;
if (adc_temp == BME680_TEMP_SKIPPED) {
/* reading was skipped */
dev_err(data->dev, "reading temperature skipped\n");
return -EIO;
}
comp_temp = bme680_compensate_temp(data, adc_temp);
if (comp_temp > 0) {
*val = comp_temp;
return IIO_VAL_INT;
}
return 0;
}
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 = 0;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_TEMP:
ret = data->chip_info->read_temp(data, val);
break;
default:
ret = -EINVAL;
break;
}
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_TEMP:
*val = 1 << data->oversampling_temp;
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
}
return ret;
}
static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
int val)
{
const int *avail = data->chip_info->oversampling_temp_avail;
const int n = data->chip_info->num_oversampling_temp_avail;
int i;
for (i=0; i < n; ++i) {
if (avail[i] == val) {
data->oversampling_temp = ilog2(val);
return data->chip_info->chip_config(data);
}
}
return -EINVAL;
}
static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
int val)
{
const int *avail = data->chip_info->oversampling_press_avail;
const int n = data->chip_info->num_oversampling_press_avail;
int i;
for (i=0; i < n; ++i) {
if (avail[i] == val) {
data->oversampling_press = ilog2(val);
return data->chip_info->chip_config(data);
}
}
return -EINVAL;
}
static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
int val)
{
const int *avail = data->chip_info->oversampling_humid_avail;
const int n = data->chip_info->num_oversampling_humid_avail;
int i;
for (i=0; i < n; ++i) {
if (avail[i] == val) {
data->oversampling_humid = ilog2(val);
return data->chip_info->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);
int ret = 0;
switch(mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_TEMP:
ret = bme680_write_oversampling_ratio_temp(data, val);
break;
case IIO_PRESSURE:
ret = bme680_write_oversampling_ratio_press(data, val);
break;
case IIO_HUMIDITYRELATIVE:
ret = bme680_write_oversampling_ratio_humid(data, val);
default:
ret = -EINVAL;
break;
}
}
return ret;
}
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 int bme680_chip_config(struct bme680_data *data)
{
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(data->dev,
"failed to write Ctrl_hum 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 |
BME680_MODE_MASK,
osrs | BME680_MODE_FORCED);
if (ret < 0) {
dev_err(data->dev,
"failed to write Ctrl_meas register\n");
return ret;
}
/* IIR filter settings */
ret = regmap_update_bits(data->regmap, BME680_REG_CONFIG,
BME680_FILTER_MASK,
BME680_FILTER_4X);
if (ret < 0) {
dev_err(data->dev,
"failed to write Config register\n");
return ret;
}
return ret;
}
static int bme680_chip_init(struct bme680_data *data, bool use_spi)
{
struct device *dev = regmap_get_device(data->regmap);
unsigned int val;
int ret;
/* Power on Soft Reset */
ret = regmap_write(data->regmap, BME680_SOFT_RESET, BME680_CMD_SOFTRESET);
if (ret < 0)
return ret;
if (!use_spi) {
ret = regmap_read(data->regmap, BME680_REG_CHIP_I2C_ID, &val);
} else {
ret = regmap_read(data->regmap, BME680_REG_CHIP_SPI_ID, &val);
}
if (ret < 0) {
dev_err(dev, "Error reading chip ID\n");
return ret;
}
if (val != BME680_CHIP_ID_VAL) {
dev_err(dev, "Wrong chip ID, got %x expected %x\n",
val, BME680_CHIP_ID_VAL);
return -ENODEV;
} else {
dev_err(dev, "Verified chip val %x\n", val);
}
return 0;
}
static const struct bme680_chip_info bme680_chip_info = {
.oversampling_temp_avail = bme680_oversampling_avail,
.num_oversampling_temp_avail = ARRAY_SIZE(bme680_oversampling_avail),
.oversampling_press_avail = bme680_oversampling_avail,
.num_oversampling_press_avail = ARRAY_SIZE(bme680_oversampling_avail),
.oversampling_humid_avail = bme680_oversampling_avail,
.num_oversampling_press_avail = ARRAY_SIZE(bme680_oversampling_avail),
.chip_config = bme680_chip_config,
.read_temp = bme680_read_temp,
};
int bme680_core_probe(struct device *dev, struct regmap *regmap,
const char *name, bool use_spi)
{
struct iio_dev *indio_dev;
struct bme680_data *data;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
ret = bme680_chip_init(data, use_spi);
if (ret < 0)
return ret;
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;
data->chip_info = &bme680_chip_info;
data->oversampling_humid = ilog2(16);
data->oversampling_press = ilog2(16);
data->oversampling_temp = ilog2(2);
ret = data->chip_info->chip_config(data);
if (ret < 0) {
dev_err(data->dev,
"failed to set chip_config data\n");
return ret;
}
ret = bme680_read_calib(data, &data->bme680);
if (ret < 0) {
dev_err(data->dev,
"failed to read calibration coefficients\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret < 0)
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(bme680_core_probe);
void bme680_core_remove(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
iio_device_unregister(indio_dev);
}
EXPORT_SYMBOL_GPL(bme680_core_remove);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 Driver");
MODULE_LICENSE("GPL v2");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment