xref: /netbsd-src/sys/dev/i2c/bmx280thpi2c.c (revision 50bb9ed19fb685948d30ad3c7186b250d750fe26)
1 /*	$NetBSD: bmx280thpi2c.c,v 1.1 2022/12/03 01:04:43 brad 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: bmx280thpi2c.c,v 1.1 2022/12/03 01:04:43 brad Exp $");
21 
22 /*
23  * I2C 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 extern void	bmx280_attach(struct bmx280_sc *);
46 
47 static int 	bmx280thpi2c_poke(i2c_tag_t, i2c_addr_t, bool);
48 static int 	bmx280thpi2c_match(device_t, cfdata_t, void *);
49 static void 	bmx280thpi2c_attach(device_t, device_t, void *);
50 static int 	bmx280thpi2c_detach(device_t, int);
51 
52 #define BMX280_DEBUG
53 #ifdef BMX280_DEBUG
54 #define DPRINTF(s, l, x) \
55     do { \
56 	if (l <= s->sc_bmx280debug) \
57 	    printf x; \
58     } while (/*CONSTCOND*/0)
59 #else
60 #define DPRINTF(s, l, x)
61 #endif
62 
63 CFATTACH_DECL_NEW(bmx280thpi2c, sizeof(struct bmx280_sc),
64     bmx280thpi2c_match, bmx280thpi2c_attach, bmx280thpi2c_detach, NULL);
65 
66 /* For the BMX280, a read consists of writing on the I2C bus
67  * a I2C START, I2C SLAVE address, then the starting register.
68  * If that works, then following will be another I2C START,
69  * I2C SLAVE address, followed by as many I2C reads that is
70  * desired and then a I2C STOP
71  */
72 
73 static int
bmx280thpi2c_read_register_direct(i2c_tag_t tag,i2c_addr_t addr,uint8_t reg,uint8_t * buf,size_t blen)74 bmx280thpi2c_read_register_direct(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg,
75     uint8_t *buf, size_t blen)
76 {
77 	int error;
78 
79 	error = iic_exec(tag,I2C_OP_WRITE,addr,&reg,1,NULL,0,0);
80 
81 	if (error == 0) {
82 		error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,
83 		    buf,blen,0);
84 	}
85 
86 	return error;
87 }
88 
89 static int
bmx280thpi2c_read_register(struct bmx280_sc * sc,uint8_t reg,uint8_t * buf,size_t blen)90 bmx280thpi2c_read_register(struct bmx280_sc *sc, uint8_t reg,
91     uint8_t *buf, size_t blen)
92 {
93 	int error;
94 
95 	KASSERT(blen > 0);
96 
97 	error = bmx280thpi2c_read_register_direct(sc->sc_tag, sc->sc_addr, reg,
98 	    buf, blen);
99 
100 	return error;
101 }
102 
103 /* For the BMX280, a write consists of sending a I2C START, I2C SLAVE
104  * address and then pairs of registers and data until a I2C STOP is
105  * sent.
106  */
107 
108 static int
bmx280thpi2c_write_register(struct bmx280_sc * sc,uint8_t * buf,size_t blen)109 bmx280thpi2c_write_register(struct bmx280_sc *sc,
110     uint8_t *buf, size_t blen)
111 {
112 	int error;
113 
114 	KASSERT(blen > 0);
115 	/* XXX - there should be a KASSERT for blen at least
116 	   being an even number */
117 
118 	error = iic_exec(sc->sc_tag,I2C_OP_WRITE_WITH_STOP,sc->sc_addr,NULL,0,
119 	    buf,blen,0);
120 
121 	return error;
122 }
123 
124 static int
bmx280thpi2c_acquire_bus(struct bmx280_sc * sc)125 bmx280thpi2c_acquire_bus(struct bmx280_sc *sc)
126 {
127 	return(iic_acquire_bus(sc->sc_tag, 0));
128 }
129 
130 static void
bmx280thpi2c_release_bus(struct bmx280_sc * sc)131 bmx280thpi2c_release_bus(struct bmx280_sc *sc)
132 {
133 	iic_release_bus(sc->sc_tag, 0);
134 }
135 
136 static int
bmx280thpi2c_poke(i2c_tag_t tag,i2c_addr_t addr,bool matchdebug)137 bmx280thpi2c_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
138 {
139 	uint8_t reg = BMX280_REGISTER_ID;
140 	uint8_t buf[1];
141 	int error;
142 
143 	error = bmx280thpi2c_read_register_direct(tag, addr, reg, buf, 1);
144 	if (matchdebug) {
145 		printf("poke X 1: %d\n", error);
146 	}
147 	return error;
148 }
149 
150 static int
bmx280thpi2c_match(device_t parent,cfdata_t match,void * aux)151 bmx280thpi2c_match(device_t parent, cfdata_t match, void *aux)
152 {
153 	struct i2c_attach_args *ia = aux;
154 	int error, match_result;
155 	const bool matchdebug = false;
156 
157 	if (iic_use_direct_match(ia, match, NULL, &match_result))
158 		return match_result;
159 
160 	/* indirect config - check for configured address */
161 	if (ia->ia_addr != BMX280_TYPICAL_ADDR_1 &&
162 	    ia->ia_addr != BMX280_TYPICAL_ADDR_2)
163 		return 0;
164 
165 	/*
166 	 * Check to see if something is really at this i2c address. This will
167 	 * keep phantom devices from appearing
168 	 */
169 	if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
170 		if (matchdebug)
171 			printf("in match acquire bus failed\n");
172 		return 0;
173 	}
174 
175 	error = bmx280thpi2c_poke(ia->ia_tag, ia->ia_addr, matchdebug);
176 	iic_release_bus(ia->ia_tag, 0);
177 
178 	return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
179 }
180 
181 static void
bmx280thpi2c_attach(device_t parent,device_t self,void * aux)182 bmx280thpi2c_attach(device_t parent, device_t self, void *aux)
183 {
184 	struct bmx280_sc *sc;
185 	struct i2c_attach_args *ia;
186 
187 	ia = aux;
188 	sc = device_private(self);
189 
190 	sc->sc_dev = self;
191 	sc->sc_tag = ia->ia_tag;
192 	sc->sc_addr = ia->ia_addr;
193 	sc->sc_bmx280debug = 0;
194 	sc->sc_func_acquire_bus = &bmx280thpi2c_acquire_bus;
195 	sc->sc_func_release_bus = &bmx280thpi2c_release_bus;
196 	sc->sc_func_read_register = &bmx280thpi2c_read_register;
197 	sc->sc_func_write_register = &bmx280thpi2c_write_register;
198 
199 	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
200 
201 	bmx280_attach(sc);
202 
203 	return;
204 }
205 
206 static int
bmx280thpi2c_detach(device_t self,int flags)207 bmx280thpi2c_detach(device_t self, int flags)
208 {
209 	struct bmx280_sc *sc;
210 
211 	sc = device_private(self);
212 
213 	mutex_enter(&sc->sc_mutex);
214 
215 	/* Remove the sensors */
216 	if (sc->sc_sme != NULL) {
217 		sysmon_envsys_unregister(sc->sc_sme);
218 		sc->sc_sme = NULL;
219 	}
220 	mutex_exit(&sc->sc_mutex);
221 
222 	/* Remove the sysctl tree */
223 	sysctl_teardown(&sc->sc_bmx280log);
224 
225 	/* Remove the mutex */
226 	mutex_destroy(&sc->sc_mutex);
227 
228 	return 0;
229 }
230 
231 MODULE(MODULE_CLASS_DRIVER, bmx280thpi2c, "iic,bmx280thp");
232 
233 #ifdef _MODULE
234 /* Like other drivers, we do this because the bmx280 common
235  * driver has the definitions already.
236  */
237 #undef  CFDRIVER_DECL
238 #define CFDRIVER_DECL(name, class, attr)
239 #include "ioconf.c"
240 #endif
241 
242 static int
bmx280thpi2c_modcmd(modcmd_t cmd,void * opaque)243 bmx280thpi2c_modcmd(modcmd_t cmd, void *opaque)
244 {
245 
246 	switch (cmd) {
247 	case MODULE_CMD_INIT:
248 #ifdef _MODULE
249 		return config_init_component(cfdriver_ioconf_bmx280thpi2c,
250 		    cfattach_ioconf_bmx280thpi2c, cfdata_ioconf_bmx280thpi2c);
251 #else
252 		return 0;
253 #endif
254 	case MODULE_CMD_FINI:
255 #ifdef _MODULE
256 		return config_fini_component(cfdriver_ioconf_bmx280thpi2c,
257 		      cfattach_ioconf_bmx280thpi2c, cfdata_ioconf_bmx280thpi2c);
258 #else
259 		return 0;
260 #endif
261 	default:
262 		return ENOTTY;
263 	}
264 }
265