aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuiz Otavio O Souza <loos@FreeBSD.org>2014-11-18 01:38:39 +0000
committerLuiz Otavio O Souza <loos@FreeBSD.org>2014-11-18 01:38:39 +0000
commit138bf9095329e61e64ed9fe9ee207d907ee15a67 (patch)
tree8218e996f780109209d86fab190c65a3584505cb
parentaf62c4ddd0117e474529232ea3657578ad79b0fb (diff)
downloadsrc-138bf9095329e61e64ed9fe9ee207d907ee15a67.tar.gz
src-138bf9095329e61e64ed9fe9ee207d907ee15a67.zip
Add basic interrupt management code to gpiobus and ofw_gpiobus.
This is the general support to allow the use of GPIO pins as interrupt sources for direct gpiobus children. The use of GPIO pins as generic interrupt sources (for an ethernet driver for example) will only be possible when arm/intrng is complete. Then, most of this code will need to be rewritten, but it works for now, is better than what we have and will allow further developments. Tested on: ar71xx (RSPRO), am335x (BBB), bcm2835 (Raspberry pi) Differential Revision: https://reviews.freebsd.org/D999 Reviewed by: rpaulo
Notes
Notes: svn path=/head/; revision=274638
-rw-r--r--sys/dev/gpio/gpiobus.c117
-rw-r--r--sys/dev/gpio/gpiobusvar.h3
-rw-r--r--sys/dev/gpio/ofw_gpiobus.c10
3 files changed, 128 insertions, 2 deletions
diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c
index 68adab05d5df..95e521daf54e 100644
--- a/sys/dev/gpio/gpiobus.c
+++ b/sys/dev/gpio/gpiobus.c
@@ -38,6 +38,13 @@ __FBSDID("$FreeBSD$");
#include "gpiobus_if.h"
+#undef GPIOBUS_DEBUG
+#ifdef GPIOBUS_DEBUG
+#define dprintf printf
+#else
+#define dprintf(x, arg...)
+#endif
+
static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int);
static int gpiobus_probe(device_t);
static int gpiobus_attach(device_t);
@@ -105,6 +112,11 @@ gpiobus_init_softc(device_t dev)
sc = GPIOBUS_SOFTC(dev);
sc->sc_busdev = dev;
sc->sc_dev = device_get_parent(dev);
+ sc->sc_intr_rman.rm_type = RMAN_ARRAY;
+ sc->sc_intr_rman.rm_descr = "GPIO Interrupts";
+ if (rman_init(&sc->sc_intr_rman) != 0 ||
+ rman_manage_region(&sc->sc_intr_rman, 0, ~0) != 0)
+ panic("%s: failed to set up rman.", __func__);
if (GPIO_PIN_MAX(sc->sc_dev, &sc->sc_npins) != 0)
return (ENXIO);
@@ -267,6 +279,7 @@ gpiobus_print_child(device_t dev, device_t child)
retval += bus_print_child_header(dev, child);
retval += printf(" at pin(s) ");
gpiobus_print_pins(devi);
+ resource_list_print_type(&devi->rl, "irq", SYS_RES_IRQ, "%ld");
retval += bus_print_child_footer(dev, child);
return (retval);
@@ -304,7 +317,9 @@ gpiobus_add_child(device_t dev, u_int order, const char *name, int unit)
device_delete_child(dev, child);
return (0);
}
+ resource_list_init(&devi->rl);
device_set_ivars(child, devi);
+
return (child);
}
@@ -314,14 +329,103 @@ gpiobus_hinted_child(device_t bus, const char *dname, int dunit)
struct gpiobus_softc *sc = GPIOBUS_SOFTC(bus);
struct gpiobus_ivar *devi;
device_t child;
- int pins;
-
+ int irq, pins;
child = BUS_ADD_CHILD(bus, 0, dname, dunit);
devi = GPIOBUS_IVAR(child);
resource_int_value(dname, dunit, "pins", &pins);
if (gpiobus_parse_pins(sc, child, pins))
device_delete_child(bus, child);
+ if (resource_int_value(dname, dunit, "irq", &irq) == 0) {
+ if (bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1) != 0)
+ device_printf(bus,
+ "warning: bus_set_resource() failed\n");
+ }
+}
+
+static int
+gpiobus_set_resource(device_t dev, device_t child, int type, int rid,
+ u_long start, u_long count)
+{
+ struct gpiobus_ivar *devi;
+ struct resource_list_entry *rle;
+
+ dprintf("%s: entry (%p, %p, %d, %d, %p, %ld)\n",
+ __func__, dev, child, type, rid, (void *)(intptr_t)start, count);
+ devi = GPIOBUS_IVAR(child);
+ rle = resource_list_add(&devi->rl, type, rid, start,
+ start + count - 1, count);
+ if (rle == NULL)
+ return (ENXIO);
+
+ return (0);
+}
+
+static struct resource *
+gpiobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
+ u_long start, u_long end, u_long count, u_int flags)
+{
+ struct gpiobus_softc *sc;
+ struct resource *rv;
+ struct resource_list *rl;
+ struct resource_list_entry *rle;
+ int isdefault;
+
+ if (type != SYS_RES_IRQ)
+ return (NULL);
+ isdefault = (start == 0UL && end == ~0UL && count == 1);
+ rle = NULL;
+ if (isdefault) {
+ rl = BUS_GET_RESOURCE_LIST(bus, child);
+ if (rl == NULL)
+ return (NULL);
+ rle = resource_list_find(rl, type, *rid);
+ if (rle == NULL)
+ return (NULL);
+ if (rle->res != NULL)
+ panic("%s: resource entry is busy", __func__);
+ start = rle->start;
+ count = rle->count;
+ end = rle->end;
+ }
+ sc = device_get_softc(bus);
+ rv = rman_reserve_resource(&sc->sc_intr_rman, start, end, count, flags,
+ child);
+ if (rv == NULL)
+ return (NULL);
+ rman_set_rid(rv, *rid);
+ if ((flags & RF_ACTIVE) != 0 &&
+ bus_activate_resource(child, type, *rid, rv) != 0) {
+ rman_release_resource(rv);
+ return (NULL);
+ }
+
+ return (rv);
+}
+
+static int
+gpiobus_release_resource(device_t bus __unused, device_t child, int type,
+ int rid, struct resource *r)
+{
+ int error;
+
+ if (rman_get_flags(r) & RF_ACTIVE) {
+ error = bus_deactivate_resource(child, type, rid, r);
+ if (error)
+ return (error);
+ }
+
+ return (rman_release_resource(r));
+}
+
+static struct resource_list *
+gpiobus_get_resource_list(device_t bus __unused, device_t child)
+{
+ struct gpiobus_ivar *ivar;
+
+ ivar = GPIOBUS_IVAR(child);
+
+ return (&ivar->rl);
}
static int
@@ -450,6 +554,15 @@ static device_method_t gpiobus_methods[] = {
DEVMETHOD(device_resume, gpiobus_resume),
/* Bus interface */
+ DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
+ DEVMETHOD(bus_config_intr, bus_generic_config_intr),
+ DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
+ DEVMETHOD(bus_set_resource, gpiobus_set_resource),
+ DEVMETHOD(bus_alloc_resource, gpiobus_alloc_resource),
+ DEVMETHOD(bus_release_resource, gpiobus_release_resource),
+ DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
+ 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_print_child, gpiobus_print_child),
DEVMETHOD(bus_child_pnpinfo_str, gpiobus_child_pnpinfo_str),
diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h
index 47c0b6b725ae..ab29583554ed 100644
--- a/sys/dev/gpio/gpiobusvar.h
+++ b/sys/dev/gpio/gpiobusvar.h
@@ -34,6 +34,7 @@
#include <sys/lock.h>
#include <sys/mutex.h>
+#include <sys/rman.h>
#ifdef FDT
#include <dev/ofw/ofw_bus_subr.h>
@@ -62,6 +63,7 @@
struct gpiobus_softc
{
struct mtx sc_mtx; /* bus mutex */
+ struct rman sc_intr_rman; /* isr resources */
device_t sc_busdev; /* bus device */
device_t sc_owner; /* bus owner */
device_t sc_dev; /* driver device */
@@ -71,6 +73,7 @@ struct gpiobus_softc
struct gpiobus_ivar
{
+ struct resource_list rl; /* isr resource list */
uint32_t npins; /* pins total */
uint32_t *flags; /* pins flags */
uint32_t *pins; /* pins map */
diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c
index 1b03e9f13239..5917a084d2da 100644
--- a/sys/dev/gpio/ofw_gpiobus.c
+++ b/sys/dev/gpio/ofw_gpiobus.c
@@ -238,6 +238,13 @@ ofw_gpiobus_setup_devinfo(device_t dev, phandle_t node)
free(dinfo, M_DEVBUF);
return (NULL);
}
+ /* And now the interrupt resources. */
+ resource_list_init(&dinfo->opd_dinfo.rl);
+ if (ofw_bus_intr_to_rl(dev, node, &dinfo->opd_dinfo.rl) != 0) {
+ ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
+ free(dinfo, M_DEVBUF);
+ return (NULL);
+ }
return (dinfo);
}
@@ -246,6 +253,7 @@ static void
ofw_gpiobus_destroy_devinfo(struct ofw_gpiobus_devinfo *dinfo)
{
+ resource_list_free(&dinfo->opd_dinfo.rl);
ofw_bus_gen_destroy_devinfo(&dinfo->opd_obdinfo);
free(dinfo, M_DEVBUF);
}
@@ -326,6 +334,8 @@ ofw_gpiobus_print_child(device_t dev, device_t child)
retval += bus_print_child_header(dev, child);
retval += printf(" at pin(s) ");
gpiobus_print_pins(&devi->opd_dinfo);
+ resource_list_print_type(&devi->opd_dinfo.rl, "irq", SYS_RES_IRQ,
+ "%ld");
retval += bus_print_child_footer(dev, child);
return (retval);