blob: d3a9dd739da31705b2441dca5379ed9b76725d4d [file] [log] [blame]
David S. Miller02fd4732006-02-11 02:25:21 -08001/* sunhv.c: Serial driver for SUN4V hypervisor console.
2 *
3 * Copyright (C) 2006 David S. Miller (davem@davemloft.net)
4 */
5
6#include <linux/module.h>
7#include <linux/kernel.h>
8#include <linux/errno.h>
9#include <linux/tty.h>
10#include <linux/tty_flip.h>
11#include <linux/major.h>
12#include <linux/circ_buf.h>
13#include <linux/serial.h>
14#include <linux/sysrq.h>
15#include <linux/console.h>
16#include <linux/spinlock.h>
17#include <linux/slab.h>
18#include <linux/delay.h>
19#include <linux/init.h>
20
21#include <asm/hypervisor.h>
22#include <asm/spitfire.h>
David S. Millerf4266ab2006-02-13 20:43:02 -080023#include <asm/vdev.h>
24#include <asm/irq.h>
David S. Miller02fd4732006-02-11 02:25:21 -080025
26#if defined(CONFIG_MAGIC_SYSRQ)
27#define SUPPORT_SYSRQ
28#endif
29
30#include <linux/serial_core.h>
31
32#include "suncore.h"
33
34#define CON_BREAK ((long)-1)
35#define CON_HUP ((long)-2)
36
37static inline long hypervisor_con_getchar(long *status)
38{
39 register unsigned long func asm("%o5");
40 register unsigned long arg0 asm("%o0");
41 register unsigned long arg1 asm("%o1");
42
43 func = HV_FAST_CONS_GETCHAR;
44 arg0 = 0;
45 arg1 = 0;
46 __asm__ __volatile__("ta %6"
47 : "=&r" (func), "=&r" (arg0), "=&r" (arg1)
48 : "0" (func), "1" (arg0), "2" (arg1),
49 "i" (HV_FAST_TRAP));
50
51 *status = arg0;
52
53 return (long) arg1;
54}
55
56static inline long hypervisor_con_putchar(long ch)
57{
58 register unsigned long func asm("%o5");
59 register unsigned long arg0 asm("%o0");
60
61 func = HV_FAST_CONS_PUTCHAR;
62 arg0 = ch;
63 __asm__ __volatile__("ta %4"
64 : "=&r" (func), "=&r" (arg0)
65 : "0" (func), "1" (arg0), "i" (HV_FAST_TRAP));
66
67 return (long) arg0;
68}
69
70#define IGNORE_BREAK 0x1
71#define IGNORE_ALL 0x2
72
73static int hung_up = 0;
74
75static struct tty_struct *receive_chars(struct uart_port *port, struct pt_regs *regs)
76{
77 struct tty_struct *tty = NULL;
78 int saw_console_brk = 0;
79 int limit = 10000;
80
81 if (port->info != NULL) /* Unopened serial console */
82 tty = port->info->tty;
83
84 while (limit-- > 0) {
85 long status;
86 long c = hypervisor_con_getchar(&status);
87 unsigned char flag;
88
89 if (status == HV_EWOULDBLOCK)
90 break;
91
92 if (c == CON_BREAK) {
93 saw_console_brk = 1;
94 c = 0;
95 }
96
97 if (c == CON_HUP) {
98 hung_up = 1;
99 uart_handle_dcd_change(port, 0);
100 } else if (hung_up) {
101 hung_up = 0;
102 uart_handle_dcd_change(port, 1);
103 }
104
105 if (tty == NULL) {
106 uart_handle_sysrq_char(port, c, regs);
107 continue;
108 }
109
110 flag = TTY_NORMAL;
111 port->icount.rx++;
112 if (c == CON_BREAK) {
113 port->icount.brk++;
114 if (uart_handle_break(port))
115 continue;
116 flag = TTY_BREAK;
117 }
118
119 if (uart_handle_sysrq_char(port, c, regs))
120 continue;
121
122 if ((port->ignore_status_mask & IGNORE_ALL) ||
123 ((port->ignore_status_mask & IGNORE_BREAK) &&
124 (c == CON_BREAK)))
125 continue;
126
127 tty_insert_flip_char(tty, c, flag);
128 }
129
130 if (saw_console_brk)
131 sun_do_break();
132
133 return tty;
134}
135
136static void transmit_chars(struct uart_port *port)
137{
138 struct circ_buf *xmit = &port->info->xmit;
139
140 if (uart_circ_empty(xmit) || uart_tx_stopped(port))
141 return;
142
143 while (!uart_circ_empty(xmit)) {
144 long status = hypervisor_con_putchar(xmit->buf[xmit->tail]);
145
146 if (status != HV_EOK)
147 break;
148
149 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
150 port->icount.tx++;
151 }
152
153 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
154 uart_write_wakeup(port);
155}
156
157static irqreturn_t sunhv_interrupt(int irq, void *dev_id, struct pt_regs *regs)
158{
159 struct uart_port *port = dev_id;
160 struct tty_struct *tty;
161 unsigned long flags;
162
163 spin_lock_irqsave(&port->lock, flags);
164 tty = receive_chars(port, regs);
165 transmit_chars(port);
166 spin_unlock_irqrestore(&port->lock, flags);
167
168 if (tty)
169 tty_flip_buffer_push(tty);
170
171 return IRQ_HANDLED;
172}
173
174/* port->lock is not held. */
175static unsigned int sunhv_tx_empty(struct uart_port *port)
176{
177 /* Transmitter is always empty for us. If the circ buffer
178 * is non-empty or there is an x_char pending, our caller
179 * will do the right thing and ignore what we return here.
180 */
181 return TIOCSER_TEMT;
182}
183
184/* port->lock held by caller. */
185static void sunhv_set_mctrl(struct uart_port *port, unsigned int mctrl)
186{
187 return;
188}
189
190/* port->lock is held by caller and interrupts are disabled. */
191static unsigned int sunhv_get_mctrl(struct uart_port *port)
192{
193 return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
194}
195
196/* port->lock held by caller. */
197static void sunhv_stop_tx(struct uart_port *port)
198{
199 return;
200}
201
202/* port->lock held by caller. */
203static void sunhv_start_tx(struct uart_port *port)
204{
205 struct circ_buf *xmit = &port->info->xmit;
206 unsigned long flags;
207
208 spin_lock_irqsave(&port->lock, flags);
209
210 while (!uart_circ_empty(xmit)) {
211 long status = hypervisor_con_putchar(xmit->buf[xmit->tail]);
212
213 if (status != HV_EOK)
214 break;
215
216 xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
217 port->icount.tx++;
218 }
219
220 spin_unlock_irqrestore(&port->lock, flags);
221}
222
223/* port->lock is not held. */
224static void sunhv_send_xchar(struct uart_port *port, char ch)
225{
226 unsigned long flags;
227 int limit = 10000;
228
229 spin_lock_irqsave(&port->lock, flags);
230
231 while (limit-- > 0) {
232 long status = hypervisor_con_putchar(ch);
233 if (status == HV_EOK)
234 break;
235 }
236
237 spin_unlock_irqrestore(&port->lock, flags);
238}
239
240/* port->lock held by caller. */
241static void sunhv_stop_rx(struct uart_port *port)
242{
243}
244
245/* port->lock held by caller. */
246static void sunhv_enable_ms(struct uart_port *port)
247{
248}
249
250/* port->lock is not held. */
251static void sunhv_break_ctl(struct uart_port *port, int break_state)
252{
253 if (break_state) {
254 unsigned long flags;
255 int limit = 10000;
256
257 spin_lock_irqsave(&port->lock, flags);
258
259 while (limit-- > 0) {
260 long status = hypervisor_con_putchar(CON_BREAK);
261 if (status == HV_EOK)
262 break;
263 }
264
265 spin_unlock_irqrestore(&port->lock, flags);
266 }
267}
268
269/* port->lock is not held. */
270static int sunhv_startup(struct uart_port *port)
271{
272 return 0;
273}
274
275/* port->lock is not held. */
276static void sunhv_shutdown(struct uart_port *port)
277{
278}
279
280/* port->lock is not held. */
281static void sunhv_set_termios(struct uart_port *port, struct termios *termios,
282 struct termios *old)
283{
284 unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);
285 unsigned int quot = uart_get_divisor(port, baud);
286 unsigned int iflag, cflag;
287 unsigned long flags;
288
289 spin_lock_irqsave(&port->lock, flags);
290
291 iflag = termios->c_iflag;
292 cflag = termios->c_cflag;
293
294 port->ignore_status_mask = 0;
295 if (iflag & IGNBRK)
296 port->ignore_status_mask |= IGNORE_BREAK;
297 if ((cflag & CREAD) == 0)
298 port->ignore_status_mask |= IGNORE_ALL;
299
300 /* XXX */
301 uart_update_timeout(port, cflag,
302 (port->uartclk / (16 * quot)));
303
304 spin_unlock_irqrestore(&port->lock, flags);
305}
306
307static const char *sunhv_type(struct uart_port *port)
308{
309 return "SUN4V HCONS";
310}
311
312static void sunhv_release_port(struct uart_port *port)
313{
314}
315
316static int sunhv_request_port(struct uart_port *port)
317{
318 return 0;
319}
320
321static void sunhv_config_port(struct uart_port *port, int flags)
322{
323}
324
325static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser)
326{
327 return -EINVAL;
328}
329
330static struct uart_ops sunhv_pops = {
331 .tx_empty = sunhv_tx_empty,
332 .set_mctrl = sunhv_set_mctrl,
333 .get_mctrl = sunhv_get_mctrl,
334 .stop_tx = sunhv_stop_tx,
335 .start_tx = sunhv_start_tx,
336 .send_xchar = sunhv_send_xchar,
337 .stop_rx = sunhv_stop_rx,
338 .enable_ms = sunhv_enable_ms,
339 .break_ctl = sunhv_break_ctl,
340 .startup = sunhv_startup,
341 .shutdown = sunhv_shutdown,
342 .set_termios = sunhv_set_termios,
343 .type = sunhv_type,
344 .release_port = sunhv_release_port,
345 .request_port = sunhv_request_port,
346 .config_port = sunhv_config_port,
347 .verify_port = sunhv_verify_port,
348};
349
350static struct uart_driver sunhv_reg = {
351 .owner = THIS_MODULE,
352 .driver_name = "serial",
353 .devfs_name = "tts/",
354 .dev_name = "ttyS",
355 .major = TTY_MAJOR,
356};
357
358static struct uart_port *sunhv_port;
359
360static inline void sunhv_console_putchar(struct uart_port *port, char c)
361{
362 unsigned long flags;
363 int limit = 10000;
364
365 spin_lock_irqsave(&port->lock, flags);
366
367 while (limit-- > 0) {
368 long status = hypervisor_con_putchar(c);
369 if (status == HV_EOK)
370 break;
371 }
372
373 spin_unlock_irqrestore(&port->lock, flags);
374}
375
376static void sunhv_console_write(struct console *con, const char *s, unsigned n)
377{
378 struct uart_port *port = sunhv_port;
379 int i;
380
381 for (i = 0; i < n; i++) {
382 if (*s == '\n')
383 sunhv_console_putchar(port, '\r');
384 sunhv_console_putchar(port, *s++);
385 }
386}
387
388static int sunhv_console_setup(struct console *con, char *options)
389{
390 return 0;
391}
392
393static struct console sunhv_console = {
394 .name = "ttyS",
395 .write = sunhv_console_write,
396 .device = uart_console_device,
397 .setup = sunhv_console_setup,
398 .flags = CON_PRINTBUFFER,
399 .index = -1,
400 .data = &sunhv_reg,
401};
402
403static void __init sunhv_console_init(void)
404{
405 if (con_is_present())
406 return;
407
408 sunhv_console.index = 0;
409 register_console(&sunhv_console);
410}
411
David S. Millerf4266ab2006-02-13 20:43:02 -0800412static int __init hv_console_compatible(char *buf, int len)
413{
414 while (len) {
415 int this_len;
416
417 if (!strcmp(buf, "qcn"))
418 return 1;
419
420 this_len = strlen(buf) + 1;
421
422 buf += this_len;
423 len -= this_len;
424 }
425
426 return 0;
427}
428
429static unsigned int __init get_interrupt(void)
430{
431 const char *cons_str = "console";
432 const char *compat_str = "compatible";
433 int node = prom_getchild(sun4v_vdev_root);
434 unsigned int irq;
435 char buf[64];
436 int err, len;
437
438 node = prom_searchsiblings(node, cons_str);
439 if (!node)
440 return 0;
441
442 len = prom_getproplen(node, compat_str);
443 if (len == 0 || len == -1)
444 return 0;
445
446 err = prom_getproperty(node, compat_str, buf, 64);
447 if (err == -1)
448 return 0;
449
450 if (!hv_console_compatible(buf, len))
451 return 0;
452
453 /* Ok, the this is the OBP node for the sun4v hypervisor
454 * console device. Decode the interrupt.
455 */
456 err = prom_getproperty(node, "interrupts",
457 (char *) &irq, sizeof(irq));
458 if (err == -1)
459 return 0;
460
461 return sun4v_build_irq(sun4v_vdev_devhandle, irq, 4, 0);
462}
463
464static u32 sunhv_irq;
465
David S. Miller02fd4732006-02-11 02:25:21 -0800466static int __init sunhv_init(void)
467{
468 struct uart_port *port;
469 int ret;
470
471 if (tlb_type != hypervisor)
472 return -ENODEV;
473
David S. Millerf4266ab2006-02-13 20:43:02 -0800474 sunhv_irq = get_interrupt();
475 if (!sunhv_irq)
476 return -ENODEV;
477
David S. Miller02fd4732006-02-11 02:25:21 -0800478 port = kmalloc(sizeof(struct uart_port), GFP_KERNEL);
479 if (unlikely(!port))
480 return -ENOMEM;
481
482 port->line = 0;
483 port->ops = &sunhv_pops;
484 port->type = PORT_SUNHV;
485 port->uartclk = ( 29491200 / 16 ); /* arbitrary */
486
David S. Millerf4266ab2006-02-13 20:43:02 -0800487 if (request_irq(sunhv_irq, sunhv_interrupt,
David S. Miller02fd4732006-02-11 02:25:21 -0800488 SA_SHIRQ, "serial(sunhv)", port)) {
David S. Millerf4266ab2006-02-13 20:43:02 -0800489 printk("sunhv: Cannot get IRQ %x\n", sunhv_irq);
David S. Miller02fd4732006-02-11 02:25:21 -0800490 kfree(port);
491 return -ENODEV;
492 }
493
David S. Millerf4266ab2006-02-13 20:43:02 -0800494 printk("SUNHV: SUN4V virtual console, IRQ[%08x]\n",
495 sunhv_irq);
496
David S. Miller02fd4732006-02-11 02:25:21 -0800497 sunhv_reg.minor = sunserial_current_minor;
498 sunhv_reg.nr = 1;
499 sunhv_reg.cons = &sunhv_console;
500
501 ret = uart_register_driver(&sunhv_reg);
502 if (ret < 0) {
David S. Millerf4266ab2006-02-13 20:43:02 -0800503 free_irq(sunhv_irq, up);
David S. Miller02fd4732006-02-11 02:25:21 -0800504 kfree(port);
505
506 return ret;
507 }
508
509 sunhv_port = port;
510
511 sunserial_current_minor += 1;
512
513 sunhv_console_init();
514
515 uart_add_one_port(&sunhv_reg, port);
516
517 return 0;
518}
519
520static void __exit sunhv_exit(void)
521{
522 struct uart_port *port = sunhv_port;
523
524 BUG_ON(!port);
525
526 uart_remove_one_port(&sunhv_reg, port);
David S. Millerf4266ab2006-02-13 20:43:02 -0800527 free_irq(sunhv_irq, port);
David S. Miller02fd4732006-02-11 02:25:21 -0800528
529 sunserial_current_minor -= 1;
530
531 uart_unregister_driver(&sunhv_reg);
532
533 kfree(sunhv_port);
534 sunhv_port = NULL;
535}
536
537module_init(sunhv_init);
538module_exit(sunhv_exit);
539
540MODULE_AUTHOR("David S. Miller");
541MODULE_DESCRIPTION("SUN4V Hypervisor console driver")
542MODULE_LICENSE("GPL");