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 <pau...@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

Reply via email to