drivers: serial: pm: Add power management support for Apollo3 SoCs UART
Add power management support for Apollo3/Apollo3P UART, and automatically enables device runtime power management Signed-off-by: Zhengwei Wang <zwang@ambiq.com>
This commit is contained in:
parent
1eb831efc3
commit
03a1fe6cd7
4 changed files with 150 additions and 28 deletions
|
@ -33,7 +33,17 @@
|
|||
#endif
|
||||
|
||||
#include "uart_pl011_registers.h"
|
||||
|
||||
#if defined(CONFIG_SOC_FAMILY_AMBIQ)
|
||||
#include "uart_pl011_ambiq.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SOC_SERIES_APOLLO3X)
|
||||
#define PM_INST_GET(n) PM_DEVICE_DT_INST_GET(n)
|
||||
#else
|
||||
#define PM_INST_GET(n) NULL
|
||||
#endif
|
||||
|
||||
#include "uart_pl011_raspberrypi_pico.h"
|
||||
|
||||
struct pl011_config {
|
||||
|
@ -648,33 +658,30 @@ void pl011_isr(const struct device *dev)
|
|||
};
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
||||
#define PL011_INIT(n) \
|
||||
PINCTRL_DEFINE(n) \
|
||||
COMPAT_SPECIFIC_DEFINE(n) \
|
||||
PL011_CONFIG_PORT(n) \
|
||||
\
|
||||
static struct pl011_data pl011_data_port_##n = { \
|
||||
.uart_cfg = { \
|
||||
.baudrate = DT_INST_PROP(n, current_speed), \
|
||||
.parity = UART_CFG_PARITY_NONE, \
|
||||
.stop_bits = UART_CFG_STOP_BITS_1, \
|
||||
.data_bits = UART_CFG_DATA_BITS_8, \
|
||||
.flow_ctrl = DT_INST_PROP(n, hw_flow_control) \
|
||||
? UART_CFG_FLOW_CTRL_RTS_CTS \
|
||||
: UART_CFG_FLOW_CTRL_NONE, \
|
||||
}, \
|
||||
.clk_freq = COND_CODE_1( \
|
||||
DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(n), fixed_clock), \
|
||||
(DT_INST_PROP_BY_PHANDLE(n, clocks, clock_frequency)), (0)), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, pl011_init, \
|
||||
NULL, \
|
||||
&pl011_data_port_##n, \
|
||||
&pl011_cfg_port_##n, \
|
||||
PRE_KERNEL_1, \
|
||||
CONFIG_SERIAL_INIT_PRIORITY, \
|
||||
&pl011_driver_api);
|
||||
#define PL011_INIT(n) \
|
||||
PINCTRL_DEFINE(n) \
|
||||
COMPAT_SPECIFIC_DEFINE(n) \
|
||||
PL011_CONFIG_PORT(n) \
|
||||
\
|
||||
static struct pl011_data pl011_data_port_##n = { \
|
||||
.uart_cfg = \
|
||||
{ \
|
||||
.baudrate = DT_INST_PROP(n, current_speed), \
|
||||
.parity = UART_CFG_PARITY_NONE, \
|
||||
.stop_bits = UART_CFG_STOP_BITS_1, \
|
||||
.data_bits = UART_CFG_DATA_BITS_8, \
|
||||
.flow_ctrl = DT_INST_PROP(n, hw_flow_control) \
|
||||
? UART_CFG_FLOW_CTRL_RTS_CTS \
|
||||
: UART_CFG_FLOW_CTRL_NONE, \
|
||||
}, \
|
||||
.clk_freq = \
|
||||
COND_CODE_1(DT_NODE_HAS_COMPAT(DT_INST_CLOCKS_CTLR(n), fixed_clock), \
|
||||
(DT_INST_PROP_BY_PHANDLE(n, clocks, clock_frequency)), (0)), \
|
||||
}; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, pl011_init, PM_INST_GET(n), &pl011_data_port_##n, \
|
||||
&pl011_cfg_port_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
|
||||
&pl011_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(PL011_INIT)
|
||||
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/policy.h>
|
||||
|
||||
#include "uart_pl011_registers.h"
|
||||
#include <am_mcu_apollo.h>
|
||||
|
||||
#define PWRCTRL_MAX_WAIT_US 5
|
||||
|
||||
|
@ -50,6 +53,113 @@ static inline int clk_enable_ambiq_uart(const struct device *dev, uint32_t clk)
|
|||
return pl011_ambiq_clk_set(dev, clk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
|
||||
/* Register status record.
|
||||
* The register status will be preserved to this variable before entering sleep mode,
|
||||
* and they will be restored after wake up.
|
||||
*/
|
||||
typedef struct {
|
||||
bool bValid;
|
||||
uint32_t regILPR;
|
||||
uint32_t regIBRD;
|
||||
uint32_t regFBRD;
|
||||
uint32_t regLCRH;
|
||||
uint32_t regCR;
|
||||
uint32_t regIFLS;
|
||||
uint32_t regIER;
|
||||
} uart_register_state_t;
|
||||
static uart_register_state_t sRegState[2];
|
||||
|
||||
static int uart_ambiq_pm_action(const struct device *dev, enum pm_device_action action)
|
||||
{
|
||||
int key;
|
||||
|
||||
/*Uart module number*/
|
||||
uint32_t ui32Module = ((uint32_t)get_uart(dev) == UART0_BASE) ? 0 : 1;
|
||||
|
||||
/*Uart Power module*/
|
||||
am_hal_pwrctrl_periph_e eUARTPowerModule =
|
||||
((am_hal_pwrctrl_periph_e)(AM_HAL_PWRCTRL_PERIPH_UART0 + ui32Module));
|
||||
|
||||
/*Uart register status*/
|
||||
uart_register_state_t *pRegisterStatus = &sRegState[ui32Module];
|
||||
|
||||
/* Decode the requested power state and update UART operation accordingly.*/
|
||||
switch (action) {
|
||||
|
||||
/* Turn on the UART. */
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
|
||||
/* Make sure we don't try to restore an invalid state.*/
|
||||
if (!pRegisterStatus->bValid) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/*The resume and suspend actions may be executed back-to-back,
|
||||
* so we add a busy wait here for stabilization.
|
||||
*/
|
||||
k_busy_wait(100);
|
||||
|
||||
/* Enable power control.*/
|
||||
am_hal_pwrctrl_periph_enable(eUARTPowerModule);
|
||||
|
||||
/* Restore UART registers*/
|
||||
key = irq_lock();
|
||||
|
||||
UARTn(ui32Module)->ILPR = pRegisterStatus->regILPR;
|
||||
UARTn(ui32Module)->IBRD = pRegisterStatus->regIBRD;
|
||||
UARTn(ui32Module)->FBRD = pRegisterStatus->regFBRD;
|
||||
UARTn(ui32Module)->LCRH = pRegisterStatus->regLCRH;
|
||||
UARTn(ui32Module)->CR = pRegisterStatus->regCR;
|
||||
UARTn(ui32Module)->IFLS = pRegisterStatus->regIFLS;
|
||||
UARTn(ui32Module)->IER = pRegisterStatus->regIER;
|
||||
pRegisterStatus->bValid = false;
|
||||
|
||||
irq_unlock(key);
|
||||
|
||||
return 0;
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
|
||||
while ((get_uart(dev)->fr & PL011_FR_BUSY) != 0)
|
||||
;
|
||||
|
||||
/* Preserve UART registers*/
|
||||
key = irq_lock();
|
||||
|
||||
pRegisterStatus->regILPR = UARTn(ui32Module)->ILPR;
|
||||
pRegisterStatus->regIBRD = UARTn(ui32Module)->IBRD;
|
||||
pRegisterStatus->regFBRD = UARTn(ui32Module)->FBRD;
|
||||
pRegisterStatus->regLCRH = UARTn(ui32Module)->LCRH;
|
||||
pRegisterStatus->regCR = UARTn(ui32Module)->CR;
|
||||
pRegisterStatus->regIFLS = UARTn(ui32Module)->IFLS;
|
||||
pRegisterStatus->regIER = UARTn(ui32Module)->IER;
|
||||
pRegisterStatus->bValid = true;
|
||||
|
||||
irq_unlock(key);
|
||||
|
||||
/* Clear all interrupts before sleeping as having a pending UART
|
||||
* interrupt burns power.
|
||||
*/
|
||||
UARTn(ui32Module)->IEC = 0xFFFFFFFF;
|
||||
|
||||
/* If the user is going to sleep, certain bits of the CR register
|
||||
* need to be 0 to be low power and have the UART shut off.
|
||||
* Since the user either wishes to retain state which takes place
|
||||
* above or the user does not wish to retain state, it is acceptable
|
||||
* to set the entire CR register to 0.
|
||||
*/
|
||||
UARTn(ui32Module)->CR = 0;
|
||||
|
||||
/* Disable power control.*/
|
||||
am_hal_pwrctrl_periph_disable(eUARTPowerModule);
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
/* Problem: writes to power configure register takes some time to take effective.
|
||||
* Solution: Check device's power status to ensure that register has taken effective.
|
||||
* Note: busy wait is not allowed to use here due to UART is initiated before timer starts.
|
||||
|
@ -57,7 +167,8 @@ static inline int clk_enable_ambiq_uart(const struct device *dev, uint32_t clk)
|
|||
#if defined(CONFIG_SOC_SERIES_APOLLO3X)
|
||||
#define DEVPWRSTATUS_OFFSET 0x10
|
||||
#define HCPA_MASK 0x4
|
||||
#define AMBIQ_UART_DEFINE(n) \
|
||||
#define AMBIQ_UART_DEFINE(n) \
|
||||
PM_DEVICE_DT_INST_DEFINE(n, uart_ambiq_pm_action); \
|
||||
static int pwr_on_ambiq_uart_##n(void) \
|
||||
{ \
|
||||
uint32_t addr = DT_REG_ADDR(DT_INST_PHANDLE(n, ambiq_pwrcfg)) + \
|
||||
|
|
|
@ -175,6 +175,7 @@
|
|||
status = "disabled";
|
||||
clocks = <&uartclk>;
|
||||
ambiq,pwrcfg = <&pwrcfg 0x8 0x80>;
|
||||
zephyr,pm-device-runtime-auto;
|
||||
};
|
||||
|
||||
uart1: uart@4001d000 {
|
||||
|
@ -185,6 +186,7 @@
|
|||
status = "disabled";
|
||||
clocks = <&uartclk>;
|
||||
ambiq,pwrcfg = <&pwrcfg 0x8 0x100>;
|
||||
zephyr,pm-device-runtime-auto;
|
||||
};
|
||||
|
||||
spi0: spi@50004000 {
|
||||
|
|
|
@ -193,6 +193,7 @@
|
|||
status = "disabled";
|
||||
clocks = <&uartclk>;
|
||||
ambiq,pwrcfg = <&pwrcfg 0x8 0x80>;
|
||||
zephyr,pm-device-runtime-auto;
|
||||
};
|
||||
|
||||
uart1: uart@4001d000 {
|
||||
|
@ -203,6 +204,7 @@
|
|||
status = "disabled";
|
||||
clocks = <&uartclk>;
|
||||
ambiq,pwrcfg = <&pwrcfg 0x8 0x100>;
|
||||
zephyr,pm-device-runtime-auto;
|
||||
};
|
||||
|
||||
spi0: spi@50004000 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue