Skip to content

Commit 0c84bea

Browse files
hvilleneuvedoogregkh
authored andcommitted
serial: sc16is7xx: refactor EFR lock
Move common code for EFR lock/unlock of mutex into functions for code reuse and clarity. With the addition of old_lcr, move irda_mode within struct sc16is7xx_one to reduce memory usage: Before: /* size: 752, cachelines: 12, members: 10 */ After: /* size: 744, cachelines: 12, members: 10 */ Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com> Link: https://lore.kernel.org/r/20231221231823.2327894-17-hugo@hugovil.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 2de8a1b commit 0c84bea

File tree

1 file changed

+57
-49
lines changed

1 file changed

+57
-49
lines changed

drivers/tty/serial/sc16is7xx.c

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,9 @@ struct sc16is7xx_one {
330330
struct kthread_work reg_work;
331331
struct kthread_delayed_work ms_work;
332332
struct sc16is7xx_one_config config;
333-
bool irda_mode;
334333
unsigned int old_mctrl;
334+
u8 old_lcr; /* Value before EFR access. */
335+
bool irda_mode;
335336
};
336337

337338
struct sc16is7xx_port {
@@ -412,6 +413,49 @@ static void sc16is7xx_power(struct uart_port *port, int on)
412413
on ? 0 : SC16IS7XX_IER_SLEEP_BIT);
413414
}
414415

416+
/*
417+
* In an amazing feat of design, the Enhanced Features Register (EFR)
418+
* shares the address of the Interrupt Identification Register (IIR).
419+
* Access to EFR is switched on by writing a magic value (0xbf) to the
420+
* Line Control Register (LCR). Any interrupt firing during this time will
421+
* see the EFR where it expects the IIR to be, leading to
422+
* "Unexpected interrupt" messages.
423+
*
424+
* Prevent this possibility by claiming a mutex while accessing the EFR,
425+
* and claiming the same mutex from within the interrupt handler. This is
426+
* similar to disabling the interrupt, but that doesn't work because the
427+
* bulk of the interrupt processing is run as a workqueue job in thread
428+
* context.
429+
*/
430+
static void sc16is7xx_efr_lock(struct uart_port *port)
431+
{
432+
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
433+
434+
mutex_lock(&one->efr_lock);
435+
436+
/* Backup content of LCR. */
437+
one->old_lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
438+
439+
/* Enable access to Enhanced register set */
440+
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B);
441+
442+
/* Disable cache updates when writing to EFR registers */
443+
regcache_cache_bypass(one->regmap, true);
444+
}
445+
446+
static void sc16is7xx_efr_unlock(struct uart_port *port)
447+
{
448+
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
449+
450+
/* Re-enable cache updates when writing to normal registers */
451+
regcache_cache_bypass(one->regmap, false);
452+
453+
/* Restore original content of LCR */
454+
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, one->old_lcr);
455+
456+
mutex_unlock(&one->efr_lock);
457+
}
458+
415459
static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
416460
{
417461
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
@@ -522,45 +566,19 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
522566
div /= 4;
523567
}
524568

525-
/* In an amazing feat of design, the Enhanced Features Register shares
526-
* the address of the Interrupt Identification Register, and is
527-
* switched in by writing a magic value (0xbf) to the Line Control
528-
* Register. Any interrupt firing during this time will see the EFR
529-
* where it expects the IIR to be, leading to "Unexpected interrupt"
530-
* messages.
531-
*
532-
* Prevent this possibility by claiming a mutex while accessing the
533-
* EFR, and claiming the same mutex from within the interrupt handler.
534-
* This is similar to disabling the interrupt, but that doesn't work
535-
* because the bulk of the interrupt processing is run as a workqueue
536-
* job in thread context.
537-
*/
538-
mutex_lock(&one->efr_lock);
539-
540-
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
541-
542-
/* Open the LCR divisors for configuration */
543-
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
544-
SC16IS7XX_LCR_CONF_MODE_B);
545-
546569
/* Enable enhanced features */
547-
regcache_cache_bypass(one->regmap, true);
570+
sc16is7xx_efr_lock(port);
548571
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
549572
SC16IS7XX_EFR_ENABLE_BIT,
550573
SC16IS7XX_EFR_ENABLE_BIT);
551-
552-
regcache_cache_bypass(one->regmap, false);
553-
554-
/* Put LCR back to the normal mode */
555-
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
556-
557-
mutex_unlock(&one->efr_lock);
574+
sc16is7xx_efr_unlock(port);
558575

559576
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
560577
SC16IS7XX_MCR_CLKSEL_BIT,
561578
prescaler);
562579

563-
/* Open the LCR divisors for configuration */
580+
/* Backup LCR and access special register set (DLL/DLH) */
581+
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
564582
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
565583
SC16IS7XX_LCR_CONF_MODE_A);
566584

@@ -570,7 +588,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
570588
sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256);
571589
regcache_cache_bypass(one->regmap, false);
572590

573-
/* Put LCR back to the normal mode */
591+
/* Restore LCR and access to general register set */
574592
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
575593

576594
return DIV_ROUND_CLOSEST(clk / 16, div);
@@ -1049,17 +1067,7 @@ static void sc16is7xx_set_termios(struct uart_port *port,
10491067
if (!(termios->c_cflag & CREAD))
10501068
port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
10511069

1052-
/* As above, claim the mutex while accessing the EFR. */
1053-
mutex_lock(&one->efr_lock);
1054-
1055-
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
1056-
SC16IS7XX_LCR_CONF_MODE_B);
1057-
10581070
/* Configure flow control */
1059-
regcache_cache_bypass(one->regmap, true);
1060-
sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
1061-
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
1062-
10631071
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
10641072
if (termios->c_cflag & CRTSCTS) {
10651073
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
@@ -1071,16 +1079,16 @@ static void sc16is7xx_set_termios(struct uart_port *port,
10711079
if (termios->c_iflag & IXOFF)
10721080
flow |= SC16IS7XX_EFR_SWFLOW1_BIT;
10731081

1074-
sc16is7xx_port_update(port,
1075-
SC16IS7XX_EFR_REG,
1076-
SC16IS7XX_EFR_FLOWCTRL_BITS,
1077-
flow);
1078-
regcache_cache_bypass(one->regmap, false);
1079-
10801082
/* Update LCR register */
10811083
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
10821084

1083-
mutex_unlock(&one->efr_lock);
1085+
/* Update EFR registers */
1086+
sc16is7xx_efr_lock(port);
1087+
sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
1088+
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
1089+
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
1090+
SC16IS7XX_EFR_FLOWCTRL_BITS, flow);
1091+
sc16is7xx_efr_unlock(port);
10841092

10851093
/* Get baud rate generator configuration */
10861094
baud = uart_get_baud_rate(port, termios, old,

0 commit comments

Comments
 (0)