232 lines
5.0 KiB
C
232 lines
5.0 KiB
C
/*
|
|
* 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 <power/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
|
|
uint32_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 uint32_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;
|
|
uint32_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 uint32_t timers[NUM_DS_TIMER_ENTRIES];
|
|
static uint8_t uart_activate[3];
|
|
|
|
static void deep_sleep_save_uarts(void)
|
|
{
|
|
uart_activate[0] = UART0_REGS->ACTV;
|
|
if (uart_activate[0]) {
|
|
while ((UART0_REGS->LSR & MCHP_UART_LSR_TEMT) == 0) {
|
|
};
|
|
}
|
|
UART0_REGS->ACTV = 0;
|
|
uart_activate[1] = UART1_REGS->ACTV;
|
|
if (uart_activate[1]) {
|
|
while ((UART1_REGS->LSR & MCHP_UART_LSR_TEMT) == 0) {
|
|
};
|
|
}
|
|
UART1_REGS->ACTV = 0;
|
|
uart_activate[2] = UART2_REGS->ACTV;
|
|
if (uart_activate[2]) {
|
|
while ((UART2_REGS->LSR & MCHP_UART_LSR_TEMT) == 0) {
|
|
};
|
|
}
|
|
UART2_REGS->ACTV = 0;
|
|
}
|
|
|
|
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++) {
|
|
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;
|
|
uint32_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 */
|