stm32mp1: Add console support

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>
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 <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_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 <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 @@
 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 <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 @@
 	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