1 /* $NetBSD: bmx280thpspi.c,v 1.7 2025/09/13 16:16:40 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2022 Brad Spencer <brad (at) anduin.eldar.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/cdefs.h> 20 __KERNEL_RCSID(0, "$NetBSD: bmx280thpspi.c,v 1.7 2025/09/13 16:16:40 thorpej Exp $"); 21 22 /* 23 * SPI driver for the Bosch BMP280 / BME280 sensor. 24 * Uses the common bmx280thp driver to do the real work. 25 */ 26 27 #include <sys/param.h> 28 #include <sys/systm.h> 29 #include <sys/kernel.h> 30 #include <sys/device.h> 31 #include <sys/module.h> 32 #include <sys/conf.h> 33 #include <sys/sysctl.h> 34 #include <sys/mutex.h> 35 #include <sys/condvar.h> 36 #include <sys/pool.h> 37 #include <sys/kmem.h> 38 39 #include <dev/sysmon/sysmonvar.h> 40 #include <dev/i2c/i2cvar.h> 41 #include <dev/spi/spivar.h> 42 #include <dev/ic/bmx280reg.h> 43 #include <dev/ic/bmx280var.h> 44 45 struct bmx280_spi_softc { 46 struct bmx280_sc sc_bmx280; 47 spi_handle_t sc_sh; 48 }; 49 50 #define BMX280_TO_SPI(sc) \ 51 container_of((sc), struct bmx280_spi_softc, sc_bmx280) 52 53 static int bmx280thpspi_match(device_t, cfdata_t, void *); 54 static void bmx280thpspi_attach(device_t, device_t, void *); 55 static int bmx280thpspi_detach(device_t, int); 56 57 CFATTACH_DECL_NEW(bmx280thpspi, sizeof(struct bmx280_spi_softc), 58 bmx280thpspi_match, bmx280thpspi_attach, bmx280thpspi_detach, NULL); 59 60 /* The SPI interface of the chip, assuming that it has managed to get into that 61 * mode to start with, is pretty simple. Simply send the register MINUS the 7th 62 * bit which will be 1 and then do as many reads as you want. The chip will 63 * auto increment for you. 64 * 65 * The delays are only hinted at in the data sheet. 66 */ 67 68 static int 69 bmx280thpspi_read_reg_direct(spi_handle_t sh, uint8_t reg, uint8_t *buf, 70 size_t rlen) 71 { 72 int err = 0; 73 uint8_t rreg = reg | 0x80; 74 75 if (buf != NULL) { 76 err = spi_send_recv(sh, 1, &rreg, 77 rlen, buf); 78 } else { 79 err = spi_send(sh, 1, &rreg); 80 } 81 82 return err; 83 } 84 85 static int 86 bmx280thpspi_read_reg(struct bmx280_sc *sc, uint8_t reg, uint8_t *buf, 87 size_t rlen) 88 { 89 struct bmx280_spi_softc *ssc = BMX280_TO_SPI(sc); 90 91 return bmx280thpspi_read_reg_direct(ssc->sc_sh, reg, buf, rlen); 92 } 93 94 /* SPI writes to this device are normal enough. You send the register 95 * you want making sure that the high bit, 0x80, is clear and then the 96 * data. These pairs can be repeated as many times as you like. 97 */ 98 static int 99 bmx280thpspi_write_reg_direct(spi_handle_t sh, uint8_t *buf, size_t slen) 100 { 101 int err = 0; 102 int i; 103 104 /* XXX - 105 this is probably BAD thing to do... but we must insure that the 106 registers have a cleared bit.. otherwise it is a read .... 107 */ 108 109 for(i = 0; i < slen;i+=2) { 110 buf[i] = buf[i] & 0x7F; 111 } 112 113 err = spi_send(sh, slen, buf); 114 115 return err; 116 } 117 118 static int 119 bmx280thpspi_write_reg(struct bmx280_sc *sc, uint8_t *buf, size_t slen) 120 { 121 struct bmx280_spi_softc *ssc = BMX280_TO_SPI(sc); 122 123 return bmx280thpspi_write_reg_direct(ssc->sc_sh, buf, slen); 124 } 125 126 /* These are to satisfy the common code */ 127 static int 128 bmx280thpspi_acquire_bus(struct bmx280_sc *sc __unused) 129 { 130 return 0; 131 } 132 133 static void 134 bmx280thpspi_release_bus(struct bmx280_sc *sc __unused) 135 { 136 return; 137 } 138 139 static const struct bmx280_accessfuncs bmx280_spi_accessfuncs = { 140 .acquire_bus = bmx280thpspi_acquire_bus, 141 .release_bus = bmx280thpspi_release_bus, 142 .read_reg = bmx280thpspi_read_reg, 143 .write_reg = bmx280thpspi_write_reg, 144 }; 145 146 /* Nothing more is done here. Assumptions on whether or not 147 * the SPI interface is set up may not be proper.... for better 148 * or worse... and there is setting that are needed such as the 149 * SPI mode and bus speed that really should not be done here, so 150 * any active match might not work anyway. 151 */ 152 static int 153 bmx280thpspi_match(device_t parent, cfdata_t match, void *aux) 154 { 155 struct spi_attach_args *sa = aux; 156 int match_result; 157 158 if (spi_use_direct_match(sa, bmx280_compat_data, &match_result)) { 159 return match_result; 160 } 161 162 return SPI_MATCH_DEFAULT; 163 } 164 165 static void 166 bmx280thpspi_attach(device_t parent, device_t self, void *aux) 167 { 168 struct bmx280_spi_softc *ssc = device_private(self); 169 struct bmx280_sc *sc = &ssc->sc_bmx280; 170 struct spi_attach_args *sa = aux; 171 int error; 172 173 sc->sc_dev = self; 174 sc->sc_funcs = &bmx280_spi_accessfuncs; 175 176 ssc->sc_sh = sa->sa_handle; 177 178 /* Configure for 1MHz and SPI mode 0 according to the data sheet. 179 * The chip will actually handle a number of different modes and 180 * can go a lot faster, just use this for now... 181 */ 182 error = spi_configure(self, sa->sa_handle, SPI_MODE_0, SPI_FREQ_MHz(1)); 183 if (error) { 184 return; 185 } 186 187 /* Please note that if the pins are not set up for SPI, the attachment 188 * will probably not work out. 189 */ 190 bmx280_attach(sc); 191 } 192 193 static int 194 bmx280thpspi_detach(device_t self, int flags) 195 { 196 struct bmx280_spi_softc *ssc = device_private(self); 197 198 return bmx280_detach(&ssc->sc_bmx280, flags); 199 } 200