diff options
Diffstat (limited to 'drivers/irqchip/exynos-combiner.c')
| -rw-r--r-- | drivers/irqchip/exynos-combiner.c | 148 |
1 files changed, 72 insertions, 76 deletions
diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 04d86a9803f..f8636a650cf 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -12,12 +12,11 @@ #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> - -#include <plat/cpu.h> #include "irqchip.h" @@ -25,16 +24,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) { @@ -75,11 +76,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)) + handle_bad_irq(irq, desc); else generic_handle_irq(cascade_irq); @@ -87,42 +88,51 @@ static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } -static struct irq_chip combiner_chip = { - .name = "COMBINER", - .irq_mask = combiner_mask_irq, - .irq_unmask = combiner_unmask_irq, -}; - -static void __init combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq) +#ifdef CONFIG_SMP +static int combiner_set_affinity(struct irq_data *d, + const struct cpumask *mask_val, bool force) { - unsigned int max_nr; + struct combiner_chip_data *chip_data = irq_data_get_irq_chip_data(d); + struct irq_chip *chip = irq_get_chip(chip_data->parent_irq); + struct irq_data *data = irq_get_irq_data(chip_data->parent_irq); - if (soc_is_exynos5250()) - max_nr = EXYNOS5_MAX_COMBINER_NR; + if (chip && chip->irq_set_affinity) + return chip->irq_set_affinity(data, mask_val, force); else - max_nr = EXYNOS4_MAX_COMBINER_NR; + return -EINVAL; +} +#endif - if (combiner_nr >= max_nr) - BUG(); - if (irq_set_handler_data(irq, &combiner_data[combiner_nr]) != 0) +static struct irq_chip combiner_chip = { + .name = "COMBINER", + .irq_mask = combiner_mask_irq, + .irq_unmask = combiner_unmask_irq, +#ifdef CONFIG_SMP + .irq_set_affinity = combiner_set_affinity, +#endif +}; + +static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data, + unsigned int irq) +{ + 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, - void __iomem *base) +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->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 static int combiner_irq_domain_xlate(struct irq_domain *d, struct device_node *controller, const u32 *intspec, unsigned int intsize, @@ -135,25 +145,17 @@ 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; } -#else -static int combiner_irq_domain_xlate(struct irq_domain *d, - struct device_node *controller, - const u32 *intspec, unsigned int intsize, - unsigned long *out_hwirq, - unsigned int *out_type) -{ - return -EINVAL; -} -#endif 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); @@ -166,54 +168,43 @@ static struct irq_domain_ops combiner_irq_domain_ops = { .map = combiner_irq_domain_map, }; -void __init combiner_init(void __iomem *combiner_base, - struct device_node *np) +static void __init combiner_init(void __iomem *combiner_base, + struct device_node *np, + unsigned int max_nr) { - int i, irq, irq_base; - unsigned int max_nr, nr_irq; - - if (np) { - if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { - pr_warning("%s: number of combiners not specified, " - "setting default as %d.\n", - __func__, EXYNOS4_MAX_COMBINER_NR); - max_nr = EXYNOS4_MAX_COMBINER_NR; - } - } else { - max_nr = soc_is_exynos5250() ? EXYNOS5_MAX_COMBINER_NR : - EXYNOS4_MAX_COMBINER_NR; - } - nr_irq = max_nr * MAX_IRQ_IN_COMBINER; + int i, irq; + unsigned int nr_irq; + struct combiner_chip_data *combiner_data; - 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); + nr_irq = max_nr * IRQ_IN_COMBINER; + + 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_linear(np, nr_irq, + &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++) { - combiner_init_one(i, combiner_base + (i >> 2) * 0x10); - irq = IRQ_SPI(i); -#ifdef CONFIG_OF - if (np) - irq = irq_of_parse_and_map(np, i); -#endif - combiner_cascade_irq(i, irq); + irq = irq_of_parse_and_map(np, i); + + combiner_init_one(&combiner_data[i], i, + combiner_base + (i >> 2) * 0x10, irq); + combiner_cascade_irq(&combiner_data[i], irq); } } -#ifdef CONFIG_OF 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) { @@ -221,10 +212,15 @@ 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; } IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner", combiner_of_init); -#endif |
