[PATCH] serial: 8250: 8250_omap: Disable RX interrupt after DMA enable

From: Vignesh Raghavendra
Date: Thu Oct 29 2020 - 07:54:23 EST


UARTs on TI SoCs prior to J7200 don't provide independent control over
RX FIFO not empty interrupt (RHR_IT) and RX timeout interrupt.
Starting with J7200 SoC, its possible to disable RHR_IT independent of
RX timeout interrupt using bit 2 of IER2 register. So disable RHR_IT
once RX DMA is started so as to avoid spurious interrupt being raised
when data is in the RX FIFO but is yet to be drained by DMA (a known
errata in older SoCs).

Signed-off-by: Vignesh Raghavendra <vigneshr@xxxxxx>
---
drivers/tty/serial/8250/8250_omap.c | 42 ++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 562087df7d33..084e7bc51f0e 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -27,6 +27,7 @@
#include <linux/pm_qos.h>
#include <linux/pm_wakeirq.h>
#include <linux/dma-mapping.h>
+#include <linux/sys_soc.h>

#include "8250.h"

@@ -41,6 +42,7 @@
*/
#define UART_ERRATA_CLOCK_DISABLE (1 << 3)
#define UART_HAS_EFR2 BIT(4)
+#define UART_HAS_RHR_IT_DIS BIT(5)

#define OMAP_UART_FCR_RX_TRIG 6
#define OMAP_UART_FCR_TX_TRIG 4
@@ -94,6 +96,10 @@
#define OMAP_UART_REV_52 0x0502
#define OMAP_UART_REV_63 0x0603

+/* Interrupt Enable Register 2 */
+#define UART_OMAP_IER2 0x1B
+#define UART_OMAP_IER2_RHR_IT_DIS BIT(2)
+
/* Enhanced features register 2 */
#define UART_OMAP_EFR2 0x23
#define UART_OMAP_EFR2_TIMEOUT_BEHAVE BIT(6)
@@ -761,17 +767,27 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
{
struct uart_8250_dma *dma = p->dma;
struct tty_port *tty_port = &p->port.state->port;
+ struct omap8250_priv *priv = p->port.private_data;
struct dma_chan *rxchan = dma->rxchan;
dma_cookie_t cookie;
struct dma_tx_state state;
int count;
int ret;
+ u32 reg;

if (!dma->rx_running)
goto out;

cookie = dma->rx_cookie;
dma->rx_running = 0;
+
+ /* Re-enable RX FIFO interrupt now that transfer is complete */
+ if (priv->habit & UART_HAS_RHR_IT_DIS) {
+ reg = serial_in(p, UART_OMAP_IER2);
+ reg &= ~UART_OMAP_IER2_RHR_IT_DIS;
+ serial_out(p, UART_OMAP_IER2, UART_OMAP_IER2_RHR_IT_DIS);
+ }
+
dmaengine_tx_status(rxchan, cookie, &state);

count = dma->rx_size - state.residue + state.in_flight_bytes;
@@ -867,6 +883,7 @@ static int omap_8250_rx_dma(struct uart_8250_port *p)
int err = 0;
struct dma_async_tx_descriptor *desc;
unsigned long flags;
+ u32 reg;

if (priv->rx_dma_broken)
return -EINVAL;
@@ -902,6 +919,17 @@ static int omap_8250_rx_dma(struct uart_8250_port *p)

dma->rx_cookie = dmaengine_submit(desc);

+ /*
+ * Disable RX FIFO interrupt while RX DMA is enabled, else
+ * spurious interrupt may be raised when data is in the RX FIFO
+ * but is yet to be drained by DMA.
+ */
+ if (priv->habit & UART_HAS_RHR_IT_DIS) {
+ reg = serial_in(p, UART_OMAP_IER2);
+ reg |= UART_OMAP_IER2_RHR_IT_DIS;
+ serial_out(p, UART_OMAP_IER2, UART_OMAP_IER2_RHR_IT_DIS);
+ }
+
dma_async_issue_pending(dma->rxchan);
out:
spin_unlock_irqrestore(&priv->rx_dma_lock, flags);
@@ -1168,6 +1196,11 @@ static int omap8250_no_handle_irq(struct uart_port *port)
return 0;
}

+static const struct soc_device_attribute k3_soc_devices[] = {
+ { .family = "AM65X", },
+ { .family = "J721E", .revision = "SR1.0" },
+};
+
static struct omap8250_dma_params am654_dma = {
.rx_size = SZ_2K,
.rx_trigger = 1,
@@ -1182,7 +1215,7 @@ static struct omap8250_dma_params am33xx_dma = {

static struct omap8250_platdata am654_platdata = {
.dma_params = &am654_dma,
- .habit = UART_HAS_EFR2,
+ .habit = UART_HAS_EFR2 | UART_HAS_RHR_IT_DIS,
};

static struct omap8250_platdata am33xx_platdata = {
@@ -1372,6 +1405,13 @@ static int omap8250_probe(struct platform_device *pdev)
up.dma->rxconf.src_maxburst = RX_TRIGGER;
up.dma->txconf.dst_maxburst = TX_TRIGGER;
}
+
+ /*
+ * AM65x SR1.0, AM65x SR2.0 and J721e SR1.0 don't
+ * don't have RHR_IT_DIS bit in IER2 register
+ */
+ if (soc_device_match(k3_soc_devices))
+ priv->habit &= ~UART_HAS_RHR_IT_DIS;
}
#endif
ret = serial8250_register_8250_port(&up);
--
2.29.0