1*45b2bd0aSStephan Gerhold/* 2*45b2bd0aSStephan Gerhold * Copyright (c) 2021-2023, Stephan Gerhold <stephan@gerhold.net> 3*45b2bd0aSStephan Gerhold * 4*45b2bd0aSStephan Gerhold * Based on aarch32/skeleton_console.S: 5*45b2bd0aSStephan Gerhold * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. 6*45b2bd0aSStephan Gerhold * 7*45b2bd0aSStephan Gerhold * SPDX-License-Identifier: BSD-3-Clause 8*45b2bd0aSStephan Gerhold */ 9*45b2bd0aSStephan Gerhold 10*45b2bd0aSStephan Gerhold#include <asm_macros.S> 11*45b2bd0aSStephan Gerhold#include <console_macros.S> 12*45b2bd0aSStephan Gerhold 13*45b2bd0aSStephan Gerhold/* UART DM registers */ 14*45b2bd0aSStephan Gerhold#define UART_DM_DMEN 0x03c /* DMA / data packing */ 15*45b2bd0aSStephan Gerhold#define UART_DM_SR 0x0a4 /* status register */ 16*45b2bd0aSStephan Gerhold#define UART_DM_CR 0x0a8 /* command register */ 17*45b2bd0aSStephan Gerhold#define UART_DM_TF 0x100 /* transmit FIFO */ 18*45b2bd0aSStephan Gerhold 19*45b2bd0aSStephan Gerhold#define UART_DM_DMEN_TX_SC BIT_32(4) /* TX single character mode */ 20*45b2bd0aSStephan Gerhold 21*45b2bd0aSStephan Gerhold#define UART_DM_SR_TXRDY BIT_32(2) /* TX FIFO has space */ 22*45b2bd0aSStephan Gerhold#define UART_DM_SR_TXEMT BIT_32(3) /* TX FIFO is empty */ 23*45b2bd0aSStephan Gerhold 24*45b2bd0aSStephan Gerhold#define UART_DM_CR_RESET_RX (U(0x01) << 4) /* reset receiver */ 25*45b2bd0aSStephan Gerhold#define UART_DM_CR_RESET_TX (U(0x02) << 4) /* reset transmitter */ 26*45b2bd0aSStephan Gerhold#define UART_DM_CR_TX_ENABLE BIT_32(2) /* enable transmitter */ 27*45b2bd0aSStephan Gerhold 28*45b2bd0aSStephan Gerhold .globl console_uartdm_register 29*45b2bd0aSStephan Gerhold .globl console_uartdm_core_init 30*45b2bd0aSStephan Gerhold .globl console_uartdm_putc 31*45b2bd0aSStephan Gerhold .globl console_uartdm_core_putc 32*45b2bd0aSStephan Gerhold .globl console_uartdm_flush 33*45b2bd0aSStephan Gerhold .globl console_uartdm_core_flush 34*45b2bd0aSStephan Gerhold 35*45b2bd0aSStephan Gerhold /* ----------------------------------------------------------- 36*45b2bd0aSStephan Gerhold * int console_uartdm_register(console_t *console, 37*45b2bd0aSStephan Gerhold * uintptr_t base_addr) 38*45b2bd0aSStephan Gerhold * Function to initialize and register the console. The caller 39*45b2bd0aSStephan Gerhold * needs to pass an empty console_t structure in which *MUST* 40*45b2bd0aSStephan Gerhold * be allocated in persistent memory (e.g. a global or static 41*45b2bd0aSStephan Gerhold * local variable, *NOT* on the stack). 42*45b2bd0aSStephan Gerhold * In : r0 - pointer to empty console_t structure 43*45b2bd0aSStephan Gerhold * r1 - base address 44*45b2bd0aSStephan Gerhold * Out: r0 - 1 on success, 0 on error 45*45b2bd0aSStephan Gerhold * Clobber list : r0 - r7 46*45b2bd0aSStephan Gerhold * ----------------------------------------------------------- 47*45b2bd0aSStephan Gerhold */ 48*45b2bd0aSStephan Gerholdfunc console_uartdm_register 49*45b2bd0aSStephan Gerhold str r1, [r0, #CONSOLE_T_BASE] 50*45b2bd0aSStephan Gerhold mov r7, lr 51*45b2bd0aSStephan Gerhold bl console_uartdm_core_init 52*45b2bd0aSStephan Gerhold mov lr, r7 53*45b2bd0aSStephan Gerhold 54*45b2bd0aSStephan Gerhold /* Register the new console */ 55*45b2bd0aSStephan Gerhold finish_console_register uartdm putc=1, flush=1 56*45b2bd0aSStephan Gerholdendfunc console_uartdm_register 57*45b2bd0aSStephan Gerhold 58*45b2bd0aSStephan Gerhold /* ----------------------------------------------------------- 59*45b2bd0aSStephan Gerhold * void console_uartdm_core_init(unused, uintptr_t base_addr) 60*45b2bd0aSStephan Gerhold * Function to initialize the console. 61*45b2bd0aSStephan Gerhold * In : r0 - unused 62*45b2bd0aSStephan Gerhold * r1 - base address 63*45b2bd0aSStephan Gerhold * Out: void 64*45b2bd0aSStephan Gerhold * Clobber list : r1, r2, r3 65*45b2bd0aSStephan Gerhold * ----------------------------------------------------------- 66*45b2bd0aSStephan Gerhold */ 67*45b2bd0aSStephan Gerholdfunc console_uartdm_core_init 68*45b2bd0aSStephan Gerhold /* 69*45b2bd0aSStephan Gerhold * Try to flush remaining characters from the TX FIFO before resetting 70*45b2bd0aSStephan Gerhold * the transmitter. Unfortunately there is no good way to check if 71*45b2bd0aSStephan Gerhold * the transmitter is actually enabled (and will finish eventually), 72*45b2bd0aSStephan Gerhold * so use a timeout to avoid looping forever. 73*45b2bd0aSStephan Gerhold */ 74*45b2bd0aSStephan Gerhold mov r2, #65536 75*45b2bd0aSStephan Gerhold1: 76*45b2bd0aSStephan Gerhold ldr r3, [r1, #UART_DM_SR] 77*45b2bd0aSStephan Gerhold tst r3, #UART_DM_SR_TXEMT 78*45b2bd0aSStephan Gerhold bne 2f 79*45b2bd0aSStephan Gerhold subs r2, r2, #1 80*45b2bd0aSStephan Gerhold bne 1b 81*45b2bd0aSStephan Gerhold /* Timeout */ 82*45b2bd0aSStephan Gerhold 83*45b2bd0aSStephan Gerhold2: /* Reset receiver */ 84*45b2bd0aSStephan Gerhold mov r3, #UART_DM_CR_RESET_RX 85*45b2bd0aSStephan Gerhold str r3, [r1, #UART_DM_CR] 86*45b2bd0aSStephan Gerhold 87*45b2bd0aSStephan Gerhold /* Reset transmitter */ 88*45b2bd0aSStephan Gerhold mov r3, #UART_DM_CR_RESET_TX 89*45b2bd0aSStephan Gerhold str r3, [r1, #UART_DM_CR] 90*45b2bd0aSStephan Gerhold 91*45b2bd0aSStephan Gerhold /* 92*45b2bd0aSStephan Gerhold * Disable BAM/DMA modes but enable single-character mode for TX. 93*45b2bd0aSStephan Gerhold * The single character mode allows simplifying the putc implementation 94*45b2bd0aSStephan Gerhold * since characters can be written directly to the FIFO instead of 95*45b2bd0aSStephan Gerhold * having to initiate a new transfer and waiting for its completion. 96*45b2bd0aSStephan Gerhold */ 97*45b2bd0aSStephan Gerhold mov r3, #UART_DM_DMEN_TX_SC 98*45b2bd0aSStephan Gerhold str r3, [r1, #UART_DM_DMEN] 99*45b2bd0aSStephan Gerhold 100*45b2bd0aSStephan Gerhold /* Enable transmitter */ 101*45b2bd0aSStephan Gerhold mov r3, #UART_DM_CR_TX_ENABLE 102*45b2bd0aSStephan Gerhold str r3, [r1, #UART_DM_CR] 103*45b2bd0aSStephan Gerhold 104*45b2bd0aSStephan Gerhold bx lr 105*45b2bd0aSStephan Gerholdendfunc console_uartdm_core_init 106*45b2bd0aSStephan Gerhold 107*45b2bd0aSStephan Gerhold /* ----------------------------------------------------------- 108*45b2bd0aSStephan Gerhold * int console_uartdm_putc(int c, console_t *console) 109*45b2bd0aSStephan Gerhold * Function to output a character over the console. 110*45b2bd0aSStephan Gerhold * In : r0 - character to be printed 111*45b2bd0aSStephan Gerhold * r1 - pointer to console_t struct 112*45b2bd0aSStephan Gerhold * Out: r0 - printed character on success, < 0 on error. 113*45b2bd0aSStephan Gerhold * Clobber list : r0, r1, r2 114*45b2bd0aSStephan Gerhold * ----------------------------------------------------------- 115*45b2bd0aSStephan Gerhold */ 116*45b2bd0aSStephan Gerholdfunc console_uartdm_putc 117*45b2bd0aSStephan Gerhold ldr r1, [r1, #CONSOLE_T_BASE] 118*45b2bd0aSStephan Gerhold b console_uartdm_core_putc 119*45b2bd0aSStephan Gerholdendfunc console_uartdm_putc 120*45b2bd0aSStephan Gerhold 121*45b2bd0aSStephan Gerhold /* ----------------------------------------------------------- 122*45b2bd0aSStephan Gerhold * int console_uartdm_core_putc(int c, uintptr_t base_addr) 123*45b2bd0aSStephan Gerhold * Function to output a character over the console. 124*45b2bd0aSStephan Gerhold * In : r0 - character to be printed 125*45b2bd0aSStephan Gerhold * r1 - base address 126*45b2bd0aSStephan Gerhold * Out: r0 - printed character on success, < 0 on error. 127*45b2bd0aSStephan Gerhold * Clobber list : r2 128*45b2bd0aSStephan Gerhold * ----------------------------------------------------------- 129*45b2bd0aSStephan Gerhold */ 130*45b2bd0aSStephan Gerholdfunc console_uartdm_core_putc 131*45b2bd0aSStephan Gerhold cmp r0, #'\n' 132*45b2bd0aSStephan Gerhold bne 2f 133*45b2bd0aSStephan Gerhold 134*45b2bd0aSStephan Gerhold1: /* Loop until TX FIFO has space */ 135*45b2bd0aSStephan Gerhold ldr r2, [r1, #UART_DM_SR] 136*45b2bd0aSStephan Gerhold tst r2, #UART_DM_SR_TXRDY 137*45b2bd0aSStephan Gerhold beq 1b 138*45b2bd0aSStephan Gerhold 139*45b2bd0aSStephan Gerhold /* Prepend '\r' to '\n' */ 140*45b2bd0aSStephan Gerhold mov r2, #'\r' 141*45b2bd0aSStephan Gerhold str r2, [r1, #UART_DM_TF] 142*45b2bd0aSStephan Gerhold 143*45b2bd0aSStephan Gerhold2: /* Loop until TX FIFO has space */ 144*45b2bd0aSStephan Gerhold ldr r2, [r1, #UART_DM_SR] 145*45b2bd0aSStephan Gerhold tst r2, #UART_DM_SR_TXRDY 146*45b2bd0aSStephan Gerhold beq 2b 147*45b2bd0aSStephan Gerhold 148*45b2bd0aSStephan Gerhold /* Write character to FIFO */ 149*45b2bd0aSStephan Gerhold str r0, [r1, #UART_DM_TF] 150*45b2bd0aSStephan Gerhold bx lr 151*45b2bd0aSStephan Gerholdendfunc console_uartdm_core_putc 152*45b2bd0aSStephan Gerhold 153*45b2bd0aSStephan Gerhold /* ----------------------------------------------------------- 154*45b2bd0aSStephan Gerhold * void console_uartdm_flush(console_t *console) 155*45b2bd0aSStephan Gerhold * Function to force a write of all buffered data 156*45b2bd0aSStephan Gerhold * that has not been output. 157*45b2bd0aSStephan Gerhold * In : r0 - pointer to console_t struct 158*45b2bd0aSStephan Gerhold * Out: void 159*45b2bd0aSStephan Gerhold * Clobber list : r0, r1, r2, r3, r4, r5 160*45b2bd0aSStephan Gerhold * ----------------------------------------------------------- 161*45b2bd0aSStephan Gerhold */ 162*45b2bd0aSStephan Gerholdfunc console_uartdm_flush 163*45b2bd0aSStephan Gerhold ldr r1, [r0, #CONSOLE_T_BASE] 164*45b2bd0aSStephan Gerhold b console_uartdm_core_flush 165*45b2bd0aSStephan Gerholdendfunc console_uartdm_flush 166*45b2bd0aSStephan Gerhold 167*45b2bd0aSStephan Gerhold /* ----------------------------------------------------------- 168*45b2bd0aSStephan Gerhold * void console_uartdm_core_flush(unused, uintptr_t base_addr) 169*45b2bd0aSStephan Gerhold * Function to force a write of all buffered data 170*45b2bd0aSStephan Gerhold * that has not been output. 171*45b2bd0aSStephan Gerhold * In : r0 - unused 172*45b2bd0aSStephan Gerhold * r1 - base address 173*45b2bd0aSStephan Gerhold * Out: void 174*45b2bd0aSStephan Gerhold * Clobber list : r2 175*45b2bd0aSStephan Gerhold * ----------------------------------------------------------- 176*45b2bd0aSStephan Gerhold */ 177*45b2bd0aSStephan Gerholdfunc console_uartdm_core_flush 178*45b2bd0aSStephan Gerhold1: /* Loop until TX FIFO is empty */ 179*45b2bd0aSStephan Gerhold ldr r2, [r1, #UART_DM_SR] 180*45b2bd0aSStephan Gerhold tst r2, #UART_DM_SR_TXEMT 181*45b2bd0aSStephan Gerhold beq 1b 182*45b2bd0aSStephan Gerhold bx lr 183*45b2bd0aSStephan Gerholdendfunc console_uartdm_core_flush 184