sc7180: Add UART support

This implements the UART driver in SoC

Developer/Reviewer, be aware of this patch from Napali:
https://review.coreboot.org/c/coreboot/+/25373/78

Change-Id: I6494daa108197c030577ac86dab71f9ca6c21bdb
Signed-off-by: Roja Rani Yarubandi <rojay@codeaurora.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/35500
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/src/soc/qualcomm/sc7180/Kconfig b/src/soc/qualcomm/sc7180/Kconfig
index 4093c93..faf036e 100644
--- a/src/soc/qualcomm/sc7180/Kconfig
+++ b/src/soc/qualcomm/sc7180/Kconfig
@@ -31,4 +31,10 @@
 	int
 	default 16
 
+config UART_FOR_CONSOLE
+	int
+	default 8
+	help
+	 Select the QUP instance to be used for UART console output.
+
 endif
diff --git a/src/soc/qualcomm/sc7180/Makefile.inc b/src/soc/qualcomm/sc7180/Makefile.inc
index 554efd8..e49521b 100644
--- a/src/soc/qualcomm/sc7180/Makefile.inc
+++ b/src/soc/qualcomm/sc7180/Makefile.inc
@@ -17,9 +17,11 @@
 verstage-y += timer.c
 verstage-y += spi.c
 verstage-y += gpio.c
-verstage-$(CONFIG_DRIVERS_UART) += uart_bitbang.c
 verstage-y += clock.c
 verstage-$(CONFIG_SC7180_QSPI) += qspi.c
+verstage-y += qcom_qup_se.c
+verstage-y += qupv3_config.c
+verstage-$(CONFIG_DRIVERS_UART) += qupv3_uart.c
 
 ################################################################################
 romstage-y += cbmem.c
@@ -31,22 +33,24 @@
 romstage-y += usb.c
 romstage-y += spi.c
 romstage-y += gpio.c
-romstage-$(CONFIG_DRIVERS_UART) += uart_bitbang.c
 romstage-y += clock.c
 romstage-$(CONFIG_SC7180_QSPI) += qspi.c
+romstage-y += qcom_qup_se.c
+romstage-y += qupv3_config.c
+romstage-$(CONFIG_DRIVERS_UART) += qupv3_uart.c
 
 ################################################################################
 ramstage-y += soc.c
 ramstage-y += timer.c
 ramstage-y += spi.c
 ramstage-y += gpio.c
-ramstage-$(CONFIG_DRIVERS_UART) += uart_bitbang.c
 ramstage-y += clock.c
 ramstage-$(CONFIG_SC7180_QSPI) += qspi.c
 ramstage-y += aop_load_reset.c
 ramstage-y += usb.c
 ramstage-y += qupv3_config.c
 ramstage-y += qcom_qup_se.c
+ramstage-$(CONFIG_DRIVERS_UART) += qupv3_uart.c
 
 ################################################################################
 
diff --git a/src/soc/qualcomm/sc7180/qupv3_uart.c b/src/soc/qualcomm/sc7180/qupv3_uart.c
new file mode 100644
index 0000000..aba3528
--- /dev/null
+++ b/src/soc/qualcomm/sc7180/qupv3_uart.c
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#include <assert.h>
+#include <boot/coreboot_tables.h>
+#include <console/uart.h>
+#include <soc/clock.h>
+#include <soc/qcom_qup_se.h>
+#include <soc/qupv3_config.h>
+#include <types.h>
+
+/* COMMON STATUS/CONFIGURATION REGISTERS AND MASKS */
+
+#define GENI_STATUS_M_GENI_CMD_ACTIVE_MASK	0x1
+#define GENI_STATUS_S_GENI_CMD_ACTIVE_MASK	0x1000
+
+#define UART_TX_WATERMARK_MARGIN	4	/* Represented in words */
+#define UART_RX_WATERMARK_MARGIN	8	/* Represented in words */
+#define UART_RX_RFR_WATERMARK_MARGIN	4	/* Represented in words */
+#define UART_TX_BITS_PER_WORD	8
+#define UART_RX_BITS_PER_WORD	8
+#define START_UART_TX		0x8000000
+#define START_UART_RX		0x8000000
+
+/* UART FIFO Packing Configuration. */
+/* Start_idx:0, direction:0, len:7, stop:0 */
+#define UART_TX_PACK_VECTOR0	0x0E
+/* Start_idx:8, direction:0, len:7, stop:0 */
+#define UART_TX_PACK_VECTOR1	0x10E
+/* Start_idx:16, direction:0, len:7, stop:0 */
+#define UART_TX_PACK_VECTOR2	0x20E
+/* Start_idx:24, direction:0, len:7, stop:1 */
+#define UART_TX_PACK_VECTOR3	0x30F
+/* Start_idx:0, direction:0, len:7, stop:1 */
+#define UART_RX_PACK_VECTOR0	0xF
+#define UART_RX_PACK_VECTOR2	0x00
+
+void uart_tx_flush(int idx)
+{
+	struct qup_regs *regs = qup[idx].regs;
+
+	while (read32(&regs->geni_status) &
+	       GENI_STATUS_M_GENI_CMD_ACTIVE_MASK)
+		;
+}
+
+void uart_init(int idx)
+{
+	struct qup_regs *regs = qup[idx].regs;
+	unsigned int reg_value;
+	unsigned int div, baud_rate, uart_freq;
+
+	/*
+	 * If the RX (secondary) sequencer is already active, it means the core
+	 * has been already initialized in the previous stage. Skip
+	 * configuration
+	 */
+	if (read32(&regs->geni_status) & GENI_STATUS_S_GENI_CMD_ACTIVE_MASK)
+		return;
+
+	qupv3_se_fw_load_and_init(idx, SE_PROTOCOL_UART, FIFO);
+	clock_enable_qup(idx);
+
+	reg_value = read32(&regs->geni_fw_revision_ro);
+	reg_value &= GENI_FW_REVISION_RO_PROTOCOL_MASK;
+	reg_value >>= GENI_FW_REVISION_RO_PROTOCOL_SHIFT;
+
+	assert(reg_value == SE_PROTOCOL_UART);
+
+	baud_rate = get_uart_baudrate();
+
+	/* sc7180 requires 16 clock pulses to sample 1 bit of data */
+	uart_freq = baud_rate * 16;
+
+	div = DIV_ROUND_CLOSEST(QUPV3_UART_SRC_HZ, uart_freq);
+	write32(&regs->geni_ser_m_clk_cfg, (div << 4) | 1);
+	write32(&regs->geni_ser_s_clk_cfg, (div << 4) | 1);
+
+	/* GPIO Configuration */
+	gpio_configure(qup[idx].pin[2], qup[idx].func[2], GPIO_PULL_UP,
+				GPIO_2MA, GPIO_OUTPUT);
+	gpio_configure(qup[idx].pin[3], qup[idx].func[3], GPIO_PULL_UP,
+				GPIO_2MA, GPIO_INPUT);
+
+	write32(&regs->geni_tx_watermark_reg, UART_TX_WATERMARK_MARGIN);
+	write32(&regs->geni_rx_watermark_reg, FIFO_DEPTH
+					- UART_RX_WATERMARK_MARGIN);
+	write32(&regs->geni_rx_rfr_watermark_reg,
+			FIFO_DEPTH - UART_RX_RFR_WATERMARK_MARGIN);
+
+	write32(&regs->uart_tx_word_len, UART_TX_BITS_PER_WORD);
+	write32(&regs->uart_rx_word_len, UART_RX_BITS_PER_WORD);
+
+	/* Disable TX parity calculation */
+	write32(&regs->uart_tx_parity_cfg, 0x0);
+	/* Ignore CTS line status for TX communication */
+	write32(&regs->uart_tx_trans_cfg_reg, 0x2);
+	/* Disable RX parity calculation */
+	write32(&regs->uart_rx_parity_cfg, 0x0);
+	/* Disable parity, framing and break check on received word */
+	write32(&regs->uart_rx_trans_cfg, 0x0);
+	/* Set UART TX stop bit len to one UART bit length */
+	write32(&regs->uart_tx_stop_bit_len, 0x0);
+	write32(&regs->uart_rx_stale_cnt, 0x16 * 10);
+
+	write32(&regs->geni_tx_packing_cfg0, UART_TX_PACK_VECTOR0 |
+						(UART_TX_PACK_VECTOR1 << 10));
+	write32(&regs->geni_tx_packing_cfg1, UART_TX_PACK_VECTOR2 |
+						(UART_TX_PACK_VECTOR3 << 10));
+	write32(&regs->geni_rx_packing_cfg0, UART_RX_PACK_VECTOR0);
+	write32(&regs->geni_rx_packing_cfg1, UART_RX_PACK_VECTOR2);
+
+	/* Start RX */
+	write32(&regs->geni_s_cmd0, START_UART_RX);
+}
+
+unsigned char uart_rx_byte(int idx)
+{
+	struct qup_regs *regs = qup[idx].regs;
+
+	if (read32(&regs->geni_rx_fifo_status) & RX_FIFO_WC_MSK)
+		return read32(&regs->geni_rx_fifon) & 0xFF;
+	return 0;
+}
+
+void uart_tx_byte(int idx, unsigned char data)
+{
+	struct qup_regs *regs = qup[idx].regs;
+
+	uart_tx_flush(idx);
+
+	write32(&regs->uart_tx_trans_len, 1);
+	/* Start TX */
+	write32(&regs->geni_m_cmd0, START_UART_TX);
+	write32(&regs->geni_tx_fifon, data);
+}
+
+uintptr_t uart_platform_base(int idx)
+{
+	return (uintptr_t)qup[idx].regs;
+}
+
+void uart_fill_lb(void *data)
+{
+	struct lb_serial serial = {0};
+
+	serial.type = LB_SERIAL_TYPE_MEMORY_MAPPED;
+	serial.baseaddr = (uint32_t)uart_platform_base(CONFIG_UART_FOR_CONSOLE);
+	serial.baud = get_uart_baudrate();
+	serial.regwidth = 4;
+	serial.input_hertz = QUPV3_UART_SRC_HZ;
+
+	lb_add_serial(&serial, data);
+}