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