mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-03 19:53:32 +00:00
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>
This commit is contained in:
parent
2de8a1b467
commit
0c84bea0ca
@ -330,8 +330,9 @@ struct sc16is7xx_one {
|
||||
struct kthread_work reg_work;
|
||||
struct kthread_delayed_work ms_work;
|
||||
struct sc16is7xx_one_config config;
|
||||
bool irda_mode;
|
||||
unsigned int old_mctrl;
|
||||
u8 old_lcr; /* Value before EFR access. */
|
||||
bool irda_mode;
|
||||
};
|
||||
|
||||
struct sc16is7xx_port {
|
||||
@ -412,6 +413,49 @@ static void sc16is7xx_power(struct uart_port *port, int on)
|
||||
on ? 0 : SC16IS7XX_IER_SLEEP_BIT);
|
||||
}
|
||||
|
||||
/*
|
||||
* In an amazing feat of design, the Enhanced Features Register (EFR)
|
||||
* shares the address of the Interrupt Identification Register (IIR).
|
||||
* Access to EFR is switched on by writing a magic value (0xbf) to the
|
||||
* Line Control Register (LCR). Any interrupt firing during this time will
|
||||
* see the EFR where it expects the IIR to be, leading to
|
||||
* "Unexpected interrupt" messages.
|
||||
*
|
||||
* Prevent this possibility by claiming a mutex while accessing the EFR,
|
||||
* and claiming the same mutex from within the interrupt handler. This is
|
||||
* similar to disabling the interrupt, but that doesn't work because the
|
||||
* bulk of the interrupt processing is run as a workqueue job in thread
|
||||
* context.
|
||||
*/
|
||||
static void sc16is7xx_efr_lock(struct uart_port *port)
|
||||
{
|
||||
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
||||
|
||||
mutex_lock(&one->efr_lock);
|
||||
|
||||
/* Backup content of LCR. */
|
||||
one->old_lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
|
||||
|
||||
/* Enable access to Enhanced register set */
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, SC16IS7XX_LCR_CONF_MODE_B);
|
||||
|
||||
/* Disable cache updates when writing to EFR registers */
|
||||
regcache_cache_bypass(one->regmap, true);
|
||||
}
|
||||
|
||||
static void sc16is7xx_efr_unlock(struct uart_port *port)
|
||||
{
|
||||
struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
|
||||
|
||||
/* Re-enable cache updates when writing to normal registers */
|
||||
regcache_cache_bypass(one->regmap, false);
|
||||
|
||||
/* Restore original content of LCR */
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, one->old_lcr);
|
||||
|
||||
mutex_unlock(&one->efr_lock);
|
||||
}
|
||||
|
||||
static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
|
||||
{
|
||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
||||
@ -522,45 +566,19 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
|
||||
div /= 4;
|
||||
}
|
||||
|
||||
/* In an amazing feat of design, the Enhanced Features Register shares
|
||||
* the address of the Interrupt Identification Register, and is
|
||||
* switched in by writing a magic value (0xbf) to the Line Control
|
||||
* Register. Any interrupt firing during this time will see the EFR
|
||||
* where it expects the IIR to be, leading to "Unexpected interrupt"
|
||||
* messages.
|
||||
*
|
||||
* Prevent this possibility by claiming a mutex while accessing the
|
||||
* EFR, and claiming the same mutex from within the interrupt handler.
|
||||
* This is similar to disabling the interrupt, but that doesn't work
|
||||
* because the bulk of the interrupt processing is run as a workqueue
|
||||
* job in thread context.
|
||||
*/
|
||||
mutex_lock(&one->efr_lock);
|
||||
|
||||
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
|
||||
|
||||
/* Open the LCR divisors for configuration */
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
|
||||
SC16IS7XX_LCR_CONF_MODE_B);
|
||||
|
||||
/* Enable enhanced features */
|
||||
regcache_cache_bypass(one->regmap, true);
|
||||
sc16is7xx_efr_lock(port);
|
||||
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
||||
SC16IS7XX_EFR_ENABLE_BIT,
|
||||
SC16IS7XX_EFR_ENABLE_BIT);
|
||||
|
||||
regcache_cache_bypass(one->regmap, false);
|
||||
|
||||
/* Put LCR back to the normal mode */
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
||||
|
||||
mutex_unlock(&one->efr_lock);
|
||||
sc16is7xx_efr_unlock(port);
|
||||
|
||||
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
|
||||
SC16IS7XX_MCR_CLKSEL_BIT,
|
||||
prescaler);
|
||||
|
||||
/* Open the LCR divisors for configuration */
|
||||
/* Backup LCR and access special register set (DLL/DLH) */
|
||||
lcr = sc16is7xx_port_read(port, SC16IS7XX_LCR_REG);
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
|
||||
SC16IS7XX_LCR_CONF_MODE_A);
|
||||
|
||||
@ -570,7 +588,7 @@ static int sc16is7xx_set_baud(struct uart_port *port, int baud)
|
||||
sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256);
|
||||
regcache_cache_bypass(one->regmap, false);
|
||||
|
||||
/* Put LCR back to the normal mode */
|
||||
/* Restore LCR and access to general register set */
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
||||
|
||||
return DIV_ROUND_CLOSEST(clk / 16, div);
|
||||
@ -1049,17 +1067,7 @@ static void sc16is7xx_set_termios(struct uart_port *port,
|
||||
if (!(termios->c_cflag & CREAD))
|
||||
port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
|
||||
|
||||
/* As above, claim the mutex while accessing the EFR. */
|
||||
mutex_lock(&one->efr_lock);
|
||||
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG,
|
||||
SC16IS7XX_LCR_CONF_MODE_B);
|
||||
|
||||
/* Configure flow control */
|
||||
regcache_cache_bypass(one->regmap, true);
|
||||
sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
|
||||
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
|
||||
|
||||
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
|
||||
if (termios->c_cflag & CRTSCTS) {
|
||||
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
|
||||
@ -1071,16 +1079,16 @@ static void sc16is7xx_set_termios(struct uart_port *port,
|
||||
if (termios->c_iflag & IXOFF)
|
||||
flow |= SC16IS7XX_EFR_SWFLOW1_BIT;
|
||||
|
||||
sc16is7xx_port_update(port,
|
||||
SC16IS7XX_EFR_REG,
|
||||
SC16IS7XX_EFR_FLOWCTRL_BITS,
|
||||
flow);
|
||||
regcache_cache_bypass(one->regmap, false);
|
||||
|
||||
/* Update LCR register */
|
||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
||||
|
||||
mutex_unlock(&one->efr_lock);
|
||||
/* Update EFR registers */
|
||||
sc16is7xx_efr_lock(port);
|
||||
sc16is7xx_port_write(port, SC16IS7XX_XON1_REG, termios->c_cc[VSTART]);
|
||||
sc16is7xx_port_write(port, SC16IS7XX_XOFF1_REG, termios->c_cc[VSTOP]);
|
||||
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
||||
SC16IS7XX_EFR_FLOWCTRL_BITS, flow);
|
||||
sc16is7xx_efr_unlock(port);
|
||||
|
||||
/* Get baud rate generator configuration */
|
||||
baud = uart_get_baud_rate(port, termios, old,
|
||||
|
Loading…
Reference in New Issue
Block a user