[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