Skip to content

Commit f14e9e4

Browse files
yashinashif
authored andcommitted
serial: xilinx: uartlite: Fix bus fault
Xilinx AXI UART Lite v2.0[1] has the following clause for both RX and TX FIFO respectively: When a read request is issued to an empty FIFO, a bus error (SLVERR) is generated and the result is undefined. When a write request is issued while the FIFO is full, a bus error (SLVERR) is generated and the data is not written into the FIFO. To protect this, we have: xlnx_uartlite_read_status(dev) & STAT_REG_RX_FIFO_VALID_DATA, and xlnx_uartlite_read_status(dev) & STAT_REG_TX_FIFO_FULL but these are not enough for multi-threaded apps. Consider two threads calling poll_out(), it is always possible for a thread to be swapped out right after reading the status register, the other thread fill the TX FIFO, and the original thread is swapped back to write more data to the FIFO because previously read status doesn't indicate the FIFO is full. To close this race condition, this commit uses a spinlock for each FIFO. This ensures that only one thread accesses the FIFO even for SMP cases. This closes #45302. [1] https://docs.xilinx.com/v/u/en-US/pg142-axi-uartlite Signed-off-by: Yasushi SHOJI <[email protected]>
1 parent df6b8c3 commit f14e9e4

File tree

1 file changed

+47
-12
lines changed

1 file changed

+47
-12
lines changed

drivers/serial/uart_xlnx_uartlite.c

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ struct xlnx_uartlite_config {
4646

4747
struct xlnx_uartlite_data {
4848
uint32_t errors;
49+
50+
/* spinlocks for RX and TX FIFO preventing a bus error */
51+
struct k_spinlock rx_lock;
52+
struct k_spinlock tx_lock;
53+
4954
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
5055
const struct device *dev;
5156
struct k_timer timer;
@@ -96,20 +101,38 @@ static inline void xlnx_uartlite_write_tx_fifo(const struct device *dev,
96101

97102
static int xlnx_uartlite_poll_in(const struct device *dev, unsigned char *c)
98103
{
99-
if (xlnx_uartlite_read_status(dev) & STAT_REG_RX_FIFO_VALID_DATA) {
104+
uint32_t status;
105+
k_spinlock_key_t key;
106+
struct xlnx_uartlite_data *data = dev->data;
107+
int ret = -1;
108+
109+
key = k_spin_lock(&data->rx_lock);
110+
status = xlnx_uartlite_read_status(dev);
111+
if ((status & STAT_REG_RX_FIFO_VALID_DATA) != 0) {
100112
*c = xlnx_uartlite_read_rx_fifo(dev);
101-
return 0;
113+
ret = 0;
102114
}
115+
k_spin_unlock(&data->rx_lock, key);
103116

104-
return -1;
117+
return ret;
105118
}
106119

107120
static void xlnx_uartlite_poll_out(const struct device *dev, unsigned char c)
108121
{
109-
while (xlnx_uartlite_read_status(dev) & STAT_REG_TX_FIFO_FULL) {
110-
}
122+
uint32_t status;
123+
k_spinlock_key_t key;
124+
struct xlnx_uartlite_data *data = dev->data;
125+
bool done = false;
111126

112-
xlnx_uartlite_write_tx_fifo(dev, c);
127+
while (!done) {
128+
key = k_spin_lock(&data->tx_lock);
129+
status = xlnx_uartlite_read_status(dev);
130+
if ((status & STAT_REG_TX_FIFO_FULL) == 0) {
131+
xlnx_uartlite_write_tx_fifo(dev, c);
132+
done = true;
133+
}
134+
k_spin_unlock(&data->tx_lock, key);
135+
}
113136
}
114137

115138
static int xlnx_uartlite_err_check(const struct device *dev)
@@ -157,12 +180,18 @@ static int xlnx_uartlite_fifo_fill(const struct device *dev,
157180
const uint8_t *tx_data,
158181
int len)
159182
{
160-
uint32_t status = xlnx_uartlite_read_status(dev);
183+
uint32_t status;
184+
k_spinlock_key_t key;
185+
struct xlnx_uartlite_data *data = dev->data;
161186
int count = 0U;
162187

163-
while ((len - count > 0) && (status & STAT_REG_TX_FIFO_FULL) == 0U) {
164-
xlnx_uartlite_write_tx_fifo(dev, tx_data[count++]);
188+
while (len - count > 0) {
189+
key = k_spin_lock(&data->tx_lock);
165190
status = xlnx_uartlite_read_status(dev);
191+
if ((status & STAT_REG_TX_FIFO_FULL) == 0U) {
192+
xlnx_uartlite_write_tx_fifo(dev, tx_data[count++]);
193+
}
194+
k_spin_unlock(&data->tx_lock, key);
166195
}
167196

168197
return count;
@@ -171,12 +200,18 @@ static int xlnx_uartlite_fifo_fill(const struct device *dev,
171200
static int xlnx_uartlite_fifo_read(const struct device *dev, uint8_t *rx_data,
172201
const int len)
173202
{
174-
uint32_t status = xlnx_uartlite_read_status(dev);
203+
uint32_t status;
204+
k_spinlock_key_t key;
205+
struct xlnx_uartlite_data *data = dev->data;
175206
int count = 0U;
176207

177-
while ((len - count > 0) && (status & STAT_REG_RX_FIFO_VALID_DATA)) {
178-
rx_data[count++] = xlnx_uartlite_read_rx_fifo(dev);
208+
while ((len - count) > 0) {
209+
key = k_spin_lock(&data->rx_lock);
179210
status = xlnx_uartlite_read_status(dev);
211+
if ((status & STAT_REG_RX_FIFO_VALID_DATA) != 0) {
212+
rx_data[count++] = xlnx_uartlite_read_rx_fifo(dev);
213+
}
214+
k_spin_unlock(&data->rx_lock, key);
180215
}
181216

182217
return count;

0 commit comments

Comments
 (0)