drivers: serial: Add pm constraint setting to mcux lpuart driver
System entering sleep state before uart tx is complete can result in characters being dropped from the transmission. Add pm constraint setting to the lpuart driver to prevent character drops Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
This commit is contained in:
parent
e9ce5cd282
commit
6696d834cf
1 changed files with 112 additions and 5 deletions
|
@ -13,6 +13,7 @@
|
|||
#include <device.h>
|
||||
#include <drivers/uart.h>
|
||||
#include <drivers/clock_control.h>
|
||||
#include <pm/pm.h>
|
||||
|
||||
struct mcux_lpuart_config {
|
||||
LPUART_Type *base;
|
||||
|
@ -20,7 +21,7 @@ struct mcux_lpuart_config {
|
|||
clock_control_subsys_t clock_subsys;
|
||||
uint32_t baud_rate;
|
||||
uint8_t flow_ctrl;
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_PM)
|
||||
void (*irq_config_func)(const struct device *dev);
|
||||
#endif
|
||||
};
|
||||
|
@ -30,9 +31,36 @@ struct mcux_lpuart_data {
|
|||
uart_irq_callback_user_data_t callback;
|
||||
void *cb_data;
|
||||
#endif
|
||||
#ifdef CONFIG_PM
|
||||
bool pm_constraint_on;
|
||||
bool tx_poll_stream_on;
|
||||
bool tx_int_stream_on;
|
||||
#endif /* CONFIG_PM */
|
||||
struct uart_config uart_config;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void mcux_lpuart_pm_constraint_set(const struct device *dev)
|
||||
{
|
||||
struct mcux_lpuart_data *data = dev->data;
|
||||
|
||||
if (!data->pm_constraint_on) {
|
||||
data->pm_constraint_on = true;
|
||||
pm_constraint_set(PM_STATE_SUSPEND_TO_IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void mcux_lpuart_pm_constraint_release(const struct device *dev)
|
||||
{
|
||||
struct mcux_lpuart_data *data = dev->data;
|
||||
|
||||
if (data->pm_constraint_on) {
|
||||
data->pm_constraint_on = false;
|
||||
pm_constraint_release(PM_STATE_SUSPEND_TO_IDLE);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static int mcux_lpuart_poll_in(const struct device *dev, unsigned char *c)
|
||||
{
|
||||
const struct mcux_lpuart_config *config = dev->config;
|
||||
|
@ -50,12 +78,35 @@ static int mcux_lpuart_poll_in(const struct device *dev, unsigned char *c)
|
|||
static void mcux_lpuart_poll_out(const struct device *dev, unsigned char c)
|
||||
{
|
||||
const struct mcux_lpuart_config *config = dev->config;
|
||||
int key;
|
||||
#ifdef CONFIG_PM
|
||||
struct mcux_lpuart_data *data = dev->data;
|
||||
#endif
|
||||
|
||||
while (!(LPUART_GetStatusFlags(config->base)
|
||||
& kLPUART_TxDataRegEmptyFlag)) {
|
||||
}
|
||||
/* Lock interrupts while we send data */
|
||||
key = irq_lock();
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* We must keep the part from entering lower power mode until the
|
||||
* tranmission completes. Set the power constraint, and enable
|
||||
* the tranmission complete interrupt so we know when tranmission is
|
||||
* completed.
|
||||
*/
|
||||
if (!data->tx_poll_stream_on && !data->tx_int_stream_on) {
|
||||
data->tx_poll_stream_on = true;
|
||||
mcux_lpuart_pm_constraint_set(dev);
|
||||
/* Enable TC interrupt */
|
||||
LPUART_EnableInterrupts(config->base,
|
||||
kLPUART_TransmissionCompleteInterruptEnable);
|
||||
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
LPUART_WriteByte(config->base, c);
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
static int mcux_lpuart_err_check(const struct device *dev)
|
||||
|
@ -97,7 +148,6 @@ static int mcux_lpuart_fifo_fill(const struct device *dev,
|
|||
|
||||
LPUART_WriteByte(config->base, tx_data[num_tx++]);
|
||||
}
|
||||
|
||||
return num_tx;
|
||||
}
|
||||
|
||||
|
@ -121,16 +171,45 @@ static void mcux_lpuart_irq_tx_enable(const struct device *dev)
|
|||
{
|
||||
const struct mcux_lpuart_config *config = dev->config;
|
||||
uint32_t mask = kLPUART_TxDataRegEmptyInterruptEnable;
|
||||
#ifdef CONFIG_PM
|
||||
struct mcux_lpuart_data *data = dev->data;
|
||||
int key;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
key = irq_lock();
|
||||
data->tx_poll_stream_on = false;
|
||||
data->tx_int_stream_on = true;
|
||||
/* Do not allow system to sleep while UART tx is ongoing */
|
||||
mcux_lpuart_pm_constraint_set(dev);
|
||||
#endif
|
||||
LPUART_EnableInterrupts(config->base, mask);
|
||||
#ifdef CONFIG_PM
|
||||
irq_unlock(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void mcux_lpuart_irq_tx_disable(const struct device *dev)
|
||||
{
|
||||
const struct mcux_lpuart_config *config = dev->config;
|
||||
uint32_t mask = kLPUART_TxDataRegEmptyInterruptEnable;
|
||||
#ifdef CONFIG_PM
|
||||
struct mcux_lpuart_data *data = dev->data;
|
||||
int key;
|
||||
|
||||
key = irq_lock();
|
||||
#endif
|
||||
|
||||
LPUART_DisableInterrupts(config->base, mask);
|
||||
#ifdef CONFIG_PM
|
||||
data->tx_int_stream_on = false;
|
||||
/*
|
||||
* If transmission IRQ is no longer enabled,
|
||||
* transmission is complete. Release pm constraint.
|
||||
*/
|
||||
mcux_lpuart_pm_constraint_release(dev);
|
||||
irq_unlock(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int mcux_lpuart_irq_tx_complete(const struct device *dev)
|
||||
|
@ -225,15 +304,37 @@ static void mcux_lpuart_irq_callback_set(const struct device *dev,
|
|||
data->cb_data = cb_data;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
||||
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_PM)
|
||||
static void mcux_lpuart_isr(const struct device *dev)
|
||||
{
|
||||
struct mcux_lpuart_data *data = dev->data;
|
||||
#if CONFIG_PM
|
||||
const struct mcux_lpuart_config *config = dev->config;
|
||||
#endif
|
||||
|
||||
#if CONFIG_PM
|
||||
if (LPUART_GetStatusFlags(config->base) &
|
||||
kLPUART_TransmissionCompleteFlag) {
|
||||
|
||||
if (data->tx_poll_stream_on) {
|
||||
/* Poll transmission complete. Allow system to sleep */
|
||||
LPUART_DisableInterrupts(config->base,
|
||||
kLPUART_TransmissionCompleteInterruptEnable);
|
||||
data->tx_poll_stream_on = false;
|
||||
mcux_lpuart_pm_constraint_release(dev);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#if CONFIG_UART_INTERRUPT_DRIVEN
|
||||
if (data->callback) {
|
||||
data->callback(dev, data->cb_data);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN || CONFIG_PM */
|
||||
|
||||
static int mcux_lpuart_configure_init(const struct device *dev,
|
||||
const struct uart_config *cfg)
|
||||
|
@ -363,10 +464,16 @@ static int mcux_lpuart_init(const struct device *dev)
|
|||
/* set initial configuration */
|
||||
mcux_lpuart_configure_init(dev, uart_api_config);
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_PM)
|
||||
config->irq_config_func(dev);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
data->pm_constraint_on = false;
|
||||
data->tx_poll_stream_on = false;
|
||||
data->tx_int_stream_on = false;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -397,7 +504,7 @@ static const struct uart_driver_api mcux_lpuart_driver_api = {
|
|||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) || defined(CONFIG_PM)
|
||||
#define MCUX_LPUART_IRQ_INIT(n, i) \
|
||||
do { \
|
||||
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, i, irq), \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue