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_work reg_work;
|
||||||
struct kthread_delayed_work ms_work;
|
struct kthread_delayed_work ms_work;
|
||||||
struct sc16is7xx_one_config config;
|
struct sc16is7xx_one_config config;
|
||||||
bool irda_mode;
|
|
||||||
unsigned int old_mctrl;
|
unsigned int old_mctrl;
|
||||||
|
u8 old_lcr; /* Value before EFR access. */
|
||||||
|
bool irda_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc16is7xx_port {
|
struct sc16is7xx_port {
|
||||||
@ -412,6 +413,49 @@ static void sc16is7xx_power(struct uart_port *port, int on)
|
|||||||
on ? 0 : SC16IS7XX_IER_SLEEP_BIT);
|
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)
|
static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
|
||||||
{
|
{
|
||||||
struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
|
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;
|
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 */
|
/* Enable enhanced features */
|
||||||
regcache_cache_bypass(one->regmap, true);
|
sc16is7xx_efr_lock(port);
|
||||||
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
sc16is7xx_port_update(port, SC16IS7XX_EFR_REG,
|
||||||
SC16IS7XX_EFR_ENABLE_BIT,
|
SC16IS7XX_EFR_ENABLE_BIT,
|
||||||
SC16IS7XX_EFR_ENABLE_BIT);
|
SC16IS7XX_EFR_ENABLE_BIT);
|
||||||
|
sc16is7xx_efr_unlock(port);
|
||||||
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_port_update(port, SC16IS7XX_MCR_REG,
|
sc16is7xx_port_update(port, SC16IS7XX_MCR_REG,
|
||||||
SC16IS7XX_MCR_CLKSEL_BIT,
|
SC16IS7XX_MCR_CLKSEL_BIT,
|
||||||
prescaler);
|
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_port_write(port, SC16IS7XX_LCR_REG,
|
||||||
SC16IS7XX_LCR_CONF_MODE_A);
|
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);
|
sc16is7xx_port_write(port, SC16IS7XX_DLL_REG, div % 256);
|
||||||
regcache_cache_bypass(one->regmap, false);
|
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);
|
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
||||||
|
|
||||||
return DIV_ROUND_CLOSEST(clk / 16, div);
|
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))
|
if (!(termios->c_cflag & CREAD))
|
||||||
port->ignore_status_mask |= SC16IS7XX_LSR_BRK_ERROR_MASK;
|
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 */
|
/* 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);
|
port->status &= ~(UPSTAT_AUTOCTS | UPSTAT_AUTORTS);
|
||||||
if (termios->c_cflag & CRTSCTS) {
|
if (termios->c_cflag & CRTSCTS) {
|
||||||
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
|
flow |= SC16IS7XX_EFR_AUTOCTS_BIT |
|
||||||
@ -1071,16 +1079,16 @@ static void sc16is7xx_set_termios(struct uart_port *port,
|
|||||||
if (termios->c_iflag & IXOFF)
|
if (termios->c_iflag & IXOFF)
|
||||||
flow |= SC16IS7XX_EFR_SWFLOW1_BIT;
|
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 */
|
/* Update LCR register */
|
||||||
sc16is7xx_port_write(port, SC16IS7XX_LCR_REG, lcr);
|
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 */
|
/* Get baud rate generator configuration */
|
||||||
baud = uart_get_baud_rate(port, termios, old,
|
baud = uart_get_baud_rate(port, termios, old,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user