Merge branch 'linus' into x86/urgent
[pandora-kernel.git] / drivers / char / vr41xx_giu.c
index a744dad..ffe9b4e 100644 (file)
@@ -3,7 +3,7 @@
  *
  *  Copyright (C) 2002 MontaVista Software Inc.
  *     Author: Yoichi Yuasa <yyuasa@mvista.com or source@mvista.com>
- *  Copyright (C) 2003-2005  Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
+ *  Copyright (C) 2003-2007  Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <linux/platform_device.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/init.h>
-#include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/smp_lock.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 
-#include <asm/cpu.h>
 #include <asm/io.h>
 #include <asm/vr41xx/giu.h>
 #include <asm/vr41xx/irq.h>
@@ -44,18 +44,6 @@ static int major;    /* default is dynamic major device number */
 module_param(major, int, 0);
 MODULE_PARM_DESC(major, "Major device number");
 
-#define GIU_TYPE1_START                0x0b000100UL
-#define GIU_TYPE1_SIZE         0x20UL
-
-#define GIU_TYPE2_START                0x0f000140UL
-#define GIU_TYPE2_SIZE         0x20UL
-
-#define GIU_TYPE3_START                0x0f000140UL
-#define GIU_TYPE3_SIZE         0x28UL
-
-#define GIU_PULLUPDOWN_START   0x0b0002e0UL
-#define GIU_PULLUPDOWN_SIZE    0x04UL
-
 #define GIUIOSELL      0x00
 #define GIUIOSELH      0x02
 #define GIUPIODL       0x04
@@ -89,8 +77,6 @@ MODULE_PARM_DESC(major, "Major device number");
 #define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100
 
 static spinlock_t giu_lock;
-static struct resource *giu_resource1;
-static struct resource *giu_resource2;
 static unsigned long giu_flags;
 static unsigned int giu_nr_pins;
 
@@ -125,30 +111,17 @@ static inline uint16_t giu_clear(uint16_t offset, uint16_t clear)
        return data;
 }
 
-static unsigned int startup_giuint_low_irq(unsigned int irq)
+static void ack_giuint_low(unsigned int irq)
 {
-       unsigned int pin;
-
-       pin = GPIO_PIN_OF_IRQ(irq);
-       giu_write(GIUINTSTATL, 1 << pin);
-       giu_set(GIUINTENL, 1 << pin);
-
-       return 0;
+       giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(irq));
 }
 
-static void shutdown_giuint_low_irq(unsigned int irq)
+static void mask_giuint_low(unsigned int irq)
 {
        giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
 }
 
-static void enable_giuint_low_irq(unsigned int irq)
-{
-       giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
-}
-
-#define disable_giuint_low_irq shutdown_giuint_low_irq
-
-static void ack_giuint_low_irq(unsigned int irq)
+static void mask_ack_giuint_low(unsigned int irq)
 {
        unsigned int pin;
 
@@ -157,46 +130,30 @@ static void ack_giuint_low_irq(unsigned int irq)
        giu_write(GIUINTSTATL, 1 << pin);
 }
 
-static void end_giuint_low_irq(unsigned int irq)
+static void unmask_giuint_low(unsigned int irq)
 {
-       if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
-               giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
+       giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(irq));
 }
 
