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. 922d168ceSScott Wood * Copyright 2010-2011 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 5114cf11afSPaul Mackerras static struct mpic *mpics; 5214cf11afSPaul Mackerras static struct mpic *mpic_primary; 53203041adSThomas Gleixner static DEFINE_RAW_SPINLOCK(mpic_lock); 5414cf11afSPaul Mackerras 55c0c0d996SPaul Mackerras #ifdef CONFIG_PPC32 /* XXX for now */ 56e40c7f02SAndy Whitcroft #ifdef CONFIG_IRQ_ALL_CPUS 57e40c7f02SAndy Whitcroft #define distribute_irqs (1) 58e40c7f02SAndy Whitcroft #else 59e40c7f02SAndy Whitcroft #define distribute_irqs (0) 60e40c7f02SAndy Whitcroft #endif 61c0c0d996SPaul Mackerras #endif 6214cf11afSPaul Mackerras 637233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD 647233593bSZang Roy-r61911 static u32 mpic_infos[][MPIC_IDX_END] = { 657233593bSZang Roy-r61911 [0] = { /* Original OpenPIC compatible MPIC */ 667233593bSZang Roy-r61911 MPIC_GREG_BASE, 677233593bSZang Roy-r61911 MPIC_GREG_FEATURE_0, 687233593bSZang Roy-r61911 MPIC_GREG_GLOBAL_CONF_0, 697233593bSZang Roy-r61911 MPIC_GREG_VENDOR_ID, 707233593bSZang Roy-r61911 MPIC_GREG_IPI_VECTOR_PRI_0, 717233593bSZang Roy-r61911 MPIC_GREG_IPI_STRIDE, 727233593bSZang Roy-r61911 MPIC_GREG_SPURIOUS, 737233593bSZang Roy-r61911 MPIC_GREG_TIMER_FREQ, 747233593bSZang Roy-r61911 757233593bSZang Roy-r61911 MPIC_TIMER_BASE, 767233593bSZang Roy-r61911 MPIC_TIMER_STRIDE, 777233593bSZang Roy-r61911 MPIC_TIMER_CURRENT_CNT, 787233593bSZang Roy-r61911 MPIC_TIMER_BASE_CNT, 797233593bSZang Roy-r61911 MPIC_TIMER_VECTOR_PRI, 807233593bSZang Roy-r61911 MPIC_TIMER_DESTINATION, 817233593bSZang Roy-r61911 827233593bSZang Roy-r61911 MPIC_CPU_BASE, 837233593bSZang Roy-r61911 MPIC_CPU_STRIDE, 847233593bSZang Roy-r61911 MPIC_CPU_IPI_DISPATCH_0, 857233593bSZang Roy-r61911 MPIC_CPU_IPI_DISPATCH_STRIDE, 867233593bSZang Roy-r61911 MPIC_CPU_CURRENT_TASK_PRI, 877233593bSZang Roy-r61911 MPIC_CPU_WHOAMI, 887233593bSZang Roy-r61911 MPIC_CPU_INTACK, 897233593bSZang Roy-r61911 MPIC_CPU_EOI, 90f365355eSOlof Johansson MPIC_CPU_MCACK, 917233593bSZang Roy-r61911 927233593bSZang Roy-r61911 MPIC_IRQ_BASE, 937233593bSZang Roy-r61911 MPIC_IRQ_STRIDE, 947233593bSZang Roy-r61911 MPIC_IRQ_VECTOR_PRI, 957233593bSZang Roy-r61911 MPIC_VECPRI_VECTOR_MASK, 967233593bSZang Roy-r61911 MPIC_VECPRI_POLARITY_POSITIVE, 977233593bSZang Roy-r61911 MPIC_VECPRI_POLARITY_NEGATIVE, 987233593bSZang Roy-r61911 MPIC_VECPRI_SENSE_LEVEL, 997233593bSZang Roy-r61911 MPIC_VECPRI_SENSE_EDGE, 1007233593bSZang Roy-r61911 MPIC_VECPRI_POLARITY_MASK, 1017233593bSZang Roy-r61911 MPIC_VECPRI_SENSE_MASK, 1027233593bSZang Roy-r61911 MPIC_IRQ_DESTINATION 1037233593bSZang Roy-r61911 }, 1047233593bSZang Roy-r61911 [1] = { /* Tsi108/109 PIC */ 1057233593bSZang Roy-r61911 TSI108_GREG_BASE, 1067233593bSZang Roy-r61911 TSI108_GREG_FEATURE_0, 1077233593bSZang Roy-r61911 TSI108_GREG_GLOBAL_CONF_0, 1087233593bSZang Roy-r61911 TSI108_GREG_VENDOR_ID, 1097233593bSZang Roy-r61911 TSI108_GREG_IPI_VECTOR_PRI_0, 1107233593bSZang Roy-r61911 TSI108_GREG_IPI_STRIDE, 1117233593bSZang Roy-r61911 TSI108_GREG_SPURIOUS, 1127233593bSZang Roy-r61911 TSI108_GREG_TIMER_FREQ, 1137233593bSZang Roy-r61911 1147233593bSZang Roy-r61911 TSI108_TIMER_BASE, 1157233593bSZang Roy-r61911 TSI108_TIMER_STRIDE, 1167233593bSZang Roy-r61911 TSI108_TIMER_CURRENT_CNT, 1177233593bSZang Roy-r61911 TSI108_TIMER_BASE_CNT, 1187233593bSZang Roy-r61911 TSI108_TIMER_VECTOR_PRI, 1197233593bSZang Roy-r61911 TSI108_TIMER_DESTINATION, 1207233593bSZang Roy-r61911 1217233593bSZang Roy-r61911 TSI108_CPU_BASE, 1227233593bSZang Roy-r61911 TSI108_CPU_STRIDE, 1237233593bSZang Roy-r61911 TSI108_CPU_IPI_DISPATCH_0, 1247233593bSZang Roy-r61911 TSI108_CPU_IPI_DISPATCH_STRIDE, 1257233593bSZang Roy-r61911 TSI108_CPU_CURRENT_TASK_PRI, 1267233593bSZang Roy-r61911 TSI108_CPU_WHOAMI, 1277233593bSZang Roy-r61911 TSI108_CPU_INTACK, 1287233593bSZang Roy-r61911 TSI108_CPU_EOI, 129f365355eSOlof Johansson TSI108_CPU_MCACK, 1307233593bSZang Roy-r61911 1317233593bSZang Roy-r61911 TSI108_IRQ_BASE, 1327233593bSZang Roy-r61911 TSI108_IRQ_STRIDE, 1337233593bSZang Roy-r61911 TSI108_IRQ_VECTOR_PRI, 1347233593bSZang Roy-r61911 TSI108_VECPRI_VECTOR_MASK, 1357233593bSZang Roy-r61911 TSI108_VECPRI_POLARITY_POSITIVE, 1367233593bSZang Roy-r61911 TSI108_VECPRI_POLARITY_NEGATIVE, 1377233593bSZang Roy-r61911 TSI108_VECPRI_SENSE_LEVEL, 1387233593bSZang Roy-r61911 TSI108_VECPRI_SENSE_EDGE, 1397233593bSZang Roy-r61911 TSI108_VECPRI_POLARITY_MASK, 1407233593bSZang Roy-r61911 TSI108_VECPRI_SENSE_MASK, 1417233593bSZang Roy-r61911 TSI108_IRQ_DESTINATION 1427233593bSZang Roy-r61911 }, 1437233593bSZang Roy-r61911 }; 1447233593bSZang Roy-r61911 1457233593bSZang Roy-r61911 #define MPIC_INFO(name) mpic->hw_set[MPIC_IDX_##name] 1467233593bSZang Roy-r61911 1477233593bSZang Roy-r61911 #else /* CONFIG_MPIC_WEIRD */ 1487233593bSZang Roy-r61911 1497233593bSZang Roy-r61911 #define MPIC_INFO(name) MPIC_##name 1507233593bSZang Roy-r61911 1517233593bSZang Roy-r61911 #endif /* CONFIG_MPIC_WEIRD */ 1527233593bSZang Roy-r61911 153d6a2639bSMeador Inge static inline unsigned int mpic_processor_id(struct mpic *mpic) 154d6a2639bSMeador Inge { 155d6a2639bSMeador Inge unsigned int cpu = 0; 156d6a2639bSMeador Inge 157be8bec56SKyle Moffett if (!(mpic->flags & MPIC_SECONDARY)) 158d6a2639bSMeador Inge cpu = hard_smp_processor_id(); 159d6a2639bSMeador Inge 160d6a2639bSMeador Inge return cpu; 161d6a2639bSMeador Inge } 162d6a2639bSMeador Inge 16314cf11afSPaul Mackerras /* 16414cf11afSPaul Mackerras * Register accessor functions 16514cf11afSPaul Mackerras */ 16614cf11afSPaul Mackerras 16714cf11afSPaul Mackerras 168fbf0274eSBenjamin Herrenschmidt static inline u32 _mpic_read(enum mpic_reg_type type, 169fbf0274eSBenjamin Herrenschmidt struct mpic_reg_bank *rb, 17014cf11afSPaul Mackerras unsigned int reg) 17114cf11afSPaul Mackerras { 172fbf0274eSBenjamin Herrenschmidt switch(type) { 173fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 174fbf0274eSBenjamin Herrenschmidt case mpic_access_dcr: 17583f34df4SMichael Ellerman return dcr_read(rb->dhost, reg); 176fbf0274eSBenjamin Herrenschmidt #endif 177fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_be: 178fbf0274eSBenjamin Herrenschmidt return in_be32(rb->base + (reg >> 2)); 179fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_le: 180fbf0274eSBenjamin Herrenschmidt default: 181fbf0274eSBenjamin Herrenschmidt return in_le32(rb->base + (reg >> 2)); 182fbf0274eSBenjamin Herrenschmidt } 18314cf11afSPaul Mackerras } 18414cf11afSPaul Mackerras 185fbf0274eSBenjamin Herrenschmidt static inline void _mpic_write(enum mpic_reg_type type, 186fbf0274eSBenjamin Herrenschmidt struct mpic_reg_bank *rb, 18714cf11afSPaul Mackerras unsigned int reg, u32 value) 18814cf11afSPaul Mackerras { 189fbf0274eSBenjamin Herrenschmidt switch(type) { 190fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 191fbf0274eSBenjamin Herrenschmidt case mpic_access_dcr: 192d9d1063dSJohannes Berg dcr_write(rb->dhost, reg, value); 193d9d1063dSJohannes Berg break; 194fbf0274eSBenjamin Herrenschmidt #endif 195fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_be: 196d9d1063dSJohannes Berg out_be32(rb->base + (reg >> 2), value); 197d9d1063dSJohannes Berg break; 198fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_le: 199fbf0274eSBenjamin Herrenschmidt default: 200d9d1063dSJohannes Berg out_le32(rb->base + (reg >> 2), value); 201d9d1063dSJohannes Berg break; 202fbf0274eSBenjamin Herrenschmidt } 20314cf11afSPaul Mackerras } 20414cf11afSPaul Mackerras 20514cf11afSPaul Mackerras static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi) 20614cf11afSPaul Mackerras { 207fbf0274eSBenjamin Herrenschmidt enum mpic_reg_type type = mpic->reg_type; 2087233593bSZang Roy-r61911 unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 2097233593bSZang Roy-r61911 (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 21014cf11afSPaul Mackerras 211fbf0274eSBenjamin Herrenschmidt if ((mpic->flags & MPIC_BROKEN_IPI) && type == mpic_access_mmio_le) 212fbf0274eSBenjamin Herrenschmidt type = mpic_access_mmio_be; 213fbf0274eSBenjamin Herrenschmidt return _mpic_read(type, &mpic->gregs, offset); 21414cf11afSPaul Mackerras } 21514cf11afSPaul Mackerras 21614cf11afSPaul Mackerras static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value) 21714cf11afSPaul Mackerras { 2187233593bSZang Roy-r61911 unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 2197233593bSZang Roy-r61911 (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 22014cf11afSPaul Mackerras 221fbf0274eSBenjamin Herrenschmidt _mpic_write(mpic->reg_type, &mpic->gregs, offset, value); 22214cf11afSPaul Mackerras } 22314cf11afSPaul Mackerras 224ea94187fSScott Wood static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm) 225ea94187fSScott Wood { 226ea94187fSScott Wood unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) + 227ea94187fSScott Wood ((tm & 3) * MPIC_INFO(TIMER_STRIDE)); 228ea94187fSScott Wood 229ea94187fSScott Wood if (tm >= 4) 230ea94187fSScott Wood offset += 0x1000 / 4; 231ea94187fSScott Wood 232ea94187fSScott Wood return _mpic_read(mpic->reg_type, &mpic->tmregs, offset); 233ea94187fSScott Wood } 234ea94187fSScott Wood 235ea94187fSScott Wood static inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value) 236ea94187fSScott Wood { 237ea94187fSScott Wood unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) + 238ea94187fSScott Wood ((tm & 3) * MPIC_INFO(TIMER_STRIDE)); 239ea94187fSScott Wood 240ea94187fSScott Wood if (tm >= 4) 241ea94187fSScott Wood offset += 0x1000 / 4; 242ea94187fSScott Wood 243ea94187fSScott Wood _mpic_write(mpic->reg_type, &mpic->tmregs, offset, value); 244ea94187fSScott Wood } 245ea94187fSScott Wood 24614cf11afSPaul Mackerras static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) 24714cf11afSPaul Mackerras { 248d6a2639bSMeador Inge unsigned int cpu = mpic_processor_id(mpic); 24914cf11afSPaul Mackerras 250fbf0274eSBenjamin Herrenschmidt return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg); 25114cf11afSPaul Mackerras } 25214cf11afSPaul Mackerras 25314cf11afSPaul Mackerras static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value) 25414cf11afSPaul Mackerras { 255d6a2639bSMeador Inge unsigned int cpu = mpic_processor_id(mpic); 25614cf11afSPaul Mackerras 257fbf0274eSBenjamin Herrenschmidt _mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value); 25814cf11afSPaul Mackerras } 25914cf11afSPaul Mackerras 26014cf11afSPaul Mackerras static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg) 26114cf11afSPaul Mackerras { 26214cf11afSPaul Mackerras unsigned int isu = src_no >> mpic->isu_shift; 26314cf11afSPaul Mackerras unsigned int idx = src_no & mpic->isu_mask; 26411a6b292SMichael Ellerman unsigned int val; 26514cf11afSPaul Mackerras 26611a6b292SMichael Ellerman val = _mpic_read(mpic->reg_type, &mpic->isus[isu], 26711a6b292SMichael Ellerman reg + (idx * MPIC_INFO(IRQ_STRIDE))); 2680d72ba93SOlof Johansson #ifdef CONFIG_MPIC_BROKEN_REGREAD 2690d72ba93SOlof Johansson if (reg == 0) 27011a6b292SMichael Ellerman val = (val & (MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY)) | 27111a6b292SMichael Ellerman mpic->isu_reg0_shadow[src_no]; 2720d72ba93SOlof Johansson #endif 27311a6b292SMichael Ellerman return val; 27414cf11afSPaul Mackerras } 27514cf11afSPaul Mackerras 27614cf11afSPaul Mackerras static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, 27714cf11afSPaul Mackerras unsigned int reg, u32 value) 27814cf11afSPaul Mackerras { 27914cf11afSPaul Mackerras unsigned int isu = src_no >> mpic->isu_shift; 28014cf11afSPaul Mackerras unsigned int idx = src_no & mpic->isu_mask; 28114cf11afSPaul Mackerras 282fbf0274eSBenjamin Herrenschmidt _mpic_write(mpic->reg_type, &mpic->isus[isu], 2837233593bSZang Roy-r61911 reg + (idx * MPIC_INFO(IRQ_STRIDE)), value); 2840d72ba93SOlof Johansson 2850d72ba93SOlof Johansson #ifdef CONFIG_MPIC_BROKEN_REGREAD 2860d72ba93SOlof Johansson if (reg == 0) 28711a6b292SMichael Ellerman mpic->isu_reg0_shadow[src_no] = 28811a6b292SMichael Ellerman value & ~(MPIC_VECPRI_MASK | MPIC_VECPRI_ACTIVITY); 2890d72ba93SOlof Johansson #endif 29014cf11afSPaul Mackerras } 29114cf11afSPaul Mackerras 292fbf0274eSBenjamin Herrenschmidt #define mpic_read(b,r) _mpic_read(mpic->reg_type,&(b),(r)) 293fbf0274eSBenjamin Herrenschmidt #define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v)) 29414cf11afSPaul Mackerras #define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) 29514cf11afSPaul Mackerras #define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) 296ea94187fSScott Wood #define mpic_tm_read(i) _mpic_tm_read(mpic,(i)) 297ea94187fSScott Wood #define mpic_tm_write(i,v) _mpic_tm_write(mpic,(i),(v)) 29814cf11afSPaul Mackerras #define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) 29914cf11afSPaul Mackerras #define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v)) 30014cf11afSPaul Mackerras #define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r)) 30114cf11afSPaul Mackerras #define mpic_irq_write(s,r,v) _mpic_irq_write(mpic,(s),(r),(v)) 30214cf11afSPaul Mackerras 30314cf11afSPaul Mackerras 30414cf11afSPaul Mackerras /* 30514cf11afSPaul Mackerras * Low level utility functions 30614cf11afSPaul Mackerras */ 30714cf11afSPaul Mackerras 30814cf11afSPaul Mackerras 309c51a3fdcSBecky Bruce static void _mpic_map_mmio(struct mpic *mpic, phys_addr_t phys_addr, 310fbf0274eSBenjamin Herrenschmidt struct mpic_reg_bank *rb, unsigned int offset, 311fbf0274eSBenjamin Herrenschmidt unsigned int size) 312fbf0274eSBenjamin Herrenschmidt { 313fbf0274eSBenjamin Herrenschmidt rb->base = ioremap(phys_addr + offset, size); 314fbf0274eSBenjamin Herrenschmidt BUG_ON(rb->base == NULL); 315fbf0274eSBenjamin Herrenschmidt } 316fbf0274eSBenjamin Herrenschmidt 317fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 318c51242e7SKyle Moffett static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb, 319fbf0274eSBenjamin Herrenschmidt unsigned int offset, unsigned int size) 320fbf0274eSBenjamin Herrenschmidt { 321c51242e7SKyle Moffett phys_addr_t phys_addr = dcr_resource_start(mpic->node, 0); 322e62b7601SKyle Moffett rb->dhost = dcr_map(mpic->node, phys_addr + offset, size); 323fbf0274eSBenjamin Herrenschmidt BUG_ON(!DCR_MAP_OK(rb->dhost)); 324fbf0274eSBenjamin Herrenschmidt } 325fbf0274eSBenjamin Herrenschmidt 326c51242e7SKyle Moffett static inline void mpic_map(struct mpic *mpic, 3275a2642f6SBenjamin Herrenschmidt phys_addr_t phys_addr, struct mpic_reg_bank *rb, 3285a2642f6SBenjamin Herrenschmidt unsigned int offset, unsigned int size) 329fbf0274eSBenjamin Herrenschmidt { 330fbf0274eSBenjamin Herrenschmidt if (mpic->flags & MPIC_USES_DCR) 331c51242e7SKyle Moffett _mpic_map_dcr(mpic, rb, offset, size); 332fbf0274eSBenjamin Herrenschmidt else 333fbf0274eSBenjamin Herrenschmidt _mpic_map_mmio(mpic, phys_addr, rb, offset, size); 334fbf0274eSBenjamin Herrenschmidt } 335fbf0274eSBenjamin Herrenschmidt #else /* CONFIG_PPC_DCR */ 336c51242e7SKyle Moffett #define mpic_map(m,p,b,o,s) _mpic_map_mmio(m,p,b,o,s) 337fbf0274eSBenjamin Herrenschmidt #endif /* !CONFIG_PPC_DCR */ 338fbf0274eSBenjamin Herrenschmidt 339fbf0274eSBenjamin Herrenschmidt 34014cf11afSPaul Mackerras 34114cf11afSPaul Mackerras /* Check if we have one of those nice broken MPICs with a flipped endian on 34214cf11afSPaul Mackerras * reads from IPI registers 34314cf11afSPaul Mackerras */ 34414cf11afSPaul Mackerras static void __init mpic_test_broken_ipi(struct mpic *mpic) 34514cf11afSPaul Mackerras { 34614cf11afSPaul Mackerras u32 r; 34714cf11afSPaul Mackerras 3487233593bSZang Roy-r61911 mpic_write(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0), MPIC_VECPRI_MASK); 3497233593bSZang Roy-r61911 r = mpic_read(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0)); 35014cf11afSPaul Mackerras 35114cf11afSPaul Mackerras if (r == le32_to_cpu(MPIC_VECPRI_MASK)) { 35214cf11afSPaul Mackerras printk(KERN_INFO "mpic: Detected reversed IPI registers\n"); 35314cf11afSPaul Mackerras mpic->flags |= MPIC_BROKEN_IPI; 35414cf11afSPaul Mackerras } 35514cf11afSPaul Mackerras } 35614cf11afSPaul Mackerras 3576cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 35814cf11afSPaul Mackerras 35914cf11afSPaul Mackerras /* Test if an interrupt is sourced from HyperTransport (used on broken U3s) 36014cf11afSPaul Mackerras * to force the edge setting on the MPIC and do the ack workaround. 36114cf11afSPaul Mackerras */ 3621beb6a7dSBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 36314cf11afSPaul Mackerras { 3641beb6a7dSBenjamin Herrenschmidt if (source >= 128 || !mpic->fixups) 36514cf11afSPaul Mackerras return 0; 3661beb6a7dSBenjamin Herrenschmidt return mpic->fixups[source].base != NULL; 36714cf11afSPaul Mackerras } 36814cf11afSPaul Mackerras 369c4b22f26SSegher Boessenkool 3701beb6a7dSBenjamin Herrenschmidt static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source) 37114cf11afSPaul Mackerras { 3721beb6a7dSBenjamin Herrenschmidt struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 37314cf11afSPaul Mackerras 3741beb6a7dSBenjamin Herrenschmidt if (fixup->applebase) { 3751beb6a7dSBenjamin Herrenschmidt unsigned int soff = (fixup->index >> 3) & ~3; 3761beb6a7dSBenjamin Herrenschmidt unsigned int mask = 1U << (fixup->index & 0x1f); 3771beb6a7dSBenjamin Herrenschmidt writel(mask, fixup->applebase + soff); 3781beb6a7dSBenjamin Herrenschmidt } else { 379203041adSThomas Gleixner raw_spin_lock(&mpic->fixup_lock); 3801beb6a7dSBenjamin Herrenschmidt writeb(0x11 + 2 * fixup->index, fixup->base + 2); 381c4b22f26SSegher Boessenkool writel(fixup->data, fixup->base + 4); 382203041adSThomas Gleixner raw_spin_unlock(&mpic->fixup_lock); 38314cf11afSPaul Mackerras } 3841beb6a7dSBenjamin Herrenschmidt } 38514cf11afSPaul Mackerras 3861beb6a7dSBenjamin Herrenschmidt static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, 38724a3f2e8SThomas Gleixner bool level) 3881beb6a7dSBenjamin Herrenschmidt { 3891beb6a7dSBenjamin Herrenschmidt struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 3901beb6a7dSBenjamin Herrenschmidt unsigned long flags; 3911beb6a7dSBenjamin Herrenschmidt u32 tmp; 39214cf11afSPaul Mackerras 3931beb6a7dSBenjamin Herrenschmidt if (fixup->base == NULL) 3941beb6a7dSBenjamin Herrenschmidt return; 3951beb6a7dSBenjamin Herrenschmidt 39624a3f2e8SThomas Gleixner DBG("startup_ht_interrupt(0x%x) index: %d\n", 39724a3f2e8SThomas Gleixner source, fixup->index); 398203041adSThomas Gleixner raw_spin_lock_irqsave(&mpic->fixup_lock, flags); 3991beb6a7dSBenjamin Herrenschmidt /* Enable and configure */ 4001beb6a7dSBenjamin Herrenschmidt writeb(0x10 + 2 * fixup->index, fixup->base + 2); 4011beb6a7dSBenjamin Herrenschmidt tmp = readl(fixup->base + 4); 4021beb6a7dSBenjamin Herrenschmidt tmp &= ~(0x23U); 40324a3f2e8SThomas Gleixner if (level) 4041beb6a7dSBenjamin Herrenschmidt tmp |= 0x22; 4051beb6a7dSBenjamin Herrenschmidt writel(tmp, fixup->base + 4); 406203041adSThomas Gleixner raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags); 4073669e930SJohannes Berg 4083669e930SJohannes Berg #ifdef CONFIG_PM 4093669e930SJohannes Berg /* use the lowest bit inverted to the actual HW, 4103669e930SJohannes Berg * set if this fixup was enabled, clear otherwise */ 4113669e930SJohannes Berg mpic->save_data[source].fixup_data = tmp | 1; 4123669e930SJohannes Berg #endif 4131beb6a7dSBenjamin Herrenschmidt } 4141beb6a7dSBenjamin Herrenschmidt 41524a3f2e8SThomas Gleixner static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source) 4161beb6a7dSBenjamin Herrenschmidt { 4171beb6a7dSBenjamin Herrenschmidt struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 4181beb6a7dSBenjamin Herrenschmidt unsigned long flags; 4191beb6a7dSBenjamin Herrenschmidt u32 tmp; 4201beb6a7dSBenjamin Herrenschmidt 4211beb6a7dSBenjamin Herrenschmidt if (fixup->base == NULL) 4221beb6a7dSBenjamin Herrenschmidt return; 4231beb6a7dSBenjamin Herrenschmidt 42424a3f2e8SThomas Gleixner DBG("shutdown_ht_interrupt(0x%x)\n", source); 4251beb6a7dSBenjamin Herrenschmidt 4261beb6a7dSBenjamin Herrenschmidt /* Disable */ 427203041adSThomas Gleixner raw_spin_lock_irqsave(&mpic->fixup_lock, flags); 4281beb6a7dSBenjamin Herrenschmidt writeb(0x10 + 2 * fixup->index, fixup->base + 2); 4291beb6a7dSBenjamin Herrenschmidt tmp = readl(fixup->base + 4); 43072b13819SSegher Boessenkool tmp |= 1; 4311beb6a7dSBenjamin Herrenschmidt writel(tmp, fixup->base + 4); 432203041adSThomas Gleixner raw_spin_unlock_irqrestore(&mpic->fixup_lock, flags); 4333669e930SJohannes Berg 4343669e930SJohannes Berg #ifdef CONFIG_PM 4353669e930SJohannes Berg /* use the lowest bit inverted to the actual HW, 4363669e930SJohannes Berg * set if this fixup was enabled, clear otherwise */ 4373669e930SJohannes Berg mpic->save_data[source].fixup_data = tmp & ~1; 4383669e930SJohannes Berg #endif 4391beb6a7dSBenjamin Herrenschmidt } 4401beb6a7dSBenjamin Herrenschmidt 441812fd1fdSMichael Ellerman #ifdef CONFIG_PCI_MSI 442812fd1fdSMichael Ellerman static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 443812fd1fdSMichael Ellerman unsigned int devfn) 444812fd1fdSMichael Ellerman { 445812fd1fdSMichael Ellerman u8 __iomem *base; 446812fd1fdSMichael Ellerman u8 pos, flags; 447812fd1fdSMichael Ellerman u64 addr = 0; 448812fd1fdSMichael Ellerman 449812fd1fdSMichael Ellerman for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 450812fd1fdSMichael Ellerman pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 451812fd1fdSMichael Ellerman u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 452812fd1fdSMichael Ellerman if (id == PCI_CAP_ID_HT) { 453812fd1fdSMichael Ellerman id = readb(devbase + pos + 3); 454812fd1fdSMichael Ellerman if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_MSI_MAPPING) 455812fd1fdSMichael Ellerman break; 456812fd1fdSMichael Ellerman } 457812fd1fdSMichael Ellerman } 458812fd1fdSMichael Ellerman 459812fd1fdSMichael Ellerman if (pos == 0) 460812fd1fdSMichael Ellerman return; 461812fd1fdSMichael Ellerman 462812fd1fdSMichael Ellerman base = devbase + pos; 463812fd1fdSMichael Ellerman 464812fd1fdSMichael Ellerman flags = readb(base + HT_MSI_FLAGS); 465812fd1fdSMichael Ellerman if (!(flags & HT_MSI_FLAGS_FIXED)) { 466812fd1fdSMichael Ellerman addr = readl(base + HT_MSI_ADDR_LO) & HT_MSI_ADDR_LO_MASK; 467812fd1fdSMichael Ellerman addr = addr | ((u64)readl(base + HT_MSI_ADDR_HI) << 32); 468812fd1fdSMichael Ellerman } 469812fd1fdSMichael Ellerman 470fe333321SIngo Molnar printk(KERN_DEBUG "mpic: - HT:%02x.%x %s MSI mapping found @ 0x%llx\n", 471812fd1fdSMichael Ellerman PCI_SLOT(devfn), PCI_FUNC(devfn), 472812fd1fdSMichael Ellerman flags & HT_MSI_FLAGS_ENABLE ? "enabled" : "disabled", addr); 473812fd1fdSMichael Ellerman 474812fd1fdSMichael Ellerman if (!(flags & HT_MSI_FLAGS_ENABLE)) 475812fd1fdSMichael Ellerman writeb(flags | HT_MSI_FLAGS_ENABLE, base + HT_MSI_FLAGS); 476812fd1fdSMichael Ellerman } 477812fd1fdSMichael Ellerman #else 478812fd1fdSMichael Ellerman static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 479812fd1fdSMichael Ellerman unsigned int devfn) 480812fd1fdSMichael Ellerman { 481812fd1fdSMichael Ellerman return; 482812fd1fdSMichael Ellerman } 483812fd1fdSMichael Ellerman #endif 484812fd1fdSMichael Ellerman 4851beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, 4861beb6a7dSBenjamin Herrenschmidt unsigned int devfn, u32 vdid) 48714cf11afSPaul Mackerras { 488c4b22f26SSegher Boessenkool int i, irq, n; 4891beb6a7dSBenjamin Herrenschmidt u8 __iomem *base; 49014cf11afSPaul Mackerras u32 tmp; 491c4b22f26SSegher Boessenkool u8 pos; 49214cf11afSPaul Mackerras 4931beb6a7dSBenjamin Herrenschmidt for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 4941beb6a7dSBenjamin Herrenschmidt pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 4951beb6a7dSBenjamin Herrenschmidt u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 49646ff3463SBrice Goglin if (id == PCI_CAP_ID_HT) { 497c4b22f26SSegher Boessenkool id = readb(devbase + pos + 3); 498beb7cc82SMichael Ellerman if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_IRQ) 499c4b22f26SSegher Boessenkool break; 500c4b22f26SSegher Boessenkool } 501c4b22f26SSegher Boessenkool } 502c4b22f26SSegher Boessenkool if (pos == 0) 503c4b22f26SSegher Boessenkool return; 504c4b22f26SSegher Boessenkool 5051beb6a7dSBenjamin Herrenschmidt base = devbase + pos; 5061beb6a7dSBenjamin Herrenschmidt writeb(0x01, base + 2); 5071beb6a7dSBenjamin Herrenschmidt n = (readl(base + 4) >> 16) & 0xff; 508c4b22f26SSegher Boessenkool 5091beb6a7dSBenjamin Herrenschmidt printk(KERN_INFO "mpic: - HT:%02x.%x [0x%02x] vendor %04x device %04x" 5101beb6a7dSBenjamin Herrenschmidt " has %d irqs\n", 5111beb6a7dSBenjamin Herrenschmidt devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1); 512c4b22f26SSegher Boessenkool 513c4b22f26SSegher Boessenkool for (i = 0; i <= n; i++) { 5141beb6a7dSBenjamin Herrenschmidt writeb(0x10 + 2 * i, base + 2); 5151beb6a7dSBenjamin Herrenschmidt tmp = readl(base + 4); 51614cf11afSPaul Mackerras irq = (tmp >> 16) & 0xff; 5171beb6a7dSBenjamin Herrenschmidt DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp); 5181beb6a7dSBenjamin Herrenschmidt /* mask it , will be unmasked later */ 5191beb6a7dSBenjamin Herrenschmidt tmp |= 0x1; 5201beb6a7dSBenjamin Herrenschmidt writel(tmp, base + 4); 5211beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].index = i; 5221beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].base = base; 5231beb6a7dSBenjamin Herrenschmidt /* Apple HT PIC has a non-standard way of doing EOIs */ 5241beb6a7dSBenjamin Herrenschmidt if ((vdid & 0xffff) == 0x106b) 5251beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].applebase = devbase + 0x60; 5261beb6a7dSBenjamin Herrenschmidt else 5271beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].applebase = NULL; 5281beb6a7dSBenjamin Herrenschmidt writeb(0x11 + 2 * i, base + 2); 5291beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].data = readl(base + 4) | 0x80000000; 53014cf11afSPaul Mackerras } 53114cf11afSPaul Mackerras } 53214cf11afSPaul Mackerras 53314cf11afSPaul Mackerras 5341beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic) 53514cf11afSPaul Mackerras { 53614cf11afSPaul Mackerras unsigned int devfn; 53714cf11afSPaul Mackerras u8 __iomem *cfgspace; 53814cf11afSPaul Mackerras 5391beb6a7dSBenjamin Herrenschmidt printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n"); 54014cf11afSPaul Mackerras 54114cf11afSPaul Mackerras /* Allocate fixups array */ 542ea96025aSAnton Vorontsov mpic->fixups = kzalloc(128 * sizeof(*mpic->fixups), GFP_KERNEL); 54314cf11afSPaul Mackerras BUG_ON(mpic->fixups == NULL); 54414cf11afSPaul Mackerras 54514cf11afSPaul Mackerras /* Init spinlock */ 546203041adSThomas Gleixner raw_spin_lock_init(&mpic->fixup_lock); 54714cf11afSPaul Mackerras 548c4b22f26SSegher Boessenkool /* Map U3 config space. We assume all IO-APICs are on the primary bus 549c4b22f26SSegher Boessenkool * so we only need to map 64kB. 55014cf11afSPaul Mackerras */ 551c4b22f26SSegher Boessenkool cfgspace = ioremap(0xf2000000, 0x10000); 55214cf11afSPaul Mackerras BUG_ON(cfgspace == NULL); 55314cf11afSPaul Mackerras 5541beb6a7dSBenjamin Herrenschmidt /* Now we scan all slots. We do a very quick scan, we read the header 5551beb6a7dSBenjamin Herrenschmidt * type, vendor ID and device ID only, that's plenty enough 55614cf11afSPaul Mackerras */ 557c4b22f26SSegher Boessenkool for (devfn = 0; devfn < 0x100; devfn++) { 55814cf11afSPaul Mackerras u8 __iomem *devbase = cfgspace + (devfn << 8); 55914cf11afSPaul Mackerras u8 hdr_type = readb(devbase + PCI_HEADER_TYPE); 56014cf11afSPaul Mackerras u32 l = readl(devbase + PCI_VENDOR_ID); 5611beb6a7dSBenjamin Herrenschmidt u16 s; 56214cf11afSPaul Mackerras 56314cf11afSPaul Mackerras DBG("devfn %x, l: %x\n", devfn, l); 56414cf11afSPaul Mackerras 56514cf11afSPaul Mackerras /* If no device, skip */ 56614cf11afSPaul Mackerras if (l == 0xffffffff || l == 0x00000000 || 56714cf11afSPaul Mackerras l == 0x0000ffff || l == 0xffff0000) 56814cf11afSPaul Mackerras goto next; 5691beb6a7dSBenjamin Herrenschmidt /* Check if is supports capability lists */ 5701beb6a7dSBenjamin Herrenschmidt s = readw(devbase + PCI_STATUS); 5711beb6a7dSBenjamin Herrenschmidt if (!(s & PCI_STATUS_CAP_LIST)) 5721beb6a7dSBenjamin Herrenschmidt goto next; 57314cf11afSPaul Mackerras 5741beb6a7dSBenjamin Herrenschmidt mpic_scan_ht_pic(mpic, devbase, devfn, l); 575812fd1fdSMichael Ellerman mpic_scan_ht_msi(mpic, devbase, devfn); 57614cf11afSPaul Mackerras 57714cf11afSPaul Mackerras next: 57814cf11afSPaul Mackerras /* next device, if function 0 */ 579c4b22f26SSegher Boessenkool if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0) 58014cf11afSPaul Mackerras devfn += 7; 58114cf11afSPaul Mackerras } 58214cf11afSPaul Mackerras } 58314cf11afSPaul Mackerras 5846cfef5b2SMichael Ellerman #else /* CONFIG_MPIC_U3_HT_IRQS */ 5856e99e458SBenjamin Herrenschmidt 5866e99e458SBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 5876e99e458SBenjamin Herrenschmidt { 5886e99e458SBenjamin Herrenschmidt return 0; 5896e99e458SBenjamin Herrenschmidt } 5906e99e458SBenjamin Herrenschmidt 5916e99e458SBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic) 5926e99e458SBenjamin Herrenschmidt { 5936e99e458SBenjamin Herrenschmidt } 5946e99e458SBenjamin Herrenschmidt 5956cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 59614cf11afSPaul Mackerras 59714cf11afSPaul Mackerras /* Find an mpic associated with a given linux interrupt */ 598d69a78d7STony Breeds static struct mpic *mpic_find(unsigned int irq) 59914cf11afSPaul Mackerras { 6000ebfff14SBenjamin Herrenschmidt if (irq < NUM_ISA_INTERRUPTS) 60114cf11afSPaul Mackerras return NULL; 6020ebfff14SBenjamin Herrenschmidt 603ec775d0eSThomas Gleixner return irq_get_chip_data(irq); 60414cf11afSPaul Mackerras } 60514cf11afSPaul Mackerras 606d69a78d7STony Breeds /* Determine if the linux irq is an IPI */ 6073a2b4f7cSBenjamin Herrenschmidt static unsigned int mpic_is_ipi(struct mpic *mpic, unsigned int src) 608d69a78d7STony Breeds { 609d69a78d7STony Breeds return (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); 610d69a78d7STony Breeds } 611d69a78d7STony Breeds 612ea94187fSScott Wood /* Determine if the linux irq is a timer */ 6133a2b4f7cSBenjamin Herrenschmidt static unsigned int mpic_is_tm(struct mpic *mpic, unsigned int src) 614ea94187fSScott Wood { 615ea94187fSScott Wood return (src >= mpic->timer_vecs[0] && src <= mpic->timer_vecs[7]); 616ea94187fSScott Wood } 617d69a78d7STony Breeds 61814cf11afSPaul Mackerras /* Convert a cpu mask from logical to physical cpu numbers. */ 61914cf11afSPaul Mackerras static inline u32 mpic_physmask(u32 cpumask) 62014cf11afSPaul Mackerras { 62114cf11afSPaul Mackerras int i; 62214cf11afSPaul Mackerras u32 mask = 0; 62314cf11afSPaul Mackerras 624ebc04215SMilton Miller for (i = 0; i < min(32, NR_CPUS); ++i, cpumask >>= 1) 62514cf11afSPaul Mackerras mask |= (cpumask & 1) << get_hard_smp_processor_id(i); 62614cf11afSPaul Mackerras return mask; 62714cf11afSPaul Mackerras } 62814cf11afSPaul Mackerras 62914cf11afSPaul Mackerras #ifdef CONFIG_SMP 63014cf11afSPaul Mackerras /* Get the mpic structure from the IPI number */ 631835c0553SLennert Buytenhek static inline struct mpic * mpic_from_ipi(struct irq_data *d) 63214cf11afSPaul Mackerras { 633835c0553SLennert Buytenhek return irq_data_get_irq_chip_data(d); 63414cf11afSPaul Mackerras } 63514cf11afSPaul Mackerras #endif 63614cf11afSPaul Mackerras 63714cf11afSPaul Mackerras /* Get the mpic structure from the irq number */ 63814cf11afSPaul Mackerras static inline struct mpic * mpic_from_irq(unsigned int irq) 63914cf11afSPaul Mackerras { 640ec775d0eSThomas Gleixner return irq_get_chip_data(irq); 641835c0553SLennert Buytenhek } 642835c0553SLennert Buytenhek 643835c0553SLennert Buytenhek /* Get the mpic structure from the irq data */ 644835c0553SLennert Buytenhek static inline struct mpic * mpic_from_irq_data(struct irq_data *d) 645835c0553SLennert Buytenhek { 646835c0553SLennert Buytenhek return irq_data_get_irq_chip_data(d); 64714cf11afSPaul Mackerras } 64814cf11afSPaul Mackerras 64914cf11afSPaul Mackerras /* Send an EOI */ 65014cf11afSPaul Mackerras static inline void mpic_eoi(struct mpic *mpic) 65114cf11afSPaul Mackerras { 6527233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_EOI), 0); 6537233593bSZang Roy-r61911 (void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI)); 65414cf11afSPaul Mackerras } 65514cf11afSPaul Mackerras 65614cf11afSPaul Mackerras /* 65714cf11afSPaul Mackerras * Linux descriptor level callbacks 65814cf11afSPaul Mackerras */ 65914cf11afSPaul Mackerras 66014cf11afSPaul Mackerras 661835c0553SLennert Buytenhek void mpic_unmask_irq(struct irq_data *d) 66214cf11afSPaul Mackerras { 66314cf11afSPaul Mackerras unsigned int loops = 100000; 664835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 665476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 66614cf11afSPaul Mackerras 667835c0553SLennert Buytenhek DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, d->irq, src); 66814cf11afSPaul Mackerras 6697233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 6707233593bSZang Roy-r61911 mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & 671e5356640SBenjamin Herrenschmidt ~MPIC_VECPRI_MASK); 67214cf11afSPaul Mackerras /* make sure mask gets to controller before we return to user */ 67314cf11afSPaul Mackerras do { 67414cf11afSPaul Mackerras if (!loops--) { 6758bfc5e36SScott Wood printk(KERN_ERR "%s: timeout on hwirq %u\n", 6768bfc5e36SScott Wood __func__, src); 67714cf11afSPaul Mackerras break; 67814cf11afSPaul Mackerras } 6797233593bSZang Roy-r61911 } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK); 6801beb6a7dSBenjamin Herrenschmidt } 6811beb6a7dSBenjamin Herrenschmidt 682835c0553SLennert Buytenhek void mpic_mask_irq(struct irq_data *d) 68314cf11afSPaul Mackerras { 68414cf11afSPaul Mackerras unsigned int loops = 100000; 685835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 686476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 68714cf11afSPaul Mackerras 688835c0553SLennert Buytenhek DBG("%s: disable_irq: %d (src %d)\n", mpic->name, d->irq, src); 68914cf11afSPaul Mackerras 6907233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 6917233593bSZang Roy-r61911 mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) | 692e5356640SBenjamin Herrenschmidt MPIC_VECPRI_MASK); 69314cf11afSPaul Mackerras 69414cf11afSPaul Mackerras /* make sure mask gets to controller before we return to user */ 69514cf11afSPaul Mackerras do { 69614cf11afSPaul Mackerras if (!loops--) { 6978bfc5e36SScott Wood printk(KERN_ERR "%s: timeout on hwirq %u\n", 6988bfc5e36SScott Wood __func__, src); 69914cf11afSPaul Mackerras break; 70014cf11afSPaul Mackerras } 7017233593bSZang Roy-r61911 } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK)); 70214cf11afSPaul Mackerras } 70314cf11afSPaul Mackerras 704835c0553SLennert Buytenhek void mpic_end_irq(struct irq_data *d) 70514cf11afSPaul Mackerras { 706835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 70714cf11afSPaul Mackerras 7081beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_IRQ 709835c0553SLennert Buytenhek DBG("%s: end_irq: %d\n", mpic->name, d->irq); 7101beb6a7dSBenjamin Herrenschmidt #endif 71114cf11afSPaul Mackerras /* We always EOI on end_irq() even for edge interrupts since that 71214cf11afSPaul Mackerras * should only lower the priority, the MPIC should have properly 71314cf11afSPaul Mackerras * latched another edge interrupt coming in anyway 71414cf11afSPaul Mackerras */ 71514cf11afSPaul Mackerras 71614cf11afSPaul Mackerras mpic_eoi(mpic); 71714cf11afSPaul Mackerras } 71814cf11afSPaul Mackerras 7196cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 720b9e5b4e6SBenjamin Herrenschmidt 721835c0553SLennert Buytenhek static void mpic_unmask_ht_irq(struct irq_data *d) 722b9e5b4e6SBenjamin Herrenschmidt { 723835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 724476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 725b9e5b4e6SBenjamin Herrenschmidt 726835c0553SLennert Buytenhek mpic_unmask_irq(d); 727b9e5b4e6SBenjamin Herrenschmidt 72824a3f2e8SThomas Gleixner if (irqd_is_level_type(d)) 729b9e5b4e6SBenjamin Herrenschmidt mpic_ht_end_irq(mpic, src); 730b9e5b4e6SBenjamin Herrenschmidt } 731b9e5b4e6SBenjamin Herrenschmidt 732835c0553SLennert Buytenhek static unsigned int mpic_startup_ht_irq(struct irq_data *d) 733b9e5b4e6SBenjamin Herrenschmidt { 734835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 735476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 736b9e5b4e6SBenjamin Herrenschmidt 737835c0553SLennert Buytenhek mpic_unmask_irq(d); 73824a3f2e8SThomas Gleixner mpic_startup_ht_interrupt(mpic, src, irqd_is_level_type(d)); 739b9e5b4e6SBenjamin Herrenschmidt 740b9e5b4e6SBenjamin Herrenschmidt return 0; 741b9e5b4e6SBenjamin Herrenschmidt } 742b9e5b4e6SBenjamin Herrenschmidt 743835c0553SLennert Buytenhek static void mpic_shutdown_ht_irq(struct irq_data *d) 744b9e5b4e6SBenjamin Herrenschmidt { 745835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 746476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 747b9e5b4e6SBenjamin Herrenschmidt 74824a3f2e8SThomas Gleixner mpic_shutdown_ht_interrupt(mpic, src); 749835c0553SLennert Buytenhek mpic_mask_irq(d); 750b9e5b4e6SBenjamin Herrenschmidt } 751b9e5b4e6SBenjamin Herrenschmidt 752835c0553SLennert Buytenhek static void mpic_end_ht_irq(struct irq_data *d) 753b9e5b4e6SBenjamin Herrenschmidt { 754835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 755476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 756b9e5b4e6SBenjamin Herrenschmidt 757b9e5b4e6SBenjamin Herrenschmidt #ifdef DEBUG_IRQ 758835c0553SLennert Buytenhek DBG("%s: end_irq: %d\n", mpic->name, d->irq); 759b9e5b4e6SBenjamin Herrenschmidt #endif 760b9e5b4e6SBenjamin Herrenschmidt /* We always EOI on end_irq() even for edge interrupts since that 761b9e5b4e6SBenjamin Herrenschmidt * should only lower the priority, the MPIC should have properly 762b9e5b4e6SBenjamin Herrenschmidt * latched another edge interrupt coming in anyway 763b9e5b4e6SBenjamin Herrenschmidt */ 764b9e5b4e6SBenjamin Herrenschmidt 76524a3f2e8SThomas Gleixner if (irqd_is_level_type(d)) 766b9e5b4e6SBenjamin Herrenschmidt mpic_ht_end_irq(mpic, src); 767b9e5b4e6SBenjamin Herrenschmidt mpic_eoi(mpic); 768b9e5b4e6SBenjamin Herrenschmidt } 7696cfef5b2SMichael Ellerman #endif /* !CONFIG_MPIC_U3_HT_IRQS */ 770b9e5b4e6SBenjamin Herrenschmidt 77114cf11afSPaul Mackerras #ifdef CONFIG_SMP 77214cf11afSPaul Mackerras 773835c0553SLennert Buytenhek static void mpic_unmask_ipi(struct irq_data *d) 77414cf11afSPaul Mackerras { 775835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_ipi(d); 776476eb491SGrant Likely unsigned int src = virq_to_hw(d->irq) - mpic->ipi_vecs[0]; 77714cf11afSPaul Mackerras 778835c0553SLennert Buytenhek DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, d->irq, src); 77914cf11afSPaul Mackerras mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK); 78014cf11afSPaul Mackerras } 78114cf11afSPaul Mackerras 782835c0553SLennert Buytenhek static void mpic_mask_ipi(struct irq_data *d) 78314cf11afSPaul Mackerras { 78414cf11afSPaul Mackerras /* NEVER disable an IPI... that's just plain wrong! */ 78514cf11afSPaul Mackerras } 78614cf11afSPaul Mackerras 787835c0553SLennert Buytenhek static void mpic_end_ipi(struct irq_data *d) 78814cf11afSPaul Mackerras { 789835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_ipi(d); 79014cf11afSPaul Mackerras 79114cf11afSPaul Mackerras /* 79214cf11afSPaul Mackerras * IPIs are marked IRQ_PER_CPU. This has the side effect of 79314cf11afSPaul Mackerras * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from 79414cf11afSPaul Mackerras * applying to them. We EOI them late to avoid re-entering. 79514cf11afSPaul Mackerras */ 79614cf11afSPaul Mackerras mpic_eoi(mpic); 79714cf11afSPaul Mackerras } 79814cf11afSPaul Mackerras 79914cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 80014cf11afSPaul Mackerras 801ea94187fSScott Wood static void mpic_unmask_tm(struct irq_data *d) 802ea94187fSScott Wood { 803ea94187fSScott Wood struct mpic *mpic = mpic_from_irq_data(d); 804ea94187fSScott Wood unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; 805ea94187fSScott Wood 80677ef4899SDmitry Eremin-Solenikov DBG("%s: enable_tm: %d (tm %d)\n", mpic->name, d->irq, src); 807ea94187fSScott Wood mpic_tm_write(src, mpic_tm_read(src) & ~MPIC_VECPRI_MASK); 808ea94187fSScott Wood mpic_tm_read(src); 809ea94187fSScott Wood } 810ea94187fSScott Wood 811ea94187fSScott Wood static void mpic_mask_tm(struct irq_data *d) 812ea94187fSScott Wood { 813ea94187fSScott Wood struct mpic *mpic = mpic_from_irq_data(d); 814ea94187fSScott Wood unsigned int src = virq_to_hw(d->irq) - mpic->timer_vecs[0]; 815ea94187fSScott Wood 816ea94187fSScott Wood mpic_tm_write(src, mpic_tm_read(src) | MPIC_VECPRI_MASK); 817ea94187fSScott Wood mpic_tm_read(src); 818ea94187fSScott Wood } 819ea94187fSScott Wood 820835c0553SLennert Buytenhek int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, 821835c0553SLennert Buytenhek bool force) 82214cf11afSPaul Mackerras { 823835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 824476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 82514cf11afSPaul Mackerras 8263c10c9c4SKumar Gala if (mpic->flags & MPIC_SINGLE_DEST_CPU) { 82738e1313fSYang Li int cpuid = irq_choose_cpu(cpumask); 8283c10c9c4SKumar Gala 8293c10c9c4SKumar Gala mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); 8303c10c9c4SKumar Gala } else { 8312a116f3dSMilton Miller u32 mask = cpumask_bits(cpumask)[0]; 83214cf11afSPaul Mackerras 8332a116f3dSMilton Miller mask &= cpumask_bits(cpu_online_mask)[0]; 83414cf11afSPaul Mackerras 8357233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 8362a116f3dSMilton Miller mpic_physmask(mask)); 83714cf11afSPaul Mackerras } 838d5dedd45SYinghai Lu 839d5dedd45SYinghai Lu return 0; 8403c10c9c4SKumar Gala } 84114cf11afSPaul Mackerras 8427233593bSZang Roy-r61911 static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type) 8430ebfff14SBenjamin Herrenschmidt { 8440ebfff14SBenjamin Herrenschmidt /* Now convert sense value */ 8456e99e458SBenjamin Herrenschmidt switch(type & IRQ_TYPE_SENSE_MASK) { 8460ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_EDGE_RISING: 8477233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_EDGE) | 8487233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_POSITIVE); 8490ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_EDGE_FALLING: 8506e99e458SBenjamin Herrenschmidt case IRQ_TYPE_EDGE_BOTH: 8517233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_EDGE) | 8527233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 8530ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_LEVEL_HIGH: 8547233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_LEVEL) | 8557233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_POSITIVE); 8560ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_LEVEL_LOW: 8570ebfff14SBenjamin Herrenschmidt default: 8587233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_LEVEL) | 8597233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 8600ebfff14SBenjamin Herrenschmidt } 8616e99e458SBenjamin Herrenschmidt } 8626e99e458SBenjamin Herrenschmidt 863835c0553SLennert Buytenhek int mpic_set_irq_type(struct irq_data *d, unsigned int flow_type) 8646e99e458SBenjamin Herrenschmidt { 865835c0553SLennert Buytenhek struct mpic *mpic = mpic_from_irq_data(d); 866476eb491SGrant Likely unsigned int src = irqd_to_hwirq(d); 8676e99e458SBenjamin Herrenschmidt unsigned int vecpri, vold, vnew; 8686e99e458SBenjamin Herrenschmidt 86906fe98e6SBenjamin Herrenschmidt DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n", 870835c0553SLennert Buytenhek mpic, d->irq, src, flow_type); 8716e99e458SBenjamin Herrenschmidt 8725019609fSKyle Moffett if (src >= mpic->num_sources) 8736e99e458SBenjamin Herrenschmidt return -EINVAL; 8746e99e458SBenjamin Herrenschmidt 875*446f6d06SBenjamin Herrenschmidt vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 8766e99e458SBenjamin Herrenschmidt 877*446f6d06SBenjamin Herrenschmidt /* We don't support "none" type */ 878*446f6d06SBenjamin Herrenschmidt if (flow_type == IRQ_TYPE_NONE) 879*446f6d06SBenjamin Herrenschmidt flow_type = IRQ_TYPE_DEFAULT; 880*446f6d06SBenjamin Herrenschmidt 881*446f6d06SBenjamin Herrenschmidt /* Default: read HW settings */ 882*446f6d06SBenjamin Herrenschmidt if (flow_type == IRQ_TYPE_DEFAULT) { 883*446f6d06SBenjamin Herrenschmidt switch(vold & (MPIC_INFO(VECPRI_POLARITY_MASK) | 884*446f6d06SBenjamin Herrenschmidt MPIC_INFO(VECPRI_SENSE_MASK))) { 885*446f6d06SBenjamin Herrenschmidt case MPIC_INFO(VECPRI_SENSE_EDGE) | 886*446f6d06SBenjamin Herrenschmidt MPIC_INFO(VECPRI_POLARITY_POSITIVE): 887*446f6d06SBenjamin Herrenschmidt flow_type = IRQ_TYPE_EDGE_RISING; 888*446f6d06SBenjamin Herrenschmidt break; 889*446f6d06SBenjamin Herrenschmidt case MPIC_INFO(VECPRI_SENSE_EDGE) | 890*446f6d06SBenjamin Herrenschmidt MPIC_INFO(VECPRI_POLARITY_NEGATIVE): 891*446f6d06SBenjamin Herrenschmidt flow_type = IRQ_TYPE_EDGE_FALLING; 892*446f6d06SBenjamin Herrenschmidt break; 893*446f6d06SBenjamin Herrenschmidt case MPIC_INFO(VECPRI_SENSE_LEVEL) | 894*446f6d06SBenjamin Herrenschmidt MPIC_INFO(VECPRI_POLARITY_POSITIVE): 895*446f6d06SBenjamin Herrenschmidt flow_type = IRQ_TYPE_LEVEL_HIGH; 896*446f6d06SBenjamin Herrenschmidt break; 897*446f6d06SBenjamin Herrenschmidt case MPIC_INFO(VECPRI_SENSE_LEVEL) | 898*446f6d06SBenjamin Herrenschmidt MPIC_INFO(VECPRI_POLARITY_NEGATIVE): 899*446f6d06SBenjamin Herrenschmidt flow_type = IRQ_TYPE_LEVEL_LOW; 900*446f6d06SBenjamin Herrenschmidt break; 901*446f6d06SBenjamin Herrenschmidt } 902*446f6d06SBenjamin Herrenschmidt } 903*446f6d06SBenjamin Herrenschmidt 904*446f6d06SBenjamin Herrenschmidt /* Apply to irq desc */ 90524a3f2e8SThomas Gleixner irqd_set_trigger_type(d, flow_type); 9066e99e458SBenjamin Herrenschmidt 907*446f6d06SBenjamin Herrenschmidt /* Apply to HW */ 9086e99e458SBenjamin Herrenschmidt if (mpic_is_ht_interrupt(mpic, src)) 9096e99e458SBenjamin Herrenschmidt vecpri = MPIC_VECPRI_POLARITY_POSITIVE | 9106e99e458SBenjamin Herrenschmidt MPIC_VECPRI_SENSE_EDGE; 9116e99e458SBenjamin Herrenschmidt else 9127233593bSZang Roy-r61911 vecpri = mpic_type_to_vecpri(mpic, flow_type); 9136e99e458SBenjamin Herrenschmidt 9147233593bSZang Roy-r61911 vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) | 9157233593bSZang Roy-r61911 MPIC_INFO(VECPRI_SENSE_MASK)); 9166e99e458SBenjamin Herrenschmidt vnew |= vecpri; 9176e99e458SBenjamin Herrenschmidt if (vold != vnew) 9187233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew); 9196e99e458SBenjamin Herrenschmidt 920e075cd70SJustin P. Mattock return IRQ_SET_MASK_OK_NOCOPY; 9210ebfff14SBenjamin Herrenschmidt } 9220ebfff14SBenjamin Herrenschmidt 92338958dd9SOlof Johansson void mpic_set_vector(unsigned int virq, unsigned int vector) 92438958dd9SOlof Johansson { 92538958dd9SOlof Johansson struct mpic *mpic = mpic_from_irq(virq); 926476eb491SGrant Likely unsigned int src = virq_to_hw(virq); 92738958dd9SOlof Johansson unsigned int vecpri; 92838958dd9SOlof Johansson 92938958dd9SOlof Johansson DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n", 93038958dd9SOlof Johansson mpic, virq, src, vector); 93138958dd9SOlof Johansson 9325019609fSKyle Moffett if (src >= mpic->num_sources) 93338958dd9SOlof Johansson return; 93438958dd9SOlof Johansson 93538958dd9SOlof Johansson vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 93638958dd9SOlof Johansson vecpri = vecpri & ~MPIC_INFO(VECPRI_VECTOR_MASK); 93738958dd9SOlof Johansson vecpri |= vector; 93838958dd9SOlof Johansson mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); 93938958dd9SOlof Johansson } 94038958dd9SOlof Johansson 941dfec2202SMeador Inge void mpic_set_destination(unsigned int virq, unsigned int cpuid) 942dfec2202SMeador Inge { 943dfec2202SMeador Inge struct mpic *mpic = mpic_from_irq(virq); 944476eb491SGrant Likely unsigned int src = virq_to_hw(virq); 945dfec2202SMeador Inge 946dfec2202SMeador Inge DBG("mpic: set_destination(mpic:@%p,virq:%d,src:%d,cpuid:0x%x)\n", 947dfec2202SMeador Inge mpic, virq, src, cpuid); 948dfec2202SMeador Inge 9495019609fSKyle Moffett if (src >= mpic->num_sources) 950dfec2202SMeador Inge return; 951dfec2202SMeador Inge 952dfec2202SMeador Inge mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 1 << cpuid); 953dfec2202SMeador Inge } 954dfec2202SMeador Inge 955b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_chip = { 956835c0553SLennert Buytenhek .irq_mask = mpic_mask_irq, 957835c0553SLennert Buytenhek .irq_unmask = mpic_unmask_irq, 958835c0553SLennert Buytenhek .irq_eoi = mpic_end_irq, 959835c0553SLennert Buytenhek .irq_set_type = mpic_set_irq_type, 960b9e5b4e6SBenjamin Herrenschmidt }; 961b9e5b4e6SBenjamin Herrenschmidt 962b9e5b4e6SBenjamin Herrenschmidt #ifdef CONFIG_SMP 963b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_ipi_chip = { 964835c0553SLennert Buytenhek .irq_mask = mpic_mask_ipi, 965835c0553SLennert Buytenhek .irq_unmask = mpic_unmask_ipi, 966835c0553SLennert Buytenhek .irq_eoi = mpic_end_ipi, 967b9e5b4e6SBenjamin Herrenschmidt }; 968b9e5b4e6SBenjamin Herrenschmidt #endif /* CONFIG_SMP */ 969b9e5b4e6SBenjamin Herrenschmidt 970ea94187fSScott Wood static struct irq_chip mpic_tm_chip = { 971ea94187fSScott Wood .irq_mask = mpic_mask_tm, 972ea94187fSScott Wood .irq_unmask = mpic_unmask_tm, 973ea94187fSScott Wood .irq_eoi = mpic_end_irq, 974ea94187fSScott Wood }; 975ea94187fSScott Wood 9766cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 977b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_ht_chip = { 978835c0553SLennert Buytenhek .irq_startup = mpic_startup_ht_irq, 979835c0553SLennert Buytenhek .irq_shutdown = mpic_shutdown_ht_irq, 980835c0553SLennert Buytenhek .irq_mask = mpic_mask_irq, 981835c0553SLennert Buytenhek .irq_unmask = mpic_unmask_ht_irq, 982835c0553SLennert Buytenhek .irq_eoi = mpic_end_ht_irq, 983835c0553SLennert Buytenhek .irq_set_type = mpic_set_irq_type, 984b9e5b4e6SBenjamin Herrenschmidt }; 9856cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 986b9e5b4e6SBenjamin Herrenschmidt 98714cf11afSPaul Mackerras 988bae1d8f1SGrant Likely static int mpic_host_match(struct irq_domain *h, struct device_node *node) 9890ebfff14SBenjamin Herrenschmidt { 9900ebfff14SBenjamin Herrenschmidt /* Exact match, unless mpic node is NULL */ 99152964f87SMichael Ellerman return h->of_node == NULL || h->of_node == node; 9920ebfff14SBenjamin Herrenschmidt } 9930ebfff14SBenjamin Herrenschmidt 994bae1d8f1SGrant Likely static int mpic_host_map(struct irq_domain *h, unsigned int virq, 9956e99e458SBenjamin Herrenschmidt irq_hw_number_t hw) 9960ebfff14SBenjamin Herrenschmidt { 9970ebfff14SBenjamin Herrenschmidt struct mpic *mpic = h->host_data; 9986e99e458SBenjamin Herrenschmidt struct irq_chip *chip; 9990ebfff14SBenjamin Herrenschmidt 100006fe98e6SBenjamin Herrenschmidt DBG("mpic: map virq %d, hwirq 0x%lx\n", virq, hw); 10010ebfff14SBenjamin Herrenschmidt 10027df2457dSOlof Johansson if (hw == mpic->spurious_vec) 10030ebfff14SBenjamin Herrenschmidt return -EINVAL; 10047fd72186SBenjamin Herrenschmidt if (mpic->protected && test_bit(hw, mpic->protected)) 10057fd72186SBenjamin Herrenschmidt return -EINVAL; 100606fe98e6SBenjamin Herrenschmidt 10070ebfff14SBenjamin Herrenschmidt #ifdef CONFIG_SMP 10087df2457dSOlof Johansson else if (hw >= mpic->ipi_vecs[0]) { 1009be8bec56SKyle Moffett WARN_ON(mpic->flags & MPIC_SECONDARY); 10100ebfff14SBenjamin Herrenschmidt 101106fe98e6SBenjamin Herrenschmidt DBG("mpic: mapping as IPI\n"); 1012ec775d0eSThomas Gleixner irq_set_chip_data(virq, mpic); 1013ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, &mpic->hc_ipi, 10140ebfff14SBenjamin Herrenschmidt handle_percpu_irq); 10150ebfff14SBenjamin Herrenschmidt return 0; 10160ebfff14SBenjamin Herrenschmidt } 10170ebfff14SBenjamin Herrenschmidt #endif /* CONFIG_SMP */ 10180ebfff14SBenjamin Herrenschmidt 1019ea94187fSScott Wood if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) { 1020be8bec56SKyle Moffett WARN_ON(mpic->flags & MPIC_SECONDARY); 1021ea94187fSScott Wood 1022ea94187fSScott Wood DBG("mpic: mapping as timer\n"); 1023ea94187fSScott Wood irq_set_chip_data(virq, mpic); 1024ea94187fSScott Wood irq_set_chip_and_handler(virq, &mpic->hc_tm, 1025ea94187fSScott Wood handle_fasteoi_irq); 1026ea94187fSScott Wood return 0; 1027ea94187fSScott Wood } 1028ea94187fSScott Wood 10295019609fSKyle Moffett if (hw >= mpic->num_sources) 10300ebfff14SBenjamin Herrenschmidt return -EINVAL; 10310ebfff14SBenjamin Herrenschmidt 1032a7de7c74SMichael Ellerman mpic_msi_reserve_hwirq(mpic, hw); 1033a7de7c74SMichael Ellerman 10346e99e458SBenjamin Herrenschmidt /* Default chip */ 10350ebfff14SBenjamin Herrenschmidt chip = &mpic->hc_irq; 10360ebfff14SBenjamin Herrenschmidt 10376cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 10380ebfff14SBenjamin Herrenschmidt /* Check for HT interrupts, override vecpri */ 10396e99e458SBenjamin Herrenschmidt if (mpic_is_ht_interrupt(mpic, hw)) 10400ebfff14SBenjamin Herrenschmidt chip = &mpic->hc_ht_irq; 10416cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 10420ebfff14SBenjamin Herrenschmidt 104306fe98e6SBenjamin Herrenschmidt DBG("mpic: mapping to irq chip @%p\n", chip); 10440ebfff14SBenjamin Herrenschmidt 1045ec775d0eSThomas Gleixner irq_set_chip_data(virq, mpic); 1046ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, chip, handle_fasteoi_irq); 10476e99e458SBenjamin Herrenschmidt 10486e99e458SBenjamin Herrenschmidt /* Set default irq type */ 1049*446f6d06SBenjamin Herrenschmidt irq_set_irq_type(virq, IRQ_TYPE_DEFAULT); 10506e99e458SBenjamin Herrenschmidt 1051dfec2202SMeador Inge /* If the MPIC was reset, then all vectors have already been 1052dfec2202SMeador Inge * initialized. Otherwise, a per source lazy initialization 1053dfec2202SMeador Inge * is done here. 1054dfec2202SMeador Inge */ 1055dfec2202SMeador Inge if (!mpic_is_ipi(mpic, hw) && (mpic->flags & MPIC_NO_RESET)) { 1056dfec2202SMeador Inge mpic_set_vector(virq, hw); 1057d6a2639bSMeador Inge mpic_set_destination(virq, mpic_processor_id(mpic)); 1058dfec2202SMeador Inge mpic_irq_set_priority(virq, 8); 1059dfec2202SMeador Inge } 1060dfec2202SMeador Inge 10610ebfff14SBenjamin Herrenschmidt return 0; 10620ebfff14SBenjamin Herrenschmidt } 10630ebfff14SBenjamin Herrenschmidt 1064bae1d8f1SGrant Likely static int mpic_host_xlate(struct irq_domain *h, struct device_node *ct, 106540d50cf7SRoman Fietze const u32 *intspec, unsigned int intsize, 10660ebfff14SBenjamin Herrenschmidt irq_hw_number_t *out_hwirq, unsigned int *out_flags) 10670ebfff14SBenjamin Herrenschmidt 10680ebfff14SBenjamin Herrenschmidt { 106922d168ceSScott Wood struct mpic *mpic = h->host_data; 10700ebfff14SBenjamin Herrenschmidt static unsigned char map_mpic_senses[4] = { 10710ebfff14SBenjamin Herrenschmidt IRQ_TYPE_EDGE_RISING, 10720ebfff14SBenjamin Herrenschmidt IRQ_TYPE_LEVEL_LOW, 10730ebfff14SBenjamin Herrenschmidt IRQ_TYPE_LEVEL_HIGH, 10740ebfff14SBenjamin Herrenschmidt IRQ_TYPE_EDGE_FALLING, 10750ebfff14SBenjamin Herrenschmidt }; 10760ebfff14SBenjamin Herrenschmidt 10770ebfff14SBenjamin Herrenschmidt *out_hwirq = intspec[0]; 107822d168ceSScott Wood if (intsize >= 4 && (mpic->flags & MPIC_FSL)) { 107922d168ceSScott Wood /* 108022d168ceSScott Wood * Freescale MPIC with extended intspec: 108122d168ceSScott Wood * First two cells are as usual. Third specifies 108222d168ceSScott Wood * an "interrupt type". Fourth is type-specific data. 108322d168ceSScott Wood * 108422d168ceSScott Wood * See Documentation/devicetree/bindings/powerpc/fsl/mpic.txt 108522d168ceSScott Wood */ 108622d168ceSScott Wood switch (intspec[2]) { 108722d168ceSScott Wood case 0: 108822d168ceSScott Wood case 1: /* no EISR/EIMR support for now, treat as shared IRQ */ 108922d168ceSScott Wood break; 109022d168ceSScott Wood case 2: 109122d168ceSScott Wood if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs)) 109222d168ceSScott Wood return -EINVAL; 109322d168ceSScott Wood 109422d168ceSScott Wood *out_hwirq = mpic->ipi_vecs[intspec[0]]; 109522d168ceSScott Wood break; 109622d168ceSScott Wood case 3: 109722d168ceSScott Wood if (intspec[0] >= ARRAY_SIZE(mpic->timer_vecs)) 109822d168ceSScott Wood return -EINVAL; 109922d168ceSScott Wood 110022d168ceSScott Wood *out_hwirq = mpic->timer_vecs[intspec[0]]; 110122d168ceSScott Wood break; 110222d168ceSScott Wood default: 110322d168ceSScott Wood pr_debug("%s: unknown irq type %u\n", 110422d168ceSScott Wood __func__, intspec[2]); 110522d168ceSScott Wood return -EINVAL; 110622d168ceSScott Wood } 110722d168ceSScott Wood 110822d168ceSScott Wood *out_flags = map_mpic_senses[intspec[1] & 3]; 110922d168ceSScott Wood } else if (intsize > 1) { 111006fe98e6SBenjamin Herrenschmidt u32 mask = 0x3; 111106fe98e6SBenjamin Herrenschmidt 111206fe98e6SBenjamin Herrenschmidt /* Apple invented a new race of encoding on machines with 111306fe98e6SBenjamin Herrenschmidt * an HT APIC. They encode, among others, the index within 111406fe98e6SBenjamin Herrenschmidt * the HT APIC. We don't care about it here since thankfully, 111506fe98e6SBenjamin Herrenschmidt * it appears that they have the APIC already properly 111606fe98e6SBenjamin Herrenschmidt * configured, and thus our current fixup code that reads the 111706fe98e6SBenjamin Herrenschmidt * APIC config works fine. However, we still need to mask out 111806fe98e6SBenjamin Herrenschmidt * bits in the specifier to make sure we only get bit 0 which 111906fe98e6SBenjamin Herrenschmidt * is the level/edge bit (the only sense bit exposed by Apple), 112006fe98e6SBenjamin Herrenschmidt * as their bit 1 means something else. 112106fe98e6SBenjamin Herrenschmidt */ 112206fe98e6SBenjamin Herrenschmidt if (machine_is(powermac)) 112306fe98e6SBenjamin Herrenschmidt mask = 0x1; 112406fe98e6SBenjamin Herrenschmidt *out_flags = map_mpic_senses[intspec[1] & mask]; 112506fe98e6SBenjamin Herrenschmidt } else 11260ebfff14SBenjamin Herrenschmidt *out_flags = IRQ_TYPE_NONE; 11270ebfff14SBenjamin Herrenschmidt 112806fe98e6SBenjamin Herrenschmidt DBG("mpic: xlate (%d cells: 0x%08x 0x%08x) to line 0x%lx sense 0x%x\n", 112906fe98e6SBenjamin Herrenschmidt intsize, intspec[0], intspec[1], *out_hwirq, *out_flags); 113006fe98e6SBenjamin Herrenschmidt 11310ebfff14SBenjamin Herrenschmidt return 0; 11320ebfff14SBenjamin Herrenschmidt } 11330ebfff14SBenjamin Herrenschmidt 113409dc34a9SKyle Moffett /* IRQ handler for a secondary MPIC cascaded from another IRQ controller */ 113509dc34a9SKyle Moffett static void mpic_cascade(unsigned int irq, struct irq_desc *desc) 113609dc34a9SKyle Moffett { 113709dc34a9SKyle Moffett struct irq_chip *chip = irq_desc_get_chip(desc); 113809dc34a9SKyle Moffett struct mpic *mpic = irq_desc_get_handler_data(desc); 113909dc34a9SKyle Moffett unsigned int virq; 114009dc34a9SKyle Moffett 114109dc34a9SKyle Moffett BUG_ON(!(mpic->flags & MPIC_SECONDARY)); 114209dc34a9SKyle Moffett 114309dc34a9SKyle Moffett virq = mpic_get_one_irq(mpic); 1144bae1d8f1SGrant Likely if (virq) 114509dc34a9SKyle Moffett generic_handle_irq(virq); 114609dc34a9SKyle Moffett 114709dc34a9SKyle Moffett chip->irq_eoi(&desc->irq_data); 114809dc34a9SKyle Moffett } 114909dc34a9SKyle Moffett 1150bae1d8f1SGrant Likely static struct irq_domain_ops mpic_host_ops = { 11510ebfff14SBenjamin Herrenschmidt .match = mpic_host_match, 11520ebfff14SBenjamin Herrenschmidt .map = mpic_host_map, 11530ebfff14SBenjamin Herrenschmidt .xlate = mpic_host_xlate, 11540ebfff14SBenjamin Herrenschmidt }; 11550ebfff14SBenjamin Herrenschmidt 115614cf11afSPaul Mackerras /* 115714cf11afSPaul Mackerras * Exported functions 115814cf11afSPaul Mackerras */ 115914cf11afSPaul Mackerras 11600ebfff14SBenjamin Herrenschmidt struct mpic * __init mpic_alloc(struct device_node *node, 1161a959ff56SBenjamin Herrenschmidt phys_addr_t phys_addr, 116214cf11afSPaul Mackerras unsigned int flags, 116314cf11afSPaul Mackerras unsigned int isu_size, 116414cf11afSPaul Mackerras unsigned int irq_count, 116514cf11afSPaul Mackerras const char *name) 116614cf11afSPaul Mackerras { 11675bdb6f2eSKyle Moffett int i, psize, intvec_top; 116814cf11afSPaul Mackerras struct mpic *mpic; 1169d9d1063dSJohannes Berg u32 greg_feature; 117014cf11afSPaul Mackerras const char *vers; 11715bdb6f2eSKyle Moffett const u32 *psrc; 1172c1b8d45dSKyle Moffett u32 last_irq; 11738bf41568SKyle Moffett 1174996983b7SKyle Moffett /* Default MPIC search parameters */ 1175996983b7SKyle Moffett static const struct of_device_id __initconst mpic_device_id[] = { 1176996983b7SKyle Moffett { .type = "open-pic", }, 1177996983b7SKyle Moffett { .compatible = "open-pic", }, 1178996983b7SKyle Moffett {}, 1179996983b7SKyle Moffett }; 1180996983b7SKyle Moffett 1181996983b7SKyle Moffett /* 1182996983b7SKyle Moffett * If we were not passed a device-tree node, then perform the default 1183996983b7SKyle Moffett * search for standardized a standardized OpenPIC. 1184996983b7SKyle Moffett */ 1185996983b7SKyle Moffett if (node) { 1186996983b7SKyle Moffett node = of_node_get(node); 1187996983b7SKyle Moffett } else { 1188996983b7SKyle Moffett node = of_find_matching_node(NULL, mpic_device_id); 1189996983b7SKyle Moffett if (!node) 1190996983b7SKyle Moffett return NULL; 1191996983b7SKyle Moffett } 11928bf41568SKyle Moffett 11935bdb6f2eSKyle Moffett /* Pick the physical address from the device tree if unspecified */ 11945bdb6f2eSKyle Moffett if (!phys_addr) { 11958bf41568SKyle Moffett /* Check if it is DCR-based */ 11968bf41568SKyle Moffett if (of_get_property(node, "dcr-reg", NULL)) { 11978bf41568SKyle Moffett flags |= MPIC_USES_DCR; 11988bf41568SKyle Moffett } else { 11998bf41568SKyle Moffett struct resource r; 12008bf41568SKyle Moffett if (of_address_to_resource(node, 0, &r)) 1201996983b7SKyle Moffett goto err_of_node_put; 12028bf41568SKyle Moffett phys_addr = r.start; 12038bf41568SKyle Moffett } 12048bf41568SKyle Moffett } 120514cf11afSPaul Mackerras 12063a7a7176SKyle Moffett /* Read extra device-tree properties into the flags variable */ 12073a7a7176SKyle Moffett if (of_get_property(node, "big-endian", NULL)) 12083a7a7176SKyle Moffett flags |= MPIC_BIG_ENDIAN; 12093a7a7176SKyle Moffett if (of_get_property(node, "pic-no-reset", NULL)) 12103a7a7176SKyle Moffett flags |= MPIC_NO_RESET; 12119ca163c8SKyle Moffett if (of_get_property(node, "single-cpu-affinity", NULL)) 12129ca163c8SKyle Moffett flags |= MPIC_SINGLE_DEST_CPU; 12133a7a7176SKyle Moffett if (of_device_is_compatible(node, "fsl,mpic")) 12143a7a7176SKyle Moffett flags |= MPIC_FSL; 12153a7a7176SKyle Moffett 121685355bb2SKumar Gala mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL); 121714cf11afSPaul Mackerras if (mpic == NULL) 1218996983b7SKyle Moffett goto err_of_node_put; 121914cf11afSPaul Mackerras 122014cf11afSPaul Mackerras mpic->name = name; 1221c51242e7SKyle Moffett mpic->node = node; 1222e7a98675SKyle Moffett mpic->paddr = phys_addr; 12233a7a7176SKyle Moffett mpic->flags = flags; 122414cf11afSPaul Mackerras 1225b9e5b4e6SBenjamin Herrenschmidt mpic->hc_irq = mpic_irq_chip; 1226b27df672SThomas Gleixner mpic->hc_irq.name = name; 12273a7a7176SKyle Moffett if (!(mpic->flags & MPIC_SECONDARY)) 1228835c0553SLennert Buytenhek mpic->hc_irq.irq_set_affinity = mpic_set_affinity; 12296cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 1230b9e5b4e6SBenjamin Herrenschmidt mpic->hc_ht_irq = mpic_irq_ht_chip; 1231b27df672SThomas Gleixner mpic->hc_ht_irq.name = name; 12323a7a7176SKyle Moffett if (!(mpic->flags & MPIC_SECONDARY)) 1233835c0553SLennert Buytenhek mpic->hc_ht_irq.irq_set_affinity = mpic_set_affinity; 12346cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 1235fbf0274eSBenjamin Herrenschmidt 123614cf11afSPaul Mackerras #ifdef CONFIG_SMP 1237b9e5b4e6SBenjamin Herrenschmidt mpic->hc_ipi = mpic_ipi_chip; 1238b27df672SThomas Gleixner mpic->hc_ipi.name = name; 123914cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 124014cf11afSPaul Mackerras 1241ea94187fSScott Wood mpic->hc_tm = mpic_tm_chip; 1242ea94187fSScott Wood mpic->hc_tm.name = name; 1243ea94187fSScott Wood 124414cf11afSPaul Mackerras mpic->num_sources = 0; /* so far */ 124514cf11afSPaul Mackerras 12463a7a7176SKyle Moffett if (mpic->flags & MPIC_LARGE_VECTORS) 12477df2457dSOlof Johansson intvec_top = 2047; 12487df2457dSOlof Johansson else 12497df2457dSOlof Johansson intvec_top = 255; 12507df2457dSOlof Johansson 1251ea94187fSScott Wood mpic->timer_vecs[0] = intvec_top - 12; 1252ea94187fSScott Wood mpic->timer_vecs[1] = intvec_top - 11; 1253ea94187fSScott Wood mpic->timer_vecs[2] = intvec_top - 10; 1254ea94187fSScott Wood mpic->timer_vecs[3] = intvec_top - 9; 1255ea94187fSScott Wood mpic->timer_vecs[4] = intvec_top - 8; 1256ea94187fSScott Wood mpic->timer_vecs[5] = intvec_top - 7; 1257ea94187fSScott Wood mpic->timer_vecs[6] = intvec_top - 6; 1258ea94187fSScott Wood mpic->timer_vecs[7] = intvec_top - 5; 12597df2457dSOlof Johansson mpic->ipi_vecs[0] = intvec_top - 4; 12607df2457dSOlof Johansson mpic->ipi_vecs[1] = intvec_top - 3; 12617df2457dSOlof Johansson mpic->ipi_vecs[2] = intvec_top - 2; 12627df2457dSOlof Johansson mpic->ipi_vecs[3] = intvec_top - 1; 12637df2457dSOlof Johansson mpic->spurious_vec = intvec_top; 12647df2457dSOlof Johansson 12657fd72186SBenjamin Herrenschmidt /* Look for protected sources */ 1266c51242e7SKyle Moffett psrc = of_get_property(mpic->node, "protected-sources", &psize); 12677fd72186SBenjamin Herrenschmidt if (psrc) { 12685bdb6f2eSKyle Moffett /* Allocate a bitmap with one bit per interrupt */ 12695bdb6f2eSKyle Moffett unsigned int mapsize = BITS_TO_LONGS(intvec_top + 1); 12705bdb6f2eSKyle Moffett mpic->protected = kzalloc(mapsize*sizeof(long), GFP_KERNEL); 12717fd72186SBenjamin Herrenschmidt BUG_ON(mpic->protected == NULL); 12725bdb6f2eSKyle Moffett for (i = 0; i < psize/sizeof(u32); i++) { 12737fd72186SBenjamin Herrenschmidt if (psrc[i] > intvec_top) 12747fd72186SBenjamin Herrenschmidt continue; 12757fd72186SBenjamin Herrenschmidt __set_bit(psrc[i], mpic->protected); 12767fd72186SBenjamin Herrenschmidt } 12777fd72186SBenjamin Herrenschmidt } 1278a959ff56SBenjamin Herrenschmidt 12797233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD 12803a7a7176SKyle Moffett mpic->hw_set = mpic_infos[MPIC_GET_REGSET(mpic->flags)]; 12817233593bSZang Roy-r61911 #endif 12827233593bSZang Roy-r61911 1283fbf0274eSBenjamin Herrenschmidt /* default register type */ 12843a7a7176SKyle Moffett if (mpic->flags & MPIC_BIG_ENDIAN) 12858bf41568SKyle Moffett mpic->reg_type = mpic_access_mmio_be; 12868bf41568SKyle Moffett else 12878bf41568SKyle Moffett mpic->reg_type = mpic_access_mmio_le; 1288fbf0274eSBenjamin Herrenschmidt 12898bf41568SKyle Moffett /* 12908bf41568SKyle Moffett * An MPIC with a "dcr-reg" property must be accessed that way, but 12918bf41568SKyle Moffett * only if the kernel includes DCR support. 12928bf41568SKyle Moffett */ 1293fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 12943a7a7176SKyle Moffett if (mpic->flags & MPIC_USES_DCR) 1295fbf0274eSBenjamin Herrenschmidt mpic->reg_type = mpic_access_dcr; 1296fbf0274eSBenjamin Herrenschmidt #else 12973a7a7176SKyle Moffett BUG_ON(mpic->flags & MPIC_USES_DCR); 12988bf41568SKyle Moffett #endif 1299a959ff56SBenjamin Herrenschmidt 130014cf11afSPaul Mackerras /* Map the global registers */ 1301c51242e7SKyle Moffett mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); 1302c51242e7SKyle Moffett mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); 130314cf11afSPaul Mackerras 130414cf11afSPaul Mackerras /* Reset */ 1305dfec2202SMeador Inge 1306dfec2202SMeador Inge /* When using a device-node, reset requests are only honored if the MPIC 1307dfec2202SMeador Inge * is allowed to reset. 1308dfec2202SMeador Inge */ 1309e55d7f73SKyle Moffett if (!(mpic->flags & MPIC_NO_RESET)) { 1310dfec2202SMeador Inge printk(KERN_DEBUG "mpic: Resetting\n"); 13117233593bSZang Roy-r61911 mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 13127233593bSZang Roy-r61911 mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 131314cf11afSPaul Mackerras | MPIC_GREG_GCONF_RESET); 13147233593bSZang Roy-r61911 while( mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 131514cf11afSPaul Mackerras & MPIC_GREG_GCONF_RESET) 131614cf11afSPaul Mackerras mb(); 131714cf11afSPaul Mackerras } 131814cf11afSPaul Mackerras 1319d91e4ea7SKumar Gala /* CoreInt */ 13203a7a7176SKyle Moffett if (mpic->flags & MPIC_ENABLE_COREINT) 1321d91e4ea7SKumar Gala mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 1322d91e4ea7SKumar Gala mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 1323d91e4ea7SKumar Gala | MPIC_GREG_GCONF_COREINT); 1324d91e4ea7SKumar Gala 13253a7a7176SKyle Moffett if (mpic->flags & MPIC_ENABLE_MCK) 1326f365355eSOlof Johansson mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 1327f365355eSOlof Johansson mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 1328f365355eSOlof Johansson | MPIC_GREG_GCONF_MCK); 1329f365355eSOlof Johansson 133014b92470STimur Tabi /* 133114b92470STimur Tabi * The MPIC driver will crash if there are more cores than we 133214b92470STimur Tabi * can initialize, so we may as well catch that problem here. 133314b92470STimur Tabi */ 133414b92470STimur Tabi BUG_ON(num_possible_cpus() > MPIC_MAX_CPUS); 133514b92470STimur Tabi 133614cf11afSPaul Mackerras /* Map the per-CPU registers */ 133714b92470STimur Tabi for_each_possible_cpu(i) { 133814b92470STimur Tabi unsigned int cpu = get_hard_smp_processor_id(i); 133914b92470STimur Tabi 1340c51242e7SKyle Moffett mpic_map(mpic, mpic->paddr, &mpic->cpuregs[cpu], 134114b92470STimur Tabi MPIC_INFO(CPU_BASE) + cpu * MPIC_INFO(CPU_STRIDE), 1342fbf0274eSBenjamin Herrenschmidt 0x1000); 134314cf11afSPaul Mackerras } 134414cf11afSPaul Mackerras 1345c1b8d45dSKyle Moffett /* 1346c1b8d45dSKyle Moffett * Read feature register. For non-ISU MPICs, num sources as well. On 1347c1b8d45dSKyle Moffett * ISU MPICs, sources are counted as ISUs are added 1348c1b8d45dSKyle Moffett */ 1349c1b8d45dSKyle Moffett greg_feature = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); 1350c1b8d45dSKyle Moffett 1351c1b8d45dSKyle Moffett /* 1352c1b8d45dSKyle Moffett * By default, the last source number comes from the MPIC, but the 1353c1b8d45dSKyle Moffett * device-tree and board support code can override it on buggy hw. 1354fe83364fSBenjamin Herrenschmidt * If we get passed an isu_size (multi-isu MPIC) then we use that 1355fe83364fSBenjamin Herrenschmidt * as a default instead of the value read from the HW. 1356c1b8d45dSKyle Moffett */ 1357c1b8d45dSKyle Moffett last_irq = (greg_feature & MPIC_GREG_FEATURE_LAST_SRC_MASK) 1358c1b8d45dSKyle Moffett >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT; 1359fe83364fSBenjamin Herrenschmidt if (isu_size) 1360fe83364fSBenjamin Herrenschmidt last_irq = isu_size * MPIC_MAX_ISU - 1; 1361c1b8d45dSKyle Moffett of_property_read_u32(mpic->node, "last-interrupt-source", &last_irq); 1362c1b8d45dSKyle Moffett if (irq_count) 1363c1b8d45dSKyle Moffett last_irq = irq_count - 1; 1364c1b8d45dSKyle Moffett 136514cf11afSPaul Mackerras /* Initialize main ISU if none provided */ 1366c1b8d45dSKyle Moffett if (!isu_size) { 1367c1b8d45dSKyle Moffett isu_size = last_irq + 1; 1368c1b8d45dSKyle Moffett mpic->num_sources = isu_size; 1369c51242e7SKyle Moffett mpic_map(mpic, mpic->paddr, &mpic->isus[0], 1370c1b8d45dSKyle Moffett MPIC_INFO(IRQ_BASE), 1371c1b8d45dSKyle Moffett MPIC_INFO(IRQ_STRIDE) * isu_size); 137214cf11afSPaul Mackerras } 1373c1b8d45dSKyle Moffett 1374c1b8d45dSKyle Moffett mpic->isu_size = isu_size; 137514cf11afSPaul Mackerras mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); 137614cf11afSPaul Mackerras mpic->isu_mask = (1 << mpic->isu_shift) - 1; 137714cf11afSPaul Mackerras 1378a8db8cf0SGrant Likely mpic->irqhost = irq_domain_add_linear(mpic->node, 13795375871dSLinus Torvalds last_irq + 1, 1380a8db8cf0SGrant Likely &mpic_host_ops, mpic); 1381996983b7SKyle Moffett 1382996983b7SKyle Moffett /* 1383996983b7SKyle Moffett * FIXME: The code leaks the MPIC object and mappings here; this 1384996983b7SKyle Moffett * is very unlikely to fail but it ought to be fixed anyways. 1385996983b7SKyle Moffett */ 138631207dabSKumar Gala if (mpic->irqhost == NULL) 138731207dabSKumar Gala return NULL; 138831207dabSKumar Gala 138914cf11afSPaul Mackerras /* Display version */ 1390d9d1063dSJohannes Berg switch (greg_feature & MPIC_GREG_FEATURE_VERSION_MASK) { 139114cf11afSPaul Mackerras case 1: 139214cf11afSPaul Mackerras vers = "1.0"; 139314cf11afSPaul Mackerras break; 139414cf11afSPaul Mackerras case 2: 139514cf11afSPaul Mackerras vers = "1.2"; 139614cf11afSPaul Mackerras break; 139714cf11afSPaul Mackerras case 3: 139814cf11afSPaul Mackerras vers = "1.3"; 139914cf11afSPaul Mackerras break; 140014cf11afSPaul Mackerras default: 140114cf11afSPaul Mackerras vers = "<unknown>"; 140214cf11afSPaul Mackerras break; 140314cf11afSPaul Mackerras } 1404a959ff56SBenjamin Herrenschmidt printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx," 1405a959ff56SBenjamin Herrenschmidt " max %d CPUs\n", 1406e7a98675SKyle Moffett name, vers, (unsigned long long)mpic->paddr, num_possible_cpus()); 1407a959ff56SBenjamin Herrenschmidt printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", 1408a959ff56SBenjamin Herrenschmidt mpic->isu_size, mpic->isu_shift, mpic->isu_mask); 140914cf11afSPaul Mackerras 141014cf11afSPaul Mackerras mpic->next = mpics; 141114cf11afSPaul Mackerras mpics = mpic; 141214cf11afSPaul Mackerras 14133a7a7176SKyle Moffett if (!(mpic->flags & MPIC_SECONDARY)) { 141414cf11afSPaul Mackerras mpic_primary = mpic; 14150ebfff14SBenjamin Herrenschmidt irq_set_default_host(mpic->irqhost); 14160ebfff14SBenjamin Herrenschmidt } 141714cf11afSPaul Mackerras 141814cf11afSPaul Mackerras return mpic; 1419996983b7SKyle Moffett 1420996983b7SKyle Moffett err_of_node_put: 1421996983b7SKyle Moffett of_node_put(node); 1422996983b7SKyle Moffett return NULL; 142314cf11afSPaul Mackerras } 142414cf11afSPaul Mackerras 142514cf11afSPaul Mackerras void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, 1426a959ff56SBenjamin Herrenschmidt phys_addr_t paddr) 142714cf11afSPaul Mackerras { 142814cf11afSPaul Mackerras unsigned int isu_first = isu_num * mpic->isu_size; 142914cf11afSPaul Mackerras 143014cf11afSPaul Mackerras BUG_ON(isu_num >= MPIC_MAX_ISU); 143114cf11afSPaul Mackerras 1432c51242e7SKyle Moffett mpic_map(mpic, 14335a2642f6SBenjamin Herrenschmidt paddr, &mpic->isus[isu_num], 0, 14347233593bSZang Roy-r61911 MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); 14355a2642f6SBenjamin Herrenschmidt 143614cf11afSPaul Mackerras if ((isu_first + mpic->isu_size) > mpic->num_sources) 143714cf11afSPaul Mackerras mpic->num_sources = isu_first + mpic->isu_size; 143814cf11afSPaul Mackerras } 143914cf11afSPaul Mackerras 144014cf11afSPaul Mackerras void __init mpic_init(struct mpic *mpic) 144114cf11afSPaul Mackerras { 144209dc34a9SKyle Moffett int i, cpu; 144314cf11afSPaul Mackerras 144414cf11afSPaul Mackerras BUG_ON(mpic->num_sources == 0); 144514cf11afSPaul Mackerras 144614cf11afSPaul Mackerras printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources); 144714cf11afSPaul Mackerras 144814cf11afSPaul Mackerras /* Set current processor priority to max */ 14497233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 145014cf11afSPaul Mackerras 1451ea94187fSScott Wood /* Initialize timers to our reserved vectors and mask them for now */ 145214cf11afSPaul Mackerras for (i = 0; i < 4; i++) { 145314cf11afSPaul Mackerras mpic_write(mpic->tmregs, 14547233593bSZang Roy-r61911 i * MPIC_INFO(TIMER_STRIDE) + 1455ea94187fSScott Wood MPIC_INFO(TIMER_DESTINATION), 1456ea94187fSScott Wood 1 << hard_smp_processor_id()); 145714cf11afSPaul Mackerras mpic_write(mpic->tmregs, 14587233593bSZang Roy-r61911 i * MPIC_INFO(TIMER_STRIDE) + 14597233593bSZang Roy-r61911 MPIC_INFO(TIMER_VECTOR_PRI), 146014cf11afSPaul Mackerras MPIC_VECPRI_MASK | 1461ea94187fSScott Wood (9 << MPIC_VECPRI_PRIORITY_SHIFT) | 14627df2457dSOlof Johansson (mpic->timer_vecs[0] + i)); 146314cf11afSPaul Mackerras } 146414cf11afSPaul Mackerras 146514cf11afSPaul Mackerras /* Initialize IPIs to our reserved vectors and mark them disabled for now */ 146614cf11afSPaul Mackerras mpic_test_broken_ipi(mpic); 146714cf11afSPaul Mackerras for (i = 0; i < 4; i++) { 146814cf11afSPaul Mackerras mpic_ipi_write(i, 146914cf11afSPaul Mackerras MPIC_VECPRI_MASK | 147014cf11afSPaul Mackerras (10 << MPIC_VECPRI_PRIORITY_SHIFT) | 14717df2457dSOlof Johansson (mpic->ipi_vecs[0] + i)); 147214cf11afSPaul Mackerras } 147314cf11afSPaul Mackerras 14741beb6a7dSBenjamin Herrenschmidt /* Do the HT PIC fixups on U3 broken mpic */ 147514cf11afSPaul Mackerras DBG("MPIC flags: %x\n", mpic->flags); 1476be8bec56SKyle Moffett if ((mpic->flags & MPIC_U3_HT_IRQS) && !(mpic->flags & MPIC_SECONDARY)) { 14771beb6a7dSBenjamin Herrenschmidt mpic_scan_ht_pics(mpic); 147805af7bd2SMichael Ellerman mpic_u3msi_init(mpic); 147905af7bd2SMichael Ellerman } 148014cf11afSPaul Mackerras 148138958dd9SOlof Johansson mpic_pasemi_msi_init(mpic); 148238958dd9SOlof Johansson 1483d6a2639bSMeador Inge cpu = mpic_processor_id(mpic); 1484cc353c30SArnd Bergmann 1485dfec2202SMeador Inge if (!(mpic->flags & MPIC_NO_RESET)) { 148614cf11afSPaul Mackerras for (i = 0; i < mpic->num_sources; i++) { 148714cf11afSPaul Mackerras /* start with vector = source number, and masked */ 14886e99e458SBenjamin Herrenschmidt u32 vecpri = MPIC_VECPRI_MASK | i | 14896e99e458SBenjamin Herrenschmidt (8 << MPIC_VECPRI_PRIORITY_SHIFT); 149014cf11afSPaul Mackerras 14917fd72186SBenjamin Herrenschmidt /* check if protected */ 14927fd72186SBenjamin Herrenschmidt if (mpic->protected && test_bit(i, mpic->protected)) 14937fd72186SBenjamin Herrenschmidt continue; 149414cf11afSPaul Mackerras /* init hw */ 14957233593bSZang Roy-r61911 mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); 1496cc353c30SArnd Bergmann mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 1 << cpu); 149714cf11afSPaul Mackerras } 1498dfec2202SMeador Inge } 149914cf11afSPaul Mackerras 15007df2457dSOlof Johansson /* Init spurious vector */ 15017df2457dSOlof Johansson mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec); 150214cf11afSPaul Mackerras 15037233593bSZang Roy-r61911 /* Disable 8259 passthrough, if supported */ 15047233593bSZang Roy-r61911 if (!(mpic->flags & MPIC_NO_PTHROU_DIS)) 15057233593bSZang Roy-r61911 mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 15067233593bSZang Roy-r61911 mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 150714cf11afSPaul Mackerras | MPIC_GREG_GCONF_8259_PTHROU_DIS); 150814cf11afSPaul Mackerras 1509d87bf3beSOlof Johansson if (mpic->flags & MPIC_NO_BIAS) 1510d87bf3beSOlof Johansson mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 1511d87bf3beSOlof Johansson mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 1512d87bf3beSOlof Johansson | MPIC_GREG_GCONF_NO_BIAS); 1513d87bf3beSOlof Johansson 151414cf11afSPaul Mackerras /* Set current processor priority to 0 */ 15157233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 15163669e930SJohannes Berg 15173669e930SJohannes Berg #ifdef CONFIG_PM 15183669e930SJohannes Berg /* allocate memory to save mpic state */ 1519ea96025aSAnton Vorontsov mpic->save_data = kmalloc(mpic->num_sources * sizeof(*mpic->save_data), 1520ea96025aSAnton Vorontsov GFP_KERNEL); 15213669e930SJohannes Berg BUG_ON(mpic->save_data == NULL); 15223669e930SJohannes Berg #endif 152309dc34a9SKyle Moffett 152409dc34a9SKyle Moffett /* Check if this MPIC is chained from a parent interrupt controller */ 152509dc34a9SKyle Moffett if (mpic->flags & MPIC_SECONDARY) { 152609dc34a9SKyle Moffett int virq = irq_of_parse_and_map(mpic->node, 0); 152709dc34a9SKyle Moffett if (virq != NO_IRQ) { 152809dc34a9SKyle Moffett printk(KERN_INFO "%s: hooking up to IRQ %d\n", 152909dc34a9SKyle Moffett mpic->node->full_name, virq); 153009dc34a9SKyle Moffett irq_set_handler_data(virq, mpic); 153109dc34a9SKyle Moffett irq_set_chained_handler(virq, &mpic_cascade); 153209dc34a9SKyle Moffett } 153309dc34a9SKyle Moffett } 153414cf11afSPaul Mackerras } 153514cf11afSPaul Mackerras 1536868ea0c9SMark A. Greer void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio) 1537868ea0c9SMark A. Greer { 1538868ea0c9SMark A. Greer u32 v; 153914cf11afSPaul Mackerras 1540868ea0c9SMark A. Greer v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1); 1541868ea0c9SMark A. Greer v &= ~MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO_MASK; 1542868ea0c9SMark A. Greer v |= MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO(clock_ratio); 1543868ea0c9SMark A. Greer mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v); 1544868ea0c9SMark A. Greer } 1545868ea0c9SMark A. Greer 1546868ea0c9SMark A. Greer void __init mpic_set_serial_int(struct mpic *mpic, int enable) 1547868ea0c9SMark A. Greer { 1548ba1826e5SBenjamin Herrenschmidt unsigned long flags; 1549868ea0c9SMark A. Greer u32 v; 1550868ea0c9SMark A. Greer 1551203041adSThomas Gleixner raw_spin_lock_irqsave(&mpic_lock, flags); 1552868ea0c9SMark A. Greer v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1); 1553868ea0c9SMark A. Greer if (enable) 1554868ea0c9SMark A. Greer v |= MPIC_GREG_GLOBAL_CONF_1_SIE; 1555868ea0c9SMark A. Greer else 1556868ea0c9SMark A. Greer v &= ~MPIC_GREG_GLOBAL_CONF_1_SIE; 1557868ea0c9SMark A. Greer mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v); 1558203041adSThomas Gleixner raw_spin_unlock_irqrestore(&mpic_lock, flags); 1559868ea0c9SMark A. Greer } 156014cf11afSPaul Mackerras 156114cf11afSPaul Mackerras void mpic_irq_set_priority(unsigned int irq, unsigned int pri) 156214cf11afSPaul Mackerras { 1563d69a78d7STony Breeds struct mpic *mpic = mpic_find(irq); 1564476eb491SGrant Likely unsigned int src = virq_to_hw(irq); 156514cf11afSPaul Mackerras unsigned long flags; 156614cf11afSPaul Mackerras u32 reg; 156714cf11afSPaul Mackerras 156806a901c5SStephen Rothwell if (!mpic) 156906a901c5SStephen Rothwell return; 157006a901c5SStephen Rothwell 1571203041adSThomas Gleixner raw_spin_lock_irqsave(&mpic_lock, flags); 15723a2b4f7cSBenjamin Herrenschmidt if (mpic_is_ipi(mpic, src)) { 15737df2457dSOlof Johansson reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) & 1574e5356640SBenjamin Herrenschmidt ~MPIC_VECPRI_PRIORITY_MASK; 15757df2457dSOlof Johansson mpic_ipi_write(src - mpic->ipi_vecs[0], 157614cf11afSPaul Mackerras reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 15773a2b4f7cSBenjamin Herrenschmidt } else if (mpic_is_tm(mpic, src)) { 1578ea94187fSScott Wood reg = mpic_tm_read(src - mpic->timer_vecs[0]) & 1579ea94187fSScott Wood ~MPIC_VECPRI_PRIORITY_MASK; 1580ea94187fSScott Wood mpic_tm_write(src - mpic->timer_vecs[0], 1581ea94187fSScott Wood reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 158214cf11afSPaul Mackerras } else { 15837233593bSZang Roy-r61911 reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) 1584e5356640SBenjamin Herrenschmidt & ~MPIC_VECPRI_PRIORITY_MASK; 15857233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 158614cf11afSPaul Mackerras reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 158714cf11afSPaul Mackerras } 1588203041adSThomas Gleixner raw_spin_unlock_irqrestore(&mpic_lock, flags); 158914cf11afSPaul Mackerras } 159014cf11afSPaul Mackerras 159114cf11afSPaul Mackerras void mpic_setup_this_cpu(void) 159214cf11afSPaul Mackerras { 159314cf11afSPaul Mackerras #ifdef CONFIG_SMP 159414cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 159514cf11afSPaul Mackerras unsigned long flags; 159614cf11afSPaul Mackerras u32 msk = 1 << hard_smp_processor_id(); 159714cf11afSPaul Mackerras unsigned int i; 159814cf11afSPaul Mackerras 159914cf11afSPaul Mackerras BUG_ON(mpic == NULL); 160014cf11afSPaul Mackerras 160114cf11afSPaul Mackerras DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 160214cf11afSPaul Mackerras 1603203041adSThomas Gleixner raw_spin_lock_irqsave(&mpic_lock, flags); 160414cf11afSPaul Mackerras 160514cf11afSPaul Mackerras /* let the mpic know we want intrs. default affinity is 0xffffffff 160614cf11afSPaul Mackerras * until changed via /proc. That's how it's done on x86. If we want 160714cf11afSPaul Mackerras * it differently, then we should make sure we also change the default 1608a53da52fSIngo Molnar * values of irq_desc[].affinity in irq.c. 160914cf11afSPaul Mackerras */ 161014cf11afSPaul Mackerras if (distribute_irqs) { 161114cf11afSPaul Mackerras for (i = 0; i < mpic->num_sources ; i++) 16127233593bSZang Roy-r61911 mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 16137233593bSZang Roy-r61911 mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk); 161414cf11afSPaul Mackerras } 161514cf11afSPaul Mackerras 161614cf11afSPaul Mackerras /* Set current processor priority to 0 */ 16177233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 161814cf11afSPaul Mackerras 1619203041adSThomas Gleixner raw_spin_unlock_irqrestore(&mpic_lock, flags); 162014cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 162114cf11afSPaul Mackerras } 162214cf11afSPaul Mackerras 162314cf11afSPaul Mackerras int mpic_cpu_get_priority(void) 162414cf11afSPaul Mackerras { 162514cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 162614cf11afSPaul Mackerras 16277233593bSZang Roy-r61911 return mpic_cpu_read(MPIC_INFO(CPU_CURRENT_TASK_PRI)); 162814cf11afSPaul Mackerras } 162914cf11afSPaul Mackerras 163014cf11afSPaul Mackerras void mpic_cpu_set_priority(int prio) 163114cf11afSPaul Mackerras { 163214cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 163314cf11afSPaul Mackerras 163414cf11afSPaul Mackerras prio &= MPIC_CPU_TASKPRI_MASK; 16357233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), prio); 163614cf11afSPaul Mackerras } 163714cf11afSPaul Mackerras 163814cf11afSPaul Mackerras void mpic_teardown_this_cpu(int secondary) 163914cf11afSPaul Mackerras { 164014cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 164114cf11afSPaul Mackerras unsigned long flags; 164214cf11afSPaul Mackerras u32 msk = 1 << hard_smp_processor_id(); 164314cf11afSPaul Mackerras unsigned int i; 164414cf11afSPaul Mackerras 164514cf11afSPaul Mackerras BUG_ON(mpic == NULL); 164614cf11afSPaul Mackerras 164714cf11afSPaul Mackerras DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 1648203041adSThomas Gleixner raw_spin_lock_irqsave(&mpic_lock, flags); 164914cf11afSPaul Mackerras 165014cf11afSPaul Mackerras /* let the mpic know we don't want intrs. */ 165114cf11afSPaul Mackerras for (i = 0; i < mpic->num_sources ; i++) 16527233593bSZang Roy-r61911 mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 16537233593bSZang Roy-r61911 mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) & ~msk); 165414cf11afSPaul Mackerras 165514cf11afSPaul Mackerras /* Set current processor priority to max */ 16567233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 16577132799bSValentine Barshak /* We need to EOI the IPI since not all platforms reset the MPIC 16587132799bSValentine Barshak * on boot and new interrupts wouldn't get delivered otherwise. 16597132799bSValentine Barshak */ 16607132799bSValentine Barshak mpic_eoi(mpic); 166114cf11afSPaul Mackerras 1662203041adSThomas Gleixner raw_spin_unlock_irqrestore(&mpic_lock, flags); 166314cf11afSPaul Mackerras } 166414cf11afSPaul Mackerras 166514cf11afSPaul Mackerras 1666f365355eSOlof Johansson static unsigned int _mpic_get_one_irq(struct mpic *mpic, int reg) 166714cf11afSPaul Mackerras { 16680ebfff14SBenjamin Herrenschmidt u32 src; 166914cf11afSPaul Mackerras 1670f365355eSOlof Johansson src = mpic_cpu_read(reg) & MPIC_INFO(VECPRI_VECTOR_MASK); 16711beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_LOW 1672f365355eSOlof Johansson DBG("%s: get_one_irq(reg 0x%x): %d\n", mpic->name, reg, src); 16731beb6a7dSBenjamin Herrenschmidt #endif 16745cddd2e3SJosh Boyer if (unlikely(src == mpic->spurious_vec)) { 16755cddd2e3SJosh Boyer if (mpic->flags & MPIC_SPV_EOI) 16765cddd2e3SJosh Boyer mpic_eoi(mpic); 16770ebfff14SBenjamin Herrenschmidt return NO_IRQ; 16785cddd2e3SJosh Boyer } 16797fd72186SBenjamin Herrenschmidt if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { 168076462232SChristian Dietrich printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", 16817fd72186SBenjamin Herrenschmidt mpic->name, (int)src); 16827fd72186SBenjamin Herrenschmidt mpic_eoi(mpic); 16837fd72186SBenjamin Herrenschmidt return NO_IRQ; 16847fd72186SBenjamin Herrenschmidt } 16857fd72186SBenjamin Herrenschmidt 16860ebfff14SBenjamin Herrenschmidt return irq_linear_revmap(mpic->irqhost, src); 168714cf11afSPaul Mackerras } 168814cf11afSPaul Mackerras 1689f365355eSOlof Johansson unsigned int mpic_get_one_irq(struct mpic *mpic) 1690f365355eSOlof Johansson { 1691f365355eSOlof Johansson return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_INTACK)); 1692f365355eSOlof Johansson } 1693f365355eSOlof Johansson 169435a84c2fSOlaf Hering unsigned int mpic_get_irq(void) 169514cf11afSPaul Mackerras { 169614cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 169714cf11afSPaul Mackerras 169814cf11afSPaul Mackerras BUG_ON(mpic == NULL); 169914cf11afSPaul Mackerras 170035a84c2fSOlaf Hering return mpic_get_one_irq(mpic); 170114cf11afSPaul Mackerras } 170214cf11afSPaul Mackerras 1703d91e4ea7SKumar Gala unsigned int mpic_get_coreint_irq(void) 1704d91e4ea7SKumar Gala { 1705d91e4ea7SKumar Gala #ifdef CONFIG_BOOKE 1706d91e4ea7SKumar Gala struct mpic *mpic = mpic_primary; 1707d91e4ea7SKumar Gala u32 src; 1708d91e4ea7SKumar Gala 1709d91e4ea7SKumar Gala BUG_ON(mpic == NULL); 1710d91e4ea7SKumar Gala 1711d91e4ea7SKumar Gala src = mfspr(SPRN_EPR); 1712d91e4ea7SKumar Gala 1713d91e4ea7SKumar Gala if (unlikely(src == mpic->spurious_vec)) { 1714d91e4ea7SKumar Gala if (mpic->flags & MPIC_SPV_EOI) 1715d91e4ea7SKumar Gala mpic_eoi(mpic); 1716d91e4ea7SKumar Gala return NO_IRQ; 1717d91e4ea7SKumar Gala } 1718d91e4ea7SKumar Gala if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { 171976462232SChristian Dietrich printk_ratelimited(KERN_WARNING "%s: Got protected source %d !\n", 1720d91e4ea7SKumar Gala mpic->name, (int)src); 1721d91e4ea7SKumar Gala return NO_IRQ; 1722d91e4ea7SKumar Gala } 1723d91e4ea7SKumar Gala 1724d91e4ea7SKumar Gala return irq_linear_revmap(mpic->irqhost, src); 1725d91e4ea7SKumar Gala #else 1726d91e4ea7SKumar Gala return NO_IRQ; 1727d91e4ea7SKumar Gala #endif 1728d91e4ea7SKumar Gala } 1729d91e4ea7SKumar Gala 1730f365355eSOlof Johansson unsigned int mpic_get_mcirq(void) 1731f365355eSOlof Johansson { 1732f365355eSOlof Johansson struct mpic *mpic = mpic_primary; 1733f365355eSOlof Johansson 1734f365355eSOlof Johansson BUG_ON(mpic == NULL); 1735f365355eSOlof Johansson 1736f365355eSOlof Johansson return _mpic_get_one_irq(mpic, MPIC_INFO(CPU_MCACK)); 1737f365355eSOlof Johansson } 173814cf11afSPaul Mackerras 173914cf11afSPaul Mackerras #ifdef CONFIG_SMP 174014cf11afSPaul Mackerras void mpic_request_ipis(void) 174114cf11afSPaul Mackerras { 174214cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 174378608dd3SMilton Miller int i; 174414cf11afSPaul Mackerras BUG_ON(mpic == NULL); 174514cf11afSPaul Mackerras 17460ebfff14SBenjamin Herrenschmidt printk(KERN_INFO "mpic: requesting IPIs...\n"); 174714cf11afSPaul Mackerras 17480ebfff14SBenjamin Herrenschmidt for (i = 0; i < 4; i++) { 17490ebfff14SBenjamin Herrenschmidt unsigned int vipi = irq_create_mapping(mpic->irqhost, 17507df2457dSOlof Johansson mpic->ipi_vecs[0] + i); 17510ebfff14SBenjamin Herrenschmidt if (vipi == NO_IRQ) { 175278608dd3SMilton Miller printk(KERN_ERR "Failed to map %s\n", smp_ipi_name[i]); 175378608dd3SMilton Miller continue; 17540ebfff14SBenjamin Herrenschmidt } 175578608dd3SMilton Miller smp_request_message_ipi(vipi, i); 17560ebfff14SBenjamin Herrenschmidt } 175714cf11afSPaul Mackerras } 1758a9c59264SPaul Mackerras 17593caba98fSMilton Miller void smp_mpic_message_pass(int cpu, int msg) 17602ef613cbSBenjamin Herrenschmidt { 17612ef613cbSBenjamin Herrenschmidt struct mpic *mpic = mpic_primary; 17623caba98fSMilton Miller u32 physmask; 17632ef613cbSBenjamin Herrenschmidt 17642ef613cbSBenjamin Herrenschmidt BUG_ON(mpic == NULL); 17652ef613cbSBenjamin Herrenschmidt 1766a9c59264SPaul Mackerras /* make sure we're sending something that translates to an IPI */ 1767a9c59264SPaul Mackerras if ((unsigned int)msg > 3) { 1768a9c59264SPaul Mackerras printk("SMP %d: smp_message_pass: unknown msg %d\n", 1769a9c59264SPaul Mackerras smp_processor_id(), msg); 1770a9c59264SPaul Mackerras return; 1771a9c59264SPaul Mackerras } 17723caba98fSMilton Miller 17733caba98fSMilton Miller #ifdef DEBUG_IPI 17743caba98fSMilton Miller DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, msg); 17753caba98fSMilton Miller #endif 17763caba98fSMilton Miller 17773caba98fSMilton Miller physmask = 1 << get_hard_smp_processor_id(cpu); 17783caba98fSMilton Miller 17793caba98fSMilton Miller mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) + 17803caba98fSMilton Miller msg * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), physmask); 1781a9c59264SPaul Mackerras } 1782775aeff4SMichael Ellerman 1783775aeff4SMichael Ellerman int __init smp_mpic_probe(void) 1784775aeff4SMichael Ellerman { 1785775aeff4SMichael Ellerman int nr_cpus; 1786775aeff4SMichael Ellerman 1787775aeff4SMichael Ellerman DBG("smp_mpic_probe()...\n"); 1788775aeff4SMichael Ellerman 17892ef613cbSBenjamin Herrenschmidt nr_cpus = cpumask_weight(cpu_possible_mask); 1790775aeff4SMichael Ellerman 1791775aeff4SMichael Ellerman DBG("nr_cpus: %d\n", nr_cpus); 1792775aeff4SMichael Ellerman 1793775aeff4SMichael Ellerman if (nr_cpus > 1) 1794775aeff4SMichael Ellerman mpic_request_ipis(); 1795775aeff4SMichael Ellerman 1796775aeff4SMichael Ellerman return nr_cpus; 1797775aeff4SMichael Ellerman } 1798775aeff4SMichael Ellerman 1799775aeff4SMichael Ellerman void __devinit smp_mpic_setup_cpu(int cpu) 1800775aeff4SMichael Ellerman { 1801775aeff4SMichael Ellerman mpic_setup_this_cpu(); 1802775aeff4SMichael Ellerman } 180366953ebeSMatthew McClintock 180466953ebeSMatthew McClintock void mpic_reset_core(int cpu) 180566953ebeSMatthew McClintock { 180666953ebeSMatthew McClintock struct mpic *mpic = mpic_primary; 180766953ebeSMatthew McClintock u32 pir; 180866953ebeSMatthew McClintock int cpuid = get_hard_smp_processor_id(cpu); 180944f16fcfSMatthew McClintock int i; 181066953ebeSMatthew McClintock 181166953ebeSMatthew McClintock /* Set target bit for core reset */ 181266953ebeSMatthew McClintock pir = mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 181366953ebeSMatthew McClintock pir |= (1 << cpuid); 181466953ebeSMatthew McClintock mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); 181566953ebeSMatthew McClintock mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 181666953ebeSMatthew McClintock 181766953ebeSMatthew McClintock /* Restore target bit after reset complete */ 181866953ebeSMatthew McClintock pir &= ~(1 << cpuid); 181966953ebeSMatthew McClintock mpic_write(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT), pir); 182066953ebeSMatthew McClintock mpic_read(mpic->gregs, MPIC_INFO(GREG_PROCESSOR_INIT)); 182144f16fcfSMatthew McClintock 182244f16fcfSMatthew McClintock /* Perform 15 EOI on each reset core to clear pending interrupts. 182344f16fcfSMatthew McClintock * This is required for FSL CoreNet based devices */ 182444f16fcfSMatthew McClintock if (mpic->flags & MPIC_FSL) { 182544f16fcfSMatthew McClintock for (i = 0; i < 15; i++) { 182644f16fcfSMatthew McClintock _mpic_write(mpic->reg_type, &mpic->cpuregs[cpuid], 182744f16fcfSMatthew McClintock MPIC_CPU_EOI, 0); 182844f16fcfSMatthew McClintock } 182944f16fcfSMatthew McClintock } 183066953ebeSMatthew McClintock } 183114cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 18323669e930SJohannes Berg 18333669e930SJohannes Berg #ifdef CONFIG_PM 1834f5a592f7SRafael J. Wysocki static void mpic_suspend_one(struct mpic *mpic) 18353669e930SJohannes Berg { 18363669e930SJohannes Berg int i; 18373669e930SJohannes Berg 18383669e930SJohannes Berg for (i = 0; i < mpic->num_sources; i++) { 18393669e930SJohannes Berg mpic->save_data[i].vecprio = 18403669e930SJohannes Berg mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI)); 18413669e930SJohannes Berg mpic->save_data[i].dest = 18423669e930SJohannes Berg mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)); 18433669e930SJohannes Berg } 1844f5a592f7SRafael J. Wysocki } 1845f5a592f7SRafael J. Wysocki 1846f5a592f7SRafael J. Wysocki static int mpic_suspend(void) 1847f5a592f7SRafael J. Wysocki { 1848f5a592f7SRafael J. Wysocki struct mpic *mpic = mpics; 1849f5a592f7SRafael J. Wysocki 1850f5a592f7SRafael J. Wysocki while (mpic) { 1851f5a592f7SRafael J. Wysocki mpic_suspend_one(mpic); 1852f5a592f7SRafael J. Wysocki mpic = mpic->next; 1853f5a592f7SRafael J. Wysocki } 18543669e930SJohannes Berg 18553669e930SJohannes Berg return 0; 18563669e930SJohannes Berg } 18573669e930SJohannes Berg 1858f5a592f7SRafael J. Wysocki static void mpic_resume_one(struct mpic *mpic) 18593669e930SJohannes Berg { 18603669e930SJohannes Berg int i; 18613669e930SJohannes Berg 18623669e930SJohannes Berg for (i = 0; i < mpic->num_sources; i++) { 18633669e930SJohannes Berg mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), 18643669e930SJohannes Berg mpic->save_data[i].vecprio); 18653669e930SJohannes Berg mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 18663669e930SJohannes Berg mpic->save_data[i].dest); 18673669e930SJohannes Berg 18683669e930SJohannes Berg #ifdef CONFIG_MPIC_U3_HT_IRQS 18697c9d9360SAlastair Bridgewater if (mpic->fixups) { 18703669e930SJohannes Berg struct mpic_irq_fixup *fixup = &mpic->fixups[i]; 18713669e930SJohannes Berg 18723669e930SJohannes Berg if (fixup->base) { 18733669e930SJohannes Berg /* we use the lowest bit in an inverted meaning */ 18743669e930SJohannes Berg if ((mpic->save_data[i].fixup_data & 1) == 0) 18753669e930SJohannes Berg continue; 18763669e930SJohannes Berg 18773669e930SJohannes Berg /* Enable and configure */ 18783669e930SJohannes Berg writeb(0x10 + 2 * fixup->index, fixup->base + 2); 18793669e930SJohannes Berg 18803669e930SJohannes Berg writel(mpic->save_data[i].fixup_data & ~1, 18813669e930SJohannes Berg fixup->base + 4); 18823669e930SJohannes Berg } 18833669e930SJohannes Berg } 18843669e930SJohannes Berg #endif 18853669e930SJohannes Berg } /* end for loop */ 18863669e930SJohannes Berg } 18873669e930SJohannes Berg 1888f5a592f7SRafael J. Wysocki static void mpic_resume(void) 1889f5a592f7SRafael J. Wysocki { 1890f5a592f7SRafael J. Wysocki struct mpic *mpic = mpics; 1891f5a592f7SRafael J. Wysocki 1892f5a592f7SRafael J. Wysocki while (mpic) { 1893f5a592f7SRafael J. Wysocki mpic_resume_one(mpic); 1894f5a592f7SRafael J. Wysocki mpic = mpic->next; 1895f5a592f7SRafael J. Wysocki } 1896f5a592f7SRafael J. Wysocki } 1897f5a592f7SRafael J. Wysocki 1898f5a592f7SRafael J. Wysocki static struct syscore_ops mpic_syscore_ops = { 18993669e930SJohannes Berg .resume = mpic_resume, 19003669e930SJohannes Berg .suspend = mpic_suspend, 19013669e930SJohannes Berg }; 19023669e930SJohannes Berg 19033669e930SJohannes Berg static int mpic_init_sys(void) 19043669e930SJohannes Berg { 1905f5a592f7SRafael J. Wysocki register_syscore_ops(&mpic_syscore_ops); 1906f5a592f7SRafael J. Wysocki return 0; 19073669e930SJohannes Berg } 19083669e930SJohannes Berg 19093669e930SJohannes Berg device_initcall(mpic_init_sys); 1910f5a592f7SRafael J. Wysocki #endif 1911