tegra124: Implement and enable serial console support for tegra124.

The driver is very similar to the 8250 driver, except it isn't in two parts,
and it also spaces its registers 4 bytes apart instead of having them directly
adjacent to each other.

Also, eliminate the UART test function in the bootblock. It's no longer needed
since the actual console output serves the same purpose.

Right now the clock divisor is fixed for now, and we'll want to actually
figure out what value to use at some point.

BUG=None
TEST=Built for link, pit, nyan and lumpy. Booted into the bootblock on nyan
and saw serial output on the console.
BRANCH=None

Change-Id: Idd659222901eb76b0ed8cbb986deb5124096f2f6
Signed-off-by: Gabe Black <gabeblack@google.com>
Reviewed-on: https://chromium-review.googlesource.com/171337
Reviewed-by: Gabe Black <gabeblack@chromium.org>
Commit-Queue: Gabe Black <gabeblack@chromium.org>
Tested-by: Gabe Black <gabeblack@chromium.org>
diff --git a/src/soc/nvidia/tegra124/Kconfig b/src/soc/nvidia/tegra124/Kconfig
index 4091e32..c2a40e9 100644
--- a/src/soc/nvidia/tegra124/Kconfig
+++ b/src/soc/nvidia/tegra124/Kconfig
@@ -2,6 +2,10 @@
 	depends on ARCH_ARMV7
 	bool
 	default n
+	select HAVE_UART_MEMORY_MAPPED
+	select HAVE_UART_SPECIAL
+	select BOOTBLOCK_CONSOLE
+	select EARLY_CONSOLE
 	select ARM_BOOTBLOCK_CUSTOM
 
 if SOC_NVIDIA_TEGRA124
@@ -52,4 +56,47 @@
 	hex
 	default 0x800
 
+choice CONSOLE_SERIAL_TEGRA124_UART_CHOICES
+	prompt "Serial Console UART"
+	default CONSOLE_SERIAL_UARTA
+	depends on CONSOLE_SERIAL_UART
+
+config CONSOLE_SERIAL_UARTA
+	bool "UARTA"
+	help
+	  Serial console on UART A.
+
+config CONSOLE_SERIAL_UARTB
+	bool "UARTB"
+	help
+	  Serial console on UART B.
+
+config CONSOLE_SERIAL_UARTC
+	bool "UARTC"
+	help
+	  Serial console on UART C.
+
+config CONSOLE_SERIAL_UARTD
+	bool "UARTD"
+	help
+	  Serial console on UART D.
+
+config CONSOLE_SERIAL_UARTE
+	bool "UARTE"
+	help
+	  Serial console on UART E.
+
+endchoice
+
+config CONSOLE_SERIAL_UART_ADDRESS
+	hex
+	depends on CONSOLE_SERIAL_UART
+	default 0x70006000 if CONSOLE_SERIAL_UARTA
+	default 0x70006040 if CONSOLE_SERIAL_UARTB
+	default 0x70006200 if CONSOLE_SERIAL_UARTC
+	default 0x70006300 if CONSOLE_SERIAL_UARTD
+	default 0x70006400 if CONSOLE_SERIAL_UARTE
+	help
+	  Map the UART names to the respective MMIO addres.
+
 endif
diff --git a/src/soc/nvidia/tegra124/Makefile.inc b/src/soc/nvidia/tegra124/Makefile.inc
index 16293c9..19aabf6 100644
--- a/src/soc/nvidia/tegra124/Makefile.inc
+++ b/src/soc/nvidia/tegra124/Makefile.inc
@@ -6,14 +6,17 @@
 bootblock-y += bootblock.c
 bootblock-y += bootblock_asm.S
 bootblock-y += cbfs.c
+bootblock-$(CONFIG_BOOTBLOCK_CONSOLE) += uart.c
 
 romstage-y += cbfs.c
 romstage-y += monotonic_timer.c
 romstage-y += timer.c
+romstage-$(CONFIG_EARLY_CONSOLE) += uart.c
 
 ramstage-y += cbfs.c
 ramstage-y += monotonic_timer.c
 ramstage-y += timer.c
+ramstage-$(CONFIG_CONSOLE_SERIAL_UART) += uart.c
 
 # We want to grab the bootblock right before it goes into the image and wrap
 # it inside a BCT, but ideally we would do that without making special, one
diff --git a/src/soc/nvidia/tegra124/bootblock.c b/src/soc/nvidia/tegra124/bootblock.c
index cd3d5d5..cd7ea34 100644
--- a/src/soc/nvidia/tegra124/bootblock.c
+++ b/src/soc/nvidia/tegra124/bootblock.c
@@ -19,22 +19,8 @@
 
 #include <arch/hlt.h>
 #include <arch/io.h>
-#include <arch/stages.h>
 #include <cbfs.h>
 #include <console/console.h>
-#include <stdint.h>
-
-#define UART_TEST 0
-
-static uint8_t readr(int reg)
-{
-	return read8((void *)(0x70000000 + 0x6000 + 4 * reg));
-}
-
-static void writer(int reg, uint8_t val)
-{
-	write8(val, (void *)(0x70000000 + 0x6000 + 4 * reg));
-}
 
 static void hacky_hardcoded_uart_setup_function(void)
 {
@@ -83,42 +69,16 @@
 	clrbits_le32((void *)(0x60006000 + 4 + 0), 1 << 6);
 }
 
-static void test_func(void)
-{
-	const unsigned divisor = 221;
-
-	while (!(readr(5) & 0x40));
-
-	writer(1, 0);
-	writer(3, 0x80 | 0x3);
-	writer(0, 0);
-	writer(1, 0);
-	writer(3, 0x3);
-	writer(2, 0x01 | 0x2 | 0x4);
-	writer(3, 0x80 | 0x3);
-	writer(0, divisor & 0xff);
-	writer(1, (divisor >> 8) & 0xff);
-	writer(3, 0x3);
-
-	for (;;) {
-		writer(0, '!');
-	}
-}
-
 void main(void)
 {
 	void *entry;
 
 	hacky_hardcoded_uart_setup_function();
 
-	if (UART_TEST)
-		test_func();
-
 	if (CONFIG_BOOTBLOCK_CONSOLE)
 		console_init();
 
 	entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA, "fallback/romstage");
 
-	if (entry) stage_exit(entry);
 	hlt();
 }
diff --git a/src/soc/nvidia/tegra124/uart.c b/src/soc/nvidia/tegra124/uart.c
new file mode 100644
index 0000000..b513d36
--- /dev/null
+++ b/src/soc/nvidia/tegra124/uart.c
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <uart.h>
+#include <arch/io.h>
+#include <console/console.h>	/* for __console definition */
+#include <stdint.h>
+#include <uart8250.h>
+
+struct tegra124_uart {
+	union {
+		uint32_t thr; // Transmit holding register.
+		uint32_t rbr; // Receive buffer register.
+		uint32_t dll; // Divisor latch lsb.
+	};
+	union {
+		uint32_t ier; // Interrupt enable register.
+		uint32_t dlm; // Divisor latch msb.
+	};
+	union {
+		uint32_t iir; // Interrupt identification register.
+		uint32_t fcr; // FIFO control register.
+	};
+	uint32_t lcr; // Line control register.
+	uint32_t mcr; // Modem control register.
+	uint32_t lsr; // Line status register.
+	uint32_t msr; // Modem status register.
+} __attribute__ ((packed));
+
+static struct tegra124_uart * const uart_ptr =
+	(void *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
+
+static void tegra124_uart_tx_flush(void);
+static int tegra124_uart_tst_byte(void);
+
+static void tegra124_uart_init(void)
+{
+	// Use a hardcoded divisor for now.
+	const unsigned divisor = 221;
+	const uint8_t line_config = UART8250_LCR_WLS_8; // 8n1
+
+	tegra124_uart_tx_flush();
+
+	// Disable interrupts.
+	write8(0, &uart_ptr->ier);
+	// Set line configuration, access divisor latches.
+	write8(UART8250_LCR_DLAB | line_config, &uart_ptr->lcr);
+	// Set the divisor.
+	write8(divisor & 0xff, &uart_ptr->dll);
+	write8((divisor >> 8) & 0xff, &uart_ptr->dlm);
+	// Hide the divisor latches.
+	write8(line_config, &uart_ptr->lcr);
+	// Enable FIFOs, and clear receive and transmit.
+	write8(UART8250_FCR_FIFO_EN |
+		UART8250_FCR_CLEAR_RCVR |
+		UART8250_FCR_CLEAR_XMIT, &uart_ptr->fcr);
+}
+
+static void tegra124_uart_tx_byte(unsigned char data)
+{
+	while (!(read8(&uart_ptr->lsr) & UART8250_LSR_THRE));
+	write8(data, &uart_ptr->thr);
+}
+
+static void tegra124_uart_tx_flush(void)
+{
+	while (!(read8(&uart_ptr->lsr) & UART8250_LSR_TEMT));
+}
+
+static unsigned char tegra124_uart_rx_byte(void)
+{
+	if (!tegra124_uart_tst_byte())
+		return 0;
+	return read8(&uart_ptr->rbr);
+}
+
+static int tegra124_uart_tst_byte(void)
+{
+	return (read8(&uart_ptr->lsr) & UART8250_LSR_DR) == UART8250_LSR_DR;
+}
+
+#if !defined(__PRE_RAM__)
+
+static const struct console_driver tegra124_uart_console __console = {
+	.init     = tegra124_uart_init,
+	.tx_byte  = tegra124_uart_tx_byte,
+	.tx_flush = tegra124_uart_tx_flush,
+	.rx_byte  = tegra124_uart_rx_byte,
+	.tst_byte = tegra124_uart_tst_byte,
+};
+
+uint32_t uartmem_getbaseaddr(void)
+{
+	return (uintptr_t)uart_ptr;
+}
+
+#else
+
+void uart_init(void)
+{
+	tegra124_uart_init();
+}
+
+void uart_tx_byte(unsigned char data)
+{
+	tegra124_uart_tx_byte(data);
+}
+
+void uart_tx_flush(void)
+{
+	tegra124_uart_tx_flush();
+}
+
+unsigned char uart_rx_byte(void)
+{
+	return tegra124_uart_rx_byte();
+}
+
+#endif