summaryrefslogtreecommitdiff
path: root/chip/it83xx/clock.c
blob: 41f800721a2cd7cea29322a277ae5ac74e3f69b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
/* Copyright 2013 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 "adc_chip.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "hwtimer.h"
#include "hwtimer_chip.h"
#include "intc.h"
#include "irq_chip.h"
#include "it83xx_pd.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
#include "util.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 SLEEP_SET_HTIMER_DELAY_USEC 250
#define SLEEP_FTIMER_SKIP_USEC      (HOOK_TICK_INTERVAL * 2)

static timestamp_t sleep_mode_t0;
static timestamp_t sleep_mode_t1;
static int idle_doze_cnt;
static int idle_sleep_cnt;
static uint64_t total_idle_sleep_time_us;
static uint32_t ec_sleep;
/*
 * 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 = 5;
static timestamp_t console_expire_time;

/* clock source is 32.768KHz */
#define TIMER_32P768K_CNT_TO_US(cnt) ((uint64_t)(cnt) * 1000000 / 32768)
#define TIMER_CNT_8M_32P768K(cnt)    (((cnt) / (8000000 / 32768)) + 1)
#endif /*CONFIG_LOW_POWER_IDLE */

static int freq;

struct clock_gate_ctrl {
	volatile uint8_t *reg;
	uint8_t mask;
};

static void clock_module_disable(void)
{
	/* bit0: FSPI interface tri-state */
	IT83XX_SMFI_FLHCTRL3R |= BIT(0);
	/* bit7: USB pad power-on disable */
	IT83XX_GCTRL_PMER2 &= ~BIT(7);
	/* bit7: USB debug disable */
	IT83XX_GCTRL_MCCR &= ~BIT(7);
	clock_disable_peripheral((CGC_OFFSET_EGPC | CGC_OFFSET_CIR), 0, 0);
	clock_disable_peripheral((CGC_OFFSET_SMBA | CGC_OFFSET_SMBB |
		CGC_OFFSET_SMBC | CGC_OFFSET_SMBD | CGC_OFFSET_SMBE |
		CGC_OFFSET_SMBF), 0, 0);
	clock_disable_peripheral((CGC_OFFSET_SSPI | CGC_OFFSET_PECI |
		CGC_OFFSET_USB), 0, 0);
}

enum pll_freq_idx {
	PLL_24_MHZ = 1,
	PLL_48_MHZ = 2,
	PLL_96_MHZ = 4,
};

static const uint8_t pll_to_idx[8] = {
	0,
	0,
	PLL_24_MHZ,
	0,
	PLL_48_MHZ,
	0,
	0,
	PLL_96_MHZ
};

struct clock_pll_t {
	int     pll_freq;
	uint8_t pll_setting;
	uint8_t div_fnd;
	uint8_t div_uart;
	uint8_t div_usb;
	uint8_t div_smb;
	uint8_t div_sspi;
	uint8_t div_ec;
	uint8_t div_jtag;
	uint8_t div_pwm;
	uint8_t div_usbpd;
};

const struct clock_pll_t clock_pll_ctrl[] = {
	/*
	 * UART:  24MHz
	 * SMB:   24MHz
	 * EC:     8MHz
	 * JTAG:  24MHz
	 * USBPD:  8MHz
	 * USB:   48MHz(no support if PLL=24MHz)
	 * SSPI:  48MHz(24MHz if PLL=24MHz)
	 */
	/* PLL:24MHz, MCU:24MHz, Fnd(e-flash):24MHz */
	[PLL_24_MHZ] = {24000000, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0x2},
#ifdef CONFIG_IT83XX_FLASH_CLOCK_48MHZ
	/* PLL:48MHz, MCU:48MHz, Fnd:48MHz */
	[PLL_48_MHZ] = {48000000, 4, 0, 1, 0, 1, 0, 6, 1, 0, 0x5},
	/* PLL:96MHz, MCU:96MHz, Fnd:48MHz */
	[PLL_96_MHZ] = {96000000, 7, 1, 3, 1, 3, 1, 6, 3, 1, 0xb},
#else
	/* PLL:48MHz, MCU:48MHz, Fnd:24MHz */
	[PLL_48_MHZ] = {48000000, 4, 1, 1, 0, 1, 0, 2, 1, 0, 0x5},
	/* PLL:96MHz, MCU:96MHz, Fnd:32MHz */
	[PLL_96_MHZ] = {96000000, 7, 2, 3, 1, 3, 1, 4, 3, 1, 0xb},
#endif
};

static uint8_t pll_div_fnd;
static uint8_t pll_div_ec;
static uint8_t pll_div_jtag;
static uint8_t pll_setting;

void __ram_code clock_ec_pll_ctrl(enum ec_pll_ctrl mode)
{
	volatile uint8_t _pll_ctrl __unused;

	IT83XX_ECPM_PLLCTRL = mode;
	/*
	 * for deep doze / sleep mode
	 * This load operation will ensure PLL setting is taken into
	 * control register before wait for interrupt instruction.
	 */
	_pll_ctrl = IT83XX_ECPM_PLLCTRL;

#ifdef IT83XX_CHIP_FLASH_NO_DEEP_POWER_DOWN
	/*
	 * WORKAROUND: this workaround is used to fix EC gets stuck in low power
	 * mode when WRST# is asserted.
	 *
	 * By default, flash will go into deep power down mode automatically
	 * when EC is in low power mode. But we got an issue on IT83202BX that
	 * flash won't be able to wake up correctly when WRST# is asserted
	 * under this condition.
	 * This issue might cause cold reset failure so we fix it.
	 *
	 * NOTE: this fix will increase power number about 40uA in low power
	 * mode.
	 */
	if (mode == EC_PLL_DOZE)
		IT83XX_SMFI_SMECCS &= ~IT83XX_SMFI_MASK_HOSTWA;
	else
		/*
		 * Don't send deep power down mode command to flash when EC in
		 * low power mode.
		 */
		IT83XX_SMFI_SMECCS |= IT83XX_SMFI_MASK_HOSTWA;
#endif
	/*
	 * barrier: ensure low power mode setting is taken into control
	 * register before standby instruction.
	 */
	data_serialization_barrier();
}

void __ram_code clock_pll_changed(void)
{
	IT83XX_GCTRL_SSCR &= ~BIT(0);
	/*
	 * Update PLL settings.
	 * Writing data to this register doesn't change the
	 * PLL frequency immediately until the status is changed
	 * into wakeup from the sleep mode.
	 * The following code is intended to make the system
	 * enter sleep mode, and set up a HW timer to wakeup EC to
	 * complete PLL update.
	 */
	IT83XX_ECPM_PLLFREQR = pll_setting;
	/* Pre-set FND clock frequency = PLL / 3 */
	IT83XX_ECPM_SCDCR0 = (2 << 4);
	/* JTAG and EC */
	IT83XX_ECPM_SCDCR3 = (pll_div_jtag << 4) | pll_div_ec;
	/* EC sleep after standby instruction */
	clock_ec_pll_ctrl(EC_PLL_SLEEP);
	if (IS_ENABLED(CHIP_CORE_NDS32)) {
		/* Global interrupt enable */
		asm volatile ("setgie.e");
		/* EC sleep */
		asm("standby wake_grant");
		/* Global interrupt disable */
		asm volatile ("setgie.d");
	} else if (IS_ENABLED(CHIP_CORE_RISCV)) {
		/* Global interrupt enable */
		asm volatile ("csrsi mstatus, 0x8");
		/* EC sleep */
		asm("wfi");
		/* Global interrupt disable */
		asm volatile ("csrci mstatus, 0x8");
	}
	/* New FND clock frequency */
	IT83XX_ECPM_SCDCR0 = (pll_div_fnd << 4);
	/* EC doze after standby instruction */
	clock_ec_pll_ctrl(EC_PLL_DOZE);
}

