diff options
Diffstat (limited to 'arch/arm/cpu/tegra20-common')
| -rw-r--r-- | arch/arm/cpu/tegra20-common/Makefile | 55 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/ap20.c | 131 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/board.c | 146 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/clock.c | 1087 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/crypto.c | 230 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/crypto.h | 36 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/emc.c | 286 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/funcmux.c | 256 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/lowlevel_init.S | 42 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/pinmux.c | 572 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/pmu.c | 70 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/sys_info.c | 35 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/timer.c | 111 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/warmboot.c | 386 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/warmboot_avp.c | 250 | ||||
| -rw-r--r-- | arch/arm/cpu/tegra20-common/warmboot_avp.h | 81 | 
16 files changed, 3774 insertions, 0 deletions
| diff --git a/arch/arm/cpu/tegra20-common/Makefile b/arch/arm/cpu/tegra20-common/Makefile new file mode 100644 index 000000000..9e91e5cb8 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/Makefile @@ -0,0 +1,55 @@ +# +# (C) Copyright 2010,2011 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. +CFLAGS_arch/arm/cpu/tegra20-common/warmboot_avp.o += -march=armv4t + +LIB	= $(obj)lib$(SOC)-common.o + +SOBJS += lowlevel_init.o +COBJS-y	+= ap20.o board.o clock.o funcmux.o pinmux.o sys_info.o timer.o +COBJS-$(CONFIG_TEGRA_LP0) += warmboot.o crypto.o warmboot_avp.o +COBJS-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o +COBJS-$(CONFIG_TEGRA_PMU) += pmu.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/tegra20-common/ap20.c b/arch/arm/cpu/tegra20-common/ap20.c new file mode 100644 index 000000000..c0ca6eb37 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/ap20.c @@ -0,0 +1,131 @@ +/* +* (C) Copyright 2010-2011 +* NVIDIA Corporation <www.nvidia.com> +* +* See file CREDITS for list of people who contributed to this +* project. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, +* MA 02111-1307 USA +*/ +#include <asm/io.h> +#include <asm/arch/ap20.h> +#include <asm/arch/fuse.h> +#include <asm/arch/gp_padctrl.h> +#include <asm/arch/pmc.h> +#include <asm/arch/scu.h> +#include <asm/arch/warmboot.h> +#include <common.h> + +int tegra_get_chip_type(void) +{ +	struct apb_misc_gp_ctlr *gp; +	struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE; +	uint tegra_sku_id, rev; + +	/* +	 * This is undocumented, Chip ID is bits 15:8 of the register +	 * APB_MISC + 0x804, and has value 0x20 for Tegra20, 0x30 for +	 * Tegra30 +	 */ +	gp = (struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; +	rev = (readl(&gp->hidrev) & HIDREV_CHIPID_MASK) >> HIDREV_CHIPID_SHIFT; + +	tegra_sku_id = readl(&fuse->sku_info) & 0xff; + +	switch (rev) { +	case CHIPID_TEGRA20: +		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; +		} +		break; +	} +	/* unknown sku id */ +	return TEGRA_SOC_UNKNOWN; +} + +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(AP20_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 ap20's L2 disabled too? */ +} diff --git a/arch/arm/cpu/tegra20-common/board.c b/arch/arm/cpu/tegra20-common/board.c new file mode 100644 index 000000000..8a8d3384a --- /dev/null +++ b/arch/arm/cpu/tegra20-common/board.c @@ -0,0 +1,146 @@ +/* + *  (C) Copyright 2010,2011 + *  NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch/pmc.h> +#include <asm/arch/sys_proto.h> +#include <asm/arch/tegra20.h> +#include <asm/arch/warmboot.h> + +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: +	default: +		return 0x20000000;	/* 512 MB */ +	case 3: +		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[] = { +#if defined(CONFIG_TEGRA_UARTA_UAA_UAB) +	FUNCMUX_UART1_UAA_UAB, +#elif defined(CONFIG_TEGRA_UARTA_GPU) +	FUNCMUX_UART1_GPU, +#elif defined(CONFIG_TEGRA_UARTA_SDIO1) +	FUNCMUX_UART1_SDIO1, +#else +	FUNCMUX_UART1_IRRX_IRTX, +#endif +	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/tegra20-common/clock.c b/arch/arm/cpu/tegra20-common/clock.c new file mode 100644 index 000000000..24038745b --- /dev/null +++ b/arch/arm/cpu/tegra20-common/clock.c @@ -0,0 +1,1087 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * 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 + */ + +/* Tegra20 Clock control functions */ + +#include <asm/io.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch/timer.h> +#include <asm/arch/tegra20.h> +#include <common.h> +#include <div64.h> +#include <fdtdec.h> + +/* + * 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 Tegra20 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_PCMT16,	/* CLOCK_TYPE_PCMT with 16-bit divider */ +	CLOCK_TYPE_PCXTS, +	CLOCK_TYPE_PDCT, + +	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	= 4	/* number of source options for each clock */ +}; + +/* + * Clock source mux for each clock type. This just converts our enum into + * a list of mux sources for use by the code. Note that CLOCK_TYPE_PCXTS + * is special as it has 5 sources. Since it also has a different number of + * bits in its register for the source, we just handle it with a special + * case in the code. + */ +#define CLK(x) CLOCK_ID_ ## x +static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX] = { +	{ CLK(AUDIO),	CLK(XCPU),	CLK(PERIPH),	CLK(OSC)	}, +	{ CLK(MEMORY),	CLK(CGENERAL),	CLK(PERIPH),	CLK(AUDIO)	}, +	{ CLK(MEMORY),	CLK(CGENERAL),	CLK(PERIPH),	CLK(OSC)	}, +	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(MEMORY),	CLK(NONE)	}, +	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(MEMORY),	CLK(OSC)	}, +	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(MEMORY),	CLK(OSC)	}, +	{ CLK(PERIPH),	CLK(CGENERAL),	CLK(XCPU),	CLK(OSC)	}, +	{ CLK(PERIPH),	CLK(DISPLAY),	CLK(CGENERAL),	CLK(OSC)	}, +}; + +/* + * Clock peripheral IDs which sadly don't match up with PERIPH_ID. This is + * not in the header file since it is for purely internal use - we want + * callers to use the PERIPH_ID for all access to peripheral clocks to avoid + * confusion bewteen PERIPH_ID_... and PERIPHC_... + * + * We don't call this CLOCK_PERIPH_ID or PERIPH_CLOCK_ID as it would just be + * confusing. + * + * Note to SOC vendors: perhaps define a unified numbering for peripherals and + * use it for reset, clock enable, clock source/divider and even pinmuxing + * if you can. + */ +enum periphc_internal_id { +	/* 0x00 */ +	PERIPHC_I2S1, +	PERIPHC_I2S2, +	PERIPHC_SPDIF_OUT, +	PERIPHC_SPDIF_IN, +	PERIPHC_PWM, +	PERIPHC_SPI1, +	PERIPHC_SPI2, +	PERIPHC_SPI3, + +	/* 0x08 */ +	PERIPHC_XIO, +	PERIPHC_I2C1, +	PERIPHC_DVC_I2C, +	PERIPHC_TWC, +	PERIPHC_0c, +	PERIPHC_10,	/* PERIPHC_SPI1, what is this really? */ +	PERIPHC_DISP1, +	PERIPHC_DISP2, + +	/* 0x10 */ +	PERIPHC_CVE, +	PERIPHC_IDE0, +	PERIPHC_VI, +	PERIPHC_1c, +	PERIPHC_SDMMC1, +	PERIPHC_SDMMC2, +	PERIPHC_G3D, +	PERIPHC_G2D, + +	/* 0x18 */ +	PERIPHC_NDFLASH, +	PERIPHC_SDMMC4, +	PERIPHC_VFIR, +	PERIPHC_EPP, +	PERIPHC_MPE, +	PERIPHC_MIPI, +	PERIPHC_UART1, +	PERIPHC_UART2, + +	/* 0x20 */ +	PERIPHC_HOST1X, +	PERIPHC_21, +	PERIPHC_TVO, +	PERIPHC_HDMI, +	PERIPHC_24, +	PERIPHC_TVDAC, +	PERIPHC_I2C2, +	PERIPHC_EMC, + +	/* 0x28 */ +	PERIPHC_UART3, +	PERIPHC_29, +	PERIPHC_VI_SENSOR, +	PERIPHC_2b, +	PERIPHC_2c, +	PERIPHC_SPI4, +	PERIPHC_I2C3, +	PERIPHC_SDMMC3, + +	/* 0x30 */ +	PERIPHC_UART4, +	PERIPHC_UART5, +	PERIPHC_VDE, +	PERIPHC_OWR, +	PERIPHC_NOR, +	PERIPHC_CSITE, + +	PERIPHC_COUNT, + +	PERIPHC_NONE = -1, +}; + +/* 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_PCXTS), +	TYPE(PERIPHC_SPI1,	CLOCK_TYPE_PCMT), +	TYPE(PERIPHC_SPI22,	CLOCK_TYPE_PCMT), +	TYPE(PERIPHC_SPI3,	CLOCK_TYPE_PCMT), + +	/* 0x08 */ +	TYPE(PERIPHC_XIO,	CLOCK_TYPE_PCMT), +	TYPE(PERIPHC_I2C1,	CLOCK_TYPE_PCMT16), +	TYPE(PERIPHC_DVC_I2C,	CLOCK_TYPE_PCMT16), +	TYPE(PERIPHC_TWC,	CLOCK_TYPE_PCMT), +	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE), +	TYPE(PERIPHC_SPI1,	CLOCK_TYPE_PCMT), +	TYPE(PERIPHC_DISP1,	CLOCK_TYPE_PDCT), +	TYPE(PERIPHC_DISP2,	CLOCK_TYPE_PDCT), + +	/* 0x10 */ +	TYPE(PERIPHC_CVE,	CLOCK_TYPE_PDCT), +	TYPE(PERIPHC_IDE0,	CLOCK_TYPE_PCMT), +	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_PDCT), +	TYPE(PERIPHC_NONE,	CLOCK_TYPE_NONE), +	TYPE(PERIPHC_TVDAC,	CLOCK_TYPE_PDCT), +	TYPE(PERIPHC_I2C2,	CLOCK_TYPE_PCMT16), +	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_SPI4,	CLOCK_TYPE_PCMT), +	TYPE(PERIPHC_I2C3,	CLOCK_TYPE_PCMT16), +	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), +}; + +/* + * 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(RESERVED1), +	NONE(RESERVED2), +	NONE(AC97), +	NONE(RTC), +	NONE(TMR), +	PERIPHC_UART1, +	PERIPHC_UART2,	/* and vfir 0x68 */ + +	/* 0x08 */ +	NONE(GPIO), +	PERIPHC_SDMMC2, +	NONE(SPDIF),		/* 0x08 and 0x0c, unclear which to use */ +	PERIPHC_I2S1, +	PERIPHC_I2C1, +	PERIPHC_NDFLASH, +	PERIPHC_SDMMC1, +	PERIPHC_SDMMC4, + +	/* 0x10 */ +	PERIPHC_TWC, +	PERIPHC_PWM, +	PERIPHC_I2S2, +	PERIPHC_EPP, +	PERIPHC_VI, +	PERIPHC_G2D, +	NONE(USBD), +	NONE(ISP), + +	/* 0x18 */ +	PERIPHC_G3D, +	PERIPHC_IDE0, +	PERIPHC_DISP2, +	PERIPHC_DISP1, +	PERIPHC_HOST1X, +	NONE(VCP), +	NONE(RESERVED30), +	NONE(CACHE2), + +	/* Middle word: 63:32 */ +	NONE(MEM), +	NONE(AHBDMA), +	NONE(APBDMA), +	NONE(RESERVED35), +	NONE(KBC), +	NONE(STAT_MON), +	NONE(PMC), +	NONE(FUSE), + +	/* 0x28 */ +	NONE(KFUSE), +	NONE(SBC1),	/* SBC1, 0x34, is this SPI1? */ +	PERIPHC_NOR, +	PERIPHC_SPI1, +	PERIPHC_SPI2, +	PERIPHC_XIO, +	PERIPHC_SPI3, +	PERIPHC_DVC_I2C, + +	/* 0x30 */ +	NONE(DSI), +	PERIPHC_TVO,	/* also CVE 0x40 */ +	PERIPHC_MIPI, +	PERIPHC_HDMI, +	PERIPHC_CSITE, +	PERIPHC_TVDAC, +	PERIPHC_I2C2, +	PERIPHC_UART3, + +	/* 0x38 */ +	NONE(RESERVED56), +	PERIPHC_EMC, +	NONE(USB2), +	NONE(USB3), +	PERIPHC_MPE, +	PERIPHC_VDE, +	NONE(BSEA), +	NONE(BSEV), + +	/* Upper word 95:64 */ +	NONE(SPEEDO), +	PERIPHC_UART4, +	PERIPHC_UART5, +	PERIPHC_I2C3, +	PERIPHC_SPI4, +	PERIPHC_SDMMC3, +	NONE(PCIE), +	PERIPHC_OWR, + +	/* 0x48 */ +	NONE(AFI), +	NONE(CORESIGHT), +	NONE(RESERVED74), +	NONE(AVPUCQ), +	NONE(RESERVED76), +	NONE(RESERVED77), +	NONE(RESERVED78), +	NONE(RESERVED79), + +	/* 0x50 */ +	NONE(RESERVED80), +	NONE(RESERVED81), +	NONE(RESERVED82), +	NONE(RESERVED83), +	NONE(IRAMA), +	NONE(IRAMB), +	NONE(IRAMC), +	NONE(IRAMD), + +	/* 0x58 */ +	NONE(CRAM2), +}; + +/* + * 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; +} + +/* return 1 if a peripheral ID is in range and valid */ +static int clock_periph_id_isvalid(enum periph_id id) +{ +	if (id < PERIPH_ID_FIRST || id >= PERIPH_ID_COUNT) +		printf("Peripheral id %d out of range\n", id); +	else { +		switch (id) { +		case PERIPH_ID_RESERVED1: +		case PERIPH_ID_RESERVED2: +		case PERIPH_ID_RESERVED30: +		case PERIPH_ID_RESERVED35: +		case PERIPH_ID_RESERVED56: +		case PERIPH_ID_RESERVED74: +		case PERIPH_ID_RESERVED76: +		case PERIPH_ID_RESERVED77: +		case PERIPH_ID_RESERVED78: +		case PERIPH_ID_RESERVED79: +		case PERIPH_ID_RESERVED80: +		case PERIPH_ID_RESERVED81: +		case PERIPH_ID_RESERVED82: +		case PERIPH_ID_RESERVED83: +			printf("Peripheral id %d is reserved\n", id); +			break; +		default: +			return 1; +		} +	} +	return 0; +} + +/* 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; + +	assert(clock_periph_id_isvalid(periph_id)); +	internal_id = periph_id_to_internal_id[periph_id]; +	assert(internal_id != -1); +	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)); + +	/* +	 * Special cases here for the clock with a 4-bit source mux and I2C +	 * with its 16-bit divisor +	 */ +	if (type == CLOCK_TYPE_PCXTS) +		*mux_bits = 4; +	else +		*mux_bits = 2; +	if (type == CLOCK_TYPE_PCMT16) +		*divider_bits = 16; +	else +		*divider_bits = 8; + +	for (mux = 0; mux < CLOCK_MAX_MUX; mux++) +		if (clock_source[type][mux] == parent) +			return mux; + +	/* +	 * Not found: it might be looking for the 'S' in CLOCK_TYPE_PCXTS +	 * which is not in our table. If not, then they are asking for a +	 * source which this peripheral can't access through its mux. +	 */ +	assert(type == CLOCK_TYPE_PCXTS); +	assert(parent == CLOCK_ID_SFROM32KHZ); +	if (type == CLOCK_TYPE_PCXTS && parent == CLOCK_ID_SFROM32KHZ) +		return 4;	/* mux value for this clock */ + +	/* 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, divider_bits, source; +	int divider; + +	/* 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 = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)]; +	u32 reg; + +	/* Enable/disable the clock to this peripheral */ +	assert(clock_periph_id_isvalid(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 = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)]; +	u32 reg; + +	/* Enable/disable reset to the peripheral */ +	assert(clock_periph_id_isvalid(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. Tegra20 has 2 */ +	assert(cpu >= 0 && cpu < 2); +	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 216MHz +	 * PLLC output frequency set to 600Mhz +	 * +	 * TODO: Can we calculate these values instead of hard-coding? +	 */ +	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, 600, 12, 0, 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/tegra20-common/crypto.c b/arch/arm/cpu/tegra20-common/crypto.c new file mode 100644 index 000000000..5f0b240e2 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/crypto.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010 - 2011 NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/errno.h> +#include "crypto.h" +#include "aes.h" + +static u8 zero_key[16]; + +#define AES_CMAC_CONST_RB 0x87  /* from RFC 4493, Figure 2.2 */ + +enum security_op { +	SECURITY_SIGN		= 1 << 0,	/* Sign the data */ +	SECURITY_ENCRYPT	= 1 << 1,	/* Encrypt the data */ +}; + +static void debug_print_vector(char *name, u32 num_bytes, u8 *data) +{ +	u32 i; + +	debug("%s [%d] @0x%08x", name, num_bytes, (u32)data); +	for (i = 0; i < num_bytes; i++) { +		if (i % 16 == 0) +			debug(" = "); +		debug("%02x", data[i]); +		if ((i+1) % 16 != 0) +			debug(" "); +	} +	debug("\n"); +} + +/** + * Apply chain data to the destination using EOR + * + * Each array is of length AES_AES_KEY_LENGTH. + * + * \param cbc_chain_data	Chain data + * \param src			Source data + * \param dst			Destination data, which is modified here + */ +static void apply_cbc_chain_data(u8 *cbc_chain_data, u8 *src, u8 *dst) +{ +	int i; + +	for (i = 0; i < 16; i++) +		*dst++ = *src++ ^ *cbc_chain_data++; +} + +/** + * Encrypt some data with AES. + * + * \param key_schedule		Expanded key to use + * \param src			Source data to encrypt + * \param dst			Destination buffer + * \param num_aes_blocks	Number of AES blocks to encrypt + */ +static void encrypt_object(u8 *key_schedule, u8 *src, u8 *dst, +			   u32 num_aes_blocks) +{ +	u8 tmp_data[AES_KEY_LENGTH]; +	u8 *cbc_chain_data; +	u32 i; + +	cbc_chain_data = zero_key;	/* Convenient array of 0's for IV */ + +	for (i = 0; i < num_aes_blocks; i++) { +		debug("encrypt_object: block %d of %d\n", i, num_aes_blocks); +		debug_print_vector("AES Src", AES_KEY_LENGTH, src); + +		/* Apply the chain data */ +		apply_cbc_chain_data(cbc_chain_data, src, tmp_data); +		debug_print_vector("AES Xor", AES_KEY_LENGTH, tmp_data); + +		/* encrypt the AES block */ +		aes_encrypt(tmp_data, key_schedule, dst); +		debug_print_vector("AES Dst", AES_KEY_LENGTH, dst); + +		/* Update pointers for next loop. */ +		cbc_chain_data = dst; +		src += AES_KEY_LENGTH; +		dst += AES_KEY_LENGTH; +	} +} + +/** + * Shift a vector left by one bit + * + * \param in	Input vector + * \param out	Output vector + * \param size	Length of vector in bytes + */ +static void left_shift_vector(u8 *in, u8 *out, int size) +{ +	int carry = 0; +	int i; + +	for (i = size - 1; i >= 0; i--) { +		out[i] = (in[i] << 1) | carry; +		carry = in[i] >> 7;	/* get most significant bit */ +	} +} + +/** + * Sign a block of data, putting the result into dst. + * + * \param key			Input AES key, length AES_KEY_LENGTH + * \param key_schedule		Expanded key to use + * \param src			Source data of length 'num_aes_blocks' blocks + * \param dst			Destination buffer, length AES_KEY_LENGTH + * \param num_aes_blocks	Number of AES blocks to encrypt + */ +static void sign_object(u8 *key, u8 *key_schedule, u8 *src, u8 *dst, +			u32 num_aes_blocks) +{ +	u8 tmp_data[AES_KEY_LENGTH]; +	u8 left[AES_KEY_LENGTH]; +	u8 k1[AES_KEY_LENGTH]; +	u8 *cbc_chain_data; +	unsigned i; + +	cbc_chain_data = zero_key;	/* Convenient array of 0's for IV */ + +	/* compute K1 constant needed by AES-CMAC calculation */ +	for (i = 0; i < AES_KEY_LENGTH; i++) +		tmp_data[i] = 0; + +	encrypt_object(key_schedule, tmp_data, left, 1); +	debug_print_vector("AES(key, nonce)", AES_KEY_LENGTH, left); + +	left_shift_vector(left, k1, sizeof(left)); +	debug_print_vector("L", AES_KEY_LENGTH, left); + +	if ((left[0] >> 7) != 0) /* get MSB of L */ +		k1[AES_KEY_LENGTH-1] ^= AES_CMAC_CONST_RB; +	debug_print_vector("K1", AES_KEY_LENGTH, k1); + +	/* compute the AES-CMAC value */ +	for (i = 0; i < num_aes_blocks; i++) { +		/* Apply the chain data */ +		apply_cbc_chain_data(cbc_chain_data, src, tmp_data); + +		/* for the final block, XOR K1 into the IV */ +		if (i == num_aes_blocks - 1) +			apply_cbc_chain_data(tmp_data, k1, tmp_data); + +		/* encrypt the AES block */ +		aes_encrypt(tmp_data, key_schedule, dst); + +		debug("sign_obj: block %d of %d\n", i, num_aes_blocks); +		debug_print_vector("AES-CMAC Src", AES_KEY_LENGTH, src); +		debug_print_vector("AES-CMAC Xor", AES_KEY_LENGTH, tmp_data); +		debug_print_vector("AES-CMAC Dst", AES_KEY_LENGTH, dst); + +		/* Update pointers for next loop. */ +		cbc_chain_data = dst; +		src += AES_KEY_LENGTH; +	} + +	debug_print_vector("AES-CMAC Hash", AES_KEY_LENGTH, dst); +} + +/** + * Encrypt and sign a block of data (depending on security mode). + * + * \param key		Input AES key, length AES_KEY_LENGTH + * \param oper		Security operations mask to perform (enum security_op) + * \param src		Source data + * \param length	Size of source data + * \param sig_dst	Destination address for signature, AES_KEY_LENGTH bytes + */ +static int encrypt_and_sign(u8 *key, enum security_op oper, u8 *src, +			    u32 length, u8 *sig_dst) +{ +	u32 num_aes_blocks; +	u8 key_schedule[AES_EXPAND_KEY_LENGTH]; + +	debug("encrypt_and_sign: length = %d\n", length); +	debug_print_vector("AES key", AES_KEY_LENGTH, key); + +	/* +	 * The only need for a key is for signing/checksum purposes, so +	 * if not encrypting, expand a key of 0s. +	 */ +	aes_expand_key(oper & SECURITY_ENCRYPT ? key : zero_key, key_schedule); + +	num_aes_blocks = (length + AES_KEY_LENGTH - 1) / AES_KEY_LENGTH; + +	if (oper & SECURITY_ENCRYPT) { +		/* Perform this in place, resulting in src being encrypted. */ +		debug("encrypt_and_sign: begin encryption\n"); +		encrypt_object(key_schedule, src, src, num_aes_blocks); +		debug("encrypt_and_sign: end encryption\n"); +	} + +	if (oper & SECURITY_SIGN) { +		/* encrypt the data, overwriting the result in signature. */ +		debug("encrypt_and_sign: begin signing\n"); +		sign_object(key, key_schedule, src, sig_dst, num_aes_blocks); +		debug("encrypt_and_sign: end signing\n"); +	} + +	return 0; +} + +int sign_data_block(u8 *source, unsigned length, u8 *signature) +{ +	return encrypt_and_sign(zero_key, SECURITY_SIGN, source, +				length, signature); +} diff --git a/arch/arm/cpu/tegra20-common/crypto.h b/arch/arm/cpu/tegra20-common/crypto.h new file mode 100644 index 000000000..aff67e77b --- /dev/null +++ b/arch/arm/cpu/tegra20-common/crypto.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010 - 2011 NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _CRYPTO_H_ +#define _CRYPTO_H_ + +/** + * Sign a block of data + * + * \param source	Source data + * \param length	Size of source data + * \param signature	Destination address for signature, AES_KEY_LENGTH bytes + */ +int sign_data_block(u8 *source, unsigned length, u8 *signature); + +#endif /* #ifndef _CRYPTO_H_ */ diff --git a/arch/arm/cpu/tegra20-common/emc.c b/arch/arm/cpu/tegra20-common/emc.c new file mode 100644 index 000000000..ffc05e453 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/emc.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * 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 <common.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/arch/ap20.h> +#include <asm/arch/apb_misc.h> +#include <asm/arch/clock.h> +#include <asm/arch/emc.h> +#include <asm/arch/tegra20.h> + +/* + * The EMC registers have shadow registers.  When the EMC clock is updated + * in the clock controller, the shadow registers are copied to the active + * registers, allowing glitchless memory bus frequency changes. + * This function updates the shadow registers for a new clock frequency, + * and relies on the clock lock on the emc clock to avoid races between + * multiple frequency changes + */ + +/* + * This table defines the ordering of the registers provided to + * tegra_set_mmc() + * TODO: Convert to fdt version once available + */ +static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = { +	0x2c,	/* RC */ +	0x30,	/* RFC */ +	0x34,	/* RAS */ +	0x38,	/* RP */ +	0x3c,	/* R2W */ +	0x40,	/* W2R */ +	0x44,	/* R2P */ +	0x48,	/* W2P */ +	0x4c,	/* RD_RCD */ +	0x50,	/* WR_RCD */ +	0x54,	/* RRD */ +	0x58,	/* REXT */ +	0x5c,	/* WDV */ +	0x60,	/* QUSE */ +	0x64,	/* QRST */ +	0x68,	/* QSAFE */ +	0x6c,	/* RDV */ +	0x70,	/* REFRESH */ +	0x74,	/* BURST_REFRESH_NUM */ +	0x78,	/* PDEX2WR */ +	0x7c,	/* PDEX2RD */ +	0x80,	/* PCHG2PDEN */ +	0x84,	/* ACT2PDEN */ +	0x88,	/* AR2PDEN */ +	0x8c,	/* RW2PDEN */ +	0x90,	/* TXSR */ +	0x94,	/* TCKE */ +	0x98,	/* TFAW */ +	0x9c,	/* TRPAB */ +	0xa0,	/* TCLKSTABLE */ +	0xa4,	/* TCLKSTOP */ +	0xa8,	/* TREFBW */ +	0xac,	/* QUSE_EXTRA */ +	0x114,	/* FBIO_CFG6 */ +	0xb0,	/* ODT_WRITE */ +	0xb4,	/* ODT_READ */ +	0x104,	/* FBIO_CFG5 */ +	0x2bc,	/* CFG_DIG_DLL */ +	0x2c0,	/* DLL_XFORM_DQS */ +	0x2c4,	/* DLL_XFORM_QUSE */ +	0x2e0,	/* ZCAL_REF_CNT */ +	0x2e4,	/* ZCAL_WAIT_CNT */ +	0x2a8,	/* AUTO_CAL_INTERVAL */ +	0x2d0,	/* CFG_CLKTRIM_0 */ +	0x2d4,	/* CFG_CLKTRIM_1 */ +	0x2d8,	/* CFG_CLKTRIM_2 */ +}; + +struct emc_ctlr *emc_get_controller(const void *blob) +{ +	fdt_addr_t addr; +	int node; + +	node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_EMC); +	if (node > 0) { +		addr = fdtdec_get_addr(blob, node, "reg"); +		if (addr != FDT_ADDR_T_NONE) +			return (struct emc_ctlr *)addr; +	} +	return NULL; +} + +/* Error codes we use */ +enum { +	ERR_NO_EMC_NODE = -10, +	ERR_NO_EMC_REG, +	ERR_NO_FREQ, +	ERR_FREQ_NOT_FOUND, +	ERR_BAD_REGS, +	ERR_NO_RAM_CODE, +	ERR_RAM_CODE_NOT_FOUND, +}; + +/** + * Find EMC tables for the given ram code. + * + * The tegra EMC binding has two options, one using the ram code and one not. + * We detect which is in use by looking for the nvidia,use-ram-code property. + * If this is not present, then the EMC tables are directly below 'node', + * otherwise we select the correct emc-tables subnode based on the 'ram_code' + * value. + * + * @param blob		Device tree blob + * @param node		EMC node (nvidia,tegra20-emc compatible string) + * @param ram_code	RAM code to select (0-3, or -1 if unknown) + * @return 0 if ok, otherwise a -ve ERR_ code (see enum above) + */ +static int find_emc_tables(const void *blob, int node, int ram_code) +{ +	int need_ram_code; +	int depth; +	int offset; + +	/* If we are using RAM codes, scan through the tables for our code */ +	need_ram_code = fdtdec_get_bool(blob, node, "nvidia,use-ram-code"); +	if (!need_ram_code) +		return node; +	if (ram_code == -1) { +		debug("%s: RAM code required but not supplied\n", __func__); +		return ERR_NO_RAM_CODE; +	} + +	offset = node; +	depth = 0; +	do { +		/* +		 * Sadly there is no compatible string so we cannot use +		 * fdtdec_next_compatible_subnode(). +		 */ +		offset = fdt_next_node(blob, offset, &depth); +		if (depth <= 0) +			break; + +		/* Make sure this is a direct subnode */ +		if (depth != 1) +			continue; +		if (strcmp("emc-tables", fdt_get_name(blob, offset, NULL))) +			continue; + +		if (fdtdec_get_int(blob, offset, "nvidia,ram-code", -1) +				== ram_code) +			return offset; +	} while (1); + +	debug("%s: Could not find tables for RAM code %d\n", __func__, +	      ram_code); +	return ERR_RAM_CODE_NOT_FOUND; +} + +/** + * Decode the EMC node of the device tree, returning a pointer to the emc + * controller and the table to be used for the given rate. + * + * @param blob	Device tree blob + * @param rate	Clock speed of memory controller in Hz (=2x memory bus rate) + * @param emcp	Returns address of EMC controller registers + * @param tablep Returns pointer to table to program into EMC. There are + *		TEGRA_EMC_NUM_REGS entries, destined for offsets as per the + *		emc_reg_addr array. + * @return 0 if ok, otherwise a -ve error code which will allow someone to + * figure out roughly what went wrong by looking at this code. + */ +static int decode_emc(const void *blob, unsigned rate, struct emc_ctlr **emcp, +		      const u32 **tablep) +{ +	struct apb_misc_pp_ctlr *pp = +		(struct apb_misc_pp_ctlr *)NV_PA_APB_MISC_BASE; +	int ram_code; +	int depth; +	int node; + +	ram_code = (readl(&pp->strapping_opt_a) & RAM_CODE_MASK) +			>> RAM_CODE_SHIFT; +	/* +	 * The EMC clock rate is twice the bus rate, and the bus rate is +	 * measured in kHz +	 */ +	rate = rate / 2 / 1000; + +	node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_EMC); +	if (node < 0) { +		debug("%s: No EMC node found in FDT\n", __func__); +		return ERR_NO_EMC_NODE; +	} +	*emcp = (struct emc_ctlr *)fdtdec_get_addr(blob, node, "reg"); +	if (*emcp == (struct emc_ctlr *)FDT_ADDR_T_NONE) { +		debug("%s: No EMC node reg property\n", __func__); +		return ERR_NO_EMC_REG; +	} + +	/* Work out the parent node which contains our EMC tables */ +	node = find_emc_tables(blob, node, ram_code & 3); +	if (node < 0) +		return node; + +	depth = 0; +	for (;;) { +		int node_rate; + +		node = fdtdec_next_compatible_subnode(blob, node, +				COMPAT_NVIDIA_TEGRA20_EMC_TABLE, &depth); +		if (node < 0) +			break; +		node_rate = fdtdec_get_int(blob, node, "clock-frequency", -1); +		if (node_rate == -1) { +			debug("%s: Missing clock-frequency\n", __func__); +			return ERR_NO_FREQ; /* we expect this property */ +		} + +		if (node_rate == rate) +			break; +	} +	if (node < 0) { +		debug("%s: No node found for clock frequency %d\n", __func__, +		      rate); +		return ERR_FREQ_NOT_FOUND; +	} + +	*tablep = fdtdec_locate_array(blob, node, "nvidia,emc-registers", +				      TEGRA_EMC_NUM_REGS); +	if (!*tablep) { +		debug("%s: node '%s' array missing / wrong size\n", __func__, +		      fdt_get_name(blob, node, NULL)); +		return ERR_BAD_REGS; +	} + +	/* All seems well */ +	return 0; +} + +int tegra_set_emc(const void *blob, unsigned rate) +{ +	struct emc_ctlr *emc; +	const u32 *table; +	int err, i; + +	err = decode_emc(blob, rate, &emc, &table); +	if (err) { +		debug("Warning: no valid EMC (%d), memory timings unset\n", +		       err); +		return err; +	} + +	debug("%s: Table found, setting EMC values as follows:\n", __func__); +	for (i = 0; i < TEGRA_EMC_NUM_REGS; i++) { +		u32 value = fdt32_to_cpu(table[i]); +		u32 addr = (uintptr_t)emc + emc_reg_addr[i]; + +		debug("   %#x: %#x\n", addr, value); +		writel(value, addr); +	} + +	/* trigger emc with new settings */ +	clock_adjust_periph_pll_div(PERIPH_ID_EMC, CLOCK_ID_MEMORY, +				clock_get_rate(CLOCK_ID_MEMORY), NULL); +	debug("EMC clock set to %lu\n", +	      clock_get_periph_rate(PERIPH_ID_EMC, CLOCK_ID_MEMORY)); + +	return 0; +} diff --git a/arch/arm/cpu/tegra20-common/funcmux.c b/arch/arm/cpu/tegra20-common/funcmux.c new file mode 100644 index 000000000..b2129adf2 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/funcmux.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * 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 + */ + +/* Tegra20 high-level function multiplexing */ +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch/pinmux.h> + +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_IRRX_IRTX: +			pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA); +			pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA); +			pinmux_tristate_disable(PINGRP_IRRX); +			pinmux_tristate_disable(PINGRP_IRTX); +			break; +		case FUNCMUX_UART1_UAA_UAB: +			pinmux_set_func(PINGRP_UAA, PMUX_FUNC_UARTA); +			pinmux_set_func(PINGRP_UAB, PMUX_FUNC_UARTA); +			pinmux_tristate_disable(PINGRP_UAA); +			pinmux_tristate_disable(PINGRP_UAB); +			bad_config = 0; +			break; +		case FUNCMUX_UART1_GPU: +			pinmux_set_func(PINGRP_GPU, PMUX_FUNC_UARTA); +			pinmux_tristate_disable(PINGRP_GPU); +			bad_config = 0; +			break; +		case FUNCMUX_UART1_SDIO1: +			pinmux_set_func(PINGRP_SDIO1, PMUX_FUNC_UARTA); +			pinmux_tristate_disable(PINGRP_SDIO1); +			bad_config = 0; +			break; +		} +		if (!bad_config) { +			/* +			 * Tegra appears to boot with function UARTA pre- +			 * selected on mux group SDB. If two mux groups are +			 * both set to the same function, it's unclear which +			 * group's pins drive the RX signals into the HW. +			 * For UARTA, SDB certainly overrides group IRTX in +			 * practice. To solve this, configure some alternative +			 * function on SDB to avoid the conflict. Also, tri- +			 * state the group to avoid driving any signal onto it +			 * until we know what's connected. +			 */ +			pinmux_tristate_enable(PINGRP_SDB); +			pinmux_set_func(PINGRP_SDB,  PMUX_FUNC_SDIO3); +		} +		break; + +	case PERIPH_ID_UART2: +		if (config == FUNCMUX_UART2_IRDA) { +			pinmux_set_func(PINGRP_UAD, PMUX_FUNC_IRDA); +			pinmux_tristate_disable(PINGRP_UAD); +		} +		break; + +	case PERIPH_ID_UART4: +		if (config == FUNCMUX_UART4_GMC) { +			pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD); +			pinmux_tristate_disable(PINGRP_GMC); +		} +		break; + +	case PERIPH_ID_DVC_I2C: +		/* there is only one selection, pinmux_config is ignored */ +		if (config == FUNCMUX_DVC_I2CP) { +			pinmux_set_func(PINGRP_I2CP, PMUX_FUNC_I2C); +			pinmux_tristate_disable(PINGRP_I2CP); +		} +		break; + +	case PERIPH_ID_I2C1: +		/* support pinmux_config of 0 for now, */ +		if (config == FUNCMUX_I2C1_RM) { +			pinmux_set_func(PINGRP_RM, PMUX_FUNC_I2C); +			pinmux_tristate_disable(PINGRP_RM); +		} +		break; +	case PERIPH_ID_I2C2: /* I2C2 */ +		switch (config) { +		case FUNCMUX_I2C2_DDC:	/* DDC pin group, select I2C2 */ +			pinmux_set_func(PINGRP_DDC, PMUX_FUNC_I2C2); +			/* PTA to HDMI */ +			pinmux_set_func(PINGRP_PTA, PMUX_FUNC_HDMI); +			pinmux_tristate_disable(PINGRP_DDC); +			break; +		case FUNCMUX_I2C2_PTA:	/* PTA pin group, select I2C2 */ +			pinmux_set_func(PINGRP_PTA, PMUX_FUNC_I2C2); +			/* set DDC_SEL to RSVDx (RSVD2 works for now) */ +			pinmux_set_func(PINGRP_DDC, PMUX_FUNC_RSVD2); +			pinmux_tristate_disable(PINGRP_PTA); +			bad_config = 0; +			break; +		} +		break; +	case PERIPH_ID_I2C3: /* I2C3 */ +		/* support pinmux_config of 0 for now */ +		if (config == FUNCMUX_I2C3_DTF) { +			pinmux_set_func(PINGRP_DTF, PMUX_FUNC_I2C3); +			pinmux_tristate_disable(PINGRP_DTF); +		} +		break; + +	case PERIPH_ID_SDMMC1: +		if (config == FUNCMUX_SDMMC1_SDIO1_4BIT) { +			pinmux_set_func(PINGRP_SDIO1, PMUX_FUNC_SDIO1); +			pinmux_tristate_disable(PINGRP_SDIO1); +		} +		break; + +	case PERIPH_ID_SDMMC2: +		if (config == FUNCMUX_SDMMC2_DTA_DTD_8BIT) { +			pinmux_set_func(PINGRP_DTA, PMUX_FUNC_SDIO2); +			pinmux_set_func(PINGRP_DTD, PMUX_FUNC_SDIO2); + +			pinmux_tristate_disable(PINGRP_DTA); +			pinmux_tristate_disable(PINGRP_DTD); +		} +		break; + +	case PERIPH_ID_SDMMC3: +		switch (config) { +		case FUNCMUX_SDMMC3_SDB_SLXA_8BIT: +			pinmux_set_func(PINGRP_SLXA, PMUX_FUNC_SDIO3); +			pinmux_set_func(PINGRP_SLXC, PMUX_FUNC_SDIO3); +			pinmux_set_func(PINGRP_SLXD, PMUX_FUNC_SDIO3); +			pinmux_set_func(PINGRP_SLXK, PMUX_FUNC_SDIO3); + +			pinmux_tristate_disable(PINGRP_SLXA); +			pinmux_tristate_disable(PINGRP_SLXC); +			pinmux_tristate_disable(PINGRP_SLXD); +			pinmux_tristate_disable(PINGRP_SLXK); +			/* fall through */ + +		case FUNCMUX_SDMMC3_SDB_4BIT: +			pinmux_set_func(PINGRP_SDB, PMUX_FUNC_SDIO3); +			pinmux_set_func(PINGRP_SDC, PMUX_FUNC_SDIO3); +			pinmux_set_func(PINGRP_SDD, PMUX_FUNC_SDIO3); + +			pinmux_tristate_disable(PINGRP_SDB); +			pinmux_tristate_disable(PINGRP_SDC); +			pinmux_tristate_disable(PINGRP_SDD); +			bad_config = 0; +			break; +		} +		break; + +	case PERIPH_ID_SDMMC4: +		switch (config) { +		case FUNCMUX_SDMMC4_ATC_ATD_8BIT: +			pinmux_set_func(PINGRP_ATC, PMUX_FUNC_SDIO4); +			pinmux_set_func(PINGRP_ATD, PMUX_FUNC_SDIO4); + +			pinmux_tristate_disable(PINGRP_ATC); +			pinmux_tristate_disable(PINGRP_ATD); +			break; + +		case FUNCMUX_SDMMC4_ATB_GMA_GME_8_BIT: +			pinmux_set_func(PINGRP_GME, PMUX_FUNC_SDIO4); +			pinmux_tristate_disable(PINGRP_GME); +			/* fall through */ + +		case FUNCMUX_SDMMC4_ATB_GMA_4_BIT: +			pinmux_set_func(PINGRP_ATB, PMUX_FUNC_SDIO4); +			pinmux_set_func(PINGRP_GMA, PMUX_FUNC_SDIO4); + +			pinmux_tristate_disable(PINGRP_ATB); +			pinmux_tristate_disable(PINGRP_GMA); +			bad_config = 0; +			break; +		} +		break; + +	case PERIPH_ID_KBC: +		if (config == FUNCMUX_DEFAULT) { +			enum pmux_pingrp grp[] = {PINGRP_KBCA, PINGRP_KBCB, +				PINGRP_KBCC, PINGRP_KBCD, PINGRP_KBCE, +				PINGRP_KBCF}; +			int i; + +			for (i = 0; i < ARRAY_SIZE(grp); i++) { +				pinmux_tristate_disable(grp[i]); +				pinmux_set_func(grp[i], PMUX_FUNC_KBC); +				pinmux_set_pullupdown(grp[i], PMUX_PULL_UP); +			} +		} +		break; + +	case PERIPH_ID_USB2: +		if (config == FUNCMUX_USB2_ULPI) { +			pinmux_set_func(PINGRP_UAA, PMUX_FUNC_ULPI); +			pinmux_set_func(PINGRP_UAB, PMUX_FUNC_ULPI); +			pinmux_set_func(PINGRP_UDA, PMUX_FUNC_ULPI); + +			pinmux_tristate_disable(PINGRP_UAA); +			pinmux_tristate_disable(PINGRP_UAB); +			pinmux_tristate_disable(PINGRP_UDA); +		} +		break; + +	case PERIPH_ID_SPI1: +		if (config == FUNCMUX_SPI1_GMC_GMD) { +			pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); +			pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); + +			pinmux_tristate_disable(PINGRP_GMC); +			pinmux_tristate_disable(PINGRP_GMD); +		} +		break; + +	case PERIPH_ID_NDFLASH: +		if (config == FUNCMUX_NDFLASH_ATC) { +			pinmux_set_func(PINGRP_ATC, PMUX_FUNC_NAND); +			pinmux_tristate_disable(PINGRP_ATC); +		} +		break; + +	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/tegra20-common/lowlevel_init.S b/arch/arm/cpu/tegra20-common/lowlevel_init.S new file mode 100644 index 000000000..d117f23a6 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/lowlevel_init.S @@ -0,0 +1,42 @@ +/* + * SoC-specific setup info + * + * (C) Copyright 2010,2011 + * NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <config.h> +#include <version.h> +#include <linux/linkage.h> + +	.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/tegra20-common/pinmux.c b/arch/arm/cpu/tegra20-common/pinmux.c new file mode 100644 index 000000000..70e84dfa1 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/pinmux.c @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * 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 + */ + +/* Tegra20 pin multiplexing functions */ + +#include <asm/io.h> +#include <asm/arch/tegra20.h> +#include <asm/arch/pinmux.h> +#include <common.h> + + +/* + * This defines the order of the pin mux control bits in the registers. For + * some reason there is no correspendence between the tristate, pin mux and + * pullup/pulldown registers. + */ +enum pmux_ctlid { +	/* 0: APB_MISC_PP_PIN_MUX_CTL_A_0 */ +	MUXCTL_UAA, +	MUXCTL_UAB, +	MUXCTL_UAC, +	MUXCTL_UAD, +	MUXCTL_UDA, +	MUXCTL_RESERVED5, +	MUXCTL_ATE, +	MUXCTL_RM, + +	MUXCTL_ATB, +	MUXCTL_RESERVED9, +	MUXCTL_ATD, +	MUXCTL_ATC, +	MUXCTL_ATA, +	MUXCTL_KBCF, +	MUXCTL_KBCE, +	MUXCTL_SDMMC1, + +	/* 16: APB_MISC_PP_PIN_MUX_CTL_B_0 */ +	MUXCTL_GMA, +	MUXCTL_GMC, +	MUXCTL_HDINT, +	MUXCTL_SLXA, +	MUXCTL_OWC, +	MUXCTL_SLXC, +	MUXCTL_SLXD, +	MUXCTL_SLXK, + +	MUXCTL_UCA, +	MUXCTL_UCB, +	MUXCTL_DTA, +	MUXCTL_DTB, +	MUXCTL_RESERVED28, +	MUXCTL_DTC, +	MUXCTL_DTD, +	MUXCTL_DTE, + +	/* 32: APB_MISC_PP_PIN_MUX_CTL_C_0 */ +	MUXCTL_DDC, +	MUXCTL_CDEV1, +	MUXCTL_CDEV2, +	MUXCTL_CSUS, +	MUXCTL_I2CP, +	MUXCTL_KBCA, +	MUXCTL_KBCB, +	MUXCTL_KBCC, + +	MUXCTL_IRTX, +	MUXCTL_IRRX, +	MUXCTL_DAP1, +	MUXCTL_DAP2, +	MUXCTL_DAP3, +	MUXCTL_DAP4, +	MUXCTL_GMB, +	MUXCTL_GMD, + +	/* 48: APB_MISC_PP_PIN_MUX_CTL_D_0 */ +	MUXCTL_GME, +	MUXCTL_GPV, +	MUXCTL_GPU, +	MUXCTL_SPDO, +	MUXCTL_SPDI, +	MUXCTL_SDB, +	MUXCTL_SDC, +	MUXCTL_SDD, + +	MUXCTL_SPIH, +	MUXCTL_SPIG, +	MUXCTL_SPIF, +	MUXCTL_SPIE, +	MUXCTL_SPID, +	MUXCTL_SPIC, +	MUXCTL_SPIB, +	MUXCTL_SPIA, + +	/* 64: APB_MISC_PP_PIN_MUX_CTL_E_0 */ +	MUXCTL_LPW0, +	MUXCTL_LPW1, +	MUXCTL_LPW2, +	MUXCTL_LSDI, +	MUXCTL_LSDA, +	MUXCTL_LSPI, +	MUXCTL_LCSN, +	MUXCTL_LDC, + +	MUXCTL_LSCK, +	MUXCTL_LSC0, +	MUXCTL_LSC1, +	MUXCTL_LHS, +	MUXCTL_LVS, +	MUXCTL_LM0, +	MUXCTL_LM1, +	MUXCTL_LVP0, + +	/* 80: APB_MISC_PP_PIN_MUX_CTL_F_0 */ +	MUXCTL_LD0, +	MUXCTL_LD1, +	MUXCTL_LD2, +	MUXCTL_LD3, +	MUXCTL_LD4, +	MUXCTL_LD5, +	MUXCTL_LD6, +	MUXCTL_LD7, + +	MUXCTL_LD8, +	MUXCTL_LD9, +	MUXCTL_LD10, +	MUXCTL_LD11, +	MUXCTL_LD12, +	MUXCTL_LD13, +	MUXCTL_LD14, +	MUXCTL_LD15, + +	/* 96: APB_MISC_PP_PIN_MUX_CTL_G_0 */ +	MUXCTL_LD16, +	MUXCTL_LD17, +	MUXCTL_LHP1, +	MUXCTL_LHP2, +	MUXCTL_LVP1, +	MUXCTL_LHP0, +	MUXCTL_RESERVED102, +	MUXCTL_LPP, + +	MUXCTL_LDI, +	MUXCTL_PMC, +	MUXCTL_CRTP, +	MUXCTL_PTA, +	MUXCTL_RESERVED108, +	MUXCTL_KBCD, +	MUXCTL_GPU7, +	MUXCTL_DTF, + +	MUXCTL_NONE = -1, +}; + +/* + * And this defines the order of the pullup/pulldown controls which are again + * in a different order + */ +enum pmux_pullid { +	/* 0: APB_MISC_PP_PULLUPDOWN_REG_A_0 */ +	PUCTL_ATA, +	PUCTL_ATB, +	PUCTL_ATC, +	PUCTL_ATD, +	PUCTL_ATE, +	PUCTL_DAP1, +	PUCTL_DAP2, +	PUCTL_DAP3, + +	PUCTL_DAP4, +	PUCTL_DTA, +	PUCTL_DTB, +	PUCTL_DTC, +	PUCTL_DTD, +	PUCTL_DTE, +	PUCTL_DTF, +	PUCTL_GPV, + +	/* 16: APB_MISC_PP_PULLUPDOWN_REG_B_0 */ +	PUCTL_RM, +	PUCTL_I2CP, +	PUCTL_PTA, +	PUCTL_GPU7, +	PUCTL_KBCA, +	PUCTL_KBCB, +	PUCTL_KBCC, +	PUCTL_KBCD, + +	PUCTL_SPDI, +	PUCTL_SPDO, +	PUCTL_GPSLXAU, +	PUCTL_CRTP, +	PUCTL_SLXC, +	PUCTL_SLXD, +	PUCTL_SLXK, + +	/* 32: APB_MISC_PP_PULLUPDOWN_REG_C_0 */ +	PUCTL_CDEV1, +	PUCTL_CDEV2, +	PUCTL_SPIA, +	PUCTL_SPIB, +	PUCTL_SPIC, +	PUCTL_SPID, +	PUCTL_SPIE, +	PUCTL_SPIF, + +	PUCTL_SPIG, +	PUCTL_SPIH, +	PUCTL_IRTX, +	PUCTL_IRRX, +	PUCTL_GME, +	PUCTL_RESERVED45, +	PUCTL_XM2D, +	PUCTL_XM2C, + +	/* 48: APB_MISC_PP_PULLUPDOWN_REG_D_0 */ +	PUCTL_UAA, +	PUCTL_UAB, +	PUCTL_UAC, +	PUCTL_UAD, +	PUCTL_UCA, +	PUCTL_UCB, +	PUCTL_LD17, +	PUCTL_LD19_18, + +	PUCTL_LD21_20, +	PUCTL_LD23_22, +	PUCTL_LS, +	PUCTL_LC, +	PUCTL_CSUS, +	PUCTL_DDRC, +	PUCTL_SDC, +	PUCTL_SDD, + +	/* 64: APB_MISC_PP_PULLUPDOWN_REG_E_0 */ +	PUCTL_KBCF, +	PUCTL_KBCE, +	PUCTL_PMCA, +	PUCTL_PMCB, +	PUCTL_PMCC, +	PUCTL_PMCD, +	PUCTL_PMCE, +	PUCTL_CK32, + +	PUCTL_UDA, +	PUCTL_SDMMC1, +	PUCTL_GMA, +	PUCTL_GMB, +	PUCTL_GMC, +	PUCTL_GMD, +	PUCTL_DDC, +	PUCTL_OWC, + +	PUCTL_NONE = -1 +}; + +struct tegra_pingroup_desc { +	const char *name; +	enum pmux_func funcs[4]; +	enum pmux_func func_safe; +	enum pmux_vddio vddio; +	enum pmux_ctlid ctl_id; +	enum pmux_pullid pull_id; +}; + + +/* Converts a pmux_pingrp number to a tristate register: 0=A, 1=B, 2=C, 3=D */ +#define TRISTATE_REG(pmux_pingrp) ((pmux_pingrp) >> 5) + +/* Mask value for a tristate (within TRISTATE_REG(id)) */ +#define TRISTATE_MASK(pmux_pingrp) (1 << ((pmux_pingrp) & 0x1f)) + +/* Converts a PUCTL id to a pull register: 0=A, 1=B...4=E */ +#define PULL_REG(pmux_pullid) ((pmux_pullid) >> 4) + +/* Converts a PUCTL id to a shift position */ +#define PULL_SHIFT(pmux_pullid) ((pmux_pullid << 1) & 0x1f) + +/* Converts a MUXCTL id to a ctl register: 0=A, 1=B...6=G */ +#define MUXCTL_REG(pmux_ctlid) ((pmux_ctlid) >> 4) + +/* Converts a MUXCTL id to a shift position */ +#define MUXCTL_SHIFT(pmux_ctlid) ((pmux_ctlid << 1) & 0x1f) + +/* Convenient macro for defining pin group properties */ +#define PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe, mux, pupd)		\ +	{						\ +		.vddio = PMUX_VDDIO_ ## vdd,		\ +		.funcs = {				\ +			PMUX_FUNC_ ## f0,			\ +			PMUX_FUNC_ ## f1,			\ +			PMUX_FUNC_ ## f2,			\ +			PMUX_FUNC_ ## f3,			\ +		},					\ +		.func_safe = PMUX_FUNC_ ## f_safe,		\ +		.ctl_id = mux,				\ +		.pull_id = pupd				\ +	} + +/* A normal pin group where the mux name and pull-up name match */ +#define PIN(pg_name, vdd, f0, f1, f2, f3, f_safe)		\ +		PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe,	\ +			MUXCTL_ ## pg_name, PUCTL_ ## pg_name) + +/* A pin group where the pull-up name doesn't have a 1-1 mapping */ +#define PINP(pg_name, vdd, f0, f1, f2, f3, f_safe, pupd)		\ +		PINALL(pg_name, vdd, f0, f1, f2, f3, f_safe,	\ +			MUXCTL_ ## pg_name, PUCTL_ ## pupd) + +/* A pin group number which is not used */ +#define PIN_RESERVED \ +	PIN(NONE, NONE, NONE, NONE, NONE, NONE, NONE) + +const struct tegra_pingroup_desc tegra_soc_pingroups[PINGRP_COUNT] = { +	PIN(ATA,  NAND,  IDE,    NAND,   GMI,       RSVD,        IDE), +	PIN(ATB,  NAND,  IDE,    NAND,   GMI,       SDIO4,       IDE), +	PIN(ATC,  NAND,  IDE,    NAND,   GMI,       SDIO4,       IDE), +	PIN(ATD,  NAND,  IDE,    NAND,   GMI,       SDIO4,       IDE), +	PIN(CDEV1, AUDIO, OSC,   PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC), +	PIN(CDEV2, AUDIO, OSC,   AHB_CLK, APB_CLK, PLLP_OUT4,    OSC), +	PIN(CSUS, VI, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, +		PLLC_OUT1), +	PIN(DAP1, AUDIO, DAP1,   RSVD,   GMI,       SDIO2,       DAP1), + +	PIN(DAP2, AUDIO, DAP2,   TWC,    RSVD,      GMI,         DAP2), +	PIN(DAP3, BB,    DAP3,   RSVD,   RSVD,      RSVD,        DAP3), +	PIN(DAP4, UART,  DAP4,   RSVD,   GMI,       RSVD,        DAP4), +	PIN(DTA,  VI,    RSVD,   SDIO2,  VI,        RSVD,        RSVD4), +	PIN(DTB,  VI,    RSVD,   RSVD,   VI,        SPI1,        RSVD1), +	PIN(DTC,  VI,    RSVD,   RSVD,   VI,        RSVD,        RSVD1), +	PIN(DTD,  VI,    RSVD,   SDIO2,  VI,        RSVD,        RSVD1), +	PIN(DTE,  VI,    RSVD,   RSVD,   VI,        SPI1,        RSVD1), + +	PINP(GPU, UART,  PWM,    UARTA,  GMI,       RSVD,        RSVD4, +		GPSLXAU), +	PIN(GPV,  SD,    PCIE,   RSVD,   RSVD,      RSVD,        PCIE), +	PIN(I2CP, SYS,   I2C,    RSVD,   RSVD,      RSVD,        RSVD4), +	PIN(IRTX, UART,  UARTA,  UARTB,  GMI,       SPI4,        UARTB), +	PIN(IRRX, UART,  UARTA,  UARTB,  GMI,       SPI4,        UARTB), +	PIN(KBCB, SYS,   KBC,    NAND,   SDIO2,     MIO,         KBC), +	PIN(KBCA, SYS,   KBC,    NAND,   SDIO2,     EMC_TEST0_DLL, KBC), +	PINP(PMC, SYS,   PWR_ON, PWR_INTR, RSVD,    RSVD,        PWR_ON, NONE), + +	PIN(PTA,  NAND,  I2C2,   HDMI,   GMI,       RSVD,        RSVD4), +	PIN(RM,   UART,  I2C,    RSVD,   RSVD,      RSVD,        RSVD4), +	PIN(KBCE, SYS,   KBC,    NAND,   OWR,       RSVD,        KBC), +	PIN(KBCF, SYS,   KBC,    NAND,   TRACE,     MIO,         KBC), +	PIN(GMA,  NAND,  UARTE,  SPI3,   GMI,       SDIO4,       SPI3), +	PIN(GMC,  NAND,  UARTD,  SPI4,   GMI,       SFLASH,      SPI4), +	PIN(SDMMC1, BB,  SDIO1,  RSVD,   UARTE,     UARTA,       RSVD2), +	PIN(OWC,  SYS,   OWR,    RSVD,   RSVD,      RSVD,        OWR), + +	PIN(GME,  NAND,  RSVD,   DAP5,   GMI,       SDIO4,       GMI), +	PIN(SDC,  SD,    PWM,    TWC,    SDIO3,     SPI3,        TWC), +	PIN(SDD,  SD,    UARTA,  PWM,    SDIO3,     SPI3,        PWM), +	PIN_RESERVED, +	PINP(SLXA, SD,   PCIE,   SPI4,   SDIO3,     SPI2,        PCIE, CRTP), +	PIN(SLXC, SD,    SPDIF,  SPI4,   SDIO3,     SPI2,        SPI4), +	PIN(SLXD, SD,    SPDIF,  SPI4,   SDIO3,     SPI2,        SPI4), +	PIN(SLXK, SD,    PCIE,   SPI4,   SDIO3,     SPI2,        PCIE), + +	PIN(SPDI, AUDIO, SPDIF,  RSVD,   I2C,       SDIO2,       RSVD2), +	PIN(SPDO, AUDIO, SPDIF,  RSVD,   I2C,       SDIO2,       RSVD2), +	PIN(SPIA, AUDIO, SPI1,   SPI2,   SPI3,      GMI,         GMI), +	PIN(SPIB, AUDIO, SPI1,   SPI2,   SPI3,      GMI,         GMI), +	PIN(SPIC, AUDIO, SPI1,   SPI2,   SPI3,      GMI,         GMI), +	PIN(SPID, AUDIO, SPI2,   SPI1,   SPI2_ALT,  GMI,         GMI), +	PIN(SPIE, AUDIO, SPI2,   SPI1,   SPI2_ALT,  GMI,         GMI), +	PIN(SPIF, AUDIO, SPI3,   SPI1,   SPI2,      RSVD,        RSVD4), + +	PIN(SPIG, AUDIO, SPI3,   SPI2,   SPI2_ALT,  I2C,         SPI2_ALT), +	PIN(SPIH, AUDIO, SPI3,   SPI2,   SPI2_ALT,  I2C,         SPI2_ALT), +	PIN(UAA,  BB,    SPI3,   MIPI_HS, UARTA,    ULPI,        MIPI_HS), +	PIN(UAB,  BB,    SPI2,   MIPI_HS, UARTA,    ULPI,        MIPI_HS), +	PIN(UAC,  BB,    OWR,    RSVD,   RSVD,      RSVD,        RSVD4), +	PIN(UAD,  UART,  IRDA,   SPDIF,  UARTA,     SPI4,        SPDIF), +	PIN(UCA,  UART,  UARTC,  RSVD,   GMI,       RSVD,        RSVD4), +	PIN(UCB,  UART,  UARTC,  PWM,    GMI,       RSVD,        RSVD4), + +	PIN_RESERVED, +	PIN(ATE,  NAND,  IDE,    NAND,   GMI,       RSVD,        IDE), +	PIN(KBCC, SYS,   KBC,    NAND,   TRACE,     EMC_TEST1_DLL, KBC), +	PIN_RESERVED, +	PIN_RESERVED, +	PIN(GMB,  NAND,  IDE,    NAND,   GMI,       GMI_INT,     GMI), +	PIN(GMD,  NAND,  RSVD,   NAND,   GMI,       SFLASH,      GMI), +	PIN(DDC,  LCD,   I2C2,   RSVD,   RSVD,      RSVD,        RSVD4), + +	/* 64 */ +	PINP(LD0,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD1,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD2,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD3,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD4,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD5,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD6,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD7,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), + +	PINP(LD8,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD9,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD10, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD11, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD12, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD13, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD14, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD15, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), + +	PINP(LD16, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LD17), +	PINP(LD17, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD17), +	PINP(LHP0, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD21_20), +	PINP(LHP1, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD19_18), +	PINP(LHP2, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD19_18), +	PINP(LVP0, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LC), +	PINP(LVP1, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD21_20), +	PINP(HDINT, LCD, HDMI,   RSVD,   RSVD,      RSVD,     HDMI , LC), + +	PINP(LM0,  LCD,  DISPA,  DISPB,  SPI3,      RSVD,     RSVD4, LC), +	PINP(LM1,  LCD,  DISPA,  DISPB,  RSVD,      CRT,      RSVD3, LC), +	PINP(LVS,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LC), +	PINP(LSC0, LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LC), +	PINP(LSC1, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS), +	PINP(LSCK, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS), +	PINP(LDC,  LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LS), +	PINP(LCSN, LCD,  DISPA,  DISPB,  SPI3,      RSVD,     RSVD4, LS), + +	/* 96 */ +	PINP(LSPI, LCD,  DISPA,  DISPB,  XIO,       HDMI,     DISPA, LC), +	PINP(LSDA, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS), +	PINP(LSDI, LCD,  DISPA,  DISPB,  SPI3,      RSVD,     DISPA, LS), +	PINP(LPW0, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS), +	PINP(LPW1, LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LS), +	PINP(LPW2, LCD,  DISPA,  DISPB,  SPI3,      HDMI,     DISPA, LS), +	PINP(LDI,  LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD23_22), +	PINP(LHS,  LCD,  DISPA,  DISPB,  XIO,       RSVD,     RSVD4, LC), + +	PINP(LPP,  LCD,  DISPA,  DISPB,  RSVD,      RSVD,     RSVD4, LD23_22), +	PIN_RESERVED, +	PIN(KBCD,  SYS,  KBC,    NAND,   SDIO2,     MIO,      KBC), +	PIN(GPU7,  SYS,  RTCK,   RSVD,   RSVD,      RSVD,     RTCK), +	PIN(DTF,   VI,   I2C3,   RSVD,   VI,        RSVD,     RSVD4), +	PIN(UDA,   BB,   SPI1,   RSVD,   UARTD,     ULPI,     RSVD2), +	PIN(CRTP,  LCD,  CRT,    RSVD,   RSVD,      RSVD,     RSVD), +	PINP(SDB,  SD,   UARTA,  PWM,    SDIO3,     SPI2,     PWM,   NONE), + +	/* these pin groups only have pullup and pull down control */ +	PINALL(CK32,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(DDRC,  DDR,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(PMCA,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(PMCB,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(PMCC,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(PMCD,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(PMCE,  SYS,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(XM2C,  DDR,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +	PINALL(XM2D,  DDR,   RSVD, RSVD, RSVD, RSVD,  RSVD, MUXCTL_NONE, +		PUCTL_NONE), +}; + +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_tri[TRISTATE_REG(pin)]; +	u32 reg; + +	reg = readl(tri); +	if (enable) +		reg |= TRISTATE_MASK(pin); +	else +		reg &= ~TRISTATE_MASK(pin); +	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; +	enum pmux_pullid pull_id = tegra_soc_pingroups[pin].pull_id; +	u32 *pull = &pmt->pmt_pull[PULL_REG(pull_id)]; +	u32 mask_bit; +	u32 reg; +	mask_bit = PULL_SHIFT(pull_id); + +	reg = readl(pull); +	reg &= ~(0x3 << mask_bit); +	reg |= pupd << mask_bit; +	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; +	enum pmux_ctlid mux_id = tegra_soc_pingroups[pin].ctl_id; +	u32 *muxctl = &pmt->pmt_ctl[MUXCTL_REG(mux_id)]; +	u32 mask_bit; +	int i, mux = -1; +	u32 reg; + +	assert(pmux_func_isvalid(func)); + +	/* Handle special values */ +	if (func >= PMUX_FUNC_RSVD1) { +		mux = (func - PMUX_FUNC_RSVD1) & 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); + +	mask_bit = MUXCTL_SHIFT(mux_id); +	reg = readl(muxctl); +	reg &= ~(0x3 << mask_bit); +	reg |= mux << mask_bit; +	writel(reg, muxctl); +} + +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); +} + +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/tegra20-common/pmu.c b/arch/arm/cpu/tegra20-common/pmu.c new file mode 100644 index 000000000..53505e9c5 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/pmu.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <tps6586x.h> +#include <asm/io.h> +#include <asm/arch/ap20.h> +#include <asm/arch/tegra20.h> +#include <asm/arch/tegra_i2c.h> +#include <asm/arch/sys_proto.h> + +#define VDD_CORE_NOMINAL_T25	0x17	/* 1.3v */ +#define VDD_CPU_NOMINAL_T25	0x10	/* 1.125v */ + +#define VDD_CORE_NOMINAL_T20	0x16	/* 1.275v */ +#define VDD_CPU_NOMINAL_T20	0x0f	/* 1.1v */ + +#define VDD_RELATION		0x02	/*  50mv */ +#define VDD_TRANSITION_STEP	0x06	/* 150mv */ +#define VDD_TRANSITION_RATE	0x06	/* 3.52mv/us */ + +int pmu_set_nominal(void) +{ +	int core, cpu, bus; + +	/* by default, the table has been filled with T25 settings */ +	switch (tegra_get_chip_type()) { +	case TEGRA_SOC_T20: +		core = VDD_CORE_NOMINAL_T20; +		cpu = VDD_CPU_NOMINAL_T20; +		break; +	case TEGRA_SOC_T25: +		core = VDD_CORE_NOMINAL_T25; +		cpu = VDD_CPU_NOMINAL_T25; +		break; +	default: +		debug("%s: Unknown chip type\n", __func__); +		return -1; +	} + +	bus = tegra_i2c_get_dvc_bus_num(); +	if (bus == -1) { +		debug("%s: Cannot find DVC I2C bus\n", __func__); +		return -1; +	} +	tps6586x_init(bus); +	tps6586x_set_pwm_mode(TPS6586X_PWM_SM1); +	return tps6586x_adjust_sm0_sm1(core, cpu, VDD_TRANSITION_STEP, +				VDD_TRANSITION_RATE, VDD_RELATION); +} diff --git a/arch/arm/cpu/tegra20-common/sys_info.c b/arch/arm/cpu/tegra20-common/sys_info.c new file mode 100644 index 000000000..1a0bb561a --- /dev/null +++ b/arch/arm/cpu/tegra20-common/sys_info.c @@ -0,0 +1,35 @@ +/* + * (C) Copyright 2010,2011 + * NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +#ifdef CONFIG_DISPLAY_CPUINFO +/* Print CPU information */ +int print_cpuinfo(void) +{ +	puts("TEGRA20\n"); + +	/* TBD: Add printf of major/minor rev info, stepping, etc. */ +	return 0; +} +#endif	/* CONFIG_DISPLAY_CPUINFO */ diff --git a/arch/arm/cpu/tegra20-common/timer.c b/arch/arm/cpu/tegra20-common/timer.c new file mode 100644 index 000000000..562e41401 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/timer.c @@ -0,0 +1,111 @@ +/* + * (C) Copyright 2010,2011 + * NVIDIA Corporation <www.nvidia.com> + * + * (C) Copyright 2008 + * Texas Instruments + * + * Richard Woodruff <r-woodruff2@ti.com> + * Syed Moahmmed Khasim <khasim@ti.com> + * + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * Alex Zuepke <azu@sysgo.de> + * + * (C) Copyright 2002 + * Gary Jennejohn, DENX Software Engineering, <garyj@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 <common.h> +#include <asm/io.h> +#include <asm/arch/tegra20.h> +#include <asm/arch/timer.h> + +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); +} diff --git a/arch/arm/cpu/tegra20-common/warmboot.c b/arch/arm/cpu/tegra20-common/warmboot.c new file mode 100644 index 000000000..6ce995ef0 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/warmboot.c @@ -0,0 +1,386 @@ +/* + * (C) Copyright 2010 - 2011 + * NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/ap20.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch/pmc.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/tegra20.h> +#include <asm/arch/fuse.h> +#include <asm/arch/emc.h> +#include <asm/arch/gp_padctrl.h> +#include <asm/arch/warmboot.h> +#include <asm/arch/sdram_param.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef CONFIG_TEGRA_CLOCK_SCALING +#error "You must enable CONFIG_TEGRA_CLOCK_SCALING to use CONFIG_TEGRA_LP0" +#endif + +/* + * This is the place in SRAM where the SDRAM parameters are stored. There + * are 4 blocks, one for each RAM code + */ +#define SDRAM_PARAMS_BASE	(AP20_BASE_PA_SRAM + 0x188) + +/* TODO: If we later add support for the Misc GP controller, refactor this */ +union xm2cfga_reg { +	struct { +		u32 reserved0:2; +		u32 hsm_en:1; +		u32 reserved1:2; +		u32 preemp_en:1; +		u32 vref_en:1; +		u32 reserved2:5; +		u32 cal_drvdn:5; +		u32 reserved3:3; +		u32 cal_drvup:5; +		u32 reserved4:3; +		u32 cal_drvdn_slwr:2; +		u32 cal_drvup_slwf:2; +	}; +	u32 word; +}; + +union xm2cfgd_reg { +	struct { +		u32 reserved0:2; +		u32 hsm_en:1; +		u32 schmt_en:1; +		u32 lpmd:2; +		u32 vref_en:1; +		u32 reserved1:5; +		u32 cal_drvdn:5; +		u32 reserved2:3; +		u32 cal_drvup:5; +		u32 reserved3:3; +		u32 cal_drvdn_slwr:2; +		u32 cal_drvup_slwf:2; +	}; +	u32 word; +}; + +/* + * TODO: This register is not documented in the TRM yet. We could move this + * into the EMC and give it a proper interface, but not while it is + * undocumented. + */ +union fbio_spare_reg { +	struct { +		u32 reserved:24; +		u32 cfg_wb0:8; +	}; +	u32 word; +}; + +/* We pack the resume information into these unions for later */ +union scratch2_reg { +	struct { +		u32 pllm_base_divm:5; +		u32 pllm_base_divn:10; +		u32 pllm_base_divp:3; +		u32 pllm_misc_lfcon:4; +		u32 pllm_misc_cpcon:4; +		u32 gp_xm2cfga_padctrl_preemp:1; +		u32 gp_xm2cfgd_padctrl_schmt:1; +		u32 osc_ctrl_xobp:1; +		u32 memory_type:3; +	}; +	u32 word; +}; + +union scratch4_reg { +	struct { +		u32 emc_clock_divider:8; +		u32 pllm_stable_time:8; +		u32 pllx_stable_time:8; +		u32 emc_fbio_spare_cfg_wb0:8; +	}; +	u32 word; +}; + +union scratch24_reg { +	struct { +		u32 emc_auto_cal_wait:8; +		u32 emc_pin_program_wait:8; +		u32 warmboot_wait:8; +		u32 reserved:8; +	}; +	u32 word; +}; + +int warmboot_save_sdram_params(void) +{ +	u32 ram_code; +	struct sdram_params sdram; +	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; +	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; +	struct apb_misc_gp_ctlr *gp = +			(struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; +	struct emc_ctlr *emc = emc_get_controller(gd->fdt_blob); +	union scratch2_reg scratch2; +	union scratch4_reg scratch4; +	union scratch24_reg scratch24; +	union xm2cfga_reg xm2cfga; +	union xm2cfgd_reg xm2cfgd; +	union fbio_spare_reg fbio_spare; + +	/* get ram code that is used as index to array sdram_params in BCT */ +	ram_code = (readl(&pmt->pmt_strap_opt_a) >> +			STRAP_OPT_A_RAM_CODE_SHIFT) & 3; +	memcpy(&sdram, +	       (char *)((struct sdram_params *)SDRAM_PARAMS_BASE + ram_code), +	       sizeof(sdram)); + +	xm2cfga.word = readl(&gp->xm2cfga); +	xm2cfgd.word = readl(&gp->xm2cfgd); + +	scratch2.word = 0; +	scratch2.osc_ctrl_xobp = clock_get_osc_bypass(); + +	/* Get the memory PLL settings */ +	{ +		u32 divm, divn, divp, cpcon, lfcon; + +		if (clock_ll_read_pll(CLOCK_ID_MEMORY, &divm, &divn, &divp, +					&cpcon, &lfcon)) +			return -1; +		scratch2.pllm_base_divm = divm; +		scratch2.pllm_base_divn = divn; +		scratch2.pllm_base_divp = divp; +		scratch2.pllm_misc_cpcon = cpcon; +		scratch2.pllm_misc_lfcon = lfcon; +	} + +	scratch2.gp_xm2cfga_padctrl_preemp = xm2cfga.preemp_en; +	scratch2.gp_xm2cfgd_padctrl_schmt = xm2cfgd.schmt_en; +	scratch2.memory_type = sdram.memory_type; +	writel(scratch2.word, &pmc->pmc_scratch2); + +	/* collect data from various sources for pmc_scratch4 */ +	fbio_spare.word = readl(&emc->fbio_spare); +	scratch4.word = 0; +	scratch4.emc_fbio_spare_cfg_wb0 = fbio_spare.cfg_wb0; +	scratch4.emc_clock_divider = sdram.emc_clock_divider; +	scratch4.pllm_stable_time = -1; +	scratch4.pllx_stable_time = -1; +	writel(scratch4.word, &pmc->pmc_scratch4); + +	/* collect various data from sdram for pmc_scratch24 */ +	scratch24.word = 0; +	scratch24.emc_pin_program_wait = sdram.emc_pin_program_wait; +	scratch24.emc_auto_cal_wait = sdram.emc_auto_cal_wait; +	scratch24.warmboot_wait = sdram.warm_boot_wait; +	writel(scratch24.word, &pmc->pmc_scratch24); + +	return 0; +} + +static u32 get_major_version(void) +{ +	u32 major_id; +	struct apb_misc_gp_ctlr *gp = +		(struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; + +	major_id = (readl(&gp->hidrev) & HIDREV_MAJORPREV_MASK) >> +			HIDREV_MAJORPREV_SHIFT; +	return major_id; +} + +static int is_production_mode_fuse_set(struct fuse_regs *fuse) +{ +	return readl(&fuse->production_mode); +} + +static int is_odm_production_mode_fuse_set(struct fuse_regs *fuse) +{ +	return readl(&fuse->security_mode); +} + +static int is_failure_analysis_mode(struct fuse_regs *fuse) +{ +	return readl(&fuse->fa); +} + +static int ap20_is_odm_production_mode(void) +{ +	struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE; + +	if (!is_failure_analysis_mode(fuse) && +	    is_odm_production_mode_fuse_set(fuse)) +		return 1; +	else +		return 0; +} + +static int ap20_is_production_mode(void) +{ +	struct fuse_regs *fuse = (struct fuse_regs *)NV_PA_FUSE_BASE; + +	if (get_major_version() == 0) +		return 1; + +	if (!is_failure_analysis_mode(fuse) && +	    is_production_mode_fuse_set(fuse) && +	    !is_odm_production_mode_fuse_set(fuse)) +		return 1; +	else +		return 0; +} + +static enum fuse_operating_mode fuse_get_operation_mode(void) +{ +	u32 chip_id; +	struct apb_misc_gp_ctlr *gp = +		(struct apb_misc_gp_ctlr *)NV_PA_APB_MISC_GP_BASE; + +	chip_id = (readl(&gp->hidrev) & HIDREV_CHIPID_MASK) >> +			HIDREV_CHIPID_SHIFT; +	if (chip_id == CHIPID_TEGRA20) { +		if (ap20_is_odm_production_mode()) { +			printf("!! odm_production_mode is not supported !!\n"); +			return MODE_UNDEFINED; +		} else +			if (ap20_is_production_mode()) +				return MODE_PRODUCTION; +			else +				return MODE_UNDEFINED; +	} +	return MODE_UNDEFINED; +} + +static void determine_crypto_options(int *is_encrypted, int *is_signed, +				     int *use_zero_key) +{ +	switch (fuse_get_operation_mode()) { +	case MODE_PRODUCTION: +		*is_encrypted = 0; +		*is_signed = 1; +		*use_zero_key = 1; +		break; +	case MODE_UNDEFINED: +	default: +		*is_encrypted = 0; +		*is_signed = 0; +		*use_zero_key  = 0; +		break; +	} +} + +static int sign_wb_code(u32 start, u32 length, int use_zero_key) +{ +	int err; +	u8 *source;		/* Pointer to source */ +	u8 *hash; + +	/* Calculate AES block parameters. */ +	source = (u8 *)(start + offsetof(struct wb_header, random_aes_block)); +	length -= offsetof(struct wb_header, random_aes_block); +	hash = (u8 *)(start + offsetof(struct wb_header, hash)); +	err = sign_data_block(source, length, hash); + +	return err; +} + +int warmboot_prepare_code(u32 seg_address, u32 seg_length) +{ +	int err = 0; +	u32 length;			/* length of the signed/encrypt code */ +	struct wb_header *dst_header;	/* Pointer to dest WB header */ +	int is_encrypted;		/* Segment is encrypted */ +	int is_signed;			/* Segment is signed */ +	int use_zero_key;		/* Use key of all zeros */ + +	/* Determine crypto options. */ +	determine_crypto_options(&is_encrypted, &is_signed, &use_zero_key); + +	/* Get the actual code limits. */ +	length = roundup(((u32)wb_end - (u32)wb_start), 16); + +	/* +	 * The region specified by seg_address must be in SDRAM and must be +	 * nonzero in length. +	 */ +	if (seg_length == 0 || seg_address < NV_PA_SDRAM_BASE || +		seg_address + seg_length >= NV_PA_SDRAM_BASE + gd->ram_size) { +		err = -EFAULT; +		goto fail; +	} + +	/* Things must be 16-byte aligned. */ +	if ((seg_length & 0xF) || (seg_address & 0xF)) { +		err = -EINVAL; +		goto fail; +	} + +	/* Will the code fit? (destination includes wb_header + wb code) */ +	if (seg_length < (length + sizeof(struct wb_header))) { +		err = -EINVAL; +		goto fail; +	} + +	dst_header = (struct wb_header *)seg_address; +	memset((char *)dst_header, 0, sizeof(struct wb_header)); + +	/* Populate the random_aes_block as requested. */ +	{ +		u32 *aes_block = (u32 *)&(dst_header->random_aes_block); +		u32 *end = (u32 *)(((u32)aes_block) + +				   sizeof(dst_header->random_aes_block)); + +		do { +			*aes_block++ = 0; +		} while (aes_block < end); +	} + +	/* Populate the header. */ +	dst_header->length_insecure = length + sizeof(struct wb_header); +	dst_header->length_secure = length + sizeof(struct wb_header); +	dst_header->destination = AP20_WB_RUN_ADDRESS; +	dst_header->entry_point = AP20_WB_RUN_ADDRESS; +	dst_header->code_length = length; + +	if (is_encrypted) { +		printf("!!!! Encryption is not supported !!!!\n"); +		dst_header->length_insecure = 0; +		err = -EACCES; +		goto fail; +	} else +		/* copy the wb code directly following dst_header. */ +		memcpy((char *)(dst_header+1), (char *)wb_start, length); + +	if (is_signed) +		err = sign_wb_code(seg_address, dst_header->length_insecure, +				   use_zero_key); + +fail: +	if (err) +		printf("Warning: warmboot code copy failed (error=%d)\n", err); + +	return err; +} diff --git a/arch/arm/cpu/tegra20-common/warmboot_avp.c b/arch/arm/cpu/tegra20-common/warmboot_avp.c new file mode 100644 index 000000000..80a5a15de --- /dev/null +++ b/arch/arm/cpu/tegra20-common/warmboot_avp.c @@ -0,0 +1,250 @@ +/* + * (C) Copyright 2010 - 2011 + * NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/ap20.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch/flow.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/pmc.h> +#include <asm/arch/tegra20.h> +#include <asm/arch/warmboot.h> +#include "warmboot_avp.h" + +#define DEBUG_RESET_CORESIGHT + +void wb_start(void) +{ +	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; +	struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; +	struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE; +	struct clk_rst_ctlr *clkrst = +			(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; +	union osc_ctrl_reg osc_ctrl; +	union pllx_base_reg pllx_base; +	union pllx_misc_reg pllx_misc; +	union scratch3_reg scratch3; +	u32 reg; + +	/* enable JTAG & TBE */ +	writel(CONFIG_CTL_TBE | CONFIG_CTL_JTAG, &pmt->pmt_cfg_ctl); + +	/* Are we running where we're supposed to be? */ +	asm volatile ( +		"adr	%0, wb_start;"	/* reg: wb_start address */ +		: "=r"(reg)		/* output */ +					/* no input, no clobber list */ +	); + +	if (reg != AP20_WB_RUN_ADDRESS) +		goto do_reset; + +	/* Are we running with AVP? */ +	if (readl(NV_PA_PG_UP_BASE + PG_UP_TAG_0) != PG_UP_TAG_AVP) +		goto do_reset; + +#ifdef DEBUG_RESET_CORESIGHT +	/* Assert CoreSight reset */ +	reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_U]); +	reg |= SWR_CSITE_RST; +	writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_U]); +#endif + +	/* TODO: Set the drive strength - maybe make this a board parameter? */ +	osc_ctrl.word = readl(&clkrst->crc_osc_ctrl); +	osc_ctrl.xofs = 4; +	osc_ctrl.xoe = 1; +	writel(osc_ctrl.word, &clkrst->crc_osc_ctrl); + +	/* Power up the CPU complex if necessary */ +	if (!(readl(&pmc->pmc_pwrgate_status) & PWRGATE_STATUS_CPU)) { +		reg = PWRGATE_TOGGLE_PARTID_CPU | PWRGATE_TOGGLE_START; +		writel(reg, &pmc->pmc_pwrgate_toggle); +		while (!(readl(&pmc->pmc_pwrgate_status) & PWRGATE_STATUS_CPU)) +			; +	} + +	/* Remove the I/O clamps from the CPU power partition. */ +	reg = readl(&pmc->pmc_remove_clamping); +	reg |= CPU_CLMP; +	writel(reg, &pmc->pmc_remove_clamping); + +	reg = EVENT_ZERO_VAL_20 | EVENT_MSEC | EVENT_MODE_STOP; +	writel(reg, &flow->halt_cop_events); + +	/* Assert CPU complex reset */ +	reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_L]); +	reg |= CPU_RST; +	writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_L]); + +	/* Hold both CPUs in reset */ +	reg = CPU_CMPLX_CPURESET0 | CPU_CMPLX_CPURESET1 | CPU_CMPLX_DERESET0 | +	      CPU_CMPLX_DERESET1 | CPU_CMPLX_DBGRESET0 | CPU_CMPLX_DBGRESET1; +	writel(reg, &clkrst->crc_cpu_cmplx_set); + +	/* Halt CPU1 at the flow controller for uni-processor configurations */ +	writel(EVENT_MODE_STOP, &flow->halt_cpu1_events); + +	/* +	 * Set the CPU reset vector. SCRATCH41 contains the physical +	 * address of the CPU-side restoration code. +	 */ +	reg = readl(&pmc->pmc_scratch41); +	writel(reg, EXCEP_VECTOR_CPU_RESET_VECTOR); + +	/* Select CPU complex clock source */ +	writel(CCLK_PLLP_BURST_POLICY, &clkrst->crc_cclk_brst_pol); + +	/* Start the CPU0 clock and stop the CPU1 clock */ +	reg = CPU_CMPLX_CPU_BRIDGE_CLKDIV_4 | CPU_CMPLX_CPU0_CLK_STP_RUN | +	      CPU_CMPLX_CPU1_CLK_STP_STOP; +	writel(reg, &clkrst->crc_clk_cpu_cmplx); + +	/* Enable the CPU complex clock */ +	reg = readl(&clkrst->crc_clk_out_enb[TEGRA_DEV_L]); +	reg |= CLK_ENB_CPU; +	writel(reg, &clkrst->crc_clk_out_enb[TEGRA_DEV_L]); + +	/* Make sure the resets were held for at least 2 microseconds */ +	reg = readl(TIMER_USEC_CNTR); +	while (readl(TIMER_USEC_CNTR) <= (reg + 2)) +		; + +#ifdef DEBUG_RESET_CORESIGHT +	/* +	 * De-assert CoreSight reset. +	 * NOTE: We're leaving the CoreSight clock on the oscillator for +	 *	now. It will be restored to its original clock source +	 *	when the CPU-side restoration code runs. +	 */ +	reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_U]); +	reg &= ~SWR_CSITE_RST; +	writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_U]); +#endif + +	/* Unlock the CPU CoreSight interfaces */ +	reg = 0xC5ACCE55; +	writel(reg, CSITE_CPU_DBG0_LAR); +	writel(reg, CSITE_CPU_DBG1_LAR); + +	/* +	 * Sample the microsecond timestamp again. This is the time we must +	 * use when returning from LP0 for PLL stabilization delays. +	 */ +	reg = readl(TIMER_USEC_CNTR); +	writel(reg, &pmc->pmc_scratch1); + +	pllx_base.word = 0; +	pllx_misc.word = 0; +	scratch3.word = readl(&pmc->pmc_scratch3); + +	/* Get the OSC. For 19.2 MHz, use 19 to make the calculations easier */ +	reg = (readl(TIMER_USEC_CFG) & USEC_CFG_DIVISOR_MASK) + 1; + +	/* +	 * According to the TRM, for 19.2MHz OSC, the USEC_DIVISOR is 0x5f, and +	 * USEC_DIVIDEND is 0x04. So, if USEC_DIVISOR > 26, OSC is 19.2 MHz. +	 * +	 * reg is used to calculate the pllx freq, which is used to determine if +	 * to set dccon or not. +	 */ +	if (reg > 26) +		reg = 19; + +	/* PLLX_BASE.PLLX_DIVM */ +	if (scratch3.pllx_base_divm == reg) +		reg = 0; +	else +		reg = 1; + +	/* PLLX_BASE.PLLX_DIVN */ +	pllx_base.divn = scratch3.pllx_base_divn; +	reg = scratch3.pllx_base_divn << reg; + +	/* PLLX_BASE.PLLX_DIVP */ +	pllx_base.divp = scratch3.pllx_base_divp; +	reg = reg >> scratch3.pllx_base_divp; + +	pllx_base.bypass = 1; + +	/* PLLX_MISC_DCCON must be set for pllx frequency > 600 MHz. */ +	if (reg > 600) +		pllx_misc.dccon = 1; + +	/* PLLX_MISC_LFCON */ +	pllx_misc.lfcon = scratch3.pllx_misc_lfcon; + +	/* PLLX_MISC_CPCON */ +	pllx_misc.cpcon = scratch3.pllx_misc_cpcon; + +	writel(pllx_misc.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_misc); +	writel(pllx_base.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); + +	pllx_base.enable = 1; +	writel(pllx_base.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); +	pllx_base.bypass = 0; +	writel(pllx_base.word, &clkrst->crc_pll_simple[SIMPLE_PLLX].pll_base); + +	writel(0, flow->halt_cpu_events); + +	reg = CPU_CMPLX_CPURESET0 | CPU_CMPLX_DBGRESET0 | CPU_CMPLX_DERESET0; +	writel(reg, &clkrst->crc_cpu_cmplx_clr); + +	reg = PLLM_OUT1_RSTN_RESET_DISABLE | PLLM_OUT1_CLKEN_ENABLE | +	      PLLM_OUT1_RATIO_VAL_8; +	writel(reg, &clkrst->crc_pll[CLOCK_ID_MEMORY].pll_out); + +	reg = SCLK_SWAKE_FIQ_SRC_PLLM_OUT1 | SCLK_SWAKE_IRQ_SRC_PLLM_OUT1 | +	      SCLK_SWAKE_RUN_SRC_PLLM_OUT1 | SCLK_SWAKE_IDLE_SRC_PLLM_OUT1 | +	      SCLK_SYS_STATE_IDLE; +	writel(reg, &clkrst->crc_sclk_brst_pol); + +	/* avp_resume: no return after the write */ +	reg = readl(&clkrst->crc_rst_dev[TEGRA_DEV_L]); +	reg &= ~CPU_RST; +	writel(reg, &clkrst->crc_rst_dev[TEGRA_DEV_L]); + +	/* avp_halt: */ +avp_halt: +	reg = EVENT_MODE_STOP | EVENT_JTAG; +	writel(reg, flow->halt_cop_events); +	goto avp_halt; + +do_reset: +	/* +	 * Execution comes here if something goes wrong. The chip is reset and +	 * a cold boot is performed. +	 */ +	writel(SWR_TRIG_SYS_RST, &clkrst->crc_rst_dev[TEGRA_DEV_L]); +	goto do_reset; +} + +/* + * wb_end() is a dummy function, and must be directly following wb_start(), + * and is used to calculate the size of wb_start(). + */ +void wb_end(void) +{ +} diff --git a/arch/arm/cpu/tegra20-common/warmboot_avp.h b/arch/arm/cpu/tegra20-common/warmboot_avp.h new file mode 100644 index 000000000..4b71c0784 --- /dev/null +++ b/arch/arm/cpu/tegra20-common/warmboot_avp.h @@ -0,0 +1,81 @@ +/* + * (C) Copyright 2010, 2011 + * NVIDIA Corporation <www.nvidia.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _WARMBOOT_AVP_H_ +#define _WARMBOOT_AVP_H_ + +#define TEGRA_DEV_L			0 +#define TEGRA_DEV_H			1 +#define TEGRA_DEV_U			2 + +#define SIMPLE_PLLX			(CLOCK_ID_XCPU - CLOCK_ID_FIRST_SIMPLE) +#define SIMPLE_PLLE			(CLOCK_ID_EPCI - CLOCK_ID_FIRST_SIMPLE) + +#define TIMER_USEC_CNTR			(NV_PA_TMRUS_BASE + 0) +#define TIMER_USEC_CFG			(NV_PA_TMRUS_BASE + 4) + +#define USEC_CFG_DIVISOR_MASK		0xffff + +#define CONFIG_CTL_TBE			(1 << 7) +#define CONFIG_CTL_JTAG			(1 << 6) + +#define CPU_RST				(1 << 0) +#define CLK_ENB_CPU			(1 << 0) +#define SWR_TRIG_SYS_RST		(1 << 2) +#define SWR_CSITE_RST			(1 << 9) + +#define PWRGATE_STATUS_CPU		(1 << 0) +#define PWRGATE_TOGGLE_PARTID_CPU	(0 << 0) +#define PWRGATE_TOGGLE_START		(1 << 8) + +#define CPU_CMPLX_CPU_BRIDGE_CLKDIV_4	(3 << 0) +#define CPU_CMPLX_CPU0_CLK_STP_STOP	(1 << 8) +#define CPU_CMPLX_CPU0_CLK_STP_RUN	(0 << 8) +#define CPU_CMPLX_CPU1_CLK_STP_STOP	(1 << 9) +#define CPU_CMPLX_CPU1_CLK_STP_RUN	(0 << 9) + +#define CPU_CMPLX_CPURESET0		(1 << 0) +#define CPU_CMPLX_CPURESET1		(1 << 1) +#define CPU_CMPLX_DERESET0		(1 << 4) +#define CPU_CMPLX_DERESET1		(1 << 5) +#define CPU_CMPLX_DBGRESET0		(1 << 12) +#define CPU_CMPLX_DBGRESET1		(1 << 13) + +#define PLLM_OUT1_RSTN_RESET_DISABLE	(1 << 0) +#define PLLM_OUT1_CLKEN_ENABLE		(1 << 1) +#define PLLM_OUT1_RATIO_VAL_8		(8 << 8) + +#define SCLK_SYS_STATE_IDLE		(1 << 28) +#define SCLK_SWAKE_FIQ_SRC_PLLM_OUT1	(7 << 12) +#define SCLK_SWAKE_IRQ_SRC_PLLM_OUT1	(7 << 8) +#define SCLK_SWAKE_RUN_SRC_PLLM_OUT1	(7 << 4) +#define SCLK_SWAKE_IDLE_SRC_PLLM_OUT1	(7 << 0) + +#define EVENT_ZERO_VAL_20		(20 << 0) +#define EVENT_MSEC			(1 << 24) +#define EVENT_JTAG			(1 << 28) +#define EVENT_MODE_STOP			(2 << 29) + +#define CCLK_PLLP_BURST_POLICY		0x20004444 + +#endif |