-
-
Save himanshujha199640/5c2b9b5f39afa4c75cc3ad59ea4d1d17 to your computer and use it in GitHub Desktop.
Temperature channel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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