diff options
| -rw-r--r-- | arch/ppc64/Kconfig | 15 | ||||
| -rw-r--r-- | arch/ppc64/kernel/Makefile | 8 | ||||
| -rw-r--r-- | arch/ppc64/kernel/bpa_iic.c | 270 | ||||
| -rw-r--r-- | arch/ppc64/kernel/bpa_iic.h | 62 | ||||
| -rw-r--r-- | arch/ppc64/kernel/bpa_setup.c | 6 | ||||
| -rw-r--r-- | arch/ppc64/kernel/pSeries_smp.c | 69 | ||||
| -rw-r--r-- | arch/ppc64/kernel/smp.c | 4 | ||||
| -rw-r--r-- | arch/ppc64/kernel/spider-pic.c | 191 |
8 files changed, 614 insertions, 11 deletions
diff --git a/arch/ppc64/Kconfig b/arch/ppc64/Kconfig index c7f2f0a4d856..bae56ec76ea7 100644 --- a/arch/ppc64/Kconfig +++ b/arch/ppc64/Kconfig | |||
| @@ -110,6 +110,21 @@ config PPC_OF | |||
| 110 | bool | 110 | bool |
| 111 | default y | 111 | default y |
| 112 | 112 | ||
| 113 | config XICS | ||
| 114 | depends on PPC_PSERIES | ||
| 115 | bool | ||
| 116 | default y | ||
| 117 | |||
| 118 | config MPIC | ||
| 119 | depends on PPC_PSERIES || PPC_PMAC || PPC_MAPLE | ||
| 120 | bool | ||
| 121 | default y | ||
| 122 | |||
| 123 | config BPA_IIC | ||
| 124 | depends on PPC_BPA | ||
| 125 | bool | ||
| 126 | default y | ||
| 127 | |||
| 113 | # VMX is pSeries only for now until somebody writes the iSeries | 128 | # VMX is pSeries only for now until somebody writes the iSeries |
| 114 | # exception vectors for it | 129 | # exception vectors for it |
| 115 | config ALTIVEC | 130 | config ALTIVEC |
diff --git a/arch/ppc64/kernel/Makefile b/arch/ppc64/kernel/Makefile index c89983ab9098..bc58d80ee3d0 100644 --- a/arch/ppc64/kernel/Makefile +++ b/arch/ppc64/kernel/Makefile | |||
| @@ -27,13 +27,13 @@ obj-$(CONFIG_PPC_ISERIES) += HvCall.o HvLpConfig.o LparData.o \ | |||
| 27 | mf.o HvLpEvent.o iSeries_proc.o iSeries_htab.o \ | 27 | mf.o HvLpEvent.o iSeries_proc.o iSeries_htab.o \ |
| 28 | iSeries_iommu.o | 28 | iSeries_iommu.o |
| 29 | 29 | ||
| 30 | obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o mpic.o | 30 | obj-$(CONFIG_PPC_MULTIPLATFORM) += nvram.o i8259.o prom_init.o prom.o |
| 31 | 31 | ||
| 32 | obj-$(CONFIG_PPC_PSERIES) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o \ | 32 | obj-$(CONFIG_PPC_PSERIES) += pSeries_pci.o pSeries_lpar.o pSeries_hvCall.o \ |
| 33 | pSeries_nvram.o rtasd.o ras.o pSeries_reconfig.o \ | 33 | pSeries_nvram.o rtasd.o ras.o pSeries_reconfig.o \ |
| 34 | xics.o pSeries_setup.o pSeries_iommu.o | 34 | pSeries_setup.o pSeries_iommu.o |
| 35 | 35 | ||
| 36 | obj-$(CONFIG_PPC_BPA) += bpa_setup.o bpa_nvram.o | 36 | obj-$(CONFIG_PPC_BPA) += bpa_setup.o bpa_nvram.o bpa_iic.o spider-pic.o |
| 37 | 37 | ||
| 38 | obj-$(CONFIG_EEH) += eeh.o | 38 | obj-$(CONFIG_EEH) += eeh.o |
| 39 | obj-$(CONFIG_PROC_FS) += proc_ppc64.o | 39 | obj-$(CONFIG_PROC_FS) += proc_ppc64.o |
| @@ -49,6 +49,8 @@ obj-$(CONFIG_HVC_CONSOLE) += hvconsole.o | |||
| 49 | obj-$(CONFIG_BOOTX_TEXT) += btext.o | 49 | obj-$(CONFIG_BOOTX_TEXT) += btext.o |
| 50 | obj-$(CONFIG_HVCS) += hvcserver.o | 50 | obj-$(CONFIG_HVCS) += hvcserver.o |
| 51 | obj-$(CONFIG_IBMVIO) += vio.o | 51 | obj-$(CONFIG_IBMVIO) += vio.o |
| 52 | obj-$(CONFIG_XICS) += xics.o | ||
| 53 | obj-$(CONFIG_MPIC) += mpic.o | ||
| 52 | 54 | ||
| 53 | obj-$(CONFIG_PPC_PMAC) += pmac_setup.o pmac_feature.o pmac_pci.o \ | 55 | obj-$(CONFIG_PPC_PMAC) += pmac_setup.o pmac_feature.o pmac_pci.o \ |
| 54 | pmac_time.o pmac_nvram.o pmac_low_i2c.o | 56 | pmac_time.o pmac_nvram.o pmac_low_i2c.o |
diff --git a/arch/ppc64/kernel/bpa_iic.c b/arch/ppc64/kernel/bpa_iic.c new file mode 100644 index 000000000000..c8f3dc3fad70 --- /dev/null +++ b/arch/ppc64/kernel/bpa_iic.c | |||
| @@ -0,0 +1,270 @@ | |||
| 1 | /* | ||
| 2 | * BPA Internal Interrupt Controller | ||
| 3 | * | ||
| 4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | ||
| 5 | * | ||
| 6 | * Author: Arnd Bergmann <arndb@de.ibm.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2, or (at your option) | ||
| 11 | * any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/config.h> | ||
| 24 | #include <linux/interrupt.h> | ||
| 25 | #include <linux/irq.h> | ||
| 26 | #include <linux/percpu.h> | ||
| 27 | #include <linux/types.h> | ||
| 28 | |||
| 29 | #include <asm/io.h> | ||
| 30 | #include <asm/pgtable.h> | ||
| 31 | #include <asm/prom.h> | ||
| 32 | #include <asm/ptrace.h> | ||
| 33 | |||
| 34 | #include "bpa_iic.h" | ||
| 35 | |||
| 36 | struct iic_pending_bits { | ||
| 37 | u32 data; | ||
| 38 | u8 flags; | ||
| 39 | u8 class; | ||
| 40 | u8 source; | ||
| 41 | u8 prio; | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum iic_pending_flags { | ||
| 45 | IIC_VALID = 0x80, | ||
| 46 | IIC_IPI = 0x40, | ||
| 47 | }; | ||
| 48 | |||
| 49 | struct iic_regs { | ||
| 50 | struct iic_pending_bits pending; | ||
| 51 | struct iic_pending_bits pending_destr; | ||
| 52 | u64 generate; | ||
| 53 | u64 prio; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct iic { | ||
| 57 | struct iic_regs __iomem *regs; | ||
| 58 | }; | ||
| 59 | |||
| 60 | static DEFINE_PER_CPU(struct iic, iic); | ||
| 61 | |||
| 62 | void iic_local_enable(void) | ||
| 63 | { | ||
| 64 | out_be64(&__get_cpu_var(iic).regs->prio, 0xff); | ||
| 65 | } | ||
| 66 | |||
| 67 | void iic_local_disable(void) | ||
| 68 | { | ||
| 69 | out_be64(&__get_cpu_var(iic).regs->prio, 0x0); | ||
| 70 | } | ||
| 71 | |||
| 72 | static unsigned int iic_startup(unsigned int irq) | ||
| 73 | { | ||
| 74 | return 0; | ||
| 75 | } | ||
| 76 | |||
| 77 | static void iic_enable(unsigned int irq) | ||
| 78 | { | ||
| 79 | iic_local_enable(); | ||
| 80 | } | ||
| 81 | |||
| 82 | static void iic_disable(unsigned int irq) | ||
| 83 | { | ||
| 84 | } | ||
| 85 | |||
| 86 | static void iic_end(unsigned int irq) | ||
| 87 | { | ||
| 88 | iic_local_enable(); | ||
| 89 | } | ||
| 90 | |||
| 91 | static struct hw_interrupt_type iic_pic = { | ||
| 92 | .typename = " BPA-IIC ", | ||
| 93 | .startup = iic_startup, | ||
| 94 | .enable = iic_enable, | ||
| 95 | .disable = iic_disable, | ||
| 96 | .end = iic_end, | ||
| 97 | }; | ||
| 98 | |||
| 99 | static int iic_external_get_irq(struct iic_pending_bits pending) | ||
| 100 | { | ||
| 101 | int irq; | ||
| 102 | unsigned char node, unit; | ||
| 103 | |||
| 104 | node = pending.source >> 4; | ||
| 105 | unit = pending.source & 0xf; | ||
| 106 | irq = -1; | ||
| 107 | |||
| 108 | /* | ||
| 109 | * This mapping is specific to the Broadband | ||
| 110 | * Engine. We might need to get the numbers | ||
| 111 | * from the device tree to support future CPUs. | ||
| 112 | */ | ||
| 113 | switch (unit) { | ||
| 114 | case 0x00: | ||
| 115 | case 0x0b: | ||
| 116 | /* | ||
| 117 | * One of these units can be connected | ||
| 118 | * to an external interrupt controller. | ||
| 119 | */ | ||
| 120 | if (pending.prio > 0x3f || | ||
| 121 | pending.class != 2) | ||
| 122 | break; | ||
| 123 | irq = IIC_EXT_OFFSET | ||
| 124 | + spider_get_irq(pending.prio + node * IIC_NODE_STRIDE) | ||
| 125 | + node * IIC_NODE_STRIDE; | ||
| 126 | break; | ||
| 127 | case 0x01 ... 0x04: | ||
| 128 | case 0x07 ... 0x0a: | ||
| 129 | /* | ||
| 130 | * These units are connected to the SPEs | ||
| 131 | */ | ||
| 132 | if (pending.class > 2) | ||
| 133 | break; | ||
| 134 | irq = IIC_SPE_OFFSET | ||
| 135 | + pending.class * IIC_CLASS_STRIDE | ||
| 136 | + node * IIC_NODE_STRIDE | ||
| 137 | + unit; | ||
| 138 | break; | ||
| 139 | } | ||
| 140 | if (irq == -1) | ||
| 141 | printk(KERN_WARNING "Unexpected interrupt class %02x, " | ||
| 142 | "source %02x, prio %02x, cpu %02x\n", pending.class, | ||
| 143 | pending.source, pending.prio, smp_processor_id()); | ||
| 144 | return irq; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* Get an IRQ number from the pending state register of the IIC */ | ||
| 148 | int iic_get_irq(struct pt_regs *regs) | ||
| 149 | { | ||
| 150 | struct iic *iic; | ||
| 151 | int irq; | ||
| 152 | struct iic_pending_bits pending; | ||
| 153 | |||
| 154 | iic = &__get_cpu_var(iic); | ||
| 155 | *(unsigned long *) &pending = | ||
| 156 | in_be64((unsigned long __iomem *) &iic->regs->pending_destr); | ||
| 157 | |||
| 158 | irq = -1; | ||
| 159 | if (pending.flags & IIC_VALID) { | ||
| 160 | if (pending.flags & IIC_IPI) { | ||
| 161 | irq = IIC_IPI_OFFSET + (pending.prio >> 4); | ||
| 162 | /* | ||
| 163 | if (irq > 0x80) | ||
| 164 | printk(KERN_WARNING "Unexpected IPI prio %02x" | ||
| 165 | "on CPU %02x\n", pending.prio, | ||
| 166 | smp_processor_id()); | ||
| 167 | */ | ||
| 168 | } else { | ||
| 169 | irq = iic_external_get_irq(pending); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | return irq; | ||
| 173 | } | ||
| 174 | |||
| 175 | static struct iic_regs __iomem *find_iic(int cpu) | ||
| 176 | { | ||
| 177 | struct device_node *np; | ||
| 178 | int nodeid = cpu / 2; | ||
| 179 | unsigned long regs; | ||
| 180 | struct iic_regs __iomem *iic_regs; | ||
| 181 | |||
| 182 | for (np = of_find_node_by_type(NULL, "cpu"); | ||
| 183 | np; | ||
| 184 | np = of_find_node_by_type(np, "cpu")) { | ||
| 185 | if (nodeid == *(int *)get_property(np, "node-id", NULL)) | ||
| 186 | break; | ||
| 187 | } | ||
| 188 | |||
| 189 | if (!np) { | ||
| 190 | printk(KERN_WARNING "IIC: CPU %d not found\n", cpu); | ||
| 191 | iic_regs = NULL; | ||
| 192 | } else { | ||
| 193 | regs = *(long *)get_property(np, "iic", NULL); | ||
| 194 | |||
| 195 | /* hack until we have decided on the devtree info */ | ||
| 196 | regs += 0x400; | ||
| 197 | if (cpu & 1) | ||
| 198 | regs += 0x20; | ||
| 199 | |||
| 200 | printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs); | ||
| 201 | iic_regs = __ioremap(regs, sizeof(struct iic_regs), | ||
| 202 | _PAGE_NO_CACHE); | ||
| 203 | } | ||
| 204 | return iic_regs; | ||
| 205 | } | ||
| 206 | |||
| 207 | #ifdef CONFIG_SMP | ||
| 208 | void iic_setup_cpu(void) | ||
| 209 | { | ||
| 210 | out_be64(&__get_cpu_var(iic).regs->prio, 0xff); | ||
| 211 | } | ||
| 212 | |||
| 213 | void iic_cause_IPI(int cpu, int mesg) | ||
| 214 | { | ||
| 215 | out_be64(&per_cpu(iic, cpu).regs->generate, mesg); | ||
| 216 | } | ||
| 217 | |||
| 218 | static irqreturn_t iic_ipi_action(int irq, void *dev_id, struct pt_regs *regs) | ||
| 219 | { | ||
| 220 | |||
| 221 | smp_message_recv(irq - IIC_IPI_OFFSET, regs); | ||
| 222 | return IRQ_HANDLED; | ||
| 223 | } | ||
| 224 | |||
| 225 | static void iic_request_ipi(int irq, const char *name) | ||
| 226 | { | ||
| 227 | /* IPIs are marked SA_INTERRUPT as they must run with irqs | ||
| 228 | * disabled */ | ||
| 229 | get_irq_desc(irq)->handler = &iic_pic; | ||
| 230 | get_irq_desc(irq)->status |= IRQ_PER_CPU; | ||
| 231 | request_irq(irq, iic_ipi_action, SA_INTERRUPT, name, NULL); | ||
| 232 | } | ||
| 233 | |||
| 234 | void iic_request_IPIs(void) | ||
| 235 | { | ||
| 236 | iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_CALL_FUNCTION, "IPI-call"); | ||
| 237 | iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_RESCHEDULE, "IPI-resched"); | ||
| 238 | #ifdef CONFIG_DEBUGGER | ||
| 239 | iic_request_ipi(IIC_IPI_OFFSET + PPC_MSG_DEBUGGER_BREAK, "IPI-debug"); | ||
| 240 | #endif /* CONFIG_DEBUGGER */ | ||
| 241 | } | ||
| 242 | #endif /* CONFIG_SMP */ | ||
| 243 | |||
| 244 | static void iic_setup_spe_handlers(void) | ||
| 245 | { | ||
| 246 | int be, isrc; | ||
| 247 | |||
| 248 | /* Assume two threads per BE are present */ | ||
| 249 | for (be=0; be < num_present_cpus() / 2; be++) { | ||
| 250 | for (isrc = 0; isrc < IIC_CLASS_STRIDE * 3; isrc++) { | ||
| 251 | int irq = IIC_NODE_STRIDE * be + IIC_SPE_OFFSET + isrc; | ||
| 252 | get_irq_desc(irq)->handler = &iic_pic; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | void iic_init_IRQ(void) | ||
| 258 | { | ||
| 259 | int cpu, irq_offset; | ||
| 260 | struct iic *iic; | ||
| 261 | |||
| 262 | irq_offset = 0; | ||
| 263 | for_each_cpu(cpu) { | ||
| 264 | iic = &per_cpu(iic, cpu); | ||
| 265 | iic->regs = find_iic(cpu); | ||
| 266 | if (iic->regs) | ||
| 267 | out_be64(&iic->regs->prio, 0xff); | ||
| 268 | } | ||
| 269 | iic_setup_spe_handlers(); | ||
| 270 | } | ||
diff --git a/arch/ppc64/kernel/bpa_iic.h b/arch/ppc64/kernel/bpa_iic.h new file mode 100644 index 000000000000..6833c3022166 --- /dev/null +++ b/arch/ppc64/kernel/bpa_iic.h | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | #ifndef ASM_BPA_IIC_H | ||
| 2 | #define ASM_BPA_IIC_H | ||
| 3 | #ifdef __KERNEL__ | ||
| 4 | /* | ||
| 5 | * Mapping of IIC pending bits into per-node | ||
| 6 | * interrupt numbers. | ||
| 7 | * | ||
| 8 | * IRQ FF CC SS PP FF CC SS PP Description | ||
| 9 | * | ||
| 10 | * 00-3f 80 02 +0 00 - 80 02 +0 3f South Bridge | ||
| 11 | * 00-3f 80 02 +b 00 - 80 02 +b 3f South Bridge | ||
| 12 | * 41-4a 80 00 +1 ** - 80 00 +a ** SPU Class 0 | ||
| 13 | * 51-5a 80 01 +1 ** - 80 01 +a ** SPU Class 1 | ||
| 14 | * 61-6a 80 02 +1 ** - 80 02 +a ** SPU Class 2 | ||
| 15 | * 70-7f C0 ** ** 00 - C0 ** ** 0f IPI | ||
| 16 | * | ||
| 17 | * F flags | ||
| 18 | * C class | ||
| 19 | * S source | ||
| 20 | * P Priority | ||
| 21 | * + node number | ||
| 22 | * * don't care | ||
| 23 | * | ||
| 24 | * A node consists of a Broadband Engine and an optional | ||
| 25 | * south bridge device providing a maximum of 64 IRQs. | ||
| 26 | * The south bridge may be connected to either IOIF0 | ||
| 27 | * or IOIF1. | ||
| 28 | * Each SPE is represented as three IRQ lines, one per | ||
| 29 | * interrupt class. | ||
| 30 | * 16 IRQ numbers are reserved for inter processor | ||
| 31 | * interruptions, although these are only used in the | ||
| 32 | * range of the first node. | ||
| 33 | * | ||
| 34 | * This scheme needs 128 IRQ numbers per BIF node ID, | ||
| 35 | * which means that with the total of 512 lines | ||
| 36 | * available, we can have a maximum of four nodes. | ||
| 37 | */ | ||
| 38 | |||
| 39 | enum { | ||
| 40 | IIC_EXT_OFFSET = 0x00, /* Start of south bridge IRQs */ | ||
| 41 | IIC_NUM_EXT = 0x40, /* Number of south bridge IRQs */ | ||
| 42 | IIC_SPE_OFFSET = 0x40, /* Start of SPE interrupts */ | ||
| 43 | IIC_CLASS_STRIDE = 0x10, /* SPE IRQs per class */ | ||
| 44 | IIC_IPI_OFFSET = 0x70, /* Start of IPI IRQs */ | ||
| 45 | IIC_NUM_IPIS = 0x10, /* IRQs reserved for IPI */ | ||
| 46 | IIC_NODE_STRIDE = 0x80, /* Total IRQs per node */ | ||
| 47 | }; | ||
| 48 | |||
| 49 | extern void iic_init_IRQ(void); | ||
| 50 | extern int iic_get_irq(struct pt_regs *regs); | ||
| 51 | extern void iic_cause_IPI(int cpu, int mesg); | ||
| 52 | extern void iic_request_IPIs(void); | ||
| 53 | extern void iic_setup_cpu(void); | ||
| 54 | extern void iic_local_enable(void); | ||
| 55 | extern void iic_local_disable(void); | ||
| 56 | |||
| 57 | |||
| 58 | extern void spider_init_IRQ(void); | ||
| 59 | extern int spider_get_irq(unsigned long int_pending); | ||
| 60 | |||
| 61 | #endif | ||
| 62 | #endif /* ASM_BPA_IIC_H */ | ||
diff --git a/arch/ppc64/kernel/bpa_setup.c b/arch/ppc64/kernel/bpa_setup.c index d1992dd2d61b..0a43d8a93d76 100644 --- a/arch/ppc64/kernel/bpa_setup.c +++ b/arch/ppc64/kernel/bpa_setup.c | |||
| @@ -45,6 +45,7 @@ | |||
| 45 | #include <asm/cputable.h> | 45 | #include <asm/cputable.h> |
| 46 | 46 | ||
| 47 | #include "pci.h" | 47 | #include "pci.h" |
| 48 | #include "bpa_iic.h" | ||
| 48 | 49 | ||
| 49 | #ifdef DEBUG | 50 | #ifdef DEBUG |
| 50 | #define DBG(fmt...) udbg_printf(fmt) | 51 | #define DBG(fmt...) udbg_printf(fmt) |
| @@ -71,6 +72,9 @@ static void bpa_progress(char *s, unsigned short hex) | |||
| 71 | 72 | ||
| 72 | static void __init bpa_setup_arch(void) | 73 | static void __init bpa_setup_arch(void) |
| 73 | { | 74 | { |
| 75 | ppc_md.init_IRQ = iic_init_IRQ; | ||
| 76 | ppc_md.get_irq = iic_get_irq; | ||
| 77 | |||
| 74 | #ifdef CONFIG_SMP | 78 | #ifdef CONFIG_SMP |
| 75 | smp_init_pSeries(); | 79 | smp_init_pSeries(); |
| 76 | #endif | 80 | #endif |
| @@ -86,7 +90,7 @@ static void __init bpa_setup_arch(void) | |||
| 86 | /* Find and initialize PCI host bridges */ | 90 | /* Find and initialize PCI host bridges */ |
| 87 | init_pci_config_tokens(); | 91 | init_pci_config_tokens(); |
| 88 | find_and_init_phbs(); | 92 | find_and_init_phbs(); |
| 89 | 93 | spider_init_IRQ(); | |
| 90 | #ifdef CONFIG_DUMMY_CONSOLE | 94 | #ifdef CONFIG_DUMMY_CONSOLE |
| 91 | conswitchp = &dummy_con; | 95 | conswitchp = &dummy_con; |
| 92 | #endif | 96 | #endif |
diff --git a/arch/ppc64/kernel/pSeries_smp.c b/arch/ppc64/kernel/pSeries_smp.c index 4203bd020c82..30154140f7e2 100644 --- a/arch/ppc64/kernel/pSeries_smp.c +++ b/arch/ppc64/kernel/pSeries_smp.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * SMP support for pSeries machines. | 2 | * SMP support for pSeries and BPA machines. |
| 3 | * | 3 | * |
| 4 | * Dave Engebretsen, Peter Bergner, and | 4 | * Dave Engebretsen, Peter Bergner, and |
| 5 | * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com | 5 | * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com |
| @@ -47,6 +47,7 @@ | |||
| 47 | #include <asm/pSeries_reconfig.h> | 47 | #include <asm/pSeries_reconfig.h> |
| 48 | 48 | ||
| 49 | #include "mpic.h" | 49 | #include "mpic.h" |
| 50 | #include "bpa_iic.h" | ||
| 50 | 51 | ||
| 51 | #ifdef DEBUG | 52 | #ifdef DEBUG |
| 52 | #define DBG(fmt...) udbg_printf(fmt) | 53 | #define DBG(fmt...) udbg_printf(fmt) |
| @@ -286,6 +287,7 @@ static inline int __devinit smp_startup_cpu(unsigned int lcpu) | |||
| 286 | return 1; | 287 | return 1; |
| 287 | } | 288 | } |
| 288 | 289 | ||
| 290 | #ifdef CONFIG_XICS | ||
| 289 | static inline void smp_xics_do_message(int cpu, int msg) | 291 | static inline void smp_xics_do_message(int cpu, int msg) |
| 290 | { | 292 | { |
| 291 | set_bit(msg, &xics_ipi_message[cpu].value); | 293 | set_bit(msg, &xics_ipi_message[cpu].value); |
| @@ -327,6 +329,37 @@ static void __devinit smp_xics_setup_cpu(int cpu) | |||
| 327 | cpu_clear(cpu, of_spin_map); | 329 | cpu_clear(cpu, of_spin_map); |
| 328 | 330 | ||
| 329 | } | 331 | } |
| 332 | #endif /* CONFIG_XICS */ | ||
| 333 | #ifdef CONFIG_BPA_IIC | ||
| 334 | static void smp_iic_message_pass(int target, int msg) | ||
| 335 | { | ||
| 336 | unsigned int i; | ||
| 337 | |||
| 338 | if (target < NR_CPUS) { | ||
| 339 | iic_cause_IPI(target, msg); | ||
| 340 | } else { | ||
| 341 | for_each_online_cpu(i) { | ||
| 342 | if (target == MSG_ALL_BUT_SELF | ||
| 343 | && i == smp_processor_id()) | ||
| 344 | continue; | ||
| 345 | iic_cause_IPI(i, msg); | ||
| 346 | } | ||
| 347 | } | ||
| 348 | } | ||
| 349 | |||
| 350 | static int __init smp_iic_probe(void) | ||
| 351 | { | ||
| 352 | iic_request_IPIs(); | ||
| 353 | |||
| 354 | return cpus_weight(cpu_possible_map); | ||
| 355 | } | ||
| 356 | |||
| 357 | static void __devinit smp_iic_setup_cpu(int cpu) | ||
| 358 | { | ||
| 359 | if (cpu != boot_cpuid) | ||
| 360 | iic_setup_cpu(); | ||
| 361 | } | ||
| 362 | #endif /* CONFIG_BPA_IIC */ | ||
| 330 | 363 | ||
| 331 | static DEFINE_SPINLOCK(timebase_lock); | 364 | static DEFINE_SPINLOCK(timebase_lock); |
| 332 | static unsigned long timebase = 0; | 365 | static unsigned long timebase = 0; |
| @@ -381,14 +414,15 @@ static int smp_pSeries_cpu_bootable(unsigned int nr) | |||
| 381 | 414 | ||
| 382 | return 1; | 415 | return 1; |
| 383 | } | 416 | } |
| 384 | 417 | #ifdef CONFIG_MPIC | |
| 385 | static struct smp_ops_t pSeries_mpic_smp_ops = { | 418 | static struct smp_ops_t pSeries_mpic_smp_ops = { |
| 386 | .message_pass = smp_mpic_message_pass, | 419 | .message_pass = smp_mpic_message_pass, |
| 387 | .probe = smp_mpic_probe, | 420 | .probe = smp_mpic_probe, |
| 388 | .kick_cpu = smp_pSeries_kick_cpu, | 421 | .kick_cpu = smp_pSeries_kick_cpu, |
| 389 | .setup_cpu = smp_mpic_setup_cpu, | 422 | .setup_cpu = smp_mpic_setup_cpu, |
| 390 | }; | 423 | }; |
| 391 | 424 | #endif | |
| 425 | #ifdef CONFIG_XICS | ||
| 392 | static struct smp_ops_t pSeries_xics_smp_ops = { | 426 | static struct smp_ops_t pSeries_xics_smp_ops = { |
| 393 | .message_pass = smp_xics_message_pass, | 427 | .message_pass = smp_xics_message_pass, |
| 394 | .probe = smp_xics_probe, | 428 | .probe = smp_xics_probe, |
| @@ -396,6 +430,16 @@ static struct smp_ops_t pSeries_xics_smp_ops = { | |||
| 396 | .setup_cpu = smp_xics_setup_cpu, | 430 | .setup_cpu = smp_xics_setup_cpu, |
| 397 | .cpu_bootable = smp_pSeries_cpu_bootable, | 431 | .cpu_bootable = smp_pSeries_cpu_bootable, |
| 398 | }; | 432 | }; |
| 433 | #endif | ||
| 434 | #ifdef CONFIG_BPA_IIC | ||
| 435 | static struct smp_ops_t bpa_iic_smp_ops = { | ||
| 436 | .message_pass = smp_iic_message_pass, | ||
| 437 | .probe = smp_iic_probe, | ||
| 438 | .kick_cpu = smp_pSeries_kick_cpu, | ||
| 439 | .setup_cpu = smp_iic_setup_cpu, | ||
| 440 | .cpu_bootable = smp_pSeries_cpu_bootable, | ||
| 441 | }; | ||
| 442 | #endif | ||
| 399 | 443 | ||
| 400 | /* This is called very early */ | 444 | /* This is called very early */ |
| 401 | void __init smp_init_pSeries(void) | 445 | void __init smp_init_pSeries(void) |
| @@ -404,10 +448,25 @@ void __init smp_init_pSeries(void) | |||
| 404 | 448 | ||
| 405 | DBG(" -> smp_init_pSeries()\n"); | 449 | DBG(" -> smp_init_pSeries()\n"); |
| 406 | 450 | ||
| 407 | if (ppc64_interrupt_controller == IC_OPEN_PIC) | 451 | switch (ppc64_interrupt_controller) { |
| 452 | #ifdef CONFIG_MPIC | ||
| 453 | case IC_OPEN_PIC: | ||
| 408 | smp_ops = &pSeries_mpic_smp_ops; | 454 | smp_ops = &pSeries_mpic_smp_ops; |
| 409 | else | 455 | break; |
| 456 | #endif | ||
| 457 | #ifdef CONFIG_XICS | ||
| 458 | case IC_PPC_XIC: | ||
| 410 | smp_ops = &pSeries_xics_smp_ops; | 459 | smp_ops = &pSeries_xics_smp_ops; |
| 460 | break; | ||
| 461 | #endif | ||
| 462 | #ifdef CONFIG_BPA_IIC | ||
| 463 | case IC_BPA_IIC: | ||
| 464 | smp_ops = &bpa_iic_smp_ops; | ||
| 465 | break; | ||
| 466 | #endif | ||
| 467 | default: | ||
| 468 | panic("Invalid interrupt controller"); | ||
| 469 | } | ||
| 411 | 470 | ||
| 412 | #ifdef CONFIG_HOTPLUG_CPU | 471 | #ifdef CONFIG_HOTPLUG_CPU |
| 413 | smp_ops->cpu_disable = pSeries_cpu_disable; | 472 | smp_ops->cpu_disable = pSeries_cpu_disable; |
diff --git a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c index 9ef5d36d6b25..2fcddfcb594d 100644 --- a/arch/ppc64/kernel/smp.c +++ b/arch/ppc64/kernel/smp.c | |||
| @@ -71,7 +71,7 @@ void smp_call_function_interrupt(void); | |||
| 71 | 71 | ||
| 72 | int smt_enabled_at_boot = 1; | 72 | int smt_enabled_at_boot = 1; |
| 73 | 73 | ||
| 74 | #ifdef CONFIG_PPC_MULTIPLATFORM | 74 | #ifdef CONFIG_MPIC |
| 75 | void smp_mpic_message_pass(int target, int msg) | 75 | void smp_mpic_message_pass(int target, int msg) |
| 76 | { | 76 | { |
| 77 | /* make sure we're sending something that translates to an IPI */ | 77 | /* make sure we're sending something that translates to an IPI */ |
| @@ -128,7 +128,7 @@ void __devinit smp_generic_kick_cpu(int nr) | |||
| 128 | smp_mb(); | 128 | smp_mb(); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | #endif /* CONFIG_PPC_MULTIPLATFORM */ | 131 | #endif /* CONFIG_MPIC */ |
| 132 | 132 | ||
| 133 | static void __init smp_space_timers(unsigned int max_cpus) | 133 | static void __init smp_space_timers(unsigned int max_cpus) |
| 134 | { | 134 | { |
diff --git a/arch/ppc64/kernel/spider-pic.c b/arch/ppc64/kernel/spider-pic.c new file mode 100644 index 000000000000..d5c9a02fb119 --- /dev/null +++ b/arch/ppc64/kernel/spider-pic.c | |||
| @@ -0,0 +1,191 @@ | |||
| 1 | /* | ||
| 2 | * External Interrupt Controller on Spider South Bridge | ||
| 3 | * | ||
| 4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | ||
| 5 | * | ||
| 6 | * Author: Arnd Bergmann <arndb@de.ibm.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; either version 2, or (at your option) | ||
| 11 | * any later version. | ||
| 12 | * | ||
| 13 | * This program is distributed in the hope that it will be useful, | ||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 16 | * GNU General Public License for more details. | ||
| 17 | * | ||
| 18 | * You should have received a copy of the GNU General Public License | ||
| 19 | * along with this program; if not, write to the Free Software | ||
| 20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/interrupt.h> | ||
| 24 | #include <linux/irq.h> | ||
| 25 | |||
| 26 | #include <asm/pgtable.h> | ||
| 27 | #include <asm/prom.h> | ||
| 28 | #include <asm/io.h> | ||
| 29 | |||
| 30 | #include "bpa_iic.h" | ||
| 31 | |||
| 32 | /* register layout taken from Spider spec, table 7.4-4 */ | ||
| 33 | enum { | ||
| 34 | TIR_DEN = 0x004, /* Detection Enable Register */ | ||
| 35 | TIR_MSK = 0x084, /* Mask Level Register */ | ||
| 36 | TIR_EDC = 0x0c0, /* Edge Detection Clear Register */ | ||
| 37 | TIR_PNDA = 0x100, /* Pending Register A */ | ||
| 38 | TIR_PNDB = 0x104, /* Pending Register B */ | ||
| 39 | TIR_CS = 0x144, /* Current Status Register */ | ||
| 40 | TIR_LCSA = 0x150, /* Level Current Status Register A */ | ||
| 41 | TIR_LCSB = 0x154, /* Level Current Status Register B */ | ||
| 42 | TIR_LCSC = 0x158, /* Level Current Status Register C */ | ||
| 43 | TIR_LCSD = 0x15c, /* Level Current Status Register D */ | ||
| 44 | TIR_CFGA = 0x200, /* Setting Register A0 */ | ||
| 45 | TIR_CFGB = 0x204, /* Setting Register B0 */ | ||
| 46 | /* 0x208 ... 0x3ff Setting Register An/Bn */ | ||
| 47 | TIR_PPNDA = 0x400, /* Packet Pending Register A */ | ||
| 48 | TIR_PPNDB = 0x404, /* Packet Pending Register B */ | ||
| 49 | TIR_PIERA = 0x408, /* Packet Output Error Register A */ | ||
| 50 | TIR_PIERB = 0x40c, /* Packet Output Error Register B */ | ||
| 51 | TIR_PIEN = 0x444, /* Packet Output Enable Register */ | ||
| 52 | TIR_PIPND = 0x454, /* Packet Output Pending Register */ | ||
| 53 | TIRDID = 0x484, /* Spider Device ID Register */ | ||
| 54 | REISTIM = 0x500, /* Reissue Command Timeout Time Setting */ | ||
| 55 | REISTIMEN = 0x504, /* Reissue Command Timeout Setting */ | ||
| 56 | REISWAITEN = 0x508, /* Reissue Wait Control*/ | ||
| 57 | }; | ||
| 58 | |||
| 59 | static void __iomem *spider_pics[4]; | ||
| 60 | |||
| 61 | static void __iomem *spider_get_pic(int irq) | ||
| 62 | { | ||
| 63 | int node = irq / IIC_NODE_STRIDE; | ||
| 64 | irq %= IIC_NODE_STRIDE; | ||
| 65 | |||
| 66 | if (irq >= IIC_EXT_OFFSET && | ||
| 67 | irq < IIC_EXT_OFFSET + IIC_NUM_EXT && | ||
| 68 | spider_pics) | ||
| 69 | return spider_pics[node]; | ||
| 70 | return NULL; | ||
| 71 | } | ||
| 72 | |||
| 73 | static int spider_get_nr(unsigned int irq) | ||
| 74 | { | ||
| 75 | return (irq % IIC_NODE_STRIDE) - IIC_EXT_OFFSET; | ||
| 76 | } | ||
| 77 | |||
| 78 | static void __iomem *spider_get_irq_config(int irq) | ||
| 79 | { | ||
| 80 | void __iomem *pic; | ||
| 81 | pic = spider_get_pic(irq); | ||
| 82 | return pic + TIR_CFGA + 8 * spider_get_nr(irq); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void spider_enable_irq(unsigned int irq) | ||
| 86 | { | ||
| 87 | void __iomem *cfg = spider_get_irq_config(irq); | ||
| 88 | irq = spider_get_nr(irq); | ||
| 89 | |||
| 90 | out_be32(cfg, in_be32(cfg) | 0x3107000eu); | ||
| 91 | out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq); | ||
| 92 | } | ||
| 93 | |||
| 94 | static void spider_disable_irq(unsigned int irq) | ||
| 95 | { | ||
| 96 | void __iomem *cfg = spider_get_irq_config(irq); | ||
| 97 | irq = spider_get_nr(irq); | ||
| 98 | |||
| 99 | out_be32(cfg, in_be32(cfg) & ~0x30000000u); | ||
| 100 | } | ||
| 101 | |||
| 102 | static unsigned int spider_startup_irq(unsigned int irq) | ||
| 103 | { | ||
| 104 | spider_enable_irq(irq); | ||
| 105 | return 0; | ||
| 106 | } | ||
| 107 | |||
| 108 | static void spider_shutdown_irq(unsigned int irq) | ||
| 109 | { | ||
| 110 | spider_disable_irq(irq); | ||
| 111 | } | ||
| 112 | |||
| 113 | static void spider_end_irq(unsigned int irq) | ||
| 114 | { | ||
| 115 | spider_enable_irq(irq); | ||
| 116 | } | ||
| 117 | |||
| 118 | static void spider_ack_irq(unsigned int irq) | ||
| 119 | { | ||
| 120 | spider_disable_irq(irq); | ||
| 121 | iic_local_enable(); | ||
| 122 | } | ||
| 123 | |||
| 124 | static struct hw_interrupt_type spider_pic = { | ||
| 125 | .typename = " SPIDER ", | ||
| 126 | .startup = spider_startup_irq, | ||
| 127 | .shutdown = spider_shutdown_irq, | ||
| 128 | .enable = spider_enable_irq, | ||
| 129 | .disable = spider_disable_irq, | ||
| 130 | .ack = spider_ack_irq, | ||
| 131 | .end = spider_end_irq, | ||
| 132 | }; | ||
| 133 | |||
| 134 | |||
| 135 | int spider_get_irq(unsigned long int_pending) | ||
| 136 | { | ||
| 137 | void __iomem *regs = spider_get_pic(int_pending); | ||
| 138 | unsigned long cs; | ||
| 139 | int irq; | ||
| 140 | |||
| 141 | cs = in_be32(regs + TIR_CS); | ||
| 142 | |||
| 143 | irq = cs >> 24; | ||
| 144 | if (irq != 63) | ||
| 145 | return irq; | ||
| 146 | |||
| 147 | return -1; | ||
| 148 | } | ||
| 149 | |||
| 150 | void spider_init_IRQ(void) | ||
| 151 | { | ||
| 152 | int node; | ||
| 153 | struct device_node *dn; | ||
| 154 | unsigned int *property; | ||
| 155 | long spiderpic; | ||
| 156 | int n; | ||
| 157 | |||
| 158 | /* FIXME: detect multiple PICs as soon as the device tree has them */ | ||
| 159 | for (node = 0; node < 1; node++) { | ||
| 160 | dn = of_find_node_by_path("/"); | ||
| 161 | n = prom_n_addr_cells(dn); | ||
| 162 | property = (unsigned int *) get_property(dn, | ||
| 163 | "platform-spider-pic", NULL); | ||
| 164 | |||
| 165 | if (!property) | ||
| 166 | continue; | ||
| 167 | for (spiderpic = 0; n > 0; --n) | ||
| 168 | spiderpic = (spiderpic << 32) + *property++; | ||
| 169 | printk(KERN_DEBUG "SPIDER addr: %lx\n", spiderpic); | ||
| 170 | spider_pics[node] = __ioremap(spiderpic, 0x800, _PAGE_NO_CACHE); | ||
| 171 | for (n = 0; n < IIC_NUM_EXT; n++) { | ||
| 172 | int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE; | ||
| 173 | get_irq_desc(irq)->handler = &spider_pic; | ||
| 174 | |||
| 175 | /* do not mask any interrupts because of level */ | ||
| 176 | out_be32(spider_pics[node] + TIR_MSK, 0x0); | ||
| 177 | |||
| 178 | /* disable edge detection clear */ | ||
| 179 | /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */ | ||
| 180 | |||
| 181 | /* enable interrupt packets to be output */ | ||
| 182 | out_be32(spider_pics[node] + TIR_PIEN, | ||
| 183 | in_be32(spider_pics[node] + TIR_PIEN) | 0x1); | ||
| 184 | |||
| 185 | /* Enable the interrupt detection enable bit. Do this last! */ | ||
| 186 | out_be32(spider_pics[node] + TIR_DEN, | ||
| 187 | in_be32(spider_pics[node] +TIR_DEN) | 0x1); | ||
| 188 | |||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
