bmx280thpspi.c revision 1.6
1/*	$NetBSD: bmx280thpspi.c,v 1.6 2025/09/13 15:55:45 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 2022 Brad Spencer <brad@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.6 2025/09/13 15:55:45 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
45struct 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
53static const struct device_compatible_entry compat_data[] = {
54	{ .compat = "bosch,bmp280" },
55	{ .compat = "bosch,bme280" },
56#if 0
57	/*
58	 * XXX Should also add support for:
59	 *	bosch,bmp085
60	 *	bosch,bmp180
61	 *	bosch,bmp380
62	 *	bosch,bmp580
63	 */
64#endif
65	DEVICE_COMPAT_EOL
66};
67
68static int 	bmx280thpspi_match(device_t, cfdata_t, void *);
69static void 	bmx280thpspi_attach(device_t, device_t, void *);
70static int 	bmx280thpspi_detach(device_t, int);
71
72#define BMX280_DEBUG
73#ifdef BMX280_DEBUG
74#define DPRINTF(s, l, x) \
75    do { \
76	if (l <= s->sc_bmx280debug) \
77	    printf x; \
78    } while (/*CONSTCOND*/0)
79#else
80#define DPRINTF(s, l, x)
81#endif
82
83CFATTACH_DECL_NEW(bmx280thpspi, sizeof(struct bmx280_spi_softc),
84    bmx280thpspi_match, bmx280thpspi_attach, bmx280thpspi_detach, NULL);
85
86/* The SPI interface of the chip, assuming that it has managed to get into that
87 * mode to start with, is pretty simple.  Simply send the register MINUS the 7th
88 * bit which will be 1 and then do as many reads as you want.  The chip will
89 * auto increment for you.
90 *
91 * The delays are only hinted at in the data sheet.
92 */
93
94static int
95bmx280thpspi_read_reg_direct(spi_handle_t sh, uint8_t reg, uint8_t *buf,
96    size_t rlen)
97{
98	int err = 0;
99	uint8_t rreg = reg | 0x80;
100
101	if (buf != NULL) {
102		err = spi_send_recv(sh, 1, &rreg,
103		    rlen, buf);
104	} else {
105		err = spi_send(sh, 1, &rreg);
106	}
107
108	return err;
109}
110
111static int
112bmx280thpspi_read_reg(struct bmx280_sc *sc, uint8_t reg, uint8_t *buf,
113    size_t rlen)
114{
115	struct bmx280_spi_softc *ssc = BMX280_TO_SPI(sc);
116
117	return bmx280thpspi_read_reg_direct(ssc->sc_sh, reg, buf, rlen);
118}
119
120/* SPI writes to this device are normal enough.  You send the register
121 * you want making sure that the high bit, 0x80, is clear and then the
122 * data.  These pairs can be repeated as many times as you like.
123 */
124static int
125bmx280thpspi_write_reg_direct(spi_handle_t sh, uint8_t *buf, size_t slen)
126{
127	int err = 0;
128	int i;
129
130	/* XXX -
131	   this is probably  BAD thing to do... but we must insure that the
132	   registers have a cleared bit.. otherwise it is a read ....
133	*/
134
135	for(i = 0; i < slen;i+=2) {
136		buf[i] = buf[i] & 0x7F;
137	}
138
139	err = spi_send(sh, slen, buf);
140
141	return err;
142}
143
144static int
145bmx280thpspi_write_reg(struct bmx280_sc *sc, uint8_t *buf, size_t slen)
146{
147	struct bmx280_spi_softc *ssc = BMX280_TO_SPI(sc);
148
149	return bmx280thpspi_write_reg_direct(ssc->sc_sh, buf, slen);
150}
151
152/* These are to satisfy the common code */
153static int
154bmx280thpspi_acquire_bus(struct bmx280_sc *sc __unused)
155{
156	return 0;
157}
158
159static void
160bmx280thpspi_release_bus(struct bmx280_sc *sc __unused)
161{
162	return;
163}
164
165static const struct bmx280_accessfuncs bmx280_spi_accessfuncs = {
166	.acquire_bus	=	bmx280thpspi_acquire_bus,
167	.release_bus	=	bmx280thpspi_release_bus,
168	.read_reg	=	bmx280thpspi_read_reg,
169	.write_reg	=	bmx280thpspi_write_reg,
170};
171
172/* Nothing more is done here.  Assumptions on whether or not
173 * the SPI interface is set up may not be proper.... for better
174 * or worse... and there is setting that are needed such as the
175 * SPI mode and bus speed that really should not be done here, so
176 * any active match might not work anyway.
177 */
178static int
179bmx280thpspi_match(device_t parent, cfdata_t match, void *aux)
180{
181	struct spi_attach_args *sa = aux;
182	int match_result;
183
184	if (spi_use_direct_match(sa, compat_data, &match_result)) {
185		return match_result;
186	}
187
188	return SPI_MATCH_DEFAULT;
189}
190
191static void
192bmx280thpspi_attach(device_t parent, device_t self, void *aux)
193{
194	struct bmx280_spi_softc *ssc = device_private(self);
195	struct bmx280_sc *sc = &ssc->sc_bmx280;
196	struct spi_attach_args *sa = aux;
197	int error;
198
199	sc->sc_dev = self;
200	sc->sc_funcs = &bmx280_spi_accessfuncs;
201
202	ssc->sc_sh = sa->sa_handle;
203
204	sc->sc_bmx280debug = 0;
205
206	/* Configure for 1MHz and SPI mode 0 according to the data sheet.
207	 * The chip will actually handle a number of different modes and
208	 * can go a lot faster, just use this for now...
209	 */
210	error = spi_configure(self, sa->sa_handle, SPI_MODE_0, SPI_FREQ_MHz(1));
211	if (error) {
212		return;
213	}
214
215	/*
216	 * XXX Need to get this data from the device tree:
217	 *
218	 *	vddd-supply	(a regulator)
219	 *	vdda-supply	(a regulator)
220	 *	reset-gpios	(a gpio, active-low reset)
221	 */
222
223	/* Please note that if the pins are not set up for SPI, the attachment
224	 * will probably not work out.
225	 */
226	bmx280_attach(sc);
227}
228
229/* These really do not do a whole lot, as SPI devices do not seem to work
230 * as modules.
231 */
232static int
233bmx280thpspi_detach(device_t self, int flags)
234{
235	struct bmx280_sc *sc;
236
237	sc = device_private(self);
238
239	mutex_destroy(&sc->sc_mutex);
240
241	return 0;
242}
243