From owner-svn-src-all@freebsd.org Sun Dec 22 18:51:06 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id E74B21D9BB2; Sun, 22 Dec 2019 18:51:06 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 47gs566RY9z3CGm; Sun, 22 Dec 2019 18:51:06 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id D820E3D2E; Sun, 22 Dec 2019 18:51:06 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xBMIp6ct012155; Sun, 22 Dec 2019 18:51:06 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xBMIp6Fh012150; Sun, 22 Dec 2019 18:51:06 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201912221851.xBMIp6Fh012150@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Sun, 22 Dec 2019 18:51:06 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r356018 - in stable/12: share/man/man4 sys/dev/gpio sys/modules/gpio sys/modules/gpio/gpioths X-SVN-Group: stable-12 X-SVN-Commit-Author: ian X-SVN-Commit-Paths: in stable/12: share/man/man4 sys/dev/gpio sys/modules/gpio sys/modules/gpio/gpioths X-SVN-Commit-Revision: 356018 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 22 Dec 2019 18:51:07 -0000 Author: ian Date: Sun Dec 22 18:51:05 2019 New Revision: 356018 URL: https://svnweb.freebsd.org/changeset/base/356018 Log: MFC r355467, r355499, r355531-r355533, r355535, r355540, r355548-r355551, r355565 r355467: Implement bus_rescan for gpiobus(4). This allows on-the-fly reconfiguration of gpio devices by using kenv to add hints for a new device and then do 'devctl rescan gpiobus4' to make the new device(s) attach. It's not particularly easy to detect whether the 'at' hint has been deleted for a child device that's currently attached, so this doesn't handle that. But the user can use devctl commands to manually detach an existing device. r355499: Add module build stuff for gpioths(4), a driver for DHT11/DHT22 sensors. r355531: Several small fixes for the gpioths (temp/humidity sensor) driver. At the end of a read cycle, set the gpio pin to INPUT rather than OUTPUT. The state of the single-wire "bus" when idle should be high; setting the pin to input allows the external pullup to pull the line high. Setting it to output (and leaving it driving low) was leading a good read cycle followed by one that would fail, and it just continued like that forever, effectively reading the sensor once every 10 seconds instead of 5. In the attach function, do an initial read from the device before registering the sysctls for accessing the last-read values, to prevent reading spurious values for the first 5 seconds after the driver attaches. Do a callout_drain() in the detach function to prevent crashes after unloading the module. r355532: Simplify sysctl stuff in the gpioths driver. There is no need to use local functions to handle the sysctls, they all just access simple readonly integer variables. There's no need to track the oids of the ones we add, since the teardown is done by newbus code, not the driver itself. Also remove the DDB code, because it just provides access to the same data that the sysctls already provide. r355533: Add support for more chips to the gpioths driver. Previously the driver supported the DHT11 sensor. Now it supports DHT11, DHT12, DHT21, DHT22, AM3201, AM3202. All these chips are similar, differing primarily in supported temperature and humidity ranges and accuracy (and, presumably, cost). There are two basic data formats reported by the various chips, and it is possible to figure out at runtime which format to use for decoding the data based on the range of values in a single byte of the humidity measurement. (which is detailed in a comment block, so I won't recapitulate it here). r355535: Add a MODULE_DEPEND() for the gpioths driver. Also, note that the prior commit changed the sysctl format for the temperature from "I" to "IK", and correspondingly changed the units from integer degrees C to decikelvin. For access via sysctl(8) the output will be the same except that now decimal fractions will be shown when available. r355540: Add FDT support to the gpioths driver. It now uses the newer gpio_pin_*() API and can attach based on either hints or fdt data. r355548: Add a man page for the gpioths(4) driver. r355549: Add myself to the copyright list. Also add an SPDX tag. And finally, fix a missing word and a spelling error in a comment. r355550: Paste things correctly so that I'm added to the *end* of the copyright list. r355551: Connect the gpioths(4) manpage to the build. r355565: Switch gpioths(4) from using a callout to a taskqueue for periodic polling of the sensor hardware. Part of the polling process involves signalling the chip then waiting 20 milliseconds. This was being done with DELAY(), which is a pretty rude thing to do in a callout. Now a taskqueue_thread task is scheduled to do the polling, and because sleeping is allowed in the task context, pause_sbt() replaces DELAY() for the 20ms wait. Added: stable/12/share/man/man4/gpioths.4 - copied unchanged from r355551, head/share/man/man4/gpioths.4 stable/12/sys/modules/gpio/gpioths/ - copied from r355499, head/sys/modules/gpio/gpioths/ Modified: stable/12/share/man/man4/Makefile stable/12/sys/dev/gpio/gpiobus.c stable/12/sys/dev/gpio/gpioths.c stable/12/sys/modules/gpio/Makefile Directory Properties: stable/12/ (props changed) Modified: stable/12/share/man/man4/Makefile ============================================================================== --- stable/12/share/man/man4/Makefile Sun Dec 22 18:11:57 2019 (r356017) +++ stable/12/share/man/man4/Makefile Sun Dec 22 18:51:05 2019 (r356018) @@ -182,6 +182,7 @@ MAN= aac.4 \ gpio.4 \ gpioiic.4 \ gpioled.4 \ + gpioths.4 \ gre.4 \ h_ertt.4 \ hifn.4 \ @@ -668,6 +669,8 @@ MLINKS+=gem.4 if_gem.4 MLINKS+=geom.4 GEOM.4 MLINKS+=gif.4 if_gif.4 MLINKS+=gpio.4 gpiobus.4 +MLINKS+=gpioths.4 dht11.4 +MLINKS+=gpioths.4 dht22.4 MLINKS+=gre.4 if_gre.4 MLINKS+=hme.4 if_hme.4 MLINKS+=hpet.4 acpi_hpet.4 Copied: stable/12/share/man/man4/gpioths.4 (from r355551, head/share/man/man4/gpioths.4) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/12/share/man/man4/gpioths.4 Sun Dec 22 18:51:05 2019 (r356018, copy of r355551, head/share/man/man4/gpioths.4) @@ -0,0 +1,152 @@ +.\"- +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.\" Copyright (c) 2019 Ian Lepore +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd December 8, 2019 +.Dt GPIOTHS 4 +.Os +.Sh NAME +.Nm gpioths +.Nd driver for DHTxx and AM320x temperature and humidity sensors +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following line in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device gpioths" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +gpioths_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver supports the DHTxx and AM320x family of +temperature and humidity sensors. +The driver automatically reads the values from the sensor +once every 5 seconds, and makes the results available via +.Xr sysctl 8 +variables. +.Sh HARDWARE +The +.Nm +driver provides support for the following devices: +.Pp +.Bl -column -compact -offset indent "XXXXXXXX" "XXXXXXXX" +.It DHT11 Ta DHT12 +.It DHT21 Ta DHT22 +.It AM3201 Ta AM3202 +.El +.Pp +The supported devices are all similar to each other, varying +primarily in accuracy and resolution. +The devices require a single wire for data communications, using a +custom protocol which is not compatible with Maxim's 1-wire(tm). +The AM320x devices also support connection to an i2c bus, +but this driver supports only the single-wire connection option. +.Sh SYSCTL VARIABLES +Sysctl variables are used to access the most recent temperature and +humidity measurements. +.Bl -tag -width indent +.It Va dev.gpioths..temp +The current temperature in integer deciKelvins. +Note that +.Xr sysctl 8 +will convert those units to display in decimal degrees Celcius. +.It Va dev.gpioths..hum +The current relative humidity, as an integer percentage. +.It Va dev.gpioths..fails +The number of failed attempts to communicate with the sensor since +the last good access. +Cleared whenever a set of measurements is successfully retrieved. +.El +.Sh FDT CONFIGURATION +On an +.Xr fdt 4 +based system, a +.Nm +device node is typically defined directly under the root node, or under +a simplebus node that represents a collection of devices on a board. +.Pp +The following properties are required in the +.Nm +device subnode: +.Bl -tag -width indent +.It Va compatible +Must be "dht11". +.It Va gpios +A reference to the gpio device and pin for data communications. +.El +.Ss Example of adding a sensor with an overlay +.Bd -unfilled -offset indent +/dts-v1/; +/plugin/; +#include + +/ { + compatible = "wand,imx6q-wandboard"; +}; + +&{/} { + dht0 { + compatible = "dht11"; + gpios = <&gpio5 15 GPIO_ACTIVE_HIGH>; + }; +}; +.Ed +.Sh HINTS CONFIGURATION +On a +.Xr device.hints 5 +based system, such as +.Li MIPS , +these values are configurable for +.Nm : +.Bl -tag -width indent +.It Va hint.gpioths..at +The +.Xr gpiobus 4 +instance the +.Nm +instance is attached to. +.It Va hint.gpioths.pins +A bitmask with a single bit set to indicate which gpio pin on the +.Xr gpiobus 4 +to use for data communications. +.El +.Sh SEE ALSO +.Xr fdt 4 , +.Xr gpiobus 4 , +.Xr sysctl 8 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 11.1 . Modified: stable/12/sys/dev/gpio/gpiobus.c ============================================================================== --- stable/12/sys/dev/gpio/gpiobus.c Sun Dec 22 18:11:57 2019 (r356017) +++ stable/12/sys/dev/gpio/gpiobus.c Sun Dec 22 18:51:05 2019 (r356018) @@ -716,6 +716,21 @@ gpiobus_add_child(device_t dev, u_int order, const cha return (child); } +static int +gpiobus_rescan(device_t dev) +{ + + /* + * Re-scan is supposed to remove and add children, but if someone has + * deleted the hints for a child we attached earlier, we have no easy + * way to handle that. So this just attaches new children for whom new + * hints or drivers have arrived since we last tried. + */ + bus_enumerate_hinted_children(dev); + bus_generic_attach(dev); + return (0); +} + static void gpiobus_hinted_child(device_t bus, const char *dname, int dunit) { @@ -725,6 +740,10 @@ gpiobus_hinted_child(device_t bus, const char *dname, const char *pins; int irq, pinmask; + if (device_find_child(bus, dname, dunit) != NULL) { + return; + } + child = BUS_ADD_CHILD(bus, 0, dname, dunit); devi = GPIOBUS_IVAR(child); if (resource_int_value(dname, dunit, "pins", &pinmask) == 0) { @@ -1077,6 +1096,7 @@ static device_method_t gpiobus_methods[] = { DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_get_resource_list, gpiobus_get_resource_list), DEVMETHOD(bus_add_child, gpiobus_add_child), + DEVMETHOD(bus_rescan, gpiobus_rescan), DEVMETHOD(bus_probe_nomatch, gpiobus_probe_nomatch), DEVMETHOD(bus_print_child, gpiobus_print_child), DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str), Modified: stable/12/sys/dev/gpio/gpioths.c ============================================================================== --- stable/12/sys/dev/gpio/gpioths.c Sun Dec 22 18:11:57 2019 (r356017) +++ stable/12/sys/dev/gpio/gpioths.c Sun Dec 22 18:51:05 2019 (r356018) @@ -1,7 +1,9 @@ /*- - * Copyright (c) 2016 Michael Zhilin - * All rights reserved. + * SPDX-License-Identifier: BSD-2-Clause * + * Copyright (c) 2016 Michael Zhilin All rights reserved. + * Copyright (c) 2019 Ian Lepore + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -24,88 +26,106 @@ * SUCH DAMAGE. */ +/* + * GPIOTHS - Temp/Humidity sensor over GPIO. + * + * This is driver for Temperature & Humidity sensor which provides digital + * output over single-wire protocol from embedded 8-bit microcontroller. + * Note that it uses a custom single-wire protocol, it is not 1-wire(tm). + * + * This driver supports the following chips: + * DHT11: Temp 0c to 50c +-2.0c, Humidity 20% to 90% +-5% + * DHT12: Temp -20c to 60c +-0.5c, Humidity 20% to 95% +-5% + * DHT21: Temp -40c to 80c +-0.3c, Humidity 0% to 100% +-3% + * DHT22: Temp -40c to 80c +-0.3c, Humidity 0% to 100% +-2% + * AM2301: Same as DHT21, but also supports i2c interface. + * AM2302: Same as DHT22, but also supports i2c interface. + * + * Temp/Humidity sensor can't be discovered automatically, please specify hints + * as part of loader or kernel configuration: + * hint.gpioths.0.at="gpiobus0" + * hint.gpioths.0.pins= + * + * Or configure via FDT data. + */ + #include __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include #include +#include -#include -#include -#include -#include +#include -#include "gpiobus_if.h" +#ifdef FDT +#include +#include -/* - * GPIOTHS - Temp/Humidity sensor over GPIO, e.g. DHT11/DHT22 - * This is driver for Temperature & Humidity sensor which provides digital - * output over single-wire protocol from embedded 8-bit microcontroller. - * - * Temp/Humidity sensor can't be discovered automatically, please specify hints - * as part of loader or kernel configuration: - * hint.gpioths.0.at="gpiobus0" - * hint.gpioths.0.pins= - */ +static struct ofw_compat_data compat_data[] = { + {"dht11", true}, + {NULL, false} +}; +OFWBUS_PNP_INFO(compat_data); +SIMPLEBUS_PNP_INFO(compat_data); +#endif /* FDT */ +#define PIN_IDX 0 /* Use the first/only configured pin. */ + #define GPIOTHS_POLLTIME 5 /* in seconds */ #define GPIOTHS_DHT_STARTCYCLE 20000 /* 20ms = 20000us */ #define GPIOTHS_DHT_TIMEOUT 1000 /* 1ms = 1000us */ #define GPIOTHS_DHT_CYCLES 41 #define GPIOTHS_DHT_ONEBYTEMASK 0xFF -#define GPIOTHS_DHT_TEMP_SHIFT 8 -#define GPIOTHS_DHT_HUM_SHIFT 24 struct gpioths_softc { device_t dev; + gpio_pin_t pin; int temp; int hum; int fails; - struct sysctl_oid *temp_oid; - struct sysctl_oid *hum_oid; - struct sysctl_oid *fails_oid; - struct callout callout; + struct timeout_task task; + bool detaching; }; -static devclass_t gpioths_devclass; - -/* Prototypes */ -static int gpioths_probe(device_t dev); -static int gpioths_attach(device_t dev); -static int gpioths_detach(device_t dev); -static void gpioths_poll(void *arg); -static int gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS); -static int gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS); -static int gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS); - -/* DHT-specific methods */ -static int gpioths_dht_initread(device_t bus, device_t dev); -static int gpioths_dht_readbytes(device_t bus, device_t dev); -static int gpioths_dht_timeuntil(device_t bus, device_t dev, - uint32_t lev, uint32_t *time); - -/* Implementation */ static int gpioths_probe(device_t dev) { - device_set_desc(dev, "Temperature and Humidity Sensor over GPIO"); - return (0); + int rv; + + /* + * By default we only bid to attach if specifically added by our parent + * (usually via hint.gpioths.#.at=busname). On FDT systems we bid as + * the default driver based on being configured in the FDT data. + */ + rv = BUS_PROBE_NOWILDCARD; + +#ifdef FDT + if (ofw_bus_status_okay(dev) && + ofw_bus_search_compatible(dev, compat_data)->ocd_data) + rv = BUS_PROBE_DEFAULT; +#endif + + device_set_desc(dev, "DHT11/DHT22 Temperature and Humidity Sensor"); + + return (rv); } static int -gpioths_dht_timeuntil(device_t bus, device_t dev, uint32_t lev, uint32_t *time) +gpioths_dht_timeuntil(struct gpioths_softc *sc, bool lev, uint32_t *time) { - uint32_t cur_level; + bool cur_level; int i; for (i = 0; i < GPIOTHS_DHT_TIMEOUT; i++) { - GPIOBUS_PIN_GET(bus, dev, 0, &cur_level); + gpio_pin_is_active(sc->pin, &cur_level); if (cur_level == lev) { if (time != NULL) *time = i; @@ -118,93 +138,54 @@ gpioths_dht_timeuntil(device_t bus, device_t dev, uint return (ETIMEDOUT); } -static int -gpioths_dht_initread(device_t bus, device_t dev) +static void +gpioths_dht_initread(struct gpioths_softc *sc) { - int err; - err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT); - if (err != 0) { - device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, OUT) = %d\n", err); - return (err); - } - DELAY(1); - - err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_LOW); - if (err != 0) { - device_printf(dev, "err(GPIOBUS_PIN_SET, LOW) = %d\n", err); - return (err); - } - /* - * According to specifications we need to wait no more than 18ms - * to start data transfer + * According to specifications we need to drive the data line low for at + * least 20ms then drive it high, to wake up the chip and signal it to + * send a measurement. After sending this start signal, we switch the + * pin back to input so the device can begin talking to us. */ - DELAY(GPIOTHS_DHT_STARTCYCLE); - err = GPIOBUS_PIN_SET(bus, dev, 0, GPIO_PIN_HIGH); - if (err != 0) { - device_printf(dev, "err(GPIOBUS_PIN_SET, HIGH) = %d\n", err); - return (err); - } - - DELAY(1); - err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_INPUT) ; - if (err != 0) { - device_printf(dev, "err(GPIOBUS_PIN_SETFLAGS, IN) = %d\n", err); - return (err); - } - - DELAY(1); - return (0); + gpio_pin_setflags(sc->pin, GPIO_PIN_OUTPUT); + gpio_pin_set_active(sc->pin, false); + pause_sbt("gpioths", ustosbt(GPIOTHS_DHT_STARTCYCLE), C_PREL(2), 0); + gpio_pin_set_active(sc->pin, true); + gpio_pin_setflags(sc->pin, GPIO_PIN_INPUT); } static int -gpioths_dht_readbytes(device_t bus, device_t dev) +gpioths_dht_readbytes(struct gpioths_softc *sc) { - struct gpioths_softc *sc; uint32_t calibrations[GPIOTHS_DHT_CYCLES]; uint32_t intervals[GPIOTHS_DHT_CYCLES]; uint32_t err, avglen, value; uint8_t crc, calc; - int i, offset, size; + int i, negmul, offset, size, tmphi, tmplo; - sc = device_get_softc(dev); - - err = gpioths_dht_initread(bus,dev); + gpioths_dht_initread(sc); + + err = gpioths_dht_timeuntil(sc, false, NULL); if (err) { - device_printf(dev, "gpioths_dht_initread error = %d\n", err); + device_printf(sc->dev, "err(START) = %d\n", err); goto error; } - err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW, NULL); - if (err) { - device_printf(dev, "err(START) = %d\n", err); - goto error; - } - /* reading - 41 cycles */ for (i = 0; i < GPIOTHS_DHT_CYCLES; i++) { - err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_HIGH, - &calibrations[i]); + err = gpioths_dht_timeuntil(sc, true, &calibrations[i]); if (err) { - device_printf(dev, "err(CAL, %d) = %d\n", i, err); + device_printf(sc->dev, "err(CAL, %d) = %d\n", i, err); goto error; } - err = gpioths_dht_timeuntil(bus, dev, GPIO_PIN_LOW, - &intervals[i]); + err = gpioths_dht_timeuntil(sc, false, &intervals[i]); if (err) { - device_printf(dev, "err(INTERVAL, %d) = %d\n", i, err); + device_printf(sc->dev, "err(INTERVAL, %d) = %d\n", i, err); goto error; } } - err = GPIOBUS_PIN_SETFLAGS(bus, dev, 0, GPIO_PIN_OUTPUT); - if (err != 0) { - device_printf(dev, "err(FINAL_SETFLAGS, OUT) = %d\n", err); - goto error; - } - DELAY(1); - /* Calculate average data calibration cycle length */ avglen = 0; for (i = 1; i < GPIOTHS_DHT_CYCLES; i++) @@ -252,9 +233,46 @@ gpioths_dht_readbytes(device_t bus, device_t dev) goto error; } + /* + * For DHT11/12, the values are split into 8 bits of integer and 8 bits + * of fractional tenths. On DHT11 the fraction bytes are always zero. + * On DHT12 the sign bit is in the high bit of the fraction byte. + * - DHT11: 0HHHHHHH 00000000 00TTTTTT 00000000 + * - DHT12: 0HHHHHHH 0000hhhh 00TTTTTT s000tttt + * + * For DHT21/21, the values are are encoded in 16 bits each, with the + * temperature sign bit in the high bit. The values are tenths of a + * degree C and tenths of a percent RH. + * - DHT21: 000000HH HHHHHHHH s00000TT TTTTTTTT + * - DHT22: 000000HH HHHHHHHH s00000TT TTTTTTTT + * + * For all devices, some bits are always zero because of the range of + * values supported by the device. + * + * We figure out how to decode things based on the high byte of the + * humidity. A DHT21/22 cannot report a value greater than 3 in + * the upper bits of its 16-bit humidity. A DHT11/12 should not report + * a value lower than 20. To allow for the possibility that a device + * could report a value slightly out of its sensitivity range, we split + * the difference and say if the value is greater than 10 it must be a + * DHT11/12 (that would be a humidity over 256% on a DHT21/22). + */ +#define DK_OFFSET 2731 /* Offset between K and C, in decikelvins. */ + if ((value >> 24) > 10) { + /* DHT11 or DHT12 */ + tmphi = (value >> 8) & 0x3f; + tmplo = value & 0x0f; + negmul = (value & 0x80) ? -1 : 1; + sc->temp = DK_OFFSET + (negmul * (tmphi * 10 + tmplo)); + sc->hum = (value >> 24) & 0x7f; + } else { + /* DHT21 or DHT22 */ + negmul = (value & 0x8000) ? -1 : 1; + sc->temp = DK_OFFSET + (negmul * (value & 0x03ff)); + sc->hum = ((value >> 16) & 0x03ff) / 10; + } + sc->fails = 0; - sc->temp = (value >> GPIOTHS_DHT_TEMP_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK; - sc->hum = (value >> GPIOTHS_DHT_HUM_SHIFT) & GPIOTHS_DHT_ONEBYTEMASK; #ifdef GPIOTHS_DEBUG /* Debug bits */ @@ -269,61 +287,25 @@ error: } static void -gpioths_poll(void *arg) +gpioths_poll(void *arg, int pending __unused) { struct gpioths_softc *sc; - device_t dev; - dev = (device_t)arg; - sc = device_get_softc(dev); + sc = (struct gpioths_softc *)arg; - gpioths_dht_readbytes(device_get_parent(dev), dev); - callout_schedule(&sc->callout, GPIOTHS_POLLTIME * hz); + gpioths_dht_readbytes(sc); + if (!sc->detaching) + taskqueue_enqueue_timeout_sbt(taskqueue_thread, &sc->task, + GPIOTHS_POLLTIME * SBT_1S, 0, C_PREL(3)); } static int -gpioths_temp_sysctl(SYSCTL_HANDLER_ARGS) -{ - struct gpioths_softc *sc; - int value; - - sc = (struct gpioths_softc*)arg1; - value = sc->temp; - - return (sysctl_handle_int(oidp, &value, 0, req)); -} - -static int -gpioths_hum_sysctl(SYSCTL_HANDLER_ARGS) -{ - struct gpioths_softc *sc; - int value; - - sc = (struct gpioths_softc*)arg1; - value = sc->hum; - - return (sysctl_handle_int(oidp, &value, 0, req)); -} - - -static int -gpioths_fails_sysctl(SYSCTL_HANDLER_ARGS) -{ - struct gpioths_softc *sc; - int value; - - sc = (struct gpioths_softc*)arg1; - value = sc->fails; - - return (sysctl_handle_int(oidp, &value, 0, req)); -} - -static int gpioths_attach(device_t dev) { struct gpioths_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; + int err; sc = device_get_softc(dev); ctx = device_get_sysctl_ctx(dev); @@ -331,66 +313,88 @@ gpioths_attach(device_t dev) sc->dev = dev; - callout_init(&sc->callout, 1); - callout_reset(&sc->callout, GPIOTHS_POLLTIME * hz, gpioths_poll, dev); + TIMEOUT_TASK_INIT(taskqueue_thread, &sc->task, 0, gpioths_poll, sc); - sc->temp_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, - "temperature", CTLTYPE_INT | CTLFLAG_RD, sc, 0, - gpioths_temp_sysctl, "I", "temperature(C)"); +#ifdef FDT + /* Try to configure our pin from fdt data on fdt-based systems. */ + err = gpio_pin_get_by_ofw_idx(dev, ofw_bus_get_node(dev), PIN_IDX, + &sc->pin); +#else + err = ENOENT; +#endif + /* + * If we didn't get configured by fdt data and our parent is gpiobus, + * see if we can be configured by the bus (allows hinted attachment even + * on fdt-based systems). + */ + if (err != 0 && + strcmp("gpiobus", device_get_name(device_get_parent(dev))) == 0) + err = gpio_pin_get_by_child_index(dev, PIN_IDX, &sc->pin); - sc->hum_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, - "humidity", CTLTYPE_INT | CTLFLAG_RD, sc, 0, - gpioths_hum_sysctl, "I", "humidity(%)"); + /* If we didn't get configured by either method, whine and punt. */ + if (err != 0) { + device_printf(sc->dev, + "cannot acquire gpio pin (config error)\n"); + return (err); + } - sc->fails_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, - "fails", CTLTYPE_INT | CTLFLAG_RD, sc, 0, - gpioths_fails_sysctl, "I", "fails since last successful read"); + /* + * Ensure we have control of our pin, and preset the data line to its + * idle condition (high). Leave the line in input mode, relying on the + * external pullup to keep the line high while idle. + */ + err = gpio_pin_setflags(sc->pin, GPIO_PIN_OUTPUT); + if (err != 0) { + device_printf(dev, "gpio_pin_setflags(OUT) = %d\n", err); + return (err); + } + err = gpio_pin_set_active(sc->pin, true); + if (err != 0) { + device_printf(dev, "gpio_pin_set_active(false) = %d\n", err); + return (err); + } + err = gpio_pin_setflags(sc->pin, GPIO_PIN_INPUT); + if (err != 0) { + device_printf(dev, "gpio_pin_setflags(IN) = %d\n", err); + return (err); + } + /* + * Do an initial read so we have correct values for reporting before + * registering the sysctls that can access those values. This also + * schedules the periodic polling the driver does every few seconds to + * update the sysctl variables. + */ + gpioths_poll(sc, 0); + + sysctl_add_oid(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "temperature", \ + CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE, + &sc->temp, 0, sysctl_handle_int, "IK", "temperature", NULL); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "humidity", + CTLFLAG_RD, &sc->hum, 0, "relative humidity(%)"); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "fails", + CTLFLAG_RD, &sc->fails, 0, + "failures since last successful read"); + return (0); } static int gpioths_detach(device_t dev) { + struct gpioths_softc *sc; + sc = device_get_softc(dev); + gpio_pin_release(sc->pin); + sc->detaching = true; + while (taskqueue_cancel_timeout(taskqueue_thread, &sc->task, NULL) != 0) + taskqueue_drain_timeout(taskqueue_thread, &sc->task); + return (0); } -/* DDB bits */ -#include "opt_ddb.h" -#ifdef DDB -#include -#include -#include - -static struct command_table db_gpioths_table = LIST_HEAD_INITIALIZER(db_t4_table); -_DB_SET(_show, gpioths, NULL, db_show_table, 0, &db_gpioths_table); - -DB_FUNC(read, db_show_gpiothsread, db_gpioths_table, CS_OWN, NULL) -{ - device_t dev; - int t; - int init; - - init = 0; - t = db_read_token(); - if (t == tIDENT) { - dev = device_lookup_by_name(db_tok_string); - init = 1; - } - - db_skip_to_eol(); - - if (init) - db_printf("read: 0x%x\n", - gpioths_dht_readbytes(dev, device_get_parent(dev))); - else - db_printf("usage: show gpioths read \n"); - -return; -} -#endif /* DDB */ - /* Driver bits */ static device_method_t gpioths_methods[] = { /* Device interface */ @@ -401,5 +405,13 @@ static device_method_t gpioths_methods[] = { DEVMETHOD_END }; +static devclass_t gpioths_devclass; + DEFINE_CLASS_0(gpioths, gpioths_driver, gpioths_methods, sizeof(struct gpioths_softc)); + +#ifdef FDT +DRIVER_MODULE(gpioths, simplebus, gpioths_driver, gpioths_devclass, 0, 0); +#endif + DRIVER_MODULE(gpioths, gpiobus, gpioths_driver, gpioths_devclass, 0, 0); +MODULE_DEPEND(gpioths, gpiobus, 1, 1, 1); Modified: stable/12/sys/modules/gpio/Makefile ============================================================================== --- stable/12/sys/modules/gpio/Makefile Sun Dec 22 18:11:57 2019 (r356017) +++ stable/12/sys/modules/gpio/Makefile Sun Dec 22 18:51:05 2019 (r356018) @@ -25,7 +25,7 @@ # SUCH DAMAGE. # -SUBDIR = gpiobus gpioiic gpioled gpiospi +SUBDIR = gpiobus gpioiic gpioled gpiospi gpioths .if !empty(OPT_FDT) SUBDIR += gpiokeys gpiopps