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