Commit 597dfb2a authored by Angel Iglesias's avatar Angel Iglesias Committed by Jonathan Cameron
Browse files

iio: pressure: bmp280: Add support for new sensor BMP580

Adds compatibility with the new sensor generation, the BMP580.

The measurement and initialization codepaths are adapted from
the device datasheet and the repository from manufacturer at
https://github.com/boschsensortec/BMP5-Sensor-API

.

Signed-off-by: default avatarAngel Iglesias <ang.iglesiasg@gmail.com>
Reviewed-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/f899fceec9b48bc173bd4b7555f0a237fa32d520.1676823250.git.ang.iglesiasg@gmail.com


Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 42cde880
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -17,14 +17,14 @@ config ABP060MG
	  will be called abp060mg.

config BMP280
	tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor driver"
	tristate "Bosch Sensortec BMP180/BMP280/BMP380/BMP580 pressure sensor driver"
	depends on (I2C || SPI_MASTER)
	select REGMAP
	select BMP280_I2C if (I2C)
	select BMP280_SPI if (SPI_MASTER)
	help
	  Say yes here to build support for Bosch Sensortec BMP180, BMP280 and
	  BMP380 pressure and temperature sensors. Also supports the BME280 with
	  Say yes here to build support for Bosch Sensortec BMP180, BMP280, BMP380
	  and BMP580 pressure and temperature sensors. Also supports the BME280 with
	  an additional humidity sensor channel.

	  To compile this driver as a module, choose M here: the core module
+336 −8
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
 * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
 * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf
 * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf
 * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf
 *
 * Notice:
 * The link to the bmp180 datasheet points to an outdated version missing these changes:
@@ -71,6 +72,41 @@ enum bmp380_odr {
	BMP380_ODR_0_0015HZ,
};

enum bmp580_odr {
	BMP580_ODR_240HZ,
	BMP580_ODR_218HZ,
	BMP580_ODR_199HZ,
	BMP580_ODR_179HZ,
	BMP580_ODR_160HZ,
	BMP580_ODR_149HZ,
	BMP580_ODR_140HZ,
	BMP580_ODR_129HZ,
	BMP580_ODR_120HZ,
	BMP580_ODR_110HZ,
	BMP580_ODR_100HZ,
	BMP580_ODR_89HZ,
	BMP580_ODR_80HZ,
	BMP580_ODR_70HZ,
	BMP580_ODR_60HZ,
	BMP580_ODR_50HZ,
	BMP580_ODR_45HZ,
	BMP580_ODR_40HZ,
	BMP580_ODR_35HZ,
	BMP580_ODR_30HZ,
	BMP580_ODR_25HZ,
	BMP580_ODR_20HZ,
	BMP580_ODR_15HZ,
	BMP580_ODR_10HZ,
	BMP580_ODR_5HZ,
	BMP580_ODR_4HZ,
	BMP580_ODR_3HZ,
	BMP580_ODR_2HZ,
	BMP580_ODR_1HZ,
	BMP580_ODR_0_5HZ,
	BMP580_ODR_0_25HZ,
	BMP580_ODR_0_125HZ,
};

/*
 * These enums are used for indexing into the array of compensation
 * parameters for BMP280.
@@ -326,7 +362,7 @@ static u32 bmp280_compensate_press(struct bmp280_data *data,
}

static int bmp280_read_temp(struct bmp280_data *data,
			    int *val)
			    int *val, int *val2)
{
	s32 adc_temp, comp_temp;
	int ret;
@@ -366,7 +402,7 @@ static int bmp280_read_press(struct bmp280_data *data,
	int ret;

	/* Read and compensate temperature so we get a reading of t_fine. */
	ret = bmp280_read_temp(data, NULL);
	ret = bmp280_read_temp(data, NULL, NULL);
	if (ret < 0)
		return ret;

@@ -398,7 +434,7 @@ static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2)
	int ret;

	/* Read and compensate temperature so we get a reading of t_fine. */
	ret = bmp280_read_temp(data, NULL);
	ret = bmp280_read_temp(data, NULL, NULL);
	if (ret < 0)
		return ret;

@@ -442,7 +478,7 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
			ret = data->chip_info->read_press(data, val, val2);
			break;
		case IIO_TEMP:
			ret = data->chip_info->read_temp(data, val);
			ret = data->chip_info->read_temp(data, val, val2);
			break;
		default:
			ret = -EINVAL;
