@@ -957,6 +957,15 @@ config SERIAL_QCOM_GENI
depends on QCOM_GENI_SE
select SERIAL_CORE
+config SERIAL_QCOM_GENI_EARLYCON
+ bool "QCOM GENI Early Console support"
+ select SERIAL_CORE
+ select SERIAL_CORE_CONSOLE
+ select SERIAL_EARLYCON
+ help
+ Serial early console driver for Qualcomm Technologies Inc's GENI
+ based QUP hardware.
+
config SERIAL_QCOM_GENI_CONSOLE
bool "QCOM GENI Serial Console support"
depends on SERIAL_QCOM_GENI
@@ -55,6 +55,7 @@ obj-$(CONFIG_SERIAL_VR41XX) += vr41xx_siu.o
obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o
obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
+obj-$(CONFIG_SERIAL_QCOM_GENI_EARLYCON) += qcom_geni_earlycon.o
obj-$(CONFIG_SERIAL_QCOM_GENI) += qcom_geni_serial.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
new file mode 100644
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2020, The Linux foundation. All rights reserved.
+
+#include <linux/console.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/* Common SE registers */
+#define GENI_FORCE_DEFAULT_REG 0x20
+#define GENI_FW_REVISION_RO 0x68
+#define SE_GENI_DMA_MODE_EN 0x258
+#define SE_GENI_M_CMD0 0x600
+#define SE_GENI_M_CMD_CTRL_REG 0x604
+#define SE_GENI_M_IRQ_STATUS 0x610
+#define SE_GENI_M_IRQ_EN 0x614
+#define SE_GENI_M_IRQ_CLEAR 0x618
+#define SE_GENI_S_CMD0 0x630
+#define SE_GENI_S_CMD_CTRL_REG 0x634
+#define SE_GENI_S_IRQ_STATUS 0x640
+#define SE_GENI_S_IRQ_EN 0x644
+#define SE_GENI_S_IRQ_CLEAR 0x648
+#define SE_GENI_TX_FIFOn 0x700
+#define SE_GENI_RX_FIFOn 0x780
+#define SE_GENI_TX_FIFO_STATUS 0x800
+#define SE_GENI_RX_FIFO_STATUS 0x804
+#define SE_GENI_TX_WATERMARK_REG 0x80c
+#define SE_GENI_RX_WATERMARK_REG 0x810
+#define SE_GENI_RX_RFR_WATERMARK_REG 0x814
+#define SE_DMA_TX_IRQ_CLR 0xc44
+#define SE_DMA_RX_IRQ_CLR 0xd44
+
+/* GENI_FORCE_DEFAULT_REG fields */
+#define FORCE_DEFAULT BIT(0)
+
+/* GENI_FW_REVISION_RO fields */
+#define FW_REV_PROTOCOL_MSK GENMASK(15, 8)
+#define FW_REV_PROTOCOL_SHFT 8
+
+/* SE_GENI_DMA_MODE_EN */
+#define GENI_DMA_MODE_EN BIT(0)
+
+/* GENI_M_CMD0 fields */
+#define M_OPCODE_MSK GENMASK(31, 27)
+#define M_OPCODE_SHFT 27
+#define M_PARAMS_MSK GENMASK(26, 0)
+
+/* GENI_M_CMD_CTRL_REG */
+#define M_GENI_CMD_CANCEL BIT(2)
+#define M_GENI_CMD_ABORT BIT(1)
+#define M_GENI_DISABLE BIT(0)
+
+/* GENI_S_CMD0 fields */
+#define S_OPCODE_MSK GENMASK(31, 27)
+#define S_OPCODE_SHFT 27
+#define S_PARAMS_MSK GENMASK(26, 0)
+
+/* GENI_S_CMD_CTRL_REG */
+#define S_GENI_CMD_CANCEL BIT(2)
+#define S_GENI_CMD_ABORT BIT(1)
+#define S_GENI_DISABLE BIT(0)
+
+/* GENI_M_IRQ_EN fields */
+#define M_CMD_DONE_EN BIT(0)
+#define M_CMD_OVERRUN_EN BIT(1)
+#define M_ILLEGAL_CMD_EN BIT(2)
+#define M_CMD_FAILURE_EN BIT(3)
+#define M_CMD_CANCEL_EN BIT(4)
+#define M_CMD_ABORT_EN BIT(5)
+#define M_TIMESTAMP_EN BIT(6)
+#define M_RX_IRQ_EN BIT(7)
+#define M_GP_SYNC_IRQ_0_EN BIT(8)
+#define M_GP_IRQ_0_EN BIT(9)
+#define M_GP_IRQ_1_EN BIT(10)
+#define M_GP_IRQ_2_EN BIT(11)
+#define M_GP_IRQ_3_EN BIT(12)
+#define M_GP_IRQ_4_EN BIT(13)
+#define M_GP_IRQ_5_EN BIT(14)
+#define M_IO_DATA_DEASSERT_EN BIT(22)
+#define M_IO_DATA_ASSERT_EN BIT(23)
+#define M_RX_FIFO_RD_ERR_EN BIT(24)
+#define M_RX_FIFO_WR_ERR_EN BIT(25)
+#define M_RX_FIFO_WATERMARK_EN BIT(26)
+#define M_RX_FIFO_LAST_EN BIT(27)
+#define M_TX_FIFO_RD_ERR_EN BIT(28)
+#define M_TX_FIFO_WR_ERR_EN BIT(29)
+#define M_TX_FIFO_WATERMARK_EN BIT(30)
+#define M_SEC_IRQ_EN BIT(31)
+#define M_COMMON_GENI_M_IRQ_EN (GENMASK(6, 1) | \
+ M_IO_DATA_DEASSERT_EN | \
+ M_IO_DATA_ASSERT_EN | M_RX_FIFO_RD_ERR_EN | \
+ M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | \
+ M_TX_FIFO_WR_ERR_EN)
+
+/* GENI_S_IRQ_EN fields */
+#define S_CMD_DONE_EN BIT(0)
+#define S_CMD_OVERRUN_EN BIT(1)
+#define S_ILLEGAL_CMD_EN BIT(2)
+#define S_CMD_FAILURE_EN BIT(3)
+#define S_CMD_CANCEL_EN BIT(4)
+#define S_CMD_ABORT_EN BIT(5)
+#define S_GP_SYNC_IRQ_0_EN BIT(8)
+#define S_GP_IRQ_0_EN BIT(9)
+#define S_GP_IRQ_1_EN BIT(10)
+#define S_GP_IRQ_2_EN BIT(11)
+#define S_GP_IRQ_3_EN BIT(12)
+#define S_GP_IRQ_4_EN BIT(13)
+#define S_GP_IRQ_5_EN BIT(14)
+#define S_IO_DATA_DEASSERT_EN BIT(22)
+#define S_IO_DATA_ASSERT_EN BIT(23)
+#define S_RX_FIFO_RD_ERR_EN BIT(24)
+#define S_RX_FIFO_WR_ERR_EN BIT(25)
+#define S_RX_FIFO_WATERMARK_EN BIT(26)
+#define S_RX_FIFO_LAST_EN BIT(27)
+#define S_COMMON_GENI_S_IRQ_EN (GENMASK(5, 1) | GENMASK(13, 9) | \
+ S_RX_FIFO_RD_ERR_EN | S_RX_FIFO_WR_ERR_EN)
+
+/* GENI_RX_FIFO_STATUS fields */
+#define RX_LAST BIT(31)
+#define RX_LAST_BYTE_VALID_MSK GENMASK(30, 28)
+#define RX_LAST_BYTE_VALID_SHFT 28
+#define RX_FIFO_WC_MSK GENMASK(24, 0)
+
+/* UART specific GENI registers */
+#define SE_UART_TX_TRANS_CFG 0x25c
+#define SE_UART_TX_WORD_LEN 0x268
+#define SE_UART_TX_STOP_BIT_LEN 0x26c
+#define SE_UART_TX_TRANS_LEN 0x270
+#define SE_UART_RX_TRANS_CFG 0x280
+#define SE_UART_RX_WORD_LEN 0x28c
+#define SE_UART_TX_PARITY_CFG 0x2a4
+#define SE_UART_RX_PARITY_CFG 0x2a8
+
+/* SE_UART_TRANS_CFG */
+#define UART_TX_PAR_EN BIT(0)
+#define UART_CTS_MASK BIT(1)
+
+/* SE_UART_TX_WORD_LEN */
+#define TX_WORD_LEN_MSK GENMASK(9, 0)
+
+/* SE_UART_TX_STOP_BIT_LEN */
+#define TX_STOP_BIT_LEN_MSK GENMASK(23, 0)
+#define TX_STOP_BIT_LEN_1 0
+#define TX_STOP_BIT_LEN_1_5 1
+#define TX_STOP_BIT_LEN_2 2
+
+/* SE_UART_TX_TRANS_LEN */
+#define TX_TRANS_LEN_MSK GENMASK(23, 0)
+
+/* SE_UART_RX_TRANS_CFG */
+#define UART_RX_INS_STATUS_BIT BIT(2)
+#define UART_RX_PAR_EN BIT(3)
+
+/* SE_UART_RX_WORD_LEN */
+#define RX_WORD_LEN_MASK GENMASK(9, 0)
+
+/* Common SE registers */
+#define GENI_OUTPUT_CTRL 0x24
+#define GENI_CGC_CTRL 0x28
+#define SE_GENI_BYTE_GRAN 0x254
+#define SE_GENI_TX_PACKING_CFG0 0x260
+#define SE_GENI_TX_PACKING_CFG1 0x264
+#define SE_GENI_RX_PACKING_CFG0 0x284
+#define SE_GENI_RX_PACKING_CFG1 0x288
+#define SE_GSI_EVENT_EN 0xe18
+#define SE_IRQ_EN 0xe1c
+#define SE_DMA_GENERAL_CFG 0xe30
+
+/* GENI_OUTPUT_CTRL fields */
+#define DEFAULT_IO_OUTPUT_CTRL_MSK GENMASK(6, 0)
+
+/* GENI_CGC_CTRL fields */
+#define CFG_AHB_CLK_CGC_ON BIT(0)
+#define CFG_AHB_WR_ACLK_CGC_ON BIT(1)
+#define DATA_AHB_CLK_CGC_ON BIT(2)
+#define SCLK_CGC_ON BIT(3)
+#define TX_CLK_CGC_ON BIT(4)
+#define RX_CLK_CGC_ON BIT(5)
+#define EXT_CLK_CGC_ON BIT(6)
+#define PROG_RAM_HCLK_OFF BIT(8)
+#define PROG_RAM_SCLK_OFF BIT(9)
+#define DEFAULT_CGC_EN GENMASK(6, 0)
+
+/* SE_GSI_EVENT_EN fields */
+#define DMA_RX_EVENT_EN BIT(0)
+#define DMA_TX_EVENT_EN BIT(1)
+#define GENI_M_EVENT_EN BIT(2)
+#define GENI_S_EVENT_EN BIT(3)
+
+/* SE_IRQ_EN fields */
+#define DMA_RX_IRQ_EN BIT(0)
+#define DMA_TX_IRQ_EN BIT(1)
+#define GENI_M_IRQ_EN BIT(2)
+#define GENI_S_IRQ_EN BIT(3)
+
+/* SE_DMA_GENERAL_CFG */
+#define DMA_RX_CLK_CGC_ON BIT(0)
+#define DMA_TX_CLK_CGC_ON BIT(1)
+#define DMA_AHB_SLV_CFG_ON BIT(2)
+#define AHB_SEC_SLV_CLK_CGC_ON BIT(3)
+#define DUMMY_RX_NON_BUFFERABLE BIT(4)
+#define RX_DMA_ZERO_PADDING_EN BIT(5)
+#define RX_DMA_IRQ_DELAY_MSK GENMASK(8, 6)
+
+/* UART M_CMD OP codes */
+#define UART_START_TX 0x1
+#define UART_START_BREAK 0x4
+#define UART_STOP_BREAK 0x5
+
+/* UART S_CMD OP codes */
+#define UART_START_READ 0x1
+#define UART_PARAM 0x1
+
+#define DEF_FIFO_DEPTH_WORDS 16
+#define DEF_TX_WM 2
+#define DEF_FIFO_WIDTH_BITS 32
+
+/* We always configure 4 bytes per FIFO word */
+#define BYTES_PER_FIFO_WORD 4
+
+#define UART_PROTOCOL 2
+
+#define NUM_PACKING_VECTORS 4
+#define PACKING_START_SHIFT 5
+#define PACKING_DIR_SHIFT 4
+#define PACKING_LEN_SHIFT 1
+#define PACKING_STOP_BIT BIT(0)
+#define PACKING_VECTOR_SHIFT 10
+
+struct geni_earlycon_private_data {
+ u32 poll_cached_bytes;
+ unsigned int poll_cached_bytes_cnt;
+
+ u32 write_cached_bytes;
+ unsigned int write_cached_bytes_cnt;
+};
+
+struct qcom_geni_serial_port {
+ struct uart_port uport;
+ unsigned int baud;
+
+ struct geni_earlycon_private_data private_data;
+};
+
+static u32 qcom_geni_earlycon_get_proto(void __iomem *base)
+{
+ u32 val;
+
+ val = readl(base + GENI_FW_REVISION_RO);
+
+ return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT;
+}
+
+static void qcom_geni_earlycon_config_packing(void __iomem *base, int bpw,
+ int pack_words, bool msb_to_lsb,
+ bool tx_cfg, bool rx_cfg)
+{
+ u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {0};
+ int len;
+ int temp_bpw = bpw;
+ int idx_start = msb_to_lsb ? bpw - 1 : 0;
+ int idx = idx_start;
+ int idx_delta = msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE;
+ int ceil_bpw = ALIGN(bpw, BITS_PER_BYTE);
+ int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE;
+ int i;
+
+ if (iter <= 0 || iter > NUM_PACKING_VECTORS)
+ return;
+
+ for (i = 0; i < iter; i++) {
+ len = min_t(int, temp_bpw, BITS_PER_BYTE) - 1;
+ cfg[i] = idx << PACKING_START_SHIFT;
+ cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT;
+ cfg[i] |= len << PACKING_LEN_SHIFT;
+
+ if (temp_bpw <= BITS_PER_BYTE) {
+ idx = ((i + 1) * BITS_PER_BYTE) + idx_start;
+ temp_bpw = bpw;
+ } else {
+ idx = idx + idx_delta;
+ temp_bpw = temp_bpw - BITS_PER_BYTE;
+ }
+ }
+ cfg[iter - 1] |= PACKING_STOP_BIT;
+ cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT);
+ cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT);
+
+ if (tx_cfg) {
+ writel(cfg0, base + SE_GENI_TX_PACKING_CFG0);
+ writel(cfg1, base + SE_GENI_TX_PACKING_CFG1);
+ }
+ if (rx_cfg) {
+ writel(cfg0, base + SE_GENI_RX_PACKING_CFG0);
+ writel(cfg1, base + SE_GENI_RX_PACKING_CFG1);
+ }
+
+ /*
+ * Number of protocol words in each FIFO entry
+ * 0 - 4x8, four words in each entry, max word size of 8 bits
+ * 1 - 2x16, two words in each entry, max word size of 16 bits
+ * 2 - 1x32, one word in each entry, max word size of 32 bits
+ * 3 - undefined
+ */
+ if (pack_words || bpw == 32)
+ writel(bpw / 16, base + SE_GENI_BYTE_GRAN);
+}
+
+static void qcom_geni_earlycon_io_set_mode(void __iomem *base)
+{
+ u32 val;
+
+ val = readl_relaxed(base + SE_IRQ_EN);
+ val |= GENI_M_IRQ_EN | GENI_S_IRQ_EN;
+ val |= DMA_TX_IRQ_EN | DMA_RX_IRQ_EN;
+ writel_relaxed(val, base + SE_IRQ_EN);
+
+ val = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
+ val &= ~GENI_DMA_MODE_EN;
+ writel_relaxed(val, base + SE_GENI_DMA_MODE_EN);
+
+ writel_relaxed(0, base + SE_GSI_EVENT_EN);
+}
+
+static void qcom_geni_earlycon_io_init(void __iomem *base)
+{
+ u32 val;
+
+ val = readl_relaxed(base + GENI_CGC_CTRL);
+ val |= DEFAULT_CGC_EN;
+ writel_relaxed(val, base + GENI_CGC_CTRL);
+
+ val = readl_relaxed(base + SE_DMA_GENERAL_CFG);
+ val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON;
+ val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON;
+ writel_relaxed(val, base + SE_DMA_GENERAL_CFG);
+
+ writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL);
+ writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG);
+}
+
+static void qcom_geni_earlycon_irq_clear(void __iomem *base)
+{
+ writel_relaxed(0, base + SE_GSI_EVENT_EN);
+ writel_relaxed(0xffffffff, base + SE_GENI_M_IRQ_CLEAR);
+ writel_relaxed(0xffffffff, base + SE_GENI_S_IRQ_CLEAR);
+ writel_relaxed(0xffffffff, base + SE_DMA_TX_IRQ_CLR);
+ writel_relaxed(0xffffffff, base + SE_DMA_RX_IRQ_CLR);
+ writel_relaxed(0xffffffff, base + SE_IRQ_EN);
+}
+
+static void qcom_geni_earlycon_init(void __iomem *base, u32 rx_wm, u32 rx_rfr)
+{
+ u32 val;
+
+ qcom_geni_earlycon_irq_clear(base);
+ qcom_geni_earlycon_io_init(base);
+ qcom_geni_earlycon_io_set_mode(base);
+
+ writel_relaxed(rx_wm, base + SE_GENI_RX_WATERMARK_REG);
+ writel_relaxed(rx_rfr, base + SE_GENI_RX_RFR_WATERMARK_REG);
+
+ val = readl_relaxed(base + SE_GENI_M_IRQ_EN);
+ val |= M_COMMON_GENI_M_IRQ_EN;
+ writel_relaxed(val, base + SE_GENI_M_IRQ_EN);
+
+ val = readl_relaxed(base + SE_GENI_S_IRQ_EN);
+ val |= S_COMMON_GENI_S_IRQ_EN;
+ writel_relaxed(val, base + SE_GENI_S_IRQ_EN);
+}
+
+static bool qcom_geni_earlycon_poll_bit(struct uart_port *uport,
+ int offset, int field, bool set)
+{
+ u32 reg;
+ unsigned long timeout_us = 20000;
+
+ /*
+ * Use custom implementation instead of readl_poll_atomic since ktimer
+ * is not ready at the time of early console.
+ */
+ while (timeout_us) {
+ reg = readl(uport->membase + offset);
+ if ((bool)(reg & field) == set)
+ return true;
+ udelay(10);
+ timeout_us -= 10;
+ }
+ return false;
+}
+
+static void qcom_geni_earlycon_setup_tx(struct uart_port *uport, u32 xmit_size)
+{
+ u32 m_cmd;
+
+ writel(xmit_size, uport->membase + SE_UART_TX_TRANS_LEN);
+ m_cmd = UART_START_TX << M_OPCODE_SHFT;
+ writel(m_cmd, uport->membase + SE_GENI_M_CMD0);
+}
+
+static void qcom_geni_earlycon_poll_tx_done(struct uart_port *uport)
+{
+ int done;
+ u32 irq_clear = M_CMD_DONE_EN;
+
+ done = qcom_geni_earlycon_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_DONE_EN, true);
+ if (!done) {
+ writel(M_GENI_CMD_ABORT, uport->membase +
+ SE_GENI_M_CMD_CTRL_REG);
+ irq_clear |= M_CMD_ABORT_EN;
+ qcom_geni_earlycon_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_CMD_ABORT_EN, true);
+ }
+ writel(irq_clear, uport->membase + SE_GENI_M_IRQ_CLEAR);
+}
+
+static void qcom_geni_earlycon_abort_rx(struct uart_port *uport)
+{
+ u32 irq_clear = S_CMD_DONE_EN | S_CMD_ABORT_EN;
+
+ writel(S_GENI_CMD_ABORT, uport->membase + SE_GENI_S_CMD_CTRL_REG);
+ qcom_geni_earlycon_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+ S_GENI_CMD_ABORT, false);
+ writel(irq_clear, uport->membase + SE_GENI_S_IRQ_CLEAR);
+ writel(FORCE_DEFAULT, uport->membase + GENI_FORCE_DEFAULT_REG);
+}
+
+static void qcom_geni_earlycon_select_fifo_mode(void __iomem *base)
+{
+ u32 val;
+
+ val = readl_relaxed(base + SE_GENI_DMA_MODE_EN);
+ val &= ~GENI_DMA_MODE_EN;
+ writel_relaxed(val, base + SE_GENI_DMA_MODE_EN);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+
+static void geni_se_setup_s_cmd(void __iomem *base, u32 cmd, u32 params)
+{
+ u32 s_cmd;
+
+ s_cmd = readl(base + SE_GENI_S_CMD0);
+ s_cmd &= ~(S_OPCODE_MSK | S_PARAMS_MSK);
+ s_cmd |= (cmd << S_OPCODE_SHFT);
+ s_cmd |= (params & S_PARAMS_MSK);
+ writel(s_cmd, base + SE_GENI_S_CMD0);
+}
+
+static int qcom_geni_earlycon_get_char(struct uart_port *uport)
+{
+ struct geni_earlycon_private_data *private_data = uport->private_data;
+ u32 status;
+ u32 word_cnt;
+ int ret;
+
+ if (!private_data->poll_cached_bytes_cnt) {
+ status = readl(uport->membase + SE_GENI_M_IRQ_STATUS);
+ writel(status, uport->membase + SE_GENI_M_IRQ_CLEAR);
+
+ status = readl(uport->membase + SE_GENI_S_IRQ_STATUS);
+ writel(status, uport->membase + SE_GENI_S_IRQ_CLEAR);
+
+ status = readl(uport->membase + SE_GENI_RX_FIFO_STATUS);
+ word_cnt = status & RX_FIFO_WC_MSK;
+ if (!word_cnt)
+ return NO_POLL_CHAR;
+
+ if (word_cnt == 1 && (status & RX_LAST))
+ private_data->poll_cached_bytes_cnt =
+ (status & RX_LAST_BYTE_VALID_MSK) >>
+ RX_LAST_BYTE_VALID_SHFT;
+ else
+ private_data->poll_cached_bytes_cnt = 4;
+
+ private_data->poll_cached_bytes =
+ readl(uport->membase + SE_GENI_RX_FIFOn);
+ }
+
+ private_data->poll_cached_bytes_cnt--;
+ ret = private_data->poll_cached_bytes & 0xff;
+ private_data->poll_cached_bytes >>= 8;
+
+ return ret;
+}
+#endif
+
+static void qcom_geni_serial_wr_char(struct uart_port *uport, int ch)
+{
+ struct geni_earlycon_private_data *private_data = uport->private_data;
+
+ private_data->write_cached_bytes =
+ (private_data->write_cached_bytes >> 8) | (ch << 24);
+ private_data->write_cached_bytes_cnt++;
+
+ if (private_data->write_cached_bytes_cnt == BYTES_PER_FIFO_WORD) {
+ writel(private_data->write_cached_bytes,
+ uport->membase + SE_GENI_TX_FIFOn);
+ private_data->write_cached_bytes_cnt = 0;
+ }
+}
+
+static void qcom_geni_earlycon_write(struct console *con, const char *s,
+ unsigned int count)
+{
+ struct earlycon_device *dev = con->data;
+ struct uart_port *uport = &dev->port;
+ struct geni_earlycon_private_data *private_data = uport->private_data;
+
+ int i;
+ u32 bytes_to_send = count;
+
+ for (i = 0; i < count; i++) {
+ /*
+ * uart_console_write() adds a carriage return for each newline.
+ * Account for additional bytes to be written.
+ */
+ if (s[i] == '\n')
+ bytes_to_send++;
+ }
+
+ writel(DEF_TX_WM, uport->membase + SE_GENI_TX_WATERMARK_REG);
+ qcom_geni_earlycon_setup_tx(uport, bytes_to_send);
+ for (i = 0; i < count; ) {
+ size_t chars_to_write = 0;
+ size_t avail = DEF_FIFO_DEPTH_WORDS - DEF_TX_WM;
+
+ /*
+ * If the WM bit never set, then the Tx state machine is not
+ * in a valid state, so break, cancel/abort any existing
+ * command. Unfortunately the current data being written is
+ * lost.
+ */
+ if (!qcom_geni_earlycon_poll_bit(uport, SE_GENI_M_IRQ_STATUS,
+ M_TX_FIFO_WATERMARK_EN, true))
+ break;
+ chars_to_write = min_t(size_t, count - i, avail / 2);
+ uart_console_write(uport, s + i, chars_to_write,
+ qcom_geni_serial_wr_char);
+ writel(M_TX_FIFO_WATERMARK_EN, uport->membase +
+ SE_GENI_M_IRQ_CLEAR);
+ i += chars_to_write;
+ }
+
+ if (private_data->write_cached_bytes_cnt) {
+ private_data->write_cached_bytes >>= BITS_PER_BYTE *
+ (BYTES_PER_FIFO_WORD - private_data->write_cached_bytes_cnt);
+ writel(private_data->write_cached_bytes,
+ uport->membase + SE_GENI_TX_FIFOn);
+ private_data->write_cached_bytes_cnt = 0;
+ }
+
+ qcom_geni_earlycon_poll_tx_done(uport);
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+static int qcom_geni_earlycon_read(struct console *con, char *s, unsigned int n)
+{
+ struct earlycon_device *dev = con->data;
+ struct uart_port *uport = &dev->port;
+ int num_read = 0;
+ int ch;
+
+ while (num_read < n) {
+ ch = qcom_geni_earlycon_get_char(uport);
+ if (ch == NO_POLL_CHAR)
+ break;
+ s[num_read++] = ch;
+ }
+
+ return num_read;
+}
+
+static void __init qcom_geni_earlycon_enable_read(void __iomem *base,
+ struct console *con)
+{
+ geni_se_setup_s_cmd(base, UART_START_READ, 0);
+ con->read = qcom_geni_earlycon_read;
+}
+#else
+static inline void qcom_geni_earlycon_enable_read(void __iomem *base,
+ struct console *con) { }
+#endif
+
+static struct geni_earlycon_private_data earlycon_private_data;
+
+static int __init qcom_geni_earlycon_setup(struct earlycon_device *dev,
+ const char *opt)
+{
+ struct uart_port *uport = &dev->port;
+ u32 tx_trans_cfg;
+ u32 tx_parity_cfg = 0; /* Disable Tx Parity */
+ u32 rx_trans_cfg = 0;
+ u32 rx_parity_cfg = 0; /* Disable Rx Parity */
+ u32 stop_bit_len = 0; /* Default stop bit length - 1 bit */
+ u32 bits_per_char;
+
+ if (!uport->membase)
+ return -EINVAL;
+
+ uport->private_data = &earlycon_private_data;
+
+ if (qcom_geni_earlycon_get_proto(uport->membase) != UART_PROTOCOL)
+ return -ENXIO;
+ /*
+ * Ignore Flow control.
+ * n = 8.
+ */
+ tx_trans_cfg = UART_CTS_MASK;
+ bits_per_char = BITS_PER_BYTE;
+
+ /*
+ * Make an unconditional cancel on the main sequencer to reset
+ * it else we could end up in data loss scenarios.
+ */
+ qcom_geni_earlycon_poll_tx_done(uport);
+ qcom_geni_earlycon_abort_rx(uport);
+ qcom_geni_earlycon_config_packing(uport->membase, BITS_PER_BYTE,
+ BYTES_PER_FIFO_WORD, false, true, true);
+ qcom_geni_earlycon_init(uport->membase, DEF_FIFO_DEPTH_WORDS / 2,
+ DEF_FIFO_DEPTH_WORDS - 2);
+ qcom_geni_earlycon_select_fifo_mode(uport->membase);
+
+ writel(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
+ writel(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
+ writel(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
+ writel(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
+ writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
+ writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
+ writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
+
+ dev->con->write = qcom_geni_earlycon_write;
+ dev->con->setup = NULL;
+ qcom_geni_earlycon_enable_read(uport->membase, dev->con);
+
+ return 0;
+}
+
+OF_EARLYCON_DECLARE(qcom_geni, "qcom,geni-debug-uart",
+ qcom_geni_earlycon_setup);
@@ -107,7 +107,6 @@
#define BYTES_PER_FIFO_WORD 4
struct qcom_geni_private_data {
- /* NOTE: earlycon port will have NULL here */
struct uart_driver *drv;
u32 poll_cached_bytes;
@@ -1139,102 +1138,6 @@ static int qcom_geni_console_setup(struct console *co, char *options)
return uart_set_options(uport, co, baud, parity, bits, flow);
}
-static void qcom_geni_serial_earlycon_write(struct console *con,
- const char *s, unsigned int n)
-{
- struct earlycon_device *dev = con->data;
-
- __qcom_geni_serial_console_write(&dev->port, s, n);
-}
-
-#ifdef CONFIG_CONSOLE_POLL
-static int qcom_geni_serial_earlycon_read(struct console *con,
- char *s, unsigned int n)
-{
- struct earlycon_device *dev = con->data;
- struct uart_port *uport = &dev->port;
- int num_read = 0;
- int ch;
-
- while (num_read < n) {
- ch = qcom_geni_serial_get_char(uport);
- if (ch == NO_POLL_CHAR)
- break;
- s[num_read++] = ch;
- }
-
- return num_read;
-}
-
-static void __init qcom_geni_serial_enable_early_read(struct geni_se *se,
- struct console *con)
-{
- geni_se_setup_s_cmd(se, UART_START_READ, 0);
- con->read = qcom_geni_serial_earlycon_read;
-}
-#else
-static inline void qcom_geni_serial_enable_early_read(struct geni_se *se,
- struct console *con) { }
-#endif
-
-static struct qcom_geni_private_data earlycon_private_data;
-
-static int __init qcom_geni_serial_earlycon_setup(struct earlycon_device *dev,
- const char *opt)
-{
- struct uart_port *uport = &dev->port;
- u32 tx_trans_cfg;
- u32 tx_parity_cfg = 0; /* Disable Tx Parity */
- u32 rx_trans_cfg = 0;
- u32 rx_parity_cfg = 0; /* Disable Rx Parity */
- u32 stop_bit_len = 0; /* Default stop bit length - 1 bit */
- u32 bits_per_char;
- struct geni_se se;
-
- if (!uport->membase)
- return -EINVAL;
-
- uport->private_data = &earlycon_private_data;
-
- memset(&se, 0, sizeof(se));
- se.base = uport->membase;
- if (geni_se_read_proto(&se) != GENI_SE_UART)
- return -ENXIO;
- /*
- * Ignore Flow control.
- * n = 8.
- */
- tx_trans_cfg = UART_CTS_MASK;
- bits_per_char = BITS_PER_BYTE;
-
- /*
- * Make an unconditional cancel on the main sequencer to reset
- * it else we could end up in data loss scenarios.
- */
- qcom_geni_serial_poll_tx_done(uport);
- qcom_geni_serial_abort_rx(uport);
- geni_se_config_packing(&se, BITS_PER_BYTE, BYTES_PER_FIFO_WORD,
- false, true, true);
- geni_se_init(&se, DEF_FIFO_DEPTH_WORDS / 2, DEF_FIFO_DEPTH_WORDS - 2);
- geni_se_select_mode(&se, GENI_SE_FIFO);
-
- writel(tx_trans_cfg, uport->membase + SE_UART_TX_TRANS_CFG);
- writel(tx_parity_cfg, uport->membase + SE_UART_TX_PARITY_CFG);
- writel(rx_trans_cfg, uport->membase + SE_UART_RX_TRANS_CFG);
- writel(rx_parity_cfg, uport->membase + SE_UART_RX_PARITY_CFG);
- writel(bits_per_char, uport->membase + SE_UART_TX_WORD_LEN);
- writel(bits_per_char, uport->membase + SE_UART_RX_WORD_LEN);
- writel(stop_bit_len, uport->membase + SE_UART_TX_STOP_BIT_LEN);
-
- dev->con->write = qcom_geni_serial_earlycon_write;
- dev->con->setup = NULL;
- qcom_geni_serial_enable_early_read(&se, dev->con);
-
- return 0;
-}
-OF_EARLYCON_DECLARE(qcom_geni, "qcom,geni-debug-uart",
- qcom_geni_serial_earlycon_setup);
-
static int __init console_register(struct uart_driver *drv)
{
return uart_register_driver(drv);
Separate out earlycon support from serial driver and remove it's dependency on QUP wrapper driver. This enable us to manage earlycon independently and we can re-use the same earlycon driver for android project which currently uses downstream version of QUP drivers. Signed-off-by: Akash Asthana <akashast@codeaurora.org> --- drivers/tty/serial/Kconfig | 9 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/qcom_geni_earlycon.c | 649 ++++++++++++++++++++++++++++++++ drivers/tty/serial/qcom_geni_serial.c | 97 ----- 4 files changed, 659 insertions(+), 97 deletions(-) create mode 100644 drivers/tty/serial/qcom_geni_earlycon.c