MIPS: CPS: Early debug using an ns16550-compatible UART
authorPaul Burton <paul.burton@imgtec.com>
Tue, 22 Sep 2015 18:12:11 +0000 (11:12 -0700)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 11 Nov 2015 07:34:25 +0000 (08:34 +0100)
Provide support for outputting early debug information, in the form of
various register values should an exception occur, during the early
bringup of secondary cores. This code requires an ns16550-compatible
UART accessible from the secondary core, and is written in assembly due
to the environment in which such early exceptions occur where way may
not have a stack, be coherent or even have initialised caches.

[ralf@linux-mips.org: Fix merge conflict.]

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: Steven J. Hill <Steven.Hill@imgtec.com>
Cc: Andrew Bresticker <abrestic@chromium.org>
Cc: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Cc: Maciej W. Rozycki <macro@linux-mips.org>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: linux-kernel@vger.kernel.org
Cc: James Hogan <james.hogan@imgtec.com>
Cc: Markos Chandras <markos.chandras@imgtec.com>
Patchwork: https://patchwork.linux-mips.org/patch/11202/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/Kconfig.debug
arch/mips/include/asm/mipsregs.h
arch/mips/kernel/Makefile
arch/mips/kernel/cps-vec-ns16550.S [new file with mode: 0644]
arch/mips/kernel/cps-vec.S

index b43e288ec56749d73be4b2de45a505ad6dbaa41c..f0e314ceb8baa84d36ea6664772f71df46ad003f 100644 (file)
@@ -159,4 +159,30 @@ config SCACHE_DEBUGFS
 
          If unsure, say N.
 
+menuconfig MIPS_CPS_NS16550
+       bool "CPS SMP NS16550 UART output"
+       depends on MIPS_CPS
+       help
+         Output debug information via an ns16550 compatible UART if exceptions
+         occur early in the boot process of a secondary core.
+
+if MIPS_CPS_NS16550
+
+config MIPS_CPS_NS16550_BASE
+       hex "UART Base Address"
+       default 0x1b0003f8 if MIPS_MALTA
+       help
+         The base address of the ns16550 compatible UART on which to output
+         debug information from the early stages of core startup.
+
+config MIPS_CPS_NS16550_SHIFT
+       int "UART Register Shift"
+       default 0 if MIPS_MALTA
+       help
+         The number of bits to shift ns16550 register indices by in order to
+         form their addresses. That is, log base 2 of the span between
+         adjacent ns16550 registers in the system.
+
+endif # MIPS_CPS_NS16550
+
 endmenu
index f935e15adcc692a7ffacd55e8486251a9b61dff3..69f8ec548eac97580b07ad5b235e7f0014951b34 100644 (file)
@@ -52,6 +52,7 @@
 #define CP0_INFO $7
 #define CP0_HWRENA $7, 0
 #define CP0_BADVADDR $8
+#define CP0_BADINSTR $8, 1
 #define CP0_COUNT $9
 #define CP0_ENTRYHI $10
 #define CP0_COMPARE $11
@@ -59,6 +60,8 @@
 #define CP0_CAUSE $13
 #define CP0_EPC $14
 #define CP0_PRID $15
+#define CP0_EBASE $15, 1
+#define CP0_CMGCRBASE $15, 3
 #define CP0_CONFIG $16
 #define CP0_CONFIG3 $16, 3
 #define CP0_CONFIG5 $16, 5
index d982be1ea1c3f3125da9762f3e1b203ae939b1d0..68e2b7db9348f1d168666411544a90bd080d0ae0 100644 (file)
@@ -51,6 +51,7 @@ obj-$(CONFIG_MIPS_MT_FPAFF)   += mips-mt-fpaff.o
 obj-$(CONFIG_MIPS_MT_SMP)      += smp-mt.o
 obj-$(CONFIG_MIPS_CMP)         += smp-cmp.o
 obj-$(CONFIG_MIPS_CPS)         += smp-cps.o cps-vec.o
+obj-$(CONFIG_MIPS_CPS_NS16550) += cps-vec-ns16550.o
 obj-$(CONFIG_MIPS_GIC_IPI)     += smp-gic.o
 obj-$(CONFIG_MIPS_SPRAM)       += spram.o
 
