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>
216f68b5e2SVasant Hegde #include <linux/kobject.h>
2214a43e69SBenjamin Herrenschmidt #include <asm/opal.h>
2314a43e69SBenjamin Herrenschmidt #include <asm/firmware.h>
2436df96f8SMahesh Salgaonkar #include <asm/mce.h>
2514a43e69SBenjamin Herrenschmidt 
2614a43e69SBenjamin Herrenschmidt #include "powernv.h"
2714a43e69SBenjamin Herrenschmidt 
286f68b5e2SVasant Hegde /* /sys/firmware/opal */
296f68b5e2SVasant Hegde struct kobject *opal_kobj;
306f68b5e2SVasant Hegde 
3114a43e69SBenjamin Herrenschmidt struct opal {
3214a43e69SBenjamin Herrenschmidt 	u64 base;
3314a43e69SBenjamin Herrenschmidt 	u64 entry;
3414a43e69SBenjamin Herrenschmidt } opal;
3514a43e69SBenjamin Herrenschmidt 
3614a43e69SBenjamin Herrenschmidt static struct device_node *opal_node;
3714a43e69SBenjamin Herrenschmidt static DEFINE_SPINLOCK(opal_write_lock);
38ed79ba9eSBenjamin Herrenschmidt extern u64 opal_mc_secondary_handler[];
3973ed148aSBenjamin Herrenschmidt static unsigned int *opal_irqs;
4073ed148aSBenjamin Herrenschmidt static unsigned int opal_irq_count;
411bc98de2SGavin Shan static ATOMIC_NOTIFIER_HEAD(opal_notifier_head);
421bc98de2SGavin Shan static DEFINE_SPINLOCK(opal_notifier_lock);
431bc98de2SGavin Shan static uint64_t last_notified_mask = 0x0ul;
441bc98de2SGavin Shan static atomic_t opal_notifier_hold = ATOMIC_INIT(0);
4514a43e69SBenjamin Herrenschmidt 
4614a43e69SBenjamin Herrenschmidt int __init early_init_dt_scan_opal(unsigned long node,
4714a43e69SBenjamin Herrenschmidt 				   const char *uname, int depth, void *data)
4814a43e69SBenjamin Herrenschmidt {
4914a43e69SBenjamin Herrenschmidt 	const void *basep, *entryp;
5014a43e69SBenjamin Herrenschmidt 	unsigned long basesz, entrysz;
5114a43e69SBenjamin Herrenschmidt 
5214a43e69SBenjamin Herrenschmidt 	if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
5314a43e69SBenjamin Herrenschmidt 		return 0;
5414a43e69SBenjamin Herrenschmidt 
5514a43e69SBenjamin Herrenschmidt 	basep  = of_get_flat_dt_prop(node, "opal-base-address", &basesz);
5614a43e69SBenjamin Herrenschmidt 	entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz);
5714a43e69SBenjamin Herrenschmidt 
5814a43e69SBenjamin Herrenschmidt 	if (!basep || !entryp)
5914a43e69SBenjamin Herrenschmidt 		return 1;
6014a43e69SBenjamin Herrenschmidt 
6114a43e69SBenjamin Herrenschmidt 	opal.base = of_read_number(basep, basesz/4);
6214a43e69SBenjamin Herrenschmidt 	opal.entry = of_read_number(entryp, entrysz/4);
6314a43e69SBenjamin Herrenschmidt 
6414a43e69SBenjamin Herrenschmidt 	pr_debug("OPAL Base  = 0x%llx (basep=%p basesz=%ld)\n",
6514a43e69SBenjamin Herrenschmidt 		 opal.base, basep, basesz);
6614a43e69SBenjamin Herrenschmidt 	pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
6714a43e69SBenjamin Herrenschmidt 		 opal.entry, entryp, entrysz);
6814a43e69SBenjamin Herrenschmidt 
6914a43e69SBenjamin Herrenschmidt 	powerpc_firmware_features |= FW_FEATURE_OPAL;
7075b93da4SBenjamin Herrenschmidt 	if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
7175b93da4SBenjamin Herrenschmidt 		powerpc_firmware_features |= FW_FEATURE_OPALv2;
7275b93da4SBenjamin Herrenschmidt 		powerpc_firmware_features |= FW_FEATURE_OPALv3;
7375b93da4SBenjamin Herrenschmidt 		printk("OPAL V3 detected !\n");
7475b93da4SBenjamin Herrenschmidt 	} else if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) {
7514a43e69SBenjamin Herrenschmidt 		powerpc_firmware_features |= FW_FEATURE_OPALv2;
7614a43e69SBenjamin Herrenschmidt 		printk("OPAL V2 detected !\n");
7714a43e69SBenjamin Herrenschmidt 	} else {
7814a43e69SBenjamin Herrenschmidt 		printk("OPAL V1 detected !\n");
7914a43e69SBenjamin Herrenschmidt 	}
8014a43e69SBenjamin Herrenschmidt 
81c4463b37SJeremy Kerr 	return 1;
82c4463b37SJeremy Kerr }
83c4463b37SJeremy Kerr 
84c4463b37SJeremy Kerr static int __init opal_register_exception_handlers(void)
85c4463b37SJeremy Kerr {
8629186097SBenjamin Herrenschmidt #ifdef __BIG_ENDIAN__
87c4463b37SJeremy Kerr 	u64 glue;
88c4463b37SJeremy Kerr 
89c4463b37SJeremy Kerr 	if (!(powerpc_firmware_features & FW_FEATURE_OPAL))
90c4463b37SJeremy Kerr 		return -ENODEV;
91c4463b37SJeremy Kerr 
92ed79ba9eSBenjamin Herrenschmidt 	/* Hookup some exception handlers. We use the fwnmi area at 0x7000
93ed79ba9eSBenjamin Herrenschmidt 	 * to provide the glue space to OPAL
94ed79ba9eSBenjamin Herrenschmidt 	 */
95ed79ba9eSBenjamin Herrenschmidt 	glue = 0x7000;
96ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER,
97ed79ba9eSBenjamin Herrenschmidt 					__pa(opal_mc_secondary_handler[0]),
98ed79ba9eSBenjamin Herrenschmidt 					glue);
99ed79ba9eSBenjamin Herrenschmidt 	glue += 128;
100ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
101ed79ba9eSBenjamin Herrenschmidt 					0, glue);
102ed79ba9eSBenjamin Herrenschmidt 	glue += 128;
103ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
10429186097SBenjamin Herrenschmidt #endif
105ed79ba9eSBenjamin Herrenschmidt 
106c4463b37SJeremy Kerr 	return 0;
10714a43e69SBenjamin Herrenschmidt }
10814a43e69SBenjamin Herrenschmidt 
109c4463b37SJeremy Kerr early_initcall(opal_register_exception_handlers);
110c4463b37SJeremy Kerr 
1111bc98de2SGavin Shan int opal_notifier_register(struct notifier_block *nb)
1121bc98de2SGavin Shan {
1131bc98de2SGavin Shan 	if (!nb) {
1141bc98de2SGavin Shan 		pr_warning("%s: Invalid argument (%p)\n",
1151bc98de2SGavin Shan 			   __func__, nb);
1161bc98de2SGavin Shan 		return -EINVAL;
1171bc98de2SGavin Shan 	}
1181bc98de2SGavin Shan 
1191bc98de2SGavin Shan 	atomic_notifier_chain_register(&opal_notifier_head, nb);
1201bc98de2SGavin Shan 	return 0;
1211bc98de2SGavin Shan }
1221bc98de2SGavin Shan 
1231bc98de2SGavin Shan static void opal_do_notifier(uint64_t events)
1241bc98de2SGavin Shan {
1251bc98de2SGavin Shan 	unsigned long flags;
1261bc98de2SGavin Shan 	uint64_t changed_mask;
1271bc98de2SGavin Shan 
1281bc98de2SGavin Shan 	if (atomic_read(&opal_notifier_hold))
1291bc98de2SGavin Shan 		return;
1301bc98de2SGavin Shan 
1311bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1321bc98de2SGavin Shan 	changed_mask = last_notified_mask ^ events;
1331bc98de2SGavin Shan 	last_notified_mask = events;
1341bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1351bc98de2SGavin Shan 
1361bc98de2SGavin Shan 	/*
1371bc98de2SGavin Shan 	 * We feed with the event bits and changed bits for
1381bc98de2SGavin Shan 	 * enough information to the callback.
1391bc98de2SGavin Shan 	 */
1401bc98de2SGavin Shan 	atomic_notifier_call_chain(&opal_notifier_head,
1411bc98de2SGavin Shan 				   events, (void *)changed_mask);
1421bc98de2SGavin Shan }
1431bc98de2SGavin Shan 
1441bc98de2SGavin Shan void opal_notifier_update_evt(uint64_t evt_mask,
1451bc98de2SGavin Shan 			      uint64_t evt_val)
1461bc98de2SGavin Shan {
1471bc98de2SGavin Shan 	unsigned long flags;
1481bc98de2SGavin Shan 
1491bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1501bc98de2SGavin Shan 	last_notified_mask &= ~evt_mask;
1511bc98de2SGavin Shan 	last_notified_mask |= evt_val;
1521bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1531bc98de2SGavin Shan }
1541bc98de2SGavin Shan 
1551bc98de2SGavin Shan void opal_notifier_enable(void)
1561bc98de2SGavin Shan {
1571bc98de2SGavin Shan 	int64_t rc;
1581bc98de2SGavin Shan 	uint64_t evt = 0;
1591bc98de2SGavin Shan 
1601bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 0);
1611bc98de2SGavin Shan 
1621bc98de2SGavin Shan 	/* Process pending events */
1631bc98de2SGavin Shan 	rc = opal_poll_events(&evt);
1641bc98de2SGavin Shan 	if (rc == OPAL_SUCCESS && evt)
1651bc98de2SGavin Shan 		opal_do_notifier(evt);
1661bc98de2SGavin Shan }
1671bc98de2SGavin Shan 
1681bc98de2SGavin Shan void opal_notifier_disable(void)
1691bc98de2SGavin Shan {
1701bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 1);
1711bc98de2SGavin Shan }
1721bc98de2SGavin Shan 
17314a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count)
17414a43e69SBenjamin Herrenschmidt {
1754f89363bSBenjamin Herrenschmidt 	s64 rc;
1764f89363bSBenjamin Herrenschmidt 	__be64 evt, len;
17714a43e69SBenjamin Herrenschmidt 
17814a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
179daea1175SBenjamin Herrenschmidt 		return -ENODEV;
18014a43e69SBenjamin Herrenschmidt 	opal_poll_events(&evt);
1814f89363bSBenjamin Herrenschmidt 	if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0)
18214a43e69SBenjamin Herrenschmidt 		return 0;
1834f89363bSBenjamin Herrenschmidt 	len = cpu_to_be64(count);
18414a43e69SBenjamin Herrenschmidt 	rc = opal_console_read(vtermno, &len, buf);
18514a43e69SBenjamin Herrenschmidt 	if (rc == OPAL_SUCCESS)
1864f89363bSBenjamin Herrenschmidt 		return be64_to_cpu(len);
18714a43e69SBenjamin Herrenschmidt 	return 0;
18814a43e69SBenjamin Herrenschmidt }
18914a43e69SBenjamin Herrenschmidt 
19014a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
19114a43e69SBenjamin Herrenschmidt {
19214a43e69SBenjamin Herrenschmidt 	int written = 0;
1934f89363bSBenjamin Herrenschmidt 	__be64 olen;
194daea1175SBenjamin Herrenschmidt 	s64 len, rc;
19514a43e69SBenjamin Herrenschmidt 	unsigned long flags;
1964f89363bSBenjamin Herrenschmidt 	__be64 evt;
19714a43e69SBenjamin Herrenschmidt 
19814a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
199daea1175SBenjamin Herrenschmidt 		return -ENODEV;
20014a43e69SBenjamin Herrenschmidt 
20114a43e69SBenjamin Herrenschmidt 	/* We want put_chars to be atomic to avoid mangling of hvsi
20214a43e69SBenjamin Herrenschmidt 	 * packets. To do that, we first test for room and return
203daea1175SBenjamin Herrenschmidt 	 * -EAGAIN if there isn't enough.
204daea1175SBenjamin Herrenschmidt 	 *
205daea1175SBenjamin Herrenschmidt 	 * Unfortunately, opal_console_write_buffer_space() doesn't
206daea1175SBenjamin Herrenschmidt 	 * appear to work on opal v1, so we just assume there is
207daea1175SBenjamin Herrenschmidt 	 * enough room and be done with it
20814a43e69SBenjamin Herrenschmidt 	 */
20914a43e69SBenjamin Herrenschmidt 	spin_lock_irqsave(&opal_write_lock, flags);
210daea1175SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2)) {
2114f89363bSBenjamin Herrenschmidt 		rc = opal_console_write_buffer_space(vtermno, &olen);
2124f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
21314a43e69SBenjamin Herrenschmidt 		if (rc || len < total_len) {
21414a43e69SBenjamin Herrenschmidt 			spin_unlock_irqrestore(&opal_write_lock, flags);
21514a43e69SBenjamin Herrenschmidt 			/* Closed -> drop characters */
21614a43e69SBenjamin Herrenschmidt 			if (rc)
21714a43e69SBenjamin Herrenschmidt 				return total_len;
2184f89363bSBenjamin Herrenschmidt 			opal_poll_events(NULL);
21914a43e69SBenjamin Herrenschmidt 			return -EAGAIN;
22014a43e69SBenjamin Herrenschmidt 		}
221daea1175SBenjamin Herrenschmidt 	}
22214a43e69SBenjamin Herrenschmidt 
22314a43e69SBenjamin Herrenschmidt 	/* We still try to handle partial completions, though they
22414a43e69SBenjamin Herrenschmidt 	 * should no longer happen.
22514a43e69SBenjamin Herrenschmidt 	 */
226daea1175SBenjamin Herrenschmidt 	rc = OPAL_BUSY;
22714a43e69SBenjamin Herrenschmidt 	while(total_len > 0 && (rc == OPAL_BUSY ||
22814a43e69SBenjamin Herrenschmidt 				rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
2294f89363bSBenjamin Herrenschmidt 		olen = cpu_to_be64(total_len);
2304f89363bSBenjamin Herrenschmidt 		rc = opal_console_write(vtermno, &olen, data);
2314f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
2321de1455fSBenjamin Herrenschmidt 
2331de1455fSBenjamin Herrenschmidt 		/* Closed or other error drop */
2341de1455fSBenjamin Herrenschmidt 		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
2351de1455fSBenjamin Herrenschmidt 		    rc != OPAL_BUSY_EVENT) {
2361de1455fSBenjamin Herrenschmidt 			written = total_len;
2371de1455fSBenjamin Herrenschmidt 			break;
2381de1455fSBenjamin Herrenschmidt 		}
23914a43e69SBenjamin Herrenschmidt 		if (rc == OPAL_SUCCESS) {
24014a43e69SBenjamin Herrenschmidt 			total_len -= len;
24114a43e69SBenjamin Herrenschmidt 			data += len;
24214a43e69SBenjamin Herrenschmidt 			written += len;
24314a43e69SBenjamin Herrenschmidt 		}
24414a43e69SBenjamin Herrenschmidt 		/* This is a bit nasty but we need that for the console to
24514a43e69SBenjamin Herrenschmidt 		 * flush when there aren't any interrupts. We will clean
24614a43e69SBenjamin Herrenschmidt 		 * things a bit later to limit that to synchronous path
24714a43e69SBenjamin Herrenschmidt 		 * such as the kernel console and xmon/udbg
24814a43e69SBenjamin Herrenschmidt 		 */
24914a43e69SBenjamin Herrenschmidt 		do
25014a43e69SBenjamin Herrenschmidt 			opal_poll_events(&evt);
2514f89363bSBenjamin Herrenschmidt 		while(rc == OPAL_SUCCESS &&
2524f89363bSBenjamin Herrenschmidt 			(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT));
25314a43e69SBenjamin Herrenschmidt 	}
25414a43e69SBenjamin Herrenschmidt 	spin_unlock_irqrestore(&opal_write_lock, flags);
25514a43e69SBenjamin Herrenschmidt 	return written;
25614a43e69SBenjamin Herrenschmidt }
25714a43e69SBenjamin Herrenschmidt 
258ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs)
259ed79ba9eSBenjamin Herrenschmidt {
26036df96f8SMahesh Salgaonkar 	struct machine_check_event evt;
261ed79ba9eSBenjamin Herrenschmidt 	const char *level, *sevstr, *subtype;
262ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_ue_types[] = {
263ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
264ed79ba9eSBenjamin Herrenschmidt 		"Instruction fetch",
265ed79ba9eSBenjamin Herrenschmidt 		"Page table walk ifetch",
266ed79ba9eSBenjamin Herrenschmidt 		"Load/Store",
267ed79ba9eSBenjamin Herrenschmidt 		"Page table walk Load/Store",
268ed79ba9eSBenjamin Herrenschmidt 	};
269ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_slb_types[] = {
270ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
271ed79ba9eSBenjamin Herrenschmidt 		"Parity",
272ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
273ed79ba9eSBenjamin Herrenschmidt 	};
274ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_erat_types[] = {
275ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
276ed79ba9eSBenjamin Herrenschmidt 		"Parity",
277ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
278ed79ba9eSBenjamin Herrenschmidt 	};
279ed79ba9eSBenjamin Herrenschmidt 	static const char *opal_mc_tlb_types[] = {
280ed79ba9eSBenjamin Herrenschmidt 		"Indeterminate",
281ed79ba9eSBenjamin Herrenschmidt 		"Parity",
282ed79ba9eSBenjamin Herrenschmidt 		"Multihit",
283ed79ba9eSBenjamin Herrenschmidt 	};
284ed79ba9eSBenjamin Herrenschmidt 
28536df96f8SMahesh Salgaonkar 	if (!get_mce_event(&evt, MCE_EVENT_RELEASE))
28636df96f8SMahesh Salgaonkar 		return 0;
287ed79ba9eSBenjamin Herrenschmidt 
288ed79ba9eSBenjamin Herrenschmidt 	/* Print things out */
28936df96f8SMahesh Salgaonkar 	if (evt.version != MCE_V1) {
290ed79ba9eSBenjamin Herrenschmidt 		pr_err("Machine Check Exception, Unknown event version %d !\n",
291ed79ba9eSBenjamin Herrenschmidt 		       evt.version);
292ed79ba9eSBenjamin Herrenschmidt 		return 0;
293ed79ba9eSBenjamin Herrenschmidt 	}
294ed79ba9eSBenjamin Herrenschmidt 	switch(evt.severity) {
29536df96f8SMahesh Salgaonkar 	case MCE_SEV_NO_ERROR:
296ed79ba9eSBenjamin Herrenschmidt 		level = KERN_INFO;
297ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Harmless";
298ed79ba9eSBenjamin Herrenschmidt 		break;
29936df96f8SMahesh Salgaonkar 	case MCE_SEV_WARNING:
300ed79ba9eSBenjamin Herrenschmidt 		level = KERN_WARNING;
301ed79ba9eSBenjamin Herrenschmidt 		sevstr = "";
302ed79ba9eSBenjamin Herrenschmidt 		break;
30336df96f8SMahesh Salgaonkar 	case MCE_SEV_ERROR_SYNC:
304ed79ba9eSBenjamin Herrenschmidt 		level = KERN_ERR;
305ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Severe";
306ed79ba9eSBenjamin Herrenschmidt 		break;
30736df96f8SMahesh Salgaonkar 	case MCE_SEV_FATAL:
308ed79ba9eSBenjamin Herrenschmidt 	default:
309ed79ba9eSBenjamin Herrenschmidt 		level = KERN_ERR;
310ed79ba9eSBenjamin Herrenschmidt 		sevstr = "Fatal";
311ed79ba9eSBenjamin Herrenschmidt 		break;
312ed79ba9eSBenjamin Herrenschmidt 	}
313ed79ba9eSBenjamin Herrenschmidt 
314ed79ba9eSBenjamin Herrenschmidt 	printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
31536df96f8SMahesh Salgaonkar 	       evt.disposition == MCE_DISPOSITION_RECOVERED ?
316ed79ba9eSBenjamin Herrenschmidt 	       "Recovered" : "[Not recovered");
317ed79ba9eSBenjamin Herrenschmidt 	printk("%s  Initiator: %s\n", level,
31836df96f8SMahesh Salgaonkar 	       evt.initiator == MCE_INITIATOR_CPU ? "CPU" : "Unknown");
319ed79ba9eSBenjamin Herrenschmidt 	switch(evt.error_type) {
32036df96f8SMahesh Salgaonkar 	case MCE_ERROR_TYPE_UE:
321ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.ue_error.ue_error_type <
322ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_ue_types) ?
323ed79ba9eSBenjamin Herrenschmidt 			opal_mc_ue_types[evt.u.ue_error.ue_error_type]
324ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
325ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: UE [%s]\n", level, subtype);
326ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.ue_error.effective_address_provided)
327ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
328ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.ue_error.effective_address);
329ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.ue_error.physical_address_provided)
330ed79ba9eSBenjamin Herrenschmidt 			printk("%s      Physial address: %016llx\n",
331ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.ue_error.physical_address);
332ed79ba9eSBenjamin Herrenschmidt 		break;
33336df96f8SMahesh Salgaonkar 	case MCE_ERROR_TYPE_SLB:
334ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.slb_error.slb_error_type <
335ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_slb_types) ?
336ed79ba9eSBenjamin Herrenschmidt 			opal_mc_slb_types[evt.u.slb_error.slb_error_type]
337ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
338ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: SLB [%s]\n", level, subtype);
339ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.slb_error.effective_address_provided)
340ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
341ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.slb_error.effective_address);
342ed79ba9eSBenjamin Herrenschmidt 		break;
34336df96f8SMahesh Salgaonkar 	case MCE_ERROR_TYPE_ERAT:
344ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.erat_error.erat_error_type <
345ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_erat_types) ?
346ed79ba9eSBenjamin Herrenschmidt 			opal_mc_erat_types[evt.u.erat_error.erat_error_type]
347ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
348ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: ERAT [%s]\n", level, subtype);
349ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.erat_error.effective_address_provided)
350ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
351ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.erat_error.effective_address);
352ed79ba9eSBenjamin Herrenschmidt 		break;
35336df96f8SMahesh Salgaonkar 	case MCE_ERROR_TYPE_TLB:
354ed79ba9eSBenjamin Herrenschmidt 		subtype = evt.u.tlb_error.tlb_error_type <
355ed79ba9eSBenjamin Herrenschmidt 			ARRAY_SIZE(opal_mc_tlb_types) ?
356ed79ba9eSBenjamin Herrenschmidt 			opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
357ed79ba9eSBenjamin Herrenschmidt 			: "Unknown";
358ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: TLB [%s]\n", level, subtype);
359ed79ba9eSBenjamin Herrenschmidt 		if (evt.u.tlb_error.effective_address_provided)
360ed79ba9eSBenjamin Herrenschmidt 			printk("%s    Effective address: %016llx\n",
361ed79ba9eSBenjamin Herrenschmidt 			       level, evt.u.tlb_error.effective_address);
362ed79ba9eSBenjamin Herrenschmidt 		break;
363ed79ba9eSBenjamin Herrenschmidt 	default:
36436df96f8SMahesh Salgaonkar 	case MCE_ERROR_TYPE_UNKNOWN:
365ed79ba9eSBenjamin Herrenschmidt 		printk("%s  Error type: Unknown\n", level);
366ed79ba9eSBenjamin Herrenschmidt 		break;
367ed79ba9eSBenjamin Herrenschmidt 	}
36836df96f8SMahesh Salgaonkar 	return evt.severity == MCE_SEV_FATAL ? 0 : 1;
369ed79ba9eSBenjamin Herrenschmidt }
370ed79ba9eSBenjamin Herrenschmidt 
371a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data)
372a125e092SBenjamin Herrenschmidt {
3735e4da530SAnton Blanchard 	__be64 events;
374a125e092SBenjamin Herrenschmidt 
375a125e092SBenjamin Herrenschmidt 	opal_handle_interrupt(virq_to_hw(irq), &events);
376a125e092SBenjamin Herrenschmidt 
3771bc98de2SGavin Shan 	opal_do_notifier(events);
378a125e092SBenjamin Herrenschmidt 
379a125e092SBenjamin Herrenschmidt 	return IRQ_HANDLED;
380a125e092SBenjamin Herrenschmidt }
381a125e092SBenjamin Herrenschmidt 
3826f68b5e2SVasant Hegde static int opal_sysfs_init(void)
3836f68b5e2SVasant Hegde {
3846f68b5e2SVasant Hegde 	opal_kobj = kobject_create_and_add("opal", firmware_kobj);
3856f68b5e2SVasant Hegde 	if (!opal_kobj) {
3866f68b5e2SVasant Hegde 		pr_warn("kobject_create_and_add opal failed\n");
3876f68b5e2SVasant Hegde 		return -ENOMEM;
3886f68b5e2SVasant Hegde 	}
3896f68b5e2SVasant Hegde 
3906f68b5e2SVasant Hegde 	return 0;
3916f68b5e2SVasant Hegde }
3926f68b5e2SVasant Hegde 
39314a43e69SBenjamin Herrenschmidt static int __init opal_init(void)
39414a43e69SBenjamin Herrenschmidt {
39514a43e69SBenjamin Herrenschmidt 	struct device_node *np, *consoles;
3961cc79bc8SAlistair Popple 	const __be32 *irqs;
397a125e092SBenjamin Herrenschmidt 	int rc, i, irqlen;
39814a43e69SBenjamin Herrenschmidt 
39914a43e69SBenjamin Herrenschmidt 	opal_node = of_find_node_by_path("/ibm,opal");
40014a43e69SBenjamin Herrenschmidt 	if (!opal_node) {
40114a43e69SBenjamin Herrenschmidt 		pr_warn("opal: Node not found\n");
40214a43e69SBenjamin Herrenschmidt 		return -ENODEV;
40314a43e69SBenjamin Herrenschmidt 	}
4042db29d28SBenjamin Herrenschmidt 
4052db29d28SBenjamin Herrenschmidt 	/* Register OPAL consoles if any ports */
40614a43e69SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2))
40714a43e69SBenjamin Herrenschmidt 		consoles = of_find_node_by_path("/ibm,opal/consoles");
40814a43e69SBenjamin Herrenschmidt 	else
40914a43e69SBenjamin Herrenschmidt 		consoles = of_node_get(opal_node);
4102db29d28SBenjamin Herrenschmidt 	if (consoles) {
41114a43e69SBenjamin Herrenschmidt 		for_each_child_of_node(consoles, np) {
41214a43e69SBenjamin Herrenschmidt 			if (strcmp(np->name, "serial"))
41314a43e69SBenjamin Herrenschmidt 				continue;
41414a43e69SBenjamin Herrenschmidt 			of_platform_device_create(np, NULL, NULL);
41514a43e69SBenjamin Herrenschmidt 		}
41614a43e69SBenjamin Herrenschmidt 		of_node_put(consoles);
4172db29d28SBenjamin Herrenschmidt 	}
418a125e092SBenjamin Herrenschmidt 
419a125e092SBenjamin Herrenschmidt 	/* Find all OPAL interrupts and request them */
420a125e092SBenjamin Herrenschmidt 	irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
421a125e092SBenjamin Herrenschmidt 	pr_debug("opal: Found %d interrupts reserved for OPAL\n",
422a125e092SBenjamin Herrenschmidt 		 irqs ? (irqlen / 4) : 0);
42373ed148aSBenjamin Herrenschmidt 	opal_irq_count = irqlen / 4;
42473ed148aSBenjamin Herrenschmidt 	opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
425a125e092SBenjamin Herrenschmidt 	for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
426a125e092SBenjamin Herrenschmidt 		unsigned int hwirq = be32_to_cpup(irqs);
427a125e092SBenjamin Herrenschmidt 		unsigned int irq = irq_create_mapping(NULL, hwirq);
428a125e092SBenjamin Herrenschmidt 		if (irq == NO_IRQ) {
429a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
430a125e092SBenjamin Herrenschmidt 			continue;
431a125e092SBenjamin Herrenschmidt 		}
432a125e092SBenjamin Herrenschmidt 		rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
433a125e092SBenjamin Herrenschmidt 		if (rc)
434a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Error %d requesting irq %d"
435a125e092SBenjamin Herrenschmidt 				   " (0x%x)\n", rc, irq, hwirq);
43673ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = irq;
437a125e092SBenjamin Herrenschmidt 	}
4386f68b5e2SVasant Hegde 
4396f68b5e2SVasant Hegde 	/* Create "opal" kobject under /sys/firmware */
4406f68b5e2SVasant Hegde 	rc = opal_sysfs_init();
44150bd6153SVasant Hegde 	if (rc == 0) {
44250bd6153SVasant Hegde 		/* Setup code update interface */
44350bd6153SVasant Hegde 		opal_flash_init();
44450bd6153SVasant Hegde 	}
4456f68b5e2SVasant Hegde 
44614a43e69SBenjamin Herrenschmidt 	return 0;
44714a43e69SBenjamin Herrenschmidt }
44814a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init);
44973ed148aSBenjamin Herrenschmidt 
45073ed148aSBenjamin Herrenschmidt void opal_shutdown(void)
45173ed148aSBenjamin Herrenschmidt {
45273ed148aSBenjamin Herrenschmidt 	unsigned int i;
45373ed148aSBenjamin Herrenschmidt 
45473ed148aSBenjamin Herrenschmidt 	for (i = 0; i < opal_irq_count; i++) {
45573ed148aSBenjamin Herrenschmidt 		if (opal_irqs[i])
456b0d436c7SAnton Blanchard 			free_irq(opal_irqs[i], NULL);
45773ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = 0;
45873ed148aSBenjamin Herrenschmidt 	}
45973ed148aSBenjamin Herrenschmidt }
460