soc : mec1501 : Deep and light sleep example
Created MEC1501 deep and light sleep example for MCHP MEC1501. Modifications were made to SoC, board, timer, and hello world sample program. Power management split into SoC power implementing the interface and device power for device specific logic. Signed-off-by: Scott Worley <scott.worley@microchip.com>
This commit is contained in:
parent
cba129ae96
commit
60915c884f
9 changed files with 438 additions and 0 deletions
|
@ -134,4 +134,15 @@ endif # SPI_XEC_QMSPI
|
|||
|
||||
endif # SPI
|
||||
|
||||
# power management stuff
|
||||
if SYS_POWER_MANAGEMENT
|
||||
|
||||
config SYS_PM_MIN_RESIDENCY_SLEEP_1
|
||||
default 1000
|
||||
|
||||
config SYS_PM_MIN_RESIDENCY_DEEP_SLEEP_1
|
||||
default 2000
|
||||
|
||||
endif # SYS_POWER_MANAGEMENT
|
||||
|
||||
endif # BOARD_MEC15XXEVB_ASSY6853
|
||||
|
|
|
@ -24,3 +24,6 @@ CONFIG_I2C_INIT_PRIORITY=60
|
|||
CONFIG_ESPI=y
|
||||
|
||||
CONFIG_SPI=y
|
||||
|
||||
# power management stuff
|
||||
CONFIG_SOC_POWER_MANAGEMENT=y
|
||||
|
|
|
@ -295,6 +295,14 @@ static int board_pinmux_init(struct device *dev)
|
|||
#endif /* DT_INST_0_MICROCHIP_XEC_QMSPI */
|
||||
#endif /* CONFIG_SPI_XEC_QMSPI */
|
||||
|
||||
#ifdef CONFIG_SYS_PM_DEBUG
|
||||
/*
|
||||
* Deep sleep testing: Enable TEST_CLK_OUT on GPIO_060 function 2.
|
||||
* TEST_CLK_OUT is the PLL 48MHz conditioned output.
|
||||
*/
|
||||
pinmux_pin_set(portb, MCHP_GPIO_060, MCHP_GPIO_CTRL_MUX_F2);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,3 +7,8 @@
|
|||
zephyr_sources(
|
||||
soc.c
|
||||
)
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_SYS_POWER_MANAGEMENT
|
||||
device_power.c
|
||||
power.c
|
||||
)
|
||||
|
|
|
@ -88,4 +88,26 @@ config SPI_XEC_QMSPI
|
|||
|
||||
endif # SPI
|
||||
|
||||
if SOC_POWER_MANAGEMENT
|
||||
|
||||
config SYS_POWER_MANAGEMENT
|
||||
default y
|
||||
|
||||
config SYS_POWER_SLEEP_STATES
|
||||
default y
|
||||
|
||||
config HAS_SYS_POWER_STATE_SLEEP_1
|
||||
default y
|
||||
|
||||
config SYS_POWER_DEEP_SLEEP_STATES
|
||||
default y
|
||||
|
||||
config HAS_SYS_POWER_STATE_DEEP_SLEEP_1
|
||||
default y
|
||||
|
||||
config DEVICE_POWER_MANAGEMENT
|
||||
default n
|
||||
|
||||
endif # SOC_POWER_MANAGEMENT
|
||||
|
||||
endif # SOC_MEC1501_HSZ
|
||||
|
|
|
@ -16,6 +16,9 @@ endchoice
|
|||
config RTOS_TIMER
|
||||
bool "MEC1501 RTOS timer"
|
||||
|
||||
config SOC_POWER_MANAGEMENT
|
||||
bool "MEC1501 Power Management"
|
||||
|
||||
config SOC_MEC1501_PROC_CLK_DIV
|
||||
int "PROC_CLK_DIV"
|
||||
default 1
|
||||
|
|
219
soc/arm/microchip_mec/mec1501/device_power.c
Normal file
219
soc/arm/microchip_mec/mec1501/device_power.c
Normal file
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Microchip Technology Inc.
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <sys_io.h>
|
||||
#include <misc/__assert.h>
|
||||
#include <power.h>
|
||||
#include <soc.h>
|
||||
|
||||
/*
|
||||
* CPU will spin up to DEEP_SLEEP_WAIT_SPIN_CLK_REQ times
|
||||
* waiting for PCR CLK_REQ bits to clear except for the
|
||||
* CPU bit itself. This is not necessary as the sleep hardware
|
||||
* will wait for all CLK_REQ to clear once WFI has executed.
|
||||
* Once all CLK_REQ signals are clear the hardware will transition
|
||||
* to the low power state.
|
||||
*/
|
||||
/* #define DEEP_SLEEP_WAIT_ON_CLK_REQ_ENABLE */
|
||||
#define DEEP_SLEEP_WAIT_SPIN_CLK_REQ 1000
|
||||
|
||||
|
||||
/*
|
||||
* Some peripherals if enabled always assert their CLK_REQ bits.
|
||||
* For example, any peripheral with a clock generator such as
|
||||
* timers, counters, UART, etc. We save the enables for these
|
||||
* peripherals, disable them, and restore the enabled state upon
|
||||
* wake.
|
||||
*/
|
||||
#define DEEP_SLEEP_PERIPH_SAVE_RESTORE
|
||||
|
||||
|
||||
/*
|
||||
* Light sleep: PLL remains on. Fastest wake latency.
|
||||
*/
|
||||
void soc_lite_sleep_enable(void)
|
||||
{
|
||||
SCB->SCR &= ~(1ul << 2);
|
||||
PCR_REGS->SYS_SLP_CTRL = MCHP_PCR_SYS_SLP_LIGHT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deep sleep: PLL is turned off. Wake is fast. PLL requires
|
||||
* a minimum of 3ms to lock. During this time the main clock
|
||||
* will be ramping up from ~16 to 24 MHz.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_SYS_POWER_DEEP_SLEEP_STATES)
|
||||
|
||||
void soc_deep_sleep_enable(void)
|
||||
{
|
||||
SCB->SCR = (1ul << 2); /* Cortex-M4 SLEEPDEEP */
|
||||
PCR_REGS->SYS_SLP_CTRL = MCHP_PCR_SYS_SLP_HEAVY;
|
||||
}
|
||||
|
||||
void soc_deep_sleep_disable(void)
|
||||
{
|
||||
SCB->SCR &= ~(1ul << 2); /* disable Cortex-M4 SLEEPDEEP */
|
||||
}
|
||||
|
||||
|
||||
void soc_deep_sleep_wait_clk_idle(void)
|
||||
{
|
||||
#ifdef DEEP_SLEEP_WAIT_ON_CLK_REQ_ENABLE
|
||||
u32_t clkreq, cnt;
|
||||
|
||||
cnt = DEEP_SLEEP_WAIT_CLK_REQ;
|
||||
do {
|
||||
clkreq = PCR_REGS->CLK_REQ0 | PCR_REGS->CLK_REQ1
|
||||
| PCR_REGS->CLK_REQ2 | PCR_REGS->CLK_REQ3
|
||||
| PCR_REGS->CLK_REQ4;
|
||||
} while ((clkreq != (1ul << MCHP_PCR1_CPU_POS)) && (cnt-- != 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allow peripherals connected to external masters to wake the PLL but not
|
||||
* the EC. Once the peripheral has serviced the external master the PLL
|
||||
* will be turned back off. For example, if the eSPI master requests eSPI
|
||||
* configuration information or state of virtual wires the EC doesn't need
|
||||
* to be involved. The hardware can power on the PLL long enough to service
|
||||
* the request and then turn the PLL back off. The SMBus and I2C peripherals
|
||||
* in slave mode can also make use of this feature.
|
||||
*/
|
||||
void soc_deep_sleep_non_wake_en(void)
|
||||
{
|
||||
#ifdef CONFIG_ESPI_XEC
|
||||
GIRQ22_REGS->SRC = 0xfffffffful;
|
||||
GIRQ22_REGS->EN_SET = (1ul << 9);
|
||||
#endif
|
||||
}
|
||||
|
||||
void soc_deep_sleep_non_wake_dis(void)
|
||||
{
|
||||
#ifdef CONFIG_ESPI_XEC
|
||||
GIRQ22_REGS->EN_CLR = 0xfffffffful;
|
||||
GIRQ22_REGS->SRC = 0xfffffffful;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Variables used to save various HW state */
|
||||
#ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE
|
||||
|
||||
static u32_t ecs[1];
|
||||
|
||||
static void deep_sleep_save_ecs(void)
|
||||
{
|
||||
ecs[0] = ECS_REGS->ETM_CTRL;
|
||||
ECS_REGS->ETM_CTRL = 0;
|
||||
}
|
||||
|
||||
struct ds_timer_info {
|
||||
uintptr_t addr;
|
||||
u32_t restore_mask;
|
||||
};
|
||||
|
||||
const struct ds_timer_info ds_timer_tbl[] = {
|
||||
{
|
||||
(uintptr_t)&B16TMR0_REGS->CTRL, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)&B16TMR1_REGS->CTRL, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)&B32TMR0_REGS->CTRL, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)&B32TMR1_REGS->CTRL, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)&CCT_REGS->CTRL,
|
||||
(MCHP_CCT_CTRL_COMP1_SET | MCHP_CCT_CTRL_COMP0_SET),
|
||||
},
|
||||
};
|
||||
#define NUM_DS_TIMER_ENTRIES \
|
||||
(sizeof(ds_timer_tbl) / sizeof(struct ds_timer_info))
|
||||
|
||||
|
||||
static u32_t timers[NUM_DS_TIMER_ENTRIES];
|
||||
static u8_t uart_activate[3];
|
||||
|
||||
static void deep_sleep_save_uarts(void)
|
||||
{
|
||||
uart_activate[0] = UART0_REGS->ACTV;
|
||||
UART0_REGS->ACTV = 0;
|
||||
uart_activate[1] = UART1_REGS->ACTV;
|
||||
UART1_REGS->ACTV = 0;
|
||||
uart_activate[2] = UART2_REGS->ACTV;
|
||||
UART2_REGS->ACTV = 0;
|
||||
}
|
||||
|
||||
static void deep_sleep_save_timers(void)
|
||||
{
|
||||
const struct ds_timer_info *p;
|
||||
u32_t i;
|
||||
|
||||
p = &ds_timer_tbl[0];
|
||||
for (i = 0; i < NUM_DS_TIMER_ENTRIES; i++) {
|
||||
timers[i] = REG32(p->addr);
|
||||
REG32(p->addr) = 0;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static void deep_sleep_restore_ecs(void)
|
||||
{
|
||||
ECS_REGS->ETM_CTRL = ecs[0];
|
||||
}
|
||||
|
||||
static void deep_sleep_restore_uarts(void)
|
||||
{
|
||||
UART0_REGS->ACTV = uart_activate[0];
|
||||
UART1_REGS->ACTV = uart_activate[1];
|
||||
UART2_REGS->ACTV = uart_activate[2];
|
||||
}
|
||||
|
||||
static void deep_sleep_restore_timers(void)
|
||||
{
|
||||
const struct ds_timer_info *p;
|
||||
u32_t i;
|
||||
|
||||
p = &ds_timer_tbl[0];
|
||||
for (i = 0; i < NUM_DS_TIMER_ENTRIES; i++) {
|
||||
REG32(p->addr) = timers[i] & ~p->restore_mask;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
void soc_deep_sleep_periph_save(void)
|
||||
{
|
||||
deep_sleep_save_uarts();
|
||||
deep_sleep_save_ecs();
|
||||
deep_sleep_save_timers();
|
||||
}
|
||||
|
||||
void soc_deep_sleep_periph_restore(void)
|
||||
{
|
||||
deep_sleep_restore_ecs();
|
||||
deep_sleep_restore_uarts();
|
||||
deep_sleep_restore_timers();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void soc_deep_sleep_periph_save(void)
|
||||
{
|
||||
}
|
||||
|
||||
void soc_deep_sleep_periph_restore(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* DEEP_SLEEP_PERIPH_SAVE_RESTORE */
|
||||
|
||||
#endif /* CONFIG_SYS_POWER_DEEP_SLEEP_STATES */
|
26
soc/arm/microchip_mec/mec1501/device_power.h
Normal file
26
soc/arm/microchip_mec/mec1501/device_power.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Microchip Technology Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __DEVICE_POWER_H
|
||||
#define __DEVICE_POWER_H
|
||||
|
||||
#ifndef _ASMLANGUAGE
|
||||
|
||||
void soc_lite_sleep_enable(void);
|
||||
|
||||
#if defined(CONFIG_SYS_POWER_DEEP_SLEEP_STATES)
|
||||
void soc_deep_sleep_enable(void);
|
||||
void soc_deep_sleep_disable(void);
|
||||
void soc_deep_sleep_periph_save(void);
|
||||
void soc_deep_sleep_periph_restore(void);
|
||||
void soc_deep_sleep_wait_clk_idle(void);
|
||||
void soc_deep_sleep_non_wake_en(void);
|
||||
void soc_deep_sleep_non_wake_dis(void);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
141
soc/arm/microchip_mec/mec1501/power.c
Normal file
141
soc/arm/microchip_mec/mec1501/power.c
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Microchip Technology Inc.
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <sys_io.h>
|
||||
#include <misc/__assert.h>
|
||||
#include <power.h>
|
||||
#include <soc.h>
|
||||
#include "device_power.h"
|
||||
|
||||
#if defined(CONFIG_SYS_POWER_DEEP_SLEEP_STATES)
|
||||
|
||||
/*
|
||||
* Deep Sleep
|
||||
* Pros:
|
||||
* Lower power dissipation, 48MHz PLL is off
|
||||
* Cons:
|
||||
* Longer wake latency. CPU start running on ring oscillator
|
||||
* between 16 to 25 MHz. Minimum 3ms until PLL reaches lock
|
||||
* frequency of 48MHz.
|
||||
*
|
||||
* Implementation Notes:
|
||||
* We touch the Cortex-M's primary mask and base priority registers
|
||||
* because we do not want to enter an ISR immediately upon wake.
|
||||
* We must restore any hardware state that was modified upon sleep
|
||||
* entry before allowing interrupts to be serviced. Zephyr arch level
|
||||
* does not provide API's to manipulate both primary mask and base priority.
|
||||
*
|
||||
* DEBUG NOTES:
|
||||
* If a JTAG/SWD debug probe is connected driving TRST# high and
|
||||
* possibly polling the DUT then MEC1501 will not shut off its 48MHz
|
||||
* PLL. Firmware should not disable JTAG/SWD in the EC subsystem
|
||||
* while a probe is using the interface. This can leave the JTAG/SWD
|
||||
* TAP controller in a state of requesting clocks preventing the PLL
|
||||
* from being shut off.
|
||||
*/
|
||||
static void z_power_soc_deep_sleep(void)
|
||||
{
|
||||
u32_t base_pri;
|
||||
|
||||
/* Mask all exceptions and interrupts except NMI and HardFault */
|
||||
__set_PRIMASK(1);
|
||||
|
||||
soc_deep_sleep_periph_save();
|
||||
|
||||
soc_deep_sleep_enable();
|
||||
|
||||
soc_deep_sleep_wait_clk_idle();
|
||||
soc_deep_sleep_non_wake_en();
|
||||
|
||||
/*
|
||||
* Unmask all interrupts in BASEPRI. PRIMASK is used above to
|
||||
* prevent entering an ISR after unmasking in BASEPRI.
|
||||
* We clear PRIMASK in exit post ops.
|
||||
*/
|
||||
base_pri = __get_BASEPRI();
|
||||
__set_BASEPRI(0);
|
||||
__DSB();
|
||||
__WFI(); /* triggers sleep hardware */
|
||||
__NOP();
|
||||
__NOP();
|
||||
|
||||
if (base_pri != 0) {
|
||||
__set_BASEPRI(base_pri);
|
||||
}
|
||||
|
||||
soc_deep_sleep_disable();
|
||||
|
||||
soc_deep_sleep_non_wake_dis();
|
||||
|
||||
soc_deep_sleep_periph_restore();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Light Sleep
|
||||
* Pros:
|
||||
* Fast wake response:
|
||||
* Cons:
|
||||
* Higher power dissipation, 48MHz PLL remains on.
|
||||
*/
|
||||
static void z_power_soc_sleep(void)
|
||||
{
|
||||
__set_PRIMASK(1);
|
||||
|
||||
soc_lite_sleep_enable();
|
||||
|
||||
__set_BASEPRI(0); /* Make sure wake interrupts are not masked! */
|
||||
__DSB();
|
||||
__WFI(); /* triggers sleep hardware */
|
||||
__NOP();
|
||||
__NOP();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from _sys_suspend(s32_t ticks) in subsys/power.c
|
||||
* For deep sleep _sys_suspend has executed all the driver
|
||||
* power management call backs.
|
||||
*/
|
||||
void sys_set_power_state(enum power_states state)
|
||||
{
|
||||
switch (state) {
|
||||
#if (defined(CONFIG_SYS_POWER_SLEEP_STATES))
|
||||
case SYS_POWER_STATE_SLEEP_1:
|
||||
z_power_soc_sleep();
|
||||
break;
|
||||
#endif
|
||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP_STATES))
|
||||
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
||||
z_power_soc_deep_sleep();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _sys_pm_power_state_exit_post_ops(enum power_states state)
|
||||
{
|
||||
switch (state) {
|
||||
#if (defined(CONFIG_SYS_POWER_SLEEP_STATES))
|
||||
case SYS_POWER_STATE_SLEEP_1:
|
||||
__enable_irq();
|
||||
break;
|
||||
#endif
|
||||
#if (defined(CONFIG_SYS_POWER_DEEP_SLEEP_STATES))
|
||||
case SYS_POWER_STATE_DEEP_SLEEP_1:
|
||||
__enable_irq();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue