1 /* 2 * PowerNV OPAL high level interfaces 3 * 4 * Copyright 2011 IBM Corp. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12 #undef DEBUG 13 14 #include <linux/types.h> 15 #include <linux/of.h> 16 #include <linux/of_platform.h> 17 #include <asm/opal.h> 18 #include <asm/firmware.h> 19 20 #include "powernv.h" 21 22 struct opal { 23 u64 base; 24 u64 entry; 25 } opal; 26 27 static struct device_node *opal_node; 28 static DEFINE_SPINLOCK(opal_write_lock); 29 30 int __init early_init_dt_scan_opal(unsigned long node, 31 const char *uname, int depth, void *data) 32 { 33 const void *basep, *entryp; 34 unsigned long basesz, entrysz; 35 36 if (depth != 1 || strcmp(uname, "ibm,opal") != 0) 37 return 0; 38 39 basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz); 40 entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz); 41 42 if (!basep || !entryp) 43 return 1; 44 45 opal.base = of_read_number(basep, basesz/4); 46 opal.entry = of_read_number(entryp, entrysz/4); 47 48 pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n", 49 opal.base, basep, basesz); 50 pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n", 51 opal.entry, entryp, entrysz); 52 53 powerpc_firmware_features |= FW_FEATURE_OPAL; 54 if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) { 55 powerpc_firmware_features |= FW_FEATURE_OPALv2; 56 printk("OPAL V2 detected !\n"); 57 } else { 58 printk("OPAL V1 detected !\n"); 59 } 60 61 return 1; 62 } 63 64 int opal_get_chars(uint32_t vtermno, char *buf, int count) 65 { 66 s64 len, rc; 67 u64 evt; 68 69 if (!opal.entry) 70 return 0; 71 opal_poll_events(&evt); 72 if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0) 73 return 0; 74 len = count; 75 rc = opal_console_read(vtermno, &len, buf); 76 if (rc == OPAL_SUCCESS) 77 return len; 78 return 0; 79 } 80 81 int opal_put_chars(uint32_t vtermno, const char *data, int total_len) 82 { 83 int written = 0; 84 s64 len, rc = OPAL_BUSY; 85 unsigned long flags; 86 u64 evt; 87 88 if (!opal.entry) 89 return 0; 90 91 /* We want put_chars to be atomic to avoid mangling of hvsi 92 * packets. To do that, we first test for room and return 93 * -EAGAIN if there isn't enough 94 */ 95 spin_lock_irqsave(&opal_write_lock, flags); 96 rc = opal_console_write_buffer_space(vtermno, &len); 97 if (rc || len < total_len) { 98 spin_unlock_irqrestore(&opal_write_lock, flags); 99 /* Closed -> drop characters */ 100 if (rc) 101 return total_len; 102 opal_poll_events(&evt); 103 return -EAGAIN; 104 } 105 106 /* We still try to handle partial completions, though they 107 * should no longer happen. 108 */ 109 while(total_len > 0 && (rc == OPAL_BUSY || 110 rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) { 111 len = total_len; 112 rc = opal_console_write(vtermno, &len, data); 113 if (rc == OPAL_SUCCESS) { 114 total_len -= len; 115 data += len; 116 written += len; 117 } 118 /* This is a bit nasty but we need that for the console to 119 * flush when there aren't any interrupts. We will clean 120 * things a bit later to limit that to synchronous path 121 * such as the kernel console and xmon/udbg 122 */ 123 do 124 opal_poll_events(&evt); 125 while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT)); 126 } 127 spin_unlock_irqrestore(&opal_write_lock, flags); 128 return written; 129 } 130 131 static int __init opal_init(void) 132 { 133 struct device_node *np, *consoles; 134 135 opal_node = of_find_node_by_path("/ibm,opal"); 136 if (!opal_node) { 137 pr_warn("opal: Node not found\n"); 138 return -ENODEV; 139 } 140 if (firmware_has_feature(FW_FEATURE_OPALv2)) 141 consoles = of_find_node_by_path("/ibm,opal/consoles"); 142 else 143 consoles = of_node_get(opal_node); 144 145 /* Register serial ports */ 146 for_each_child_of_node(consoles, np) { 147 if (strcmp(np->name, "serial")) 148 continue; 149 of_platform_device_create(np, NULL, NULL); 150 } 151 of_node_put(consoles); 152 return 0; 153 } 154 subsys_initcall(opal_init); 155