diff options
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 123 |
1 files changed, 122 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 | |||
72 | static void pl011_lockup_wa(unsigned long data); | ||
73 | static 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 | |||
90 | static u32 uart_wa_regdata[UART_WA_SAVE_NR]; | ||
91 | static 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 */ |
69 | struct vendor_data { | 94 | struct 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 | ||
123 | static struct uart_amba_port *amba_ports[UART_NR]; | ||
124 | |||
96 | /* Deals with DMA transactions */ | 125 | /* Deals with DMA transactions */ |
97 | 126 | ||
98 | struct pl011_sgbuf { | 127 | struct 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 | */ | ||
1054 | static 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 | |||
1011 | static void pl011_stop_tx(struct uart_port *port) | 1103 | static 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 | ||
1399 | static void | 1511 | static 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; |