260 lines
7.7 KiB
C
260 lines
7.7 KiB
C
/* Copyright 2017 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
/* System module driver depends on chip series for Chrome EC */
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "cpu.h"
|
|
#include "gpio.h"
|
|
#include "hwtimer_chip.h"
|
|
#include "mpu.h"
|
|
#include "registers.h"
|
|
#include "rom_chip.h"
|
|
#include "system.h"
|
|
#include "system_chip.h"
|
|
#include "task.h"
|
|
#include "util.h"
|
|
|
|
/* Begin address of Suspend RAM for hibernate utility */
|
|
uintptr_t __lpram_fw_start = CONFIG_LPRAM_BASE;
|
|
/* Offset of little FW in Suspend Ram for GDMA bypass */
|
|
#define LFW_OFFSET 0x160
|
|
/* Begin address of Suspend RAM for little FW (GDMA utilities). */
|
|
uintptr_t __lpram_lfw_start = CONFIG_LPRAM_BASE + LFW_OFFSET;
|
|
|
|
/*****************************************************************************/
|
|
/* IC specific low-level driver depends on chip series */
|
|
|
|
/**
|
|
* Configure address 0x40001600 (Low Power RAM) 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 to allow execution from low-power ram */
|
|
CPU_MPU_RNR = REGION_CHIP_RESERVED;
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* hibernate function in low power ram for npcx5 series.
|
|
*/
|
|
void __keep __attribute__ ((noreturn, section(".lowpower_ram")))
|
|
__enter_hibernate_in_lpram(void)
|
|
{
|
|
/*
|
|
* TODO (ML): Set stack pointer to upper 512B of Suspend RAM.
|
|
* Our bypass needs stack instructions but FW will turn off main ram
|
|
* later for better power consumption.
|
|
*/
|
|
asm (
|
|
"ldr r0, =0x40001800\n"
|
|
"mov sp, r0\n"
|
|
);
|
|
|
|
/* Disable Code RAM first */
|
|
SET_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_5), NPCX_PWDWN_CTL5_MRFSH_DIS);
|
|
SET_BIT(NPCX_DISIDL_CTL, NPCX_DISIDL_CTL_RAM_DID);
|
|
|
|
/* Set deep idle mode*/
|
|
NPCX_PMCSR = 0x6;
|
|
|
|
/* Enter deep idle, wake-up by GPIOs or RTC */
|
|
/*
|
|
* TODO (ML): Although the probability is small, it still has chance
|
|
* to meet the same symptom that CPU's behavior is abnormal after
|
|
* wake-up from deep idle.
|
|
* Workaround: Apply the same bypass of idle but don't enable interrupt.
|
|
*/
|
|
asm (
|
|
"push {r0-r5}\n" /* Save needed registers */
|
|
"ldr r0, =0x40001600\n" /* Set r0 to Suspend RAM addr */
|
|
"wfi\n" /* Wait for int to enter idle */
|
|
"ldm r0, {r0-r5}\n" /* Add a delay after WFI */
|
|
"pop {r0-r5}\n" /* Restore regs before enabling ints */
|
|
"isb\n" /* Flush the cpu pipeline */
|
|
);
|
|
|
|
/* RTC wake-up */
|
|
if (IS_BIT_SET(NPCX_WTC, NPCX_WTC_PTO))
|
|
/*
|
|
* Mark wake-up reason for hibernate
|
|
* Do not call bbram_data_write directly cause of
|
|
* executing in low-power ram
|
|
*/
|
|
NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_MTC;
|
|
else
|
|
/* Otherwise, we treat it as GPIOs wake-up */
|
|
NPCX_BBRAM(BBRM_DATA_INDEX_WAKE) = HIBERNATE_WAKE_PIN;
|
|
|
|
/* 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)
|
|
;
|
|
}
|
|
|
|
/**
|
|
* Hibernate function for different Nuvoton chip series.
|
|
*/
|
|
void __hibernate_npcx_series(void)
|
|
{
|
|
int i;
|
|
void (*__hibernate_in_lpram)(void) =
|
|
(void(*)(void))(__lpram_fw_start | 0x01);
|
|
|
|
/* Enable power for the Low Power RAM */
|
|
CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6), 6);
|
|
|
|
/* Enable Low Power RAM */
|
|
NPCX_LPRAM_CTRL = 1;
|
|
|
|
/* 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);
|
|
|
|
/* execute hibernate func in LPRAM */
|
|
__hibernate_in_lpram();
|
|
}
|
|
|
|
#ifdef CONFIG_EXTERNAL_STORAGE
|
|
/* Sysjump utilities in low power ram for npcx5 series. */
|
|
void __keep __attribute__ ((noreturn, section(".lowpower_ram2")))
|
|
__start_gdma(uint32_t exeAddr)
|
|
{
|
|
/* Enable GDMA now */
|
|
SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAEN);
|
|
|
|
/* Start GDMA */
|
|
SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_SOFTREQ);
|
|
|
|
/* Wait for transfer to complete/fail */
|
|
while (!IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_TC) &&
|
|
!IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAERR))
|
|
;
|
|
|
|
/* Disable GDMA now */
|
|
CLEAR_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAEN);
|
|
|
|
/*
|
|
* Failure occurs during GMDA transaction. Let watchdog issue and
|
|
* boot from RO region again.
|
|
*/
|
|
if (IS_BIT_SET(NPCX_GDMA_CTL, NPCX_GDMA_CTL_GDMAERR))
|
|
while (1)
|
|
;
|
|
|
|
/*
|
|
* Jump to the exeAddr address if needed. Setting bit 0 of address to
|
|
* indicate it's a thumb branch for cortex-m series CPU.
|
|
*/
|
|
((void (*)(void))(exeAddr | 0x01))();
|
|
|
|
/* Should never get here */
|
|
while (1)
|
|
;
|
|
}
|
|
|
|
/* Bypass for GMDA issue of ROM api utilities only on npcx5 series. */
|
|
void system_download_from_flash(uint32_t srcAddr, uint32_t dstAddr,
|
|
uint32_t size, uint32_t exeAddr)
|
|
{
|
|
int i;
|
|
uint8_t chunkSize = 16; /* 4 data burst mode. ie.16 bytes */
|
|
/*
|
|
* GDMA utility in Suspend RAM. Setting bit 0 of address to indicate
|
|
* it's a thumb branch for cortex-m series CPU.
|
|
*/
|
|
void (*__start_gdma_in_lpram)(uint32_t) =
|
|
(void(*)(uint32_t))(__lpram_lfw_start | 0x01);
|
|
|
|
/*
|
|
* Before enabling burst mode for better performance of GDMA, it's
|
|
* important to make sure srcAddr, dstAddr and size of transactions
|
|
* are 16 bytes aligned in case failure occurs.
|
|
*/
|
|
ASSERT((size % chunkSize) == 0 && (srcAddr % chunkSize) == 0 &&
|
|
(dstAddr % chunkSize) == 0);
|
|
|
|
/* Check valid address for jumpiing */
|
|
ASSERT(exeAddr != 0x0);
|
|
|
|
/* Enable power for the Low Power RAM */
|
|
CLEAR_BIT(NPCX_PWDWN_CTL(NPCX_PMC_PWDWN_6), 6);
|
|
|
|
/* Enable Low Power RAM */
|
|
NPCX_LPRAM_CTRL = 1;
|
|
|
|
/*
|
|
* Initialize GDMA for flash reading.
|
|
* [31:21] - Reserved.
|
|
* [20] - GDMAERR = 0 (Indicate GMDA transfer error)
|
|
* [19] - Reserved.
|
|
* [18] - TC = 0 (Terminal Count. Indicate operation is end.)
|
|
* [17] - Reserved.
|
|
* [16] - SOFTREQ = 0 (Don't trigger here)
|
|
* [15] - DM = 0 (Set normal demand mode)
|
|
* [14] - Reserved.
|
|
* [13:12] - TWS. = 10 (One double-word for every GDMA transaction)
|
|
* [11:10] - Reserved.
|
|
* [9] - BME = 1 (4-data ie.16 bytes - Burst mode enable)
|
|
* [8] - SIEN = 0 (Stop interrupt disable)
|
|
* [7] - SAFIX = 0 (Fixed source address)
|
|
* [6] - Reserved.
|
|
* [5] - SADIR = 0 (Source address incremented)
|
|
* [4] - DADIR = 0 (Destination address incremented)
|
|
* [3:2] - GDMAMS = 00 (Software mode)
|
|
* [1] - Reserved.
|
|
* [0] - ENABLE = 0 (Don't enable yet)
|
|
*/
|
|
NPCX_GDMA_CTL = 0x00002200;
|
|
|
|
/* Set source base address */
|
|
NPCX_GDMA_SRCB = CONFIG_MAPPED_STORAGE_BASE + srcAddr;
|
|
|
|
/* Set destination base address */
|
|
NPCX_GDMA_DSTB = dstAddr;
|
|
|
|
/* Set number of transfers */
|
|
NPCX_GDMA_TCNT = (size / chunkSize);
|
|
|
|
/* Clear Transfer Complete event */
|
|
SET_BIT(NPCX_GDMA_CTL, NPCX_GDMA_CTL_TC);
|
|
|
|
/* Copy the __start_gdma_in_lpram instructions to LPRAM */
|
|
for (i = 0; i < &__flash_lplfw_end - &__flash_lplfw_start; i++)
|
|
*((uint32_t *)__lpram_lfw_start + i) =
|
|
*(&__flash_lplfw_start + i);
|
|
|
|
/* Start GDMA in Suspend RAM */
|
|
__start_gdma_in_lpram(exeAddr);
|
|
}
|
|
#endif /* CONFIG_EXTERNAL_STORAGE */
|