xref: /linux/arch/powerpc/sysdev/mpic.c (revision 7233593b7844c2db930594ee9c0c872a6900bfcc)
114cf11afSPaul Mackerras /*
214cf11afSPaul Mackerras  *  arch/powerpc/kernel/mpic.c
314cf11afSPaul Mackerras  *
414cf11afSPaul Mackerras  *  Driver for interrupt controllers following the OpenPIC standard, the
514cf11afSPaul Mackerras  *  common implementation beeing IBM's MPIC. This driver also can deal
614cf11afSPaul Mackerras  *  with various broken implementations of this HW.
714cf11afSPaul Mackerras  *
814cf11afSPaul Mackerras  *  Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp.
914cf11afSPaul Mackerras  *
1014cf11afSPaul Mackerras  *  This file is subject to the terms and conditions of the GNU General Public
1114cf11afSPaul Mackerras  *  License.  See the file COPYING in the main directory of this archive
1214cf11afSPaul Mackerras  *  for more details.
1314cf11afSPaul Mackerras  */
1414cf11afSPaul Mackerras 
1514cf11afSPaul Mackerras #undef DEBUG
161beb6a7dSBenjamin Herrenschmidt #undef DEBUG_IPI
171beb6a7dSBenjamin Herrenschmidt #undef DEBUG_IRQ
181beb6a7dSBenjamin Herrenschmidt #undef DEBUG_LOW
1914cf11afSPaul Mackerras 
2014cf11afSPaul Mackerras #include <linux/types.h>
2114cf11afSPaul Mackerras #include <linux/kernel.h>
2214cf11afSPaul Mackerras #include <linux/init.h>
2314cf11afSPaul Mackerras #include <linux/irq.h>
2414cf11afSPaul Mackerras #include <linux/smp.h>
2514cf11afSPaul Mackerras #include <linux/interrupt.h>
2614cf11afSPaul Mackerras #include <linux/bootmem.h>
2714cf11afSPaul Mackerras #include <linux/spinlock.h>
2814cf11afSPaul Mackerras #include <linux/pci.h>
2914cf11afSPaul Mackerras 
3014cf11afSPaul Mackerras #include <asm/ptrace.h>
3114cf11afSPaul Mackerras #include <asm/signal.h>
3214cf11afSPaul Mackerras #include <asm/io.h>
3314cf11afSPaul Mackerras #include <asm/pgtable.h>
3414cf11afSPaul Mackerras #include <asm/irq.h>
3514cf11afSPaul Mackerras #include <asm/machdep.h>
3614cf11afSPaul Mackerras #include <asm/mpic.h>
3714cf11afSPaul Mackerras #include <asm/smp.h>
3814cf11afSPaul Mackerras 
3914cf11afSPaul Mackerras #ifdef DEBUG
4014cf11afSPaul Mackerras #define DBG(fmt...) printk(fmt)
4114cf11afSPaul Mackerras #else
4214cf11afSPaul Mackerras #define DBG(fmt...)
4314cf11afSPaul Mackerras #endif
4414cf11afSPaul Mackerras 
4514cf11afSPaul Mackerras static struct mpic *mpics;
4614cf11afSPaul Mackerras static struct mpic *mpic_primary;
4714cf11afSPaul Mackerras static DEFINE_SPINLOCK(mpic_lock);
4814cf11afSPaul Mackerras 
49c0c0d996SPaul Mackerras #ifdef CONFIG_PPC32	/* XXX for now */
50e40c7f02SAndy Whitcroft #ifdef CONFIG_IRQ_ALL_CPUS
51e40c7f02SAndy Whitcroft #define distribute_irqs	(1)
52e40c7f02SAndy Whitcroft #else
53e40c7f02SAndy Whitcroft #define distribute_irqs	(0)
54e40c7f02SAndy Whitcroft #endif
55c0c0d996SPaul Mackerras #endif
5614cf11afSPaul Mackerras 
57*7233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD
58*7233593bSZang Roy-r61911 static u32 mpic_infos[][MPIC_IDX_END] = {
59*7233593bSZang Roy-r61911 	[0] = {	/* Original OpenPIC compatible MPIC */
60*7233593bSZang Roy-r61911 		MPIC_GREG_BASE,
61*7233593bSZang Roy-r61911 		MPIC_GREG_FEATURE_0,
62*7233593bSZang Roy-r61911 		MPIC_GREG_GLOBAL_CONF_0,
63*7233593bSZang Roy-r61911 		MPIC_GREG_VENDOR_ID,
64*7233593bSZang Roy-r61911 		MPIC_GREG_IPI_VECTOR_PRI_0,
65*7233593bSZang Roy-r61911 		MPIC_GREG_IPI_STRIDE,
66*7233593bSZang Roy-r61911 		MPIC_GREG_SPURIOUS,
67*7233593bSZang Roy-r61911 		MPIC_GREG_TIMER_FREQ,
68*7233593bSZang Roy-r61911 
69*7233593bSZang Roy-r61911 		MPIC_TIMER_BASE,
70*7233593bSZang Roy-r61911 		MPIC_TIMER_STRIDE,
71*7233593bSZang Roy-r61911 		MPIC_TIMER_CURRENT_CNT,
72*7233593bSZang Roy-r61911 		MPIC_TIMER_BASE_CNT,
73*7233593bSZang Roy-r61911 		MPIC_TIMER_VECTOR_PRI,
74*7233593bSZang Roy-r61911 		MPIC_TIMER_DESTINATION,
75*7233593bSZang Roy-r61911 
76*7233593bSZang Roy-r61911 		MPIC_CPU_BASE,
77*7233593bSZang Roy-r61911 		MPIC_CPU_STRIDE,
78*7233593bSZang Roy-r61911 		MPIC_CPU_IPI_DISPATCH_0,
79*7233593bSZang Roy-r61911 		MPIC_CPU_IPI_DISPATCH_STRIDE,
80*7233593bSZang Roy-r61911 		MPIC_CPU_CURRENT_TASK_PRI,
81*7233593bSZang Roy-r61911 		MPIC_CPU_WHOAMI,
82*7233593bSZang Roy-r61911 		MPIC_CPU_INTACK,
83*7233593bSZang Roy-r61911 		MPIC_CPU_EOI,
84*7233593bSZang Roy-r61911 
85*7233593bSZang Roy-r61911 		MPIC_IRQ_BASE,
86*7233593bSZang Roy-r61911 		MPIC_IRQ_STRIDE,
87*7233593bSZang Roy-r61911 		MPIC_IRQ_VECTOR_PRI,
88*7233593bSZang Roy-r61911 		MPIC_VECPRI_VECTOR_MASK,
89*7233593bSZang Roy-r61911 		MPIC_VECPRI_POLARITY_POSITIVE,
90*7233593bSZang Roy-r61911 		MPIC_VECPRI_POLARITY_NEGATIVE,
91*7233593bSZang Roy-r61911 		MPIC_VECPRI_SENSE_LEVEL,
92*7233593bSZang Roy-r61911 		MPIC_VECPRI_SENSE_EDGE,
93*7233593bSZang Roy-r61911 		MPIC_VECPRI_POLARITY_MASK,
94*7233593bSZang Roy-r61911 		MPIC_VECPRI_SENSE_MASK,
95*7233593bSZang Roy-r61911 		MPIC_IRQ_DESTINATION
96*7233593bSZang Roy-r61911 	},
97*7233593bSZang Roy-r61911 	[1] = {	/* Tsi108/109 PIC */
98*7233593bSZang Roy-r61911 		TSI108_GREG_BASE,
99*7233593bSZang Roy-r61911 		TSI108_GREG_FEATURE_0,
100*7233593bSZang Roy-r61911 		TSI108_GREG_GLOBAL_CONF_0,
101*7233593bSZang Roy-r61911 		TSI108_GREG_VENDOR_ID,
102*7233593bSZang Roy-r61911 		TSI108_GREG_IPI_VECTOR_PRI_0,
103*7233593bSZang Roy-r61911 		TSI108_GREG_IPI_STRIDE,
104*7233593bSZang Roy-r61911 		TSI108_GREG_SPURIOUS,
105*7233593bSZang Roy-r61911 		TSI108_GREG_TIMER_FREQ,
106*7233593bSZang Roy-r61911 
107*7233593bSZang Roy-r61911 		TSI108_TIMER_BASE,
108*7233593bSZang Roy-r61911 		TSI108_TIMER_STRIDE,
109*7233593bSZang Roy-r61911 		TSI108_TIMER_CURRENT_CNT,
110*7233593bSZang Roy-r61911 		TSI108_TIMER_BASE_CNT,
111*7233593bSZang Roy-r61911 		TSI108_TIMER_VECTOR_PRI,
112*7233593bSZang Roy-r61911 		TSI108_TIMER_DESTINATION,
113*7233593bSZang Roy-r61911 
114*7233593bSZang Roy-r61911 		TSI108_CPU_BASE,
115*7233593bSZang Roy-r61911 		TSI108_CPU_STRIDE,
116*7233593bSZang Roy-r61911 		TSI108_CPU_IPI_DISPATCH_0,
117*7233593bSZang Roy-r61911 		TSI108_CPU_IPI_DISPATCH_STRIDE,
118*7233593bSZang Roy-r61911 		TSI108_CPU_CURRENT_TASK_PRI,
119*7233593bSZang Roy-r61911 		TSI108_CPU_WHOAMI,
120*7233593bSZang Roy-r61911 		TSI108_CPU_INTACK,
121*7233593bSZang Roy-r61911 		TSI108_CPU_EOI,
122*7233593bSZang Roy-r61911 
123*7233593bSZang Roy-r61911 		TSI108_IRQ_BASE,
124*7233593bSZang Roy-r61911 		TSI108_IRQ_STRIDE,
125*7233593bSZang Roy-r61911 		TSI108_IRQ_VECTOR_PRI,
126*7233593bSZang Roy-r61911 		TSI108_VECPRI_VECTOR_MASK,
127*7233593bSZang Roy-r61911 		TSI108_VECPRI_POLARITY_POSITIVE,
128*7233593bSZang Roy-r61911 		TSI108_VECPRI_POLARITY_NEGATIVE,
129*7233593bSZang Roy-r61911 		TSI108_VECPRI_SENSE_LEVEL,
130*7233593bSZang Roy-r61911 		TSI108_VECPRI_SENSE_EDGE,
131*7233593bSZang Roy-r61911 		TSI108_VECPRI_POLARITY_MASK,
132*7233593bSZang Roy-r61911 		TSI108_VECPRI_SENSE_MASK,
133*7233593bSZang Roy-r61911 		TSI108_IRQ_DESTINATION
134*7233593bSZang Roy-r61911 	},
135*7233593bSZang Roy-r61911 };
136*7233593bSZang Roy-r61911 
137*7233593bSZang Roy-r61911 #define MPIC_INFO(name) mpic->hw_set[MPIC_IDX_##name]
138*7233593bSZang Roy-r61911 
139*7233593bSZang Roy-r61911 #else /* CONFIG_MPIC_WEIRD */
140*7233593bSZang Roy-r61911 
141*7233593bSZang Roy-r61911 #define MPIC_INFO(name) MPIC_##name
142*7233593bSZang Roy-r61911 
143*7233593bSZang Roy-r61911 #endif /* CONFIG_MPIC_WEIRD */
144*7233593bSZang Roy-r61911 
14514cf11afSPaul Mackerras /*
14614cf11afSPaul Mackerras  * Register accessor functions
14714cf11afSPaul Mackerras  */
14814cf11afSPaul Mackerras 
14914cf11afSPaul Mackerras 
15014cf11afSPaul Mackerras static inline u32 _mpic_read(unsigned int be, volatile u32 __iomem *base,
15114cf11afSPaul Mackerras 			    unsigned int reg)
15214cf11afSPaul Mackerras {
15314cf11afSPaul Mackerras 	if (be)
15414cf11afSPaul Mackerras 		return in_be32(base + (reg >> 2));
15514cf11afSPaul Mackerras 	else
15614cf11afSPaul Mackerras 		return in_le32(base + (reg >> 2));
15714cf11afSPaul Mackerras }
15814cf11afSPaul Mackerras 
15914cf11afSPaul Mackerras static inline void _mpic_write(unsigned int be, volatile u32 __iomem *base,
16014cf11afSPaul Mackerras 			      unsigned int reg, u32 value)
16114cf11afSPaul Mackerras {
16214cf11afSPaul Mackerras 	if (be)
16314cf11afSPaul Mackerras 		out_be32(base + (reg >> 2), value);
16414cf11afSPaul Mackerras 	else
16514cf11afSPaul Mackerras 		out_le32(base + (reg >> 2), value);
16614cf11afSPaul Mackerras }
16714cf11afSPaul Mackerras 
16814cf11afSPaul Mackerras static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
16914cf11afSPaul Mackerras {
17014cf11afSPaul Mackerras 	unsigned int be = (mpic->flags & MPIC_BIG_ENDIAN) != 0;
171*7233593bSZang Roy-r61911 	unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) +
172*7233593bSZang Roy-r61911 			      (ipi * MPIC_INFO(GREG_IPI_STRIDE));
17314cf11afSPaul Mackerras 
17414cf11afSPaul Mackerras 	if (mpic->flags & MPIC_BROKEN_IPI)
17514cf11afSPaul Mackerras 		be = !be;
17614cf11afSPaul Mackerras 	return _mpic_read(be, mpic->gregs, offset);
17714cf11afSPaul Mackerras }
17814cf11afSPaul Mackerras 
17914cf11afSPaul Mackerras static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value)
18014cf11afSPaul Mackerras {
181*7233593bSZang Roy-r61911 	unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) +
182*7233593bSZang Roy-r61911 			      (ipi * MPIC_INFO(GREG_IPI_STRIDE));
18314cf11afSPaul Mackerras 
18414cf11afSPaul Mackerras 	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->gregs, offset, value);
18514cf11afSPaul Mackerras }
18614cf11afSPaul Mackerras 
18714cf11afSPaul Mackerras static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg)
18814cf11afSPaul Mackerras {
18914cf11afSPaul Mackerras 	unsigned int cpu = 0;
19014cf11afSPaul Mackerras 
19114cf11afSPaul Mackerras 	if (mpic->flags & MPIC_PRIMARY)
19214cf11afSPaul Mackerras 		cpu = hard_smp_processor_id();
193b9e5b4e6SBenjamin Herrenschmidt 	return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN,
194b9e5b4e6SBenjamin Herrenschmidt 			  mpic->cpuregs[cpu], reg);
19514cf11afSPaul Mackerras }
19614cf11afSPaul Mackerras 
19714cf11afSPaul Mackerras static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value)
19814cf11afSPaul Mackerras {
19914cf11afSPaul Mackerras 	unsigned int cpu = 0;
20014cf11afSPaul Mackerras 
20114cf11afSPaul Mackerras 	if (mpic->flags & MPIC_PRIMARY)
20214cf11afSPaul Mackerras 		cpu = hard_smp_processor_id();
20314cf11afSPaul Mackerras 
20414cf11afSPaul Mackerras 	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->cpuregs[cpu], reg, value);
20514cf11afSPaul Mackerras }
20614cf11afSPaul Mackerras 
20714cf11afSPaul Mackerras static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg)
20814cf11afSPaul Mackerras {
20914cf11afSPaul Mackerras 	unsigned int	isu = src_no >> mpic->isu_shift;
21014cf11afSPaul Mackerras 	unsigned int	idx = src_no & mpic->isu_mask;
21114cf11afSPaul Mackerras 
21214cf11afSPaul Mackerras 	return _mpic_read(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
213*7233593bSZang Roy-r61911 			  reg + (idx * MPIC_INFO(IRQ_STRIDE)));
21414cf11afSPaul Mackerras }
21514cf11afSPaul Mackerras 
21614cf11afSPaul Mackerras static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
21714cf11afSPaul Mackerras 				   unsigned int reg, u32 value)
21814cf11afSPaul Mackerras {
21914cf11afSPaul Mackerras 	unsigned int	isu = src_no >> mpic->isu_shift;
22014cf11afSPaul Mackerras 	unsigned int	idx = src_no & mpic->isu_mask;
22114cf11afSPaul Mackerras 
22214cf11afSPaul Mackerras 	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN, mpic->isus[isu],
223*7233593bSZang Roy-r61911 		    reg + (idx * MPIC_INFO(IRQ_STRIDE)), value);
22414cf11afSPaul Mackerras }
22514cf11afSPaul Mackerras 
22614cf11afSPaul Mackerras #define mpic_read(b,r)		_mpic_read(mpic->flags & MPIC_BIG_ENDIAN,(b),(r))
22714cf11afSPaul Mackerras #define mpic_write(b,r,v)	_mpic_write(mpic->flags & MPIC_BIG_ENDIAN,(b),(r),(v))
22814cf11afSPaul Mackerras #define mpic_ipi_read(i)	_mpic_ipi_read(mpic,(i))
22914cf11afSPaul Mackerras #define mpic_ipi_write(i,v)	_mpic_ipi_write(mpic,(i),(v))
23014cf11afSPaul Mackerras #define mpic_cpu_read(i)	_mpic_cpu_read(mpic,(i))
23114cf11afSPaul Mackerras #define mpic_cpu_write(i,v)	_mpic_cpu_write(mpic,(i),(v))
23214cf11afSPaul Mackerras #define mpic_irq_read(s,r)	_mpic_irq_read(mpic,(s),(r))
23314cf11afSPaul Mackerras #define mpic_irq_write(s,r,v)	_mpic_irq_write(mpic,(s),(r),(v))
23414cf11afSPaul Mackerras 
23514cf11afSPaul Mackerras 
23614cf11afSPaul Mackerras /*
23714cf11afSPaul Mackerras  * Low level utility functions
23814cf11afSPaul Mackerras  */
23914cf11afSPaul Mackerras 
24014cf11afSPaul Mackerras 
24114cf11afSPaul Mackerras 
24214cf11afSPaul Mackerras /* Check if we have one of those nice broken MPICs with a flipped endian on
24314cf11afSPaul Mackerras  * reads from IPI registers
24414cf11afSPaul Mackerras  */
24514cf11afSPaul Mackerras static void __init mpic_test_broken_ipi(struct mpic *mpic)
24614cf11afSPaul Mackerras {
24714cf11afSPaul Mackerras 	u32 r;
24814cf11afSPaul Mackerras 
249*7233593bSZang Roy-r61911 	mpic_write(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0), MPIC_VECPRI_MASK);
250*7233593bSZang Roy-r61911 	r = mpic_read(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0));
25114cf11afSPaul Mackerras 
25214cf11afSPaul Mackerras 	if (r == le32_to_cpu(MPIC_VECPRI_MASK)) {
25314cf11afSPaul Mackerras 		printk(KERN_INFO "mpic: Detected reversed IPI registers\n");
25414cf11afSPaul Mackerras 		mpic->flags |= MPIC_BROKEN_IPI;
25514cf11afSPaul Mackerras 	}
25614cf11afSPaul Mackerras }
25714cf11afSPaul Mackerras 
25814cf11afSPaul Mackerras #ifdef CONFIG_MPIC_BROKEN_U3
25914cf11afSPaul Mackerras 
26014cf11afSPaul Mackerras /* Test if an interrupt is sourced from HyperTransport (used on broken U3s)
26114cf11afSPaul Mackerras  * to force the edge setting on the MPIC and do the ack workaround.
26214cf11afSPaul Mackerras  */
2631beb6a7dSBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source)
26414cf11afSPaul Mackerras {
2651beb6a7dSBenjamin Herrenschmidt 	if (source >= 128 || !mpic->fixups)
26614cf11afSPaul Mackerras 		return 0;
2671beb6a7dSBenjamin Herrenschmidt 	return mpic->fixups[source].base != NULL;
26814cf11afSPaul Mackerras }
26914cf11afSPaul Mackerras 
270c4b22f26SSegher Boessenkool 
2711beb6a7dSBenjamin Herrenschmidt static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source)
27214cf11afSPaul Mackerras {
2731beb6a7dSBenjamin Herrenschmidt 	struct mpic_irq_fixup *fixup = &mpic->fixups[source];
27414cf11afSPaul Mackerras 
2751beb6a7dSBenjamin Herrenschmidt 	if (fixup->applebase) {
2761beb6a7dSBenjamin Herrenschmidt 		unsigned int soff = (fixup->index >> 3) & ~3;
2771beb6a7dSBenjamin Herrenschmidt 		unsigned int mask = 1U << (fixup->index & 0x1f);
2781beb6a7dSBenjamin Herrenschmidt 		writel(mask, fixup->applebase + soff);
2791beb6a7dSBenjamin Herrenschmidt 	} else {
28014cf11afSPaul Mackerras 		spin_lock(&mpic->fixup_lock);
2811beb6a7dSBenjamin Herrenschmidt 		writeb(0x11 + 2 * fixup->index, fixup->base + 2);
282c4b22f26SSegher Boessenkool 		writel(fixup->data, fixup->base + 4);
28314cf11afSPaul Mackerras 		spin_unlock(&mpic->fixup_lock);
28414cf11afSPaul Mackerras 	}
2851beb6a7dSBenjamin Herrenschmidt }
28614cf11afSPaul Mackerras 
2871beb6a7dSBenjamin Herrenschmidt static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source,
2881beb6a7dSBenjamin Herrenschmidt 				      unsigned int irqflags)
2891beb6a7dSBenjamin Herrenschmidt {
2901beb6a7dSBenjamin Herrenschmidt 	struct mpic_irq_fixup *fixup = &mpic->fixups[source];
2911beb6a7dSBenjamin Herrenschmidt 	unsigned long flags;
2921beb6a7dSBenjamin Herrenschmidt 	u32 tmp;
29314cf11afSPaul Mackerras 
2941beb6a7dSBenjamin Herrenschmidt 	if (fixup->base == NULL)
2951beb6a7dSBenjamin Herrenschmidt 		return;
2961beb6a7dSBenjamin Herrenschmidt 
29706fe98e6SBenjamin Herrenschmidt 	DBG("startup_ht_interrupt(0x%x, 0x%x) index: %d\n",
2981beb6a7dSBenjamin Herrenschmidt 	    source, irqflags, fixup->index);
2991beb6a7dSBenjamin Herrenschmidt 	spin_lock_irqsave(&mpic->fixup_lock, flags);
3001beb6a7dSBenjamin Herrenschmidt 	/* Enable and configure */
3011beb6a7dSBenjamin Herrenschmidt 	writeb(0x10 + 2 * fixup->index, fixup->base + 2);
3021beb6a7dSBenjamin Herrenschmidt 	tmp = readl(fixup->base + 4);
3031beb6a7dSBenjamin Herrenschmidt 	tmp &= ~(0x23U);
3041beb6a7dSBenjamin Herrenschmidt 	if (irqflags & IRQ_LEVEL)
3051beb6a7dSBenjamin Herrenschmidt 		tmp |= 0x22;
3061beb6a7dSBenjamin Herrenschmidt 	writel(tmp, fixup->base + 4);
3071beb6a7dSBenjamin Herrenschmidt 	spin_unlock_irqrestore(&mpic->fixup_lock, flags);
3081beb6a7dSBenjamin Herrenschmidt }
3091beb6a7dSBenjamin Herrenschmidt 
3101beb6a7dSBenjamin Herrenschmidt static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source,
3111beb6a7dSBenjamin Herrenschmidt 				       unsigned int irqflags)
3121beb6a7dSBenjamin Herrenschmidt {
3131beb6a7dSBenjamin Herrenschmidt 	struct mpic_irq_fixup *fixup = &mpic->fixups[source];
3141beb6a7dSBenjamin Herrenschmidt 	unsigned long flags;
3151beb6a7dSBenjamin Herrenschmidt 	u32 tmp;
3161beb6a7dSBenjamin Herrenschmidt 
3171beb6a7dSBenjamin Herrenschmidt 	if (fixup->base == NULL)
3181beb6a7dSBenjamin Herrenschmidt 		return;
3191beb6a7dSBenjamin Herrenschmidt 
32006fe98e6SBenjamin Herrenschmidt 	DBG("shutdown_ht_interrupt(0x%x, 0x%x)\n", source, irqflags);
3211beb6a7dSBenjamin Herrenschmidt 
3221beb6a7dSBenjamin Herrenschmidt 	/* Disable */
3231beb6a7dSBenjamin Herrenschmidt 	spin_lock_irqsave(&mpic->fixup_lock, flags);
3241beb6a7dSBenjamin Herrenschmidt 	writeb(0x10 + 2 * fixup->index, fixup->base + 2);
3251beb6a7dSBenjamin Herrenschmidt 	tmp = readl(fixup->base + 4);
32672b13819SSegher Boessenkool 	tmp |= 1;
3271beb6a7dSBenjamin Herrenschmidt 	writel(tmp, fixup->base + 4);
3281beb6a7dSBenjamin Herrenschmidt 	spin_unlock_irqrestore(&mpic->fixup_lock, flags);
3291beb6a7dSBenjamin Herrenschmidt }
3301beb6a7dSBenjamin Herrenschmidt 
3311beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase,
3321beb6a7dSBenjamin Herrenschmidt 				    unsigned int devfn, u32 vdid)
33314cf11afSPaul Mackerras {
334c4b22f26SSegher Boessenkool 	int i, irq, n;
3351beb6a7dSBenjamin Herrenschmidt 	u8 __iomem *base;
33614cf11afSPaul Mackerras 	u32 tmp;
337c4b22f26SSegher Boessenkool 	u8 pos;
33814cf11afSPaul Mackerras 
3391beb6a7dSBenjamin Herrenschmidt 	for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0;
3401beb6a7dSBenjamin Herrenschmidt 	     pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) {
3411beb6a7dSBenjamin Herrenschmidt 		u8 id = readb(devbase + pos + PCI_CAP_LIST_ID);
3421beb6a7dSBenjamin Herrenschmidt 		if (id == PCI_CAP_ID_HT_IRQCONF) {
343c4b22f26SSegher Boessenkool 			id = readb(devbase + pos + 3);
344c4b22f26SSegher Boessenkool 			if (id == 0x80)
345c4b22f26SSegher Boessenkool 				break;
346c4b22f26SSegher Boessenkool 		}
347c4b22f26SSegher Boessenkool 	}
348c4b22f26SSegher Boessenkool 	if (pos == 0)
349c4b22f26SSegher Boessenkool 		return;
350c4b22f26SSegher Boessenkool 
3511beb6a7dSBenjamin Herrenschmidt 	base = devbase + pos;
3521beb6a7dSBenjamin Herrenschmidt 	writeb(0x01, base + 2);
3531beb6a7dSBenjamin Herrenschmidt 	n = (readl(base + 4) >> 16) & 0xff;
354c4b22f26SSegher Boessenkool 
3551beb6a7dSBenjamin Herrenschmidt 	printk(KERN_INFO "mpic:   - HT:%02x.%x [0x%02x] vendor %04x device %04x"
3561beb6a7dSBenjamin Herrenschmidt 	       " has %d irqs\n",
3571beb6a7dSBenjamin Herrenschmidt 	       devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1);
358c4b22f26SSegher Boessenkool 
359c4b22f26SSegher Boessenkool 	for (i = 0; i <= n; i++) {
3601beb6a7dSBenjamin Herrenschmidt 		writeb(0x10 + 2 * i, base + 2);
3611beb6a7dSBenjamin Herrenschmidt 		tmp = readl(base + 4);
36214cf11afSPaul Mackerras 		irq = (tmp >> 16) & 0xff;
3631beb6a7dSBenjamin Herrenschmidt 		DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp);
3641beb6a7dSBenjamin Herrenschmidt 		/* mask it , will be unmasked later */
3651beb6a7dSBenjamin Herrenschmidt 		tmp |= 0x1;
3661beb6a7dSBenjamin Herrenschmidt 		writel(tmp, base + 4);
3671beb6a7dSBenjamin Herrenschmidt 		mpic->fixups[irq].index = i;
3681beb6a7dSBenjamin Herrenschmidt 		mpic->fixups[irq].base = base;
3691beb6a7dSBenjamin Herrenschmidt 		/* Apple HT PIC has a non-standard way of doing EOIs */
3701beb6a7dSBenjamin Herrenschmidt 		if ((vdid & 0xffff) == 0x106b)
3711beb6a7dSBenjamin Herrenschmidt 			mpic->fixups[irq].applebase = devbase + 0x60;
3721beb6a7dSBenjamin Herrenschmidt 		else
3731beb6a7dSBenjamin Herrenschmidt 			mpic->fixups[irq].applebase = NULL;
3741beb6a7dSBenjamin Herrenschmidt 		writeb(0x11 + 2 * i, base + 2);
3751beb6a7dSBenjamin Herrenschmidt 		mpic->fixups[irq].data = readl(base + 4) | 0x80000000;
37614cf11afSPaul Mackerras 	}
37714cf11afSPaul Mackerras }
37814cf11afSPaul Mackerras 
37914cf11afSPaul Mackerras 
3801beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic)
38114cf11afSPaul Mackerras {
38214cf11afSPaul Mackerras 	unsigned int devfn;
38314cf11afSPaul Mackerras 	u8 __iomem *cfgspace;
38414cf11afSPaul Mackerras 
3851beb6a7dSBenjamin Herrenschmidt 	printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n");
38614cf11afSPaul Mackerras 
38714cf11afSPaul Mackerras 	/* Allocate fixups array */
38814cf11afSPaul Mackerras 	mpic->fixups = alloc_bootmem(128 * sizeof(struct mpic_irq_fixup));
38914cf11afSPaul Mackerras 	BUG_ON(mpic->fixups == NULL);
39014cf11afSPaul Mackerras 	memset(mpic->fixups, 0, 128 * sizeof(struct mpic_irq_fixup));
39114cf11afSPaul Mackerras 
39214cf11afSPaul Mackerras 	/* Init spinlock */
39314cf11afSPaul Mackerras 	spin_lock_init(&mpic->fixup_lock);
39414cf11afSPaul Mackerras 
395c4b22f26SSegher Boessenkool 	/* Map U3 config space. We assume all IO-APICs are on the primary bus
396c4b22f26SSegher Boessenkool 	 * so we only need to map 64kB.
39714cf11afSPaul Mackerras 	 */
398c4b22f26SSegher Boessenkool 	cfgspace = ioremap(0xf2000000, 0x10000);
39914cf11afSPaul Mackerras 	BUG_ON(cfgspace == NULL);
40014cf11afSPaul Mackerras 
4011beb6a7dSBenjamin Herrenschmidt 	/* Now we scan all slots. We do a very quick scan, we read the header
4021beb6a7dSBenjamin Herrenschmidt 	 * type, vendor ID and device ID only, that's plenty enough
40314cf11afSPaul Mackerras 	 */
404c4b22f26SSegher Boessenkool 	for (devfn = 0; devfn < 0x100; devfn++) {
40514cf11afSPaul Mackerras 		u8 __iomem *devbase = cfgspace + (devfn << 8);
40614cf11afSPaul Mackerras 		u8 hdr_type = readb(devbase + PCI_HEADER_TYPE);
40714cf11afSPaul Mackerras 		u32 l = readl(devbase + PCI_VENDOR_ID);
4081beb6a7dSBenjamin Herrenschmidt 		u16 s;
40914cf11afSPaul Mackerras 
41014cf11afSPaul Mackerras 		DBG("devfn %x, l: %x\n", devfn, l);
41114cf11afSPaul Mackerras 
41214cf11afSPaul Mackerras 		/* If no device, skip */
41314cf11afSPaul Mackerras 		if (l == 0xffffffff || l == 0x00000000 ||
41414cf11afSPaul Mackerras 		    l == 0x0000ffff || l == 0xffff0000)
41514cf11afSPaul Mackerras 			goto next;
4161beb6a7dSBenjamin Herrenschmidt 		/* Check if is supports capability lists */
4171beb6a7dSBenjamin Herrenschmidt 		s = readw(devbase + PCI_STATUS);
4181beb6a7dSBenjamin Herrenschmidt 		if (!(s & PCI_STATUS_CAP_LIST))
4191beb6a7dSBenjamin Herrenschmidt 			goto next;
42014cf11afSPaul Mackerras 
4211beb6a7dSBenjamin Herrenschmidt 		mpic_scan_ht_pic(mpic, devbase, devfn, l);
42214cf11afSPaul Mackerras 
42314cf11afSPaul Mackerras 	next:
42414cf11afSPaul Mackerras 		/* next device, if function 0 */
425c4b22f26SSegher Boessenkool 		if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0)
42614cf11afSPaul Mackerras 			devfn += 7;
42714cf11afSPaul Mackerras 	}
42814cf11afSPaul Mackerras }
42914cf11afSPaul Mackerras 
4306e99e458SBenjamin Herrenschmidt #else /* CONFIG_MPIC_BROKEN_U3 */
4316e99e458SBenjamin Herrenschmidt 
4326e99e458SBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source)
4336e99e458SBenjamin Herrenschmidt {
4346e99e458SBenjamin Herrenschmidt 	return 0;
4356e99e458SBenjamin Herrenschmidt }
4366e99e458SBenjamin Herrenschmidt 
4376e99e458SBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic)
4386e99e458SBenjamin Herrenschmidt {
4396e99e458SBenjamin Herrenschmidt }
4406e99e458SBenjamin Herrenschmidt 
44114cf11afSPaul Mackerras #endif /* CONFIG_MPIC_BROKEN_U3 */
44214cf11afSPaul Mackerras 
44314cf11afSPaul Mackerras 
4440ebfff14SBenjamin Herrenschmidt #define mpic_irq_to_hw(virq)	((unsigned int)irq_map[virq].hwirq)
4450ebfff14SBenjamin Herrenschmidt 
44614cf11afSPaul Mackerras /* Find an mpic associated with a given linux interrupt */
44714cf11afSPaul Mackerras static struct mpic *mpic_find(unsigned int irq, unsigned int *is_ipi)
44814cf11afSPaul Mackerras {
4490ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
45014cf11afSPaul Mackerras 
4510ebfff14SBenjamin Herrenschmidt 	if (irq < NUM_ISA_INTERRUPTS)
45214cf11afSPaul Mackerras 		return NULL;
4530ebfff14SBenjamin Herrenschmidt 	if (is_ipi)
4540ebfff14SBenjamin Herrenschmidt 		*is_ipi = (src >= MPIC_VEC_IPI_0 && src <= MPIC_VEC_IPI_3);
4550ebfff14SBenjamin Herrenschmidt 
4560ebfff14SBenjamin Herrenschmidt 	return irq_desc[irq].chip_data;
45714cf11afSPaul Mackerras }
45814cf11afSPaul Mackerras 
45914cf11afSPaul Mackerras /* Convert a cpu mask from logical to physical cpu numbers. */
46014cf11afSPaul Mackerras static inline u32 mpic_physmask(u32 cpumask)
46114cf11afSPaul Mackerras {
46214cf11afSPaul Mackerras 	int i;
46314cf11afSPaul Mackerras 	u32 mask = 0;
46414cf11afSPaul Mackerras 
46514cf11afSPaul Mackerras 	for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1)
46614cf11afSPaul Mackerras 		mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
46714cf11afSPaul Mackerras 	return mask;
46814cf11afSPaul Mackerras }
46914cf11afSPaul Mackerras 
47014cf11afSPaul Mackerras #ifdef CONFIG_SMP
47114cf11afSPaul Mackerras /* Get the mpic structure from the IPI number */
47214cf11afSPaul Mackerras static inline struct mpic * mpic_from_ipi(unsigned int ipi)
47314cf11afSPaul Mackerras {
474b9e5b4e6SBenjamin Herrenschmidt 	return irq_desc[ipi].chip_data;
47514cf11afSPaul Mackerras }
47614cf11afSPaul Mackerras #endif
47714cf11afSPaul Mackerras 
47814cf11afSPaul Mackerras /* Get the mpic structure from the irq number */
47914cf11afSPaul Mackerras static inline struct mpic * mpic_from_irq(unsigned int irq)
48014cf11afSPaul Mackerras {
481b9e5b4e6SBenjamin Herrenschmidt 	return irq_desc[irq].chip_data;
48214cf11afSPaul Mackerras }
48314cf11afSPaul Mackerras 
48414cf11afSPaul Mackerras /* Send an EOI */
48514cf11afSPaul Mackerras static inline void mpic_eoi(struct mpic *mpic)
48614cf11afSPaul Mackerras {
487*7233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_EOI), 0);
488*7233593bSZang Roy-r61911 	(void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI));
48914cf11afSPaul Mackerras }
49014cf11afSPaul Mackerras 
49114cf11afSPaul Mackerras #ifdef CONFIG_SMP
49214cf11afSPaul Mackerras static irqreturn_t mpic_ipi_action(int irq, void *dev_id, struct pt_regs *regs)
49314cf11afSPaul Mackerras {
4940ebfff14SBenjamin Herrenschmidt 	smp_message_recv(mpic_irq_to_hw(irq) - MPIC_VEC_IPI_0, regs);
49514cf11afSPaul Mackerras 	return IRQ_HANDLED;
49614cf11afSPaul Mackerras }
49714cf11afSPaul Mackerras #endif /* CONFIG_SMP */
49814cf11afSPaul Mackerras 
49914cf11afSPaul Mackerras /*
50014cf11afSPaul Mackerras  * Linux descriptor level callbacks
50114cf11afSPaul Mackerras  */
50214cf11afSPaul Mackerras 
50314cf11afSPaul Mackerras 
504b9e5b4e6SBenjamin Herrenschmidt static void mpic_unmask_irq(unsigned int irq)
50514cf11afSPaul Mackerras {
50614cf11afSPaul Mackerras 	unsigned int loops = 100000;
50714cf11afSPaul Mackerras 	struct mpic *mpic = mpic_from_irq(irq);
5080ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
50914cf11afSPaul Mackerras 
510bd561c79SPaul Mackerras 	DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src);
51114cf11afSPaul Mackerras 
512*7233593bSZang Roy-r61911 	mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
513*7233593bSZang Roy-r61911 		       mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) &
514e5356640SBenjamin Herrenschmidt 		       ~MPIC_VECPRI_MASK);
51514cf11afSPaul Mackerras 	/* make sure mask gets to controller before we return to user */
51614cf11afSPaul Mackerras 	do {
51714cf11afSPaul Mackerras 		if (!loops--) {
51814cf11afSPaul Mackerras 			printk(KERN_ERR "mpic_enable_irq timeout\n");
51914cf11afSPaul Mackerras 			break;
52014cf11afSPaul Mackerras 		}
521*7233593bSZang Roy-r61911 	} while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK);
5221beb6a7dSBenjamin Herrenschmidt }
5231beb6a7dSBenjamin Herrenschmidt 
524b9e5b4e6SBenjamin Herrenschmidt static void mpic_mask_irq(unsigned int irq)
52514cf11afSPaul Mackerras {
52614cf11afSPaul Mackerras 	unsigned int loops = 100000;
52714cf11afSPaul Mackerras 	struct mpic *mpic = mpic_from_irq(irq);
5280ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
52914cf11afSPaul Mackerras 
53014cf11afSPaul Mackerras 	DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src);
53114cf11afSPaul Mackerras 
532*7233593bSZang Roy-r61911 	mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
533*7233593bSZang Roy-r61911 		       mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) |
534e5356640SBenjamin Herrenschmidt 		       MPIC_VECPRI_MASK);
53514cf11afSPaul Mackerras 
53614cf11afSPaul Mackerras 	/* make sure mask gets to controller before we return to user */
53714cf11afSPaul Mackerras 	do {
53814cf11afSPaul Mackerras 		if (!loops--) {
53914cf11afSPaul Mackerras 			printk(KERN_ERR "mpic_enable_irq timeout\n");
54014cf11afSPaul Mackerras 			break;
54114cf11afSPaul Mackerras 		}
542*7233593bSZang Roy-r61911 	} while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK));
54314cf11afSPaul Mackerras }
54414cf11afSPaul Mackerras 
54514cf11afSPaul Mackerras static void mpic_end_irq(unsigned int irq)
54614cf11afSPaul Mackerras {
54714cf11afSPaul Mackerras 	struct mpic *mpic = mpic_from_irq(irq);
54814cf11afSPaul Mackerras 
5491beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_IRQ
55014cf11afSPaul Mackerras 	DBG("%s: end_irq: %d\n", mpic->name, irq);
5511beb6a7dSBenjamin Herrenschmidt #endif
55214cf11afSPaul Mackerras 	/* We always EOI on end_irq() even for edge interrupts since that
55314cf11afSPaul Mackerras 	 * should only lower the priority, the MPIC should have properly
55414cf11afSPaul Mackerras 	 * latched another edge interrupt coming in anyway
55514cf11afSPaul Mackerras 	 */
55614cf11afSPaul Mackerras 
55714cf11afSPaul Mackerras 	mpic_eoi(mpic);
55814cf11afSPaul Mackerras }
55914cf11afSPaul Mackerras 
560b9e5b4e6SBenjamin Herrenschmidt #ifdef CONFIG_MPIC_BROKEN_U3
561b9e5b4e6SBenjamin Herrenschmidt 
562b9e5b4e6SBenjamin Herrenschmidt static void mpic_unmask_ht_irq(unsigned int irq)
563b9e5b4e6SBenjamin Herrenschmidt {
564b9e5b4e6SBenjamin Herrenschmidt 	struct mpic *mpic = mpic_from_irq(irq);
5650ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
566b9e5b4e6SBenjamin Herrenschmidt 
567b9e5b4e6SBenjamin Herrenschmidt 	mpic_unmask_irq(irq);
568b9e5b4e6SBenjamin Herrenschmidt 
569b9e5b4e6SBenjamin Herrenschmidt 	if (irq_desc[irq].status & IRQ_LEVEL)
570b9e5b4e6SBenjamin Herrenschmidt 		mpic_ht_end_irq(mpic, src);
571b9e5b4e6SBenjamin Herrenschmidt }
572b9e5b4e6SBenjamin Herrenschmidt 
573b9e5b4e6SBenjamin Herrenschmidt static unsigned int mpic_startup_ht_irq(unsigned int irq)
574b9e5b4e6SBenjamin Herrenschmidt {
575b9e5b4e6SBenjamin Herrenschmidt 	struct mpic *mpic = mpic_from_irq(irq);
5760ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
577b9e5b4e6SBenjamin Herrenschmidt 
578b9e5b4e6SBenjamin Herrenschmidt 	mpic_unmask_irq(irq);
579b9e5b4e6SBenjamin Herrenschmidt 	mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status);
580b9e5b4e6SBenjamin Herrenschmidt 
581b9e5b4e6SBenjamin Herrenschmidt 	return 0;
582b9e5b4e6SBenjamin Herrenschmidt }
583b9e5b4e6SBenjamin Herrenschmidt 
584b9e5b4e6SBenjamin Herrenschmidt static void mpic_shutdown_ht_irq(unsigned int irq)
585b9e5b4e6SBenjamin Herrenschmidt {
586b9e5b4e6SBenjamin Herrenschmidt 	struct mpic *mpic = mpic_from_irq(irq);
5870ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
588b9e5b4e6SBenjamin Herrenschmidt 
589b9e5b4e6SBenjamin Herrenschmidt 	mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status);
590b9e5b4e6SBenjamin Herrenschmidt 	mpic_mask_irq(irq);
591b9e5b4e6SBenjamin Herrenschmidt }
592b9e5b4e6SBenjamin Herrenschmidt 
593b9e5b4e6SBenjamin Herrenschmidt static void mpic_end_ht_irq(unsigned int irq)
594b9e5b4e6SBenjamin Herrenschmidt {
595b9e5b4e6SBenjamin Herrenschmidt 	struct mpic *mpic = mpic_from_irq(irq);
5960ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
597b9e5b4e6SBenjamin Herrenschmidt 
598b9e5b4e6SBenjamin Herrenschmidt #ifdef DEBUG_IRQ
599b9e5b4e6SBenjamin Herrenschmidt 	DBG("%s: end_irq: %d\n", mpic->name, irq);
600b9e5b4e6SBenjamin Herrenschmidt #endif
601b9e5b4e6SBenjamin Herrenschmidt 	/* We always EOI on end_irq() even for edge interrupts since that
602b9e5b4e6SBenjamin Herrenschmidt 	 * should only lower the priority, the MPIC should have properly
603b9e5b4e6SBenjamin Herrenschmidt 	 * latched another edge interrupt coming in anyway
604b9e5b4e6SBenjamin Herrenschmidt 	 */
605b9e5b4e6SBenjamin Herrenschmidt 
606b9e5b4e6SBenjamin Herrenschmidt 	if (irq_desc[irq].status & IRQ_LEVEL)
607b9e5b4e6SBenjamin Herrenschmidt 		mpic_ht_end_irq(mpic, src);
608b9e5b4e6SBenjamin Herrenschmidt 	mpic_eoi(mpic);
609b9e5b4e6SBenjamin Herrenschmidt }
6106e99e458SBenjamin Herrenschmidt #endif /* !CONFIG_MPIC_BROKEN_U3 */
611b9e5b4e6SBenjamin Herrenschmidt 
61214cf11afSPaul Mackerras #ifdef CONFIG_SMP
61314cf11afSPaul Mackerras 
614b9e5b4e6SBenjamin Herrenschmidt static void mpic_unmask_ipi(unsigned int irq)
61514cf11afSPaul Mackerras {
61614cf11afSPaul Mackerras 	struct mpic *mpic = mpic_from_ipi(irq);
6170ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq) - MPIC_VEC_IPI_0;
61814cf11afSPaul Mackerras 
61914cf11afSPaul Mackerras 	DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src);
62014cf11afSPaul Mackerras 	mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);
62114cf11afSPaul Mackerras }
62214cf11afSPaul Mackerras 
623b9e5b4e6SBenjamin Herrenschmidt static void mpic_mask_ipi(unsigned int irq)
62414cf11afSPaul Mackerras {
62514cf11afSPaul Mackerras 	/* NEVER disable an IPI... that's just plain wrong! */
62614cf11afSPaul Mackerras }
62714cf11afSPaul Mackerras 
62814cf11afSPaul Mackerras static void mpic_end_ipi(unsigned int irq)
62914cf11afSPaul Mackerras {
63014cf11afSPaul Mackerras 	struct mpic *mpic = mpic_from_ipi(irq);
63114cf11afSPaul Mackerras 
63214cf11afSPaul Mackerras 	/*
63314cf11afSPaul Mackerras 	 * IPIs are marked IRQ_PER_CPU. This has the side effect of
63414cf11afSPaul Mackerras 	 * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from
63514cf11afSPaul Mackerras 	 * applying to them. We EOI them late to avoid re-entering.
6366714465eSThomas Gleixner 	 * We mark IPI's with IRQF_DISABLED as they must run with
63714cf11afSPaul Mackerras 	 * irqs disabled.
63814cf11afSPaul Mackerras 	 */
63914cf11afSPaul Mackerras 	mpic_eoi(mpic);
64014cf11afSPaul Mackerras }
64114cf11afSPaul Mackerras 
64214cf11afSPaul Mackerras #endif /* CONFIG_SMP */
64314cf11afSPaul Mackerras 
64414cf11afSPaul Mackerras static void mpic_set_affinity(unsigned int irq, cpumask_t cpumask)
64514cf11afSPaul Mackerras {
64614cf11afSPaul Mackerras 	struct mpic *mpic = mpic_from_irq(irq);
6470ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
64814cf11afSPaul Mackerras 
64914cf11afSPaul Mackerras 	cpumask_t tmp;
65014cf11afSPaul Mackerras 
65114cf11afSPaul Mackerras 	cpus_and(tmp, cpumask, cpu_online_map);
65214cf11afSPaul Mackerras 
653*7233593bSZang Roy-r61911 	mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION),
65414cf11afSPaul Mackerras 		       mpic_physmask(cpus_addr(tmp)[0]));
65514cf11afSPaul Mackerras }
65614cf11afSPaul Mackerras 
657*7233593bSZang Roy-r61911 static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type)
6580ebfff14SBenjamin Herrenschmidt {
6590ebfff14SBenjamin Herrenschmidt 	/* Now convert sense value */
6606e99e458SBenjamin Herrenschmidt 	switch(type & IRQ_TYPE_SENSE_MASK) {
6610ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_EDGE_RISING:
662*7233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_EDGE) |
663*7233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_POSITIVE);
6640ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_EDGE_FALLING:
6656e99e458SBenjamin Herrenschmidt 	case IRQ_TYPE_EDGE_BOTH:
666*7233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_EDGE) |
667*7233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_NEGATIVE);
6680ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_LEVEL_HIGH:
669*7233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_LEVEL) |
670*7233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_POSITIVE);
6710ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_LEVEL_LOW:
6720ebfff14SBenjamin Herrenschmidt 	default:
673*7233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_LEVEL) |
674*7233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_NEGATIVE);
6750ebfff14SBenjamin Herrenschmidt 	}
6766e99e458SBenjamin Herrenschmidt }
6776e99e458SBenjamin Herrenschmidt 
6786e99e458SBenjamin Herrenschmidt static int mpic_set_irq_type(unsigned int virq, unsigned int flow_type)
6796e99e458SBenjamin Herrenschmidt {
6806e99e458SBenjamin Herrenschmidt 	struct mpic *mpic = mpic_from_irq(virq);
6816e99e458SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(virq);
6826e99e458SBenjamin Herrenschmidt 	struct irq_desc *desc = get_irq_desc(virq);
6836e99e458SBenjamin Herrenschmidt 	unsigned int vecpri, vold, vnew;
6846e99e458SBenjamin Herrenschmidt 
68506fe98e6SBenjamin Herrenschmidt 	DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n",
6866e99e458SBenjamin Herrenschmidt 	    mpic, virq, src, flow_type);
6876e99e458SBenjamin Herrenschmidt 
6886e99e458SBenjamin Herrenschmidt 	if (src >= mpic->irq_count)
6896e99e458SBenjamin Herrenschmidt 		return -EINVAL;
6906e99e458SBenjamin Herrenschmidt 
6916e99e458SBenjamin Herrenschmidt 	if (flow_type == IRQ_TYPE_NONE)
6926e99e458SBenjamin Herrenschmidt 		if (mpic->senses && src < mpic->senses_count)
6936e99e458SBenjamin Herrenschmidt 			flow_type = mpic->senses[src];
6946e99e458SBenjamin Herrenschmidt 	if (flow_type == IRQ_TYPE_NONE)
6956e99e458SBenjamin Herrenschmidt 		flow_type = IRQ_TYPE_LEVEL_LOW;
6966e99e458SBenjamin Herrenschmidt 
6976e99e458SBenjamin Herrenschmidt 	desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
6986e99e458SBenjamin Herrenschmidt 	desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
6996e99e458SBenjamin Herrenschmidt 	if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW))
7006e99e458SBenjamin Herrenschmidt 		desc->status |= IRQ_LEVEL;
7016e99e458SBenjamin Herrenschmidt 
7026e99e458SBenjamin Herrenschmidt 	if (mpic_is_ht_interrupt(mpic, src))
7036e99e458SBenjamin Herrenschmidt 		vecpri = MPIC_VECPRI_POLARITY_POSITIVE |
7046e99e458SBenjamin Herrenschmidt 			MPIC_VECPRI_SENSE_EDGE;
7056e99e458SBenjamin Herrenschmidt 	else
706*7233593bSZang Roy-r61911 		vecpri = mpic_type_to_vecpri(mpic, flow_type);
7076e99e458SBenjamin Herrenschmidt 
708*7233593bSZang Roy-r61911 	vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
709*7233593bSZang Roy-r61911 	vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) |
710*7233593bSZang Roy-r61911 			MPIC_INFO(VECPRI_SENSE_MASK));
7116e99e458SBenjamin Herrenschmidt 	vnew |= vecpri;
7126e99e458SBenjamin Herrenschmidt 	if (vold != vnew)
713*7233593bSZang Roy-r61911 		mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew);
7146e99e458SBenjamin Herrenschmidt 
7156e99e458SBenjamin Herrenschmidt 	return 0;
7160ebfff14SBenjamin Herrenschmidt }
7170ebfff14SBenjamin Herrenschmidt 
718b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_chip = {
719b9e5b4e6SBenjamin Herrenschmidt 	.mask		= mpic_mask_irq,
720b9e5b4e6SBenjamin Herrenschmidt 	.unmask		= mpic_unmask_irq,
721b9e5b4e6SBenjamin Herrenschmidt 	.eoi		= mpic_end_irq,
7226e99e458SBenjamin Herrenschmidt 	.set_type	= mpic_set_irq_type,
723b9e5b4e6SBenjamin Herrenschmidt };
724b9e5b4e6SBenjamin Herrenschmidt 
725b9e5b4e6SBenjamin Herrenschmidt #ifdef CONFIG_SMP
726b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_ipi_chip = {
727b9e5b4e6SBenjamin Herrenschmidt 	.mask		= mpic_mask_ipi,
728b9e5b4e6SBenjamin Herrenschmidt 	.unmask		= mpic_unmask_ipi,
729b9e5b4e6SBenjamin Herrenschmidt 	.eoi		= mpic_end_ipi,
730b9e5b4e6SBenjamin Herrenschmidt };
731b9e5b4e6SBenjamin Herrenschmidt #endif /* CONFIG_SMP */
732b9e5b4e6SBenjamin Herrenschmidt 
733b9e5b4e6SBenjamin Herrenschmidt #ifdef CONFIG_MPIC_BROKEN_U3
734b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_ht_chip = {
735b9e5b4e6SBenjamin Herrenschmidt 	.startup	= mpic_startup_ht_irq,
736b9e5b4e6SBenjamin Herrenschmidt 	.shutdown	= mpic_shutdown_ht_irq,
737b9e5b4e6SBenjamin Herrenschmidt 	.mask		= mpic_mask_irq,
738b9e5b4e6SBenjamin Herrenschmidt 	.unmask		= mpic_unmask_ht_irq,
739b9e5b4e6SBenjamin Herrenschmidt 	.eoi		= mpic_end_ht_irq,
7406e99e458SBenjamin Herrenschmidt 	.set_type	= mpic_set_irq_type,
741b9e5b4e6SBenjamin Herrenschmidt };
742b9e5b4e6SBenjamin Herrenschmidt #endif /* CONFIG_MPIC_BROKEN_U3 */
743b9e5b4e6SBenjamin Herrenschmidt 
74414cf11afSPaul Mackerras 
7450ebfff14SBenjamin Herrenschmidt static int mpic_host_match(struct irq_host *h, struct device_node *node)
7460ebfff14SBenjamin Herrenschmidt {
7470ebfff14SBenjamin Herrenschmidt 	struct mpic *mpic = h->host_data;
7480ebfff14SBenjamin Herrenschmidt 
7490ebfff14SBenjamin Herrenschmidt 	/* Exact match, unless mpic node is NULL */
7500ebfff14SBenjamin Herrenschmidt 	return mpic->of_node == NULL || mpic->of_node == node;
7510ebfff14SBenjamin Herrenschmidt }
7520ebfff14SBenjamin Herrenschmidt 
7530ebfff14SBenjamin Herrenschmidt static int mpic_host_map(struct irq_host *h, unsigned int virq,
7546e99e458SBenjamin Herrenschmidt 			 irq_hw_number_t hw)
7550ebfff14SBenjamin Herrenschmidt {
7560ebfff14SBenjamin Herrenschmidt 	struct mpic *mpic = h->host_data;
7576e99e458SBenjamin Herrenschmidt 	struct irq_chip *chip;
7580ebfff14SBenjamin Herrenschmidt 
75906fe98e6SBenjamin Herrenschmidt 	DBG("mpic: map virq %d, hwirq 0x%lx\n", virq, hw);
7600ebfff14SBenjamin Herrenschmidt 
7610ebfff14SBenjamin Herrenschmidt 	if (hw == MPIC_VEC_SPURRIOUS)
7620ebfff14SBenjamin Herrenschmidt 		return -EINVAL;
76306fe98e6SBenjamin Herrenschmidt 
7640ebfff14SBenjamin Herrenschmidt #ifdef CONFIG_SMP
7650ebfff14SBenjamin Herrenschmidt 	else if (hw >= MPIC_VEC_IPI_0) {
7660ebfff14SBenjamin Herrenschmidt 		WARN_ON(!(mpic->flags & MPIC_PRIMARY));
7670ebfff14SBenjamin Herrenschmidt 
76806fe98e6SBenjamin Herrenschmidt 		DBG("mpic: mapping as IPI\n");
7690ebfff14SBenjamin Herrenschmidt 		set_irq_chip_data(virq, mpic);
7700ebfff14SBenjamin Herrenschmidt 		set_irq_chip_and_handler(virq, &mpic->hc_ipi,
7710ebfff14SBenjamin Herrenschmidt 					 handle_percpu_irq);
7720ebfff14SBenjamin Herrenschmidt 		return 0;
7730ebfff14SBenjamin Herrenschmidt 	}
7740ebfff14SBenjamin Herrenschmidt #endif /* CONFIG_SMP */
7750ebfff14SBenjamin Herrenschmidt 
7760ebfff14SBenjamin Herrenschmidt 	if (hw >= mpic->irq_count)
7770ebfff14SBenjamin Herrenschmidt 		return -EINVAL;
7780ebfff14SBenjamin Herrenschmidt 
7796e99e458SBenjamin Herrenschmidt 	/* Default chip */
7800ebfff14SBenjamin Herrenschmidt 	chip = &mpic->hc_irq;
7810ebfff14SBenjamin Herrenschmidt 
7820ebfff14SBenjamin Herrenschmidt #ifdef CONFIG_MPIC_BROKEN_U3
7830ebfff14SBenjamin Herrenschmidt 	/* Check for HT interrupts, override vecpri */
7846e99e458SBenjamin Herrenschmidt 	if (mpic_is_ht_interrupt(mpic, hw))
7850ebfff14SBenjamin Herrenschmidt 		chip = &mpic->hc_ht_irq;
7866e99e458SBenjamin Herrenschmidt #endif /* CONFIG_MPIC_BROKEN_U3 */
7870ebfff14SBenjamin Herrenschmidt 
78806fe98e6SBenjamin Herrenschmidt 	DBG("mpic: mapping to irq chip @%p\n", chip);
7890ebfff14SBenjamin Herrenschmidt 
7900ebfff14SBenjamin Herrenschmidt 	set_irq_chip_data(virq, mpic);
7910ebfff14SBenjamin Herrenschmidt 	set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq);
7926e99e458SBenjamin Herrenschmidt 
7936e99e458SBenjamin Herrenschmidt 	/* Set default irq type */
7946e99e458SBenjamin Herrenschmidt 	set_irq_type(virq, IRQ_TYPE_NONE);
7956e99e458SBenjamin Herrenschmidt 
7960ebfff14SBenjamin Herrenschmidt 	return 0;
7970ebfff14SBenjamin Herrenschmidt }
7980ebfff14SBenjamin Herrenschmidt 
7990ebfff14SBenjamin Herrenschmidt static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
8000ebfff14SBenjamin Herrenschmidt 			   u32 *intspec, unsigned int intsize,
8010ebfff14SBenjamin Herrenschmidt 			   irq_hw_number_t *out_hwirq, unsigned int *out_flags)
8020ebfff14SBenjamin Herrenschmidt 
8030ebfff14SBenjamin Herrenschmidt {
8040ebfff14SBenjamin Herrenschmidt 	static unsigned char map_mpic_senses[4] = {
8050ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_EDGE_RISING,
8060ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_LEVEL_LOW,
8070ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_LEVEL_HIGH,
8080ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_EDGE_FALLING,
8090ebfff14SBenjamin Herrenschmidt 	};
8100ebfff14SBenjamin Herrenschmidt 
8110ebfff14SBenjamin Herrenschmidt 	*out_hwirq = intspec[0];
81206fe98e6SBenjamin Herrenschmidt 	if (intsize > 1) {
81306fe98e6SBenjamin Herrenschmidt 		u32 mask = 0x3;
81406fe98e6SBenjamin Herrenschmidt 
81506fe98e6SBenjamin Herrenschmidt 		/* Apple invented a new race of encoding on machines with
81606fe98e6SBenjamin Herrenschmidt 		 * an HT APIC. They encode, among others, the index within
81706fe98e6SBenjamin Herrenschmidt 		 * the HT APIC. We don't care about it here since thankfully,
81806fe98e6SBenjamin Herrenschmidt 		 * it appears that they have the APIC already properly
81906fe98e6SBenjamin Herrenschmidt 		 * configured, and thus our current fixup code that reads the
82006fe98e6SBenjamin Herrenschmidt 		 * APIC config works fine. However, we still need to mask out
82106fe98e6SBenjamin Herrenschmidt 		 * bits in the specifier to make sure we only get bit 0 which
82206fe98e6SBenjamin Herrenschmidt 		 * is the level/edge bit (the only sense bit exposed by Apple),
82306fe98e6SBenjamin Herrenschmidt 		 * as their bit 1 means something else.
82406fe98e6SBenjamin Herrenschmidt 		 */
82506fe98e6SBenjamin Herrenschmidt 		if (machine_is(powermac))
82606fe98e6SBenjamin Herrenschmidt 			mask = 0x1;
82706fe98e6SBenjamin Herrenschmidt 		*out_flags = map_mpic_senses[intspec[1] & mask];
82806fe98e6SBenjamin Herrenschmidt 	} else
8290ebfff14SBenjamin Herrenschmidt 		*out_flags = IRQ_TYPE_NONE;
8300ebfff14SBenjamin Herrenschmidt 
83106fe98e6SBenjamin Herrenschmidt 	DBG("mpic: xlate (%d cells: 0x%08x 0x%08x) to line 0x%lx sense 0x%x\n",
83206fe98e6SBenjamin Herrenschmidt 	    intsize, intspec[0], intspec[1], *out_hwirq, *out_flags);
83306fe98e6SBenjamin Herrenschmidt 
8340ebfff14SBenjamin Herrenschmidt 	return 0;
8350ebfff14SBenjamin Herrenschmidt }
8360ebfff14SBenjamin Herrenschmidt 
8370ebfff14SBenjamin Herrenschmidt static struct irq_host_ops mpic_host_ops = {
8380ebfff14SBenjamin Herrenschmidt 	.match = mpic_host_match,
8390ebfff14SBenjamin Herrenschmidt 	.map = mpic_host_map,
8400ebfff14SBenjamin Herrenschmidt 	.xlate = mpic_host_xlate,
8410ebfff14SBenjamin Herrenschmidt };
8420ebfff14SBenjamin Herrenschmidt 
84314cf11afSPaul Mackerras /*
84414cf11afSPaul Mackerras  * Exported functions
84514cf11afSPaul Mackerras  */
84614cf11afSPaul Mackerras 
8470ebfff14SBenjamin Herrenschmidt struct mpic * __init mpic_alloc(struct device_node *node,
8480ebfff14SBenjamin Herrenschmidt 				unsigned long phys_addr,
84914cf11afSPaul Mackerras 				unsigned int flags,
85014cf11afSPaul Mackerras 				unsigned int isu_size,
85114cf11afSPaul Mackerras 				unsigned int irq_count,
85214cf11afSPaul Mackerras 				const char *name)
85314cf11afSPaul Mackerras {
85414cf11afSPaul Mackerras 	struct mpic	*mpic;
85514cf11afSPaul Mackerras 	u32		reg;
85614cf11afSPaul Mackerras 	const char	*vers;
85714cf11afSPaul Mackerras 	int		i;
85814cf11afSPaul Mackerras 
85914cf11afSPaul Mackerras 	mpic = alloc_bootmem(sizeof(struct mpic));
86014cf11afSPaul Mackerras 	if (mpic == NULL)
86114cf11afSPaul Mackerras 		return NULL;
86214cf11afSPaul Mackerras 
86314cf11afSPaul Mackerras 	memset(mpic, 0, sizeof(struct mpic));
86414cf11afSPaul Mackerras 	mpic->name = name;
8650ebfff14SBenjamin Herrenschmidt 	mpic->of_node = node ? of_node_get(node) : NULL;
86614cf11afSPaul Mackerras 
8670ebfff14SBenjamin Herrenschmidt 	mpic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, 256,
8680ebfff14SBenjamin Herrenschmidt 				       &mpic_host_ops,
8690ebfff14SBenjamin Herrenschmidt 				       MPIC_VEC_SPURRIOUS);
8700ebfff14SBenjamin Herrenschmidt 	if (mpic->irqhost == NULL) {
8710ebfff14SBenjamin Herrenschmidt 		of_node_put(node);
8720ebfff14SBenjamin Herrenschmidt 		return NULL;
8730ebfff14SBenjamin Herrenschmidt 	}
8740ebfff14SBenjamin Herrenschmidt 
8750ebfff14SBenjamin Herrenschmidt 	mpic->irqhost->host_data = mpic;
876b9e5b4e6SBenjamin Herrenschmidt 	mpic->hc_irq = mpic_irq_chip;
87714cf11afSPaul Mackerras 	mpic->hc_irq.typename = name;
87814cf11afSPaul Mackerras 	if (flags & MPIC_PRIMARY)
87914cf11afSPaul Mackerras 		mpic->hc_irq.set_affinity = mpic_set_affinity;
880b9e5b4e6SBenjamin Herrenschmidt #ifdef CONFIG_MPIC_BROKEN_U3
881b9e5b4e6SBenjamin Herrenschmidt 	mpic->hc_ht_irq = mpic_irq_ht_chip;
882b9e5b4e6SBenjamin Herrenschmidt 	mpic->hc_ht_irq.typename = name;
883b9e5b4e6SBenjamin Herrenschmidt 	if (flags & MPIC_PRIMARY)
884b9e5b4e6SBenjamin Herrenschmidt 		mpic->hc_ht_irq.set_affinity = mpic_set_affinity;
885b9e5b4e6SBenjamin Herrenschmidt #endif /* CONFIG_MPIC_BROKEN_U3 */
88614cf11afSPaul Mackerras #ifdef CONFIG_SMP
887b9e5b4e6SBenjamin Herrenschmidt 	mpic->hc_ipi = mpic_ipi_chip;
8880ebfff14SBenjamin Herrenschmidt 	mpic->hc_ipi.typename = name;
88914cf11afSPaul Mackerras #endif /* CONFIG_SMP */
89014cf11afSPaul Mackerras 
89114cf11afSPaul Mackerras 	mpic->flags = flags;
89214cf11afSPaul Mackerras 	mpic->isu_size = isu_size;
89314cf11afSPaul Mackerras 	mpic->irq_count = irq_count;
89414cf11afSPaul Mackerras 	mpic->num_sources = 0; /* so far */
89514cf11afSPaul Mackerras 
896*7233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD
897*7233593bSZang Roy-r61911 	mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)];
898*7233593bSZang Roy-r61911 #endif
899*7233593bSZang Roy-r61911 
90014cf11afSPaul Mackerras 	/* Map the global registers */
901*7233593bSZang Roy-r61911 	mpic->gregs = ioremap(phys_addr + MPIC_INFO(GREG_BASE), 0x1000);
902*7233593bSZang Roy-r61911 	mpic->tmregs = mpic->gregs +
903*7233593bSZang Roy-r61911 		       ((MPIC_INFO(TIMER_BASE) - MPIC_INFO(GREG_BASE)) >> 2);
90414cf11afSPaul Mackerras 	BUG_ON(mpic->gregs == NULL);
90514cf11afSPaul Mackerras 
90614cf11afSPaul Mackerras 	/* Reset */
90714cf11afSPaul Mackerras 	if (flags & MPIC_WANTS_RESET) {
908*7233593bSZang Roy-r61911 		mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
909*7233593bSZang Roy-r61911 			   mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
91014cf11afSPaul Mackerras 			   | MPIC_GREG_GCONF_RESET);
911*7233593bSZang Roy-r61911 		while( mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
91214cf11afSPaul Mackerras 		       & MPIC_GREG_GCONF_RESET)
91314cf11afSPaul Mackerras 			mb();
91414cf11afSPaul Mackerras 	}
91514cf11afSPaul Mackerras 
91614cf11afSPaul Mackerras 	/* Read feature register, calculate num CPUs and, for non-ISU
91714cf11afSPaul Mackerras 	 * MPICs, num sources as well. On ISU MPICs, sources are counted
91814cf11afSPaul Mackerras 	 * as ISUs are added
91914cf11afSPaul Mackerras 	 */
920*7233593bSZang Roy-r61911 	reg = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0));
92114cf11afSPaul Mackerras 	mpic->num_cpus = ((reg & MPIC_GREG_FEATURE_LAST_CPU_MASK)
92214cf11afSPaul Mackerras 			  >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1;
92314cf11afSPaul Mackerras 	if (isu_size == 0)
92414cf11afSPaul Mackerras 		mpic->num_sources = ((reg & MPIC_GREG_FEATURE_LAST_SRC_MASK)
92514cf11afSPaul Mackerras 				     >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1;
92614cf11afSPaul Mackerras 
92714cf11afSPaul Mackerras 	/* Map the per-CPU registers */
92814cf11afSPaul Mackerras 	for (i = 0; i < mpic->num_cpus; i++) {
929*7233593bSZang Roy-r61911 		mpic->cpuregs[i] = ioremap(phys_addr + MPIC_INFO(CPU_BASE) +
930*7233593bSZang Roy-r61911 					   i * MPIC_INFO(CPU_STRIDE), 0x1000);
93114cf11afSPaul Mackerras 		BUG_ON(mpic->cpuregs[i] == NULL);
93214cf11afSPaul Mackerras 	}
93314cf11afSPaul Mackerras 
93414cf11afSPaul Mackerras 	/* Initialize main ISU if none provided */
93514cf11afSPaul Mackerras 	if (mpic->isu_size == 0) {
93614cf11afSPaul Mackerras 		mpic->isu_size = mpic->num_sources;
937*7233593bSZang Roy-r61911 		mpic->isus[0] = ioremap(phys_addr + MPIC_INFO(IRQ_BASE),
938*7233593bSZang Roy-r61911 					MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
93914cf11afSPaul Mackerras 		BUG_ON(mpic->isus[0] == NULL);
94014cf11afSPaul Mackerras 	}
94114cf11afSPaul Mackerras 	mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
94214cf11afSPaul Mackerras 	mpic->isu_mask = (1 << mpic->isu_shift) - 1;
94314cf11afSPaul Mackerras 
94414cf11afSPaul Mackerras 	/* Display version */
94514cf11afSPaul Mackerras 	switch (reg & MPIC_GREG_FEATURE_VERSION_MASK) {
94614cf11afSPaul Mackerras 	case 1:
94714cf11afSPaul Mackerras 		vers = "1.0";
94814cf11afSPaul Mackerras 		break;
94914cf11afSPaul Mackerras 	case 2:
95014cf11afSPaul Mackerras 		vers = "1.2";
95114cf11afSPaul Mackerras 		break;
95214cf11afSPaul Mackerras 	case 3:
95314cf11afSPaul Mackerras 		vers = "1.3";
95414cf11afSPaul Mackerras 		break;
95514cf11afSPaul Mackerras 	default:
95614cf11afSPaul Mackerras 		vers = "<unknown>";
95714cf11afSPaul Mackerras 		break;
95814cf11afSPaul Mackerras 	}
95914cf11afSPaul Mackerras 	printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %lx, max %d CPUs\n",
96014cf11afSPaul Mackerras 	       name, vers, phys_addr, mpic->num_cpus);
96114cf11afSPaul Mackerras 	printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", mpic->isu_size,
96214cf11afSPaul Mackerras 	       mpic->isu_shift, mpic->isu_mask);
96314cf11afSPaul Mackerras 
96414cf11afSPaul Mackerras 	mpic->next = mpics;
96514cf11afSPaul Mackerras 	mpics = mpic;
96614cf11afSPaul Mackerras 
9670ebfff14SBenjamin Herrenschmidt 	if (flags & MPIC_PRIMARY) {
96814cf11afSPaul Mackerras 		mpic_primary = mpic;
9690ebfff14SBenjamin Herrenschmidt 		irq_set_default_host(mpic->irqhost);
9700ebfff14SBenjamin Herrenschmidt 	}
97114cf11afSPaul Mackerras 
97214cf11afSPaul Mackerras 	return mpic;
97314cf11afSPaul Mackerras }
97414cf11afSPaul Mackerras 
97514cf11afSPaul Mackerras void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
97614cf11afSPaul Mackerras 			    unsigned long phys_addr)
97714cf11afSPaul Mackerras {
97814cf11afSPaul Mackerras 	unsigned int isu_first = isu_num * mpic->isu_size;
97914cf11afSPaul Mackerras 
98014cf11afSPaul Mackerras 	BUG_ON(isu_num >= MPIC_MAX_ISU);
98114cf11afSPaul Mackerras 
982*7233593bSZang Roy-r61911 	mpic->isus[isu_num] = ioremap(phys_addr,
983*7233593bSZang Roy-r61911 				      MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
98414cf11afSPaul Mackerras 	if ((isu_first + mpic->isu_size) > mpic->num_sources)
98514cf11afSPaul Mackerras 		mpic->num_sources = isu_first + mpic->isu_size;
98614cf11afSPaul Mackerras }
98714cf11afSPaul Mackerras 
9880ebfff14SBenjamin Herrenschmidt void __init mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count)
9890ebfff14SBenjamin Herrenschmidt {
9900ebfff14SBenjamin Herrenschmidt 	mpic->senses = senses;
9910ebfff14SBenjamin Herrenschmidt 	mpic->senses_count = count;
9920ebfff14SBenjamin Herrenschmidt }
9930ebfff14SBenjamin Herrenschmidt 
99414cf11afSPaul Mackerras void __init mpic_init(struct mpic *mpic)
99514cf11afSPaul Mackerras {
99614cf11afSPaul Mackerras 	int i;
99714cf11afSPaul Mackerras 
99814cf11afSPaul Mackerras 	BUG_ON(mpic->num_sources == 0);
9990ebfff14SBenjamin Herrenschmidt 	WARN_ON(mpic->num_sources > MPIC_VEC_IPI_0);
10000ebfff14SBenjamin Herrenschmidt 
10010ebfff14SBenjamin Herrenschmidt 	/* Sanitize source count */
10020ebfff14SBenjamin Herrenschmidt 	if (mpic->num_sources > MPIC_VEC_IPI_0)
10030ebfff14SBenjamin Herrenschmidt 		mpic->num_sources = MPIC_VEC_IPI_0;
100414cf11afSPaul Mackerras 
100514cf11afSPaul Mackerras 	printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources);
100614cf11afSPaul Mackerras 
100714cf11afSPaul Mackerras 	/* Set current processor priority to max */
1008*7233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
100914cf11afSPaul Mackerras 
101014cf11afSPaul Mackerras 	/* Initialize timers: just disable them all */
101114cf11afSPaul Mackerras 	for (i = 0; i < 4; i++) {
101214cf11afSPaul Mackerras 		mpic_write(mpic->tmregs,
1013*7233593bSZang Roy-r61911 			   i * MPIC_INFO(TIMER_STRIDE) +
1014*7233593bSZang Roy-r61911 			   MPIC_INFO(TIMER_DESTINATION), 0);
101514cf11afSPaul Mackerras 		mpic_write(mpic->tmregs,
1016*7233593bSZang Roy-r61911 			   i * MPIC_INFO(TIMER_STRIDE) +
1017*7233593bSZang Roy-r61911 			   MPIC_INFO(TIMER_VECTOR_PRI),
101814cf11afSPaul Mackerras 			   MPIC_VECPRI_MASK |
101914cf11afSPaul Mackerras 			   (MPIC_VEC_TIMER_0 + i));
102014cf11afSPaul Mackerras 	}
102114cf11afSPaul Mackerras 
102214cf11afSPaul Mackerras 	/* Initialize IPIs to our reserved vectors and mark them disabled for now */
102314cf11afSPaul Mackerras 	mpic_test_broken_ipi(mpic);
102414cf11afSPaul Mackerras 	for (i = 0; i < 4; i++) {
102514cf11afSPaul Mackerras 		mpic_ipi_write(i,
102614cf11afSPaul Mackerras 			       MPIC_VECPRI_MASK |
102714cf11afSPaul Mackerras 			       (10 << MPIC_VECPRI_PRIORITY_SHIFT) |
102814cf11afSPaul Mackerras 			       (MPIC_VEC_IPI_0 + i));
102914cf11afSPaul Mackerras 	}
103014cf11afSPaul Mackerras 
103114cf11afSPaul Mackerras 	/* Initialize interrupt sources */
103214cf11afSPaul Mackerras 	if (mpic->irq_count == 0)
103314cf11afSPaul Mackerras 		mpic->irq_count = mpic->num_sources;
103414cf11afSPaul Mackerras 
10351beb6a7dSBenjamin Herrenschmidt 	/* Do the HT PIC fixups on U3 broken mpic */
103614cf11afSPaul Mackerras 	DBG("MPIC flags: %x\n", mpic->flags);
103714cf11afSPaul Mackerras 	if ((mpic->flags & MPIC_BROKEN_U3) && (mpic->flags & MPIC_PRIMARY))
10381beb6a7dSBenjamin Herrenschmidt  		mpic_scan_ht_pics(mpic);
103914cf11afSPaul Mackerras 
104014cf11afSPaul Mackerras 	for (i = 0; i < mpic->num_sources; i++) {
104114cf11afSPaul Mackerras 		/* start with vector = source number, and masked */
10426e99e458SBenjamin Herrenschmidt 		u32 vecpri = MPIC_VECPRI_MASK | i |
10436e99e458SBenjamin Herrenschmidt 			(8 << MPIC_VECPRI_PRIORITY_SHIFT);
104414cf11afSPaul Mackerras 
104514cf11afSPaul Mackerras 		/* init hw */
1046*7233593bSZang Roy-r61911 		mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
1047*7233593bSZang Roy-r61911 		mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
104814cf11afSPaul Mackerras 			       1 << hard_smp_processor_id());
104914cf11afSPaul Mackerras 	}
105014cf11afSPaul Mackerras 
105114cf11afSPaul Mackerras 	/* Init spurrious vector */
1052*7233593bSZang Roy-r61911 	mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), MPIC_VEC_SPURRIOUS);
105314cf11afSPaul Mackerras 
1054*7233593bSZang Roy-r61911 	/* Disable 8259 passthrough, if supported */
1055*7233593bSZang Roy-r61911 	if (!(mpic->flags & MPIC_NO_PTHROU_DIS))
1056*7233593bSZang Roy-r61911 		mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
1057*7233593bSZang Roy-r61911 			   mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
105814cf11afSPaul Mackerras 			   | MPIC_GREG_GCONF_8259_PTHROU_DIS);
105914cf11afSPaul Mackerras 
106014cf11afSPaul Mackerras 	/* Set current processor priority to 0 */
1061*7233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0);
106214cf11afSPaul Mackerras }
106314cf11afSPaul Mackerras 
1064868ea0c9SMark A. Greer void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio)
1065868ea0c9SMark A. Greer {
1066868ea0c9SMark A. Greer 	u32 v;
106714cf11afSPaul Mackerras 
1068868ea0c9SMark A. Greer 	v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1);
1069868ea0c9SMark A. Greer 	v &= ~MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO_MASK;
1070868ea0c9SMark A. Greer 	v |= MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO(clock_ratio);
1071868ea0c9SMark A. Greer 	mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v);
1072868ea0c9SMark A. Greer }
1073868ea0c9SMark A. Greer 
1074868ea0c9SMark A. Greer void __init mpic_set_serial_int(struct mpic *mpic, int enable)
1075868ea0c9SMark A. Greer {
1076ba1826e5SBenjamin Herrenschmidt 	unsigned long flags;
1077868ea0c9SMark A. Greer 	u32 v;
1078868ea0c9SMark A. Greer 
1079ba1826e5SBenjamin Herrenschmidt 	spin_lock_irqsave(&mpic_lock, flags);
1080868ea0c9SMark A. Greer 	v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1);
1081868ea0c9SMark A. Greer 	if (enable)
1082868ea0c9SMark A. Greer 		v |= MPIC_GREG_GLOBAL_CONF_1_SIE;
1083868ea0c9SMark A. Greer 	else
1084868ea0c9SMark A. Greer 		v &= ~MPIC_GREG_GLOBAL_CONF_1_SIE;
1085868ea0c9SMark A. Greer 	mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v);
1086ba1826e5SBenjamin Herrenschmidt 	spin_unlock_irqrestore(&mpic_lock, flags);
1087868ea0c9SMark A. Greer }
108814cf11afSPaul Mackerras 
108914cf11afSPaul Mackerras void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
109014cf11afSPaul Mackerras {
109114cf11afSPaul Mackerras 	int is_ipi;
109214cf11afSPaul Mackerras 	struct mpic *mpic = mpic_find(irq, &is_ipi);
10930ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
109414cf11afSPaul Mackerras 	unsigned long flags;
109514cf11afSPaul Mackerras 	u32 reg;
109614cf11afSPaul Mackerras 
109714cf11afSPaul Mackerras 	spin_lock_irqsave(&mpic_lock, flags);
109814cf11afSPaul Mackerras 	if (is_ipi) {
10990ebfff14SBenjamin Herrenschmidt 		reg = mpic_ipi_read(src - MPIC_VEC_IPI_0) &
1100e5356640SBenjamin Herrenschmidt 			~MPIC_VECPRI_PRIORITY_MASK;
11010ebfff14SBenjamin Herrenschmidt 		mpic_ipi_write(src - MPIC_VEC_IPI_0,
110214cf11afSPaul Mackerras 			       reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
110314cf11afSPaul Mackerras 	} else {
1104*7233593bSZang Roy-r61911 		reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI))
1105e5356640SBenjamin Herrenschmidt 			& ~MPIC_VECPRI_PRIORITY_MASK;
1106*7233593bSZang Roy-r61911 		mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
110714cf11afSPaul Mackerras 			       reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
110814cf11afSPaul Mackerras 	}
110914cf11afSPaul Mackerras 	spin_unlock_irqrestore(&mpic_lock, flags);
111014cf11afSPaul Mackerras }
111114cf11afSPaul Mackerras 
111214cf11afSPaul Mackerras unsigned int mpic_irq_get_priority(unsigned int irq)
111314cf11afSPaul Mackerras {
111414cf11afSPaul Mackerras 	int is_ipi;
111514cf11afSPaul Mackerras 	struct mpic *mpic = mpic_find(irq, &is_ipi);
11160ebfff14SBenjamin Herrenschmidt 	unsigned int src = mpic_irq_to_hw(irq);
111714cf11afSPaul Mackerras 	unsigned long flags;
111814cf11afSPaul Mackerras 	u32 reg;
111914cf11afSPaul Mackerras 
112014cf11afSPaul Mackerras 	spin_lock_irqsave(&mpic_lock, flags);
112114cf11afSPaul Mackerras 	if (is_ipi)
11220ebfff14SBenjamin Herrenschmidt 		reg = mpic_ipi_read(src = MPIC_VEC_IPI_0);
112314cf11afSPaul Mackerras 	else
1124*7233593bSZang Roy-r61911 		reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
112514cf11afSPaul Mackerras 	spin_unlock_irqrestore(&mpic_lock, flags);
112614cf11afSPaul Mackerras 	return (reg & MPIC_VECPRI_PRIORITY_MASK) >> MPIC_VECPRI_PRIORITY_SHIFT;
112714cf11afSPaul Mackerras }
112814cf11afSPaul Mackerras 
112914cf11afSPaul Mackerras void mpic_setup_this_cpu(void)
113014cf11afSPaul Mackerras {
113114cf11afSPaul Mackerras #ifdef CONFIG_SMP
113214cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
113314cf11afSPaul Mackerras 	unsigned long flags;
113414cf11afSPaul Mackerras 	u32 msk = 1 << hard_smp_processor_id();
113514cf11afSPaul Mackerras 	unsigned int i;
113614cf11afSPaul Mackerras 
113714cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
113814cf11afSPaul Mackerras 
113914cf11afSPaul Mackerras 	DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
114014cf11afSPaul Mackerras 
114114cf11afSPaul Mackerras 	spin_lock_irqsave(&mpic_lock, flags);
114214cf11afSPaul Mackerras 
114314cf11afSPaul Mackerras  	/* let the mpic know we want intrs. default affinity is 0xffffffff
114414cf11afSPaul Mackerras 	 * until changed via /proc. That's how it's done on x86. If we want
114514cf11afSPaul Mackerras 	 * it differently, then we should make sure we also change the default
1146a53da52fSIngo Molnar 	 * values of irq_desc[].affinity in irq.c.
114714cf11afSPaul Mackerras  	 */
114814cf11afSPaul Mackerras 	if (distribute_irqs) {
114914cf11afSPaul Mackerras 	 	for (i = 0; i < mpic->num_sources ; i++)
1150*7233593bSZang Roy-r61911 			mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
1151*7233593bSZang Roy-r61911 				mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk);
115214cf11afSPaul Mackerras 	}
115314cf11afSPaul Mackerras 
115414cf11afSPaul Mackerras 	/* Set current processor priority to 0 */
1155*7233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0);
115614cf11afSPaul Mackerras 
115714cf11afSPaul Mackerras 	spin_unlock_irqrestore(&mpic_lock, flags);
115814cf11afSPaul Mackerras #endif /* CONFIG_SMP */
115914cf11afSPaul Mackerras }
116014cf11afSPaul Mackerras 
116114cf11afSPaul Mackerras int mpic_cpu_get_priority(void)
116214cf11afSPaul Mackerras {
116314cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
116414cf11afSPaul Mackerras 
1165*7233593bSZang Roy-r61911 	return mpic_cpu_read(MPIC_INFO(CPU_CURRENT_TASK_PRI));
116614cf11afSPaul Mackerras }
116714cf11afSPaul Mackerras 
116814cf11afSPaul Mackerras void mpic_cpu_set_priority(int prio)
116914cf11afSPaul Mackerras {
117014cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
117114cf11afSPaul Mackerras 
117214cf11afSPaul Mackerras 	prio &= MPIC_CPU_TASKPRI_MASK;
1173*7233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), prio);
117414cf11afSPaul Mackerras }
117514cf11afSPaul Mackerras 
117614cf11afSPaul Mackerras /*
117714cf11afSPaul Mackerras  * XXX: someone who knows mpic should check this.
117814cf11afSPaul Mackerras  * do we need to eoi the ipi including for kexec cpu here (see xics comments)?
117914cf11afSPaul Mackerras  * or can we reset the mpic in the new kernel?
118014cf11afSPaul Mackerras  */
118114cf11afSPaul Mackerras void mpic_teardown_this_cpu(int secondary)
118214cf11afSPaul Mackerras {
118314cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
118414cf11afSPaul Mackerras 	unsigned long flags;
118514cf11afSPaul Mackerras 	u32 msk = 1 << hard_smp_processor_id();
118614cf11afSPaul Mackerras 	unsigned int i;
118714cf11afSPaul Mackerras 
118814cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
118914cf11afSPaul Mackerras 
119014cf11afSPaul Mackerras 	DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
119114cf11afSPaul Mackerras 	spin_lock_irqsave(&mpic_lock, flags);
119214cf11afSPaul Mackerras 
119314cf11afSPaul Mackerras 	/* let the mpic know we don't want intrs.  */
119414cf11afSPaul Mackerras 	for (i = 0; i < mpic->num_sources ; i++)
1195*7233593bSZang Roy-r61911 		mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
1196*7233593bSZang Roy-r61911 			mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) & ~msk);
119714cf11afSPaul Mackerras 
119814cf11afSPaul Mackerras 	/* Set current processor priority to max */
1199*7233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
120014cf11afSPaul Mackerras 
120114cf11afSPaul Mackerras 	spin_unlock_irqrestore(&mpic_lock, flags);
120214cf11afSPaul Mackerras }
120314cf11afSPaul Mackerras 
120414cf11afSPaul Mackerras 
120514cf11afSPaul Mackerras void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask)
120614cf11afSPaul Mackerras {
120714cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
120814cf11afSPaul Mackerras 
120914cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
121014cf11afSPaul Mackerras 
12111beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_IPI
121214cf11afSPaul Mackerras 	DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no);
12131beb6a7dSBenjamin Herrenschmidt #endif
121414cf11afSPaul Mackerras 
1215*7233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) +
1216*7233593bSZang Roy-r61911 		       ipi_no * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE),
121714cf11afSPaul Mackerras 		       mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0]));
121814cf11afSPaul Mackerras }
121914cf11afSPaul Mackerras 
12200ebfff14SBenjamin Herrenschmidt unsigned int mpic_get_one_irq(struct mpic *mpic, struct pt_regs *regs)
122114cf11afSPaul Mackerras {
12220ebfff14SBenjamin Herrenschmidt 	u32 src;
122314cf11afSPaul Mackerras 
1224*7233593bSZang Roy-r61911 	src = mpic_cpu_read(MPIC_INFO(CPU_INTACK)) & MPIC_INFO(VECPRI_VECTOR_MASK);
12251beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_LOW
12260ebfff14SBenjamin Herrenschmidt 	DBG("%s: get_one_irq(): %d\n", mpic->name, src);
12271beb6a7dSBenjamin Herrenschmidt #endif
12280ebfff14SBenjamin Herrenschmidt 	if (unlikely(src == MPIC_VEC_SPURRIOUS))
12290ebfff14SBenjamin Herrenschmidt 		return NO_IRQ;
12300ebfff14SBenjamin Herrenschmidt 	return irq_linear_revmap(mpic->irqhost, src);
123114cf11afSPaul Mackerras }
123214cf11afSPaul Mackerras 
12330ebfff14SBenjamin Herrenschmidt unsigned int mpic_get_irq(struct pt_regs *regs)
123414cf11afSPaul Mackerras {
123514cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
123614cf11afSPaul Mackerras 
123714cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
123814cf11afSPaul Mackerras 
123914cf11afSPaul Mackerras 	return mpic_get_one_irq(mpic, regs);
124014cf11afSPaul Mackerras }
124114cf11afSPaul Mackerras 
124214cf11afSPaul Mackerras 
124314cf11afSPaul Mackerras #ifdef CONFIG_SMP
124414cf11afSPaul Mackerras void mpic_request_ipis(void)
124514cf11afSPaul Mackerras {
124614cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
12470ebfff14SBenjamin Herrenschmidt 	int i;
12480ebfff14SBenjamin Herrenschmidt 	static char *ipi_names[] = {
12490ebfff14SBenjamin Herrenschmidt 		"IPI0 (call function)",
12500ebfff14SBenjamin Herrenschmidt 		"IPI1 (reschedule)",
12510ebfff14SBenjamin Herrenschmidt 		"IPI2 (unused)",
12520ebfff14SBenjamin Herrenschmidt 		"IPI3 (debugger break)",
12530ebfff14SBenjamin Herrenschmidt 	};
125414cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
125514cf11afSPaul Mackerras 
12560ebfff14SBenjamin Herrenschmidt 	printk(KERN_INFO "mpic: requesting IPIs ... \n");
125714cf11afSPaul Mackerras 
12580ebfff14SBenjamin Herrenschmidt 	for (i = 0; i < 4; i++) {
12590ebfff14SBenjamin Herrenschmidt 		unsigned int vipi = irq_create_mapping(mpic->irqhost,
12606e99e458SBenjamin Herrenschmidt 						       MPIC_VEC_IPI_0 + i);
12610ebfff14SBenjamin Herrenschmidt 		if (vipi == NO_IRQ) {
12620ebfff14SBenjamin Herrenschmidt 			printk(KERN_ERR "Failed to map IPI %d\n", i);
12630ebfff14SBenjamin Herrenschmidt 			break;
12640ebfff14SBenjamin Herrenschmidt 		}
12650ebfff14SBenjamin Herrenschmidt 		request_irq(vipi, mpic_ipi_action, IRQF_DISABLED,
12660ebfff14SBenjamin Herrenschmidt 			    ipi_names[i], mpic);
12670ebfff14SBenjamin Herrenschmidt 	}
126814cf11afSPaul Mackerras }
1269a9c59264SPaul Mackerras 
1270a9c59264SPaul Mackerras void smp_mpic_message_pass(int target, int msg)
1271a9c59264SPaul Mackerras {
1272a9c59264SPaul Mackerras 	/* make sure we're sending something that translates to an IPI */
1273a9c59264SPaul Mackerras 	if ((unsigned int)msg > 3) {
1274a9c59264SPaul Mackerras 		printk("SMP %d: smp_message_pass: unknown msg %d\n",
1275a9c59264SPaul Mackerras 		       smp_processor_id(), msg);
1276a9c59264SPaul Mackerras 		return;
1277a9c59264SPaul Mackerras 	}
1278a9c59264SPaul Mackerras 	switch (target) {
1279a9c59264SPaul Mackerras 	case MSG_ALL:
1280a9c59264SPaul Mackerras 		mpic_send_ipi(msg, 0xffffffff);
1281a9c59264SPaul Mackerras 		break;
1282a9c59264SPaul Mackerras 	case MSG_ALL_BUT_SELF:
1283a9c59264SPaul Mackerras 		mpic_send_ipi(msg, 0xffffffff & ~(1 << smp_processor_id()));
1284a9c59264SPaul Mackerras 		break;
1285a9c59264SPaul Mackerras 	default:
1286a9c59264SPaul Mackerras 		mpic_send_ipi(msg, 1 << target);
1287a9c59264SPaul Mackerras 		break;
1288a9c59264SPaul Mackerras 	}
1289a9c59264SPaul Mackerras }
129014cf11afSPaul Mackerras #endif /* CONFIG_SMP */
1291