diff --git a/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts b/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts index 96ace6dcb6..20989a2b3f 100644 --- a/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts +++ b/boards/arm/mec172xevb_assy6906/mec172xevb_assy6906.dts @@ -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 { diff --git a/soc/arm/microchip_mec/mec172x/CMakeLists.txt b/soc/arm/microchip_mec/mec172x/CMakeLists.txt index 493bd1be0b..47f91773dd 100644 --- a/soc/arm/microchip_mec/mec172x/CMakeLists.txt +++ b/soc/arm/microchip_mec/mec172x/CMakeLists.txt @@ -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 diff --git a/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.series b/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.series index 451c7146ee..7708d518b1 100644 --- a/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.series +++ b/soc/arm/microchip_mec/mec172x/Kconfig.defconfig.series @@ -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 diff --git a/soc/arm/microchip_mec/mec172x/Kconfig.soc b/soc/arm/microchip_mec/mec172x/Kconfig.soc index 4b910c154d..d74993b446 100644 --- a/soc/arm/microchip_mec/mec172x/Kconfig.soc +++ b/soc/arm/microchip_mec/mec172x/Kconfig.soc @@ -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 diff --git a/soc/arm/microchip_mec/mec172x/device_power.c b/soc/arm/microchip_mec/mec172x/device_power.c new file mode 100644 index 0000000000..e24b9cf184 --- /dev/null +++ b/soc/arm/microchip_mec/mec172x/device_power.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2019 Microchip Technology Inc. + * Copyright (c) 2016 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#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 */ diff --git a/soc/arm/microchip_mec/mec172x/device_power.h b/soc/arm/microchip_mec/mec172x/device_power.h new file mode 100644 index 0000000000..774bcea819 --- /dev/null +++ b/soc/arm/microchip_mec/mec172x/device_power.h @@ -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 */ diff --git a/soc/arm/microchip_mec/mec172x/power.c b/soc/arm/microchip_mec/mec172x/power.c new file mode 100644 index 0000000000..7aaf3a8273 --- /dev/null +++ b/soc/arm/microchip_mec/mec172x/power.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2019 Microchip Technology Inc. + * Copyright (c) 2016 Intel Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#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; + } +} diff --git a/soc/arm/microchip_mec/mec172x/soc.c b/soc/arm/microchip_mec/mec172x/soc.c index 9bc891e6b8..8ecdf7d3be 100644 --- a/soc/arm/microchip_mec/mec172x/soc.c +++ b/soc/arm/microchip_mec/mec172x/soc.c @@ -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; } diff --git a/soc/arm/microchip_mec/mec172x/soc_power_debug.h b/soc/arm/microchip_mec/mec172x/soc_power_debug.h new file mode 100644 index 0000000000..e93114d13d --- /dev/null +++ b/soc/arm/microchip_mec/mec172x/soc_power_debug.h @@ -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__ */