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 74struct 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 83static struct ofw_compat_data compat_data[] = { 84 { "meas,htu21", true }, 85 { NULL, false } 86}; 87#endif 88 89static uint8_t 90calc_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 105static int 106check_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 114static int 115check_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 123static int 124htu21_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 146static int 147htu21_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 182static int 183htu21_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 217static int 218htu21_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 252static int 253htu21_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 277static int 278htu21_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 298static int 299htu21_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 323static int 324htu21_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 */ 345static int 346htu21_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 409static void 410htu21_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 459static int 460htu21_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 484static int 485htu21_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 501static int 502htu21_detach(device_t dev) 503{ 504 return (0); 505} 506 507static 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 516static driver_t htu21_driver = { 517 "htu21", 518 htu21_methods, 519 sizeof(struct htu21_softc) 520}; 521 522DRIVER_MODULE(htu21, iicbus, htu21_driver, 0, 0); 523MODULE_DEPEND(htu21, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); 524MODULE_VERSION(htu21, 1); 525IICBUS_FDT_PNP_INFO(compat_data); 526