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> 1626a2056eSRob Herring #include <linux/of_fdt.h> 1714a43e69SBenjamin Herrenschmidt #include <linux/of_platform.h> 18a125e092SBenjamin Herrenschmidt #include <linux/interrupt.h> 191bc98de2SGavin Shan #include <linux/notifier.h> 2073ed148aSBenjamin Herrenschmidt #include <linux/slab.h> 21b63a0ffeSMahesh Salgaonkar #include <linux/sched.h> 226f68b5e2SVasant Hegde #include <linux/kobject.h> 23f7d98d18SVasant Hegde #include <linux/delay.h> 2414a43e69SBenjamin Herrenschmidt #include <asm/opal.h> 2514a43e69SBenjamin Herrenschmidt #include <asm/firmware.h> 2636df96f8SMahesh Salgaonkar #include <asm/mce.h> 2714a43e69SBenjamin Herrenschmidt 2814a43e69SBenjamin Herrenschmidt #include "powernv.h" 2914a43e69SBenjamin Herrenschmidt 306f68b5e2SVasant Hegde /* /sys/firmware/opal */ 316f68b5e2SVasant Hegde struct kobject *opal_kobj; 326f68b5e2SVasant Hegde 3314a43e69SBenjamin Herrenschmidt struct opal { 3414a43e69SBenjamin Herrenschmidt u64 base; 3514a43e69SBenjamin Herrenschmidt u64 entry; 3614a43e69SBenjamin Herrenschmidt } opal; 3714a43e69SBenjamin Herrenschmidt 3814a43e69SBenjamin Herrenschmidt static struct device_node *opal_node; 3914a43e69SBenjamin Herrenschmidt static DEFINE_SPINLOCK(opal_write_lock); 40ed79ba9eSBenjamin Herrenschmidt extern u64 opal_mc_secondary_handler[]; 4173ed148aSBenjamin Herrenschmidt static unsigned int *opal_irqs; 4273ed148aSBenjamin Herrenschmidt static unsigned int opal_irq_count; 431bc98de2SGavin Shan static ATOMIC_NOTIFIER_HEAD(opal_notifier_head); 4424366360SMahesh Salgaonkar static struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX]; 451bc98de2SGavin Shan static DEFINE_SPINLOCK(opal_notifier_lock); 461bc98de2SGavin Shan static uint64_t last_notified_mask = 0x0ul; 471bc98de2SGavin Shan static atomic_t opal_notifier_hold = ATOMIC_INIT(0); 4814a43e69SBenjamin Herrenschmidt 4914a43e69SBenjamin Herrenschmidt int __init early_init_dt_scan_opal(unsigned long node, 5014a43e69SBenjamin Herrenschmidt const char *uname, int depth, void *data) 5114a43e69SBenjamin Herrenschmidt { 5214a43e69SBenjamin Herrenschmidt const void *basep, *entryp; 5314a43e69SBenjamin Herrenschmidt unsigned long basesz, entrysz; 5414a43e69SBenjamin Herrenschmidt 5514a43e69SBenjamin Herrenschmidt if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 5614a43e69SBenjamin Herrenschmidt return 0; 5714a43e69SBenjamin Herrenschmidt 5814a43e69SBenjamin Herrenschmidt basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 5914a43e69SBenjamin Herrenschmidt entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 6014a43e69SBenjamin Herrenschmidt 6114a43e69SBenjamin Herrenschmidt if (!basep || !entryp) 6214a43e69SBenjamin Herrenschmidt return 1; 6314a43e69SBenjamin Herrenschmidt 6414a43e69SBenjamin Herrenschmidt opal.base = of_read_number(basep, basesz/4); 6514a43e69SBenjamin Herrenschmidt opal.entry = of_read_number(entryp, entrysz/4); 6614a43e69SBenjamin Herrenschmidt 6714a43e69SBenjamin Herrenschmidt pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", 6814a43e69SBenjamin Herrenschmidt opal.base, basep, basesz); 6914a43e69SBenjamin Herrenschmidt pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", 7014a43e69SBenjamin Herrenschmidt opal.entry, entryp, entrysz); 7114a43e69SBenjamin Herrenschmidt 7214a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPAL; 7375b93da4SBenjamin Herrenschmidt if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { 7475b93da4SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv2; 7575b93da4SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv3; 7675b93da4SBenjamin Herrenschmidt printk("OPAL V3 detected !\n"); 7775b93da4SBenjamin Herrenschmidt } else if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { 7814a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv2; 7914a43e69SBenjamin Herrenschmidt printk("OPAL V2 detected !\n"); 8014a43e69SBenjamin Herrenschmidt } else { 8114a43e69SBenjamin Herrenschmidt printk("OPAL V1 detected !\n"); 8214a43e69SBenjamin Herrenschmidt } 8314a43e69SBenjamin Herrenschmidt 84c4463b37SJeremy Kerr return 1; 85c4463b37SJeremy Kerr } 86c4463b37SJeremy Kerr 87c4463b37SJeremy Kerr static int __init opal_register_exception_handlers(void) 88c4463b37SJeremy Kerr { 8929186097SBenjamin Herrenschmidt #ifdef __BIG_ENDIAN__ 90c4463b37SJeremy Kerr u64 glue; 91c4463b37SJeremy Kerr 92c4463b37SJeremy Kerr if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) 93c4463b37SJeremy Kerr return -ENODEV; 94c4463b37SJeremy Kerr 9528446de2SMahesh Salgaonkar /* Hookup some exception handlers except machine check. We use the 9628446de2SMahesh Salgaonkar * fwnmi area at 0x7000 to provide the glue space to OPAL 97ed79ba9eSBenjamin Herrenschmidt */ 98ed79ba9eSBenjamin Herrenschmidt glue = 0x7000; 99ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, 100ed79ba9eSBenjamin Herrenschmidt 0, glue); 101ed79ba9eSBenjamin Herrenschmidt glue += 128; 102ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); 10329186097SBenjamin Herrenschmidt #endif 104ed79ba9eSBenjamin Herrenschmidt 105c4463b37SJeremy Kerr return 0; 10614a43e69SBenjamin Herrenschmidt } 10714a43e69SBenjamin Herrenschmidt 108c4463b37SJeremy Kerr early_initcall(opal_register_exception_handlers); 109c4463b37SJeremy Kerr 1101bc98de2SGavin Shan int opal_notifier_register(struct notifier_block *nb) 1111bc98de2SGavin Shan { 1121bc98de2SGavin Shan if (!nb) { 1131bc98de2SGavin Shan pr_warning("%s: Invalid argument (%p)\n", 1141bc98de2SGavin Shan __func__, nb); 1151bc98de2SGavin Shan return -EINVAL; 1161bc98de2SGavin Shan } 1171bc98de2SGavin Shan 1181bc98de2SGavin Shan atomic_notifier_chain_register(&opal_notifier_head, nb); 1191bc98de2SGavin Shan return 0; 1201bc98de2SGavin Shan } 1211bc98de2SGavin Shan 1221bc98de2SGavin Shan static void opal_do_notifier(uint64_t events) 1231bc98de2SGavin Shan { 1241bc98de2SGavin Shan unsigned long flags; 1251bc98de2SGavin Shan uint64_t changed_mask; 1261bc98de2SGavin Shan 1271bc98de2SGavin Shan if (atomic_read(&opal_notifier_hold)) 1281bc98de2SGavin Shan return; 1291bc98de2SGavin Shan 1301bc98de2SGavin Shan spin_lock_irqsave(&opal_notifier_lock, flags); 1311bc98de2SGavin Shan changed_mask = last_notified_mask ^ events; 1321bc98de2SGavin Shan last_notified_mask = events; 1331bc98de2SGavin Shan spin_unlock_irqrestore(&opal_notifier_lock, flags); 1341bc98de2SGavin Shan 1351bc98de2SGavin Shan /* 1361bc98de2SGavin Shan * We feed with the event bits and changed bits for 1371bc98de2SGavin Shan * enough information to the callback. 1381bc98de2SGavin Shan */ 1391bc98de2SGavin Shan atomic_notifier_call_chain(&opal_notifier_head, 1401bc98de2SGavin Shan events, (void *)changed_mask); 1411bc98de2SGavin Shan } 1421bc98de2SGavin Shan 1431bc98de2SGavin Shan void opal_notifier_update_evt(uint64_t evt_mask, 1441bc98de2SGavin Shan uint64_t evt_val) 1451bc98de2SGavin Shan { 1461bc98de2SGavin Shan unsigned long flags; 1471bc98de2SGavin Shan 1481bc98de2SGavin Shan spin_lock_irqsave(&opal_notifier_lock, flags); 1491bc98de2SGavin Shan last_notified_mask &= ~evt_mask; 1501bc98de2SGavin Shan last_notified_mask |= evt_val; 1511bc98de2SGavin Shan spin_unlock_irqrestore(&opal_notifier_lock, flags); 1521bc98de2SGavin Shan } 1531bc98de2SGavin Shan 1541bc98de2SGavin Shan void opal_notifier_enable(void) 1551bc98de2SGavin Shan { 1561bc98de2SGavin Shan int64_t rc; 1571bc98de2SGavin Shan uint64_t evt = 0; 1581bc98de2SGavin Shan 1591bc98de2SGavin Shan atomic_set(&opal_notifier_hold, 0); 1601bc98de2SGavin Shan 1611bc98de2SGavin Shan /* Process pending events */ 1621bc98de2SGavin Shan rc = opal_poll_events(&evt); 1631bc98de2SGavin Shan if (rc == OPAL_SUCCESS && evt) 1641bc98de2SGavin Shan opal_do_notifier(evt); 1651bc98de2SGavin Shan } 1661bc98de2SGavin Shan 1671bc98de2SGavin Shan void opal_notifier_disable(void) 1681bc98de2SGavin Shan { 1691bc98de2SGavin Shan atomic_set(&opal_notifier_hold, 1); 1701bc98de2SGavin Shan } 1711bc98de2SGavin Shan 17224366360SMahesh Salgaonkar /* 17324366360SMahesh Salgaonkar * Opal message notifier based on message type. Allow subscribers to get 17424366360SMahesh Salgaonkar * notified for specific messgae type. 17524366360SMahesh Salgaonkar */ 17624366360SMahesh Salgaonkar int opal_message_notifier_register(enum OpalMessageType msg_type, 17724366360SMahesh Salgaonkar struct notifier_block *nb) 17824366360SMahesh Salgaonkar { 17924366360SMahesh Salgaonkar if (!nb) { 18024366360SMahesh Salgaonkar pr_warning("%s: Invalid argument (%p)\n", 18124366360SMahesh Salgaonkar __func__, nb); 18224366360SMahesh Salgaonkar return -EINVAL; 18324366360SMahesh Salgaonkar } 18424366360SMahesh Salgaonkar if (msg_type > OPAL_MSG_TYPE_MAX) { 18524366360SMahesh Salgaonkar pr_warning("%s: Invalid message type argument (%d)\n", 18624366360SMahesh Salgaonkar __func__, msg_type); 18724366360SMahesh Salgaonkar return -EINVAL; 18824366360SMahesh Salgaonkar } 18924366360SMahesh Salgaonkar return atomic_notifier_chain_register( 19024366360SMahesh Salgaonkar &opal_msg_notifier_head[msg_type], nb); 19124366360SMahesh Salgaonkar } 19224366360SMahesh Salgaonkar 19324366360SMahesh Salgaonkar static void opal_message_do_notify(uint32_t msg_type, void *msg) 19424366360SMahesh Salgaonkar { 19524366360SMahesh Salgaonkar /* notify subscribers */ 19624366360SMahesh Salgaonkar atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type], 19724366360SMahesh Salgaonkar msg_type, msg); 19824366360SMahesh Salgaonkar } 19924366360SMahesh Salgaonkar 20024366360SMahesh Salgaonkar static void opal_handle_message(void) 20124366360SMahesh Salgaonkar { 20224366360SMahesh Salgaonkar s64 ret; 20324366360SMahesh Salgaonkar /* 20424366360SMahesh Salgaonkar * TODO: pre-allocate a message buffer depending on opal-msg-size 20524366360SMahesh Salgaonkar * value in /proc/device-tree. 20624366360SMahesh Salgaonkar */ 20724366360SMahesh Salgaonkar static struct opal_msg msg; 20824366360SMahesh Salgaonkar 20924366360SMahesh Salgaonkar ret = opal_get_msg(__pa(&msg), sizeof(msg)); 21024366360SMahesh Salgaonkar /* No opal message pending. */ 21124366360SMahesh Salgaonkar if (ret == OPAL_RESOURCE) 21224366360SMahesh Salgaonkar return; 21324366360SMahesh Salgaonkar 21424366360SMahesh Salgaonkar /* check for errors. */ 21524366360SMahesh Salgaonkar if (ret) { 21624366360SMahesh Salgaonkar pr_warning("%s: Failed to retrive opal message, err=%lld\n", 21724366360SMahesh Salgaonkar __func__, ret); 21824366360SMahesh Salgaonkar return; 21924366360SMahesh Salgaonkar } 22024366360SMahesh Salgaonkar 22124366360SMahesh Salgaonkar /* Sanity check */ 22224366360SMahesh Salgaonkar if (msg.msg_type > OPAL_MSG_TYPE_MAX) { 22324366360SMahesh Salgaonkar pr_warning("%s: Unknown message type: %u\n", 22424366360SMahesh Salgaonkar __func__, msg.msg_type); 22524366360SMahesh Salgaonkar return; 22624366360SMahesh Salgaonkar } 22724366360SMahesh Salgaonkar opal_message_do_notify(msg.msg_type, (void *)&msg); 22824366360SMahesh Salgaonkar } 22924366360SMahesh Salgaonkar 23024366360SMahesh Salgaonkar static int opal_message_notify(struct notifier_block *nb, 23124366360SMahesh Salgaonkar unsigned long events, void *change) 23224366360SMahesh Salgaonkar { 23324366360SMahesh Salgaonkar if (events & OPAL_EVENT_MSG_PENDING) 23424366360SMahesh Salgaonkar opal_handle_message(); 23524366360SMahesh Salgaonkar return 0; 23624366360SMahesh Salgaonkar } 23724366360SMahesh Salgaonkar 23824366360SMahesh Salgaonkar static struct notifier_block opal_message_nb = { 23924366360SMahesh Salgaonkar .notifier_call = opal_message_notify, 24024366360SMahesh Salgaonkar .next = NULL, 24124366360SMahesh Salgaonkar .priority = 0, 24224366360SMahesh Salgaonkar }; 24324366360SMahesh Salgaonkar 24424366360SMahesh Salgaonkar static int __init opal_message_init(void) 24524366360SMahesh Salgaonkar { 24624366360SMahesh Salgaonkar int ret, i; 24724366360SMahesh Salgaonkar 24824366360SMahesh Salgaonkar for (i = 0; i < OPAL_MSG_TYPE_MAX; i++) 24924366360SMahesh Salgaonkar ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]); 25024366360SMahesh Salgaonkar 25124366360SMahesh Salgaonkar ret = opal_notifier_register(&opal_message_nb); 25224366360SMahesh Salgaonkar if (ret) { 25324366360SMahesh Salgaonkar pr_err("%s: Can't register OPAL event notifier (%d)\n", 25424366360SMahesh Salgaonkar __func__, ret); 25524366360SMahesh Salgaonkar return ret; 25624366360SMahesh Salgaonkar } 25724366360SMahesh Salgaonkar return 0; 25824366360SMahesh Salgaonkar } 25924366360SMahesh Salgaonkar early_initcall(opal_message_init); 26024366360SMahesh Salgaonkar 26114a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count) 26214a43e69SBenjamin Herrenschmidt { 2634f89363bSBenjamin Herrenschmidt s64 rc; 2644f89363bSBenjamin Herrenschmidt __be64 evt, len; 26514a43e69SBenjamin Herrenschmidt 26614a43e69SBenjamin Herrenschmidt if (!opal.entry) 267daea1175SBenjamin Herrenschmidt return -ENODEV; 26814a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 2694f89363bSBenjamin Herrenschmidt if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0) 27014a43e69SBenjamin Herrenschmidt return 0; 2714f89363bSBenjamin Herrenschmidt len = cpu_to_be64(count); 27214a43e69SBenjamin Herrenschmidt rc = opal_console_read(vtermno, &len, buf); 27314a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) 2744f89363bSBenjamin Herrenschmidt return be64_to_cpu(len); 27514a43e69SBenjamin Herrenschmidt return 0; 27614a43e69SBenjamin Herrenschmidt } 27714a43e69SBenjamin Herrenschmidt 27814a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len) 27914a43e69SBenjamin Herrenschmidt { 28014a43e69SBenjamin Herrenschmidt int written = 0; 2814f89363bSBenjamin Herrenschmidt __be64 olen; 282daea1175SBenjamin Herrenschmidt s64 len, rc; 28314a43e69SBenjamin Herrenschmidt unsigned long flags; 2844f89363bSBenjamin Herrenschmidt __be64 evt; 28514a43e69SBenjamin Herrenschmidt 28614a43e69SBenjamin Herrenschmidt if (!opal.entry) 287daea1175SBenjamin Herrenschmidt return -ENODEV; 28814a43e69SBenjamin Herrenschmidt 28914a43e69SBenjamin Herrenschmidt /* We want put_chars to be atomic to avoid mangling of hvsi 29014a43e69SBenjamin Herrenschmidt * packets. To do that, we first test for room and return 291daea1175SBenjamin Herrenschmidt * -EAGAIN if there isn't enough. 292daea1175SBenjamin Herrenschmidt * 293daea1175SBenjamin Herrenschmidt * Unfortunately, opal_console_write_buffer_space() doesn't 294daea1175SBenjamin Herrenschmidt * appear to work on opal v1, so we just assume there is 295daea1175SBenjamin Herrenschmidt * enough room and be done with it 29614a43e69SBenjamin Herrenschmidt */ 29714a43e69SBenjamin Herrenschmidt spin_lock_irqsave(&opal_write_lock, flags); 298daea1175SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) { 2994f89363bSBenjamin Herrenschmidt rc = opal_console_write_buffer_space(vtermno, &olen); 3004f89363bSBenjamin Herrenschmidt len = be64_to_cpu(olen); 30114a43e69SBenjamin Herrenschmidt if (rc || len < total_len) { 30214a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 30314a43e69SBenjamin Herrenschmidt /* Closed -> drop characters */ 30414a43e69SBenjamin Herrenschmidt if (rc) 30514a43e69SBenjamin Herrenschmidt return total_len; 3064f89363bSBenjamin Herrenschmidt opal_poll_events(NULL); 30714a43e69SBenjamin Herrenschmidt return -EAGAIN; 30814a43e69SBenjamin Herrenschmidt } 309daea1175SBenjamin Herrenschmidt } 31014a43e69SBenjamin Herrenschmidt 31114a43e69SBenjamin Herrenschmidt /* We still try to handle partial completions, though they 31214a43e69SBenjamin Herrenschmidt * should no longer happen. 31314a43e69SBenjamin Herrenschmidt */ 314daea1175SBenjamin Herrenschmidt rc = OPAL_BUSY; 31514a43e69SBenjamin Herrenschmidt while(total_len > 0 && (rc == OPAL_BUSY || 31614a43e69SBenjamin Herrenschmidt rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { 3174f89363bSBenjamin Herrenschmidt olen = cpu_to_be64(total_len); 3184f89363bSBenjamin Herrenschmidt rc = opal_console_write(vtermno, &olen, data); 3194f89363bSBenjamin Herrenschmidt len = be64_to_cpu(olen); 3201de1455fSBenjamin Herrenschmidt 3211de1455fSBenjamin Herrenschmidt /* Closed or other error drop */ 3221de1455fSBenjamin Herrenschmidt if (rc != OPAL_SUCCESS && rc != OPAL_BUSY && 3231de1455fSBenjamin Herrenschmidt rc != OPAL_BUSY_EVENT) { 3241de1455fSBenjamin Herrenschmidt written = total_len; 3251de1455fSBenjamin Herrenschmidt break; 3261de1455fSBenjamin Herrenschmidt } 32714a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) { 32814a43e69SBenjamin Herrenschmidt total_len -= len; 32914a43e69SBenjamin Herrenschmidt data += len; 33014a43e69SBenjamin Herrenschmidt written += len; 33114a43e69SBenjamin Herrenschmidt } 33214a43e69SBenjamin Herrenschmidt /* This is a bit nasty but we need that for the console to 33314a43e69SBenjamin Herrenschmidt * flush when there aren't any interrupts. We will clean 33414a43e69SBenjamin Herrenschmidt * things a bit later to limit that to synchronous path 33514a43e69SBenjamin Herrenschmidt * such as the kernel console and xmon/udbg 33614a43e69SBenjamin Herrenschmidt */ 33714a43e69SBenjamin Herrenschmidt do 33814a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 3394f89363bSBenjamin Herrenschmidt while(rc == OPAL_SUCCESS && 3404f89363bSBenjamin Herrenschmidt (be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT)); 34114a43e69SBenjamin Herrenschmidt } 34214a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 34314a43e69SBenjamin Herrenschmidt return written; 34414a43e69SBenjamin Herrenschmidt } 34514a43e69SBenjamin Herrenschmidt 346b63a0ffeSMahesh Salgaonkar static int opal_recover_mce(struct pt_regs *regs, 347b63a0ffeSMahesh Salgaonkar struct machine_check_event *evt) 348b63a0ffeSMahesh Salgaonkar { 349b63a0ffeSMahesh Salgaonkar int recovered = 0; 350b63a0ffeSMahesh Salgaonkar uint64_t ea = get_mce_fault_addr(evt); 351b63a0ffeSMahesh Salgaonkar 352b63a0ffeSMahesh Salgaonkar if (!(regs->msr & MSR_RI)) { 353b63a0ffeSMahesh Salgaonkar /* If MSR_RI isn't set, we cannot recover */ 354b63a0ffeSMahesh Salgaonkar recovered = 0; 355b63a0ffeSMahesh Salgaonkar } else if (evt->disposition == MCE_DISPOSITION_RECOVERED) { 356b63a0ffeSMahesh Salgaonkar /* Platform corrected itself */ 357b63a0ffeSMahesh Salgaonkar recovered = 1; 358b63a0ffeSMahesh Salgaonkar } else if (ea && !is_kernel_addr(ea)) { 359b63a0ffeSMahesh Salgaonkar /* 360b63a0ffeSMahesh Salgaonkar * Faulting address is not in kernel text. We should be fine. 361b63a0ffeSMahesh Salgaonkar * We need to find which process uses this address. 362b63a0ffeSMahesh Salgaonkar * For now, kill the task if we have received exception when 363b63a0ffeSMahesh Salgaonkar * in userspace. 364b63a0ffeSMahesh Salgaonkar * 365b63a0ffeSMahesh Salgaonkar * TODO: Queue up this address for hwpoisioning later. 366b63a0ffeSMahesh Salgaonkar */ 367b63a0ffeSMahesh Salgaonkar if (user_mode(regs) && !is_global_init(current)) { 368b63a0ffeSMahesh Salgaonkar _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip); 369b63a0ffeSMahesh Salgaonkar recovered = 1; 370b63a0ffeSMahesh Salgaonkar } else 371b63a0ffeSMahesh Salgaonkar recovered = 0; 372b63a0ffeSMahesh Salgaonkar } else if (user_mode(regs) && !is_global_init(current) && 373b63a0ffeSMahesh Salgaonkar evt->severity == MCE_SEV_ERROR_SYNC) { 374b63a0ffeSMahesh Salgaonkar /* 375b63a0ffeSMahesh Salgaonkar * If we have received a synchronous error when in userspace 376b63a0ffeSMahesh Salgaonkar * kill the task. 377b63a0ffeSMahesh Salgaonkar */ 378b63a0ffeSMahesh Salgaonkar _exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip); 379b63a0ffeSMahesh Salgaonkar recovered = 1; 380b63a0ffeSMahesh Salgaonkar } 381b63a0ffeSMahesh Salgaonkar return recovered; 382b63a0ffeSMahesh Salgaonkar } 383b63a0ffeSMahesh Salgaonkar 384ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs) 385ed79ba9eSBenjamin Herrenschmidt { 38636df96f8SMahesh Salgaonkar struct machine_check_event evt; 387ed79ba9eSBenjamin Herrenschmidt 38836df96f8SMahesh Salgaonkar if (!get_mce_event(&evt, MCE_EVENT_RELEASE)) 38936df96f8SMahesh Salgaonkar return 0; 390ed79ba9eSBenjamin Herrenschmidt 391ed79ba9eSBenjamin Herrenschmidt /* Print things out */ 39236df96f8SMahesh Salgaonkar if (evt.version != MCE_V1) { 393ed79ba9eSBenjamin Herrenschmidt pr_err("Machine Check Exception, Unknown event version %d !\n", 394ed79ba9eSBenjamin Herrenschmidt evt.version); 395ed79ba9eSBenjamin Herrenschmidt return 0; 396ed79ba9eSBenjamin Herrenschmidt } 397b5ff4211SMahesh Salgaonkar machine_check_print_event_info(&evt); 398ed79ba9eSBenjamin Herrenschmidt 399b63a0ffeSMahesh Salgaonkar if (opal_recover_mce(regs, &evt)) 400b63a0ffeSMahesh Salgaonkar return 1; 401b63a0ffeSMahesh Salgaonkar return 0; 402ed79ba9eSBenjamin Herrenschmidt } 403ed79ba9eSBenjamin Herrenschmidt 404a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data) 405a125e092SBenjamin Herrenschmidt { 4065e4da530SAnton Blanchard __be64 events; 407a125e092SBenjamin Herrenschmidt 408a125e092SBenjamin Herrenschmidt opal_handle_interrupt(virq_to_hw(irq), &events); 409a125e092SBenjamin Herrenschmidt 4101bc98de2SGavin Shan opal_do_notifier(events); 411a125e092SBenjamin Herrenschmidt 412a125e092SBenjamin Herrenschmidt return IRQ_HANDLED; 413a125e092SBenjamin Herrenschmidt } 414a125e092SBenjamin Herrenschmidt 4156f68b5e2SVasant Hegde static int opal_sysfs_init(void) 4166f68b5e2SVasant Hegde { 4176f68b5e2SVasant Hegde opal_kobj = kobject_create_and_add("opal", firmware_kobj); 4186f68b5e2SVasant Hegde if (!opal_kobj) { 4196f68b5e2SVasant Hegde pr_warn("kobject_create_and_add opal failed\n"); 4206f68b5e2SVasant Hegde return -ENOMEM; 4216f68b5e2SVasant Hegde } 4226f68b5e2SVasant Hegde 4236f68b5e2SVasant Hegde return 0; 4246f68b5e2SVasant Hegde } 4256f68b5e2SVasant Hegde 42614a43e69SBenjamin Herrenschmidt static int __init opal_init(void) 42714a43e69SBenjamin Herrenschmidt { 42814a43e69SBenjamin Herrenschmidt struct device_node *np, *consoles; 4291cc79bc8SAlistair Popple const __be32 *irqs; 430a125e092SBenjamin Herrenschmidt int rc, i, irqlen; 43114a43e69SBenjamin Herrenschmidt 43214a43e69SBenjamin Herrenschmidt opal_node = of_find_node_by_path("/ibm,opal"); 43314a43e69SBenjamin Herrenschmidt if (!opal_node) { 43414a43e69SBenjamin Herrenschmidt pr_warn("opal: Node not found\n"); 43514a43e69SBenjamin Herrenschmidt return -ENODEV; 43614a43e69SBenjamin Herrenschmidt } 4372db29d28SBenjamin Herrenschmidt 4382db29d28SBenjamin Herrenschmidt /* Register OPAL consoles if any ports */ 43914a43e69SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) 44014a43e69SBenjamin Herrenschmidt consoles = of_find_node_by_path("/ibm,opal/consoles"); 44114a43e69SBenjamin Herrenschmidt else 44214a43e69SBenjamin Herrenschmidt consoles = of_node_get(opal_node); 4432db29d28SBenjamin Herrenschmidt if (consoles) { 44414a43e69SBenjamin Herrenschmidt for_each_child_of_node(consoles, np) { 44514a43e69SBenjamin Herrenschmidt if (strcmp(np->name, "serial")) 44614a43e69SBenjamin Herrenschmidt continue; 44714a43e69SBenjamin Herrenschmidt of_platform_device_create(np, NULL, NULL); 44814a43e69SBenjamin Herrenschmidt } 44914a43e69SBenjamin Herrenschmidt of_node_put(consoles); 4502db29d28SBenjamin Herrenschmidt } 451a125e092SBenjamin Herrenschmidt 452a125e092SBenjamin Herrenschmidt /* Find all OPAL interrupts and request them */ 453a125e092SBenjamin Herrenschmidt irqs = of_get_property(opal_node, "opal-interrupts", &irqlen); 454a125e092SBenjamin Herrenschmidt pr_debug("opal: Found %d interrupts reserved for OPAL\n", 455a125e092SBenjamin Herrenschmidt irqs ? (irqlen / 4) : 0); 45673ed148aSBenjamin Herrenschmidt opal_irq_count = irqlen / 4; 45773ed148aSBenjamin Herrenschmidt opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL); 458a125e092SBenjamin Herrenschmidt for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) { 459a125e092SBenjamin Herrenschmidt unsigned int hwirq = be32_to_cpup(irqs); 460a125e092SBenjamin Herrenschmidt unsigned int irq = irq_create_mapping(NULL, hwirq); 461a125e092SBenjamin Herrenschmidt if (irq == NO_IRQ) { 462a125e092SBenjamin Herrenschmidt pr_warning("opal: Failed to map irq 0x%x\n", hwirq); 463a125e092SBenjamin Herrenschmidt continue; 464a125e092SBenjamin Herrenschmidt } 465a125e092SBenjamin Herrenschmidt rc = request_irq(irq, opal_interrupt, 0, "opal", NULL); 466a125e092SBenjamin Herrenschmidt if (rc) 467a125e092SBenjamin Herrenschmidt pr_warning("opal: Error %d requesting irq %d" 468a125e092SBenjamin Herrenschmidt " (0x%x)\n", rc, irq, hwirq); 46973ed148aSBenjamin Herrenschmidt opal_irqs[i] = irq; 470a125e092SBenjamin Herrenschmidt } 4716f68b5e2SVasant Hegde 4726f68b5e2SVasant Hegde /* Create "opal" kobject under /sys/firmware */ 4736f68b5e2SVasant Hegde rc = opal_sysfs_init(); 47450bd6153SVasant Hegde if (rc == 0) { 47550bd6153SVasant Hegde /* Setup code update interface */ 47650bd6153SVasant Hegde opal_flash_init(); 47750bd6153SVasant Hegde } 4786f68b5e2SVasant Hegde 47914a43e69SBenjamin Herrenschmidt return 0; 48014a43e69SBenjamin Herrenschmidt } 48114a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init); 48273ed148aSBenjamin Herrenschmidt 48373ed148aSBenjamin Herrenschmidt void opal_shutdown(void) 48473ed148aSBenjamin Herrenschmidt { 48573ed148aSBenjamin Herrenschmidt unsigned int i; 486f7d98d18SVasant Hegde long rc = OPAL_BUSY; 48773ed148aSBenjamin Herrenschmidt 488f7d98d18SVasant Hegde /* First free interrupts, which will also mask them */ 48973ed148aSBenjamin Herrenschmidt for (i = 0; i < opal_irq_count; i++) { 49073ed148aSBenjamin Herrenschmidt if (opal_irqs[i]) 491b0d436c7SAnton Blanchard free_irq(opal_irqs[i], NULL); 49273ed148aSBenjamin Herrenschmidt opal_irqs[i] = 0; 49373ed148aSBenjamin Herrenschmidt } 494f7d98d18SVasant Hegde 495f7d98d18SVasant Hegde /* 496f7d98d18SVasant Hegde * Then sync with OPAL which ensure anything that can 497f7d98d18SVasant Hegde * potentially write to our memory has completed such 498f7d98d18SVasant Hegde * as an ongoing dump retrieval 499f7d98d18SVasant Hegde */ 500f7d98d18SVasant Hegde while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) { 501f7d98d18SVasant Hegde rc = opal_sync_host_reboot(); 502f7d98d18SVasant Hegde if (rc == OPAL_BUSY) 503f7d98d18SVasant Hegde opal_poll_events(NULL); 504f7d98d18SVasant Hegde else 505f7d98d18SVasant Hegde mdelay(10); 506f7d98d18SVasant Hegde } 50773ed148aSBenjamin Herrenschmidt } 508