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