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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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