Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F148064050
D11810.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D11810.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D11810: Add gpio methods to read/write/configure the state of up to 32 pins simultaneously
Attached
Detach File
Event Timeline
Log In to Comment