drivers: serial: uart: ns16550 add missing isr locking

The existing uart driver ns16550 did not have ISR locking that
effected IO APIC working in fixed delivery mode in SMP system
x86_64. This commit adds ISR locking mechanism using spinlock
for the interrupt related services.

The CONFIG_IPM_CONSOLE_STACK_SIZE is increased to lift
limitation of stack size experienced in IPM driver test with
this spinlock impelentation.

Fixes #23026

Signed-off-by: Jennifer Williams <jennifer.m.williams@intel.com>
This commit is contained in:
Jennifer Williams 2020-03-23 09:31:15 -07:00 committed by Anas Nashif
commit d2c74eb987
2 changed files with 131 additions and 25 deletions

View file

@ -34,6 +34,7 @@
#include <linker/sections.h>
#include <drivers/uart.h>
#include <sys/sys_io.h>
#include <spinlock.h>
#include "uart_ns16550.h"
@ -270,6 +271,7 @@ struct uart_ns16550_device_config {
/** Device data structure */
struct uart_ns16550_dev_data_t {
struct uart_config uart_config;
struct k_spinlock lock;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
u8_t iir_cache; /**< cache of IIR since it clears when read */
@ -318,16 +320,21 @@ static int uart_ns16550_configure(struct device *dev,
struct uart_ns16550_dev_data_t * const dev_data = DEV_DATA(dev);
struct uart_ns16550_device_config * const dev_cfg = DEV_CFG(dev);
unsigned int old_level; /* old interrupt lock level */
u8_t mdc = 0U;
/* temp for return value if error occurs in this locked region */
int ret = 0;
k_spinlock_key_t key = k_spin_lock(&dev_data->lock);
ARG_UNUSED(dev_data);
ARG_UNUSED(dev_cfg);
#ifdef UART_NS16550_PCIE_ENABLED
if (dev_cfg->pcie) {
if (!pcie_probe(dev_cfg->pcie_bdf, dev_cfg->pcie_id)) {
return -EINVAL;
ret = -EINVAL;
goto out;
}
dev_cfg->devconf.port = pcie_get_mbar(dev_cfg->pcie_bdf, 0);
@ -339,8 +346,6 @@ static int uart_ns16550_configure(struct device *dev,
dev_data->iir_cache = 0U;
#endif
old_level = irq_lock();
#ifdef UART_NS16550_DLF_ENABLED
OUTBYTE(DLF(dev), dev_data->dlf);
#endif
@ -374,7 +379,8 @@ static int uart_ns16550_configure(struct device *dev,
uart_cfg.data_bits = LCR_CS8;
break;
default:
return -ENOTSUP;
ret = -ENOTSUP;
goto out;
}
switch (cfg->stop_bits) {
@ -385,7 +391,8 @@ static int uart_ns16550_configure(struct device *dev,
uart_cfg.stop_bits = LCR_2_STB;
break;
default:
return -ENOTSUP;
ret = -ENOTSUP;
goto out;
}
switch (cfg->parity) {
@ -396,7 +403,8 @@ static int uart_ns16550_configure(struct device *dev,
uart_cfg.parity = LCR_EPS;
break;
default:
return -ENOTSUP;
ret = -ENOTSUP;
goto out;
}
dev_data->uart_config = *cfg;
@ -432,9 +440,9 @@ static int uart_ns16550_configure(struct device *dev,
/* disable interrupts */
OUTBYTE(IER(dev), 0x00);
irq_unlock(old_level);
return 0;
out:
k_spin_unlock(&dev_data->lock, key);
return ret;
};
static int uart_ns16550_config_get(struct device *dev, struct uart_config *cfg)
@ -480,14 +488,27 @@ static int uart_ns16550_init(struct device *dev)
*/
static int uart_ns16550_poll_in(struct device *dev, unsigned char *c)
{
if ((INBYTE(LSR(dev)) & LSR_RXRDY) == 0x00) {
return (-1);
int ret = -1;
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
while (1) {
if ((INBYTE(LSR(dev)) & LSR_RXRDY) != 0) {
/* got a character */
*c = INBYTE(RDR(dev));
ret = 0;
}
if ((INBYTE(LSR(dev)) & LSR_RXRDY) != 0) {
continue;
}
break;
}
/* got a character */
*c = INBYTE(RDR(dev));
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return 0;
return ret;
}
/**
@ -505,11 +526,20 @@ static int uart_ns16550_poll_in(struct device *dev, unsigned char *c)
static void uart_ns16550_poll_out(struct device *dev,
unsigned char c)
{
/* wait for transmitter to ready to accept a character */
while ((INBYTE(LSR(dev)) & LSR_THRE) == 0) {
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
while (1) {
/* wait for transmitter to ready to accept a character */
if ((INBYTE(LSR(dev)) & LSR_THRE) == 0) {
continue;
}
OUTBYTE(THR(dev), c);
break;
}
OUTBYTE(THR(dev), c);
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}
/**
@ -522,7 +552,12 @@ static void uart_ns16550_poll_out(struct device *dev,
*/
static int uart_ns16550_err_check(struct device *dev)
{
return (INBYTE(LSR(dev)) & LSR_EOB_MASK) >> 1;
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
int check = (INBYTE(LSR(dev)) & LSR_EOB_MASK);
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return check >> 1;
}
#if CONFIG_UART_INTERRUPT_DRIVEN
@ -540,10 +575,14 @@ static int uart_ns16550_fifo_fill(struct device *dev, const u8_t *tx_data,
int size)
{
int i;
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
for (i = 0; i < size && (INBYTE(LSR(dev)) & LSR_THRE) != 0; i++) {
for (i = 0; (i < size) && (INBYTE(LSR(dev)) & LSR_THRE) != 0; i++) {
OUTBYTE(THR(dev), tx_data[i]);
}
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return i;
}
@ -560,11 +599,14 @@ static int uart_ns16550_fifo_read(struct device *dev, u8_t *rx_data,
const int size)
{
int i;
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
for (i = 0; i < size && (INBYTE(LSR(dev)) & LSR_RXRDY) != 0; i++) {
for (i = 0; (i < size) && (INBYTE(LSR(dev)) & LSR_RXRDY) != 0; i++) {
rx_data[i] = INBYTE(RDR(dev));
}
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return i;
}
@ -577,7 +619,11 @@ static int uart_ns16550_fifo_read(struct device *dev, u8_t *rx_data,
*/
static void uart_ns16550_irq_tx_enable(struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
OUTBYTE(IER(dev), INBYTE(IER(dev)) | IER_TBE);
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}
/**
@ -589,7 +635,11 @@ static void uart_ns16550_irq_tx_enable(struct device *dev)
*/
static void uart_ns16550_irq_tx_disable(struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
OUTBYTE(IER(dev), INBYTE(IER(dev)) & (~IER_TBE));
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}
/**
@ -601,7 +651,13 @@ static void uart_ns16550_irq_tx_disable(struct device *dev)
*/
static int uart_ns16550_irq_tx_ready(struct device *dev)
{
return ((IIRC(dev) & IIR_ID) == IIR_THRE);
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
int ret = ((IIRC(dev) & IIR_ID) == IIR_THRE) ? 1 : 0;
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return ret;
}
/**
@ -613,7 +669,14 @@ static int uart_ns16550_irq_tx_ready(struct device *dev)
*/
static int uart_ns16550_irq_tx_complete(struct device *dev)
{
return (INBYTE(LSR(dev)) & (LSR_TEMT | LSR_THRE)) == (LSR_TEMT | LSR_THRE);
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
int ret = ((INBYTE(LSR(dev)) & (LSR_TEMT | LSR_THRE))
== (LSR_TEMT | LSR_THRE)) ? 1 : 0;
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return ret;
}
/**
@ -625,7 +688,11 @@ static int uart_ns16550_irq_tx_complete(struct device *dev)
*/
static void uart_ns16550_irq_rx_enable(struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
OUTBYTE(IER(dev), INBYTE(IER(dev)) | IER_RXRDY);
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}
/**
@ -637,7 +704,11 @@ static void uart_ns16550_irq_rx_enable(struct device *dev)
*/
static void uart_ns16550_irq_rx_disable(struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
OUTBYTE(IER(dev), INBYTE(IER(dev)) & (~IER_RXRDY));
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}
/**
@ -649,7 +720,13 @@ static void uart_ns16550_irq_rx_disable(struct device *dev)
*/
static int uart_ns16550_irq_rx_ready(struct device *dev)
{
return ((IIRC(dev) & IIR_ID) == IIR_RBRF);
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
int ret = ((IIRC(dev) & IIR_ID) == IIR_RBRF) ? 1 : 0;
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return ret;
}
/**
@ -661,7 +738,11 @@ static int uart_ns16550_irq_rx_ready(struct device *dev)
*/
static void uart_ns16550_irq_err_enable(struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
OUTBYTE(IER(dev), INBYTE(IER(dev)) | IER_LSR);
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}
/**
@ -673,7 +754,11 @@ static void uart_ns16550_irq_err_enable(struct device *dev)
*/
static void uart_ns16550_irq_err_disable(struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
OUTBYTE(IER(dev), INBYTE(IER(dev)) & (~IER_LSR));
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}
/**
@ -685,7 +770,13 @@ static void uart_ns16550_irq_err_disable(struct device *dev)
*/
static int uart_ns16550_irq_is_pending(struct device *dev)
{
return (!(IIRC(dev) & IIR_NIP));
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
int ret = (!(IIRC(dev) & IIR_NIP)) ? 1 : 0;
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return ret;
}
/**
@ -697,8 +788,12 @@ static int uart_ns16550_irq_is_pending(struct device *dev)
*/
static int uart_ns16550_irq_update(struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
IIRC(dev) = INBYTE(IIR(dev));
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return 1;
}
@ -715,9 +810,12 @@ static void uart_ns16550_irq_callback_set(struct device *dev,
void *cb_data)
{
struct uart_ns16550_dev_data_t * const dev_data = DEV_DATA(dev);
k_spinlock_key_t key = k_spin_lock(&dev_data->lock);
dev_data->cb = cb;
dev_data->cb_data = cb_data;
k_spin_unlock(&dev_data->lock, key);
}
/**
@ -737,6 +835,7 @@ static void uart_ns16550_isr(void *arg)
if (dev_data->cb) {
dev_data->cb(dev_data->cb_data);
}
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
@ -756,6 +855,7 @@ static int uart_ns16550_line_ctrl_set(struct device *dev,
u32_t ctrl, u32_t val)
{
u32_t mdc, chg;
k_spinlock_key_t key;
switch (ctrl) {
case UART_LINE_CTRL_BAUD_RATE:
@ -764,6 +864,7 @@ static int uart_ns16550_line_ctrl_set(struct device *dev,
case UART_LINE_CTRL_RTS:
case UART_LINE_CTRL_DTR:
key = k_spin_lock(&DEV_DATA(dev)->lock);
mdc = INBYTE(MDC(dev));
if (ctrl == UART_LINE_CTRL_RTS) {
@ -778,6 +879,7 @@ static int uart_ns16550_line_ctrl_set(struct device *dev,
mdc &= ~(chg);
}
OUTBYTE(MDC(dev), mdc);
k_spin_unlock(&DEV_DATA(dev)->lock, key);
return 0;
}
@ -802,8 +904,11 @@ static int uart_ns16550_drv_cmd(struct device *dev, u32_t cmd, u32_t p)
#ifdef UART_NS16550_DLF_ENABLED
if (cmd == CMD_SET_DLF) {
struct uart_ns16550_dev_data_t * const dev_data = DEV_DATA(dev);
k_spinlock_key_t key = k_spin_lock(&dev_data->lock);
dev_data->dlf = p;
OUTBYTE(DLF(dev), dev_data->dlf);
k_spin_unlock(&dev_data->lock, key);
return 0;
}
#endif

View file

@ -6,3 +6,4 @@ CONFIG_IPM_CONSOLE_RECEIVER=y
CONFIG_IPM_CONSOLE_SENDER=y
CONFIG_IRQ_OFFLOAD=y
CONFIG_MP_NUM_CPUS=1
CONFIG_IPM_CONSOLE_STACK_SIZE=2048