text: /* Copyright (c) 2014 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.
   */
  
  /* System module for Chrome EC : NPCX hardware specific implementation */
  
  #include "clock.h"
  #include "clock_chip.h"
  #include "common.h"
  #include "console.h"
  #include "cpu.h"
  #include "gpio.h"
  #include "hooks.h"
  #include "host_command.h"
  #include "hwtimer_chip.h"
  #include "registers.h"
  #include "rom_chip.h"
  #include "system.h"
  #include "system_chip.h"
  #include "task.h"
  #include "timer.h"
  #include "util.h"
  
  /* Delay after writing TTC for value to latch */
  #define MTC_TTC_LOAD_DELAY_US 250
  #define MTC_ALARM_MASK     ((1 << 25) - 1)
  #define MTC_WUI_GROUP      MIWU_GROUP_4
  #define MTC_WUI_MASK       MASK_PIN7
  
  /* ROM address of chip revision */
  #define CHIP_REV_ADDR 0x00007FFC
  
  /* Console output macros */
  #define CPUTS(outstr) cputs(CC_SYSTEM, outstr)
  #define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
  #define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
  
  /*****************************************************************************/
  /* Internal functions */
  
  void system_watchdog_reset(void)
  {
  	/* Unlock & stop watchdog registers */
  	NPCX_WDSDM = 0x87;
  	NPCX_WDSDM = 0x61;
  	NPCX_WDSDM = 0x63;
  
  	/* Reset TWCFG */
  	NPCX_TWCFG = 0;
  	/* Select T0IN clock as watchdog prescaler clock */
  	SET_BIT(NPCX_TWCFG, NPCX_TWCFG_WDCT0I);
  
  	/* Clear watchdog reset status initially*/
  	SET_BIT(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS);
  
  	/* Keep prescaler ratio timer0 clock to 1:1 */
  	NPCX_TWCP = 0x00;
  
  	/* Set internal counter and prescaler */
  	NPCX_TWDT0 = 0x00;
  	NPCX_WDCNT = 0x01;
  
  	/* Disable interrupt */
  	interrupt_disable();
  	/* Reload and restart Timer 0*/
  	SET_BIT(NPCX_T0CSR, NPCX_T0CSR_RST);
  	/* Wait for timer is loaded and restart */
  	while (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_RST))
  		;
  	/* Enable interrupt */
  	interrupt_enable();
  }
  
  /* Return true if index is stored as a single byte in bbram */
  static int bbram_is_byte_access(enum bbram_data_index index)
  {
  	return (index >= BBRM_DATA_INDEX_VBNVCNTXT &&
  		index <  BBRM_DATA_INDEX_RAMLOG)
  #ifdef CONFIG_USB_PD_DUAL_ROLE
  		|| index == BBRM_DATA_INDEX_PD0
  		|| index == BBRM_DATA_INDEX_PD1
  #endif
  		|| index == BBRM_DATA_INDEX_PANIC_FLAGS
  	;
  }
  
  /* Check and clear BBRAM status on any reset */
  void system_check_bbram_on_reset(void)
  {
  	if (IS_BIT_SET(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR)) {
  		/*
  		 * If the reset cause is not power-on reset and VBAT has ever
  		 * dropped, print a warning message.
  		 */
  		if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_SCRATCH) ||
  			IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_STS))
  			CPRINTF("VBAT drop!\n");
  
  		/* Clear IBBR bit */
  		SET_BIT(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR);
  	}
  }
  
  /* Check index is within valid BBRAM range and IBBR is not set */
  static int bbram_valid(enum bbram_data_index index, int bytes)
  {
  	/* Check index */
  	if (index < 0 || index + bytes > NPCX_BBRAM_SIZE)
  		return 0;
  
  	/* Check BBRAM is valid */
  	if (IS_BIT_SET(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR)) {
  		SET_BIT(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR);
  		panic_printf("IBBR set: BBRAM corrupted!\n");
  		return 0;
  	}
  	return 1;
  }
  
  /**
   * Read battery-backed ram (BBRAM) at specified index.
   *
   * @return The value of the register or 0 if invalid index.
   */
  static uint32_t bbram_data_read(enum bbram_data_index index)
  {
  	uint32_t value = 0;
  	int bytes = bbram_is_byte_access(index) ? 1 : 4;
  
  	if (!bbram_valid(index, bytes))
  		return 0;
  
  	/* Read BBRAM */
  	if (bytes == 4) {
  		value += NPCX_BBRAM(index + 3);
  		value = value << 8;
  		value += NPCX_BBRAM(index + 2);
  		value = value << 8;
  		value += NPCX_BBRAM(index + 1);
  		value = value << 8;
  	}
  	value += NPCX_BBRAM(index);
  
  	return value;
  }
  
  /**
   * Write battery-backed ram (BBRAM) at specified index.
   *
   * @return nonzero if error.
   */
  static int bbram_data_write(enum bbram_data_index index, uint32_t value)
  {
  	int bytes = bbram_is_byte_access(index) ? 1 : 4;
  
  	if (!bbram_valid(index, bytes))
  		return EC_ERROR_INVAL;
  
  	/* Write BBRAM */
  	NPCX_BBRAM(index) = value & 0xFF;
  	if (bytes == 4) {
  		NPCX_BBRAM(index + 1) = (value >> 8)  & 0xFF;
  		NPCX_BBRAM(index + 2) = (value >> 16) & 0xFF;
  		NPCX_BBRAM(index + 3) = (value >> 24) & 0xFF;
  	}
  
  	/* Wait for write-complete */
  	return EC_SUCCESS;
  }
  
  /* Map idx to a returned BBRM_DATA_INDEX_*, or return -1 on invalid idx */
  static int bbram_idx_lookup(enum system_bbram_idx idx)
  {
  	if (idx >= SYSTEM_BBRAM_IDX_VBNVBLOCK0 &&
  	    idx <= SYSTEM_BBRAM_IDX_VBNVBLOCK15)
  		return BBRM_DATA_INDEX_VBNVCNTXT +
  		       idx - SYSTEM_BBRAM_IDX_VBNVBLOCK0;
  #ifdef CONFIG_USB_PD_DUAL_ROLE
  	if (idx == SYSTEM_BBRAM_IDX_PD0)
  		return BBRM_DATA_INDEX_PD0;
  	if (idx == SYSTEM_BBRAM_IDX_PD1)
  		return BBRM_DATA_INDEX_PD1;
  #endif
  #ifdef CONFIG_VBOOT_EFS
  	if (idx == SYSTEM_BBRAM_IDX_TRY_SLOT)
  		return BBRM_DATA_INDEX_TRY_SLOT;
  #endif
  	return -1;
  }
  
  int system_get_bbram(enum system_bbram_idx idx, uint8_t *value)
  {
  	int bbram_idx = bbram_idx_lookup(idx);
  
  	if (bbram_idx < 0)
  		return EC_ERROR_INVAL;
  
  	*value = bbram_data_read(bbram_idx);
  	return EC_SUCCESS;
  }
  
  int system_set_bbram(enum system_bbram_idx idx, uint8_t value)
  {
  	int bbram_idx = bbram_idx_lookup(idx);
  
  	if (bbram_idx < 0)
  		return EC_ERROR_INVAL;
  
  	return bbram_data_write(bbram_idx, value);
  }
  
  /* MTC functions */
  uint32_t system_get_rtc_sec(void)
  {
  	/* Get MTC counter unit:seconds */
  	uint32_t sec = NPCX_TTC;
  	return sec;
  }
  
  void system_set_rtc(uint32_t seconds)
  {
  	/*
  	 * Set MTC counter unit:seconds, write twice to ensure values
  	 * latch to NVMem.
  	 */
  	NPCX_TTC = seconds;
  	udelay(MTC_TTC_LOAD_DELAY_US);
  	NPCX_TTC = seconds;
  	udelay(MTC_TTC_LOAD_DELAY_US);
  }
  
  #ifdef CONFIG_CHIP_PANIC_BACKUP
  /*
   * Following information from panic data is stored in BBRAM:
   *
   * index     |       data
   * ==========|=============
   *   36      |       MMFS
   *   40      |       HFSR
   *   44      |       BFAR
   *   48      |      LREG1
   *   52      |      LREG3
   *   56      |      LREG4
   *   60      |     reserved
   *
   * Above registers are chosen to be saved in case of panic because:
   * 1. MMFS, HFSR and BFAR seem to provide more information about the fault.
   * 2. LREG1, LREG3 and LREG4 store exception, reason and info in case of
   * software panic.
   */
  #define BKUP_MMFS		(BBRM_DATA_INDEX_PANIC_BKUP + 0)
  #define BKUP_HFSR		(BBRM_DATA_INDEX_PANIC_BKUP + 4)
  #define BKUP_BFAR		(BBRM_DATA_INDEX_PANIC_BKUP + 8)
  #define BKUP_LREG1		(BBRM_DATA_INDEX_PANIC_BKUP + 12)
  #define BKUP_LREG3		(BBRM_DATA_INDEX_PANIC_BKUP + 16)
  #define BKUP_LREG4		(BBRM_DATA_INDEX_PANIC_BKUP + 20)
  
  #define BKUP_PANIC_DATA_VALID	(1 << 0)
  
  void chip_panic_data_backup(void)
  {
  	struct panic_data *d = panic_get_data();
  
  	if (!d)
  		return;
  
  	bbram_data_write(BKUP_MMFS, d->cm.mmfs);
  	bbram_data_write(BKUP_HFSR, d->cm.hfsr);
  	bbram_data_write(BKUP_BFAR, d->cm.dfsr);
  	bbram_data_write(BKUP_LREG1, d->cm.regs[1]);
  	bbram_data_write(BKUP_LREG3, d->cm.regs[3]);
  	bbram_data_write(BKUP_LREG4, d->cm.regs[4]);
  	bbram_data_write(BBRM_DATA_INDEX_PANIC_FLAGS, BKUP_PANIC_DATA_VALID);
  }
  
  static void chip_panic_data_restore(void)
  {
  	struct panic_data *d = PANIC_DATA_PTR;
  
  	/* Ensure BBRAM is valid. */
  	if (!bbram_valid(BKUP_MMFS, 4))
  		return;
  
  	/* Ensure Panic data in BBRAM is valid. */
  	if (!(bbram_data_read(BBRM_DATA_INDEX_PANIC_FLAGS) &
  	      BKUP_PANIC_DATA_VALID))
  		return;
  
  	memset(d, 0, sizeof(*d));
  	d->magic = PANIC_DATA_MAGIC;
  	d->struct_size = sizeof(*d);
  	d->struct_version = 2;
  	d->arch = PANIC_ARCH_CORTEX_M;
  
  	d->cm.mmfs = bbram_data_read(BKUP_MMFS);
  	d->cm.hfsr = bbram_data_read(BKUP_HFSR);
  	d->cm.dfsr = bbram_data_read(BKUP_BFAR);
  
  	d->cm.regs[1] = bbram_data_read(BKUP_LREG1);
  	d->cm.regs[3] = bbram_data_read(BKUP_LREG3);
  	d->cm.regs[4] = bbram_data_read(BKUP_LREG4);
  
  	/* Reset panic data in BBRAM. */
  	bbram_data_write(BBRM_DATA_INDEX_PANIC_FLAGS, 0);
  }
  #endif /* CONFIG_CHIP_PANIC_BACKUP */
  
  void chip_save_reset_flags(int flags)
  {
  	bbram_data_write(BBRM_DATA_INDEX_SAVED_RESET_FLAGS, flags);
  }
  
  #ifdef CONFIG_POWER_BUTTON_INIT_IDLE
  /*
   * Set/clear AP_OFF flag. It's set when the system gracefully shuts down and
   * it's cleared when the system boots up. The result is the system tries to
   * go back to the previous state upon AC plug-in. If the system uncleanly
   * shuts down, it boots immediately. If the system shuts down gracefully,
   * it'll stay at S5 and wait for power button press.
   */
  static void board_chipset_startup(void)
  {
  	uint32_t flags = bbram_data_read(BBRM_DATA_INDEX_SAVED_RESET_FLAGS);
  	flags &= ~RESET_FLAG_AP_OFF;
  	chip_save_reset_flags(flags);
  	system_clear_reset_flags(RESET_FLAG_AP_OFF);
  	CPRINTS("Cleared AP_OFF flag");
  }
  DECLARE_HOOK(HOOK_CHIPSET_STARTUP, board_chipset_startup, HOOK_PRIO_DEFAULT);
  
  static void board_chipset_shutdown(void)
  {
  	uint32_t flags = bbram_data_read(BBRM_DATA_INDEX_SAVED_RESET_FLAGS);
  	flags |= RESET_FLAG_AP_OFF;
  	chip_save_reset_flags(flags);
  	system_set_reset_flags(RESET_FLAG_AP_OFF);
  	CPRINTS("Set AP_OFF flag");
  }
  DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, board_chipset_shutdown, HOOK_PRIO_DEFAULT);
  #endif
  
  /* Check reset cause */
  void system_check_reset_cause(void)
  {
  	uint32_t hib_wake_flags = bbram_data_read(BBRM_DATA_INDEX_WAKE);
  	uint32_t flags = bbram_data_read(BBRM_DATA_INDEX_SAVED_RESET_FLAGS);
  
  	/* Clear saved reset flags in bbram */
  #ifdef CONFIG_POWER_BUTTON_INIT_IDLE
  	/* We'll clear AP_OFF on S5->S3 transition */
  	chip_save_reset_flags(flags & RESET_FLAG_AP_OFF);
  #else
  	chip_save_reset_flags(0);
  #endif
  	/* Clear saved hibernate wake flag in bbram , too */
  	bbram_data_write(BBRM_DATA_INDEX_WAKE, 0);
  
  	/* Use scratch bit to check power on reset or VCC1_RST reset */
  	if (!IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_SCRATCH)) {
  #if defined(BOARD_WHEATLEY) || defined(BOARD_EVE) || defined(BOARD_POPPY) || defined(BOARD_SORAKA)
  		/* TODO(crosbug.com/p/61028): Remove workaround for Eve */
  		flags |= RESET_FLAG_RESET_PIN;
  #else
  		/* Check for VCC1 reset */
  		if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_STS))
  			flags |= RESET_FLAG_RESET_PIN;
  		else
  			flags |= RESET_FLAG_POWER_ON;
  #endif
  	}
  
  	/*
  	 * Set scratch bit to distinguish VCC1RST# is asserted again
  	 * or not. This bit will be clear automatically when VCC1RST#
  	 * is asserted or power-on reset occurs
  	 */
  	SET_BIT(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_SCRATCH);
  
  	/* Software debugger reset */
  	if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_DBGRST_STS)) {
  		flags |= RESET_FLAG_SOFT;
  		/* Clear debugger reset status initially*/
  		SET_BIT(NPCX_RSTCTL, NPCX_RSTCTL_DBGRST_STS);
  	}
  
  	/* Reset by hibernate */
  	if (hib_wake_flags & HIBERNATE_WAKE_PIN)
  		flags |= RESET_FLAG_WAKE_PIN | RESET_FLAG_HIBERNATE;
  	else if (hib_wake_flags & HIBERNATE_WAKE_MTC)
  		flags |= RESET_FLAG_RTC_ALARM | RESET_FLAG_HIBERNATE;
  
  	/* Watchdog Reset */
  	if (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS)) {
  		/*
  		 * Don't set RESET_FLAG_WATCHDOG flag if watchdog is issued by
  		 * system_reset or hibernate in order to distinguish reset cause
  		 * is panic reason or not.
  		 */
  		if (!(flags & (RESET_FLAG_SOFT | RESET_FLAG_HARD |
  				RESET_FLAG_HIBERNATE)))
  			flags |= RESET_FLAG_WATCHDOG;
  
  		/* Clear watchdog reset status initially*/
  		SET_BIT(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS);
  	}
  
  	system_set_reset_flags(flags);
  }
  
  /**
   * Chip-level function to set GPIOs and wake-up inputs for hibernate.
   */
  void system_set_gpios_and_wakeup_inputs_hibernate(void)
  {
  	int table, i;
  
  	/* Disable all MIWU inputs before entering hibernate */
  	for (table = MIWU_TABLE_0 ; table < MIWU_TABLE_2 ; table++) {
  		for (i = 0 ; i < 8 ; i++) {
  			/* Disable all wake-ups */
  			NPCX_WKEN(table, i)  = 0x00;
  			/* Clear all pending bits of wake-ups */
  			NPCX_WKPCL(table, i) = 0xFF;
  			/*
  			 * Disable all inputs of wake-ups to prevent leakage
  			 * caused by input floating.
  			 */
  			NPCX_WKINEN(table, i) = 0x00;
  		}
  	}
  
  #if defined(CHIP_FAMILY_NPCX7)
  	/* Disable MIWU 2 group 6 inputs which used for the additional GPIOs */
  	NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_6)  = 0x00;
  	NPCX_WKPCL(MIWU_TABLE_2, MIWU_GROUP_6) = 0xFF;
  	NPCX_WKINEN(MIWU_TABLE_2, MIWU_GROUP_6) = 0x00;
  #endif
  
  	/* Enable wake-up inputs of hibernate_wake_pins array */
  	for (i = 0; i < hibernate_wake_pins_used; i++) {
  		gpio_reset(hibernate_wake_pins[i]);
  		/* Re-enable interrupt for wake-up inputs */
  		gpio_enable_interrupt(hibernate_wake_pins[i]);
  #if defined(CONFIG_HIBERNATE_PSL)
  		/* Config PSL pins setting for wake-up inputs */
  		if (!system_config_psl_mode(hibernate_wake_pins[i]))
  			ccprintf("Invalid PSL setting in wake-up pin %d\n", i);
  #endif
  	}
  }
  
  /**
   * hibernate function for npcx ec.
   *
   * @param seconds      Number of seconds to sleep before LCT alarm
   * @param microseconds Number of microseconds to sleep before LCT alarm
   */
  void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
  {
  	int i;
  
  	/* Disable ADC */
  	NPCX_ADCCNF = 0;
  	usleep(1000);
  
  	/* Set SPI pins to be in Tri-State */
  	SET_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS);
  
  	/* Disable instant wake up mode for better power consumption */
  	CLEAR_BIT(NPCX_ENIDL_CTL, NPCX_ENIDL_CTL_LP_WK_CTL);
  
  	/* Disable interrupt */
  	interrupt_disable();
  
  	/* ITIM event module disable */
  	CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITCTS_ITEN);
  	/* ITIM time module disable */
  	CLEAR_BIT(NPCX_ITCTS(ITIM32), NPCX_ITCTS_ITEN);
  	/* ITIM watchdog warn module disable */
  	CLEAR_BIT(NPCX_ITCTS(ITIM_WDG_NO), NPCX_ITCTS_ITEN);
  
  	/* Unlock & stop watchdog */
  	NPCX_WDSDM = 0x87;
  	NPCX_WDSDM = 0x61;
  	NPCX_WDSDM = 0x63;
  
  	/* Initialize watchdog */
  	NPCX_TWCFG = 0; /* Select T0IN clock as watchdog prescaler clock */
  	SET_BIT(NPCX_TWCFG, NPCX_TWCFG_WDCT0I);
  	NPCX_TWCP = 0x00; /* Keep prescaler ratio timer0 clock to 1:1 */
  	NPCX_TWDT0 = 0x00; /* Set internal counter and prescaler */
  
  	/* Disable interrupt */
  	interrupt_disable();
  
  	/*
  	 * Set gpios and wake-up input for better power consumption before
  	 * entering hibernate.
  	 */
  	system_set_gpios_and_wakeup_inputs_hibernate();
  
  	/*
  	 * Give the board a chance to do any late stage hibernation work.
  	 * This is likely going to configure GPIOs for hibernation.
  	 */
  	if (board_hibernate_late)
  		board_hibernate_late();
  
  	/* Clear all pending IRQ otherwise wfi will have no affect */
  	for (i = NPCX_IRQ_0 ; i < NPCX_IRQ_COUNT ; i++)
  		task_clear_pending_irq(i);
  
  	/*
  	 * Set RTC interrupt in time to wake up before
  	 * next event.
  	 */
  	if (seconds || microseconds)
  		system_set_rtc_alarm(seconds, microseconds);
  
  
  	/* execute hibernate func depend on chip series */
  	__hibernate_npcx_series();
  
  }
  
  static char system_to_hex(uint8_t x)
  {
  	if (x <= 9)
  		return '0' + x;
  	return 'a' + x - 10;
  }
  
  /*****************************************************************************/
  /* IC specific low-level driver */
  
  /*
   * Microseconds will be ignored.  The WTC register only
   * stores wakeup time in seconds.
   * Set seconds = 0 to disable the alarm
   */
  void system_set_rtc_alarm(uint32_t seconds, uint32_t microseconds)
  {
  	uint32_t cur_secs, alarm_secs;
  
  	if (seconds == EC_RTC_ALARM_CLEAR) {
  		CLEAR_BIT(NPCX_WTC, NPCX_WTC_WIE);
  		SET_BIT(NPCX_WTC, NPCX_WTC_PTO);
  
  		return;
  	}
  
  	/* Get current clock */
  	cur_secs = NPCX_TTC;
  
  	/* If alarm clock is not sequential or not in range */
  	alarm_secs = cur_secs + seconds;
  	alarm_secs = alarm_secs & MTC_ALARM_MASK;
  
  	/*
  	 * We should set new alarm (first 25 bits of clock value) first before
  	 * clearing PTO in case issue rtc interrupt immediately.
  	 */
  	NPCX_WTC = alarm_secs;
  
  	/* Reset alarm first */
  	system_reset_rtc_alarm();
  
  	/* Enable interrupt mode alarm */
  	SET_BIT(NPCX_WTC, NPCX_WTC_WIE);
  
  	/* Enable MTC interrupt */
  	task_enable_irq(NPCX_IRQ_MTC_WKINTAD_0);
  
  	/* Enable wake-up input sources & clear pending bit */
  	NPCX_WKPCL(MIWU_TABLE_0, MTC_WUI_GROUP)  |= MTC_WUI_MASK;
  	NPCX_WKINEN(MIWU_TABLE_0, MTC_WUI_GROUP) |= MTC_WUI_MASK;
  	NPCX_WKEN(MIWU_TABLE_0, MTC_WUI_GROUP)   |= MTC_WUI_MASK;
  }
  
  void system_reset_rtc_alarm(void)
  {
  	/*
  	 * Clear interrupt & Disable alarm interrupt
  	 * Update alarm value to zero
  	 */
  	CLEAR_BIT(NPCX_WTC, NPCX_WTC_WIE);
  	SET_BIT(NPCX_WTC, NPCX_WTC_PTO);
  
  	/* Disable MTC interrupt */
  	task_disable_irq(NPCX_IRQ_MTC_WKINTAD_0);
  }
  
  /*
   * Return the seconds remaining before the RTC alarm goes off.
   * Returns 0 if alarm is not set.
   */
  uint32_t system_get_rtc_alarm(void)
  {
  	/*
  	 * Return 0:
  	 * 1. If alarm is not set to go off, OR
  	 * 2. If alarm is set and has already gone off
  	 */
  	if (!IS_BIT_SET(NPCX_WTC, NPCX_WTC_WIE) ||
  	    IS_BIT_SET(NPCX_WTC, NPCX_WTC_PTO)) {
  		return 0;
  	}
  	/* Get seconds before alarm goes off */
  	return (NPCX_WTC - NPCX_TTC) & MTC_ALARM_MASK;
  }
  
  /**
   * Enable hibernate interrupt
   */
  void system_enable_hib_interrupt(void)
  {
  	task_enable_irq(NPCX_IRQ_MTC_WKINTAD_0);
  }
  
  void system_hibernate(uint32_t seconds, uint32_t microseconds)
  {
  	/* Flush console before hibernating */
  	cflush();
  
  	if (board_hibernate)
  		board_hibernate();
  
  #if SUPPORT_HIB
  	/* Add additional hibernate operations here */
  	__enter_hibernate(seconds, microseconds);
  #endif
  }
  
  void system_pre_init(void)
  {
  	/*
  	 * Add additional initialization here
  	 * EC should be initialized in Booter
  	 */
  
  	/* Power-down the modules we don't need */
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_1) = 0xF9; /* Skip SDP_PD FIU_PD */
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_2) = 0xFF;
  #if defined(CHIP_FAMILY_NPCX5)
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_3) = 0x0F; /* Skip GDMA */
  #elif defined(CHIP_FAMILY_NPCX7)
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_3) = 0x1F; /* Skip GDMA */
  #endif
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_4) = 0xF4; /* Skip ITIM2/1_PD */
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_5) = 0xF8;
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6) = 0xF5; /* Skip ITIM5_PD */
  #if defined(CHIP_FAMILY_NPCX7)
  	NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0x07;
  #endif
  
  	/* Following modules can be powered down automatically in npcx7 */
  #if defined(CHIP_FAMILY_NPCX5)
  	/* Power down the modules of npcx5 used internally */
  	NPCX_INTERNAL_CTRL1 = 0x03;
  	NPCX_INTERNAL_CTRL2 = 0x03;
  	NPCX_INTERNAL_CTRL3 = 0x03;
  
  	/* Enable low-power regulator */
  	CLEAR_BIT(NPCX_LFCGCALCNT, NPCX_LFCGCALCNT_LPREG_CTL_EN);
  	SET_BIT(NPCX_LFCGCALCNT, NPCX_LFCGCALCNT_LPREG_CTL_EN);
  #endif
  
  	/*
  	 * Configure LPRAM in the MPU as a regular memory
  	 * and DATA RAM to prevent code execution
  	 */
  	system_mpu_config();
  
  #ifdef CONFIG_CHIP_PANIC_BACKUP
  	chip_panic_data_restore();
  #endif
  }
  
  void system_reset(int flags)
  {
  	uint32_t save_flags = 0;
  
  	/* Disable interrupts to avoid task swaps during reboot */
  	interrupt_disable();
  
  	/* Save current reset reasons if necessary */
  	if (flags & SYSTEM_RESET_PRESERVE_FLAGS)
  		save_flags = system_get_reset_flags() | RESET_FLAG_PRESERVED;
  
  	/* Add in AP off flag into saved flags. */
  	if (flags & SYSTEM_RESET_LEAVE_AP_OFF)
  		save_flags |= RESET_FLAG_AP_OFF;
  
  	/* Save reset flag */
  	if (flags & SYSTEM_RESET_HARD)
  		save_flags |= RESET_FLAG_HARD;
  	else
  		save_flags |= RESET_FLAG_SOFT;
  
  	/* Store flags to battery backed RAM. */
  	chip_save_reset_flags(save_flags);
  
  	/* Ask the watchdog to trigger a hard reboot */
  	system_watchdog_reset();
  
  	/* Spin and wait for reboot; should never return */
  	while (1)
  		;
  }
  
  /**
   * Return the chip vendor/name/revision string.
   */
  const char *system_get_chip_vendor(void)
  {
  	static char str[15] = "Unknown-";
  	char *p = str + 8;
  
  	/* Read Vendor ID in core register */
  	uint8_t fam_id = NPCX_SID_CR;
  	switch (fam_id) {
  	case 0x20:
  		return "Nuvoton";
  	default:
  		*p       = system_to_hex((fam_id & 0xF0) >> 4);
  		*(p + 1) = system_to_hex(fam_id & 0x0F);
  		*(p + 2) = '\0';
  		return str;
  	}
  }
  
  const char *system_get_chip_name(void)
  {
  	static char str[15] = "Unknown-";
  	char *p = str + 8;
  
  	/* Read Chip ID in core register */
  	uint8_t chip_id = NPCX_DEVICE_ID_CR;
  	switch (chip_id) {
  #if defined(CHIP_FAMILY_NPCX5)
  	case 0x12:
  		return "NPCX585G";
  	case 0x13:
  		return "NPCX575G";
  	case 0x16:
  		return "NPCX586G";
  	case 0x17:
  		return "NPCX576G";
  #elif defined(CHIP_FAMILY_NPCX7)
  	case 0x21:
  		return "NPCX796F";
  #endif
  	default:
  		*p       = system_to_hex((chip_id & 0xF0) >> 4);
  		*(p + 1) = system_to_hex(chip_id & 0x0F);
  		*(p + 2) = '\0';
  		return str;
  	}
  }
  
  const char *system_get_chip_revision(void)
  {
  	static char rev[5];
  	/* Read ROM data for chip revision directly */
  	uint8_t rev_num = *((uint8_t *)CHIP_REV_ADDR);
  
  	*(rev) = 'A';
  	*(rev + 1) = '.';
  	*(rev + 2) = system_to_hex((rev_num & 0xF0) >> 4);
  	*(rev + 3) = system_to_hex(rev_num & 0x0F);
  	*(rev + 4) = '\0';
  
  	return rev;
  }
  
  BUILD_ASSERT(BBRM_DATA_INDEX_VBNVCNTXT + EC_VBNV_BLOCK_SIZE <= NPCX_BBRAM_SIZE);
  
  /**
   * Set a scratchpad register to the specified value.
   *
   * The scratchpad register must maintain its contents across a
   * software-requested warm reset.
   *
   * @param value		Value to store.
   * @return EC_SUCCESS, or non-zero if error.
   */
  int system_set_scratchpad(uint32_t value)
  {
  	return bbram_data_write(BBRM_DATA_INDEX_SCRATCHPAD, value);
  }
  
  uint32_t system_get_scratchpad(void)
  {
  	return bbram_data_read(BBRM_DATA_INDEX_SCRATCHPAD);
  }
  
  int system_is_reboot_warm(void)
  {
  	uint32_t reset_flags;
  
  	/*
  	 * Check reset cause here,
  	 * gpio_pre_init is executed faster than system_pre_init
  	 */
  	system_check_reset_cause();
  	reset_flags = system_get_reset_flags();
  
  	if ((reset_flags & RESET_FLAG_RESET_PIN) ||
  	    (reset_flags & RESET_FLAG_POWER_ON) ||
  	    (reset_flags & RESET_FLAG_WATCHDOG) ||
  	    (reset_flags & RESET_FLAG_HARD) ||
  	    (reset_flags & RESET_FLAG_SOFT) ||
  	    (reset_flags & RESET_FLAG_HIBERNATE))
  		return 0;
  	else
  		return 1;
  }
  
  /*****************************************************************************/
  /* Console commands */
  #ifdef CONFIG_CMD_RTC
  void print_system_rtc(enum console_channel ch)
  {
  	uint32_t sec = system_get_rtc_sec();
  
  	cprintf(ch, "RTC: 0x%08x (%d.00 s)\n", sec, sec);
  }
  
  static int command_system_rtc(int argc, char **argv)
  {
  	if (argc == 3 && !strcasecmp(argv[1], "set")) {
  		char *e;
  		uint32_t t = strtoi(argv[2], &e, 0);
  		if (*e)
  			return EC_ERROR_PARAM2;
  
  		system_set_rtc(t);
  	} else if (argc > 1) {
  		return EC_ERROR_INVAL;
  	}
  
  	print_system_rtc(CC_COMMAND);
  
  	return EC_SUCCESS;
  }
  DECLARE_CONSOLE_COMMAND(rtc, command_system_rtc,
  		"[set <seconds>]",
  		"Get/set real-time clock");
  
  #ifdef CONFIG_CMD_RTC_ALARM
  /**
   * Test the RTC alarm by setting an interrupt on RTC match.
   */
  static int command_rtc_alarm_test(int argc, char **argv)
  {
  	int s = 1, us = 0;
  	char *e;
  
  	ccprintf("Setting RTC alarm\n");
  	system_enable_hib_interrupt();
  
  	if (argc > 1) {
  		s = strtoi(argv[1], &e, 10);
  		if (*e)
  			return EC_ERROR_PARAM1;
  
  	}
  	if (argc > 2) {
  		us = strtoi(argv[2], &e, 10);
  		if (*e)
  			return EC_ERROR_PARAM2;
  
  	}
  
  	system_set_rtc_alarm(s, us);
  
  	return EC_SUCCESS;
  }
  DECLARE_CONSOLE_COMMAND(rtc_alarm, command_rtc_alarm_test,
  		"[seconds [microseconds]]",
  		"Test alarm");
  #endif /* CONFIG_CMD_RTC_ALARM */
  #endif /* CONFIG_CMD_RTC */
  
  /*****************************************************************************/
  /* Host commands */
  
  #ifdef CONFIG_HOSTCMD_RTC
  static int system_rtc_get_value(struct host_cmd_handler_args *args)
  {
  	struct ec_response_rtc *r = args->response;
  
  	r->time = system_get_rtc_sec();
  	args->response_size = sizeof(*r);
  
  	return EC_RES_SUCCESS;
  }
  DECLARE_HOST_COMMAND(EC_CMD_RTC_GET_VALUE,
  		system_rtc_get_value,
  		EC_VER_MASK(0));
  
  static int system_rtc_set_value(struct host_cmd_handler_args *args)
  {
  	const struct ec_params_rtc *p = args->params;
  
  	system_set_rtc(p->time);
  	return EC_RES_SUCCESS;
  }
  DECLARE_HOST_COMMAND(EC_CMD_RTC_SET_VALUE,
  		system_rtc_set_value,
  		EC_VER_MASK(0));
  
  static int system_rtc_set_alarm(struct host_cmd_handler_args *args)
  {
  	const struct ec_params_rtc *p = args->params;
  
  	system_set_rtc_alarm(p->time, 0);
  	return EC_RES_SUCCESS;
  }
  DECLARE_HOST_COMMAND(EC_CMD_RTC_SET_ALARM,
  		system_rtc_set_alarm,
  		EC_VER_MASK(0));
  
  static int system_rtc_get_alarm(struct host_cmd_handler_args *args)
  {
  	struct ec_response_rtc *r = args->response;
  
  	r->time = system_get_rtc_alarm();
  	args->response_size = sizeof(*r);
  
  	return EC_RES_SUCCESS;
  }
  DECLARE_HOST_COMMAND(EC_CMD_RTC_GET_ALARM,
  		system_rtc_get_alarm,
  		EC_VER_MASK(0));
  
  #endif /* CONFIG_HOSTCMD_RTC */
  #ifdef CONFIG_EXTERNAL_STORAGE
  void system_jump_to_booter(void)
  {
  	enum API_RETURN_STATUS_T status __attribute__((unused));
  	static uint32_t flash_offset;
  	static uint32_t flash_used;
  	static uint32_t addr_entry;
  
  	/*
  	 * Get memory offset and size for RO/RW regions.
  	 * Both of them need 16-bytes alignment since GDMA burst mode.
  	 */
  	switch (system_get_shrspi_image_copy()) {
  	case SYSTEM_IMAGE_RW:
  		flash_offset = CONFIG_EC_WRITABLE_STORAGE_OFF +
  				CONFIG_RW_STORAGE_OFF;
  		flash_used = CONFIG_RW_SIZE;
  		break;
  #ifdef CONFIG_RW_B
  	case SYSTEM_IMAGE_RW_B:
  		flash_offset = CONFIG_EC_WRITABLE_STORAGE_OFF +
  				CONFIG_RW_B_STORAGE_OFF;
  		flash_used = CONFIG_RW_SIZE;
  		break;
  #endif
  	case SYSTEM_IMAGE_RO:
  	default: /* Jump to RO by default */
  		flash_offset = CONFIG_EC_PROTECTED_STORAGE_OFF +
  				CONFIG_RO_STORAGE_OFF;
  		flash_used = CONFIG_RO_SIZE;
  		break;
  	}
  
  	/* Make sure the reset vector is inside the destination image */
  	addr_entry = *(uintptr_t *)(flash_offset +
  				    CONFIG_MAPPED_STORAGE_BASE + 4);
  
  	/*
  	 * Speed up FW download time by increasing clock freq of EC. It will
  	 * restore to default in clock_init() later.
  	 */
  	clock_turbo();
  
  	/* Bypass for GMDA issue of ROM api utilities */
  #if defined(CHIP_FAMILY_NPCX5)
  	system_download_from_flash(
  		flash_offset,      /* The offset of the data in spi flash */
  		CONFIG_PROGRAM_MEMORY_BASE, /* RAM Addr of downloaded data */
  		flash_used,        /* Number of bytes to download      */
  		addr_entry         /* jump to this address after download */
  	);
  #else
  	download_from_flash(
  		flash_offset,      /* The offset of the data in spi flash */
  		CONFIG_PROGRAM_MEMORY_BASE, /* RAM Addr of downloaded data */
  		flash_used,        /* Number of bytes to download      */
  		SIGN_NO_CHECK,     /* Need CRC check or not               */
  		addr_entry,        /* jump to this address after download */
  		&status            /* Status fo download */
  	);
  #endif
  }
  
  uint32_t system_get_lfw_address()
  {
  	/*
  	 * In A3 version, we don't use little FW anymore
  	 * We provide the alternative function in ROM
  	 */
  	uint32_t jump_addr = (uint32_t)system_jump_to_booter;
  	return jump_addr;
  }
  
  /*
   * Set and clear image copy flags in MDC register.
   *
   * NPCX_FWCTRL_RO_REGION: 1 - RO, 0 - RW
   * NPCX_FWCTRL_FW_SLOT: 1 - SLOT_A, 0 - SLOT_B
   */
  void system_set_image_copy(enum system_image_copy_t copy)
  {
  	switch (copy) {
  	case SYSTEM_IMAGE_RW:
  		CLEAR_BIT(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION);
  		SET_BIT(NPCX_FWCTRL, NPCX_FWCTRL_FW_SLOT);
  		break;
  #ifdef CONFIG_RW_B
  	case SYSTEM_IMAGE_RW_B:
  		CLEAR_BIT(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION);
  		CLEAR_BIT(NPCX_FWCTRL, NPCX_FWCTRL_FW_SLOT);
  		break;
  #endif
  	default:
  		CPRINTS("Invalid copy (%d) is requested as a jump destination. "
  			"Change it to %d.", copy, SYSTEM_IMAGE_RO);
  		/* Fall through to SYSTEM_IMAGE_RO */
  	case SYSTEM_IMAGE_RO:
  		SET_BIT(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION);
  		SET_BIT(NPCX_FWCTRL, NPCX_FWCTRL_FW_SLOT);
  		break;
  	}
  }
  
  enum system_image_copy_t system_get_shrspi_image_copy(void)
  {
  	if (IS_BIT_SET(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION)) {
  		/* RO image */
  #ifdef CHIP_HAS_RO_B
  		if (!IS_BIT_SET(NPCX_FWCTRL, NPCX_FWCTRL_FW_SLOT))
  			return SYSTEM_IMAGE_RO_B;
  #endif
  		return SYSTEM_IMAGE_RO;
  	} else {
  #ifdef CONFIG_RW_B
  		/* RW image */
  		if (!IS_BIT_SET(NPCX_FWCTRL, NPCX_FWCTRL_FW_SLOT))
  			/* Slot A */
  			return SYSTEM_IMAGE_RW_B;
  #endif
  		return SYSTEM_IMAGE_RW;
  	}
  }
  
  #endif
  