diff --git a/arch/mips/kernel/cps-vec-ns16550.S b/arch/mips/kernel/cps-vec-ns16550.S
new file mode 100644 (file)
index 0000000..6d246ad
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <linux/serial_reg.h>
+
+#define UART_TX_OFS    (UART_TX << CONFIG_MIPS_CPS_NS16550_SHIFT)
+#define UART_LSR_OFS   (UART_LSR << CONFIG_MIPS_CPS_NS16550_SHIFT)
+
+/**
+ * _mips_cps_putc() - write a character to the UART
+ * @a0: ASCII character to write
+ * @t9: UART base address
+ */
+LEAF(_mips_cps_putc)
+1:     lw              t0, UART_LSR_OFS(t9)
+       andi            t0, t0, UART_LSR_TEMT
+       beqz            t0, 1b
+       sb              a0, UART_TX_OFS(t9)
+       jr              ra
+       END(_mips_cps_putc)
+
+/**
+ * _mips_cps_puts() - write a string to the UART
+ * @a0: pointer to NULL-terminated ASCII string
+ * @t9: UART base address
+ *
+ * Write a null-terminated ASCII string to the UART.
+ */
+NESTED(_mips_cps_puts, 0, ra)
+       move            s7, ra
+       move            s6, a0
+
+1:     lb              a0, 0(s6)
+       beqz            a0, 2f
+       jal             _mips_cps_putc
+       PTR_ADDIU       s6, s6, 1
+       b               1b
+
+2:     jr              s7
+       END(_mips_cps_puts)
+
+/**
+ * _mips_cps_putx4 - write a 4b hex value to the UART
+ * @a0: the 4b value to write to the UART
+ * @t9: UART base address
+ *
+ * Write a single hexadecimal character to the UART.
+ */
+NESTED(_mips_cps_putx4, 0, ra)
+       andi            a0, a0, 0xf
+       li              t0, '0'
+       blt             a0, 10, 1f
+       li              t0, 'a'
+       addiu           a0, a0, -10
+1:     addu            a0, a0, t0
+       b               _mips_cps_putc
+       END(_mips_cps_putx4)
+
+/**
+ * _mips_cps_putx8 - write an 8b hex value to the UART
+ * @a0: the 8b value to write to the UART
+ * @t9: UART base address
+ *
+ * Write an 8 bit value (ie. 2 hexadecimal characters) to the UART.
+ */
+NESTED(_mips_cps_putx8, 0, ra)
+       move            s3, ra
+       move            s2, a0
+       srl             a0, a0, 4
+       jal             _mips_cps_putx4
+       move            a0, s2
+       move            ra, s3
+       b               _mips_cps_putx4
+       END(_mips_cps_putx8)
+
+/**
+ * _mips_cps_putx16 - write a 16b hex value to the UART
+ * @a0: the 16b value to write to the UART
+ * @t9: UART base address
+ *
+ * Write a 16 bit value (ie. 4 hexadecimal characters) to the UART.
+ */
+NESTED(_mips_cps_putx16, 0, ra)
+       move            s5, ra
+       move            s4, a0
+       srl             a0, a0, 8
+       jal             _mips_cps_putx8
+       move            a0, s4
+       move            ra, s5
+       b               _mips_cps_putx8
+       END(_mips_cps_putx16)
+
+/**
+ * _mips_cps_putx32 - write a 32b hex value to the UART
+ * @a0: the 32b value to write to the UART
+ * @t9: UART base address
+ *
+ * Write a 32 bit value (ie. 8 hexadecimal characters) to the UART.
+ */
+NESTED(_mips_cps_putx32, 0, ra)
+       move            s7, ra
+       move            s6, a0
+       srl             a0, a0, 16
+       jal             _mips_cps_putx16
+       move            a0, s6
+       move            ra, s7
+       b               _mips_cps_putx16
+       END(_mips_cps_putx32)
+
+#ifdef CONFIG_64BIT
+
+/**
+ * _mips_cps_putx64 - write a 64b hex value to the UART
+ * @a0: the 64b value to write to the UART
+ * @t9: UART base address
+ *
+ * Write a 64 bit value (ie. 16 hexadecimal characters) to the UART.
+ */
+NESTED(_mips_cps_putx64, 0, ra)
+       move            sp, ra
+       move            s8, a0
+       dsrl32          a0, a0, 0
+       jal             _mips_cps_putx32
+       move            a0, s8
+       move            ra, sp
+       b               _mips_cps_putx32
+       END(_mips_cps_putx64)
+
+#define _mips_cps_putxlong _mips_cps_putx64
+
+#else /* !CONFIG_64BIT */
+
+#define _mips_cps_putxlong _mips_cps_putx32
+
+#endif /* !CONFIG_64BIT */
+
+/**
+ * mips_cps_bev_dump() - dump relevant exception state to UART
+ * @a0: pointer to NULL-terminated ASCII string naming the exception
+ *
+ * Write information that may be useful in debugging an exception to the
+ * UART configured by CONFIG_MIPS_CPS_NS16550_*. As this BEV exception
+ * will only be run if something goes horribly wrong very early during
+ * the bringup of a core and it is very likely to be unsafe to perform
+ * memory accesses at that point (cache state indeterminate, EVA may not
+ * be configured, coherence may be disabled) let alone have a stack,
+ * this is all written in assembly using only registers & unmapped
+ * uncached access to the UART registers.
+ */
+LEAF(mips_cps_bev_dump)
+       move            s0, ra
+       move            s1, a0
+
+       li              t9, CKSEG1ADDR(CONFIG_MIPS_CPS_NS16550_BASE)
+
+       PTR_LA          a0, str_newline
+       jal             _mips_cps_puts
+       PTR_LA          a0, str_bev
+       jal             _mips_cps_puts
+       move            a0, s1
+       jal             _mips_cps_puts
+       PTR_LA          a0, str_newline
+       jal             _mips_cps_puts
+       PTR_LA          a0, str_newline
+       jal             _mips_cps_puts
+
+#define DUMP_COP0_REG(reg, name, sz, _mfc0)            \
+       PTR_LA          a0, 8f;                         \
+       jal             _mips_cps_puts;                 \
+       _mfc0           a0, reg;                        \
+       jal             _mips_cps_putx##sz;             \
+       PTR_LA          a0, str_newline;                \
+       jal             _mips_cps_puts;                 \
+       TEXT(name)
+
+       DUMP_COP0_REG(CP0_CAUSE,    "Cause:    0x", 32, mfc0)
+       DUMP_COP0_REG(CP0_STATUS,   "Status:   0x", 32, mfc0)
+       DUMP_COP0_REG(CP0_EBASE,    "EBase:    0x", long, MFC0)
+       DUMP_COP0_REG(CP0_BADVADDR, "BadVAddr: 0x", long, MFC0)
+       DUMP_COP0_REG(CP0_BADINSTR, "BadInstr: 0x", 32, mfc0)
+
+       PTR_LA          a0, str_newline
+       jal             _mips_cps_puts
+       jr              s0
+       END(mips_cps_bev_dump)
+
+.pushsection   .data
+str_bev: .asciiz "BEV Exception: "
+str_newline: .asciiz "\r\n"
+.popsection
index f2663e460d374cc6e9626e2ebd284ddfcde47d8c..b242ae3eb4663e247386f3215034e793a8e054f6 100644 (file)
 # define STATUS_BITDEPS                0
 #endif
 
