summaryrefslogtreecommitdiffstats
path: root/sys/dev/gpio
diff options
context:
space:
mode:
authorloos <loos@FreeBSD.org>2014-05-15 15:15:23 +0000
committerloos <loos@FreeBSD.org>2014-05-15 15:15:23 +0000
commitfc17b609325ddeec39fd53728652360e8f9148b9 (patch)
tree933e725c3b4c6afee71ccdecd718737052c7fb0c /sys/dev/gpio
parent72ce30b8f8c9716e0cc26a68fe55e89ab3902958 (diff)
downloadFreeBSD-src-fc17b609325ddeec39fd53728652360e8f9148b9.zip
FreeBSD-src-fc17b609325ddeec39fd53728652360e8f9148b9.tar.gz
MFC r265012, r265191, r265289, r265310
r265012: 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. r265191: Remove unnecessary headers. Sort out the headers. Add a missing header on ofw_gpiobus.c (it was working because of sys/libkern.h). r265289: eally sort out the headers. sys/systm.h must always come after sys/param.h. Remove sys/types.h which should never be included together with sys/param.h. Add sys/malloc.h for correctness even if it seems to don't be needed. Remove more unused headers found by unusedinc (from bde@). r265310: Move gpiobus routines to dev/gpio. Avoid polluting ofw_bus with bus specific parts.
Diffstat (limited to 'sys/dev/gpio')
-rw-r--r--sys/dev/gpio/gpio_if.m39
-rw-r--r--sys/dev/gpio/gpiobus.c14
-rw-r--r--sys/dev/gpio/gpiobusvar.h19
-rw-r--r--sys/dev/gpio/ofw_gpiobus.c138
4 files changed, 154 insertions, 56 deletions
diff --git a/sys/dev/gpio/gpio_if.m b/sys/dev/gpio/gpio_if.m
index 78383d3..4d6dfd1 100644
--- a/sys/dev/gpio/gpio_if.m
+++ b/sys/dev/gpio/gpio_if.m
@@ -31,6 +31,32 @@
INTERFACE gpio;
+CODE {
+ static gpio_map_gpios_t gpio_default_map_gpios;
+
+ int
+ gpio_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 (GPIO_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);
+ }
+};
+
+HEADER {
+ #include <dev/ofw/openfirm.h>
+};
+
#
# Get total number of pins
#
@@ -100,3 +126,16 @@ METHOD int pin_setflags {
uint32_t pin_num;
uint32_t flags;
};
+
+#
+# Allow the GPIO controller to map the gpio-specifier on its own.
+#
+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 gpio_default_map_gpios;
diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c
index d8eacc6..e09393c 100644
--- a/sys/dev/gpio/gpiobus.c
+++ b/sys/dev/gpio/gpiobus.c
@@ -29,21 +29,13 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
-#include <sys/kernel.h>
-#include <sys/queue.h>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#include <sys/bus.h>
-#include <machine/bus.h>
-#include <sys/rman.h>
-#include <machine/resource.h>
-
-#include <sys/gpio.h>
#include <dev/gpio/gpiobusvar.h>
-#include "gpio_if.h"
+
#include "gpiobus_if.h"
static int gpiobus_parse_pins(struct gpiobus_softc *, device_t, int);
diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h
index 94ae1ad..e2ee51b 100644
--- a/sys/dev/gpio/gpiobusvar.h
+++ b/sys/dev/gpio/gpiobusvar.h
@@ -32,7 +32,6 @@
#include "opt_platform.h"
-#include <sys/param.h>
#include <sys/lock.h>
#include <sys/mutex.h>
@@ -40,6 +39,8 @@
#include <dev/ofw/ofw_bus_subr.h>
#endif
+#include "gpio_if.h"
+
#define GPIOBUS_IVAR(d) (struct gpiobus_ivar *) device_get_ivars(d)
#define GPIOBUS_SOFTC(d) (struct gpiobus_softc *) device_get_softc(d)
#define GPIOBUS_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
@@ -60,17 +61,29 @@ 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;
+};
+
+static __inline int
+gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells,
+ pcell_t *gpios, uint32_t *pin, uint32_t *flags)
+{
+ return (GPIO_MAP_GPIOS(bus, dev, gparent, gcells, gpios, pin, flags));
+}
+
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..6e18292 100644
--- a/sys/dev/gpio/ofw_gpiobus.c
+++ b/sys/dev/gpio/ofw_gpiobus.c
@@ -30,27 +30,14 @@
__FBSDID("$FreeBSD$");
#include <sys/param.h>
+#include <sys/systm.h>
#include <sys/bus.h>
-#include <sys/gpio.h>
#include <sys/kernel.h>
-#include <sys/libkern.h>
-#include <sys/lock.h>
+#include <sys/malloc.h>
#include <sys/module.h>
-#include <sys/mutex.h>
#include <dev/gpio/gpiobusvar.h>
#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/openfirm.h>
-
-#include <machine/resource.h>
-
-#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);
@@ -83,10 +70,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 +116,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 (gpio_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 +198,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);
OpenPOWER on IntegriCloud