@@ -954,7 +990,7 @@ static u32 bmp380_compensate_press(struct bmp280_data *data, u32 adc_press)
	return comp_press;
}

static int bmp380_read_temp(struct bmp280_data *data, int *val)
static int bmp380_read_temp(struct bmp280_data *data, int *val, int *val2)
{
	s32 comp_temp;
	u32 adc_temp;
@@ -994,7 +1030,7 @@ static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2)
	int ret;

	/* Read and compensate for temperature so we get a reading of t_fine */
	ret = bmp380_read_temp(data, NULL);
	ret = bmp380_read_temp(data, NULL, NULL);
	if (ret)
		return ret;

@@ -1216,6 +1252,298 @@ const struct bmp280_chip_info bmp380_chip_info = {
};
EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280);

static int bmp580_soft_reset(struct bmp280_data *data)
{
	unsigned int reg;
	int ret;

	ret = regmap_write(data->regmap, BMP580_REG_CMD, BMP580_CMD_SOFT_RESET);
	if (ret) {
		dev_err(data->dev, "failed to send reset command to device\n");
		return ret;
	}
	usleep_range(2000, 2500);

	/* Dummy read of chip_id */
	ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, &reg);
	if (ret) {
		dev_err(data->dev, "failed to reestablish comms after reset\n");
		return ret;
	}

	ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, &reg);
	if (ret) {
		dev_err(data->dev, "error reading interrupt status register\n");
		return ret;
	}
	if (!(reg & BMP580_INT_STATUS_POR_MASK)) {
		dev_err(data->dev, "error resetting sensor\n");
		return -EINVAL;
	}

	return 0;
}

/*
 * Contrary to previous sensors families, compensation algorithm is builtin.
 * We are only required to read the register raw data and adapt the ranges
 * for what is expected on IIO ABI.
 */

static int bmp580_read_temp(struct bmp280_data *data, int *val, int *val2)
{
	s32 raw_temp;
	int ret;

	ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB, data->buf,
			       sizeof(data->buf));
	if (ret) {
		dev_err(data->dev, "failed to read temperature\n");
		return ret;
	}

	raw_temp = get_unaligned_le24(data->buf);
	if (raw_temp == BMP580_TEMP_SKIPPED) {
		dev_err(data->dev, "reading temperature skipped\n");
		return -EIO;
	}

	/*
	 * Temperature is returned in Celsius degrees in fractional
	 * form down 2^16. We reescale by x1000 to return milli Celsius
	 * to respect IIO ABI.
	 */
	*val = raw_temp * 1000;
	*val2 = 16;
	return IIO_VAL_FRACTIONAL_LOG2;
}

static int bmp580_read_press(struct bmp280_data *data, int *val, int *val2)
{
	u32 raw_press;
	int ret;

	ret = regmap_bulk_read(data->regmap, BMP580_REG_PRESS_XLSB, data->buf,
			       sizeof(data->buf));
	if (ret) {
		dev_err(data->dev, "failed to read pressure\n");
		return ret;
	}

	raw_press = get_unaligned_le24(data->buf);
	if (raw_press == BMP580_PRESS_SKIPPED) {
		dev_err(data->dev, "reading pressure skipped\n");
		return -EIO;
	}
	/*
	 * Pressure is returned in Pascals in fractional form down 2^16.
	 * We reescale /1000 to convert to kilopascal to respect IIO ABI.
	 */
	*val = raw_press;
	*val2 = 64000; /* 2^6 * 1000 */
	return IIO_VAL_FRACTIONAL;
}

