[RFC 2/3] iio: imu: bme680: Add temperaure, pressure & humidity channels

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add temperature,pressure,humidity channel function definitions to read
raw values from the channels in bme680_read_*() and calibrate them
to get the required readings in bme680_compensate_*() using the
calibration parameters. These calibration parameters(read-only) are
stored in the chip's NVM memory at the time of production and are
used to get compensated readings for various channels.

The various channels' oversampling ratio and IIR filter configurations
are handled by bme680_chip_config().

Cc: Daniel Baluta <daniel.baluta@xxxxxxxxx>
Signed-off-by: Himanshu Jha <himanshujha199640@xxxxxxxxx>
---
 drivers/iio/imu/bme680/bme680_core.c | 434 ++++++++++++++++++++++++++++++++++-
 1 file changed, 430 insertions(+), 4 deletions(-)

diff --git a/drivers/iio/imu/bme680/bme680_core.c b/drivers/iio/imu/bme680/bme680_core.c
index a6d013d..05712de 100644
--- a/drivers/iio/imu/bme680/bme680_core.c
+++ b/drivers/iio/imu/bme680/bme680_core.c
@@ -6,15 +6,73 @@
 #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_REG_MEAS_STAT	0x1D
+
+#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_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_REG_CONFIG		0x75
+#define BME680_FILTER_MASK		GENMASK(4, 2)
+#define BME680_FILTER_4X		BIT(2)
+
+/* TEMP/PRESS/HUMID reading skipped */
+#define BME680_MEAS_SKIPPED		0x8000
+
+/* 
+ * TODO: the following structs stores various calibration
+ * paramters which are read from the sensor's NVM and used in
+ * the compensation function to obtain processed output.
+ */
+struct bme680_calib {
+};
 
 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;
+};
+
+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 *data);
+	int (*read_temp)(struct bme680_data *data, int *val);
+	int (*read_press)(struct bme680_data *data, int *val);
+	int (*read_humid)(struct bme680_data *data, int *val);
 };
 
 const struct regmap_config bme680_regmap_config = {
@@ -23,9 +81,339 @@ const struct regmap_config bme680_regmap_config = {
 };
 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),
+	},
+};
+
+static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
+
+/* TODO: read calibration parameters from the sensor's NVM */
+static int bme680_read_calib(struct bme680_data *data,
+			     struct bme680_calib *calib)
+{
+	memset(calib, 0, sizeof(*calib));
+	return 0;
+}
+
+/* TODO: compensate raw temp readings using temp calibration parameters */
+static s16 bme680_compensate_temp(struct bme680_data *data,
+				  s32 adc_temp)
+{
+	return adc_temp;
+}
+
+/* TODO: compensate raw press readings using press calibration parameters */
+static u32 bme680_compensate_press(struct bme680_data *data,
+				   u32 adc_press)
+{
+	return adc_press;
+}
+
+/* TODO: compensate raw humid readings using humid calibration parameters */
+static u32 bme680_compensate_humid(struct bme680_data *data,
+				   u16 adc_humid)
+{
+	return adc_humid;
+}
+
+static int bme680_read_temp(struct bme680_data *data,
+			    int *val)
+{
+	int ret;
+	__be32 tmp = 0;
+	u32 adc_temp;
+	s16 comp_temp;
+
+	/* Set Forced Mode & sampling rate before reading raw data */
+	ret = data->chip_info->chip_config(data);
+	if (ret < 0) {
+		dev_err(data->dev,
+			"failed to set chip config %s\n", __func__);
+	}
+
+	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_MEAS_SKIPPED) {
+		/* reading was skipped */
+		dev_err(data->dev, "reading temperature skipped\n");
+		return -EIO;
+	}
+	comp_temp = bme680_compensate_temp(data, adc_temp);
+
+	*val = comp_temp;
+	return IIO_VAL_INT;
+
+	return 0;
+}
+
+static int bme680_read_press(struct bme680_data *data,
+			     int *val)
+{
+	int ret;
+	__be32 tmp = 0;
+	u32 comp_press, adc_press;
+
+	ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
+				(u8 *) &tmp, 3);
+	if (ret < 0) {
+		dev_err(data->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(data->dev, "reading pressure skipped\n");
+		return -EIO;
+	}
+	comp_press = bme680_compensate_press(data, adc_press);
+
+	*val = comp_press;
+	return IIO_VAL_INT;
+}
+
+static int bme680_read_humid(struct bme680_data *data,
+			     int *val)
+{
+	int ret;
+	__be16 tmp = 0;
+	u16 adc_humidity;
+	u32 comp_humidity;
+
+	ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
+				(u8 *) &tmp, 2);
+	if (ret < 0) {
+		dev_err(data->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(data->dev, "reading humidity skipped\n");
+		return -EIO;
+	}
+	comp_humidity = bme680_compensate_humid(data, adc_humidity);
+
+	*val = comp_humidity;
+	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);
+	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;
+		case IIO_PRESSURE:
+			ret = data->chip_info->read_press(data, val);
+			break;
+		case IIO_HUMIDITYRELATIVE:
+			ret = data->chip_info->read_humid(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;
+		case IIO_PRESSURE:
+			*val = 1 << data->oversampling_press;
+			ret = IIO_VAL_INT;
+			break;
+		case IIO_HUMIDITYRELATIVE:
+			*val = 1 << data->oversampling_humid;
+			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);
+			break;
+		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);
@@ -33,15 +421,15 @@ static int bme680_chip_init(struct bme680_data *data, bool use_spi)
 	int ret;
 
 	/* Power on Soft Reset */
-	ret = regmap_write(data->regmap, BME680_SOFT_RESET, BME680_CMD_SOFTRESET);
+	ret = regmap_write(data->regmap, BME680_SOFT_RESET,
+			   BME680_CMD_SOFTRESET);
 	if (ret < 0)
 		return ret;
 
-	if (!use_spi) {
+	if (!use_spi)
 		ret = regmap_read(data->regmap, BME680_REG_CHIP_I2C_ID, &val);
-	} else {
+	else
 		ret = regmap_read(data->regmap, BME680_REG_CHIP_SPI_ID, &val);
-	}
 
 	if (ret < 0) {
 		dev_err(dev, "Error reading chip ID\n");
@@ -57,6 +445,22 @@ static int bme680_chip_init(struct bme680_data *data, bool use_spi)
 	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,
+	.read_press = bme680_read_press,
+	.read_humid = bme680_read_humid,
+};
+
 int bme680_core_probe(struct device *dev, struct regmap *regmap,
 		      const char *name, bool use_spi)
 {
@@ -78,11 +482,33 @@ int bme680_core_probe(struct device *dev, struct 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;
+
+	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);
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux