Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 609cf6f2 authored by Paul Burton's avatar Paul Burton Committed by Ralf Baechle
Browse files

MIPS: CPS: Early debug using an ns16550-compatible UART



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: default avatarPaul 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: default avatarRalf Baechle <ralf@linux-mips.org>
parent 195cee92
Loading
Loading
Loading
Loading
+26 −0
Original line number Original line Diff line number Diff line
@@ -159,4 +159,30 @@ config SCACHE_DEBUGFS


	  If unsure, say N.
	  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
endmenu
+3 −0
Original line number Original line Diff line number Diff line
@@ -52,6 +52,7 @@
#define CP0_INFO $7
#define CP0_INFO $7
#define CP0_HWRENA $7, 0
#define CP0_HWRENA $7, 0
#define CP0_BADVADDR $8
#define CP0_BADVADDR $8
#define CP0_BADINSTR $8, 1
#define CP0_COUNT $9
#define CP0_COUNT $9
#define CP0_ENTRYHI $10
#define CP0_ENTRYHI $10
#define CP0_COMPARE $11
#define CP0_COMPARE $11
@@ -59,6 +60,8 @@
#define CP0_CAUSE $13
#define CP0_CAUSE $13
#define CP0_EPC $14
#define CP0_EPC $14
#define CP0_PRID $15
#define CP0_PRID $15
#define CP0_EBASE $15, 1
#define CP0_CMGCRBASE $15, 3
#define CP0_CONFIG $16
#define CP0_CONFIG $16
#define CP0_CONFIG3 $16, 3
#define CP0_CONFIG3 $16, 3
#define CP0_CONFIG5 $16, 5
#define CP0_CONFIG5 $16, 5
+1 −0
Original line number Original line Diff line number Diff line
@@ -51,6 +51,7 @@ obj-$(CONFIG_MIPS_MT_FPAFF) += mips-mt-fpaff.o
obj-$(CONFIG_MIPS_MT_SMP)	+= smp-mt.o
obj-$(CONFIG_MIPS_MT_SMP)	+= smp-mt.o
obj-$(CONFIG_MIPS_CMP)		+= smp-cmp.o
obj-$(CONFIG_MIPS_CMP)		+= smp-cmp.o
obj-$(CONFIG_MIPS_CPS)		+= smp-cps.o cps-vec.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_GIC_IPI)	+= smp-gic.o
obj-$(CONFIG_MIPS_SPRAM)	+= spram.o
obj-$(CONFIG_MIPS_SPRAM)	+= spram.o


+202 −0
Original line number Original line Diff line number Diff line
/*
 * 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
+20 −0
Original line number Original line Diff line number Diff line
@@ -31,6 +31,20 @@
# define STATUS_BITDEPS		0
# define STATUS_BITDEPS		0
#endif
#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
	 * Set dest to non-zero if the core supports the MT ASE, else zero. If
	 * MT is not supported then branch to nomt.
	 * MT is not supported then branch to nomt.
@@ -194,36 +208,42 @@ dcache_done:


.org 0x200
.org 0x200
LEAF(excep_tlbfill)
LEAF(excep_tlbfill)
	DUMP_EXCEP("TLB Fill")
	b	.
	b	.
	 nop
	 nop
	END(excep_tlbfill)
	END(excep_tlbfill)


.org 0x280
.org 0x280
LEAF(excep_xtlbfill)
LEAF(excep_xtlbfill)
	DUMP_EXCEP("XTLB Fill")
	b	.
	b	.
	 nop
	 nop
	END(excep_xtlbfill)
	END(excep_xtlbfill)


.org 0x300
.org 0x300
LEAF(excep_cache)
LEAF(excep_cache)
	DUMP_EXCEP("Cache")
	b	.
	b	.
	 nop
	 nop
	END(excep_cache)
	END(excep_cache)


.org 0x380
.org 0x380
LEAF(excep_genex)
LEAF(excep_genex)
	DUMP_EXCEP("General")
	b	.
	b	.
	 nop
	 nop
	END(excep_genex)
	END(excep_genex)


.org 0x400
.org 0x400
LEAF(excep_intex)
LEAF(excep_intex)
	DUMP_EXCEP("Interrupt")
	b	.
	b	.
	 nop
	 nop
	END(excep_intex)
	END(excep_intex)


.org 0x480
.org 0x480
LEAF(excep_ejtag)
LEAF(excep_ejtag)
	DUMP_EXCEP("EJTAG")
	PTR_LA	k0, ejtag_debug_handler
	PTR_LA	k0, ejtag_debug_handler
	jr	k0
	jr	k0
	 nop
	 nop