]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpio/gpio-mxc.c
Merge branch 'merge' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
[karo-tx-linux.git] / drivers / gpio / gpio-mxc.c
index c89c4c1e668d97cf170f4fc07dc5911d83cb52d9..80f44bb64a871835b9cdf59561d8bff90d03126d 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/irq.h>
+#include <linux/irqdomain.h>
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <asm-generic/bug.h>
 #include <asm/mach/irq.h>
 
-#define irq_to_gpio(irq)       ((irq) - MXC_GPIO_IRQ_START)
-
 enum mxc_gpio_hwtype {
        IMX1_GPIO,      /* runs on i.mx1 */
        IMX21_GPIO,     /* runs on i.mx21 and i.mx27 */
-       IMX31_GPIO,     /* runs on all other i.mx */
+       IMX31_GPIO,     /* runs on i.mx31 */
+       IMX35_GPIO,     /* runs on all other i.mx */
 };
 
 /* device type dependent stuff */
@@ -50,6 +50,7 @@ struct mxc_gpio_hwdata {
        unsigned icr2_reg;
        unsigned imr_reg;
        unsigned isr_reg;
+       int edge_sel_reg;
        unsigned low_level;
        unsigned high_level;
        unsigned rise_edge;
@@ -61,7 +62,7 @@ struct mxc_gpio_port {
        void __iomem *base;
        int irq;
        int irq_high;
-       int virtual_irq_start;
+       struct irq_domain *domain;
        struct bgpio_chip bgc;
        u32 both_edges;
 };
@@ -74,6 +75,7 @@ static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
        .icr2_reg       = 0x2c,
        .imr_reg        = 0x30,
        .isr_reg        = 0x34,
+       .edge_sel_reg   = -EINVAL,
        .low_level      = 0x03,
        .high_level     = 0x02,
        .rise_edge      = 0x00,
@@ -88,6 +90,22 @@ static struct mxc_gpio_hwdata imx31_gpio_hwdata = {
        .icr2_reg       = 0x10,
        .imr_reg        = 0x14,
        .isr_reg        = 0x18,
+       .edge_sel_reg   = -EINVAL,
+       .low_level      = 0x00,
+       .high_level     = 0x01,
+       .rise_edge      = 0x02,
+       .fall_edge      = 0x03,
+};
+
+static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
+       .dr_reg         = 0x00,
+       .gdir_reg       = 0x04,
+       .psr_reg        = 0x08,
+       .icr1_reg       = 0x0c,
+       .icr2_reg       = 0x10,
+       .imr_reg        = 0x14,
+       .isr_reg        = 0x18,
+       .edge_sel_reg   = 0x1c,
        .low_level      = 0x00,
        .high_level     = 0x01,
        .rise_edge      = 0x02,
@@ -104,12 +122,13 @@ static struct mxc_gpio_hwdata *mxc_gpio_hwdata;
 #define GPIO_ICR2              (mxc_gpio_hwdata->icr2_reg)
 #define GPIO_IMR               (mxc_gpio_hwdata->imr_reg)
 #define GPIO_ISR               (mxc_gpio_hwdata->isr_reg)
+#define GPIO_EDGE_SEL          (mxc_gpio_hwdata->edge_sel_reg)
 
 #define GPIO_INT_LOW_LEV       (mxc_gpio_hwdata->low_level)
 #define GPIO_INT_HIGH_LEV      (mxc_gpio_hwdata->high_level)
 #define GPIO_INT_RISE_EDGE     (mxc_gpio_hwdata->rise_edge)
 #define GPIO_INT_FALL_EDGE     (mxc_gpio_hwdata->fall_edge)
-#define GPIO_INT_NONE          0x4
+#define GPIO_INT_BOTH_EDGES    0x4
 
 static struct platform_device_id mxc_gpio_devtype[] = {
        {
@@ -121,6 +140,9 @@ static struct platform_device_id mxc_gpio_devtype[] = {
        }, {
                .name = "imx31-gpio",
                .driver_data = IMX31_GPIO,
+       }, {
+               .name = "imx35-gpio",
+               .driver_data = IMX35_GPIO,
        }, {
                /* sentinel */
        }
@@ -130,6 +152,7 @@ static const struct of_device_id mxc_gpio_dt_ids[] = {
        { .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
        { .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
        { .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
+       { .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
        { /* sentinel */ }
 };
 
@@ -144,14 +167,15 @@ static LIST_HEAD(mxc_gpio_ports);
 
 static int gpio_set_irq_type(struct irq_data *d, u32 type)
 {
-       u32 gpio = irq_to_gpio(d->irq);
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        struct mxc_gpio_port *port = gc->private;
        u32 bit, val;
+       u32 gpio_idx = d->hwirq;
+       u32 gpio = port->bgc.gc.base + gpio_idx;
        int edge;
        void __iomem *reg = port->base;
 
-       port->both_edges &= ~(1 << (gpio & 31));
+       port->both_edges &= ~(1 << gpio_idx);
        switch (type) {
        case IRQ_TYPE_EDGE_RISING:
                edge = GPIO_INT_RISE_EDGE;
@@ -160,15 +184,19 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
                edge = GPIO_INT_FALL_EDGE;
                break;
        case IRQ_TYPE_EDGE_BOTH:
-               val = gpio_get_value(gpio);
-               if (val) {
-                       edge = GPIO_INT_LOW_LEV;
-                       pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
+               if (GPIO_EDGE_SEL >= 0) {
+                       edge = GPIO_INT_BOTH_EDGES;
                } else {
-                       edge = GPIO_INT_HIGH_LEV;
-                       pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
+                       val = gpio_get_value(gpio);
+                       if (val) {
+                               edge = GPIO_INT_LOW_LEV;
+                               pr_debug("mxc: set GPIO %d to low trigger\n", gpio);
+                       } else {
+                               edge = GPIO_INT_HIGH_LEV;
+                               pr_debug("mxc: set GPIO %d to high trigger\n", gpio);
+                       }
+                       port->both_edges |= 1 << gpio_idx;
                }
-               port->both_edges |= 1 << (gpio & 31);
                break;
        case IRQ_TYPE_LEVEL_LOW:
                edge = GPIO_INT_LOW_LEV;
@@ -180,11 +208,24 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
                return -EINVAL;
        }
 
-       reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
-       bit = gpio & 0xf;
-       val = readl(reg) & ~(0x3 << (bit << 1));
-       writel(val | (edge << (bit << 1)), reg);
-       writel(1 << (gpio & 0x1f), port->base + GPIO_ISR);
+       if (GPIO_EDGE_SEL >= 0) {
+               val = readl(port->base + GPIO_EDGE_SEL);
+               if (edge == GPIO_INT_BOTH_EDGES)
+                       writel(val | (1 << gpio_idx),
+                               port->base + GPIO_EDGE_SEL);
+               else
+                       writel(val & ~(1 << gpio_idx),
+                               port->base + GPIO_EDGE_SEL);
+       }
+
+       if (edge != GPIO_INT_BOTH_EDGES) {
+               reg += GPIO_ICR1 + ((gpio_idx & 0x10) >> 2); /* lower or upper register */
+               bit = gpio_idx & 0xf;
+               val = readl(reg) & ~(0x3 << (bit << 1));
+               writel(val | (edge << (bit << 1)), reg);
+       }
+
+       writel(1 << gpio_idx, port->base + GPIO_ISR);
 
        return 0;
 }
@@ -217,15 +258,13 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
 /* handle 32 interrupts in one status register */
 static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
 {
-       u32 gpio_irq_no_base = port->virtual_irq_start;
-
        while (irq_stat != 0) {
                int irqoffset = fls(irq_stat) - 1;
 
                if (port->both_edges & (1 << irqoffset))
                        mxc_flip_edge(port, irqoffset);
 
-               generic_handle_irq(gpio_irq_no_base + irqoffset);
+               generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
 
                irq_stat &= ~(1 << irqoffset);
        }
@@ -276,10 +315,9 @@ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
  */
 static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
 {
-       u32 gpio = irq_to_gpio(d->irq);
-       u32 gpio_idx = gpio & 0x1F;
        struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
        struct mxc_gpio_port *port = gc->private;
+       u32 gpio_idx = d->hwirq;
 
        if (enable) {
                if (port->irq_high && (gpio_idx >= 16))
@@ -296,12 +334,12 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
        return 0;
 }
 
-static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port)
+static void __init mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
 {
        struct irq_chip_generic *gc;
        struct irq_chip_type *ct;
 
-       gc = irq_alloc_generic_chip("gpio-mxc", 1, port->virtual_irq_start,
+       gc = irq_alloc_generic_chip("gpio-mxc", 1, irq_base,
                                    port->base, handle_level_irq);
        gc->private = port;
 
@@ -338,7 +376,9 @@ static void __devinit mxc_gpio_get_hw(struct platform_device *pdev)
                return;
        }
 
-       if (hwtype == IMX31_GPIO)
+       if (hwtype == IMX35_GPIO)
+               mxc_gpio_hwdata = &imx35_gpio_hwdata;
+       else if (hwtype == IMX31_GPIO)
                mxc_gpio_hwdata = &imx31_gpio_hwdata;
        else
                mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;
@@ -352,7 +392,7 @@ static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
        struct mxc_gpio_port *port =
                container_of(bgc, struct mxc_gpio_port, bgc);
 
-       return port->virtual_irq_start + offset;
+       return irq_find_mapping(port->domain, offset);
 }
 
 static int __devinit mxc_gpio_probe(struct platform_device *pdev)
@@ -360,6 +400,7 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev)
        struct device_node *np = pdev->dev.of_node;
        struct mxc_gpio_port *port;
        struct resource *iores;
+       int irq_base;
        int err;
 
        mxc_gpio_get_hw(pdev);
@@ -424,28 +465,37 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev)
                goto out_iounmap;
 
        port->bgc.gc.to_irq = mxc_gpio_to_irq;
-       port->bgc.gc.base = pdev->id * 32;
-       port->bgc.dir = port->bgc.read_reg(port->bgc.reg_dir);
-       port->bgc.data = port->bgc.read_reg(port->bgc.reg_set);
+       port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
+                                            pdev->id * 32;
 
        err = gpiochip_add(&port->bgc.gc);
        if (err)
                goto out_bgpio_remove;
 
-       /*
-        * In dt case, we use gpio number range dynamically
-        * allocated by gpio core.
-        */
-       port->virtual_irq_start = MXC_GPIO_IRQ_START + (np ? port->bgc.gc.base :
-                                                            pdev->id * 32);
+       irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
+       if (irq_base < 0) {
+               err = irq_base;
+               goto out_gpiochip_remove;
+       }
+
+       port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
+                                            &irq_domain_simple_ops, NULL);
+       if (!port->domain) {
+               err = -ENODEV;
+               goto out_irqdesc_free;
+       }
 
        /* gpio-mxc can be a generic irq chip */
-       mxc_gpio_init_gc(port);
+       mxc_gpio_init_gc(port, irq_base);
 
        list_add_tail(&port->node, &mxc_gpio_ports);
 
        return 0;
 
+out_irqdesc_free:
+       irq_free_descs(irq_base, 32);
+out_gpiochip_remove:
+       WARN_ON(gpiochip_remove(&port->bgc.gc) < 0);
 out_bgpio_remove:
        bgpio_remove(&port->bgc);
 out_iounmap: