2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2021 Andriy Gapon
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include "opt_platform.h"
34 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/sysctl.h>
39 #include <sys/systm.h>
41 #include <machine/bus.h>
43 #include <dev/iicbus/iicbus.h>
44 #include <dev/iicbus/iiconf.h>
47 #include <dev/ofw/ofw_bus.h>
48 #include <dev/ofw/ofw_bus_subr.h>
52 * Driver for HTU21D and compatible temperature and humidity sensors.
53 * Reference documents:
54 * - Measurement Specialties HTU21D datasheet,
55 * - Sensirion SHT21 datasheet,
56 * - Silicon Labs Si7021 datasheet,
57 * - HTU2X Serial Number Reading application note,
58 * - Sensirion Electronic Identification Code (How to read-out the serial number
59 * of SHT2x) application note.
61 #define HTU21_ADDR 0x40
63 #define HTU21_GET_TEMP 0xe3
64 #define HTU21_GET_HUM 0xe5
65 #define HTU21_GET_TEMP_NH 0xf3
66 #define HTU21_GET_HUM_NH 0xf5
67 #define HTU21_WRITE_CFG 0xe6
68 #define HTU21_READ_CFG 0xe7
69 #define HTU21_RESET 0xfe
71 #define HTU2x_SERIAL0_0 0xfa
72 #define HTU2x_SERIAL0_1 0x0f
73 #define HTU2x_SERIAL1_0 0xfc
74 #define HTU2x_SERIAL1_1 0xc9
85 static struct ofw_compat_data compat_data[] = {
86 { "meas,htu21", true },
92 calc_crc(uint16_t data)
94 static const uint16_t polynomial = 0x3100;
97 for (i = 0; i < 16; i++) {
98 int msb_neq = data & 0x8000;
108 check_crc_16(const uint8_t *data, uint8_t expected)
112 crc = calc_crc(((uint16_t)data[0] << 8) | data[1]);
113 return (crc == expected);
117 check_crc_8(const uint8_t data, uint8_t expected)
121 crc = calc_crc(data);
122 return (crc == expected);
126 htu21_get_measurement(device_t dev, uint8_t cmd, uint8_t *data, int count)
129 struct iic_msg msgs[2];
130 struct htu21_softc *sc;
133 sc = device_get_softc(dev);
134 msgs[0].slave = sc->sc_addr;
135 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
139 msgs[1].slave = sc->sc_addr;
140 msgs[1].flags = IIC_M_RD;
144 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
149 htu21_get_measurement_nohold(device_t dev, uint8_t cmd,
150 uint8_t *data, int count)
152 struct iic_msg msgs[2];
153 struct htu21_softc *sc;
157 sc = device_get_softc(dev);
159 msgs[0].slave = sc->sc_addr;
160 msgs[0].flags = IIC_M_WR;
164 msgs[1].slave = sc->sc_addr;
165 msgs[1].flags = IIC_M_RD;
169 error = iicbus_transfer_excl(dev, &msgs[0], 1, IIC_INTRWAIT);
173 for (i = 0; i < hz; i++) {
174 error = iicbus_transfer_excl(dev, &msgs[1], 1, IIC_INTRWAIT);
177 if (error != IIC_ENOACK)
185 htu21_temp_sysctl(SYSCTL_HANDLER_ARGS)
187 struct htu21_softc *sc;
193 sc = device_get_softc(dev);
196 error = htu21_get_measurement(dev, HTU21_GET_TEMP,
197 raw_data, nitems(raw_data));
199 error = htu21_get_measurement_nohold(dev, HTU21_GET_TEMP_NH,
200 raw_data, nitems(raw_data));
204 } else if (!check_crc_16(raw_data, raw_data[2])) {
208 temp = (((uint16_t)raw_data[0]) << 8) | (raw_data[1] & 0xfc);
209 temp = ((temp * 17572) >> 16 ) + 27315 - 4685;
212 error = sysctl_handle_int(oidp, &temp, 0, req);
217 htu21_rh_sysctl(SYSCTL_HANDLER_ARGS)
219 struct htu21_softc *sc;
225 sc = device_get_softc(dev);
228 error = htu21_get_measurement(dev, HTU21_GET_HUM,
229 raw_data, nitems(raw_data));
231 error = htu21_get_measurement_nohold(dev, HTU21_GET_HUM_NH,
232 raw_data, nitems(raw_data));
236 } else if (!check_crc_16(raw_data, raw_data[2])) {
240 rh = (((uint16_t)raw_data[0]) << 8) | (raw_data[1] & 0xfc);
241 rh = ((rh * 12500) >> 16 ) - 600;
244 error = sysctl_handle_int(oidp, &rh, 0, req);
249 htu21_get_cfg(device_t dev, uint8_t *cfg)
252 struct iic_msg msgs[2];
253 struct htu21_softc *sc;
257 sc = device_get_softc(dev);
258 cmd = HTU21_READ_CFG;
259 msgs[0].slave = sc->sc_addr;
260 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
264 msgs[1].slave = sc->sc_addr;
265 msgs[1].flags = IIC_M_RD;
269 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
274 htu21_set_cfg(device_t dev, uint8_t cfg)
278 struct htu21_softc *sc;
282 sc = device_get_softc(dev);
283 buf[0] = HTU21_WRITE_CFG;
285 msg.slave = sc->sc_addr;
286 msg.flags = IIC_M_WR;
290 error = iicbus_transfer_excl(dev, &msg, 1, IIC_INTRWAIT);
295 htu21_heater_sysctl(SYSCTL_HANDLER_ARGS)
297 struct htu21_softc *sc;
303 sc = device_get_softc(dev);
305 error = htu21_get_cfg(dev, &cfg);
309 heater = (cfg & 0x04) != 0;
310 error = sysctl_handle_int(oidp, &heater, 0, req);
311 if (error != 0 || req->newptr == NULL)
315 cfg |= (heater > 0) << 2;
316 error = htu21_set_cfg(dev, cfg);
317 return (error != 0 ? EIO : 0);
321 htu21_power_sysctl(SYSCTL_HANDLER_ARGS)
323 struct htu21_softc *sc;
329 sc = device_get_softc(dev);
331 error = htu21_get_cfg(dev, &cfg);
335 power = (cfg & 0x40) == 0;
336 error = sysctl_handle_int(oidp, &power, 0, req);
341 * May be incompatible with some chips like SHT21 and Si7021.
344 htu21_get_serial(device_t dev)
347 struct iic_msg msgs[2];
348 struct htu21_softc *sc;
351 int error, cksum_err;
354 sc = device_get_softc(dev);
355 cmd[0] = HTU2x_SERIAL0_0;
356 cmd[1] = HTU2x_SERIAL0_1;
357 msgs[0].slave = sc->sc_addr;
358 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
359 msgs[0].len = nitems(cmd);
362 msgs[1].slave = sc->sc_addr;
363 msgs[1].flags = IIC_M_RD;
364 msgs[1].len = nitems(data);
367 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
372 for (i = 0; i < nitems(data); i += 2) {
373 if (!check_crc_8(data[i], data[i + 1]))
375 sc->sc_serial[2 + i / 2] = data[i];
378 cmd[0] = HTU2x_SERIAL1_0;
379 cmd[1] = HTU2x_SERIAL1_1;
380 msgs[0].slave = sc->sc_addr;
381 msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
382 msgs[0].len = nitems(cmd);
385 msgs[1].slave = sc->sc_addr;
386 msgs[1].flags = IIC_M_RD;
390 error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
394 if (!check_crc_16(&data[0], data[2]))
396 sc->sc_serial[6] = data[0];
397 sc->sc_serial[7] = data[1];
399 if (!check_crc_16(&data[3], data[5]))
401 sc->sc_serial[0] = data[3];
402 sc->sc_serial[1] = data[4];
408 htu21_start(void *arg)
411 struct htu21_softc *sc;
412 struct sysctl_ctx_list *ctx;
413 struct sysctl_oid *tree_node;
414 struct sysctl_oid_list *tree;
420 for (int i = 0; i < 5; i++) {
421 error = htu21_get_serial(dev);
426 device_printf(dev, "serial number: %8D (checksum %scorrect)\n",
427 sc->sc_serial, ":", error == 0 ? "" : "in");
429 device_printf(dev, "failed to get serial number, err = %d\n",
433 ctx = device_get_sysctl_ctx(dev);
434 tree_node = device_get_sysctl_tree(dev);
435 tree = SYSCTL_CHILDREN(tree_node);
437 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temperature",
438 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
439 htu21_temp_sysctl, "IK2", "Current temperature");
440 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "humidity",
441 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
442 htu21_rh_sysctl, "I", "Relative humidity in 0.01%% units");
443 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "heater",
444 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, 0,
445 htu21_heater_sysctl, "IU", "Enable built-in heater");
446 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "power",
447 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
448 htu21_power_sysctl, "IU", "If sensor's power is good");
449 SYSCTL_ADD_BOOL(ctx, tree, OID_AUTO, "hold_bus",
450 CTLFLAG_RW, &sc->sc_hold, 0,
451 "Whether device should hold I2C bus while measuring");
452 SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "crc_errors",
453 CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, &sc->sc_errcount, 0,
454 "Number of checksum errors");
458 htu21_probe(device_t dev)
463 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
466 addr = iicbus_get_addr(dev);
467 if (addr != (HTU21_ADDR << 1)) {
468 device_printf(dev, "non-standard slave address 0x%02x\n",
472 device_set_desc(dev, "HTU21 temperature and humidity sensor");
473 return (BUS_PROBE_GENERIC);
477 htu21_attach(device_t dev)
479 struct htu21_softc *sc;
481 sc = device_get_softc(dev);
483 sc->sc_addr = iicbus_get_addr(dev);
486 * We have to wait until interrupts are enabled. Usually I2C read
487 * and write only works when the interrupts are available.
489 config_intrhook_oneshot(htu21_start, sc);
494 htu21_detach(device_t dev)
499 static device_method_t htu21_methods[] = {
500 /* Device interface */
501 DEVMETHOD(device_probe, htu21_probe),
502 DEVMETHOD(device_attach, htu21_attach),
503 DEVMETHOD(device_detach, htu21_detach),
508 static driver_t htu21_driver = {
511 sizeof(struct htu21_softc)
514 static devclass_t htu21_devclass;
516 DRIVER_MODULE(htu21, iicbus, htu21_driver, htu21_devclass, 0, 0);
517 MODULE_DEPEND(htu21, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
518 MODULE_VERSION(htu21, 1);
520 IICBUS_FDT_PNP_INFO(compat_data);