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>
2314a43e69SBenjamin Herrenschmidt #include <asm/opal.h>
2414a43e69SBenjamin Herrenschmidt #include <asm/firmware.h>
2536df96f8SMahesh Salgaonkar #include <asm/mce.h>
2614a43e69SBenjamin Herrenschmidt 
2714a43e69SBenjamin Herrenschmidt #include "powernv.h"
2814a43e69SBenjamin Herrenschmidt 
296f68b5e2SVasant Hegde /* /sys/firmware/opal */
306f68b5e2SVasant Hegde struct kobject *opal_kobj;
316f68b5e2SVasant Hegde 
3214a43e69SBenjamin Herrenschmidt struct opal {
3314a43e69SBenjamin Herrenschmidt 	u64 base;
3414a43e69SBenjamin Herrenschmidt 	u64 entry;
3514a43e69SBenjamin Herrenschmidt } opal;
3614a43e69SBenjamin Herrenschmidt 
3714a43e69SBenjamin Herrenschmidt static struct device_node *opal_node;
3814a43e69SBenjamin Herrenschmidt static DEFINE_SPINLOCK(opal_write_lock);
39ed79ba9eSBenjamin Herrenschmidt extern u64 opal_mc_secondary_handler[];
4073ed148aSBenjamin Herrenschmidt static unsigned int *opal_irqs;
4173ed148aSBenjamin Herrenschmidt static unsigned int opal_irq_count;
421bc98de2SGavin Shan static ATOMIC_NOTIFIER_HEAD(opal_notifier_head);
431bc98de2SGavin Shan static DEFINE_SPINLOCK(opal_notifier_lock);
441bc98de2SGavin Shan static uint64_t last_notified_mask = 0x0ul;
451bc98de2SGavin Shan static atomic_t opal_notifier_hold = ATOMIC_INIT(0);
4614a43e69SBenjamin Herrenschmidt 
4714a43e69SBenjamin Herrenschmidt int __init early_init_dt_scan_opal(unsigned long node,
4814a43e69SBenjamin Herrenschmidt 				   const char *uname, int depth, void *data)
4914a43e69SBenjamin Herrenschmidt {
5014a43e69SBenjamin Herrenschmidt 	const void *basep, *entryp;
5114a43e69SBenjamin Herrenschmidt 	unsigned long basesz, entrysz;
5214a43e69SBenjamin Herrenschmidt 
5314a43e69SBenjamin Herrenschmidt 	if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
5414a43e69SBenjamin Herrenschmidt 		return 0;
5514a43e69SBenjamin Herrenschmidt 
5614a43e69SBenjamin Herrenschmidt 	basep  = of_get_flat_dt_prop(node, "opal-base-address", &basesz);
5714a43e69SBenjamin Herrenschmidt 	entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz);
5814a43e69SBenjamin Herrenschmidt 
5914a43e69SBenjamin Herrenschmidt 	if (!basep || !entryp)
6014a43e69SBenjamin Herrenschmidt 		return 1;
6114a43e69SBenjamin Herrenschmidt 
6214a43e69SBenjamin Herrenschmidt 	opal.base = of_read_number(basep, basesz/4);
6314a43e69SBenjamin Herrenschmidt 	opal.entry = of_read_number(entryp, entrysz/4);
6414a43e69SBenjamin Herrenschmidt 
6514a43e69SBenjamin Herrenschmidt 	pr_debug("OPAL Base  = 0x%llx (basep=%p basesz=%ld)\n",
6614a43e69SBenjamin Herrenschmidt 		 opal.base, basep, basesz);
6714a43e69SBenjamin Herrenschmidt 	pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
6814a43e69SBenjamin Herrenschmidt 		 opal.entry, entryp, entrysz);
6914a43e69SBenjamin Herrenschmidt 
7014a43e69SBenjamin Herrenschmidt 	powerpc_firmware_features |= FW_FEATURE_OPAL;
7175b93da4SBenjamin Herrenschmidt 	if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
7275b93da4SBenjamin Herrenschmidt 		powerpc_firmware_features |= FW_FEATURE_OPALv2;
7375b93da4SBenjamin Herrenschmidt 		powerpc_firmware_features |= FW_FEATURE_OPALv3;
7475b93da4SBenjamin Herrenschmidt 		printk("OPAL V3 detected !\n");
7575b93da4SBenjamin Herrenschmidt 	} else if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) {
7614a43e69SBenjamin Herrenschmidt 		powerpc_firmware_features |= FW_FEATURE_OPALv2;
7714a43e69SBenjamin Herrenschmidt 		printk("OPAL V2 detected !\n");
7814a43e69SBenjamin Herrenschmidt 	} else {
7914a43e69SBenjamin Herrenschmidt 		printk("OPAL V1 detected !\n");
8014a43e69SBenjamin Herrenschmidt 	}
8114a43e69SBenjamin Herrenschmidt 
82c4463b37SJeremy Kerr 	return 1;
83c4463b37SJeremy Kerr }
84c4463b37SJeremy Kerr 
85c4463b37SJeremy Kerr static int __init opal_register_exception_handlers(void)
86c4463b37SJeremy Kerr {
8729186097SBenjamin Herrenschmidt #ifdef __BIG_ENDIAN__
88c4463b37SJeremy Kerr 	u64 glue;
89c4463b37SJeremy Kerr 
90c4463b37SJeremy Kerr 	if (!(powerpc_firmware_features & FW_FEATURE_OPAL))
91c4463b37SJeremy Kerr 		return -ENODEV;
92c4463b37SJeremy Kerr 
9328446de2SMahesh Salgaonkar 	/* Hookup some exception handlers except machine check. We use the
9428446de2SMahesh Salgaonkar 	 * fwnmi area at 0x7000 to provide the glue space to OPAL
95ed79ba9eSBenjamin Herrenschmidt 	 */
96ed79ba9eSBenjamin Herrenschmidt 	glue = 0x7000;
97ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
98ed79ba9eSBenjamin Herrenschmidt 					0, glue);
99ed79ba9eSBenjamin Herrenschmidt 	glue += 128;
100ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
10129186097SBenjamin Herrenschmidt #endif
102ed79ba9eSBenjamin Herrenschmidt 
103c4463b37SJeremy Kerr 	return 0;
10414a43e69SBenjamin Herrenschmidt }
10514a43e69SBenjamin Herrenschmidt 
106c4463b37SJeremy Kerr early_initcall(opal_register_exception_handlers);
107c4463b37SJeremy Kerr 
1081bc98de2SGavin Shan int opal_notifier_register(struct notifier_block *nb)
1091bc98de2SGavin Shan {
1101bc98de2SGavin Shan 	if (!nb) {
1111bc98de2SGavin Shan 		pr_warning("%s: Invalid argument (%p)\n",
1121bc98de2SGavin Shan 			   __func__, nb);
1131bc98de2SGavin Shan 		return -EINVAL;
1141bc98de2SGavin Shan 	}
1151bc98de2SGavin Shan 
1161bc98de2SGavin Shan 	atomic_notifier_chain_register(&opal_notifier_head, nb);
1171bc98de2SGavin Shan 	return 0;
1181bc98de2SGavin Shan }
1191bc98de2SGavin Shan 
1201bc98de2SGavin Shan static void opal_do_notifier(uint64_t events)
1211bc98de2SGavin Shan {
1221bc98de2SGavin Shan 	unsigned long flags;
1231bc98de2SGavin Shan 	uint64_t changed_mask;
1241bc98de2SGavin Shan 
1251bc98de2SGavin Shan 	if (atomic_read(&opal_notifier_hold))
1261bc98de2SGavin Shan 		return;
1271bc98de2SGavin Shan 
1281bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1291bc98de2SGavin Shan 	changed_mask = last_notified_mask ^ events;
1301bc98de2SGavin Shan 	last_notified_mask = events;
1311bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1321bc98de2SGavin Shan 
1331bc98de2SGavin Shan 	/*
1341bc98de2SGavin Shan 	 * We feed with the event bits and changed bits for
1351bc98de2SGavin Shan 	 * enough information to the callback.
1361bc98de2SGavin Shan 	 */
1371bc98de2SGavin Shan 	atomic_notifier_call_chain(&opal_notifier_head,
1381bc98de2SGavin Shan 				   events, (void *)changed_mask);
1391bc98de2SGavin Shan }
1401bc98de2SGavin Shan 
1411bc98de2SGavin Shan void opal_notifier_update_evt(uint64_t evt_mask,
1421bc98de2SGavin Shan 			      uint64_t evt_val)
1431bc98de2SGavin Shan {
1441bc98de2SGavin Shan 	unsigned long flags;
1451bc98de2SGavin Shan 
1461bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1471bc98de2SGavin Shan 	last_notified_mask &= ~evt_mask;
1481bc98de2SGavin Shan 	last_notified_mask |= evt_val;
1491bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1501bc98de2SGavin Shan }
1511bc98de2SGavin Shan 
1521bc98de2SGavin Shan void opal_notifier_enable(void)
1531bc98de2SGavin Shan {
1541bc98de2SGavin Shan 	int64_t rc;
1551bc98de2SGavin Shan 	uint64_t evt = 0;
1561bc98de2SGavin Shan 
1571bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 0);
1581bc98de2SGavin Shan 
1591bc98de2SGavin Shan 	/* Process pending events */
1601bc98de2SGavin Shan 	rc = opal_poll_events(&evt);
1611bc98de2SGavin Shan 	if (rc == OPAL_SUCCESS && evt)
1621bc98de2SGavin Shan 		opal_do_notifier(evt);
1631bc98de2SGavin Shan }
1641bc98de2SGavin Shan 
1651bc98de2SGavin Shan void opal_notifier_disable(void)
1661bc98de2SGavin Shan {
1671bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 1);
1681bc98de2SGavin Shan }
1691bc98de2SGavin Shan 
17014a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count)
17114a43e69SBenjamin Herrenschmidt {
1724f89363bSBenjamin Herrenschmidt 	s64 rc;
1734f89363bSBenjamin Herrenschmidt 	__be64 evt, len;
17414a43e69SBenjamin Herrenschmidt 
17514a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
176daea1175SBenjamin Herrenschmidt 		return -ENODEV;
17714a43e69SBenjamin Herrenschmidt 	opal_poll_events(&evt);
1784f89363bSBenjamin Herrenschmidt 	if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0)
17914a43e69SBenjamin Herrenschmidt 		return 0;
1804f89363bSBenjamin Herrenschmidt 	len = cpu_to_be64(count);
18114a43e69SBenjamin Herrenschmidt 	rc = opal_console_read(vtermno, &len, buf);
18214a43e69SBenjamin Herrenschmidt 	if (rc == OPAL_SUCCESS)
1834f89363bSBenjamin Herrenschmidt 		return be64_to_cpu(len);
18414a43e69SBenjamin Herrenschmidt 	return 0;
18514a43e69SBenjamin Herrenschmidt }
18614a43e69SBenjamin Herrenschmidt 
18714a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
18814a43e69SBenjamin Herrenschmidt {
18914a43e69SBenjamin Herrenschmidt 	int written = 0;
1904f89363bSBenjamin Herrenschmidt 	__be64 olen;
191daea1175SBenjamin Herrenschmidt 	s64 len, rc;
19214a43e69SBenjamin Herrenschmidt 	unsigned long flags;
1934f89363bSBenjamin Herrenschmidt 	__be64 evt;
19414a43e69SBenjamin Herrenschmidt 
19514a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
196daea1175SBenjamin Herrenschmidt 		return -ENODEV;
19714a43e69SBenjamin Herrenschmidt 
19814a43e69SBenjamin Herrenschmidt 	/* We want put_chars to be atomic to avoid mangling of hvsi
19914a43e69SBenjamin Herrenschmidt 	 * packets. To do that, we first test for room and return
200daea1175SBenjamin Herrenschmidt 	 * -EAGAIN if there isn't enough.
201daea1175SBenjamin Herrenschmidt 	 *
202daea1175SBenjamin Herrenschmidt 	 * Unfortunately, opal_console_write_buffer_space() doesn't
203daea1175SBenjamin Herrenschmidt 	 * appear to work on opal v1, so we just assume there is
204daea1175SBenjamin Herrenschmidt 	 * enough room and be done with it
20514a43e69SBenjamin Herrenschmidt 	 */
20614a43e69SBenjamin Herrenschmidt 	spin_lock_irqsave(&opal_write_lock, flags);
207daea1175SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2)) {
2084f89363bSBenjamin Herrenschmidt 		rc = opal_console_write_buffer_space(vtermno, &olen);
2094f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
21014a43e69SBenjamin Herrenschmidt 		if (rc || len < total_len) {
21114a43e69SBenjamin Herrenschmidt 			spin_unlock_irqrestore(&opal_write_lock, flags);
21214a43e69SBenjamin Herrenschmidt 			/* Closed -> drop characters */
21314a43e69SBenjamin Herrenschmidt 			if (rc)
21414a43e69SBenjamin Herrenschmidt 				return total_len;
2154f89363bSBenjamin Herrenschmidt 			opal_poll_events(NULL);
21614a43e69SBenjamin Herrenschmidt 			return -EAGAIN;
21714a43e69SBenjamin Herrenschmidt 		}
218daea1175SBenjamin Herrenschmidt 	}
21914a43e69SBenjamin Herrenschmidt 
22014a43e69SBenjamin Herrenschmidt 	/* We still try to handle partial completions, though they
22114a43e69SBenjamin Herrenschmidt 	 * should no longer happen.
22214a43e69SBenjamin Herrenschmidt 	 */
223daea1175SBenjamin Herrenschmidt 	rc = OPAL_BUSY;
22414a43e69SBenjamin Herrenschmidt 	while(total_len > 0 && (rc == OPAL_BUSY ||
22514a43e69SBenjamin Herrenschmidt 				rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
2264f89363bSBenjamin Herrenschmidt 		olen = cpu_to_be64(total_len);
2274f89363bSBenjamin Herrenschmidt 		rc = opal_console_write(vtermno, &olen, data);
2284f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
2291de1455fSBenjamin Herrenschmidt 
2301de1455fSBenjamin Herrenschmidt 		/* Closed or other error drop */
2311de1455fSBenjamin Herrenschmidt 		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
2321de1455fSBenjamin Herrenschmidt 		    rc != OPAL_BUSY_EVENT) {
2331de1455fSBenjamin Herrenschmidt 			written = total_len;
2341de1455fSBenjamin Herrenschmidt 			break;
2351de1455fSBenjamin Herrenschmidt 		}
23614a43e69SBenjamin Herrenschmidt 		if (rc == OPAL_SUCCESS) {
23714a43e69SBenjamin Herrenschmidt 			total_len -= len;
23814a43e69SBenjamin Herrenschmidt 			data += len;
23914a43e69SBenjamin Herrenschmidt 			written += len;
24014a43e69SBenjamin Herrenschmidt 		}
24114a43e69SBenjamin Herrenschmidt 		/* This is a bit nasty but we need that for the console to
24214a43e69SBenjamin Herrenschmidt 		 * flush when there aren't any interrupts. We will clean
24314a43e69SBenjamin Herrenschmidt 		 * things a bit later to limit that to synchronous path
24414a43e69SBenjamin Herrenschmidt 		 * such as the kernel console and xmon/udbg
24514a43e69SBenjamin Herrenschmidt 		 */
24614a43e69SBenjamin Herrenschmidt 		do
24714a43e69SBenjamin Herrenschmidt 			opal_poll_events(&evt);
2484f89363bSBenjamin Herrenschmidt 		while(rc == OPAL_SUCCESS &&
2494f89363bSBenjamin Herrenschmidt 			(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT));
25014a43e69SBenjamin Herrenschmidt 	}
25114a43e69SBenjamin Herrenschmidt 	spin_unlock_irqrestore(&opal_write_lock, flags);
25214a43e69SBenjamin Herrenschmidt 	return written;
25314a43e69SBenjamin Herrenschmidt }
25414a43e69SBenjamin Herrenschmidt 
255b63a0ffeSMahesh Salgaonkar static int opal_recover_mce(struct pt_regs *regs,
256b63a0ffeSMahesh Salgaonkar 					struct machine_check_event *evt)
257b63a0ffeSMahesh Salgaonkar {
258b63a0ffeSMahesh Salgaonkar 	int recovered = 0;
259b63a0ffeSMahesh Salgaonkar 	uint64_t ea = get_mce_fault_addr(evt);
260b63a0ffeSMahesh Salgaonkar 
261b63a0ffeSMahesh Salgaonkar 	if (!(regs->msr & MSR_RI)) {
262b63a0ffeSMahesh Salgaonkar 		/* If MSR_RI isn't set, we cannot recover */
263b63a0ffeSMahesh Salgaonkar 		recovered = 0;
264b63a0ffeSMahesh Salgaonkar 	} else if (evt->disposition == MCE_DISPOSITION_RECOVERED) {
265b63a0ffeSMahesh Salgaonkar 		/* Platform corrected itself */
266b63a0ffeSMahesh Salgaonkar 		recovered = 1;
267b63a0ffeSMahesh Salgaonkar 	} else if (ea && !is_kernel_addr(ea)) {
268b63a0ffeSMahesh Salgaonkar 		/*
269b63a0ffeSMahesh Salgaonkar 		 * Faulting address is not in kernel text. We should be fine.
270b63a0ffeSMahesh Salgaonkar 		 * We need to find which process uses this address.
271b63a0ffeSMahesh Salgaonkar 		 * For now, kill the task if we have received exception when
272b63a0ffeSMahesh Salgaonkar 		 * in userspace.
273b63a0ffeSMahesh Salgaonkar 		 *
274b63a0ffeSMahesh Salgaonkar 		 * TODO: Queue up this address for hwpoisioning later.
275b63a0ffeSMahesh Salgaonkar 		 */
276b63a0ffeSMahesh Salgaonkar 		if (user_mode(regs) && !is_global_init(current)) {
277b63a0ffeSMahesh Salgaonkar 			_exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip);
278b63a0ffeSMahesh Salgaonkar 			recovered = 1;
279b63a0ffeSMahesh Salgaonkar 		} else
280b63a0ffeSMahesh Salgaonkar 			recovered = 0;
281b63a0ffeSMahesh Salgaonkar 	} else if (user_mode(regs) && !is_global_init(current) &&
282b63a0ffeSMahesh Salgaonkar 		evt->severity == MCE_SEV_ERROR_SYNC) {
283b63a0ffeSMahesh Salgaonkar 		/*
284b63a0ffeSMahesh Salgaonkar 		 * If we have received a synchronous error when in userspace
285b63a0ffeSMahesh Salgaonkar 		 * kill the task.
286b63a0ffeSMahesh Salgaonkar 		 */
287b63a0ffeSMahesh Salgaonkar 		_exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip);
288b63a0ffeSMahesh Salgaonkar 		recovered = 1;
289b63a0ffeSMahesh Salgaonkar 	}
290b63a0ffeSMahesh Salgaonkar 	return recovered;
291b63a0ffeSMahesh Salgaonkar }
292b63a0ffeSMahesh Salgaonkar 
293ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs)
294ed79ba9eSBenjamin Herrenschmidt {
29536df96f8SMahesh Salgaonkar 	struct machine_check_event evt;
296ed79ba9eSBenjamin Herrenschmidt 
29736df96f8SMahesh Salgaonkar 	if (!get_mce_event(&evt, MCE_EVENT_RELEASE))
29836df96f8SMahesh Salgaonkar 		return 0;
299ed79ba9eSBenjamin Herrenschmidt 
300ed79ba9eSBenjamin Herrenschmidt 	/* Print things out */
30136df96f8SMahesh Salgaonkar 	if (evt.version != MCE_V1) {
302ed79ba9eSBenjamin Herrenschmidt 		pr_err("Machine Check Exception, Unknown event version %d !\n",
303ed79ba9eSBenjamin Herrenschmidt 		       evt.version);
304ed79ba9eSBenjamin Herrenschmidt 		return 0;
305ed79ba9eSBenjamin Herrenschmidt 	}
306b5ff4211SMahesh Salgaonkar 	machine_check_print_event_info(&evt);
307ed79ba9eSBenjamin Herrenschmidt 
308b63a0ffeSMahesh Salgaonkar 	if (opal_recover_mce(regs, &evt))
309b63a0ffeSMahesh Salgaonkar 		return 1;
310b63a0ffeSMahesh Salgaonkar 	return 0;
311ed79ba9eSBenjamin Herrenschmidt }
312ed79ba9eSBenjamin Herrenschmidt 
313a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data)
314a125e092SBenjamin Herrenschmidt {
3155e4da530SAnton Blanchard 	__be64 events;
316a125e092SBenjamin Herrenschmidt 
317a125e092SBenjamin Herrenschmidt 	opal_handle_interrupt(virq_to_hw(irq), &events);
318a125e092SBenjamin Herrenschmidt 
3191bc98de2SGavin Shan 	opal_do_notifier(events);
320a125e092SBenjamin Herrenschmidt 
321a125e092SBenjamin Herrenschmidt 	return IRQ_HANDLED;
322a125e092SBenjamin Herrenschmidt }
323a125e092SBenjamin Herrenschmidt 
3246f68b5e2SVasant Hegde static int opal_sysfs_init(void)
3256f68b5e2SVasant Hegde {
3266f68b5e2SVasant Hegde 	opal_kobj = kobject_create_and_add("opal", firmware_kobj);
3276f68b5e2SVasant Hegde 	if (!opal_kobj) {
3286f68b5e2SVasant Hegde 		pr_warn("kobject_create_and_add opal failed\n");
3296f68b5e2SVasant Hegde 		return -ENOMEM;
3306f68b5e2SVasant Hegde 	}
3316f68b5e2SVasant Hegde 
3326f68b5e2SVasant Hegde 	return 0;
3336f68b5e2SVasant Hegde }
3346f68b5e2SVasant Hegde 
33514a43e69SBenjamin Herrenschmidt static int __init opal_init(void)
33614a43e69SBenjamin Herrenschmidt {
33714a43e69SBenjamin Herrenschmidt 	struct device_node *np, *consoles;
3381cc79bc8SAlistair Popple 	const __be32 *irqs;
339a125e092SBenjamin Herrenschmidt 	int rc, i, irqlen;
34014a43e69SBenjamin Herrenschmidt 
34114a43e69SBenjamin Herrenschmidt 	opal_node = of_find_node_by_path("/ibm,opal");
34214a43e69SBenjamin Herrenschmidt 	if (!opal_node) {
34314a43e69SBenjamin Herrenschmidt 		pr_warn("opal: Node not found\n");
34414a43e69SBenjamin Herrenschmidt 		return -ENODEV;
34514a43e69SBenjamin Herrenschmidt 	}
3462db29d28SBenjamin Herrenschmidt 
3472db29d28SBenjamin Herrenschmidt 	/* Register OPAL consoles if any ports */
34814a43e69SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2))
34914a43e69SBenjamin Herrenschmidt 		consoles = of_find_node_by_path("/ibm,opal/consoles");
35014a43e69SBenjamin Herrenschmidt 	else
35114a43e69SBenjamin Herrenschmidt 		consoles = of_node_get(opal_node);
3522db29d28SBenjamin Herrenschmidt 	if (consoles) {
35314a43e69SBenjamin Herrenschmidt 		for_each_child_of_node(consoles, np) {
35414a43e69SBenjamin Herrenschmidt 			if (strcmp(np->name, "serial"))
35514a43e69SBenjamin Herrenschmidt 				continue;
35614a43e69SBenjamin Herrenschmidt 			of_platform_device_create(np, NULL, NULL);
35714a43e69SBenjamin Herrenschmidt 		}
35814a43e69SBenjamin Herrenschmidt 		of_node_put(consoles);
3592db29d28SBenjamin Herrenschmidt 	}
360a125e092SBenjamin Herrenschmidt 
361a125e092SBenjamin Herrenschmidt 	/* Find all OPAL interrupts and request them */
362a125e092SBenjamin Herrenschmidt 	irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
363a125e092SBenjamin Herrenschmidt 	pr_debug("opal: Found %d interrupts reserved for OPAL\n",
364a125e092SBenjamin Herrenschmidt 		 irqs ? (irqlen / 4) : 0);
36573ed148aSBenjamin Herrenschmidt 	opal_irq_count = irqlen / 4;
36673ed148aSBenjamin Herrenschmidt 	opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
367a125e092SBenjamin Herrenschmidt 	for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
368a125e092SBenjamin Herrenschmidt 		unsigned int hwirq = be32_to_cpup(irqs);
369a125e092SBenjamin Herrenschmidt 		unsigned int irq = irq_create_mapping(NULL, hwirq);
370a125e092SBenjamin Herrenschmidt 		if (irq == NO_IRQ) {
371a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
372a125e092SBenjamin Herrenschmidt 			continue;
373a125e092SBenjamin Herrenschmidt 		}
374a125e092SBenjamin Herrenschmidt 		rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
375a125e092SBenjamin Herrenschmidt 		if (rc)
376a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Error %d requesting irq %d"
377a125e092SBenjamin Herrenschmidt 				   " (0x%x)\n", rc, irq, hwirq);
37873ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = irq;
379a125e092SBenjamin Herrenschmidt 	}
3806f68b5e2SVasant Hegde 
3816f68b5e2SVasant Hegde 	/* Create "opal" kobject under /sys/firmware */
3826f68b5e2SVasant Hegde 	rc = opal_sysfs_init();
38350bd6153SVasant Hegde 	if (rc == 0) {
38450bd6153SVasant Hegde 		/* Setup code update interface */
38550bd6153SVasant Hegde 		opal_flash_init();
38650bd6153SVasant Hegde 	}
3876f68b5e2SVasant Hegde 
38814a43e69SBenjamin Herrenschmidt 	return 0;
38914a43e69SBenjamin Herrenschmidt }
39014a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init);
39173ed148aSBenjamin Herrenschmidt 
39273ed148aSBenjamin Herrenschmidt void opal_shutdown(void)
39373ed148aSBenjamin Herrenschmidt {
39473ed148aSBenjamin Herrenschmidt 	unsigned int i;
39573ed148aSBenjamin Herrenschmidt 
39673ed148aSBenjamin Herrenschmidt 	for (i = 0; i < opal_irq_count; i++) {
39773ed148aSBenjamin Herrenschmidt 		if (opal_irqs[i])
398b0d436c7SAnton Blanchard 			free_irq(opal_irqs[i], NULL);
39973ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = 0;
40073ed148aSBenjamin Herrenschmidt 	}
40173ed148aSBenjamin Herrenschmidt }
402