summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorbrad <brad@NetBSD.org>2022-11-21 21:24:00 +0000
committerbrad <brad@NetBSD.org>2022-11-21 21:24:00 +0000
commitb73c2b020a28458ab92fe6428cb92c7451afecfb (patch)
tree0a8c0e0ba0d74f7e2f69cb7871d82927909c78e6 /sys/dev
parent82cdf4ad60ef21c4f59b923db2ed78d15d3a4719 (diff)
A driver for the Bosch BMP280 / BME280 temperature, humidity and
atmospheric pressure sensor. This is an inexpensive to moderately expensive chip available from a large number of places. The driver supports all aspects of the two chips, except for the repeating read mode which would allow for sub-second queries, such as fall detection or perhaps even as an altimeter. This driver also only supports the I2C interface and not the SPI interface. The BME280, the one with humidity, is not fully tested at this point, awaiting upon a breakout board and may not show proper humidity.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/i2c/bmx280.c1074
-rw-r--r--sys/dev/i2c/bmx280reg.h96
-rw-r--r--sys/dev/i2c/bmx280var.h85
-rw-r--r--sys/dev/i2c/files.i2c7
-rw-r--r--sys/dev/sysmon/sysmon_envsys_tables.c5
5 files changed, 1264 insertions, 3 deletions
diff --git a/sys/dev/i2c/bmx280.c b/sys/dev/i2c/bmx280.c
new file mode 100644
index 00000000000..a9a29710a83
--- /dev/null
+++ b/sys/dev/i2c/bmx280.c
@@ -0,0 +1,1074 @@
+/* $NetBSD: bmx280.c,v 1.1 2022/11/21 21:24:01 brad Exp $ */
+
+/*
+ * Copyright (c) 2022 Brad Spencer <brad@anduin.eldar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bmx280.c,v 1.1 2022/11/21 21:24:01 brad Exp $");
+
+/*
+ Driver for the Bosch BMP280/BME280 temperature, humidity (sometimes) and
+ (usually barometric) pressure sensor
+*/
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/mutex.h>
+
+#include <dev/sysmon/sysmonvar.h>
+#include <dev/i2c/i2cvar.h>
+#include <dev/i2c/bmx280reg.h>
+#include <dev/i2c/bmx280var.h>
+
+
+static int bmx280_write_register(i2c_tag_t, i2c_addr_t,
+ uint8_t *, size_t);
+static int bmx280_read_register(i2c_tag_t, i2c_addr_t, uint8_t *,
+ uint8_t *, size_t);
+static void bmx280_store_raw_blob_tp(struct bmx280_sc *, uint8_t *);
+static void bmx280_store_raw_blob_h(struct bmx280_sc *, uint8_t *);
+static int bmx280_poke(i2c_tag_t, i2c_addr_t, bool);
+static int bmx280_match(device_t, cfdata_t, void *);
+static void bmx280_attach(device_t, device_t, void *);
+static int bmx280_detach(device_t, int);
+static void bmx280_refresh(struct sysmon_envsys *, envsys_data_t *);
+static int bmx280_verify_sysctl(SYSCTLFN_ARGS);
+static int bmx280_verify_sysctl_osrs(SYSCTLFN_ARGS);
+static int bmx280_verify_sysctl_irr(SYSCTLFN_ARGS);
+
+#define BMX280_DEBUG
+#ifdef BMX280_DEBUG
+#define DPRINTF(s, l, x) \
+ do { \
+ if (l <= s->sc_bmx280debug) \
+ printf x; \
+ } while (/*CONSTCOND*/0)
+#else
+#define DPRINTF(s, l, x)
+#endif
+
+CFATTACH_DECL_NEW(bmx280thp, sizeof(struct bmx280_sc),
+ bmx280_match, bmx280_attach, bmx280_detach, NULL);
+
+static struct bmx280_sensor bmx280_sensors[] = {
+ {
+ .desc = "temperature",
+ .type = ENVSYS_STEMP,
+ },
+ {
+ .desc = "pressure",
+ .type = ENVSYS_PRESSURE,
+ },
+ {
+ .desc = "humidity",
+ .type = ENVSYS_SRELHUMIDITY,
+ }
+};
+
+static struct bmx280_osrs_list bmx280_osrs[] = {
+ {
+ .text = 1,
+ .mask = BMX280_OSRS_TP_VALUE_X1,
+ },
+ {
+ .text = 2,
+ .mask = BMX280_OSRS_TP_VALUE_X2,
+ },
+ {
+ .text = 4,
+ .mask = BMX280_OSRS_TP_VALUE_X4,
+ },
+ {
+ .text = 8,
+ .mask = BMX280_OSRS_TP_VALUE_X8,
+ },
+ {
+ .text = 16,
+ .mask = BMX280_OSRS_TP_VALUE_X16,
+ }
+};
+
+static struct bmx280_irr_list bmx280_irr[] = {
+ {
+ .text = 1,
+ .mask = BMX280_FILTER_VALUE_OFF,
+ },
+ {
+ .text = 2,
+ .mask = BMX280_FILTER_VALUE_2,
+ },
+ {
+ .text = 5,
+ .mask = BMX280_FILTER_VALUE_5,
+ },
+ {
+ .text = 11,
+ .mask = BMX280_FILTER_VALUE_11,
+ },
+ {
+ .text = 22,
+ .mask = BMX280_FILTER_VALUE_22,
+ }
+};
+
+static uint8_t
+bmx280_osrs_text_to_mask(int t)
+{
+ int i;
+ uint8_t m = 0;
+
+ for (i = 0; i < __arraycount(bmx280_osrs); i++) {
+ if (t == bmx280_osrs[i].text) {
+ m = bmx280_osrs[i].mask;
+ break;
+ }
+ }
+
+ return m;
+}
+
+static uint8_t
+bmx280_irr_text_to_mask(int t)
+{
+ int i;
+ uint8_t m = 0;
+
+ for (i = 0; i < __arraycount(bmx280_irr); i++) {
+ if (t == bmx280_irr[i].text) {
+ m = bmx280_irr[i].mask;
+ break;
+ }
+ }
+
+ return m;
+}
+
+int
+bmx280_verify_sysctl(SYSCTLFN_ARGS)
+{
+ int error, t;
+ struct sysctlnode node;
+
+ node = *rnode;
+ t = *(int *)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ if (t < 0)
+ return EINVAL;
+
+ *(int *)rnode->sysctl_data = t;
+
+ return 0;
+}
+
+int
+bmx280_verify_sysctl_osrs(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node;
+ int error = 0, t;
+ size_t i;
+
+ node = *rnode;
+ t = *(int *)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ for (i = 0; i < __arraycount(bmx280_osrs); i++) {
+ if (t == bmx280_osrs[i].text) {
+ break;
+ }
+ }
+
+ if (i == __arraycount(bmx280_osrs))
+ return EINVAL;
+
+ *(int *)rnode->sysctl_data = t;
+
+ return error;
+}
+
+int
+bmx280_verify_sysctl_irr(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node;
+ int error = 0, t;
+ size_t i;
+
+ node = *rnode;
+ t = *(int *)rnode->sysctl_data;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ for (i = 0; i < __arraycount(bmx280_irr); i++) {
+ if (t == bmx280_irr[i].text) {
+ break;
+ }
+ }
+
+ if (i == __arraycount(bmx280_irr))
+ return EINVAL;
+
+ *(int *)rnode->sysctl_data = t;
+
+ return error;
+}
+
+/* The datasheet was pretty vague as to the byte order...
+ * in fact, down right deceptive...
+ */
+
+static void
+bmx280_store_raw_blob_tp(struct bmx280_sc *sc, uint8_t *b) {
+ sc->sc_cal_blob.dig_T1 = (uint16_t)b[1] << 8;
+ sc->sc_cal_blob.dig_T1 = sc->sc_cal_blob.dig_T1 | (uint16_t)b[0];
+ sc->sc_cal_blob.dig_T2 = (int16_t)b[3] << 8;
+ sc->sc_cal_blob.dig_T2 = sc->sc_cal_blob.dig_T2 | (int16_t)b[2];
+ sc->sc_cal_blob.dig_T3 = (int16_t)b[5] << 8;
+ sc->sc_cal_blob.dig_T3 = sc->sc_cal_blob.dig_T3 | (int16_t)b[4];
+
+ sc->sc_cal_blob.dig_P1 = (uint16_t)b[7] << 8;
+ sc->sc_cal_blob.dig_P1 = sc->sc_cal_blob.dig_P1 | (uint16_t)b[6];
+ sc->sc_cal_blob.dig_P2 = (int16_t)b[9] << 8;
+ sc->sc_cal_blob.dig_P2 = sc->sc_cal_blob.dig_P2 | (int16_t)b[8];
+ sc->sc_cal_blob.dig_P3 = (int16_t)b[11] << 8;
+ sc->sc_cal_blob.dig_P3 = sc->sc_cal_blob.dig_P3 | (int16_t)b[10];
+ sc->sc_cal_blob.dig_P4 = (int16_t)b[13] << 8;
+ sc->sc_cal_blob.dig_P4 = sc->sc_cal_blob.dig_P4 | (int16_t)b[12];
+ sc->sc_cal_blob.dig_P5 = (int16_t)b[15] << 8;
+ sc->sc_cal_blob.dig_P5 = sc->sc_cal_blob.dig_P5 | (int16_t)b[14];
+ sc->sc_cal_blob.dig_P6 = (int16_t)b[17] << 8;
+ sc->sc_cal_blob.dig_P6 = sc->sc_cal_blob.dig_P6 | (int16_t)b[16];
+ sc->sc_cal_blob.dig_P7 = (int16_t)b[19] << 8;
+ sc->sc_cal_blob.dig_P7 = sc->sc_cal_blob.dig_P7 | (int16_t)b[18];
+ sc->sc_cal_blob.dig_P8 = (int16_t)b[21] << 8;
+ sc->sc_cal_blob.dig_P8 = sc->sc_cal_blob.dig_P8 | (int16_t)b[20];
+ sc->sc_cal_blob.dig_P9 = (int16_t)b[23] << 8;
+ sc->sc_cal_blob.dig_P9 = sc->sc_cal_blob.dig_P9 | (int16_t)b[22];
+}
+
+static void
+bmx280_store_raw_blob_h(struct bmx280_sc *sc, uint8_t *b) {
+ sc->sc_cal_blob.dig_H1 = (uint8_t)b[0];
+ sc->sc_cal_blob.dig_H2 = (int16_t)b[2] << 8;
+ sc->sc_cal_blob.dig_H2 = sc->sc_cal_blob.dig_H2 | (int16_t)b[1];
+ sc->sc_cal_blob.dig_H3 = (uint8_t)b[3];
+ sc->sc_cal_blob.dig_H4 = ((int16_t)b[5] & 0x000f) << 8;
+ sc->sc_cal_blob.dig_H4 = sc->sc_cal_blob.dig_H4 | (int16_t)b[4];
+ sc->sc_cal_blob.dig_H5 = (int16_t)b[6] << 4;
+ sc->sc_cal_blob.dig_H5 = sc->sc_cal_blob.dig_H5 | (((int16_t)b[5] & 0x00f0) >> 4);
+ sc->sc_cal_blob.dig_H6 = (int8_t)b[7];
+}
+
+/* For the BMX280, a write consists of sending a I2C START, I2C SLAVE
+ * address and then pairs of registers and data until a I2C STOP is
+ * sent.
+ */
+
+static int
+bmx280_write_register(i2c_tag_t tag, i2c_addr_t addr,
+ uint8_t *buf, size_t blen)
+{
+ int error;
+
+ KASSERT(blen > 0);
+ /* XXX - there should be a KASSERT for blen at least
+ being an even number */
+
+ error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,NULL,0,
+ buf,blen,0);
+
+ return error;
+}
+
+/* For the BMX280, a read consists of writing on the I2C bus
+ * a I2C START, I2C SLAVE address, then the starting register.
+ * If that works, then following will be another I2C START,
+ * I2C SLAVE address, followed by as many I2C reads that is
+ * desired and then a I2C STOP
+ */
+
+static int
+bmx280_read_register(i2c_tag_t tag, i2c_addr_t addr, uint8_t *reg,
+ uint8_t *buf, size_t blen)
+{
+ int error;
+
+ KASSERT(blen > 0);
+
+ error = iic_exec(tag,I2C_OP_WRITE,addr,reg,1,NULL,0,0);
+
+ if (error == 0) {
+ error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,
+ buf,blen,0);
+ }
+
+ return error;
+}
+
+
+static int
+bmx280_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
+{
+ uint8_t reg = BMX280_REGISTER_ID;
+ uint8_t buf[1];
+ int error;
+
+ error = bmx280_read_register(tag, addr, &reg, buf, 1);
+ if (matchdebug) {
+ printf("poke X 1: %d\n", error);
+ }
+ return error;
+}
+
+static int
+bmx280_sysctl_init(struct bmx280_sc *sc)
+{
+ int error;
+ const struct sysctlnode *cnode;
+ int sysctlroot_num;
+
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ 0, CTLTYPE_NODE, device_xname(sc->sc_dev),
+ SYSCTL_DESCR("bmx280 controls"), NULL, 0, NULL, 0, CTL_HW,
+ CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ sysctlroot_num = cnode->sysctl_num;
+
+#ifdef BMX280_DEBUG
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
+ SYSCTL_DESCR("Debug level"), bmx280_verify_sysctl, 0,
+ &sc->sc_bmx280debug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
+ CTL_EOL)) != 0)
+ return error;
+
+ /* It would be nice to have a CTLTYPE_SHORT */
+
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_BOOL, "dump_calibration",
+ SYSCTL_DESCR("Dumps the calibration values to the console"),
+ bmx280_verify_sysctl, 0,
+ &sc->sc_bmx280dump, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
+ CTL_EOL)) != 0)
+ return error;
+#endif
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
+ SYSCTL_DESCR("Read attempts"), bmx280_verify_sysctl, 0,
+ &sc->sc_readattempts, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
+ CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "osrs_t",
+ SYSCTL_DESCR("Temperature oversample"),
+ bmx280_verify_sysctl_osrs, 0, &sc->sc_osrs_t,
+ 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "osrs_p",
+ SYSCTL_DESCR("Pressure oversample"),
+ bmx280_verify_sysctl_osrs, 0, &sc->sc_osrs_p,
+ 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ if (sc->sc_has_humidity) {
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "osrs_h",
+ SYSCTL_DESCR("Humidity oversample"),
+ bmx280_verify_sysctl_osrs, 0, &sc->sc_osrs_h,
+ 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+ }
+
+ if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "irr_samples",
+ SYSCTL_DESCR("IRR samples"),
+ bmx280_verify_sysctl_irr, 0, &sc->sc_irr_samples,
+ 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
+ return error;
+
+ return 0;
+}
+
+static int
+bmx280_match(device_t parent, cfdata_t match, void *aux)
+{
+ struct i2c_attach_args *ia = aux;
+ int error, match_result;
+ const bool matchdebug = false;
+
+ if (iic_use_direct_match(ia, match, NULL, &match_result))
+ return match_result;
+
+ /* indirect config - check for configured address */
+ if (ia->ia_addr != BMX280_TYPICAL_ADDR_1 &&
+ ia->ia_addr != BMX280_TYPICAL_ADDR_2)
+ return 0;
+
+ /*
+ * Check to see if something is really at this i2c address. This will
+ * keep phantom devices from appearing
+ */
+ if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
+ if (matchdebug)
+ printf("in match acquire bus failed\n");
+ return 0;
+ }
+
+ error = bmx280_poke(ia->ia_tag, ia->ia_addr, matchdebug);
+ iic_release_bus(ia->ia_tag, 0);
+
+ return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
+}
+
+static void
+bmx280_attach(device_t parent, device_t self, void *aux)
+{
+ struct bmx280_sc *sc;
+ struct i2c_attach_args *ia;
+ int error, i;
+ uint8_t reg, chip_id;
+ uint8_t buf[2];
+
+ ia = aux;
+ sc = device_private(self);
+
+ sc->sc_dev = self;
+ sc->sc_tag = ia->ia_tag;
+ sc->sc_addr = ia->ia_addr;
+ sc->sc_bmx280debug = 0;
+ sc->sc_bmx280dump = false;
+ sc->sc_has_humidity = false;
+ sc->sc_readattempts = 25;
+ sc->sc_osrs_t = 1;
+ sc->sc_osrs_p = 4;
+ sc->sc_osrs_h = 1;
+ sc->sc_irr_samples = 1;
+ sc->sc_previous_irr = 0xff;
+ sc->sc_sme = NULL;
+
+ aprint_normal("\n");
+
+ mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
+ sc->sc_numsensors = __arraycount(bmx280_sensors);
+
+ if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
+ aprint_error_dev(self,
+ "Unable to create sysmon structure\n");
+ sc->sc_sme = NULL;
+ return;
+ }
+
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ aprint_error_dev(self, "Could not acquire iic bus: %d\n",
+ error);
+ goto out;
+ }
+
+ buf[0] = BMX280_REGISTER_RESET;
+ buf[1] = BMX280_TRIGGER_RESET;
+ error = bmx280_write_register(sc->sc_tag, sc->sc_addr, buf, 2);
+ if (error) {
+ aprint_error_dev(self, "Failed to reset chip: %d\n",
+ error);
+ }
+
+ delay(100);
+
+ reg = BMX280_REGISTER_ID;
+ error = bmx280_read_register(sc->sc_tag, sc->sc_addr, &reg, &chip_id, 1);
+ if (error) {
+ aprint_error_dev(self, "Failed to read ID: %d\n",
+ error);
+ }
+
+ delay(100);
+
+ DPRINTF(sc, 2, ("%s: read ID value: %02x\n",
+ device_xname(sc->sc_dev), chip_id));
+
+ if (chip_id == BMX280_ID_BME280) {
+ sc->sc_has_humidity = true;
+ }
+
+ if ((error = bmx280_sysctl_init(sc)) != 0) {
+ aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
+ goto out;
+ }
+
+ uint8_t raw_blob_tp[24];
+ reg = BMX280_REGISTER_DIG_T1;
+ error = bmx280_read_register(sc->sc_tag, sc->sc_addr, &reg, raw_blob_tp, 24);
+ if (error) {
+ aprint_error_dev(self, "Failed to read the calibration registers for tp: %d\n",
+ error);
+ }
+
+ bmx280_store_raw_blob_tp(sc,raw_blob_tp);
+
+ if (sc->sc_has_humidity) {
+ uint8_t raw_blob_h[8];
+
+ reg = BMX280_REGISTER_DIG_H1;
+ error = bmx280_read_register(sc->sc_tag, sc->sc_addr, &reg, raw_blob_h, 1);
+ if (error) {
+ aprint_error_dev(self, "Failed to read the calibration registers for h1: %d\n",
+ error);
+ }
+
+ reg = BMX280_REGISTER_DIG_H2;
+ error = bmx280_read_register(sc->sc_tag, sc->sc_addr, &reg, &raw_blob_h[1], 7);
+ if (error) {
+ aprint_error_dev(self, "Failed to read the calibration registers for h2 - h6: %d\n",
+ error);
+ }
+
+ bmx280_store_raw_blob_h(sc,raw_blob_h);
+ }
+
+ iic_release_bus(sc->sc_tag, 0);
+
+ if (error != 0) {
+ aprint_error_dev(self, "Unable to setup device\n");
+ goto out;
+ }
+
+ for (i = 0; i < sc->sc_numsensors; i++) {
+ if (sc->sc_has_humidity == false &&
+ bmx280_sensors[i].type == ENVSYS_SRELHUMIDITY) {
+ break;
+ }
+
+ strlcpy(sc->sc_sensors[i].desc, bmx280_sensors[i].desc,
+ sizeof(sc->sc_sensors[i].desc));
+
+ sc->sc_sensors[i].units = bmx280_sensors[i].type;
+ sc->sc_sensors[i].state = ENVSYS_SINVALID;
+
+ DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
+ sc->sc_sensors[i].desc));
+
+ error = sysmon_envsys_sensor_attach(sc->sc_sme,
+ &sc->sc_sensors[i]);
+ if (error) {
+ aprint_error_dev(self,
+ "Unable to attach sensor %d: %d\n", i, error);
+ goto out;
+ }
+ }
+
+ sc->sc_sme->sme_name = device_xname(sc->sc_dev);
+ sc->sc_sme->sme_cookie = sc;
+ sc->sc_sme->sme_refresh = bmx280_refresh;
+
+ DPRINTF(sc, 2, ("bmx280_attach: registering with envsys\n"));
+
+ if (sysmon_envsys_register(sc->sc_sme)) {
+ aprint_error_dev(self,
+ "unable to register with sysmon\n");
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+ return;
+ }
+
+ aprint_normal_dev(self, "Bosch Sensortec %s, Chip ID: 0x%02x\n",
+ (chip_id == BMX280_ID_BMP280) ? "BMP280" : (chip_id == BMX280_ID_BME280) ? "BME280" : "Unknown chip",
+ chip_id);
+
+ return;
+out:
+ sysmon_envsys_destroy(sc->sc_sme);
+ sc->sc_sme = NULL;
+}
+
+/* The conversion algorithms are taken from the Bosch datasheet for
+ * the BMX280 and adapted to the envsys infrastructure.
+ */
+
+static int32_t
+bmx280_compensate_T_int32(struct bmx280_calibration_blob *b,
+ int32_t adc_T,
+ int32_t *t_fine)
+{
+ int32_t var1, var2, T;
+ var1 = ((((adc_T>>3) - ((int32_t)b->dig_T1<<1))) * ((int32_t)b->dig_T2)) >> 11;
+ var2 = (((((adc_T>>4) - ((int32_t)b->dig_T1)) * ((adc_T>>4) - ((int32_t)b->dig_T1))) >> 12) *
+ ((int32_t)b->dig_T3)) >> 14;
+ *t_fine = var1 + var2;
+ T = (*t_fine * 5 + 128) >> 8;
+ return T;
+}
+
+/* Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
+ * Output value of 24674867 represents 24674867/256 = 96386.2 Pa = 963.862 hPa
+ */
+static uint32_t
+bmx280_compensate_P_int64(struct bmx280_calibration_blob *b,
+ int32_t adc_P,
+ int32_t t_fine)
+{
+ int64_t var1, var2, p;
+ var1 = ((int64_t)t_fine) - 128000;
+ var2 = var1 * var1 * (int64_t)b->dig_P6;
+ var2 = var2 + ((var1*(int64_t)b->dig_P5)<<17);
+ var2 = var2 + (((int64_t)b->dig_P4)<<35);
+ var1 = ((var1 * var1 * (int64_t)b->dig_P3)>>8) + ((var1 * (int64_t)b->dig_P2)<<12);
+ var1 = (((((int64_t)1)<<47)+var1))*((int64_t)b->dig_P1)>>33;
+ if (var1 == 0) {
+ return 0; /* avoid exception caused by division by zero */
+ }
+ p = 1048576-adc_P;
+ p = (((p<<31)-var2)*3125)/var1;
+ var1 = (((int64_t)b->dig_P9) * (p>>13) * (p>>13)) >> 25;
+ var2 = (((int64_t)b->dig_P8) * p) >> 19;
+ p = ((p + var1 + var2) >> 8) + (((int64_t)b->dig_P7)<<4);
+ return (uint32_t)p;
+}
+
+/* Returns humidity in %RH as unsigned 32 bit integer in Q22.10 format (22 integer and 10 fractional bits).
+ *
+ * Output value of 47445 represents 47445/1024 = 46.333 %RH
+ */
+static uint32_t
+bmx280_compensate_H_int32(struct bmx280_calibration_blob *b,
+ int32_t adc_H,
+ int32_t t_fine)
+{
+ int32_t v_x1_u32r;
+ v_x1_u32r = (t_fine - ((int32_t)76800));
+ v_x1_u32r = (((((adc_H << 14) - (((int32_t)b->dig_H4) << 20) - (((int32_t)b->dig_H5) *
+ v_x1_u32r)) + ((int32_t)16384)) >> 15) * (((((((v_x1_u32r *
+ ((int32_t)b->dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)b->dig_H3)) >> 11) +
+ ((int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)b->dig_H2) +
+ 8192) >> 14));
+ v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) *
+ ((int32_t)b->dig_H1)) >> 4));
+ v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
+ v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
+ return (uint32_t)(v_x1_u32r>>12);
+}
+
+
+static int
+bmx280_set_control_and_trigger(struct bmx280_sc *sc,
+ uint8_t osrs_t_mask,
+ uint8_t osrs_p_mask,
+ uint8_t osrs_h_mask,
+ uint8_t filter_mask)
+{
+ uint8_t cr[6];
+ int error;
+ int s = 0;
+
+ cr[0] = cr[1] = cr[2] = cr[3] = cr[4] = cr[5] = 0;
+
+ if (filter_mask != sc->sc_previous_irr) {
+ cr[s] = BMX280_REGISTER_CONFIG;
+ s++;
+ cr[s] = filter_mask << BMX280_CONFIG_FILTER_SHIFT;
+ s++;
+ sc->sc_previous_irr = filter_mask;
+ }
+ if (sc->sc_has_humidity) {
+ cr[s] = BMX280_REGISTER_CTRL_HUM;
+ s++;
+ cr[s] = osrs_h_mask;
+ s++;
+ }
+ cr[s] = BMX280_REGISTER_CTRL_MEAS;
+ s++;
+ cr[s] = osrs_t_mask << BMX280_CTRL_OSRS_T_SHIFT;
+ cr[s] = cr[s] | osrs_p_mask << BMX280_CTRL_OSRS_P_SHIFT;
+ cr[s] = cr[s] | BMX280_MODE_FORCED;
+ s++;
+ DPRINTF(sc, 2, ("%s: control register set up: num: %d ; %02x %02x ; %02x %02x ; %02x %02x\n",
+ device_xname(sc->sc_dev), s, cr[0], cr[1], cr[2], cr[3], cr[4], cr[5]));
+ error = bmx280_write_register(sc->sc_tag, sc->sc_addr, cr, s);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: write control registers: %d\n",
+ device_xname(sc->sc_dev), error));
+ error = EINVAL;
+ }
+
+ return error;
+}
+
+static int
+bmx280_wait_for_data(struct bmx280_sc *sc)
+{
+ uint8_t reg;
+ uint8_t running = 99;
+ int c = sc->sc_readattempts;
+ int error = 0, ierror;
+
+ reg = BMX280_REGISTER_STATUS;
+ do {
+ delay(10);
+ ierror = bmx280_read_register(sc->sc_tag, sc->sc_addr, &reg, &running, 1);
+ if (ierror) {
+ DPRINTF(sc, 2, ("%s: Refresh failed to read back status: %d\n",
+ device_xname(sc->sc_dev), ierror));
+ error = EINVAL;
+ break;
+ }
+
+ DPRINTF(sc, 2, ("%s: Refresh status read back: %02x\n",
+ device_xname(sc->sc_dev), running));
+
+ c--;
+ } while (c > 0 && (running & BMX280_STATUS_MEASURING_MASK));
+
+ return error;
+}
+
+static int
+bmx280_read_data(struct bmx280_sc *sc,
+ int32_t *temp,
+ int32_t *press,
+ int32_t *hum,
+ bool justtemp)
+{
+ int error = 0, ierror;
+ int rlen, rtstart, rpstart, rhstart;
+ int x_temp, x_press, x_hum;
+ uint8_t raw_press_temp_hum[8], reg;
+
+ raw_press_temp_hum[0] = raw_press_temp_hum[1] =
+ raw_press_temp_hum[2] = raw_press_temp_hum[3] =
+ raw_press_temp_hum[4] = raw_press_temp_hum[5] =
+ raw_press_temp_hum[6] = raw_press_temp_hum[7] = 0;
+
+ if (justtemp) {
+ reg = BMX280_REGISTER_TEMP_MSB;
+ rlen = 3;
+ rtstart = 0;
+ rpstart = 0;
+ rhstart = 0;
+ } else {
+ reg = BMX280_REGISTER_PRESS_MSB;
+ if (sc->sc_has_humidity == false) {
+ rlen = 6;
+ } else {
+ rlen = 8;
+ }
+ rtstart = 3;
+ rpstart = 0;
+ rhstart = 6;
+ }
+
+ DPRINTF(sc, 2, ("%s: read data: reg: %02x ; len: %d ; tstart: %d ; pstart: %d\n",
+ device_xname(sc->sc_dev), reg, rlen, rtstart, rpstart));
+
+ ierror = bmx280_read_register(sc->sc_tag, sc->sc_addr, &reg, raw_press_temp_hum, rlen);
+ if (ierror) {
+ DPRINTF(sc, 2, ("%s: failed to read pressure and temp registers: %d\n",
+ device_xname(sc->sc_dev), ierror));
+ error = EINVAL;
+ goto out;
+ }
+
+ DPRINTF(sc, 2, ("%s: raw pressure, temp and hum: %02x %02x %02x - %02x %02x %02x - %02x %02x\n",
+ device_xname(sc->sc_dev),
+ raw_press_temp_hum[0], raw_press_temp_hum[1], raw_press_temp_hum[2],
+ raw_press_temp_hum[3], raw_press_temp_hum[4], raw_press_temp_hum[5],
+ raw_press_temp_hum[6],raw_press_temp_hum[7]));
+
+ x_temp = raw_press_temp_hum[rtstart] << 12;
+ x_temp = x_temp | (raw_press_temp_hum[rtstart + 1] << 4);
+ x_temp = x_temp | (raw_press_temp_hum[rtstart + 2] >> 4);
+
+ DPRINTF(sc, 1, ("%s: intermediate temp: %d (%04x)\n",
+ device_xname(sc->sc_dev), x_temp, x_temp));
+
+ *temp = x_temp;
+
+ *hum = 0;
+ *press = 0;
+
+ if (justtemp == false) {
+ x_press = raw_press_temp_hum[rpstart] << 12;
+ x_press = x_press | (raw_press_temp_hum[rpstart + 1] << 4);
+ x_press = x_press | (raw_press_temp_hum[rpstart + 2] >> 4);
+
+ DPRINTF(sc, 1, ("%s: intermediate pressure: %d (%04x)\n",
+ device_xname(sc->sc_dev), x_press, x_press));
+ *press = x_press;
+ }
+ if (sc->sc_has_humidity) {
+ x_hum = raw_press_temp_hum[rhstart] << 8;
+ x_hum = x_hum | raw_press_temp_hum[rhstart + 1];
+
+ DPRINTF(sc, 1, ("%s: intermediate humidity: %d (%02x)\n",
+ device_xname(sc->sc_dev), x_hum, x_hum));
+ *hum = x_hum;
+ }
+
+ out:
+ return error;
+}
+
+static void
+bmx280_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
+{
+ struct bmx280_sc *sc;
+ sc = sme->sme_cookie;
+ int error = 0;
+ int32_t t_fine;
+ int32_t m_temp, m_press, m_hum;
+ int32_t comp_temp;
+ uint32_t comp_press;
+ uint32_t comp_hum;
+ edata->state = ENVSYS_SINVALID;
+
+ /* Ya... just do this on a refresh... */
+
+ if (sc->sc_bmx280dump) {
+ DPRINTF(sc, 1, ("%s: dig_T1: %d %04x\n",__func__,sc->sc_cal_blob.dig_T1,sc->sc_cal_blob.dig_T1));
+ DPRINTF(sc, 1, ("%s: dig_T2: %d %04x\n",__func__,sc->sc_cal_blob.dig_T2,sc->sc_cal_blob.dig_T2));
+ DPRINTF(sc, 1, ("%s: dig_T3: %d %04x\n",__func__,sc->sc_cal_blob.dig_T3,sc->sc_cal_blob.dig_T3));
+ DPRINTF(sc, 1, ("%s: dig_P1: %d %04x\n",__func__,sc->sc_cal_blob.dig_P1,sc->sc_cal_blob.dig_P1));
+ DPRINTF(sc, 1, ("%s: dig_P2: %d %04x\n",__func__,sc->sc_cal_blob.dig_P2,sc->sc_cal_blob.dig_P2));
+ DPRINTF(sc, 1, ("%s: dig_P3: %d %04x\n",__func__,sc->sc_cal_blob.dig_P3,sc->sc_cal_blob.dig_P3));
+ DPRINTF(sc, 1, ("%s: dig_P4: %d %04x\n",__func__,sc->sc_cal_blob.dig_P4,sc->sc_cal_blob.dig_P4));
+ DPRINTF(sc, 1, ("%s: dig_P5: %d %04x\n",__func__,sc->sc_cal_blob.dig_P5,sc->sc_cal_blob.dig_P5));
+ DPRINTF(sc, 1, ("%s: dig_P6: %d %04x\n",__func__,sc->sc_cal_blob.dig_P6,sc->sc_cal_blob.dig_P6));
+ DPRINTF(sc, 1, ("%s: dig_P7: %d %04x\n",__func__,sc->sc_cal_blob.dig_P7,sc->sc_cal_blob.dig_P7));
+ DPRINTF(sc, 1, ("%s: dig_P8: %d %04x\n",__func__,sc->sc_cal_blob.dig_P8,sc->sc_cal_blob.dig_P8));
+ DPRINTF(sc, 1, ("%s: dig_P9: %d %04x\n",__func__,sc->sc_cal_blob.dig_P9,sc->sc_cal_blob.dig_P9));
+
+ if (sc->sc_has_humidity) {
+ DPRINTF(sc, 1, ("%s: dig_H1: %d %02x\n",__func__,sc->sc_cal_blob.dig_H1,sc->sc_cal_blob.dig_H1));
+ DPRINTF(sc, 1, ("%s: dig_H2: %d %04x\n",__func__,sc->sc_cal_blob.dig_H2,sc->sc_cal_blob.dig_H2));
+ DPRINTF(sc, 1, ("%s: dig_H3: %d %02x\n",__func__,sc->sc_cal_blob.dig_H3,sc->sc_cal_blob.dig_H3));
+ DPRINTF(sc, 1, ("%s: dig_H4: %d %04x\n",__func__,sc->sc_cal_blob.dig_H4,sc->sc_cal_blob.dig_H4));
+ DPRINTF(sc, 1, ("%s: dig_H5: %d %04x\n",__func__,sc->sc_cal_blob.dig_H5,sc->sc_cal_blob.dig_H5));
+ DPRINTF(sc, 1, ("%s: dig_H6: %d %02x\n",__func__,sc->sc_cal_blob.dig_H6,sc->sc_cal_blob.dig_H6));
+ }
+
+ sc->sc_bmx280dump = false;
+ }
+
+ mutex_enter(&sc->sc_mutex);
+ error = iic_acquire_bus(sc->sc_tag, 0);
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
+ device_xname(sc->sc_dev), error));
+ goto out;
+ }
+
+ if (error == 0) {
+ switch (edata->sensor) {
+ case BMX280_TEMP_SENSOR:
+ /* A temperature reading does not need pressure */
+
+ error = bmx280_set_control_and_trigger(sc,
+ bmx280_osrs_text_to_mask(sc->sc_osrs_t),
+ 0,
+ 0,
+ bmx280_irr_text_to_mask(sc->sc_irr_samples));
+
+ if (error == 0) {
+ error = bmx280_wait_for_data(sc);
+
+ if (error == 0) {
+ error = bmx280_read_data(sc, &m_temp, &m_press, &m_hum, true);
+
+ if (error == 0) {
+ comp_temp = bmx280_compensate_T_int32(&sc->sc_cal_blob, m_temp, &t_fine);
+
+ DPRINTF(sc, 1, ("%s: Refresh compensated temp: %d - t_fine: %d\n",
+ device_xname(sc->sc_dev), comp_temp, t_fine));
+
+ /* comp_temp is in Celcius * 100. This converts it to microkelvin */
+
+ uint32_t q;
+
+ q = (uint32_t)comp_temp;
+ q = q + 27315;
+ q = q * 10000;
+
+ DPRINTF(sc, 1, ("%s: Refresh Q: %d\n", __func__, q));
+
+ edata->value_cur = q;
+ edata->state = ENVSYS_SVALID;
+ }
+ }
+ }
+ break;
+ case BMX280_PRESSURE_SENSOR:
+
+ /* Pressure needs the temp too */
+ error = bmx280_set_control_and_trigger(sc,
+ bmx280_osrs_text_to_mask(sc->sc_osrs_t),
+ bmx280_osrs_text_to_mask(sc->sc_osrs_p),
+ 0,
+ bmx280_irr_text_to_mask(sc->sc_irr_samples));
+
+ if (error == 0) {
+ error = bmx280_wait_for_data(sc);
+
+ if (error == 0) {
+ error = bmx280_read_data(sc, &m_temp, &m_press, &m_hum, false);
+
+ if (error == 0) {
+ comp_temp = bmx280_compensate_T_int32(&sc->sc_cal_blob, m_temp, &t_fine);
+
+ DPRINTF(sc, 1, ("%s: Refresh compensated temp for pressure: %d - t_fine: %d\n",
+ device_xname(sc->sc_dev), comp_temp, t_fine));
+
+ comp_press = bmx280_compensate_P_int64(&sc->sc_cal_blob, m_press, t_fine);
+
+ DPRINTF(sc, 1, ("%s: Refresh compensated pressure: %d\n",
+ device_xname(sc->sc_dev), comp_press));
+
+ uint32_t q;
+
+ q = comp_press;
+ q = q / 256;
+ q = q * 100;
+
+ DPRINTF(sc, 1, ("%s: Refresh pressure Q: %d\n", __func__, q));
+
+ edata->value_cur = q;
+ edata->state = ENVSYS_SVALID;
+ }
+ }
+ }
+ break;
+
+ case BMX280_HUMIDITY_SENSOR:
+
+ /* Humidity wants temperature */
+
+ error = bmx280_set_control_and_trigger(sc,
+ bmx280_osrs_text_to_mask(sc->sc_osrs_t),
+ 0,
+ bmx280_osrs_text_to_mask(sc->sc_osrs_h),
+ bmx280_irr_text_to_mask(sc->sc_irr_samples));
+
+ if (error == 0) {
+ error = bmx280_wait_for_data(sc);
+
+ if (error == 0) {
+ error = bmx280_read_data(sc, &m_temp, &m_press, &m_hum, false);
+
+ if (error == 0) {
+ comp_temp = bmx280_compensate_T_int32(&sc->sc_cal_blob, m_temp, &t_fine);
+
+ DPRINTF(sc, 1, ("%s: Refresh compensated temp for humidity: %d - t_fine: %d\n",
+ device_xname(sc->sc_dev), comp_temp, t_fine));
+
+ comp_hum = bmx280_compensate_H_int32(&sc->sc_cal_blob, m_hum, t_fine);
+
+ DPRINTF(sc, 2, ("%s: Refresh compensated humidity: %d\n",
+ device_xname(sc->sc_dev), comp_hum));
+
+ uint32_t q;
+
+ q = comp_hum;
+ q = q / 1024;
+ q = q * 100; /* XXX - this probably is not correct */
+
+ DPRINTF(sc, 1, ("%s: Refresh humidity Q: %d\n", __func__, q));
+
+ edata->value_cur = q;
+ edata->state = ENVSYS_SVALID;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ if (error) {
+ DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n",
+ device_xname(sc->sc_dev), error));
+ }
+
+ iic_release_bus(sc->sc_tag, 0);
+out:
+ mutex_exit(&sc->sc_mutex);
+}
+
+static int
+bmx280_detach(device_t self, int flags)
+{
+ struct bmx280_sc *sc;
+
+ sc = device_private(self);
+
+ mutex_enter(&sc->sc_mutex);
+
+ /* Remove the sensors */
+ if (sc->sc_sme != NULL) {
+ sysmon_envsys_unregister(sc->sc_sme);
+ sc->sc_sme = NULL;
+ }
+ mutex_exit(&sc->sc_mutex);
+
+ /* Remove the sysctl tree */
+ sysctl_teardown(&sc->sc_bmx280log);
+
+ /* Remove the mutex */
+ mutex_destroy(&sc->sc_mutex);
+
+ return 0;
+}
+
+MODULE(MODULE_CLASS_DRIVER, bmx280thp, "iic,sysmon_envsys");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+bmx280thp_modcmd(modcmd_t cmd, void *opaque)
+{
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ return config_init_component(cfdriver_ioconf_bmx280thp,
+ cfattach_ioconf_bmx280thp, cfdata_ioconf_bmx280thp);
+#else
+ return 0;
+#endif
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ return config_fini_component(cfdriver_ioconf_bmx280thp,
+ cfattach_ioconf_bmx280thp, cfdata_ioconf_bmx280thp);
+#else
+ return 0;
+#endif
+ default:
+ return ENOTTY;
+ }
+}
diff --git a/sys/dev/i2c/bmx280reg.h b/sys/dev/i2c/bmx280reg.h
new file mode 100644
index 00000000000..20a4031ffba
--- /dev/null
+++ b/sys/dev/i2c/bmx280reg.h
@@ -0,0 +1,96 @@
+/* $NetBSD: bmx280reg.h,v 1.1 2022/11/21 21:24:01 brad Exp $ */
+
+/*
+ * Copyright (c) 2022 Brad Spencer <brad@anduin.eldar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_I2C_BMX280REG_H_
+#define _DEV_I2C_BMX280REG_H_
+
+#define BMX280_TYPICAL_ADDR_1 0x76
+#define BMX280_TYPICAL_ADDR_2 0x77
+
+#define BMX280_REGISTER_DIG_T1 0x88
+#define BMX280_REGISTER_DIG_T2 0x8A
+#define BMX280_REGISTER_DIG_T3 0x8C
+#define BMX280_REGISTER_DIG_P1 0x8E
+#define BMX280_REGISTER_DIG_P2 0x90
+#define BMX280_REGISTER_DIG_P3 0x92
+#define BMX280_REGISTER_DIG_P4 0x94
+#define BMX280_REGISTER_DIG_P5 0x96
+#define BMX280_REGISTER_DIG_P6 0x98
+#define BMX280_REGISTER_DIG_P7 0x9A
+#define BMX280_REGISTER_DIG_P8 0x9C
+#define BMX280_REGISTER_DIG_P9 0x9E
+#define BMX280_REGISTER_DIG_H1 0xA1
+#define BMX280_REGISTER_DIG_H2 0xE1
+#define BMX280_REGISTER_DIG_H3 0xE3
+#define BMX280_REGISTER_DIG_H4 0xE4
+#define BMX280_REGISTER_DIG_H5 0xE5
+
+#define BMX280_REGISTER_ID 0xD0
+#define BMX280_ID_BMP280 0x58
+#define BMX280_ID_BME280 0x60
+
+#define BMX280_REGISTER_RESET 0xE0
+#define BMX280_TRIGGER_RESET 0xB6
+
+#define BMX280_REGISTER_CTRL_HUM 0xF2
+
+#define BMX280_REGISTER_STATUS 0xF3
+#define BMX280_STATUS_MEASURING_MASK 0x08
+#define BMX280_STATUS_IM_UPDATE_MASK 0x01
+
+#define BMX280_REGISTER_CTRL_MEAS 0xF4
+#define BMX280_CTRL_OSRS_T_MASK 0xE0
+#define BMX280_CTRL_OSRS_P_MASK 0x1C
+#define BMX280_CTRL_OSRS_T_SHIFT 5
+#define BMX280_CTRL_OSRS_P_SHIFT 2
+#define BMX280_OSRS_TP_VALUE_SKIPPED 0x00
+#define BMX280_OSRS_TP_VALUE_X1 0x01
+#define BMX280_OSRS_TP_VALUE_X2 0x02
+#define BMX280_OSRS_TP_VALUE_X4 0x03
+#define BMX280_OSRS_TP_VALUE_X8 0x04
+#define BMX280_OSRS_TP_VALUE_X16 0x05
+#define BMX280_CTRL_MODE_MASK 0x03
+#define BMX280_MODE_SLEEP 0x00
+#define BMX280_MODE_FORCED 0x01
+#define BMX280_MODE_NORMAL 0x03
+
+#define BMX280_REGISTER_CONFIG 0xF5
+#define BMX280_CONFIG_T_SB_MASK 0xE0
+#define BMX280_CONFIG_FILTER_MASK 0x1C
+#define BMX280_CONFIG_FILTER_SHIFT 2
+#define BMX280_FILTER_VALUE_OFF 0x00
+#define BMX280_FILTER_VALUE_2 0x01
+#define BMX280_FILTER_VALUE_5 0x02
+#define BMX280_FILTER_VALUE_11 0x04
+#define BMX280_FILTER_VALUE_22 0x05
+#define BMX280_CONFIG_SPI3W_EN_MASK 0x01
+
+#define BMX280_REGISTER_PRESS_MSB 0xF7
+#define BMX280_REGISTER_PRESS_LSB 0xF8
+#define BMX280_REGISTER_PRESS_XLSB 0xF9
+
+#define BMX280_REGISTER_TEMP_MSB 0xFA
+#define BMX280_REGISTER_TEMP_LSB 0xFB
+#define BMX280_REGISTER_TEMP_XLSB 0xFC
+
+#define BMX280_TEMPPRES_XLSB_MASK 0xF0
+
+#define BMX280_REGISTER_HUM_MSB 0xFD
+#define BMX280_REGISTER_HUM_LSB 0xFE
+
+#endif
diff --git a/sys/dev/i2c/bmx280var.h b/sys/dev/i2c/bmx280var.h
new file mode 100644
index 00000000000..b49e890b4f2
--- /dev/null
+++ b/sys/dev/i2c/bmx280var.h
@@ -0,0 +1,85 @@
+/* $NetBSD: bmx280var.h,v 1.1 2022/11/21 21:24:01 brad Exp $ */
+
+/*
+ * Copyright (c) 2022 Brad Spencer <brad@anduin.eldar.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _DEV_I2C_BMX280VAR_H_
+#define _DEV_I2C_BMX280VAR_H_
+
+#define BMX280_NUM_SENSORS 3
+#define BMX280_TEMP_SENSOR 0
+#define BMX280_PRESSURE_SENSOR 1
+#define BMX280_HUMIDITY_SENSOR 2
+
+struct bmx280_calibration_blob {
+ uint16_t dig_T1;
+ int16_t dig_T2;
+ int16_t dig_T3;
+
+ uint16_t dig_P1;
+ int16_t dig_P2;
+ int16_t dig_P3;
+ int16_t dig_P4;
+ int16_t dig_P5;
+ int16_t dig_P6;
+ int16_t dig_P7;
+ int16_t dig_P8;
+ int16_t dig_P9;
+ uint8_t dig_H1;
+ int16_t dig_H2;
+ uint8_t dig_H3;
+ int16_t dig_H4;
+ int16_t dig_H5;
+ int8_t dig_H6;
+};
+
+struct bmx280_sc {
+ int sc_bmx280debug;
+ device_t sc_dev;
+ i2c_tag_t sc_tag;
+ i2c_addr_t sc_addr;
+ kmutex_t sc_mutex;
+ int sc_numsensors;
+ struct sysmon_envsys *sc_sme;
+ struct sysctllog *sc_bmx280log;
+ envsys_data_t sc_sensors[BMX280_NUM_SENSORS];
+ struct bmx280_calibration_blob sc_cal_blob;
+ bool sc_has_humidity;
+ int sc_readattempts;
+ int sc_osrs_t;
+ int sc_osrs_p;
+ int sc_osrs_h;
+ int sc_irr_samples;
+ uint8_t sc_previous_irr;
+ bool sc_bmx280dump;
+};
+
+struct bmx280_sensor {
+ const char *desc;
+ enum envsys_units type;
+};
+
+struct bmx280_osrs_list {
+ const int text;
+ uint8_t mask;
+};
+
+struct bmx280_irr_list {
+ const int text;
+ uint8_t mask;
+};
+
+#endif
diff --git a/sys/dev/i2c/files.i2c b/sys/dev/i2c/files.i2c
index 8ff32f49aa1..6e10edb7429 100644
--- a/sys/dev/i2c/files.i2c
+++ b/sys/dev/i2c/files.i2c
@@ -1,4 +1,4 @@
-# $NetBSD: files.i2c,v 1.124 2022/11/17 19:20:06 brad Exp $
+# $NetBSD: files.i2c,v 1.125 2022/11/21 21:24:01 brad Exp $
obsolete defflag opt_i2cbus.h I2C_SCAN
define i2cbus { }
@@ -435,3 +435,8 @@ file dev/i2c/scmdi2c.c scmdi2c
device aht20temp
attach aht20temp at iic
file dev/i2c/aht20.c aht20temp
+
+# Bosch Sensortec BMP280/BME280 Temperature, Humidity and Pressure sensor
+device bmx280thp
+attach bmx280thp at iic
+file dev/i2c/bmx280.c bmx280thp
diff --git a/sys/dev/sysmon/sysmon_envsys_tables.c b/sys/dev/sysmon/sysmon_envsys_tables.c
index 75fed669892..6f139a1a845 100644
--- a/sys/dev/sysmon/sysmon_envsys_tables.c
+++ b/sys/dev/sysmon/sysmon_envsys_tables.c
@@ -1,4 +1,4 @@
-/* $NetBSD: sysmon_envsys_tables.c,v 1.13 2018/05/27 01:39:00 thorpej Exp $ */
+/* $NetBSD: sysmon_envsys_tables.c,v 1.14 2022/11/21 21:24:01 brad Exp $ */
/*-
* Copyright (c) 2007 Juan Romero Pardines.
@@ -26,7 +26,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_tables.c,v 1.13 2018/05/27 01:39:00 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sysmon_envsys_tables.c,v 1.14 2022/11/21 21:24:01 brad Exp $");
#include <sys/types.h>
@@ -53,6 +53,7 @@ static const struct sme_descr_entry sme_units_description[] = {
{ ENVSYS_BATTERY_CHARGE, -1, "Battery charge" },
{ ENVSYS_SRELHUMIDITY, -1, "relative Humidity" },
{ ENVSYS_LUX, -1, "Illuminance" },
+ { ENVSYS_PRESSURE, -1, "pressure" },
{ -1, -1, "unknown" }
};