diff options
author | Yen Lin <yelin@nvidia.com> | 2011-06-10 18:45:54 -0700 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2011-08-29 10:39:26 -0700 |
commit | 4b723a7ac493c11e40866bd42099ebe99954f756 (patch) | |
tree | a43cb6a7d5c228b68b8e31a5fb07462067e069bc /arch/arm/cpu | |
parent | bcff9a34dca37631a7b8f65dd9a4002f47ff01c0 (diff) |
tegra2: add warmboot code to support LP0
BUG=None
TEST=Boot to kernel, suspend to LP0 then resume
Change-Id: I4744b70a1febae0ceae546c5836634f11a5db5dc
Reviewed-on: http://gerrit.chromium.org/gerrit/2504
Tested-by: Yen Lin <yelin@nvidia.com>
Reviewed-by: Anton Staaf <robotboy@chromium.org>
Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'arch/arm/cpu')
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/Makefile | 10 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/ap20.c | 6 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/warmboot.c | 448 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/warmboot_avp.S | 402 | ||||
-rw-r--r-- | arch/arm/cpu/armv7/tegra2/warmboot_avp.h | 161 |
5 files changed, 1025 insertions, 2 deletions
diff --git a/arch/arm/cpu/armv7/tegra2/Makefile b/arch/arm/cpu/armv7/tegra2/Makefile index 06035f48ee..f9b81a4208 100644 --- a/arch/arm/cpu/armv7/tegra2/Makefile +++ b/arch/arm/cpu/armv7/tegra2/Makefile @@ -32,10 +32,16 @@ include $(TOPDIR)/config.mk LIB = $(obj)lib$(SOC).o -SOBJS := lowlevel_init.o -COBJS := ap20.o board.o clock.o display.o pinmux.o power.o pwfm.o \ +SOBJS-y := lowlevel_init.o +COBJS-y := ap20.o board.o clock.o display.o pinmux.o power.o pwfm.o \ sys_info.o timer.o +SOBJS-$(CONFIG_TEGRA2_LP0) += warmboot_avp.o +COBJS-$(CONFIG_TEGRA2_LP0) += warmboot.o + +SOBJS := $(SOBJS-y) +COBJS := $(COBJS-y) + SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS) $(SOBJS)) diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c index 4fec9f8dd1..abcfa977ab 100644 --- a/arch/arm/cpu/armv7/tegra2/ap20.c +++ b/arch/arm/cpu/armv7/tegra2/ap20.c @@ -31,6 +31,7 @@ #include <asm/arch/pinmux.h> #include <asm/arch/scu.h> #include <common.h> +#include <asm/arch/warmboot.h> #include "../../../../../board/nvidia/common/board.h" struct clk_pll_table { @@ -340,6 +341,11 @@ void init_pmc_scratch(void) /* ODMDATA is for kernel use to determine RAM size, LP config, etc. */ writel(CONFIG_SYS_BOARD_ODMDATA, &pmc->pmc_scratch20); + +#ifdef CONFIG_TEGRA2_LP0 + /* save Sdram params to PMC 2, 4, and 24 for WB0 */ + warmboot_save_sdram_params(); +#endif } void tegra2_start(void) diff --git a/arch/arm/cpu/armv7/tegra2/warmboot.c b/arch/arm/cpu/armv7/tegra2/warmboot.c new file mode 100644 index 0000000000..dfa51e4aec --- /dev/null +++ b/arch/arm/cpu/armv7/tegra2/warmboot.c @@ -0,0 +1,448 @@ +/* + * (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/bitfield.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> +#include "ap20.h" + +#define BCT_OFFSET 0x100 /* BCT starts at 0x100 */ +#define BCT_SDRAM_PARAMS_OFFSET (BCT_OFFSET + 0x88) +#define SDRAM_PARAMS_BASE (AP20_BASE_PA_SRAM + BCT_SDRAM_PARAMS_OFFSET) + +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 pll_base_reg { + struct { + u32 pll_divm:5; + u32 reserved0:3; + u32 pll_divn:10; + u32 reserved1:2; + u32 pll_divp:3; + u32 reserved2:4; + u32 pll_lock:1; + u32 reserved3:1; + u32 pll_ref_dis:1; + u32 pll_enable:1; + u32 pll_bypass:1; + }; + u32 word; +}; + +union pll_misc_reg { + struct { + u32 pll_vcocon:4; + u32 pll_lfcon:4; + u32 pll_cpcon:4; + u32 pll_lock_sel:6; + u32 reserved0:1; + u32 pll_lock_enable:1; + u32 reserved1:1; + u32 pll_dccon:1; + u32 pll_pts:2; + u32 reserved2:6; + u32 pll_out1_div_byp:1; + u32 pll_out1_inv_clk:1; + }; + u32 word; +}; + +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; +}; + +union fbio_spare_reg { + struct { + u32 reserved:24; + u32 cfg_wb0:8; + }; + u32 word; +}; + +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; +}; + +void 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 clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + struct apb_misc_gp_ctlr *gp = + (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; + struct emc_ctlr *emc = (struct emc_ctlr *)NV_PA_EMC_BASE; + union osc_ctrl_reg osc_ctrl; + union pll_base_reg pllm_base; + union pll_misc_reg pllm_misc; + 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 = bf_readl(STRAP_OPT_A_RAM_CODE, &pmt->pmt_strap_opt_a) & 3; + memcpy(&sdram, + (char *)((struct sdram_params *)SDRAM_PARAMS_BASE + ram_code), + sizeof(sdram)); + + osc_ctrl.word = readl(&clkrst->crc_osc_ctrl); + pllm_base.word = readl(&clkrst->crc_pll[CLOCK_ID_MEMORY].pll_base); + pllm_misc.word = readl(&clkrst->crc_pll[CLOCK_ID_MEMORY].pll_misc); + xm2cfga.word = readl(&gp->xm2cfga); + xm2cfgd.word = readl(&gp->xm2cfgd); + + scratch2.word = 0; + scratch2.osc_ctrl_xobp = osc_ctrl.xobp; + scratch2.pllm_base_divm = pllm_base.pll_divm; + scratch2.pllm_base_divn = pllm_base.pll_divn; + scratch2.pllm_base_divp = pllm_base.pll_divp; + scratch2.pllm_misc_cpcon = pllm_misc.pll_cpcon; + scratch2.pllm_misc_lfcon = pllm_misc.pll_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); +} + +/* + * NOTE: If more than one of the following is enabled, only one of them will + * actually be used. RANDOM takes precedence over PATTERN and ZERO, and + * PATTERN takes precedence overy ZERO. + * + * RANDOM_AES_BLOCK_IS_PATTERN is to define a 32-bit PATTERN. + */ +#define RANDOM_AES_BLOCK_IS_RANDOM /* to randomize the header */ +#undef RANDOM_AES_BLOCK_IS_PATTERN /* to patternize the header */ +#undef RANDOM_AES_BLOCK_IS_ZERO /* to clear the header */ + +static u32 get_major_version(void) +{ + u32 major_id; + struct apb_misc_gp_ctlr *gp = + (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; + + major_id = bf_readl(HIDREV_MAJORPREV, &gp->hidrev); + 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 *)NV_PA_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 *)NV_PA_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 *)NV_PA_APB_MISC_GP_BASE; + + chip_id = bf_readl(HIDREV_CHIPID, &gp->hidrev); + 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; +} + +/* Currently, this routine returns a 32-bit all 0 seed. */ +static u32 query_random_seed(void) +{ + return 0; +} + +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 start; /* start of the actual code */ + u32 length; /* length of the signed/encrypt code */ + struct wb_header *src_header; /* Pointer to src WB header */ + 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. */ + start = (u32)wb_start; + length = roundup(((u32)wb_end - start), 16); + + /* + * The region specified by seg_address must not be in IRAM and must be + * nonzero in length. + */ + if ((seg_length == 0) || (seg_address == 0) || + (seg_address >= AP20_BASE_PA_SRAM)) { + 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? */ + if (seg_length < length) { + err = -EINVAL; + goto fail; + } + + /* Get a pointers to the source and destination region header. */ + src_header = (struct wb_header *)start; + dst_header = (struct wb_header *)seg_address; + + /* Populate the random_aes_block as requested. */ + { + u32 *aes_block = (u32 *)&(src_header->random_aes_block); + u32 *end = (u32 *)(((u32)aes_block) + + sizeof(src_header->random_aes_block)); + + do { +#if defined(RANDOM_AES_BLOCK_IS_RANDOM) + *aes_block++ = query_random_seed(); +#elif defined(RANDOM_AES_BLOCK_IS_PATTERN) + *aes_block++ = RANDOM_AES_BLOCK_IS_PATTERN; +#elif defined(RANDOM_AES_BLOCK_IS_ZERO) + *aes_block++ = 0; +#else + printf("None of RANDOM_AES_BLOCK_IS_XXX is defined; "); + printf("Default to pattern 0.\n"); + *aes_block++ = 0; +#endif + } while (aes_block < end); + } + + /* Populate the header. */ + src_header->length_in_secure = length; + src_header->length_secure = length; + src_header->destination = AP20_WB_RUN_ADDRESS; + src_header->entry_point = AP20_WB_RUN_ADDRESS; + src_header->code_length = length - sizeof(struct wb_header); + + if (is_encrypted) { + printf("!!!! Encryption is not supported !!!!\n"); + dst_header->length_in_secure = 0; + err = -EACCES; + goto fail; + } else + /* No, just copy the code directly. */ + memcpy(dst_header, src_header, length); + + /* Clear the signature in the destination code segment. */ + memset(&(dst_header->hash), 0, sizeof(dst_header->hash)); + + if (is_signed) + err = sign_wb_code(seg_address, length, use_zero_key); + +fail: + if (err) + printf("WB code not copied to LP0 location! (error=%d)\n", err); + + return err; +} diff --git a/arch/arm/cpu/armv7/tegra2/warmboot_avp.S b/arch/arm/cpu/armv7/tegra2/warmboot_avp.S new file mode 100644 index 0000000000..3f712acb7d --- /dev/null +++ b/arch/arm/cpu/armv7/tegra2/warmboot_avp.S @@ -0,0 +1,402 @@ +/* + * (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 <asm/arch/tegra2.h> +#include "warmboot_avp.h" + +/* Set non-zero to skip resetting CoreSight */ +#define DEBUG_DO_NOT_RESET_CORESIGHT 0 + + .section .text + .align 4 + +/* ------------------------------------------------------ + * void wb_start(void) + * + * Input: + * + * Output: + * None + * + * Registers Used: + * ALL + * + * Description: + * This function restarts the CPU and then waits for the AVP driver to + * tell it where to transfer control to finish restoration of the AVP state. + * --------------------------------------------------------------------------- + */ + + .globl wb_start +wb_start: + + .word 0, 0, 0, 0 + .word 0, 0, 0, 0 + .word 0, 0, 0, 0 + .word 0, 0, 0, 0 + +start: + ldr r0, =NV_PA_APB_MISC_BASE /* R0 = MISC PA base address */ + ldr r1, =APB_MISC_PP_CONFIG_CTL_JTAG_ENABLE \ + | APB_MISC_PP_CONFIG_CTL_TBE_ENABLE + str r1, [r0, #APB_MISC_PP_CONFIG_CTL_OFFSET] + + /* ----------------------------------------------------------------- + * Load up the base addresses for the register blocks. + * ----------------------------------------------------------------- + */ + ldr r5, =NV_PA_PMC_BASE /* R5 = PMC PA base address */ + ldr r6, =NV_PA_FLOW_BASE /* R6 = FLOW PA base address */ + ldr r7, =NV_PA_TMRUS_BASE /* R7 = TIMERUS PA base address */ + ldr r8, =NV_PA_CLK_RST_BASE /* R8 = CLK PA base address */ + ldr r9, =NV_PA_EVP_BASE /* R9 = EVP PA base address */ + ldr r10,=NV_PA_CSITE_BASE /* R10 = CSITE base address */ + + /* ----------------------------------------------------------------- + * Are we running where we're supposed to be? + * ----------------------------------------------------------------- + */ + + ldr r0, =AP20_WB_RUN_ADDRESS /* R0 = expected load address */ + add r1, pc, #here-(.+8) /* R1 = &here */ + +here: + sub r1, r1, #(here-start) /* R1 = actual load address */ + ldr r2, =NV_PA_PG_UP_BASE /* R2 = PG PA base address */ + ldr r3, [r2, #PG_UP_TAG_OFFSET] /* R3 = processor tag */ + ldr r2, =PG_UP_TAG_AVP /* R2 = AVP processor tag */ + cmp r0, r1 /* Addresses match? */ + cmpeq r2, r3 /* Processor tags match? */ + bne do_reset /* No -- reset the chip */ + + + /* ----------------------------------------------------------------- + * Get a snapshot of the Usec count. This is a good indicator of + * the overhead of BOOTROM after a wake-up event. + * ----------------------------------------------------------------- + */ + + ldr r11, [r7, #TIMERUS_CNTR_1US_OFFSET] + + /* ================================================================== + * BEGIN CPU COMPLEX INITIALIZATON + * ================================================================== + */ + +#if !DEBUG_DO_NOT_RESET_CORESIGHT + + /* ---------------------------------------------------------------- + * Assert CoreSight reset. + * ---------------------------------------------------------------- + */ + + mov r1, #CLK_RST_DEV_U_SET_SET_CSITE_RST + str r1, [r8, #CLK_RST_CTLR_RST_DEV_U_SET_OFFSET] + +#endif /*!DEBUG_DO_NOT_RESET_CORESIGHT */ + + /* Set the drive strength */ + ldr r1, [r8, #CLK_RST_CTLR_OSC_CTRL_OFFSET] + ldr r3, =CLK_RST_CTLR_OSC_CTRL_XOFS_MASK \ + | CLK_RST_CTLR_OSC_CTRL_XOE_MASK + bic r1, r1, r3 + ldr r3, =CLK_RST_CTLR_OSC_CTRL_XOFS_VAL_4 \ + | CLK_RST_CTLR_OSC_CTRL_XOE_ENABLE + orr r3, r1, r3 + str r3, [r8, #CLK_RST_CTLR_OSC_CTRL_OFFSET] + + /* ----------------------------------------------------------------- + * Power up the CPU complex if necessary. + * ----------------------------------------------------------------- + */ + + ldr r3, [r5, #PMC_PWRGATE_STATUS_OFFSET] + tst r3, #PMC_PWRGATE_STATUS_CPU_MASK + + ldreq r2, =PMC_PWRGATE_TOGGLE_PARTID_CPU \ + | PMC_PWRGATE_TOGGLE_START_ENABLE + streq r2, [r5, #PMC_PWRGATE_TOGGLE_OFFSET] + +is_cpu_on: + ldr r3, [r5, #PMC_PWRGATE_STATUS_OFFSET] + tst r3, #PMC_PWRGATE_STATUS_CPU_MASK + beq is_cpu_on + + /* ----------------------------------------------------------------- + * Remove the I/O clamps from the CPU power partition. + * ----------------------------------------------------------------- + */ + + mov r3, #PMC_REMOVE_CLAMPING_CMD_CPU_ENABLE + str r3, [r5, #PMC_REMOVE_CLAMPING_CMD_OFFSET] + + ldr r3, =FLOW_CTLR_HALT_COP_EVENTS_ZERO_VAL_20 \ + | FLOW_CTLR_HALT_COP_EVENTS_MSEC \ + | FLOW_CTLR_HALT_COP_EVENTS_MODE_STOP + str r3, [r6, #FLOW_CTLR_HALT_COP_EVENTS_OFFSET] + + /* ------------------------------------------------------------------ + * Assert CPU complex reset. + * ------------------------------------------------------------------ + */ + + mov r1, #CLK_RST_CTLR_RST_DEV_L_SET_CPU_RST + str r1, [r8, #CLK_RST_CTLR_RST_DEV_L_SET_OFFSET] + + /* ------------------------------------------------------------------ + * Hold both CPUs in reset. + * ------------------------------------------------------------------ + */ + + ldr r3, =CLK_RST_CTLR_RST_CPU_CMPLX_SET_CPURESET0 \ + | CLK_RST_CTLR_RST_CPU_CMPLX_SET_CPURESET1 \ + | CLK_RST_CTLR_RST_CPU_CMPLX_SET_DERESET0 \ + | CLK_RST_CTLR_RST_CPU_CMPLX_SET_DERESET1 \ + | CLK_RST_CTLR_RST_CPU_CMPLX_SET_DBGRESET0 \ + | CLK_RST_CTLR_RST_CPU_CMPLX_SET_DBGRESET1 + str r3, [r8, #CLK_RST_CTLR_RST_CPU_CMPLX_SET_OFFSET] + + /* ------------------------------------------------------------------ + * Halt CPU1 at the flow controller for uni-processor configurations. + * ------------------------------------------------------------------ + */ + + mov r3, #FLOW_CTLR_HALT_CPU1_EVENTS_MODE_FLOW_MODE_STOP + str r3, [r6, #FLOW_CTLR_HALT_CPU1_EVENTS_OFFSET] + + /* ----------------------------------------------------------------- + * Set the CPU reset vector. SCRATCH41 contains the physical + * address of the CPU-side restoration code. + * ----------------------------------------------------------------- + */ + + ldr r3, [r5, #PMC_SCRATCH41_OFFSET] + str r3, [r9, #EVP_CPU_RESET_VECTOR_OFFSET] + + /* ------------------------------------------------------------------ + * Select CPU complex clock source. + * ------------------------------------------------------------------ + */ +#define CPU_CLK_SRC 4 /* 4 = PLLP_OUT0 */ + ldr r3, =(CPU_CLK_SRC << CLK_RST_CTLR_CCLK_CWAKE_FIQ_SOURCE_SHIFT) \ + | (CPU_CLK_SRC << CLK_RST_CTLR_CCLK_CWAKE_IRQ_SOURCE_SHIFT) \ + | (CPU_CLK_SRC << CLK_RST_CTLR_CCLK_CWAKE_RUN_SOURCE_SHIFT) \ + | (CPU_CLK_SRC << CLK_RST_CTLR_CCLK_CWAKE_IDLE_SOURCE_SHIFT) \ + | CLK_RST_CTLR_CCLK_CPU_STATE_RUN + str r3, [r8, #CLK_RST_CTLR_CCLK_BURST_POLICY_OFFSET] +#undef CPU_CLK_SRC + + /* ------------------------------------------------------------------ + * Start the CPU0 clock and stop the CPU1 clock. + * ------------------------------------------------------------------ + */ + + ldr r3, =CLK_RST_CTLR_CPU_CMPLX_CPU_BRIDGE_CLKDIV_BY_4 \ + | CLK_RST_CTLR_CPU_CMPLX_CPU0_CLK_STP_RUN \ + | CLK_RST_CTLR_CPU_CMPLX_CPU1_CLK_STP_STOP + str r3, [r8, #CLK_RST_CTLR_CPU_CMPLX_OFFSET] + + /* ------------------------------------------------------------------ + * Enable the CPU complex clock. + * ------------------------------------------------------------------ + */ + + mov r3, #CLK_RST_CTLR_CLK_ENB_L_SET_CLK_ENB_CPU + str r3, [r8, #CLK_RST_CTLR_CLK_ENB_L_SET_OFFSET] + + /* ----------------------------------------------------------------- + * Make sure the resets were held for at least 2 microseconds. + * ----------------------------------------------------------------- + */ + + add r3, r11, #2 + +wait: + ldr r2, [r7, #TIMERUS_CNTR_1US_OFFSET] + cmp r2, r3 + ble wait + +#if !DEBUG_DO_NOT_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. + * ----------------------------------------------------------------- + */ + + mov r1, #CLK_RST_CTLR_RST_DEV_U_CLR_CSITE_RST_MASK + str r1, [r8, #CLK_RST_CTLR_RST_DEV_U_CLR_OFFSET] + +#endif /*!DEBUG_DO_NOT_RESET_CORESIGHT */ + + ldr r1, =0xC5ACCE55 /* R0 = CoreSight unlock value*/ + ldr r2, =CSITE_CPUDBG0_LAR_OFFSET /* R1 = CPU0 lock offset */ + ldr r3, =CSITE_CPUDBG1_LAR_OFFSET /* R2 = CPU1 lock offset */ + str r1, [r10, r2] /* Unlock CPU0 */ + str r1, [r10, r3] /* Unlock CPU1 */ + + /* ----------------------------------------------------------------- + * Sample the microsecond timestamp again. This is the time we must + * use when returning from LP0 for PLL stabilization delays. + * ---------------------------------------------------------------- + */ + + ldr r11, [r7, #TIMERUS_CNTR_1US_OFFSET] + str r11, [r5, #PMC_SCRATCH1_OFFSET] + + /* ----------------------------------------------------------------- + * Get the oscillator frequency. For 19.2 MHz, just use 19 to + * make the calculations easier. + * ----------------------------------------------------------------- + */ + + ldr r4, [r7, #TIMERUS_USEC_CFG_OFFSET] + and r4, r4, #TIMERUS_USEC_CFG_USEC_DIVISOR_MASK + add r4, r4, #1 + cmp r4, #26 + MOVGT r4, #19 + + /* PLLX_BASE.PLLX_DIVM */ + ldr r0, [r5, #PMC_SCRATCH3_OFFSET] + and r2, r0, #PMC_SCRATCH3_PLLX_BASE_DIVM_MASK + cmp r2, r4 + moveq r4, #0 + movne r4, #1 + + /* PLLX_BASE.PLLX_DIVN */ + mov r0, r0, ASR #(PMC_SCRATCH3_PLLX_BASE_DIVN_SHIFT - \ + PMC_SCRATCH3_PLLX_BASE_DIVM_SHIFT) + ldr r3, =PMC_SCRATCH3_PLLX_BASE_DIVN_MASK + and r1, r0, r3 + orr r2, r2, r1, LSL #CLK_RST_CTLR_PLLX_BASE_DIVN_SHIFT + mov r4, r1, LSL r4 + + /* PLLX_BASE.PLLX_DIVP */ + mov r0, r0, ASR #(PMC_SCRATCH3_PLLX_BASE_DIVP_SHIFT - \ + PMC_SCRATCH3_PLLX_BASE_DIVN_SHIFT) + and r1, r0, #PMC_SCRATCH3_PLLX_BASE_DIVP_MASK + orr r2, r2, r1, LSL #CLK_RST_CTLR_PLLX_BASE_DIVP_SHIFT + mov r4, r4, ASR r1 + + /* PLLX_BASE.PLLX_BYPASS_ENABLE | PLLX_BASE.PLLX_ENABLE_DISABLE | + * PLLX_BASE.PLLX_REF_DIS_REF_ENABLE + */ + orr r2, r2, #CLK_RST_CTLR_PLLX_BASE_BYPASS_ENABLE \ + | CLK_RST_CTLR_PLLX_BASE_ENABLE_DISABLE \ + | CLK_RST_CTLR_PLLX_BASE_REF_DIS_REF_ENABLE + + /* PLLX_MISC_DCCON must be set for frequencies > 600 MHz. */ + cmp r4, #600 + movlt r3, #0 + movge r3, #CLK_RST_CTLR_PLLX_MISC_DCCON_DEFAULT + + /* PLLX_MISC_LFCON */ + mov r0, r0, ASR #(PMC_SCRATCH3_PLLX_MISC_LFCON_SHIFT - \ + PMC_SCRATCH3_PLLX_BASE_DIVP_SHIFT) + and r1, r0, #PMC_SCRATCH3_PLLX_MISC_LFCON_MASK + orr r3, r3, r1, LSL #CLK_RST_CTLR_PLLX_MISC_LFCON_SHIFT + + /* PLLX_MISC_CPCON */ + mov r0, r0, ASR #(PMC_SCRATCH3_PLLX_MISC_CPCON_SHIFT - \ + PMC_SCRATCH3_PLLX_MISC_LFCON_SHIFT) + and r1, r0, #PMC_SCRATCH3_PLLX_MISC_LFCON_MASK + orr r3, r3, r1, LSL #CLK_RST_CTLR_PLLX_MISC_CPCON_SHIFT + + str r3, [r8, #CLK_RST_CTLR_PLLX_MISC_OFFSET] + str r2, [r8, #CLK_RST_CTLR_PLLX_BASE_OFFSET] + orr r2, r2, #CLK_RST_CTLR_PLLX_BASE_ENABLE_ENABLE + str r2, [r8, #CLK_RST_CTLR_PLLX_BASE_OFFSET] + BIC r2, r2, #CLK_RST_CTLR_PLLX_BASE_BYPASS_ENABLE + str r2, [r8, #CLK_RST_CTLR_PLLX_BASE_OFFSET] + + mov r3, #0 + str r3, [r6, #FLOW_CTLR_HALT_CPU_EVENTS_OFFSET] + + ldr r3, =CLK_RST_CTLR_RST_CPU_CMPLX_CLR_CPURESET0 \ + | CLK_RST_CTLR_RST_CPU_CMPLX_CLR_DBGRESET0 \ + | CLK_RST_CTLR_RST_CPU_CMPLX_CLR_DERESET0 + str r3, [r8, #CLK_RST_CTLR_RST_CPU_CMPLX_CLR_OFFSET] + + ldr r1, = CLK_RST_CTLR_PLLM_OUT1_RSTN_RESET_DISABLE \ + | CLK_RST_CTLR_PLLM_OUT1_CLKEN_ENABLE \ + | CLK_RST_CTLR_PLLM_OUT1_RATIO_VAL_8 + str r1, [r8, #CLK_RST_CTLR_PLLM_OUT_OFFSET] + + ldr r2, =CLK_RST_CTLR_SCLK_SWAKE_FIQ_SOURCE_PLLM_OUT1 \ + | CLK_RST_CTLR_SCLK_SWAKE_IRQ_SOURCE_PLLM_OUT1 \ + | CLK_RST_CTLR_SCLK_SWAKE_RUN_SOURCE_PLLM_OUT1 \ + | CLK_RST_CTLR_SCLK_SWAKE_IDLE_SOURCE_PLLM_OUT1 \ + | CLK_RST_CTLR_SCLK_SYS_STATE_IDLE + str r2, [r8, #CLK_RST_CTLR_SCLK_BURST_POLICY_OFFSET] + b avp_resume + + .ltorg + .align 4 + +avp_resume: + + mov r1, #CLK_RST_CTLR_RST_DEV_L_CLR_CPU_RST + str r1, [r8, #CLK_RST_CTLR_RST_DEV_L_CLR_OFFSET] + +avp_halt: + + mov r3, #FLOW_CTLR_HALT_COP_EVENTS_MODE_STOP + orr r3, r3, #FLOW_CTLR_HALT_COP_EVENTS_JTAG + str r3, [r6, #FLOW_CTLR_HALT_COP_EVENTS_OFFSET] + b avp_halt + +/* --------------------------------------------------------------------------- + * Prototype: + * do_reset + * + * Input: + * None + * + * Output: + * None + * + * Registers Used: + * All + * + * Description: + * Execution comes here it something goes wrong. The chip is reset and a + * cold boot is performed. + * --------------------------------------------------------------------------- + */ + +do_reset: + + mov r0, #CLK_RST_CTLR_RST_DEVICES_L_SWR_TRIG_SYS_RST + str r0, [r8, #CLK_RST_CTLR_RST_DEVICES_L_OFFSET] + b . + + .ltorg + + .globl wb_end +wb_end: + + .end + + diff --git a/arch/arm/cpu/armv7/tegra2/warmboot_avp.h b/arch/arm/cpu/armv7/tegra2/warmboot_avp.h new file mode 100644 index 0000000000..56cdf7157f --- /dev/null +++ b/arch/arm/cpu/armv7/tegra2/warmboot_avp.h @@ -0,0 +1,161 @@ +/* + * (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_ + +/* Followings are used by warmboot_avp.S */ + +#define PMC_SCRATCH1_OFFSET 0x54 +#define PMC_SCRATCH3_OFFSET 0x5c +#define PMC_SCRATCH41_OFFSET 0x140 + +#define PG_UP_TAG_OFFSET 0 +#define TIMERUS_CNTR_1US_OFFSET 0 + +#define CSITE_CPUDBG0_LAR_OFFSET 0x10fb0 +#define CSITE_CPUDBG1_LAR_OFFSET 0x12fb0 + +#define APB_MISC_PP_CONFIG_CTL_OFFSET 0x24 +#define APB_MISC_PP_CONFIG_CTL_TBE_ENABLE (1 << 7) +#define APB_MISC_PP_CONFIG_CTL_JTAG_ENABLE (1 << 6) + +#define CLK_RST_CTLR_RST_DEV_U_SET_OFFSET 0x310 +#define CLK_RST_DEV_U_SET_SET_CSITE_RST (1 << 9) + +#define CLK_RST_CTLR_OSC_CTRL_OFFSET 0x50 +#define CLK_RST_CTLR_OSC_CTRL_XOFS_MASK (0x3f << 4) +#define CLK_RST_CTLR_OSC_CTRL_XOFS_VAL_4 (0x04 << 4) +#define CLK_RST_CTLR_OSC_CTRL_XOE_MASK (0x01 << 0) +#define CLK_RST_CTLR_OSC_CTRL_XOE_ENABLE (0x01 << 0) + +#define PMC_PWRGATE_STATUS_OFFSET 0x38 +#define PMC_PWRGATE_STATUS_CPU_MASK (0x01 << 0) + +#define PMC_PWRGATE_TOGGLE_OFFSET 0x30 +#define PMC_PWRGATE_TOGGLE_PARTID_CPU (0 << 0) +#define PMC_PWRGATE_TOGGLE_START_ENABLE (1 << 8) + +#define PMC_REMOVE_CLAMPING_CMD_OFFSET 0x34 +#define PMC_REMOVE_CLAMPING_CMD_CPU_ENABLE (1 << 0) + +#define FLOW_CTLR_HALT_COP_EVENTS_OFFSET 0x4 +#define FLOW_CTLR_HALT_COP_EVENTS_ZERO_VAL_20 (20 << 0) +#define FLOW_CTLR_HALT_COP_EVENTS_MSEC (1 << 24) +#define FLOW_CTLR_HALT_COP_EVENTS_JTAG (1 << 28) +#define FLOW_CTLR_HALT_COP_EVENTS_MODE_STOP (2 << 29) + +#define CLK_RST_CTLR_RST_DEV_L_SET_OFFSET 0x300 +#define CLK_RST_CTLR_RST_DEV_L_SET_CPU_RST (1 << 0) + +#define CLK_RST_CTLR_RST_CPU_CMPLX_SET_OFFSET 0x340 +#define CLK_RST_CTLR_RST_CPU_CMPLX_SET_CPURESET0 (1 << 0) +#define CLK_RST_CTLR_RST_CPU_CMPLX_SET_CPURESET1 (1 << 1) +#define CLK_RST_CTLR_RST_CPU_CMPLX_SET_DERESET0 (1 << 4) +#define CLK_RST_CTLR_RST_CPU_CMPLX_SET_DERESET1 (1 << 5) +#define CLK_RST_CTLR_RST_CPU_CMPLX_SET_DBGRESET0 (1 << 12) +#define CLK_RST_CTLR_RST_CPU_CMPLX_SET_DBGRESET1 (1 << 13) + +#define FLOW_CTLR_HALT_CPU1_EVENTS_OFFSET 0x14 +#define FLOW_CTLR_HALT_CPU1_EVENTS_MODE_FLOW_MODE_STOP (2 << 29) + +#define EVP_CPU_RESET_VECTOR_OFFSET 0x100 + +#define CLK_RST_CTLR_CCLK_BURST_POLICY_OFFSET 0x20 +#define CLK_RST_CTLR_CCLK_CWAKE_FIQ_SOURCE_SHIFT 12 +#define CLK_RST_CTLR_CCLK_CWAKE_IRQ_SOURCE_SHIFT 8 +#define CLK_RST_CTLR_CCLK_CWAKE_RUN_SOURCE_SHIFT 4 +#define CLK_RST_CTLR_CCLK_CWAKE_IDLE_SOURCE_SHIFT 0 +#define CLK_RST_CTLR_CCLK_CPU_STATE_RUN (2 << 28) + +#define CLK_RST_CTLR_CPU_CMPLX_OFFSET 0x4c +#define CLK_RST_CTLR_CPU_CMPLX_CPU_BRIDGE_CLKDIV_BY_4 (3 << 0) +#define CLK_RST_CTLR_CPU_CMPLX_CPU0_CLK_STP_STOP (1 << 8) +#define CLK_RST_CTLR_CPU_CMPLX_CPU0_CLK_STP_RUN (0 << 8) +#define CLK_RST_CTLR_CPU_CMPLX_CPU1_CLK_STP_STOP (1 << 9) +#define CLK_RST_CTLR_CPU_CMPLX_CPU1_CLK_STP_RUN (0 << 9) + +#define CLK_RST_CTLR_CLK_ENB_L_SET_OFFSET 0x320 +#define CLK_RST_CTLR_CLK_ENB_L_SET_CLK_ENB_CPU (1 << 0) + +#define CLK_RST_CTLR_RST_DEV_U_CLR_OFFSET 0x314 +#define CLK_RST_CTLR_RST_DEV_U_CLR_CSITE_RST_MASK (1 << 9) + +#define TIMERUS_USEC_CFG_OFFSET 0x4 +#define TIMERUS_USEC_CFG_USEC_DIVISOR_MASK 0xff + +#define CLK_RST_CTLR_PLLX_BASE_OFFSET 0xe0 +#define CLK_RST_CTLR_PLLX_MISC_OFFSET 0xe4 +#define CLK_RST_CTLR_PLLX_BASE_BYPASS_ENABLE (1 << 31) +#define CLK_RST_CTLR_PLLX_BASE_ENABLE_ENABLE (1 << 30) +#define CLK_RST_CTLR_PLLX_BASE_ENABLE_DISABLE (0 << 30) +#define CLK_RST_CTLR_PLLX_BASE_REF_DIS_REF_ENABLE (0 << 29) +#define CLK_RST_CTLR_PLLX_BASE_DIVP_SHIFT 20 +#define CLK_RST_CTLR_PLLX_BASE_DIVN_SHIFT 8 + +#define CLK_RST_CTLR_PLLX_MISC_DCCON_DEFAULT (1 << 20) +#define CLK_RST_CTLR_PLLX_MISC_CPCON_SHIFT 8 +#define CLK_RST_CTLR_PLLX_MISC_LFCON_SHIFT 4 + +#define PMC_SCRATCH3_PLLX_MISC_CPCON_SHIFT 22 +#define PMC_SCRATCH3_PLLX_MISC_CPCON_MASK 0xf +#define PMC_SCRATCH3_PLLX_MISC_LFCON_SHIFT 18 +#define PMC_SCRATCH3_PLLX_MISC_LFCON_MASK 0xf +#define PMC_SCRATCH3_PLLX_BASE_DIVP_SHIFT 15 +#define PMC_SCRATCH3_PLLX_BASE_DIVM_MASK 0x1f +#define PMC_SCRATCH3_PLLX_BASE_DIVN_SHIFT 5 +#define PMC_SCRATCH3_PLLX_BASE_DIVM_SHIFT 0 +#define PMC_SCRATCH3_PLLX_BASE_DIVN_MASK 0x3ff +#define PMC_SCRATCH3_PLLX_BASE_DIVN_SHIFT 5 +#define PMC_SCRATCH3_PLLX_BASE_DIVP_MASK 0x07 + +#define FLOW_CTLR_HALT_CPU_EVENTS_OFFSET 0x0 + +#define CLK_RST_CTLR_RST_CPU_CMPLX_CLR_OFFSET 0x344 +#define CLK_RST_CTLR_RST_CPU_CMPLX_CLR_CPURESET0 (1 << 0) +#define CLK_RST_CTLR_RST_CPU_CMPLX_CLR_CPURESET1 (1 << 1) +#define CLK_RST_CTLR_RST_CPU_CMPLX_CLR_DERESET0 (1 << 4) +#define CLK_RST_CTLR_RST_CPU_CMPLX_CLR_DERESET1 (1 << 5) +#define CLK_RST_CTLR_RST_CPU_CMPLX_CLR_DBGRESET0 (1 << 12) +#define CLK_RST_CTLR_RST_CPU_CMPLX_CLR_DBGRESET1 (1 << 13) + +#define CLK_RST_CTLR_PLLM_OUT_OFFSET 0x94 +#define CLK_RST_CTLR_PLLM_OUT1_RSTN_RESET_DISABLE (1 << 0) +#define CLK_RST_CTLR_PLLM_OUT1_CLKEN_ENABLE (1 << 1) +#define CLK_RST_CTLR_PLLM_OUT1_RATIO_VAL_8 (8 << 8) + +#define CLK_RST_CTLR_SCLK_BURST_POLICY_OFFSET 0x28 +#define CLK_RST_CTLR_SCLK_SYS_STATE_IDLE (1 << 28) +#define CLK_RST_CTLR_SCLK_SWAKE_FIQ_SOURCE_PLLM_OUT1 (7 << 12) +#define CLK_RST_CTLR_SCLK_SWAKE_IRQ_SOURCE_PLLM_OUT1 (7 << 8) +#define CLK_RST_CTLR_SCLK_SWAKE_RUN_SOURCE_PLLM_OUT1 (7 << 4) +#define CLK_RST_CTLR_SCLK_SWAKE_IDLE_SOURCE_PLLM_OUT1 (7 << 0) + +#define CLK_RST_CTLR_RST_DEV_L_CLR_OFFSET 0x304 +#define CLK_RST_CTLR_RST_DEV_L_CLR_CPU_RST (1 << 0) + +#define CLK_RST_CTLR_RST_DEVICES_L_OFFSET 0x4 +#define CLK_RST_CTLR_RST_DEVICES_L_SWR_TRIG_SYS_RST (1 << 2) + +#endif + |