/* NOTE: Don't use this function in other place. */
static void clock_set_pll(enum pll_freq_idx idx)
{
	int pll;

	pll_div_fnd  = clock_pll_ctrl[idx].div_fnd;
	pll_div_ec   = clock_pll_ctrl[idx].div_ec;
	pll_div_jtag = clock_pll_ctrl[idx].div_jtag;
	pll_setting  = clock_pll_ctrl[idx].pll_setting;

	/* Update PLL settings or not */
	if (((IT83XX_ECPM_PLLFREQR & 0xf) != pll_setting) ||
		((IT83XX_ECPM_SCDCR0 & 0xf0) != (pll_div_fnd << 4)) ||
		((IT83XX_ECPM_SCDCR3 & 0xf) != pll_div_ec)) {
		/* Enable hw timer to wakeup EC from the sleep mode */
		ext_timer_ms(LOW_POWER_EXT_TIMER, EXT_PSR_32P768K_HZ,
				1, 1, 5, 1, 0);
		task_clear_pending_irq(et_ctrl_regs[LOW_POWER_EXT_TIMER].irq);
#ifdef CONFIG_HOSTCMD_ESPI
		/*
		 * Workaround for (b:70537592):
		 * We have to set chip select pin as input mode in order to
		 * change PLL.
		 */
		IT83XX_GPIO_GPCRM5 = (IT83XX_GPIO_GPCRM5 & ~0xc0) | BIT(7);
#ifdef IT83XX_ESPI_INHIBIT_CS_BY_PAD_DISABLED
		/*
		 * On DX version, we have to disable eSPI pad before changing
		 * PLL sequence or sequence will fail if CS# pin is low.
		 */
		espi_enable_pad(0);
#endif
#endif
		/* Update PLL settings. */
		clock_pll_changed();
#ifdef CONFIG_HOSTCMD_ESPI
#ifdef IT83XX_ESPI_INHIBIT_CS_BY_PAD_DISABLED
		/* Enable eSPI pad after changing PLL sequence. */
		espi_enable_pad(1);
#endif
		/* (b:70537592) Change back to ESPI CS# function. */
		IT83XX_GPIO_GPCRM5 &= ~0xc0;
#endif
	}

	/* Get new/current setting of PLL frequency */
	pll = pll_to_idx[IT83XX_ECPM_PLLFREQR & 0xf];
	/* USB and UART */
	IT83XX_ECPM_SCDCR1 = (clock_pll_ctrl[pll].div_usb << 4) |
				clock_pll_ctrl[pll].div_uart;
	/* SSPI and SMB */
	IT83XX_ECPM_SCDCR2 = (clock_pll_ctrl[pll].div_sspi << 4) |
				clock_pll_ctrl[pll].div_smb;
	/* USBPD and PWM */
	IT83XX_ECPM_SCDCR4 = (clock_pll_ctrl[pll].div_usbpd << 4) |
				clock_pll_ctrl[pll].div_pwm;
	/* Current PLL frequency  */
	freq = clock_pll_ctrl[pll].pll_freq;
}

