diff mbox series

[3/3] serial: Add an earlycon driver for MIPS UHI semihosting

Message ID 20231027182650.281405-4-jiaxun.yang@flygoat.com (mailing list archive)
State Superseded
Headers show
Series serial, MIPS: Add MIPS UHI semihosting support | expand

Commit Message

Jiaxun Yang Oct. 27, 2023, 6:26 p.m. UTC
UHI is MIPS's implementation of semihosting.
Add an earlycon driver to help with debugging on boot.

This driver is capable for print log using UHI's "Plog" or interact
with KGDB using UHI's stdio function.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/tty/serial/Kconfig             | 13 ++++
 drivers/tty/serial/Makefile            |  1 +
 drivers/tty/serial/earlycon-mips-uhi.c | 85 ++++++++++++++++++++++++++
 3 files changed, 99 insertions(+)
 create mode 100644 drivers/tty/serial/earlycon-mips-uhi.c
diff mbox series

Patch

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index bdc568a4ab66..04c62c6b45cd 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -85,6 +85,19 @@  config SERIAL_EARLYCON_SEMIHOST
 	  This is enabled with "earlycon=smh" on the kernel command line.
 	  The console is enabled when early_param is processed.
 
+config SERIAL_EARLYCON_UHI
+	bool "Early console using MIPS UHI semihosting"
+	depends on MIPS
+	select SERIAL_CORE
+	select SERIAL_CORE_CONSOLE
+	select SERIAL_EARLYCON
+	help
+	  Support for early debug console using UHI semihosting.
+	  This enables the console before standard serial driver is probed.
+	  This is enabled with "earlycon=uhi" or "earlycon=uhi_stdio" on the
+	  kernel command line.
+	  The console is enabled when early_param is processed.
+
 config SERIAL_EARLYCON_RISCV_SBI
 	bool "Early console using RISC-V SBI"
 	depends on RISCV_SBI_V01
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index f6b8c220dcfb..ef5e9c87aea1 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -9,6 +9,7 @@  serial_base-y := serial_core.o serial_base_bus.o serial_ctrl.o serial_port.o
 obj-$(CONFIG_SERIAL_EARLYCON) += earlycon.o
 obj-$(CONFIG_SERIAL_EARLYCON_SEMIHOST) += earlycon-semihost.o
 obj-$(CONFIG_SERIAL_EARLYCON_RISCV_SBI) += earlycon-riscv-sbi.o
+obj-$(CONFIG_SERIAL_EARLYCON_MIPS_UHI) += earlycon-mips-uhi.o
 
 # These Sparc drivers have to appear before others such as 8250
 # which share ttySx minor node space.  Otherwise console device
diff --git a/drivers/tty/serial/earlycon-mips-uhi.c b/drivers/tty/serial/earlycon-mips-uhi.c
new file mode 100644
index 000000000000..002bb2c37064
--- /dev/null
+++ b/drivers/tty/serial/earlycon-mips-uhi.c
@@ -0,0 +1,85 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MIPS UHI semihosting based earlycon
+ *
+ * Copyright (C) 2023 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <asm/uhi.h>
+
+static int stdin_fd = -1;
+static int stdout_fd = -1;
+
+static void uhi_plog_write(struct console *con, const char *s, unsigned int n)
+{
+	uhi_plog(s, 0);
+}
+
+static void uhi_stdout_write(struct console *con, const char *s, unsigned int n)
+{
+	if (stdout_fd < 0)
+		return;
+
+	uhi_write(stdout_fd, s, n);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int uhi_stdin_read(struct console *con, char *s, unsigned int n)
+{
+	if (stdin_fd < 0)
+		return 0;
+
+	return uhi_read(stdin_fd, s, n);
+}
+#endif
+
+static int uhi_stdio_fd_open(struct console *co, char *options)
+{
+	/*
+	 * You have to open both stdin and stdout to get console work
+	 * properly on some old CodeScape debugger.
+	 */
+	stdin_fd = uhi_open("/dev/stdin", UHI_O_RDONLY, 0);
+	stdout_fd = uhi_open("/dev/stdout", UHI_O_WRONLY, 0);
+
+	return (stdin_fd < 0 || stdout_fd < 0) ? -ENODEV : 0;
+}
+
+static int uhi_stdio_fd_close(struct console *co)
+{
+	int ret1 = 0, ret2 = 0;
+
+	if (stdin_fd >= 0)
+		ret1 = uhi_close(stdin_fd);
+	if (stdout_fd >= 0)
+		ret2 = uhi_close(stdout_fd);
+
+	return (ret1 < 0 || ret2 < 0) ? -ENODEV : 0;
+}
+
+static int
+__init early_uhi_setup(struct earlycon_device *device, const char *opt)
+{
+	device->con->write = uhi_plog_write;
+	return 0;
+}
+
+static int
+__init early_uhi_stdio_setup(struct earlycon_device *device, const char *opt)
+{
+
+	device->con->setup = uhi_stdio_fd_open;
+	device->con->exit = uhi_stdio_fd_close;
+	device->con->write = uhi_stdout_write;
+#ifdef CONFIG_CONSOLE_POLL
+	device->con->read = uhi_stdin_read;
+#endif
+	return 0;
+}
+
+EARLYCON_DECLARE(uhi, early_uhi_setup);
+EARLYCON_DECLARE(uhi_stdio, early_uhi_stdio_setup);