aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-19 13:44:59 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-09-20 02:09:54 -0400
commitdaea1175a9f0f70eab5b33e2827d57ba8c686816 (patch)
treeaac48bd8bdfb9c7ee8e3e5058aea07e04e105da3 /drivers
parent6e35d5dac0c83ebb616ff3b9c2d6155c9a9ccb86 (diff)
powerpc/powernv: Support for OPAL console
This adds a udbg and an hvc console backend for supporting a console using the OPAL console interfaces. On OPAL v1 we have hvc0 mapped to whatever console the system was configured for (network or hvsi serial port) via the service processor. On OPAL v2 we have hvcN mapped to the Nth console provided by OPAL which generally corresponds to: hvc0 : network console (raw protocol) hvc1 : serial port S1 (hvsi) hvc2 : serial port S2 (hvsi) Note: At this point, early debug console only works with OPAL v1 and shouldn't be enabled in a normal kernel. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/tty/hvc/Kconfig9
-rw-r--r--drivers/tty/hvc/Makefile1
-rw-r--r--drivers/tty/hvc/hvc_opal.c424
-rw-r--r--drivers/tty/hvc/hvsi_lib.c4
4 files changed, 436 insertions, 2 deletions
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index e371753ba921..4222035acfb7 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -34,6 +34,15 @@ config HVC_ISERIES
34 help 34 help
35 iSeries machines support a hypervisor virtual console. 35 iSeries machines support a hypervisor virtual console.
36 36
37config HVC_OPAL
38 bool "OPAL Console support"
39 depends on PPC_POWERNV
40 select HVC_DRIVER
41 select HVC_IRQ
42 default y
43 help
44 PowerNV machines running under OPAL need that driver to get a console
45
37config HVC_RTAS 46config HVC_RTAS
38 bool "IBM RTAS Console support" 47 bool "IBM RTAS Console support"
39 depends on PPC_RTAS 48 depends on PPC_RTAS
diff --git a/drivers/tty/hvc/Makefile b/drivers/tty/hvc/Makefile
index e29205316376..89abf40bc73d 100644
--- a/drivers/tty/hvc/Makefile
+++ b/drivers/tty/hvc/Makefile
@@ -1,4 +1,5 @@
1obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o 1obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi_lib.o
2obj-$(CONFIG_HVC_OPAL) += hvc_opal.o hvsi_lib.o
2obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o 3obj-$(CONFIG_HVC_OLD_HVSI) += hvsi.o
3obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o 4obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o
4obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o 5obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c
new file mode 100644
index 000000000000..7b38512d6c41
--- /dev/null
+++ b/drivers/tty/hvc/hvc_opal.c
@@ -0,0 +1,424 @@
1/*
2 * opal driver interface to hvc_console.c
3 *
4 * Copyright 2011 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 */
21
22#undef DEBUG
23
24#include <linux/types.h>
25#include <linux/init.h>
26#include <linux/delay.h>
27#include <linux/slab.h>
28#include <linux/console.h>
29#include <linux/of.h>
30#include <linux/of_platform.h>
31
32#include <asm/hvconsole.h>
33#include <asm/prom.h>
34#include <asm/firmware.h>
35#include <asm/hvsi.h>
36#include <asm/udbg.h>
37#include <asm/opal.h>
38
39#include "hvc_console.h"
40
41static const char hvc_opal_name[] = "hvc_opal";
42
43static struct of_device_id hvc_opal_match[] __devinitdata = {
44 { .name = "serial", .compatible = "ibm,opal-console-raw" },
45 { .name = "serial", .compatible = "ibm,opal-console-hvsi" },
46 { },
47};
48
49typedef enum hv_protocol {
50 HV_PROTOCOL_RAW,
51 HV_PROTOCOL_HVSI
52} hv_protocol_t;
53
54struct hvc_opal_priv {
55 hv_protocol_t proto; /* Raw data or HVSI packets */
56 struct hvsi_priv hvsi; /* HVSI specific data */
57};
58static struct hvc_opal_priv *hvc_opal_privs[MAX_NR_HVC_CONSOLES];
59
60/* For early boot console */
61static struct hvc_opal_priv hvc_opal_boot_priv;
62static u32 hvc_opal_boot_termno;
63
64static const struct hv_ops hvc_opal_raw_ops = {
65 .get_chars = opal_get_chars,
66 .put_chars = opal_put_chars,
67 .notifier_add = notifier_add_irq,
68 .notifier_del = notifier_del_irq,
69 .notifier_hangup = notifier_hangup_irq,
70};
71
72static int hvc_opal_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
73{
74 struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
75
76 if (WARN_ON(!pv))
77 return -ENODEV;
78
79 return hvsilib_get_chars(&pv->hvsi, buf, count);
80}
81
82static int hvc_opal_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
83{
84 struct hvc_opal_priv *pv = hvc_opal_privs[vtermno];
85
86 if (WARN_ON(!pv))
87 return -ENODEV;
88
89 return hvsilib_put_chars(&pv->hvsi, buf, count);
90}
91
92static int hvc_opal_hvsi_open(struct hvc_struct *hp, int data)
93{
94 struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
95 int rc;
96
97 pr_devel("HVSI@%x: do open !\n", hp->vtermno);
98
99 rc = notifier_add_irq(hp, data);
100 if (rc)
101 return rc;
102
103 return hvsilib_open(&pv->hvsi, hp);
104}
105
106static void hvc_opal_hvsi_close(struct hvc_struct *hp, int data)
107{
108 struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
109
110 pr_devel("HVSI@%x: do close !\n", hp->vtermno);
111
112 hvsilib_close(&pv->hvsi, hp);
113
114 notifier_del_irq(hp, data);
115}
116
117void hvc_opal_hvsi_hangup(struct hvc_struct *hp, int data)
118{
119 struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
120
121 pr_devel("HVSI@%x: do hangup !\n", hp->vtermno);
122
123 hvsilib_close(&pv->hvsi, hp);
124
125 notifier_hangup_irq(hp, data);
126}
127
128static int hvc_opal_hvsi_tiocmget(struct hvc_struct *hp)
129{
130 struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
131
132 if (!pv)
133 return -EINVAL;
134 return pv->hvsi.mctrl;
135}
136
137static int hvc_opal_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
138 unsigned int clear)
139{
140 struct hvc_opal_priv *pv = hvc_opal_privs[hp->vtermno];
141
142 pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",
143 hp->vtermno, set, clear);
144
145 if (set & TIOCM_DTR)
146 hvsilib_write_mctrl(&pv->hvsi, 1);
147 else if (clear & TIOCM_DTR)
148 hvsilib_write_mctrl(&pv->hvsi, 0);
149
150 return 0;
151}
152
153static const struct hv_ops hvc_opal_hvsi_ops = {
154 .get_chars = hvc_opal_hvsi_get_chars,
155 .put_chars = hvc_opal_hvsi_put_chars,
156 .notifier_add = hvc_opal_hvsi_open,
157 .notifier_del = hvc_opal_hvsi_close,
158 .notifier_hangup = hvc_opal_hvsi_hangup,
159 .tiocmget = hvc_opal_hvsi_tiocmget,
160 .tiocmset = hvc_opal_hvsi_tiocmset,
161};
162
163static int __devinit hvc_opal_probe(struct platform_device *dev)
164{
165 const struct hv_ops *ops;
166 struct hvc_struct *hp;
167 struct hvc_opal_priv *pv;
168 hv_protocol_t proto;
169 unsigned int termno, boot = 0;
170 const __be32 *reg;
171
172 if (of_device_is_compatible(dev->dev.of_node, "ibm,opal-console-raw")) {
173 proto = HV_PROTOCOL_RAW;
174 ops = &hvc_opal_raw_ops;
175 } else if (of_device_is_compatible(dev->dev.of_node,
176 "ibm,opal-console-hvsi")) {
177 proto = HV_PROTOCOL_HVSI;
178 ops = &hvc_opal_hvsi_ops;
179 } else {
180 pr_err("hvc_opal: Unkown protocol for %s\n",
181 dev->dev.of_node->full_name);
182 return -ENXIO;
183 }
184
185 reg = of_get_property(dev->dev.of_node, "reg", NULL);
186 termno = reg ? be32_to_cpup(reg) : 0;
187
188 /* Is it our boot one ? */
189 if (hvc_opal_privs[termno] == &hvc_opal_boot_priv) {
190 pv = hvc_opal_privs[termno];
191 boot = 1;
192 } else if (hvc_opal_privs[termno] == NULL) {
193 pv = kzalloc(sizeof(struct hvc_opal_priv), GFP_KERNEL);
194 if (!pv)
195 return -ENOMEM;
196 pv->proto = proto;
197 hvc_opal_privs[termno] = pv;
198 if (proto == HV_PROTOCOL_HVSI)
199 hvsilib_init(&pv->hvsi, opal_get_chars, opal_put_chars,
200 termno, 0);
201
202 /* Instanciate now to establish a mapping index==vtermno */
203 hvc_instantiate(termno, termno, ops);
204 } else {
205 pr_err("hvc_opal: Device %s has duplicate terminal number #%d\n",
206 dev->dev.of_node->full_name, termno);
207 return -ENXIO;
208 }
209
210 pr_info("hvc%d: %s protocol on %s%s\n", termno,
211 proto == HV_PROTOCOL_RAW ? "raw" : "hvsi",
212 dev->dev.of_node->full_name,
213 boot ? " (boot console)" : "");
214
215 /* We don't do IRQ yet */
216 hp = hvc_alloc(termno, 0, ops, MAX_VIO_PUT_CHARS);
217 if (IS_ERR(hp))
218 return PTR_ERR(hp);
219 dev_set_drvdata(&dev->dev, hp);
220
221 return 0;
222}
223
224static int __devexit hvc_opal_remove(struct platform_device *dev)
225{
226 struct hvc_struct *hp = dev_get_drvdata(&dev->dev);
227 int rc, termno;
228
229 termno = hp->vtermno;
230 rc = hvc_remove(hp);
231 if (rc == 0) {
232 if (hvc_opal_privs[termno] != &hvc_opal_boot_priv)
233 kfree(hvc_opal_privs[termno]);
234 hvc_opal_privs[termno] = NULL;
235 }
236 return rc;
237}
238
239static struct platform_driver hvc_opal_driver = {
240 .probe = hvc_opal_probe,
241 .remove = __devexit_p(hvc_opal_remove),
242 .driver = {
243 .name = hvc_opal_name,
244 .owner = THIS_MODULE,
245 .of_match_table = hvc_opal_match,
246 }
247};
248
249static int __init hvc_opal_init(void)
250{
251 if (!firmware_has_feature(FW_FEATURE_OPAL))
252 return -ENODEV;
253
254 /* Register as a vio device to receive callbacks */
255 return platform_driver_register(&hvc_opal_driver);
256}
257module_init(hvc_opal_init);
258
259static void __exit hvc_opal_exit(void)
260{
261 platform_driver_unregister(&hvc_opal_driver);
262}
263module_exit(hvc_opal_exit);
264
265static void udbg_opal_putc(char c)
266{
267 unsigned int termno = hvc_opal_boot_termno;
268 int count = -1;
269
270 if (c == '\n')
271 udbg_opal_putc('\r');
272
273 do {
274 switch(hvc_opal_boot_priv.proto) {
275 case HV_PROTOCOL_RAW:
276 count = opal_put_chars(termno, &c, 1);
277 break;
278 case HV_PROTOCOL_HVSI:
279 count = hvc_opal_hvsi_put_chars(termno, &c, 1);
280 break;
281 }
282 } while(count == 0 || count == -EAGAIN);
283}
284
285static int udbg_opal_getc_poll(void)
286{
287 unsigned int termno = hvc_opal_boot_termno;
288 int rc = 0;
289 char c;
290
291 switch(hvc_opal_boot_priv.proto) {
292 case HV_PROTOCOL_RAW:
293 rc = opal_get_chars(termno, &c, 1);
294 break;
295 case HV_PROTOCOL_HVSI:
296 rc = hvc_opal_hvsi_get_chars(termno, &c, 1);
297 break;
298 }
299 if (!rc)
300 return -1;
301 return c;
302}
303
304static int udbg_opal_getc(void)
305{
306 int ch;
307 for (;;) {
308 ch = udbg_opal_getc_poll();
309 if (ch == -1) {
310 /* This shouldn't be needed...but... */
311 volatile unsigned long delay;
312 for (delay=0; delay < 2000000; delay++)
313 ;
314 } else {
315 return ch;
316 }
317 }
318}
319
320static void udbg_init_opal_common(void)
321{
322 udbg_putc = udbg_opal_putc;
323 udbg_getc = udbg_opal_getc;
324 udbg_getc_poll = udbg_opal_getc_poll;
325 tb_ticks_per_usec = 0x200; /* Make udelay not suck */
326}
327
328void __init hvc_opal_init_early(void)
329{
330 struct device_node *stdout_node = NULL;
331 const u32 *termno;
332 const char *name = NULL;
333 const struct hv_ops *ops;
334 u32 index;
335
336 /* find the boot console from /chosen/stdout */
337 if (of_chosen)
338 name = of_get_property(of_chosen, "linux,stdout-path", NULL);
339 if (name) {
340 stdout_node = of_find_node_by_path(name);
341 if (!stdout_node) {
342 pr_err("hvc_opal: Failed to locate default console!\n");
343 return;
344 }
345 } else {
346 struct device_node *opal, *np;
347
348 /* Current OPAL takeover doesn't provide the stdout
349 * path, so we hard wire it
350 */
351 opal = of_find_node_by_path("/ibm,opal/consoles");
352 if (opal)
353 pr_devel("hvc_opal: Found consoles in new location\n");
354 if (!opal) {
355 opal = of_find_node_by_path("/ibm,opal");
356 if (opal)
357 pr_devel("hvc_opal: "
358 "Found consoles in old location\n");
359 }
360 if (!opal)
361 return;
362 for_each_child_of_node(opal, np) {
363 if (!strcmp(np->name, "serial")) {
364 stdout_node = np;
365 break;
366 }
367 }
368 of_node_put(opal);
369 }
370 if (!stdout_node)
371 return;
372 termno = of_get_property(stdout_node, "reg", NULL);
373 index = termno ? *termno : 0;
374 if (index >= MAX_NR_HVC_CONSOLES)
375 return;
376 hvc_opal_privs[index] = &hvc_opal_boot_priv;
377
378 /* Check the protocol */
379 if (of_device_is_compatible(stdout_node, "ibm,opal-console-raw")) {
380 hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
381 ops = &hvc_opal_raw_ops;
382 pr_devel("hvc_opal: Found RAW console\n");
383 }
384 else if (of_device_is_compatible(stdout_node,"ibm,opal-console-hvsi")) {
385 hvc_opal_boot_priv.proto = HV_PROTOCOL_HVSI;
386 ops = &hvc_opal_hvsi_ops;
387 hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars,
388 opal_put_chars, index, 1);
389 /* HVSI, perform the handshake now */
390 hvsilib_establish(&hvc_opal_boot_priv.hvsi);
391 pr_devel("hvc_opal: Found HVSI console\n");
392 } else
393 goto out;
394 hvc_opal_boot_termno = index;
395 udbg_init_opal_common();
396 add_preferred_console("hvc", index, NULL);
397 hvc_instantiate(index, index, ops);
398out:
399 of_node_put(stdout_node);
400}
401
402#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_RAW
403void __init udbg_init_debug_opal(void)
404{
405 u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
406 hvc_opal_privs[index] = &hvc_opal_boot_priv;
407 hvc_opal_boot_priv.proto = HV_PROTOCOL_RAW;
408 hvc_opal_boot_termno = index;
409 udbg_init_opal_common();
410}
411#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_RAW */
412
413#ifdef CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI
414void __init udbg_init_debug_opal_hvsi(void)
415{
416 u32 index = CONFIG_PPC_EARLY_DEBUG_OPAL_VTERMNO;
417 hvc_opal_privs[index] = &hvc_opal_boot_priv;
418 hvc_opal_boot_termno = index;
419 udbg_init_opal_common();
420 hvsilib_init(&hvc_opal_boot_priv.hvsi, opal_get_chars, opal_put_chars,
421 index, 1);
422 hvsilib_establish(&hvc_opal_boot_priv.hvsi);
423}
424#endif /* CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI */
diff --git a/drivers/tty/hvc/hvsi_lib.c b/drivers/tty/hvc/hvsi_lib.c
index bd9b09827b24..6f4dd83d8695 100644
--- a/drivers/tty/hvc/hvsi_lib.c
+++ b/drivers/tty/hvc/hvsi_lib.c
@@ -183,7 +183,7 @@ int hvsilib_get_chars(struct hvsi_priv *pv, char *buf, int count)
183 unsigned int tries, read = 0; 183 unsigned int tries, read = 0;
184 184
185 if (WARN_ON(!pv)) 185 if (WARN_ON(!pv))
186 return 0; 186 return -ENXIO;
187 187
188 /* If we aren't open, don't do anything in order to avoid races 188 /* If we aren't open, don't do anything in order to avoid races
189 * with connection establishment. The hvc core will call this 189 * with connection establishment. The hvc core will call this
@@ -234,7 +234,7 @@ int hvsilib_put_chars(struct hvsi_priv *pv, const char *buf, int count)
234 int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA); 234 int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
235 235
236 if (WARN_ON(!pv)) 236 if (WARN_ON(!pv))
237 return 0; 237 return -ENODEV;
238 238
239 dp.hdr.type = VS_DATA_PACKET_HEADER; 239 dp.hdr.type = VS_DATA_PACKET_HEADER;
240 dp.hdr.len = adjcount + sizeof(struct hvsi_header); 240 dp.hdr.len = adjcount + sizeof(struct hvsi_header);