1662e30fcSMichael Zhilin /*-
25f0cf995SIan Lepore * SPDX-License-Identifier: BSD-2-Clause
35f0cf995SIan Lepore *
45f0cf995SIan Lepore * Copyright (c) 2016 Michael Zhilin <mizhka@freebsd.org> All rights reserved.
5ffe0ca86SIan Lepore * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
6662e30fcSMichael Zhilin *
7662e30fcSMichael Zhilin * Redistribution and use in source and binary forms, with or without
8662e30fcSMichael Zhilin * modification, are permitted provided that the following conditions
9662e30fcSMichael Zhilin * are met:
10662e30fcSMichael Zhilin * 1. Redistributions of source code must retain the above copyright
11662e30fcSMichael Zhilin * notice, this list of conditions and the following disclaimer.
12662e30fcSMichael Zhilin * 2. Redistributions in binary form must reproduce the above copyright
13662e30fcSMichael Zhilin * notice, this list of conditions and the following disclaimer in the
14662e30fcSMichael Zhilin * documentation and/or other materials provided with the distribution.
15662e30fcSMichael Zhilin *
16662e30fcSMichael Zhilin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17662e30fcSMichael Zhilin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18662e30fcSMichael Zhilin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19662e30fcSMichael Zhilin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20662e30fcSMichael Zhilin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21662e30fcSMichael Zhilin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22662e30fcSMichael Zhilin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23662e30fcSMichael Zhilin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24662e30fcSMichael Zhilin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25662e30fcSMichael Zhilin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26662e30fcSMichael Zhilin * SUCH DAMAGE.
27662e30fcSMichael Zhilin */
28662e30fcSMichael Zhilin
29662e30fcSMichael Zhilin /*
301398c4c5SIan Lepore * GPIOTHS - Temp/Humidity sensor over GPIO.
311398c4c5SIan Lepore *
32662e30fcSMichael Zhilin * This is driver for Temperature & Humidity sensor which provides digital
33662e30fcSMichael Zhilin * output over single-wire protocol from embedded 8-bit microcontroller.
345f0cf995SIan Lepore * Note that it uses a custom single-wire protocol, it is not 1-wire(tm).
35662e30fcSMichael Zhilin *
36ce508b36SIan Lepore * This driver supports the following chips:
37ce508b36SIan Lepore * DHT11: Temp 0c to 50c +-2.0c, Humidity 20% to 90% +-5%
38ce508b36SIan Lepore * DHT12: Temp -20c to 60c +-0.5c, Humidity 20% to 95% +-5%
39ce508b36SIan Lepore * DHT21: Temp -40c to 80c +-0.3c, Humidity 0% to 100% +-3%
40ce508b36SIan Lepore * DHT22: Temp -40c to 80c +-0.3c, Humidity 0% to 100% +-2%
41ce508b36SIan Lepore * AM2301: Same as DHT21, but also supports i2c interface.
42ce508b36SIan Lepore * AM2302: Same as DHT22, but also supports i2c interface.
43ce508b36SIan Lepore *
44662e30fcSMichael Zhilin * Temp/Humidity sensor can't be discovered automatically, please specify hints
45662e30fcSMichael Zhilin * as part of loader or kernel configuration:
46662e30fcSMichael Zhilin * hint.gpioths.0.at="gpiobus0"
47662e30fcSMichael Zhilin * hint.gpioths.0.pins=<PIN>
481398c4c5SIan Lepore *
491398c4c5SIan Lepore * Or configure via FDT data.
50662e30fcSMichael Zhilin */
51662e30fcSMichael Zhilin
521398c4c5SIan Lepore #include <sys/param.h>
531398c4c5SIan Lepore #include <sys/kernel.h>
541398c4c5SIan Lepore #include <sys/bus.h>
551398c4c5SIan Lepore #include <sys/gpio.h>
561398c4c5SIan Lepore #include <sys/module.h>
571398c4c5SIan Lepore #include <sys/errno.h>
581398c4c5SIan Lepore #include <sys/systm.h>
591398c4c5SIan Lepore #include <sys/sysctl.h>
60989da27eSIan Lepore #include <sys/taskqueue.h>
611398c4c5SIan Lepore
621398c4c5SIan Lepore #include <dev/gpio/gpiobusvar.h>
631398c4c5SIan Lepore
641398c4c5SIan Lepore #ifdef FDT
651398c4c5SIan Lepore #include <dev/ofw/ofw_bus.h>
661398c4c5SIan Lepore #include <dev/ofw/ofw_bus_subr.h>
671398c4c5SIan Lepore
681398c4c5SIan Lepore static struct ofw_compat_data compat_data[] = {
691398c4c5SIan Lepore {"dht11", true},
701398c4c5SIan Lepore {NULL, false}
711398c4c5SIan Lepore };
72519b64e2SMark Johnston OFWBUS_PNP_INFO(compat_data);
73519b64e2SMark Johnston SIMPLEBUS_PNP_INFO(compat_data);
741398c4c5SIan Lepore #endif /* FDT */
751398c4c5SIan Lepore
761398c4c5SIan Lepore #define PIN_IDX 0 /* Use the first/only configured pin. */
771398c4c5SIan Lepore
78662e30fcSMichael Zhilin #define GPIOTHS_POLLTIME 5 /* in seconds */
79662e30fcSMichael Zhilin
80662e30fcSMichael Zhilin #define GPIOTHS_DHT_STARTCYCLE 20000 /* 20ms = 20000us */
81662e30fcSMichael Zhilin #define GPIOTHS_DHT_TIMEOUT 1000 /* 1ms = 1000us */
82662e30fcSMichael Zhilin #define GPIOTHS_DHT_CYCLES 41
83662e30fcSMichael Zhilin #define GPIOTHS_DHT_ONEBYTEMASK 0xFF
84662e30fcSMichael Zhilin
85662e30fcSMichael Zhilin struct gpioths_softc {
86662e30fcSMichael Zhilin device_t dev;
871398c4c5SIan Lepore gpio_pin_t pin;
88662e30fcSMichael Zhilin int temp;
89662e30fcSMichael Zhilin int hum;
90662e30fcSMichael Zhilin int fails;
91989da27eSIan Lepore struct timeout_task task;
92989da27eSIan Lepore bool detaching;
93662e30fcSMichael Zhilin };
94662e30fcSMichael Zhilin
95662e30fcSMichael Zhilin static int
gpioths_probe(device_t dev)96662e30fcSMichael Zhilin gpioths_probe(device_t dev)
97662e30fcSMichael Zhilin {
981398c4c5SIan Lepore int rv;
991398c4c5SIan Lepore
1001398c4c5SIan Lepore /*
1011398c4c5SIan Lepore * By default we only bid to attach if specifically added by our parent
1021398c4c5SIan Lepore * (usually via hint.gpioths.#.at=busname). On FDT systems we bid as
1031398c4c5SIan Lepore * the default driver based on being configured in the FDT data.
1041398c4c5SIan Lepore */
1051398c4c5SIan Lepore rv = BUS_PROBE_NOWILDCARD;
1061398c4c5SIan Lepore
1071398c4c5SIan Lepore #ifdef FDT
1081398c4c5SIan Lepore if (ofw_bus_status_okay(dev) &&
1091398c4c5SIan Lepore ofw_bus_search_compatible(dev, compat_data)->ocd_data)
1101398c4c5SIan Lepore rv = BUS_PROBE_DEFAULT;
1111398c4c5SIan Lepore #endif
1121398c4c5SIan Lepore
1131398c4c5SIan Lepore device_set_desc(dev, "DHT11/DHT22 Temperature and Humidity Sensor");
1141398c4c5SIan Lepore
1151398c4c5SIan Lepore return (rv);
116662e30fcSMichael Zhilin }
117662e30fcSMichael Zhilin
118662e30fcSMichael Zhilin static int
gpioths_dht_timeuntil(struct gpioths_softc * sc,bool lev,uint32_t * time)1191398c4c5SIan Lepore gpioths_dht_timeuntil(struct gpioths_softc *sc, bool lev, uint32_t *time)
120662e30fcSMichael Zhilin {
1211398c4c5SIan Lepore bool cur_level;
122662e30fcSMichael Zhilin int i;
123662e30fcSMichael Zhilin
124662e30fcSMichael Zhilin for (i = 0; i < GPIOTHS_DHT_TIMEOUT; i++) {
1251398c4c5SIan Lepore gpio_pin_is_active(sc->pin, &cur_level);
126662e30fcSMichael Zhilin if (cur_level == lev) {
127662e30fcSMichael Zhilin if (time != NULL)
128662e30fcSMichael Zhilin *time = i;
129662e30fcSMichael Zhilin return (0);
130662e30fcSMichael Zhilin }
131662e30fcSMichael Zhilin DELAY(1);
132662e30fcSMichael Zhilin }
133662e30fcSMichael Zhilin
134662e30fcSMichael Zhilin /* Timeout */
135662e30fcSMichael Zhilin return (ETIMEDOUT);
136662e30fcSMichael Zhilin }
137662e30fcSMichael Zhilin
1381398c4c5SIan Lepore static void
gpioths_dht_initread(struct gpioths_softc * sc)1391398c4c5SIan Lepore gpioths_dht_initread(struct gpioths_softc *sc)
140662e30fcSMichael Zhilin {
141662e30fcSMichael Zhilin
142662e30fcSMichael Zhilin /*
1431398c4c5SIan Lepore * According to specifications we need to drive the data line low for at
1441398c4c5SIan Lepore * least 20ms then drive it high, to wake up the chip and signal it to
1451398c4c5SIan Lepore * send a measurement. After sending this start signal, we switch the
1461398c4c5SIan Lepore * pin back to input so the device can begin talking to us.
147662e30fcSMichael Zhilin */
1481398c4c5SIan Lepore gpio_pin_setflags(sc->pin, GPIO_PIN_OUTPUT);
1491398c4c5SIan Lepore gpio_pin_set_active(sc->pin, false);
150989da27eSIan Lepore pause_sbt("gpioths", ustosbt(GPIOTHS_DHT_STARTCYCLE), C_PREL(2), 0);
1511398c4c5SIan Lepore gpio_pin_set_active(sc->pin, true);
1521398c4c5SIan Lepore gpio_pin_setflags(sc->pin, GPIO_PIN_INPUT);
153662e30fcSMichael Zhilin }
154662e30fcSMichael Zhilin
155662e30fcSMichael Zhilin static int
gpioths_dht_readbytes(struct gpioths_softc * sc)1561398c4c5SIan Lepore gpioths_dht_readbytes(struct gpioths_softc *sc)
157662e30fcSMichael Zhilin {
158662e30fcSMichael Zhilin uint32_t calibrations[GPIOTHS_DHT_CYCLES];
159662e30fcSMichael Zhilin uint32_t intervals[GPIOTHS_DHT_CYCLES];
160662e30fcSMichael Zhilin uint32_t err, avglen, value;
161662e30fcSMichael Zhilin uint8_t crc, calc;
162ce508b36SIan Lepore int i, negmul, offset, size, tmphi, tmplo;
163662e30fcSMichael Zhilin
1641398c4c5SIan Lepore gpioths_dht_initread(sc);
165662e30fcSMichael Zhilin
1661398c4c5SIan Lepore err = gpioths_dht_timeuntil(sc, false, NULL);
167662e30fcSMichael Zhilin if (err) {
1681398c4c5SIan Lepore device_printf(sc->dev, "err(START) = %d\n", err);
169662e30fcSMichael Zhilin goto error;
170662e30fcSMichael Zhilin }
171662e30fcSMichael Zhilin
172662e30fcSMichael Zhilin /* reading - 41 cycles */
173662e30fcSMichael Zhilin for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) {
1741398c4c5SIan Lepore err = gpioths_dht_timeuntil(sc, true, &calibrations[i]);
175662e30fcSMichael Zhilin if (err) {
1761398c4c5SIan Lepore device_printf(sc->dev, "err(CAL, %d) = %d\n", i, err);
177662e30fcSMichael Zhilin goto error;
178662e30fcSMichael Zhilin }
1791398c4c5SIan Lepore err = gpioths_dht_timeuntil(sc, false, &intervals[i]);
180662e30fcSMichael Zhilin if (err) {
1811398c4c5SIan Lepore device_printf(sc->dev, "err(INTERVAL, %d) = %d\n", i, err);
182662e30fcSMichael Zhilin goto error;
183662e30fcSMichael Zhilin }
184662e30fcSMichael Zhilin }
185662e30fcSMichael Zhilin
186662e30fcSMichael Zhilin /* Calculate average data calibration cycle length */
187662e30fcSMichael Zhilin avglen = 0;
188662e30fcSMichael Zhilin for (i = 1; i < GPIOTHS_DHT_CYCLES; i++)
189662e30fcSMichael Zhilin avglen += calibrations[i];
190662e30fcSMichael Zhilin
191662e30fcSMichael Zhilin avglen = avglen / (GPIOTHS_DHT_CYCLES - 1);
192662e30fcSMichael Zhilin
193662e30fcSMichael Zhilin /* Calculate data */
194662e30fcSMichael Zhilin value = 0;
195662e30fcSMichael Zhilin offset = 1;
196662e30fcSMichael Zhilin size = sizeof(value) * 8;
197662e30fcSMichael Zhilin for (i = offset; i < size + offset; i++) {
198662e30fcSMichael Zhilin value <<= 1;
199662e30fcSMichael Zhilin if (intervals[i] > avglen)
200662e30fcSMichael Zhilin value += 1;
201662e30fcSMichael Zhilin }
202662e30fcSMichael Zhilin
203662e30fcSMichael Zhilin /* Calculate CRC */
204662e30fcSMichael Zhilin crc = 0;
205662e30fcSMichael Zhilin offset = sizeof(value) * 8 + 1;
206662e30fcSMichael Zhilin size = sizeof(crc) * 8;
207662e30fcSMichael Zhilin for (i = offset; i < size + offset; i++) {
208662e30fcSMichael Zhilin crc <<= 1;
209662e30fcSMichael Zhilin if (intervals[i] > avglen)
210662e30fcSMichael Zhilin crc += 1;
211662e30fcSMichael Zhilin }
212662e30fcSMichael Zhilin
213662e30fcSMichael Zhilin calc = 0;
214662e30fcSMichael Zhilin for (i = 0; i < sizeof(value); i++)
215662e30fcSMichael Zhilin calc += (value >> (8*i)) & GPIOTHS_DHT_ONEBYTEMASK;
216662e30fcSMichael Zhilin
217662e30fcSMichael Zhilin #ifdef GPIOTHS_DEBUG
218662e30fcSMichael Zhilin /* Debug bits */
219662e30fcSMichael Zhilin for (i = 0; i < GPIOTHS_DHT_CYCLES; i++)
220aa063061SBrad Davis device_printf(sc->dev, "%d: %d %d\n", i, calibrations[i],
221662e30fcSMichael Zhilin intervals[i]);
222662e30fcSMichael Zhilin
223aa063061SBrad Davis device_printf(sc->dev, "len=%d, data=%x, crc=%x/%x\n", avglen, value, crc,
224662e30fcSMichael Zhilin calc);
225662e30fcSMichael Zhilin #endif /* GPIOTHS_DEBUG */
226662e30fcSMichael Zhilin
227662e30fcSMichael Zhilin /* CRC check */
228662e30fcSMichael Zhilin if (calc != crc) {
229662e30fcSMichael Zhilin err = -1;
230662e30fcSMichael Zhilin goto error;
231662e30fcSMichael Zhilin }
232662e30fcSMichael Zhilin
233ce508b36SIan Lepore /*
234ce508b36SIan Lepore * For DHT11/12, the values are split into 8 bits of integer and 8 bits
235ce508b36SIan Lepore * of fractional tenths. On DHT11 the fraction bytes are always zero.
236ce508b36SIan Lepore * On DHT12 the sign bit is in the high bit of the fraction byte.
237ce508b36SIan Lepore * - DHT11: 0HHHHHHH 00000000 00TTTTTT 00000000
238ce508b36SIan Lepore * - DHT12: 0HHHHHHH 0000hhhh 00TTTTTT s000tttt
239ce508b36SIan Lepore *
240ce508b36SIan Lepore * For DHT21/21, the values are are encoded in 16 bits each, with the
241ce508b36SIan Lepore * temperature sign bit in the high bit. The values are tenths of a
242ce508b36SIan Lepore * degree C and tenths of a percent RH.
243ce508b36SIan Lepore * - DHT21: 000000HH HHHHHHHH s00000TT TTTTTTTT
244ce508b36SIan Lepore * - DHT22: 000000HH HHHHHHHH s00000TT TTTTTTTT
245ce508b36SIan Lepore *
246ce508b36SIan Lepore * For all devices, some bits are always zero because of the range of
247ce508b36SIan Lepore * values supported by the device.
248ce508b36SIan Lepore *
249ce508b36SIan Lepore * We figure out how to decode things based on the high byte of the
250ce508b36SIan Lepore * humidity. A DHT21/22 cannot report a value greater than 3 in
251ce508b36SIan Lepore * the upper bits of its 16-bit humidity. A DHT11/12 should not report
252ce508b36SIan Lepore * a value lower than 20. To allow for the possibility that a device
253ce508b36SIan Lepore * could report a value slightly out of its sensitivity range, we split
2541398c4c5SIan Lepore * the difference and say if the value is greater than 10 it must be a
2551398c4c5SIan Lepore * DHT11/12 (that would be a humidity over 256% on a DHT21/22).
256ce508b36SIan Lepore */
257ce508b36SIan Lepore #define DK_OFFSET 2731 /* Offset between K and C, in decikelvins. */
258ce508b36SIan Lepore if ((value >> 24) > 10) {
259ce508b36SIan Lepore /* DHT11 or DHT12 */
260ce508b36SIan Lepore tmphi = (value >> 8) & 0x3f;
261ce508b36SIan Lepore tmplo = value & 0x0f;
262ce508b36SIan Lepore negmul = (value & 0x80) ? -1 : 1;
263ce508b36SIan Lepore sc->temp = DK_OFFSET + (negmul * (tmphi * 10 + tmplo));
264ce508b36SIan Lepore sc->hum = (value >> 24) & 0x7f;
265ce508b36SIan Lepore } else {
266ce508b36SIan Lepore /* DHT21 or DHT22 */
267ce508b36SIan Lepore negmul = (value & 0x8000) ? -1 : 1;
268ce508b36SIan Lepore sc->temp = DK_OFFSET + (negmul * (value & 0x03ff));
269ce508b36SIan Lepore sc->hum = ((value >> 16) & 0x03ff) / 10;
270ce508b36SIan Lepore }
271ce508b36SIan Lepore
272662e30fcSMichael Zhilin sc->fails = 0;
273662e30fcSMichael Zhilin
274662e30fcSMichael Zhilin #ifdef GPIOTHS_DEBUG
275662e30fcSMichael Zhilin /* Debug bits */
276662e30fcSMichael Zhilin device_printf(dev, "fails=%d, temp=%d, hum=%d\n", sc->fails,
277662e30fcSMichael Zhilin sc->temp, sc->hum);
278662e30fcSMichael Zhilin #endif /* GPIOTHS_DEBUG */
279662e30fcSMichael Zhilin
280662e30fcSMichael Zhilin return (0);
281662e30fcSMichael Zhilin error:
282662e30fcSMichael Zhilin sc->fails++;
283662e30fcSMichael Zhilin return (err);
284662e30fcSMichael Zhilin }
285662e30fcSMichael Zhilin
286662e30fcSMichael Zhilin static void
gpioths_poll(void * arg,int pending __unused)287989da27eSIan Lepore gpioths_poll(void *arg, int pending __unused)
288662e30fcSMichael Zhilin {
289662e30fcSMichael Zhilin struct gpioths_softc *sc;
290662e30fcSMichael Zhilin
2911398c4c5SIan Lepore sc = (struct gpioths_softc *)arg;
292662e30fcSMichael Zhilin
2931398c4c5SIan Lepore gpioths_dht_readbytes(sc);
294989da27eSIan Lepore if (!sc->detaching)
295989da27eSIan Lepore taskqueue_enqueue_timeout_sbt(taskqueue_thread, &sc->task,
296989da27eSIan Lepore GPIOTHS_POLLTIME * SBT_1S, 0, C_PREL(3));
297662e30fcSMichael Zhilin }
298662e30fcSMichael Zhilin
299662e30fcSMichael Zhilin static int
gpioths_attach(device_t dev)300662e30fcSMichael Zhilin gpioths_attach(device_t dev)
301662e30fcSMichael Zhilin {
302662e30fcSMichael Zhilin struct gpioths_softc *sc;
303662e30fcSMichael Zhilin struct sysctl_ctx_list *ctx;
304662e30fcSMichael Zhilin struct sysctl_oid *tree;
3051398c4c5SIan Lepore int err;
306662e30fcSMichael Zhilin
307662e30fcSMichael Zhilin sc = device_get_softc(dev);
308662e30fcSMichael Zhilin ctx = device_get_sysctl_ctx(dev);
309662e30fcSMichael Zhilin tree = device_get_sysctl_tree(dev);
310662e30fcSMichael Zhilin
311662e30fcSMichael Zhilin sc->dev = dev;
312662e30fcSMichael Zhilin
313989da27eSIan Lepore TIMEOUT_TASK_INIT(taskqueue_thread, &sc->task, 0, gpioths_poll, sc);
314989da27eSIan Lepore
3151398c4c5SIan Lepore #ifdef FDT
3161398c4c5SIan Lepore /* Try to configure our pin from fdt data on fdt-based systems. */
3171398c4c5SIan Lepore err = gpio_pin_get_by_ofw_idx(dev, ofw_bus_get_node(dev), PIN_IDX,
3181398c4c5SIan Lepore &sc->pin);
3191398c4c5SIan Lepore #else
3201398c4c5SIan Lepore err = ENOENT;
3211398c4c5SIan Lepore #endif
3221398c4c5SIan Lepore /*
3231398c4c5SIan Lepore * If we didn't get configured by fdt data and our parent is gpiobus,
3241398c4c5SIan Lepore * see if we can be configured by the bus (allows hinted attachment even
3251398c4c5SIan Lepore * on fdt-based systems).
3261398c4c5SIan Lepore */
3271398c4c5SIan Lepore if (err != 0 &&
3281398c4c5SIan Lepore strcmp("gpiobus", device_get_name(device_get_parent(dev))) == 0)
3291398c4c5SIan Lepore err = gpio_pin_get_by_child_index(dev, PIN_IDX, &sc->pin);
3301398c4c5SIan Lepore
3311398c4c5SIan Lepore /* If we didn't get configured by either method, whine and punt. */
3321398c4c5SIan Lepore if (err != 0) {
3331398c4c5SIan Lepore device_printf(sc->dev,
3341398c4c5SIan Lepore "cannot acquire gpio pin (config error)\n");
3351398c4c5SIan Lepore return (err);
3361398c4c5SIan Lepore }
3371398c4c5SIan Lepore
3381398c4c5SIan Lepore /*
3391398c4c5SIan Lepore * Ensure we have control of our pin, and preset the data line to its
3401398c4c5SIan Lepore * idle condition (high). Leave the line in input mode, relying on the
3411398c4c5SIan Lepore * external pullup to keep the line high while idle.
3421398c4c5SIan Lepore */
3431398c4c5SIan Lepore err = gpio_pin_setflags(sc->pin, GPIO_PIN_OUTPUT);
3441398c4c5SIan Lepore if (err != 0) {
3451398c4c5SIan Lepore device_printf(dev, "gpio_pin_setflags(OUT) = %d\n", err);
3461398c4c5SIan Lepore return (err);
3471398c4c5SIan Lepore }
3481398c4c5SIan Lepore err = gpio_pin_set_active(sc->pin, true);
3491398c4c5SIan Lepore if (err != 0) {
3501398c4c5SIan Lepore device_printf(dev, "gpio_pin_set_active(false) = %d\n", err);
3511398c4c5SIan Lepore return (err);
3521398c4c5SIan Lepore }
3531398c4c5SIan Lepore err = gpio_pin_setflags(sc->pin, GPIO_PIN_INPUT);
3541398c4c5SIan Lepore if (err != 0) {
3551398c4c5SIan Lepore device_printf(dev, "gpio_pin_setflags(IN) = %d\n", err);
3561398c4c5SIan Lepore return (err);
3571398c4c5SIan Lepore }
3581398c4c5SIan Lepore
3599f8df20cSIan Lepore /*
3609f8df20cSIan Lepore * Do an initial read so we have correct values for reporting before
361989da27eSIan Lepore * registering the sysctls that can access those values. This also
362989da27eSIan Lepore * schedules the periodic polling the driver does every few seconds to
363989da27eSIan Lepore * update the sysctl variables.
3649f8df20cSIan Lepore */
365989da27eSIan Lepore gpioths_poll(sc, 0);
366662e30fcSMichael Zhilin
36778e70ab9SIan Lepore sysctl_add_oid(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "temperature", \
36878e70ab9SIan Lepore CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE,
369ce508b36SIan Lepore &sc->temp, 0, sysctl_handle_int, "IK", "temperature", NULL);
370662e30fcSMichael Zhilin
37178e70ab9SIan Lepore SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "humidity",
37278e70ab9SIan Lepore CTLFLAG_RD, &sc->hum, 0, "relative humidity(%)");
373662e30fcSMichael Zhilin
37478e70ab9SIan Lepore SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "fails",
37578e70ab9SIan Lepore CTLFLAG_RD, &sc->fails, 0,
37678e70ab9SIan Lepore "failures since last successful read");
377662e30fcSMichael Zhilin
378662e30fcSMichael Zhilin return (0);
379662e30fcSMichael Zhilin }
380662e30fcSMichael Zhilin
381662e30fcSMichael Zhilin static int
gpioths_detach(device_t dev)382662e30fcSMichael Zhilin gpioths_detach(device_t dev)
383662e30fcSMichael Zhilin {
3849f8df20cSIan Lepore struct gpioths_softc *sc;
3859f8df20cSIan Lepore
3869f8df20cSIan Lepore sc = device_get_softc(dev);
3871398c4c5SIan Lepore gpio_pin_release(sc->pin);
388989da27eSIan Lepore sc->detaching = true;
389989da27eSIan Lepore while (taskqueue_cancel_timeout(taskqueue_thread, &sc->task, NULL) != 0)
390989da27eSIan Lepore taskqueue_drain_timeout(taskqueue_thread, &sc->task);
391662e30fcSMichael Zhilin
392662e30fcSMichael Zhilin return (0);
393662e30fcSMichael Zhilin }
394662e30fcSMichael Zhilin
395662e30fcSMichael Zhilin /* Driver bits */
396662e30fcSMichael Zhilin static device_method_t gpioths_methods[] = {
397662e30fcSMichael Zhilin /* Device interface */
398662e30fcSMichael Zhilin DEVMETHOD(device_probe, gpioths_probe),
399662e30fcSMichael Zhilin DEVMETHOD(device_attach, gpioths_attach),
400662e30fcSMichael Zhilin DEVMETHOD(device_detach, gpioths_detach),
401662e30fcSMichael Zhilin
402662e30fcSMichael Zhilin DEVMETHOD_END
403662e30fcSMichael Zhilin };
404662e30fcSMichael Zhilin
405662e30fcSMichael Zhilin DEFINE_CLASS_0(gpioths, gpioths_driver, gpioths_methods, sizeof(struct gpioths_softc));
4061398c4c5SIan Lepore
4071398c4c5SIan Lepore #ifdef FDT
408*84c5f982SJohn Baldwin DRIVER_MODULE(gpioths, simplebus, gpioths_driver, 0, 0);
4091398c4c5SIan Lepore #endif
4101398c4c5SIan Lepore
411*84c5f982SJohn Baldwin DRIVER_MODULE(gpioths, gpiobus, gpioths_driver, 0, 0);
41235e9bfc9SIan Lepore MODULE_DEPEND(gpioths, gpiobus, 1, 1, 1);
413