static const int bmp580_odr_table[][2] = {
	[BMP580_ODR_240HZ] =	{240, 0},
	[BMP580_ODR_218HZ] =	{218, 0},
	[BMP580_ODR_199HZ] =	{199, 0},
	[BMP580_ODR_179HZ] =	{179, 0},
	[BMP580_ODR_160HZ] =	{160, 0},
	[BMP580_ODR_149HZ] =	{149, 0},
	[BMP580_ODR_140HZ] =	{140, 0},
	[BMP580_ODR_129HZ] =	{129, 0},
	[BMP580_ODR_120HZ] =	{120, 0},
	[BMP580_ODR_110HZ] =	{110, 0},
	[BMP580_ODR_100HZ] =	{100, 0},
	[BMP580_ODR_89HZ] =	{89, 0},
	[BMP580_ODR_80HZ] =	{80, 0},
	[BMP580_ODR_70HZ] =	{70, 0},
	[BMP580_ODR_60HZ] =	{60, 0},
	[BMP580_ODR_50HZ] =	{50, 0},
	[BMP580_ODR_45HZ] =	{45, 0},
	[BMP580_ODR_40HZ] =	{40, 0},
	[BMP580_ODR_35HZ] =	{35, 0},
	[BMP580_ODR_30HZ] =	{30, 0},
	[BMP580_ODR_25HZ] =	{25, 0},
	[BMP580_ODR_20HZ] =	{20, 0},
	[BMP580_ODR_15HZ] =	{15, 0},
	[BMP580_ODR_10HZ] =	{10, 0},
	[BMP580_ODR_5HZ] =	{5, 0},
	[BMP580_ODR_4HZ] =	{4, 0},
	[BMP580_ODR_3HZ] =	{3, 0},
	[BMP580_ODR_2HZ] =	{2, 0},
	[BMP580_ODR_1HZ] =	{1, 0},
	[BMP580_ODR_0_5HZ] =	{0, 500000},
	[BMP580_ODR_0_25HZ] =	{0, 250000},
	[BMP580_ODR_0_125HZ] =	{0, 125000},
};

static int bmp580_preinit(struct bmp280_data *data)
{
	unsigned int reg;
	int ret;

	/* Issue soft-reset command */
	ret = bmp580_soft_reset(data);
	if (ret)
		return ret;

	/* Post powerup sequence */
	ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, &reg);
	if (ret)
		return ret;

	/* Print warn message if we don't know the chip id */
	if (reg != BMP580_CHIP_ID && reg != BMP580_CHIP_ID_ALT)
		dev_warn(data->dev, "preinit: unexpected chip_id\n");

	ret = regmap_read(data->regmap, BMP580_REG_STATUS, &reg);
	if (ret)
		return ret;

	/* Check nvm status */
	if (!(reg & BMP580_STATUS_NVM_RDY_MASK) || (reg & BMP580_STATUS_NVM_ERR_MASK)) {
		dev_err(data->dev, "preinit: nvm error on powerup sequence\n");
		return -EIO;
	}

	return 0;
}

