diff options
author | Tony Prisk <linux@prisktech.co.nz> | 2012-08-03 04:56:25 -0400 |
---|---|---|
committer | Tony Prisk <linux@prisktech.co.nz> | 2012-09-21 03:23:55 -0400 |
commit | 4001130df1a74948cfa8be02b420953a84ab83e8 (patch) | |
tree | 021057590ad22b15f6725a67c216e3f7ae444b3f | |
parent | 7f4b48b363263d2b62e3d0adecf56045994e721d (diff) |
serial: vt8500: Add devicetree support for vt8500-serial
Increase vt8500_max_ports to 6 as the WM8505 as 6 available uarts.
Use devicetree port id as primary addressing for ports but allow
auto-allocation if id not specified.
Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
Acked-by: Alan Cox <alan@linux.intel.com>
-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"); |