]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/gpio/gpioths.c
MFV r329502: 7614 zfs device evacuation/removal
[FreeBSD/FreeBSD.git] / sys / dev / gpio / gpioths.c
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);