[PATCH RFC 3/4] powerpc/microwatt: Add early debug UART support for Microwatt

Paul Mackerras paulus at ozlabs.org
Sat May 9 15:03:40 AEST 2020


Currently microwatt-based SoCs come with a "potato" UART.  This
adds udbg support for the potato UART, giving us both an early
debug console, and a runtime console using the hvc-udbg support.

Signed-off-by: Paul Mackerras <paulus at ozlabs.org>
---
 arch/powerpc/Kconfig.debug               |   6 ++
 arch/powerpc/include/asm/udbg.h          |   1 +
 arch/powerpc/kernel/udbg.c               |   2 +
 arch/powerpc/platforms/microwatt/setup.c | 108 +++++++++++++++++++++++
 4 files changed, 117 insertions(+)

diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug
index 0b063830eea8..399abc6d2af7 100644
--- a/arch/powerpc/Kconfig.debug
+++ b/arch/powerpc/Kconfig.debug
@@ -277,6 +277,12 @@ config PPC_EARLY_DEBUG_MEMCONS
 	  This console provides input and output buffers stored within the
 	  kernel BSS and should be safe to select on any system. A debugger
 	  can then be used to read kernel output or send input to the console.
+
+config PPC_EARLY_DEBUG_MICROWATT
+       bool "Microwatt potato UART"
+       help
+         Select this to enable early debugging using the potato UART
+	 included in the Microwatt SOC.
 endchoice
 
 config PPC_MEMCONS_OUTPUT_SIZE
diff --git a/arch/powerpc/include/asm/udbg.h b/arch/powerpc/include/asm/udbg.h
index 0ea9e70ed78b..2dbd2d3b0591 100644
--- a/arch/powerpc/include/asm/udbg.h
+++ b/arch/powerpc/include/asm/udbg.h
@@ -53,6 +53,7 @@ extern void __init udbg_init_ehv_bc(void);
 extern void __init udbg_init_ps3gelic(void);
 extern void __init udbg_init_debug_opal_raw(void);
 extern void __init udbg_init_debug_opal_hvsi(void);
+extern void __init udbg_init_debug_microwatt(void);
 
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_UDBG_H */
diff --git a/arch/powerpc/kernel/udbg.c b/arch/powerpc/kernel/udbg.c
index 01595e8cafe7..e614993021c6 100644
--- a/arch/powerpc/kernel/udbg.c
+++ b/arch/powerpc/kernel/udbg.c
@@ -67,6 +67,8 @@ void __init udbg_early_init(void)
 	udbg_init_debug_opal_raw();
 #elif defined(CONFIG_PPC_EARLY_DEBUG_OPAL_HVSI)
 	udbg_init_debug_opal_hvsi();
+#elif defined(CONFIG_PPC_EARLY_DEBUG_MICROWATT)
+	udbg_init_debug_microwatt();
 #endif
 
 #ifdef CONFIG_PPC_EARLY_DEBUG
diff --git a/arch/powerpc/platforms/microwatt/setup.c b/arch/powerpc/platforms/microwatt/setup.c
index 3cfc5955a6fe..a5145adeaae7 100644
--- a/arch/powerpc/platforms/microwatt/setup.c
+++ b/arch/powerpc/platforms/microwatt/setup.c
@@ -10,8 +10,115 @@
 #include <linux/init.h>
 #include <linux/of.h>
 #include <asm/machdep.h>
+#include <asm/udbg.h>
+#include <asm/reg.h>
 #include <asm/time.h>
 
+static u64 potato_uart_base;
+
+#define PROC_FREQ 100000000
+#define UART_FREQ 115200
+#define UART_BASE 0xc0002000
+
+#define POTATO_CONSOLE_TX		0x00
+#define POTATO_CONSOLE_RX		0x08
+#define POTATO_CONSOLE_STATUS		0x10
+#define   POTATO_CONSOLE_STATUS_RX_EMPTY		0x01
+#define   POTATO_CONSOLE_STATUS_TX_EMPTY		0x02
+#define   POTATO_CONSOLE_STATUS_RX_FULL			0x04
+#define   POTATO_CONSOLE_STATUS_TX_FULL			0x08
+#define POTATO_CONSOLE_CLOCK_DIV	0x18
+#define POTATO_CONSOLE_IRQ_EN		0x20
+
+static u64 potato_uart_reg_read(int offset)
+{
+	u64 val, msr;
+
+	msr = mfmsr();
+	__asm__ volatile("mtmsrd %3,0; ldcix %0,%1,%2; mtmsrd %4,0"
+			 : "=r" (val) : "b" (potato_uart_base), "r" (offset),
+			   "r" (msr & ~MSR_DR), "r" (msr));
+
+	return val;
+}
+
+static void potato_uart_reg_write(int offset, u64 val)
+{
+	u64 msr;
+
+	msr = mfmsr();
+	__asm__ volatile("mtmsrd %3,0; stdcix %0,%1,%2; mtmsrd %4,0"
+			 : : "r" (val), "b" (potato_uart_base), "r" (offset),
+			   "r" (msr & ~MSR_DR), "r" (msr));
+}
+
+static int potato_uart_rx_empty(void)
+{
+	u64 val;
+
+	val = potato_uart_reg_read(POTATO_CONSOLE_STATUS);
+
+	if (val & POTATO_CONSOLE_STATUS_RX_EMPTY)
+		return 1;
+
+	return 0;
+}
+
+static int potato_uart_tx_full(void)
+{
+	u64 val;
+
+	val = potato_uart_reg_read(POTATO_CONSOLE_STATUS);
+
+	if (val & POTATO_CONSOLE_STATUS_TX_FULL)
+		return 1;
+
+	return 0;
+}
+
+static int potato_uart_read(void)
+{
+	while (potato_uart_rx_empty())
+		;
+	return potato_uart_reg_read(POTATO_CONSOLE_RX);
+}
+
+static int potato_uart_read_poll(void)
+{
+	if (potato_uart_rx_empty())
+		return -1;
+	return potato_uart_reg_read(POTATO_CONSOLE_RX);
+}
+
+static void potato_uart_write(char c)
+{
+	if (c == '\n')
+		potato_uart_write('\r');
+	while (potato_uart_tx_full())
+		;
+	potato_uart_reg_write(POTATO_CONSOLE_TX, c);
+}
+
+static unsigned long potato_uart_divisor(unsigned long proc_freq, unsigned long uart_freq)
+{
+	return proc_freq / (uart_freq * 16) - 1;
+}
+
+void potato_uart_init(void)
+{
+	potato_uart_base = UART_BASE;
+
+	potato_uart_reg_write(POTATO_CONSOLE_CLOCK_DIV, potato_uart_divisor(PROC_FREQ, UART_FREQ));
+}
+
+void udbg_init_debug_microwatt(void)
+{
+	potato_uart_init();
+	udbg_putc = potato_uart_write;
+	udbg_getc = potato_uart_read;
+	udbg_getc_poll = potato_uart_read_poll;
+}
+
 static void __init microwatt_calibrate_decr(void)
 {
 	ppc_tb_freq = 100000000;
@@ -36,5 +143,6 @@ define_machine(microwatt) {
 	.probe			= microwatt_probe,
 	.setup_arch		= microwatt_setup_arch,
 	.init_IRQ		= microwatt_init_IRQ,
+	.progress		= udbg_progress,
 	.calibrate_decr		= microwatt_calibrate_decr,
 };
-- 
2.25.3



More information about the Linuxppc-dev mailing list