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