xref: /freebsd/sys/dev/amdgpio/amdgpio.c (revision 8ce574de3b680e0a08b4c58c2a949f26963828af)
1*8ce574deSOleksandr Tymoshenko /*-
2*8ce574deSOleksandr Tymoshenko  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*8ce574deSOleksandr Tymoshenko  *
4*8ce574deSOleksandr Tymoshenko  * Copyright (c) 2018 Advanced Micro Devices
5*8ce574deSOleksandr Tymoshenko  * All rights reserved.
6*8ce574deSOleksandr Tymoshenko  *
7*8ce574deSOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
8*8ce574deSOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
9*8ce574deSOleksandr Tymoshenko  * are met:
10*8ce574deSOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
11*8ce574deSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
12*8ce574deSOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
13*8ce574deSOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
14*8ce574deSOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
15*8ce574deSOleksandr Tymoshenko  *
16*8ce574deSOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*8ce574deSOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*8ce574deSOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*8ce574deSOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*8ce574deSOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*8ce574deSOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*8ce574deSOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*8ce574deSOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*8ce574deSOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*8ce574deSOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*8ce574deSOleksandr Tymoshenko  * SUCH DAMAGE.
27*8ce574deSOleksandr Tymoshenko  */
28*8ce574deSOleksandr Tymoshenko 
29*8ce574deSOleksandr Tymoshenko #include <sys/cdefs.h>
30*8ce574deSOleksandr Tymoshenko __FBSDID("$FreeBSD$");
31*8ce574deSOleksandr Tymoshenko 
32*8ce574deSOleksandr Tymoshenko #include "opt_acpi.h"
33*8ce574deSOleksandr Tymoshenko 
34*8ce574deSOleksandr Tymoshenko #include <sys/param.h>
35*8ce574deSOleksandr Tymoshenko #include <sys/systm.h>
36*8ce574deSOleksandr Tymoshenko #include <sys/bus.h>
37*8ce574deSOleksandr Tymoshenko #include <sys/gpio.h>
38*8ce574deSOleksandr Tymoshenko #include <sys/interrupt.h>
39*8ce574deSOleksandr Tymoshenko #include <sys/kernel.h>
40*8ce574deSOleksandr Tymoshenko #include <sys/lock.h>
41*8ce574deSOleksandr Tymoshenko #include <sys/module.h>
42*8ce574deSOleksandr Tymoshenko #include <sys/mutex.h>
43*8ce574deSOleksandr Tymoshenko #include <sys/proc.h>
44*8ce574deSOleksandr Tymoshenko #include <sys/rman.h>
45*8ce574deSOleksandr Tymoshenko #include <sys/sysctl.h>
46*8ce574deSOleksandr Tymoshenko 
47*8ce574deSOleksandr Tymoshenko #include <machine/bus.h>
48*8ce574deSOleksandr Tymoshenko #include <machine/resource.h>
49*8ce574deSOleksandr Tymoshenko 
50*8ce574deSOleksandr Tymoshenko #include <contrib/dev/acpica/include/acpi.h>
51*8ce574deSOleksandr Tymoshenko #include <contrib/dev/acpica/include/accommon.h>
52*8ce574deSOleksandr Tymoshenko 
53*8ce574deSOleksandr Tymoshenko #include <dev/acpica/acpivar.h>
54*8ce574deSOleksandr Tymoshenko #include <dev/gpio/gpiobusvar.h>
55*8ce574deSOleksandr Tymoshenko 
56*8ce574deSOleksandr Tymoshenko #include "gpio_if.h"
57*8ce574deSOleksandr Tymoshenko #include "amdgpio.h"
58*8ce574deSOleksandr Tymoshenko 
59*8ce574deSOleksandr Tymoshenko static struct resource_spec amdgpio_spec[] = {
60*8ce574deSOleksandr Tymoshenko 	{ SYS_RES_MEMORY, 0, RF_ACTIVE },
61*8ce574deSOleksandr Tymoshenko 	{ -1, 0, 0 }
62*8ce574deSOleksandr Tymoshenko };
63*8ce574deSOleksandr Tymoshenko 
64*8ce574deSOleksandr Tymoshenko static inline uint32_t
65*8ce574deSOleksandr Tymoshenko amdgpio_read_4(struct amdgpio_softc *sc, bus_size_t off)
66*8ce574deSOleksandr Tymoshenko {
67*8ce574deSOleksandr Tymoshenko 	return (bus_read_4(sc->sc_res[0], off));
68*8ce574deSOleksandr Tymoshenko }
69*8ce574deSOleksandr Tymoshenko 
70*8ce574deSOleksandr Tymoshenko static inline void
71*8ce574deSOleksandr Tymoshenko amdgpio_write_4(struct amdgpio_softc *sc, bus_size_t off,
72*8ce574deSOleksandr Tymoshenko 		uint32_t val)
73*8ce574deSOleksandr Tymoshenko {
74*8ce574deSOleksandr Tymoshenko 	bus_write_4(sc->sc_res[0], off, val);
75*8ce574deSOleksandr Tymoshenko }
76*8ce574deSOleksandr Tymoshenko 
77*8ce574deSOleksandr Tymoshenko static bool
78*8ce574deSOleksandr Tymoshenko amdgpio_is_pin_output(struct amdgpio_softc *sc, uint32_t pin)
79*8ce574deSOleksandr Tymoshenko {
80*8ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
81*8ce574deSOleksandr Tymoshenko 	bool ret;
82*8ce574deSOleksandr Tymoshenko 
83*8ce574deSOleksandr Tymoshenko 	/* Get the current pin state */
84*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
85*8ce574deSOleksandr Tymoshenko 
86*8ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
87*8ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
88*8ce574deSOleksandr Tymoshenko 
89*8ce574deSOleksandr Tymoshenko 	if (val & BIT(OUTPUT_ENABLE_OFF))
90*8ce574deSOleksandr Tymoshenko 		ret = true;
91*8ce574deSOleksandr Tymoshenko 	else
92*8ce574deSOleksandr Tymoshenko 		ret = false;
93*8ce574deSOleksandr Tymoshenko 
94*8ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
95*8ce574deSOleksandr Tymoshenko 
96*8ce574deSOleksandr Tymoshenko 	return (ret);
97*8ce574deSOleksandr Tymoshenko }
98*8ce574deSOleksandr Tymoshenko 
99*8ce574deSOleksandr Tymoshenko static device_t
100*8ce574deSOleksandr Tymoshenko amdgpio_get_bus(device_t dev)
101*8ce574deSOleksandr Tymoshenko {
102*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
103*8ce574deSOleksandr Tymoshenko 
104*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
105*8ce574deSOleksandr Tymoshenko 
106*8ce574deSOleksandr Tymoshenko 	dprintf("busdev %p\n", sc->sc_busdev);
107*8ce574deSOleksandr Tymoshenko 	return (sc->sc_busdev);
108*8ce574deSOleksandr Tymoshenko }
109*8ce574deSOleksandr Tymoshenko 
110*8ce574deSOleksandr Tymoshenko static int
111*8ce574deSOleksandr Tymoshenko amdgpio_pin_max(device_t dev, int *maxpin)
112*8ce574deSOleksandr Tymoshenko {
113*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
114*8ce574deSOleksandr Tymoshenko 
115*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
116*8ce574deSOleksandr Tymoshenko 
117*8ce574deSOleksandr Tymoshenko 	*maxpin = sc->sc_npins - 1;
118*8ce574deSOleksandr Tymoshenko 	dprintf("npins %d maxpin %d\n", sc->sc_npins, *maxpin);
119*8ce574deSOleksandr Tymoshenko 
120*8ce574deSOleksandr Tymoshenko 	return (0);
121*8ce574deSOleksandr Tymoshenko }
122*8ce574deSOleksandr Tymoshenko 
123*8ce574deSOleksandr Tymoshenko static bool
124*8ce574deSOleksandr Tymoshenko amdgpio_valid_pin(struct amdgpio_softc *sc, int pin)
125*8ce574deSOleksandr Tymoshenko {
126*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
127*8ce574deSOleksandr Tymoshenko 	if (sc->sc_res[0] == NULL)
128*8ce574deSOleksandr Tymoshenko 		return (false);
129*8ce574deSOleksandr Tymoshenko 
130*8ce574deSOleksandr Tymoshenko 	if ((sc->sc_gpio_pins[pin].gp_pin == pin) &&
131*8ce574deSOleksandr Tymoshenko 		(sc->sc_gpio_pins[pin].gp_caps != 0))
132*8ce574deSOleksandr Tymoshenko 		return (true);
133*8ce574deSOleksandr Tymoshenko 
134*8ce574deSOleksandr Tymoshenko 	return (false);
135*8ce574deSOleksandr Tymoshenko }
136*8ce574deSOleksandr Tymoshenko 
137*8ce574deSOleksandr Tymoshenko static int
138*8ce574deSOleksandr Tymoshenko amdgpio_pin_getname(device_t dev, uint32_t pin, char *name)
139*8ce574deSOleksandr Tymoshenko {
140*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
141*8ce574deSOleksandr Tymoshenko 
142*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
143*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
144*8ce574deSOleksandr Tymoshenko 
145*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
146*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
147*8ce574deSOleksandr Tymoshenko 
148*8ce574deSOleksandr Tymoshenko 	/* Set a very simple name */
149*8ce574deSOleksandr Tymoshenko 	snprintf(name, GPIOMAXNAME, "%s", sc->sc_gpio_pins[pin].gp_name);
150*8ce574deSOleksandr Tymoshenko 	name[GPIOMAXNAME - 1] = '\0';
151*8ce574deSOleksandr Tymoshenko 
152*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d name %s\n", pin, name);
153*8ce574deSOleksandr Tymoshenko 
154*8ce574deSOleksandr Tymoshenko 	return (0);
155*8ce574deSOleksandr Tymoshenko }
156*8ce574deSOleksandr Tymoshenko 
157*8ce574deSOleksandr Tymoshenko static int
158*8ce574deSOleksandr Tymoshenko amdgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
159*8ce574deSOleksandr Tymoshenko {
160*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
161*8ce574deSOleksandr Tymoshenko 
162*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
163*8ce574deSOleksandr Tymoshenko 
164*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
165*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
166*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
167*8ce574deSOleksandr Tymoshenko 
168*8ce574deSOleksandr Tymoshenko 	*caps = sc->sc_gpio_pins[pin].gp_caps;
169*8ce574deSOleksandr Tymoshenko 
170*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d caps 0x%x\n", pin, *caps);
171*8ce574deSOleksandr Tymoshenko 
172*8ce574deSOleksandr Tymoshenko 	return (0);
173*8ce574deSOleksandr Tymoshenko }
174*8ce574deSOleksandr Tymoshenko 
175*8ce574deSOleksandr Tymoshenko static int
176*8ce574deSOleksandr Tymoshenko amdgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
177*8ce574deSOleksandr Tymoshenko {
178*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
179*8ce574deSOleksandr Tymoshenko 
180*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
181*8ce574deSOleksandr Tymoshenko 
182*8ce574deSOleksandr Tymoshenko 
183*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
184*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
185*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
186*8ce574deSOleksandr Tymoshenko 
187*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
188*8ce574deSOleksandr Tymoshenko 
189*8ce574deSOleksandr Tymoshenko 	*flags = sc->sc_gpio_pins[pin].gp_flags;
190*8ce574deSOleksandr Tymoshenko 
191*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x\n", pin, *flags);
192*8ce574deSOleksandr Tymoshenko 
193*8ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
194*8ce574deSOleksandr Tymoshenko 
195*8ce574deSOleksandr Tymoshenko 	return (0);
196*8ce574deSOleksandr Tymoshenko }
197*8ce574deSOleksandr Tymoshenko 
198*8ce574deSOleksandr Tymoshenko static int
199*8ce574deSOleksandr Tymoshenko amdgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
200*8ce574deSOleksandr Tymoshenko {
201*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
202*8ce574deSOleksandr Tymoshenko 	uint32_t reg, val, allowed;
203*8ce574deSOleksandr Tymoshenko 
204*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
205*8ce574deSOleksandr Tymoshenko 
206*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x\n", pin, flags);
207*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
208*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
209*8ce574deSOleksandr Tymoshenko 
210*8ce574deSOleksandr Tymoshenko 	allowed = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
211*8ce574deSOleksandr Tymoshenko 
212*8ce574deSOleksandr Tymoshenko 	/*
213*8ce574deSOleksandr Tymoshenko 	 * Only directtion flag allowed
214*8ce574deSOleksandr Tymoshenko 	 */
215*8ce574deSOleksandr Tymoshenko 	if (flags & ~allowed)
216*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
217*8ce574deSOleksandr Tymoshenko 
218*8ce574deSOleksandr Tymoshenko 	/*
219*8ce574deSOleksandr Tymoshenko 	 * Not both directions simultaneously
220*8ce574deSOleksandr Tymoshenko 	 */
221*8ce574deSOleksandr Tymoshenko 	if ((flags & allowed) == allowed)
222*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
223*8ce574deSOleksandr Tymoshenko 
224*8ce574deSOleksandr Tymoshenko 	/* Set the GPIO mode and state */
225*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
226*8ce574deSOleksandr Tymoshenko 
227*8ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
228*8ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
229*8ce574deSOleksandr Tymoshenko 
230*8ce574deSOleksandr Tymoshenko 	if (flags & GPIO_PIN_INPUT) {
231*8ce574deSOleksandr Tymoshenko 		val &= ~BIT(OUTPUT_ENABLE_OFF);
232*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_INPUT;
233*8ce574deSOleksandr Tymoshenko 	} else {
234*8ce574deSOleksandr Tymoshenko 		val |= BIT(OUTPUT_ENABLE_OFF);
235*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_flags = GPIO_PIN_OUTPUT;
236*8ce574deSOleksandr Tymoshenko 	}
237*8ce574deSOleksandr Tymoshenko 
238*8ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
239*8ce574deSOleksandr Tymoshenko 
240*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d flags 0x%x val 0x%x gp_flags 0x%x\n",
241*8ce574deSOleksandr Tymoshenko 		pin, flags, val, sc->sc_gpio_pins[pin].gp_flags);
242*8ce574deSOleksandr Tymoshenko 
243*8ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
244*8ce574deSOleksandr Tymoshenko 
245*8ce574deSOleksandr Tymoshenko 	return (0);
246*8ce574deSOleksandr Tymoshenko }
247*8ce574deSOleksandr Tymoshenko 
248*8ce574deSOleksandr Tymoshenko static int
249*8ce574deSOleksandr Tymoshenko amdgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
250*8ce574deSOleksandr Tymoshenko {
251*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
252*8ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
253*8ce574deSOleksandr Tymoshenko 
254*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
255*8ce574deSOleksandr Tymoshenko 
256*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
257*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
258*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
259*8ce574deSOleksandr Tymoshenko 
260*8ce574deSOleksandr Tymoshenko 	*value = 0;
261*8ce574deSOleksandr Tymoshenko 
262*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
263*8ce574deSOleksandr Tymoshenko 
264*8ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
265*8ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
266*8ce574deSOleksandr Tymoshenko 
267*8ce574deSOleksandr Tymoshenko 	if (val & BIT(OUTPUT_VALUE_OFF))
268*8ce574deSOleksandr Tymoshenko 		*value = GPIO_PIN_HIGH;
269*8ce574deSOleksandr Tymoshenko 	else
270*8ce574deSOleksandr Tymoshenko 		*value = GPIO_PIN_LOW;
271*8ce574deSOleksandr Tymoshenko 
272*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x\n", pin, *value);
273*8ce574deSOleksandr Tymoshenko 
274*8ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
275*8ce574deSOleksandr Tymoshenko 
276*8ce574deSOleksandr Tymoshenko 	return (0);
277*8ce574deSOleksandr Tymoshenko }
278*8ce574deSOleksandr Tymoshenko 
279*8ce574deSOleksandr Tymoshenko static int
280*8ce574deSOleksandr Tymoshenko amdgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
281*8ce574deSOleksandr Tymoshenko {
282*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
283*8ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
284*8ce574deSOleksandr Tymoshenko 
285*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
286*8ce574deSOleksandr Tymoshenko 
287*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x\n", pin, value);
288*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
289*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
290*8ce574deSOleksandr Tymoshenko 
291*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_is_pin_output(sc, pin))
292*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
293*8ce574deSOleksandr Tymoshenko 
294*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
295*8ce574deSOleksandr Tymoshenko 
296*8ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
297*8ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
298*8ce574deSOleksandr Tymoshenko 
299*8ce574deSOleksandr Tymoshenko 	if (value == GPIO_PIN_LOW)
300*8ce574deSOleksandr Tymoshenko 		val &= ~BIT(OUTPUT_VALUE_OFF);
301*8ce574deSOleksandr Tymoshenko 	else
302*8ce574deSOleksandr Tymoshenko 		val |= BIT(OUTPUT_VALUE_OFF);
303*8ce574deSOleksandr Tymoshenko 
304*8ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
305*8ce574deSOleksandr Tymoshenko 
306*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d value 0x%x val 0x%x\n", pin, value, val);
307*8ce574deSOleksandr Tymoshenko 
308*8ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
309*8ce574deSOleksandr Tymoshenko 
310*8ce574deSOleksandr Tymoshenko 	return (0);
311*8ce574deSOleksandr Tymoshenko }
312*8ce574deSOleksandr Tymoshenko 
313*8ce574deSOleksandr Tymoshenko static int
314*8ce574deSOleksandr Tymoshenko amdgpio_pin_toggle(device_t dev, uint32_t pin)
315*8ce574deSOleksandr Tymoshenko {
316*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
317*8ce574deSOleksandr Tymoshenko 	uint32_t reg, val;
318*8ce574deSOleksandr Tymoshenko 
319*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
320*8ce574deSOleksandr Tymoshenko 
321*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d\n", pin);
322*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_valid_pin(sc, pin))
323*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
324*8ce574deSOleksandr Tymoshenko 
325*8ce574deSOleksandr Tymoshenko 	if (!amdgpio_is_pin_output(sc, pin))
326*8ce574deSOleksandr Tymoshenko 		return (EINVAL);
327*8ce574deSOleksandr Tymoshenko 
328*8ce574deSOleksandr Tymoshenko 	/* Toggle the pin */
329*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK(sc);
330*8ce574deSOleksandr Tymoshenko 
331*8ce574deSOleksandr Tymoshenko 	reg = AMDGPIO_PIN_REGISTER(pin);
332*8ce574deSOleksandr Tymoshenko 	val = amdgpio_read_4(sc, reg);
333*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d value before 0x%x\n", pin, val);
334*8ce574deSOleksandr Tymoshenko 	val = val ^ BIT(OUTPUT_VALUE_OFF);
335*8ce574deSOleksandr Tymoshenko 	dprintf("pin %d value after 0x%x\n", pin, val);
336*8ce574deSOleksandr Tymoshenko 	amdgpio_write_4(sc, reg, val);
337*8ce574deSOleksandr Tymoshenko 
338*8ce574deSOleksandr Tymoshenko 	AMDGPIO_UNLOCK(sc);
339*8ce574deSOleksandr Tymoshenko 
340*8ce574deSOleksandr Tymoshenko 	return (0);
341*8ce574deSOleksandr Tymoshenko }
342*8ce574deSOleksandr Tymoshenko 
343*8ce574deSOleksandr Tymoshenko static int
344*8ce574deSOleksandr Tymoshenko amdgpio_probe(device_t dev)
345*8ce574deSOleksandr Tymoshenko {
346*8ce574deSOleksandr Tymoshenko 	static char *gpio_ids[] = { "AMD0030", "AMDI0030", NULL };
347*8ce574deSOleksandr Tymoshenko 
348*8ce574deSOleksandr Tymoshenko 	if (acpi_disabled("gpio") ||
349*8ce574deSOleksandr Tymoshenko 		ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids) == NULL)
350*8ce574deSOleksandr Tymoshenko 	return (ENXIO);
351*8ce574deSOleksandr Tymoshenko 
352*8ce574deSOleksandr Tymoshenko 	device_set_desc(dev, "AMD GPIO Controller");
353*8ce574deSOleksandr Tymoshenko 	return (0);
354*8ce574deSOleksandr Tymoshenko }
355*8ce574deSOleksandr Tymoshenko 
356*8ce574deSOleksandr Tymoshenko static int
357*8ce574deSOleksandr Tymoshenko amdgpio_attach(device_t dev)
358*8ce574deSOleksandr Tymoshenko {
359*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
360*8ce574deSOleksandr Tymoshenko 	int i, pin, bank;
361*8ce574deSOleksandr Tymoshenko 
362*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
363*8ce574deSOleksandr Tymoshenko 	sc->sc_dev = dev;
364*8ce574deSOleksandr Tymoshenko 	sc->sc_handle = acpi_get_handle(dev);
365*8ce574deSOleksandr Tymoshenko 
366*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_INIT(sc);
367*8ce574deSOleksandr Tymoshenko 
368*8ce574deSOleksandr Tymoshenko 	sc->sc_nbanks = AMD_GPIO_NUM_PIN_BANK;
369*8ce574deSOleksandr Tymoshenko 	sc->sc_npins = AMD_GPIO_PINS_MAX;
370*8ce574deSOleksandr Tymoshenko 	sc->sc_bank_prefix = AMD_GPIO_PREFIX;
371*8ce574deSOleksandr Tymoshenko 	sc->sc_pin_info = kernzp_pins;
372*8ce574deSOleksandr Tymoshenko 	sc->sc_ngroups = nitems(kernzp_groups);
373*8ce574deSOleksandr Tymoshenko 	sc->sc_groups = kernzp_groups;
374*8ce574deSOleksandr Tymoshenko 
375*8ce574deSOleksandr Tymoshenko 	if (bus_alloc_resources(dev, amdgpio_spec, sc->sc_res)) {
376*8ce574deSOleksandr Tymoshenko 		device_printf(dev, "could not allocate resources\n");
377*8ce574deSOleksandr Tymoshenko 		goto err_rsrc;
378*8ce574deSOleksandr Tymoshenko 	}
379*8ce574deSOleksandr Tymoshenko 
380*8ce574deSOleksandr Tymoshenko 	sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
381*8ce574deSOleksandr Tymoshenko 	sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
382*8ce574deSOleksandr Tymoshenko 
383*8ce574deSOleksandr Tymoshenko 	/* Initialize all possible pins to be Invalid */
384*8ce574deSOleksandr Tymoshenko 	for (i = 0; i < AMD_GPIO_PINS_MAX ; i++) {
385*8ce574deSOleksandr Tymoshenko 		snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
386*8ce574deSOleksandr Tymoshenko 			"Unexposed PIN %d\n", i);
387*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_pin = -1;
388*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_caps = 0;
389*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[i].gp_flags = 0;
390*8ce574deSOleksandr Tymoshenko 	}
391*8ce574deSOleksandr Tymoshenko 
392*8ce574deSOleksandr Tymoshenko 	/* Initialize only driver exposed pins with appropriate capabilities */
393*8ce574deSOleksandr Tymoshenko 	for (i = 0; i < AMD_GPIO_PINS_EXPOSED ; i++) {
394*8ce574deSOleksandr Tymoshenko 		pin = kernzp_pins[i].pin_num;
395*8ce574deSOleksandr Tymoshenko 		bank = pin/AMD_GPIO_PINS_PER_BANK;
396*8ce574deSOleksandr Tymoshenko 		snprintf(sc->sc_gpio_pins[pin].gp_name, GPIOMAXNAME, "%s%d_%s\n",
397*8ce574deSOleksandr Tymoshenko 			AMD_GPIO_PREFIX, bank, kernzp_pins[i].pin_name);
398*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_pin = pin;
399*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_caps = AMDGPIO_DEFAULT_CAPS;
400*8ce574deSOleksandr Tymoshenko 		sc->sc_gpio_pins[pin].gp_flags = (amdgpio_is_pin_output(sc, pin)?
401*8ce574deSOleksandr Tymoshenko 						GPIO_PIN_OUTPUT : GPIO_PIN_INPUT);
402*8ce574deSOleksandr Tymoshenko 	}
403*8ce574deSOleksandr Tymoshenko 
404*8ce574deSOleksandr Tymoshenko 	sc->sc_busdev = gpiobus_attach_bus(dev);
405*8ce574deSOleksandr Tymoshenko 	if (sc->sc_busdev == NULL) {
406*8ce574deSOleksandr Tymoshenko 		device_printf(dev, "could not attach gpiobus\n");
407*8ce574deSOleksandr Tymoshenko 		goto err_bus;
408*8ce574deSOleksandr Tymoshenko 	}
409*8ce574deSOleksandr Tymoshenko 
410*8ce574deSOleksandr Tymoshenko 	return (0);
411*8ce574deSOleksandr Tymoshenko 
412*8ce574deSOleksandr Tymoshenko err_bus:
413*8ce574deSOleksandr Tymoshenko 	bus_release_resources(dev, amdgpio_spec, sc->sc_res);
414*8ce574deSOleksandr Tymoshenko 
415*8ce574deSOleksandr Tymoshenko err_rsrc:
416*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_DESTROY(sc);
417*8ce574deSOleksandr Tymoshenko 
418*8ce574deSOleksandr Tymoshenko 	return (ENXIO);
419*8ce574deSOleksandr Tymoshenko }
420*8ce574deSOleksandr Tymoshenko 
421*8ce574deSOleksandr Tymoshenko 
422*8ce574deSOleksandr Tymoshenko static int
423*8ce574deSOleksandr Tymoshenko amdgpio_detach(device_t dev)
424*8ce574deSOleksandr Tymoshenko {
425*8ce574deSOleksandr Tymoshenko 	struct amdgpio_softc *sc;
426*8ce574deSOleksandr Tymoshenko 	sc = device_get_softc(dev);
427*8ce574deSOleksandr Tymoshenko 
428*8ce574deSOleksandr Tymoshenko 	if (sc->sc_busdev)
429*8ce574deSOleksandr Tymoshenko 		gpiobus_detach_bus(dev);
430*8ce574deSOleksandr Tymoshenko 
431*8ce574deSOleksandr Tymoshenko 	bus_release_resources(dev, amdgpio_spec, sc->sc_res);
432*8ce574deSOleksandr Tymoshenko 
433*8ce574deSOleksandr Tymoshenko 	AMDGPIO_LOCK_DESTROY(sc);
434*8ce574deSOleksandr Tymoshenko 
435*8ce574deSOleksandr Tymoshenko 	return (0);
436*8ce574deSOleksandr Tymoshenko }
437*8ce574deSOleksandr Tymoshenko 
438*8ce574deSOleksandr Tymoshenko static device_method_t amdgpio_methods[] = {
439*8ce574deSOleksandr Tymoshenko 	/* Device interface */
440*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_probe, amdgpio_probe),
441*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_attach, amdgpio_attach),
442*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(device_detach, amdgpio_detach),
443*8ce574deSOleksandr Tymoshenko 
444*8ce574deSOleksandr Tymoshenko 	/* GPIO protocol */
445*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_get_bus, amdgpio_get_bus),
446*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_max, amdgpio_pin_max),
447*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getname, amdgpio_pin_getname),
448*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getcaps, amdgpio_pin_getcaps),
449*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_getflags, amdgpio_pin_getflags),
450*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_setflags, amdgpio_pin_setflags),
451*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_get, amdgpio_pin_get),
452*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_set, amdgpio_pin_set),
453*8ce574deSOleksandr Tymoshenko 	DEVMETHOD(gpio_pin_toggle, amdgpio_pin_toggle),
454*8ce574deSOleksandr Tymoshenko 
455*8ce574deSOleksandr Tymoshenko 	DEVMETHOD_END
456*8ce574deSOleksandr Tymoshenko };
457*8ce574deSOleksandr Tymoshenko 
458*8ce574deSOleksandr Tymoshenko static driver_t amdgpio_driver = {
459*8ce574deSOleksandr Tymoshenko 	"gpio",
460*8ce574deSOleksandr Tymoshenko 	amdgpio_methods,
461*8ce574deSOleksandr Tymoshenko 	sizeof(struct amdgpio_softc),
462*8ce574deSOleksandr Tymoshenko };
463*8ce574deSOleksandr Tymoshenko 
464*8ce574deSOleksandr Tymoshenko static devclass_t amdgpio_devclass;
465*8ce574deSOleksandr Tymoshenko DRIVER_MODULE(amdgpio, acpi, amdgpio_driver, amdgpio_devclass, 0, 0);
466*8ce574deSOleksandr Tymoshenko MODULE_DEPEND(amdgpio, acpi, 1, 1, 1);
467*8ce574deSOleksandr Tymoshenko MODULE_DEPEND(amdgpio, gpiobus, 1, 1, 1);
468*8ce574deSOleksandr Tymoshenko MODULE_VERSION(amdgpio, 1);
469