blob: 71c70d7a998ccd0586296132b1b735116e835a0a [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;
David S. Millerd5a2aa22006-02-13 21:28:40 -0800363 int limit = 1000000;
David S. Miller02fd4732006-02-11 02:25:21 -0800364
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;
David S. Millerd5a2aa22006-02-13 21:28:40 -0800371 udelay(2);
David S. Miller02fd4732006-02-11 02:25:21 -0800372 }
373
374 spin_unlock_irqrestore(&port->lock, flags);
375}
376
377static void sunhv_console_write(struct console *con, const char *s, unsigned n)
378{
379 struct uart_port *port = sunhv_port;
380 int i;
381
382 for (i = 0; i < n; i++) {
383 if (*s == '\n')
384 sunhv_console_putchar(port, '\r');
385 sunhv_console_putchar(port, *s++);
386 }
387}
388
David S. Miller02fd4732006-02-11 02:25:21 -0800389static struct console sunhv_console = {
David S. Millerd5a2aa22006-02-13 21:28:40 -0800390 .name = "ttyHV",
David S. Miller02fd4732006-02-11 02:25:21 -0800391 .write = sunhv_console_write,
392 .device = uart_console_device,
David S. Miller02fd4732006-02-11 02:25:21 -0800393 .flags = CON_PRINTBUFFER,
394 .index = -1,
395 .data = &sunhv_reg,
396};
397
David S. Millerd5a2aa22006-02-13 21:28:40 -0800398static inline struct console *SUNHV_CONSOLE(void)
David S. Miller02fd4732006-02-11 02:25:21 -0800399{
400 if (con_is_present())
David S. Millerd5a2aa22006-02-13 21:28:40 -0800401 return NULL;
David S. Miller02fd4732006-02-11 02:25:21 -0800402
403 sunhv_console.index = 0;
David S. Millerd5a2aa22006-02-13 21:28:40 -0800404
405 return &sunhv_console;
David S. Miller02fd4732006-02-11 02:25:21 -0800406}
407
David S. Millerf4266ab2006-02-13 20:43:02 -0800408static int __init hv_console_compatible(char *buf, int len)
409{
410 while (len) {
411 int this_len;
412
413 if (!strcmp(buf, "qcn"))
414 return 1;
415
416 this_len = strlen(buf) + 1;
417
418 buf += this_len;
419 len -= this_len;
420 }
421
422 return 0;
423}
424
425static unsigned int __init get_interrupt(void)
426{
427 const char *cons_str = "console";
428 const char *compat_str = "compatible";
429 int node = prom_getchild(sun4v_vdev_root);
430 unsigned int irq;
431 char buf[64];
432 int err, len;
433
434 node = prom_searchsiblings(node, cons_str);
435 if (!node)
436 return 0;
437
438 len = prom_getproplen(node, compat_str);
439 if (len == 0 || len == -1)
440 return 0;
441
442 err = prom_getproperty(node, compat_str, buf, 64);
443 if (err == -1)
444 return 0;
445
446 if (!hv_console_compatible(buf, len))
447 return 0;
448
449 /* Ok, the this is the OBP node for the sun4v hypervisor
450 * console device. Decode the interrupt.
451 */
452 err = prom_getproperty(node, "interrupts",
453 (char *) &irq, sizeof(irq));
454 if (err == -1)
455 return 0;
456
457 return sun4v_build_irq(sun4v_vdev_devhandle, irq, 4, 0);
458}
459
460static u32 sunhv_irq;
461
David S. Miller02fd4732006-02-11 02:25:21 -0800462static int __init sunhv_init(void)
463{
464 struct uart_port *port;
465 int ret;
466
467 if (tlb_type != hypervisor)
468 return -ENODEV;
469
David S. Millerf4266ab2006-02-13 20:43:02 -0800470 sunhv_irq = get_interrupt();
471 if (!sunhv_irq)
472 return -ENODEV;
473
David S. Miller02fd4732006-02-11 02:25:21 -0800474 port = kmalloc(sizeof(struct uart_port), GFP_KERNEL);
475 if (unlikely(!port))
476 return -ENOMEM;
477
478 port->line = 0;
479 port->ops = &sunhv_pops;
480 port->type = PORT_SUNHV;
481 port->uartclk = ( 29491200 / 16 ); /* arbitrary */
482
David S. Millerf4266ab2006-02-13 20:43:02 -0800483 if (request_irq(sunhv_irq, sunhv_interrupt,
David S. Miller02fd4732006-02-11 02:25:21 -0800484 SA_SHIRQ, "serial(sunhv)", port)) {
David S. Millerf4266ab2006-02-13 20:43:02 -0800485 printk("sunhv: Cannot get IRQ %x\n", sunhv_irq);
David S. Miller02fd4732006-02-11 02:25:21 -0800486 kfree(port);
487 return -ENODEV;
488 }
489
David S. Millerf4266ab2006-02-13 20:43:02 -0800490 printk("SUNHV: SUN4V virtual console, IRQ[%08x]\n",
491 sunhv_irq);
492
David S. Miller02fd4732006-02-11 02:25:21 -0800493 sunhv_reg.minor = sunserial_current_minor;
494 sunhv_reg.nr = 1;
David S. Miller02fd4732006-02-11 02:25:21 -0800495
496 ret = uart_register_driver(&sunhv_reg);
497 if (ret < 0) {
David S. Millerf4266ab2006-02-13 20:43:02 -0800498 free_irq(sunhv_irq, up);
David S. Miller02fd4732006-02-11 02:25:21 -0800499 kfree(port);
500
501 return ret;
502 }
503
David S. Miller02fd4732006-02-11 02:25:21 -0800504 sunserial_current_minor += 1;
505
David S. Millerd5a2aa22006-02-13 21:28:40 -0800506 sunhv_reg.cons = SUNHV_CONSOLE();
507
508 sunhv_port = port;
David S. Miller02fd4732006-02-11 02:25:21 -0800509
510 uart_add_one_port(&sunhv_reg, port);
511
512 return 0;
513}
514
515static void __exit sunhv_exit(void)
516{
517 struct uart_port *port = sunhv_port;
518
519 BUG_ON(!port);
520
521 uart_remove_one_port(&sunhv_reg, port);
David S. Millerf4266ab2006-02-13 20:43:02 -0800522 free_irq(sunhv_irq, port);
David S. Miller02fd4732006-02-11 02:25:21 -0800523
524 sunserial_current_minor -= 1;
525
526 uart_unregister_driver(&sunhv_reg);
527
528 kfree(sunhv_port);
529 sunhv_port = NULL;
530}
531
532module_init(sunhv_init);
533module_exit(sunhv_exit);
534
535MODULE_AUTHOR("David S. Miller");
536MODULE_DESCRIPTION("SUN4V Hypervisor console driver")
537MODULE_LICENSE("GPL");