root/arch/mips/kernel/cps-vec-ns16550.S
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2015 Imagination Technologies
 * Author: Paul Burton <paul.burton@mips.com>
 */

#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)

#if CONFIG_MIPS_CPS_NS16550_WIDTH == 1
# define UART_L         lb
# define UART_S         sb
#elif CONFIG_MIPS_CPS_NS16550_WIDTH == 2
# define UART_L         lh
# define UART_S         sh
#elif CONFIG_MIPS_CPS_NS16550_WIDTH == 4
# define UART_L         lw
# define UART_S         sw
#else
# define UART_L         lb
# define UART_S         sb
#endif

/**
 * _mips_cps_putc() - write a character to the UART
 * @a0: ASCII character to write
 * @t9: UART base address
 */
LEAF(_mips_cps_putc)
1:      UART_L          t0, UART_LSR_OFS(t9)
        andi            t0, t0, UART_LSR_TEMT
        beqz            t0, 1b
        UART_S          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