serial: stm32: Add debug uart support

Add support for early debug printf, before the availability of
driver model and device tree support.

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
Signed-off-by: Patrice Chotard <patrice.chotard@st.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 8777909..2940bd0 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -324,6 +324,15 @@
 	  will need to provide parameters to make this work. The driver will
 	  be available until the real driver model serial is running.
 
+config DEBUG_UART_STM32
+	bool "STMicroelectronics STM32"
+	depends on STM32_SERIAL
+	help
+	  Select this to enable a debug UART using the serial_stm32 driver
+	  You will need to provide parameters to make this work.
+	  The driver will be available until the real driver model
+	  serial is running.
+
 config DEBUG_UART_UNIPHIER
 	bool "UniPhier on-chip UART"
 	depends on ARCH_UNIPHIER
diff --git a/drivers/serial/serial_stm32.c b/drivers/serial/serial_stm32.c
index 6717ffa..724d6f9 100644
--- a/drivers/serial/serial_stm32.c
+++ b/drivers/serial/serial_stm32.c
@@ -7,19 +7,21 @@
 #include <common.h>
 #include <clk.h>
 #include <dm.h>
-#include <asm/io.h>
 #include <serial.h>
+#include <watchdog.h>
+#include <asm/io.h>
 #include <asm/arch/stm32.h>
 #include "serial_stm32.h"
 
-static int stm32_serial_setbrg(struct udevice *dev, int baudrate)
+static void _stm32_serial_setbrg(fdt_addr_t base,
+				 struct stm32_uart_info *uart_info,
+				 u32 clock_rate,
+				 int baudrate)
 {
-	struct stm32x7_serial_platdata *plat = dev_get_platdata(dev);
-	bool stm32f4 = plat->uart_info->stm32f4;
-	fdt_addr_t base = plat->base;
+	bool stm32f4 = uart_info->stm32f4;
 	u32 int_div, mantissa, fraction, oversampling;
 
-	int_div = DIV_ROUND_CLOSEST(plat->clock_rate, baudrate);
+	int_div = DIV_ROUND_CLOSEST(clock_rate, baudrate);
 
 	if (int_div < 16) {
 		oversampling = 8;
@@ -33,6 +35,14 @@
 	fraction = int_div % oversampling;
 
 	writel(mantissa | fraction, base + BRR_OFFSET(stm32f4));
+}
+
+static int stm32_serial_setbrg(struct udevice *dev, int baudrate)
+{
+	struct stm32x7_serial_platdata *plat = dev_get_platdata(dev);
+
+	_stm32_serial_setbrg(plat->base, plat->uart_info,
+			     plat->clock_rate, baudrate);
 
 	return 0;
 }
@@ -58,11 +68,11 @@
 	return readl(base + RDR_OFFSET(stm32f4));
 }
 
-static int stm32_serial_putc(struct udevice *dev, const char c)
+static int _stm32_serial_putc(fdt_addr_t base,
+			      struct stm32_uart_info *uart_info,
+			      const char c)
 {
-	struct stm32x7_serial_platdata *plat = dev_get_platdata(dev);
-	bool stm32f4 = plat->uart_info->stm32f4;
-	fdt_addr_t base = plat->base;
+	bool stm32f4 = uart_info->stm32f4;
 
 	if ((readl(base + ISR_OFFSET(stm32f4)) & USART_ISR_FLAG_TXE) == 0)
 		return -EAGAIN;
@@ -72,6 +82,13 @@
 	return 0;
 }
 
+static int stm32_serial_putc(struct udevice *dev, const char c)
+{
+	struct stm32x7_serial_platdata *plat = dev_get_platdata(dev);
+
+	return _stm32_serial_putc(plat->base, plat->uart_info, c);
+}
+
 static int stm32_serial_pending(struct udevice *dev, bool input)
 {
 	struct stm32x7_serial_platdata *plat = dev_get_platdata(dev);
@@ -86,18 +103,28 @@
 			USART_ISR_FLAG_TXE ? 0 : 1;
 }
 
+static void _stm32_serial_init(fdt_addr_t base,
+			       struct stm32_uart_info *uart_info)
+{
+	bool stm32f4 = uart_info->stm32f4;
+	u8 uart_enable_bit = uart_info->uart_enable_bit;
+
+	/* Disable uart-> enable fifo -> enable uart */
+	clrbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_RE | USART_CR1_TE |
+		     BIT(uart_enable_bit));
+	if (uart_info->has_fifo)
+		setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_FIFOEN);
+	setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_RE | USART_CR1_TE |
+		     BIT(uart_enable_bit));
+}
+
 static int stm32_serial_probe(struct udevice *dev)
 {
 	struct stm32x7_serial_platdata *plat = dev_get_platdata(dev);
 	struct clk clk;
-	fdt_addr_t base = plat->base;
 	int ret;
-	bool stm32f4;
-	u8 uart_enable_bit;
 
 	plat->uart_info = (struct stm32_uart_info *)dev_get_driver_data(dev);
-	stm32f4 = plat->uart_info->stm32f4;
-	uart_enable_bit = plat->uart_info->uart_enable_bit;
 
 	ret = clk_get_by_index(dev, 0, &clk);
 	if (ret < 0)
@@ -115,13 +142,7 @@
 		return plat->clock_rate;
 	};
 
-	/* Disable uart-> enable fifo-> enable uart */
-	clrbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_RE | USART_CR1_TE |
-		     BIT(uart_enable_bit));
-	if (plat->uart_info->has_fifo)
-		setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_FIFOEN);
-	setbits_le32(base + CR1_OFFSET(stm32f4), USART_CR1_RE | USART_CR1_TE |
-		     BIT(uart_enable_bit));
+	_stm32_serial_init(plat->base, plat->uart_info);
 
 	return 0;
 }
@@ -161,3 +182,43 @@
 	.probe = stm32_serial_probe,
 	.flags = DM_FLAG_PRE_RELOC,
 };
+
+#ifdef CONFIG_DEBUG_UART_STM32
+#include <debug_uart.h>
+static inline struct stm32_uart_info *_debug_uart_info(void)
+{
+	struct stm32_uart_info *uart_info;
+
+#if defined(CONFIG_STM32F4)
+	uart_info = &stm32f4_info;
+#elif defined(CONFIG_STM32F7)
+	uart_info = &stm32f7_info;
+#else
+	uart_info = &stm32h7_info;
+#endif
+	return uart_info;
+}
+
+static inline void _debug_uart_init(void)
+{
+	fdt_addr_t base = CONFIG_DEBUG_UART_BASE;
+	struct stm32_uart_info *uart_info = _debug_uart_info();
+
+	_stm32_serial_init(base, uart_info);
+	_stm32_serial_setbrg(base, uart_info,
+			     CONFIG_DEBUG_UART_CLOCK,
+			     CONFIG_BAUDRATE);
+	printf("DEBUG done\n");
+}
+
+static inline void _debug_uart_putc(int c)
+{
+	fdt_addr_t base = CONFIG_DEBUG_UART_BASE;
+	struct stm32_uart_info *uart_info = _debug_uart_info();
+
+	while (_stm32_serial_putc(base, uart_info, c) == -EAGAIN)
+		WATCHDOG_RESET();
+}
+
+DEBUG_UART_FUNCS
+#endif