Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

serial: pl011: implement workaround for CTS clear event issue

Problem Observed:
- interrupt status is set by rising or falling edge on CTS line
- interrupt status is cleared on a .0. to .1. transition of the
interrupt-clear register bit 1.
- interrupt-clear register is reset by hardware once the interrupt
status is .0..
Remark: It seems not possible to read this register back by the
CPU though, but internally this register exists.
- when simultaneous set and reset event on the interrupt status
happens, then the set-event has priority and the status remains
.1.. As a result the interrupt-clear register is not reset to
.0., and no new .0. to .1. transition can be detected on it when
writing a .1. to it.
This implies race condition, the clear must be performed at least
one UARTCLK the riding edge of CTS RIS interrupt.

Fix:
Instead of resetting UART as done in commit
c16d51a32bbb61ac8fd96f78b5ce2fccfe0fb4c3
"amba pl011: workaround for uart registers lockup" do the
following:

write .0. and then .1. to the interrupt-clear register to make
sure that this transition is detected. According to the datasheet
writing a .0. does not have any effect, but actually it allows to
reset the internal interrupt-clear register.

Take into account:
The .0. needs to last at least for one clk_uart clock period
(~ 38 MHz, 26.08ns)

This way we can do away with the tasklet and keep only a tiny
fix triggered by the variant flag introduced in this patch.

Signed-off-by: Guillaume Jaunet <guillaume.jaunet@stericsson.com>
Signed-off-by: Christophe Arnal <christophe.arnal@stericsson.com>
Signed-off-by: Matthias Locher <Matthias.Locher@stericsson.com>
Signed-off-by: Rajanikanth H.V <rajanikanth.hv@stericsson.com>
Reviewed-by: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Rajanikanth H.V and committed by
Greg Kroah-Hartman
4fd0690b e695b286