-static struct hw_interrupt_type giuint_low_irq_type = {
-       .typename       = "GIUINTL",
-       .startup        = startup_giuint_low_irq,
-       .shutdown       = shutdown_giuint_low_irq,
-       .enable         = enable_giuint_low_irq,
-       .disable        = disable_giuint_low_irq,
-       .ack            = ack_giuint_low_irq,
-       .end            = end_giuint_low_irq,
+static struct irq_chip giuint_low_irq_chip = {
+       .name           = "GIUINTL",
+       .ack            = ack_giuint_low,
+       .mask           = mask_giuint_low,
+       .mask_ack       = mask_ack_giuint_low,
+       .unmask         = unmask_giuint_low,
 };
 
-static unsigned int startup_giuint_high_irq(unsigned int irq)
+static void ack_giuint_high(unsigned int irq)
 {
-       unsigned int pin;
-
-       pin = GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET;
-       giu_write(GIUINTSTATH, 1 << pin);
-       giu_set(GIUINTENH, 1 << pin);
-
-       return 0;
+       giu_write(GIUINTSTATH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
 }
 
-static void shutdown_giuint_high_irq(unsigned int irq)
+static void mask_giuint_high(unsigned int irq)
 {
        giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
 }
 
-static void enable_giuint_high_irq(unsigned int irq)
-{
-       giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
-}
-
-#define disable_giuint_high_irq        shutdown_giuint_high_irq
-
-static void ack_giuint_high_irq(unsigned int irq)
+static void mask_ack_giuint_high(unsigned int irq)
 {
        unsigned int pin;
 
@@ -205,20 +162,17 @@ static void ack_giuint_high_irq(unsigned int irq)
        giu_write(GIUINTSTATH, 1 << pin);
 }
 
-static void end_giuint_high_irq(unsigned int irq)
+static void unmask_giuint_high(unsigned int irq)
 {
-       if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
-               giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
+       giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(irq) - GIUINT_HIGH_OFFSET));
 }
 
-static struct hw_interrupt_type giuint_high_irq_type = {
-       .typename       = "GIUINTH",
-       .startup        = startup_giuint_high_irq,
-       .shutdown       = shutdown_giuint_high_irq,
-       .enable         = enable_giuint_high_irq,
-       .disable        = disable_giuint_high_irq,
-       .ack            = ack_giuint_high_irq,
-       .end            = end_giuint_high_irq,
+static struct irq_chip giuint_high_irq_chip = {
+       .name           = "GIUINTH",
+       .ack            = ack_giuint_high,
+       .mask           = mask_giuint_high,
+       .mask_ack       = mask_ack_giuint_high,
+       .unmask         = unmask_giuint_high,
 };
 
 static int giu_get_irq(unsigned int irq)
@@ -266,7 +220,7 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_
                                giu_set(GIUINTHTSELL, mask);
                        else
                                giu_clear(GIUINTHTSELL, mask);
-                       if (current_cpu_data.cputype == CPU_VR4133) {
+                       if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {
                                switch (trigger) {
                                case IRQ_TRIGGER_EDGE_FALLING:
                                        giu_set(GIUFEDGEINHL, mask);
@@ -282,9 +236,15 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_
                                        break;
                                }
                        }
+                       set_irq_chip_and_handler(GIU_IRQ(pin),
+                                                &giuint_low_irq_chip,
+                                                handle_edge_irq);
                } else {
                        giu_clear(GIUINTTYPL, mask);
                        giu_clear(GIUINTHTSELL, mask);
+                       set_irq_chip_and_handler(GIU_IRQ(pin),
+                                                &giuint_low_irq_chip,
+                                                handle_level_irq);
                }
                giu_write(GIUINTSTATL, mask);
        } else if (pin < GIUINT_HIGH_MAX) {
@@ -295,7 +255,7 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_
                                giu_set(GIUINTHTSELH, mask);
                        else
                                giu_clear(GIUINTHTSELH, mask);
-                       if (current_cpu_data.cputype == CPU_VR4133) {
+                       if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) {
                                switch (trigger) {
                                case IRQ_TRIGGER_EDGE_FALLING:
                                        giu_set(GIUFEDGEINHH, mask);
@@ -311,14 +271,19 @@ void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, irq_signal_
                                        break;
                                }
                        }
+                       set_irq_chip_and_handler(GIU_IRQ(pin),
+                                                &giuint_high_irq_chip,
+                                                handle_edge_irq);
                } else {
                        giu_clear(GIUINTTYPH, mask);
                        giu_clear(GIUINTHTSELH, mask);
+                       set_irq_chip_and_handler(GIU_IRQ(pin),
+                                                &giuint_high_irq_chip,
+                                                handle_level_irq);
                }
                giu_write(GIUINTSTATH, mask);
        }
 }
