Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 19 Aug 2012 09:38:52 -0600
From:      Warner Losh <imp@bsdimp.com>
To:        Hiroki Sato <hrs@FreeBSD.org>
Cc:        gonzo@FreeBSD.org, freebsd-arm@FreeBSD.org
Subject:   Re: gpiobus_hinted_child >32 pins support, pin_getname method, and gpio-sysctl bridge patch
Message-ID:  <8CDAB51C-14A0-42F0-8E16-43A3EABA2703@bsdimp.com>
In-Reply-To: <20120819.171723.523519054460575158.hrs@allbsd.org>
References:  <20120819.171723.523519054460575158.hrs@allbsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help

On Aug 19, 2012, at 2:17 AM, Hiroki Sato wrote:

> Hi,

Hi Sato-san,

In general, I like this code in the context of the current GPIO =
framework.  I've been growing dissatisfied with the current GPIO =
framework, however, and some of my comments reflect that more than any =
comments about this specific code.

> Can you review the attached patches?  The first one is to add >32 pins
> support for hint.devname.%d.pins and pin_getname method into gpiobus
> similar to gpio interface.  After applying this, the following hint
> will be supported:
>=20
> hint.gpioled.0.at=3D"gpiobus0"
> hint.gpioled.0.pins0=3D"0x00000000" # 41
> hint.gpioled.0.pins1=3D"0x00000200"

This is, at best, awkward.  Why not '41' here?

Why not hints.gpioled.0.pins0=3D41?

In general, the gpio stuff is too mask heavy and should be more pin =
oriented.  We also need a more generic way to pass around all the GPIO =
pins used for aux functions of embedded devices.  which pin is used for =
media status interrupts, write protect detection, vcc power, etc.

Looks a lot like we already keep book internally in pins.

> hint.gpioled.0.pins can still be used when the pin number is lower
> than 32.  The addition of pin_getname method is to reuse pin name if
> it is already defined in the parent gpio driver.  The maximum of the
> pin number is limited by GPIOBUS_PINS_MAX (currently 128).
>=20
> Another one is a simple GPIO-sysctl bridge driver.  Currently it
> supports read operation only, but it is easy to add write one.

I was rather hoping to recast much of the GPIO stuff so we can also =
support interrupts based on pin numbers for any device in the tree.  I'd =
like this for a gpiokey generic interface that devices can register =
button presses.  How does one configure in vs out for these pins?  And =
how does one guard against trying to use the GPIO pins for things like =
ethernet?  Again, that may be beyond the scope of what you are trying to =
achieve.

> I confirmed if the both work fine by using several Marvell
> 88F628[12]-based boards.
>=20
> -- Hiroki
> Index: sys/dev/gpio/gpiobus.c
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> --- sys/dev/gpio/gpiobus.c	(revision 239381)
> +++ sys/dev/gpio/gpiobus.c	(working copy)
> @@ -40,6 +40,7 @@
> #include <machine/bus.h>
> #include <sys/rman.h>
> #include <machine/resource.h>
> +#include <machine/_inttypes.h>
>=20
> #include <sys/gpio.h>
> #include <dev/gpio/gpiobusvar.h>
> @@ -47,7 +48,7 @@
> #include "gpiobus_if.h"
>=20
> static void gpiobus_print_pins(struct gpiobus_ivar *);
> -static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int);
> +static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int =
*, size_t);
> static int gpiobus_probe(device_t);
> static int gpiobus_attach(device_t);
> static int gpiobus_detach(device_t);
> @@ -69,6 +70,7 @@
> static int gpiobus_pin_setflags(device_t, device_t, uint32_t, =
uint32_t);
> static int gpiobus_pin_getflags(device_t, device_t, uint32_t, =
uint32_t*);
> static int gpiobus_pin_getcaps(device_t, device_t, uint32_t, =
uint32_t*);
> +static int gpiobus_pin_getname(device_t, device_t, uint32_t, char *);
> static int gpiobus_pin_set(device_t, device_t, uint32_t, unsigned =
int);
> static int gpiobus_pin_get(device_t, device_t, uint32_t, unsigned =
int*);
> static int gpiobus_pin_toggle(device_t, device_t, uint32_t);
> @@ -119,15 +121,18 @@
> }
>=20
> static int
> -gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int =
mask)
> +gpiobus_parse_pins(struct gpiobus_softc *sc, device_t child, int =
*mask,
> +    size_t masklen)

