[U-Boot] [PATCH 3/8] Tegra124: Add SPL/AVP (arm720t) cpu files

Tom Warren twarren.nvidia at gmail.com
Tue Oct 8 00:42:53 CEST 2013


This provides SPL support for T124 boards - AVP
early init, plus CPU (A15) init/jump to main U-Boot.

Change-Id: I721f83f1d5fa549e0698e0cc76ab3e5ea11ba895
Signed-off-by: Tom Warren <twarren at nvidia.com>
---
 arch/arm/cpu/arm720t/tegra-common/cpu.c |  63 +++++--
 arch/arm/cpu/arm720t/tegra-common/cpu.h |   6 +-
 arch/arm/cpu/arm720t/tegra124/Makefile  |  31 ++++
 arch/arm/cpu/arm720t/tegra124/config.mk |   7 +
 arch/arm/cpu/arm720t/tegra124/cpu.c     | 301 ++++++++++++++++++++++++++++++++
 5 files changed, 387 insertions(+), 21 deletions(-)
 create mode 100644 arch/arm/cpu/arm720t/tegra124/Makefile
 create mode 100644 arch/arm/cpu/arm720t/tegra124/config.mk
 create mode 100644 arch/arm/cpu/arm720t/tegra124/cpu.c

diff --git a/arch/arm/cpu/arm720t/tegra-common/cpu.c b/arch/arm/cpu/arm720t/tegra-common/cpu.c
index 72c69b9..fbe553a 100644
--- a/arch/arm/cpu/arm720t/tegra-common/cpu.c
+++ b/arch/arm/cpu/arm720t/tegra-common/cpu.c
@@ -1,17 +1,8 @@
 /*
- * Copyright (c) 2010-2012, NVIDIA CORPORATION.  All rights reserved.
+ * (C) Copyright 2013
+ * NVIDIA Corporation <www.nvidia.com>
  *
- * 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/>.
+ * SPDX-License-Identifier:     GPL-2.0+
  */
 
 #include <common.h>
@@ -112,6 +103,22 @@ struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_CNT][CLOCK_OSC_FREQ_COUNT] = {
 		{ .n = 116, .m = 1, .p = 1 }, /* OSC: 12.0 MHz */
 		{ .n = 108, .m = 2, .p = 1 }, /* OSC: 26.0 MHz */
 	},
+
+	/*
+	 * T124: 700 MHz
+	 *
+	 * Register   Field  Bits   Width
+	 * ------------------------------
+	 * PLLX_BASE  p      23:20    4
+	 * PLLX_BASE  n      15: 8    8
+	 * PLLX_BASE  m       7: 0    8
+	 */
+	{
+		{ .n = 108, .m = 1, .p = 1 }, /* OSC: 13.0 MHz */
+		{ .n =  73, .m = 1, .p = 1 }, /* OSC: 19.2 MHz */
+		{ .n = 116, .m = 1, .p = 1 }, /* OSC: 12.0 MHz */
+		{ .n = 108, .m = 2, .p = 1 }, /* OSC: 26.0 MHz */
+	},
 };
 
 void adjust_pllp_out_freqs(void)
