uart_qmsi: Implement suspend and resume functions
In order to restore uart context after entering SYS_PM_DEEP_SLEEP, suspend and resume functions are called. The following parameters are restored: - All registers for the UART device. - UART clocks. The FIFO control register cannot be read back and is not stored in the device configuration. The default parameters are applied for them. The suspend/resume functionality is implemented in the QMSI shim layer as a fast and temporary solution, it will be removed and migrated to QMSI later. Change-Id: I4be9246f6aa5a6e0d91df54c1c69574060136607 Signed-off-by: Julien Delayen <julien.delayen@intel.com>
This commit is contained in:
parent
ce26aeeba8
commit
bc60506d3e
1 changed files with 83 additions and 14 deletions
|
@ -58,40 +58,105 @@ struct uart_qmsi_drv_data {
|
||||||
uart_irq_callback_t user_cb;
|
uart_irq_callback_t user_cb;
|
||||||
uint8_t iir_cache;
|
uint8_t iir_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define uart_qmsi_pm_save_config(dev, cfg) do { } while ((0))
|
|
||||||
#else
|
#else
|
||||||
|
struct uart_context_t {
|
||||||
|
uint32_t ier; /**< Interrupt Enable Register. */
|
||||||
|
uint32_t dlh; /**< Divisor Latch High. */
|
||||||
|
uint32_t dll; /**< Divisor Latch Low. */
|
||||||
|
uint32_t lcr; /**< Line Control. */
|
||||||
|
uint32_t mcr; /**< Modem Control. */
|
||||||
|
uint32_t scr; /**< Scratchpad. */
|
||||||
|
uint32_t htx; /**< Halt Transmission. */
|
||||||
|
uint32_t dlf; /**< Divisor Latch Fraction. */
|
||||||
|
uint32_t int_uart_mask; /**< Interrupt Mask. */
|
||||||
|
};
|
||||||
|
|
||||||
struct uart_qmsi_drv_data {
|
struct uart_qmsi_drv_data {
|
||||||
uart_irq_callback_t user_cb;
|
uart_irq_callback_t user_cb;
|
||||||
uint8_t iir_cache;
|
uint8_t iir_cache;
|
||||||
qm_uart_config_t cfg_save;
|
struct uart_context_t ctx_save;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline
|
static int uart_suspend_device(struct device *dev, int pm_policy)
|
||||||
void uart_qmsi_pm_save_config(struct device *dev, qm_uart_config_t *cfg)
|
|
||||||
{
|
{
|
||||||
struct uart_qmsi_drv_data *drv_data = dev->driver_data;
|
if (device_busy_check(dev)) {
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
drv_data->cfg_save = *cfg;
|
if (pm_policy == SYS_PM_DEEP_SLEEP) {
|
||||||
|
struct uart_qmsi_config_info *config = dev->config->config_info;
|
||||||
|
qm_uart_reg_t *const regs = QM_UART[config->instance];
|
||||||
|
struct uart_qmsi_drv_data *drv_data = dev->driver_data;
|
||||||
|
struct uart_context_t *const ctx_save = &drv_data->ctx_save;
|
||||||
|
|
||||||
|
if (config->instance == QM_UART_0) {
|
||||||
|
ctx_save->int_uart_mask = QM_SCSS_INT->int_uart_0_mask;
|
||||||
|
} else {
|
||||||
|
ctx_save->int_uart_mask = QM_SCSS_INT->int_uart_1_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx_save->ier = regs->ier_dlh;
|
||||||
|
ctx_save->lcr = regs->lcr;
|
||||||
|
ctx_save->mcr = regs->mcr;
|
||||||
|
ctx_save->scr = regs->scr;
|
||||||
|
ctx_save->htx = regs->htx;
|
||||||
|
ctx_save->dlf = regs->dlf;
|
||||||
|
|
||||||
|
/* When DLAB is set, DLL and DLH registers can be accessed. */
|
||||||
|
regs->lcr |= QM_UART_LCR_DLAB;
|
||||||
|
ctx_save->dlh = regs->ier_dlh;
|
||||||
|
ctx_save->dll = regs->rbr_thr_dll;
|
||||||
|
regs->lcr &= ~QM_UART_LCR_DLAB;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uart_resume_device(struct device *dev, int pm_policy)
|
static int uart_resume_device(struct device *dev, int pm_policy)
|
||||||
{
|
{
|
||||||
if (pm_policy == SYS_PM_DEEP_SLEEP) {
|
if (pm_policy == SYS_PM_DEEP_SLEEP) {
|
||||||
struct uart_qmsi_config_info *config = dev->config->config_info;
|
struct uart_qmsi_config_info *config = dev->config->config_info;
|
||||||
|
qm_uart_reg_t *const regs = QM_UART[config->instance];
|
||||||
struct uart_qmsi_drv_data *drv_data = dev->driver_data;
|
struct uart_qmsi_drv_data *drv_data = dev->driver_data;
|
||||||
|
struct uart_context_t *const ctx_save = &drv_data->ctx_save;
|
||||||
|
|
||||||
clk_periph_enable(config->clock_gate);
|
clk_periph_enable(config->clock_gate);
|
||||||
|
|
||||||
qm_uart_set_config(config->instance,
|
if (config->instance == QM_UART_0) {
|
||||||
&drv_data->cfg_save);
|
QM_SCSS_INT->int_uart_0_mask = ctx_save->int_uart_mask;
|
||||||
|
} else {
|
||||||
|
QM_SCSS_INT->int_uart_1_mask = ctx_save->int_uart_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When DLAB is set, DLL and DLH registers can be accessed. */
|
||||||
|
regs->lcr |= QM_UART_LCR_DLAB;
|
||||||
|
regs->ier_dlh = ctx_save->dlh;
|
||||||
|
regs->rbr_thr_dll = ctx_save->dll;
|
||||||
|
regs->lcr &= ~QM_UART_LCR_DLAB;
|
||||||
|
|
||||||
|
regs->ier_dlh = ctx_save->ier;
|
||||||
|
regs->lcr = ctx_save->lcr;
|
||||||
|
regs->mcr = ctx_save->mcr;
|
||||||
|
regs->scr = ctx_save->scr;
|
||||||
|
regs->htx = ctx_save->htx;
|
||||||
|
regs->dlf = ctx_save->dlf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIFO control register cannot be read back,
|
||||||
|
* default config is applied for this register.
|
||||||
|
* Application will need to restore its own parameters.
|
||||||
|
*/
|
||||||
|
regs->iir_fcr =
|
||||||
|
(QM_UART_FCR_FIFOE | QM_UART_FCR_RFIFOR |
|
||||||
|
QM_UART_FCR_XFIFOR |
|
||||||
|
QM_UART_FCR_DEFAULT_TX_RX_THRESHOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
|
||||||
|
|
||||||
DEFINE_DEVICE_PM_OPS(uart, device_pm_nop, uart_resume_device);
|
DEFINE_DEVICE_PM_OPS(uart, uart_suspend_device, uart_resume_device);
|
||||||
|
|
||||||
#ifdef CONFIG_UART_QMSI_0
|
#ifdef CONFIG_UART_QMSI_0
|
||||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||||
|
@ -202,6 +267,8 @@ static int uart_qmsi_fifo_fill(struct device *dev, const uint8_t *tx_data,
|
||||||
qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev);
|
qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
device_busy_set(dev);
|
||||||
|
|
||||||
for (i = 0; i < size && !is_tx_fifo_full(instance); i++) {
|
for (i = 0; i < size && !is_tx_fifo_full(instance); i++) {
|
||||||
QM_UART[instance]->rbr_thr_dll = tx_data[i];
|
QM_UART[instance]->rbr_thr_dll = tx_data[i];
|
||||||
}
|
}
|
||||||
|
@ -220,6 +287,8 @@ static int uart_qmsi_fifo_read(struct device *dev, uint8_t *rx_data,
|
||||||
qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev);
|
qm_uart_t instance = GET_CONTROLLER_INSTANCE(dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
device_busy_set(dev);
|
||||||
|
|
||||||
for (i = 0; i < size && is_data_ready(instance); i++) {
|
for (i = 0; i < size && is_data_ready(instance); i++) {
|
||||||
rx_data[i] = QM_UART[instance]->rbr_thr_dll;
|
rx_data[i] = QM_UART[instance]->rbr_thr_dll;
|
||||||
}
|
}
|
||||||
|
@ -321,11 +390,13 @@ static void uart_qmsi_irq_callback_set(struct device *dev,
|
||||||
|
|
||||||
static void uart_qmsi_isr(void *arg)
|
static void uart_qmsi_isr(void *arg)
|
||||||
{
|
{
|
||||||
struct device *dev = arg;
|
struct device *dev = arg;
|
||||||
struct uart_qmsi_drv_data *drv_data = dev->driver_data;
|
struct uart_qmsi_drv_data *drv_data = dev->driver_data;
|
||||||
|
|
||||||
if (drv_data->user_cb)
|
if (drv_data->user_cb)
|
||||||
drv_data->user_cb(dev);
|
drv_data->user_cb(dev);
|
||||||
|
|
||||||
|
device_busy_clear(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_UART_QMSI_0
|
#ifdef CONFIG_UART_QMSI_0
|
||||||
|
@ -427,8 +498,6 @@ static int uart_qmsi_init(struct device *dev)
|
||||||
|
|
||||||
qm_uart_set_config(config->instance, &cfg);
|
qm_uart_set_config(config->instance, &cfg);
|
||||||
|
|
||||||
uart_qmsi_pm_save_config(dev, &cfg);
|
|
||||||
|
|
||||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||||
config->irq_config_func(dev);
|
config->irq_config_func(dev);
|
||||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue