serial: uart_nrfx_uarte: graceful async power down
Gracefully shutdown the UARTE peripheral when the async API is used. Failure to do so results in the driver being unusable when powered back up as the required events (ENDTX & TXSTOPPED) are not set. This also ensures that the last byte sent out via `poll_out` is properly output on the serial line before powering down. Fixes #31930. Signed-off-by: Jordan Yates <jordan.yates@data61.csiro.au>
This commit is contained in:
parent
5979b0085f
commit
238067dfc1
1 changed files with 38 additions and 16 deletions
|
@ -424,6 +424,33 @@ static bool is_tx_ready(const struct device *dev)
|
|||
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX) : 0);
|
||||
}
|
||||
|
||||
/* Wait until the transmitter is in the idle state. When this function returns,
|
||||
* IRQ's are locked with the returned key.
|
||||
*/
|
||||
static int wait_tx_ready(const struct device *dev)
|
||||
{
|
||||
int key;
|
||||
|
||||
do {
|
||||
/* wait arbitrary time before back off. */
|
||||
bool res;
|
||||
|
||||
NRFX_WAIT_FOR(is_tx_ready(dev), 100, 1, res);
|
||||
|
||||
if (res) {
|
||||
key = irq_lock();
|
||||
if (is_tx_ready(dev)) {
|
||||
break;
|
||||
}
|
||||
|
||||
irq_unlock(key);
|
||||
}
|
||||
k_msleep(1);
|
||||
} while (1);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static void tx_start(NRF_UARTE_Type *uarte, const uint8_t *buf, size_t len)
|
||||
{
|
||||
nrf_uarte_tx_buffer_set(uarte, buf, len);
|
||||
|
@ -1148,22 +1175,7 @@ static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c)
|
|||
irq_unlock(key);
|
||||
}
|
||||
} else {
|
||||
do {
|
||||
/* wait arbitrary time before back off. */
|
||||
bool res;
|
||||
|
||||
NRFX_WAIT_FOR(is_tx_ready(dev), 100, 1, res);
|
||||
|
||||
if (res) {
|
||||
key = irq_lock();
|
||||
if (is_tx_ready(dev)) {
|
||||
break;
|
||||
}
|
||||
|
||||
irq_unlock(key);
|
||||
}
|
||||
k_msleep(1);
|
||||
} while (1);
|
||||
key = wait_tx_ready(dev);
|
||||
}
|
||||
|
||||
/* At this point we should have irq locked and any previous transfer
|
||||
|
@ -1587,6 +1599,16 @@ static void uarte_nrfx_set_power_state(const struct device *dev,
|
|||
data->async->rx_total_user_byte_cnt = 0;
|
||||
}
|
||||
if (get_dev_data(dev)->async) {
|
||||
/* Wait for the transmitter to go idle.
|
||||
* While the async API can wait for transmission to
|
||||
* complete before disabling the peripheral, the poll
|
||||
* API exits before the character is sent on the wire.
|
||||
* If the transmission is ongoing when the peripheral
|
||||
* is disabled, the ENDTX and TXSTOPPED events will
|
||||
* never be generated. This will block the driver in
|
||||
* any future calls to poll_out.
|
||||
*/
|
||||
irq_unlock(wait_tx_ready(dev));
|
||||
nrf_uarte_disable(uarte);
|
||||
uarte_nrfx_pins_enable(dev, false);
|
||||
return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue