xref: /freebsd/sys/dev/bhnd/cores/chipc/chipc_gpio.c (revision fdafd315)
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