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 
9228446de2SMahesh Salgaonkar 	/* Hookup some exception handlers except machine check. We use the
9328446de2SMahesh Salgaonkar 	 * fwnmi area at 0x7000 to provide the glue space to OPAL
94ed79ba9eSBenjamin Herrenschmidt 	 */
95ed79ba9eSBenjamin Herrenschmidt 	glue = 0x7000;
96ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
97ed79ba9eSBenjamin Herrenschmidt 					0, glue);
98ed79ba9eSBenjamin Herrenschmidt 	glue += 128;
99ed79ba9eSBenjamin Herrenschmidt 	opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
10029186097SBenjamin Herrenschmidt #endif
101ed79ba9eSBenjamin Herrenschmidt 
102c4463b37SJeremy Kerr 	return 0;
10314a43e69SBenjamin Herrenschmidt }
10414a43e69SBenjamin Herrenschmidt 
105c4463b37SJeremy Kerr early_initcall(opal_register_exception_handlers);
106c4463b37SJeremy Kerr 
1071bc98de2SGavin Shan int opal_notifier_register(struct notifier_block *nb)
1081bc98de2SGavin Shan {
1091bc98de2SGavin Shan 	if (!nb) {
1101bc98de2SGavin Shan 		pr_warning("%s: Invalid argument (%p)\n",
1111bc98de2SGavin Shan 			   __func__, nb);
1121bc98de2SGavin Shan 		return -EINVAL;
1131bc98de2SGavin Shan 	}
1141bc98de2SGavin Shan 
1151bc98de2SGavin Shan 	atomic_notifier_chain_register(&opal_notifier_head, nb);
1161bc98de2SGavin Shan 	return 0;
1171bc98de2SGavin Shan }
1181bc98de2SGavin Shan 
1191bc98de2SGavin Shan static void opal_do_notifier(uint64_t events)
1201bc98de2SGavin Shan {
1211bc98de2SGavin Shan 	unsigned long flags;
1221bc98de2SGavin Shan 	uint64_t changed_mask;
1231bc98de2SGavin Shan 
1241bc98de2SGavin Shan 	if (atomic_read(&opal_notifier_hold))
1251bc98de2SGavin Shan 		return;
1261bc98de2SGavin Shan 
1271bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1281bc98de2SGavin Shan 	changed_mask = last_notified_mask ^ events;
1291bc98de2SGavin Shan 	last_notified_mask = events;
1301bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1311bc98de2SGavin Shan 
1321bc98de2SGavin Shan 	/*
1331bc98de2SGavin Shan 	 * We feed with the event bits and changed bits for
1341bc98de2SGavin Shan 	 * enough information to the callback.
1351bc98de2SGavin Shan 	 */
1361bc98de2SGavin Shan 	atomic_notifier_call_chain(&opal_notifier_head,
1371bc98de2SGavin Shan 				   events, (void *)changed_mask);
1381bc98de2SGavin Shan }
1391bc98de2SGavin Shan 
1401bc98de2SGavin Shan void opal_notifier_update_evt(uint64_t evt_mask,
1411bc98de2SGavin Shan 			      uint64_t evt_val)
1421bc98de2SGavin Shan {
1431bc98de2SGavin Shan 	unsigned long flags;
1441bc98de2SGavin Shan 
1451bc98de2SGavin Shan 	spin_lock_irqsave(&opal_notifier_lock, flags);
1461bc98de2SGavin Shan 	last_notified_mask &= ~evt_mask;
1471bc98de2SGavin Shan 	last_notified_mask |= evt_val;
1481bc98de2SGavin Shan 	spin_unlock_irqrestore(&opal_notifier_lock, flags);
1491bc98de2SGavin Shan }
1501bc98de2SGavin Shan 
1511bc98de2SGavin Shan void opal_notifier_enable(void)
1521bc98de2SGavin Shan {
1531bc98de2SGavin Shan 	int64_t rc;
1541bc98de2SGavin Shan 	uint64_t evt = 0;
1551bc98de2SGavin Shan 
1561bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 0);
1571bc98de2SGavin Shan 
1581bc98de2SGavin Shan 	/* Process pending events */
1591bc98de2SGavin Shan 	rc = opal_poll_events(&evt);
1601bc98de2SGavin Shan 	if (rc == OPAL_SUCCESS && evt)
1611bc98de2SGavin Shan 		opal_do_notifier(evt);
1621bc98de2SGavin Shan }
1631bc98de2SGavin Shan 
1641bc98de2SGavin Shan void opal_notifier_disable(void)
1651bc98de2SGavin Shan {
1661bc98de2SGavin Shan 	atomic_set(&opal_notifier_hold, 1);
1671bc98de2SGavin Shan }
1681bc98de2SGavin Shan 
16914a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count)
17014a43e69SBenjamin Herrenschmidt {
1714f89363bSBenjamin Herrenschmidt 	s64 rc;
1724f89363bSBenjamin Herrenschmidt 	__be64 evt, len;
17314a43e69SBenjamin Herrenschmidt 
17414a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
175daea1175SBenjamin Herrenschmidt 		return -ENODEV;
17614a43e69SBenjamin Herrenschmidt 	opal_poll_events(&evt);
1774f89363bSBenjamin Herrenschmidt 	if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0)
17814a43e69SBenjamin Herrenschmidt 		return 0;
1794f89363bSBenjamin Herrenschmidt 	len = cpu_to_be64(count);
18014a43e69SBenjamin Herrenschmidt 	rc = opal_console_read(vtermno, &len, buf);
18114a43e69SBenjamin Herrenschmidt 	if (rc == OPAL_SUCCESS)
1824f89363bSBenjamin Herrenschmidt 		return be64_to_cpu(len);
18314a43e69SBenjamin Herrenschmidt 	return 0;
18414a43e69SBenjamin Herrenschmidt }
18514a43e69SBenjamin Herrenschmidt 
18614a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
18714a43e69SBenjamin Herrenschmidt {
18814a43e69SBenjamin Herrenschmidt 	int written = 0;
1894f89363bSBenjamin Herrenschmidt 	__be64 olen;
190daea1175SBenjamin Herrenschmidt 	s64 len, rc;
19114a43e69SBenjamin Herrenschmidt 	unsigned long flags;
1924f89363bSBenjamin Herrenschmidt 	__be64 evt;
19314a43e69SBenjamin Herrenschmidt 
19414a43e69SBenjamin Herrenschmidt 	if (!opal.entry)
195daea1175SBenjamin Herrenschmidt 		return -ENODEV;
19614a43e69SBenjamin Herrenschmidt 
19714a43e69SBenjamin Herrenschmidt 	/* We want put_chars to be atomic to avoid mangling of hvsi
19814a43e69SBenjamin Herrenschmidt 	 * packets. To do that, we first test for room and return
199daea1175SBenjamin Herrenschmidt 	 * -EAGAIN if there isn't enough.
200daea1175SBenjamin Herrenschmidt 	 *
201daea1175SBenjamin Herrenschmidt 	 * Unfortunately, opal_console_write_buffer_space() doesn't
202daea1175SBenjamin Herrenschmidt 	 * appear to work on opal v1, so we just assume there is
203daea1175SBenjamin Herrenschmidt 	 * enough room and be done with it
20414a43e69SBenjamin Herrenschmidt 	 */
20514a43e69SBenjamin Herrenschmidt 	spin_lock_irqsave(&opal_write_lock, flags);
206daea1175SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2)) {
2074f89363bSBenjamin Herrenschmidt 		rc = opal_console_write_buffer_space(vtermno, &olen);
2084f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
20914a43e69SBenjamin Herrenschmidt 		if (rc || len < total_len) {
21014a43e69SBenjamin Herrenschmidt 			spin_unlock_irqrestore(&opal_write_lock, flags);
21114a43e69SBenjamin Herrenschmidt 			/* Closed -> drop characters */
21214a43e69SBenjamin Herrenschmidt 			if (rc)
21314a43e69SBenjamin Herrenschmidt 				return total_len;
2144f89363bSBenjamin Herrenschmidt 			opal_poll_events(NULL);
21514a43e69SBenjamin Herrenschmidt 			return -EAGAIN;
21614a43e69SBenjamin Herrenschmidt 		}
217daea1175SBenjamin Herrenschmidt 	}
21814a43e69SBenjamin Herrenschmidt 
21914a43e69SBenjamin Herrenschmidt 	/* We still try to handle partial completions, though they
22014a43e69SBenjamin Herrenschmidt 	 * should no longer happen.
22114a43e69SBenjamin Herrenschmidt 	 */
222daea1175SBenjamin Herrenschmidt 	rc = OPAL_BUSY;
22314a43e69SBenjamin Herrenschmidt 	while(total_len > 0 && (rc == OPAL_BUSY ||
22414a43e69SBenjamin Herrenschmidt 				rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
2254f89363bSBenjamin Herrenschmidt 		olen = cpu_to_be64(total_len);
2264f89363bSBenjamin Herrenschmidt 		rc = opal_console_write(vtermno, &olen, data);
2274f89363bSBenjamin Herrenschmidt 		len = be64_to_cpu(olen);
2281de1455fSBenjamin Herrenschmidt 
2291de1455fSBenjamin Herrenschmidt 		/* Closed or other error drop */
2301de1455fSBenjamin Herrenschmidt 		if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
2311de1455fSBenjamin Herrenschmidt 		    rc != OPAL_BUSY_EVENT) {
2321de1455fSBenjamin Herrenschmidt 			written = total_len;
2331de1455fSBenjamin Herrenschmidt 			break;
2341de1455fSBenjamin Herrenschmidt 		}
23514a43e69SBenjamin Herrenschmidt 		if (rc == OPAL_SUCCESS) {
23614a43e69SBenjamin Herrenschmidt 			total_len -= len;
23714a43e69SBenjamin Herrenschmidt 			data += len;
23814a43e69SBenjamin Herrenschmidt 			written += len;
23914a43e69SBenjamin Herrenschmidt 		}
24014a43e69SBenjamin Herrenschmidt 		/* This is a bit nasty but we need that for the console to
24114a43e69SBenjamin Herrenschmidt 		 * flush when there aren't any interrupts. We will clean
24214a43e69SBenjamin Herrenschmidt 		 * things a bit later to limit that to synchronous path
24314a43e69SBenjamin Herrenschmidt 		 * such as the kernel console and xmon/udbg
24414a43e69SBenjamin Herrenschmidt 		 */
24514a43e69SBenjamin Herrenschmidt 		do
24614a43e69SBenjamin Herrenschmidt 			opal_poll_events(&evt);
2474f89363bSBenjamin Herrenschmidt 		while(rc == OPAL_SUCCESS &&
2484f89363bSBenjamin Herrenschmidt 			(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT));
24914a43e69SBenjamin Herrenschmidt 	}
25014a43e69SBenjamin Herrenschmidt 	spin_unlock_irqrestore(&opal_write_lock, flags);
25114a43e69SBenjamin Herrenschmidt 	return written;
25214a43e69SBenjamin Herrenschmidt }
25314a43e69SBenjamin Herrenschmidt 
254ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs)
255ed79ba9eSBenjamin Herrenschmidt {
25636df96f8SMahesh Salgaonkar 	struct machine_check_event evt;
257ed79ba9eSBenjamin Herrenschmidt 
25836df96f8SMahesh Salgaonkar 	if (!get_mce_event(&evt, MCE_EVENT_RELEASE))
25936df96f8SMahesh Salgaonkar 		return 0;
260ed79ba9eSBenjamin Herrenschmidt 
261ed79ba9eSBenjamin Herrenschmidt 	/* Print things out */
26236df96f8SMahesh Salgaonkar 	if (evt.version != MCE_V1) {
263ed79ba9eSBenjamin Herrenschmidt 		pr_err("Machine Check Exception, Unknown event version %d !\n",
264ed79ba9eSBenjamin Herrenschmidt 		       evt.version);
265ed79ba9eSBenjamin Herrenschmidt 		return 0;
266ed79ba9eSBenjamin Herrenschmidt 	}
267b5ff4211SMahesh Salgaonkar 	machine_check_print_event_info(&evt);
268ed79ba9eSBenjamin Herrenschmidt 
26936df96f8SMahesh Salgaonkar 	return evt.severity == MCE_SEV_FATAL ? 0 : 1;
270ed79ba9eSBenjamin Herrenschmidt }
271ed79ba9eSBenjamin Herrenschmidt 
272a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data)
273a125e092SBenjamin Herrenschmidt {
2745e4da530SAnton Blanchard 	__be64 events;
275a125e092SBenjamin Herrenschmidt 
276a125e092SBenjamin Herrenschmidt 	opal_handle_interrupt(virq_to_hw(irq), &events);
277a125e092SBenjamin Herrenschmidt 
2781bc98de2SGavin Shan 	opal_do_notifier(events);
279a125e092SBenjamin Herrenschmidt 
280a125e092SBenjamin Herrenschmidt 	return IRQ_HANDLED;
281a125e092SBenjamin Herrenschmidt }
282a125e092SBenjamin Herrenschmidt 
2836f68b5e2SVasant Hegde static int opal_sysfs_init(void)
2846f68b5e2SVasant Hegde {
2856f68b5e2SVasant Hegde 	opal_kobj = kobject_create_and_add("opal", firmware_kobj);
2866f68b5e2SVasant Hegde 	if (!opal_kobj) {
2876f68b5e2SVasant Hegde 		pr_warn("kobject_create_and_add opal failed\n");
2886f68b5e2SVasant Hegde 		return -ENOMEM;
2896f68b5e2SVasant Hegde 	}
2906f68b5e2SVasant Hegde 
2916f68b5e2SVasant Hegde 	return 0;
2926f68b5e2SVasant Hegde }
2936f68b5e2SVasant Hegde 
29414a43e69SBenjamin Herrenschmidt static int __init opal_init(void)
29514a43e69SBenjamin Herrenschmidt {
29614a43e69SBenjamin Herrenschmidt 	struct device_node *np, *consoles;
2971cc79bc8SAlistair Popple 	const __be32 *irqs;
298a125e092SBenjamin Herrenschmidt 	int rc, i, irqlen;
29914a43e69SBenjamin Herrenschmidt 
30014a43e69SBenjamin Herrenschmidt 	opal_node = of_find_node_by_path("/ibm,opal");
30114a43e69SBenjamin Herrenschmidt 	if (!opal_node) {
30214a43e69SBenjamin Herrenschmidt 		pr_warn("opal: Node not found\n");
30314a43e69SBenjamin Herrenschmidt 		return -ENODEV;
30414a43e69SBenjamin Herrenschmidt 	}
3052db29d28SBenjamin Herrenschmidt 
3062db29d28SBenjamin Herrenschmidt 	/* Register OPAL consoles if any ports */
30714a43e69SBenjamin Herrenschmidt 	if (firmware_has_feature(FW_FEATURE_OPALv2))
30814a43e69SBenjamin Herrenschmidt 		consoles = of_find_node_by_path("/ibm,opal/consoles");
30914a43e69SBenjamin Herrenschmidt 	else
31014a43e69SBenjamin Herrenschmidt 		consoles = of_node_get(opal_node);
3112db29d28SBenjamin Herrenschmidt 	if (consoles) {
31214a43e69SBenjamin Herrenschmidt 		for_each_child_of_node(consoles, np) {
31314a43e69SBenjamin Herrenschmidt 			if (strcmp(np->name, "serial"))
31414a43e69SBenjamin Herrenschmidt 				continue;
31514a43e69SBenjamin Herrenschmidt 			of_platform_device_create(np, NULL, NULL);
31614a43e69SBenjamin Herrenschmidt 		}
31714a43e69SBenjamin Herrenschmidt 		of_node_put(consoles);
3182db29d28SBenjamin Herrenschmidt 	}
319a125e092SBenjamin Herrenschmidt 
320a125e092SBenjamin Herrenschmidt 	/* Find all OPAL interrupts and request them */
321a125e092SBenjamin Herrenschmidt 	irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
322a125e092SBenjamin Herrenschmidt 	pr_debug("opal: Found %d interrupts reserved for OPAL\n",
323a125e092SBenjamin Herrenschmidt 		 irqs ? (irqlen / 4) : 0);
32473ed148aSBenjamin Herrenschmidt 	opal_irq_count = irqlen / 4;
32573ed148aSBenjamin Herrenschmidt 	opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
326a125e092SBenjamin Herrenschmidt 	for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
327a125e092SBenjamin Herrenschmidt 		unsigned int hwirq = be32_to_cpup(irqs);
328a125e092SBenjamin Herrenschmidt 		unsigned int irq = irq_create_mapping(NULL, hwirq);
329a125e092SBenjamin Herrenschmidt 		if (irq == NO_IRQ) {
330a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
331a125e092SBenjamin Herrenschmidt 			continue;
332a125e092SBenjamin Herrenschmidt 		}
333a125e092SBenjamin Herrenschmidt 		rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
334a125e092SBenjamin Herrenschmidt 		if (rc)
335a125e092SBenjamin Herrenschmidt 			pr_warning("opal: Error %d requesting irq %d"
336a125e092SBenjamin Herrenschmidt 				   " (0x%x)\n", rc, irq, hwirq);
33773ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = irq;
338a125e092SBenjamin Herrenschmidt 	}
3396f68b5e2SVasant Hegde 
3406f68b5e2SVasant Hegde 	/* Create "opal" kobject under /sys/firmware */
3416f68b5e2SVasant Hegde 	rc = opal_sysfs_init();
34250bd6153SVasant Hegde 	if (rc == 0) {
34350bd6153SVasant Hegde 		/* Setup code update interface */
34450bd6153SVasant Hegde 		opal_flash_init();
34550bd6153SVasant Hegde 	}
3466f68b5e2SVasant Hegde 
34714a43e69SBenjamin Herrenschmidt 	return 0;
34814a43e69SBenjamin Herrenschmidt }
34914a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init);
35073ed148aSBenjamin Herrenschmidt 
35173ed148aSBenjamin Herrenschmidt void opal_shutdown(void)
35273ed148aSBenjamin Herrenschmidt {
35373ed148aSBenjamin Herrenschmidt 	unsigned int i;
35473ed148aSBenjamin Herrenschmidt 
35573ed148aSBenjamin Herrenschmidt 	for (i = 0; i < opal_irq_count; i++) {
35673ed148aSBenjamin Herrenschmidt 		if (opal_irqs[i])
357b0d436c7SAnton Blanchard 			free_irq(opal_irqs[i], NULL);
35873ed148aSBenjamin Herrenschmidt 		opal_irqs[i] = 0;
35973ed148aSBenjamin Herrenschmidt 	}
36073ed148aSBenjamin Herrenschmidt }
361