diff options
-rw-r--r-- | drivers/serial/sunhv.c | 166 |
1 files changed, 71 insertions, 95 deletions
diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index d36bc4003399..f851f0f44f9b 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c | |||
@@ -20,8 +20,8 @@ | |||
20 | 20 | ||
21 | #include <asm/hypervisor.h> | 21 | #include <asm/hypervisor.h> |
22 | #include <asm/spitfire.h> | 22 | #include <asm/spitfire.h> |
23 | #include <asm/vdev.h> | 23 | #include <asm/prom.h> |
24 | #include <asm/oplib.h> | 24 | #include <asm/of_device.h> |
25 | #include <asm/irq.h> | 25 | #include <asm/irq.h> |
26 | 26 | ||
27 | #if defined(CONFIG_MAGIC_SYSRQ) | 27 | #if defined(CONFIG_MAGIC_SYSRQ) |
@@ -407,144 +407,120 @@ static inline struct console *SUNHV_CONSOLE(void) | |||
407 | return &sunhv_console; | 407 | return &sunhv_console; |
408 | } | 408 | } |
409 | 409 | ||
410 | static int __init hv_console_compatible(char *buf, int len) | 410 | static int __devinit hv_probe(struct of_device *op, const struct of_device_id *match) |
411 | { | ||
412 | while (len) { | ||
413 | int this_len; | ||
414 | |||
415 | if (!strcmp(buf, "qcn")) | ||
416 | return 1; | ||
417 | |||
418 | this_len = strlen(buf) + 1; | ||
419 | |||
420 | buf += this_len; | ||
421 | len -= this_len; | ||
422 | } | ||
423 | |||
424 | return 0; | ||
425 | } | ||
426 | |||
427 | static unsigned int __init get_interrupt(void) | ||
428 | { | ||
429 | struct device_node *dev_node; | ||
430 | |||
431 | dev_node = sun4v_vdev_root->child; | ||
432 | while (dev_node != NULL) { | ||
433 | struct property *prop; | ||
434 | |||
435 | if (strcmp(dev_node->name, "console")) | ||
436 | goto next_sibling; | ||
437 | |||
438 | prop = of_find_property(dev_node, "compatible", NULL); | ||
439 | if (!prop) | ||
440 | goto next_sibling; | ||
441 | |||
442 | if (hv_console_compatible(prop->value, prop->length)) | ||
443 | break; | ||
444 | |||
445 | next_sibling: | ||
446 | dev_node = dev_node->sibling; | ||
447 | } | ||
448 | if (!dev_node) | ||
449 | return 0; | ||
450 | |||
451 | /* Ok, the this is the OBP node for the sun4v hypervisor | ||
452 | * console device. Decode the interrupt. | ||
453 | */ | ||
454 | return sun4v_vdev_device_interrupt(dev_node); | ||
455 | } | ||
456 | |||
457 | static int __init sunhv_init(void) | ||
458 | { | 411 | { |
459 | struct uart_port *port; | 412 | struct uart_port *port; |
460 | int ret; | 413 | int err; |
461 | 414 | ||
462 | if (tlb_type != hypervisor) | 415 | if (op->irqs[0] == 0xffffffff) |
463 | return -ENODEV; | 416 | return -ENODEV; |
464 | 417 | ||
465 | port = kmalloc(sizeof(struct uart_port), GFP_KERNEL); | 418 | port = kzalloc(sizeof(struct uart_port), GFP_KERNEL); |
466 | if (unlikely(!port)) | 419 | if (unlikely(!port)) |
467 | return -ENOMEM; | 420 | return -ENOMEM; |
468 | 421 | ||
469 | memset(port, 0, sizeof(struct uart_port)); | 422 | sunhv_port = port; |
470 | 423 | ||
471 | port->line = 0; | 424 | port->line = 0; |
472 | port->ops = &sunhv_pops; | 425 | port->ops = &sunhv_pops; |
473 | port->type = PORT_SUNHV; | 426 | port->type = PORT_SUNHV; |
474 | port->uartclk = ( 29491200 / 16 ); /* arbitrary */ | 427 | port->uartclk = ( 29491200 / 16 ); /* arbitrary */ |
475 | 428 | ||
476 | /* Set this just to make uart_configure_port() happy. */ | ||
477 | port->membase = (unsigned char __iomem *) __pa(port); | 429 | port->membase = (unsigned char __iomem *) __pa(port); |
478 | 430 | ||
479 | port->irq = get_interrupt(); | 431 | port->irq = op->irqs[0]; |
480 | if (!port->irq) { | 432 | |
481 | kfree(port); | 433 | port->dev = &op->dev; |
482 | return -ENODEV; | ||
483 | } | ||
484 | 434 | ||
485 | sunhv_reg.minor = sunserial_current_minor; | 435 | sunhv_reg.minor = sunserial_current_minor; |
486 | sunhv_reg.nr = 1; | 436 | sunhv_reg.nr = 1; |
487 | 437 | ||
488 | ret = uart_register_driver(&sunhv_reg); | 438 | err = uart_register_driver(&sunhv_reg); |
489 | if (ret < 0) { | 439 | if (err) |
490 | printk(KERN_ERR "SUNHV: uart_register_driver() failed %d\n", | 440 | goto out_free_port; |
491 | ret); | ||
492 | kfree(port); | ||
493 | |||
494 | return ret; | ||
495 | } | ||
496 | 441 | ||
497 | sunhv_reg.tty_driver->name_base = sunhv_reg.minor - 64; | 442 | sunhv_reg.tty_driver->name_base = sunhv_reg.minor - 64; |
498 | sunserial_current_minor += 1; | 443 | sunserial_current_minor += 1; |
499 | 444 | ||
500 | sunhv_reg.cons = SUNHV_CONSOLE(); | 445 | sunhv_reg.cons = SUNHV_CONSOLE(); |
501 | 446 | ||
502 | sunhv_port = port; | 447 | err = uart_add_one_port(&sunhv_reg, port); |
448 | if (err) | ||
449 | goto out_unregister_driver; | ||
503 | 450 | ||
504 | ret = uart_add_one_port(&sunhv_reg, port); | 451 | err = request_irq(port->irq, sunhv_interrupt, 0, "hvcons", port); |
505 | if (ret < 0) { | 452 | if (err) |
506 | printk(KERN_ERR "SUNHV: uart_add_one_port() failed %d\n", ret); | 453 | goto out_remove_port; |
507 | sunserial_current_minor -= 1; | ||
508 | uart_unregister_driver(&sunhv_reg); | ||
509 | kfree(port); | ||
510 | sunhv_port = NULL; | ||
511 | return -ENODEV; | ||
512 | } | ||
513 | 454 | ||
514 | if (request_irq(port->irq, sunhv_interrupt, | 455 | dev_set_drvdata(&op->dev, port); |
515 | SA_SHIRQ, "serial(sunhv)", port)) { | ||
516 | printk(KERN_ERR "sunhv: Cannot register IRQ\n"); | ||
517 | uart_remove_one_port(&sunhv_reg, port); | ||
518 | sunserial_current_minor -= 1; | ||
519 | uart_unregister_driver(&sunhv_reg); | ||
520 | kfree(port); | ||
521 | sunhv_port = NULL; | ||
522 | return -ENODEV; | ||
523 | } | ||
524 | 456 | ||
525 | return 0; | 457 | return 0; |
458 | |||
459 | out_remove_port: | ||
460 | uart_remove_one_port(&sunhv_reg, port); | ||
461 | |||
462 | out_unregister_driver: | ||
463 | sunserial_current_minor -= 1; | ||
464 | uart_unregister_driver(&sunhv_reg); | ||
465 | |||
466 | out_free_port: | ||
467 | kfree(port); | ||
468 | sunhv_port = NULL; | ||
469 | return err; | ||
526 | } | 470 | } |
527 | 471 | ||
528 | static void __exit sunhv_exit(void) | 472 | static int __devexit hv_remove(struct of_device *dev) |
529 | { | 473 | { |
530 | struct uart_port *port = sunhv_port; | 474 | struct uart_port *port = dev_get_drvdata(&dev->dev); |
531 | |||
532 | BUG_ON(!port); | ||
533 | 475 | ||
534 | free_irq(port->irq, port); | 476 | free_irq(port->irq, port); |
535 | 477 | ||
536 | uart_remove_one_port(&sunhv_reg, port); | 478 | uart_remove_one_port(&sunhv_reg, port); |
537 | sunserial_current_minor -= 1; | ||
538 | 479 | ||
480 | sunserial_current_minor -= 1; | ||
539 | uart_unregister_driver(&sunhv_reg); | 481 | uart_unregister_driver(&sunhv_reg); |
540 | 482 | ||
541 | kfree(sunhv_port); | 483 | kfree(port); |
542 | sunhv_port = NULL; | 484 | sunhv_port = NULL; |
485 | |||
486 | dev_set_drvdata(&dev->dev, NULL); | ||
487 | |||
488 | return 0; | ||
489 | } | ||
490 | |||
491 | static struct of_device_id hv_match[] = { | ||
492 | { | ||
493 | .name = "console", | ||
494 | .compatible = "qcn", | ||
495 | }, | ||
496 | {}, | ||
497 | }; | ||
498 | MODULE_DEVICE_TABLE(of, hv_match); | ||
499 | |||
500 | static struct of_platform_driver hv_driver = { | ||
501 | .name = "hv", | ||
502 | .match_table = hv_match, | ||
503 | .probe = hv_probe, | ||
504 | .remove = __devexit_p(hv_remove), | ||
505 | }; | ||
506 | |||
507 | static int __init sunhv_init(void) | ||
508 | { | ||
509 | if (tlb_type != hypervisor) | ||
510 | return -ENODEV; | ||
511 | |||
512 | return of_register_driver(&hv_driver, &of_bus_type); | ||
513 | } | ||
514 | |||
515 | static void __exit sunhv_exit(void) | ||
516 | { | ||
517 | of_unregister_driver(&hv_driver); | ||
543 | } | 518 | } |
544 | 519 | ||
545 | module_init(sunhv_init); | 520 | module_init(sunhv_init); |
546 | module_exit(sunhv_exit); | 521 | module_exit(sunhv_exit); |
547 | 522 | ||
548 | MODULE_AUTHOR("David S. Miller"); | 523 | MODULE_AUTHOR("David S. Miller"); |
549 | MODULE_DESCRIPTION("SUN4V Hypervisor console driver") | 524 | MODULE_DESCRIPTION("SUN4V Hypervisor console driver"); |
525 | MODULE_VERSION("2.0"); | ||
550 | MODULE_LICENSE("GPL"); | 526 | MODULE_LICENSE("GPL"); |