blob: e23b20e56ae7083e0b2ca4b7317df01b0da64f04 [file] [log] [blame]
Daniel Boulbyec249282018-09-19 13:55:47 +01001/*
2 * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <asm_macros.S>
8#include <assert_macros.S>
9#include <console.h>
10
11 .globl console_register
12 .globl console_unregister
13 .globl console_is_registered
14 .globl console_set_scope
15 .globl console_switch_state
16 .globl console_putc
17 .globl console_getc
18 .globl console_flush
19
20 /*
21 * The console list pointer is in the data section and not in
22 * .bss even though it is zero-init. In particular, this allows
23 * the console functions to start using this variable before
24 * the runtime memory is initialized for images which do not
25 * need to copy the .data section from ROM to RAM.
26 */
27.section .data.console_list ; .align 2
28 console_list: .word 0x0
29.section .data.console_state ; .align 0
30 console_state: .byte CONSOLE_FLAG_BOOT
31
32 /* -----------------------------------------------
33 * int console_register(console_t *console)
34 * Function to insert a new console structure into
35 * the console list. Should usually be called by
36 * console_<driver>_register implementations. The
37 * data structure passed will be taken over by the
38 * console framework and *MUST* be allocated in
39 * persistent memory (e.g. the data section).
40 * In : r0 - address of console_t structure
41 * Out: r0 - Always 1 (for easier tail calling)
42 * Clobber list: r0, r1
43 * -----------------------------------------------
44 */
45func console_register
46 push {r6, lr}
47#if ENABLE_ASSERTIONS
48 /* Assert that r0 isn't a NULL pointer */
49 cmp r0, #0
50 ASM_ASSERT(ne)
51 /* Assert that the struct isn't in the stack */
52 ldr r1, =__STACKS_START__
53 cmp r0, r1
54 blo not_on_stack
55 ldr r1, =__STACKS_END__
56 cmp r0, r1
57 ASM_ASSERT(hs)
58not_on_stack:
59 /* Assert that this struct isn't in the list */
60 mov r1, r0 /* Preserve r0 and lr */
61 bl console_is_registered
62 cmp r0, #0
63 ASM_ASSERT(eq)
64 mov r0, r1
65#endif /* ENABLE_ASSERTIONS */
66 ldr r6, =console_list
67 ldr r1, [r6] /* R1 = first struct in list */
68 str r0, [r6] /* list head = new console */
69 str r1, [r0, #CONSOLE_T_NEXT] /* new console next ptr = R1 */
70 mov r0, #1
71 pop {r6, pc}
72endfunc console_register
73
74 /* -----------------------------------------------
75 * int console_unregister(console_t *console)
76 * Function to find a specific console in the list
77 * of currently active consoles and remove it.
78 * In: r0 - address of console_t struct to remove
79 * Out: r0 - removed address, or NULL if not found
80 * Clobber list: r0, r1
81 * -----------------------------------------------
82 */
83func console_unregister
84#if ENABLE_ASSERTIONS
85 /* Assert that r0 isn't a NULL pointer */
86 cmp r0, #0
87 ASM_ASSERT(ne)
88#endif /* ENABLE_ASSERTIONS */
89 push {r6}
90 ldr r6, =console_list /* R6 = ptr to first struct */
91 ldr r1, [r6] /* R1 = first struct */
92
93unregister_loop:
94 cmp r1, #0
95 beq unregister_not_found
96 cmp r0, r1
97 beq unregister_found
98 ldr r6, [r6] /* R6 = next ptr of struct */
99 ldr r1, [r6] /* R1 = next struct */
100 b unregister_loop
101
102unregister_found:
103 ldr r1, [r1] /* R1 = next struct */
104 str r1, [r6] /* prev->next = cur->next */
105 pop {r6}
106 bx lr
107
108unregister_not_found:
109 mov r0, #0 /* return NULL if not found */
110 pop {r6}
111 bx lr
112endfunc console_unregister
113
114 /* -----------------------------------------------
115 * int console_is_registered(console_t *console)
116 * Function to detect if a specific console is
117 * registered or not.
118 * In: r0 - address of console_t struct to remove
119 * Out: r0 - 1 if it is registered, 0 if not.
120 * Clobber list: r0
121 * -----------------------------------------------
122 */
123func console_is_registered
124#if ENABLE_ASSERTIONS
125 /* Assert that r0 isn't a NULL pointer */
126 cmp r0, #0
127 ASM_ASSERT(ne)
128#endif /* ENABLE_ASSERTIONS */
129 push {r6}
130 ldr r6, =console_list
131 ldr r6, [r6] /* R6 = first console struct */
132check_registered_loop:
133 cmp r6, #0 /* Check if end of list */
134 beq console_not_registered
135 cmp r0, r6 /* Check if the pointers are different */
136 beq console_registered
137 ldr r6, [r6, #CONSOLE_T_NEXT] /* Get pointer to next struct */
138 b check_registered_loop
139console_not_registered:
140 mov r0, #0
141 pop {r6}
142 bx lr
143console_registered:
144 mov r0, #1
145 pop {r6}
146 bx lr
147endfunc console_is_registered
148
149 /* -----------------------------------------------
150 * void console_switch_state(unsigned int new_state)
151 * Function to switch the current console state.
152 * The console state determines which of the
153 * registered consoles are actually used at a time.
154 * In : r0 - global console state to move to
155 * Clobber list: r0, r1
156 * -----------------------------------------------
157 */
158func console_switch_state
159 ldr r1, =console_state
160 strb r0, [r1]
161 bx lr
162endfunc console_switch_state
163
164 /* -----------------------------------------------
165 * void console_set_scope(console_t *console,
166 * unsigned int scope)
167 * Function to update the states that a given console
168 * may be active in.
169 * In : r0 - pointer to console_t struct
170 * : r1 - new active state mask
171 * Clobber list: r0, r1, r2
172 * -----------------------------------------------
173 */
174func console_set_scope
175#if ENABLE_ASSERTIONS
176 ands r2, r1, #~CONSOLE_FLAG_SCOPE_MASK
177 ASM_ASSERT(eq)
178#endif /* ENABLE_ASSERTIONS */
179 ldr r2, [r0, #CONSOLE_T_FLAGS]
180 and r2, r2, #~CONSOLE_FLAG_SCOPE_MASK
181 orr r2, r2, r1
182 str r2, [r0, #CONSOLE_T_FLAGS]
183 bx lr
184endfunc console_set_scope
185
186 /* ---------------------------------------------
187 * int console_putc(int c)
188 * Function to output a character. Calls all
189 * active console's putc() handlers in succession.
190 * In : r0 - character to be printed
191 * Out: r0 - printed character on success, or < 0
192 if at least one console had an error
193 * Clobber list : r0, r1, r2
194 * ---------------------------------------------
195 */
196func console_putc
197 push {r4-r6, lr}
198 mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */
199 mov r4, r0 /* R4 = character to print */
200 ldr r6, =console_list
201 ldr r6, [r6] /* R6 = first console struct */
202
203putc_loop:
204 cmp r6, #0
205 beq putc_done
206 ldr r1, =console_state
207 ldrb r1, [r1]
208 ldr r2, [r6, #CONSOLE_T_FLAGS]
209 tst r1, r2
210 beq putc_continue
211 ldr r2, [r6, #CONSOLE_T_PUTC]
212 cmp r2, #0
213 beq putc_continue
214 mov r0, r4
215 mov r1, r6
216 blx r2
217 cmp r5, #ERROR_NO_VALID_CONSOLE /* update R5 if it's NOVALID */
218 cmpne r0, #0 /* else update it if R0 < 0 */
219 movlt r5, r0
220putc_continue:
221 ldr r6, [r6] /* R6 = next struct */
222 b putc_loop
223
224putc_done:
225 mov r0, r5
226 pop {r4-r6, pc}
227endfunc console_putc
228
229 /* ---------------------------------------------
230 * int console_getc(void)
231 * Function to get a character from any console.
232 * Keeps looping through all consoles' getc()
233 * handlers until one of them returns a
234 * character, then stops iterating and returns
235 * that character to the caller. Will stop looping
236 * if all active consoles report real errors
237 * (other than just not having a char available).
238 * Out : r0 - read character, or < 0 on error
239 * Clobber list : r0, r1
240 * ---------------------------------------------
241 */
242func console_getc
243 push {r5-r6, lr}
244getc_try_again:
245 mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */
246 ldr r6, =console_list
247 ldr r6, [r6] /* R6 = first console struct */
248 cmp r6, #0
249 bne getc_loop
250 mov r0, r5 /* If no consoles registered */
251 pop {r5-r6, pc} /* return immediately. */
252
253getc_loop:
254 ldr r0, =console_state
255 ldrb r0, [r0]
256 ldr r1, [r6, #CONSOLE_T_FLAGS]
257 tst r0, r1
258 beq getc_continue
259 ldr r1, [r6, #CONSOLE_T_GETC]
260 cmp r1, #0
261 beq getc_continue
262 mov r0, r6
263 blx r1
264 cmp r0, #0 /* if R0 >= 0: return */
265 bge getc_found
266 cmp r5, #ERROR_NO_PENDING_CHAR /* may update R5 (NOCHAR has */
267 movne r5, r0 /* precedence vs real errors) */
268getc_continue:
269 ldr r6, [r6] /* R6 = next struct */
270 cmp r6, #0
271 bne getc_loop
272 cmp r5, #ERROR_NO_PENDING_CHAR /* Keep scanning if at least */
273 beq getc_try_again /* one console returns NOCHAR */
274 mov r0, r5
275
276getc_found:
277 pop {r5-r6, pc}
278endfunc console_getc
279
280 /* ---------------------------------------------
281 * int console_flush(void)
282 * Function to force a write of all buffered
283 * data that hasn't been output. Calls all
284 * console's flush() handlers in succession.
285 * Out: r0 - 0 on success, < 0 if at least one error
286 * Clobber list : r0, r1, r2
287 * ---------------------------------------------
288 */
289func console_flush
290 push {r5-r6, lr}
291 mov r5, #ERROR_NO_VALID_CONSOLE /* R5 = current return value */
292 ldr r6, =console_list
293 ldr r6, [r6] /* R6 = first console struct */
294
295flush_loop:
296 cmp r6, #0
297 beq flush_done
298 ldr r1, =console_state
299 ldrb r1, [r1]
300 ldr r2, [r6, #CONSOLE_T_FLAGS]
301 tst r1, r2
302 beq flush_continue
303 ldr r1, [r6, #CONSOLE_T_FLUSH]
304 cmp r1, #0
305 beq flush_continue
306 mov r0, r6
307 blx r1
308 cmp r5, #ERROR_NO_VALID_CONSOLE /* update R5 if it's NOVALID */
309 cmpne r0, #0 /* else update it if R0 < 0 */
310 movlt r5, r0
311flush_continue:
312 ldr r6, [r6] /* R6 = next struct */
313 b flush_loop
314
315flush_done:
316 mov r0, r5
317 pop {r5-r6, pc}
318endfunc console_flush