diff options
Diffstat (limited to 'sys/arm/allwinner/a10_gpio.c')
-rw-r--r-- | sys/arm/allwinner/a10_gpio.c | 255 |
1 files changed, 199 insertions, 56 deletions
diff --git a/sys/arm/allwinner/a10_gpio.c b/sys/arm/allwinner/a10_gpio.c index cda2c7b..898c18e 100644 --- a/sys/arm/allwinner/a10_gpio.c +++ b/sys/arm/allwinner/a10_gpio.c @@ -47,12 +47,15 @@ __FBSDID("$FreeBSD$"); #include <machine/intr.h> #include <dev/fdt/fdt_common.h> +#include <dev/fdt/fdt_pinctrl.h> #include <dev/gpio/gpiobusvar.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#include <arm/allwinner/allwinner_machdep.h> +#include <arm/allwinner/allwinner_pinctrl.h> + #include "gpio_if.h" -#include "a10_gpio.h" /* * A10 have 9 banks of gpio. @@ -62,7 +65,6 @@ __FBSDID("$FreeBSD$"); * PG0 - PG9 | PH0 - PH27 | PI0 - PI12 */ -#define A10_GPIO_PINS 288 #define A10_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) @@ -73,6 +75,9 @@ __FBSDID("$FreeBSD$"); #define A10_GPIO_INPUT 0 #define A10_GPIO_OUTPUT 1 +#define AW_GPIO_DRV_MASK 0x3 +#define AW_GPIO_PUD_MASK 0x3 + static struct ofw_compat_data compat_data[] = { {"allwinner,sun4i-a10-pinctrl", 1}, {"allwinner,sun7i-a20-pinctrl", 1}, @@ -88,8 +93,19 @@ struct a10_gpio_softc { bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; + const struct allwinner_padconf * padconf; }; +/* Defined in a10_padconf.c */ +#ifdef SOC_ALLWINNER_A10 +extern const struct allwinner_padconf a10_padconf; +#endif + +/* Defined in a20_padconf.c */ +#ifdef SOC_ALLWINNER_A20 +extern const struct allwinner_padconf a20_padconf; +#endif + #define A10_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define A10_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define A10_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) @@ -123,8 +139,10 @@ a10_gpio_get_function(struct a10_gpio_softc *sc, uint32_t pin) /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); - bank = pin / 32; - pin = pin % 32; + if (pin > sc->padconf->npins) + return (0); + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x07) << 2); func = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, pin >> 3)); @@ -146,8 +164,8 @@ a10_gpio_set_function(struct a10_gpio_softc *sc, uint32_t pin, uint32_t f) /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); - bank = pin / 32; - pin = pin % 32; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x07) << 2); data = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, pin >> 3)); @@ -164,8 +182,8 @@ a10_gpio_get_pud(struct a10_gpio_softc *sc, uint32_t pin) /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); - bank = pin / 32; - pin = pin % 32; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); @@ -187,17 +205,35 @@ a10_gpio_set_pud(struct a10_gpio_softc *sc, uint32_t pin, uint32_t state) /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); - bank = pin / 32; - pin = pin % 32; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pin >> 4)); - val &= ~(0x03 << offset); + val &= ~(AW_GPIO_PUD_MASK << offset); val |= (state << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_PUL(bank, pin >> 4), val); } static void +a10_gpio_set_drv(struct a10_gpio_softc *sc, uint32_t pin, uint32_t drive) +{ + uint32_t bank, offset, val; + + /* Must be called with lock held. */ + A10_GPIO_LOCK_ASSERT(sc); + + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; + offset = ((pin & 0x0f) << 1); + + val = A10_GPIO_READ(sc, A10_GPIO_GP_DRV(bank, pin >> 4)); + val &= ~(AW_GPIO_DRV_MASK << offset); + val |= (drive << offset); + A10_GPIO_WRITE(sc, A10_GPIO_GP_DRV(bank, pin >> 4), val); +} + +static void a10_gpio_pin_configure(struct a10_gpio_softc *sc, uint32_t pin, uint32_t flags) { @@ -218,7 +254,7 @@ a10_gpio_pin_configure(struct a10_gpio_softc *sc, uint32_t pin, uint32_t flags) a10_gpio_set_pud(sc, pin, A10_GPIO_PULLUP); else a10_gpio_set_pud(sc, pin, A10_GPIO_PULLDOWN); - } else + } else a10_gpio_set_pud(sc, pin, A10_GPIO_NONE); } @@ -235,16 +271,21 @@ a10_gpio_get_bus(device_t dev) static int a10_gpio_pin_max(device_t dev, int *maxpin) { + struct a10_gpio_softc *sc; + + sc = device_get_softc(dev); - *maxpin = A10_GPIO_PINS - 1; + *maxpin = sc->padconf->npins - 1; return (0); } static int a10_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { + struct a10_gpio_softc *sc; - if (pin >= A10_GPIO_PINS) + sc = device_get_softc(dev); + if (pin >= sc->padconf->npins) return (EINVAL); *caps = A10_GPIO_DEFAULT_CAPS; @@ -257,10 +298,10 @@ a10_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct a10_gpio_softc *sc; - if (pin >= A10_GPIO_PINS) + sc = device_get_softc(dev); + if (pin >= sc->padconf->npins) return (EINVAL); - sc = device_get_softc(dev); A10_GPIO_LOCK(sc); *flags = a10_gpio_get_function(sc, pin); *flags |= a10_gpio_get_pud(sc, pin); @@ -272,14 +313,14 @@ a10_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) static int a10_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { - uint32_t bank; + struct a10_gpio_softc *sc; - if (pin >= A10_GPIO_PINS) + sc = device_get_softc(dev); + if (pin >= sc->padconf->npins) return (EINVAL); - bank = pin / 32; - snprintf(name, GPIOMAXNAME - 1, "pin %d (P%c%d)", - pin, bank + 'A', pin % 32); + snprintf(name, GPIOMAXNAME - 1, "%s", + sc->padconf->pins[pin].name); name[GPIOMAXNAME - 1] = '\0'; return (0); @@ -290,10 +331,10 @@ a10_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct a10_gpio_softc *sc; - if (pin >= A10_GPIO_PINS) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - sc = device_get_softc(dev); A10_GPIO_LOCK(sc); a10_gpio_pin_configure(sc, pin, flags); A10_GPIO_UNLOCK(sc); @@ -307,13 +348,13 @@ a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) struct a10_gpio_softc *sc; uint32_t bank, data; - if (pin >= A10_GPIO_PINS) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - bank = pin / 32; - pin = pin % 32; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; - sc = device_get_softc(dev); A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (value) @@ -332,13 +373,13 @@ a10_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) struct a10_gpio_softc *sc; uint32_t bank, reg_data; - if (pin >= A10_GPIO_PINS) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - bank = pin / 32; - pin = pin % 32; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; - sc = device_get_softc(dev); A10_GPIO_LOCK(sc); reg_data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); A10_GPIO_UNLOCK(sc); @@ -353,13 +394,13 @@ a10_gpio_pin_toggle(device_t dev, uint32_t pin) struct a10_gpio_softc *sc; uint32_t bank, data; - if (pin >= A10_GPIO_PINS) + sc = device_get_softc(dev); + if (pin > sc->padconf->npins) return (EINVAL); - bank = pin / 32; - pin = pin % 32; + bank = sc->padconf->pins[pin].port; + pin = sc->padconf->pins[pin].pin; - sc = device_get_softc(dev); A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (data & (1 << pin)) @@ -373,6 +414,92 @@ a10_gpio_pin_toggle(device_t dev, uint32_t pin) } static int +aw_find_pinnum_by_name(struct a10_gpio_softc *sc, const char *pinname) +{ + int i; + + for (i = 0; i < sc->padconf->npins; i++) + if (!strcmp(pinname, sc->padconf->pins[i].name)) + return i; + + return (-1); +} + +static int +aw_find_pin_func(struct a10_gpio_softc *sc, int pin, const char *func) +{ + int i; + + for (i = 0; i < AW_MAX_FUNC_BY_PIN; i++) + if (sc->padconf->pins[pin].functions[i] && + !strcmp(func, sc->padconf->pins[pin].functions[i])) + return (i); + + return (-1); +} + +static int +aw_fdt_configure_pins(device_t dev, phandle_t cfgxref) +{ + struct a10_gpio_softc *sc; + phandle_t node; + const char **pinlist = NULL; + char *pin_function = NULL; + uint32_t pin_drive, pin_pull; + int pins_nb, pin_num, pin_func, i, ret; + + sc = device_get_softc(dev); + node = OF_node_from_xref(cfgxref); + ret = 0; + + /* Getting all prop for configuring pins */ + pins_nb = ofw_bus_string_list_to_array(node, "allwinner,pins", &pinlist); + if (pins_nb <= 0) + return (ENOENT); + if (OF_getprop_alloc(node, "allwinner,function", + sizeof(*pin_function), + (void **)&pin_function) == -1) { + ret = ENOENT; + goto out; + } + if (OF_getencprop(node, "allwinner,drive", + &pin_drive, sizeof(pin_drive)) == -1) { + ret = ENOENT; + goto out; + } + if (OF_getencprop(node, "allwinner,pull", + &pin_pull, sizeof(pin_pull)) == -1) { + ret = ENOENT; + goto out; + } + + /* Configure each pin to the correct function, drive and pull */ + for (i = 0; i < pins_nb; i++) { + pin_num = aw_find_pinnum_by_name(sc, pinlist[i]); + if (pin_num == -1) { + ret = ENOENT; + goto out; + } + pin_func = aw_find_pin_func(sc, pin_num, pin_function); + if (pin_func == -1) { + ret = ENOENT; + goto out; + } + + A10_GPIO_LOCK(sc); + a10_gpio_set_function(sc, pin_num, pin_func); + a10_gpio_set_drv(sc, pin_num, pin_drive); + a10_gpio_set_pud(sc, pin_num, pin_pull); + A10_GPIO_UNLOCK(sc); + } + + out: + free(pinlist, M_OFWPROP); + free(pin_function, M_OFWPROP); + return (ret); +} + +static int a10_gpio_probe(device_t dev) { @@ -382,7 +509,7 @@ a10_gpio_probe(device_t dev) if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); - device_set_desc(dev, "Allwinner GPIO controller"); + device_set_desc(dev, "Allwinner GPIO/Pinmux controller"); return (BUS_PROBE_DEFAULT); } @@ -428,6 +555,29 @@ a10_gpio_attach(device_t dev) if (sc->sc_busdev == NULL) goto fail; + + /* Use the right pin data for the current SoC */ + switch (allwinner_soc_type()) { +#ifdef SOC_ALLWINNER_A10 + case ALLWINNERSOC_A10: + sc->padconf = &a10_padconf; + break; +#endif +#ifdef SOC_ALLWINNER_A20 + case ALLWINNERSOC_A20: + sc->padconf = &a20_padconf; + break; +#endif + default: + return (ENOENT); + } + + /* + * Register as a pinctrl device + */ + fdt_pinctrl_register(dev, "allwinner,pins"); + fdt_pinctrl_configure_tree(dev); + return (0); fail: @@ -459,9 +609,18 @@ static int a10_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) { + struct a10_gpio_softc *sc; + int i; + + sc = device_get_softc(bus); /* The GPIO pins are mapped as: <gpio-phandle bank pin flags>. */ - *pin = gpios[0] * 32 + gpios[1]; + for (i = 0; i < sc->padconf->npins; i++) + if (sc->padconf->pins[i].port == gpios[0] && + sc->padconf->pins[i].pin == gpios[1]) { + *pin = i; + break; + } *flags = gpios[gcells - 1]; return (0); @@ -488,6 +647,9 @@ static device_method_t a10_gpio_methods[] = { /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, a10_gpio_get_node), + /* fdt_pinctrl interface */ + DEVMETHOD(fdt_pinctrl_configure,aw_fdt_configure_pins), + DEVMETHOD_END }; @@ -501,22 +663,3 @@ static driver_t a10_gpio_driver = { EARLY_DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); - - -int -a10_gpio_ethernet_activate(uint32_t func) -{ - int i; - struct a10_gpio_softc *sc = a10_gpio_sc; - - if (sc == NULL) - return (ENXIO); - - /* Configure pin mux settings for MII. */ - A10_GPIO_LOCK(sc); - for (i = 0; i <= 17; i++) - a10_gpio_set_function(sc, i, func); - A10_GPIO_UNLOCK(sc); - - return (0); -} |