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