diff options
| -rw-r--r-- | drivers/tty/serial/vt8500_serial.c | 58 |
1 files changed, 53 insertions, 5 deletions
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c index 2be006fb3da0..205d4cf4a063 100644 --- a/drivers/tty/serial/vt8500_serial.c +++ b/drivers/tty/serial/vt8500_serial.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
| 35 | #include <linux/clk.h> | 35 | #include <linux/clk.h> |
| 36 | #include <linux/platform_device.h> | 36 | #include <linux/platform_device.h> |
| 37 | #include <linux/of.h> | ||
| 37 | 38 | ||
| 38 | /* | 39 | /* |
| 39 | * UART Register offsets | 40 | * UART Register offsets |
| @@ -76,6 +77,8 @@ | |||
| 76 | #define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) | 77 | #define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT) |
| 77 | #define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) | 78 | #define TX_FIFO_INTS (TXFAE | TXFE | TXUDR) |
| 78 | 79 | ||
| 80 | #define VT8500_MAX_PORTS 6 | ||
| 81 | |||
| 79 | struct vt8500_port { | 82 | struct vt8500_port { |
| 80 | struct uart_port uart; | 83 | struct uart_port uart; |
| 81 | char name[16]; | 84 | char name[16]; |
| @@ -83,6 +86,13 @@ struct vt8500_port { | |||
| 83 | unsigned int ier; | 86 | unsigned int ier; |
| 84 | }; | 87 | }; |
| 85 | 88 | ||
| 89 | /* | ||
| 90 | * we use this variable to keep track of which ports | ||
| 91 | * have been allocated as we can't use pdev->id in | ||
| 92 | * devicetree | ||
| 93 | */ | ||
| 94 | static unsigned long vt8500_ports_in_use; | ||
| 95 | |||
| 86 | static inline void vt8500_write(struct uart_port *port, unsigned int val, | 96 | static inline void vt8500_write(struct uart_port *port, unsigned int val, |
| 87 | unsigned int off) | 97 | unsigned int off) |
| 88 | { | 98 | { |
| @@ -431,7 +441,7 @@ static int vt8500_verify_port(struct uart_port *port, | |||
| 431 | return 0; | 441 | return 0; |
| 432 | } | 442 | } |
| 433 | 443 | ||
| 434 | static struct vt8500_port *vt8500_uart_ports[4]; | 444 | static struct vt8500_port *vt8500_uart_ports[VT8500_MAX_PORTS]; |
| 435 | static struct uart_driver vt8500_uart_driver; | 445 | static struct uart_driver vt8500_uart_driver; |
| 436 | 446 | ||
| 437 | #ifdef CONFIG_SERIAL_VT8500_CONSOLE | 447 | #ifdef CONFIG_SERIAL_VT8500_CONSOLE |
| @@ -548,7 +558,9 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev) | |||
| 548 | { | 558 | { |
| 549 | struct vt8500_port *vt8500_port; | 559 | struct vt8500_port *vt8500_port; |
| 550 | struct resource *mmres, *irqres; | 560 | struct resource *mmres, *irqres; |
| 561 | struct device_node *np = pdev->dev.of_node; | ||
| 551 | int ret; | 562 | int ret; |
| 563 | int port; | ||
| 552 | 564 | ||
| 553 | mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 565 | mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 554 | irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 566 | irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
| @@ -559,16 +571,46 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev) | |||
| 559 | if (!vt8500_port) | 571 | if (!vt8500_port) |
| 560 | return -ENOMEM; | 572 | return -ENOMEM; |
| 561 | 573 | ||
| 574 | if (np) | ||
| 575 | port = of_alias_get_id(np, "serial"); | ||
| 576 | if (port > VT8500_MAX_PORTS) | ||
| 577 | port = -1; | ||
| 578 | else | ||
| 579 | port = -1; | ||
| 580 | |||
| 581 | if (port < 0) { | ||
| 582 | /* calculate the port id */ | ||
| 583 | port = find_first_zero_bit(&vt8500_ports_in_use, | ||
| 584 | sizeof(vt8500_ports_in_use)); | ||
| 585 | } | ||
| 586 | |||
| 587 | if (port > VT8500_MAX_PORTS) | ||
| 588 | return -ENODEV; | ||
| 589 | |||
| 590 | /* reserve the port id */ | ||
| 591 | if (test_and_set_bit(port, &vt8500_ports_in_use)) { | ||
| 592 | /* port already in use - shouldn't really happen */ | ||
| 593 | return -EBUSY; | ||
| 594 | } | ||
| 595 | |||
| 562 | vt8500_port->uart.type = PORT_VT8500; | 596 | vt8500_port->uart.type = PORT_VT8500; |
| 563 | vt8500_port->uart.iotype = UPIO_MEM; | 597 | vt8500_port->uart.iotype = UPIO_MEM; |
| 564 | vt8500_port->uart.mapbase = mmres->start; | 598 | vt8500_port->uart.mapbase = mmres->start; |
| 565 | vt8500_port->uart.irq = irqres->start; | 599 | vt8500_port->uart.irq = irqres->start; |
| 566 | vt8500_port->uart.fifosize = 16; | 600 | vt8500_port->uart.fifosize = 16; |
| 567 | vt8500_port->uart.ops = &vt8500_uart_pops; | 601 | vt8500_port->uart.ops = &vt8500_uart_pops; |
| 568 | vt8500_port->uart.line = pdev->id; | 602 | vt8500_port->uart.line = port; |
| 569 | vt8500_port->uart.dev = &pdev->dev; | 603 | vt8500_port->uart.dev = &pdev->dev; |
| 570 | vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; | 604 | vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF; |
| 571 | vt8500_port->uart.uartclk = 24000000; | 605 | |
| 606 | vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0); | ||
| 607 | if (vt8500_port->clk) { | ||
| 608 | vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk); | ||
| 609 | } else { | ||
| 610 | /* use the default of 24Mhz if not specified and warn */ | ||
| 611 | pr_warn("%s: serial clock source not specified\n", __func__); | ||
| 612 | vt8500_port->uart.uartclk = 24000000; | ||
| 613 | } | ||
| 572 | 614 | ||
| 573 | snprintf(vt8500_port->name, sizeof(vt8500_port->name), | 615 | snprintf(vt8500_port->name, sizeof(vt8500_port->name), |
| 574 | "VT8500 UART%d", pdev->id); | 616 | "VT8500 UART%d", pdev->id); |
| @@ -579,7 +621,7 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev) | |||
| 579 | goto err; | 621 | goto err; |
| 580 | } | 622 | } |
| 581 | 623 | ||
| 582 | vt8500_uart_ports[pdev->id] = vt8500_port; | 624 | vt8500_uart_ports[port] = vt8500_port; |
| 583 | 625 | ||
| 584 | uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart); | 626 | uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart); |
| 585 | 627 | ||
| @@ -603,12 +645,18 @@ static int __devexit vt8500_serial_remove(struct platform_device *pdev) | |||
| 603 | return 0; | 645 | return 0; |
| 604 | } | 646 | } |
| 605 | 647 | ||
| 648 | static const struct of_device_id wmt_dt_ids[] = { | ||
| 649 | { .compatible = "via,vt8500-uart", }, | ||
| 650 | {} | ||
| 651 | }; | ||
| 652 | |||
| 606 | static struct platform_driver vt8500_platform_driver = { | 653 | static struct platform_driver vt8500_platform_driver = { |
| 607 | .probe = vt8500_serial_probe, | 654 | .probe = vt8500_serial_probe, |
| 608 | .remove = __devexit_p(vt8500_serial_remove), | 655 | .remove = __devexit_p(vt8500_serial_remove), |
| 609 | .driver = { | 656 | .driver = { |
| 610 | .name = "vt8500_serial", | 657 | .name = "vt8500_serial", |
| 611 | .owner = THIS_MODULE, | 658 | .owner = THIS_MODULE, |
| 659 | .of_match_table = of_match_ptr(wmt_dt_ids), | ||
| 612 | }, | 660 | }, |
| 613 | }; | 661 | }; |
| 614 | 662 | ||
| @@ -642,4 +690,4 @@ module_exit(vt8500_serial_exit); | |||
| 642 | 690 | ||
| 643 | MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); | 691 | MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); |
| 644 | MODULE_DESCRIPTION("Driver for vt8500 serial device"); | 692 | MODULE_DESCRIPTION("Driver for vt8500 serial device"); |
| 645 | MODULE_LICENSE("GPL"); | 693 | MODULE_LICENSE("GPL v2"); |
