From patchwork Wed Sep 12 22:10:48 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Warren X-Patchwork-Id: 183458 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id C95862C0088 for ; Thu, 13 Sep 2012 08:12:08 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 8843A28090; Thu, 13 Sep 2012 00:12:07 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id rLx7Wo5FJqbn; Thu, 13 Sep 2012 00:12:07 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B08E228095; Thu, 13 Sep 2012 00:11:58 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C20172809D for ; Thu, 13 Sep 2012 00:11:54 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id i26buCWgEcw8 for ; Thu, 13 Sep 2012 00:11:53 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-pb0-f44.google.com (mail-pb0-f44.google.com [209.85.160.44]) by theia.denx.de (Postfix) with ESMTPS id 0ED3E2808D for ; Thu, 13 Sep 2012 00:11:52 +0200 (CEST) Received: by pbbrr4 with SMTP id rr4so3038722pbb.3 for ; Wed, 12 Sep 2012 15:11:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :x-nvconfidentiality; bh=zdUdk2x55D3Oy61Liy10cAbN45aHxbhmOUznXQw8ad8=; b=fKcEStwcuL4BPFlFK0dhFTqEmtHXYchZ4QR42Z14smeSt9RjW0jWa361/CsUaqJk8t P4rhTSP0qQGm2b4uh0i5+3brqEMy6KaLU/g65yw5L+BFX2Ogj9qoVcjEzvq2tbnLKQcI 1BljACvunoIvfdwBB0jVKECGMHtqY9mSdXw+n3a7hM1HrsO324DIXw5h4Huu6VD/Fq5Z NFMnGt/wINkt1G2FuP/+t1Q5B5c4lH54VCTOLD7C6mCtwzt1UTJvc9bPSoN8r4DSy0JF 2Y56oF0IVnbSwscCYq/CGAmuIdNdyZKCtW202TiHzoDDUx0p3ATzRslaibHjWH7mTkSn N2Rg== Received: by 10.66.75.106 with SMTP id b10mr33541090paw.73.1347487911205; Wed, 12 Sep 2012 15:11:51 -0700 (PDT) Received: from localhost.localdomain (ip68-230-103-25.ph.ph.cox.net. [68.230.103.25]) by mx.google.com with ESMTPS id gt2sm12041999pbc.62.2012.09.12.15.11.48 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 12 Sep 2012 15:11:50 -0700 (PDT) From: Tom Warren To: u-boot@lists.denx.de Date: Wed, 12 Sep 2012 15:10:48 -0700 Message-Id: <1347487855-27077-3-git-send-email-twarren@nvidia.com> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: <1347487855-27077-1-git-send-email-twarren@nvidia.com> References: <1347487855-27077-1-git-send-email-twarren@nvidia.com> X-NVConfidentiality: public Cc: swarren@nvidia.com, Tom Warren , trini@ti.com, twarren.nvidia@gmail.com Subject: [U-Boot] [PATCH 2/9] Tegra: T30: Add AVP (arm720t) files X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Signed-off-by: Tom Warren --- arch/arm/cpu/arm720t/tegra30/Makefile | 48 +++ arch/arm/cpu/arm720t/tegra30/board.h | 25 ++ arch/arm/cpu/arm720t/tegra30/config.mk | 26 ++ arch/arm/cpu/arm720t/tegra30/cpu.c | 570 ++++++++++++++++++++++++++++++++ arch/arm/cpu/arm720t/tegra30/cpu.h | 65 ++++ arch/arm/cpu/arm720t/tegra30/spl.c | 132 ++++++++ 6 files changed, 866 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/arm720t/tegra30/Makefile create mode 100644 arch/arm/cpu/arm720t/tegra30/board.h create mode 100644 arch/arm/cpu/arm720t/tegra30/config.mk create mode 100644 arch/arm/cpu/arm720t/tegra30/cpu.c create mode 100644 arch/arm/cpu/arm720t/tegra30/cpu.h create mode 100644 arch/arm/cpu/arm720t/tegra30/spl.c diff --git a/arch/arm/cpu/arm720t/tegra30/Makefile b/arch/arm/cpu/arm720t/tegra30/Makefile new file mode 100644 index 0000000..96e722c --- /dev/null +++ b/arch/arm/cpu/arm720t/tegra30/Makefile @@ -0,0 +1,48 @@ +# +# (C) Copyright 2010-2012 Nvidia Corporation. +# +# (C) Copyright 2000-2008 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# 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 $(TOPDIR)/config.mk + +LIB = $(obj)lib$(SOC).o + +COBJS-y += cpu.o +COBJS-$(CONFIG_SPL_BUILD) += spl.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/tegra30/board.h b/arch/arm/cpu/arm720t/tegra30/board.h new file mode 100644 index 0000000..fc11a7b --- /dev/null +++ b/arch/arm/cpu/arm720t/tegra30/board.h @@ -0,0 +1,25 @@ +/* + * (C) Copyright 2010-2012 + * NVIDIA Corporation + * + * 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 + */ + +void board_init_uart_f(void); +void gpio_config_uart(void); diff --git a/arch/arm/cpu/arm720t/tegra30/config.mk b/arch/arm/cpu/arm720t/tegra30/config.mk new file mode 100644 index 0000000..ca9c6ea --- /dev/null +++ b/arch/arm/cpu/arm720t/tegra30/config.mk @@ -0,0 +1,26 @@ +# +# (C) Copyright 2010-2012 +# NVIDIA Corporation +# +# (C) Copyright 2002 +# Gary Jennejohn, DENX Software Engineering, +# +# 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 +# +USE_PRIVATE_LIBGCC = yes diff --git a/arch/arm/cpu/arm720t/tegra30/cpu.c b/arch/arm/cpu/arm720t/tegra30/cpu.c new file mode 100644 index 0000000..f7d9b87 --- /dev/null +++ b/arch/arm/cpu/arm720t/tegra30/cpu.c @@ -0,0 +1,570 @@ +/* + * (C) Copyright 2010-2012 + * NVIDIA Corporation + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cpu.h" + +struct clk_pll_table { + u16 n; + u16 m; + u8 p; + u8 cpcon; +}; + +/* ~0=uninitialized/unknown, 0=false, 1=true */ +uint32_t is_tegra_processor_reset = 0xffffffff; + +/* + * Timing tables for each SOC for all four oscillator options. + */ +static struct clk_pll_table tegra_pll_x_table[TEGRA_SOC_COUNT] + [CLOCK_OSC_FREQ_COUNT] = { + /* T20: 1 GHz */ + {{ 1000, 13, 0, 12}, /* OSC 13M */ + { 625, 12, 0, 8}, /* OSC 19.2M */ + { 1000, 12, 0, 12}, /* OSC 12M */ + { 1000, 26, 0, 12}, /* OSC 26M */ + }, + + /* T25: 1.2 GHz */ + {{ 923, 10, 0, 12}, + { 750, 12, 0, 8}, + { 600, 6, 0, 12}, + { 600, 13, 0, 12}, + }, + + /* T30(slow): 1.0 GHz */ + {{ 1000, 13, 0, 8}, + { 625, 12, 0, 4}, + { 1000, 12, 0, 8}, + { 1000, 26, 0, 8}, + }, + + /* T30(high): 1.4 GHz */ + {{ 862, 8, 0, 8}, + { 583, 8, 0, 4}, + { 700, 6, 0, 8}, + { 700, 13, 0, 8}, + }, + + /* TEGRA_SOC2_SLOW: 312 MHz */ + {{ 312, 13, 0, 12}, /* OSC 13M */ + { 260, 16, 0, 8}, /* OSC 19.2M */ + { 312, 12, 0, 12}, /* OSC 12M */ + { 312, 26, 0, 12}, /* OSC 26M */ + }, +}; + +enum tegra_family_t { + TEGRA_FAMILY_T2x, + TEGRA_FAMILY_T3x, +}; + +int tegra_get_chip_type(void) +{ + struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE; + uint tegra_sku_id; + + debug("tegra_get_chip_type entry\n"); + tegra_sku_id = readl(&fuse->sku_info) & 0xff; + debug(" tegra_get_chip_type: sku_id = %d\n", tegra_sku_id); + + switch (tegra_sku_id) { + case SKU_ID_T20: + return TEGRA_SOC_T20; + case SKU_ID_T25SE: + case SKU_ID_AP25: + case SKU_ID_T25: + case SKU_ID_AP25E: + case SKU_ID_T25E: + return TEGRA_SOC_T25; + case SKU_ID_T30: + /* + * T30 has two options. We will return TEGRA_SOC_T30 until + * we have the fdt set up when it may change to + * TEGRA_SOC_T30_408MHZ depending on what we set PLLP to. + */ + if (clock_get_rate(CLOCK_ID_PERIPH) == 408000000) + return TEGRA_SOC_T30_408MHZ; + else + return TEGRA_SOC_T30; + + default: + /* unknown sku id */ + return TEGRA_SOC_UNKNOWN; + } +} + +static enum tegra_family_t get_family(void) +{ + u32 reg, chip_id; + + debug("tegra_get_family entry\n"); + reg = readl(NV_PA_APB_MISC_BASE + GP_HIDREV); + + chip_id = reg >> 8; + chip_id &= 0xff; + debug(" tegra_get_family: chip_id = %x\n", chip_id); + if (chip_id == 0x30) + return TEGRA_FAMILY_T3x; + else + return TEGRA_FAMILY_T2x; +} + +int get_num_cpus(void) +{ + return get_family() == TEGRA_FAMILY_T3x ? 4 : 2; +} + +/* Returns 1 if the current CPU executing is a Cortex-A9, else 0 */ +int cpu_is_cortexa9(void) +{ + u32 id = readb(NV_PA_PG_UP_BASE + PG_UP_TAG_0); + return id == (PG_UP_TAG_0_PID_CPU & 0xff); +} + +static void adjust_pllp_out_freqs(void) +{ + struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_PERIPH]; + u32 reg; + + /* Set T30 PLLP_OUT1, 2, 3 & 4 freqs to 9.6, 48, 102 & 204MHz */ + reg = readl(&pll->pll_out); /* OUTA, contains OUT2 / OUT1 */ + reg |= (IN_408_OUT_48_DIVISOR << PLLP_OUT2_RATIO) | PLLP_OUT2_OVR + | (IN_408_OUT_9_6_DIVISOR << PLLP_OUT1_RATIO) | PLLP_OUT1_OVR; + writel(reg, &pll->pll_out); + + reg = readl(&pll->pll_out_b); /* OUTB, contains OUT4 / OUT3 */ + reg |= (IN_408_OUT_204_DIVISOR << PLLP_OUT4_RATIO) | PLLP_OUT4_OVR + | (IN_408_OUT_102_DIVISOR << PLLP_OUT3_RATIO) | PLLP_OUT3_OVR; + writel(reg, &pll->pll_out_b); +} + +static int pllx_set_rate(struct clk_pll_simple *pll , u32 divn, u32 divm, + u32 divp, u32 cpcon) +{ + u32 reg; + + /* If PLLX is already enabled, just return */ + if (readl(&pll->pll_base) & PLL_ENABLE_MASK) { + debug("pllx_set_rate: PLLX already enabled, returning\n"); + return 0; + } + + debug(" pllx_set_rate entry\n"); + + /* 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)); + writel(reg, &pll->pll_base); + + /* Set cpcon to PLLX_MISC */ + reg = (cpcon << PLL_CPCON_SHIFT); + + /* Set dccon to PLLX_MISC if freq > 600MHz */ + if (divn > 600) + reg |= (1 << PLL_DCCON_SHIFT); + writel(reg, &pll->pll_misc); + + /* Enable PLLX */ + reg = readl(&pll->pll_base); + reg |= PLL_ENABLE_MASK; + + /* Disable BYPASS */ + reg &= ~PLL_BYPASS_MASK; + writel(reg, &pll->pll_base); + + /* Set lock_enable to PLLX_MISC */ + reg = readl(&pll->pll_misc); + reg |= PLL_LOCK_ENABLE_MASK; + writel(reg, &pll->pll_misc); + + return 0; +} + +void init_pllx(int slow) +{ + struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct clk_pll_simple *pll = &clkrst->crc_pll_simple[SIMPLE_PLLX]; + int chip_type; + enum clock_osc_freq osc; + struct clk_pll_table *sel; + + debug("init_pllx entry\n"); + + /* get chip type. If unknown, assign to T30 */ + chip_type = tegra_get_chip_type(); + if (chip_type == TEGRA_SOC_UNKNOWN) + chip_type = TEGRA_SOC_T30; + debug(" init_pllx: chip_type = %d\n", chip_type); + + /* get osc freq */ + osc = clock_get_osc_freq(); + debug(" init_pllx: osc = %d\n", osc); + + /* set pllx */ + sel = &tegra_pll_x_table[chip_type][osc]; + pllx_set_rate(pll, sel->n, sel->m, sel->p, sel->cpcon); + + /* once we are out of slow mode, set up the T30 PLLs also */ + if (!slow && chip_type == TEGRA_SOC_T30_408MHZ) { + debug(" init_pllx: adjusting PLLP out freqs\n"); + adjust_pllp_out_freqs(); + } +} + +static void enable_cpu_clock(int enable) +{ + struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 clk; + + debug("enable_cpu_clock entry, enable = %d\n", enable); + /* + * 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) { + /* Initialize PLLX in 'slow' mode */ + init_pllx(1); + + /* 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); + } + + /* + * Read the register containing the individual CPU clock enables and + * always stop the clock to CPUs 1, 2 & 3. + */ + clk = readl(&clkrst->crc_clk_cpu_cmplx); + clk |= 1 << CPU1_CLK_STP_SHIFT; + clk |= 1 << CPU2_CLK_STP_SHIFT; + clk |= 1 << CPU3_CLK_STP_SHIFT; + + /* Stop/Unstop the CPU clock */ + clk &= ~CPU0_CLK_STP_MASK; + clk |= !enable << CPU0_CLK_STP_SHIFT; + writel(clk, &clkrst->crc_clk_cpu_cmplx); + + clock_enable(PERIPH_ID_CPU); + debug("enable_cpu_clock entry, enabled CPU clock\n"); +} + +static int is_cpu_powered(void) +{ + struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + + debug("is_cpu_powered entry\n"); + 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; + + debug("remove_cpu_io_clamps entry\n"); + /* 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; + int timeout = IO_STABILIZATION_DELAY; + + debug("powerup_cpu entry\n"); + 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()) { + if (timeout-- == 0) + printf("CPU failed to power up!\n"); + else + udelay(10); + } + + /* + * 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(); + } +} + +void tegra_i2c_ll_write_addr(uint addr, uint config) +{ + struct i2c_ctlr *reg = (struct i2c_ctlr *)TEGRA_DVC_BASE; + + writel(addr, ®->cmd_addr0); + writel(config, ®->cnfg); +} + +void tegra_i2c_ll_write_data(uint data, uint config) +{ + struct i2c_ctlr *reg = (struct i2c_ctlr *)TEGRA_DVC_BASE; + + writel(data, ®->cmd_data1); + writel(config, ®->cnfg); +} + +static void enable_cpu_power_rail(void) +{ + struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + u32 reg; + + debug("enable_cpu_power_rail entry\n"); + reg = readl(&pmc->pmc_cntrl); + reg |= CPUPWRREQ_OE; + writel(reg, &pmc->pmc_cntrl); + + /* + * Pulse PWRREQ via I2C. We need to find out what this is + * doing, tidy up the code and maybe find a better place for it. + */ + tegra_i2c_ll_write_addr(0x005a, 0x0002); + tegra_i2c_ll_write_data(0x2328, 0x0a02); + udelay(1000); + tegra_i2c_ll_write_data(0x0127, 0x0a02); + udelay(10 * 1000); + + /* + * The TI PMU65861C needs a 3.75ms delay between enabling + * the power rail and enabling the CPU clock. This delay + * between SM1EN and SM1 is for switching time + the ramp + * up of the voltage to the CPU (VDD_CPU from PMU). We use 0xf00 as + * is is ARM-friendly (can fit in a single ARMv4T mov immmediate + * instruction). + */ + udelay(3840); +} + +static void reset_A9_cpu(int reset) +{ + /* + * 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. + */ + int mask = crc_rst_cpu | crc_rst_de | crc_rst_debug; + int num_cpus = get_num_cpus(); + int cpu; + + debug("reset_a9_cpu entry\n"); + /* Hold CPUs 1 onwards in reset, and CPU 0 if asked */ + for (cpu = 1; cpu < num_cpus; cpu++) + reset_cmplx_set_enable(cpu, mask, 1); + reset_cmplx_set_enable(0, mask, reset); + + /* Enable/Disable master CPU reset */ + reset_set_enable(PERIPH_ID_CPU, reset); +} + +/** + * The T30 requires some special clock initialization, including setting up + * the dvc i2c, turning on mselect and selecting the G CPU cluster + */ +void t30_init_clocks(void) +{ + /* + * Sadly our clock functions don't support the V and W clocks of T30 + * yet, as well as a few other functions, so use low-level register + * access for now. This eventual removal of low-level code from + * ap20.c is the same process we went through for T20. + */ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE; + u32 val; + + debug("t30_init_clocks entry\n"); + /* Set active CPU cluster to G */ + clrbits_le32(flow->cluster_control, 1 << 0); + + /* + * Switch system clock to PLLP_OUT4 (108 MHz), AVP will now run + * at 108 MHz. This is glitch free as only the source is changed, no + * special precaution needed. + */ + val = (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_FIQ_SOURCE_SHIFT) | + (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_IRQ_SOURCE_SHIFT) | + (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_RUN_SOURCE_SHIFT) | + (SCLK_SOURCE_PLLP_OUT4 << SCLK_SWAKEUP_IDLE_SOURCE_SHIFT) | + (SCLK_SYS_STATE_RUN << SCLK_SYS_STATE_SHIFT); + writel(val, &clkrst->crc_sclk_brst_pol); + + writel(SUPER_SCLK_ENB_MASK, &clkrst->crc_super_sclk_div); + + val = (0 << CLK_SYS_RATE_HCLK_DISABLE_SHIFT) | + (1 << CLK_SYS_RATE_AHB_RATE_SHIFT) | + (0 << CLK_SYS_RATE_PCLK_DISABLE_SHIFT) | + (0 << CLK_SYS_RATE_APB_RATE_SHIFT); + writel(val, &clkrst->crc_clk_sys_rate); + + /* Put i2c, mselect in reset and enable clocks */ + reset_set_enable(PERIPH_ID_DVC_I2C, 1); + clock_set_enable(PERIPH_ID_DVC_I2C, 1); + reset_set_enable(PERIPH_ID_MSELECT, 1); + clock_set_enable(PERIPH_ID_MSELECT, 1); + + /* Switch MSELECT clock to PLLP (00) */ + clock_ll_set_source(PERIPH_ID_MSELECT, 0); + + /* + * Our high-level clock routines are not available prior to + * relocation. We use the low-level functions which require a + * hard-coded divisor. Use CLK_M with divide by (n + 1 = 17) + */ + clock_ll_set_source_divisor(PERIPH_ID_DVC_I2C, 3, 16); + + /* + * Give clocks time to stabilize, then take i2c and mselect out of + * reset + */ + udelay(1000); + reset_set_enable(PERIPH_ID_DVC_I2C, 0); + reset_set_enable(PERIPH_ID_MSELECT, 0); +} + +static void clock_enable_coresight(int enable) +{ + u32 rst, src; + + debug("clock_enable_coresight entry\n"); + clock_set_enable(PERIPH_ID_CORESIGHT, enable); + reset_set_enable(PERIPH_ID_CORESIGHT, !enable); + + 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) + * + * Clock divider request for 204MHz would setup CSITE clock as + * 144MHz for PLLP base 216MHz and 204MHz for PLLP base 408MHz + */ + if (tegra_get_chip_type() == TEGRA_SOC_T30_408MHZ) + src = CLK_DIVIDER(NVBL_PLLP_KHZ, 204000); + else + src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000); + clock_ll_set_source_divisor(PERIPH_ID_CSI, 0, src); + + /* Unlock the CPU CoreSight interfaces */ + rst = CORESIGHT_UNLOCK; + writel(rst, CSITE_CPU_DBG0_LAR); + writel(rst, CSITE_CPU_DBG1_LAR); + writel(rst, CSITE_CPU_DBG2_LAR); + writel(rst, CSITE_CPU_DBG3_LAR); + } +} + +static void set_cpu_running(int run) +{ + struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE; + + debug("set_cpu_running entry, run = %d\n", run); + writel(run ? FLOW_MODE_NONE : FLOW_MODE_STOP, &flow->halt_cpu_events); +} + +void start_cpu(u32 reset_vector) +{ + debug("start_cpu entry, reset_vector = %x\n", reset_vector); + t30_init_clocks(); + + /* Enable VDD_CPU */ + enable_cpu_power_rail(); + + set_cpu_running(0); + + /* 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 */ + powerup_cpu(); + + /* Take the CPU out of reset */ + reset_A9_cpu(0); + + set_cpu_running(1); +} + + +void halt_avp(void) +{ + debug("halt_avp entry\n"); + 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); + } +} diff --git a/arch/arm/cpu/arm720t/tegra30/cpu.h b/arch/arm/cpu/arm720t/tegra30/cpu.h new file mode 100644 index 0000000..a5c0474 --- /dev/null +++ b/arch/arm/cpu/arm720t/tegra30/cpu.h @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2010-2012 + * NVIDIA Corporation + * + * 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 + +/* Stabilization delays, in usec */ +#define PLL_STABILIZATION_DELAY (300) +#define IO_STABILIZATION_DELAY (1000) + +#define NVBL_PLLP_KHZ (216000) + +#define PLLX_ENABLED (1 << 30) +#define CCLK_BURST_POLICY 0x20008888 +#define SUPER_CCLK_DIVIDER 0x80000000 + +/* Calculate clock fractional divider value from ref and target frequencies */ +#define CLK_DIVIDER(REF, FREQ) ((((REF) * 2) / FREQ) - 2) + +/* Calculate clock frequency value from reference and clock divider value */ +#define CLK_FREQUENCY(REF, REG) (((REF) * 2) / (REG + 2)) + +/* AVP/CPU ID */ +#define PG_UP_TAG_0_PID_CPU 0x55555555 /* CPU aka "a9" aka "mpcore" */ +#define PG_UP_TAG_0 0x0 + +#define CORESIGHT_UNLOCK 0xC5ACCE55; + +#define EXCEP_VECTOR_CPU_RESET_VECTOR (NV_PA_EVP_BASE + 0x100) +#define CSITE_CPU_DBG0_LAR (NV_PA_CSITE_BASE + 0x10FB0) +#define CSITE_CPU_DBG1_LAR (NV_PA_CSITE_BASE + 0x12FB0) +#define CSITE_CPU_DBG2_LAR (NV_PA_CSITE_BASE + 0x14FB0) +#define CSITE_CPU_DBG3_LAR (NV_PA_CSITE_BASE + 0x16FB0) + +#define FLOW_CTLR_HALT_COP_EVENTS (NV_PA_FLOW_BASE + 4) +#define FLOW_MODE_STOP 2 +#define HALT_COP_EVENT_JTAG (1 << 28) +#define HALT_COP_EVENT_IRQ_1 (1 << 11) +#define HALT_COP_EVENT_FIQ_1 (1 << 9) + +#define GP_HIDREV 0x804 +#define FLOW_MODE_NONE 0 + +#define SIMPLE_PLLX (CLOCK_ID_XCPU - CLOCK_ID_FIRST_SIMPLE) + +void start_cpu(u32 reset_vector); +void halt_avp(void) __attribute__ ((noreturn)); diff --git a/arch/arm/cpu/arm720t/tegra30/spl.c b/arch/arm/cpu/arm720t/tegra30/spl.c new file mode 100644 index 0000000..c391746 --- /dev/null +++ b/arch/arm/cpu/arm720t/tegra30/spl.c @@ -0,0 +1,132 @@ +/* + * (C) Copyright 2012 + * NVIDIA Inc, + * + * Allen Martin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "board.h" +#include "cpu.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Define global data structure pointer to it*/ +static gd_t gdata __attribute__ ((section(".data"))); +static bd_t bdata __attribute__ ((section(".data"))); + +inline void hang(void) +{ + puts("### ERROR ### Please RESET the board ###\n"); + for (;;) + ; +} + +void board_init_f(ulong dummy) +{ + board_init_uart_f(); + + /* Initialize periph GPIOs */ +#ifdef CONFIG_SPI_UART_SWITCH + gpio_early_init_uart(); +#else + gpio_config_uart(); +#endif + + /* + * We call relocate_code() with relocation target same as the + * CONFIG_SYS_SPL_TEXT_BASE. This will result in relocation getting + * skipped. Instead, only .bss initialization will happen. That's + * all we need + */ + debug(">>board_init_f()\n"); + relocate_code(CONFIG_SPL_STACK, &gdata, CONFIG_SPL_TEXT_BASE); +} + +/* This requires UART clocks to be enabled */ +static void preloader_console_init(void) +{ + const char *u_boot_rev = U_BOOT_VERSION; + + gd = &gdata; + gd->bd = &bdata; + gd->flags |= GD_FLG_RELOC; + gd->baudrate = CONFIG_BAUDRATE; + + serial_init(); /* serial communications setup */ + + gd->have_console = 1; + + /* Avoid a second "U-Boot" coming from this string */ + u_boot_rev = &u_boot_rev[7]; + + printf("\nU-Boot SPL %s (%s - %s)\n", u_boot_rev, U_BOOT_DATE, + U_BOOT_TIME); +} + +void board_init_r(gd_t *id, ulong dummy) +{ + struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + + /* enable JTAG */ + writel(0xC0, &pmt->pmt_cfg_ctl); + + debug(">>spl:board_init_r()\n"); + + mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START, + CONFIG_SYS_SPL_MALLOC_SIZE); + +#ifdef CONFIG_SPL_BOARD_INIT + spl_board_init(); +#endif + + clock_early_init(); + preloader_console_init(); + + start_cpu((u32)CONFIG_SYS_TEXT_BASE); + halt_avp(); + /* not reached */ +} + +int board_usb_init(const void *blob) +{ + return 0; +}