-
 EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger);
 
 void vr41xx_set_irq_level(unsigned int pin, irq_level_t level)
@@ -341,7 +306,6 @@ void vr41xx_set_irq_level(unsigned int pin, irq_level_t level)
                giu_write(GIUINTSTATH, mask);
        }
 }
-
 EXPORT_SYMBOL_GPL(vr41xx_set_irq_level);
 
 gpio_data_t vr41xx_gpio_get_pin(unsigned int pin)
@@ -370,7 +334,6 @@ gpio_data_t vr41xx_gpio_get_pin(unsigned int pin)
 
        return GPIO_DATA_LOW;
 }
-
 EXPORT_SYMBOL_GPL(vr41xx_gpio_get_pin);
 
 int vr41xx_gpio_set_pin(unsigned int pin, gpio_data_t data)
@@ -408,7 +371,6 @@ int vr41xx_gpio_set_pin(unsigned int pin, gpio_data_t data)
 
        return 0;
 }
-
 EXPORT_SYMBOL_GPL(vr41xx_gpio_set_pin);
 
 int vr41xx_gpio_set_direction(unsigned int pin, gpio_direction_t dir)
@@ -458,7 +420,6 @@ int vr41xx_gpio_set_direction(unsigned int pin, gpio_direction_t dir)
 
        return 0;
 }
-
 EXPORT_SYMBOL_GPL(vr41xx_gpio_set_direction);
 
 int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull)
@@ -497,7 +458,6 @@ int vr41xx_gpio_pullupdown(unsigned int pin, gpio_pull_t pull)
 
        return 0;
 }
