1/*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <lib/libc/errno.h>
8
9#include <asm_macros.S>
10#include <console_macros.S>
11#include <lib/utils_def.h>
12
13#define LDIV_MULTIPLIER		U(16)
14
15#define LINFLEX_LINCR1		(0x0)
16#define LINCR1_INIT		BIT_32(0)
17#define LINCR1_MME		BIT_32(4)
18
19#define LINFLEX_LINSR		(0x8)
20#define LINSR_LINS_INITMODE	(0x00001000)
21#define LINSR_LINS_MASK		(0x0000F000)
22
23#define LINFLEX_UARTCR		(0x10)
24#define UARTCR_ROSE		BIT_32(23)
25
26#define LINFLEX_UARTSR		(0x14)
27#define LINFLEX_LINIBRR		(0x28)
28#define LINFLEX_LINFBRR		(0x24)
29#define LINFLEX_BDRL		(0x38)
30#define LINFLEX_UARTPTO		(0x50)
31
32#define UARTCR_UART		BIT_32(0)
33#define UARTCR_WL0		BIT_32(1)
34#define UARTCR_PC0		BIT_32(3)
35#define UARTCR_TXEN		BIT_32(4)
36#define UARTCR_RXEN		BIT_32(5)
37#define UARTCR_PC1		BIT_32(6)
38#define UARTCR_TFBM		BIT_32(8)
39#define UARTCR_RFBM		BIT_32(9)
40#define UARTCR_OSR_SHIFT	U(24)
41#define UARTCR_OSR_WIDTH	U(4)
42
43#define UARTSR_DTF		BIT_32(1)
44
45/*
46 * "core" functions are low-level implementations that do not require
47 * writable memory and are thus safe to call in BL1 crash context.
48 */
49.globl console_linflex_core_init
50.globl console_linflex_core_putc
51
52.globl console_linflex_register
53.globl console_linflex_putc
54
55/**
56 * uint32_t get_ldiv_mult(uintptr_t baseaddr, uint32_t clock,
57 *                        uint32_t baud, console_t *console,);
58 *
59 * Clobber list : x0 - x6
60 * Out x4: LDIV multiplier
61 */
62func get_ldiv_mult
63	ldr	w4, [x0, LINFLEX_UARTCR]
64	mov	w5, w4
65
66	/* Prepare choices in w5 and w6 */
67	ubfx	x5, x5, #UARTCR_OSR_SHIFT, #UARTCR_OSR_WIDTH
68	mov	w6, #LDIV_MULTIPLIER
69
70	and	w4, w4, #UARTCR_ROSE
71	cmp	w4, #0x0
72	csel	w4, w5, w6, ne
73	ret
74endfunc get_ldiv_mult
75
76/*
77 * void linflex_set_brg(uintptr_t baseaddr, uint32_t clock
78 *                      uint32_t baud, console_t *console);
79 *
80 * Clobber list : x0 - x7, x13
81 */
82func linflex_set_brg
83	mov	x13, x30
84	bl	get_ldiv_mult
85	mov	x30, x13
86
87	/* (x4) dividr = baudrate * ldiv_mult */
88	mul	x4, x4, x2
89	/* (x5) divisr = clock rate */
90	mov	x5, x1
91	/* (x6) ibr = divisr / dividr */
92	udiv	x6, x5, x4
93	/* (x7) fbr = divisr % dividr */
94	msub	x7, x6, x4, x5
95	/* fbr *= 16 / dividr */
96	lsl	x7, x7, #4
97	udiv	x7, x7, x4
98	/* fbr &= 0xf */
99	and	w7, w7, #0xf
100	str	w6, [x0, LINFLEX_LINIBRR]
101	str	w7, [x0, LINFLEX_LINFBRR]
102	ret
103endfunc linflex_set_brg
104
105/**
106 * int console_linflex_core_init(uintptr_t baseaddr, uint32_t clock,
107 *                               uint32_t baud);
108 *
109 * In:  x0 - Linflex base address
110 *      x1 - clock frequency
111 *      x2 - baudrate
112 * Out: x0 - 1 on success, 0 on error
113 * Clobber list : x0 - x7, x13 - x14
114 */
115func console_linflex_core_init
116	/* Set master mode and init mode */
117	mov	w4, #(LINCR1_INIT)
118	str	w4, [x0, LINFLEX_LINCR1]
119	mov	w4, #(LINCR1_MME | LINCR1_INIT)
120	str	w4, [x0, LINFLEX_LINCR1]
121
122	/* wait for init mode entry */
123wait_init_entry:
124	ldr	w4, [x0, LINFLEX_LINSR]
125	and	w4, w4, #LINSR_LINS_MASK
126	cmp	w4, #LINSR_LINS_INITMODE
127	b.ne	wait_init_entry
128
129	/* Set UART bit */
130	mov	w4, #UARTCR_UART
131	str	w4, [x0, LINFLEX_UARTCR]
132
133	mov	x14, x30
134	bl	linflex_set_brg
135	mov	x30, x14
136
137	/* Set preset timeout register value. */
138	mov	w4, #0xf
139	str	w4, [x0, LINFLEX_UARTPTO]
140
141	/* 8-bit data, no parity, Tx/Rx enabled, UART mode */
142	mov	w4, #(UARTCR_PC1 | UARTCR_RXEN | UARTCR_TXEN | UARTCR_PC0 | \
143		      UARTCR_WL0 | UARTCR_UART | UARTCR_RFBM | UARTCR_TFBM)
144	str	w4, [x0, LINFLEX_UARTCR]
145
146	/* End init mode */
147	ldr	w4, [x0, LINFLEX_LINCR1]
148	bic	w4, w4, #LINCR1_INIT
149	str	w4, [x0, LINFLEX_LINCR1]
150	ret
151endfunc console_linflex_core_init
152
153/**
154 * int console_linflex_register(uintptr_t baseaddr, uint32_t clock,
155 *                              uint32_t clock, uint32_t baud);
156 *
157 * Function to initialize and register the console.
158 * The caller needs to pass an empty console_linflex_t
159 * structure in which *MUST* be allocated in
160 * persistent memory (e.g. a global or static local
161 * variable, *NOT* on the stack).
162 * In:  x0 - Linflex base address
163 *      x1 - clock frequency
164 *      x2 - baudrate
165 *      x3 - pointer to empty console_t structure
166 * Out: x0 - 1 on success, 0 on error
167 * Clobber list : x0 - x7, x13 - x15
168 */
169func console_linflex_register
170	mov	x15, x30
171	bl	console_linflex_core_init
172	mov	x30, x15
173
174	/* Populate the base address */
175	str	x0, [x3, #CONSOLE_T_BASE]
176
177	mov	x0, x3
178	finish_console_register linflex, putc=1, getc=0, flush=0
179endfunc console_linflex_register
180
181/**
182 * int console_linflex_core_putc(int c, uintptr_t baseaddr);
183
184 * Out: w0 - printed character on success, < 0 on error.
185 * Clobber list : x0 - x3
186 */
187func console_linflex_core_putc
188	cbz	x1, putc_error
189
190	cmp	w0, #'\n'
191	b.ne	print_char
192
193	/* Print '\r\n' for each '\n' */
194	mov	x0, #'\r'
195	mov	x14, x30
196	bl	console_linflex_core_putc
197	mov	x30, x14
198	mov	x0, #'\n'
199
200print_char:
201	ldr	w2, [x1, LINFLEX_UARTCR]
202	and	w2, w2, #UARTCR_TFBM
203	cmp	w2, #0x0
204	b.eq	buffer_mode
205
206fifo_mode:
207	/* UART is in FIFO mode */
208	ldr	w2, [x1, LINFLEX_UARTSR]
209	and	w2, w2, #UARTSR_DTF
210	cmp	w2, #0
211	b.ne	fifo_mode
212
213	strb	w0, [x1, LINFLEX_BDRL]
214	b	no_error
215
216buffer_mode:
217	strb	w0, [x1, LINFLEX_BDRL]
218
219buffer_loop:
220	ldr	w2, [x1, LINFLEX_UARTSR]
221	and	w3, w2, #UARTSR_DTF
222	cmp	w3, #0
223	b.eq	buffer_loop
224
225	/**
226	 * In Buffer Mode the DTFTFF bit of UARTSR register
227	 * has to be set in software
228	 */
229	mov	w2, #UARTSR_DTF
230	str	w2, [x1, LINFLEX_UARTSR]
231
232no_error:
233	mov	x0, #0
234	ret
235
236putc_error:
237	mov	x0, #-EINVAL
238	ret
239endfunc console_linflex_core_putc
240
241/**
242 * int console_linflex_putc(int c, console_t *console);
243 *
244 * Function to output a character over the console. It
245 * returns the character printed on success or -EINVAL on error.
246 * In : w0 - character to be printed
247 *      x1 - pointer to console_t struct
248 * Out: w0 - printed character on success, < 0 on error.
249 * Clobber list : x0 - x3, x15
250 */
251func console_linflex_putc
252	cbz	x1, putc_error
253	ldr	x1, [x1, #CONSOLE_T_BASE]
254
255	b	console_linflex_core_putc
256puct_error:
257	mov	x0, #-EINVAL
258	ret
259endfunc console_linflex_putc
260