|
@@ -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;
|