aboutsummaryrefslogtreecommitdiffstats
path: root/packages/linux/linux-2.6.18/gpio-dev-robustness.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/linux-2.6.18/gpio-dev-robustness.patch')
-rw-r--r--packages/linux/linux-2.6.18/gpio-dev-robustness.patch204
1 files changed, 204 insertions, 0 deletions
diff --git a/packages/linux/linux-2.6.18/gpio-dev-robustness.patch b/packages/linux/linux-2.6.18/gpio-dev-robustness.patch
new file mode 100644
index 0000000000..56ef0ebc6c
--- /dev/null
+++ b/packages/linux/linux-2.6.18/gpio-dev-robustness.patch
@@ -0,0 +1,204 @@
+Make the GPIO /dev interface a bit more robust
+
+Instead of allocating gpio resources on-the-fly as the files are
+written, defer it until enable is set to 1 and disallow updates to
+any of the other files while enable=1.
+
+Otherwise, the number of checks in each _store function will rapidly
+approach insanity.
+---
+ arch/avr32/mach-at32ap/pio.c | 99 ++++++++++++++++++-------------------------
+ 1 file changed, 43 insertions(+), 56 deletions(-)
+
+Index: linux-2.6.18-avr32/arch/avr32/mach-at32ap/pio.c
+===================================================================
+--- linux-2.6.18-avr32.orig/arch/avr32/mach-at32ap/pio.c 2006-11-29 17:10:54.000000000 +0100
++++ linux-2.6.18-avr32/arch/avr32/mach-at32ap/pio.c 2006-11-29 18:11:38.000000000 +0100
+@@ -124,13 +124,6 @@ static unsigned int pio_id(struct pio_de
+ return pio - pio_dev;
+ }
+
+-static void __enable_gpio(struct pio_device *pio, u32 mask)
+-{
+- pio_writel(pio, PUER, mask);
+- pio_writel(pio, ODR, mask);
+- pio_writel(pio, PER, mask);
+-}
+-
+ static void __disable_gpio(struct pio_device *pio, u32 mask)
+ {
+ pio_writel(pio, PUER, mask);
+@@ -251,11 +244,12 @@ static dev_t gpio_devt;
+ struct gpio_item {
+ spinlock_t lock;
+
+- /* Too bad we don't have committable items... */
+- int enabled;
+-
+ struct pio_device *pio;
++
++ int enabled;
++ int pio_id;
+ u32 pin_mask;
++ u32 oe_mask;
+
+ int id;
+ struct class_device *gpio_dev;
+@@ -339,10 +333,7 @@ static struct gpio_item *to_gpio_item(st
+
+ static ssize_t gpio_show_gpio_id(struct gpio_item *gpio, char *page)
+ {
+- if (gpio->pio)
+- return sprintf(page, "%u\n", pio_id(gpio->pio));
+- else
+- return sprintf(page, "-1\n");
++ return sprintf(page, "%d\n", gpio->pio_id);
+ }
+
+ static ssize_t gpio_store_gpio_id(struct gpio_item *gpio,
+@@ -361,7 +352,7 @@ static ssize_t gpio_store_gpio_id(struct
+ if (!gpio->enabled) {
+ ret = -ENXIO;
+ if ((id < MAX_NR_PIO_DEVICES) && pio_dev[id].regs) {
+- gpio->pio = &pio_dev[id];
++ gpio->pio_id = id;
+ ret = count;
+ }
+ }
+@@ -378,9 +369,7 @@ static ssize_t gpio_show_pin_mask(struct
+ static ssize_t gpio_store_pin_mask(struct gpio_item *gpio,
+ const char *page, size_t count)
+ {
+- struct pio_device *pio;
+- u32 old_mask, new_mask;
+- u32 old, new;
++ u32 new_mask;
+ char *p = (char *)page;
+ ssize_t ret = -EINVAL;
+
+@@ -388,47 +377,21 @@ static ssize_t gpio_store_pin_mask(struc
+ if (!p || (*p && (*p != '\n')))
+ return -EINVAL;
+
+- /*
+- * Must have a PIO before we can start allocating pins, but we
+- * must not be live.
+- */
++ /* Can't update the pin mask while live. */
+ spin_lock(&gpio->lock);
+- pio = gpio->pio;
+- if (!pio || gpio->enabled)
+- goto out;
+-
+- ret = -EBUSY;
+- old_mask = gpio->pin_mask;
+- do {
+- old = pio->pinmux_mask;
+- if ((old & ~old_mask) & new_mask)
+- goto out;
+-
+- new = (old & ~old_mask) | new_mask;
+- } while (cmpxchg(&pio->pinmux_mask, old, new) != old);
+-
+- gpio->pin_mask = new_mask;
+- __disable_gpio(pio, old_mask);
+- __enable_gpio(pio, new_mask);
+- ret = count;
+-
+-out:
++ if (!gpio->enabled) {
++ gpio->oe_mask &= new_mask;
++ gpio->pin_mask = new_mask;
++ ret = count;
++ }
+ spin_unlock(&gpio->lock);
++
+ return ret;
+ }
+
+ static ssize_t gpio_show_oe_mask(struct gpio_item *gpio, char *page)
+ {
+- u32 mask = 0;
+-
+- spin_lock(&gpio->lock);
+- if (gpio->pio) {
+- mask = pio_readl(gpio->pio, OSR);
+- mask &= gpio->pin_mask;
+- }
+- spin_unlock(&gpio->lock);
+-
+- return sprintf(page, "0x%08x\n", mask);
++ return sprintf(page, "0x%08x\n", gpio->oe_mask);
+ }
+
+ static ssize_t gpio_store_oe_mask(struct gpio_item *gpio,
+@@ -443,10 +406,8 @@ static ssize_t gpio_store_oe_mask(struct
+ return -EINVAL;
+
+ spin_lock(&gpio->lock);
+- if (gpio->pio) {
+- mask &= gpio->pin_mask;
+- pio_writel(gpio->pio, ODR, mask ^ gpio->pin_mask);
+- pio_writel(gpio->pio, OER, mask);
++ if (!gpio->enabled) {
++ gpio->oe_mask = mask & gpio->pin_mask;
+ ret = count;
+ }
+ spin_unlock(&gpio->lock);
+@@ -462,6 +423,8 @@ static ssize_t gpio_show_enabled(struct
+ static ssize_t gpio_store_enabled(struct gpio_item *gpio,
+ const char *page, size_t count)
+ {
++ struct pio_device *pio;
++ u32 old, new;
+ char *p = (char *)page;
+ int enabled;
+ int ret;
+@@ -482,6 +445,12 @@ static ssize_t gpio_store_enabled(struct
+ if (!enabled) {
+ class_device_unregister(gpio->gpio_dev);
+ cdev_del(&gpio->char_dev);
++ __disable_gpio(gpio->pio, gpio->pin_mask);
++ pio_dealloc_mask(gpio->pio, gpio->pin_mask);
++ gpio->pio = NULL;
++ } else {
++ if (gpio->pio_id < 0 || !gpio->pin_mask)
++ return -ENODEV;
+ }
+
+ /* Disallow any updates to gpio_id or pin_mask */
+@@ -492,6 +461,20 @@ static ssize_t gpio_store_enabled(struct
+ if (!enabled)
+ return count;
+
++ /* Now, try to allocate the pins */
++ ret = -EBUSY;
++ pio = gpio->pio = &pio_dev[gpio->pio_id];
++ do {
++ old = pio->pinmux_mask;
++ if (old & gpio->pin_mask)
++ goto err_alloc_pins;
++
++ new = old | gpio->pin_mask;
++ } while (cmpxchg(&pio->pinmux_mask, old, new) != old);
++
++ pio_writel(pio, OER, gpio->oe_mask);
++ pio_writel(pio, PER, gpio->pin_mask);
++
+ cdev_init(&gpio->char_dev, &gpio_dev_fops);
+ gpio->char_dev.owner = THIS_MODULE;
+ ret = cdev_add(&gpio->char_dev, MKDEV(MAJOR(gpio_devt), gpio->id), 1);
+@@ -516,9 +499,13 @@ static ssize_t gpio_store_enabled(struct
+ err_class_dev:
+ cdev_del(&gpio->char_dev);
+ err_cdev_add:
++ __disable_gpio(pio, gpio->pin_mask);
++ pio_dealloc_mask(pio, gpio->pin_mask);
++err_alloc_pins:
+ spin_lock(&gpio->lock);
+ gpio->enabled = 0;
+ spin_unlock(&gpio->lock);
++ gpio->pio = NULL;
+
+ return ret;
+ }