xref: /freebsd/sys/dev/amdgpio/amdgpio.c (revision cfdb42f880bdab0cbc35389d714b73bf9b10de91)
18ce574deSOleksandr Tymoshenko /*-
28ce574deSOleksandr Tymoshenko  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
38ce574deSOleksandr Tymoshenko  *
48ce574deSOleksandr Tymoshenko  * Copyright (c) 2018 Advanced Micro Devices
58ce574deSOleksandr Tymoshenko  * All rights reserved.
68ce574deSOleksandr Tymoshenko  *
78ce574deSOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
88ce574deSOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
98ce574deSOleksandr Tymoshenko  * are met:
108ce574deSOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
118ce574deSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
128ce574deSOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
138ce574deSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
148ce574deSOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
158ce574deSOleksandr Tymoshenko  *
168ce574deSOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
178ce574deSOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
188ce574deSOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
198ce574deSOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
208ce574deSOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
218ce574deSOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
228ce574deSOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
238ce574deSOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
248ce574deSOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258ce574deSOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268ce574deSOleksandr Tymoshenko  * SUCH DAMAGE.
278ce574deSOleksandr Tymoshenko  */
288ce574deSOleksandr Tymoshenko 
298ce574deSOleksandr Tymoshenko #include <sys/cdefs.h>
308ce574deSOleksandr Tymoshenko __FBSDID("$FreeBSD$");
318ce574deSOleksandr Tymoshenko 
328ce574deSOleksandr Tymoshenko #include "opt_acpi.h"
338ce574deSOleksandr Tymoshenko 
348ce574deSOleksandr Tymoshenko #include <sys/param.h>
358ce574deSOleksandr Tymoshenko #include <sys/systm.h>
368ce574deSOleksandr Tymoshenko #include <sys/bus.h>
378ce574deSOleksandr Tymoshenko #include <sys/gpio.h>
388ce574deSOleksandr Tymoshenko #include <sys/interrupt.h>
398ce574deSOleksandr Tymoshenko #include <sys/kernel.h>
408ce574deSOleksandr Tymoshenko #include <sys/lock.h>
418ce574deSOleksandr Tymoshenko #include <sys/module.h>
428ce574deSOleksandr Tymoshenko #include <sys/mutex.h>
438ce574deSOleksandr Tymoshenko #include <sys/proc.h>
448ce574deSOleksandr Tymoshenko #include <sys/rman.h>
458ce574deSOleksandr Tymoshenko #include <sys/sysctl.h>
468ce574deSOleksandr Tymoshenko 
478ce574deSOleksandr Tymoshenko #include <machine/bus.h>
488ce574deSOleksandr Tymoshenko #include <machine/resource.h>
498ce574deSOleksandr Tymoshenko 
508ce574deSOleksandr Tymoshenko #include <contrib/dev/acpica/include/acpi.h>
518ce574deSOleksandr Tymoshenko #include <contrib/dev/acpica/include/accommon.h>
528ce574deSOleksandr Tymoshenko 
538ce574deSOleksandr Tymoshenko #include <dev/acpica/acpivar.h>
548ce574deSOleksandr Tymoshenko #include <dev/gpio/gpiobusvar.h>
558ce574deSOleksandr Tymoshenko 
568ce574deSOleksandr Tymoshenko #include "gpio_if.h"
578ce574deSOleksandr Tymoshenko #include "amdgpio.h"
588ce574deSOleksandr Tymoshenko 
598ce574deSOleksandr Tymoshenko static struct resource_spec amdgpio_spec[] = {
608ce574deSOleksandr Tymoshenko 	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
618ce574deSOleksandr Tymoshenko 	{ -1, 0, 0 }
628ce574deSOleksandr Tymoshenko };
638ce574deSOleksandr Tymoshenko 
648ce574deSOleksandr Tymoshenko static inline uint32_t
658ce574deSOleksandr Tymoshenko amdgpio_read_4(struct amdgpio_softc *sc, bus_size_t off)
668ce574deSOleksandr Tymoshenko {
678ce574deSOleksandr Tymoshenko 	return (bus_read_4(sc->sc_res[0], off));
688ce574deSOleksandr Tymoshenko }
698ce574deSOleksandr Tymoshenko 
708ce574deSOleksandr Tymoshenko static inline void
718ce574deSOleksandr Tymoshenko amdgpio_write_4(struct amdgpio_softc *sc, bus_size_t off,
728ce574deSOleksandr Tymoshenko 		uint32_t val)
738ce574deSOleksandr Tymoshenko {
748ce574deSOleksandr Tymoshenko 	bus_write_4(sc->sc_res[0], off, val);
758ce574deSOleksandr Tymoshenko }
768ce574deSOleksandr Tymoshenko 
778ce574deSOleksandr Tymoshenko static bool
788ce574deSOleksandr Tymoshenko amdgpio_is_pin_output(struct amdgpio_softc *sc, uint32_t pin)
798ce574deSOleksandr Tymoshenko {
808ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
818ce574deSOleksandr Tymoshenko 	bool ret;
828ce574deSOleksandr Tymoshenko 
838ce574deSOleksandr Tymoshenko 	/* Get the current pin state */
848ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
858ce574deSOleksandr Tymoshenko 
868ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
878ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
888ce574deSOleksandr Tymoshenko 
898ce574deSOleksandr Tymoshenko 	if (val & BIT(OUTPUT_ENABLE_OFF))
908ce574deSOleksandr Tymoshenko 		ret = true;
918ce574deSOleksandr Tymoshenko 	else
928ce574deSOleksandr Tymoshenko 		ret = false;
938ce574deSOleksandr Tymoshenko 
948ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
958ce574deSOleksandr Tymoshenko 
968ce574deSOleksandr Tymoshenko 	return (ret);
978ce574deSOleksandr Tymoshenko }
988ce574deSOleksandr Tymoshenko 
998ce574deSOleksandr Tymoshenko static device_t
1008ce574deSOleksandr Tymoshenko amdgpio_get_bus(device_t dev)
1018ce574deSOleksandr Tymoshenko {
1028ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1038ce574deSOleksandr Tymoshenko 
1048ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1058ce574deSOleksandr Tymoshenko 
1068ce574deSOleksandr Tymoshenko 	dprintf("busdev %p\n", sc->sc_busdev);
1078ce574deSOleksandr Tymoshenko 	return (sc->sc_busdev);
1088ce574deSOleksandr Tymoshenko }
1098ce574deSOleksandr Tymoshenko 
1108ce574deSOleksandr Tymoshenko static int
1118ce574deSOleksandr Tymoshenko amdgpio_pin_max(device_t dev, int *maxpin)
1128ce574deSOleksandr Tymoshenko {
1138ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1148ce574deSOleksandr Tymoshenko 
1158ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1168ce574deSOleksandr Tymoshenko 
1178ce574deSOleksandr Tymoshenko 	*maxpin = sc->sc_npins - 1;
1188ce574deSOleksandr Tymoshenko 	dprintf("npins %d maxpin %d\n", sc->sc_npins, *maxpin);
1198ce574deSOleksandr Tymoshenko 
1208ce574deSOleksandr Tymoshenko 	return (0);
1218ce574deSOleksandr Tymoshenko }
1228ce574deSOleksandr Tymoshenko 
1238ce574deSOleksandr Tymoshenko static bool
1248ce574deSOleksandr Tymoshenko amdgpio_valid_pin(struct amdgpio_softc *sc, int pin)
1258ce574deSOleksandr Tymoshenko {
1268ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1278ce574deSOleksandr Tymoshenko 	if (sc->sc_res[0] == NULL)
1288ce574deSOleksandr Tymoshenko 		return (false);
1298ce574deSOleksandr Tymoshenko 
1308ce574deSOleksandr Tymoshenko 	if ((sc->sc_gpio_pins[pin].gp_pin == pin) &&
1318ce574deSOleksandr Tymoshenko 		(sc->sc_gpio_pins[pin].gp_caps != 0))
1328ce574deSOleksandr Tymoshenko 		return (true);
1338ce574deSOleksandr Tymoshenko 
1348ce574deSOleksandr Tymoshenko 	return (false);
1358ce574deSOleksandr Tymoshenko }
1368ce574deSOleksandr Tymoshenko 
1378ce574deSOleksandr Tymoshenko static int
1388ce574deSOleksandr Tymoshenko amdgpio_pin_getname(device_t dev, uint32_t pin, char *name)
1398ce574deSOleksandr Tymoshenko {
1408ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1418ce574deSOleksandr Tymoshenko 
1428ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1438ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1448ce574deSOleksandr Tymoshenko 
1458ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
1468ce574deSOleksandr Tymoshenko 		return (EINVAL);
1478ce574deSOleksandr Tymoshenko 
1488ce574deSOleksandr Tymoshenko 	/* Set a very simple name */
1498ce574deSOleksandr Tymoshenko 	snprintf(name, GPIOMAXNAME, "%s", sc->sc_gpio_pins[pin].gp_name);
1508ce574deSOleksandr Tymoshenko 	name[GPIOMAXNAME - 1] = '\0';
1518ce574deSOleksandr Tymoshenko 
1528ce574deSOleksandr Tymoshenko 	dprintf("pin %d name %s\n", pin, name);
1538ce574deSOleksandr Tymoshenko 
1548ce574deSOleksandr Tymoshenko 	return (0);
1558ce574deSOleksandr Tymoshenko }
1568ce574deSOleksandr Tymoshenko 
1578ce574deSOleksandr Tymoshenko static int
1588ce574deSOleksandr Tymoshenko amdgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
1598ce574deSOleksandr Tymoshenko {
1608ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1618ce574deSOleksandr Tymoshenko 
1628ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1638ce574deSOleksandr Tymoshenko 
1648ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1658ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
1668ce574deSOleksandr Tymoshenko 		return (EINVAL);
1678ce574deSOleksandr Tymoshenko 
1688ce574deSOleksandr Tymoshenko 	*caps = sc->sc_gpio_pins[pin].gp_caps;
1698ce574deSOleksandr Tymoshenko 
1708ce574deSOleksandr Tymoshenko 	dprintf("pin %d caps 0x%x\n", pin, *caps);
1718ce574deSOleksandr Tymoshenko 
1728ce574deSOleksandr Tymoshenko 	return (0);
1738ce574deSOleksandr Tymoshenko }
1748ce574deSOleksandr Tymoshenko 
1758ce574deSOleksandr Tymoshenko static int
1768ce574deSOleksandr Tymoshenko amdgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
1778ce574deSOleksandr Tymoshenko {
1788ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1798ce574deSOleksandr Tymoshenko 
1808ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1818ce574deSOleksandr Tymoshenko 
1828ce574deSOleksandr Tymoshenko 
1838ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1848ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
1858ce574deSOleksandr Tymoshenko 		return (EINVAL);
1868ce574deSOleksandr Tymoshenko 
1878ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
1888ce574deSOleksandr Tymoshenko 
1898ce574deSOleksandr Tymoshenko 	*flags = sc->sc_gpio_pins[pin].gp_flags;
1908ce574deSOleksandr Tymoshenko 
1918ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x\n", pin, *flags);
1928ce574deSOleksandr Tymoshenko 
1938ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
1948ce574deSOleksandr Tymoshenko 
1958ce574deSOleksandr Tymoshenko 	return (0);
1968ce574deSOleksandr Tymoshenko }
1978ce574deSOleksandr Tymoshenko 
1988ce574deSOleksandr Tymoshenko static int
1998ce574deSOleksandr Tymoshenko amdgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
2008ce574deSOleksandr Tymoshenko {
2018ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
2028ce574deSOleksandr Tymoshenko 	uint32_t reg, val, allowed;
2038ce574deSOleksandr Tymoshenko 
2048ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
2058ce574deSOleksandr Tymoshenko 
2068ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x\n", pin, flags);
2078ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
2088ce574deSOleksandr Tymoshenko 		return (EINVAL);
2098ce574deSOleksandr Tymoshenko 
2108ce574deSOleksandr Tymoshenko 	allowed = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
2118ce574deSOleksandr Tymoshenko 
2128ce574deSOleksandr Tymoshenko 	/*
2138ce574deSOleksandr Tymoshenko 	 * Only directtion flag allowed
2148ce574deSOleksandr Tymoshenko 	 */
2158ce574deSOleksandr Tymoshenko 	if (flags & ~allowed)
2168ce574deSOleksandr Tymoshenko 		return (EINVAL);
2178ce574deSOleksandr Tymoshenko 
2188ce574deSOleksandr Tymoshenko 	/*
2198ce574deSOleksandr Tymoshenko 	 * Not both directions simultaneously
2208ce574deSOleksandr Tymoshenko 	 */
2218ce574deSOleksandr Tymoshenko 	if ((flags & allowed) == allowed)
2228ce574deSOleksandr Tymoshenko 		return (EINVAL);
2238ce574deSOleksandr Tymoshenko 
2248ce574deSOleksandr Tymoshenko 	/* Set the GPIO mode and state */
2258ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
2268ce574deSOleksandr Tymoshenko 
2278ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
2288ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
2298ce574deSOleksandr Tymoshenko 
2308ce574deSOleksandr Tymoshenko 	if (flags & GPIO_PIN_INPUT) {
2318ce574deSOleksandr Tymoshenko 		val &= ~BIT(OUTPUT_ENABLE_OFF);
2328ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_INPUT;
2338ce574deSOleksandr Tymoshenko 	} else {
2348ce574deSOleksandr Tymoshenko 		val |= BIT(OUTPUT_ENABLE_OFF);
2358ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_OUTPUT;
2368ce574deSOleksandr Tymoshenko 	}
2378ce574deSOleksandr Tymoshenko 
2388ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
2398ce574deSOleksandr Tymoshenko 
2408ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x val 0x%x gp_flags 0x%x\n",
2418ce574deSOleksandr Tymoshenko 		pin, flags, val, sc->sc_gpio_pins[pin].gp_flags);
2428ce574deSOleksandr Tymoshenko 
2438ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
2448ce574deSOleksandr Tymoshenko 
2458ce574deSOleksandr Tymoshenko 	return (0);
2468ce574deSOleksandr Tymoshenko }
2478ce574deSOleksandr Tymoshenko 
2488ce574deSOleksandr Tymoshenko static int
2498ce574deSOleksandr Tymoshenko amdgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
2508ce574deSOleksandr Tymoshenko {
2518ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
2528ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
2538ce574deSOleksandr Tymoshenko 
2548ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
2558ce574deSOleksandr Tymoshenko 
2568ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
2578ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
2588ce574deSOleksandr Tymoshenko 		return (EINVAL);
2598ce574deSOleksandr Tymoshenko 
2608ce574deSOleksandr Tymoshenko 	*value = 0;
2618ce574deSOleksandr Tymoshenko 
2628ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
2638ce574deSOleksandr Tymoshenko 
2648ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
2658ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
2668ce574deSOleksandr Tymoshenko 
2678ce574deSOleksandr Tymoshenko 	if (val & BIT(OUTPUT_VALUE_OFF))
2688ce574deSOleksandr Tymoshenko 		*value = GPIO_PIN_HIGH;
2698ce574deSOleksandr Tymoshenko 	else
2708ce574deSOleksandr Tymoshenko 		*value = GPIO_PIN_LOW;
2718ce574deSOleksandr Tymoshenko 
2728ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x\n", pin, *value);
2738ce574deSOleksandr Tymoshenko 
2748ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
2758ce574deSOleksandr Tymoshenko 
2768ce574deSOleksandr Tymoshenko 	return (0);
2778ce574deSOleksandr Tymoshenko }
2788ce574deSOleksandr Tymoshenko 
2798ce574deSOleksandr Tymoshenko static int
2808ce574deSOleksandr Tymoshenko amdgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
2818ce574deSOleksandr Tymoshenko {
2828ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
2838ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
2848ce574deSOleksandr Tymoshenko 
2858ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
2868ce574deSOleksandr Tymoshenko 
2878ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x\n", pin, value);
2888ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
2898ce574deSOleksandr Tymoshenko 		return (EINVAL);
2908ce574deSOleksandr Tymoshenko 
2918ce574deSOleksandr Tymoshenko 	if (!amdgpio_is_pin_output(sc, pin))
2928ce574deSOleksandr Tymoshenko 		return (EINVAL);
2938ce574deSOleksandr Tymoshenko 
2948ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
2958ce574deSOleksandr Tymoshenko 
2968ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
2978ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
2988ce574deSOleksandr Tymoshenko 
2998ce574deSOleksandr Tymoshenko 	if (value == GPIO_PIN_LOW)
3008ce574deSOleksandr Tymoshenko 		val &= ~BIT(OUTPUT_VALUE_OFF);
3018ce574deSOleksandr Tymoshenko 	else
3028ce574deSOleksandr Tymoshenko 		val |= BIT(OUTPUT_VALUE_OFF);
3038ce574deSOleksandr Tymoshenko 
3048ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
3058ce574deSOleksandr Tymoshenko 
3068ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x val 0x%x\n", pin, value, val);
3078ce574deSOleksandr Tymoshenko 
3088ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
3098ce574deSOleksandr Tymoshenko 
3108ce574deSOleksandr Tymoshenko 	return (0);
3118ce574deSOleksandr Tymoshenko }
3128ce574deSOleksandr Tymoshenko 
3138ce574deSOleksandr Tymoshenko static int
3148ce574deSOleksandr Tymoshenko amdgpio_pin_toggle(device_t dev, uint32_t pin)
3158ce574deSOleksandr Tymoshenko {
3168ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
3178ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
3188ce574deSOleksandr Tymoshenko 
3198ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
3208ce574deSOleksandr Tymoshenko 
3218ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
3228ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
3238ce574deSOleksandr Tymoshenko 		return (EINVAL);
3248ce574deSOleksandr Tymoshenko 
3258ce574deSOleksandr Tymoshenko 	if (!amdgpio_is_pin_output(sc, pin))
3268ce574deSOleksandr Tymoshenko 		return (EINVAL);
3278ce574deSOleksandr Tymoshenko 
3288ce574deSOleksandr Tymoshenko 	/* Toggle the pin */
3298ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
3308ce574deSOleksandr Tymoshenko 
3318ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
3328ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
3338ce574deSOleksandr Tymoshenko 	dprintf("pin %d value before 0x%x\n", pin, val);
3348ce574deSOleksandr Tymoshenko 	val = val ^ BIT(OUTPUT_VALUE_OFF);
3358ce574deSOleksandr Tymoshenko 	dprintf("pin %d value after 0x%x\n", pin, val);
3368ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
3378ce574deSOleksandr Tymoshenko 
3388ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
3398ce574deSOleksandr Tymoshenko 
3408ce574deSOleksandr Tymoshenko 	return (0);
3418ce574deSOleksandr Tymoshenko }
3428ce574deSOleksandr Tymoshenko 
3438ce574deSOleksandr Tymoshenko static int
3448ce574deSOleksandr Tymoshenko amdgpio_probe(device_t dev)
3458ce574deSOleksandr Tymoshenko {
3468ce574deSOleksandr Tymoshenko 	static char *gpio_ids[] = { "AMD0030", "AMDI0030", NULL };
3475efca36fSTakanori Watanabe 	int rv;
3488ce574deSOleksandr Tymoshenko 
3495efca36fSTakanori Watanabe 	if (acpi_disabled("gpio"))
3508ce574deSOleksandr Tymoshenko 		return (ENXIO);
3515efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids, NULL);
3528ce574deSOleksandr Tymoshenko 
3535efca36fSTakanori Watanabe 	if (rv <= 0)
3548ce574deSOleksandr Tymoshenko 		device_set_desc(dev, "AMD GPIO Controller");
3555efca36fSTakanori Watanabe 
3565efca36fSTakanori Watanabe 	return (rv);
3578ce574deSOleksandr Tymoshenko }
3588ce574deSOleksandr Tymoshenko 
3598ce574deSOleksandr Tymoshenko static int
3608ce574deSOleksandr Tymoshenko amdgpio_attach(device_t dev)
3618ce574deSOleksandr Tymoshenko {
3628ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
3638ce574deSOleksandr Tymoshenko 	int i, pin, bank;
3648ce574deSOleksandr Tymoshenko 
3658ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
3668ce574deSOleksandr Tymoshenko 	sc->sc_dev = dev;
3678ce574deSOleksandr Tymoshenko 	sc->sc_handle = acpi_get_handle(dev);
3688ce574deSOleksandr Tymoshenko 
3698ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_INIT(sc);
3708ce574deSOleksandr Tymoshenko 
3718ce574deSOleksandr Tymoshenko 	sc->sc_nbanks = AMD_GPIO_NUM_PIN_BANK;
3728ce574deSOleksandr Tymoshenko 	sc->sc_npins = AMD_GPIO_PINS_MAX;
3738ce574deSOleksandr Tymoshenko 	sc->sc_bank_prefix = AMD_GPIO_PREFIX;
3748ce574deSOleksandr Tymoshenko 	sc->sc_pin_info = kernzp_pins;
3758ce574deSOleksandr Tymoshenko 	sc->sc_ngroups = nitems(kernzp_groups);
3768ce574deSOleksandr Tymoshenko 	sc->sc_groups = kernzp_groups;
3778ce574deSOleksandr Tymoshenko 
3788ce574deSOleksandr Tymoshenko 	if (bus_alloc_resources(dev, amdgpio_spec, sc->sc_res)) {
3798ce574deSOleksandr Tymoshenko 		device_printf(dev, "could not allocate resources\n");
3808ce574deSOleksandr Tymoshenko 		goto err_rsrc;
3818ce574deSOleksandr Tymoshenko 	}
3828ce574deSOleksandr Tymoshenko 
3838ce574deSOleksandr Tymoshenko 	sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
3848ce574deSOleksandr Tymoshenko 	sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
3858ce574deSOleksandr Tymoshenko 
3868ce574deSOleksandr Tymoshenko 	/* Initialize all possible pins to be Invalid */
3878ce574deSOleksandr Tymoshenko 	for (i = 0; i < AMD_GPIO_PINS_MAX ; i++) {
3888ce574deSOleksandr Tymoshenko 		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
389*cfdb42f8SAndriy Gapon 			"Unexposed PIN %d", i);
3908ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_pin = -1;
3918ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_caps = 0;
3928ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_flags = 0;
3938ce574deSOleksandr Tymoshenko 	}
3948ce574deSOleksandr Tymoshenko 
3958ce574deSOleksandr Tymoshenko 	/* Initialize only driver exposed pins with appropriate capabilities */
3968ce574deSOleksandr Tymoshenko 	for (i = 0; i < AMD_GPIO_PINS_EXPOSED ; i++) {
3978ce574deSOleksandr Tymoshenko 		pin = kernzp_pins[i].pin_num;
3988ce574deSOleksandr Tymoshenko 		bank = pin/AMD_GPIO_PINS_PER_BANK;
399*cfdb42f8SAndriy Gapon 		snprintf(sc->sc_gpio_pins[pin].gp_name, GPIOMAXNAME, "%s%d_%s",
4008ce574deSOleksandr Tymoshenko 			AMD_GPIO_PREFIX, bank, kernzp_pins[i].pin_name);
4018ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_pin = pin;
4028ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_caps = AMDGPIO_DEFAULT_CAPS;
403*cfdb42f8SAndriy Gapon 		sc->sc_gpio_pins[pin].gp_flags =
404*cfdb42f8SAndriy Gapon 		    amdgpio_is_pin_output(sc, pin) ?
405*cfdb42f8SAndriy Gapon 		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
4068ce574deSOleksandr Tymoshenko 	}
4078ce574deSOleksandr Tymoshenko 
4088ce574deSOleksandr Tymoshenko 	sc->sc_busdev = gpiobus_attach_bus(dev);
4098ce574deSOleksandr Tymoshenko 	if (sc->sc_busdev == NULL) {
4108ce574deSOleksandr Tymoshenko 		device_printf(dev, "could not attach gpiobus\n");
4118ce574deSOleksandr Tymoshenko 		goto err_bus;
4128ce574deSOleksandr Tymoshenko 	}
4138ce574deSOleksandr Tymoshenko 
4148ce574deSOleksandr Tymoshenko 	return (0);
4158ce574deSOleksandr Tymoshenko 
4168ce574deSOleksandr Tymoshenko err_bus:
4178ce574deSOleksandr Tymoshenko 	bus_release_resources(dev, amdgpio_spec, sc->sc_res);
4188ce574deSOleksandr Tymoshenko 
4198ce574deSOleksandr Tymoshenko err_rsrc:
4208ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_DESTROY(sc);
4218ce574deSOleksandr Tymoshenko 
4228ce574deSOleksandr Tymoshenko 	return (ENXIO);
4238ce574deSOleksandr Tymoshenko }
4248ce574deSOleksandr Tymoshenko 
4258ce574deSOleksandr Tymoshenko 
4268ce574deSOleksandr Tymoshenko static int
4278ce574deSOleksandr Tymoshenko amdgpio_detach(device_t dev)
4288ce574deSOleksandr Tymoshenko {
4298ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
4308ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
4318ce574deSOleksandr Tymoshenko 
4328ce574deSOleksandr Tymoshenko 	if (sc->sc_busdev)
4338ce574deSOleksandr Tymoshenko 		gpiobus_detach_bus(dev);
4348ce574deSOleksandr Tymoshenko 
4358ce574deSOleksandr Tymoshenko 	bus_release_resources(dev, amdgpio_spec, sc->sc_res);
4368ce574deSOleksandr Tymoshenko 
4378ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_DESTROY(sc);
4388ce574deSOleksandr Tymoshenko 
4398ce574deSOleksandr Tymoshenko 	return (0);
4408ce574deSOleksandr Tymoshenko }
4418ce574deSOleksandr Tymoshenko 
4428ce574deSOleksandr Tymoshenko static device_method_t amdgpio_methods[] = {
4438ce574deSOleksandr Tymoshenko 	/* Device interface */
4448ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_probe, amdgpio_probe),
4458ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_attach, amdgpio_attach),
4468ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_detach, amdgpio_detach),
4478ce574deSOleksandr Tymoshenko 
4488ce574deSOleksandr Tymoshenko 	/* GPIO protocol */
4498ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_get_bus, amdgpio_get_bus),
4508ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_max, amdgpio_pin_max),
4518ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getname, amdgpio_pin_getname),
4528ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getcaps, amdgpio_pin_getcaps),
4538ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getflags, amdgpio_pin_getflags),
4548ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_setflags, amdgpio_pin_setflags),
4558ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_get, amdgpio_pin_get),
4568ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_set, amdgpio_pin_set),
4578ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_toggle, amdgpio_pin_toggle),
4588ce574deSOleksandr Tymoshenko 
4598ce574deSOleksandr Tymoshenko 	DEVMETHOD_END
4608ce574deSOleksandr Tymoshenko };
4618ce574deSOleksandr Tymoshenko 
4628ce574deSOleksandr Tymoshenko static driver_t amdgpio_driver = {
4638ce574deSOleksandr Tymoshenko 	"gpio",
4648ce574deSOleksandr Tymoshenko 	amdgpio_methods,
4658ce574deSOleksandr Tymoshenko 	sizeof(struct amdgpio_softc),
4668ce574deSOleksandr Tymoshenko };
4678ce574deSOleksandr Tymoshenko 
4688ce574deSOleksandr Tymoshenko static devclass_t amdgpio_devclass;
4698ce574deSOleksandr Tymoshenko DRIVER_MODULE(amdgpio, acpi, amdgpio_driver, amdgpio_devclass, 0, 0);
4708ce574deSOleksandr Tymoshenko MODULE_DEPEND(amdgpio, acpi, 1, 1, 1);
4718ce574deSOleksandr Tymoshenko MODULE_DEPEND(amdgpio, gpiobus, 1, 1, 1);
4728ce574deSOleksandr Tymoshenko MODULE_VERSION(amdgpio, 1);
473