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

Configure Feed

Select the types of activity you want to include in your feed.

serial: 8250: omap: Fix missing PM runtime calls for omap8250_set_mctrl()

There are cases where omap8250_set_mctrl() may get called after the
UART has already autoidled causing an asynchronous external abort.

This can happen on ttyport_open():

mem_serial_in from omap8250_set_mctrl+0x38/0xa0
omap8250_set_mctrl from uart_update_mctrl+0x4c/0x58
uart_update_mctrl from uart_dtr_rts+0x60/0xa8
uart_dtr_rts from tty_port_block_til_ready+0xd0/0x2a8
tty_port_block_til_ready from uart_open+0x14/0x1c
uart_open from ttyport_open+0x64/0x148

And on ttyport_close():

omap8250_set_mctrl from uart_update_mctrl+0x3c/0x48
uart_update_mctrl from uart_dtr_rts+0x54/0x9c
uart_dtr_rts from tty_port_shutdown+0x78/0x9c
tty_port_shutdown from tty_port_close+0x3c/0x74
tty_port_close from ttyport_close+0x40/0x58

It can also happen on disassociate_ctty() calling uart_shutdown()
that ends up calling omap8250_set_mctrl().

Let's fix the issue by adding missing PM runtime calls to
omap8250_set_mctrl(). To do this, we need to add __omap8250_set_mctrl()
that can be called from both omap8250_set_mctrl(), and from runtime PM
resume path when restoring the registers.

Fixes: 61929cf0169d ("tty: serial: Add 8250-core based omap driver")
Reported-by: Merlijn Wajer <merlijn@wizzup.org>
Reported-by: Romain Naour <romain.naour@smile.fr>
Reported-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
Tested-by: Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Depends-on: dd8088d5a896 ("PM: runtime: Add pm_runtime_resume_and_get to deal with usage counter")
Link: https://lore.kernel.org/r/20221024063613.25943-1-tony@atomide.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Tony Lindgren and committed by
Greg Kroah-Hartman
93810191 038ee49f

+20 -2
+20 -2
drivers/tty/serial/8250/8250_omap.c
··· 157 157 return readl(up->port.membase + (reg << up->port.regshift)); 158 158 } 159 159 160 - static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) 160 + /* 161 + * Called on runtime PM resume path from omap8250_restore_regs(), and 162 + * omap8250_set_mctrl(). 163 + */ 164 + static void __omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) 161 165 { 162 166 struct uart_8250_port *up = up_to_u8250p(port); 163 167 struct omap8250_priv *priv = up->port.private_data; ··· 183 179 serial_out(up, UART_EFR, priv->efr); 184 180 serial_out(up, UART_LCR, lcr); 185 181 } 182 + } 183 + 184 + static void omap8250_set_mctrl(struct uart_port *port, unsigned int mctrl) 185 + { 186 + int err; 187 + 188 + err = pm_runtime_resume_and_get(port->dev); 189 + if (err) 190 + return; 191 + 192 + __omap8250_set_mctrl(port, mctrl); 193 + 194 + pm_runtime_mark_last_busy(port->dev); 195 + pm_runtime_put_autosuspend(port->dev); 186 196 } 187 197 188 198 /* ··· 361 343 362 344 omap8250_update_mdr1(up, priv); 363 345 364 - up->port.ops->set_mctrl(&up->port, up->port.mctrl); 346 + __omap8250_set_mctrl(&up->port, up->port.mctrl); 365 347 366 348 if (up->port.rs485.flags & SER_RS485_ENABLED) 367 349 serial8250_em485_stop_tx(up);