-
 EXPORT_SYMBOL_GPL(vr41xx_gpio_pullupdown);
 
 static ssize_t gpio_read(struct file *file, char __user *buf, size_t len,
@@ -588,6 +548,7 @@ static int gpio_open(struct inode *inode, struct file *file)
 {
        unsigned int pin;
 
+       cycle_kernel_lock();
        pin = iminor(inode);
        if (pin >= giu_nr_pins)
                return -EBADF;
@@ -616,60 +577,40 @@ static const struct file_operations gpio_fops = {
 
 static int __devinit giu_probe(struct platform_device *dev)
 {
-       unsigned long start, size, flags = 0;
-       unsigned int nr_pins = 0;
-       struct resource *res1, *res2 = NULL;
-       void *base;
-       int retval, i;
-
-       switch (current_cpu_data.cputype) {
-       case CPU_VR4111:
-       case CPU_VR4121:
-               start = GIU_TYPE1_START;
-               size = GIU_TYPE1_SIZE;
-               flags = GPIO_HAS_PULLUPDOWN_IO;
-               nr_pins = 50;
+       struct resource *res;
+       unsigned int trigger, i, pin;
+       struct irq_chip *chip;
+       int irq, retval;
+
+       switch (dev->id) {
+       case GPIO_50PINS_PULLUPDOWN:
+               giu_flags = GPIO_HAS_PULLUPDOWN_IO;
+               giu_nr_pins = 50;
                break;
-       case CPU_VR4122:
-       case CPU_VR4131:
-               start = GIU_TYPE2_START;
-               size = GIU_TYPE2_SIZE;
-               nr_pins = 36;
+       case GPIO_36PINS:
+               giu_nr_pins = 36;
                break;
-       case CPU_VR4133:
-               start = GIU_TYPE3_START;
-               size = GIU_TYPE3_SIZE;
-               flags = GPIO_HAS_INTERRUPT_EDGE_SELECT;
-               nr_pins = 48;
+       case GPIO_48PINS_EDGE_SELECT:
+               giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT;
+               giu_nr_pins = 48;
                break;
        default:
+               printk(KERN_ERR "GIU: unknown ID %d\n", dev->id);
                return -ENODEV;
        }
 
-       res1 = request_mem_region(start, size, "GIU");
-       if (res1 == NULL)
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!res)
                return -EBUSY;
 
-       base = ioremap(start, size);
-       if (base == NULL) {
-               release_resource(res1);
+       giu_base = ioremap(res->start, res->end - res->start + 1);
+       if (!giu_base)
                return -ENOMEM;
-       }
-
-       if (flags & GPIO_HAS_PULLUPDOWN_IO) {
-               res2 = request_mem_region(GIU_PULLUPDOWN_START, GIU_PULLUPDOWN_SIZE, "GIU");
-               if (res2 == NULL) {
-                       iounmap(base);
-                       release_resource(res1);
-                       return -EBUSY;
-               }
-       }
 
        retval = register_chrdev(major, "GIU", &gpio_fops);
        if (retval < 0) {
-               iounmap(base);
-               release_resource(res1);
-               release_resource(res2);
+               iounmap(giu_base);
+               giu_base = NULL;
                return retval;
        }
 
@@ -679,38 +620,43 @@ static int __devinit giu_probe(struct platform_device *dev)
        }
 
        spin_lock_init(&giu_lock);
-       giu_base = base;
-       giu_resource1 = res1;
-       giu_resource2 = res2;
-       giu_flags = flags;
-       giu_nr_pins = nr_pins;
 
        giu_write(GIUINTENL, 0);
        giu_write(GIUINTENH, 0);
 
+       trigger = giu_read(GIUINTTYPH) << 16;
+       trigger |= giu_read(GIUINTTYPL);
        for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) {
-               if (i < GIU_IRQ(GIUINT_HIGH_OFFSET))
-                       irq_desc[i].chip = &giuint_low_irq_type;
+               pin = GPIO_PIN_OF_IRQ(i);
+               if (pin < GIUINT_HIGH_OFFSET)
+                       chip = &giuint_low_irq_chip;
                else
-                       irq_desc[i].chip = &giuint_high_irq_type;
+                       chip = &giuint_high_irq_chip;
+
+               if (trigger & (1 << pin))
+                       set_irq_chip_and_handler(i, chip, handle_edge_irq);
+               else
+                       set_irq_chip_and_handler(i, chip, handle_level_irq);
+
        }
 
-       return cascade_irq(GIUINT_IRQ, giu_get_irq);
+       irq = platform_get_irq(dev, 0);
+       if (irq < 0 || irq >= NR_IRQS)
+               return -EBUSY;
+
+       return cascade_irq(irq, giu_get_irq);
 }
 
 static int __devexit giu_remove(struct platform_device *dev)
 {
-       iounmap(giu_base);
-
-       release_resource(giu_resource1);
-       if (giu_flags & GPIO_HAS_PULLUPDOWN_IO)
-               release_resource(giu_resource2);
+       if (giu_base) {
+               iounmap(giu_base);
+               giu_base = NULL;
+       }
 
        return 0;
 }
 
-static struct platform_device *giu_platform_device;
-
 static struct platform_driver giu_device_driver = {
        .probe          = giu_probe,
        .remove         = __devexit_p(giu_remove),
@@ -722,30 +668,12 @@ static struct platform_driver giu_device_driver = {
 
 static int __init vr41xx_giu_init(void)
 {
-       int retval;
-
-       giu_platform_device = platform_device_alloc("GIU", -1);
-       if (!giu_platform_device)
-               return -ENOMEM;
-
-       retval = platform_device_add(giu_platform_device);
-       if (retval < 0) {
-               platform_device_put(giu_platform_device);
-               return retval;
-       }
-
-       retval = platform_driver_register(&giu_device_driver);
-       if (retval < 0)
-               platform_device_unregister(giu_platform_device);
-
-       return retval;
+       return platform_driver_register(&giu_device_driver);
 }
 
 static void __exit vr41xx_giu_exit(void)
 {
        platform_driver_unregister(&giu_device_driver);
-
-       platform_device_unregister(giu_platform_device);
 }
 
 module_init(vr41xx_giu_init);