coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/mchp/clock.c

764 lines
18 KiB
C
Raw Normal View History

2024-03-04 11:14:53 +01:00
/* Copyright 2017 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Clocks and power management settings */
#include "clock.h"
#include "common.h"
#include "console.h"
#include "cpu.h"
#include "hooks.h"
#include "hwtimer.h"
#include "pwm.h"
#include "pwm_chip.h"
#include "registers.h"
#include "shared_mem.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
#include "util.h"
#include "tfdp_chip.h"
#include "vboot_hash.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_CLOCK, outstr)
#define CPRINTS(format, args...) cprints(CC_CLOCK, format, ## args)
#ifdef CONFIG_LOW_POWER_IDLE
#define HTIMER_DIV_1_US_MAX (1998848)
#define HTIMER_DIV_1_1SEC (0x8012)
/* Recovery time for HvySlp2 is 0 usec */
#define HEAVY_SLEEP_RECOVER_TIME_USEC 75
#define SET_HTIMER_DELAY_USEC 200
static int idle_sleep_cnt;
static int idle_dsleep_cnt;
static uint64_t total_idle_dsleep_time_us;
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
static uint32_t pcr_slp_en[MCHP_PCR_SLP_RST_REG_MAX];
static uint32_t pcr_clk_req[MCHP_PCR_SLP_RST_REG_MAX];
static uint32_t ecia_result[MCHP_INT_GIRQ_NUM];
#endif
/*
* Fixed amount of time to keep the console in use flag true after
* boot in order to give a permanent window in which the heavy sleep
* mode is not used.
*/
#define CONSOLE_IN_USE_ON_BOOT_TIME (15*SECOND)
static int console_in_use_timeout_sec = 60;
static timestamp_t console_expire_time;
#endif /*CONFIG_LOW_POWER_IDLE */
static int freq = 48000000;
void clock_wait_cycles(uint32_t cycles)
{
asm volatile("1: subs %0, #1\n"
" bne 1b\n" : "+r"(cycles));
}
int clock_get_freq(void)
{
return freq;
}
/** clock_init
* @note
* MCHP MEC implements 4 control bits in the VBAT Clock Enable register.
* It also implements an internal silicon 32KHz +/- 2% oscillator powered
* by VBAT.
* b[3] = XOSEL 0=parallel, 1=single-ended
* b[2] = 32KHZ_SOURCE specifies source of always-on clock domain
* 0=internal silicon oscillator
* 1=crystal XOSEL pin(s)
* b[1] = EXT_32K use always-on clock domain or external 32KHZ_IN pin
* 0=32K source is always-on clock domain
* 1=32K source is 32KHZ_IN pin (GPIO 0165)
* b[0] = 32K_SUPPRESS
* 0=32K clock domain stays enabled if VTR is off. Powered by VBAT
* 1=32K clock domain is disabled if VTR is off.
* Set b[3] based on CONFIG_CLOCK_CRYSTAL
* Set b[2:0] = 100b
* b[0]=0 32K clock domain always on (requires VBAT if VTR is off)
* b[1]=0 32K source is the 32K clock domain NOT the 32KHZ_IN pin
* b[2]=1 If activity detected on crystal pins switch 32K input from
* internal silicon oscillator to XOSEL pin(s) based on b[3].
*/
void clock_init(void)
{
int __attribute__((unused)) dummy;
trace0(0, MEC, 0, "Clock Init");
#ifdef CONFIG_CLOCK_CRYSTAL
/* XOSEL: 0 = Parallel resonant crystal */
MCHP_VBAT_CE &= ~(1ul << 3);
#else
/* XOSEL: 1 = Single ended clock source */
MCHP_VBAT_CE |= (1ul << 3);
#endif
/* 32K clock enable */
MCHP_VBAT_CE = (MCHP_VBAT_CE & ~(0x03)) | (1ul << 2);
#ifdef CONFIG_CLOCK_CRYSTAL
/* Wait for crystal to stabilize (OSC_LOCK == 1) */
while (!(MCHP_PCR_CHIP_OSC_ID & 0x100))
;
#endif
trace0(0, MEC, 0, "PLL OSC is Locked");
#ifndef LFW
dummy = shared_mem_size();
trace11(0, MEC, 0, "Shared Memory size = 0x%08x", (uint32_t)dummy);
#endif
}
/**
* Speed through boot + vboot hash calculation, dropping our processor
* clock only after vboot hashing is completed.
*/
static void clock_turbo_disable(void);
DECLARE_DEFERRED(clock_turbo_disable);
static void clock_turbo_disable(void)
{
#ifdef CONFIG_VBOOT_HASH
if (vboot_hash_in_progress())
hook_call_deferred(&clock_turbo_disable_data, 100 * MSEC);
else
#endif
/* Use 12 MHz processor clock for power savings */
MCHP_PCR_PROC_CLK_CTL = 4;
}
DECLARE_HOOK(HOOK_INIT,
clock_turbo_disable,
HOOK_PRIO_INIT_VBOOT_HASH + 1);
/**
* initialization of Hibernation timer0
* Clear PCR sleep enable.
* GIRQ=21, aggregator bit = 1, Direct NVIC = 112
* NVIC direct connect interrupts are used for all peripherals
* (exception GPIO's) then the MCHP_INT_BLK_EN GIRQ bit should not be
* set.
*/
void htimer_init(void)
{
MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_HTMR0);
MCHP_HTIMER_PRELOAD(0) = 0; /* disable at beginning */
MCHP_INT_SOURCE(MCHP_HTIMER_GIRQ) =
MCHP_HTIMER_GIRQ_BIT(0);
MCHP_INT_ENABLE(MCHP_HTIMER_GIRQ) =
MCHP_HTIMER_GIRQ_BIT(0);
task_enable_irq(MCHP_IRQ_HTIMER0);
}
/**
* Use hibernate module to set up an htimer interrupt at a given
* time from now
*
* @param seconds Number of seconds before htimer interrupt
* @param microseconds Number of microseconds before htimer interrupt
* @note hibernation timer input clock is 32.768KHz.
* Control register bit[0] selects the divider.
* 0 is divide by 1 for 30.5us per LSB for a maximum of
* 65535 * 30.5us = 1998817.5 us or 32.786 counts per second
* 1 is divide by 4096 for 0.125s per LSB for a maximum of ~2 hours.
* 65535 * 0.125s ~ 8192 s = 2.27 hours
*/
void system_set_htimer_alarm(uint32_t seconds,
uint32_t microseconds)
{
uint32_t hcnt, ns;
uint8_t hctrl;
MCHP_HTIMER_PRELOAD(0) = 0; /* disable */
trace12(0, SLP, 0, "sys set htimer: sec=%d us=%d",
seconds, microseconds);
if (microseconds > 1000000ul) {
ns = (microseconds / 1000000ul);
microseconds %= 1000000ul;
if ((0xfffffffful - seconds) > ns)
seconds += ns;
else
seconds = 0xfffffffful;
}
if (seconds > 1) {
hcnt = (seconds << 3); /* divide by 0.125 */
if (hcnt > 0xfffful)
hcnt = 0xfffful;
hctrl = 1;
} else {
/*
* approximate(~2% error) as seconds is 0 or 1
* seconds / 30.5e-6 + microseonds / 30.5
*/
hcnt = (seconds << 15) + (microseconds >> 5) +
(microseconds >> 10);
hctrl = 0;
}
trace12(0, SLP, 0,
"sys set htimer: ctrl=0x%0x preload=0x%0x",
hctrl, hcnt);
MCHP_HTIMER_CONTROL(0) = hctrl;
MCHP_HTIMER_PRELOAD(0) = hcnt;
}
#ifdef CONFIG_LOW_POWER_IDLE
/**
* return time slept in micro-seconds
*/
static timestamp_t system_get_htimer(void)
{
uint16_t count;
timestamp_t time;
count = MCHP_HTIMER_COUNT(0);
if (MCHP_HTIMER_CONTROL(0) == 1) /* if > 2 sec */
/* 0.125 sec per count */
time.le.lo = (uint32_t)(count * 125000);
else /* if < 2 sec */
/* 30.5(=61/2)usec per count */
time.le.lo = (uint32_t)(count * 61 / 2);
time.le.hi = 0;
return time; /* in uSec */
}
/**
* Disable and clear hibernation timer interrupt
*/
static void system_reset_htimer_alarm(void)
{
MCHP_HTIMER_PRELOAD(0) = 0;
MCHP_INT_SOURCE(MCHP_HTIMER_GIRQ) =
MCHP_HTIMER_GIRQ_BIT(0);
}
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
static void print_pcr_regs(void)
{
int i;
trace0(0, MEC, 0, "Current PCR registers");
for (i = 0; i < 5; i++) {
trace12(0, MEC, 0, "REG SLP_EN[%d] = 0x%08X",
i, MCHP_PCR_SLP_EN(i));
trace12(0, MEC, 0, "REG CLK_REQ[%d] = 0x%08X",
i, MCHP_PCR_CLK_REQ(i));
}
}
static void print_ecia_regs(void)
{
int i;
trace0(0, MEC, 0, "Current GIRQn.Result registers");
for (i = MCHP_INT_GIRQ_FIRST;
i <= MCHP_INT_GIRQ_LAST; i++)
trace12(0, MEC, 0, "GIRQ[%d].Result = 0x%08X",
i, MCHP_INT_RESULT(i));
}
static void save_regs(void)
{
int i;
for (i = 0; i < MCHP_PCR_SLP_RST_REG_MAX; i++) {
pcr_slp_en[i] = MCHP_PCR_SLP_EN(i);
pcr_clk_req[i] = MCHP_PCR_CLK_REQ(i);
}
for (i = 0; i < MCHP_INT_GIRQ_NUM; i++)
ecia_result[i] =
MCHP_INT_RESULT(MCHP_INT_GIRQ_FIRST + i);
}
static void print_saved_regs(void)
{
int i;
trace0(0, BRD, 0, "Before sleep saved registers");
for (i = 0; i < MCHP_PCR_SLP_RST_REG_MAX; i++) {
trace12(0, BRD, 0, "PCR_SLP_EN[%d] = 0x%08X",
i, pcr_slp_en[i]);
trace12(0, BRD, 0, "PCR_CLK_REQ[%d] = 0x%08X",
i, pcr_clk_req[i]);
}
for (i = 0; i < MCHP_INT_GIRQ_NUM; i++)
trace12(0, BRD, 0, "GIRQ[%d].Result = 0x%08X",
(i+MCHP_INT_GIRQ_FIRST), ecia_result[i]);
}
#endif /* #ifdef CONFIG_MCHP_DEEP_SLP_DEBUG */
/**
* This is MCHP specific and equivalent to ARM Cortex's
* 'DeepSleep' via system control block register, CPU_SCB_SYSCTRL
* MCHP has new SLP_ALL feature.
* When SLP_ALL is enabled and HW sees sleep entry trigger from CPU.
* 1. HW saves PCR.SLP_EN registers
* 2. HW sets all PCR.SLP_EN bits to 1.
* 3. System sleeps
* 4. wake event wakes system
* 5. HW restores original values of all PCR.SLP_EN registers
* NOTE1: Current RTOS core (Cortex-Mx) does not use SysTick timer.
* We can leave code to disable it but do not re-enable on wake.
* NOTE2: Some peripherals will not sleep until outstanding transactions
* are complete: I2C, DMA, GPSPI, QMSPI, etc.
* NOTE3: Security blocks do not fully implement HW sleep therefore their
* sleep enables must be manually set/restored.
*
*/
static void prepare_for_deep_sleep(void)
{
trace0(0, MEC, 0, "Prepare for Deep Sleep");
/* sysTick timer */
CPU_NVIC_ST_CTRL &= ~ST_ENABLE;
CPU_NVIC_ST_CTRL &= ~ST_COUNTFLAG;
CPU_NVIC_ST_CTRL &= ~ST_TICKINT; /* SYS_TICK_INT_DISABLE */
/* Enable assertion of DeepSleep signals
* from the core when core enters sleep.
*/
CPU_SCB_SYSCTRL |= BIT(2);
/* Stop timers */
MCHP_TMR32_CTL(0) &= ~1;
MCHP_TMR32_CTL(1) &= ~1;
#ifdef CONFIG_WATCHDOG_HELP
MCHP_TMR16_CTL(0) &= ~1;
MCHP_INT_DISABLE(MCHP_TMR16_GIRQ) =
MCHP_TMR16_GIRQ_BIT(0);
MCHP_INT_SOURCE(MCHP_TMR16_GIRQ) =
MCHP_TMR16_GIRQ_BIT(0);
#endif
MCHP_INT_DISABLE(MCHP_TMR32_GIRQ) =
MCHP_TMR32_GIRQ_BIT(0) +
MCHP_TMR32_GIRQ_BIT(1);
MCHP_INT_SOURCE(MCHP_TMR32_GIRQ) =
MCHP_TMR32_GIRQ_BIT(0) +
MCHP_TMR32_GIRQ_BIT(1);
#ifdef CONFIG_WATCHDOG
/* Stop watchdog */
MCHP_WDG_CTL &= ~1;
#endif
#ifdef CONFIG_HOSTCMD_ESPI
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
MCHP_INT_ENABLE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
#else
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_LPC;
MCHP_INT_ENABLE(22) = MCHP_INT22_WAKE_ONLY_LPC;
#endif
#ifdef CONFIG_ADC
/*
* Clear ADC activate bit. If a conversion is in progress the
* ADC block will not enter low power until the converstion is
* complete.
*/
MCHP_ADC_CTRL &= ~1;
#endif
/* stop Port80 capture timer */
MCHP_P80_ACTIVATE(0) = 0;
/*
* Clear SLP_EN bit(s) for wake sources.
* Currently only Hibernation timer 0.
* GPIO pins can always wake.
*/
MCHP_PCR_SLP_EN3 &= ~(MCHP_PCR_SLP_EN3_HTMR0);
#ifdef CONFIG_PWM
pwm_keep_awake(); /* clear sleep enables of active PWM's */
#else
/* Disable 100 Khz clock */
MCHP_PCR_SLOW_CLK_CTL &= 0xFFFFFC00;
#endif
#ifdef CONFIG_CHIPSET_DEBUG
/* Disable JTAG and preserve mode */
MCHP_EC_JTAG_EN &= ~(MCHP_JTAG_ENABLE);
#endif
/* call board level */
#ifdef CONFIG_BOARD_DEEP_SLEEP
board_prepare_for_deep_sleep();
#endif
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
save_regs();
#endif
}
static void resume_from_deep_sleep(void)
{
trace0(0, MEC, 0, "resume_from_deep_sleep");
MCHP_PCR_SYS_SLP_CTL = 0x00; /* default */
/* Disable assertion of DeepSleep signal when core executes WFI */
CPU_SCB_SYSCTRL &= ~BIT(2);
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
print_saved_regs();
print_pcr_regs();
print_ecia_regs();
#endif
#ifdef CONFIG_CHIPSET_DEBUG
MCHP_EC_JTAG_EN |= (MCHP_JTAG_ENABLE);
#endif
MCHP_PCR_SLOW_CLK_CTL |= 0x1e0;
/* call board level */
#ifdef CONFIG_BOARD_DEEP_SLEEP
board_resume_from_deep_sleep();
#endif
/*
* re-enable hibernation timer 0 PCR.SLP_EN to
* reduce power.
*/
MCHP_PCR_SLP_EN3 |= (MCHP_PCR_SLP_EN3_HTMR0);
#ifdef CONFIG_HOSTCMD_ESPI
#ifdef CONFIG_POWER_S0IX
MCHP_INT_DISABLE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
#else
MCHP_ESPI_ACTIVATE |= 1;
#endif
#else
#ifdef CONFIG_POWER_S0IX
MCHP_INT_DISABLE(22) = MCHP_INT22_WAKE_ONLY_LPC;
MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_LPC;
#else
MCHP_LPC_ACT |= 1;
#endif
#endif
/* re-enable Port80 capture */
MCHP_P80_ACTIVATE(0) = 1;
#ifdef CONFIG_ADC
MCHP_ADC_CTRL |= 1;
#endif
/* Enable timer */
MCHP_TMR32_CTL(0) |= 1;
MCHP_TMR32_CTL(1) |= 1;
MCHP_TMR16_CTL(0) |= 1;
MCHP_INT_ENABLE(MCHP_TMR32_GIRQ) =
MCHP_TMR32_GIRQ_BIT(0) +
MCHP_TMR32_GIRQ_BIT(1);
MCHP_INT_ENABLE(MCHP_TMR16_GIRQ) =
MCHP_TMR16_GIRQ_BIT(0);
/* Enable watchdog */
#ifdef CONFIG_WATCHDOG
#ifdef CONFIG_CHIPSET_DEBUG
/* enable WDG stall on active JTAG and do not start */
MCHP_WDG_CTL = BIT(4);
#else
MCHP_WDG_CTL |= 1;
#endif
#endif
}
void clock_refresh_console_in_use(void)
{
disable_sleep(SLEEP_MASK_CONSOLE);
/* Set console in use expire time. */
console_expire_time = get_time();
console_expire_time.val += console_in_use_timeout_sec * SECOND;
}
/**
* Low power idle task. Executed when no tasks are ready to be scheduled.
*/
void __idle(void)
{
timestamp_t t0;
timestamp_t t1;
timestamp_t ht_t1;
uint32_t next_delay;
uint32_t max_sleep_time;
int time_for_dsleep;
int uart_ready_for_deepsleep;
htimer_init(); /* hibernation timer initialize */
disable_sleep(SLEEP_MASK_CONSOLE);
console_expire_time.val = get_time().val +
CONSOLE_IN_USE_ON_BOOT_TIME;
/*
* Print when the idle task starts. This is the lowest priority
* task, so this only starts once all other tasks have gotten a
* chance to do their task inits and have gone to sleep.
*/
CPRINTS("MEC1701 low power idle task started");
while (1) {
/* Disable interrupts */
interrupt_disable();
t0 = get_time(); /* uSec */
/* __hw_clock_event_get() is next programmed timer event */
next_delay = __hw_clock_event_get() - t0.le.lo;
time_for_dsleep = next_delay >
(HEAVY_SLEEP_RECOVER_TIME_USEC +
SET_HTIMER_DELAY_USEC);
max_sleep_time = next_delay -
HEAVY_SLEEP_RECOVER_TIME_USEC;
/* check if there enough time for deep sleep */
if (DEEP_SLEEP_ALLOWED && time_for_dsleep) {
trace0(0, MEC, 0, "Enough time for Deep Sleep");
/*
* Check if the console use has expired and
* console sleep is masked by GPIO(UART-RX)
* interrupt.
*/
if ((sleep_mask & SLEEP_MASK_CONSOLE) &&
t0.val > console_expire_time.val) {
/* allow console to sleep. */
enable_sleep(SLEEP_MASK_CONSOLE);
/*
* Wait one clock before checking if
* heavy sleep is allowed to give time
* for sleep mask to be updated.
*/
clock_wait_cycles(1);
if (LOW_SPEED_DEEP_SLEEP_ALLOWED)
CPRINTS("MEC1701 Disable console "
"in deepsleep");
}
/* UART is not being used */
uart_ready_for_deepsleep =
LOW_SPEED_DEEP_SLEEP_ALLOWED &&
!uart_tx_in_progress() &&
uart_buffer_empty();
/*
* Since MCHP's heavy sleep mode requires all
* blocks to be sleepable, UART/console's
* readiness is final decision factor of
* heavy sleep of EC.
*/
if (uart_ready_for_deepsleep) {
idle_dsleep_cnt++;
/*
* config UART Rx as GPIO wakeup
* interrupt source
*/
uart_enter_dsleep();
/* MCHP specific deep-sleep mode */
prepare_for_deep_sleep();
/*
* 'max_sleep_time' value should be big
* enough so that hibernation timer's
* interrupt triggers only after 'wfi'
* completes its excution.
*/
max_sleep_time -=
(get_time().le.lo - t0.le.lo);
/* setup/enable htimer wakeup interrupt */
system_set_htimer_alarm(0,
max_sleep_time);
/* set sleep all just before WFI */
MCHP_PCR_SYS_SLP_CTL |=
MCHP_PCR_SYS_SLP_HEAVY;
MCHP_PCR_SYS_SLP_CTL |=
MCHP_PCR_SYS_SLP_ALL;
} else {
idle_sleep_cnt++;
}
/* Wait for interrupt: goes into deep sleep. */
asm("dsb");
asm("wfi");
asm("isb");
asm("nop");
if (uart_ready_for_deepsleep) {
resume_from_deep_sleep();
/*
* Fast forward timer according to htimer
* counter:
* Since all blocks including timers
* will be in sleep mode, timers stops
* except hibernate timer.
* And system schedule timer should be
* corrected after wakeup by either
* hibernate timer or GPIO_UART_RX
* interrupt.
*/
ht_t1 = system_get_htimer();
/* disable/clear htimer wakeup interrupt */
system_reset_htimer_alarm();
t1.val = t0.val +
(uint64_t)(max_sleep_time -
ht_t1.le.lo);
force_time(t1);
/* re-eanble UART */
uart_exit_dsleep();
/* Record time spent in deep sleep. */
total_idle_dsleep_time_us +=
(uint64_t)(max_sleep_time -
ht_t1.le.lo);
}
} else { /* CPU 'Sleep' mode */
idle_sleep_cnt++;
asm("wfi");
}
interrupt_enable();
} /* while(1) */
}
#ifdef CONFIG_CMD_IDLE_STATS
/**
* Print low power idle statistics
*/
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
static void print_pcr_regs(void)
{
int i;
ccprintf("PCR regs before WFI\n");
for (i = 0; i < 5; i++) {
ccprintf("PCR SLP_EN[%d] = 0x%08X\n", pcr_slp_en[i]);
ccprintf("PCR CLK_REQ[%d] = 0x%08X\n", pcr_clk_req[i]);
}
}
#endif
static int command_idle_stats(int argc, char **argv)
{
timestamp_t ts = get_time();
ccprintf("Num idle calls that sleep: %d\n",
idle_sleep_cnt);
ccprintf("Num idle calls that deep-sleep: %d\n",
idle_dsleep_cnt);
ccprintf("Total Time spent in deep-sleep(sec): %.6ld(s)\n",
total_idle_dsleep_time_us);
ccprintf("Total time on: %.6lds\n\n",
ts.val);
#ifdef CONFIG_MCHP_DEEP_SLP_DEBUG
print_pcr_regs(); /* debug */
#endif
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats,
"",
"Print last idle stats");
#endif /* defined(CONFIG_CMD_IDLE_STATS) */
/**
* Configure deep sleep clock settings.
*/
static int command_dsleep(int argc, char **argv)
{
int v;
if (argc > 1) {
if (parse_bool(argv[1], &v)) {
/*
* Force deep sleep not to use heavy sleep mode or
* allow it to use the heavy sleep mode.
*/
if (v) /* 'on' */
disable_sleep(
SLEEP_MASK_FORCE_NO_LOW_SPEED);
else /* 'off' */
enable_sleep(
SLEEP_MASK_FORCE_NO_LOW_SPEED);
} else {
/* Set console in use timeout. */
char *e;
v = strtoi(argv[1], &e, 10);
if (*e)
return EC_ERROR_PARAM1;
console_in_use_timeout_sec = v;
/* Refresh console in use to use new timeout. */
clock_refresh_console_in_use();
}
}
ccprintf("Sleep mask: %08x\n", sleep_mask);
ccprintf("Console in use timeout: %d sec\n",
console_in_use_timeout_sec);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(dsleep, command_dsleep,
"[ on | off | <timeout> sec]",
"Deep sleep clock settings:\nUse 'on' to force deep "
"sleep NOT to enter heavysleep mode.\nUse 'off' to "
"allow deepsleep to use heavysleep whenever conditions "
"allow.\n"
"Give a timeout value for the console in use timeout.\n"
"See also 'sleepmask'.");
#endif /* CONFIG_LOW_POWER_IDLE */