tegra132: Add tegra_lp0_resume code
BUG=chrome-os-partner:32015 BRANCH=None TEST=successfully suspend/resume on Rush/Ryu Signed-off-by: Yen Lin <yelin@nvidia.com> Change-Id: I279e42fd055805f0060951d272571bda66514ea6 Signed-off-by: Patrick Georgi <pgeorgi@chromium.org> Original-Commit-Id: a02452e431d9aa6245fb2421773d66fc416d0a6e Original-Change-Id: I11cca0a8f5e7a36c1fff690c8070c74706348949 Original-Reviewed-on: https://chromium-review.googlesource.com/214580 Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org> Original-Commit-Queue: Yen Lin <yelin@nvidia.com> Original-Tested-by: Yen Lin <yelin@nvidia.com> Reviewed-on: http://review.coreboot.org/9102 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
d3b40bfc2d
commit
d126a749b8
|
@ -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
|
|
@ -0,0 +1,656 @@
|
|||
/*
|
||||
* 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,
|
||||
APB_MISC_BASE = 0x70000000,
|
||||
PMC_CTLR_BASE = 0X7000e400,
|
||||
MC_CTLR_BASE = 0X70019000,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* UP tag registers. */
|
||||
static uint32_t *up_tag_ptr = (void *)(UP_TAG_BASE + 0x0);
|
||||
enum {
|
||||
UP_TAG_AVP = 0xaaaaaaaa
|
||||
};
|
||||
|
||||
|
||||
/* APB Misc JTAG Configuration Register */
|
||||
static uint32_t *misc_pp_config_ctl_ptr = (void *)(APB_MISC_BASE + 0x24);
|
||||
enum {
|
||||
PP_CONFIG_CTL_JTAG = 0x1 << 6
|
||||
};
|
||||
|
||||
|
||||
/* 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_enb_v_set_ptr = (void *)(CLK_RST_BASE + 0x440);
|
||||
enum {
|
||||
CLK_ENB_CPUG = 0x1 << 0,
|
||||
CLK_ENB_CPULP = 0x1 << 1,
|
||||
};
|
||||
|
||||
static uint32_t *clk_rst_rst_cpulp_cmplx_set_ptr =
|
||||
(void *)(CLK_RST_BASE + 0x450);
|
||||
enum {
|
||||
SET_CXRESET0 = 0x1 << 20,
|
||||
SET_CXRESET1 = 0x1 << 21
|
||||
};
|
||||
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_L2RESET = 0x1 << 24,
|
||||
CLR_NONCPURESET = 0x1 << 29,
|
||||
CLR_PRESETDBG = 0x1 << 30
|
||||
};
|
||||
|
||||
|
||||
/* 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
|
||||
};
|
||||
|
||||
static uint32_t *flow_ctlr_ram_repair_ptr =
|
||||
(void *)(FLOW_CTLR_BASE + 0x40);
|
||||
static uint32_t *flow_ctlr_ram_repair_cluster1_ptr =
|
||||
(void *)(FLOW_CTLR_BASE + 0x58);
|
||||
enum {
|
||||
RAM_REPAIR_REQ = 0x1 << 0,
|
||||
RAM_REPAIR_STS = 0x1 << 1,
|
||||
};
|
||||
|
||||
|
||||
/* Power management controller registers. */
|
||||
enum {
|
||||
PARTID_CRAIL = 0,
|
||||
PARTID_CE0 = 14,
|
||||
PARTID_C0NC = 15,
|
||||
};
|
||||
|
||||
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_cpupwrgood_timer_ptr =
|
||||
(void *)(PMC_CTLR_BASE + 0xc8);
|
||||
|
||||
static uint32_t *pmc_odmdata_ptr = (void *)(PMC_CTLR_BASE + 0xa0);
|
||||
|
||||
static uint32_t *pmc_ctlr_scratch41_ptr = (void *)(PMC_CTLR_BASE + 0x140);
|
||||
static uint32_t *pmc_ctlr_secure_scratch34_ptr = (void *)(PMC_CTLR_BASE + 0x368);
|
||||
static uint32_t *pmc_ctlr_secure_scratch35_ptr = (void *)(PMC_CTLR_BASE + 0x36c);
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
/* 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)
|
||||
;
|
||||
}
|
||||
|
||||
/* UART related defines */
|
||||
static uint32_t *uart_clk_out_enb_regs[4] = {
|
||||
(uint32_t *)0x60006010,
|
||||
(uint32_t *)0x60006010,
|
||||
(uint32_t *)0x60006014,
|
||||
(uint32_t *)0x60006018
|
||||
};
|
||||
|
||||
static uint32_t *uart_rst_devices_regs[4] = {
|
||||
(uint32_t *)0x60006004,
|
||||
(uint32_t *)0x60006004,
|
||||
(uint32_t *)0x60006008,
|
||||
(uint32_t *)0x6000600c
|
||||
};
|
||||
|
||||
static uint32_t uart_enable_mask[4] = {
|
||||
1 << 6,
|
||||
1 << 7,
|
||||
1 << 23,
|
||||
1 << 1
|
||||
};
|
||||
|
||||
static uint32_t *uart_clk_source_regs[4] = {
|
||||
(uint32_t *)0x60006178,
|
||||
(uint32_t *)0x6000617c,
|
||||
(uint32_t *)0x600061a0,
|
||||
(uint32_t *)0x600061c0,
|
||||
};
|
||||
|
||||
static void enable_uart(void)
|
||||
{
|
||||
uint32_t *uart_clk_enb_reg;
|
||||
uint32_t *uart_rst_reg;
|
||||
uint32_t *uart_clk_source;
|
||||
uint32_t uart_port;
|
||||
uint32_t uart_mask;
|
||||
|
||||
/*
|
||||
* Read odmdata (stored in pmc->odmdata) to determine debug uart port.
|
||||
*
|
||||
* Bits 15-17 of odmdata contains debug uart port.
|
||||
* 0 : UARTA
|
||||
* 1 : UARTB
|
||||
* 2 : UARTC
|
||||
* 3 : UARTD
|
||||
*/
|
||||
uart_port = (read32(pmc_odmdata_ptr) >> 15) & 0x7;
|
||||
|
||||
/* Default to UARTA, since pmc_odmdata is not programmed yet. */
|
||||
/* TODO: if (uart_port >= 4) */
|
||||
uart_port = 0;
|
||||
|
||||
uart_clk_enb_reg = uart_clk_out_enb_regs[uart_port];
|
||||
uart_rst_reg = uart_rst_devices_regs[uart_port];
|
||||
uart_mask = uart_enable_mask[uart_port];
|
||||
uart_clk_source = uart_clk_source_regs[uart_port];
|
||||
|
||||
/* Enable UART clock */
|
||||
setbits32(uart_mask, uart_clk_enb_reg);
|
||||
|
||||
/* Reset and unreset UART */
|
||||
setbits32(uart_mask, uart_rst_reg);
|
||||
clrbits32(uart_mask, uart_rst_reg);
|
||||
|
||||
/* Program UART clock source: PLLP (408000000) */
|
||||
write32(0, uart_clk_source);
|
||||
}
|
||||
|
||||
/* Accessors. */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/* Jtag configuration. */
|
||||
|
||||
static void enable_jtag(void)
|
||||
{
|
||||
write32(PP_CONFIG_CTL_JTAG, misc_pp_config_ctl_ptr);
|
||||
}
|
||||
|
||||
/* 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 enable_cpu_clocks(void)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
||||
/* Resets. */
|
||||
|
||||
static void clear_cpu_resets(void)
|
||||
{
|
||||
/* Hold CPU1 in reset */
|
||||
setbits32(SET_CXRESET1, clk_rst_rst_cpulp_cmplx_set_ptr);
|
||||
|
||||
write32(CLR_NONCPURESET | CLR_L2RESET | CLR_PRESETDBG,
|
||||
clk_rst_rst_cpug_cmplx_clr_ptr);
|
||||
|
||||
write32(CLR_CPURESET0 | CLR_DBGRESET0 | CLR_CORERESET0 | CLR_CXRESET0,
|
||||
clk_rst_rst_cpug_cmplx_clr_ptr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* RAM repair */
|
||||
|
||||
void ram_repair(void)
|
||||
{
|
||||
// Request Cluster0 RAM repair.
|
||||
setbits32(RAM_REPAIR_REQ, flow_ctlr_ram_repair_ptr);
|
||||
// Poll for Cluster0 RAM repair status.
|
||||
while (!(read32(flow_ctlr_ram_repair_ptr) & RAM_REPAIR_STS))
|
||||
;
|
||||
|
||||
// Request Cluster1 RAM repair.
|
||||
setbits32(RAM_REPAIR_REQ, flow_ctlr_ram_repair_cluster1_ptr);
|
||||
// Poll for Cluster1 RAM repair status.
|
||||
while (!(read32(flow_ctlr_ram_repair_cluster1_ptr) & RAM_REPAIR_STS))
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
/* 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 204Mhz (pclk = sclk = pllp_out2) after BootROM. Multiply
|
||||
* PMC_CPUPWRGOOD_TIMER by 204M / 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 * (204000000 / 32768),
|
||||
pmc_ctlr_cpupwrgood_timer_ptr);
|
||||
|
||||
power_on_partition(PARTID_CRAIL);
|
||||
power_on_partition(PARTID_C0NC);
|
||||
power_on_partition(PARTID_CE0);
|
||||
|
||||
// Restore the original PMC_CPUPWRGOOD_TIMER.
|
||||
write32(orig_timer, pmc_ctlr_cpupwrgood_timer_ptr);
|
||||
}
|
||||
|
||||
|
||||
static void aarch64_trampoline(void)
|
||||
{
|
||||
uint32_t val = 3; /* bit1: to warm reset; bit0: AARCH64*/
|
||||
|
||||
asm volatile ("mcr p15, 0, %0, c12, c0, 2" : : "r" (val));
|
||||
|
||||
/* unreachable */
|
||||
halt();
|
||||
}
|
||||
|
||||
|
||||
/* Entry point. */
|
||||
|
||||
void lp0_resume(void)
|
||||
{
|
||||
// If not on the AVP, reset.
|
||||
if (read32(up_tag_ptr) != UP_TAG_AVP)
|
||||
reset();
|
||||
|
||||
// Enable JTAG
|
||||
enable_jtag();
|
||||
|
||||
config_oscillator();
|
||||
|
||||
// Program SUPER_CCLK_DIVIDER.
|
||||
write32(SUPER_CDIV_ENB, clk_rst_super_cclk_div_ptr);
|
||||
|
||||
config_core_sight();
|
||||
|
||||
enable_uart();
|
||||
|
||||
config_pllu();
|
||||
|
||||
/*
|
||||
* Set the CPU reset vector.
|
||||
*
|
||||
* T132 always resets to AARCH32 and SW needs to write RMR_EL3
|
||||
* to bootstrap into AARCH64.
|
||||
*/
|
||||
write32(get_wakeup_vector(), pmc_ctlr_secure_scratch34_ptr);
|
||||
write32(0, pmc_ctlr_secure_scratch35_ptr);
|
||||
write32((uint32_t)aarch64_trampoline, evp_cpu_reset_ptr);
|
||||
|
||||
// Select CPU complex clock source.
|
||||
write32(CCLK_PLLP_BURST_POLICY, clk_rst_cclk_burst_policy_ptr);
|
||||
|
||||
// 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);
|
||||
|
||||
// Disable VPR.
|
||||
write32(0, mc_video_protect_size_mb_ptr);
|
||||
write32(VIDEO_PROTECT_WRITE_ACCESS_DISABLE,
|
||||
mc_video_protect_reg_ctrl_ptr);
|
||||
|
||||
enable_cpu_clocks();
|
||||
|
||||
power_on_main_cpu();
|
||||
|
||||
// Perform ram repair after cpu is powered on.
|
||||
ram_repair();
|
||||
|
||||
clear_cpu_resets();
|
||||
|
||||
// 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
|
||||
};
|
|
@ -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.*)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue