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