/* 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 */
