soc: pm: Microchip MEC172x SoC based power management
Add support for SoC power management for Microchip MEC172x. Signed-off-by: Jay Vasanth <jay.vasanth@microchip.com>
This commit is contained in:
parent
a8bffd9302
commit
de5296203a
|
@ -46,6 +46,20 @@
|
|||
label = "LED 5";
|
||||
};
|
||||
};
|
||||
|
||||
power-states {
|
||||
idle: idle {
|
||||
compatible = "zephyr,power-state";
|
||||
power-state-name = "suspend-to-idle";
|
||||
min-residency-us = <1000000>;
|
||||
};
|
||||
|
||||
suspend_to_ram: suspend_to_ram {
|
||||
compatible = "zephyr,power-state";
|
||||
power-state-name = "suspend-to-ram";
|
||||
min-residency-us = <2000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
&cpu0 {
|
||||
|
|
|
@ -9,6 +9,13 @@ zephyr_sources(
|
|||
soc.c
|
||||
)
|
||||
|
||||
if(CONFIG_PM)
|
||||
zephyr_library_sources(power.c)
|
||||
if(NOT CONFIG_PM_DEVICE)
|
||||
zephyr_library_sources(device_power.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_HAS_TIMING_FUNCTIONS AND NOT CONFIG_BOARD_HAS_TIMING_FUNCTIONS)
|
||||
if(CONFIG_TIMING_FUNCTIONS)
|
||||
# Use MEC172x timing calculations only if DWT is not present
|
||||
|
|
|
@ -43,4 +43,10 @@ config PS2_XEC
|
|||
default y
|
||||
depends on PS2
|
||||
|
||||
config PM
|
||||
default y if SYS_CLOCK_EXISTS
|
||||
|
||||
config PM_DEVICE
|
||||
default n
|
||||
|
||||
endif # SOC_SERIES_MEC172X
|
||||
|
|
|
@ -24,3 +24,8 @@ config SOC_MEC172X_PROC_CLK_DIV
|
|||
and main 96 MHz clock (MCK):
|
||||
HCLK = MCK / PROC_CLK_DIV
|
||||
Allowed divider values: 1, 3, 4, 16, and 48.
|
||||
|
||||
config SOC_MEC172X_TEST_CLK_OUT
|
||||
bool "Test clock out pin"
|
||||
help
|
||||
Enables a test clock out pin
|
||||
|
|
384
soc/arm/microchip_mec/mec172x/device_power.c
Normal file
384
soc/arm/microchip_mec/mec172x/device_power.c
Normal file
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Microchip Technology Inc.
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <sys/sys_io.h>
|
||||
#include <sys/__assert.h>
|
||||
#include <pm/pm.h>
|
||||
#include <soc.h>
|
||||
#include "device_power.h"
|
||||
|
||||
#define ADC_0_XEC_REG_BASE \
|
||||
((struct adc_regs *)(DT_REG_ADDR(DT_NODELABEL(adc0))))
|
||||
#define ECIA_XEC_REG_BASE \
|
||||
((struct ecia_named_regs *)(DT_REG_ADDR(DT_NODELABEL(ecia))))
|
||||
#define ECS_XEC_REG_BASE \
|
||||
((struct ecs_regs *)(DT_REG_ADDR(DT_NODELABEL(ecs))))
|
||||
#define PECI_XEC_REG_BASE \
|
||||
((struct peci_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr))))
|
||||
#define PCR_XEC_REG_BASE \
|
||||
((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr))))
|
||||
#define TFDP_0_XEC_REG_BASE \
|
||||
((struct tfdp_regs *)(DT_REG_ADDR(DT_NODELABEL(tfdp0))))
|
||||
#define UART_0_XEC_REG_BASE \
|
||||
((struct uart_regs *)(DT_REG_ADDR(DT_NODELABEL(uart0))))
|
||||
#define UART_1_XEC_REG_BASE \
|
||||
((struct uart_regs *)(DT_REG_ADDR(DT_NODELABEL(uart1))))
|
||||
#define VBATR_XEC_REG_BASE \
|
||||
((struct vbatr_regs *)(DT_REG_ADDR_BY_NAME(DT_NODELABEL(pcr), vbatr)))
|
||||
#define VBATM_XEC_BASE_ADDR \
|
||||
((uintptr_t)(DT_REG_ADDR(DT_NODELABEL(bbram))))
|
||||
|
||||
#define BTMR16_0_ADDR DT_REG_ADDR(DT_NODELABEL(timer0))
|
||||
#define BTMR16_1_ADDR DT_REG_ADDR(DT_NODELABEL(timer1))
|
||||
#define BTMR16_2_ADDR DT_REG_ADDR(DT_NODELABEL(timer2))
|
||||
#define BTMR16_3_ADDR DT_REG_ADDR(DT_NODELABEL(timer3))
|
||||
#define BTMR32_0_ADDR DT_REG_ADDR(DT_NODELABEL(timer4))
|
||||
#define BTMR32_1_ADDR DT_REG_ADDR(DT_NODELABEL(timer5))
|
||||
#define VBATM_XEC_ADDR DT_REG_ADDR(DT_NODELABEL(vbr))
|
||||
|
||||
#ifdef DEBUG_DEEP_SLEEP_CLK_REQ
|
||||
void soc_debug_sleep_clk_req(void)
|
||||
{
|
||||
struct ecs_regs *ecs = ECS_XEC_REG_BASE;
|
||||
struct pcr_regs *pcr = PCR_XEC_REG_BASE;
|
||||
uintptr_t vbm_addr = VBATM_XEC_BASE_ADDR;
|
||||
|
||||
/* Save status to debug LPM been blocked */
|
||||
for (int i = 0; i < 5; i++) {
|
||||
sys_write32(pcr->CLK_REQ[i], vbm_addr);
|
||||
vbm_addr += 4;
|
||||
}
|
||||
|
||||
sys_write32(pcr->SYS_SLP_CTRL, vbm_addr);
|
||||
vbm_addr += 4;
|
||||
sys_write32(ecs->SLP_STS_MIRROR, vbm_addr);
|
||||
}
|
||||
#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
|
||||
struct ecia_named_regs *regs = ECIA_XEC_REG_BASE;
|
||||
|
||||
regs->GIRQ22.SRC = UINT32_MAX;
|
||||
regs->GIRQ22.EN_SET = MCHP_ESPI_WK_CLK_GIRQ_BIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void soc_deep_sleep_non_wake_dis(void)
|
||||
{
|
||||
#ifdef CONFIG_ESPI
|
||||
struct ecia_named_regs *regs = ECIA_XEC_REG_BASE;
|
||||
|
||||
regs->GIRQ22.EN_CLR = UINT32_MAX;
|
||||
regs->GIRQ22.SRC = UINT32_MAX;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* When MEC172x drivers are power-aware this should be move there */
|
||||
void soc_deep_sleep_wake_en(void)
|
||||
{
|
||||
#if defined(CONFIG_KSCAN) || DT_NODE_HAS_STATUS(DT_NODELABEL(ps2_0), okay)
|
||||
struct ecia_named_regs *regs = ECIA_XEC_REG_BASE;
|
||||
#if defined(CONFIG_KSCAN)
|
||||
/* Enable PLL wake via KSCAN */
|
||||
regs->GIRQ21.SRC = MCHP_KEYSCAN_GIRQ_BIT;
|
||||
regs->GIRQ21.EN_SET = MCHP_KEYSCAN_GIRQ_BIT;
|
||||
#endif
|
||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(ps2_0), okay)
|
||||
/* Enable PS2_0B_WK */
|
||||
regs->GIRQ21.SRC = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT;
|
||||
regs->GIRQ21.EN_SET = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void soc_deep_sleep_wake_dis(void)
|
||||
{
|
||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(ps2_0), okay)
|
||||
struct ecia_named_regs *regs = ECIA_XEC_REG_BASE;
|
||||
|
||||
/* Enable PS2_0B_WK */
|
||||
regs->GIRQ21.EN_CLR = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT;
|
||||
regs->GIRQ21.SRC = MCHP_PS2_0_PORT0B_WK_GIRQ_BIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Variables used to save various HW state */
|
||||
#ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE
|
||||
|
||||
const struct ds_timer_info ds_timer_tbl[NUM_DS_TIMER_ENTRIES] = {
|
||||
{
|
||||
(uintptr_t)(BTMR16_0_ADDR + MCHP_BTMR_CTRL_OFS),
|
||||
MCHP_BTMR_CTRL_HALT, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)(BTMR16_1_ADDR + MCHP_BTMR_CTRL_OFS),
|
||||
MCHP_BTMR_CTRL_HALT, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)(BTMR16_2_ADDR + MCHP_BTMR_CTRL_OFS),
|
||||
MCHP_BTMR_CTRL_HALT, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)(BTMR16_3_ADDR + MCHP_BTMR_CTRL_OFS),
|
||||
MCHP_BTMR_CTRL_HALT, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)(BTMR32_0_ADDR + MCHP_BTMR_CTRL_OFS),
|
||||
MCHP_BTMR_CTRL_HALT, 0
|
||||
},
|
||||
{
|
||||
(uintptr_t)(BTMR32_1_ADDR + MCHP_BTMR_CTRL_OFS),
|
||||
MCHP_BTMR_CTRL_HALT, 0
|
||||
},
|
||||
};
|
||||
|
||||
static struct ds_dev_info ds_ctx;
|
||||
|
||||
static void deep_sleep_save_ecs(void)
|
||||
{
|
||||
struct ecs_regs *regs = ECS_XEC_REG_BASE;
|
||||
|
||||
ds_ctx.ecs[0] = regs->ETM_CTRL;
|
||||
ds_ctx.ecs[1] = regs->DEBUG_CTRL;
|
||||
#ifdef DEEP_SLEEP_JTAG
|
||||
regs->ETM_CTRL = 0;
|
||||
regs->DEBUG_CTRL = 0x00;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEEP_SLEEP_UART_SAVE_RESTORE
|
||||
static void deep_sleep_save_uarts(void)
|
||||
{
|
||||
struct uart_regs *regs = UART_0_XEC_REG_BASE;
|
||||
|
||||
ds_ctx.uart_info[0] = regs->ACTV;
|
||||
if (ds_ctx.uart_info[0]) {
|
||||
while ((regs->LSR & MCHP_UART_LSR_TEMT) == 0) {
|
||||
}
|
||||
}
|
||||
regs->ACTV = 0;
|
||||
|
||||
regs = UART_1_XEC_REG_BASE;
|
||||
ds_ctx.uart_info[1] = regs->ACTV;
|
||||
if (ds_ctx.uart_info[1]) {
|
||||
while ((regs->LSR & MCHP_UART_LSR_TEMT) == 0) {
|
||||
}
|
||||
}
|
||||
regs->ACTV = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void deep_sleep_save_timers(void)
|
||||
{
|
||||
const struct ds_timer_info *p;
|
||||
uint32_t i;
|
||||
|
||||
p = &ds_timer_tbl[0];
|
||||
for (i = 0; i < NUM_DS_TIMER_ENTRIES; i++) {
|
||||
ds_ctx.timers[i] = sys_read32(p->addr);
|
||||
if (p->stop_mask) {
|
||||
sys_write32(ds_ctx.timers[i] | p->stop_mask, p->addr);
|
||||
} else {
|
||||
sys_write32(0, p->addr);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
static void deep_sleep_restore_ecs(void)
|
||||
{
|
||||
#ifdef DEEP_SLEEP_JTAG
|
||||
struct ecs_regs *regs = ECS_XEC_REG_BASE;
|
||||
|
||||
regs->ETM_CTRL = ds_ctx.ecs[0];
|
||||
regs->DEBUG_CTRL = ds_ctx.ecs[1];
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEEP_SLEEP_UART_SAVE_RESTORE
|
||||
static void deep_sleep_restore_uarts(void)
|
||||
{
|
||||
struct uart_regs *regs0 = UART_0_XEC_REG_BASE;
|
||||
struct uart_regs *regs1 = UART_1_XEC_REG_BASE;
|
||||
|
||||
regs0->ACTV = ds_ctx.uart_info[0];
|
||||
regs1->ACTV = ds_ctx.uart_info[1];
|
||||
}
|
||||
#endif
|
||||
|
||||
static void deep_sleep_restore_timers(void)
|
||||
{
|
||||
const struct ds_timer_info *p;
|
||||
uint32_t i, temp;
|
||||
|
||||
p = &ds_timer_tbl[0];
|
||||
for (i = 0; i < NUM_DS_TIMER_ENTRIES; i++) {
|
||||
if (p->stop_mask) {
|
||||
temp = sys_read32(p->addr) & ~(p->stop_mask);
|
||||
sys_write32(temp, p->addr);
|
||||
} else {
|
||||
sys_write32(ds_ctx.timers[i] & ~p->restore_mask,
|
||||
p->addr);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED
|
||||
|
||||
static void deep_sleep_save_blocks(void)
|
||||
{
|
||||
struct tfdp_regs *tfdp = TFDP_0_XEC_REG_BASE;
|
||||
struct ecs_regs *ecs = ECS_XEC_REG_BASE;
|
||||
#ifdef CONFIG_ADC
|
||||
struct adc_regs *adc0 = ADC_0_XEC_REG_BASE;
|
||||
|
||||
/* ADC deactivate */
|
||||
adc0->CONTROL &= ~(MCHP_ADC_CTRL_ACTV);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PECI
|
||||
struct peci_regs *peci = PECI_XEC_REG_BASE;
|
||||
|
||||
ds_ctx.peci_info.peci_ctrl = peci->CONTROL;
|
||||
ds_ctx.peci_info.peci_dis = ecs->PECI_DIS;
|
||||
ecs->PECI_DIS |= MCHP_ECS_PECI_DISABLE;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_I2C
|
||||
for (size_t n = 0; n > MCHP_I2C_SMB_INSTANCES; n++) {
|
||||
uint32_t addr = MCHP_I2C_SMB_BASE_ADDR(n) +
|
||||
MCHP_I2C_SMB_CFG_OFS;
|
||||
uint32_t regval = sys_read32(addr);
|
||||
|
||||
ds_ctx.smb_info[n] = regval;
|
||||
sys_write32(regval & ~(MCHP_I2C_SMB_CFG_ENAB), addr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Disable comparator if enabled */
|
||||
if (ecs->CMP_CTRL & BIT(0)) {
|
||||
ds_ctx.comp_en = 1;
|
||||
ecs->CMP_CTRL &= ~(MCHP_ECS_ACC_EN0);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TACH_XEC) || defined(CONFIG_PWM_XEC)
|
||||
struct pcr_regs *pcr = PCR_XEC_REG_BASE;
|
||||
|
||||
/* This low-speed clock derived from the 48MHz clock domain is used as
|
||||
* a time base for PWMs and TACHs
|
||||
* Set SLOW_CLOCK_DIVIDE = CLKOFF to save additional power
|
||||
*/
|
||||
ds_ctx.slwclk_info = pcr->SLOW_CLK_CTRL;
|
||||
pcr->SLOW_CLK_CTRL &= (~MCHP_PCR_SLOW_CLK_CTRL_100KHZ &
|
||||
MCHP_PCR_SLOW_CLK_CTRL_MASK);
|
||||
#endif
|
||||
|
||||
/* TFDP HW block is not expose to any Zephyr subsystem */
|
||||
if (tfdp->CTRL & MCHP_TFDP_CTRL_EN) {
|
||||
ds_ctx.tfdp_en = 1;
|
||||
tfdp->CTRL &= ~MCHP_TFDP_CTRL_EN;
|
||||
}
|
||||
|
||||
/* Port 80 TODO Do we need to do anything? MEC172x BDP does not
|
||||
* include a timer so it should de-assert its CLK_REQ in response
|
||||
* to SLP_EN 0->1.
|
||||
*/
|
||||
}
|
||||
|
||||
static void deep_sleep_restore_blocks(void)
|
||||
{
|
||||
struct tfdp_regs *tfdp = TFDP_0_XEC_REG_BASE;
|
||||
struct ecs_regs *ecs = ECS_XEC_REG_BASE;
|
||||
#ifdef CONFIG_ADC
|
||||
struct adc_regs *adc0 = ADC_0_XEC_REG_BASE;
|
||||
|
||||
adc0->CONTROL |= MCHP_ADC_CTRL_ACTV;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PECI
|
||||
struct peci_regs *peci = PECI_XEC_REG_BASE;
|
||||
|
||||
ecs->PECI_DIS = ds_ctx.peci_info.peci_dis;
|
||||
peci->CONTROL = ds_ctx.peci_info.peci_ctrl;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_I2C
|
||||
for (size_t n = 0; n > MCHP_I2C_SMB_INSTANCES; n++) {
|
||||
uint32_t addr = MCHP_I2C_SMB_BASE_ADDR(n) +
|
||||
MCHP_I2C_SMB_CFG_OFS;
|
||||
|
||||
sys_write32(ds_ctx.smb_info[n], addr);
|
||||
}
|
||||
#endif
|
||||
/* Restore comparator control values */
|
||||
if (ds_ctx.comp_en) {
|
||||
ecs->CMP_CTRL |= MCHP_ECS_ACC_EN0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TACH_XEC) || defined(CONFIG_PWM_XEC)
|
||||
struct pcr_regs *pcr = PCR_XEC_REG_BASE;
|
||||
|
||||
/* Restore slow clock control */
|
||||
pcr->SLOW_CLK_CTRL = ds_ctx.slwclk_info;
|
||||
#endif
|
||||
|
||||
/* TFDP HW block is not expose to any Zephyr subsystem */
|
||||
if (ds_ctx.tfdp_en) {
|
||||
tfdp->CTRL |= MCHP_TFDP_CTRL_EN;
|
||||
}
|
||||
}
|
||||
#endif /* DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED */
|
||||
|
||||
void soc_deep_sleep_periph_save(void)
|
||||
{
|
||||
#ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED
|
||||
deep_sleep_save_blocks();
|
||||
#endif
|
||||
deep_sleep_save_ecs();
|
||||
deep_sleep_save_timers();
|
||||
#ifdef DEEP_SLEEP_UART_SAVE_RESTORE
|
||||
deep_sleep_save_uarts();
|
||||
#endif
|
||||
}
|
||||
|
||||
void soc_deep_sleep_periph_restore(void)
|
||||
{
|
||||
deep_sleep_restore_ecs();
|
||||
#ifdef DEEP_SLEEP_UART_SAVE_RESTORE
|
||||
deep_sleep_restore_uarts();
|
||||
#endif
|
||||
deep_sleep_restore_timers();
|
||||
#ifdef DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED
|
||||
deep_sleep_restore_blocks();
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void soc_deep_sleep_periph_save(void)
|
||||
{
|
||||
}
|
||||
|
||||
void soc_deep_sleep_periph_restore(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* DEEP_SLEEP_PERIPH_SAVE_RESTORE */
|
101
soc/arm/microchip_mec/mec172x/device_power.h
Normal file
101
soc/arm/microchip_mec/mec172x/device_power.h
Normal file
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Microchip Technology Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __DEVICE_POWER_H
|
||||
#define __DEVICE_POWER_H
|
||||
|
||||
#ifndef _ASMLANGUAGE
|
||||
|
||||
#define DEBUG_SLEEP
|
||||
#define DEBUG_DEEP_SLEEP_CLK_REQ
|
||||
|
||||
/*
|
||||
* Disable UART deep sleep save/restore. If a UART TX FIFO has data on deep
|
||||
* sleep entry it will de-assert its CLK_REQ once TX FIFO empties. If the
|
||||
* UART has TX empty interrupt enabled then the system will wake.
|
||||
*/
|
||||
/* #define DEEP_SLEEP_UART_SAVE_RESTORE */
|
||||
|
||||
/* Enable SoC control over peripherals only when drivers do not support
|
||||
* power management
|
||||
*/
|
||||
#ifndef CONFIG_PM_DEVICE
|
||||
|
||||
/* Comment out to use JTAG without interruptions.
|
||||
* Beware this blocks PLL going off, hence should be enabled
|
||||
* for power consumption measurements
|
||||
* Note: To attach JTAG for any debug need to be performed with breakpoint
|
||||
* prior to deep sleep entry.
|
||||
*/
|
||||
/* #define DEEP_SLEEP_JTAG */
|
||||
|
||||
/*
|
||||
* Enabling this would take a snapshot from clock requested values,
|
||||
* these can be dump on exit to identify which HW block is blocking.
|
||||
*/
|
||||
#define DEEP_SLEEP_CLK_REQ_DUMP
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
/* Power optimization if certain HW blocks are not used.
|
||||
* These are not part of any Zephyr subsystem.
|
||||
* #define DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED
|
||||
*/
|
||||
#define DEEP_SLEEP_PERIPH_SAVE_RESTORE_EXTENDED
|
||||
|
||||
#define NUM_DS_TIMER_ENTRIES 6
|
||||
|
||||
struct ds_timer_info {
|
||||
uintptr_t addr;
|
||||
uint32_t stop_mask;
|
||||
uint32_t restore_mask;
|
||||
};
|
||||
|
||||
struct ds_peci_info {
|
||||
uint32_t peci_ctrl;
|
||||
uint32_t peci_dis;
|
||||
};
|
||||
|
||||
struct ds_dev_info {
|
||||
uint32_t ecs[2];
|
||||
uint32_t timers[NUM_DS_TIMER_ENTRIES];
|
||||
#ifdef DEEP_SLEEP_UART_SAVE_RESTORE
|
||||
uint8_t uart_info[MCHP_UART_INSTANCES];
|
||||
#endif
|
||||
/* Analog power */
|
||||
#ifdef CONFIG_PECI
|
||||
struct ds_peci_info peci_info;
|
||||
#endif
|
||||
#ifdef CONFIG_I2C
|
||||
uint32_t smb_info[MCHP_I2C_SMB_INSTANCES];
|
||||
#endif
|
||||
uint32_t slwclk_info;
|
||||
uint8_t tfdp_en;
|
||||
uint8_t comp_en;
|
||||
};
|
||||
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
void soc_deep_sleep_periph_save(void);
|
||||
void soc_deep_sleep_periph_restore(void);
|
||||
void soc_deep_sleep_non_wake_en(void);
|
||||
void soc_deep_sleep_non_wake_dis(void);
|
||||
void soc_deep_sleep_wake_en(void);
|
||||
void soc_deep_sleep_wake_dis(void);
|
||||
|
||||
#ifdef DEBUG_DEEP_SLEEP_CLK_REQ
|
||||
void soc_debug_sleep_clk_req(void);
|
||||
#endif
|
||||
|
||||
#endif /* _ASMLANGUAGE */
|
||||
#endif /* __DEVICE_POWER_H */
|
189
soc/arm/microchip_mec/mec172x/power.c
Normal file
189
soc/arm/microchip_mec/mec172x/power.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Microchip Technology Inc.
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <sys/sys_io.h>
|
||||
#include <sys/__assert.h>
|
||||
#include <pm/pm.h>
|
||||
#include <soc.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <arch/arm/aarch32/cortex_m/cmsis.h>
|
||||
#ifndef CONFIG_PM_DEVICE
|
||||
#include "device_power.h"
|
||||
#endif
|
||||
|
||||
#include "soc_power_debug.h"
|
||||
|
||||
#define HTMR_0_XEC_REG_BASE \
|
||||
((struct htmr_regs *)(DT_REG_ADDR(DT_NODELABEL(hibtimer0))))
|
||||
#define PCR_XEC_REG_BASE \
|
||||
((struct pcr_regs *)(DT_REG_ADDR(DT_NODELABEL(pcr))))
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* NOTE: Zephyr masks interrupts using BASEPRI before calling PM subsystem */
|
||||
static void z_power_soc_deep_sleep(void)
|
||||
{
|
||||
struct pcr_regs *pcr = PCR_XEC_REG_BASE;
|
||||
struct htmr_regs *htmr0 = HTMR_0_XEC_REG_BASE;
|
||||
uint32_t basepri = 0U;
|
||||
uint32_t temp = 0U;
|
||||
|
||||
PM_DP_ENTER();
|
||||
|
||||
__disable_irq();
|
||||
basepri = __get_BASEPRI();
|
||||
|
||||
#ifndef CONFIG_PM_DEVICE
|
||||
soc_deep_sleep_periph_save();
|
||||
soc_deep_sleep_wake_en();
|
||||
soc_deep_sleep_non_wake_en();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enable deep sleep mode in CM4 and MEC172x.
|
||||
* Enable CM4 deep sleep and sleep signals assertion on WFI.
|
||||
* Set MCHP Heavy sleep (PLL OFF when all CLK_REQ clear) and SLEEP_ALL
|
||||
* to assert SLP_EN to all peripherals on WFI.
|
||||
* Set PRIMASK = 1 so on wake the CPU will not vector to any ISR.
|
||||
* Set BASEPRI = 0 to allow any priority to wake.
|
||||
*/
|
||||
SCB->SCR |= BIT(2);
|
||||
pcr->SYS_SLP_CTRL = MCHP_PCR_SYS_SLP_HEAVY;
|
||||
pcr->OSC_ID = pcr->SYS_SLP_CTRL;
|
||||
#ifdef DEBUG_DEEP_SLEEP_CLK_REQ
|
||||
soc_debug_sleep_clk_req();
|
||||
#endif
|
||||
__set_BASEPRI(0);
|
||||
__WFI(); /* triggers sleep hardware */
|
||||
__NOP();
|
||||
__NOP();
|
||||
|
||||
/*
|
||||
* Clear SLEEP_ALL manually since we are not vectoring to an ISR until
|
||||
* PM post ops. This de-asserts peripheral SLP_EN signals.
|
||||
*/
|
||||
pcr->SYS_SLP_CTRL = 0U;
|
||||
SCB->SCR &= ~BIT(2);
|
||||
|
||||
/* Wait for PLL to lock with timeout */
|
||||
htmr0->PRLD = 0U; /* make sure its stopped */
|
||||
htmr0->CTRL = 0U; /* 30.5 us per tick */
|
||||
htmr0->PRLD = 216U; /* ~6.6 ms 2x the expected lock time */
|
||||
temp = htmr0->PRLD;
|
||||
while ((pcr->OSC_ID & MCHP_PCR_OSC_ID_PLL_LOCK) == 0) {
|
||||
temp = htmr0->PRLD;
|
||||
if (!temp) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
htmr0->PRLD = 0U; /* stop */
|
||||
|
||||
__set_BASEPRI(basepri);
|
||||
|
||||
#ifndef CONFIG_PM_DEVICE
|
||||
soc_deep_sleep_non_wake_dis();
|
||||
soc_deep_sleep_wake_dis();
|
||||
soc_deep_sleep_periph_restore();
|
||||
#endif
|
||||
|
||||
PM_DP_EXIT();
|
||||
}
|
||||
|
||||
/*
|
||||
* Light Sleep
|
||||
* Pros:
|
||||
* Fast wake response:
|
||||
* Cons:
|
||||
* Higher power dissipation, 48MHz PLL remains on.
|
||||
*
|
||||
* When the kernel calls this it has masked interrupt by setting NVIC BASEPRI
|
||||
* equal to a value equal to the highest Zephyr ISR priority. Only NVIC
|
||||
* exceptions will be served.
|
||||
*/
|
||||
static void z_power_soc_sleep(void)
|
||||
{
|
||||
struct pcr_regs *pcr = PCR_XEC_REG_BASE;
|
||||
|
||||
__disable_irq();
|
||||
|
||||
SCB->SCR &= ~BIT(2);
|
||||
pcr->SYS_SLP_CTRL = MCHP_PCR_SYS_SLP_LIGHT;
|
||||
pcr->OSC_ID = pcr->SYS_SLP_CTRL;
|
||||
__set_BASEPRI(0);
|
||||
__WFI(); /* triggers sleep hardware */
|
||||
__NOP();
|
||||
__NOP();
|
||||
pcr->SYS_SLP_CTRL = 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from pm_system_suspend(int32_t ticks) in subsys/power.c
|
||||
* For deep sleep pm_system_suspend has executed all the driver
|
||||
* power management call backs.
|
||||
*/
|
||||
__weak void pm_state_set(enum pm_state state, uint8_t substate_id)
|
||||
{
|
||||
ARG_UNUSED(substate_id);
|
||||
|
||||
switch (state) {
|
||||
case PM_STATE_SUSPEND_TO_IDLE:
|
||||
z_power_soc_sleep();
|
||||
break;
|
||||
case PM_STATE_SUSPEND_TO_RAM:
|
||||
z_power_soc_deep_sleep();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Zephyr PM code expects us to enabled interrupt at post op exit. Zephyr used
|
||||
* arch_irq_lock() which sets BASEPRI to a non-zero value masking all interrupts
|
||||
* preventing wake. MCHP z_power_soc_(deep)_sleep sets PRIMASK=1 and BASEPRI=0
|
||||
* allowing wake from any enabled interrupt and prevent CPU from entering any
|
||||
* ISR on wake except for faults. We re-enable interrupt by setting PRIMASK to 0.
|
||||
* Side-effect is we set BASEPRI=0. Is this the same value as Zephyr uses during
|
||||
* NVIC initialization?
|
||||
*/
|
||||
__weak void pm_state_exit_post_ops(enum pm_state state, uint8_t substate_id)
|
||||
{
|
||||
switch (state) {
|
||||
case PM_STATE_SUSPEND_TO_IDLE:
|
||||
case PM_STATE_SUSPEND_TO_RAM:
|
||||
__enable_irq();
|
||||
break;
|
||||
default:
|
||||
irq_unlock(0); /* this writes CM4 BASEPRI=0 */
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,15 @@ static int soc_init(const struct device *dev)
|
|||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
if (IS_ENABLED(CONFIG_SOC_MEC172X_TEST_CLK_OUT)) {
|
||||
|
||||
struct gpio_regs * const regs =
|
||||
(struct gpio_regs * const)DT_REG_ADDR(DT_NODELABEL(pinctrl));
|
||||
|
||||
regs->CTRL[MCHP_GPIO_0060_ID] = MCHP_GPIO_CTRL_MUX_F2 |
|
||||
MCHP_GPIO_CTRL_IDET_DISABLE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
40
soc/arm/microchip_mec/mec172x/soc_power_debug.h
Normal file
40
soc/arm/microchip_mec/mec172x/soc_power_debug.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Intel Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __SOC_POWER_DEBUG_H__
|
||||
#define __SOC_POWER_DEBUG_H__
|
||||
|
||||
/* #define SOC_SLEEP_STATE_GPIO_MARKER_DEBUG */
|
||||
|
||||
#ifdef SOC_SLEEP_STATE_GPIO_MARKER_DEBUG
|
||||
|
||||
/* Select a gpio not used. LED4 on EVB. High = ON */
|
||||
#define DP_GPIO_ID MCHP_GPIO_0241_ID
|
||||
|
||||
/* output drive high */
|
||||
#define PM_DP_ENTER_GPIO_VAL 0x10240U
|
||||
/* output drive low */
|
||||
#define PM_DP_EXIT_GPIO_VAL 0x0240U
|
||||
|
||||
static inline void pm_dp_gpio(uint32_t gpio_ctrl_val)
|
||||
{
|
||||
struct gpio_regs * const regs =
|
||||
(struct gpio_regs * const)(DT_REG_ADDR(DT_NODELABEL(gpio_000_036)));
|
||||
|
||||
regs->CTRL[DP_GPIO_ID] = gpio_ctrl_val;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DP_GPIO_ID
|
||||
#define PM_DP_ENTER() pm_dp_gpio(PM_DP_ENTER_GPIO_VAL)
|
||||
#define PM_DP_EXIT() pm_dp_gpio(PM_DP_EXIT_GPIO_VAL)
|
||||
#else
|
||||
#define PM_DP_ENTER()
|
||||
#define PM_DP_EXIT()
|
||||
#endif
|
||||
|
||||
#endif /* __SOC_POWER_DEBUG_H__ */
|
Loading…
Reference in a new issue