summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorloos <loos@FreeBSD.org>2014-04-27 12:11:00 +0000
committerloos <loos@FreeBSD.org>2014-04-27 12:11:00 +0000
commit401918349b37ff258742381c027518bcc4f650cc (patch)
treeab6c2cab6977035917d3b9b3e99633575078fb66
parentc5479b1c51e46a4e9c407c20e8ec65a986a5c556 (diff)
downloadFreeBSD-src-401918349b37ff258742381c027518bcc4f650cc.zip
FreeBSD-src-401918349b37ff258742381c027518bcc4f650cc.tar.gz
Fix the gpio-specifier decoding by respecting the GPIO controller's
#gpio-cells property. Add a new ofw_bus method (OFW_BUS_MAP_GPIOS()) that allows the GPIO controller to implement its own mapping to deal with gpio-specifiers, allowing the decoding of gpio-specifiers to be controller specific. The default ofw_bus_map_gpios() decodes the linux standard (#gpio-cells = <2>) and the FreeBSD standard (#gpio-cells = <3>). It pass the gpio-specifier flag field to the children as an ivar variable so they can act upon.
-rw-r--r--sys/dev/gpio/gpiobusvar.h9
-rw-r--r--sys/dev/gpio/ofw_gpiobus.c126
-rw-r--r--sys/dev/ofw/ofw_bus.h8
-rw-r--r--sys/dev/ofw/ofw_bus_if.m30
4 files changed, 138 insertions, 35 deletions
diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h
index 94ae1ad..3d38710 100644
--- a/sys/dev/gpio/gpiobusvar.h
+++ b/sys/dev/gpio/gpiobusvar.h
@@ -60,17 +60,22 @@ struct gpiobus_softc
int *sc_pins_mapped; /* mark mapped pins */
};
-
struct gpiobus_ivar
{
uint32_t npins; /* pins total */
+ uint32_t *flags; /* pins flags */
uint32_t *pins; /* pins map */
};
-void gpiobus_print_pins(struct gpiobus_ivar *);
#ifdef FDT
+struct ofw_gpiobus_devinfo {
+ struct gpiobus_ivar opd_dinfo;
+ struct ofw_bus_devinfo opd_obdinfo;
+};
+
device_t ofw_gpiobus_add_fdt_child(device_t, phandle_t);
#endif
+void gpiobus_print_pins(struct gpiobus_ivar *);
extern driver_t gpiobus_driver;
diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c
index 7641b56..f4eb37d 100644
--- a/sys/dev/gpio/ofw_gpiobus.c
+++ b/sys/dev/gpio/ofw_gpiobus.c
@@ -47,11 +47,6 @@ __FBSDID("$FreeBSD$");
#include "gpio_if.h"
#include "gpiobus_if.h"
-struct ofw_gpiobus_devinfo {
- struct gpiobus_ivar opd_dinfo;
- struct ofw_bus_devinfo opd_obdinfo;
-};
-
static int ofw_gpiobus_parse_gpios(struct gpiobus_softc *,
struct gpiobus_ivar *, phandle_t);
static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t,
@@ -83,10 +78,37 @@ ofw_gpiobus_add_fdt_child(device_t bus, phandle_t child)
}
static int
+ofw_gpiobus_alloc_ivars(struct gpiobus_ivar *dinfo)
+{
+
+ /* Allocate pins and flags memory. */
+ dinfo->pins = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ if (dinfo->pins == NULL)
+ return (ENOMEM);
+ dinfo->flags = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF,
+ M_NOWAIT | M_ZERO);
+ if (dinfo->flags == NULL) {
+ free(dinfo->pins, M_DEVBUF);
+ return (ENOMEM);
+ }
+
+ return (0);
+}
+
+static void
+ofw_gpiobus_free_ivars(struct gpiobus_ivar *dinfo)
+{
+
+ free(dinfo->flags, M_DEVBUF);
+ free(dinfo->pins, M_DEVBUF);
+}
+
+static int
ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo,
phandle_t child)
{
- int i, len;
+ int cells, i, j, len;
pcell_t *gpios;
phandle_t gpio;
@@ -102,44 +124,81 @@ ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo,
}
/*
- * Each 'gpios' entry must contain 4 pcells.
- * The first one is the GPIO controller phandler.
- * Then the last three are the GPIO pin, the GPIO pin direction and
- * the GPIO pin flags.
+ * The gpio-specifier is controller independent, but the first pcell
+ * has the reference to the GPIO controller phandler.
+ * One the first pass we count the number of encoded gpio-specifiers.
*/
- if ((len / sizeof(pcell_t)) % 4) {
+ i = 0;
+ len /= sizeof(pcell_t);
+ while (i < len) {
+ /* Allow NULL specifiers. */
+ if (gpios[i] == 0) {
+ dinfo->npins++;
+ i++;
+ continue;
+ }
+ gpio = OF_xref_phandle(gpios[i]);
+ /* Verify if we're attaching to the correct GPIO controller. */
+ if (!OF_hasprop(gpio, "gpio-controller") ||
+ gpio != ofw_bus_get_node(sc->sc_dev)) {
+ free(gpios, M_DEVBUF);
+ return (EINVAL);
+ }
+ /* Read gpio-cells property for this GPIO controller. */
+ if (OF_getencprop(gpio, "#gpio-cells", &cells,
+ sizeof(cells)) < 0) {
+ free(gpios, M_DEVBUF);
+ return (EINVAL);
+ }
+ dinfo->npins++;
+ i += cells + 1;
+ }
+
+ if (dinfo->npins == 0) {
free(gpios, M_DEVBUF);
return (EINVAL);
}
- dinfo->npins = len / (sizeof(pcell_t) * 4);
- dinfo->pins = malloc(sizeof(uint32_t) * dinfo->npins, M_DEVBUF,
- M_NOWAIT | M_ZERO);
- if (dinfo->pins == NULL) {
+
+ /* Allocate the child resources. */
+ if (ofw_gpiobus_alloc_ivars(dinfo) != 0) {
free(gpios, M_DEVBUF);
return (ENOMEM);
}
- for (i = 0; i < dinfo->npins; i++) {
+ /* Decode the gpio specifier on the second pass. */
+ i = 0;
+ j = 0;
+ while (i < len) {
+ /* Allow NULL specifiers. */
+ if (gpios[i] == 0) {
+ i++;
+ j++;
+ continue;
+ }
- /* Verify if we're attaching to the correct gpio controller. */
- gpio = OF_xref_phandle(gpios[i * 4 + 0]);
- if (!OF_hasprop(gpio, "gpio-controller") ||
- gpio != ofw_bus_get_node(sc->sc_dev)) {
- free(dinfo->pins, M_DEVBUF);
+ gpio = OF_xref_phandle(gpios[i]);
+ /* Read gpio-cells property for this GPIO controller. */
+ if (OF_getencprop(gpio, "#gpio-cells", &cells,
+ sizeof(cells)) < 0) {
+ ofw_gpiobus_free_ivars(dinfo);
free(gpios, M_DEVBUF);
return (EINVAL);
}
- /* Get the GPIO pin number. */
- dinfo->pins[i] = gpios[i * 4 + 1];
- /* gpios[i * 4 + 2] - GPIO pin direction */
- /* gpios[i * 4 + 3] - GPIO pin flags */
+ /* Get the GPIO pin number and flags. */
+ if (ofw_bus_map_gpios(sc->sc_dev, child, gpio, cells,
+ &gpios[i + 1], &dinfo->pins[j], &dinfo->flags[j]) != 0) {
+ ofw_gpiobus_free_ivars(dinfo);
+ free(gpios, M_DEVBUF);
+ return (EINVAL);
+ }
- if (dinfo->pins[i] > sc->sc_npins) {
+ /* Consistency check. */
+ if (dinfo->pins[j] > sc->sc_npins) {
device_printf(sc->sc_busdev,
"invalid pin %d, max: %d\n",
- dinfo->pins[i], sc->sc_npins - 1);
- free(dinfo->pins, M_DEVBUF);
+ dinfo->pins[j], sc->sc_npins - 1);
+ ofw_gpiobus_free_ivars(dinfo);
free(gpios, M_DEVBUF);
return (EINVAL);
}
@@ -147,15 +206,18 @@ ofw_gpiobus_parse_gpios(struct gpiobus_softc *sc, struct gpiobus_ivar *dinfo,
/*
* Mark pin as mapped and give warning if it's already mapped.
*/
- if (sc->sc_pins_mapped[dinfo->pins[i]]) {
+ if (sc->sc_pins_mapped[dinfo->pins[j]]) {
device_printf(sc->sc_busdev,
"warning: pin %d is already mapped\n",
- dinfo->pins[i]);
- free(dinfo->pins, M_DEVBUF);
+ dinfo->pins[j]);
+ ofw_gpiobus_free_ivars(dinfo);
free(gpios, M_DEVBUF);
return (EINVAL);
}
- sc->sc_pins_mapped[dinfo->pins[i]] = 1;
+ sc->sc_pins_mapped[dinfo->pins[j]] = 1;
+
+ i += cells + 1;
+ j++;
}
free(gpios, M_DEVBUF);
diff --git a/sys/dev/ofw/ofw_bus.h b/sys/dev/ofw/ofw_bus.h
index f7c7295..7f0e50e 100644
--- a/sys/dev/ofw/ofw_bus.h
+++ b/sys/dev/ofw/ofw_bus.h
@@ -76,4 +76,12 @@ ofw_bus_map_intr(device_t dev, phandle_t iparent, int icells, pcell_t *intr)
return (OFW_BUS_MAP_INTR(dev, dev, iparent, icells, intr));
}
+static __inline int
+ofw_bus_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells,
+ pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+ return (OFW_BUS_MAP_GPIOS(bus, dev, gparent, gcells, gpios, pin,
+ flags));
+}
+
#endif /* !_DEV_OFW_OFW_BUS_H_ */
diff --git a/sys/dev/ofw/ofw_bus_if.m b/sys/dev/ofw/ofw_bus_if.m
index 6486e96..86c9876 100644
--- a/sys/dev/ofw/ofw_bus_if.m
+++ b/sys/dev/ofw/ofw_bus_if.m
@@ -58,6 +58,7 @@ CODE {
static ofw_bus_get_node_t ofw_bus_default_get_node;
static ofw_bus_get_type_t ofw_bus_default_get_type;
static ofw_bus_map_intr_t ofw_bus_default_map_intr;
+ static ofw_bus_map_gpios_t ofw_bus_default_map_gpios;
static const struct ofw_bus_devinfo *
ofw_bus_default_get_devinfo(device_t bus, device_t dev)
@@ -113,6 +114,24 @@ CODE {
/* If that fails, then assume a one-domain system */
return (interrupt[0]);
}
+
+ int
+ ofw_bus_default_map_gpios(device_t bus, phandle_t dev,
+ phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin,
+ uint32_t *flags)
+ {
+ /* Propagate up the bus hierarchy until someone handles it. */
+ if (device_get_parent(bus) != NULL)
+ return OFW_BUS_MAP_GPIOS(device_get_parent(bus), dev,
+ gparent, gcells, gpios, pin, flags);
+
+ /* If that fails, then assume the FreeBSD defaults. */
+ *pin = gpios[0];
+ if (gcells == 2 || gcells == 3)
+ *flags = gpios[gcells - 1];
+
+ return (0);
+ }
};
# Get the ofw_bus_devinfo struct for the device dev on the bus. Used for bus
@@ -170,4 +189,13 @@ METHOD int map_intr {
pcell_t *interrupt;
} DEFAULT ofw_bus_default_map_intr;
-
+# Map the GPIO controller specific gpio-specifier to GPIO pin and flags.
+METHOD int map_gpios {
+ device_t bus;
+ phandle_t dev;
+ phandle_t gparent;
+ int gcells;
+ pcell_t *gpios;
+ uint32_t *pin;
+ uint32_t *flags;
+} DEFAULT ofw_bus_default_map_gpios;
OpenPOWER on IntegriCloud