summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv7/tegra-common/ap20.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/cpu/armv7/tegra-common/ap20.c')
-rw-r--r--arch/arm/cpu/armv7/tegra-common/ap20.c124
1 files changed, 117 insertions, 7 deletions
diff --git a/arch/arm/cpu/armv7/tegra-common/ap20.c b/arch/arm/cpu/armv7/tegra-common/ap20.c
index 3a7e402b0c..a3e7858e63 100644
--- a/arch/arm/cpu/armv7/tegra-common/ap20.c
+++ b/arch/arm/cpu/armv7/tegra-common/ap20.c
@@ -21,15 +21,17 @@
* MA 02111-1307 USA
*/
+#include <common.h>
#include <asm/io.h>
#include <asm/arch/tegra.h>
#include <asm/arch-tegra/bitfield.h>
#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch-tegra/flow.h>
#include <asm/arch/clock.h>
#include <asm/arch-tegra/pmc.h>
#include <asm/arch/pinmux.h>
#include <asm/arch-tegra/scu.h>
-#include <common.h>
+#include <asm/arch-tegra/i2c.h>
#include <asm/arch-tegra/warmboot.h>
#include <asm/arch-tegra/ap20.h>
#include "../../../../../board/nvidia/common/board.h"
@@ -58,10 +60,24 @@ static struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_COUNT]
/* T25: 1.2 GHz */
{{ 923, 10, 0, 12},
- { 750, 12, 0, 8},
+ { 750, 12, 0, 8},
{ 600, 6, 0, 12},
{ 600, 13, 0, 12},
},
+
+ /* T30: 1.5 GHz with slower PLLP */
+ {{ 0xd8, 13, 1, 8},
+ { 0xb4, 22, 1, 4},
+ { 0x1b0, 12, 1, 8},
+ { 0xd8, 26, 1, 8},
+ },
+
+ /* T30: 1.5 GHz with 408MHz PLLP */
+ {{ 0x198, 13, 0, 8},
+ { 0x154, 22, 0, 4},
+ { 0x198, 12, 0, 8},
+ { 0x198, 26, 0, 8},
+ },
};
enum tegra_family_t {
@@ -236,7 +252,7 @@ static void powerup_cpu(void)
}
}
-static void enable_cpu_power_rail(void)
+static void enable_cpu_power_rail(enum tegra_family_t family)
{
struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
u32 reg;
@@ -245,6 +261,19 @@ static void enable_cpu_power_rail(void)
reg |= CPUPWRREQ_OE;
writel(reg, &pmc->pmc_cntrl);
+ if (family == TEGRA_FAMILY_T3x) {
+ /*
+ * TODO(sjg):
+ * Fow now we do this here. We need to find out what this is
+ * doing, tidy up the code and find a better place for it.
+ */
+ tegra_i2c_ll_write_addr(0x005a, 0x0002);
+ tegra_i2c_ll_write_data(0x2328, 0x0a02);
+ udelay(1000);
+ tegra_i2c_ll_write_data(0x0127, 0x0a02);
+ udelay(10 * 1000);
+ }
+
/*
* The TI PMU65861C needs a 3.75ms delay between enabling
* the power rail and enabling the CPU clock. This delay
@@ -278,6 +307,72 @@ static void reset_A9_cpu(int reset)
reset_set_enable(PERIPH_ID_CPU, reset);
}
+/**
+ * The T30 requires some special clock initialization, including setting up
+ * the dvc i2c, turning on mselect and selecting the G CPU cluster
+ */
+void t30_init_clocks(void)
+{
+ /*
+ * Sadly our clock functions don't support the V and W clocks of T30
+ * yet, as well as a few other functions, so use low-level register
+ * access for now. This eventual removable of low-level code from
+ * ap20.c is the same process we went through for T20.
+ */
+ struct clk_rst_ctlr *clkrst =
+ (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+ struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
+ u32 val;
+
+ /* Set active CPU cluster to G */
+ clrbits_le32(flow->control, 1 << 0);
+
+ /*
+ * Switch system clock to PLLP_out 4 (108 MHz) MHz, AVP will now run
+ * at 108 MHz. This is glitch free as only the source is changed, no
+ * special precaution needed.
+ */
+ val = (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_FIQ_SOURCE_SHIFT) |
+ (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_IRQ_SOURCE_SHIFT) |
+ (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_RUN_SOURCE_SHIFT) |
+ (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_IDLE_SOURCE_SHIFT) |
+ (SCLK_SYS_STATE_RUN << SCLK_SYS_STATE_SHIFT);
+ writel(val, &clkrst->crc_sclk_brst_pol);
+
+ writel(SUPER_SCLK_ENB_MASK, &clkrst->crc_super_sclk_div);
+
+ val = (0 << CLK_SYS_RATE_HCLK_DISABLE_SHIFT) |
+ (1 << CLK_SYS_RATE_AHB_RATE_SHIFT) |
+ (0 << CLK_SYS_RATE_PCLK_DISABLE_SHIFT) |
+ (0 << CLK_SYS_RATE_APB_RATE_SHIFT);
+ writel(val, &clkrst->crc_clk_sys_rate);
+
+ /* Put i2c, mselect in reset and enable clocks */
+ reset_set_enable(PERIPH_ID_DVC_I2C, 1);
+ clock_set_enable(PERIPH_ID_DVC_I2C, 1);
+ setbits_le32(&clkrst->crc_clk_out_enb_v, 1 << 3);
+
+ /* Switch MSELECT clock to PLLP */
+ val = readl(&clkrst->crc_clk_source_mselect);
+ val &= ~MSELECT_CLK_M_MASK;
+ writel(val, &clkrst->crc_clk_source_mselect);
+
+ /*
+ * Our high-level clock routines are not available prior to
+ * relocation. We use the low-level functions which require a
+ * hard-coded divisor. Use CLK_M with divide by (n + 1 = 17
+ */
+ clock_ll_set_source_divisor(PERIPH_ID_DVC_I2C, 3, 16);
+
+ /*
+ * Give clocks time to stabilize, then take i2c and mselect out of
+ * reset
+ */
+ udelay(1000);
+ reset_set_enable(PERIPH_ID_DVC_I2C, 0);
+ clrbits_le32(&clkrst->crc_rst_devices_v, 1 << 3);
+}
+
static void clock_enable_coresight(int enable)
{
u32 rst, src;
@@ -302,10 +397,23 @@ static void clock_enable_coresight(int enable)
}
}
-void start_cpu(u32 reset_vector)
+static void set_cpu_running(int run)
{
+ struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
+
+ writel(run ? FLOW_MODE_NONE : FLOW_MODE_STOP, &flow->halt_cpu_events);
+}
+
+void start_cpu(enum tegra_family_t family, u32 reset_vector)
+{
+ if (family == TEGRA_FAMILY_T3x)
+ t30_init_clocks();
+
/* Enable VDD_CPU */
- enable_cpu_power_rail();
+ enable_cpu_power_rail(family);
+
+ if (family == TEGRA_FAMILY_T3x)
+ set_cpu_running(0);
/* Hold the CPUs in reset */
reset_A9_cpu(1);
@@ -331,8 +439,10 @@ void start_cpu(u32 reset_vector)
/* Take the CPU out of reset */
reset_A9_cpu(0);
-}
+ if (family == TEGRA_FAMILY_T3x)
+ set_cpu_running(1);
+}
void halt_avp(void)
{
@@ -397,7 +507,7 @@ void tegra_start(void)
asm volatile("ldr sp, =%c0\n"
: : "i"(AVP_EARLY_BOOT_STACK_LIMIT));
- start_cpu((u32)_start);
+ start_cpu(ap20_get_family(), (u32)_start);
halt_avp();
/* not reached */
}