I'd be tempted to adapt a different specification method for > 32bit =
devices.  But that may be beyond the scope of what you are trying to do.

Might not be bad to document the args, but that, sadly, doesn't follow =
the rest of the file...

> {
> 	struct gpiobus_ivar *devi =3D GPIOBUS_IVAR(child);
> -	int i, npins;
> +	int i, j, pin, npins;
>=20
> 	npins =3D 0;
> -	for (i =3D 0; i < 32; i++) {
> -		if (mask & (1 << i))
> -			npins++;
> +	for (i =3D 0; i < masklen; i++) {
> +		for (j =3D 0; j < sizeof(mask[i]) * 8; j++) {
> +			if (mask[i] & (1 << j))
> +				npins++;
> +		}
> 	}
>=20
> 	if (npins =3D=3D 0) {
> @@ -143,27 +148,30 @@
> 		return (ENOMEM);
>=20
> 	npins =3D 0;
> -	for (i =3D 0; i < 32; i++) {
> +	for (i =3D 0; i < masklen; i++) {
> +		for (j =3D 0; j < sizeof(mask[i]) * 8; j++) {
> +			if ((mask[i] & (1 << j)) =3D=3D 0)
> +				continue;
>=20
> -		if ((mask & (1 << i)) =3D=3D 0)
> -			continue;
> -
> -		if (i >=3D sc->sc_npins) {
> -			device_printf(child,
> -			    "invalid pin %d, max: %d\n", i, sc->sc_npins =
- 1);
> -			return (EINVAL);
> +			pin =3D i * sizeof(mask[i]) * 8 + j;
> +			if (pin >=3D sc->sc_npins) {
> +				device_printf(child,
> +				    "invalid pin %d, max: %d\n", pin,
> +				    sc->sc_npins - 1);
> +				return (EINVAL);
> +			}
> +			devi->pins[npins++] =3D pin;
> +			/*
> +			 * Mark pin as mapped and give warning if it's
> +			 * already mapped
> +			 */
> +			if (sc->sc_pins_mapped[pin]) {
> +				device_printf(child,
> +				    "warning: pin %d is already =
mapped\n", pin);
> +				return (EINVAL);
> +			}
> +			sc->sc_pins_mapped[pin] =3D 1;
> 		}
> -
> -		devi->pins[npins++] =3D i;
> -		/*
> -		 * Mark pin as mapped and give warning if it's already =
mapped
> -		 */
> -		if (sc->sc_pins_mapped[i]) {
> -			device_printf(child,
> -			    "warning: pin %d is already mapped\n", i);
> -			return (EINVAL);
> -		}
> -		sc->sc_pins_mapped[i] =3D 1;
> 	}
>=20
> 	return (0);
> @@ -192,6 +200,8 @@
> 	 * Increase to get number of pins
> 	 */
> 	sc->sc_npins++;
> +	if (bootverbose)
> +		device_printf(dev, "number of pins =3D %d\n", =
sc->sc_npins);
>=20
> 	KASSERT(sc->sc_npins !=3D 0, ("GPIO device with no pins"));
>=20
> @@ -304,19 +314,47 @@
> 	return (child);
> }
>=20
> +#define	GPIOBUS_PINS_MAX	128
> +static int pinmask[(GPIOBUS_PINS_MAX - 1) / (sizeof(int) * 8) + 1];
> +static char pinresname[] =3D "pinsNNN";
> +
> static void
> gpiobus_hinted_child(device_t bus, const char *dname, int dunit)
> {
> 	struct gpiobus_softc *sc =3D GPIOBUS_SOFTC(bus);
> 	struct gpiobus_ivar *devi;
> 	device_t child;
> -	int pins;
> +	int i, error;
>=20
> +	bzero((char *)&pinmask, sizeof(pinmask));
>=20
> 	child =3D BUS_ADD_CHILD(bus, 0, dname, dunit);
> 	devi =3D GPIOBUS_IVAR(child);
> -	resource_int_value(dname, dunit, "pins", &pins);
> -	if (gpiobus_parse_pins(sc, child, pins))
> +	if (resource_int_value(dname, dunit, "pins", &pinmask[0]) =3D=3D =
0) {
> +		/*
> +		 * Backward compatibility for 32-pin configuration in a
> +		 * 32-bit hint:
> +		 *
> +		 * hint.devname.%d.pins=3D0x00000000
> +		 */
> +		i =3D 1;
> +	} else {
> +		/*
> +		 * A 32-bit hint configures a 32-pin block:
> +		 *
> +		 * hint.devname.%d.pins0=3D0x00000000	#  0..31
> +		 * hint.devname.%d.pins1=3D0x00000000	# 32..63
> +		 * hint.devname.%d.pins2=3D0x00000000	# 64..91
> +		 */
> +		for (i =3D 0; i < sizeof(pinmask)/sizeof(pinmask[0]); =
i++) {
> +			snprintf(pinresname, sizeof(pinresname), =
"pins%d", i);
> +			error =3D resource_int_value(dname, dunit, =
pinresname,
> +			     &pinmask[i]);
> +			if (error)
> +				break;
> +		}
> +	}
> +	if (gpiobus_parse_pins(sc, child, pinmask, i))
> 		device_delete_child(bus, child);
> }
>=20
> @@ -409,6 +447,18 @@
> }
>=20
> static int
> +gpiobus_pin_getname(device_t dev, device_t child, uint32_t pin, char =
*name)
> +{
> +	struct gpiobus_softc *sc =3D GPIOBUS_SOFTC(dev);
> +	struct gpiobus_ivar *devi =3D GPIOBUS_IVAR(child);
> +
> +	if (pin >=3D devi->npins)
> +		return (EINVAL);
> +
> +	return GPIO_PIN_GETNAME(sc->sc_dev, devi->pins[pin], name);
> +}
> +
> +static int
> gpiobus_pin_set(device_t dev, device_t child, uint32_t pin,
>     unsigned int value)
> {
> @@ -469,6 +519,7 @@
> 	DEVMETHOD(gpiobus_release_bus,	gpiobus_release_bus),
> 	DEVMETHOD(gpiobus_pin_getflags,	gpiobus_pin_getflags),
> 	DEVMETHOD(gpiobus_pin_getcaps,	gpiobus_pin_getcaps),
> +	DEVMETHOD(gpiobus_pin_getname,	gpiobus_pin_getname),
> 	DEVMETHOD(gpiobus_pin_setflags,	gpiobus_pin_setflags),
> 	DEVMETHOD(gpiobus_pin_get,	gpiobus_pin_get),
> 	DEVMETHOD(gpiobus_pin_set,	gpiobus_pin_set),
> Index: sys/dev/gpio/gpiobus_if.m
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> --- sys/dev/gpio/gpiobus_if.m	(revision 239381)
> +++ sys/dev/gpio/gpiobus_if.m	(working copy)
> @@ -111,6 +111,16 @@
> };
>=20
> #
> +# Get pin name
> +#
> +METHOD int pin_getname {
> +	device_t dev;
> +	device_t child;
> +	uint32_t pin_num;
> +	char *name;
> +};
> +
> +#
> # Set current configuration and capabilities
> #
> METHOD int pin_setflags {
> Index: sys/dev/gpio/gpioled.c
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> --- sys/dev/gpio/gpioled.c	(revision 239381)
> +++ sys/dev/gpio/gpioled.c	(working copy)
> @@ -94,20 +94,25 @@
> {
> 	struct gpioled_softc *sc;
> 	const char *name;
> +	char nbuf[GPIOMAXNAME];
>=20
> 	sc =3D device_get_softc(dev);
> 	sc->sc_dev =3D dev;
> 	sc->sc_busdev =3D device_get_parent(dev);
> 	GPIOLED_LOCK_INIT(sc);
> 	if (resource_string_value(device_get_name(dev),
> -	    device_get_unit(dev), "name", &name))
> -		name =3D NULL;
> -
> +	    device_get_unit(dev), "name", &name) !=3D 0) {
> +		GPIOBUS_PIN_GETNAME(sc->sc_busdev, sc->sc_dev, 0, nbuf);
> +		nbuf[sizeof(nbuf) - 1] =3D '\0';
> +		if (nbuf[0] !=3D '\0')
> +			name =3D nbuf;
> +		else
> +			name =3D device_get_nameunit(dev);
> +	}
> 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, GPIOLED_PIN,
> 	    GPIO_PIN_OUTPUT);
>=20
> -	sc->sc_leddev =3D led_create(gpioled_control, sc, name ? name :
> -	    device_get_nameunit(dev));
> +	sc->sc_leddev =3D led_create(gpioled_control, sc, name);
>=20
> 	return (0);
> }
> Index: sys/conf/files
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> --- sys/conf/files	(revision 239381)
> +++ sys/conf/files	(working copy)
> @@ -1268,6 +1268,7 @@
> 	dependency	"gpio_if.h"
> dev/gpio/gpioiic.c		optional gpioiic
> dev/gpio/gpioled.c		optional gpioled
> +dev/gpio/gpiosysctl.c		optional gpiosysctl
> dev/gpio/gpio_if.m		optional gpio
> dev/gpio/gpiobus_if.m		optional gpio
> dev/hatm/if_hatm.c		optional hatm pci
> Index: sys/dev/gpio/gpiosysctl.c
> =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
> --- sys/dev/gpio/gpiosysctl.c	(revision 0)
> +++ sys/dev/gpio/gpiosysctl.c	(working copy)
> @@ -0,0 +1,197 @@
> +/*-
> + * Copyright (c) 2012 Hiroki Sato <hrs@FreeBSD.org>
> + * All rights reserved.
> + *
> + * 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.
> + */
> +
> +#include <sys/cdefs.h>
> +__FBSDID("$FreeBSD$");
> +
> +#include <sys/param.h>
> +#include <sys/systm.h>
> +#include <sys/kernel.h>
> +#include <sys/module.h>
> +#include <sys/bus.h>
> +#include <sys/sysctl.h>
> +#include <sys/lock.h>
> +#include <sys/mutex.h>
> +
> +#include <machine/fdt.h>
> +#include <dev/fdt/fdt_common.h>
> +#include <dev/ofw/ofw_bus.h>
> +#include <dev/ofw/ofw_bus_subr.h>
> +
> +#include <sys/gpio.h>
> +
> +#include "gpio_if.h"
> +#include "gpiobus_if.h"
> +
> +struct gpiosysctl_softc
> +{
> +	device_t	sc_dev;
> +	device_t	sc_busdev;
> +	struct mtx	sc_mtx;
> +};
> +
> +#define	GPIO_SYSCTL_DEVNAME		"gpiosysctl"
> +#define	GPIO_SYSCTL_LOCK(sc)		mtx_lock(&(sc)->sc_mtx)
> +#define	GPIO_SYSCTL_UNLOCK(sc)		=
mtx_unlock(&(sc)->sc_mtx)
> +#define	GPIO_SYSCTL_LOCK_INIT(sc)	mtx_init(&(sc)->sc_mtx,		=
\
> +					=
device_get_nameunit((sc)->sc_dev), \
> +					GPIO_SYSCTL_DEVNAME, MTX_DEF)
> +#define	GPIO_SYSCTL_LOCK_DESTROY(sc)	=
mtx_destroy(&(sc)->sc_mtx)
> +
> +static int gs_probe(device_t);
> +static int gs_attach(device_t);
> +static int gs_detach(device_t);
> +
> +static int
> +gs_probe(device_t dev)
> +{
> +
> +	device_set_desc(dev, "GPIO-sysctl bridge driver");
> +
> +	return (0);
> +}
> +
> +static int
> +gs_gpio_in(struct gpiosysctl_softc *sc)
> +{
> +	int val;
> +
> +	GPIO_SYSCTL_LOCK(sc);
> +	GPIOBUS_LOCK_BUS(sc->sc_busdev);
> +	GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
> +	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, 0, &val);
> +	GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
> +	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
> +	GPIO_SYSCTL_UNLOCK(sc);
> +
> +	return (val);
> +}
> +
> +static void
> +gs_gpio_out(struct gpiosysctl_softc *sc, int val)
> +{
> +	GPIO_SYSCTL_LOCK(sc);
> +	GPIOBUS_LOCK_BUS(sc->sc_busdev);
> +	GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
> +	GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, 0,
> +	    (val =3D=3D 0) ? GPIO_PIN_LOW : GPIO_PIN_HIGH);
> +	GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
> +	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
> +	GPIO_SYSCTL_UNLOCK(sc);
> +}
> +
> +static int
> +gs_sysctl_handler(SYSCTL_HANDLER_ARGS)
> +{
> +	device_t dev;
> +	struct gpiosysctl_softc *sc;
> +	int val, ret;
> +
> +	dev =3D (device_t)arg1;
> +	sc =3D device_get_softc(dev);
> +	if (req->newptr =3D=3D NULL) {
> +		val =3D gs_gpio_in(sc);
> +		ret =3D sysctl_handle_int(oidp, &val, 0, req);
> +	} else {
> +		ret =3D sysctl_handle_int(oidp, &val, 0, req);
> +		if (ret < 0 || ret > 1) {
> +			device_printf(dev, "%d: invalid value.\n", ret);
> +			ret =3D 0;
> +		}
> +		gs_gpio_out(sc, val);
> +		ret =3D val;
> +	}
> +
> +	return (ret);
> +}
> +
> +static int
> +gs_attach(device_t dev)
> +{
> +	struct gpiosysctl_softc *sc;
> +	struct sysctl_ctx_list *ctx;
> +	struct sysctl_oid *oid;
> +	device_t pdev;
> +	const char *name;
> +	char nbuf[GPIOMAXNAME];
> +	int flags;
> +
> +	sc =3D device_get_softc(dev);
> +	sc->sc_dev =3D dev;
> +	sc->sc_busdev =3D device_get_parent(dev);
> +	GPIO_SYSCTL_LOCK_INIT(sc);
> +
> +	if (resource_string_value(device_get_name(dev), =
device_get_unit(dev),
> +	    "name", &name) !=3D 0) {
> +		GPIOBUS_PIN_GETNAME(sc->sc_busdev, sc->sc_dev, 0, nbuf);
> +		nbuf[sizeof(nbuf) - 1] =3D '\0';
> +		if (nbuf[0] !=3D '\0')
> +			name =3D nbuf;
> +		else {
> +			device_printf(dev, "no valid name.\n");
> +			return (ENXIO);
> +		}
> +	}
> +	ctx =3D device_get_sysctl_ctx(dev);
> +
> +	pdev =3D device_get_parent(dev);
> +	oid =3D device_get_sysctl_tree(pdev);
> +	flags =3D CTLTYPE_INT | CTLFLAG_RD;
> +	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO, name, =
flags, dev,
> +	    0, gs_sysctl_handler, "I", NULL);
> +
> +	return (0);
> +}
> +
> +static int
> +gs_detach(device_t dev)
> +{
> +	struct gpiosysctl_softc *sc;
> +
> +	sc =3D device_get_softc(dev);
> +	GPIO_SYSCTL_LOCK_DESTROY(sc);
> +
> +	return (0);
> +}
> +
> +static devclass_t gs_devclass;
> +
> +static device_method_t gs_methods[] =3D {
> +	/* Device interface */
> +	DEVMETHOD(device_probe,		gs_probe),
> +	DEVMETHOD(device_attach,	gs_attach),
> +	DEVMETHOD(device_detach,	gs_detach),
> +	{ 0, 0 },
> +};
> +
> +static driver_t gs_driver =3D {
> +	GPIO_SYSCTL_DEVNAME,
> +	gs_methods,
> +	sizeof(struct gpiosysctl_softc),
> +};
> +
> +DRIVER_MODULE(gpiosysctl, gpiobus, gs_driver, gs_devclass, 0, 0);
> +MODULE_VERSION(gpiosysctl, 1);




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?8CDAB51C-14A0-42F0-8E16-43A3EABA2703>