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

login
register
mail settings
Submitter Tom Warren
Date Oct. 7, 2013, 10:42 p.m.
Message ID <1381185778-25722-3-git-send-email-twarren@nvidia.com>
Download mbox | patch
Permalink /patch/281284/
State Changes Requested
Delegated to: Tom Warren
Headers show

Comments

Tom Warren - Oct. 7, 2013, 10:42 p.m.
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@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
Thierry Reding - Oct. 8, 2013, 8:13 a.m.
On Tue, Oct 08, 2013 at 12:42:53AM +0200, Tom Warren wrote:
> 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@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>

What about years 2010-2012? Shouldn't they be kept in the copyright
line? Also, we should probably try to be more consistent with our
copyright notices. There's a variety of ways that we use:

	(C) Copyright 2013
	NVIDIA Corporation <www.nvidia.com>

	Copyright (c) 2010-2013, NVIDIA CORPORATION.  All rights reserved.

	(C) Copyright 2010-2013, NVIDIA Corporation <www.nvidia.com>

	Copyright (c) 2010-2011 NVIDIA Corporation

There may be more. I personally have a preference for the last of these,
but as long as we can keep some consistency I don't mind which one we
settle on.

>  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 */

Perhaps this should be moved to a separate function that can be provided
as a dummy for non-Tegra124?

> @@ -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);

Why's there a leading space in that debug message? I see that other
debug messages have it as well, but I don't see any reason for it.
Copied and pasted?

>         /* 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 */

I guess JZ is Jimmy Zhang, but a proper explanation for why this is done
on necessary would be more valuable.

> -       /* adjust PLLP_out1-4 on T3x/T114 */
> +       /* adjust PLLP_out1-4 on T3x/T1x4 */

I don't know about this. What ever happens if engineering comes up with
a chip, say, T194 that's not compatible? I think there's some sense in
being explicit here and making this T3x/T114/T124. I don't know why T3x
is used here either for that matter.

>                 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)

Perhaps:

		if (soc_type >= CHIPID_TEGRA30)

?

> @@ -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);

This doesn't seem to exist until patch 8. Perhaps this should really be
an weak function so that it always exists but can still be overwritten
by individual boards?

> diff --git a/arch/arm/cpu/arm720t/tegra124/cpu.c b/arch/arm/cpu/arm720t/tegra124/cpu.c
> +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).

Nit: "102MHz/5ms". And it should probably be 102MHz*5ms to yield the
correct result.

> +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);

1 << 27 -> PLLX_LOCK?

> +       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);

Perhaps:

	reg = readl(&clkrst->crc_clk_cpu_cmplx_clr);
	reg |= CLR_CPU3_CLK_STP | CLR_CPU2_CLK_STP | CLR_CPU1_CLK_STP |
	       CLR_CPU0_CLK_STP;
	writel(reg, &clkrst->crc_clk_cpu_cmplx_clr);

? Also this is a write-1-to-clear register, right? So there should be no
need to read it first and OR in the new bits.

> +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);

s/+/|/? And you can safely drop the parentheses as they aren't required.

> +       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);

Same here.

> +static int is_partition_powered(u32 mask)

Perhaps this should return bool?

> +       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)) {

Shouldn't this pass along the partid as well? Otherwise, how does the
is_partition_powered() function know what to check for. Perhaps I'm
confused by the API here. Why do we even pass around status in the first
place? The partition ID should be enough, shouldn't it?

> +               /* 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);

The register has only two fields, so no need for read/modify/write. This
can be abbreviated to:

		writel(START_CP | partid, &pmc->pmc_pwrgate_toggle);

> +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);

Ah, I see now. I'd find something like the following more intuitive:

	power_partition_set(CRAIL, true);

But looking deeper I see that we already have the same API for earlier
SoC generations, so I suppose we might as well stick with it.

The current API always has the risk of someone doing something like:

	power_partition(CONC, CRAILID);

> +void start_cpu(u32 reset_vector)
> +{
[...]
> +       /* 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();

I don't think any of the comments above add value, so they can just as
well be dropped. The code is self-explanatory.

> +/*
> + * On poweron, AVP clock source (also called system clock) is set to PLLP_out0

Nit: "power on"

Thierry

-----------------------------------------------------------------------------------
This email message is for the sole use of the intended recipient(s) and may contain
confidential information.  Any unauthorized review, use, disclosure or distribution
is prohibited.  If you are not the intended recipient, please contact the sender by
reply email and destroy all copies of the original message.
-----------------------------------------------------------------------------------
Stephen Warren - Oct. 8, 2013, 9:34 p.m.
On 10/08/2013 02:13 AM, Thierry Reding wrote:
> On Tue, Oct 08, 2013 at 12:42:53AM +0200, Tom Warren wrote:
>> This provides SPL support for T124 boards - AVP early init, plus
>> CPU (A15) init/jump to main U-Boot.

>> 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)
> 
> Perhaps:
> 
> if (soc_type >= CHIPID_TEGRA30)

Given that the only exception is Tegra20, wouldn't it be better as:

if (soc_type == CHIPID_TEGRA20)

and then swap the Tegra20/not-Tegra20 branches of the if statement?
Stephen Warren - Oct. 8, 2013, 9:36 p.m.
On 10/07/2013 04:42 PM, Tom Warren wrote:
> This provides SPL support for T124 boards - AVP
> early init, plus CPU (A15) init/jump to main U-Boot.

>  arch/arm/cpu/arm720t/tegra124/cpu.c     | 301 ++++++++++++++++++++++++++++++++

I would have expected a lot more of that file to be common with
tegra114/cpu.c, considering the tiny size of the kernel patches for SMP
support, CPU hotplug support, etc.
Stephen Warren - Jan. 22, 2014, 11:12 p.m.
On 10/08/2013 02:13 AM, Thierry Reding wrote:
> On Tue, Oct 08, 2013 at 12:42:53AM +0200, Tom Warren wrote:
>> This provides SPL support for T124 boards - AVP
>> early init, plus CPU (A15) init/jump to main U-Boot.

>> +#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 */
> 
> Perhaps this should be moved to a separate function that can be provided
> as a dummy for non-Tegra124?

(I'm working on finalizing this patch for submission now)

I think it's fine for the core driver to know about the different SoCs.
A simple ifdef like this is quite manageable. If the code starts to get
unmanageable, or does a lot of things that aren't common across chips,
we can always split it out into the SoC-specific files.

>> +void pmic_enable_cpu_vdd(void);
> 
> This doesn't seem to exist until patch 8. Perhaps this should really be
> an weak function so that it always exists but can still be overwritten
> by individual boards?

The build of these files isn't enabled until the last patch anyway, so
it's all perfectly bisectable. I'm not included to do anything to fix
this unless you feel strongly..

I'll fix up all the other issues you mentioned.

Patch

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@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);
+}