arm: zynq: Support the debug UART

Add support for the debug UART to assist with early debugging. Enable it
for Zybo as an example.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 53b4e1b..d462244 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -91,6 +91,13 @@
 	  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_ZYNQ
+	bool "Xilinx Zynq"
+	help
+	  Select this to enable a debug UART using the serial_s5p driver. You
+	  will need to provide parameters to make this work. The driver will
+	  be available until the real driver-model serial is running.
+
 endchoice
 
 config DEBUG_UART_BASE
diff --git a/drivers/serial/serial_zynq.c b/drivers/serial/serial_zynq.c
index 9d84290..3b12c01 100644
--- a/drivers/serial/serial_zynq.c
+++ b/drivers/serial/serial_zynq.c
@@ -6,6 +6,7 @@
  */
 
 #include <common.h>
+#include <errno.h>
 #include <fdtdec.h>
 #include <watchdog.h>
 #include <asm/io.h>
@@ -43,20 +44,16 @@
 };
 
 /* Set up the baud rate in gd struct */
-static void uart_zynq_serial_setbrg(const int port)
+static void _uart_zynq_serial_setbrg(struct uart_zynq *regs,
+				     unsigned long clock, unsigned long baud)
 {
 	/* Calculation results. */
 	unsigned int calc_bauderror, bdiv, bgen;
 	unsigned long calc_baud = 0;
-	unsigned long baud;
-	unsigned long clock = get_uart_clk(port);
-	struct uart_zynq *regs = uart_zynq_ports[port];
 
 	/* Covering case where input clock is so slow */
-	if (clock < 1000000 && gd->baudrate > 4800)
-		gd->baudrate = 4800;
-
-	baud = gd->baudrate;
+	if (clock < 1000000 && baud > 4800)
+		baud = 4800;
 
 	/*                master clock
 	 * Baud rate = ------------------
@@ -87,6 +84,24 @@
 	writel(bgen, &regs->baud_rate_gen);
 }
 
+/* Set up the baud rate in gd struct */
+static void uart_zynq_serial_setbrg(const int port)
+{
+	unsigned long clock = get_uart_clk(port);
+	struct uart_zynq *regs = uart_zynq_ports[port];
+
+	return _uart_zynq_serial_setbrg(regs, clock, gd->baudrate);
+}
+
+/* Initialize the UART, with...some settings. */
+static void _uart_zynq_serial_init(struct uart_zynq *regs)
+{
+	/* RX/TX enabled & reset */
+	writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \
+					ZYNQ_UART_CR_RXRST, &regs->control);
+	writel(ZYNQ_UART_MR_PARITY_NONE, &regs->mode); /* 8 bit, no parity */
+}
+
 /* Initialize the UART, with...some settings. */
 static int uart_zynq_serial_init(const int port)
 {
@@ -95,28 +110,33 @@
 	if (!regs)
 		return -1;
 
-	/* RX/TX enabled & reset */
-	writel(ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | \
-					ZYNQ_UART_CR_RXRST, &regs->control);
-	writel(ZYNQ_UART_MR_PARITY_NONE, &regs->mode); /* 8 bit, no parity */
+	_uart_zynq_serial_init(regs);
 	uart_zynq_serial_setbrg(port);
 
 	return 0;
 }
 
+static int _uart_zynq_serial_putc(struct uart_zynq *regs, const char c)
+{
+	if (readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL)
+		return -EAGAIN;
+
+	writel(c, &regs->tx_rx_fifo);
+
+	return 0;
+}
+
 static void uart_zynq_serial_putc(const char c, const int port)
 {
 	struct uart_zynq *regs = uart_zynq_ports[port];
 
-	while ((readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
+	while (_uart_zynq_serial_putc(regs, c) == -EAGAIN)
 		WATCHDOG_RESET();
 
 	if (c == '\n') {
-		writel('\r', &regs->tx_rx_fifo);
-		while ((readl(&regs->channel_sts) & ZYNQ_UART_SR_TXFULL) != 0)
+		while (_uart_zynq_serial_putc(regs, '\r') == -EAGAIN)
 			WATCHDOG_RESET();
 	}
-	writel(c, &regs->tx_rx_fifo);
 }
 
 static void uart_zynq_serial_puts(const char *s, const int port)
@@ -218,3 +238,28 @@
 	serial_register(&uart_zynq_serial0_device);
 	serial_register(&uart_zynq_serial1_device);
 }
+
+#ifdef CONFIG_DEBUG_UART_ZYNQ
+
+#include <debug_uart.h>
+
+void _debug_uart_init(void)
+{
+	struct uart_zynq *regs = (struct uart_zynq *)CONFIG_DEBUG_UART_BASE;
+
+	_uart_zynq_serial_init(regs);
+	_uart_zynq_serial_setbrg(regs, CONFIG_DEBUG_UART_CLOCK,
+				 CONFIG_BAUDRATE);
+}
+
+static inline void _debug_uart_putc(int ch)
+{
+	struct uart_zynq *regs = (struct uart_zynq *)CONFIG_DEBUG_UART_BASE;
+
+	while (_uart_zynq_serial_putc(regs, ch) == -EAGAIN)
+		WATCHDOG_RESET();
+}
+
+DEBUG_UART_FUNCS
+
+#endif