void clock_init(void)
{
	uint32_t image_type = (uint32_t)clock_init;

	/* To change interrupt vector base if at RW image */
	if (image_type > CONFIG_RW_MEM_OFF)
		/* Interrupt Vector Table Base Address, in 64k Byte unit */
		IT83XX_GCTRL_IVTBAR = (CONFIG_RW_MEM_OFF >> 16) & 0xFF;

#if (PLL_CLOCK == 24000000)     || \
	(PLL_CLOCK == 48000000) || \
	(PLL_CLOCK == 96000000)
	/* Set PLL frequency */
	clock_set_pll(PLL_CLOCK / 24000000);
#else
#error "Support only for PLL clock speed of 24/48/96MHz."
#endif
	/*
	 * The VCC power status is treated as power-on.
	 * The VCC supply of LPC and related functions (EC2I,
	 * KBC, SWUC, PMC, CIR, SSPI, UART, BRAM, and PECI).
	 * It means VCC (pin 11) should be logic high before using
	 * these functions, or firmware treats VCC logic high
	 * as following setting.
	 */
	IT83XX_GCTRL_RSTS = (IT83XX_GCTRL_RSTS & 0x3F) + 0x40;

#if defined(IT83XX_ESPI_RESET_MODULE_BY_FW) && defined(CONFIG_HOSTCMD_ESPI)
	/*
	 * Because we don't support eSPI HW reset function (b/111480168) on DX
	 * version, so we have to reset eSPI configurations during init to
	 * ensure Host and EC are synchronized (especially for the field of
	 * I/O mode)
	 */
	if (!system_jumped_to_this_image())
		espi_fw_reset_module();
#endif
	/* Turn off auto clock gating. */
	IT83XX_ECPM_AUTOCG = 0x00;

	/* Default doze mode */
	clock_ec_pll_ctrl(EC_PLL_DOZE);

	clock_module_disable();

#ifdef CONFIG_HOSTCMD_X86
	IT83XX_WUC_WUESR4 = BIT(2);
	task_clear_pending_irq(IT83XX_IRQ_WKINTAD);
	/* bit2, wake-up enable for LPC access */
	IT83XX_WUC_WUENR4 |= BIT(2);
#endif
}

int clock_get_freq(void)
{
	return freq;
}

/**
 * Enable clock to specified peripheral
 *
 * @param offset Should be element of clock_gate_offsets enum.
 *               Bits 8-15 specify the ECPM offset of the specific clock reg.
 *               Bits 0-7 specify the mask for the clock register.
 * @param mask   Unused
 * @param mode   Unused
 */
void clock_enable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode)
{
	volatile uint8_t *reg = (volatile uint8_t *)
			(IT83XX_ECPM_BASE + (offset >> 8));
	uint8_t reg_mask = offset & 0xff;

	/*
	 * Note: CGCTRL3R, bit 6, must always write 1, but since there is no
	 * offset argument that addresses this bit, then we are guaranteed
	 * that this line will write a 1 to that bit.
	 */
	*reg &= ~reg_mask;
}

/**
 * Disable clock to specified peripheral
 *
 * @param offset Should be element of clock_gate_offsets enum.
 *               Bits 8-15 specify the ECPM offset of the specific clock reg.
 *               Bits 0-7 specify the mask for the clock register.
 * @param mask   Unused
 * @param mode   Unused
 */
void clock_disable_peripheral(uint32_t offset, uint32_t mask, uint32_t mode)
{
	volatile uint8_t *reg = (volatile uint8_t *)
			(IT83XX_ECPM_BASE + (offset >> 8));
	uint8_t reg_mask = offset & 0xff;
	uint8_t tmp_mask = 0;

	/* CGCTRL3R, bit 6, must always write a 1. */
	tmp_mask |= ((offset >> 8) == IT83XX_ECPM_CGCTRL3R_OFF) ? 0x40 : 0x00;

	*reg |= reg_mask | tmp_mask;
}

#ifdef CONFIG_LOW_POWER_IDLE
void clock_refresh_console_in_use(void)
{
	/* Set console in use expire time. */
	console_expire_time = get_time();
	console_expire_time.val += console_in_use_timeout_sec * SECOND;
}

static void clock_event_timer_clock_change(enum ext_timer_clock_source clock,
					uint32_t count)
{
	IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) &= ~BIT(0);
	IT83XX_ETWD_ETXPSR(EVENT_EXT_TIMER) = clock;
	IT83XX_ETWD_ETXCNTLR(EVENT_EXT_TIMER) = count;
	IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) |= 0x3;
}

