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"); |
