diff options
Diffstat (limited to 'arch/arm/cpu/armv7/tegra-common/ap20.c')
-rw-r--r-- | arch/arm/cpu/armv7/tegra-common/ap20.c | 124 |
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 */ } |