Browse Source

serial: 8250_omap: Add DMA support for UARTs on AM654 SoC

Unlike, older OMAP SoCs, AM654 SoC has configurable RX timeout behavior
(controlled via EFR2) and better DMA integration. This allows to
transfer as larger amount data per DMA transfer compared to older SoC.
Add support for the same.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Signed-off-by: Sekhar Nori <nsekhar@ti.com>
Vignesh Raghavendra 6 years ago
parent
commit
2ece78ee86
1 changed files with 71 additions and 10 deletions
  1. 71 10
      drivers/tty/serial/8250/8250_omap.c

+ 71 - 10
drivers/tty/serial/8250/8250_omap.c

@@ -40,6 +40,7 @@
  * The same errata is applicable to AM335x and DRA7x processors too.
  * The same errata is applicable to AM335x and DRA7x processors too.
  */
  */
 #define UART_ERRATA_CLOCK_DISABLE	(1 << 3)
 #define UART_ERRATA_CLOCK_DISABLE	(1 << 3)
+#define	UART_HAS_EFR2			BIT(4)
 
 
 #define OMAP_UART_FCR_RX_TRIG		6
 #define OMAP_UART_FCR_RX_TRIG		6
 #define OMAP_UART_FCR_TX_TRIG		4
 #define OMAP_UART_FCR_TX_TRIG		4
@@ -93,6 +94,10 @@
 #define OMAP_UART_REV_52 0x0502
 #define OMAP_UART_REV_52 0x0502
 #define OMAP_UART_REV_63 0x0603
 #define OMAP_UART_REV_63 0x0603
 
 
+/* Enhanced features register 2 */
+#define UART_OMAP_EFR2			0x23
+#define UART_OMAP_EFR2_TIMEOUT_BEHAVE	BIT(6)
+
 struct omap8250_priv {
 struct omap8250_priv {
 	int line;
 	int line;
 	u8 habit;
 	u8 habit;
@@ -660,7 +665,7 @@ static int omap_8250_startup(struct uart_port *port)
 		priv->wer |= OMAP_UART_TX_WAKEUP_EN;
 		priv->wer |= OMAP_UART_TX_WAKEUP_EN;
 	serial_out(up, UART_OMAP_WER, priv->wer);
 	serial_out(up, UART_OMAP_WER, priv->wer);
 
 
-	if (up->dma)
+	if (up->dma && !(priv->habit & UART_HAS_EFR2))
 		up->dma->rx_dma(up);
 		up->dma->rx_dma(up);
 
 
 	pm_runtime_mark_last_busy(port->dev);
 	pm_runtime_mark_last_busy(port->dev);
@@ -685,6 +690,8 @@ static void omap_8250_shutdown(struct uart_port *port)
 	pm_runtime_get_sync(port->dev);
 	pm_runtime_get_sync(port->dev);
 
 
 	serial_out(up, UART_OMAP_WER, 0);
 	serial_out(up, UART_OMAP_WER, 0);
+	if (priv->habit & UART_HAS_EFR2)
+		serial_out(up, UART_OMAP_EFR2, 0x0);
 
 
 	up->ier = 0;
 	up->ier = 0;
 	serial_out(up, UART_IER, 0);
 	serial_out(up, UART_IER, 0);
@@ -830,7 +837,7 @@ static void __dma_rx_complete(void *param)
 		return;
 		return;
 	}
 	}
 	__dma_rx_do_complete(p);
 	__dma_rx_do_complete(p);
-	if (!priv->throttled)
+	if (!priv->throttled && !(priv->habit & UART_HAS_EFR2))
 		omap_8250_rx_dma(p);
 		omap_8250_rx_dma(p);
 
 
 	spin_unlock_irqrestore(&p->port.lock, flags);
 	spin_unlock_irqrestore(&p->port.lock, flags);
@@ -1055,6 +1062,49 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
 	return omap_8250_rx_dma(up);
 	return omap_8250_rx_dma(up);
 }
 }
 
 
