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 {
16714a43e69SBenjamin Herrenschmidt 	s64 len, rc;
16814a43e69SBenjamin Herrenschmidt 	u64 evt;
16914a43e69SBenjamin Herrenschmidt 
17014a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
171daea1175SBenjamin Herrenschmidt 		return -ENODEV;
17214a43e69SBenjamin Herrenschmidt 	opal_poll_events(&evt);
17314a43e69SBenjamin Herrenschmidt 	if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0)
17414a43e69SBenjamin Herrenschmidt 		return 0;
17514a43e69SBenjamin Herrenschmidt 	len = count;
17614a43e69SBenjamin Herrenschmidt 	rc = opal_console_read(vtermno, &len, buf);
17714a43e69SBenjamin Herrenschmidt 	if (rc == OPAL_SUCCESS)
17814a43e69SBenjamin Herrenschmidt 		return 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;
185daea1175SBenjamin Herrenschmidt 	s64 len, rc;
18614a43e69SBenjamin Herrenschmidt 	unsigned long flags;
18714a43e69SBenjamin Herrenschmidt 	u64 evt;
18814a43e69SBenjamin Herrenschmidt 
18914a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
190daea1175SBenjamin Herrenschmidt 		return -ENODEV;
19114a43e69SBenjamin Herrenschmidt 
19214a43e69SBenjamin Herrenschmidt 	/* We want put_chars to be atomic to avoid mangling of hvsi
19314a43e69SBenjamin Herrenschmidt 	 * packets. To do that, we first test for room and return
194daea1175SBenjamin Herrenschmidt 	 * -EAGAIN if there isn't enough.
195daea1175SBenjamin Herrenschmidt 	 *
196daea1175SBenjamin Herrenschmidt 	 * Unfortunately, opal_console_write_buffer_space() doesn't
197daea1175SBenjamin Herrenschmidt 	 * appear to work on opal v1, so we just assume there is
198daea1175SBenjamin Herrenschmidt 	 * enough room and be done with it
19914a43e69SBenjamin Herrenschmidt 	 */
20014a43e69SBenjamin Herrenschmidt 	spin_lock_irqsave(&opal_write_lock, flags);
201daea1175SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2)) {
20214a43e69SBenjamin Herrenschmidt 		rc = opal_console_write_buffer_space(vtermno, &len);
20314a43e69SBenjamin Herrenschmidt 		if (rc || len < total_len) {
20414a43e69SBenjamin Herrenschmidt 			spin_unlock_irqrestore(&opal_write_lock, flags);
20514a43e69SBenjamin Herrenschmidt 			/* Closed -> drop characters */
20614a43e69SBenjamin Herrenschmidt 			if (rc)
20714a43e69SBenjamin Herrenschmidt 				return total_len;
20814a43e69SBenjamin Herrenschmidt 			opal_poll_events(&evt);
20914a43e69SBenjamin Herrenschmidt 			return -EAGAIN;
21014a43e69SBenjamin Herrenschmidt 		}
211daea1175SBenjamin Herrenschmidt 	}
21214a43e69SBenjamin Herrenschmidt 
21314a43e69SBenjamin Herrenschmidt 	/* We still try to handle partial completions, though they
21414a43e69SBenjamin Herrenschmidt 	 * should no longer happen.
21514a43e69SBenjamin Herrenschmidt 	 */
216daea1175SBenjamin Herrenschmidt 	rc = OPAL_BUSY;
21714a43e69SBenjamin Herrenschmidt 	while(total_len > 0 && (rc == OPAL_BUSY ||
21814a43e69SBenjamin Herrenschmidt 				rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
21914a43e69SBenjamin Herrenschmidt 		len = total_len;
22014a43e69SBenjamin Herrenschmidt 		rc = opal_console_write(vtermno, &len, data);
2211de1455fSBenjamin Herrenschmidt 
2221de1455fSBenjamin Herrenschmidt 		/* Closed or other error drop */
2231de1455fSBenjamin Herrenschmidt 		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
2241de1455fSBenjamin Herrenschmidt 		    rc != OPAL_BUSY_EVENT) {
2251de1455fSBenjamin Herrenschmidt 			written = total_len;
2261de1455fSBenjamin Herrenschmidt 			break;
2271de1455fSBenjamin Herrenschmidt 		}
22814a43e69SBenjamin Herrenschmidt 		if (rc == OPAL_SUCCESS) {
22914a43e69SBenjamin Herrenschmidt 			total_len -= len;
23014a43e69SBenjamin Herrenschmidt 			data += len;
23114a43e69SBenjamin Herrenschmidt 			written += len;
23214a43e69SBenjamin Herrenschmidt 		}
23314a43e69SBenjamin Herrenschmidt 		/* This is a bit nasty but we need that for the console to
23414a43e69SBenjamin Herrenschmidt 		 * flush when there aren't any interrupts. We will clean
23514a43e69SBenjamin Herrenschmidt 		 * things a bit later to limit that to synchronous path
23614a43e69SBenjamin Herrenschmidt 		 * such as the kernel console and xmon/udbg
23714a43e69SBenjamin Herrenschmidt 		 */
23814a43e69SBenjamin Herrenschmidt 		do
23914a43e69SBenjamin Herrenschmidt 			opal_poll_events(&evt);
24014a43e69SBenjamin Herrenschmidt 		while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT));
24114a43e69SBenjamin Herrenschmidt 	}
24214a43e69SBenjamin Herrenschmidt 	spin_unlock_irqrestore(&opal_write_lock, flags);
24314a43e69SBenjamin Herrenschmidt 	return written;
24414a43e69SBenjamin Herrenschmidt }
24514a43e69SBenjamin Herrenschmidt 
246ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs)
247ed79ba9eSBenjamin Herrenschmidt {
248ed79ba9eSBenjamin Herrenschmidt 	struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
249ed79ba9eSBenjamin Herrenschmidt 	struct opal_machine_check_event evt;
250ed79ba9eSBenjamin Herrenschmidt 	const char *level, *sevstr, *subtype;
251ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_ue_types[] = {
252ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
253ed79ba9eSBenjamin Herrenschmidt 		"Instruction fetch",
254ed79ba9eSBenjamin Herrenschmidt 		"Page table walk ifetch",
255ed79ba9eSBenjamin Herrenschmidt 		"Load/Store",
256ed79ba9eSBenjamin Herrenschmidt 		"Page table walk Load/Store",
257ed79ba9eSBenjamin Herrenschmidt 	};
258ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_slb_types[] = {
259ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
260ed79ba9eSBenjamin Herrenschmidt 		"Parity",
261ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
262ed79ba9eSBenjamin Herrenschmidt 	};
263ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_erat_types[] = {
264ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
265ed79ba9eSBenjamin Herrenschmidt 		"Parity",
266ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
267ed79ba9eSBenjamin Herrenschmidt 	};
268ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_tlb_types[] = {
269ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
270ed79ba9eSBenjamin Herrenschmidt 		"Parity",
271ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
272ed79ba9eSBenjamin Herrenschmidt 	};
273ed79ba9eSBenjamin Herrenschmidt 
274ed79ba9eSBenjamin Herrenschmidt 	/* Copy the event structure and release the original */
275ed79ba9eSBenjamin Herrenschmidt 	evt = *opal_evt;
276ed79ba9eSBenjamin Herrenschmidt 	opal_evt->in_use = 0;
277ed79ba9eSBenjamin Herrenschmidt 
278ed79ba9eSBenjamin Herrenschmidt 	/* Print things out */
279ed79ba9eSBenjamin Herrenschmidt 	if (evt.version != OpalMCE_V1) {
280ed79ba9eSBenjamin Herrenschmidt 		pr_err("Machine Check Exception, Unknown event version %d !\n",
281ed79ba9eSBenjamin Herrenschmidt 		       evt.version);
282ed79ba9eSBenjamin Herrenschmidt 		return 0;
283ed79ba9eSBenjamin Herrenschmidt 	}
284ed79ba9eSBenjamin Herrenschmidt 	switch(evt.severity) {
285ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_NO_ERROR:
286ed79ba9eSBenjamin Herrenschmidt 		level = KERN_INFO;
287ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Harmless";
288ed79ba9eSBenjamin Herrenschmidt 		break;
289ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_WARNING:
290ed79ba9eSBenjamin Herrenschmidt 		level = KERN_WARNING;
291ed79ba9eSBenjamin Herrenschmidt 		sevstr = "";
292ed79ba9eSBenjamin Herrenschmidt 		break;
293ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_ERROR_SYNC:
294ed79ba9eSBenjamin Herrenschmidt 		level = KERN_ERR;
295ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Severe";
296ed79ba9eSBenjamin Herrenschmidt 		break;
297ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_SEV_FATAL:
298ed79ba9eSBenjamin Herrenschmidt 	default:
299ed79ba9eSBenjamin Herrenschmidt 		level = KERN_ERR;
300ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Fatal";
301ed79ba9eSBenjamin Herrenschmidt 		break;
302ed79ba9eSBenjamin Herrenschmidt 	}
303ed79ba9eSBenjamin Herrenschmidt 
304ed79ba9eSBenjamin Herrenschmidt 	printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
305ed79ba9eSBenjamin Herrenschmidt 	       evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
306ed79ba9eSBenjamin Herrenschmidt 	       "Recovered" : "[Not recovered");
307ed79ba9eSBenjamin Herrenschmidt 	printk("%s  Initiator: %s\n", level,
308ed79ba9eSBenjamin Herrenschmidt 	       evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
309ed79ba9eSBenjamin Herrenschmidt 	switch(evt.error_type) {
310ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_UE:
311ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.ue_error.ue_error_type <
312ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_ue_types) ?
313ed79ba9eSBenjamin Herrenschmidt 			opal_mc_ue_types[evt.u.ue_error.ue_error_type]
314ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
315ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: UE [%s]\n", level, subtype);
316ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.ue_error.effective_address_provided)
317ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
318ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.ue_error.effective_address);
319ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.ue_error.physical_address_provided)
320ed79ba9eSBenjamin Herrenschmidt 			printk("%s      Physial address: %016llx\n",
321ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.ue_error.physical_address);
322ed79ba9eSBenjamin Herrenschmidt 		break;
323ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_SLB:
324ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.slb_error.slb_error_type <
325ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_slb_types) ?
326ed79ba9eSBenjamin Herrenschmidt 			opal_mc_slb_types[evt.u.slb_error.slb_error_type]
327ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
328ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: SLB [%s]\n", level, subtype);
329ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.slb_error.effective_address_provided)
330ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
331ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.slb_error.effective_address);
332ed79ba9eSBenjamin Herrenschmidt 		break;
333ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_ERAT:
334ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.erat_error.erat_error_type <
335ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_erat_types) ?
336ed79ba9eSBenjamin Herrenschmidt 			opal_mc_erat_types[evt.u.erat_error.erat_error_type]
337ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
338ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: ERAT [%s]\n", level, subtype);
339ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.erat_error.effective_address_provided)
340ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
341ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.erat_error.effective_address);
342ed79ba9eSBenjamin Herrenschmidt 		break;
343ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_TLB:
344ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.tlb_error.tlb_error_type <
345ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_tlb_types) ?
346ed79ba9eSBenjamin Herrenschmidt 			opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
347ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
348ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: TLB [%s]\n", level, subtype);
349ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.tlb_error.effective_address_provided)
350ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
351ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.tlb_error.effective_address);
352ed79ba9eSBenjamin Herrenschmidt 		break;
353ed79ba9eSBenjamin Herrenschmidt 	default:
354ed79ba9eSBenjamin Herrenschmidt 	case OpalMCE_ERROR_TYPE_UNKNOWN:
355ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: Unknown\n", level);
356ed79ba9eSBenjamin Herrenschmidt 		break;
357ed79ba9eSBenjamin Herrenschmidt 	}
358ed79ba9eSBenjamin Herrenschmidt 	return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
359ed79ba9eSBenjamin Herrenschmidt }
360ed79ba9eSBenjamin Herrenschmidt 
361a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data)
362a125e092SBenjamin Herrenschmidt {
363a125e092SBenjamin Herrenschmidt 	uint64_t events;
364a125e092SBenjamin Herrenschmidt 
365a125e092SBenjamin Herrenschmidt 	opal_handle_interrupt(virq_to_hw(irq), &events);
366a125e092SBenjamin Herrenschmidt 
3671bc98de2SGavin Shan 	opal_do_notifier(events);
368a125e092SBenjamin Herrenschmidt 
369a125e092SBenjamin Herrenschmidt 	return IRQ_HANDLED;
370a125e092SBenjamin Herrenschmidt }
371a125e092SBenjamin Herrenschmidt 
37214a43e69SBenjamin Herrenschmidt static int __init opal_init(void)
37314a43e69SBenjamin Herrenschmidt {
37414a43e69SBenjamin Herrenschmidt 	struct device_node *np, *consoles;
375a125e092SBenjamin Herrenschmidt 	const u32 *irqs;
376a125e092SBenjamin Herrenschmidt 	int rc, i, irqlen;
37714a43e69SBenjamin Herrenschmidt 
37814a43e69SBenjamin Herrenschmidt 	opal_node = of_find_node_by_path("/ibm,opal");
37914a43e69SBenjamin Herrenschmidt 	if (!opal_node) {
38014a43e69SBenjamin Herrenschmidt 		pr_warn("opal: Node not found\n");
38114a43e69SBenjamin Herrenschmidt 		return -ENODEV;
38214a43e69SBenjamin Herrenschmidt 	}
38314a43e69SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2))
38414a43e69SBenjamin Herrenschmidt 		consoles = of_find_node_by_path("/ibm,opal/consoles");
38514a43e69SBenjamin Herrenschmidt 	else
38614a43e69SBenjamin Herrenschmidt 		consoles = of_node_get(opal_node);
38714a43e69SBenjamin Herrenschmidt 
38814a43e69SBenjamin Herrenschmidt 	/* Register serial ports */
38914a43e69SBenjamin Herrenschmidt 	for_each_child_of_node(consoles, np) {
39014a43e69SBenjamin Herrenschmidt 		if (strcmp(np->name, "serial"))
39114a43e69SBenjamin Herrenschmidt 			continue;
39214a43e69SBenjamin Herrenschmidt 		of_platform_device_create(np, NULL, NULL);
39314a43e69SBenjamin Herrenschmidt 	}
39414a43e69SBenjamin Herrenschmidt 	of_node_put(consoles);
395a125e092SBenjamin Herrenschmidt 
396a125e092SBenjamin Herrenschmidt 	/* Find all OPAL interrupts and request them */
397a125e092SBenjamin Herrenschmidt 	irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
398a125e092SBenjamin Herrenschmidt 	pr_debug("opal: Found %d interrupts reserved for OPAL\n",
399a125e092SBenjamin Herrenschmidt 		 irqs ? (irqlen / 4) : 0);
40073ed148aSBenjamin Herrenschmidt 	opal_irq_count = irqlen / 4;
40173ed148aSBenjamin Herrenschmidt 	opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
402a125e092SBenjamin Herrenschmidt 	for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
403a125e092SBenjamin Herrenschmidt 		unsigned int hwirq = be32_to_cpup(irqs);
404a125e092SBenjamin Herrenschmidt 		unsigned int irq = irq_create_mapping(NULL, hwirq);
405a125e092SBenjamin Herrenschmidt 		if (irq == NO_IRQ) {
406a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
407a125e092SBenjamin Herrenschmidt 			continue;
408a125e092SBenjamin Herrenschmidt 		}
409a125e092SBenjamin Herrenschmidt 		rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
410a125e092SBenjamin Herrenschmidt 		if (rc)
411a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Error %d requesting irq %d"
412a125e092SBenjamin Herrenschmidt 				   " (0x%x)\n", rc, irq, hwirq);
41373ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = irq;
414a125e092SBenjamin Herrenschmidt 	}
41514a43e69SBenjamin Herrenschmidt 	return 0;
41614a43e69SBenjamin Herrenschmidt }
41714a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init);
41873ed148aSBenjamin Herrenschmidt 
41973ed148aSBenjamin Herrenschmidt void opal_shutdown(void)
42073ed148aSBenjamin Herrenschmidt {
42173ed148aSBenjamin Herrenschmidt 	unsigned int i;
42273ed148aSBenjamin Herrenschmidt 
42373ed148aSBenjamin Herrenschmidt 	for (i = 0; i < opal_irq_count; i++) {
42473ed148aSBenjamin Herrenschmidt 		if (opal_irqs[i])
42573ed148aSBenjamin Herrenschmidt 			free_irq(opal_irqs[i], 0);
42673ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = 0;
42773ed148aSBenjamin Herrenschmidt 	}
42873ed148aSBenjamin Herrenschmidt }
429