1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-13 14:40:22 +00:00

Update owc_gpiobus (one-wire over gpio) to the modern gpio_pin interface.

It used to be required that a device be a child of gpiobus(4) to manipulate
gpio pins. That requirement didn't work well for FDT-based systems with many
cross-hierarchy users of gpio, so a more modern framework was created that
removed the old hierarchy requirement.

These changes adapt the owc_gpiobus driver to use the newer gpio_pin_*
functions to acquire, release, and manipulate gpio pins. This allows a
single driver to work for both hinted-attachment and fdt-based systems, and
removes the requirement that any one-wire fdt nodes must appear at the root
of the devicetree.

Differential Revision:	https://reviews.freebsd.org/D22710
This commit is contained in:
Ian Lepore 2019-12-17 15:56:48 +00:00
parent 98d97cdec7
commit c7b0edf219
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=355858

View File

@ -38,16 +38,20 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/mutex.h>
#include <dev/gpio/gpiobusvar.h>
#include <dev/ow/owll.h>
#ifdef FDT
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#endif
#include <dev/gpio/gpiobusvar.h>
#include "gpiobus_if.h"
#include <dev/ow/owll.h>
static struct ofw_compat_data compat_data[] = {
{"w1-gpio", true},
{NULL, false}
};
OFWBUS_PNP_INFO(compat_data);
SIMPLEBUS_PNP_INFO(compat_data);
#endif /* FDT */
#define OW_PIN 0
@ -61,7 +65,7 @@ __FBSDID("$FreeBSD$");
struct owc_gpiobus_softc
{
device_t sc_dev;
device_t sc_busdev;
gpio_pin_t sc_pin;
struct mtx sc_mtx;
};
@ -69,68 +73,69 @@ static int owc_gpiobus_probe(device_t);
static int owc_gpiobus_attach(device_t);
static int owc_gpiobus_detach(device_t);
#ifdef FDT
static void
owc_gpiobus_identify(driver_t *driver, device_t bus)
{
phandle_t w1, root;
/*
* Find all the 1-wire bus pseudo-nodes that are
* at the top level of the FDT. Would be nice to
* somehow preserve the node name of these busses,
* but there's no good place to put it. The driver's
* name is used for the device name, and the 1-wire
* bus overwrites the description.
*/
root = OF_finddevice("/");
if (root == -1)
return;
for (w1 = OF_child(root); w1 != 0; w1 = OF_peer(w1)) {
if (!fdt_is_compatible_strict(w1, "w1-gpio"))
continue;
if (!OF_hasprop(w1, "gpios"))
continue;
ofw_gpiobus_add_fdt_child(bus, driver->name, w1);
}
}
#endif
static int
owc_gpiobus_probe(device_t dev)
{
int rv;
/*
* By default we only bid to attach if specifically added by our parent
* (usually via hint.owc_gpiobus.#.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))
return (ENXIO);
if (ofw_bus_is_compatible(dev, "w1-gpio")) {
device_set_desc(dev, "FDT GPIO attached one-wire bus");
return (BUS_PROBE_DEFAULT);
}
return (ENXIO);
#else
device_set_desc(dev, "GPIO attached one-wire bus");
return 0;
if (ofw_bus_status_okay(dev) &&
ofw_bus_search_compatible(dev, compat_data)->ocd_data)
rv = BUS_PROBE_DEFAULT;
#endif
device_set_desc(dev, "GPIO one-wire bus");
return (rv);
}
static int
owc_gpiobus_attach(device_t dev)
{
struct owc_gpiobus_softc *sc;
device_t *kids;
int nkid;
int err;
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->sc_busdev = device_get_parent(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), OW_PIN,
&sc->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, OW_PIN, &sc->sc_pin);
/* If we didn't get configured by either method, whine and punt. */
if (err != 0) {
device_printf(sc->sc_dev,
"cannot acquire gpio pin (config error)\n");
return (err);
}
OWC_GPIOBUS_LOCK_INIT(sc);
nkid = 0;
if (device_get_children(dev, &kids, &nkid) == 0)
free(kids, M_TEMP);
if (nkid == 0)
device_add_child(dev, "ow", -1);
/*
* Add the ow bus as a child, but defer probing and attaching it until
* interrupts work, because we can't do IO for them until we can read
* the system timecounter (which initializes after device attachments).
*/
device_add_child(sc->sc_dev, "ow", -1);
return (bus_delayed_attach_children(dev));
}
@ -138,10 +143,16 @@ static int
owc_gpiobus_detach(device_t dev)
{
struct owc_gpiobus_softc *sc;
int err;
sc = device_get_softc(dev);
if ((err = device_delete_children(dev)) != 0)
return (err);
gpio_pin_release(sc->sc_pin);
OWC_GPIOBUS_LOCK_DESTROY(sc);
bus_generic_detach(dev);
return (0);
}
@ -154,18 +165,10 @@ owc_gpiobus_detach(device_t dev)
* These macros let what why we're doing stuff shine in the code
* below, and let the how be confined to here.
*/
#define GETBUS(sc) GPIOBUS_ACQUIRE_BUS((sc)->sc_busdev, \
(sc)->sc_dev, GPIOBUS_WAIT)
#define RELBUS(sc) GPIOBUS_RELEASE_BUS((sc)->sc_busdev, \
(sc)->sc_dev)
#define OUTPIN(sc) GPIOBUS_PIN_SETFLAGS((sc)->sc_busdev, \
(sc)->sc_dev, OW_PIN, GPIO_PIN_OUTPUT)
#define INPIN(sc) GPIOBUS_PIN_SETFLAGS((sc)->sc_busdev, \
(sc)->sc_dev, OW_PIN, GPIO_PIN_INPUT)
#define GETPIN(sc, bit) GPIOBUS_PIN_GET((sc)->sc_busdev, \
(sc)->sc_dev, OW_PIN, bit)
#define LOW(sc) GPIOBUS_PIN_SET((sc)->sc_busdev, \
(sc)->sc_dev, OW_PIN, GPIO_PIN_LOW)
#define OUTPIN(sc) gpio_pin_setflags((sc)->sc_pin, GPIO_PIN_OUTPUT)
#define INPIN(sc) gpio_pin_setflags((sc)->sc_pin, GPIO_PIN_INPUT)
#define GETPIN(sc, bp) gpio_pin_is_active((sc)->sc_pin, (bp))
#define LOW(sc) gpio_pin_set_active((sc)->sc_pin, false)
/*
* WRITE-ONE (see owll_if.m for timings) From Figure 4-1 AN-937
@ -183,12 +186,8 @@ static int
owc_gpiobus_write_one(device_t dev, struct ow_timing *t)
{
struct owc_gpiobus_softc *sc;
int error;
sc = device_get_softc(dev);
error = GETBUS(sc);
if (error != 0)
return (error);
critical_enter();
@ -203,8 +202,6 @@ owc_gpiobus_write_one(device_t dev, struct ow_timing *t)
critical_exit();
RELBUS(sc);
return (0);
}
@ -224,12 +221,8 @@ static int
owc_gpiobus_write_zero(device_t dev, struct ow_timing *t)
{
struct owc_gpiobus_softc *sc;
int error;
sc = device_get_softc(dev);
error = GETBUS(sc);
if (error != 0)
return (error);
critical_enter();
@ -244,8 +237,6 @@ owc_gpiobus_write_zero(device_t dev, struct ow_timing *t)
critical_exit();
RELBUS(sc);
return (0);
}
@ -268,13 +259,10 @@ static int
owc_gpiobus_read_data(device_t dev, struct ow_timing *t, int *bit)
{
struct owc_gpiobus_softc *sc;
int error, sample;
bool sample;
sbintime_t then, now;
sc = device_get_softc(dev);
error = GETBUS(sc);
if (error != 0)
return (error);
critical_enter();
@ -293,7 +281,7 @@ owc_gpiobus_read_data(device_t dev, struct ow_timing *t, int *bit)
do {
now = sbinuptime();
GETPIN(sc, &sample);
} while (now - then < (t->t_rdv + 2) * SBT_1US && sample == 0);
} while (now - then < (t->t_rdv + 2) * SBT_1US && sample == false);
critical_exit();
if (now - then < t->t_rdv * SBT_1US)
@ -306,9 +294,7 @@ owc_gpiobus_read_data(device_t dev, struct ow_timing *t, int *bit)
now = sbinuptime();
} while (now - then < (t->t_slot + t->t_rec) * SBT_1US);
RELBUS(sc);
return (error);
return (0);
}
/*
@ -324,32 +310,31 @@ owc_gpiobus_read_data(device_t dev, struct ow_timing *t, int *bit)
* |<tPDH>|
*
* Note: for Regular Speed operations, tRSTL + tR should be less than 960us to
* avoid interferring with other devices on the bus
* avoid interfering with other devices on the bus.
*
* Return values in *bit:
* -1 = Bus wiring error (stuck low).
* 0 = no presence pulse
* 1 = presence pulse detected
*/
static int
owc_gpiobus_reset_and_presence(device_t dev, struct ow_timing *t, int *bit)
{
struct owc_gpiobus_softc *sc;
int error;
int buf = -1;
bool sample;
sc = device_get_softc(dev);
error = GETBUS(sc);
if (error != 0)
return (error);
/*
* Read the current state of the bus. The steady state of an idle bus is
* high. Badly wired buses that are missing the required pull up, or
* that have a short circuit to ground cause all kinds of mischief when
* we try to read them later. Return EIO and release the bus if the bus
* is currently low.
* we try to read them later. Return EIO if the bus is currently low.
*/
INPIN(sc);
GETPIN(sc, &buf);
if (buf == 0) {
GETPIN(sc, &sample);
if (sample == false) {
*bit = -1;
RELBUS(sc);
return (EIO);
}
@ -365,8 +350,8 @@ owc_gpiobus_reset_and_presence(device_t dev, struct ow_timing *t, int *bit)
DELAY(t->t_pdh + t->t_pdl / 2);
/* Read presence pulse */
GETPIN(sc, &buf);
*bit = !!buf;
GETPIN(sc, &sample);
*bit = sample;
critical_exit();
@ -377,15 +362,12 @@ owc_gpiobus_reset_and_presence(device_t dev, struct ow_timing *t, int *bit)
* window. It should return to high. If it is low, then we have some
* problem and should abort the reset.
*/
GETPIN(sc, &buf);
if (buf == 0) {
GETPIN(sc, &sample);
if (sample == false) {
*bit = -1;
RELBUS(sc);
return (EIO);
}
RELBUS(sc);
return (0);
}
@ -393,9 +375,6 @@ static devclass_t owc_gpiobus_devclass;
static device_method_t owc_gpiobus_methods[] = {
/* Device interface */
#ifdef FDT
DEVMETHOD(device_identify, owc_gpiobus_identify),
#endif
DEVMETHOD(device_probe, owc_gpiobus_probe),
DEVMETHOD(device_attach, owc_gpiobus_attach),
DEVMETHOD(device_detach, owc_gpiobus_detach),
@ -413,6 +392,10 @@ static driver_t owc_gpiobus_driver = {
sizeof(struct owc_gpiobus_softc),
};
#ifdef FDT
DRIVER_MODULE(owc_gpiobus, simplebus, owc_gpiobus_driver, owc_gpiobus_devclass, 0, 0);
#endif
DRIVER_MODULE(owc_gpiobus, gpiobus, owc_gpiobus_driver, owc_gpiobus_devclass, 0, 0);
MODULE_DEPEND(owc_gpiobus, ow, 1, 1, 1);
MODULE_DEPEND(owc_gpiobus, gpiobus, 1, 1, 1);