Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 22 Dec 2019 18:51:06 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
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
Message-ID:  <201912221851.xBMIp6Fh012150@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <ian@freebsd.org>
+.\"
+.\" 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.<unit>.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.<unit>.hum
+The current relative humidity, as an integer percentage.
+.It Va dev.gpioths.<unit>.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 <dt-bindings/gpio/gpio.h>
+
+/ {
+    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.<unit>.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 <mizhka@freebsd.org>
- * All rights reserved.
+ * SPDX-License-Identifier: BSD-2-Clause
  *
+ * Copyright (c) 2016 Michael Zhilin <mizhka@freebsd.org> All rights reserved.
+ * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
+ *
  * 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=<PIN>
+ *
+ * Or configure via FDT data.
+ */
+
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/bus.h>
+#include <sys/gpio.h>
 #include <sys/module.h>
 #include <sys/errno.h>
 #include <sys/systm.h>
 #include <sys/sysctl.h>
+#include <sys/taskqueue.h>
 
-#include <machine/bus.h>
-#include <sys/rman.h>
-#include <sys/gpio.h>
-#include <machine/resource.h>
+#include <dev/gpio/gpiobusvar.h>
 
-#include "gpiobus_if.h"
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
 
-/*
- * 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=<PIN>
- */
+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 <ddb/ddb.h>
-#include <ddb/db_lex.h>
-#include <sys/cons.h>
-
-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 <gpiothsdevice>\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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201912221851.xBMIp6Fh012150>