xref: /rk3399_ARM-atf/drivers/nxp/console/linflex_console.S (revision 95ac568b6137ee8d3a53d3ec911a7116c90e8d5d)
1306946b0SGhennadi Procopciuc/*
2306946b0SGhennadi Procopciuc * Copyright 2024 NXP
3306946b0SGhennadi Procopciuc *
4306946b0SGhennadi Procopciuc * SPDX-License-Identifier: BSD-3-Clause
5306946b0SGhennadi Procopciuc */
6306946b0SGhennadi Procopciuc
7306946b0SGhennadi Procopciuc#include <lib/libc/errno.h>
8306946b0SGhennadi Procopciuc
9306946b0SGhennadi Procopciuc#include <asm_macros.S>
10306946b0SGhennadi Procopciuc#include <console_macros.S>
11306946b0SGhennadi Procopciuc#include <lib/utils_def.h>
12306946b0SGhennadi Procopciuc
13306946b0SGhennadi Procopciuc#define LDIV_MULTIPLIER		U(16)
14306946b0SGhennadi Procopciuc
15306946b0SGhennadi Procopciuc#define LINFLEX_LINCR1		(0x0)
16306946b0SGhennadi Procopciuc#define LINCR1_INIT		BIT_32(0)
17306946b0SGhennadi Procopciuc#define LINCR1_MME		BIT_32(4)
18306946b0SGhennadi Procopciuc
19306946b0SGhennadi Procopciuc#define LINFLEX_LINSR		(0x8)
20306946b0SGhennadi Procopciuc#define LINSR_LINS_INITMODE	(0x00001000)
21*95ac568bSGhennadi Procopciuc#define LINSR_LINS_RX_TX_MODE	(0x00008000)
22306946b0SGhennadi Procopciuc#define LINSR_LINS_MASK		(0x0000F000)
23306946b0SGhennadi Procopciuc
24306946b0SGhennadi Procopciuc#define LINFLEX_UARTCR		(0x10)
25306946b0SGhennadi Procopciuc#define UARTCR_ROSE		BIT_32(23)
26306946b0SGhennadi Procopciuc
27306946b0SGhennadi Procopciuc#define LINFLEX_UARTSR		(0x14)
28306946b0SGhennadi Procopciuc#define LINFLEX_LINIBRR		(0x28)
29306946b0SGhennadi Procopciuc#define LINFLEX_LINFBRR		(0x24)
30306946b0SGhennadi Procopciuc#define LINFLEX_BDRL		(0x38)
31306946b0SGhennadi Procopciuc#define LINFLEX_UARTPTO		(0x50)
32306946b0SGhennadi Procopciuc
33306946b0SGhennadi Procopciuc#define UARTCR_UART		BIT_32(0)
34306946b0SGhennadi Procopciuc#define UARTCR_WL0		BIT_32(1)
35306946b0SGhennadi Procopciuc#define UARTCR_PC0		BIT_32(3)
36306946b0SGhennadi Procopciuc#define UARTCR_TXEN		BIT_32(4)
37306946b0SGhennadi Procopciuc#define UARTCR_RXEN		BIT_32(5)
38306946b0SGhennadi Procopciuc#define UARTCR_PC1		BIT_32(6)
39306946b0SGhennadi Procopciuc#define UARTCR_TFBM		BIT_32(8)
40306946b0SGhennadi Procopciuc#define UARTCR_RFBM		BIT_32(9)
41306946b0SGhennadi Procopciuc#define UARTCR_OSR_SHIFT	U(24)
42306946b0SGhennadi Procopciuc#define UARTCR_OSR_WIDTH	U(4)
43306946b0SGhennadi Procopciuc
44306946b0SGhennadi Procopciuc#define UARTSR_DTF		BIT_32(1)
45306946b0SGhennadi Procopciuc
46306946b0SGhennadi Procopciuc/*
47306946b0SGhennadi Procopciuc * "core" functions are low-level implementations that do not require
48306946b0SGhennadi Procopciuc * writable memory and are thus safe to call in BL1 crash context.
49306946b0SGhennadi Procopciuc */
50306946b0SGhennadi Procopciuc.globl console_linflex_core_init
51306946b0SGhennadi Procopciuc.globl console_linflex_core_putc
52*95ac568bSGhennadi Procopciuc.globl console_linflex_core_flush
53306946b0SGhennadi Procopciuc
54306946b0SGhennadi Procopciuc.globl console_linflex_register
55306946b0SGhennadi Procopciuc.globl console_linflex_putc
56*95ac568bSGhennadi Procopciuc.globl console_linflex_flush
57306946b0SGhennadi Procopciuc
58306946b0SGhennadi Procopciuc/**
59306946b0SGhennadi Procopciuc * uint32_t get_ldiv_mult(uintptr_t baseaddr, uint32_t clock,
60306946b0SGhennadi Procopciuc *                        uint32_t baud, console_t *console,);
61306946b0SGhennadi Procopciuc *
62306946b0SGhennadi Procopciuc * Clobber list : x0 - x6
63306946b0SGhennadi Procopciuc * Out x4: LDIV multiplier
64306946b0SGhennadi Procopciuc */
65306946b0SGhennadi Procopciucfunc get_ldiv_mult
66306946b0SGhennadi Procopciuc	ldr	w4, [x0, LINFLEX_UARTCR]
67306946b0SGhennadi Procopciuc	mov	w5, w4
68306946b0SGhennadi Procopciuc
69306946b0SGhennadi Procopciuc	/* Prepare choices in w5 and w6 */
70306946b0SGhennadi Procopciuc	ubfx	x5, x5, #UARTCR_OSR_SHIFT, #UARTCR_OSR_WIDTH
71306946b0SGhennadi Procopciuc	mov	w6, #LDIV_MULTIPLIER
72306946b0SGhennadi Procopciuc
73306946b0SGhennadi Procopciuc	and	w4, w4, #UARTCR_ROSE
74306946b0SGhennadi Procopciuc	cmp	w4, #0x0
75306946b0SGhennadi Procopciuc	csel	w4, w5, w6, ne
76306946b0SGhennadi Procopciuc	ret
77306946b0SGhennadi Procopciucendfunc get_ldiv_mult
78306946b0SGhennadi Procopciuc
79306946b0SGhennadi Procopciuc/*
80306946b0SGhennadi Procopciuc * void linflex_set_brg(uintptr_t baseaddr, uint32_t clock
81306946b0SGhennadi Procopciuc *                      uint32_t baud, console_t *console);
82306946b0SGhennadi Procopciuc *
83306946b0SGhennadi Procopciuc * Clobber list : x0 - x7, x13
84306946b0SGhennadi Procopciuc */
85306946b0SGhennadi Procopciucfunc linflex_set_brg
86306946b0SGhennadi Procopciuc	mov	x13, x30
87306946b0SGhennadi Procopciuc	bl	get_ldiv_mult
88306946b0SGhennadi Procopciuc	mov	x30, x13
89306946b0SGhennadi Procopciuc
90306946b0SGhennadi Procopciuc	/* (x4) dividr = baudrate * ldiv_mult */
91306946b0SGhennadi Procopciuc	mul	x4, x4, x2
92306946b0SGhennadi Procopciuc	/* (x5) divisr = clock rate */
93306946b0SGhennadi Procopciuc	mov	x5, x1
94306946b0SGhennadi Procopciuc	/* (x6) ibr = divisr / dividr */
95306946b0SGhennadi Procopciuc	udiv	x6, x5, x4
96306946b0SGhennadi Procopciuc	/* (x7) fbr = divisr % dividr */
97306946b0SGhennadi Procopciuc	msub	x7, x6, x4, x5
98306946b0SGhennadi Procopciuc	/* fbr *= 16 / dividr */
99306946b0SGhennadi Procopciuc	lsl	x7, x7, #4
100306946b0SGhennadi Procopciuc	udiv	x7, x7, x4
101306946b0SGhennadi Procopciuc	/* fbr &= 0xf */
102306946b0SGhennadi Procopciuc	and	w7, w7, #0xf
103306946b0SGhennadi Procopciuc	str	w6, [x0, LINFLEX_LINIBRR]
104306946b0SGhennadi Procopciuc	str	w7, [x0, LINFLEX_LINFBRR]
105306946b0SGhennadi Procopciuc	ret
106306946b0SGhennadi Procopciucendfunc linflex_set_brg
107306946b0SGhennadi Procopciuc
108306946b0SGhennadi Procopciuc/**
109306946b0SGhennadi Procopciuc * int console_linflex_core_init(uintptr_t baseaddr, uint32_t clock,
110306946b0SGhennadi Procopciuc *                               uint32_t baud);
111306946b0SGhennadi Procopciuc *
112306946b0SGhennadi Procopciuc * In:  x0 - Linflex base address
113306946b0SGhennadi Procopciuc *      x1 - clock frequency
114306946b0SGhennadi Procopciuc *      x2 - baudrate
115306946b0SGhennadi Procopciuc * Out: x0 - 1 on success, 0 on error
116306946b0SGhennadi Procopciuc * Clobber list : x0 - x7, x13 - x14
117306946b0SGhennadi Procopciuc */
118306946b0SGhennadi Procopciucfunc console_linflex_core_init
119306946b0SGhennadi Procopciuc	/* Set master mode and init mode */
120306946b0SGhennadi Procopciuc	mov	w4, #(LINCR1_INIT)
121306946b0SGhennadi Procopciuc	str	w4, [x0, LINFLEX_LINCR1]
122306946b0SGhennadi Procopciuc	mov	w4, #(LINCR1_MME | LINCR1_INIT)
123306946b0SGhennadi Procopciuc	str	w4, [x0, LINFLEX_LINCR1]
124306946b0SGhennadi Procopciuc
125306946b0SGhennadi Procopciuc	/* wait for init mode entry */
126306946b0SGhennadi Procopciucwait_init_entry:
127306946b0SGhennadi Procopciuc	ldr	w4, [x0, LINFLEX_LINSR]
128306946b0SGhennadi Procopciuc	and	w4, w4, #LINSR_LINS_MASK
129306946b0SGhennadi Procopciuc	cmp	w4, #LINSR_LINS_INITMODE
130306946b0SGhennadi Procopciuc	b.ne	wait_init_entry
131306946b0SGhennadi Procopciuc
132306946b0SGhennadi Procopciuc	/* Set UART bit */
133306946b0SGhennadi Procopciuc	mov	w4, #UARTCR_UART
134306946b0SGhennadi Procopciuc	str	w4, [x0, LINFLEX_UARTCR]
135306946b0SGhennadi Procopciuc
136306946b0SGhennadi Procopciuc	mov	x14, x30
137306946b0SGhennadi Procopciuc	bl	linflex_set_brg
138306946b0SGhennadi Procopciuc	mov	x30, x14
139306946b0SGhennadi Procopciuc
140306946b0SGhennadi Procopciuc	/* Set preset timeout register value. */
141306946b0SGhennadi Procopciuc	mov	w4, #0xf
142306946b0SGhennadi Procopciuc	str	w4, [x0, LINFLEX_UARTPTO]
143306946b0SGhennadi Procopciuc
144306946b0SGhennadi Procopciuc	/* 8-bit data, no parity, Tx/Rx enabled, UART mode */
145306946b0SGhennadi Procopciuc	mov	w4, #(UARTCR_PC1 | UARTCR_RXEN | UARTCR_TXEN | UARTCR_PC0 | \
146306946b0SGhennadi Procopciuc		      UARTCR_WL0 | UARTCR_UART | UARTCR_RFBM | UARTCR_TFBM)
147306946b0SGhennadi Procopciuc	str	w4, [x0, LINFLEX_UARTCR]
148306946b0SGhennadi Procopciuc
149306946b0SGhennadi Procopciuc	/* End init mode */
150306946b0SGhennadi Procopciuc	ldr	w4, [x0, LINFLEX_LINCR1]
151306946b0SGhennadi Procopciuc	bic	w4, w4, #LINCR1_INIT
152306946b0SGhennadi Procopciuc	str	w4, [x0, LINFLEX_LINCR1]
153306946b0SGhennadi Procopciuc	ret
154306946b0SGhennadi Procopciucendfunc console_linflex_core_init
155306946b0SGhennadi Procopciuc
156306946b0SGhennadi Procopciuc/**
157306946b0SGhennadi Procopciuc * int console_linflex_register(uintptr_t baseaddr, uint32_t clock,
158306946b0SGhennadi Procopciuc *                              uint32_t clock, uint32_t baud);
159306946b0SGhennadi Procopciuc *
160306946b0SGhennadi Procopciuc * Function to initialize and register the console.
161306946b0SGhennadi Procopciuc * The caller needs to pass an empty console_linflex_t
162306946b0SGhennadi Procopciuc * structure in which *MUST* be allocated in
163306946b0SGhennadi Procopciuc * persistent memory (e.g. a global or static local
164306946b0SGhennadi Procopciuc * variable, *NOT* on the stack).
165306946b0SGhennadi Procopciuc * In:  x0 - Linflex base address
166306946b0SGhennadi Procopciuc *      x1 - clock frequency
167306946b0SGhennadi Procopciuc *      x2 - baudrate
168306946b0SGhennadi Procopciuc *      x3 - pointer to empty console_t structure
169306946b0SGhennadi Procopciuc * Out: x0 - 1 on success, 0 on error
170306946b0SGhennadi Procopciuc * Clobber list : x0 - x7, x13 - x15
171306946b0SGhennadi Procopciuc */
172306946b0SGhennadi Procopciucfunc console_linflex_register
173306946b0SGhennadi Procopciuc	mov	x15, x30
174306946b0SGhennadi Procopciuc	bl	console_linflex_core_init
175306946b0SGhennadi Procopciuc	mov	x30, x15
176306946b0SGhennadi Procopciuc
177306946b0SGhennadi Procopciuc	/* Populate the base address */
178306946b0SGhennadi Procopciuc	str	x0, [x3, #CONSOLE_T_BASE]
179306946b0SGhennadi Procopciuc
180306946b0SGhennadi Procopciuc	mov	x0, x3
181*95ac568bSGhennadi Procopciuc	finish_console_register linflex, putc=1, getc=0, flush=1
182306946b0SGhennadi Procopciucendfunc console_linflex_register
183306946b0SGhennadi Procopciuc
184306946b0SGhennadi Procopciuc/**
185*95ac568bSGhennadi Procopciuc * int console_linflex_core_flush(uintptr_t baseaddr);
186*95ac568bSGhennadi Procopciuc *
187*95ac568bSGhennadi Procopciuc * Loop while the TX fifo is not empty, depending on the selected UART mode.
188*95ac568bSGhennadi Procopciuc *
189*95ac568bSGhennadi Procopciuc * In:  x0 - Linflex base address
190*95ac568bSGhennadi Procopciuc * Clobber list : x0 - x1
191*95ac568bSGhennadi Procopciuc */
192*95ac568bSGhennadi Procopciucfunc console_linflex_core_flush
193*95ac568bSGhennadi Procopciucwait_rx_tx:
194*95ac568bSGhennadi Procopciuc	ldr	w1, [x0, LINFLEX_LINSR]
195*95ac568bSGhennadi Procopciuc	and	w1, w1, #LINSR_LINS_MASK
196*95ac568bSGhennadi Procopciuc	cmp	w1, #LINSR_LINS_RX_TX_MODE
197*95ac568bSGhennadi Procopciuc	b.eq	wait_rx_tx
198*95ac568bSGhennadi Procopciuc
199*95ac568bSGhennadi Procopciuc	mov	x0, #0
200*95ac568bSGhennadi Procopciuc	ret
201*95ac568bSGhennadi Procopciucendfunc console_linflex_core_flush
202*95ac568bSGhennadi Procopciuc
203*95ac568bSGhennadi Procopciuc/**
204306946b0SGhennadi Procopciuc * int console_linflex_core_putc(int c, uintptr_t baseaddr);
205306946b0SGhennadi Procopciuc
206306946b0SGhennadi Procopciuc * Out: w0 - printed character on success, < 0 on error.
207306946b0SGhennadi Procopciuc * Clobber list : x0 - x3
208306946b0SGhennadi Procopciuc */
209306946b0SGhennadi Procopciucfunc console_linflex_core_putc
210306946b0SGhennadi Procopciuc	cbz	x1, putc_error
211306946b0SGhennadi Procopciuc
212306946b0SGhennadi Procopciuc	cmp	w0, #'\n'
213306946b0SGhennadi Procopciuc	b.ne	print_char
214306946b0SGhennadi Procopciuc
215306946b0SGhennadi Procopciuc	/* Print '\r\n' for each '\n' */
216306946b0SGhennadi Procopciuc	mov	x0, #'\r'
217306946b0SGhennadi Procopciuc	mov	x14, x30
218306946b0SGhennadi Procopciuc	bl	console_linflex_core_putc
219306946b0SGhennadi Procopciuc	mov	x30, x14
220306946b0SGhennadi Procopciuc	mov	x0, #'\n'
221306946b0SGhennadi Procopciuc
222306946b0SGhennadi Procopciucprint_char:
223306946b0SGhennadi Procopciuc	ldr	w2, [x1, LINFLEX_UARTCR]
224306946b0SGhennadi Procopciuc	and	w2, w2, #UARTCR_TFBM
225306946b0SGhennadi Procopciuc	cmp	w2, #0x0
226306946b0SGhennadi Procopciuc	b.eq	buffer_mode
227306946b0SGhennadi Procopciuc
228306946b0SGhennadi Procopciucfifo_mode:
229306946b0SGhennadi Procopciuc	/* UART is in FIFO mode */
230306946b0SGhennadi Procopciuc	ldr	w2, [x1, LINFLEX_UARTSR]
231306946b0SGhennadi Procopciuc	and	w2, w2, #UARTSR_DTF
232306946b0SGhennadi Procopciuc	cmp	w2, #0
233306946b0SGhennadi Procopciuc	b.ne	fifo_mode
234306946b0SGhennadi Procopciuc
235306946b0SGhennadi Procopciuc	strb	w0, [x1, LINFLEX_BDRL]
236306946b0SGhennadi Procopciuc	b	no_error
237306946b0SGhennadi Procopciuc
238306946b0SGhennadi Procopciucbuffer_mode:
239306946b0SGhennadi Procopciuc	strb	w0, [x1, LINFLEX_BDRL]
240306946b0SGhennadi Procopciuc
241306946b0SGhennadi Procopciucbuffer_loop:
242306946b0SGhennadi Procopciuc	ldr	w2, [x1, LINFLEX_UARTSR]
243306946b0SGhennadi Procopciuc	and	w3, w2, #UARTSR_DTF
244306946b0SGhennadi Procopciuc	cmp	w3, #0
245306946b0SGhennadi Procopciuc	b.eq	buffer_loop
246306946b0SGhennadi Procopciuc
247306946b0SGhennadi Procopciuc	/**
248306946b0SGhennadi Procopciuc	 * In Buffer Mode the DTFTFF bit of UARTSR register
249306946b0SGhennadi Procopciuc	 * has to be set in software
250306946b0SGhennadi Procopciuc	 */
251306946b0SGhennadi Procopciuc	mov	w2, #UARTSR_DTF
252306946b0SGhennadi Procopciuc	str	w2, [x1, LINFLEX_UARTSR]
253306946b0SGhennadi Procopciuc
254306946b0SGhennadi Procopciucno_error:
255306946b0SGhennadi Procopciuc	mov	x0, #0
256306946b0SGhennadi Procopciuc	ret
257306946b0SGhennadi Procopciuc
258306946b0SGhennadi Procopciucputc_error:
259306946b0SGhennadi Procopciuc	mov	x0, #-EINVAL
260306946b0SGhennadi Procopciuc	ret
261306946b0SGhennadi Procopciucendfunc console_linflex_core_putc
262306946b0SGhennadi Procopciuc
263306946b0SGhennadi Procopciuc/**
264306946b0SGhennadi Procopciuc * int console_linflex_putc(int c, console_t *console);
265306946b0SGhennadi Procopciuc *
266306946b0SGhennadi Procopciuc * Function to output a character over the console. It
267306946b0SGhennadi Procopciuc * returns the character printed on success or -EINVAL on error.
268306946b0SGhennadi Procopciuc * In : w0 - character to be printed
269306946b0SGhennadi Procopciuc *      x1 - pointer to console_t struct
270306946b0SGhennadi Procopciuc * Out: w0 - printed character on success, < 0 on error.
271306946b0SGhennadi Procopciuc * Clobber list : x0 - x3, x15
272306946b0SGhennadi Procopciuc */
273306946b0SGhennadi Procopciucfunc console_linflex_putc
274306946b0SGhennadi Procopciuc	cbz	x1, putc_error
275306946b0SGhennadi Procopciuc	ldr	x1, [x1, #CONSOLE_T_BASE]
276306946b0SGhennadi Procopciuc
277306946b0SGhennadi Procopciuc	b	console_linflex_core_putc
278306946b0SGhennadi Procopciucpuct_error:
279306946b0SGhennadi Procopciuc	mov	x0, #-EINVAL
280306946b0SGhennadi Procopciuc	ret
281306946b0SGhennadi Procopciucendfunc console_linflex_putc
282*95ac568bSGhennadi Procopciuc
283*95ac568bSGhennadi Procopciuc/**
284*95ac568bSGhennadi Procopciuc * int console_linflex_flush(console_t *console);
285*95ac568bSGhennadi Procopciuc *
286*95ac568bSGhennadi Procopciuc * Function to wait for the TX FIFO to be cleared.
287*95ac568bSGhennadi Procopciuc * In : x0 - pointer to console_t struct
288*95ac568bSGhennadi Procopciuc * Out: x0 - return -1 on error else return 0.
289*95ac568bSGhennadi Procopciuc * Clobber list : x0 - x1
290*95ac568bSGhennadi Procopciuc */
291*95ac568bSGhennadi Procopciucfunc console_linflex_flush
292*95ac568bSGhennadi Procopciuc	cbz	x0, flush_error
293*95ac568bSGhennadi Procopciuc	ldr	x0, [x0, #CONSOLE_T_BASE]
294*95ac568bSGhennadi Procopciuc
295*95ac568bSGhennadi Procopciuc	b	console_linflex_core_flush
296*95ac568bSGhennadi Procopciucflush_error:
297*95ac568bSGhennadi Procopciuc	mov	x0, #-EINVAL
298*95ac568bSGhennadi Procopciuc	ret
299*95ac568bSGhennadi Procopciucendfunc console_linflex_flush
300