summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormmel <mmel@FreeBSD.org>2016-03-01 16:10:15 +0000
committermmel <mmel@FreeBSD.org>2016-03-01 16:10:15 +0000
commitba6f296a4e33b1c49995e8a73f9f741638bb8847 (patch)
tree5746a7ef5820a57eed92a3bb5af53fb285f55352
parentac7a1d6753fdf33b970359a74fffe286a60968fb (diff)
downloadFreeBSD-src-ba6f296a4e33b1c49995e8a73f9f741638bb8847.zip
FreeBSD-src-ba6f296a4e33b1c49995e8a73f9f741638bb8847.tar.gz
OFW_GPIOBUS: Add utility functions for easier handling of OFW GPIO pins.
Reviewed by: ian, loos (paritaly)
-rw-r--r--sys/dev/gpio/gpiobusvar.h12
-rw-r--r--sys/dev/gpio/ofw_gpiobus.c160
2 files changed, 172 insertions, 0 deletions
diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h
index 6a614cf..2ed9731 100644
--- a/sys/dev/gpio/gpiobusvar.h
+++ b/sys/dev/gpio/gpiobusvar.h
@@ -38,6 +38,7 @@
#ifdef FDT
#include <dev/ofw/ofw_bus_subr.h>
+#include <gnu/dts/include/dt-bindings/gpio/gpio.h>
#endif
#include "gpio_if.h"
@@ -83,6 +84,7 @@ struct gpiobus_pin
uint32_t flags; /* pin flags */
uint32_t pin; /* pin number */
};
+typedef struct gpiobus_pin *gpio_pin_t;
struct gpiobus_ivar
{
@@ -109,6 +111,16 @@ device_t ofw_gpiobus_add_fdt_child(device_t, const char *, phandle_t);
int ofw_gpiobus_parse_gpios(device_t, char *, struct gpiobus_pin **);
void ofw_gpiobus_register_provider(device_t);
void ofw_gpiobus_unregister_provider(device_t);
+
+/* Consumers interface. */
+int gpio_pin_get_by_ofw_name(device_t consumer, char *name, gpio_pin_t *gpio);
+int gpio_pin_get_by_ofw_idx(device_t consumer, int idx, gpio_pin_t *gpio);
+int gpio_pin_get_by_ofw_property(device_t consumer, char *name,
+ gpio_pin_t *gpio);
+void gpio_pin_release(gpio_pin_t gpio);
+int gpio_pin_is_active(gpio_pin_t pin, bool *active);
+int gpio_pin_set_active(gpio_pin_t pin, bool active);
+int gpio_pin_setflags(gpio_pin_t pin, uint32_t flags);
#endif
int gpio_check_flags(uint32_t, uint32_t);
device_t gpiobus_attach_bus(device_t);
diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c
index 1dbb526..be5a747 100644
--- a/sys/dev/gpio/ofw_gpiobus.c
+++ b/sys/dev/gpio/ofw_gpiobus.c
@@ -47,6 +47,166 @@ static void ofw_gpiobus_destroy_devinfo(device_t, struct ofw_gpiobus_devinfo *);
static int ofw_gpiobus_parse_gpios_impl(device_t, phandle_t, char *,
struct gpiobus_softc *, struct gpiobus_pin **);
+/*
+ * Utility functions for easier handling of OFW GPIO pins.
+ *
+ * !!! BEWARE !!!
+ * GPIOBUS uses children's IVARs, so we cannot use this interface for cross
+ * tree consumers.
+ *
+ */
+static int
+gpio_pin_get_by_ofw_impl(device_t consumer_dev, char *prop_name, int idx,
+ gpio_pin_t *out_pin)
+{
+ phandle_t cnode, xref;
+ pcell_t *cells;
+ device_t busdev;
+ struct gpiobus_pin pin;
+ int ncells, rv;
+
+ cnode = ofw_bus_get_node(consumer_dev);
+ if (cnode <= 0) {
+ device_printf(consumer_dev,
+ "%s called on not ofw based device\n", __func__);
+ return (ENXIO);
+ }
+
+ rv = ofw_bus_parse_xref_list_alloc(cnode, prop_name, "#gpio-cells",
+ idx, &xref, &ncells, &cells);
+ if (rv != 0)
+ return (rv);
+
+ /* Translate provider to device. */
+ pin.dev = OF_device_from_xref(xref);
+ if (pin.dev == NULL) {
+ free(cells, M_OFWPROP);
+ return (ENODEV);
+ }
+
+ /* Test if GPIO bus already exist. */
+ busdev = GPIO_GET_BUS(pin.dev);
+ if (busdev == NULL) {
+ free(cells, M_OFWPROP);
+ return (ENODEV);
+ }
+
+ /* Map GPIO pin. */
+ rv = gpio_map_gpios(pin.dev, cnode, OF_node_from_xref(xref), ncells,
+ cells, &pin.pin, &pin.flags);
+ free(cells, M_OFWPROP);
+ if (rv != 0) {
+ device_printf(consumer_dev, "Cannot map the gpio property.\n");
+ return (ENXIO);
+ }
+
+ /* Reserve GPIO pin. */
+ rv = gpiobus_map_pin(busdev, pin.pin);
+ if (rv != 0) {
+ device_printf(consumer_dev, "Cannot reserve gpio pin.\n");
+ return (EBUSY);
+ }
+
+ *out_pin = malloc(sizeof(struct gpiobus_pin), M_DEVBUF,
+ M_WAITOK | M_ZERO);
+ **out_pin = pin;
+ return (0);
+}
+
+int
+gpio_pin_get_by_ofw_idx(device_t consumer_dev, int idx, gpio_pin_t *pin)
+{
+
+ return (gpio_pin_get_by_ofw_impl(consumer_dev, "gpios", idx, pin));
+}
+
+int
+gpio_pin_get_by_ofw_property(device_t consumer_dev, char *name, gpio_pin_t *pin)
+{
+
+ return (gpio_pin_get_by_ofw_impl(consumer_dev, name, 0, pin));
+}
+
+int
+gpio_pin_get_by_ofw_name(device_t consumer_dev, char *name, gpio_pin_t *pin)
+{
+ int rv, idx;
+ phandle_t cnode;
+
+ cnode = ofw_bus_get_node(consumer_dev);
+ if (cnode <= 0) {
+ device_printf(consumer_dev,
+ "%s called on not ofw based device\n", __func__);
+ return (ENXIO);
+ }
+ rv = ofw_bus_find_string_index(cnode, "gpio-names", name, &idx);
+ if (rv != 0)
+ return (rv);
+ return (gpio_pin_get_by_ofw_idx(consumer_dev, idx, pin));
+}
+
+void
+gpio_pin_release(gpio_pin_t gpio)
+{
+
+ if (gpio == NULL)
+ return;
+
+ /* XXXX Unreserve pin. */
+ free(gpio, M_DEVBUF);
+}
+
+int
+gpio_pin_is_active(gpio_pin_t pin, bool *active)
+{
+ int rv;
+ uint32_t tmp;
+
+ KASSERT(pin != NULL, ("GPIO pin is NULL."));
+ KASSERT(pin->dev != NULL, ("GPIO pin device is NULL."));
+ rv = GPIO_PIN_GET(pin->dev, pin->pin, &tmp);
+ if (rv != 0) {
+ return (rv);
+ }
+
+ *active = tmp != 0;
+ if (pin->flags & GPIO_ACTIVE_LOW)
+ *active = !(*active);
+ return (0);
+}
+
+int
+gpio_pin_set_active(gpio_pin_t pin, bool active)
+{
+ int rv;
+ uint32_t tmp;
+
+ if (pin->flags & GPIO_ACTIVE_LOW)
+ tmp = active ? 0 : 1;
+ else
+ tmp = active ? 1 : 0;
+
+ KASSERT(pin != NULL, ("GPIO pin is NULL."));
+ KASSERT(pin->dev != NULL, ("GPIO pin device is NULL."));
+ rv = GPIO_PIN_SET(pin->dev, pin->pin, tmp);
+ return (rv);
+}
+
+int
+gpio_pin_setflags(gpio_pin_t pin, uint32_t flags)
+{
+ int rv;
+
+ KASSERT(pin != NULL, ("GPIO pin is NULL."));
+ KASSERT(pin->dev != NULL, ("GPIO pin device is NULL."));
+
+ rv = GPIO_PIN_SETFLAGS(pin->dev, pin->pin, flags);
+ return (rv);
+}
+
+/*
+ * OFW_GPIOBUS driver.
+ */
device_t
ofw_gpiobus_add_fdt_child(device_t bus, const char *drvname, phandle_t child)
{
OpenPOWER on IntegriCloud