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 {
80c4463b37SJeremy Kerr 	u64 glue;
81c4463b37SJeremy Kerr 
82c4463b37SJeremy Kerr 	if (!(powerpc_firmware_features & FW_FEATURE_OPAL))
83c4463b37SJeremy Kerr 		return -ENODEV;
84c4463b37SJeremy Kerr 
85ed79ba9eSBenjamin Herrenschmidt 	/* Hookup some exception handlers. We use the fwnmi area at 0x7000
86ed79ba9eSBenjamin Herrenschmidt 	 * to provide the glue space to OPAL
87ed79ba9eSBenjamin Herrenschmidt 	 */
88ed79ba9eSBenjamin Herrenschmidt 	glue = 0x7000;
89ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER,
90ed79ba9eSBenjamin Herrenschmidt 					__pa(opal_mc_secondary_handler[0]),
91ed79ba9eSBenjamin Herrenschmidt 					glue);
92ed79ba9eSBenjamin Herrenschmidt 	glue += 128;
93ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
94ed79ba9eSBenjamin Herrenschmidt 					0, glue);
95ed79ba9eSBenjamin Herrenschmidt 	glue += 128;
96ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
97ed79ba9eSBenjamin Herrenschmidt 
98c4463b37SJeremy Kerr 	return 0;
9914a43e69SBenjamin Herrenschmidt }
10014a43e69SBenjamin Herrenschmidt 
101c4463b37SJeremy Kerr early_initcall(opal_register_exception_handlers);
102c4463b37SJeremy Kerr 
1031bc98de2SGavin Shan int opal_notifier_register(struct notifier_block *nb)
1041bc98de2SGavin Shan {
1051bc98de2SGavin Shan 	if (!nb) {
1061bc98de2SGavin Shan 		pr_warning("%s: Invalid argument (%p)\n",
1071bc98de2SGavin Shan 			   __func__, nb);
1081bc98de2SGavin Shan 		return -EINVAL;
1091bc98de2SGavin Shan 	}
1101bc98de2SGavin Shan 
1111bc98de2SGavin Shan 	atomic_notifier_chain_register(&opal_notifier_head, nb);
1121bc98de2SGavin Shan 	return 0;
1131bc98de2SGavin Shan }
1141bc98de2SGavin Shan 
1151bc98de2SGavin Shan static void opal_do_notifier(uint64_t events)
1161bc98de2SGavin Shan {
1171bc98de2SGavin Shan 	unsigned long flags;
1181bc98de2SGavin Shan 	uint64_t changed_mask;
1191bc98de2SGavin Shan 
1201bc98de2SGavin Shan 	if (atomic_read(&opal_notifier_hold))
1211bc98de2SGavin Shan 		return;
1221bc98de2SGavin Shan 
1231bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1241bc98de2SGavin Shan 	changed_mask = last_notified_mask ^ events;
1251bc98de2SGavin Shan 	last_notified_mask = events;
1261bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1271bc98de2SGavin Shan 
1281bc98de2SGavin Shan 	/*
1291bc98de2SGavin Shan 	 * We feed with the event bits and changed bits for
1301bc98de2SGavin Shan 	 * enough information to the callback.
1311bc98de2SGavin Shan 	 */
1321bc98de2SGavin Shan 	atomic_notifier_call_chain(&opal_notifier_head,
1331bc98de2SGavin Shan 				   events, (void *)changed_mask);
1341bc98de2SGavin Shan }
1351bc98de2SGavin Shan 
1361bc98de2SGavin Shan void opal_notifier_update_evt(uint64_t evt_mask,
1371bc98de2SGavin Shan 			      uint64_t evt_val)
1381bc98de2SGavin Shan {
1391bc98de2SGavin Shan 	unsigned long flags;
1401bc98de2SGavin Shan 
1411bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1421bc98de2SGavin Shan 	last_notified_mask &= ~evt_mask;
1431bc98de2SGavin Shan 	last_notified_mask |= evt_val;
1441bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1451bc98de2SGavin Shan }
1461bc98de2SGavin Shan 
1471bc98de2SGavin Shan void opal_notifier_enable(void)
1481bc98de2SGavin Shan {
1491bc98de2SGavin Shan 	int64_t rc;
1501bc98de2SGavin Shan 	uint64_t evt = 0;
1511bc98de2SGavin Shan 
1521bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 0);
1531bc98de2SGavin Shan 
1541bc98de2SGavin Shan 	/* Process pending events */
1551bc98de2SGavin Shan 	rc = opal_poll_events(&evt);
1561bc98de2SGavin Shan 	if (rc == OPAL_SUCCESS && evt)
1571bc98de2SGavin Shan 		opal_do_notifier(evt);
1581bc98de2SGavin Shan }
1591bc98de2SGavin Shan 
1601bc98de2SGavin Shan void opal_notifier_disable(void)
1611bc98de2SGavin Shan {
1621bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 1);
1631bc98de2SGavin Shan }
1641bc98de2SGavin Shan 
16514a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count)
16614a43e69SBenjamin Herrenschmidt {
1674f89363bSBenjamin Herrenschmidt 	s64 rc;
1684f89363bSBenjamin Herrenschmidt 	__be64 evt, len;
16914a43e69SBenjamin Herrenschmidt 
17014a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
171daea1175SBenjamin Herrenschmidt 		return -ENODEV;
17214a43e69SBenjamin Herrenschmidt 	opal_poll_events(&evt);
1734f89363bSBenjamin Herrenschmidt 	if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0)
17414a43e69SBenjamin Herrenschmidt 		return 0;
1754f89363bSBenjamin Herrenschmidt 	len = cpu_to_be64(count);
17614a43e69SBenjamin Herrenschmidt 	rc = opal_console_read(vtermno, &len, buf);
17714a43e69SBenjamin Herrenschmidt 	if (rc == OPAL_SUCCESS)
1784f89363bSBenjamin Herrenschmidt 		return be64_to_cpu(len);
17914a43e69SBenjamin Herrenschmidt 	return 0;
18014a43e69SBenjamin Herrenschmidt }
18114a43e69SBenjamin Herrenschmidt 
18214a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
18314a43e69SBenjamin Herrenschmidt {
18414a43e69SBenjamin Herrenschmidt 	int written = 0;
1854f89363bSBenjamin Herrenschmidt 	__be64 olen;
186daea1175SBenjamin Herrenschmidt 	s64 len, rc;
18714a43e69SBenjamin Herrenschmidt 	unsigned long flags;
1884f89363bSBenjamin Herrenschmidt 	__be64 evt;
18914a43e69SBenjamin Herrenschmidt 
19014a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
191daea1175SBenjamin Herrenschmidt 		return -ENODEV;
19214a43e69SBenjamin Herrenschmidt 
19314a43e69SBenjamin Herrenschmidt 	/* We want put_chars to be atomic to avoid mangling of hvsi
19414a43e69SBenjamin Herrenschmidt 	 * packets. To do that, we first test for room and return
195daea1175SBenjamin Herrenschmidt 	 * -EAGAIN if there isn't enough.
196daea1175SBenjamin Herrenschmidt 	 *
197daea1175SBenjamin Herrenschmidt 	 * Unfortunately, opal_console_write_buffer_space() doesn't
198daea1175SBenjamin Herrenschmidt 	 * appear to work on opal v1, so we just assume there is
199daea1175SBenjamin Herrenschmidt 	 * enough room and be done with it
20014a43e69SBenjamin Herrenschmidt 	 */
20114a43e69SBenjamin Herrenschmidt 	spin_lock_irqsave(&opal_write_lock, flags);
202daea1175SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2)) {
2034f89363bSBenjamin Herrenschmidt 		rc = opal_console_write_buffer_space(vtermno, &olen);
2044f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
20514a43e69SBenjamin Herrenschmidt 		if (rc || len < total_len) {
20614a43e69SBenjamin Herrenschmidt 			spin_unlock_irqrestore(&opal_write_lock, flags);
20714a43e69SBenjamin Herrenschmidt 			/* Closed -> drop characters */
20814a43e69SBenjamin Herrenschmidt 			if (rc)
20914a43e69SBenjamin Herrenschmidt 				return total_len;
2104f89363bSBenjamin Herrenschmidt 			opal_poll_events(NULL);
21114a43e69SBenjamin Herrenschmidt 			return -EAGAIN;
21214a43e69SBenjamin Herrenschmidt 		}
213daea1175SBenjamin Herrenschmidt 	}
21414a43e69SBenjamin Herrenschmidt 
21514a43e69SBenjamin Herrenschmidt 	/* We still try to handle partial completions, though they
21614a43e69SBenjamin Herrenschmidt 	 * should no longer happen.
21714a43e69SBenjamin Herrenschmidt 	 */
218daea1175SBenjamin Herrenschmidt 	rc = OPAL_BUSY;
21914a43e69SBenjamin Herrenschmidt 	while(total_len > 0 && (rc == OPAL_BUSY ||
22014a43e69SBenjamin Herrenschmidt 				rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
2214f89363bSBenjamin Herrenschmidt 		olen = cpu_to_be64(total_len);
2224f89363bSBenjamin Herrenschmidt 		rc = opal_console_write(vtermno, &olen, data);
2234f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
2241de1455fSBenjamin Herrenschmidt 
2251de1455fSBenjamin Herrenschmidt 		/* Closed or other error drop */
2261de1455fSBenjamin Herrenschmidt 		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
2271de1455fSBenjamin Herrenschmidt 		    rc != OPAL_BUSY_EVENT) {
2281de1455fSBenjamin Herrenschmidt 			written = total_len;
2291de1455fSBenjamin Herrenschmidt 			break;
2301de1455fSBenjamin Herrenschmidt 		}
23114a43e69SBenjamin Herrenschmidt 		if (rc == OPAL_SUCCESS) {
23214a43e69SBenjamin Herrenschmidt 			total_len -= len;
23314a43e69SBenjamin Herrenschmidt 			data += len;
23414a43e69SBenjamin Herrenschmidt 			written += len;
23514a43e69SBenjamin Herrenschmidt 		}
23614a43e69SBenjamin Herrenschmidt 		/* This is a bit nasty but we need that for the console to
23714a43e69SBenjamin Herrenschmidt 		 * flush when there aren't any interrupts. We will clean
23814a43e69SBenjamin Herrenschmidt 		 * things a bit later to limit that to synchronous path
23914a43e69SBenjamin Herrenschmidt 		 * such as the kernel console and xmon/udbg
24014a43e69SBenjamin Herrenschmidt 		 */
24114a43e69SBenjamin Herrenschmidt 		do
24214a43e69SBenjamin Herrenschmidt 			opal_poll_events(&evt);
2434f89363bSBenjamin Herrenschmidt 		while(rc == OPAL_SUCCESS &&
2444f89363bSBenjamin Herrenschmidt 			(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT));
24514a43e69SBenjamin Herrenschmidt 	}
24614a43e69SBenjamin Herrenschmidt 	spin_unlock_irqrestore(&opal_write_lock, flags);
24714a43e69SBenjamin Herrenschmidt 	return written;
24814a43e69SBenjamin Herrenschmidt }
24914a43e69SBenjamin Herrenschmidt 
250ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs)
251ed79ba9eSBenjamin Herrenschmidt {
252ed79ba9eSBenjamin Herrenschmidt 	struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
253ed79ba9eSBenjamin Herrenschmidt 	struct opal_machine_check_event evt;
254ed79ba9eSBenjamin Herrenschmidt 	const char *level, *sevstr, *subtype;
255ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_ue_types[] = {
256ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
257ed79ba9eSBenjamin Herrenschmidt 		"Instruction fetch",
258ed79ba9eSBenjamin Herrenschmidt 		"Page table walk ifetch",
259ed79ba9eSBenjamin Herrenschmidt 		"Load/Store",
260ed79ba9eSBenjamin Herrenschmidt 		"Page table walk Load/Store",
261ed79ba9eSBenjamin Herrenschmidt 	};
262ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_slb_types[] = {
263ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
264ed79ba9eSBenjamin Herrenschmidt 		"Parity",
265ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
266ed79ba9eSBenjamin Herrenschmidt 	};
267ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_erat_types[] = {
268ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
269ed79ba9eSBenjamin Herrenschmidt 		"Parity",
270ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
271ed79ba9eSBenjamin Herrenschmidt 	};
272ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_tlb_types[] = {
273ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
274ed79ba9eSBenjamin Herrenschmidt 		"Parity",
275ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
276ed79ba9eSBenjamin Herrenschmidt 	};
277ed79ba9eSBenjamin Herrenschmidt 
278ed79ba9eSBenjamin Herrenschmidt 	/* Copy the event structure and release the original */
279ed79ba9eSBenjamin Herrenschmidt 	evt = *opal_evt;
280ed79ba9eSBenjamin Herrenschmidt 	opal_evt->in_use = 0;
281ed79ba9eSBenjamin Herrenschmidt 
282ed79ba9eSBenjamin Herrenschmidt 	/* Print things out */
283ed79ba9eSBenjamin Herrenschmidt 	if (evt.version != OpalMCE_V1) {
284ed79ba9eSBenjamin Herrenschmidt 		pr_err("Machine Check Exception, Unknown event version %d !\n",
285ed79ba9eSBenjamin Herrenschmidt 		       evt.version);
286ed79ba9eSBenjamin Herrenschmidt 		return 0;
287ed79ba9eSBenjamin Herrenschmidt 	}
288ed79ba9eSBenjamin Herrenschmidt 	switch(evt.severity) {
289ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_NO_ERROR:
290ed79ba9eSBenjamin Herrenschmidt 		level = KERN_INFO;
291ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Harmless";
292ed79ba9eSBenjamin Herrenschmidt 		break;
293ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_WARNING:
294ed79ba9eSBenjamin Herrenschmidt 		level = KERN_WARNING;
295ed79ba9eSBenjamin Herrenschmidt 		sevstr = "";
296ed79ba9eSBenjamin Herrenschmidt 		break;
297ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_ERROR_SYNC:
298ed79ba9eSBenjamin Herrenschmidt 		level = KERN_ERR;
299ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Severe";
300ed79ba9eSBenjamin Herrenschmidt 		break;
301ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_FATAL:
302ed79ba9eSBenjamin Herrenschmidt 	default:
303ed79ba9eSBenjamin Herrenschmidt 		level = KERN_ERR;
304ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Fatal";
305ed79ba9eSBenjamin Herrenschmidt 		break;
306ed79ba9eSBenjamin Herrenschmidt 	}
307ed79ba9eSBenjamin Herrenschmidt 
308ed79ba9eSBenjamin Herrenschmidt 	printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
309ed79ba9eSBenjamin Herrenschmidt 	       evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
310ed79ba9eSBenjamin Herrenschmidt 	       "Recovered" : "[Not recovered");
311ed79ba9eSBenjamin Herrenschmidt 	printk("%s  Initiator: %s\n", level,
312ed79ba9eSBenjamin Herrenschmidt 	       evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
313ed79ba9eSBenjamin Herrenschmidt 	switch(evt.error_type) {
314ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_UE:
315ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.ue_error.ue_error_type <
316ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_ue_types) ?
317ed79ba9eSBenjamin Herrenschmidt 			opal_mc_ue_types[evt.u.ue_error.ue_error_type]
318ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
319ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: UE [%s]\n", level, subtype);
320ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.ue_error.effective_address_provided)
321ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
322ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.ue_error.effective_address);
323ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.ue_error.physical_address_provided)
324ed79ba9eSBenjamin Herrenschmidt 			printk("%s      Physial address: %016llx\n",
325ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.ue_error.physical_address);
326ed79ba9eSBenjamin Herrenschmidt 		break;
327ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_SLB:
328ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.slb_error.slb_error_type <
329ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_slb_types) ?
330ed79ba9eSBenjamin Herrenschmidt 			opal_mc_slb_types[evt.u.slb_error.slb_error_type]
331ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
332ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: SLB [%s]\n", level, subtype);
333ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.slb_error.effective_address_provided)
334ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
335ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.slb_error.effective_address);
336ed79ba9eSBenjamin Herrenschmidt 		break;
337ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_ERAT:
338ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.erat_error.erat_error_type <
339ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_erat_types) ?
340ed79ba9eSBenjamin Herrenschmidt 			opal_mc_erat_types[evt.u.erat_error.erat_error_type]
341ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
342ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: ERAT [%s]\n", level, subtype);
343ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.erat_error.effective_address_provided)
344ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
345ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.erat_error.effective_address);
346ed79ba9eSBenjamin Herrenschmidt 		break;
347ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_TLB:
348ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.tlb_error.tlb_error_type <
349ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_tlb_types) ?
350ed79ba9eSBenjamin Herrenschmidt 			opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
351ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
352ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: TLB [%s]\n", level, subtype);
353ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.tlb_error.effective_address_provided)
354ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
355ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.tlb_error.effective_address);
356ed79ba9eSBenjamin Herrenschmidt 		break;
357ed79ba9eSBenjamin Herrenschmidt 	default:
358ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_UNKNOWN:
359ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: Unknown\n", level);
360ed79ba9eSBenjamin Herrenschmidt 		break;
361ed79ba9eSBenjamin Herrenschmidt 	}
362ed79ba9eSBenjamin Herrenschmidt 	return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
363ed79ba9eSBenjamin Herrenschmidt }
364ed79ba9eSBenjamin Herrenschmidt 
365a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data)
366a125e092SBenjamin Herrenschmidt {
367a125e092SBenjamin Herrenschmidt 	uint64_t events;
368a125e092SBenjamin Herrenschmidt 
369a125e092SBenjamin Herrenschmidt 	opal_handle_interrupt(virq_to_hw(irq), &events);
370a125e092SBenjamin Herrenschmidt 
3711bc98de2SGavin Shan 	opal_do_notifier(events);
372a125e092SBenjamin Herrenschmidt 
373a125e092SBenjamin Herrenschmidt 	return IRQ_HANDLED;
374a125e092SBenjamin Herrenschmidt }
375a125e092SBenjamin Herrenschmidt 
37614a43e69SBenjamin Herrenschmidt static int __init opal_init(void)
37714a43e69SBenjamin Herrenschmidt {
37814a43e69SBenjamin Herrenschmidt 	struct device_node *np, *consoles;
3791cc79bc8SAlistair Popple 	const __be32 *irqs;
380a125e092SBenjamin Herrenschmidt 	int rc, i, irqlen;
38114a43e69SBenjamin Herrenschmidt 
38214a43e69SBenjamin Herrenschmidt 	opal_node = of_find_node_by_path("/ibm,opal");
38314a43e69SBenjamin Herrenschmidt 	if (!opal_node) {
38414a43e69SBenjamin Herrenschmidt 		pr_warn("opal: Node not found\n");
38514a43e69SBenjamin Herrenschmidt 		return -ENODEV;
38614a43e69SBenjamin Herrenschmidt 	}
3872db29d28SBenjamin Herrenschmidt 
3882db29d28SBenjamin Herrenschmidt 	/* Register OPAL consoles if any ports */
38914a43e69SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2))
39014a43e69SBenjamin Herrenschmidt 		consoles = of_find_node_by_path("/ibm,opal/consoles");
39114a43e69SBenjamin Herrenschmidt 	else
39214a43e69SBenjamin Herrenschmidt 		consoles = of_node_get(opal_node);
3932db29d28SBenjamin Herrenschmidt 	if (consoles) {
39414a43e69SBenjamin Herrenschmidt 		for_each_child_of_node(consoles, np) {
39514a43e69SBenjamin Herrenschmidt 			if (strcmp(np->name, "serial"))
39614a43e69SBenjamin Herrenschmidt 				continue;
39714a43e69SBenjamin Herrenschmidt 			of_platform_device_create(np, NULL, NULL);
39814a43e69SBenjamin Herrenschmidt 		}
39914a43e69SBenjamin Herrenschmidt 		of_node_put(consoles);
4002db29d28SBenjamin Herrenschmidt 	}
401a125e092SBenjamin Herrenschmidt 
402a125e092SBenjamin Herrenschmidt 	/* Find all OPAL interrupts and request them */
403a125e092SBenjamin Herrenschmidt 	irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
404a125e092SBenjamin Herrenschmidt 	pr_debug("opal: Found %d interrupts reserved for OPAL\n",
405a125e092SBenjamin Herrenschmidt 		 irqs ? (irqlen / 4) : 0);
40673ed148aSBenjamin Herrenschmidt 	opal_irq_count = irqlen / 4;
40773ed148aSBenjamin Herrenschmidt 	opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
408a125e092SBenjamin Herrenschmidt 	for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
409a125e092SBenjamin Herrenschmidt 		unsigned int hwirq = be32_to_cpup(irqs);
410a125e092SBenjamin Herrenschmidt 		unsigned int irq = irq_create_mapping(NULL, hwirq);
411a125e092SBenjamin Herrenschmidt 		if (irq == NO_IRQ) {
412a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
413a125e092SBenjamin Herrenschmidt 			continue;
414a125e092SBenjamin Herrenschmidt 		}
415a125e092SBenjamin Herrenschmidt 		rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
416a125e092SBenjamin Herrenschmidt 		if (rc)
417a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Error %d requesting irq %d"
418a125e092SBenjamin Herrenschmidt 				   " (0x%x)\n", rc, irq, hwirq);
41973ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = irq;
420a125e092SBenjamin Herrenschmidt 	}
42114a43e69SBenjamin Herrenschmidt 	return 0;
42214a43e69SBenjamin Herrenschmidt }
42314a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init);
42473ed148aSBenjamin Herrenschmidt 
42573ed148aSBenjamin Herrenschmidt void opal_shutdown(void)
42673ed148aSBenjamin Herrenschmidt {
42773ed148aSBenjamin Herrenschmidt 	unsigned int i;
42873ed148aSBenjamin Herrenschmidt 
42973ed148aSBenjamin Herrenschmidt 	for (i = 0; i < opal_irq_count; i++) {
43073ed148aSBenjamin Herrenschmidt 		if (opal_irqs[i])
431b0d436c7SAnton Blanchard 			free_irq(opal_irqs[i], NULL);
43273ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = 0;
43373ed148aSBenjamin Herrenschmidt 	}
43473ed148aSBenjamin Herrenschmidt }
435