--- zzzz-none-000/linux-5.4.213/drivers/iio/pressure/bmp280-core.c 2022-09-15 10:04:56.000000000 +0000 +++ alder-5690pro-762/linux-5.4.213/drivers/iio/pressure/bmp280-core.c 2024-08-14 09:01:58.000000000 +0000 @@ -6,15 +6,16 @@ * Copyright (c) 2014 Intel Corporation * Copyright (c) 2016 Linus Walleij * - * Driver for Bosch Sensortec BMP180 and BMP280 digital pressure sensor. + * Driver for Bosch Sensortec BMP180, BMP280 and BMP390 digital pressure sensor. * * Datasheet: * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf */ -#define pr_fmt(fmt) "bmp280: " fmt +#define pr_fmt(fmt) "bmp390: " fmt #include #include @@ -74,6 +75,24 @@ s8 H6; }; +/* See datasheet Section 3.11.1 */ +struct bmp390_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; +}; + struct bmp280_data { struct device *dev; struct mutex lock; @@ -84,6 +103,7 @@ union { struct bmp180_calib bmp180; struct bmp280_calib bmp280; + struct bmp390_calib bmp390; } calib; struct regulator *vddd; struct regulator *vdda; @@ -98,7 +118,7 @@ * Carryover value from temperature conversion, used in pressure * calculation. */ - s32 t_fine; + s64 t_fine; }; struct bmp280_chip_info { @@ -112,7 +132,7 @@ int num_oversampling_humid_avail; int (*chip_config)(struct bmp280_data *); - int (*read_temp)(struct bmp280_data *, int *); + int (*read_temp)(struct bmp280_data *, int *, int *); int (*read_press)(struct bmp280_data *, int *, int *); int (*read_humid)(struct bmp280_data *, int *, int *); }; @@ -326,7 +346,7 @@ } static int bmp280_read_temp(struct bmp280_data *data, - int *val) + int *val, int *val2) { int ret; __be32 tmp = 0; @@ -368,7 +388,7 @@ u32 comp_press; /* 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; @@ -401,7 +421,7 @@ u32 comp_humidity; /* 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; @@ -445,7 +465,7 @@ 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; @@ -653,6 +673,7 @@ } static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 }; +static const int bmp390_oversampling_avail[] = { 1, 2, 4, 8, 16, 32}; static const struct bmp280_chip_info bmp280_chip_info = { .oversampling_temp_avail = bmp280_oversampling_avail, @@ -818,7 +839,7 @@ 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) { int ret; s32 adc_temp, comp_temp; @@ -903,7 +924,7 @@ u32 comp_press; /* 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; @@ -984,6 +1005,322 @@ return 0; } +/* + * This API is used to parse the calibration data, compensate + * it and store it in device structure + */ +static int bmp390_read_calib(struct bmp280_data *data, struct bmp390_calib *calib) +{ + int ret; + u8 t_buf[BMP390_TEMP_CALIB_DATA_LEN]; + u8 p_buf[BMP390_PRESS_CALIB_DATA_LEN]; + + /* Read temperature calibration values. */ + ret = regmap_bulk_read(data->regmap, BMP390_REG_COMP_TEMP_START, + t_buf, BMP390_TEMP_CALIB_DATA_LEN); + if (ret < 0) { + dev_err(data->dev, + "failed to read temperature calibration parameters\n"); + return ret; + } + + /* Toss the temperature calibration data into the entropy pool */ + add_device_randomness(t_buf, sizeof(t_buf)); + + calib->T1 = BMP390_CONCAT_BYTES(t_buf[1], t_buf[0]); + calib->T2 = BMP390_CONCAT_BYTES(t_buf[3], t_buf[2]); + calib->T3 = t_buf[4]; + + /* Read pressure calibration values. */ + ret = regmap_bulk_read(data->regmap, BMP390_REG_COMP_PRESS_START, + p_buf, BMP390_PRESS_CALIB_DATA_LEN); + if (ret < 0) { + dev_err(data->dev, + "failed to read pressure calibration parameters\n"); + return ret; + } + + /* Toss the pressure calibration data into the entropy pool */ + add_device_randomness(p_buf, sizeof(p_buf)); + + calib->P1 = BMP390_CONCAT_BYTES(p_buf[1], p_buf[0]); + calib->P2 = BMP390_CONCAT_BYTES(p_buf[3], p_buf[2]); + calib->P3 = p_buf[4]; + calib->P4 = p_buf[5]; + calib->P5 = BMP390_CONCAT_BYTES(p_buf[7], p_buf[6]); + calib->P6 = BMP390_CONCAT_BYTES(p_buf[9], p_buf[8]); + calib->P7 = p_buf[10]; + calib->P8 = p_buf[11]; + calib->P9 = BMP390_CONCAT_BYTES(p_buf[13], p_buf[12]); + calib->P10 = p_buf[14]; + calib->P11 = p_buf[15]; + + return 0; +} + +/* This API is used to compensate the raw temperature data and + * return the compensated data in integer data type. + * The Actual temperature is obtained by dividing the value by 100. + * For eg : If returned temperature is 2426 then it is 2426/100 = 24 deg Celsius + */ +static s64 bmp390_compensate_temp(struct bmp280_data *data, + u32 uncomp_temp) +{ + u64 partial_data1, partial_data2, partial_data3; + s64 partial_data4, partial_data5, partial_data6; + s64 comp_temp; + struct bmp390_calib *calib = &data->calib.bmp390; + + partial_data1 = (u64)(uncomp_temp - (256 * (u64)(calib->T1))); + partial_data2 = (u64)(calib->T2 * partial_data1); + partial_data3 = (u64)(partial_data1 * partial_data1); + partial_data4 = (s64)(((s64)partial_data3) * ((s64)calib->T3)); + partial_data5 = ((s64)(((s64)partial_data2) * 262144) + (s64)partial_data4); + partial_data6 = (s64)(((s64)partial_data5) / 4294967296U); + + /*Update the compensated temperature in calib structure since this is needed + * for pressure calculation */ + data->t_fine = partial_data6; + comp_temp = (s64)((partial_data6 * 25) / 16384); + + /*Return compensated temperature */ + return comp_temp; +} + +/* This API is used to compensate the raw pressure data and + * return the compensated pressure data in integer data type. + * for eg if the returned pressure value is 9528709 which is 9528709/100 = 95287.09 Pascal or 952.87hPa + */ +static u64 bmp390_compensate_press(struct bmp280_data *data, + u32 uncomp_press) +{ + /* Variable to hold the compensated pressure */ + u64 comp_press; + /* Temporary variables used for compensation */ + s64 partial_data1, partial_data2, partial_data3, partial_data4, partial_data5, partial_data6, offset, sensitivity; + struct bmp390_calib *calib = &data->calib.bmp390; + + partial_data1 = data->t_fine * data->t_fine; + partial_data2 = partial_data1 / 64; + partial_data3 = (partial_data2 * data->t_fine) / 256; + partial_data4 = (calib->P8 * partial_data3) / 32; + partial_data5 = (calib->P7 * partial_data1) * 16; + partial_data6 = (calib->P6 * data->t_fine) * 4194304; + offset = (s64)((s64)(calib->P5) * (s64)140737488355328U) + partial_data4 + partial_data5 + partial_data6; + partial_data2 = (((s64)calib->P4) * partial_data3) / 32; + partial_data4 = (calib->P3 * partial_data1) * 4; + partial_data5 = ((s64)(calib->P2) - 16384) * ((s64)data->t_fine) * 2097152; + sensitivity = (((s64)(calib->P1) - 16384) * (s64)70368744177664U) + partial_data2 + partial_data4 + partial_data5; + partial_data1 = (sensitivity / 16777216) * uncomp_press; + partial_data2 = (s64)(calib->P10) * (s64)data->t_fine; + partial_data3 = partial_data2 + (65536 * (s64)(calib->P9)); + partial_data4 = (partial_data3 * uncomp_press) / 8192; + partial_data5 = (uncomp_press * partial_data4) / 512; + partial_data6 = (s64)((u64)uncomp_press * (u64)uncomp_press); + partial_data2 = ((s64)(calib->P11) * (s64)(partial_data6)) / 65536; + partial_data3 = (partial_data2 * uncomp_press) / 128; + partial_data4 = (offset / 4) + partial_data1 + partial_data5 + partial_data3; + comp_press = (((u64)partial_data4 * 25) / (u64)1099511627776U); + + return comp_press; +} + +/* This API checks for the temperature data ready status, reads the raw temperature, compensates it + * and updates the *val argument + */ +static int bmp390_read_temp(struct bmp280_data *data, + int *val, int *val2) +{ + int ret; + u8 tmp[3]; + u32 adc_temp; + u64 comp_temp; + unsigned int cmd_rdy_status; + + ret = regmap_read(data->regmap, BMP390_REG_STATUS, &cmd_rdy_status); + if (ret < 0) { + dev_err(data->dev, "Failed to read status register\n"); + return ret; + } + /* Check if the temperature data is ready before reading the temperature */ + if((cmd_rdy_status & BMP390_TEMP_DRDY) != 0) { + ret = regmap_bulk_read(data->regmap, BMP390_REG_TEMP_XLSB, + tmp, 3); + if (ret < 0) { + dev_err(data->dev, "failed to read temperature\n"); + return ret; + } + adc_temp = (u32)tmp[2] << 16 | (u32)tmp[1] << 8 | tmp[0]; + + comp_temp = bmp390_compensate_temp(data, adc_temp); + + /* + * val might be NULL if we're called by the read_press routine, + * who only cares about the carry over t_fine value. + * */ + if (val) { + *val = comp_temp; + *val2 = 100; + return IIO_VAL_FRACTIONAL; + } + } + else { + dev_err(data->dev, "Temperature data not ready \n"); + } + + return 0; +} + +/* This API checks for the temperature/pressure data ready status and reads + * the raw data, compensates and updates the compensated pressure in the *val argument + */ +static int bmp390_read_press(struct bmp280_data *data, + int *val, int *val2) +{ + int ret; + u8 tmp[3]; + u32 adc_press; + u64 comp_press; + unsigned int cmd_rdy_status; + + ret = regmap_read(data->regmap, BMP390_REG_STATUS, &cmd_rdy_status); + if (ret < 0) { + dev_err(data->dev, "Failed to read status register\n"); + return ret; + } + /* Check if the temperature and pressure data are ready before it is being read */ + if (((cmd_rdy_status & BMP390_TEMP_DRDY) != 0) && ((cmd_rdy_status & BMP390_PRESS_DRDY) != 0)) { + + /* Read and compensate temperature so we get a reading of t_fine used in pressure compensation. */ + ret = bmp390_read_temp(data, NULL, NULL); + if (ret < 0) + return ret; + ret = regmap_bulk_read(data->regmap, BMP390_REG_PRESS_XLSB, + tmp, 3); + if (ret < 0) { + dev_err(data->dev, "failed to read pressure\n"); + return ret; + } + + adc_press = (u32)tmp[2] << 16 | (u32)tmp[1] << 8 | tmp[0]; + + comp_press = bmp390_compensate_press(data, adc_press); + *val = comp_press; + *val2 = 10000; + + return IIO_VAL_FRACTIONAL; + } + else { + dev_err(data->dev, "Pressure data not ready..\n"); + } + + return 0; +} +/* This API is used to configure the power control(pressure enable, + * temperature enable, power mode), over sampling, ODR and filter + * settings in the sensor. + */ +static int bmp390_chip_config(struct bmp280_data *data) +{ + int ret; + u8 osrs = BMP390_OSR_TEMP_X(data->oversampling_temp) | + BMP390_OSR_PRESS_X(data->oversampling_press); + + /* Updating the pressure and temperature oversampling values */ + ret = regmap_write_bits(data->regmap, BMP390_REG_OSR, + BMP390_OSR_TEMP_MASK | + BMP390_OSR_PRESS_MASK, + osrs); + if (ret < 0) { + dev_err(data->dev, + "failed to write OSR register\n"); + return ret; + } + /* Updating the IIR filter coefficient */ + ret = regmap_update_bits(data->regmap, BMP390_REG_CONFIG, + BMP390_FILTER_MASK, + BMP390_FILTER_COEFF_3); + if (ret < 0) { + dev_err(data->dev, + "failed to write config register\n"); + return ret; + } + /* Configuring the Output Data rate */ + ret = regmap_write(data->regmap, BMP390_REG_ODR, BMP390_ODR_25Hz); + if (ret < 0) { + dev_err(data->dev, + "failed to write ODR register\n"); + return ret; + } + + /* Enabling pressure and temperature measurement, setting mode to normal */ + ret = regmap_write_bits(data->regmap, BMP390_REG_PWR_CTRL, + BMP390_PRESS_EN_MASK | + BMP390_TEMP_EN_MASK | + BMP390_MODE_MASK, + BMP390_PRESS_EN | BMP390_TEMP_EN | BMP390_MODE_NORMAL); + + if (ret < 0) { + dev_err(data->dev, + "failed to write pwr ctrl register\n"); + return ret; + } + + return 0; +} +/* This API is used to perform soft reset of the sensor to reset all the user configuration + * settings to default + */ +static int bmp390_soft_reset(struct bmp280_data *data) +{ + int ret; + unsigned int cmd_rdy_status, cmd_err_status; + + /* Checking for command ready status */ + ret = regmap_read(data->regmap, BMP390_REG_STATUS, &cmd_rdy_status); + if (ret < 0) { + dev_err(data->dev, "Failed to read status register\n"); + return ret; + } + /* Device is ready to accept a new command */ + if (cmd_rdy_status & BMP390_CMD_READY) { + + /* Trigger a soft reset to reset all user configuration settings to default */ + ret = regmap_write(data->regmap, BMP390_REG_CMD, BMP390_SOFT_RESET_VAL); + if (ret < 0) { + dev_err(data->dev, "Soft reset failed!\n"); + return ret; + } + /* Wait for 2ms */ + usleep_range(data->start_up_time, data->start_up_time + 100); + + /* Read for Command error status */ + ret = regmap_read(data->regmap, BMP390_REG_ERR, &cmd_err_status); + if (ret < 0) { + dev_err(data->dev, "Failed to read Error status\n"); + return ret; + } + + if (cmd_err_status & BMP390_CMD_ERR) { + dev_err(data->dev, "Sensor error reported: %d\n", cmd_err_status); + return ret; + } + } + + return 0; +} +static const struct bmp280_chip_info bmp390_chip_info = { + .oversampling_temp_avail = bmp390_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp390_oversampling_avail), + + .oversampling_press_avail = bmp390_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp390_oversampling_avail), + + .chip_config = bmp390_chip_config, + .read_temp = bmp390_read_temp, + .read_press = bmp390_read_press, +}; + int bmp280_common_probe(struct device *dev, struct regmap *regmap, unsigned int chip, @@ -993,7 +1330,7 @@ int ret; struct iio_dev *indio_dev; struct bmp280_data *data; - unsigned int chip_id; + unsigned int chip_id, rev_id; struct gpio_desc *gpiod; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); @@ -1033,6 +1370,13 @@ data->oversampling_temp = ilog2(2); data->start_up_time = 2000; break; + case BMP390_CHIP_ID: + indio_dev->num_channels = 2; + data->chip_info = &bmp390_chip_info; + data->oversampling_press = ilog2(16); + data->oversampling_temp = ilog2(2); + data->start_up_time = 2000; + break; default: return -EINVAL; } @@ -1071,7 +1415,22 @@ } data->regmap = regmap; - ret = regmap_read(regmap, BMP280_REG_ID, &chip_id); + if (chip == BMP280_CHIP_ID) + ret = regmap_read(regmap, BMP280_REG_ID, &chip_id); + else if (chip == BMP390_CHIP_ID) { + ret = regmap_read(regmap, BMP390_REG_ID, &chip_id); + if (ret < 0) + goto out_disable_vdda; + ret = regmap_read(regmap, BMP390_REG_REV, &rev_id); + if (ret < 0) + goto out_disable_vdda; + pr_info("CHIP_ID: %x, REV_ID: %x\n", chip_id, rev_id); + /* Concatenating the chip identification code and mask revision of the ASIC + * as BME280 and BMP390 share the same chip id (0x60)*/ + chip_id = ((chip_id << 8) | rev_id); + /* Trigger soft reset */ + ret = bmp390_soft_reset(data); + } if (ret < 0) goto out_disable_vdda; if (chip_id != chip) { @@ -1106,8 +1465,14 @@ "failed to read calibration coefficients\n"); goto out_disable_vdda; } + } else if (chip_id == BMP390_CHIP_ID) { + ret = bmp390_read_calib(data, &data->calib.bmp390); + if (ret < 0) { + dev_err(data->dev, + "failed to read calibration coefficients\n"); + goto out_disable_vdda; + } } - /* * Attempt to grab an optional EOC IRQ - only the BMP085 has this * however as it happens, the BMP085 shares the chip ID of BMP180