Skip to contentSkip to conversation

arm: Tegra2: add support for A9 CPU init

PATCH Superseded Tom Warren 2 participants 5 messages Apr 12, 2011 – Apr 13, 2011

Subsystems (3)

v4 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Tom Warren

author
#0 lore raw

PATCH v4 1/1 arm: Tegra2: add support for A9 CPU init

---
Changes for V2:
- Remove returns in void functions
- Move inline assembly code to .S file
- Simplify some if/else code, break out common code
- Minimize the use of local vars
- Inline some single-instance functions
- Remove TRUE/FALSE define, use 1/0 instead
- Replace memset of mem-mapped regs w/loop of writel's
Changes for V3:
- Fix C-style comments in lowlevel_init.S cache_configure
Changes for V4:
- Move cold_boot() from C to assembly
- Fix spacing in cache_configure

 arch/arm/cpu/armv7/start.S                 |   12 +
 arch/arm/cpu/armv7/tegra2/Makefile         |    2 +-
 arch/arm/cpu/armv7/tegra2/ap20.c           |  325 ++++++++++++++++++++++++++++
 arch/arm/cpu/armv7/tegra2/ap20.h           |  104 +++++++++
 arch/arm/cpu/armv7/tegra2/lowlevel_init.S  |   94 ++++++++
 arch/arm/include/asm/arch-tegra2/clk_rst.h |   27 +++
 arch/arm/include/asm/arch-tegra2/pmc.h     |    8 +
 arch/arm/include/asm/arch-tegra2/scu.h     |   43 ++++
 arch/arm/include/asm/arch-tegra2/tegra2.h  |    8 +
 board/nvidia/common/board.c                |   10 +
 board/nvidia/common/board.h                |   29 +++
 include/configs/harmony.h                  |    1 +
 include/configs/seaboard.h                 |    3 +-
 include/configs/tegra2-common.h            |    2 +
 14 files changed, 666 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/cpu/armv7/tegra2/ap20.c
 create mode 100644 arch/arm/cpu/armv7/tegra2/ap20.h
 create mode 100644 arch/arm/include/asm/arch-tegra2/scu.h
 create mode 100644 board/nvidia/common/board.h
arch/arm/cpu/armv7/start.S +12 -0
index cb4f92f..4b36693 100644
@@ -70,6 +70,18 @@ _end_vect:
 _TEXT_BASE:
 	.word	CONFIG_SYS_TEXT_BASE
 
+#ifdef CONFIG_TEGRA2
+/*
+ * Tegra2 uses 2 separate CPUs - the AVP (ARM7TDMI) and the CPU (dual A9s).
+ * U-Boot runs on the AVP first, setting things up for the CPU (PLLs,
+ * muxes, clocks, clamps, etc.). Then the AVP halts, and expects the CPU
+ * to pick up its reset vector, which points here.
+ */
+.globl _armboot_start
+_armboot_start:
+        .word _start
+#endif
+
 /*
  * These are defined in the board-specific linker script.
  */
arch/arm/cpu/armv7/tegra2/Makefile +1 -1
index 687c887..f1ea915 100644
@@ -28,7 +28,7 @@ include $(TOPDIR)/config.mk
 LIB	=  $(obj)lib$(SOC).o
 
 SOBJS	:= lowlevel_init.o
-COBJS	:= board.o sys_info.o timer.o
+COBJS	:= ap20.o board.o sys_info.o timer.o
 
 SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS	:= $(addprefix $(obj),$(COBJS) $(SOBJS))
arch/arm/cpu/armv7/tegra2/ap20.c new file +325 -0

325 lines not on this page · raw message

arch/arm/cpu/armv7/tegra2/ap20.h new file +104 -0

104 lines not on this page · raw message

arch/arm/cpu/armv7/tegra2/lowlevel_init.S +94 -0

108 lines not on this page · raw message

arch/arm/include/asm/arch-tegra2/clk_rst.h +27 -0
index 6d573bf..d67a5d7 100644
@@ -149,6 +149,9 @@ struct clk_rst_ctlr {
 	uint crc_clk_src_csite;		/*_CSITE_0,		0x1D4 */
 	uint crc_reserved19[9];		/*			0x1D8-1F8 */
 	uint crc_clk_src_osc;		/*_OSC_0,		0x1FC */
+	uint crc_reserved20[80];	/*			0x200-33C */
+	uint crc_cpu_cmplx_set;		/* _CPU_CMPLX_SET_0,	0x340 */
+	uint crc_cpu_cmplx_clr;		/* _CPU_CMPLX_CLR_0,	0x344 */
 };
 
 #define PLL_BYPASS		(1 << 31)
