stm32mp1: Add console support
authorYann Gautier <yann.gautier@st.com>
Thu, 5 Jul 2018 14:48:16 +0000 (16:48 +0200)
committerYann Gautier <yann.gautier@st.com>
Tue, 24 Jul 2018 15:14:39 +0000 (17:14 +0200)
Signed-off-by: Yann Gautier <yann.gautier@st.com>
Signed-off-by: Nicolas Le Bayon <nicolas.le.bayon@st.com>
Signed-off-by: Lionel Debieve <lionel.debieve@st.com>
drivers/st/uart/aarch32/stm32_console.S
plat/st/stm32mp1/bl2_plat_setup.c
plat/st/stm32mp1/include/stm32mp1_dt.h
plat/st/stm32mp1/stm32mp1_dt.c
plat/st/stm32mp1/stm32mp1_helper.S

index 2b7c83d59380d3ad4d0686c9d647cbabbe8b6c71..792703a9312d20673fcc5d537f501ee5f0f64fe9 100644 (file)
@@ -5,6 +5,25 @@
  */
 #include <asm_macros.S>
 
+#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
         * -----------------------------------------------------------------
         */
 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 @@ endfunc console_core_init
         * ---------------------------------------------------------------
         */
 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 @@ endfunc console_core_getc
         * ---------------------------------------------------------------
         */
 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
index 125d2cc9756ff24eb4f8482ca7eaff5bbfd32a19..97abdc4774d9ac269c4f3be1487b46e97838988e 100644 (file)
@@ -22,6 +22,7 @@
 #include <stm32mp1_context.h>
 #include <stm32mp1_pwr.h>
 #include <stm32mp1_rcc.h>
+#include <stm32mp1_reset.h>
 #include <string.h>
 #include <xlat_tables_v2.h>
 
@@ -38,8 +39,12 @@ void bl2_platform_setup(void)
 
 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 @@ void bl2_el3_plat_arch_setup(void)
                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) {
index 1e0b722b6f36b00310212ac54a81316a6eac5212..1b1024a5241fd4f2fbd66e69724da37f8caa1783 100644 (file)
@@ -9,6 +9,14 @@
 
 #include <stdbool.h>
 
+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 @@ uint32_t fdt_read_uint32_default(int node, const char *prop_name,
 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__ */
index fe46ffac984073d30e26b377a5d310d0e9523f4f..7caf6559130a3b12e74424d4028aba83521cba80 100644 (file)
@@ -309,3 +309,148 @@ int dt_set_pinctrl_config(int node)
 
        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);
+}
index 421d9577ece2df6498befb3d15495983d4594c48..b0ea0d8edd42f7d1761a682560a9f88757abfbe3 100644 (file)
@@ -8,6 +8,14 @@
 #include <asm_macros.S>
 #include <bl_common.h>
 #include <platform_def.h>
+#include <stm32_gpio.h>
+#include <stm32mp1_rcc.h>
+
+#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 @@ func plat_my_core_pos
        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