Skip to content

Commit 8002d6d

Browse files
ij-intelgregkh
authored andcommitted
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 <[email protected]> Tested-by: Bandal, Shankar <[email protected]> Tested-by: Murthy, Shanth <[email protected]> Cc: stable <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Signed-off-by: Ilpo Järvinen <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 59a33d8 commit 8002d6d

1 file changed

Lines changed: 31 additions & 0 deletions

File tree

drivers/tty/serial/8250/8250_dw.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,22 @@ static void dw8250_check_lcr(struct uart_port *p, unsigned int offset, u32 value
181181
*/
182182
}
183183

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+
184200
/* Returns once the transmitter is empty or we run out of retries */
185201
static void dw8250_tx_wait_empty(struct uart_port *p)
186202
{
@@ -207,12 +223,18 @@ static void dw8250_tx_wait_empty(struct uart_port *p)
207223

208224
static void dw8250_serial_out(struct uart_port *p, unsigned int offset, u32 value)
209225
{
226+
if (dw8250_can_skip_reg_write(p, offset, value))
227+
return;
228+
210229
writeb(value, p->membase + (offset << p->regshift));
211230
dw8250_check_lcr(p, offset, value);
212231
}
213232

214233
static void dw8250_serial_out38x(struct uart_port *p, unsigned int offset, u32 value)
215234
{
235+
if (dw8250_can_skip_reg_write(p, offset, value))
236+
return;
237+
216238
/* Allow the TX to drain before we reconfigure */
217239
if (offset == UART_LCR)
218240
dw8250_tx_wait_empty(p);
@@ -237,6 +259,9 @@ static u32 dw8250_serial_inq(struct uart_port *p, unsigned int offset)
237259

238260
static void dw8250_serial_outq(struct uart_port *p, unsigned int offset, u32 value)
239261
{
262+
if (dw8250_can_skip_reg_write(p, offset, value))
263+
return;
264+
240265
value &= 0xff;
241266
__raw_writeq(value, p->membase + (offset << p->regshift));
242267
/* Read back to ensure register write ordering. */
@@ -248,6 +273,9 @@ static void dw8250_serial_outq(struct uart_port *p, unsigned int offset, u32 val
248273

249274
static void dw8250_serial_out32(struct uart_port *p, unsigned int offset, u32 value)
250275
{
276+
if (dw8250_can_skip_reg_write(p, offset, value))
277+
return;
278+
251279
writel(value, p->membase + (offset << p->regshift));
252280
dw8250_check_lcr(p, offset, value);
253281
}
@@ -261,6 +289,9 @@ static u32 dw8250_serial_in32(struct uart_port *p, unsigned int offset)
261289

262290
static void dw8250_serial_out32be(struct uart_port *p, unsigned int offset, u32 value)
263291
{
292+
if (dw8250_can_skip_reg_write(p, offset, value))
293+
return;
294+
264295
iowrite32be(value, p->membase + (offset << p->regshift));
265296
dw8250_check_lcr(p, offset, value);
266297
}

0 commit comments

Comments
 (0)