static int bmp580_chip_config(struct bmp280_data *data)
{
	bool change = false, aux;
	unsigned int tmp;
	u8 reg_val;
	int ret;

	/* Sets sensor in standby mode */
	ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG,
				 BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS,
				 BMP580_ODR_DEEPSLEEP_DIS |
				 FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP));
	if (ret) {
		dev_err(data->dev, "failed to change sensor to standby mode\n");
		return ret;
	}
	/* From datasheet's table 4: electrical characteristics */
	usleep_range(2500, 3000);

	/* Set default DSP mode settings */
	reg_val = FIELD_PREP(BMP580_DSP_COMP_MASK, BMP580_DSP_PRESS_TEMP_COMP_EN) |
		  BMP580_DSP_SHDW_IIR_TEMP_EN | BMP580_DSP_SHDW_IIR_PRESS_EN;

	ret = regmap_update_bits(data->regmap, BMP580_REG_DSP_CONFIG,
				 BMP580_DSP_COMP_MASK |
				 BMP580_DSP_SHDW_IIR_TEMP_EN |
				 BMP580_DSP_SHDW_IIR_PRESS_EN, reg_val);

	/* Configure oversampling */
	reg_val = FIELD_PREP(BMP580_OSR_TEMP_MASK, data->oversampling_temp) |
		  FIELD_PREP(BMP580_OSR_PRESS_MASK, data->oversampling_press) |
		  BMP580_OSR_PRESS_EN;

	ret = regmap_update_bits_check(data->regmap, BMP580_REG_OSR_CONFIG,
				       BMP580_OSR_TEMP_MASK | BMP580_OSR_PRESS_MASK |
				       BMP580_OSR_PRESS_EN,
				       reg_val, &aux);
	if (ret) {
		dev_err(data->dev, "failed to write oversampling register\n");
		return ret;
	}
	change = change || aux;

	/* Configure output data rate */
	ret = regmap_update_bits_check(data->regmap, BMP580_REG_ODR_CONFIG, BMP580_ODR_MASK,
				       FIELD_PREP(BMP580_ODR_MASK, data->sampling_freq),
				       &aux);
	if (ret) {
		dev_err(data->dev, "failed to write ODR configuration register\n");
		return ret;
	}
	change = change || aux;

	/* Set filter data */
	reg_val = FIELD_PREP(BMP580_DSP_IIR_PRESS_MASK, data->iir_filter_coeff) |
		  FIELD_PREP(BMP580_DSP_IIR_TEMP_MASK, data->iir_filter_coeff);

	ret = regmap_update_bits_check(data->regmap, BMP580_REG_DSP_IIR,
				       BMP580_DSP_IIR_PRESS_MASK |
				       BMP580_DSP_IIR_TEMP_MASK,
				       reg_val, &aux);
	if (ret) {
		dev_err(data->dev, "failed to write config register\n");
		return ret;
	}
	change = change || aux;

	/* Restore sensor to normal operation mode */
	ret = regmap_write_bits(data->regmap, BMP580_REG_ODR_CONFIG,
				BMP580_MODE_MASK,
				FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_NORMAL));
	if (ret) {
		dev_err(data->dev, "failed to set normal mode\n");
		return ret;
	}
	/* From datasheet's table 4: electrical characteristics */
	usleep_range(3000, 3500);

	if (change) {
		/*
		 * Check if ODR and OSR settings are valid or we are
		 * operating in a degraded mode.
		 */
		ret = regmap_read(data->regmap, BMP580_REG_EFF_OSR, &tmp);
		if (ret) {
			dev_err(data->dev, "error reading effective OSR register\n");
			return ret;
		}
		if (!(tmp & BMP580_EFF_OSR_VALID_ODR)) {
			dev_warn(data->dev, "OSR and ODR incompatible settings detected\n");
			/* Set current OSR settings from data on effective OSR */
			data->oversampling_temp = FIELD_GET(BMP580_EFF_OSR_TEMP_MASK, tmp);
			data->oversampling_press = FIELD_GET(BMP580_EFF_OSR_PRESS_MASK, tmp);
			return -EINVAL;
		}
	}

	return 0;
}

static const int bmp580_oversampling_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 };

const struct bmp280_chip_info bmp580_chip_info = {
	.id_reg = BMP580_REG_CHIP_ID,
	.chip_id = BMP580_CHIP_ID,
	.regmap_config = &bmp580_regmap_config,
	.start_up_time = 2000,
	.channels = bmp380_channels,
	.num_channels = 2,

	.oversampling_temp_avail = bmp580_oversampling_avail,
	.num_oversampling_temp_avail = ARRAY_SIZE(bmp580_oversampling_avail),
	.oversampling_temp_default = ilog2(1),

	.oversampling_press_avail = bmp580_oversampling_avail,
	.num_oversampling_press_avail = ARRAY_SIZE(bmp580_oversampling_avail),
	.oversampling_press_default = ilog2(4),

	.sampling_freq_avail = bmp580_odr_table,
	.num_sampling_freq_avail = ARRAY_SIZE(bmp580_odr_table) * 2,
	.sampling_freq_default = BMP580_ODR_50HZ,

	.iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail,
	.num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail),
	.iir_filter_coeff_default = 2,

	.chip_config = bmp580_chip_config,
	.read_temp = bmp580_read_temp,
	.read_press = bmp580_read_press,
	.preinit = bmp580_preinit,
};
EXPORT_SYMBOL_NS(bmp580_chip_info, IIO_BMP280);

static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas)
{
	const int conversion_time_max[] = { 4500, 7500, 13500, 25500 };
@@ -1336,7 +1664,7 @@ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp)
	return (data->t_fine + 8) >> 4;
}

