diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/card/sdio_uart.c | 21 |
1 files changed, 19 insertions, 2 deletions
diff --git a/drivers/mmc/card/sdio_uart.c b/drivers/mmc/card/sdio_uart.c index 606100212afa..d552de683110 100644 --- a/drivers/mmc/card/sdio_uart.c +++ b/drivers/mmc/card/sdio_uart.c | |||
@@ -79,6 +79,7 @@ struct sdio_uart_port { | |||
79 | struct mutex open_lock; | 79 | struct mutex open_lock; |
80 | struct sdio_func *func; | 80 | struct sdio_func *func; |
81 | struct mutex func_lock; | 81 | struct mutex func_lock; |
82 | struct task_struct *in_sdio_uart_irq; | ||
82 | unsigned int regs_offset; | 83 | unsigned int regs_offset; |
83 | struct circ_buf xmit; | 84 | struct circ_buf xmit; |
84 | spinlock_t write_lock; | 85 | spinlock_t write_lock; |
@@ -186,14 +187,16 @@ static int sdio_uart_claim_func(struct sdio_uart_port *port) | |||
186 | mutex_unlock(&port->func_lock); | 187 | mutex_unlock(&port->func_lock); |
187 | return -ENODEV; | 188 | return -ENODEV; |
188 | } | 189 | } |
189 | sdio_claim_host(port->func); | 190 | if (likely(port->in_sdio_uart_irq != current)) |
191 | sdio_claim_host(port->func); | ||
190 | mutex_unlock(&port->func_lock); | 192 | mutex_unlock(&port->func_lock); |
191 | return 0; | 193 | return 0; |
192 | } | 194 | } |
193 | 195 | ||
194 | static inline void sdio_uart_release_func(struct sdio_uart_port *port) | 196 | static inline void sdio_uart_release_func(struct sdio_uart_port *port) |
195 | { | 197 | { |
196 | sdio_release_host(port->func); | 198 | if (likely(port->in_sdio_uart_irq != current)) |
199 | sdio_release_host(port->func); | ||
197 | } | 200 | } |
198 | 201 | ||
199 | static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset) | 202 | static inline unsigned int sdio_in(struct sdio_uart_port *port, int offset) |
@@ -511,15 +514,29 @@ static void sdio_uart_irq(struct sdio_func *func) | |||
511 | struct sdio_uart_port *port = sdio_get_drvdata(func); | 514 | struct sdio_uart_port *port = sdio_get_drvdata(func); |
512 | unsigned int iir, lsr; | 515 | unsigned int iir, lsr; |
513 | 516 | ||
517 | /* | ||
518 | * In a few places sdio_uart_irq() is called directly instead of | ||
519 | * waiting for the actual interrupt to be raised and the SDIO IRQ | ||
520 | * thread scheduled in order to reduce latency. However, some | ||
521 | * interaction with the tty core may end up calling us back | ||
522 | * (serial echo, flow control, etc.) through those same places | ||
523 | * causing undesirable effects. Let's stop the recursion here. | ||
524 | */ | ||
525 | if (unlikely(port->in_sdio_uart_irq == current)) | ||
526 | return; | ||
527 | |||
514 | iir = sdio_in(port, UART_IIR); | 528 | iir = sdio_in(port, UART_IIR); |
515 | if (iir & UART_IIR_NO_INT) | 529 | if (iir & UART_IIR_NO_INT) |
516 | return; | 530 | return; |
531 | |||
532 | port->in_sdio_uart_irq = current; | ||
517 | lsr = sdio_in(port, UART_LSR); | 533 | lsr = sdio_in(port, UART_LSR); |
518 | if (lsr & UART_LSR_DR) | 534 | if (lsr & UART_LSR_DR) |
519 | sdio_uart_receive_chars(port, &lsr); | 535 | sdio_uart_receive_chars(port, &lsr); |
520 | sdio_uart_check_modem_status(port); | 536 | sdio_uart_check_modem_status(port); |
521 | if (lsr & UART_LSR_THRE) | 537 | if (lsr & UART_LSR_THRE) |
522 | sdio_uart_transmit_chars(port); | 538 | sdio_uart_transmit_chars(port); |
539 | port->in_sdio_uart_irq = NULL; | ||
523 | } | 540 | } |
524 | 541 | ||
525 | static int sdio_uart_startup(struct sdio_uart_port *port) | 542 | static int sdio_uart_startup(struct sdio_uart_port *port) |