Index: head/sys/arm/allwinner/a10_gpio.c =================================================================== --- head/sys/arm/allwinner/a10_gpio.c +++ head/sys/arm/allwinner/a10_gpio.c @@ -195,6 +195,9 @@ #define A10_GPIO_GP_INT_STA 0x214 #define A10_GPIO_GP_INT_DEB 0x218 +static int a10_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value); +static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); + #define A10_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define A10_GPIO_READ(_sc, _off) \ @@ -316,29 +319,44 @@ static int a10_gpio_pin_configure(struct a10_gpio_softc *sc, uint32_t pin, uint32_t flags) { + u_int val; int err = 0; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); + if (pin > sc->padconf->npins) + return (EINVAL); + /* Manage input/output. */ - if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { - if (flags & GPIO_PIN_OUTPUT) - err = a10_gpio_set_function(sc, pin, A10_GPIO_OUTPUT); - else + if (flags & GPIO_PIN_INPUT) { + err = a10_gpio_set_function(sc, pin, A10_GPIO_INPUT); + } else if (flags & GPIO_PIN_OUTPUT) { + if (flags & GPIO_PIN_PRESET_LOW) { + a10_gpio_pin_set(sc->sc_dev, pin, 0); + } else if (flags & GPIO_PIN_PRESET_HIGH) { + a10_gpio_pin_set(sc->sc_dev, pin, 1); + } else { + /* Read the pin and preset output to current state. */ err = a10_gpio_set_function(sc, pin, A10_GPIO_INPUT); + if (err == 0) { + a10_gpio_pin_get(sc->sc_dev, pin, &val); + a10_gpio_pin_set(sc->sc_dev, pin, val); + } + } + if (err == 0) + err = a10_gpio_set_function(sc, pin, A10_GPIO_OUTPUT); } if (err) return (err); /* Manage Pull-up/pull-down. */ - if (flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)) { - if (flags & GPIO_PIN_PULLUP) - a10_gpio_set_pud(sc, pin, A10_GPIO_PULLUP); - else - a10_gpio_set_pud(sc, pin, A10_GPIO_PULLDOWN); - } else + if (flags & GPIO_PIN_PULLUP) + a10_gpio_set_pud(sc, pin, A10_GPIO_PULLUP); + else if (flags & GPIO_PIN_PULLDOWN) + a10_gpio_set_pud(sc, pin, A10_GPIO_PULLDOWN); + else a10_gpio_set_pud(sc, pin, A10_GPIO_NONE); return (0); @@ -526,6 +544,73 @@ } static int +a10_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, + uint32_t change_pins, uint32_t *orig_pins) +{ + struct a10_gpio_softc *sc; + uint32_t bank, data, pin; + + sc = device_get_softc(dev); + if (first_pin > sc->padconf->npins) + return (EINVAL); + + /* + * We require that first_pin refers to the first pin in a bank, because + * this API is not about convenience, it's for making a set of pins + * change simultaneously (required) with reasonably high performance + * (desired); we need to do a read-modify-write on a single register. + */ + bank = sc->padconf->pins[first_pin].port; + pin = sc->padconf->pins[first_pin].pin; + if (pin != 0) + return (EINVAL); + + A10_GPIO_LOCK(sc); + data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); + if ((clear_pins | change_pins) != 0) + A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), + (data & ~clear_pins) ^ change_pins); + A10_GPIO_UNLOCK(sc); + + if (orig_pins != NULL) + *orig_pins = data; + + return (0); +} + +static int +a10_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, + uint32_t *pin_flags) +{ + struct a10_gpio_softc *sc; + uint32_t bank, pin; + int err; + + sc = device_get_softc(dev); + if (first_pin > sc->padconf->npins) + return (EINVAL); + + bank = sc->padconf->pins[first_pin].port; + if (sc->padconf->pins[first_pin].pin != 0) + return (EINVAL); + + /* + * The configuration for a bank of pins is scattered among several + * registers; we cannot g'tee to simultaneously change the state of all + * the pins in the flags array. So just loop through the array + * configuring each pin for now. If there was a strong need, it might + * be possible to support some limited simultaneous config, such as + * adjacent groups of 8 pins that line up the same as the config regs. + */ + for (err = 0, pin = first_pin; err == 0 && pin < num_pins; ++pin) { + if (pin_flags[pin] & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) + err = a10_gpio_pin_configure(sc, pin, pin_flags[pin]); + } + + return (err); +} + +static int aw_find_pinnum_by_name(struct a10_gpio_softc *sc, const char *pinname) { int i; @@ -780,6 +865,8 @@ DEVMETHOD(gpio_pin_get, a10_gpio_pin_get), DEVMETHOD(gpio_pin_set, a10_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, a10_gpio_pin_toggle), + DEVMETHOD(gpio_pin_access_32, a10_gpio_pin_access_32), + DEVMETHOD(gpio_pin_config_32, a10_gpio_pin_config_32), DEVMETHOD(gpio_map_gpios, a10_gpio_map_gpios), /* ofw_bus interface */ Index: head/sys/arm/freescale/imx/imx_gpio.c =================================================================== --- head/sys/arm/freescale/imx/imx_gpio.c +++ head/sys/arm/freescale/imx/imx_gpio.c @@ -668,6 +668,72 @@ } static int +imx51_gpio_pin_access_32(device_t dev, uint32_t first_pin, uint32_t clear_pins, + uint32_t change_pins, uint32_t *orig_pins) +{ + struct imx51_gpio_softc *sc; + + if (first_pin != 0) + return (EINVAL); + + sc = device_get_softc(dev); + + if (orig_pins != NULL) + *orig_pins = READ4(sc, IMX_GPIO_PSR_REG); + + if ((clear_pins | change_pins) != 0) { + mtx_lock_spin(&sc->sc_mtx); + WRITE4(sc, IMX_GPIO_DR_REG, + (READ4(sc, IMX_GPIO_DR_REG) & ~clear_pins) ^ change_pins); + mtx_unlock_spin(&sc->sc_mtx); + } + + return (0); +} + +static int +imx51_gpio_pin_config_32(device_t dev, uint32_t first_pin, uint32_t num_pins, + uint32_t *pin_flags) +{ + struct imx51_gpio_softc *sc; + u_int i; + uint32_t bit, drclr, drset, flags, oeclr, oeset, pads; + + sc = device_get_softc(dev); + + if (first_pin != 0 || num_pins > sc->gpio_npins) + return (EINVAL); + + drclr = drset = oeclr = oeset = 0; + pads = READ4(sc, IMX_GPIO_PSR_REG); + + for (i = 0; i < num_pins; ++i) { + bit = 1u << i; + flags = pin_flags[i]; + if (flags & GPIO_PIN_INPUT) { + oeclr |= bit; + } else if (flags & GPIO_PIN_OUTPUT) { + oeset |= bit; + if (flags & GPIO_PIN_PRESET_LOW) + drclr |= bit; + else if (flags & GPIO_PIN_PRESET_HIGH) + drset |= bit; + else /* Drive whatever it's now pulled to. */ + drset |= pads & bit; + } + } + + mtx_lock_spin(&sc->sc_mtx); + WRITE4(sc, IMX_GPIO_DR_REG, + (READ4(sc, IMX_GPIO_DR_REG) & ~drclr) | drset); + WRITE4(sc, IMX_GPIO_OE_REG, + (READ4(sc, IMX_GPIO_OE_REG) & ~oeclr) | oeset); + mtx_unlock_spin(&sc->sc_mtx); + + return (0); +} + +static int imx51_gpio_probe(device_t dev) { @@ -790,6 +856,8 @@ DEVMETHOD(gpio_pin_get, imx51_gpio_pin_get), DEVMETHOD(gpio_pin_set, imx51_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, imx51_gpio_pin_toggle), + DEVMETHOD(gpio_pin_access_32, imx51_gpio_pin_access_32), + DEVMETHOD(gpio_pin_config_32, imx51_gpio_pin_config_32), {0, 0}, }; Index: head/sys/dev/gpio/gpio_if.m =================================================================== --- head/sys/dev/gpio/gpio_if.m +++ head/sys/dev/gpio/gpio_if.m @@ -40,6 +40,13 @@ } static int + gpio_default_nosupport(void) + { + + return (EOPNOTSUPP); + } + + static int gpio_default_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) @@ -151,3 +158,31 @@ uint32_t *pin; uint32_t *flags; } DEFAULT gpio_default_map_gpios; + +# +# Simultaneously read and/or change up to 32 adjacent pins. +# If the device cannot change the pins simultaneously, returns EOPNOTSUPP. +# +# More details about using this interface can be found in sys/gpio.h +# +METHOD int pin_access_32 { + device_t dev; + uint32_t first_pin; + uint32_t clear_pins; + uint32_t change_pins; + uint32_t *orig_pins; +} DEFAULT gpio_default_nosupport; + +# +# Simultaneously configure up to 32 adjacent pins. +# This is intended to change the configuration of all the pins simultaneously, +# but unlike pin_access_32, this will not fail if the hardware can't do so. +# +# More details about using this interface can be found in sys/gpio.h +# +METHOD int pin_config_32 { + device_t dev; + uint32_t first_pin; + uint32_t num_pins; + uint32_t *pin_flags; +} DEFAULT gpio_default_nosupport; Index: head/sys/dev/gpio/gpioc.c =================================================================== --- head/sys/dev/gpio/gpioc.c +++ head/sys/dev/gpio/gpioc.c @@ -125,6 +125,8 @@ struct gpioc_softc *sc = cdev->si_drv1; struct gpio_pin pin; struct gpio_req req; + struct gpio_access_32 *a32; + struct gpio_config_32 *c32; uint32_t caps; bus = GPIO_GET_BUS(sc->sc_pdev); @@ -184,6 +186,16 @@ dprintf("set name on pin %d\n", pin.gp_pin); res = GPIOBUS_PIN_SETNAME(bus, pin.gp_pin, pin.gp_name); + break; + case GPIOACCESS32: + a32 = (struct gpio_access_32 *)arg; + res = GPIO_PIN_ACCESS_32(sc->sc_pdev, a32->first_pin, + a32->clear_pins, a32->orig_pins, &a32->orig_pins); + break; + case GPIOCONFIG32: + c32 = (struct gpio_config_32 *)arg; + res = GPIO_PIN_CONFIG_32(sc->sc_pdev, c32->first_pin, + c32->num_pins, c32->pin_flags); break; default: return (ENOTTY); Index: head/sys/sys/gpio.h =================================================================== --- head/sys/sys/gpio.h +++ head/sys/sys/gpio.h @@ -70,6 +70,8 @@ #define GPIO_PIN_INVIN 0x00000080 /* invert input */ #define GPIO_PIN_INVOUT 0x00000100 /* invert output */ #define GPIO_PIN_PULSATE 0x00000200 /* pulsate in hardware */ +#define GPIO_PIN_PRESET_LOW 0x00000400 /* preset pin to high or */ +#define GPIO_PIN_PRESET_HIGH 0x00000800 /* low before enabling output */ /* GPIO interrupt capabilities */ #define GPIO_INTR_NONE 0x00000000 /* no interrupt support */ #define GPIO_INTR_LEVEL_LOW 0x00010000 /* level trigger, low */ @@ -95,6 +97,71 @@ }; /* + * gpio_access_32 / GPIOACCESS32 + * + * Simultaneously read and/or change up to 32 adjacent pins. + * If the device cannot change the pins simultaneously, returns EOPNOTSUPP. + * + * This accesses an adjacent set of up to 32 pins starting at first_pin within + * the device's collection of pins. How the hardware pins are mapped to the 32 + * bits in the arguments is device-specific. It is expected that lower-numbered + * pins in the device's number space map linearly to lower-ordered bits within + * the 32-bit words (i.e., bit 0 is first_pin, bit 1 is first_pin+1, etc). + * Other mappings are possible; know your device. + * + * Some devices may limit the value of first_pin to 0, or to multiples of 16 or + * 32 or some other hardware-specific number; to access pin 2 would require + * first_pin to be zero and then manipulate bit (1 << 2) in the 32-bit word. + * Invalid values in first_pin result in an EINVAL error return. + * + * The starting state of the pins is captured and stored in orig_pins, then the + * pins are set to ((starting_state & ~clear_pins) ^ change_pins). + * + * Clear Change Hardware pin after call + * 0 0 No change + * 0 1 Opposite of current value + * 1 0 Cleared + * 1 1 Set + */ +struct gpio_access_32 { + uint32_t first_pin; /* First pin in group of 32 adjacent */ + uint32_t clear_pins; /* Pins are changed using: */ + uint32_t change_pins; /* ((hwstate & ~clear_pins) ^ change_pins) */ + uint32_t orig_pins; /* Returned hwstate of pins before change. */ +}; + +/* + * gpio_config_32 / GPIOCONFIG32 + * + * Simultaneously configure up to 32 adjacent pins. This is intended to change + * the configuration of all the pins simultaneously, such that pins configured + * for output all begin to drive the configured values simultaneously, but not + * all hardware can do that, so the driver "does the best it can" in this + * regard. Notably unlike pin_access_32(), this does NOT fail if the pins + * cannot be atomically configured; it is expected that callers understand the + * hardware and have decided to live with any such limitations it may have. + * + * The pin_flags argument is an array of GPIO_PIN_xxxx flags. If the array + * contains any GPIO_PIN_OUTPUT flags, the driver will manipulate the hardware + * such that all output pins become driven with the proper initial values + * simultaneously if it can. The elements in the array map to pins in the same + * way that bits are mapped by pin_acces_32(), and the same restrictions may + * apply. For example, to configure pins 2 and 3 it may be necessary to set + * first_pin to zero and only populate pin_flags[2] and pin_flags[3]. If a + * given array entry doesn't contain GPIO_PIN_INPUT or GPIO_PIN_OUTPUT then no + * configuration is done for that pin. + * + * Some devices may limit the value of first_pin to 0, or to multiples of 16 or + * 32 or some other hardware-specific number. Invalid values in first_pin or + * num_pins result in an error return with errno set to EINVAL. + */ +struct gpio_config_32 { + uint32_t first_pin; + uint32_t num_pins; + uint32_t pin_flags[32]; +}; + +/* * ioctls */ #define GPIOMAXPIN _IOR('G', 0, int) @@ -104,5 +171,7 @@ #define GPIOSET _IOW('G', 4, struct gpio_req) #define GPIOTOGGLE _IOWR('G', 5, struct gpio_req) #define GPIOSETNAME _IOW('G', 6, struct gpio_pin) +#define GPIOACCESS32 _IOWR('G', 7, struct gpio_access_32) +#define GPIOCONFIG32 _IOW('G', 8, struct gpio_config_32) #endif /* __GPIO_H__ */