Skip to content

Commit 26d8714

Browse files
SgrrZhfcarlescufi
authored andcommitted
driver: uart: pl011: fix interrupt driven API
API function: - `pl011_irq_tx_enable` is expected to enable and trigger TX interrupt. Due to HW limiation, PL011 won't trigger TX interrupt if some data wasn't filled to TX FIFO at the beginning. So that `isr_cb` must be called at first time to enable TX irq. - `pl011_irq_tx_ready` will return true when FIFO can accept more data. Here we don't need wait TX FIFO to be empty. - `pl011_irq_tx_complete` will return true when all data have been sent from the shift register. Signed-off-by: Huifeng Zhang <[email protected]>
1 parent 0da7e06 commit 26d8714

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

drivers/serial/uart_pl011.c

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct pl011_data {
6565
uint32_t baud_rate; /* Baud rate */
6666
bool sbsa; /* SBSA mode */
6767
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
68+
volatile bool sw_call_txdrdy;
6869
uart_irq_callback_user_data_t irq_cb;
6970
void *irq_cb_data;
7071
#endif
@@ -134,10 +135,10 @@ struct pl011_data {
134135
#define PL011_CR_CTSEn BIT(15) /* CTS hw flow control enable */
135136

136137
/* PL011 Interrupt Fifo Level Select Register */
137-
#define PL011_IFLS_TXIFLSEL_SHIFT 0 /* bits 2:0 */
138-
#define PL011_IFLS_TXIFLSEL_WIDTH 3
139-
#define PL011_IFLS_RXIFLSEL_SHIFT 3 /* bits 5:3 */
140-
#define PL011_IFLS_RXIFLSEL_WIDTH 3
138+
#define PL011_IFLS_RXIFLSEL_M GENMASK(5, 3)
139+
#define RXIFLSEL_1_2_FULL 2UL
140+
#define PL011_IFLS_TXIFLSEL_M GENMASK(2, 0)
141+
#define TXIFLSEL_1_8_FULL 0UL
141142

142143
/* PL011 Interrupt Mask Set/Clear Register */
143144
#define PL011_IMSC_RIMIM BIT(0) /* RTR modem interrupt mask */
@@ -163,6 +164,9 @@ struct pl011_data {
163164
PL011_IMSC_RXIM | PL011_IMSC_TXIM | \
164165
PL011_IMSC_RTIM)
165166

167+
/* PL011 Raw Interrupt Status Register */
168+
#define PL011_RIS_TXRIS BIT(5) /* Transmit interrupt status */
169+
166170
static inline
167171
volatile struct pl011_regs *const get_uart(const struct device *dev)
168172
{
@@ -281,7 +285,28 @@ static int pl011_fifo_read(const struct device *dev,
281285

282286
static void pl011_irq_tx_enable(const struct device *dev)
283287
{
288+
struct pl011_data *data = dev->data;
289+
284290
get_uart(dev)->imsc |= PL011_IMSC_TXIM;
291+
if (data->sw_call_txdrdy) {
292+
/* Verify if the callback has been registered */
293+
if (data->irq_cb) {
294+
/*
295+
* Due to HW limitation, the first TX interrupt should
296+
* be triggered by the software.
297+
*
298+
* PL011 TX interrupt is based on a transition through
299+
* a level, rather than on the level itself[1]. So that,
300+
* enable TX interrupt can not trigger TX interrupt if
301+
* no data was filled to TX FIFO at the beginning.
302+
*
303+
* [1]: PrimeCell UART (PL011) Technical Reference Manual
304+
* functional-overview/interrupts
305+
*/
306+
data->irq_cb(dev, data->irq_cb_data);
307+
}
308+
data->sw_call_txdrdy = false;
309+
}
285310
}
286311

287312
static void pl011_irq_tx_disable(const struct device *dev)
@@ -291,8 +316,8 @@ static void pl011_irq_tx_disable(const struct device *dev)
291316

292317
static int pl011_irq_tx_complete(const struct device *dev)
293318
{
294-
/* check for TX FIFO empty */
295-
return get_uart(dev)->fr & PL011_FR_TXFE;
319+
/* Check for UART is busy transmitting data. */
320+
return ((get_uart(dev)->fr & PL011_FR_BUSY) == 0);
296321
}
297322

298323
static int pl011_irq_tx_ready(const struct device *dev)
@@ -303,7 +328,8 @@ static int pl011_irq_tx_ready(const struct device *dev)
303328
return false;
304329

305330
return ((get_uart(dev)->imsc & PL011_IMSC_TXIM) &&
306-
pl011_irq_tx_complete(dev));
331+
/* Check for TX interrupt status is set or TX FIFO is empty. */
332+
(get_uart(dev)->ris & PL011_RIS_TXRIS || get_uart(dev)->fr & PL011_FR_TXFE));
307333
}
308334

309335
static void pl011_irq_rx_enable(const struct device *dev)
@@ -418,6 +444,10 @@ static int pl011_init(const struct device *dev)
418444
lcrh |= PL011_LCRH_WLEN_SIZE(8) << PL011_LCRH_WLEN_SHIFT;
419445
get_uart(dev)->lcr_h = lcrh;
420446

447+
/* Setting transmit and receive interrupt FIFO level */
448+
get_uart(dev)->ifls = FIELD_PREP(PL011_IFLS_TXIFLSEL_M, TXIFLSEL_1_8_FULL)
449+
| FIELD_PREP(PL011_IFLS_RXIFLSEL_M, RXIFLSEL_1_2_FULL);
450+
421451
/* Enabling the FIFOs */
422452
pl011_enable_fifo(dev);
423453
}
@@ -434,6 +464,7 @@ static int pl011_init(const struct device *dev)
434464
}
435465
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
436466
config->irq_config_func(dev);
467+
data->sw_call_txdrdy = true;
437468
#endif
438469
if (!data->sbsa) {
439470
pl011_enable(dev);

0 commit comments

Comments
 (0)