@@ -162,4 +165,28 @@ struct clk_rst_ctlr {
 #define SWR_UARTA_RST		(1 << 6)
 #define CLK_ENB_UARTA		(1 << 6)
 
+#define SWR_CPU_RST		(1 << 0)
+#define CLK_ENB_CPU		(1 << 0)
+#define SWR_CSITE_RST		(1 << 9)
+#define CLK_ENB_CSITE		(1 << 9)
+
+#define SET_CPURESET0		(1 << 0)
+#define SET_DERESET0		(1 << 4)
+#define SET_DBGRESET0		(1 << 12)
+
+#define SET_CPURESET1		(1 << 1)
+#define SET_DERESET1		(1 << 5)
+#define SET_DBGRESET1		(1 << 13)
+
+#define CLR_CPURESET0		(1 << 0)
+#define CLR_DERESET0		(1 << 4)
+#define CLR_DBGRESET0		(1 << 12)
+
+#define CLR_CPURESET1		(1 << 1)
+#define CLR_DERESET1		(1 << 5)
+#define CLR_DBGRESET1		(1 << 13)
+
+#define CPU0_CLK_STP		(1 << 8)
+#define CPU1_CLK_STP		(1 << 9)
+
 #endif	/* CLK_RST_H */
arch/arm/include/asm/arch-tegra2/pmc.h +8 -0
index 7ec9eeb..b1d47cd 100644
@@ -121,4 +121,12 @@ struct pmc_ctlr {
 	uint pmc_gate;			/* _GATE_0, offset 15C */
 };
 
+#define CPU_PWRED	1
+#define CPU_CLMP	1
+
+#define PARTID_CP	0xFFFFFFF8
+#define START_CP	(1 << 8)
+
+#define CPUPWRREQ_OE	(1 << 16)
+
 #endif	/* PMC_H */
arch/arm/include/asm/arch-tegra2/scu.h new file +43 -0

43 lines not on this page · raw message

arch/arm/include/asm/arch-tegra2/tegra2.h +8 -0
index 9001b68..7b0f5cc 100644
@@ -25,8 +25,12 @@
 #define _TEGRA2_H_
 
 #define NV_PA_SDRAM_BASE	0x00000000
+#define NV_PA_ARM_PERIPHBASE	0x50040000
+#define NV_PA_PG_UP_BASE	0x60000000
 #define NV_PA_TMRUS_BASE	0x60005010
 #define NV_PA_CLK_RST_BASE	0x60006000
+#define NV_PA_FLOW_BASE		0x60007000
+#define NV_PA_EVP_BASE		0x6000F000
 #define NV_PA_APB_MISC_BASE	0x70000000
 #define NV_PA_APB_UARTA_BASE	(NV_PA_APB_MISC_BASE + 0x6000)
 #define NV_PA_APB_UARTB_BASE	(NV_PA_APB_MISC_BASE + 0x6040)
@@ -34,9 +38,13 @@
 #define NV_PA_APB_UARTD_BASE	(NV_PA_APB_MISC_BASE + 0x6300)
 #define NV_PA_APB_UARTE_BASE	(NV_PA_APB_MISC_BASE + 0x6400)
 #define NV_PA_PMC_BASE		0x7000E400
+#define NV_PA_CSITE_BASE	0x70040000
 
 #define TEGRA2_SDRC_CS0		NV_PA_SDRAM_BASE
 #define LOW_LEVEL_SRAM_STACK	0x4000FFFC
+#define EARLY_AVP_STACK		(NV_PA_SDRAM_BASE + 0x20000)
+#define EARLY_CPU_STACK		(EARLY_AVP_STACK - 4096)
+#define PG_UP_TAG_AVP		0xAAAAAAAA
 
 #ifndef __ASSEMBLY__
 struct timerus {
board/nvidia/common/board.c +10 -0
index b2c412c..078547b 100644
@@ -30,6 +30,7 @@
 #include <asm/arch/clk_rst.h>
 #include <asm/arch/pinmux.h>
 #include <asm/arch/uart.h>
+#include "board.h"
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -37,6 +38,15 @@ const struct tegra2_sysinfo sysinfo = {
 	CONFIG_TEGRA2_BOARD_STRING
 };
 
+#ifdef CONFIG_BOARD_EARLY_INIT_F
+int board_early_init_f(void)
+{
+	debug("Board Early Init\n");
+	tegra2_start();
+	return 0;
+}
+#endif	/* EARLY_INIT */
+
 /*
  * Routine: timer_init
  * Description: init the timestamp and lastinc value
board/nvidia/common/board.h new file +29 -0
new file mode 100644
index 0000000..47c7885
@@ -0,0 +1,29 @@
+/*
+ *  (C) Copyright 2010,2011
+ *  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
+ */
+
+#ifndef _BOARD_H_
+#define _BOARD_H_
+
+void tegra2_start(void);
+
+#endif	/* BOARD_H */
include/configs/harmony.h +1 -0
index d004f31..34bd899 100644
@@ -46,4 +46,5 @@
 #define CONFIG_MACH_TYPE		MACH_TYPE_HARMONY
 #define CONFIG_SYS_BOARD_ODMDATA	0x300d8011 /* lp1, 1GB */
 
+#define CONFIG_BOARD_EARLY_INIT_F
 #endif /* __CONFIG_H */
include/configs/seaboard.h +2 -1
index fd87560..06ce3e2 100644
@@ -37,7 +37,8 @@
 #define CONFIG_TEGRA2_ENABLE_UARTD
 #define CONFIG_SYS_NS16550_COM1		NV_PA_APB_UARTD_BASE
 
-#define CONFIG_MACH_TYPE		MACH_TYPE_TEGRA_SEABOARD
+#define CONFIG_MACH_TYPE		MACH_TYPE_SEABOARD
 #define CONFIG_SYS_BOARD_ODMDATA	0x300d8011 /* lp1, 1GB */
 
+#define CONFIG_BOARD_EARLY_INIT_F
 #endif /* __CONFIG_H */
include/configs/tegra2-common.h +2 -0
index 4f4374a..2924325 100644
@@ -33,6 +33,8 @@
 #define CONFIG_MACH_TEGRA_GENERIC	/* which is a Tegra generic machine */
 #define CONFIG_L2_OFF			/* No L2 cache */
 
+#define CONFIG_ENABLE_CORTEXA9		/* enable CPU (A9 complex) */
+
 #include <asm/arch/tegra2.h>		/* get chip and board defs */
 
 /*
Signature · 1 line
1.7.4.3
#1 lore raw

PATCH v4 1/1 arm: Tegra2: add support for A9 CPU init

Le 12/04/2011 23:02, Tom Warren a ?crit :

Quoted 111 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +105 -0
new file mode 100644
index 0000000..075341e
@@ -0,0 +1,325 @@
+/*
+* (C) Copyright 2010-2011
+* 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 "ap20.h"
+#include<asm/io.h>
+#include<asm/arch/tegra2.h>
+#include<asm/arch/clk_rst.h>
+#include<asm/arch/pmc.h>
+#include<asm/arch/pinmux.h>
+#include<asm/arch/scu.h>
+#include<common.h>
+
+u32 s_first_boot = 1;
+
+static void enable_cpu_clock(int enable)
+{
+	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 reg, clk;
+
+	/*
+	 * NOTE:
+	 * Regardless of whether the request is to enable or disable the CPU
+	 * clock, every processor in the CPU complex except the master (CPU 0)
+	 * will have it's clock stopped because the AVP only talks to the
+	 * master. The AVP does not know (nor does it need to know) that there
+	 * are multiple processors in the CPU complex.
+	 */
+
+	if (enable) {
+		/* Wait until all clocks are stable */
+		udelay(PLL_STABILIZATION_DELAY);
+
+		writel(CCLK_BURST_POLICY,&clkrst->crc_cclk_brst_pol);
+		writel(SUPER_CCLK_DIVIDER,&clkrst->crc_super_cclk_div);
+	}
+
+	/* Fetch the register containing the main CPU complex clock enable */
+	reg = readl(&clkrst->crc_clk_out_enb_l);
+	reg |= CLK_ENB_CPU;
+
+	/*
+	 * Read the register containing the individual CPU clock enables and
+	 * always stop the clock to CPU 1.
+	 */
+	clk = readl(&clkrst->crc_clk_cpu_cmplx);
+	clk |= CPU1_CLK_STP;
+
+	if (enable) {
+		/* Unstop the CPU clock */
+		clk&= ~CPU0_CLK_STP;
+	} else {
+		/* Stop the CPU clock */
+		clk |= CPU0_CLK_STP;
+	}
+
+	writel(clk,&clkrst->crc_clk_cpu_cmplx);
+	writel(reg,&clkrst->crc_clk_out_enb_l);
+}
+
+static int is_cpu_powered(void)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+	return (readl(&pmc->pmc_pwrgate_status)&  CPU_PWRED) ? 1 : 0;
+}
+
+static void remove_cpu_io_clamps(void)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+	u32 reg;
+
+	/* Remove the clamps on the CPU I/O signals */
+	reg = readl(&pmc->pmc_remove_clamping);
+	reg |= CPU_CLMP;
+	writel(reg,&pmc->pmc_remove_clamping);
+
+	/* Give I/O signals time to stabilize */
+	udelay(IO_STABILIZATION_DELAY);
+}
+
+static void powerup_cpu(void)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+	u32 reg;
+

