drivers/serial: uart_ns16550: add power management constraint API

Instead of busy wait until completed transaction, the constraint set
is used before enabling tx interrupt to not allow system to enter
suspend when tx is transmitting.

Application defined policy should use the pm_constraint_get function
to check if given state is enabled and could be used.

TEST=Test on hayato board(soc:it8xxx2), the logs print normally before
system enters suspend.

Signed-off-by: Tim Lin <tim2.lin@ite.corp-partner.google.com>
This commit is contained in:
Tim Lin 2021-11-17 11:08:48 +08:00 committed by Carles Cufí
commit f52ff1a79e

View file

@ -33,6 +33,7 @@
#include <toolchain.h>
#include <linker/sections.h>
#include <drivers/uart.h>
#include <pm/pm.h>
#include <sys/sys_io.h>
#include <spinlock.h>
@ -274,6 +275,10 @@ struct uart_ns16550_dev_data {
#if UART_NS16550_DLF_ENABLED
uint8_t dlf; /**< DLF value */
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) && defined(CONFIG_PM)
bool tx_stream_on;
#endif
};
#if defined(UART_REG_ADDR_INTERVAL)
@ -297,6 +302,11 @@ static inline uint8_t reg_interval(const struct device *dev)
#define reg_interval(dev) DEFAULT_REG_INTERVAL
#endif
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) && defined(CONFIG_PM)
static const enum pm_state pm_states[] =
PM_STATE_DT_ITEMS_LIST(DT_NODELABEL(cpu0));
#endif
static const struct uart_driver_api uart_ns16550_driver_api;
static inline uintptr_t get_port(const struct device *dev)
@ -657,6 +667,21 @@ static void uart_ns16550_irq_tx_enable(const struct device *dev)
{
k_spinlock_key_t key = k_spin_lock(&DEV_DATA(dev)->lock);
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) && defined(CONFIG_PM)
struct uart_ns16550_dev_data *const dev_data = dev->data;
if (!dev_data->tx_stream_on) {
dev_data->tx_stream_on = true;
/*
* Power state to be disabled. Some platforms have multiple
* states and need to be given a constraint set according to
* different states.
*/
for (size_t i = 0U; i < ARRAY_SIZE(pm_states); i++) {
pm_constraint_set(pm_states[i]);
}
}
#endif
OUTBYTE(IER(dev), INBYTE(IER(dev)) | IER_TBE);
k_spin_unlock(&DEV_DATA(dev)->lock, key);
@ -675,6 +700,21 @@ static void uart_ns16550_irq_tx_disable(const struct device *dev)
OUTBYTE(IER(dev), INBYTE(IER(dev)) & (~IER_TBE));
#if defined(CONFIG_UART_INTERRUPT_DRIVEN) && defined(CONFIG_PM)
struct uart_ns16550_dev_data *const dev_data = dev->data;
if (dev_data->tx_stream_on) {
dev_data->tx_stream_on = false;
/*
* Power state to be enabled. Some platforms have multiple
* states and need to be given a constraint release according
* to different states.
*/
for (size_t i = 0U; i < ARRAY_SIZE(pm_states); i++) {
pm_constraint_release(pm_states[i]);
}
}
#endif
k_spin_unlock(&DEV_DATA(dev)->lock, key);
}