summaryrefslogtreecommitdiff
path: root/src/soc/nvidia/tegra124/lp0
diff options
context:
space:
mode:
authorAndrew Bresticker <abrestic@chromium.org>2013-12-18 22:41:34 -0800
committerIsaac Christensen <isaac.christensen@se-eng.com>2014-09-22 19:00:19 +0200
commit24d4f7f8defca9c68d4a96ba5cbedf5b01ca6e53 (patch)
treea38ef152b5381a72d68cef5e1860daffd1198227 /src/soc/nvidia/tegra124/lp0
parentd65e214d666269d0bd20d88ba2bc83349810c668 (diff)
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 <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6914 Reviewed-by: Ronald G. Minnich <rminnich@gmail.com> Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/soc/nvidia/tegra124/lp0')
-rw-r--r--src/soc/nvidia/tegra124/lp0/Makefile58
-rw-r--r--src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.c627
-rw-r--r--src/soc/nvidia/tegra124/lp0/tegra_lp0_resume.ld73
3 files changed, 758 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+
+/* 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.*)
+ }
+}