From 0b0b772637cd7f35c15830bbb8e8ec66d6c34ddd Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 17:57:59 +0100 Subject: iio: pressure: bmp280: Use chip_info pointers for each chip as driver data Refactor driver I2C and SPI implementations using pointers for each variant's chip_info as the driver data. Adds the regmap configuration to the chip_info struct. Suggested-by: Andy Shevchenko Suggested-by: Jonathan Cameron Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/a48cfa756be48d61dbf656c65daff6e9a1290e6f.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 190 ++++--------------------------------- 1 file changed, 19 insertions(+), 171 deletions(-) (limited to 'drivers/iio/pressure/bmp280-core.c') diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index c0aff78489b4..2c8773db4b73 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -49,65 +49,6 @@ */ enum { AC1, AC2, AC3, AC4, AC5, AC6, B1, B2, MB, MC, MD }; -struct bmp180_calib { - s16 AC1; - s16 AC2; - s16 AC3; - u16 AC4; - u16 AC5; - u16 AC6; - s16 B1; - s16 B2; - s16 MB; - s16 MC; - s16 MD; -}; - -/* See datasheet Section 4.2.2. */ -struct bmp280_calib { - u16 T1; - s16 T2; - s16 T3; - u16 P1; - s16 P2; - s16 P3; - s16 P4; - s16 P5; - s16 P6; - s16 P7; - s16 P8; - s16 P9; - u8 H1; - s16 H2; - u8 H3; - s16 H4; - s16 H5; - s8 H6; -}; - -/* See datasheet Section 3.11.1. */ -struct bmp380_calib { - u16 T1; - u16 T2; - s8 T3; - s16 P1; - s16 P2; - s8 P3; - s8 P4; - u16 P5; - u16 P6; - s8 P7; - s8 P8; - s16 P9; - s8 P10; - s8 P11; -}; - -static const char *const bmp280_supply_names[] = { - "vddd", "vdda" -}; - -#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names) enum bmp380_odr { BMP380_ODR_200HZ, @@ -130,94 +71,6 @@ enum bmp380_odr { BMP380_ODR_0_0015HZ, }; -struct bmp280_data { - struct device *dev; - struct mutex lock; - struct regmap *regmap; - struct completion done; - bool use_eoc; - const struct bmp280_chip_info *chip_info; - union { - struct bmp180_calib bmp180; - struct bmp280_calib bmp280; - struct bmp380_calib bmp380; - } calib; - struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES]; - unsigned int start_up_time; /* in microseconds */ - - /* log of base 2 of oversampling rate */ - u8 oversampling_press; - u8 oversampling_temp; - u8 oversampling_humid; - u8 iir_filter_coeff; - - /* - * BMP380 devices introduce sampling frequency configuration. See - * datasheet sections 3.3.3. and 4.3.19 for more details. - * - * BMx280 devices allowed indirect configuration of sampling frequency - * changing the t_standby duration between measurements, as detailed on - * section 3.6.3 of the datasheet. - */ - int sampling_freq; - - /* - * Carryover value from temperature conversion, used in pressure - * calculation. - */ - s32 t_fine; - - /* - * DMA (thus cache coherency maintenance) may require the - * transfer buffers to live in their own cache lines. - */ - union { - /* Sensor data buffer */ - u8 buf[3]; - /* Calibration data buffers */ - __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2]; - __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2]; - u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT]; - /* Miscellaneous, endianess-aware data buffers */ - __le16 le16; - __be16 be16; - } __aligned(IIO_DMA_MINALIGN); -}; - -struct bmp280_chip_info { - unsigned int id_reg; - - const struct iio_chan_spec *channels; - int num_channels; - unsigned int start_up_time; - - const int *oversampling_temp_avail; - int num_oversampling_temp_avail; - int oversampling_temp_default; - - const int *oversampling_press_avail; - int num_oversampling_press_avail; - int oversampling_press_default; - - const int *oversampling_humid_avail; - int num_oversampling_humid_avail; - int oversampling_humid_default; - - const int *iir_filter_coeffs_avail; - int num_iir_filter_coeffs_avail; - int iir_filter_coeff_default; - - const int (*sampling_freq_avail)[2]; - int num_sampling_freq_avail; - int sampling_freq_default; - - int (*chip_config)(struct bmp280_data *); - int (*read_temp)(struct bmp280_data *, int *); - int (*read_press)(struct bmp280_data *, int *, int *); - int (*read_humid)(struct bmp280_data *, int *, int *); - int (*read_calib)(struct bmp280_data *); -}; - /* * These enums are used for indexing into the array of compensation * parameters for BMP280. @@ -905,8 +758,10 @@ static int bmp280_chip_config(struct bmp280_data *data) static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; -static const struct bmp280_chip_info bmp280_chip_info = { +const struct bmp280_chip_info bmp280_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BMP280_CHIP_ID, + .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 2, @@ -934,6 +789,7 @@ static const struct bmp280_chip_info bmp280_chip_info = { .read_press = bmp280_read_press, .read_calib = bmp280_read_calib, }; +EXPORT_SYMBOL_NS(bmp280_chip_info, IIO_BMP280); static int bme280_chip_config(struct bmp280_data *data) { @@ -953,8 +809,10 @@ static int bme280_chip_config(struct bmp280_data *data) return bmp280_chip_config(data); } -static const struct bmp280_chip_info bme280_chip_info = { +const struct bmp280_chip_info bme280_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BME280_CHIP_ID, + .regmap_config = &bmp280_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 3, @@ -977,6 +835,7 @@ static const struct bmp280_chip_info bme280_chip_info = { .read_humid = bmp280_read_humid, .read_calib = bme280_read_calib, }; +EXPORT_SYMBOL_NS(bme280_chip_info, IIO_BMP280); /* * Helper function to send a command to BMP3XX sensors. @@ -1319,8 +1178,10 @@ static int bmp380_chip_config(struct bmp280_data *data) static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 }; static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128}; -static const struct bmp280_chip_info bmp380_chip_info = { +const struct bmp280_chip_info bmp380_chip_info = { .id_reg = BMP380_REG_ID, + .chip_id = BMP380_CHIP_ID, + .regmap_config = &bmp380_regmap_config, .start_up_time = 2000, .channels = bmp380_channels, .num_channels = 2, @@ -1346,6 +1207,7 @@ static const struct bmp280_chip_info bmp380_chip_info = { .read_press = bmp380_read_press, .read_calib = bmp380_read_calib, }; +EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas) { @@ -1579,8 +1441,10 @@ static int bmp180_chip_config(struct bmp280_data *data) static const int bmp180_oversampling_temp_avail[] = { 1 }; static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 }; -static const struct bmp280_chip_info bmp180_chip_info = { +const struct bmp280_chip_info bmp180_chip_info = { .id_reg = BMP280_REG_ID, + .chip_id = BMP180_CHIP_ID, + .regmap_config = &bmp180_regmap_config, .start_up_time = 2000, .channels = bmp280_channels, .num_channels = 2, @@ -1600,6 +1464,7 @@ static const struct bmp280_chip_info bmp180_chip_info = { .read_press = bmp180_read_press, .read_calib = bmp180_read_calib, }; +EXPORT_SYMBOL_NS(bmp180_chip_info, IIO_BMP280); static irqreturn_t bmp085_eoc_irq(int irq, void *d) { @@ -1661,11 +1526,10 @@ static void bmp280_regulators_disable(void *data) int bmp280_common_probe(struct device *dev, struct regmap *regmap, - unsigned int chip, + const struct bmp280_chip_info *chip_info, const char *name, int irq) { - const struct bmp280_chip_info *chip_info; struct iio_dev *indio_dev; struct bmp280_data *data; struct gpio_desc *gpiod; @@ -1684,22 +1548,6 @@ int bmp280_common_probe(struct device *dev, indio_dev->info = &bmp280_info; indio_dev->modes = INDIO_DIRECT_MODE; - switch (chip) { - case BMP180_CHIP_ID: - chip_info = &bmp180_chip_info; - break; - case BMP280_CHIP_ID: - chip_info = &bmp280_chip_info; - break; - case BME280_CHIP_ID: - chip_info = &bme280_chip_info; - break; - case BMP380_CHIP_ID: - chip_info = &bmp380_chip_info; - break; - default: - return -EINVAL; - } data->chip_info = chip_info; /* Apply initial values from chip info structure */ @@ -1751,9 +1599,9 @@ int bmp280_common_probe(struct device *dev, ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id); if (ret < 0) return ret; - if (chip_id != chip) { + if (chip_id != data->chip_info->chip_id) { dev_err(dev, "bad chip id: expected %x got %x\n", - chip, chip_id); + data->chip_info->chip_id, chip_id); return -EINVAL; } -- cgit From c25ea00fefa4e57a63209769fa3e4903791d05af Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 17:58:00 +0100 Subject: iio: pressure: bmp280: Add preinit callback Adds preinit callback to execute operations on probe before applying initial configuration. Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/fa9513ffad37a6a43b6d46df9d8319ccab6f5870.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/iio/pressure/bmp280-core.c') diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 2c8773db4b73..6467034b1d3e 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1076,6 +1076,12 @@ static const int bmp380_odr_table[][2] = { [BMP380_ODR_0_0015HZ] = {0, 1526}, }; +static int bmp380_preinit(struct bmp280_data *data) +{ + /* BMP3xx requires soft-reset as part of initialization */ + return bmp380_cmd(data, BMP380_CMD_SOFT_RESET); +} + static int bmp380_chip_config(struct bmp280_data *data) { bool change = false, aux; @@ -1206,6 +1212,7 @@ const struct bmp280_chip_info bmp380_chip_info = { .read_temp = bmp380_read_temp, .read_press = bmp380_read_press, .read_calib = bmp380_read_calib, + .preinit = bmp380_preinit, }; EXPORT_SYMBOL_NS(bmp380_chip_info, IIO_BMP280); @@ -1605,11 +1612,11 @@ int bmp280_common_probe(struct device *dev, return -EINVAL; } - /* BMP3xx requires soft-reset as part of initialization */ - if (chip_id == BMP380_CHIP_ID) { - ret = bmp380_cmd(data, BMP380_CMD_SOFT_RESET); - if (ret < 0) - return ret; + if (data->chip_info->preinit) { + ret = data->chip_info->preinit(data); + if (ret) + return dev_err_probe(data->dev, ret, + "error running preinit tasks\n"); } ret = data->chip_info->chip_config(data); -- cgit From 4d545f9649253a7defda89b44a6a2a2b1b377d9d Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 17:58:01 +0100 Subject: iio: pressure: bmp280: Make read calibration callback optional Newer models do not require read the calibration parameters and apply the compensation algorithms in the sensor. Suggested-by: Jonathan Cameron Signed-off-by: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/bb1b95ab3f4e71d3c76543370325c5c9aaa07add.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/iio/pressure/bmp280-core.c') diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 6467034b1d3e..22addaaa5393 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -1631,10 +1631,12 @@ int bmp280_common_probe(struct device *dev, * time once. They will not change. */ - ret = data->chip_info->read_calib(data); - if (ret < 0) - return dev_err_probe(data->dev, ret, - "failed to read calibration coefficients\n"); + if (data->chip_info->read_calib) { + ret = data->chip_info->read_calib(data); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "failed to read calibration coefficients\n"); + } /* * Attempt to grab an optional EOC IRQ - only the BMP085 has this -- cgit From 597dfb2af052f175cdd5caa7b62095c7de1ba29e Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 18:03:04 +0100 Subject: 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: Angel Iglesias Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/f899fceec9b48bc173bd4b7555f0a237fa32d520.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 344 ++++++++++++++++++++++++++++++++++++- 1 file changed, 336 insertions(+), 8 deletions(-) (limited to 'drivers/iio/pressure/bmp280-core.c') diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 22addaaa5393..a7d26d81ec08 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -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, ®); + if (ret) { + dev_err(data->dev, "failed to reestablish comms after reset\n"); + return ret; + } + + ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, ®); + 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, ®); + 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, ®); + 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; -- cgit From accb9d05df394c38db2a1bab416ee25d6078ca5c Mon Sep 17 00:00:00 2001 From: Angel Iglesias Date: Sun, 19 Feb 2023 18:03:06 +0100 Subject: iio: pressure: bmp280: Add nvmem operations for BMP580 The pressure sensor BMP580 contains a non-volatile memory that stores trimming and configuration params. That memory provides an programmable user range of three 2-byte words. Signed-off-by: Angel Iglesias Link: https://lore.kernel.org/r/f3f453d9b2c0f7820ca9c56e24e2165b6c39bb67.1676823250.git.ang.iglesiasg@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/bmp280-core.c | 210 ++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 1 deletion(-) (limited to 'drivers/iio/pressure/bmp280-core.c') diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index a7d26d81ec08..6089f3f9d8f4 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1284,6 +1285,80 @@ static int bmp580_soft_reset(struct bmp280_data *data) return 0; } +/** + * bmp580_nvm_operation() - Helper function to commit NVM memory operations + * @data: sensor data struct + * @is_write: flag to signal write operation + */ +static int bmp580_nvm_operation(struct bmp280_data *data, bool is_write) +{ + unsigned long timeout, poll; + unsigned int reg; + int ret; + + /* Check NVM ready flag */ + ret = regmap_read(data->regmap, BMP580_REG_STATUS, ®); + if (ret) { + dev_err(data->dev, "failed to check nvm status\n"); + return ret; + } + if (!(reg & BMP580_STATUS_NVM_RDY_MASK)) { + dev_err(data->dev, "sensor's nvm is not ready\n"); + return -EIO; + } + + /* Start NVM operation sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, BMP580_CMD_NVM_OP_SEQ_0); + if (ret) { + dev_err(data->dev, "failed to send nvm operation's first sequence\n"); + return ret; + } + if (is_write) { + /* Send NVM write sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, + BMP580_CMD_NVM_WRITE_SEQ_1); + if (ret) { + dev_err(data->dev, "failed to send nvm write sequence\n"); + return ret; + } + /* Datasheet says on 4.8.1.2 it takes approximately 10ms */ + poll = 2000; + timeout = 12000; + } else { + /* Send NVM read sequence */ + ret = regmap_write(data->regmap, BMP580_REG_CMD, + BMP580_CMD_NVM_READ_SEQ_1); + if (ret) { + dev_err(data->dev, "failed to send nvm read sequence\n"); + return ret; + } + /* Datasheet says on 4.8.1.1 it takes approximately 200us */ + poll = 50; + timeout = 400; + } + if (ret) { + dev_err(data->dev, "failed to write command sequence\n"); + return -EIO; + } + + /* Wait until NVM is ready again */ + ret = regmap_read_poll_timeout(data->regmap, BMP580_REG_STATUS, reg, + (reg & BMP580_STATUS_NVM_RDY_MASK), + poll, timeout); + if (ret) { + dev_err(data->dev, "error checking nvm operation status\n"); + return ret; + } + + /* Check NVM error flags */ + if ((reg & BMP580_STATUS_NVM_ERR_MASK) || (reg & BMP580_STATUS_NVM_CMD_ERR_MASK)) { + dev_err(data->dev, "error processing nvm operation\n"); + return -EIO; + } + + 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 @@ -1379,8 +1454,140 @@ static const int bmp580_odr_table[][2] = { [BMP580_ODR_0_125HZ] = {0, 125000}, }; +static const int bmp580_nvmem_addrs[] = { 0x20, 0x21, 0x22 }; + +static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct bmp280_data *data = priv; + u16 *dst = val; + int ret, addr; + + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + + /* Set 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"); + goto exit; + } + /* Wait standby transition time */ + usleep_range(2500, 3000); + + while (bytes >= sizeof(*dst)) { + addr = bmp580_nvmem_addrs[offset / sizeof(*dst)]; + + ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR, + FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr)); + if (ret) { + dev_err(data->dev, "error writing nvm address\n"); + goto exit; + } + + ret = bmp580_nvm_operation(data, false); + if (ret) + goto exit; + + ret = regmap_bulk_read(data->regmap, BMP580_REG_NVM_DATA_LSB, &data->le16, + sizeof(data->le16)); + if (ret) { + dev_err(data->dev, "error reading nvm data regs\n"); + goto exit; + } + + *dst++ = le16_to_cpu(data->le16); + bytes -= sizeof(*dst); + offset += sizeof(*dst); + } +exit: + /* Restore chip config */ + data->chip_info->chip_config(data); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + return ret; +} + +static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct bmp280_data *data = priv; + u16 *buf = val; + int ret, addr; + + pm_runtime_get_sync(data->dev); + mutex_lock(&data->lock); + + /* Set 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"); + goto exit; + } + /* Wait standby transition time */ + usleep_range(2500, 3000); + + while (bytes >= sizeof(*buf)) { + addr = bmp580_nvmem_addrs[offset / sizeof(*buf)]; + + ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR, BMP580_NVM_PROG_EN | + FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr)); + if (ret) { + dev_err(data->dev, "error writing nvm address\n"); + goto exit; + } + data->le16 = cpu_to_le16(*buf++); + + ret = regmap_bulk_write(data->regmap, BMP580_REG_NVM_DATA_LSB, &data->le16, + sizeof(data->le16)); + if (ret) { + dev_err(data->dev, "error writing LSB NVM data regs\n"); + goto exit; + } + + ret = bmp580_nvm_operation(data, true); + if (ret) + goto exit; + + /* Disable programming mode bit */ + ret = regmap_update_bits(data->regmap, BMP580_REG_NVM_ADDR, + BMP580_NVM_PROG_EN, 0); + if (ret) { + dev_err(data->dev, "error resetting nvm write\n"); + goto exit; + } + + bytes -= sizeof(*buf); + offset += sizeof(*buf); + } +exit: + /* Restore chip config */ + data->chip_info->chip_config(data); + mutex_unlock(&data->lock); + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + return ret; +} + static int bmp580_preinit(struct bmp280_data *data) { + struct nvmem_config config = { + .dev = data->dev, + .priv = data, + .name = "bmp580_nvmem", + .word_size = sizeof(u16), + .stride = sizeof(u16), + .size = 3 * sizeof(u16), + .reg_read = bmp580_nvmem_read, + .reg_write = bmp580_nvmem_write, + }; unsigned int reg; int ret; @@ -1408,7 +1615,8 @@ static int bmp580_preinit(struct bmp280_data *data) return -EIO; } - return 0; + /* Register nvmem device */ + return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config)); } static int bmp580_chip_config(struct bmp280_data *data) -- cgit