xref: /freebsd/sys/dev/amdgpio/amdgpio.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
18ce574deSOleksandr Tymoshenko /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 #include "opt_acpi.h"
318ce574deSOleksandr Tymoshenko 
328ce574deSOleksandr Tymoshenko #include <sys/param.h>
338ce574deSOleksandr Tymoshenko #include <sys/systm.h>
348ce574deSOleksandr Tymoshenko #include <sys/bus.h>
358ce574deSOleksandr Tymoshenko #include <sys/gpio.h>
368ce574deSOleksandr Tymoshenko #include <sys/interrupt.h>
378ce574deSOleksandr Tymoshenko #include <sys/kernel.h>
388ce574deSOleksandr Tymoshenko #include <sys/lock.h>
398ce574deSOleksandr Tymoshenko #include <sys/module.h>
408ce574deSOleksandr Tymoshenko #include <sys/mutex.h>
418ce574deSOleksandr Tymoshenko #include <sys/proc.h>
428ce574deSOleksandr Tymoshenko #include <sys/rman.h>
438ce574deSOleksandr Tymoshenko #include <sys/sysctl.h>
448ce574deSOleksandr Tymoshenko 
458ce574deSOleksandr Tymoshenko #include <machine/bus.h>
468ce574deSOleksandr Tymoshenko #include <machine/resource.h>
478ce574deSOleksandr Tymoshenko 
488ce574deSOleksandr Tymoshenko #include <contrib/dev/acpica/include/acpi.h>
498ce574deSOleksandr Tymoshenko #include <contrib/dev/acpica/include/accommon.h>
508ce574deSOleksandr Tymoshenko 
518ce574deSOleksandr Tymoshenko #include <dev/acpica/acpivar.h>
528ce574deSOleksandr Tymoshenko #include <dev/gpio/gpiobusvar.h>
538ce574deSOleksandr Tymoshenko 
548ce574deSOleksandr Tymoshenko #include "gpio_if.h"
558ce574deSOleksandr Tymoshenko #include "amdgpio.h"
568ce574deSOleksandr Tymoshenko 
578ce574deSOleksandr Tymoshenko static struct resource_spec amdgpio_spec[] = {
588ce574deSOleksandr Tymoshenko 	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
598ce574deSOleksandr Tymoshenko 	{ -1, 0, 0 }
608ce574deSOleksandr Tymoshenko };
618ce574deSOleksandr Tymoshenko 
628ce574deSOleksandr Tymoshenko static inline uint32_t
amdgpio_read_4(struct amdgpio_softc * sc,bus_size_t off)638ce574deSOleksandr Tymoshenko amdgpio_read_4(struct amdgpio_softc *sc, bus_size_t off)
648ce574deSOleksandr Tymoshenko {
658ce574deSOleksandr Tymoshenko 	return (bus_read_4(sc->sc_res[0], off));
668ce574deSOleksandr Tymoshenko }
678ce574deSOleksandr Tymoshenko 
688ce574deSOleksandr Tymoshenko static inline void
amdgpio_write_4(struct amdgpio_softc * sc,bus_size_t off,uint32_t val)698ce574deSOleksandr Tymoshenko amdgpio_write_4(struct amdgpio_softc *sc, bus_size_t off,
708ce574deSOleksandr Tymoshenko 		uint32_t val)
718ce574deSOleksandr Tymoshenko {
728ce574deSOleksandr Tymoshenko 	bus_write_4(sc->sc_res[0], off, val);
738ce574deSOleksandr Tymoshenko }
748ce574deSOleksandr Tymoshenko 
758ce574deSOleksandr Tymoshenko static bool
amdgpio_is_pin_output(struct amdgpio_softc * sc,uint32_t pin)768ce574deSOleksandr Tymoshenko amdgpio_is_pin_output(struct amdgpio_softc *sc, uint32_t pin)
778ce574deSOleksandr Tymoshenko {
788ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
798ce574deSOleksandr Tymoshenko 	bool ret;
808ce574deSOleksandr Tymoshenko 
818ce574deSOleksandr Tymoshenko 	/* Get the current pin state */
828ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
838ce574deSOleksandr Tymoshenko 
848ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
858ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
868ce574deSOleksandr Tymoshenko 
878ce574deSOleksandr Tymoshenko 	if (val & BIT(OUTPUT_ENABLE_OFF))
888ce574deSOleksandr Tymoshenko 		ret = true;
898ce574deSOleksandr Tymoshenko 	else
908ce574deSOleksandr Tymoshenko 		ret = false;
918ce574deSOleksandr Tymoshenko 
928ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
938ce574deSOleksandr Tymoshenko 
948ce574deSOleksandr Tymoshenko 	return (ret);
958ce574deSOleksandr Tymoshenko }
968ce574deSOleksandr Tymoshenko 
978ce574deSOleksandr Tymoshenko static device_t
amdgpio_get_bus(device_t dev)988ce574deSOleksandr Tymoshenko amdgpio_get_bus(device_t dev)
998ce574deSOleksandr Tymoshenko {
1008ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1018ce574deSOleksandr Tymoshenko 
1028ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1038ce574deSOleksandr Tymoshenko 
1048ce574deSOleksandr Tymoshenko 	dprintf("busdev %p\n", sc->sc_busdev);
1058ce574deSOleksandr Tymoshenko 	return (sc->sc_busdev);
1068ce574deSOleksandr Tymoshenko }
1078ce574deSOleksandr Tymoshenko 
1088ce574deSOleksandr Tymoshenko static int
amdgpio_pin_max(device_t dev,int * maxpin)1098ce574deSOleksandr Tymoshenko amdgpio_pin_max(device_t dev, int *maxpin)
1108ce574deSOleksandr Tymoshenko {
1118ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1128ce574deSOleksandr Tymoshenko 
1138ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1148ce574deSOleksandr Tymoshenko 
1158ce574deSOleksandr Tymoshenko 	*maxpin = sc->sc_npins - 1;
1168ce574deSOleksandr Tymoshenko 	dprintf("npins %d maxpin %d\n", sc->sc_npins, *maxpin);
1178ce574deSOleksandr Tymoshenko 
1188ce574deSOleksandr Tymoshenko 	return (0);
1198ce574deSOleksandr Tymoshenko }
1208ce574deSOleksandr Tymoshenko 
1218ce574deSOleksandr Tymoshenko static bool
amdgpio_valid_pin(struct amdgpio_softc * sc,int pin)1228ce574deSOleksandr Tymoshenko amdgpio_valid_pin(struct amdgpio_softc *sc, int pin)
1238ce574deSOleksandr Tymoshenko {
1248ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1258ce574deSOleksandr Tymoshenko 	if (sc->sc_res[0] == NULL)
1268ce574deSOleksandr Tymoshenko 		return (false);
1278ce574deSOleksandr Tymoshenko 
1288ce574deSOleksandr Tymoshenko 	if ((sc->sc_gpio_pins[pin].gp_pin == pin) &&
1298ce574deSOleksandr Tymoshenko 		(sc->sc_gpio_pins[pin].gp_caps != 0))
1308ce574deSOleksandr Tymoshenko 		return (true);
1318ce574deSOleksandr Tymoshenko 
1328ce574deSOleksandr Tymoshenko 	return (false);
1338ce574deSOleksandr Tymoshenko }
1348ce574deSOleksandr Tymoshenko 
1358ce574deSOleksandr Tymoshenko static int
amdgpio_pin_getname(device_t dev,uint32_t pin,char * name)1368ce574deSOleksandr Tymoshenko amdgpio_pin_getname(device_t dev, uint32_t pin, char *name)
1378ce574deSOleksandr Tymoshenko {
1388ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1398ce574deSOleksandr Tymoshenko 
1408ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1418ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1428ce574deSOleksandr Tymoshenko 
1438ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
1448ce574deSOleksandr Tymoshenko 		return (EINVAL);
1458ce574deSOleksandr Tymoshenko 
1468ce574deSOleksandr Tymoshenko 	/* Set a very simple name */
1478ce574deSOleksandr Tymoshenko 	snprintf(name, GPIOMAXNAME, "%s", sc->sc_gpio_pins[pin].gp_name);
1488ce574deSOleksandr Tymoshenko 	name[GPIOMAXNAME - 1] = '\0';
1498ce574deSOleksandr Tymoshenko 
1508ce574deSOleksandr Tymoshenko 	dprintf("pin %d name %s\n", pin, name);
1518ce574deSOleksandr Tymoshenko 
1528ce574deSOleksandr Tymoshenko 	return (0);
1538ce574deSOleksandr Tymoshenko }
1548ce574deSOleksandr Tymoshenko 
1558ce574deSOleksandr Tymoshenko static int
amdgpio_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)1568ce574deSOleksandr Tymoshenko amdgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
1578ce574deSOleksandr Tymoshenko {
1588ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1598ce574deSOleksandr Tymoshenko 
1608ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1618ce574deSOleksandr Tymoshenko 
1628ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1638ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
1648ce574deSOleksandr Tymoshenko 		return (EINVAL);
1658ce574deSOleksandr Tymoshenko 
1668ce574deSOleksandr Tymoshenko 	*caps = sc->sc_gpio_pins[pin].gp_caps;
1678ce574deSOleksandr Tymoshenko 
1688ce574deSOleksandr Tymoshenko 	dprintf("pin %d caps 0x%x\n", pin, *caps);
1698ce574deSOleksandr Tymoshenko 
1708ce574deSOleksandr Tymoshenko 	return (0);
1718ce574deSOleksandr Tymoshenko }
1728ce574deSOleksandr Tymoshenko 
1738ce574deSOleksandr Tymoshenko static int
amdgpio_pin_getflags(device_t dev,uint32_t pin,uint32_t * flags)1748ce574deSOleksandr Tymoshenko amdgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
1758ce574deSOleksandr Tymoshenko {
1768ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1778ce574deSOleksandr Tymoshenko 
1788ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
1798ce574deSOleksandr Tymoshenko 
1808ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
1818ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
1828ce574deSOleksandr Tymoshenko 		return (EINVAL);
1838ce574deSOleksandr Tymoshenko 
1848ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
1858ce574deSOleksandr Tymoshenko 
1868ce574deSOleksandr Tymoshenko 	*flags = sc->sc_gpio_pins[pin].gp_flags;
1878ce574deSOleksandr Tymoshenko 
1888ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x\n", pin, *flags);
1898ce574deSOleksandr Tymoshenko 
1908ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
1918ce574deSOleksandr Tymoshenko 
1928ce574deSOleksandr Tymoshenko 	return (0);
1938ce574deSOleksandr Tymoshenko }
1948ce574deSOleksandr Tymoshenko 
1958ce574deSOleksandr Tymoshenko static int
amdgpio_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)1968ce574deSOleksandr Tymoshenko amdgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
1978ce574deSOleksandr Tymoshenko {
1988ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
1998ce574deSOleksandr Tymoshenko 	uint32_t reg, val, allowed;
2008ce574deSOleksandr Tymoshenko 
2018ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
2028ce574deSOleksandr Tymoshenko 
2038ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x\n", pin, flags);
2048ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
2058ce574deSOleksandr Tymoshenko 		return (EINVAL);
2068ce574deSOleksandr Tymoshenko 
2078ce574deSOleksandr Tymoshenko 	allowed = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
2088ce574deSOleksandr Tymoshenko 
2098ce574deSOleksandr Tymoshenko 	/*
2108ce574deSOleksandr Tymoshenko 	 * Only directtion flag allowed
2118ce574deSOleksandr Tymoshenko 	 */
2128ce574deSOleksandr Tymoshenko 	if (flags & ~allowed)
2138ce574deSOleksandr Tymoshenko 		return (EINVAL);
2148ce574deSOleksandr Tymoshenko 
2158ce574deSOleksandr Tymoshenko 	/*
2168ce574deSOleksandr Tymoshenko 	 * Not both directions simultaneously
2178ce574deSOleksandr Tymoshenko 	 */
2188ce574deSOleksandr Tymoshenko 	if ((flags & allowed) == allowed)
2198ce574deSOleksandr Tymoshenko 		return (EINVAL);
2208ce574deSOleksandr Tymoshenko 
2218ce574deSOleksandr Tymoshenko 	/* Set the GPIO mode and state */
2228ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
2238ce574deSOleksandr Tymoshenko 
2248ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
2258ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
2268ce574deSOleksandr Tymoshenko 
2278ce574deSOleksandr Tymoshenko 	if (flags & GPIO_PIN_INPUT) {
2288ce574deSOleksandr Tymoshenko 		val &= ~BIT(OUTPUT_ENABLE_OFF);
2298ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_INPUT;
2308ce574deSOleksandr Tymoshenko 	} else {
2318ce574deSOleksandr Tymoshenko 		val |= BIT(OUTPUT_ENABLE_OFF);
2328ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_OUTPUT;
2338ce574deSOleksandr Tymoshenko 	}
2348ce574deSOleksandr Tymoshenko 
2358ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
2368ce574deSOleksandr Tymoshenko 
2378ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x val 0x%x gp_flags 0x%x\n",
2388ce574deSOleksandr Tymoshenko 		pin, flags, val, sc->sc_gpio_pins[pin].gp_flags);
2398ce574deSOleksandr Tymoshenko 
2408ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
2418ce574deSOleksandr Tymoshenko 
2428ce574deSOleksandr Tymoshenko 	return (0);
2438ce574deSOleksandr Tymoshenko }
2448ce574deSOleksandr Tymoshenko 
2458ce574deSOleksandr Tymoshenko static int
amdgpio_pin_get(device_t dev,uint32_t pin,unsigned int * value)2468ce574deSOleksandr Tymoshenko amdgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
2478ce574deSOleksandr Tymoshenko {
2488ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
2498ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
2508ce574deSOleksandr Tymoshenko 
2518ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
2528ce574deSOleksandr Tymoshenko 
2538ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
2548ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
2558ce574deSOleksandr Tymoshenko 		return (EINVAL);
2568ce574deSOleksandr Tymoshenko 
2578ce574deSOleksandr Tymoshenko 	*value = 0;
2588ce574deSOleksandr Tymoshenko 
2598ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
2608ce574deSOleksandr Tymoshenko 
2618ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
2628ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
2638ce574deSOleksandr Tymoshenko 
264cada7eafSAndriy Gapon 	if ((sc->sc_gpio_pins[pin].gp_flags & GPIO_PIN_OUTPUT) != 0) {
2658ce574deSOleksandr Tymoshenko 		if (val & BIT(OUTPUT_VALUE_OFF))
2668ce574deSOleksandr Tymoshenko 			*value = GPIO_PIN_HIGH;
2678ce574deSOleksandr Tymoshenko 		else
2688ce574deSOleksandr Tymoshenko 			*value = GPIO_PIN_LOW;
269cada7eafSAndriy Gapon 	} else {
270cada7eafSAndriy Gapon 		if (val & BIT(PIN_STS_OFF))
271cada7eafSAndriy Gapon 			*value = GPIO_PIN_HIGH;
272cada7eafSAndriy Gapon 		else
273cada7eafSAndriy Gapon 			*value = GPIO_PIN_LOW;
274cada7eafSAndriy Gapon 	}
2758ce574deSOleksandr Tymoshenko 
2768ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x\n", pin, *value);
2778ce574deSOleksandr Tymoshenko 
2788ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
2798ce574deSOleksandr Tymoshenko 
2808ce574deSOleksandr Tymoshenko 	return (0);
2818ce574deSOleksandr Tymoshenko }
2828ce574deSOleksandr Tymoshenko 
2838ce574deSOleksandr Tymoshenko static int
amdgpio_pin_set(device_t dev,uint32_t pin,unsigned int value)2848ce574deSOleksandr Tymoshenko amdgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
2858ce574deSOleksandr Tymoshenko {
2868ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
2878ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
2888ce574deSOleksandr Tymoshenko 
2898ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
2908ce574deSOleksandr Tymoshenko 
2918ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x\n", pin, value);
2928ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
2938ce574deSOleksandr Tymoshenko 		return (EINVAL);
2948ce574deSOleksandr Tymoshenko 
2958ce574deSOleksandr Tymoshenko 	if (!amdgpio_is_pin_output(sc, pin))
2968ce574deSOleksandr Tymoshenko 		return (EINVAL);
2978ce574deSOleksandr Tymoshenko 
2988ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
2998ce574deSOleksandr Tymoshenko 
3008ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
3018ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
3028ce574deSOleksandr Tymoshenko 
3038ce574deSOleksandr Tymoshenko 	if (value == GPIO_PIN_LOW)
3048ce574deSOleksandr Tymoshenko 		val &= ~BIT(OUTPUT_VALUE_OFF);
3058ce574deSOleksandr Tymoshenko 	else
3068ce574deSOleksandr Tymoshenko 		val |= BIT(OUTPUT_VALUE_OFF);
3078ce574deSOleksandr Tymoshenko 
3088ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
3098ce574deSOleksandr Tymoshenko 
3108ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x val 0x%x\n", pin, value, val);
3118ce574deSOleksandr Tymoshenko 
3128ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
3138ce574deSOleksandr Tymoshenko 
3148ce574deSOleksandr Tymoshenko 	return (0);
3158ce574deSOleksandr Tymoshenko }
3168ce574deSOleksandr Tymoshenko 
3178ce574deSOleksandr Tymoshenko static int
amdgpio_pin_toggle(device_t dev,uint32_t pin)3188ce574deSOleksandr Tymoshenko amdgpio_pin_toggle(device_t dev, uint32_t pin)
3198ce574deSOleksandr Tymoshenko {
3208ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
3218ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
3228ce574deSOleksandr Tymoshenko 
3238ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
3248ce574deSOleksandr Tymoshenko 
3258ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
3268ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
3278ce574deSOleksandr Tymoshenko 		return (EINVAL);
3288ce574deSOleksandr Tymoshenko 
3298ce574deSOleksandr Tymoshenko 	if (!amdgpio_is_pin_output(sc, pin))
3308ce574deSOleksandr Tymoshenko 		return (EINVAL);
3318ce574deSOleksandr Tymoshenko 
3328ce574deSOleksandr Tymoshenko 	/* Toggle the pin */
3338ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
3348ce574deSOleksandr Tymoshenko 
3358ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
3368ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
3378ce574deSOleksandr Tymoshenko 	dprintf("pin %d value before 0x%x\n", pin, val);
3388ce574deSOleksandr Tymoshenko 	val = val ^ BIT(OUTPUT_VALUE_OFF);
3398ce574deSOleksandr Tymoshenko 	dprintf("pin %d value after 0x%x\n", pin, val);
3408ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
3418ce574deSOleksandr Tymoshenko 
3428ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
3438ce574deSOleksandr Tymoshenko 
3448ce574deSOleksandr Tymoshenko 	return (0);
3458ce574deSOleksandr Tymoshenko }
3468ce574deSOleksandr Tymoshenko 
3478ce574deSOleksandr Tymoshenko static int
amdgpio_probe(device_t dev)3488ce574deSOleksandr Tymoshenko amdgpio_probe(device_t dev)
3498ce574deSOleksandr Tymoshenko {
3508ce574deSOleksandr Tymoshenko 	static char *gpio_ids[] = { "AMD0030", "AMDI0030", NULL };
3515efca36fSTakanori Watanabe 	int rv;
3528ce574deSOleksandr Tymoshenko 
3535efca36fSTakanori Watanabe 	if (acpi_disabled("gpio"))
3548ce574deSOleksandr Tymoshenko 		return (ENXIO);
3555efca36fSTakanori Watanabe 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids, NULL);
3565efca36fSTakanori Watanabe 	if (rv <= 0)
3578ce574deSOleksandr Tymoshenko 		device_set_desc(dev, "AMD GPIO Controller");
3585efca36fSTakanori Watanabe 
3595efca36fSTakanori Watanabe 	return (rv);
3608ce574deSOleksandr Tymoshenko }
3618ce574deSOleksandr Tymoshenko 
3628ce574deSOleksandr Tymoshenko static int
amdgpio_attach(device_t dev)3638ce574deSOleksandr Tymoshenko amdgpio_attach(device_t dev)
3648ce574deSOleksandr Tymoshenko {
3658ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
3668ce574deSOleksandr Tymoshenko 	int i, pin, bank;
3678ce574deSOleksandr Tymoshenko 
3688ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
3698ce574deSOleksandr Tymoshenko 	sc->sc_dev = dev;
3708ce574deSOleksandr Tymoshenko 	sc->sc_handle = acpi_get_handle(dev);
3718ce574deSOleksandr Tymoshenko 
3728ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_INIT(sc);
3738ce574deSOleksandr Tymoshenko 
3748ce574deSOleksandr Tymoshenko 	sc->sc_nbanks = AMD_GPIO_NUM_PIN_BANK;
3758ce574deSOleksandr Tymoshenko 	sc->sc_npins = AMD_GPIO_PINS_MAX;
3768ce574deSOleksandr Tymoshenko 	sc->sc_bank_prefix = AMD_GPIO_PREFIX;
3778ce574deSOleksandr Tymoshenko 	sc->sc_pin_info = kernzp_pins;
3788ce574deSOleksandr Tymoshenko 	sc->sc_ngroups = nitems(kernzp_groups);
3798ce574deSOleksandr Tymoshenko 	sc->sc_groups = kernzp_groups;
3808ce574deSOleksandr Tymoshenko 
3818ce574deSOleksandr Tymoshenko 	if (bus_alloc_resources(dev, amdgpio_spec, sc->sc_res)) {
3828ce574deSOleksandr Tymoshenko 		device_printf(dev, "could not allocate resources\n");
3838ce574deSOleksandr Tymoshenko 		goto err_rsrc;
3848ce574deSOleksandr Tymoshenko 	}
3858ce574deSOleksandr Tymoshenko 
3868ce574deSOleksandr Tymoshenko 	sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
3878ce574deSOleksandr Tymoshenko 	sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
3888ce574deSOleksandr Tymoshenko 
3898ce574deSOleksandr Tymoshenko 	/* Initialize all possible pins to be Invalid */
3908ce574deSOleksandr Tymoshenko 	for (i = 0; i < AMD_GPIO_PINS_MAX ; i++) {
3918ce574deSOleksandr Tymoshenko 		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
392cfdb42f8SAndriy Gapon 			"Unexposed PIN %d", i);
3938ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_pin = -1;
3948ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_caps = 0;
3958ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_flags = 0;
3968ce574deSOleksandr Tymoshenko 	}
3978ce574deSOleksandr Tymoshenko 
3988ce574deSOleksandr Tymoshenko 	/* Initialize only driver exposed pins with appropriate capabilities */
3998ce574deSOleksandr Tymoshenko 	for (i = 0; i < AMD_GPIO_PINS_EXPOSED ; i++) {
4008ce574deSOleksandr Tymoshenko 		pin = kernzp_pins[i].pin_num;
4018ce574deSOleksandr Tymoshenko 		bank = pin/AMD_GPIO_PINS_PER_BANK;
402cfdb42f8SAndriy Gapon 		snprintf(sc->sc_gpio_pins[pin].gp_name, GPIOMAXNAME, "%s%d_%s",
4038ce574deSOleksandr Tymoshenko 			AMD_GPIO_PREFIX, bank, kernzp_pins[i].pin_name);
4048ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_pin = pin;
4058ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_caps = AMDGPIO_DEFAULT_CAPS;
406cfdb42f8SAndriy Gapon 		sc->sc_gpio_pins[pin].gp_flags =
407cfdb42f8SAndriy Gapon 		    amdgpio_is_pin_output(sc, pin) ?
408cfdb42f8SAndriy Gapon 		    GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
4098ce574deSOleksandr Tymoshenko 	}
4108ce574deSOleksandr Tymoshenko 
4118ce574deSOleksandr Tymoshenko 	sc->sc_busdev = gpiobus_attach_bus(dev);
4128ce574deSOleksandr Tymoshenko 	if (sc->sc_busdev == NULL) {
4138ce574deSOleksandr Tymoshenko 		device_printf(dev, "could not attach gpiobus\n");
4148ce574deSOleksandr Tymoshenko 		goto err_bus;
4158ce574deSOleksandr Tymoshenko 	}
4168ce574deSOleksandr Tymoshenko 
4178ce574deSOleksandr Tymoshenko 	return (0);
4188ce574deSOleksandr Tymoshenko 
4198ce574deSOleksandr Tymoshenko err_bus:
4208ce574deSOleksandr Tymoshenko 	bus_release_resources(dev, amdgpio_spec, sc->sc_res);
4218ce574deSOleksandr Tymoshenko 
4228ce574deSOleksandr Tymoshenko err_rsrc:
4238ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_DESTROY(sc);
4248ce574deSOleksandr Tymoshenko 
4258ce574deSOleksandr Tymoshenko 	return (ENXIO);
4268ce574deSOleksandr Tymoshenko }
4278ce574deSOleksandr Tymoshenko 
4288ce574deSOleksandr Tymoshenko static int
amdgpio_detach(device_t dev)4298ce574deSOleksandr Tymoshenko amdgpio_detach(device_t dev)
4308ce574deSOleksandr Tymoshenko {
4318ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
4328ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
4338ce574deSOleksandr Tymoshenko 
4348ce574deSOleksandr Tymoshenko 	if (sc->sc_busdev)
4358ce574deSOleksandr Tymoshenko 		gpiobus_detach_bus(dev);
4368ce574deSOleksandr Tymoshenko 
4378ce574deSOleksandr Tymoshenko 	bus_release_resources(dev, amdgpio_spec, sc->sc_res);
4388ce574deSOleksandr Tymoshenko 
4398ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_DESTROY(sc);
4408ce574deSOleksandr Tymoshenko 
4418ce574deSOleksandr Tymoshenko 	return (0);
4428ce574deSOleksandr Tymoshenko }
4438ce574deSOleksandr Tymoshenko 
4448ce574deSOleksandr Tymoshenko static device_method_t amdgpio_methods[] = {
4458ce574deSOleksandr Tymoshenko 	/* Device interface */
4468ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_probe, amdgpio_probe),
4478ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_attach, amdgpio_attach),
4488ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_detach, amdgpio_detach),
4498ce574deSOleksandr Tymoshenko 
4508ce574deSOleksandr Tymoshenko 	/* GPIO protocol */
4518ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_get_bus, amdgpio_get_bus),
4528ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_max, amdgpio_pin_max),
4538ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getname, amdgpio_pin_getname),
4548ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getcaps, amdgpio_pin_getcaps),
4558ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getflags, amdgpio_pin_getflags),
4568ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_setflags, amdgpio_pin_setflags),
4578ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_get, amdgpio_pin_get),
4588ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_set, amdgpio_pin_set),
4598ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_toggle, amdgpio_pin_toggle),
4608ce574deSOleksandr Tymoshenko 
4618ce574deSOleksandr Tymoshenko 	DEVMETHOD_END
4628ce574deSOleksandr Tymoshenko };
4638ce574deSOleksandr Tymoshenko 
4648ce574deSOleksandr Tymoshenko static driver_t amdgpio_driver = {
4658ce574deSOleksandr Tymoshenko 	"gpio",
4668ce574deSOleksandr Tymoshenko 	amdgpio_methods,
4678ce574deSOleksandr Tymoshenko 	sizeof(struct amdgpio_softc),
4688ce574deSOleksandr Tymoshenko };
4698ce574deSOleksandr Tymoshenko 
47083a273efSJohn Baldwin DRIVER_MODULE(amdgpio, acpi, amdgpio_driver, 0, 0);
4718ce574deSOleksandr Tymoshenko MODULE_DEPEND(amdgpio, acpi, 1, 1, 1);
4728ce574deSOleksandr Tymoshenko MODULE_DEPEND(amdgpio, gpiobus, 1, 1, 1);
4738ce574deSOleksandr Tymoshenko MODULE_VERSION(amdgpio, 1);
474