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