From patchwork Wed Sep 12 22:10:50 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom Warren X-Patchwork-Id: 183460 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 BB1692C008D for ; Thu, 13 Sep 2012 08:12:35 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 1E35A280C6; Thu, 13 Sep 2012 00:12:34 +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 1XaAnc+YBB+c; Thu, 13 Sep 2012 00:12:33 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 0D698280CA; Thu, 13 Sep 2012 00:12:19 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 0B19A280AF for ; Thu, 13 Sep 2012 00:12:06 +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 rGaGT+FAXU1k for ; Thu, 13 Sep 2012 00:11:57 +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 975572808D for ; Thu, 13 Sep 2012 00:11:56 +0200 (CEST) Received: by mail-pb0-f44.google.com with SMTP id rr4so3038722pbb.3 for ; Wed, 12 Sep 2012 15:11:56 -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=brudHuDJCRCX061QXmSTJbsB+R+OzyIBN3xiP5jECJI=; b=v7WW6fa8UxgctwkH0r+t8GFe+xFUDKzpRAeRlci4dat8DYZKF2KYQTK1ZJaD/TEzXK 6X5IieFfRsdGMcJhKpgNzZTAjhYVQ8J7hywtQr9MvL6kkEqDNC1KpcczgvyRdhOl2oH9 OrGVP8EjwtwVYZ2zGlyQD+dlnLd2KQ3HTK6Ax23JGh94KIhk133xEb1tJgnxS3EsWOsC pDZbq4T49lYbJhSdp81AKh3bwaPF9jekVhsBQJPNWdI40e6KJbQfSyT5LLASBo8dFOks 1CetwCQEen1pVcRRQVAAXbjuxQjjo8ix99RRPO9m/1zCZmp3JjPsFKh7crKRn63fRcXg H5DA== Received: by 10.68.224.162 with SMTP id rd2mr921475pbc.118.1347487916037; Wed, 12 Sep 2012 15:11:56 -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.53 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 12 Sep 2012 15:11:54 -0700 (PDT) From: Tom Warren To: u-boot@lists.denx.de Date: Wed, 12 Sep 2012 15:10:50 -0700 Message-Id: <1347487855-27077-5-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 4/9] Tegra: T30: Add common Tegra30 CPU 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/tegra30-common/Makefile | 51 ++ arch/arm/cpu/tegra30-common/ap30.c | 98 +++ arch/arm/cpu/tegra30-common/board.c | 141 ++++ arch/arm/cpu/tegra30-common/clock.c | 1099 +++++++++++++++++++++++++++ arch/arm/cpu/tegra30-common/funcmux.c | 74 ++ arch/arm/cpu/tegra30-common/lowlevel_init.S | 42 + arch/arm/cpu/tegra30-common/pinmux.c | 507 ++++++++++++ arch/arm/cpu/tegra30-common/sys_info.c | 35 + arch/arm/cpu/tegra30-common/timer.c | 111 +++ 9 files changed, 2158 insertions(+), 0 deletions(-) create mode 100644 arch/arm/cpu/tegra30-common/Makefile create mode 100644 arch/arm/cpu/tegra30-common/ap30.c create mode 100644 arch/arm/cpu/tegra30-common/board.c create mode 100644 arch/arm/cpu/tegra30-common/clock.c create mode 100644 arch/arm/cpu/tegra30-common/funcmux.c create mode 100644 arch/arm/cpu/tegra30-common/lowlevel_init.S create mode 100644 arch/arm/cpu/tegra30-common/pinmux.c create mode 100644 arch/arm/cpu/tegra30-common/sys_info.c create mode 100644 arch/arm/cpu/tegra30-common/timer.c diff --git a/arch/arm/cpu/tegra30-common/Makefile b/arch/arm/cpu/tegra30-common/Makefile new file mode 100644 index 0000000..6efc554 --- /dev/null +++ b/arch/arm/cpu/tegra30-common/Makefile @@ -0,0 +1,51 @@ +# +# (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 + +# The AVP is ARMv4T architecture so we must use special compiler +# flags for any startup files it might use. + +LIB = $(obj)lib$(SOC)-common.o + +SOBJS += lowlevel_init.o +COBJS-y += ap30.o board.o clock.o funcmux.o pinmux.o sys_info.o timer.o + +SRCS := $(SOBJS:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(SOBJS) $(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/tegra30-common/ap30.c b/arch/arm/cpu/tegra30-common/ap30.c new file mode 100644 index 0000000..4f07c4e --- /dev/null +++ b/arch/arm/cpu/tegra30-common/ap30.c @@ -0,0 +1,98 @@ +/* +* (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 + +static 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); +} + +static u32 get_odmdata(void) +{ + /* + * ODMDATA is stored in the BCT in IRAM by the BootROM. + * The BCT start and size are stored in the BIT in IRAM. + * Read the data @ bct_start + (bct_size - 12). This works + * on T20 and T30 BCTs, which are locked down. If this changes + * in new chips (T114, etc.), we can revisit this algorithm. + */ + + u32 bct_start, odmdata; + + bct_start = readl(T30_BASE_PA_SRAM + NVBOOTINFOTABLE_BCTPTR); + odmdata = readl(bct_start + BCT_ODMDATA_OFFSET); + + return odmdata; +} + +static void init_pmc_scratch(void) +{ + struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + u32 odmdata; + 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. */ + odmdata = get_odmdata(); + writel(odmdata, &pmc->pmc_scratch20); +} + +void s_init(void) +{ + /* Init PMC scratch memory */ + init_pmc_scratch(); + + enable_scu(); + + /* enable SMP mode and FW for CPU0, by writing to Auxiliary Ctl reg */ + asm volatile( + "mrc p15, 0, r0, c1, c0, 1\n" + "orr r0, r0, #0x41\n" + "mcr p15, 0, r0, c1, c0, 1\n"); + + /* FIXME: should have ap30's L2 disabled too? */ +} diff --git a/arch/arm/cpu/tegra30-common/board.c b/arch/arm/cpu/tegra30-common/board.c new file mode 100644 index 0000000..6fe017d --- /dev/null +++ b/arch/arm/cpu/tegra30-common/board.c @@ -0,0 +1,141 @@ +/* + * (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 + +DECLARE_GLOBAL_DATA_PTR; + +enum { + /* UARTs which we can enable */ + UARTA = 1 << 0, + UARTB = 1 << 1, + UARTD = 1 << 3, + UART_COUNT = 4, +}; + +/* + * Boot ROM initializes the odmdata in APBDEV_PMC_SCRATCH20_0, + * so we are using this value to identify memory size. + */ + +unsigned int query_sdram_size(void) +{ + struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + u32 reg; + + reg = readl(&pmc->pmc_scratch20); + debug("pmc->pmc_scratch20 (ODMData) = 0x%08x\n", reg); + + /* bits 31:28 in OdmData are used for RAM size */ + switch ((reg) >> 28) { + case 1: + return 0x10000000; /* 256 MB */ + case 2: + return 0x20000000; /* 512 MB */ + case 4: + return 0x40000000; /* 1GB */ + case 8: + return 0x7ff00000; /* 2GB - 1MB */ + default: + return 0x40000000; /* 1GB */ + } +} + +int dram_init(void) +{ + /* We do not initialise DRAM here. We just query the size */ + gd->ram_size = query_sdram_size(); + return 0; +} + +#ifdef CONFIG_DISPLAY_BOARDINFO +int checkboard(void) +{ + printf("Board: %s\n", sysinfo.board_string); + return 0; +} +#endif /* CONFIG_DISPLAY_BOARDINFO */ + +static int uart_configs[] = { + FUNCMUX_UART1_ULPI_UART2, /* UARTA */ + FUNCMUX_UART2_IRDA, + -1, + FUNCMUX_UART4_GMC, + -1, +}; + +/** + * Set up the specified uarts + * + * @param uarts_ids Mask containing UARTs to init (UARTx) + */ +static void setup_uarts(int uart_ids) +{ + static enum periph_id id_for_uart[] = { + PERIPH_ID_UART1, + PERIPH_ID_UART2, + PERIPH_ID_UART3, + PERIPH_ID_UART4, + }; + size_t i; + + for (i = 0; i < UART_COUNT; i++) { + if (uart_ids & (1 << i)) { + enum periph_id id = id_for_uart[i]; + + funcmux_select(id, uart_configs[i]); + clock_ll_start_uart(id); + } + } +} + +void board_init_uart_f(void) +{ + int uart_ids = 0; /* bit mask of which UART ids to enable */ + +#ifdef CONFIG_TEGRA_ENABLE_UARTA + uart_ids |= UARTA; +#endif +#ifdef CONFIG_TEGRA_ENABLE_UARTB + uart_ids |= UARTB; +#endif +#ifdef CONFIG_TEGRA_ENABLE_UARTD + uart_ids |= UARTD; +#endif + setup_uarts(uart_ids); +} + +#ifndef CONFIG_SYS_DCACHE_OFF +void enable_caches(void) +{ + /* Enable D-cache. I-cache is already enabled in start.S */ + dcache_enable(); +} +#endif diff --git a/arch/arm/cpu/tegra30-common/clock.c b/arch/arm/cpu/tegra30-common/clock.c new file mode 100644 index 0000000..dd5450f --- /dev/null +++ b/arch/arm/cpu/tegra30-common/clock.c @@ -0,0 +1,1099 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (C) 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 + */ + +/* Tegra30 Clock control functions */ +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* + * This is our record of the current clock rate of each clock. We don't + * fill all of these in since we are only really interested in clocks which + * we use as parents. + */ +static unsigned pll_rate[CLOCK_ID_COUNT]; + +/* + * The oscillator frequency is fixed to one of four set values. Based on this + * the other clocks are set up appropriately. + */ +static unsigned osc_freq[CLOCK_OSC_FREQ_COUNT] = { + 13000000, + 19200000, + 12000000, + 26000000, +}; + +/* + * Clock types that we can use as a source. The Tegra3 has muxes for the + * peripheral clocks, and in most cases there are four options for the clock + * source. This gives us a clock 'type' and exploits what commonality exists + * in the device. + * + * Letters are obvious, except for T which means CLK_M, and S which means the + * clock derived from 32KHz. Beware that CLK_M (also called OSC in the + * datasheet) and PLL_M are different things. The former is the basic + * clock supplied to the SOC from an external oscillator. The latter is the + * memory clock PLL. + * + * See definitions in clock_id in the header file. + */ +enum clock_type_id { + CLOCK_TYPE_AXPT, /* PLL_A, PLL_X, PLL_P, CLK_M */ + CLOCK_TYPE_MCPA, /* and so on */ + CLOCK_TYPE_MCPT, + CLOCK_TYPE_PCM, + CLOCK_TYPE_PCMT, + CLOCK_TYPE_PDCT, + CLOCK_TYPE_ACPT, + CLOCK_TYPE_ASPTE, + CLOCK_TYPE_PMDACD2T, + CLOCK_TYPE_PCST, + + CLOCK_TYPE_COUNT, + CLOCK_TYPE_NONE = -1, /* invalid clock type */ +}; + +/* return 1 if a peripheral ID is in range */ +#define clock_type_id_isvalid(id) ((id) >= 0 && \ + (id) < CLOCK_TYPE_COUNT) + +char pllp_valid = 1; /* PLLP is set up correctly */ + +enum { + CLOCK_MAX_MUX = 8 /* number of source options for each clock */ +}; + +enum { + MASK_BITS_31_30 = 2, /* num of bits used to specify clock source */ + MASK_BITS_31_29, + MASK_BITS_29_28, +}; + +/* + * Clock source mux for each clock type. This just converts our enum into + * a list of mux sources for use by the code. + * + * Note: + * The extra column in each clock source array is used to store the mask + * bits in its register for the source. + */ +#define CLK(x) CLOCK_ID_ ## x +static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX+1] = { + { CLK(AUDIO), CLK(XCPU), CLK(PERIPH), CLK(OSC), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_30}, + { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(AUDIO), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_30}, + { CLK(MEMORY), CLK(CGENERAL), CLK(PERIPH), CLK(OSC), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_30}, + { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(NONE), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_30}, + { CLK(PERIPH), CLK(CGENERAL), CLK(MEMORY), CLK(OSC), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_30}, + { CLK(PERIPH), CLK(DISPLAY), CLK(CGENERAL), CLK(OSC), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_30}, + { CLK(AUDIO), CLK(CGENERAL), CLK(PERIPH), CLK(OSC), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_30}, + { CLK(AUDIO), CLK(SFROM32KHZ), CLK(PERIPH), CLK(OSC), + CLK(EPCI), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_31_29}, + { CLK(PERIPH), CLK(MEMORY), CLK(DISPLAY), CLK(AUDIO), + CLK(CGENERAL), CLK(DISPLAY2), CLK(OSC), CLK(NONE), + MASK_BITS_31_29}, + { CLK(PERIPH), CLK(CGENERAL), CLK(SFROM32KHZ), CLK(OSC), + CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE), + MASK_BITS_29_28} +}; + +/* return 1 if a periphc_internal_id is in range */ +#define periphc_internal_id_isvalid(id) ((id) >= 0 && \ + (id) < PERIPHC_COUNT) + +/* + * Clock type for each peripheral clock source. We put the name in each + * record just so it is easy to match things up + */ +#define TYPE(name, type) type +static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = { + /* 0x00 */ + TYPE(PERIPHC_I2S1, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_I2S2, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_SPDIF_OUT, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_SPDIF_IN, CLOCK_TYPE_PCM), + TYPE(PERIPHC_PWM, CLOCK_TYPE_PCST), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SBC2, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SBC3, CLOCK_TYPE_PCMT), + + /* 0x08 */ + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_I2C1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_DVC_I2C, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SBC1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_DISP1, CLOCK_TYPE_PMDACD2T), + TYPE(PERIPHC_DISP2, CLOCK_TYPE_PMDACD2T), + + /* 0x10 */ + TYPE(PERIPHC_CVE, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_VI, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SDMMC1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SDMMC2, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_G3D, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_G2D, CLOCK_TYPE_MCPA), + + /* 0x18 */ + TYPE(PERIPHC_NDFLASH, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SDMMC4, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_VFIR, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_EPP, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_MPE, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_MIPI, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_UART1, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_UART2, CLOCK_TYPE_PCMT), + + /* 0x20 */ + TYPE(PERIPHC_HOST1X, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_TVO, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_HDMI, CLOCK_TYPE_PMDACD2T), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_TVDAC, CLOCK_TYPE_PDCT), + TYPE(PERIPHC_I2C2, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_EMC, CLOCK_TYPE_MCPT), + + /* 0x28 */ + TYPE(PERIPHC_UART3, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_VI, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SBC4, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2C3, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SDMMC3, CLOCK_TYPE_PCMT), + + /* 0x30 */ + TYPE(PERIPHC_UART4, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_UART5, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_VDE, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_OWR, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_NOR, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_CSITE, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2S0, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + + /* 0x38h */ + TYPE(PERIPHC_G3D2, CLOCK_TYPE_MCPA), + TYPE(PERIPHC_MSELECT, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_TSENSOR, CLOCK_TYPE_PCM), + TYPE(PERIPHC_I2S3, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_I2S4, CLOCK_TYPE_AXPT), + TYPE(PERIPHC_I2C4, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SBC5, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SBC6, CLOCK_TYPE_PCMT), + + /* 0x40 */ + TYPE(PERIPHC_AUDIO, CLOCK_TYPE_ACPT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_DAM0, CLOCK_TYPE_ACPT), + TYPE(PERIPHC_DAM1, CLOCK_TYPE_ACPT), + TYPE(PERIPHC_DAM2, CLOCK_TYPE_ACPT), + TYPE(PERIPHC_HDA2CODEC2X, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_ACTMON, CLOCK_TYPE_PCM), + TYPE(PERIPHC_EXTPERIPH1, CLOCK_TYPE_ASPTE), + + /* 0x48 */ + TYPE(PERIPHC_EXTPERIPH2, CLOCK_TYPE_ASPTE), + TYPE(PERIPHC_EXTPERIPH3, CLOCK_TYPE_ASPTE), + TYPE(PERIPHC_NANDSPEED, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_I2CSLOW, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SYS, CLOCK_TYPE_NONE), + TYPE(PERIPHC_SPEEDO, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + TYPE(PERIPHC_NONE, CLOCK_TYPE_NONE), + + /* 0x50 */ + TYPE(PERIPHC_SATAOOB, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_SATA, CLOCK_TYPE_PCMT), + TYPE(PERIPHC_HDA, CLOCK_TYPE_PCMT), +}; + +/* + * This array translates a periph_id to a periphc_internal_id + * + * Not present/matched up: + * uint vi_sensor; _VI_SENSOR_0, 0x1A8 + * SPDIF - which is both 0x08 and 0x0c + * + */ +#define NONE(name) (-1) +#define OFFSET(name, value) PERIPHC_ ## name +static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = { + /* Low word: 31:0 */ + NONE(CPU), + NONE(COP), + NONE(TRIGSYS), + NONE(RESERVED3), + NONE(RESERVED4), + NONE(TMR), + PERIPHC_UART1, + PERIPHC_UART2, /* and vfir 0x68 */ + + /* 8 */ + NONE(GPIO), + PERIPHC_SDMMC2, + NONE(SPDIF), /* 0x08 and 0x0c, unclear which to use */ + PERIPHC_I2S1, + PERIPHC_I2C1, + PERIPHC_NDFLASH, + PERIPHC_SDMMC1, + PERIPHC_SDMMC4, + + /* 16 */ + NONE(RESERVED16), + PERIPHC_PWM, + PERIPHC_I2S2, + PERIPHC_EPP, + PERIPHC_VI, + PERIPHC_G2D, + NONE(USBD), + NONE(ISP), + + /* 24 */ + PERIPHC_G3D, + NONE(RESERVED25), + PERIPHC_DISP2, + PERIPHC_DISP1, + PERIPHC_HOST1X, + NONE(VCP), + PERIPHC_I2S0, + NONE(CACHE2), + + /* Middle word: 63:32 */ + NONE(MEM), + NONE(AHBDMA), + NONE(APBDMA), + NONE(RESERVED35), + NONE(RESERVED36), + NONE(STAT_MON), + NONE(RESERVED38), + NONE(RESERVED39), + + /* 40 */ + NONE(KFUSE), + NONE(SBC1), /* SBC1, 0x34, is this SPI1? */ + PERIPHC_NOR, + NONE(RESERVED43), + PERIPHC_SBC2, + NONE(RESERVED45), + PERIPHC_SBC3, + PERIPHC_DVC_I2C, + + /* 48 */ + NONE(DSI), + PERIPHC_TVO, /* also CVE 0x40 */ + PERIPHC_MIPI, + PERIPHC_HDMI, + NONE(CSI), + PERIPHC_TVDAC, + PERIPHC_I2C2, + PERIPHC_UART3, + + /* 56 */ + NONE(RESERVED56), + PERIPHC_EMC, + NONE(USB2), + NONE(USB3), + PERIPHC_MPE, + PERIPHC_VDE, + NONE(BSEA), + NONE(BSEV), + + /* Upper word 95:64 */ + PERIPHC_SPEEDO, + PERIPHC_UART4, + PERIPHC_UART5, + PERIPHC_I2C3, + PERIPHC_SBC4, + PERIPHC_SDMMC3, + NONE(PCIE), + PERIPHC_OWR, + + /* 72 */ + NONE(AFI), + PERIPHC_CSITE, + NONE(PCIEXCLK), + NONE(AVPUCQ), + NONE(RESERVED76), + NONE(RESERVED77), + NONE(RESERVED78), + NONE(DTV), + + /* 80 */ + PERIPHC_NANDSPEED, + PERIPHC_I2CSLOW, + NONE(DSIB), + NONE(RESERVED83), + NONE(IRAMA), + NONE(IRAMB), + NONE(IRAMC), + NONE(IRAMD), + + /* 88 */ + NONE(CRAM2), + NONE(RESERVED89), + NONE(MDOUBLER), + NONE(RESERVED91), + NONE(SUSOUT), + NONE(RESERVED93), + NONE(RESERVED94), + NONE(RESERVED95), + + /* V word: 31:0 */ + NONE(CPUG), + NONE(CPULP), + PERIPHC_G3D2, + PERIPHC_MSELECT, + PERIPHC_TSENSOR, + PERIPHC_I2S3, + PERIPHC_I2S4, + PERIPHC_I2C4, + + /* 08 */ + PERIPHC_SBC5, + PERIPHC_SBC6, + PERIPHC_AUDIO, + NONE(APBIF), + PERIPHC_DAM0, + PERIPHC_DAM1, + PERIPHC_DAM2, + PERIPHC_HDA2CODEC2X, + + /* 16 */ + NONE(ATOMICS), + NONE(RESERVED17), + NONE(RESERVED18), + NONE(RESERVED19), + NONE(RESERVED20), + NONE(RESERVED21), + NONE(RESERVED22), + PERIPHC_ACTMON, + + /* 24 */ + NONE(RESERVED24), + NONE(RESERVED25), + NONE(RESERVED26), + NONE(RESERVED27), + PERIPHC_SATA, + PERIPHC_HDA, + NONE(RESERVED30), + NONE(RESERVED31), + + /* W word: 31:0 */ + NONE(HDA2HDMICODEC), + NONE(SATACOLD), + NONE(RESERVED0_PCIERX0), + NONE(RESERVED1_PCIERX1), + NONE(RESERVED2_PCIERX2), + NONE(RESERVED3_PCIERX3), + NONE(RESERVED4_PCIERX4), + NONE(RESERVED5_PCIERX5), + + /* 40 */ + NONE(CEC), + NONE(RESERVED6_PCIE2), + NONE(RESERVED7_EMC), + NONE(RESERVED8_HDMI), + NONE(RESERVED9_SATA), + NONE(RESERVED10_MIPI), + NONE(EX_RESERVED46), + NONE(EX_RESERVED47), +}; + +/* + * Get the oscillator frequency, from the corresponding hardware configuration + * field. + */ +enum clock_osc_freq clock_get_osc_freq(void) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 reg; + + reg = readl(&clkrst->crc_osc_ctrl); + return (reg & OSC_FREQ_MASK) >> OSC_FREQ_SHIFT; +} + +int clock_get_osc_bypass(void) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 reg; + + reg = readl(&clkrst->crc_osc_ctrl); + return (reg & OSC_XOBP_MASK) >> OSC_XOBP_SHIFT; +} + +/* Returns a pointer to the registers of the given pll */ +static struct clk_pll *get_pll(enum clock_id clkid) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + + assert(clock_id_is_pll(clkid)); + return &clkrst->crc_pll[clkid]; +} + +int clock_ll_read_pll(enum clock_id clkid, u32 *divm, u32 *divn, + u32 *divp, u32 *cpcon, u32 *lfcon) +{ + struct clk_pll *pll = get_pll(clkid); + u32 data; + + assert(clkid != CLOCK_ID_USB); + + /* Safety check, adds to code size but is small */ + if (!clock_id_is_pll(clkid) || clkid == CLOCK_ID_USB) + return -1; + data = readl(&pll->pll_base); + *divm = (data & PLL_DIVM_MASK) >> PLL_DIVM_SHIFT; + *divn = (data & PLL_DIVN_MASK) >> PLL_DIVN_SHIFT; + *divp = (data & PLL_DIVP_MASK) >> PLL_DIVP_SHIFT; + data = readl(&pll->pll_misc); + *cpcon = (data & PLL_CPCON_MASK) >> PLL_CPCON_SHIFT; + *lfcon = (data & PLL_LFCON_MASK) >> PLL_LFCON_SHIFT; + return 0; +} + +unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn, + u32 divp, u32 cpcon, u32 lfcon) +{ + struct clk_pll *pll = get_pll(clkid); + u32 data; + + /* + * We cheat by treating all PLL (except PLLU) in the same fashion. + * This works only because: + * - same fields are always mapped at same offsets, except DCCON + * - DCCON is always 0, doesn't conflict + * - M,N, P of PLLP values are ignored for PLLP + */ + data = (cpcon << PLL_CPCON_SHIFT) | (lfcon << PLL_LFCON_SHIFT); + writel(data, &pll->pll_misc); + + data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) | + (0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT); + + if (clkid == CLOCK_ID_USB) + data |= divp << PLLU_VCO_FREQ_SHIFT; + else + data |= divp << PLL_DIVP_SHIFT; + writel(data, &pll->pll_base); + + /* calculate the stable time */ + return timer_get_us() + CLOCK_PLL_STABLE_DELAY_US; +} + +/* Returns a pointer to the clock source register for a peripheral */ +static u32 *get_periph_source_reg(enum periph_id periph_id) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + enum periphc_internal_id internal_id; + + /* Coresight is a special case */ + if (periph_id == PERIPH_ID_CSI) + return &clkrst->crc_clk_src[PERIPH_ID_CSI+1]; + + assert(periph_id >= PERIPH_ID_FIRST && periph_id < PERIPH_ID_COUNT); + internal_id = periph_id_to_internal_id[periph_id]; + assert(internal_id != -1); + if (internal_id >= PERIPHC_VW_FIRST) { + internal_id -= PERIPHC_VW_FIRST; + return &clkrst->crc_clk_src_vw[internal_id]; + } else + return &clkrst->crc_clk_src[internal_id]; +} + +void clock_ll_set_source_divisor(enum periph_id periph_id, unsigned source, + unsigned divisor) +{ + u32 *reg = get_periph_source_reg(periph_id); + u32 value; + + value = readl(reg); + + value &= ~OUT_CLK_SOURCE_MASK; + value |= source << OUT_CLK_SOURCE_SHIFT; + + value &= ~OUT_CLK_DIVISOR_MASK; + value |= divisor << OUT_CLK_DIVISOR_SHIFT; + + writel(value, reg); +} + +void clock_ll_set_source(enum periph_id periph_id, unsigned source) +{ + u32 *reg = get_periph_source_reg(periph_id); + + clrsetbits_le32(reg, OUT_CLK_SOURCE_MASK, + source << OUT_CLK_SOURCE_SHIFT); +} + +/** + * Given the parent's rate and the required rate for the children, this works + * out the peripheral clock divider to use, in 7.1 binary format. + * + * @param divider_bits number of divider bits (8 or 16) + * @param parent_rate clock rate of parent clock in Hz + * @param rate required clock rate for this clock + * @return divider which should be used + */ +static int clk_get_divider(unsigned divider_bits, unsigned long parent_rate, + unsigned long rate) +{ + u64 divider = parent_rate * 2; + unsigned max_divider = 1 << divider_bits; + + divider += rate - 1; + do_div(divider, rate); + + if ((s64)divider - 2 < 0) + return 0; + + if ((s64)divider - 2 >= max_divider) + return -1; + + return divider - 2; +} + +/** + * Given the parent's rate and the divider in 7.1 format, this works out the + * resulting peripheral clock rate. + * + * @param parent_rate clock rate of parent clock in Hz + * @param divider which should be used in 7.1 format + * @return effective clock rate of peripheral + */ +static unsigned long get_rate_from_divider(unsigned long parent_rate, + int divider) +{ + u64 rate; + + rate = (u64)parent_rate * 2; + do_div(rate, divider + 2); + return rate; +} + +unsigned long clock_get_periph_rate(enum periph_id periph_id, + enum clock_id parent) +{ + u32 *reg = get_periph_source_reg(periph_id); + + return get_rate_from_divider(pll_rate[parent], + (readl(reg) & OUT_CLK_DIVISOR_MASK) >> OUT_CLK_DIVISOR_SHIFT); +} + +/** + * Find the best available 7.1 format divisor given a parent clock rate and + * required child clock rate. This function assumes that a second-stage + * divisor is available which can divide by powers of 2 from 1 to 256. + * + * @param divider_bits number of divider bits (8 or 16) + * @param parent_rate clock rate of parent clock in Hz + * @param rate required clock rate for this clock + * @param extra_div value for the second-stage divisor (not set if this + * function returns -1. + * @return divider which should be used, or -1 if nothing is valid + * + */ +static int find_best_divider(unsigned divider_bits, unsigned long parent_rate, + unsigned long rate, int *extra_div) +{ + int shift; + int best_divider = -1; + int best_error = rate; + + /* try dividers from 1 to 256 and find closest match */ + for (shift = 0; shift <= 8 && best_error > 0; shift++) { + unsigned divided_parent = parent_rate >> shift; + int divider = clk_get_divider(divider_bits, divided_parent, + rate); + unsigned effective_rate = get_rate_from_divider(divided_parent, + divider); + int error = rate - effective_rate; + + /* Given a valid divider, look for the lowest error */ + if (divider != -1 && error < best_error) { + best_error = error; + *extra_div = 1 << shift; + best_divider = divider; + } + } + + /* return what we found - *extra_div will already be set */ + return best_divider; +} + +/** + * Given a peripheral ID and the required source clock, this returns which + * value should be programmed into the source mux for that peripheral. + * + * There is special code here to handle the one source type with 5 sources. + * + * @param periph_id peripheral to start + * @param source PLL id of required parent clock + * @param mux_bits Set to number of bits in mux register: 2 or 4 + * @param divider_bits Set to number of divider bits (8 or 16) + * @return mux value (0-4, or -1 if not found) + */ +static int get_periph_clock_source(enum periph_id periph_id, + enum clock_id parent, int *mux_bits, int *divider_bits) +{ + enum clock_type_id type; + enum periphc_internal_id internal_id; + int mux; + + assert(clock_periph_id_isvalid(periph_id)); + + internal_id = periph_id_to_internal_id[periph_id]; + assert(periphc_internal_id_isvalid(internal_id)); + + type = clock_periph_type[internal_id]; + assert(clock_type_id_isvalid(type)); + + *mux_bits = clock_source[type][CLOCK_MAX_MUX]; + + for (mux = 0; mux < CLOCK_MAX_MUX; mux++) + if (clock_source[type][mux] == parent) + return mux; + + /* if we get here, either us or the caller has made a mistake */ + printf("Caller requested bad clock: periph=%d, parent=%d\n", periph_id, + parent); + return -1; +} + +/** + * Adjust peripheral PLL to use the given divider and source. + * + * @param periph_id peripheral to adjust + * @param source Source number (0-3 or 0-7) + * @param mux_bits Number of mux bits (2 or 4) + * @param divider Required divider in 7.1 or 15.1 format + * @return 0 if ok, -1 on error (requesting a parent clock which is not valid + * for this peripheral) + */ +static int adjust_periph_pll(enum periph_id periph_id, int source, + int mux_bits, unsigned divider) +{ + u32 *reg = get_periph_source_reg(periph_id); + + clrsetbits_le32(reg, OUT_CLK_DIVISOR_MASK, + divider << OUT_CLK_DIVISOR_SHIFT); + udelay(1); + + /* work out the source clock and set it */ + if (source < 0) + return -1; + if (mux_bits == 4) { + clrsetbits_le32(reg, OUT_CLK_SOURCE4_MASK, + source << OUT_CLK_SOURCE4_SHIFT); + } else { + clrsetbits_le32(reg, OUT_CLK_SOURCE_MASK, + source << OUT_CLK_SOURCE_SHIFT); + } + udelay(2); + return 0; +} + +unsigned clock_adjust_periph_pll_div(enum periph_id periph_id, + enum clock_id parent, unsigned rate, int *extra_div) +{ + unsigned effective_rate; + int mux_bits, source; + int divider, divider_bits = 0; + + /* work out the source clock and set it */ + source = get_periph_clock_source(periph_id, parent, &mux_bits, + ÷r_bits); + + if (extra_div) + divider = find_best_divider(divider_bits, pll_rate[parent], + rate, extra_div); + else + divider = clk_get_divider(divider_bits, pll_rate[parent], + rate); + assert(divider >= 0); + if (adjust_periph_pll(periph_id, source, mux_bits, divider)) + return -1U; + debug("periph %d, rate=%d, reg=%p = %x\n", periph_id, rate, + get_periph_source_reg(periph_id), + readl(get_periph_source_reg(periph_id))); + + /* Check what we ended up with. This shouldn't matter though */ + effective_rate = clock_get_periph_rate(periph_id, parent); + if (extra_div) + effective_rate /= *extra_div; + if (rate != effective_rate) + debug("Requested clock rate %u not honored (got %u)\n", + rate, effective_rate); + return effective_rate; +} + +unsigned clock_start_periph_pll(enum periph_id periph_id, + enum clock_id parent, unsigned rate) +{ + unsigned effective_rate; + + reset_set_enable(periph_id, 1); + clock_enable(periph_id); + + effective_rate = clock_adjust_periph_pll_div(periph_id, parent, rate, + NULL); + + reset_set_enable(periph_id, 0); + return effective_rate; +} + +void clock_set_enable(enum periph_id periph_id, int enable) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 *clk; + u32 reg; + + /* Enable/disable the clock to this peripheral */ + assert(clock_periph_id_isvalid(periph_id)); + if ((int)periph_id < (int)PERIPH_ID_VW_FIRST) + clk = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)]; + else + clk = &clkrst->crc_clk_out_enb_vw[PERIPH_REG(periph_id)]; + reg = readl(clk); + if (enable) + reg |= PERIPH_MASK(periph_id); + else + reg &= ~PERIPH_MASK(periph_id); + writel(reg, clk); +} + +void clock_enable(enum periph_id clkid) +{ + clock_set_enable(clkid, 1); +} + +void clock_disable(enum periph_id clkid) +{ + clock_set_enable(clkid, 0); +} + +void reset_set_enable(enum periph_id periph_id, int enable) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 *reset; + u32 reg; + + /* Enable/disable reset to the peripheral */ + assert(clock_periph_id_isvalid(periph_id)); + if (periph_id < PERIPH_ID_VW_FIRST) + reset = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)]; + else + reset = &clkrst->crc_rst_dev_vw[PERIPH_REG(periph_id)]; + reg = readl(reset); + if (enable) + reg |= PERIPH_MASK(periph_id); + else + reg &= ~PERIPH_MASK(periph_id); + writel(reg, reset); +} + +void reset_periph(enum periph_id periph_id, int us_delay) +{ + /* Put peripheral into reset */ + reset_set_enable(periph_id, 1); + udelay(us_delay); + + /* Remove reset */ + reset_set_enable(periph_id, 0); + + udelay(us_delay); +} + +void reset_cmplx_set_enable(int cpu, int which, int reset) +{ + struct clk_rst_ctlr *clkrst = + (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; + u32 mask; + + /* Form the mask, which depends on the cpu chosen. Tegra3 has 4 */ + assert(cpu >= 0 && cpu < 4); + mask = which << cpu; + + /* either enable or disable those reset for that CPU */ + if (reset) + writel(mask, &clkrst->crc_cpu_cmplx_set); + else + writel(mask, &clkrst->crc_cpu_cmplx_clr); +} + +unsigned clock_get_rate(enum clock_id clkid) +{ + struct clk_pll *pll; + u32 base; + u32 divm; + u64 parent_rate; + u64 rate; + + parent_rate = osc_freq[clock_get_osc_freq()]; + if (clkid == CLOCK_ID_OSC) + return parent_rate; + + pll = get_pll(clkid); + base = readl(&pll->pll_base); + + /* Oh for bf_unpack()... */ + rate = parent_rate * ((base & PLL_DIVN_MASK) >> PLL_DIVN_SHIFT); + divm = (base & PLL_DIVM_MASK) >> PLL_DIVM_SHIFT; + if (clkid == CLOCK_ID_USB) + divm <<= (base & PLLU_VCO_FREQ_MASK) >> PLLU_VCO_FREQ_SHIFT; + else + divm <<= (base & PLL_DIVP_MASK) >> PLL_DIVP_SHIFT; + do_div(rate, divm); + return rate; +} + +/** + * Set the output frequency you want for each PLL clock. + * PLL output frequencies are programmed by setting their N, M and P values. + * The governing equations are: + * VCO = (Fi / m) * n, Fo = VCO / (2^p) + * where Fo is the output frequency from the PLL. + * Example: Set the output frequency to 216Mhz(Fo) with 12Mhz OSC(Fi) + * 216Mhz = ((12Mhz / m) * n) / (2^p) so n=432,m=12,p=1 + * Please see Tegra TRM section 5.3 to get the detail for PLL Programming + * + * @param n PLL feedback divider(DIVN) + * @param m PLL input divider(DIVN) + * @param p post divider(DIVP) + * @param cpcon base PLL charge pump(CPCON) + * @return 0 if ok, -1 on error (the requested PLL is incorrect and cannot + * be overriden), 1 if PLL is already correct + */ +static int clock_set_rate(enum clock_id clkid, u32 n, u32 m, u32 p, u32 cpcon) +{ + u32 base_reg; + u32 misc_reg; + struct clk_pll *pll; + + pll = get_pll(clkid); + + base_reg = readl(&pll->pll_base); + + /* Set BYPASS, m, n and p to PLL_BASE */ + base_reg &= ~PLL_DIVM_MASK; + base_reg |= m << PLL_DIVM_SHIFT; + + base_reg &= ~PLL_DIVN_MASK; + base_reg |= n << PLL_DIVN_SHIFT; + + base_reg &= ~PLL_DIVP_MASK; + base_reg |= p << PLL_DIVP_SHIFT; + + if (clkid == CLOCK_ID_PERIPH) { + /* + * If the PLL is already set up, check that it is correct + * and record this info for clock_verify() to check. + */ + if (base_reg & PLL_BASE_OVRRIDE_MASK) { + base_reg |= PLL_ENABLE_MASK; + if (base_reg != readl(&pll->pll_base)) + pllp_valid = 0; + return pllp_valid ? 1 : -1; + } + base_reg |= PLL_BASE_OVRRIDE_MASK; + } + + base_reg |= PLL_BYPASS_MASK; + writel(base_reg, &pll->pll_base); + + /* Set cpcon to PLL_MISC */ + misc_reg = readl(&pll->pll_misc); + misc_reg &= ~PLL_CPCON_MASK; + misc_reg |= cpcon << PLL_CPCON_SHIFT; + writel(misc_reg, &pll->pll_misc); + + /* Enable PLL */ + base_reg |= PLL_ENABLE_MASK; + writel(base_reg, &pll->pll_base); + + /* Disable BYPASS */ + base_reg &= ~PLL_BYPASS_MASK; + writel(base_reg, &pll->pll_base); + + return 0; +} + +void clock_ll_start_uart(enum periph_id periph_id) +{ + /* Assert UART reset and enable clock */ + reset_set_enable(periph_id, 1); + clock_enable(periph_id); + clock_ll_set_source(periph_id, 0); /* UARTx_CLK_SRC = 00, PLLP_OUT0 */ + + /* wait for 2us */ + udelay(2); + + /* De-assert reset to UART */ + reset_set_enable(periph_id, 0); +} + +#ifdef CONFIG_OF_CONTROL +/* + * Convert a device tree clock ID to our peripheral ID. They are mostly + * the same but we are very cautious so we check that a valid clock ID is + * provided. + * + * @param clk_id Clock ID according to tegra20 device tree binding + * @return peripheral ID, or PERIPH_ID_NONE if the clock ID is invalid + */ +static enum periph_id clk_id_to_periph_id(int clk_id) +{ + if (clk_id > 95) + return PERIPH_ID_NONE; + + switch (clk_id) { + case 1: + case 2: + case 7: + case 10: + case 20: + case 30: + case 35: + case 49: + case 56: + case 74: + case 76: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 91: + case 95: + return PERIPH_ID_NONE; + default: + return clk_id; + } +} + +int clock_decode_periph_id(const void *blob, int node) +{ + enum periph_id id; + u32 cell[2]; + int err; + + err = fdtdec_get_int_array(blob, node, "clocks", cell, + ARRAY_SIZE(cell)); + if (err) + return -1; + id = clk_id_to_periph_id(cell[1]); + assert(clock_periph_id_isvalid(id)); + return id; +} +#endif /* CONFIG_OF_CONTROL */ + +int clock_verify(void) +{ + struct clk_pll *pll = get_pll(CLOCK_ID_PERIPH); + u32 reg = readl(&pll->pll_base); + + if (!pllp_valid) { + printf("Warning: PLLP %x is not correct\n", reg); + return -1; + } + debug("PLLX %x is correct\n", reg); + return 0; +} + +void clock_early_init(void) +{ + /* + * PLLP output frequency set to 216Mh + * PLLC output frequency set to 228Mhz + */ + switch (clock_get_osc_freq()) { + case CLOCK_OSC_FREQ_12_0: /* OSC is 12Mhz */ + clock_set_rate(CLOCK_ID_PERIPH, 432, 12, 1, 8); + clock_set_rate(CLOCK_ID_CGENERAL, 456, 12, 1, 8); + break; + + case CLOCK_OSC_FREQ_26_0: /* OSC is 26Mhz */ + clock_set_rate(CLOCK_ID_PERIPH, 432, 26, 1, 8); + clock_set_rate(CLOCK_ID_CGENERAL, 600, 26, 0, 8); + break; + + case CLOCK_OSC_FREQ_13_0: /* OSC is 13Mhz */ + clock_set_rate(CLOCK_ID_PERIPH, 432, 13, 1, 8); + clock_set_rate(CLOCK_ID_CGENERAL, 600, 13, 0, 8); + break; + case CLOCK_OSC_FREQ_19_2: + default: + /* + * These are not supported. It is too early to print a + * message and the UART likely won't work anyway due to the + * oscillator being wrong. + */ + break; + } +} + +void clock_init(void) +{ + pll_rate[CLOCK_ID_MEMORY] = clock_get_rate(CLOCK_ID_MEMORY); + pll_rate[CLOCK_ID_PERIPH] = clock_get_rate(CLOCK_ID_PERIPH); + pll_rate[CLOCK_ID_CGENERAL] = clock_get_rate(CLOCK_ID_CGENERAL); + pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC); + pll_rate[CLOCK_ID_SFROM32KHZ] = 32768; + debug("Osc = %d\n", pll_rate[CLOCK_ID_OSC]); + debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]); + debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]); +} diff --git a/arch/arm/cpu/tegra30-common/funcmux.c b/arch/arm/cpu/tegra30-common/funcmux.c new file mode 100644 index 0000000..6e28ac9 --- /dev/null +++ b/arch/arm/cpu/tegra30-common/funcmux.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (C) 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 + */ + +/* Tegra30 high-level function multiplexing */ + +#include +#include +#include +#include + +int funcmux_select(enum periph_id id, int config) +{ + int bad_config = config != FUNCMUX_DEFAULT; + + switch (id) { + case PERIPH_ID_UART1: + switch (config) { + case FUNCMUX_UART1_ULPI_UART2: + pinmux_set_func(PINGRP_ULPI_DATA0, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_ULPI_DATA1, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_ULPI_DATA2, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_ULPI_DATA3, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_ULPI_DATA4, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_ULPI_DATA5, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_ULPI_DATA6, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_UART2_RTS_N, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_UART2_CTS_N, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PINGRP_ULPI_DATA0); + pinmux_tristate_disable(PINGRP_ULPI_DATA1); + pinmux_tristate_disable(PINGRP_ULPI_DATA2); + pinmux_tristate_disable(PINGRP_ULPI_DATA3); + pinmux_tristate_disable(PINGRP_ULPI_DATA4); + pinmux_tristate_disable(PINGRP_ULPI_DATA5); + pinmux_tristate_disable(PINGRP_ULPI_DATA6); + pinmux_tristate_disable(PINGRP_ULPI_DATA7); + pinmux_tristate_disable(PINGRP_UART2_RTS_N); + pinmux_tristate_disable(PINGRP_UART2_CTS_N); + break; + } + break; + + /* Add other periph IDs here as needed */ + + default: + debug("%s: invalid periph_id %d", __func__, id); + return -1; + } + + if (bad_config) { + debug("%s: invalid config %d for periph_id %d", __func__, + config, id); + return -1; + } + return 0; +} diff --git a/arch/arm/cpu/tegra30-common/lowlevel_init.S b/arch/arm/cpu/tegra30-common/lowlevel_init.S new file mode 100644 index 0000000..91ff3b9 --- /dev/null +++ b/arch/arm/cpu/tegra30-common/lowlevel_init.S @@ -0,0 +1,42 @@ +/* + * SoC-specific setup info + * + * (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 + + .align 5 +ENTRY(reset_cpu) + ldr r1, rstctl @ get addr for global reset + @ reg + ldr r3, [r1] + orr r3, r3, #0x10 + str r3, [r1] @ force reset + mov r0, r0 +_loop_forever: + b _loop_forever +rstctl: + .word PRM_RSTCTRL +ENDPROC(reset_cpu) diff --git a/arch/arm/cpu/tegra30-common/pinmux.c b/arch/arm/cpu/tegra30-common/pinmux.c new file mode 100644 index 0000000..387110e --- /dev/null +++ b/arch/arm/cpu/tegra30-common/pinmux.c @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * Copyright (C) 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 + */ + +/* Tegra30 pin multiplexing functions */ + +#include +#include +#include +#include + +struct tegra_pingroup_desc { + const char *name; + enum pmux_func funcs[4]; + enum pmux_func func_safe; + enum pmux_vddio vddio; + enum pmux_pin_io io; +}; + +#define PMUX_MUXCTL_SHIFT 0 +#define PMUX_PULL_SHIFT 2 +#define PMUX_TRISTATE_SHIFT 4 +#define PMUX_TRISTATE_MASK (1 << PMUX_TRISTATE_SHIFT) +#define PMUX_IO_SHIFT 5 +#define PMUX_OD_SHIFT 6 +#define PMUX_LOCK_SHIFT 7 +#define PMUX_IO_RESET_SHIFT 8 + +/* Convenient macro for defining pin group properties */ +#define PIN(pg_name, vdd, f0, f1, f2, f3, iod) \ + { \ + .vddio = PMUX_VDDIO_ ## vdd, \ + .funcs = { \ + PMUX_FUNC_ ## f0, \ + PMUX_FUNC_ ## f1, \ + PMUX_FUNC_ ## f2, \ + PMUX_FUNC_ ## f3, \ + }, \ + .func_safe = PMUX_FUNC_RSVD, \ + .io = PMUX_PIN_ ## iod, \ + } + +/* Input and output pins */ +#define PINI(pg_name, vdd, f0, f1, f2, f3) \ + PIN(pg_name, vdd, f0, f1, f2, f3, INPUT) +#define PINO(pg_name, vdd, f0, f1, f2, f3) \ + PIN(pg_name, vdd, f0, f1, f2, f3, OUTPUT) + +const struct tegra_pingroup_desc tegra_soc_pingroups[PINGRP_COUNT] = { + /* NAME VDD f0 f1 f2 f3 fSafe io */ + PINI(ULPI_DATA0, BB, SPI3, HSI, UARTA, ULPI), + PINI(ULPI_DATA1, BB, SPI3, HSI, UARTA, ULPI), + PINI(ULPI_DATA2, BB, SPI3, HSI, UARTA, ULPI), + PINI(ULPI_DATA3, BB, SPI3, HSI, UARTA, ULPI), + PINI(ULPI_DATA4, BB, SPI2, HSI, UARTA, ULPI), + PINI(ULPI_DATA5, BB, SPI2, HSI, UARTA, ULPI), + PINI(ULPI_DATA6, BB, SPI2, HSI, UARTA, ULPI), + PINI(ULPI_DATA7, BB, SPI2, HSI, UARTA, ULPI), + PINI(ULPI_CLK, BB, SPI1, RSVD, UARTD, ULPI), + PINI(ULPI_DIR, BB, SPI1, RSVD, UARTD, ULPI), + PINI(ULPI_NXT, BB, SPI1, RSVD, UARTD, ULPI), + PINI(ULPI_STP, BB, SPI1, RSVD, UARTD, ULPI), + PINI(DAP3_FS, BB, I2S2, RSVD1, DISPA, DISPB), + PINI(DAP3_DIN, BB, I2S2, RSVD1, DISPA, DISPB), + PINI(DAP3_DOUT, BB, I2S2, RSVD1, DISPA, DISPB), + PINI(DAP3_SCLK, BB, I2S2, RSVD1, DISPA, DISPB), + PINI(GPIO_PV0, BB, RSVD, RSVD, RSVD, RSVD), + PINI(GPIO_PV1, BB, RSVD, RSVD, RSVD, RSVD), + PINI(SDMMC1_CLK, SDMMC1, SDMMC1, RSVD1, RSVD2, BAD), + PINI(SDMMC1_CMD, SDMMC1, SDMMC1, RSVD1, RSVD2, BAD), + PINI(SDMMC1_DAT3, SDMMC1, SDMMC1, RSVD1, UARTE, BAD), + PINI(SDMMC1_DAT2, SDMMC1, SDMMC1, RSVD1, UARTE, BAD), + PINI(SDMMC1_DAT1, SDMMC1, SDMMC1, RSVD1, UARTE, BAD), + PINI(SDMMC1_DAT0, SDMMC1, SDMMC1, RSVD1, UARTE, BAD), + PINI(GPIO_PV2, SDMMC1, OWR, RSVD1, RSVD2, RSVD3), + PINI(GPIO_PV3, SDMMC1, BAD, RSVD1, RSVD2, RSVD3), + PINI(CLK2_OUT, SDMMC1, EXTPERIPH2, RSVD1, RSVD2, RSVD3), + PINI(CLK2_REQ, SDMMC1, DAP, RSVD1, RSVD2, RSVD3), + PINO(LCD_PWR1, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_PWR2, LCD, DISPA, DISPB, SPI5, BAD), + PINO(LCD_SDIN, LCD, DISPA, DISPB, SPI5, RSVD), + PINO(LCD_SDOUT, LCD, DISPA, DISPB, SPI5, BAD), + PINO(LCD_WR_N, LCD, DISPA, DISPB, SPI5, BAD), + PINO(LCD_CS0_N, LCD, DISPA, DISPB, SPI5, RSVD), + PINO(LCD_DC0, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_SCK, LCD, DISPA, DISPB, SPI5, BAD), + PINO(LCD_PWR0, LCD, DISPA, DISPB, SPI5, BAD), + PINO(LCD_PCLK, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_DE, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_HSYNC, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_VSYNC, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D0, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D1, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D2, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D3, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D4, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D5, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D6, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D7, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D8, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D9, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D10, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D11, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D12, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D13, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D14, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D15, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D16, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D17, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D18, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D19, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D20, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D21, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D22, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_D23, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_CS1_N, LCD, DISPA, DISPB, SPI5, RSVD2), + PINO(LCD_M1, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINO(LCD_DC1, LCD, DISPA, DISPB, RSVD1, RSVD2), + PINI(HDMI_INT, LCD, RSVD, RSVD, RSVD, RSVD), + PINI(DDC_SCL, LCD, I2C4, RSVD1, RSVD2, RSVD3), + PINI(DDC_SDA, LCD, I2C4, RSVD1, RSVD2, RSVD3), + PINI(CRT_HSYNC, LCD, CRT, RSVD1, RSVD2, RSVD3), + PINI(CRT_VSYNC, LCD, CRT, RSVD1, RSVD2, RSVD3), + PINI(VI_D0, VI, BAD, RSVD1, VI, RSVD2), + PINI(VI_D1, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D2, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D3, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D4, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D5, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D6, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D7, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D8, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D9, VI, BAD, SDMMC2, VI, RSVD1), + PINI(VI_D10, VI, BAD, RSVD1, VI, RSVD2), + PINI(VI_D11, VI, BAD, RSVD1, VI, RSVD2), + PINI(VI_PCLK, VI, RSVD1, SDMMC2, VI, RSVD2), + PINI(VI_MCLK, VI, VI, BAD, BAD, BAD), + PINI(VI_VSYNC, VI, BAD, RSVD1, VI, RSVD2), + PINI(VI_HSYNC, VI, BAD, RSVD1, VI, RSVD2), + PINI(UART2_RXD, UART, IRDA, SPDIF, UARTA, SPI4), + PINI(UART2_TXD, UART, IRDA, SPDIF, UARTA, SPI4), + PINI(UART2_RTS_N, UART, UARTA, UARTB, GMI, SPI4), + PINI(UART2_CTS_N, UART, UARTA, UARTB, GMI, SPI4), + PINI(UART3_TXD, UART, UARTC, RSVD1, GMI, RSVD2), + PINI(UART3_RXD, UART, UARTC, RSVD1, GMI, RSVD2), + PINI(UART3_CTS_N, UART, UARTC, RSVD1, GMI, RSVD2), + PINI(UART3_RTS_N, UART, UARTC, PWM0, GMI, RSVD2), + PINI(GPIO_PU0, UART, OWR, UARTA, GMI, RSVD1), + PINI(GPIO_PU1, UART, RSVD1, UARTA, GMI, RSVD2), + PINI(GPIO_PU2, UART, RSVD1, UARTA, GMI, RSVD2), + PINI(GPIO_PU3, UART, PWM0, UARTA, GMI, RSVD1), + PINI(GPIO_PU4, UART, PWM1, UARTA, GMI, RSVD1), + PINI(GPIO_PU5, UART, PWM2, UARTA, GMI, RSVD1), + PINI(GPIO_PU6, UART, PWM3, UARTA, GMI, RSVD1), + PINI(GEN1_I2C_SDA, UART, I2C1, RSVD1, RSVD2, RSVD3), + PINI(GEN1_I2C_SCL, UART, I2C1, RSVD1, RSVD2, RSVD3), + PINI(DAP4_FS, UART, I2S3, RSVD1, GMI, RSVD2), + PINI(DAP4_DIN, UART, I2S3, RSVD1, GMI, RSVD2), + PINI(DAP4_DOUT, UART, I2S3, RSVD1, GMI, RSVD2), + PINI(DAP4_SCLK, UART, I2S3, RSVD1, GMI, RSVD2), + PINI(CLK3_OUT, UART, EXTPERIPH3, RSVD1, RSVD2, RSVD3), + PINI(CLK3_REQ, UART, DEV3, RSVD1, RSVD2, RSVD3), + PINI(GMI_WP_N, GMI, RSVD1, NAND, GMI, GMI_ALT), + PINI(GMI_IORDY, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_WAIT, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_ADV_N, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_CLK, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_CS0_N, GMI, RSVD1, NAND, GMI, BAD), + PINI(GMI_CS1_N, GMI, RSVD1, NAND, GMI, DTV), + PINI(GMI_CS2_N, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_CS3_N, GMI, RSVD1, NAND, GMI, GMI_ALT), + PINI(GMI_CS4_N, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_CS6_N, GMI, NAND, NAND_ALT, GMI, SATA), + PINI(GMI_CS7_N, GMI, NAND, NAND_ALT, GMI, GMI_ALT), + PINI(GMI_AD0, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD1, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD2, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD3, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD4, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD5, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD6, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD7, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD8, GMI, PWM0, NAND, GMI, RSVD2), + PINI(GMI_AD9, GMI, PWM1, NAND, GMI, RSVD2), + PINI(GMI_AD10, GMI, PWM2, NAND, GMI, RSVD2), + PINI(GMI_AD11, GMI, PWM3, NAND, GMI, RSVD2), + PINI(GMI_AD12, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD13, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD14, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_AD15, GMI, RSVD1, NAND, GMI, RSVD2), + PINI(GMI_A16, GMI, UARTD, SPI4, GMI, GMI_ALT), + PINI(GMI_A17, GMI, UARTD, SPI4, GMI, BAD), + PINI(GMI_A18, GMI, UARTD, SPI4, GMI, BAD), + PINI(GMI_A19, GMI, UARTD, SPI4, GMI, RSVD3), + PINI(GMI_WR_N, GMI, RSVD1, NAND, GMI, RSVD3), + PINI(GMI_OE_N, GMI, RSVD1, NAND, GMI, RSVD3), + PINI(GMI_DQS, GMI, RSVD1, NAND, GMI, RSVD3), + PINI(GMI_RST_N, GMI, NAND, NAND_ALT, GMI, RSVD3), + PINI(GEN2_I2C_SCL, GMI, I2C2, BAD, GMI, RSVD3), + PINI(GEN2_I2C_SDA, GMI, I2C2, BAD, GMI, RSVD3), + PINI(SDMMC4_CLK, SDMMC4, BAD, NAND, GMI, SDMMC4), + PINI(SDMMC4_CMD, SDMMC4, I2C3, NAND, GMI, SDMMC4), + PINI(SDMMC4_DAT0, SDMMC4, UARTE, SPI3, GMI, SDMMC4), + PINI(SDMMC4_DAT1, SDMMC4, UARTE, SPI3, GMI, SDMMC4), + PINI(SDMMC4_DAT2, SDMMC4, UARTE, SPI3, GMI, SDMMC4), + PINI(SDMMC4_DAT3, SDMMC4, UARTE, SPI3, GMI, SDMMC4), + PINI(SDMMC4_DAT4, SDMMC4, I2C3, I2S4, GMI, SDMMC4), + PINI(SDMMC4_DAT5, SDMMC4, VGP3, I2S4, GMI, SDMMC4), + PINI(SDMMC4_DAT6, SDMMC4, VGP4, I2S4, GMI, SDMMC4), + PINI(SDMMC4_DAT7, SDMMC4, VGP5, I2S4, GMI, SDMMC4), + PINI(SDMMC4_RST_N, SDMMC4, VGP6, RSVD1, RSVD2, POPSDMMC4), + PINI(CAM_MCLK, CAM, VI, BAD, VI_ALT2, POPSDMMC4), + PINI(GPIO_PCC1, CAM, I2S4, RSVD1, RSVD2, POPSDMMC4), + PINI(GPIO_PBB0, CAM, I2S4, RSVD1, RSVD2, POPSDMMC4), + PINI(CAM_I2C_SCL, CAM, BAD, I2C3, RSVD2, POPSDMMC4), + PINI(CAM_I2C_SDA, CAM, BAD, I2C3, RSVD2, POPSDMMC4), + PINI(GPIO_PBB3, CAM, VGP3, DISPA, DISPB, POPSDMMC4), + PINI(GPIO_PBB4, CAM, VGP4, DISPA, DISPB, POPSDMMC4), + PINI(GPIO_PBB5, CAM, VGP5, DISPA, DISPB, POPSDMMC4), + PINI(GPIO_PBB6, CAM, VGP6, DISPA, DISPB, POPSDMMC4), + PINI(GPIO_PBB7, CAM, I2S4, RSVD1, RSVD2, POPSDMMC4), + PINI(GPIO_PCC2, CAM, I2S4, RSVD1, RSVD2, RSVD3), + PINI(JTAG_RTCK, SYS, RTCK, RSVD1, RSVD2, RSVD3), + PINI(PWR_I2C_SCL, SYS, I2CPWR, RSVD1, RSVD2, RSVD3), + PINI(PWR_I2C_SDA, SYS, I2CPWR, RSVD1, RSVD2, RSVD3), + PINI(KB_ROW0, SYS, KBC, BAD, RSVD2, RSVD3), + PINI(KB_ROW1, SYS, KBC, BAD, RSVD2, RSVD3), + PINI(KB_ROW2, SYS, KBC, BAD, RSVD2, RSVD3), + PINI(KB_ROW3, SYS, KBC, BAD, RSVD2, BAD), + PINI(KB_ROW4, SYS, KBC, BAD, TRACE, RSVD3), + PINI(KB_ROW5, SYS, KBC, BAD, TRACE, OWR), + PINI(KB_ROW6, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW7, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW8, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW9, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW10, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW11, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW12, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW13, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW14, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_ROW15, SYS, KBC, BAD, SDMMC2, BAD), + PINI(KB_COL0, SYS, KBC, BAD, TRACE, BAD), + PINI(KB_COL1, SYS, KBC, BAD, TRACE, BAD), + PINI(KB_COL2, SYS, KBC, BAD, TRACE, RSVD), + PINI(KB_COL3, SYS, KBC, BAD, TRACE, RSVD), + PINI(KB_COL4, SYS, KBC, BAD, TRACE, RSVD), + PINI(KB_COL5, SYS, KBC, BAD, TRACE, RSVD), + PINI(KB_COL6, SYS, KBC, BAD, TRACE, BAD), + PINI(KB_COL7, SYS, KBC, BAD, TRACE, BAD), + PINI(CLK_32K_OUT, SYS, BLINK, RSVD1, RSVD2, RSVD3), + PINI(SYS_CLK_REQ, SYS, SYSCLK, RSVD1, RSVD2, RSVD3), + PINI(CORE_PWR_REQ, SYS, RSVD, RSVD, RSVD, RSVD), + PINI(CPU_PWR_REQ, SYS, RSVD, RSVD, RSVD, RSVD), + PINI(PWR_INT_N, SYS, RSVD, RSVD, RSVD, RSVD), + PINI(CLK_32K_IN, SYS, RSVD, RSVD, RSVD, RSVD), + PINI(OWR, SYS, OWR, RSVD, RSVD, RSVD), + PINI(DAP1_FS, AUDIO, I2S0, HDA, GMI, SDMMC2), + PINI(DAP1_DIN, AUDIO, I2S0, HDA, GMI, SDMMC2), + PINI(DAP1_DOUT, AUDIO, I2S0, HDA, GMI, SDMMC2), + PINI(DAP1_SCLK, AUDIO, I2S0, HDA, GMI, SDMMC2), + PINI(CLK1_REQ, AUDIO, DAP, HDA, RSVD2, RSVD3), + PINI(CLK1_OUT, AUDIO, EXTPERIPH1, RSVD1, RSVD2, RSVD3), + PINI(SPDIF_IN, AUDIO, SPDIF, HDA, BAD, DAPSDMMC2), + PINI(SPDIF_OUT, AUDIO, SPDIF, RSVD1, BAD, DAPSDMMC2), + PINI(DAP2_FS, AUDIO, I2S1, HDA, RSVD2, GMI), + PINI(DAP2_DIN, AUDIO, I2S1, HDA, RSVD2, GMI), + PINI(DAP2_DOUT, AUDIO, I2S1, HDA, RSVD2, GMI), + PINI(DAP2_SCLK, AUDIO, I2S1, HDA, RSVD2, GMI), + PINI(SPI2_MOSI, AUDIO, SPI6, SPI2, BAD, GMI), + PINI(SPI2_MISO, AUDIO, SPI6, SPI2, BAD, GMI), + PINI(SPI2_CS0_N, AUDIO, SPI6, SPI2, BAD, GMI), + PINI(SPI2_SCK, AUDIO, SPI6, SPI2, BAD, GMI), + PINI(SPI1_MOSI, AUDIO, SPI2, SPI1, BAD, GMI), + PINI(SPI1_SCK, AUDIO, SPI2, SPI1, BAD, GMI), + PINI(SPI1_CS0_N, AUDIO, SPI2, SPI1, BAD, GMI), + PINI(SPI1_MISO, AUDIO, BAD, SPI1, BAD, RSVD3), + PINI(SPI2_CS1_N, AUDIO, BAD, SPI2, BAD, BAD), + PINI(SPI2_CS2_N, AUDIO, BAD, SPI2, BAD, BAD), + PINI(SDMMC3_CLK, SDMMC3, UARTA, PWM2, SDMMC3, BAD), + PINI(SDMMC3_CMD, SDMMC3, UARTA, PWM3, SDMMC3, BAD), + PINI(SDMMC3_DAT0, SDMMC3, RSVD0, RSVD1, SDMMC3, BAD), + PINI(SDMMC3_DAT1, SDMMC3, RSVD0, RSVD1, SDMMC3, BAD), + PINI(SDMMC3_DAT2, SDMMC3, RSVD0, PWM1, SDMMC3, BAD), + PINI(SDMMC3_DAT3, SDMMC3, RSVD0, PWM0, SDMMC3, BAD), + PINI(SDMMC3_DAT4, SDMMC3, PWM1, BAD, SDMMC3, BAD), + PINI(SDMMC3_DAT5, SDMMC3, PWM0, BAD, SDMMC3, BAD), + PINI(SDMMC3_DAT6, SDMMC3, SPDIF, BAD, SDMMC3, BAD), + PINI(SDMMC3_DAT7, SDMMC3, SPDIF, BAD, SDMMC3, BAD), + PINI(PEX_L0_PRSNT_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L0_RST_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L0_CLKREQ_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_WAKE_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L1_PRSNT_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L1_RST_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L1_CLKREQ_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L2_PRSNT_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L2_RST_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(PEX_L2_CLKREQ_N, PEXCTL, PCIE, HDA, RSVD2, RSVD3), + PINI(HDMI_CEC, SYS, CEC, RSVD1, RSVD2, RSVD3), +}; + +void pinmux_set_tristate(enum pmux_pingrp pin, int enable) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *tri = &pmt->pmt_ctl[pin]; + u32 reg; + + /* Error check on pin */ + assert(pmux_pingrp_isvalid(pin)); + + reg = readl(tri); + if (enable) + reg |= PMUX_TRISTATE_MASK; + else + reg &= ~PMUX_TRISTATE_MASK; + writel(reg, tri); +} + +void pinmux_tristate_enable(enum pmux_pingrp pin) +{ + pinmux_set_tristate(pin, 1); +} + +void pinmux_tristate_disable(enum pmux_pingrp pin) +{ + pinmux_set_tristate(pin, 0); +} + +void pinmux_set_pullupdown(enum pmux_pingrp pin, enum pmux_pull pupd) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pull = &pmt->pmt_ctl[pin]; + u32 reg; + + /* Error check on pin and pupd */ + assert(pmux_pingrp_isvalid(pin)); + assert(pmux_pin_pupd_isvalid(pupd)); + + reg = readl(pull); + reg &= ~(0x3 << PMUX_PULL_SHIFT); + reg |= (pupd << PMUX_PULL_SHIFT); + writel(reg, pull); +} + +void pinmux_set_func(enum pmux_pingrp pin, enum pmux_func func) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *muxctl = &pmt->pmt_ctl[pin]; + int i, mux = -1; + u32 reg; + + /* Error check on pin and func */ + assert(pmux_pingrp_isvalid(pin)); + assert(pmux_func_isvalid(func)); + + /* Handle special values */ + if (func == PMUX_FUNC_SAFE) + func = tegra_soc_pingroups[pin].func_safe; + + if (func & PMUX_FUNC_RSVD) { + mux = func & 0x3; + } else { + /* Search for the appropriate function */ + for (i = 0; i < 4; i++) { + if (tegra_soc_pingroups[pin].funcs[i] == func) { + mux = i; + break; + } + } + } + assert(mux != -1); + + reg = readl(muxctl); + reg &= ~(0x3 << PMUX_MUXCTL_SHIFT); + reg |= (mux << PMUX_MUXCTL_SHIFT); + writel(reg, muxctl); + +} + +void pinmux_set_io(enum pmux_pingrp pin, enum pmux_pin_io io) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pin_io = &pmt->pmt_ctl[pin]; + u32 reg; + + /* Error check on pin and io */ + assert(pmux_pingrp_isvalid(pin)); + assert(pmux_pin_io_isvalid(io)); + + reg = readl(pin_io); + reg &= ~(0x1 << PMUX_IO_SHIFT); + reg |= (io & 0x1) << PMUX_IO_SHIFT; + writel(reg, pin_io); +} + +static int pinmux_set_lock(enum pmux_pingrp pin, enum pmux_pin_lock lock) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pin_lock = &pmt->pmt_ctl[pin]; + u32 reg; + + /* Error check on pin and lock */ + assert(pmux_pingrp_isvalid(pin)); + assert(pmux_pin_lock_isvalid(lock)); + + if (lock == PMUX_PIN_LOCK_DEFAULT) + return 0; + + reg = readl(pin_lock); + reg &= ~(0x1 << PMUX_LOCK_SHIFT); + if (lock == PMUX_PIN_LOCK_ENABLE) + reg |= (0x1 << PMUX_LOCK_SHIFT); + writel(reg, pin_lock); + + return 0; +} + +static int pinmux_set_od(enum pmux_pingrp pin, enum pmux_pin_od od) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pin_od = &pmt->pmt_ctl[pin]; + u32 reg; + + /* Error check on pin and od */ + assert(pmux_pingrp_isvalid(pin)); + assert(pmux_pin_od_isvalid(od)); + + if (od == PMUX_PIN_OD_DEFAULT) + return 0; + + reg = readl(pin_od); + reg &= ~(0x1 << PMUX_OD_SHIFT); + if (od == PMUX_PIN_OD_ENABLE) + reg |= (0x1 << PMUX_OD_SHIFT); + writel(reg, pin_od); + + return 0; +} + +static int pinmux_set_ioreset(enum pmux_pingrp pin, + enum pmux_pin_ioreset ioreset) +{ + struct pmux_tri_ctlr *pmt = + (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; + u32 *pin_ioreset = &pmt->pmt_ctl[pin]; + u32 reg; + + /* Error check on pin and ioreset */ + assert(pmux_pingrp_isvalid(pin)); + assert(pmux_pin_ioreset_isvalid(ioreset)); + + if (ioreset == PMUX_PIN_IO_RESET_DEFAULT) + return 0; + + reg = readl(pin_ioreset); + reg &= ~(0x1 << PMUX_IO_RESET_SHIFT); + if (ioreset == PMUX_PIN_IO_RESET_ENABLE) + reg |= (0x1 << PMUX_IO_RESET_SHIFT); + writel(reg, pin_ioreset); + + return 0; +} + +void pinmux_config_pingroup(struct pingroup_config *config) +{ + enum pmux_pingrp pin = config->pingroup; + + pinmux_set_func(pin, config->func); + pinmux_set_pullupdown(pin, config->pull); + pinmux_set_tristate(pin, config->tristate); + pinmux_set_io(pin, config->io); + pinmux_set_lock(pin, config->lock); + pinmux_set_od(pin, config->od); + pinmux_set_ioreset(pin, config->ioreset); +} + +void pinmux_config_table(struct pingroup_config *config, int len) +{ + int i; + + for (i = 0; i < len; i++) + pinmux_config_pingroup(&config[i]); +} diff --git a/arch/arm/cpu/tegra30-common/sys_info.c b/arch/arm/cpu/tegra30-common/sys_info.c new file mode 100644 index 0000000..eb638da --- /dev/null +++ b/arch/arm/cpu/tegra30-common/sys_info.c @@ -0,0 +1,35 @@ +/* + * (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 + +#ifdef CONFIG_DISPLAY_CPUINFO +/* Print CPU information */ +int print_cpuinfo(void) +{ + puts("TEGRA30\n"); + + /* TBD: Add printf of major/minor rev info, stepping, etc. */ + return 0; +} +#endif /* CONFIG_DISPLAY_CPUINFO */ diff --git a/arch/arm/cpu/tegra30-common/timer.c b/arch/arm/cpu/tegra30-common/timer.c new file mode 100644 index 0000000..c532580 --- /dev/null +++ b/arch/arm/cpu/tegra30-common/timer.c @@ -0,0 +1,111 @@ +/* + * (C) Copyright 2010-2012 + * NVIDIA Corporation + * + * (C) Copyright 2008 + * Texas Instruments + * + * Richard Woodruff + * Syed Moahmmed Khasim + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH + * Marius Groeger + * Alex Zuepke + * + * (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 + */ + +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* counter runs at 1MHz */ +#define TIMER_CLK 1000000 +#define TIMER_LOAD_VAL 0xffffffff + +/* timer without interrupts */ +ulong get_timer(ulong base) +{ + return get_timer_masked() - base; +} + +/* delay x useconds */ +void __udelay(unsigned long usec) +{ + long tmo = usec * (TIMER_CLK / 1000) / 1000; + unsigned long now, last = timer_get_us(); + + while (tmo > 0) { + now = timer_get_us(); + if (last > now) /* count up timer overflow */ + tmo -= TIMER_LOAD_VAL - last + now; + else + tmo -= now - last; + last = now; + } +} + +ulong get_timer_masked(void) +{ + ulong now; + + /* current tick value */ + now = timer_get_us() / (TIMER_CLK / CONFIG_SYS_HZ); + + if (now >= gd->lastinc) /* normal mode (non roll) */ + /* move stamp forward with absolute diff ticks */ + gd->tbl += (now - gd->lastinc); + else /* we have rollover of incrementer */ + gd->tbl += ((TIMER_LOAD_VAL / (TIMER_CLK / CONFIG_SYS_HZ)) + - gd->lastinc) + now; + gd->lastinc = now; + return gd->tbl; +} + +/* + * This function is derived from PowerPC code (read timebase as long long). + * On ARM it just returns the timer value. + */ +unsigned long long get_ticks(void) +{ + return get_timer(0); +} + +/* + * This function is derived from PowerPC code (timebase clock frequency). + * On ARM it returns the number of timer ticks per second. + */ +ulong get_tbclk(void) +{ + return CONFIG_SYS_HZ; +} + +unsigned long timer_get_us(void) +{ + struct timerus *timer_base = (struct timerus *)NV_PA_TMRUS_BASE; + + return readl(&timer_base->cntr_1us); +}