From 24d4f7f8defca9c68d4a96ba5cbedf5b01ca6e53 Mon Sep 17 00:00:00 2001 From: Andrew Bresticker Date: Wed, 18 Dec 2013 22:41:34 -0800 Subject: tegra124/nyan: memory and display updates tegra124: use pll_c_out1 as sclk parent Reviewed-on: https://chromium-review.googlesource.com/180865 (cherry picked from commit 418337a5bde70df6a770222201c51bf3e8892d5f) tegra124: take LP cluster out of reset Reviewed-on: https://chromium-review.googlesource.com/180866 (cherry picked from commit 74cdc68ea9b29da9af313635787e82bacb9e23e3) tegra124: norrin: display code clean up Reviewed-on: https://chromium-review.googlesource.com/181003 (cherry picked from commit 63843ec61b3b47ffc985edcb589771591c5c9f17) tegra124: Change the display hack to use window A Reviewed-on: https://chromium-review.googlesource.com/182001 (cherry picked from commit ef245e42eb17b2eb0e8712f252353a95ee6fc01a) tegra124: norrin: Initialize frame buffer Reviewed-on: https://chromium-review.googlesource.com/182090 (cherry picked from commit b7c1d1b3c9519cbbe1615737aed4c4c0efed2167) nyan: do not enable pull-ups on SPI1 (EC) data pins Reviewed-on: https://chromium-review.googlesource.com/181063 (cherry picked from commit 2f55188501ebcae9e01b12831f152d4520c7047c) tegra124: Add source for the LP0 resume blob. Reviewed-on: https://chromium-review.googlesource.com/183152 (cherry picked from commit a00d099bf710c297320d7edff7f7c608283d1b0b) tegra124: Revise Memory Controller registers structure definition. Reviewed-on: https://chromium-review.googlesource.com/182992 (cherry picked from commit ae83564cdd1d46c8166df1a95703e8cb1060c0a1) tegra124: Add more PMC register details. Reviewed-on: https://chromium-review.googlesource.com/183231 (cherry picked from commit d62ed2c19693284f10c2a12f4295091de3ace829) tegra124: Add SDRAM configuration header file from cbootimage. Reviewed-on: https://chromium-review.googlesource.com/182613 (cherry picked from commit 193ed2a104af38f6c41a332a649ce06a3238e0a4) tegra124: Revise sdram_param.h for Coreboot. Reviewed-on: https://chromium-review.googlesource.com/182614 (cherry picked from commit 311b0568c5de627435a5b035a7a1e40ecc2672f8) tegra124: Fix EMC base address. Reviewed-on: https://chromium-review.googlesource.com/183602 (cherry picked from commit 587c8969292ccecfa29c7720bcf24c704ed4ac4e) tegra124: Add EMC registers definition. Reviewed-on: https://chromium-review.googlesource.com/183622 (cherry picked from commit 67a8e5c7e87a1cc6bf006ad806751b549ffd3d5a) tegra124: Never touch MEM(MC)/EMC clocks in ramstage. Reviewed-on: https://chromium-review.googlesource.com/183623 (cherry picked from commit 8e3bb34d4ae37feae89b4a39850b2988a334d023) tegra124: use RAM_CODE[3:2] for ram code Reviewed-on: https://chromium-review.googlesource.com/183833 (cherry picked from commit 0154239467064ffcbdb82fc4c6b629f5d0c3568d) tegra124: Allow setting PLLM (clock for SDRAM). Reviewed-on: https://chromium-review.googlesource.com/183621 (cherry picked from commit a534e5b7c61d655eedd409dbd7780a4f90d40683) tegra124: SDRAM Initialization. Reviewed-on: https://chromium-review.googlesource.com/182615 (cherry picked from commit 5a60ae93b0603ee0d4806132be0360f3b1612bce) tegra124: Get RAM_CODE for SDRAM initialization. Reviewed-on: https://chromium-review.googlesource.com/183781 (cherry picked from commit a5b7ce70525d7ffef3fac90b8eb14b3f3787f4d8) Squashed 18 nyan/tegra commits for memory and display. Change-Id: I59a781ee8dc2fd9c9085373f5a9bb7c8108b094c Signed-off-by: Isaac Christensen Reviewed-on: http://review.coreboot.org/6914 Reviewed-by: Ronald G. Minnich Tested-by: build bot (Jenkins) --- src/soc/nvidia/tegra124/lp0/Makefile | 58 +++ src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.c | 627 ++++++++++++++++++++++++ src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.ld | 73 +++ 3 files changed, 758 insertions(+) create mode 100644 src/soc/nvidia/tegra124/lp0/Makefile create mode 100644 src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.c create mode 100644 src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.ld (limited to 'src/soc/nvidia/tegra124/lp0') diff --git a/src/soc/nvidia/tegra124/lp0/Makefile b/src/soc/nvidia/tegra124/lp0/Makefile new file mode 100644 index 0000000000..57680c562f --- /dev/null +++ b/src/soc/nvidia/tegra124/lp0/Makefile @@ -0,0 +1,58 @@ +################################################################################ +## +## Copyright 2014 Google Inc. +## +## 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; version 2 of the License. +## +## 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +## +################################################################################ + +CC = $(GCC_PREFIX)gcc +NM = $(GCC_PREFIX)nm +OBJCOPY = $(GCC_PREFIX)objcopy + +OPENSSL = openssl +DD = dd +CP = cp +MV = mv +RM = rm + +SIGKEY = 00000000000000000000000000000000 + +.PHONY: all +all: tegra_lp0_resume.fw + +tegra_lp0_resume.elf: tegra_lp0_resume.ld tegra_lp0_resume.c + $(CC) -marm -march=armv4t -mno-unaligned-access -nostdlib -static \ + -Os -fpie -Wl,--build-id=none -ggdb3 -T tegra_lp0_resume.ld \ + -o $@ $(filter %.c,$+) + +tegra_lp0_resume.fw: tegra_lp0_resume.elf + @# Get rid of any files we're about to create. + $(RM) -f $@.nosig $@.sig $@.tosig + @# Convert the ELF image into a binary image. + $(OBJCOPY) -O binary $< $@.nosig + @# Extract the part of the binary which needs to be signed. + $(DD) bs=1 skip=544 if=$@.nosig of=$@.tosig + @# Calculate a signature for that part. + $(OPENSSL) dgst -mac cmac -macopt cipher:aes-128-cbc \ + -macopt hexkey:$(SIGKEY) -md5 -binary \ + $@.tosig > $@.sig + @# Inject the signature into the binary image's header. + $(DD) conv=notrunc bs=1 seek=272 count=16 if=$@.sig of=$@.nosig + @# Copy the signed binary to the target file name. + $(MV) $@.nosig $@ + +clean: + $(RM) -f tegra_lp0_resume.fw tegra_lp0_resume.fw.sig + $(RM) -f tegra_lp0_resume.fw.tosig tegra_lp0_resume.elf diff --git a/src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.c b/src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.c new file mode 100644 index 0000000000..72506c81f5 --- /dev/null +++ b/src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.c @@ -0,0 +1,627 @@ +/* + * Copyright 2014 Google Inc. + * Copyright 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + */ + +#include + +/* Function unit addresses. */ +enum { + UP_TAG_BASE = 0X60000000, + TIMER_BASE = 0X60005000, + CLK_RST_BASE = 0X60006000, + FLOW_CTLR_BASE = 0X60007000, + TEGRA_EVP_BASE = 0x6000f000, + PMC_CTLR_BASE = 0X7000e400, + MC_CTLR_BASE = 0X70019000, + SYSCTR_CTLR_BASE = 0X700f0000 +}; + + + +/* UP tag registers. */ +static uint32_t *up_tag_ptr = (void *)(UP_TAG_BASE + 0x0); +enum { + UP_TAG_AVP = 0xaaaaaaaa +}; + + + +/* Timer registers. */ +static uint32_t *timer_us_ptr = (void *)(TIMER_BASE + 0x10); + + + +/* Clock and reset controller registers. */ +static uint32_t *clk_rst_rst_devices_l_ptr = (void *)(CLK_RST_BASE + 0x4); +enum { + SWR_TRIG_SYS_RST = 0x1 << 2 +}; + +static uint32_t *clk_rst_cclk_burst_policy_ptr = (void *)(CLK_RST_BASE + 0x20); +enum { + CCLK_PLLP_BURST_POLICY = 0x20004444 +}; + +static uint32_t *clk_rst_super_cclk_div_ptr = (void *)(CLK_RST_BASE + 0x24); +enum { + SUPER_CDIV_ENB = 0x1 << 31 +}; + +static uint32_t *clk_rst_osc_ctrl_ptr = (void *)(CLK_RST_BASE + 0x50); +enum { + OSC_XOE = 0x1 << 0, + OSC_XOFS_SHIFT = 4, + OSC_XOFS_MASK = 0x3f << OSC_XOFS_SHIFT, + OSC_FREQ_SHIFT = 28, + OSC_FREQ_MASK = 0xf << OSC_FREQ_SHIFT +}; +enum { + OSC_FREQ_13 = 0, + OSC_FREQ_16P8 = 1, + OSC_FREQ_19P2 = 4, + OSC_FREQ_38P4 = 5, + OSC_FREQ_12 = 8, + OSC_FREQ_48 = 9, + OSC_FREQ_26 = 12 +}; + +static uint32_t *clk_rst_pllu_base_ptr = (void *)(CLK_RST_BASE + 0xc0); +enum { + PLLU_DIVM_SHIFT = 0, + PLLU_DIVN_SHIFT = 8, + PLLU_OVERRIDE = 0x1 << 24, + PLLU_ENABLE = 0x1 << 30, + PLLU_BYPASS = 0x1 << 31 +}; + +static uint32_t *clk_rst_pllu_misc_ptr = (void *)(CLK_RST_BASE + 0xcc); +enum { + PLLU_LFCON_SHIFT = 4, + PLLU_CPCON_SHIFT = 8, + PLLU_LOCK_ENABLE = 22 +}; + +static uint32_t *clk_rst_pllx_base_ptr = (void *)(CLK_RST_BASE + 0xe0); +enum { + PLLX_ENABLE = 0x1 << 30 +}; + +static uint32_t *clk_rst_rst_dev_u_clr_ptr = (void *)(CLK_RST_BASE + 0x314); +enum { + SWR_CSITE_RST = 0x1 << 9 +}; + +static uint32_t *clk_rst_clk_enb_l_set_ptr = (void *)(CLK_RST_BASE + 0x320); +enum { + CLK_ENB_CPU = 0x1 << 0 +}; + +static uint32_t *clk_rst_clk_out_enb_u_set_ptr = + (void *)(CLK_RST_BASE + 0x330); +enum { + CLK_ENB_CSITE = 0x1 << 9 +}; + +static uint32_t *clk_rst_cpu_softrst_ctrl2_ptr = + (void *)(CLK_RST_BASE + 0x388); +enum { + CAR2PMC_CPU_ACK_WIDTH_SHIFT = 0, + CAR2PMC_CPU_ACK_WIDTH_MASK = 0xfff << CAR2PMC_CPU_ACK_WIDTH_SHIFT +}; + +static uint32_t *clk_rst_clk_src_mselect_ptr = + (void *)(CLK_RST_BASE + 0x3b4); +enum { + MSELECT_CLK_DIV_SHIFT = 0, + MSELECT_CLK_SRC_SHIFT = 29, + MSELECT_CLK_SRC_PLLP_OUT0 = 0x0 << MSELECT_CLK_SRC_SHIFT, + MSELECT_CLK_SRC_PLLC2_OUT0 = 0x1 << MSELECT_CLK_SRC_SHIFT, + MSELECT_CLK_SRC_PLLC_OUT0 = 0x2 << MSELECT_CLK_SRC_SHIFT, + MSELECT_CLK_SRC_PLLC3_OUT0 = 0x3 << MSELECT_CLK_SRC_SHIFT +}; + +static uint32_t *clk_rst_rst_dev_v_clr_ptr = (void *)(CLK_RST_BASE + 0x434); +enum { + SWR_MSELECT_RST = 0x1 << 3 +}; + +static uint32_t *clk_rst_clk_enb_v_set_ptr = (void *)(CLK_RST_BASE + 0x440); +enum { + CLK_ENB_CPUG = 0x1 << 0, + CLK_ENB_CPULP = 0x1 << 1, + CLK_ENB_MSELECT = 0x1 << 3 +}; + +static uint32_t *clk_rst_rst_cpulp_cmplx_clr_ptr = + (void *)(CLK_RST_BASE + 0x45c); +static uint32_t *clk_rst_rst_cpug_cmplx_clr_ptr = + (void *)(CLK_RST_BASE + 0x454); +enum { + CLR_CPURESET0 = 0x1 << 0, + CLR_CPURESET1 = 0x1 << 1, + CLR_CPURESET2 = 0x1 << 2, + CLR_CPURESET3 = 0x1 << 3, + CLR_DBGRESET0 = 0x1 << 12, + CLR_DBGRESET1 = 0x1 << 13, + CLR_DBGRESET2 = 0x1 << 14, + CLR_DBGRESET3 = 0x1 << 15, + CLR_CORERESET0 = 0x1 << 16, + CLR_CORERESET1 = 0x1 << 17, + CLR_CORERESET2 = 0x1 << 18, + CLR_CORERESET3 = 0x1 << 19, + CLR_CXRESET0 = 0x1 << 20, + CLR_CXRESET1 = 0x1 << 21, + CLR_CXRESET2 = 0x1 << 22, + CLR_CXRESET3 = 0x1 << 23, + CLR_NONCPURESET = 0x1 << 29 +}; + + + +/* Reset vector. */ + +static uint32_t *evp_cpu_reset_ptr = (void *)(TEGRA_EVP_BASE + 0x100); + + + +/* Flow controller registers. */ +static uint32_t *flow_ctlr_halt_cop_events_ptr = + (void *)(FLOW_CTLR_BASE + 0x4); +enum { + EVENT_MSEC = 0x1 << 24, + EVENT_JTAG = 0x1 << 28, + FLOW_MODE_SHIFT = 29, + FLOW_MODE_STOP = 2 << FLOW_MODE_SHIFT, +}; + +static uint32_t *flow_ctlr_cluster_control_ptr = + (void *)(FLOW_CTLR_BASE + 0x2c); +enum { + FLOW_CLUSTER_ACTIVE_LP = 0x1 << 0 +}; + + + +/* Power management controller registers. */ +enum { + PARTID_CRAIL = 0, + PARTID_CELP = 12, + PARTID_CE0 = 14, + PARTID_C0NC = 15, + PARTID_C1NC = 16 +}; + +static uint32_t *pmc_ctlr_clamp_status_ptr = (void *)(PMC_CTLR_BASE + 0x2c); + +static uint32_t *pmc_ctlr_pwrgate_toggle_ptr = (void *)(PMC_CTLR_BASE + 0x30); +enum { + PWRGATE_TOGGLE_START = 0x1 << 8 +}; + +static uint32_t *pmc_ctlr_pwrgate_status_ptr = (void *)(PMC_CTLR_BASE + 0x38); + +static uint32_t *pmc_ctlr_scratch4_ptr = (void *)(PMC_CTLR_BASE + 0x60); +enum { + PMC_SCRATCH4_LP = 0x1 << 31 +}; + +static uint32_t *pmc_ctlr_cpupwrgood_timer_ptr = + (void *)(PMC_CTLR_BASE + 0xc8); + +static uint32_t *pmc_ctlr_scratch41_ptr = (void *)(PMC_CTLR_BASE + 0x140); + +static uint32_t *pmc_ctlr_osc_edpd_over_ptr = (void *)(PMC_CTLR_BASE + 0x1a4); +enum { + PMC_XOFS_SHIFT = 1, + PMC_XOFS_MASK = 0x3f << PMC_XOFS_SHIFT +}; + + + +/* Memory controller registers. */ +static uint32_t *mc_video_protect_size_mb_ptr = (void *)(MC_CTLR_BASE + 0x64c); + +static uint32_t *mc_video_protect_reg_ctrl_ptr = + (void *)(MC_CTLR_BASE + 0x650); +enum { + VIDEO_PROTECT_WRITE_ACCESS_DISABLE = 0x1 << 0, + VIDEO_PROTECT_ALLOW_TZ_WRITE_ACCESS = 0x1 << 1 +}; + + + +/* System counter registers. */ +static uint32_t *sysctr_cntcr_ptr = (void *)(SYSCTR_CTLR_BASE + 0x0); +enum { + TSC_CNTCR_ENABLE = 0x1 << 0, + TSC_CNTCR_HDBG = 0x1 << 1 +}; + +static uint32_t *sysctr_cntfid0_ptr = (void *)(SYSCTR_CTLR_BASE + 0x20); + + + +/* Utility functions. */ + +static inline void __attribute__((always_inline)) + __attribute__((noreturn)) halt(void) +{ + for (;;); +} + +inline static uint32_t read32(const void *addr) +{ + return *(volatile uint32_t *)addr; +} + +inline static void write32(uint32_t val, void *addr) +{ + *(volatile uint32_t *)addr = val; +} + +inline static void setbits32(uint32_t bits, void *addr) +{ + write32(read32(addr) | bits, addr); +} + +inline static void clrbits32(uint32_t bits, void *addr) +{ + write32(read32(addr) & ~bits, addr); +} + +static void __attribute__((noreturn)) reset(void) +{ + write32(SWR_TRIG_SYS_RST, clk_rst_rst_devices_l_ptr); + halt(); +} + +static void udelay(unsigned usecs) +{ + uint32_t start = read32(timer_us_ptr); + while (read32(timer_us_ptr) - start < usecs) + ; +} + + + +/* Accessors. */ + +static int wakeup_on_lp(void) +{ + return !!(read32(pmc_ctlr_scratch4_ptr) & PMC_SCRATCH4_LP); +} + +static uint32_t get_wakeup_vector(void) +{ + return read32(pmc_ctlr_scratch41_ptr); +} + +static unsigned get_osc_freq(void) +{ + return (read32(clk_rst_osc_ctrl_ptr) & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; +} + + + +/* Clock configuration. */ + +static void config_oscillator(void) +{ + // Read oscillator drive strength from OSC_EDPD_OVER.XOFS and copy + // to OSC_CTRL.XOFS and set XOE. + uint32_t xofs = (read32(pmc_ctlr_osc_edpd_over_ptr) & + PMC_XOFS_MASK) >> PMC_XOFS_SHIFT; + + uint32_t osc_ctrl = read32(clk_rst_osc_ctrl_ptr); + osc_ctrl &= OSC_XOFS_MASK; + osc_ctrl |= (xofs << OSC_XOFS_SHIFT); + osc_ctrl |= OSC_XOE; + write32(osc_ctrl, clk_rst_osc_ctrl_ptr); +} + +static void config_pllu(void) +{ + // Figure out what parameters to use for PLLU. + uint32_t divm, divn, cpcon, lfcon; + switch (get_osc_freq()) { + case OSC_FREQ_12: + case OSC_FREQ_48: + divm = 0x0c; + divn = 0x3c0; + cpcon = 0x0c; + lfcon = 0x02; + break; + case OSC_FREQ_16P8: + divm = 0x07; + divn = 0x190; + cpcon = 0x05; + lfcon = 0x02; + break; + case OSC_FREQ_19P2: + case OSC_FREQ_38P4: + divm = 0x04; + divn = 0xc8; + cpcon = 0x03; + lfcon = 0x02; + break; + case OSC_FREQ_26: + divm = 0x1a; + divn = 0x3c0; + cpcon = 0x0c; + lfcon = 0x02; + break; + default: + // Map anything that's not recognized to 13MHz. + divm = 0x0d; + divn = 0x3c0; + cpcon = 0x0c; + lfcon = 0x02; + } + + // Configure PLLU. + uint32_t base = PLLU_BYPASS | PLLU_OVERRIDE | + (divn << PLLU_DIVN_SHIFT) | (divm << PLLU_DIVM_SHIFT); + write32(base, clk_rst_pllu_base_ptr); + uint32_t misc = (cpcon << PLLU_CPCON_SHIFT) | + (lfcon << PLLU_LFCON_SHIFT); + write32(misc, clk_rst_pllu_misc_ptr); + + // Enable PLLU. + base &= ~PLLU_BYPASS; + base |= PLLU_ENABLE; + write32(base, clk_rst_pllu_base_ptr); + misc |= PLLU_LOCK_ENABLE; + write32(misc, clk_rst_pllu_misc_ptr); +} + +static void config_tsc(void) +{ + // Tell the TSC the oscillator frequency. + switch (get_osc_freq()) { + case OSC_FREQ_12: + write32(12000000, sysctr_cntfid0_ptr); + break; + case OSC_FREQ_48: + write32(48000000, sysctr_cntfid0_ptr); + break; + case OSC_FREQ_16P8: + write32(16800000, sysctr_cntfid0_ptr); + break; + case OSC_FREQ_19P2: + write32(19200000, sysctr_cntfid0_ptr); + break; + case OSC_FREQ_38P4: + write32(38400000, sysctr_cntfid0_ptr); + break; + case OSC_FREQ_26: + write32(26000000, sysctr_cntfid0_ptr); + break; + default: + // Default to 13MHz. + write32(13000000, sysctr_cntfid0_ptr); + break; + } + + // Enable the TSC. + setbits32(TSC_CNTCR_ENABLE | TSC_CNTCR_HDBG, sysctr_cntcr_ptr); +} + + + +/* Function unit configuration. */ + +static void config_core_sight(void) +{ + // Enable the CoreSight clock. + write32(CLK_ENB_CSITE, clk_rst_clk_out_enb_u_set_ptr); + + /* + * 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. + */ + write32(SWR_CSITE_RST, clk_rst_rst_dev_u_clr_ptr); +} + +static void config_mselect(void) +{ + // Set MSELECT clock source to PLLP with 1:4 divider. + write32((6 << MSELECT_CLK_DIV_SHIFT) | MSELECT_CLK_SRC_PLLP_OUT0, + clk_rst_clk_src_mselect_ptr); + + // Enable clock to MSELECT. + write32(CLK_ENB_MSELECT, clk_rst_clk_enb_v_set_ptr); + + udelay(2); + + // Bring MSELECT out of reset. + write32(SWR_MSELECT_RST, clk_rst_rst_dev_v_clr_ptr); +} + + + +/* Resets. */ + +static void clear_cpu_resets(void) +{ + // Take the non-cpu of the G and LP clusters out of reset. + write32(CLR_NONCPURESET, clk_rst_rst_cpulp_cmplx_clr_ptr); + write32(CLR_NONCPURESET, clk_rst_rst_cpug_cmplx_clr_ptr); + + // Clear software controlled reset of the slow cluster. + write32(CLR_CPURESET0 | CLR_DBGRESET0 | CLR_CORERESET0 | CLR_CXRESET0, + clk_rst_rst_cpulp_cmplx_clr_ptr); + + // Clear software controlled reset of the fast cluster. + write32(CLR_CPURESET0 | CLR_DBGRESET0 | CLR_CORERESET0 | CLR_CXRESET0 | + CLR_CPURESET1 | CLR_DBGRESET1 | CLR_CORERESET1 | CLR_CXRESET1 | + CLR_CPURESET2 | CLR_DBGRESET2 | CLR_CORERESET2 | CLR_CXRESET2 | + CLR_CPURESET3 | CLR_DBGRESET3 | CLR_CORERESET3 | CLR_CXRESET3, + clk_rst_rst_cpug_cmplx_clr_ptr); +} + + + +/* Power. */ + +static void power_on_partition(unsigned id) +{ + uint32_t bit = 0x1 << id; + if (!(read32(pmc_ctlr_pwrgate_status_ptr) & bit)) { + // Partition is not on. Turn it on. + write32(id | PWRGATE_TOGGLE_START, pmc_ctlr_pwrgate_toggle_ptr); + + // Wait until the partition is powerd on. + while (!(read32(pmc_ctlr_pwrgate_status_ptr) & bit)) + ; + + // Wait until clamp is off. + while (read32(pmc_ctlr_clamp_status_ptr) & bit) + ; + } +} + +static void power_on_main_cpu(void) +{ + /* + * Reprogram PMC_CPUPWRGOOD_TIMER register: + * + * XXX This is a fragile assumption. XXX + * The kernel prepares PMC_CPUPWRGOOD_TIMER based on a 32768Hz clock. + * Note that PMC_CPUPWRGOOD_TIMER is running at pclk. + * + * We need to reprogram PMC_CPUPWRGOOD_TIMER based on the current pclk + * which is at 408Mhz (pclk = sclk = pllp_out0) after reset. Multiply + * PMC_CPUPWRGOOD_TIMER by 408M / 32K. + * + * Save the original PMC_CPUPWRGOOD_TIMER register which we need to + * restore after the CPU is powered up. + */ + uint32_t orig_timer = read32(pmc_ctlr_cpupwrgood_timer_ptr); + + write32(orig_timer * (408000000 / 32768), + pmc_ctlr_cpupwrgood_timer_ptr); + + if (wakeup_on_lp()) { + power_on_partition(PARTID_C1NC); + power_on_partition(PARTID_CELP); + } else { + power_on_partition(PARTID_CRAIL); + power_on_partition(PARTID_C0NC); + power_on_partition(PARTID_CE0); + } + + // Give I/O signals time to stablize. + write32(20 | EVENT_MSEC | FLOW_MODE_STOP, + flow_ctlr_halt_cop_events_ptr); + + // Restore the original PMC_CPUPWRGOOD_TIMER. + write32(orig_timer, pmc_ctlr_cpupwrgood_timer_ptr); +} + + + +/* Entry point. */ + +void lp0_resume(void) +{ + // If not on the AVP, reset. + if (read32(up_tag_ptr) != UP_TAG_AVP) + reset(); + + config_oscillator(); + + // Tell the flow controller which cluster to wake up. The default is + // the fast cluster. + if (wakeup_on_lp()) + setbits32(FLOW_CLUSTER_ACTIVE_LP, + flow_ctlr_cluster_control_ptr); + + // Program SUPER_CCLK_DIVIDER. + write32(SUPER_CDIV_ENB, clk_rst_super_cclk_div_ptr); + + config_core_sight(); + + config_pllu(); + + // Set the CPU reset vector. + write32(get_wakeup_vector(), evp_cpu_reset_ptr); + + // Select CPU complex clock source. + write32(CCLK_PLLP_BURST_POLICY, clk_rst_cclk_burst_policy_ptr); + + config_mselect(); + + // Disable PLLX since it isn't used as CPU clock source. + clrbits32(PLLX_ENABLE, clk_rst_pllx_base_ptr); + + // Set CAR2PMC_CPU_ACK_WIDTH to 408. + uint32_t ack_width = read32(clk_rst_cpu_softrst_ctrl2_ptr); + ack_width &= ~CAR2PMC_CPU_ACK_WIDTH_MASK; + ack_width |= 408 << CAR2PMC_CPU_ACK_WIDTH_SHIFT; + write32(ack_width, clk_rst_cpu_softrst_ctrl2_ptr); + + // Enable the CPU complex clock. + write32(CLK_ENB_CPU, clk_rst_clk_enb_l_set_ptr); + write32(CLK_ENB_CPUG | CLK_ENB_CPULP, clk_rst_clk_enb_v_set_ptr); + + clear_cpu_resets(); + + config_tsc(); + + power_on_main_cpu(); + + // Disable VPR. + write32(0, mc_video_protect_size_mb_ptr); + write32(VIDEO_PROTECT_WRITE_ACCESS_DISABLE, + mc_video_protect_reg_ctrl_ptr); + + // Halt the AVP. + while (1) + write32(FLOW_MODE_STOP | EVENT_JTAG, + flow_ctlr_halt_cop_events_ptr); +} + + + +/* Header. */ + +extern uint8_t blob_data; +extern uint8_t blob_data_size; +extern uint8_t blob_total_size; + +struct lp0_header { + uint32_t length_insecure; // Insecure total length. + uint32_t reserved[3]; + uint8_t rsa_modulus[256]; // RSA key modulus. + uint8_t aes_signature[16]; // AES signature. + uint8_t rsa_signature[256]; // RSA-PSS signature. + uint8_t random_aes_block[16]; // Random data, may be zero. + uint32_t length_secure; // Secure total length. + uint32_t destination; // Where to load the blob in iRAM. + uint32_t entry_point; // Entry point for the blob. + uint32_t code_length; // Length of just the data. +} __attribute__((packed)); + +struct lp0_header header __attribute__((section(".header"))) = +{ + .length_insecure = (uintptr_t)&blob_total_size, + .length_secure = (uintptr_t)&blob_total_size, + .destination = (uintptr_t)&blob_data, + .entry_point = (uintptr_t)&lp0_resume, + .code_length = (uintptr_t)&blob_data_size +}; diff --git a/src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.ld b/src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.ld new file mode 100644 index 0000000000..c4c508ff4c --- /dev/null +++ b/src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.ld @@ -0,0 +1,73 @@ +/* + * Copyright 2014 Google Inc. + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* We use ELF as output format. So that we can debug the code in some form. */ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +PHDRS +{ + to_load PT_LOAD; +} + +ENTRY(lp0_resume) +SECTIONS +{ + . = 0x40020000 - 0x240; + + /* + * The lp0 blob header is built as a static data structure and put + * in the .header section. + */ + .header_start = .; + .header . : { + *(.header); + } : to_load = 0xff + .header_end = .; + + . = 0x40020000; + + /* The actual lp0 blob code. */ + .data_start = .; + .data . : { + *(.text); + *(.text.*); + *(.rodata); + *(.rodata.*); + *(.data); + *(.data.*); + *(.bss); + *(.bss.*); + *(.sbss); + *(.sbss.*); + . = ALIGN(16); + } + .data_end = .; + + /* Some values we need in the header. */ + blob_data = .data_start; + blob_data_size = .data_end - .data_start; + blob_total_size = .data_end - .header_start; + + /DISCARD/ : { + *(.comment) + *(.note) + *(.comment.*) + *(.note.*) + *(.ARM.*) + } +} -- cgit v1.2.3