arch/arm/cpu/armv7/tegra2/ap20.c line 115

Quoted 10 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +10 -0
+	if (!is_cpu_powered()) {
+		/* Toggle the CPU power state (OFF ->  ON) */
+		reg = readl(&pmc->pmc_pwrgate_toggle);
+		reg&= PARTID_CP;
+		reg |= START_CP;
+		writel(reg,&pmc->pmc_pwrgate_toggle);
+
+		/* Wait for the power to come up */
+		while (!is_cpu_powered())
+			;			/* Do nothing */

What if power never comes up?

Quoted 118 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +118 -0
+		/*
+		 * Remove the I/O clamps from CPU power partition.
+		 * Recommended only on a Warm boot, if the CPU partition gets
+		 * power gated. Shouldn't cause any harm when called after a
+		 * cold boot according to HW, probably just redundant.
+		 */
+		remove_cpu_io_clamps();
+	}
+}
+
+static void enable_cpu_power_rail(void)
+{
+	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+	u32 reg;
+
+	reg = readl(&pmc->pmc_cntrl);
+	reg |= CPUPWRREQ_OE;
+	writel(reg,&pmc->pmc_cntrl);
+
+	/*
+	 * The TI PMU65861C needs a 3.75ms delay between enabling
+	 * the power rail and enabling the CPU clock.  This delay
+	 * between SM1EN and SM1 is for switching time + the ramp
+	 * up of the voltage to the CPU (VDD_CPU from PMU).
+	 */
+	udelay(3750);
+}
+
+static void reset_A9_cpu(int reset)
+{
+	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 reg, cpu;
+
+	/*
+	* NOTE:  Regardless of whether the request is to hold the CPU in reset
+	*        or take it out of reset, every processor in the CPU complex
+	*        except the master (CPU 0) will be held in reset because the
+	*        AVP only talks to the master. The AVP does not know that there
+	*        are multiple processors in the CPU complex.
+	*/
+
+	/* Hold CPU 1 in reset */
+	cpu = SET_DBGRESET1 | SET_DERESET1 | SET_CPURESET1;
+	writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+	reg = readl(&clkrst->crc_rst_dev_l);
+	if (reset) {
+		/* Now place CPU0 into reset */
+		cpu |= SET_DBGRESET0 | SET_DERESET0 | SET_CPURESET0;
+		writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+		/* Enable master CPU reset */
+		reg |= SWR_CPU_RST;
+	} else {
+		/* Take CPU0 out of reset */
+		cpu = CLR_DBGRESET0 | CLR_DERESET0 | CLR_CPURESET0;
+		writel(cpu,&clkrst->crc_cpu_cmplx_clr);
+
+		/* Disable master CPU reset */
+		reg&= ~SWR_CPU_RST;
+	}
+
+	writel(reg,&clkrst->crc_rst_dev_l);
+}
+
+static void clock_enable_coresight(int enable)
+{
+	struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	u32 rst, clk, src;
+
+	rst = readl(&clkrst->crc_rst_dev_u);
+	clk = readl(&clkrst->crc_clk_out_enb_u);
+
+	if (enable) {
+		rst&= ~SWR_CSITE_RST;
+		clk |= CLK_ENB_CSITE;
+	} else {
+		rst |= SWR_CSITE_RST;
+		clk&= ~CLK_ENB_CSITE;
+	}
+
+	writel(clk,&clkrst->crc_clk_out_enb_u);
+	writel(rst,&clkrst->crc_rst_dev_u);
+
+	if (enable) {
+		/*
+		 * Put CoreSight on PLLP_OUT0 (216 MHz) and divide it down by
+		 *  1.5, giving an effective frequency of 144MHz.
+		 * Set PLLP_OUT0 [bits31:30 = 00], and use a 7.1 divisor
+		 *  (bits 7:0), so 00000001b == 1.5 (n+1 + .5)
+		 */
+		src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000);
+		writel(src,&clkrst->crc_clk_src_csite);
+
+		/* Unlock the CPU CoreSight interfaces */
+		rst = 0xC5ACCE55;
+		writel(rst, CSITE_CPU_DBG0_LAR);
+		writel(rst, CSITE_CPU_DBG1_LAR);
+	}
+}
+
+void start_cpu(u32 reset_vector)
+{
+	/* Enable VDD_CPU */
+	enable_cpu_power_rail();
+
+	/* Hold the CPUs in reset */
+	reset_A9_cpu(1);
+
+	/* Disable the CPU clock */
+	enable_cpu_clock(0);
+
+	/* Enable CoreSight */
+	clock_enable_coresight(1);
+
+	/*
+	 * Set the entry point for CPU execution from reset,
+	 *  if it's a non-zero value.

arch/arm/cpu/armv7/tegra2/ap20.c line 244

Quoted 10 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +10 -0
+	 */
+	if (reset_vector)
+		writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+	/* Enable the CPU clock */
+	enable_cpu_clock(1);
+
+	/* If the CPU doesn't already have power, power it up */
+	if (!is_cpu_powered())
+		powerup_cpu();

For my education (I don't know Tegra2) haven't the AVP already enabled the CPU power rail and waited 3.75 ms for it to come up? If so, what could prevent the CPU from being powered now?

arch/arm/cpu/armv7/tegra2/ap20.c line 257

Quoted 12 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +12 -0
+	/* Take the CPU out of reset */
+	reset_A9_cpu(0);
+}
+
+
+void halt_avp(void)
+{
+	for (;;) {
+		writel((HALT_COP_EVENT_JTAG | HALT_COP_EVENT_IRQ_1 \
+			| HALT_COP_EVENT_FIQ_1 | (FLOW_MODE_STOP<<29)),
+			FLOW_CTLR_HALT_COP_EVENTS);
+	}

Must the write be repeated indefinitely? Can it not be done once then followed by an empty for(;;) ?

Amicalement,

Signature · 1 line
Albert.
author
#2 lore raw

PATCH v4 1/1 arm: Tegra2: add support for A9 CPU init

Albert,

Quoted 124 lines · Albert ARIBAUD

Le 12/04/2011 23:02, Tom Warren a ?crit :

Quoted 122 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +37 -0
new file mode 100644
index 0000000..075341e
@@ -0,0 +1,325 @@
+/*
+* (C) Copyright 2010-2011
+* 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 "ap20.h"
+#include<asm/io.h>
+#include<asm/arch/tegra2.h>
+#include<asm/arch/clk_rst.h>
+#include<asm/arch/pmc.h>
+#include<asm/arch/pinmux.h>
+#include<asm/arch/scu.h>
+#include<common.h>
+
+u32 s_first_boot = 1;
+
+static void enable_cpu_clock(int enable)
+{
+ ? ? ? struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr

*)NV_PA_CLK_RST_BASE;
+ ? ? ? u32 reg, clk;
+
+ ? ? ? /*
+ ? ? ? ?* NOTE:
+ ? ? ? ?* Regardless of whether the request is to enable or disable the CPU
+ ? ? ? ?* clock, every processor in the CPU complex except the master (CPU 0)
+ ? ? ? ?* will have it's clock stopped because the AVP only talks to the
+ ? ? ? ?* master. The AVP does not know (nor does it need to know) that there
+ ? ? ? ?* are multiple processors in the CPU complex.
+ ? ? ? ?*/

arch/arm/cpu/armv7/tegra2/ap20.c new file +10 -0
+
+ ? ? ? if (enable) {
+ ? ? ? ? ? ? ? /* Wait until all clocks are stable */
+ ? ? ? ? ? ? ? udelay(PLL_STABILIZATION_DELAY);
+
+ ? ? ? ? ? ? ? writel(CCLK_BURST_POLICY,&clkrst->crc_cclk_brst_pol);
+ ? ? ? ? ? ? ? writel(SUPER_CCLK_DIVIDER,&clkrst->crc_super_cclk_div);
+ ? ? ? }
+
+ ? ? ? /* Fetch the register containing the main CPU complex clock enable

*/
+ ? ? ? reg = readl(&clkrst->crc_clk_out_enb_l);
+ ? ? ? reg |= CLK_ENB_CPU;
+
+ ? ? ? /*
+ ? ? ? ?* Read the register containing the individual CPU clock enables and
+ ? ? ? ?* always stop the clock to CPU 1.
+ ? ? ? ?*/
+ ? ? ? clk = readl(&clkrst->crc_clk_cpu_cmplx);
+ ? ? ? clk |= CPU1_CLK_STP;

arch/arm/cpu/armv7/tegra2/ap20.c new file +43 -0
+
+ ? ? ? if (enable) {
+ ? ? ? ? ? ? ? /* Unstop the CPU clock */
+ ? ? ? ? ? ? ? clk&= ~CPU0_CLK_STP;
+ ? ? ? } else {
+ ? ? ? ? ? ? ? /* Stop the CPU clock */
+ ? ? ? ? ? ? ? clk |= CPU0_CLK_STP;
+ ? ? ? }
+
+ ? ? ? writel(clk,&clkrst->crc_clk_cpu_cmplx);
+ ? ? ? writel(reg,&clkrst->crc_clk_out_enb_l);
+}
+
+static int is_cpu_powered(void)
+{
+ ? ? ? struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+ ? ? ? return (readl(&pmc->pmc_pwrgate_status)& ?CPU_PWRED) ? 1 : 0;
+}
+
+static void remove_cpu_io_clamps(void)
+{
+ ? ? ? struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+ ? ? ? u32 reg;
+
+ ? ? ? /* Remove the clamps on the CPU I/O signals */
+ ? ? ? reg = readl(&pmc->pmc_remove_clamping);
+ ? ? ? reg |= CPU_CLMP;
+ ? ? ? writel(reg,&pmc->pmc_remove_clamping);
+
+ ? ? ? /* Give I/O signals time to stabilize */
+ ? ? ? udelay(IO_STABILIZATION_DELAY);
+}
+
+static void powerup_cpu(void)
+{
+ ? ? ? struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+ ? ? ? u32 reg;
+
+ ? ? ? if (!is_cpu_powered()) {
+ ? ? ? ? ? ? ? /* Toggle the CPU power state (OFF -> ?ON) */
+ ? ? ? ? ? ? ? reg = readl(&pmc->pmc_pwrgate_toggle);
+ ? ? ? ? ? ? ? reg&= PARTID_CP;
Quoted 8 lines
Quoted 6 lines

+ ? ? ? ? ? ? ? reg |= START_CP;
+ ? ? ? ? ? ? ? writel(reg,&pmc->pmc_pwrgate_toggle); +
+ ? ? ? ? ? ? ? /* Wait for the power to come up */
+ ? ? ? ? ? ? ? while (!is_cpu_powered())
+ ? ? ? ? ? ? ? ? ? ? ? ; ? ? ? ? ? ? ? ? ? ? ? /* Do nothing */

What if power never comes up?

Then the system is hung. I can put a printf here, if you'd like.

Quoted 134 lines
Quoted 134 lines

+ ? ? ? ? ? ? ? /*
+ ? ? ? ? ? ? ? ?* Remove the I/O clamps from CPU power partition.
+ ? ? ? ? ? ? ? ?* Recommended only on a Warm boot, if the CPU partition gets
+ ? ? ? ? ? ? ? ?* power gated. Shouldn't cause any harm when called after a
+ ? ? ? ? ? ? ? ?* cold boot according to HW, probably just redundant.
+ ? ? ? ? ? ? ? ?*/
+ ? ? ? ? ? ? ? remove_cpu_io_clamps();
+ ? ? ? }

arch/arm/cpu/armv7/tegra2/ap20.c new file +23 -0
+}
+
+static void enable_cpu_power_rail(void)
+{
+ ? ? ? struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+ ? ? ? u32 reg;
+
+ ? ? ? reg = readl(&pmc->pmc_cntrl);
+ ? ? ? reg |= CPUPWRREQ_OE;
+ ? ? ? writel(reg,&pmc->pmc_cntrl);
+
+ ? ? ? /*
+ ? ? ? ?* The TI PMU65861C needs a 3.75ms delay between enabling
+ ? ? ? ?* the power rail and enabling the CPU clock. ?This delay
+ ? ? ? ?* between SM1EN and SM1 is for switching time + the ramp
+ ? ? ? ?* up of the voltage to the CPU (VDD_CPU from PMU).
+ ? ? ? ?*/
+ ? ? ? udelay(3750);
+}
+
+static void reset_A9_cpu(int reset)
+{
+ ? ? ? struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr

*)NV_PA_CLK_RST_BASE;
+ ? ? ? u32 reg, cpu;
+
+ ? ? ? /*
+ ? ? ? * NOTE: ?Regardless of whether the request is to hold the CPU in reset
+ ? ? ? * ? ? ? ?or take it out of reset, every processor in the CPU complex
+ ? ? ? * ? ? ? ?except the master (CPU 0) will be held in reset because the
+ ? ? ? * ? ? ? ?AVP only talks to the master. The AVP does not know that there
+ ? ? ? * ? ? ? ?are multiple processors in the CPU complex.
+ ? ? ? */

arch/arm/cpu/armv7/tegra2/ap20.c new file +28 -0
+
+ ? ? ? /* Hold CPU 1 in reset */
+ ? ? ? cpu = SET_DBGRESET1 | SET_DERESET1 | SET_CPURESET1;
+ ? ? ? writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+ ? ? ? reg = readl(&clkrst->crc_rst_dev_l);
+ ? ? ? if (reset) {
+ ? ? ? ? ? ? ? /* Now place CPU0 into reset */
+ ? ? ? ? ? ? ? cpu |= SET_DBGRESET0 | SET_DERESET0 | SET_CPURESET0;
+ ? ? ? ? ? ? ? writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+ ? ? ? ? ? ? ? /* Enable master CPU reset */
+ ? ? ? ? ? ? ? reg |= SWR_CPU_RST;
+ ? ? ? } else {
+ ? ? ? ? ? ? ? /* Take CPU0 out of reset */
+ ? ? ? ? ? ? ? cpu = CLR_DBGRESET0 | CLR_DERESET0 | CLR_CPURESET0;
+ ? ? ? ? ? ? ? writel(cpu,&clkrst->crc_cpu_cmplx_clr);
+
+ ? ? ? ? ? ? ? /* Disable master CPU reset */
+ ? ? ? ? ? ? ? reg&= ~SWR_CPU_RST;
+ ? ? ? }
+
+ ? ? ? writel(reg,&clkrst->crc_rst_dev_l);
+}
+
+static void clock_enable_coresight(int enable)
+{
+ ? ? ? struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr

*)NV_PA_CLK_RST_BASE;
+ ? ? ? u32 rst, clk, src;

arch/arm/cpu/armv7/tegra2/ap20.c new file +18 -0
+
+ ? ? ? rst = readl(&clkrst->crc_rst_dev_u);
+ ? ? ? clk = readl(&clkrst->crc_clk_out_enb_u);
+
+ ? ? ? if (enable) {
+ ? ? ? ? ? ? ? rst&= ~SWR_CSITE_RST;
+ ? ? ? ? ? ? ? clk |= CLK_ENB_CSITE;
+ ? ? ? } else {
+ ? ? ? ? ? ? ? rst |= SWR_CSITE_RST;
+ ? ? ? ? ? ? ? clk&= ~CLK_ENB_CSITE;
+ ? ? ? }
+
+ ? ? ? writel(clk,&clkrst->crc_clk_out_enb_u);
+ ? ? ? writel(rst,&clkrst->crc_rst_dev_u);
+
+ ? ? ? if (enable) {
+ ? ? ? ? ? ? ? /*
+ ? ? ? ? ? ? ? ?* Put CoreSight on PLLP_OUT0 (216 MHz) and divide it down

by
+ ? ? ? ? ? ? ? ?* ?1.5, giving an effective frequency of 144MHz.
+ ? ? ? ? ? ? ? ?* Set PLLP_OUT0 [bits31:30 = 00], and use a 7.1 divisor
+ ? ? ? ? ? ? ? ?* ?(bits 7:0), so 00000001b == 1.5 (n+1 + .5)
+ ? ? ? ? ? ? ? ?*/
+ ? ? ? ? ? ? ? src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000);
+ ? ? ? ? ? ? ? writel(src,&clkrst->crc_clk_src_csite);

arch/arm/cpu/armv7/tegra2/ap20.c new file +31 -0
+
+ ? ? ? ? ? ? ? /* Unlock the CPU CoreSight interfaces */
+ ? ? ? ? ? ? ? rst = 0xC5ACCE55;
+ ? ? ? ? ? ? ? writel(rst, CSITE_CPU_DBG0_LAR);
+ ? ? ? ? ? ? ? writel(rst, CSITE_CPU_DBG1_LAR);
+ ? ? ? }
+}
+
+void start_cpu(u32 reset_vector)
+{
+ ? ? ? /* Enable VDD_CPU */
+ ? ? ? enable_cpu_power_rail();
+
+ ? ? ? /* Hold the CPUs in reset */
+ ? ? ? reset_A9_cpu(1);
+
+ ? ? ? /* Disable the CPU clock */
+ ? ? ? enable_cpu_clock(0);
+
+ ? ? ? /* Enable CoreSight */
+ ? ? ? clock_enable_coresight(1);
+
+ ? ? ? /*
+ ? ? ? ?* Set the entry point for CPU execution from reset,
+ ? ? ? ?* ?if it's a non-zero value.
+ ? ? ? ?*/
+ ? ? ? if (reset_vector)
+ ? ? ? ? ? ? ? writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+ ? ? ? /* Enable the CPU clock */
+ ? ? ? enable_cpu_clock(1);
Quoted 8 lines
Quoted 4 lines

+
+ ? ? ? /* If the CPU doesn't already have power, power it up */
+ ? ? ? if (!is_cpu_powered())
+ ? ? ? ? ? ? ? powerup_cpu();

For my education (I don't know Tegra2) haven't the AVP already enabled the CPU power rail and waited 3.75 ms for it to come up? If so, what could prevent the CPU from being powered now?

True, this is just an additional check that was in the code I ported. I'll remove it as redundant.

Quoted 16 lines

arch/arm/cpu/armv7/tegra2/ap20.c

Quoted 13 lines

+ ? ? ? /* Take the CPU out of reset */
+ ? ? ? reset_A9_cpu(0);

arch/arm/cpu/armv7/tegra2/ap20.c new file +10 -0
+}
+
+
+void halt_avp(void)
+{
+ ? ? ? for (;;) {
+ ? ? ? ? ? ? ? writel((HALT_COP_EVENT_JTAG | HALT_COP_EVENT_IRQ_1 \
+ ? ? ? ? ? ? ? ? ? ? ? | HALT_COP_EVENT_FIQ_1 | (FLOW_MODE_STOP<<29)),
+ ? ? ? ? ? ? ? ? ? ? ? FLOW_CTLR_HALT_COP_EVENTS);
+ ? ? ? }

Must the write be repeated indefinitely? Can it not be done once then followed by an empty for(;;) ?

IIRC, there was an additional infinite for (;;) in previous code, but that was removed to satisfy a reviewer.
I can change it if you insist, but I don't know if it's written this way (ported from legacy bootloader) on purpose, i.e. to keep the AVP from spontaneously waking up and executing code from SRAM, etc. If it's not a deal-breaker, I'd prefer to leave it as-is.

Quoted 5 lines

Amicalement,
--
Albert.

Thanks for the quick review!

Tom

#3 lore raw

PATCH v4 1/1 arm: Tegra2: add support for A9 CPU init

Hi Tom,

Le 13/04/2011 22:21, Tom Warren a ?crit :

Quoted 7 lines
Quoted 6 lines

arch/arm/cpu/armv7/tegra2/ap20.c line 115

Quoted 4 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +4 -0
+
+               /* Wait for the power to come up */
+               while (!is_cpu_powered())
+                       ;                       /* Do nothing */

What if power never comes up?

Then the system is hung. I can put a printf here, if you'd like.

Is the system hung? Can it really not proceed to the prompt? Anyway, at least, yes, a printf would be welcome.

Quoted 161 lines
Quoted 134 lines
Quoted 134 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +3 -0
+               /*
+                * Remove the I/O clamps from CPU power partition.
+                * Recommended only on a Warm boot, if the CPU partition

gets
+ * power gated. Shouldn't cause any harm when called after
a

arch/arm/cpu/armv7/tegra2/ap20.c new file +27 -0
+                * cold boot according to HW, probably just redundant.
+                */
+               remove_cpu_io_clamps();
+       }
+}
+
+static void enable_cpu_power_rail(void)
+{
+       struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+       u32 reg;
+
+       reg = readl(&pmc->pmc_cntrl);
+       reg |= CPUPWRREQ_OE;
+       writel(reg,&pmc->pmc_cntrl);
+
+       /*
+        * The TI PMU65861C needs a 3.75ms delay between enabling
+        * the power rail and enabling the CPU clock.  This delay
+        * between SM1EN and SM1 is for switching time + the ramp
+        * up of the voltage to the CPU (VDD_CPU from PMU).
+        */
+       udelay(3750);
+}
+
+static void reset_A9_cpu(int reset)
+{
+       struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr

*)NV_PA_CLK_RST_BASE;

arch/arm/cpu/armv7/tegra2/ap20.c new file +4 -0
+       u32 reg, cpu;
+
+       /*
+       * NOTE:  Regardless of whether the request is to hold the CPU in

reset
+ * or take it out of reset, every processor in the CPU
complex
+ * except the master (CPU 0) will be held in reset because
the
+ * AVP only talks to the master. The AVP does not know that
there

arch/arm/cpu/armv7/tegra2/ap20.c new file +30 -0
+       *        are multiple processors in the CPU complex.
+       */
+
+       /* Hold CPU 1 in reset */
+       cpu = SET_DBGRESET1 | SET_DERESET1 | SET_CPURESET1;
+       writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+       reg = readl(&clkrst->crc_rst_dev_l);
+       if (reset) {
+               /* Now place CPU0 into reset */
+               cpu |= SET_DBGRESET0 | SET_DERESET0 | SET_CPURESET0;
+               writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+               /* Enable master CPU reset */
+               reg |= SWR_CPU_RST;
+       } else {
+               /* Take CPU0 out of reset */
+               cpu = CLR_DBGRESET0 | CLR_DERESET0 | CLR_CPURESET0;
+               writel(cpu,&clkrst->crc_cpu_cmplx_clr);
+
+               /* Disable master CPU reset */
+               reg&= ~SWR_CPU_RST;
+       }
+
+       writel(reg,&clkrst->crc_rst_dev_l);
+}
+
+static void clock_enable_coresight(int enable)
+{
+       struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr

*)NV_PA_CLK_RST_BASE;

arch/arm/cpu/armv7/tegra2/ap20.c new file +19 -0
+       u32 rst, clk, src;
+
+       rst = readl(&clkrst->crc_rst_dev_u);
+       clk = readl(&clkrst->crc_clk_out_enb_u);
+
+       if (enable) {
+               rst&= ~SWR_CSITE_RST;
+               clk |= CLK_ENB_CSITE;
+       } else {
+               rst |= SWR_CSITE_RST;
+               clk&= ~CLK_ENB_CSITE;
+       }
+
+       writel(clk,&clkrst->crc_clk_out_enb_u);
+       writel(rst,&clkrst->crc_rst_dev_u);
+
+       if (enable) {
+               /*
+                * Put CoreSight on PLLP_OUT0 (216 MHz) and divide it down

by

arch/arm/cpu/armv7/tegra2/ap20.c new file +37 -0
+                *  1.5, giving an effective frequency of 144MHz.
+                * Set PLLP_OUT0 [bits31:30 = 00], and use a 7.1 divisor
+                *  (bits 7:0), so 00000001b == 1.5 (n+1 + .5)
+                */
+               src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000);
+               writel(src,&clkrst->crc_clk_src_csite);
+
+               /* Unlock the CPU CoreSight interfaces */
+               rst = 0xC5ACCE55;
+               writel(rst, CSITE_CPU_DBG0_LAR);
+               writel(rst, CSITE_CPU_DBG1_LAR);
+       }
+}
+
+void start_cpu(u32 reset_vector)
+{
+       /* Enable VDD_CPU */
+       enable_cpu_power_rail();
+
+       /* Hold the CPUs in reset */
+       reset_A9_cpu(1);
+
+       /* Disable the CPU clock */
+       enable_cpu_clock(0);
+
+       /* Enable CoreSight */
+       clock_enable_coresight(1);
+
+       /*
+        * Set the entry point for CPU execution from reset,
+        *  if it's a non-zero value.
+        */
+       if (reset_vector)
+               writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+       /* Enable the CPU clock */
+       enable_cpu_clock(1);
Quoted 8 lines

arch/arm/cpu/armv7/tegra2/ap20.c line 244

Quoted 4 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +4 -0
+
+       /* If the CPU doesn't already have power, power it up */
+       if (!is_cpu_powered())
+               powerup_cpu();

For my education (I don't know Tegra2) haven't the AVP already enabled the CPU power rail and waited 3.75 ms for it to come up? If so, what could prevent the CPU from being powered now?

True, this is just an additional check that was in the code I ported. I'll remove it as redundant.

Quoted 16 lines

arch/arm/cpu/armv7/tegra2/ap20.c line 257

Quoted 13 lines
arch/arm/cpu/armv7/tegra2/ap20.c new file +12 -0
+       /* Take the CPU out of reset */
+       reset_A9_cpu(0);
+}
+
+
+void halt_avp(void)
+{
+       for (;;) {
+               writel((HALT_COP_EVENT_JTAG | HALT_COP_EVENT_IRQ_1 \
+                       | HALT_COP_EVENT_FIQ_1 | (FLOW_MODE_STOP<<29)),
+                       FLOW_CTLR_HALT_COP_EVENTS);
+       }

Must the write be repeated indefinitely? Can it not be done once then followed by an empty for(;;) ?

Quoted 2 lines

IIRC, there was an additional infinite for (;;) in previous code, but that was removed to satisfy a reviewer.

:)

Quoted 4 lines

I can change it if you insist, but I don't know if it's written this way (ported from legacy bootloader) on purpose, i.e. to keep the AVP from spontaneously waking up and executing code from SRAM, etc. If it's not a deal-breaker, I'd prefer to leave it as-is.

That's ok, leave it as it is.

Amicalement,

Signature · 1 line
Albert.
author
#4 lore raw

PATCH v4 1/1 arm: Tegra2: add support for A9 CPU init

Albert,

Quoted 9 lines · Albert ARIBAUD

Hi Tom,

Le 13/04/2011 22:21, Tom Warren a ?crit :

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines

+
+ ? ? ? ? ? ? ? /* Wait for the power to come up */
+ ? ? ? ? ? ? ? while (!is_cpu_powered())
+ ? ? ? ? ? ? ? ? ? ? ? ; ? ? ? ? ? ? ? ? ? ? ? /* Do nothing */

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

What if power never comes up?

Then the system is hung. I can put a printf here, if you'd like.

Is the system hung? Can it really not proceed to the prompt? Anyway, at least, yes, a printf would be welcome.

It's hung because it's looping on the while ;. While it's possible we could limp along on the AVP only, that's not a viable config, and I don't know what a partially enabled CPU complex is going to do - interrupt storm, memory corruption, etc. I'll just put in a printf for the failure case. Thanks.

Quoted 170 lines
Quoted 167 lines
Quoted 138 lines
Quoted 138 lines

+ ? ? ? ? ? ? ? /*
+ ? ? ? ? ? ? ? ?* Remove the I/O clamps from CPU power partition.
+ ? ? ? ? ? ? ? ?* Recommended only on a Warm boot, if the CPU partition gets
+ ? ? ? ? ? ? ? ?* power gated. Shouldn't cause any harm when called after
a
+ ? ? ? ? ? ? ? ?* cold boot according to HW, probably just redundant.
+ ? ? ? ? ? ? ? ?*/
+ ? ? ? ? ? ? ? remove_cpu_io_clamps();
+ ? ? ? }

(quoted diff) +23 -0
+}
+
+static void enable_cpu_power_rail(void)
+{
+ ? ? ? struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+ ? ? ? u32 reg;
+
+ ? ? ? reg = readl(&pmc->pmc_cntrl);
+ ? ? ? reg |= CPUPWRREQ_OE;
+ ? ? ? writel(reg,&pmc->pmc_cntrl);
+
+ ? ? ? /*
+ ? ? ? ?* The TI PMU65861C needs a 3.75ms delay between enabling
+ ? ? ? ?* the power rail and enabling the CPU clock. ?This delay
+ ? ? ? ?* between SM1EN and SM1 is for switching time + the ramp
+ ? ? ? ?* up of the voltage to the CPU (VDD_CPU from PMU).
+ ? ? ? ?*/
+ ? ? ? udelay(3750);
+}
+
+static void reset_A9_cpu(int reset)
+{
+ ? ? ? struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr

*)NV_PA_CLK_RST_BASE;
+ ? ? ? u32 reg, cpu;
+
+ ? ? ? /*
+ ? ? ? * NOTE: ?Regardless of whether the request is to hold the CPU in reset
+ ? ? ? * ? ? ? ?or take it out of reset, every processor in the CPU complex
+ ? ? ? * ? ? ? ?except the master (CPU 0) will be held in reset because the
+ ? ? ? * ? ? ? ?AVP only talks to the master. The AVP does not know that
there
+ ? ? ? * ? ? ? ?are multiple processors in the CPU complex.
+ ? ? ? */

