| .. | .. |
|---|
| 5 | 5 | * Copyright (C) 2014 Carlo Caione <carlo@caione.org> |
|---|
| 6 | 6 | */ |
|---|
| 7 | 7 | |
|---|
| 8 | | -#if defined(CONFIG_SERIAL_MESON_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) |
|---|
| 9 | | -#define SUPPORT_SYSRQ |
|---|
| 10 | | -#endif |
|---|
| 11 | | - |
|---|
| 12 | 8 | #include <linux/clk.h> |
|---|
| 13 | 9 | #include <linux/console.h> |
|---|
| 14 | 10 | #include <linux/delay.h> |
|---|
| 15 | 11 | #include <linux/init.h> |
|---|
| 16 | 12 | #include <linux/io.h> |
|---|
| 13 | +#include <linux/iopoll.h> |
|---|
| 17 | 14 | #include <linux/module.h> |
|---|
| 18 | 15 | #include <linux/kernel.h> |
|---|
| 19 | 16 | #include <linux/of.h> |
|---|
| .. | .. |
|---|
| 72 | 69 | #define AML_UART_BAUD_USE BIT(23) |
|---|
| 73 | 70 | #define AML_UART_BAUD_XTAL BIT(24) |
|---|
| 74 | 71 | |
|---|
| 75 | | -#define AML_UART_PORT_NUM 6 |
|---|
| 72 | +#define AML_UART_PORT_NUM 12 |
|---|
| 73 | +#define AML_UART_PORT_OFFSET 6 |
|---|
| 76 | 74 | #define AML_UART_DEV_NAME "ttyAML" |
|---|
| 77 | 75 | |
|---|
| 76 | +#define AML_UART_POLL_USEC 5 |
|---|
| 77 | +#define AML_UART_TIMEOUT_USEC 10000 |
|---|
| 78 | 78 | |
|---|
| 79 | 79 | static struct uart_driver meson_uart_driver; |
|---|
| 80 | 80 | |
|---|
| .. | .. |
|---|
| 255 | 255 | return (port->type == PORT_MESON) ? "meson_uart" : NULL; |
|---|
| 256 | 256 | } |
|---|
| 257 | 257 | |
|---|
| 258 | +/* |
|---|
| 259 | + * This function is called only from probe() using a temporary io mapping |
|---|
| 260 | + * in order to perform a reset before setting up the device. Since the |
|---|
| 261 | + * temporarily mapped region was successfully requested, there can be no |
|---|
| 262 | + * console on this port at this time. Hence it is not necessary for this |
|---|
| 263 | + * function to acquire the port->lock. (Since there is no console on this |
|---|
| 264 | + * port at this time, the port->lock is not initialized yet.) |
|---|
| 265 | + */ |
|---|
| 258 | 266 | static void meson_uart_reset(struct uart_port *port) |
|---|
| 259 | 267 | { |
|---|
| 260 | 268 | u32 val; |
|---|
| .. | .. |
|---|
| 269 | 277 | |
|---|
| 270 | 278 | static int meson_uart_startup(struct uart_port *port) |
|---|
| 271 | 279 | { |
|---|
| 280 | + unsigned long flags; |
|---|
| 272 | 281 | u32 val; |
|---|
| 273 | 282 | int ret = 0; |
|---|
| 283 | + |
|---|
| 284 | + spin_lock_irqsave(&port->lock, flags); |
|---|
| 274 | 285 | |
|---|
| 275 | 286 | val = readl(port->membase + AML_UART_CONTROL); |
|---|
| 276 | 287 | val |= AML_UART_CLEAR_ERR; |
|---|
| .. | .. |
|---|
| 286 | 297 | |
|---|
| 287 | 298 | val = (AML_UART_RECV_IRQ(1) | AML_UART_XMIT_IRQ(port->fifosize / 2)); |
|---|
| 288 | 299 | writel(val, port->membase + AML_UART_MISC); |
|---|
| 300 | + |
|---|
| 301 | + spin_unlock_irqrestore(&port->lock, flags); |
|---|
| 289 | 302 | |
|---|
| 290 | 303 | ret = request_irq(port->irq, meson_uart_interrupt, 0, |
|---|
| 291 | 304 | port->name, port); |
|---|
| .. | .. |
|---|
| 410 | 423 | return -EBUSY; |
|---|
| 411 | 424 | } |
|---|
| 412 | 425 | |
|---|
| 413 | | - port->membase = devm_ioremap_nocache(port->dev, port->mapbase, |
|---|
| 426 | + port->membase = devm_ioremap(port->dev, port->mapbase, |
|---|
| 414 | 427 | port->mapsize); |
|---|
| 415 | 428 | if (!port->membase) |
|---|
| 416 | 429 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 425 | 438 | meson_uart_request_port(port); |
|---|
| 426 | 439 | } |
|---|
| 427 | 440 | } |
|---|
| 441 | + |
|---|
| 442 | +#ifdef CONFIG_CONSOLE_POLL |
|---|
| 443 | +/* |
|---|
| 444 | + * Console polling routines for writing and reading from the uart while |
|---|
| 445 | + * in an interrupt or debug context (i.e. kgdb). |
|---|
| 446 | + */ |
|---|
| 447 | + |
|---|
| 448 | +static int meson_uart_poll_get_char(struct uart_port *port) |
|---|
| 449 | +{ |
|---|
| 450 | + u32 c; |
|---|
| 451 | + unsigned long flags; |
|---|
| 452 | + |
|---|
| 453 | + spin_lock_irqsave(&port->lock, flags); |
|---|
| 454 | + |
|---|
| 455 | + if (readl(port->membase + AML_UART_STATUS) & AML_UART_RX_EMPTY) |
|---|
| 456 | + c = NO_POLL_CHAR; |
|---|
| 457 | + else |
|---|
| 458 | + c = readl(port->membase + AML_UART_RFIFO); |
|---|
| 459 | + |
|---|
| 460 | + spin_unlock_irqrestore(&port->lock, flags); |
|---|
| 461 | + |
|---|
| 462 | + return c; |
|---|
| 463 | +} |
|---|
| 464 | + |
|---|
| 465 | +static void meson_uart_poll_put_char(struct uart_port *port, unsigned char c) |
|---|
| 466 | +{ |
|---|
| 467 | + unsigned long flags; |
|---|
| 468 | + u32 reg; |
|---|
| 469 | + int ret; |
|---|
| 470 | + |
|---|
| 471 | + spin_lock_irqsave(&port->lock, flags); |
|---|
| 472 | + |
|---|
| 473 | + /* Wait until FIFO is empty or timeout */ |
|---|
| 474 | + ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg, |
|---|
| 475 | + reg & AML_UART_TX_EMPTY, |
|---|
| 476 | + AML_UART_POLL_USEC, |
|---|
| 477 | + AML_UART_TIMEOUT_USEC); |
|---|
| 478 | + if (ret == -ETIMEDOUT) { |
|---|
| 479 | + dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n"); |
|---|
| 480 | + goto out; |
|---|
| 481 | + } |
|---|
| 482 | + |
|---|
| 483 | + /* Write the character */ |
|---|
| 484 | + writel(c, port->membase + AML_UART_WFIFO); |
|---|
| 485 | + |
|---|
| 486 | + /* Wait until FIFO is empty or timeout */ |
|---|
| 487 | + ret = readl_poll_timeout_atomic(port->membase + AML_UART_STATUS, reg, |
|---|
| 488 | + reg & AML_UART_TX_EMPTY, |
|---|
| 489 | + AML_UART_POLL_USEC, |
|---|
| 490 | + AML_UART_TIMEOUT_USEC); |
|---|
| 491 | + if (ret == -ETIMEDOUT) |
|---|
| 492 | + dev_err(port->dev, "Timeout waiting for UART TX EMPTY\n"); |
|---|
| 493 | + |
|---|
| 494 | +out: |
|---|
| 495 | + spin_unlock_irqrestore(&port->lock, flags); |
|---|
| 496 | +} |
|---|
| 497 | + |
|---|
| 498 | +#endif /* CONFIG_CONSOLE_POLL */ |
|---|
| 428 | 499 | |
|---|
| 429 | 500 | static const struct uart_ops meson_uart_ops = { |
|---|
| 430 | 501 | .set_mctrl = meson_uart_set_mctrl, |
|---|
| .. | .. |
|---|
| 441 | 512 | .request_port = meson_uart_request_port, |
|---|
| 442 | 513 | .release_port = meson_uart_release_port, |
|---|
| 443 | 514 | .verify_port = meson_uart_verify_port, |
|---|
| 515 | +#ifdef CONFIG_CONSOLE_POLL |
|---|
| 516 | + .poll_get_char = meson_uart_poll_get_char, |
|---|
| 517 | + .poll_put_char = meson_uart_poll_put_char, |
|---|
| 518 | +#endif |
|---|
| 444 | 519 | }; |
|---|
| 445 | 520 | |
|---|
| 446 | 521 | #ifdef CONFIG_SERIAL_MESON_CONSOLE |
|---|
| .. | .. |
|---|
| 542 | 617 | register_console(&meson_serial_console); |
|---|
| 543 | 618 | return 0; |
|---|
| 544 | 619 | } |
|---|
| 545 | | -console_initcall(meson_serial_console_init); |
|---|
| 546 | 620 | |
|---|
| 547 | 621 | static void meson_serial_early_console_write(struct console *co, |
|---|
| 548 | 622 | const char *s, |
|---|
| .. | .. |
|---|
| 572 | 646 | |
|---|
| 573 | 647 | #define MESON_SERIAL_CONSOLE (&meson_serial_console) |
|---|
| 574 | 648 | #else |
|---|
| 649 | +static int __init meson_serial_console_init(void) { |
|---|
| 650 | + return 0; |
|---|
| 651 | +} |
|---|
| 575 | 652 | #define MESON_SERIAL_CONSOLE NULL |
|---|
| 576 | 653 | #endif |
|---|
| 577 | 654 | |
|---|
| .. | .. |
|---|
| 654 | 731 | struct resource *res_mem, *res_irq; |
|---|
| 655 | 732 | struct uart_port *port; |
|---|
| 656 | 733 | int ret = 0; |
|---|
| 734 | + int id = -1; |
|---|
| 657 | 735 | |
|---|
| 658 | 736 | if (pdev->dev.of_node) |
|---|
| 659 | 737 | pdev->id = of_alias_get_id(pdev->dev.of_node, "serial"); |
|---|
| 738 | + |
|---|
| 739 | + if (pdev->id < 0) { |
|---|
| 740 | + for (id = AML_UART_PORT_OFFSET; id < AML_UART_PORT_NUM; id++) { |
|---|
| 741 | + if (!meson_ports[id]) { |
|---|
| 742 | + pdev->id = id; |
|---|
| 743 | + break; |
|---|
| 744 | + } |
|---|
| 745 | + } |
|---|
| 746 | + } |
|---|
| 660 | 747 | |
|---|
| 661 | 748 | if (pdev->id < 0 || pdev->id >= AML_UART_PORT_NUM) |
|---|
| 662 | 749 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 692 | 779 | port->mapsize = resource_size(res_mem); |
|---|
| 693 | 780 | port->irq = res_irq->start; |
|---|
| 694 | 781 | port->flags = UPF_BOOT_AUTOCONF | UPF_LOW_LATENCY; |
|---|
| 782 | + port->has_sysrq = IS_ENABLED(CONFIG_SERIAL_MESON_CONSOLE); |
|---|
| 695 | 783 | port->dev = &pdev->dev; |
|---|
| 696 | 784 | port->line = pdev->id; |
|---|
| 697 | 785 | port->type = PORT_MESON; |
|---|
| .. | .. |
|---|
| 751 | 839 | { |
|---|
| 752 | 840 | int ret; |
|---|
| 753 | 841 | |
|---|
| 842 | + ret = meson_serial_console_init(); |
|---|
| 843 | + if (ret) |
|---|
| 844 | + return ret; |
|---|
| 845 | + |
|---|
| 754 | 846 | ret = uart_register_driver(&meson_uart_driver); |
|---|
| 755 | 847 | if (ret) |
|---|
| 756 | 848 | return ret; |
|---|