static void clock_htimer_enable(void)
{
	uint32_t c;

	/* change event timer clock source to 32.768 KHz */
#ifdef IT83XX_EXT_OBSERVATION_REG_READ_TWO_TIMES
	c = TIMER_CNT_8M_32P768K(ext_observation_reg_read(EVENT_EXT_TIMER));
#else
	c = TIMER_CNT_8M_32P768K(IT83XX_ETWD_ETXCNTOR(EVENT_EXT_TIMER));
#endif
	clock_event_timer_clock_change(EXT_PSR_32P768K_HZ, c);
}

static int clock_allow_low_power_idle(void)
{
	/*
	 * Avoiding using low frequency clock run the same count as awaken in
	 * sleep mode, so don't go to sleep mode before timer reload count.
	 */
	if (!(IT83XX_ETWD_ETXCTRL(EVENT_EXT_TIMER) & BIT(0)))
		return 0;

	/* If timer interrupt status is set, don't go to sleep mode. */
	if (*et_ctrl_regs[EVENT_EXT_TIMER].isr &
		et_ctrl_regs[EVENT_EXT_TIMER].mask)
		return 0;

	/*
	 * If timer is less than 250us to expire, then we don't go to sleep
	 * mode.
	 */
#ifdef IT83XX_EXT_OBSERVATION_REG_READ_TWO_TIMES
	if (EVENT_TIMER_COUNT_TO_US(ext_observation_reg_read(EVENT_EXT_TIMER)) <
#else
	if (EVENT_TIMER_COUNT_TO_US(IT83XX_ETWD_ETXCNTOR(EVENT_EXT_TIMER)) <
#endif
		SLEEP_SET_HTIMER_DELAY_USEC)
		return 0;

	/*
	 * We calculate 32bit free clock overflow counts for 64bit value,
	 * if clock almost reach overflow, we don't go to sleep mode for
	 * avoiding miss overflow count.
	 */
	sleep_mode_t0 = get_time();
	if ((sleep_mode_t0.le.lo > (0xffffffff - SLEEP_FTIMER_SKIP_USEC)) ||
		(sleep_mode_t0.le.lo < SLEEP_FTIMER_SKIP_USEC))
		return 0;

	/* If we are waked up by console, then keep awake at least 5s. */
	if (sleep_mode_t0.val < console_expire_time.val)
		return 0;

	return 1;
}

int clock_ec_wake_from_sleep(void)
{
	return ec_sleep;
}

void __ram_code clock_cpu_standby(void)
{
	/* standby instruction */
	if (IS_ENABLED(CHIP_CORE_NDS32)) {
		asm("standby wake_grant");
	} else if (IS_ENABLED(CHIP_CORE_RISCV)) {
		if (!IS_ENABLED(IT83XX_RISCV_WAKEUP_CPU_WITHOUT_INT_ENABLED))
			/*
			 * we have to enable interrupts before
			 * standby instruction on IT83202 bx version.
			 */
			interrupt_enable();

		asm("wfi");
	}
}

void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
{
	int i;

	/* disable all interrupts */
	interrupt_disable();
	for (i = 0; i < IT83XX_IRQ_COUNT; i++) {
		chip_disable_irq(i);
		chip_clear_pending_irq(i);
	}
	/* bit5: watchdog is disabled. */
	IT83XX_ETWD_ETWCTRL |= BIT(5);

	/*
	 * Setup GPIOs for hibernate.  On some boards, it's possible that this
	 * may not return at all.  On those boards, power to the EC is likely
	 * being turn off entirely.
	 */
	if (board_hibernate_late) {
		/*
		 * Set reset flag in case board_hibernate_late() doesn't
		 * return.
		 */
		chip_save_reset_flags(EC_RESET_FLAG_HIBERNATE);
		board_hibernate_late();
	}

	if (seconds || microseconds) {
		/* At least 1 ms for hibernate. */
		uint64_t c = (seconds * 1000 + microseconds / 1000 + 1) * 1024;

		uint64divmod(&c, 1000);
		/* enable a 56-bit timer and clock source is 1.024 KHz */
		ext_timer_stop(FREE_EXT_TIMER_L, 1);
		ext_timer_stop(FREE_EXT_TIMER_H, 1);
		IT83XX_ETWD_ETXPSR(FREE_EXT_TIMER_L) = EXT_PSR_1P024K_HZ;
		IT83XX_ETWD_ETXPSR(FREE_EXT_TIMER_H) = EXT_PSR_1P024K_HZ;
		IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_L) = c & 0xffffff;
		IT83XX_ETWD_ETXCNTLR(FREE_EXT_TIMER_H) = (c >> 24) & 0xffffffff;
		ext_timer_start(FREE_EXT_TIMER_H, 1);
		ext_timer_start(FREE_EXT_TIMER_L, 0);
	}

	if (IS_ENABLED(CONFIG_USB_PD_TCPM_ITE_ON_CHIP)) {
		/*
		 * Disable active cc and pd modules and only left Rd_5.1k (Not
		 * Rd_DB) alive in hibernate for better power consumption.
		 */
		for (i = 0; i < CONFIG_USB_PD_ITE_ACTIVE_PORT_COUNT; i++)
			it83xx_Rd_5_1K_only_for_hibernate(i);
	}

	if (IS_ENABLED(CONFIG_ADC_VOLTAGE_COMPARATOR)) {
		/*
		 * Disable all voltage comparator modules in hibernate
		 * for better power consumption.
		 */
		for (i = CHIP_VCMP0; i < CHIP_VCMP_COUNT; i++)
			vcmp_enable(i, 0);
	}

	for (i = 0; i < hibernate_wake_pins_used; ++i)
		gpio_enable_interrupt(hibernate_wake_pins[i]);

	/* EC sleep */
	ec_sleep = 1;
#if defined(IT83XX_ESPI_INHIBIT_CS_BY_PAD_DISABLED) && \
defined(CONFIG_HOSTCMD_ESPI)
	/* Disable eSPI pad. */
	espi_enable_pad(0);
#endif
	clock_ec_pll_ctrl(EC_PLL_SLEEP);
	interrupt_enable();
	/* standby instruction */
	clock_cpu_standby();

	/* we should never reach that point */
	__builtin_unreachable();
}

