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;
 }