xref: /netbsd-src/sys/arch/arm/sunxi/sunxi_gpio.c (revision c7941324122a924b6e20c322a6b49a6a3b6137e7)
1*c7941324Sskrll /* $NetBSD: sunxi_gpio.c,v 1.39 2024/08/13 07:20:23 skrll Exp $ */
249f361a5Sjmcneill 
349f361a5Sjmcneill /*-
449f361a5Sjmcneill  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
549f361a5Sjmcneill  * All rights reserved.
649f361a5Sjmcneill  *
749f361a5Sjmcneill  * Redistribution and use in source and binary forms, with or without
849f361a5Sjmcneill  * modification, are permitted provided that the following conditions
949f361a5Sjmcneill  * are met:
1049f361a5Sjmcneill  * 1. Redistributions of source code must retain the above copyright
1149f361a5Sjmcneill  *    notice, this list of conditions and the following disclaimer.
1249f361a5Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1349f361a5Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
1449f361a5Sjmcneill  *    documentation and/or other materials provided with the distribution.
1549f361a5Sjmcneill  *
1649f361a5Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1749f361a5Sjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1849f361a5Sjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1949f361a5Sjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2049f361a5Sjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2149f361a5Sjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2249f361a5Sjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2349f361a5Sjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2449f361a5Sjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2549f361a5Sjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2649f361a5Sjmcneill  * SUCH DAMAGE.
2749f361a5Sjmcneill  */
2849f361a5Sjmcneill 
2949f361a5Sjmcneill #include "opt_soc.h"
3049f361a5Sjmcneill 
3149f361a5Sjmcneill #include <sys/cdefs.h>
32*c7941324Sskrll __KERNEL_RCSID(0, "$NetBSD: sunxi_gpio.c,v 1.39 2024/08/13 07:20:23 skrll Exp $");
3349f361a5Sjmcneill 
3449f361a5Sjmcneill #include <sys/param.h>
3549f361a5Sjmcneill #include <sys/bus.h>
3649f361a5Sjmcneill #include <sys/device.h>
3749f361a5Sjmcneill #include <sys/intr.h>
3849f361a5Sjmcneill #include <sys/systm.h>
3949f361a5Sjmcneill #include <sys/mutex.h>
4049f361a5Sjmcneill #include <sys/kmem.h>
4149f361a5Sjmcneill #include <sys/gpio.h>
4254287382Sjmcneill #include <sys/bitops.h>
43317b609aSjmcneill #include <sys/lwp.h>
4449f361a5Sjmcneill 
4549f361a5Sjmcneill #include <dev/fdt/fdtvar.h>
46cddb63cbSjmcneill #include <dev/gpio/gpiovar.h>
4749f361a5Sjmcneill 
4849f361a5Sjmcneill #include <arm/sunxi/sunxi_gpio.h>
4949f361a5Sjmcneill 
50d72f6453Sjmcneill #define	SUNXI_GPIO_MAX_EINT_BANK	5
5154287382Sjmcneill #define	SUNXI_GPIO_MAX_EINT		32
5254287382Sjmcneill 
53e163685eSjmcneill #define	SUNXI_GPIO_MAX_BANK		26
54e163685eSjmcneill 
5562e898a0Sjmcneill #define	SUNXI_GPIO_PORT(port)		(0x24 * (port))
5662e898a0Sjmcneill #define SUNXI_GPIO_CFG(port, pin)	(SUNXI_GPIO_PORT(port) + 0x00 + (0x4 * ((pin) / 8)))
577f9d4e51Sskrll #define  SUNXI_GPIO_CFG_PINMASK(pin)	(0x7U << (((pin) % 8) * 4))
5849f361a5Sjmcneill #define	SUNXI_GPIO_DATA(port)		(SUNXI_GPIO_PORT(port) + 0x10)
5949f361a5Sjmcneill #define	SUNXI_GPIO_DRV(port, pin)	(SUNXI_GPIO_PORT(port) + 0x14 + (0x4 * ((pin) / 16)))
607f9d4e51Sskrll #define  SUNXI_GPIO_DRV_PINMASK(pin)	(0x3U << (((pin) % 16) * 2))
6149f361a5Sjmcneill #define	SUNXI_GPIO_PULL(port, pin)	(SUNXI_GPIO_PORT(port) + 0x1c + (0x4 * ((pin) / 16)))
623d7f7a9dSjmcneill #define	 SUNXI_GPIO_PULL_DISABLE	0
633d7f7a9dSjmcneill #define	 SUNXI_GPIO_PULL_UP		1
643d7f7a9dSjmcneill #define	 SUNXI_GPIO_PULL_DOWN		2
657f9d4e51Sskrll #define  SUNXI_GPIO_PULL_PINMASK(pin)	(0x3U << (((pin) % 16) * 2))
66d72f6453Sjmcneill #define	SUNXI_GPIO_INT_CFG(bank, eint)	(0x200 + (0x20 * (bank)) + (0x4 * ((eint) / 8)))
677f9d4e51Sskrll #define	 SUNXI_GPIO_INT_MODEMASK(eint)	(0xfU << (((eint) % 8) * 4))
6854287382Sjmcneill #define	  SUNXI_GPIO_INT_MODE_POS_EDGE		0x0
6954287382Sjmcneill #define	  SUNXI_GPIO_INT_MODE_NEG_EDGE		0x1
7054287382Sjmcneill #define	  SUNXI_GPIO_INT_MODE_HIGH_LEVEL	0x2
7154287382Sjmcneill #define	  SUNXI_GPIO_INT_MODE_LOW_LEVEL		0x3
7254287382Sjmcneill #define	  SUNXI_GPIO_INT_MODE_DOUBLE_EDGE	0x4
73d72f6453Sjmcneill #define	SUNXI_GPIO_INT_CTL(bank)	(0x210 + 0x20 * (bank))
74d72f6453Sjmcneill #define	SUNXI_GPIO_INT_STATUS(bank)	(0x214 + 0x20 * (bank))
7518f7b1b9Stnn #define	SUNXI_GPIO_INT_DEBOUNCE(bank)	(0x218 + 0x20 * (bank))
7618f7b1b9Stnn #define	  SUNXI_GPIO_INT_DEBOUNCE_CLK_PRESCALE	__BITS(6,4)
7718f7b1b9Stnn #define	  SUNXI_GPIO_INT_DEBOUNCE_CLK_SEL	__BIT(0)
78e163685eSjmcneill #define	SUNXI_GPIO_GRP_CONFIG(bank)	(0x300 + 0x4 * (bank))
79e163685eSjmcneill #define	 SUNXI_GPIO_GRP_IO_BIAS_CONFIGMASK	0xf
8049f361a5Sjmcneill 
81646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
82671537beSjmcneill #ifdef SOC_SUN4I_A10
83646c0f59Sthorpej 	{ .compat = "allwinner,sun4i-a10-pinctrl",
84646c0f59Sthorpej 	  .data = &sun4i_a10_padconf },
85671537beSjmcneill #endif
8669b44ac7Sjmcneill #ifdef SOC_SUN5I_A13
87646c0f59Sthorpej 	{ .compat = "allwinner,sun5i-a13-pinctrl",
88646c0f59Sthorpej 	  .data = &sun5i_a13_padconf },
89646c0f59Sthorpej 	{ .compat = "nextthing,gr8-pinctrl",
90646c0f59Sthorpej 	  .data = &sun5i_a13_padconf },
9169b44ac7Sjmcneill #endif
9249f361a5Sjmcneill #ifdef SOC_SUN6I_A31
93646c0f59Sthorpej 	{ .compat = "allwinner,sun6i-a31-pinctrl",
94646c0f59Sthorpej 	  .data = &sun6i_a31_padconf },
95646c0f59Sthorpej 	{ .compat = "allwinner,sun6i-a31-r-pinctrl",
96646c0f59Sthorpej 	  .data = &sun6i_a31_r_padconf },
9749f361a5Sjmcneill #endif
98671537beSjmcneill #ifdef SOC_SUN7I_A20
99646c0f59Sthorpej 	{ .compat = "allwinner,sun7i-a20-pinctrl",
100646c0f59Sthorpej 	  .data = &sun7i_a20_padconf },
101671537beSjmcneill #endif
1026685c529Sjmcneill #ifdef SOC_SUN8I_A83T
103646c0f59Sthorpej 	{ .compat = "allwinner,sun8i-a83t-pinctrl",
104646c0f59Sthorpej 	  .data = &sun8i_a83t_padconf },
105646c0f59Sthorpej 	{ .compat = "allwinner,sun8i-a83t-r-pinctrl",
106646c0f59Sthorpej 	  .data = &sun8i_a83t_r_padconf },
1076685c529Sjmcneill #endif
10849f361a5Sjmcneill #ifdef SOC_SUN8I_H3
109646c0f59Sthorpej 	{ .compat = "allwinner,sun8i-h3-pinctrl",
110646c0f59Sthorpej 	  .data = &sun8i_h3_padconf },
111646c0f59Sthorpej 	{ .compat = "allwinner,sun8i-h3-r-pinctrl",
112646c0f59Sthorpej 	  .data = &sun8i_h3_r_padconf },
11349f361a5Sjmcneill #endif
1145639be33Sskrll #ifdef SOC_SUN8I_V3S
1155639be33Sskrll 	{ .compat = "allwinner,sun8i-v3s-pinctrl",
1165639be33Sskrll 	  .data = &sun8i_v3s_padconf },
1175639be33Sskrll #endif
118d72f6453Sjmcneill #ifdef SOC_SUN9I_A80
119646c0f59Sthorpej 	{ .compat = "allwinner,sun9i-a80-pinctrl",
120646c0f59Sthorpej 	  .data = &sun9i_a80_padconf },
121646c0f59Sthorpej 	{ .compat = "allwinner,sun9i-a80-r-pinctrl",
122646c0f59Sthorpej 	  .data = &sun9i_a80_r_padconf },
123d72f6453Sjmcneill #endif
124*c7941324Sskrll #ifdef SOC_SUN20I_D1
125*c7941324Sskrll 	{ .compat = "allwinner,sun20i-d1-pinctrl",
126*c7941324Sskrll 	  .data = &sun20i_d1_padconf },
127*c7941324Sskrll #endif
12857b6b99fSjmcneill #ifdef SOC_SUN50I_A64
129646c0f59Sthorpej 	{ .compat = "allwinner,sun50i-a64-pinctrl",
130646c0f59Sthorpej 	  .data = &sun50i_a64_padconf },
131646c0f59Sthorpej 	{ .compat = "allwinner,sun50i-a64-r-pinctrl",
132646c0f59Sthorpej 	  .data = &sun50i_a64_r_padconf },
13357b6b99fSjmcneill #endif
134be91d075Sjmcneill #ifdef SOC_SUN50I_H5
135646c0f59Sthorpej 	{ .compat = "allwinner,sun50i-h5-pinctrl",
136646c0f59Sthorpej 	  .data = &sun8i_h3_padconf },
137646c0f59Sthorpej 	{ .compat = "allwinner,sun50i-h5-r-pinctrl",
138646c0f59Sthorpej 	  .data = &sun8i_h3_r_padconf },
139be91d075Sjmcneill #endif
14068e0a2cdSjmcneill #ifdef SOC_SUN50I_H6
141646c0f59Sthorpej 	{ .compat = "allwinner,sun50i-h6-pinctrl",
142646c0f59Sthorpej 	  .data = &sun50i_h6_padconf },
143646c0f59Sthorpej 	{ .compat = "allwinner,sun50i-h6-r-pinctrl",
144646c0f59Sthorpej 	  .data = &sun50i_h6_r_padconf },
14568e0a2cdSjmcneill #endif
146ec189949Sthorpej 	DEVICE_COMPAT_EOL
14749f361a5Sjmcneill };
14849f361a5Sjmcneill 
14954287382Sjmcneill struct sunxi_gpio_eint {
15054287382Sjmcneill 	int (*eint_func)(void *);
15154287382Sjmcneill 	void *eint_arg;
15213cad5b9Stnn 	bool eint_mpsafe;
153d72f6453Sjmcneill 	int eint_bank;
15454287382Sjmcneill 	int eint_num;
15554287382Sjmcneill };
15654287382Sjmcneill 
15749f361a5Sjmcneill struct sunxi_gpio_softc {
15849f361a5Sjmcneill 	device_t sc_dev;
15949f361a5Sjmcneill 	bus_space_tag_t sc_bst;
16049f361a5Sjmcneill 	bus_space_handle_t sc_bsh;
161e163685eSjmcneill 	int sc_phandle;
16249f361a5Sjmcneill 	const struct sunxi_gpio_padconf *sc_padconf;
163cddb63cbSjmcneill 	kmutex_t sc_lock;
164cddb63cbSjmcneill 
165cddb63cbSjmcneill 	struct gpio_chipset_tag sc_gp;
166cddb63cbSjmcneill 	gpio_pin_t *sc_pins;
167cddb63cbSjmcneill 	device_t sc_gpiodev;
16854287382Sjmcneill 
169e163685eSjmcneill 	struct fdtbus_regulator *sc_pin_supply[SUNXI_GPIO_MAX_BANK];
170e163685eSjmcneill 
171d72f6453Sjmcneill 	u_int sc_eint_bank_max;
172d72f6453Sjmcneill 
17354287382Sjmcneill 	void *sc_ih;
174d72f6453Sjmcneill 	struct sunxi_gpio_eint sc_eint[SUNXI_GPIO_MAX_EINT_BANK][SUNXI_GPIO_MAX_EINT];
17549f361a5Sjmcneill };
17649f361a5Sjmcneill 
17749f361a5Sjmcneill struct sunxi_gpio_pin {
17849f361a5Sjmcneill 	struct sunxi_gpio_softc *pin_sc;
17949f361a5Sjmcneill 	const struct sunxi_gpio_pins *pin_def;
18049f361a5Sjmcneill 	int pin_flags;
18149f361a5Sjmcneill 	bool pin_actlo;
18249f361a5Sjmcneill };
18349f361a5Sjmcneill 
18449f361a5Sjmcneill #define GPIO_READ(sc, reg) 		\
18549f361a5Sjmcneill     bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
18649f361a5Sjmcneill #define GPIO_WRITE(sc, reg, val) 	\
18749f361a5Sjmcneill     bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
18849f361a5Sjmcneill 
18949f361a5Sjmcneill static int	sunxi_gpio_match(device_t, cfdata_t, void *);
19049f361a5Sjmcneill static void	sunxi_gpio_attach(device_t, device_t, void *);
19149f361a5Sjmcneill 
19249f361a5Sjmcneill CFATTACH_DECL_NEW(sunxi_gpio, sizeof(struct sunxi_gpio_softc),
19349f361a5Sjmcneill 	sunxi_gpio_match, sunxi_gpio_attach, NULL, NULL);
19449f361a5Sjmcneill 
19549f361a5Sjmcneill static const struct sunxi_gpio_pins *
19649f361a5Sjmcneill sunxi_gpio_lookup(struct sunxi_gpio_softc *sc, uint8_t port, uint8_t pin)
19749f361a5Sjmcneill {
19849f361a5Sjmcneill 	const struct sunxi_gpio_pins *pin_def;
19949f361a5Sjmcneill 	u_int n;
20049f361a5Sjmcneill 
20149f361a5Sjmcneill 	for (n = 0; n < sc->sc_padconf->npins; n++) {
20249f361a5Sjmcneill 		pin_def = &sc->sc_padconf->pins[n];
20349f361a5Sjmcneill 		if (pin_def->port == port && pin_def->pin == pin)
20449f361a5Sjmcneill 			return pin_def;
20549f361a5Sjmcneill 	}
20649f361a5Sjmcneill 
20749f361a5Sjmcneill 	return NULL;
20849f361a5Sjmcneill }
20949f361a5Sjmcneill 
2103d7f7a9dSjmcneill static const struct sunxi_gpio_pins *
2113d7f7a9dSjmcneill sunxi_gpio_lookup_byname(struct sunxi_gpio_softc *sc, const char *name)
2123d7f7a9dSjmcneill {
2133d7f7a9dSjmcneill 	const struct sunxi_gpio_pins *pin_def;
2143d7f7a9dSjmcneill 	u_int n;
2153d7f7a9dSjmcneill 
2163d7f7a9dSjmcneill 	for (n = 0; n < sc->sc_padconf->npins; n++) {
2173d7f7a9dSjmcneill 		pin_def = &sc->sc_padconf->pins[n];
2183d7f7a9dSjmcneill 		if (strcmp(pin_def->name, name) == 0)
2193d7f7a9dSjmcneill 			return pin_def;
2203d7f7a9dSjmcneill 	}
2213d7f7a9dSjmcneill 
2223d7f7a9dSjmcneill 	return NULL;
2233d7f7a9dSjmcneill }
2243d7f7a9dSjmcneill 
22549f361a5Sjmcneill static int
22649f361a5Sjmcneill sunxi_gpio_setfunc(struct sunxi_gpio_softc *sc,
22749f361a5Sjmcneill     const struct sunxi_gpio_pins *pin_def, const char *func)
22849f361a5Sjmcneill {
22949f361a5Sjmcneill 	uint32_t cfg;
23049f361a5Sjmcneill 	u_int n;
23149f361a5Sjmcneill 
232cddb63cbSjmcneill 	KASSERT(mutex_owned(&sc->sc_lock));
233cddb63cbSjmcneill 
23449f361a5Sjmcneill 	const bus_size_t cfg_reg = SUNXI_GPIO_CFG(pin_def->port, pin_def->pin);
23549f361a5Sjmcneill 	const uint32_t cfg_mask = SUNXI_GPIO_CFG_PINMASK(pin_def->pin);
23649f361a5Sjmcneill 
23749f361a5Sjmcneill 	for (n = 0; n < SUNXI_GPIO_MAXFUNC; n++) {
23849f361a5Sjmcneill 		if (pin_def->functions[n] == NULL)
23949f361a5Sjmcneill 			continue;
24049f361a5Sjmcneill 		if (strcmp(pin_def->functions[n], func) == 0) {
24149f361a5Sjmcneill 			cfg = GPIO_READ(sc, cfg_reg);
24249f361a5Sjmcneill 			cfg &= ~cfg_mask;
24349f361a5Sjmcneill 			cfg |= __SHIFTIN(n, cfg_mask);
24462e898a0Sjmcneill #ifdef SUNXI_GPIO_DEBUG
24562e898a0Sjmcneill 			device_printf(sc->sc_dev, "P%c%02d cfg %08x -> %08x\n",
24662e898a0Sjmcneill 			    pin_def->port + 'A', pin_def->pin, GPIO_READ(sc, cfg_reg), cfg);
24762e898a0Sjmcneill #endif
24849f361a5Sjmcneill 			GPIO_WRITE(sc, cfg_reg, cfg);
24949f361a5Sjmcneill 			return 0;
25049f361a5Sjmcneill 		}
25149f361a5Sjmcneill 	}
25249f361a5Sjmcneill 
25349f361a5Sjmcneill 	/* Function not found */
25449f361a5Sjmcneill 	device_printf(sc->sc_dev, "function '%s' not supported on P%c%02d\n",
25549f361a5Sjmcneill 	    func, pin_def->port + 'A', pin_def->pin);
25649f361a5Sjmcneill 
25749f361a5Sjmcneill 	return ENXIO;
25849f361a5Sjmcneill }
25949f361a5Sjmcneill 
26049f361a5Sjmcneill static int
2613d7f7a9dSjmcneill sunxi_gpio_setpull(struct sunxi_gpio_softc *sc,
2623d7f7a9dSjmcneill     const struct sunxi_gpio_pins *pin_def, int flags)
2633d7f7a9dSjmcneill {
2643d7f7a9dSjmcneill 	uint32_t pull;
2653d7f7a9dSjmcneill 
266cddb63cbSjmcneill 	KASSERT(mutex_owned(&sc->sc_lock));
267cddb63cbSjmcneill 
2683d7f7a9dSjmcneill 	const bus_size_t pull_reg = SUNXI_GPIO_PULL(pin_def->port, pin_def->pin);
2693d7f7a9dSjmcneill 	const uint32_t pull_mask = SUNXI_GPIO_PULL_PINMASK(pin_def->pin);
2703d7f7a9dSjmcneill 
2713d7f7a9dSjmcneill 	pull = GPIO_READ(sc, pull_reg);
2723d7f7a9dSjmcneill 	pull &= ~pull_mask;
2733d7f7a9dSjmcneill 	if (flags & GPIO_PIN_PULLUP)
2743d7f7a9dSjmcneill 		pull |= __SHIFTIN(SUNXI_GPIO_PULL_UP, pull_mask);
2753d7f7a9dSjmcneill 	else if (flags & GPIO_PIN_PULLDOWN)
2763d7f7a9dSjmcneill 		pull |= __SHIFTIN(SUNXI_GPIO_PULL_DOWN, pull_mask);
2773d7f7a9dSjmcneill 	else
2783d7f7a9dSjmcneill 		pull |= __SHIFTIN(SUNXI_GPIO_PULL_DISABLE, pull_mask);
27962e898a0Sjmcneill #ifdef SUNXI_GPIO_DEBUG
28062e898a0Sjmcneill 	device_printf(sc->sc_dev, "P%c%02d pull %08x -> %08x\n",
28162e898a0Sjmcneill 	    pin_def->port + 'A', pin_def->pin, GPIO_READ(sc, pull_reg), pull);
28262e898a0Sjmcneill #endif
2833d7f7a9dSjmcneill 	GPIO_WRITE(sc, pull_reg, pull);
2843d7f7a9dSjmcneill 
2853d7f7a9dSjmcneill 	return 0;
2863d7f7a9dSjmcneill }
2873d7f7a9dSjmcneill 
2883d7f7a9dSjmcneill static int
2893d7f7a9dSjmcneill sunxi_gpio_setdrv(struct sunxi_gpio_softc *sc,
2903d7f7a9dSjmcneill     const struct sunxi_gpio_pins *pin_def, int drive_strength)
2913d7f7a9dSjmcneill {
2923d7f7a9dSjmcneill 	uint32_t drv;
2933d7f7a9dSjmcneill 
294cddb63cbSjmcneill 	KASSERT(mutex_owned(&sc->sc_lock));
295cddb63cbSjmcneill 
2963d7f7a9dSjmcneill 	if (drive_strength < 10 || drive_strength > 40)
2973d7f7a9dSjmcneill 		return EINVAL;
2983d7f7a9dSjmcneill 
2993d7f7a9dSjmcneill 	const bus_size_t drv_reg = SUNXI_GPIO_DRV(pin_def->port, pin_def->pin);
3003d7f7a9dSjmcneill 	const uint32_t drv_mask = SUNXI_GPIO_DRV_PINMASK(pin_def->pin);
3013d7f7a9dSjmcneill 
3023d7f7a9dSjmcneill 	drv = GPIO_READ(sc, drv_reg);
3033d7f7a9dSjmcneill 	drv &= ~drv_mask;
3043d7f7a9dSjmcneill 	drv |= __SHIFTIN((drive_strength / 10) - 1, drv_mask);
30562e898a0Sjmcneill #ifdef SUNXI_GPIO_DEBUG
30662e898a0Sjmcneill 	device_printf(sc->sc_dev, "P%c%02d drv %08x -> %08x\n",
30762e898a0Sjmcneill 	    pin_def->port + 'A', pin_def->pin, GPIO_READ(sc, drv_reg), drv);
30862e898a0Sjmcneill #endif
3093d7f7a9dSjmcneill 	GPIO_WRITE(sc, drv_reg, drv);
3103d7f7a9dSjmcneill 
3113d7f7a9dSjmcneill 	return 0;
3123d7f7a9dSjmcneill }
3133d7f7a9dSjmcneill 
3143d7f7a9dSjmcneill static int
31549f361a5Sjmcneill sunxi_gpio_ctl(struct sunxi_gpio_softc *sc, const struct sunxi_gpio_pins *pin_def,
31649f361a5Sjmcneill     int flags)
31749f361a5Sjmcneill {
318cddb63cbSjmcneill 	KASSERT(mutex_owned(&sc->sc_lock));
319cddb63cbSjmcneill 
32049f361a5Sjmcneill 	if (flags & GPIO_PIN_INPUT)
32149f361a5Sjmcneill 		return sunxi_gpio_setfunc(sc, pin_def, "gpio_in");
32249f361a5Sjmcneill 	if (flags & GPIO_PIN_OUTPUT)
32349f361a5Sjmcneill 		return sunxi_gpio_setfunc(sc, pin_def, "gpio_out");
32449f361a5Sjmcneill 
32549f361a5Sjmcneill 	return EINVAL;
32649f361a5Sjmcneill }
32749f361a5Sjmcneill 
32849f361a5Sjmcneill static void *
32949f361a5Sjmcneill sunxi_gpio_acquire(device_t dev, const void *data, size_t len, int flags)
33049f361a5Sjmcneill {
33149f361a5Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
33249f361a5Sjmcneill 	const struct sunxi_gpio_pins *pin_def;
33349f361a5Sjmcneill 	struct sunxi_gpio_pin *gpin;
33449f361a5Sjmcneill 	const u_int *gpio = data;
33549f361a5Sjmcneill 	int error;
33649f361a5Sjmcneill 
33749f361a5Sjmcneill 	if (len != 16)
33849f361a5Sjmcneill 		return NULL;
33949f361a5Sjmcneill 
34049f361a5Sjmcneill 	const uint8_t port = be32toh(gpio[1]) & 0xff;
34149f361a5Sjmcneill 	const uint8_t pin = be32toh(gpio[2]) & 0xff;
34249f361a5Sjmcneill 	const bool actlo = be32toh(gpio[3]) & 1;
34349f361a5Sjmcneill 
34449f361a5Sjmcneill 	pin_def = sunxi_gpio_lookup(sc, port, pin);
34549f361a5Sjmcneill 	if (pin_def == NULL)
34649f361a5Sjmcneill 		return NULL;
34749f361a5Sjmcneill 
348cddb63cbSjmcneill 	mutex_enter(&sc->sc_lock);
34949f361a5Sjmcneill 	error = sunxi_gpio_ctl(sc, pin_def, flags);
350cddb63cbSjmcneill 	mutex_exit(&sc->sc_lock);
351cddb63cbSjmcneill 
35249f361a5Sjmcneill 	if (error != 0)
35349f361a5Sjmcneill 		return NULL;
35449f361a5Sjmcneill 
35549f361a5Sjmcneill 	gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP);
35649f361a5Sjmcneill 	gpin->pin_sc = sc;
35749f361a5Sjmcneill 	gpin->pin_def = pin_def;
35849f361a5Sjmcneill 	gpin->pin_flags = flags;
35949f361a5Sjmcneill 	gpin->pin_actlo = actlo;
36049f361a5Sjmcneill 
36149f361a5Sjmcneill 	return gpin;
36249f361a5Sjmcneill }
36349f361a5Sjmcneill 
36449f361a5Sjmcneill static void
36549f361a5Sjmcneill sunxi_gpio_release(device_t dev, void *priv)
36649f361a5Sjmcneill {
3674cc813e6Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
36849f361a5Sjmcneill 	struct sunxi_gpio_pin *pin = priv;
36949f361a5Sjmcneill 
3704cc813e6Sjmcneill 	mutex_enter(&sc->sc_lock);
37149f361a5Sjmcneill 	sunxi_gpio_ctl(pin->pin_sc, pin->pin_def, GPIO_PIN_INPUT);
3724cc813e6Sjmcneill 	mutex_exit(&sc->sc_lock);
37349f361a5Sjmcneill 
37449f361a5Sjmcneill 	kmem_free(pin, sizeof(*pin));
37549f361a5Sjmcneill }
37649f361a5Sjmcneill 
37749f361a5Sjmcneill static int
37849f361a5Sjmcneill sunxi_gpio_read(device_t dev, void *priv, bool raw)
37949f361a5Sjmcneill {
38049f361a5Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
38149f361a5Sjmcneill 	struct sunxi_gpio_pin *pin = priv;
38249f361a5Sjmcneill 	const struct sunxi_gpio_pins *pin_def = pin->pin_def;
38349f361a5Sjmcneill 	uint32_t data;
38449f361a5Sjmcneill 	int val;
38549f361a5Sjmcneill 
38649f361a5Sjmcneill 	KASSERT(sc == pin->pin_sc);
38749f361a5Sjmcneill 
38849f361a5Sjmcneill 	const bus_size_t data_reg = SUNXI_GPIO_DATA(pin_def->port);
38949f361a5Sjmcneill 	const uint32_t data_mask = __BIT(pin_def->pin);
39049f361a5Sjmcneill 
391cddb63cbSjmcneill 	/* No lock required for reads */
39249f361a5Sjmcneill 	data = GPIO_READ(sc, data_reg);
39349f361a5Sjmcneill 	val = __SHIFTOUT(data, data_mask);
39449f361a5Sjmcneill 	if (!raw && pin->pin_actlo)
39549f361a5Sjmcneill 		val = !val;
39649f361a5Sjmcneill 
39749f361a5Sjmcneill #ifdef SUNXI_GPIO_DEBUG
39849f361a5Sjmcneill 	device_printf(dev, "P%c%02d rd %08x (%d %d)\n",
39949f361a5Sjmcneill 	    pin_def->port + 'A', pin_def->pin, data,
40049f361a5Sjmcneill 	    __SHIFTOUT(val, data_mask), val);
40149f361a5Sjmcneill #endif
40249f361a5Sjmcneill 
40349f361a5Sjmcneill 	return val;
40449f361a5Sjmcneill }
40549f361a5Sjmcneill 
40649f361a5Sjmcneill static void
40749f361a5Sjmcneill sunxi_gpio_write(device_t dev, void *priv, int val, bool raw)
40849f361a5Sjmcneill {
40949f361a5Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
41049f361a5Sjmcneill 	struct sunxi_gpio_pin *pin = priv;
41149f361a5Sjmcneill 	const struct sunxi_gpio_pins *pin_def = pin->pin_def;
41249f361a5Sjmcneill 	uint32_t data;
41349f361a5Sjmcneill 
41449f361a5Sjmcneill 	KASSERT(sc == pin->pin_sc);
41549f361a5Sjmcneill 
41649f361a5Sjmcneill 	const bus_size_t data_reg = SUNXI_GPIO_DATA(pin_def->port);
41749f361a5Sjmcneill 	const uint32_t data_mask = __BIT(pin_def->pin);
41849f361a5Sjmcneill 
41949f361a5Sjmcneill 	if (!raw && pin->pin_actlo)
42049f361a5Sjmcneill 		val = !val;
42149f361a5Sjmcneill 
422cddb63cbSjmcneill 	mutex_enter(&sc->sc_lock);
42349f361a5Sjmcneill 	data = GPIO_READ(sc, data_reg);
42449f361a5Sjmcneill 	data &= ~data_mask;
42549f361a5Sjmcneill 	data |= __SHIFTIN(val, data_mask);
42649f361a5Sjmcneill #ifdef SUNXI_GPIO_DEBUG
42749f361a5Sjmcneill 	device_printf(dev, "P%c%02d wr %08x -> %08x\n",
42862e898a0Sjmcneill 	    pin_def->port + 'A', pin_def->pin, GPIO_READ(sc, data_reg), data);
42949f361a5Sjmcneill #endif
43098e6c063Sjmcneill 	GPIO_WRITE(sc, data_reg, data);
431cddb63cbSjmcneill 	mutex_exit(&sc->sc_lock);
43249f361a5Sjmcneill }
43349f361a5Sjmcneill 
43449f361a5Sjmcneill static struct fdtbus_gpio_controller_func sunxi_gpio_funcs = {
43549f361a5Sjmcneill 	.acquire = sunxi_gpio_acquire,
43649f361a5Sjmcneill 	.release = sunxi_gpio_release,
43749f361a5Sjmcneill 	.read = sunxi_gpio_read,
43849f361a5Sjmcneill 	.write = sunxi_gpio_write,
43949f361a5Sjmcneill };
44049f361a5Sjmcneill 
44154287382Sjmcneill static int
44254287382Sjmcneill sunxi_gpio_intr(void *priv)
44354287382Sjmcneill {
44454287382Sjmcneill 	struct sunxi_gpio_softc * const sc = priv;
44554287382Sjmcneill 	struct sunxi_gpio_eint *eint;
44654287382Sjmcneill 	uint32_t status, bit;
447d72f6453Sjmcneill 	u_int bank;
44854287382Sjmcneill 	int ret = 0;
44954287382Sjmcneill 
450d72f6453Sjmcneill 	for (bank = 0; bank <= sc->sc_eint_bank_max; bank++) {
451d72f6453Sjmcneill 		status = GPIO_READ(sc, SUNXI_GPIO_INT_STATUS(bank));
452d72f6453Sjmcneill 		if (status == 0)
453d72f6453Sjmcneill 			continue;
454d72f6453Sjmcneill 		GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS(bank), status);
45554287382Sjmcneill 
45654287382Sjmcneill 		while ((bit = ffs32(status)) != 0) {
45754287382Sjmcneill 			status &= ~__BIT(bit - 1);
458d72f6453Sjmcneill 			eint = &sc->sc_eint[bank][bit - 1];
45954287382Sjmcneill 			if (eint->eint_func == NULL)
46054287382Sjmcneill 				continue;
46113cad5b9Stnn 			if (!eint->eint_mpsafe)
46254287382Sjmcneill 				KERNEL_LOCK(1, curlwp);
46354287382Sjmcneill 			ret |= eint->eint_func(eint->eint_arg);
46413cad5b9Stnn 			if (!eint->eint_mpsafe)
46554287382Sjmcneill 				KERNEL_UNLOCK_ONE(curlwp);
46654287382Sjmcneill 		}
467d72f6453Sjmcneill 	}
46854287382Sjmcneill 
46954287382Sjmcneill 	return ret;
47054287382Sjmcneill }
47154287382Sjmcneill 
47254287382Sjmcneill static void *
47313cad5b9Stnn sunxi_intr_enable(struct sunxi_gpio_softc *sc,
47413cad5b9Stnn     const struct sunxi_gpio_pins *pin_def, u_int mode, bool mpsafe,
47513cad5b9Stnn     int (*func)(void *), void *arg)
47613cad5b9Stnn {
47713cad5b9Stnn 	uint32_t val;
47813cad5b9Stnn 	struct sunxi_gpio_eint *eint;
47913cad5b9Stnn 
48013cad5b9Stnn 	if (pin_def->functions[pin_def->eint_func] == NULL ||
48113cad5b9Stnn 	    strcmp(pin_def->functions[pin_def->eint_func], "irq") != 0)
48213cad5b9Stnn 		return NULL;
48313cad5b9Stnn 
48413cad5b9Stnn 	KASSERT(pin_def->eint_num < SUNXI_GPIO_MAX_EINT);
48513cad5b9Stnn 
48613cad5b9Stnn 	mutex_enter(&sc->sc_lock);
48713cad5b9Stnn 
48813cad5b9Stnn 	eint = &sc->sc_eint[pin_def->eint_bank][pin_def->eint_num];
48913cad5b9Stnn 	if (eint->eint_func != NULL) {
49013cad5b9Stnn 		mutex_exit(&sc->sc_lock);
49113cad5b9Stnn 		return NULL;	/* in use */
49213cad5b9Stnn 	}
49313cad5b9Stnn 
49413cad5b9Stnn 	/* Set function */
49513cad5b9Stnn 	if (sunxi_gpio_setfunc(sc, pin_def, "irq") != 0) {
49613cad5b9Stnn 		mutex_exit(&sc->sc_lock);
49713cad5b9Stnn 		return NULL;
49813cad5b9Stnn 	}
49913cad5b9Stnn 
50013cad5b9Stnn 	eint->eint_func = func;
50113cad5b9Stnn 	eint->eint_arg = arg;
50213cad5b9Stnn 	eint->eint_mpsafe = mpsafe;
50313cad5b9Stnn 	eint->eint_bank = pin_def->eint_bank;
50413cad5b9Stnn 	eint->eint_num = pin_def->eint_num;
50513cad5b9Stnn 
50613cad5b9Stnn 	/* Configure eint mode */
50713cad5b9Stnn 	val = GPIO_READ(sc, SUNXI_GPIO_INT_CFG(eint->eint_bank, eint->eint_num));
50813cad5b9Stnn 	val &= ~SUNXI_GPIO_INT_MODEMASK(eint->eint_num);
50913cad5b9Stnn 	val |= __SHIFTIN(mode, SUNXI_GPIO_INT_MODEMASK(eint->eint_num));
51013cad5b9Stnn 	GPIO_WRITE(sc, SUNXI_GPIO_INT_CFG(eint->eint_bank, eint->eint_num), val);
51113cad5b9Stnn 
51218f7b1b9Stnn 	val = SUNXI_GPIO_INT_DEBOUNCE_CLK_SEL;
51318f7b1b9Stnn 	GPIO_WRITE(sc, SUNXI_GPIO_INT_DEBOUNCE(eint->eint_bank), val);
51418f7b1b9Stnn 
51513cad5b9Stnn 	/* Enable eint */
51613cad5b9Stnn 	val = GPIO_READ(sc, SUNXI_GPIO_INT_CTL(eint->eint_bank));
51713cad5b9Stnn 	val |= __BIT(eint->eint_num);
51813cad5b9Stnn 	GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL(eint->eint_bank), val);
51913cad5b9Stnn 
52013cad5b9Stnn 	mutex_exit(&sc->sc_lock);
52113cad5b9Stnn 
52213cad5b9Stnn 	return eint;
52313cad5b9Stnn }
52413cad5b9Stnn 
52513cad5b9Stnn static void
52613cad5b9Stnn sunxi_intr_disable(struct sunxi_gpio_softc *sc, struct sunxi_gpio_eint *eint)
52713cad5b9Stnn {
52813cad5b9Stnn 	uint32_t val;
52913cad5b9Stnn 
53013cad5b9Stnn 	KASSERT(eint->eint_func != NULL);
53113cad5b9Stnn 
53213cad5b9Stnn 	mutex_enter(&sc->sc_lock);
53313cad5b9Stnn 
53413cad5b9Stnn 	/* Disable eint */
53513cad5b9Stnn 	val = GPIO_READ(sc, SUNXI_GPIO_INT_CTL(eint->eint_bank));
53613cad5b9Stnn 	val &= ~__BIT(eint->eint_num);
53713cad5b9Stnn 	GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL(eint->eint_bank), val);
53813cad5b9Stnn 	GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS(eint->eint_bank), __BIT(eint->eint_num));
53913cad5b9Stnn 
54013cad5b9Stnn 	eint->eint_func = NULL;
54113cad5b9Stnn 	eint->eint_arg = NULL;
54213cad5b9Stnn 	eint->eint_mpsafe = false;
54313cad5b9Stnn 
54413cad5b9Stnn 	mutex_exit(&sc->sc_lock);
54513cad5b9Stnn }
54613cad5b9Stnn 
54713cad5b9Stnn static void *
54813cad5b9Stnn sunxi_fdt_intr_establish(device_t dev, u_int *specifier, int ipl, int flags,
54959ad346dSjmcneill     int (*func)(void *), void *arg, const char *xname)
55054287382Sjmcneill {
55154287382Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
55213cad5b9Stnn 	bool mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
55354287382Sjmcneill 	const struct sunxi_gpio_pins *pin_def;
55454287382Sjmcneill 	u_int mode;
55554287382Sjmcneill 
55654287382Sjmcneill 	if (ipl != IPL_VM) {
55754287382Sjmcneill 		aprint_error_dev(dev, "%s: wrong IPL %d (expected %d)\n",
55854287382Sjmcneill 		    __func__, ipl, IPL_VM);
55954287382Sjmcneill 		return NULL;
56054287382Sjmcneill 	}
56154287382Sjmcneill 
56254287382Sjmcneill 	/* 1st cell is the bank */
56354287382Sjmcneill 	/* 2nd cell is the pin */
56454287382Sjmcneill 	/* 3rd cell is flags */
56554287382Sjmcneill 	const u_int port = be32toh(specifier[0]);
56654287382Sjmcneill 	const u_int pin = be32toh(specifier[1]);
56754287382Sjmcneill 	const u_int type = be32toh(specifier[2]) & 0xf;
56854287382Sjmcneill 
56954287382Sjmcneill 	switch (type) {
5709ee05496Sthorpej 	case FDT_INTR_TYPE_POS_EDGE:
57154287382Sjmcneill 		mode = SUNXI_GPIO_INT_MODE_POS_EDGE;
57254287382Sjmcneill 		break;
5739ee05496Sthorpej 	case FDT_INTR_TYPE_NEG_EDGE:
57454287382Sjmcneill 		mode = SUNXI_GPIO_INT_MODE_NEG_EDGE;
57554287382Sjmcneill 		break;
5769ee05496Sthorpej 	case FDT_INTR_TYPE_DOUBLE_EDGE:
57754287382Sjmcneill 		mode = SUNXI_GPIO_INT_MODE_DOUBLE_EDGE;
57854287382Sjmcneill 		break;
5799ee05496Sthorpej 	case FDT_INTR_TYPE_HIGH_LEVEL:
58054287382Sjmcneill 		mode = SUNXI_GPIO_INT_MODE_HIGH_LEVEL;
58154287382Sjmcneill 		break;
5829ee05496Sthorpej 	case FDT_INTR_TYPE_LOW_LEVEL:
58354287382Sjmcneill 		mode = SUNXI_GPIO_INT_MODE_LOW_LEVEL;
58454287382Sjmcneill 		break;
58554287382Sjmcneill 	default:
58654287382Sjmcneill 		aprint_error_dev(dev, "%s: unsupported irq type 0x%x\n",
58754287382Sjmcneill 		    __func__, type);
58854287382Sjmcneill 		return NULL;
58954287382Sjmcneill 	}
59054287382Sjmcneill 
59154287382Sjmcneill 	pin_def = sunxi_gpio_lookup(sc, port, pin);
59254287382Sjmcneill 	if (pin_def == NULL)
59354287382Sjmcneill 		return NULL;
59454287382Sjmcneill 
59513cad5b9Stnn 	return sunxi_intr_enable(sc, pin_def, mode, mpsafe, func, arg);
59654287382Sjmcneill }
59754287382Sjmcneill 
59854287382Sjmcneill static void
59913cad5b9Stnn sunxi_fdt_intr_disestablish(device_t dev, void *ih)
60054287382Sjmcneill {
60154287382Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
60254287382Sjmcneill 	struct sunxi_gpio_eint * const eint = ih;
60354287382Sjmcneill 
60413cad5b9Stnn 	sunxi_intr_disable(sc, eint);
60554287382Sjmcneill }
60654287382Sjmcneill 
60754287382Sjmcneill static bool
60813cad5b9Stnn sunxi_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
60954287382Sjmcneill {
61054287382Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
61154287382Sjmcneill 	const struct sunxi_gpio_pins *pin_def;
61254287382Sjmcneill 
61354287382Sjmcneill 	/* 1st cell is the bank */
61454287382Sjmcneill 	/* 2nd cell is the pin */
61554287382Sjmcneill 	/* 3rd cell is flags */
61654287382Sjmcneill 	if (!specifier)
61754287382Sjmcneill 		return false;
61854287382Sjmcneill 	const u_int port = be32toh(specifier[0]);
61954287382Sjmcneill 	const u_int pin = be32toh(specifier[1]);
62054287382Sjmcneill 
62154287382Sjmcneill 	pin_def = sunxi_gpio_lookup(sc, port, pin);
62254287382Sjmcneill 	if (pin_def == NULL)
62354287382Sjmcneill 		return false;
62454287382Sjmcneill 
62554287382Sjmcneill 	snprintf(buf, buflen, "GPIO %s", pin_def->name);
62654287382Sjmcneill 
62754287382Sjmcneill 	return true;
62854287382Sjmcneill }
62954287382Sjmcneill 
63054287382Sjmcneill static struct fdtbus_interrupt_controller_func sunxi_gpio_intrfuncs = {
63113cad5b9Stnn 	.establish = sunxi_fdt_intr_establish,
63213cad5b9Stnn 	.disestablish = sunxi_fdt_intr_disestablish,
63313cad5b9Stnn 	.intrstr = sunxi_fdt_intrstr,
63454287382Sjmcneill };
63554287382Sjmcneill 
63613cad5b9Stnn static void *
63713cad5b9Stnn sunxi_gpio_intr_establish(void *vsc, int pin, int ipl, int irqmode,
63813cad5b9Stnn     int (*func)(void *), void *arg)
63913cad5b9Stnn {
64013cad5b9Stnn 	struct sunxi_gpio_softc * const sc = vsc;
64113cad5b9Stnn 	bool mpsafe = (irqmode & GPIO_INTR_MPSAFE) != 0;
64213cad5b9Stnn 	int type = irqmode & GPIO_INTR_MODE_MASK;
64313cad5b9Stnn 	const struct sunxi_gpio_pins *pin_def;
64413cad5b9Stnn 	u_int mode;
64513cad5b9Stnn 
64613cad5b9Stnn 	switch (type) {
64713cad5b9Stnn 	case GPIO_INTR_POS_EDGE:
64813cad5b9Stnn 		mode = SUNXI_GPIO_INT_MODE_POS_EDGE;
64913cad5b9Stnn 		break;
65013cad5b9Stnn 	case GPIO_INTR_NEG_EDGE:
65113cad5b9Stnn 		mode = SUNXI_GPIO_INT_MODE_NEG_EDGE;
65213cad5b9Stnn 		break;
65313cad5b9Stnn 	case GPIO_INTR_DOUBLE_EDGE:
65413cad5b9Stnn 		mode = SUNXI_GPIO_INT_MODE_DOUBLE_EDGE;
65513cad5b9Stnn 		break;
65613cad5b9Stnn 	case GPIO_INTR_HIGH_LEVEL:
65713cad5b9Stnn 		mode = SUNXI_GPIO_INT_MODE_HIGH_LEVEL;
65813cad5b9Stnn 		break;
65913cad5b9Stnn 	case GPIO_INTR_LOW_LEVEL:
66013cad5b9Stnn 		mode = SUNXI_GPIO_INT_MODE_LOW_LEVEL;
66113cad5b9Stnn 		break;
66213cad5b9Stnn 	default:
66313cad5b9Stnn 		aprint_error_dev(sc->sc_dev, "%s: unsupported irq type 0x%x\n",
66413cad5b9Stnn 				 __func__, type);
66513cad5b9Stnn 		return NULL;
66613cad5b9Stnn 	}
66713cad5b9Stnn 
66813cad5b9Stnn 	if (pin < 0 || pin >= sc->sc_padconf->npins)
66913cad5b9Stnn 		return NULL;
67013cad5b9Stnn 	pin_def = &sc->sc_padconf->pins[pin];
67113cad5b9Stnn 
67213cad5b9Stnn 	return sunxi_intr_enable(sc, pin_def, mode, mpsafe, func, arg);
67313cad5b9Stnn }
67413cad5b9Stnn 
67513cad5b9Stnn static void
67613cad5b9Stnn sunxi_gpio_intr_disestablish(void *vsc, void *ih)
67713cad5b9Stnn {
67813cad5b9Stnn 	struct sunxi_gpio_softc * const sc = vsc;
67913cad5b9Stnn 	struct sunxi_gpio_eint * const eint = ih;
68013cad5b9Stnn 
68113cad5b9Stnn 	sunxi_intr_disable(sc, eint);
68213cad5b9Stnn }
68313cad5b9Stnn 
68413cad5b9Stnn static bool
68513cad5b9Stnn sunxi_gpio_intrstr(void *vsc, int pin, int irqmode, char *buf, size_t buflen)
68613cad5b9Stnn {
68713cad5b9Stnn 	struct sunxi_gpio_softc * const sc = vsc;
68813cad5b9Stnn 	const struct sunxi_gpio_pins *pin_def;
68913cad5b9Stnn 
69013cad5b9Stnn 	if (pin < 0 || pin >= sc->sc_padconf->npins)
69113cad5b9Stnn 		return NULL;
69213cad5b9Stnn 	pin_def = &sc->sc_padconf->pins[pin];
69313cad5b9Stnn 
69413cad5b9Stnn 	snprintf(buf, buflen, "GPIO %s", pin_def->name);
69513cad5b9Stnn 
69613cad5b9Stnn 	return true;
69713cad5b9Stnn }
69813cad5b9Stnn 
69909953da5Sjmcneill static const char *
70009953da5Sjmcneill sunxi_pinctrl_parse_function(int phandle)
70109953da5Sjmcneill {
70209953da5Sjmcneill 	const char *function;
70309953da5Sjmcneill 
704dfaf6e79Sthorpej 	function = fdtbus_pinctrl_parse_function(phandle);
70509953da5Sjmcneill 	if (function != NULL)
70609953da5Sjmcneill 		return function;
70709953da5Sjmcneill 
70809953da5Sjmcneill 	return fdtbus_get_string(phandle, "allwinner,function");
70909953da5Sjmcneill }
71009953da5Sjmcneill 
71109953da5Sjmcneill static const char *
71209953da5Sjmcneill sunxi_pinctrl_parse_pins(int phandle, int *pins_len)
71309953da5Sjmcneill {
714dfaf6e79Sthorpej 	const char *pins;
71509953da5Sjmcneill 	int len;
71609953da5Sjmcneill 
717dfaf6e79Sthorpej 	pins = fdtbus_pinctrl_parse_pins(phandle, pins_len);
718dfaf6e79Sthorpej 	if (pins != NULL)
719dfaf6e79Sthorpej 		return pins;
72009953da5Sjmcneill 
72109953da5Sjmcneill 	len = OF_getproplen(phandle, "allwinner,pins");
72209953da5Sjmcneill 	if (len > 0) {
72309953da5Sjmcneill 		*pins_len = len;
724e163685eSjmcneill 		return fdtbus_get_prop(phandle, "allwinner,pins", pins_len);
72509953da5Sjmcneill 	}
72609953da5Sjmcneill 
72709953da5Sjmcneill 	return NULL;
72809953da5Sjmcneill }
72909953da5Sjmcneill 
73009953da5Sjmcneill static int
73109953da5Sjmcneill sunxi_pinctrl_parse_bias(int phandle)
73209953da5Sjmcneill {
73309953da5Sjmcneill 	u_int pull;
734dfaf6e79Sthorpej 	int bias;
73509953da5Sjmcneill 
736dfaf6e79Sthorpej 	bias = fdtbus_pinctrl_parse_bias(phandle, NULL);
737dfaf6e79Sthorpej 	if (bias != -1)
738dfaf6e79Sthorpej 		return bias;
739dfaf6e79Sthorpej 
740dfaf6e79Sthorpej 	if (of_getprop_uint32(phandle, "allwinner,pull", &pull) == 0) {
74109953da5Sjmcneill 		switch (pull) {
74209953da5Sjmcneill 		case 0:
74309953da5Sjmcneill 			bias = 0;
74409953da5Sjmcneill 			break;
74509953da5Sjmcneill 		case 1:
74609953da5Sjmcneill 			bias = GPIO_PIN_PULLUP;
74709953da5Sjmcneill 			break;
74809953da5Sjmcneill 		case 2:
74909953da5Sjmcneill 			bias = GPIO_PIN_PULLDOWN;
75009953da5Sjmcneill 			break;
75109953da5Sjmcneill 		}
75209953da5Sjmcneill 	}
75309953da5Sjmcneill 
75409953da5Sjmcneill 	return bias;
75509953da5Sjmcneill }
75609953da5Sjmcneill 
75709953da5Sjmcneill static int
75809953da5Sjmcneill sunxi_pinctrl_parse_drive_strength(int phandle)
75909953da5Sjmcneill {
76009953da5Sjmcneill 	int val;
76109953da5Sjmcneill 
762dfaf6e79Sthorpej 	val = fdtbus_pinctrl_parse_drive_strength(phandle);
763dfaf6e79Sthorpej 	if (val != -1)
76409953da5Sjmcneill 		return val;
76509953da5Sjmcneill 
76609953da5Sjmcneill 	if (of_getprop_uint32(phandle, "allwinner,drive", &val) == 0)
76709953da5Sjmcneill 		return (val + 1) * 10;
76809953da5Sjmcneill 
76909953da5Sjmcneill 	return -1;
77009953da5Sjmcneill }
77109953da5Sjmcneill 
772e163685eSjmcneill static void
773e163685eSjmcneill sunxi_pinctrl_enable_regulator(struct sunxi_gpio_softc *sc,
774e163685eSjmcneill     const struct sunxi_gpio_pins *pin_def)
775e163685eSjmcneill {
776e163685eSjmcneill 	char supply_prop[16];
777e163685eSjmcneill 	uint32_t val;
778e163685eSjmcneill 	u_int uvol;
779e163685eSjmcneill 	int error;
780e163685eSjmcneill 
781e163685eSjmcneill 	const char c = tolower(pin_def->name[1]);
782e163685eSjmcneill 	if (c < 'a' || c > 'z')
783e163685eSjmcneill 		return;
784e163685eSjmcneill 	const int index = c - 'a';
785e163685eSjmcneill 
786e163685eSjmcneill 	if (sc->sc_pin_supply[index] != NULL) {
787e163685eSjmcneill 		/* Already enabled */
788e163685eSjmcneill 		return;
789e163685eSjmcneill 	}
790e163685eSjmcneill 
791e163685eSjmcneill 	snprintf(supply_prop, sizeof(supply_prop), "vcc-p%c-supply", c);
792e163685eSjmcneill 	sc->sc_pin_supply[index] = fdtbus_regulator_acquire(sc->sc_phandle, supply_prop);
793e163685eSjmcneill 	if (sc->sc_pin_supply[index] == NULL)
794e163685eSjmcneill 		return;
795e163685eSjmcneill 
796e163685eSjmcneill 	aprint_debug_dev(sc->sc_dev, "enable \"%s\"\n", supply_prop);
797e163685eSjmcneill 	error = fdtbus_regulator_enable(sc->sc_pin_supply[index]);
798e163685eSjmcneill 	if (error != 0)
799e163685eSjmcneill 		aprint_error_dev(sc->sc_dev, "failed to enable %s: %d\n", supply_prop, error);
800e163685eSjmcneill 
801e163685eSjmcneill 	if (sc->sc_padconf->has_io_bias_config) {
802e163685eSjmcneill 		error = fdtbus_regulator_get_voltage(sc->sc_pin_supply[index], &uvol);
803e163685eSjmcneill 		if (error != 0) {
804e163685eSjmcneill 			aprint_error_dev(sc->sc_dev, "failed to get %s voltage: %d\n",
805e163685eSjmcneill 			    supply_prop, error);
806e163685eSjmcneill 			uvol = 0;
807e163685eSjmcneill 		}
808e163685eSjmcneill 		if (uvol != 0) {
809e163685eSjmcneill 			if (uvol <= 1800000)
810e163685eSjmcneill 				val = 0x0;	/* 1.8V */
811e163685eSjmcneill 			else if (uvol <= 2500000)
812e163685eSjmcneill 				val = 0x6;	/* 2.5V */
813e163685eSjmcneill 			else if (uvol <= 2800000)
814e163685eSjmcneill 				val = 0x9;	/* 2.8V */
815e163685eSjmcneill 			else if (uvol <= 3000000)
816e163685eSjmcneill 				val = 0xa;	/* 3.0V */
817e163685eSjmcneill 			else
818e163685eSjmcneill 				val = 0xd;	/* 3.3V */
819e163685eSjmcneill 
820e163685eSjmcneill 			aprint_debug_dev(sc->sc_dev, "set io bias config for port %d to 0x%x\n",
821e163685eSjmcneill 			    pin_def->port, val);
822e163685eSjmcneill 			val = GPIO_READ(sc, SUNXI_GPIO_GRP_CONFIG(pin_def->port));
823e163685eSjmcneill 			val &= ~SUNXI_GPIO_GRP_IO_BIAS_CONFIGMASK;
824e163685eSjmcneill 			val |= __SHIFTIN(val, SUNXI_GPIO_GRP_IO_BIAS_CONFIGMASK);
825e163685eSjmcneill 			GPIO_WRITE(sc, SUNXI_GPIO_GRP_CONFIG(pin_def->port), val);
826e163685eSjmcneill 		}
827e163685eSjmcneill 	}
828e163685eSjmcneill }
829e163685eSjmcneill 
83049f361a5Sjmcneill static int
8313d7f7a9dSjmcneill sunxi_pinctrl_set_config(device_t dev, const void *data, size_t len)
8323d7f7a9dSjmcneill {
8333d7f7a9dSjmcneill 	struct sunxi_gpio_softc * const sc = device_private(dev);
8343d7f7a9dSjmcneill 	const struct sunxi_gpio_pins *pin_def;
83509953da5Sjmcneill 	int pins_len;
8363d7f7a9dSjmcneill 
8373d7f7a9dSjmcneill 	if (len != 4)
8383d7f7a9dSjmcneill 		return -1;
8393d7f7a9dSjmcneill 
8403d7f7a9dSjmcneill 	const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
8413d7f7a9dSjmcneill 
8423d7f7a9dSjmcneill 	/*
8433d7f7a9dSjmcneill 	 * Required: pins, function
84409953da5Sjmcneill 	 * Optional: bias, drive strength
8453d7f7a9dSjmcneill 	 */
8463d7f7a9dSjmcneill 
84709953da5Sjmcneill 	const char *function = sunxi_pinctrl_parse_function(phandle);
8483d7f7a9dSjmcneill 	if (function == NULL)
8493d7f7a9dSjmcneill 		return -1;
85009953da5Sjmcneill 	const char *pins = sunxi_pinctrl_parse_pins(phandle, &pins_len);
85109953da5Sjmcneill 	if (pins == NULL)
8523d7f7a9dSjmcneill 		return -1;
85309953da5Sjmcneill 
85409953da5Sjmcneill 	const int bias = sunxi_pinctrl_parse_bias(phandle);
85509953da5Sjmcneill 	const int drive_strength = sunxi_pinctrl_parse_drive_strength(phandle);
8563d7f7a9dSjmcneill 
857cddb63cbSjmcneill 	mutex_enter(&sc->sc_lock);
858cddb63cbSjmcneill 
85909953da5Sjmcneill 	for (; pins_len > 0;
8603d7f7a9dSjmcneill 	    pins_len -= strlen(pins) + 1, pins += strlen(pins) + 1) {
8613d7f7a9dSjmcneill 		pin_def = sunxi_gpio_lookup_byname(sc, pins);
8623d7f7a9dSjmcneill 		if (pin_def == NULL) {
8633d7f7a9dSjmcneill 			aprint_error_dev(dev, "unknown pin name '%s'\n", pins);
8643d7f7a9dSjmcneill 			continue;
8653d7f7a9dSjmcneill 		}
8663d7f7a9dSjmcneill 		if (sunxi_gpio_setfunc(sc, pin_def, function) != 0)
8673d7f7a9dSjmcneill 			continue;
8683d7f7a9dSjmcneill 
86909953da5Sjmcneill 		if (bias != -1)
87009953da5Sjmcneill 			sunxi_gpio_setpull(sc, pin_def, bias);
8713d7f7a9dSjmcneill 
87209953da5Sjmcneill 		if (drive_strength != -1)
8733d7f7a9dSjmcneill 			sunxi_gpio_setdrv(sc, pin_def, drive_strength);
874e163685eSjmcneill 
875e163685eSjmcneill 		sunxi_pinctrl_enable_regulator(sc, pin_def);
8763d7f7a9dSjmcneill 	}
8773d7f7a9dSjmcneill 
878cddb63cbSjmcneill 	mutex_exit(&sc->sc_lock);
879cddb63cbSjmcneill 
8803d7f7a9dSjmcneill 	return 0;
8813d7f7a9dSjmcneill }
8823d7f7a9dSjmcneill 
8833d7f7a9dSjmcneill static struct fdtbus_pinctrl_controller_func sunxi_pinctrl_funcs = {
8843d7f7a9dSjmcneill 	.set_config = sunxi_pinctrl_set_config,
8853d7f7a9dSjmcneill };
8863d7f7a9dSjmcneill 
8873d7f7a9dSjmcneill static int
888cddb63cbSjmcneill sunxi_gpio_pin_read(void *priv, int pin)
889cddb63cbSjmcneill {
890cddb63cbSjmcneill 	struct sunxi_gpio_softc * const sc = priv;
891cddb63cbSjmcneill 	const struct sunxi_gpio_pins *pin_def = &sc->sc_padconf->pins[pin];
892cddb63cbSjmcneill 	uint32_t data;
893cddb63cbSjmcneill 	int val;
894cddb63cbSjmcneill 
895cddb63cbSjmcneill 	KASSERT(pin < sc->sc_padconf->npins);
896cddb63cbSjmcneill 
897cddb63cbSjmcneill 	const bus_size_t data_reg = SUNXI_GPIO_DATA(pin_def->port);
898cddb63cbSjmcneill 	const uint32_t data_mask = __BIT(pin_def->pin);
899cddb63cbSjmcneill 
900cddb63cbSjmcneill 	/* No lock required for reads */
901cddb63cbSjmcneill 	data = GPIO_READ(sc, data_reg);
902cddb63cbSjmcneill 	val = __SHIFTOUT(data, data_mask);
903cddb63cbSjmcneill 
904cddb63cbSjmcneill 	return val;
905cddb63cbSjmcneill }
906cddb63cbSjmcneill 
907cddb63cbSjmcneill static void
908cddb63cbSjmcneill sunxi_gpio_pin_write(void *priv, int pin, int val)
909cddb63cbSjmcneill {
910cddb63cbSjmcneill 	struct sunxi_gpio_softc * const sc = priv;
911cddb63cbSjmcneill 	const struct sunxi_gpio_pins *pin_def = &sc->sc_padconf->pins[pin];
912cddb63cbSjmcneill 	uint32_t data;
913cddb63cbSjmcneill 
914cddb63cbSjmcneill 	KASSERT(pin < sc->sc_padconf->npins);
915cddb63cbSjmcneill 
916cddb63cbSjmcneill 	const bus_size_t data_reg = SUNXI_GPIO_DATA(pin_def->port);
917cddb63cbSjmcneill 	const uint32_t data_mask = __BIT(pin_def->pin);
918cddb63cbSjmcneill 
919cddb63cbSjmcneill 	mutex_enter(&sc->sc_lock);
920cddb63cbSjmcneill 	data = GPIO_READ(sc, data_reg);
921cddb63cbSjmcneill 	if (val)
922cddb63cbSjmcneill 		data |= data_mask;
923cddb63cbSjmcneill 	else
924cddb63cbSjmcneill 		data &= ~data_mask;
925cddb63cbSjmcneill 	GPIO_WRITE(sc, data_reg, data);
926cddb63cbSjmcneill 	mutex_exit(&sc->sc_lock);
927cddb63cbSjmcneill }
928cddb63cbSjmcneill 
929cddb63cbSjmcneill static void
930cddb63cbSjmcneill sunxi_gpio_pin_ctl(void *priv, int pin, int flags)
931cddb63cbSjmcneill {
932cddb63cbSjmcneill 	struct sunxi_gpio_softc * const sc = priv;
933cddb63cbSjmcneill 	const struct sunxi_gpio_pins *pin_def = &sc->sc_padconf->pins[pin];
934cddb63cbSjmcneill 
935cddb63cbSjmcneill 	KASSERT(pin < sc->sc_padconf->npins);
936cddb63cbSjmcneill 
937cddb63cbSjmcneill 	mutex_enter(&sc->sc_lock);
938cddb63cbSjmcneill 	sunxi_gpio_ctl(sc, pin_def, flags);
939cddb63cbSjmcneill 	sunxi_gpio_setpull(sc, pin_def, flags);
940cddb63cbSjmcneill 	mutex_exit(&sc->sc_lock);
941cddb63cbSjmcneill }
942cddb63cbSjmcneill 
943cddb63cbSjmcneill static void
944cddb63cbSjmcneill sunxi_gpio_attach_ports(struct sunxi_gpio_softc *sc)
945cddb63cbSjmcneill {
946cddb63cbSjmcneill 	const struct sunxi_gpio_pins *pin_def;
947cddb63cbSjmcneill 	struct gpio_chipset_tag *gp = &sc->sc_gp;
948cddb63cbSjmcneill 	struct gpiobus_attach_args gba;
949cddb63cbSjmcneill 	u_int pin;
950cddb63cbSjmcneill 
951cddb63cbSjmcneill 	gp->gp_cookie = sc;
952cddb63cbSjmcneill 	gp->gp_pin_read = sunxi_gpio_pin_read;
953cddb63cbSjmcneill 	gp->gp_pin_write = sunxi_gpio_pin_write;
954cddb63cbSjmcneill 	gp->gp_pin_ctl = sunxi_gpio_pin_ctl;
95513cad5b9Stnn 	gp->gp_intr_establish = sunxi_gpio_intr_establish;
95613cad5b9Stnn 	gp->gp_intr_disestablish = sunxi_gpio_intr_disestablish;
95713cad5b9Stnn 	gp->gp_intr_str = sunxi_gpio_intrstr;
958cddb63cbSjmcneill 
959cddb63cbSjmcneill 	const u_int npins = sc->sc_padconf->npins;
960cddb63cbSjmcneill 	sc->sc_pins = kmem_zalloc(sizeof(*sc->sc_pins) * npins, KM_SLEEP);
961cddb63cbSjmcneill 
962cddb63cbSjmcneill 	for (pin = 0; pin < sc->sc_padconf->npins; pin++) {
963cddb63cbSjmcneill 		pin_def = &sc->sc_padconf->pins[pin];
964cddb63cbSjmcneill 		sc->sc_pins[pin].pin_num = pin;
965cddb63cbSjmcneill 		sc->sc_pins[pin].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
966cddb63cbSjmcneill 		    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN;
96713cad5b9Stnn 		if (pin_def->functions[pin_def->eint_func] != NULL &&
96813cad5b9Stnn 		    strcmp(pin_def->functions[pin_def->eint_func], "irq") == 0) {
96913cad5b9Stnn 			sc->sc_pins[pin].pin_intrcaps =
97013cad5b9Stnn 			    GPIO_INTR_POS_EDGE | GPIO_INTR_NEG_EDGE |
97113cad5b9Stnn 			    GPIO_INTR_HIGH_LEVEL | GPIO_INTR_LOW_LEVEL |
97213cad5b9Stnn 			    GPIO_INTR_DOUBLE_EDGE | GPIO_INTR_MPSAFE;
97313cad5b9Stnn 		}
974cddb63cbSjmcneill 		sc->sc_pins[pin].pin_state = sunxi_gpio_pin_read(sc, pin);
975cddb63cbSjmcneill 		strlcpy(sc->sc_pins[pin].pin_defname, pin_def->name,
976cddb63cbSjmcneill 		    sizeof(sc->sc_pins[pin].pin_defname));
977cddb63cbSjmcneill 	}
978cddb63cbSjmcneill 
979cddb63cbSjmcneill 	memset(&gba, 0, sizeof(gba));
980cddb63cbSjmcneill 	gba.gba_gc = gp;
981cddb63cbSjmcneill 	gba.gba_pins = sc->sc_pins;
982cddb63cbSjmcneill 	gba.gba_npins = npins;
983c7fb772bSthorpej 	sc->sc_gpiodev = config_found(sc->sc_dev, &gba, NULL, CFARGS_NONE);
984cddb63cbSjmcneill }
985cddb63cbSjmcneill 
986cddb63cbSjmcneill static int
98749f361a5Sjmcneill sunxi_gpio_match(device_t parent, cfdata_t cf, void *aux)
98849f361a5Sjmcneill {
98949f361a5Sjmcneill 	struct fdt_attach_args * const faa = aux;
99049f361a5Sjmcneill 
9916e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
99249f361a5Sjmcneill }
99349f361a5Sjmcneill 
99449f361a5Sjmcneill static void
99549f361a5Sjmcneill sunxi_gpio_attach(device_t parent, device_t self, void *aux)
99649f361a5Sjmcneill {
99749f361a5Sjmcneill 	struct sunxi_gpio_softc * const sc = device_private(self);
99849f361a5Sjmcneill 	struct fdt_attach_args * const faa = aux;
99949f361a5Sjmcneill 	const int phandle = faa->faa_phandle;
100054287382Sjmcneill 	char intrstr[128];
10017283846bSjmcneill 	struct fdtbus_reset *rst;
10027283846bSjmcneill 	struct clk *clk;
100349f361a5Sjmcneill 	bus_addr_t addr;
100449f361a5Sjmcneill 	bus_size_t size;
10053d7f7a9dSjmcneill 	int child;
100649f361a5Sjmcneill 
100749f361a5Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
100849f361a5Sjmcneill 		aprint_error(": couldn't get registers\n");
100949f361a5Sjmcneill 		return;
101049f361a5Sjmcneill 	}
101149f361a5Sjmcneill 
10127283846bSjmcneill 	if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL)
10137283846bSjmcneill 		if (clk_enable(clk) != 0) {
10147283846bSjmcneill 			aprint_error(": couldn't enable clock\n");
10157283846bSjmcneill 			return;
10167283846bSjmcneill 		}
10177283846bSjmcneill 
10187283846bSjmcneill 	if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL)
10197283846bSjmcneill 		if (fdtbus_reset_deassert(rst) != 0) {
10207283846bSjmcneill 			aprint_error(": couldn't de-assert reset\n");
10217283846bSjmcneill 			return;
10227283846bSjmcneill 		}
10237283846bSjmcneill 
102449f361a5Sjmcneill 	sc->sc_dev = self;
1025e163685eSjmcneill 	sc->sc_phandle = phandle;
102649f361a5Sjmcneill 	sc->sc_bst = faa->faa_bst;
102749f361a5Sjmcneill 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
102849f361a5Sjmcneill 		aprint_error(": couldn't map registers\n");
102949f361a5Sjmcneill 		return;
103049f361a5Sjmcneill 	}
1031cddb63cbSjmcneill 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
10326e54367aSthorpej 	sc->sc_padconf = of_compatible_lookup(phandle, compat_data)->data;
103349f361a5Sjmcneill 
103449f361a5Sjmcneill 	aprint_naive("\n");
103549f361a5Sjmcneill 	aprint_normal(": PIO\n");
103649f361a5Sjmcneill 
103749f361a5Sjmcneill 	fdtbus_register_gpio_controller(self, phandle, &sunxi_gpio_funcs);
10383d7f7a9dSjmcneill 
10393d7f7a9dSjmcneill 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
1040e163685eSjmcneill 		bool is_valid =
1041e163685eSjmcneill 		    (of_hasprop(child, "function") && of_hasprop(child, "pins")) ||
1042e163685eSjmcneill 		    (of_hasprop(child, "allwinner,function") && of_hasprop(child, "allwinner,pins"));
1043e163685eSjmcneill 		if (!is_valid)
10443d7f7a9dSjmcneill 			continue;
10453d7f7a9dSjmcneill 		fdtbus_register_pinctrl_config(self, child, &sunxi_pinctrl_funcs);
10463d7f7a9dSjmcneill 	}
10473d7f7a9dSjmcneill 
1048cddb63cbSjmcneill 	sunxi_gpio_attach_ports(sc);
104954287382Sjmcneill 
105054287382Sjmcneill 	/* Disable all external interrupts */
1051d72f6453Sjmcneill 	for (int i = 0; i < sc->sc_padconf->npins; i++) {
1052d72f6453Sjmcneill 		const struct sunxi_gpio_pins *pin_def = &sc->sc_padconf->pins[i];
1053d72f6453Sjmcneill 		if (pin_def->eint_func == 0)
1054d72f6453Sjmcneill 			continue;
1055d72f6453Sjmcneill 		GPIO_WRITE(sc, SUNXI_GPIO_INT_CTL(pin_def->eint_bank), __BIT(pin_def->eint_num));
1056d72f6453Sjmcneill 		GPIO_WRITE(sc, SUNXI_GPIO_INT_STATUS(pin_def->eint_bank), __BIT(pin_def->eint_num));
1057d72f6453Sjmcneill 
1058d72f6453Sjmcneill 		if (sc->sc_eint_bank_max < pin_def->eint_bank)
1059d72f6453Sjmcneill 			sc->sc_eint_bank_max = pin_def->eint_bank;
1060d72f6453Sjmcneill 	}
1061d72f6453Sjmcneill 	KASSERT(sc->sc_eint_bank_max < SUNXI_GPIO_MAX_EINT_BANK);
106254287382Sjmcneill 
106354287382Sjmcneill 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
106454287382Sjmcneill 		aprint_error_dev(self, "failed to decode interrupt\n");
106554287382Sjmcneill 		return;
106654287382Sjmcneill 	}
1067076a1169Sjmcneill 	sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM,
1068076a1169Sjmcneill 	    FDT_INTR_MPSAFE, sunxi_gpio_intr, sc, device_xname(self));
106954287382Sjmcneill 	if (sc->sc_ih == NULL) {
107054287382Sjmcneill 		aprint_error_dev(self, "failed to establish interrupt on %s\n",
107154287382Sjmcneill 		    intrstr);
107254287382Sjmcneill 		return;
107354287382Sjmcneill 	}
107454287382Sjmcneill 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
107554287382Sjmcneill 	fdtbus_register_interrupt_controller(self, phandle,
107654287382Sjmcneill 	    &sunxi_gpio_intrfuncs);
107749f361a5Sjmcneill }
1078