--- arch/avr32/mach-at32ap/pio.c | 113 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) 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-30 13:16:43.000000000 +0100 +++ linux-2.6.18-avr32/arch/avr32/mach-at32ap/pio.c 2006-11-30 17:15:24.000000000 +0100 @@ -250,7 +250,11 @@ EXPORT_SYMBOL(gpio_set_value); #ifdef CONFIG_PIO_DEV #include #include +#include +#include +#include #include +#include #define GPIO_DEV_MAX 8 @@ -267,6 +271,13 @@ struct gpio_item { u32 pin_mask; u32 oe_mask; + /* Pin state last time we read it (for blocking reads) */ + u32 pin_state; + int changed; + + wait_queue_head_t change_wq; + struct fasync_struct *async_queue; + int id; struct class_device *gpio_dev; struct cdev char_dev; @@ -279,36 +290,135 @@ struct gpio_attribute { ssize_t (*store)(struct gpio_item *, const char *, size_t); }; +static irqreturn_t gpio_dev_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct gpio_item *gpio = dev_id; + u32 old_state, new_state; + + old_state = gpio->pin_state; + new_state = pio_readl(gpio->pio, PDSR); + gpio->pin_state = new_state; + + if (new_state != old_state) { + gpio->changed = 1; + wake_up_interruptible(&gpio->change_wq); + + if (gpio->async_queue) + kill_fasync(&gpio->async_queue, SIGIO, POLL_IN); + } + + return IRQ_HANDLED; +} + static int gpio_dev_open(struct inode *inode, struct file *file) { struct gpio_item *gpio = container_of(inode->i_cdev, struct gpio_item, char_dev); + unsigned int irq; + unsigned int i; + int ret; + nonseekable_open(inode, file); config_item_get(&gpio->item); file->private_data = gpio; + + gpio->pin_state = pio_readl(gpio->pio, PDSR) & gpio->pin_mask; + gpio->changed = 1; + + for (i = 0; i < 32; i++) { + if (gpio->pin_mask & (1 << i)) { + irq = gpio_to_irq(32 * pio_id(gpio->pio) + i); + ret = request_irq(irq, gpio_dev_interrupt, 0, + "gpio-dev", gpio); + if (ret) + goto err_irq; + } + } + return 0; + +err_irq: + while (i--) { + if (gpio->pin_mask & (1 << i)) { + irq = gpio_to_irq(32 * pio_id(gpio->pio) + i); + free_irq(irq, gpio); + } + } + + config_item_put(&gpio->item); + + return ret; +} + +static int gpio_dev_fasync(int fd, struct file *file, int mode) +{ + struct gpio_item *gpio = file->private_data; + + return fasync_helper(fd, file, mode, &gpio->async_queue); } static int gpio_dev_release(struct inode *inode, struct file *file) { struct gpio_item *gpio = file->private_data; + unsigned int irq; + unsigned int i; + + gpio_dev_fasync(-1, file, 0); + + for (i = 0; i < 32; i++) { + if (gpio->pin_mask & (1 << i)) { + irq = gpio_to_irq(32 * pio_id(gpio->pio) + i); + free_irq(irq, gpio); + } + } config_item_put(&gpio->item); + return 0; } +static unsigned int gpio_dev_poll(struct file *file, poll_table *wait) +{ + struct gpio_item *gpio = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &gpio->change_wq, wait); + if (gpio->changed) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + static ssize_t gpio_dev_read(struct file *file, char __user *buf, size_t count, loff_t *offset) { struct gpio_item *gpio = file->private_data; u32 value; + spin_lock_irq(&gpio->lock); + while (!gpio->changed) { + spin_unlock_irq(&gpio->lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(gpio->change_wq, gpio->changed)) + return -ERESTARTSYS; + + spin_lock_irq(&gpio->lock); + } + + gpio->changed = 0; value = pio_readl(gpio->pio, PDSR) & gpio->pin_mask; + spin_unlock_irq(&gpio->lock); + count = min(count, (size_t)4); if (copy_to_user(buf, &value, count)) return -EFAULT; + return count; } @@ -338,6 +448,8 @@ static struct file_operations gpio_dev_f .llseek = no_llseek, .open = gpio_dev_open, .release = gpio_dev_release, + .fasync = gpio_dev_fasync, + .poll = gpio_dev_poll, .read = gpio_dev_read, .write = gpio_dev_write, }; @@ -632,6 +744,7 @@ static struct config_item *gpio_make_ite gpio->id = next_id++; config_item_init_type_name(&gpio->item, name, &gpio_item_type); spin_lock_init(&gpio->lock); + init_waitqueue_head(&gpio->change_wq); return &gpio->item; }