diff options
Diffstat (limited to 'arch/ppc64')
-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 | } | ||