summaryrefslogtreecommitdiff
path: root/chip/mchp/clock.c
diff options
context:
space:
mode:
authorScott Worley <scott.worley@microchip.corp-partner.google.com>2017-12-20 17:08:09 -0500
committerchrome-bot <chrome-bot@chromium.org>2017-12-28 12:35:07 -0800
commit4e9588ddcffb9315b0a74ac62121efb76b7b2202 (patch)
treed1ce8cea735b68c5e43f7631af2e90026006df58 /chip/mchp/clock.c
parentc334f648bd644f5e72e841458fdac6796efa1ceb (diff)
downloadchrome-ec-4e9588ddcffb9315b0a74ac62121efb76b7b2202.tar.gz
ec_chip_mchp: Add MCHP chip folder
BRANCH=none BUG= TEST=Review only. Committing small pieces until all code passes review. Change-Id: I9d16f95314a7c97b11c4fe61602c6db2621e6024 Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com>
Diffstat (limited to 'chip/mchp/clock.c')
-rw-r--r--chip/mchp/clock.c763
1 files changed, 763 insertions, 0 deletions
diff --git a/chip/mchp/clock.c b/chip/mchp/clock.c
new file mode 100644
index 0000000000..d0df0240f9
--- /dev/null
+++ b/chip/mchp/clock.c
@@ -0,0 +1,763 @@
+/* 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);
+
+#ifdef CONFIG_LOW_POWER_IDLE
+/**
+ * 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.
+ */
+static void htimer_init(void)
+{
+ MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_HTMR0);
+ MCHP_INT_ENABLE(MCHP_HTIMER_GIRQ) =
+ MCHP_HTIMER_GIRQ_BIT(0);
+ MCHP_HTIMER_PRELOAD(0) = 0; /* disable at beginning */
+
+ 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 and has two
+ * divider values.
+ * Control register bit[0] selects the divider.
+ * 0 is divide by 1 for 30.5us per LSB for a maximum of ~2 seconds.
+ * 1 is divide by 4096 for 0.125s per LSB for a maximum of ~2 hours.
+ */
+static void system_set_htimer_alarm(uint32_t seconds,
+ uint32_t microseconds)
+{
+ uint32_t hcnt;
+
+ if (microseconds >= 1000000) {
+ seconds += microseconds / 1000000;
+ microseconds -= (microseconds / 1000000) * 1000000;
+ }
+
+ if (seconds || microseconds) {
+
+ /* if (seconds > 2) { */
+ if (seconds > 1) {
+ /* count from 2 sec to 2 hrs, mec1322 sec 18.10.2 */
+ ASSERT(seconds <= 0xffff / 8);
+ /* 0.125(=1/8) per clock */
+ MCHP_HTIMER_CONTROL(0) = 1;
+ /* (number of counts to be loaded)
+ * = seconds * ( 8 clocks per second )
+ * + microseconds / 125000
+ * ---> (0 if (microseconds < 125000)
+ */
+ hcnt = (seconds * 8 + microseconds / 125000);
+
+ } else { /* count up to 2 sec. */
+ /* 30.5(= 2/61) usec */
+ MCHP_HTIMER_CONTROL(0) = 0;
+
+ /* (number of counts to be loaded)
+ * = (total microseconds) / 30.5;
+ */
+ hcnt = (seconds * 1000000 + microseconds) *
+ 2 / 61;
+ }
+
+ MCHP_HTIMER_PRELOAD(0) = hcnt;
+ }
+}
+
+/**
+ * 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 |= (1 << 2);
+
+ /* Stop timers */
+ MCHP_TMR32_CTL(0) &= ~1;
+ MCHP_TMR32_CTL(1) &= ~1;
+ MCHP_TMR16_CTL(0) &= ~1;
+ 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);
+ MCHP_INT_DISABLE(MCHP_TMR16_GIRQ) =
+ MCHP_TMR16_GIRQ_BIT(0);
+ MCHP_INT_SOURCE(MCHP_TMR16_GIRQ) =
+ MCHP_TMR16_GIRQ_BIT(0);
+
+#ifdef CONFIG_WATCHDOG
+ /* Stop watchdog */
+ MCHP_WDG_CTL &= ~1;
+#endif
+
+
+#ifdef CONFIG_ESPI
+ #ifdef CONFIG_POWER_S0IX
+ MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
+ MCHP_INT_ENABLE(22) = MCHP_INT22_WAKE_ONLY_ESPI;
+ #else
+ MCHP_ESPI_ACTIVATE &= ~1;
+ #endif
+#else
+ #ifdef CONFIG_POWER_S0IX
+ MCHP_INT_SOURCE(22) = MCHP_INT22_WAKE_ONLY_LPC;
+ MCHP_INT_ENABLE(22) = MCHP_INT22_WAKE_ONLY_LPC;
+ #else
+ MCHP_LPC_ACT |= 1;
+ #endif
+#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 &= ~(1 << 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_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 = (1 << 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 */