diff options
Diffstat (limited to 'arch/powerpc/platforms/pasemi/setup.c')
-rw-r--r-- | arch/powerpc/platforms/pasemi/setup.c | 134 |
1 files changed, 119 insertions, 15 deletions
diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index ffe6528048b5..5ddf40a66ae8 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c | |||
@@ -39,8 +39,21 @@ | |||
39 | 39 | ||
40 | #include "pasemi.h" | 40 | #include "pasemi.h" |
41 | 41 | ||
42 | /* SDC reset register, must be pre-mapped at reset time */ | ||
42 | static void __iomem *reset_reg; | 43 | static void __iomem *reset_reg; |
43 | 44 | ||
45 | /* Various error status registers, must be pre-mapped at MCE time */ | ||
46 | |||
47 | #define MAX_MCE_REGS 32 | ||
48 | struct mce_regs { | ||
49 | char *name; | ||
50 | void __iomem *addr; | ||
51 | }; | ||
52 | |||
53 | static struct mce_regs mce_regs[MAX_MCE_REGS]; | ||
54 | static int num_mce_regs; | ||
55 | |||
56 | |||
44 | static void pas_restart(char *cmd) | 57 | static void pas_restart(char *cmd) |
45 | { | 58 | { |
46 | printk("Restarting...\n"); | 59 | printk("Restarting...\n"); |
@@ -50,26 +63,30 @@ static void pas_restart(char *cmd) | |||
50 | 63 | ||
51 | #ifdef CONFIG_SMP | 64 | #ifdef CONFIG_SMP |
52 | static DEFINE_SPINLOCK(timebase_lock); | 65 | static DEFINE_SPINLOCK(timebase_lock); |
66 | static unsigned long timebase; | ||
53 | 67 | ||
54 | static void __devinit pas_give_timebase(void) | 68 | static void __devinit pas_give_timebase(void) |
55 | { | 69 | { |
56 | unsigned long tb; | ||
57 | |||
58 | spin_lock(&timebase_lock); | 70 | spin_lock(&timebase_lock); |
59 | mtspr(SPRN_TBCTL, TBCTL_FREEZE); | 71 | mtspr(SPRN_TBCTL, TBCTL_FREEZE); |
60 | tb = mftb(); | 72 | isync(); |
61 | mtspr(SPRN_TBCTL, TBCTL_UPDATE_LOWER | (tb & 0xffffffff)); | 73 | timebase = get_tb(); |
62 | mtspr(SPRN_TBCTL, TBCTL_UPDATE_UPPER | (tb >> 32)); | ||
63 | mtspr(SPRN_TBCTL, TBCTL_RESTART); | ||
64 | spin_unlock(&timebase_lock); | 74 | spin_unlock(&timebase_lock); |
65 | pr_debug("pas_give_timebase: cpu %d gave tb %lx\n", | 75 | |
66 | smp_processor_id(), tb); | 76 | while (timebase) |
77 | barrier(); | ||
78 | mtspr(SPRN_TBCTL, TBCTL_RESTART); | ||
67 | } | 79 | } |
68 | 80 | ||
69 | static void __devinit pas_take_timebase(void) | 81 | static void __devinit pas_take_timebase(void) |
70 | { | 82 | { |
71 | pr_debug("pas_take_timebase: cpu %d has tb %lx\n", | 83 | while (!timebase) |
72 | smp_processor_id(), mftb()); | 84 | smp_rmb(); |
85 | |||
86 | spin_lock(&timebase_lock); | ||
87 | set_tb(timebase >> 32, timebase & 0xffffffff); | ||
88 | timebase = 0; | ||
89 | spin_unlock(&timebase_lock); | ||
73 | } | 90 | } |
74 | 91 | ||
75 | struct smp_ops_t pas_smp_ops = { | 92 | struct smp_ops_t pas_smp_ops = { |
@@ -98,9 +115,60 @@ void __init pas_setup_arch(void) | |||
98 | /* Remap SDC register for doing reset */ | 115 | /* Remap SDC register for doing reset */ |
99 | /* XXXOJN This should maybe come out of the device tree */ | 116 | /* XXXOJN This should maybe come out of the device tree */ |
100 | reset_reg = ioremap(0xfc101100, 4); | 117 | reset_reg = ioremap(0xfc101100, 4); |
118 | } | ||
119 | |||
120 | static int __init pas_setup_mce_regs(void) | ||
121 | { | ||
122 | struct pci_dev *dev; | ||
123 | int reg; | ||
124 | |||
125 | if (!machine_is(pasemi)) | ||
126 | return -ENODEV; | ||
127 | |||
128 | /* Remap various SoC status registers for use by the MCE handler */ | ||
129 | |||
130 | reg = 0; | ||
131 | |||
132 | dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa00a, NULL); | ||
133 | while (dev && reg < MAX_MCE_REGS) { | ||
134 | mce_regs[reg].name = kasprintf(GFP_KERNEL, | ||
135 | "mc%d_mcdebug_errsta", reg); | ||
136 | mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x730); | ||
137 | dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa00a, dev); | ||
138 | reg++; | ||
139 | } | ||
140 | |||
141 | dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa001, NULL); | ||
142 | if (dev && reg+4 < MAX_MCE_REGS) { | ||
143 | mce_regs[reg].name = "iobdbg_IntStatus1"; | ||
144 | mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x438); | ||
145 | reg++; | ||
146 | mce_regs[reg].name = "iobdbg_IOCTbusIntDbgReg"; | ||
147 | mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x454); | ||
148 | reg++; | ||
149 | mce_regs[reg].name = "iobiom_IntStatus"; | ||
150 | mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0xc10); | ||
151 | reg++; | ||
152 | mce_regs[reg].name = "iobiom_IntDbgReg"; | ||
153 | mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0xc1c); | ||
154 | reg++; | ||
155 | } | ||
156 | |||
157 | dev = pci_get_device(PCI_VENDOR_ID_PASEMI, 0xa009, NULL); | ||
158 | if (dev && reg+2 < MAX_MCE_REGS) { | ||
159 | mce_regs[reg].name = "l2csts_IntStatus"; | ||
160 | mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x200); | ||
161 | reg++; | ||
162 | mce_regs[reg].name = "l2csts_Cnt"; | ||
163 | mce_regs[reg].addr = pasemi_pci_getcfgaddr(dev, 0x214); | ||
164 | reg++; | ||
165 | } | ||
101 | 166 | ||
102 | pasemi_idle_init(); | 167 | num_mce_regs = reg; |
168 | |||
169 | return 0; | ||
103 | } | 170 | } |
171 | device_initcall(pas_setup_mce_regs); | ||
104 | 172 | ||
105 | static __init void pas_init_IRQ(void) | 173 | static __init void pas_init_IRQ(void) |
106 | { | 174 | { |
@@ -162,25 +230,34 @@ static int pas_machine_check_handler(struct pt_regs *regs) | |||
162 | { | 230 | { |
163 | int cpu = smp_processor_id(); | 231 | int cpu = smp_processor_id(); |
164 | unsigned long srr0, srr1, dsisr; | 232 | unsigned long srr0, srr1, dsisr; |
233 | int dump_slb = 0; | ||
234 | int i; | ||
165 | 235 | ||
166 | srr0 = regs->nip; | 236 | srr0 = regs->nip; |
167 | srr1 = regs->msr; | 237 | srr1 = regs->msr; |
168 | dsisr = mfspr(SPRN_DSISR); | 238 | dsisr = mfspr(SPRN_DSISR); |
169 | printk(KERN_ERR "Machine Check on CPU %d\n", cpu); | 239 | printk(KERN_ERR "Machine Check on CPU %d\n", cpu); |
170 | printk(KERN_ERR "SRR0 0x%016lx SRR1 0x%016lx\n", srr0, srr1); | 240 | printk(KERN_ERR "SRR0 0x%016lx SRR1 0x%016lx\n", srr0, srr1); |
171 | printk(KERN_ERR "DSISR 0x%016lx DAR 0x%016lx\n", dsisr, regs->dar); | 241 | printk(KERN_ERR "DSISR 0x%016lx DAR 0x%016lx\n", dsisr, regs->dar); |
242 | printk(KERN_ERR "BER 0x%016lx MER 0x%016lx\n", mfspr(SPRN_PA6T_BER), | ||
243 | mfspr(SPRN_PA6T_MER)); | ||
244 | printk(KERN_ERR "IER 0x%016lx DER 0x%016lx\n", mfspr(SPRN_PA6T_IER), | ||
245 | mfspr(SPRN_PA6T_DER)); | ||
172 | printk(KERN_ERR "Cause:\n"); | 246 | printk(KERN_ERR "Cause:\n"); |
173 | 247 | ||
174 | if (srr1 & 0x200000) | 248 | if (srr1 & 0x200000) |
175 | printk(KERN_ERR "Signalled by SDC\n"); | 249 | printk(KERN_ERR "Signalled by SDC\n"); |
250 | |||
176 | if (srr1 & 0x100000) { | 251 | if (srr1 & 0x100000) { |
177 | printk(KERN_ERR "Load/Store detected error:\n"); | 252 | printk(KERN_ERR "Load/Store detected error:\n"); |
178 | if (dsisr & 0x8000) | 253 | if (dsisr & 0x8000) |
179 | printk(KERN_ERR "D-cache ECC double-bit error or bus error\n"); | 254 | printk(KERN_ERR "D-cache ECC double-bit error or bus error\n"); |
180 | if (dsisr & 0x4000) | 255 | if (dsisr & 0x4000) |
181 | printk(KERN_ERR "LSU snoop response error\n"); | 256 | printk(KERN_ERR "LSU snoop response error\n"); |
182 | if (dsisr & 0x2000) | 257 | if (dsisr & 0x2000) { |
183 | printk(KERN_ERR "MMU SLB multi-hit or invalid B field\n"); | 258 | printk(KERN_ERR "MMU SLB multi-hit or invalid B field\n"); |
259 | dump_slb = 1; | ||
260 | } | ||
184 | if (dsisr & 0x1000) | 261 | if (dsisr & 0x1000) |
185 | printk(KERN_ERR "Recoverable Duptags\n"); | 262 | printk(KERN_ERR "Recoverable Duptags\n"); |
186 | if (dsisr & 0x800) | 263 | if (dsisr & 0x800) |
@@ -188,13 +265,40 @@ static int pas_machine_check_handler(struct pt_regs *regs) | |||
188 | if (dsisr & 0x400) | 265 | if (dsisr & 0x400) |
189 | printk(KERN_ERR "TLB parity error count overflow\n"); | 266 | printk(KERN_ERR "TLB parity error count overflow\n"); |
190 | } | 267 | } |
268 | |||
191 | if (srr1 & 0x80000) | 269 | if (srr1 & 0x80000) |
192 | printk(KERN_ERR "Bus Error\n"); | 270 | printk(KERN_ERR "Bus Error\n"); |
193 | if (srr1 & 0x40000) | 271 | |
272 | if (srr1 & 0x40000) { | ||
194 | printk(KERN_ERR "I-side SLB multiple hit\n"); | 273 | printk(KERN_ERR "I-side SLB multiple hit\n"); |
274 | dump_slb = 1; | ||
275 | } | ||
276 | |||
195 | if (srr1 & 0x20000) | 277 | if (srr1 & 0x20000) |
196 | printk(KERN_ERR "I-cache parity error hit\n"); | 278 | printk(KERN_ERR "I-cache parity error hit\n"); |
197 | 279 | ||
280 | if (num_mce_regs == 0) | ||
281 | printk(KERN_ERR "No MCE registers mapped yet, can't dump\n"); | ||
282 | else | ||
283 | printk(KERN_ERR "SoC debug registers:\n"); | ||
284 | |||
285 | for (i = 0; i < num_mce_regs; i++) | ||
286 | printk(KERN_ERR "%s: 0x%08x\n", mce_regs[i].name, | ||
287 | in_le32(mce_regs[i].addr)); | ||
288 | |||
289 | if (dump_slb) { | ||
290 | unsigned long e, v; | ||
291 | int i; | ||
292 | |||
293 | printk(KERN_ERR "slb contents:\n"); | ||
294 | for (i = 0; i < SLB_NUM_ENTRIES; i++) { | ||
295 | asm volatile("slbmfee %0,%1" : "=r" (e) : "r" (i)); | ||
296 | asm volatile("slbmfev %0,%1" : "=r" (v) : "r" (i)); | ||
297 | printk(KERN_ERR "%02d %016lx %016lx\n", i, e, v); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | |||
198 | /* SRR1[62] is from MSR[62] if recoverable, so pass that back */ | 302 | /* SRR1[62] is from MSR[62] if recoverable, so pass that back */ |
199 | return !!(srr1 & 0x2); | 303 | return !!(srr1 & 0x2); |
200 | } | 304 | } |