From patchwork Mon Jul 2 11:40:31 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mikko Perttunen X-Patchwork-Id: 10501219 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id EEC9760362 for ; Mon, 2 Jul 2018 11:52:29 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D980C287CC for ; Mon, 2 Jul 2018 11:52:29 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id CB56B287EF; Mon, 2 Jul 2018 11:52:29 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI autolearn=ham version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 113B7287CC for ; Mon, 2 Jul 2018 11:52:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=p4ZXjEB8WndiRADWaDCXVxL4bQK02BeNIx773iLl5Oc=; b=u1mvs7XVpRs8gi XqmlTmqqL8WmTcAMpdkgj32GZwGEY4TOg1LxlZ8IZsdXqSJirmd7h3v9aOblW5Kn6elFXzMcRX0kJ GDPLX755lyIk1ySXQZ9Tlwv9jNst604tRzeJzcooKbH1Y0IooLuTxvW48KuhDQJn6JqEAywoHM1Pb d64AZUypvw2yJ6z1AbOKU3BqTMp7Myh8P/FBaxZ1EEth9cHdPgMI4dxvJjriRbGWf5KWB1N0AcT9q pr48fUVsBBrxgvZhEcQp2FpJxfpndPBkkITKVnUNBUCHr7WBucemZBdJmFSh7FBXnVv0h0Bg7t6j6 o3tkaDzaywycK2QahHKA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fZxNY-00082q-Dl; Mon, 02 Jul 2018 11:52:24 +0000 Received: from hqemgate14.nvidia.com ([216.228.121.143]) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fZxDX-0000qW-Ks for linux-arm-kernel@lists.infradead.org; Mon, 02 Jul 2018 11:42:20 +0000 Received: from hqpgpgate101.nvidia.com (Not Verified[216.228.121.13]) by hqemgate14.nvidia.com (using TLS: TLSv1, AES128-SHA) id ; Mon, 02 Jul 2018 04:41:53 -0700 Received: from HQMAIL107.nvidia.com ([172.20.161.6]) by hqpgpgate101.nvidia.com (PGP Universal service); Mon, 02 Jul 2018 04:41:52 -0700 X-PGP-Universal: processed; by hqpgpgate101.nvidia.com on Mon, 02 Jul 2018 04:41:52 -0700 Received: from HQMAIL112.nvidia.com (172.18.146.18) by HQMAIL107.nvidia.com (172.20.187.13) with Microsoft SMTP Server (TLS) id 15.0.1347.2; Mon, 2 Jul 2018 11:41:52 +0000 Received: from HQMAIL104.nvidia.com (172.18.146.11) by HQMAIL112.nvidia.com (172.18.146.18) with Microsoft SMTP Server (TLS) id 15.0.1347.2; Mon, 2 Jul 2018 11:41:52 +0000 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by HQMAIL104.nvidia.com (172.18.146.11) with Microsoft SMTP Server (TLS) id 15.0.1347.2 via Frontend Transport; Mon, 2 Jul 2018 11:41:52 +0000 Received: from mperttunen-lnx.Nvidia.com (Not Verified[10.21.26.144]) by hqnvemgw01.nvidia.com with Trustwave SEG (v7, 5, 8, 10121) id ; Mon, 02 Jul 2018 04:41:51 -0700 From: Mikko Perttunen To: , , , Subject: [PATCH v3 6/8] serial: Add Tegra Combined UART driver Date: Mon, 2 Jul 2018 14:40:31 +0300 Message-ID: <20180702114033.15654-7-mperttunen@nvidia.com> X-Mailer: git-send-email 2.16.1 In-Reply-To: <20180702114033.15654-1-mperttunen@nvidia.com> References: <20180702114033.15654-1-mperttunen@nvidia.com> X-NVConfidentiality: public MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180702_044204_452143_880B3B28 X-CRM114-Status: GOOD ( 18.66 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Mikko Perttunen , linux-serial@vger.kernel.org, linux-tegra@vger.kernel.org, linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP The Tegra Combined UART (TCU) is a mailbox-based mechanism that allows multiplexing multiple "virtual UARTs" into a single hardware serial port. The TCU is the primary serial port on Tegra194 devices. Add a TCU driver utilizing the mailbox framework, as the used mailboxes are part of Tegra HSP blocks that are already controlled by the Tegra HSP mailbox driver. Signed-off-by: Mikko Perttunen --- Notes: v2: - Removed (void) casts for unused variables. - Changed the uart_set_options() call to be on one line, even if its over 80 characters. - Added defines for magic numbers. - Style fixes. - Changed Kconfig entry to depend on the Tegra HSP driver instead of just the mailbox framework. v3: - Removed FLUSH bit, as it's unnecessary and slows down printing - Removed call to uart_set_options - Added mbox_free_channel calls to remove() drivers/tty/serial/Kconfig | 9 ++ drivers/tty/serial/Makefile | 1 + drivers/tty/serial/tegra-tcu.c | 291 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/serial_core.h | 3 + 4 files changed, 304 insertions(+) create mode 100644 drivers/tty/serial/tegra-tcu.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index df8bd0c7b97d..5fdd336e8937 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -322,6 +322,15 @@ config SERIAL_TEGRA are enabled). This driver uses the APB DMA to achieve higher baudrate and better performance. +config SERIAL_TEGRA_TCU + tristate "NVIDIA Tegra Combined UART" + depends on ARCH_TEGRA && TEGRA_HSP_MBOX + select SERIAL_CORE + help + Support for the mailbox-based TCU (Tegra Combined UART) serial port. + TCU is a virtual serial port that allows multiplexing multiple data + streams into a single hardware serial port. + config SERIAL_MAX3100 tristate "MAX3100 support" depends on SPI diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index daac675612df..4ad82231ff8a 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_SERIAL_LANTIQ) += lantiq.o obj-$(CONFIG_SERIAL_XILINX_PS_UART) += xilinx_uartps.o obj-$(CONFIG_SERIAL_SIRFSOC) += sirfsoc_uart.o obj-$(CONFIG_SERIAL_TEGRA) += serial-tegra.o +obj-$(CONFIG_SERIAL_TEGRA_TCU) += tegra-tcu.o obj-$(CONFIG_SERIAL_AR933X) += ar933x_uart.o obj-$(CONFIG_SERIAL_EFM32_UART) += efm32-uart.o obj-$(CONFIG_SERIAL_ARC) += arc_uart.o diff --git a/drivers/tty/serial/tegra-tcu.c b/drivers/tty/serial/tegra-tcu.c new file mode 100644 index 000000000000..de58815bb215 --- /dev/null +++ b/drivers/tty/serial/tegra-tcu.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TCU_MBOX_BYTE(i, x) ((x) << (i*8)) +#define TCU_MBOX_BYTE_V(x, i) (((x) >> (i*8)) & 0xff) +#define TCU_MBOX_NUM_BYTES(x) ((x) << 24) +#define TCU_MBOX_NUM_BYTES_V(x) (((x) >> 24) & 0x3) + +static struct uart_driver tegra_tcu_uart_driver; +static struct uart_port tegra_tcu_uart_port; + +struct tegra_tcu { + struct mbox_client tx_client, rx_client; + struct mbox_chan *tx, *rx; +}; + +static unsigned int tegra_tcu_uart_tx_empty(struct uart_port *port) +{ + return TIOCSER_TEMT; +} + +static void tegra_tcu_uart_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ +} + +static unsigned int tegra_tcu_uart_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void tegra_tcu_uart_stop_tx(struct uart_port *port) +{ +} + +static void tegra_tcu_write(const char *s, unsigned int count) +{ + struct tegra_tcu *tcu = tegra_tcu_uart_port.private_data; + unsigned int written = 0, i = 0; + bool insert_nl = false; + uint32_t value = 0; + + while (i < count) { + if (insert_nl) { + value |= TCU_MBOX_BYTE(written++, '\n'); + insert_nl = false; + i++; + } else if (s[i] == '\n') { + value |= TCU_MBOX_BYTE(written++, '\r'); + insert_nl = true; + } else { + value |= TCU_MBOX_BYTE(written++, s[i++]); + } + + if (written == 3) { + value |= TCU_MBOX_NUM_BYTES(3); + mbox_send_message(tcu->tx, &value); + value = 0; + written = 0; + } + } + + if (written) { + value |= TCU_MBOX_NUM_BYTES(written); + mbox_send_message(tcu->tx, &value); + } +} + +static void tegra_tcu_uart_start_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->state->xmit; + unsigned long count; + + for (;;) { + count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); + if (!count) + break; + + tegra_tcu_write(&xmit->buf[xmit->tail], count); + xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1); + } + + uart_write_wakeup(port); +} + +static void tegra_tcu_uart_stop_rx(struct uart_port *port) +{ +} + +static void tegra_tcu_uart_break_ctl(struct uart_port *port, int ctl) +{ +} + +static int tegra_tcu_uart_startup(struct uart_port *port) +{ + return 0; +} + +static void tegra_tcu_uart_shutdown(struct uart_port *port) +{ +} + +static void tegra_tcu_uart_set_termios(struct uart_port *port, + struct ktermios *new, + struct ktermios *old) +{ +} + +static const struct uart_ops tegra_tcu_uart_ops = { + .tx_empty = tegra_tcu_uart_tx_empty, + .set_mctrl = tegra_tcu_uart_set_mctrl, + .get_mctrl = tegra_tcu_uart_get_mctrl, + .stop_tx = tegra_tcu_uart_stop_tx, + .start_tx = tegra_tcu_uart_start_tx, + .stop_rx = tegra_tcu_uart_stop_rx, + .break_ctl = tegra_tcu_uart_break_ctl, + .startup = tegra_tcu_uart_startup, + .shutdown = tegra_tcu_uart_shutdown, + .set_termios = tegra_tcu_uart_set_termios, +}; + +static void tegra_tcu_console_write(struct console *cons, const char *s, + unsigned int count) +{ + tegra_tcu_write(s, count); +} + +static int tegra_tcu_console_setup(struct console *cons, char *options) +{ + return 0; +} + +static struct console tegra_tcu_console = { + .name = "ttyTCU", + .device = uart_console_device, + .flags = CON_PRINTBUFFER | CON_ANYTIME, + .index = -1, + .write = tegra_tcu_console_write, + .setup = tegra_tcu_console_setup, + .data = &tegra_tcu_uart_driver, +}; + +static struct uart_driver tegra_tcu_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "tegra-tcu", + .dev_name = "ttyTCU", + .cons = &tegra_tcu_console, + .nr = 1, +}; + +static void tegra_tcu_receive(struct mbox_client *client, void *msg_p) +{ + struct tty_port *port = &tegra_tcu_uart_port.state->port; + uint32_t msg = *(uint32_t *)msg_p; + unsigned int num_bytes; + int i; + + num_bytes = TCU_MBOX_NUM_BYTES_V(msg); + for (i = 0; i < num_bytes; i++) + tty_insert_flip_char(port, TCU_MBOX_BYTE_V(msg, i), TTY_NORMAL); + + tty_flip_buffer_push(port); +} + +static int tegra_tcu_probe(struct platform_device *pdev) +{ + struct uart_port *port = &tegra_tcu_uart_port; + struct tegra_tcu *tcu; + int err; + + tcu = devm_kzalloc(&pdev->dev, sizeof(*tcu), GFP_KERNEL); + if (!tcu) + return -ENOMEM; + + tcu->tx_client.dev = &pdev->dev; + tcu->rx_client.dev = &pdev->dev; + tcu->rx_client.rx_callback = tegra_tcu_receive; + + tcu->tx = mbox_request_channel_byname(&tcu->tx_client, "tx"); + if (IS_ERR(tcu->tx)) { + err = PTR_ERR(tcu->tx); + dev_err(&pdev->dev, "failed to get tx mailbox: %d\n", err); + return err; + } + + tcu->rx = mbox_request_channel_byname(&tcu->rx_client, "rx"); + if (IS_ERR(tcu->rx)) { + err = PTR_ERR(tcu->rx); + dev_err(&pdev->dev, "failed to get rx mailbox: %d\n", err); + goto free_tx; + } + + err = uart_register_driver(&tegra_tcu_uart_driver); + if (err) { + dev_err(&pdev->dev, "failed to register UART driver: %d\n", + err); + goto free_rx; + } + + spin_lock_init(&port->lock); + port->dev = &pdev->dev; + port->type = PORT_TEGRA_TCU; + port->ops = &tegra_tcu_uart_ops; + port->fifosize = 1; + port->iotype = UPIO_MEM; + port->flags = UPF_BOOT_AUTOCONF; + port->private_data = tcu; + + err = uart_add_one_port(&tegra_tcu_uart_driver, port); + if (err) { + dev_err(&pdev->dev, "failed to add UART port: %d\n", err); + goto unregister_uart; + } + + platform_set_drvdata(pdev, tcu); + + return 0; + +unregister_uart: + uart_unregister_driver(&tegra_tcu_uart_driver); +free_rx: + mbox_free_channel(tcu->rx); +free_tx: + mbox_free_channel(tcu->tx); + + return err; +} + +static int tegra_tcu_remove(struct platform_device *pdev) +{ + struct tegra_tcu *tcu = platform_get_drvdata(pdev); + + uart_remove_one_port(&tegra_tcu_uart_driver, &tegra_tcu_uart_port); + uart_unregister_driver(&tegra_tcu_uart_driver); + mbox_free_channel(tcu->rx); + mbox_free_channel(tcu->tx); + + return 0; +} + +static const struct of_device_id tegra_tcu_match[] = { + { .compatible = "nvidia,tegra194-tcu" }, + { } +}; + +static struct platform_driver tegra_tcu_driver = { + .driver = { + .name = "tegra-tcu", + .of_match_table = tegra_tcu_match, + }, + .probe = tegra_tcu_probe, + .remove = tegra_tcu_remove, +}; + +static int __init tegra_tcu_init(void) +{ + int err; + + err = platform_driver_register(&tegra_tcu_driver); + if (err) + return err; + + register_console(&tegra_tcu_console); + + return 0; +} +module_init(tegra_tcu_init); + +static void __exit tegra_tcu_exit(void) +{ + unregister_console(&tegra_tcu_console); + platform_driver_unregister(&tegra_tcu_driver); +} +module_exit(tegra_tcu_exit); + +MODULE_AUTHOR("Mikko Perttunen "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("NVIDIA Tegra Combined UART driver"); diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index dce5f9dae121..69883c32cb98 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -79,6 +79,9 @@ /* Nuvoton UART */ #define PORT_NPCM 40 +/* NVIDIA Tegra Combined UART */ +#define PORT_TEGRA_TCU 41 + /* Intel EG20 */ #define PORT_PCH_8LINE 44 #define PORT_PCH_2LINE 45