114a43e69SBenjamin Herrenschmidt /* 214a43e69SBenjamin Herrenschmidt * PowerNV OPAL high level interfaces 314a43e69SBenjamin Herrenschmidt * 414a43e69SBenjamin Herrenschmidt * Copyright 2011 IBM Corp. 514a43e69SBenjamin Herrenschmidt * 614a43e69SBenjamin Herrenschmidt * This program is free software; you can redistribute it and/or 714a43e69SBenjamin Herrenschmidt * modify it under the terms of the GNU General Public License 814a43e69SBenjamin Herrenschmidt * as published by the Free Software Foundation; either version 914a43e69SBenjamin Herrenschmidt * 2 of the License, or (at your option) any later version. 1014a43e69SBenjamin Herrenschmidt */ 1114a43e69SBenjamin Herrenschmidt 1214a43e69SBenjamin Herrenschmidt #undef DEBUG 1314a43e69SBenjamin Herrenschmidt 1414a43e69SBenjamin Herrenschmidt #include <linux/types.h> 1514a43e69SBenjamin Herrenschmidt #include <linux/of.h> 1614a43e69SBenjamin Herrenschmidt #include <linux/of_platform.h> 17a125e092SBenjamin Herrenschmidt #include <linux/interrupt.h> 181bc98de2SGavin Shan #include <linux/notifier.h> 1973ed148aSBenjamin Herrenschmidt #include <linux/slab.h> 2014a43e69SBenjamin Herrenschmidt #include <asm/opal.h> 2114a43e69SBenjamin Herrenschmidt #include <asm/firmware.h> 2214a43e69SBenjamin Herrenschmidt 2314a43e69SBenjamin Herrenschmidt #include "powernv.h" 2414a43e69SBenjamin Herrenschmidt 2514a43e69SBenjamin Herrenschmidt struct opal { 2614a43e69SBenjamin Herrenschmidt u64 base; 2714a43e69SBenjamin Herrenschmidt u64 entry; 2814a43e69SBenjamin Herrenschmidt } opal; 2914a43e69SBenjamin Herrenschmidt 3014a43e69SBenjamin Herrenschmidt static struct device_node *opal_node; 3114a43e69SBenjamin Herrenschmidt static DEFINE_SPINLOCK(opal_write_lock); 32ed79ba9eSBenjamin Herrenschmidt extern u64 opal_mc_secondary_handler[]; 3373ed148aSBenjamin Herrenschmidt static unsigned int *opal_irqs; 3473ed148aSBenjamin Herrenschmidt static unsigned int opal_irq_count; 351bc98de2SGavin Shan static ATOMIC_NOTIFIER_HEAD(opal_notifier_head); 361bc98de2SGavin Shan static DEFINE_SPINLOCK(opal_notifier_lock); 371bc98de2SGavin Shan static uint64_t last_notified_mask = 0x0ul; 381bc98de2SGavin Shan static atomic_t opal_notifier_hold = ATOMIC_INIT(0); 3914a43e69SBenjamin Herrenschmidt 4014a43e69SBenjamin Herrenschmidt int __init early_init_dt_scan_opal(unsigned long node, 4114a43e69SBenjamin Herrenschmidt const char *uname, int depth, void *data) 4214a43e69SBenjamin Herrenschmidt { 4314a43e69SBenjamin Herrenschmidt const void *basep, *entryp; 4414a43e69SBenjamin Herrenschmidt unsigned long basesz, entrysz; 4514a43e69SBenjamin Herrenschmidt 4614a43e69SBenjamin Herrenschmidt if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 4714a43e69SBenjamin Herrenschmidt return 0; 4814a43e69SBenjamin Herrenschmidt 4914a43e69SBenjamin Herrenschmidt basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 5014a43e69SBenjamin Herrenschmidt entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 5114a43e69SBenjamin Herrenschmidt 5214a43e69SBenjamin Herrenschmidt if (!basep || !entryp) 5314a43e69SBenjamin Herrenschmidt return 1; 5414a43e69SBenjamin Herrenschmidt 5514a43e69SBenjamin Herrenschmidt opal.base = of_read_number(basep, basesz/4); 5614a43e69SBenjamin Herrenschmidt opal.entry = of_read_number(entryp, entrysz/4); 5714a43e69SBenjamin Herrenschmidt 5814a43e69SBenjamin Herrenschmidt pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", 5914a43e69SBenjamin Herrenschmidt opal.base, basep, basesz); 6014a43e69SBenjamin Herrenschmidt pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", 6114a43e69SBenjamin Herrenschmidt opal.entry, entryp, entrysz); 6214a43e69SBenjamin Herrenschmidt 6314a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPAL; 6475b93da4SBenjamin Herrenschmidt if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { 6575b93da4SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv2; 6675b93da4SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv3; 6775b93da4SBenjamin Herrenschmidt printk("OPAL V3 detected !\n"); 6875b93da4SBenjamin Herrenschmidt } else if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { 6914a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv2; 7014a43e69SBenjamin Herrenschmidt printk("OPAL V2 detected !\n"); 7114a43e69SBenjamin Herrenschmidt } else { 7214a43e69SBenjamin Herrenschmidt printk("OPAL V1 detected !\n"); 7314a43e69SBenjamin Herrenschmidt } 7414a43e69SBenjamin Herrenschmidt 75c4463b37SJeremy Kerr return 1; 76c4463b37SJeremy Kerr } 77c4463b37SJeremy Kerr 78c4463b37SJeremy Kerr static int __init opal_register_exception_handlers(void) 79c4463b37SJeremy Kerr { 8029186097SBenjamin Herrenschmidt #ifdef __BIG_ENDIAN__ 81c4463b37SJeremy Kerr u64 glue; 82c4463b37SJeremy Kerr 83c4463b37SJeremy Kerr if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) 84c4463b37SJeremy Kerr return -ENODEV; 85c4463b37SJeremy Kerr 86ed79ba9eSBenjamin Herrenschmidt /* Hookup some exception handlers. We use the fwnmi area at 0x7000 87ed79ba9eSBenjamin Herrenschmidt * to provide the glue space to OPAL 88ed79ba9eSBenjamin Herrenschmidt */ 89ed79ba9eSBenjamin Herrenschmidt glue = 0x7000; 90ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER, 91ed79ba9eSBenjamin Herrenschmidt __pa(opal_mc_secondary_handler[0]), 92ed79ba9eSBenjamin Herrenschmidt glue); 93ed79ba9eSBenjamin Herrenschmidt glue += 128; 94ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, 95ed79ba9eSBenjamin Herrenschmidt 0, glue); 96ed79ba9eSBenjamin Herrenschmidt glue += 128; 97ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); 9829186097SBenjamin Herrenschmidt #endif 99ed79ba9eSBenjamin Herrenschmidt 100c4463b37SJeremy Kerr return 0; 10114a43e69SBenjamin Herrenschmidt } 10214a43e69SBenjamin Herrenschmidt 103c4463b37SJeremy Kerr early_initcall(opal_register_exception_handlers); 104c4463b37SJeremy Kerr 1051bc98de2SGavin Shan int opal_notifier_register(struct notifier_block *nb) 1061bc98de2SGavin Shan { 1071bc98de2SGavin Shan if (!nb) { 1081bc98de2SGavin Shan pr_warning("%s: Invalid argument (%p)\n", 1091bc98de2SGavin Shan __func__, nb); 1101bc98de2SGavin Shan return -EINVAL; 1111bc98de2SGavin Shan } 1121bc98de2SGavin Shan 1131bc98de2SGavin Shan atomic_notifier_chain_register(&opal_notifier_head, nb); 1141bc98de2SGavin Shan return 0; 1151bc98de2SGavin Shan } 1161bc98de2SGavin Shan 1171bc98de2SGavin Shan static void opal_do_notifier(uint64_t events) 1181bc98de2SGavin Shan { 1191bc98de2SGavin Shan unsigned long flags; 1201bc98de2SGavin Shan uint64_t changed_mask; 1211bc98de2SGavin Shan 1221bc98de2SGavin Shan if (atomic_read(&opal_notifier_hold)) 1231bc98de2SGavin Shan return; 1241bc98de2SGavin Shan 1251bc98de2SGavin Shan spin_lock_irqsave(&opal_notifier_lock, flags); 1261bc98de2SGavin Shan changed_mask = last_notified_mask ^ events; 1271bc98de2SGavin Shan last_notified_mask = events; 1281bc98de2SGavin Shan spin_unlock_irqrestore(&opal_notifier_lock, flags); 1291bc98de2SGavin Shan 1301bc98de2SGavin Shan /* 1311bc98de2SGavin Shan * We feed with the event bits and changed bits for 1321bc98de2SGavin Shan * enough information to the callback. 1331bc98de2SGavin Shan */ 1341bc98de2SGavin Shan atomic_notifier_call_chain(&opal_notifier_head, 1351bc98de2SGavin Shan events, (void *)changed_mask); 1361bc98de2SGavin Shan } 1371bc98de2SGavin Shan 1381bc98de2SGavin Shan void opal_notifier_update_evt(uint64_t evt_mask, 1391bc98de2SGavin Shan uint64_t evt_val) 1401bc98de2SGavin Shan { 1411bc98de2SGavin Shan unsigned long flags; 1421bc98de2SGavin Shan 1431bc98de2SGavin Shan spin_lock_irqsave(&opal_notifier_lock, flags); 1441bc98de2SGavin Shan last_notified_mask &= ~evt_mask; 1451bc98de2SGavin Shan last_notified_mask |= evt_val; 1461bc98de2SGavin Shan spin_unlock_irqrestore(&opal_notifier_lock, flags); 1471bc98de2SGavin Shan } 1481bc98de2SGavin Shan 1491bc98de2SGavin Shan void opal_notifier_enable(void) 1501bc98de2SGavin Shan { 1511bc98de2SGavin Shan int64_t rc; 1521bc98de2SGavin Shan uint64_t evt = 0; 1531bc98de2SGavin Shan 1541bc98de2SGavin Shan atomic_set(&opal_notifier_hold, 0); 1551bc98de2SGavin Shan 1561bc98de2SGavin Shan /* Process pending events */ 1571bc98de2SGavin Shan rc = opal_poll_events(&evt); 1581bc98de2SGavin Shan if (rc == OPAL_SUCCESS && evt) 1591bc98de2SGavin Shan opal_do_notifier(evt); 1601bc98de2SGavin Shan } 1611bc98de2SGavin Shan 1621bc98de2SGavin Shan void opal_notifier_disable(void) 1631bc98de2SGavin Shan { 1641bc98de2SGavin Shan atomic_set(&opal_notifier_hold, 1); 1651bc98de2SGavin Shan } 1661bc98de2SGavin Shan 16714a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count) 16814a43e69SBenjamin Herrenschmidt { 1694f89363bSBenjamin Herrenschmidt s64 rc; 1704f89363bSBenjamin Herrenschmidt __be64 evt, len; 17114a43e69SBenjamin Herrenschmidt 17214a43e69SBenjamin Herrenschmidt if (!opal.entry) 173daea1175SBenjamin Herrenschmidt return -ENODEV; 17414a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 1754f89363bSBenjamin Herrenschmidt if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0) 17614a43e69SBenjamin Herrenschmidt return 0; 1774f89363bSBenjamin Herrenschmidt len = cpu_to_be64(count); 17814a43e69SBenjamin Herrenschmidt rc = opal_console_read(vtermno, &len, buf); 17914a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) 1804f89363bSBenjamin Herrenschmidt return be64_to_cpu(len); 18114a43e69SBenjamin Herrenschmidt return 0; 18214a43e69SBenjamin Herrenschmidt } 18314a43e69SBenjamin Herrenschmidt 18414a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len) 18514a43e69SBenjamin Herrenschmidt { 18614a43e69SBenjamin Herrenschmidt int written = 0; 1874f89363bSBenjamin Herrenschmidt __be64 olen; 188daea1175SBenjamin Herrenschmidt s64 len, rc; 18914a43e69SBenjamin Herrenschmidt unsigned long flags; 1904f89363bSBenjamin Herrenschmidt __be64 evt; 19114a43e69SBenjamin Herrenschmidt 19214a43e69SBenjamin Herrenschmidt if (!opal.entry) 193daea1175SBenjamin Herrenschmidt return -ENODEV; 19414a43e69SBenjamin Herrenschmidt 19514a43e69SBenjamin Herrenschmidt /* We want put_chars to be atomic to avoid mangling of hvsi 19614a43e69SBenjamin Herrenschmidt * packets. To do that, we first test for room and return 197daea1175SBenjamin Herrenschmidt * -EAGAIN if there isn't enough. 198daea1175SBenjamin Herrenschmidt * 199daea1175SBenjamin Herrenschmidt * Unfortunately, opal_console_write_buffer_space() doesn't 200daea1175SBenjamin Herrenschmidt * appear to work on opal v1, so we just assume there is 201daea1175SBenjamin Herrenschmidt * enough room and be done with it 20214a43e69SBenjamin Herrenschmidt */ 20314a43e69SBenjamin Herrenschmidt spin_lock_irqsave(&opal_write_lock, flags); 204daea1175SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) { 2054f89363bSBenjamin Herrenschmidt rc = opal_console_write_buffer_space(vtermno, &olen); 2064f89363bSBenjamin Herrenschmidt len = be64_to_cpu(olen); 20714a43e69SBenjamin Herrenschmidt if (rc || len < total_len) { 20814a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 20914a43e69SBenjamin Herrenschmidt /* Closed -> drop characters */ 21014a43e69SBenjamin Herrenschmidt if (rc) 21114a43e69SBenjamin Herrenschmidt return total_len; 2124f89363bSBenjamin Herrenschmidt opal_poll_events(NULL); 21314a43e69SBenjamin Herrenschmidt return -EAGAIN; 21414a43e69SBenjamin Herrenschmidt } 215daea1175SBenjamin Herrenschmidt } 21614a43e69SBenjamin Herrenschmidt 21714a43e69SBenjamin Herrenschmidt /* We still try to handle partial completions, though they 21814a43e69SBenjamin Herrenschmidt * should no longer happen. 21914a43e69SBenjamin Herrenschmidt */ 220daea1175SBenjamin Herrenschmidt rc = OPAL_BUSY; 22114a43e69SBenjamin Herrenschmidt while(total_len > 0 && (rc == OPAL_BUSY || 22214a43e69SBenjamin Herrenschmidt rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { 2234f89363bSBenjamin Herrenschmidt olen = cpu_to_be64(total_len); 2244f89363bSBenjamin Herrenschmidt rc = opal_console_write(vtermno, &olen, data); 2254f89363bSBenjamin Herrenschmidt len = be64_to_cpu(olen); 2261de1455fSBenjamin Herrenschmidt 2271de1455fSBenjamin Herrenschmidt /* Closed or other error drop */ 2281de1455fSBenjamin Herrenschmidt if (rc != OPAL_SUCCESS && rc != OPAL_BUSY && 2291de1455fSBenjamin Herrenschmidt rc != OPAL_BUSY_EVENT) { 2301de1455fSBenjamin Herrenschmidt written = total_len; 2311de1455fSBenjamin Herrenschmidt break; 2321de1455fSBenjamin Herrenschmidt } 23314a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) { 23414a43e69SBenjamin Herrenschmidt total_len -= len; 23514a43e69SBenjamin Herrenschmidt data += len; 23614a43e69SBenjamin Herrenschmidt written += len; 23714a43e69SBenjamin Herrenschmidt } 23814a43e69SBenjamin Herrenschmidt /* This is a bit nasty but we need that for the console to 23914a43e69SBenjamin Herrenschmidt * flush when there aren't any interrupts. We will clean 24014a43e69SBenjamin Herrenschmidt * things a bit later to limit that to synchronous path 24114a43e69SBenjamin Herrenschmidt * such as the kernel console and xmon/udbg 24214a43e69SBenjamin Herrenschmidt */ 24314a43e69SBenjamin Herrenschmidt do 24414a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 2454f89363bSBenjamin Herrenschmidt while(rc == OPAL_SUCCESS && 2464f89363bSBenjamin Herrenschmidt (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT)); 24714a43e69SBenjamin Herrenschmidt } 24814a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 24914a43e69SBenjamin Herrenschmidt return written; 25014a43e69SBenjamin Herrenschmidt } 25114a43e69SBenjamin Herrenschmidt 252ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs) 253ed79ba9eSBenjamin Herrenschmidt { 254ed79ba9eSBenjamin Herrenschmidt struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt; 255ed79ba9eSBenjamin Herrenschmidt struct opal_machine_check_event evt; 256ed79ba9eSBenjamin Herrenschmidt const char *level, *sevstr, *subtype; 257ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_ue_types[] = { 258ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 259ed79ba9eSBenjamin Herrenschmidt "Instruction fetch", 260ed79ba9eSBenjamin Herrenschmidt "Page table walk ifetch", 261ed79ba9eSBenjamin Herrenschmidt "Load/Store", 262ed79ba9eSBenjamin Herrenschmidt "Page table walk Load/Store", 263ed79ba9eSBenjamin Herrenschmidt }; 264ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_slb_types[] = { 265ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 266ed79ba9eSBenjamin Herrenschmidt "Parity", 267ed79ba9eSBenjamin Herrenschmidt "Multihit", 268ed79ba9eSBenjamin Herrenschmidt }; 269ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_erat_types[] = { 270ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 271ed79ba9eSBenjamin Herrenschmidt "Parity", 272ed79ba9eSBenjamin Herrenschmidt "Multihit", 273ed79ba9eSBenjamin Herrenschmidt }; 274ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_tlb_types[] = { 275ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 276ed79ba9eSBenjamin Herrenschmidt "Parity", 277ed79ba9eSBenjamin Herrenschmidt "Multihit", 278ed79ba9eSBenjamin Herrenschmidt }; 279ed79ba9eSBenjamin Herrenschmidt 280ed79ba9eSBenjamin Herrenschmidt /* Copy the event structure and release the original */ 281ed79ba9eSBenjamin Herrenschmidt evt = *opal_evt; 282ed79ba9eSBenjamin Herrenschmidt opal_evt->in_use = 0; 283ed79ba9eSBenjamin Herrenschmidt 284ed79ba9eSBenjamin Herrenschmidt /* Print things out */ 285ed79ba9eSBenjamin Herrenschmidt if (evt.version != OpalMCE_V1) { 286ed79ba9eSBenjamin Herrenschmidt pr_err("Machine Check Exception, Unknown event version %d !\n", 287ed79ba9eSBenjamin Herrenschmidt evt.version); 288ed79ba9eSBenjamin Herrenschmidt return 0; 289ed79ba9eSBenjamin Herrenschmidt } 290ed79ba9eSBenjamin Herrenschmidt switch(evt.severity) { 291ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_NO_ERROR: 292ed79ba9eSBenjamin Herrenschmidt level = KERN_INFO; 293ed79ba9eSBenjamin Herrenschmidt sevstr = "Harmless"; 294ed79ba9eSBenjamin Herrenschmidt break; 295ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_WARNING: 296ed79ba9eSBenjamin Herrenschmidt level = KERN_WARNING; 297ed79ba9eSBenjamin Herrenschmidt sevstr = ""; 298ed79ba9eSBenjamin Herrenschmidt break; 299ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_ERROR_SYNC: 300ed79ba9eSBenjamin Herrenschmidt level = KERN_ERR; 301ed79ba9eSBenjamin Herrenschmidt sevstr = "Severe"; 302ed79ba9eSBenjamin Herrenschmidt break; 303ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_FATAL: 304ed79ba9eSBenjamin Herrenschmidt default: 305ed79ba9eSBenjamin Herrenschmidt level = KERN_ERR; 306ed79ba9eSBenjamin Herrenschmidt sevstr = "Fatal"; 307ed79ba9eSBenjamin Herrenschmidt break; 308ed79ba9eSBenjamin Herrenschmidt } 309ed79ba9eSBenjamin Herrenschmidt 310ed79ba9eSBenjamin Herrenschmidt printk("%s%s Machine check interrupt [%s]\n", level, sevstr, 311ed79ba9eSBenjamin Herrenschmidt evt.disposition == OpalMCE_DISPOSITION_RECOVERED ? 312ed79ba9eSBenjamin Herrenschmidt "Recovered" : "[Not recovered"); 313ed79ba9eSBenjamin Herrenschmidt printk("%s Initiator: %s\n", level, 314ed79ba9eSBenjamin Herrenschmidt evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown"); 315ed79ba9eSBenjamin Herrenschmidt switch(evt.error_type) { 316ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_UE: 317ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.ue_error.ue_error_type < 318ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_ue_types) ? 319ed79ba9eSBenjamin Herrenschmidt opal_mc_ue_types[evt.u.ue_error.ue_error_type] 320ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 321ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: UE [%s]\n", level, subtype); 322ed79ba9eSBenjamin Herrenschmidt if (evt.u.ue_error.effective_address_provided) 323ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 324ed79ba9eSBenjamin Herrenschmidt level, evt.u.ue_error.effective_address); 325ed79ba9eSBenjamin Herrenschmidt if (evt.u.ue_error.physical_address_provided) 326ed79ba9eSBenjamin Herrenschmidt printk("%s Physial address: %016llx\n", 327ed79ba9eSBenjamin Herrenschmidt level, evt.u.ue_error.physical_address); 328ed79ba9eSBenjamin Herrenschmidt break; 329ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_SLB: 330ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.slb_error.slb_error_type < 331ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_slb_types) ? 332ed79ba9eSBenjamin Herrenschmidt opal_mc_slb_types[evt.u.slb_error.slb_error_type] 333ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 334ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: SLB [%s]\n", level, subtype); 335ed79ba9eSBenjamin Herrenschmidt if (evt.u.slb_error.effective_address_provided) 336ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 337ed79ba9eSBenjamin Herrenschmidt level, evt.u.slb_error.effective_address); 338ed79ba9eSBenjamin Herrenschmidt break; 339ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_ERAT: 340ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.erat_error.erat_error_type < 341ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_erat_types) ? 342ed79ba9eSBenjamin Herrenschmidt opal_mc_erat_types[evt.u.erat_error.erat_error_type] 343ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 344ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: ERAT [%s]\n", level, subtype); 345ed79ba9eSBenjamin Herrenschmidt if (evt.u.erat_error.effective_address_provided) 346ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 347ed79ba9eSBenjamin Herrenschmidt level, evt.u.erat_error.effective_address); 348ed79ba9eSBenjamin Herrenschmidt break; 349ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_TLB: 350ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.tlb_error.tlb_error_type < 351ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_tlb_types) ? 352ed79ba9eSBenjamin Herrenschmidt opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type] 353ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 354ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: TLB [%s]\n", level, subtype); 355ed79ba9eSBenjamin Herrenschmidt if (evt.u.tlb_error.effective_address_provided) 356ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 357ed79ba9eSBenjamin Herrenschmidt level, evt.u.tlb_error.effective_address); 358ed79ba9eSBenjamin Herrenschmidt break; 359ed79ba9eSBenjamin Herrenschmidt default: 360ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_UNKNOWN: 361ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: Unknown\n", level); 362ed79ba9eSBenjamin Herrenschmidt break; 363ed79ba9eSBenjamin Herrenschmidt } 364ed79ba9eSBenjamin Herrenschmidt return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1; 365ed79ba9eSBenjamin Herrenschmidt } 366ed79ba9eSBenjamin Herrenschmidt 367a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data) 368a125e092SBenjamin Herrenschmidt { 369a125e092SBenjamin Herrenschmidt uint64_t events; 370a125e092SBenjamin Herrenschmidt 371a125e092SBenjamin Herrenschmidt opal_handle_interrupt(virq_to_hw(irq), &events); 372a125e092SBenjamin Herrenschmidt 3731bc98de2SGavin Shan opal_do_notifier(events); 374a125e092SBenjamin Herrenschmidt 375a125e092SBenjamin Herrenschmidt return IRQ_HANDLED; 376a125e092SBenjamin Herrenschmidt } 377a125e092SBenjamin Herrenschmidt 37814a43e69SBenjamin Herrenschmidt static int __init opal_init(void) 37914a43e69SBenjamin Herrenschmidt { 38014a43e69SBenjamin Herrenschmidt struct device_node *np, *consoles; 3811cc79bc8SAlistair Popple const __be32 *irqs; 382a125e092SBenjamin Herrenschmidt int rc, i, irqlen; 38314a43e69SBenjamin Herrenschmidt 38414a43e69SBenjamin Herrenschmidt opal_node = of_find_node_by_path("/ibm,opal"); 38514a43e69SBenjamin Herrenschmidt if (!opal_node) { 38614a43e69SBenjamin Herrenschmidt pr_warn("opal: Node not found\n"); 38714a43e69SBenjamin Herrenschmidt return -ENODEV; 38814a43e69SBenjamin Herrenschmidt } 3892db29d28SBenjamin Herrenschmidt 3902db29d28SBenjamin Herrenschmidt /* Register OPAL consoles if any ports */ 39114a43e69SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) 39214a43e69SBenjamin Herrenschmidt consoles = of_find_node_by_path("/ibm,opal/consoles"); 39314a43e69SBenjamin Herrenschmidt else 39414a43e69SBenjamin Herrenschmidt consoles = of_node_get(opal_node); 3952db29d28SBenjamin Herrenschmidt if (consoles) { 39614a43e69SBenjamin Herrenschmidt for_each_child_of_node(consoles, np) { 39714a43e69SBenjamin Herrenschmidt if (strcmp(np->name, "serial")) 39814a43e69SBenjamin Herrenschmidt continue; 39914a43e69SBenjamin Herrenschmidt of_platform_device_create(np, NULL, NULL); 40014a43e69SBenjamin Herrenschmidt } 40114a43e69SBenjamin Herrenschmidt of_node_put(consoles); 4022db29d28SBenjamin Herrenschmidt } 403a125e092SBenjamin Herrenschmidt 404a125e092SBenjamin Herrenschmidt /* Find all OPAL interrupts and request them */ 405a125e092SBenjamin Herrenschmidt irqs = of_get_property(opal_node, "opal-interrupts", &irqlen); 406a125e092SBenjamin Herrenschmidt pr_debug("opal: Found %d interrupts reserved for OPAL\n", 407a125e092SBenjamin Herrenschmidt irqs ? (irqlen / 4) : 0); 40873ed148aSBenjamin Herrenschmidt opal_irq_count = irqlen / 4; 40973ed148aSBenjamin Herrenschmidt opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL); 410a125e092SBenjamin Herrenschmidt for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) { 411a125e092SBenjamin Herrenschmidt unsigned int hwirq = be32_to_cpup(irqs); 412a125e092SBenjamin Herrenschmidt unsigned int irq = irq_create_mapping(NULL, hwirq); 413a125e092SBenjamin Herrenschmidt if (irq == NO_IRQ) { 414a125e092SBenjamin Herrenschmidt pr_warning("opal: Failed to map irq 0x%x\n", hwirq); 415a125e092SBenjamin Herrenschmidt continue; 416a125e092SBenjamin Herrenschmidt } 417a125e092SBenjamin Herrenschmidt rc = request_irq(irq, opal_interrupt, 0, "opal", NULL); 418a125e092SBenjamin Herrenschmidt if (rc) 419a125e092SBenjamin Herrenschmidt pr_warning("opal: Error %d requesting irq %d" 420a125e092SBenjamin Herrenschmidt " (0x%x)\n", rc, irq, hwirq); 42173ed148aSBenjamin Herrenschmidt opal_irqs[i] = irq; 422a125e092SBenjamin Herrenschmidt } 42314a43e69SBenjamin Herrenschmidt return 0; 42414a43e69SBenjamin Herrenschmidt } 42514a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init); 42673ed148aSBenjamin Herrenschmidt 42773ed148aSBenjamin Herrenschmidt void opal_shutdown(void) 42873ed148aSBenjamin Herrenschmidt { 42973ed148aSBenjamin Herrenschmidt unsigned int i; 43073ed148aSBenjamin Herrenschmidt 43173ed148aSBenjamin Herrenschmidt for (i = 0; i < opal_irq_count; i++) { 43273ed148aSBenjamin Herrenschmidt if (opal_irqs[i]) 433b0d436c7SAnton Blanchard free_irq(opal_irqs[i], NULL); 43473ed148aSBenjamin Herrenschmidt opal_irqs[i] = 0; 43573ed148aSBenjamin Herrenschmidt } 43673ed148aSBenjamin Herrenschmidt } 437