]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iicbus/htu21.c
unbount: Vendor import 1.14.0rc1
[FreeBSD/FreeBSD.git] / sys / dev / iicbus / htu21.c
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 __FBSDID("$FreeBSD$");
31
32 #include "opt_platform.h"
33
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/sysctl.h>
39 #include <sys/systm.h>
40
41 #include <machine/bus.h>
42
43 #include <dev/iicbus/iicbus.h>
44 #include <dev/iicbus/iiconf.h>
45
46 #ifdef FDT
47 #include <dev/ofw/ofw_bus.h>
48 #include <dev/ofw/ofw_bus_subr.h>
49 #endif
50
51 /*
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.
60  */
61 #define HTU21_ADDR              0x40
62
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
70
71 #define HTU2x_SERIAL0_0         0xfa
72 #define HTU2x_SERIAL0_1         0x0f
73 #define HTU2x_SERIAL1_0         0xfc
74 #define HTU2x_SERIAL1_1         0xc9
75
76 struct htu21_softc {
77         device_t                sc_dev;
78         uint32_t                sc_addr;
79         uint8_t                 sc_serial[8];
80         int                     sc_errcount;
81         bool                    sc_hold;
82 };
83
84 #ifdef FDT
85 static struct ofw_compat_data compat_data[] = {
86         { "meas,htu21",         true },
87         { NULL,                 false }
88 };
89 #endif
90
91 static uint8_t
92 calc_crc(uint16_t data)
93 {
94         static const uint16_t polynomial = 0x3100;
95         int i;
96
97         for (i = 0; i < 16; i++) {
98                 int msb_neq = data & 0x8000;
99
100                 data <<= 1;
101                 if (msb_neq)
102                         data ^= polynomial;
103         }
104         return (data >> 8);
105 }
106
107 static int
108 check_crc_16(const uint8_t *data, uint8_t expected)
109 {
110         uint8_t crc;
111
112         crc = calc_crc(((uint16_t)data[0] << 8) | data[1]);
113         return (crc == expected);
114 }
115
116 static int
117 check_crc_8(const uint8_t data, uint8_t expected)
118 {
119         uint8_t crc;
120
121         crc = calc_crc(data);
122         return (crc == expected);
123 }
124
125 static int
126 htu21_get_measurement(device_t dev, uint8_t cmd, uint8_t *data, int count)
127 {
128
129         struct iic_msg msgs[2];
130         struct htu21_softc *sc;
131         int error;
132
133         sc = device_get_softc(dev);
134         msgs[0].slave = sc->sc_addr;
135         msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
136         msgs[0].len = 1;
137         msgs[0].buf = &cmd;
138
139         msgs[1].slave = sc->sc_addr;
140         msgs[1].flags = IIC_M_RD;
141         msgs[1].len = count;
142         msgs[1].buf = data;
143
144         error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
145         return (error);
146 }
147
148 static int
149 htu21_get_measurement_nohold(device_t dev, uint8_t cmd,
150     uint8_t *data, int count)
151 {
152         struct iic_msg msgs[2];
153         struct htu21_softc *sc;
154         int error;
155         int i;
156
157         sc = device_get_softc(dev);
158
159         msgs[0].slave = sc->sc_addr;
160         msgs[0].flags = IIC_M_WR;
161         msgs[0].len = 1;
162         msgs[0].buf = &cmd;
163
164         msgs[1].slave = sc->sc_addr;
165         msgs[1].flags = IIC_M_RD;
166         msgs[1].len = count;
167         msgs[1].buf = data;
168
169         error = iicbus_transfer_excl(dev, &msgs[0], 1, IIC_INTRWAIT);
170         if (error != 0)
171                 return (error);
172
173         for (i = 0; i < hz; i++) {
174                 error = iicbus_transfer_excl(dev, &msgs[1], 1, IIC_INTRWAIT);
175                 if (error == 0)
176                         return (0);
177                 if (error != IIC_ENOACK)
178                         break;
179                 pause("htu21", 1);
180         }
181         return (error);
182 }
183
184 static int
185 htu21_temp_sysctl(SYSCTL_HANDLER_ARGS)
186 {
187         struct htu21_softc *sc;
188         device_t dev;
189         uint8_t raw_data[3];
190         int error, temp;
191
192         dev = arg1;
193         sc = device_get_softc(dev);
194
195         if (req->oldptr != NULL) {
196                 if (sc->sc_hold)
197                         error = htu21_get_measurement(dev, HTU21_GET_TEMP,
198                             raw_data, nitems(raw_data));
199                 else
200                         error = htu21_get_measurement_nohold(dev,
201                             HTU21_GET_TEMP_NH, raw_data, nitems(raw_data));
202
203                 if (error != 0) {
204                         return (EIO);
205                 } else if (!check_crc_16(raw_data, raw_data[2])) {
206                         temp = -1;
207                         sc->sc_errcount++;
208                 } else {
209                         temp = (((uint16_t)raw_data[0]) << 8) |
210                             (raw_data[1] & 0xfc);
211                         temp = ((temp * 17572) >> 16 ) + 27315 - 4685;
212                 }
213         }
214
215         error = sysctl_handle_int(oidp, &temp, 0, req);
216         return (error);
217 }
218
219 static int
220 htu21_rh_sysctl(SYSCTL_HANDLER_ARGS)
221 {
222         struct htu21_softc *sc;
223         device_t dev;
224         uint8_t raw_data[3];
225         int error, rh;
226
227         dev = arg1;
228         sc = device_get_softc(dev);
229
230         if (req->oldptr != NULL) {
231                 if (sc->sc_hold)
232                         error = htu21_get_measurement(dev, HTU21_GET_HUM,
233                             raw_data, nitems(raw_data));
234                 else
235                         error = htu21_get_measurement_nohold(dev,
236                             HTU21_GET_HUM_NH, raw_data, nitems(raw_data));
237
238                 if (error != 0) {
239                         return (EIO);
240                 } else if (!check_crc_16(raw_data, raw_data[2])) {
241                         rh = -1;
242                         sc->sc_errcount++;
243                 } else {
244                         rh = (((uint16_t)raw_data[0]) << 8) |
245                             (raw_data[1] & 0xfc);
246                         rh = ((rh * 12500) >> 16 ) - 600;
247                 }
248         }
249
250         error = sysctl_handle_int(oidp, &rh, 0, req);
251         return (error);
252 }
253
254 static int
255 htu21_get_cfg(device_t dev, uint8_t *cfg)
256 {
257
258         struct iic_msg msgs[2];
259         struct htu21_softc *sc;
260         uint8_t cmd;
261         int error;
262
263         sc = device_get_softc(dev);
264         cmd = HTU21_READ_CFG;
265         msgs[0].slave = sc->sc_addr;
266         msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
267         msgs[0].len = 1;
268         msgs[0].buf = &cmd;
269
270         msgs[1].slave = sc->sc_addr;
271         msgs[1].flags = IIC_M_RD;
272         msgs[1].len = 1;
273         msgs[1].buf = cfg;
274
275         error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
276         return (error);
277 }
278
279 static int
280 htu21_set_cfg(device_t dev, uint8_t cfg)
281 {
282
283         struct iic_msg msg;
284         struct htu21_softc *sc;
285         uint8_t buf[2];
286         int error;
287
288         sc = device_get_softc(dev);
289         buf[0] = HTU21_WRITE_CFG;
290         buf[1] = cfg;
291         msg.slave = sc->sc_addr;
292         msg.flags = IIC_M_WR;
293         msg.len = 2;
294         msg.buf = buf;
295
296         error = iicbus_transfer_excl(dev, &msg, 1, IIC_INTRWAIT);
297         return (error);
298 }
299
300 static int
301 htu21_heater_sysctl(SYSCTL_HANDLER_ARGS)
302 {
303         struct htu21_softc *sc;
304         device_t dev;
305         uint8_t cfg;
306         int error, heater;
307
308         dev = arg1;
309         sc = device_get_softc(dev);
310
311         if (req->oldptr != NULL) {
312                 error = htu21_get_cfg(dev, &cfg);
313                 if (error != 0)
314                         return (EIO);
315                 heater = (cfg & 0x04) != 0;
316         }
317         error = sysctl_handle_int(oidp, &heater, 0, req);
318         if (error != 0 || req->newptr == NULL)
319                 return (error);
320
321         cfg &= ~0x04;
322         cfg |= (heater > 0) << 2;
323         error = htu21_set_cfg(dev, cfg);
324         return (error != 0 ? EIO : 0);
325 }
326
327 static int
328 htu21_power_sysctl(SYSCTL_HANDLER_ARGS)
329 {
330         struct htu21_softc *sc;
331         device_t dev;
332         uint8_t cfg;
333         int error, power;
334
335         dev = arg1;
336         sc = device_get_softc(dev);
337
338         if (req->oldptr != NULL) {
339                 error = htu21_get_cfg(dev, &cfg);
340                 if (error != 0)
341                         return (EIO);
342                 power = (cfg & 0x40) == 0;
343         }
344         error = sysctl_handle_int(oidp, &power, 0, req);
345         return (error);
346 }
347
348 /*
349  * May be incompatible with some chips like SHT21 and Si7021.
350  */
351 static int
352 htu21_get_serial(device_t dev)
353 {
354
355         struct iic_msg msgs[2];
356         struct htu21_softc *sc;
357         uint8_t data[8];
358         uint8_t cmd[2];
359         int error, cksum_err;
360         int i;
361
362         sc = device_get_softc(dev);
363         cmd[0] = HTU2x_SERIAL0_0;
364         cmd[1] = HTU2x_SERIAL0_1;
365         msgs[0].slave = sc->sc_addr;
366         msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
367         msgs[0].len = nitems(cmd);
368         msgs[0].buf = cmd;
369
370         msgs[1].slave = sc->sc_addr;
371         msgs[1].flags = IIC_M_RD;
372         msgs[1].len = nitems(data);
373         msgs[1].buf = data;
374
375         error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
376         if (error != 0)
377                 return (EIO);
378
379         cksum_err = 0;
380         for (i = 0; i < nitems(data); i += 2) {
381                 if (!check_crc_8(data[i], data[i + 1]))
382                         cksum_err = EINVAL;
383                 sc->sc_serial[2 + i / 2] = data[i];
384         }
385
386         cmd[0] = HTU2x_SERIAL1_0;
387         cmd[1] = HTU2x_SERIAL1_1;
388         msgs[0].slave = sc->sc_addr;
389         msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
390         msgs[0].len = nitems(cmd);
391         msgs[0].buf = cmd;
392
393         msgs[1].slave = sc->sc_addr;
394         msgs[1].flags = IIC_M_RD;
395         msgs[1].len = 6;
396         msgs[1].buf = data;
397
398         error = iicbus_transfer_excl(dev, msgs, nitems(msgs), IIC_INTRWAIT);
399         if (error != 0)
400                 return (EIO);
401
402         if (!check_crc_16(&data[0], data[2]))
403                 cksum_err = EINVAL;
404         sc->sc_serial[6] = data[0];
405         sc->sc_serial[7] = data[1];
406
407         if (!check_crc_16(&data[3], data[5]))
408                 cksum_err = EINVAL;
409         sc->sc_serial[0] = data[3];
410         sc->sc_serial[1] = data[4];
411
412         return (cksum_err);
413 }
414
415 static void
416 htu21_start(void *arg)
417 {
418         device_t dev;
419         struct htu21_softc *sc;
420         struct sysctl_ctx_list *ctx;
421         struct sysctl_oid *tree_node;
422         struct sysctl_oid_list *tree;
423         int error;
424
425         sc = arg;
426         dev = sc->sc_dev;
427
428         for (int i = 0; i < 5; i++) {
429                 error = htu21_get_serial(dev);
430                 if (error == 0)
431                         break;
432         }
433         if (error != EIO) {
434                 device_printf(dev, "serial number: %8D (checksum %scorrect)\n",
435                     sc->sc_serial, ":", error == 0 ? "" : "in");
436         } else {
437                 device_printf(dev, "failed to get serial number, err = %d\n",
438                     error);
439         }
440
441         ctx = device_get_sysctl_ctx(dev);
442         tree_node = device_get_sysctl_tree(dev);
443         tree = SYSCTL_CHILDREN(tree_node);
444
445         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temperature",
446             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
447             htu21_temp_sysctl, "IK2", "Current temperature");
448         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "humidity",
449             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
450             htu21_rh_sysctl, "I", "Relative humidity in 0.01%% units");
451         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "heater",
452             CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, dev, 0,
453             htu21_heater_sysctl, "IU", "Enable built-in heater");
454         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "power",
455             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0,
456             htu21_power_sysctl, "IU", "If sensor's power is good");
457         SYSCTL_ADD_BOOL(ctx, tree, OID_AUTO, "hold_bus",
458             CTLFLAG_RW, &sc->sc_hold, 0,
459             "Whether device should hold I2C bus while measuring");
460         SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "crc_errors",
461             CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, &sc->sc_errcount, 0,
462             "Number of checksum errors");
463 }
464
465 static int
466 htu21_probe(device_t dev)
467 {
468         uint8_t addr;
469         int rc;
470
471 #ifdef FDT
472         if (!ofw_bus_status_okay(dev))
473                 return (ENXIO);
474         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data)
475                 rc = BUS_PROBE_GENERIC;
476         else
477 #endif
478                 rc = BUS_PROBE_NOWILDCARD;
479
480         addr = iicbus_get_addr(dev);
481         if (addr != (HTU21_ADDR << 1)) {
482                 device_printf(dev, "non-standard slave address 0x%02x\n",
483                     addr >> 1);
484         }
485
486         device_set_desc(dev, "HTU21 temperature and humidity sensor");
487         return (rc);
488 }
489
490 static int
491 htu21_attach(device_t dev)
492 {
493         struct htu21_softc *sc;
494
495         sc = device_get_softc(dev);
496         sc->sc_dev = dev;
497         sc->sc_addr = iicbus_get_addr(dev);
498
499         /*
500          * We have to wait until interrupts are enabled.  Usually I2C read
501          * and write only works when the interrupts are available.
502          */
503         config_intrhook_oneshot(htu21_start, sc);
504         return (0);
505 }
506
507 static int
508 htu21_detach(device_t dev)
509 {
510         return (0);
511 }
512
513 static device_method_t  htu21_methods[] = {
514         /* Device interface */
515         DEVMETHOD(device_probe,         htu21_probe),
516         DEVMETHOD(device_attach,        htu21_attach),
517         DEVMETHOD(device_detach,        htu21_detach),
518
519         DEVMETHOD_END
520 };
521
522 static driver_t htu21_driver = {
523         "htu21",
524         htu21_methods,
525         sizeof(struct htu21_softc)
526 };
527
528 static devclass_t htu21_devclass;
529
530 DRIVER_MODULE(htu21, iicbus, htu21_driver, htu21_devclass, 0, 0);
531 MODULE_DEPEND(htu21, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
532 MODULE_VERSION(htu21, 1);
533 IICBUS_FDT_PNP_INFO(compat_data);