From 41a83e06e2bb9ac46731681fd44d1e6ab184dac5 Mon Sep 17 00:00:00 2001 From: Joel Porquet Date: Tue, 7 Jul 2015 17:11:46 -0400 Subject: irqchip: Prepare for local stub header removal The IRQCHIP_DECLARE macro moved to to 'include/linux/irqchip.h', so the local irqchip.h became an empty shell, which solely includes include/linux/irqchip.h Include the global header in all irqchip drivers instead of the local header, so we can remove it. Signed-off-by: Joel Porquet Cc: vgupta@synopsys.com Cc: monstr@monstr.eu Cc: ralf@linux-mips.org Cc: jason@lakedaemon.net Link: http://lkml.kernel.org/r/1882096.X39jVG8e0D@joel-zenbook Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm7120-l2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/irqchip/irq-bcm7120-l2.c') diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 3ba5cc780fcb..7de378e98cf2 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -26,10 +26,9 @@ #include #include #include +#include #include -#include "irqchip.h" - /* Register offset in the L2 interrupt controller */ #define IRQEN 0x00 #define IRQSTAT 0x04 -- cgit v1.2.3 From 99e32ab1732e8e66c775087f5361fca235bbee0e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 21 Jun 2015 21:10:51 +0200 Subject: irqchip/bcm7120-l2: Consolidate chained IRQ handler install/remove Chained irq handlers usually set up handler data as well. We now have a function to set both under irq_desc->lock. Replace the two calls with one. Search and conversion was done with coccinelle: @@ expression E1, E2, E3; @@ ( -if (irq_set_handler_data(E1, E2) != 0) - BUG(); | -irq_set_handler_data(E1, E2); ) -irq_set_chained_handler(E1, E3); +irq_set_chained_handler_and_data(E1, E3, E2); @@ expression E1, E2, E3; @@ ( -if (irq_set_handler_data(E1, E2) != 0) - BUG(); ... | -irq_set_handler_data(E1, E2); ... ) -irq_set_chained_handler(E1, E3); +irq_set_chained_handler_and_data(E1, E3, E2); Reported-by: Russell King Signed-off-by: Thomas Gleixner Cc: Julia Lawall Cc: Kevin Cernekee Cc: Florian Fainelli Cc: Thomas Gleixner Cc: Jason Cooper Cc: linux-mips@linux-mips.org --- drivers/irqchip/irq-bcm7120-l2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/irqchip/irq-bcm7120-l2.c') diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 7de378e98cf2..88c9719e5d33 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -130,8 +130,8 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, } } - irq_set_handler_data(parent_irq, data); - irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); + irq_set_chained_handler_and_data(parent_irq, + bcm7120_l2_intc_irq_handle, data); return 0; } -- cgit v1.2.3 From fd537766715e9b6bf7ff07abb22f4817201433db Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 22 Jul 2015 16:21:40 -0700 Subject: irqchip/bcm7120-l2: Perform suspend/resume even without installed child IRQs Make use of the new irq_chip_generic suspend/resume callbacks. This is required because if there are no installed child IRQs for this chip, the irq_chip::irq_{suspend,resume} functions will not be called. However, we still need to save/restore the forwarding mask, to enable the top-level GIC interrupt; otherwise, we lose UART output after S3 resume. In addition to refactoring the callbacks, we have to self-initialize the mask cache, since the genirq core also doesn't initialize this until the first child IRQ is installed. The original problem report is described in extra detail here: http://lkml.kernel.org/g/20150619224123.GL4917@ld-irv-0074 Signed-off-by: Brian Norris Tested-by: Florian Fainelli Cc: Gregory Fong Cc: bcm-kernel-feedback-list@broadcom.com Cc: linux-mips@linux-mips.org Cc: Kevin Cernekee Cc: Jason Cooper Link: http://lkml.kernel.org/r/1437607300-40858-2-git-send-email-computersforpeace@gmail.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm7120-l2.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers/irqchip/irq-bcm7120-l2.c') diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index 88c9719e5d33..c885a5c4632a 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -80,11 +80,10 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) chained_irq_exit(chip, desc); } -static void bcm7120_l2_intc_suspend(struct irq_data *d) +static void bcm7120_l2_intc_suspend(struct irq_chip_generic *gc) { - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - struct irq_chip_type *ct = irq_data_get_chip_type(d); struct bcm7120_l2_intc_data *b = gc->private; + struct irq_chip_type *ct = gc->chip_types; irq_gc_lock(gc); if (b->can_wake) @@ -93,10 +92,9 @@ static void bcm7120_l2_intc_suspend(struct irq_data *d) irq_gc_unlock(gc); } -static void bcm7120_l2_intc_resume(struct irq_data *d) +static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc) { - struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); - struct irq_chip_type *ct = irq_data_get_chip_type(d); + struct irq_chip_type *ct = gc->chip_types; /* Restore the saved mask */ irq_gc_lock(gc); @@ -279,8 +277,15 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, ct->chip.irq_mask = irq_gc_mask_clr_bit; ct->chip.irq_unmask = irq_gc_mask_set_bit; ct->chip.irq_ack = irq_gc_noop; - ct->chip.irq_suspend = bcm7120_l2_intc_suspend; - ct->chip.irq_resume = bcm7120_l2_intc_resume; + gc->suspend = bcm7120_l2_intc_suspend; + gc->resume = bcm7120_l2_intc_resume; + + /* + * Initialize mask-cache, in case we need it for + * saving/restoring fwd mask even w/o any child interrupts + * installed + */ + gc->mask_cache = irq_reg_readl(gc, ct->regs.mask); if (data->can_wake) { /* This IRQ chip can wake the system, set all -- cgit v1.2.3 From 0aef3997e12a10d4dfb6e01133e2fe478b9aa5eb Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 23 Jul 2015 15:52:21 -0700 Subject: irqchip/bcm7120-l2: Fix interrupt status for multiple parent IRQs Our irq-bcm7120-l2 interrupt controller driver utilizes the same handler function for the different parent interrupts it services: UPG_MAIN, UPG_BSC for instance. The problem is that function reads the IRQSTAT register which can combine interrupt causes for different parent interrupts, such that we can end-up in the following situation: - CPU takes an interrupt - bcm7120_l2_intc_irq_handle() reads IRQSTAT - generic_handle_irq() is invoked - there are still pending interrupts flagged in IRQSTAT from a different parent - handle_bad_irq() is invoked for these since they come from a different irq_desc/irq In order to fix this, make sure that we always mask IRQSTAT with the appropriate bits that correspond go the parent interrupt source this is coming from. To simplify things, associate an unique structure per parent interrupt handler to avoid multiplying the number of lookups. Fixes: a5042de2688d ("irqchip: bcm7120-l2: Add Broadcom BCM7120-style Level 2 interrupt controller") Signed-off-by: Florian Fainelli Cc: linux-mips@linux-mips.org Cc: cernekee@gmail.com Cc: jason@lakedaemon.net Cc: bcm-kernel-feedback-list@broadcom.com Cc: gregory.0xf0@gmail.com Cc: computersforpeace@gmail.com Link: http://lkml.kernel.org/r/1437691941-3100-1-git-send-email-f.fainelli@gmail.com Signed-off-by: Thomas Gleixner --- drivers/irqchip/irq-bcm7120-l2.c | 52 ++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 13 deletions(-) (limited to 'drivers/irqchip/irq-bcm7120-l2.c') diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index c885a5c4632a..d3f976913a6f 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c @@ -37,6 +37,11 @@ #define MAX_MAPPINGS (MAX_WORDS * 2) #define IRQS_PER_WORD 32 +struct bcm7120_l1_intc_data { + struct bcm7120_l2_intc_data *b; + u32 irq_map_mask[MAX_WORDS]; +}; + struct bcm7120_l2_intc_data { unsigned int n_words; void __iomem *map_base[MAX_MAPPINGS]; @@ -46,14 +51,15 @@ struct bcm7120_l2_intc_data { struct irq_domain *domain; bool can_wake; u32 irq_fwd_mask[MAX_WORDS]; - u32 irq_map_mask[MAX_WORDS]; + struct bcm7120_l1_intc_data *l1_data; int num_parent_irqs; const __be32 *map_mask_prop; }; static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) { - struct bcm7120_l2_intc_data *b = irq_desc_get_handler_data(desc); + struct bcm7120_l1_intc_data *data = irq_desc_get_handler_data(desc); + struct bcm7120_l2_intc_data *b = data->b; struct irq_chip *chip = irq_desc_get_chip(desc); unsigned int idx; @@ -68,7 +74,8 @@ static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) irq_gc_lock(gc); pending = irq_reg_readl(gc, b->stat_offset[idx]) & - gc->mask_cache; + gc->mask_cache & + data->irq_map_mask[idx]; irq_gc_unlock(gc); for_each_set_bit(hwirq, &pending, IRQS_PER_WORD) { @@ -104,8 +111,9 @@ static void bcm7120_l2_intc_resume(struct irq_chip_generic *gc) static int bcm7120_l2_intc_init_one(struct device_node *dn, struct bcm7120_l2_intc_data *data, - int irq) + int irq, u32 *valid_mask) { + struct bcm7120_l1_intc_data *l1_data = &data->l1_data[irq]; int parent_irq; unsigned int idx; @@ -117,20 +125,28 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, /* For multiple parent IRQs with multiple words, this looks like: * + * + * We need to associate a given parent interrupt with its corresponding + * map_mask in order to mask the status register with it because we + * have the same handler being called for multiple parent interrupts. + * + * This is typically something needed on BCM7xxx (STB chips). */ for (idx = 0; idx < data->n_words; idx++) { if (data->map_mask_prop) { - data->irq_map_mask[idx] |= + l1_data->irq_map_mask[idx] |= be32_to_cpup(data->map_mask_prop + irq * data->n_words + idx); } else { - data->irq_map_mask[idx] = 0xffffffff; + l1_data->irq_map_mask[idx] = 0xffffffff; } + valid_mask[idx] |= l1_data->irq_map_mask[idx]; } - irq_set_chained_handler_and_data(parent_irq, - bcm7120_l2_intc_irq_handle, data); + l1_data->b = data; + irq_set_chained_handler_and_data(parent_irq, + bcm7120_l2_intc_irq_handle, l1_data); return 0; } @@ -211,6 +227,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, struct irq_chip_type *ct; int ret = 0; unsigned int idx, irq, flags; + u32 valid_mask[MAX_WORDS] = { }; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) @@ -223,9 +240,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, goto out_unmap; } + data->l1_data = kcalloc(data->num_parent_irqs, sizeof(*data->l1_data), + GFP_KERNEL); + if (!data->l1_data) { + ret = -ENOMEM; + goto out_free_l1_data; + } + ret = iomap_regs_fn(dn, data); if (ret < 0) - goto out_unmap; + goto out_free_l1_data; for (idx = 0; idx < data->n_words; idx++) { __raw_writel(data->irq_fwd_mask[idx], @@ -234,16 +258,16 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, } for (irq = 0; irq < data->num_parent_irqs; irq++) { - ret = bcm7120_l2_intc_init_one(dn, data, irq); + ret = bcm7120_l2_intc_init_one(dn, data, irq, valid_mask); if (ret) - goto out_unmap; + goto out_free_l1_data; } data->domain = irq_domain_add_linear(dn, IRQS_PER_WORD * data->n_words, &irq_generic_chip_ops, NULL); if (!data->domain) { ret = -ENOMEM; - goto out_unmap; + goto out_free_l1_data; } /* MIPS chips strapped for BE will automagically configure the @@ -267,7 +291,7 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, irq = idx * IRQS_PER_WORD; gc = irq_get_domain_generic_chip(data->domain, irq); - gc->unused = 0xffffffff & ~data->irq_map_mask[idx]; + gc->unused = 0xffffffff & ~valid_mask[idx]; gc->private = data; ct = gc->chip_types; @@ -304,6 +328,8 @@ int __init bcm7120_l2_intc_probe(struct device_node *dn, out_free_domain: irq_domain_remove(data->domain); +out_free_l1_data: + kfree(data->l1_data); out_unmap: for (idx = 0; idx < MAX_MAPPINGS; idx++) { if (data->map_base[idx]) -- cgit v1.2.3