12f909a9fSLandon J. Fuller /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
36e778a7eSPedro F. Giffuni *
42f909a9fSLandon J. Fuller * Copyright (c) 2017 The FreeBSD Foundation
52f909a9fSLandon J. Fuller *
62f909a9fSLandon J. Fuller * This software was developed by Landon Fuller under sponsorship from
72f909a9fSLandon J. Fuller * the FreeBSD Foundation.
82f909a9fSLandon J. Fuller *
92f909a9fSLandon J. Fuller * Redistribution and use in source and binary forms, with or without
102f909a9fSLandon J. Fuller * modification, are permitted provided that the following conditions
112f909a9fSLandon J. Fuller * are met:
122f909a9fSLandon J. Fuller * 1. Redistributions of source code must retain the above copyright
132f909a9fSLandon J. Fuller * notice, this list of conditions and the following disclaimer.
142f909a9fSLandon J. Fuller * 2. Redistributions in binary form must reproduce the above copyright
152f909a9fSLandon J. Fuller * notice, this list of conditions and the following disclaimer in the
162f909a9fSLandon J. Fuller * documentation and/or other materials provided with the distribution.
172f909a9fSLandon J. Fuller *
182f909a9fSLandon J. Fuller * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
192f909a9fSLandon J. Fuller * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
202f909a9fSLandon J. Fuller * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
212f909a9fSLandon J. Fuller * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
222f909a9fSLandon J. Fuller * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
232f909a9fSLandon J. Fuller * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
242f909a9fSLandon J. Fuller * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
252f909a9fSLandon J. Fuller * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
262f909a9fSLandon J. Fuller * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
272f909a9fSLandon J. Fuller * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
282f909a9fSLandon J. Fuller * SUCH DAMAGE.
292f909a9fSLandon J. Fuller */
302f909a9fSLandon J. Fuller
312f909a9fSLandon J. Fuller #include <sys/param.h>
322f909a9fSLandon J. Fuller #include <sys/kernel.h>
332f909a9fSLandon J. Fuller #include <sys/bus.h>
342f909a9fSLandon J. Fuller #include <sys/gpio.h>
352f909a9fSLandon J. Fuller #include <sys/limits.h>
362f909a9fSLandon J. Fuller #include <sys/module.h>
372f909a9fSLandon J. Fuller
382f909a9fSLandon J. Fuller #include <machine/_inttypes.h>
392f909a9fSLandon J. Fuller #include <machine/bus.h>
402f909a9fSLandon J. Fuller #include <sys/rman.h>
412f909a9fSLandon J. Fuller #include <machine/resource.h>
422f909a9fSLandon J. Fuller
432f909a9fSLandon J. Fuller #include <dev/bhnd/bhnd.h>
442f909a9fSLandon J. Fuller #include <dev/gpio/gpiobusvar.h>
452f909a9fSLandon J. Fuller
462f909a9fSLandon J. Fuller #include "gpio_if.h"
472f909a9fSLandon J. Fuller
482f909a9fSLandon J. Fuller #include "bhnd_nvram_map.h"
492f909a9fSLandon J. Fuller
502f909a9fSLandon J. Fuller #include "chipcreg.h"
512f909a9fSLandon J. Fuller #include "chipc_gpiovar.h"
522f909a9fSLandon J. Fuller
532f909a9fSLandon J. Fuller /*
542f909a9fSLandon J. Fuller * ChipCommon GPIO driver
552f909a9fSLandon J. Fuller */
562f909a9fSLandon J. Fuller
572f909a9fSLandon J. Fuller static int chipc_gpio_check_flags(
582f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc,
592f909a9fSLandon J. Fuller uint32_t pin_num, uint32_t flags,
602f909a9fSLandon J. Fuller chipc_gpio_pin_mode *mode);
612f909a9fSLandon J. Fuller static int chipc_gpio_pin_update(
622f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc,
632f909a9fSLandon J. Fuller struct chipc_gpio_update *update,
642f909a9fSLandon J. Fuller uint32_t pin_num, uint32_t flags);
652f909a9fSLandon J. Fuller static int chipc_gpio_commit_update(
662f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc,
672f909a9fSLandon J. Fuller struct chipc_gpio_update *update);
682f909a9fSLandon J. Fuller static chipc_gpio_pin_mode chipc_gpio_pin_get_mode(
692f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc,
702f909a9fSLandon J. Fuller uint32_t pin_num);
712f909a9fSLandon J. Fuller
722f909a9fSLandon J. Fuller /* Debugging flags */
732f909a9fSLandon J. Fuller static u_long chipc_gpio_debug = 0;
742f909a9fSLandon J. Fuller TUNABLE_ULONG("hw.bhnd_chipc.gpio_debug", &chipc_gpio_debug);
752f909a9fSLandon J. Fuller
762f909a9fSLandon J. Fuller enum {
772f909a9fSLandon J. Fuller /** Allow userspace GPIO access on bridged network (e.g. wi-fi)
782f909a9fSLandon J. Fuller * adapters */
792f909a9fSLandon J. Fuller CC_GPIO_DEBUG_ADAPTER_GPIOC = 1 << 0,
802f909a9fSLandon J. Fuller };
812f909a9fSLandon J. Fuller
822f909a9fSLandon J. Fuller #define CC_GPIO_DEBUG(_type) (CC_GPIO_DEBUG_ ## _type & chipc_gpio_debug)
832f909a9fSLandon J. Fuller
842f909a9fSLandon J. Fuller static struct bhnd_device_quirk chipc_gpio_quirks[];
852f909a9fSLandon J. Fuller
862f909a9fSLandon J. Fuller /* Supported parent core device identifiers */
872f909a9fSLandon J. Fuller static const struct bhnd_device chipc_gpio_devices[] = {
882f909a9fSLandon J. Fuller BHND_DEVICE(BCM, CC, "Broadcom ChipCommon GPIO", chipc_gpio_quirks),
892f909a9fSLandon J. Fuller BHND_DEVICE_END
902f909a9fSLandon J. Fuller };
912f909a9fSLandon J. Fuller
922f909a9fSLandon J. Fuller /* Device quirks table */
932f909a9fSLandon J. Fuller static struct bhnd_device_quirk chipc_gpio_quirks[] = {
942f909a9fSLandon J. Fuller BHND_CORE_QUIRK (HWREV_LTE(10), CC_GPIO_QUIRK_NO_EVENTS),
952f909a9fSLandon J. Fuller BHND_CORE_QUIRK (HWREV_LTE(15), CC_GPIO_QUIRK_NO_DCTIMER),
962f909a9fSLandon J. Fuller BHND_CORE_QUIRK (HWREV_LTE(19), CC_GPIO_QUIRK_NO_PULLUPDOWN),
972f909a9fSLandon J. Fuller
982f909a9fSLandon J. Fuller BHND_DEVICE_QUIRK_END
992f909a9fSLandon J. Fuller };
1002f909a9fSLandon J. Fuller
1012f909a9fSLandon J. Fuller static int
chipc_gpio_probe(device_t dev)1022f909a9fSLandon J. Fuller chipc_gpio_probe(device_t dev)
1032f909a9fSLandon J. Fuller {
1042f909a9fSLandon J. Fuller const struct bhnd_device *id;
1052f909a9fSLandon J. Fuller device_t chipc;
1062f909a9fSLandon J. Fuller
1072f909a9fSLandon J. Fuller /* Look for compatible chipc parent */
1082f909a9fSLandon J. Fuller chipc = device_get_parent(dev);
1092f909a9fSLandon J. Fuller id = bhnd_device_lookup(chipc, chipc_gpio_devices,
1102f909a9fSLandon J. Fuller sizeof(chipc_gpio_devices[0]));
1112f909a9fSLandon J. Fuller if (id == NULL)
1122f909a9fSLandon J. Fuller return (ENXIO);
1132f909a9fSLandon J. Fuller
1142f909a9fSLandon J. Fuller device_set_desc(dev, id->desc);
1152f909a9fSLandon J. Fuller return (BUS_PROBE_NOWILDCARD);
1162f909a9fSLandon J. Fuller }
1172f909a9fSLandon J. Fuller
1182f909a9fSLandon J. Fuller static int
chipc_gpio_attach(device_t dev)1192f909a9fSLandon J. Fuller chipc_gpio_attach(device_t dev)
1202f909a9fSLandon J. Fuller {
1212f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
1222f909a9fSLandon J. Fuller device_t chipc;
1232f909a9fSLandon J. Fuller int error;
1242f909a9fSLandon J. Fuller
1252f909a9fSLandon J. Fuller chipc = device_get_parent(dev);
1262f909a9fSLandon J. Fuller
1272f909a9fSLandon J. Fuller sc = device_get_softc(dev);
1282f909a9fSLandon J. Fuller sc->dev = dev;
1292f909a9fSLandon J. Fuller sc->quirks = bhnd_device_quirks(chipc, chipc_gpio_devices,
1302f909a9fSLandon J. Fuller sizeof(chipc_gpio_devices[0]));
1312f909a9fSLandon J. Fuller
1322f909a9fSLandon J. Fuller /* If this is a bridged wi-fi adapter, we don't want to support
1332f909a9fSLandon J. Fuller * userspace requests via gpioc(4) */
1342f909a9fSLandon J. Fuller if (bhnd_get_attach_type(chipc) == BHND_ATTACH_ADAPTER) {
1352f909a9fSLandon J. Fuller if (!CC_GPIO_DEBUG(ADAPTER_GPIOC))
1362f909a9fSLandon J. Fuller sc->quirks |= CC_GPIO_QUIRK_NO_GPIOC;
1372f909a9fSLandon J. Fuller }
1382f909a9fSLandon J. Fuller
1392f909a9fSLandon J. Fuller CC_GPIO_LOCK_INIT(sc);
1402f909a9fSLandon J. Fuller
1412f909a9fSLandon J. Fuller sc->mem_rid = 0;
1422f909a9fSLandon J. Fuller sc->mem_res = bhnd_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
1432f909a9fSLandon J. Fuller RF_ACTIVE|RF_SHAREABLE);
1442f909a9fSLandon J. Fuller if (sc->mem_res == NULL) {
1452f909a9fSLandon J. Fuller device_printf(dev, "failed to allocate chipcommon registers\n");
1462f909a9fSLandon J. Fuller error = ENXIO;
1472f909a9fSLandon J. Fuller goto failed;
1482f909a9fSLandon J. Fuller }
1492f909a9fSLandon J. Fuller
1502f909a9fSLandon J. Fuller /*
1512f909a9fSLandon J. Fuller * If hardware 'pulsate' support is available, set the timer duty-cycle
1522f909a9fSLandon J. Fuller * to either the NVRAM 'leddc' value if available, or the default duty
1532f909a9fSLandon J. Fuller * cycle.
1542f909a9fSLandon J. Fuller */
1552f909a9fSLandon J. Fuller if (!CC_GPIO_QUIRK(sc, NO_DCTIMER)) {
1562f909a9fSLandon J. Fuller uint32_t dctimerval;
1572f909a9fSLandon J. Fuller
1582f909a9fSLandon J. Fuller error = bhnd_nvram_getvar_uint32(chipc, BHND_NVAR_LEDDC,
1592f909a9fSLandon J. Fuller &dctimerval);
1602f909a9fSLandon J. Fuller if (error == ENOENT) {
1612f909a9fSLandon J. Fuller /* Fall back on default duty cycle */
1622f909a9fSLandon J. Fuller dctimerval = CHIPC_GPIOTIMERVAL_DEFAULT;
1632f909a9fSLandon J. Fuller } else if (error) {
1642f909a9fSLandon J. Fuller device_printf(dev, "error reading %s from NVRAM: %d\n",
1652f909a9fSLandon J. Fuller BHND_NVAR_LEDDC, error);
1662f909a9fSLandon J. Fuller goto failed;
1672f909a9fSLandon J. Fuller }
1682f909a9fSLandon J. Fuller
1692f909a9fSLandon J. Fuller CC_GPIO_WR4(sc, CHIPC_GPIOTIMERVAL, dctimerval);
1702f909a9fSLandon J. Fuller }
1712f909a9fSLandon J. Fuller
1722f909a9fSLandon J. Fuller /* Attach gpioc/gpiobus */
1732f909a9fSLandon J. Fuller if (CC_GPIO_QUIRK(sc, NO_GPIOC)) {
1742f909a9fSLandon J. Fuller sc->gpiobus = NULL;
1752f909a9fSLandon J. Fuller } else {
1762f909a9fSLandon J. Fuller if ((sc->gpiobus = gpiobus_attach_bus(dev)) == NULL) {
1772f909a9fSLandon J. Fuller device_printf(dev, "failed to attach gpiobus\n");
1782f909a9fSLandon J. Fuller error = ENXIO;
1792f909a9fSLandon J. Fuller goto failed;
1802f909a9fSLandon J. Fuller }
1812f909a9fSLandon J. Fuller }
1822f909a9fSLandon J. Fuller
1832f909a9fSLandon J. Fuller /* Register as the bus GPIO provider */
1842f909a9fSLandon J. Fuller if ((error = bhnd_register_provider(dev, BHND_SERVICE_GPIO))) {
1852f909a9fSLandon J. Fuller device_printf(dev, "failed to register gpio with bus: %d\n",
1862f909a9fSLandon J. Fuller error);
1872f909a9fSLandon J. Fuller goto failed;
1882f909a9fSLandon J. Fuller }
1892f909a9fSLandon J. Fuller
1902f909a9fSLandon J. Fuller return (0);
1912f909a9fSLandon J. Fuller
1922f909a9fSLandon J. Fuller failed:
1932f909a9fSLandon J. Fuller device_delete_children(dev);
1942f909a9fSLandon J. Fuller
1952f909a9fSLandon J. Fuller if (sc->mem_res != NULL) {
1962f909a9fSLandon J. Fuller bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid,
1972f909a9fSLandon J. Fuller sc->mem_res);
1982f909a9fSLandon J. Fuller }
1992f909a9fSLandon J. Fuller
2002f909a9fSLandon J. Fuller CC_GPIO_LOCK_DESTROY(sc);
2012f909a9fSLandon J. Fuller
2022f909a9fSLandon J. Fuller return (error);
2032f909a9fSLandon J. Fuller }
2042f909a9fSLandon J. Fuller
2052f909a9fSLandon J. Fuller static int
chipc_gpio_detach(device_t dev)2062f909a9fSLandon J. Fuller chipc_gpio_detach(device_t dev)
2072f909a9fSLandon J. Fuller {
2082f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
2092f909a9fSLandon J. Fuller int error;
2102f909a9fSLandon J. Fuller
2112f909a9fSLandon J. Fuller sc = device_get_softc(dev);
2122f909a9fSLandon J. Fuller
2132f909a9fSLandon J. Fuller if ((error = bus_generic_detach(dev)))
2142f909a9fSLandon J. Fuller return (error);
2152f909a9fSLandon J. Fuller
2162f909a9fSLandon J. Fuller if ((error = bhnd_deregister_provider(dev, BHND_SERVICE_ANY)))
2172f909a9fSLandon J. Fuller return (error);
2182f909a9fSLandon J. Fuller
2192f909a9fSLandon J. Fuller bhnd_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem_res);
2202f909a9fSLandon J. Fuller CC_GPIO_LOCK_DESTROY(sc);
2212f909a9fSLandon J. Fuller
2222f909a9fSLandon J. Fuller return (0);
2232f909a9fSLandon J. Fuller }
2242f909a9fSLandon J. Fuller
2252f909a9fSLandon J. Fuller static device_t
chipc_gpio_get_bus(device_t dev)2262f909a9fSLandon J. Fuller chipc_gpio_get_bus(device_t dev)
2272f909a9fSLandon J. Fuller {
2282f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc = device_get_softc(dev);
2292f909a9fSLandon J. Fuller
2302f909a9fSLandon J. Fuller return (sc->gpiobus);
2312f909a9fSLandon J. Fuller }
2322f909a9fSLandon J. Fuller
2332f909a9fSLandon J. Fuller static int
chipc_gpio_pin_max(device_t dev,int * maxpin)2342f909a9fSLandon J. Fuller chipc_gpio_pin_max(device_t dev, int *maxpin)
2352f909a9fSLandon J. Fuller {
2362f909a9fSLandon J. Fuller *maxpin = CC_GPIO_NPINS-1;
2372f909a9fSLandon J. Fuller return (0);
2382f909a9fSLandon J. Fuller }
2392f909a9fSLandon J. Fuller
2402f909a9fSLandon J. Fuller static int
chipc_gpio_pin_set(device_t dev,uint32_t pin_num,uint32_t pin_value)2412f909a9fSLandon J. Fuller chipc_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
2422f909a9fSLandon J. Fuller {
2432f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
2442f909a9fSLandon J. Fuller bool pin_high;
2452f909a9fSLandon J. Fuller int error;
2462f909a9fSLandon J. Fuller
2472f909a9fSLandon J. Fuller sc = device_get_softc(dev);
2482f909a9fSLandon J. Fuller error = 0;
2492f909a9fSLandon J. Fuller
2502f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
2512f909a9fSLandon J. Fuller return (EINVAL);
2522f909a9fSLandon J. Fuller
2532f909a9fSLandon J. Fuller switch (pin_value) {
2542f909a9fSLandon J. Fuller case GPIO_PIN_HIGH:
2552f909a9fSLandon J. Fuller pin_high = true;
2562f909a9fSLandon J. Fuller break;
2572f909a9fSLandon J. Fuller case GPIO_PIN_LOW:
2582f909a9fSLandon J. Fuller pin_high = false;
2592f909a9fSLandon J. Fuller break;
2602f909a9fSLandon J. Fuller default:
2612f909a9fSLandon J. Fuller return (EINVAL);
2622f909a9fSLandon J. Fuller }
2632f909a9fSLandon J. Fuller
2642f909a9fSLandon J. Fuller CC_GPIO_LOCK(sc);
2652f909a9fSLandon J. Fuller
2662f909a9fSLandon J. Fuller switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
2672f909a9fSLandon J. Fuller case CC_GPIO_PIN_INPUT:
2682f909a9fSLandon J. Fuller case CC_GPIO_PIN_TRISTATE:
2692f909a9fSLandon J. Fuller error = ENODEV;
2702f909a9fSLandon J. Fuller break;
2712f909a9fSLandon J. Fuller
2722f909a9fSLandon J. Fuller case CC_GPIO_PIN_OUTPUT:
2732f909a9fSLandon J. Fuller CC_GPIO_WRFLAG(sc, pin_num, GPIOOUT, pin_high);
2742f909a9fSLandon J. Fuller break;
2752f909a9fSLandon J. Fuller }
2762f909a9fSLandon J. Fuller
2772f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
2782f909a9fSLandon J. Fuller
2792f909a9fSLandon J. Fuller return (error);
2802f909a9fSLandon J. Fuller }
2812f909a9fSLandon J. Fuller
2822f909a9fSLandon J. Fuller static int
chipc_gpio_pin_get(device_t dev,uint32_t pin_num,uint32_t * pin_value)2832f909a9fSLandon J. Fuller chipc_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
2842f909a9fSLandon J. Fuller {
2852f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
2862f909a9fSLandon J. Fuller bool pin_high;
2872f909a9fSLandon J. Fuller
2882f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
2892f909a9fSLandon J. Fuller return (EINVAL);
2902f909a9fSLandon J. Fuller
2912f909a9fSLandon J. Fuller sc = device_get_softc(dev);
2922f909a9fSLandon J. Fuller pin_high = false;
2932f909a9fSLandon J. Fuller
2942f909a9fSLandon J. Fuller CC_GPIO_LOCK(sc);
2952f909a9fSLandon J. Fuller
2962f909a9fSLandon J. Fuller switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
2972f909a9fSLandon J. Fuller case CC_GPIO_PIN_INPUT:
2982f909a9fSLandon J. Fuller pin_high = CC_GPIO_RDFLAG(sc, pin_num, GPIOIN);
2992f909a9fSLandon J. Fuller break;
3002f909a9fSLandon J. Fuller
3012f909a9fSLandon J. Fuller case CC_GPIO_PIN_OUTPUT:
3022f909a9fSLandon J. Fuller pin_high = CC_GPIO_RDFLAG(sc, pin_num, GPIOOUT);
3032f909a9fSLandon J. Fuller break;
3042f909a9fSLandon J. Fuller
3052f909a9fSLandon J. Fuller case CC_GPIO_PIN_TRISTATE:
3062f909a9fSLandon J. Fuller pin_high = false;
3072f909a9fSLandon J. Fuller break;
3082f909a9fSLandon J. Fuller }
3092f909a9fSLandon J. Fuller
3102f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
3112f909a9fSLandon J. Fuller
3122f909a9fSLandon J. Fuller *pin_value = pin_high ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
3132f909a9fSLandon J. Fuller
3142f909a9fSLandon J. Fuller return (0);
3152f909a9fSLandon J. Fuller }
3162f909a9fSLandon J. Fuller
3172f909a9fSLandon J. Fuller static int
chipc_gpio_pin_toggle(device_t dev,uint32_t pin_num)3182f909a9fSLandon J. Fuller chipc_gpio_pin_toggle(device_t dev, uint32_t pin_num)
3192f909a9fSLandon J. Fuller {
3202f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
3212f909a9fSLandon J. Fuller bool pin_high;
3222f909a9fSLandon J. Fuller int error;
3232f909a9fSLandon J. Fuller
3242f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
3252f909a9fSLandon J. Fuller return (EINVAL);
3262f909a9fSLandon J. Fuller
3272f909a9fSLandon J. Fuller sc = device_get_softc(dev);
3282f909a9fSLandon J. Fuller error = 0;
3292f909a9fSLandon J. Fuller
3302f909a9fSLandon J. Fuller CC_GPIO_LOCK(sc);
3312f909a9fSLandon J. Fuller
3322f909a9fSLandon J. Fuller switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
3332f909a9fSLandon J. Fuller case CC_GPIO_PIN_INPUT:
3342f909a9fSLandon J. Fuller case CC_GPIO_PIN_TRISTATE:
3352f909a9fSLandon J. Fuller error = ENODEV;
3362f909a9fSLandon J. Fuller break;
3372f909a9fSLandon J. Fuller
3382f909a9fSLandon J. Fuller case CC_GPIO_PIN_OUTPUT:
3392f909a9fSLandon J. Fuller pin_high = CC_GPIO_RDFLAG(sc, pin_num, GPIOOUT);
3402f909a9fSLandon J. Fuller CC_GPIO_WRFLAG(sc, pin_num, GPIOOUT, !pin_high);
3412f909a9fSLandon J. Fuller break;
3422f909a9fSLandon J. Fuller }
3432f909a9fSLandon J. Fuller
3442f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
3452f909a9fSLandon J. Fuller
3462f909a9fSLandon J. Fuller return (error);
3472f909a9fSLandon J. Fuller }
3482f909a9fSLandon J. Fuller
3492f909a9fSLandon J. Fuller static int
chipc_gpio_pin_getcaps(device_t dev,uint32_t pin_num,uint32_t * caps)3502f909a9fSLandon J. Fuller chipc_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
3512f909a9fSLandon J. Fuller {
3522f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc = device_get_softc(dev);
3532f909a9fSLandon J. Fuller
3542f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
3552f909a9fSLandon J. Fuller return (EINVAL);
3562f909a9fSLandon J. Fuller
3572f909a9fSLandon J. Fuller *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
3582f909a9fSLandon J. Fuller
3592f909a9fSLandon J. Fuller if (!CC_GPIO_QUIRK(sc, NO_PULLUPDOWN))
3602f909a9fSLandon J. Fuller *caps |= (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
3612f909a9fSLandon J. Fuller
3622f909a9fSLandon J. Fuller if (!CC_GPIO_QUIRK(sc, NO_DCTIMER))
3632f909a9fSLandon J. Fuller *caps |= GPIO_PIN_PULSATE;
3642f909a9fSLandon J. Fuller
3652f909a9fSLandon J. Fuller return (0);
3662f909a9fSLandon J. Fuller }
3672f909a9fSLandon J. Fuller
3682f909a9fSLandon J. Fuller static int
chipc_gpio_pin_getflags(device_t dev,uint32_t pin_num,uint32_t * flags)3692f909a9fSLandon J. Fuller chipc_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
3702f909a9fSLandon J. Fuller {
3712f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc = device_get_softc(dev);
3722f909a9fSLandon J. Fuller
3732f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
3742f909a9fSLandon J. Fuller return (EINVAL);
3752f909a9fSLandon J. Fuller
3762f909a9fSLandon J. Fuller CC_GPIO_LOCK(sc);
3772f909a9fSLandon J. Fuller
3782f909a9fSLandon J. Fuller switch (chipc_gpio_pin_get_mode(sc, pin_num)) {
3792f909a9fSLandon J. Fuller case CC_GPIO_PIN_INPUT:
3802f909a9fSLandon J. Fuller *flags = GPIO_PIN_INPUT;
3812f909a9fSLandon J. Fuller
3822f909a9fSLandon J. Fuller if (!CC_GPIO_QUIRK(sc, NO_PULLUPDOWN)) {
3832f909a9fSLandon J. Fuller if (CC_GPIO_RDFLAG(sc, pin_num, GPIOPU)) {
3842f909a9fSLandon J. Fuller *flags |= GPIO_PIN_PULLUP;
3852f909a9fSLandon J. Fuller } else if (CC_GPIO_RDFLAG(sc, pin_num, GPIOPD)) {
3862f909a9fSLandon J. Fuller *flags |= GPIO_PIN_PULLDOWN;
3872f909a9fSLandon J. Fuller }
3882f909a9fSLandon J. Fuller }
3892f909a9fSLandon J. Fuller break;
3902f909a9fSLandon J. Fuller
3912f909a9fSLandon J. Fuller case CC_GPIO_PIN_OUTPUT:
3922f909a9fSLandon J. Fuller *flags = GPIO_PIN_OUTPUT;
3932f909a9fSLandon J. Fuller
3942f909a9fSLandon J. Fuller if (!CC_GPIO_QUIRK(sc, NO_DCTIMER)) {
3952f909a9fSLandon J. Fuller if (CC_GPIO_RDFLAG(sc, pin_num, GPIOTIMEROUTMASK))
3962f909a9fSLandon J. Fuller *flags |= GPIO_PIN_PULSATE;
3972f909a9fSLandon J. Fuller }
3982f909a9fSLandon J. Fuller
3992f909a9fSLandon J. Fuller break;
4002f909a9fSLandon J. Fuller
4012f909a9fSLandon J. Fuller case CC_GPIO_PIN_TRISTATE:
4022f909a9fSLandon J. Fuller *flags = GPIO_PIN_TRISTATE|GPIO_PIN_OUTPUT;
4032f909a9fSLandon J. Fuller break;
4042f909a9fSLandon J. Fuller }
4052f909a9fSLandon J. Fuller
4062f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
4072f909a9fSLandon J. Fuller
4082f909a9fSLandon J. Fuller return (0);
4092f909a9fSLandon J. Fuller }
4102f909a9fSLandon J. Fuller
4112f909a9fSLandon J. Fuller static int
chipc_gpio_pin_getname(device_t dev,uint32_t pin_num,char * name)4122f909a9fSLandon J. Fuller chipc_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
4132f909a9fSLandon J. Fuller {
4142f909a9fSLandon J. Fuller int ret;
4152f909a9fSLandon J. Fuller
4162f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
4172f909a9fSLandon J. Fuller return (EINVAL);
4182f909a9fSLandon J. Fuller
4192f909a9fSLandon J. Fuller ret = snprintf(name, GPIOMAXNAME, "bhnd_gpio%02" PRIu32, pin_num);
4202f909a9fSLandon J. Fuller
4212f909a9fSLandon J. Fuller if (ret < 0)
4222f909a9fSLandon J. Fuller return (ENXIO);
4232f909a9fSLandon J. Fuller
4242f909a9fSLandon J. Fuller if (ret >= GPIOMAXNAME)
4252f909a9fSLandon J. Fuller return (ENOMEM);
4262f909a9fSLandon J. Fuller
4272f909a9fSLandon J. Fuller return (0);
4282f909a9fSLandon J. Fuller }
4292f909a9fSLandon J. Fuller
4302f909a9fSLandon J. Fuller static int
chipc_gpio_pin_setflags(device_t dev,uint32_t pin_num,uint32_t flags)4312f909a9fSLandon J. Fuller chipc_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
4322f909a9fSLandon J. Fuller {
4332f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
4342f909a9fSLandon J. Fuller struct chipc_gpio_update upd;
4352f909a9fSLandon J. Fuller int error;
4362f909a9fSLandon J. Fuller
4372f909a9fSLandon J. Fuller sc = device_get_softc(dev);
4382f909a9fSLandon J. Fuller
4392f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
4402f909a9fSLandon J. Fuller return (EINVAL);
4412f909a9fSLandon J. Fuller
4422f909a9fSLandon J. Fuller /* Produce an update descriptor */
4432f909a9fSLandon J. Fuller memset(&upd, 0, sizeof(upd));
4442f909a9fSLandon J. Fuller if ((error = chipc_gpio_pin_update(sc, &upd, pin_num, flags)))
4452f909a9fSLandon J. Fuller return (error);
4462f909a9fSLandon J. Fuller
4472f909a9fSLandon J. Fuller /* Commit the update */
4482f909a9fSLandon J. Fuller CC_GPIO_LOCK(sc);
4492f909a9fSLandon J. Fuller error = chipc_gpio_commit_update(sc, &upd);
4502f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
4512f909a9fSLandon J. Fuller
4522f909a9fSLandon J. Fuller return (error);
4532f909a9fSLandon J. Fuller }
4542f909a9fSLandon J. Fuller
4552f909a9fSLandon J. Fuller 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)4562f909a9fSLandon J. Fuller chipc_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins,
4572f909a9fSLandon J. Fuller uint32_t change_pins, uint32_t *orig_pins)
4582f909a9fSLandon J. Fuller {
4592f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
4602f909a9fSLandon J. Fuller struct chipc_gpio_update upd;
4612f909a9fSLandon J. Fuller uint32_t out, outen, ctrl;
4622f909a9fSLandon J. Fuller uint32_t num_pins;
4632f909a9fSLandon J. Fuller int error;
4642f909a9fSLandon J. Fuller
4652f909a9fSLandon J. Fuller sc = device_get_softc(dev);
4662f909a9fSLandon J. Fuller
4672f909a9fSLandon J. Fuller if (first_pin >= CC_GPIO_NPINS)
4682f909a9fSLandon J. Fuller return (EINVAL);
4692f909a9fSLandon J. Fuller
4702f909a9fSLandon J. Fuller /* Determine the actual number of referenced pins */
4712f909a9fSLandon J. Fuller if (clear_pins == 0 && change_pins == 0) {
4722f909a9fSLandon J. Fuller num_pins = CC_GPIO_NPINS - first_pin;
4732f909a9fSLandon J. Fuller } else {
4742f909a9fSLandon J. Fuller int num_clear_pins, num_change_pins;
4752f909a9fSLandon J. Fuller
4762f909a9fSLandon J. Fuller num_clear_pins = flsl((u_long)clear_pins);
4772f909a9fSLandon J. Fuller num_change_pins = flsl((u_long)change_pins);
4782f909a9fSLandon J. Fuller num_pins = MAX(num_clear_pins, num_change_pins);
4792f909a9fSLandon J. Fuller }
4802f909a9fSLandon J. Fuller
4812f909a9fSLandon J. Fuller /* Validate the full pin range */
4822f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PINS(first_pin, num_pins))
4832f909a9fSLandon J. Fuller return (EINVAL);
4842f909a9fSLandon J. Fuller
4852f909a9fSLandon J. Fuller /* Produce an update descriptor for all pins, relative to the current
4862f909a9fSLandon J. Fuller * pin state */
4872f909a9fSLandon J. Fuller CC_GPIO_LOCK(sc);
4882f909a9fSLandon J. Fuller memset(&upd, 0, sizeof(upd));
4892f909a9fSLandon J. Fuller
4902f909a9fSLandon J. Fuller out = CC_GPIO_RD4(sc, CHIPC_GPIOOUT);
4912f909a9fSLandon J. Fuller outen = CC_GPIO_RD4(sc, CHIPC_GPIOOUTEN);
4922f909a9fSLandon J. Fuller ctrl = CC_GPIO_RD4(sc, CHIPC_GPIOCTRL);
4932f909a9fSLandon J. Fuller
4942f909a9fSLandon J. Fuller for (uint32_t i = 0; i < num_pins; i++) {
4952f909a9fSLandon J. Fuller uint32_t pin;
4962f909a9fSLandon J. Fuller bool pin_high;
4972f909a9fSLandon J. Fuller
4982f909a9fSLandon J. Fuller pin = first_pin + i;
4992f909a9fSLandon J. Fuller
5002f909a9fSLandon J. Fuller /* The pin must be configured for output */
5012f909a9fSLandon J. Fuller if ((outen & (1 << pin)) == 0) {
5022f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
5032f909a9fSLandon J. Fuller return (EINVAL);
5042f909a9fSLandon J. Fuller }
5052f909a9fSLandon J. Fuller
5062f909a9fSLandon J. Fuller /* The pin must not tristated */
5072f909a9fSLandon J. Fuller if ((ctrl & (1 << pin)) != 0) {
5082f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
5092f909a9fSLandon J. Fuller return (EINVAL);
5102f909a9fSLandon J. Fuller }
5112f909a9fSLandon J. Fuller
5122f909a9fSLandon J. Fuller /* Fetch current state */
5132f909a9fSLandon J. Fuller if (out & (1 << pin)) {
5142f909a9fSLandon J. Fuller pin_high = true;
5152f909a9fSLandon J. Fuller } else {
5162f909a9fSLandon J. Fuller pin_high = false;
5172f909a9fSLandon J. Fuller }
5182f909a9fSLandon J. Fuller
5192f909a9fSLandon J. Fuller /* Apply clear/toggle request */
5202f909a9fSLandon J. Fuller if (clear_pins & (1 << pin))
5212f909a9fSLandon J. Fuller pin_high = false;
5222f909a9fSLandon J. Fuller
5232f909a9fSLandon J. Fuller if (change_pins & (1 << pin))
5242f909a9fSLandon J. Fuller pin_high = !pin_high;
5252f909a9fSLandon J. Fuller
5262f909a9fSLandon J. Fuller /* Add to our update descriptor */
5272f909a9fSLandon J. Fuller CC_GPIO_UPDATE(&upd, pin, out, pin_high);
5282f909a9fSLandon J. Fuller }
5292f909a9fSLandon J. Fuller
5302f909a9fSLandon J. Fuller /* Commit the update */
5312f909a9fSLandon J. Fuller error = chipc_gpio_commit_update(sc, &upd);
5322f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
5332f909a9fSLandon J. Fuller
5342f909a9fSLandon J. Fuller return (error);
5352f909a9fSLandon J. Fuller }
5362f909a9fSLandon J. Fuller
5372f909a9fSLandon J. Fuller static int
chipc_gpio_pin_config_32(device_t dev,uint32_t first_pin,uint32_t num_pins,uint32_t * pin_flags)5382f909a9fSLandon J. Fuller chipc_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins,
5392f909a9fSLandon J. Fuller uint32_t *pin_flags)
5402f909a9fSLandon J. Fuller {
5412f909a9fSLandon J. Fuller struct chipc_gpio_softc *sc;
5422f909a9fSLandon J. Fuller struct chipc_gpio_update upd;
5432f909a9fSLandon J. Fuller int error;
5442f909a9fSLandon J. Fuller
5452f909a9fSLandon J. Fuller sc = device_get_softc(dev);
5462f909a9fSLandon J. Fuller
5472f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PINS(first_pin, num_pins))
5482f909a9fSLandon J. Fuller return (EINVAL);
5492f909a9fSLandon J. Fuller
5502f909a9fSLandon J. Fuller /* Produce an update descriptor */
5512f909a9fSLandon J. Fuller memset(&upd, 0, sizeof(upd));
5522f909a9fSLandon J. Fuller for (uint32_t i = 0; i < num_pins; i++) {
5532f909a9fSLandon J. Fuller uint32_t pin, flags;
5542f909a9fSLandon J. Fuller
5552f909a9fSLandon J. Fuller pin = first_pin + i;
5562f909a9fSLandon J. Fuller flags = pin_flags[i];
5572f909a9fSLandon J. Fuller
5582f909a9fSLandon J. Fuller /* As per the gpio_config_32 API documentation, any pins for
5592f909a9fSLandon J. Fuller * which neither GPIO_PIN_OUTPUT or GPIO_PIN_INPUT are set
5602f909a9fSLandon J. Fuller * should be ignored and left unmodified */
5612f909a9fSLandon J. Fuller if ((flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) == 0)
5622f909a9fSLandon J. Fuller continue;
5632f909a9fSLandon J. Fuller
5642f909a9fSLandon J. Fuller if ((error = chipc_gpio_pin_update(sc, &upd, pin, flags)))
5652f909a9fSLandon J. Fuller return (error);
5662f909a9fSLandon J. Fuller }
5672f909a9fSLandon J. Fuller
5682f909a9fSLandon J. Fuller /* Commit the update */
5692f909a9fSLandon J. Fuller CC_GPIO_LOCK(sc);
5702f909a9fSLandon J. Fuller error = chipc_gpio_commit_update(sc, &upd);
5712f909a9fSLandon J. Fuller CC_GPIO_UNLOCK(sc);
5722f909a9fSLandon J. Fuller
5732f909a9fSLandon J. Fuller return (error);
5742f909a9fSLandon J. Fuller }
5752f909a9fSLandon J. Fuller
5762f909a9fSLandon J. Fuller /**
5772f909a9fSLandon J. Fuller * Commit a single @p reg register update.
5782f909a9fSLandon J. Fuller */
5792f909a9fSLandon J. Fuller static void
chipc_gpio_commit_reg(struct chipc_gpio_softc * sc,bus_size_t offset,struct chipc_gpio_reg * reg)5802f909a9fSLandon J. Fuller chipc_gpio_commit_reg(struct chipc_gpio_softc *sc, bus_size_t offset,
5812f909a9fSLandon J. Fuller struct chipc_gpio_reg *reg)
5822f909a9fSLandon J. Fuller {
5832f909a9fSLandon J. Fuller uint32_t value;
5842f909a9fSLandon J. Fuller
5852f909a9fSLandon J. Fuller CC_GPIO_LOCK_ASSERT(sc, MA_OWNED);
5862f909a9fSLandon J. Fuller
5872f909a9fSLandon J. Fuller if (reg->mask == 0)
5882f909a9fSLandon J. Fuller return;
5892f909a9fSLandon J. Fuller
5902f909a9fSLandon J. Fuller value = bhnd_bus_read_4(sc->mem_res, offset);
5912f909a9fSLandon J. Fuller value &= ~reg->mask;
5922f909a9fSLandon J. Fuller value |= reg->value;
5932f909a9fSLandon J. Fuller
5942f909a9fSLandon J. Fuller bhnd_bus_write_4(sc->mem_res, offset, value);
5952f909a9fSLandon J. Fuller }
5962f909a9fSLandon J. Fuller
5972f909a9fSLandon J. Fuller /**
5982f909a9fSLandon J. Fuller * Commit the set of GPIO register updates described by @p update.
5992f909a9fSLandon J. Fuller */
6002f909a9fSLandon J. Fuller static int
chipc_gpio_commit_update(struct chipc_gpio_softc * sc,struct chipc_gpio_update * update)6012f909a9fSLandon J. Fuller chipc_gpio_commit_update(struct chipc_gpio_softc *sc,
6022f909a9fSLandon J. Fuller struct chipc_gpio_update *update)
6032f909a9fSLandon J. Fuller {
6042f909a9fSLandon J. Fuller CC_GPIO_LOCK_ASSERT(sc, MA_OWNED);
6052f909a9fSLandon J. Fuller
6062f909a9fSLandon J. Fuller /* Commit pulldown/pullup before potentially disabling an output pin */
6072f909a9fSLandon J. Fuller chipc_gpio_commit_reg(sc, CHIPC_GPIOPD, &update->pulldown);
6082f909a9fSLandon J. Fuller chipc_gpio_commit_reg(sc, CHIPC_GPIOPU, &update->pullup);
6092f909a9fSLandon J. Fuller
6102f909a9fSLandon J. Fuller /* Commit output settings before potentially enabling an output pin */
6112f909a9fSLandon J. Fuller chipc_gpio_commit_reg(sc, CHIPC_GPIOTIMEROUTMASK,
6122f909a9fSLandon J. Fuller &update->timeroutmask);
6132f909a9fSLandon J. Fuller chipc_gpio_commit_reg(sc, CHIPC_GPIOOUT, &update->out);
6142f909a9fSLandon J. Fuller
6152f909a9fSLandon J. Fuller /* Commit input/output/tristate modes */
6162f909a9fSLandon J. Fuller chipc_gpio_commit_reg(sc, CHIPC_GPIOOUTEN, &update->outen);
6172f909a9fSLandon J. Fuller chipc_gpio_commit_reg(sc, CHIPC_GPIOCTRL, &update->ctrl);
6182f909a9fSLandon J. Fuller
6192f909a9fSLandon J. Fuller return (0);
6202f909a9fSLandon J. Fuller }
6212f909a9fSLandon J. Fuller
6222f909a9fSLandon J. Fuller /**
6232f909a9fSLandon J. Fuller * Apply the changes described by @p flags for @p pin_num to the given @p update
6242f909a9fSLandon J. Fuller * descriptor.
6252f909a9fSLandon J. Fuller */
6262f909a9fSLandon J. Fuller static int
chipc_gpio_pin_update(struct chipc_gpio_softc * sc,struct chipc_gpio_update * update,uint32_t pin_num,uint32_t flags)6272f909a9fSLandon J. Fuller chipc_gpio_pin_update(struct chipc_gpio_softc *sc,
6282f909a9fSLandon J. Fuller struct chipc_gpio_update *update, uint32_t pin_num, uint32_t flags)
6292f909a9fSLandon J. Fuller {
6302f909a9fSLandon J. Fuller chipc_gpio_pin_mode mode;
6312f909a9fSLandon J. Fuller int error;
6322f909a9fSLandon J. Fuller
6332f909a9fSLandon J. Fuller if (!CC_GPIO_VALID_PIN(pin_num))
6342f909a9fSLandon J. Fuller return (EINVAL);
6352f909a9fSLandon J. Fuller
6362f909a9fSLandon J. Fuller /* Verify flag compatibility and determine the pin mode */
6372f909a9fSLandon J. Fuller if ((error = chipc_gpio_check_flags(sc, pin_num, flags, &mode)))
6382f909a9fSLandon J. Fuller return (error);
6392f909a9fSLandon J. Fuller
6402f909a9fSLandon J. Fuller /* Apply the mode-specific changes */
6412f909a9fSLandon J. Fuller switch (mode) {
6422f909a9fSLandon J. Fuller case CC_GPIO_PIN_INPUT:
6432f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pullup, false);
6442f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pulldown, false);
6452f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, out, false);
6462f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, outen, false);
6472f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, timeroutmask, false);
6482f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, ctrl, false);
6492f909a9fSLandon J. Fuller
6502f909a9fSLandon J. Fuller if (flags & GPIO_PIN_PULLUP) {
6512f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pullup, true);
6522f909a9fSLandon J. Fuller } else if (flags & GPIO_PIN_PULLDOWN) {
6532f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pulldown, true);
6542f909a9fSLandon J. Fuller }
6552f909a9fSLandon J. Fuller
6562f909a9fSLandon J. Fuller return (0);
6572f909a9fSLandon J. Fuller
6582f909a9fSLandon J. Fuller case CC_GPIO_PIN_OUTPUT:
6592f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pullup, false);
6602f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pulldown, false);
6612f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, outen, true);
6622f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, timeroutmask, false);
6632f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, ctrl, false);
6642f909a9fSLandon J. Fuller
6652f909a9fSLandon J. Fuller if (flags & GPIO_PIN_PRESET_HIGH) {
6662f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, out, true);
6672f909a9fSLandon J. Fuller } else if (flags & GPIO_PIN_PRESET_LOW) {
6682f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, out, false);
6692f909a9fSLandon J. Fuller }
6702f909a9fSLandon J. Fuller
6712f909a9fSLandon J. Fuller if (flags & GPIO_PIN_PULSATE)
6722f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, timeroutmask, true);
6732f909a9fSLandon J. Fuller
6742f909a9fSLandon J. Fuller return (0);
6752f909a9fSLandon J. Fuller
6762f909a9fSLandon J. Fuller case CC_GPIO_PIN_TRISTATE:
6772f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pullup, false);
6782f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, pulldown, false);
6792f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, out, false);
6802f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, outen, false);
6812f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, timeroutmask, false);
6822f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, ctrl, true);
6832f909a9fSLandon J. Fuller
6842f909a9fSLandon J. Fuller if (flags & GPIO_PIN_OUTPUT)
6852f909a9fSLandon J. Fuller CC_GPIO_UPDATE(update, pin_num, outen, true);
6862f909a9fSLandon J. Fuller
6872f909a9fSLandon J. Fuller return (0);
6882f909a9fSLandon J. Fuller }
6892f909a9fSLandon J. Fuller
6902f909a9fSLandon J. Fuller device_printf(sc->dev, "unknown pin mode %d\n", mode);
6912f909a9fSLandon J. Fuller return (EINVAL);
6922f909a9fSLandon J. Fuller }
6932f909a9fSLandon J. Fuller
6942f909a9fSLandon J. Fuller /**
6952f909a9fSLandon J. Fuller * Verify that @p flags are valid for use with @p pin_num, and on success,
6962f909a9fSLandon J. Fuller * return the pin mode described by @p flags in @p mode.
6972f909a9fSLandon J. Fuller *
6982f909a9fSLandon J. Fuller * @param sc GPIO driver instance state.
6992f909a9fSLandon J. Fuller * @param pin_num The pin number to configure.
7002f909a9fSLandon J. Fuller * @param flags The pin flags to be validated.
7012f909a9fSLandon J. Fuller * @param[out] mode On success, will be populated with the GPIO pin mode
7022f909a9fSLandon J. Fuller * defined by @p flags.
7032f909a9fSLandon J. Fuller *
7042f909a9fSLandon J. Fuller * @retval 0 success
7052f909a9fSLandon J. Fuller * @retval EINVAL if @p flags are invalid.
7062f909a9fSLandon J. Fuller */
7072f909a9fSLandon J. Fuller static int
chipc_gpio_check_flags(struct chipc_gpio_softc * sc,uint32_t pin_num,uint32_t flags,chipc_gpio_pin_mode * mode)7082f909a9fSLandon J. Fuller chipc_gpio_check_flags(struct chipc_gpio_softc *sc, uint32_t pin_num,
7092f909a9fSLandon J. Fuller uint32_t flags, chipc_gpio_pin_mode *mode)
7102f909a9fSLandon J. Fuller {
7112f909a9fSLandon J. Fuller uint32_t mode_flag, input_flag, output_flag;
7122f909a9fSLandon J. Fuller
7132f909a9fSLandon J. Fuller CC_GPIO_ASSERT_VALID_PIN(sc, pin_num);
7142f909a9fSLandon J. Fuller
7152f909a9fSLandon J. Fuller mode_flag = flags & (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT |
7162f909a9fSLandon J. Fuller GPIO_PIN_TRISTATE);
7172f909a9fSLandon J. Fuller output_flag = flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW
7182f909a9fSLandon J. Fuller | GPIO_PIN_PULSATE);
7192f909a9fSLandon J. Fuller input_flag = flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN);
7202f909a9fSLandon J. Fuller
7212f909a9fSLandon J. Fuller switch (mode_flag) {
7222f909a9fSLandon J. Fuller case GPIO_PIN_OUTPUT:
7232f909a9fSLandon J. Fuller /* No input flag(s) should be set */
7242f909a9fSLandon J. Fuller if (input_flag != 0)
7252f909a9fSLandon J. Fuller return (EINVAL);
7262f909a9fSLandon J. Fuller
7272f909a9fSLandon J. Fuller /* Validate our output flag(s) */
7282f909a9fSLandon J. Fuller switch (output_flag) {
7292f909a9fSLandon J. Fuller case GPIO_PIN_PRESET_HIGH:
7302f909a9fSLandon J. Fuller case GPIO_PIN_PRESET_LOW:
7312f909a9fSLandon J. Fuller case (GPIO_PIN_PRESET_HIGH|GPIO_PIN_PULSATE):
7322f909a9fSLandon J. Fuller case (GPIO_PIN_PRESET_LOW|GPIO_PIN_PULSATE):
7332f909a9fSLandon J. Fuller case 0:
7342f909a9fSLandon J. Fuller /* Check for unhandled flags */
7352f909a9fSLandon J. Fuller if ((flags & ~(mode_flag | output_flag)) != 0)
7362f909a9fSLandon J. Fuller return (EINVAL);
7372f909a9fSLandon J. Fuller
7382f909a9fSLandon J. Fuller *mode = CC_GPIO_PIN_OUTPUT;
7392f909a9fSLandon J. Fuller return (0);
7402f909a9fSLandon J. Fuller
7412f909a9fSLandon J. Fuller default:
7422f909a9fSLandon J. Fuller /* Incompatible output flags */
7432f909a9fSLandon J. Fuller return (EINVAL);
7442f909a9fSLandon J. Fuller }
7452f909a9fSLandon J. Fuller
7462f909a9fSLandon J. Fuller case GPIO_PIN_INPUT:
7472f909a9fSLandon J. Fuller /* No output flag(s) should be set */
7482f909a9fSLandon J. Fuller if (output_flag != 0)
7492f909a9fSLandon J. Fuller return (EINVAL);
7502f909a9fSLandon J. Fuller
7512f909a9fSLandon J. Fuller /* Validate our input flag(s) */
7522f909a9fSLandon J. Fuller switch (input_flag) {
7532f909a9fSLandon J. Fuller case GPIO_PIN_PULLUP:
7542f909a9fSLandon J. Fuller case GPIO_PIN_PULLDOWN:
7552f909a9fSLandon J. Fuller case 0:
7562f909a9fSLandon J. Fuller /* Check for unhandled flags */
7572f909a9fSLandon J. Fuller if ((flags & ~(mode_flag | input_flag)) != 0)
7582f909a9fSLandon J. Fuller return (EINVAL);
7592f909a9fSLandon J. Fuller
7602f909a9fSLandon J. Fuller *mode = CC_GPIO_PIN_INPUT;
7612f909a9fSLandon J. Fuller return (0);
7622f909a9fSLandon J. Fuller
7632f909a9fSLandon J. Fuller default:
7642f909a9fSLandon J. Fuller /* Incompatible input flags */
7652f909a9fSLandon J. Fuller return (EINVAL);
7662f909a9fSLandon J. Fuller }
7672f909a9fSLandon J. Fuller
7682f909a9fSLandon J. Fuller break;
7692f909a9fSLandon J. Fuller
7702f909a9fSLandon J. Fuller case (GPIO_PIN_TRISTATE|GPIO_PIN_OUTPUT):
7712f909a9fSLandon J. Fuller case GPIO_PIN_TRISTATE:
7722f909a9fSLandon J. Fuller /* No input or output flag(s) should be set */
7732f909a9fSLandon J. Fuller if (input_flag != 0 || output_flag != 0)
7742f909a9fSLandon J. Fuller return (EINVAL);
7752f909a9fSLandon J. Fuller
7762f909a9fSLandon J. Fuller /* Check for unhandled flags */
7772f909a9fSLandon J. Fuller if ((flags & ~mode_flag) != 0)
7782f909a9fSLandon J. Fuller return (EINVAL);
7792f909a9fSLandon J. Fuller
7802f909a9fSLandon J. Fuller *mode = CC_GPIO_PIN_TRISTATE;
7812f909a9fSLandon J. Fuller return (0);
7822f909a9fSLandon J. Fuller
7832f909a9fSLandon J. Fuller default:
7842f909a9fSLandon J. Fuller /* Incompatible mode flags */
7852f909a9fSLandon J. Fuller return (EINVAL);
7862f909a9fSLandon J. Fuller }
7872f909a9fSLandon J. Fuller }
7882f909a9fSLandon J. Fuller
7892f909a9fSLandon J. Fuller /**
7902f909a9fSLandon J. Fuller * Return the current pin mode for @p pin_num.
7912f909a9fSLandon J. Fuller *
7922f909a9fSLandon J. Fuller * @param sc GPIO driver instance state.
7932f909a9fSLandon J. Fuller * @param pin_num The pin number to query.
7942f909a9fSLandon J. Fuller */
7952f909a9fSLandon J. Fuller static chipc_gpio_pin_mode
chipc_gpio_pin_get_mode(struct chipc_gpio_softc * sc,uint32_t pin_num)7962f909a9fSLandon J. Fuller chipc_gpio_pin_get_mode(struct chipc_gpio_softc *sc, uint32_t pin_num)
7972f909a9fSLandon J. Fuller {
7982f909a9fSLandon J. Fuller CC_GPIO_LOCK_ASSERT(sc, MA_OWNED);
7992f909a9fSLandon J. Fuller CC_GPIO_ASSERT_VALID_PIN(sc, pin_num);
8002f909a9fSLandon J. Fuller
8012f909a9fSLandon J. Fuller if (CC_GPIO_RDFLAG(sc, pin_num, GPIOCTRL)) {
8022f909a9fSLandon J. Fuller return (CC_GPIO_PIN_TRISTATE);
8032f909a9fSLandon J. Fuller } else if (CC_GPIO_RDFLAG(sc, pin_num, GPIOOUTEN)) {
8042f909a9fSLandon J. Fuller return (CC_GPIO_PIN_OUTPUT);
8052f909a9fSLandon J. Fuller } else {
8062f909a9fSLandon J. Fuller return (CC_GPIO_PIN_INPUT);
8072f909a9fSLandon J. Fuller }
8082f909a9fSLandon J. Fuller }
8092f909a9fSLandon J. Fuller
8102f909a9fSLandon J. Fuller static device_method_t chipc_gpio_methods[] = {
8112f909a9fSLandon J. Fuller /* Device interface */
8122f909a9fSLandon J. Fuller DEVMETHOD(device_probe, chipc_gpio_probe),
8132f909a9fSLandon J. Fuller DEVMETHOD(device_attach, chipc_gpio_attach),
8142f909a9fSLandon J. Fuller DEVMETHOD(device_detach, chipc_gpio_detach),
8152f909a9fSLandon J. Fuller
8162f909a9fSLandon J. Fuller /* GPIO interface */
8172f909a9fSLandon J. Fuller DEVMETHOD(gpio_get_bus, chipc_gpio_get_bus),
8182f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_max, chipc_gpio_pin_max),
8192f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_getname, chipc_gpio_pin_getname),
8202f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_getflags, chipc_gpio_pin_getflags),
8212f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_getcaps, chipc_gpio_pin_getcaps),
8222f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_setflags, chipc_gpio_pin_setflags),
8232f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_get, chipc_gpio_pin_get),
8242f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_set, chipc_gpio_pin_set),
8252f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_toggle, chipc_gpio_pin_toggle),
8262f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_access_32, chipc_gpio_pin_access_32),
8272f909a9fSLandon J. Fuller DEVMETHOD(gpio_pin_config_32, chipc_gpio_pin_config_32),
8282f909a9fSLandon J. Fuller
8292f909a9fSLandon J. Fuller DEVMETHOD_END
8302f909a9fSLandon J. Fuller };
8312f909a9fSLandon J. Fuller
8322f909a9fSLandon J. Fuller DEFINE_CLASS_0(gpio, chipc_gpio_driver, chipc_gpio_methods, sizeof(struct chipc_gpio_softc));
833162c26adSJohn Baldwin EARLY_DRIVER_MODULE(chipc_gpio, bhnd_chipc, chipc_gpio_driver, NULL, NULL,
834162c26adSJohn Baldwin BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
8352f909a9fSLandon J. Fuller
8362f909a9fSLandon J. Fuller MODULE_DEPEND(chipc_gpio, bhnd, 1, 1, 1);
8372f909a9fSLandon J. Fuller MODULE_DEPEND(chipc_gpio, gpiobus, 1, 1, 1);
8382f909a9fSLandon J. Fuller MODULE_VERSION(chipc_gpio, 1);
839