aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial
diff options
context:
space:
mode:
authorAnirudha Sarangi <anirudha.sarangi@xilinx.com>2016-09-22 11:58:14 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-27 06:54:41 -0400
commitc8dbdc842d30618e4f7e315e3b0e6c43de7915f3 (patch)
tree7443792c2619eb219f720948ca3c45e64ac5d88d /drivers/tty/serial
parent8e5481d98bbf1de0ff06a3e488b668572d578e61 (diff)
serial: xuartps: Rewrite the interrupt handling logic
The existing interrupt handling logic has following issues. - Upon a parity error with default configuration, the control never comes out of the ISR thereby hanging Linux. - The error handling logic around framing and parity error are buggy. There are chances that the errors will never be captured. This patch ensures that the status registers are cleared on all cases so that a hang situation never arises. Signed-off-by: Anirudha Sarangi <anirudh@xilinx.com> Signed-off-by: Michal Simek <michal.simek@xilinx.com> [stelford@cadence.com: cherry picked from https://github.com/Xilinx/linux-xlnx commit ac297e20d399850d7a8e373b6eccf2e183c15165 with manual conflict resolution] Signed-off-by: Scott Telford <stelford@cadence.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial')
-rw-r--r--drivers/tty/serial/xilinx_uartps.c219
1 files changed, 114 insertions, 105 deletions
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index 98f7b590ec55..511999755f5c 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -198,58 +198,43 @@ struct cdns_platform_data {
198#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \ 198#define to_cdns_uart(_nb) container_of(_nb, struct cdns_uart, \
199 clk_rate_change_nb); 199 clk_rate_change_nb);
200 200
201static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus) 201/**
202 * cdns_uart_handle_rx - Handle the received bytes along with Rx errors.
203 * @dev_id: Id of the UART port
204 * @isrstatus: The interrupt status register value as read
205 * Return: None
206 */
207static void cdns_uart_handle_rx(void *dev_id, unsigned int isrstatus)
202{ 208{
209 struct uart_port *port = (struct uart_port *)dev_id;
203 struct cdns_uart *cdns_uart = port->private_data; 210 struct cdns_uart *cdns_uart = port->private_data;
204 bool is_rxbs_support; 211 unsigned int data;
205 unsigned int rxbs_status = 0; 212 unsigned int rxbs_status = 0;
206 unsigned int status_mask; 213 unsigned int status_mask;
214 unsigned int framerrprocessed = 0;
215 char status = TTY_NORMAL;
216 bool is_rxbs_support;
207 217
208 is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT; 218 is_rxbs_support = cdns_uart->quirks & CDNS_UART_RXBS_SUPPORT;
209 219
210 /* 220 while ((readl(port->membase + CDNS_UART_SR) &
211 * There is no hardware break detection, so we interpret framing 221 CDNS_UART_SR_RXEMPTY) != CDNS_UART_SR_RXEMPTY) {
212 * error with all-zeros data as a break sequence. Most of the time,
213 * there's another non-zero byte at the end of the sequence.
214 */
215 if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) {
216 while (!(readl(port->membase + CDNS_UART_SR) &
217 CDNS_UART_SR_RXEMPTY)) {
218 if (!readl(port->membase + CDNS_UART_FIFO)) {
219 port->read_status_mask |= CDNS_UART_IXR_BRK;
220 isrstatus &= ~CDNS_UART_IXR_FRAMING;
221 }
222 }
223 writel(CDNS_UART_IXR_FRAMING, port->membase + CDNS_UART_ISR);
224 }
225
226 /* drop byte with parity error if IGNPAR specified */
227 if (isrstatus & port->ignore_status_mask & CDNS_UART_IXR_PARITY)
228 isrstatus &= ~(CDNS_UART_IXR_RXTRIG | CDNS_UART_IXR_TOUT);
229
230 isrstatus &= port->read_status_mask;
231 isrstatus &= ~port->ignore_status_mask;
232 status_mask = port->read_status_mask;
233 status_mask &= ~port->ignore_status_mask;
234
235 if (!(isrstatus & (CDNS_UART_IXR_TOUT | CDNS_UART_IXR_RXTRIG)))
236 return;
237
238 while (!(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_RXEMPTY)) {
239 u32 data;
240 char status = TTY_NORMAL;
241
242 if (is_rxbs_support) 222 if (is_rxbs_support)
243 rxbs_status = readl(port->membase + CDNS_UART_RXBS); 223 rxbs_status = readl(port->membase + CDNS_UART_RXBS);
244
245 data = readl(port->membase + CDNS_UART_FIFO); 224 data = readl(port->membase + CDNS_UART_FIFO);
246 225 port->icount.rx++;
247 /* Non-NULL byte after BREAK is garbage (99%) */ 226 /*
248 if (data && (port->read_status_mask & CDNS_UART_IXR_BRK)) { 227 * There is no hardware break detection in Zynq, so we interpret
249 port->read_status_mask &= ~CDNS_UART_IXR_BRK; 228 * framing error with all-zeros data as a break sequence.
250 port->icount.brk++; 229 * Most of the time, there's another non-zero byte at the
251 if (uart_handle_break(port)) 230 * end of the sequence.
231 */
232 if (!is_rxbs_support && (isrstatus & CDNS_UART_IXR_FRAMING)) {
233 if (!data) {
234 port->read_status_mask |= CDNS_UART_IXR_BRK;
235 framerrprocessed = 1;
252 continue; 236 continue;
237 }
253 } 238 }
254 if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) { 239 if (is_rxbs_support && (rxbs_status & CDNS_UART_RXBS_BRK)) {
255 port->icount.brk++; 240 port->icount.brk++;
@@ -258,74 +243,101 @@ static void cdns_uart_handle_rx(struct uart_port *port, unsigned int isrstatus)
258 continue; 243 continue;
259 } 244 }
260 245
261 if (uart_handle_sysrq_char(port, data)) 246 isrstatus &= port->read_status_mask;
262 continue; 247 isrstatus &= ~port->ignore_status_mask;
248 status_mask = port->read_status_mask;
249 status_mask &= ~port->ignore_status_mask;
250
251 if ((isrstatus & CDNS_UART_IXR_TOUT) ||
252 (isrstatus & CDNS_UART_IXR_RXTRIG)) {
253 if (data &&
254 (port->read_status_mask & CDNS_UART_IXR_BRK)) {
255 port->read_status_mask &= ~CDNS_UART_IXR_BRK;
256 port->icount.brk++;
257 if (uart_handle_break(port))
258 continue;
259 }
263 260
264 port->icount.rx++; 261 if (uart_handle_sysrq_char(port, data))
262 continue;
265 263
266 if (is_rxbs_support) { 264 if (is_rxbs_support) {
267 if ((rxbs_status & CDNS_UART_RXBS_PARITY) 265 if ((rxbs_status & CDNS_UART_RXBS_PARITY)
268 && (status_mask & CDNS_UART_IXR_PARITY)) { 266 && (status_mask & CDNS_UART_IXR_PARITY)) {
269 port->icount.parity++; 267 port->icount.parity++;
270 status = TTY_PARITY; 268 status = TTY_PARITY;
269 }
270 if ((rxbs_status & CDNS_UART_RXBS_FRAMING)
271 && (status_mask & CDNS_UART_IXR_PARITY)) {
272 port->icount.frame++;
273 status = TTY_FRAME;
274 }
275 } else {
276 if (isrstatus & CDNS_UART_IXR_PARITY) {
277 port->icount.parity++;
278 status = TTY_PARITY;
279 }
280 if ((isrstatus & CDNS_UART_IXR_FRAMING) &&
281 !framerrprocessed) {
282 port->icount.frame++;
283 status = TTY_FRAME;
284 }
271 } 285 }
272 if ((rxbs_status & CDNS_UART_RXBS_FRAMING) 286 if (isrstatus & CDNS_UART_IXR_OVERRUN) {
273 && (status_mask & CDNS_UART_IXR_PARITY)) { 287 port->icount.overrun++;
274 port->icount.frame++; 288 tty_insert_flip_char(&port->state->port, 0,
275 status = TTY_FRAME; 289 TTY_OVERRUN);
276 }
277 } else {
278 if (isrstatus & CDNS_UART_IXR_PARITY) {
279 port->icount.parity++;
280 status = TTY_PARITY;
281 }
282 if (isrstatus & CDNS_UART_IXR_FRAMING) {
283 port->icount.frame++;
284 status = TTY_FRAME;
285 } 290 }
291 tty_insert_flip_char(&port->state->port, data, status);
286 } 292 }
287 if (isrstatus & CDNS_UART_IXR_OVERRUN)
288 port->icount.overrun++;
289
290 uart_insert_char(port, isrstatus, CDNS_UART_IXR_OVERRUN,
291 data, status);
292 } 293 }
294 spin_unlock(&port->lock);
293 tty_flip_buffer_push(&port->state->port); 295 tty_flip_buffer_push(&port->state->port);
296 spin_lock(&port->lock);
294} 297}
295 298
296static void cdns_uart_handle_tx(struct uart_port *port) 299/**
300 * cdns_uart_handle_tx - Handle the bytes to be Txed.
301 * @dev_id: Id of the UART port
302 * Return: None
303 */
304static void cdns_uart_handle_tx(void *dev_id)
297{ 305{
306 struct uart_port *port = (struct uart_port *)dev_id;
298 unsigned int numbytes; 307 unsigned int numbytes;
299 308
300 if (uart_circ_empty(&port->state->xmit)) { 309 if (uart_circ_empty(&port->state->xmit)) {
301 writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR); 310 writel(CDNS_UART_IXR_TXEMPTY, port->membase + CDNS_UART_IDR);
302 return; 311 } else {
303 } 312 numbytes = port->fifosize;
304 313 while (numbytes && !uart_circ_empty(&port->state->xmit) &&
305 numbytes = port->fifosize; 314 !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) {
306 while (numbytes && !uart_circ_empty(&port->state->xmit) && 315 /*
307 !(readl(port->membase + CDNS_UART_SR) & CDNS_UART_SR_TXFULL)) { 316 * Get the data from the UART circular buffer
308 /* 317 * and write it to the cdns_uart's TX_FIFO
309 * Get the data from the UART circular buffer 318 * register.
310 * and write it to the cdns_uart's TX_FIFO 319 */
311 * register. 320 writel(
312 */ 321 port->state->xmit.buf[port->state->xmit.
313 writel(port->state->xmit.buf[port->state->xmit.tail], 322 tail], port->membase + CDNS_UART_FIFO);
314 port->membase + CDNS_UART_FIFO); 323
315 port->icount.tx++; 324 port->icount.tx++;
316 325
317 /* 326 /*
318 * Adjust the tail of the UART buffer and wrap 327 * Adjust the tail of the UART buffer and wrap
319 * the buffer if it reaches limit. 328 * the buffer if it reaches limit.
320 */ 329 */
321 port->state->xmit.tail = 330 port->state->xmit.tail =
322 (port->state->xmit.tail + 1) & (UART_XMIT_SIZE - 1); 331 (port->state->xmit.tail + 1) &
332 (UART_XMIT_SIZE - 1);
333
334 numbytes--;
335 }
323 336
324 numbytes--; 337 if (uart_circ_chars_pending(
338 &port->state->xmit) < WAKEUP_CHARS)
339 uart_write_wakeup(port);
325 } 340 }
326
327 if (uart_circ_chars_pending(&port->state->xmit) < WAKEUP_CHARS)
328 uart_write_wakeup(port);
329} 341}
330 342
331/** 343/**
@@ -338,27 +350,24 @@ static void cdns_uart_handle_tx(struct uart_port *port)
338static irqreturn_t cdns_uart_isr(int irq, void *dev_id) 350static irqreturn_t cdns_uart_isr(int irq, void *dev_id)
339{ 351{
340 struct uart_port *port = (struct uart_port *)dev_id; 352 struct uart_port *port = (struct uart_port *)dev_id;
341 unsigned long flags;
342 unsigned int isrstatus; 353 unsigned int isrstatus;
343 354
344 spin_lock_irqsave(&port->lock, flags); 355 spin_lock(&port->lock);
345 356
346 /* Read the interrupt status register to determine which 357 /* Read the interrupt status register to determine which
347 * interrupt(s) is/are active. 358 * interrupt(s) is/are active and clear them.
348 */ 359 */
349 isrstatus = readl(port->membase + CDNS_UART_ISR); 360 isrstatus = readl(port->membase + CDNS_UART_ISR);
350
351 if (isrstatus & CDNS_UART_RX_IRQS)
352 cdns_uart_handle_rx(port, isrstatus);
353
354 if ((isrstatus & CDNS_UART_IXR_TXEMPTY) == CDNS_UART_IXR_TXEMPTY)
355 cdns_uart_handle_tx(port);
356
357 writel(isrstatus, port->membase + CDNS_UART_ISR); 361 writel(isrstatus, port->membase + CDNS_UART_ISR);
358 362
359 /* be sure to release the lock and tty before leaving */ 363 if (isrstatus & CDNS_UART_IXR_TXEMPTY) {
360 spin_unlock_irqrestore(&port->lock, flags); 364 cdns_uart_handle_tx(dev_id);
365 isrstatus &= ~CDNS_UART_IXR_TXEMPTY;
366 }
367 if (isrstatus & CDNS_UART_IXR_MASK)
368 cdns_uart_handle_rx(dev_id, isrstatus);
361 369
370 spin_unlock(&port->lock);
362 return IRQ_HANDLED; 371 return IRQ_HANDLED;
363} 372}
364 373