Add code to set up the warm boot area in the Tegra CPU ready for a resume after suspend. Signed-off-by: Yen Lin <yelin@nvidia.com> Signed-off-by: Simon Glass <sjg@chromium.org> Signed-off-by: Tom Warren <twarren@nvidia.com>master
parent
8723626dd9
commit
6570438a70
@ -0,0 +1,386 @@ |
||||
/*
|
||||
* (C) Copyright 2010 - 2011 |
||||
* NVIDIA Corporation <www.nvidia.com> |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/io.h> |
||||
#include <asm/errno.h> |
||||
#include <asm/arch/ap20.h> |
||||
#include <asm/arch/clk_rst.h> |
||||
#include <asm/arch/clock.h> |
||||
#include <asm/arch/pmc.h> |
||||
#include <asm/arch/pinmux.h> |
||||
#include <asm/arch/tegra2.h> |
||||
#include <asm/arch/fuse.h> |
||||
#include <asm/arch/emc.h> |
||||
#include <asm/arch/gp_padctrl.h> |
||||
#include <asm/arch/warmboot.h> |
||||
#include <asm/arch/sdram_param.h> |
||||
|
||||
DECLARE_GLOBAL_DATA_PTR; |
||||
|
||||
#ifndef CONFIG_TEGRA_CLOCK_SCALING |
||||
#error "You must enable CONFIG_TEGRA_CLOCK_SCALING to use CONFIG_TEGRA2_LP0" |
||||
#endif |
||||
|
||||
/*
|
||||
* This is the place in SRAM where the SDRAM parameters are stored. There |
||||
* are 4 blocks, one for each RAM code |
||||
*/ |
||||
#define SDRAM_PARAMS_BASE (AP20_BASE_PA_SRAM + 0x188) |
||||
|
||||
/* TODO: If we later add support for the Misc GP controller, refactor this */ |
||||
union xm2cfga_reg { |
||||
struct { |
||||
u32 reserved0:2; |
||||
u32 hsm_en:1; |
||||
u32 reserved1:2; |
||||
u32 preemp_en:1; |
||||
u32 vref_en:1; |
||||
u32 reserved2:5; |
||||
u32 cal_drvdn:5; |
||||
u32 reserved3:3; |
||||
u32 cal_drvup:5; |
||||
u32 reserved4:3; |
||||
u32 cal_drvdn_slwr:2; |
||||
u32 cal_drvup_slwf:2; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
union xm2cfgd_reg { |
||||
struct { |
||||
u32 reserved0:2; |
||||
u32 hsm_en:1; |
||||
u32 schmt_en:1; |
||||
u32 lpmd:2; |
||||
u32 vref_en:1; |
||||
u32 reserved1:5; |
||||
u32 cal_drvdn:5; |
||||
u32 reserved2:3; |
||||
u32 cal_drvup:5; |
||||
u32 reserved3:3; |
||||
u32 cal_drvdn_slwr:2; |
||||
u32 cal_drvup_slwf:2; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
/*
|
||||
* TODO: This register is not documented in the TRM yet. We could move this |
||||
* into the EMC and give it a proper interface, but not while it is |
||||
* undocumented. |
||||
*/ |
||||
union fbio_spare_reg { |
||||
struct { |
||||
u32 reserved:24; |
||||
u32 cfg_wb0:8; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
/* We pack the resume information into these unions for later */ |
||||
union scratch2_reg { |
||||
struct { |
||||
u32 pllm_base_divm:5; |
||||
u32 pllm_base_divn:10; |
||||
u32 pllm_base_divp:3; |
||||
u32 pllm_misc_lfcon:4; |
||||
u32 pllm_misc_cpcon:4; |
||||
u32 gp_xm2cfga_padctrl_preemp:1; |
||||
u32 gp_xm2cfgd_padctrl_schmt:1; |
||||
u32 osc_ctrl_xobp:1; |
||||
u32 memory_type:3; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
union scratch4_reg { |
||||
struct { |
||||
u32 emc_clock_divider:8; |
||||
u32 pllm_stable_time:8; |
||||
u32 pllx_stable_time:8; |
||||
u32 emc_fbio_spare_cfg_wb0:8; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
union scratch24_reg { |
||||
struct { |
||||
u32 emc_auto_cal_wait:8; |
||||
u32 emc_pin_program_wait:8; |
||||
u32 warmboot_wait:8; |
||||
u32 reserved:8; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
int warmboot_save_sdram_params(void) |
||||
{ |
||||
u32 ram_code; |
||||
struct sdram_params sdram; |
||||
struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; |
||||
struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; |
||||
struct apb_misc_gp_ctlr *gp = |
||||
(struct apb_misc_gp_ctlr *)TEGRA2_APB_MISC_GP_BASE; |
||||
struct emc_ctlr *emc = emc_get_controller(gd->fdt_blob); |
||||
union scratch2_reg scratch2; |
||||
union scratch4_reg scratch4; |
||||
union scratch24_reg scratch24; |
||||
union xm2cfga_reg xm2cfga; |
||||
union xm2cfgd_reg xm2cfgd; |
||||
union fbio_spare_reg fbio_spare; |
||||
|
||||
/* get ram code that is used as index to array sdram_params in BCT */ |
||||
ram_code = (readl(&pmt->pmt_strap_opt_a) >> |
||||
STRAP_OPT_A_RAM_CODE_SHIFT) & 3; |
||||
memcpy(&sdram, |
||||
(char *)((struct sdram_params *)SDRAM_PARAMS_BASE + ram_code), |
||||
sizeof(sdram)); |
||||
|
||||
xm2cfga.word = readl(&gp->xm2cfga); |
||||
xm2cfgd.word = readl(&gp->xm2cfgd); |
||||
|
||||
scratch2.word = 0; |
||||
scratch2.osc_ctrl_xobp = clock_get_osc_bypass(); |
||||
|
||||
/* Get the memory PLL settings */ |
||||
{ |
||||
u32 divm, divn, divp, cpcon, lfcon; |
||||
|
||||
if (clock_ll_read_pll(CLOCK_ID_MEMORY, &divm, &divn, &divp, |
||||
&cpcon, &lfcon)) |
||||
return -1; |
||||
scratch2.pllm_base_divm = divm; |
||||
scratch2.pllm_base_divn = divn; |
||||
scratch2.pllm_base_divp = divp; |
||||
scratch2.pllm_misc_cpcon = cpcon; |
||||
scratch2.pllm_misc_lfcon = lfcon; |
||||
} |
||||
|
||||
scratch2.gp_xm2cfga_padctrl_preemp = xm2cfga.preemp_en; |
||||
scratch2.gp_xm2cfgd_padctrl_schmt = xm2cfgd.schmt_en; |
||||
scratch2.memory_type = sdram.memory_type; |
||||
writel(scratch2.word, &pmc->pmc_scratch2); |
||||
|
||||
/* collect data from various sources for pmc_scratch4 */ |
||||
fbio_spare.word = readl(&emc->fbio_spare); |
||||
scratch4.word = 0; |
||||
scratch4.emc_fbio_spare_cfg_wb0 = fbio_spare.cfg_wb0; |
||||
scratch4.emc_clock_divider = sdram.emc_clock_divider; |
||||
scratch4.pllm_stable_time = -1; |
||||
scratch4.pllx_stable_time = -1; |
||||
writel(scratch4.word, &pmc->pmc_scratch4); |
||||
|
||||
/* collect various data from sdram for pmc_scratch24 */ |
||||
scratch24.word = 0; |
||||
scratch24.emc_pin_program_wait = sdram.emc_pin_program_wait; |
||||
scratch24.emc_auto_cal_wait = sdram.emc_auto_cal_wait; |
||||
scratch24.warmboot_wait = sdram.warm_boot_wait; |
||||
writel(scratch24.word, &pmc->pmc_scratch24); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static u32 get_major_version(void) |
||||
{ |
||||
u32 major_id; |
||||
struct apb_misc_gp_ctlr *gp = |
||||
(struct apb_misc_gp_ctlr *)TEGRA2_APB_MISC_GP_BASE; |
||||
|
||||
major_id = (readl(&gp->hidrev) & HIDREV_MAJORPREV_MASK) >> |
||||
HIDREV_MAJORPREV_SHIFT; |
||||
return major_id; |
||||
} |
||||
|
||||
static int is_production_mode_fuse_set(struct fuse_regs *fuse) |
||||
{ |
||||
return readl(&fuse->production_mode); |
||||
} |
||||
|
||||
static int is_odm_production_mode_fuse_set(struct fuse_regs *fuse) |
||||
{ |
||||
return readl(&fuse->security_mode); |
||||
} |
||||
|
||||
static int is_failure_analysis_mode(struct fuse_regs *fuse) |
||||
{ |
||||
return readl(&fuse->fa); |
||||
} |
||||
|
||||
static int ap20_is_odm_production_mode(void) |
||||
{ |
||||
struct fuse_regs *fuse = (struct fuse_regs *)TEGRA2_FUSE_BASE; |
||||
|
||||
if (!is_failure_analysis_mode(fuse) && |
||||
is_odm_production_mode_fuse_set(fuse)) |
||||
return 1; |
||||
else |
||||
return 0; |
||||
} |
||||
|
||||
static int ap20_is_production_mode(void) |
||||
{ |
||||
struct fuse_regs *fuse = (struct fuse_regs *)TEGRA2_FUSE_BASE; |
||||
|
||||
if (get_major_version() == 0) |
||||
return 1; |
||||
|
||||
if (!is_failure_analysis_mode(fuse) && |
||||
is_production_mode_fuse_set(fuse) && |
||||
!is_odm_production_mode_fuse_set(fuse)) |
||||
return 1; |
||||
else |
||||
return 0; |
||||
} |
||||
|
||||
static enum fuse_operating_mode fuse_get_operation_mode(void) |
||||
{ |
||||
u32 chip_id; |
||||
struct apb_misc_gp_ctlr *gp = |
||||
(struct apb_misc_gp_ctlr *)TEGRA2_APB_MISC_GP_BASE; |
||||
|
||||
chip_id = (readl(&gp->hidrev) & HIDREV_CHIPID_MASK) >> |
||||
HIDREV_CHIPID_SHIFT; |
||||
if (chip_id == CHIPID_TEGRA2) { |
||||
if (ap20_is_odm_production_mode()) { |
||||
printf("!! odm_production_mode is not supported !!\n"); |
||||
return MODE_UNDEFINED; |
||||
} else |
||||
if (ap20_is_production_mode()) |
||||
return MODE_PRODUCTION; |
||||
else |
||||
return MODE_UNDEFINED; |
||||
} |
||||
return MODE_UNDEFINED; |
||||
} |
||||
|
||||
static void determine_crypto_options(int *is_encrypted, int *is_signed, |
||||
int *use_zero_key) |
||||
{ |
||||
switch (fuse_get_operation_mode()) { |
||||
case MODE_PRODUCTION: |
||||
*is_encrypted = 0; |
||||
*is_signed = 1; |
||||
*use_zero_key = 1; |
||||
break; |
||||
case MODE_UNDEFINED: |
||||
default: |
||||
*is_encrypted = 0; |
||||
*is_signed = 0; |
||||
*use_zero_key = 0; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
static int sign_wb_code(u32 start, u32 length, int use_zero_key) |
||||
{ |
||||
int err; |
||||
u8 *source; /* Pointer to source */ |
||||
u8 *hash; |
||||
|
||||
/* Calculate AES block parameters. */ |
||||
source = (u8 *)(start + offsetof(struct wb_header, random_aes_block)); |
||||
length -= offsetof(struct wb_header, random_aes_block); |
||||
hash = (u8 *)(start + offsetof(struct wb_header, hash)); |
||||
err = sign_data_block(source, length, hash); |
||||
|
||||
return err; |
||||
} |
||||
|
||||
int warmboot_prepare_code(u32 seg_address, u32 seg_length) |
||||
{ |
||||
int err = 0; |
||||
u32 length; /* length of the signed/encrypt code */ |
||||
struct wb_header *dst_header; /* Pointer to dest WB header */ |
||||
int is_encrypted; /* Segment is encrypted */ |
||||
int is_signed; /* Segment is signed */ |
||||
int use_zero_key; /* Use key of all zeros */ |
||||
|
||||
/* Determine crypto options. */ |
||||
determine_crypto_options(&is_encrypted, &is_signed, &use_zero_key); |
||||
|
||||
/* Get the actual code limits. */ |
||||
length = roundup(((u32)wb_end - (u32)wb_start), 16); |
||||
|
||||
/*
|
||||
* The region specified by seg_address must be in SDRAM and must be |
||||
* nonzero in length. |
||||
*/ |
||||
if (seg_length == 0 || seg_address < NV_PA_SDRAM_BASE || |
||||
seg_address + seg_length >= NV_PA_SDRAM_BASE + gd->ram_size) { |
||||
err = -EFAULT; |
||||
goto fail; |
||||
} |
||||
|
||||
/* Things must be 16-byte aligned. */ |
||||
if ((seg_length & 0xF) || (seg_address & 0xF)) { |
||||
err = -EINVAL; |
||||
goto fail; |
||||
} |
||||
|
||||
/* Will the code fit? (destination includes wb_header + wb code) */ |
||||
if (seg_length < (length + sizeof(struct wb_header))) { |
||||
err = -EINVAL; |
||||
goto fail; |
||||
} |
||||
|
||||
dst_header = (struct wb_header *)seg_address; |
||||
memset((char *)dst_header, 0, sizeof(struct wb_header)); |
||||
|
||||
/* Populate the random_aes_block as requested. */ |
||||
{ |
||||
u32 *aes_block = (u32 *)&(dst_header->random_aes_block); |
||||
u32 *end = (u32 *)(((u32)aes_block) + |
||||
sizeof(dst_header->random_aes_block)); |
||||
|
||||
do { |
||||
*aes_block++ = 0; |
||||
} while (aes_block < end); |
||||
} |
||||
|
||||
/* Populate the header. */ |
||||
dst_header->length_insecure = length + sizeof(struct wb_header); |
||||
dst_header->length_secure = length + sizeof(struct wb_header); |
||||
dst_header->destination = AP20_WB_RUN_ADDRESS; |
||||
dst_header->entry_point = AP20_WB_RUN_ADDRESS; |
||||
dst_header->code_length = length; |
||||
|
||||
if (is_encrypted) { |
||||
printf("!!!! Encryption is not supported !!!!\n"); |
||||
dst_header->length_insecure = 0; |
||||
err = -EACCES; |
||||
goto fail; |
||||
} else |
||||
/* copy the wb code directly following dst_header. */ |
||||
memcpy((char *)(dst_header+1), (char *)wb_start, length); |
||||
|
||||
if (is_signed) |
||||
err = sign_wb_code(seg_address, dst_header->length_insecure, |
||||
use_zero_key); |
||||
|
||||
fail: |
||||
if (err) |
||||
printf("Warning: warmboot code copy failed (error=%d)\n", err); |
||||
|
||||
return err; |
||||
} |
@ -0,0 +1,250 @@ |
||||
/*
|
||||
* (C) Copyright 2010 - 2011 |
||||
* NVIDIA Corporation <www.nvidia.com> |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#include <common.h> |
||||
#include <asm/io.h> |
||||
#include <asm/arch/ap20.h> |
||||
#include <asm/arch/clk_rst.h> |
||||
#include <asm/arch/clock.h> |
||||
#include <asm/arch/flow.h> |
||||
#include <asm/arch/pinmux.h> |
||||
#include <asm/arch/pmc.h> |
||||
#include <asm/arch/tegra2.h> |
||||
#include <asm/arch/warmboot.h> |
||||
#include "warmboot_avp.h" |
||||
|
||||
#define DEBUG_RESET_CORESIGHT |
||||
|
||||
void wb_start(void) |
||||
{ |
||||
struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; |
||||
struct pmc_ctlr *pmc = (struct pmc_ctlr *)TEGRA2_PMC_BASE; |
||||
struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE; |
||||
struct clk_rst_ctlr *clkrst = |
||||
(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; |
||||
union osc_ctrl_reg osc_ctrl; |
||||
union pllx_base_reg pllx_base; |
||||
union pllx_misc_reg pllx_misc; |
||||
union scratch3_reg scratch3; |
||||
u32 reg; |
||||
|
||||
/* enable JTAG & TBE */ |
||||
writel(CONFIG_CTL_TBE | CONFIG_CTL_JTAG, &pmt->pmt_cfg_ctl); |
||||
|
||||
/* Are we running where we're supposed to be? */ |
||||
asm volatile ( |
||||
"adr %0, wb_start;" /* reg: wb_start address */ |
||||
: "=r"(reg) /* output */ |
||||
/* no input, no clobber list */ |
||||
); |
||||
|
||||
if (reg != AP20_WB_RUN_ADDRESS) |
||||
goto do_reset; |
||||
|
||||
/* Are we running with AVP? */ |
||||
if (readl(NV_PA_PG_UP_BASE + PG_UP_TAG_0) != PG_UP_TAG_AVP) |
||||
goto do_reset; |
||||
|
||||
#ifdef DEBUG_RESET_CORESIGHT |
||||
/* Assert CoreSight reset */ |
||||
reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_U]); |
||||
reg |= SWR_CSITE_RST; |
||||
writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_U]); |
||||
#endif |
||||
|
||||
/* TODO: Set the drive strength - maybe make this a board parameter? */ |
||||
osc_ctrl.word = readl(&clkrst->crc_osc_ctrl); |
||||
osc_ctrl.xofs = 4; |
||||
osc_ctrl.xoe = 1; |
||||
writel(osc_ctrl.word, &clkrst->crc_osc_ctrl); |
||||
|
||||
/* Power up the CPU complex if necessary */ |
||||
if (!(readl(&pmc->pmc_pwrgate_status) & PWRGATE_STATUS_CPU)) { |
||||
reg = PWRGATE_TOGGLE_PARTID_CPU | PWRGATE_TOGGLE_START; |
||||
writel(reg, &pmc->pmc_pwrgate_toggle); |
||||
while (!(readl(&pmc->pmc_pwrgate_status) & PWRGATE_STATUS_CPU)) |
||||
; |
||||
} |
||||
|
||||
/* Remove the I/O clamps from the CPU power partition. */ |
||||
reg = readl(&pmc->pmc_remove_clamping); |
||||
reg |= CPU_CLMP; |
||||
writel(reg, &pmc->pmc_remove_clamping); |
||||
|
||||
reg = EVENT_ZERO_VAL_20 | EVENT_MSEC | EVENT_MODE_STOP; |
||||
writel(reg, &flow->halt_cop_events); |
||||
|
||||
/* Assert CPU complex reset */ |
||||
reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_L]); |
||||
reg |= CPU_RST; |
||||
writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_L]); |
||||
|
||||
/* Hold both CPUs in reset */ |
||||
reg = CPU_CMPLX_CPURESET0 | CPU_CMPLX_CPURESET1 | CPU_CMPLX_DERESET0 | |
||||
CPU_CMPLX_DERESET1 | CPU_CMPLX_DBGRESET0 | CPU_CMPLX_DBGRESET1; |
||||
writel(reg, &clkrst->crc_cpu_cmplx_set); |
||||
|
||||
/* Halt CPU1 at the flow controller for uni-processor configurations */ |
||||
writel(EVENT_MODE_STOP, &flow->halt_cpu1_events); |
||||
|
||||
/*
|
||||
* Set the CPU reset vector. SCRATCH41 contains the physical |
||||
* address of the CPU-side restoration code. |
||||
*/ |
||||
reg = readl(&pmc->pmc_scratch41); |
||||
writel(reg, EXCEP_VECTOR_CPU_RESET_VECTOR); |
||||
|
||||
/* Select CPU complex clock source */ |
||||
writel(CCLK_PLLP_BURST_POLICY, &clkrst->crc_cclk_brst_pol); |
||||
|
||||
/* Start the CPU0 clock and stop the CPU1 clock */ |
||||
reg = CPU_CMPLX_CPU_BRIDGE_CLKDIV_4 | CPU_CMPLX_CPU0_CLK_STP_RUN | |
||||
CPU_CMPLX_CPU1_CLK_STP_STOP; |
||||
writel(reg, &clkrst->crc_clk_cpu_cmplx); |
||||
|
||||
/* Enable the CPU complex clock */ |
||||
reg = readl(&clkrst->crc_clk_out_enb[TEGRA_DEV_L]); |
||||
reg |= CLK_ENB_CPU; |
||||
writel(reg, &clkrst->crc_clk_out_enb[TEGRA_DEV_L]); |
||||
|
||||
/* Make sure the resets were held for at least 2 microseconds */ |
||||
reg = readl(TIMER_USEC_CNTR); |
||||
while (readl(TIMER_USEC_CNTR) <= (reg + 2)) |
||||
; |
||||
|
||||
#ifdef DEBUG_RESET_CORESIGHT |
||||
/*
|
||||
* De-assert CoreSight reset. |
||||
* NOTE: We're leaving the CoreSight clock on the oscillator for |
||||
* now. It will be restored to its original clock source |
||||
* when the CPU-side restoration code runs. |
||||
*/ |
||||
reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_U]); |
||||
reg &= ~SWR_CSITE_RST; |
||||
writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_U]); |
||||
#endif |
||||
|
||||
/* Unlock the CPU CoreSight interfaces */ |
||||
reg = 0xC5ACCE55; |
||||
writel(reg, CSITE_CPU_DBG0_LAR); |
||||
writel(reg, CSITE_CPU_DBG1_LAR); |
||||
|
||||
/*
|
||||
* Sample the microsecond timestamp again. This is the time we must |
||||
* use when returning from LP0 for PLL stabilization delays. |
||||
*/ |
||||
reg = readl(TIMER_USEC_CNTR); |
||||
writel(reg, &pmc->pmc_scratch1); |
||||
|
||||
pllx_base.word = 0; |
||||
pllx_misc.word = 0; |
||||
scratch3.word = readl(&pmc->pmc_scratch3); |
||||
|
||||
/* Get the OSC. For 19.2 MHz, use 19 to make the calculations easier */ |
||||
reg = (readl(TIMER_USEC_CFG) & USEC_CFG_DIVISOR_MASK) + 1; |
||||
|
||||
/*
|
||||
* According to the TRM, for 19.2MHz OSC, the USEC_DIVISOR is 0x5f, and |
||||
* USEC_DIVIDEND is 0x04. So, if USEC_DIVISOR > 26, OSC is 19.2 MHz. |
||||
* |
||||
* reg is used to calculate the pllx freq, which is used to determine if |
||||
* to set dccon or not. |
||||
*/ |
||||
if (reg > 26) |
||||
reg = 19; |
||||
|
||||
/* PLLX_BASE.PLLX_DIVM */ |
||||
if (scratch3.pllx_base_divm == reg) |
||||
reg = 0; |
||||
else |
||||
reg = 1; |
||||
|
||||
/* PLLX_BASE.PLLX_DIVN */ |
||||
pllx_base.divn = scratch3.pllx_base_divn; |
||||
reg = scratch3.pllx_base_divn << reg; |
||||
|
||||
/* PLLX_BASE.PLLX_DIVP */ |
||||
pllx_base.divp = scratch3.pllx_base_divp; |
||||
reg = reg >> scratch3.pllx_base_divp; |
||||
|
||||
pllx_base.bypass = 1; |
||||
|
||||
/* PLLX_MISC_DCCON must be set for pllx frequency > 600 MHz. */ |
||||
if (reg > 600) |
||||
pllx_misc.dccon = 1; |
||||
|
||||
/* PLLX_MISC_LFCON */ |
||||
pllx_misc.lfcon = scratch3.pllx_misc_lfcon; |
||||
|
||||
/* PLLX_MISC_CPCON */ |
||||
pllx_misc.cpcon = scratch3.pllx_misc_cpcon; |
||||
|
||||
writel(pllx_misc.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_misc); |
||||
writel(pllx_base.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); |
||||
|
||||
pllx_base.enable = 1; |
||||
writel(pllx_base.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); |
||||
pllx_base.bypass = 0; |
||||
writel(pllx_base.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); |
||||
|
||||
writel(0, flow->halt_cpu_events); |
||||
|
||||
reg = CPU_CMPLX_CPURESET0 | CPU_CMPLX_DBGRESET0 | CPU_CMPLX_DERESET0; |
||||
writel(reg, &clkrst->crc_cpu_cmplx_clr); |
||||
|
||||
reg = PLLM_OUT1_RSTN_RESET_DISABLE | PLLM_OUT1_CLKEN_ENABLE | |
||||
PLLM_OUT1_RATIO_VAL_8; |
||||
writel(reg, &clkrst->crc_pll[CLOCK_ID_MEMORY].pll_out); |
||||
|
||||
reg = SCLK_SWAKE_FIQ_SRC_PLLM_OUT1 | SCLK_SWAKE_IRQ_SRC_PLLM_OUT1 | |
||||
SCLK_SWAKE_RUN_SRC_PLLM_OUT1 | SCLK_SWAKE_IDLE_SRC_PLLM_OUT1 | |
||||
SCLK_SYS_STATE_IDLE; |
||||
writel(reg, &clkrst->crc_sclk_brst_pol); |
||||
|
||||
/* avp_resume: no return after the write */ |
||||
reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_L]); |
||||
reg &= ~CPU_RST; |
||||
writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_L]); |
||||
|
||||
/* avp_halt: */ |
||||
avp_halt: |
||||
reg = EVENT_MODE_STOP | EVENT_JTAG; |
||||
writel(reg, flow->halt_cop_events); |
||||
goto avp_halt; |
||||
|
||||
do_reset: |
||||
/*
|
||||
* Execution comes here if something goes wrong. The chip is reset and |
||||
* a cold boot is performed. |
||||
*/ |
||||
writel(SWR_TRIG_SYS_RST, &clkrst->crc_rst_dev[TEGRA_DEV_L]); |
||||
goto do_reset; |
||||
} |
||||
|
||||
/*
|
||||
* wb_end() is a dummy function, and must be directly following wb_start(), |
||||
* and is used to calculate the size of wb_start(). |
||||
*/ |
||||
void wb_end(void) |
||||
{ |
||||
} |
@ -0,0 +1,81 @@ |
||||
/*
|
||||
* (C) Copyright 2010, 2011 |
||||
* NVIDIA Corporation <www.nvidia.com> |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#ifndef _WARMBOOT_AVP_H_ |
||||
#define _WARMBOOT_AVP_H_ |
||||
|
||||
#define TEGRA_DEV_L 0 |
||||
#define TEGRA_DEV_H 1 |
||||
#define TEGRA_DEV_U 2 |
||||
|
||||
#define SIMPLE_PLLX (CLOCK_ID_XCPU - CLOCK_ID_FIRST_SIMPLE) |
||||
#define SIMPLE_PLLE (CLOCK_ID_EPCI - CLOCK_ID_FIRST_SIMPLE) |
||||
|
||||
#define TIMER_USEC_CNTR (NV_PA_TMRUS_BASE + 0) |
||||
#define TIMER_USEC_CFG (NV_PA_TMRUS_BASE + 4) |
||||
|
||||
#define USEC_CFG_DIVISOR_MASK 0xffff |
||||
|
||||
#define CONFIG_CTL_TBE (1 << 7) |
||||
#define CONFIG_CTL_JTAG (1 << 6) |
||||
|
||||
#define CPU_RST (1 << 0) |
||||
#define CLK_ENB_CPU (1 << 0) |
||||
#define SWR_TRIG_SYS_RST (1 << 2) |
||||
#define SWR_CSITE_RST (1 << 9) |
||||
|
||||
#define PWRGATE_STATUS_CPU (1 << 0) |
||||
#define PWRGATE_TOGGLE_PARTID_CPU (0 << 0) |
||||
#define PWRGATE_TOGGLE_START (1 << 8) |
||||
|
||||
#define CPU_CMPLX_CPU_BRIDGE_CLKDIV_4 (3 << 0) |
||||
#define CPU_CMPLX_CPU0_CLK_STP_STOP (1 << 8) |
||||
#define CPU_CMPLX_CPU0_CLK_STP_RUN (0 << 8) |
||||
#define CPU_CMPLX_CPU1_CLK_STP_STOP (1 << 9) |
||||
#define CPU_CMPLX_CPU1_CLK_STP_RUN (0 << 9) |
||||
|
||||
#define CPU_CMPLX_CPURESET0 (1 << 0) |
||||
#define CPU_CMPLX_CPURESET1 (1 << 1) |
||||
#define CPU_CMPLX_DERESET0 (1 << 4) |
||||
#define CPU_CMPLX_DERESET1 (1 << 5) |
||||
#define CPU_CMPLX_DBGRESET0 (1 << 12) |
||||
#define CPU_CMPLX_DBGRESET1 (1 << 13) |
||||
|
||||
#define PLLM_OUT1_RSTN_RESET_DISABLE (1 << 0) |
||||
#define PLLM_OUT1_CLKEN_ENABLE (1 << 1) |
||||
#define PLLM_OUT1_RATIO_VAL_8 (8 << 8) |
||||
|
||||
#define SCLK_SYS_STATE_IDLE (1 << 28) |
||||
#define SCLK_SWAKE_FIQ_SRC_PLLM_OUT1 (7 << 12) |
||||
#define SCLK_SWAKE_IRQ_SRC_PLLM_OUT1 (7 << 8) |
||||
#define SCLK_SWAKE_RUN_SRC_PLLM_OUT1 (7 << 4) |
||||
#define SCLK_SWAKE_IDLE_SRC_PLLM_OUT1 (7 << 0) |
||||
|
||||
#define EVENT_ZERO_VAL_20 (20 << 0) |
||||
#define EVENT_MSEC (1 << 24) |
||||
#define EVENT_JTAG (1 << 28) |
||||
#define EVENT_MODE_STOP (2 << 29) |
||||
|
||||
#define CCLK_PLLP_BURST_POLICY 0x20004444 |
||||
|
||||
#endif |
@ -0,0 +1,150 @@ |
||||
/*
|
||||
* (C) Copyright 2010, 2011 |
||||
* NVIDIA Corporation <www.nvidia.com> |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* This program is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU General Public License as |
||||
* published by the Free Software Foundation; either version 2 of |
||||
* the License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU General Public License |
||||
* along with this program; if not, write to the Free Software |
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
||||
* MA 02111-1307 USA |
||||
*/ |
||||
|
||||
#ifndef _WARM_BOOT_H_ |
||||
#define _WARM_BOOT_H_ |
||||
|
||||
#define STRAP_OPT_A_RAM_CODE_SHIFT 4 |
||||
#define STRAP_OPT_A_RAM_CODE_MASK (0xf << STRAP_OPT_A_RAM_CODE_SHIFT) |
||||
|
||||
/* Defines the supported operating modes */ |
||||
enum fuse_operating_mode { |
||||
MODE_PRODUCTION = 3, |
||||
MODE_UNDEFINED, |
||||
}; |
||||
|
||||
/* Defines the CMAC-AES-128 hash length in 32 bit words. (128 bits = 4 words) */ |
||||
enum { |
||||
HASH_LENGTH = 4 |
||||
}; |
||||
|
||||
/* Defines the storage for a hash value (128 bits) */ |
||||
struct hash { |
||||
u32 hash[HASH_LENGTH]; |
||||
}; |
||||
|
||||
/*
|
||||
* Defines the code header information for the boot rom. |
||||
* |
||||
* The code immediately follows the code header. |
||||
* |
||||
* Note that the code header needs to be 16 bytes aligned to preserve |
||||
* the alignment of relevant data for hash and decryption computations without |
||||
* requiring extra copies to temporary memory areas. |
||||
*/ |
||||
struct wb_header { |
||||
u32 length_insecure; /* length of the code header */ |
||||
u32 reserved[3]; |
||||
struct hash hash; /* hash of header+code, starts next field*/ |
||||
struct hash random_aes_block; /* a data block to aid security. */ |
||||
u32 length_secure; /* length of the code header */ |
||||
u32 destination; /* destination address to put the wb code */ |
||||
u32 entry_point; /* execution address of the wb code */ |
||||
u32 code_length; /* length of the code */ |
||||
}; |
||||
|
||||
/*
|
||||
* The warm boot code needs direct access to these registers since it runs in |
||||
* SRAM and cannot call other U-Boot code. |
||||
*/ |
||||
union osc_ctrl_reg { |
||||
struct { |
||||
u32 xoe:1; |
||||
u32 xobp:1; |
||||
u32 reserved0:2; |
||||
u32 xofs:6; |
||||
u32 reserved1:2; |
||||
u32 xods:5; |
||||
u32 reserved2:3; |
||||
u32 oscfi_spare:8; |
||||
u32 pll_ref_div:2; |
||||
u32 osc_freq:2; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
union pllx_base_reg { |
||||
struct { |
||||
u32 divm:5; |
||||
u32 reserved0:3; |
||||
u32 divn:10; |
||||
u32 reserved1:2; |
||||
u32 divp:3; |
||||
u32 reserved2:4; |
||||
u32 lock:1; |
||||
u32 reserved3:1; |
||||
u32 ref_dis:1; |
||||
u32 enable:1; |
||||
u32 bypass:1; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
union pllx_misc_reg { |
||||
struct { |
||||
u32 vcocon:4; |
||||
u32 lfcon:4; |
||||
u32 cpcon:4; |
||||
u32 lock_sel:6; |
||||
u32 reserved0:1; |
||||
u32 lock_enable:1; |
||||
u32 reserved1:1; |
||||
u32 dccon:1; |
||||
u32 pts:2; |
||||
u32 reserved2:6; |
||||
u32 out1_div_byp:1; |
||||
u32 out1_inv_clk:1; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
/*
|
||||
* TODO: This register is not documented in the TRM yet. We could move this |
||||
* into the EMC and give it a proper interface, but not while it is |
||||
* undocumented. |
||||
*/ |
||||
union scratch3_reg { |
||||
struct { |
||||
u32 pllx_base_divm:5; |
||||
u32 pllx_base_divn:10; |
||||
u32 pllx_base_divp:3; |
||||
u32 pllx_misc_lfcon:4; |
||||
u32 pllx_misc_cpcon:4; |
||||
}; |
||||
u32 word; |
||||
}; |
||||
|
||||
|
||||
/**
|
||||
* Save warmboot memory settings for a later resume |
||||
* |
||||
* @return 0 if ok, -1 on error |
||||
*/ |
||||
int warmboot_save_sdram_params(void); |
||||
|
||||
int warmboot_prepare_code(u32 seg_address, u32 seg_length); |
||||
int sign_data_block(u8 *source, u32 length, u8 *signature); |
||||
void wb_start(void); /* Start of WB assembly code */ |
||||
void wb_end(void); /* End of WB assembly code */ |
||||
|
||||
#endif |
Loading…
Reference in new issue