114cf11afSPaul Mackerras /* 214cf11afSPaul Mackerras * arch/powerpc/kernel/mpic.c 314cf11afSPaul Mackerras * 414cf11afSPaul Mackerras * Driver for interrupt controllers following the OpenPIC standard, the 514cf11afSPaul Mackerras * common implementation beeing IBM's MPIC. This driver also can deal 614cf11afSPaul Mackerras * with various broken implementations of this HW. 714cf11afSPaul Mackerras * 814cf11afSPaul Mackerras * Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp. 914cf11afSPaul Mackerras * 1014cf11afSPaul Mackerras * This file is subject to the terms and conditions of the GNU General Public 1114cf11afSPaul Mackerras * License. See the file COPYING in the main directory of this archive 1214cf11afSPaul Mackerras * for more details. 1314cf11afSPaul Mackerras */ 1414cf11afSPaul Mackerras 1514cf11afSPaul Mackerras #undef DEBUG 161beb6a7dSBenjamin Herrenschmidt #undef DEBUG_IPI 171beb6a7dSBenjamin Herrenschmidt #undef DEBUG_IRQ 181beb6a7dSBenjamin Herrenschmidt #undef DEBUG_LOW 1914cf11afSPaul Mackerras 2014cf11afSPaul Mackerras #include <linux/types.h> 2114cf11afSPaul Mackerras #include <linux/kernel.h> 2214cf11afSPaul Mackerras #include <linux/init.h> 2314cf11afSPaul Mackerras #include <linux/irq.h> 2414cf11afSPaul Mackerras #include <linux/smp.h> 2514cf11afSPaul Mackerras #include <linux/interrupt.h> 2614cf11afSPaul Mackerras #include <linux/bootmem.h> 2714cf11afSPaul Mackerras #include <linux/spinlock.h> 2814cf11afSPaul Mackerras #include <linux/pci.h> 2914cf11afSPaul Mackerras 3014cf11afSPaul Mackerras #include <asm/ptrace.h> 3114cf11afSPaul Mackerras #include <asm/signal.h> 3214cf11afSPaul Mackerras #include <asm/io.h> 3314cf11afSPaul Mackerras #include <asm/pgtable.h> 3414cf11afSPaul Mackerras #include <asm/irq.h> 3514cf11afSPaul Mackerras #include <asm/machdep.h> 3614cf11afSPaul Mackerras #include <asm/mpic.h> 3714cf11afSPaul Mackerras #include <asm/smp.h> 3814cf11afSPaul Mackerras 39a7de7c74SMichael Ellerman #include "mpic.h" 40a7de7c74SMichael Ellerman 4114cf11afSPaul Mackerras #ifdef DEBUG 4214cf11afSPaul Mackerras #define DBG(fmt...) printk(fmt) 4314cf11afSPaul Mackerras #else 4414cf11afSPaul Mackerras #define DBG(fmt...) 4514cf11afSPaul Mackerras #endif 4614cf11afSPaul Mackerras 4714cf11afSPaul Mackerras static struct mpic *mpics; 4814cf11afSPaul Mackerras static struct mpic *mpic_primary; 4914cf11afSPaul Mackerras static DEFINE_SPINLOCK(mpic_lock); 5014cf11afSPaul Mackerras 51c0c0d996SPaul Mackerras #ifdef CONFIG_PPC32 /* XXX for now */ 52e40c7f02SAndy Whitcroft #ifdef CONFIG_IRQ_ALL_CPUS 53e40c7f02SAndy Whitcroft #define distribute_irqs (1) 54e40c7f02SAndy Whitcroft #else 55e40c7f02SAndy Whitcroft #define distribute_irqs (0) 56e40c7f02SAndy Whitcroft #endif 57c0c0d996SPaul Mackerras #endif 5814cf11afSPaul Mackerras 597233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD 607233593bSZang Roy-r61911 static u32 mpic_infos[][MPIC_IDX_END] = { 617233593bSZang Roy-r61911 [0] = { /* Original OpenPIC compatible MPIC */ 627233593bSZang Roy-r61911 MPIC_GREG_BASE, 637233593bSZang Roy-r61911 MPIC_GREG_FEATURE_0, 647233593bSZang Roy-r61911 MPIC_GREG_GLOBAL_CONF_0, 657233593bSZang Roy-r61911 MPIC_GREG_VENDOR_ID, 667233593bSZang Roy-r61911 MPIC_GREG_IPI_VECTOR_PRI_0, 677233593bSZang Roy-r61911 MPIC_GREG_IPI_STRIDE, 687233593bSZang Roy-r61911 MPIC_GREG_SPURIOUS, 697233593bSZang Roy-r61911 MPIC_GREG_TIMER_FREQ, 707233593bSZang Roy-r61911 717233593bSZang Roy-r61911 MPIC_TIMER_BASE, 727233593bSZang Roy-r61911 MPIC_TIMER_STRIDE, 737233593bSZang Roy-r61911 MPIC_TIMER_CURRENT_CNT, 747233593bSZang Roy-r61911 MPIC_TIMER_BASE_CNT, 757233593bSZang Roy-r61911 MPIC_TIMER_VECTOR_PRI, 767233593bSZang Roy-r61911 MPIC_TIMER_DESTINATION, 777233593bSZang Roy-r61911 787233593bSZang Roy-r61911 MPIC_CPU_BASE, 797233593bSZang Roy-r61911 MPIC_CPU_STRIDE, 807233593bSZang Roy-r61911 MPIC_CPU_IPI_DISPATCH_0, 817233593bSZang Roy-r61911 MPIC_CPU_IPI_DISPATCH_STRIDE, 827233593bSZang Roy-r61911 MPIC_CPU_CURRENT_TASK_PRI, 837233593bSZang Roy-r61911 MPIC_CPU_WHOAMI, 847233593bSZang Roy-r61911 MPIC_CPU_INTACK, 857233593bSZang Roy-r61911 MPIC_CPU_EOI, 867233593bSZang Roy-r61911 877233593bSZang Roy-r61911 MPIC_IRQ_BASE, 887233593bSZang Roy-r61911 MPIC_IRQ_STRIDE, 897233593bSZang Roy-r61911 MPIC_IRQ_VECTOR_PRI, 907233593bSZang Roy-r61911 MPIC_VECPRI_VECTOR_MASK, 917233593bSZang Roy-r61911 MPIC_VECPRI_POLARITY_POSITIVE, 927233593bSZang Roy-r61911 MPIC_VECPRI_POLARITY_NEGATIVE, 937233593bSZang Roy-r61911 MPIC_VECPRI_SENSE_LEVEL, 947233593bSZang Roy-r61911 MPIC_VECPRI_SENSE_EDGE, 957233593bSZang Roy-r61911 MPIC_VECPRI_POLARITY_MASK, 967233593bSZang Roy-r61911 MPIC_VECPRI_SENSE_MASK, 977233593bSZang Roy-r61911 MPIC_IRQ_DESTINATION 987233593bSZang Roy-r61911 }, 997233593bSZang Roy-r61911 [1] = { /* Tsi108/109 PIC */ 1007233593bSZang Roy-r61911 TSI108_GREG_BASE, 1017233593bSZang Roy-r61911 TSI108_GREG_FEATURE_0, 1027233593bSZang Roy-r61911 TSI108_GREG_GLOBAL_CONF_0, 1037233593bSZang Roy-r61911 TSI108_GREG_VENDOR_ID, 1047233593bSZang Roy-r61911 TSI108_GREG_IPI_VECTOR_PRI_0, 1057233593bSZang Roy-r61911 TSI108_GREG_IPI_STRIDE, 1067233593bSZang Roy-r61911 TSI108_GREG_SPURIOUS, 1077233593bSZang Roy-r61911 TSI108_GREG_TIMER_FREQ, 1087233593bSZang Roy-r61911 1097233593bSZang Roy-r61911 TSI108_TIMER_BASE, 1107233593bSZang Roy-r61911 TSI108_TIMER_STRIDE, 1117233593bSZang Roy-r61911 TSI108_TIMER_CURRENT_CNT, 1127233593bSZang Roy-r61911 TSI108_TIMER_BASE_CNT, 1137233593bSZang Roy-r61911 TSI108_TIMER_VECTOR_PRI, 1147233593bSZang Roy-r61911 TSI108_TIMER_DESTINATION, 1157233593bSZang Roy-r61911 1167233593bSZang Roy-r61911 TSI108_CPU_BASE, 1177233593bSZang Roy-r61911 TSI108_CPU_STRIDE, 1187233593bSZang Roy-r61911 TSI108_CPU_IPI_DISPATCH_0, 1197233593bSZang Roy-r61911 TSI108_CPU_IPI_DISPATCH_STRIDE, 1207233593bSZang Roy-r61911 TSI108_CPU_CURRENT_TASK_PRI, 1217233593bSZang Roy-r61911 TSI108_CPU_WHOAMI, 1227233593bSZang Roy-r61911 TSI108_CPU_INTACK, 1237233593bSZang Roy-r61911 TSI108_CPU_EOI, 1247233593bSZang Roy-r61911 1257233593bSZang Roy-r61911 TSI108_IRQ_BASE, 1267233593bSZang Roy-r61911 TSI108_IRQ_STRIDE, 1277233593bSZang Roy-r61911 TSI108_IRQ_VECTOR_PRI, 1287233593bSZang Roy-r61911 TSI108_VECPRI_VECTOR_MASK, 1297233593bSZang Roy-r61911 TSI108_VECPRI_POLARITY_POSITIVE, 1307233593bSZang Roy-r61911 TSI108_VECPRI_POLARITY_NEGATIVE, 1317233593bSZang Roy-r61911 TSI108_VECPRI_SENSE_LEVEL, 1327233593bSZang Roy-r61911 TSI108_VECPRI_SENSE_EDGE, 1337233593bSZang Roy-r61911 TSI108_VECPRI_POLARITY_MASK, 1347233593bSZang Roy-r61911 TSI108_VECPRI_SENSE_MASK, 1357233593bSZang Roy-r61911 TSI108_IRQ_DESTINATION 1367233593bSZang Roy-r61911 }, 1377233593bSZang Roy-r61911 }; 1387233593bSZang Roy-r61911 1397233593bSZang Roy-r61911 #define MPIC_INFO(name) mpic->hw_set[MPIC_IDX_##name] 1407233593bSZang Roy-r61911 1417233593bSZang Roy-r61911 #else /* CONFIG_MPIC_WEIRD */ 1427233593bSZang Roy-r61911 1437233593bSZang Roy-r61911 #define MPIC_INFO(name) MPIC_##name 1447233593bSZang Roy-r61911 1457233593bSZang Roy-r61911 #endif /* CONFIG_MPIC_WEIRD */ 1467233593bSZang Roy-r61911 14714cf11afSPaul Mackerras /* 14814cf11afSPaul Mackerras * Register accessor functions 14914cf11afSPaul Mackerras */ 15014cf11afSPaul Mackerras 15114cf11afSPaul Mackerras 152fbf0274eSBenjamin Herrenschmidt static inline u32 _mpic_read(enum mpic_reg_type type, 153fbf0274eSBenjamin Herrenschmidt struct mpic_reg_bank *rb, 15414cf11afSPaul Mackerras unsigned int reg) 15514cf11afSPaul Mackerras { 156fbf0274eSBenjamin Herrenschmidt switch(type) { 157fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 158fbf0274eSBenjamin Herrenschmidt case mpic_access_dcr: 159fbf0274eSBenjamin Herrenschmidt return dcr_read(rb->dhost, 160fbf0274eSBenjamin Herrenschmidt rb->dbase + reg + rb->doff); 161fbf0274eSBenjamin Herrenschmidt #endif 162fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_be: 163fbf0274eSBenjamin Herrenschmidt return in_be32(rb->base + (reg >> 2)); 164fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_le: 165fbf0274eSBenjamin Herrenschmidt default: 166fbf0274eSBenjamin Herrenschmidt return in_le32(rb->base + (reg >> 2)); 167fbf0274eSBenjamin Herrenschmidt } 16814cf11afSPaul Mackerras } 16914cf11afSPaul Mackerras 170fbf0274eSBenjamin Herrenschmidt static inline void _mpic_write(enum mpic_reg_type type, 171fbf0274eSBenjamin Herrenschmidt struct mpic_reg_bank *rb, 17214cf11afSPaul Mackerras unsigned int reg, u32 value) 17314cf11afSPaul Mackerras { 174fbf0274eSBenjamin Herrenschmidt switch(type) { 175fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 176fbf0274eSBenjamin Herrenschmidt case mpic_access_dcr: 177fbf0274eSBenjamin Herrenschmidt return dcr_write(rb->dhost, 178fbf0274eSBenjamin Herrenschmidt rb->dbase + reg + rb->doff, value); 179fbf0274eSBenjamin Herrenschmidt #endif 180fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_be: 181fbf0274eSBenjamin Herrenschmidt return out_be32(rb->base + (reg >> 2), value); 182fbf0274eSBenjamin Herrenschmidt case mpic_access_mmio_le: 183fbf0274eSBenjamin Herrenschmidt default: 184fbf0274eSBenjamin Herrenschmidt return out_le32(rb->base + (reg >> 2), value); 185fbf0274eSBenjamin Herrenschmidt } 18614cf11afSPaul Mackerras } 18714cf11afSPaul Mackerras 18814cf11afSPaul Mackerras static inline u32 _mpic_ipi_read(struct mpic *mpic, unsigned int ipi) 18914cf11afSPaul Mackerras { 190fbf0274eSBenjamin Herrenschmidt enum mpic_reg_type type = mpic->reg_type; 1917233593bSZang Roy-r61911 unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 1927233593bSZang Roy-r61911 (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 19314cf11afSPaul Mackerras 194fbf0274eSBenjamin Herrenschmidt if ((mpic->flags & MPIC_BROKEN_IPI) && type == mpic_access_mmio_le) 195fbf0274eSBenjamin Herrenschmidt type = mpic_access_mmio_be; 196fbf0274eSBenjamin Herrenschmidt return _mpic_read(type, &mpic->gregs, offset); 19714cf11afSPaul Mackerras } 19814cf11afSPaul Mackerras 19914cf11afSPaul Mackerras static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 value) 20014cf11afSPaul Mackerras { 2017233593bSZang Roy-r61911 unsigned int offset = MPIC_INFO(GREG_IPI_VECTOR_PRI_0) + 2027233593bSZang Roy-r61911 (ipi * MPIC_INFO(GREG_IPI_STRIDE)); 20314cf11afSPaul Mackerras 204fbf0274eSBenjamin Herrenschmidt _mpic_write(mpic->reg_type, &mpic->gregs, offset, value); 20514cf11afSPaul Mackerras } 20614cf11afSPaul Mackerras 20714cf11afSPaul Mackerras static inline u32 _mpic_cpu_read(struct mpic *mpic, unsigned int reg) 20814cf11afSPaul Mackerras { 20914cf11afSPaul Mackerras unsigned int cpu = 0; 21014cf11afSPaul Mackerras 21114cf11afSPaul Mackerras if (mpic->flags & MPIC_PRIMARY) 21214cf11afSPaul Mackerras cpu = hard_smp_processor_id(); 213fbf0274eSBenjamin Herrenschmidt return _mpic_read(mpic->reg_type, &mpic->cpuregs[cpu], reg); 21414cf11afSPaul Mackerras } 21514cf11afSPaul Mackerras 21614cf11afSPaul Mackerras static inline void _mpic_cpu_write(struct mpic *mpic, unsigned int reg, u32 value) 21714cf11afSPaul Mackerras { 21814cf11afSPaul Mackerras unsigned int cpu = 0; 21914cf11afSPaul Mackerras 22014cf11afSPaul Mackerras if (mpic->flags & MPIC_PRIMARY) 22114cf11afSPaul Mackerras cpu = hard_smp_processor_id(); 22214cf11afSPaul Mackerras 223fbf0274eSBenjamin Herrenschmidt _mpic_write(mpic->reg_type, &mpic->cpuregs[cpu], reg, value); 22414cf11afSPaul Mackerras } 22514cf11afSPaul Mackerras 22614cf11afSPaul Mackerras static inline u32 _mpic_irq_read(struct mpic *mpic, unsigned int src_no, unsigned int reg) 22714cf11afSPaul Mackerras { 22814cf11afSPaul Mackerras unsigned int isu = src_no >> mpic->isu_shift; 22914cf11afSPaul Mackerras unsigned int idx = src_no & mpic->isu_mask; 23014cf11afSPaul Mackerras 231*0d72ba93SOlof Johansson #ifdef CONFIG_MPIC_BROKEN_REGREAD 232*0d72ba93SOlof Johansson if (reg == 0) 233*0d72ba93SOlof Johansson return mpic->isu_reg0_shadow[idx]; 234*0d72ba93SOlof Johansson else 235*0d72ba93SOlof Johansson #endif 236fbf0274eSBenjamin Herrenschmidt return _mpic_read(mpic->reg_type, &mpic->isus[isu], 2377233593bSZang Roy-r61911 reg + (idx * MPIC_INFO(IRQ_STRIDE))); 23814cf11afSPaul Mackerras } 23914cf11afSPaul Mackerras 24014cf11afSPaul Mackerras static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no, 24114cf11afSPaul Mackerras unsigned int reg, u32 value) 24214cf11afSPaul Mackerras { 24314cf11afSPaul Mackerras unsigned int isu = src_no >> mpic->isu_shift; 24414cf11afSPaul Mackerras unsigned int idx = src_no & mpic->isu_mask; 24514cf11afSPaul Mackerras 246fbf0274eSBenjamin Herrenschmidt _mpic_write(mpic->reg_type, &mpic->isus[isu], 2477233593bSZang Roy-r61911 reg + (idx * MPIC_INFO(IRQ_STRIDE)), value); 248*0d72ba93SOlof Johansson 249*0d72ba93SOlof Johansson #ifdef CONFIG_MPIC_BROKEN_REGREAD 250*0d72ba93SOlof Johansson if (reg == 0) 251*0d72ba93SOlof Johansson mpic->isu_reg0_shadow[idx] = value; 252*0d72ba93SOlof Johansson #endif 25314cf11afSPaul Mackerras } 25414cf11afSPaul Mackerras 255fbf0274eSBenjamin Herrenschmidt #define mpic_read(b,r) _mpic_read(mpic->reg_type,&(b),(r)) 256fbf0274eSBenjamin Herrenschmidt #define mpic_write(b,r,v) _mpic_write(mpic->reg_type,&(b),(r),(v)) 25714cf11afSPaul Mackerras #define mpic_ipi_read(i) _mpic_ipi_read(mpic,(i)) 25814cf11afSPaul Mackerras #define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v)) 25914cf11afSPaul Mackerras #define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i)) 26014cf11afSPaul Mackerras #define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v)) 26114cf11afSPaul Mackerras #define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r)) 26214cf11afSPaul Mackerras #define mpic_irq_write(s,r,v) _mpic_irq_write(mpic,(s),(r),(v)) 26314cf11afSPaul Mackerras 26414cf11afSPaul Mackerras 26514cf11afSPaul Mackerras /* 26614cf11afSPaul Mackerras * Low level utility functions 26714cf11afSPaul Mackerras */ 26814cf11afSPaul Mackerras 26914cf11afSPaul Mackerras 270fbf0274eSBenjamin Herrenschmidt static void _mpic_map_mmio(struct mpic *mpic, unsigned long phys_addr, 271fbf0274eSBenjamin Herrenschmidt struct mpic_reg_bank *rb, unsigned int offset, 272fbf0274eSBenjamin Herrenschmidt unsigned int size) 273fbf0274eSBenjamin Herrenschmidt { 274fbf0274eSBenjamin Herrenschmidt rb->base = ioremap(phys_addr + offset, size); 275fbf0274eSBenjamin Herrenschmidt BUG_ON(rb->base == NULL); 276fbf0274eSBenjamin Herrenschmidt } 277fbf0274eSBenjamin Herrenschmidt 278fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 279fbf0274eSBenjamin Herrenschmidt static void _mpic_map_dcr(struct mpic *mpic, struct mpic_reg_bank *rb, 280fbf0274eSBenjamin Herrenschmidt unsigned int offset, unsigned int size) 281fbf0274eSBenjamin Herrenschmidt { 282fbf0274eSBenjamin Herrenschmidt rb->dbase = mpic->dcr_base; 283fbf0274eSBenjamin Herrenschmidt rb->doff = offset; 28452964f87SMichael Ellerman rb->dhost = dcr_map(mpic->irqhost->of_node, rb->dbase + rb->doff, size); 285fbf0274eSBenjamin Herrenschmidt BUG_ON(!DCR_MAP_OK(rb->dhost)); 286fbf0274eSBenjamin Herrenschmidt } 287fbf0274eSBenjamin Herrenschmidt 288fbf0274eSBenjamin Herrenschmidt static inline void mpic_map(struct mpic *mpic, unsigned long phys_addr, 289fbf0274eSBenjamin Herrenschmidt struct mpic_reg_bank *rb, unsigned int offset, 290fbf0274eSBenjamin Herrenschmidt unsigned int size) 291fbf0274eSBenjamin Herrenschmidt { 292fbf0274eSBenjamin Herrenschmidt if (mpic->flags & MPIC_USES_DCR) 293fbf0274eSBenjamin Herrenschmidt _mpic_map_dcr(mpic, rb, offset, size); 294fbf0274eSBenjamin Herrenschmidt else 295fbf0274eSBenjamin Herrenschmidt _mpic_map_mmio(mpic, phys_addr, rb, offset, size); 296fbf0274eSBenjamin Herrenschmidt } 297fbf0274eSBenjamin Herrenschmidt #else /* CONFIG_PPC_DCR */ 298fbf0274eSBenjamin Herrenschmidt #define mpic_map(m,p,b,o,s) _mpic_map_mmio(m,p,b,o,s) 299fbf0274eSBenjamin Herrenschmidt #endif /* !CONFIG_PPC_DCR */ 300fbf0274eSBenjamin Herrenschmidt 301fbf0274eSBenjamin Herrenschmidt 30214cf11afSPaul Mackerras 30314cf11afSPaul Mackerras /* Check if we have one of those nice broken MPICs with a flipped endian on 30414cf11afSPaul Mackerras * reads from IPI registers 30514cf11afSPaul Mackerras */ 30614cf11afSPaul Mackerras static void __init mpic_test_broken_ipi(struct mpic *mpic) 30714cf11afSPaul Mackerras { 30814cf11afSPaul Mackerras u32 r; 30914cf11afSPaul Mackerras 3107233593bSZang Roy-r61911 mpic_write(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0), MPIC_VECPRI_MASK); 3117233593bSZang Roy-r61911 r = mpic_read(mpic->gregs, MPIC_INFO(GREG_IPI_VECTOR_PRI_0)); 31214cf11afSPaul Mackerras 31314cf11afSPaul Mackerras if (r == le32_to_cpu(MPIC_VECPRI_MASK)) { 31414cf11afSPaul Mackerras printk(KERN_INFO "mpic: Detected reversed IPI registers\n"); 31514cf11afSPaul Mackerras mpic->flags |= MPIC_BROKEN_IPI; 31614cf11afSPaul Mackerras } 31714cf11afSPaul Mackerras } 31814cf11afSPaul Mackerras 3196cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 32014cf11afSPaul Mackerras 32114cf11afSPaul Mackerras /* Test if an interrupt is sourced from HyperTransport (used on broken U3s) 32214cf11afSPaul Mackerras * to force the edge setting on the MPIC and do the ack workaround. 32314cf11afSPaul Mackerras */ 3241beb6a7dSBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 32514cf11afSPaul Mackerras { 3261beb6a7dSBenjamin Herrenschmidt if (source >= 128 || !mpic->fixups) 32714cf11afSPaul Mackerras return 0; 3281beb6a7dSBenjamin Herrenschmidt return mpic->fixups[source].base != NULL; 32914cf11afSPaul Mackerras } 33014cf11afSPaul Mackerras 331c4b22f26SSegher Boessenkool 3321beb6a7dSBenjamin Herrenschmidt static inline void mpic_ht_end_irq(struct mpic *mpic, unsigned int source) 33314cf11afSPaul Mackerras { 3341beb6a7dSBenjamin Herrenschmidt struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 33514cf11afSPaul Mackerras 3361beb6a7dSBenjamin Herrenschmidt if (fixup->applebase) { 3371beb6a7dSBenjamin Herrenschmidt unsigned int soff = (fixup->index >> 3) & ~3; 3381beb6a7dSBenjamin Herrenschmidt unsigned int mask = 1U << (fixup->index & 0x1f); 3391beb6a7dSBenjamin Herrenschmidt writel(mask, fixup->applebase + soff); 3401beb6a7dSBenjamin Herrenschmidt } else { 34114cf11afSPaul Mackerras spin_lock(&mpic->fixup_lock); 3421beb6a7dSBenjamin Herrenschmidt writeb(0x11 + 2 * fixup->index, fixup->base + 2); 343c4b22f26SSegher Boessenkool writel(fixup->data, fixup->base + 4); 34414cf11afSPaul Mackerras spin_unlock(&mpic->fixup_lock); 34514cf11afSPaul Mackerras } 3461beb6a7dSBenjamin Herrenschmidt } 34714cf11afSPaul Mackerras 3481beb6a7dSBenjamin Herrenschmidt static void mpic_startup_ht_interrupt(struct mpic *mpic, unsigned int source, 3491beb6a7dSBenjamin Herrenschmidt unsigned int irqflags) 3501beb6a7dSBenjamin Herrenschmidt { 3511beb6a7dSBenjamin Herrenschmidt struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 3521beb6a7dSBenjamin Herrenschmidt unsigned long flags; 3531beb6a7dSBenjamin Herrenschmidt u32 tmp; 35414cf11afSPaul Mackerras 3551beb6a7dSBenjamin Herrenschmidt if (fixup->base == NULL) 3561beb6a7dSBenjamin Herrenschmidt return; 3571beb6a7dSBenjamin Herrenschmidt 35806fe98e6SBenjamin Herrenschmidt DBG("startup_ht_interrupt(0x%x, 0x%x) index: %d\n", 3591beb6a7dSBenjamin Herrenschmidt source, irqflags, fixup->index); 3601beb6a7dSBenjamin Herrenschmidt spin_lock_irqsave(&mpic->fixup_lock, flags); 3611beb6a7dSBenjamin Herrenschmidt /* Enable and configure */ 3621beb6a7dSBenjamin Herrenschmidt writeb(0x10 + 2 * fixup->index, fixup->base + 2); 3631beb6a7dSBenjamin Herrenschmidt tmp = readl(fixup->base + 4); 3641beb6a7dSBenjamin Herrenschmidt tmp &= ~(0x23U); 3651beb6a7dSBenjamin Herrenschmidt if (irqflags & IRQ_LEVEL) 3661beb6a7dSBenjamin Herrenschmidt tmp |= 0x22; 3671beb6a7dSBenjamin Herrenschmidt writel(tmp, fixup->base + 4); 3681beb6a7dSBenjamin Herrenschmidt spin_unlock_irqrestore(&mpic->fixup_lock, flags); 3693669e930SJohannes Berg 3703669e930SJohannes Berg #ifdef CONFIG_PM 3713669e930SJohannes Berg /* use the lowest bit inverted to the actual HW, 3723669e930SJohannes Berg * set if this fixup was enabled, clear otherwise */ 3733669e930SJohannes Berg mpic->save_data[source].fixup_data = tmp | 1; 3743669e930SJohannes Berg #endif 3751beb6a7dSBenjamin Herrenschmidt } 3761beb6a7dSBenjamin Herrenschmidt 3771beb6a7dSBenjamin Herrenschmidt static void mpic_shutdown_ht_interrupt(struct mpic *mpic, unsigned int source, 3781beb6a7dSBenjamin Herrenschmidt unsigned int irqflags) 3791beb6a7dSBenjamin Herrenschmidt { 3801beb6a7dSBenjamin Herrenschmidt struct mpic_irq_fixup *fixup = &mpic->fixups[source]; 3811beb6a7dSBenjamin Herrenschmidt unsigned long flags; 3821beb6a7dSBenjamin Herrenschmidt u32 tmp; 3831beb6a7dSBenjamin Herrenschmidt 3841beb6a7dSBenjamin Herrenschmidt if (fixup->base == NULL) 3851beb6a7dSBenjamin Herrenschmidt return; 3861beb6a7dSBenjamin Herrenschmidt 38706fe98e6SBenjamin Herrenschmidt DBG("shutdown_ht_interrupt(0x%x, 0x%x)\n", source, irqflags); 3881beb6a7dSBenjamin Herrenschmidt 3891beb6a7dSBenjamin Herrenschmidt /* Disable */ 3901beb6a7dSBenjamin Herrenschmidt spin_lock_irqsave(&mpic->fixup_lock, flags); 3911beb6a7dSBenjamin Herrenschmidt writeb(0x10 + 2 * fixup->index, fixup->base + 2); 3921beb6a7dSBenjamin Herrenschmidt tmp = readl(fixup->base + 4); 39372b13819SSegher Boessenkool tmp |= 1; 3941beb6a7dSBenjamin Herrenschmidt writel(tmp, fixup->base + 4); 3951beb6a7dSBenjamin Herrenschmidt spin_unlock_irqrestore(&mpic->fixup_lock, flags); 3963669e930SJohannes Berg 3973669e930SJohannes Berg #ifdef CONFIG_PM 3983669e930SJohannes Berg /* use the lowest bit inverted to the actual HW, 3993669e930SJohannes Berg * set if this fixup was enabled, clear otherwise */ 4003669e930SJohannes Berg mpic->save_data[source].fixup_data = tmp & ~1; 4013669e930SJohannes Berg #endif 4021beb6a7dSBenjamin Herrenschmidt } 4031beb6a7dSBenjamin Herrenschmidt 404812fd1fdSMichael Ellerman #ifdef CONFIG_PCI_MSI 405812fd1fdSMichael Ellerman static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 406812fd1fdSMichael Ellerman unsigned int devfn) 407812fd1fdSMichael Ellerman { 408812fd1fdSMichael Ellerman u8 __iomem *base; 409812fd1fdSMichael Ellerman u8 pos, flags; 410812fd1fdSMichael Ellerman u64 addr = 0; 411812fd1fdSMichael Ellerman 412812fd1fdSMichael Ellerman for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 413812fd1fdSMichael Ellerman pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 414812fd1fdSMichael Ellerman u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 415812fd1fdSMichael Ellerman if (id == PCI_CAP_ID_HT) { 416812fd1fdSMichael Ellerman id = readb(devbase + pos + 3); 417812fd1fdSMichael Ellerman if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_MSI_MAPPING) 418812fd1fdSMichael Ellerman break; 419812fd1fdSMichael Ellerman } 420812fd1fdSMichael Ellerman } 421812fd1fdSMichael Ellerman 422812fd1fdSMichael Ellerman if (pos == 0) 423812fd1fdSMichael Ellerman return; 424812fd1fdSMichael Ellerman 425812fd1fdSMichael Ellerman base = devbase + pos; 426812fd1fdSMichael Ellerman 427812fd1fdSMichael Ellerman flags = readb(base + HT_MSI_FLAGS); 428812fd1fdSMichael Ellerman if (!(flags & HT_MSI_FLAGS_FIXED)) { 429812fd1fdSMichael Ellerman addr = readl(base + HT_MSI_ADDR_LO) & HT_MSI_ADDR_LO_MASK; 430812fd1fdSMichael Ellerman addr = addr | ((u64)readl(base + HT_MSI_ADDR_HI) << 32); 431812fd1fdSMichael Ellerman } 432812fd1fdSMichael Ellerman 433812fd1fdSMichael Ellerman printk(KERN_DEBUG "mpic: - HT:%02x.%x %s MSI mapping found @ 0x%lx\n", 434812fd1fdSMichael Ellerman PCI_SLOT(devfn), PCI_FUNC(devfn), 435812fd1fdSMichael Ellerman flags & HT_MSI_FLAGS_ENABLE ? "enabled" : "disabled", addr); 436812fd1fdSMichael Ellerman 437812fd1fdSMichael Ellerman if (!(flags & HT_MSI_FLAGS_ENABLE)) 438812fd1fdSMichael Ellerman writeb(flags | HT_MSI_FLAGS_ENABLE, base + HT_MSI_FLAGS); 439812fd1fdSMichael Ellerman } 440812fd1fdSMichael Ellerman #else 441812fd1fdSMichael Ellerman static void __init mpic_scan_ht_msi(struct mpic *mpic, u8 __iomem *devbase, 442812fd1fdSMichael Ellerman unsigned int devfn) 443812fd1fdSMichael Ellerman { 444812fd1fdSMichael Ellerman return; 445812fd1fdSMichael Ellerman } 446812fd1fdSMichael Ellerman #endif 447812fd1fdSMichael Ellerman 4481beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pic(struct mpic *mpic, u8 __iomem *devbase, 4491beb6a7dSBenjamin Herrenschmidt unsigned int devfn, u32 vdid) 45014cf11afSPaul Mackerras { 451c4b22f26SSegher Boessenkool int i, irq, n; 4521beb6a7dSBenjamin Herrenschmidt u8 __iomem *base; 45314cf11afSPaul Mackerras u32 tmp; 454c4b22f26SSegher Boessenkool u8 pos; 45514cf11afSPaul Mackerras 4561beb6a7dSBenjamin Herrenschmidt for (pos = readb(devbase + PCI_CAPABILITY_LIST); pos != 0; 4571beb6a7dSBenjamin Herrenschmidt pos = readb(devbase + pos + PCI_CAP_LIST_NEXT)) { 4581beb6a7dSBenjamin Herrenschmidt u8 id = readb(devbase + pos + PCI_CAP_LIST_ID); 45946ff3463SBrice Goglin if (id == PCI_CAP_ID_HT) { 460c4b22f26SSegher Boessenkool id = readb(devbase + pos + 3); 461beb7cc82SMichael Ellerman if ((id & HT_5BIT_CAP_MASK) == HT_CAPTYPE_IRQ) 462c4b22f26SSegher Boessenkool break; 463c4b22f26SSegher Boessenkool } 464c4b22f26SSegher Boessenkool } 465c4b22f26SSegher Boessenkool if (pos == 0) 466c4b22f26SSegher Boessenkool return; 467c4b22f26SSegher Boessenkool 4681beb6a7dSBenjamin Herrenschmidt base = devbase + pos; 4691beb6a7dSBenjamin Herrenschmidt writeb(0x01, base + 2); 4701beb6a7dSBenjamin Herrenschmidt n = (readl(base + 4) >> 16) & 0xff; 471c4b22f26SSegher Boessenkool 4721beb6a7dSBenjamin Herrenschmidt printk(KERN_INFO "mpic: - HT:%02x.%x [0x%02x] vendor %04x device %04x" 4731beb6a7dSBenjamin Herrenschmidt " has %d irqs\n", 4741beb6a7dSBenjamin Herrenschmidt devfn >> 3, devfn & 0x7, pos, vdid & 0xffff, vdid >> 16, n + 1); 475c4b22f26SSegher Boessenkool 476c4b22f26SSegher Boessenkool for (i = 0; i <= n; i++) { 4771beb6a7dSBenjamin Herrenschmidt writeb(0x10 + 2 * i, base + 2); 4781beb6a7dSBenjamin Herrenschmidt tmp = readl(base + 4); 47914cf11afSPaul Mackerras irq = (tmp >> 16) & 0xff; 4801beb6a7dSBenjamin Herrenschmidt DBG("HT PIC index 0x%x, irq 0x%x, tmp: %08x\n", i, irq, tmp); 4811beb6a7dSBenjamin Herrenschmidt /* mask it , will be unmasked later */ 4821beb6a7dSBenjamin Herrenschmidt tmp |= 0x1; 4831beb6a7dSBenjamin Herrenschmidt writel(tmp, base + 4); 4841beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].index = i; 4851beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].base = base; 4861beb6a7dSBenjamin Herrenschmidt /* Apple HT PIC has a non-standard way of doing EOIs */ 4871beb6a7dSBenjamin Herrenschmidt if ((vdid & 0xffff) == 0x106b) 4881beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].applebase = devbase + 0x60; 4891beb6a7dSBenjamin Herrenschmidt else 4901beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].applebase = NULL; 4911beb6a7dSBenjamin Herrenschmidt writeb(0x11 + 2 * i, base + 2); 4921beb6a7dSBenjamin Herrenschmidt mpic->fixups[irq].data = readl(base + 4) | 0x80000000; 49314cf11afSPaul Mackerras } 49414cf11afSPaul Mackerras } 49514cf11afSPaul Mackerras 49614cf11afSPaul Mackerras 4971beb6a7dSBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic) 49814cf11afSPaul Mackerras { 49914cf11afSPaul Mackerras unsigned int devfn; 50014cf11afSPaul Mackerras u8 __iomem *cfgspace; 50114cf11afSPaul Mackerras 5021beb6a7dSBenjamin Herrenschmidt printk(KERN_INFO "mpic: Setting up HT PICs workarounds for U3/U4\n"); 50314cf11afSPaul Mackerras 50414cf11afSPaul Mackerras /* Allocate fixups array */ 50514cf11afSPaul Mackerras mpic->fixups = alloc_bootmem(128 * sizeof(struct mpic_irq_fixup)); 50614cf11afSPaul Mackerras BUG_ON(mpic->fixups == NULL); 50714cf11afSPaul Mackerras memset(mpic->fixups, 0, 128 * sizeof(struct mpic_irq_fixup)); 50814cf11afSPaul Mackerras 50914cf11afSPaul Mackerras /* Init spinlock */ 51014cf11afSPaul Mackerras spin_lock_init(&mpic->fixup_lock); 51114cf11afSPaul Mackerras 512c4b22f26SSegher Boessenkool /* Map U3 config space. We assume all IO-APICs are on the primary bus 513c4b22f26SSegher Boessenkool * so we only need to map 64kB. 51414cf11afSPaul Mackerras */ 515c4b22f26SSegher Boessenkool cfgspace = ioremap(0xf2000000, 0x10000); 51614cf11afSPaul Mackerras BUG_ON(cfgspace == NULL); 51714cf11afSPaul Mackerras 5181beb6a7dSBenjamin Herrenschmidt /* Now we scan all slots. We do a very quick scan, we read the header 5191beb6a7dSBenjamin Herrenschmidt * type, vendor ID and device ID only, that's plenty enough 52014cf11afSPaul Mackerras */ 521c4b22f26SSegher Boessenkool for (devfn = 0; devfn < 0x100; devfn++) { 52214cf11afSPaul Mackerras u8 __iomem *devbase = cfgspace + (devfn << 8); 52314cf11afSPaul Mackerras u8 hdr_type = readb(devbase + PCI_HEADER_TYPE); 52414cf11afSPaul Mackerras u32 l = readl(devbase + PCI_VENDOR_ID); 5251beb6a7dSBenjamin Herrenschmidt u16 s; 52614cf11afSPaul Mackerras 52714cf11afSPaul Mackerras DBG("devfn %x, l: %x\n", devfn, l); 52814cf11afSPaul Mackerras 52914cf11afSPaul Mackerras /* If no device, skip */ 53014cf11afSPaul Mackerras if (l == 0xffffffff || l == 0x00000000 || 53114cf11afSPaul Mackerras l == 0x0000ffff || l == 0xffff0000) 53214cf11afSPaul Mackerras goto next; 5331beb6a7dSBenjamin Herrenschmidt /* Check if is supports capability lists */ 5341beb6a7dSBenjamin Herrenschmidt s = readw(devbase + PCI_STATUS); 5351beb6a7dSBenjamin Herrenschmidt if (!(s & PCI_STATUS_CAP_LIST)) 5361beb6a7dSBenjamin Herrenschmidt goto next; 53714cf11afSPaul Mackerras 5381beb6a7dSBenjamin Herrenschmidt mpic_scan_ht_pic(mpic, devbase, devfn, l); 539812fd1fdSMichael Ellerman mpic_scan_ht_msi(mpic, devbase, devfn); 54014cf11afSPaul Mackerras 54114cf11afSPaul Mackerras next: 54214cf11afSPaul Mackerras /* next device, if function 0 */ 543c4b22f26SSegher Boessenkool if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0) 54414cf11afSPaul Mackerras devfn += 7; 54514cf11afSPaul Mackerras } 54614cf11afSPaul Mackerras } 54714cf11afSPaul Mackerras 5486cfef5b2SMichael Ellerman #else /* CONFIG_MPIC_U3_HT_IRQS */ 5496e99e458SBenjamin Herrenschmidt 5506e99e458SBenjamin Herrenschmidt static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source) 5516e99e458SBenjamin Herrenschmidt { 5526e99e458SBenjamin Herrenschmidt return 0; 5536e99e458SBenjamin Herrenschmidt } 5546e99e458SBenjamin Herrenschmidt 5556e99e458SBenjamin Herrenschmidt static void __init mpic_scan_ht_pics(struct mpic *mpic) 5566e99e458SBenjamin Herrenschmidt { 5576e99e458SBenjamin Herrenschmidt } 5586e99e458SBenjamin Herrenschmidt 5596cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 56014cf11afSPaul Mackerras 56114cf11afSPaul Mackerras 5620ebfff14SBenjamin Herrenschmidt #define mpic_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq) 5630ebfff14SBenjamin Herrenschmidt 56414cf11afSPaul Mackerras /* Find an mpic associated with a given linux interrupt */ 56514cf11afSPaul Mackerras static struct mpic *mpic_find(unsigned int irq, unsigned int *is_ipi) 56614cf11afSPaul Mackerras { 5670ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 5687df2457dSOlof Johansson struct mpic *mpic; 56914cf11afSPaul Mackerras 5700ebfff14SBenjamin Herrenschmidt if (irq < NUM_ISA_INTERRUPTS) 57114cf11afSPaul Mackerras return NULL; 5720ebfff14SBenjamin Herrenschmidt 5737df2457dSOlof Johansson mpic = irq_desc[irq].chip_data; 5747df2457dSOlof Johansson 5757df2457dSOlof Johansson if (is_ipi) 5767df2457dSOlof Johansson *is_ipi = (src >= mpic->ipi_vecs[0] && 5777df2457dSOlof Johansson src <= mpic->ipi_vecs[3]); 5787df2457dSOlof Johansson 5797df2457dSOlof Johansson return mpic; 58014cf11afSPaul Mackerras } 58114cf11afSPaul Mackerras 58214cf11afSPaul Mackerras /* Convert a cpu mask from logical to physical cpu numbers. */ 58314cf11afSPaul Mackerras static inline u32 mpic_physmask(u32 cpumask) 58414cf11afSPaul Mackerras { 58514cf11afSPaul Mackerras int i; 58614cf11afSPaul Mackerras u32 mask = 0; 58714cf11afSPaul Mackerras 58814cf11afSPaul Mackerras for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1) 58914cf11afSPaul Mackerras mask |= (cpumask & 1) << get_hard_smp_processor_id(i); 59014cf11afSPaul Mackerras return mask; 59114cf11afSPaul Mackerras } 59214cf11afSPaul Mackerras 59314cf11afSPaul Mackerras #ifdef CONFIG_SMP 59414cf11afSPaul Mackerras /* Get the mpic structure from the IPI number */ 59514cf11afSPaul Mackerras static inline struct mpic * mpic_from_ipi(unsigned int ipi) 59614cf11afSPaul Mackerras { 597b9e5b4e6SBenjamin Herrenschmidt return irq_desc[ipi].chip_data; 59814cf11afSPaul Mackerras } 59914cf11afSPaul Mackerras #endif 60014cf11afSPaul Mackerras 60114cf11afSPaul Mackerras /* Get the mpic structure from the irq number */ 60214cf11afSPaul Mackerras static inline struct mpic * mpic_from_irq(unsigned int irq) 60314cf11afSPaul Mackerras { 604b9e5b4e6SBenjamin Herrenschmidt return irq_desc[irq].chip_data; 60514cf11afSPaul Mackerras } 60614cf11afSPaul Mackerras 60714cf11afSPaul Mackerras /* Send an EOI */ 60814cf11afSPaul Mackerras static inline void mpic_eoi(struct mpic *mpic) 60914cf11afSPaul Mackerras { 6107233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_EOI), 0); 6117233593bSZang Roy-r61911 (void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI)); 61214cf11afSPaul Mackerras } 61314cf11afSPaul Mackerras 61414cf11afSPaul Mackerras #ifdef CONFIG_SMP 6157d12e780SDavid Howells static irqreturn_t mpic_ipi_action(int irq, void *dev_id) 61614cf11afSPaul Mackerras { 6177df2457dSOlof Johansson struct mpic *mpic; 6187df2457dSOlof Johansson 6197df2457dSOlof Johansson mpic = mpic_find(irq, NULL); 6207df2457dSOlof Johansson smp_message_recv(mpic_irq_to_hw(irq) - mpic->ipi_vecs[0]); 6217df2457dSOlof Johansson 62214cf11afSPaul Mackerras return IRQ_HANDLED; 62314cf11afSPaul Mackerras } 62414cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 62514cf11afSPaul Mackerras 62614cf11afSPaul Mackerras /* 62714cf11afSPaul Mackerras * Linux descriptor level callbacks 62814cf11afSPaul Mackerras */ 62914cf11afSPaul Mackerras 63014cf11afSPaul Mackerras 63105af7bd2SMichael Ellerman void mpic_unmask_irq(unsigned int irq) 63214cf11afSPaul Mackerras { 63314cf11afSPaul Mackerras unsigned int loops = 100000; 63414cf11afSPaul Mackerras struct mpic *mpic = mpic_from_irq(irq); 6350ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 63614cf11afSPaul Mackerras 637bd561c79SPaul Mackerras DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src); 63814cf11afSPaul Mackerras 6397233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 6407233593bSZang Roy-r61911 mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & 641e5356640SBenjamin Herrenschmidt ~MPIC_VECPRI_MASK); 64214cf11afSPaul Mackerras /* make sure mask gets to controller before we return to user */ 64314cf11afSPaul Mackerras do { 64414cf11afSPaul Mackerras if (!loops--) { 64514cf11afSPaul Mackerras printk(KERN_ERR "mpic_enable_irq timeout\n"); 64614cf11afSPaul Mackerras break; 64714cf11afSPaul Mackerras } 6487233593bSZang Roy-r61911 } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK); 6491beb6a7dSBenjamin Herrenschmidt } 6501beb6a7dSBenjamin Herrenschmidt 65105af7bd2SMichael Ellerman void mpic_mask_irq(unsigned int irq) 65214cf11afSPaul Mackerras { 65314cf11afSPaul Mackerras unsigned int loops = 100000; 65414cf11afSPaul Mackerras struct mpic *mpic = mpic_from_irq(irq); 6550ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 65614cf11afSPaul Mackerras 65714cf11afSPaul Mackerras DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src); 65814cf11afSPaul Mackerras 6597233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 6607233593bSZang Roy-r61911 mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) | 661e5356640SBenjamin Herrenschmidt MPIC_VECPRI_MASK); 66214cf11afSPaul Mackerras 66314cf11afSPaul Mackerras /* make sure mask gets to controller before we return to user */ 66414cf11afSPaul Mackerras do { 66514cf11afSPaul Mackerras if (!loops--) { 66614cf11afSPaul Mackerras printk(KERN_ERR "mpic_enable_irq timeout\n"); 66714cf11afSPaul Mackerras break; 66814cf11afSPaul Mackerras } 6697233593bSZang Roy-r61911 } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK)); 67014cf11afSPaul Mackerras } 67114cf11afSPaul Mackerras 67205af7bd2SMichael Ellerman void mpic_end_irq(unsigned int irq) 67314cf11afSPaul Mackerras { 67414cf11afSPaul Mackerras struct mpic *mpic = mpic_from_irq(irq); 67514cf11afSPaul Mackerras 6761beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_IRQ 67714cf11afSPaul Mackerras DBG("%s: end_irq: %d\n", mpic->name, irq); 6781beb6a7dSBenjamin Herrenschmidt #endif 67914cf11afSPaul Mackerras /* We always EOI on end_irq() even for edge interrupts since that 68014cf11afSPaul Mackerras * should only lower the priority, the MPIC should have properly 68114cf11afSPaul Mackerras * latched another edge interrupt coming in anyway 68214cf11afSPaul Mackerras */ 68314cf11afSPaul Mackerras 68414cf11afSPaul Mackerras mpic_eoi(mpic); 68514cf11afSPaul Mackerras } 68614cf11afSPaul Mackerras 6876cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 688b9e5b4e6SBenjamin Herrenschmidt 689b9e5b4e6SBenjamin Herrenschmidt static void mpic_unmask_ht_irq(unsigned int irq) 690b9e5b4e6SBenjamin Herrenschmidt { 691b9e5b4e6SBenjamin Herrenschmidt struct mpic *mpic = mpic_from_irq(irq); 6920ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 693b9e5b4e6SBenjamin Herrenschmidt 694b9e5b4e6SBenjamin Herrenschmidt mpic_unmask_irq(irq); 695b9e5b4e6SBenjamin Herrenschmidt 696b9e5b4e6SBenjamin Herrenschmidt if (irq_desc[irq].status & IRQ_LEVEL) 697b9e5b4e6SBenjamin Herrenschmidt mpic_ht_end_irq(mpic, src); 698b9e5b4e6SBenjamin Herrenschmidt } 699b9e5b4e6SBenjamin Herrenschmidt 700b9e5b4e6SBenjamin Herrenschmidt static unsigned int mpic_startup_ht_irq(unsigned int irq) 701b9e5b4e6SBenjamin Herrenschmidt { 702b9e5b4e6SBenjamin Herrenschmidt struct mpic *mpic = mpic_from_irq(irq); 7030ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 704b9e5b4e6SBenjamin Herrenschmidt 705b9e5b4e6SBenjamin Herrenschmidt mpic_unmask_irq(irq); 706b9e5b4e6SBenjamin Herrenschmidt mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status); 707b9e5b4e6SBenjamin Herrenschmidt 708b9e5b4e6SBenjamin Herrenschmidt return 0; 709b9e5b4e6SBenjamin Herrenschmidt } 710b9e5b4e6SBenjamin Herrenschmidt 711b9e5b4e6SBenjamin Herrenschmidt static void mpic_shutdown_ht_irq(unsigned int irq) 712b9e5b4e6SBenjamin Herrenschmidt { 713b9e5b4e6SBenjamin Herrenschmidt struct mpic *mpic = mpic_from_irq(irq); 7140ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 715b9e5b4e6SBenjamin Herrenschmidt 716b9e5b4e6SBenjamin Herrenschmidt mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status); 717b9e5b4e6SBenjamin Herrenschmidt mpic_mask_irq(irq); 718b9e5b4e6SBenjamin Herrenschmidt } 719b9e5b4e6SBenjamin Herrenschmidt 720b9e5b4e6SBenjamin Herrenschmidt static void mpic_end_ht_irq(unsigned int irq) 721b9e5b4e6SBenjamin Herrenschmidt { 722b9e5b4e6SBenjamin Herrenschmidt struct mpic *mpic = mpic_from_irq(irq); 7230ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 724b9e5b4e6SBenjamin Herrenschmidt 725b9e5b4e6SBenjamin Herrenschmidt #ifdef DEBUG_IRQ 726b9e5b4e6SBenjamin Herrenschmidt DBG("%s: end_irq: %d\n", mpic->name, irq); 727b9e5b4e6SBenjamin Herrenschmidt #endif 728b9e5b4e6SBenjamin Herrenschmidt /* We always EOI on end_irq() even for edge interrupts since that 729b9e5b4e6SBenjamin Herrenschmidt * should only lower the priority, the MPIC should have properly 730b9e5b4e6SBenjamin Herrenschmidt * latched another edge interrupt coming in anyway 731b9e5b4e6SBenjamin Herrenschmidt */ 732b9e5b4e6SBenjamin Herrenschmidt 733b9e5b4e6SBenjamin Herrenschmidt if (irq_desc[irq].status & IRQ_LEVEL) 734b9e5b4e6SBenjamin Herrenschmidt mpic_ht_end_irq(mpic, src); 735b9e5b4e6SBenjamin Herrenschmidt mpic_eoi(mpic); 736b9e5b4e6SBenjamin Herrenschmidt } 7376cfef5b2SMichael Ellerman #endif /* !CONFIG_MPIC_U3_HT_IRQS */ 738b9e5b4e6SBenjamin Herrenschmidt 73914cf11afSPaul Mackerras #ifdef CONFIG_SMP 74014cf11afSPaul Mackerras 741b9e5b4e6SBenjamin Herrenschmidt static void mpic_unmask_ipi(unsigned int irq) 74214cf11afSPaul Mackerras { 74314cf11afSPaul Mackerras struct mpic *mpic = mpic_from_ipi(irq); 7447df2457dSOlof Johansson unsigned int src = mpic_irq_to_hw(irq) - mpic->ipi_vecs[0]; 74514cf11afSPaul Mackerras 74614cf11afSPaul Mackerras DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src); 74714cf11afSPaul Mackerras mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK); 74814cf11afSPaul Mackerras } 74914cf11afSPaul Mackerras 750b9e5b4e6SBenjamin Herrenschmidt static void mpic_mask_ipi(unsigned int irq) 75114cf11afSPaul Mackerras { 75214cf11afSPaul Mackerras /* NEVER disable an IPI... that's just plain wrong! */ 75314cf11afSPaul Mackerras } 75414cf11afSPaul Mackerras 75514cf11afSPaul Mackerras static void mpic_end_ipi(unsigned int irq) 75614cf11afSPaul Mackerras { 75714cf11afSPaul Mackerras struct mpic *mpic = mpic_from_ipi(irq); 75814cf11afSPaul Mackerras 75914cf11afSPaul Mackerras /* 76014cf11afSPaul Mackerras * IPIs are marked IRQ_PER_CPU. This has the side effect of 76114cf11afSPaul Mackerras * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from 76214cf11afSPaul Mackerras * applying to them. We EOI them late to avoid re-entering. 7636714465eSThomas Gleixner * We mark IPI's with IRQF_DISABLED as they must run with 76414cf11afSPaul Mackerras * irqs disabled. 76514cf11afSPaul Mackerras */ 76614cf11afSPaul Mackerras mpic_eoi(mpic); 76714cf11afSPaul Mackerras } 76814cf11afSPaul Mackerras 76914cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 77014cf11afSPaul Mackerras 77114cf11afSPaul Mackerras static void mpic_set_affinity(unsigned int irq, cpumask_t cpumask) 77214cf11afSPaul Mackerras { 77314cf11afSPaul Mackerras struct mpic *mpic = mpic_from_irq(irq); 7740ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 77514cf11afSPaul Mackerras 77614cf11afSPaul Mackerras cpumask_t tmp; 77714cf11afSPaul Mackerras 77814cf11afSPaul Mackerras cpus_and(tmp, cpumask, cpu_online_map); 77914cf11afSPaul Mackerras 7807233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), 78114cf11afSPaul Mackerras mpic_physmask(cpus_addr(tmp)[0])); 78214cf11afSPaul Mackerras } 78314cf11afSPaul Mackerras 7847233593bSZang Roy-r61911 static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type) 7850ebfff14SBenjamin Herrenschmidt { 7860ebfff14SBenjamin Herrenschmidt /* Now convert sense value */ 7876e99e458SBenjamin Herrenschmidt switch(type & IRQ_TYPE_SENSE_MASK) { 7880ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_EDGE_RISING: 7897233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_EDGE) | 7907233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_POSITIVE); 7910ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_EDGE_FALLING: 7926e99e458SBenjamin Herrenschmidt case IRQ_TYPE_EDGE_BOTH: 7937233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_EDGE) | 7947233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 7950ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_LEVEL_HIGH: 7967233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_LEVEL) | 7977233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_POSITIVE); 7980ebfff14SBenjamin Herrenschmidt case IRQ_TYPE_LEVEL_LOW: 7990ebfff14SBenjamin Herrenschmidt default: 8007233593bSZang Roy-r61911 return MPIC_INFO(VECPRI_SENSE_LEVEL) | 8017233593bSZang Roy-r61911 MPIC_INFO(VECPRI_POLARITY_NEGATIVE); 8020ebfff14SBenjamin Herrenschmidt } 8036e99e458SBenjamin Herrenschmidt } 8046e99e458SBenjamin Herrenschmidt 80505af7bd2SMichael Ellerman int mpic_set_irq_type(unsigned int virq, unsigned int flow_type) 8066e99e458SBenjamin Herrenschmidt { 8076e99e458SBenjamin Herrenschmidt struct mpic *mpic = mpic_from_irq(virq); 8086e99e458SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(virq); 8096e99e458SBenjamin Herrenschmidt struct irq_desc *desc = get_irq_desc(virq); 8106e99e458SBenjamin Herrenschmidt unsigned int vecpri, vold, vnew; 8116e99e458SBenjamin Herrenschmidt 81206fe98e6SBenjamin Herrenschmidt DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n", 8136e99e458SBenjamin Herrenschmidt mpic, virq, src, flow_type); 8146e99e458SBenjamin Herrenschmidt 8156e99e458SBenjamin Herrenschmidt if (src >= mpic->irq_count) 8166e99e458SBenjamin Herrenschmidt return -EINVAL; 8176e99e458SBenjamin Herrenschmidt 8186e99e458SBenjamin Herrenschmidt if (flow_type == IRQ_TYPE_NONE) 8196e99e458SBenjamin Herrenschmidt if (mpic->senses && src < mpic->senses_count) 8206e99e458SBenjamin Herrenschmidt flow_type = mpic->senses[src]; 8216e99e458SBenjamin Herrenschmidt if (flow_type == IRQ_TYPE_NONE) 8226e99e458SBenjamin Herrenschmidt flow_type = IRQ_TYPE_LEVEL_LOW; 8236e99e458SBenjamin Herrenschmidt 8246e99e458SBenjamin Herrenschmidt desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); 8256e99e458SBenjamin Herrenschmidt desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; 8266e99e458SBenjamin Herrenschmidt if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) 8276e99e458SBenjamin Herrenschmidt desc->status |= IRQ_LEVEL; 8286e99e458SBenjamin Herrenschmidt 8296e99e458SBenjamin Herrenschmidt if (mpic_is_ht_interrupt(mpic, src)) 8306e99e458SBenjamin Herrenschmidt vecpri = MPIC_VECPRI_POLARITY_POSITIVE | 8316e99e458SBenjamin Herrenschmidt MPIC_VECPRI_SENSE_EDGE; 8326e99e458SBenjamin Herrenschmidt else 8337233593bSZang Roy-r61911 vecpri = mpic_type_to_vecpri(mpic, flow_type); 8346e99e458SBenjamin Herrenschmidt 8357233593bSZang Roy-r61911 vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 8367233593bSZang Roy-r61911 vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) | 8377233593bSZang Roy-r61911 MPIC_INFO(VECPRI_SENSE_MASK)); 8386e99e458SBenjamin Herrenschmidt vnew |= vecpri; 8396e99e458SBenjamin Herrenschmidt if (vold != vnew) 8407233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew); 8416e99e458SBenjamin Herrenschmidt 8426e99e458SBenjamin Herrenschmidt return 0; 8430ebfff14SBenjamin Herrenschmidt } 8440ebfff14SBenjamin Herrenschmidt 845b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_chip = { 846b9e5b4e6SBenjamin Herrenschmidt .mask = mpic_mask_irq, 847b9e5b4e6SBenjamin Herrenschmidt .unmask = mpic_unmask_irq, 848b9e5b4e6SBenjamin Herrenschmidt .eoi = mpic_end_irq, 8496e99e458SBenjamin Herrenschmidt .set_type = mpic_set_irq_type, 850b9e5b4e6SBenjamin Herrenschmidt }; 851b9e5b4e6SBenjamin Herrenschmidt 852b9e5b4e6SBenjamin Herrenschmidt #ifdef CONFIG_SMP 853b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_ipi_chip = { 854b9e5b4e6SBenjamin Herrenschmidt .mask = mpic_mask_ipi, 855b9e5b4e6SBenjamin Herrenschmidt .unmask = mpic_unmask_ipi, 856b9e5b4e6SBenjamin Herrenschmidt .eoi = mpic_end_ipi, 857b9e5b4e6SBenjamin Herrenschmidt }; 858b9e5b4e6SBenjamin Herrenschmidt #endif /* CONFIG_SMP */ 859b9e5b4e6SBenjamin Herrenschmidt 8606cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 861b9e5b4e6SBenjamin Herrenschmidt static struct irq_chip mpic_irq_ht_chip = { 862b9e5b4e6SBenjamin Herrenschmidt .startup = mpic_startup_ht_irq, 863b9e5b4e6SBenjamin Herrenschmidt .shutdown = mpic_shutdown_ht_irq, 864b9e5b4e6SBenjamin Herrenschmidt .mask = mpic_mask_irq, 865b9e5b4e6SBenjamin Herrenschmidt .unmask = mpic_unmask_ht_irq, 866b9e5b4e6SBenjamin Herrenschmidt .eoi = mpic_end_ht_irq, 8676e99e458SBenjamin Herrenschmidt .set_type = mpic_set_irq_type, 868b9e5b4e6SBenjamin Herrenschmidt }; 8696cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 870b9e5b4e6SBenjamin Herrenschmidt 87114cf11afSPaul Mackerras 8720ebfff14SBenjamin Herrenschmidt static int mpic_host_match(struct irq_host *h, struct device_node *node) 8730ebfff14SBenjamin Herrenschmidt { 8740ebfff14SBenjamin Herrenschmidt /* Exact match, unless mpic node is NULL */ 87552964f87SMichael Ellerman return h->of_node == NULL || h->of_node == node; 8760ebfff14SBenjamin Herrenschmidt } 8770ebfff14SBenjamin Herrenschmidt 8780ebfff14SBenjamin Herrenschmidt static int mpic_host_map(struct irq_host *h, unsigned int virq, 8796e99e458SBenjamin Herrenschmidt irq_hw_number_t hw) 8800ebfff14SBenjamin Herrenschmidt { 8810ebfff14SBenjamin Herrenschmidt struct mpic *mpic = h->host_data; 8826e99e458SBenjamin Herrenschmidt struct irq_chip *chip; 8830ebfff14SBenjamin Herrenschmidt 88406fe98e6SBenjamin Herrenschmidt DBG("mpic: map virq %d, hwirq 0x%lx\n", virq, hw); 8850ebfff14SBenjamin Herrenschmidt 8867df2457dSOlof Johansson if (hw == mpic->spurious_vec) 8870ebfff14SBenjamin Herrenschmidt return -EINVAL; 8887fd72186SBenjamin Herrenschmidt if (mpic->protected && test_bit(hw, mpic->protected)) 8897fd72186SBenjamin Herrenschmidt return -EINVAL; 89006fe98e6SBenjamin Herrenschmidt 8910ebfff14SBenjamin Herrenschmidt #ifdef CONFIG_SMP 8927df2457dSOlof Johansson else if (hw >= mpic->ipi_vecs[0]) { 8930ebfff14SBenjamin Herrenschmidt WARN_ON(!(mpic->flags & MPIC_PRIMARY)); 8940ebfff14SBenjamin Herrenschmidt 89506fe98e6SBenjamin Herrenschmidt DBG("mpic: mapping as IPI\n"); 8960ebfff14SBenjamin Herrenschmidt set_irq_chip_data(virq, mpic); 8970ebfff14SBenjamin Herrenschmidt set_irq_chip_and_handler(virq, &mpic->hc_ipi, 8980ebfff14SBenjamin Herrenschmidt handle_percpu_irq); 8990ebfff14SBenjamin Herrenschmidt return 0; 9000ebfff14SBenjamin Herrenschmidt } 9010ebfff14SBenjamin Herrenschmidt #endif /* CONFIG_SMP */ 9020ebfff14SBenjamin Herrenschmidt 9030ebfff14SBenjamin Herrenschmidt if (hw >= mpic->irq_count) 9040ebfff14SBenjamin Herrenschmidt return -EINVAL; 9050ebfff14SBenjamin Herrenschmidt 906a7de7c74SMichael Ellerman mpic_msi_reserve_hwirq(mpic, hw); 907a7de7c74SMichael Ellerman 9086e99e458SBenjamin Herrenschmidt /* Default chip */ 9090ebfff14SBenjamin Herrenschmidt chip = &mpic->hc_irq; 9100ebfff14SBenjamin Herrenschmidt 9116cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 9120ebfff14SBenjamin Herrenschmidt /* Check for HT interrupts, override vecpri */ 9136e99e458SBenjamin Herrenschmidt if (mpic_is_ht_interrupt(mpic, hw)) 9140ebfff14SBenjamin Herrenschmidt chip = &mpic->hc_ht_irq; 9156cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 9160ebfff14SBenjamin Herrenschmidt 91706fe98e6SBenjamin Herrenschmidt DBG("mpic: mapping to irq chip @%p\n", chip); 9180ebfff14SBenjamin Herrenschmidt 9190ebfff14SBenjamin Herrenschmidt set_irq_chip_data(virq, mpic); 9200ebfff14SBenjamin Herrenschmidt set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq); 9216e99e458SBenjamin Herrenschmidt 9226e99e458SBenjamin Herrenschmidt /* Set default irq type */ 9236e99e458SBenjamin Herrenschmidt set_irq_type(virq, IRQ_TYPE_NONE); 9246e99e458SBenjamin Herrenschmidt 9250ebfff14SBenjamin Herrenschmidt return 0; 9260ebfff14SBenjamin Herrenschmidt } 9270ebfff14SBenjamin Herrenschmidt 9280ebfff14SBenjamin Herrenschmidt static int mpic_host_xlate(struct irq_host *h, struct device_node *ct, 9290ebfff14SBenjamin Herrenschmidt u32 *intspec, unsigned int intsize, 9300ebfff14SBenjamin Herrenschmidt irq_hw_number_t *out_hwirq, unsigned int *out_flags) 9310ebfff14SBenjamin Herrenschmidt 9320ebfff14SBenjamin Herrenschmidt { 9330ebfff14SBenjamin Herrenschmidt static unsigned char map_mpic_senses[4] = { 9340ebfff14SBenjamin Herrenschmidt IRQ_TYPE_EDGE_RISING, 9350ebfff14SBenjamin Herrenschmidt IRQ_TYPE_LEVEL_LOW, 9360ebfff14SBenjamin Herrenschmidt IRQ_TYPE_LEVEL_HIGH, 9370ebfff14SBenjamin Herrenschmidt IRQ_TYPE_EDGE_FALLING, 9380ebfff14SBenjamin Herrenschmidt }; 9390ebfff14SBenjamin Herrenschmidt 9400ebfff14SBenjamin Herrenschmidt *out_hwirq = intspec[0]; 94106fe98e6SBenjamin Herrenschmidt if (intsize > 1) { 94206fe98e6SBenjamin Herrenschmidt u32 mask = 0x3; 94306fe98e6SBenjamin Herrenschmidt 94406fe98e6SBenjamin Herrenschmidt /* Apple invented a new race of encoding on machines with 94506fe98e6SBenjamin Herrenschmidt * an HT APIC. They encode, among others, the index within 94606fe98e6SBenjamin Herrenschmidt * the HT APIC. We don't care about it here since thankfully, 94706fe98e6SBenjamin Herrenschmidt * it appears that they have the APIC already properly 94806fe98e6SBenjamin Herrenschmidt * configured, and thus our current fixup code that reads the 94906fe98e6SBenjamin Herrenschmidt * APIC config works fine. However, we still need to mask out 95006fe98e6SBenjamin Herrenschmidt * bits in the specifier to make sure we only get bit 0 which 95106fe98e6SBenjamin Herrenschmidt * is the level/edge bit (the only sense bit exposed by Apple), 95206fe98e6SBenjamin Herrenschmidt * as their bit 1 means something else. 95306fe98e6SBenjamin Herrenschmidt */ 95406fe98e6SBenjamin Herrenschmidt if (machine_is(powermac)) 95506fe98e6SBenjamin Herrenschmidt mask = 0x1; 95606fe98e6SBenjamin Herrenschmidt *out_flags = map_mpic_senses[intspec[1] & mask]; 95706fe98e6SBenjamin Herrenschmidt } else 9580ebfff14SBenjamin Herrenschmidt *out_flags = IRQ_TYPE_NONE; 9590ebfff14SBenjamin Herrenschmidt 96006fe98e6SBenjamin Herrenschmidt DBG("mpic: xlate (%d cells: 0x%08x 0x%08x) to line 0x%lx sense 0x%x\n", 96106fe98e6SBenjamin Herrenschmidt intsize, intspec[0], intspec[1], *out_hwirq, *out_flags); 96206fe98e6SBenjamin Herrenschmidt 9630ebfff14SBenjamin Herrenschmidt return 0; 9640ebfff14SBenjamin Herrenschmidt } 9650ebfff14SBenjamin Herrenschmidt 9660ebfff14SBenjamin Herrenschmidt static struct irq_host_ops mpic_host_ops = { 9670ebfff14SBenjamin Herrenschmidt .match = mpic_host_match, 9680ebfff14SBenjamin Herrenschmidt .map = mpic_host_map, 9690ebfff14SBenjamin Herrenschmidt .xlate = mpic_host_xlate, 9700ebfff14SBenjamin Herrenschmidt }; 9710ebfff14SBenjamin Herrenschmidt 97214cf11afSPaul Mackerras /* 97314cf11afSPaul Mackerras * Exported functions 97414cf11afSPaul Mackerras */ 97514cf11afSPaul Mackerras 9760ebfff14SBenjamin Herrenschmidt struct mpic * __init mpic_alloc(struct device_node *node, 977a959ff56SBenjamin Herrenschmidt phys_addr_t phys_addr, 97814cf11afSPaul Mackerras unsigned int flags, 97914cf11afSPaul Mackerras unsigned int isu_size, 98014cf11afSPaul Mackerras unsigned int irq_count, 98114cf11afSPaul Mackerras const char *name) 98214cf11afSPaul Mackerras { 98314cf11afSPaul Mackerras struct mpic *mpic; 98414cf11afSPaul Mackerras u32 reg; 98514cf11afSPaul Mackerras const char *vers; 98614cf11afSPaul Mackerras int i; 9877df2457dSOlof Johansson int intvec_top; 988a959ff56SBenjamin Herrenschmidt u64 paddr = phys_addr; 98914cf11afSPaul Mackerras 99014cf11afSPaul Mackerras mpic = alloc_bootmem(sizeof(struct mpic)); 99114cf11afSPaul Mackerras if (mpic == NULL) 99214cf11afSPaul Mackerras return NULL; 99314cf11afSPaul Mackerras 99414cf11afSPaul Mackerras memset(mpic, 0, sizeof(struct mpic)); 99514cf11afSPaul Mackerras mpic->name = name; 99614cf11afSPaul Mackerras 99752964f87SMichael Ellerman mpic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR, 99852964f87SMichael Ellerman isu_size, &mpic_host_ops, 9997df2457dSOlof Johansson flags & MPIC_LARGE_VECTORS ? 2048 : 256); 10000ebfff14SBenjamin Herrenschmidt if (mpic->irqhost == NULL) { 10010ebfff14SBenjamin Herrenschmidt of_node_put(node); 10020ebfff14SBenjamin Herrenschmidt return NULL; 10030ebfff14SBenjamin Herrenschmidt } 10040ebfff14SBenjamin Herrenschmidt 10050ebfff14SBenjamin Herrenschmidt mpic->irqhost->host_data = mpic; 1006b9e5b4e6SBenjamin Herrenschmidt mpic->hc_irq = mpic_irq_chip; 100714cf11afSPaul Mackerras mpic->hc_irq.typename = name; 100814cf11afSPaul Mackerras if (flags & MPIC_PRIMARY) 100914cf11afSPaul Mackerras mpic->hc_irq.set_affinity = mpic_set_affinity; 10106cfef5b2SMichael Ellerman #ifdef CONFIG_MPIC_U3_HT_IRQS 1011b9e5b4e6SBenjamin Herrenschmidt mpic->hc_ht_irq = mpic_irq_ht_chip; 1012b9e5b4e6SBenjamin Herrenschmidt mpic->hc_ht_irq.typename = name; 1013b9e5b4e6SBenjamin Herrenschmidt if (flags & MPIC_PRIMARY) 1014b9e5b4e6SBenjamin Herrenschmidt mpic->hc_ht_irq.set_affinity = mpic_set_affinity; 10156cfef5b2SMichael Ellerman #endif /* CONFIG_MPIC_U3_HT_IRQS */ 1016fbf0274eSBenjamin Herrenschmidt 101714cf11afSPaul Mackerras #ifdef CONFIG_SMP 1018b9e5b4e6SBenjamin Herrenschmidt mpic->hc_ipi = mpic_ipi_chip; 10190ebfff14SBenjamin Herrenschmidt mpic->hc_ipi.typename = name; 102014cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 102114cf11afSPaul Mackerras 102214cf11afSPaul Mackerras mpic->flags = flags; 102314cf11afSPaul Mackerras mpic->isu_size = isu_size; 102414cf11afSPaul Mackerras mpic->irq_count = irq_count; 102514cf11afSPaul Mackerras mpic->num_sources = 0; /* so far */ 102614cf11afSPaul Mackerras 10277df2457dSOlof Johansson if (flags & MPIC_LARGE_VECTORS) 10287df2457dSOlof Johansson intvec_top = 2047; 10297df2457dSOlof Johansson else 10307df2457dSOlof Johansson intvec_top = 255; 10317df2457dSOlof Johansson 10327df2457dSOlof Johansson mpic->timer_vecs[0] = intvec_top - 8; 10337df2457dSOlof Johansson mpic->timer_vecs[1] = intvec_top - 7; 10347df2457dSOlof Johansson mpic->timer_vecs[2] = intvec_top - 6; 10357df2457dSOlof Johansson mpic->timer_vecs[3] = intvec_top - 5; 10367df2457dSOlof Johansson mpic->ipi_vecs[0] = intvec_top - 4; 10377df2457dSOlof Johansson mpic->ipi_vecs[1] = intvec_top - 3; 10387df2457dSOlof Johansson mpic->ipi_vecs[2] = intvec_top - 2; 10397df2457dSOlof Johansson mpic->ipi_vecs[3] = intvec_top - 1; 10407df2457dSOlof Johansson mpic->spurious_vec = intvec_top; 10417df2457dSOlof Johansson 1042a959ff56SBenjamin Herrenschmidt /* Check for "big-endian" in device-tree */ 1043e2eb6392SStephen Rothwell if (node && of_get_property(node, "big-endian", NULL) != NULL) 1044a959ff56SBenjamin Herrenschmidt mpic->flags |= MPIC_BIG_ENDIAN; 1045a959ff56SBenjamin Herrenschmidt 10467fd72186SBenjamin Herrenschmidt /* Look for protected sources */ 10477fd72186SBenjamin Herrenschmidt if (node) { 10487fd72186SBenjamin Herrenschmidt unsigned int psize, bits, mapsize; 10497fd72186SBenjamin Herrenschmidt const u32 *psrc = 10507fd72186SBenjamin Herrenschmidt of_get_property(node, "protected-sources", &psize); 10517fd72186SBenjamin Herrenschmidt if (psrc) { 10527fd72186SBenjamin Herrenschmidt psize /= 4; 10537fd72186SBenjamin Herrenschmidt bits = intvec_top + 1; 10547fd72186SBenjamin Herrenschmidt mapsize = BITS_TO_LONGS(bits) * sizeof(unsigned long); 10557fd72186SBenjamin Herrenschmidt mpic->protected = alloc_bootmem(mapsize); 10567fd72186SBenjamin Herrenschmidt BUG_ON(mpic->protected == NULL); 10577fd72186SBenjamin Herrenschmidt memset(mpic->protected, 0, mapsize); 10587fd72186SBenjamin Herrenschmidt for (i = 0; i < psize; i++) { 10597fd72186SBenjamin Herrenschmidt if (psrc[i] > intvec_top) 10607fd72186SBenjamin Herrenschmidt continue; 10617fd72186SBenjamin Herrenschmidt __set_bit(psrc[i], mpic->protected); 10627fd72186SBenjamin Herrenschmidt } 10637fd72186SBenjamin Herrenschmidt } 10647fd72186SBenjamin Herrenschmidt } 1065a959ff56SBenjamin Herrenschmidt 10667233593bSZang Roy-r61911 #ifdef CONFIG_MPIC_WEIRD 10677233593bSZang Roy-r61911 mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)]; 10687233593bSZang Roy-r61911 #endif 10697233593bSZang Roy-r61911 1070fbf0274eSBenjamin Herrenschmidt /* default register type */ 1071fbf0274eSBenjamin Herrenschmidt mpic->reg_type = (flags & MPIC_BIG_ENDIAN) ? 1072fbf0274eSBenjamin Herrenschmidt mpic_access_mmio_be : mpic_access_mmio_le; 1073fbf0274eSBenjamin Herrenschmidt 1074a959ff56SBenjamin Herrenschmidt /* If no physical address is passed in, a device-node is mandatory */ 1075a959ff56SBenjamin Herrenschmidt BUG_ON(paddr == 0 && node == NULL); 1076a959ff56SBenjamin Herrenschmidt 1077a959ff56SBenjamin Herrenschmidt /* If no physical address passed in, check if it's dcr based */ 1078e2eb6392SStephen Rothwell if (paddr == 0 && of_get_property(node, "dcr-reg", NULL) != NULL) 1079a959ff56SBenjamin Herrenschmidt mpic->flags |= MPIC_USES_DCR; 1080a959ff56SBenjamin Herrenschmidt 1081fbf0274eSBenjamin Herrenschmidt #ifdef CONFIG_PPC_DCR 1082fbf0274eSBenjamin Herrenschmidt if (mpic->flags & MPIC_USES_DCR) { 1083fbf0274eSBenjamin Herrenschmidt const u32 *dbasep; 1084e2eb6392SStephen Rothwell dbasep = of_get_property(node, "dcr-reg", NULL); 1085fbf0274eSBenjamin Herrenschmidt BUG_ON(dbasep == NULL); 1086fbf0274eSBenjamin Herrenschmidt mpic->dcr_base = *dbasep; 1087fbf0274eSBenjamin Herrenschmidt mpic->reg_type = mpic_access_dcr; 1088fbf0274eSBenjamin Herrenschmidt } 1089fbf0274eSBenjamin Herrenschmidt #else 1090fbf0274eSBenjamin Herrenschmidt BUG_ON (mpic->flags & MPIC_USES_DCR); 1091fbf0274eSBenjamin Herrenschmidt #endif /* CONFIG_PPC_DCR */ 1092fbf0274eSBenjamin Herrenschmidt 1093a959ff56SBenjamin Herrenschmidt /* If the MPIC is not DCR based, and no physical address was passed 1094a959ff56SBenjamin Herrenschmidt * in, try to obtain one 1095a959ff56SBenjamin Herrenschmidt */ 1096a959ff56SBenjamin Herrenschmidt if (paddr == 0 && !(mpic->flags & MPIC_USES_DCR)) { 1097a959ff56SBenjamin Herrenschmidt const u32 *reg; 1098e2eb6392SStephen Rothwell reg = of_get_property(node, "reg", NULL); 1099a959ff56SBenjamin Herrenschmidt BUG_ON(reg == NULL); 1100a959ff56SBenjamin Herrenschmidt paddr = of_translate_address(node, reg); 1101a959ff56SBenjamin Herrenschmidt BUG_ON(paddr == OF_BAD_ADDR); 1102a959ff56SBenjamin Herrenschmidt } 1103a959ff56SBenjamin Herrenschmidt 110414cf11afSPaul Mackerras /* Map the global registers */ 1105a959ff56SBenjamin Herrenschmidt mpic_map(mpic, paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000); 1106a959ff56SBenjamin Herrenschmidt mpic_map(mpic, paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000); 110714cf11afSPaul Mackerras 110814cf11afSPaul Mackerras /* Reset */ 110914cf11afSPaul Mackerras if (flags & MPIC_WANTS_RESET) { 11107233593bSZang Roy-r61911 mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 11117233593bSZang Roy-r61911 mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 111214cf11afSPaul Mackerras | MPIC_GREG_GCONF_RESET); 11137233593bSZang Roy-r61911 while( mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 111414cf11afSPaul Mackerras & MPIC_GREG_GCONF_RESET) 111514cf11afSPaul Mackerras mb(); 111614cf11afSPaul Mackerras } 111714cf11afSPaul Mackerras 111814cf11afSPaul Mackerras /* Read feature register, calculate num CPUs and, for non-ISU 111914cf11afSPaul Mackerras * MPICs, num sources as well. On ISU MPICs, sources are counted 112014cf11afSPaul Mackerras * as ISUs are added 112114cf11afSPaul Mackerras */ 11227233593bSZang Roy-r61911 reg = mpic_read(mpic->gregs, MPIC_INFO(GREG_FEATURE_0)); 112314cf11afSPaul Mackerras mpic->num_cpus = ((reg & MPIC_GREG_FEATURE_LAST_CPU_MASK) 112414cf11afSPaul Mackerras >> MPIC_GREG_FEATURE_LAST_CPU_SHIFT) + 1; 112514cf11afSPaul Mackerras if (isu_size == 0) 112614cf11afSPaul Mackerras mpic->num_sources = ((reg & MPIC_GREG_FEATURE_LAST_SRC_MASK) 112714cf11afSPaul Mackerras >> MPIC_GREG_FEATURE_LAST_SRC_SHIFT) + 1; 112814cf11afSPaul Mackerras 112914cf11afSPaul Mackerras /* Map the per-CPU registers */ 113014cf11afSPaul Mackerras for (i = 0; i < mpic->num_cpus; i++) { 1131a959ff56SBenjamin Herrenschmidt mpic_map(mpic, paddr, &mpic->cpuregs[i], 1132fbf0274eSBenjamin Herrenschmidt MPIC_INFO(CPU_BASE) + i * MPIC_INFO(CPU_STRIDE), 1133fbf0274eSBenjamin Herrenschmidt 0x1000); 113414cf11afSPaul Mackerras } 113514cf11afSPaul Mackerras 113614cf11afSPaul Mackerras /* Initialize main ISU if none provided */ 113714cf11afSPaul Mackerras if (mpic->isu_size == 0) { 113814cf11afSPaul Mackerras mpic->isu_size = mpic->num_sources; 1139a959ff56SBenjamin Herrenschmidt mpic_map(mpic, paddr, &mpic->isus[0], 1140fbf0274eSBenjamin Herrenschmidt MPIC_INFO(IRQ_BASE), MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); 114114cf11afSPaul Mackerras } 114214cf11afSPaul Mackerras mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1); 114314cf11afSPaul Mackerras mpic->isu_mask = (1 << mpic->isu_shift) - 1; 114414cf11afSPaul Mackerras 114514cf11afSPaul Mackerras /* Display version */ 114614cf11afSPaul Mackerras switch (reg & MPIC_GREG_FEATURE_VERSION_MASK) { 114714cf11afSPaul Mackerras case 1: 114814cf11afSPaul Mackerras vers = "1.0"; 114914cf11afSPaul Mackerras break; 115014cf11afSPaul Mackerras case 2: 115114cf11afSPaul Mackerras vers = "1.2"; 115214cf11afSPaul Mackerras break; 115314cf11afSPaul Mackerras case 3: 115414cf11afSPaul Mackerras vers = "1.3"; 115514cf11afSPaul Mackerras break; 115614cf11afSPaul Mackerras default: 115714cf11afSPaul Mackerras vers = "<unknown>"; 115814cf11afSPaul Mackerras break; 115914cf11afSPaul Mackerras } 1160a959ff56SBenjamin Herrenschmidt printk(KERN_INFO "mpic: Setting up MPIC \"%s\" version %s at %llx," 1161a959ff56SBenjamin Herrenschmidt " max %d CPUs\n", 1162a959ff56SBenjamin Herrenschmidt name, vers, (unsigned long long)paddr, mpic->num_cpus); 1163a959ff56SBenjamin Herrenschmidt printk(KERN_INFO "mpic: ISU size: %d, shift: %d, mask: %x\n", 1164a959ff56SBenjamin Herrenschmidt mpic->isu_size, mpic->isu_shift, mpic->isu_mask); 116514cf11afSPaul Mackerras 116614cf11afSPaul Mackerras mpic->next = mpics; 116714cf11afSPaul Mackerras mpics = mpic; 116814cf11afSPaul Mackerras 11690ebfff14SBenjamin Herrenschmidt if (flags & MPIC_PRIMARY) { 117014cf11afSPaul Mackerras mpic_primary = mpic; 11710ebfff14SBenjamin Herrenschmidt irq_set_default_host(mpic->irqhost); 11720ebfff14SBenjamin Herrenschmidt } 117314cf11afSPaul Mackerras 117414cf11afSPaul Mackerras return mpic; 117514cf11afSPaul Mackerras } 117614cf11afSPaul Mackerras 117714cf11afSPaul Mackerras void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num, 1178a959ff56SBenjamin Herrenschmidt phys_addr_t paddr) 117914cf11afSPaul Mackerras { 118014cf11afSPaul Mackerras unsigned int isu_first = isu_num * mpic->isu_size; 118114cf11afSPaul Mackerras 118214cf11afSPaul Mackerras BUG_ON(isu_num >= MPIC_MAX_ISU); 118314cf11afSPaul Mackerras 1184a959ff56SBenjamin Herrenschmidt mpic_map(mpic, paddr, &mpic->isus[isu_num], 0, 11857233593bSZang Roy-r61911 MPIC_INFO(IRQ_STRIDE) * mpic->isu_size); 118614cf11afSPaul Mackerras if ((isu_first + mpic->isu_size) > mpic->num_sources) 118714cf11afSPaul Mackerras mpic->num_sources = isu_first + mpic->isu_size; 118814cf11afSPaul Mackerras } 118914cf11afSPaul Mackerras 11900ebfff14SBenjamin Herrenschmidt void __init mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count) 11910ebfff14SBenjamin Herrenschmidt { 11920ebfff14SBenjamin Herrenschmidt mpic->senses = senses; 11930ebfff14SBenjamin Herrenschmidt mpic->senses_count = count; 11940ebfff14SBenjamin Herrenschmidt } 11950ebfff14SBenjamin Herrenschmidt 119614cf11afSPaul Mackerras void __init mpic_init(struct mpic *mpic) 119714cf11afSPaul Mackerras { 119814cf11afSPaul Mackerras int i; 119914cf11afSPaul Mackerras 120014cf11afSPaul Mackerras BUG_ON(mpic->num_sources == 0); 120114cf11afSPaul Mackerras 120214cf11afSPaul Mackerras printk(KERN_INFO "mpic: Initializing for %d sources\n", mpic->num_sources); 120314cf11afSPaul Mackerras 120414cf11afSPaul Mackerras /* Set current processor priority to max */ 12057233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 120614cf11afSPaul Mackerras 120714cf11afSPaul Mackerras /* Initialize timers: just disable them all */ 120814cf11afSPaul Mackerras for (i = 0; i < 4; i++) { 120914cf11afSPaul Mackerras mpic_write(mpic->tmregs, 12107233593bSZang Roy-r61911 i * MPIC_INFO(TIMER_STRIDE) + 12117233593bSZang Roy-r61911 MPIC_INFO(TIMER_DESTINATION), 0); 121214cf11afSPaul Mackerras mpic_write(mpic->tmregs, 12137233593bSZang Roy-r61911 i * MPIC_INFO(TIMER_STRIDE) + 12147233593bSZang Roy-r61911 MPIC_INFO(TIMER_VECTOR_PRI), 121514cf11afSPaul Mackerras MPIC_VECPRI_MASK | 12167df2457dSOlof Johansson (mpic->timer_vecs[0] + i)); 121714cf11afSPaul Mackerras } 121814cf11afSPaul Mackerras 121914cf11afSPaul Mackerras /* Initialize IPIs to our reserved vectors and mark them disabled for now */ 122014cf11afSPaul Mackerras mpic_test_broken_ipi(mpic); 122114cf11afSPaul Mackerras for (i = 0; i < 4; i++) { 122214cf11afSPaul Mackerras mpic_ipi_write(i, 122314cf11afSPaul Mackerras MPIC_VECPRI_MASK | 122414cf11afSPaul Mackerras (10 << MPIC_VECPRI_PRIORITY_SHIFT) | 12257df2457dSOlof Johansson (mpic->ipi_vecs[0] + i)); 122614cf11afSPaul Mackerras } 122714cf11afSPaul Mackerras 122814cf11afSPaul Mackerras /* Initialize interrupt sources */ 122914cf11afSPaul Mackerras if (mpic->irq_count == 0) 123014cf11afSPaul Mackerras mpic->irq_count = mpic->num_sources; 123114cf11afSPaul Mackerras 12321beb6a7dSBenjamin Herrenschmidt /* Do the HT PIC fixups on U3 broken mpic */ 123314cf11afSPaul Mackerras DBG("MPIC flags: %x\n", mpic->flags); 123405af7bd2SMichael Ellerman if ((mpic->flags & MPIC_U3_HT_IRQS) && (mpic->flags & MPIC_PRIMARY)) { 12351beb6a7dSBenjamin Herrenschmidt mpic_scan_ht_pics(mpic); 123605af7bd2SMichael Ellerman mpic_u3msi_init(mpic); 123705af7bd2SMichael Ellerman } 123814cf11afSPaul Mackerras 123914cf11afSPaul Mackerras for (i = 0; i < mpic->num_sources; i++) { 124014cf11afSPaul Mackerras /* start with vector = source number, and masked */ 12416e99e458SBenjamin Herrenschmidt u32 vecpri = MPIC_VECPRI_MASK | i | 12426e99e458SBenjamin Herrenschmidt (8 << MPIC_VECPRI_PRIORITY_SHIFT); 124314cf11afSPaul Mackerras 12447fd72186SBenjamin Herrenschmidt /* check if protected */ 12457fd72186SBenjamin Herrenschmidt if (mpic->protected && test_bit(i, mpic->protected)) 12467fd72186SBenjamin Herrenschmidt continue; 124714cf11afSPaul Mackerras /* init hw */ 12487233593bSZang Roy-r61911 mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), vecpri); 12497233593bSZang Roy-r61911 mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 125014cf11afSPaul Mackerras 1 << hard_smp_processor_id()); 125114cf11afSPaul Mackerras } 125214cf11afSPaul Mackerras 12537df2457dSOlof Johansson /* Init spurious vector */ 12547df2457dSOlof Johansson mpic_write(mpic->gregs, MPIC_INFO(GREG_SPURIOUS), mpic->spurious_vec); 125514cf11afSPaul Mackerras 12567233593bSZang Roy-r61911 /* Disable 8259 passthrough, if supported */ 12577233593bSZang Roy-r61911 if (!(mpic->flags & MPIC_NO_PTHROU_DIS)) 12587233593bSZang Roy-r61911 mpic_write(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0), 12597233593bSZang Roy-r61911 mpic_read(mpic->gregs, MPIC_INFO(GREG_GLOBAL_CONF_0)) 126014cf11afSPaul Mackerras | MPIC_GREG_GCONF_8259_PTHROU_DIS); 126114cf11afSPaul Mackerras 126214cf11afSPaul Mackerras /* Set current processor priority to 0 */ 12637233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 12643669e930SJohannes Berg 12653669e930SJohannes Berg #ifdef CONFIG_PM 12663669e930SJohannes Berg /* allocate memory to save mpic state */ 12673669e930SJohannes Berg mpic->save_data = alloc_bootmem(mpic->num_sources * sizeof(struct mpic_irq_save)); 12683669e930SJohannes Berg BUG_ON(mpic->save_data == NULL); 12693669e930SJohannes Berg #endif 127014cf11afSPaul Mackerras } 127114cf11afSPaul Mackerras 1272868ea0c9SMark A. Greer void __init mpic_set_clk_ratio(struct mpic *mpic, u32 clock_ratio) 1273868ea0c9SMark A. Greer { 1274868ea0c9SMark A. Greer u32 v; 127514cf11afSPaul Mackerras 1276868ea0c9SMark A. Greer v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1); 1277868ea0c9SMark A. Greer v &= ~MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO_MASK; 1278868ea0c9SMark A. Greer v |= MPIC_GREG_GLOBAL_CONF_1_CLK_RATIO(clock_ratio); 1279868ea0c9SMark A. Greer mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v); 1280868ea0c9SMark A. Greer } 1281868ea0c9SMark A. Greer 1282868ea0c9SMark A. Greer void __init mpic_set_serial_int(struct mpic *mpic, int enable) 1283868ea0c9SMark A. Greer { 1284ba1826e5SBenjamin Herrenschmidt unsigned long flags; 1285868ea0c9SMark A. Greer u32 v; 1286868ea0c9SMark A. Greer 1287ba1826e5SBenjamin Herrenschmidt spin_lock_irqsave(&mpic_lock, flags); 1288868ea0c9SMark A. Greer v = mpic_read(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1); 1289868ea0c9SMark A. Greer if (enable) 1290868ea0c9SMark A. Greer v |= MPIC_GREG_GLOBAL_CONF_1_SIE; 1291868ea0c9SMark A. Greer else 1292868ea0c9SMark A. Greer v &= ~MPIC_GREG_GLOBAL_CONF_1_SIE; 1293868ea0c9SMark A. Greer mpic_write(mpic->gregs, MPIC_GREG_GLOBAL_CONF_1, v); 1294ba1826e5SBenjamin Herrenschmidt spin_unlock_irqrestore(&mpic_lock, flags); 1295868ea0c9SMark A. Greer } 129614cf11afSPaul Mackerras 129714cf11afSPaul Mackerras void mpic_irq_set_priority(unsigned int irq, unsigned int pri) 129814cf11afSPaul Mackerras { 129914cf11afSPaul Mackerras int is_ipi; 130014cf11afSPaul Mackerras struct mpic *mpic = mpic_find(irq, &is_ipi); 13010ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 130214cf11afSPaul Mackerras unsigned long flags; 130314cf11afSPaul Mackerras u32 reg; 130414cf11afSPaul Mackerras 130514cf11afSPaul Mackerras spin_lock_irqsave(&mpic_lock, flags); 130614cf11afSPaul Mackerras if (is_ipi) { 13077df2457dSOlof Johansson reg = mpic_ipi_read(src - mpic->ipi_vecs[0]) & 1308e5356640SBenjamin Herrenschmidt ~MPIC_VECPRI_PRIORITY_MASK; 13097df2457dSOlof Johansson mpic_ipi_write(src - mpic->ipi_vecs[0], 131014cf11afSPaul Mackerras reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 131114cf11afSPaul Mackerras } else { 13127233593bSZang Roy-r61911 reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) 1313e5356640SBenjamin Herrenschmidt & ~MPIC_VECPRI_PRIORITY_MASK; 13147233593bSZang Roy-r61911 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), 131514cf11afSPaul Mackerras reg | (pri << MPIC_VECPRI_PRIORITY_SHIFT)); 131614cf11afSPaul Mackerras } 131714cf11afSPaul Mackerras spin_unlock_irqrestore(&mpic_lock, flags); 131814cf11afSPaul Mackerras } 131914cf11afSPaul Mackerras 132014cf11afSPaul Mackerras unsigned int mpic_irq_get_priority(unsigned int irq) 132114cf11afSPaul Mackerras { 132214cf11afSPaul Mackerras int is_ipi; 132314cf11afSPaul Mackerras struct mpic *mpic = mpic_find(irq, &is_ipi); 13240ebfff14SBenjamin Herrenschmidt unsigned int src = mpic_irq_to_hw(irq); 132514cf11afSPaul Mackerras unsigned long flags; 132614cf11afSPaul Mackerras u32 reg; 132714cf11afSPaul Mackerras 132814cf11afSPaul Mackerras spin_lock_irqsave(&mpic_lock, flags); 132914cf11afSPaul Mackerras if (is_ipi) 13307df2457dSOlof Johansson reg = mpic_ipi_read(src = mpic->ipi_vecs[0]); 133114cf11afSPaul Mackerras else 13327233593bSZang Roy-r61911 reg = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); 133314cf11afSPaul Mackerras spin_unlock_irqrestore(&mpic_lock, flags); 133414cf11afSPaul Mackerras return (reg & MPIC_VECPRI_PRIORITY_MASK) >> MPIC_VECPRI_PRIORITY_SHIFT; 133514cf11afSPaul Mackerras } 133614cf11afSPaul Mackerras 133714cf11afSPaul Mackerras void mpic_setup_this_cpu(void) 133814cf11afSPaul Mackerras { 133914cf11afSPaul Mackerras #ifdef CONFIG_SMP 134014cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 134114cf11afSPaul Mackerras unsigned long flags; 134214cf11afSPaul Mackerras u32 msk = 1 << hard_smp_processor_id(); 134314cf11afSPaul Mackerras unsigned int i; 134414cf11afSPaul Mackerras 134514cf11afSPaul Mackerras BUG_ON(mpic == NULL); 134614cf11afSPaul Mackerras 134714cf11afSPaul Mackerras DBG("%s: setup_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 134814cf11afSPaul Mackerras 134914cf11afSPaul Mackerras spin_lock_irqsave(&mpic_lock, flags); 135014cf11afSPaul Mackerras 135114cf11afSPaul Mackerras /* let the mpic know we want intrs. default affinity is 0xffffffff 135214cf11afSPaul Mackerras * until changed via /proc. That's how it's done on x86. If we want 135314cf11afSPaul Mackerras * it differently, then we should make sure we also change the default 1354a53da52fSIngo Molnar * values of irq_desc[].affinity in irq.c. 135514cf11afSPaul Mackerras */ 135614cf11afSPaul Mackerras if (distribute_irqs) { 135714cf11afSPaul Mackerras for (i = 0; i < mpic->num_sources ; i++) 13587233593bSZang Roy-r61911 mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 13597233593bSZang Roy-r61911 mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) | msk); 136014cf11afSPaul Mackerras } 136114cf11afSPaul Mackerras 136214cf11afSPaul Mackerras /* Set current processor priority to 0 */ 13637233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0); 136414cf11afSPaul Mackerras 136514cf11afSPaul Mackerras spin_unlock_irqrestore(&mpic_lock, flags); 136614cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 136714cf11afSPaul Mackerras } 136814cf11afSPaul Mackerras 136914cf11afSPaul Mackerras int mpic_cpu_get_priority(void) 137014cf11afSPaul Mackerras { 137114cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 137214cf11afSPaul Mackerras 13737233593bSZang Roy-r61911 return mpic_cpu_read(MPIC_INFO(CPU_CURRENT_TASK_PRI)); 137414cf11afSPaul Mackerras } 137514cf11afSPaul Mackerras 137614cf11afSPaul Mackerras void mpic_cpu_set_priority(int prio) 137714cf11afSPaul Mackerras { 137814cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 137914cf11afSPaul Mackerras 138014cf11afSPaul Mackerras prio &= MPIC_CPU_TASKPRI_MASK; 13817233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), prio); 138214cf11afSPaul Mackerras } 138314cf11afSPaul Mackerras 138414cf11afSPaul Mackerras /* 138514cf11afSPaul Mackerras * XXX: someone who knows mpic should check this. 138614cf11afSPaul Mackerras * do we need to eoi the ipi including for kexec cpu here (see xics comments)? 138714cf11afSPaul Mackerras * or can we reset the mpic in the new kernel? 138814cf11afSPaul Mackerras */ 138914cf11afSPaul Mackerras void mpic_teardown_this_cpu(int secondary) 139014cf11afSPaul Mackerras { 139114cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 139214cf11afSPaul Mackerras unsigned long flags; 139314cf11afSPaul Mackerras u32 msk = 1 << hard_smp_processor_id(); 139414cf11afSPaul Mackerras unsigned int i; 139514cf11afSPaul Mackerras 139614cf11afSPaul Mackerras BUG_ON(mpic == NULL); 139714cf11afSPaul Mackerras 139814cf11afSPaul Mackerras DBG("%s: teardown_this_cpu(%d)\n", mpic->name, hard_smp_processor_id()); 139914cf11afSPaul Mackerras spin_lock_irqsave(&mpic_lock, flags); 140014cf11afSPaul Mackerras 140114cf11afSPaul Mackerras /* let the mpic know we don't want intrs. */ 140214cf11afSPaul Mackerras for (i = 0; i < mpic->num_sources ; i++) 14037233593bSZang Roy-r61911 mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 14047233593bSZang Roy-r61911 mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)) & ~msk); 140514cf11afSPaul Mackerras 140614cf11afSPaul Mackerras /* Set current processor priority to max */ 14077233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf); 140814cf11afSPaul Mackerras 140914cf11afSPaul Mackerras spin_unlock_irqrestore(&mpic_lock, flags); 141014cf11afSPaul Mackerras } 141114cf11afSPaul Mackerras 141214cf11afSPaul Mackerras 141314cf11afSPaul Mackerras void mpic_send_ipi(unsigned int ipi_no, unsigned int cpu_mask) 141414cf11afSPaul Mackerras { 141514cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 141614cf11afSPaul Mackerras 141714cf11afSPaul Mackerras BUG_ON(mpic == NULL); 141814cf11afSPaul Mackerras 14191beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_IPI 142014cf11afSPaul Mackerras DBG("%s: send_ipi(ipi_no: %d)\n", mpic->name, ipi_no); 14211beb6a7dSBenjamin Herrenschmidt #endif 142214cf11afSPaul Mackerras 14237233593bSZang Roy-r61911 mpic_cpu_write(MPIC_INFO(CPU_IPI_DISPATCH_0) + 14247233593bSZang Roy-r61911 ipi_no * MPIC_INFO(CPU_IPI_DISPATCH_STRIDE), 142514cf11afSPaul Mackerras mpic_physmask(cpu_mask & cpus_addr(cpu_online_map)[0])); 142614cf11afSPaul Mackerras } 142714cf11afSPaul Mackerras 142835a84c2fSOlaf Hering unsigned int mpic_get_one_irq(struct mpic *mpic) 142914cf11afSPaul Mackerras { 14300ebfff14SBenjamin Herrenschmidt u32 src; 143114cf11afSPaul Mackerras 14327233593bSZang Roy-r61911 src = mpic_cpu_read(MPIC_INFO(CPU_INTACK)) & MPIC_INFO(VECPRI_VECTOR_MASK); 14331beb6a7dSBenjamin Herrenschmidt #ifdef DEBUG_LOW 14340ebfff14SBenjamin Herrenschmidt DBG("%s: get_one_irq(): %d\n", mpic->name, src); 14351beb6a7dSBenjamin Herrenschmidt #endif 14365cddd2e3SJosh Boyer if (unlikely(src == mpic->spurious_vec)) { 14375cddd2e3SJosh Boyer if (mpic->flags & MPIC_SPV_EOI) 14385cddd2e3SJosh Boyer mpic_eoi(mpic); 14390ebfff14SBenjamin Herrenschmidt return NO_IRQ; 14405cddd2e3SJosh Boyer } 14417fd72186SBenjamin Herrenschmidt if (unlikely(mpic->protected && test_bit(src, mpic->protected))) { 14427fd72186SBenjamin Herrenschmidt if (printk_ratelimit()) 14437fd72186SBenjamin Herrenschmidt printk(KERN_WARNING "%s: Got protected source %d !\n", 14447fd72186SBenjamin Herrenschmidt mpic->name, (int)src); 14457fd72186SBenjamin Herrenschmidt mpic_eoi(mpic); 14467fd72186SBenjamin Herrenschmidt return NO_IRQ; 14477fd72186SBenjamin Herrenschmidt } 14487fd72186SBenjamin Herrenschmidt 14490ebfff14SBenjamin Herrenschmidt return irq_linear_revmap(mpic->irqhost, src); 145014cf11afSPaul Mackerras } 145114cf11afSPaul Mackerras 145235a84c2fSOlaf Hering unsigned int mpic_get_irq(void) 145314cf11afSPaul Mackerras { 145414cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 145514cf11afSPaul Mackerras 145614cf11afSPaul Mackerras BUG_ON(mpic == NULL); 145714cf11afSPaul Mackerras 145835a84c2fSOlaf Hering return mpic_get_one_irq(mpic); 145914cf11afSPaul Mackerras } 146014cf11afSPaul Mackerras 146114cf11afSPaul Mackerras 146214cf11afSPaul Mackerras #ifdef CONFIG_SMP 146314cf11afSPaul Mackerras void mpic_request_ipis(void) 146414cf11afSPaul Mackerras { 146514cf11afSPaul Mackerras struct mpic *mpic = mpic_primary; 1466d16f1b64SOlof Johansson int i, err; 14670ebfff14SBenjamin Herrenschmidt static char *ipi_names[] = { 14680ebfff14SBenjamin Herrenschmidt "IPI0 (call function)", 14690ebfff14SBenjamin Herrenschmidt "IPI1 (reschedule)", 14700ebfff14SBenjamin Herrenschmidt "IPI2 (unused)", 14710ebfff14SBenjamin Herrenschmidt "IPI3 (debugger break)", 14720ebfff14SBenjamin Herrenschmidt }; 147314cf11afSPaul Mackerras BUG_ON(mpic == NULL); 147414cf11afSPaul Mackerras 14750ebfff14SBenjamin Herrenschmidt printk(KERN_INFO "mpic: requesting IPIs ... \n"); 147614cf11afSPaul Mackerras 14770ebfff14SBenjamin Herrenschmidt for (i = 0; i < 4; i++) { 14780ebfff14SBenjamin Herrenschmidt unsigned int vipi = irq_create_mapping(mpic->irqhost, 14797df2457dSOlof Johansson mpic->ipi_vecs[0] + i); 14800ebfff14SBenjamin Herrenschmidt if (vipi == NO_IRQ) { 14810ebfff14SBenjamin Herrenschmidt printk(KERN_ERR "Failed to map IPI %d\n", i); 14820ebfff14SBenjamin Herrenschmidt break; 14830ebfff14SBenjamin Herrenschmidt } 1484d16f1b64SOlof Johansson err = request_irq(vipi, mpic_ipi_action, 1485d16f1b64SOlof Johansson IRQF_DISABLED|IRQF_PERCPU, 14860ebfff14SBenjamin Herrenschmidt ipi_names[i], mpic); 1487d16f1b64SOlof Johansson if (err) { 1488d16f1b64SOlof Johansson printk(KERN_ERR "Request of irq %d for IPI %d failed\n", 1489d16f1b64SOlof Johansson vipi, i); 1490d16f1b64SOlof Johansson break; 1491d16f1b64SOlof Johansson } 14920ebfff14SBenjamin Herrenschmidt } 149314cf11afSPaul Mackerras } 1494a9c59264SPaul Mackerras 1495a9c59264SPaul Mackerras void smp_mpic_message_pass(int target, int msg) 1496a9c59264SPaul Mackerras { 1497a9c59264SPaul Mackerras /* make sure we're sending something that translates to an IPI */ 1498a9c59264SPaul Mackerras if ((unsigned int)msg > 3) { 1499a9c59264SPaul Mackerras printk("SMP %d: smp_message_pass: unknown msg %d\n", 1500a9c59264SPaul Mackerras smp_processor_id(), msg); 1501a9c59264SPaul Mackerras return; 1502a9c59264SPaul Mackerras } 1503a9c59264SPaul Mackerras switch (target) { 1504a9c59264SPaul Mackerras case MSG_ALL: 1505a9c59264SPaul Mackerras mpic_send_ipi(msg, 0xffffffff); 1506a9c59264SPaul Mackerras break; 1507a9c59264SPaul Mackerras case MSG_ALL_BUT_SELF: 1508a9c59264SPaul Mackerras mpic_send_ipi(msg, 0xffffffff & ~(1 << smp_processor_id())); 1509a9c59264SPaul Mackerras break; 1510a9c59264SPaul Mackerras default: 1511a9c59264SPaul Mackerras mpic_send_ipi(msg, 1 << target); 1512a9c59264SPaul Mackerras break; 1513a9c59264SPaul Mackerras } 1514a9c59264SPaul Mackerras } 1515775aeff4SMichael Ellerman 1516775aeff4SMichael Ellerman int __init smp_mpic_probe(void) 1517775aeff4SMichael Ellerman { 1518775aeff4SMichael Ellerman int nr_cpus; 1519775aeff4SMichael Ellerman 1520775aeff4SMichael Ellerman DBG("smp_mpic_probe()...\n"); 1521775aeff4SMichael Ellerman 1522775aeff4SMichael Ellerman nr_cpus = cpus_weight(cpu_possible_map); 1523775aeff4SMichael Ellerman 1524775aeff4SMichael Ellerman DBG("nr_cpus: %d\n", nr_cpus); 1525775aeff4SMichael Ellerman 1526775aeff4SMichael Ellerman if (nr_cpus > 1) 1527775aeff4SMichael Ellerman mpic_request_ipis(); 1528775aeff4SMichael Ellerman 1529775aeff4SMichael Ellerman return nr_cpus; 1530775aeff4SMichael Ellerman } 1531775aeff4SMichael Ellerman 1532775aeff4SMichael Ellerman void __devinit smp_mpic_setup_cpu(int cpu) 1533775aeff4SMichael Ellerman { 1534775aeff4SMichael Ellerman mpic_setup_this_cpu(); 1535775aeff4SMichael Ellerman } 153614cf11afSPaul Mackerras #endif /* CONFIG_SMP */ 15373669e930SJohannes Berg 15383669e930SJohannes Berg #ifdef CONFIG_PM 15393669e930SJohannes Berg static int mpic_suspend(struct sys_device *dev, pm_message_t state) 15403669e930SJohannes Berg { 15413669e930SJohannes Berg struct mpic *mpic = container_of(dev, struct mpic, sysdev); 15423669e930SJohannes Berg int i; 15433669e930SJohannes Berg 15443669e930SJohannes Berg for (i = 0; i < mpic->num_sources; i++) { 15453669e930SJohannes Berg mpic->save_data[i].vecprio = 15463669e930SJohannes Berg mpic_irq_read(i, MPIC_INFO(IRQ_VECTOR_PRI)); 15473669e930SJohannes Berg mpic->save_data[i].dest = 15483669e930SJohannes Berg mpic_irq_read(i, MPIC_INFO(IRQ_DESTINATION)); 15493669e930SJohannes Berg } 15503669e930SJohannes Berg 15513669e930SJohannes Berg return 0; 15523669e930SJohannes Berg } 15533669e930SJohannes Berg 15543669e930SJohannes Berg static int mpic_resume(struct sys_device *dev) 15553669e930SJohannes Berg { 15563669e930SJohannes Berg struct mpic *mpic = container_of(dev, struct mpic, sysdev); 15573669e930SJohannes Berg int i; 15583669e930SJohannes Berg 15593669e930SJohannes Berg for (i = 0; i < mpic->num_sources; i++) { 15603669e930SJohannes Berg mpic_irq_write(i, MPIC_INFO(IRQ_VECTOR_PRI), 15613669e930SJohannes Berg mpic->save_data[i].vecprio); 15623669e930SJohannes Berg mpic_irq_write(i, MPIC_INFO(IRQ_DESTINATION), 15633669e930SJohannes Berg mpic->save_data[i].dest); 15643669e930SJohannes Berg 15653669e930SJohannes Berg #ifdef CONFIG_MPIC_U3_HT_IRQS 15663669e930SJohannes Berg { 15673669e930SJohannes Berg struct mpic_irq_fixup *fixup = &mpic->fixups[i]; 15683669e930SJohannes Berg 15693669e930SJohannes Berg if (fixup->base) { 15703669e930SJohannes Berg /* we use the lowest bit in an inverted meaning */ 15713669e930SJohannes Berg if ((mpic->save_data[i].fixup_data & 1) == 0) 15723669e930SJohannes Berg continue; 15733669e930SJohannes Berg 15743669e930SJohannes Berg /* Enable and configure */ 15753669e930SJohannes Berg writeb(0x10 + 2 * fixup->index, fixup->base + 2); 15763669e930SJohannes Berg 15773669e930SJohannes Berg writel(mpic->save_data[i].fixup_data & ~1, 15783669e930SJohannes Berg fixup->base + 4); 15793669e930SJohannes Berg } 15803669e930SJohannes Berg } 15813669e930SJohannes Berg #endif 15823669e930SJohannes Berg } /* end for loop */ 15833669e930SJohannes Berg 15843669e930SJohannes Berg return 0; 15853669e930SJohannes Berg } 15863669e930SJohannes Berg #endif 15873669e930SJohannes Berg 15883669e930SJohannes Berg static struct sysdev_class mpic_sysclass = { 15893669e930SJohannes Berg #ifdef CONFIG_PM 15903669e930SJohannes Berg .resume = mpic_resume, 15913669e930SJohannes Berg .suspend = mpic_suspend, 15923669e930SJohannes Berg #endif 15933669e930SJohannes Berg set_kset_name("mpic"), 15943669e930SJohannes Berg }; 15953669e930SJohannes Berg 15963669e930SJohannes Berg static int mpic_init_sys(void) 15973669e930SJohannes Berg { 15983669e930SJohannes Berg struct mpic *mpic = mpics; 15993669e930SJohannes Berg int error, id = 0; 16003669e930SJohannes Berg 16013669e930SJohannes Berg error = sysdev_class_register(&mpic_sysclass); 16023669e930SJohannes Berg 16033669e930SJohannes Berg while (mpic && !error) { 16043669e930SJohannes Berg mpic->sysdev.cls = &mpic_sysclass; 16053669e930SJohannes Berg mpic->sysdev.id = id++; 16063669e930SJohannes Berg error = sysdev_register(&mpic->sysdev); 16073669e930SJohannes Berg mpic = mpic->next; 16083669e930SJohannes Berg } 16093669e930SJohannes Berg return error; 16103669e930SJohannes Berg } 16113669e930SJohannes Berg 16123669e930SJohannes Berg device_initcall(mpic_init_sys); 1613