12d908b38SThierry Reding // SPDX-License-Identifier: GPL-2.0 22d908b38SThierry Reding /* 32d908b38SThierry Reding * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. 42d908b38SThierry Reding */ 52d908b38SThierry Reding 62d908b38SThierry Reding #include <linux/console.h> 72d908b38SThierry Reding #include <linux/mailbox_client.h> 82d908b38SThierry Reding #include <linux/module.h> 92d908b38SThierry Reding #include <linux/of.h> 102d908b38SThierry Reding #include <linux/platform_device.h> 112d908b38SThierry Reding #include <linux/serial.h> 122d908b38SThierry Reding #include <linux/serial_core.h> 132d908b38SThierry Reding #include <linux/slab.h> 142d908b38SThierry Reding #include <linux/tty.h> 152d908b38SThierry Reding #include <linux/tty_flip.h> 162d908b38SThierry Reding 172d908b38SThierry Reding #define TCU_MBOX_BYTE(i, x) ((x) << (i * 8)) 182d908b38SThierry Reding #define TCU_MBOX_BYTE_V(x, i) (((x) >> (i * 8)) & 0xff) 192d908b38SThierry Reding #define TCU_MBOX_NUM_BYTES(x) ((x) << 24) 202d908b38SThierry Reding #define TCU_MBOX_NUM_BYTES_V(x) (((x) >> 24) & 0x3) 212d908b38SThierry Reding 222d908b38SThierry Reding struct tegra_tcu { 232d908b38SThierry Reding struct uart_driver driver; 242d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE) 252d908b38SThierry Reding struct console console; 262d908b38SThierry Reding #endif 272d908b38SThierry Reding struct uart_port port; 282d908b38SThierry Reding 292d908b38SThierry Reding struct mbox_client tx_client, rx_client; 302d908b38SThierry Reding struct mbox_chan *tx, *rx; 312d908b38SThierry Reding }; 322d908b38SThierry Reding 332d908b38SThierry Reding static unsigned int tegra_tcu_uart_tx_empty(struct uart_port *port) 342d908b38SThierry Reding { 352d908b38SThierry Reding return TIOCSER_TEMT; 362d908b38SThierry Reding } 372d908b38SThierry Reding 382d908b38SThierry Reding static void tegra_tcu_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) 392d908b38SThierry Reding { 402d908b38SThierry Reding } 412d908b38SThierry Reding 422d908b38SThierry Reding static unsigned int tegra_tcu_uart_get_mctrl(struct uart_port *port) 432d908b38SThierry Reding { 442d908b38SThierry Reding return 0; 452d908b38SThierry Reding } 462d908b38SThierry Reding 472d908b38SThierry Reding static void tegra_tcu_uart_stop_tx(struct uart_port *port) 482d908b38SThierry Reding { 492d908b38SThierry Reding } 502d908b38SThierry Reding 512d908b38SThierry Reding static void tegra_tcu_write_one(struct tegra_tcu *tcu, u32 value, 522d908b38SThierry Reding unsigned int count) 532d908b38SThierry Reding { 542d908b38SThierry Reding void *msg; 552d908b38SThierry Reding 562d908b38SThierry Reding value |= TCU_MBOX_NUM_BYTES(count); 572d908b38SThierry Reding msg = (void *)(unsigned long)value; 582d908b38SThierry Reding mbox_send_message(tcu->tx, msg); 592d908b38SThierry Reding mbox_flush(tcu->tx, 1000); 602d908b38SThierry Reding } 612d908b38SThierry Reding 622d908b38SThierry Reding static void tegra_tcu_write(struct tegra_tcu *tcu, const char *s, 632d908b38SThierry Reding unsigned int count) 642d908b38SThierry Reding { 652d908b38SThierry Reding unsigned int written = 0, i = 0; 662d908b38SThierry Reding bool insert_nl = false; 672d908b38SThierry Reding u32 value = 0; 682d908b38SThierry Reding 692d908b38SThierry Reding while (i < count) { 702d908b38SThierry Reding if (insert_nl) { 712d908b38SThierry Reding value |= TCU_MBOX_BYTE(written++, '\n'); 722d908b38SThierry Reding insert_nl = false; 732d908b38SThierry Reding i++; 742d908b38SThierry Reding } else if (s[i] == '\n') { 752d908b38SThierry Reding value |= TCU_MBOX_BYTE(written++, '\r'); 762d908b38SThierry Reding insert_nl = true; 772d908b38SThierry Reding } else { 782d908b38SThierry Reding value |= TCU_MBOX_BYTE(written++, s[i++]); 792d908b38SThierry Reding } 802d908b38SThierry Reding 812d908b38SThierry Reding if (written == 3) { 822d908b38SThierry Reding tegra_tcu_write_one(tcu, value, 3); 832d908b38SThierry Reding value = written = 0; 842d908b38SThierry Reding } 852d908b38SThierry Reding } 862d908b38SThierry Reding 872d908b38SThierry Reding if (written) 882d908b38SThierry Reding tegra_tcu_write_one(tcu, value, written); 892d908b38SThierry Reding } 902d908b38SThierry Reding 912d908b38SThierry Reding static void tegra_tcu_uart_start_tx(struct uart_port *port) 922d908b38SThierry Reding { 932d908b38SThierry Reding struct tegra_tcu *tcu = port->private_data; 94*1788cf6aSJiri Slaby (SUSE) struct tty_port *tport = &port->state->port; 95*1788cf6aSJiri Slaby (SUSE) unsigned char *tail; 96*1788cf6aSJiri Slaby (SUSE) unsigned int count; 972d908b38SThierry Reding 982d908b38SThierry Reding for (;;) { 99*1788cf6aSJiri Slaby (SUSE) count = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail, 100*1788cf6aSJiri Slaby (SUSE) UART_XMIT_SIZE); 1012d908b38SThierry Reding if (!count) 1022d908b38SThierry Reding break; 1032d908b38SThierry Reding 104*1788cf6aSJiri Slaby (SUSE) tegra_tcu_write(tcu, tail, count); 1051d10cd4dSIlpo Järvinen uart_xmit_advance(port, count); 1062d908b38SThierry Reding } 1072d908b38SThierry Reding 1082d908b38SThierry Reding uart_write_wakeup(port); 1092d908b38SThierry Reding } 1102d908b38SThierry Reding 1112d908b38SThierry Reding static void tegra_tcu_uart_stop_rx(struct uart_port *port) 1122d908b38SThierry Reding { 1132d908b38SThierry Reding } 1142d908b38SThierry Reding 1152d908b38SThierry Reding static void tegra_tcu_uart_break_ctl(struct uart_port *port, int ctl) 1162d908b38SThierry Reding { 1172d908b38SThierry Reding } 1182d908b38SThierry Reding 1192d908b38SThierry Reding static int tegra_tcu_uart_startup(struct uart_port *port) 1202d908b38SThierry Reding { 1212d908b38SThierry Reding return 0; 1222d908b38SThierry Reding } 1232d908b38SThierry Reding 1242d908b38SThierry Reding static void tegra_tcu_uart_shutdown(struct uart_port *port) 1252d908b38SThierry Reding { 1262d908b38SThierry Reding } 1272d908b38SThierry Reding 1282d908b38SThierry Reding static void tegra_tcu_uart_set_termios(struct uart_port *port, 1292d908b38SThierry Reding struct ktermios *new, 130bec5b814SIlpo Järvinen const struct ktermios *old) 1312d908b38SThierry Reding { 1322d908b38SThierry Reding } 1332d908b38SThierry Reding 1342d908b38SThierry Reding static const struct uart_ops tegra_tcu_uart_ops = { 1352d908b38SThierry Reding .tx_empty = tegra_tcu_uart_tx_empty, 1362d908b38SThierry Reding .set_mctrl = tegra_tcu_uart_set_mctrl, 1372d908b38SThierry Reding .get_mctrl = tegra_tcu_uart_get_mctrl, 1382d908b38SThierry Reding .stop_tx = tegra_tcu_uart_stop_tx, 1392d908b38SThierry Reding .start_tx = tegra_tcu_uart_start_tx, 1402d908b38SThierry Reding .stop_rx = tegra_tcu_uart_stop_rx, 1412d908b38SThierry Reding .break_ctl = tegra_tcu_uart_break_ctl, 1422d908b38SThierry Reding .startup = tegra_tcu_uart_startup, 1432d908b38SThierry Reding .shutdown = tegra_tcu_uart_shutdown, 1442d908b38SThierry Reding .set_termios = tegra_tcu_uart_set_termios, 1452d908b38SThierry Reding }; 1462d908b38SThierry Reding 1472d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE) 1482d908b38SThierry Reding static void tegra_tcu_console_write(struct console *cons, const char *s, 1492d908b38SThierry Reding unsigned int count) 1502d908b38SThierry Reding { 1512d908b38SThierry Reding struct tegra_tcu *tcu = container_of(cons, struct tegra_tcu, console); 1522d908b38SThierry Reding 1532d908b38SThierry Reding tegra_tcu_write(tcu, s, count); 1542d908b38SThierry Reding } 1552d908b38SThierry Reding 1562d908b38SThierry Reding static int tegra_tcu_console_setup(struct console *cons, char *options) 1572d908b38SThierry Reding { 1582d908b38SThierry Reding return 0; 1592d908b38SThierry Reding } 1602d908b38SThierry Reding #endif 1612d908b38SThierry Reding 1622d908b38SThierry Reding static void tegra_tcu_receive(struct mbox_client *cl, void *msg) 1632d908b38SThierry Reding { 1642d908b38SThierry Reding struct tegra_tcu *tcu = container_of(cl, struct tegra_tcu, rx_client); 1652d908b38SThierry Reding struct tty_port *port = &tcu->port.state->port; 1662d908b38SThierry Reding u32 value = (u32)(unsigned long)msg; 1672d908b38SThierry Reding unsigned int num_bytes, i; 1682d908b38SThierry Reding 1692d908b38SThierry Reding num_bytes = TCU_MBOX_NUM_BYTES_V(value); 1702d908b38SThierry Reding 1712d908b38SThierry Reding for (i = 0; i < num_bytes; i++) 1722d908b38SThierry Reding tty_insert_flip_char(port, TCU_MBOX_BYTE_V(value, i), 1732d908b38SThierry Reding TTY_NORMAL); 1742d908b38SThierry Reding 1752d908b38SThierry Reding tty_flip_buffer_push(port); 1762d908b38SThierry Reding } 1772d908b38SThierry Reding 1782d908b38SThierry Reding static int tegra_tcu_probe(struct platform_device *pdev) 1792d908b38SThierry Reding { 1802d908b38SThierry Reding struct uart_port *port; 1812d908b38SThierry Reding struct tegra_tcu *tcu; 1822d908b38SThierry Reding int err; 1832d908b38SThierry Reding 1842d908b38SThierry Reding tcu = devm_kzalloc(&pdev->dev, sizeof(*tcu), GFP_KERNEL); 1852d908b38SThierry Reding if (!tcu) 1862d908b38SThierry Reding return -ENOMEM; 1872d908b38SThierry Reding 1882d908b38SThierry Reding tcu->tx_client.dev = &pdev->dev; 1892d908b38SThierry Reding tcu->rx_client.dev = &pdev->dev; 1902d908b38SThierry Reding tcu->rx_client.rx_callback = tegra_tcu_receive; 1912d908b38SThierry Reding 1922d908b38SThierry Reding tcu->tx = mbox_request_channel_byname(&tcu->tx_client, "tx"); 1932d908b38SThierry Reding if (IS_ERR(tcu->tx)) { 1942d908b38SThierry Reding err = PTR_ERR(tcu->tx); 1952d908b38SThierry Reding dev_err(&pdev->dev, "failed to get tx mailbox: %d\n", err); 1962d908b38SThierry Reding return err; 1972d908b38SThierry Reding } 1982d908b38SThierry Reding 1992d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE) 2002d908b38SThierry Reding /* setup the console */ 2012d908b38SThierry Reding strcpy(tcu->console.name, "ttyTCU"); 2022d908b38SThierry Reding tcu->console.device = uart_console_device; 2032d908b38SThierry Reding tcu->console.flags = CON_PRINTBUFFER | CON_ANYTIME; 2042d908b38SThierry Reding tcu->console.index = -1; 2052d908b38SThierry Reding tcu->console.write = tegra_tcu_console_write; 2062d908b38SThierry Reding tcu->console.setup = tegra_tcu_console_setup; 2072d908b38SThierry Reding tcu->console.data = &tcu->driver; 2082d908b38SThierry Reding #endif 2092d908b38SThierry Reding 2102d908b38SThierry Reding /* setup the driver */ 2112d908b38SThierry Reding tcu->driver.owner = THIS_MODULE; 2122d908b38SThierry Reding tcu->driver.driver_name = "tegra-tcu"; 2132d908b38SThierry Reding tcu->driver.dev_name = "ttyTCU"; 2142d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE) 2152d908b38SThierry Reding tcu->driver.cons = &tcu->console; 2162d908b38SThierry Reding #endif 2172d908b38SThierry Reding tcu->driver.nr = 1; 2182d908b38SThierry Reding 2192d908b38SThierry Reding err = uart_register_driver(&tcu->driver); 2202d908b38SThierry Reding if (err) { 2212d908b38SThierry Reding dev_err(&pdev->dev, "failed to register UART driver: %d\n", 2222d908b38SThierry Reding err); 223af9a1f61SMikko Perttunen goto free_tx; 2242d908b38SThierry Reding } 2252d908b38SThierry Reding 2262d908b38SThierry Reding /* setup the port */ 2272d908b38SThierry Reding port = &tcu->port; 2282d908b38SThierry Reding spin_lock_init(&port->lock); 2292d908b38SThierry Reding port->dev = &pdev->dev; 2302d908b38SThierry Reding port->type = PORT_TEGRA_TCU; 2312d908b38SThierry Reding port->ops = &tegra_tcu_uart_ops; 2322d908b38SThierry Reding port->fifosize = 1; 2332d908b38SThierry Reding port->iotype = UPIO_MEM; 2342d908b38SThierry Reding port->flags = UPF_BOOT_AUTOCONF; 2352d908b38SThierry Reding port->private_data = tcu; 2362d908b38SThierry Reding 2372d908b38SThierry Reding err = uart_add_one_port(&tcu->driver, port); 2382d908b38SThierry Reding if (err) { 2392d908b38SThierry Reding dev_err(&pdev->dev, "failed to add UART port: %d\n", err); 2402d908b38SThierry Reding goto unregister_uart; 2412d908b38SThierry Reding } 2422d908b38SThierry Reding 243af9a1f61SMikko Perttunen /* 244af9a1f61SMikko Perttunen * Request RX channel after creating port to ensure tcu->port 245af9a1f61SMikko Perttunen * is ready for any immediate incoming bytes. 246af9a1f61SMikko Perttunen */ 247af9a1f61SMikko Perttunen tcu->rx = mbox_request_channel_byname(&tcu->rx_client, "rx"); 248af9a1f61SMikko Perttunen if (IS_ERR(tcu->rx)) { 249af9a1f61SMikko Perttunen err = PTR_ERR(tcu->rx); 250af9a1f61SMikko Perttunen dev_err(&pdev->dev, "failed to get rx mailbox: %d\n", err); 251af9a1f61SMikko Perttunen goto remove_uart_port; 252af9a1f61SMikko Perttunen } 253af9a1f61SMikko Perttunen 2542d908b38SThierry Reding platform_set_drvdata(pdev, tcu); 2552d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE) 2562d908b38SThierry Reding register_console(&tcu->console); 2572d908b38SThierry Reding #endif 2582d908b38SThierry Reding 2592d908b38SThierry Reding return 0; 2602d908b38SThierry Reding 261af9a1f61SMikko Perttunen remove_uart_port: 262af9a1f61SMikko Perttunen uart_remove_one_port(&tcu->driver, &tcu->port); 2632d908b38SThierry Reding unregister_uart: 2642d908b38SThierry Reding uart_unregister_driver(&tcu->driver); 2652d908b38SThierry Reding free_tx: 2662d908b38SThierry Reding mbox_free_channel(tcu->tx); 2672d908b38SThierry Reding 2682d908b38SThierry Reding return err; 2692d908b38SThierry Reding } 2702d908b38SThierry Reding 2715e29d46fSUwe Kleine-König static void tegra_tcu_remove(struct platform_device *pdev) 2722d908b38SThierry Reding { 2732d908b38SThierry Reding struct tegra_tcu *tcu = platform_get_drvdata(pdev); 2742d908b38SThierry Reding 2752d908b38SThierry Reding #if IS_ENABLED(CONFIG_SERIAL_TEGRA_TCU_CONSOLE) 2762d908b38SThierry Reding unregister_console(&tcu->console); 2772d908b38SThierry Reding #endif 278af9a1f61SMikko Perttunen mbox_free_channel(tcu->rx); 2792d908b38SThierry Reding uart_remove_one_port(&tcu->driver, &tcu->port); 2802d908b38SThierry Reding uart_unregister_driver(&tcu->driver); 2812d908b38SThierry Reding mbox_free_channel(tcu->tx); 2822d908b38SThierry Reding } 2832d908b38SThierry Reding 2842d908b38SThierry Reding static const struct of_device_id tegra_tcu_match[] = { 2852d908b38SThierry Reding { .compatible = "nvidia,tegra194-tcu" }, 2862d908b38SThierry Reding { } 2872d908b38SThierry Reding }; 2889e5313acSBixuan Cui MODULE_DEVICE_TABLE(of, tegra_tcu_match); 2892d908b38SThierry Reding 2902d908b38SThierry Reding static struct platform_driver tegra_tcu_driver = { 2912d908b38SThierry Reding .driver = { 2922d908b38SThierry Reding .name = "tegra-tcu", 2932d908b38SThierry Reding .of_match_table = tegra_tcu_match, 2942d908b38SThierry Reding }, 2952d908b38SThierry Reding .probe = tegra_tcu_probe, 2965e29d46fSUwe Kleine-König .remove_new = tegra_tcu_remove, 2972d908b38SThierry Reding }; 2982d908b38SThierry Reding module_platform_driver(tegra_tcu_driver); 2992d908b38SThierry Reding 3002d908b38SThierry Reding MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>"); 3012d908b38SThierry Reding MODULE_LICENSE("GPL v2"); 3022d908b38SThierry Reding MODULE_DESCRIPTION("NVIDIA Tegra Combined UART driver"); 303