blob: 27907cb18b8a8de95920e7b74f143b0945fd18ee [file] [log] [blame]
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +00001/*
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>
Benjamin Herrenschmidta125e092011-09-19 17:45:03 +000017#include <linux/interrupt.h>
Benjamin Herrenschmidt73ed1482013-05-10 16:59:18 +100018#include <linux/slab.h>
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +000019#include <asm/opal.h>
20#include <asm/firmware.h>
21
22#include "powernv.h"
23
24struct opal {
25 u64 base;
26 u64 entry;
27} opal;
28
29static struct device_node *opal_node;
30static DEFINE_SPINLOCK(opal_write_lock);
Benjamin Herrenschmidted79ba92011-09-19 17:45:04 +000031extern u64 opal_mc_secondary_handler[];
Benjamin Herrenschmidt73ed1482013-05-10 16:59:18 +100032static unsigned int *opal_irqs;
33static unsigned int opal_irq_count;
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +000034
35int __init early_init_dt_scan_opal(unsigned long node,
36 const char *uname, int depth, void *data)
37{
38 const void *basep, *entryp;
39 unsigned long basesz, entrysz;
40
41 if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
42 return 0;
43
44 basep = of_get_flat_dt_prop(node, "opal-base-address", &basesz);
45 entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz);
46
47 if (!basep || !entryp)
48 return 1;
49
50 opal.base = of_read_number(basep, basesz/4);
51 opal.entry = of_read_number(entryp, entrysz/4);
52
53 pr_debug("OPAL Base = 0x%llx (basep=%p basesz=%ld)\n",
54 opal.base, basep, basesz);
55 pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%ld)\n",
56 opal.entry, entryp, entrysz);
57
58 powerpc_firmware_features |= FW_FEATURE_OPAL;
59 if (of_flat_dt_is_compatible(node, "ibm,opal-v2")) {
60 powerpc_firmware_features |= FW_FEATURE_OPALv2;
61 printk("OPAL V2 detected !\n");
62 } else {
63 printk("OPAL V1 detected !\n");
64 }
65
Jeremy Kerrc4463b32013-05-01 22:31:50 +000066 return 1;
67}
68
69static int __init opal_register_exception_handlers(void)
70{
71 u64 glue;
72
73 if (!(powerpc_firmware_features & FW_FEATURE_OPAL))
74 return -ENODEV;
75
Benjamin Herrenschmidted79ba92011-09-19 17:45:04 +000076 /* Hookup some exception handlers. We use the fwnmi area at 0x7000
77 * to provide the glue space to OPAL
78 */
79 glue = 0x7000;
80 opal_register_exception_handler(OPAL_MACHINE_CHECK_HANDLER,
81 __pa(opal_mc_secondary_handler[0]),
82 glue);
83 glue += 128;
84 opal_register_exception_handler(OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
85 0, glue);
86 glue += 128;
87 opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
88
Jeremy Kerrc4463b32013-05-01 22:31:50 +000089 return 0;
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +000090}
91
Jeremy Kerrc4463b32013-05-01 22:31:50 +000092early_initcall(opal_register_exception_handlers);
93
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +000094int opal_get_chars(uint32_t vtermno, char *buf, int count)
95{
96 s64 len, rc;
97 u64 evt;
98
99 if (!opal.entry)
Benjamin Herrenschmidtdaea1172011-09-19 17:44:59 +0000100 return -ENODEV;
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000101 opal_poll_events(&evt);
102 if ((evt & OPAL_EVENT_CONSOLE_INPUT) == 0)
103 return 0;
104 len = count;
105 rc = opal_console_read(vtermno, &len, buf);
106 if (rc == OPAL_SUCCESS)
107 return len;
108 return 0;
109}
110
111int opal_put_chars(uint32_t vtermno, const char *data, int total_len)
112{
113 int written = 0;
Benjamin Herrenschmidtdaea1172011-09-19 17:44:59 +0000114 s64 len, rc;
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000115 unsigned long flags;
116 u64 evt;
117
118 if (!opal.entry)
Benjamin Herrenschmidtdaea1172011-09-19 17:44:59 +0000119 return -ENODEV;
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000120
121 /* We want put_chars to be atomic to avoid mangling of hvsi
122 * packets. To do that, we first test for room and return
Benjamin Herrenschmidtdaea1172011-09-19 17:44:59 +0000123 * -EAGAIN if there isn't enough.
124 *
125 * Unfortunately, opal_console_write_buffer_space() doesn't
126 * appear to work on opal v1, so we just assume there is
127 * enough room and be done with it
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000128 */
129 spin_lock_irqsave(&opal_write_lock, flags);
Benjamin Herrenschmidtdaea1172011-09-19 17:44:59 +0000130 if (firmware_has_feature(FW_FEATURE_OPALv2)) {
131 rc = opal_console_write_buffer_space(vtermno, &len);
132 if (rc || len < total_len) {
133 spin_unlock_irqrestore(&opal_write_lock, flags);
134 /* Closed -> drop characters */
135 if (rc)
136 return total_len;
137 opal_poll_events(&evt);
138 return -EAGAIN;
139 }
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000140 }
141
142 /* We still try to handle partial completions, though they
143 * should no longer happen.
144 */
Benjamin Herrenschmidtdaea1172011-09-19 17:44:59 +0000145 rc = OPAL_BUSY;
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000146 while(total_len > 0 && (rc == OPAL_BUSY ||
147 rc == OPAL_BUSY_EVENT || rc == OPAL_SUCCESS)) {
148 len = total_len;
149 rc = opal_console_write(vtermno, &len, data);
Benjamin Herrenschmidt1de14552013-05-08 14:14:26 +1000150
151 /* Closed or other error drop */
152 if (rc != OPAL_SUCCESS && rc != OPAL_BUSY &&
153 rc != OPAL_BUSY_EVENT) {
154 written = total_len;
155 break;
156 }
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000157 if (rc == OPAL_SUCCESS) {
158 total_len -= len;
159 data += len;
160 written += len;
161 }
162 /* This is a bit nasty but we need that for the console to
163 * flush when there aren't any interrupts. We will clean
164 * things a bit later to limit that to synchronous path
165 * such as the kernel console and xmon/udbg
166 */
167 do
168 opal_poll_events(&evt);
169 while(rc == OPAL_SUCCESS && (evt & OPAL_EVENT_CONSOLE_OUTPUT));
170 }
171 spin_unlock_irqrestore(&opal_write_lock, flags);
172 return written;
173}
174
Benjamin Herrenschmidted79ba92011-09-19 17:45:04 +0000175int opal_machine_check(struct pt_regs *regs)
176{
177 struct opal_machine_check_event *opal_evt = get_paca()->opal_mc_evt;
178 struct opal_machine_check_event evt;
179 const char *level, *sevstr, *subtype;
180 static const char *opal_mc_ue_types[] = {
181 "Indeterminate",
182 "Instruction fetch",
183 "Page table walk ifetch",
184 "Load/Store",
185 "Page table walk Load/Store",
186 };
187 static const char *opal_mc_slb_types[] = {
188 "Indeterminate",
189 "Parity",
190 "Multihit",
191 };
192 static const char *opal_mc_erat_types[] = {
193 "Indeterminate",
194 "Parity",
195 "Multihit",
196 };
197 static const char *opal_mc_tlb_types[] = {
198 "Indeterminate",
199 "Parity",
200 "Multihit",
201 };
202
203 /* Copy the event structure and release the original */
204 evt = *opal_evt;
205 opal_evt->in_use = 0;
206
207 /* Print things out */
208 if (evt.version != OpalMCE_V1) {
209 pr_err("Machine Check Exception, Unknown event version %d !\n",
210 evt.version);
211 return 0;
212 }
213 switch(evt.severity) {
214 case OpalMCE_SEV_NO_ERROR:
215 level = KERN_INFO;
216 sevstr = "Harmless";
217 break;
218 case OpalMCE_SEV_WARNING:
219 level = KERN_WARNING;
220 sevstr = "";
221 break;
222 case OpalMCE_SEV_ERROR_SYNC:
223 level = KERN_ERR;
224 sevstr = "Severe";
225 break;
226 case OpalMCE_SEV_FATAL:
227 default:
228 level = KERN_ERR;
229 sevstr = "Fatal";
230 break;
231 }
232
233 printk("%s%s Machine check interrupt [%s]\n", level, sevstr,
234 evt.disposition == OpalMCE_DISPOSITION_RECOVERED ?
235 "Recovered" : "[Not recovered");
236 printk("%s Initiator: %s\n", level,
237 evt.initiator == OpalMCE_INITIATOR_CPU ? "CPU" : "Unknown");
238 switch(evt.error_type) {
239 case OpalMCE_ERROR_TYPE_UE:
240 subtype = evt.u.ue_error.ue_error_type <
241 ARRAY_SIZE(opal_mc_ue_types) ?
242 opal_mc_ue_types[evt.u.ue_error.ue_error_type]
243 : "Unknown";
244 printk("%s Error type: UE [%s]\n", level, subtype);
245 if (evt.u.ue_error.effective_address_provided)
246 printk("%s Effective address: %016llx\n",
247 level, evt.u.ue_error.effective_address);
248 if (evt.u.ue_error.physical_address_provided)
249 printk("%s Physial address: %016llx\n",
250 level, evt.u.ue_error.physical_address);
251 break;
252 case OpalMCE_ERROR_TYPE_SLB:
253 subtype = evt.u.slb_error.slb_error_type <
254 ARRAY_SIZE(opal_mc_slb_types) ?
255 opal_mc_slb_types[evt.u.slb_error.slb_error_type]
256 : "Unknown";
257 printk("%s Error type: SLB [%s]\n", level, subtype);
258 if (evt.u.slb_error.effective_address_provided)
259 printk("%s Effective address: %016llx\n",
260 level, evt.u.slb_error.effective_address);
261 break;
262 case OpalMCE_ERROR_TYPE_ERAT:
263 subtype = evt.u.erat_error.erat_error_type <
264 ARRAY_SIZE(opal_mc_erat_types) ?
265 opal_mc_erat_types[evt.u.erat_error.erat_error_type]
266 : "Unknown";
267 printk("%s Error type: ERAT [%s]\n", level, subtype);
268 if (evt.u.erat_error.effective_address_provided)
269 printk("%s Effective address: %016llx\n",
270 level, evt.u.erat_error.effective_address);
271 break;
272 case OpalMCE_ERROR_TYPE_TLB:
273 subtype = evt.u.tlb_error.tlb_error_type <
274 ARRAY_SIZE(opal_mc_tlb_types) ?
275 opal_mc_tlb_types[evt.u.tlb_error.tlb_error_type]
276 : "Unknown";
277 printk("%s Error type: TLB [%s]\n", level, subtype);
278 if (evt.u.tlb_error.effective_address_provided)
279 printk("%s Effective address: %016llx\n",
280 level, evt.u.tlb_error.effective_address);
281 break;
282 default:
283 case OpalMCE_ERROR_TYPE_UNKNOWN:
284 printk("%s Error type: Unknown\n", level);
285 break;
286 }
287 return evt.severity == OpalMCE_SEV_FATAL ? 0 : 1;
288}
289
Benjamin Herrenschmidta125e092011-09-19 17:45:03 +0000290static irqreturn_t opal_interrupt(int irq, void *data)
291{
292 uint64_t events;
293
294 opal_handle_interrupt(virq_to_hw(irq), &events);
295
296 /* XXX TODO: Do something with the events */
297
298 return IRQ_HANDLED;
299}
300
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000301static int __init opal_init(void)
302{
303 struct device_node *np, *consoles;
Benjamin Herrenschmidta125e092011-09-19 17:45:03 +0000304 const u32 *irqs;
305 int rc, i, irqlen;
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000306
307 opal_node = of_find_node_by_path("/ibm,opal");
308 if (!opal_node) {
309 pr_warn("opal: Node not found\n");
310 return -ENODEV;
311 }
312 if (firmware_has_feature(FW_FEATURE_OPALv2))
313 consoles = of_find_node_by_path("/ibm,opal/consoles");
314 else
315 consoles = of_node_get(opal_node);
316
317 /* Register serial ports */
318 for_each_child_of_node(consoles, np) {
319 if (strcmp(np->name, "serial"))
320 continue;
321 of_platform_device_create(np, NULL, NULL);
322 }
323 of_node_put(consoles);
Benjamin Herrenschmidta125e092011-09-19 17:45:03 +0000324
325 /* Find all OPAL interrupts and request them */
326 irqs = of_get_property(opal_node, "opal-interrupts", &irqlen);
327 pr_debug("opal: Found %d interrupts reserved for OPAL\n",
328 irqs ? (irqlen / 4) : 0);
Benjamin Herrenschmidt73ed1482013-05-10 16:59:18 +1000329 opal_irq_count = irqlen / 4;
330 opal_irqs = kzalloc(opal_irq_count * sizeof(unsigned int), GFP_KERNEL);
Benjamin Herrenschmidta125e092011-09-19 17:45:03 +0000331 for (i = 0; irqs && i < (irqlen / 4); i++, irqs++) {
332 unsigned int hwirq = be32_to_cpup(irqs);
333 unsigned int irq = irq_create_mapping(NULL, hwirq);
334 if (irq == NO_IRQ) {
335 pr_warning("opal: Failed to map irq 0x%x\n", hwirq);
336 continue;
337 }
338 rc = request_irq(irq, opal_interrupt, 0, "opal", NULL);
339 if (rc)
340 pr_warning("opal: Error %d requesting irq %d"
341 " (0x%x)\n", rc, irq, hwirq);
Benjamin Herrenschmidt73ed1482013-05-10 16:59:18 +1000342 opal_irqs[i] = irq;
Benjamin Herrenschmidta125e092011-09-19 17:45:03 +0000343 }
Benjamin Herrenschmidt14a43e62011-09-19 17:44:57 +0000344 return 0;
345}
346subsys_initcall(opal_init);
Benjamin Herrenschmidt73ed1482013-05-10 16:59:18 +1000347
348void opal_shutdown(void)
349{
350 unsigned int i;
351
352 for (i = 0; i < opal_irq_count; i++) {
353 if (opal_irqs[i])
354 free_irq(opal_irqs[i], 0);
355 opal_irqs[i] = 0;
356 }
357}