xref: /linux/arch/powerpc/sysdev/mpic.c (revision e51df2c170efaeadce4d416e1825b0830de0a795)
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.
903bcb7e3SVarun Sethi  *  Copyright 2010-2012 Freescale Semiconductor, Inc.
1014cf11afSPaul Mackerras  *
1114cf11afSPaul Mackerras  *  This file is subject to the terms and conditions of the GNU General Public
1214cf11afSPaul Mackerras  *  License.  See the file COPYING in the main directory of this archive
1314cf11afSPaul Mackerras  *  for more details.
1414cf11afSPaul Mackerras  */
1514cf11afSPaul Mackerras 
1614cf11afSPaul Mackerras #undef DEBUG
171beb6a7dSBenjamin Herrenschmidt #undef DEBUG_IPI
181beb6a7dSBenjamin Herrenschmidt #undef DEBUG_IRQ
191beb6a7dSBenjamin Herrenschmidt #undef DEBUG_LOW
2014cf11afSPaul Mackerras 
2114cf11afSPaul Mackerras #include <linux/types.h>
2214cf11afSPaul Mackerras #include <linux/kernel.h>
2314cf11afSPaul Mackerras #include <linux/init.h>
2414cf11afSPaul Mackerras #include <linux/irq.h>
2514cf11afSPaul Mackerras #include <linux/smp.h>
2614cf11afSPaul Mackerras #include <linux/interrupt.h>
2714cf11afSPaul Mackerras #include <linux/bootmem.h>
2814cf11afSPaul Mackerras #include <linux/spinlock.h>
2914cf11afSPaul Mackerras #include <linux/pci.h>
305a0e3ad6STejun Heo #include <linux/slab.h>
31f5a592f7SRafael J. Wysocki #include <linux/syscore_ops.h>
3276462232SChristian Dietrich #include <linux/ratelimit.h>
3314cf11afSPaul Mackerras 
3414cf11afSPaul Mackerras #include <asm/ptrace.h>
3514cf11afSPaul Mackerras #include <asm/signal.h>
3614cf11afSPaul Mackerras #include <asm/io.h>
3714cf11afSPaul Mackerras #include <asm/pgtable.h>
3814cf11afSPaul Mackerras #include <asm/irq.h>
3914cf11afSPaul Mackerras #include <asm/machdep.h>
4014cf11afSPaul Mackerras #include <asm/mpic.h>
4114cf11afSPaul Mackerras #include <asm/smp.h>
4214cf11afSPaul Mackerras 
43a7de7c74SMichael Ellerman #include "mpic.h"
44a7de7c74SMichael Ellerman 
4514cf11afSPaul Mackerras #ifdef DEBUG
4614cf11afSPaul Mackerras #define DBG(fmt...) printk(fmt)
4714cf11afSPaul Mackerras #else
4814cf11afSPaul Mackerras #define DBG(fmt...)
4914cf11afSPaul Mackerras #endif
5014cf11afSPaul Mackerras 
519e6f31a9SDongsheng.wang@freescale.com struct bus_type mpic_subsys = {
529e6f31a9SDongsheng.wang@freescale.com 	.name = "mpic",
539e6f31a9SDongsheng.wang@freescale.com 	.dev_name = "mpic",
549e6f31a9SDongsheng.wang@freescale.com };
559e6f31a9SDongsheng.wang@freescale.com EXPORT_SYMBOL_GPL(mpic_subsys);
569e6f31a9SDongsheng.wang@freescale.com 
5714cf11afSPaul Mackerras static struct mpic *mpics;
5814cf11afSPaul Mackerras static struct mpic *mpic_primary;
59203041adSThomas Gleixner static DEFINE_RAW_SPINLOCK(mpic_lock);
6014cf11afSPaul Mackerras 
61c0c0d996SPaul Mackerras #ifdef CONFIG_PPC32	/* XXX for now */
62e40c7f02SAndy Whitcroft #ifdef CONFIG_IRQ_ALL_CPUS
63e242114aSchenhui zhao #define distribute_irqs	(1)
64e40c7f02SAndy Whitcroft #else
65e40c7f02SAndy Whitcroft #define distribute_irqs	(0)
66e40c7f02SAndy Whitcroft #endif
67c0c0d996SPaul Mackerras #endif
6814cf11afSPaul Mackerras 
697233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD
707233593bSZang Roy-r61911 static u32 mpic_infos[][MPIC_IDX_END] = {
717233593bSZang Roy-r61911 	[0] = {	/* Original OpenPIC compatible MPIC */
727233593bSZang Roy-r61911 		MPIC_GREG_BASE,
737233593bSZang Roy-r61911 		MPIC_GREG_FEATURE_0,
747233593bSZang Roy-r61911 		MPIC_GREG_GLOBAL_CONF_0,
757233593bSZang Roy-r61911 		MPIC_GREG_VENDOR_ID,
767233593bSZang Roy-r61911 		MPIC_GREG_IPI_VECTOR_PRI_0,
777233593bSZang Roy-r61911 		MPIC_GREG_IPI_STRIDE,
787233593bSZang Roy-r61911 		MPIC_GREG_SPURIOUS,
797233593bSZang Roy-r61911 		MPIC_GREG_TIMER_FREQ,
807233593bSZang Roy-r61911 
817233593bSZang Roy-r61911 		MPIC_TIMER_BASE,
827233593bSZang Roy-r61911 		MPIC_TIMER_STRIDE,
837233593bSZang Roy-r61911 		MPIC_TIMER_CURRENT_CNT,
847233593bSZang Roy-r61911 		MPIC_TIMER_BASE_CNT,
857233593bSZang Roy-r61911 		MPIC_TIMER_VECTOR_PRI,
867233593bSZang Roy-r61911 		MPIC_TIMER_DESTINATION,
877233593bSZang Roy-r61911 
887233593bSZang Roy-r61911 		MPIC_CPU_BASE,
897233593bSZang Roy-r61911 		MPIC_CPU_STRIDE,
907233593bSZang Roy-r61911 		MPIC_CPU_IPI_DISPATCH_0,
917233593bSZang Roy-r61911 		MPIC_CPU_IPI_DISPATCH_STRIDE,
927233593bSZang Roy-r61911 		MPIC_CPU_CURRENT_TASK_PRI,
937233593bSZang Roy-r61911 		MPIC_CPU_WHOAMI,
947233593bSZang Roy-r61911 		MPIC_CPU_INTACK,
957233593bSZang Roy-r61911 		MPIC_CPU_EOI,
96f365355eSOlof Johansson 		MPIC_CPU_MCACK,
977233593bSZang Roy-r61911 
987233593bSZang Roy-r61911 		MPIC_IRQ_BASE,
997233593bSZang Roy-r61911 		MPIC_IRQ_STRIDE,
1007233593bSZang Roy-r61911 		MPIC_IRQ_VECTOR_PRI,
1017233593bSZang Roy-r61911 		MPIC_VECPRI_VECTOR_MASK,
1027233593bSZang Roy-r61911 		MPIC_VECPRI_POLARITY_POSITIVE,
1037233593bSZang Roy-r61911 		MPIC_VECPRI_POLARITY_NEGATIVE,
1047233593bSZang Roy-r61911 		MPIC_VECPRI_SENSE_LEVEL,
1057233593bSZang Roy-r61911 		MPIC_VECPRI_SENSE_EDGE,
1067233593bSZang Roy-r61911 		MPIC_VECPRI_POLARITY_MASK,
1077233593bSZang Roy-r61911 		MPIC_VECPRI_SENSE_MASK,
1087233593bSZang Roy-r61911 		MPIC_IRQ_DESTINATION
1097233593bSZang Roy-r61911 	},
1107233593bSZang Roy-r61911 	[1] = {	/* Tsi108/109 PIC */
1117233593bSZang Roy-r61911 		TSI108_GREG_BASE,
1127233593bSZang Roy-r61911 		TSI108_GREG_FEATURE_0,
1137233593bSZang Roy-r61911 		TSI108_GREG_GLOBAL_CONF_0,
1147233593bSZang Roy-r61911 		TSI108_GREG_VENDOR_ID,
1157233593bSZang Roy-r61911 		TSI108_GREG_IPI_VECTOR_PRI_0,
1167233593bSZang Roy-r61911 		TSI108_GREG_IPI_STRIDE,
1177233593bSZang Roy-r61911 		TSI108_GREG_SPURIOUS,
1187233593bSZang Roy-r61911 		TSI108_GREG_TIMER_FREQ,
1197233593bSZang Roy-r61911 
1207233593bSZang Roy-r61911 		TSI108_TIMER_BASE,
1217233593bSZang Roy-r61911 		TSI108_TIMER_STRIDE,
1227233593bSZang Roy-r61911 		TSI108_TIMER_CURRENT_CNT,
1237233593bSZang Roy-r61911 		TSI108_TIMER_BASE_CNT,
1247233593bSZang Roy-r61911 		TSI108_TIMER_VECTOR_PRI,
1257233593bSZang Roy-r61911 		TSI108_TIMER_DESTINATION,
1267233593bSZang Roy-r61911 
1277233593bSZang Roy-r61911 		TSI108_CPU_BASE,
1287233593bSZang Roy-r61911 		TSI108_CPU_STRIDE,
1297233593bSZang Roy-r61911 		TSI108_CPU_IPI_DISPATCH_0,
1307233593bSZang Roy-r61911 		TSI108_CPU_IPI_DISPATCH_STRIDE,
1317233593bSZang Roy-r61911 		TSI108_CPU_CURRENT_TASK_PRI,
1327233593bSZang Roy-r61911 		TSI108_CPU_WHOAMI,
1337233593bSZang Roy-r61911 		TSI108_CPU_INTACK,
1347233593bSZang Roy-r61911 		TSI108_CPU_EOI,
135f365355eSOlof Johansson 		TSI108_CPU_MCACK,
1367233593bSZang Roy-r61911 
1377233593bSZang Roy-r61911 		TSI108_IRQ_BASE,
1387233593bSZang Roy-r61911 		TSI108_IRQ_STRIDE,
1397233593bSZang Roy-r61911 		TSI108_IRQ_VECTOR_PRI,
1407233593bSZang Roy-r61911 		TSI108_VECPRI_VECTOR_MASK,
1417233593bSZang Roy-r61911 		TSI108_VECPRI_POLARITY_POSITIVE,
1427233593bSZang Roy-r61911 		TSI108_VECPRI_POLARITY_NEGATIVE,
1437233593bSZang Roy-r61911 		TSI108_VECPRI_SENSE_LEVEL,
1447233593bSZang Roy-r61911 		TSI108_VECPRI_SENSE_EDGE,
1457233593bSZang Roy-r61911 		TSI108_VECPRI_POLARITY_MASK,
1467233593bSZang Roy-r61911 		TSI108_VECPRI_SENSE_MASK,
1477233593bSZang Roy-r61911 		TSI108_IRQ_DESTINATION
1487233593bSZang Roy-r61911 	},
1497233593bSZang Roy-r61911 };
1507233593bSZang Roy-r61911 
1517233593bSZang Roy-r61911 #define MPIC_INFO(name) mpic->hw_set[MPIC_IDX_##name]
1527233593bSZang Roy-r61911 
1537233593bSZang Roy-r61911 #else /* CONFIG_MPIC_WEIRD */
1547233593bSZang Roy-r61911 
1557233593bSZang Roy-r61911 #define MPIC_INFO(name) MPIC_##name
1567233593bSZang Roy-r61911 
1577233593bSZang Roy-r61911 #endif /* CONFIG_MPIC_WEIRD */
1587233593bSZang Roy-r61911 
159d6a2639bSMeador Inge static inline unsigned int mpic_processor_id(struct mpic *mpic)
160d6a2639bSMeador Inge {
161d6a2639bSMeador Inge 	unsigned int cpu = 0;
162d6a2639bSMeador Inge 
163be8bec56SKyle Moffett 	if (!(mpic->flags & MPIC_SECONDARY))
164d6a2639bSMeador Inge 		cpu = hard_smp_processor_id();
165d6a2639bSMeador Inge 
166d6a2639bSMeador Inge 	return cpu;
167d6a2639bSMeador Inge }
168d6a2639bSMeador Inge 
16914cf11afSPaul Mackerras /*
17014cf11afSPaul Mackerras  * Register accessor functions
17114cf11afSPaul Mackerras  */
17214cf11afSPaul Mackerras 
17314cf11afSPaul Mackerras 
174fbf0274eSBenjamin Herrenschmidt static inline u32 _mpic_read(enum mpic_reg_type type,
175fbf0274eSBenjamin Herrenschmidt 			     struct mpic_reg_bank *rb,
17614cf11afSPaul Mackerras 			     unsigned int reg)
17714cf11afSPaul Mackerras {
178fbf0274eSBenjamin Herrenschmidt 	switch(type) {
179fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR
180fbf0274eSBenjamin Herrenschmidt 	case mpic_access_dcr:
18183f34df4SMichael Ellerman 		return dcr_read(rb->dhost, reg);
182fbf0274eSBenjamin Herrenschmidt #endif
183fbf0274eSBenjamin Herrenschmidt 	case mpic_access_mmio_be:
184fbf0274eSBenjamin Herrenschmidt 		return in_be32(rb->base + (reg >> 2));
185fbf0274eSBenjamin Herrenschmidt 	case mpic_access_mmio_le:
186fbf0274eSBenjamin Herrenschmidt 	default:
187fbf0274eSBenjamin Herrenschmidt 		return in_le32(rb->base + (reg >> 2));
188fbf0274eSBenjamin Herrenschmidt 	}
18914cf11afSPaul Mackerras }
19014cf11afSPaul Mackerras 
191fbf0274eSBenjamin Herrenschmidt static inline void _mpic_write(enum mpic_reg_type type,
192fbf0274eSBenjamin Herrenschmidt 			       struct mpic_reg_bank *rb,
19314cf11afSPaul Mackerras  			       unsigned int reg, u32 value)
19414cf11afSPaul Mackerras {
195fbf0274eSBenjamin Herrenschmidt 	switch(type) {
196fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR
197fbf0274eSBenjamin Herrenschmidt 	case mpic_access_dcr:
198d9d1063dSJohannes Berg 		dcr_write(rb->dhost, reg, value);
199d9d1063dSJohannes Berg 		break;
200fbf0274eSBenjamin Herrenschmidt #endif
201fbf0274eSBenjamin Herrenschmidt 	case mpic_access_mmio_be:
202d9d1063dSJohannes Berg 		out_be32(rb->base + (reg >> 2), value);
203d9d1063dSJohannes Berg 		break;
204fbf0274eSBenjamin Herrenschmidt 	case mpic_access_mmio_le:
205fbf0274eSBenjamin Herrenschmidt 	default:
206d9d1063dSJohannes Berg 		out_le32(rb->base + (reg >> 2), value);
207d9d1063dSJohannes Berg 		break;
208fbf0274eSBenjamin Herrenschmidt 	}
20914cf11afSPaul Mackerras }
21014cf11afSPaul Mackerras 
21114cf11afSPaul Mackerras static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi)
21214cf11afSPaul Mackerras {
213fbf0274eSBenjamin Herrenschmidt 	enum mpic_reg_type type = mpic->reg_type;
2147233593bSZang Roy-r61911 	unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) +
2157233593bSZang Roy-r61911 			      (ipi * MPIC_INFO(GREG_IPI_STRIDE));
21614cf11afSPaul Mackerras 
217fbf0274eSBenjamin Herrenschmidt 	if ((mpic->flags & MPIC_BROKEN_IPI) && type == mpic_access_mmio_le)
218fbf0274eSBenjamin Herrenschmidt 		type = mpic_access_mmio_be;
219fbf0274eSBenjamin Herrenschmidt 	return _mpic_read(type, &mpic->gregs, offset);
22014cf11afSPaul Mackerras }
22114cf11afSPaul Mackerras 
22214cf11afSPaul Mackerras static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value)
22314cf11afSPaul Mackerras {
2247233593bSZang Roy-r61911 	unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) +
2257233593bSZang Roy-r61911 			      (ipi * MPIC_INFO(GREG_IPI_STRIDE));
22614cf11afSPaul Mackerras 
227fbf0274eSBenjamin Herrenschmidt 	_mpic_write(mpic->reg_type, &mpic->gregs, offset, value);
22814cf11afSPaul Mackerras }
22914cf11afSPaul Mackerras 
23003bcb7e3SVarun Sethi static inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm)
23103bcb7e3SVarun Sethi {
23203bcb7e3SVarun Sethi 	return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE +
23303bcb7e3SVarun Sethi 	       (tm & 3) * MPIC_INFO(TIMER_STRIDE);
23403bcb7e3SVarun Sethi }
23503bcb7e3SVarun Sethi 
236ea94187fSScott Wood static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm)
237ea94187fSScott Wood {
23803bcb7e3SVarun Sethi 	unsigned int offset = mpic_tm_offset(mpic, tm) +
23903bcb7e3SVarun Sethi 			      MPIC_INFO(TIMER_VECTOR_PRI);
240ea94187fSScott Wood 
241ea94187fSScott Wood 	return _mpic_read(mpic->reg_type, &mpic->tmregs, offset);
242ea94187fSScott Wood }
243ea94187fSScott Wood 
244ea94187fSScott Wood static inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value)
245ea94187fSScott Wood {
24603bcb7e3SVarun Sethi 	unsigned int offset = mpic_tm_offset(mpic, tm) +
24703bcb7e3SVarun Sethi 			      MPIC_INFO(TIMER_VECTOR_PRI);
248ea94187fSScott Wood 
249ea94187fSScott Wood 	_mpic_write(mpic->reg_type, &mpic->tmregs, offset, value);
250ea94187fSScott Wood }
251ea94187fSScott Wood 
25214cf11afSPaul Mackerras static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg)
25314cf11afSPaul Mackerras {
254d6a2639bSMeador Inge 	unsigned int cpu = mpic_processor_id(mpic);
25514cf11afSPaul Mackerras 
256fbf0274eSBenjamin Herrenschmidt 	return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg);
25714cf11afSPaul Mackerras }
25814cf11afSPaul Mackerras 
25914cf11afSPaul Mackerras static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value)
26014cf11afSPaul Mackerras {
261d6a2639bSMeador Inge 	unsigned int cpu = mpic_processor_id(mpic);
26214cf11afSPaul Mackerras 
263fbf0274eSBenjamin Herrenschmidt 	_mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value);
26414cf11afSPaul Mackerras }
26514cf11afSPaul Mackerras 
26614cf11afSPaul Mackerras static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg)
26714cf11afSPaul Mackerras {
26814cf11afSPaul Mackerras 	unsigned int	isu = src_no >> mpic->isu_shift;
26914cf11afSPaul Mackerras 	unsigned int	idx = src_no & mpic->isu_mask;
27011a6b292SMichael Ellerman 	unsigned int	val;
27114cf11afSPaul Mackerras 
27211a6b292SMichael Ellerman 	val = _mpic_read(mpic->reg_type, &mpic->isus[isu],
27311a6b292SMichael Ellerman 			 reg + (idx * MPIC_INFO(IRQ_STRIDE)));
2740d72ba93SOlof Johansson #ifdef CONFIG_MPIC_BROKEN_REGREAD
2750d72ba93SOlof Johansson 	if (reg == 0)
27611a6b292SMichael Ellerman 		val = (val & (MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY)) |
27711a6b292SMichael Ellerman 			mpic->isu_reg0_shadow[src_no];
2780d72ba93SOlof Johansson #endif
27911a6b292SMichael Ellerman 	return val;
28014cf11afSPaul Mackerras }
28114cf11afSPaul Mackerras 
28214cf11afSPaul Mackerras static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
28314cf11afSPaul Mackerras 				   unsigned int reg, u32 value)
28414cf11afSPaul Mackerras {
28514cf11afSPaul Mackerras 	unsigned int	isu = src_no >> mpic->isu_shift;
28614cf11afSPaul Mackerras 	unsigned int	idx = src_no & mpic->isu_mask;
28714cf11afSPaul Mackerras 
288fbf0274eSBenjamin Herrenschmidt 	_mpic_write(mpic->reg_type, &mpic->isus[isu],
2897233593bSZang Roy-r61911 		    reg + (idx * MPIC_INFO(IRQ_STRIDE)), value);
2900d72ba93SOlof Johansson 
2910d72ba93SOlof Johansson #ifdef CONFIG_MPIC_BROKEN_REGREAD
2920d72ba93SOlof Johansson 	if (reg == 0)
29311a6b292SMichael Ellerman 		mpic->isu_reg0_shadow[src_no] =
29411a6b292SMichael Ellerman 			value & ~(MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY);
2950d72ba93SOlof Johansson #endif
29614cf11afSPaul Mackerras }
29714cf11afSPaul Mackerras 
298fbf0274eSBenjamin Herrenschmidt #define mpic_read(b,r)		_mpic_read(mpic->reg_type,&(b),(r))
299fbf0274eSBenjamin Herrenschmidt #define mpic_write(b,r,v)	_mpic_write(mpic->reg_type,&(b),(r),(v))
30014cf11afSPaul Mackerras #define mpic_ipi_read(i)	_mpic_ipi_read(mpic,(i))
30114cf11afSPaul Mackerras #define mpic_ipi_write(i,v)	_mpic_ipi_write(mpic,(i),(v))
302ea94187fSScott Wood #define mpic_tm_read(i)		_mpic_tm_read(mpic,(i))
303ea94187fSScott Wood #define mpic_tm_write(i,v)	_mpic_tm_write(mpic,(i),(v))
30414cf11afSPaul Mackerras #define mpic_cpu_read(i)	_mpic_cpu_read(mpic,(i))
30514cf11afSPaul Mackerras #define mpic_cpu_write(i,v)	_mpic_cpu_write(mpic,(i),(v))
30614cf11afSPaul Mackerras #define mpic_irq_read(s,r)	_mpic_irq_read(mpic,(s),(r))
30714cf11afSPaul Mackerras #define mpic_irq_write(s,r,v)	_mpic_irq_write(mpic,(s),(r),(v))
30814cf11afSPaul Mackerras 
30914cf11afSPaul Mackerras 
31014cf11afSPaul Mackerras /*
31114cf11afSPaul Mackerras  * Low level utility functions
31214cf11afSPaul Mackerras  */
31314cf11afSPaul Mackerras 
31414cf11afSPaul Mackerras 
315c51a3fdcSBecky Bruce static void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr,
316fbf0274eSBenjamin Herrenschmidt 			   struct mpic_reg_bank *rb, unsigned int offset,
317fbf0274eSBenjamin Herrenschmidt 			   unsigned int size)
318fbf0274eSBenjamin Herrenschmidt {
319fbf0274eSBenjamin Herrenschmidt 	rb->base = ioremap(phys_addr + offset, size);
320fbf0274eSBenjamin Herrenschmidt 	BUG_ON(rb->base == NULL);
321fbf0274eSBenjamin Herrenschmidt }
322fbf0274eSBenjamin Herrenschmidt 
323fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR
324c51242e7SKyle Moffett static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb,
325fbf0274eSBenjamin Herrenschmidt 			  unsigned int offset, unsigned int size)
326fbf0274eSBenjamin Herrenschmidt {
327c51242e7SKyle Moffett 	phys_addr_t phys_addr = dcr_resource_start(mpic->node, 0);
328e62b7601SKyle Moffett 	rb->dhost = dcr_map(mpic->node, phys_addr + offset, size);
329fbf0274eSBenjamin Herrenschmidt 	BUG_ON(!DCR_MAP_OK(rb->dhost));
330fbf0274eSBenjamin Herrenschmidt }
331fbf0274eSBenjamin Herrenschmidt 
332c51242e7SKyle Moffett static inline void mpic_map(struct mpic *mpic,
3335a2642f6SBenjamin Herrenschmidt 			    phys_addr_t phys_addr, struct mpic_reg_bank *rb,
3345a2642f6SBenjamin Herrenschmidt 			    unsigned int offset, unsigned int size)
335fbf0274eSBenjamin Herrenschmidt {
336fbf0274eSBenjamin Herrenschmidt 	if (mpic->flags & MPIC_USES_DCR)
337c51242e7SKyle Moffett 		_mpic_map_dcr(mpic, rb, offset, size);
338fbf0274eSBenjamin Herrenschmidt 	else
339fbf0274eSBenjamin Herrenschmidt 		_mpic_map_mmio(mpic, phys_addr, rb, offset, size);
340fbf0274eSBenjamin Herrenschmidt }
341fbf0274eSBenjamin Herrenschmidt #else /* CONFIG_PPC_DCR */
342c51242e7SKyle Moffett #define mpic_map(m,p,b,o,s)	_mpic_map_mmio(m,p,b,o,s)
343fbf0274eSBenjamin Herrenschmidt #endif /* !CONFIG_PPC_DCR */
344fbf0274eSBenjamin Herrenschmidt 
345fbf0274eSBenjamin Herrenschmidt 
34614cf11afSPaul Mackerras 
34714cf11afSPaul Mackerras /* Check if we have one of those nice broken MPICs with a flipped endian on
34814cf11afSPaul Mackerras  * reads from IPI registers
34914cf11afSPaul Mackerras  */
35014cf11afSPaul Mackerras static void __init mpic_test_broken_ipi(struct mpic *mpic)
35114cf11afSPaul Mackerras {
35214cf11afSPaul Mackerras 	u32 r;
35314cf11afSPaul Mackerras 
3547233593bSZang Roy-r61911 	mpic_write(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0), MPIC_VECPRI_MASK);
3557233593bSZang Roy-r61911 	r = mpic_read(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0));
35614cf11afSPaul Mackerras 
35714cf11afSPaul Mackerras 	if (r == le32_to_cpu(MPIC_VECPRI_MASK)) {
35814cf11afSPaul Mackerras 		printk(KERN_INFO "mpic: Detected reversed IPI registers\n");
35914cf11afSPaul Mackerras 		mpic->flags |= MPIC_BROKEN_IPI;
36014cf11afSPaul Mackerras 	}
36114cf11afSPaul Mackerras }
36214cf11afSPaul Mackerras 
3636cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS
36414cf11afSPaul Mackerras 
36514cf11afSPaul Mackerras /* Test if an interrupt is sourced from HyperTransport (used on broken U3s)
36614cf11afSPaul Mackerras  * to force the edge setting on the MPIC and do the ack workaround.
36714cf11afSPaul Mackerras  */
3681beb6a7dSBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source)
36914cf11afSPaul Mackerras {
3701beb6a7dSBenjamin Herrenschmidt 	if (source >= 128 || !mpic->fixups)
37114cf11afSPaul Mackerras 		return 0;
3721beb6a7dSBenjamin Herrenschmidt 	return mpic->fixups[source].base != NULL;
37314cf11afSPaul Mackerras }
37414cf11afSPaul Mackerras 
375c4b22f26SSegher Boessenkool 
3761beb6a7dSBenjamin Herrenschmidt static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source)
37714cf11afSPaul Mackerras {
3781beb6a7dSBenjamin Herrenschmidt 	struct mpic_irq_fixup *fixup = &mpic->fixups[source];
37914cf11afSPaul Mackerras 
3801beb6a7dSBenjamin Herrenschmidt 	if (fixup->applebase) {
3811beb6a7dSBenjamin Herrenschmidt 		unsigned int soff = (fixup->index >> 3) & ~3;
3821beb6a7dSBenjamin Herrenschmidt 		unsigned int mask = 1U << (fixup->index & 0x1f);
3831beb6a7dSBenjamin Herrenschmidt 		writel(mask, fixup->applebase + soff);
3841beb6a7dSBenjamin Herrenschmidt 	} else {
385203041adSThomas Gleixner 		raw_spin_lock(&mpic->fixup_lock);
3861beb6a7dSBenjamin Herrenschmidt 		writeb(0x11 + 2 * fixup->index, fixup->base + 2);
387c4b22f26SSegher Boessenkool 		writel(fixup->data, fixup->base + 4);
388203041adSThomas Gleixner 		raw_spin_unlock(&mpic->fixup_lock);
38914cf11afSPaul Mackerras 	}
3901beb6a7dSBenjamin Herrenschmidt }
39114cf11afSPaul Mackerras 
3921beb6a7dSBenjamin Herrenschmidt static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source,
39324a3f2e8SThomas Gleixner 				      bool level)
3941beb6a7dSBenjamin Herrenschmidt {
3951beb6a7dSBenjamin Herrenschmidt 	struct mpic_irq_fixup *fixup = &mpic->fixups[source];
3961beb6a7dSBenjamin Herrenschmidt 	unsigned long flags;
3971beb6a7dSBenjamin Herrenschmidt 	u32 tmp;
39814cf11afSPaul Mackerras 
3991beb6a7dSBenjamin Herrenschmidt 	if (fixup->base == NULL)
4001beb6a7dSBenjamin Herrenschmidt 		return;
4011beb6a7dSBenjamin Herrenschmidt 
40224a3f2e8SThomas Gleixner 	DBG("startup_ht_interrupt(0x%x) index: %d\n",
40324a3f2e8SThomas Gleixner 	    source, fixup->index);
404203041adSThomas Gleixner 	raw_spin_lock_irqsave(&mpic->fixup_lock, flags);
4051beb6a7dSBenjamin Herrenschmidt 	/* Enable and configure */
4061beb6a7dSBenjamin Herrenschmidt 	writeb(0x10 + 2 * fixup->index, fixup->base + 2);
4071beb6a7dSBenjamin Herrenschmidt 	tmp = readl(fixup->base + 4);
4081beb6a7dSBenjamin Herrenschmidt 	tmp &= ~(0x23U);
40924a3f2e8SThomas Gleixner 	if (level)
4101beb6a7dSBenjamin Herrenschmidt 		tmp |= 0x22;
4111beb6a7dSBenjamin Herrenschmidt 	writel(tmp, fixup->base + 4);
412203041adSThomas Gleixner 	raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags);
4133669e930SJohannes Berg 
4143669e930SJohannes Berg #ifdef CONFIG_PM
4153669e930SJohannes Berg 	/* use the lowest bit inverted to the actual HW,
4163669e930SJohannes Berg 	 * set if this fixup was enabled, clear otherwise */
4173669e930SJohannes Berg 	mpic->save_data[source].fixup_data = tmp | 1;
4183669e930SJohannes Berg #endif
4191beb6a7dSBenjamin Herrenschmidt }
4201beb6a7dSBenjamin Herrenschmidt 
42124a3f2e8SThomas Gleixner static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source)
4221beb6a7dSBenjamin Herrenschmidt {
4231beb6a7dSBenjamin Herrenschmidt 	struct mpic_irq_fixup *fixup = &mpic->fixups[source];
4241beb6a7dSBenjamin Herrenschmidt 	unsigned long flags;
4251beb6a7dSBenjamin Herrenschmidt 	u32 tmp;
4261beb6a7dSBenjamin Herrenschmidt 
4271beb6a7dSBenjamin Herrenschmidt 	if (fixup->base == NULL)
4281beb6a7dSBenjamin Herrenschmidt 		return;
4291beb6a7dSBenjamin Herrenschmidt 
43024a3f2e8SThomas Gleixner 	DBG("shutdown_ht_interrupt(0x%x)\n", source);
4311beb6a7dSBenjamin Herrenschmidt 
4321beb6a7dSBenjamin Herrenschmidt 	/* Disable */
433203041adSThomas Gleixner 	raw_spin_lock_irqsave(&mpic->fixup_lock, flags);
4341beb6a7dSBenjamin Herrenschmidt 	writeb(0x10 + 2 * fixup->index, fixup->base + 2);
4351beb6a7dSBenjamin Herrenschmidt 	tmp = readl(fixup->base + 4);
43672b13819SSegher Boessenkool 	tmp |= 1;
4371beb6a7dSBenjamin Herrenschmidt 	writel(tmp, fixup->base + 4);
438203041adSThomas Gleixner 	raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags);
4393669e930SJohannes Berg 
4403669e930SJohannes Berg #ifdef CONFIG_PM
4413669e930SJohannes Berg 	/* use the lowest bit inverted to the actual HW,
4423669e930SJohannes Berg 	 * set if this fixup was enabled, clear otherwise */
4433669e930SJohannes Berg 	mpic->save_data[source].fixup_data = tmp & ~1;
4443669e930SJohannes Berg #endif
4451beb6a7dSBenjamin Herrenschmidt }
4461beb6a7dSBenjamin Herrenschmidt 
447812fd1fdSMichael Ellerman #ifdef CONFIG_PCI_MSI
448812fd1fdSMichael Ellerman static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase,
449812fd1fdSMichael Ellerman 				    unsigned int devfn)
450812fd1fdSMichael Ellerman {
451812fd1fdSMichael Ellerman 	u8 __iomem *base;
452812fd1fdSMichael Ellerman 	u8 pos, flags;
453812fd1fdSMichael Ellerman 	u64 addr = 0;
454812fd1fdSMichael Ellerman 
455812fd1fdSMichael Ellerman 	for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0;
456812fd1fdSMichael Ellerman 	     pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) {
457812fd1fdSMichael Ellerman 		u8 id = readb(devbase + pos + PCI_CAP_LIST_ID);
458812fd1fdSMichael Ellerman 		if (id == PCI_CAP_ID_HT) {
459812fd1fdSMichael Ellerman 			id = readb(devbase + pos + 3);
460812fd1fdSMichael Ellerman 			if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_MSI_MAPPING)
461812fd1fdSMichael Ellerman 				break;
462812fd1fdSMichael Ellerman 		}
463812fd1fdSMichael Ellerman 	}
464812fd1fdSMichael Ellerman 
465812fd1fdSMichael Ellerman 	if (pos == 0)
466812fd1fdSMichael Ellerman 		return;
467812fd1fdSMichael Ellerman 
468812fd1fdSMichael Ellerman 	base = devbase + pos;
469812fd1fdSMichael Ellerman 
470812fd1fdSMichael Ellerman 	flags = readb(base + HT_MSI_FLAGS);
471812fd1fdSMichael Ellerman 	if (!(flags & HT_MSI_FLAGS_FIXED)) {
472812fd1fdSMichael Ellerman 		addr = readl(base + HT_MSI_ADDR_LO) & HT_MSI_ADDR_LO_MASK;
473812fd1fdSMichael Ellerman 		addr = addr | ((u64)readl(base + HT_MSI_ADDR_HI) << 32);
474812fd1fdSMichael Ellerman 	}
475812fd1fdSMichael Ellerman 
476fe333321SIngo Molnar 	printk(KERN_DEBUG "mpic:   - HT:%02x.%x %s MSI mapping found @ 0x%llx\n",
477812fd1fdSMichael Ellerman 		PCI_SLOT(devfn), PCI_FUNC(devfn),
478812fd1fdSMichael Ellerman 		flags & HT_MSI_FLAGS_ENABLE ? "enabled" : "disabled", addr);
479812fd1fdSMichael Ellerman 
480812fd1fdSMichael Ellerman 	if (!(flags & HT_MSI_FLAGS_ENABLE))
481812fd1fdSMichael Ellerman 		writeb(flags | HT_MSI_FLAGS_ENABLE, base + HT_MSI_FLAGS);
482812fd1fdSMichael Ellerman }
483812fd1fdSMichael Ellerman #else
484812fd1fdSMichael Ellerman static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase,
485812fd1fdSMichael Ellerman 				    unsigned int devfn)
486812fd1fdSMichael Ellerman {
487812fd1fdSMichael Ellerman 	return;
488812fd1fdSMichael Ellerman }
489812fd1fdSMichael Ellerman #endif
490812fd1fdSMichael Ellerman 
4911beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase,
4921beb6a7dSBenjamin Herrenschmidt 				    unsigned int devfn, u32 vdid)
49314cf11afSPaul Mackerras {
494c4b22f26SSegher Boessenkool 	int i, irq, n;
4951beb6a7dSBenjamin Herrenschmidt 	u8 __iomem *base;
49614cf11afSPaul Mackerras 	u32 tmp;
497c4b22f26SSegher Boessenkool 	u8 pos;
49814cf11afSPaul Mackerras 
4991beb6a7dSBenjamin Herrenschmidt 	for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0;
5001beb6a7dSBenjamin Herrenschmidt 	     pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) {
5011beb6a7dSBenjamin Herrenschmidt 		u8 id = readb(devbase + pos + PCI_CAP_LIST_ID);
50246ff3463SBrice Goglin 		if (id == PCI_CAP_ID_HT) {
503c4b22f26SSegher Boessenkool 			id = readb(devbase + pos + 3);
504beb7cc82SMichael Ellerman 			if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_IRQ)
505c4b22f26SSegher Boessenkool 				break;
506c4b22f26SSegher Boessenkool 		}
507c4b22f26SSegher Boessenkool 	}
508c4b22f26SSegher Boessenkool 	if (pos == 0)
509c4b22f26SSegher Boessenkool 		return;
510c4b22f26SSegher Boessenkool 
5111beb6a7dSBenjamin Herrenschmidt 	base = devbase + pos;
5121beb6a7dSBenjamin Herrenschmidt 	writeb(0x01, base + 2);
5131beb6a7dSBenjamin Herrenschmidt 	n = (readl(base + 4) >> 16) & 0xff;
514c4b22f26SSegher Boessenkool 
5151beb6a7dSBenjamin Herrenschmidt 	printk(KERN_INFO "mpic:   - HT:%02x.%x [0x%02x] vendor %04x device %04x"
5161beb6a7dSBenjamin Herrenschmidt 	       " has %d irqs\n",
5171beb6a7dSBenjamin Herrenschmidt 	       devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1);
518c4b22f26SSegher Boessenkool 
519c4b22f26SSegher Boessenkool 	for (i = 0; i <= n; i++) {
5201beb6a7dSBenjamin Herrenschmidt 		writeb(0x10 + 2 * i, base + 2);
5211beb6a7dSBenjamin Herrenschmidt 		tmp = readl(base + 4);
52214cf11afSPaul Mackerras 		irq = (tmp >> 16) & 0xff;
5231beb6a7dSBenjamin Herrenschmidt 		DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp);
5241beb6a7dSBenjamin Herrenschmidt 		/* mask it , will be unmasked later */
5251beb6a7dSBenjamin Herrenschmidt 		tmp |= 0x1;
5261beb6a7dSBenjamin Herrenschmidt 		writel(tmp, base + 4);
5271beb6a7dSBenjamin Herrenschmidt 		mpic->fixups[irq].index = i;
5281beb6a7dSBenjamin Herrenschmidt 		mpic->fixups[irq].base = base;
5291beb6a7dSBenjamin Herrenschmidt 		/* Apple HT PIC has a non-standard way of doing EOIs */
5301beb6a7dSBenjamin Herrenschmidt 		if ((vdid & 0xffff) == 0x106b)
5311beb6a7dSBenjamin Herrenschmidt 			mpic->fixups[irq].applebase = devbase + 0x60;
5321beb6a7dSBenjamin Herrenschmidt 		else
5331beb6a7dSBenjamin Herrenschmidt 			mpic->fixups[irq].applebase = NULL;
5341beb6a7dSBenjamin Herrenschmidt 		writeb(0x11 + 2 * i, base + 2);
5351beb6a7dSBenjamin Herrenschmidt 		mpic->fixups[irq].data = readl(base + 4) | 0x80000000;
53614cf11afSPaul Mackerras 	}
53714cf11afSPaul Mackerras }
53814cf11afSPaul Mackerras 
53914cf11afSPaul Mackerras 
5401beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic)
54114cf11afSPaul Mackerras {
54214cf11afSPaul Mackerras 	unsigned int devfn;
54314cf11afSPaul Mackerras 	u8 __iomem *cfgspace;
54414cf11afSPaul Mackerras 
5451beb6a7dSBenjamin Herrenschmidt 	printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n");
54614cf11afSPaul Mackerras 
54714cf11afSPaul Mackerras 	/* Allocate fixups array */
548ea96025aSAnton Vorontsov 	mpic->fixups = kzalloc(128 * sizeof(*mpic->fixups), GFP_KERNEL);
54914cf11afSPaul Mackerras 	BUG_ON(mpic->fixups == NULL);
55014cf11afSPaul Mackerras 
55114cf11afSPaul Mackerras 	/* Init spinlock */
552203041adSThomas Gleixner 	raw_spin_lock_init(&mpic->fixup_lock);
55314cf11afSPaul Mackerras 
554c4b22f26SSegher Boessenkool 	/* Map U3 config space. We assume all IO-APICs are on the primary bus
555c4b22f26SSegher Boessenkool 	 * so we only need to map 64kB.
55614cf11afSPaul Mackerras 	 */
557c4b22f26SSegher Boessenkool 	cfgspace = ioremap(0xf2000000, 0x10000);
55814cf11afSPaul Mackerras 	BUG_ON(cfgspace == NULL);
55914cf11afSPaul Mackerras 
5601beb6a7dSBenjamin Herrenschmidt 	/* Now we scan all slots. We do a very quick scan, we read the header
5611beb6a7dSBenjamin Herrenschmidt 	 * type, vendor ID and device ID only, that's plenty enough
56214cf11afSPaul Mackerras 	 */
563c4b22f26SSegher Boessenkool 	for (devfn = 0; devfn < 0x100; devfn++) {
56414cf11afSPaul Mackerras 		u8 __iomem *devbase = cfgspace + (devfn << 8);
56514cf11afSPaul Mackerras 		u8 hdr_type = readb(devbase + PCI_HEADER_TYPE);
56614cf11afSPaul Mackerras 		u32 l = readl(devbase + PCI_VENDOR_ID);
5671beb6a7dSBenjamin Herrenschmidt 		u16 s;
56814cf11afSPaul Mackerras 
56914cf11afSPaul Mackerras 		DBG("devfn %x, l: %x\n", devfn, l);
57014cf11afSPaul Mackerras 
57114cf11afSPaul Mackerras 		/* If no device, skip */
57214cf11afSPaul Mackerras 		if (l == 0xffffffff || l == 0x00000000 ||
57314cf11afSPaul Mackerras 		    l == 0x0000ffff || l == 0xffff0000)
57414cf11afSPaul Mackerras 			goto next;
5751beb6a7dSBenjamin Herrenschmidt 		/* Check if is supports capability lists */
5761beb6a7dSBenjamin Herrenschmidt 		s = readw(devbase + PCI_STATUS);
5771beb6a7dSBenjamin Herrenschmidt 		if (!(s & PCI_STATUS_CAP_LIST))
5781beb6a7dSBenjamin Herrenschmidt 			goto next;
57914cf11afSPaul Mackerras 
5801beb6a7dSBenjamin Herrenschmidt 		mpic_scan_ht_pic(mpic, devbase, devfn, l);
581812fd1fdSMichael Ellerman 		mpic_scan_ht_msi(mpic, devbase, devfn);
58214cf11afSPaul Mackerras 
58314cf11afSPaul Mackerras 	next:
58414cf11afSPaul Mackerras 		/* next device, if function 0 */
585c4b22f26SSegher Boessenkool 		if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0)
58614cf11afSPaul Mackerras 			devfn += 7;
58714cf11afSPaul Mackerras 	}
58814cf11afSPaul Mackerras }
58914cf11afSPaul Mackerras 
5906cfef5b2SMichael Ellerman #else /* CONFIG_MPIC_U3_HT_IRQS */
5916e99e458SBenjamin Herrenschmidt 
5926e99e458SBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source)
5936e99e458SBenjamin Herrenschmidt {
5946e99e458SBenjamin Herrenschmidt 	return 0;
5956e99e458SBenjamin Herrenschmidt }
5966e99e458SBenjamin Herrenschmidt 
5976e99e458SBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic)
5986e99e458SBenjamin Herrenschmidt {
5996e99e458SBenjamin Herrenschmidt }
6006e99e458SBenjamin Herrenschmidt 
6016cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */
60214cf11afSPaul Mackerras 
60314cf11afSPaul Mackerras /* Find an mpic associated with a given linux interrupt */
604d69a78d7STony Breeds static struct mpic *mpic_find(unsigned int irq)
60514cf11afSPaul Mackerras {
6060ebfff14SBenjamin Herrenschmidt 	if (irq < NUM_ISA_INTERRUPTS)
60714cf11afSPaul Mackerras 		return NULL;
6080ebfff14SBenjamin Herrenschmidt 
609ec775d0eSThomas Gleixner 	return irq_get_chip_data(irq);
61014cf11afSPaul Mackerras }
61114cf11afSPaul Mackerras 
612d69a78d7STony Breeds /* Determine if the linux irq is an IPI */
6133a2b4f7cSBenjamin Herrenschmidt static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int src)
614d69a78d7STony Breeds {
615d69a78d7STony Breeds 	return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]);
616d69a78d7STony Breeds }
617d69a78d7STony Breeds 
618ea94187fSScott Wood /* Determine if the linux irq is a timer */
6193a2b4f7cSBenjamin Herrenschmidt static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int src)
620ea94187fSScott Wood {
621ea94187fSScott Wood 	return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]);
622ea94187fSScott Wood }
623d69a78d7STony Breeds 
62414cf11afSPaul Mackerras /* Convert a cpu mask from logical to physical cpu numbers. */
62514cf11afSPaul Mackerras static inline u32 mpic_physmask(u32 cpumask)
62614cf11afSPaul Mackerras {
62714cf11afSPaul Mackerras 	int i;
62814cf11afSPaul Mackerras 	u32 mask = 0;
62914cf11afSPaul Mackerras 
630ebc04215SMilton Miller 	for (i = 0; i < min(32, NR_CPUS); ++i, cpumask >>= 1)
63114cf11afSPaul Mackerras 		mask |= (cpumask & 1) << get_hard_smp_processor_id(i);
63214cf11afSPaul Mackerras 	return mask;
63314cf11afSPaul Mackerras }
63414cf11afSPaul Mackerras 
63514cf11afSPaul Mackerras #ifdef CONFIG_SMP
63614cf11afSPaul Mackerras /* Get the mpic structure from the IPI number */
637835c0553SLennert Buytenhek static inline struct mpic * mpic_from_ipi(struct irq_data *d)
63814cf11afSPaul Mackerras {
639835c0553SLennert Buytenhek 	return irq_data_get_irq_chip_data(d);
64014cf11afSPaul Mackerras }
64114cf11afSPaul Mackerras #endif
64214cf11afSPaul Mackerras 
64314cf11afSPaul Mackerras /* Get the mpic structure from the irq number */
64414cf11afSPaul Mackerras static inline struct mpic * mpic_from_irq(unsigned int irq)
64514cf11afSPaul Mackerras {
646ec775d0eSThomas Gleixner 	return irq_get_chip_data(irq);
647835c0553SLennert Buytenhek }
648835c0553SLennert Buytenhek 
649835c0553SLennert Buytenhek /* Get the mpic structure from the irq data */
650835c0553SLennert Buytenhek static inline struct mpic * mpic_from_irq_data(struct irq_data *d)
651835c0553SLennert Buytenhek {
652835c0553SLennert Buytenhek 	return irq_data_get_irq_chip_data(d);
65314cf11afSPaul Mackerras }
65414cf11afSPaul Mackerras 
65514cf11afSPaul Mackerras /* Send an EOI */
65614cf11afSPaul Mackerras static inline void mpic_eoi(struct mpic *mpic)
65714cf11afSPaul Mackerras {
6587233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_EOI), 0);
6597233593bSZang Roy-r61911 	(void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI));
66014cf11afSPaul Mackerras }
66114cf11afSPaul Mackerras 
66214cf11afSPaul Mackerras /*
66314cf11afSPaul Mackerras  * Linux descriptor level callbacks
66414cf11afSPaul Mackerras  */
66514cf11afSPaul Mackerras 
66614cf11afSPaul Mackerras 
667835c0553SLennert Buytenhek void mpic_unmask_irq(struct irq_data *d)
66814cf11afSPaul Mackerras {
66914cf11afSPaul Mackerras 	unsigned int loops = 100000;
670835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
671476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
67214cf11afSPaul Mackerras 
673835c0553SLennert Buytenhek 	DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, d->irq, src);
67414cf11afSPaul Mackerras 
6757233593bSZang Roy-r61911 	mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
6767233593bSZang Roy-r61911 		       mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) &
677e5356640SBenjamin Herrenschmidt 		       ~MPIC_VECPRI_MASK);
67814cf11afSPaul Mackerras 	/* make sure mask gets to controller before we return to user */
67914cf11afSPaul Mackerras 	do {
68014cf11afSPaul Mackerras 		if (!loops--) {
6818bfc5e36SScott Wood 			printk(KERN_ERR "%s: timeout on hwirq %u\n",
6828bfc5e36SScott Wood 			       __func__, src);
68314cf11afSPaul Mackerras 			break;
68414cf11afSPaul Mackerras 		}
6857233593bSZang Roy-r61911 	} while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK);
6861beb6a7dSBenjamin Herrenschmidt }
6871beb6a7dSBenjamin Herrenschmidt 
688835c0553SLennert Buytenhek void mpic_mask_irq(struct irq_data *d)
68914cf11afSPaul Mackerras {
69014cf11afSPaul Mackerras 	unsigned int loops = 100000;
691835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
692476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
69314cf11afSPaul Mackerras 
694835c0553SLennert Buytenhek 	DBG("%s: disable_irq: %d (src %d)\n", mpic->name, d->irq, src);
69514cf11afSPaul Mackerras 
6967233593bSZang Roy-r61911 	mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
6977233593bSZang Roy-r61911 		       mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) |
698e5356640SBenjamin Herrenschmidt 		       MPIC_VECPRI_MASK);
69914cf11afSPaul Mackerras 
70014cf11afSPaul Mackerras 	/* make sure mask gets to controller before we return to user */
70114cf11afSPaul Mackerras 	do {
70214cf11afSPaul Mackerras 		if (!loops--) {
7038bfc5e36SScott Wood 			printk(KERN_ERR "%s: timeout on hwirq %u\n",
7048bfc5e36SScott Wood 			       __func__, src);
70514cf11afSPaul Mackerras 			break;
70614cf11afSPaul Mackerras 		}
7077233593bSZang Roy-r61911 	} while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK));
70814cf11afSPaul Mackerras }
70914cf11afSPaul Mackerras 
710835c0553SLennert Buytenhek void mpic_end_irq(struct irq_data *d)
71114cf11afSPaul Mackerras {
712835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
71314cf11afSPaul Mackerras 
7141beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_IRQ
715835c0553SLennert Buytenhek 	DBG("%s: end_irq: %d\n", mpic->name, d->irq);
7161beb6a7dSBenjamin Herrenschmidt #endif
71714cf11afSPaul Mackerras 	/* We always EOI on end_irq() even for edge interrupts since that
71814cf11afSPaul Mackerras 	 * should only lower the priority, the MPIC should have properly
71914cf11afSPaul Mackerras 	 * latched another edge interrupt coming in anyway
72014cf11afSPaul Mackerras 	 */
72114cf11afSPaul Mackerras 
72214cf11afSPaul Mackerras 	mpic_eoi(mpic);
72314cf11afSPaul Mackerras }
72414cf11afSPaul Mackerras 
7256cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS
726b9e5b4e6SBenjamin Herrenschmidt 
727835c0553SLennert Buytenhek static void mpic_unmask_ht_irq(struct irq_data *d)
728b9e5b4e6SBenjamin Herrenschmidt {
729835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
730476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
731b9e5b4e6SBenjamin Herrenschmidt 
732835c0553SLennert Buytenhek 	mpic_unmask_irq(d);
733b9e5b4e6SBenjamin Herrenschmidt 
73424a3f2e8SThomas Gleixner 	if (irqd_is_level_type(d))
735b9e5b4e6SBenjamin Herrenschmidt 		mpic_ht_end_irq(mpic, src);
736b9e5b4e6SBenjamin Herrenschmidt }
737b9e5b4e6SBenjamin Herrenschmidt 
738835c0553SLennert Buytenhek static unsigned int mpic_startup_ht_irq(struct irq_data *d)
739b9e5b4e6SBenjamin Herrenschmidt {
740835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
741476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
742b9e5b4e6SBenjamin Herrenschmidt 
743835c0553SLennert Buytenhek 	mpic_unmask_irq(d);
74424a3f2e8SThomas Gleixner 	mpic_startup_ht_interrupt(mpic, src, irqd_is_level_type(d));
745b9e5b4e6SBenjamin Herrenschmidt 
746b9e5b4e6SBenjamin Herrenschmidt 	return 0;
747b9e5b4e6SBenjamin Herrenschmidt }
748b9e5b4e6SBenjamin Herrenschmidt 
749835c0553SLennert Buytenhek static void mpic_shutdown_ht_irq(struct irq_data *d)
750b9e5b4e6SBenjamin Herrenschmidt {
751835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
752476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
753b9e5b4e6SBenjamin Herrenschmidt 
75424a3f2e8SThomas Gleixner 	mpic_shutdown_ht_interrupt(mpic, src);
755835c0553SLennert Buytenhek 	mpic_mask_irq(d);
756b9e5b4e6SBenjamin Herrenschmidt }
757b9e5b4e6SBenjamin Herrenschmidt 
758835c0553SLennert Buytenhek static void mpic_end_ht_irq(struct irq_data *d)
759b9e5b4e6SBenjamin Herrenschmidt {
760835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
761476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
762b9e5b4e6SBenjamin Herrenschmidt 
763b9e5b4e6SBenjamin Herrenschmidt #ifdef DEBUG_IRQ
764835c0553SLennert Buytenhek 	DBG("%s: end_irq: %d\n", mpic->name, d->irq);
765b9e5b4e6SBenjamin Herrenschmidt #endif
766b9e5b4e6SBenjamin Herrenschmidt 	/* We always EOI on end_irq() even for edge interrupts since that
767b9e5b4e6SBenjamin Herrenschmidt 	 * should only lower the priority, the MPIC should have properly
768b9e5b4e6SBenjamin Herrenschmidt 	 * latched another edge interrupt coming in anyway
769b9e5b4e6SBenjamin Herrenschmidt 	 */
770b9e5b4e6SBenjamin Herrenschmidt 
77124a3f2e8SThomas Gleixner 	if (irqd_is_level_type(d))
772b9e5b4e6SBenjamin Herrenschmidt 		mpic_ht_end_irq(mpic, src);
773b9e5b4e6SBenjamin Herrenschmidt 	mpic_eoi(mpic);
774b9e5b4e6SBenjamin Herrenschmidt }
7756cfef5b2SMichael Ellerman #endif /* !CONFIG_MPIC_U3_HT_IRQS */
776b9e5b4e6SBenjamin Herrenschmidt 
77714cf11afSPaul Mackerras #ifdef CONFIG_SMP
77814cf11afSPaul Mackerras 
779835c0553SLennert Buytenhek static void mpic_unmask_ipi(struct irq_data *d)
78014cf11afSPaul Mackerras {
781835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_ipi(d);
782476eb491SGrant Likely 	unsigned int src = virq_to_hw(d->irq) - mpic->ipi_vecs[0];
78314cf11afSPaul Mackerras 
784835c0553SLennert Buytenhek 	DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, d->irq, src);
78514cf11afSPaul Mackerras 	mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);
78614cf11afSPaul Mackerras }
78714cf11afSPaul Mackerras 
788835c0553SLennert Buytenhek static void mpic_mask_ipi(struct irq_data *d)
78914cf11afSPaul Mackerras {
79014cf11afSPaul Mackerras 	/* NEVER disable an IPI... that's just plain wrong! */
79114cf11afSPaul Mackerras }
79214cf11afSPaul Mackerras 
793835c0553SLennert Buytenhek static void mpic_end_ipi(struct irq_data *d)
79414cf11afSPaul Mackerras {
795835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_ipi(d);
79614cf11afSPaul Mackerras 
79714cf11afSPaul Mackerras 	/*
79814cf11afSPaul Mackerras 	 * IPIs are marked IRQ_PER_CPU. This has the side effect of
79914cf11afSPaul Mackerras 	 * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from
80014cf11afSPaul Mackerras 	 * applying to them. We EOI them late to avoid re-entering.
80114cf11afSPaul Mackerras 	 */
80214cf11afSPaul Mackerras 	mpic_eoi(mpic);
80314cf11afSPaul Mackerras }
80414cf11afSPaul Mackerras 
80514cf11afSPaul Mackerras #endif /* CONFIG_SMP */
80614cf11afSPaul Mackerras 
807ea94187fSScott Wood static void mpic_unmask_tm(struct irq_data *d)
808ea94187fSScott Wood {
809ea94187fSScott Wood 	struct mpic *mpic = mpic_from_irq_data(d);
810ea94187fSScott Wood 	unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0];
811ea94187fSScott Wood 
81277ef4899SDmitry Eremin-Solenikov 	DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, d->irq, src);
813ea94187fSScott Wood 	mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK);
814ea94187fSScott Wood 	mpic_tm_read(src);
815ea94187fSScott Wood }
816ea94187fSScott Wood 
817ea94187fSScott Wood static void mpic_mask_tm(struct irq_data *d)
818ea94187fSScott Wood {
819ea94187fSScott Wood 	struct mpic *mpic = mpic_from_irq_data(d);
820ea94187fSScott Wood 	unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0];
821ea94187fSScott Wood 
822ea94187fSScott Wood 	mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK);
823ea94187fSScott Wood 	mpic_tm_read(src);
824ea94187fSScott Wood }
825ea94187fSScott Wood 
826835c0553SLennert Buytenhek int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
827835c0553SLennert Buytenhek 		      bool force)
82814cf11afSPaul Mackerras {
829835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
830476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
83114cf11afSPaul Mackerras 
8323c10c9c4SKumar Gala 	if (mpic->flags & MPIC_SINGLE_DEST_CPU) {
83338e1313fSYang Li 		int cpuid = irq_choose_cpu(cpumask);
8343c10c9c4SKumar Gala 
8353c10c9c4SKumar Gala 		mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid);
8363c10c9c4SKumar Gala 	} else {
8372a116f3dSMilton Miller 		u32 mask = cpumask_bits(cpumask)[0];
83814cf11afSPaul Mackerras 
8392a116f3dSMilton Miller 		mask &= cpumask_bits(cpu_online_mask)[0];
84014cf11afSPaul Mackerras 
8417233593bSZang Roy-r61911 		mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION),
8422a116f3dSMilton Miller 			       mpic_physmask(mask));
84314cf11afSPaul Mackerras 	}
844d5dedd45SYinghai Lu 
845dcb615aeSAlexander Gordeev 	return IRQ_SET_MASK_OK;
8463c10c9c4SKumar Gala }
84714cf11afSPaul Mackerras 
8487233593bSZang Roy-r61911 static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type)
8490ebfff14SBenjamin Herrenschmidt {
8500ebfff14SBenjamin Herrenschmidt 	/* Now convert sense value */
8516e99e458SBenjamin Herrenschmidt 	switch(type & IRQ_TYPE_SENSE_MASK) {
8520ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_EDGE_RISING:
8537233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_EDGE) |
8547233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_POSITIVE);
8550ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_EDGE_FALLING:
8566e99e458SBenjamin Herrenschmidt 	case IRQ_TYPE_EDGE_BOTH:
8577233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_EDGE) |
8587233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_NEGATIVE);
8590ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_LEVEL_HIGH:
8607233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_LEVEL) |
8617233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_POSITIVE);
8620ebfff14SBenjamin Herrenschmidt 	case IRQ_TYPE_LEVEL_LOW:
8630ebfff14SBenjamin Herrenschmidt 	default:
8647233593bSZang Roy-r61911 		return MPIC_INFO(VECPRI_SENSE_LEVEL) |
8657233593bSZang Roy-r61911 		       MPIC_INFO(VECPRI_POLARITY_NEGATIVE);
8660ebfff14SBenjamin Herrenschmidt 	}
8676e99e458SBenjamin Herrenschmidt }
8686e99e458SBenjamin Herrenschmidt 
869835c0553SLennert Buytenhek int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type)
8706e99e458SBenjamin Herrenschmidt {
871835c0553SLennert Buytenhek 	struct mpic *mpic = mpic_from_irq_data(d);
872476eb491SGrant Likely 	unsigned int src = irqd_to_hwirq(d);
8736e99e458SBenjamin Herrenschmidt 	unsigned int vecpri, vold, vnew;
8746e99e458SBenjamin Herrenschmidt 
87506fe98e6SBenjamin Herrenschmidt 	DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n",
876835c0553SLennert Buytenhek 	    mpic, d->irq, src, flow_type);
8776e99e458SBenjamin Herrenschmidt 
8785019609fSKyle Moffett 	if (src >= mpic->num_sources)
8796e99e458SBenjamin Herrenschmidt 		return -EINVAL;
8806e99e458SBenjamin Herrenschmidt 
881446f6d06SBenjamin Herrenschmidt 	vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
8826e99e458SBenjamin Herrenschmidt 
883446f6d06SBenjamin Herrenschmidt 	/* We don't support "none" type */
884446f6d06SBenjamin Herrenschmidt 	if (flow_type == IRQ_TYPE_NONE)
885446f6d06SBenjamin Herrenschmidt 		flow_type = IRQ_TYPE_DEFAULT;
886446f6d06SBenjamin Herrenschmidt 
887446f6d06SBenjamin Herrenschmidt 	/* Default: read HW settings */
888446f6d06SBenjamin Herrenschmidt 	if (flow_type == IRQ_TYPE_DEFAULT) {
8890215b4aaSPaul Gortmaker 		int vold_ps;
8900215b4aaSPaul Gortmaker 
8910215b4aaSPaul Gortmaker 		vold_ps = vold & (MPIC_INFO(VECPRI_POLARITY_MASK) |
8920215b4aaSPaul Gortmaker 				  MPIC_INFO(VECPRI_SENSE_MASK));
8930215b4aaSPaul Gortmaker 
8940215b4aaSPaul Gortmaker 		if (vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) |
8950215b4aaSPaul Gortmaker 				MPIC_INFO(VECPRI_POLARITY_POSITIVE)))
896446f6d06SBenjamin Herrenschmidt 			flow_type = IRQ_TYPE_EDGE_RISING;
8970215b4aaSPaul Gortmaker 		else if	(vold_ps == (MPIC_INFO(VECPRI_SENSE_EDGE) |
8980215b4aaSPaul Gortmaker 				     MPIC_INFO(VECPRI_POLARITY_NEGATIVE)))
899446f6d06SBenjamin Herrenschmidt 			flow_type = IRQ_TYPE_EDGE_FALLING;
9000215b4aaSPaul Gortmaker 		else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) |
9010215b4aaSPaul Gortmaker 				     MPIC_INFO(VECPRI_POLARITY_POSITIVE)))
902446f6d06SBenjamin Herrenschmidt 			flow_type = IRQ_TYPE_LEVEL_HIGH;
9030215b4aaSPaul Gortmaker 		else if (vold_ps == (MPIC_INFO(VECPRI_SENSE_LEVEL) |
9040215b4aaSPaul Gortmaker 				     MPIC_INFO(VECPRI_POLARITY_NEGATIVE)))
905446f6d06SBenjamin Herrenschmidt 			flow_type = IRQ_TYPE_LEVEL_LOW;
9060215b4aaSPaul Gortmaker 		else
9070215b4aaSPaul Gortmaker 			WARN_ONCE(1, "mpic: unknown IRQ type %d\n", vold);
908446f6d06SBenjamin Herrenschmidt 	}
909446f6d06SBenjamin Herrenschmidt 
910446f6d06SBenjamin Herrenschmidt 	/* Apply to irq desc */
91124a3f2e8SThomas Gleixner 	irqd_set_trigger_type(d, flow_type);
9126e99e458SBenjamin Herrenschmidt 
913446f6d06SBenjamin Herrenschmidt 	/* Apply to HW */
9146e99e458SBenjamin Herrenschmidt 	if (mpic_is_ht_interrupt(mpic, src))
9156e99e458SBenjamin Herrenschmidt 		vecpri = MPIC_VECPRI_POLARITY_POSITIVE |
9166e99e458SBenjamin Herrenschmidt 			MPIC_VECPRI_SENSE_EDGE;
9176e99e458SBenjamin Herrenschmidt 	else
9187233593bSZang Roy-r61911 		vecpri = mpic_type_to_vecpri(mpic, flow_type);
9196e99e458SBenjamin Herrenschmidt 
9207233593bSZang Roy-r61911 	vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) |
9217233593bSZang Roy-r61911 			MPIC_INFO(VECPRI_SENSE_MASK));
9226e99e458SBenjamin Herrenschmidt 	vnew |= vecpri;
9236e99e458SBenjamin Herrenschmidt 	if (vold != vnew)
9247233593bSZang Roy-r61911 		mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew);
9256e99e458SBenjamin Herrenschmidt 
926e075cd70SJustin P. Mattock 	return IRQ_SET_MASK_OK_NOCOPY;
9270ebfff14SBenjamin Herrenschmidt }
9280ebfff14SBenjamin Herrenschmidt 
9295ff04b72SDongsheng.wang@freescale.com static int mpic_irq_set_wake(struct irq_data *d, unsigned int on)
9305ff04b72SDongsheng.wang@freescale.com {
9315ff04b72SDongsheng.wang@freescale.com 	struct irq_desc *desc = container_of(d, struct irq_desc, irq_data);
9325ff04b72SDongsheng.wang@freescale.com 	struct mpic *mpic = mpic_from_irq_data(d);
9335ff04b72SDongsheng.wang@freescale.com 
9345ff04b72SDongsheng.wang@freescale.com 	if (!(mpic->flags & MPIC_FSL))
9355ff04b72SDongsheng.wang@freescale.com 		return -ENXIO;
9365ff04b72SDongsheng.wang@freescale.com 
9375ff04b72SDongsheng.wang@freescale.com 	if (on)
9385ff04b72SDongsheng.wang@freescale.com 		desc->action->flags |= IRQF_NO_SUSPEND;
9395ff04b72SDongsheng.wang@freescale.com 	else
9405ff04b72SDongsheng.wang@freescale.com 		desc->action->flags &= ~IRQF_NO_SUSPEND;
9415ff04b72SDongsheng.wang@freescale.com 
9425ff04b72SDongsheng.wang@freescale.com 	return 0;
9435ff04b72SDongsheng.wang@freescale.com }
9445ff04b72SDongsheng.wang@freescale.com 
94538958dd9SOlof Johansson void mpic_set_vector(unsigned int virq, unsigned int vector)
94638958dd9SOlof Johansson {
94738958dd9SOlof Johansson 	struct mpic *mpic = mpic_from_irq(virq);
948476eb491SGrant Likely 	unsigned int src = virq_to_hw(virq);
94938958dd9SOlof Johansson 	unsigned int vecpri;
95038958dd9SOlof Johansson 
95138958dd9SOlof Johansson 	DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n",
95238958dd9SOlof Johansson 	    mpic, virq, src, vector);
95338958dd9SOlof Johansson 
9545019609fSKyle Moffett 	if (src >= mpic->num_sources)
95538958dd9SOlof Johansson 		return;
95638958dd9SOlof Johansson 
95738958dd9SOlof Johansson 	vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
95838958dd9SOlof Johansson 	vecpri = vecpri & ~MPIC_INFO(VECPRI_VECTOR_MASK);
95938958dd9SOlof Johansson 	vecpri |= vector;
96038958dd9SOlof Johansson 	mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
96138958dd9SOlof Johansson }
96238958dd9SOlof Johansson 
963*e51df2c1SAnton Blanchard static void mpic_set_destination(unsigned int virq, unsigned int cpuid)
964dfec2202SMeador Inge {
965dfec2202SMeador Inge 	struct mpic *mpic = mpic_from_irq(virq);
966476eb491SGrant Likely 	unsigned int src = virq_to_hw(virq);
967dfec2202SMeador Inge 
968dfec2202SMeador Inge 	DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n",
969dfec2202SMeador Inge 	    mpic, virq, src, cpuid);
970dfec2202SMeador Inge 
9715019609fSKyle Moffett 	if (src >= mpic->num_sources)
972dfec2202SMeador Inge 		return;
973dfec2202SMeador Inge 
974dfec2202SMeador Inge 	mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid);
975dfec2202SMeador Inge }
976dfec2202SMeador Inge 
977b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_chip = {
978835c0553SLennert Buytenhek 	.irq_mask	= mpic_mask_irq,
979835c0553SLennert Buytenhek 	.irq_unmask	= mpic_unmask_irq,
980835c0553SLennert Buytenhek 	.irq_eoi	= mpic_end_irq,
981835c0553SLennert Buytenhek 	.irq_set_type	= mpic_set_irq_type,
9825ff04b72SDongsheng.wang@freescale.com 	.irq_set_wake	= mpic_irq_set_wake,
983b9e5b4e6SBenjamin Herrenschmidt };
984b9e5b4e6SBenjamin Herrenschmidt 
985b9e5b4e6SBenjamin Herrenschmidt #ifdef CONFIG_SMP
986b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_ipi_chip = {
987835c0553SLennert Buytenhek 	.irq_mask	= mpic_mask_ipi,
988835c0553SLennert Buytenhek 	.irq_unmask	= mpic_unmask_ipi,
989835c0553SLennert Buytenhek 	.irq_eoi	= mpic_end_ipi,
990b9e5b4e6SBenjamin Herrenschmidt };
991b9e5b4e6SBenjamin Herrenschmidt #endif /* CONFIG_SMP */
992b9e5b4e6SBenjamin Herrenschmidt 
993ea94187fSScott Wood static struct irq_chip mpic_tm_chip = {
994ea94187fSScott Wood 	.irq_mask	= mpic_mask_tm,
995ea94187fSScott Wood 	.irq_unmask	= mpic_unmask_tm,
996ea94187fSScott Wood 	.irq_eoi	= mpic_end_irq,
9975ff04b72SDongsheng.wang@freescale.com 	.irq_set_wake	= mpic_irq_set_wake,
998ea94187fSScott Wood };
999ea94187fSScott Wood 
10006cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS
1001b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_ht_chip = {
1002835c0553SLennert Buytenhek 	.irq_startup	= mpic_startup_ht_irq,
1003835c0553SLennert Buytenhek 	.irq_shutdown	= mpic_shutdown_ht_irq,
1004835c0553SLennert Buytenhek 	.irq_mask	= mpic_mask_irq,
1005835c0553SLennert Buytenhek 	.irq_unmask	= mpic_unmask_ht_irq,
1006835c0553SLennert Buytenhek 	.irq_eoi	= mpic_end_ht_irq,
1007835c0553SLennert Buytenhek 	.irq_set_type	= mpic_set_irq_type,
1008b9e5b4e6SBenjamin Herrenschmidt };
10096cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */
1010b9e5b4e6SBenjamin Herrenschmidt 
101114cf11afSPaul Mackerras 
1012bae1d8f1SGrant Likely static int mpic_host_match(struct irq_domain *h, struct device_node *node)
10130ebfff14SBenjamin Herrenschmidt {
10140ebfff14SBenjamin Herrenschmidt 	/* Exact match, unless mpic node is NULL */
101552964f87SMichael Ellerman 	return h->of_node == NULL || h->of_node == node;
10160ebfff14SBenjamin Herrenschmidt }
10170ebfff14SBenjamin Herrenschmidt 
1018bae1d8f1SGrant Likely static int mpic_host_map(struct irq_domain *h, unsigned int virq,
10196e99e458SBenjamin Herrenschmidt 			 irq_hw_number_t hw)
10200ebfff14SBenjamin Herrenschmidt {
10210ebfff14SBenjamin Herrenschmidt 	struct mpic *mpic = h->host_data;
10226e99e458SBenjamin Herrenschmidt 	struct irq_chip *chip;
10230ebfff14SBenjamin Herrenschmidt 
102406fe98e6SBenjamin Herrenschmidt 	DBG("mpic: map virq %d, hwirq 0x%lx\n", virq, hw);
10250ebfff14SBenjamin Herrenschmidt 
10267df2457dSOlof Johansson 	if (hw == mpic->spurious_vec)
10270ebfff14SBenjamin Herrenschmidt 		return -EINVAL;
10285fe0c1f2SBenjamin Herrenschmidt 	if (mpic->protected && test_bit(hw, mpic->protected)) {
10295fe0c1f2SBenjamin Herrenschmidt 		pr_warning("mpic: Mapping of source 0x%x failed, "
10305fe0c1f2SBenjamin Herrenschmidt 			   "source protected by firmware !\n",\
10315fe0c1f2SBenjamin Herrenschmidt 			   (unsigned int)hw);
10325fe0c1f2SBenjamin Herrenschmidt 		return -EPERM;
10335fe0c1f2SBenjamin Herrenschmidt 	}
103406fe98e6SBenjamin Herrenschmidt 
10350ebfff14SBenjamin Herrenschmidt #ifdef CONFIG_SMP
10367df2457dSOlof Johansson 	else if (hw >= mpic->ipi_vecs[0]) {
1037be8bec56SKyle Moffett 		WARN_ON(mpic->flags & MPIC_SECONDARY);
10380ebfff14SBenjamin Herrenschmidt 
103906fe98e6SBenjamin Herrenschmidt 		DBG("mpic: mapping as IPI\n");
1040ec775d0eSThomas Gleixner 		irq_set_chip_data(virq, mpic);
1041ec775d0eSThomas Gleixner 		irq_set_chip_and_handler(virq, &mpic->hc_ipi,
10420ebfff14SBenjamin Herrenschmidt 					 handle_percpu_irq);
10430ebfff14SBenjamin Herrenschmidt 		return 0;
10440ebfff14SBenjamin Herrenschmidt 	}
10450ebfff14SBenjamin Herrenschmidt #endif /* CONFIG_SMP */
10460ebfff14SBenjamin Herrenschmidt 
1047ea94187fSScott Wood 	if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) {
1048be8bec56SKyle Moffett 		WARN_ON(mpic->flags & MPIC_SECONDARY);
1049ea94187fSScott Wood 
1050ea94187fSScott Wood 		DBG("mpic: mapping as timer\n");
1051ea94187fSScott Wood 		irq_set_chip_data(virq, mpic);
1052ea94187fSScott Wood 		irq_set_chip_and_handler(virq, &mpic->hc_tm,
1053ea94187fSScott Wood 					 handle_fasteoi_irq);
1054ea94187fSScott Wood 		return 0;
1055ea94187fSScott Wood 	}
1056ea94187fSScott Wood 
10570a408164SVarun Sethi 	if (mpic_map_error_int(mpic, virq, hw))
10580a408164SVarun Sethi 		return 0;
10590a408164SVarun Sethi 
10605fe0c1f2SBenjamin Herrenschmidt 	if (hw >= mpic->num_sources) {
10615fe0c1f2SBenjamin Herrenschmidt 		pr_warning("mpic: Mapping of source 0x%x failed, "
10625fe0c1f2SBenjamin Herrenschmidt 			   "source out of range !\n",\
10635fe0c1f2SBenjamin Herrenschmidt 			   (unsigned int)hw);
10640ebfff14SBenjamin Herrenschmidt 		return -EINVAL;
10655fe0c1f2SBenjamin Herrenschmidt 	}
10660ebfff14SBenjamin Herrenschmidt 
1067a7de7c74SMichael Ellerman 	mpic_msi_reserve_hwirq(mpic, hw);
1068a7de7c74SMichael Ellerman 
10696e99e458SBenjamin Herrenschmidt 	/* Default chip */
10700ebfff14SBenjamin Herrenschmidt 	chip = &mpic->hc_irq;
10710ebfff14SBenjamin Herrenschmidt 
10726cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS
10730ebfff14SBenjamin Herrenschmidt 	/* Check for HT interrupts, override vecpri */
10746e99e458SBenjamin Herrenschmidt 	if (mpic_is_ht_interrupt(mpic, hw))
10750ebfff14SBenjamin Herrenschmidt 		chip = &mpic->hc_ht_irq;
10766cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */
10770ebfff14SBenjamin Herrenschmidt 
107806fe98e6SBenjamin Herrenschmidt 	DBG("mpic: mapping to irq chip @%p\n", chip);
10790ebfff14SBenjamin Herrenschmidt 
1080ec775d0eSThomas Gleixner 	irq_set_chip_data(virq, mpic);
1081ec775d0eSThomas Gleixner 	irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq);
10826e99e458SBenjamin Herrenschmidt 
10836e99e458SBenjamin Herrenschmidt 	/* Set default irq type */
1084446f6d06SBenjamin Herrenschmidt 	irq_set_irq_type(virq, IRQ_TYPE_DEFAULT);
10856e99e458SBenjamin Herrenschmidt 
1086dfec2202SMeador Inge 	/* If the MPIC was reset, then all vectors have already been
1087dfec2202SMeador Inge 	 * initialized.  Otherwise, a per source lazy initialization
1088dfec2202SMeador Inge 	 * is done here.
1089dfec2202SMeador Inge 	 */
1090dfec2202SMeador Inge 	if (!mpic_is_ipi(mpic, hw) && (mpic->flags & MPIC_NO_RESET)) {
109132dda05fSScott Wood 		int cpu;
109232dda05fSScott Wood 
109332dda05fSScott Wood 		preempt_disable();
109432dda05fSScott Wood 		cpu = mpic_processor_id(mpic);
109532dda05fSScott Wood 		preempt_enable();
109632dda05fSScott Wood 
1097dfec2202SMeador Inge 		mpic_set_vector(virq, hw);
109832dda05fSScott Wood 		mpic_set_destination(virq, cpu);
1099dfec2202SMeador Inge 		mpic_irq_set_priority(virq, 8);
1100dfec2202SMeador Inge 	}
1101dfec2202SMeador Inge 
11020ebfff14SBenjamin Herrenschmidt 	return 0;
11030ebfff14SBenjamin Herrenschmidt }
11040ebfff14SBenjamin Herrenschmidt 
1105bae1d8f1SGrant Likely static int mpic_host_xlate(struct irq_domain *h, struct device_node *ct,
110640d50cf7SRoman Fietze 			   const u32 *intspec, unsigned int intsize,
11070ebfff14SBenjamin Herrenschmidt 			   irq_hw_number_t *out_hwirq, unsigned int *out_flags)
11080ebfff14SBenjamin Herrenschmidt 
11090ebfff14SBenjamin Herrenschmidt {
111022d168ceSScott Wood 	struct mpic *mpic = h->host_data;
11110ebfff14SBenjamin Herrenschmidt 	static unsigned char map_mpic_senses[4] = {
11120ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_EDGE_RISING,
11130ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_LEVEL_LOW,
11140ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_LEVEL_HIGH,
11150ebfff14SBenjamin Herrenschmidt 		IRQ_TYPE_EDGE_FALLING,
11160ebfff14SBenjamin Herrenschmidt 	};
11170ebfff14SBenjamin Herrenschmidt 
11180ebfff14SBenjamin Herrenschmidt 	*out_hwirq = intspec[0];
111922d168ceSScott Wood 	if (intsize >= 4 && (mpic->flags & MPIC_FSL)) {
112022d168ceSScott Wood 		/*
112122d168ceSScott Wood 		 * Freescale MPIC with extended intspec:
112222d168ceSScott Wood 		 * First two cells are as usual.  Third specifies
112322d168ceSScott Wood 		 * an "interrupt type".  Fourth is type-specific data.
112422d168ceSScott Wood 		 *
112522d168ceSScott Wood 		 * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt
112622d168ceSScott Wood 		 */
112722d168ceSScott Wood 		switch (intspec[2]) {
112822d168ceSScott Wood 		case 0:
11290a408164SVarun Sethi 			break;
11300a408164SVarun Sethi 		case 1:
11310a408164SVarun Sethi 			if (!(mpic->flags & MPIC_FSL_HAS_EIMR))
11320a408164SVarun Sethi 				break;
11330a408164SVarun Sethi 
11340a408164SVarun Sethi 			if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs))
11350a408164SVarun Sethi 				return -EINVAL;
11360a408164SVarun Sethi 
11370a408164SVarun Sethi 			*out_hwirq = mpic->err_int_vecs[intspec[3]];
11380a408164SVarun Sethi 
113922d168ceSScott Wood 			break;
114022d168ceSScott Wood 		case 2:
114122d168ceSScott Wood 			if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs))
114222d168ceSScott Wood 				return -EINVAL;
114322d168ceSScott Wood 
114422d168ceSScott Wood 			*out_hwirq = mpic->ipi_vecs[intspec[0]];
114522d168ceSScott Wood 			break;
114622d168ceSScott Wood 		case 3:
114722d168ceSScott Wood 			if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs))
114822d168ceSScott Wood 				return -EINVAL;
114922d168ceSScott Wood 
115022d168ceSScott Wood 			*out_hwirq = mpic->timer_vecs[intspec[0]];
115122d168ceSScott Wood 			break;
115222d168ceSScott Wood 		default:
115322d168ceSScott Wood 			pr_debug("%s: unknown irq type %u\n",
115422d168ceSScott Wood 				 __func__, intspec[2]);
115522d168ceSScott Wood 			return -EINVAL;
115622d168ceSScott Wood 		}
115722d168ceSScott Wood 
115822d168ceSScott Wood 		*out_flags = map_mpic_senses[intspec[1] & 3];
115922d168ceSScott Wood 	} else if (intsize > 1) {
116006fe98e6SBenjamin Herrenschmidt 		u32 mask = 0x3;
116106fe98e6SBenjamin Herrenschmidt 
116206fe98e6SBenjamin Herrenschmidt 		/* Apple invented a new race of encoding on machines with
116306fe98e6SBenjamin Herrenschmidt 		 * an HT APIC. They encode, among others, the index within
116406fe98e6SBenjamin Herrenschmidt 		 * the HT APIC. We don't care about it here since thankfully,
116506fe98e6SBenjamin Herrenschmidt 		 * it appears that they have the APIC already properly
116606fe98e6SBenjamin Herrenschmidt 		 * configured, and thus our current fixup code that reads the
116706fe98e6SBenjamin Herrenschmidt 		 * APIC config works fine. However, we still need to mask out
116806fe98e6SBenjamin Herrenschmidt 		 * bits in the specifier to make sure we only get bit 0 which
116906fe98e6SBenjamin Herrenschmidt 		 * is the level/edge bit (the only sense bit exposed by Apple),
117006fe98e6SBenjamin Herrenschmidt 		 * as their bit 1 means something else.
117106fe98e6SBenjamin Herrenschmidt 		 */
117206fe98e6SBenjamin Herrenschmidt 		if (machine_is(powermac))
117306fe98e6SBenjamin Herrenschmidt 			mask = 0x1;
117406fe98e6SBenjamin Herrenschmidt 		*out_flags = map_mpic_senses[intspec[1] & mask];
117506fe98e6SBenjamin Herrenschmidt 	} else
11760ebfff14SBenjamin Herrenschmidt 		*out_flags = IRQ_TYPE_NONE;
11770ebfff14SBenjamin Herrenschmidt 
117806fe98e6SBenjamin Herrenschmidt 	DBG("mpic: xlate (%d cells: 0x%08x 0x%08x) to line 0x%lx sense 0x%x\n",
117906fe98e6SBenjamin Herrenschmidt 	    intsize, intspec[0], intspec[1], *out_hwirq, *out_flags);
118006fe98e6SBenjamin Herrenschmidt 
11810ebfff14SBenjamin Herrenschmidt 	return 0;
11820ebfff14SBenjamin Herrenschmidt }
11830ebfff14SBenjamin Herrenschmidt 
118409dc34a9SKyle Moffett /* IRQ handler for a secondary MPIC cascaded from another IRQ controller */
118509dc34a9SKyle Moffett static void mpic_cascade(unsigned int irq, struct irq_desc *desc)
118609dc34a9SKyle Moffett {
118709dc34a9SKyle Moffett 	struct irq_chip *chip = irq_desc_get_chip(desc);
118809dc34a9SKyle Moffett 	struct mpic *mpic = irq_desc_get_handler_data(desc);
118909dc34a9SKyle Moffett 	unsigned int virq;
119009dc34a9SKyle Moffett 
119109dc34a9SKyle Moffett 	BUG_ON(!(mpic->flags & MPIC_SECONDARY));
119209dc34a9SKyle Moffett 
119309dc34a9SKyle Moffett 	virq = mpic_get_one_irq(mpic);
1194bae1d8f1SGrant Likely 	if (virq)
119509dc34a9SKyle Moffett 		generic_handle_irq(virq);
119609dc34a9SKyle Moffett 
119709dc34a9SKyle Moffett 	chip->irq_eoi(&desc->irq_data);
119809dc34a9SKyle Moffett }
119909dc34a9SKyle Moffett 
1200bae1d8f1SGrant Likely static struct irq_domain_ops mpic_host_ops = {
12010ebfff14SBenjamin Herrenschmidt 	.match = mpic_host_match,
12020ebfff14SBenjamin Herrenschmidt 	.map = mpic_host_map,
12030ebfff14SBenjamin Herrenschmidt 	.xlate = mpic_host_xlate,
12040ebfff14SBenjamin Herrenschmidt };
12050ebfff14SBenjamin Herrenschmidt 
120686d37969SHongtao Jia static u32 fsl_mpic_get_version(struct mpic *mpic)
120786d37969SHongtao Jia {
120886d37969SHongtao Jia 	u32 brr1;
120986d37969SHongtao Jia 
121086d37969SHongtao Jia 	if (!(mpic->flags & MPIC_FSL))
121186d37969SHongtao Jia 		return 0;
121286d37969SHongtao Jia 
121386d37969SHongtao Jia 	brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs,
121486d37969SHongtao Jia 			MPIC_FSL_BRR1);
121586d37969SHongtao Jia 
121686d37969SHongtao Jia 	return brr1 & MPIC_FSL_BRR1_VER;
121786d37969SHongtao Jia }
121886d37969SHongtao Jia 
121914cf11afSPaul Mackerras /*
122014cf11afSPaul Mackerras  * Exported functions
122114cf11afSPaul Mackerras  */
122214cf11afSPaul Mackerras 
122386d37969SHongtao Jia u32 fsl_mpic_primary_get_version(void)
122486d37969SHongtao Jia {
122586d37969SHongtao Jia 	struct mpic *mpic = mpic_primary;
122686d37969SHongtao Jia 
122786d37969SHongtao Jia 	if (mpic)
122886d37969SHongtao Jia 		return fsl_mpic_get_version(mpic);
122986d37969SHongtao Jia 
123086d37969SHongtao Jia 	return 0;
123186d37969SHongtao Jia }
123286d37969SHongtao Jia 
12330ebfff14SBenjamin Herrenschmidt struct mpic * __init mpic_alloc(struct device_node *node,
1234a959ff56SBenjamin Herrenschmidt 				phys_addr_t phys_addr,
123514cf11afSPaul Mackerras 				unsigned int flags,
123614cf11afSPaul Mackerras 				unsigned int isu_size,
123714cf11afSPaul Mackerras 				unsigned int irq_count,
123814cf11afSPaul Mackerras 				const char *name)
123914cf11afSPaul Mackerras {
12405bdb6f2eSKyle Moffett 	int i, psize, intvec_top;
124114cf11afSPaul Mackerras 	struct mpic *mpic;
1242d9d1063dSJohannes Berg 	u32 greg_feature;
124314cf11afSPaul Mackerras 	const char *vers;
12445bdb6f2eSKyle Moffett 	const u32 *psrc;
1245c1b8d45dSKyle Moffett 	u32 last_irq;
12467c509ee0SScott Wood 	u32 fsl_version = 0;
12478bf41568SKyle Moffett 
1248996983b7SKyle Moffett 	/* Default MPIC search parameters */
1249996983b7SKyle Moffett 	static const struct of_device_id __initconst mpic_device_id[] = {
1250996983b7SKyle Moffett 		{ .type	      = "open-pic", },
1251996983b7SKyle Moffett 		{ .compatible = "open-pic", },
1252996983b7SKyle Moffett 		{},
1253996983b7SKyle Moffett 	};
1254996983b7SKyle Moffett 
1255996983b7SKyle Moffett 	/*
1256996983b7SKyle Moffett 	 * If we were not passed a device-tree node, then perform the default
1257996983b7SKyle Moffett 	 * search for standardized a standardized OpenPIC.
1258996983b7SKyle Moffett 	 */
1259996983b7SKyle Moffett 	if (node) {
1260996983b7SKyle Moffett 		node = of_node_get(node);
1261996983b7SKyle Moffett 	} else {
1262996983b7SKyle Moffett 		node = of_find_matching_node(NULL, mpic_device_id);
1263996983b7SKyle Moffett 		if (!node)
1264996983b7SKyle Moffett 			return NULL;
1265996983b7SKyle Moffett 	}
12668bf41568SKyle Moffett 
12675bdb6f2eSKyle Moffett 	/* Pick the physical address from the device tree if unspecified */
12685bdb6f2eSKyle Moffett 	if (!phys_addr) {
12698bf41568SKyle Moffett 		/* Check if it is DCR-based */
12708bf41568SKyle Moffett 		if (of_get_property(node, "dcr-reg", NULL)) {
12718bf41568SKyle Moffett 			flags |= MPIC_USES_DCR;
12728bf41568SKyle Moffett 		} else {
12738bf41568SKyle Moffett 			struct resource r;
12748bf41568SKyle Moffett 			if (of_address_to_resource(node, 0, &r))
1275996983b7SKyle Moffett 				goto err_of_node_put;
12768bf41568SKyle Moffett 			phys_addr = r.start;
12778bf41568SKyle Moffett 		}
12788bf41568SKyle Moffett 	}
127914cf11afSPaul Mackerras 
12803a7a7176SKyle Moffett 	/* Read extra device-tree properties into the flags variable */
12813a7a7176SKyle Moffett 	if (of_get_property(node, "big-endian", NULL))
12823a7a7176SKyle Moffett 		flags |= MPIC_BIG_ENDIAN;
12833a7a7176SKyle Moffett 	if (of_get_property(node, "pic-no-reset", NULL))
12843a7a7176SKyle Moffett 		flags |= MPIC_NO_RESET;
12859ca163c8SKyle Moffett 	if (of_get_property(node, "single-cpu-affinity", NULL))
12869ca163c8SKyle Moffett 		flags |= MPIC_SINGLE_DEST_CPU;
12873a7a7176SKyle Moffett 	if (of_device_is_compatible(node, "fsl,mpic"))
12885a271fe7SVarun Sethi 		flags |= MPIC_FSL | MPIC_LARGE_VECTORS;
12893a7a7176SKyle Moffett 
129085355bb2SKumar Gala 	mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL);
129114cf11afSPaul Mackerras 	if (mpic == NULL)
1292996983b7SKyle Moffett 		goto err_of_node_put;
129314cf11afSPaul Mackerras 
129414cf11afSPaul Mackerras 	mpic->name = name;
1295c51242e7SKyle Moffett 	mpic->node = node;
1296e7a98675SKyle Moffett 	mpic->paddr = phys_addr;
12973a7a7176SKyle Moffett 	mpic->flags = flags;
129814cf11afSPaul Mackerras 
1299b9e5b4e6SBenjamin Herrenschmidt 	mpic->hc_irq = mpic_irq_chip;
1300b27df672SThomas Gleixner 	mpic->hc_irq.name = name;
13013a7a7176SKyle Moffett 	if (!(mpic->flags & MPIC_SECONDARY))
1302835c0553SLennert Buytenhek 		mpic->hc_irq.irq_set_affinity = mpic_set_affinity;
13036cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS
1304b9e5b4e6SBenjamin Herrenschmidt 	mpic->hc_ht_irq = mpic_irq_ht_chip;
1305b27df672SThomas Gleixner 	mpic->hc_ht_irq.name = name;
13063a7a7176SKyle Moffett 	if (!(mpic->flags & MPIC_SECONDARY))
1307835c0553SLennert Buytenhek 		mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity;
13086cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */
1309fbf0274eSBenjamin Herrenschmidt 
131014cf11afSPaul Mackerras #ifdef CONFIG_SMP
1311b9e5b4e6SBenjamin Herrenschmidt 	mpic->hc_ipi = mpic_ipi_chip;
1312b27df672SThomas Gleixner 	mpic->hc_ipi.name = name;
131314cf11afSPaul Mackerras #endif /* CONFIG_SMP */
131414cf11afSPaul Mackerras 
1315ea94187fSScott Wood 	mpic->hc_tm = mpic_tm_chip;
1316ea94187fSScott Wood 	mpic->hc_tm.name = name;
1317ea94187fSScott Wood 
131814cf11afSPaul Mackerras 	mpic->num_sources = 0; /* so far */
131914cf11afSPaul Mackerras 
13203a7a7176SKyle Moffett 	if (mpic->flags & MPIC_LARGE_VECTORS)
13217df2457dSOlof Johansson 		intvec_top = 2047;
13227df2457dSOlof Johansson 	else
13237df2457dSOlof Johansson 		intvec_top = 255;
13247df2457dSOlof Johansson 
1325ea94187fSScott Wood 	mpic->timer_vecs[0] = intvec_top - 12;
1326ea94187fSScott Wood 	mpic->timer_vecs[1] = intvec_top - 11;
1327ea94187fSScott Wood 	mpic->timer_vecs[2] = intvec_top - 10;
1328ea94187fSScott Wood 	mpic->timer_vecs[3] = intvec_top - 9;
1329ea94187fSScott Wood 	mpic->timer_vecs[4] = intvec_top - 8;
1330ea94187fSScott Wood 	mpic->timer_vecs[5] = intvec_top - 7;
1331ea94187fSScott Wood 	mpic->timer_vecs[6] = intvec_top - 6;
1332ea94187fSScott Wood 	mpic->timer_vecs[7] = intvec_top - 5;
13337df2457dSOlof Johansson 	mpic->ipi_vecs[0]   = intvec_top - 4;
13347df2457dSOlof Johansson 	mpic->ipi_vecs[1]   = intvec_top - 3;
13357df2457dSOlof Johansson 	mpic->ipi_vecs[2]   = intvec_top - 2;
13367df2457dSOlof Johansson 	mpic->ipi_vecs[3]   = intvec_top - 1;
13377df2457dSOlof Johansson 	mpic->spurious_vec  = intvec_top;
13387df2457dSOlof Johansson 
13397fd72186SBenjamin Herrenschmidt 	/* Look for protected sources */
1340c51242e7SKyle Moffett 	psrc = of_get_property(mpic->node, "protected-sources", &psize);
13417fd72186SBenjamin Herrenschmidt 	if (psrc) {
13425bdb6f2eSKyle Moffett 		/* Allocate a bitmap with one bit per interrupt */
13435bdb6f2eSKyle Moffett 		unsigned int mapsize = BITS_TO_LONGS(intvec_top + 1);
13445bdb6f2eSKyle Moffett 		mpic->protected = kzalloc(mapsize*sizeof(long), GFP_KERNEL);
13457fd72186SBenjamin Herrenschmidt 		BUG_ON(mpic->protected == NULL);
13465bdb6f2eSKyle Moffett 		for (i = 0; i < psize/sizeof(u32); i++) {
13477fd72186SBenjamin Herrenschmidt 			if (psrc[i] > intvec_top)
13487fd72186SBenjamin Herrenschmidt 				continue;
13497fd72186SBenjamin Herrenschmidt 			__set_bit(psrc[i], mpic->protected);
13507fd72186SBenjamin Herrenschmidt 		}
13517fd72186SBenjamin Herrenschmidt 	}
1352a959ff56SBenjamin Herrenschmidt 
13537233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD
13543a7a7176SKyle Moffett 	mpic->hw_set = mpic_infos[MPIC_GET_REGSET(mpic->flags)];
13557233593bSZang Roy-r61911 #endif
13567233593bSZang Roy-r61911 
1357fbf0274eSBenjamin Herrenschmidt 	/* default register type */
13583a7a7176SKyle Moffett 	if (mpic->flags & MPIC_BIG_ENDIAN)
13598bf41568SKyle Moffett 		mpic->reg_type = mpic_access_mmio_be;
13608bf41568SKyle Moffett 	else
13618bf41568SKyle Moffett 		mpic->reg_type = mpic_access_mmio_le;
1362fbf0274eSBenjamin Herrenschmidt 
13638bf41568SKyle Moffett 	/*
13648bf41568SKyle Moffett 	 * An MPIC with a "dcr-reg" property must be accessed that way, but
13658bf41568SKyle Moffett 	 * only if the kernel includes DCR support.
13668bf41568SKyle Moffett 	 */
1367fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR
13683a7a7176SKyle Moffett 	if (mpic->flags & MPIC_USES_DCR)
1369fbf0274eSBenjamin Herrenschmidt 		mpic->reg_type = mpic_access_dcr;
1370fbf0274eSBenjamin Herrenschmidt #else
13713a7a7176SKyle Moffett 	BUG_ON(mpic->flags & MPIC_USES_DCR);
13728bf41568SKyle Moffett #endif
1373a959ff56SBenjamin Herrenschmidt 
137414cf11afSPaul Mackerras 	/* Map the global registers */
1375c51242e7SKyle Moffett 	mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000);
1376c51242e7SKyle Moffett 	mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
137714cf11afSPaul Mackerras 
137803bcb7e3SVarun Sethi 	if (mpic->flags & MPIC_FSL) {
13790a408164SVarun Sethi 		int ret;
13800a408164SVarun Sethi 
138103bcb7e3SVarun Sethi 		/*
138203bcb7e3SVarun Sethi 		 * Yes, Freescale really did put global registers in the
138303bcb7e3SVarun Sethi 		 * magic per-cpu area -- and they don't even show up in the
138403bcb7e3SVarun Sethi 		 * non-magic per-cpu copies that this driver normally uses.
138503bcb7e3SVarun Sethi 		 */
138603bcb7e3SVarun Sethi 		mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs,
138703bcb7e3SVarun Sethi 			 MPIC_CPU_THISBASE, 0x1000);
13880a408164SVarun Sethi 
138986d37969SHongtao Jia 		fsl_version = fsl_mpic_get_version(mpic);
13900a408164SVarun Sethi 
13910a408164SVarun Sethi 		/* Error interrupt mask register (EIMR) is required for
13920a408164SVarun Sethi 		 * handling individual device error interrupts. EIMR
13930a408164SVarun Sethi 		 * was added in MPIC version 4.1.
13940a408164SVarun Sethi 		 *
13950a408164SVarun Sethi 		 * Over here we reserve vector number space for error
13960a408164SVarun Sethi 		 * interrupt vectors. This space is stolen from the
13970a408164SVarun Sethi 		 * global vector number space, as in case of ipis
13980a408164SVarun Sethi 		 * and timer interrupts.
13990a408164SVarun Sethi 		 *
14000a408164SVarun Sethi 		 * Available vector space = intvec_top - 12, where 12
14010a408164SVarun Sethi 		 * is the number of vectors which have been consumed by
14020a408164SVarun Sethi 		 * ipis and timer interrupts.
14030a408164SVarun Sethi 		 */
14047c509ee0SScott Wood 		if (fsl_version >= 0x401) {
14050a408164SVarun Sethi 			ret = mpic_setup_error_int(mpic, intvec_top - 12);
14060a408164SVarun Sethi 			if (ret)
14070a408164SVarun Sethi 				return NULL;
14080a408164SVarun Sethi 		}
14097c509ee0SScott Wood 
14107c509ee0SScott Wood 	}
14117c509ee0SScott Wood 
14127c509ee0SScott Wood 	/*
14137c509ee0SScott Wood 	 * EPR is only available starting with v4.0.  To support
14147c509ee0SScott Wood 	 * platforms that don't know the MPIC version at compile-time,
14157c509ee0SScott Wood 	 * such as qemu-e500, turn off coreint if this MPIC doesn't
14167c509ee0SScott Wood 	 * support it.  Note that we never enable it if it wasn't
14177c509ee0SScott Wood 	 * requested in the first place.
14187c509ee0SScott Wood 	 *
14197c509ee0SScott Wood 	 * This is done outside the MPIC_FSL check, so that we
14207c509ee0SScott Wood 	 * also disable coreint if the MPIC node doesn't have
14217c509ee0SScott Wood 	 * an "fsl,mpic" compatible at all.  This will be the case
14227c509ee0SScott Wood 	 * with device trees generated by older versions of QEMU.
14237c509ee0SScott Wood 	 * fsl_version will be zero if MPIC_FSL is not set.
14247c509ee0SScott Wood 	 */
14257c509ee0SScott Wood 	if (fsl_version < 0x400 && (flags & MPIC_ENABLE_COREINT)) {
14267c509ee0SScott Wood 		WARN_ON(ppc_md.get_irq != mpic_get_coreint_irq);
14277c509ee0SScott Wood 		ppc_md.get_irq = mpic_get_irq;
142803bcb7e3SVarun Sethi 	}
142903bcb7e3SVarun Sethi 
143014cf11afSPaul Mackerras 	/* Reset */
1431dfec2202SMeador Inge 
1432dfec2202SMeador Inge 	/* When using a device-node, reset requests are only honored if the MPIC
1433dfec2202SMeador Inge 	 * is allowed to reset.
1434dfec2202SMeador Inge 	 */
1435e55d7f73SKyle Moffett 	if (!(mpic->flags & MPIC_NO_RESET)) {
1436dfec2202SMeador Inge 		printk(KERN_DEBUG "mpic: Resetting\n");
14377233593bSZang Roy-r61911 		mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
14387233593bSZang Roy-r61911 			   mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
143914cf11afSPaul Mackerras 			   | MPIC_GREG_GCONF_RESET);
14407233593bSZang Roy-r61911 		while( mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
144114cf11afSPaul Mackerras 		       & MPIC_GREG_GCONF_RESET)
144214cf11afSPaul Mackerras 			mb();
144314cf11afSPaul Mackerras 	}
144414cf11afSPaul Mackerras 
1445d91e4ea7SKumar Gala 	/* CoreInt */
14463a7a7176SKyle Moffett 	if (mpic->flags & MPIC_ENABLE_COREINT)
1447d91e4ea7SKumar Gala 		mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
1448d91e4ea7SKumar Gala 			   mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
1449d91e4ea7SKumar Gala 			   | MPIC_GREG_GCONF_COREINT);
1450d91e4ea7SKumar Gala 
14513a7a7176SKyle Moffett 	if (mpic->flags & MPIC_ENABLE_MCK)
1452f365355eSOlof Johansson 		mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
1453f365355eSOlof Johansson 			   mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
1454f365355eSOlof Johansson 			   | MPIC_GREG_GCONF_MCK);
1455f365355eSOlof Johansson 
145614b92470STimur Tabi 	/*
145714b92470STimur Tabi 	 * The MPIC driver will crash if there are more cores than we
145814b92470STimur Tabi 	 * can initialize, so we may as well catch that problem here.
145914b92470STimur Tabi 	 */
146014b92470STimur Tabi 	BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS);
146114b92470STimur Tabi 
146214cf11afSPaul Mackerras 	/* Map the per-CPU registers */
146314b92470STimur Tabi 	for_each_possible_cpu(i) {
146414b92470STimur Tabi 		unsigned int cpu = get_hard_smp_processor_id(i);
146514b92470STimur Tabi 
1466c51242e7SKyle Moffett 		mpic_map(mpic, mpic->paddr, &mpic->cpuregs[cpu],
146714b92470STimur Tabi 			 MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE),
1468fbf0274eSBenjamin Herrenschmidt 			 0x1000);
146914cf11afSPaul Mackerras 	}
147014cf11afSPaul Mackerras 
1471c1b8d45dSKyle Moffett 	/*
1472c1b8d45dSKyle Moffett 	 * Read feature register.  For non-ISU MPICs, num sources as well. On
1473c1b8d45dSKyle Moffett 	 * ISU MPICs, sources are counted as ISUs are added
1474c1b8d45dSKyle Moffett 	 */
1475c1b8d45dSKyle Moffett 	greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0));
1476c1b8d45dSKyle Moffett 
1477c1b8d45dSKyle Moffett 	/*
1478c1b8d45dSKyle Moffett 	 * By default, the last source number comes from the MPIC, but the
1479c1b8d45dSKyle Moffett 	 * device-tree and board support code can override it on buggy hw.
1480fe83364fSBenjamin Herrenschmidt 	 * If we get passed an isu_size (multi-isu MPIC) then we use that
1481fe83364fSBenjamin Herrenschmidt 	 * as a default instead of the value read from the HW.
1482c1b8d45dSKyle Moffett 	 */
1483c1b8d45dSKyle Moffett 	last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK)
1484c1b8d45dSKyle Moffett 				>> MPIC_GREG_FEATURE_LAST_SRC_SHIFT;
1485fe83364fSBenjamin Herrenschmidt 	if (isu_size)
1486fe83364fSBenjamin Herrenschmidt 		last_irq = isu_size  * MPIC_MAX_ISU - 1;
1487c1b8d45dSKyle Moffett 	of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq);
1488c1b8d45dSKyle Moffett 	if (irq_count)
1489c1b8d45dSKyle Moffett 		last_irq = irq_count - 1;
1490c1b8d45dSKyle Moffett 
149114cf11afSPaul Mackerras 	/* Initialize main ISU if none provided */
1492c1b8d45dSKyle Moffett 	if (!isu_size) {
1493c1b8d45dSKyle Moffett 		isu_size = last_irq + 1;
1494c1b8d45dSKyle Moffett 		mpic->num_sources = isu_size;
1495c51242e7SKyle Moffett 		mpic_map(mpic, mpic->paddr, &mpic->isus[0],
1496c1b8d45dSKyle Moffett 				MPIC_INFO(IRQ_BASE),
1497c1b8d45dSKyle Moffett 				MPIC_INFO(IRQ_STRIDE) * isu_size);
149814cf11afSPaul Mackerras 	}
1499c1b8d45dSKyle Moffett 
1500c1b8d45dSKyle Moffett 	mpic->isu_size = isu_size;
150114cf11afSPaul Mackerras 	mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
150214cf11afSPaul Mackerras 	mpic->isu_mask = (1 << mpic->isu_shift) - 1;
150314cf11afSPaul Mackerras 
1504a8db8cf0SGrant Likely 	mpic->irqhost = irq_domain_add_linear(mpic->node,
1505574ce79cSBenjamin Herrenschmidt 				       intvec_top,
1506a8db8cf0SGrant Likely 				       &mpic_host_ops, mpic);
1507996983b7SKyle Moffett 
1508996983b7SKyle Moffett 	/*
1509996983b7SKyle Moffett 	 * FIXME: The code leaks the MPIC object and mappings here; this
1510996983b7SKyle Moffett 	 * is very unlikely to fail but it ought to be fixed anyways.
1511996983b7SKyle Moffett 	 */
151231207dabSKumar Gala 	if (mpic->irqhost == NULL)
151331207dabSKumar Gala 		return NULL;
151431207dabSKumar Gala 
151514cf11afSPaul Mackerras 	/* Display version */
1516d9d1063dSJohannes Berg 	switch (greg_feature & MPIC_GREG_FEATURE_VERSION_MASK) {
151714cf11afSPaul Mackerras 	case 1:
151814cf11afSPaul Mackerras 		vers = "1.0";
151914cf11afSPaul Mackerras 		break;
152014cf11afSPaul Mackerras 	case 2:
152114cf11afSPaul Mackerras 		vers = "1.2";
152214cf11afSPaul Mackerras 		break;
152314cf11afSPaul Mackerras 	case 3:
152414cf11afSPaul Mackerras 		vers = "1.3";
152514cf11afSPaul Mackerras 		break;
152614cf11afSPaul Mackerras 	default:
152714cf11afSPaul Mackerras 		vers = "<unknown>";
152814cf11afSPaul Mackerras 		break;
152914cf11afSPaul Mackerras 	}
1530a959ff56SBenjamin Herrenschmidt 	printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx,"
1531a959ff56SBenjamin Herrenschmidt 	       " max %d CPUs\n",
1532e7a98675SKyle Moffett 	       name, vers, (unsigned long long)mpic->paddr, num_possible_cpus());
1533a959ff56SBenjamin Herrenschmidt 	printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n",
1534a959ff56SBenjamin Herrenschmidt 	       mpic->isu_size, mpic->isu_shift, mpic->isu_mask);
153514cf11afSPaul Mackerras 
153614cf11afSPaul Mackerras 	mpic->next = mpics;
153714cf11afSPaul Mackerras 	mpics = mpic;
153814cf11afSPaul Mackerras 
15393a7a7176SKyle Moffett 	if (!(mpic->flags & MPIC_SECONDARY)) {
154014cf11afSPaul Mackerras 		mpic_primary = mpic;
15410ebfff14SBenjamin Herrenschmidt 		irq_set_default_host(mpic->irqhost);
15420ebfff14SBenjamin Herrenschmidt 	}
154314cf11afSPaul Mackerras 
154414cf11afSPaul Mackerras 	return mpic;
1545996983b7SKyle Moffett 
1546996983b7SKyle Moffett err_of_node_put:
1547996983b7SKyle Moffett 	of_node_put(node);
1548996983b7SKyle Moffett 	return NULL;
154914cf11afSPaul Mackerras }
155014cf11afSPaul Mackerras 
155114cf11afSPaul Mackerras void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
1552a959ff56SBenjamin Herrenschmidt 			    phys_addr_t paddr)
155314cf11afSPaul Mackerras {
155414cf11afSPaul Mackerras 	unsigned int isu_first = isu_num * mpic->isu_size;
155514cf11afSPaul Mackerras 
155614cf11afSPaul Mackerras 	BUG_ON(isu_num >= MPIC_MAX_ISU);
155714cf11afSPaul Mackerras 
1558c51242e7SKyle Moffett 	mpic_map(mpic,
15595a2642f6SBenjamin Herrenschmidt 		 paddr, &mpic->isus[isu_num], 0,
15607233593bSZang Roy-r61911 		 MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
15615a2642f6SBenjamin Herrenschmidt 
156214cf11afSPaul Mackerras 	if ((isu_first + mpic->isu_size) > mpic->num_sources)
156314cf11afSPaul Mackerras 		mpic->num_sources = isu_first + mpic->isu_size;
156414cf11afSPaul Mackerras }
156514cf11afSPaul Mackerras 
156614cf11afSPaul Mackerras void __init mpic_init(struct mpic *mpic)
156714cf11afSPaul Mackerras {
156809dc34a9SKyle Moffett 	int i, cpu;
156903bcb7e3SVarun Sethi 	int num_timers = 4;
157014cf11afSPaul Mackerras 
157114cf11afSPaul Mackerras 	BUG_ON(mpic->num_sources == 0);
157214cf11afSPaul Mackerras 
157314cf11afSPaul Mackerras 	printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources);
157414cf11afSPaul Mackerras 
157514cf11afSPaul Mackerras 	/* Set current processor priority to max */
15767233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
157714cf11afSPaul Mackerras 
157803bcb7e3SVarun Sethi 	if (mpic->flags & MPIC_FSL) {
157986d37969SHongtao Jia 		u32 version = fsl_mpic_get_version(mpic);
158003bcb7e3SVarun Sethi 
158103bcb7e3SVarun Sethi 		/*
158203bcb7e3SVarun Sethi 		 * Timer group B is present at the latest in MPIC 3.1 (e.g.
158303bcb7e3SVarun Sethi 		 * mpc8536).  It is not present in MPIC 2.0 (e.g. mpc8544).
158403bcb7e3SVarun Sethi 		 * I don't know about the status of intermediate versions (or
158503bcb7e3SVarun Sethi 		 * whether they even exist).
158603bcb7e3SVarun Sethi 		 */
158703bcb7e3SVarun Sethi 		if (version >= 0x0301)
158803bcb7e3SVarun Sethi 			num_timers = 8;
158903bcb7e3SVarun Sethi 	}
159003bcb7e3SVarun Sethi 
1591ea94187fSScott Wood 	/* Initialize timers to our reserved vectors and mask them for now */
159203bcb7e3SVarun Sethi 	for (i = 0; i < num_timers; i++) {
159303bcb7e3SVarun Sethi 		unsigned int offset = mpic_tm_offset(mpic, i);
159403bcb7e3SVarun Sethi 
159514cf11afSPaul Mackerras 		mpic_write(mpic->tmregs,
159603bcb7e3SVarun Sethi 			   offset + MPIC_INFO(TIMER_DESTINATION),
1597ea94187fSScott Wood 			   1 << hard_smp_processor_id());
159814cf11afSPaul Mackerras 		mpic_write(mpic->tmregs,
159903bcb7e3SVarun Sethi 			   offset + MPIC_INFO(TIMER_VECTOR_PRI),
160014cf11afSPaul Mackerras 			   MPIC_VECPRI_MASK |
1601ea94187fSScott Wood 			   (9 << MPIC_VECPRI_PRIORITY_SHIFT) |
16027df2457dSOlof Johansson 			   (mpic->timer_vecs[0] + i));
160314cf11afSPaul Mackerras 	}
160414cf11afSPaul Mackerras 
160514cf11afSPaul Mackerras 	/* Initialize IPIs to our reserved vectors and mark them disabled for now */
160614cf11afSPaul Mackerras 	mpic_test_broken_ipi(mpic);
160714cf11afSPaul Mackerras 	for (i = 0; i < 4; i++) {
160814cf11afSPaul Mackerras 		mpic_ipi_write(i,
160914cf11afSPaul Mackerras 			       MPIC_VECPRI_MASK |
161014cf11afSPaul Mackerras 			       (10 << MPIC_VECPRI_PRIORITY_SHIFT) |
16117df2457dSOlof Johansson 			       (mpic->ipi_vecs[0] + i));
161214cf11afSPaul Mackerras 	}
161314cf11afSPaul Mackerras 
16141beb6a7dSBenjamin Herrenschmidt 	/* Do the HT PIC fixups on U3 broken mpic */
161514cf11afSPaul Mackerras 	DBG("MPIC flags: %x\n", mpic->flags);
1616be8bec56SKyle Moffett 	if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) {
16171beb6a7dSBenjamin Herrenschmidt 		mpic_scan_ht_pics(mpic);
161805af7bd2SMichael Ellerman 		mpic_u3msi_init(mpic);
161905af7bd2SMichael Ellerman 	}
162014cf11afSPaul Mackerras 
162138958dd9SOlof Johansson 	mpic_pasemi_msi_init(mpic);
162238958dd9SOlof Johansson 
1623d6a2639bSMeador Inge 	cpu = mpic_processor_id(mpic);
1624cc353c30SArnd Bergmann 
1625dfec2202SMeador Inge 	if (!(mpic->flags & MPIC_NO_RESET)) {
162614cf11afSPaul Mackerras 		for (i = 0; i < mpic->num_sources; i++) {
162714cf11afSPaul Mackerras 			/* start with vector = source number, and masked */
16286e99e458SBenjamin Herrenschmidt 			u32 vecpri = MPIC_VECPRI_MASK | i |
16296e99e458SBenjamin Herrenschmidt 				(8 << MPIC_VECPRI_PRIORITY_SHIFT);
163014cf11afSPaul Mackerras 
16317fd72186SBenjamin Herrenschmidt 			/* check if protected */
16327fd72186SBenjamin Herrenschmidt 			if (mpic->protected && test_bit(i, mpic->protected))
16337fd72186SBenjamin Herrenschmidt 				continue;
163414cf11afSPaul Mackerras 			/* init hw */
16357233593bSZang Roy-r61911 			mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
1636cc353c30SArnd Bergmann 			mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu);
163714cf11afSPaul Mackerras 		}
1638dfec2202SMeador Inge 	}
163914cf11afSPaul Mackerras 
16407df2457dSOlof Johansson 	/* Init spurious vector */
16417df2457dSOlof Johansson 	mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec);
164214cf11afSPaul Mackerras 
16437233593bSZang Roy-r61911 	/* Disable 8259 passthrough, if supported */
16447233593bSZang Roy-r61911 	if (!(mpic->flags & MPIC_NO_PTHROU_DIS))
16457233593bSZang Roy-r61911 		mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
16467233593bSZang Roy-r61911 			   mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
164714cf11afSPaul Mackerras 			   | MPIC_GREG_GCONF_8259_PTHROU_DIS);
164814cf11afSPaul Mackerras 
1649d87bf3beSOlof Johansson 	if (mpic->flags & MPIC_NO_BIAS)
1650d87bf3beSOlof Johansson 		mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0),
1651d87bf3beSOlof Johansson 			mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0))
1652d87bf3beSOlof Johansson 			| MPIC_GREG_GCONF_NO_BIAS);
1653d87bf3beSOlof Johansson 
165414cf11afSPaul Mackerras 	/* Set current processor priority to 0 */
16557233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0);
16563669e930SJohannes Berg 
16573669e930SJohannes Berg #ifdef CONFIG_PM
16583669e930SJohannes Berg 	/* allocate memory to save mpic state */
1659ea96025aSAnton Vorontsov 	mpic->save_data = kmalloc(mpic->num_sources * sizeof(*mpic->save_data),
1660ea96025aSAnton Vorontsov 				  GFP_KERNEL);
16613669e930SJohannes Berg 	BUG_ON(mpic->save_data == NULL);
16623669e930SJohannes Berg #endif
166309dc34a9SKyle Moffett 
166409dc34a9SKyle Moffett 	/* Check if this MPIC is chained from a parent interrupt controller */
166509dc34a9SKyle Moffett 	if (mpic->flags & MPIC_SECONDARY) {
166609dc34a9SKyle Moffett 		int virq = irq_of_parse_and_map(mpic->node, 0);
166709dc34a9SKyle Moffett 		if (virq != NO_IRQ) {
166809dc34a9SKyle Moffett 			printk(KERN_INFO "%s: hooking up to IRQ %d\n",
166909dc34a9SKyle Moffett 					mpic->node->full_name, virq);
167009dc34a9SKyle Moffett 			irq_set_handler_data(virq, mpic);
167109dc34a9SKyle Moffett 			irq_set_chained_handler(virq, &mpic_cascade);
167209dc34a9SKyle Moffett 		}
167309dc34a9SKyle Moffett 	}
1674aa80581dSScott Wood 
1675aa80581dSScott Wood 	/* FSL mpic error interrupt intialization */
1676aa80581dSScott Wood 	if (mpic->flags & MPIC_FSL_HAS_EIMR)
1677aa80581dSScott Wood 		mpic_err_int_init(mpic, MPIC_FSL_ERR_INT);
167814cf11afSPaul Mackerras }
167914cf11afSPaul Mackerras 
1680868ea0c9SMark A. Greer void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio)
1681868ea0c9SMark A. Greer {
1682868ea0c9SMark A. Greer 	u32 v;
168314cf11afSPaul Mackerras 
1684868ea0c9SMark A. Greer 	v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1);
1685868ea0c9SMark A. Greer 	v &= ~MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO_MASK;
1686868ea0c9SMark A. Greer 	v |= MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO(clock_ratio);
1687868ea0c9SMark A. Greer 	mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v);
1688868ea0c9SMark A. Greer }
1689868ea0c9SMark A. Greer 
1690868ea0c9SMark A. Greer void __init mpic_set_serial_int(struct mpic *mpic, int enable)
1691868ea0c9SMark A. Greer {
1692ba1826e5SBenjamin Herrenschmidt 	unsigned long flags;
1693868ea0c9SMark A. Greer 	u32 v;
1694868ea0c9SMark A. Greer 
1695203041adSThomas Gleixner 	raw_spin_lock_irqsave(&mpic_lock, flags);
1696868ea0c9SMark A. Greer 	v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1);
1697868ea0c9SMark A. Greer 	if (enable)
1698868ea0c9SMark A. Greer 		v |= MPIC_GREG_GLOBAL_CONF_1_SIE;
1699868ea0c9SMark A. Greer 	else
1700868ea0c9SMark A. Greer 		v &= ~MPIC_GREG_GLOBAL_CONF_1_SIE;
1701868ea0c9SMark A. Greer 	mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v);
1702203041adSThomas Gleixner 	raw_spin_unlock_irqrestore(&mpic_lock, flags);
1703868ea0c9SMark A. Greer }
170414cf11afSPaul Mackerras 
170514cf11afSPaul Mackerras void mpic_irq_set_priority(unsigned int irq, unsigned int pri)
170614cf11afSPaul Mackerras {
1707d69a78d7STony Breeds 	struct mpic *mpic = mpic_find(irq);
1708476eb491SGrant Likely 	unsigned int src = virq_to_hw(irq);
170914cf11afSPaul Mackerras 	unsigned long flags;
171014cf11afSPaul Mackerras 	u32 reg;
171114cf11afSPaul Mackerras 
171206a901c5SStephen Rothwell 	if (!mpic)
171306a901c5SStephen Rothwell 		return;
171406a901c5SStephen Rothwell 
1715203041adSThomas Gleixner 	raw_spin_lock_irqsave(&mpic_lock, flags);
17163a2b4f7cSBenjamin Herrenschmidt 	if (mpic_is_ipi(mpic, src)) {
17177df2457dSOlof Johansson 		reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) &
1718e5356640SBenjamin Herrenschmidt 			~MPIC_VECPRI_PRIORITY_MASK;
17197df2457dSOlof Johansson 		mpic_ipi_write(src - mpic->ipi_vecs[0],
172014cf11afSPaul Mackerras 			       reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
17213a2b4f7cSBenjamin Herrenschmidt 	} else if (mpic_is_tm(mpic, src)) {
1722ea94187fSScott Wood 		reg = mpic_tm_read(src - mpic->timer_vecs[0]) &
1723ea94187fSScott Wood 			~MPIC_VECPRI_PRIORITY_MASK;
1724ea94187fSScott Wood 		mpic_tm_write(src - mpic->timer_vecs[0],
1725ea94187fSScott Wood 			      reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
172614cf11afSPaul Mackerras 	} else {
17277233593bSZang Roy-r61911 		reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI))
1728e5356640SBenjamin Herrenschmidt 			& ~MPIC_VECPRI_PRIORITY_MASK;
17297233593bSZang Roy-r61911 		mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI),
173014cf11afSPaul Mackerras 			       reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT));
173114cf11afSPaul Mackerras 	}
1732203041adSThomas Gleixner 	raw_spin_unlock_irqrestore(&mpic_lock, flags);
173314cf11afSPaul Mackerras }
173414cf11afSPaul Mackerras 
173514cf11afSPaul Mackerras void mpic_setup_this_cpu(void)
173614cf11afSPaul Mackerras {
173714cf11afSPaul Mackerras #ifdef CONFIG_SMP
173814cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
173914cf11afSPaul Mackerras 	unsigned long flags;
174014cf11afSPaul Mackerras 	u32 msk = 1 << hard_smp_processor_id();
174114cf11afSPaul Mackerras 	unsigned int i;
174214cf11afSPaul Mackerras 
174314cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
174414cf11afSPaul Mackerras 
174514cf11afSPaul Mackerras 	DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
174614cf11afSPaul Mackerras 
1747203041adSThomas Gleixner 	raw_spin_lock_irqsave(&mpic_lock, flags);
174814cf11afSPaul Mackerras 
174914cf11afSPaul Mackerras  	/* let the mpic know we want intrs. default affinity is 0xffffffff
175014cf11afSPaul Mackerras 	 * until changed via /proc. That's how it's done on x86. If we want
175114cf11afSPaul Mackerras 	 * it differently, then we should make sure we also change the default
1752a53da52fSIngo Molnar 	 * values of irq_desc[].affinity in irq.c.
175314cf11afSPaul Mackerras  	 */
1754e242114aSchenhui zhao 	if (distribute_irqs && !(mpic->flags & MPIC_SINGLE_DEST_CPU)) {
175514cf11afSPaul Mackerras 	 	for (i = 0; i < mpic->num_sources ; i++)
17567233593bSZang Roy-r61911 			mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
17577233593bSZang Roy-r61911 				mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk);
175814cf11afSPaul Mackerras 	}
175914cf11afSPaul Mackerras 
176014cf11afSPaul Mackerras 	/* Set current processor priority to 0 */
17617233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0);
176214cf11afSPaul Mackerras 
1763203041adSThomas Gleixner 	raw_spin_unlock_irqrestore(&mpic_lock, flags);
176414cf11afSPaul Mackerras #endif /* CONFIG_SMP */
176514cf11afSPaul Mackerras }
176614cf11afSPaul Mackerras 
176714cf11afSPaul Mackerras int mpic_cpu_get_priority(void)
176814cf11afSPaul Mackerras {
176914cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
177014cf11afSPaul Mackerras 
17717233593bSZang Roy-r61911 	return mpic_cpu_read(MPIC_INFO(CPU_CURRENT_TASK_PRI));
177214cf11afSPaul Mackerras }
177314cf11afSPaul Mackerras 
177414cf11afSPaul Mackerras void mpic_cpu_set_priority(int prio)
177514cf11afSPaul Mackerras {
177614cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
177714cf11afSPaul Mackerras 
177814cf11afSPaul Mackerras 	prio &= MPIC_CPU_TASKPRI_MASK;
17797233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), prio);
178014cf11afSPaul Mackerras }
178114cf11afSPaul Mackerras 
178214cf11afSPaul Mackerras void mpic_teardown_this_cpu(int secondary)
178314cf11afSPaul Mackerras {
178414cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
178514cf11afSPaul Mackerras 	unsigned long flags;
178614cf11afSPaul Mackerras 	u32 msk = 1 << hard_smp_processor_id();
178714cf11afSPaul Mackerras 	unsigned int i;
178814cf11afSPaul Mackerras 
178914cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
179014cf11afSPaul Mackerras 
179114cf11afSPaul Mackerras 	DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id());
1792203041adSThomas Gleixner 	raw_spin_lock_irqsave(&mpic_lock, flags);
179314cf11afSPaul Mackerras 
179414cf11afSPaul Mackerras 	/* let the mpic know we don't want intrs.  */
179514cf11afSPaul Mackerras 	for (i = 0; i < mpic->num_sources ; i++)
17967233593bSZang Roy-r61911 		mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
17977233593bSZang Roy-r61911 			mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) & ~msk);
179814cf11afSPaul Mackerras 
179914cf11afSPaul Mackerras 	/* Set current processor priority to max */
18007233593bSZang Roy-r61911 	mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
18017132799bSValentine Barshak 	/* We need to EOI the IPI since not all platforms reset the MPIC
18027132799bSValentine Barshak 	 * on boot and new interrupts wouldn't get delivered otherwise.
18037132799bSValentine Barshak 	 */
18047132799bSValentine Barshak 	mpic_eoi(mpic);
180514cf11afSPaul Mackerras 
1806203041adSThomas Gleixner 	raw_spin_unlock_irqrestore(&mpic_lock, flags);
180714cf11afSPaul Mackerras }
180814cf11afSPaul Mackerras 
180914cf11afSPaul Mackerras 
1810f365355eSOlof Johansson static unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg)
181114cf11afSPaul Mackerras {
18120ebfff14SBenjamin Herrenschmidt 	u32 src;
181314cf11afSPaul Mackerras 
1814f365355eSOlof Johansson 	src = mpic_cpu_read(reg) & MPIC_INFO(VECPRI_VECTOR_MASK);
18151beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_LOW
1816f365355eSOlof Johansson 	DBG("%s: get_one_irq(reg 0x%x): %d\n", mpic->name, reg, src);
18171beb6a7dSBenjamin Herrenschmidt #endif
18185cddd2e3SJosh Boyer 	if (unlikely(src == mpic->spurious_vec)) {
18195cddd2e3SJosh Boyer 		if (mpic->flags & MPIC_SPV_EOI)
18205cddd2e3SJosh Boyer 			mpic_eoi(mpic);
18210ebfff14SBenjamin Herrenschmidt 		return NO_IRQ;
18225cddd2e3SJosh Boyer 	}
18237fd72186SBenjamin Herrenschmidt 	if (unlikely(mpic->protected && test_bit(src, mpic->protected))) {
182476462232SChristian Dietrich 		printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n",
18257fd72186SBenjamin Herrenschmidt 				   mpic->name, (int)src);
18267fd72186SBenjamin Herrenschmidt 		mpic_eoi(mpic);
18277fd72186SBenjamin Herrenschmidt 		return NO_IRQ;
18287fd72186SBenjamin Herrenschmidt 	}
18297fd72186SBenjamin Herrenschmidt 
18300ebfff14SBenjamin Herrenschmidt 	return irq_linear_revmap(mpic->irqhost, src);
183114cf11afSPaul Mackerras }
183214cf11afSPaul Mackerras 
1833f365355eSOlof Johansson unsigned int mpic_get_one_irq(struct mpic *mpic)
1834f365355eSOlof Johansson {
1835f365355eSOlof Johansson 	return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_INTACK));
1836f365355eSOlof Johansson }
1837f365355eSOlof Johansson 
183835a84c2fSOlaf Hering unsigned int mpic_get_irq(void)
183914cf11afSPaul Mackerras {
184014cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
184114cf11afSPaul Mackerras 
184214cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
184314cf11afSPaul Mackerras 
184435a84c2fSOlaf Hering 	return mpic_get_one_irq(mpic);
184514cf11afSPaul Mackerras }
184614cf11afSPaul Mackerras 
1847d91e4ea7SKumar Gala unsigned int mpic_get_coreint_irq(void)
1848d91e4ea7SKumar Gala {
1849d91e4ea7SKumar Gala #ifdef CONFIG_BOOKE
1850d91e4ea7SKumar Gala 	struct mpic *mpic = mpic_primary;
1851d91e4ea7SKumar Gala 	u32 src;
1852d91e4ea7SKumar Gala 
1853d91e4ea7SKumar Gala 	BUG_ON(mpic == NULL);
1854d91e4ea7SKumar Gala 
1855d91e4ea7SKumar Gala 	src = mfspr(SPRN_EPR);
1856d91e4ea7SKumar Gala 
1857d91e4ea7SKumar Gala 	if (unlikely(src == mpic->spurious_vec)) {
1858d91e4ea7SKumar Gala 		if (mpic->flags & MPIC_SPV_EOI)
1859d91e4ea7SKumar Gala 			mpic_eoi(mpic);
1860d91e4ea7SKumar Gala 		return NO_IRQ;
1861d91e4ea7SKumar Gala 	}
1862d91e4ea7SKumar Gala 	if (unlikely(mpic->protected && test_bit(src, mpic->protected))) {
186376462232SChristian Dietrich 		printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n",
1864d91e4ea7SKumar Gala 				   mpic->name, (int)src);
1865d91e4ea7SKumar Gala 		return NO_IRQ;
1866d91e4ea7SKumar Gala 	}
1867d91e4ea7SKumar Gala 
1868d91e4ea7SKumar Gala 	return irq_linear_revmap(mpic->irqhost, src);
1869d91e4ea7SKumar Gala #else
1870d91e4ea7SKumar Gala 	return NO_IRQ;
1871d91e4ea7SKumar Gala #endif
1872d91e4ea7SKumar Gala }
1873d91e4ea7SKumar Gala 
1874f365355eSOlof Johansson unsigned int mpic_get_mcirq(void)
1875f365355eSOlof Johansson {
1876f365355eSOlof Johansson 	struct mpic *mpic = mpic_primary;
1877f365355eSOlof Johansson 
1878f365355eSOlof Johansson 	BUG_ON(mpic == NULL);
1879f365355eSOlof Johansson 
1880f365355eSOlof Johansson 	return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_MCACK));
1881f365355eSOlof Johansson }
188214cf11afSPaul Mackerras 
188314cf11afSPaul Mackerras #ifdef CONFIG_SMP
188414cf11afSPaul Mackerras void mpic_request_ipis(void)
188514cf11afSPaul Mackerras {
188614cf11afSPaul Mackerras 	struct mpic *mpic = mpic_primary;
188778608dd3SMilton Miller 	int i;
188814cf11afSPaul Mackerras 	BUG_ON(mpic == NULL);
188914cf11afSPaul Mackerras 
18900ebfff14SBenjamin Herrenschmidt 	printk(KERN_INFO "mpic: requesting IPIs...\n");
189114cf11afSPaul Mackerras 
18920ebfff14SBenjamin Herrenschmidt 	for (i = 0; i < 4; i++) {
18930ebfff14SBenjamin Herrenschmidt 		unsigned int vipi = irq_create_mapping(mpic->irqhost,
18947df2457dSOlof Johansson 						       mpic->ipi_vecs[0] + i);
18950ebfff14SBenjamin Herrenschmidt 		if (vipi == NO_IRQ) {
189678608dd3SMilton Miller 			printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]);
189778608dd3SMilton Miller 			continue;
18980ebfff14SBenjamin Herrenschmidt 		}
189978608dd3SMilton Miller 		smp_request_message_ipi(vipi, i);
19000ebfff14SBenjamin Herrenschmidt 	}
190114cf11afSPaul Mackerras }
1902a9c59264SPaul Mackerras 
19033caba98fSMilton Miller void smp_mpic_message_pass(int cpu, int msg)
19042ef613cbSBenjamin Herrenschmidt {
19052ef613cbSBenjamin Herrenschmidt 	struct mpic *mpic = mpic_primary;
19063caba98fSMilton Miller 	u32 physmask;
19072ef613cbSBenjamin Herrenschmidt 
19082ef613cbSBenjamin Herrenschmidt 	BUG_ON(mpic == NULL);
19092ef613cbSBenjamin Herrenschmidt 
1910a9c59264SPaul Mackerras 	/* make sure we're sending something that translates to an IPI */
1911a9c59264SPaul Mackerras 	if ((unsigned int)msg > 3) {
1912a9c59264SPaul Mackerras 		printk("SMP %d: smp_message_pass: unknown msg %d\n",
1913a9c59264SPaul Mackerras 		       smp_processor_id(), msg);
1914a9c59264SPaul Mackerras 		return;
1915a9c59264SPaul Mackerras 	}
19163caba98fSMilton Miller 
19173caba98fSMilton Miller #ifdef DEBUG_IPI
19183caba98fSMilton Miller 	DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, msg);
19193caba98fSMilton Miller #endif
19203caba98fSMilton Miller 
19213caba98fSMilton Miller 	physmask = 1 << get_hard_smp_processor_id(cpu);
19223caba98fSMilton Miller 
19233caba98fSMilton Miller 	mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) +
19243caba98fSMilton Miller 		       msg * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), physmask);
1925a9c59264SPaul Mackerras }
1926775aeff4SMichael Ellerman 
1927775aeff4SMichael Ellerman int __init smp_mpic_probe(void)
1928775aeff4SMichael Ellerman {
1929775aeff4SMichael Ellerman 	int nr_cpus;
1930775aeff4SMichael Ellerman 
1931775aeff4SMichael Ellerman 	DBG("smp_mpic_probe()...\n");
1932775aeff4SMichael Ellerman 
19332ef613cbSBenjamin Herrenschmidt 	nr_cpus = cpumask_weight(cpu_possible_mask);
1934775aeff4SMichael Ellerman 
1935775aeff4SMichael Ellerman 	DBG("nr_cpus: %d\n", nr_cpus);
1936775aeff4SMichael Ellerman 
1937775aeff4SMichael Ellerman 	if (nr_cpus > 1)
1938775aeff4SMichael Ellerman 		mpic_request_ipis();
1939775aeff4SMichael Ellerman 
1940775aeff4SMichael Ellerman 	return nr_cpus;
1941775aeff4SMichael Ellerman }
1942775aeff4SMichael Ellerman 
1943cad5cef6SGreg Kroah-Hartman void smp_mpic_setup_cpu(int cpu)
1944775aeff4SMichael Ellerman {
1945775aeff4SMichael Ellerman 	mpic_setup_this_cpu();
1946775aeff4SMichael Ellerman }
194766953ebeSMatthew McClintock 
194866953ebeSMatthew McClintock void mpic_reset_core(int cpu)
194966953ebeSMatthew McClintock {
195066953ebeSMatthew McClintock 	struct mpic *mpic = mpic_primary;
195166953ebeSMatthew McClintock 	u32 pir;
195266953ebeSMatthew McClintock 	int cpuid = get_hard_smp_processor_id(cpu);
195344f16fcfSMatthew McClintock 	int i;
195466953ebeSMatthew McClintock 
195566953ebeSMatthew McClintock 	/* Set target bit for core reset */
195666953ebeSMatthew McClintock 	pir = mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT));
195766953ebeSMatthew McClintock 	pir |= (1 << cpuid);
195866953ebeSMatthew McClintock 	mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir);
195966953ebeSMatthew McClintock 	mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT));
196066953ebeSMatthew McClintock 
196166953ebeSMatthew McClintock 	/* Restore target bit after reset complete */
196266953ebeSMatthew McClintock 	pir &= ~(1 << cpuid);
196366953ebeSMatthew McClintock 	mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir);
196466953ebeSMatthew McClintock 	mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT));
196544f16fcfSMatthew McClintock 
196644f16fcfSMatthew McClintock 	/* Perform 15 EOI on each reset core to clear pending interrupts.
196744f16fcfSMatthew McClintock 	 * This is required for FSL CoreNet based devices */
196844f16fcfSMatthew McClintock 	if (mpic->flags & MPIC_FSL) {
196944f16fcfSMatthew McClintock 		for (i = 0; i < 15; i++) {
197044f16fcfSMatthew McClintock 			_mpic_write(mpic->reg_type, &mpic->cpuregs[cpuid],
197144f16fcfSMatthew McClintock 				      MPIC_CPU_EOI, 0);
197244f16fcfSMatthew McClintock 		}
197344f16fcfSMatthew McClintock 	}
197466953ebeSMatthew McClintock }
197514cf11afSPaul Mackerras #endif /* CONFIG_SMP */
19763669e930SJohannes Berg 
19773669e930SJohannes Berg #ifdef CONFIG_PM
1978f5a592f7SRafael J. Wysocki static void mpic_suspend_one(struct mpic *mpic)
19793669e930SJohannes Berg {
19803669e930SJohannes Berg 	int i;
19813669e930SJohannes Berg 
19823669e930SJohannes Berg 	for (i = 0; i < mpic->num_sources; i++) {
19833669e930SJohannes Berg 		mpic->save_data[i].vecprio =
19843669e930SJohannes Berg 			mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI));
19853669e930SJohannes Berg 		mpic->save_data[i].dest =
19863669e930SJohannes Berg 			mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION));
19873669e930SJohannes Berg 	}
1988f5a592f7SRafael J. Wysocki }
1989f5a592f7SRafael J. Wysocki 
1990f5a592f7SRafael J. Wysocki static int mpic_suspend(void)
1991f5a592f7SRafael J. Wysocki {
1992f5a592f7SRafael J. Wysocki 	struct mpic *mpic = mpics;
1993f5a592f7SRafael J. Wysocki 
1994f5a592f7SRafael J. Wysocki 	while (mpic) {
1995f5a592f7SRafael J. Wysocki 		mpic_suspend_one(mpic);
1996f5a592f7SRafael J. Wysocki 		mpic = mpic->next;
1997f5a592f7SRafael J. Wysocki 	}
19983669e930SJohannes Berg 
19993669e930SJohannes Berg 	return 0;
20003669e930SJohannes Berg }
20013669e930SJohannes Berg 
2002f5a592f7SRafael J. Wysocki static void mpic_resume_one(struct mpic *mpic)
20033669e930SJohannes Berg {
20043669e930SJohannes Berg 	int i;
20053669e930SJohannes Berg 
20063669e930SJohannes Berg 	for (i = 0; i < mpic->num_sources; i++) {
20073669e930SJohannes Berg 		mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI),
20083669e930SJohannes Berg 			       mpic->save_data[i].vecprio);
20093669e930SJohannes Berg 		mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION),
20103669e930SJohannes Berg 			       mpic->save_data[i].dest);
20113669e930SJohannes Berg 
20123669e930SJohannes Berg #ifdef CONFIG_MPIC_U3_HT_IRQS
20137c9d9360SAlastair Bridgewater 	if (mpic->fixups) {
20143669e930SJohannes Berg 		struct mpic_irq_fixup *fixup = &mpic->fixups[i];
20153669e930SJohannes Berg 
20163669e930SJohannes Berg 		if (fixup->base) {
20173669e930SJohannes Berg 			/* we use the lowest bit in an inverted meaning */
20183669e930SJohannes Berg 			if ((mpic->save_data[i].fixup_data & 1) == 0)
20193669e930SJohannes Berg 				continue;
20203669e930SJohannes Berg 
20213669e930SJohannes Berg 			/* Enable and configure */
20223669e930SJohannes Berg 			writeb(0x10 + 2 * fixup->index, fixup->base + 2);
20233669e930SJohannes Berg 
20243669e930SJohannes Berg 			writel(mpic->save_data[i].fixup_data & ~1,
20253669e930SJohannes Berg 			       fixup->base + 4);
20263669e930SJohannes Berg 		}
20273669e930SJohannes Berg 	}
20283669e930SJohannes Berg #endif
20293669e930SJohannes Berg 	} /* end for loop */
20303669e930SJohannes Berg }
20313669e930SJohannes Berg 
2032f5a592f7SRafael J. Wysocki static void mpic_resume(void)
2033f5a592f7SRafael J. Wysocki {
2034f5a592f7SRafael J. Wysocki 	struct mpic *mpic = mpics;
2035f5a592f7SRafael J. Wysocki 
2036f5a592f7SRafael J. Wysocki 	while (mpic) {
2037f5a592f7SRafael J. Wysocki 		mpic_resume_one(mpic);
2038f5a592f7SRafael J. Wysocki 		mpic = mpic->next;
2039f5a592f7SRafael J. Wysocki 	}
2040f5a592f7SRafael J. Wysocki }
2041f5a592f7SRafael J. Wysocki 
2042f5a592f7SRafael J. Wysocki static struct syscore_ops mpic_syscore_ops = {
20433669e930SJohannes Berg 	.resume = mpic_resume,
20443669e930SJohannes Berg 	.suspend = mpic_suspend,
20453669e930SJohannes Berg };
20463669e930SJohannes Berg 
20473669e930SJohannes Berg static int mpic_init_sys(void)
20483669e930SJohannes Berg {
2049f5a592f7SRafael J. Wysocki 	register_syscore_ops(&mpic_syscore_ops);
20509e6f31a9SDongsheng.wang@freescale.com 	subsys_system_register(&mpic_subsys, NULL);
20519e6f31a9SDongsheng.wang@freescale.com 
2052f5a592f7SRafael J. Wysocki 	return 0;
20533669e930SJohannes Berg }
20543669e930SJohannes Berg 
20553669e930SJohannes Berg device_initcall(mpic_init_sys);
2056f5a592f7SRafael J. Wysocki #endif
2057