+#ifdef CONFIG_MIPS_CPS_NS16550
+
+#define DUMP_EXCEP(name)               \
+       PTR_LA  a0, 8f;                 \
+       jal     mips_cps_bev_dump;      \
+        nop;                           \
+       TEXT(name)
+
+#else /* !CONFIG_MIPS_CPS_NS16550 */
+
+#define DUMP_EXCEP(name)
+
+#endif /* !CONFIG_MIPS_CPS_NS16550 */
+
        /*
         * Set dest to non-zero if the core supports the MT ASE, else zero. If
         * MT is not supported then branch to nomt.
@@ -194,36 +208,42 @@ dcache_done:
 
 .org 0x200
 LEAF(excep_tlbfill)
+       DUMP_EXCEP("TLB Fill")
        b       .
         nop
        END(excep_tlbfill)
 
 .org 0x280
 LEAF(excep_xtlbfill)
+       DUMP_EXCEP("XTLB Fill")
        b       .
         nop
        END(excep_xtlbfill)
 
 .org 0x300
 LEAF(excep_cache)
+       DUMP_EXCEP("Cache")
        b       .
         nop
        END(excep_cache)
 
 .org 0x380
 LEAF(excep_genex)
+       DUMP_EXCEP("General")
        b       .
         nop
        END(excep_genex)
 
 .org 0x400
 LEAF(excep_intex)
+       DUMP_EXCEP("Interrupt")
        b       .
         nop
        END(excep_intex)
 
 .org 0x480
 LEAF(excep_ejtag)
+       DUMP_EXCEP("EJTAG")
        PTR_LA  k0, ejtag_debug_handler
        jr      k0
         nop