static int bmp180_read_temp(struct bmp280_data *data, int *val)
static int bmp180_read_temp(struct bmp280_data *data, int *val, int *val2)
{
	s32 adc_temp, comp_temp;
	int ret;
@@ -1424,7 +1752,7 @@ static int bmp180_read_press(struct bmp280_data *data,
	int ret;

	/* Read and compensate temperature so we get a reading of t_fine. */
	ret = bmp180_read_temp(data, NULL);
	ret = bmp180_read_temp(data, NULL, NULL);
	if (ret)
		return ret;

+2 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ static const struct of_device_id bmp280_of_i2c_match[] = {
	{ .compatible = "bosch,bmp280", .data = &bmp280_chip_info },
	{ .compatible = "bosch,bme280", .data = &bme280_chip_info },
	{ .compatible = "bosch,bmp380", .data = &bmp380_chip_info },
	{ .compatible = "bosch,bmp580", .data = &bmp580_chip_info },
	{ },
};
MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match);
@@ -44,6 +45,7 @@ static const struct i2c_device_id bmp280_i2c_id[] = {
	{"bmp280", (kernel_ulong_t)&bmp280_chip_info },
	{"bme280", (kernel_ulong_t)&bme280_chip_info },
	{"bmp380", (kernel_ulong_t)&bmp380_chip_info },
	{"bmp580", (kernel_ulong_t)&bmp580_chip_info },
	{ },
};
MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id);
+60 −0
Original line number Diff line number Diff line
@@ -115,6 +115,54 @@ static bool bmp380_is_volatile_reg(struct device *dev, unsigned int reg)
	}
}

static bool bmp580_is_writeable_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case BMP580_REG_NVM_DATA_MSB:
	case BMP580_REG_NVM_DATA_LSB:
	case BMP580_REG_NVM_ADDR:
	case BMP580_REG_ODR_CONFIG:
	case BMP580_REG_OSR_CONFIG:
	case BMP580_REG_INT_SOURCE:
	case BMP580_REG_INT_CONFIG:
	case BMP580_REG_OOR_THR_MSB:
	case BMP580_REG_OOR_THR_LSB:
	case BMP580_REG_OOR_CONFIG:
	case BMP580_REG_OOR_RANGE:
	case BMP580_REG_IF_CONFIG:
	case BMP580_REG_FIFO_CONFIG:
	case BMP580_REG_FIFO_SEL:
	case BMP580_REG_DSP_CONFIG:
	case BMP580_REG_DSP_IIR:
	case BMP580_REG_CMD:
		return true;
	default:
		return false;
	}
}

static bool bmp580_is_volatile_reg(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case BMP580_REG_NVM_DATA_MSB:
	case BMP580_REG_NVM_DATA_LSB:
	case BMP580_REG_FIFO_COUNT:
	case BMP580_REG_INT_STATUS:
	case BMP580_REG_PRESS_XLSB:
	case BMP580_REG_PRESS_LSB:
	case BMP580_REG_PRESS_MSB:
	case BMP580_REG_FIFO_DATA:
	case BMP580_REG_TEMP_XLSB:
	case BMP580_REG_TEMP_LSB:
	case BMP580_REG_TEMP_MSB:
	case BMP580_REG_EFF_OSR:
	case BMP580_REG_STATUS:
		return true;
	default:
		return false;
	}
}

const struct regmap_config bmp280_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
@@ -138,3 +186,15 @@ const struct regmap_config bmp380_regmap_config = {
	.volatile_reg = bmp380_is_volatile_reg,
};
EXPORT_SYMBOL_NS(bmp380_regmap_config, IIO_BMP280);

const struct regmap_config bmp580_regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = BMP580_REG_CMD,
	.cache_type = REGCACHE_RBTREE,

	.writeable_reg = bmp580_is_writeable_reg,
	.volatile_reg = bmp580_is_volatile_reg,
};
EXPORT_SYMBOL_NS(bmp580_regmap_config, IIO_BMP280);
+2 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ static const struct of_device_id bmp280_of_spi_match[] = {
	{ .compatible = "bosch,bmp280", .data = &bmp280_chip_info },
	{ .compatible = "bosch,bme280", .data = &bmp280_chip_info },
	{ .compatible = "bosch,bmp380", .data = &bmp380_chip_info },
	{ .compatible = "bosch,bmp580", .data = &bmp580_chip_info },
	{ },
};
MODULE_DEVICE_TABLE(of, bmp280_of_spi_match);
@@ -95,6 +96,7 @@ static const struct spi_device_id bmp280_spi_id[] = {
	{ "bmp280", (kernel_ulong_t)&bmp280_chip_info },
	{ "bme280", (kernel_ulong_t)&bmp280_chip_info },
	{ "bmp380", (kernel_ulong_t)&bmp380_chip_info },
	{ "bmp580", (kernel_ulong_t)&bmp580_chip_info },
	{ }
};
MODULE_DEVICE_TABLE(spi, bmp280_spi_id);
Loading