Skip to content

Commit

Permalink
tty: serial: tegra-tcu: Add earlycon support
Browse files Browse the repository at this point in the history
Add earlycon support to the Tegra TCU driver. This can come in two
forms: if no arguments are passed to the earlycon kernel command-line
option, the TCU will be looked up via the /chosen/stdout-path property
and the TX mailbox address will be read from the "reg" property. Note
that this is slightly fake because the "reg" property doesn't actually
contain any of the registers that control the TCU (in fact the TCU is
not controlled by any registers, it works solely through the RX and TX
mailboxes).

If additional arguments are passed, they are expected to contain the
earlycon driver name (i.e. tegra_comb_uart), followed by the physical
address of the TX mailbox that is to be used.

Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
[treding@nvidia.com: merged code into tegra-tcu driver]
Signed-off-by: Thierry Reding <treding@nvidia.com>
  • Loading branch information
thierryreding committed May 6, 2021
1 parent 2fbda7f commit 1c62098
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 1 deletion.
1 change: 1 addition & 0 deletions drivers/tty/serial/Kconfig
Expand Up @@ -300,6 +300,7 @@ config SERIAL_TEGRA_TCU
config SERIAL_TEGRA_TCU_CONSOLE
bool "Support for console on a Tegra TCU serial port"
depends on SERIAL_TEGRA_TCU=y
select SERIAL_EARLYCON
select SERIAL_CORE_CONSOLE
default y
help
Expand Down
68 changes: 67 additions & 1 deletion drivers/tty/serial/tegra-tcu.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2018-2021, NVIDIA CORPORATION. All rights reserved.
*/

#include <linux/console.h>
Expand All @@ -19,6 +19,7 @@
#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)
#define TCU_MBOX_FULL BIT(31)

struct tegra_tcu {
struct uart_driver driver;
Expand Down Expand Up @@ -294,6 +295,71 @@ static struct platform_driver tegra_tcu_driver = {
};
module_platform_driver(tegra_tcu_driver);

static u32 update_and_send_mbox(u8 __iomem *addr, u32 value, char c)
{
unsigned int bytes = TCU_MBOX_NUM_BYTES_V(value);

value |= TCU_MBOX_FULL;
value |= c << (bytes * 8);
bytes++;

value &= ~TCU_MBOX_NUM_BYTES(3);
value |= TCU_MBOX_NUM_BYTES(bytes);

if (bytes == 3) {
/* Send current packet to SPE */
while (readl(addr) & TCU_MBOX_FULL)
cpu_relax();

writel(value, addr);
value = TCU_MBOX_FULL;
}

return value;
}

/*
* This function splits the string to be printed (const char *s) into multiple
* packets. Each packet contains a max of 3 characters. Packets are sent to the
* SPE-based combined UART server for printing. Communication with SPE is done
* through mailbox registers which can generate interrupts for SPE.
*/
static void early_tcu_write(struct console *console, const char *s, unsigned int count)
{
struct earlycon_device *device = console->data;
u8 __iomem *addr = device->port.membase;
u32 value = TCU_MBOX_FULL;
unsigned int i;

/* Loop for processing each 3 char packet */
for (i = 0; i < count; i++) {
if (s[i] == '\n')
value = update_and_send_mbox(addr, value, '\r');

value = update_and_send_mbox(addr, value, s[i]);
}

if (TCU_MBOX_NUM_BYTES_V(value) & 0x3) {
while (readl(addr) & TCU_MBOX_FULL)
cpu_relax();

writel(value, addr);
}
}

static int __init early_tegra_combined_uart_setup(struct earlycon_device *device,
const char *options)
{
if (!device->port.membase)
return -ENODEV;

device->con->write = early_tcu_write;

return 0;
}

OF_EARLYCON_DECLARE(tegra_comb_uart, "nvidia,tegra194-tcu", early_tegra_combined_uart_setup);

MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("NVIDIA Tegra Combined UART driver");

0 comments on commit 1c62098

Please sign in to comment.