xref: /openbsd-src/sys/dev/usb/uthum.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: uthum.c,v 1.40 2024/05/23 03:21:09 jsg Exp $   */
2fe0fc4c7Syuo 
3fe0fc4c7Syuo /*
4983b9116Syuo  * Copyright (c) 2009, 2010 Yojiro UO <yuo@nui.org>
5fe0fc4c7Syuo  *
6fe0fc4c7Syuo  * Permission to use, copy, modify, and distribute this software for any
7fe0fc4c7Syuo  * purpose with or without fee is hereby granted, provided that the above
8fe0fc4c7Syuo  * copyright notice and this permission notice appear in all copies.
9fe0fc4c7Syuo  *
10fe0fc4c7Syuo  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCAIMS ALL WARRANTIES
11fe0fc4c7Syuo  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12fe0fc4c7Syuo  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13fe0fc4c7Syuo  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14fe0fc4c7Syuo  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15fe0fc4c7Syuo  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16fe0fc4c7Syuo  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17fe0fc4c7Syuo  */
18fe0fc4c7Syuo 
194b1a56afSjsg /* Driver for HID based TEMPer series Temperature(/Humidity) sensors */
20fe0fc4c7Syuo 
21fe0fc4c7Syuo #include <sys/param.h>
22fe0fc4c7Syuo #include <sys/systm.h>
23fe0fc4c7Syuo #include <sys/device.h>
24fe0fc4c7Syuo #include <sys/sensors.h>
25fe0fc4c7Syuo 
26fe0fc4c7Syuo #include <dev/usb/usb.h>
27fe0fc4c7Syuo #include <dev/usb/usbhid.h>
28fe0fc4c7Syuo #include <dev/usb/usbdi.h>
29fe0fc4c7Syuo #include <dev/usb/usbdevs.h>
30fe0fc4c7Syuo #include <dev/usb/uhidev.h>
318a83145eSjcs 
32fe0fc4c7Syuo #ifdef UTHUM_DEBUG
33e249a5e9Ssthen #define DPRINTF(x)	do { printf x; } while (0)
34fe0fc4c7Syuo #else
35e249a5e9Ssthen #define DPRINTF(x)
36fe0fc4c7Syuo #endif
37fe0fc4c7Syuo 
38983b9116Syuo /* Device types */
39983b9116Syuo #define UTHUM_TYPE_TEMPERHUM	0x535a
40c218860eSyuo #define UTHUM_TYPE_TEMPERHUM_2	0x575a /* alternative TEMPerHUM */
41983b9116Syuo #define UTHUM_TYPE_TEMPER1	0x5758 /* TEMPer1 and HID TEMPer */
42983b9116Syuo #define UTHUM_TYPE_TEMPER2	0x5759
43983b9116Syuo #define UTHUM_TYPE_TEMPERNTC	0x575b
44ec12b9dfSmatthieu #define UTHUM_TYPE_TEMPERHUM_3	0x5f5a
45983b9116Syuo #define UTHUM_TYPE_UNKNOWN	0xffff
46fe0fc4c7Syuo 
47983b9116Syuo /* Common */
48983b9116Syuo #define UTHUM_CAL_OFFSET	0x14
49983b9116Syuo #define UTHUM_MAX_SENSORS	2
50983b9116Syuo #define CMD_DEVTYPE		0x52
51983b9116Syuo #define DEVTYPE_EOF		0x53
52983b9116Syuo 
53983b9116Syuo /* query commands */
54983b9116Syuo #define CMD_GETDATA_NTC		0x41 /* TEMPerNTC NTC part */
55983b9116Syuo #define CMD_RESET0		0x43 /* TEMPer, TEMPer[12], TEMPerNTC */
56983b9116Syuo #define CMD_RESET1		0x44 /* TEMPer, TEMPer[12] */
57983b9116Syuo #define CMD_GETDATA		0x48 /* TEMPerHUM */
58983b9116Syuo #define CMD_GETDATA_OUTER	0x53 /* TEMPer, TEMPer[12], TEMPerNTC */
59983b9116Syuo #define CMD_GETDATA_INNER	0x54 /* TEMPer, TEMPer[12], TEMPerNTC */
60983b9116Syuo #define CMD_GETDATA_EOF		0x31
61983b9116Syuo #define CMD_GETDATA_EOF2	0xaa
62983b9116Syuo 
63983b9116Syuo /* temperntc mode */
64983b9116Syuo #define TEMPERNTC_MODE_BASE	0x61 /* 0x61 - 0x68 */
65983b9116Syuo #define TEMPERNTC_MODE_MAX	0x68
66983b9116Syuo #define CMD_TEMPERNTC_MODE_DONE	0x69
67983b9116Syuo #define UTHUM_NTC_MIN_THRESHOLD	0xb300
68983b9116Syuo #define UTHUM_NTC_MAX_THRESHOLD	0xf200
69983b9116Syuo 
70983b9116Syuo /* sensor name */
71983b9116Syuo #define UTHUM_TEMPER_INNER	0
72983b9116Syuo #define UTHUM_TEMPER_OUTER	1
73983b9116Syuo #define UTHUM_TEMPER_NTC	1
74983b9116Syuo #define UTHUM_TEMPERHUM_TEMP	0
75983b9116Syuo #define UTHUM_TEMPERHUM_HUM	1
76983b9116Syuo 
77983b9116Syuo enum uthum_sensor_type {
78983b9116Syuo 	UTHUM_SENSOR_UNKNOWN,
79983b9116Syuo 	UTHUM_SENSOR_SHT1X,
80983b9116Syuo 	UTHUM_SENSOR_DS75,
81983b9116Syuo 	UTHUM_SENSOR_NTC,
82983b9116Syuo 	UTHUM_SENSOR_MAXTYPES,
83983b9116Syuo };
84983b9116Syuo 
85983b9116Syuo static const char * const uthum_sensor_type_s[UTHUM_SENSOR_MAXTYPES] = {
86983b9116Syuo 	"unknown",
87983b9116Syuo 	"sht1x",
88983b9116Syuo 	"ds75/12bit",
89983b9116Syuo 	"NTC"
90983b9116Syuo };
91983b9116Syuo 
92983b9116Syuo static uint8_t cmd_issue[8] =
93fe0fc4c7Syuo 	{ 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00, 0x02, 0x00 };
94983b9116Syuo static uint8_t cmd_query[8] =
95fe0fc4c7Syuo 	{ 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x00, 0x01, 0x00 };
96fe0fc4c7Syuo 
97983b9116Syuo struct uthum_sensor {
98983b9116Syuo 	struct ksensor sensor;
99983b9116Syuo 	int cal_offset;	/* mC or m%RH */
100983b9116Syuo 	int attached;
101983b9116Syuo 	enum uthum_sensor_type dev_type;
102983b9116Syuo 	int cur_state;	/* for TEMPerNTC */
103983b9116Syuo };
104fe0fc4c7Syuo 
105fe0fc4c7Syuo struct uthum_softc {
106fe0fc4c7Syuo 	struct uhidev		 sc_hdev;
107ab0b1be7Smglocker 	struct usbd_device	*sc_udev;
108983b9116Syuo 	int			 sc_device_type;
109983b9116Syuo 	int			 sc_num_sensors;
110fe0fc4c7Syuo 
111fe0fc4c7Syuo 	/* uhidev parameters */
112fe0fc4c7Syuo 	size_t			 sc_flen;	/* feature report length */
113fe0fc4c7Syuo 	size_t			 sc_ilen;	/* input report length */
114fe0fc4c7Syuo 	size_t			 sc_olen;	/* output report length */
115fe0fc4c7Syuo 
116fe0fc4c7Syuo 	/* sensor framework */
117983b9116Syuo 	struct uthum_sensor	 sc_sensor[UTHUM_MAX_SENSORS];
118fe0fc4c7Syuo 	struct ksensordev	 sc_sensordev;
119fe0fc4c7Syuo 	struct sensor_task	*sc_sensortask;
120fe0fc4c7Syuo };
121fe0fc4c7Syuo 
122fe0fc4c7Syuo const struct usb_devno uthum_devs[] = {
123983b9116Syuo 	/* XXX: various TEMPer variants are using same VID/PID */
124fe0fc4c7Syuo 	{ USB_VENDOR_TENX, USB_PRODUCT_TENX_TEMPER},
125fe0fc4c7Syuo };
126fe0fc4c7Syuo #define uthum_lookup(v, p) usb_lookup(uthum_devs, v, p)
127fe0fc4c7Syuo 
128fe0fc4c7Syuo int  uthum_match(struct device *, void *, void *);
129fe0fc4c7Syuo void uthum_attach(struct device *, struct device *, void *);
130fe0fc4c7Syuo int  uthum_detach(struct device *, int);
131fe0fc4c7Syuo 
132983b9116Syuo int  uthum_issue_cmd(struct uthum_softc *, uint8_t, int);
133fe0fc4c7Syuo int  uthum_read_data(struct uthum_softc *, uint8_t, uint8_t *, size_t, int);
134983b9116Syuo int  uthum_check_device_info(struct uthum_softc *);
13530c572b9Smglocker void uthum_reset_device(struct uthum_softc *);
136983b9116Syuo void uthum_setup_sensors(struct uthum_softc *);
137fe0fc4c7Syuo 
138fe0fc4c7Syuo void uthum_intr(struct uhidev *, void *, u_int);
139fe0fc4c7Syuo void uthum_refresh(void *);
140983b9116Syuo void uthum_refresh_temper(struct uthum_softc *, int);
141983b9116Syuo void uthum_refresh_temperhum(struct uthum_softc *);
142983b9116Syuo void uthum_refresh_temperntc(struct uthum_softc *, int);
143983b9116Syuo 
144983b9116Syuo int  uthum_ntc_getdata(struct uthum_softc *, int *);
145983b9116Syuo int  uthum_ntc_tuning(struct uthum_softc *, int, int *);
146983b9116Syuo int64_t uthum_ntc_temp(int64_t, int);
147983b9116Syuo int  uthum_sht1x_temp(uint8_t, uint8_t);
148983b9116Syuo int  uthum_sht1x_rh(uint8_t, uint8_t, int);
149983b9116Syuo int  uthum_ds75_temp(uint8_t, uint8_t);
150983b9116Syuo void uthum_print_sensorinfo(struct uthum_softc *, int);
151fe0fc4c7Syuo 
152fe0fc4c7Syuo struct cfdriver uthum_cd = {
153fe0fc4c7Syuo 	NULL, "uthum", DV_DULL
154fe0fc4c7Syuo };
155fe0fc4c7Syuo 
156fe0fc4c7Syuo const struct cfattach uthum_ca = {
157fe0fc4c7Syuo 	sizeof(struct uthum_softc),
158fe0fc4c7Syuo 	uthum_match,
159fe0fc4c7Syuo 	uthum_attach,
16008e17b09Ssthen 	uthum_detach
161fe0fc4c7Syuo };
162fe0fc4c7Syuo 
163fe0fc4c7Syuo int
uthum_match(struct device * parent,void * match,void * aux)164fe0fc4c7Syuo uthum_match(struct device *parent, void *match, void *aux)
165fe0fc4c7Syuo {
166f964a65cSmpi 	struct uhidev_attach_arg *uha = aux;
167f964a65cSmpi 
168faac88c0Santon 	if (UHIDEV_CLAIM_MULTIPLE_REPORTID(uha))
169f964a65cSmpi 		return (UMATCH_NONE);
170fe0fc4c7Syuo 
1717a15e3bcSderaadt 	if (uthum_lookup(uha->uaa->vendor, uha->uaa->product) == NULL)
172fe0fc4c7Syuo 		return UMATCH_NONE;
173fe0fc4c7Syuo 
174fe0fc4c7Syuo #if 0 /* attach only sensor part of HID as uthum* */
175fe0fc4c7Syuo #define HUG_UNKNOWN_3	0x0003
176fe0fc4c7Syuo 	void *desc;
177fe0fc4c7Syuo 	int size;
178fe0fc4c7Syuo 	uhidev_get_report_desc(uha->parent, &desc, &size);
179fe0fc4c7Syuo 	if (!hid_is_collection(desc, size, uha->reportid,
180fe0fc4c7Syuo 	    HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_UNKNOWN_3)))
181fe0fc4c7Syuo 		return (UMATCH_NONE);
182fe0fc4c7Syuo #undef HUG_UNKNOWN_3
183fe0fc4c7Syuo #endif
184fe0fc4c7Syuo 
185fe0fc4c7Syuo 	return (UMATCH_VENDOR_PRODUCT);
186fe0fc4c7Syuo }
187fe0fc4c7Syuo 
188fe0fc4c7Syuo void
uthum_attach(struct device * parent,struct device * self,void * aux)189fe0fc4c7Syuo uthum_attach(struct device *parent, struct device *self, void *aux)
190fe0fc4c7Syuo {
191fe0fc4c7Syuo 	struct uthum_softc *sc = (struct uthum_softc *)self;
192fe0fc4c7Syuo 	struct usb_attach_arg *uaa = aux;
193fe0fc4c7Syuo 	struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
194ab0b1be7Smglocker 	struct usbd_device *dev = uha->parent->sc_udev;
195983b9116Syuo 	int i, size, repid;
196fe0fc4c7Syuo 	void *desc;
197fe0fc4c7Syuo 
198fe0fc4c7Syuo 	sc->sc_udev = dev;
199fe0fc4c7Syuo 	sc->sc_hdev.sc_intr = uthum_intr;
200fe0fc4c7Syuo 	sc->sc_hdev.sc_parent = uha->parent;
201fe0fc4c7Syuo 	sc->sc_hdev.sc_report_id = uha->reportid;
202fe0fc4c7Syuo 	sc->sc_num_sensors = 0;
203fe0fc4c7Syuo 
204fe0fc4c7Syuo 	uhidev_get_report_desc(uha->parent, &desc, &size);
205fe0fc4c7Syuo 	repid = uha->reportid;
206fe0fc4c7Syuo 	sc->sc_ilen = hid_report_size(desc, size, hid_input, repid);
207fe0fc4c7Syuo 	sc->sc_olen = hid_report_size(desc, size, hid_output, repid);
208fe0fc4c7Syuo 	sc->sc_flen = hid_report_size(desc, size, hid_feature, repid);
209fe0fc4c7Syuo 
210fe0fc4c7Syuo 	printf("\n");
211fe0fc4c7Syuo 
212fe0fc4c7Syuo 	if (sc->sc_flen < 32) {
213fe0fc4c7Syuo 		/* not sensor interface, just attach */
214fe0fc4c7Syuo 		return;
215fe0fc4c7Syuo 	}
216fe0fc4c7Syuo 
217983b9116Syuo 	/* maybe unsupported device */
218983b9116Syuo 	if (uthum_check_device_info(sc) < 0) {
219983b9116Syuo 		DPRINTF(("uthum: unknown device\n"));
220983b9116Syuo 		return;
221983b9116Syuo 	};
222fe0fc4c7Syuo 
223fe0fc4c7Syuo 	/* attach sensor */
22403f789beSyuo 	strlcpy(sc->sc_sensordev.xname, sc->sc_hdev.sc_dev.dv_xname,
225fe0fc4c7Syuo 	    sizeof(sc->sc_sensordev.xname));
226983b9116Syuo 	uthum_setup_sensors(sc);
227fe0fc4c7Syuo 
228983b9116Syuo 	/* attach sensors */
229983b9116Syuo 	for (i = 0; i < UTHUM_MAX_SENSORS; i++) {
230983b9116Syuo 		if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_UNKNOWN)
231983b9116Syuo 			continue;
232983b9116Syuo 		uthum_print_sensorinfo(sc, i);
233d6471548Sderaadt 		sc->sc_sensor[i].sensor.flags |= SENSOR_FINVALID;
234983b9116Syuo 		sensor_attach(&sc->sc_sensordev, &sc->sc_sensor[i].sensor);
235983b9116Syuo 		sc->sc_sensor[i].attached = 1;
236983b9116Syuo 		sc->sc_num_sensors++;
237fe0fc4c7Syuo 	}
238fe0fc4c7Syuo 
239fe0fc4c7Syuo 	if (sc->sc_num_sensors > 0) {
240983b9116Syuo 		/* 0.1Hz */
241983b9116Syuo 		sc->sc_sensortask = sensor_task_register(sc, uthum_refresh, 6);
242fe0fc4c7Syuo 		if (sc->sc_sensortask == NULL) {
243fe0fc4c7Syuo 			printf(", unable to register update task\n");
244fe0fc4c7Syuo 			return;
245fe0fc4c7Syuo 		}
246fe0fc4c7Syuo 		sensordev_install(&sc->sc_sensordev);
247fe0fc4c7Syuo 	}
248fe0fc4c7Syuo 
249fe0fc4c7Syuo 	DPRINTF(("uthum_attach: complete\n"));
250fe0fc4c7Syuo }
251fe0fc4c7Syuo 
252fe0fc4c7Syuo int
uthum_detach(struct device * self,int flags)253fe0fc4c7Syuo uthum_detach(struct device *self, int flags)
254fe0fc4c7Syuo {
255fe0fc4c7Syuo 	struct uthum_softc *sc = (struct uthum_softc *)self;
256fe0fc4c7Syuo 	int i, rv = 0;
257fe0fc4c7Syuo 
258fe0fc4c7Syuo 	if (sc->sc_num_sensors > 0) {
259fe0fc4c7Syuo 		wakeup(&sc->sc_sensortask);
260fe0fc4c7Syuo 		sensordev_deinstall(&sc->sc_sensordev);
261983b9116Syuo 		for (i = 0; i < UTHUM_MAX_SENSORS; i++) {
262983b9116Syuo 			if (sc->sc_sensor[i].attached)
263983b9116Syuo 				sensor_detach(&sc->sc_sensordev,
264983b9116Syuo 					&sc->sc_sensor[i].sensor);
265983b9116Syuo 		}
266fe0fc4c7Syuo 		if (sc->sc_sensortask != NULL)
267fe0fc4c7Syuo 			sensor_task_unregister(sc->sc_sensortask);
268fe0fc4c7Syuo 	}
269fe0fc4c7Syuo 
27030c572b9Smglocker 	uthum_reset_device(sc);
27130c572b9Smglocker 
272fe0fc4c7Syuo 	return (rv);
273fe0fc4c7Syuo }
274fe0fc4c7Syuo 
275fe0fc4c7Syuo void
uthum_intr(struct uhidev * addr,void * ibuf,u_int len)276fe0fc4c7Syuo uthum_intr(struct uhidev *addr, void *ibuf, u_int len)
277fe0fc4c7Syuo {
2784c70c36cSderaadt 	/* do nothing */
279fe0fc4c7Syuo }
280fe0fc4c7Syuo 
281fe0fc4c7Syuo int
uthum_issue_cmd(struct uthum_softc * sc,uint8_t target_cmd,int delay)282983b9116Syuo uthum_issue_cmd(struct uthum_softc *sc, uint8_t target_cmd, int delay)
283fe0fc4c7Syuo {
284983b9116Syuo 	uint8_t cmdbuf[32];
2853f9f024dSmpi 	int i, actlen, olen;
2863f9f024dSmpi 
2873f9f024dSmpi 	olen = MIN(sc->sc_olen, sizeof(cmdbuf));
288fe0fc4c7Syuo 
289fe0fc4c7Syuo 	bzero(cmdbuf, sizeof(cmdbuf));
290983b9116Syuo 	memcpy(cmdbuf, cmd_issue, sizeof(cmd_issue));
291e6a02383Smpi 	actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
2923f9f024dSmpi 	    sc->sc_hdev.sc_report_id, cmdbuf, olen);
2933f9f024dSmpi 	if (actlen != olen)
294fe0fc4c7Syuo 		return EIO;
2957a15e3bcSderaadt 
296fe0fc4c7Syuo 	bzero(cmdbuf, sizeof(cmdbuf));
297fe0fc4c7Syuo 	cmdbuf[0] = target_cmd;
298e6a02383Smpi 	actlen = uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
2993f9f024dSmpi 	    sc->sc_hdev.sc_report_id, cmdbuf, olen);
3003f9f024dSmpi 	if (actlen != olen)
301fe0fc4c7Syuo 		return EIO;
3027a15e3bcSderaadt 
303fe0fc4c7Syuo 	bzero(cmdbuf, sizeof(cmdbuf));
304fe0fc4c7Syuo 	for (i = 0; i < 7; i++) {
305e6a02383Smpi 		actlen = uhidev_set_report(sc->sc_hdev.sc_parent,
3063f9f024dSmpi 		    UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, cmdbuf, olen);
3073f9f024dSmpi 		if (actlen != olen)
308fe0fc4c7Syuo 			return EIO;
309fe0fc4c7Syuo 	}
310983b9116Syuo 
311983b9116Syuo 	/* wait if required */
312983b9116Syuo 	if (delay > 0)
31355cdce91Smpi 		tsleep_nsec(&sc->sc_sensortask, 0, "uthum",
31455cdce91Smpi 		    MSEC_TO_NSEC(delay));
315983b9116Syuo 
316983b9116Syuo 	return 0;
317983b9116Syuo }
318983b9116Syuo 
319983b9116Syuo int
uthum_read_data(struct uthum_softc * sc,uint8_t target_cmd,uint8_t * buf,size_t len,int delay)320983b9116Syuo uthum_read_data(struct uthum_softc *sc, uint8_t target_cmd, uint8_t *buf,
321983b9116Syuo 	size_t len, int delay)
322983b9116Syuo {
323983b9116Syuo 	uint8_t cmdbuf[32], report[256];
3243f9f024dSmpi 	int olen, flen;
325983b9116Syuo 
326983b9116Syuo 	/* if return buffer is null, do nothing */
327983b9116Syuo 	if ((buf == NULL) || len == 0)
328983b9116Syuo 		return 0;
329983b9116Syuo 
330983b9116Syuo 	if (uthum_issue_cmd(sc, target_cmd, 50))
331983b9116Syuo 		return 0;
332983b9116Syuo 
3333f9f024dSmpi 	olen = MIN(sc->sc_olen, sizeof(cmdbuf));
3343f9f024dSmpi 
335983b9116Syuo 	bzero(cmdbuf, sizeof(cmdbuf));
336983b9116Syuo 	memcpy(cmdbuf, cmd_query, sizeof(cmd_query));
337e6a02383Smpi 	if (uhidev_set_report(sc->sc_hdev.sc_parent, UHID_OUTPUT_REPORT,
3383f9f024dSmpi 	    sc->sc_hdev.sc_report_id, cmdbuf, olen) != olen)
339fe0fc4c7Syuo 		return EIO;
340fe0fc4c7Syuo 
341fe0fc4c7Syuo 	/* wait if required */
3427a15e3bcSderaadt 	if (delay > 0)
34355cdce91Smpi 		tsleep_nsec(&sc->sc_sensortask, 0, "uthum",
34455cdce91Smpi 		    MSEC_TO_NSEC(delay));
345fe0fc4c7Syuo 
346fe0fc4c7Syuo 	/* get answer */
3473f9f024dSmpi 	flen = MIN(sc->sc_flen, sizeof(report));
348e6a02383Smpi 	if (uhidev_get_report(sc->sc_hdev.sc_parent, UHID_FEATURE_REPORT,
3493f9f024dSmpi 	    sc->sc_hdev.sc_report_id, report, flen) != flen)
350fe0fc4c7Syuo 		return EIO;
351fe0fc4c7Syuo 	memcpy(buf, report, len);
352fe0fc4c7Syuo 	return 0;
353fe0fc4c7Syuo }
354fe0fc4c7Syuo 
355fe0fc4c7Syuo int
uthum_check_device_info(struct uthum_softc * sc)356983b9116Syuo uthum_check_device_info(struct uthum_softc *sc)
357983b9116Syuo {
358983b9116Syuo 	struct uthum_dev_info {
359983b9116Syuo 		uint16_t dev_type;
360983b9116Syuo 		uint8_t	 cal[2][2];  /* calibration offsets */
361983b9116Syuo 		uint8_t  footer;
362983b9116Syuo 		uint8_t  padding[25];
363983b9116Syuo 	} dinfo;
364983b9116Syuo 	int val, dev_type;
365983b9116Syuo 	int retry = 3;
366983b9116Syuo 
367983b9116Syuo 	/* issue query to device */
368983b9116Syuo 	while (retry) {
369983b9116Syuo 		if (uthum_read_data(sc, CMD_DEVTYPE, (void *)&dinfo,
370983b9116Syuo 		    sizeof(struct uthum_dev_info), 0) != 0) {
371983b9116Syuo 			DPRINTF(("uthum: device information query fail.\n"));
372983b9116Syuo 			retry--;
373983b9116Syuo 			continue;
374983b9116Syuo 		}
375983b9116Syuo 		if (dinfo.footer !=  DEVTYPE_EOF) {
376983b9116Syuo 			/* it will be a bogus entry, retry. */
377983b9116Syuo 			retry--;
378983b9116Syuo 		} else
379983b9116Syuo 			break;
380983b9116Syuo 	}
381983b9116Syuo 
382649fefe4Syuo 	if (retry <= 0)
383983b9116Syuo 		return EIO;
384983b9116Syuo 
385983b9116Syuo 	dev_type = betoh16(dinfo.dev_type);
386ec12b9dfSmatthieu 	/* TEMPerHUM has 3 different device identifiers, unify them */
387ec12b9dfSmatthieu 	if (dev_type == UTHUM_TYPE_TEMPERHUM_2 ||
388ec12b9dfSmatthieu 	    dev_type == UTHUM_TYPE_TEMPERHUM_3)
389c218860eSyuo 		dev_type = UTHUM_TYPE_TEMPERHUM;
390983b9116Syuo 
391983b9116Syuo 	/* check device type and calibration offset*/
392983b9116Syuo 	switch (dev_type) {
393983b9116Syuo 	case UTHUM_TYPE_TEMPER2:
394983b9116Syuo 	case UTHUM_TYPE_TEMPERHUM:
395983b9116Syuo 	case UTHUM_TYPE_TEMPERNTC:
396983b9116Syuo 		val = (dinfo.cal[1][0] - UTHUM_CAL_OFFSET) * 100;
397983b9116Syuo 		val += dinfo.cal[1][1] * 10;
398983b9116Syuo 		sc->sc_sensor[1].cal_offset = val;
399983b9116Syuo 		/* fall down, don't break */
400983b9116Syuo 	case UTHUM_TYPE_TEMPER1:
401983b9116Syuo 		val = (dinfo.cal[0][0] - UTHUM_CAL_OFFSET) * 100;
402983b9116Syuo 		val += dinfo.cal[0][1] * 10;
403983b9116Syuo 		sc->sc_sensor[0].cal_offset = val;
404983b9116Syuo 		sc->sc_device_type = dev_type;
405983b9116Syuo 		break;
406983b9116Syuo 	default:
407983b9116Syuo 		sc->sc_device_type = UTHUM_TYPE_UNKNOWN;
408983b9116Syuo 		printf("uthum: unknown device (devtype = 0x%.2x)\n",
409983b9116Syuo 		    dev_type);
410983b9116Syuo 		return EIO;
411983b9116Syuo 	}
412983b9116Syuo 
413983b9116Syuo 	/* device specific init process */
414983b9116Syuo 	switch (dev_type) {
41530c572b9Smglocker 	case UTHUM_TYPE_TEMPERHUM:
41630c572b9Smglocker 		sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = 0;
41730c572b9Smglocker 		break;
41830c572b9Smglocker 	};
41930c572b9Smglocker 
42030c572b9Smglocker 	uthum_reset_device(sc);
42130c572b9Smglocker 
42230c572b9Smglocker 	return 0;
42330c572b9Smglocker };
42430c572b9Smglocker 
42530c572b9Smglocker void
uthum_reset_device(struct uthum_softc * sc)42630c572b9Smglocker uthum_reset_device(struct uthum_softc *sc)
42730c572b9Smglocker {
42830c572b9Smglocker 	switch (sc->sc_device_type) {
429983b9116Syuo 	case UTHUM_TYPE_TEMPER1:
430983b9116Syuo 	case UTHUM_TYPE_TEMPERNTC:
431983b9116Syuo 		uthum_issue_cmd(sc, CMD_RESET0, 200);
432983b9116Syuo 		break;
433983b9116Syuo 	case UTHUM_TYPE_TEMPER2:
434983b9116Syuo 		uthum_issue_cmd(sc, CMD_RESET0, 200);
435983b9116Syuo 		uthum_issue_cmd(sc, CMD_RESET1, 200);
436983b9116Syuo 		break;
43730c572b9Smglocker 	}
43830c572b9Smglocker }
439983b9116Syuo 
440983b9116Syuo void
uthum_setup_sensors(struct uthum_softc * sc)441983b9116Syuo uthum_setup_sensors(struct uthum_softc *sc)
442983b9116Syuo {
443983b9116Syuo 	int i;
444983b9116Syuo 
445983b9116Syuo 	for (i = 0; i < UTHUM_MAX_SENSORS; i++)
446983b9116Syuo 		sc->sc_sensor[i].dev_type = UTHUM_SENSOR_UNKNOWN;
447983b9116Syuo 
448983b9116Syuo 	switch (sc->sc_device_type) {
449983b9116Syuo 	case UTHUM_TYPE_TEMPER2:	/* 2 temperature sensors */
450983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPER_OUTER].dev_type =
451983b9116Syuo 		    UTHUM_SENSOR_DS75;
452983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.type =
453983b9116Syuo 		    SENSOR_TEMP;
454983b9116Syuo 		strlcpy(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc,
455983b9116Syuo 		    "outer",
456983b9116Syuo 		    sizeof(sc->sc_sensor[UTHUM_TEMPER_OUTER].sensor.desc));
457983b9116Syuo 		/* fall down */
458983b9116Syuo 	case UTHUM_TYPE_TEMPER1:	/* 1 temperature sensor */
459983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type =
460983b9116Syuo 		    UTHUM_SENSOR_DS75;
461983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.type =
462983b9116Syuo 		    SENSOR_TEMP;
463983b9116Syuo 		strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc,
464983b9116Syuo 		    "inner",
465983b9116Syuo 		    sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc));
466983b9116Syuo 		break;
467983b9116Syuo 	case UTHUM_TYPE_TEMPERHUM:
468983b9116Syuo 		/* 1 temperature sensor and 1 humidity sensor */
469983b9116Syuo 		for (i = 0; i < 2; i++)
470983b9116Syuo 			sc->sc_sensor[i].dev_type = UTHUM_SENSOR_SHT1X;
471983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.type = SENSOR_TEMP;
472983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.type =
473983b9116Syuo 		    SENSOR_HUMIDITY;
474983b9116Syuo 		strlcpy(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc,
475983b9116Syuo 		    "RH",
476983b9116Syuo 		    sizeof(sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.desc));
477983b9116Syuo 		break;
478983b9116Syuo 	case UTHUM_TYPE_TEMPERNTC:
479983b9116Syuo 		/* 2 temperature sensors */
480983b9116Syuo 		for (i = 0; i < 2; i++)
481983b9116Syuo 			sc->sc_sensor[i].sensor.type = SENSOR_TEMP;
482983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPER_INNER].dev_type =
483983b9116Syuo 		    UTHUM_SENSOR_DS75;
484983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPER_NTC].dev_type =
485983b9116Syuo 		    UTHUM_SENSOR_NTC;
486983b9116Syuo 		strlcpy(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc,
487983b9116Syuo 		    "inner",
488983b9116Syuo 		    sizeof(sc->sc_sensor[UTHUM_TEMPER_INNER].sensor.desc));
489983b9116Syuo 		strlcpy(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc,
490983b9116Syuo 		    "outer/ntc",
491983b9116Syuo 		    sizeof(sc->sc_sensor[UTHUM_TEMPER_NTC].sensor.desc));
492983b9116Syuo 
493983b9116Syuo 		/* sensor state tuning */
494983b9116Syuo 		for (i = 0; i < 4; i++)
495983b9116Syuo 			uthum_issue_cmd(sc, TEMPERNTC_MODE_BASE, 50);
496983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPER_NTC].cur_state = TEMPERNTC_MODE_BASE;
497983b9116Syuo 		if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, NULL))
498983b9116Syuo 			DPRINTF(("uthum: NTC sensor tuning failed\n"));
499983b9116Syuo 		uthum_issue_cmd(sc, CMD_TEMPERNTC_MODE_DONE, 100);
500983b9116Syuo 		break;
501983b9116Syuo 	default:
502983b9116Syuo 		/* do nothing */
50304aef575Sderaadt 		break;
504983b9116Syuo 	}
505983b9116Syuo }
506983b9116Syuo 
507983b9116Syuo int
uthum_ntc_getdata(struct uthum_softc * sc,int * val)508983b9116Syuo uthum_ntc_getdata(struct uthum_softc *sc, int *val)
509fe0fc4c7Syuo {
510fe0fc4c7Syuo 	uint8_t buf[8];
511fe0fc4c7Syuo 
512983b9116Syuo 	if (val == NULL)
513983b9116Syuo 		return EIO;
514983b9116Syuo 
515983b9116Syuo 	/* get sensor value */
516983b9116Syuo 	if (uthum_read_data(sc, CMD_GETDATA_NTC, buf, sizeof(buf), 10) != 0) {
517983b9116Syuo 		DPRINTF(("uthum: data read fail\n"));
518983b9116Syuo 		return EIO;
519fe0fc4c7Syuo 	}
520fe0fc4c7Syuo 
521983b9116Syuo 	/* check data integrity */
522983b9116Syuo 	if (buf[2] !=  CMD_GETDATA_EOF2) {
523983b9116Syuo 		DPRINTF(("uthum: broken ntc data 0x%.2x 0x%.2x 0x%.2x\n",
524983b9116Syuo 		    buf[0], buf[1], buf[2]));
525983b9116Syuo 		return EIO;
526fe0fc4c7Syuo 	}
527fe0fc4c7Syuo 
528983b9116Syuo 	*val = (buf[0] << 8) + buf[1];
529983b9116Syuo 	return 0;
530983b9116Syuo }
531983b9116Syuo 
532983b9116Syuo int
uthum_ntc_tuning(struct uthum_softc * sc,int sensor,int * val)533983b9116Syuo uthum_ntc_tuning(struct uthum_softc *sc, int sensor, int *val)
534983b9116Syuo {
535983b9116Syuo 	struct uthum_sensor *s;
536983b9116Syuo 	int done, state, ostate, curval;
537983b9116Syuo 	int retry = 3;
538983b9116Syuo 
539983b9116Syuo 	s = &sc->sc_sensor[sensor];
540983b9116Syuo 	state = s->cur_state;
541983b9116Syuo 
542983b9116Syuo 	/* get current sensor value */
543983b9116Syuo 	if (val == NULL) {
544983b9116Syuo 		while (retry) {
545983b9116Syuo 			if (uthum_ntc_getdata(sc, &curval)) {
546983b9116Syuo 				retry--;
547983b9116Syuo 				continue;
548983b9116Syuo 			} else
549983b9116Syuo 				break;
550983b9116Syuo 		}
551649fefe4Syuo 		if (retry <= 0)
552983b9116Syuo 			return EIO;
553983b9116Syuo 	} else {
554983b9116Syuo 		curval = *val;
555983b9116Syuo 	}
556983b9116Syuo 
557983b9116Syuo 	/* no state change is required */
558983b9116Syuo 	if ((curval >= UTHUM_NTC_MIN_THRESHOLD) &&
559983b9116Syuo 	    (curval <= UTHUM_NTC_MAX_THRESHOLD)) {
560983b9116Syuo 		return 0;
561983b9116Syuo 	}
562983b9116Syuo 
563983b9116Syuo 	if (((curval < UTHUM_NTC_MIN_THRESHOLD) &&
564983b9116Syuo 	     (state == TEMPERNTC_MODE_MAX)) ||
565983b9116Syuo 	    ((curval > UTHUM_NTC_MAX_THRESHOLD) &&
566983b9116Syuo 	     (state == TEMPERNTC_MODE_BASE)))
567983b9116Syuo 		return 0;
568983b9116Syuo 
569983b9116Syuo 	DPRINTF(("uthum: ntc tuning start. cur state = 0x%.2x, val = 0x%.4x\n",
570983b9116Syuo 	    state, curval));
571983b9116Syuo 
572983b9116Syuo 	/* tuning loop */
573983b9116Syuo 	ostate = state;
574983b9116Syuo 	done = 0;
575983b9116Syuo 	while (!done) {
576983b9116Syuo 		if (curval < UTHUM_NTC_MIN_THRESHOLD) {
577983b9116Syuo 			if (state == TEMPERNTC_MODE_MAX)
578983b9116Syuo 				done++;
579983b9116Syuo 			else
580983b9116Syuo 				state++;
581983b9116Syuo 		} else if (curval > UTHUM_NTC_MAX_THRESHOLD) {
582983b9116Syuo 			if (state == TEMPERNTC_MODE_BASE)
583983b9116Syuo 				done++;
584983b9116Syuo 			else
585983b9116Syuo 				state--;
586983b9116Syuo 		} else {
587983b9116Syuo 			uthum_ntc_getdata(sc, &curval);
588983b9116Syuo 			if ((curval >= UTHUM_NTC_MIN_THRESHOLD) &&
589983b9116Syuo 			    (curval <= UTHUM_NTC_MAX_THRESHOLD))
590983b9116Syuo 				done++;
591983b9116Syuo 		}
592983b9116Syuo 
593983b9116Syuo 		/* update state */
594983b9116Syuo 		if (state != ostate) {
595983b9116Syuo 			uthum_issue_cmd(sc, state, 50);
596983b9116Syuo 			uthum_issue_cmd(sc, state, 50);
597983b9116Syuo 			uthum_ntc_getdata(sc, &curval);
598983b9116Syuo 		}
599983b9116Syuo 		ostate = state;
600983b9116Syuo 	}
601983b9116Syuo 
602983b9116Syuo 	DPRINTF(("uthum: ntc tuning done. state change: 0x%.2x->0x%.2x\n",
603983b9116Syuo 	    s->cur_state, state));
604983b9116Syuo 	s->cur_state = state;
605983b9116Syuo 	if (val != NULL)
606983b9116Syuo 		*val = curval;
607983b9116Syuo 
608983b9116Syuo 	return 0;
609983b9116Syuo }
610fe0fc4c7Syuo 
611fe0fc4c7Syuo void
uthum_refresh(void * arg)612fe0fc4c7Syuo uthum_refresh(void *arg)
613fe0fc4c7Syuo {
614fe0fc4c7Syuo 	struct uthum_softc *sc = arg;
615983b9116Syuo 	int i;
616983b9116Syuo 
617983b9116Syuo 	switch (sc->sc_device_type) {
618983b9116Syuo 	case UTHUM_TYPE_TEMPER1:
619983b9116Syuo 	case UTHUM_TYPE_TEMPER2:
620983b9116Syuo 	case UTHUM_TYPE_TEMPERNTC:
621983b9116Syuo 		for (i = 0; i < sc->sc_num_sensors; i++) {
622983b9116Syuo 			if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_DS75)
623983b9116Syuo 				uthum_refresh_temper(sc, i);
624983b9116Syuo 			else if (sc->sc_sensor[i].dev_type == UTHUM_SENSOR_NTC)
625983b9116Syuo 				uthum_refresh_temperntc(sc, i);
626983b9116Syuo 		}
627983b9116Syuo 		break;
628983b9116Syuo 	case UTHUM_TYPE_TEMPERHUM:
629983b9116Syuo 		uthum_refresh_temperhum(sc);
630983b9116Syuo 		break;
631983b9116Syuo 	default:
632983b9116Syuo 		break;
633983b9116Syuo 		/* never reach */
634983b9116Syuo 	}
635983b9116Syuo }
636983b9116Syuo 
637983b9116Syuo void
uthum_refresh_temperhum(struct uthum_softc * sc)638983b9116Syuo uthum_refresh_temperhum(struct uthum_softc *sc)
639983b9116Syuo {
640fe0fc4c7Syuo 	uint8_t buf[8];
641fe0fc4c7Syuo 	int temp, rh;
642fe0fc4c7Syuo 
643fe0fc4c7Syuo 	if (uthum_read_data(sc, CMD_GETDATA, buf, sizeof(buf), 1000) != 0) {
644fe0fc4c7Syuo 		DPRINTF(("uthum: data read fail\n"));
645983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags
646983b9116Syuo 		    |= SENSOR_FINVALID;
647983b9116Syuo 		sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags
648983b9116Syuo 		    |= SENSOR_FINVALID;
649fe0fc4c7Syuo 		return;
650fe0fc4c7Syuo 	}
651fe0fc4c7Syuo 
652983b9116Syuo 	temp = uthum_sht1x_temp(buf[0], buf[1]);
653983b9116Syuo 	rh = uthum_sht1x_rh(buf[2], buf[3], temp);
654fe0fc4c7Syuo 
655983b9116Syuo 	/* apply calibration offsets */
656983b9116Syuo 	temp += sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].cal_offset;
657983b9116Syuo 	rh += sc->sc_sensor[UTHUM_TEMPERHUM_HUM].cal_offset;
658983b9116Syuo 
659983b9116Syuo 	sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.value =
660983b9116Syuo 	    (temp * 10000) + 273150000;
661983b9116Syuo 	sc->sc_sensor[UTHUM_TEMPERHUM_TEMP].sensor.flags &= ~SENSOR_FINVALID;
662983b9116Syuo 	sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.value = rh;
663983b9116Syuo 	sc->sc_sensor[UTHUM_TEMPERHUM_HUM].sensor.flags &= ~SENSOR_FINVALID;
664983b9116Syuo }
665983b9116Syuo 
666983b9116Syuo void
uthum_refresh_temper(struct uthum_softc * sc,int sensor)667983b9116Syuo uthum_refresh_temper(struct uthum_softc *sc, int sensor)
668983b9116Syuo {
669983b9116Syuo 	uint8_t buf[8];
670983b9116Syuo 	uint8_t cmd;
671983b9116Syuo 	int temp;
672983b9116Syuo 
673983b9116Syuo 	if (sensor == UTHUM_TEMPER_INNER)
674983b9116Syuo 		cmd = CMD_GETDATA_INNER;
675983b9116Syuo 	else if (sensor == UTHUM_TEMPER_OUTER)
676983b9116Syuo 		cmd = CMD_GETDATA_OUTER;
677983b9116Syuo 	else
678983b9116Syuo 		return;
679983b9116Syuo 
680983b9116Syuo 	/* get sensor value */
681983b9116Syuo 	if (uthum_read_data(sc, cmd, buf, sizeof(buf), 1000) != 0) {
682983b9116Syuo 		DPRINTF(("uthum: data read fail\n"));
683983b9116Syuo 		sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
6849fc88e7cSderaadt 		return;
685fe0fc4c7Syuo 	}
686fe0fc4c7Syuo 
687983b9116Syuo 	/* check integrity */
688983b9116Syuo 	if (buf[2] !=  CMD_GETDATA_EOF) {
689983b9116Syuo 		DPRINTF(("uthum: broken ds75 data: 0x%.2x 0x%.2x 0x%.2x\n",
690983b9116Syuo 		    buf[0], buf[1], buf[2]));
691983b9116Syuo 		sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
692983b9116Syuo 		return;
693983b9116Syuo 	}
694983b9116Syuo 	temp = uthum_ds75_temp(buf[0], buf[1]);
695983b9116Syuo 
696983b9116Syuo 	/* apply calibration offset */
697983b9116Syuo 	temp += sc->sc_sensor[sensor].cal_offset;
698983b9116Syuo 
699983b9116Syuo 	sc->sc_sensor[sensor].sensor.value = (temp * 10000) + 273150000;
700983b9116Syuo 	sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID;
701983b9116Syuo }
702983b9116Syuo 
703983b9116Syuo void
uthum_refresh_temperntc(struct uthum_softc * sc,int sensor)704983b9116Syuo uthum_refresh_temperntc(struct uthum_softc *sc, int sensor)
705983b9116Syuo {
706983b9116Syuo 	int val;
707983b9116Syuo 	int64_t temp;
708983b9116Syuo 
709983b9116Syuo 	/* get sensor data */
710983b9116Syuo 	if (uthum_ntc_getdata(sc, &val)) {
711983b9116Syuo 		DPRINTF(("uthum: ntc data read fail\n"));
712983b9116Syuo 		sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
713983b9116Syuo 		return;
714983b9116Syuo 	}
715983b9116Syuo 
716983b9116Syuo 	/* adjust sensor state */
717983b9116Syuo 	if ((val < UTHUM_NTC_MIN_THRESHOLD) ||
718983b9116Syuo 	    (val > UTHUM_NTC_MAX_THRESHOLD)) {
719983b9116Syuo 		if (uthum_ntc_tuning(sc, UTHUM_TEMPER_NTC, &val)) {
720983b9116Syuo 			DPRINTF(("uthum: NTC sensor tuning failed\n"));
721983b9116Syuo 			sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
722983b9116Syuo 			return;
723983b9116Syuo 		}
724983b9116Syuo 	}
725983b9116Syuo 
726983b9116Syuo 	temp = uthum_ntc_temp(val, sc->sc_sensor[sensor].cur_state);
727983b9116Syuo 	if (temp == 0) {
728983b9116Syuo 		/* XXX: work around. */
729983b9116Syuo 		sc->sc_sensor[sensor].sensor.flags |= SENSOR_FINVALID;
730983b9116Syuo 	} else {
731983b9116Syuo 		/* apply calibration offset */
732983b9116Syuo 		temp += sc->sc_sensor[sensor].cal_offset * 10000;
733983b9116Syuo 		sc->sc_sensor[sensor].sensor.value = temp;
734983b9116Syuo 		sc->sc_sensor[sensor].sensor.flags &= ~SENSOR_FINVALID;
735983b9116Syuo 	}
736fe0fc4c7Syuo }
737fe0fc4c7Syuo 
738fe0fc4c7Syuo /* return C-degree * 100 value */
739fe0fc4c7Syuo int
uthum_ds75_temp(uint8_t msb,uint8_t lsb)740983b9116Syuo uthum_ds75_temp(uint8_t msb, uint8_t lsb)
741fe0fc4c7Syuo {
742ce1ffb1fSmglocker 	int val;
743ce1ffb1fSmglocker 
744983b9116Syuo 	/* DS75: 12bit precision mode : 0.0625 degrees Celsius ticks */
745ce1ffb1fSmglocker 
746ce1ffb1fSmglocker 	val = (msb << 8) | lsb;
747ce1ffb1fSmglocker 	if (val >= 32768)
748ce1ffb1fSmglocker 		val = val - 65536;
749ce1ffb1fSmglocker 	val = (val * 100) >> 8;
750ce1ffb1fSmglocker 
751ce1ffb1fSmglocker 	return val;
752983b9116Syuo }
753983b9116Syuo 
754983b9116Syuo /* return C-degree * 100 value */
755983b9116Syuo int
uthum_sht1x_temp(uint8_t msb,uint8_t lsb)756983b9116Syuo uthum_sht1x_temp(uint8_t msb, uint8_t lsb)
757983b9116Syuo {
758e253c8f7Smpi 	int nticks;
759983b9116Syuo 
760983b9116Syuo 	/* sensor device VDD-bias value table
761983b9116Syuo 	 * ----------------------------------------------
762983b9116Syuo 	 * VDD	2.5V	3.0V	3.5V	4.0V	5.0V
763983b9116Syuo 	 * bias	-3940	-3960	-3970	-3980	-4010
764983b9116Syuo 	 * ----------------------------------------------
765983b9116Syuo 	 *
7666026027fSyuo 	 * as the VDD of the SHT10 on my TEMPerHUM is 3.43V +/- 0.05V,
767983b9116Syuo 	 * bias -3970 will be best for that device.
7686026027fSyuo 	 */
769983b9116Syuo 
770e253c8f7Smpi 	nticks = (msb * 256 + lsb) & 0x3fff;
771e253c8f7Smpi 	return (nticks - 3970);
772fe0fc4c7Syuo }
773fe0fc4c7Syuo 
774fe0fc4c7Syuo /* return %RH * 1000 */
775fe0fc4c7Syuo int
uthum_sht1x_rh(uint8_t msb,uint8_t lsb,int temp)776983b9116Syuo uthum_sht1x_rh(uint8_t msb, uint8_t lsb, int temp)
777fe0fc4c7Syuo {
778e253c8f7Smpi 	int nticks, rh_l;
779fe0fc4c7Syuo 
780e253c8f7Smpi 	nticks = (msb * 256 + lsb) & 0x0fff;
781e253c8f7Smpi 	rh_l = (-40000 + 405 * nticks) - ((7 * nticks * nticks) / 250);
782983b9116Syuo 
783e253c8f7Smpi 	return ((temp - 2500) * (1 + (nticks >> 7)) + rh_l) / 10;
784983b9116Syuo }
785983b9116Syuo 
786983b9116Syuo /* return muK */
787983b9116Syuo int64_t
uthum_ntc_temp(int64_t val,int state)788983b9116Syuo uthum_ntc_temp(int64_t val, int state)
789983b9116Syuo {
790983b9116Syuo 	int64_t temp = 0;
791983b9116Syuo 
792983b9116Syuo 	switch (state) {
793983b9116Syuo 	case TEMPERNTC_MODE_BASE:	/* 0x61 */
794983b9116Syuo 	case TEMPERNTC_MODE_BASE+1:	/* 0x62 */
795983b9116Syuo 	case TEMPERNTC_MODE_BASE+2:	/* 0x63 */
796983b9116Syuo 	case TEMPERNTC_MODE_BASE+3:	/* 0x64 */
797983b9116Syuo 		/* XXX, no data */
798983b9116Syuo 		temp = -273150000;
799983b9116Syuo 		break;
800983b9116Syuo 	case TEMPERNTC_MODE_BASE+4:	/* 0x65 */
801983b9116Syuo 		temp = ((val * val * 2977) / 100000) - (val * 4300) + 152450000;
802983b9116Syuo 		break;
803983b9116Syuo 	case TEMPERNTC_MODE_BASE+5:	/* 0x66 */
804983b9116Syuo 		temp = ((val * val * 3887) / 100000) - (val * 5300) + 197590000;
805983b9116Syuo 		break;
806983b9116Syuo 	case TEMPERNTC_MODE_BASE+6:	/* 0x67 */
807983b9116Syuo 		temp = ((val * val * 3495) / 100000) - (val * 5000) + 210590000;
808983b9116Syuo 		break;
809983b9116Syuo 	case TEMPERNTC_MODE_BASE+7:	/* 0x68 */
810983b9116Syuo 		if (val < UTHUM_NTC_MIN_THRESHOLD)
811983b9116Syuo 			temp = (val * -1700) + 149630000;
812983b9116Syuo 		else
813983b9116Syuo 			temp = ((val * val * 3257) / 100000) - (val * 4900) +
814983b9116Syuo 			    230470000;
815983b9116Syuo 		break;
816983b9116Syuo 	default:
817983b9116Syuo 		DPRINTF(("NTC state error, unknown state 0x%.2x\n", state));
81804aef575Sderaadt 		break;
81904aef575Sderaadt 	}
820983b9116Syuo 
821983b9116Syuo 	/* convert muC->muK value */
822983b9116Syuo 	return temp + 273150000;
823983b9116Syuo }
824983b9116Syuo 
825983b9116Syuo void
uthum_print_sensorinfo(struct uthum_softc * sc,int num)826983b9116Syuo uthum_print_sensorinfo(struct uthum_softc *sc, int num)
827983b9116Syuo {
828983b9116Syuo 	struct uthum_sensor *s;
829983b9116Syuo 	s = &sc->sc_sensor[num];
830983b9116Syuo 
83104aef575Sderaadt 	printf("%s: ", sc->sc_hdev.sc_dev.dv_xname);
832983b9116Syuo 	switch (s->sensor.type) {
833983b9116Syuo 	case SENSOR_TEMP:
83404aef575Sderaadt 		printf("type %s (temperature)",
835983b9116Syuo 		    uthum_sensor_type_s[s->dev_type]);
83604aef575Sderaadt 		if (s->cal_offset)
837b9e27d9dSsasano 			printf(", calibration offset %c%d.%d degC",
838b9e27d9dSsasano 			    (s->cal_offset < 0) ? '-' : '+',
839b9e27d9dSsasano 			    abs(s->cal_offset / 100),
840b9e27d9dSsasano 			    abs(s->cal_offset % 100));
841983b9116Syuo 		break;
842983b9116Syuo 	case SENSOR_HUMIDITY:
84304aef575Sderaadt 		printf("type %s (humidity)",
844983b9116Syuo 		    uthum_sensor_type_s[s->dev_type]);
84504aef575Sderaadt 		if (s->cal_offset)
846b9e27d9dSsasano 			printf("calibration offset %c%d.%d %%RH",
847b9e27d9dSsasano 			    (s->cal_offset < 0) ? '-' : '+',
848b9e27d9dSsasano 			    abs(s->cal_offset / 100),
849b9e27d9dSsasano 			    abs(s->cal_offset % 100));
850983b9116Syuo 		break;
851983b9116Syuo 	default:
852983b9116Syuo 		printf("unknown");
853983b9116Syuo 	}
854983b9116Syuo 	printf("\n");
855fe0fc4c7Syuo }
856