[U-Boot] [PATCH V3] arm: Tegra2: add support for A9 CPU init
Albert ARIBAUD
albert.aribaud at free.fr
Fri Mar 25 19:44:04 CET 2011
Le 25/03/2011 18:06, Tom Warren a écrit :
> Signed-off-by: Tom Warren<twarren at nvidia.com>
> ---
> 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
>
> arch/arm/cpu/armv7/start.S | 12 +
> arch/arm/cpu/armv7/tegra2/Makefile | 2 +-
> arch/arm/cpu/armv7/tegra2/ap20.c | 366 ++++++++++++++++++++++++++++
> arch/arm/cpu/armv7/tegra2/ap20.h | 105 ++++++++
> arch/arm/cpu/armv7/tegra2/lowlevel_init.S | 70 ++++++
> 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 | 5 +
> 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, 681 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
>
> diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S
> index cb4f92f..4b36693 100644
> --- a/arch/arm/cpu/armv7/start.S
> +++ b/arch/arm/cpu/armv7/start.S
> @@ -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.
> */
> diff --git a/arch/arm/cpu/armv7/tegra2/Makefile b/arch/arm/cpu/armv7/tegra2/Makefile
> index 687c887..f1ea915 100644
> --- a/arch/arm/cpu/armv7/tegra2/Makefile
> +++ b/arch/arm/cpu/armv7/tegra2/Makefile
> @@ -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))
> diff --git a/arch/arm/cpu/armv7/tegra2/ap20.c b/arch/arm/cpu/armv7/tegra2/ap20.c
> new file mode 100644
> index 0000000..d0ad41f
> --- /dev/null
> +++ b/arch/arm/cpu/armv7/tegra2/ap20.c
> @@ -0,0 +1,366 @@
> +/*
> +* (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;
> +
> + 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 the CPU never come up?
> + /*
> + * 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.
> + */
> + 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 power now?
> + /* 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);
> + }
> +}
You're writing this indefinitely inside the loop. Is this necessary? I'd
rather expect a single write then an empty for(;;) loop.
> +void enable_scu(void)
> +{
> + struct scu_ctlr *scu = (struct scu_ctlr *)NV_PA_ARM_PERIPHBASE;
> + u32 reg;
> +
> + /* If SCU already setup/enabled, return */
> + if (readl(&scu->scu_ctrl)& SCU_CTRL_ENABLE)
> + return;
> +
> + /* Invalidate all ways for all processors */
> + writel(0xFFFF,&scu->scu_inv_all);
> +
> + /* Enable SCU - bit 0 */
> + reg = readl(&scu->scu_ctrl);
> + reg |= SCU_CTRL_ENABLE;
> + writel(reg,&scu->scu_ctrl);
> +}
> +
> +void init_pmc_scratch(void)
> +{
> + struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
> + int i;
> +
> + /* SCRATCH0 is initialized by the boot ROM and shouldn't be cleared */
> + for (i = 0; i< 23; i++)
> + writel(0,&pmc->pmc_scratch1+i);
> +
> + /* ODMDATA is for kernel use to determine RAM size, LP config, etc. */
> + writel(CONFIG_SYS_BOARD_ODMDATA,&pmc->pmc_scratch20);
> +}
> +
> +void cpu_start(void)
> +{
> + struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
> +
> + /* enable JTAG */
> + writel(0xC0,&pmt->pmt_cfg_ctl);
> +
> + if (s_first_boot) {
> + /*
> + * Need to set this before cold-booting,
> + * otherwise we'll end up in an infinite loop.
> + */
> + s_first_boot = 0;
> + cold_boot();
> + }
> +}
> +
> +void tegra2_start()
> +{
> + if (s_first_boot) {
> + /* Init Debug UART Port (115200 8n1) */
> + uart_init();
> +
> + /* Init PMC scratch memory */
> + init_pmc_scratch();
> + }
> +
> +#ifdef CONFIG_ENABLE_CORTEXA9
> + /* take the mpcore out of reset */
> + cpu_start();
> +
> + /* configure cache */
> + cache_configure();
> +#endif
> +}
> +
> +extern ulong _armboot_start;
> +u32 cpu_boot_stack = CPU_EARLY_BOOT_STACK_LIMIT;
> +u32 avp_boot_stack = AVP_EARLY_BOOT_STACK_LIMIT;
> +u32 proc_tag = PG_UP_TAG_0_PID_CPU& 0xFF;
> +
> +/*
> + * TBD: Move cold_boot() to assembly file.
> + * Values/offsets of the table vars make this difficult.
> + */
TBD is usually "To Be Defined". Use TODO instead.
Amicalement,
--
Albert.
More information about the U-Boot
mailing list