1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021 Andriy Gapon
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #include <sys/cdefs.h>
30 #include "opt_platform.h"
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/sysctl.h>
37 #include <sys/systm.h>
38
39 #include <machine/bus.h>
40
41 #include <dev/iicbus/iicbus.h>
42 #include <dev/iicbus/iiconf.h>
43
44 #ifdef FDT
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47 #endif
48
49 /*
50 * Driver for HTU21D and compatible temperature and humidity sensors.
51 * Reference documents:
52 * - Measurement Specialties HTU21D datasheet,
53 * - Sensirion SHT21 datasheet,
54 * - Silicon Labs Si7021 datasheet,
55 * - HTU2X Serial Number Reading application note,
56 * - Sensirion Electronic Identification Code (How to read-out the serial number
57 * of SHT2x) application note.
58 */
59 #define HTU21_ADDR 0x40
60
61 #define HTU21_GET_TEMP 0xe3
62 #define HTU21_GET_HUM 0xe5
63 #define HTU21_GET_TEMP_NH 0xf3
64 #define HTU21_GET_HUM_NH 0xf5
65 #define HTU21_WRITE_CFG 0xe6
66 #define HTU21_READ_CFG 0xe7
67 #define HTU21_RESET 0xfe
68
69 #define HTU2x_SERIAL0_0 0xfa
70 #define HTU2x_SERIAL0_1 0x0f
71 #define HTU2x_SERIAL1_0 0xfc
72 #define HTU2x_SERIAL1_1 0xc9
73
74 struct htu21_softc {
75 device_t sc_dev;
76 uint32_t sc_addr;
77 uint8_t sc_serial[8];
78 int sc_errcount;
79 bool sc_hold;
80 };
81
82 #ifdef FDT
83 static struct ofw_compat_data compat_data[] = {
84 { "meas,htu21", true },
85 { NULL, false }
86 };
87 #endif
88
89 static uint8_t
calc_crc(uint16_t data)90 calc_crc(uint16_t data)
91 {
92 static const uint16_t polynomial = 0x3100;
93 int i;
94
95 for (i = 0; i < 16; i++) {
96 int msb_neq = data & 0x8000;
97
98 data <<= 1;
99 if (msb_neq)
100 data ^= polynomial;
101 }
102 return (data >> 8);
103 }
104
105 static int
check_crc_16(const uint8_t * data,uint8_t expected)106 check_crc_16(const uint8_t *data, uint8_t expected)
107 {
108 uint8_t crc;
109
110 crc = calc_crc(((uint16_t)data[0] << 8) | data[1]);
111 return (crc == expected);
112 }
113
114 static int
check_crc_8(const uint8_t data,uint8_t expected)115 check_crc_8(const uint8_t data, uint8_t expected)
116 {
117 uint8_t crc;
118
119 crc = calc_crc(data);
120 return (crc == expected);
121 }
122
123 static int
htu21_get_measurement(device_t dev,uint8_t cmd,uint8_t * data,int count)124 htu21_get_measurement(device_t dev, uint8_t cmd, uint8_t *data, int count)
125 {
126
127 struct iic_msg msgs[2];
128 struct htu21_softc *sc;
129 int error;
130
131 sc = device_get_softc(dev);
132 msgs[0].slave = sc->sc_addr;
133 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
134 msgs[0].len = 1;
135 msgs[0].buf = &cmd;
136
137 msgs[1].slave = sc->sc_addr;
138 msgs[1].flags = IIC_M_RD;
139 msgs[1].len = count;
140 msgs[1].buf = data;
141
142 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
143 return (error);
144 }
145
146 static int
htu21_get_measurement_nohold(device_t dev,uint8_t cmd,uint8_t * data,int count)147 htu21_get_measurement_nohold(device_t dev, uint8_t cmd,
148 uint8_t *data, int count)
149 {
150 struct iic_msg msgs[2];
151 struct htu21_softc *sc;
152 int error;
153 int i;
154
155 sc = device_get_softc(dev);
156
157 msgs[0].slave = sc->sc_addr;
158 msgs[0].flags = IIC_M_WR;
159 msgs[0].len = 1;
160 msgs[0].buf = &cmd;
161
162 msgs[1].slave = sc->sc_addr;
163 msgs[1].flags = IIC_M_RD;
164 msgs[1].len = count;
165 msgs[1].buf = data;
166
167 error = iicbus_transfer_excl(dev, &msgs[0], 1, IIC_INTRWAIT);
168 if (error != 0)
169 return (error);
170
171 for (i = 0; i < hz; i++) {
172 error = iicbus_transfer_excl(dev, &msgs[1], 1, IIC_INTRWAIT);
173 if (error == 0)
174 return (0);
175 if (error != IIC_ENOACK)
176 break;
177 pause("htu21", 1);
178 }
179 return (error);
180 }
181
182 static int
htu21_temp_sysctl(SYSCTL_HANDLER_ARGS)183 htu21_temp_sysctl(SYSCTL_HANDLER_ARGS)
184 {
185 struct htu21_softc *sc;
186 device_t dev;
187 uint8_t raw_data[3];
188 int error, temp;
189
190 dev = arg1;
191 sc = device_get_softc(dev);
192
193 if (req->oldptr != NULL) {
194 if (sc->sc_hold)
195 error = htu21_get_measurement(dev, HTU21_GET_TEMP,
196 raw_data, nitems(raw_data));
197 else
198 error = htu21_get_measurement_nohold(dev,
199 HTU21_GET_TEMP_NH, raw_data, nitems(raw_data));
200
201 if (error != 0) {
202 return (EIO);
203 } else if (!check_crc_16(raw_data, raw_data[2])) {
204 temp = -1;
205 sc->sc_errcount++;
206 } else {
207 temp = (((uint16_t)raw_data[0]) << 8) |
208 (raw_data[1] & 0xfc);
209 temp = ((temp * 17572) >> 16 ) + 27315 - 4685;
210 }
211 }
212
213 error = sysctl_handle_int(oidp, &temp, 0, req);
214 return (error);
215 }
216
217 static int
htu21_rh_sysctl(SYSCTL_HANDLER_ARGS)218 htu21_rh_sysctl(SYSCTL_HANDLER_ARGS)
219 {
220 struct htu21_softc *sc;
221 device_t dev;
222 uint8_t raw_data[3];
223 int error, rh;
224
225 dev = arg1;
226 sc = device_get_softc(dev);
227
228 if (req->oldptr != NULL) {
229 if (sc->sc_hold)
230 error = htu21_get_measurement(dev, HTU21_GET_HUM,
231 raw_data, nitems(raw_data));
232 else
233 error = htu21_get_measurement_nohold(dev,
234 HTU21_GET_HUM_NH, raw_data, nitems(raw_data));
235
236 if (error != 0) {
237 return (EIO);
238 } else if (!check_crc_16(raw_data, raw_data[2])) {
239 rh = -1;
240 sc->sc_errcount++;
241 } else {
242 rh = (((uint16_t)raw_data[0]) << 8) |
243 (raw_data[1] & 0xfc);
244 rh = ((rh * 12500) >> 16 ) - 600;
245 }
246 }
247
248 error = sysctl_handle_int(oidp, &rh, 0, req);
249 return (error);
250 }
251
252 static int
htu21_get_cfg(device_t dev,uint8_t * cfg)253 htu21_get_cfg(device_t dev, uint8_t *cfg)
254 {
255
256 struct iic_msg msgs[2];
257 struct htu21_softc *sc;
258 uint8_t cmd;
259 int error;
260
261 sc = device_get_softc(dev);
262 cmd = HTU21_READ_CFG;
263 msgs[0].slave = sc->sc_addr;
264 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
265 msgs[0].len = 1;
266 msgs[0].buf = &cmd;
267
268 msgs[1].slave = sc->sc_addr;
269 msgs[1].flags = IIC_M_RD;
270 msgs[1].len = 1;
271 msgs[1].buf = cfg;
272
273 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
274 return (error);
275 }
276
277 static int
htu21_set_cfg(device_t dev,uint8_t cfg)278 htu21_set_cfg(device_t dev, uint8_t cfg)
279 {
280
281 struct iic_msg msg;
282 struct htu21_softc *sc;
283 uint8_t buf[2];
284 int error;
285
286 sc = device_get_softc(dev);
287 buf[0] = HTU21_WRITE_CFG;
288 buf[1] = cfg;
289 msg.slave = sc->sc_addr;
290 msg.flags = IIC_M_WR;
291 msg.len = 2;
292 msg.buf = buf;
293
294 error = iicbus_transfer_excl(dev, &msg, 1, IIC_INTRWAIT);
295 return (error);
296 }
297
298 static int
htu21_heater_sysctl(SYSCTL_HANDLER_ARGS)299 htu21_heater_sysctl(SYSCTL_HANDLER_ARGS)
300 {
301 device_t dev;
302 uint8_t cfg;
303 int error, heater;
304
305 dev = arg1;
306
307 if (req->oldptr != NULL) {
308 error = htu21_get_cfg(dev, &cfg);
309 if (error != 0)
310 return (EIO);
311 heater = (cfg & 0x04) != 0;
312 }
313 error = sysctl_handle_int(oidp, &heater, 0, req);
314 if (error != 0 || req->newptr == NULL)
315 return (error);
316
317 cfg &= ~0x04;
318 cfg |= (heater > 0) << 2;
319 error = htu21_set_cfg(dev, cfg);
320 return (error != 0 ? EIO : 0);
321 }
322
323 static int
htu21_power_sysctl(SYSCTL_HANDLER_ARGS)324 htu21_power_sysctl(SYSCTL_HANDLER_ARGS)
325 {
326 device_t dev;
327 uint8_t cfg;
328 int error, power;
329
330 dev = arg1;
331
332 if (req->oldptr != NULL) {
333 error = htu21_get_cfg(dev, &cfg);
334 if (error != 0)
335 return (EIO);
336 power = (cfg & 0x40) == 0;
337 }
338 error = sysctl_handle_int(oidp, &power, 0, req);
339 return (error);
340 }
341
342 /*
343 * May be incompatible with some chips like SHT21 and Si7021.
344 */
345 static int
htu21_get_serial(device_t dev)346 htu21_get_serial(device_t dev)
347 {
348
349 struct iic_msg msgs[2];
350 struct htu21_softc *sc;
351 uint8_t data[8];
352 uint8_t cmd[2];
353 int error, cksum_err;
354 int i;
355
356 sc = device_get_softc(dev);
357 cmd[0] = HTU2x_SERIAL0_0;
358 cmd[1] = HTU2x_SERIAL0_1;
359 msgs[0].slave = sc->sc_addr;
360 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
361 msgs[0].len = nitems(cmd);
362 msgs[0].buf = cmd;
363
364 msgs[1].slave = sc->sc_addr;
365 msgs[1].flags = IIC_M_RD;
366 msgs[1].len = nitems(data);
367 msgs[1].buf = data;
368
369 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
370 if (error != 0)
371 return (EIO);
372
373 cksum_err = 0;
374 for (i = 0; i < nitems(data); i += 2) {
375 if (!check_crc_8(data[i], data[i + 1]))
376 cksum_err = EINVAL;
377 sc->sc_serial[2 + i / 2] = data[i];
378 }
379
380 cmd[0] = HTU2x_SERIAL1_0;
381 cmd[1] = HTU2x_SERIAL1_1;
382 msgs[0].slave = sc->sc_addr;
383 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
384 msgs[0].len = nitems(cmd);
385 msgs[0].buf = cmd;
386
387 msgs[1].slave = sc->sc_addr;
388 msgs[1].flags = IIC_M_RD;
389 msgs[1].len = 6;
390 msgs[1].buf = data;
391
392 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
393 if (error != 0)
394 return (EIO);
395
396 if (!check_crc_16(&data[0], data[2]))
397 cksum_err = EINVAL;
398 sc->sc_serial[6] = data[0];
399 sc->sc_serial[7] = data[1];
400
401 if (!check_crc_16(&data[3], data[5]))
402 cksum_err = EINVAL;
403 sc->sc_serial[0] = data[3];
404 sc->sc_serial[1] = data[4];
405
406 return (cksum_err);
407 }
408
409 static void
htu21_start(void * arg)410 htu21_start(void *arg)
411 {
412 device_t dev;
413 struct htu21_softc *sc;
414 struct sysctl_ctx_list *ctx;
415 struct sysctl_oid *tree_node;
416 struct sysctl_oid_list *tree;
417 int error;
418
419 sc = arg;
420 dev = sc->sc_dev;
421
422 for (int i = 0; i < 5; i++) {
423 error = htu21_get_serial(dev);
424 if (error == 0)
425 break;
426 }
427 if (error != EIO) {
428 device_printf(dev, "serial number: %8D (checksum %scorrect)\n",
429 sc->sc_serial, ":", error == 0 ? "" : "in");
430 } else {
431 device_printf(dev, "failed to get serial number, err = %d\n",
432 error);
433 }
434
435 ctx = device_get_sysctl_ctx(dev);
436 tree_node = device_get_sysctl_tree(dev);
437 tree = SYSCTL_CHILDREN(tree_node);
438
439 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temperature",
440 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
441 htu21_temp_sysctl, "IK2", "Current temperature");
442 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "humidity",
443 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
444 htu21_rh_sysctl, "I", "Relative humidity in 0.01%% units");
445 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "heater",
446 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, 0,
447 htu21_heater_sysctl, "IU", "Enable built-in heater");
448 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "power",
449 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
450 htu21_power_sysctl, "IU", "If sensor's power is good");
451 SYSCTL_ADD_BOOL(ctx, tree, OID_AUTO, "hold_bus",
452 CTLFLAG_RW, &sc->sc_hold, 0,
453 "Whether device should hold I2C bus while measuring");
454 SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "crc_errors",
455 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, &sc->sc_errcount, 0,
456 "Number of checksum errors");
457 }
458
459 static int
htu21_probe(device_t dev)460 htu21_probe(device_t dev)
461 {
462 uint8_t addr;
463 int rc;
464
465 #ifdef FDT
466 if (!ofw_bus_status_okay(dev))
467 return (ENXIO);
468 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data)
469 rc = BUS_PROBE_GENERIC;
470 else
471 #endif
472 rc = BUS_PROBE_NOWILDCARD;
473
474 addr = iicbus_get_addr(dev);
475 if (addr != (HTU21_ADDR << 1)) {
476 device_printf(dev, "non-standard slave address 0x%02x\n",
477 addr >> 1);
478 }
479
480 device_set_desc(dev, "HTU21 temperature and humidity sensor");
481 return (rc);
482 }
483
484 static int
htu21_attach(device_t dev)485 htu21_attach(device_t dev)
486 {
487 struct htu21_softc *sc;
488
489 sc = device_get_softc(dev);
490 sc->sc_dev = dev;
491 sc->sc_addr = iicbus_get_addr(dev);
492
493 /*
494 * We have to wait until interrupts are enabled. Usually I2C read
495 * and write only works when the interrupts are available.
496 */
497 config_intrhook_oneshot(htu21_start, sc);
498 return (0);
499 }
500
501 static int
htu21_detach(device_t dev)502 htu21_detach(device_t dev)
503 {
504 return (0);
505 }
506
507 static device_method_t htu21_methods[] = {
508 /* Device interface */
509 DEVMETHOD(device_probe, htu21_probe),
510 DEVMETHOD(device_attach, htu21_attach),
511 DEVMETHOD(device_detach, htu21_detach),
512
513 DEVMETHOD_END
514 };
515
516 static driver_t htu21_driver = {
517 "htu21",
518 htu21_methods,
519 sizeof(struct htu21_softc)
520 };
521
522 DRIVER_MODULE(htu21, iicbus, htu21_driver, 0, 0);
523 MODULE_DEPEND(htu21, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
524 MODULE_VERSION(htu21, 1);
525 IICBUS_FDT_PNP_INFO(compat_data);
526