(quoted diff) +28 -0
+
+ ? ? ? /* Hold CPU 1 in reset */
+ ? ? ? cpu = SET_DBGRESET1 | SET_DERESET1 | SET_CPURESET1;
+ ? ? ? writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+ ? ? ? reg = readl(&clkrst->crc_rst_dev_l);
+ ? ? ? if (reset) {
+ ? ? ? ? ? ? ? /* Now place CPU0 into reset */
+ ? ? ? ? ? ? ? cpu |= SET_DBGRESET0 | SET_DERESET0 | SET_CPURESET0;
+ ? ? ? ? ? ? ? writel(cpu,&clkrst->crc_cpu_cmplx_set);
+
+ ? ? ? ? ? ? ? /* Enable master CPU reset */
+ ? ? ? ? ? ? ? reg |= SWR_CPU_RST;
+ ? ? ? } else {
+ ? ? ? ? ? ? ? /* Take CPU0 out of reset */
+ ? ? ? ? ? ? ? cpu = CLR_DBGRESET0 | CLR_DERESET0 | CLR_CPURESET0;
+ ? ? ? ? ? ? ? writel(cpu,&clkrst->crc_cpu_cmplx_clr);
+
+ ? ? ? ? ? ? ? /* Disable master CPU reset */
+ ? ? ? ? ? ? ? reg&= ~SWR_CPU_RST;
+ ? ? ? }
+
+ ? ? ? writel(reg,&clkrst->crc_rst_dev_l);
+}
+
+static void clock_enable_coresight(int enable)
+{
+ ? ? ? struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr

*)NV_PA_CLK_RST_BASE;
+ ? ? ? u32 rst, clk, src;

(quoted diff) +18 -0
+
+ ? ? ? rst = readl(&clkrst->crc_rst_dev_u);
+ ? ? ? clk = readl(&clkrst->crc_clk_out_enb_u);
+
+ ? ? ? if (enable) {
+ ? ? ? ? ? ? ? rst&= ~SWR_CSITE_RST;
+ ? ? ? ? ? ? ? clk |= CLK_ENB_CSITE;
+ ? ? ? } else {
+ ? ? ? ? ? ? ? rst |= SWR_CSITE_RST;
+ ? ? ? ? ? ? ? clk&= ~CLK_ENB_CSITE;
+ ? ? ? }
+
+ ? ? ? writel(clk,&clkrst->crc_clk_out_enb_u);
+ ? ? ? writel(rst,&clkrst->crc_rst_dev_u);
+
+ ? ? ? if (enable) {
+ ? ? ? ? ? ? ? /*
+ ? ? ? ? ? ? ? ?* Put CoreSight on PLLP_OUT0 (216 MHz) and divide it

down
by
+ ? ? ? ? ? ? ? ?* ?1.5, giving an effective frequency of 144MHz.
+ ? ? ? ? ? ? ? ?* Set PLLP_OUT0 [bits31:30 = 00], and use a 7.1 divisor
+ ? ? ? ? ? ? ? ?* ?(bits 7:0), so 00000001b == 1.5 (n+1 + .5)
+ ? ? ? ? ? ? ? ?*/
+ ? ? ? ? ? ? ? src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000);
+ ? ? ? ? ? ? ? writel(src,&clkrst->crc_clk_src_csite);

(quoted diff) +31 -0
+
+ ? ? ? ? ? ? ? /* Unlock the CPU CoreSight interfaces */
+ ? ? ? ? ? ? ? rst = 0xC5ACCE55;
+ ? ? ? ? ? ? ? writel(rst, CSITE_CPU_DBG0_LAR);
+ ? ? ? ? ? ? ? writel(rst, CSITE_CPU_DBG1_LAR);
+ ? ? ? }
+}
+
+void start_cpu(u32 reset_vector)
+{
+ ? ? ? /* Enable VDD_CPU */
+ ? ? ? enable_cpu_power_rail();
+
+ ? ? ? /* Hold the CPUs in reset */
+ ? ? ? reset_A9_cpu(1);
+
+ ? ? ? /* Disable the CPU clock */
+ ? ? ? enable_cpu_clock(0);
+
+ ? ? ? /* Enable CoreSight */
+ ? ? ? clock_enable_coresight(1);
+
+ ? ? ? /*
+ ? ? ? ?* Set the entry point for CPU execution from reset,
+ ? ? ? ?* ?if it's a non-zero value.
+ ? ? ? ?*/
+ ? ? ? if (reset_vector)
+ ? ? ? ? ? ? ? writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+ ? ? ? /* Enable the CPU clock */
+ ? ? ? enable_cpu_clock(1);
Quoted 8 lines
Quoted 4 lines

+
+ ? ? ? /* If the CPU doesn't already have power, power it up */
+ ? ? ? if (!is_cpu_powered())
+ ? ? ? ? ? ? ? powerup_cpu();

For my education (I don't know Tegra2) haven't the AVP already enabled the CPU power rail and waited 3.75 ms for it to come up? If so, what could prevent the CPU from being powered now?

True, this is just an additional check that was in the code I ported. I'll remove it as redundant.

Quoted 16 lines

arch/arm/cpu/armv7/tegra2/ap20.c

Quoted 13 lines

+ ? ? ? /* Take the CPU out of reset */
+ ? ? ? reset_A9_cpu(0);

arch/arm/cpu/armv7/tegra2/ap20.c new file +10 -0
+}
+
+
+void halt_avp(void)
+{
+ ? ? ? for (;;) {
+ ? ? ? ? ? ? ? writel((HALT_COP_EVENT_JTAG | HALT_COP_EVENT_IRQ_1 \
+ ? ? ? ? ? ? ? ? ? ? ? | HALT_COP_EVENT_FIQ_1 | (FLOW_MODE_STOP<<29)),
+ ? ? ? ? ? ? ? ? ? ? ? FLOW_CTLR_HALT_COP_EVENTS);
+ ? ? ? }

Must the write be repeated indefinitely? Can it not be done once then followed by an empty for(;;) ?

Quoted 2 lines

IIRC, there was an additional infinite for (;;) in previous code, but that was removed to satisfy a reviewer.

Quoted 8 lines

:)

Quoted 4 lines

I can change it if you insist, but I don't know if it's written this way (ported from legacy bootloader) on purpose, i.e. to keep the AVP from spontaneously waking up and executing code from SRAM, etc. ?If it's not a deal-breaker, I'd prefer to leave it as-is.

That's ok, leave it as it is.

Great. Thanks!

Quoted 5 lines

Amicalement,
--
Albert.