|
@@ -33,6 +33,11 @@
|
|
|
#define UART_ERRATA_i202_MDR1_ACCESS (1 << 0)
|
|
|
#define OMAP_UART_WER_HAS_TX_WAKEUP (1 << 1)
|
|
|
#define OMAP_DMA_TX_KICK (1 << 2)
|
|
|
+/*
|
|
|
+ * See Advisory 21 in AM437x errata SPRZ408B, updated April 2015.
|
|
|
+ * The same errata is applicable to AM335x and DRA7x processors too.
|
|
|
+ */
|
|
|
+#define UART_ERRATA_CLOCK_DISABLE (1 << 3)
|
|
|
|
|
|
#define OMAP_UART_FCR_RX_TRIG 6
|
|
|
#define OMAP_UART_FCR_TX_TRIG 4
|
|
@@ -54,6 +59,12 @@
|
|
|
#define OMAP_UART_MVR_MAJ_SHIFT 8
|
|
|
#define OMAP_UART_MVR_MIN_MASK 0x3f
|
|
|
|
|
|
+/* SYSC register bitmasks */
|
|
|
+#define OMAP_UART_SYSC_SOFTRESET (1 << 1)
|
|
|
+
|
|
|
+/* SYSS register bitmasks */
|
|
|
+#define OMAP_UART_SYSS_RESETDONE (1 << 0)
|
|
|
+
|
|
|
#define UART_TI752_TLR_TX 0
|
|
|
#define UART_TI752_TLR_RX 4
|
|
|
|
|
@@ -1062,13 +1073,15 @@ static int omap8250_no_handle_irq(struct uart_port *port)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static const u8 am3352_habit = OMAP_DMA_TX_KICK;
|
|
|
+static const u8 am3352_habit = OMAP_DMA_TX_KICK | UART_ERRATA_CLOCK_DISABLE;
|
|
|
+static const u8 am4372_habit = UART_ERRATA_CLOCK_DISABLE;
|
|
|
|
|
|
static const struct of_device_id omap8250_dt_ids[] = {
|
|
|
{ .compatible = "ti,omap2-uart" },
|
|
|
{ .compatible = "ti,omap3-uart" },
|
|
|
{ .compatible = "ti,omap4-uart" },
|
|
|
{ .compatible = "ti,am3352-uart", .data = &am3352_habit, },
|
|
|
+ { .compatible = "ti,am4372-uart", .data = &am4372_habit, },
|
|
|
{},
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(of, omap8250_dt_ids);
|
|
@@ -1282,17 +1295,46 @@ static int omap8250_lost_context(struct uart_8250_port *up)
|
|
|
{
|
|
|
u32 val;
|
|
|
|
|
|
- val = serial_in(up, UART_OMAP_MDR1);
|
|
|
+ val = serial_in(up, UART_OMAP_SCR);
|
|
|
/*
|
|
|
- * If we lose context, then MDR1 is set to its reset value which is
|
|
|
- * UART_OMAP_MDR1_DISABLE. After set_termios() we set it either to 13x
|
|
|
- * or 16x but never to disable again.
|
|
|
+ * If we lose context, then SCR is set to its reset value of zero.
|
|
|
+ * After set_termios() we set bit 3 of SCR (TX_EMPTY_CTL_IT) to 1,
|
|
|
+ * among other bits, to never set the register back to zero again.
|
|
|
*/
|
|
|
- if (val == UART_OMAP_MDR1_DISABLE)
|
|
|
+ if (!val)
|
|
|
return 1;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+/* TODO: in future, this should happen via API in drivers/reset/ */
|
|
|
+static int omap8250_soft_reset(struct device *dev)
|
|
|
+{
|
|
|
+ struct omap8250_priv *priv = dev_get_drvdata(dev);
|
|
|
+ struct uart_8250_port *up = serial8250_get_port(priv->line);
|
|
|
+ int timeout = 100;
|
|
|
+ int sysc;
|
|
|
+ int syss;
|
|
|
+
|
|
|
+ sysc = serial_in(up, UART_OMAP_SYSC);
|
|
|
+
|
|
|
+ /* softreset the UART */
|
|
|
+ sysc |= OMAP_UART_SYSC_SOFTRESET;
|
|
|
+ serial_out(up, UART_OMAP_SYSC, sysc);
|
|
|
+
|
|
|
+ /* By experiments, 1us enough for reset complete on AM335x */
|
|
|
+ do {
|
|
|
+ udelay(1);
|
|
|
+ syss = serial_in(up, UART_OMAP_SYSS);
|
|
|
+ } while (--timeout && !(syss & OMAP_UART_SYSS_RESETDONE));
|
|
|
+
|
|
|
+ if (!timeout) {
|
|
|
+ dev_err(dev, "timed out waiting for reset done\n");
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int omap8250_runtime_suspend(struct device *dev)
|
|
|
{
|
|
|
struct omap8250_priv *priv = dev_get_drvdata(dev);
|
|
@@ -1310,6 +1352,17 @@ static int omap8250_runtime_suspend(struct device *dev)
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
|
|
|
+ if (priv->habit & UART_ERRATA_CLOCK_DISABLE) {
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = omap8250_soft_reset(dev);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* Restore to UART mode after reset (for wakeup) */
|
|
|
+ omap8250_update_mdr1(up, priv);
|
|
|
+ }
|
|
|
+
|
|
|
if (up->dma && up->dma->rxchan)
|
|
|
omap_8250_rx_dma(up, UART_IIR_RX_TIMEOUT);
|
|
|
|