From 02b8954fe89d72e580aac9928e8f0dd91cabaedc Mon Sep 17 00:00:00 2001 From: brad Date: Sat, 3 Dec 2022 01:04:42 +0000 Subject: Split the BMP280 / BME280 driver into common code and create I2C and SPI attachments. --- sys/dev/i2c/bmx280.c | 1145 -------------------------------------------- sys/dev/i2c/bmx280reg.h | 96 ---- sys/dev/i2c/bmx280thpi2c.c | 264 ++++++++++ sys/dev/i2c/bmx280var.h | 88 ---- sys/dev/i2c/files.i2c | 7 +- sys/dev/ic/bmx280.c | 1013 +++++++++++++++++++++++++++++++++++++++ sys/dev/ic/bmx280reg.h | 96 ++++ sys/dev/ic/bmx280var.h | 94 ++++ sys/dev/spi/bmx280thpspi.c | 205 ++++++++ sys/dev/spi/files.spi | 6 +- 10 files changed, 1680 insertions(+), 1334 deletions(-) delete mode 100644 sys/dev/i2c/bmx280.c delete mode 100644 sys/dev/i2c/bmx280reg.h create mode 100644 sys/dev/i2c/bmx280thpi2c.c delete mode 100644 sys/dev/i2c/bmx280var.h create mode 100644 sys/dev/ic/bmx280.c create mode 100644 sys/dev/ic/bmx280reg.h create mode 100644 sys/dev/ic/bmx280var.h create mode 100644 sys/dev/spi/bmx280thpspi.c (limited to 'sys/dev') diff --git a/sys/dev/i2c/bmx280.c b/sys/dev/i2c/bmx280.c deleted file mode 100644 index 5a8e402ce68..00000000000 --- a/sys/dev/i2c/bmx280.c +++ /dev/null @@ -1,1145 +0,0 @@ -/* $NetBSD: bmx280.c,v 1.6 2022/12/01 02:29:37 brad Exp $ */ - -/* - * Copyright (c) 2022 Brad Spencer - * - * 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 -__KERNEL_RCSID(0, "$NetBSD: bmx280.c,v 1.6 2022/12/01 02:29:37 brad Exp $"); - -/* - Driver for the Bosch BMP280/BME280 temperature, humidity (sometimes) and - (usually barometric) pressure sensor -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -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[4] << 4) | (b[5] & 0x0F))); - 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, ®, 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, sysctlwait_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; - - if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, - 0, CTLTYPE_NODE, "waitfactor", - SYSCTL_DESCR("bmx280 wait factors"), NULL, 0, NULL, 0, CTL_HW, - sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) - return error; - sysctlwait_num = cnode->sysctl_num; - - if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, - CTLFLAG_READWRITE, CTLTYPE_INT, "t", - SYSCTL_DESCR("Temperature wait multiplier"), - bmx280_verify_sysctl, 0, &sc->sc_waitfactor_t, - 0, CTL_HW, sysctlroot_num, sysctlwait_num, CTL_CREATE, CTL_EOL)) != 0) - return error; - - if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, - CTLFLAG_READWRITE, CTLTYPE_INT, "p", - SYSCTL_DESCR("Pressure wait multiplier"), - bmx280_verify_sysctl, 0, &sc->sc_waitfactor_p, - 0, CTL_HW, sysctlroot_num, sysctlwait_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, "h", - SYSCTL_DESCR("Humidity wait multiplier"), - bmx280_verify_sysctl, 0, &sc->sc_waitfactor_h, - 0, CTL_HW, sysctlroot_num, sysctlwait_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_waitfactor_t = 6; - sc->sc_waitfactor_p = 2; - sc->sc_waitfactor_h = 2; - 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(30000); - - reg = BMX280_REGISTER_ID; - error = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, &chip_id, 1); - if (error) { - aprint_error_dev(self, "Failed to read ID: %d\n", - error); - } - - delay(1000); - - 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, ®, raw_blob_tp, 24); - if (error) { - aprint_error_dev(self, "Failed to read the calibration registers for tp: %d\n", - error); - } - - if (sc->sc_bmx280debug > 0) { - for(int _d = 0;_d < 24;_d++) { - DPRINTF(sc, 0, ("%s: %d %02x\n", - device_xname(sc->sc_dev), _d, raw_blob_tp[_d])); - } - } - - 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, ®, 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, ®, &raw_blob_h[1], 7); - if (error) { - aprint_error_dev(self, "Failed to read the calibration registers for h2 - h6: %d\n", - error); - } - - if (sc->sc_bmx280debug > 0) { - for(int _d = 0;_d < 8;_d++) { - DPRINTF(sc, 0, ("%s: %d %02x\n", - device_xname(sc->sc_dev), _d, raw_blob_h[_d])); - } - } - - 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 BMP280 datasheet. The - * same algorithms are used with the BME280. - * - * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf - * - * Section 3.11.3, page 21 - * - */ - -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; - } - - /* The wait needed is not well documented, so this is somewhat of a guess. - * There is an attempt with this to only wait as long as needed. - */ - - int p1, p2; - - p1 = (osrs_t_mask * sc->sc_waitfactor_t) + (osrs_p_mask * sc->sc_waitfactor_p); - if (sc->sc_has_humidity) { - p1 = p1 + (osrs_h_mask * sc->sc_waitfactor_h); - } - p2 = mstohz(p1); - if (p2 == 0) { - p2 = 1; - } - /* Be careful with this... the print itself will cause extra delay */ - DPRINTF(sc, 2, ("%s: p1: %d ; %d\n", - device_xname(sc->sc_dev), p1, p2)); - kpause("b280mea",false,p2,NULL); - - 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(1000); - ierror = bmx280_read_register(sc->sc_tag, sc->sc_addr, ®, &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, ®, 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)); - - uint64_t q; - - q = (uint64_t)comp_hum * 1000000; - DPRINTF(sc, 1, ("%s: Refresh humidity Q 1: %jd\n", __func__, (uintmax_t)q)); - q = q / 1024; - - DPRINTF(sc, 1, ("%s: Refresh humidity Q 2: %jd\n", __func__, (uintmax_t)q)); - - edata->value_cur = (uint32_t) 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 deleted file mode 100644 index 20a4031ffba..00000000000 --- a/sys/dev/i2c/bmx280reg.h +++ /dev/null @@ -1,96 +0,0 @@ -/* $NetBSD: bmx280reg.h,v 1.1 2022/11/21 21:24:01 brad Exp $ */ - -/* - * Copyright (c) 2022 Brad Spencer - * - * 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/bmx280thpi2c.c b/sys/dev/i2c/bmx280thpi2c.c new file mode 100644 index 00000000000..f381b2a6f1e --- /dev/null +++ b/sys/dev/i2c/bmx280thpi2c.c @@ -0,0 +1,264 @@ +/* $NetBSD: bmx280thpi2c.c,v 1.1 2022/12/03 01:04:43 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer + * + * 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 +__KERNEL_RCSID(0, "$NetBSD: bmx280thpi2c.c,v 1.1 2022/12/03 01:04:43 brad Exp $"); + +/* + * I2C driver for the Bosch BMP280 / BME280 sensor. + * Uses the common bmx280thp driver to do the real work. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern void bmx280_attach(struct bmx280_sc *); + +static int bmx280thpi2c_poke(i2c_tag_t, i2c_addr_t, bool); +static int bmx280thpi2c_match(device_t, cfdata_t, void *); +static void bmx280thpi2c_attach(device_t, device_t, void *); +static int bmx280thpi2c_detach(device_t, int); + +#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(bmx280thpi2c, sizeof(struct bmx280_sc), + bmx280thpi2c_match, bmx280thpi2c_attach, bmx280thpi2c_detach, NULL); + +/* 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 +bmx280thpi2c_read_register_direct(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, + uint8_t *buf, size_t blen) +{ + int error; + + error = iic_exec(tag,I2C_OP_WRITE,addr,®,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 +bmx280thpi2c_read_register(struct bmx280_sc *sc, uint8_t reg, + uint8_t *buf, size_t blen) +{ + int error; + + KASSERT(blen > 0); + + error = bmx280thpi2c_read_register_direct(sc->sc_tag, sc->sc_addr, reg, + buf, blen); + + return error; +} + +/* 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 +bmx280thpi2c_write_register(struct bmx280_sc *sc, + 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(sc->sc_tag,I2C_OP_WRITE_WITH_STOP,sc->sc_addr,NULL,0, + buf,blen,0); + + return error; +} + +static int +bmx280thpi2c_acquire_bus(struct bmx280_sc *sc) +{ + return(iic_acquire_bus(sc->sc_tag, 0)); +} + +static void +bmx280thpi2c_release_bus(struct bmx280_sc *sc) +{ + iic_release_bus(sc->sc_tag, 0); +} + +static int +bmx280thpi2c_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug) +{ + uint8_t reg = BMX280_REGISTER_ID; + uint8_t buf[1]; + int error; + + error = bmx280thpi2c_read_register_direct(tag, addr, reg, buf, 1); + if (matchdebug) { + printf("poke X 1: %d\n", error); + } + return error; +} + +static int +bmx280thpi2c_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 = bmx280thpi2c_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 +bmx280thpi2c_attach(device_t parent, device_t self, void *aux) +{ + struct bmx280_sc *sc; + struct i2c_attach_args *ia; + + 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_func_acquire_bus = &bmx280thpi2c_acquire_bus; + sc->sc_func_release_bus = &bmx280thpi2c_release_bus; + sc->sc_func_read_register = &bmx280thpi2c_read_register; + sc->sc_func_write_register = &bmx280thpi2c_write_register; + + mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE); + + bmx280_attach(sc); + + return; +} + +static int +bmx280thpi2c_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, bmx280thpi2c, "iic,bmx280thp"); + +#ifdef _MODULE +/* Like other drivers, we do this because the bmx280 common + * driver has the definitions already. + */ +#undef CFDRIVER_DECL +#define CFDRIVER_DECL(name, class, attr) +#include "ioconf.c" +#endif + +static int +bmx280thpi2c_modcmd(modcmd_t cmd, void *opaque) +{ + + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + return config_init_component(cfdriver_ioconf_bmx280thpi2c, + cfattach_ioconf_bmx280thpi2c, cfdata_ioconf_bmx280thpi2c); +#else + return 0; +#endif + case MODULE_CMD_FINI: +#ifdef _MODULE + return config_fini_component(cfdriver_ioconf_bmx280thpi2c, + cfattach_ioconf_bmx280thpi2c, cfdata_ioconf_bmx280thpi2c); +#else + return 0; +#endif + default: + return ENOTTY; + } +} diff --git a/sys/dev/i2c/bmx280var.h b/sys/dev/i2c/bmx280var.h deleted file mode 100644 index 19cc99ceb9f..00000000000 --- a/sys/dev/i2c/bmx280var.h +++ /dev/null @@ -1,88 +0,0 @@ -/* $NetBSD: bmx280var.h,v 1.2 2022/11/23 23:45:29 brad Exp $ */ - -/* - * Copyright (c) 2022 Brad Spencer - * - * 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; - int sc_waitfactor_t; - int sc_waitfactor_p; - int sc_waitfactor_h; -}; - -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 6e10edb7429..c1c61c5f4c9 100644 --- a/sys/dev/i2c/files.i2c +++ b/sys/dev/i2c/files.i2c @@ -1,4 +1,4 @@ -# $NetBSD: files.i2c,v 1.125 2022/11/21 21:24:01 brad Exp $ +# $NetBSD: files.i2c,v 1.126 2022/12/03 01:04:43 brad Exp $ obsolete defflag opt_i2cbus.h I2C_SCAN define i2cbus { } @@ -437,6 +437,5 @@ 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 +attach bmx280thp at iic with bmx280thpi2c +file dev/i2c/bmx280thpi2c.c bmx280thpi2c diff --git a/sys/dev/ic/bmx280.c b/sys/dev/ic/bmx280.c new file mode 100644 index 00000000000..8cd4dcf0b09 --- /dev/null +++ b/sys/dev/ic/bmx280.c @@ -0,0 +1,1013 @@ +/* $NetBSD: bmx280.c,v 1.1 2022/12/03 01:04:43 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer + * + * 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 +__KERNEL_RCSID(0, "$NetBSD: bmx280.c,v 1.1 2022/12/03 01:04:43 brad Exp $"); + +/* + * Common driver for the Bosch BMP280/BME280 temperature, humidity (sometimes) and + * (usually barometric) pressure sensor. Calls out to specific frontends to + * the move bits around. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +static void bmx280_store_raw_blob_tp(struct bmx280_sc *, uint8_t *); +static void bmx280_store_raw_blob_h(struct bmx280_sc *, uint8_t *); +void bmx280_attach(struct bmx280_sc *); +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 + +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[4] << 4) | (b[5] & 0x0F))); + 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]; +} + +static int +bmx280_sysctl_init(struct bmx280_sc *sc) +{ + int error; + const struct sysctlnode *cnode; + int sysctlroot_num, sysctlwait_num; + + sc->sc_func_attach = &bmx280_attach; + + 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; + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + 0, CTLTYPE_NODE, "waitfactor", + SYSCTL_DESCR("bmx280 wait factors"), NULL, 0, NULL, 0, CTL_HW, + sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + sysctlwait_num = cnode->sysctl_num; + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "t", + SYSCTL_DESCR("Temperature wait multiplier"), + bmx280_verify_sysctl, 0, &sc->sc_waitfactor_t, + 0, CTL_HW, sysctlroot_num, sysctlwait_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + + if ((error = sysctl_createv(&sc->sc_bmx280log, 0, NULL, &cnode, + CTLFLAG_READWRITE, CTLTYPE_INT, "p", + SYSCTL_DESCR("Pressure wait multiplier"), + bmx280_verify_sysctl, 0, &sc->sc_waitfactor_p, + 0, CTL_HW, sysctlroot_num, sysctlwait_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, "h", + SYSCTL_DESCR("Humidity wait multiplier"), + bmx280_verify_sysctl, 0, &sc->sc_waitfactor_h, + 0, CTL_HW, sysctlroot_num, sysctlwait_num, CTL_CREATE, CTL_EOL)) != 0) + return error; + } + + return 0; +} +void +bmx280_attach(struct bmx280_sc *sc) +{ + int error, i; + uint8_t reg, chip_id; + uint8_t buf[2]; + + 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_waitfactor_t = 6; + sc->sc_waitfactor_p = 2; + sc->sc_waitfactor_h = 2; + 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(sc->sc_dev, + "Unable to create sysmon structure\n"); + sc->sc_sme = NULL; + return; + } + + error = (*(sc->sc_func_acquire_bus))(sc); + if (error) { + aprint_error_dev(sc->sc_dev, "Could not acquire the bus: %d\n", + error); + goto out; + } + + buf[0] = BMX280_REGISTER_RESET; + buf[1] = BMX280_TRIGGER_RESET; + error = (*(sc->sc_func_write_register))(sc, buf, 2); + if (error) { + aprint_error_dev(sc->sc_dev, "Failed to reset chip: %d\n", + error); + } + + delay(30000); + + reg = BMX280_REGISTER_ID; + error = (*(sc->sc_func_read_register))(sc, reg, &chip_id, 1); + if (error) { + aprint_error_dev(sc->sc_dev, "Failed to read ID: %d\n", + error); + } + + delay(1000); + + 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(sc->sc_dev, "Can't setup sysctl tree (%d)\n", error); + goto out; + } + + uint8_t raw_blob_tp[24]; + reg = BMX280_REGISTER_DIG_T1; + error = (*(sc->sc_func_read_register))(sc, reg, raw_blob_tp, 24); + if (error) { + aprint_error_dev(sc->sc_dev, "Failed to read the calibration registers for tp: %d\n", + error); + } + + if (sc->sc_bmx280debug > 0) { + for(int _d = 0;_d < 24;_d++) { + DPRINTF(sc, 0, ("%s: %d %02x\n", + device_xname(sc->sc_dev), _d, raw_blob_tp[_d])); + } + } + + 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 = (*(sc->sc_func_read_register))(sc, reg, raw_blob_h, 1); + if (error) { + aprint_error_dev(sc->sc_dev, "Failed to read the calibration registers for h1: %d\n", + error); + } + + reg = BMX280_REGISTER_DIG_H2; + error = (*(sc->sc_func_read_register))(sc, reg, &raw_blob_h[1], 7); + if (error) { + aprint_error_dev(sc->sc_dev, "Failed to read the calibration registers for h2 - h6: %d\n", + error); + } + + if (sc->sc_bmx280debug > 0) { + for(int _d = 0;_d < 8;_d++) { + DPRINTF(sc, 0, ("%s: %d %02x\n", + device_xname(sc->sc_dev), _d, raw_blob_h[_d])); + } + } + + bmx280_store_raw_blob_h(sc,raw_blob_h); + } + + (*(sc->sc_func_release_bus))(sc); + + if (error != 0) { + aprint_error_dev(sc->sc_dev, "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(sc->sc_dev, + "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(sc->sc_dev, + "unable to register with sysmon\n"); + sysmon_envsys_destroy(sc->sc_sme); + sc->sc_sme = NULL; + return; + } + + aprint_normal_dev(sc->sc_dev, "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 BMP280 datasheet. The + * same algorithms are used with the BME280. + * + * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf + * + * Section 3.11.3, page 21 + * + */ + +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 = (*(sc->sc_func_write_register))(sc, cr, s); + if (error) { + DPRINTF(sc, 2, ("%s: write control registers: %d\n", + device_xname(sc->sc_dev), error)); + error = EINVAL; + } + + /* The wait needed is not well documented, so this is somewhat of a guess. + * There is an attempt with this to only wait as long as needed. + */ + + int p1, p2; + + p1 = (osrs_t_mask * sc->sc_waitfactor_t) + (osrs_p_mask * sc->sc_waitfactor_p); + if (sc->sc_has_humidity) { + p1 = p1 + (osrs_h_mask * sc->sc_waitfactor_h); + } + p2 = mstohz(p1); + if (p2 == 0) { + p2 = 1; + } + /* Be careful with this... the print itself will cause extra delay */ + DPRINTF(sc, 2, ("%s: p1: %d ; %d\n", + device_xname(sc->sc_dev), p1, p2)); + kpause("b280mea",false,p2,NULL); + + 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(1000); + ierror = (*(sc->sc_func_read_register))(sc, 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 = (*(sc->sc_func_read_register))(sc, 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 = (*(sc->sc_func_acquire_bus))(sc); + 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)); + + uint64_t q; + + q = (uint64_t)comp_hum * 1000000; + DPRINTF(sc, 1, ("%s: Refresh humidity Q 1: %jd\n", __func__, (uintmax_t)q)); + q = q / 1024; + + DPRINTF(sc, 1, ("%s: Refresh humidity Q 2: %jd\n", __func__, (uintmax_t)q)); + + edata->value_cur = (uint32_t) 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)); + } + + (*(sc->sc_func_release_bus))(sc); +out: + mutex_exit(&sc->sc_mutex); +} + +MODULE(MODULE_CLASS_DRIVER, bmx280thp, NULL); + +#ifdef _MODULE +CFDRIVER_DECL(bmx280thp, DV_DULL, NULL); +#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/ic/bmx280reg.h b/sys/dev/ic/bmx280reg.h new file mode 100644 index 00000000000..df07bd07ca7 --- /dev/null +++ b/sys/dev/ic/bmx280reg.h @@ -0,0 +1,96 @@ +/* $NetBSD: bmx280reg.h,v 1.1 2022/12/03 01:04:43 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer + * + * 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_IC_BMX280REG_H_ +#define _DEV_IC_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/ic/bmx280var.h b/sys/dev/ic/bmx280var.h new file mode 100644 index 00000000000..b303465fc42 --- /dev/null +++ b/sys/dev/ic/bmx280var.h @@ -0,0 +1,94 @@ +/* $NetBSD: bmx280var.h,v 1.1 2022/12/03 01:04:43 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer + * + * 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_IC_BMX280VAR_H_ +#define _DEV_IC_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; + struct spi_handle *sc_sh; + 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; + int sc_waitfactor_t; + int sc_waitfactor_p; + int sc_waitfactor_h; + void (*sc_func_attach)(struct bmx280_sc *); + int (*sc_func_acquire_bus)(struct bmx280_sc *); + void (*sc_func_release_bus)(struct bmx280_sc *); + int (*sc_func_read_register)(struct bmx280_sc *, uint8_t, uint8_t *, size_t); + int (*sc_func_write_register)(struct bmx280_sc *, uint8_t *, size_t); +}; + +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/spi/bmx280thpspi.c b/sys/dev/spi/bmx280thpspi.c new file mode 100644 index 00000000000..f3262ad5250 --- /dev/null +++ b/sys/dev/spi/bmx280thpspi.c @@ -0,0 +1,205 @@ +/* $NetBSD: bmx280thpspi.c,v 1.1 2022/12/03 01:04:43 brad Exp $ */ + +/* + * Copyright (c) 2022 Brad Spencer + * + * 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 +__KERNEL_RCSID(0, "$NetBSD: bmx280thpspi.c,v 1.1 2022/12/03 01:04:43 brad Exp $"); + +/* + * SPI driver for the Bosch BMP280 / BME280 sensor. + * Uses the common bmx280thp driver to do the real work. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern void bmx280_attach(struct bmx280_sc *); + +static int bmx280thpspi_match(device_t, cfdata_t, void *); +static void bmx280thpspi_attach(device_t, device_t, void *); +static int bmx280thpspi_detach(device_t, int); + +#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(bmx280thpspi, sizeof(struct bmx280_sc), + bmx280thpspi_match, bmx280thpspi_attach, bmx280thpspi_detach, NULL); + +/* The SPI interface of the chip, assuming that it has managed to get into that + * mode to start with, is pretty simple. Simply send the register MINUS the 7th + * bit which will be 1 and then do as many reads as you want. The chip will + * auto increment for you. + * + * The delays are only hinted at in the data sheet. + */ + +static int +bmx280thpspi_read_reg_direct(struct spi_handle *sh, uint8_t reg, + uint8_t *buf, size_t rlen) +{ + int err = 0; + uint8_t rreg = reg | 0x80; + + if (buf != NULL) { + err = spi_send_recv(sh, 1, &rreg, + rlen, buf); + } else { + err = spi_send(sh, 1, &rreg); + } + + return err; +} + +static int +bmx280thpspi_read_reg(struct bmx280_sc *sc, uint8_t reg, uint8_t *buf, size_t rlen) +{ + return bmx280thpspi_read_reg_direct(sc->sc_sh, reg, buf, rlen); +} + +/* SPI writes to this device are normal enough. You send the register + * you want making sure that the high bit, 0x80, is clear and then the + * data. These pairs can be repeated as many times as you like. + */ +static int +bmx280thpspi_write_reg_direct(struct spi_handle *sh, uint8_t *buf, size_t slen) +{ + int err = 0; + int i; + + /* XXX - + this is probably BAD thing to do... but we must insure that the + registers have a cleared bit.. otherwise it is a read .... + */ + + for(i = 0; i < slen;i+=2) { + buf[i] = buf[i] & 0x7F; + } + + err = spi_send(sh, slen, buf); + + return err; +} + +static int +bmx280thpspi_write_reg(struct bmx280_sc *sc, uint8_t *buf, size_t slen) +{ + return bmx280thpspi_write_reg_direct(sc->sc_sh, buf, slen); +} + +/* These are to satisfy the common code */ +static int +bmx280thpspi_acquire_bus(struct bmx280_sc *sc) +{ + return 0; +} + +static void +bmx280thpspi_release_bus(struct bmx280_sc *sc) +{ + return; +} + +/* Nothing more is done here. Assumptions on whether or not + * the SPI interface is set up may not be proper.... for better + * or worse... and there is setting that are needed such as the + * SPI mode and bus speed that really should not be done here, so + * any active match might not work anyway. + */ +static int +bmx280thpspi_match(device_t parent, cfdata_t match, void *aux) +{ + const bool matchdebug = false; + + if (matchdebug) { + printf("Trying to match\n"); + } + + return 1; +} + +static void +bmx280thpspi_attach(device_t parent, device_t self, void *aux) +{ + struct bmx280_sc *sc; + struct spi_attach_args *sa; + int error; + + sa = aux; + sc = device_private(self); + + sc->sc_dev = self; + sc->sc_sh = sa->sa_handle; + sc->sc_bmx280debug = 0; + sc->sc_func_acquire_bus = &bmx280thpspi_acquire_bus; + sc->sc_func_release_bus = &bmx280thpspi_release_bus; + sc->sc_func_read_register = &bmx280thpspi_read_reg; + sc->sc_func_write_register = &bmx280thpspi_write_reg; + + /* Configure for 1MHz and SPI mode 0 according to the data sheet. + * The chip will actually handle a number of different modes and + * can go a lot faster, just use this for now... + */ + error = spi_configure(self, sa->sa_handle, SPI_MODE_0, 1000000); + if (error) { + return; + } + + /* Please note that if the pins are not set up for SPI, the attachment + * will probably not work out. + */ + bmx280_attach(sc); + + return; +} + +/* These really do not do a whole lot, as SPI devices do not seem to work + * as modules. + */ +static int +bmx280thpspi_detach(device_t self, int flags) +{ + struct bmx280_sc *sc; + + sc = device_private(self); + + mutex_destroy(&sc->sc_mutex); + + return 0; +} diff --git a/sys/dev/spi/files.spi b/sys/dev/spi/files.spi index e73dde62112..f7807089bfe 100644 --- a/sys/dev/spi/files.spi +++ b/sys/dev/spi/files.spi @@ -1,4 +1,4 @@ -# $NetBSD: files.spi,v 1.9 2022/01/17 16:31:23 thorpej Exp $ +# $NetBSD: files.spi,v 1.10 2022/12/03 01:04:43 brad Exp $ define spibus { } @@ -47,3 +47,7 @@ file dev/spi/mcp3k.c mcp3kadc # Sparkfun Serial motor controller attach scmd at spi with scmdspi file dev/spi/scmdspi.c scmdspi + +# Bosch BMP280 / BME280 sensor +attach bmx280thp at spi with bmx280thpspi +file dev/spi/bmx280thpspi.c bmx280thpspi -- cgit