aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-2.6.18/avr32-gpio-dev.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-2.6.18/avr32-gpio-dev.patch')
-rw-r--r--recipes/linux/linux-2.6.18/avr32-gpio-dev.patch548
1 files changed, 548 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.18/avr32-gpio-dev.patch b/recipes/linux/linux-2.6.18/avr32-gpio-dev.patch
new file mode 100644
index 0000000000..43971cf4e6
--- /dev/null
+++ b/recipes/linux/linux-2.6.18/avr32-gpio-dev.patch
@@ -0,0 +1,548 @@
+---
+ arch/avr32/mach-at32ap/Kconfig | 8
+ arch/avr32/mach-at32ap/pio.c | 499 ++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 506 insertions(+), 1 deletion(-)
+
+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 16:22:14.000000000 +0100
++++ linux-2.6.18-avr32/arch/avr32/mach-at32ap/pio.c 2006-11-29 16:29:20.000000000 +0100
+@@ -22,7 +22,7 @@
+
+ struct pio_device {
+ void __iomem *regs;
+- const struct platform_device *pdev;
++ struct platform_device *pdev;
+ struct clk *clk;
+ u32 pinmux_mask;
+ u32 gpio_mask;
+@@ -119,6 +119,34 @@ fail:
+ dump_stack();
+ }
+
++static unsigned int pio_id(struct pio_device *pio)
++{
++ 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);
++ pio_writel(pio, ODR, mask);
++}
++
++static void pio_dealloc_mask(struct pio_device *pio, u32 mask)
++{
++ u32 old, new;
++
++ do {
++ old = pio->pinmux_mask;
++ new = old & ~mask;
++ } while (cmpxchg(&pio->pinmux_mask, old, new) != old);
++}
++
+ /* GPIO API */
+
+ int gpio_request(unsigned int gpio, const char *label)
+@@ -210,6 +238,475 @@ void gpio_set_value(unsigned int gpio, i
+ }
+ EXPORT_SYMBOL(gpio_set_value);
+
++#ifdef CONFIG_PIO_DEV
++#include <linux/configfs.h>
++#include <linux/cdev.h>
++#include <linux/uaccess.h>
++
++#define GPIO_DEV_MAX 8
++
++static struct class *gpio_dev_class;
++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;
++ u32 pin_mask;
++
++ int id;
++ struct class_device *gpio_dev;
++ struct cdev char_dev;
++ struct config_item item;
++};
++
++struct gpio_attribute {
++ struct configfs_attribute attr;
++ ssize_t (*show)(struct gpio_item *, char *);
++ ssize_t (*store)(struct gpio_item *, const char *, size_t);
++};
++
++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);
++
++ config_item_get(&gpio->item);
++ file->private_data = gpio;
++ return 0;
++}
++
++static int gpio_dev_release(struct inode *inode, struct file *file)
++{
++ struct gpio_item *gpio = file->private_data;
++
++ config_item_put(&gpio->item);
++ return 0;
++}
++
++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;
++
++ value = pio_readl(gpio->pio, PDSR) & gpio->pin_mask;
++
++ count = min(count, (size_t)4);
++ if (copy_to_user(buf, &value, count))
++ return -EFAULT;
++ return count;
++}
++
++static ssize_t gpio_dev_write(struct file *file, const char __user *buf,
++ size_t count, loff_t *offset)
++{
++ struct gpio_item *gpio = file->private_data;
++ u32 value = 0;
++ u32 mask = ~0UL;
++
++ count = min(count, (size_t)4);
++ if (copy_from_user(&value, buf, count))
++ return -EFAULT;
++
++ /* Assuming big endian */
++ mask <<= (4 - count) * 8;
++ mask &= gpio->pin_mask;
++
++ pio_writel(gpio->pio, CODR, ~value & mask);
++ pio_writel(gpio->pio, SODR, value & mask);
++
++ return count;
++}
++
++static struct file_operations gpio_dev_fops = {
++ .owner = THIS_MODULE,
++ .llseek = no_llseek,
++ .open = gpio_dev_open,
++ .release = gpio_dev_release,
++ .read = gpio_dev_read,
++ .write = gpio_dev_write,
++};
++
++static struct gpio_item *to_gpio_item(struct config_item *item)
++{
++ return item ? container_of(item, struct gpio_item, item) : NULL;
++}
++
++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");
++}
++
++static ssize_t gpio_store_gpio_id(struct gpio_item *gpio,
++ const char *page, size_t count)
++{
++ unsigned long id;
++ char *p = (char *)page;
++ ssize_t ret = -EINVAL;
++
++ id = simple_strtoul(p, &p, 0);
++ if (!p || (*p && (*p != '\n')))
++ return -EINVAL;
++
++ /* Switching PIO is not allowed when live... */
++ spin_lock(&gpio->lock);
++ if (!gpio->enabled) {
++ ret = -ENXIO;
++ if ((id < MAX_NR_PIO_DEVICES) && pio_dev[id].regs) {
++ gpio->pio = &pio_dev[id];
++ ret = count;
++ }
++ }
++ spin_unlock(&gpio->lock);
++
++ return ret;
++}
++
++static ssize_t gpio_show_pin_mask(struct gpio_item *gpio, char *page)
++{
++ return sprintf(page, "0x%08x\n", gpio->pin_mask);
++}
++
++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;
++ char *p = (char *)page;
++ ssize_t ret = -EINVAL;
++
++ new_mask = simple_strtoul(p, &p, 0);
++ if (!p || (*p && (*p != '\n')))
++ return -EINVAL;
++
++ /*
++ * Must have a PIO before we can start allocating pins, but we
++ * must not be 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:
++ 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);
++}
++
++static ssize_t gpio_store_oe_mask(struct gpio_item *gpio,
++ const char *page, size_t count)
++{
++ u32 mask;
++ char *p = (char *)page;
++ ssize_t ret = -EINVAL;
++
++ mask = simple_strtoul(p, &p, 0);
++ if (!p || (*p && (*p != '\n')))
++ 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);
++ ret = count;
++ }
++ spin_unlock(&gpio->lock);
++
++ return ret;
++}
++
++static ssize_t gpio_show_enabled(struct gpio_item *gpio, char *page)
++{
++ return sprintf(page, "%d\n", gpio->enabled);
++}
++
++static ssize_t gpio_store_enabled(struct gpio_item *gpio,
++ const char *page, size_t count)
++{
++ char *p = (char *)page;
++ int enabled;
++ int ret;
++
++ enabled = simple_strtoul(p, &p, 0);
++ if (!p || (*p && (*p != '\n')))
++ return -EINVAL;
++
++ /* make it a boolean value */
++ enabled = !!enabled;
++
++ if (gpio->enabled == enabled)
++ /* Already enabled; do nothing. */
++ return count;
++
++ BUG_ON(gpio->id >= GPIO_DEV_MAX);
++
++ if (!enabled) {
++ class_device_unregister(gpio->gpio_dev);
++ cdev_del(&gpio->char_dev);
++ }
++
++ /* Disallow any updates to gpio_id or pin_mask */
++ spin_lock(&gpio->lock);
++ gpio->enabled = enabled;
++ spin_unlock(&gpio->lock);
++
++ if (!enabled)
++ return count;
++
++ 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);
++ if (ret < 0)
++ goto err_cdev_add;
++ gpio->gpio_dev = class_device_create(gpio_dev_class, NULL,
++ MKDEV(MAJOR(gpio_devt), gpio->id),
++ &gpio->pio->pdev->dev,
++ "gpio%d", gpio->id);
++ if (IS_ERR(gpio->gpio_dev)) {
++ printk(KERN_ERR "failed to create gpio%d\n", gpio->id);
++ ret = PTR_ERR(gpio->gpio_dev);
++ goto err_class_dev;
++ }
++
++ printk(KERN_INFO "created gpio%d (pio%d/0x%08x) as (%d:%d)\n",
++ gpio->id, pio_id(gpio->pio), gpio->pin_mask,
++ MAJOR(gpio->gpio_dev->devt), MINOR(gpio->gpio_dev->devt));
++
++ return 0;
++
++err_class_dev:
++ cdev_del(&gpio->char_dev);
++err_cdev_add:
++ spin_lock(&gpio->lock);
++ gpio->enabled = 0;
++ spin_unlock(&gpio->lock);
++
++ return ret;
++}
++
++static struct gpio_attribute gpio_item_attr_gpio_id = {
++ .attr = {
++ .ca_owner = THIS_MODULE,
++ .ca_name = "gpio_id",
++ .ca_mode = S_IRUGO | S_IWUSR,
++ },
++ .show = gpio_show_gpio_id,
++ .store = gpio_store_gpio_id,
++};
++static struct gpio_attribute gpio_item_attr_pin_mask = {
++ .attr = {
++ .ca_owner = THIS_MODULE,
++ .ca_name = "pin_mask",
++ .ca_mode = S_IRUGO | S_IWUSR,
++ },
++ .show = gpio_show_pin_mask,
++ .store = gpio_store_pin_mask,
++};
++static struct gpio_attribute gpio_item_attr_oe_mask = {
++ .attr = {
++ .ca_owner = THIS_MODULE,
++ .ca_name = "oe_mask",
++ .ca_mode = S_IRUGO | S_IWUSR,
++ },
++ .show = gpio_show_oe_mask,
++ .store = gpio_store_oe_mask,
++};
++static struct gpio_attribute gpio_item_attr_enabled = {
++ .attr = {
++ .ca_owner = THIS_MODULE,
++ .ca_name = "enabled",
++ .ca_mode = S_IRUGO | S_IWUSR,
++ },
++ .show = gpio_show_enabled,
++ .store = gpio_store_enabled,
++};
++
++static struct configfs_attribute *gpio_item_attrs[] = {
++ &gpio_item_attr_gpio_id.attr,
++ &gpio_item_attr_pin_mask.attr,
++ &gpio_item_attr_oe_mask.attr,
++ &gpio_item_attr_enabled.attr,
++ NULL,
++};
++
++static ssize_t gpio_show_attr(struct config_item *item,
++ struct configfs_attribute *attr,
++ char *page)
++{
++ struct gpio_item *gpio_item = to_gpio_item(item);
++ struct gpio_attribute *gpio_attr
++ = container_of(attr, struct gpio_attribute, attr);
++ ssize_t ret = 0;
++
++ if (gpio_attr->show)
++ ret = gpio_attr->show(gpio_item, page);
++ return ret;
++}
++
++static ssize_t gpio_store_attr(struct config_item *item,
++ struct configfs_attribute *attr,
++ const char *page, size_t count)
++{
++ struct gpio_item *gpio_item = to_gpio_item(item);
++ struct gpio_attribute *gpio_attr
++ = container_of(attr, struct gpio_attribute, attr);
++ ssize_t ret = -EINVAL;
++
++ if (gpio_attr->store)
++ ret = gpio_attr->store(gpio_item, page, count);
++ return ret;
++}
++
++static void gpio_release(struct config_item *item)
++{
++ kfree(to_gpio_item(item));
++}
++
++static struct configfs_item_operations gpio_item_ops = {
++ .release = gpio_release,
++ .show_attribute = gpio_show_attr,
++ .store_attribute = gpio_store_attr,
++};
++
++static struct config_item_type gpio_item_type = {
++ .ct_item_ops = &gpio_item_ops,
++ .ct_attrs = gpio_item_attrs,
++ .ct_owner = THIS_MODULE,
++};
++
++static struct config_item *gpio_make_item(struct config_group *group,
++ const char *name)
++{
++ static int next_id;
++ struct gpio_item *gpio;
++
++ if (next_id >= GPIO_DEV_MAX)
++ return NULL;
++
++ gpio = kzalloc(sizeof(struct gpio_item), GFP_KERNEL);
++ if (!gpio)
++ return NULL;
++
++ gpio->id = next_id++;
++ config_item_init_type_name(&gpio->item, name, &gpio_item_type);
++ spin_lock_init(&gpio->lock);
++
++ return &gpio->item;
++}
++
++static void gpio_drop_item(struct config_group *group,
++ struct config_item *item)
++{
++ struct gpio_item *gpio = to_gpio_item(item);
++ struct pio_device *pio;
++
++ spin_lock(&gpio->lock);
++ if (gpio->enabled) {
++ class_device_unregister(gpio->gpio_dev);
++ cdev_del(&gpio->char_dev);
++ }
++
++ pio = gpio->pio;
++ if (pio) {
++ __disable_gpio(pio, gpio->pin_mask);
++ pio_dealloc_mask(pio, gpio->pin_mask);
++ gpio->pio = NULL;
++ }
++ spin_unlock(&gpio->lock);
++}
++
++static struct configfs_group_operations gpio_group_ops = {
++ .make_item = gpio_make_item,
++ .drop_item = gpio_drop_item,
++};
++
++static struct config_item_type gpio_group_type = {
++ .ct_group_ops = &gpio_group_ops,
++ .ct_owner = THIS_MODULE,
++};
++
++static struct configfs_subsystem gpio_subsys = {
++ .su_group = {
++ .cg_item = {
++ .ci_namebuf = "gpio",
++ .ci_type = &gpio_group_type,
++ },
++ },
++};
++
++static int __init pio_init_dev(void)
++{
++ int err;
++
++ gpio_dev_class = class_create(THIS_MODULE, "gpio-dev");
++ if (IS_ERR(gpio_dev_class)) {
++ err = PTR_ERR(gpio_dev_class);
++ goto err_class_create;
++ }
++
++ err = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpio");
++ if (err < 0)
++ goto err_alloc_chrdev;
++
++ /* Configfs initialization */
++ config_group_init(&gpio_subsys.su_group);
++ init_MUTEX(&gpio_subsys.su_sem);
++ err = configfs_register_subsystem(&gpio_subsys);
++ if (err)
++ goto err_register_subsys;
++
++ return 0;
++
++err_register_subsys:
++ unregister_chrdev_region(gpio_devt, GPIO_DEV_MAX);
++err_alloc_chrdev:
++ class_destroy(gpio_dev_class);
++err_class_create:
++ printk(KERN_WARNING "Failed to initialize gpio /dev interface\n");
++ return err;
++}
++late_initcall(pio_init_dev);
++#endif /* CONFIG_PIO_DEV */
++
+ static int __init pio_probe(struct platform_device *pdev)
+ {
+ struct pio_device *pio = NULL;
+Index: linux-2.6.18-avr32/arch/avr32/mach-at32ap/Kconfig
+===================================================================
+--- linux-2.6.18-avr32.orig/arch/avr32/mach-at32ap/Kconfig 2006-11-29 16:23:31.000000000 +0100
++++ linux-2.6.18-avr32/arch/avr32/mach-at32ap/Kconfig 2006-11-29 16:24:35.000000000 +0100
+@@ -2,6 +2,14 @@ if PLATFORM_AT32AP
+
+ menu "Atmel AVR32 AP options"
+
++config PIO_DEV
++ bool "PIO /dev interface"
++ select CONFIGFS_FS
++ default y
++ help
++ Say `Y' to enable a /dev interface to the Parallel I/O
++ Controller.
++
+ endmenu
+
+ endif