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_dw: Avoid unnecessary LCR writes

When DW UART is configured with BUSY flag, LCR writes may not always
succeed which can make any LCR write complex and very expensive.
Performing write directly can trigger IRQ and the driver has to perform
complex and distruptive sequence while retrying the write.

Therefore, it's better to avoid doing LCR write that would not change
the value of the LCR register. Add LCR write avoidance code into the
8250_dw driver's .serial_out() functions.

Reported-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Bandal, Shankar <shankar.bandal@intel.com>
Tested-by: Murthy, Shanth <shanth.murthy@intel.com>
Cc: stable <stable@kernel.org>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://patch.msgid.link/20260203171049.4353-3-ilpo.jarvinen@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Ilpo Järvinen and committed by
Greg Kroah-Hartman
8002d6d6 59a33d83

+31
+31
drivers/tty/serial/8250/8250_dw.c
··· 181 181 */ 182 182 } 183 183 184 + /* 185 + * With BUSY, LCR writes can be very expensive (IRQ + complex retry logic). 186 + * If the write does not change the value of the LCR register, skip it entirely. 187 + */ 188 + static bool dw8250_can_skip_reg_write(struct uart_port *p, unsigned int offset, u32 value) 189 + { 190 + struct dw8250_data *d = to_dw8250_data(p->private_data); 191 + u32 lcr; 192 + 193 + if (offset != UART_LCR || d->uart_16550_compatible) 194 + return false; 195 + 196 + lcr = serial_port_in(p, offset); 197 + return lcr == value; 198 + } 199 + 184 200 /* Returns once the transmitter is empty or we run out of retries */ 185 201 static void dw8250_tx_wait_empty(struct uart_port *p) 186 202 { ··· 223 207 224 208 static void dw8250_serial_out(struct uart_port *p, unsigned int offset, u32 value) 225 209 { 210 + if (dw8250_can_skip_reg_write(p, offset, value)) 211 + return; 212 + 226 213 writeb(value, p->membase + (offset << p->regshift)); 227 214 dw8250_check_lcr(p, offset, value); 228 215 } 229 216 230 217 static void dw8250_serial_out38x(struct uart_port *p, unsigned int offset, u32 value) 231 218 { 219 + if (dw8250_can_skip_reg_write(p, offset, value)) 220 + return; 221 + 232 222 /* Allow the TX to drain before we reconfigure */ 233 223 if (offset == UART_LCR) 234 224 dw8250_tx_wait_empty(p); ··· 259 237 260 238 static void dw8250_serial_outq(struct uart_port *p, unsigned int offset, u32 value) 261 239 { 240 + if (dw8250_can_skip_reg_write(p, offset, value)) 241 + return; 242 + 262 243 value &= 0xff; 263 244 __raw_writeq(value, p->membase + (offset << p->regshift)); 264 245 /* Read back to ensure register write ordering. */ ··· 273 248 274 249 static void dw8250_serial_out32(struct uart_port *p, unsigned int offset, u32 value) 275 250 { 251 + if (dw8250_can_skip_reg_write(p, offset, value)) 252 + return; 253 + 276 254 writel(value, p->membase + (offset << p->regshift)); 277 255 dw8250_check_lcr(p, offset, value); 278 256 } ··· 289 261 290 262 static void dw8250_serial_out32be(struct uart_port *p, unsigned int offset, u32 value) 291 263 { 264 + if (dw8250_can_skip_reg_write(p, offset, value)) 265 + return; 266 + 292 267 iowrite32be(value, p->membase + (offset << p->regshift)); 293 268 dw8250_check_lcr(p, offset, value); 294 269 }