Add MSM_GENI_SERIAL_CABLE_DETECT Kconfig option and detect a
persistent RX-line BREAK condition (no S_GP_IRQ_3 after
S_GP_IRQ_2) to identify a disconnected debug UART cable, and
suppress TX output in that case to avoid driving an
unterminated line.

Signed-off-by: Aswin Murugan <[email protected]>
---
 drivers/serial/Kconfig           |  9 +++++++
 drivers/serial/serial_msm_geni.c | 41 ++++++++++++++++++++++++++++++++
 2 files changed, 50 insertions(+)

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index ffc58d663ff..326088c7027 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -1020,6 +1020,15 @@ config MSM_GENI_SERIAL
          Driver works in FIFO mode.
          Multiple baudrates supported.
 
+config MSM_GENI_SERIAL_CABLE_DETECT
+       bool "Disable GENI UART TX when no cable/console is connected"
+       depends on MSM_GENI_SERIAL
+       default n
+       help
+         Detect a UART RX-line BREAK condition, which indicates that no
+         cable is connected to the debug UART port, and suppress UART TX
+         output.
+
 config MXS_AUART_SERIAL
        bool "MXS AUART"
        depends on DM_SERIAL
diff --git a/drivers/serial/serial_msm_geni.c b/drivers/serial/serial_msm_geni.c
index ff91e0018fa..8bf5f6eea26 100644
--- a/drivers/serial/serial_msm_geni.c
+++ b/drivers/serial/serial_msm_geni.c
@@ -98,6 +98,7 @@ struct msm_serial_data {
        u32 baud;
        u32 oversampling;
        struct clk *se;
+       bool cable_connected;
 };
 
 unsigned long root_freq[] = {7372800,  14745600, 19200000, 29491200,
@@ -298,6 +299,28 @@ static u32 qcom_geni_serial_tx_empty(u64 base)
        return !readl(base + SE_GENI_TX_FIFO_STATUS);
 }
 
+/**
+ * qcom_geni_serial_is_cable_connected() - Check UART RX-line BREAK condition.
+ * @base:      Pointer to the concerned serial engine.
+ *
+ * A serial cable that is not connected leaves the RX line in a state that
+ * the UART hardware reports as a BREAK condition that never ends, i.e.
+ * S_GP_IRQ_2 (RX_BREAK_START) gets set without a following S_GP_IRQ_3
+ * (RX_BREAK_END). This function checks the secondary sequencer IRQ status
+ * for that pattern.
+ *
+ * Return: false, if no cable is connected, true otherwise.
+ */
+static bool qcom_geni_serial_is_cable_connected(u64 base)
+{
+       u32 s_irq_status = readl(base + SE_GENI_S_IRQ_STATUS);
+
+       if ((s_irq_status & S_GP_IRQ_2_EN) && !(s_irq_status & S_GP_IRQ_3_EN))
+               return false;
+
+       return true;
+}
+
 /**
  * geni_se_setup_s_cmd() - Setup the secondary sequencer
  * @se:                Pointer to the concerned serial engine.
@@ -384,6 +407,10 @@ static int msm_serial_putc(struct udevice *dev, const char 
ch)
 {
        struct msm_serial_data *priv = dev_get_priv(dev);
 
+       if (CONFIG_IS_ENABLED(MSM_GENI_SERIAL_CABLE_DETECT) &&
+           !priv->cable_connected)
+               return 0;
+
        writel(DEF_TX_WM, priv->base + SE_GENI_TX_WATERMARK_REG);
        qcom_geni_serial_setup_tx(priv->base, 1);
 
@@ -565,6 +592,11 @@ static int msm_serial_probe(struct udevice *dev)
        qcom_geni_serial_start_rx(dev);
        qcom_geni_serial_start_tx(priv->base);
 
+       if (CONFIG_IS_ENABLED(MSM_GENI_SERIAL_CABLE_DETECT))
+               priv->cable_connected = 
qcom_geni_serial_is_cable_connected(priv->base);
+       else
+               priv->cable_connected = true;
+
        return 0;
 }
 
@@ -615,6 +647,8 @@ static struct udevice init_dev = {
 #error Clocks cannot be set at early debug. Change CONFIG_BAUDRATE
 #endif
 
+static bool debug_uart_cable_connected = true;
+
 static inline void _debug_uart_init(void)
 {
        phys_addr_t base = CONFIG_VAL(DEBUG_UART_BASE);
@@ -623,12 +657,19 @@ static inline void _debug_uart_init(void)
        writel(DEF_RX_WM, base + SE_GENI_RX_WATERMARK_REG);
        geni_serial_baud(base, CLK_DIV, CONFIG_BAUDRATE);
        qcom_geni_serial_start_tx(base);
+
+       if (CONFIG_IS_ENABLED(MSM_GENI_SERIAL_CABLE_DETECT))
+               debug_uart_cable_connected = 
qcom_geni_serial_is_cable_connected(base);
 }
 
 static inline void _debug_uart_putc(int ch)
 {
        phys_addr_t base = CONFIG_VAL(DEBUG_UART_BASE);
 
+       if (CONFIG_IS_ENABLED(MSM_GENI_SERIAL_CABLE_DETECT) &&
+           !debug_uart_cable_connected)
+               return;
+
        writel(DEF_TX_WM, base + SE_GENI_TX_WATERMARK_REG);
        qcom_geni_serial_setup_tx(base, 1);
        qcom_geni_serial_poll_bit(&init_dev, SE_GENI_M_IRQ_STATUS,
-- 
2.34.1

Reply via email to