bmx280thpspi.c revision 1.7
1/*	$NetBSD: bmx280thpspi.c,v 1.7 2025/09/13 16:16:40 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.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
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 int 	bmx280thpspi_match(device_t, cfdata_t, void *);
54static void 	bmx280thpspi_attach(device_t, device_t, void *);
55static int 	bmx280thpspi_detach(device_t, int);
56
57CFATTACH_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
68static int
69bmx280thpspi_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
85static int
86bmx280thpspi_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 */
98static int
99bmx280thpspi_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
118static int
119bmx280thpspi_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 */
127static int
128bmx280thpspi_acquire_bus(struct bmx280_sc *sc __unused)
129{
130	return 0;
131}
132
133static void
134bmx280thpspi_release_bus(struct bmx280_sc *sc __unused)
135{
136	return;
137}
138
139static 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 */
152static int
153bmx280thpspi_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
165static void
166bmx280thpspi_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
193static int
194bmx280thpspi_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