mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-12 19:04:59 +00:00
Issues fixed on 0216: 1.Modified CONFIG_KEYBOARD_COL2_INVERTED support in keyboard_raw.c 2.Modified warm_reset checking in gpio.c 3.Modified system_get_chip_name in system.c for package info. 4.Modified fan.c and pwm.c for: ● If the DCRn value is greater than the CTRn value, the PWM_n signal is always low. ● Fan stall condition event: If the measured fan speed is lower than the lowLimit value (unless the Fan Speed Low Limit value is 0) or in case of erroneous measurement, the userCallback is called. 5. Change cycle_pluses to 480 in board.c Issues fixed: 1. Jump data at top of RAM is getting corrupted. Changed the flag to RESET_FLAG_RESET_PIN. Added a workaround method to fix VCC1_RST issue. 2. Hibernate wake need to report whether wake reason was GPIO or RTC 3. Hibernate wake must be distinguishable from watchdog reset. The booter will log reset reason in Code RAM. I copy the log data to battery-backup RAM in little FW. And system driver will refer this data to distinguish if it's watchdog reset or not. 4. Watchdog reset flag is not set. Same fix as 3. 5. Should return error if unable to clear SPI flash status register. 6. Remove chip_temp_sensor.c 7. Remove use of pstate from flash driver 8. Remove support for watchdog warm reset 9. Keyboard raw driver must support COL2 inverted 10. LPC memory mapped data must be read-only from host 11. LPC should support PLTRST# signal 12. Problems reading chip type/version. Use core registers and ROM data to read IDs. 13. When chip type/version is unknown, report hex value. 14. Watchdog does not consistently print panic information. 15. Remove console force enable logic. 16. Enable only the peripheral clocks that are needed. Please notice user should add bit mask in CGC_XXX_MASK if they want to enable additional module. For example, if user wants to enable PWM3, he must add PWDWN_CTL2_PWM3_PD bit in CGC_PWM_MASK. Please see HOOK_FREQ_CHANGE and HOOK_INIT these two hook functions. If I turn off all I2C modules in system_pre_init and turn on the modules I need in i2c_init, I found its freq is not correct. The root cause is hook_notify(HOOK_FREQ_CHANGE) is executed first (in clock_init) before i2c_init. At this time, i2c modules are power-down and writing to freq register is useless. I re-execute freq-changed hook function after turning on modules again. 17. MPU properly configured to prevent code execution from data RAM 18. Partial nvcontext implementation. Copy these 16 bytes in our battery-backup RAM. Additional items we also modified: 1. pwm.c: Support open-drain IO type of PWM. (PWM IO-Type cannot by determined by GPIO, we use bit 1 & 2 of function byte of gpio_alt_func array to support it) 2. ec_npcxflash.c: Use definition to replace constant value. Stop watchdog during flash programing. 3. npcx_cmds.tcl: Adjust script sequence for robustness. Add unlock MPU commands for Data RAM. BUG=chrome-os-partner:34346 BRANCH=none TEST=manually verify changes Change-Id: I722a77d29e7543b054819480c7b7477af4263119 Signed-off-by: Ian Chao <mlchao@nuvoton.com> Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/248670
864 lines
22 KiB
C
864 lines
22 KiB
C
/* 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 "common.h"
|
|
#include "console.h"
|
|
#include "cpu.h"
|
|
#include "host_command.h"
|
|
#include "registers.h"
|
|
#include "system.h"
|
|
#include "hooks.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
#include "hwtimer_chip.h"
|
|
#include "system_chip.h"
|
|
|
|
#ifdef CONFIG_CODERAM_ARCH
|
|
/* base address for jumping */
|
|
uint32_t base_addr;
|
|
#endif
|
|
|
|
/* Flags for BBRM_DATA_INDEX_WAKE */
|
|
#define HIBERNATE_WAKE_MTC (1 << 0) /* MTC alarm */
|
|
#define HIBERNATE_WAKE_PIN (1 << 1) /* Wake pin */
|
|
|
|
/* Super-IO index and register definitions */
|
|
#define SIO_OFFSET 0x4E
|
|
#define INDEX_SID 0x20
|
|
#define INDEX_CHPREV 0x24
|
|
#define INDEX_SRID 0x27
|
|
|
|
/* equivalent to 250us according to 48MHz core clock */
|
|
#define MTC_TTC_LOAD_DELAY 1500
|
|
#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
|
|
|
|
/* Begin address for the .lpram section; defined in linker script */
|
|
uintptr_t __lpram_fw_start = CONFIG_LPRAM_BASE;
|
|
|
|
/*****************************************************************************/
|
|
/* Internal functions */
|
|
|
|
/* Super-IO read/write function */
|
|
void system_sib_write_reg(uint8_t io_offset, uint8_t index_value,
|
|
uint8_t io_data)
|
|
{
|
|
/* Disable interrupts */
|
|
interrupt_disable();
|
|
|
|
/* Lock host CFG module */
|
|
SET_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG);
|
|
/* Enable Core-to-Host Modules Access */
|
|
SET_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE);
|
|
/* Enable Core access to CFG module */
|
|
SET_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE);
|
|
/* Verify Core read/write to host modules is not in progress */
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD))
|
|
;
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR))
|
|
;
|
|
|
|
/* Specify the io_offset A0 = 0. the index register is accessed */
|
|
NPCX_IHIOA = io_offset;
|
|
/* Write the data. This starts the write access to the host module */
|
|
NPCX_IHD = index_value;
|
|
/* Wait while Core write operation is in progress */
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR))
|
|
;
|
|
|
|
/* Specify the io_offset A0 = 1. the data register is accessed */
|
|
NPCX_IHIOA = io_offset+1;
|
|
/* Write the data. This starts the write access to the host module */
|
|
NPCX_IHD = io_data;
|
|
/* Wait while Core write operation is in progress */
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR))
|
|
;
|
|
|
|
/* Disable Core access to CFG module */
|
|
CLEAR_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE);
|
|
/* Disable Core-to-Host Modules Access */
|
|
CLEAR_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE);
|
|
/* unlock host CFG module */
|
|
CLEAR_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG);
|
|
|
|
/* Enable interrupts */
|
|
interrupt_enable();
|
|
}
|
|
|
|
uint8_t system_sib_read_reg(uint8_t io_offset, uint8_t index_value)
|
|
{
|
|
uint8_t data_value;
|
|
|
|
/* Disable interrupts */
|
|
interrupt_disable();
|
|
|
|
/* Lock host CFG module */
|
|
SET_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG);
|
|
/* Enable Core-to-Host Modules Access */
|
|
SET_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE);
|
|
/* Enable Core access to CFG module */
|
|
SET_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE);
|
|
/* Verify Core read/write to host modules is not in progress */
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD))
|
|
;
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR))
|
|
;
|
|
|
|
|
|
/* Specify the io_offset A0 = 0. the index register is accessed */
|
|
NPCX_IHIOA = io_offset;
|
|
/* Write the data. This starts the write access to the host module */
|
|
NPCX_IHD = index_value;
|
|
/* Wait while Core write operation is in progress */
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSWR))
|
|
;
|
|
|
|
/* Specify the io_offset A0 = 1. the data register is accessed */
|
|
NPCX_IHIOA = io_offset+1;
|
|
/* Start a Core read from host module */
|
|
SET_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD);
|
|
/* Wait while Core read operation is in progress */
|
|
while (IS_BIT_SET(NPCX_SIBCTRL, NPCX_SIBCTRL_CSRD))
|
|
;
|
|
/* Read the data */
|
|
data_value = NPCX_IHD;
|
|
|
|
/* Disable Core access to CFG module */
|
|
CLEAR_BIT(NPCX_CRSMAE, NPCX_CRSMAE_CFGAE);
|
|
/* Disable Core-to-Host Modules Access */
|
|
CLEAR_BIT(NPCX_SIBCTRL, NPCX_SIBCTRL_CSAE);
|
|
/* unlock host CFG module */
|
|
CLEAR_BIT(NPCX_LKSIOHA, NPCX_LKSIOHA_LKCFG);
|
|
|
|
/* Enable interrupts */
|
|
interrupt_enable();
|
|
|
|
return data_value;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
/* Check index */
|
|
if (index < 0 || index >= NPCX_BBRAM_SIZE)
|
|
return 0;
|
|
|
|
/* BBRAM is valid */
|
|
if (IS_BIT_SET(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR))
|
|
return 0;
|
|
|
|
/* Read BBRAM */
|
|
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)
|
|
{
|
|
/* Check index */
|
|
if (index < 0 || index >= NPCX_BBRAM_SIZE)
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* BBRAM is valid */
|
|
if (IS_BIT_SET(NPCX_BKUP_STS, NPCX_BKUP_STS_IBBR))
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* Write BBRAM */
|
|
NPCX_BBRAM(index) = value & 0xFF;
|
|
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;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
volatile uint16_t __i;
|
|
|
|
/* Set MTC counter unit:seconds */
|
|
NPCX_TTC = seconds;
|
|
|
|
/* Wait till clock is readable */
|
|
for (__i = 0; __i < MTC_TTC_LOAD_DELAY; ++__i)
|
|
;
|
|
}
|
|
|
|
/* Check reset cause */
|
|
void system_check_reset_cause(void)
|
|
{
|
|
uint32_t hib_wake_flags = bbram_data_read(BBRM_DATA_INDEX_WAKE);
|
|
uint32_t flags = 0;
|
|
|
|
/* Use scratch bit to check power on reset or VCC1_RST reset */
|
|
if (!IS_BIT_SET(NPCX_RSTCTL, NPCX_RSTCTL_VCC1_RST_SCRATCH)) {
|
|
/* 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;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/* Watchdog Reset */
|
|
#ifndef CHIP_NPCX5M5G
|
|
if (IS_BIT_SET(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS)) {
|
|
flags |= RESET_FLAG_WATCHDOG;
|
|
/* Clear watchdog reset status initially*/
|
|
SET_BIT(NPCX_T0CSR, NPCX_T0CSR_WDRST_STS);
|
|
}
|
|
#else
|
|
/* Workaround method to check watchdog reset */
|
|
if (NPCX_BBRAM(BBRM_DATA_INDEX_RAMLOG) & 0x04)
|
|
flags |= RESET_FLAG_WATCHDOG;
|
|
#endif
|
|
|
|
if ((hib_wake_flags & HIBERNATE_WAKE_PIN))
|
|
flags |= RESET_FLAG_WAKE_PIN;
|
|
else if ((hib_wake_flags & HIBERNATE_WAKE_MTC))
|
|
flags |= RESET_FLAG_RTC_ALARM;
|
|
|
|
/* Restore then clear saved reset flags */
|
|
flags |= bbram_data_read(BBRM_DATA_INDEX_SAVED_RESET_FLAGS);
|
|
bbram_data_write(BBRM_DATA_INDEX_SAVED_RESET_FLAGS, 0);
|
|
/* Clear saved hibernate wake flag, too */
|
|
bbram_data_write(BBRM_DATA_INDEX_WAKE, 0);
|
|
|
|
system_set_reset_flags(flags);
|
|
}
|
|
|
|
/**
|
|
* Configure address 0x40001600 in the the MPU
|
|
* (Memory Protection Unit) as a "regular" memory
|
|
*/
|
|
void system_mpu_config(void)
|
|
{
|
|
/* Enable MPU */
|
|
CPU_MPU_CTRL = 0x7;
|
|
|
|
/* Create a new MPU Region for low-power ram */
|
|
CPU_MPU_RNR = 0; /* Select region number 0 */
|
|
CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */
|
|
CPU_MPU_RBAR = CONFIG_LPRAM_BASE; /* Set region base address */
|
|
/*
|
|
* Set region size & attribute and enable region
|
|
* [31:29] - Reserved.
|
|
* [28] - XN (Execute Never) = 0
|
|
* [27] - Reserved.
|
|
* [26:24] - AP = 011 (Full access)
|
|
* [23:22] - Reserved.
|
|
* [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory)
|
|
* [15:8] - SRD = 0 (Subregions enabled)
|
|
* [7:6] - Reserved.
|
|
* [5:1] - SIZE = 01001 (1K)
|
|
* [0] - ENABLE = 1 (enabled)
|
|
*/
|
|
CPU_MPU_RASR = 0x03080013;
|
|
|
|
/* Create a new MPU Region for data ram */
|
|
CPU_MPU_RNR = 1; /* Select region number 1 */
|
|
CPU_MPU_RASR = CPU_MPU_RASR & 0xFFFFFFFE; /* Disable region */
|
|
CPU_MPU_RBAR = CONFIG_RAM_BASE; /* Set region base address */
|
|
/*
|
|
* Set region size & attribute and enable region
|
|
* [31:29] - Reserved.
|
|
* [28] - XN (Execute Never) = 1
|
|
* [27] - Reserved.
|
|
* [26:24] - AP = 011 (Full access)
|
|
* [23:22] - Reserved.
|
|
* [21:19,18,17,16] - TEX,S,C,B = 001000 (Normal memory)
|
|
* [15:8] - SRD = 0 (Subregions enabled)
|
|
* [7:6] - Reserved.
|
|
* [5:1] - SIZE = 01110 (32K)
|
|
* [0] - ENABLE = 1 (enabled)
|
|
*/
|
|
CPU_MPU_RASR = 0x1308001D;
|
|
}
|
|
|
|
void __attribute__ ((section(".lowpower_ram")))
|
|
__enter_hibernate_in_lpram(void)
|
|
{
|
|
|
|
/* Disable Code RAM first */
|
|
SET_BIT(NPCX_PWDWN_CTL(5), NPCX_PWDWN_CTL5_MRFSH_DIS);
|
|
SET_BIT(NPCX_DISIDL_CTL, NPCX_DISIDL_CTL_RAM_DID);
|
|
|
|
while (1) {
|
|
/* Set deep idle - instant wake-up mode*/
|
|
NPCX_PMCSR = 0x7;
|
|
/* Enter deep idle, wake-up by GPIOxx or RTC */
|
|
asm("wfi");
|
|
|
|
/* POWER_BUTTON_L wake-up */
|
|
if (NPCX_WKPND(NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON),
|
|
NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON + 1))
|
|
& NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON + 2)) {
|
|
/* Clear WUI pending bit of POWER_BUTTON_L */
|
|
NPCX_WKPCL(NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON),
|
|
NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON + 1))
|
|
= NPCX_BBRAM(BBRM_DATA_INDEX_PBUTTON + 2);
|
|
/*
|
|
* Mark wake-up reason for hibernate
|
|
* Do not call bbram_data_write directly cause of
|
|
* excuting in low-power ram
|
|
*/
|
|
NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_PIN;
|
|
break;
|
|
}
|
|
/* RTC wake-up */
|
|
else if (IS_BIT_SET(NPCX_WTC, NPCX_WTC_PTO)) {
|
|
/* Clear WUI pending bit of MTC */
|
|
NPCX_WKPCL(MIWU_TABLE_0, MTC_WUI_GROUP) = MTC_WUI_MASK;
|
|
/* Clear interrupt & Disable alarm interrupt */
|
|
CLEAR_BIT(NPCX_WTC, NPCX_WTC_WIE);
|
|
SET_BIT(NPCX_WTC, NPCX_WTC_PTO);
|
|
|
|
/* Mark wake-up reason for hibernate */
|
|
NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_MTC;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Start a watchdog reset */
|
|
NPCX_WDCNT = 0x01;
|
|
/* 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))
|
|
;
|
|
|
|
/* Spin and wait for reboot; should never return */
|
|
while (1)
|
|
;
|
|
}
|
|
/**
|
|
* Internal hibernate function.
|
|
*
|
|
* @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;
|
|
void (*__hibernate_in_lpram)(void) =
|
|
(void(*)(void))(__lpram_fw_start | 0x01);
|
|
|
|
/* Set instant wake up mode */
|
|
SET_BIT(NPCX_ENIDL_CTL, NPCX_ENIDL_CTL_LP_WK_CTL);
|
|
interrupt_disable();
|
|
|
|
/* ITIM event module disable */
|
|
CLEAR_BIT(NPCX_ITCTS(ITIM_EVENT_NO), NPCX_ITIM16_ITEN);
|
|
/* ITIM time module disable */
|
|
CLEAR_BIT(NPCX_ITCTS(ITIM_TIME_NO), NPCX_ITIM16_ITEN);
|
|
/* ITIM watchdog warn module disable */
|
|
CLEAR_BIT(NPCX_ITCTS(ITIM_WDG_NO), NPCX_ITIM16_ITEN);
|
|
|
|
/*
|
|
* Set RTC interrupt in time to wake up before
|
|
* next event.
|
|
*/
|
|
if (seconds || microseconds)
|
|
system_set_rtc_alarm(seconds, microseconds);
|
|
|
|
/* Unlock & stop watchdog */
|
|
NPCX_WDSDM = 0x87;
|
|
NPCX_WDSDM = 0x61;
|
|
NPCX_WDSDM = 0x63;
|
|
|
|
/* Enable Low Power RAM */
|
|
NPCX_LPRAM_CTRL = 1;
|
|
|
|
/* 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 */
|
|
|
|
/* Copy the __enter_hibernate_in_lpram instructions to LPRAM */
|
|
for (i = 0; i < &__flash_lpfw_end - &__flash_lpfw_start; i++)
|
|
*((uint32_t *)__lpram_fw_start + i) =
|
|
*(&__flash_lpfw_start + i);
|
|
|
|
/* Disable interrupt */
|
|
interrupt_disable();
|
|
|
|
/* execute hibernate func in LPRAM */
|
|
__hibernate_in_lpram();
|
|
|
|
}
|
|
|
|
static char system_to_hex(uint8_t x)
|
|
{
|
|
if (x >= 0 && x <= 9)
|
|
return '0' + x;
|
|
return 'a' + x - 10;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* IC specific low-level driver */
|
|
|
|
/* Microsecond will be ignore for hardware limitation */
|
|
void system_set_rtc_alarm(uint32_t seconds, uint32_t microseconds)
|
|
{
|
|
uint32_t cur_secs, alarm_secs;
|
|
|
|
if (seconds == 0)
|
|
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;
|
|
|
|
/* Reset alarm first */
|
|
system_reset_rtc_alarm();
|
|
|
|
/* Set alarm, use first 25 bits of clock value */
|
|
NPCX_WTC = alarm_secs;
|
|
|
|
/* 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 */
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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 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(0) = 0xF9; /* Skip SDP_PD FIU_PD */
|
|
NPCX_PWDWN_CTL(1) = 0xFF;
|
|
NPCX_PWDWN_CTL(2) = 0x8F;
|
|
NPCX_PWDWN_CTL(3) = 0xF4; /* Skip ITIM2/1_PD */
|
|
NPCX_PWDWN_CTL(4) = 0xF8;
|
|
NPCX_PWDWN_CTL(5) = 0x85; /* Skip ITIM5_PD */
|
|
|
|
/*
|
|
* Configure LPRAM in the MPU as a regular memory
|
|
* and DATA RAM to prevent code execution
|
|
*/
|
|
system_mpu_config();
|
|
}
|
|
|
|
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;
|
|
/* Use SYSRESETREQ to trigger a soft reboot */
|
|
CPU_NVIC_APINT = 0x05fa0004;
|
|
}
|
|
/* Store flags to battery backed RAM. */
|
|
bbram_data_write(BBRM_DATA_INDEX_SAVED_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) {
|
|
case 0x12:
|
|
return "NPCX585G";
|
|
case 0x13:
|
|
return "NPCX575G";
|
|
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[4];
|
|
#ifndef CHIP_NPCX5M5G
|
|
uint8_t rev_num = system_sib_read_reg(SIO_OFFSET, INDEX_CHPREV);
|
|
#else
|
|
/* Read ROM data for chip revision directly */
|
|
uint8_t rev_num = *((uint8_t *)CHIP_REV_ADDR);
|
|
#endif
|
|
*(rev) = 'A';
|
|
*(rev + 1) = '.';
|
|
*(rev + 2) = system_to_hex((rev_num & 0xF0) >> 4);
|
|
*(rev + 3) = system_to_hex(rev_num & 0x0F);
|
|
|
|
return rev;
|
|
}
|
|
|
|
/**
|
|
* Get/Set VbNvContext in non-volatile storage. The block should be 16 bytes
|
|
* long, which is the current size of VbNvContext block.
|
|
*
|
|
* @param block Pointer to a buffer holding VbNvContext.
|
|
* @return 0 on success, !0 on error.
|
|
*/
|
|
int system_get_vbnvcontext(uint8_t *block)
|
|
{
|
|
int i;
|
|
uint32_t *pblock = (uint32_t *) block;
|
|
for (i = 0; i < 4; i++)
|
|
pblock[i] = bbram_data_read(BBRM_DATA_INDEX_VBNVCNTXT + i*4);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int system_set_vbnvcontext(const uint8_t *block)
|
|
{
|
|
int i, result;
|
|
uint32_t *pblock = (uint32_t *) block;
|
|
for (i = 0; i < 4; i++) {
|
|
result = bbram_data_write(BBRM_DATA_INDEX_VBNVCNTXT + i*4,
|
|
pblock[i]);
|
|
if (result != EC_SUCCESS)
|
|
return result;
|
|
}
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Console commands */
|
|
|
|
static int command_system_rtc(int argc, char **argv)
|
|
{
|
|
uint32_t sec;
|
|
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;
|
|
}
|
|
|
|
sec = system_get_rtc_sec();
|
|
ccprintf("RTC: 0x%08x (%d.00 s)\n", sec, sec);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(rtc, command_system_rtc,
|
|
"[set <seconds>]",
|
|
"Get/set real-time clock",
|
|
NULL);
|
|
|
|
#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",
|
|
NULL);
|
|
#endif /* CONFIG_CMD_RTC_ALARM */
|
|
|
|
/*****************************************************************************/
|
|
/* Host commands */
|
|
|
|
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));
|
|
|
|
/* For LPC host register initial via SIB module */
|
|
void system_lpc_host_register_init(void){
|
|
/* Setting PMC2 */
|
|
/* LDN register = 0x12(PMC2) */
|
|
system_sib_write_reg(SIO_OFFSET, 0x07, 0x12);
|
|
/* CMD port is 0x200 */
|
|
system_sib_write_reg(SIO_OFFSET, 0x60, 0x02);
|
|
system_sib_write_reg(SIO_OFFSET, 0x61, 0x00);
|
|
/* Data port is 0x204 */
|
|
system_sib_write_reg(SIO_OFFSET, 0x62, 0x02);
|
|
system_sib_write_reg(SIO_OFFSET, 0x63, 0x04);
|
|
/* enable PMC2 */
|
|
system_sib_write_reg(SIO_OFFSET, 0x30, 0x01);
|
|
|
|
/* Setting SHM */
|
|
/* LDN register = 0x0F(SHM) */
|
|
system_sib_write_reg(SIO_OFFSET, 0x07, 0x0F);
|
|
/* WIN1&2 mapping to IO */
|
|
system_sib_write_reg(SIO_OFFSET, 0xF1,
|
|
system_sib_read_reg(SIO_OFFSET, 0xF1) | 0x30);
|
|
/* Host Command on the IO:0x0800 */
|
|
system_sib_write_reg(SIO_OFFSET, 0xF7, 0x00);
|
|
system_sib_write_reg(SIO_OFFSET, 0xF6, 0x00);
|
|
system_sib_write_reg(SIO_OFFSET, 0xF5, 0x08);
|
|
system_sib_write_reg(SIO_OFFSET, 0xF4, 0x00);
|
|
/* WIN1 as Host Command on the IO:0x0800 */
|
|
system_sib_write_reg(SIO_OFFSET, 0xFB, 0x00);
|
|
system_sib_write_reg(SIO_OFFSET, 0xFA, 0x00);
|
|
/* WIN2 as MEMMAP on the IO:0x900 */
|
|
system_sib_write_reg(SIO_OFFSET, 0xF9, 0x09);
|
|
system_sib_write_reg(SIO_OFFSET, 0xF8, 0x00);
|
|
/* enable SHM */
|
|
system_sib_write_reg(SIO_OFFSET, 0x30, 0x01);
|
|
}
|
|
#ifdef CONFIG_CODERAM_ARCH
|
|
uint32_t system_get_lfw_address(uint32_t flash_addr)
|
|
{
|
|
/* Little FW located on top of flash - 4K */
|
|
uint32_t jump_addr = (CONFIG_FLASH_BASE + CONFIG_SPI_FLASH_SIZE
|
|
- CONFIG_LFW_OFFSET + 1);
|
|
/* restore base address for jumping*/
|
|
base_addr = flash_addr;
|
|
return jump_addr;
|
|
}
|
|
|
|
enum system_image_copy_t system_get_shrspi_image_copy(void)
|
|
{
|
|
/* RO region FW */
|
|
if (IS_BIT_SET(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION))
|
|
return SYSTEM_IMAGE_RO;
|
|
else/* RW region FW */
|
|
return SYSTEM_IMAGE_RW;
|
|
}
|
|
|
|
/**
|
|
* Set flag for jumping across a sysjump.
|
|
*/
|
|
static void system_sysjump(void)
|
|
{
|
|
/* Jump to RO region -- set flag */
|
|
if (base_addr == CONFIG_FLASH_BASE + CONFIG_FW_RO_OFF)
|
|
SET_BIT(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION);
|
|
else /* Jump to RW region -- clear flag */
|
|
CLEAR_BIT(NPCX_FWCTRL, NPCX_FWCTRL_RO_REGION);
|
|
}
|
|
DECLARE_HOOK(HOOK_SYSJUMP, system_sysjump, HOOK_PRIO_DEFAULT);
|
|
#endif
|