/* use data type int here not bool to get better instruction number. */
static volatile int wait_interrupt_fired;
void clock_sleep_mode_wakeup_isr(void)
{
	uint32_t st_us, c;

	/* Clear flag on each interrupt. */
	if (IS_ENABLED(CHIP_CORE_RISCV))
		wait_interrupt_fired = 0;

	/* trigger a reboot if wake up EC from sleep mode (system hibernate) */
	if (clock_ec_wake_from_sleep()) {
#if defined(IT83XX_ESPI_INHIBIT_CS_BY_PAD_DISABLED) && \
defined(CONFIG_HOSTCMD_ESPI)
		/*
		 * Enable eSPI pad.
		 * We will not need to enable eSPI pad here if Dx is able to
		 * enable watchdog hardware reset function. But the function is
		 * failed (b:111264984), so the following system reset is
		 * software reset (PLL setting is not reset).
		 * We will not go into the change PLL sequence on reboot if PLL
		 * setting is the same, so the operation of enabling eSPI pad we
		 * added in clock_set_pll() will not be applied.
		 */
		espi_enable_pad(1);
#endif
		system_reset(SYSTEM_RESET_HARD);
	}

	if (IT83XX_ECPM_PLLCTRL == EC_PLL_DEEP_DOZE) {
		clock_ec_pll_ctrl(EC_PLL_DOZE);
		/* update free running timer */
		c = LOW_POWER_TIMER_MASK -
			IT83XX_ETWD_ETXCNTOR(LOW_POWER_EXT_TIMER);
		st_us = TIMER_32P768K_CNT_TO_US(c);
		sleep_mode_t1.val = sleep_mode_t0.val + st_us;
		__hw_clock_source_set(sleep_mode_t1.le.lo);

		/* reset event timer and clock source is 8 MHz */
		clock_event_timer_clock_change(EXT_PSR_8M_HZ, 0xffffffff);
		task_clear_pending_irq(et_ctrl_regs[EVENT_EXT_TIMER].irq);
		process_timers(0);
#ifdef CONFIG_HOSTCMD_X86
		/* disable lpc access wui */
		task_disable_irq(IT83XX_IRQ_WKINTAD);
		IT83XX_WUC_WUESR4 = BIT(2);
		task_clear_pending_irq(IT83XX_IRQ_WKINTAD);
#endif
		/* disable uart wui */
		uart_exit_dsleep();
		/* Record time spent in sleep. */
		total_idle_sleep_time_us += st_us;
	}
}

