diff --git a/drivers/st/uart/aarch32/stm32_console.S b/drivers/st/uart/aarch32/stm32_console.S index 2b7c83d..792703a 100644 --- a/drivers/st/uart/aarch32/stm32_console.S +++ b/drivers/st/uart/aarch32/stm32_console.S @@ -5,6 +5,25 @@ */ #include +#define USART_TIMEOUT 0x1000 + +#define USART_CR1 0x00 +#define USART_CR1_UE 0x00000001 +#define USART_CR1_TE 0x00000008 +#define USART_CR1_FIFOEN 0x20000000 + +#define USART_CR2 0x04 +#define USART_CR2_STOP 0x00003000 + +#define USART_BRR 0x0C + +#define USART_ISR 0x1C +#define USART_ISR_TC 0x00000040 +#define USART_ISR_TXE 0x00000080 +#define USART_ISR_TEACK 0x00200000 + +#define USART_TDR 0x28 + .globl console_core_init .globl console_core_putc .globl console_core_getc @@ -27,6 +46,47 @@ * ----------------------------------------------------------------- */ func console_core_init + /* Check the input base address */ + cmp r0, #0 + beq core_init_fail +#if defined(IMAGE_BL2) + /* Check baud rate and uart clock for sanity */ + cmp r1, #0 + beq core_init_fail + cmp r2, #0 + beq core_init_fail + /* Disable UART */ + ldr r3, [r0, #USART_CR1] + bic r3, r3, #USART_CR1_UE + str r3, [r0, #USART_CR1] + /* Configure UART */ + orr r3, r3, #(USART_CR1_TE | USART_CR1_FIFOEN) + str r3, [r0, #USART_CR1] + ldr r3, [r0, #USART_CR2] + bic r3, r3, #USART_CR2_STOP + str r3, [r0, #USART_CR2] + /* Divisor = (Uart clock + (baudrate / 2)) / baudrate */ + lsl r3, r2, #1 + add r3, r1, r3 + udiv r3, r3, r2 + str r3, [r0, #USART_BRR] + /* Enable UART */ + ldr r3, [r0, #USART_CR1] + orr r3, r3, #USART_CR1_UE + str r3, [r0, #USART_CR1] + /* Check TEACK bit */ + mov r2, #USART_TIMEOUT +teack_loop: + subs r2, r2, #1 + beq core_init_fail + ldr r3, [r0, #USART_ISR] + tst r3, #USART_ISR_TEACK + beq teack_loop +#endif /* IMAGE_BL2 */ + mov r0, #1 + bx lr +core_init_fail: + mov r0, #0 bx lr endfunc console_core_init @@ -43,6 +103,40 @@ * --------------------------------------------------------------- */ func console_core_putc + /* Check the input parameter */ + cmp r1, #0 + beq putc_error + /* Prepend '\r' to '\n' */ + cmp r0, #0xA + bne 2f +1: + /* Check Transmit Data Register Empty */ +txe_loop_1: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TXE + beq txe_loop_1 + mov r2, #0xD + str r2, [r1, #USART_TDR] + /* Check transmit complete flag */ +tc_loop_1: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TC + beq tc_loop_1 +2: + /* Check Transmit Data Register Empty */ +txe_loop_2: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TXE + beq txe_loop_2 + str r0, [r1, #USART_TDR] + /* Check transmit complete flag */ +tc_loop_2: + ldr r2, [r1, #USART_ISR] + tst r2, #USART_ISR_TC + beq tc_loop_2 + bx lr +putc_error: + mov r0, #-1 bx lr endfunc console_core_putc @@ -75,5 +169,16 @@ * --------------------------------------------------------------- */ func console_core_flush + cmp r0, #0 + beq flush_error + /* Check Transmit Data Register Empty */ +txe_loop_3: + ldr r1, [r0, #USART_ISR] + tst r1, #USART_ISR_TXE + beq txe_loop_3 + mov r0, #0 + bx lr +flush_error: + mov r0, #-1 bx lr endfunc console_core_flush diff --git a/plat/st/stm32mp1/bl2_plat_setup.c b/plat/st/stm32mp1/bl2_plat_setup.c index 125d2cc..97abdc4 100644 --- a/plat/st/stm32mp1/bl2_plat_setup.c +++ b/plat/st/stm32mp1/bl2_plat_setup.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -38,8 +39,12 @@ void bl2_el3_plat_arch_setup(void) { + int32_t result; + struct dt_node_info dt_dev_info; + const char *board_model; boot_api_context_t *boot_context = (boot_api_context_t *)stm32mp1_get_boot_ctx_address(); + uint32_t clk_rate; /* * Disable the backup domain write protection. @@ -94,6 +99,42 @@ panic(); } + result = dt_get_stdout_uart_info(&dt_dev_info); + + if ((result <= 0) || + (dt_dev_info.status == 0U) || + (dt_dev_info.clock < 0) || + (dt_dev_info.reset < 0)) { + goto skip_console_init; + } + + if (dt_set_stdout_pinctrl() != 0) { + goto skip_console_init; + } + + if (stm32mp1_clk_enable((unsigned long)dt_dev_info.clock) != 0) { + goto skip_console_init; + } + + stm32mp1_reset_assert((uint32_t)dt_dev_info.reset); + udelay(2); + stm32mp1_reset_deassert((uint32_t)dt_dev_info.reset); + mdelay(1); + + clk_rate = stm32mp1_clk_get_rate((unsigned long)dt_dev_info.clock); + + if (console_init(dt_dev_info.base, clk_rate, + STM32MP1_UART_BAUDRATE) == 0) { + panic(); + } + + board_model = dt_get_board_model(); + if (board_model != NULL) { + NOTICE("%s\n", board_model); + } + +skip_console_init: + if (stm32_save_boot_interface(boot_context->boot_interface_selected, boot_context->boot_interface_instance) != 0) { diff --git a/plat/st/stm32mp1/include/stm32mp1_dt.h b/plat/st/stm32mp1/include/stm32mp1_dt.h index 1e0b722..1b1024a 100644 --- a/plat/st/stm32mp1/include/stm32mp1_dt.h +++ b/plat/st/stm32mp1/include/stm32mp1_dt.h @@ -9,6 +9,14 @@ #include +struct dt_node_info { + uint32_t base; + int32_t clock; + int32_t reset; + bool status; + bool sec_status; +}; + /******************************************************************************* * Function and variable prototypes ******************************************************************************/ @@ -22,5 +30,11 @@ int fdt_read_uint32_array(int node, const char *prop_name, uint32_t *array, uint32_t count); int dt_set_pinctrl_config(int node); +int dt_set_stdout_pinctrl(void); +void dt_fill_device_info(struct dt_node_info *info, int node); +int dt_get_node(struct dt_node_info *info, int offset, const char *compat); +int dt_get_stdout_uart_info(struct dt_node_info *info); +int dt_get_stdout_node_offset(void); +const char *dt_get_board_model(void); #endif /* __STM32MP1_DT_H__ */ diff --git a/plat/st/stm32mp1/stm32mp1_dt.c b/plat/st/stm32mp1/stm32mp1_dt.c index fe46ffa..7caf655 100644 --- a/plat/st/stm32mp1/stm32mp1_dt.c +++ b/plat/st/stm32mp1/stm32mp1_dt.c @@ -309,3 +309,148 @@ return 0; } + +/******************************************************************************* + * This function gets the stdout pin configuration information from the DT. + * And then calls the sub-function to treat it and set GPIO registers. + * Returns 0 if success, and a negative value else. + ******************************************************************************/ +int dt_set_stdout_pinctrl(void) +{ + int node; + + node = dt_get_stdout_node_offset(); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + return dt_set_pinctrl_config(node); +} + +/******************************************************************************* + * This function fills the generic information from a given node. + ******************************************************************************/ +void dt_fill_device_info(struct dt_node_info *info, int node) +{ + const fdt32_t *cuint; + + cuint = fdt_getprop(fdt, node, "reg", NULL); + if (cuint != NULL) { + info->base = fdt32_to_cpu(*cuint); + } else { + info->base = 0; + } + + cuint = fdt_getprop(fdt, node, "clocks", NULL); + if (cuint != NULL) { + cuint++; + info->clock = (int)fdt32_to_cpu(*cuint); + } else { + info->clock = -1; + } + + cuint = fdt_getprop(fdt, node, "resets", NULL); + if (cuint != NULL) { + cuint++; + info->reset = (int)fdt32_to_cpu(*cuint); + } else { + info->reset = -1; + } + + info->status = fdt_check_status(node); + info->sec_status = fdt_check_secure_status(node); +} + +/******************************************************************************* + * This function retrieve the generic information from DT. + * Returns node if success, and a negative value else. + ******************************************************************************/ +int dt_get_node(struct dt_node_info *info, int offset, const char *compat) +{ + int node; + + node = fdt_node_offset_by_compatible(fdt, offset, compat); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + dt_fill_device_info(info, node); + + return node; +} + +/******************************************************************************* + * This function gets the UART instance info of stdout from the DT. + * Returns node if success, and a negative value else. + ******************************************************************************/ +int dt_get_stdout_uart_info(struct dt_node_info *info) +{ + int node; + + node = dt_get_stdout_node_offset(); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + dt_fill_device_info(info, node); + + return node; +} + +/******************************************************************************* + * This function gets the stdout path node. + * It reads the value indicated inside the device tree. + * Returns node if success, and a negative value else. + ******************************************************************************/ +int dt_get_stdout_node_offset(void) +{ + int node; + const char *cchar; + + node = fdt_path_offset(fdt, "/chosen"); + if (node < 0) { + return -FDT_ERR_NOTFOUND; + } + + cchar = fdt_getprop(fdt, node, "stdout-path", NULL); + if (cchar == NULL) { + return -FDT_ERR_NOTFOUND; + } + + node = -FDT_ERR_NOTFOUND; + if (strchr(cchar, (int)':') != NULL) { + const char *name; + char *str = (char *)cchar; + int len = 0; + + while (strncmp(":", str, 1)) { + len++; + str++; + } + + name = fdt_get_alias_namelen(fdt, cchar, len); + + if (name != NULL) { + node = fdt_path_offset(fdt, name); + } + } else { + node = fdt_path_offset(fdt, cchar); + } + + return node; +} + +/******************************************************************************* + * This function retrieves board model from DT + * Returns string taken from model node, NULL otherwise + ******************************************************************************/ +const char *dt_get_board_model(void) +{ + int node = fdt_path_offset(fdt, "/"); + + if (node < 0) { + return NULL; + } + + return (const char *)fdt_getprop(fdt, node, "model", NULL); +} diff --git a/plat/st/stm32mp1/stm32mp1_helper.S b/plat/st/stm32mp1/stm32mp1_helper.S index 421d957..b0ea0d8 100644 --- a/plat/st/stm32mp1/stm32mp1_helper.S +++ b/plat/st/stm32mp1/stm32mp1_helper.S @@ -8,6 +8,14 @@ #include #include #include +#include +#include + +#define GPIO_BANK_G_ADDRESS 0x50008000 +#define GPIO_TX_PORT 11 +#define GPIO_TX_SHIFT (GPIO_TX_PORT << 1) +#define GPIO_TX_ALT_SHIFT ((GPIO_TX_PORT - GPIO_ALT_LOWER_LIMIT) << 2) +#define STM32MP1_HSI_CLK 64000000 .globl platform_mem_init .globl plat_report_exception @@ -16,6 +24,9 @@ .globl plat_reset_handler .globl plat_is_my_cpu_primary .globl plat_my_core_pos + .globl plat_crash_console_init + .globl plat_crash_console_flush + .globl plat_crash_console_putc .globl plat_panic_handler func platform_mem_init @@ -92,3 +103,78 @@ ldcopr r0, MPIDR b plat_stm32mp1_get_core_pos endfunc plat_my_core_pos + + /* --------------------------------------------- + * int plat_crash_console_init(void) + * + * Initialize the crash console without a C Runtime stack. + * --------------------------------------------- + */ +func plat_crash_console_init + /* Enable GPIOs for UART4 TX */ + ldr r1, =(RCC_BASE + RCC_MP_AHB4ENSETR) + ldr r2, [r1] + /* Configure GPIO G11 */ + orr r2, r2, #RCC_MP_AHB4ENSETR_GPIOGEN + str r2, [r1] + ldr r1, =GPIO_BANK_G_ADDRESS + /* Set GPIO mode alternate */ + ldr r2, [r1, #GPIO_MODE_OFFSET] + bic r2, r2, #(GPIO_MODE_MASK << GPIO_TX_SHIFT) + orr r2, r2, #(GPIO_MODE_ALTERNATE << GPIO_TX_SHIFT) + str r2, [r1, #GPIO_MODE_OFFSET] + /* Set GPIO speed low */ + ldr r2, [r1, #GPIO_SPEED_OFFSET] + bic r2, r2, #(GPIO_SPEED_MASK << GPIO_TX_SHIFT) + str r2, [r1, #GPIO_SPEED_OFFSET] + /* Set no-pull */ + ldr r2, [r1, #GPIO_PUPD_OFFSET] + bic r2, r2, #(GPIO_PULL_MASK << GPIO_TX_SHIFT) + str r2, [r1, #GPIO_PUPD_OFFSET] + /* Set alternate AF6 */ + ldr r2, [r1, #GPIO_AFRH_OFFSET] + bic r2, r2, #(GPIO_ALTERNATE_MASK << GPIO_TX_ALT_SHIFT) + orr r2, r2, #(GPIO_ALTERNATE_6 << GPIO_TX_ALT_SHIFT) + str r2, [r1, #GPIO_AFRH_OFFSET] + + /* Enable UART clock, with HSI source */ + ldr r1, =(RCC_BASE + RCC_UART24CKSELR) + mov r2, #RCC_UART24CKSELR_HSI + str r2, [r1] + ldr r1, =(RCC_BASE + RCC_MP_APB1ENSETR) + ldr r2, [r1] + orr r2, r2, #RCC_MP_APB1ENSETR_UART4EN + str r2, [r1] + + ldr r0, =STM32MP1_DEBUG_USART_BASE + ldr r1, =STM32MP1_HSI_CLK + ldr r2, =STM32MP1_UART_BAUDRATE + b console_core_init +endfunc plat_crash_console_init + + /* --------------------------------------------- + * int plat_crash_console_flush(void) + * + * Flush the crash console without a C Runtime stack. + * --------------------------------------------- + */ +func plat_crash_console_flush + ldr r1, =STM32MP1_DEBUG_USART_BASE + b console_core_flush +endfunc plat_crash_console_flush + + /* --------------------------------------------- + * int plat_crash_console_putc(int c) + * + * Print a character on the crash console without a C Runtime stack. + * Clobber list : r1 - r3 + * + * In case of bootloading through uart, we keep console crash as this. + * Characters could be sent to the programmer, but will be ignored. + * No specific code in that case. + * --------------------------------------------- + */ +func plat_crash_console_putc + ldr r1, =STM32MP1_DEBUG_USART_BASE + b console_core_putc +endfunc plat_crash_console_putc