diff options
Diffstat (limited to 'arch/powerpc/sysdev/mpic.c')
| -rw-r--r-- | arch/powerpc/sysdev/mpic.c | 1037 |
1 files changed, 680 insertions, 357 deletions
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c index 7c1342618a3..be33c9768ea 100644 --- a/arch/powerpc/sysdev/mpic.c +++ b/arch/powerpc/sysdev/mpic.c @@ -6,6 +6,7 @@ * with various broken implementations of this HW. * * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. + * Copyright 2010-2012 Freescale Semiconductor, Inc. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive @@ -27,6 +28,8 @@ #include <linux/spinlock.h> #include <linux/pci.h> #include <linux/slab.h> +#include <linux/syscore_ops.h> +#include <linux/ratelimit.h> #include <asm/ptrace.h> #include <asm/signal.h> @@ -45,6 +48,12 @@ #define DBG(fmt...) #endif +struct bus_type mpic_subsys = { + .name = "mpic", + .dev_name = "mpic", +}; +EXPORT_SYMBOL_GPL(mpic_subsys); + static struct mpic *mpics; static struct mpic *mpic_primary; static DEFINE_RAW_SPINLOCK(mpic_lock); @@ -147,6 +156,16 @@ static u32 mpic_infos[][MPIC_IDX_END] = { #endif /* CONFIG_MPIC_WEIRD */ +static inline unsigned int mpic_processor_id(struct mpic *mpic) +{ + unsigned int cpu = 0; + + if (!(mpic->flags & MPIC_SECONDARY)) + cpu = hard_smp_processor_id(); + + return cpu; +} + /* * Register accessor functions */ @@ -208,21 +227,38 @@ static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 valu _mpic_write(mpic->reg_type, &mpic->gregs, offset, value); } +static inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm) +{ + return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE + + (tm & 3) * MPIC_INFO(TIMER_STRIDE); +} + +static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm) +{ + unsigned int offset = mpic_tm_offset(mpic, tm) + + MPIC_INFO(TIMER_VECTOR_PRI); + + return _mpic_read(mpic->reg_type, &mpic->tmregs, offset); +} + +static inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value) +{ + unsigned int offset = mpic_tm_offset(mpic, tm) + + MPIC_INFO(TIMER_VECTOR_PRI); + + _mpic_write(mpic->reg_type, &mpic->tmregs, offset, value); +} + static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) { - unsigned int cpu = 0; + unsigned int cpu = mpic_processor_id(mpic); - if (mpic->flags & MPIC_PRIMARY) - cpu = hard_smp_processor_id(); return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg); } static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value) { - unsigned int cpu = 0; - - if (mpic->flags & MPIC_PRIMARY) - cpu = hard_smp_processor_id(); + unsigned int cpu = mpic_processor_id(mpic); _mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value); } @@ -263,6 +299,8 @@ static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, #define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v)) #define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) #define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) +#define mpic_tm_read(i) _mpic_tm_read(mpic,(i)) +#define mpic_tm_write(i,v) _mpic_tm_write(mpic,(i),(v)) #define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) #define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v)) #define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r)) @@ -283,29 +321,25 @@ static void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr, } #ifdef CONFIG_PPC_DCR -static void _mpic_map_dcr(struct mpic *mpic, struct device_node *node, - struct mpic_reg_bank *rb, +static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb, unsigned int offset, unsigned int size) { - const u32 *dbasep; - - dbasep = of_get_property(node, "dcr-reg", NULL); - - rb->dhost = dcr_map(node, *dbasep + offset, size); + phys_addr_t phys_addr = dcr_resource_start(mpic->node, 0); + rb->dhost = dcr_map(mpic->node, phys_addr + offset, size); BUG_ON(!DCR_MAP_OK(rb->dhost)); } -static inline void mpic_map(struct mpic *mpic, struct device_node *node, +static inline void mpic_map(struct mpic *mpic, phys_addr_t phys_addr, struct mpic_reg_bank *rb, unsigned int offset, unsigned int size) { if (mpic->flags & MPIC_USES_DCR) - _mpic_map_dcr(mpic, node, rb, offset, size); + _mpic_map_dcr(mpic, rb, offset, size); else _mpic_map_mmio(mpic, phys_addr, rb, offset, size); } #else /* CONFIG_PPC_DCR */ -#define mpic_map(m,n,p,b,o,s) _mpic_map_mmio(m,p,b,o,s) +#define mpic_map(m,p,b,o,s) _mpic_map_mmio(m,p,b,o,s) #endif /* !CONFIG_PPC_DCR */ @@ -356,7 +390,7 @@ static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source) } static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, - unsigned int irqflags) + bool level) { struct mpic_irq_fixup *fixup = &mpic->fixups[source]; unsigned long flags; @@ -365,14 +399,14 @@ static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, if (fixup->base == NULL) return; - DBG("startup_ht_interrupt(0x%x, 0x%x) index: %d\n", - source, irqflags, fixup->index); + DBG("startup_ht_interrupt(0x%x) index: %d\n", + source, fixup->index); raw_spin_lock_irqsave(&mpic->fixup_lock, flags); /* Enable and configure */ writeb(0x10 + 2 * fixup->index, fixup->base + 2); tmp = readl(fixup->base + 4); tmp &= ~(0x23U); - if (irqflags & IRQ_LEVEL) + if (level) tmp |= 0x22; writel(tmp, fixup->base + 4); raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags); @@ -384,8 +418,7 @@ static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, #endif } -static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, - unsigned int irqflags) +static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source) { struct mpic_irq_fixup *fixup = &mpic->fixups[source]; unsigned long flags; @@ -394,7 +427,7 @@ static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, if (fixup->base == NULL) return; - DBG("shutdown_ht_interrupt(0x%x, 0x%x)\n", source, irqflags); + DBG("shutdown_ht_interrupt(0x%x)\n", source); /* Disable */ raw_spin_lock_irqsave(&mpic->fixup_lock, flags); @@ -502,7 +535,7 @@ static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, mpic->fixups[irq].data = readl(base + 4) | 0x80000000; } } - + static void __init mpic_scan_ht_pics(struct mpic *mpic) { @@ -567,61 +600,26 @@ static void __init mpic_scan_ht_pics(struct mpic *mpic) #endif /* CONFIG_MPIC_U3_HT_IRQS */ -#ifdef CONFIG_SMP -static int irq_choose_cpu(const struct cpumask *mask) -{ - int cpuid; - - if (cpumask_equal(mask, cpu_all_mask)) { - static int irq_rover = 0; - static DEFINE_RAW_SPINLOCK(irq_rover_lock); - unsigned long flags; - - /* Round-robin distribution... */ - do_round_robin: - raw_spin_lock_irqsave(&irq_rover_lock, flags); - - irq_rover = cpumask_next(irq_rover, cpu_online_mask); - if (irq_rover >= nr_cpu_ids) - irq_rover = cpumask_first(cpu_online_mask); - - cpuid = irq_rover; - - raw_spin_unlock_irqrestore(&irq_rover_lock, flags); - } else { - cpuid = cpumask_first_and(mask, cpu_online_mask); - if (cpuid >= nr_cpu_ids) - goto do_round_robin; - } - - return get_hard_smp_processor_id(cpuid); -} -#else -static int irq_choose_cpu(const struct cpumask *mask) -{ - return hard_smp_processor_id(); -} -#endif - -#define mpic_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq) - /* Find an mpic associated with a given linux interrupt */ static struct mpic *mpic_find(unsigned int irq) { if (irq < NUM_ISA_INTERRUPTS) return NULL; - return irq_to_desc(irq)->chip_data; + return irq_get_chip_data(irq); } /* Determine if the linux irq is an IPI */ -static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int irq) +static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int src) { - unsigned int src = mpic_irq_to_hw(irq); - return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); } +/* Determine if the linux irq is a timer */ +static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int src) +{ + return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]); +} /* Convert a cpu mask from logical to physical cpu numbers. */ static inline u32 mpic_physmask(u32 cpumask) @@ -629,23 +627,29 @@ static inline u32 mpic_physmask(u32 cpumask) int i; u32 mask = 0; - for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1) + for (i = 0; i < min(32, NR_CPUS); ++i, cpumask >>= 1) mask |= (cpumask & 1) << get_hard_smp_processor_id(i); return mask; } #ifdef CONFIG_SMP /* Get the mpic structure from the IPI number */ -static inline struct mpic * mpic_from_ipi(unsigned int ipi) +static inline struct mpic * mpic_from_ipi(struct irq_data *d) { - return irq_to_desc(ipi)->chip_data; + return irq_data_get_irq_chip_data(d); } #endif /* Get the mpic structure from the irq number */ static inline struct mpic * mpic_from_irq(unsigned int irq) { - return irq_to_desc(irq)->chip_data; + return irq_get_chip_data(irq); +} + +/* Get the mpic structure from the irq data */ +static inline struct mpic * mpic_from_irq_data(struct irq_data *d) +{ + return irq_data_get_irq_chip_data(d); } /* Send an EOI */ @@ -660,13 +664,13 @@ static inline void mpic_eoi(struct mpic *mpic) */ -void mpic_unmask_irq(unsigned int irq) +void mpic_unmask_irq(struct irq_data *d) { unsigned int loops = 100000; - struct mpic *mpic = mpic_from_irq(irq); - unsigned int src = mpic_irq_to_hw(irq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); - DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src); + DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, d->irq, src); mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & @@ -674,19 +678,20 @@ void mpic_unmask_irq(unsigned int irq) /* make sure mask gets to controller before we return to user */ do { if (!loops--) { - printk(KERN_ERR "mpic_enable_irq timeout\n"); + printk(KERN_ERR "%s: timeout on hwirq %u\n", + __func__, src); break; } } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK); } -void mpic_mask_irq(unsigned int irq) +void mpic_mask_irq(struct irq_data *d) { unsigned int loops = 100000; - struct mpic *mpic = mpic_from_irq(irq); - unsigned int src = mpic_irq_to_hw(irq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); - DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src); + DBG("%s: disable_irq: %d (src %d)\n", mpic->name, d->irq, src); mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) | @@ -695,18 +700,19 @@ void mpic_mask_irq(unsigned int irq) /* make sure mask gets to controller before we return to user */ do { if (!loops--) { - printk(KERN_ERR "mpic_enable_irq timeout\n"); + printk(KERN_ERR "%s: timeout on hwirq %u\n", + __func__, src); break; } } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK)); } -void mpic_end_irq(unsigned int irq) +void mpic_end_irq(struct irq_data *d) { - struct mpic *mpic = mpic_from_irq(irq); + struct mpic *mpic = mpic_from_irq_data(d); #ifdef DEBUG_IRQ - DBG("%s: end_irq: %d\n", mpic->name, irq); + DBG("%s: end_irq: %d\n", mpic->name, d->irq); #endif /* We always EOI on end_irq() even for edge interrupts since that * should only lower the priority, the MPIC should have properly @@ -718,51 +724,51 @@ void mpic_end_irq(unsigned int irq) #ifdef CONFIG_MPIC_U3_HT_IRQS -static void mpic_unmask_ht_irq(unsigned int irq) +static void mpic_unmask_ht_irq(struct irq_data *d) { - struct mpic *mpic = mpic_from_irq(irq); - unsigned int src = mpic_irq_to_hw(irq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); - mpic_unmask_irq(irq); + mpic_unmask_irq(d); - if (irq_to_desc(irq)->status & IRQ_LEVEL) + if (irqd_is_level_type(d)) mpic_ht_end_irq(mpic, src); } -static unsigned int mpic_startup_ht_irq(unsigned int irq) +static unsigned int mpic_startup_ht_irq(struct irq_data *d) { - struct mpic *mpic = mpic_from_irq(irq); - unsigned int src = mpic_irq_to_hw(irq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); - mpic_unmask_irq(irq); - mpic_startup_ht_interrupt(mpic, src, irq_to_desc(irq)->status); + mpic_unmask_irq(d); + mpic_startup_ht_interrupt(mpic, src, irqd_is_level_type(d)); return 0; } -static void mpic_shutdown_ht_irq(unsigned int irq) +static void mpic_shutdown_ht_irq(struct irq_data *d) { - struct mpic *mpic = mpic_from_irq(irq); - unsigned int src = mpic_irq_to_hw(irq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); - mpic_shutdown_ht_interrupt(mpic, src, irq_to_desc(irq)->status); - mpic_mask_irq(irq); + mpic_shutdown_ht_interrupt(mpic, src); + mpic_mask_irq(d); } -static void mpic_end_ht_irq(unsigned int irq) +static void mpic_end_ht_irq(struct irq_data *d) { - struct mpic *mpic = mpic_from_irq(irq); - unsigned int src = mpic_irq_to_hw(irq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); #ifdef DEBUG_IRQ - DBG("%s: end_irq: %d\n", mpic->name, irq); + DBG("%s: end_irq: %d\n", mpic->name, d->irq); #endif /* We always EOI on end_irq() even for edge interrupts since that * should only lower the priority, the MPIC should have properly * latched another edge interrupt coming in anyway */ - if (irq_to_desc(irq)->status & IRQ_LEVEL) + if (irqd_is_level_type(d)) mpic_ht_end_irq(mpic, src); mpic_eoi(mpic); } @@ -770,59 +776,73 @@ static void mpic_end_ht_irq(unsigned int irq) #ifdef CONFIG_SMP -static void mpic_unmask_ipi(unsigned int irq) +static void mpic_unmask_ipi(struct irq_data *d) { - struct mpic *mpic = mpic_from_ipi(irq); - unsigned int src = mpic_irq_to_hw(irq) - mpic->ipi_vecs[0]; + struct mpic *mpic = mpic_from_ipi(d); + unsigned int src = virq_to_hw(d->irq) - mpic->ipi_vecs[0]; - DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src); + DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, d->irq, src); mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK); } -static void mpic_mask_ipi(unsigned int irq) +static void mpic_mask_ipi(struct irq_data *d) { /* NEVER disable an IPI... that's just plain wrong! */ } -static void mpic_end_ipi(unsigned int irq) +static void mpic_end_ipi(struct irq_data *d) { - struct mpic *mpic = mpic_from_ipi(irq); + struct mpic *mpic = mpic_from_ipi(d); /* * IPIs are marked IRQ_PER_CPU. This has the side effect of * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from * applying to them. We EOI them late to avoid re-entering. - * We mark IPI's with IRQF_DISABLED as they must run with - * irqs disabled. */ mpic_eoi(mpic); } #endif /* CONFIG_SMP */ -int mpic_set_affinity(unsigned int irq, const struct cpumask *cpumask) +static void mpic_unmask_tm(struct irq_data *d) +{ + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; + + DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, d->irq, src); + mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK); + mpic_tm_read(src); +} + +static void mpic_mask_tm(struct irq_data *d) +{ + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; + + mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK); + mpic_tm_read(src); +} + +int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, + bool force) { - struct mpic *mpic = mpic_from_irq(irq); - unsigned int src = mpic_irq_to_hw(irq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); if (mpic->flags & MPIC_SINGLE_DEST_CPU) { int cpuid = irq_choose_cpu(cpumask); mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); } else { - cpumask_var_t tmp; + u32 mask = cpumask_bits(cpumask)[0]; - alloc_cpumask_var(&tmp, GFP_KERNEL); - - cpumask_and(tmp, cpumask, cpu_online_mask); + mask &= cpumask_bits(cpu_online_mask)[0]; mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), - mpic_physmask(cpumask_bits(tmp)[0])); - - free_cpumask_var(tmp); + mpic_physmask(mask)); } - return 0; + return IRQ_SET_MASK_OK; } static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type) @@ -846,56 +866,92 @@ static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type) } } -int mpic_set_irq_type(unsigned int virq, unsigned int flow_type) +int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) { - struct mpic *mpic = mpic_from_irq(virq); - unsigned int src = mpic_irq_to_hw(virq); - struct irq_desc *desc = irq_to_desc(virq); + struct mpic *mpic = mpic_from_irq_data(d); + unsigned int src = irqd_to_hwirq(d); unsigned int vecpri, vold, vnew; DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n", - mpic, virq, src, flow_type); + mpic, d->irq, src, flow_type); - if (src >= mpic->irq_count) + if (src >= mpic->num_sources) return -EINVAL; + vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); + + /* We don't support "none" type */ if (flow_type == IRQ_TYPE_NONE) - if (mpic->senses && src < mpic->senses_count) - flow_type = mpic->senses[src]; - if (flow_type == IRQ_TYPE_NONE) - flow_type = IRQ_TYPE_LEVEL_LOW; + flow_type = IRQ_TYPE_DEFAULT; + + /* Default: read HW settings */ + if (flow_type == IRQ_TYPE_DEFAULT) { + int vold_ps; + + vold_ps = vold & (MPIC_INFO(VECPRI_POLARITY_MASK) | + MPIC_INFO(VECPRI_SENSE_MASK)); + + if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) | + MPIC_INFO(VECPRI_POLARITY_POSITIVE))) + flow_type = IRQ_TYPE_EDGE_RISING; + else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) | + MPIC_INFO(VECPRI_POLARITY_NEGATIVE))) + flow_type = IRQ_TYPE_EDGE_FALLING; + else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) | + MPIC_INFO(VECPRI_POLARITY_POSITIVE))) + flow_type = IRQ_TYPE_LEVEL_HIGH; + else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) | + MPIC_INFO(VECPRI_POLARITY_NEGATIVE))) + flow_type = IRQ_TYPE_LEVEL_LOW; + else + WARN_ONCE(1, "mpic: unknown IRQ type %d\n", vold); + } - desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); - desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; - if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) - desc->status |= IRQ_LEVEL; + /* Apply to irq desc */ + irqd_set_trigger_type(d, flow_type); + /* Apply to HW */ if (mpic_is_ht_interrupt(mpic, src)) vecpri = MPIC_VECPRI_POLARITY_POSITIVE | MPIC_VECPRI_SENSE_EDGE; else vecpri = mpic_type_to_vecpri(mpic, flow_type); - vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) | MPIC_INFO(VECPRI_SENSE_MASK)); vnew |= vecpri; if (vold != vnew) mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew); + return IRQ_SET_MASK_OK_NOCOPY; +} + +static int mpic_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct irq_desc *desc = container_of(d, struct irq_desc, irq_data); + struct mpic *mpic = mpic_from_irq_data(d); + + if (!(mpic->flags & MPIC_FSL)) + return -ENXIO; + + if (on) + desc->action->flags |= IRQF_NO_SUSPEND; + else + desc->action->flags &= ~IRQF_NO_SUSPEND; + return 0; } void mpic_set_vector(unsigned int virq, unsigned int vector) { struct mpic *mpic = mpic_from_irq(virq); - unsigned int src = mpic_irq_to_hw(virq); + unsigned int src = virq_to_hw(virq); unsigned int vecpri; DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n", mpic, virq, src, vector); - if (src >= mpic->irq_count) + if (src >= mpic->num_sources) return; vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); @@ -904,40 +960,62 @@ void mpic_set_vector(unsigned int virq, unsigned int vector) mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); } +void mpic_set_destination(unsigned int virq, unsigned int cpuid) +{ + struct mpic *mpic = mpic_from_irq(virq); + unsigned int src = virq_to_hw(virq); + + DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n", + mpic, virq, src, cpuid); + + if (src >= mpic->num_sources) + return; + + mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); +} + static struct irq_chip mpic_irq_chip = { - .mask = mpic_mask_irq, - .unmask = mpic_unmask_irq, - .eoi = mpic_end_irq, - .set_type = mpic_set_irq_type, + .irq_mask = mpic_mask_irq, + .irq_unmask = mpic_unmask_irq, + .irq_eoi = mpic_end_irq, + .irq_set_type = mpic_set_irq_type, + .irq_set_wake = mpic_irq_set_wake, }; #ifdef CONFIG_SMP static struct irq_chip mpic_ipi_chip = { - .mask = mpic_mask_ipi, - .unmask = mpic_unmask_ipi, - .eoi = mpic_end_ipi, + .irq_mask = mpic_mask_ipi, + .irq_unmask = mpic_unmask_ipi, + .irq_eoi = mpic_end_ipi, }; #endif /* CONFIG_SMP */ +static struct irq_chip mpic_tm_chip = { + .irq_mask = mpic_mask_tm, + .irq_unmask = mpic_unmask_tm, + .irq_eoi = mpic_end_irq, + .irq_set_wake = mpic_irq_set_wake, +}; + #ifdef CONFIG_MPIC_U3_HT_IRQS static struct irq_chip mpic_irq_ht_chip = { - .startup = mpic_startup_ht_irq, - .shutdown = mpic_shutdown_ht_irq, - .mask = mpic_mask_irq, - .unmask = mpic_unmask_ht_irq, - .eoi = mpic_end_ht_irq, - .set_type = mpic_set_irq_type, + .irq_startup = mpic_startup_ht_irq, + .irq_shutdown = mpic_shutdown_ht_irq, + .irq_mask = mpic_mask_irq, + .irq_unmask = mpic_unmask_ht_irq, + .irq_eoi = mpic_end_ht_irq, + .irq_set_type = mpic_set_irq_type, }; #endif /* CONFIG_MPIC_U3_HT_IRQS */ -static int mpic_host_match(struct irq_host *h, struct device_node *node) +static int mpic_host_match(struct irq_domain *h, struct device_node *node) { /* Exact match, unless mpic node is NULL */ return h->of_node == NULL || h->of_node == node; } -static int mpic_host_map(struct irq_host *h, unsigned int virq, +static int mpic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { struct mpic *mpic = h->host_data; @@ -947,23 +1025,44 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, if (hw == mpic->spurious_vec) return -EINVAL; - if (mpic->protected && test_bit(hw, mpic->protected)) - return -EINVAL; + if (mpic->protected && test_bit(hw, mpic->protected)) { + pr_warning("mpic: Mapping of source 0x%x failed, " + "source protected by firmware !\n",\ + (unsigned int)hw); + return -EPERM; + } #ifdef CONFIG_SMP else if (hw >= mpic->ipi_vecs[0]) { - WARN_ON(!(mpic->flags & MPIC_PRIMARY)); + WARN_ON(mpic->flags & MPIC_SECONDARY); DBG("mpic: mapping as IPI\n"); - set_irq_chip_data(virq, mpic); - set_irq_chip_and_handler(virq, &mpic->hc_ipi, + irq_set_chip_data(virq, mpic); + irq_set_chip_and_handler(virq, &mpic->hc_ipi, handle_percpu_irq); return 0; } #endif /* CONFIG_SMP */ - if (hw >= mpic->irq_count) + if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) { + WARN_ON(mpic->flags & MPIC_SECONDARY); + + DBG("mpic: mapping as timer\n"); + irq_set_chip_data(virq, mpic); + irq_set_chip_and_handler(virq, &mpic->hc_tm, + handle_fasteoi_irq); + return 0; + } + + if (mpic_map_error_int(mpic, virq, hw)) + return 0; + + if (hw >= mpic->num_sources) { + pr_warning("mpic: Mapping of source 0x%x failed, " + "source out of range !\n",\ + (unsigned int)hw); return -EINVAL; + } mpic_msi_reserve_hwirq(mpic, hw); @@ -978,20 +1077,37 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq, DBG("mpic: mapping to irq chip @%p\n", chip); - set_irq_chip_data(virq, mpic); - set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq); + irq_set_chip_data(virq, mpic); + irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq); /* Set default irq type */ - set_irq_type(virq, IRQ_TYPE_NONE); + irq_set_irq_type(virq, IRQ_TYPE_DEFAULT); + + /* If the MPIC was reset, then all vectors have already been + * initialized. Otherwise, a per source lazy initialization + * is done here. + */ + if (!mpic_is_ipi(mpic, hw) && (mpic->flags & MPIC_NO_RESET)) { + int cpu; + + preempt_disable(); + cpu = mpic_processor_id(mpic); + preempt_enable(); + + mpic_set_vector(virq, hw); + mpic_set_destination(virq, cpu); + mpic_irq_set_priority(virq, 8); + } return 0; } -static int mpic_host_xlate(struct irq_host *h, struct device_node *ct, +static int mpic_host_xlate(struct irq_domain *h, struct device_node *ct, const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_flags) { + struct mpic *mpic = h->host_data; static unsigned char map_mpic_senses[4] = { IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW, @@ -1000,7 +1116,47 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct, }; *out_hwirq = intspec[0]; - if (intsize > 1) { + if (intsize >= 4 && (mpic->flags & MPIC_FSL)) { + /* + * Freescale MPIC with extended intspec: + * First two cells are as usual. Third specifies + * an "interrupt type". Fourth is type-specific data. + * + * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt + */ + switch (intspec[2]) { + case 0: + break; + case 1: + if (!(mpic->flags & MPIC_FSL_HAS_EIMR)) + break; + + if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs)) + return -EINVAL; + + *out_hwirq = mpic->err_int_vecs[intspec[3]]; + + break; + case 2: + if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) + return -EINVAL; + + *out_hwirq = mpic->ipi_vecs[intspec[0]]; + break; + case 3: + if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs)) + return -EINVAL; + + *out_hwirq = mpic->timer_vecs[intspec[0]]; + break; + default: + pr_debug("%s: unknown irq type %u\n", + __func__, intspec[2]); + return -EINVAL; + } + + *out_flags = map_mpic_senses[intspec[1] & 3]; + } else if (intsize > 1) { u32 mask = 0x3; /* Apple invented a new race of encoding on machines with @@ -1025,16 +1181,55 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct, return 0; } -static struct irq_host_ops mpic_host_ops = { +/* IRQ handler for a secondary MPIC cascaded from another IRQ controller */ +static void mpic_cascade(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct mpic *mpic = irq_desc_get_handler_data(desc); + unsigned int virq; + + BUG_ON(!(mpic->flags & MPIC_SECONDARY)); + + virq = mpic_get_one_irq(mpic); + if (virq) + generic_handle_irq(virq); + + chip->irq_eoi(&desc->irq_data); +} + +static struct irq_domain_ops mpic_host_ops = { .match = mpic_host_match, .map = mpic_host_map, .xlate = mpic_host_xlate, }; +static u32 fsl_mpic_get_version(struct mpic *mpic) +{ + u32 brr1; + + if (!(mpic->flags & MPIC_FSL)) + return 0; + + brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs, + MPIC_FSL_BRR1); + + return brr1 & MPIC_FSL_BRR1_VER; +} + /* * Exported functions */ +u32 fsl_mpic_primary_get_version(void) +{ + struct mpic *mpic = mpic_primary; + + if (mpic) + return fsl_mpic_get_version(mpic); + + return 0; +} + struct mpic * __init mpic_alloc(struct device_node *node, phys_addr_t phys_addr, unsigned int flags, @@ -1042,28 +1237,74 @@ struct mpic * __init mpic_alloc(struct device_node *node, unsigned int irq_count, const char *name) { - struct mpic *mpic; - u32 greg_feature; - const char *vers; - int i; - int intvec_top; - u64 paddr = phys_addr; + int i, psize, intvec_top; + struct mpic *mpic; + u32 greg_feature; + const char *vers; + const u32 *psrc; + u32 last_irq; + u32 fsl_version = 0; + + /* Default MPIC search parameters */ + static const struct of_device_id __initconst mpic_device_id[] = { + { .type = "open-pic", }, + { .compatible = "open-pic", }, + {}, + }; + + /* + * If we were not passed a device-tree node, then perform the default + * search for standardized a standardized OpenPIC. + */ + if (node) { + node = of_node_get(node); + } else { + node = of_find_matching_node(NULL, mpic_device_id); + if (!node) + return NULL; + } + + /* Pick the physical address from the device tree if unspecified */ + if (!phys_addr) { + /* Check if it is DCR-based */ + if (of_get_property(node, "dcr-reg", NULL)) { + flags |= MPIC_USES_DCR; + } else { + struct resource r; + if (of_address_to_resource(node, 0, &r)) + goto err_of_node_put; + phys_addr = r.start; + } + } + + /* Read extra device-tree properties into the flags variable */ + if (of_get_property(node, "big-endian", NULL)) + flags |= MPIC_BIG_ENDIAN; + if (of_get_property(node, "pic-no-reset", NULL)) + flags |= MPIC_NO_RESET; + if (of_get_property(node, "single-cpu-affinity", NULL)) + flags |= MPIC_SINGLE_DEST_CPU; + if (of_device_is_compatible(node, "fsl,mpic")) + flags |= MPIC_FSL | MPIC_LARGE_VECTORS; mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL); if (mpic == NULL) - return NULL; + goto err_of_node_put; mpic->name = name; + mpic->node = node; + mpic->paddr = phys_addr; + mpic->flags = flags; mpic->hc_irq = mpic_irq_chip; mpic->hc_irq.name = name; - if (flags & MPIC_PRIMARY) - mpic->hc_irq.set_affinity = mpic_set_affinity; + if (!(mpic->flags & MPIC_SECONDARY)) + mpic->hc_irq.irq_set_affinity = mpic_set_affinity; #ifdef CONFIG_MPIC_U3_HT_IRQS mpic->hc_ht_irq = mpic_irq_ht_chip; mpic->hc_ht_irq.name = name; - if (flags & MPIC_PRIMARY) - mpic->hc_ht_irq.set_affinity = mpic_set_affinity; + if (!(mpic->flags & MPIC_SECONDARY)) + mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity; #endif /* CONFIG_MPIC_U3_HT_IRQS */ #ifdef CONFIG_SMP @@ -1071,87 +1312,128 @@ struct mpic * __init mpic_alloc(struct device_node *node, mpic->hc_ipi.name = name; #endif /* CONFIG_SMP */ - mpic->flags = flags; - mpic->isu_size = isu_size; - mpic->irq_count = irq_count; + mpic->hc_tm = mpic_tm_chip; + mpic->hc_tm.name = name; + mpic->num_sources = 0; /* so far */ - if (flags & MPIC_LARGE_VECTORS) + if (mpic->flags & MPIC_LARGE_VECTORS) intvec_top = 2047; else intvec_top = 255; - mpic->timer_vecs[0] = intvec_top - 8; - mpic->timer_vecs[1] = intvec_top - 7; - mpic->timer_vecs[2] = intvec_top - 6; - mpic->timer_vecs[3] = intvec_top - 5; + mpic->timer_vecs[0] = intvec_top - 12; + mpic->timer_vecs[1] = intvec_top - 11; + mpic->timer_vecs[2] = intvec_top - 10; + mpic->timer_vecs[3] = intvec_top - 9; + mpic->timer_vecs[4] = intvec_top - 8; + mpic->timer_vecs[5] = intvec_top - 7; + mpic->timer_vecs[6] = intvec_top - 6; + mpic->timer_vecs[7] = intvec_top - 5; mpic->ipi_vecs[0] = intvec_top - 4; mpic->ipi_vecs[1] = intvec_top - 3; mpic->ipi_vecs[2] = intvec_top - 2; mpic->ipi_vecs[3] = intvec_top - 1; mpic->spurious_vec = intvec_top; - /* Check for "big-endian" in device-tree */ - if (node && of_get_property(node, "big-endian", NULL) != NULL) - mpic->flags |= MPIC_BIG_ENDIAN; - /* Look for protected sources */ - if (node) { - int psize; - unsigned int bits, mapsize; - const u32 *psrc = - of_get_property(node, "protected-sources", &psize); - if (psrc) { - psize /= 4; - bits = intvec_top + 1; - mapsize = BITS_TO_LONGS(bits) * sizeof(unsigned long); - mpic->protected = kzalloc(mapsize, GFP_KERNEL); - BUG_ON(mpic->protected == NULL); - for (i = 0; i < psize; i++) { - if (psrc[i] > intvec_top) - continue; - __set_bit(psrc[i], mpic->protected); - } + psrc = of_get_property(mpic->node, "protected-sources", &psize); + if (psrc) { + /* Allocate a bitmap with one bit per interrupt */ + unsigned int mapsize = BITS_TO_LONGS(intvec_top + 1); + mpic->protected = kzalloc(mapsize*sizeof(long), GFP_KERNEL); + BUG_ON(mpic->protected == NULL); + for (i = 0; i < psize/sizeof(u32); i++) { + if (psrc[i] > intvec_top) + continue; + __set_bit(psrc[i], mpic->protected); } } #ifdef CONFIG_MPIC_WEIRD - mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)]; + mpic->hw_set = mpic_infos[MPIC_GET_REGSET(mpic->flags)]; #endif /* default register type */ - mpic->reg_type = (flags & MPIC_BIG_ENDIAN) ? - mpic_access_mmio_be : mpic_access_mmio_le; - - /* If no physical address is passed in, a device-node is mandatory */ - BUG_ON(paddr == 0 && node == NULL); + if (mpic->flags & MPIC_BIG_ENDIAN) + mpic->reg_type = mpic_access_mmio_be; + else + mpic->reg_type = mpic_access_mmio_le; - /* If no physical address passed in, check if it's dcr based */ - if (paddr == 0 && of_get_property(node, "dcr-reg", NULL) != NULL) { + /* + * An MPIC with a "dcr-reg" property must be accessed that way, but + * only if the kernel includes DCR support. + */ #ifdef CONFIG_PPC_DCR - mpic->flags |= MPIC_USES_DCR; + if (mpic->flags & MPIC_USES_DCR) mpic->reg_type = mpic_access_dcr; #else - BUG(); -#endif /* CONFIG_PPC_DCR */ + BUG_ON(mpic->flags & MPIC_USES_DCR); +#endif + + /* Map the global registers */ + mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); + mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); + + if (mpic->flags & MPIC_FSL) { + int ret; + + /* + * Yes, Freescale really did put global registers in the + * magic per-cpu area -- and they don't even show up in the + * non-magic per-cpu copies that this driver normally uses. + */ + mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs, + MPIC_CPU_THISBASE, 0x1000); + + fsl_version = fsl_mpic_get_version(mpic); + + /* Error interrupt mask register (EIMR) is required for + * handling individual device error interrupts. EIMR + * was added in MPIC version 4.1. + * + * Over here we reserve vector number space for error + * interrupt vectors. This space is stolen from the + * global vector number space, as in case of ipis + * and timer interrupts. + * + * Available vector space = intvec_top - 12, where 12 + * is the number of vectors which have been consumed by + * ipis and timer interrupts. + */ + if (fsl_version >= 0x401) { + ret = mpic_setup_error_int(mpic, intvec_top - 12); + if (ret) + return NULL; + } + } - /* If the MPIC is not DCR based, and no physical address was passed - * in, try to obtain one + /* + * EPR is only available starting with v4.0. To support + * platforms that don't know the MPIC version at compile-time, + * such as qemu-e500, turn off coreint if this MPIC doesn't + * support it. Note that we never enable it if it wasn't + * requested in the first place. + * + * This is done outside the MPIC_FSL check, so that we + * also disable coreint if the MPIC node doesn't have + * an "fsl,mpic" compatible at all. This will be the case + * with device trees generated by older versions of QEMU. + * fsl_version will be zero if MPIC_FSL is not set. */ - if (paddr == 0 && !(mpic->flags & MPIC_USES_DCR)) { - const u32 *reg = of_get_property(node, "reg", NULL); - BUG_ON(reg == NULL); - paddr = of_translate_address(node, reg); - BUG_ON(paddr == OF_BAD_ADDR); + if (fsl_version < 0x400 && (flags & MPIC_ENABLE_COREINT)) { + WARN_ON(ppc_md.get_irq != mpic_get_coreint_irq); + ppc_md.get_irq = mpic_get_irq; } - /* Map the global registers */ - mpic_map(mpic, node, paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); - mpic_map(mpic, node, paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); - /* Reset */ - if (flags & MPIC_WANTS_RESET) { + + /* When using a device-node, reset requests are only honored if the MPIC + * is allowed to reset. + */ + if (!(mpic->flags & MPIC_NO_RESET)) { + printk(KERN_DEBUG "mpic: Resetting\n"); mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_RESET); @@ -1161,57 +1443,75 @@ struct mpic * __init mpic_alloc(struct device_node *node, } /* CoreInt */ - if (flags & MPIC_ENABLE_COREINT) + if (mpic->flags & MPIC_ENABLE_COREINT) mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_COREINT); - if (flags & MPIC_ENABLE_MCK) + if (mpic->flags & MPIC_ENABLE_MCK) mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) | MPIC_GREG_GCONF_MCK); - /* Read feature register, calculate num CPUs and, for non-ISU - * MPICs, num sources as well. On ISU MPICs, sources are counted - * as ISUs are added + /* + * The MPIC driver will crash if there are more cores than we + * can initialize, so we may as well catch that problem here. */ - greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); - mpic->num_cpus = ((greg_feature & MPIC_GREG_FEATURE_LAST_CPU_MASK) - >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1; - if (isu_size == 0) { - if (flags & MPIC_BROKEN_FRR_NIRQS) - mpic->num_sources = mpic->irq_count; - else - mpic->num_sources = - ((greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) - >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1; - } + BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS); /* Map the per-CPU registers */ - for (i = 0; i < mpic->num_cpus; i++) { - mpic_map(mpic, node, paddr, &mpic->cpuregs[i], - MPIC_INFO(CPU_BASE) + i * MPIC_INFO(CPU_STRIDE), + for_each_possible_cpu(i) { + unsigned int cpu = get_hard_smp_processor_id(i); + + mpic_map(mpic, mpic->paddr, &mpic->cpuregs[cpu], + MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE), 0x1000); } + /* + * Read feature register. For non-ISU MPICs, num sources as well. On + * ISU MPICs, sources are counted as ISUs are added + */ + greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); + + /* + * By default, the last source number comes from the MPIC, but the + * device-tree and board support code can override it on buggy hw. + * If we get passed an isu_size (multi-isu MPIC) then we use that + * as a default instead of the value read from the HW. + */ + last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) + >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; + if (isu_size) + last_irq = isu_size * MPIC_MAX_ISU - 1; + of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq); + if (irq_count) + last_irq = irq_count - 1; + /* Initialize main ISU if none provided */ - if (mpic->isu_size == 0) { - mpic->isu_size = mpic->num_sources; - mpic_map(mpic, node, paddr, &mpic->isus[0], - MPIC_INFO(IRQ_BASE), MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); + if (!isu_size) { + isu_size = last_irq + 1; + mpic->num_sources = isu_size; + mpic_map(mpic, mpic->paddr, &mpic->isus[0], + MPIC_INFO(IRQ_BASE), + MPIC_INFO(IRQ_STRIDE) * isu_size); } + + mpic->isu_size = isu_size; mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); mpic->isu_mask = (1 << mpic->isu_shift) - 1; - mpic->irqhost = irq_alloc_host(node, IRQ_HOST_MAP_LINEAR, - isu_size ? isu_size : mpic->num_sources, - &mpic_host_ops, - flags & MPIC_LARGE_VECTORS ? 2048 : 256); + mpic->irqhost = irq_domain_add_linear(mpic->node, + intvec_top, + &mpic_host_ops, mpic); + + /* + * FIXME: The code leaks the MPIC object and mappings here; this + * is very unlikely to fail but it ought to be fixed anyways. + */ if (mpic->irqhost == NULL) return NULL; - mpic->irqhost->host_data = mpic; - /* Display version */ switch (greg_feature & MPIC_GREG_FEATURE_VERSION_MASK) { case 1: @@ -1229,19 +1529,23 @@ struct mpic * __init mpic_alloc(struct device_node *node, } printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx," " max %d CPUs\n", - name, vers, (unsigned long long)paddr, mpic->num_cpus); + name, vers, (unsigned long long)mpic->paddr, num_possible_cpus()); printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", mpic->isu_size, mpic->isu_shift, mpic->isu_mask); mpic->next = mpics; mpics = mpic; - if (flags & MPIC_PRIMARY) { + if (!(mpic->flags & MPIC_SECONDARY)) { mpic_primary = mpic; irq_set_default_host(mpic->irqhost); } return mpic; + +err_of_node_put: + of_node_put(node); + return NULL; } void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, @@ -1251,7 +1555,7 @@ void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, BUG_ON(isu_num >= MPIC_MAX_ISU); - mpic_map(mpic, mpic->irqhost->of_node, + mpic_map(mpic, paddr, &mpic->isus[isu_num], 0, MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); @@ -1259,16 +1563,10 @@ void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, mpic->num_sources = isu_first + mpic->isu_size; } -void __init mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count) -{ - mpic->senses = senses; - mpic->senses_count = count; -} - void __init mpic_init(struct mpic *mpic) { - int i; - int cpu; + int i, cpu; + int num_timers = 4; BUG_ON(mpic->num_sources == 0); @@ -1277,15 +1575,30 @@ void __init mpic_init(struct mpic *mpic) /* Set current processor priority to max */ mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); - /* Initialize timers: just disable them all */ - for (i = 0; i < 4; i++) { + if (mpic->flags & MPIC_FSL) { + u32 version = fsl_mpic_get_version(mpic); + + /* + * Timer group B is present at the latest in MPIC 3.1 (e.g. + * mpc8536). It is not present in MPIC 2.0 (e.g. mpc8544). + * I don't know about the status of intermediate versions (or + * whether they even exist). + */ + if (version >= 0x0301) + num_timers = 8; + } + + /* Initialize timers to our reserved vectors and mask them for now */ + for (i = 0; i < num_timers; i++) { + unsigned int offset = mpic_tm_offset(mpic, i); + mpic_write(mpic->tmregs, - i * MPIC_INFO(TIMER_STRIDE) + - MPIC_INFO(TIMER_DESTINATION), 0); + offset + MPIC_INFO(TIMER_DESTINATION), + 1 << hard_smp_processor_id()); mpic_write(mpic->tmregs, - i * MPIC_INFO(TIMER_STRIDE) + - MPIC_INFO(TIMER_VECTOR_PRI), + offset + MPIC_INFO(TIMER_VECTOR_PRI), MPIC_VECPRI_MASK | + (9 << MPIC_VECPRI_PRIORITY_SHIFT) | (mpic->timer_vecs[0] + i)); } @@ -1298,37 +1611,32 @@ void __init mpic_init(struct mpic *mpic) (mpic->ipi_vecs[0] + i)); } - /* Initialize interrupt sources */ - if (mpic->irq_count == 0) - mpic->irq_count = mpic->num_sources; - /* Do the HT PIC fixups on U3 broken mpic */ DBG("MPIC flags: %x\n", mpic->flags); - if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) { + if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) { mpic_scan_ht_pics(mpic); mpic_u3msi_init(mpic); } mpic_pasemi_msi_init(mpic); - if (mpic->flags & MPIC_PRIMARY) - cpu = hard_smp_processor_id(); - else - cpu = 0; + cpu = mpic_processor_id(mpic); - for (i = 0; i < mpic->num_sources; i++) { - /* start with vector = source number, and masked */ - u32 vecpri = MPIC_VECPRI_MASK | i | - (8 << MPIC_VECPRI_PRIORITY_SHIFT); - - /* check if protected */ - if (mpic->protected && test_bit(i, mpic->protected)) - continue; - /* init hw */ - mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); - mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu); + if (!(mpic->flags & MPIC_NO_RESET)) { + for (i = 0; i < mpic->num_sources; i++) { + /* start with vector = source number, and masked */ + u32 vecpri = MPIC_VECPRI_MASK | i | + (8 << MPIC_VECPRI_PRIORITY_SHIFT); + + /* check if protected */ + if (mpic->protected && test_bit(i, mpic->protected)) + continue; + /* init hw */ + mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); + mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu); + } } - + /* Init spurious vector */ mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec); @@ -1352,6 +1660,21 @@ void __init mpic_init(struct mpic *mpic) GFP_KERNEL); BUG_ON(mpic->save_data == NULL); #endif + + /* Check if this MPIC is chained from a parent interrupt controller */ + if (mpic->flags & MPIC_SECONDARY) { + int virq = irq_of_parse_and_map(mpic->node, 0); + if (virq != NO_IRQ) { + printk(KERN_INFO "%s: hooking up to IRQ %d\n", + mpic->node->full_name, virq); + irq_set_handler_data(virq, mpic); + irq_set_chained_handler(virq, &mpic_cascade); + } + } + + /* FSL mpic error interrupt intialization */ + if (mpic->flags & MPIC_FSL_HAS_EIMR) + mpic_err_int_init(mpic, MPIC_FSL_ERR_INT); } void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio) @@ -1382,7 +1705,7 @@ void __init mpic_set_serial_int(struct mpic *mpic, int enable) void mpic_irq_set_priority(unsigned int irq, unsigned int pri) { struct mpic *mpic = mpic_find(irq); - unsigned int src = mpic_irq_to_hw(irq); + unsigned int src = virq_to_hw(irq); unsigned long flags; u32 reg; @@ -1390,11 +1713,16 @@ void mpic_irq_set_priority(unsigned int irq, unsigned int pri) return; raw_spin_lock_irqsave(&mpic_lock, flags); - if (mpic_is_ipi(mpic, irq)) { + if (mpic_is_ipi(mpic, src)) { reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) & ~MPIC_VECPRI_PRIORITY_MASK; mpic_ipi_write(src - mpic->ipi_vecs[0], reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); + } else if (mpic_is_tm(mpic, src)) { + reg = mpic_tm_read(src - mpic->timer_vecs[0]) & + ~MPIC_VECPRI_PRIORITY_MASK; + mpic_tm_write(src - mpic->timer_vecs[0], + reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); } else { reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & ~MPIC_VECPRI_PRIORITY_MASK; @@ -1423,7 +1751,7 @@ void mpic_setup_this_cpu(void) * it differently, then we should make sure we also change the default * values of irq_desc[].affinity in irq.c. */ - if (distribute_irqs) { + if (distribute_irqs && !(mpic->flags & MPIC_SINGLE_DEST_CPU)) { for (i = 0; i < mpic->num_sources ; i++) mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk); @@ -1493,9 +1821,8 @@ static unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg) return NO_IRQ; } if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { - if (printk_ratelimit()) - printk(KERN_WARNING "%s: Got protected source %d !\n", - mpic->name, (int)src); + printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", + mpic->name, (int)src); mpic_eoi(mpic); return NO_IRQ; } @@ -1533,9 +1860,8 @@ unsigned int mpic_get_coreint_irq(void) return NO_IRQ; } if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { - if (printk_ratelimit()) - printk(KERN_WARNING "%s: Got protected source %d !\n", - mpic->name, (int)src); + printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", + mpic->name, (int)src); return NO_IRQ; } @@ -1574,46 +1900,28 @@ void mpic_request_ipis(void) } } -static void mpic_send_ipi(unsigned int ipi_no, const struct cpumask *cpu_mask) +void smp_mpic_message_pass(int cpu, int msg) { struct mpic *mpic = mpic_primary; + u32 physmask; BUG_ON(mpic == NULL); -#ifdef DEBUG_IPI - DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no); -#endif - - mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) + - ipi_no * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), - mpic_physmask(cpumask_bits(cpu_mask)[0])); -} - -void smp_mpic_message_pass(int target, int msg) -{ - cpumask_var_t tmp; - /* make sure we're sending something that translates to an IPI */ if ((unsigned int)msg > 3) { printk("SMP %d: smp_message_pass: unknown msg %d\n", smp_processor_id(), msg); return; } - switch (target) { - case MSG_ALL: - mpic_send_ipi(msg, cpu_online_mask); - break; - case MSG_ALL_BUT_SELF: - alloc_cpumask_var(&tmp, GFP_NOWAIT); - cpumask_andnot(tmp, cpu_online_mask, - cpumask_of(smp_processor_id())); - mpic_send_ipi(msg, tmp); - free_cpumask_var(tmp); - break; - default: - mpic_send_ipi(msg, cpumask_of(target)); - break; - } + +#ifdef DEBUG_IPI + DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, msg); +#endif + + physmask = 1 << get_hard_smp_processor_id(cpu); + + mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) + + msg * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), physmask); } int __init smp_mpic_probe(void) @@ -1632,7 +1940,7 @@ int __init smp_mpic_probe(void) return nr_cpus; } -void __devinit smp_mpic_setup_cpu(int cpu) +void smp_mpic_setup_cpu(int cpu) { mpic_setup_this_cpu(); } @@ -1642,6 +1950,7 @@ void mpic_reset_core(int cpu) struct mpic *mpic = mpic_primary; u32 pir; int cpuid = get_hard_smp_processor_id(cpu); + int i; /* Set target bit for core reset */ pir = mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); @@ -1653,13 +1962,21 @@ void mpic_reset_core(int cpu) pir &= ~(1 << cpuid); mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); + + /* Perform 15 EOI on each reset core to clear pending interrupts. + * This is required for FSL CoreNet based devices */ + if (mpic->flags & MPIC_FSL) { + for (i = 0; i < 15; i++) { + _mpic_write(mpic->reg_type, &mpic->cpuregs[cpuid], + MPIC_CPU_EOI, 0); + } + } } #endif /* CONFIG_SMP */ #ifdef CONFIG_PM -static int mpic_suspend(struct sys_device *dev, pm_message_t state) +static void mpic_suspend_one(struct mpic *mpic) { - struct mpic *mpic = container_of(dev, struct mpic, sysdev); int i; for (i = 0; i < mpic->num_sources; i++) { @@ -1668,13 +1985,22 @@ static int mpic_suspend(struct sys_device *dev, pm_message_t state) mpic->save_data[i].dest = mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)); } +} + +static int mpic_suspend(void) +{ + struct mpic *mpic = mpics; + + while (mpic) { + mpic_suspend_one(mpic); + mpic = mpic->next; + } return 0; } -static int mpic_resume(struct sys_device *dev) +static void mpic_resume_one(struct mpic *mpic) { - struct mpic *mpic = container_of(dev, struct mpic, sysdev); int i; for (i = 0; i < mpic->num_sources; i++) { @@ -1701,33 +2027,30 @@ static int mpic_resume(struct sys_device *dev) } #endif } /* end for loop */ +} - return 0; +static void mpic_resume(void) +{ + struct mpic *mpic = mpics; + + while (mpic) { + mpic_resume_one(mpic); + mpic = mpic->next; + } } -#endif -static struct sysdev_class mpic_sysclass = { -#ifdef CONFIG_PM +static struct syscore_ops mpic_syscore_ops = { .resume = mpic_resume, .suspend = mpic_suspend, -#endif - .name = "mpic", }; static int mpic_init_sys(void) { - struct mpic *mpic = mpics; - int error, id = 0; - - error = sysdev_class_register(&mpic_sysclass); + register_syscore_ops(&mpic_syscore_ops); + subsys_system_register(&mpic_subsys, NULL); - while (mpic && !error) { - mpic->sysdev.cls = &mpic_sysclass; - mpic->sysdev.id = id++; - error = sysdev_register(&mpic->sysdev); - mpic = mpic->next; - } - return error; + return 0; } device_initcall(mpic_init_sys); +#endif |
