svn commit: r355540 - head/sys/dev/gpio
Ian Lepore
ian at FreeBSD.org
Mon Dec 9 00:30:06 UTC 2019
Author: ian
Date: Mon Dec 9 00:30:05 2019
New Revision: 355540
URL: https://svnweb.freebsd.org/changeset/base/355540
Log:
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.
Modified:
head/sys/dev/gpio/gpioths.c
Modified: head/sys/dev/gpio/gpioths.c
==============================================================================
--- head/sys/dev/gpio/gpioths.c Sun Dec 8 22:36:37 2019 (r355539)
+++ head/sys/dev/gpio/gpioths.c Mon Dec 9 00:30:05 2019 (r355540)
@@ -24,28 +24,12 @@
* SUCH DAMAGE.
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/kernel.h>
-#include <sys/bus.h>
-#include <sys/module.h>
-#include <sys/errno.h>
-#include <sys/systm.h>
-#include <sys/sysctl.h>
-
-#include <machine/bus.h>
-#include <sys/rman.h>
-#include <sys/gpio.h>
-#include <machine/resource.h>
-
-#include "gpiobus_if.h"
-
/*
- * GPIOTHS - Temp/Humidity sensor over GPIO, e.g. DHT11/DHT22
+ * 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 uses a custom single-wire protocol, it is not One-wire(tm).
*
* This driver supports the following chips:
* DHT11: Temp 0c to 50c +-2.0c, Humidity 20% to 90% +-5%
@@ -59,8 +43,38 @@ __FBSDID("$FreeBSD$");
* 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 <dev/gpio/gpiobusvar.h>
+
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+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 */
@@ -70,40 +84,44 @@ __FBSDID("$FreeBSD$");
struct gpioths_softc {
device_t dev;
+ gpio_pin_t pin;
int temp;
int hum;
int fails;
struct callout callout;
};
-/* 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);
-
-/* 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;
@@ -116,93 +134,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.
*/
+ gpio_pin_setflags(sc->pin, GPIO_PIN_OUTPUT);
+ gpio_pin_set_active(sc->pin, false);
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_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, 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_INPUT);
- if (err != 0) {
- device_printf(dev, "err(FINAL_SETFLAGS, IN) = %d\n", err);
- goto error;
- }
- DELAY(1);
-
/* Calculate average data calibration cycle length */
avglen = 0;
for (i = 1; i < GPIOTHS_DHT_CYCLES; i++)
@@ -271,8 +250,8 @@ gpioths_dht_readbytes(device_t bus, device_t dev)
* 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 cannot be a
- * DHT22 (that would be a humidity over 256%).
+ * 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) {
@@ -307,12 +286,10 @@ static void
gpioths_poll(void *arg)
{
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);
+ gpioths_dht_readbytes(sc);
callout_schedule(&sc->callout, GPIOTHS_POLLTIME * hz);
}
@@ -322,6 +299,7 @@ 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);
@@ -329,11 +307,55 @@ gpioths_attach(device_t dev)
sc->dev = dev;
+#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);
+
+ /* 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);
+ }
+
+ /*
+ * 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.
*/
- gpioths_dht_readbytes(device_get_parent(dev), dev);
+ gpioths_dht_readbytes(sc);
sysctl_add_oid(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "temperature", \
CTLFLAG_RD | CTLTYPE_INT | CTLFLAG_MPSAFE,
@@ -347,7 +369,7 @@ gpioths_attach(device_t dev)
"failures since last successful read");
callout_init(&sc->callout, 1);
- callout_reset(&sc->callout, GPIOTHS_POLLTIME * hz, gpioths_poll, dev);
+ callout_reset(&sc->callout, GPIOTHS_POLLTIME * hz, gpioths_poll, sc);
return (0);
}
@@ -358,7 +380,7 @@ gpioths_detach(device_t dev)
struct gpioths_softc *sc;
sc = device_get_softc(dev);
-
+ gpio_pin_release(sc->pin);
callout_drain(&sc->callout);
return (0);
@@ -377,5 +399,10 @@ static device_method_t gpioths_methods[] = {
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);
More information about the svn-src-head
mailing list