diff options
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/exynos-combiner.c | 125 |
1 files changed, 64 insertions, 61 deletions
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 02492ab20d22..a9d2b2fa4afd 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -12,13 +12,16 @@ #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" @@ -26,17 +29,18 @@ #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; } |