+static unsigned char omap_8250_handle_rx_dma(struct uart_8250_port *up,
+					     u8 iir, unsigned char status)
+{
+	if ((status & (UART_LSR_DR | UART_LSR_BI)) &&
+	    (iir & UART_IIR_RDI)) {
+		if (handle_rx_dma(up, iir)) {
+			status = serial8250_rx_chars(up, status);
+			omap_8250_rx_dma(up);
+		}
+	}
+
+	return status;
+}
+
+static unsigned char am654_8250_handle_rx_dma(struct uart_8250_port *up,
+					      u8 iir, unsigned char status)
+{
+	if ((status & (UART_LSR_DR | UART_LSR_BI)) && (iir & UART_IIR_RDI)) {
+		omap_8250_rx_dma(up);
+		serial_out(up, UART_OMAP_EFR2, UART_OMAP_EFR2_TIMEOUT_BEHAVE);
+	} else if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) {
+		if (!up->dma->rx_running) {
+			omap_8250_rx_dma(up);
+		} else {
+			omap_8250_rx_dma_flush(up);
+			/*
+			 * Disable RX timeout, read IIR to clear
+			 * current timeout condition, clear EFR2 to
+			 * periodic timeouts, re-enable interrupts.
+			 */
+			up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
+			serial_out(up, UART_IER, up->ier);
+			omap_8250_rx_dma_flush(up);
+			serial_in(up, UART_IIR);
+			serial_out(up, UART_OMAP_EFR2, 0x0);
+			up->ier |= UART_IER_RLSI | UART_IER_RDI;
+			serial_out(up, UART_IER, up->ier);
+		}
+	}
+
+	return status;
+}
+
 /*
 /*
  * This is mostly serial8250_handle_irq(). We have a slightly different DMA
  * This is mostly serial8250_handle_irq(). We have a slightly different DMA
  * hoook for RX/TX and need different logic for them in the ISR. Therefore we
  * hoook for RX/TX and need different logic for them in the ISR. Therefore we
@@ -1063,6 +1113,7 @@ static bool handle_rx_dma(struct uart_8250_port *up, unsigned int iir)
 static int omap_8250_dma_handle_irq(struct uart_port *port)
 static int omap_8250_dma_handle_irq(struct uart_port *port)
 {
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 	struct uart_8250_port *up = up_to_u8250p(port);
+	struct omap8250_priv *priv = up->port.private_data;
 	unsigned char status;
 	unsigned char status;
 	unsigned long flags;
 	unsigned long flags;
 	u8 iir;
 	u8 iir;
@@ -1079,12 +1130,11 @@ static int omap_8250_dma_handle_irq(struct uart_port *port)
 
 
 	status = serial_port_in(port, UART_LSR);
 	status = serial_port_in(port, UART_LSR);
 
 
-	if (status & (UART_LSR_DR | UART_LSR_BI)) {
-		if (handle_rx_dma(up, iir)) {
-			status = serial8250_rx_chars(up, status);
-			omap_8250_rx_dma(up);
-		}
-	}
+	if (priv->habit & UART_HAS_EFR2)
+		status = am654_8250_handle_rx_dma(up, iir, status);
+	else
+		status = omap_8250_handle_rx_dma(up, iir, status);
+
 	serial8250_modem_status(up);
 	serial8250_modem_status(up);
 	if (status & UART_LSR_THRE && up->dma->tx_err) {
 	if (status & UART_LSR_THRE && up->dma->tx_err) {
 		if (uart_tx_stopped(&up->port) ||
 		if (uart_tx_stopped(&up->port) ||
@@ -1126,12 +1176,23 @@ static int omap8250_no_handle_irq(struct uart_port *port)
 	return 0;
 	return 0;
 }
 }
 
 
+static struct omap8250_dma_params am654_dma = {
+	.rx_size = SZ_2K,
+	.rx_trigger = 1,
+	.tx_trigger = TX_TRIGGER,
+};
+
 static struct omap8250_dma_params am33xx_dma = {
 static struct omap8250_dma_params am33xx_dma = {
 	.rx_size = RX_TRIGGER,
 	.rx_size = RX_TRIGGER,
 	.rx_trigger = RX_TRIGGER,
 	.rx_trigger = RX_TRIGGER,
 	.tx_trigger = TX_TRIGGER,
 	.tx_trigger = TX_TRIGGER,
 };
 };
 
 
+static struct omap8250_platdata am654_platdata = {
+	.dma_params	= &am654_dma,
+	.habit		= UART_HAS_EFR2,
+};
+
 static struct omap8250_platdata am33xx_platdata = {
 static struct omap8250_platdata am33xx_platdata = {
 	.dma_params	= &am33xx_dma,
 	.dma_params	= &am33xx_dma,
 	.habit		= OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE,
 	.habit		= OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE,
@@ -1143,7 +1204,7 @@ static struct omap8250_platdata omap4_platdata = {
 };
 };
 
 
 static const struct of_device_id omap8250_dt_ids[] = {
 static const struct of_device_id omap8250_dt_ids[] = {
-	{ .compatible = "ti,am654-uart" },
+	{ .compatible = "ti,am654-uart", .data = &am654_platdata, },
 	{ .compatible = "ti,omap2-uart" },
 	{ .compatible = "ti,omap2-uart" },
 	{ .compatible = "ti,omap3-uart" },
 	{ .compatible = "ti,omap3-uart" },
 	{ .compatible = "ti,omap4-uart", .data = &omap4_platdata, },
 	{ .compatible = "ti,omap4-uart", .data = &omap4_platdata, },
@@ -1501,7 +1562,7 @@ static int omap8250_runtime_resume(struct device *dev)
 	if (omap8250_lost_context(up))
 	if (omap8250_lost_context(up))
 		omap8250_restore_regs(up);
 		omap8250_restore_regs(up);
 
 
-	if (up->dma && up->dma->rxchan)
+	if (up->dma && up->dma->rxchan && !(priv->habit & UART_HAS_EFR2))
 		omap_8250_rx_dma(up);
 		omap_8250_rx_dma(up);
 
 
 	priv->latency = priv->calc_latency;
 	priv->latency = priv->calc_latency;