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