1 /* 2 * Copyright 2006-2008, IBM Corporation. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 7 * 2 of the License, or (at your option) any later version. 8 */ 9 10 #undef DEBUG 11 12 #include <linux/types.h> 13 #include <linux/kernel.h> 14 #include <linux/smp.h> 15 #include <linux/reboot.h> 16 #include <linux/kexec.h> 17 #include <linux/crash_dump.h> 18 19 #include <asm/reg.h> 20 #include <asm/io.h> 21 #include <asm/prom.h> 22 #include <asm/machdep.h> 23 #include <asm/rtas.h> 24 #include <asm/cell-regs.h> 25 26 #include "ras.h" 27 28 29 static void dump_fir(int cpu) 30 { 31 struct cbe_pmd_regs __iomem *pregs = cbe_get_cpu_pmd_regs(cpu); 32 struct cbe_iic_regs __iomem *iregs = cbe_get_cpu_iic_regs(cpu); 33 34 if (pregs == NULL) 35 return; 36 37 /* Todo: do some nicer parsing of bits and based on them go down 38 * to other sub-units FIRs and not only IIC 39 */ 40 printk(KERN_ERR "Global Checkstop FIR : 0x%016lx\n", 41 in_be64(&pregs->checkstop_fir)); 42 printk(KERN_ERR "Global Recoverable FIR : 0x%016lx\n", 43 in_be64(&pregs->checkstop_fir)); 44 printk(KERN_ERR "Global MachineCheck FIR : 0x%016lx\n", 45 in_be64(&pregs->spec_att_mchk_fir)); 46 47 if (iregs == NULL) 48 return; 49 printk(KERN_ERR "IOC FIR : 0x%016lx\n", 50 in_be64(&iregs->ioc_fir)); 51 52 } 53 54 void cbe_system_error_exception(struct pt_regs *regs) 55 { 56 int cpu = smp_processor_id(); 57 58 printk(KERN_ERR "System Error Interrupt on CPU %d !\n", cpu); 59 dump_fir(cpu); 60 dump_stack(); 61 } 62 63 void cbe_maintenance_exception(struct pt_regs *regs) 64 { 65 int cpu = smp_processor_id(); 66 67 /* 68 * Nothing implemented for the maintenance interrupt at this point 69 */ 70 71 printk(KERN_ERR "Unhandled Maintenance interrupt on CPU %d !\n", cpu); 72 dump_stack(); 73 } 74 75 void cbe_thermal_exception(struct pt_regs *regs) 76 { 77 int cpu = smp_processor_id(); 78 79 /* 80 * Nothing implemented for the thermal interrupt at this point 81 */ 82 83 printk(KERN_ERR "Unhandled Thermal interrupt on CPU %d !\n", cpu); 84 dump_stack(); 85 } 86 87 static int cbe_machine_check_handler(struct pt_regs *regs) 88 { 89 int cpu = smp_processor_id(); 90 91 printk(KERN_ERR "Machine Check Interrupt on CPU %d !\n", cpu); 92 dump_fir(cpu); 93 94 /* No recovery from this code now, lets continue */ 95 return 0; 96 } 97 98 struct ptcal_area { 99 struct list_head list; 100 int nid; 101 int order; 102 struct page *pages; 103 }; 104 105 static LIST_HEAD(ptcal_list); 106 107 static int ptcal_start_tok, ptcal_stop_tok; 108 109 static int __init cbe_ptcal_enable_on_node(int nid, int order) 110 { 111 struct ptcal_area *area; 112 int ret = -ENOMEM; 113 unsigned long addr; 114 115 if (is_kdump_kernel()) 116 rtas_call(ptcal_stop_tok, 1, 1, NULL, nid); 117 118 area = kmalloc(sizeof(*area), GFP_KERNEL); 119 if (!area) 120 goto out_err; 121 122 area->nid = nid; 123 area->order = order; 124 area->pages = alloc_pages_node(area->nid, GFP_KERNEL, area->order); 125 126 if (!area->pages) 127 goto out_free_area; 128 129 addr = __pa(page_address(area->pages)); 130 131 ret = -EIO; 132 if (rtas_call(ptcal_start_tok, 3, 1, NULL, area->nid, 133 (unsigned int)(addr >> 32), 134 (unsigned int)(addr & 0xffffffff))) { 135 printk(KERN_ERR "%s: error enabling PTCAL on node %d!\n", 136 __func__, nid); 137 goto out_free_pages; 138 } 139 140 list_add(&area->list, &ptcal_list); 141 142 return 0; 143 144 out_free_pages: 145 __free_pages(area->pages, area->order); 146 out_free_area: 147 kfree(area); 148 out_err: 149 return ret; 150 } 151 152 static int __init cbe_ptcal_enable(void) 153 { 154 const u32 *size; 155 struct device_node *np; 156 int order, found_mic = 0; 157 158 np = of_find_node_by_path("/rtas"); 159 if (!np) 160 return -ENODEV; 161 162 size = of_get_property(np, "ibm,cbe-ptcal-size", NULL); 163 if (!size) 164 return -ENODEV; 165 166 pr_debug("%s: enabling PTCAL, size = 0x%x\n", __func__, *size); 167 order = get_order(*size); 168 of_node_put(np); 169 170 /* support for malta device trees, with be@/mic@ nodes */ 171 for_each_node_by_type(np, "mic-tm") { 172 cbe_ptcal_enable_on_node(of_node_to_nid(np), order); 173 found_mic = 1; 174 } 175 176 if (found_mic) 177 return 0; 178 179 /* support for older device tree - use cpu nodes */ 180 for_each_node_by_type(np, "cpu") { 181 const u32 *nid = of_get_property(np, "node-id", NULL); 182 if (!nid) { 183 printk(KERN_ERR "%s: node %s is missing node-id?\n", 184 __func__, np->full_name); 185 continue; 186 } 187 cbe_ptcal_enable_on_node(*nid, order); 188 found_mic = 1; 189 } 190 191 return found_mic ? 0 : -ENODEV; 192 } 193 194 static int cbe_ptcal_disable(void) 195 { 196 struct ptcal_area *area, *tmp; 197 int ret = 0; 198 199 pr_debug("%s: disabling PTCAL\n", __func__); 200 201 list_for_each_entry_safe(area, tmp, &ptcal_list, list) { 202 /* disable ptcal on this node */ 203 if (rtas_call(ptcal_stop_tok, 1, 1, NULL, area->nid)) { 204 printk(KERN_ERR "%s: error disabling PTCAL " 205 "on node %d!\n", __func__, 206 area->nid); 207 ret = -EIO; 208 continue; 209 } 210 211 /* ensure we can access the PTCAL area */ 212 memset(page_address(area->pages), 0, 213 1 << (area->order + PAGE_SHIFT)); 214 215 /* clean up */ 216 list_del(&area->list); 217 __free_pages(area->pages, area->order); 218 kfree(area); 219 } 220 221 return ret; 222 } 223 224 static int cbe_ptcal_notify_reboot(struct notifier_block *nb, 225 unsigned long code, void *data) 226 { 227 return cbe_ptcal_disable(); 228 } 229 230 static void cbe_ptcal_crash_shutdown(void) 231 { 232 cbe_ptcal_disable(); 233 } 234 235 static struct notifier_block cbe_ptcal_reboot_notifier = { 236 .notifier_call = cbe_ptcal_notify_reboot 237 }; 238 239 #ifdef CONFIG_PPC_IBM_CELL_RESETBUTTON 240 static int sysreset_hack; 241 242 static int __init cbe_sysreset_init(void) 243 { 244 struct cbe_pmd_regs __iomem *regs; 245 246 sysreset_hack = machine_is_compatible("IBM,CBPLUS-1.0"); 247 if (!sysreset_hack) 248 return 0; 249 250 regs = cbe_get_cpu_pmd_regs(0); 251 if (!regs) 252 return 0; 253 254 /* Enable JTAG system-reset hack */ 255 out_be32(®s->fir_mode_reg, 256 in_be32(®s->fir_mode_reg) | 257 CBE_PMD_FIR_MODE_M8); 258 259 return 0; 260 } 261 device_initcall(cbe_sysreset_init); 262 263 int cbe_sysreset_hack(void) 264 { 265 struct cbe_pmd_regs __iomem *regs; 266 267 /* 268 * The BMC can inject user triggered system reset exceptions, 269 * but cannot set the system reset reason in srr1, 270 * so check an extra register here. 271 */ 272 if (sysreset_hack && (smp_processor_id() == 0)) { 273 regs = cbe_get_cpu_pmd_regs(0); 274 if (!regs) 275 return 0; 276 if (in_be64(®s->ras_esc_0) & 0x0000ffff) { 277 out_be64(®s->ras_esc_0, 0); 278 return 0; 279 } 280 } 281 return 1; 282 } 283 #endif /* CONFIG_PPC_IBM_CELL_RESETBUTTON */ 284 285 int __init cbe_ptcal_init(void) 286 { 287 int ret; 288 ptcal_start_tok = rtas_token("ibm,cbe-start-ptcal"); 289 ptcal_stop_tok = rtas_token("ibm,cbe-stop-ptcal"); 290 291 if (ptcal_start_tok == RTAS_UNKNOWN_SERVICE 292 || ptcal_stop_tok == RTAS_UNKNOWN_SERVICE) 293 return -ENODEV; 294 295 ret = register_reboot_notifier(&cbe_ptcal_reboot_notifier); 296 if (ret) 297 goto out1; 298 299 ret = crash_shutdown_register(&cbe_ptcal_crash_shutdown); 300 if (ret) 301 goto out2; 302 303 return cbe_ptcal_enable(); 304 305 out2: 306 unregister_reboot_notifier(&cbe_ptcal_reboot_notifier); 307 out1: 308 printk(KERN_ERR "Can't disable PTCAL, so not enabling\n"); 309 return ret; 310 } 311 312 arch_initcall(cbe_ptcal_init); 313 314 void __init cbe_ras_init(void) 315 { 316 unsigned long hid0; 317 318 /* 319 * Enable System Error & thermal interrupts and wakeup conditions 320 */ 321 322 hid0 = mfspr(SPRN_HID0); 323 hid0 |= HID0_CBE_THERM_INT_EN | HID0_CBE_THERM_WAKEUP | 324 HID0_CBE_SYSERR_INT_EN | HID0_CBE_SYSERR_WAKEUP; 325 mtspr(SPRN_HID0, hid0); 326 mb(); 327 328 /* 329 * Install machine check handler. Leave setting of precise mode to 330 * what the firmware did for now 331 */ 332 ppc_md.machine_check_exception = cbe_machine_check_handler; 333 mb(); 334 335 /* 336 * For now, we assume that IOC_FIR is already set to forward some 337 * error conditions to the System Error handler. If that is not true 338 * then it will have to be fixed up here. 339 */ 340 } 341