void __keep __idle_init(void)
{
	console_expire_time.val = get_time().val + CONSOLE_IN_USE_ON_BOOT_TIME;
	/* init hw timer and clock source is 32.768 KHz */
	ext_timer_ms(LOW_POWER_EXT_TIMER, EXT_PSR_32P768K_HZ, 1, 0,
		0xffffffff, 1, 1);

	/*
	 * 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("low power idle task started");
}

/**
 * Low power idle task. Executed when no tasks are ready to be scheduled.
 */
void __ram_code __idle(void)
{
	/*
	 * There is not enough space from ram code section to cache entire idle
	 * function, hence pull initialization function out of the section.
	 */
	__idle_init();

	while (1) {
		/* Disable interrupts */
		interrupt_disable();
		/* Check if the EC can enter deep doze mode or not */
		if (DEEP_SLEEP_ALLOWED && clock_allow_low_power_idle()) {
			/* reset low power mode hw timer */
			IT83XX_ETWD_ETXCTRL(LOW_POWER_EXT_TIMER) |= BIT(1);
			sleep_mode_t0 = get_time();
#ifdef CONFIG_HOSTCMD_X86
			/* enable lpc access wui */
			task_enable_irq(IT83XX_IRQ_WKINTAD);
#endif
			/* enable uart wui */
			uart_enter_dsleep();
			/* enable hw timer for deep doze / sleep mode wake-up */
			clock_htimer_enable();
			/* deep doze mode */
			clock_ec_pll_ctrl(EC_PLL_DEEP_DOZE);
			idle_sleep_cnt++;
		} else {
			/* doze mode */
			clock_ec_pll_ctrl(EC_PLL_DOZE);
			idle_doze_cnt++;
		}
		/* Set flag before entering low power mode. */
		if (IS_ENABLED(CHIP_CORE_RISCV))
			wait_interrupt_fired = 1;
		clock_cpu_standby();
		interrupt_enable();
		/*
		 * Sometimes wfi instruction may fail due to CPU's MTIP@mip
		 * register is non-zero.
		 * If the wait_interrupt_fired flag is true at this point,
		 * it means that EC waked-up by the above issue not an
		 * interrupt. Hence we loop running wfi instruction here until
		 * wfi success.
		 */
		while (IS_ENABLED(CHIP_CORE_RISCV) && wait_interrupt_fired)
			clock_cpu_standby();
	}
}
#endif /* CONFIG_LOW_POWER_IDLE */

#ifdef CONFIG_LOW_POWER_IDLE
#ifdef CONFIG_CMD_IDLE_STATS
/**
 * Print low power idle statistics
 */
static int command_idle_stats(int argc, char **argv)
{
	timestamp_t ts = get_time();

	ccprintf("Num idle calls that doze:            %d\n", idle_doze_cnt);
	ccprintf("Num idle calls that sleep:           %d\n", idle_sleep_cnt);

	ccprintf("Total Time spent in sleep(sec):      %.6lld(s)\n",
						total_idle_sleep_time_us);
	ccprintf("Total time on:                       %.6llds\n\n", ts.val);
	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(idlestats, command_idle_stats,
			"",
			"Print last idle stats");

#endif /* CONFIG_CMD_IDLE_STATS */
#endif /* CONFIG_LOW_POWER_IDLE */