From 8b53ec260e601d4b433e2e64863ee8e56ff8e312 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 4 Jun 2014 16:01:52 +0100 Subject: [PATCH 01/41] irqchip: nvic: Use the generic noop function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the generic function saves looking up this custom one in a source navigator. Signed-off-by: Daniel Thompson Link: https://lkml.kernel.org/r/1401894112-13386-1-git-send-email-daniel.thompson@linaro.org Cc: Thomas Gleixner Cc: Jason Cooper Acked-by: Uwe Kleine-König Signed-off-by: Jason Cooper --- drivers/irqchip/irq-nvic.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c index 70bdf6edb7bb..4ff0805fca01 100644 --- a/drivers/irqchip/irq-nvic.c +++ b/drivers/irqchip/irq-nvic.c @@ -49,14 +49,6 @@ nvic_handle_irq(irq_hw_number_t hwirq, struct pt_regs *regs) handle_IRQ(irq, regs); } -static void nvic_eoi(struct irq_data *d) -{ - /* - * This is a no-op as end of interrupt is signaled by the exception - * return sequence. - */ -} - static int __init nvic_of_init(struct device_node *node, struct device_node *parent) { @@ -102,7 +94,10 @@ static int __init nvic_of_init(struct device_node *node, gc->chip_types[0].regs.disable = NVIC_ICER; gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; - gc->chip_types[0].chip.irq_eoi = nvic_eoi; + /* This is a no-op as end of interrupt is signaled by the + * exception return sequence. + */ + gc->chip_types[0].chip.irq_eoi = irq_gc_noop; /* disable interrupts */ writel_relaxed(~0, gc->reg_base + NVIC_ICER); From 43a775916d63d1c822107b39987192ca5ced445c Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Mon, 9 Jun 2014 16:20:05 +0800 Subject: [PATCH 02/41] genirq: Export irq_domain_disassociate() to architecture interrupt drivers Export irq_domain_disassociate() to architecture interrupt drivers, so it could be used to handle legacy IRQ descriptors on x86. Signed-off-by: Jiang Liu Cc: Konrad Rzeszutek Wilk Cc: Tony Luck Cc: Joerg Roedel Cc: Paul Gortmaker Cc: Greg Kroah-Hartman Cc: Benjamin Herrenschmidt Cc: Grant Likely Cc: Rafael J. Wysocki Cc: Bjorn Helgaas Cc: Randy Dunlap Cc: Yinghai Lu Link: http://lkml.kernel.org/r/1402302011-23642-37-git-send-email-jiang.liu@linux.intel.com Signed-off-by: Thomas Gleixner --- include/linux/irqdomain.h | 2 ++ kernel/irq/irqdomain.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h index c983ed18c332..b0f9d16e48f6 100644 --- a/include/linux/irqdomain.h +++ b/include/linux/irqdomain.h @@ -172,6 +172,8 @@ extern int irq_domain_associate(struct irq_domain *domain, unsigned int irq, extern void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base, irq_hw_number_t hwirq_base, int count); +extern void irq_domain_disassociate(struct irq_domain *domain, + unsigned int irq); extern unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq); diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c index eb5e10e32e05..6534ff6ce02e 100644 --- a/kernel/irq/irqdomain.c +++ b/kernel/irq/irqdomain.c @@ -231,7 +231,7 @@ void irq_set_default_host(struct irq_domain *domain) } EXPORT_SYMBOL_GPL(irq_set_default_host); -static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq) +void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq) { struct irq_data *irq_data = irq_get_irq_data(irq); irq_hw_number_t hwirq; From a26c06f96eff6cb1834320463b7945b7a4c516ad Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:37 +0000 Subject: [PATCH 03/41] irqchip: spear_shirq: Kill pointless static Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212712.948802939@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 6ce6bd3441bf..93f2196e8a08 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -252,7 +252,7 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, struct device_node *np) { int i, irq_base, hwirq = 0, irq_nr = 0; - static struct irq_domain *shirq_domain; + struct irq_domain *shirq_domain; void __iomem *base; base = of_iomap(np, 0); From 078bc005651cfb134135c5f6eca48a997afb4014 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:38 +0000 Subject: [PATCH 04/41] irqchip: spear_shirq: Move private structs to source No point in having them in a separate header file. Make the init functions static. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.038658058@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 52 +++++++++++++++++++---- include/linux/irqchip/spear-shirq.h | 64 ----------------------------- 2 files changed, 45 insertions(+), 71 deletions(-) delete mode 100644 include/linux/irqchip/spear-shirq.h diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 93f2196e8a08..441e39f08135 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,45 @@ #include "irqchip.h" +/* + * struct shirq_regs: shared irq register configuration + * + * enb_reg: enable register offset + * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt + * status_reg: status register offset + * status_reg_mask: status register valid mask + * clear_reg: clear register offset + * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt + */ +struct shirq_regs { + u32 enb_reg; + u32 reset_to_enb; + u32 status_reg; + u32 clear_reg; + u32 reset_to_clear; +}; + +/* + * struct spear_shirq: shared irq structure + * + * irq: hardware irq number + * irq_base: base irq in linux domain + * irq_nr: no. of shared interrupts in a particular block + * irq_bit_off: starting bit offset in the status register + * invalid_irq: irq group is currently disabled + * base: base address of shared irq register + * regs: register configuration for shared irq block + */ +struct spear_shirq { + u32 irq; + u32 irq_base; + u32 irq_nr; + u32 irq_bit_off; + int invalid_irq; + void __iomem *base; + struct shirq_regs regs; +}; + static DEFINE_SPINLOCK(lock); /* spear300 shared irq registers offsets and masks */ @@ -296,24 +334,24 @@ err_unmap: return -ENXIO; } -int __init spear300_shirq_of_init(struct device_node *np, - struct device_node *parent) +static int __init spear300_shirq_of_init(struct device_node *np, + struct device_node *parent) { return shirq_init(spear300_shirq_blocks, ARRAY_SIZE(spear300_shirq_blocks), np); } IRQCHIP_DECLARE(spear300_shirq, "st,spear300-shirq", spear300_shirq_of_init); -int __init spear310_shirq_of_init(struct device_node *np, - struct device_node *parent) +static int __init spear310_shirq_of_init(struct device_node *np, + struct device_node *parent) { return shirq_init(spear310_shirq_blocks, ARRAY_SIZE(spear310_shirq_blocks), np); } IRQCHIP_DECLARE(spear310_shirq, "st,spear310-shirq", spear310_shirq_of_init); -int __init spear320_shirq_of_init(struct device_node *np, - struct device_node *parent) +static int __init spear320_shirq_of_init(struct device_node *np, + struct device_node *parent) { return shirq_init(spear320_shirq_blocks, ARRAY_SIZE(spear320_shirq_blocks), np); diff --git a/include/linux/irqchip/spear-shirq.h b/include/linux/irqchip/spear-shirq.h deleted file mode 100644 index c8be16d213a3..000000000000 --- a/include/linux/irqchip/spear-shirq.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * SPEAr platform shared irq layer header file - * - * Copyright (C) 2009-2012 ST Microelectronics - * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __SPEAR_SHIRQ_H -#define __SPEAR_SHIRQ_H - -#include -#include - -/* - * struct shirq_regs: shared irq register configuration - * - * enb_reg: enable register offset - * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt - * status_reg: status register offset - * status_reg_mask: status register valid mask - * clear_reg: clear register offset - * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt - */ -struct shirq_regs { - u32 enb_reg; - u32 reset_to_enb; - u32 status_reg; - u32 clear_reg; - u32 reset_to_clear; -}; - -/* - * struct spear_shirq: shared irq structure - * - * irq: hardware irq number - * irq_base: base irq in linux domain - * irq_nr: no. of shared interrupts in a particular block - * irq_bit_off: starting bit offset in the status register - * invalid_irq: irq group is currently disabled - * base: base address of shared irq register - * regs: register configuration for shared irq block - */ -struct spear_shirq { - u32 irq; - u32 irq_base; - u32 irq_nr; - u32 irq_bit_off; - int invalid_irq; - void __iomem *base; - struct shirq_regs regs; -}; - -int __init spear300_shirq_of_init(struct device_node *np, - struct device_node *parent); -int __init spear310_shirq_of_init(struct device_node *np, - struct device_node *parent); -int __init spear320_shirq_of_init(struct device_node *np, - struct device_node *parent); - -#endif /* __SPEAR_SHIRQ_H */ From f37ecbce8bf8867ce19fe9ef09e789002d7aad15 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:39 +0000 Subject: [PATCH 05/41] irqchip: spear_shirq: No point in storing the parent irq The struct member is pointless and a nismomer as well. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.129694036@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 441e39f08135..576968efbc4d 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -47,7 +47,6 @@ struct shirq_regs { /* * struct spear_shirq: shared irq structure * - * irq: hardware irq number * irq_base: base irq in linux domain * irq_nr: no. of shared interrupts in a particular block * irq_bit_off: starting bit offset in the status register @@ -56,7 +55,6 @@ struct shirq_regs { * regs: register configuration for shared irq block */ struct spear_shirq { - u32 irq; u32 irq_base; u32 irq_nr; u32 irq_bit_off; @@ -268,28 +266,29 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) chip->irq_unmask(&desc->irq_data); } -static void __init spear_shirq_register(struct spear_shirq *shirq) +static void __init spear_shirq_register(struct spear_shirq *shirq, + int parent_irq) { int i; if (shirq->invalid_irq) return; - irq_set_chained_handler(shirq->irq, shirq_handler); + irq_set_chained_handler(parent_irq, shirq_handler); + irq_set_handler_data(parent_irq, shirq); + for (i = 0; i < shirq->irq_nr; i++) { irq_set_chip_and_handler(shirq->irq_base + i, &shirq_chip, handle_simple_irq); set_irq_flags(shirq->irq_base + i, IRQF_VALID); irq_set_chip_data(shirq->irq_base + i, shirq); } - - irq_set_handler_data(shirq->irq, shirq); } static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, struct device_node *np) { - int i, irq_base, hwirq = 0, irq_nr = 0; + int i, parent_irq, irq_base, hwirq = 0, irq_nr = 0; struct irq_domain *shirq_domain; void __iomem *base; @@ -319,9 +318,9 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, shirq_blocks[i]->base = base; shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain, hwirq); - shirq_blocks[i]->irq = irq_of_parse_and_map(np, i); - spear_shirq_register(shirq_blocks[i]); + parent_irq = irq_of_parse_and_map(np, i); + spear_shirq_register(shirq_blocks[i], parent_irq); hwirq += shirq_blocks[i]->irq_nr; } From c5d1d857482b080875640bb68bc9d8b65ad29b6f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:39 +0000 Subject: [PATCH 06/41] irqchip: spear_shirq: Namespace cleanup The struct members of the shirq block struct are named to confuse the hell out of the casual reader. Clean it up. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.219411832@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 106 +++++++++++++++++----------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 576968efbc4d..f7c25a77845a 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -47,20 +47,20 @@ struct shirq_regs { /* * struct spear_shirq: shared irq structure * - * irq_base: base irq in linux domain - * irq_nr: no. of shared interrupts in a particular block - * irq_bit_off: starting bit offset in the status register - * invalid_irq: irq group is currently disabled - * base: base address of shared irq register - * regs: register configuration for shared irq block + * base: Base register address + * regs: Register configuration for shared irq block + * virq_base: Base virtual interrupt number + * nr_irqs: Number of interrupts handled by this block + * offset: Bit offset of the first interrupt + * disabled: Group is disabled, but accounted */ struct spear_shirq { - u32 irq_base; - u32 irq_nr; - u32 irq_bit_off; - int invalid_irq; - void __iomem *base; - struct shirq_regs regs; + void __iomem *base; + struct shirq_regs regs; + u32 virq_base; + u32 nr_irqs; + u32 offset; + bool disabled; }; static DEFINE_SPINLOCK(lock); @@ -70,8 +70,8 @@ static DEFINE_SPINLOCK(lock); #define SPEAR300_INT_STS_MASK_REG 0x58 static struct spear_shirq spear300_shirq_ras1 = { - .irq_nr = 9, - .irq_bit_off = 0, + .offset = 0, + .nr_irqs = 9, .regs = { .enb_reg = SPEAR300_INT_ENB_MASK_REG, .status_reg = SPEAR300_INT_STS_MASK_REG, @@ -87,8 +87,8 @@ static struct spear_shirq *spear300_shirq_blocks[] = { #define SPEAR310_INT_STS_MASK_REG 0x04 static struct spear_shirq spear310_shirq_ras1 = { - .irq_nr = 8, - .irq_bit_off = 0, + .offset = 0, + .nr_irqs = 8, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -97,8 +97,8 @@ static struct spear_shirq spear310_shirq_ras1 = { }; static struct spear_shirq spear310_shirq_ras2 = { - .irq_nr = 5, - .irq_bit_off = 8, + .offset = 8, + .nr_irqs = 5, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -107,8 +107,8 @@ static struct spear_shirq spear310_shirq_ras2 = { }; static struct spear_shirq spear310_shirq_ras3 = { - .irq_nr = 1, - .irq_bit_off = 13, + .offset = 13, + .nr_irqs = 1, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -117,8 +117,8 @@ static struct spear_shirq spear310_shirq_ras3 = { }; static struct spear_shirq spear310_shirq_intrcomm_ras = { - .irq_nr = 3, - .irq_bit_off = 14, + .offset = 14, + .nr_irqs = 3, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -139,8 +139,8 @@ static struct spear_shirq *spear310_shirq_blocks[] = { #define SPEAR320_INT_ENB_MASK_REG 0x08 static struct spear_shirq spear320_shirq_ras1 = { - .irq_nr = 3, - .irq_bit_off = 7, + .offset = 7, + .nr_irqs = 3, .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, @@ -150,8 +150,8 @@ static struct spear_shirq spear320_shirq_ras1 = { }; static struct spear_shirq spear320_shirq_ras2 = { - .irq_nr = 1, - .irq_bit_off = 10, + .offset = 10, + .nr_irqs = 1, .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, @@ -161,9 +161,9 @@ static struct spear_shirq spear320_shirq_ras2 = { }; static struct spear_shirq spear320_shirq_ras3 = { - .irq_nr = 7, - .irq_bit_off = 0, - .invalid_irq = 1, + .offset = 0, + .nr_irqs = 7, + .disabled = 1, .regs = { .enb_reg = SPEAR320_INT_ENB_MASK_REG, .reset_to_enb = 1, @@ -174,8 +174,8 @@ static struct spear_shirq spear320_shirq_ras3 = { }; static struct spear_shirq spear320_shirq_intrcomm_ras = { - .irq_nr = 11, - .irq_bit_off = 11, + .offset = 11, + .nr_irqs = 11, .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, @@ -194,7 +194,7 @@ static struct spear_shirq *spear320_shirq_blocks[] = { static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) { struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, offset = d->irq - shirq->irq_base; + u32 val, offset = d->irq - shirq->virq_base; unsigned long flags; if (shirq->regs.enb_reg == -1) @@ -204,9 +204,9 @@ static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) val = readl(shirq->base + shirq->regs.enb_reg); if (mask ^ shirq->regs.reset_to_enb) - val &= ~(0x1 << shirq->irq_bit_off << offset); + val &= ~(0x1 << shirq->offset << offset); else - val |= 0x1 << shirq->irq_bit_off << offset; + val |= 0x1 << shirq->offset << offset; writel(val, shirq->base + shirq->regs.enb_reg); spin_unlock_irqrestore(&lock, flags); @@ -239,17 +239,17 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) chip = irq_get_chip(irq); chip->irq_ack(&desc->irq_data); - mask = ((0x1 << shirq->irq_nr) - 1) << shirq->irq_bit_off; + mask = ((0x1 << shirq->nr_irqs) - 1) << shirq->offset; while ((val = readl(shirq->base + shirq->regs.status_reg) & mask)) { - val >>= shirq->irq_bit_off; - for (i = 0, j = 1; i < shirq->irq_nr; i++, j <<= 1) { + val >>= shirq->offset; + for (i = 0, j = 1; i < shirq->nr_irqs; i++, j <<= 1) { if (!(j & val)) continue; - generic_handle_irq(shirq->irq_base + i); + generic_handle_irq(shirq->virq_base + i); /* clear interrupt */ if (shirq->regs.clear_reg == -1) @@ -257,9 +257,9 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) tmp = readl(shirq->base + shirq->regs.clear_reg); if (shirq->regs.reset_to_clear) - tmp &= ~(j << shirq->irq_bit_off); + tmp &= ~(j << shirq->offset); else - tmp |= (j << shirq->irq_bit_off); + tmp |= (j << shirq->offset); writel(tmp, shirq->base + shirq->regs.clear_reg); } } @@ -271,24 +271,24 @@ static void __init spear_shirq_register(struct spear_shirq *shirq, { int i; - if (shirq->invalid_irq) + if (shirq->disabled) return; irq_set_chained_handler(parent_irq, shirq_handler); irq_set_handler_data(parent_irq, shirq); - for (i = 0; i < shirq->irq_nr; i++) { - irq_set_chip_and_handler(shirq->irq_base + i, + for (i = 0; i < shirq->nr_irqs; i++) { + irq_set_chip_and_handler(shirq->virq_base + i, &shirq_chip, handle_simple_irq); - set_irq_flags(shirq->irq_base + i, IRQF_VALID); - irq_set_chip_data(shirq->irq_base + i, shirq); + set_irq_flags(shirq->virq_base + i, IRQF_VALID); + irq_set_chip_data(shirq->virq_base + i, shirq); } } static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, struct device_node *np) { - int i, parent_irq, irq_base, hwirq = 0, irq_nr = 0; + int i, parent_irq, virq_base, hwirq = 0, nr_irqs = 0; struct irq_domain *shirq_domain; void __iomem *base; @@ -299,15 +299,15 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, } for (i = 0; i < block_nr; i++) - irq_nr += shirq_blocks[i]->irq_nr; + nr_irqs += shirq_blocks[i]->nr_irqs; - irq_base = irq_alloc_descs(-1, 0, irq_nr, 0); - if (IS_ERR_VALUE(irq_base)) { + virq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); + if (IS_ERR_VALUE(virq_base)) { pr_err("%s: irq desc alloc failed\n", __func__); goto err_unmap; } - shirq_domain = irq_domain_add_legacy(np, irq_nr, irq_base, 0, + shirq_domain = irq_domain_add_legacy(np, nr_irqs, virq_base, 0, &irq_domain_simple_ops, NULL); if (WARN_ON(!shirq_domain)) { pr_warn("%s: irq domain init failed\n", __func__); @@ -316,18 +316,18 @@ static int __init shirq_init(struct spear_shirq **shirq_blocks, int block_nr, for (i = 0; i < block_nr; i++) { shirq_blocks[i]->base = base; - shirq_blocks[i]->irq_base = irq_find_mapping(shirq_domain, + shirq_blocks[i]->virq_base = irq_find_mapping(shirq_domain, hwirq); parent_irq = irq_of_parse_and_map(np, i); spear_shirq_register(shirq_blocks[i], parent_irq); - hwirq += shirq_blocks[i]->irq_nr; + hwirq += shirq_blocks[i]->nr_irqs; } return 0; err_free_desc: - irq_free_descs(irq_base, irq_nr); + irq_free_descs(virq_base, nr_irqs); err_unmap: iounmap(base); return -ENXIO; From 03319a1a2966ec39be79182d6d529221c38fde72 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:40 +0000 Subject: [PATCH 07/41] irqchip: spear_shirq: Reorder the spear320 ras blocks Order the ras blocks in the order of interrupts not alphabetically. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.310591579@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index f7c25a77845a..7ebb1a2fbfc7 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -138,6 +138,19 @@ static struct spear_shirq *spear310_shirq_blocks[] = { #define SPEAR320_INT_CLR_MASK_REG 0x04 #define SPEAR320_INT_ENB_MASK_REG 0x08 +static struct spear_shirq spear320_shirq_ras3 = { + .offset = 0, + .nr_irqs = 7, + .disabled = 1, + .regs = { + .enb_reg = SPEAR320_INT_ENB_MASK_REG, + .reset_to_enb = 1, + .status_reg = SPEAR320_INT_STS_MASK_REG, + .clear_reg = SPEAR320_INT_CLR_MASK_REG, + .reset_to_clear = 1, + }, +}; + static struct spear_shirq spear320_shirq_ras1 = { .offset = 7, .nr_irqs = 3, @@ -160,19 +173,6 @@ static struct spear_shirq spear320_shirq_ras2 = { }, }; -static struct spear_shirq spear320_shirq_ras3 = { - .offset = 0, - .nr_irqs = 7, - .disabled = 1, - .regs = { - .enb_reg = SPEAR320_INT_ENB_MASK_REG, - .reset_to_enb = 1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, -}; - static struct spear_shirq spear320_shirq_intrcomm_ras = { .offset = 11, .nr_irqs = 11, From e3c871ab232ccc5fd82f76b21b9cae0113f01dc0 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:40 +0000 Subject: [PATCH 08/41] irqchip: spear_shirq: Use the proper interfaces No point in doing a full irq lookup, when the desc pointer is available. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.404243909@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 7ebb1a2fbfc7..874950c014be 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -232,12 +232,12 @@ static struct irq_chip shirq_chip = { static void shirq_handler(unsigned irq, struct irq_desc *desc) { - u32 i, j, val, mask, tmp; - struct irq_chip *chip; struct spear_shirq *shirq = irq_get_handler_data(irq); + struct irq_data *idata = irq_desc_get_irq_data(desc); + struct irq_chip *chip = irq_data_get_irq_chip(idata); + u32 i, j, val, mask, tmp; - chip = irq_get_chip(irq); - chip->irq_ack(&desc->irq_data); + chip->irq_ack(idata); mask = ((0x1 << shirq->nr_irqs) - 1) << shirq->offset; while ((val = readl(shirq->base + shirq->regs.status_reg) & @@ -263,7 +263,7 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) writel(tmp, shirq->base + shirq->regs.clear_reg); } } - chip->irq_unmask(&desc->irq_data); + chip->irq_unmask(idata); } static void __init spear_shirq_register(struct spear_shirq *shirq, From 4ecc832f4ef25dcb684ca986de3612e881748c0e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:41 +0000 Subject: [PATCH 09/41] irqchip: spear_shirq: Precalculate status mask Calculate the status mask at compile time, not at runtime. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.496614337@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 874950c014be..fc57c35a20b4 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -49,6 +49,7 @@ struct shirq_regs { * * base: Base register address * regs: Register configuration for shared irq block + * mask: Mask to apply to the status register * virq_base: Base virtual interrupt number * nr_irqs: Number of interrupts handled by this block * offset: Bit offset of the first interrupt @@ -57,6 +58,7 @@ struct shirq_regs { struct spear_shirq { void __iomem *base; struct shirq_regs regs; + u32 mask; u32 virq_base; u32 nr_irqs; u32 offset; @@ -72,6 +74,7 @@ static DEFINE_SPINLOCK(lock); static struct spear_shirq spear300_shirq_ras1 = { .offset = 0, .nr_irqs = 9, + .mask = ((0x1 << 9) - 1) << 0, .regs = { .enb_reg = SPEAR300_INT_ENB_MASK_REG, .status_reg = SPEAR300_INT_STS_MASK_REG, @@ -89,6 +92,7 @@ static struct spear_shirq *spear300_shirq_blocks[] = { static struct spear_shirq spear310_shirq_ras1 = { .offset = 0, .nr_irqs = 8, + .mask = ((0x1 << 8) - 1) << 0, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -99,6 +103,7 @@ static struct spear_shirq spear310_shirq_ras1 = { static struct spear_shirq spear310_shirq_ras2 = { .offset = 8, .nr_irqs = 5, + .mask = ((0x1 << 5) - 1) << 8, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -109,6 +114,7 @@ static struct spear_shirq spear310_shirq_ras2 = { static struct spear_shirq spear310_shirq_ras3 = { .offset = 13, .nr_irqs = 1, + .mask = ((0x1 << 1) - 1) << 13, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -119,6 +125,7 @@ static struct spear_shirq spear310_shirq_ras3 = { static struct spear_shirq spear310_shirq_intrcomm_ras = { .offset = 14, .nr_irqs = 3, + .mask = ((0x1 << 3) - 1) << 14, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -141,6 +148,7 @@ static struct spear_shirq *spear310_shirq_blocks[] = { static struct spear_shirq spear320_shirq_ras3 = { .offset = 0, .nr_irqs = 7, + .mask = ((0x1 << 7) - 1) << 0, .disabled = 1, .regs = { .enb_reg = SPEAR320_INT_ENB_MASK_REG, @@ -154,6 +162,7 @@ static struct spear_shirq spear320_shirq_ras3 = { static struct spear_shirq spear320_shirq_ras1 = { .offset = 7, .nr_irqs = 3, + .mask = ((0x1 << 3) - 1) << 7, .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, @@ -165,6 +174,7 @@ static struct spear_shirq spear320_shirq_ras1 = { static struct spear_shirq spear320_shirq_ras2 = { .offset = 10, .nr_irqs = 1, + .mask = ((0x1 << 1) - 1) << 10, .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, @@ -176,6 +186,7 @@ static struct spear_shirq spear320_shirq_ras2 = { static struct spear_shirq spear320_shirq_intrcomm_ras = { .offset = 11, .nr_irqs = 11, + .mask = ((0x1 << 11) - 1) << 11, .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, @@ -239,7 +250,7 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) chip->irq_ack(idata); - mask = ((0x1 << shirq->nr_irqs) - 1) << shirq->offset; + mask = shirq->mask; while ((val = readl(shirq->base + shirq->regs.status_reg) & mask)) { From 97dcc21bd3dc7f04a48ff37700ae838feb35fca4 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:41 +0000 Subject: [PATCH 10/41] irqchip: spear_shirq: Kill the clear_reg nonsense None of the chips has a ACK register. The code brainlessly fiddles with the enable register, so it might even reenable a disabled interrupt at least on spear300. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.570396433@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index fc57c35a20b4..2a33129c4f4b 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -33,15 +33,11 @@ * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt * status_reg: status register offset * status_reg_mask: status register valid mask - * clear_reg: clear register offset - * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt */ struct shirq_regs { u32 enb_reg; u32 reset_to_enb; u32 status_reg; - u32 clear_reg; - u32 reset_to_clear; }; /* @@ -78,7 +74,6 @@ static struct spear_shirq spear300_shirq_ras1 = { .regs = { .enb_reg = SPEAR300_INT_ENB_MASK_REG, .status_reg = SPEAR300_INT_STS_MASK_REG, - .clear_reg = -1, }, }; @@ -96,7 +91,6 @@ static struct spear_shirq spear310_shirq_ras1 = { .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, }, }; @@ -107,7 +101,6 @@ static struct spear_shirq spear310_shirq_ras2 = { .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, }, }; @@ -118,7 +111,6 @@ static struct spear_shirq spear310_shirq_ras3 = { .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, }, }; @@ -129,7 +121,6 @@ static struct spear_shirq spear310_shirq_intrcomm_ras = { .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, - .clear_reg = -1, }, }; @@ -150,13 +141,6 @@ static struct spear_shirq spear320_shirq_ras3 = { .nr_irqs = 7, .mask = ((0x1 << 7) - 1) << 0, .disabled = 1, - .regs = { - .enb_reg = SPEAR320_INT_ENB_MASK_REG, - .reset_to_enb = 1, - .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, - }, }; static struct spear_shirq spear320_shirq_ras1 = { @@ -166,8 +150,6 @@ static struct spear_shirq spear320_shirq_ras1 = { .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, }, }; @@ -178,8 +160,6 @@ static struct spear_shirq spear320_shirq_ras2 = { .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, }, }; @@ -190,8 +170,6 @@ static struct spear_shirq spear320_shirq_intrcomm_ras = { .regs = { .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, - .clear_reg = SPEAR320_INT_CLR_MASK_REG, - .reset_to_clear = 1, }, }; @@ -246,7 +224,7 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) struct spear_shirq *shirq = irq_get_handler_data(irq); struct irq_data *idata = irq_desc_get_irq_data(desc); struct irq_chip *chip = irq_data_get_irq_chip(idata); - u32 i, j, val, mask, tmp; + u32 i, j, val, mask; chip->irq_ack(idata); @@ -261,17 +239,6 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) continue; generic_handle_irq(shirq->virq_base + i); - - /* clear interrupt */ - if (shirq->regs.clear_reg == -1) - continue; - - tmp = readl(shirq->base + shirq->regs.clear_reg); - if (shirq->regs.reset_to_clear) - tmp &= ~(j << shirq->offset); - else - tmp |= (j << shirq->offset); - writel(tmp, shirq->base + shirq->regs.clear_reg); } } chip->irq_unmask(idata); From 25dc49e3321a3b3f17b3f78297432073bb14ec0b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:42 +0000 Subject: [PATCH 11/41] irqchip: spear_shirq: Simplify chained handler I don't know if there are less efficient ways to code that. Get rid of the loop mess and use efficient code. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.662897061@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 2a33129c4f4b..8521a7295b02 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -224,23 +224,20 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) struct spear_shirq *shirq = irq_get_handler_data(irq); struct irq_data *idata = irq_desc_get_irq_data(desc); struct irq_chip *chip = irq_data_get_irq_chip(idata); - u32 i, j, val, mask; + u32 pend; chip->irq_ack(idata); - mask = shirq->mask; - while ((val = readl(shirq->base + shirq->regs.status_reg) & - mask)) { + pend = readl(shirq->base + shirq->regs.status_reg) & shirq->mask; + pend >>= shirq->offset; - val >>= shirq->offset; - for (i = 0, j = 1; i < shirq->nr_irqs; i++, j <<= 1) { + while (pend) { + int irq = __ffs(pend); - if (!(j & val)) - continue; - - generic_handle_irq(shirq->virq_base + i); - } + pend &= ~(0x1 << irq); + generic_handle_irq(shirq->virq_base + irq); } + chip->irq_unmask(idata); } From fe64ac89cf8f5067cf7f6757b6cd9fd77c527bdf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:43 +0000 Subject: [PATCH 12/41] irqchip: spear_shirq: Remove the parent irq "ack"/unmask "ack" is actually a mask in the parent irq. The demultiplexer and the handlers run with interrupts disabled. No point in masking and unmasking the parent. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.754300980@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 8521a7295b02..8765aa7f7a15 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -222,12 +222,8 @@ static struct irq_chip shirq_chip = { static void shirq_handler(unsigned irq, struct irq_desc *desc) { struct spear_shirq *shirq = irq_get_handler_data(irq); - struct irq_data *idata = irq_desc_get_irq_data(desc); - struct irq_chip *chip = irq_data_get_irq_chip(idata); u32 pend; - chip->irq_ack(idata); - pend = readl(shirq->base + shirq->regs.status_reg) & shirq->mask; pend >>= shirq->offset; @@ -237,8 +233,6 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) pend &= ~(0x1 << irq); generic_handle_irq(shirq->virq_base + irq); } - - chip->irq_unmask(idata); } static void __init spear_shirq_register(struct spear_shirq *shirq, From f07e42f96f06dca3f9f897b956d83aec165ee693 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:43 +0000 Subject: [PATCH 13/41] irqchip: spear_shirq: Use proper irq chips for the different SoCs Only spear300 has an actual mask register for the RAS interrupts. Add an irq chip pointer to the shirq struct and initialize spear300 with the actual implementation and the others with dummy_irq_chip. The disabled RAS3 block has no irq chip assigned, so we can check for this and remove the disabled member. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.831341023@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 97 ++++++++++++++++------------------- 1 file changed, 45 insertions(+), 52 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 8765aa7f7a15..169ef9a385d0 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -49,7 +49,8 @@ struct shirq_regs { * virq_base: Base virtual interrupt number * nr_irqs: Number of interrupts handled by this block * offset: Bit offset of the first interrupt - * disabled: Group is disabled, but accounted + * irq_chip: Interrupt controller chip used for this instance, + * if NULL group is disabled, but accounted */ struct spear_shirq { void __iomem *base; @@ -58,19 +59,50 @@ struct spear_shirq { u32 virq_base; u32 nr_irqs; u32 offset; - bool disabled; + struct irq_chip *irq_chip; }; -static DEFINE_SPINLOCK(lock); - /* spear300 shared irq registers offsets and masks */ #define SPEAR300_INT_ENB_MASK_REG 0x54 #define SPEAR300_INT_STS_MASK_REG 0x58 +static DEFINE_RAW_SPINLOCK(shirq_lock); + +static void shirq_irq_mask(struct irq_data *d) +{ + struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); + u32 val, shift = d->irq - shirq->virq_base + shirq->offset; + u32 __iomem *reg = shirq->base + shirq->regs.enb_reg; + + raw_spin_lock(&shirq_lock); + val = readl(reg) & ~(0x1 << shift); + writel(val, reg); + raw_spin_unlock(&shirq_lock); +} + +static void shirq_irq_unmask(struct irq_data *d) +{ + struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); + u32 val, shift = d->irq - shirq->virq_base + shirq->offset; + u32 __iomem *reg = shirq->base + shirq->regs.enb_reg; + + raw_spin_lock(&shirq_lock); + val = readl(reg) | (0x1 << shift); + writel(val, reg); + raw_spin_unlock(&shirq_lock); +} + +static struct irq_chip shirq_chip = { + .name = "spear-shirq", + .irq_mask = shirq_irq_mask, + .irq_unmask = shirq_irq_unmask, +}; + static struct spear_shirq spear300_shirq_ras1 = { .offset = 0, .nr_irqs = 9, .mask = ((0x1 << 9) - 1) << 0, + .irq_chip = &shirq_chip, .regs = { .enb_reg = SPEAR300_INT_ENB_MASK_REG, .status_reg = SPEAR300_INT_STS_MASK_REG, @@ -88,8 +120,8 @@ static struct spear_shirq spear310_shirq_ras1 = { .offset = 0, .nr_irqs = 8, .mask = ((0x1 << 8) - 1) << 0, + .irq_chip = &dummy_irq_chip, .regs = { - .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, }, }; @@ -98,6 +130,7 @@ static struct spear_shirq spear310_shirq_ras2 = { .offset = 8, .nr_irqs = 5, .mask = ((0x1 << 5) - 1) << 8, + .irq_chip = &dummy_irq_chip, .regs = { .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, @@ -108,8 +141,8 @@ static struct spear_shirq spear310_shirq_ras3 = { .offset = 13, .nr_irqs = 1, .mask = ((0x1 << 1) - 1) << 13, + .irq_chip = &dummy_irq_chip, .regs = { - .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, }, }; @@ -118,8 +151,8 @@ static struct spear_shirq spear310_shirq_intrcomm_ras = { .offset = 14, .nr_irqs = 3, .mask = ((0x1 << 3) - 1) << 14, + .irq_chip = &dummy_irq_chip, .regs = { - .enb_reg = -1, .status_reg = SPEAR310_INT_STS_MASK_REG, }, }; @@ -140,15 +173,14 @@ static struct spear_shirq spear320_shirq_ras3 = { .offset = 0, .nr_irqs = 7, .mask = ((0x1 << 7) - 1) << 0, - .disabled = 1, }; static struct spear_shirq spear320_shirq_ras1 = { .offset = 7, .nr_irqs = 3, .mask = ((0x1 << 3) - 1) << 7, + .irq_chip = &dummy_irq_chip, .regs = { - .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, }, }; @@ -157,8 +189,8 @@ static struct spear_shirq spear320_shirq_ras2 = { .offset = 10, .nr_irqs = 1, .mask = ((0x1 << 1) - 1) << 10, + .irq_chip = &dummy_irq_chip, .regs = { - .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, }, }; @@ -167,8 +199,8 @@ static struct spear_shirq spear320_shirq_intrcomm_ras = { .offset = 11, .nr_irqs = 11, .mask = ((0x1 << 11) - 1) << 11, + .irq_chip = &dummy_irq_chip, .regs = { - .enb_reg = -1, .status_reg = SPEAR320_INT_STS_MASK_REG, }, }; @@ -180,45 +212,6 @@ static struct spear_shirq *spear320_shirq_blocks[] = { &spear320_shirq_intrcomm_ras, }; -static void shirq_irq_mask_unmask(struct irq_data *d, bool mask) -{ - struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); - u32 val, offset = d->irq - shirq->virq_base; - unsigned long flags; - - if (shirq->regs.enb_reg == -1) - return; - - spin_lock_irqsave(&lock, flags); - val = readl(shirq->base + shirq->regs.enb_reg); - - if (mask ^ shirq->regs.reset_to_enb) - val &= ~(0x1 << shirq->offset << offset); - else - val |= 0x1 << shirq->offset << offset; - - writel(val, shirq->base + shirq->regs.enb_reg); - spin_unlock_irqrestore(&lock, flags); - -} - -static void shirq_irq_mask(struct irq_data *d) -{ - shirq_irq_mask_unmask(d, 1); -} - -static void shirq_irq_unmask(struct irq_data *d) -{ - shirq_irq_mask_unmask(d, 0); -} - -static struct irq_chip shirq_chip = { - .name = "spear-shirq", - .irq_ack = shirq_irq_mask, - .irq_mask = shirq_irq_mask, - .irq_unmask = shirq_irq_unmask, -}; - static void shirq_handler(unsigned irq, struct irq_desc *desc) { struct spear_shirq *shirq = irq_get_handler_data(irq); @@ -240,7 +233,7 @@ static void __init spear_shirq_register(struct spear_shirq *shirq, { int i; - if (shirq->disabled) + if (!shirq->irq_chip) return; irq_set_chained_handler(parent_irq, shirq_handler); @@ -248,7 +241,7 @@ static void __init spear_shirq_register(struct spear_shirq *shirq, for (i = 0; i < shirq->nr_irqs; i++) { irq_set_chip_and_handler(shirq->virq_base + i, - &shirq_chip, handle_simple_irq); + shirq->irq_chip, handle_simple_irq); set_irq_flags(shirq->virq_base + i, IRQF_VALID); irq_set_chip_data(shirq->virq_base + i, shirq); } From 1b0a76c146adce782ddb8e71f01729f5f3671c66 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 19 Jun 2014 21:34:44 +0000 Subject: [PATCH 14/41] irqchip: spear_shirq: Simplify register access code The extra register data structure is pointless. Move the offsets of the status and the mask register into the shirq block structure. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20140619212713.923306179@linutronix.de Acked-by: Viresh Kumar Signed-off-by: Jason Cooper --- drivers/irqchip/spear-shirq.c | 61 +++++++++-------------------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/drivers/irqchip/spear-shirq.c b/drivers/irqchip/spear-shirq.c index 169ef9a385d0..9c145a7cb056 100644 --- a/drivers/irqchip/spear-shirq.c +++ b/drivers/irqchip/spear-shirq.c @@ -26,25 +26,12 @@ #include "irqchip.h" -/* - * struct shirq_regs: shared irq register configuration - * - * enb_reg: enable register offset - * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt - * status_reg: status register offset - * status_reg_mask: status register valid mask - */ -struct shirq_regs { - u32 enb_reg; - u32 reset_to_enb; - u32 status_reg; -}; - /* * struct spear_shirq: shared irq structure * * base: Base register address - * regs: Register configuration for shared irq block + * status_reg: Status register offset for chained interrupt handler + * mask_reg: Mask register offset for irq chip * mask: Mask to apply to the status register * virq_base: Base virtual interrupt number * nr_irqs: Number of interrupts handled by this block @@ -54,7 +41,8 @@ struct shirq_regs { */ struct spear_shirq { void __iomem *base; - struct shirq_regs regs; + u32 status_reg; + u32 mask_reg; u32 mask; u32 virq_base; u32 nr_irqs; @@ -72,7 +60,7 @@ static void shirq_irq_mask(struct irq_data *d) { struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); u32 val, shift = d->irq - shirq->virq_base + shirq->offset; - u32 __iomem *reg = shirq->base + shirq->regs.enb_reg; + u32 __iomem *reg = shirq->base + shirq->mask_reg; raw_spin_lock(&shirq_lock); val = readl(reg) & ~(0x1 << shift); @@ -84,7 +72,7 @@ static void shirq_irq_unmask(struct irq_data *d) { struct spear_shirq *shirq = irq_data_get_irq_chip_data(d); u32 val, shift = d->irq - shirq->virq_base + shirq->offset; - u32 __iomem *reg = shirq->base + shirq->regs.enb_reg; + u32 __iomem *reg = shirq->base + shirq->mask_reg; raw_spin_lock(&shirq_lock); val = readl(reg) | (0x1 << shift); @@ -103,10 +91,8 @@ static struct spear_shirq spear300_shirq_ras1 = { .nr_irqs = 9, .mask = ((0x1 << 9) - 1) << 0, .irq_chip = &shirq_chip, - .regs = { - .enb_reg = SPEAR300_INT_ENB_MASK_REG, - .status_reg = SPEAR300_INT_STS_MASK_REG, - }, + .status_reg = SPEAR300_INT_STS_MASK_REG, + .mask_reg = SPEAR300_INT_ENB_MASK_REG, }; static struct spear_shirq *spear300_shirq_blocks[] = { @@ -121,9 +107,7 @@ static struct spear_shirq spear310_shirq_ras1 = { .nr_irqs = 8, .mask = ((0x1 << 8) - 1) << 0, .irq_chip = &dummy_irq_chip, - .regs = { - .status_reg = SPEAR310_INT_STS_MASK_REG, - }, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq spear310_shirq_ras2 = { @@ -131,10 +115,7 @@ static struct spear_shirq spear310_shirq_ras2 = { .nr_irqs = 5, .mask = ((0x1 << 5) - 1) << 8, .irq_chip = &dummy_irq_chip, - .regs = { - .enb_reg = -1, - .status_reg = SPEAR310_INT_STS_MASK_REG, - }, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq spear310_shirq_ras3 = { @@ -142,9 +123,7 @@ static struct spear_shirq spear310_shirq_ras3 = { .nr_irqs = 1, .mask = ((0x1 << 1) - 1) << 13, .irq_chip = &dummy_irq_chip, - .regs = { - .status_reg = SPEAR310_INT_STS_MASK_REG, - }, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq spear310_shirq_intrcomm_ras = { @@ -152,9 +131,7 @@ static struct spear_shirq spear310_shirq_intrcomm_ras = { .nr_irqs = 3, .mask = ((0x1 << 3) - 1) << 14, .irq_chip = &dummy_irq_chip, - .regs = { - .status_reg = SPEAR310_INT_STS_MASK_REG, - }, + .status_reg = SPEAR310_INT_STS_MASK_REG, }; static struct spear_shirq *spear310_shirq_blocks[] = { @@ -180,9 +157,7 @@ static struct spear_shirq spear320_shirq_ras1 = { .nr_irqs = 3, .mask = ((0x1 << 3) - 1) << 7, .irq_chip = &dummy_irq_chip, - .regs = { - .status_reg = SPEAR320_INT_STS_MASK_REG, - }, + .status_reg = SPEAR320_INT_STS_MASK_REG, }; static struct spear_shirq spear320_shirq_ras2 = { @@ -190,9 +165,7 @@ static struct spear_shirq spear320_shirq_ras2 = { .nr_irqs = 1, .mask = ((0x1 << 1) - 1) << 10, .irq_chip = &dummy_irq_chip, - .regs = { - .status_reg = SPEAR320_INT_STS_MASK_REG, - }, + .status_reg = SPEAR320_INT_STS_MASK_REG, }; static struct spear_shirq spear320_shirq_intrcomm_ras = { @@ -200,9 +173,7 @@ static struct spear_shirq spear320_shirq_intrcomm_ras = { .nr_irqs = 11, .mask = ((0x1 << 11) - 1) << 11, .irq_chip = &dummy_irq_chip, - .regs = { - .status_reg = SPEAR320_INT_STS_MASK_REG, - }, + .status_reg = SPEAR320_INT_STS_MASK_REG, }; static struct spear_shirq *spear320_shirq_blocks[] = { @@ -217,7 +188,7 @@ static void shirq_handler(unsigned irq, struct irq_desc *desc) struct spear_shirq *shirq = irq_get_handler_data(irq); u32 pend; - pend = readl(shirq->base + shirq->regs.status_reg) & shirq->mask; + pend = readl(shirq->base + shirq->status_reg) & shirq->mask; pend >>= shirq->offset; while (pend) { From 1d50d2ce6337724d30055bdbe082236cc86e6785 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:19 +0530 Subject: [PATCH 15/41] irqchip: crossbar: Dont use '0' to mark reserved interrupts Today '0' is actually reserved, but may not be the same in the future. So, use a flag to mark the GIC interrupts that are reserved. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-2-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 3d15d16a7088..20105bcdf408 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -17,6 +17,7 @@ #include #define IRQ_FREE -1 +#define IRQ_RESERVED -2 #define GIC_IRQ_START 32 /* @@ -139,7 +140,7 @@ static int __init crossbar_of_init(struct device_node *node) pr_err("Invalid reserved entry\n"); goto err3; } - cb->irq_map[entry] = 0; + cb->irq_map[entry] = IRQ_RESERVED; } } @@ -170,7 +171,7 @@ static int __init crossbar_of_init(struct device_node *node) * reserved irqs. so find and store the offsets once. */ for (i = 0; i < max; i++) { - if (!cb->irq_map[i]) + if (cb->irq_map[i] == IRQ_RESERVED) continue; cb->register_offsets[i] = reserved; From 6f16fc878a51572a998655e5ef1c396cb269648d Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:20 +0530 Subject: [PATCH 16/41] irqchip: crossbar: Check for premapped crossbar before allocating If irq_of_parse_and_map is executed twice, the same crossbar is mapped to two different GIC interrupts. This is completely undesirable. Instead, check if the requested crossbar event is pre-allocated and provide that GIC mapping back to caller if already allocated. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-3-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 20105bcdf408..51d4b878e5d6 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -51,6 +51,17 @@ static inline void crossbar_writeb(int irq_no, int cb_no) writeb(cb_no, cb->crossbar_base + cb->register_offsets[irq_no]); } +static inline int get_prev_map_irq(int cb_no) +{ + int i; + + for (i = 0; i < cb->int_max; i++) + if (cb->irq_map[i] == cb_no) + return i; + + return -ENODEV; +} + static inline int allocate_free_irq(int cb_no) { int i; @@ -88,11 +99,16 @@ static int crossbar_domain_xlate(struct irq_domain *d, { unsigned long ret; + ret = get_prev_map_irq(intspec[1]); + if (!IS_ERR_VALUE(ret)) + goto found; + ret = allocate_free_irq(intspec[1]); if (IS_ERR_VALUE(ret)) return ret; +found: *out_hwirq = ret + GIC_IRQ_START; return 0; } From 64e0f8ba5cae74471f72e0cb218c67915e365f47 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:21 +0530 Subject: [PATCH 17/41] irqchip: crossbar: Introduce ti, irqs-skip to skip irqs that bypass crossbar When, in the system due to varied reasons, interrupts might be unusable due to hardware behavior, but register maps do exist, then those interrupts should be skipped while mapping irq to crossbars. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-4-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 6 ++++++ drivers/irqchip/irq-crossbar.c | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index fb88585cfb93..079576573ec0 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -17,6 +17,11 @@ Required properties: so crossbar bar driver should not consider them as free lines. +Optional properties: +- ti,irqs-skip: This is similar to "ti,irqs-reserved", but these are for + SOC-specific hard-wiring of those irqs which unexpectedly bypasses the + crossbar. These irqs have a crossbar register, but still cannot be used. + Examples: crossbar_mpu: @4a020000 { compatible = "ti,irq-crossbar"; @@ -24,4 +29,5 @@ Examples: ti,max-irqs = <160>; ti,reg-size = <2>; ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; + ti,irqs-skip = <10 133 139 140>; }; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 51d4b878e5d6..0533a71fa86f 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -18,6 +18,7 @@ #define IRQ_FREE -1 #define IRQ_RESERVED -2 +#define IRQ_SKIP -3 #define GIC_IRQ_START 32 /* @@ -160,6 +161,25 @@ static int __init crossbar_of_init(struct device_node *node) } } + /* Skip irqs hardwired to bypass the crossbar */ + irqsr = of_get_property(node, "ti,irqs-skip", &size); + if (irqsr) { + size /= sizeof(__be32); + + for (i = 0; i < size; i++) { + of_property_read_u32_index(node, + "ti,irqs-skip", + i, &entry); + if (entry > max) { + pr_err("Invalid skip entry\n"); + ret = -EINVAL; + goto err3; + } + cb->irq_map[entry] = IRQ_SKIP; + } + } + + cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); if (!cb->register_offsets) goto err3; From a35057d1dcb11ae67c9347ef7987cf65ac743c36 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:22 +0530 Subject: [PATCH 18/41] irqchip: crossbar: Initialise the crossbar with a safe value Since crossbar is s/w configurable, the initial settings of the crossbar cannot be assumed to be sane. This implies that: a) On initialization all un-reserved crossbars must be initialized to a known 'safe' value. b) When unmapping the interrupt, the safe value must be written to ensure that the crossbar mapping matches with interrupt controller usage. So provide a safe value in the dt data to map if '0' is not safe for the platform and use it during init and unmap While at this, fix the below checkpatch warning. Fixes checkpatch warning: WARNING: Unnecessary space before function pointer arguments #37: FILE: drivers/irqchip/irq-crossbar.c:37: + void (*write) (int, int); Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-5-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 3 +++ drivers/irqchip/irq-crossbar.c | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index 079576573ec0..5f45c78e31a9 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -22,6 +22,9 @@ Optional properties: SOC-specific hard-wiring of those irqs which unexpectedly bypasses the crossbar. These irqs have a crossbar register, but still cannot be used. +- ti,irqs-safe-map: integer which maps to a safe configuration to use + when the interrupt controller irq is unused (when not provided, default is 0) + Examples: crossbar_mpu: @4a020000 { compatible = "ti,irq-crossbar"; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 0533a71fa86f..4be30c00f041 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -23,16 +23,18 @@ /* * @int_max: maximum number of supported interrupts + * @safe_map: safe default value to initialize the crossbar * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number */ struct crossbar_device { uint int_max; + uint safe_map; uint *irq_map; void __iomem *crossbar_base; int *register_offsets; - void (*write) (int, int); + void (*write)(int, int); }; static struct crossbar_device *cb; @@ -88,8 +90,10 @@ static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; - if (hw > GIC_IRQ_START) + if (hw > GIC_IRQ_START) { cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; + cb->write(hw - GIC_IRQ_START, cb->safe_map); + } } static int crossbar_domain_xlate(struct irq_domain *d, @@ -214,6 +218,17 @@ static int __init crossbar_of_init(struct device_node *node) reserved += size; } + of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); + + /* Initialize the crossbar with safe map to start with */ + for (i = 0; i < max; i++) { + if (cb->irq_map[i] == IRQ_RESERVED || + cb->irq_map[i] == IRQ_SKIP) + continue; + + cb->write(i, cb->safe_map); + } + register_routable_domain_ops(&routable_irq_domain_ops); return 0; From ddee0fb46d26174e71ee1df225b9f9feaff05e10 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:23 +0530 Subject: [PATCH 19/41] irqchip: crossbar: Change allocation logic by reversing search for free irqs Reverse the search algorithm to ensure that address mapping and IRQ allocation logics are proper. This makes the below bugs visible sooner. class 1. address space errors -> example: reg = ti,max-irqs = is a wrong parameter class 2: irq-reserved list - which decides which entries in the address space is not actually wired in class 3: wrong list of routable-irqs. In general allocating from max to min tends to have benefits in ensuring the different issues that may be present in dts is easily caught at definition time, rather than at a later point in time. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-6-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 4be30c00f041..a39cb316d2b0 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -58,7 +58,7 @@ static inline int get_prev_map_irq(int cb_no) { int i; - for (i = 0; i < cb->int_max; i++) + for (i = cb->int_max - 1; i >= 0; i--) if (cb->irq_map[i] == cb_no) return i; @@ -69,7 +69,7 @@ static inline int allocate_free_irq(int cb_no) { int i; - for (i = 0; i < cb->int_max; i++) { + for (i = cb->int_max - 1; i >= 0; i--) { if (cb->irq_map[i] == IRQ_FREE) { cb->irq_map[i] = cb_no; return i; From d4922a95a70f0c8c5e45e22af629a5b5f370b867 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:24 +0530 Subject: [PATCH 20/41] irqchip: crossbar: Remove IS_ERR_VALUE check IS_ERR_VALUE makes sense only *if* there could be valid values in negative error range. But in the cases that we do use it, there is no such case. Just remove the same. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-7-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index a39cb316d2b0..88fbe0ff1dde 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -102,15 +102,15 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { - unsigned long ret; + int ret; ret = get_prev_map_irq(intspec[1]); - if (!IS_ERR_VALUE(ret)) + if (ret >= 0) goto found; ret = allocate_free_irq(intspec[1]); - if (IS_ERR_VALUE(ret)) + if (ret < 0) return ret; found: From 4dbf45e3c2f3acfd2096cb6a731d159492ddca99 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:25 +0530 Subject: [PATCH 21/41] irqchip: crossbar: Fix sparse and checkpatch warnings There is absolutely no need for crossbar driver to expose functions and variables into global namespace. So make them all static Also fix a couple of checkpatch warnings. Fixes sparse warnings: drivers/irqchip/irq-crossbar.c:129:29: warning: symbol 'routable_irq_domain_ops' was not declared. Should it be static? drivers/irqchip/irq-crossbar.c:261:12: warning: symbol 'irqcrossbar_init' was not declared. Should it be static? Checkpatch warnings: WARNING: Prefer kcalloc over kzalloc with multiply + cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); WARNING: Prefer kcalloc over kzalloc with multiply + cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-8-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 88fbe0ff1dde..f7eda9f5997e 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -15,6 +15,7 @@ #include #include #include +#include #define IRQ_FREE -1 #define IRQ_RESERVED -2 @@ -118,7 +119,7 @@ found: return 0; } -const struct irq_domain_ops routable_irq_domain_ops = { +static const struct irq_domain_ops routable_irq_domain_ops = { .map = crossbar_domain_map, .unmap = crossbar_domain_unmap, .xlate = crossbar_domain_xlate @@ -139,7 +140,7 @@ static int __init crossbar_of_init(struct device_node *node) goto err1; of_property_read_u32(node, "ti,max-irqs", &max); - cb->irq_map = kzalloc(max * sizeof(int), GFP_KERNEL); + cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) goto err2; @@ -184,7 +185,7 @@ static int __init crossbar_of_init(struct device_node *node) } - cb->register_offsets = kzalloc(max * sizeof(int), GFP_KERNEL); + cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->register_offsets) goto err3; From e30ef8abb383903f2b7e1bced971b98fd30cafc3 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:26 +0530 Subject: [PATCH 22/41] irqchip: crossbar: Fix kerneldoc warning Adding missing properties for kerneldoc (@write) and cleanup of harmless warnings while we are here. kerneldoc warnings: Warning(drivers/irqchip/irq-crossbar.c:27): missing initial short description on line: * struct crossbar_device: crossbar device description Info(drivers/irqchip/irq-crossbar.c:27): Scanning doc for struct Warning(drivers/irqchip/irq-crossbar.c:39): No description found for parameter 'write' 2 warnings Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-9-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index f7eda9f5997e..cee556bf603c 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -22,12 +22,14 @@ #define IRQ_SKIP -3 #define GIC_IRQ_START 32 -/* +/** + * struct crossbar_device - crossbar device description * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number + * @write: register write function pointer */ struct crossbar_device { uint int_max; From edb442def98c3c64acc316b6b1a64791c138ab07 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:27 +0530 Subject: [PATCH 23/41] irqchip: crossbar: Return proper error value crossbar_of_init always returns -ENOMEM in case of errors. There can be other causes of failure like invalid data from DT. So return a appropriate error value for that case. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-10-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index cee556bf603c..10d723dfa348 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -129,19 +129,25 @@ static const struct irq_domain_ops routable_irq_domain_ops = { static int __init crossbar_of_init(struct device_node *node) { - int i, size, max, reserved = 0, entry; + int i, size, max = 0, reserved = 0, entry; const __be32 *irqsr; + int ret = -ENOMEM; cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (!cb) - return -ENOMEM; + return ret; cb->crossbar_base = of_iomap(node, 0); if (!cb->crossbar_base) goto err1; of_property_read_u32(node, "ti,max-irqs", &max); + if (!max) { + pr_err("missing 'ti,max-irqs' property\n"); + ret = -EINVAL; + goto err2; + } cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) goto err2; @@ -162,6 +168,7 @@ static int __init crossbar_of_init(struct device_node *node) i, &entry); if (entry > max) { pr_err("Invalid reserved entry\n"); + ret = -EINVAL; goto err3; } cb->irq_map[entry] = IRQ_RESERVED; @@ -205,6 +212,7 @@ static int __init crossbar_of_init(struct device_node *node) break; default: pr_err("Invalid reg-size property\n"); + ret = -EINVAL; goto err4; break; } @@ -243,7 +251,7 @@ err2: iounmap(cb->crossbar_base); err1: kfree(cb); - return -ENOMEM; + return ret; } static const struct of_device_id crossbar_match[] __initconst = { From 3c44d5151246947442f2fe8eede842e3f76dd2f1 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:28 +0530 Subject: [PATCH 24/41] irqchip: crossbar: Change the goto naming Using err1,2,3,4 etc makes it hard to ensure a new exit path in the middle will not result in spurious changes, so rename the error paths as per the function it does. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-11-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 10d723dfa348..afc35fd1d9aa 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -140,17 +140,17 @@ static int __init crossbar_of_init(struct device_node *node) cb->crossbar_base = of_iomap(node, 0); if (!cb->crossbar_base) - goto err1; + goto err_cb; of_property_read_u32(node, "ti,max-irqs", &max); if (!max) { pr_err("missing 'ti,max-irqs' property\n"); ret = -EINVAL; - goto err2; + goto err_base; } cb->irq_map = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->irq_map) - goto err2; + goto err_base; cb->int_max = max; @@ -169,7 +169,7 @@ static int __init crossbar_of_init(struct device_node *node) if (entry > max) { pr_err("Invalid reserved entry\n"); ret = -EINVAL; - goto err3; + goto err_irq_map; } cb->irq_map[entry] = IRQ_RESERVED; } @@ -187,7 +187,7 @@ static int __init crossbar_of_init(struct device_node *node) if (entry > max) { pr_err("Invalid skip entry\n"); ret = -EINVAL; - goto err3; + goto err_irq_map; } cb->irq_map[entry] = IRQ_SKIP; } @@ -196,7 +196,7 @@ static int __init crossbar_of_init(struct device_node *node) cb->register_offsets = kcalloc(max, sizeof(int), GFP_KERNEL); if (!cb->register_offsets) - goto err3; + goto err_irq_map; of_property_read_u32(node, "ti,reg-size", &size); @@ -213,7 +213,7 @@ static int __init crossbar_of_init(struct device_node *node) default: pr_err("Invalid reg-size property\n"); ret = -EINVAL; - goto err4; + goto err_reg_offset; break; } @@ -230,7 +230,6 @@ static int __init crossbar_of_init(struct device_node *node) } of_property_read_u32(node, "ti,irqs-safe-map", &cb->safe_map); - /* Initialize the crossbar with safe map to start with */ for (i = 0; i < max; i++) { if (cb->irq_map[i] == IRQ_RESERVED || @@ -243,13 +242,13 @@ static int __init crossbar_of_init(struct device_node *node) register_routable_domain_ops(&routable_irq_domain_ops); return 0; -err4: +err_reg_offset: kfree(cb->register_offsets); -err3: +err_irq_map: kfree(cb->irq_map); -err2: +err_base: iounmap(cb->crossbar_base); -err1: +err_cb: kfree(cb); return ret; } From 99e37d0ea607623f286b4841cc355e65b297170c Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Thu, 26 Jun 2014 12:40:29 +0530 Subject: [PATCH 25/41] irqchip: crossbar: Set cb pointer to null in case of error If crossbar_of_init returns with a error, then set the cb pointer to null. Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-12-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index afc35fd1d9aa..a8c615692803 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -250,6 +250,8 @@ err_base: iounmap(cb->crossbar_base); err_cb: kfree(cb); + + cb = NULL; return ret; } From 8b09a45dc12f83f2312a47f0f0087ec4004ebacc Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Thu, 26 Jun 2014 12:40:30 +0530 Subject: [PATCH 26/41] irqchip: crossbar: Add kerneldoc for crossbar_domain_unmap callback Adding kerneldoc for unmap callback function. Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-13-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index a8c615692803..518d712c475a 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -89,6 +89,17 @@ static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, return 0; } +/** + * crossbar_domain_unmap - unmap a crossbar<->irq connection + * @d: domain of irq to unmap + * @irq: virq number + * + * We do not maintain a use count of total number of map/unmap + * calls for a particular irq to find out if a irq can be really + * unmapped. This is because unmap is called during irq_dispose_mapping(irq), + * after which irq is anyways unusable. So an explicit map has to be called + * after that. + */ static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; From 2f7d2fb71dd0c14f9c0fe66f2ed7b4685fa745e2 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:31 +0530 Subject: [PATCH 27/41] irqchip: crossbar: Introduce ti, max-crossbar-sources to identify valid crossbar mapping Currently we attempt to map any crossbar value to an IRQ, however, this is not correct from hardware perspective. There is a max crossbar event number upto which hardware supports. So describe the same in device tree using 'ti,max-crossbar-sources' property and use it to validate requests. [ jac - remove MAX_SOURCES from binding doc, use integer because we shouldn't put implementation details in the binding docs ] Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-14-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 2 ++ drivers/irqchip/irq-crossbar.c | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index 5f45c78e31a9..a6e462f88889 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -10,6 +10,7 @@ Required properties: - compatible : Should be "ti,irq-crossbar" - reg: Base address and the size of the crossbar registers. - ti,max-irqs: Total number of irqs available at the interrupt controller. +- ti,max-crossbar-sources: Maximum number of crossbar sources that can be routed. - ti,reg-size: Size of a individual register in bytes. Every individual register is assumed to be of same size. Valid sizes are 1, 2, 4. - ti,irqs-reserved: List of the reserved irq lines that are not muxed using @@ -30,6 +31,7 @@ Examples: compatible = "ti,irq-crossbar"; reg = <0x4a002a48 0x130>; ti,max-irqs = <160>; + ti,max-crossbar-sources = <400>; ti,reg-size = <2>; ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; ti,irqs-skip = <10 133 139 140>; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 518d712c475a..c9f068ca7bc9 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -26,6 +26,7 @@ * struct crossbar_device - crossbar device description * @int_max: maximum number of supported interrupts * @safe_map: safe default value to initialize the crossbar + * @max_crossbar_sources: Maximum number of crossbar sources * @irq_map: array of interrupts to crossbar number mapping * @crossbar_base: crossbar base address * @register_offsets: offsets for each irq number @@ -34,6 +35,7 @@ struct crossbar_device { uint int_max; uint safe_map; + uint max_crossbar_sources; uint *irq_map; void __iomem *crossbar_base; int *register_offsets; @@ -117,12 +119,19 @@ static int crossbar_domain_xlate(struct irq_domain *d, unsigned int *out_type) { int ret; + int req_num = intspec[1]; - ret = get_prev_map_irq(intspec[1]); + if (req_num >= cb->max_crossbar_sources) { + pr_err("%s: requested crossbar number %d > max %d\n", + __func__, req_num, cb->max_crossbar_sources); + return -EINVAL; + } + + ret = get_prev_map_irq(req_num); if (ret >= 0) goto found; - ret = allocate_free_irq(intspec[1]); + ret = allocate_free_irq(req_num); if (ret < 0) return ret; @@ -153,6 +162,14 @@ static int __init crossbar_of_init(struct device_node *node) if (!cb->crossbar_base) goto err_cb; + of_property_read_u32(node, "ti,max-crossbar-sources", + &cb->max_crossbar_sources); + if (!cb->max_crossbar_sources) { + pr_err("missing 'ti,max-crossbar-sources' property\n"); + ret = -EINVAL; + goto err_base; + } + of_property_read_u32(node, "ti,max-irqs", &max); if (!max) { pr_err("missing 'ti,max-irqs' property\n"); From 29918b6790d92456cab87ff41a01224e0882d04a Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:32 +0530 Subject: [PATCH 28/41] irqchip: crossbar: Introduce centralized check for crossbar write This is a basic check to ensure that crossbar register needs to be written. This ensures that we have a common check which is used in both map and unmap logic. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-15-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-crossbar.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index c9f068ca7bc9..83f803bfab76 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -84,10 +84,20 @@ static inline int allocate_free_irq(int cb_no) return -ENODEV; } +static inline bool needs_crossbar_write(irq_hw_number_t hw) +{ + if (hw > GIC_IRQ_START) + return true; + + return false; +} + static int crossbar_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { - cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + if (needs_crossbar_write(hw)) + cb->write(hw - GIC_IRQ_START, cb->irq_map[hw - GIC_IRQ_START]); + return 0; } @@ -106,7 +116,7 @@ static void crossbar_domain_unmap(struct irq_domain *d, unsigned int irq) { irq_hw_number_t hw = irq_get_irq_data(irq)->hwirq; - if (hw > GIC_IRQ_START) { + if (needs_crossbar_write(hw)) { cb->irq_map[hw - GIC_IRQ_START] = IRQ_FREE; cb->write(hw - GIC_IRQ_START, cb->safe_map); } From 9a34f73fb75531507760b957407fb55278b38ae8 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:33 +0530 Subject: [PATCH 29/41] documentation: dt: omap: crossbar: Add description for interrupt consumer The current crossbar description does not include the description required for the consumer of the crossbar, a.k.a devices whoes events pass through the crossbar into the GIC interrupt controller. So, provide documentation for the same. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-16-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index a6e462f88889..ce7d01d86705 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -36,3 +36,20 @@ Examples: ti,irqs-reserved = <0 1 2 3 5 6 131 132 139 140>; ti,irqs-skip = <10 133 139 140>; }; + +Consumer: +======== +See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt and +Documentation/devicetree/bindings/arm/gic.txt for further details. + +An interrupt consumer on an SoC using crossbar will use: + interrupts = +request number shall be between 0 to that described by +"ti,max-crossbar-sources" + +Example: + device_x@0x4a023000 { + /* Crossbar 8 used */ + interrupts = ; + ... + }; From d360892d37b5d0e82595001c4be6d49311e2c265 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 Jun 2014 12:40:34 +0530 Subject: [PATCH 30/41] irqchip: crossbar: Allow for quirky hardware with direct hardwiring of GIC On certain platforms such as DRA7, SPIs 0, 1, 2, 3, 5, 6, 10, 131, 132, 133 are direct wired to hardware blocks bypassing crossbar. This quirky implementation is *NOT* supposed to be the expectation of crossbar hardware usage. However, these are already marked in our description of the hardware with SKIP and RESERVED where appropriate. Unfortunately, we need to be able to refer to these hardwired IRQs. So, to request these, crossbar driver can use the existing information from it's table that these SKIP/RESERVED maps are direct wired sources and generic allocation/programming of crossbar should be avoided. Signed-off-by: Nishanth Menon Signed-off-by: Sricharan R Acked-by: Santosh Shilimkar Link: https://lkml.kernel.org/r/1403766634-18543-17-git-send-email-r.sricharan@ti.com Signed-off-by: Jason Cooper --- .../devicetree/bindings/arm/omap/crossbar.txt | 12 +++++++++-- drivers/irqchip/irq-crossbar.c | 20 +++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/omap/crossbar.txt b/Documentation/devicetree/bindings/arm/omap/crossbar.txt index ce7d01d86705..4139db353d0a 100644 --- a/Documentation/devicetree/bindings/arm/omap/crossbar.txt +++ b/Documentation/devicetree/bindings/arm/omap/crossbar.txt @@ -44,8 +44,10 @@ Documentation/devicetree/bindings/arm/gic.txt for further details. An interrupt consumer on an SoC using crossbar will use: interrupts = -request number shall be between 0 to that described by -"ti,max-crossbar-sources" +When the request number is between 0 to that described by +"ti,max-crossbar-sources", it is assumed to be a crossbar mapping. If the +request_number is greater than "ti,max-crossbar-sources", then it is mapped as a +quirky hardware mapping direct to GIC. Example: device_x@0x4a023000 { @@ -53,3 +55,9 @@ Example: interrupts = ; ... }; + + device_y@0x4a033000 { + /* Direct mapped GIC SPI 1 used */ + interrupts = ; + ... + }; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 83f803bfab76..85c2985d8bcb 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -86,8 +86,13 @@ static inline int allocate_free_irq(int cb_no) static inline bool needs_crossbar_write(irq_hw_number_t hw) { - if (hw > GIC_IRQ_START) - return true; + int cb_no; + + if (hw > GIC_IRQ_START) { + cb_no = cb->irq_map[hw - GIC_IRQ_START]; + if (cb_no != IRQ_RESERVED && cb_no != IRQ_SKIP) + return true; + } return false; } @@ -130,8 +135,19 @@ static int crossbar_domain_xlate(struct irq_domain *d, { int ret; int req_num = intspec[1]; + int direct_map_num; if (req_num >= cb->max_crossbar_sources) { + direct_map_num = req_num - cb->max_crossbar_sources; + if (direct_map_num < cb->int_max) { + ret = cb->irq_map[direct_map_num]; + if (ret == IRQ_RESERVED || ret == IRQ_SKIP) { + /* We use the interrupt num as h/w irq num */ + ret = direct_map_num; + goto found; + } + } + pr_err("%s: requested crossbar number %d > max %d\n", __func__, req_num, cb->max_crossbar_sources); return -EINVAL; From 4db8e6d20ccb158de2e06d30a1a421f4d053e429 Mon Sep 17 00:00:00 2001 From: Stefan Kristiansson Date: Mon, 26 May 2014 23:31:42 +0300 Subject: [PATCH 31/41] irqchip: or1k-pic: Migrate from arch/openrisc/ In addition to consolidating the or1k-pic with other interrupt controllers, this makes OpenRISC less tied to its on-cpu interrupt controller. All or1k-pic specific parts are moved out of irq.c and into drivers/irqchip/irq-or1k-pic.c In that transition, the functionality have been divided into three chip variants. One that handles level triggered interrupts, one that handles edge triggered interrupts and one that handles the interrupt controller that is present in the or1200 OpenRISC cpu implementation. Signed-off-by: Stefan Kristiansson Link: https://lkml.kernel.org/r/1401136302-27654-1-git-send-email-stefan.kristiansson@saunalahti.fi Acked-by: Jonas Bonn Signed-off-by: Jason Cooper --- .../opencores,or1k-pic.txt | 23 +++ arch/openrisc/Kconfig | 1 + arch/openrisc/include/asm/irq.h | 3 + arch/openrisc/kernel/irq.c | 152 ++------------- drivers/irqchip/Kconfig | 4 + drivers/irqchip/Makefile | 1 + drivers/irqchip/irq-or1k-pic.c | 182 ++++++++++++++++++ 7 files changed, 230 insertions(+), 136 deletions(-) create mode 100644 Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt create mode 100644 drivers/irqchip/irq-or1k-pic.c diff --git a/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt b/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt new file mode 100644 index 000000000000..55c04faa3f3f --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/opencores,or1k-pic.txt @@ -0,0 +1,23 @@ +OpenRISC 1000 Programmable Interrupt Controller + +Required properties: + +- compatible : should be "opencores,or1k-pic-level" for variants with + level triggered interrupt lines, "opencores,or1k-pic-edge" for variants with + edge triggered interrupt lines or "opencores,or1200-pic" for machines + with the non-spec compliant or1200 type implementation. + + "opencores,or1k-pic" is also provided as an alias to "opencores,or1200-pic", + but this is only for backwards compatibility. + +- interrupt-controller : Identifies the node as an interrupt controller +- #interrupt-cells : Specifies the number of cells needed to encode an + interrupt source. The value shall be 1. + +Example: + +intc: interrupt-controller { + compatible = "opencores,or1k-pic-level"; + interrupt-controller; + #interrupt-cells = <1>; +}; diff --git a/arch/openrisc/Kconfig b/arch/openrisc/Kconfig index e71d712afb79..88e83368bbf5 100644 --- a/arch/openrisc/Kconfig +++ b/arch/openrisc/Kconfig @@ -22,6 +22,7 @@ config OPENRISC select GENERIC_STRNLEN_USER select MODULES_USE_ELF_RELA select HAVE_DEBUG_STACKOVERFLOW + select OR1K_PIC config MMU def_bool y diff --git a/arch/openrisc/include/asm/irq.h b/arch/openrisc/include/asm/irq.h index eb612b1865d2..b84634cc95eb 100644 --- a/arch/openrisc/include/asm/irq.h +++ b/arch/openrisc/include/asm/irq.h @@ -24,4 +24,7 @@ #define NO_IRQ (-1) +void handle_IRQ(unsigned int, struct pt_regs *); +extern void set_handle_irq(void (*handle_irq)(struct pt_regs *)); + #endif /* __ASM_OPENRISC_IRQ_H__ */ diff --git a/arch/openrisc/kernel/irq.c b/arch/openrisc/kernel/irq.c index 8ec77bc9f1e7..967eb1430203 100644 --- a/arch/openrisc/kernel/irq.c +++ b/arch/openrisc/kernel/irq.c @@ -16,11 +16,10 @@ #include #include -#include #include #include +#include #include -#include #include /* read interrupt enabled status */ @@ -37,150 +36,31 @@ void arch_local_irq_restore(unsigned long flags) } EXPORT_SYMBOL(arch_local_irq_restore); - -/* OR1K PIC implementation */ - -/* We're a couple of cycles faster than the generic implementations with - * these 'fast' versions. - */ - -static void or1k_pic_mask(struct irq_data *data) -{ - mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); -} - -static void or1k_pic_unmask(struct irq_data *data) -{ - mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq)); -} - -static void or1k_pic_ack(struct irq_data *data) -{ - /* EDGE-triggered interrupts need to be ack'ed in order to clear - * the latch. - * LEVEL-triggered interrupts do not need to be ack'ed; however, - * ack'ing the interrupt has no ill-effect and is quicker than - * trying to figure out what type it is... - */ - - /* The OpenRISC 1000 spec says to write a 1 to the bit to ack the - * interrupt, but the OR1200 does this backwards and requires a 0 - * to be written... - */ - -#ifdef CONFIG_OR1K_1200 - /* There are two oddities with the OR1200 PIC implementation: - * i) LEVEL-triggered interrupts are latched and need to be cleared - * ii) the interrupt latch is cleared by writing a 0 to the bit, - * as opposed to a 1 as mandated by the spec - */ - - mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); -#else - WARN(1, "Interrupt handling possibly broken\n"); - mtspr(SPR_PICSR, (1UL << data->hwirq)); -#endif -} - -static void or1k_pic_mask_ack(struct irq_data *data) -{ - /* Comments for pic_ack apply here, too */ - -#ifdef CONFIG_OR1K_1200 - mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); - mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); -#else - WARN(1, "Interrupt handling possibly broken\n"); - mtspr(SPR_PICMR, (1UL << data->hwirq)); - mtspr(SPR_PICSR, (1UL << data->hwirq)); -#endif -} - -#if 0 -static int or1k_pic_set_type(struct irq_data *data, unsigned int flow_type) -{ - /* There's nothing to do in the PIC configuration when changing - * flow type. Level and edge-triggered interrupts are both - * supported, but it's PIC-implementation specific which type - * is handled. */ - - return irq_setup_alt_chip(data, flow_type); -} -#endif - -static struct irq_chip or1k_dev = { - .name = "or1k-PIC", - .irq_unmask = or1k_pic_unmask, - .irq_mask = or1k_pic_mask, - .irq_ack = or1k_pic_ack, - .irq_mask_ack = or1k_pic_mask_ack, -}; - -static struct irq_domain *root_domain; - -static inline int pic_get_irq(int first) -{ - int hwirq; - - hwirq = ffs(mfspr(SPR_PICSR) >> first); - if (!hwirq) - return NO_IRQ; - else - hwirq = hwirq + first -1; - - return irq_find_mapping(root_domain, hwirq); -} - - -static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) -{ - irq_set_chip_and_handler_name(irq, &or1k_dev, - handle_level_irq, "level"); - irq_set_status_flags(irq, IRQ_LEVEL | IRQ_NOPROBE); - - return 0; -} - -static const struct irq_domain_ops or1k_irq_domain_ops = { - .xlate = irq_domain_xlate_onecell, - .map = or1k_map, -}; - -/* - * This sets up the IRQ domain for the PIC built in to the OpenRISC - * 1000 CPU. This is the "root" domain as these are the interrupts - * that directly trigger an exception in the CPU. - */ -static void __init or1k_irq_init(void) -{ - struct device_node *intc = NULL; - - /* The interrupt controller device node is mandatory */ - intc = of_find_compatible_node(NULL, NULL, "opencores,or1k-pic"); - BUG_ON(!intc); - - /* Disable all interrupts until explicitly requested */ - mtspr(SPR_PICMR, (0UL)); - - root_domain = irq_domain_add_linear(intc, 32, - &or1k_irq_domain_ops, NULL); -} - void __init init_IRQ(void) { - or1k_irq_init(); + irqchip_init(); } -void __irq_entry do_IRQ(struct pt_regs *regs) +static void (*handle_arch_irq)(struct pt_regs *); + +void __init set_handle_irq(void (*handle_irq)(struct pt_regs *)) +{ + handle_arch_irq = handle_irq; +} + +void handle_IRQ(unsigned int irq, struct pt_regs *regs) { - int irq = -1; struct pt_regs *old_regs = set_irq_regs(regs); irq_enter(); - while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) - generic_handle_irq(irq); + generic_handle_irq(irq); irq_exit(); set_irq_regs(old_regs); } + +void __irq_entry do_IRQ(struct pt_regs *regs) +{ + handle_arch_irq(regs); +} diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index bbb746e35500..131f18562d7d 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -53,6 +53,10 @@ config CLPS711X_IRQCHIP select SPARSE_IRQ default y +config OR1K_PIC + bool + select IRQ_DOMAIN + config ORION_IRQCHIP bool select IRQ_DOMAIN diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 62a13e5ef98f..7fba336c4daf 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o obj-$(CONFIG_ARCH_MOXART) += irq-moxart.o obj-$(CONFIG_CLPS711X_IRQCHIP) += irq-clps711x.o +obj-$(CONFIG_OR1K_PIC) += irq-or1k-pic.o obj-$(CONFIG_ORION_IRQCHIP) += irq-orion.o obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi-nmi.o diff --git a/drivers/irqchip/irq-or1k-pic.c b/drivers/irqchip/irq-or1k-pic.c new file mode 100644 index 000000000000..17ff033d9925 --- /dev/null +++ b/drivers/irqchip/irq-or1k-pic.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2010-2011 Jonas Bonn + * Copyright (C) 2014 Stefan Kristansson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include + +#include "irqchip.h" + +/* OR1K PIC implementation */ + +struct or1k_pic_dev { + struct irq_chip chip; + irq_flow_handler_t handle; + unsigned long flags; +}; + +/* + * We're a couple of cycles faster than the generic implementations with + * these 'fast' versions. + */ + +static void or1k_pic_mask(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); +} + +static void or1k_pic_unmask(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) | (1UL << data->hwirq)); +} + +static void or1k_pic_ack(struct irq_data *data) +{ + mtspr(SPR_PICSR, (1UL << data->hwirq)); +} + +static void or1k_pic_mask_ack(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); + mtspr(SPR_PICSR, (1UL << data->hwirq)); +} + +/* + * There are two oddities with the OR1200 PIC implementation: + * i) LEVEL-triggered interrupts are latched and need to be cleared + * ii) the interrupt latch is cleared by writing a 0 to the bit, + * as opposed to a 1 as mandated by the spec + */ +static void or1k_pic_or1200_ack(struct irq_data *data) +{ + mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); +} + +static void or1k_pic_or1200_mask_ack(struct irq_data *data) +{ + mtspr(SPR_PICMR, mfspr(SPR_PICMR) & ~(1UL << data->hwirq)); + mtspr(SPR_PICSR, mfspr(SPR_PICSR) & ~(1UL << data->hwirq)); +} + +static struct or1k_pic_dev or1k_pic_level = { + .chip = { + .name = "or1k-PIC-level", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_mask_ack = or1k_pic_mask, + }, + .handle = handle_level_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct or1k_pic_dev or1k_pic_edge = { + .chip = { + .name = "or1k-PIC-edge", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_ack = or1k_pic_ack, + .irq_mask_ack = or1k_pic_mask_ack, + }, + .handle = handle_edge_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct or1k_pic_dev or1k_pic_or1200 = { + .chip = { + .name = "or1200-PIC", + .irq_unmask = or1k_pic_unmask, + .irq_mask = or1k_pic_mask, + .irq_ack = or1k_pic_or1200_ack, + .irq_mask_ack = or1k_pic_or1200_mask_ack, + }, + .handle = handle_level_irq, + .flags = IRQ_LEVEL | IRQ_NOPROBE, +}; + +static struct irq_domain *root_domain; + +static inline int pic_get_irq(int first) +{ + int hwirq; + + hwirq = ffs(mfspr(SPR_PICSR) >> first); + if (!hwirq) + return NO_IRQ; + else + hwirq = hwirq + first - 1; + + return irq_find_mapping(root_domain, hwirq); +} + +static void or1k_pic_handle_irq(struct pt_regs *regs) +{ + int irq = -1; + + while ((irq = pic_get_irq(irq + 1)) != NO_IRQ) + handle_IRQ(irq, regs); +} + +static int or1k_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ + struct or1k_pic_dev *pic = d->host_data; + + irq_set_chip_and_handler(irq, &pic->chip, pic->handle); + irq_set_status_flags(irq, pic->flags); + + return 0; +} + +static const struct irq_domain_ops or1k_irq_domain_ops = { + .xlate = irq_domain_xlate_onecell, + .map = or1k_map, +}; + +/* + * This sets up the IRQ domain for the PIC built in to the OpenRISC + * 1000 CPU. This is the "root" domain as these are the interrupts + * that directly trigger an exception in the CPU. + */ +static int __init or1k_pic_init(struct device_node *node, + struct or1k_pic_dev *pic) +{ + /* Disable all interrupts until explicitly requested */ + mtspr(SPR_PICMR, (0UL)); + + root_domain = irq_domain_add_linear(node, 32, &or1k_irq_domain_ops, + pic); + + set_handle_irq(or1k_pic_handle_irq); + + return 0; +} + +static int __init or1k_pic_or1200_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_or1200); +} +IRQCHIP_DECLARE(or1k_pic_or1200, "opencores,or1200-pic", or1k_pic_or1200_init); +IRQCHIP_DECLARE(or1k_pic, "opencores,or1k-pic", or1k_pic_or1200_init); + +static int __init or1k_pic_level_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_level); +} +IRQCHIP_DECLARE(or1k_pic_level, "opencores,or1k-pic-level", + or1k_pic_level_init); + +static int __init or1k_pic_edge_init(struct device_node *node, + struct device_node *parent) +{ + return or1k_pic_init(node, &or1k_pic_edge); +} +IRQCHIP_DECLARE(or1k_pic_edge, "opencores,or1k-pic-edge", or1k_pic_edge_init); From d709f7bcbb3ab01704fa7b37a2e4b981cf3783c1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 10 Jul 2014 23:37:54 +0200 Subject: [PATCH 32/41] PM / sleep / irq: Do not suspend wakeup interrupts If an IRQ has been configured for wakeup via enable_irq_wake(), the driver who has done that must be prepared for receiving interrupts after suspend_device_irqs() has returned, so there is no need to "suspend" such IRQs. Moreover, if drivers using enable_irq_wake() actually want to receive interrupts after suspend_device_irqs() has returned, they need to add IRQF_NO_SUSPEND to the IRQ flags while requesting the IRQs, which shouldn't be necessary (it also goes a bit too far, as IRQF_NO_SUSPEND causes the IRQ to be ignored by suspend_device_irqs() all the time regardless of whether or not it has been configured for signaling wakeup). For the above reasons, make __disable_irq() ignore IRQ descriptors with IRQD_WAKEUP_STATE set when its suspend argument is true which effectively causes them to behave like IRQs with IRQF_NO_SUSPEND set. This also allows IRQs configured for wakeup via enable_irq_wake() to work as wakeup interrupts for the "freeze" (suspend-to-idle) sleep mode automatically just like for any other sleep states. Signed-off-by: Rafael J. Wysocki Cc: Li Aubrey Cc: Dmitry Torokhov Cc: One Thousand Gnomes Link: http://lkml.kernel.org/r/4679574.kGUnqAuNl9@vostro.rjw.lan Signed-off-by: Thomas Gleixner --- kernel/irq/manage.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 3dc6a61bf06a..88657d7bc9dd 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -385,7 +385,8 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask) void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend) { if (suspend) { - if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND)) + if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND) || + irqd_has_set(&desc->irq_data, IRQD_WAKEUP_STATE)) return; desc->istate |= IRQS_SUSPENDED; } From a5152c8a125da3c5e16dc2208dd52e80f0803c5c Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Jul 2014 19:14:16 +0200 Subject: [PATCH 33/41] genirq: generic chip: Export irq_map_generic_chip function Export the generic irq map function in order to provide irq_domain ops with generic mapping and specific of xlate function (needed by the new atmel AIC driver). Signed-off-by: Boris BREZILLON Acked-by: Thomas Gleixner Link: https://lkml.kernel.org/r/1405012462-766-2-git-send-email-boris.brezillon@free-electrons.com Signed-off-by: Jason Cooper --- include/linux/irq.h | 2 ++ kernel/irq/generic-chip.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/linux/irq.h b/include/linux/irq.h index 0d998d8b01d8..62af59242ddc 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -771,6 +771,8 @@ void irq_gc_eoi(struct irq_data *d); int irq_gc_set_wake(struct irq_data *d, unsigned int on); /* Setup functions for irq_chip_generic */ +int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw_irq); struct irq_chip_generic * irq_alloc_generic_chip(const char *name, int nr_ct, unsigned int irq_base, void __iomem *reg_base, irq_flow_handler_t handler); diff --git a/kernel/irq/generic-chip.c b/kernel/irq/generic-chip.c index 452d6f2ba21d..cf80e7b0ddab 100644 --- a/kernel/irq/generic-chip.c +++ b/kernel/irq/generic-chip.c @@ -341,8 +341,8 @@ static struct lock_class_key irq_nested_lock_class; /* * irq_map_generic_chip - Map a generic chip for an irq domain */ -static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw_irq) +int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw_irq) { struct irq_data *data = irq_get_irq_data(virq); struct irq_domain_chip_generic *dgc = d->gc; @@ -394,6 +394,7 @@ static int irq_map_generic_chip(struct irq_domain *d, unsigned int virq, irq_modify_status(virq, dgc->irq_flags_to_clear, dgc->irq_flags_to_set); return 0; } +EXPORT_SYMBOL_GPL(irq_map_generic_chip); struct irq_domain_ops irq_generic_chip_ops = { .map = irq_map_generic_chip, From e9a0caa3d551351527729c6b13b97b849152fe2c Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Jul 2014 19:14:17 +0200 Subject: [PATCH 34/41] irqchip: atmel-aic: Move binding doc to interrupt-controller directory Move atmel aic driver doc to the interrupt-controller directory as the new driver now lays in drivers/irqchip/atmel-aic.c. Signed-off-by: Boris BREZILLON Acked-by: Nicolas Ferre Link: https://lkml.kernel.org/r/1405012462-766-3-git-send-email-boris.brezillon@free-electrons.com Signed-off-by: Jason Cooper --- .../{arm/atmel-aic.txt => interrupt-controller/atmel,aic.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Documentation/devicetree/bindings/{arm/atmel-aic.txt => interrupt-controller/atmel,aic.txt} (100%) diff --git a/Documentation/devicetree/bindings/arm/atmel-aic.txt b/Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt similarity index 100% rename from Documentation/devicetree/bindings/arm/atmel-aic.txt rename to Documentation/devicetree/bindings/interrupt-controller/atmel,aic.txt From b1479ebb772003461f0458a0b3a68cb1c4036288 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Jul 2014 19:14:18 +0200 Subject: [PATCH 35/41] irqchip: atmel-aic: Add atmel AIC/AIC5 drivers Add AIC (Advanced Interrupt Controller) and AIC5 (AIC5 is an evolution of the AIC block) drivers. Put common code in irq-atmel-aic-common.c/.h so that both driver can access shared functions (this will ease maintenance). These drivers are only compatible with dt enabled board and replace the old implementation found in arch/arm/mach-at91/irq.c. Signed-off-by: Boris BREZILLON Acked-by: Nicolas Ferre Link: https://lkml.kernel.org/r/1405012462-766-4-git-send-email-boris.brezillon@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/Kconfig | 14 + drivers/irqchip/Makefile | 2 + drivers/irqchip/irq-atmel-aic-common.c | 207 +++++++++++++++ drivers/irqchip/irq-atmel-aic-common.h | 35 +++ drivers/irqchip/irq-atmel-aic.c | 247 ++++++++++++++++++ drivers/irqchip/irq-atmel-aic5.c | 341 +++++++++++++++++++++++++ 6 files changed, 846 insertions(+) create mode 100644 drivers/irqchip/irq-atmel-aic-common.c create mode 100644 drivers/irqchip/irq-atmel-aic-common.h create mode 100644 drivers/irqchip/irq-atmel-aic.c create mode 100644 drivers/irqchip/irq-atmel-aic5.c diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index bbb746e35500..baa32cc64d6e 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -30,6 +30,20 @@ config ARM_VIC_NR The maximum number of VICs available in the system, for power management. +config ATMEL_AIC_IRQ + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + select MULTI_IRQ_HANDLER + select SPARSE_IRQ + +config ATMEL_AIC5_IRQ + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + select MULTI_IRQ_HANDLER + select SPARSE_IRQ + config BRCMSTB_L2_IRQ bool depends on ARM diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 62a13e5ef98f..674e895fa659 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -18,6 +18,8 @@ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_NVIC) += irq-nvic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o +obj-$(CONFIG_ATMEL_AIC_IRQ) += irq-atmel-aic-common.o irq-atmel-aic.o +obj-$(CONFIG_ATMEL_AIC5_IRQ) += irq-atmel-aic-common.o irq-atmel-aic5.o obj-$(CONFIG_IMGPDC_IRQ) += irq-imgpdc.o obj-$(CONFIG_SIRF_IRQ) += irq-sirfsoc.o obj-$(CONFIG_RENESAS_INTC_IRQPIN) += irq-renesas-intc-irqpin.o diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c new file mode 100644 index 000000000000..18b76fc03d53 --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -0,0 +1,207 @@ +/* + * Atmel AT91 common AIC (Advanced Interrupt Controller) code shared by + * irq-atmel-aic and irq-atmel-aic5 drivers + * + * Copyright (C) 2004 SAN People + * Copyright (C) 2004 ATMEL + * Copyright (C) Rick Bronson + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "irq-atmel-aic-common.h" + +#define AT91_AIC_PRIOR GENMASK(2, 0) +#define AT91_AIC_IRQ_MIN_PRIORITY 0 +#define AT91_AIC_IRQ_MAX_PRIORITY 7 + +#define AT91_AIC_SRCTYPE GENMASK(7, 6) +#define AT91_AIC_SRCTYPE_LOW (0 << 5) +#define AT91_AIC_SRCTYPE_FALLING (1 << 5) +#define AT91_AIC_SRCTYPE_HIGH (2 << 5) +#define AT91_AIC_SRCTYPE_RISING (3 << 5) + +struct aic_chip_data { + u32 ext_irqs; +}; + +static void aic_common_shutdown(struct irq_data *d) +{ + struct irq_chip_type *ct = irq_data_get_chip_type(d); + + ct->chip.irq_mask(d); +} + +int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct aic_chip_data *aic = gc->private; + unsigned aic_type; + + switch (type) { + case IRQ_TYPE_LEVEL_HIGH: + aic_type = AT91_AIC_SRCTYPE_HIGH; + break; + case IRQ_TYPE_EDGE_RISING: + aic_type = AT91_AIC_SRCTYPE_RISING; + break; + case IRQ_TYPE_LEVEL_LOW: + if (!(d->mask & aic->ext_irqs)) + return -EINVAL; + + aic_type = AT91_AIC_SRCTYPE_LOW; + break; + case IRQ_TYPE_EDGE_FALLING: + if (!(d->mask & aic->ext_irqs)) + return -EINVAL; + + aic_type = AT91_AIC_SRCTYPE_FALLING; + break; + default: + return -EINVAL; + } + + *val &= AT91_AIC_SRCTYPE; + *val |= aic_type; + + return 0; +} + +int aic_common_set_priority(int priority, unsigned *val) +{ + if (priority < AT91_AIC_IRQ_MIN_PRIORITY || + priority > AT91_AIC_IRQ_MAX_PRIORITY) + return -EINVAL; + + *val &= AT91_AIC_PRIOR; + *val |= priority; + + return 0; +} + +int aic_common_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, + unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_type) +{ + if (WARN_ON(intsize < 3)) + return -EINVAL; + + if (WARN_ON((intspec[2] < AT91_AIC_IRQ_MIN_PRIORITY) || + (intspec[2] > AT91_AIC_IRQ_MAX_PRIORITY))) + return -EINVAL; + + *out_hwirq = intspec[0]; + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; + + return 0; +} + +static void __init aic_common_ext_irq_of_init(struct irq_domain *domain) +{ + struct device_node *node = domain->of_node; + struct irq_chip_generic *gc; + struct aic_chip_data *aic; + struct property *prop; + const __be32 *p; + u32 hwirq; + + gc = irq_get_domain_generic_chip(domain, 0); + + aic = gc->private; + aic->ext_irqs |= 1; + + of_property_for_each_u32(node, "atmel,external-irqs", prop, p, hwirq) { + gc = irq_get_domain_generic_chip(domain, hwirq); + if (!gc) { + pr_warn("AIC: external irq %d >= %d skip it\n", + hwirq, domain->revmap_size); + continue; + } + + aic = gc->private; + aic->ext_irqs |= (1 << (hwirq % 32)); + } +} + +struct irq_domain *__init aic_common_of_init(struct device_node *node, + const struct irq_domain_ops *ops, + const char *name, int nirqs) +{ + struct irq_chip_generic *gc; + struct irq_domain *domain; + struct aic_chip_data *aic; + void __iomem *reg_base; + int nchips; + int ret; + int i; + + nchips = DIV_ROUND_UP(nirqs, 32); + + reg_base = of_iomap(node, 0); + if (!reg_base) + return ERR_PTR(-ENOMEM); + + aic = kcalloc(nchips, sizeof(*aic), GFP_KERNEL); + if (!aic) { + ret = -ENOMEM; + goto err_iounmap; + } + + domain = irq_domain_add_linear(node, nchips * 32, ops, aic); + if (!domain) { + ret = -ENOMEM; + goto err_free_aic; + } + + ret = irq_alloc_domain_generic_chips(domain, 32, 1, name, + handle_level_irq, 0, 0, + IRQCHIP_SKIP_SET_WAKE); + if (ret) + goto err_domain_remove; + + for (i = 0; i < nchips; i++) { + gc = irq_get_domain_generic_chip(domain, i * 32); + + gc->reg_base = reg_base; + + gc->unused = 0; + gc->wake_enabled = ~0; + gc->chip_types[0].type = IRQ_TYPE_SENSE_MASK; + gc->chip_types[0].handler = handle_fasteoi_irq; + gc->chip_types[0].chip.irq_eoi = irq_gc_eoi; + gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; + gc->chip_types[0].chip.irq_shutdown = aic_common_shutdown; + gc->private = &aic[i]; + } + + aic_common_ext_irq_of_init(domain); + + return domain; + +err_domain_remove: + irq_domain_remove(domain); + +err_free_aic: + kfree(aic); + +err_iounmap: + iounmap(reg_base); + + return ERR_PTR(ret); +} diff --git a/drivers/irqchip/irq-atmel-aic-common.h b/drivers/irqchip/irq-atmel-aic-common.h new file mode 100644 index 000000000000..c178557ab7e8 --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic-common.h @@ -0,0 +1,35 @@ +/* + * Atmel AT91 common AIC (Advanced Interrupt Controller) header file + * + * Copyright (C) 2004 SAN People + * Copyright (C) 2004 ATMEL + * Copyright (C) Rick Bronson + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef __IRQ_ATMEL_AIC_COMMON_H +#define __IRQ_ATMEL_AIC_COMMON_H + + +int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val); + +int aic_common_set_priority(int priority, unsigned *val); + +int aic_common_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, + unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_type); + +struct irq_domain *__init aic_common_of_init(struct device_node *node, + const struct irq_domain_ops *ops, + const char *name, int nirqs); + +#endif /* __IRQ_ATMEL_AIC_COMMON_H */ diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c new file mode 100644 index 000000000000..b15b5667991c --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic.c @@ -0,0 +1,247 @@ +/* + * Atmel AT91 AIC (Advanced Interrupt Controller) driver + * + * Copyright (C) 2004 SAN People + * Copyright (C) 2004 ATMEL + * Copyright (C) Rick Bronson + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "irq-atmel-aic-common.h" +#include "irqchip.h" + +/* Number of irq lines managed by AIC */ +#define NR_AIC_IRQS 32 + +#define AT91_AIC_SMR(n) ((n) * 4) + +#define AT91_AIC_SVR(n) (0x80 + ((n) * 4)) +#define AT91_AIC_IVR 0x100 +#define AT91_AIC_FVR 0x104 +#define AT91_AIC_ISR 0x108 + +#define AT91_AIC_IPR 0x10c +#define AT91_AIC_IMR 0x110 +#define AT91_AIC_CISR 0x114 + +#define AT91_AIC_IECR 0x120 +#define AT91_AIC_IDCR 0x124 +#define AT91_AIC_ICCR 0x128 +#define AT91_AIC_ISCR 0x12c +#define AT91_AIC_EOICR 0x130 +#define AT91_AIC_SPU 0x134 +#define AT91_AIC_DCR 0x138 + +static struct irq_domain *aic_domain; + +static asmlinkage void __exception_irq_entry +aic_handle(struct pt_regs *regs) +{ + struct irq_domain_chip_generic *dgc = aic_domain->gc; + struct irq_chip_generic *gc = dgc->gc[0]; + u32 irqnr; + u32 irqstat; + + irqnr = irq_reg_readl(gc->reg_base + AT91_AIC_IVR); + irqstat = irq_reg_readl(gc->reg_base + AT91_AIC_ISR); + + irqnr = irq_find_mapping(aic_domain, irqnr); + + if (!irqstat) + irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); + else + handle_IRQ(irqnr, regs); +} + +static int aic_retrigger(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + + /* Enable interrupt on AIC5 */ + irq_gc_lock(gc); + irq_reg_writel(d->mask, gc->reg_base + AT91_AIC_ISCR); + irq_gc_unlock(gc); + + return 0; +} + +static int aic_set_type(struct irq_data *d, unsigned type) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + unsigned int smr; + int ret; + + smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(d->hwirq)); + ret = aic_common_set_type(d, type, &smr); + if (ret) + return ret; + + irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(d->hwirq)); + + return 0; +} + +#ifdef CONFIG_PM +static void aic_suspend(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + + irq_gc_lock(gc); + irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IDCR); + irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IECR); + irq_gc_unlock(gc); +} + +static void aic_resume(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + + irq_gc_lock(gc); + irq_reg_writel(gc->wake_active, gc->reg_base + AT91_AIC_IDCR); + irq_reg_writel(gc->mask_cache, gc->reg_base + AT91_AIC_IECR); + irq_gc_unlock(gc); +} + +static void aic_pm_shutdown(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + + irq_gc_lock(gc); + irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); + irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); + irq_gc_unlock(gc); +} +#else +#define aic_suspend NULL +#define aic_resume NULL +#define aic_pm_shutdown NULL +#endif /* CONFIG_PM */ + +static void __init aic_hw_init(struct irq_domain *domain) +{ + struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); + int i; + + /* + * Perform 8 End Of Interrupt Command to make sure AIC + * will not Lock out nIRQ + */ + for (i = 0; i < 8; i++) + irq_reg_writel(0, gc->reg_base + AT91_AIC_EOICR); + + /* + * Spurious Interrupt ID in Spurious Vector Register. + * When there is no current interrupt, the IRQ Vector Register + * reads the value stored in AIC_SPU + */ + irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_SPU); + + /* No debugging in AIC: Debug (Protect) Control Register */ + irq_reg_writel(0, gc->reg_base + AT91_AIC_DCR); + + /* Disable and clear all interrupts initially */ + irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_IDCR); + irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC_ICCR); + + for (i = 0; i < 32; i++) + irq_reg_writel(i, gc->reg_base + AT91_AIC_SVR(i)); +} + +static int aic_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_type) +{ + struct irq_domain_chip_generic *dgc = d->gc; + struct irq_chip_generic *gc; + unsigned smr; + int idx; + int ret; + + if (!dgc) + return -EINVAL; + + ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize, + out_hwirq, out_type); + if (ret) + return ret; + + idx = intspec[0] / dgc->irqs_per_chip; + if (idx >= dgc->num_chips) + return -EINVAL; + + gc = dgc->gc[idx]; + + irq_gc_lock(gc); + smr = irq_reg_readl(gc->reg_base + AT91_AIC_SMR(*out_hwirq)); + ret = aic_common_set_priority(intspec[2], &smr); + if (!ret) + irq_reg_writel(smr, gc->reg_base + AT91_AIC_SMR(*out_hwirq)); + irq_gc_unlock(gc); + + return ret; +} + +static const struct irq_domain_ops aic_irq_ops = { + .map = irq_map_generic_chip, + .xlate = aic_irq_domain_xlate, +}; + +static int __init aic_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_chip_generic *gc; + struct irq_domain *domain; + + if (aic_domain) + return -EEXIST; + + domain = aic_common_of_init(node, &aic_irq_ops, "atmel-aic", + NR_AIC_IRQS); + if (IS_ERR(domain)) + return PTR_ERR(domain); + + aic_domain = domain; + gc = irq_get_domain_generic_chip(domain, 0); + + gc->chip_types[0].regs.eoi = AT91_AIC_EOICR; + gc->chip_types[0].regs.enable = AT91_AIC_IECR; + gc->chip_types[0].regs.disable = AT91_AIC_IDCR; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_disable_reg; + gc->chip_types[0].chip.irq_unmask = irq_gc_unmask_enable_reg; + gc->chip_types[0].chip.irq_retrigger = aic_retrigger; + gc->chip_types[0].chip.irq_set_type = aic_set_type; + gc->chip_types[0].chip.irq_suspend = aic_suspend; + gc->chip_types[0].chip.irq_resume = aic_resume; + gc->chip_types[0].chip.irq_pm_shutdown = aic_pm_shutdown; + + aic_hw_init(domain); + set_handle_irq(aic_handle); + + return 0; +} +IRQCHIP_DECLARE(at91rm9200_aic, "atmel,at91rm9200-aic", aic_of_init); diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c new file mode 100644 index 000000000000..c9c753adc15d --- /dev/null +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -0,0 +1,341 @@ +/* + * Atmel AT91 AIC5 (Advanced Interrupt Controller) driver + * + * Copyright (C) 2004 SAN People + * Copyright (C) 2004 ATMEL + * Copyright (C) Rick Bronson + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "irq-atmel-aic-common.h" +#include "irqchip.h" + +/* Number of irq lines managed by AIC */ +#define NR_AIC5_IRQS 128 + +#define AT91_AIC5_SSR 0x0 +#define AT91_AIC5_INTSEL_MSK (0x7f << 0) + +#define AT91_AIC5_SMR 0x4 + +#define AT91_AIC5_SVR 0x8 +#define AT91_AIC5_IVR 0x10 +#define AT91_AIC5_FVR 0x14 +#define AT91_AIC5_ISR 0x18 + +#define AT91_AIC5_IPR0 0x20 +#define AT91_AIC5_IPR1 0x24 +#define AT91_AIC5_IPR2 0x28 +#define AT91_AIC5_IPR3 0x2c +#define AT91_AIC5_IMR 0x30 +#define AT91_AIC5_CISR 0x34 + +#define AT91_AIC5_IECR 0x40 +#define AT91_AIC5_IDCR 0x44 +#define AT91_AIC5_ICCR 0x48 +#define AT91_AIC5_ISCR 0x4c +#define AT91_AIC5_EOICR 0x38 +#define AT91_AIC5_SPU 0x3c +#define AT91_AIC5_DCR 0x6c + +#define AT91_AIC5_FFER 0x50 +#define AT91_AIC5_FFDR 0x54 +#define AT91_AIC5_FFSR 0x58 + +static struct irq_domain *aic5_domain; + +static asmlinkage void __exception_irq_entry +aic5_handle(struct pt_regs *regs) +{ + struct irq_domain_chip_generic *dgc = aic5_domain->gc; + struct irq_chip_generic *gc = dgc->gc[0]; + u32 irqnr; + u32 irqstat; + + irqnr = irq_reg_readl(gc->reg_base + AT91_AIC5_IVR); + irqstat = irq_reg_readl(gc->reg_base + AT91_AIC5_ISR); + + irqnr = irq_find_mapping(aic5_domain, irqnr); + + if (!irqstat) + irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); + else + handle_IRQ(irqnr, regs); +} + +static void aic5_mask(struct irq_data *d) +{ + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; + struct irq_chip_generic *gc = dgc->gc[0]; + + /* Disable interrupt on AIC5 */ + irq_gc_lock(gc); + irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); + irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); + gc->mask_cache &= ~d->mask; + irq_gc_unlock(gc); +} + +static void aic5_unmask(struct irq_data *d) +{ + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; + struct irq_chip_generic *gc = dgc->gc[0]; + + /* Enable interrupt on AIC5 */ + irq_gc_lock(gc); + irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); + irq_reg_writel(1, gc->reg_base + AT91_AIC5_IECR); + gc->mask_cache |= d->mask; + irq_gc_unlock(gc); +} + +static int aic5_retrigger(struct irq_data *d) +{ + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; + struct irq_chip_generic *gc = dgc->gc[0]; + + /* Enable interrupt on AIC5 */ + irq_gc_lock(gc); + irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); + irq_reg_writel(1, gc->reg_base + AT91_AIC5_ISCR); + irq_gc_unlock(gc); + + return 0; +} + +static int aic5_set_type(struct irq_data *d, unsigned type) +{ + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; + struct irq_chip_generic *gc = dgc->gc[0]; + unsigned int smr; + int ret; + + irq_gc_lock(gc); + irq_reg_writel(d->hwirq, gc->reg_base + AT91_AIC5_SSR); + smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); + ret = aic_common_set_type(d, type, &smr); + if (!ret) + irq_reg_writel(smr, gc->reg_base + AT91_AIC5_SMR); + irq_gc_unlock(gc); + + return ret; +} + +#ifdef CONFIG_PM +static void aic5_suspend(struct irq_data *d) +{ + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; + struct irq_chip_generic *bgc = dgc->gc[0]; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + int i; + u32 mask; + + irq_gc_lock(bgc); + for (i = 0; i < dgc->irqs_per_chip; i++) { + mask = 1 << i; + if ((mask & gc->mask_cache) == (mask & gc->wake_active)) + continue; + + irq_reg_writel(i + gc->irq_base, + bgc->reg_base + AT91_AIC5_SSR); + if (mask & gc->wake_active) + irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); + else + irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); + } + irq_gc_unlock(bgc); +} + +static void aic5_resume(struct irq_data *d) +{ + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; + struct irq_chip_generic *bgc = dgc->gc[0]; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + int i; + u32 mask; + + irq_gc_lock(bgc); + for (i = 0; i < dgc->irqs_per_chip; i++) { + mask = 1 << i; + if ((mask & gc->mask_cache) == (mask & gc->wake_active)) + continue; + + irq_reg_writel(i + gc->irq_base, + bgc->reg_base + AT91_AIC5_SSR); + if (mask & gc->mask_cache) + irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IECR); + else + irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); + } + irq_gc_unlock(bgc); +} + +static void aic5_pm_shutdown(struct irq_data *d) +{ + struct irq_domain *domain = d->domain; + struct irq_domain_chip_generic *dgc = domain->gc; + struct irq_chip_generic *bgc = dgc->gc[0]; + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + int i; + + irq_gc_lock(bgc); + for (i = 0; i < dgc->irqs_per_chip; i++) { + irq_reg_writel(i + gc->irq_base, + bgc->reg_base + AT91_AIC5_SSR); + irq_reg_writel(1, bgc->reg_base + AT91_AIC5_IDCR); + irq_reg_writel(1, bgc->reg_base + AT91_AIC5_ICCR); + } + irq_gc_unlock(bgc); +} +#else +#define aic5_suspend NULL +#define aic5_resume NULL +#define aic5_pm_shutdown NULL +#endif /* CONFIG_PM */ + +static void __init aic5_hw_init(struct irq_domain *domain) +{ + struct irq_chip_generic *gc = irq_get_domain_generic_chip(domain, 0); + int i; + + /* + * Perform 8 End Of Interrupt Command to make sure AIC + * will not Lock out nIRQ + */ + for (i = 0; i < 8; i++) + irq_reg_writel(0, gc->reg_base + AT91_AIC5_EOICR); + + /* + * Spurious Interrupt ID in Spurious Vector Register. + * When there is no current interrupt, the IRQ Vector Register + * reads the value stored in AIC_SPU + */ + irq_reg_writel(0xffffffff, gc->reg_base + AT91_AIC5_SPU); + + /* No debugging in AIC: Debug (Protect) Control Register */ + irq_reg_writel(0, gc->reg_base + AT91_AIC5_DCR); + + /* Disable and clear all interrupts initially */ + for (i = 0; i < domain->revmap_size; i++) { + irq_reg_writel(i, gc->reg_base + AT91_AIC5_SSR); + irq_reg_writel(i, gc->reg_base + AT91_AIC5_SVR); + irq_reg_writel(1, gc->reg_base + AT91_AIC5_IDCR); + irq_reg_writel(1, gc->reg_base + AT91_AIC5_ICCR); + } +} + +static int aic5_irq_domain_xlate(struct irq_domain *d, + struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, + unsigned int *out_type) +{ + struct irq_domain_chip_generic *dgc = d->gc; + struct irq_chip_generic *gc; + unsigned smr; + int ret; + + if (!dgc) + return -EINVAL; + + ret = aic_common_irq_domain_xlate(d, ctrlr, intspec, intsize, + out_hwirq, out_type); + if (ret) + return ret; + + gc = dgc->gc[0]; + + irq_gc_lock(gc); + irq_reg_writel(*out_hwirq, gc->reg_base + AT91_AIC5_SSR); + smr = irq_reg_readl(gc->reg_base + AT91_AIC5_SMR); + ret = aic_common_set_priority(intspec[2], &smr); + if (!ret) + irq_reg_writel(intspec[2] | smr, gc->reg_base + AT91_AIC5_SMR); + irq_gc_unlock(gc); + + return ret; +} + +static const struct irq_domain_ops aic5_irq_ops = { + .map = irq_map_generic_chip, + .xlate = aic5_irq_domain_xlate, +}; + +static int __init aic5_of_init(struct device_node *node, + struct device_node *parent, + int nirqs) +{ + struct irq_chip_generic *gc; + struct irq_domain *domain; + int nchips; + int i; + + if (nirqs > NR_AIC5_IRQS) + return -EINVAL; + + if (aic5_domain) + return -EEXIST; + + domain = aic_common_of_init(node, &aic5_irq_ops, "atmel-aic5", + nirqs); + if (IS_ERR(domain)) + return PTR_ERR(domain); + + aic5_domain = domain; + nchips = aic5_domain->revmap_size / 32; + for (i = 0; i < nchips; i++) { + gc = irq_get_domain_generic_chip(domain, i * 32); + + gc->chip_types[0].regs.eoi = AT91_AIC5_EOICR; + gc->chip_types[0].chip.irq_mask = aic5_mask; + gc->chip_types[0].chip.irq_unmask = aic5_unmask; + gc->chip_types[0].chip.irq_retrigger = aic5_retrigger; + gc->chip_types[0].chip.irq_set_type = aic5_set_type; + gc->chip_types[0].chip.irq_suspend = aic5_suspend; + gc->chip_types[0].chip.irq_resume = aic5_resume; + gc->chip_types[0].chip.irq_pm_shutdown = aic5_pm_shutdown; + } + + aic5_hw_init(domain); + set_handle_irq(aic5_handle); + + return 0; +} + +#define NR_SAMA5D3_IRQS 50 + +static int __init sama5d3_aic5_of_init(struct device_node *node, + struct device_node *parent) +{ + return aic5_of_init(node, parent, NR_SAMA5D3_IRQS); +} +IRQCHIP_DECLARE(sama5d3_aic5, "atmel,sama5d3-aic", sama5d3_aic5_of_init); From b2f579b58e93ded5916fb69a28cfc86e0ab951a6 Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Jul 2014 20:25:39 +0200 Subject: [PATCH 36/41] irqchip: atmel-aic: Add irq fixup infrastructure Add irq fixup infrastructure to handle IP blocks connected to shared irqs that are left in an unknown state when booting the kernel. In this case the IP block which has not masked its interrupt and has no driver loaded (either because it is not compiled or because it is not loaded yet) might generate spurious interrupts when another IP block request the shared irq. A good example of this case is the RTC block on which register configs are kept even after a shutdown (if a proper VDDcore is supplied), and thus might generate spurious interrupts when the platform is switched on. Signed-off-by: Boris BREZILLON Link: https://lkml.kernel.org/r/1405016741-2407-2-git-send-email-boris.brezillon@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-atmel-aic-common.c | 19 +++++++++++++++++++ drivers/irqchip/irq-atmel-aic-common.h | 2 ++ 2 files changed, 21 insertions(+) diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index 18b76fc03d53..4705bdbc6e7b 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -139,6 +139,25 @@ static void __init aic_common_ext_irq_of_init(struct irq_domain *domain) } } +void __init aic_common_irq_fixup(const struct of_device_id *matches) +{ + struct device_node *root = of_find_node_by_path("/"); + const struct of_device_id *match; + + if (!root) + return; + + match = of_match_node(matches, root); + of_node_put(root); + + if (match) { + void (*fixup)(struct device_node *) = match->data; + fixup(root); + } + + of_node_put(root); +} + struct irq_domain *__init aic_common_of_init(struct device_node *node, const struct irq_domain_ops *ops, const char *name, int nirqs) diff --git a/drivers/irqchip/irq-atmel-aic-common.h b/drivers/irqchip/irq-atmel-aic-common.h index c178557ab7e8..aa0a42c36a7f 100644 --- a/drivers/irqchip/irq-atmel-aic-common.h +++ b/drivers/irqchip/irq-atmel-aic-common.h @@ -32,4 +32,6 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, const struct irq_domain_ops *ops, const char *name, int nirqs); +void __init aic_common_irq_fixup(const struct of_device_id *matches); + #endif /* __IRQ_ATMEL_AIC_COMMON_H */ From 3d61467f9bab36aee786f762730b73565dbef3bf Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Jul 2014 20:25:40 +0200 Subject: [PATCH 37/41] irqchip: atmel-aic: Implement RTC irq fixup Provide an implementation to fix RTC irqs before enabling the irqchip. This was previously done in arch/arm/mach-at91/sysirq_mask.c but as we're trying to use standard implementation (IRQCHIP_DECLARE and automatic call of irqchip_init within arch/arm/kernel/irq.c) we need to do those fixups in the irqchip driver. Signed-off-by: Boris BREZILLON Link: https://lkml.kernel.org/r/1405016741-2407-3-git-send-email-boris.brezillon@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-atmel-aic-common.c | 28 ++++++++++++++++++++++++++ drivers/irqchip/irq-atmel-aic-common.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index 4705bdbc6e7b..6ae3cdee0681 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -139,6 +139,34 @@ static void __init aic_common_ext_irq_of_init(struct irq_domain *domain) } } +#define AT91_RTC_IDR 0x24 +#define AT91_RTC_IMR 0x28 +#define AT91_RTC_IRQ_MASK 0x1f + +void __init aic_common_rtc_irq_fixup(struct device_node *root) +{ + struct device_node *np; + void __iomem *regs; + + np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc"); + if (!np) + np = of_find_compatible_node(root, NULL, + "atmel,at91sam9x5-rtc"); + + if (!np) + return; + + regs = of_iomap(np, 0); + of_node_put(np); + + if (!regs) + return; + + writel(AT91_RTC_IRQ_MASK, regs + AT91_RTC_IDR); + + iounmap(regs); +} + void __init aic_common_irq_fixup(const struct of_device_id *matches) { struct device_node *root = of_find_node_by_path("/"); diff --git a/drivers/irqchip/irq-atmel-aic-common.h b/drivers/irqchip/irq-atmel-aic-common.h index aa0a42c36a7f..90aa00e918d6 100644 --- a/drivers/irqchip/irq-atmel-aic-common.h +++ b/drivers/irqchip/irq-atmel-aic-common.h @@ -32,6 +32,8 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, const struct irq_domain_ops *ops, const char *name, int nirqs); +void __init aic_common_rtc_irq_fixup(struct device_node *root); + void __init aic_common_irq_fixup(const struct of_device_id *matches); #endif /* __IRQ_ATMEL_AIC_COMMON_H */ From 6704d12d688192366f3a70e6f8a85cb5a869cd5a Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Thu, 10 Jul 2014 20:25:41 +0200 Subject: [PATCH 38/41] irqchip: atmel-aic: Define irq fixups for atmel SoCs Define SoCs that need irq fixups before enabling the AIC irqchip. At the moment we're only fixing irq generated by the RTC block, but other fixups will be added later on. Signed-off-by: Boris BREZILLON Link: https://lkml.kernel.org/r/1405016741-2407-4-git-send-email-boris.brezillon@free-electrons.com Signed-off-by: Jason Cooper --- drivers/irqchip/irq-atmel-aic.c | 15 +++++++++++++++ drivers/irqchip/irq-atmel-aic5.c | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c index b15b5667991c..a82869e9fb26 100644 --- a/drivers/irqchip/irq-atmel-aic.c +++ b/drivers/irqchip/irq-atmel-aic.c @@ -211,6 +211,19 @@ static const struct irq_domain_ops aic_irq_ops = { .xlate = aic_irq_domain_xlate, }; +static void __init at91sam9_aic_irq_fixup(struct device_node *root) +{ + aic_common_rtc_irq_fixup(root); +} + +static const struct of_device_id __initdata aic_irq_fixups[] = { + { .compatible = "atmel,at91sam9g45", .data = at91sam9_aic_irq_fixup }, + { .compatible = "atmel,at91sam9n12", .data = at91sam9_aic_irq_fixup }, + { .compatible = "atmel,at91sam9rl", .data = at91sam9_aic_irq_fixup }, + { .compatible = "atmel,at91sam9x5", .data = at91sam9_aic_irq_fixup }, + { /* sentinel */ }, +}; + static int __init aic_of_init(struct device_node *node, struct device_node *parent) { @@ -225,6 +238,8 @@ static int __init aic_of_init(struct device_node *node, if (IS_ERR(domain)) return PTR_ERR(domain); + aic_common_irq_fixup(aic_irq_fixups); + aic_domain = domain; gc = irq_get_domain_generic_chip(domain, 0); diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index c9c753adc15d..edb227081524 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -290,6 +290,16 @@ static const struct irq_domain_ops aic5_irq_ops = { .xlate = aic5_irq_domain_xlate, }; +static void __init sama5d3_aic_irq_fixup(struct device_node *root) +{ + aic_common_rtc_irq_fixup(root); +} + +static const struct of_device_id __initdata aic5_irq_fixups[] = { + { .compatible = "atmel,sama5d3", .data = sama5d3_aic_irq_fixup }, + { /* sentinel */ }, +}; + static int __init aic5_of_init(struct device_node *node, struct device_node *parent, int nirqs) @@ -310,6 +320,8 @@ static int __init aic5_of_init(struct device_node *node, if (IS_ERR(domain)) return PTR_ERR(domain); + aic_common_irq_fixup(aic5_irq_fixups); + aic5_domain = domain; nchips = aic5_domain->revmap_size / 32; for (i = 0; i < nchips; i++) { From 4fae4e7624653ef498d0e2a38f00620b9701ab04 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Thu, 24 Jul 2014 15:39:21 +0200 Subject: [PATCH 39/41] irq: Warn when shared interrupts do not match on NO_SUSPEND When suspend_device_irqs() iterates all descriptors, its pointless if one has NO_SUSPEND set while another has not. Validate on request_irq() that NO_SUSPEND state maches for SHARED interrupts. Signed-off-by: Peter Zijlstra Acked-by: "Rafael J. Wysocki" Link: http://lkml.kernel.org/r/20140724133921.GY6758@twins.programming.kicks-ass.net Signed-off-by: Thomas Gleixner --- kernel/irq/manage.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 88657d7bc9dd..27a1fe028afb 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1077,9 +1077,12 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) * set the trigger type must match. Also all must * agree on ONESHOT. */ + +#define IRQF_MISMATCH \ + (IRQF_TRIGGER_MASK | IRQF_ONESHOT | IRQF_NO_SUSPEND) + if (!((old->flags & new->flags) & IRQF_SHARED) || - ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) || - ((old->flags ^ new->flags) & IRQF_ONESHOT)) + ((old->flags ^ new->flags) & IRQF_MISMATCH)) goto mismatch; /* All handlers must agree on per-cpuness */ From 21d1f908d39559b013ea857c1685253476b95a4a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 31 Jul 2014 20:57:10 +0200 Subject: [PATCH 40/41] Revert "PM / sleep / irq: Do not suspend wakeup interrupts" This reverts commit d709f7bcbb3ab01704fa7b37a2e4b981cf3783c1. Undo, because it might break exisiting functionality. Requested-by: Rafael J. Wysocki Signed-off-by: Thomas Gleixner --- kernel/irq/manage.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 27a1fe028afb..53e944811ca6 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -385,8 +385,7 @@ setup_affinity(unsigned int irq, struct irq_desc *desc, struct cpumask *mask) void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend) { if (suspend) { - if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND) || - irqd_has_set(&desc->irq_data, IRQD_WAKEUP_STATE)) + if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND)) return; desc->istate |= IRQS_SUSPENDED; } From c6f1224573c3b609bd8073b39f496637a16cc06f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Thu, 31 Jul 2014 20:58:28 +0200 Subject: [PATCH 41/41] Revert "irq: Warn when shared interrupts do not match on NO_SUSPEND" This reverts commit 4fae4e7624653ef498d0e2a38f00620b9701ab04. Undo because it breaks working systems. Requested-by: Rafael J. Wysocki Signed-off-by: Thomas Gleixner --- kernel/irq/manage.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 53e944811ca6..3dc6a61bf06a 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1076,12 +1076,9 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) * set the trigger type must match. Also all must * agree on ONESHOT. */ - -#define IRQF_MISMATCH \ - (IRQF_TRIGGER_MASK | IRQF_ONESHOT | IRQF_NO_SUSPEND) - if (!((old->flags & new->flags) & IRQF_SHARED) || - ((old->flags ^ new->flags) & IRQF_MISMATCH)) + ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK) || + ((old->flags ^ new->flags) & IRQF_ONESHOT)) goto mismatch; /* All handlers must agree on per-cpuness */