[SPARC64] sunhv: Use virtual-devices layer to get interrupt.
[deliverable/linux.git] / drivers / serial / sunhv.c
1 /* 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>
23 #include <asm/vdev.h>
24 #include <asm/irq.h>
25
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
37 static 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
56 static 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
73 static int hung_up = 0;
74
75 static 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
136 static 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
157 static 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. */
175 static 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. */
185 static 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. */
191 static 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. */
197 static void sunhv_stop_tx(struct uart_port *port)
198 {
199 return;
200 }
201
202 /* port->lock held by caller. */
203 static 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. */
224 static 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. */
241 static void sunhv_stop_rx(struct uart_port *port)
242 {
243 }
244
245 /* port->lock held by caller. */
246 static void sunhv_enable_ms(struct uart_port *port)
247 {
248 }
249
250 /* port->lock is not held. */
251 static 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. */
270 static int sunhv_startup(struct uart_port *port)
271 {
272 return 0;
273 }
274
275 /* port->lock is not held. */
276 static void sunhv_shutdown(struct uart_port *port)
277 {
278 }
279
280 /* port->lock is not held. */
281 static 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
307 static const char *sunhv_type(struct uart_port *port)
308 {
309 return "SUN4V HCONS";
310 }
311
312 static void sunhv_release_port(struct uart_port *port)
313 {
314 }
315
316 static int sunhv_request_port(struct uart_port *port)
317 {
318 return 0;
319 }
320
321 static void sunhv_config_port(struct uart_port *port, int flags)
322 {
323 }
324
325 static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser)
326 {
327 return -EINVAL;
328 }
329
330 static 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
350 static 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
358 static struct uart_port *sunhv_port;
359
360 static 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
376 static 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
388 static int sunhv_console_setup(struct console *con, char *options)
389 {
390 return 0;
391 }
392
393 static 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
403 static 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
412 static 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
429 static 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
464 static u32 sunhv_irq;
465
466 static int __init sunhv_init(void)
467 {
468 struct uart_port *port;
469 int ret;
470
471 if (tlb_type != hypervisor)
472 return -ENODEV;
473
474 sunhv_irq = get_interrupt();
475 if (!sunhv_irq)
476 return -ENODEV;
477
478 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
487 if (request_irq(sunhv_irq, sunhv_interrupt,
488 SA_SHIRQ, "serial(sunhv)", port)) {
489 printk("sunhv: Cannot get IRQ %x\n", sunhv_irq);
490 kfree(port);
491 return -ENODEV;
492 }
493
494 printk("SUNHV: SUN4V virtual console, IRQ[%08x]\n",
495 sunhv_irq);
496
497 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) {
503 free_irq(sunhv_irq, up);
504 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
520 static 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);
527 free_irq(sunhv_irq, port);
528
529 sunserial_current_minor -= 1;
530
531 uart_unregister_driver(&sunhv_reg);
532
533 kfree(sunhv_port);
534 sunhv_port = NULL;
535 }
536
537 module_init(sunhv_init);
538 module_exit(sunhv_exit);
539
540 MODULE_AUTHOR("David S. Miller");
541 MODULE_DESCRIPTION("SUN4V Hypervisor console driver")
542 MODULE_LICENSE("GPL");
This page took 0.041288 seconds and 6 git commands to generate.