]> Pileus Git - ~andy/linux/blobdiff - drivers/irqchip/exynos-combiner.c
drm/i915: document why dvo/sdvo/crt need a special dpms function
[~andy/linux] / drivers / irqchip / exynos-combiner.c
index 02492ab20d22a466a7ba4923e94c02ed892870f3..a9d2b2fa4afd3149b261fb256fb47bcde79c81aa 100644 (file)
 #include <linux/export.h>
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/slab.h>
 #include <linux/irqdomain.h>
 #include <linux/irqchip/chained_irq.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <asm/mach/irq.h>
 
+#ifdef CONFIG_EXYNOS_ATAGS
 #include <plat/cpu.h>
+#endif
 
 #include "irqchip.h"
 
 #define COMBINER_ENABLE_CLEAR  0x4
 #define COMBINER_INT_STATUS    0xC
 
+#define IRQ_IN_COMBINER                8
+
 static DEFINE_SPINLOCK(irq_controller_lock);
 
 struct combiner_chip_data {
-       unsigned int irq_offset;
+       unsigned int hwirq_offset;
        unsigned int irq_mask;
        void __iomem *base;
        unsigned int parent_irq;
 };
 
 static struct irq_domain *combiner_irq_domain;
-static struct combiner_chip_data combiner_data[MAX_COMBINER_NR];
 
 static inline void __iomem *combiner_base(struct irq_data *data)
 {
@@ -77,11 +81,11 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc)
        if (status == 0)
                goto out;
 
-       combiner_irq = __ffs(status);
+       combiner_irq = chip_data->hwirq_offset + __ffs(status);
+       cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq);
 
-       cascade_irq = combiner_irq + (chip_data->irq_offset & ~31);
-       if (unlikely(cascade_irq >= NR_IRQS))
-               do_bad_IRQ(cascade_irq, desc);
+       if (unlikely(!cascade_irq))
+               do_bad_IRQ(irq, desc);
        else
                generic_handle_irq(cascade_irq);
 
@@ -113,40 +117,25 @@ static struct irq_chip combiner_chip = {
 #endif
 };
 
-static unsigned int max_combiner_nr(void)
-{
-       if (soc_is_exynos5250())
-               return EXYNOS5_MAX_COMBINER_NR;
-       else if (soc_is_exynos4412())
-               return EXYNOS4412_MAX_COMBINER_NR;
-       else if (soc_is_exynos4212())
-               return EXYNOS4212_MAX_COMBINER_NR;
-       else
-               return EXYNOS4210_MAX_COMBINER_NR;
-}
-
-static void __init combiner_cascade_irq(unsigned int combiner_nr,
+static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data,
                                        unsigned int irq)
 {
-       if (combiner_nr >= max_combiner_nr())
-               BUG();
-       if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0)
+       if (irq_set_handler_data(irq, combiner_data) != 0)
                BUG();
        irq_set_chained_handler(irq, combiner_handle_cascade_irq);
 }
 
-static void __init combiner_init_one(unsigned int combiner_nr,
+static void __init combiner_init_one(struct combiner_chip_data *combiner_data,
+                                    unsigned int combiner_nr,
                                     void __iomem *base, unsigned int irq)
 {
-       combiner_data[combiner_nr].base = base;
-       combiner_data[combiner_nr].irq_offset = irq_find_mapping(
-               combiner_irq_domain, combiner_nr * MAX_IRQ_IN_COMBINER);
-       combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3);
-       combiner_data[combiner_nr].parent_irq = irq;
+       combiner_data->base = base;
+       combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER;
+       combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3);
+       combiner_data->parent_irq = irq;
 
        /* Disable all interrupts */
-       __raw_writel(combiner_data[combiner_nr].irq_mask,
-                    base + COMBINER_ENABLE_CLEAR);
+       __raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR);
 }
 
 #ifdef CONFIG_OF
@@ -162,7 +151,7 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
        if (intsize < 2)
                return -EINVAL;
 
-       *out_hwirq = intspec[0] * MAX_IRQ_IN_COMBINER + intspec[1];
+       *out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1];
        *out_type = 0;
 
        return 0;
