bmx280thpspi.c revision 1.6
11.6Sthorpej/*	$NetBSD: bmx280thpspi.c,v 1.6 2025/09/13 15:55:45 thorpej Exp $	*/
21.1Sbrad
31.1Sbrad/*
41.1Sbrad * Copyright (c) 2022 Brad Spencer <brad@anduin.eldar.org>
51.1Sbrad *
61.1Sbrad * Permission to use, copy, modify, and distribute this software for any
71.1Sbrad * purpose with or without fee is hereby granted, provided that the above
81.1Sbrad * copyright notice and this permission notice appear in all copies.
91.1Sbrad *
101.1Sbrad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
111.1Sbrad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
121.1Sbrad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
131.1Sbrad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
141.1Sbrad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
151.1Sbrad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
161.1Sbrad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
171.1Sbrad */
181.1Sbrad
191.1Sbrad#include <sys/cdefs.h>
201.6Sthorpej__KERNEL_RCSID(0, "$NetBSD: bmx280thpspi.c,v 1.6 2025/09/13 15:55:45 thorpej Exp $");
211.1Sbrad
221.1Sbrad/*
231.1Sbrad * SPI driver for the Bosch BMP280 / BME280 sensor.
241.1Sbrad * Uses the common bmx280thp driver to do the real work.
251.1Sbrad*/
261.1Sbrad
271.1Sbrad#include <sys/param.h>
281.1Sbrad#include <sys/systm.h>
291.1Sbrad#include <sys/kernel.h>
301.1Sbrad#include <sys/device.h>
311.1Sbrad#include <sys/module.h>
321.1Sbrad#include <sys/conf.h>
331.1Sbrad#include <sys/sysctl.h>
341.1Sbrad#include <sys/mutex.h>
351.1Sbrad#include <sys/condvar.h>
361.1Sbrad#include <sys/pool.h>
371.1Sbrad#include <sys/kmem.h>
381.1Sbrad
391.1Sbrad#include <dev/sysmon/sysmonvar.h>
401.1Sbrad#include <dev/i2c/i2cvar.h>
411.1Sbrad#include <dev/spi/spivar.h>
421.1Sbrad#include <dev/ic/bmx280reg.h>
431.1Sbrad#include <dev/ic/bmx280var.h>
441.1Sbrad
451.6Sthorpejstruct bmx280_spi_softc {
461.6Sthorpej	struct bmx280_sc	sc_bmx280;
471.6Sthorpej	spi_handle_t		sc_sh;
481.6Sthorpej};
491.6Sthorpej
501.6Sthorpej#define	BMX280_TO_SPI(sc)	\
511.6Sthorpej	container_of((sc), struct bmx280_spi_softc, sc_bmx280)
521.6Sthorpej
531.4Sthorpejstatic const struct device_compatible_entry compat_data[] = {
541.4Sthorpej	{ .compat = "bosch,bmp280" },
551.4Sthorpej	{ .compat = "bosch,bme280" },
561.4Sthorpej#if 0
571.4Sthorpej	/*
581.4Sthorpej	 * XXX Should also add support for:
591.4Sthorpej	 *	bosch,bmp085
601.4Sthorpej	 *	bosch,bmp180
611.4Sthorpej	 *	bosch,bmp380
621.4Sthorpej	 *	bosch,bmp580
631.4Sthorpej	 */
641.4Sthorpej#endif
651.4Sthorpej	DEVICE_COMPAT_EOL
661.4Sthorpej};
671.4Sthorpej
681.1Sbradstatic int 	bmx280thpspi_match(device_t, cfdata_t, void *);
691.1Sbradstatic void 	bmx280thpspi_attach(device_t, device_t, void *);
701.1Sbradstatic int 	bmx280thpspi_detach(device_t, int);
711.1Sbrad
721.1Sbrad#define BMX280_DEBUG
731.1Sbrad#ifdef BMX280_DEBUG
741.1Sbrad#define DPRINTF(s, l, x) \
751.1Sbrad    do { \
761.1Sbrad	if (l <= s->sc_bmx280debug) \
771.1Sbrad	    printf x; \
781.1Sbrad    } while (/*CONSTCOND*/0)
791.1Sbrad#else
801.1Sbrad#define DPRINTF(s, l, x)
811.1Sbrad#endif
821.1Sbrad
831.6SthorpejCFATTACH_DECL_NEW(bmx280thpspi, sizeof(struct bmx280_spi_softc),
841.1Sbrad    bmx280thpspi_match, bmx280thpspi_attach, bmx280thpspi_detach, NULL);
851.1Sbrad
861.1Sbrad/* The SPI interface of the chip, assuming that it has managed to get into that
871.1Sbrad * mode to start with, is pretty simple.  Simply send the register MINUS the 7th
881.1Sbrad * bit which will be 1 and then do as many reads as you want.  The chip will
891.1Sbrad * auto increment for you.
901.1Sbrad *
911.1Sbrad * The delays are only hinted at in the data sheet.
921.1Sbrad */
931.1Sbrad
941.1Sbradstatic int
951.6Sthorpejbmx280thpspi_read_reg_direct(spi_handle_t sh, uint8_t reg, uint8_t *buf,
961.6Sthorpej    size_t rlen)
971.1Sbrad{
981.1Sbrad	int err = 0;
991.1Sbrad	uint8_t rreg = reg | 0x80;
1001.1Sbrad
1011.1Sbrad	if (buf != NULL) {
1021.1Sbrad		err = spi_send_recv(sh, 1, &rreg,
1031.1Sbrad		    rlen, buf);
1041.1Sbrad	} else {
1051.1Sbrad		err = spi_send(sh, 1, &rreg);
1061.1Sbrad	}
1071.1Sbrad
1081.1Sbrad	return err;
1091.1Sbrad}
1101.1Sbrad
1111.1Sbradstatic int
1121.6Sthorpejbmx280thpspi_read_reg(struct bmx280_sc *sc, uint8_t reg, uint8_t *buf,
1131.6Sthorpej    size_t rlen)
1141.1Sbrad{
1151.6Sthorpej	struct bmx280_spi_softc *ssc = BMX280_TO_SPI(sc);
1161.6Sthorpej
1171.6Sthorpej	return bmx280thpspi_read_reg_direct(ssc->sc_sh, reg, buf, rlen);
1181.1Sbrad}
1191.1Sbrad
1201.1Sbrad/* SPI writes to this device are normal enough.  You send the register
1211.1Sbrad * you want making sure that the high bit, 0x80, is clear and then the
1221.1Sbrad * data.  These pairs can be repeated as many times as you like.
1231.1Sbrad */
1241.1Sbradstatic int
1251.5Sthorpejbmx280thpspi_write_reg_direct(spi_handle_t sh, uint8_t *buf, size_t slen)
1261.1Sbrad{
1271.1Sbrad	int err = 0;
1281.1Sbrad	int i;
1291.1Sbrad
1301.1Sbrad	/* XXX -
1311.1Sbrad	   this is probably  BAD thing to do... but we must insure that the
1321.1Sbrad	   registers have a cleared bit.. otherwise it is a read ....
1331.1Sbrad	*/
1341.1Sbrad
1351.1Sbrad	for(i = 0; i < slen;i+=2) {
1361.1Sbrad		buf[i] = buf[i] & 0x7F;
1371.1Sbrad	}
1381.1Sbrad
1391.1Sbrad	err = spi_send(sh, slen, buf);
1401.1Sbrad
1411.1Sbrad	return err;
1421.1Sbrad}
1431.1Sbrad
1441.1Sbradstatic int
1451.1Sbradbmx280thpspi_write_reg(struct bmx280_sc *sc, uint8_t *buf, size_t slen)
1461.1Sbrad{
1471.6Sthorpej	struct bmx280_spi_softc *ssc = BMX280_TO_SPI(sc);
1481.6Sthorpej
1491.6Sthorpej	return bmx280thpspi_write_reg_direct(ssc->sc_sh, buf, slen);
1501.1Sbrad}
1511.1Sbrad
1521.1Sbrad/* These are to satisfy the common code */
1531.1Sbradstatic int
1541.6Sthorpejbmx280thpspi_acquire_bus(struct bmx280_sc *sc __unused)
1551.1Sbrad{
1561.1Sbrad	return 0;
1571.1Sbrad}
1581.1Sbrad
1591.1Sbradstatic void
1601.6Sthorpejbmx280thpspi_release_bus(struct bmx280_sc *sc __unused)
1611.1Sbrad{
1621.1Sbrad	return;
1631.1Sbrad}
1641.1Sbrad
1651.6Sthorpejstatic const struct bmx280_accessfuncs bmx280_spi_accessfuncs = {
1661.6Sthorpej	.acquire_bus	=	bmx280thpspi_acquire_bus,
1671.6Sthorpej	.release_bus	=	bmx280thpspi_release_bus,
1681.6Sthorpej	.read_reg	=	bmx280thpspi_read_reg,
1691.6Sthorpej	.write_reg	=	bmx280thpspi_write_reg,
1701.6Sthorpej};
1711.6Sthorpej
1721.1Sbrad/* Nothing more is done here.  Assumptions on whether or not
1731.1Sbrad * the SPI interface is set up may not be proper.... for better
1741.1Sbrad * or worse... and there is setting that are needed such as the
1751.1Sbrad * SPI mode and bus speed that really should not be done here, so
1761.1Sbrad * any active match might not work anyway.
1771.1Sbrad */
1781.1Sbradstatic int
1791.1Sbradbmx280thpspi_match(device_t parent, cfdata_t match, void *aux)
1801.1Sbrad{
1811.4Sthorpej	struct spi_attach_args *sa = aux;
1821.4Sthorpej	int match_result;
1831.1Sbrad
1841.4Sthorpej	if (spi_use_direct_match(sa, compat_data, &match_result)) {
1851.4Sthorpej		return match_result;
1861.1Sbrad	}
1871.1Sbrad
1881.3Sthorpej	return SPI_MATCH_DEFAULT;
1891.1Sbrad}
1901.1Sbrad
1911.1Sbradstatic void
1921.1Sbradbmx280thpspi_attach(device_t parent, device_t self, void *aux)
1931.1Sbrad{
1941.6Sthorpej	struct bmx280_spi_softc *ssc = device_private(self);
1951.6Sthorpej	struct bmx280_sc *sc = &ssc->sc_bmx280;
1961.6Sthorpej	struct spi_attach_args *sa = aux;
1971.1Sbrad	int error;
1981.1Sbrad
1991.6Sthorpej	sc->sc_dev = self;
2001.6Sthorpej	sc->sc_funcs = &bmx280_spi_accessfuncs;
2011.6Sthorpej
2021.6Sthorpej	ssc->sc_sh = sa->sa_handle;
2031.1Sbrad
2041.1Sbrad	sc->sc_bmx280debug = 0;
2051.1Sbrad
2061.1Sbrad	/* Configure for 1MHz and SPI mode 0 according to the data sheet.
2071.1Sbrad	 * The chip will actually handle a number of different modes and
2081.1Sbrad	 * can go a lot faster, just use this for now...
2091.1Sbrad	 */
2101.2Sthorpej	error = spi_configure(self, sa->sa_handle, SPI_MODE_0, SPI_FREQ_MHz(1));
2111.1Sbrad	if (error) {
2121.1Sbrad		return;
2131.1Sbrad	}
2141.1Sbrad
2151.4Sthorpej	/*
2161.4Sthorpej	 * XXX Need to get this data from the device tree:
2171.4Sthorpej	 *
2181.4Sthorpej	 *	vddd-supply	(a regulator)
2191.4Sthorpej	 *	vdda-supply	(a regulator)
2201.4Sthorpej	 *	reset-gpios	(a gpio, active-low reset)
2211.4Sthorpej	 */
2221.4Sthorpej
2231.1Sbrad	/* Please note that if the pins are not set up for SPI, the attachment
2241.1Sbrad	 * will probably not work out.
2251.1Sbrad	 */
2261.1Sbrad	bmx280_attach(sc);
2271.1Sbrad}
2281.1Sbrad
2291.1Sbrad/* These really do not do a whole lot, as SPI devices do not seem to work
2301.1Sbrad * as modules.
2311.1Sbrad */
2321.1Sbradstatic int
2331.1Sbradbmx280thpspi_detach(device_t self, int flags)
2341.1Sbrad{
2351.1Sbrad	struct bmx280_sc *sc;
2361.1Sbrad
2371.1Sbrad	sc = device_private(self);
2381.1Sbrad
2391.1Sbrad	mutex_destroy(&sc->sc_mutex);
2401.1Sbrad
2411.1Sbrad	return 0;
2421.1Sbrad}
243