+18 -91
+18 -91
drivers/tty/serial/amba-pl011.c
··· 67 67 #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) 68 68 #define UART_DUMMY_DR_RX (1 << 16) 69 69 70 - 71 - #define UART_WA_SAVE_NR 14 72 - 73 - static void pl011_lockup_wa(unsigned long data); 74 - static const u32 uart_wa_reg[UART_WA_SAVE_NR] = { 75 - ST_UART011_DMAWM, 76 - ST_UART011_TIMEOUT, 77 - ST_UART011_LCRH_RX, 78 - UART011_IBRD, 79 - UART011_FBRD, 80 - ST_UART011_LCRH_TX, 81 - UART011_IFLS, 82 - ST_UART011_XFCR, 83 - ST_UART011_XON1, 84 - ST_UART011_XON2, 85 - ST_UART011_XOFF1, 86 - ST_UART011_XOFF2, 87 - UART011_CR, 88 - UART011_IMSC 89 - }; 90 - 91 - static u32 uart_wa_regdata[UART_WA_SAVE_NR]; 92 - static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0); 93 - 94 70 /* There is by now at least one vendor with differing details, so handle it */ 95 71 struct vendor_data { 96 72 unsigned int ifls; ··· 76 100 bool oversampling; 77 101 bool interrupt_may_hang; /* vendor-specific */ 78 102 bool dma_threshold; 103 + bool cts_event_workaround; 79 104 }; 80 105 81 106 static struct vendor_data vendor_arm = { ··· 86 109 .lcrh_rx = UART011_LCRH, 87 110 .oversampling = false, 88 111 .dma_threshold = false, 112 + .cts_event_workaround = false, 89 113 }; 90 114 91 115 static struct vendor_data vendor_st = { ··· 97 119 .oversampling = true, 98 120 .interrupt_may_hang = true, 99 121 .dma_threshold = true, 122 + .cts_event_workaround = true, 100 123 }; 101 124 102 125 static struct uart_amba_port *amba_ports[UART_NR]; ··· 1033 1054 #define pl011_dma_flush_buffer NULL 1034 1055 #endif 1035 1056 1036 - 1037 - /* 1038 - * pl011_lockup_wa 1039 - * This workaround aims to break the deadlock situation 1040 - * when after long transfer over uart in hardware flow 1041 - * control, uart interrupt registers cannot be cleared. 1042 - * Hence uart transfer gets blocked. 1043 - * 1044 - * It is seen that during such deadlock condition ICR 1045 - * don't get cleared even on multiple write. This leads 1046 - * pass_counter to decrease and finally reach zero. This 1047 - * can be taken as trigger point to run this UART_BT_WA. 1048 - * 1049 - */ 1050 - static void pl011_lockup_wa(unsigned long data) 1051 - { 1052 - struct uart_amba_port *uap = amba_ports[0]; 1053 - void __iomem *base = uap->port.membase; 1054 - struct circ_buf *xmit = &uap->port.state->xmit; 1055 - struct tty_struct *tty = uap->port.state->port.tty; 1056 - int buf_empty_retries = 200; 1057 - int loop; 1058 - 1059 - /* Stop HCI layer from submitting data for tx */ 1060 - tty->hw_stopped = 1; 1061 - while (!uart_circ_empty(xmit)) { 1062 - if (buf_empty_retries-- == 0) 1063 - break; 1064 - udelay(100); 1065 - } 1066 - 1067 - /* Backup registers */ 1068 - for (loop = 0; loop < UART_WA_SAVE_NR; loop++) 1069 - uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]); 1070 - 1071 - /* Disable UART so that FIFO data is flushed out */ 1072 - writew(0x00, uap->port.membase + UART011_CR); 1073 - 1074 - /* Soft reset UART module */ 1075 - if (uap->port.dev->platform_data) { 1076 - struct amba_pl011_data *plat; 1077 - 1078 - plat = uap->port.dev->platform_data; 1079 - if (plat->reset) 1080 - plat->reset(); 1081 - } 1082 - 1083 - /* Restore registers */ 1084 - for (loop = 0; loop < UART_WA_SAVE_NR; loop++) 1085 - writew(uart_wa_regdata[loop] , 1086 - uap->port.membase + uart_wa_reg[loop]); 1087 - 1088 - /* Initialise the old status of the modem signals */ 1089 - uap->old_status = readw(uap->port.membase + UART01x_FR) & 1090 - UART01x_FR_MODEM_ANY; 1091 - 1092 - if (readl(base + UART011_MIS) & 0x2) 1093 - printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n"); 1094 - 1095 - /* Start Tx/Rx */ 1096 - tty->hw_stopped = 0; 1097 - } 1098 - 1099 1057 static void pl011_stop_tx(struct uart_port *port) 1100 1058 { 1101 1059 struct uart_amba_port *uap = (struct uart_amba_port *)port; ··· 1161 1245 unsigned long flags; 1162 1246 unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; 1163 1247 int handled = 0; 1248 + unsigned int dummy_read; 1164 1249 1165 1250 spin_lock_irqsave(&uap->port.lock, flags); 1166 1251 1167 1252 status = readw(uap->port.membase + UART011_MIS); 1168 1253 if (status) { 1169 1254 do { 1255 + if (uap->vendor->cts_event_workaround) { 1256 + /* workaround to make sure that all bits are unlocked.. */ 1257 + writew(0x00, uap->port.membase + UART011_ICR); 1258 + 1259 + /* 1260 + * WA: introduce 26ns(1 uart clk) delay before W1C; 1261 + * single apb access will incur 2 pclk(133.12Mhz) delay, 1262 + * so add 2 dummy reads 1263 + */ 1264 + dummy_read = readw(uap->port.membase + UART011_ICR); 1265 + dummy_read = readw(uap->port.membase + UART011_ICR); 1266 + } 1267 + 1170 1268 writew(status & ~(UART011_TXIS|UART011_RTIS| 1171 1269 UART011_RXIS), 1172 1270 uap->port.membase + UART011_ICR); ··· 1197 1267 if (status & UART011_TXIS) 1198 1268 pl011_tx_chars(uap); 1199 1269 1200 - if (pass_counter-- == 0) { 1201 - if (uap->interrupt_may_hang) 1202 - tasklet_schedule(&pl011_lockup_tlet); 1270 + if (pass_counter-- == 0) 1203 1271 break; 1204 - } 1205 1272 1206 1273 status = readw(uap->port.membase + UART011_MIS); 1207 1274 } while (status != 0);