Home | History | Annotate | Line # | Download | only in spi
      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