@@ -181,6 +170,8 @@ static int combiner_irq_domain_xlate(struct irq_domain *d,
 static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq,
                                   irq_hw_number_t hw)
 {
+       struct combiner_chip_data *combiner_data = d->host_data;
+
        irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq);
        irq_set_chip_data(irq, &combiner_data[hw >> 3]);
        set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
@@ -193,8 +184,12 @@ static struct irq_domain_ops combiner_irq_domain_ops = {
        .map    = combiner_irq_domain_map,
 };
 
-static unsigned int exynos4x12_combiner_extra_irq(int group)
+static unsigned int combiner_lookup_irq(int group)
 {
+#ifdef CONFIG_EXYNOS_ATAGS
+       if (group < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
+               return IRQ_SPI(group);
+
        switch (group) {
        case 16:
                return IRQ_SPI(107);
@@ -204,53 +199,46 @@ static unsigned int exynos4x12_combiner_extra_irq(int group)
                return IRQ_SPI(48);
        case 19:
                return IRQ_SPI(42);
-       default:
-               return 0;
        }
+#endif
+       return 0;
 }
 
 void __init combiner_init(void __iomem *combiner_base,
-                         struct device_node *np)
+                         struct device_node *np,
+                         unsigned int max_nr,
+                         int irq_base)
 {
-       int i, irq, irq_base;
-       unsigned int max_nr, nr_irq;
+       int i, irq;
+       unsigned int nr_irq;
+       struct combiner_chip_data *combiner_data;
 
-       max_nr = max_combiner_nr();
+       nr_irq = max_nr * IRQ_IN_COMBINER;
 
-       if (np) {
-               if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
-                       pr_info("%s: number of combiners not specified, "
-                               "setting default as %d.\n",
-                               __func__, max_nr);
-               }
-       }
-
-       nr_irq = max_nr * MAX_IRQ_IN_COMBINER;
-
-       irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0);
-       if (IS_ERR_VALUE(irq_base)) {
-               irq_base = COMBINER_IRQ(0, 0);
-               pr_warning("%s: irq desc alloc failed. Continuing with %d as linux irq base\n", __func__, irq_base);
+       combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL);
+       if (!combiner_data) {
+               pr_warning("%s: could not allocate combiner data\n", __func__);
+               return;
        }
 
-       combiner_irq_domain = irq_domain_add_legacy(np, nr_irq, irq_base, 0,
-                               &combiner_irq_domain_ops, &combiner_data);
+       combiner_irq_domain = irq_domain_add_simple(np, nr_irq, irq_base,
+                               &combiner_irq_domain_ops, combiner_data);
        if (WARN_ON(!combiner_irq_domain)) {
                pr_warning("%s: irq domain init failed\n", __func__);
                return;
        }
 
        for (i = 0; i < max_nr; i++) {
-               if (i < EXYNOS4210_MAX_COMBINER_NR || soc_is_exynos5250())
-                       irq = IRQ_SPI(i);
-               else
-                       irq = exynos4x12_combiner_extra_irq(i);
 #ifdef CONFIG_OF
                if (np)
                        irq = irq_of_parse_and_map(np, i);
+               else
 #endif
-               combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq);
-               combiner_cascade_irq(i, irq);
+                       irq = combiner_lookup_irq(i);
+
+               combiner_init_one(&combiner_data[i], i,
+                                 combiner_base + (i >> 2) * 0x10, irq);
+               combiner_cascade_irq(&combiner_data[i], irq);
        }
 }
 
@@ -259,6 +247,8 @@ static int __init combiner_of_init(struct device_node *np,
                                   struct device_node *parent)
 {
        void __iomem *combiner_base;
+       unsigned int max_nr = 20;
+       int irq_base = -1;
 
        combiner_base = of_iomap(np, 0);
        if (!combiner_base) {
@@ -266,7 +256,20 @@ static int __init combiner_of_init(struct device_node *np,
                return -ENXIO;
        }
 
-       combiner_init(combiner_base, np);
+       if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) {
+               pr_info("%s: number of combiners not specified, "
+                       "setting default as %d.\n",
+                       __func__, max_nr);
+       }
+
+       /* 
+        * FIXME: This is a hardwired COMBINER_IRQ(0,0). Once all devices
+        * get their IRQ from DT, remove this in order to get dynamic
+        * allocation.
+        */
+       irq_base = 160;
+
+       combiner_init(combiner_base, np, max_nr, irq_base);
 
        return 0;
 }