Page MenuHomeFreeBSD

D11810.diff
No OneTemporary

D11810.diff

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__ */

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 16, 12:34 PM (4 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29770678
Default Alt Text
D11810.diff (13 KB)

Event Timeline