sht4x.c revision 1.3 1 1.3 pgoyette /* $NetBSD: sht4x.c,v 1.3 2022/03/30 00:06:50 pgoyette Exp $ */
2 1.1 brad
3 1.1 brad /*
4 1.1 brad * Copyright (c) 2021 Brad Spencer <brad (at) anduin.eldar.org>
5 1.1 brad *
6 1.1 brad * Permission to use, copy, modify, and distribute this software for any
7 1.1 brad * purpose with or without fee is hereby granted, provided that the above
8 1.1 brad * copyright notice and this permission notice appear in all copies.
9 1.1 brad *
10 1.1 brad * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 1.1 brad * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 1.1 brad * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 1.1 brad * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 1.1 brad * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 1.1 brad * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 1.1 brad * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 1.1 brad */
18 1.1 brad
19 1.1 brad #include <sys/cdefs.h>
20 1.3 pgoyette __KERNEL_RCSID(0, "$NetBSD: sht4x.c,v 1.3 2022/03/30 00:06:50 pgoyette Exp $");
21 1.1 brad
22 1.1 brad /*
23 1.1 brad Driver for the Sensirion SHT40/SHT41/SHT45
24 1.1 brad */
25 1.1 brad
26 1.1 brad #include <sys/param.h>
27 1.1 brad #include <sys/systm.h>
28 1.1 brad #include <sys/kernel.h>
29 1.1 brad #include <sys/device.h>
30 1.1 brad #include <sys/module.h>
31 1.1 brad #include <sys/sysctl.h>
32 1.1 brad #include <sys/mutex.h>
33 1.1 brad
34 1.1 brad #include <dev/sysmon/sysmonvar.h>
35 1.1 brad #include <dev/i2c/i2cvar.h>
36 1.1 brad #include <dev/i2c/sht4xreg.h>
37 1.1 brad #include <dev/i2c/sht4xvar.h>
38 1.1 brad
39 1.1 brad
40 1.1 brad static uint8_t sht4x_crc(uint8_t *, size_t);
41 1.1 brad static int sht4x_poke(i2c_tag_t, i2c_addr_t, bool);
42 1.1 brad static int sht4x_match(device_t, cfdata_t, void *);
43 1.1 brad static void sht4x_attach(device_t, device_t, void *);
44 1.1 brad static int sht4x_detach(device_t, int);
45 1.1 brad static void sht4x_refresh(struct sysmon_envsys *, envsys_data_t *);
46 1.1 brad static int sht4x_verify_sysctl(SYSCTLFN_ARGS);
47 1.1 brad static int sht4x_verify_sysctl_resolution(SYSCTLFN_ARGS);
48 1.1 brad static int sht4x_verify_sysctl_heateron(SYSCTLFN_ARGS);
49 1.1 brad static int sht4x_verify_sysctl_heatervalue(SYSCTLFN_ARGS);
50 1.1 brad static int sht4x_verify_sysctl_heaterpulse(SYSCTLFN_ARGS);
51 1.1 brad
52 1.1 brad #define SHT4X_DEBUG
53 1.1 brad #ifdef SHT4X_DEBUG
54 1.1 brad #define DPRINTF(s, l, x) \
55 1.1 brad do { \
56 1.1 brad if (l <= s->sc_sht4xdebug) \
57 1.1 brad printf x; \
58 1.1 brad } while (/*CONSTCOND*/0)
59 1.1 brad #else
60 1.1 brad #define DPRINTF(s, l, x)
61 1.1 brad #endif
62 1.1 brad
63 1.1 brad CFATTACH_DECL_NEW(sht4xtemp, sizeof(struct sht4x_sc),
64 1.1 brad sht4x_match, sht4x_attach, sht4x_detach, NULL);
65 1.1 brad
66 1.1 brad static struct sht4x_sensor sht4x_sensors[] = {
67 1.1 brad {
68 1.1 brad .desc = "humidity",
69 1.1 brad .type = ENVSYS_SRELHUMIDITY,
70 1.1 brad },
71 1.1 brad {
72 1.1 brad .desc = "temperature",
73 1.1 brad .type = ENVSYS_STEMP,
74 1.1 brad }
75 1.1 brad };
76 1.1 brad
77 1.1 brad /* The typical delays are documented in the datasheet for the chip.
78 1.1 brad There is no need to be very accurate with these, just rough estimates
79 1.1 brad will work fine.
80 1.1 brad */
81 1.1 brad
82 1.1 brad static struct sht4x_timing sht4x_timings[] = {
83 1.1 brad {
84 1.1 brad .cmd = SHT4X_READ_SERIAL,
85 1.1 brad .typicaldelay = 5000,
86 1.1 brad },
87 1.1 brad {
88 1.1 brad .cmd = SHT4X_SOFT_RESET,
89 1.1 brad .typicaldelay = 1000,
90 1.1 brad },
91 1.1 brad {
92 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION,
93 1.1 brad .typicaldelay = 8000,
94 1.1 brad },
95 1.1 brad {
96 1.1 brad .cmd = SHT4X_MEASURE_MEDIUM_PRECISION,
97 1.1 brad .typicaldelay = 4000,
98 1.1 brad },
99 1.1 brad {
100 1.1 brad .cmd = SHT4X_MEASURE_LOW_PRECISION,
101 1.1 brad .typicaldelay = 2000,
102 1.1 brad },
103 1.1 brad {
104 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_1_S,
105 1.1 brad .typicaldelay = 1000000,
106 1.1 brad },
107 1.1 brad {
108 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_1_S,
109 1.1 brad .typicaldelay = 1000000,
110 1.1 brad },
111 1.1 brad {
112 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_1_S,
113 1.1 brad .typicaldelay = 1000000,
114 1.1 brad },
115 1.1 brad {
116 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_TENTH_S,
117 1.1 brad .typicaldelay = 100000,
118 1.1 brad },
119 1.1 brad {
120 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_TENTH_S,
121 1.1 brad .typicaldelay = 100000,
122 1.1 brad },
123 1.1 brad {
124 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_TENTH_S,
125 1.1 brad .typicaldelay = 100000,
126 1.1 brad }
127 1.1 brad };
128 1.1 brad
129 1.1 brad /* Used when the heater is not on to find the command to use for the
130 1.1 brad * measurement.
131 1.1 brad */
132 1.1 brad
133 1.1 brad static struct sht4x_resolution sht4x_resolutions[] = {
134 1.1 brad {
135 1.1 brad .text = "high",
136 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION,
137 1.1 brad },
138 1.1 brad {
139 1.1 brad .text = "medium",
140 1.1 brad .cmd = SHT4X_MEASURE_MEDIUM_PRECISION,
141 1.1 brad },
142 1.1 brad {
143 1.1 brad .text = "low",
144 1.1 brad .cmd = SHT4X_MEASURE_LOW_PRECISION,
145 1.1 brad }
146 1.1 brad };
147 1.1 brad
148 1.1 brad static const char sht4x_resolution_names[] =
149 1.1 brad "high, medium, low";
150 1.1 brad
151 1.1 brad static struct sht4x_heaterpulse sht4x_heaterpulses[] = {
152 1.1 brad {
153 1.1 brad .length = "short",
154 1.1 brad },
155 1.1 brad {
156 1.1 brad .length = "long",
157 1.1 brad }
158 1.1 brad };
159 1.1 brad
160 1.1 brad /* This is consulted when the heater is on for which command is to be
161 1.1 brad used for the measurement.
162 1.1 brad */
163 1.1 brad
164 1.1 brad static struct sht4x_heateron_command sht4x_heateron_commands[] = {
165 1.1 brad {
166 1.1 brad .heatervalue = 1,
167 1.1 brad .pulselength = "short",
168 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_TENTH_S,
169 1.1 brad },
170 1.1 brad {
171 1.1 brad .heatervalue = 2,
172 1.1 brad .pulselength = "short",
173 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_TENTH_S,
174 1.1 brad },
175 1.1 brad {
176 1.1 brad .heatervalue = 3,
177 1.1 brad .pulselength = "short",
178 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_TENTH_S,
179 1.1 brad },
180 1.1 brad {
181 1.1 brad .heatervalue = 1,
182 1.1 brad .pulselength = "long",
183 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_LOW_HEAT_1_S,
184 1.1 brad },
185 1.1 brad {
186 1.1 brad .heatervalue = 2,
187 1.1 brad .pulselength = "long",
188 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_MEDIUM_HEAT_1_S,
189 1.1 brad },
190 1.1 brad {
191 1.1 brad .heatervalue = 3,
192 1.1 brad .pulselength = "long",
193 1.1 brad .cmd = SHT4X_MEASURE_HIGH_PRECISION_HIGH_HEAT_1_S,
194 1.1 brad }
195 1.1 brad };
196 1.1 brad
197 1.1 brad static const char sht4x_heaterpulse_names[] =
198 1.1 brad "short, long";
199 1.1 brad
200 1.1 brad int
201 1.1 brad sht4x_verify_sysctl(SYSCTLFN_ARGS)
202 1.1 brad {
203 1.1 brad int error, t;
204 1.1 brad struct sysctlnode node;
205 1.1 brad
206 1.1 brad node = *rnode;
207 1.1 brad t = *(int *)rnode->sysctl_data;
208 1.1 brad node.sysctl_data = &t;
209 1.1 brad error = sysctl_lookup(SYSCTLFN_CALL(&node));
210 1.1 brad if (error || newp == NULL)
211 1.1 brad return error;
212 1.1 brad
213 1.1 brad if (t < 0)
214 1.1 brad return EINVAL;
215 1.1 brad
216 1.1 brad *(int *)rnode->sysctl_data = t;
217 1.1 brad
218 1.1 brad return 0;
219 1.1 brad }
220 1.1 brad
221 1.1 brad /* None of the heater and resolutions sysctls change anything on the chip in
222 1.1 brad real time. The values set are used to send different commands depending on
223 1.1 brad how they are set up.
224 1.1 brad
225 1.1 brad What this implies is that the chip could be reset and the driver would not care.
226 1.1 brad
227 1.1 brad */
228 1.1 brad
229 1.1 brad int
230 1.1 brad sht4x_verify_sysctl_resolution(SYSCTLFN_ARGS)
231 1.1 brad {
232 1.1 brad char buf[SHT4X_RES_NAME];
233 1.1 brad struct sht4x_sc *sc;
234 1.1 brad struct sysctlnode node;
235 1.1 brad int error = 0;
236 1.1 brad size_t i;
237 1.1 brad
238 1.1 brad node = *rnode;
239 1.1 brad sc = node.sysctl_data;
240 1.1 brad (void) memcpy(buf, sc->sc_resolution, SHT4X_RES_NAME);
241 1.1 brad node.sysctl_data = buf;
242 1.1 brad error = sysctl_lookup(SYSCTLFN_CALL(&node));
243 1.1 brad if (error || newp == NULL)
244 1.1 brad return error;
245 1.1 brad
246 1.1 brad for (i = 0; i < __arraycount(sht4x_resolutions); i++) {
247 1.1 brad if (strncmp(node.sysctl_data, sht4x_resolutions[i].text,
248 1.1 brad SHT4X_RES_NAME) == 0) {
249 1.1 brad break;
250 1.1 brad }
251 1.1 brad }
252 1.1 brad
253 1.1 brad if (i == __arraycount(sht4x_resolutions))
254 1.1 brad return EINVAL;
255 1.1 brad (void) memcpy(sc->sc_resolution, node.sysctl_data, SHT4X_RES_NAME);
256 1.1 brad
257 1.1 brad return error;
258 1.1 brad }
259 1.1 brad
260 1.1 brad int
261 1.1 brad sht4x_verify_sysctl_heateron(SYSCTLFN_ARGS)
262 1.1 brad {
263 1.1 brad int error;
264 1.1 brad bool t;
265 1.1 brad struct sht4x_sc *sc;
266 1.1 brad struct sysctlnode node;
267 1.1 brad
268 1.1 brad node = *rnode;
269 1.1 brad sc = node.sysctl_data;
270 1.1 brad t = sc->sc_heateron;
271 1.1 brad node.sysctl_data = &t;
272 1.1 brad error = sysctl_lookup(SYSCTLFN_CALL(&node));
273 1.1 brad if (error || newp == NULL)
274 1.1 brad return error;
275 1.1 brad
276 1.1 brad sc->sc_heateron = t;
277 1.1 brad
278 1.1 brad return error;
279 1.1 brad }
280 1.1 brad
281 1.1 brad int
282 1.1 brad sht4x_verify_sysctl_heatervalue(SYSCTLFN_ARGS)
283 1.1 brad {
284 1.1 brad int error = 0, t;
285 1.1 brad struct sht4x_sc *sc;
286 1.1 brad struct sysctlnode node;
287 1.1 brad
288 1.1 brad node = *rnode;
289 1.1 brad sc = node.sysctl_data;
290 1.1 brad t = sc->sc_heaterval;
291 1.1 brad node.sysctl_data = &t;
292 1.1 brad error = sysctl_lookup(SYSCTLFN_CALL(&node));
293 1.1 brad if (error || newp == NULL)
294 1.1 brad return (error);
295 1.1 brad
296 1.1 brad if (t < 1 || t > 3)
297 1.1 brad return (EINVAL);
298 1.1 brad
299 1.1 brad sc->sc_heaterval = t;
300 1.1 brad
301 1.1 brad return error;
302 1.1 brad }
303 1.1 brad
304 1.1 brad int
305 1.1 brad sht4x_verify_sysctl_heaterpulse(SYSCTLFN_ARGS)
306 1.1 brad {
307 1.1 brad char buf[SHT4X_PULSE_NAME];
308 1.1 brad struct sht4x_sc *sc;
309 1.1 brad struct sysctlnode node;
310 1.1 brad int error = 0;
311 1.1 brad size_t i;
312 1.1 brad
313 1.1 brad node = *rnode;
314 1.1 brad sc = node.sysctl_data;
315 1.1 brad (void) memcpy(buf, sc->sc_heaterpulse, SHT4X_PULSE_NAME);
316 1.1 brad node.sysctl_data = buf;
317 1.1 brad error = sysctl_lookup(SYSCTLFN_CALL(&node));
318 1.1 brad if (error || newp == NULL)
319 1.1 brad return error;
320 1.1 brad
321 1.1 brad for (i = 0; i < __arraycount(sht4x_heaterpulses); i++) {
322 1.1 brad if (strncmp(node.sysctl_data, sht4x_heaterpulses[i].length,
323 1.1 brad SHT4X_RES_NAME) == 0) {
324 1.1 brad break;
325 1.1 brad }
326 1.1 brad }
327 1.1 brad
328 1.1 brad if (i == __arraycount(sht4x_heaterpulses))
329 1.1 brad return EINVAL;
330 1.1 brad (void) memcpy(sc->sc_heaterpulse, node.sysctl_data, SHT4X_PULSE_NAME);
331 1.1 brad
332 1.1 brad return error;
333 1.1 brad }
334 1.1 brad
335 1.1 brad static int
336 1.1 brad sht4x_cmddelay(uint8_t cmd)
337 1.1 brad {
338 1.1 brad int r = -1;
339 1.1 brad
340 1.1 brad for(int i = 0;i < __arraycount(sht4x_timings);i++) {
341 1.1 brad if (cmd == sht4x_timings[i].cmd) {
342 1.1 brad r = sht4x_timings[i].typicaldelay;
343 1.1 brad break;
344 1.1 brad }
345 1.1 brad }
346 1.1 brad
347 1.1 brad if (r == -1) {
348 1.1 brad panic("Bad command look up in cmd delay: cmd: %d\n",cmd);
349 1.1 brad }
350 1.1 brad
351 1.1 brad return r;
352 1.1 brad }
353 1.1 brad
354 1.1 brad static int
355 1.1 brad sht4x_cmd(i2c_tag_t tag, i2c_addr_t addr, uint8_t *cmd,
356 1.1 brad uint8_t clen, uint8_t *buf, size_t blen, int readattempts)
357 1.1 brad {
358 1.1 brad int error;
359 1.1 brad int cmddelay;
360 1.1 brad
361 1.1 brad error = iic_exec(tag,I2C_OP_WRITE_WITH_STOP,addr,cmd,clen,NULL,0,0);
362 1.1 brad
363 1.1 brad /* Every command returns something except for the soft reset
364 1.1 brad which returns nothing. This chip is also nice in that pretty
365 1.1 brad much every command that returns something does it in the same way.
366 1.1 brad */
367 1.1 brad if (error == 0 && cmd[0] != SHT4X_SOFT_RESET) {
368 1.1 brad cmddelay = sht4x_cmddelay(cmd[0]);
369 1.1 brad delay(cmddelay);
370 1.1 brad
371 1.1 brad for (int aint = 0; aint < readattempts; aint++) {
372 1.1 brad error = iic_exec(tag,I2C_OP_READ_WITH_STOP,addr,NULL,0,buf,blen,0);
373 1.1 brad if (error == 0)
374 1.1 brad break;
375 1.1 brad delay(1000);
376 1.1 brad }
377 1.1 brad }
378 1.1 brad
379 1.1 brad return error;
380 1.1 brad }
381 1.1 brad
382 1.1 brad static int
383 1.1 brad sht4x_cmdr(struct sht4x_sc *sc, uint8_t cmd, uint8_t *buf, size_t blen)
384 1.1 brad {
385 1.1 brad return sht4x_cmd(sc->sc_tag, sc->sc_addr, &cmd, 1, buf, blen, sc->sc_readattempts);
386 1.1 brad }
387 1.1 brad
388 1.1 brad static uint8_t
389 1.1 brad sht4x_crc(uint8_t * data, size_t size)
390 1.1 brad {
391 1.1 brad uint8_t crc = 0xFF;
392 1.1 brad
393 1.1 brad for (size_t i = 0; i < size; i++) {
394 1.1 brad crc ^= data[i];
395 1.1 brad for (size_t j = 8; j > 0; j--) {
396 1.1 brad if (crc & 0x80)
397 1.1 brad crc = (crc << 1) ^ 0x131;
398 1.1 brad else
399 1.1 brad crc <<= 1;
400 1.1 brad }
401 1.1 brad }
402 1.1 brad return crc;
403 1.1 brad }
404 1.1 brad
405 1.1 brad static int
406 1.1 brad sht4x_poke(i2c_tag_t tag, i2c_addr_t addr, bool matchdebug)
407 1.1 brad {
408 1.1 brad uint8_t reg = SHT4X_READ_SERIAL;
409 1.1 brad uint8_t buf[6];
410 1.1 brad int error;
411 1.1 brad
412 1.1 brad error = sht4x_cmd(tag, addr, ®, 1, buf, 6, 10);
413 1.1 brad if (matchdebug) {
414 1.1 brad printf("poke X 1: %d\n", error);
415 1.1 brad }
416 1.1 brad return error;
417 1.1 brad }
418 1.1 brad
419 1.1 brad static int
420 1.1 brad sht4x_sysctl_init(struct sht4x_sc *sc)
421 1.1 brad {
422 1.1 brad int error;
423 1.1 brad const struct sysctlnode *cnode;
424 1.1 brad int sysctlroot_num;
425 1.1 brad
426 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
427 1.1 brad 0, CTLTYPE_NODE, device_xname(sc->sc_dev),
428 1.1 brad SYSCTL_DESCR("sht4x controls"), NULL, 0, NULL, 0, CTL_HW,
429 1.1 brad CTL_CREATE, CTL_EOL)) != 0)
430 1.1 brad return error;
431 1.1 brad
432 1.1 brad sysctlroot_num = cnode->sysctl_num;
433 1.1 brad
434 1.1 brad #ifdef SHT4X_DEBUG
435 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
436 1.1 brad CTLFLAG_READWRITE, CTLTYPE_INT, "debug",
437 1.1 brad SYSCTL_DESCR("Debug level"), sht4x_verify_sysctl, 0,
438 1.1 brad &sc->sc_sht4xdebug, 0, CTL_HW, sysctlroot_num, CTL_CREATE,
439 1.1 brad CTL_EOL)) != 0)
440 1.1 brad return error;
441 1.1 brad
442 1.1 brad #endif
443 1.1 brad
444 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
445 1.1 brad CTLFLAG_READWRITE, CTLTYPE_INT, "readattempts",
446 1.1 brad SYSCTL_DESCR("The number of times to attempt to read the values"),
447 1.1 brad sht4x_verify_sysctl, 0, &sc->sc_readattempts, 0, CTL_HW,
448 1.1 brad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
449 1.1 brad return error;
450 1.1 brad
451 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
452 1.1 brad CTLFLAG_READONLY, CTLTYPE_STRING, "resolutions",
453 1.1 brad SYSCTL_DESCR("Valid resolutions"), 0, 0,
454 1.1 brad __UNCONST(sht4x_resolution_names),
455 1.1 brad sizeof(sht4x_resolution_names) + 1,
456 1.1 brad CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
457 1.1 brad return error;
458 1.1 brad
459 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
460 1.1 brad CTLFLAG_READWRITE, CTLTYPE_STRING, "resolution",
461 1.1 brad SYSCTL_DESCR("Resolution of RH and Temp"),
462 1.1 brad sht4x_verify_sysctl_resolution, 0, (void *) sc,
463 1.1 brad SHT4X_RES_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
464 1.1 brad return error;
465 1.1 brad
466 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
467 1.1 brad CTLFLAG_READWRITE, CTLTYPE_BOOL, "ignorecrc",
468 1.1 brad SYSCTL_DESCR("Ignore the CRC byte"), NULL, 0, &sc->sc_ignorecrc,
469 1.1 brad 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
470 1.1 brad return error;
471 1.1 brad
472 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
473 1.1 brad CTLFLAG_READWRITE, CTLTYPE_BOOL, "heateron",
474 1.1 brad SYSCTL_DESCR("Heater on"), sht4x_verify_sysctl_heateron, 0,
475 1.1 brad (void *)sc, 0, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
476 1.1 brad return error;
477 1.1 brad
478 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
479 1.1 brad CTLFLAG_READWRITE, CTLTYPE_INT, "heaterstrength",
480 1.1 brad SYSCTL_DESCR("Heater strength 1 to 3"),
481 1.1 brad sht4x_verify_sysctl_heatervalue, 0, (void *)sc, 0, CTL_HW,
482 1.1 brad sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
483 1.1 brad return error;
484 1.1 brad
485 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
486 1.1 brad CTLFLAG_READONLY, CTLTYPE_STRING, "heaterpulses",
487 1.1 brad SYSCTL_DESCR("Valid heater pulse lengths"), 0, 0,
488 1.1 brad __UNCONST(sht4x_heaterpulse_names),
489 1.1 brad sizeof(sht4x_heaterpulse_names) + 1,
490 1.1 brad CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
491 1.1 brad return error;
492 1.1 brad
493 1.1 brad if ((error = sysctl_createv(&sc->sc_sht4xlog, 0, NULL, &cnode,
494 1.1 brad CTLFLAG_READWRITE, CTLTYPE_STRING, "heaterpulse",
495 1.1 brad SYSCTL_DESCR("Heater pulse length"),
496 1.1 brad sht4x_verify_sysctl_heaterpulse, 0, (void *) sc,
497 1.1 brad SHT4X_RES_NAME, CTL_HW, sysctlroot_num, CTL_CREATE, CTL_EOL)) != 0)
498 1.1 brad return error;
499 1.1 brad return 0;
500 1.1 brad }
501 1.1 brad
502 1.1 brad static int
503 1.1 brad sht4x_match(device_t parent, cfdata_t match, void *aux)
504 1.1 brad {
505 1.1 brad struct i2c_attach_args *ia = aux;
506 1.1 brad int error, match_result;
507 1.1 brad const bool matchdebug = false;
508 1.1 brad
509 1.1 brad if (iic_use_direct_match(ia, match, NULL, &match_result))
510 1.1 brad return match_result;
511 1.1 brad
512 1.1 brad /* indirect config - check for configured address */
513 1.1 brad if (ia->ia_addr != SHT4X_TYPICAL_ADDR)
514 1.1 brad return 0;
515 1.1 brad
516 1.1 brad /*
517 1.1 brad * Check to see if something is really at this i2c address. This will
518 1.1 brad * keep phantom devices from appearing
519 1.1 brad */
520 1.1 brad if (iic_acquire_bus(ia->ia_tag, 0) != 0) {
521 1.1 brad if (matchdebug)
522 1.1 brad printf("in match acquire bus failed\n");
523 1.1 brad return 0;
524 1.1 brad }
525 1.1 brad
526 1.1 brad error = sht4x_poke(ia->ia_tag, ia->ia_addr, matchdebug);
527 1.1 brad iic_release_bus(ia->ia_tag, 0);
528 1.1 brad
529 1.1 brad return error == 0 ? I2C_MATCH_ADDRESS_AND_PROBE : 0;
530 1.1 brad }
531 1.1 brad
532 1.1 brad static void
533 1.1 brad sht4x_attach(device_t parent, device_t self, void *aux)
534 1.1 brad {
535 1.1 brad struct sht4x_sc *sc;
536 1.1 brad struct i2c_attach_args *ia;
537 1.1 brad int error, i;
538 1.1 brad int ecount = 0;
539 1.1 brad uint8_t buf[6];
540 1.1 brad uint8_t sncrcpt1, sncrcpt2;
541 1.1 brad
542 1.1 brad ia = aux;
543 1.1 brad sc = device_private(self);
544 1.1 brad
545 1.1 brad sc->sc_dev = self;
546 1.1 brad sc->sc_tag = ia->ia_tag;
547 1.1 brad sc->sc_addr = ia->ia_addr;
548 1.1 brad sc->sc_sht4xdebug = 0;
549 1.1 brad strlcpy(sc->sc_resolution,"high",SHT4X_RES_NAME);
550 1.1 brad sc->sc_readattempts = 10;
551 1.1 brad sc->sc_ignorecrc = false;
552 1.1 brad sc->sc_heateron = false;
553 1.1 brad sc->sc_heaterval = 1;
554 1.1 brad strlcpy(sc->sc_heaterpulse,"short",SHT4X_PULSE_NAME);
555 1.1 brad sc->sc_sme = NULL;
556 1.1 brad
557 1.1 brad aprint_normal("\n");
558 1.1 brad
559 1.1 brad mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
560 1.1 brad sc->sc_numsensors = __arraycount(sht4x_sensors);
561 1.1 brad
562 1.1 brad if ((sc->sc_sme = sysmon_envsys_create()) == NULL) {
563 1.1 brad aprint_error_dev(self,
564 1.1 brad "Unable to create sysmon structure\n");
565 1.1 brad sc->sc_sme = NULL;
566 1.1 brad return;
567 1.1 brad }
568 1.1 brad if ((error = sht4x_sysctl_init(sc)) != 0) {
569 1.1 brad aprint_error_dev(self, "Can't setup sysctl tree (%d)\n", error);
570 1.1 brad goto out;
571 1.1 brad }
572 1.1 brad
573 1.1 brad error = iic_acquire_bus(sc->sc_tag, 0);
574 1.1 brad if (error) {
575 1.1 brad aprint_error_dev(self, "Could not acquire iic bus: %d\n",
576 1.1 brad error);
577 1.1 brad goto out;
578 1.1 brad }
579 1.1 brad
580 1.1 brad error = sht4x_cmdr(sc, SHT4X_SOFT_RESET, NULL, 0);
581 1.1 brad if (error != 0)
582 1.1 brad aprint_error_dev(self, "Reset failed: %d\n", error);
583 1.1 brad
584 1.1 brad delay(1000); /* 1 ms max */
585 1.1 brad
586 1.1 brad error = sht4x_cmdr(sc, SHT4X_READ_SERIAL, buf, 6);
587 1.1 brad if (error) {
588 1.1 brad aprint_error_dev(self, "Failed to read serial number: %d\n",
589 1.1 brad error);
590 1.1 brad ecount++;
591 1.1 brad }
592 1.1 brad
593 1.1 brad sncrcpt1 = sht4x_crc(&buf[0],2);
594 1.1 brad sncrcpt2 = sht4x_crc(&buf[3],2);
595 1.1 brad
596 1.1 brad DPRINTF(sc, 2, ("%s: read serial number values: %02x%02x - %02x, %02x%02x - %02x ; %02x %02x\n",
597 1.1 brad device_xname(sc->sc_dev), buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], sncrcpt1, sncrcpt2));
598 1.1 brad
599 1.1 brad iic_release_bus(sc->sc_tag, 0);
600 1.1 brad if (error != 0) {
601 1.1 brad aprint_error_dev(self, "Unable to setup device\n");
602 1.1 brad goto out;
603 1.1 brad }
604 1.1 brad
605 1.1 brad for (i = 0; i < sc->sc_numsensors; i++) {
606 1.1 brad strlcpy(sc->sc_sensors[i].desc, sht4x_sensors[i].desc,
607 1.1 brad sizeof(sc->sc_sensors[i].desc));
608 1.1 brad
609 1.1 brad sc->sc_sensors[i].units = sht4x_sensors[i].type;
610 1.1 brad sc->sc_sensors[i].state = ENVSYS_SINVALID;
611 1.1 brad
612 1.1 brad DPRINTF(sc, 2, ("%s: registering sensor %d (%s)\n", __func__, i,
613 1.1 brad sc->sc_sensors[i].desc));
614 1.1 brad
615 1.1 brad error = sysmon_envsys_sensor_attach(sc->sc_sme,
616 1.1 brad &sc->sc_sensors[i]);
617 1.1 brad if (error) {
618 1.1 brad aprint_error_dev(self,
619 1.1 brad "Unable to attach sensor %d: %d\n", i, error);
620 1.1 brad goto out;
621 1.1 brad }
622 1.1 brad }
623 1.1 brad
624 1.1 brad sc->sc_sme->sme_name = device_xname(sc->sc_dev);
625 1.1 brad sc->sc_sme->sme_cookie = sc;
626 1.1 brad sc->sc_sme->sme_refresh = sht4x_refresh;
627 1.1 brad
628 1.1 brad DPRINTF(sc, 2, ("sht4x_attach: registering with envsys\n"));
629 1.1 brad
630 1.1 brad if (sysmon_envsys_register(sc->sc_sme)) {
631 1.1 brad aprint_error_dev(self,
632 1.1 brad "unable to register with sysmon\n");
633 1.1 brad sysmon_envsys_destroy(sc->sc_sme);
634 1.1 brad sc->sc_sme = NULL;
635 1.1 brad return;
636 1.1 brad }
637 1.1 brad
638 1.1 brad /* There is no documented way to ask the chip what version it is. This
639 1.1 brad is likely fine as the only apparent difference is in how precise the
640 1.1 brad measurements will be. The actual conversation with the chip is
641 1.1 brad identical no matter which one you are talking to.
642 1.1 brad */
643 1.1 brad
644 1.1 brad aprint_normal_dev(self, "Sensirion SHT40/SHT41/SHT45, "
645 1.1 brad "Serial number: %02x%02x%02x%02x%s",
646 1.1 brad buf[0], buf[1], buf[3], buf[4],
647 1.1 brad (sncrcpt1 == buf[2] && sncrcpt2 == buf[5]) ? "\n" : " (bad crc)\n");
648 1.1 brad return;
649 1.1 brad out:
650 1.1 brad sysmon_envsys_destroy(sc->sc_sme);
651 1.1 brad sc->sc_sme = NULL;
652 1.1 brad }
653 1.1 brad
654 1.1 brad /* If you use the heater on this chip, there is no documented choice but to use
655 1.1 brad the highest precision. If the heater is not in use one may select different
656 1.1 brad precisions or repeatability for the measurement.
657 1.1 brad
658 1.1 brad Further, if the heater is used, it will only be active during the measurement.
659 1.1 brad The use of the heater will add delay to the measurement as chip will not
660 1.1 brad return anything until the heater pulse time is over.
661 1.1 brad */
662 1.1 brad
663 1.1 brad static uint8_t
664 1.1 brad sht4x_compute_measure_command(char *resolution, bool heateron,
665 1.1 brad int heatervalue, char *heaterpulse)
666 1.1 brad {
667 1.1 brad int i;
668 1.1 brad uint8_t r;
669 1.1 brad
670 1.1 brad if (heateron == false) {
671 1.1 brad for (i = 0; i < __arraycount(sht4x_resolutions); i++) {
672 1.1 brad if (strncmp(resolution, sht4x_resolutions[i].text,
673 1.1 brad SHT4X_RES_NAME) == 0) {
674 1.1 brad r = sht4x_resolutions[i].cmd;
675 1.1 brad break;
676 1.1 brad }
677 1.1 brad }
678 1.1 brad
679 1.1 brad if (i == __arraycount(sht4x_resolutions))
680 1.1 brad panic("Heater off could not find command for resolution: %s\n",resolution);
681 1.1 brad } else {
682 1.1 brad for (i = 0; i < __arraycount(sht4x_heateron_commands); i++) {
683 1.1 brad if (heatervalue == sht4x_heateron_commands[i].heatervalue &&
684 1.1 brad strncmp(heaterpulse, sht4x_heateron_commands[i].pulselength,
685 1.1 brad SHT4X_PULSE_NAME) == 0) {
686 1.1 brad r = sht4x_heateron_commands[i].cmd;
687 1.1 brad break;
688 1.1 brad }
689 1.1 brad }
690 1.1 brad
691 1.1 brad if (i == __arraycount(sht4x_heateron_commands))
692 1.1 brad panic("Heater on could not find command for heatervalue, heaterpulse: %d %s\n",
693 1.1 brad heatervalue,heaterpulse);
694 1.1 brad }
695 1.1 brad
696 1.1 brad return r;
697 1.1 brad }
698 1.1 brad
699 1.1 brad static void
700 1.1 brad sht4x_refresh(struct sysmon_envsys * sme, envsys_data_t * edata)
701 1.1 brad {
702 1.1 brad struct sht4x_sc *sc;
703 1.1 brad sc = sme->sme_cookie;
704 1.1 brad int error;
705 1.1 brad uint8_t rawdata[6];
706 1.1 brad uint8_t measurement_command;
707 1.1 brad edata->state = ENVSYS_SINVALID;
708 1.1 brad
709 1.1 brad mutex_enter(&sc->sc_mutex);
710 1.1 brad error = iic_acquire_bus(sc->sc_tag, 0);
711 1.1 brad if (error) {
712 1.1 brad DPRINTF(sc, 2, ("%s: Could not acquire i2c bus: %x\n",
713 1.1 brad device_xname(sc->sc_dev), error));
714 1.1 brad goto out;
715 1.1 brad }
716 1.1 brad
717 1.1 brad /*
718 1.1 brad The documented conversion calculations for the raw values are as follows:
719 1.1 brad
720 1.1 brad %RH = (-6 + 125 * rawvalue / 65535)
721 1.1 brad
722 1.1 brad T in Celsius = (-45 + 175 * rawvalue / 65535)
723 1.1 brad
724 1.1 brad It follows then:
725 1.1 brad
726 1.2 brad T in Kelvin = (228.15 + 175 * rawvalue / 65535)
727 1.1 brad
728 1.1 brad given the relationship between Celsius and Kelvin.
729 1.1 brad
730 1.1 brad What follows reorders the calculation a bit and scales it up to avoid
731 1.1 brad the use of any floating point. All that would really have to happen
732 1.1 brad is a scale up to 10^6 for the sysenv framework, which wants
733 1.1 brad temperature in micro-kelvin and percent relative humidity scaled up
734 1.1 brad 10^6, but since this conversion uses 64 bits due to intermediate
735 1.1 brad values that are bigger than 32 bits the conversion first scales up to
736 1.1 brad 10^9 and the scales back down by 10^3 at the end. This preserves some
737 1.1 brad precision in the conversion that would otherwise be lost.
738 1.1 brad */
739 1.1 brad
740 1.1 brad measurement_command = sht4x_compute_measure_command(sc->sc_resolution,
741 1.1 brad sc->sc_heateron, sc->sc_heaterval, sc->sc_heaterpulse);
742 1.1 brad DPRINTF(sc, 2, ("%s: Measurement command: %02x\n",
743 1.1 brad device_xname(sc->sc_dev), measurement_command));
744 1.1 brad
745 1.1 brad /* This chip is pretty nice in that all commands are the same length and
746 1.1 brad return the same result. What is not so nice is that you can not ask
747 1.1 brad for temperature and humidity independently.
748 1.1 brad
749 1.1 brad The result will be 16 bits of raw temperature and a CRC byte followed
750 1.1 brad by 16 bits of humidity followed by a CRC byte.
751 1.1 brad */
752 1.1 brad
753 1.1 brad error = sht4x_cmdr(sc,measurement_command,rawdata,6);
754 1.1 brad
755 1.1 brad if (error == 0) {
756 1.1 brad DPRINTF(sc, 2, ("%s: Raw data: %02x%02x %02x - %02x%02x %02x\n",
757 1.1 brad device_xname(sc->sc_dev), rawdata[0], rawdata[1], rawdata[2],
758 1.1 brad rawdata[3], rawdata[4], rawdata[5]));
759 1.1 brad
760 1.1 brad
761 1.1 brad uint8_t *svalptr;
762 1.1 brad uint64_t svalue;
763 1.1 brad int64_t v1;
764 1.1 brad uint64_t v2;
765 1.1 brad uint64_t d1 = 65535;
766 1.1 brad uint64_t mul1;
767 1.1 brad uint64_t mul2;
768 1.1 brad uint64_t div1 = 10000;
769 1.1 brad uint64_t q;
770 1.1 brad
771 1.1 brad switch (edata->sensor) {
772 1.1 brad case SHT4X_TEMP_SENSOR:
773 1.1 brad svalptr = &rawdata[0];
774 1.2 brad v1 = 22815; /* this is scaled up already from 228.15 */
775 1.1 brad v2 = 175;
776 1.1 brad mul1 = 10000000000;
777 1.1 brad mul2 = 100000000;
778 1.1 brad break;
779 1.1 brad case SHT4X_HUMIDITY_SENSOR:
780 1.1 brad svalptr = &rawdata[3];
781 1.1 brad v1 = -6;
782 1.1 brad v2 = 125;
783 1.1 brad mul1 = 10000000000;
784 1.1 brad mul2 = 10000000000;
785 1.1 brad break;
786 1.1 brad default:
787 1.1 brad error = EINVAL;
788 1.1 brad break;
789 1.1 brad }
790 1.1 brad
791 1.1 brad if (error == 0) {
792 1.1 brad uint8_t testcrc;
793 1.1 brad
794 1.1 brad /* Fake out the CRC check if being asked to ignore CRC */
795 1.1 brad if (sc->sc_ignorecrc) {
796 1.1 brad testcrc = *(svalptr + 2);
797 1.1 brad } else {
798 1.1 brad testcrc = sht4x_crc(svalptr,2);
799 1.1 brad }
800 1.1 brad
801 1.1 brad if (*(svalptr + 2) == testcrc) {
802 1.1 brad svalue = *svalptr << 8 | *(svalptr + 1);
803 1.1 brad DPRINTF(sc, 2, ("%s: Raw sensor 16 bit: %#jx\n",
804 1.1 brad device_xname(sc->sc_dev), (uintmax_t)svalue));
805 1.1 brad
806 1.1 brad /* Scale up */
807 1.1 brad svalue = svalue * mul1;
808 1.1 brad v1 = v1 * mul2;
809 1.1 brad /* Perform the conversion */
810 1.1 brad q = ((v2 * (svalue / d1)) + v1) / div1;
811 1.1 brad
812 1.1 brad DPRINTF(sc, 2, ("%s: Computed sensor: %#jx\n",
813 1.1 brad device_xname(sc->sc_dev), (uintmax_t)q));
814 1.1 brad /* The results will fit in 32 bits, so nothing will be lost */
815 1.1 brad edata->value_cur = (uint32_t) q;
816 1.1 brad edata->state = ENVSYS_SVALID;
817 1.1 brad } else {
818 1.1 brad error = EINVAL;
819 1.1 brad }
820 1.1 brad }
821 1.1 brad }
822 1.1 brad
823 1.1 brad if (error) {
824 1.1 brad DPRINTF(sc, 2, ("%s: Failed to get new status in refresh %d\n",
825 1.1 brad device_xname(sc->sc_dev), error));
826 1.1 brad }
827 1.1 brad
828 1.1 brad iic_release_bus(sc->sc_tag, 0);
829 1.1 brad out:
830 1.1 brad mutex_exit(&sc->sc_mutex);
831 1.1 brad }
832 1.1 brad
833 1.1 brad static int
834 1.1 brad sht4x_detach(device_t self, int flags)
835 1.1 brad {
836 1.1 brad struct sht4x_sc *sc;
837 1.1 brad
838 1.1 brad sc = device_private(self);
839 1.1 brad
840 1.1 brad mutex_enter(&sc->sc_mutex);
841 1.1 brad
842 1.1 brad /* Remove the sensors */
843 1.1 brad if (sc->sc_sme != NULL) {
844 1.1 brad sysmon_envsys_unregister(sc->sc_sme);
845 1.1 brad sc->sc_sme = NULL;
846 1.1 brad }
847 1.1 brad mutex_exit(&sc->sc_mutex);
848 1.1 brad
849 1.1 brad /* Remove the sysctl tree */
850 1.1 brad sysctl_teardown(&sc->sc_sht4xlog);
851 1.1 brad
852 1.1 brad /* Remove the mutex */
853 1.1 brad mutex_destroy(&sc->sc_mutex);
854 1.1 brad
855 1.1 brad return 0;
856 1.1 brad }
857 1.1 brad
858 1.3 pgoyette MODULE(MODULE_CLASS_DRIVER, sht4xtemp, "iic,sysmon_envsys");
859 1.1 brad
860 1.1 brad #ifdef _MODULE
861 1.1 brad #include "ioconf.c"
862 1.1 brad #endif
863 1.1 brad
864 1.1 brad static int
865 1.1 brad sht4xtemp_modcmd(modcmd_t cmd, void *opaque)
866 1.1 brad {
867 1.1 brad
868 1.1 brad switch (cmd) {
869 1.1 brad case MODULE_CMD_INIT:
870 1.1 brad #ifdef _MODULE
871 1.1 brad return config_init_component(cfdriver_ioconf_sht4xtemp,
872 1.1 brad cfattach_ioconf_sht4xtemp, cfdata_ioconf_sht4xtemp);
873 1.1 brad #else
874 1.1 brad return 0;
875 1.1 brad #endif
876 1.1 brad case MODULE_CMD_FINI:
877 1.1 brad #ifdef _MODULE
878 1.1 brad return config_fini_component(cfdriver_ioconf_sht4xtemp,
879 1.1 brad cfattach_ioconf_sht4xtemp, cfdata_ioconf_sht4xtemp);
880 1.1 brad #else
881 1.1 brad return 0;
882 1.1 brad #endif
883 1.1 brad default:
884 1.1 brad return ENOTTY;
885 1.1 brad }
886 1.1 brad }
887