aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com>2011-06-13 04:11:33 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-06-16 15:01:57 -0400
commitc16d51a32bbb61ac8fd96f78b5ce2fccfe0fb4c3 (patch)
tree3899f24f4b1cc06e24055952f1d9114dc493caf0
parent57f2104f39995bac332ddc492fbf60aa28e0c35e (diff)
amba pl011: workaround for uart registers lockup
This workaround aims to break the deadlock situation which raises during continuous transfer of data for long duration over uart with hardware flow control. It is observed that CTS interrupt cannot be cleared in uart interrupt register (ICR). Hence further transfer over uart gets blocked. It is seen that during such deadlock condition ICR don't get cleared even on multiple write. This leads pass_counter to decrease and finally reach zero. This can be taken as trigger point to run this UART_BT_WA. Workaround backups the register configuration, does soft reset of UART using BIT-0 of PRCC_K_SOFTRST_SET/CLEAR registers and restores the registers. This patch also provides support for uart init and exit function calls if present. Signed-off-by: Shreshtha Kumar Sahu <shreshthakumar.sahu@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/tty/serial/amba-pl011.c123
-rw-r--r--include/linux/amba/serial.h3
2 files changed, 125 insertions, 1 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 8dc0541feecc..f5f6831b0a64 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -50,6 +50,7 @@
50#include <linux/dmaengine.h> 50#include <linux/dmaengine.h>
51#include <linux/dma-mapping.h> 51#include <linux/dma-mapping.h>
52#include <linux/scatterlist.h> 52#include <linux/scatterlist.h>
53#include <linux/delay.h>
53 54
54#include <asm/io.h> 55#include <asm/io.h>
55#include <asm/sizes.h> 56#include <asm/sizes.h>
@@ -65,6 +66,30 @@
65#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) 66#define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
66#define UART_DUMMY_DR_RX (1 << 16) 67#define UART_DUMMY_DR_RX (1 << 16)
67 68
69
70#define UART_WA_SAVE_NR 14
71
72static void pl011_lockup_wa(unsigned long data);
73static const u32 uart_wa_reg[UART_WA_SAVE_NR] = {
74 ST_UART011_DMAWM,
75 ST_UART011_TIMEOUT,
76 ST_UART011_LCRH_RX,
77 UART011_IBRD,
78 UART011_FBRD,
79 ST_UART011_LCRH_TX,
80 UART011_IFLS,
81 ST_UART011_XFCR,
82 ST_UART011_XON1,
83 ST_UART011_XON2,
84 ST_UART011_XOFF1,
85 ST_UART011_XOFF2,
86 UART011_CR,
87 UART011_IMSC
88};
89
90static u32 uart_wa_regdata[UART_WA_SAVE_NR];
91static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0);
92
68/* There is by now at least one vendor with differing details, so handle it */ 93/* There is by now at least one vendor with differing details, so handle it */
69struct vendor_data { 94struct vendor_data {
70 unsigned int ifls; 95 unsigned int ifls;
@@ -72,6 +97,7 @@ struct vendor_data {
72 unsigned int lcrh_tx; 97 unsigned int lcrh_tx;
73 unsigned int lcrh_rx; 98 unsigned int lcrh_rx;
74 bool oversampling; 99 bool oversampling;
100 bool interrupt_may_hang; /* vendor-specific */
75 bool dma_threshold; 101 bool dma_threshold;
76}; 102};
77 103
@@ -90,9 +116,12 @@ static struct vendor_data vendor_st = {
90 .lcrh_tx = ST_UART011_LCRH_TX, 116 .lcrh_tx = ST_UART011_LCRH_TX,
91 .lcrh_rx = ST_UART011_LCRH_RX, 117 .lcrh_rx = ST_UART011_LCRH_RX,
92 .oversampling = true, 118 .oversampling = true,
119 .interrupt_may_hang = true,
93 .dma_threshold = true, 120 .dma_threshold = true,
94}; 121};
95 122
123static struct uart_amba_port *amba_ports[UART_NR];
124
96/* Deals with DMA transactions */ 125/* Deals with DMA transactions */
97 126
98struct pl011_sgbuf { 127struct pl011_sgbuf {
@@ -132,6 +161,7 @@ struct uart_amba_port {
132 unsigned int lcrh_rx; /* vendor-specific */ 161 unsigned int lcrh_rx; /* vendor-specific */
133 bool autorts; 162 bool autorts;
134 char type[12]; 163 char type[12];
164 bool interrupt_may_hang; /* vendor-specific */
135#ifdef CONFIG_DMA_ENGINE 165#ifdef CONFIG_DMA_ENGINE
136 /* DMA stuff */ 166 /* DMA stuff */
137 bool using_tx_dma; 167 bool using_tx_dma;
@@ -1008,6 +1038,68 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
1008#endif 1038#endif
1009 1039
1010 1040
1041/*
1042 * pl011_lockup_wa
1043 * This workaround aims to break the deadlock situation
1044 * when after long transfer over uart in hardware flow
1045 * control, uart interrupt registers cannot be cleared.
1046 * Hence uart transfer gets blocked.
1047 *
1048 * It is seen that during such deadlock condition ICR
1049 * don't get cleared even on multiple write. This leads
1050 * pass_counter to decrease and finally reach zero. This
1051 * can be taken as trigger point to run this UART_BT_WA.
1052 *
1053 */
1054static void pl011_lockup_wa(unsigned long data)
1055{
1056 struct uart_amba_port *uap = amba_ports[0];
1057 void __iomem *base = uap->port.membase;
1058 struct circ_buf *xmit = &uap->port.state->xmit;
1059 struct tty_struct *tty = uap->port.state->port.tty;
1060 int buf_empty_retries = 200;
1061 int loop;
1062
1063 /* Stop HCI layer from submitting data for tx */
1064 tty->hw_stopped = 1;
1065 while (!uart_circ_empty(xmit)) {
1066 if (buf_empty_retries-- == 0)
1067 break;
1068 udelay(100);
1069 }
1070
1071 /* Backup registers */
1072 for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
1073 uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]);
1074
1075 /* Disable UART so that FIFO data is flushed out */
1076 writew(0x00, uap->port.membase + UART011_CR);
1077
1078 /* Soft reset UART module */
1079 if (uap->port.dev->platform_data) {
1080 struct amba_pl011_data *plat;
1081
1082 plat = uap->port.dev->platform_data;
1083 if (plat->reset)
1084 plat->reset();
1085 }
1086
1087 /* Restore registers */
1088 for (loop = 0; loop < UART_WA_SAVE_NR; loop++)
1089 writew(uart_wa_regdata[loop] ,
1090 uap->port.membase + uart_wa_reg[loop]);
1091
1092 /* Initialise the old status of the modem signals */
1093 uap->old_status = readw(uap->port.membase + UART01x_FR) &
1094 UART01x_FR_MODEM_ANY;
1095
1096 if (readl(base + UART011_MIS) & 0x2)
1097 printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n");
1098
1099 /* Start Tx/Rx */
1100 tty->hw_stopped = 0;
1101}
1102
1011static void pl011_stop_tx(struct uart_port *port) 1103static void pl011_stop_tx(struct uart_port *port)
1012{ 1104{
1013 struct uart_amba_port *uap = (struct uart_amba_port *)port; 1105 struct uart_amba_port *uap = (struct uart_amba_port *)port;
@@ -1158,8 +1250,11 @@ static irqreturn_t pl011_int(int irq, void *dev_id)
1158 if (status & UART011_TXIS) 1250 if (status & UART011_TXIS)
1159 pl011_tx_chars(uap); 1251 pl011_tx_chars(uap);
1160 1252
1161 if (pass_counter-- == 0) 1253 if (pass_counter-- == 0) {
1254 if (uap->interrupt_may_hang)
1255 tasklet_schedule(&pl011_lockup_tlet);
1162 break; 1256 break;
1257 }
1163 1258
1164 status = readw(uap->port.membase + UART011_MIS); 1259 status = readw(uap->port.membase + UART011_MIS);
1165 } while (status != 0); 1260 } while (status != 0);
@@ -1339,6 +1434,14 @@ static int pl011_startup(struct uart_port *port)
1339 writew(uap->im, uap->port.membase + UART011_IMSC); 1434 writew(uap->im, uap->port.membase + UART011_IMSC);
1340 spin_unlock_irq(&uap->port.lock); 1435 spin_unlock_irq(&uap->port.lock);
1341 1436
1437 if (uap->port.dev->platform_data) {
1438 struct amba_pl011_data *plat;
1439
1440 plat = uap->port.dev->platform_data;
1441 if (plat->init)
1442 plat->init();
1443 }
1444
1342 return 0; 1445 return 0;
1343 1446
1344 clk_dis: 1447 clk_dis:
@@ -1394,6 +1497,15 @@ static void pl011_shutdown(struct uart_port *port)
1394 * Shut down the clock producer 1497 * Shut down the clock producer
1395 */ 1498 */
1396 clk_disable(uap->clk); 1499 clk_disable(uap->clk);
1500
1501 if (uap->port.dev->platform_data) {
1502 struct amba_pl011_data *plat;
1503
1504 plat = uap->port.dev->platform_data;
1505 if (plat->exit)
1506 plat->exit();
1507 }
1508
1397} 1509}
1398 1510
1399static void 1511static void
@@ -1700,6 +1812,14 @@ static int __init pl011_console_setup(struct console *co, char *options)
1700 if (!uap) 1812 if (!uap)
1701 return -ENODEV; 1813 return -ENODEV;
1702 1814
1815 if (uap->port.dev->platform_data) {
1816 struct amba_pl011_data *plat;
1817
1818 plat = uap->port.dev->platform_data;
1819 if (plat->init)
1820 plat->init();
1821 }
1822
1703 uap->port.uartclk = clk_get_rate(uap->clk); 1823 uap->port.uartclk = clk_get_rate(uap->clk);
1704 1824
1705 if (options) 1825 if (options)
@@ -1774,6 +1894,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
1774 uap->lcrh_rx = vendor->lcrh_rx; 1894 uap->lcrh_rx = vendor->lcrh_rx;
1775 uap->lcrh_tx = vendor->lcrh_tx; 1895 uap->lcrh_tx = vendor->lcrh_tx;
1776 uap->fifosize = vendor->fifosize; 1896 uap->fifosize = vendor->fifosize;
1897 uap->interrupt_may_hang = vendor->interrupt_may_hang;
1777 uap->port.dev = &dev->dev; 1898 uap->port.dev = &dev->dev;
1778 uap->port.mapbase = dev->res.start; 1899 uap->port.mapbase = dev->res.start;
1779 uap->port.membase = base; 1900 uap->port.membase = base;
diff --git a/include/linux/amba/serial.h b/include/linux/amba/serial.h
index 5479fdc849e9..514ed45c462e 100644
--- a/include/linux/amba/serial.h
+++ b/include/linux/amba/serial.h
@@ -201,6 +201,9 @@ struct amba_pl011_data {
201 bool (*dma_filter)(struct dma_chan *chan, void *filter_param); 201 bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
202 void *dma_rx_param; 202 void *dma_rx_param;
203 void *dma_tx_param; 203 void *dma_tx_param;
204 void (*init) (void);
205 void (*exit) (void);
206 void (*reset) (void);
204}; 207};
205#endif 208#endif
206 209