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