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