summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv7/tegra3/warmboot_avp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/cpu/armv7/tegra3/warmboot_avp.c')
-rw-r--r--arch/arm/cpu/armv7/tegra3/warmboot_avp.c410
1 files changed, 410 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv7/tegra3/warmboot_avp.c b/arch/arm/cpu/armv7/tegra3/warmboot_avp.c
new file mode 100644
index 0000000000..02772d42c1
--- /dev/null
+++ b/arch/arm/cpu/armv7/tegra3/warmboot_avp.c
@@ -0,0 +1,410 @@
+/*
+ * (C) Copyright 2010 - 2012
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <asm/io.h>
+
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch-tegra/pmc.h>
+#include <asm/arch-tegra/flow.h>
+#include <asm/arch-tegra/ap20.h>
+#include <asm/arch-tegra/power.h>
+#include <asm/arch/ahb.h>
+#include <asm/arch-tegra/warmboot.h>
+#include <asm/arch-tegra/warmboot_avp.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/tegra.h>
+#include <asm/arch/sdmmc.h>
+
+#define RESET_CORESIGHT
+
+void wb_start(void)
+{
+ struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+ struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+ struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
+ struct sdmmc_ctlr *sdmmc4 = (struct sdmmc_ctlr *)NV_PA_SDMMC4_BASE;
+ struct clk_rst_ctlr *clkrst =
+ (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+ struct ahb_ctlr *ahb = (struct ahb_ctlr *)TEGRA2_AHB_BASE;
+ struct clk_pll *pllp = &clkrst->crc_pll[CLOCK_ID_PERIPH];
+ u32 reg, saved_reg;
+ u32 divm, divn;
+ u32 cpcon, lfcon;
+ u32 base, misc;
+ u32 status_mask, toggle, clamp;
+ u32 internal_periph_id;
+
+ /* enable JTAG & TBE */
+ writel(CONFIG_CTL_TBE | CONFIG_CTL_JTAG, &pmt->pmt_cfg_ctl);
+
+ /* Are we running where we're supposed to be? */
+ asm volatile (
+ "adr %0, wb_start;" /* reg: wb_start address */
+ : "=r"(reg) /* output */
+ /* no input, no clobber list */
+ );
+
+ if (reg != AP20_WB_RUN_ADDRESS)
+ goto do_reset;
+
+ /* Are we running with AVP? */
+ if (readl(NV_PA_PG_UP_BASE + PG_UP_TAG_0) != PG_UP_TAG_AVP)
+ goto do_reset;
+
+ /*
+ * Save the current setting for CLK_BURST_POLICY and change clock
+ * source to CLKM
+ */
+ saved_reg = readl(&clkrst->crc_sclk_brst_pol);
+
+ reg = SCLK_SWAKE_FIQ_SRC_CLKM | SCLK_SWAKE_IRQ_SRC_CLKM |
+ SCLK_SWAKE_RUN_SRC_CLKM | SCLK_SWAKE_IDLE_SRC_CLKM |
+ SCLK_SYS_STATE_RUN;
+ writel(reg, &clkrst->crc_sclk_brst_pol);
+
+ /* Update PLLP output dividers for 408 MHz operation */
+ /* Set OUT1 to 9.6MHz and OUT2 to 48MHz (based on 408MHz of PLLP) */
+ writel(PLLP_408_OUTA, &pllp->pll_out);
+
+ writel(PLLP_408_OUTB, &pllp->pll_out_b);
+
+ /* Set oscillator the drive strength */
+ reg = readl(&clkrst->crc_osc_ctrl);
+ reg &= ~(OSC_CTRL_XOE | OSC_CTRL_XOFS);
+ reg |= OSC_CTRL_XOE_ENABLE | OSC_CTRL_XOFS_4;
+ writel(reg, &clkrst->crc_osc_ctrl);
+
+ /* Set CPCON and enable PLLP lock bit */
+ writel(PLLP_MISC_PLLP_CPCON_8 | PLLP_MISC_PLLP_LOCK_ENABLE,
+ &pllp->pll_misc);
+
+ /* Find out the current osc frequency */
+ reg = readl(&clkrst->crc_osc_ctrl);
+
+ /* Find out the PLLP_BASE value to use */
+
+ /*
+ * Note: can not use switch statement: compiler builds tables on the
+ * local stack. We don't want to use anything on the stack.
+ */
+ reg >>= OSC_CTRL_OSC_FREQ_SHIFT;
+ if ((reg == OSC_FREQ_OSC12) || (reg == OSC_FREQ_OSC48)) {
+ divm = 0x0c;
+ divn = 0x198;
+ } else if (reg == OSC_FREQ_OSC16P8) {
+ divm = 0x0e;
+ divn = 0x154;
+ } else if ((reg == OSC_FREQ_OSC19P2) || (reg == OSC_FREQ_OSC38P4)) {
+ divm = 0x10;
+ divn = 0x154;
+ } else if (reg == OSC_FREQ_OSC26) {
+ divm = 0x1a;
+ divn = 0x198;
+ } else {
+ /*
+ * Unused code in OSC_FREQ is mapped to 13MHz - use 13MHz as
+ * default settings.
+ */
+ divm = 0x0d;
+ divn = 0x198;
+ }
+
+ /* Change PLLP to be 408MHz */
+ reg = (divm << PLLP_BASE_PLLP_DIVM_SHIFT) |
+ (divn << PLLP_BASE_PLLP_DIVN_SHIFT) |
+ PLLP_BASE_OVRRIDE_ENABLE |
+ PLLP_BASE_PLLP_ENABLE;
+ writel(reg, &pllp->pll_base);
+
+ /* Wait till PLLP locks */
+ while (1) {
+ reg = readl(&pllp->pll_base);
+ if (reg & PLLP_BASE_PLLP_LOCK_LOCK)
+ break;
+ }
+
+ /*
+ * Wait for 250uS after lock bit is set to make sure pll is stable.
+ * The typical wait time is 300uS. Since we already check the lock
+ * bit, reduce the wait time to 250uS.
+ */
+ reg = EVENT_ZERO_VAL_250 | EVENT_USEC | EVENT_MODE_STOP;
+ writel(reg, &flow->halt_cop_events);
+
+ /* Restore setting for SCLK_BURST_POLICY */
+ writel(saved_reg, &clkrst->crc_sclk_brst_pol);
+
+ /*
+ * Enable the PPSB_STOPCLK feature to allow SCLK to be run at
+ * higher frequencies. See bug 811773.
+ */
+ reg = readl(&clkrst->crc_misc_clk_enb);
+ reg |= MISC_CLK_ENB_EN_PPSB_STOPCLK_ENABLE;
+ writel(reg, &clkrst->crc_misc_clk_enb);
+
+ reg = readl(&ahb->arbitration_xbar_ctrl);
+ reg |= ARBITRATION_XBAR_CTRL_PPSB_ENABLE_ENABLE;
+ writel(reg, &ahb->arbitration_xbar_ctrl);
+
+#ifdef RESET_CORESIGHT
+ /* Assert CoreSight reset */
+ writel(SWR_CSITE_RST, &clkrst->crc_rst_dev_ex[TEGRA_DEV_U].set);
+#endif
+
+ /*
+ * Halt the G complex CPUs at the flow controller in case the G
+ * complex was running in a uni-processor configuration.
+ */
+ writel(EVENT_MODE_STOP, &flow->halt_cpu_events);
+ writel(EVENT_MODE_STOP, &flow->halt_cpu1_events);
+ writel(EVENT_MODE_STOP, &flow->halt_cpu2_events);
+ writel(EVENT_MODE_STOP, &flow->halt_cpu3_events);
+
+ /*
+ * Find out which CPU (LP or G) to wake up. The default setting
+ * in flow controller is to wake up GCPU.
+ *
+ * Select the LP CPU cluster. All accesses to the cluster-dependent
+ * CPU registers (legacy clock enables, resets, burst policy, flow
+ * controller) now refer to the LP CPU.
+ */
+ reg = readl(&pmc->pmc_scratch4);
+ reg &= CPU_WAKEUP_CLUSTER;
+ if (reg)
+ setbits_le32(&flow->cluster_control, ACTIVE_LP);
+
+ /* Hold all CPUs in reset. */
+ reg = CPU_CMPLX_CPURESET0 | CPU_CMPLX_CPURESET1 |
+ CPU_CMPLX_CPURESET2 | CPU_CMPLX_CPURESET3 |
+ CPU_CMPLX_DERESET0 | CPU_CMPLX_DERESET1 |
+ CPU_CMPLX_DERESET2 | CPU_CMPLX_DERESET3 |
+ CPU_CMPLX_DBGRESET0 | CPU_CMPLX_DBGRESET1 |
+ CPU_CMPLX_DBGRESET2 | CPU_CMPLX_DBGRESET3;
+ writel(reg, &clkrst->crc_cpu_cmplx_set);
+
+ /* Assert CPU complex reset. */
+ reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_L]);
+ reg |= CPU_RST;
+ writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_L]);
+
+ /* Program SUPER_CCLK_DIVIDER */
+ writel(SUPER_CDIV_ENB, &clkrst->crc_super_cclk_div);
+
+ /* Stop the clock to all CPUs. */
+ reg = SET_CPU0_CLK_STP | SET_CPU1_CLK_STP | SET_CPU2_CLK_STP |
+ SET_CPU3_CLK_STP;
+ writel(reg, &clkrst->crc_clk_cpu_cmplx_set);
+
+ /* Make sure the resets are held for at least 2 microseconds. */
+ reg = readl(TIMER_USEC_CNTR);
+ while ((int)(readl(TIMER_USEC_CNTR) - reg) < 3)
+ {}
+
+ writel(CLK_ENB_CSITE, &clkrst->crc_clk_enb_ex[TEGRA_DEV_U].set);
+
+ /* De-assert CoreSight reset */
+ writel(SWR_CSITE_RST, &clkrst->crc_rst_dev_ex[TEGRA_DEV_U].clr);
+
+ /* Unlock debugger access. */
+ writel(0xC5ACCE55, CSITE_CPU_DBG0_LAR);
+
+ /* Find out the current osc frequency */
+ reg = readl(&clkrst->crc_osc_ctrl);
+ reg >>= OSC_CTRL_OSC_FREQ_SHIFT;
+ if ((reg == OSC_FREQ_OSC12) || (reg == OSC_FREQ_OSC48)) {
+ divm = 0x0c;
+ divn = 0x3c0;
+ cpcon = 0x0c;
+ lfcon = 0x01;
+ } else if (reg == OSC_FREQ_OSC16P8) {
+ divm = 0x07;
+ divn = 0x190;
+ cpcon = 0x05;
+ lfcon = 0x00;
+ } else if ((reg == OSC_FREQ_OSC19P2) || (reg == OSC_FREQ_OSC38P4)) {
+ divm = 0x04;
+ divn = 0xc8;
+ cpcon = 0x03;
+ lfcon = 0x00;
+ } else if (reg == OSC_FREQ_OSC26) {
+ divm = 0x1a;
+ divn = 0x3c0;
+ cpcon = 0x0c;
+ lfcon = 0x01;
+ } else {
+ /*
+ * Unused code in OSC_FREQ is mapped to 13MHz - use 13MHz as
+ * default settings.
+ */
+ divm = 0x0d;
+ divn = 0x3c0;
+ cpcon = 0x0c;
+ lfcon = 0x01;
+ }
+
+ base = PLLU_BYPASS_ENABLE | (divn << 8) | (divm << 0);
+ writel(base, &clkrst->crc_pll[CLOCK_ID_USB].pll_base);
+ misc = (cpcon << 8) | (lfcon << 4);
+ writel(misc, &clkrst->crc_pll[CLOCK_ID_USB].pll_misc);
+
+ base &= ~PLLU_BYPASS_ENABLE;
+ base |= PLLU_ENABLE;
+ writel(base, &clkrst->crc_pll[CLOCK_ID_USB].pll_base);
+ misc |= PLLU_LOCK_ENABLE;
+ writel(misc, &clkrst->crc_pll[CLOCK_ID_USB].pll_misc);
+
+ /*
+ * Reenable eMMC boot block write protection
+ * 1. Enable SDMMC4 clock (it's OK to leave it on the default clock
+ * source, CLK_M, since only one register will be written).
+ * 2. Take SDMMC4 controller out of reset.
+ * 3. Set SDMMC4_VENDOR_CLOCK_CNTRL_0_HW_RSTN_OVERRIDE.
+ * 4. Restore SDMMC4 reset state.
+ * 5. Stop the clock to SDMMC4 controller.
+ */
+ writel(SET_CLK_ENB_SDMMC4, &clkrst->crc_clk_enb_ex[TEGRA_DEV_L].set);
+
+ saved_reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_L]);
+ reg = CLR_SDMMC4_RST_ENABLE;
+ writel(reg, &clkrst->crc_rst_dev_ex[TEGRA_DEV_L].clr);
+
+ reg = readl(sdmmc4->vendor_clk_cntrl);
+ reg |= HW_RSTN_OVERRIDE_OVERRIDE;
+ writel(reg, &sdmmc4->vendor_clk_cntrl);
+
+ writel(saved_reg, &clkrst->crc_rst_dev[TEGRA_DEV_L]);
+
+ writel(CLR_CLK_ENB_SDMMC4_ENABLE,
+ &clkrst->crc_clk_enb_ex[TEGRA_DEV_L].clr);
+
+ /*
+ * Set the CPU reset vector. SCRATCH41 contains the physical
+ * address of the CPU-side restoration code.
+ */
+ reg = readl(&pmc->pmc_scratch41);
+ writel(reg, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+ /* Select CPU complex clock source */
+ writel(CCLK_PLLP_BURST_POLICY, &clkrst->crc_cclk_brst_pol);
+
+ /* Enable CPU0 clock */
+ reg = CPU_CMPLX_CLR_CPU0_CLK_STP;
+ writel(reg, &clkrst->crc_clk_cpu_cmplx_clr);
+
+ /* Enable the CPU complex clock */
+ writel(CLK_ENB_CPU, &clkrst->crc_clk_enb_ex[TEGRA_DEV_L].set);
+
+ /* Set MSELECT clock source to PLL_P */
+ reg = MSELECT_CLK_SRC_PLLP_OUT0;
+ internal_periph_id = 1;
+ writel(reg, &clkrst->crc_clk_src_vw[internal_periph_id]);
+
+ /* Enable clock to MSELECT */
+ writel(SET_CLK_ENB_MSELECT,
+ &clkrst->crc_clk_enb_ex_vw[TEGRA_DEV_V].set);
+
+ /* Bring MSELECT out of reset */
+ writel(SET_MSELECT_RST_ENABLE,
+ &clkrst->crc_rst_dev_ex_vw[TEGRA_DEV_V].clr);
+
+ /*
+ * Find out which CPU (LP or G) to power on
+ * Power up the CPU0 partition if necessary.
+ * status_mask: bit mask for CPU enable in APBDEV_PMC_PWRGATE_STATUS_0
+ * toggle: value to power on cpu
+ * clamp: value to remove clamping to CPU
+ */
+ reg = readl(&pmc->pmc_scratch4);
+ if (reg & CPU_WAKEUP_CLUSTER) {
+ /*
+ * Setup registers for powering up LPCPU
+ * - PWRGATE_STATUS, A9LP
+ * - PWRGATE_TOGGLE, PARTID, A9LP , START, ENABLE
+ * - REMOVE_CLAMPING_CMD, A9LP, ENABLE
+ */
+ status_mask = PWRGATE_STATUS_A9LP_ENABLE;
+ toggle = PWRGATE_TOGGLE_START | PWRGATE_TOGGLE_PARTID_A9LP;
+ clamp = REMOVE_CLAMPING_CMD_A9LP_ENABLE;
+ } else {
+ /*
+ * Setup registers for powering up GCPU
+ * - PWRGATE_STATUS, CPU
+ * - PWRGATE_TOGGLE, PARTID, CP , START, ENABLE
+ * - REMOVE_CLAMPING_CMD, CPU, ENABLE
+ */
+ status_mask = PWRGATE_STATUS_CPU_ENABLE;
+ toggle = PWRGATE_TOGGLE_START | PWRGATE_TOGGLE_PARTID_CP;
+ clamp = REMOVE_CLAMPING_CMD_CPU_ENABLE;
+ }
+
+ reg = readl(&pmc->pmc_pwrgate_status);
+ if (!(reg & status_mask))
+ writel(toggle, &pmc->pmc_pwrgate_toggle);
+
+ while (!(readl(&pmc->pmc_pwrgate_status) & status_mask))
+ {}
+
+ /* Remove the I/O clamps from the CPU0 power partition. */
+ writel(clamp, &pmc->pmc_remove_clamping);
+
+ /* Give I/O signals 20ms to stabilize. */
+ writel(EVENT_ZERO_VAL_20 | EVENT_MSEC | EVENT_MODE_STOP,
+ &flow->halt_cop_events);
+
+ /* Take CPU0 out of reset. */
+ writel(CPU_CMPLX_CPURESET0 | CPU_CMPLX_DERESET0 | CPU_CMPLX_DBGRESET0,
+ &clkrst->crc_cpu_cmplx_clr);
+
+ /* De-assert CPU complex reset. */
+ writel(CPU_RST, &clkrst->crc_rst_dev_ex[TEGRA_DEV_L].clr);
+
+ /* Unhalt the CPU at the flow controller. */
+ writel(0, &flow->halt_cpu_events);
+
+ /* avp_resume: no return after the write */
+ reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_L]);
+ reg &= ~CPU_RST;
+ writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_L]);
+
+ /* avp_halt: */
+ while (1)
+ writel(EVENT_MODE_STOP | EVENT_JTAG, &flow->halt_cop_events);
+
+do_reset:
+ /*
+ * Execution comes here if something goes wrong. The chip is reset and
+ * a cold boot is performed.
+ */
+ while (1)
+ writel(SWR_TRIG_SYS_RST, &clkrst->crc_rst_dev[TEGRA_DEV_L]);
+}
+
+/*
+ * wb_end() is a dummy function, and must be directly following wb_start(),
+ * and is used to calculate the size of wb_start().
+ */
+void wb_end(void)
+{
+} \ No newline at end of file