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