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