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> 1873ed148aSBenjamin Herrenschmidt #include <linux/slab.h> 1914a43e69SBenjamin Herrenschmidt #include <asm/opal.h> 2014a43e69SBenjamin Herrenschmidt #include <asm/firmware.h> 2114a43e69SBenjamin Herrenschmidt 2214a43e69SBenjamin Herrenschmidt #include "powernv.h" 2314a43e69SBenjamin Herrenschmidt 2414a43e69SBenjamin Herrenschmidt struct opal { 2514a43e69SBenjamin Herrenschmidt u64 base; 2614a43e69SBenjamin Herrenschmidt u64 entry; 2714a43e69SBenjamin Herrenschmidt } opal; 2814a43e69SBenjamin Herrenschmidt 2914a43e69SBenjamin Herrenschmidt static struct device_node *opal_node; 3014a43e69SBenjamin Herrenschmidt static DEFINE_SPINLOCK(opal_write_lock); 31ed79ba9eSBenjamin Herrenschmidt extern u64 opal_mc_secondary_handler[]; 3273ed148aSBenjamin Herrenschmidt static unsigned int *opal_irqs; 3373ed148aSBenjamin Herrenschmidt static unsigned int opal_irq_count; 3414a43e69SBenjamin Herrenschmidt 3514a43e69SBenjamin Herrenschmidt int __init early_init_dt_scan_opal(unsigned long node, 3614a43e69SBenjamin Herrenschmidt const char *uname, int depth, void *data) 3714a43e69SBenjamin Herrenschmidt { 3814a43e69SBenjamin Herrenschmidt const void *basep, *entryp; 3914a43e69SBenjamin Herrenschmidt unsigned long basesz, entrysz; 4014a43e69SBenjamin Herrenschmidt 4114a43e69SBenjamin Herrenschmidt if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 4214a43e69SBenjamin Herrenschmidt return 0; 4314a43e69SBenjamin Herrenschmidt 4414a43e69SBenjamin Herrenschmidt basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 4514a43e69SBenjamin Herrenschmidt entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 4614a43e69SBenjamin Herrenschmidt 4714a43e69SBenjamin Herrenschmidt if (!basep || !entryp) 4814a43e69SBenjamin Herrenschmidt return 1; 4914a43e69SBenjamin Herrenschmidt 5014a43e69SBenjamin Herrenschmidt opal.base = of_read_number(basep, basesz/4); 5114a43e69SBenjamin Herrenschmidt opal.entry = of_read_number(entryp, entrysz/4); 5214a43e69SBenjamin Herrenschmidt 5314a43e69SBenjamin Herrenschmidt pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", 5414a43e69SBenjamin Herrenschmidt opal.base, basep, basesz); 5514a43e69SBenjamin Herrenschmidt pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", 5614a43e69SBenjamin Herrenschmidt opal.entry, entryp, entrysz); 5714a43e69SBenjamin Herrenschmidt 5814a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPAL; 5975b93da4SBenjamin Herrenschmidt if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) { 6075b93da4SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv2; 6175b93da4SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv3; 6275b93da4SBenjamin Herrenschmidt printk("OPAL V3 detected !\n"); 6375b93da4SBenjamin Herrenschmidt } else if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { 6414a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv2; 6514a43e69SBenjamin Herrenschmidt printk("OPAL V2 detected !\n"); 6614a43e69SBenjamin Herrenschmidt } else { 6714a43e69SBenjamin Herrenschmidt printk("OPAL V1 detected !\n"); 6814a43e69SBenjamin Herrenschmidt } 6914a43e69SBenjamin Herrenschmidt 70c4463b37SJeremy Kerr return 1; 71c4463b37SJeremy Kerr } 72c4463b37SJeremy Kerr 73c4463b37SJeremy Kerr static int __init opal_register_exception_handlers(void) 74c4463b37SJeremy Kerr { 75c4463b37SJeremy Kerr u64 glue; 76c4463b37SJeremy Kerr 77c4463b37SJeremy Kerr if (!(powerpc_firmware_features & FW_FEATURE_OPAL)) 78c4463b37SJeremy Kerr return -ENODEV; 79c4463b37SJeremy Kerr 80ed79ba9eSBenjamin Herrenschmidt /* Hookup some exception handlers. We use the fwnmi area at 0x7000 81ed79ba9eSBenjamin Herrenschmidt * to provide the glue space to OPAL 82ed79ba9eSBenjamin Herrenschmidt */ 83ed79ba9eSBenjamin Herrenschmidt glue = 0x7000; 84ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER, 85ed79ba9eSBenjamin Herrenschmidt __pa(opal_mc_secondary_handler[0]), 86ed79ba9eSBenjamin Herrenschmidt glue); 87ed79ba9eSBenjamin Herrenschmidt glue += 128; 88ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER, 89ed79ba9eSBenjamin Herrenschmidt 0, glue); 90ed79ba9eSBenjamin Herrenschmidt glue += 128; 91ed79ba9eSBenjamin Herrenschmidt opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue); 92ed79ba9eSBenjamin Herrenschmidt 93c4463b37SJeremy Kerr return 0; 9414a43e69SBenjamin Herrenschmidt } 9514a43e69SBenjamin Herrenschmidt 96c4463b37SJeremy Kerr early_initcall(opal_register_exception_handlers); 97c4463b37SJeremy Kerr 9814a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count) 9914a43e69SBenjamin Herrenschmidt { 10014a43e69SBenjamin Herrenschmidt s64 len, rc; 10114a43e69SBenjamin Herrenschmidt u64 evt; 10214a43e69SBenjamin Herrenschmidt 10314a43e69SBenjamin Herrenschmidt if (!opal.entry) 104daea1175SBenjamin Herrenschmidt return -ENODEV; 10514a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 10614a43e69SBenjamin Herrenschmidt if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) 10714a43e69SBenjamin Herrenschmidt return 0; 10814a43e69SBenjamin Herrenschmidt len = count; 10914a43e69SBenjamin Herrenschmidt rc = opal_console_read(vtermno, &len, buf); 11014a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) 11114a43e69SBenjamin Herrenschmidt return len; 11214a43e69SBenjamin Herrenschmidt return 0; 11314a43e69SBenjamin Herrenschmidt } 11414a43e69SBenjamin Herrenschmidt 11514a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len) 11614a43e69SBenjamin Herrenschmidt { 11714a43e69SBenjamin Herrenschmidt int written = 0; 118daea1175SBenjamin Herrenschmidt s64 len, rc; 11914a43e69SBenjamin Herrenschmidt unsigned long flags; 12014a43e69SBenjamin Herrenschmidt u64 evt; 12114a43e69SBenjamin Herrenschmidt 12214a43e69SBenjamin Herrenschmidt if (!opal.entry) 123daea1175SBenjamin Herrenschmidt return -ENODEV; 12414a43e69SBenjamin Herrenschmidt 12514a43e69SBenjamin Herrenschmidt /* We want put_chars to be atomic to avoid mangling of hvsi 12614a43e69SBenjamin Herrenschmidt * packets. To do that, we first test for room and return 127daea1175SBenjamin Herrenschmidt * -EAGAIN if there isn't enough. 128daea1175SBenjamin Herrenschmidt * 129daea1175SBenjamin Herrenschmidt * Unfortunately, opal_console_write_buffer_space() doesn't 130daea1175SBenjamin Herrenschmidt * appear to work on opal v1, so we just assume there is 131daea1175SBenjamin Herrenschmidt * enough room and be done with it 13214a43e69SBenjamin Herrenschmidt */ 13314a43e69SBenjamin Herrenschmidt spin_lock_irqsave(&opal_write_lock, flags); 134daea1175SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) { 13514a43e69SBenjamin Herrenschmidt rc = opal_console_write_buffer_space(vtermno, &len); 13614a43e69SBenjamin Herrenschmidt if (rc || len < total_len) { 13714a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 13814a43e69SBenjamin Herrenschmidt /* Closed -> drop characters */ 13914a43e69SBenjamin Herrenschmidt if (rc) 14014a43e69SBenjamin Herrenschmidt return total_len; 14114a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 14214a43e69SBenjamin Herrenschmidt return -EAGAIN; 14314a43e69SBenjamin Herrenschmidt } 144daea1175SBenjamin Herrenschmidt } 14514a43e69SBenjamin Herrenschmidt 14614a43e69SBenjamin Herrenschmidt /* We still try to handle partial completions, though they 14714a43e69SBenjamin Herrenschmidt * should no longer happen. 14814a43e69SBenjamin Herrenschmidt */ 149daea1175SBenjamin Herrenschmidt rc = OPAL_BUSY; 15014a43e69SBenjamin Herrenschmidt while(total_len > 0 && (rc == OPAL_BUSY || 15114a43e69SBenjamin Herrenschmidt rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { 15214a43e69SBenjamin Herrenschmidt len = total_len; 15314a43e69SBenjamin Herrenschmidt rc = opal_console_write(vtermno, &len, data); 1541de1455fSBenjamin Herrenschmidt 1551de1455fSBenjamin Herrenschmidt /* Closed or other error drop */ 1561de1455fSBenjamin Herrenschmidt if (rc != OPAL_SUCCESS && rc != OPAL_BUSY && 1571de1455fSBenjamin Herrenschmidt rc != OPAL_BUSY_EVENT) { 1581de1455fSBenjamin Herrenschmidt written = total_len; 1591de1455fSBenjamin Herrenschmidt break; 1601de1455fSBenjamin Herrenschmidt } 16114a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) { 16214a43e69SBenjamin Herrenschmidt total_len -= len; 16314a43e69SBenjamin Herrenschmidt data += len; 16414a43e69SBenjamin Herrenschmidt written += len; 16514a43e69SBenjamin Herrenschmidt } 16614a43e69SBenjamin Herrenschmidt /* This is a bit nasty but we need that for the console to 16714a43e69SBenjamin Herrenschmidt * flush when there aren't any interrupts. We will clean 16814a43e69SBenjamin Herrenschmidt * things a bit later to limit that to synchronous path 16914a43e69SBenjamin Herrenschmidt * such as the kernel console and xmon/udbg 17014a43e69SBenjamin Herrenschmidt */ 17114a43e69SBenjamin Herrenschmidt do 17214a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 17314a43e69SBenjamin Herrenschmidt while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); 17414a43e69SBenjamin Herrenschmidt } 17514a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 17614a43e69SBenjamin Herrenschmidt return written; 17714a43e69SBenjamin Herrenschmidt } 17814a43e69SBenjamin Herrenschmidt 179ed79ba9eSBenjamin Herrenschmidt int opal_machine_check(struct pt_regs *regs) 180ed79ba9eSBenjamin Herrenschmidt { 181ed79ba9eSBenjamin Herrenschmidt struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt; 182ed79ba9eSBenjamin Herrenschmidt struct opal_machine_check_event evt; 183ed79ba9eSBenjamin Herrenschmidt const char *level, *sevstr, *subtype; 184ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_ue_types[] = { 185ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 186ed79ba9eSBenjamin Herrenschmidt "Instruction fetch", 187ed79ba9eSBenjamin Herrenschmidt "Page table walk ifetch", 188ed79ba9eSBenjamin Herrenschmidt "Load/Store", 189ed79ba9eSBenjamin Herrenschmidt "Page table walk Load/Store", 190ed79ba9eSBenjamin Herrenschmidt }; 191ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_slb_types[] = { 192ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 193ed79ba9eSBenjamin Herrenschmidt "Parity", 194ed79ba9eSBenjamin Herrenschmidt "Multihit", 195ed79ba9eSBenjamin Herrenschmidt }; 196ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_erat_types[] = { 197ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 198ed79ba9eSBenjamin Herrenschmidt "Parity", 199ed79ba9eSBenjamin Herrenschmidt "Multihit", 200ed79ba9eSBenjamin Herrenschmidt }; 201ed79ba9eSBenjamin Herrenschmidt static const char *opal_mc_tlb_types[] = { 202ed79ba9eSBenjamin Herrenschmidt "Indeterminate", 203ed79ba9eSBenjamin Herrenschmidt "Parity", 204ed79ba9eSBenjamin Herrenschmidt "Multihit", 205ed79ba9eSBenjamin Herrenschmidt }; 206ed79ba9eSBenjamin Herrenschmidt 207ed79ba9eSBenjamin Herrenschmidt /* Copy the event structure and release the original */ 208ed79ba9eSBenjamin Herrenschmidt evt = *opal_evt; 209ed79ba9eSBenjamin Herrenschmidt opal_evt->in_use = 0; 210ed79ba9eSBenjamin Herrenschmidt 211ed79ba9eSBenjamin Herrenschmidt /* Print things out */ 212ed79ba9eSBenjamin Herrenschmidt if (evt.version != OpalMCE_V1) { 213ed79ba9eSBenjamin Herrenschmidt pr_err("Machine Check Exception, Unknown event version %d !\n", 214ed79ba9eSBenjamin Herrenschmidt evt.version); 215ed79ba9eSBenjamin Herrenschmidt return 0; 216ed79ba9eSBenjamin Herrenschmidt } 217ed79ba9eSBenjamin Herrenschmidt switch(evt.severity) { 218ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_NO_ERROR: 219ed79ba9eSBenjamin Herrenschmidt level = KERN_INFO; 220ed79ba9eSBenjamin Herrenschmidt sevstr = "Harmless"; 221ed79ba9eSBenjamin Herrenschmidt break; 222ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_WARNING: 223ed79ba9eSBenjamin Herrenschmidt level = KERN_WARNING; 224ed79ba9eSBenjamin Herrenschmidt sevstr = ""; 225ed79ba9eSBenjamin Herrenschmidt break; 226ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_ERROR_SYNC: 227ed79ba9eSBenjamin Herrenschmidt level = KERN_ERR; 228ed79ba9eSBenjamin Herrenschmidt sevstr = "Severe"; 229ed79ba9eSBenjamin Herrenschmidt break; 230ed79ba9eSBenjamin Herrenschmidt case OpalMCE_SEV_FATAL: 231ed79ba9eSBenjamin Herrenschmidt default: 232ed79ba9eSBenjamin Herrenschmidt level = KERN_ERR; 233ed79ba9eSBenjamin Herrenschmidt sevstr = "Fatal"; 234ed79ba9eSBenjamin Herrenschmidt break; 235ed79ba9eSBenjamin Herrenschmidt } 236ed79ba9eSBenjamin Herrenschmidt 237ed79ba9eSBenjamin Herrenschmidt printk("%s%s Machine check interrupt [%s]\n", level, sevstr, 238ed79ba9eSBenjamin Herrenschmidt evt.disposition == OpalMCE_DISPOSITION_RECOVERED ? 239ed79ba9eSBenjamin Herrenschmidt "Recovered" : "[Not recovered"); 240ed79ba9eSBenjamin Herrenschmidt printk("%s Initiator: %s\n", level, 241ed79ba9eSBenjamin Herrenschmidt evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown"); 242ed79ba9eSBenjamin Herrenschmidt switch(evt.error_type) { 243ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_UE: 244ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.ue_error.ue_error_type < 245ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_ue_types) ? 246ed79ba9eSBenjamin Herrenschmidt opal_mc_ue_types[evt.u.ue_error.ue_error_type] 247ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 248ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: UE [%s]\n", level, subtype); 249ed79ba9eSBenjamin Herrenschmidt if (evt.u.ue_error.effective_address_provided) 250ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 251ed79ba9eSBenjamin Herrenschmidt level, evt.u.ue_error.effective_address); 252ed79ba9eSBenjamin Herrenschmidt if (evt.u.ue_error.physical_address_provided) 253ed79ba9eSBenjamin Herrenschmidt printk("%s Physial address: %016llx\n", 254ed79ba9eSBenjamin Herrenschmidt level, evt.u.ue_error.physical_address); 255ed79ba9eSBenjamin Herrenschmidt break; 256ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_SLB: 257ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.slb_error.slb_error_type < 258ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_slb_types) ? 259ed79ba9eSBenjamin Herrenschmidt opal_mc_slb_types[evt.u.slb_error.slb_error_type] 260ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 261ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: SLB [%s]\n", level, subtype); 262ed79ba9eSBenjamin Herrenschmidt if (evt.u.slb_error.effective_address_provided) 263ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 264ed79ba9eSBenjamin Herrenschmidt level, evt.u.slb_error.effective_address); 265ed79ba9eSBenjamin Herrenschmidt break; 266ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_ERAT: 267ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.erat_error.erat_error_type < 268ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_erat_types) ? 269ed79ba9eSBenjamin Herrenschmidt opal_mc_erat_types[evt.u.erat_error.erat_error_type] 270ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 271ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: ERAT [%s]\n", level, subtype); 272ed79ba9eSBenjamin Herrenschmidt if (evt.u.erat_error.effective_address_provided) 273ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 274ed79ba9eSBenjamin Herrenschmidt level, evt.u.erat_error.effective_address); 275ed79ba9eSBenjamin Herrenschmidt break; 276ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_TLB: 277ed79ba9eSBenjamin Herrenschmidt subtype = evt.u.tlb_error.tlb_error_type < 278ed79ba9eSBenjamin Herrenschmidt ARRAY_SIZE(opal_mc_tlb_types) ? 279ed79ba9eSBenjamin Herrenschmidt opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type] 280ed79ba9eSBenjamin Herrenschmidt : "Unknown"; 281ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: TLB [%s]\n", level, subtype); 282ed79ba9eSBenjamin Herrenschmidt if (evt.u.tlb_error.effective_address_provided) 283ed79ba9eSBenjamin Herrenschmidt printk("%s Effective address: %016llx\n", 284ed79ba9eSBenjamin Herrenschmidt level, evt.u.tlb_error.effective_address); 285ed79ba9eSBenjamin Herrenschmidt break; 286ed79ba9eSBenjamin Herrenschmidt default: 287ed79ba9eSBenjamin Herrenschmidt case OpalMCE_ERROR_TYPE_UNKNOWN: 288ed79ba9eSBenjamin Herrenschmidt printk("%s Error type: Unknown\n", level); 289ed79ba9eSBenjamin Herrenschmidt break; 290ed79ba9eSBenjamin Herrenschmidt } 291ed79ba9eSBenjamin Herrenschmidt return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1; 292ed79ba9eSBenjamin Herrenschmidt } 293ed79ba9eSBenjamin Herrenschmidt 294a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data) 295a125e092SBenjamin Herrenschmidt { 296a125e092SBenjamin Herrenschmidt uint64_t events; 297a125e092SBenjamin Herrenschmidt 298a125e092SBenjamin Herrenschmidt opal_handle_interrupt(virq_to_hw(irq), &events); 299a125e092SBenjamin Herrenschmidt 300a125e092SBenjamin Herrenschmidt /* XXX TODO: Do something with the events */ 301a125e092SBenjamin Herrenschmidt 302a125e092SBenjamin Herrenschmidt return IRQ_HANDLED; 303a125e092SBenjamin Herrenschmidt } 304a125e092SBenjamin Herrenschmidt 30514a43e69SBenjamin Herrenschmidt static int __init opal_init(void) 30614a43e69SBenjamin Herrenschmidt { 30714a43e69SBenjamin Herrenschmidt struct device_node *np, *consoles; 308a125e092SBenjamin Herrenschmidt const u32 *irqs; 309a125e092SBenjamin Herrenschmidt int rc, i, irqlen; 31014a43e69SBenjamin Herrenschmidt 31114a43e69SBenjamin Herrenschmidt opal_node = of_find_node_by_path("/ibm,opal"); 31214a43e69SBenjamin Herrenschmidt if (!opal_node) { 31314a43e69SBenjamin Herrenschmidt pr_warn("opal: Node not found\n"); 31414a43e69SBenjamin Herrenschmidt return -ENODEV; 31514a43e69SBenjamin Herrenschmidt } 31614a43e69SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) 31714a43e69SBenjamin Herrenschmidt consoles = of_find_node_by_path("/ibm,opal/consoles"); 31814a43e69SBenjamin Herrenschmidt else 31914a43e69SBenjamin Herrenschmidt consoles = of_node_get(opal_node); 32014a43e69SBenjamin Herrenschmidt 32114a43e69SBenjamin Herrenschmidt /* Register serial ports */ 32214a43e69SBenjamin Herrenschmidt for_each_child_of_node(consoles, np) { 32314a43e69SBenjamin Herrenschmidt if (strcmp(np->name, "serial")) 32414a43e69SBenjamin Herrenschmidt continue; 32514a43e69SBenjamin Herrenschmidt of_platform_device_create(np, NULL, NULL); 32614a43e69SBenjamin Herrenschmidt } 32714a43e69SBenjamin Herrenschmidt of_node_put(consoles); 328a125e092SBenjamin Herrenschmidt 329a125e092SBenjamin Herrenschmidt /* Find all OPAL interrupts and request them */ 330a125e092SBenjamin Herrenschmidt irqs = of_get_property(opal_node, "opal-interrupts", &irqlen); 331a125e092SBenjamin Herrenschmidt pr_debug("opal: Found %d interrupts reserved for OPAL\n", 332a125e092SBenjamin Herrenschmidt irqs ? (irqlen / 4) : 0); 33373ed148aSBenjamin Herrenschmidt opal_irq_count = irqlen / 4; 33473ed148aSBenjamin Herrenschmidt opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL); 335a125e092SBenjamin Herrenschmidt for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) { 336a125e092SBenjamin Herrenschmidt unsigned int hwirq = be32_to_cpup(irqs); 337a125e092SBenjamin Herrenschmidt unsigned int irq = irq_create_mapping(NULL, hwirq); 338a125e092SBenjamin Herrenschmidt if (irq == NO_IRQ) { 339a125e092SBenjamin Herrenschmidt pr_warning("opal: Failed to map irq 0x%x\n", hwirq); 340a125e092SBenjamin Herrenschmidt continue; 341a125e092SBenjamin Herrenschmidt } 342a125e092SBenjamin Herrenschmidt rc = request_irq(irq, opal_interrupt, 0, "opal", NULL); 343a125e092SBenjamin Herrenschmidt if (rc) 344a125e092SBenjamin Herrenschmidt pr_warning("opal: Error %d requesting irq %d" 345a125e092SBenjamin Herrenschmidt " (0x%x)\n", rc, irq, hwirq); 34673ed148aSBenjamin Herrenschmidt opal_irqs[i] = irq; 347a125e092SBenjamin Herrenschmidt } 34814a43e69SBenjamin Herrenschmidt return 0; 34914a43e69SBenjamin Herrenschmidt } 35014a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init); 35173ed148aSBenjamin Herrenschmidt 35273ed148aSBenjamin Herrenschmidt void opal_shutdown(void) 35373ed148aSBenjamin Herrenschmidt { 35473ed148aSBenjamin Herrenschmidt unsigned int i; 35573ed148aSBenjamin Herrenschmidt 35673ed148aSBenjamin Herrenschmidt for (i = 0; i < opal_irq_count; i++) { 35773ed148aSBenjamin Herrenschmidt if (opal_irqs[i]) 35873ed148aSBenjamin Herrenschmidt free_irq(opal_irqs[i], 0); 35973ed148aSBenjamin Herrenschmidt opal_irqs[i] = 0; 36073ed148aSBenjamin Herrenschmidt } 36173ed148aSBenjamin Herrenschmidt } 362