diff options
author | Masahiro Yamada <yamada.m@jp.panasonic.com> | 2015-02-20 17:04:04 +0900 |
---|---|---|
committer | Tom Rini <trini@ti.com> | 2015-02-21 08:23:51 -0500 |
commit | 09f455dca74973ef5e42311162c8dff7e83d44a2 (patch) | |
tree | 3107041e5a3c55644d8323b121026b062884d079 /arch/arm/mach-tegra/tegra20 | |
parent | 620118403e1521b4c883848a84d2fb68e3fa1aa0 (diff) | |
download | u-boot-imx-09f455dca74973ef5e42311162c8dff7e83d44a2.zip u-boot-imx-09f455dca74973ef5e42311162c8dff7e83d44a2.tar.gz u-boot-imx-09f455dca74973ef5e42311162c8dff7e83d44a2.tar.bz2 |
ARM: tegra: collect SoC sources into mach-tegra
This commit moves files as follows:
arch/arm/cpu/arm720t/tegra20/* -> arch/arm/mach-tegra/tegra20/*
arch/arm/cpu/arm720t/tegra30/* -> arch/arm/mach-tegra/tegra30/*
arch/arm/cpu/arm720t/tegra114/* -> arch/arm/mach-tegra/tegra114/*
arch/arm/cpu/arm720t/tegra124* -> arch/arm/mach-tegra/tegra124/*
arch/arm/cpu/arm720t/tegra-common/* -> arch/arm/mach-tegra/*
arch/arm/cpu/armv7/tegra20/* -> arch/arm/mach-tegra/tegra20/*
arch/arm/cpu/armv7/tegra30/* -> arch/arm/mach-tegra/tegra30/*
arch/arm/cpu/armv7/tegra114/* -> arch/arm/mach-tegra/tegra114/*
arch/arm/cpu/armv7/tegra124/* -> arch/arm/mach-tegra/tegra124/*
arch/arm/cpu/armv7/tegra-common/* -> arch/arm/mach-tegra/*
arch/arm/cpu/tegra20-common/* -> arch/arm/mach-tegra/tegra20/*
arch/arm/cpu/tegra30-common/* -> arch/arm/mach-tegra/tegra30/*
arch/arm/cpu/tegra114-common/* -> arch/arm/mach-tegra/tegra114/*
arch/arm/cpu/tegra124-common/* -> arch/arm/mach-tegra/tegra124/*
arch/arm/cpu/tegra-common/* -> arch/arm/mach-tegra/*
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Tested-by: Simon Glass <sjg@chromium.org> [ on nyan-big ]
Cc: Stephen Warren <swarren@nvidia.com>
Cc: Tom Warren <twarren@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra20')
-rw-r--r-- | arch/arm/mach-tegra/tegra20/Kconfig | 52 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/Makefile | 21 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/clock.c | 687 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/cpu.c | 70 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/crypto.c | 137 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/crypto.h | 20 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/display.c | 394 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/emc.c | 270 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/funcmux.c | 296 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/pinmux.c | 425 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/pmu.c | 65 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/pwm.c | 86 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/warmboot.c | 372 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/warmboot_avp.c | 236 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20/warmboot_avp.h | 65 |
15 files changed, 3196 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/tegra20/Kconfig b/arch/arm/mach-tegra/tegra20/Kconfig new file mode 100644 index 0000000..a354e2a --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/Kconfig @@ -0,0 +1,52 @@ +if TEGRA20 + +choice + prompt "Tegra20 board select" + +config TARGET_HARMONY + bool "NVIDIA Tegra20 Harmony evaluation board" + +config TARGET_MEDCOM_WIDE + bool "Avionic Design Medcom-Wide board" + +config TARGET_PAZ00 + bool "Paz00 board" + +config TARGET_PLUTUX + bool "Avionic Design Plutux board" + +config TARGET_SEABOARD + bool "NVIDIA Seaboard" + +config TARGET_TEC + bool "Avionic Design Tamonten Evaluation Carrier" + +config TARGET_TRIMSLICE + bool "Compulab TrimSlice board" + +config TARGET_VENTANA + bool "NVIDIA Tegra20 Ventana evaluation board" + +config TARGET_WHISTLER + bool "NVIDIA Tegra20 Whistler evaluation board" + +config TARGET_COLIBRI_T20_IRIS + bool "Toradex Colibri T20 board" + +endchoice + +config SYS_SOC + default "tegra20" + +source "board/nvidia/harmony/Kconfig" +source "board/avionic-design/medcom-wide/Kconfig" +source "board/compal/paz00/Kconfig" +source "board/avionic-design/plutux/Kconfig" +source "board/nvidia/seaboard/Kconfig" +source "board/avionic-design/tec/Kconfig" +source "board/compulab/trimslice/Kconfig" +source "board/nvidia/ventana/Kconfig" +source "board/nvidia/whistler/Kconfig" +source "board/toradex/colibri_t20_iris/Kconfig" + +endif diff --git a/arch/arm/mach-tegra/tegra20/Makefile b/arch/arm/mach-tegra/tegra20/Makefile new file mode 100644 index 0000000..d48f9bb --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/Makefile @@ -0,0 +1,21 @@ +# +# (C) Copyright 2010,2011 Nvidia Corporation. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +ifdef CONFIG_SPL_BUILD +obj-y += cpu.o +else +obj-$(CONFIG_PWM_TEGRA) += pwm.o +obj-$(CONFIG_VIDEO_TEGRA) += display.o +endif + +# The AVP is ARMv4T architecture so we must use special compiler +# flags for any startup files it might use. +CFLAGS_warmboot_avp.o += -march=armv4t + +obj-y += clock.o funcmux.o pinmux.o +obj-$(CONFIG_TEGRA_LP0) += warmboot.o crypto.o warmboot_avp.o +obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o +obj-$(CONFIG_TEGRA_PMU) += pmu.o diff --git a/arch/arm/mach-tegra/tegra20/clock.c b/arch/arm/mach-tegra/tegra20/clock.c new file mode 100644 index 0000000..7b9e10c --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/clock.c @@ -0,0 +1,687 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Tegra20 Clock control functions */ + +#include <common.h> +#include <errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/tegra.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra/timer.h> +#include <div64.h> +#include <fdtdec.h> + +/* + * 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 */ +}; + +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, +}; + +/* + * 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(PCIEXCLK), + 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. T20 has 4 frequencies that it supports. + */ +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; +} + +/* Returns a pointer to the clock source register for a peripheral */ +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]; +} + +/** + * 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) + */ +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 = MASK_BITS_31_28; + else + *mux_bits = MASK_BITS_31_30; + 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; +} + +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 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); +} + +#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 + */ +enum periph_id clk_id_to_periph_id(int clk_id) +{ + if (clk_id > PERIPH_ID_COUNT) + return PERIPH_ID_NONE; + + switch (clk_id) { + case PERIPH_ID_RESERVED1: + case PERIPH_ID_RESERVED2: + case PERIPH_ID_RESERVED30: + case PERIPH_ID_RESERVED35: + case PERIPH_ID_RESERVED56: + case PERIPH_ID_PCIEXCLK: + 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: + case PERIPH_ID_RESERVED91: + return PERIPH_ID_NONE; + default: + return clk_id; + } +} +#endif /* CONFIG_OF_CONTROL */ + +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 arch_timer_init(void) +{ +} + +#define PMC_SATA_PWRGT 0x1ac +#define PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE (1 << 5) +#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL (1 << 4) + +#define PLLE_SS_CNTL 0x68 +#define PLLE_SS_CNTL_SSCINCINTRV(x) (((x) & 0x3f) << 24) +#define PLLE_SS_CNTL_SSCINC(x) (((x) & 0xff) << 16) +#define PLLE_SS_CNTL_SSCBYP (1 << 12) +#define PLLE_SS_CNTL_INTERP_RESET (1 << 11) +#define PLLE_SS_CNTL_BYPASS_SS (1 << 10) +#define PLLE_SS_CNTL_SSCMAX(x) (((x) & 0x1ff) << 0) + +#define PLLE_BASE 0x0e8 +#define PLLE_BASE_ENABLE_CML (1 << 31) +#define PLLE_BASE_ENABLE (1 << 30) +#define PLLE_BASE_PLDIV_CML(x) (((x) & 0xf) << 24) +#define PLLE_BASE_PLDIV(x) (((x) & 0x3f) << 16) +#define PLLE_BASE_NDIV(x) (((x) & 0xff) << 8) +#define PLLE_BASE_MDIV(x) (((x) & 0xff) << 0) + +#define PLLE_MISC 0x0ec +#define PLLE_MISC_SETUP_BASE(x) (((x) & 0xffff) << 16) +#define PLLE_MISC_PLL_READY (1 << 15) +#define PLLE_MISC_LOCK (1 << 11) +#define PLLE_MISC_LOCK_ENABLE (1 << 9) +#define PLLE_MISC_SETUP_EXT(x) (((x) & 0x3) << 2) + +static int tegra_plle_train(void) +{ + unsigned int timeout = 2000; + unsigned long value; + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value |= PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + value = readl(NV_PA_PMC_BASE + PMC_SATA_PWRGT); + value &= ~PMC_SATA_PWRGT_PLLE_IDDQ_OVERRIDE; + writel(value, NV_PA_PMC_BASE + PMC_SATA_PWRGT); + + do { + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if (value & PLLE_MISC_PLL_READY) + break; + + udelay(100); + } while (--timeout); + + if (timeout == 0) { + error("timeout waiting for PLLE to become ready"); + return -ETIMEDOUT; + } + + return 0; +} + +int tegra_plle_enable(void) +{ + unsigned int timeout = 1000; + u32 value; + int err; + + /* disable PLLE clock */ + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value &= ~PLLE_BASE_ENABLE_CML; + value &= ~PLLE_BASE_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + /* clear lock enable and setup field */ + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value &= ~PLLE_MISC_LOCK_ENABLE; + value &= ~PLLE_MISC_SETUP_BASE(0xffff); + value &= ~PLLE_MISC_SETUP_EXT(0x3); + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if ((value & PLLE_MISC_PLL_READY) == 0) { + err = tegra_plle_train(); + if (err < 0) { + error("failed to train PLLE: %d", err); + return err; + } + } + + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + value |= PLLE_MISC_SETUP_BASE(0x7); + value |= PLLE_MISC_LOCK_ENABLE; + value |= PLLE_MISC_SETUP_EXT(0); + writel(value, NV_PA_CLK_RST_BASE + PLLE_MISC); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value |= PLLE_SS_CNTL_SSCBYP | PLLE_SS_CNTL_INTERP_RESET | + PLLE_SS_CNTL_BYPASS_SS; + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_BASE); + value |= PLLE_BASE_ENABLE_CML | PLLE_BASE_ENABLE; + writel(value, NV_PA_CLK_RST_BASE + PLLE_BASE); + + do { + value = readl(NV_PA_CLK_RST_BASE + PLLE_MISC); + if (value & PLLE_MISC_LOCK) + break; + + udelay(2); + } while (--timeout); + + if (timeout == 0) { + error("timeout waiting for PLLE to lock"); + return -ETIMEDOUT; + } + + udelay(50); + + value = readl(NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + value &= ~PLLE_SS_CNTL_SSCINCINTRV(0x3f); + value |= PLLE_SS_CNTL_SSCINCINTRV(0x18); + + value &= ~PLLE_SS_CNTL_SSCINC(0xff); + value |= PLLE_SS_CNTL_SSCINC(0x01); + + value &= ~PLLE_SS_CNTL_SSCBYP; + value &= ~PLLE_SS_CNTL_INTERP_RESET; + value &= ~PLLE_SS_CNTL_BYPASS_SS; + + value &= ~PLLE_SS_CNTL_SSCMAX(0x1ff); + value |= PLLE_SS_CNTL_SSCMAX(0x24); + writel(value, NV_PA_CLK_RST_BASE + PLLE_SS_CNTL); + + return 0; +} diff --git a/arch/arm/mach-tegra/tegra20/cpu.c b/arch/arm/mach-tegra/tegra20/cpu.c new file mode 100644 index 0000000..67f49d7 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/cpu.c @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/tegra.h> +#include <asm/arch-tegra/pmc.h> +#include "../cpu.h" + +static void enable_cpu_power_rail(void) +{ + struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; + u32 reg; + + reg = readl(&pmc->pmc_cntrl); + reg |= CPUPWRREQ_OE; + writel(reg, &pmc->pmc_cntrl); + + /* + * The TI PMU65861C needs a 3.75ms delay between enabling + * the power rail and enabling the CPU clock. This delay + * between SM1EN and SM1 is for switching time + the ramp + * up of the voltage to the CPU (VDD_CPU from PMU). + */ + udelay(3750); +} + +void start_cpu(u32 reset_vector) +{ + /* Enable VDD_CPU */ + enable_cpu_power_rail(); + + /* Hold the CPUs in reset */ + reset_A9_cpu(1); + + /* Disable the CPU clock */ + enable_cpu_clock(0); + + /* Enable CoreSight */ + clock_enable_coresight(1); + + /* + * Set the entry point for CPU execution from reset, + * if it's a non-zero value. + */ + if (reset_vector) + writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR); + + /* Enable the CPU clock */ + enable_cpu_clock(1); + + /* If the CPU doesn't already have power, power it up */ + powerup_cpu(); + + /* Take the CPU out of reset */ + reset_A9_cpu(0); +} diff --git a/arch/arm/mach-tegra/tegra20/crypto.c b/arch/arm/mach-tegra/tegra20/crypto.c new file mode 100644 index 0000000..ec95d7c --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/crypto.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010 - 2011 NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#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 */ +}; + +/** + * 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; + + aes_cbc_encrypt_blocks(key_schedule, tmp_data, left, 1); + + left_shift_vector(left, k1, sizeof(left)); + + if ((left[0] >> 7) != 0) /* get MSB of L */ + k1[AES_KEY_LENGTH-1] ^= AES_CMAC_CONST_RB; + + /* compute the AES-CMAC value */ + for (i = 0; i < num_aes_blocks; i++) { + /* Apply the chain data */ + aes_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) + aes_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); + + /* Update pointers for next loop. */ + cbc_chain_data = dst; + src += AES_KEY_LENGTH; + } +} + +/** + * 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); + + /* + * 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"); + aes_cbc_encrypt_blocks(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/mach-tegra/tegra20/crypto.h b/arch/arm/mach-tegra/tegra20/crypto.h new file mode 100644 index 0000000..f59b927 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/crypto.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010 - 2011 NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#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/mach-tegra/tegra20/display.c b/arch/arm/mach-tegra/tegra20/display.c new file mode 100644 index 0000000..61efed6 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/display.c @@ -0,0 +1,394 @@ +/* + * (C) Copyright 2010 + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/tegra.h> +#include <asm/arch/display.h> +#include <asm/arch/dc.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra/timer.h> + +static struct fdt_disp_config config; + +static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) +{ + unsigned h_dda, v_dda; + unsigned long val; + + val = readl(&dc->cmd.disp_win_header); + val |= WINDOW_A_SELECT; + writel(val, &dc->cmd.disp_win_header); + + writel(win->fmt, &dc->win.color_depth); + + clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK, + BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT); + + val = win->out_x << H_POSITION_SHIFT; + val |= win->out_y << V_POSITION_SHIFT; + writel(val, &dc->win.pos); + + val = win->out_w << H_SIZE_SHIFT; + val |= win->out_h << V_SIZE_SHIFT; + writel(val, &dc->win.size); + + val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT; + val |= win->h << V_PRESCALED_SIZE_SHIFT; + writel(val, &dc->win.prescaled_size); + + writel(0, &dc->win.h_initial_dda); + writel(0, &dc->win.v_initial_dda); + + h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1U); + v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1U); + + val = h_dda << H_DDA_INC_SHIFT; + val |= v_dda << V_DDA_INC_SHIFT; + writel(val, &dc->win.dda_increment); + + writel(win->stride, &dc->win.line_stride); + writel(0, &dc->win.buf_stride); + + val = WIN_ENABLE; + if (win->bpp < 24) + val |= COLOR_EXPAND; + writel(val, &dc->win.win_opt); + + writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr); + writel(win->x, &dc->winbuf.addr_h_offset); + writel(win->y, &dc->winbuf.addr_v_offset); + + writel(0xff00, &dc->win.blend_nokey); + writel(0xff00, &dc->win.blend_1win); + + val = GENERAL_ACT_REQ | WIN_A_ACT_REQ; + val |= GENERAL_UPDATE | WIN_A_UPDATE; + writel(val, &dc->cmd.state_ctrl); +} + +static void write_pair(struct fdt_disp_config *config, int item, u32 *reg) +{ + writel(config->horiz_timing[item] | + (config->vert_timing[item] << 16), reg); +} + +static int update_display_mode(struct dc_disp_reg *disp, + struct fdt_disp_config *config) +{ + unsigned long val; + unsigned long rate; + unsigned long div; + + writel(0x0, &disp->disp_timing_opt); + write_pair(config, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); + write_pair(config, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); + write_pair(config, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); + write_pair(config, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); + + writel(config->width | (config->height << 16), &disp->disp_active); + + val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; + val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; + writel(val, &disp->data_enable_opt); + + val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT; + val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT; + val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT; + writel(val, &disp->disp_interface_ctrl); + + /* + * The pixel clock divider is in 7.1 format (where the bottom bit + * represents 0.5). Here we calculate the divider needed to get from + * the display clock (typically 600MHz) to the pixel clock. We round + * up or down as requried. + */ + rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL); + div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2; + debug("Display clock %lu, divider %lu\n", rate, div); + + writel(0x00010001, &disp->shift_clk_opt); + + val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT; + val |= div << SHIFT_CLK_DIVIDER_SHIFT; + writel(val, &disp->disp_clk_ctrl); + + return 0; +} + +/* Start up the display and turn on power to PWMs */ +static void basic_init(struct dc_cmd_reg *cmd) +{ + u32 val; + + writel(0x00000100, &cmd->gen_incr_syncpt_ctrl); + writel(0x0000011a, &cmd->cont_syncpt_vsync); + writel(0x00000000, &cmd->int_type); + writel(0x00000000, &cmd->int_polarity); + writel(0x00000000, &cmd->int_mask); + writel(0x00000000, &cmd->int_enb); + + val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE; + val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE; + val |= PM1_ENABLE; + writel(val, &cmd->disp_pow_ctrl); + + val = readl(&cmd->disp_cmd); + val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT; + writel(val, &cmd->disp_cmd); +} + +static void basic_init_timer(struct dc_disp_reg *disp) +{ + writel(0x00000020, &disp->mem_high_pri); + writel(0x00000001, &disp->mem_high_pri_timer); +} + +static const u32 rgb_enb_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_polarity_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x01000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_data_tab[PIN_REG_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, +}; + +static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = { + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00210222, + 0x00002200, + 0x00020000, +}; + +static void rgb_enable(struct dc_com_reg *com) +{ + int i; + + for (i = 0; i < PIN_REG_COUNT; i++) { + writel(rgb_enb_tab[i], &com->pin_output_enb[i]); + writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]); + writel(rgb_data_tab[i], &com->pin_output_data[i]); + } + + for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++) + writel(rgb_sel_tab[i], &com->pin_output_sel[i]); +} + +static int setup_window(struct disp_ctl_win *win, + struct fdt_disp_config *config) +{ + win->x = 0; + win->y = 0; + win->w = config->width; + win->h = config->height; + win->out_x = 0; + win->out_y = 0; + win->out_w = config->width; + win->out_h = config->height; + win->phys_addr = config->frame_buffer; + win->stride = config->width * (1 << config->log2_bpp) / 8; + debug("%s: depth = %d\n", __func__, config->log2_bpp); + switch (config->log2_bpp) { + case 5: + case 24: + win->fmt = COLOR_DEPTH_R8G8B8A8; + win->bpp = 32; + break; + case 4: + win->fmt = COLOR_DEPTH_B5G6R5; + win->bpp = 16; + break; + + default: + debug("Unsupported LCD bit depth"); + return -1; + } + + return 0; +} + +struct fdt_disp_config *tegra_display_get_config(void) +{ + return config.valid ? &config : NULL; +} + +static void debug_timing(const char *name, unsigned int timing[]) +{ +#ifdef DEBUG + int i; + + debug("%s timing: ", name); + for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) + debug("%d ", timing[i]); + debug("\n"); +#endif +} + +/** + * Decode panel information from the fdt, according to a standard binding + * + * @param blob fdt blob + * @param node offset of fdt node to read from + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_decode_panel(const void *blob, int node, + struct fdt_disp_config *config) +{ + int front, back, ref; + + config->width = fdtdec_get_int(blob, node, "xres", -1); + config->height = fdtdec_get_int(blob, node, "yres", -1); + config->pixel_clock = fdtdec_get_int(blob, node, "clock", 0); + if (!config->pixel_clock || config->width == -1 || + config->height == -1) { + debug("%s: Pixel parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + back = fdtdec_get_int(blob, node, "left-margin", -1); + front = fdtdec_get_int(blob, node, "right-margin", -1); + ref = fdtdec_get_int(blob, node, "hsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Horizontal parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + /* Use a ref-to-sync of 1 always, and take this from the front porch */ + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("horiz", config->horiz_timing); + + back = fdtdec_get_int(blob, node, "upper-margin", -1); + front = fdtdec_get_int(blob, node, "lower-margin", -1); + ref = fdtdec_get_int(blob, node, "vsync-len", -1); + if ((back | front | ref) == -1) { + debug("%s: Vertical parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; + config->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; + config->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; + config->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - + config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; + debug_timing("vert", config->vert_timing); + + return 0; +} + +/** + * Decode the display controller information from the fdt. + * + * @param blob fdt blob + * @param config structure to store fdt config into + * @return 0 if ok, -ve on error + */ +static int tegra_display_decode_config(const void *blob, + struct fdt_disp_config *config) +{ + int node, rgb; + int bpp, bit; + + /* TODO: Support multiple controllers */ + node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_DC); + if (node < 0) { + debug("%s: Cannot find display controller node in fdt\n", + __func__); + return node; + } + config->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg"); + if (!config->disp) { + debug("%s: No display controller address\n", __func__); + return -1; + } + + rgb = fdt_subnode_offset(blob, node, "rgb"); + + config->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); + if (config->panel_node < 0) { + debug("%s: Cannot find panel information\n", __func__); + return -1; + } + + if (tegra_decode_panel(blob, config->panel_node, config)) { + debug("%s: Failed to decode panel information\n", __func__); + return -1; + } + + bpp = fdtdec_get_int(blob, config->panel_node, "nvidia,bits-per-pixel", + -1); + bit = ffs(bpp) - 1; + if (bpp == (1 << bit)) + config->log2_bpp = bit; + else + config->log2_bpp = bpp; + if (bpp == -1) { + debug("%s: Pixel bpp parameters missing\n", __func__); + return -FDT_ERR_NOTFOUND; + } + config->bpp = bpp; + + config->valid = 1; /* we have a valid configuration */ + + return 0; +} + +int tegra_display_probe(const void *blob, void *default_lcd_base) +{ + struct disp_ctl_win window; + struct dc_ctlr *dc; + + if (tegra_display_decode_config(blob, &config)) + return -1; + + config.frame_buffer = (u32)default_lcd_base; + + dc = (struct dc_ctlr *)config.disp; + + /* + * A header file for clock constants was NAKed upstream. + * TODO: Put this into the FDT and fdt_lcd struct when we have clock + * support there + */ + clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, + 144 * 1000000); + clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL, + 600 * 1000000); + basic_init(&dc->cmd); + basic_init_timer(&dc->disp); + rgb_enable(&dc->com); + + if (config.pixel_clock) + update_display_mode(&dc->disp, &config); + + if (setup_window(&window, &config)) + return -1; + + update_window(dc, &window); + + return 0; +} diff --git a/arch/arm/mach-tegra/tegra20/emc.c b/arch/arm/mach-tegra/tegra20/emc.c new file mode 100644 index 0000000..ed2462a --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/emc.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/arch-tegra/ap.h> +#include <asm/arch-tegra/apb_misc.h> +#include <asm/arch/clock.h> +#include <asm/arch/emc.h> +#include <asm/arch/tegra.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 = NULL; + 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/mach-tegra/tegra20/funcmux.c b/arch/arm/mach-tegra/tegra20/funcmux.c new file mode 100644 index 0000000..0df4a07 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/funcmux.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Tegra20 high-level function multiplexing */ +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch/pinmux.h> + +/* + * The PINMUX macro is used to set up pinmux tables. + */ +#define PINMUX(grp, mux, pupd, tri) \ + {PMUX_PINGRP_##grp, PMUX_FUNC_##mux, PMUX_PULL_##pupd, PMUX_TRI_##tri} + +static const struct pmux_pingrp_config disp1_default[] = { + PINMUX(LDI, DISPA, NORMAL, NORMAL), + PINMUX(LHP0, DISPA, NORMAL, NORMAL), + PINMUX(LHP1, DISPA, NORMAL, NORMAL), + PINMUX(LHP2, DISPA, NORMAL, NORMAL), + PINMUX(LHS, DISPA, NORMAL, NORMAL), + PINMUX(LM0, RSVD4, NORMAL, NORMAL), + PINMUX(LPP, DISPA, NORMAL, NORMAL), + PINMUX(LPW0, DISPA, NORMAL, NORMAL), + PINMUX(LPW2, DISPA, NORMAL, NORMAL), + PINMUX(LSC0, DISPA, NORMAL, NORMAL), + PINMUX(LSPI, DISPA, NORMAL, NORMAL), + PINMUX(LVP1, DISPA, NORMAL, NORMAL), + PINMUX(LVS, DISPA, NORMAL, NORMAL), + PINMUX(SLXD, SPDIF, NORMAL, NORMAL), +}; + + +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(PMUX_PINGRP_IRRX, PMUX_FUNC_UARTA); + pinmux_set_func(PMUX_PINGRP_IRTX, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PMUX_PINGRP_IRRX); + pinmux_tristate_disable(PMUX_PINGRP_IRTX); + break; + case FUNCMUX_UART1_UAA_UAB: + pinmux_set_func(PMUX_PINGRP_UAA, PMUX_FUNC_UARTA); + pinmux_set_func(PMUX_PINGRP_UAB, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PMUX_PINGRP_UAA); + pinmux_tristate_disable(PMUX_PINGRP_UAB); + bad_config = 0; + break; + case FUNCMUX_UART1_GPU: + pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PMUX_PINGRP_GPU); + bad_config = 0; + break; + case FUNCMUX_UART1_SDIO1: + pinmux_set_func(PMUX_PINGRP_SDIO1, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PMUX_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(PMUX_PINGRP_SDB); + pinmux_set_func(PMUX_PINGRP_SDB, PMUX_FUNC_SDIO3); + } + break; + + case PERIPH_ID_UART2: + if (config == FUNCMUX_UART2_UAD) { + pinmux_set_func(PMUX_PINGRP_UAD, PMUX_FUNC_UARTB); + pinmux_tristate_disable(PMUX_PINGRP_UAD); + } + break; + + case PERIPH_ID_UART4: + if (config == FUNCMUX_UART4_GMC) { + pinmux_set_func(PMUX_PINGRP_GMC, PMUX_FUNC_UARTD); + pinmux_tristate_disable(PMUX_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(PMUX_PINGRP_I2CP, PMUX_FUNC_I2C); + pinmux_tristate_disable(PMUX_PINGRP_I2CP); + } + break; + + case PERIPH_ID_I2C1: + /* support pinmux_config of 0 for now, */ + if (config == FUNCMUX_I2C1_RM) { + pinmux_set_func(PMUX_PINGRP_RM, PMUX_FUNC_I2C); + pinmux_tristate_disable(PMUX_PINGRP_RM); + } + break; + case PERIPH_ID_I2C2: /* I2C2 */ + switch (config) { + case FUNCMUX_I2C2_DDC: /* DDC pin group, select I2C2 */ + pinmux_set_func(PMUX_PINGRP_DDC, PMUX_FUNC_I2C2); + /* PTA to HDMI */ + pinmux_set_func(PMUX_PINGRP_PTA, PMUX_FUNC_HDMI); + pinmux_tristate_disable(PMUX_PINGRP_DDC); + break; + case FUNCMUX_I2C2_PTA: /* PTA pin group, select I2C2 */ + pinmux_set_func(PMUX_PINGRP_PTA, PMUX_FUNC_I2C2); + /* set DDC_SEL to RSVDx (RSVD2 works for now) */ + pinmux_set_func(PMUX_PINGRP_DDC, PMUX_FUNC_RSVD2); + pinmux_tristate_disable(PMUX_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(PMUX_PINGRP_DTF, PMUX_FUNC_I2C3); + pinmux_tristate_disable(PMUX_PINGRP_DTF); + } + break; + + case PERIPH_ID_SDMMC1: + if (config == FUNCMUX_SDMMC1_SDIO1_4BIT) { + pinmux_set_func(PMUX_PINGRP_SDIO1, PMUX_FUNC_SDIO1); + pinmux_tristate_disable(PMUX_PINGRP_SDIO1); + } + break; + + case PERIPH_ID_SDMMC2: + if (config == FUNCMUX_SDMMC2_DTA_DTD_8BIT) { + pinmux_set_func(PMUX_PINGRP_DTA, PMUX_FUNC_SDIO2); + pinmux_set_func(PMUX_PINGRP_DTD, PMUX_FUNC_SDIO2); + + pinmux_tristate_disable(PMUX_PINGRP_DTA); + pinmux_tristate_disable(PMUX_PINGRP_DTD); + } + break; + + case PERIPH_ID_SDMMC3: + switch (config) { + case FUNCMUX_SDMMC3_SDB_SLXA_8BIT: + pinmux_set_func(PMUX_PINGRP_SLXA, PMUX_FUNC_SDIO3); + pinmux_set_func(PMUX_PINGRP_SLXC, PMUX_FUNC_SDIO3); + pinmux_set_func(PMUX_PINGRP_SLXD, PMUX_FUNC_SDIO3); + pinmux_set_func(PMUX_PINGRP_SLXK, PMUX_FUNC_SDIO3); + + pinmux_tristate_disable(PMUX_PINGRP_SLXA); + pinmux_tristate_disable(PMUX_PINGRP_SLXC); + pinmux_tristate_disable(PMUX_PINGRP_SLXD); + pinmux_tristate_disable(PMUX_PINGRP_SLXK); + /* fall through */ + + case FUNCMUX_SDMMC3_SDB_4BIT: + pinmux_set_func(PMUX_PINGRP_SDB, PMUX_FUNC_SDIO3); + pinmux_set_func(PMUX_PINGRP_SDC, PMUX_FUNC_SDIO3); + pinmux_set_func(PMUX_PINGRP_SDD, PMUX_FUNC_SDIO3); + + pinmux_tristate_disable(PMUX_PINGRP_SDB); + pinmux_tristate_disable(PMUX_PINGRP_SDC); + pinmux_tristate_disable(PMUX_PINGRP_SDD); + bad_config = 0; + break; + } + break; + + case PERIPH_ID_SDMMC4: + switch (config) { + case FUNCMUX_SDMMC4_ATC_ATD_8BIT: + pinmux_set_func(PMUX_PINGRP_ATC, PMUX_FUNC_SDIO4); + pinmux_set_func(PMUX_PINGRP_ATD, PMUX_FUNC_SDIO4); + + pinmux_tristate_disable(PMUX_PINGRP_ATC); + pinmux_tristate_disable(PMUX_PINGRP_ATD); + break; + + case FUNCMUX_SDMMC4_ATB_GMA_GME_8_BIT: + pinmux_set_func(PMUX_PINGRP_GME, PMUX_FUNC_SDIO4); + pinmux_tristate_disable(PMUX_PINGRP_GME); + /* fall through */ + + case FUNCMUX_SDMMC4_ATB_GMA_4_BIT: + pinmux_set_func(PMUX_PINGRP_ATB, PMUX_FUNC_SDIO4); + pinmux_set_func(PMUX_PINGRP_GMA, PMUX_FUNC_SDIO4); + + pinmux_tristate_disable(PMUX_PINGRP_ATB); + pinmux_tristate_disable(PMUX_PINGRP_GMA); + bad_config = 0; + break; + } + break; + + case PERIPH_ID_KBC: + if (config == FUNCMUX_DEFAULT) { + enum pmux_pingrp grp[] = {PMUX_PINGRP_KBCA, + PMUX_PINGRP_KBCB, PMUX_PINGRP_KBCC, + PMUX_PINGRP_KBCD, PMUX_PINGRP_KBCE, + PMUX_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(PMUX_PINGRP_UAA, PMUX_FUNC_ULPI); + pinmux_set_func(PMUX_PINGRP_UAB, PMUX_FUNC_ULPI); + pinmux_set_func(PMUX_PINGRP_UDA, PMUX_FUNC_ULPI); + + pinmux_tristate_disable(PMUX_PINGRP_UAA); + pinmux_tristate_disable(PMUX_PINGRP_UAB); + pinmux_tristate_disable(PMUX_PINGRP_UDA); + } + break; + + case PERIPH_ID_SPI1: + if (config == FUNCMUX_SPI1_GMC_GMD) { + pinmux_set_func(PMUX_PINGRP_GMC, PMUX_FUNC_SFLASH); + pinmux_set_func(PMUX_PINGRP_GMD, PMUX_FUNC_SFLASH); + + pinmux_tristate_disable(PMUX_PINGRP_GMC); + pinmux_tristate_disable(PMUX_PINGRP_GMD); + } + break; + + case PERIPH_ID_NDFLASH: + switch (config) { + case FUNCMUX_NDFLASH_ATC: + pinmux_set_func(PMUX_PINGRP_ATC, PMUX_FUNC_NAND); + pinmux_tristate_disable(PMUX_PINGRP_ATC); + break; + case FUNCMUX_NDFLASH_KBC_8_BIT: + pinmux_set_func(PMUX_PINGRP_KBCA, PMUX_FUNC_NAND); + pinmux_set_func(PMUX_PINGRP_KBCC, PMUX_FUNC_NAND); + pinmux_set_func(PMUX_PINGRP_KBCD, PMUX_FUNC_NAND); + pinmux_set_func(PMUX_PINGRP_KBCE, PMUX_FUNC_NAND); + pinmux_set_func(PMUX_PINGRP_KBCF, PMUX_FUNC_NAND); + + pinmux_tristate_disable(PMUX_PINGRP_KBCA); + pinmux_tristate_disable(PMUX_PINGRP_KBCC); + pinmux_tristate_disable(PMUX_PINGRP_KBCD); + pinmux_tristate_disable(PMUX_PINGRP_KBCE); + pinmux_tristate_disable(PMUX_PINGRP_KBCF); + + bad_config = 0; + break; + } + break; + case PERIPH_ID_DISP1: + if (config == FUNCMUX_DEFAULT) { + int i; + + for (i = PMUX_PINGRP_LD0; i <= PMUX_PINGRP_LD17; i++) { + pinmux_set_func(i, PMUX_FUNC_DISPA); + pinmux_tristate_disable(i); + pinmux_set_pullupdown(i, PMUX_PULL_NORMAL); + } + pinmux_config_pingrp_table(disp1_default, + ARRAY_SIZE(disp1_default)); + } + 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/mach-tegra/tegra20/pinmux.c b/arch/arm/mach-tegra/tegra20/pinmux.c new file mode 100644 index 0000000..e484f99 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/pinmux.c @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* Tegra20 pin multiplexing functions */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/pinmux.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 +}; + +/* Convenient macro for defining pin group properties */ +#define PINALL(pingrp, f0, f1, f2, f3, mux, pupd) \ + { \ + .funcs = { \ + PMUX_FUNC_ ## f0, \ + PMUX_FUNC_ ## f1, \ + PMUX_FUNC_ ## f2, \ + PMUX_FUNC_ ## f3, \ + }, \ + .ctl_id = mux, \ + .pull_id = pupd \ + } + +/* A normal pin group where the mux name and pull-up name match */ +#define PIN(pingrp, f0, f1, f2, f3) \ + PINALL(pingrp, f0, f1, f2, f3, MUXCTL_##pingrp, PUCTL_##pingrp) + +/* A pin group where the pull-up name doesn't have a 1-1 mapping */ +#define PINP(pingrp, f0, f1, f2, f3, pupd) \ + PINALL(pingrp, f0, f1, f2, f3, MUXCTL_##pingrp, PUCTL_##pupd) + +/* A pin group number which is not used */ +#define PIN_RESERVED \ + PIN(NONE, RSVD1, RSVD2, RSVD3, RSVD4) + +#define DRVGRP(drvgrp) \ + PINALL(drvgrp, RSVD1, RSVD2, RSVD3, RSVD4, MUXCTL_NONE, PUCTL_NONE) + +static const struct pmux_pingrp_desc tegra20_pingroups[] = { + PIN(ATA, IDE, NAND, GMI, RSVD4), + PIN(ATB, IDE, NAND, GMI, SDIO4), + PIN(ATC, IDE, NAND, GMI, SDIO4), + PIN(ATD, IDE, NAND, GMI, SDIO4), + PIN(CDEV1, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC), + PIN(CDEV2, OSC, AHB_CLK, APB_CLK, PLLP_OUT4), + PIN(CSUS, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK), + PIN(DAP1, DAP1, RSVD2, GMI, SDIO2), + + PIN(DAP2, DAP2, TWC, RSVD3, GMI), + PIN(DAP3, DAP3, RSVD2, RSVD3, RSVD4), + PIN(DAP4, DAP4, RSVD2, GMI, RSVD4), + PIN(DTA, RSVD1, SDIO2, VI, RSVD4), + PIN(DTB, RSVD1, RSVD2, VI, SPI1), + PIN(DTC, RSVD1, RSVD2, VI, RSVD4), + PIN(DTD, RSVD1, SDIO2, VI, RSVD4), + PIN(DTE, RSVD1, RSVD2, VI, SPI1), + + PINP(GPU, PWM, UARTA, GMI, RSVD4, GPSLXAU), + PIN(GPV, PCIE, RSVD2, RSVD3, RSVD4), + PIN(I2CP, I2C, RSVD2, RSVD3, RSVD4), + PIN(IRTX, UARTA, UARTB, GMI, SPI4), + PIN(IRRX, UARTA, UARTB, GMI, SPI4), + PIN(KBCB, KBC, NAND, SDIO2, MIO), + PIN(KBCA, KBC, NAND, SDIO2, EMC_TEST0_DLL), + PINP(PMC, PWR_ON, PWR_INTR, RSVD3, RSVD4, NONE), + + PIN(PTA, I2C2, HDMI, GMI, RSVD4), + PIN(RM, I2C, RSVD2, RSVD3, RSVD4), + PIN(KBCE, KBC, NAND, OWR, RSVD4), + PIN(KBCF, KBC, NAND, TRACE, MIO), + PIN(GMA, UARTE, SPI3, GMI, SDIO4), + PIN(GMC, UARTD, SPI4, GMI, SFLASH), + PIN(SDMMC1, SDIO1, RSVD2, UARTE, UARTA), + PIN(OWC, OWR, RSVD2, RSVD3, RSVD4), + + PIN(GME, RSVD1, DAP5, GMI, SDIO4), + PIN(SDC, PWM, TWC, SDIO3, SPI3), + PIN(SDD, UARTA, PWM, SDIO3, SPI3), + PIN_RESERVED, + PINP(SLXA, PCIE, SPI4, SDIO3, SPI2, CRTP), + PIN(SLXC, SPDIF, SPI4, SDIO3, SPI2), + PIN(SLXD, SPDIF, SPI4, SDIO3, SPI2), + PIN(SLXK, PCIE, SPI4, SDIO3, SPI2), + + PIN(SPDI, SPDIF, RSVD2, I2C, SDIO2), + PIN(SPDO, SPDIF, RSVD2, I2C, SDIO2), + PIN(SPIA, SPI1, SPI2, SPI3, GMI), + PIN(SPIB, SPI1, SPI2, SPI3, GMI), + PIN(SPIC, SPI1, SPI2, SPI3, GMI), + PIN(SPID, SPI2, SPI1, SPI2_ALT, GMI), + PIN(SPIE, SPI2, SPI1, SPI2_ALT, GMI), + PIN(SPIF, SPI3, SPI1, SPI2, RSVD4), + + PIN(SPIG, SPI3, SPI2, SPI2_ALT, I2C), + PIN(SPIH, SPI3, SPI2, SPI2_ALT, I2C), + PIN(UAA, SPI3, MIPI_HS, UARTA, ULPI), + PIN(UAB, SPI2, MIPI_HS, UARTA, ULPI), + PIN(UAC, OWR, RSVD2, RSVD3, RSVD4), + PIN(UAD, UARTB, SPDIF, UARTA, SPI4), + PIN(UCA, UARTC, RSVD2, GMI, RSVD4), + PIN(UCB, UARTC, PWM, GMI, RSVD4), + + PIN_RESERVED, + PIN(ATE, IDE, NAND, GMI, RSVD4), + PIN(KBCC, KBC, NAND, TRACE, EMC_TEST1_DLL), + PIN_RESERVED, + PIN_RESERVED, + PIN(GMB, IDE, NAND, GMI, GMI_INT), + PIN(GMD, RSVD1, NAND, GMI, SFLASH), + PIN(DDC, I2C2, RSVD2, RSVD3, RSVD4), + + /* 64 */ + PINP(LD0, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD1, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD2, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD3, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD4, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD5, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD6, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD7, DISPA, DISPB, XIO, RSVD4, LD17), + + PINP(LD8, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD9, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD10, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD11, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD12, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD13, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD14, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD15, DISPA, DISPB, XIO, RSVD4, LD17), + + PINP(LD16, DISPA, DISPB, XIO, RSVD4, LD17), + PINP(LD17, DISPA, DISPB, RSVD3, RSVD4, LD17), + PINP(LHP0, DISPA, DISPB, RSVD3, RSVD4, LD21_20), + PINP(LHP1, DISPA, DISPB, RSVD3, RSVD4, LD19_18), + PINP(LHP2, DISPA, DISPB, RSVD3, RSVD4, LD19_18), + PINP(LVP0, DISPA, DISPB, RSVD3, RSVD4, LC), + PINP(LVP1, DISPA, DISPB, RSVD3, RSVD4, LD21_20), + PINP(HDINT, HDMI, RSVD2, RSVD3, RSVD4, LC), + + PINP(LM0, DISPA, DISPB, SPI3, RSVD4, LC), + PINP(LM1, DISPA, DISPB, RSVD3, CRT, LC), + PINP(LVS, DISPA, DISPB, XIO, RSVD4, LC), + PINP(LSC0, DISPA, DISPB, XIO, RSVD4, LC), + PINP(LSC1, DISPA, DISPB, SPI3, HDMI, LS), + PINP(LSCK, DISPA, DISPB, SPI3, HDMI, LS), + PINP(LDC, DISPA, DISPB, RSVD3, RSVD4, LS), + PINP(LCSN, DISPA, DISPB, SPI3, RSVD4, LS), + + /* 96 */ + PINP(LSPI, DISPA, DISPB, XIO, HDMI, LC), + PINP(LSDA, DISPA, DISPB, SPI3, HDMI, LS), + PINP(LSDI, DISPA, DISPB, SPI3, RSVD4, LS), + PINP(LPW0, DISPA, DISPB, SPI3, HDMI, LS), + PINP(LPW1, DISPA, DISPB, RSVD3, RSVD4, LS), + PINP(LPW2, DISPA, DISPB, SPI3, HDMI, LS), + PINP(LDI, DISPA, DISPB, RSVD3, RSVD4, LD23_22), + PINP(LHS, DISPA, DISPB, XIO, RSVD4, LC), + + PINP(LPP, DISPA, DISPB, RSVD3, RSVD4, LD23_22), + PIN_RESERVED, + PIN(KBCD, KBC, NAND, SDIO2, MIO), + PIN(GPU7, RTCK, RSVD2, RSVD3, RSVD4), + PIN(DTF, I2C3, RSVD2, VI, RSVD4), + PIN(UDA, SPI1, RSVD2, UARTD, ULPI), + PIN(CRTP, CRT, RSVD2, RSVD3, RSVD4), + PINP(SDB, UARTA, PWM, SDIO3, SPI2, NONE), + + /* these pin groups only have pullup and pull down control */ + DRVGRP(CK32), + DRVGRP(DDRC), + DRVGRP(PMCA), + DRVGRP(PMCB), + DRVGRP(PMCC), + DRVGRP(PMCD), + DRVGRP(PMCE), + DRVGRP(XM2C), + DRVGRP(XM2D), +}; +const struct pmux_pingrp_desc *tegra_soc_pingroups = tegra20_pingroups; diff --git a/arch/arm/mach-tegra/tegra20/pmu.c b/arch/arm/mach-tegra/tegra20/pmu.c new file mode 100644 index 0000000..a774246 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/pmu.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <tps6586x.h> +#include <asm/io.h> +#include <asm/arch/tegra.h> +#include <asm/arch-tegra/ap.h> +#include <asm/arch-tegra/tegra_i2c.h> +#include <asm/arch-tegra/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 */ + +#define PMI_I2C_ADDRESS 0x34 /* chip requires this address */ + +int pmu_set_nominal(void) +{ + struct udevice *bus, *dev; + int core, cpu; + int ret; + + /* by default, the table has been filled with T25 settings */ + switch (tegra_get_chip_sku()) { + 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 SKU id\n", __func__); + return -1; + } + + ret = tegra_i2c_get_dvc_bus(&bus); + if (ret) { + debug("%s: Cannot find DVC I2C bus\n", __func__); + return ret; + } + ret = i2c_get_chip(bus, PMI_I2C_ADDRESS, 1, &dev); + if (ret) { + debug("%s: Cannot find DVC I2C chip\n", __func__); + return ret; + } + + tps6586x_init(dev); + 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/mach-tegra/tegra20/pwm.c b/arch/arm/mach-tegra/tegra20/pwm.c new file mode 100644 index 0000000..5b88636 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/pwm.c @@ -0,0 +1,86 @@ +/* + * Tegra2 pulse width frequency modulator definitions + * + * Copyright (c) 2011 The Chromium OS Authors. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/pwm.h> + +struct pwm_info { + struct pwm_ctlr *pwm; /* Registers for our pwm controller */ + int pwm_node; /* PWM device tree node */ +} local; + +void pwm_enable(unsigned channel, int rate, int pulse_width, int freq_divider) +{ + u32 reg; + + assert(channel < PWM_NUM_CHANNELS); + + /* TODO: Can we use clock_adjust_periph_pll_div() here? */ + clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ, rate); + + reg = PWM_ENABLE_MASK; + reg |= pulse_width << PWM_WIDTH_SHIFT; + reg |= freq_divider << PWM_DIVIDER_SHIFT; + writel(reg, &local.pwm[channel].control); + debug("%s: channel=%d, rate=%d\n", __func__, channel, rate); +} + +int pwm_request(const void *blob, int node, const char *prop_name) +{ + int pwm_node; + u32 data[3]; + + if (fdtdec_get_int_array(blob, node, prop_name, data, + ARRAY_SIZE(data))) { + debug("%s: Cannot decode PWM property '%s'\n", __func__, + prop_name); + return -1; + } + + pwm_node = fdt_node_offset_by_phandle(blob, data[0]); + if (pwm_node != local.pwm_node) { + debug("%s: PWM property '%s' phandle %d not recognised" + "- expecting %d\n", __func__, prop_name, data[0], + local.pwm_node); + return -1; + } + if (data[1] >= PWM_NUM_CHANNELS) { + debug("%s: PWM property '%s': invalid channel %u\n", __func__, + prop_name, data[1]); + return -1; + } + + /* + * TODO: We could maintain a list of requests, but it might not be + * worth it for U-Boot. + */ + return data[1]; +} + +int pwm_init(const void *blob) +{ + local.pwm_node = fdtdec_next_compatible(blob, 0, + COMPAT_NVIDIA_TEGRA20_PWM); + if (local.pwm_node < 0) { + debug("%s: Cannot find device tree node\n", __func__); + return -1; + } + + local.pwm = (struct pwm_ctlr *)fdtdec_get_addr(blob, local.pwm_node, + "reg"); + if (local.pwm == (struct pwm_ctlr *)FDT_ADDR_T_NONE) { + debug("%s: Cannot find pwm reg address\n", __func__); + return -1; + } + debug("Tegra PWM at %p, node %d\n", local.pwm, local.pwm_node); + + return 0; +} diff --git a/arch/arm/mach-tegra/tegra20/warmboot.c b/arch/arm/mach-tegra/tegra20/warmboot.c new file mode 100644 index 0000000..5fdc4bb --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/warmboot.c @@ -0,0 +1,372 @@ +/* + * (C) Copyright 2010 - 2011 + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/clock.h> +#include <asm/arch/emc.h> +#include <asm/arch/gp_padctrl.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/sdram_param.h> +#include <asm/arch/tegra.h> +#include <asm/arch-tegra/ap.h> +#include <asm/arch-tegra/apb_misc.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra/pmc.h> +#include <asm/arch-tegra/fuse.h> +#include <asm/arch-tegra/warmboot.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 (NV_PA_BASE_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 apb_misc_pp_ctlr *apb_misc = + (struct apb_misc_pp_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(&apb_misc->strapping_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 = NV_WB_RUN_ADDRESS; + dst_header->entry_point = NV_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/mach-tegra/tegra20/warmboot_avp.c b/arch/arm/mach-tegra/tegra20/warmboot_avp.c new file mode 100644 index 0000000..27ce5f4 --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/warmboot_avp.c @@ -0,0 +1,236 @@ +/* + * (C) Copyright 2010 - 2011 + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/flow.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/tegra.h> +#include <asm/arch-tegra/ap.h> +#include <asm/arch-tegra/apb_misc.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra/pmc.h> +#include <asm/arch-tegra/warmboot.h> +#include "warmboot_avp.h" + +#define DEBUG_RESET_CORESIGHT + +void wb_start(void) +{ + struct apb_misc_pp_ctlr *apb_misc = + (struct apb_misc_pp_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, &apb_misc->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 != NV_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[0]); + + 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/mach-tegra/tegra20/warmboot_avp.h b/arch/arm/mach-tegra/tegra20/warmboot_avp.h new file mode 100644 index 0000000..7b86acb --- /dev/null +++ b/arch/arm/mach-tegra/tegra20/warmboot_avp.h @@ -0,0 +1,65 @@ +/* + * (C) Copyright 2010, 2011 + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#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 |