From 6761dcfe8c42b55076753bc8bea7b5dcbfb445c0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 10 Apr 2013 15:17:47 +0200 Subject: irqchip: exynos: pass max combiner number to combiner_init We can find out the number of combined IRQs from the device tree, but in case of ATAGS boot, the driver currently uses hardcoded values based on the SoC type. We can't do that in general for a multiplatform kernel, so let's instead pass this information from platform code directly in case of ATAGS boot. Signed-off-by: Arnd Bergmann Cc: Thomas Gleixner --- drivers/irqchip/exynos-combiner.c | 46 ++++++++++++++------------------------- 1 file changed, 16 insertions(+), 30 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index e8501dbaa0b7..d8683836ee1e 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -25,6 +25,8 @@ #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 { @@ -112,23 +114,9 @@ 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, unsigned int irq) { - if (combiner_nr >= max_combiner_nr()) - BUG(); if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0) BUG(); irq_set_chained_handler(irq, combiner_handle_cascade_irq); @@ -139,7 +127,7 @@ static void __init combiner_init_one(unsigned int combiner_nr, { 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_irq_domain, combiner_nr * IRQ_IN_COMBINER); combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); combiner_data[combiner_nr].parent_irq = irq; @@ -161,7 +149,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; @@ -209,22 +197,13 @@ static unsigned int exynos4x12_combiner_extra_irq(int group) } void __init combiner_init(void __iomem *combiner_base, - struct device_node *np) + struct device_node *np, + unsigned int max_nr) { int i, irq, irq_base; - unsigned int max_nr, nr_irq; + unsigned int nr_irq; - max_nr = max_combiner_nr(); - - 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; + nr_irq = max_nr * IRQ_IN_COMBINER; irq_base = irq_alloc_descs(COMBINER_IRQ(0, 0), 1, nr_irq, 0); if (IS_ERR_VALUE(irq_base)) { @@ -258,6 +237,7 @@ static int __init combiner_of_init(struct device_node *np, struct device_node *parent) { void __iomem *combiner_base; + unsigned int max_nr = 20; combiner_base = of_iomap(np, 0); if (!combiner_base) { @@ -265,7 +245,13 @@ 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); + } + + combiner_init(combiner_base, np, max_nr); return 0; } -- cgit v1.2.3 From d34f03d4a1e4e56f5944186c2e74cbed58b27090 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 10 Apr 2013 15:31:11 +0200 Subject: irqchip: exynos: allocate combiner_data dynamically The number of combiners on a given SoC is a platform specific constant, and we cannot encode this number on a multiplatform kernel since the header file defining it is not available. Allocating the structure dynamically ends up cleaner anyway since we keep all the data local. Signed-off-by: Arnd Bergmann Cc: Thomas Gleixner --- drivers/irqchip/exynos-combiner.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index d8683836ee1e..7fcdeee869ce 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,6 @@ struct combiner_chip_data { }; 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) { @@ -114,26 +114,26 @@ static struct irq_chip combiner_chip = { #endif }; -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 (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_data->base = base; + combiner_data->irq_offset = irq_find_mapping( combiner_irq_domain, combiner_nr * IRQ_IN_COMBINER); - combiner_data[combiner_nr].irq_mask = 0xff << ((combiner_nr % 4) << 3); - combiner_data[combiner_nr].parent_irq = irq; + 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 @@ -168,6 +168,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); @@ -202,6 +204,7 @@ void __init combiner_init(void __iomem *combiner_base, { int i, irq, irq_base; unsigned int nr_irq; + struct combiner_chip_data *combiner_data; nr_irq = max_nr * IRQ_IN_COMBINER; @@ -211,8 +214,14 @@ void __init combiner_init(void __iomem *combiner_base, 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_ops, combiner_data); if (WARN_ON(!combiner_irq_domain)) { pr_warning("%s: irq domain init failed\n", __func__); return; @@ -227,8 +236,9 @@ void __init combiner_init(void __iomem *combiner_base, if (np) irq = irq_of_parse_and_map(np, i); #endif - combiner_init_one(i, combiner_base + (i >> 2) * 0x10, irq); - combiner_cascade_irq(i, irq); + combiner_init_one(&combiner_data[i], i, + combiner_base + (i >> 2) * 0x10, irq); + combiner_cascade_irq(&combiner_data[i], irq); } } -- cgit v1.2.3 From 92c8e4962054a6cf5171b3d7a3a77b799ca62c10 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 10 Apr 2013 15:59:58 +0200 Subject: irqchip: exynos: localize irq lookup for ATAGS The IRQ_SPI() macro is not available in the driver when building with sparse IRQs or multiplatform, so let's move all users of this into one function that we can leave out when building DT-only. Signed-off-by: Arnd Bergmann Cc: Thomas Gleixner --- drivers/irqchip/exynos-combiner.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 7fcdeee869ce..acb9c74b070a 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -18,7 +18,9 @@ #include #include +#ifdef CONFIG_EXYNOS_ATAGS #include +#endif #include "irqchip.h" @@ -182,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); @@ -193,9 +199,9 @@ 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, @@ -228,14 +234,13 @@ void __init combiner_init(void __iomem *combiner_base, } 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 + 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); -- cgit v1.2.3 From 863a08dc8bc7ce32ecc9136671610a93a0dd68b1 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 12 Apr 2013 15:27:09 +0200 Subject: irqchip: exynos: pass irq_base from platform The platform code knows the IRQ base, while the irqchip driver should really not. This is a littly hacky because we still hardwire the IRQ base to 160 for the combiner in the DT case, when we should really use -1. Removing that line will cause a linear IRQ domain to be use, as we should. Signed-off-by: Arnd Bergmann Cc: Thomas Gleixner --- drivers/irqchip/exynos-combiner.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index acb9c74b070a..6855c92c2262 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -206,27 +206,22 @@ static unsigned int combiner_lookup_irq(int group) void __init combiner_init(void __iomem *combiner_base, struct device_node *np, - unsigned int max_nr) + unsigned int max_nr, + int irq_base) { - int i, irq, irq_base; + int i, irq; unsigned int nr_irq; struct combiner_chip_data *combiner_data; nr_irq = max_nr * 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 = 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__); @@ -253,6 +248,7 @@ static int __init combiner_of_init(struct device_node *np, { void __iomem *combiner_base; unsigned int max_nr = 20; + int irq_base = -1; combiner_base = of_iomap(np, 0); if (!combiner_base) { @@ -266,7 +262,14 @@ static int __init combiner_of_init(struct device_node *np, __func__, max_nr); } - combiner_init(combiner_base, np, 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; } -- cgit v1.2.3 From 20adee8fa06ef69012bc277739e9e3762c78b7b7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 18 Apr 2013 23:57:26 +0200 Subject: irqchip: exynos: look up irq using irq_find_mapping Since we want to move to using the linear IRQ domain in the future, we cannot rely on the irq numbers to be contiguous and need to look up the irq from the hwirq using the domain. This also turns the bogus comparison with NR_IRQ into a more meaningful check to see if the number has a valid mapping. Signed-off-by: Arnd Bergmann --- drivers/irqchip/exynos-combiner.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/irqchip') diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 6855c92c2262..494c2e21b538 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -33,7 +33,7 @@ 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; @@ -80,11 +80,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); @@ -129,8 +129,7 @@ static void __init combiner_init_one(struct combiner_chip_data *combiner_data, void __iomem *base, unsigned int irq) { combiner_data->base = base; - combiner_data->irq_offset = irq_find_mapping( - combiner_irq_domain, combiner_nr * IRQ_IN_COMBINER); + combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER; combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3); combiner_data->parent_irq = irq; -- cgit v1.2.3