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> 1814a43e69SBenjamin Herrenschmidt #include <asm/opal.h> 1914a43e69SBenjamin Herrenschmidt #include <asm/firmware.h> 2014a43e69SBenjamin Herrenschmidt 2114a43e69SBenjamin Herrenschmidt #include "powernv.h" 2214a43e69SBenjamin Herrenschmidt 2314a43e69SBenjamin Herrenschmidt struct opal { 2414a43e69SBenjamin Herrenschmidt u64 base; 2514a43e69SBenjamin Herrenschmidt u64 entry; 2614a43e69SBenjamin Herrenschmidt } opal; 2714a43e69SBenjamin Herrenschmidt 2814a43e69SBenjamin Herrenschmidt static struct device_node *opal_node; 2914a43e69SBenjamin Herrenschmidt static DEFINE_SPINLOCK(opal_write_lock); 3014a43e69SBenjamin Herrenschmidt 3114a43e69SBenjamin Herrenschmidt int __init early_init_dt_scan_opal(unsigned long node, 3214a43e69SBenjamin Herrenschmidt const char *uname, int depth, void *data) 3314a43e69SBenjamin Herrenschmidt { 3414a43e69SBenjamin Herrenschmidt const void *basep, *entryp; 3514a43e69SBenjamin Herrenschmidt unsigned long basesz, entrysz; 3614a43e69SBenjamin Herrenschmidt 3714a43e69SBenjamin Herrenschmidt if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 3814a43e69SBenjamin Herrenschmidt return 0; 3914a43e69SBenjamin Herrenschmidt 4014a43e69SBenjamin Herrenschmidt basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 4114a43e69SBenjamin Herrenschmidt entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 4214a43e69SBenjamin Herrenschmidt 4314a43e69SBenjamin Herrenschmidt if (!basep || !entryp) 4414a43e69SBenjamin Herrenschmidt return 1; 4514a43e69SBenjamin Herrenschmidt 4614a43e69SBenjamin Herrenschmidt opal.base = of_read_number(basep, basesz/4); 4714a43e69SBenjamin Herrenschmidt opal.entry = of_read_number(entryp, entrysz/4); 4814a43e69SBenjamin Herrenschmidt 4914a43e69SBenjamin Herrenschmidt pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", 5014a43e69SBenjamin Herrenschmidt opal.base, basep, basesz); 5114a43e69SBenjamin Herrenschmidt pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", 5214a43e69SBenjamin Herrenschmidt opal.entry, entryp, entrysz); 5314a43e69SBenjamin Herrenschmidt 5414a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPAL; 5514a43e69SBenjamin Herrenschmidt if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { 5614a43e69SBenjamin Herrenschmidt powerpc_firmware_features |= FW_FEATURE_OPALv2; 5714a43e69SBenjamin Herrenschmidt printk("OPAL V2 detected !\n"); 5814a43e69SBenjamin Herrenschmidt } else { 5914a43e69SBenjamin Herrenschmidt printk("OPAL V1 detected !\n"); 6014a43e69SBenjamin Herrenschmidt } 6114a43e69SBenjamin Herrenschmidt 6214a43e69SBenjamin Herrenschmidt return 1; 6314a43e69SBenjamin Herrenschmidt } 6414a43e69SBenjamin Herrenschmidt 6514a43e69SBenjamin Herrenschmidt int opal_get_chars(uint32_t vtermno, char *buf, int count) 6614a43e69SBenjamin Herrenschmidt { 6714a43e69SBenjamin Herrenschmidt s64 len, rc; 6814a43e69SBenjamin Herrenschmidt u64 evt; 6914a43e69SBenjamin Herrenschmidt 7014a43e69SBenjamin Herrenschmidt if (!opal.entry) 71daea1175SBenjamin Herrenschmidt return -ENODEV; 7214a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 7314a43e69SBenjamin Herrenschmidt if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) 7414a43e69SBenjamin Herrenschmidt return 0; 7514a43e69SBenjamin Herrenschmidt len = count; 7614a43e69SBenjamin Herrenschmidt rc = opal_console_read(vtermno, &len, buf); 7714a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) 7814a43e69SBenjamin Herrenschmidt return len; 7914a43e69SBenjamin Herrenschmidt return 0; 8014a43e69SBenjamin Herrenschmidt } 8114a43e69SBenjamin Herrenschmidt 8214a43e69SBenjamin Herrenschmidt int opal_put_chars(uint32_t vtermno, const char *data, int total_len) 8314a43e69SBenjamin Herrenschmidt { 8414a43e69SBenjamin Herrenschmidt int written = 0; 85daea1175SBenjamin Herrenschmidt s64 len, rc; 8614a43e69SBenjamin Herrenschmidt unsigned long flags; 8714a43e69SBenjamin Herrenschmidt u64 evt; 8814a43e69SBenjamin Herrenschmidt 8914a43e69SBenjamin Herrenschmidt if (!opal.entry) 90daea1175SBenjamin Herrenschmidt return -ENODEV; 9114a43e69SBenjamin Herrenschmidt 9214a43e69SBenjamin Herrenschmidt /* We want put_chars to be atomic to avoid mangling of hvsi 9314a43e69SBenjamin Herrenschmidt * packets. To do that, we first test for room and return 94daea1175SBenjamin Herrenschmidt * -EAGAIN if there isn't enough. 95daea1175SBenjamin Herrenschmidt * 96daea1175SBenjamin Herrenschmidt * Unfortunately, opal_console_write_buffer_space() doesn't 97daea1175SBenjamin Herrenschmidt * appear to work on opal v1, so we just assume there is 98daea1175SBenjamin Herrenschmidt * enough room and be done with it 9914a43e69SBenjamin Herrenschmidt */ 10014a43e69SBenjamin Herrenschmidt spin_lock_irqsave(&opal_write_lock, flags); 101daea1175SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) { 10214a43e69SBenjamin Herrenschmidt rc = opal_console_write_buffer_space(vtermno, &len); 10314a43e69SBenjamin Herrenschmidt if (rc || len < total_len) { 10414a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 10514a43e69SBenjamin Herrenschmidt /* Closed -> drop characters */ 10614a43e69SBenjamin Herrenschmidt if (rc) 10714a43e69SBenjamin Herrenschmidt return total_len; 10814a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 10914a43e69SBenjamin Herrenschmidt return -EAGAIN; 11014a43e69SBenjamin Herrenschmidt } 111daea1175SBenjamin Herrenschmidt } 11214a43e69SBenjamin Herrenschmidt 11314a43e69SBenjamin Herrenschmidt /* We still try to handle partial completions, though they 11414a43e69SBenjamin Herrenschmidt * should no longer happen. 11514a43e69SBenjamin Herrenschmidt */ 116daea1175SBenjamin Herrenschmidt rc = OPAL_BUSY; 11714a43e69SBenjamin Herrenschmidt while(total_len > 0 && (rc == OPAL_BUSY || 11814a43e69SBenjamin Herrenschmidt rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { 11914a43e69SBenjamin Herrenschmidt len = total_len; 12014a43e69SBenjamin Herrenschmidt rc = opal_console_write(vtermno, &len, data); 12114a43e69SBenjamin Herrenschmidt if (rc == OPAL_SUCCESS) { 12214a43e69SBenjamin Herrenschmidt total_len -= len; 12314a43e69SBenjamin Herrenschmidt data += len; 12414a43e69SBenjamin Herrenschmidt written += len; 12514a43e69SBenjamin Herrenschmidt } 12614a43e69SBenjamin Herrenschmidt /* This is a bit nasty but we need that for the console to 12714a43e69SBenjamin Herrenschmidt * flush when there aren't any interrupts. We will clean 12814a43e69SBenjamin Herrenschmidt * things a bit later to limit that to synchronous path 12914a43e69SBenjamin Herrenschmidt * such as the kernel console and xmon/udbg 13014a43e69SBenjamin Herrenschmidt */ 13114a43e69SBenjamin Herrenschmidt do 13214a43e69SBenjamin Herrenschmidt opal_poll_events(&evt); 13314a43e69SBenjamin Herrenschmidt while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); 13414a43e69SBenjamin Herrenschmidt } 13514a43e69SBenjamin Herrenschmidt spin_unlock_irqrestore(&opal_write_lock, flags); 13614a43e69SBenjamin Herrenschmidt return written; 13714a43e69SBenjamin Herrenschmidt } 13814a43e69SBenjamin Herrenschmidt 139a125e092SBenjamin Herrenschmidt static irqreturn_t opal_interrupt(int irq, void *data) 140a125e092SBenjamin Herrenschmidt { 141a125e092SBenjamin Herrenschmidt uint64_t events; 142a125e092SBenjamin Herrenschmidt 143a125e092SBenjamin Herrenschmidt opal_handle_interrupt(virq_to_hw(irq), &events); 144a125e092SBenjamin Herrenschmidt 145a125e092SBenjamin Herrenschmidt /* XXX TODO: Do something with the events */ 146a125e092SBenjamin Herrenschmidt 147a125e092SBenjamin Herrenschmidt return IRQ_HANDLED; 148a125e092SBenjamin Herrenschmidt } 149a125e092SBenjamin Herrenschmidt 15014a43e69SBenjamin Herrenschmidt static int __init opal_init(void) 15114a43e69SBenjamin Herrenschmidt { 15214a43e69SBenjamin Herrenschmidt struct device_node *np, *consoles; 153a125e092SBenjamin Herrenschmidt const u32 *irqs; 154a125e092SBenjamin Herrenschmidt int rc, i, irqlen; 15514a43e69SBenjamin Herrenschmidt 15614a43e69SBenjamin Herrenschmidt opal_node = of_find_node_by_path("/ibm,opal"); 15714a43e69SBenjamin Herrenschmidt if (!opal_node) { 15814a43e69SBenjamin Herrenschmidt pr_warn("opal: Node not found\n"); 15914a43e69SBenjamin Herrenschmidt return -ENODEV; 16014a43e69SBenjamin Herrenschmidt } 16114a43e69SBenjamin Herrenschmidt if (firmware_has_feature(FW_FEATURE_OPALv2)) 16214a43e69SBenjamin Herrenschmidt consoles = of_find_node_by_path("/ibm,opal/consoles"); 16314a43e69SBenjamin Herrenschmidt else 16414a43e69SBenjamin Herrenschmidt consoles = of_node_get(opal_node); 16514a43e69SBenjamin Herrenschmidt 16614a43e69SBenjamin Herrenschmidt /* Register serial ports */ 16714a43e69SBenjamin Herrenschmidt for_each_child_of_node(consoles, np) { 16814a43e69SBenjamin Herrenschmidt if (strcmp(np->name, "serial")) 16914a43e69SBenjamin Herrenschmidt continue; 17014a43e69SBenjamin Herrenschmidt of_platform_device_create(np, NULL, NULL); 17114a43e69SBenjamin Herrenschmidt } 17214a43e69SBenjamin Herrenschmidt of_node_put(consoles); 173a125e092SBenjamin Herrenschmidt 174a125e092SBenjamin Herrenschmidt /* Find all OPAL interrupts and request them */ 175a125e092SBenjamin Herrenschmidt irqs = of_get_property(opal_node, "opal-interrupts", &irqlen); 176a125e092SBenjamin Herrenschmidt pr_debug("opal: Found %d interrupts reserved for OPAL\n", 177a125e092SBenjamin Herrenschmidt irqs ? (irqlen / 4) : 0); 178a125e092SBenjamin Herrenschmidt for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) { 179a125e092SBenjamin Herrenschmidt unsigned int hwirq = be32_to_cpup(irqs); 180a125e092SBenjamin Herrenschmidt unsigned int irq = irq_create_mapping(NULL, hwirq); 181a125e092SBenjamin Herrenschmidt if (irq == NO_IRQ) { 182a125e092SBenjamin Herrenschmidt pr_warning("opal: Failed to map irq 0x%x\n", hwirq); 183a125e092SBenjamin Herrenschmidt continue; 184a125e092SBenjamin Herrenschmidt } 185a125e092SBenjamin Herrenschmidt rc = request_irq(irq, opal_interrupt, 0, "opal", NULL); 186a125e092SBenjamin Herrenschmidt if (rc) 187a125e092SBenjamin Herrenschmidt pr_warning("opal: Error %d requesting irq %d" 188a125e092SBenjamin Herrenschmidt " (0x%x)\n", rc, irq, hwirq); 189a125e092SBenjamin Herrenschmidt } 19014a43e69SBenjamin Herrenschmidt return 0; 19114a43e69SBenjamin Herrenschmidt } 19214a43e69SBenjamin Herrenschmidt subsys_initcall(opal_init); 193