@@ -146,6 +153,18 @@ int pllx_set_rate(struct clk_pll_simple *pll , u32 divn, u32 divm,
 
 	debug(" pllx_set_rate entry\n");
 
+#if defined(CONFIG_TEGRA124)
+	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+
+	/* Disable IDDQ */
+	reg = readl(&clkrst->crc_pllx_misc3);
+	reg &= ~PLLX_IDDQ_MASK;
+	writel(reg, &clkrst->crc_pllx_misc3);
+	udelay(2);
+	debug("%s: IDDQ: PLLX IDDQ = 0x%08X\n", __func__,
+	      readl(&clkrst->crc_pllx_misc3));
+#endif	/* T124 */
+
 	/* Set BYPASS, m, n and p to PLLX_BASE */
 	reg = PLL_BYPASS_MASK | (divm << PLL_DIVM_SHIFT);
 	reg |= ((divn << PLL_DIVN_SHIFT) | (divp << PLL_DIVP_SHIFT));
@@ -162,18 +181,23 @@ int pllx_set_rate(struct clk_pll_simple *pll , u32 divn, u32 divm,
 		reg |= (1 << PLL_DCCON_SHIFT);
 	writel(reg, &pll->pll_misc);
 
-	/* Enable PLLX */
-	reg = readl(&pll->pll_base);
-	reg |= PLL_ENABLE_MASK;
-
 	/* Disable BYPASS */
+	reg = readl(&pll->pll_base);
 	reg &= ~PLL_BYPASS_MASK;
 	writel(reg, &pll->pll_base);
+	debug(" pllx_set_rate: base = 0x%08X\n", reg);
 
 	/* Set lock_enable to PLLX_MISC */
 	reg = readl(&pll->pll_misc);
 	reg |= PLL_LOCK_ENABLE_MASK;
 	writel(reg, &pll->pll_misc);
+	debug(" pllx_set_rate: misc = 0x%08X\n", reg);
+
+	/* Enable PLLX last, as per JZ */
+	reg = readl(&pll->pll_base);
+	reg |= PLL_ENABLE_MASK;
+	writel(reg, &pll->pll_base);
+	debug(" pllx_set_rate: base final = 0x%08X\n", reg);
 
 	return 0;
 }
@@ -208,7 +232,7 @@ void init_pllx(void)
 	sel = &tegra_pll_x_table[chip_sku][osc];
 	pllx_set_rate(pll, sel->n, sel->m, sel->p, sel->cpcon);
 
-	/* adjust PLLP_out1-4 on T3x/T114 */
+	/* adjust PLLP_out1-4 on T3x/T1x4 */
 	if (soc_type >= CHIPID_TEGRA30) {
 		debug("  init_pllx: adjusting PLLP out freqs\n");
 		adjust_pllp_out_freqs();
@@ -343,13 +367,14 @@ void clock_enable_coresight(int enable)
 	if (enable) {
 		/*
 		 * Put CoreSight on PLLP_OUT0 and divide it down as per
-		 * PLLP base frequency based on SoC type (T20/T30/T114).
+		 * PLLP base frequency based on SoC type (T20/T30/T1x4).
 		 * Clock divider request would setup CSITE clock as 144MHz
 		 * for PLLP base 216MHz and 204MHz for PLLP base 408MHz
 		 */
 
 		soc_type = tegra_get_chip();
-		if (soc_type == CHIPID_TEGRA30 || soc_type == CHIPID_TEGRA114)
+		if (soc_type == CHIPID_TEGRA30 || soc_type == CHIPID_TEGRA114 ||
+		    soc_type == CHIPID_TEGRA124)
 			src = CLK_DIVIDER(NVBL_PLLP_KHZ, 204000);
 		else if (soc_type == CHIPID_TEGRA20)
 			src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000);
diff --git a/arch/arm/cpu/arm720t/tegra-common/cpu.h b/arch/arm/cpu/arm720t/tegra-common/cpu.h
index 60412c7..7e0b314 100644
--- a/arch/arm/cpu/arm720t/tegra-common/cpu.h
+++ b/arch/arm/cpu/arm720t/tegra-common/cpu.h
@@ -1,5 +1,5 @@
 /*
- * (C) Copyright 2010-2011
+ * (C) Copyright 2010-2013
  * NVIDIA Corporation <www.nvidia.com>
  *
  * SPDX-License-Identifier:	GPL-2.0+
@@ -12,7 +12,8 @@
 
 #if defined(CONFIG_TEGRA20)
 #define NVBL_PLLP_KHZ	(216000)
-#elif defined(CONFIG_TEGRA30) || defined(CONFIG_TEGRA114)
+#elif defined(CONFIG_TEGRA30) || defined(CONFIG_TEGRA114) || \
+	defined(CONFIG_TEGRA124)
 #define NVBL_PLLP_KHZ	(408000)
 #else
 #error "Unknown Tegra chip!"
@@ -68,3 +69,4 @@ int tegra_get_chip(void);
 int tegra_get_sku_info(void);
 int tegra_get_chip_sku(void);
 void adjust_pllp_out_freqs(void);
+void pmic_enable_cpu_vdd(void);
diff --git a/arch/arm/cpu/arm720t/tegra124/Makefile b/arch/arm/cpu/arm720t/tegra124/Makefile
new file mode 100644
index 0000000..836eca0
--- /dev/null
+++ b/arch/arm/cpu/arm720t/tegra124/Makefile
@@ -0,0 +1,31 @@
+#
+# (C) Copyright 2010-2013
+# NVIDIA Corporation <www.nvidia.com>
+# (C) Copyright 2000-2008
+# Wolfgang Denk, DENX Software Engineering, wd at denx.de.
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#
+     
+include $(TOPDIR)/config.mk
+
+LIB	= $(obj)lib$(SOC).o
+
+COBJS-y	+= cpu.o
+
+SRCS	:= $(COBJS-y:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS-y))
+
+all:	$(obj).depend $(LIB)
+
+$(LIB):	$(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/arch/arm/cpu/arm720t/tegra124/config.mk b/arch/arm/cpu/arm720t/tegra124/config.mk
new file mode 100644
index 0000000..5e10701
--- /dev/null
+++ b/arch/arm/cpu/arm720t/tegra124/config.mk
@@ -0,0 +1,7 @@
+#
+# (C) Copyright 2010-2013
+# NVIDIA Corporation <www.nvidia.com>
+#
+# SPDX-License-Identifier:     GPL-2.0+
+#/
+USE_PRIVATE_LIBGCC = yes
diff --git a/arch/arm/cpu/arm720t/tegra124/cpu.c b/arch/arm/cpu/arm720t/tegra124/cpu.c
new file mode 100644
index 0000000..b40bc6c
--- /dev/null
+++ b/arch/arm/cpu/arm720t/tegra124/cpu.c
@@ -0,0 +1,301 @@
+/*
+ * (C) Copyright 2013
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/arch/ahb.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/flow.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/tegra.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch/pmc.h>
+#include <asm/arch-tegra/ap.h>
+#include "../tegra-common/cpu.h"
+
+/* Tegra124-specific CPU init code */
+
+static void enable_cpu_power_rail(void)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+	debug("enable_cpu_power_rail entry\n");
+
+	/* un-tristate PWR_I2C SCL/SDA, rest of the defaults are correct */
+	pinmux_tristate_disable(PINGRP_PWR_I2C_SCL);
+	pinmux_tristate_disable(PINGRP_PWR_I2C_SDA);
+
+	pmic_enable_cpu_vdd();
+
+	/*
+	 * Set CPUPWRGOOD_TIMER - APB clock is 1/2 of SCLK (102MHz),
+	 * set it for 5ms as per SysEng (102MHz/5mS = 510000 (7C830h).
+	 */
+	writel(0x7C830, &pmc->pmc_cpupwrgood_timer);
+
+	/* Set polarity to 0 (normal) and enable CPUPWRREQ_OE */
+	clrbits_le32(&pmc->pmc_cntrl, CPUPWRREQ_POL);
+	setbits_le32(&pmc->pmc_cntrl, CPUPWRREQ_OE);
+}
+
+static void enable_cpu_clocks(void)
+{
+	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 reg;
+
+	debug("enable_cpu_clocks entry\n");
+
+	/* Wait for PLL-X to lock */
+	do {
+		reg = readl(&clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base);
+		debug("%s: PLLX base = 0x%08X\n", __func__, reg);
+	} while ((reg & (1 << 27)) == 0);
+
+	debug("%s: PLLX locked, delay for stable clocks\n", __func__);
+	/* Wait until all clocks are stable */
+	udelay(PLL_STABILIZATION_DELAY);
+
+	debug("%s: Setting CCLK_BURST and DIVIDER\n", __func__);
+	writel(CCLK_BURST_POLICY, &clkrst->crc_cclk_brst_pol);
+	writel(SUPER_CCLK_DIVIDER, &clkrst->crc_super_cclk_div);
+
+	debug("%s: Enabling clock to all CPUs\n", __func__);
+	/* Enable the clock to all CPUs */
+	reg = readl(&clkrst->crc_clk_cpu_cmplx_clr);
+	reg |= (CLR_CPU3_CLK_STP + CLR_CPU2_CLK_STP);
+	reg |= CLR_CPU1_CLK_STP;
+	writel((reg | CLR_CPU0_CLK_STP), &clkrst->crc_clk_cpu_cmplx_clr);
+
+	debug("%s: Enabling main CPU complex clocks\n", __func__);
+	/* Always enable the main CPU complex clocks */
+	clock_enable(PERIPH_ID_CPU);
+	clock_enable(PERIPH_ID_CPULP);
+	clock_enable(PERIPH_ID_CPUG);
+
+	debug("%s: Done\n", __func__);
+}
+
+static void remove_cpu_resets(void)
+{
+	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 reg;
+
+	debug("remove_cpu_resets entry\n");
+
+	/* Take the slow and fast partitions out of reset */
+	reg = CLR_NONCPURESET;
+	writel(reg, &clkrst->crc_rst_cpulp_cmplx_clr);
+	writel(reg, &clkrst->crc_rst_cpug_cmplx_clr);
+
+	/* Clear the SW-controlled reset of the slow cluster */
+	reg = (CLR_CPURESET0 + CLR_DBGRESET0 + CLR_CORERESET0 + CLR_CXRESET0);
+	reg |= (CLR_L2RESET + CLR_PRESETDBG);
+	writel(reg, &clkrst->crc_rst_cpulp_cmplx_clr);
+
+	/* Clear the SW-controlled reset of the fast cluster */
+	reg = (CLR_CPURESET0 + CLR_DBGRESET0 + CLR_CORERESET0 + CLR_CXRESET0);
+	reg |= (CLR_CPURESET1 + CLR_DBGRESET1 + CLR_CORERESET1 + CLR_CXRESET1);
+	reg |= (CLR_CPURESET2 + CLR_DBGRESET2 + CLR_CORERESET2 + CLR_CXRESET2);
+	reg |= (CLR_CPURESET3 + CLR_DBGRESET3 + CLR_CORERESET3 + CLR_CXRESET3);
+	reg |= (CLR_L2RESET + CLR_PRESETDBG);
+	writel(reg, &clkrst->crc_rst_cpug_cmplx_clr);
+}
+
+/**
+ * The T1x4 requires some special clock initialization, including setting up
+ * the DVC I2C, turning on MSELECT and selecting the G CPU cluster
+ */
+void t1x4_init_clocks(void)
+{
+	struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+	struct clk_rst_ctlr *clkrst =
+			(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 val;
+
+	debug("t1x4_init_clocks entry\n");
+
+	/* Set active CPU cluster to G */
+	clrbits_le32(&flow->cluster_control, 1);
+
+	/* Change the oscillator drive strength */
+	val = readl(&clkrst->crc_osc_ctrl);
+	val &= ~OSC_XOFS_MASK;
+	val |= (OSC_DRIVE_STRENGTH << OSC_XOFS_SHIFT);
+	writel(val, &clkrst->crc_osc_ctrl);
+
+	/* Update same value in PMC_OSC_EDPD_OVER XOFS field for warmboot */
+	val = readl(&pmc->pmc_osc_edpd_over);
+	val &= ~PMC_XOFS_MASK;
+	val |= (OSC_DRIVE_STRENGTH << PMC_XOFS_SHIFT);
+	writel(val, &pmc->pmc_osc_edpd_over);
+
+	/* Set HOLD_CKE_LOW_EN to 1 */
+	setbits_le32(&pmc->pmc_cntrl2, HOLD_CKE_LOW_EN);
+
+	debug("Setting up PLLX\n");
+	init_pllx();
+
+	val = (1 << CLK_SYS_RATE_AHB_RATE_SHIFT);
+	writel(val, &clkrst->crc_clk_sys_rate);
+
+	/* Enable clocks to required peripherals. TBD - minimize this list */
+	debug("Enabling clocks\n");
+
+	clock_set_enable(PERIPH_ID_CACHE2, 1);
+	clock_set_enable(PERIPH_ID_GPIO, 1);
+	clock_set_enable(PERIPH_ID_TMR, 1);
+	clock_set_enable(PERIPH_ID_CPU, 1);
+	clock_set_enable(PERIPH_ID_EMC, 1);
+	clock_set_enable(PERIPH_ID_I2C5, 1);
+	clock_set_enable(PERIPH_ID_APBDMA, 1);
+	clock_set_enable(PERIPH_ID_MEM, 1);
+	clock_set_enable(PERIPH_ID_CORESIGHT, 1);
+	clock_set_enable(PERIPH_ID_MSELECT, 1);
+	clock_set_enable(PERIPH_ID_DVFS, 1);
+
+	/*
+	 * Set MSELECT clock source as PLLP (00), and ask for a clock
+	 * divider that would set the MSELECT clock at 102MHz for a
+	 * PLLP base of 408MHz.
+	 */
+	clock_ll_set_source_divisor(PERIPH_ID_MSELECT, 0,
+		CLK_DIVIDER(NVBL_PLLP_KHZ, 102000));
+
+	/* Give clock time to stabilize */
+	udelay(IO_STABILIZATION_DELAY);
+
+	/* I2C5 (DVC) gets CLK_M and a divisor of 17 */
+	clock_ll_set_source_divisor(PERIPH_ID_I2C5, 3, 16);
+
+	/* Give clock time to stabilize */
+	udelay(IO_STABILIZATION_DELAY);
+
+	/* Take required peripherals out of reset */
+	debug("Taking periphs out of reset\n");
+	reset_set_enable(PERIPH_ID_CACHE2, 0);
+	reset_set_enable(PERIPH_ID_GPIO, 0);
+	reset_set_enable(PERIPH_ID_TMR, 0);
+	reset_set_enable(PERIPH_ID_COP, 0);
+	reset_set_enable(PERIPH_ID_EMC, 0);
+	reset_set_enable(PERIPH_ID_I2C5, 0);
+	reset_set_enable(PERIPH_ID_APBDMA, 0);
+	reset_set_enable(PERIPH_ID_MEM, 0);
+	reset_set_enable(PERIPH_ID_CORESIGHT, 0);
+	reset_set_enable(PERIPH_ID_MSELECT, 0);
+	reset_set_enable(PERIPH_ID_DVFS, 0);
+
+	debug("t1x4_init_clocks exit\n");
+}
+
+static int is_partition_powered(u32 mask)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+	u32 reg;
+
+	/* Get power gate status */
+	reg = readl(&pmc->pmc_pwrgate_status);
+	return (reg & mask) == mask;
+}
+
+static void power_partition(u32 status, u32 partid)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+	debug("%s: status = %08X, part ID = %08X\n", __func__, status, partid);
+	/* Is the partition already on? */
+	if (!is_partition_powered(status)) {
+		/* No, toggle the partition power state (OFF -> ON) */
+		debug("power_partition, toggling state\n");
+		clrbits_le32(&pmc->pmc_pwrgate_toggle, 0x1F);
+		setbits_le32(&pmc->pmc_pwrgate_toggle, partid);
+		setbits_le32(&pmc->pmc_pwrgate_toggle, START_CP);
+
+		/* Wait for the power to come up */
+		while (!is_partition_powered(status))
+			;
+
+		/* Give I/O signals time to stabilize */
+		udelay(IO_STABILIZATION_DELAY);
+	}
+}
+
+void powerup_cpus(void)
+{
+	debug("powerup_cpus entry\n");
+
+	/* We boot to the fast cluster */
+	debug("powerup_cpus entry: G cluster\n");
+
+	/* Power up the fast cluster rail partition */
+	debug("powerup_cpus: CRAIL\n");
+	power_partition(CRAIL, CRAILID);
+
+	/* Power up the fast cluster non-CPU partition */
+	debug("powerup_cpus: C0NC\n");
+	power_partition(C0NC, C0NCID);
+
+	/* Power up the fast cluster CPU0 partition */
+	debug("powerup_cpus: CE0\n");
+	power_partition(CE0, CE0ID);
+
+	debug("powerup_cpus: done\n");
+}
+
+void start_cpu(u32 reset_vector)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+	debug("start_cpu entry, reset_vector = %x\n", reset_vector);
+
+	t1x4_init_clocks();
+
+	/* Set power-gating timer multiplier */
+	clrbits_le32(&pmc->pmc_pwrgate_timer_mult, TIMER_MULT_MASK);
+	setbits_le32(&pmc->pmc_pwrgate_timer_mult, MULT_8);
+
+	/* Enable VDD_CPU */
+	enable_cpu_power_rail();
+
+	/* Get the CPU(s) running */
+	enable_cpu_clocks();
+
+	/* Enable CoreSight */
+	clock_enable_coresight(1);
+
+	/* Take CPU(s) out of reset */
+	remove_cpu_resets();
+
+	/* Set the entry point for CPU execution from reset */
+	writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+	/* If the CPU(s) don't already have power, power 'em up */
+	powerup_cpus();
+	debug("start_cpu exit, should continue @ reset_vector\n");
+}
+
+/*
+ * On poweron, AVP clock source (also called system clock) is set to PLLP_out0
+ * with frequency set at 1MHz. Before initializing PLLP, we need to move the
+ * system clock's source to CLK_M temporarily. And then switch it to PLLP_out4
+ * (204MHz) at a later time.
+ */
+void set_avp_clock_to_clkm(void)
+{
+	struct clk_rst_ctlr *clkrst =
+			(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 val;
+
+	val = (SCLK_SOURCE_CLKM << SCLK_SWAKEUP_FIQ_SOURCE_SHIFT) |
+		(SCLK_SOURCE_CLKM << SCLK_SWAKEUP_IRQ_SOURCE_SHIFT) |
+		(SCLK_SOURCE_CLKM << SCLK_SWAKEUP_RUN_SOURCE_SHIFT) |
+		(SCLK_SOURCE_CLKM << SCLK_SWAKEUP_IDLE_SOURCE_SHIFT) |
+		(SCLK_SYS_STATE_RUN << SCLK_SYS_STATE_SHIFT);
+	writel(val, &clkrst->crc_sclk_brst_pol);
+	/* Wait 2-3us for the clock to flush thru the logic as per the TRM */
+	udelay(3);
+}
-- 
1.8.1.5



More information about the U-Boot mailing list