Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F162382487
D12708.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
31 KB
Referenced Files
None
Subscribers
None
D12708.diff
View Options
Index: head/sys/conf/files
===================================================================
--- head/sys/conf/files
+++ head/sys/conf/files
@@ -1246,6 +1246,7 @@
dev/bhnd/cores/chipc/bhnd_pmu_chipc.c optional bhnd
dev/bhnd/cores/chipc/chipc.c optional bhnd
dev/bhnd/cores/chipc/chipc_cfi.c optional bhnd cfi
+dev/bhnd/cores/chipc/chipc_gpio.c optional bhnd gpio
dev/bhnd/cores/chipc/chipc_slicer.c optional bhnd cfi | bhnd spibus
dev/bhnd/cores/chipc/chipc_spi.c optional bhnd spibus
dev/bhnd/cores/chipc/chipc_subr.c optional bhnd
Index: head/sys/dev/bhnd/bhnd_types.h
===================================================================
--- head/sys/dev/bhnd/bhnd_types.h
+++ head/sys/dev/bhnd/bhnd_types.h
@@ -75,6 +75,7 @@
BHND_SERVICE_PWRCTL, /**< legacy pwrctl service; implements the bhnd_pwrctl interface */
BHND_SERVICE_PMU, /**< pmu service; implements the bhnd_pmu interface */
BHND_SERVICE_NVRAM, /**< nvram service; implements the bhnd_nvram interface */
+ BHND_SERVICE_GPIO, /**< gpio service; implements the standard gpio interface */
BHND_SERVICE_ANY = 1000, /**< match on any service type */
} bhnd_service_t;
Index: head/sys/dev/bhnd/cores/chipc/chipc.c
===================================================================
--- head/sys/dev/bhnd/cores/chipc/chipc.c
+++ head/sys/dev/bhnd/cores/chipc/chipc.c
@@ -306,6 +306,20 @@
}
}
+ /* GPIO */
+ child = BUS_ADD_CHILD(sc->dev, 0, "gpio", 0);
+ if (child == NULL) {
+ device_printf(sc->dev, "failed to add gpio\n");
+ return (ENXIO);
+ }
+
+ error = chipc_set_mem_resource(sc, child, 0, 0, RM_MAX_END, 0, 0);
+ if (error) {
+ device_printf(sc->dev, "failed to set gpio memory resource: "
+ "%d\n", error);
+ return (error);
+ }
+
/* All remaining devices are SoC-only */
if (bhnd_get_attach_type(sc->dev) != BHND_ATTACH_NATIVE)
return (0);
@@ -835,6 +849,25 @@
if ((cr = chipc_find_region(sc, start, end)) == NULL) {
/* Resource requests outside our shared port regions can be
* delegated to our parent. */
+ rv = bus_generic_rl_alloc_resource(dev, child, type, rid,
+ start, end, count, flags);
+ return (rv);
+ }
+
+ /*
+ * As a special case, children that map the complete ChipCommon register
+ * block are delegated to our parent.
+ *
+ * The rman API does not support sharing resources that are not
+ * identical in size; since we allocate subregions to various children,
+ * any children that need to map the entire register block (e.g. because
+ * they require access to discontiguous register ranges) must make the
+ * allocation through our parent, where we hold a compatible
+ * RF_SHAREABLE allocation.
+ */
+ if (cr == sc->core_region && cr->cr_addr == start &&
+ cr->cr_end == end && cr->cr_count == count)
+ {
rv = bus_generic_rl_alloc_resource(dev, child, type, rid,
start, end, count, flags);
return (rv);
Index: head/sys/dev/bhnd/cores/chipc/chipc_gpio.c
===================================================================
--- head/sys/dev/bhnd/cores/chipc/chipc_gpio.c
+++ head/sys/dev/bhnd/cores/chipc/chipc_gpio.c
@@ -0,0 +1,846 @@
+/*-
+ * Copyright (c) 2017 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Landon Fuller under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/limits.h>
+#include <sys/module.h>
+
+#include <machine/_inttypes.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/bhnd/bhnd.h>
+#include <dev/gpio/gpiobusvar.h>
+
+#include "gpio_if.h"
+
+#include "bhnd_nvram_map.h"
+
+#include "chipcreg.h"
+#include "chipc_gpiovar.h"
+
+/*
+ * ChipCommon GPIO driver
+ */
+
+static int chipc_gpio_check_flags(
+ struct chipc_gpio_softc *sc,
+ uint32_t pin_num, uint32_t flags,
+ chipc_gpio_pin_mode *mode);
+static int chipc_gpio_pin_update(
+ struct chipc_gpio_softc *sc,
+ struct chipc_gpio_update *update,
+ uint32_t pin_num, uint32_t flags);
+static int chipc_gpio_commit_update(
+ struct chipc_gpio_softc *sc,
+ struct chipc_gpio_update *update);
+static chipc_gpio_pin_mode chipc_gpio_pin_get_mode(
+ struct chipc_gpio_softc *sc,
+ uint32_t pin_num);
+
+
+/* Debugging flags */
+static u_long chipc_gpio_debug = 0;
+TUNABLE_ULONG("hw.bhnd_chipc.gpio_debug", &chipc_gpio_debug);
+
+enum {
+ /** Allow userspace GPIO access on bridged network (e.g. wi-fi)
+ * adapters */
+ CC_GPIO_DEBUG_ADAPTER_GPIOC = 1 << 0,
+};
+
+#define CC_GPIO_DEBUG(_type) (CC_GPIO_DEBUG_ ## _type & chipc_gpio_debug)
+
+static struct bhnd_device_quirk chipc_gpio_quirks[];
+
+/* Supported parent core device identifiers */
+static const struct bhnd_device chipc_gpio_devices[] = {
+ BHND_DEVICE(BCM, CC, "Broadcom ChipCommon GPIO", chipc_gpio_quirks),
+ BHND_DEVICE_END
+};
+
+/* Device quirks table */
+static struct bhnd_device_quirk chipc_gpio_quirks[] = {
+ BHND_CORE_QUIRK (HWREV_LTE(10), CC_GPIO_QUIRK_NO_EVENTS),
+ BHND_CORE_QUIRK (HWREV_LTE(15), CC_GPIO_QUIRK_NO_DCTIMER),
+ BHND_CORE_QUIRK (HWREV_LTE(19), CC_GPIO_QUIRK_NO_PULLUPDOWN),
+
+ BHND_DEVICE_QUIRK_END
+};
+
+static int
+chipc_gpio_probe(device_t dev)
+{
+ const struct bhnd_device *id;
+ device_t chipc;
+
+ /* Look for compatible chipc parent */
+ chipc = device_get_parent(dev);
+ id = bhnd_device_lookup(chipc, chipc_gpio_devices,
+ sizeof(chipc_gpio_devices[0]));
+ if (id == NULL)
+ return (ENXIO);
+
+ device_set_desc(dev, id->desc);
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+static int
+chipc_gpio_attach(device_t dev)
+{
+ struct chipc_gpio_softc *sc;
+ device_t chipc;
+ int error;
+
+ chipc = device_get_parent(dev);
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->quirks = bhnd_device_quirks(chipc, chipc_gpio_devices,
+ sizeof(chipc_gpio_devices[0]));
+
+ /* If this is a bridged wi-fi adapter, we don't want to support
+ * userspace requests via gpioc(4) */
+ if (bhnd_get_attach_type(chipc) == BHND_ATTACH_ADAPTER) {
+ if (!CC_GPIO_DEBUG(ADAPTER_GPIOC))
+ sc->quirks |= CC_GPIO_QUIRK_NO_GPIOC;
+ }
+
+ CC_GPIO_LOCK_INIT(sc);
+
+ sc->mem_rid = 0;
+ sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
+ RF_ACTIVE|RF_SHAREABLE);
+ if (sc->mem_res == NULL) {
+ device_printf(dev, "failed to allocate chipcommon registers\n");
+ error = ENXIO;
+ goto failed;
+ }
+
+ /*
+ * If hardware 'pulsate' support is available, set the timer duty-cycle
+ * to either the NVRAM 'leddc' value if available, or the default duty
+ * cycle.
+ */
+ if (!CC_GPIO_QUIRK(sc, NO_DCTIMER)) {
+ uint32_t dctimerval;
+
+ error = bhnd_nvram_getvar_uint32(chipc, BHND_NVAR_LEDDC,
+ &dctimerval);
+ if (error == ENOENT) {
+ /* Fall back on default duty cycle */
+ dctimerval = CHIPC_GPIOTIMERVAL_DEFAULT;
+ } else if (error) {
+ device_printf(dev, "error reading %s from NVRAM: %d\n",
+ BHND_NVAR_LEDDC, error);
+ goto failed;
+ }
+
+ CC_GPIO_WR4(sc, CHIPC_GPIOTIMERVAL, dctimerval);
+ }
+
+ /* Attach gpioc/gpiobus */
+ if (CC_GPIO_QUIRK(sc, NO_GPIOC)) {
+ sc->gpiobus = NULL;
+ } else {
+ if ((sc->gpiobus = gpiobus_attach_bus(dev)) == NULL) {
+ device_printf(dev, "failed to attach gpiobus\n");
+ error = ENXIO;
+ goto failed;
+ }
+ }
+
+ /* Register as the bus GPIO provider */
+ if ((error = bhnd_register_provider(dev, BHND_SERVICE_GPIO))) {
+ device_printf(dev, "failed to register gpio with bus: %d\n",
+ error);
+ goto failed;
+ }
+
+ return (0);
+
+failed:
+ device_delete_children(dev);
+
+ if (sc->mem_res != NULL) {
+ bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid,
+ sc->mem_res);
+ }
+
+ CC_GPIO_LOCK_DESTROY(sc);
+
+ return (error);
+}
+
+static int
+chipc_gpio_detach(device_t dev)
+{
+ struct chipc_gpio_softc *sc;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if ((error = bus_generic_detach(dev)))
+ return (error);
+
+ if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY)))
+ return (error);
+
+ bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
+ CC_GPIO_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static device_t
+chipc_gpio_get_bus(device_t dev)
+{
+ struct chipc_gpio_softc *sc = device_get_softc(dev);
+
+ return (sc->gpiobus);
+}
+
+static int
+chipc_gpio_pin_max(device_t dev, int *maxpin)
+{
+ *maxpin = CC_GPIO_NPINS-1;
+ return (0);
+}
+
+static int
+chipc_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
+{
+ struct chipc_gpio_softc *sc;
+ bool pin_high;
+ int error;
+
+ sc = device_get_softc(dev);
+ error = 0;
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ switch (pin_value) {
+ case GPIO_PIN_HIGH:
+ pin_high = true;
+ break;
+ case GPIO_PIN_LOW:
+ pin_high = false;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ CC_GPIO_LOCK(sc);
+
+ switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
+ case CC_GPIO_PIN_INPUT:
+ case CC_GPIO_PIN_TRISTATE:
+ error = ENODEV;
+ break;
+
+ case CC_GPIO_PIN_OUTPUT:
+ CC_GPIO_WRFLAG(sc, pin_num, GPIOOUT, pin_high);
+ break;
+ }
+
+ CC_GPIO_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+chipc_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
+{
+ struct chipc_gpio_softc *sc;
+ bool pin_high;
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ pin_high = false;
+
+ CC_GPIO_LOCK(sc);
+
+ switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
+ case CC_GPIO_PIN_INPUT:
+ pin_high = CC_GPIO_RDFLAG(sc, pin_num, GPIOIN);
+ break;
+
+ case CC_GPIO_PIN_OUTPUT:
+ pin_high = CC_GPIO_RDFLAG(sc, pin_num, GPIOOUT);
+ break;
+
+ case CC_GPIO_PIN_TRISTATE:
+ pin_high = false;
+ break;
+ }
+
+ CC_GPIO_UNLOCK(sc);
+
+ *pin_value = pin_high ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
+
+ return (0);
+}
+
+static int
+chipc_gpio_pin_toggle(device_t dev, uint32_t pin_num)
+{
+ struct chipc_gpio_softc *sc;
+ bool pin_high;
+ int error;
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ sc = device_get_softc(dev);
+ error = 0;
+
+ CC_GPIO_LOCK(sc);
+
+ switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
+ case CC_GPIO_PIN_INPUT:
+ case CC_GPIO_PIN_TRISTATE:
+ error = ENODEV;
+ break;
+
+ case CC_GPIO_PIN_OUTPUT:
+ pin_high = CC_GPIO_RDFLAG(sc, pin_num, GPIOOUT);
+ CC_GPIO_WRFLAG(sc, pin_num, GPIOOUT, !pin_high);
+ break;
+ }
+
+ CC_GPIO_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+chipc_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
+{
+ struct chipc_gpio_softc *sc = device_get_softc(dev);
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
+
+ if (!CC_GPIO_QUIRK(sc, NO_PULLUPDOWN))
+ *caps |= (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
+
+ if (!CC_GPIO_QUIRK(sc, NO_DCTIMER))
+ *caps |= GPIO_PIN_PULSATE;
+
+ return (0);
+}
+
+static int
+chipc_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
+{
+ struct chipc_gpio_softc *sc = device_get_softc(dev);
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ CC_GPIO_LOCK(sc);
+
+ switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
+ case CC_GPIO_PIN_INPUT:
+ *flags = GPIO_PIN_INPUT;
+
+ if (!CC_GPIO_QUIRK(sc, NO_PULLUPDOWN)) {
+ if (CC_GPIO_RDFLAG(sc, pin_num, GPIOPU)) {
+ *flags |= GPIO_PIN_PULLUP;
+ } else if (CC_GPIO_RDFLAG(sc, pin_num, GPIOPD)) {
+ *flags |= GPIO_PIN_PULLDOWN;
+ }
+ }
+ break;
+
+ case CC_GPIO_PIN_OUTPUT:
+ *flags = GPIO_PIN_OUTPUT;
+
+ if (!CC_GPIO_QUIRK(sc, NO_DCTIMER)) {
+ if (CC_GPIO_RDFLAG(sc, pin_num, GPIOTIMEROUTMASK))
+ *flags |= GPIO_PIN_PULSATE;
+ }
+
+ break;
+
+ case CC_GPIO_PIN_TRISTATE:
+ *flags = GPIO_PIN_TRISTATE|GPIO_PIN_OUTPUT;
+ break;
+ }
+
+ CC_GPIO_UNLOCK(sc);
+
+ return (0);
+}
+
+static int
+chipc_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
+{
+ int ret;
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ ret = snprintf(name, GPIOMAXNAME, "bhnd_gpio%02" PRIu32, pin_num);
+
+ if (ret < 0)
+ return (ENXIO);
+
+ if (ret >= GPIOMAXNAME)
+ return (ENOMEM);
+
+ return (0);
+}
+
+static int
+chipc_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
+{
+ struct chipc_gpio_softc *sc;
+ struct chipc_gpio_update upd;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ /* Produce an update descriptor */
+ memset(&upd, 0, sizeof(upd));
+ if ((error = chipc_gpio_pin_update(sc, &upd, pin_num, flags)))
+ return (error);
+
+ /* Commit the update */
+ CC_GPIO_LOCK(sc);
+ error = chipc_gpio_commit_update(sc, &upd);
+ CC_GPIO_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+chipc_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins,
+ uint32_t change_pins, uint32_t *orig_pins)
+{
+ struct chipc_gpio_softc *sc;
+ struct chipc_gpio_update upd;
+ uint32_t out, outen, ctrl;
+ uint32_t num_pins;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (first_pin >= CC_GPIO_NPINS)
+ return (EINVAL);
+
+ /* Determine the actual number of referenced pins */
+ if (clear_pins == 0 && change_pins == 0) {
+ num_pins = CC_GPIO_NPINS - first_pin;
+ } else {
+ int num_clear_pins, num_change_pins;
+
+ num_clear_pins = flsl((u_long)clear_pins);
+ num_change_pins = flsl((u_long)change_pins);
+ num_pins = MAX(num_clear_pins, num_change_pins);
+ }
+
+ /* Validate the full pin range */
+ if (!CC_GPIO_VALID_PINS(first_pin, num_pins))
+ return (EINVAL);
+
+ /* Produce an update descriptor for all pins, relative to the current
+ * pin state */
+ CC_GPIO_LOCK(sc);
+ memset(&upd, 0, sizeof(upd));
+
+ out = CC_GPIO_RD4(sc, CHIPC_GPIOOUT);
+ outen = CC_GPIO_RD4(sc, CHIPC_GPIOOUTEN);
+ ctrl = CC_GPIO_RD4(sc, CHIPC_GPIOCTRL);
+
+ for (uint32_t i = 0; i < num_pins; i++) {
+ uint32_t pin;
+ bool pin_high;
+
+ pin = first_pin + i;
+
+ /* The pin must be configured for output */
+ if ((outen & (1 << pin)) == 0) {
+ CC_GPIO_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ /* The pin must not tristated */
+ if ((ctrl & (1 << pin)) != 0) {
+ CC_GPIO_UNLOCK(sc);
+ return (EINVAL);
+ }
+
+ /* Fetch current state */
+ if (out & (1 << pin)) {
+ pin_high = true;
+ } else {
+ pin_high = false;
+ }
+
+ /* Apply clear/toggle request */
+ if (clear_pins & (1 << pin))
+ pin_high = false;
+
+ if (change_pins & (1 << pin))
+ pin_high = !pin_high;
+
+ /* Add to our update descriptor */
+ CC_GPIO_UPDATE(&upd, pin, out, pin_high);
+ }
+
+ /* Commit the update */
+ error = chipc_gpio_commit_update(sc, &upd);
+ CC_GPIO_UNLOCK(sc);
+
+ return (error);
+}
+
+static int
+chipc_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins,
+ uint32_t *pin_flags)
+{
+ struct chipc_gpio_softc *sc;
+ struct chipc_gpio_update upd;
+ int error;
+
+ sc = device_get_softc(dev);
+
+ if (!CC_GPIO_VALID_PINS(first_pin, num_pins))
+ return (EINVAL);
+
+ /* Produce an update descriptor */
+ memset(&upd, 0, sizeof(upd));
+ for (uint32_t i = 0; i < num_pins; i++) {
+ uint32_t pin, flags;
+
+ pin = first_pin + i;
+ flags = pin_flags[i];
+
+ /* As per the gpio_config_32 API documentation, any pins for
+ * which neither GPIO_PIN_OUTPUT or GPIO_PIN_INPUT are set
+ * should be ignored and left unmodified */
+ if ((flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) == 0)
+ continue;
+
+ if ((error = chipc_gpio_pin_update(sc, &upd, pin, flags)))
+ return (error);
+ }
+
+ /* Commit the update */
+ CC_GPIO_LOCK(sc);
+ error = chipc_gpio_commit_update(sc, &upd);
+ CC_GPIO_UNLOCK(sc);
+
+ return (error);
+}
+
+
+/**
+ * Commit a single @p reg register update.
+ */
+static void
+chipc_gpio_commit_reg(struct chipc_gpio_softc *sc, bus_size_t offset,
+ struct chipc_gpio_reg *reg)
+{
+ uint32_t value;
+
+ CC_GPIO_LOCK_ASSERT(sc, MA_OWNED);
+
+ if (reg->mask == 0)
+ return;
+
+ value = bhnd_bus_read_4(sc->mem_res, offset);
+ value &= ~reg->mask;
+ value |= reg->value;
+
+ bhnd_bus_write_4(sc->mem_res, offset, value);
+}
+
+/**
+ * Commit the set of GPIO register updates described by @p update.
+ */
+static int
+chipc_gpio_commit_update(struct chipc_gpio_softc *sc,
+ struct chipc_gpio_update *update)
+{
+ CC_GPIO_LOCK_ASSERT(sc, MA_OWNED);
+
+ /* Commit pulldown/pullup before potentially disabling an output pin */
+ chipc_gpio_commit_reg(sc, CHIPC_GPIOPD, &update->pulldown);
+ chipc_gpio_commit_reg(sc, CHIPC_GPIOPU, &update->pullup);
+
+ /* Commit output settings before potentially enabling an output pin */
+ chipc_gpio_commit_reg(sc, CHIPC_GPIOTIMEROUTMASK,
+ &update->timeroutmask);
+ chipc_gpio_commit_reg(sc, CHIPC_GPIOOUT, &update->out);
+
+ /* Commit input/output/tristate modes */
+ chipc_gpio_commit_reg(sc, CHIPC_GPIOOUTEN, &update->outen);
+ chipc_gpio_commit_reg(sc, CHIPC_GPIOCTRL, &update->ctrl);
+
+ return (0);
+}
+
+/**
+ * Apply the changes described by @p flags for @p pin_num to the given @p update
+ * descriptor.
+ */
+static int
+chipc_gpio_pin_update(struct chipc_gpio_softc *sc,
+ struct chipc_gpio_update *update, uint32_t pin_num, uint32_t flags)
+{
+ chipc_gpio_pin_mode mode;
+ int error;
+
+ if (!CC_GPIO_VALID_PIN(pin_num))
+ return (EINVAL);
+
+ /* Verify flag compatibility and determine the pin mode */
+ if ((error = chipc_gpio_check_flags(sc, pin_num, flags, &mode)))
+ return (error);
+
+ /* Apply the mode-specific changes */
+ switch (mode) {
+ case CC_GPIO_PIN_INPUT:
+ CC_GPIO_UPDATE(update, pin_num, pullup, false);
+ CC_GPIO_UPDATE(update, pin_num, pulldown, false);
+ CC_GPIO_UPDATE(update, pin_num, out, false);
+ CC_GPIO_UPDATE(update, pin_num, outen, false);
+ CC_GPIO_UPDATE(update, pin_num, timeroutmask, false);
+ CC_GPIO_UPDATE(update, pin_num, ctrl, false);
+
+ if (flags & GPIO_PIN_PULLUP) {
+ CC_GPIO_UPDATE(update, pin_num, pullup, true);
+ } else if (flags & GPIO_PIN_PULLDOWN) {
+ CC_GPIO_UPDATE(update, pin_num, pulldown, true);
+ }
+
+ return (0);
+
+ case CC_GPIO_PIN_OUTPUT:
+ CC_GPIO_UPDATE(update, pin_num, pullup, false);
+ CC_GPIO_UPDATE(update, pin_num, pulldown, false);
+ CC_GPIO_UPDATE(update, pin_num, outen, true);
+ CC_GPIO_UPDATE(update, pin_num, timeroutmask, false);
+ CC_GPIO_UPDATE(update, pin_num, ctrl, false);
+
+ if (flags & GPIO_PIN_PRESET_HIGH) {
+ CC_GPIO_UPDATE(update, pin_num, out, true);
+ } else if (flags & GPIO_PIN_PRESET_LOW) {
+ CC_GPIO_UPDATE(update, pin_num, out, false);
+ }
+
+ if (flags & GPIO_PIN_PULSATE)
+ CC_GPIO_UPDATE(update, pin_num, timeroutmask, true);
+
+ return (0);
+
+ case CC_GPIO_PIN_TRISTATE:
+ CC_GPIO_UPDATE(update, pin_num, pullup, false);
+ CC_GPIO_UPDATE(update, pin_num, pulldown, false);
+ CC_GPIO_UPDATE(update, pin_num, out, false);
+ CC_GPIO_UPDATE(update, pin_num, outen, false);
+ CC_GPIO_UPDATE(update, pin_num, timeroutmask, false);
+ CC_GPIO_UPDATE(update, pin_num, ctrl, true);
+
+ if (flags & GPIO_PIN_OUTPUT)
+ CC_GPIO_UPDATE(update, pin_num, outen, true);
+
+ return (0);
+ }
+
+ device_printf(sc->dev, "unknown pin mode %d\n", mode);
+ return (EINVAL);
+}
+
+/**
+ * Verify that @p flags are valid for use with @p pin_num, and on success,
+ * return the pin mode described by @p flags in @p mode.
+ *
+ * @param sc GPIO driver instance state.
+ * @param pin_num The pin number to configure.
+ * @param flags The pin flags to be validated.
+ * @param[out] mode On success, will be populated with the GPIO pin mode
+ * defined by @p flags.
+ *
+ * @retval 0 success
+ * @retval EINVAL if @p flags are invalid.
+ */
+static int
+chipc_gpio_check_flags(struct chipc_gpio_softc *sc, uint32_t pin_num,
+ uint32_t flags, chipc_gpio_pin_mode *mode)
+{
+ uint32_t mode_flag, input_flag, output_flag;
+
+ CC_GPIO_ASSERT_VALID_PIN(sc, pin_num);
+
+ mode_flag = flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT |
+ GPIO_PIN_TRISTATE);
+ output_flag = flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW
+ | GPIO_PIN_PULSATE);
+ input_flag = flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
+
+ switch (mode_flag) {
+ case GPIO_PIN_OUTPUT:
+ /* No input flag(s) should be set */
+ if (input_flag != 0)
+ return (EINVAL);
+
+ /* Validate our output flag(s) */
+ switch (output_flag) {
+ case GPIO_PIN_PRESET_HIGH:
+ case GPIO_PIN_PRESET_LOW:
+ case (GPIO_PIN_PRESET_HIGH|GPIO_PIN_PULSATE):
+ case (GPIO_PIN_PRESET_LOW|GPIO_PIN_PULSATE):
+ case 0:
+ /* Check for unhandled flags */
+ if ((flags & ~(mode_flag | output_flag)) != 0)
+ return (EINVAL);
+
+ *mode = CC_GPIO_PIN_OUTPUT;
+ return (0);
+
+ default:
+ /* Incompatible output flags */
+ return (EINVAL);
+ }
+
+ case GPIO_PIN_INPUT:
+ /* No output flag(s) should be set */
+ if (output_flag != 0)
+ return (EINVAL);
+
+ /* Validate our input flag(s) */
+ switch (input_flag) {
+ case GPIO_PIN_PULLUP:
+ case GPIO_PIN_PULLDOWN:
+ case 0:
+ /* Check for unhandled flags */
+ if ((flags & ~(mode_flag | input_flag)) != 0)
+ return (EINVAL);
+
+ *mode = CC_GPIO_PIN_INPUT;
+ return (0);
+
+ default:
+ /* Incompatible input flags */
+ return (EINVAL);
+ }
+
+ break;
+
+ case (GPIO_PIN_TRISTATE|GPIO_PIN_OUTPUT):
+ case GPIO_PIN_TRISTATE:
+ /* No input or output flag(s) should be set */
+ if (input_flag != 0 || output_flag != 0)
+ return (EINVAL);
+
+ /* Check for unhandled flags */
+ if ((flags & ~mode_flag) != 0)
+ return (EINVAL);
+
+ *mode = CC_GPIO_PIN_TRISTATE;
+ return (0);
+
+ default:
+ /* Incompatible mode flags */
+ return (EINVAL);
+ }
+}
+
+/**
+ * Return the current pin mode for @p pin_num.
+ *
+ * @param sc GPIO driver instance state.
+ * @param pin_num The pin number to query.
+ */
+static chipc_gpio_pin_mode
+chipc_gpio_pin_get_mode(struct chipc_gpio_softc *sc, uint32_t pin_num)
+{
+ CC_GPIO_LOCK_ASSERT(sc, MA_OWNED);
+ CC_GPIO_ASSERT_VALID_PIN(sc, pin_num);
+
+ if (CC_GPIO_RDFLAG(sc, pin_num, GPIOCTRL)) {
+ return (CC_GPIO_PIN_TRISTATE);
+ } else if (CC_GPIO_RDFLAG(sc, pin_num, GPIOOUTEN)) {
+ return (CC_GPIO_PIN_OUTPUT);
+ } else {
+ return (CC_GPIO_PIN_INPUT);
+ }
+}
+
+static device_method_t chipc_gpio_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, chipc_gpio_probe),
+ DEVMETHOD(device_attach, chipc_gpio_attach),
+ DEVMETHOD(device_detach, chipc_gpio_detach),
+
+ /* GPIO interface */
+ DEVMETHOD(gpio_get_bus, chipc_gpio_get_bus),
+ DEVMETHOD(gpio_pin_max, chipc_gpio_pin_max),
+ DEVMETHOD(gpio_pin_getname, chipc_gpio_pin_getname),
+ DEVMETHOD(gpio_pin_getflags, chipc_gpio_pin_getflags),
+ DEVMETHOD(gpio_pin_getcaps, chipc_gpio_pin_getcaps),
+ DEVMETHOD(gpio_pin_setflags, chipc_gpio_pin_setflags),
+ DEVMETHOD(gpio_pin_get, chipc_gpio_pin_get),
+ DEVMETHOD(gpio_pin_set, chipc_gpio_pin_set),
+ DEVMETHOD(gpio_pin_toggle, chipc_gpio_pin_toggle),
+ DEVMETHOD(gpio_pin_access_32, chipc_gpio_pin_access_32),
+ DEVMETHOD(gpio_pin_config_32, chipc_gpio_pin_config_32),
+
+ DEVMETHOD_END
+};
+
+static devclass_t gpio_devclass;
+
+DEFINE_CLASS_0(gpio, chipc_gpio_driver, chipc_gpio_methods, sizeof(struct chipc_gpio_softc));
+EARLY_DRIVER_MODULE(chipc_gpio, bhnd_chipc, chipc_gpio_driver,
+ gpio_devclass, NULL, NULL, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+
+MODULE_DEPEND(chipc_gpio, bhnd, 1, 1, 1);
+MODULE_DEPEND(chipc_gpio, gpiobus, 1, 1, 1);
+MODULE_VERSION(chipc_gpio, 1);
Index: head/sys/dev/bhnd/cores/chipc/chipc_gpiovar.h
===================================================================
--- head/sys/dev/bhnd/cores/chipc/chipc_gpiovar.h
+++ head/sys/dev/bhnd/cores/chipc/chipc_gpiovar.h
@@ -0,0 +1,161 @@
+/*-
+ * Copyright (c) 2017 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Landon Fuller under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BHND_CORES_CHIPC_CHIPC_GPIOVAR_H_
+#define _BHND_CORES_CHIPC_CHIPC_GPIOVAR_H_
+
+#include <sys/param.h>
+#include <sys/bus.h>
+
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <dev/bhnd/bhnd.h>
+
+/**
+ * ChipCommon GPIO device quirks.
+ */
+enum {
+ /**
+ * No GPIO event support.
+ *
+ * The CHIPC_GPIOEVENT, CHIPC_GPIOEVENT_INTM, and
+ * CHIPC_GPIOEVENT_INTPOLARITY registers are not available.
+ */
+ CC_GPIO_QUIRK_NO_EVENTS = (1<<0),
+
+ /**
+ * No GPIO duty-cycle timer support.
+ *
+ * The CHIPC_GPIOTIMERVAL and CHIPC_GPIOTIMEROUTMASK registers are not
+ * available.
+ */
+ CC_GPIO_QUIRK_NO_DCTIMER = (1<<1),
+
+ /**
+ * No GPIO pull-up/pull-down configuration support.
+ *
+ * The CHIPC_GPIOPU and CHIPC_GPIOPD registers are not available.
+ */
+ CC_GPIO_QUIRK_NO_PULLUPDOWN = (1<<2),
+
+ /**
+ * Do not attach a child gpioc(4) device.
+ *
+ * This is primarily intended for use on bridged Wi-Fi adapters, where
+ * userspace modification of GPIO pin configuration could introduce
+ * significant undesirable behavior.
+ */
+ CC_GPIO_QUIRK_NO_GPIOC = (1<<3),
+};
+
+/** ChipCommon GPIO pin modes */
+typedef enum {
+ CC_GPIO_PIN_INPUT,
+ CC_GPIO_PIN_OUTPUT,
+ CC_GPIO_PIN_TRISTATE
+} chipc_gpio_pin_mode;
+
+/**
+ * A single GPIO update register.
+ */
+struct chipc_gpio_reg {
+ uint32_t value; /**< register update value */
+ uint32_t mask; /**< register update mask */
+};
+
+/**
+ * A GPIO register update descriptor.
+ */
+struct chipc_gpio_update {
+ struct chipc_gpio_reg pullup; /**< CHIPC_GPIOPU changes */
+ struct chipc_gpio_reg pulldown; /**< CHIPC_GPIOPD changes */
+ struct chipc_gpio_reg out; /**< CHIPC_GPIOOUT changes */
+ struct chipc_gpio_reg outen; /**< CHIPC_GPIOOUTEN changes */
+ struct chipc_gpio_reg timeroutmask; /**< CHIPC_GPIOTIMEROUTMASK changes */
+ struct chipc_gpio_reg ctrl; /**< CHIPC_GPIOCTRL changes */
+};
+
+#define CC_GPIO_UPDATE(_upd, _pin, _reg, _val) do { \
+ (_upd)->_reg.mask |= (1 << (_pin)); \
+ if (_val) \
+ (_upd)->_reg.value |= (1 << (_pin)); \
+ else \
+ (_upd)->_reg.value &= (1 << (_pin)); \
+} while(0)
+
+/**
+ * ChipCommon GPIO driver instance state.
+ */
+struct chipc_gpio_softc {
+ device_t dev;
+ device_t gpiobus; /**< attached gpiobus child */
+ struct bhnd_resource *mem_res; /**< chipcommon register block */
+ int mem_rid; /**< resource ID of mem_res */
+ uint32_t quirks; /**< device quirks (see CC_GPIO_QUIRK_*) */
+ struct mtx mtx; /**< lock protecting RMW register access */
+};
+
+#define CC_GPIO_LOCK_INIT(sc) mtx_init(&(sc)->mtx, \
+ device_get_nameunit((sc)->dev), NULL, MTX_DEF)
+#define CC_GPIO_LOCK(sc) mtx_lock(&(sc)->mtx)
+#define CC_GPIO_UNLOCK(sc) mtx_unlock(&(sc)->mtx)
+#define CC_GPIO_LOCK_ASSERT(sc, what) mtx_assert(&(sc)->mtx, what)
+#define CC_GPIO_LOCK_DESTROY(sc) mtx_destroy(&(sc)->mtx)
+
+#define CC_GPIO_WR4(sc, off, val) \
+ bhnd_bus_write_4((sc)->mem_res, (off), (val))
+#define CC_GPIO_WRFLAG(sc, pin_num, flag, val) \
+ CC_GPIO_WR4(sc, CHIPC_ ## flag, \
+ (CC_GPIO_RD4(sc, CHIPC_ ## flag) & ~(1 << pin_num)) | \
+ (val ? (1 << pin_num) : 0))
+
+#define CC_GPIO_RD4(sc, off) \
+ bhnd_bus_read_4((sc)->mem_res, (off))
+#define CC_GPIO_RDFLAG(sc, pin_num, flag) \
+ ((CC_GPIO_RD4(sc, CHIPC_ ## flag) & (1 << pin_num)) != 0)
+
+#define CC_GPIO_NPINS 32
+#define CC_GPIO_VALID_PIN(_pin) \
+ ((_pin) >= 0 && (_pin) < CC_GPIO_NPINS)
+#define CC_GPIO_VALID_PINS(_first, _num) \
+ ((_num) <= CC_GPIO_NPINS && CC_GPIO_NPINS - (_num) >= _first)
+
+#define CC_GPIO_ASSERT_VALID_PIN(sc, pin_num) \
+ KASSERT(CC_GPIO_VALID_PIN(pin_num), ("invalid pin# %" PRIu32, pin_num));
+
+#define CC_GPIO_QUIRK(_sc, _name) \
+ ((_sc)->quirks & CC_GPIO_QUIRK_ ## _name)
+
+#define CC_GPIO_ASSERT_QUIRK(_sc, name) \
+ KASSERT(CC_GPIO_QUIRK((_sc), name), ("quirk " __STRING(_name) " not set"))
+
+#endif /* _BHND_PWRCTL_BHND_PWRCTLVAR_H_ */
Index: head/sys/dev/bhnd/cores/chipc/chipc_subr.c
===================================================================
--- head/sys/dev/bhnd/cores/chipc/chipc_subr.c
+++ head/sys/dev/bhnd/cores/chipc/chipc_subr.c
@@ -473,7 +473,7 @@
/* Allocate resource */
cr->cr_res = bhnd_alloc_resource(sc->dev,
SYS_RES_MEMORY, &cr->cr_res_rid, cr->cr_addr,
- cr->cr_end, cr->cr_count, 0);
+ cr->cr_end, cr->cr_count, RF_SHAREABLE);
if (cr->cr_res == NULL) {
CHIPC_UNLOCK(sc);
return (ENXIO);
Index: head/sys/dev/bhnd/cores/chipc/chipcreg.h
===================================================================
--- head/sys/dev/bhnd/cores/chipc/chipcreg.h
+++ head/sys/dev/bhnd/cores/chipc/chipcreg.h
@@ -557,7 +557,12 @@
#define CHIPC_SRC_PRESENT 0x00000001
/* gpiotimerval */
-#define CHIPC_GPIO_ONTIME_SHIFT 16
+#define CHIPC_GPIO_ONTIME_SHIFT 16
+#define CHIPC_GPIOTIMERVAL_DEFAULT_ON 10 /**< default 10% on duty cycle */
+#define CHIPC_GPIOTIMERVAL_DEFAULT_OFF 90 /**< default 90% off duty cycle */
+#define CHIPC_GPIOTIMERVAL_DEFAULT \
+ ((CHIPC_GPIOTIMERVAL_DEFAULT_ON << CHIPC_GPIO_ONTIME_SHIFT) | \
+ (CHIPC_GPIOTIMERVAL_DEFAULT_OFF))
/* clockcontrol_n */
#define CHIPC_CN_N1_MASK 0x3f /* n1 control */
Index: head/sys/mips/conf/BCM
===================================================================
--- head/sys/mips/conf/BCM
+++ head/sys/mips/conf/BCM
@@ -67,6 +67,7 @@
#device bgmac # Broadcom GMAC - not yet
+device gpio
device mdio
#Flash
Index: head/sys/mips/conf/SENTRY5
===================================================================
--- head/sys/mips/conf/SENTRY5
+++ head/sys/mips/conf/SENTRY5
@@ -83,6 +83,7 @@
device cfi # parallel flash
device cfid
+device gpio
device uart
device loop
Index: head/sys/modules/bhnd/Makefile
===================================================================
--- head/sys/modules/bhnd/Makefile
+++ head/sys/modules/bhnd/Makefile
@@ -15,11 +15,19 @@
# ChipCommon
SRCS+= chipc.c chipc_subr.c
+
+SRCS+= chipc_gpio.c
+SRCS+= gpio_if.h
+
SRCS+= bhnd_sprom_chipc.c \
bhnd_pmu_chipc.c
-SRCS+= bhnd_pwrctl.c bhnd_pwrctl_subr.c \
- bhnd_pwrctl_if.c bhnd_pwrctl_if.h \
- bhnd_pwrctl_hostb_if.c bhnd_pwrctl_hostb_if.h
+
+SRCS+= bhnd_pwrctl.c \
+ bhnd_pwrctl_subr.c \
+ bhnd_pwrctl_if.c \
+ bhnd_pwrctl_if.h
+SRCS+= bhnd_pwrctl_hostb_if.c bhnd_pwrctl_hostb_if.h
+
SRCS+= bhnd_chipc_if.c bhnd_chipc_if.h
# PMU
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jul 13, 5:15 PM (53 m, 28 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
35039403
Default Alt Text
D12708.diff (31 KB)
Attached To
Mode
D12708: bhnd(4): ChipCommon GPIO driver
Attached
Detach File
Event Timeline
Log In to Comment