aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2006-06-19 14:33:16 -0400
committerPaul Mackerras <paulus@samba.org>2006-06-21 01:01:29 -0400
commitacf7d76827a577059636e949079021e6af6dd702 (patch)
tree283e94488c79e75dd3df9a376e1e8a27a69e26ec
parentef82a306b46dbedaecbb154b24d05dfab937df35 (diff)
[POWERPC] cell: add RAS support
This is a first version of support for the Cell BE "Reliability, Availability and Serviceability" features. It doesn't yet handle some of the RAS interrupts (the ones described in iic_is/iic_irr), I'm still working on a proper way to expose these. They are essentially a cascaded controller by themselves (sic !) though I may just handle them locally to the iic driver. I need also to sync with David Erb on the way he hooked in the performance monitor interrupt. So that's all for 2.6.17 and I'll do more work on that with my rework of the powerpc interrupt layer that I'm hacking on at the moment. Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/configs/cell_defconfig1
-rw-r--r--arch/powerpc/kernel/head_64.S29
-rw-r--r--arch/powerpc/kernel/prom.c43
-rw-r--r--arch/powerpc/platforms/cell/Kconfig4
-rw-r--r--arch/powerpc/platforms/cell/Makefile3
-rw-r--r--arch/powerpc/platforms/cell/cbe_regs.c128
-rw-r--r--arch/powerpc/platforms/cell/cbe_regs.h129
-rw-r--r--arch/powerpc/platforms/cell/interrupt.c37
-rw-r--r--arch/powerpc/platforms/cell/pervasive.c104
-rw-r--r--arch/powerpc/platforms/cell/pervasive.h37
-rw-r--r--arch/powerpc/platforms/cell/ras.c112
-rw-r--r--arch/powerpc/platforms/cell/ras.h9
-rw-r--r--arch/powerpc/platforms/cell/setup.c10
-rw-r--r--include/asm-powerpc/prom.h3
-rw-r--r--include/asm-powerpc/reg.h2
15 files changed, 505 insertions, 146 deletions
diff --git a/arch/powerpc/configs/cell_defconfig b/arch/powerpc/configs/cell_defconfig
index dbe421dc3c11..5b8599d03f16 100644
--- a/arch/powerpc/configs/cell_defconfig
+++ b/arch/powerpc/configs/cell_defconfig
@@ -133,6 +133,7 @@ CONFIG_CELL_IIC=y
133# 133#
134CONFIG_SPU_FS=m 134CONFIG_SPU_FS=m
135CONFIG_SPUFS_MMAP=y 135CONFIG_SPUFS_MMAP=y
136CONFIG_CBE_RAS=y
136 137
137# 138#
138# Kernel options 139# Kernel options
diff --git a/arch/powerpc/kernel/head_64.S b/arch/powerpc/kernel/head_64.S
index b7d140430a41..831acbdf2592 100644
--- a/arch/powerpc/kernel/head_64.S
+++ b/arch/powerpc/kernel/head_64.S
@@ -316,6 +316,21 @@ label##_pSeries: \
316 mtspr SPRN_SPRG1,r13; /* save r13 */ \ 316 mtspr SPRN_SPRG1,r13; /* save r13 */ \
317 EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common) 317 EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common)
318 318
319#define HSTD_EXCEPTION_PSERIES(n, label) \
320 . = n; \
321 .globl label##_pSeries; \
322label##_pSeries: \
323 HMT_MEDIUM; \
324 mtspr SPRN_SPRG1,r20; /* save r20 */ \
325 mfspr r20,SPRN_HSRR0; /* copy HSRR0 to SRR0 */ \
326 mtspr SPRN_SRR0,r20; \
327 mfspr r20,SPRN_HSRR1; /* copy HSRR0 to SRR0 */ \
328 mtspr SPRN_SRR1,r20; \
329 mfspr r20,SPRN_SPRG1; /* restore r20 */ \
330 mtspr SPRN_SPRG1,r13; /* save r13 */ \
331 EXCEPTION_PROLOG_PSERIES(PACA_EXGEN, label##_common)
332
333
319#define STD_EXCEPTION_ISERIES(n, label, area) \ 334#define STD_EXCEPTION_ISERIES(n, label, area) \
320 .globl label##_iSeries; \ 335 .globl label##_iSeries; \
321label##_iSeries: \ 336label##_iSeries: \
@@ -544,8 +559,17 @@ system_call_pSeries:
544 559
545 STD_EXCEPTION_PSERIES(0xf20, altivec_unavailable) 560 STD_EXCEPTION_PSERIES(0xf20, altivec_unavailable)
546 561
562#ifdef CONFIG_CBE_RAS
563 HSTD_EXCEPTION_PSERIES(0x1200, cbe_system_error)
564#endif /* CONFIG_CBE_RAS */
547 STD_EXCEPTION_PSERIES(0x1300, instruction_breakpoint) 565 STD_EXCEPTION_PSERIES(0x1300, instruction_breakpoint)
566#ifdef CONFIG_CBE_RAS
567 HSTD_EXCEPTION_PSERIES(0x1600, cbe_maintenance)
568#endif /* CONFIG_CBE_RAS */
548 STD_EXCEPTION_PSERIES(0x1700, altivec_assist) 569 STD_EXCEPTION_PSERIES(0x1700, altivec_assist)
570#ifdef CONFIG_CBE_RAS
571 HSTD_EXCEPTION_PSERIES(0x1800, cbe_thermal)
572#endif /* CONFIG_CBE_RAS */
549 573
550 . = 0x3000 574 . = 0x3000
551 575
@@ -827,6 +851,11 @@ machine_check_common:
827#else 851#else
828 STD_EXCEPTION_COMMON(0x1700, altivec_assist, .unknown_exception) 852 STD_EXCEPTION_COMMON(0x1700, altivec_assist, .unknown_exception)
829#endif 853#endif
854#ifdef CONFIG_CBE_RAS
855 STD_EXCEPTION_COMMON(0x1200, cbe_system_error, .cbe_system_error_exception)
856 STD_EXCEPTION_COMMON(0x1600, cbe_maintenance, .cbe_maintenance_exception)
857 STD_EXCEPTION_COMMON(0x1800, cbe_thermal, .cbe_thermal_exception)
858#endif /* CONFIG_CBE_RAS */
830 859
831/* 860/*
832 * Here we have detected that the kernel stack pointer is bad. 861 * Here we have detected that the kernel stack pointer is bad.
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 629023240ece..483455c5bb02 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -2105,3 +2105,46 @@ int prom_update_property(struct device_node *np,
2105 return 0; 2105 return 0;
2106} 2106}
2107 2107
2108
2109/* Find the device node for a given logical cpu number, also returns the cpu
2110 * local thread number (index in ibm,interrupt-server#s) if relevant and
2111 * asked for (non NULL)
2112 */
2113struct device_node *of_get_cpu_node(int cpu, unsigned int *thread)
2114{
2115 int hardid;
2116 struct device_node *np;
2117
2118 hardid = get_hard_smp_processor_id(cpu);
2119
2120 for_each_node_by_type(np, "cpu") {
2121 u32 *intserv;
2122 unsigned int plen, t;
2123
2124 /* Check for ibm,ppc-interrupt-server#s. If it doesn't exist
2125 * fallback to "reg" property and assume no threads
2126 */
2127 intserv = (u32 *)get_property(np, "ibm,ppc-interrupt-server#s",
2128 &plen);
2129 if (intserv == NULL) {
2130 u32 *reg = (u32 *)get_property(np, "reg", NULL);
2131 if (reg == NULL)
2132 continue;
2133 if (*reg == hardid) {
2134 if (thread)
2135 *thread = 0;
2136 return np;
2137 }
2138 } else {
2139 plen /= sizeof(u32);
2140 for (t = 0; t < plen; t++) {
2141 if (hardid == intserv[t]) {
2142 if (thread)
2143 *thread = t;
2144 return np;
2145 }
2146 }
2147 }
2148 }
2149 return NULL;
2150}
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig
index 6a02d51086c8..00b83db2ce73 100644
--- a/arch/powerpc/platforms/cell/Kconfig
+++ b/arch/powerpc/platforms/cell/Kconfig
@@ -16,4 +16,8 @@ config SPUFS_MMAP
16 select MEMORY_HOTPLUG 16 select MEMORY_HOTPLUG
17 default y 17 default y
18 18
19config CBE_RAS
20 bool "RAS features for bare metal Cell BE"
21 default y
22
19endmenu 23endmenu
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
index e570bad06394..6b11f6adca08 100644
--- a/arch/powerpc/platforms/cell/Makefile
+++ b/arch/powerpc/platforms/cell/Makefile
@@ -1,5 +1,6 @@
1obj-y += interrupt.o iommu.o setup.o spider-pic.o 1obj-y += interrupt.o iommu.o setup.o spider-pic.o
2obj-y += pervasive.o 2obj-y += cbe_regs.o pervasive.o
3obj-$(CONFIG_CBE_RAS) += ras.o
3 4
4obj-$(CONFIG_SMP) += smp.o 5obj-$(CONFIG_SMP) += smp.o
5obj-$(CONFIG_SPU_FS) += spu-base.o spufs/ 6obj-$(CONFIG_SPU_FS) += spu-base.o spufs/
diff --git a/arch/powerpc/platforms/cell/cbe_regs.c b/arch/powerpc/platforms/cell/cbe_regs.c
new file mode 100644
index 000000000000..2dfde61c8412
--- /dev/null
+++ b/arch/powerpc/platforms/cell/cbe_regs.c
@@ -0,0 +1,128 @@
1/*
2 * cbe_regs.c
3 *
4 * Accessor routines for the various MMIO register blocks of the CBE
5 *
6 * (c) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
7 */
8
9
10#include <linux/config.h>
11#include <linux/percpu.h>
12#include <linux/types.h>
13
14#include <asm/io.h>
15#include <asm/pgtable.h>
16#include <asm/prom.h>
17#include <asm/ptrace.h>
18
19#include "cbe_regs.h"
20
21#define MAX_CBE 2
22
23/*
24 * Current implementation uses "cpu" nodes. We build our own mapping
25 * array of cpu numbers to cpu nodes locally for now to allow interrupt
26 * time code to have a fast path rather than call of_get_cpu_node(). If
27 * we implement cpu hotplug, we'll have to install an appropriate norifier
28 * in order to release references to the cpu going away
29 */
30static struct cbe_regs_map
31{
32 struct device_node *cpu_node;
33 struct cbe_pmd_regs __iomem *pmd_regs;
34 struct cbe_iic_regs __iomem *iic_regs;
35} cbe_regs_maps[MAX_CBE];
36static int cbe_regs_map_count;
37
38static struct cbe_thread_map
39{
40 struct device_node *cpu_node;
41 struct cbe_regs_map *regs;
42} cbe_thread_map[NR_CPUS];
43
44static struct cbe_regs_map *cbe_find_map(struct device_node *np)
45{
46 int i;
47
48 for (i = 0; i < cbe_regs_map_count; i++)
49 if (cbe_regs_maps[i].cpu_node == np)
50 return &cbe_regs_maps[i];
51 return NULL;
52}
53
54struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np)
55{
56 struct cbe_regs_map *map = cbe_find_map(np);
57 if (map == NULL)
58 return NULL;
59 return map->pmd_regs;
60}
61
62struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu)
63{
64 struct cbe_regs_map *map = cbe_thread_map[cpu].regs;
65 if (map == NULL)
66 return NULL;
67 return map->pmd_regs;
68}
69
70
71struct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np)
72{
73 struct cbe_regs_map *map = cbe_find_map(np);
74 if (map == NULL)
75 return NULL;
76 return map->iic_regs;
77}
78struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu)
79{
80 struct cbe_regs_map *map = cbe_thread_map[cpu].regs;
81 if (map == NULL)
82 return NULL;
83 return map->iic_regs;
84}
85
86void __init cbe_regs_init(void)
87{
88 int i;
89 struct device_node *cpu;
90
91 /* Build local fast map of CPUs */
92 for_each_cpu(i)
93 cbe_thread_map[i].cpu_node = of_get_cpu_node(i, NULL);
94
95 /* Find maps for each device tree CPU */
96 for_each_node_by_type(cpu, "cpu") {
97 struct cbe_regs_map *map = &cbe_regs_maps[cbe_regs_map_count++];
98
99 /* That hack must die die die ! */
100 struct address_prop {
101 unsigned long address;
102 unsigned int len;
103 } __attribute__((packed)) *prop;
104
105
106 if (cbe_regs_map_count > MAX_CBE) {
107 printk(KERN_ERR "cbe_regs: More BE chips than supported"
108 "!\n");
109 cbe_regs_map_count--;
110 return;
111 }
112 map->cpu_node = cpu;
113 for_each_cpu(i)
114 if (cbe_thread_map[i].cpu_node == cpu)
115 cbe_thread_map[i].regs = map;
116
117 prop = (struct address_prop *)get_property(cpu, "pervasive",
118 NULL);
119 if (prop != NULL)
120 map->pmd_regs = ioremap(prop->address, prop->len);
121
122 prop = (struct address_prop *)get_property(cpu, "iic",
123 NULL);
124 if (prop != NULL)
125 map->iic_regs = ioremap(prop->address, prop->len);
126 }
127}
128
diff --git a/arch/powerpc/platforms/cell/cbe_regs.h b/arch/powerpc/platforms/cell/cbe_regs.h
new file mode 100644
index 000000000000..e76e4a6af5bc
--- /dev/null
+++ b/arch/powerpc/platforms/cell/cbe_regs.h
@@ -0,0 +1,129 @@
1/*
2 * cbe_regs.h
3 *
4 * This file is intended to hold the various register definitions for CBE
5 * on-chip system devices (memory controller, IO controller, etc...)
6 *
7 * (c) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
8 */
9
10#ifndef CBE_REGS_H
11#define CBE_REGS_H
12
13/*
14 *
15 * Some HID register definitions
16 *
17 */
18
19/* CBE specific HID0 bits */
20#define HID0_CBE_THERM_WAKEUP 0x0000020000000000ul
21#define HID0_CBE_SYSERR_WAKEUP 0x0000008000000000ul
22#define HID0_CBE_THERM_INT_EN 0x0000000400000000ul
23#define HID0_CBE_SYSERR_INT_EN 0x0000000200000000ul
24
25
26/*
27 *
28 * Pervasive unit register definitions
29 *
30 */
31
32struct cbe_pmd_regs {
33 u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */
34
35 /* Thermal Sensor Registers */
36 u64 ts_ctsr1; /* 0x0800 */
37 u64 ts_ctsr2; /* 0x0808 */
38 u64 ts_mtsr1; /* 0x0810 */
39 u64 ts_mtsr2; /* 0x0818 */
40 u64 ts_itr1; /* 0x0820 */
41 u64 ts_itr2; /* 0x0828 */
42 u64 ts_gitr; /* 0x0830 */
43 u64 ts_isr; /* 0x0838 */
44 u64 ts_imr; /* 0x0840 */
45 u64 tm_cr1; /* 0x0848 */
46 u64 tm_cr2; /* 0x0850 */
47 u64 tm_simr; /* 0x0858 */
48 u64 tm_tpr; /* 0x0860 */
49 u64 tm_str1; /* 0x0868 */
50 u64 tm_str2; /* 0x0870 */
51 u64 tm_tsr; /* 0x0878 */
52
53 /* Power Management */
54 u64 pm_control; /* 0x0880 */
55#define CBE_PMD_PAUSE_ZERO_CONTROL 0x10000
56 u64 pm_status; /* 0x0888 */
57
58 /* Time Base Register */
59 u64 tbr; /* 0x0890 */
60
61 u8 pad_0x0898_0x0c00 [0x0c00 - 0x0898]; /* 0x0898 */
62
63 /* Fault Isolation Registers */
64 u64 checkstop_fir; /* 0x0c00 */
65 u64 recoverable_fir;
66 u64 spec_att_mchk_fir;
67 u64 fir_mode_reg;
68 u64 fir_enable_mask;
69
70 u8 pad_0x0c28_0x1000 [0x1000 - 0x0c28]; /* 0x0c28 */
71};
72
73extern struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np);
74extern struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu);
75
76/*
77 *
78 * IIC unit register definitions
79 *
80 */
81
82struct cbe_iic_pending_bits {
83 u32 data;
84 u8 flags;
85 u8 class;
86 u8 source;
87 u8 prio;
88};
89
90#define CBE_IIC_IRQ_VALID 0x80
91#define CBE_IIC_IRQ_IPI 0x40
92
93struct cbe_iic_thread_regs {
94 struct cbe_iic_pending_bits pending;
95 struct cbe_iic_pending_bits pending_destr;
96 u64 generate;
97 u64 prio;
98};
99
100struct cbe_iic_regs {
101 u8 pad_0x0000_0x0400[0x0400 - 0x0000]; /* 0x0000 */
102
103 /* IIC interrupt registers */
104 struct cbe_iic_thread_regs thread[2]; /* 0x0400 */
105 u64 iic_ir; /* 0x0440 */
106 u64 iic_is; /* 0x0448 */
107
108 u8 pad_0x0450_0x0500[0x0500 - 0x0450]; /* 0x0450 */
109
110 /* IOC FIR */
111 u64 ioc_fir_reset; /* 0x0500 */
112 u64 ioc_fir_set;
113 u64 ioc_checkstop_enable;
114 u64 ioc_fir_error_mask;
115 u64 ioc_syserr_enable;
116 u64 ioc_fir;
117
118 u8 pad_0x0530_0x1000[0x1000 - 0x0530]; /* 0x0530 */
119};
120
121extern struct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np);
122extern struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu);
123
124
125/* Init this module early */
126extern void cbe_regs_init(void);
127
128
129#endif /* CBE_REGS_H */
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c
index 978be1c30c1b..0a707bcabef6 100644
--- a/arch/powerpc/platforms/cell/interrupt.c
+++ b/arch/powerpc/platforms/cell/interrupt.c
@@ -33,29 +33,10 @@
33#include <asm/ptrace.h> 33#include <asm/ptrace.h>
34 34
35#include "interrupt.h" 35#include "interrupt.h"
36 36#include "cbe_regs.h"
37struct iic_pending_bits {
38 u32 data;
39 u8 flags;
40 u8 class;
41 u8 source;
42 u8 prio;
43};
44
45enum iic_pending_flags {
46 IIC_VALID = 0x80,
47 IIC_IPI = 0x40,
48};
49
50struct iic_regs {
51 struct iic_pending_bits pending;
52 struct iic_pending_bits pending_destr;
53 u64 generate;
54 u64 prio;
55};
56 37
57struct iic { 38struct iic {
58 struct iic_regs __iomem *regs; 39 struct cbe_iic_thread_regs __iomem *regs;
59 u8 target_id; 40 u8 target_id;
60}; 41};
61 42
@@ -115,7 +96,7 @@ static struct hw_interrupt_type iic_pic = {
115 .end = iic_end, 96 .end = iic_end,
116}; 97};
117 98
118static int iic_external_get_irq(struct iic_pending_bits pending) 99static int iic_external_get_irq(struct cbe_iic_pending_bits pending)
119{ 100{
120 int irq; 101 int irq;
121 unsigned char node, unit; 102 unsigned char node, unit;
@@ -168,15 +149,15 @@ int iic_get_irq(struct pt_regs *regs)
168{ 149{
169 struct iic *iic; 150 struct iic *iic;
170 int irq; 151 int irq;
171 struct iic_pending_bits pending; 152 struct cbe_iic_pending_bits pending;
172 153
173 iic = &__get_cpu_var(iic); 154 iic = &__get_cpu_var(iic);
174 *(unsigned long *) &pending = 155 *(unsigned long *) &pending =
175 in_be64((unsigned long __iomem *) &iic->regs->pending_destr); 156 in_be64((unsigned long __iomem *) &iic->regs->pending_destr);
176 157
177 irq = -1; 158 irq = -1;
178 if (pending.flags & IIC_VALID) { 159 if (pending.flags & CBE_IIC_IRQ_VALID) {
179 if (pending.flags & IIC_IPI) { 160 if (pending.flags & CBE_IIC_IRQ_IPI) {
180 irq = IIC_IPI_OFFSET + (pending.prio >> 4); 161 irq = IIC_IPI_OFFSET + (pending.prio >> 4);
181/* 162/*
182 if (irq > 0x80) 163 if (irq > 0x80)
@@ -226,7 +207,7 @@ static int setup_iic_hardcoded(void)
226 regs += 0x20; 207 regs += 0x20;
227 208
228 printk(KERN_INFO "IIC for CPU %d at %lx\n", cpu, regs); 209 printk(KERN_INFO "IIC for CPU %d at %lx\n", cpu, regs);
229 iic->regs = ioremap(regs, sizeof(struct iic_regs)); 210 iic->regs = ioremap(regs, sizeof(struct cbe_iic_thread_regs));
230 iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe); 211 iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe);
231 } 212 }
232 213
@@ -267,12 +248,12 @@ static int setup_iic(void)
267 } 248 }
268 249
269 iic = &per_cpu(iic, np[0]); 250 iic = &per_cpu(iic, np[0]);
270 iic->regs = ioremap(regs[0], sizeof(struct iic_regs)); 251 iic->regs = ioremap(regs[0], sizeof(struct cbe_iic_thread_regs));
271 iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe); 252 iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe);
272 printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs); 253 printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs);
273 254
274 iic = &per_cpu(iic, np[1]); 255 iic = &per_cpu(iic, np[1]);
275 iic->regs = ioremap(regs[2], sizeof(struct iic_regs)); 256 iic->regs = ioremap(regs[2], sizeof(struct cbe_iic_thread_regs));
276 iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe); 257 iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe);
277 printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs); 258 printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs);
278 259
diff --git a/arch/powerpc/platforms/cell/pervasive.c b/arch/powerpc/platforms/cell/pervasive.c
index 7eed8c624517..695ac4e1617e 100644
--- a/arch/powerpc/platforms/cell/pervasive.c
+++ b/arch/powerpc/platforms/cell/pervasive.c
@@ -37,36 +37,28 @@
37#include <asm/reg.h> 37#include <asm/reg.h>
38 38
39#include "pervasive.h" 39#include "pervasive.h"
40#include "cbe_regs.h"
40 41
41static DEFINE_SPINLOCK(cbe_pervasive_lock); 42static DEFINE_SPINLOCK(cbe_pervasive_lock);
42struct cbe_pervasive {
43 struct pmd_regs __iomem *regs;
44 unsigned int thread;
45};
46
47/* can't use per_cpu from setup_arch */
48static struct cbe_pervasive cbe_pervasive[NR_CPUS];
49 43
50static void __init cbe_enable_pause_zero(void) 44static void __init cbe_enable_pause_zero(void)
51{ 45{
52 unsigned long thread_switch_control; 46 unsigned long thread_switch_control;
53 unsigned long temp_register; 47 unsigned long temp_register;
54 struct cbe_pervasive *p; 48 struct cbe_pmd_regs __iomem *pregs;
55 int thread;
56 49
57 spin_lock_irq(&cbe_pervasive_lock); 50 spin_lock_irq(&cbe_pervasive_lock);
58 p = &cbe_pervasive[smp_processor_id()]; 51 pregs = cbe_get_cpu_pmd_regs(smp_processor_id());
59 52 if (pregs == NULL)
60 if (!cbe_pervasive->regs)
61 goto out; 53 goto out;
62 54
63 pr_debug("Power Management: CPU %d\n", smp_processor_id()); 55 pr_debug("Power Management: CPU %d\n", smp_processor_id());
64 56
65 /* Enable Pause(0) control bit */ 57 /* Enable Pause(0) control bit */
66 temp_register = in_be64(&p->regs->pm_control); 58 temp_register = in_be64(&pregs->pm_control);
67 59
68 out_be64(&p->regs->pm_control, 60 out_be64(&pregs->pm_control,
69 temp_register|PMD_PAUSE_ZERO_CONTROL); 61 temp_register | CBE_PMD_PAUSE_ZERO_CONTROL);
70 62
71 /* Enable DEC and EE interrupt request */ 63 /* Enable DEC and EE interrupt request */
72 thread_switch_control = mfspr(SPRN_TSC_CELL); 64 thread_switch_control = mfspr(SPRN_TSC_CELL);
@@ -75,25 +67,16 @@ static void __init cbe_enable_pause_zero(void)
75 switch ((mfspr(SPRN_CTRLF) & CTRL_CT)) { 67 switch ((mfspr(SPRN_CTRLF) & CTRL_CT)) {
76 case CTRL_CT0: 68 case CTRL_CT0:
77 thread_switch_control |= TSC_CELL_DEC_ENABLE_0; 69 thread_switch_control |= TSC_CELL_DEC_ENABLE_0;
78 thread = 0;
79 break; 70 break;
80 case CTRL_CT1: 71 case CTRL_CT1:
81 thread_switch_control |= TSC_CELL_DEC_ENABLE_1; 72 thread_switch_control |= TSC_CELL_DEC_ENABLE_1;
82 thread = 1;
83 break; 73 break;
84 default: 74 default:
85 printk(KERN_WARNING "%s: unknown configuration\n", 75 printk(KERN_WARNING "%s: unknown configuration\n",
86 __FUNCTION__); 76 __FUNCTION__);
87 thread = -1;
88 break; 77 break;
89 } 78 }
90 79
91 if (p->thread != thread)
92 printk(KERN_WARNING "%s: device tree inconsistant, "
93 "cpu %i: %d/%d\n", __FUNCTION__,
94 smp_processor_id(),
95 p->thread, thread);
96
97 mtspr(SPRN_TSC_CELL, thread_switch_control); 80 mtspr(SPRN_TSC_CELL, thread_switch_control);
98 81
99out: 82out:
@@ -104,6 +87,11 @@ static void cbe_idle(void)
104{ 87{
105 unsigned long ctrl; 88 unsigned long ctrl;
106 89
90 /* Why do we do that on every idle ? Couldn't that be done once for
91 * all or do we lose the state some way ? Also, the pm_control
92 * register setting, that can't be set once at boot ? We really want
93 * to move that away in order to implement a simple powersave
94 */
107 cbe_enable_pause_zero(); 95 cbe_enable_pause_zero();
108 96
109 while (1) { 97 while (1) {
@@ -152,8 +140,15 @@ static int cbe_system_reset_exception(struct pt_regs *regs)
152 timer_interrupt(regs); 140 timer_interrupt(regs);
153 break; 141 break;
154 case SRR1_WAKEMT: 142 case SRR1_WAKEMT:
155 /* no action required */
156 break; 143 break;
144#ifdef CONFIG_CBE_RAS
145 case SRR1_WAKESYSERR:
146 cbe_system_error_exception(regs);
147 break;
148 case SRR1_WAKETHERM:
149 cbe_thermal_exception(regs);
150 break;
151#endif /* CONFIG_CBE_RAS */
157 default: 152 default:
158 /* do system reset */ 153 /* do system reset */
159 return 0; 154 return 0;
@@ -162,68 +157,11 @@ static int cbe_system_reset_exception(struct pt_regs *regs)
162 return 1; 157 return 1;
163} 158}
164 159
165static int __init cbe_find_pmd_mmio(int cpu, struct cbe_pervasive *p) 160void __init cbe_pervasive_init(void)
166{
167 struct device_node *node;
168 unsigned int *int_servers;
169 char *addr;
170 unsigned long real_address;
171 unsigned int size;
172
173 struct pmd_regs __iomem *pmd_mmio_area;
174 int hardid, thread;
175 int proplen;
176
177 pmd_mmio_area = NULL;
178 hardid = get_hard_smp_processor_id(cpu);
179 for (node = NULL; (node = of_find_node_by_type(node, "cpu"));) {
180 int_servers = (void *) get_property(node,
181 "ibm,ppc-interrupt-server#s", &proplen);
182 if (!int_servers) {
183 printk(KERN_WARNING "%s misses "
184 "ibm,ppc-interrupt-server#s property",
185 node->full_name);
186 continue;
187 }
188 for (thread = 0; thread < proplen / sizeof (int); thread++) {
189 if (hardid == int_servers[thread]) {
190 addr = get_property(node, "pervasive", NULL);
191 goto found;
192 }
193 }
194 }
195
196 printk(KERN_WARNING "%s: CPU %d not found\n", __FUNCTION__, cpu);
197 return -EINVAL;
198
199found:
200 real_address = *(unsigned long*) addr;
201 addr += sizeof (unsigned long);
202 size = *(unsigned int*) addr;
203
204 pr_debug("pervasive area for CPU %d at %lx, size %x\n",
205 cpu, real_address, size);
206 p->regs = ioremap(real_address, size);
207 p->thread = thread;
208 return 0;
209}
210
211void __init cell_pervasive_init(void)
212{ 161{
213 struct cbe_pervasive *p;
214 int cpu;
215 int ret;
216
217 if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) 162 if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO))
218 return; 163 return;
219 164
220 for_each_possible_cpu(cpu) {
221 p = &cbe_pervasive[cpu];
222 ret = cbe_find_pmd_mmio(cpu, p);
223 if (ret)
224 return;
225 }
226
227 ppc_md.idle_loop = cbe_idle; 165 ppc_md.idle_loop = cbe_idle;
228 ppc_md.system_reset_exception = cbe_system_reset_exception; 166 ppc_md.system_reset_exception = cbe_system_reset_exception;
229} 167}
diff --git a/arch/powerpc/platforms/cell/pervasive.h b/arch/powerpc/platforms/cell/pervasive.h
index da1fb85ca3e8..7b50947f8044 100644
--- a/arch/powerpc/platforms/cell/pervasive.h
+++ b/arch/powerpc/platforms/cell/pervasive.h
@@ -25,38 +25,9 @@
25#ifndef PERVASIVE_H 25#ifndef PERVASIVE_H
26#define PERVASIVE_H 26#define PERVASIVE_H
27 27
28struct pmd_regs { 28extern void cbe_pervasive_init(void);
29 u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */ 29extern void cbe_system_error_exception(struct pt_regs *regs);
30 30extern void cbe_maintenance_exception(struct pt_regs *regs);
31 /* Thermal Sensor Registers */ 31extern void cbe_thermal_exception(struct pt_regs *regs);
32 u64 ts_ctsr1; /* 0x0800 */
33 u64 ts_ctsr2; /* 0x0808 */
34 u64 ts_mtsr1; /* 0x0810 */
35 u64 ts_mtsr2; /* 0x0818 */
36 u64 ts_itr1; /* 0x0820 */
37 u64 ts_itr2; /* 0x0828 */
38 u64 ts_gitr; /* 0x0830 */
39 u64 ts_isr; /* 0x0838 */
40 u64 ts_imr; /* 0x0840 */
41 u64 tm_cr1; /* 0x0848 */
42 u64 tm_cr2; /* 0x0850 */
43 u64 tm_simr; /* 0x0858 */
44 u64 tm_tpr; /* 0x0860 */
45 u64 tm_str1; /* 0x0868 */
46 u64 tm_str2; /* 0x0870 */
47 u64 tm_tsr; /* 0x0878 */
48
49 /* Power Management */
50 u64 pm_control; /* 0x0880 */
51#define PMD_PAUSE_ZERO_CONTROL 0x10000
52 u64 pm_status; /* 0x0888 */
53
54 /* Time Base Register */
55 u64 tbr; /* 0x0890 */
56
57 u8 pad_0x0898_0x1000 [0x1000 - 0x0898]; /* 0x0898 */
58};
59
60void __init cell_pervasive_init(void);
61 32
62#endif 33#endif
diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c
new file mode 100644
index 000000000000..033ad6e2827b
--- /dev/null
+++ b/arch/powerpc/platforms/cell/ras.c
@@ -0,0 +1,112 @@
1#define DEBUG
2
3#include <linux/config.h>
4#include <linux/types.h>
5#include <linux/kernel.h>
6#include <linux/smp.h>
7
8#include <asm/reg.h>
9#include <asm/io.h>
10#include <asm/prom.h>
11#include <asm/machdep.h>
12
13#include "ras.h"
14#include "cbe_regs.h"
15
16
17static void dump_fir(int cpu)
18{
19 struct cbe_pmd_regs __iomem *pregs = cbe_get_cpu_pmd_regs(cpu);
20 struct cbe_iic_regs __iomem *iregs = cbe_get_cpu_iic_regs(cpu);
21
22 if (pregs == NULL)
23 return;
24
25 /* Todo: do some nicer parsing of bits and based on them go down
26 * to other sub-units FIRs and not only IIC
27 */
28 printk(KERN_ERR "Global Checkstop FIR : 0x%016lx\n",
29 in_be64(&pregs->checkstop_fir));
30 printk(KERN_ERR "Global Recoverable FIR : 0x%016lx\n",
31 in_be64(&pregs->checkstop_fir));
32 printk(KERN_ERR "Global MachineCheck FIR : 0x%016lx\n",
33 in_be64(&pregs->spec_att_mchk_fir));
34
35 if (iregs == NULL)
36 return;
37 printk(KERN_ERR "IOC FIR : 0x%016lx\n",
38 in_be64(&iregs->ioc_fir));
39
40}
41
42void cbe_system_error_exception(struct pt_regs *regs)
43{
44 int cpu = smp_processor_id();
45
46 printk(KERN_ERR "System Error Interrupt on CPU %d !\n", cpu);
47 dump_fir(cpu);
48 dump_stack();
49}
50
51void cbe_maintenance_exception(struct pt_regs *regs)
52{
53 int cpu = smp_processor_id();
54
55 /*
56 * Nothing implemented for the maintenance interrupt at this point
57 */
58
59 printk(KERN_ERR "Unhandled Maintenance interrupt on CPU %d !\n", cpu);
60 dump_stack();
61}
62
63void cbe_thermal_exception(struct pt_regs *regs)
64{
65 int cpu = smp_processor_id();
66
67 /*
68 * Nothing implemented for the thermal interrupt at this point
69 */
70
71 printk(KERN_ERR "Unhandled Thermal interrupt on CPU %d !\n", cpu);
72 dump_stack();
73}
74
75static int cbe_machine_check_handler(struct pt_regs *regs)
76{
77 int cpu = smp_processor_id();
78
79 printk(KERN_ERR "Machine Check Interrupt on CPU %d !\n", cpu);
80 dump_fir(cpu);
81
82 /* No recovery from this code now, lets continue */
83 return 0;
84}
85
86void __init cbe_ras_init(void)
87{
88 unsigned long hid0;
89
90 /*
91 * Enable System Error & thermal interrupts and wakeup conditions
92 */
93
94 hid0 = mfspr(SPRN_HID0);
95 hid0 |= HID0_CBE_THERM_INT_EN | HID0_CBE_THERM_WAKEUP |
96 HID0_CBE_SYSERR_INT_EN | HID0_CBE_SYSERR_WAKEUP;
97 mtspr(SPRN_HID0, hid0);
98 mb();
99
100 /*
101 * Install machine check handler. Leave setting of precise mode to
102 * what the firmware did for now
103 */
104 ppc_md.machine_check_exception = cbe_machine_check_handler;
105 mb();
106
107 /*
108 * For now, we assume that IOC_FIR is already set to forward some
109 * error conditions to the System Error handler. If that is not true
110 * then it will have to be fixed up here.
111 */
112}
diff --git a/arch/powerpc/platforms/cell/ras.h b/arch/powerpc/platforms/cell/ras.h
new file mode 100644
index 000000000000..eb7ee54c82a0
--- /dev/null
+++ b/arch/powerpc/platforms/cell/ras.h
@@ -0,0 +1,9 @@
1#ifndef RAS_H
2#define RAS_H
3
4extern void cbe_system_error_exception(struct pt_regs *regs);
5extern void cbe_maintenance_exception(struct pt_regs *regs);
6extern void cbe_thermal_exception(struct pt_regs *regs);
7extern void cbe_ras_init(void);
8
9#endif /* RAS_H */
diff --git a/arch/powerpc/platforms/cell/setup.c b/arch/powerpc/platforms/cell/setup.c
index fd3e5609e3e0..eb8ab9bbf744 100644
--- a/arch/powerpc/platforms/cell/setup.c
+++ b/arch/powerpc/platforms/cell/setup.c
@@ -52,7 +52,9 @@
52 52
53#include "interrupt.h" 53#include "interrupt.h"
54#include "iommu.h" 54#include "iommu.h"
55#include "cbe_regs.h"
55#include "pervasive.h" 56#include "pervasive.h"
57#include "ras.h"
56 58
57#ifdef DEBUG 59#ifdef DEBUG
58#define DBG(fmt...) udbg_printf(fmt) 60#define DBG(fmt...) udbg_printf(fmt)
@@ -82,6 +84,12 @@ static void __init cell_setup_arch(void)
82 ppc_md.init_IRQ = iic_init_IRQ; 84 ppc_md.init_IRQ = iic_init_IRQ;
83 ppc_md.get_irq = iic_get_irq; 85 ppc_md.get_irq = iic_get_irq;
84 86
87 cbe_regs_init();
88
89#ifdef CONFIG_CBE_RAS
90 cbe_ras_init();
91#endif
92
85#ifdef CONFIG_SMP 93#ifdef CONFIG_SMP
86 smp_init_cell(); 94 smp_init_cell();
87#endif 95#endif
@@ -98,7 +106,7 @@ static void __init cell_setup_arch(void)
98 init_pci_config_tokens(); 106 init_pci_config_tokens();
99 find_and_init_phbs(); 107 find_and_init_phbs();
100 spider_init_IRQ(); 108 spider_init_IRQ();
101 cell_pervasive_init(); 109 cbe_pervasive_init();
102#ifdef CONFIG_DUMMY_CONSOLE 110#ifdef CONFIG_DUMMY_CONSOLE
103 conswitchp = &dummy_con; 111 conswitchp = &dummy_con;
104#endif 112#endif
diff --git a/include/asm-powerpc/prom.h b/include/asm-powerpc/prom.h
index c79d58ab7441..8bdcd5178302 100644
--- a/include/asm-powerpc/prom.h
+++ b/include/asm-powerpc/prom.h
@@ -238,5 +238,8 @@ void of_parse_dma_window(struct device_node *dn, unsigned char *dma_window_prop,
238 238
239extern void kdump_move_device_tree(void); 239extern void kdump_move_device_tree(void);
240 240
241/* CPU OF node matching */
242struct device_node *of_get_cpu_node(int cpu, unsigned int *thread);
243
241#endif /* __KERNEL__ */ 244#endif /* __KERNEL__ */
242#endif /* _POWERPC_PROM_H */ 245#endif /* _POWERPC_PROM_H */
diff --git a/include/asm-powerpc/reg.h b/include/asm-powerpc/reg.h
index f2ed7577aa35..cf73475a0c69 100644
--- a/include/asm-powerpc/reg.h
+++ b/include/asm-powerpc/reg.h
@@ -386,6 +386,8 @@
386#define SRR1_WAKEMT 0x00280000 /* mtctrl */ 386#define SRR1_WAKEMT 0x00280000 /* mtctrl */
387#define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */ 387#define SRR1_WAKEDEC 0x00180000 /* Decrementer interrupt */
388#define SRR1_WAKETHERM 0x00100000 /* Thermal management interrupt */ 388#define SRR1_WAKETHERM 0x00100000 /* Thermal management interrupt */
389#define SPRN_HSRR0 0x13A /* Save/Restore Register 0 */
390#define SPRN_HSRR1 0x13B /* Save/Restore Register 1 */
389 391
390#ifndef SPRN_SVR 392#ifndef SPRN_SVR
391#define SPRN_SVR 0x11E /* System Version Register */ 393#define SPRN_SVR 0x11E /* System Version Register */