/* Copyright 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 "sib_chip.h" #include "system.h" #include "system_chip.h" #include "task.h" #include "timer.h" #include "util.h" #include "watchdog.h" /* Delay after writing TTC for value to latch */ #define MTC_TTC_LOAD_DELAY_US 250 #define MTC_ALARM_MASK (BIT(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) || index == BBRM_DATA_INDEX_PD0 || index == BBRM_DATA_INDEX_PD1 || index == BBRM_DATA_INDEX_PD2 || 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"); /* * npcx5/npcx7m6g/npcx7m6f: * Clear IBBR bit * npcx7m6fb/npcx7m6fc/npcx7m7wb/npcx7m7wc: * Clear IBBR/VSBY_STS/VCC1_STS bit */ NPCX_BKUP_STS = NPCX_BKUP_STS_ALL_MASK; } } /* 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)) { NPCX_BKUP_STS = BIT(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; if (idx == SYSTEM_BBRAM_IDX_PD0) return BBRM_DATA_INDEX_PD0; if (idx == SYSTEM_BBRAM_IDX_PD1) return BBRM_DATA_INDEX_PD1; if (idx == SYSTEM_BBRAM_IDX_PD2) return BBRM_DATA_INDEX_PD2; if (idx == SYSTEM_BBRAM_IDX_TRY_SLOT) return BBRM_DATA_INDEX_TRY_SLOT; 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 BIT(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(uint32_t flags) { bbram_data_write(BBRM_DATA_INDEX_SAVED_RESET_FLAGS, flags); } uint32_t chip_read_reset_flags(void) { return bbram_data_read(BBRM_DATA_INDEX_SAVED_RESET_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 &= ~EC_RESET_FLAG_AP_OFF; chip_save_reset_flags(flags); system_clear_reset_flags(EC_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 |= EC_RESET_FLAG_AP_OFF; chip_save_reset_flags(flags); system_set_reset_flags(EC_RESET_FLAG_AP_OFF); CPRINTS("Set AP_OFF flag"); } DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, board_chipset_shutdown, /* Slightly higher than handle_pending_reboot because * it may clear AP_OFF flag. */ HOOK_PRIO_DEFAULT - 1); #endif static void 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 & EC_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)) { #ifdef CONFIG_BOARD_FORCE_RESET_PIN /* Treat all resets as RESET_PIN */ flags |= EC_RESET_FLAG_RESET_PIN; #else /* Check for VCC1 reset */ if (IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_STS)) flags |= EC_RESET_FLAG_RESET_PIN; else flags |= EC_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 |= EC_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 |= EC_RESET_FLAG_WAKE_PIN | EC_RESET_FLAG_HIBERNATE; else if (hib_wake_flags & HIBERNATE_WAKE_MTC) flags |= EC_RESET_FLAG_RTC_ALARM | EC_RESET_FLAG_HIBERNATE; /* Watchdog Reset */ if (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS)) { /* * Don't set EC_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 & (EC_RESET_FLAG_SOFT | EC_RESET_FLAG_HARD | EC_RESET_FLAG_HIBERNATE))) flags |= EC_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. */ #ifdef CONFIG_SUPPORT_CHIP_HIBERNATION static 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(); } #endif /* CONFIG_SUPPORT_CHIP_HIBERNATION */ 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 && !microseconds) { 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(); #ifdef CONFIG_SUPPORT_CHIP_HIBERNATION /* Add additional hibernate operations here */ __enter_hibernate(seconds, microseconds); #endif } void chip_pre_init(void) { /* Setting for fixing JTAG issue */ NPCX_DBGCTRL = 0x04; /* Enable automatic freeze mode */ CLEAR_BIT(NPCX_DBGFRZEN3, NPCX_DBGFRZEN3_GLBL_FRZ_DIS); /* * Enable JTAG functionality by SW without pulling down strap-pin * nJEN0 or nJEN1 during ec POWERON or VCCRST reset occurs. * Please notice it will change pinmux to JTAG directly. */ #ifdef NPCX_ENABLE_JTAG #if NPCX_JTAG_MODULE2 CLEAR_BIT(NPCX_DEVALT(ALT_GROUP_5), NPCX_DEVALT5_NJEN1_EN); #else CLEAR_BIT(NPCX_DEVALT(ALT_GROUP_5), NPCX_DEVALT5_NJEN0_EN); #endif #endif #ifndef CONFIG_ENABLE_JTAG_SELECTION /* * (b/129908668) * This is the workaround to disable the JTAG0 which is enabled * accidentally by a special key combination. */ if (!IS_BIT_SET(NPCX_DEVALT(5), NPCX_DEVALT5_NJEN0_EN)) { int data; /* Set DEVALT5.nJEN0_EN to disable JTAG0 */ SET_BIT(NPCX_DEVALT(5), NPCX_DEVALT5_NJEN0_EN); /* Enable Core-to-Host Modules Access */ SET_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE); /* Clear SIOCFD.JEN0_HSL to disable JTAG0 */ data = sib_read_reg(SIO_OFFSET, 0x2D); data &= ~0x80; sib_write_reg(SIO_OFFSET, 0x2D, data); /* Disable Core-to-Host Modules Access */ CLEAR_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE); } #endif } void system_pre_init(void) { uint8_t pwdwn6; /* * 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; pwdwn6 = 0x70 | BIT(NPCX_PWDWN_CTL6_ITIM6_PD) | BIT(NPCX_PWDWN_CTL6_ITIM4_PD); /* Skip ITIM5_PD */ #if !defined(CONFIG_HOSTCMD_ESPI) pwdwn6 |= 1 << NPCX_PWDWN_CTL6_ESPI_PD; #endif NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6) = pwdwn6; #if defined(CHIP_FAMILY_NPCX7) #if defined(CHIP_VARIANT_NPCX7M6FB) || defined(CHIP_VARIANT_NPCX7M6FC) || \ defined(CHIP_VARIANT_NPCX7M7WB) || defined(CHIP_VARIANT_NPCX7M7WC) NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0xE7; #else NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_7) = 0x07; #endif #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(); /* * Change FMUL_WIN_DLY from 0x8A to 0x81 for better WoV * audio quality. */ #ifdef CHIP_FAMILY_NPCX7 NPCX_FMUL_WIN_DLY = 0x81; #endif #ifdef CONFIG_CHIP_PANIC_BACKUP chip_panic_data_restore(); #endif } void system_reset(int flags) { uint32_t save_flags; /* Disable interrupts to avoid task swaps during reboot */ interrupt_disable(); /* Get flags to be saved in BBRAM */ system_encode_save_flags(flags, &save_flags); /* Store flags to battery backed RAM. */ chip_save_reset_flags(save_flags); /* If WAIT_EXT is set, then allow 10 seconds for external reset */ if (flags & SYSTEM_RESET_WAIT_EXT) { int i; /* Wait 10 seconds for external reset */ for (i = 0; i < 1000; i++) { watchdog_reload(); udelay(10000); } } /* 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 0x1F: return "NPCX787G"; case 0x21: case 0x29: return "NPCX796F"; case 0x24: case 0x2C: return "NPCX797W"; #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[6]; char *p = rev; /* Read chip generation from SRID_CR */ uint8_t chip_gen = NPCX_SRID_CR; /* Read ROM data for chip revision directly */ uint8_t rev_num = *((uint8_t *)CHIP_REV_ADDR); #ifdef CHIP_FAMILY_NPCX7 uint8_t chip_id = NPCX_DEVICE_ID_CR; #endif switch (chip_gen) { #if defined(CHIP_FAMILY_NPCX5) case 0x05: *p++ = 'A'; break; #elif defined(CHIP_FAMILY_NPCX7) case 0x06: *p++ = 'A'; break; case 0x07: if (chip_id == 0x21 || chip_id == 0x24) *p++ = 'B'; else *p++ = 'C'; break; #endif default: *p++ = system_to_hex((chip_gen & 0xF0) >> 4); *p++ = system_to_hex(chip_gen & 0x0F); break; } *p++ = '.'; *p++ = system_to_hex((rev_num & 0xF0) >> 4); *p++ = system_to_hex(rev_num & 0x0F); *p++ = '\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 */ check_reset_cause(); reset_flags = system_get_reset_flags(); if ((reset_flags & EC_RESET_FLAG_RESET_PIN) || (reset_flags & EC_RESET_FLAG_POWER_ON) || (reset_flags & EC_RESET_FLAG_WATCHDOG) || (reset_flags & EC_RESET_FLAG_HARD) || (reset_flags & EC_RESET_FLAG_SOFT) || (reset_flags & EC_RESET_FLAG_HIBERNATE)) return 0; else return 1; } /*****************************************************************************/ /* Console commands */ 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); } #ifdef CONFIG_CMD_RTC 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 ]", "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 enum ec_status 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 enum ec_status 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 enum ec_status 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 enum ec_status 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