feat(nxp-drivers): add Linflex driver
This is a UART controller found on NXP automotive parts.
For instance: S32V, S32G and S32R.
Change-Id: Iff0dd0c379633ac0651e5db287537c87666b57d2
Signed-off-by: Bogdan Hamciuc <bogdan.hamciuc@nxp.com>
Signed-off-by: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
diff --git a/drivers/nxp/console/console.mk b/drivers/nxp/console/console.mk
index 6174650..5f3c6e3 100644
--- a/drivers/nxp/console/console.mk
+++ b/drivers/nxp/console/console.mk
@@ -1,5 +1,5 @@
#
-# Copyright 2021 NXP
+# Copyright 2021-2024 NXP
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -28,8 +28,13 @@
CONSOLE_SOURCES := drivers/arm/pl011/aarch64/pl011_console.S \
${PLAT_DRIVERS_PATH}/console/console_pl011.c
else
+ifeq ($(CONSOLE), LINFLEX)
+CONSOLE_SOURCES := ${PLAT_DRIVERS_PATH}/console/linflex_console.S
+else
$(error -> CONSOLE not set!)
endif
+
+endif
endif
ifeq (${BL_COMM_CONSOLE_NEEDED},yes)
diff --git a/drivers/nxp/console/linflex_console.S b/drivers/nxp/console/linflex_console.S
new file mode 100644
index 0000000..abcbb59
--- /dev/null
+++ b/drivers/nxp/console/linflex_console.S
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2024 NXP
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <lib/libc/errno.h>
+
+#include <asm_macros.S>
+#include <console_macros.S>
+#include <lib/utils_def.h>
+
+#define LDIV_MULTIPLIER U(16)
+
+#define LINFLEX_LINCR1 (0x0)
+#define LINCR1_INIT BIT_32(0)
+#define LINCR1_MME BIT_32(4)
+
+#define LINFLEX_LINSR (0x8)
+#define LINSR_LINS_INITMODE (0x00001000)
+#define LINSR_LINS_MASK (0x0000F000)
+
+#define LINFLEX_UARTCR (0x10)
+#define UARTCR_ROSE BIT_32(23)
+
+#define LINFLEX_UARTSR (0x14)
+#define LINFLEX_LINIBRR (0x28)
+#define LINFLEX_LINFBRR (0x24)
+#define LINFLEX_BDRL (0x38)
+#define LINFLEX_UARTPTO (0x50)
+
+#define UARTCR_UART BIT_32(0)
+#define UARTCR_WL0 BIT_32(1)
+#define UARTCR_PC0 BIT_32(3)
+#define UARTCR_TXEN BIT_32(4)
+#define UARTCR_RXEN BIT_32(5)
+#define UARTCR_PC1 BIT_32(6)
+#define UARTCR_TFBM BIT_32(8)
+#define UARTCR_RFBM BIT_32(9)
+#define UARTCR_OSR_SHIFT U(24)
+#define UARTCR_OSR_WIDTH U(4)
+
+#define UARTSR_DTF BIT_32(1)
+
+/*
+ * "core" functions are low-level implementations that do not require
+ * writable memory and are thus safe to call in BL1 crash context.
+ */
+.globl console_linflex_core_init
+.globl console_linflex_core_putc
+
+.globl console_linflex_register
+.globl console_linflex_putc
+
+/**
+ * uint32_t get_ldiv_mult(uintptr_t baseaddr, uint32_t clock,
+ * uint32_t baud, console_t *console,);
+ *
+ * Clobber list : x0 - x6
+ * Out x4: LDIV multiplier
+ */
+func get_ldiv_mult
+ ldr w4, [x0, LINFLEX_UARTCR]
+ mov w5, w4
+
+ /* Prepare choices in w5 and w6 */
+ ubfx x5, x5, #UARTCR_OSR_SHIFT, #UARTCR_OSR_WIDTH
+ mov w6, #LDIV_MULTIPLIER
+
+ and w4, w4, #UARTCR_ROSE
+ cmp w4, #0x0
+ csel w4, w5, w6, ne
+ ret
+endfunc get_ldiv_mult
+
+/*
+ * void linflex_set_brg(uintptr_t baseaddr, uint32_t clock
+ * uint32_t baud, console_t *console);
+ *
+ * Clobber list : x0 - x7, x13
+ */
+func linflex_set_brg
+ mov x13, x30
+ bl get_ldiv_mult
+ mov x30, x13
+
+ /* (x4) dividr = baudrate * ldiv_mult */
+ mul x4, x4, x2
+ /* (x5) divisr = clock rate */
+ mov x5, x1
+ /* (x6) ibr = divisr / dividr */
+ udiv x6, x5, x4
+ /* (x7) fbr = divisr % dividr */
+ msub x7, x6, x4, x5
+ /* fbr *= 16 / dividr */
+ lsl x7, x7, #4
+ udiv x7, x7, x4
+ /* fbr &= 0xf */
+ and w7, w7, #0xf
+ str w6, [x0, LINFLEX_LINIBRR]
+ str w7, [x0, LINFLEX_LINFBRR]
+ ret
+endfunc linflex_set_brg
+
+/**
+ * int console_linflex_core_init(uintptr_t baseaddr, uint32_t clock,
+ * uint32_t baud);
+ *
+ * In: x0 - Linflex base address
+ * x1 - clock frequency
+ * x2 - baudrate
+ * Out: x0 - 1 on success, 0 on error
+ * Clobber list : x0 - x7, x13 - x14
+ */
+func console_linflex_core_init
+ /* Set master mode and init mode */
+ mov w4, #(LINCR1_INIT)
+ str w4, [x0, LINFLEX_LINCR1]
+ mov w4, #(LINCR1_MME | LINCR1_INIT)
+ str w4, [x0, LINFLEX_LINCR1]
+
+ /* wait for init mode entry */
+wait_init_entry:
+ ldr w4, [x0, LINFLEX_LINSR]
+ and w4, w4, #LINSR_LINS_MASK
+ cmp w4, #LINSR_LINS_INITMODE
+ b.ne wait_init_entry
+
+ /* Set UART bit */
+ mov w4, #UARTCR_UART
+ str w4, [x0, LINFLEX_UARTCR]
+
+ mov x14, x30
+ bl linflex_set_brg
+ mov x30, x14
+
+ /* Set preset timeout register value. */
+ mov w4, #0xf
+ str w4, [x0, LINFLEX_UARTPTO]
+
+ /* 8-bit data, no parity, Tx/Rx enabled, UART mode */
+ mov w4, #(UARTCR_PC1 | UARTCR_RXEN | UARTCR_TXEN | UARTCR_PC0 | \
+ UARTCR_WL0 | UARTCR_UART | UARTCR_RFBM | UARTCR_TFBM)
+ str w4, [x0, LINFLEX_UARTCR]
+
+ /* End init mode */
+ ldr w4, [x0, LINFLEX_LINCR1]
+ bic w4, w4, #LINCR1_INIT
+ str w4, [x0, LINFLEX_LINCR1]
+ ret
+endfunc console_linflex_core_init
+
+/**
+ * int console_linflex_register(uintptr_t baseaddr, uint32_t clock,
+ * uint32_t clock, uint32_t baud);
+ *
+ * Function to initialize and register the console.
+ * The caller needs to pass an empty console_linflex_t
+ * structure in which *MUST* be allocated in
+ * persistent memory (e.g. a global or static local
+ * variable, *NOT* on the stack).
+ * In: x0 - Linflex base address
+ * x1 - clock frequency
+ * x2 - baudrate
+ * x3 - pointer to empty console_t structure
+ * Out: x0 - 1 on success, 0 on error
+ * Clobber list : x0 - x7, x13 - x15
+ */
+func console_linflex_register
+ mov x15, x30
+ bl console_linflex_core_init
+ mov x30, x15
+
+ /* Populate the base address */
+ str x0, [x3, #CONSOLE_T_BASE]
+
+ mov x0, x3
+ finish_console_register linflex, putc=1, getc=0, flush=0
+endfunc console_linflex_register
+
+/**
+ * int console_linflex_core_putc(int c, uintptr_t baseaddr);
+
+ * Out: w0 - printed character on success, < 0 on error.
+ * Clobber list : x0 - x3
+ */
+func console_linflex_core_putc
+ cbz x1, putc_error
+
+ cmp w0, #'\n'
+ b.ne print_char
+
+ /* Print '\r\n' for each '\n' */
+ mov x0, #'\r'
+ mov x14, x30
+ bl console_linflex_core_putc
+ mov x30, x14
+ mov x0, #'\n'
+
+print_char:
+ ldr w2, [x1, LINFLEX_UARTCR]
+ and w2, w2, #UARTCR_TFBM
+ cmp w2, #0x0
+ b.eq buffer_mode
+
+fifo_mode:
+ /* UART is in FIFO mode */
+ ldr w2, [x1, LINFLEX_UARTSR]
+ and w2, w2, #UARTSR_DTF
+ cmp w2, #0
+ b.ne fifo_mode
+
+ strb w0, [x1, LINFLEX_BDRL]
+ b no_error
+
+buffer_mode:
+ strb w0, [x1, LINFLEX_BDRL]
+
+buffer_loop:
+ ldr w2, [x1, LINFLEX_UARTSR]
+ and w3, w2, #UARTSR_DTF
+ cmp w3, #0
+ b.eq buffer_loop
+
+ /**
+ * In Buffer Mode the DTFTFF bit of UARTSR register
+ * has to be set in software
+ */
+ mov w2, #UARTSR_DTF
+ str w2, [x1, LINFLEX_UARTSR]
+
+no_error:
+ mov x0, #0
+ ret
+
+putc_error:
+ mov x0, #-EINVAL
+ ret
+endfunc console_linflex_core_putc
+
+/**
+ * int console_linflex_putc(int c, console_t *console);
+ *
+ * Function to output a character over the console. It
+ * returns the character printed on success or -EINVAL on error.
+ * In : w0 - character to be printed
+ * x1 - pointer to console_t struct
+ * Out: w0 - printed character on success, < 0 on error.
+ * Clobber list : x0 - x3, x15
+ */
+func console_linflex_putc
+ cbz x1, putc_error
+ ldr x1, [x1, #CONSOLE_T_BASE]
+
+ b console_linflex_core_putc
+puct_error:
+ mov x0, #-EINVAL
+ ret
+endfunc console_linflex_putc