Commit ddd80608 authored by Andy Shevchenko's avatar Andy Shevchenko Committed by Greg Kroah-Hartman

serial: 8250_port: fix runtime PM use in __do_stop_tx_rs485()

commit b3965767 upstream.

There are calls to serial8250_rpm_{get|put}() in __do_stop_tx_rs485() that are
certainly placed in a wrong location. I dunno how it had been tested with
runtime PM enabled because it is obvious "sleep in atomic context" error.

Besides that serial8250_rpm_get() is called immediately after an IO just
happened. It implies that the device is already powered on, see implementation
of serial8250_em485_rts_after_send() and serial8250_clear_fifos() for the
details.

There is no bug have been seen due to, as I can guess, use of auto suspend mode
when scheduled transaction to suspend is invoked quite lately than it's needed
for a few writes to the port. It might be possible to trigger a warning if
stop_tx_timer fires when device is suspended.

Refactor the code to use runtime PM only in case of timer function.

Fixes: 0c66940d ("tty/serial/8250: fix RS485 half-duplex RX")
Cc: "Matwey V. Kornilov" <matwey@sai.msu.ru>
Tested-by: default avatarYegor Yefremov <yegorslists@googlemail.com>
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 80ece27e
...@@ -1414,12 +1414,8 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p) ...@@ -1414,12 +1414,8 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p)
if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) { if (!(p->port.rs485.flags & SER_RS485_RX_DURING_TX)) {
serial8250_clear_fifos(p); serial8250_clear_fifos(p);
serial8250_rpm_get(p);
p->ier |= UART_IER_RLSI | UART_IER_RDI; p->ier |= UART_IER_RLSI | UART_IER_RDI;
serial_port_out(&p->port, UART_IER, p->ier); serial_port_out(&p->port, UART_IER, p->ier);
serial8250_rpm_put(p);
} }
} }
...@@ -1429,6 +1425,7 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg) ...@@ -1429,6 +1425,7 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg)
struct uart_8250_em485 *em485 = p->em485; struct uart_8250_em485 *em485 = p->em485;
unsigned long flags; unsigned long flags;
serial8250_rpm_get(p);
spin_lock_irqsave(&p->port.lock, flags); spin_lock_irqsave(&p->port.lock, flags);
if (em485 && if (em485 &&
em485->active_timer == &em485->stop_tx_timer) { em485->active_timer == &em485->stop_tx_timer) {
...@@ -1436,6 +1433,7 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg) ...@@ -1436,6 +1433,7 @@ static void serial8250_em485_handle_stop_tx(unsigned long arg)
em485->active_timer = NULL; em485->active_timer = NULL;
} }
spin_unlock_irqrestore(&p->port.lock, flags); spin_unlock_irqrestore(&p->port.lock, flags);
serial8250_rpm_put(p);
} }
static void __stop_tx_rs485(struct uart_8250_port *p) static void __stop_tx_rs485(struct uart_8250_port *p)
...@@ -1475,7 +1473,7 @@ static inline void __stop_tx(struct uart_8250_port *p) ...@@ -1475,7 +1473,7 @@ static inline void __stop_tx(struct uart_8250_port *p)
unsigned char lsr = serial_in(p, UART_LSR); unsigned char lsr = serial_in(p, UART_LSR);
/* /*
* To provide required timeing and allow FIFO transfer, * To provide required timeing and allow FIFO transfer,
* __stop_tx_rs485 must be called only when both FIFO and * __stop_tx_rs485() must be called only when both FIFO and
* shift register are empty. It is for device driver to enable * shift register are empty. It is for device driver to enable
* interrupt on TEMT. * interrupt on TEMT.
*/ */
...@@ -1484,9 +1482,10 @@ static inline void __stop_tx(struct uart_8250_port *p) ...@@ -1484,9 +1482,10 @@ static inline void __stop_tx(struct uart_8250_port *p)
del_timer(&em485->start_tx_timer); del_timer(&em485->start_tx_timer);
em485->active_timer = NULL; em485->active_timer = NULL;
__stop_tx_rs485(p);
} }
__do_stop_tx(p); __do_stop_tx(p);
__stop_tx_rs485(p);
} }
static void serial8250_stop_tx(struct uart_port *port) static void serial8250_stop_tx(struct uart_port *port)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment