xref: /freebsd/sys/dev/gpio/gpioths.c (revision 662e30fca3530b5dba603d97aeedcc3fbc81ee97)
1*662e30fcSMichael Zhilin /*-
2*662e30fcSMichael Zhilin  * Copyright (c) 2016 Michael Zhilin <mizhka@freebsd.org>
3*662e30fcSMichael Zhilin  * All rights reserved.
4*662e30fcSMichael Zhilin  *
5*662e30fcSMichael Zhilin  * Redistribution and use in source and binary forms, with or without
6*662e30fcSMichael Zhilin  * modification, are permitted provided that the following conditions
7*662e30fcSMichael Zhilin  * are met:
8*662e30fcSMichael Zhilin  * 1. Redistributions of source code must retain the above copyright
9*662e30fcSMichael Zhilin  *    notice, this list of conditions and the following disclaimer.
10*662e30fcSMichael Zhilin  * 2. Redistributions in binary form must reproduce the above copyright
11*662e30fcSMichael Zhilin  *    notice, this list of conditions and the following disclaimer in the
12*662e30fcSMichael Zhilin  *    documentation and/or other materials provided with the distribution.
13*662e30fcSMichael Zhilin  *
14*662e30fcSMichael Zhilin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*662e30fcSMichael Zhilin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*662e30fcSMichael Zhilin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*662e30fcSMichael Zhilin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*662e30fcSMichael Zhilin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*662e30fcSMichael Zhilin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*662e30fcSMichael Zhilin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*662e30fcSMichael Zhilin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*662e30fcSMichael Zhilin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*662e30fcSMichael Zhilin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*662e30fcSMichael Zhilin  * SUCH DAMAGE.
25*662e30fcSMichael Zhilin  */
26*662e30fcSMichael Zhilin 
27*662e30fcSMichael Zhilin #include <sys/cdefs.h>
28*662e30fcSMichael Zhilin __FBSDID("$FreeBSD$");
29*662e30fcSMichael Zhilin 
30*662e30fcSMichael Zhilin #include <sys/param.h>
31*662e30fcSMichael Zhilin #include <sys/kernel.h>
32*662e30fcSMichael Zhilin #include <sys/bus.h>
33*662e30fcSMichael Zhilin #include <sys/module.h>
34*662e30fcSMichael Zhilin #include <sys/errno.h>
35*662e30fcSMichael Zhilin #include <sys/systm.h>
36*662e30fcSMichael Zhilin #include <sys/sysctl.h>
37*662e30fcSMichael Zhilin 
38*662e30fcSMichael Zhilin #include <machine/bus.h>
39*662e30fcSMichael Zhilin #include <sys/rman.h>
40*662e30fcSMichael Zhilin #include <sys/gpio.h>
41*662e30fcSMichael Zhilin #include <machine/resource.h>
42*662e30fcSMichael Zhilin 
43*662e30fcSMichael Zhilin #include "gpiobus_if.h"
44*662e30fcSMichael Zhilin 
45*662e30fcSMichael Zhilin /*
46*662e30fcSMichael Zhilin  * GPIOTHS - Temp/Humidity sensor over GPIO, e.g. DHT11/DHT22
47*662e30fcSMichael Zhilin  * This is driver for Temperature & Humidity sensor which provides digital
48*662e30fcSMichael Zhilin  * output over single-wire protocol from embedded 8-bit microcontroller.
49*662e30fcSMichael Zhilin  *
50*662e30fcSMichael Zhilin  * Temp/Humidity sensor can't be discovered automatically, please specify hints
51*662e30fcSMichael Zhilin  * as part of loader or kernel configuration:
52*662e30fcSMichael Zhilin  *	hint.gpioths.0.at="gpiobus0"
53*662e30fcSMichael Zhilin  *	hint.gpioths.0.pins=<PIN>
54*662e30fcSMichael Zhilin  */
55*662e30fcSMichael Zhilin 
56*662e30fcSMichael Zhilin #define	GPIOTHS_POLLTIME	5	/* in seconds */
57*662e30fcSMichael Zhilin 
58*662e30fcSMichael Zhilin #define	GPIOTHS_DHT_STARTCYCLE	20000	/* 20ms = 20000us */
59*662e30fcSMichael Zhilin #define	GPIOTHS_DHT_TIMEOUT	1000	/* 1ms = 1000us */
60*662e30fcSMichael Zhilin #define	GPIOTHS_DHT_CYCLES	41
61*662e30fcSMichael Zhilin #define	GPIOTHS_DHT_ONEBYTEMASK	0xFF
62*662e30fcSMichael Zhilin #define	GPIOTHS_DHT_TEMP_SHIFT	8
63*662e30fcSMichael Zhilin #define	GPIOTHS_DHT_HUM_SHIFT	24
64*662e30fcSMichael Zhilin 
65*662e30fcSMichael Zhilin struct gpioths_softc {
66*662e30fcSMichael Zhilin 	device_t		 dev;
67*662e30fcSMichael Zhilin 	int			 temp;
68*662e30fcSMichael Zhilin 	int			 hum;
69*662e30fcSMichael Zhilin 	int			 fails;
70*662e30fcSMichael Zhilin 	struct sysctl_oid	*temp_oid;
71*662e30fcSMichael Zhilin 	struct sysctl_oid	*hum_oid;
72*662e30fcSMichael Zhilin 	struct sysctl_oid	*fails_oid;
73*662e30fcSMichael Zhilin 	struct callout		 callout;
74*662e30fcSMichael Zhilin };
75*662e30fcSMichael Zhilin 
76*662e30fcSMichael Zhilin static devclass_t gpioths_devclass;
77*662e30fcSMichael Zhilin 
78*662e30fcSMichael Zhilin /* Prototypes */
79*662e30fcSMichael Zhilin static int		gpioths_probe(device_t dev);
80*662e30fcSMichael Zhilin static int		gpioths_attach(device_t dev);
81*662e30fcSMichael Zhilin static int		gpioths_detach(device_t dev);
82*662e30fcSMichael Zhilin static void		gpioths_poll(void *arg);
83*662e30fcSMichael Zhilin static int		gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS);
84*662e30fcSMichael Zhilin static int		gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS);
85*662e30fcSMichael Zhilin static int		gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS);
86*662e30fcSMichael Zhilin 
87*662e30fcSMichael Zhilin /* DHT-specific methods */
88*662e30fcSMichael Zhilin static int		gpioths_dht_initread(device_t bus, device_t dev);
89*662e30fcSMichael Zhilin static int		gpioths_dht_readbytes(device_t bus, device_t dev);
90*662e30fcSMichael Zhilin static int		gpioths_dht_timeuntil(device_t bus, device_t dev,
91*662e30fcSMichael Zhilin 			    uint32_t lev, uint32_t *time);
92*662e30fcSMichael Zhilin 
93*662e30fcSMichael Zhilin /* Implementation */
94*662e30fcSMichael Zhilin static int
95*662e30fcSMichael Zhilin gpioths_probe(device_t dev)
96*662e30fcSMichael Zhilin {
97*662e30fcSMichael Zhilin 	device_set_desc(dev, "Temperature and Humidity Sensor over GPIO");
98*662e30fcSMichael Zhilin 	return (0);
99*662e30fcSMichael Zhilin }
100*662e30fcSMichael Zhilin 
101*662e30fcSMichael Zhilin static int
102*662e30fcSMichael Zhilin gpioths_dht_timeuntil(device_t bus, device_t dev, uint32_t lev, uint32_t *time)
103*662e30fcSMichael Zhilin {
104*662e30fcSMichael Zhilin 	uint32_t	cur_level;
105*662e30fcSMichael Zhilin 	int		i;
106*662e30fcSMichael Zhilin 
107*662e30fcSMichael Zhilin 	for (i = 0; i < GPIOTHS_DHT_TIMEOUT; i++) {
108*662e30fcSMichael Zhilin 		GPIOBUS_PIN_GET(bus, dev, 0, &cur_level);
109*662e30fcSMichael Zhilin 		if (cur_level == lev) {
110*662e30fcSMichael Zhilin 			if (time != NULL)
111*662e30fcSMichael Zhilin 				*time = i;
112*662e30fcSMichael Zhilin 			return (0);
113*662e30fcSMichael Zhilin 		}
114*662e30fcSMichael Zhilin 		DELAY(1);
115*662e30fcSMichael Zhilin 	}
116*662e30fcSMichael Zhilin 
117*662e30fcSMichael Zhilin 	/* Timeout */
118*662e30fcSMichael Zhilin 	return (ETIMEDOUT);
119*662e30fcSMichael Zhilin }
120*662e30fcSMichael Zhilin 
121*662e30fcSMichael Zhilin static int
122*662e30fcSMichael Zhilin gpioths_dht_initread(device_t bus, device_t dev)
123*662e30fcSMichael Zhilin {
124*662e30fcSMichael Zhilin 	int	err;
125*662e30fcSMichael Zhilin 
126*662e30fcSMichael Zhilin 	err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT);
127*662e30fcSMichael Zhilin 	if (err != 0) {
128*662e30fcSMichael Zhilin 		device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, OUT) = %d\n", err);
129*662e30fcSMichael Zhilin 		return (err);
130*662e30fcSMichael Zhilin 	}
131*662e30fcSMichael Zhilin 	DELAY(1);
132*662e30fcSMichael Zhilin 
133*662e30fcSMichael Zhilin 	err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_LOW);
134*662e30fcSMichael Zhilin 	if (err != 0) {
135*662e30fcSMichael Zhilin 		device_printf(dev, "err(GPIOBUS_PIN_SET, LOW) = %d\n", err);
136*662e30fcSMichael Zhilin 		return (err);
137*662e30fcSMichael Zhilin 	}
138*662e30fcSMichael Zhilin 
139*662e30fcSMichael Zhilin 	/*
140*662e30fcSMichael Zhilin 	 * According to specifications we need to wait no more than 18ms
141*662e30fcSMichael Zhilin 	 * to start data transfer
142*662e30fcSMichael Zhilin 	 */
143*662e30fcSMichael Zhilin 	DELAY(GPIOTHS_DHT_STARTCYCLE);
144*662e30fcSMichael Zhilin 	err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_HIGH);
145*662e30fcSMichael Zhilin 	if (err != 0) {
146*662e30fcSMichael Zhilin 		device_printf(dev, "err(GPIOBUS_PIN_SET, HIGH) = %d\n", err);
147*662e30fcSMichael Zhilin 		return (err);
148*662e30fcSMichael Zhilin 	}
149*662e30fcSMichael Zhilin 
150*662e30fcSMichael Zhilin 	DELAY(1);
151*662e30fcSMichael Zhilin 	err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_INPUT) ;
152*662e30fcSMichael Zhilin 	if (err != 0) {
153*662e30fcSMichael Zhilin 		device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, IN) = %d\n", err);
154*662e30fcSMichael Zhilin 		return (err);
155*662e30fcSMichael Zhilin 	}
156*662e30fcSMichael Zhilin 
157*662e30fcSMichael Zhilin 	DELAY(1);
158*662e30fcSMichael Zhilin 	return (0);
159*662e30fcSMichael Zhilin }
160*662e30fcSMichael Zhilin 
161*662e30fcSMichael Zhilin static int
162*662e30fcSMichael Zhilin gpioths_dht_readbytes(device_t bus, device_t dev)
163*662e30fcSMichael Zhilin {
164*662e30fcSMichael Zhilin 	struct gpioths_softc	*sc;
165*662e30fcSMichael Zhilin 	uint32_t		 calibrations[GPIOTHS_DHT_CYCLES];
166*662e30fcSMichael Zhilin 	uint32_t		 intervals[GPIOTHS_DHT_CYCLES];
167*662e30fcSMichael Zhilin 	uint32_t		 err, avglen, value;
168*662e30fcSMichael Zhilin 	uint8_t			 crc, calc;
169*662e30fcSMichael Zhilin 	int			 i, offset, size;
170*662e30fcSMichael Zhilin 
171*662e30fcSMichael Zhilin 	sc = device_get_softc(dev);
172*662e30fcSMichael Zhilin 
173*662e30fcSMichael Zhilin 	err = gpioths_dht_initread(bus,dev);
174*662e30fcSMichael Zhilin 	if (err) {
175*662e30fcSMichael Zhilin 		device_printf(dev, "gpioths_dht_initread error = %d\n", err);
176*662e30fcSMichael Zhilin 		goto error;
177*662e30fcSMichael Zhilin 	}
178*662e30fcSMichael Zhilin 
179*662e30fcSMichael Zhilin 	err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW, NULL);
180*662e30fcSMichael Zhilin 	if (err) {
181*662e30fcSMichael Zhilin 		device_printf(dev, "err(START) = %d\n", err);
182*662e30fcSMichael Zhilin 		goto error;
183*662e30fcSMichael Zhilin 	}
184*662e30fcSMichael Zhilin 
185*662e30fcSMichael Zhilin 	/* reading - 41 cycles */
186*662e30fcSMichael Zhilin 	for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) {
187*662e30fcSMichael Zhilin 		err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_HIGH,
188*662e30fcSMichael Zhilin 		          &calibrations[i]);
189*662e30fcSMichael Zhilin 		if (err) {
190*662e30fcSMichael Zhilin 			device_printf(dev, "err(CAL, %d) = %d\n", i, err);
191*662e30fcSMichael Zhilin 			goto error;
192*662e30fcSMichael Zhilin 		}
193*662e30fcSMichael Zhilin 		err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW,
194*662e30fcSMichael Zhilin 			  &intervals[i]);
195*662e30fcSMichael Zhilin 		if (err) {
196*662e30fcSMichael Zhilin 			device_printf(dev, "err(INTERVAL, %d) = %d\n", i, err);
197*662e30fcSMichael Zhilin 			goto error;
198*662e30fcSMichael Zhilin 		}
199*662e30fcSMichael Zhilin 	}
200*662e30fcSMichael Zhilin 
201*662e30fcSMichael Zhilin 	err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT);
202*662e30fcSMichael Zhilin 	if (err != 0) {
203*662e30fcSMichael Zhilin 		device_printf(dev, "err(FINAL_SETFLAGS, OUT) = %d\n", err);
204*662e30fcSMichael Zhilin 		goto error;
205*662e30fcSMichael Zhilin 	}
206*662e30fcSMichael Zhilin 	DELAY(1);
207*662e30fcSMichael Zhilin 
208*662e30fcSMichael Zhilin 	/* Calculate average data calibration cycle length */
209*662e30fcSMichael Zhilin 	avglen = 0;
210*662e30fcSMichael Zhilin 	for (i = 1; i < GPIOTHS_DHT_CYCLES; i++)
211*662e30fcSMichael Zhilin 		avglen += calibrations[i];
212*662e30fcSMichael Zhilin 
213*662e30fcSMichael Zhilin 	avglen = avglen / (GPIOTHS_DHT_CYCLES - 1);
214*662e30fcSMichael Zhilin 
215*662e30fcSMichael Zhilin 	/* Calculate data */
216*662e30fcSMichael Zhilin 	value = 0;
217*662e30fcSMichael Zhilin 	offset = 1;
218*662e30fcSMichael Zhilin 	size = sizeof(value) * 8;
219*662e30fcSMichael Zhilin 	for (i = offset; i < size + offset; i++) {
220*662e30fcSMichael Zhilin 		value <<= 1;
221*662e30fcSMichael Zhilin 		if (intervals[i] > avglen)
222*662e30fcSMichael Zhilin 			value += 1;
223*662e30fcSMichael Zhilin 	}
224*662e30fcSMichael Zhilin 
225*662e30fcSMichael Zhilin 	/* Calculate CRC */
226*662e30fcSMichael Zhilin 	crc = 0;
227*662e30fcSMichael Zhilin 	offset = sizeof(value) * 8 + 1;
228*662e30fcSMichael Zhilin 	size = sizeof(crc) * 8;
229*662e30fcSMichael Zhilin 	for (i = offset;  i < size + offset; i++) {
230*662e30fcSMichael Zhilin 		crc <<= 1;
231*662e30fcSMichael Zhilin 		if (intervals[i] > avglen)
232*662e30fcSMichael Zhilin 			crc += 1;
233*662e30fcSMichael Zhilin 	}
234*662e30fcSMichael Zhilin 
235*662e30fcSMichael Zhilin 	calc = 0;
236*662e30fcSMichael Zhilin 	for (i = 0; i < sizeof(value); i++)
237*662e30fcSMichael Zhilin 		calc += (value >> (8*i)) & GPIOTHS_DHT_ONEBYTEMASK;
238*662e30fcSMichael Zhilin 
239*662e30fcSMichael Zhilin #ifdef GPIOTHS_DEBUG
240*662e30fcSMichael Zhilin 	/* Debug bits */
241*662e30fcSMichael Zhilin 	for (i = 0; i < GPIOTHS_DHT_CYCLES; i++)
242*662e30fcSMichael Zhilin 		device_printf(dev, "%d: %d %d\n", i, calibrations[i],
243*662e30fcSMichael Zhilin 		    intervals[i]);
244*662e30fcSMichael Zhilin 
245*662e30fcSMichael Zhilin 	device_printf(dev, "len=%d, data=%x, crc=%x/%x\n", avglen, value, crc,
246*662e30fcSMichael Zhilin 	    calc);
247*662e30fcSMichael Zhilin #endif /* GPIOTHS_DEBUG */
248*662e30fcSMichael Zhilin 
249*662e30fcSMichael Zhilin 	/* CRC check */
250*662e30fcSMichael Zhilin 	if (calc != crc) {
251*662e30fcSMichael Zhilin 		err = -1;
252*662e30fcSMichael Zhilin 		goto error;
253*662e30fcSMichael Zhilin 	}
254*662e30fcSMichael Zhilin 
255*662e30fcSMichael Zhilin 	sc->fails = 0;
256*662e30fcSMichael Zhilin 	sc->temp = (value >> GPIOTHS_DHT_TEMP_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
257*662e30fcSMichael Zhilin 	sc->hum = (value >> GPIOTHS_DHT_HUM_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK;
258*662e30fcSMichael Zhilin 
259*662e30fcSMichael Zhilin #ifdef GPIOTHS_DEBUG
260*662e30fcSMichael Zhilin 	/* Debug bits */
261*662e30fcSMichael Zhilin 	device_printf(dev, "fails=%d, temp=%d, hum=%d\n", sc->fails,
262*662e30fcSMichael Zhilin 	    sc->temp, sc->hum);
263*662e30fcSMichael Zhilin #endif /* GPIOTHS_DEBUG */
264*662e30fcSMichael Zhilin 
265*662e30fcSMichael Zhilin 	return (0);
266*662e30fcSMichael Zhilin error:
267*662e30fcSMichael Zhilin 	sc->fails++;
268*662e30fcSMichael Zhilin 	return (err);
269*662e30fcSMichael Zhilin }
270*662e30fcSMichael Zhilin 
271*662e30fcSMichael Zhilin static void
272*662e30fcSMichael Zhilin gpioths_poll(void *arg)
273*662e30fcSMichael Zhilin {
274*662e30fcSMichael Zhilin 	struct gpioths_softc	*sc;
275*662e30fcSMichael Zhilin 	device_t		 dev;
276*662e30fcSMichael Zhilin 
277*662e30fcSMichael Zhilin 	dev = (device_t)arg;
278*662e30fcSMichael Zhilin 	sc = device_get_softc(dev);
279*662e30fcSMichael Zhilin 
280*662e30fcSMichael Zhilin 	gpioths_dht_readbytes(device_get_parent(dev), dev);
281*662e30fcSMichael Zhilin 	callout_schedule(&sc->callout, GPIOTHS_POLLTIME * hz);
282*662e30fcSMichael Zhilin }
283*662e30fcSMichael Zhilin 
284*662e30fcSMichael Zhilin static int
285*662e30fcSMichael Zhilin gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS)
286*662e30fcSMichael Zhilin {
287*662e30fcSMichael Zhilin 	struct gpioths_softc	*sc;
288*662e30fcSMichael Zhilin 	int			 value;
289*662e30fcSMichael Zhilin 
290*662e30fcSMichael Zhilin 	sc = (struct gpioths_softc*)arg1;
291*662e30fcSMichael Zhilin 	value = sc->temp;
292*662e30fcSMichael Zhilin 
293*662e30fcSMichael Zhilin 	return (sysctl_handle_int(oidp, &value, 0, req));
294*662e30fcSMichael Zhilin }
295*662e30fcSMichael Zhilin 
296*662e30fcSMichael Zhilin static int
297*662e30fcSMichael Zhilin gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS)
298*662e30fcSMichael Zhilin {
299*662e30fcSMichael Zhilin 	struct gpioths_softc	*sc;
300*662e30fcSMichael Zhilin 	int			 value;
301*662e30fcSMichael Zhilin 
302*662e30fcSMichael Zhilin 	sc = (struct gpioths_softc*)arg1;
303*662e30fcSMichael Zhilin 	value = sc->hum;
304*662e30fcSMichael Zhilin 
305*662e30fcSMichael Zhilin 	return (sysctl_handle_int(oidp, &value, 0, req));
306*662e30fcSMichael Zhilin }
307*662e30fcSMichael Zhilin 
308*662e30fcSMichael Zhilin 
309*662e30fcSMichael Zhilin static int
310*662e30fcSMichael Zhilin gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS)
311*662e30fcSMichael Zhilin {
312*662e30fcSMichael Zhilin 	struct gpioths_softc	*sc;
313*662e30fcSMichael Zhilin 	int			 value;
314*662e30fcSMichael Zhilin 
315*662e30fcSMichael Zhilin 	sc = (struct gpioths_softc*)arg1;
316*662e30fcSMichael Zhilin 	value = sc->fails;
317*662e30fcSMichael Zhilin 
318*662e30fcSMichael Zhilin 	return (sysctl_handle_int(oidp, &value, 0, req));
319*662e30fcSMichael Zhilin }
320*662e30fcSMichael Zhilin 
321*662e30fcSMichael Zhilin static int
322*662e30fcSMichael Zhilin gpioths_attach(device_t dev)
323*662e30fcSMichael Zhilin {
324*662e30fcSMichael Zhilin 	struct gpioths_softc	*sc;
325*662e30fcSMichael Zhilin 	struct sysctl_ctx_list	*ctx;
326*662e30fcSMichael Zhilin 	struct sysctl_oid	*tree;
327*662e30fcSMichael Zhilin 
328*662e30fcSMichael Zhilin 	sc = device_get_softc(dev);
329*662e30fcSMichael Zhilin 	ctx = device_get_sysctl_ctx(dev);
330*662e30fcSMichael Zhilin 	tree = device_get_sysctl_tree(dev);
331*662e30fcSMichael Zhilin 
332*662e30fcSMichael Zhilin 	sc->dev = dev;
333*662e30fcSMichael Zhilin 
334*662e30fcSMichael Zhilin 	callout_init(&sc->callout, 1);
335*662e30fcSMichael Zhilin 	callout_reset(&sc->callout, GPIOTHS_POLLTIME * hz, gpioths_poll, dev);
336*662e30fcSMichael Zhilin 
337*662e30fcSMichael Zhilin 	sc->temp_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
338*662e30fcSMichael Zhilin 	    "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
339*662e30fcSMichael Zhilin 	    gpioths_temp_sysctl, "I", "temperature(C)");
340*662e30fcSMichael Zhilin 
341*662e30fcSMichael Zhilin 	sc->hum_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
342*662e30fcSMichael Zhilin 	    "humidity", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
343*662e30fcSMichael Zhilin 	    gpioths_hum_sysctl, "I", "humidity(%)");
344*662e30fcSMichael Zhilin 
345*662e30fcSMichael Zhilin 	sc->fails_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
346*662e30fcSMichael Zhilin 	    "fails", CTLTYPE_INT | CTLFLAG_RD, sc, 0,
347*662e30fcSMichael Zhilin 	    gpioths_fails_sysctl, "I", "fails since last successful read");
348*662e30fcSMichael Zhilin 
349*662e30fcSMichael Zhilin 	return (0);
350*662e30fcSMichael Zhilin }
351*662e30fcSMichael Zhilin 
352*662e30fcSMichael Zhilin static int
353*662e30fcSMichael Zhilin gpioths_detach(device_t dev)
354*662e30fcSMichael Zhilin {
355*662e30fcSMichael Zhilin 
356*662e30fcSMichael Zhilin 	return (0);
357*662e30fcSMichael Zhilin }
358*662e30fcSMichael Zhilin 
359*662e30fcSMichael Zhilin /* DDB bits */
360*662e30fcSMichael Zhilin #include "opt_ddb.h"
361*662e30fcSMichael Zhilin #ifdef DDB
362*662e30fcSMichael Zhilin #include <ddb/ddb.h>
363*662e30fcSMichael Zhilin #include <ddb/db_lex.h>
364*662e30fcSMichael Zhilin #include <sys/cons.h>
365*662e30fcSMichael Zhilin 
366*662e30fcSMichael Zhilin static struct command_table db_gpioths_table = LIST_HEAD_INITIALIZER(db_t4_table);
367*662e30fcSMichael Zhilin _DB_SET(_show, gpioths, NULL, db_show_table, 0, &db_gpioths_table);
368*662e30fcSMichael Zhilin 
369*662e30fcSMichael Zhilin DB_FUNC(read, db_show_gpiothsread, db_gpioths_table, CS_OWN, NULL)
370*662e30fcSMichael Zhilin {
371*662e30fcSMichael Zhilin 	device_t	dev;
372*662e30fcSMichael Zhilin 	int		t;
373*662e30fcSMichael Zhilin 	int		init;
374*662e30fcSMichael Zhilin 
375*662e30fcSMichael Zhilin 	init = 0;
376*662e30fcSMichael Zhilin 	t = db_read_token();
377*662e30fcSMichael Zhilin 	if (t == tIDENT) {
378*662e30fcSMichael Zhilin 		dev = device_lookup_by_name(db_tok_string);
379*662e30fcSMichael Zhilin 		init = 1;
380*662e30fcSMichael Zhilin 	}
381*662e30fcSMichael Zhilin 
382*662e30fcSMichael Zhilin 	db_skip_to_eol();
383*662e30fcSMichael Zhilin 
384*662e30fcSMichael Zhilin 	if (init)
385*662e30fcSMichael Zhilin 		db_printf("read: 0x%x\n",
386*662e30fcSMichael Zhilin 		    gpioths_dht_readbytes(dev, device_get_parent(dev)));
387*662e30fcSMichael Zhilin 	else
388*662e30fcSMichael Zhilin 		db_printf("usage: show gpioths read <gpiothsdevice>\n");
389*662e30fcSMichael Zhilin 
390*662e30fcSMichael Zhilin return;
391*662e30fcSMichael Zhilin }
392*662e30fcSMichael Zhilin #endif /* DDB */
393*662e30fcSMichael Zhilin 
394*662e30fcSMichael Zhilin /* Driver bits */
395*662e30fcSMichael Zhilin static device_method_t gpioths_methods[] = {
396*662e30fcSMichael Zhilin 	/* Device interface */
397*662e30fcSMichael Zhilin 	DEVMETHOD(device_probe,			gpioths_probe),
398*662e30fcSMichael Zhilin 	DEVMETHOD(device_attach,		gpioths_attach),
399*662e30fcSMichael Zhilin 	DEVMETHOD(device_detach,		gpioths_detach),
400*662e30fcSMichael Zhilin 
401*662e30fcSMichael Zhilin 	DEVMETHOD_END
402*662e30fcSMichael Zhilin };
403*662e30fcSMichael Zhilin 
404*662e30fcSMichael Zhilin DEFINE_CLASS_0(gpioths, gpioths_driver, gpioths_methods, sizeof(struct gpioths_softc));
405*662e30fcSMichael Zhilin DRIVER_MODULE(gpioths, gpiobus, gpioths_driver, gpioths_devclass, 0, 0);
406