summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r--arch/arm/mach-tegra/Makefile8
-rw-r--r--arch/arm/mach-tegra/ap.c55
-rw-r--r--arch/arm/mach-tegra/board.c8
-rw-r--r--arch/arm/mach-tegra/board2.c273
-rw-r--r--arch/arm/mach-tegra/clock.c83
-rw-r--r--arch/arm/mach-tegra/emc.c38
-rw-r--r--arch/arm/mach-tegra/emc.h13
-rw-r--r--arch/arm/mach-tegra/powergate.c2
-rw-r--r--arch/arm/mach-tegra/psci.S114
-rw-r--r--arch/arm/mach-tegra/pwm.c (renamed from arch/arm/mach-tegra/tegra20/pwm.c)7
-rw-r--r--arch/arm/mach-tegra/tegra124/Kconfig2
-rw-r--r--arch/arm/mach-tegra/tegra124/Makefile4
-rw-r--r--arch/arm/mach-tegra/tegra124/clock.c141
-rw-r--r--arch/arm/mach-tegra/tegra124/psci.c59
-rw-r--r--arch/arm/mach-tegra/tegra20/Makefile1
-rw-r--r--arch/arm/mach-tegra/tegra20/display.c2
16 files changed, 765 insertions, 45 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 04cef0a..fefc180 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -12,10 +12,11 @@ obj-y += spl.o
obj-y += cpu.o
else
obj-$(CONFIG_CMD_ENTERRCM) += cmd_enterrcm.o
+obj-$(CONFIG_PWM_TEGRA) += pwm.o
endif
obj-y += ap.o
-obj-y += board.o
+obj-y += board.o board2.o
obj-y += cache.o
obj-y += clock.o
obj-y += lowlevel_init.o
@@ -24,6 +25,11 @@ obj-y += powergate.o
obj-y += xusb-padctl.o
obj-$(CONFIG_DISPLAY_CPUINFO) += sys_info.o
obj-$(CONFIG_TEGRA124) += vpr.o
+obj-$(CONFIG_TEGRA_CLOCK_SCALING) += emc.o
+
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_ARMV7_PSCI) += psci.o
+endif
obj-$(CONFIG_TEGRA20) += tegra20/
obj-$(CONFIG_TEGRA30) += tegra30/
diff --git a/arch/arm/mach-tegra/ap.c b/arch/arm/mach-tegra/ap.c
index a17dfd1..0b94e8a 100644
--- a/arch/arm/mach-tegra/ap.c
+++ b/arch/arm/mach-tegra/ap.c
@@ -10,6 +10,7 @@
#include <common.h>
#include <asm/io.h>
#include <asm/arch/gp_padctrl.h>
+#include <asm/arch/mc.h>
#include <asm/arch-tegra/ap.h>
#include <asm/arch-tegra/clock.h>
#include <asm/arch-tegra/fuse.h>
@@ -154,6 +155,57 @@ static void init_pmc_scratch(void)
writel(odmdata, &pmc->pmc_scratch20);
}
+#ifdef CONFIG_ARMV7_SECURE_RESERVE_SIZE
+void protect_secure_section(void)
+{
+ struct mc_ctlr *mc = (struct mc_ctlr *)NV_PA_MC_BASE;
+
+ /* Must be MB aligned */
+ BUILD_BUG_ON(CONFIG_ARMV7_SECURE_BASE & 0xFFFFF);
+ BUILD_BUG_ON(CONFIG_ARMV7_SECURE_RESERVE_SIZE & 0xFFFFF);
+
+ writel(CONFIG_ARMV7_SECURE_BASE, &mc->mc_security_cfg0);
+ writel(CONFIG_ARMV7_SECURE_RESERVE_SIZE >> 20, &mc->mc_security_cfg1);
+}
+#endif
+
+#if defined(CONFIG_ARMV7_NONSEC)
+static void smmu_flush(struct mc_ctlr *mc)
+{
+ (void)readl(&mc->mc_smmu_config);
+}
+
+static void smmu_enable(void)
+{
+ struct mc_ctlr *mc = (struct mc_ctlr *)NV_PA_MC_BASE;
+ u32 value;
+
+ /*
+ * Enable translation for all clients since access to this register
+ * is restricted to TrustZone-secured requestors. The kernel will use
+ * the per-SWGROUP enable bits to enable or disable translations.
+ */
+ writel(0xffffffff, &mc->mc_smmu_translation_enable_0);
+ writel(0xffffffff, &mc->mc_smmu_translation_enable_1);
+ writel(0xffffffff, &mc->mc_smmu_translation_enable_2);
+ writel(0xffffffff, &mc->mc_smmu_translation_enable_3);
+
+ /*
+ * Enable SMMU globally since access to this register is restricted
+ * to TrustZone-secured requestors.
+ */
+ value = readl(&mc->mc_smmu_config);
+ value |= TEGRA_MC_SMMU_CONFIG_ENABLE;
+ writel(value, &mc->mc_smmu_config);
+
+ smmu_flush(mc);
+}
+#else
+static void smmu_enable(void)
+{
+}
+#endif
+
void s_init(void)
{
/* Init PMC scratch memory */
@@ -164,6 +216,9 @@ void s_init(void)
/* init the cache */
config_cache();
+ /* enable SMMU */
+ smmu_enable();
+
/* init vpr */
config_vpr();
}
diff --git a/arch/arm/mach-tegra/board.c b/arch/arm/mach-tegra/board.c
index 0ebaf19..222de6a 100644
--- a/arch/arm/mach-tegra/board.c
+++ b/arch/arm/mach-tegra/board.c
@@ -98,14 +98,6 @@ int dram_init(void)
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_TEGRA20)
#if defined(CONFIG_TEGRA_UARTA_UAA_UAB)
diff --git a/arch/arm/mach-tegra/board2.c b/arch/arm/mach-tegra/board2.c
new file mode 100644
index 0000000..131802a
--- /dev/null
+++ b/arch/arm/mach-tegra/board2.c
@@ -0,0 +1,273 @@
+/*
+ * (C) Copyright 2010,2011
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <ns16550.h>
+#include <linux/compiler.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#ifdef CONFIG_LCD
+#include <asm/arch/display.h>
+#endif
+#include <asm/arch/funcmux.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/pmu.h>
+#ifdef CONFIG_PWM_TEGRA
+#include <asm/arch/pwm.h>
+#endif
+#include <asm/arch/tegra.h>
+#include <asm/arch-tegra/ap.h>
+#include <asm/arch-tegra/board.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch-tegra/pmc.h>
+#include <asm/arch-tegra/sys_proto.h>
+#include <asm/arch-tegra/uart.h>
+#include <asm/arch-tegra/warmboot.h>
+#ifdef CONFIG_TEGRA_CLOCK_SCALING
+#include <asm/arch/emc.h>
+#endif
+#ifdef CONFIG_USB_EHCI_TEGRA
+#include <asm/arch-tegra/usb.h>
+#include <usb.h>
+#endif
+#ifdef CONFIG_TEGRA_MMC
+#include <asm/arch-tegra/tegra_mmc.h>
+#include <asm/arch-tegra/mmc.h>
+#endif
+#include <asm/arch-tegra/xusb-padctl.h>
+#include <power/as3722.h>
+#include <i2c.h>
+#include <spi.h>
+#include "emc.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_SPL_BUILD
+/* TODO(sjg@chromium.org): Remove once SPL supports device tree */
+U_BOOT_DEVICE(tegra_gpios) = {
+ "gpio_tegra"
+};
+#endif
+
+__weak void pinmux_init(void) {}
+__weak void pin_mux_usb(void) {}
+__weak void pin_mux_spi(void) {}
+__weak void gpio_early_init_uart(void) {}
+__weak void pin_mux_display(void) {}
+
+#if defined(CONFIG_TEGRA_NAND)
+__weak void pin_mux_nand(void)
+{
+ funcmux_select(PERIPH_ID_NDFLASH, FUNCMUX_DEFAULT);
+}
+#endif
+
+/*
+ * Routine: power_det_init
+ * Description: turn off power detects
+ */
+static void power_det_init(void)
+{
+#if defined(CONFIG_TEGRA20)
+ struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE;
+
+ /* turn off power detects */
+ writel(0, &pmc->pmc_pwr_det_latch);
+ writel(0, &pmc->pmc_pwr_det);
+#endif
+}
+
+__weak int tegra_board_id(void)
+{
+ return -1;
+}
+
+#ifdef CONFIG_DISPLAY_BOARDINFO
+int checkboard(void)
+{
+ int board_id = tegra_board_id();
+
+ printf("Board: %s", CONFIG_TEGRA_BOARD_STRING);
+ if (board_id != -1)
+ printf(", ID: %d\n", board_id);
+ printf("\n");
+
+ return 0;
+}
+#endif /* CONFIG_DISPLAY_BOARDINFO */
+
+__weak int tegra_lcd_pmic_init(int board_it)
+{
+ return 0;
+}
+
+/*
+ * Routine: board_init
+ * Description: Early hardware init.
+ */
+int board_init(void)
+{
+ __maybe_unused int err;
+ __maybe_unused int board_id;
+
+ /* Do clocks and UART first so that printf() works */
+ clock_init();
+ clock_verify();
+
+#ifdef CONFIG_TEGRA_SPI
+ pin_mux_spi();
+#endif
+
+#ifdef CONFIG_PWM_TEGRA
+ if (pwm_init(gd->fdt_blob))
+ debug("%s: Failed to init pwm\n", __func__);
+#endif
+#ifdef CONFIG_LCD
+ pin_mux_display();
+ tegra_lcd_check_next_stage(gd->fdt_blob, 0);
+#endif
+ /* boot param addr */
+ gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100);
+
+ power_det_init();
+
+#ifdef CONFIG_SYS_I2C_TEGRA
+# ifdef CONFIG_TEGRA_PMU
+ if (pmu_set_nominal())
+ debug("Failed to select nominal voltages\n");
+# ifdef CONFIG_TEGRA_CLOCK_SCALING
+ err = board_emc_init();
+ if (err)
+ debug("Memory controller init failed: %d\n", err);
+# endif
+# endif /* CONFIG_TEGRA_PMU */
+#ifdef CONFIG_AS3722_POWER
+ err = as3722_init(NULL);
+ if (err && err != -ENODEV)
+ return err;
+#endif
+#endif /* CONFIG_SYS_I2C_TEGRA */
+
+#ifdef CONFIG_USB_EHCI_TEGRA
+ pin_mux_usb();
+ usb_process_devicetree(gd->fdt_blob);
+#endif
+
+#ifdef CONFIG_LCD
+ board_id = tegra_board_id();
+ err = tegra_lcd_pmic_init(board_id);
+ if (err)
+ return err;
+ tegra_lcd_check_next_stage(gd->fdt_blob, 0);
+#endif
+
+#ifdef CONFIG_TEGRA_NAND
+ pin_mux_nand();
+#endif
+
+ tegra_xusb_padctl_init(gd->fdt_blob);
+
+#ifdef CONFIG_TEGRA_LP0
+ /* save Sdram params to PMC 2, 4, and 24 for WB0 */
+ warmboot_save_sdram_params();
+
+ /* prepare the WB code to LP0 location */
+ warmboot_prepare_code(TEGRA_LP0_ADDR, TEGRA_LP0_SIZE);
+#endif
+
+ return 0;
+}
+
+#ifdef CONFIG_BOARD_EARLY_INIT_F
+static void __gpio_early_init(void)
+{
+}
+
+void gpio_early_init(void) __attribute__((weak, alias("__gpio_early_init")));
+
+int board_early_init_f(void)
+{
+ pinmux_init();
+ board_init_uart_f();
+
+ /* Initialize periph GPIOs */
+ gpio_early_init();
+ gpio_early_init_uart();
+#ifdef CONFIG_LCD
+ tegra_lcd_early_init(gd->fdt_blob);
+#endif
+
+ return 0;
+}
+#endif /* EARLY_INIT */
+
+int board_late_init(void)
+{
+#ifdef CONFIG_LCD
+ /* Make sure we finish initing the LCD */
+ tegra_lcd_check_next_stage(gd->fdt_blob, 1);
+#endif
+#if defined(CONFIG_TEGRA_SUPPORT_NON_SECURE)
+ if (tegra_cpu_is_non_secure()) {
+ printf("CPU is in NS mode\n");
+ setenv("cpu_ns_mode", "1");
+ } else {
+ setenv("cpu_ns_mode", "");
+ }
+#endif
+ return 0;
+}
+
+#if defined(CONFIG_TEGRA_MMC)
+__weak void pin_mux_mmc(void)
+{
+}
+
+/* this is a weak define that we are overriding */
+int board_mmc_init(bd_t *bd)
+{
+ debug("%s called\n", __func__);
+
+ /* Enable muxes, etc. for SDMMC controllers */
+ pin_mux_mmc();
+
+ debug("%s: init MMC\n", __func__);
+ tegra_mmc_init();
+
+ return 0;
+}
+
+void pad_init_mmc(struct mmc_host *host)
+{
+#if defined(CONFIG_TEGRA30)
+ enum periph_id id = host->mmc_id;
+ u32 val;
+
+ debug("%s: sdmmc address = %08x, id = %d\n", __func__,
+ (unsigned int)host->reg, id);
+
+ /* Set the pad drive strength for SDMMC1 or 3 only */
+ if (id != PERIPH_ID_SDMMC1 && id != PERIPH_ID_SDMMC3) {
+ debug("%s: settings are only valid for SDMMC1/SDMMC3!\n",
+ __func__);
+ return;
+ }
+
+ val = readl(&host->reg->sdmemcmppadctl);
+ val &= 0xFFFFFFF0;
+ val |= MEMCOMP_PADCTRL_VREF;
+ writel(val, &host->reg->sdmemcmppadctl);
+
+ val = readl(&host->reg->autocalcfg);
+ val &= 0xFFFF0000;
+ val |= AUTO_CAL_PU_OFFSET | AUTO_CAL_PD_OFFSET | AUTO_CAL_ENABLED;
+ writel(val, &host->reg->autocalcfg);
+#endif /* T30 */
+}
+#endif /* MMC */
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 7c274b5..cdd5438 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -81,9 +81,18 @@ static struct clk_pll *get_pll(enum clock_id clkid)
(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
assert(clock_id_is_pll(clkid));
+ if (clkid >= (enum clock_id)TEGRA_CLK_PLLS) {
+ debug("%s: Invalid PLL\n", __func__);
+ return NULL;
+ }
return &clkrst->crc_pll[clkid];
}
+__weak struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid)
+{
+ return NULL;
+}
+
int clock_ll_read_pll(enum clock_id clkid, u32 *divm, u32 *divn,
u32 *divp, u32 *cpcon, u32 *lfcon)
{
@@ -110,7 +119,7 @@ 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;
+ u32 misc_data, data;
/*
* We cheat by treating all PLL (except PLLU) in the same fashion.
@@ -119,8 +128,7 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn,
* - 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);
+ misc_data = (cpcon << PLL_CPCON_SHIFT) | (lfcon << PLL_LFCON_SHIFT);
data = (divm << PLL_DIVM_SHIFT) | (divn << PLL_DIVN_SHIFT) |
(0 << PLL_BYPASS_SHIFT) | (1 << PLL_ENABLE_SHIFT);
@@ -129,7 +137,19 @@ unsigned long clock_start_pll(enum clock_id clkid, u32 divm, u32 divn,
data |= divp << PLLU_VCO_FREQ_SHIFT;
else
data |= divp << PLL_DIVP_SHIFT;
- writel(data, &pll->pll_base);
+ if (pll) {
+ writel(misc_data, &pll->pll_misc);
+ writel(data, &pll->pll_base);
+ } else {
+ struct clk_pll_simple *pll = clock_get_simple_pll(clkid);
+
+ if (!pll) {
+ debug("%s: Uknown simple PLL %d\n", __func__, clkid);
+ return 0;
+ }
+ writel(misc_data, &pll->pll_misc);
+ writel(data, &pll->pll_base);
+ }
/* calculate the stable time */
return timer_get_us() + CLOCK_PLL_STABLE_DELAY_US;
@@ -152,12 +172,37 @@ void clock_ll_set_source_divisor(enum periph_id periph_id, unsigned source,
writel(value, reg);
}
-void clock_ll_set_source(enum periph_id periph_id, unsigned source)
+int clock_ll_set_source_bits(enum periph_id periph_id, int mux_bits,
+ unsigned source)
{
u32 *reg = get_periph_source_reg(periph_id);
- clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK,
- source << OUT_CLK_SOURCE_31_30_SHIFT);
+ switch (mux_bits) {
+ case MASK_BITS_31_30:
+ clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK,
+ source << OUT_CLK_SOURCE_31_30_SHIFT);
+ break;
+
+ case MASK_BITS_31_29:
+ clrsetbits_le32(reg, OUT_CLK_SOURCE_31_29_MASK,
+ source << OUT_CLK_SOURCE_31_29_SHIFT);
+ break;
+
+ case MASK_BITS_31_28:
+ clrsetbits_le32(reg, OUT_CLK_SOURCE_31_28_MASK,
+ source << OUT_CLK_SOURCE_31_28_SHIFT);
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+void clock_ll_set_source(enum periph_id periph_id, unsigned source)
+{
+ clock_ll_set_source_bits(periph_id, MASK_BITS_31_30, source);
}
/**
@@ -306,25 +351,7 @@ static int adjust_periph_pll(enum periph_id periph_id, int source,
if (source < 0)
return -1;
- switch (mux_bits) {
- case MASK_BITS_31_30:
- clrsetbits_le32(reg, OUT_CLK_SOURCE_31_30_MASK,
- source << OUT_CLK_SOURCE_31_30_SHIFT);
- break;
-
- case MASK_BITS_31_29:
- clrsetbits_le32(reg, OUT_CLK_SOURCE_31_29_MASK,
- source << OUT_CLK_SOURCE_31_29_SHIFT);
- break;
-
- case MASK_BITS_31_28:
- clrsetbits_le32(reg, OUT_CLK_SOURCE_31_28_MASK,
- source << OUT_CLK_SOURCE_31_28_SHIFT);
- break;
-
- default:
- return -1;
- }
+ clock_ll_set_source_bits(periph_id, mux_bits, source);
udelay(2);
return 0;
@@ -431,6 +458,8 @@ unsigned clock_get_rate(enum clock_id clkid)
return parent_rate;
pll = get_pll(clkid);
+ if (!pll)
+ return 0;
base = readl(&pll->pll_base);
/* Oh for bf_unpack()... */
@@ -564,6 +593,7 @@ 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_DISPLAY] = clock_get_rate(CLOCK_ID_DISPLAY);
pll_rate[CLOCK_ID_OSC] = clock_get_rate(CLOCK_ID_OSC);
pll_rate[CLOCK_ID_SFROM32KHZ] = 32768;
pll_rate[CLOCK_ID_XCPU] = clock_get_rate(CLOCK_ID_XCPU);
@@ -571,6 +601,7 @@ void clock_init(void)
debug("PLLM = %d\n", pll_rate[CLOCK_ID_MEMORY]);
debug("PLLP = %d\n", pll_rate[CLOCK_ID_PERIPH]);
debug("PLLC = %d\n", pll_rate[CLOCK_ID_CGENERAL]);
+ debug("PLLD = %d\n", pll_rate[CLOCK_ID_DISPLAY]);
debug("PLLX = %d\n", pll_rate[CLOCK_ID_XCPU]);
/* Do any special system timer/TSC setup */
diff --git a/arch/arm/mach-tegra/emc.c b/arch/arm/mach-tegra/emc.c
new file mode 100644
index 0000000..8c62f36
--- /dev/null
+++ b/arch/arm/mach-tegra/emc.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include "emc.h"
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/emc.h>
+#include <asm/arch/pmu.h>
+#include <asm/arch/tegra.h>
+#include <asm/arch-tegra/ap.h>
+#include <asm/arch-tegra/clk_rst.h>
+#include <asm/arch-tegra/sys_proto.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* These rates are hard-coded for now, until fdt provides them */
+#define EMC_SDRAM_RATE_T20 (333000 * 2 * 1000)
+#define EMC_SDRAM_RATE_T25 (380000 * 2 * 1000)
+
+int board_emc_init(void)
+{
+ unsigned rate;
+
+ switch (tegra_get_chip_sku()) {
+ default:
+ case TEGRA_SOC_T20:
+ rate = EMC_SDRAM_RATE_T20;
+ break;
+ case TEGRA_SOC_T25:
+ rate = EMC_SDRAM_RATE_T25;
+ break;
+ }
+ return tegra_set_emc(gd->fdt_blob, rate);
+}
diff --git a/arch/arm/mach-tegra/emc.h b/arch/arm/mach-tegra/emc.h
new file mode 100644
index 0000000..4095235
--- /dev/null
+++ b/arch/arm/mach-tegra/emc.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef _NVIDIA_EMC_H_
+#define _NVIDIA_EMC_H_
+
+int board_emc_init(void);
+
+#endif
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
index 439cff3..6331cd4 100644
--- a/arch/arm/mach-tegra/powergate.c
+++ b/arch/arm/mach-tegra/powergate.c
@@ -44,7 +44,7 @@ static int tegra_powergate_set(enum tegra_powergate id, bool state)
return -ETIMEDOUT;
}
-static int tegra_powergate_power_on(enum tegra_powergate id)
+int tegra_powergate_power_on(enum tegra_powergate id)
{
return tegra_powergate_set(id, true);
}
diff --git a/arch/arm/mach-tegra/psci.S b/arch/arm/mach-tegra/psci.S
new file mode 100644
index 0000000..b836da1
--- /dev/null
+++ b/arch/arm/mach-tegra/psci.S
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014, NVIDIA
+ * Copyright (C) 2015, Siemens AG
+ *
+ * Authors:
+ * Thierry Reding <treding@nvidia.com>
+ * Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/linkage.h>
+#include <asm/macro.h>
+#include <asm/psci.h>
+
+ .pushsection ._secure.text, "ax"
+ .arch_extension sec
+
+#define TEGRA_SB_CSR_0 0x6000c200
+#define NS_RST_VEC_WR_DIS (1 << 1)
+
+#define TEGRA_RESET_EXCEPTION_VECTOR 0x6000f100
+
+#define TEGRA_FLOW_CTRL_BASE 0x60007000
+#define FLOW_CTRL_CPU_CSR 0x08
+#define CSR_ENABLE (1 << 0)
+#define CSR_IMMEDIATE_WAKE (1 << 3)
+#define CSR_WAIT_WFI_SHIFT 8
+#define FLOW_CTRL_CPU1_CSR 0x18
+
+@ converts CPU ID into FLOW_CTRL_CPUn_CSR offset
+.macro get_csr_reg cpu, ofs, tmp
+ cmp \cpu, #0 @ CPU0?
+ lsl \tmp, \cpu, #3 @ multiple by 8 (register offset CPU1-3)
+ moveq \ofs, #FLOW_CTRL_CPU_CSR
+ addne \ofs, \tmp, #FLOW_CTRL_CPU1_CSR - 8
+.endm
+
+ENTRY(psci_arch_init)
+ mov r6, lr
+
+ mrc p15, 0, r5, c1, c1, 0 @ Read SCR
+ bic r5, r5, #1 @ Secure mode
+ mcr p15, 0, r5, c1, c1, 0 @ Write SCR
+ isb
+
+ @ lock reset vector for non-secure
+ ldr r4, =TEGRA_SB_CSR_0
+ ldr r5, [r4]
+ orr r5, r5, #NS_RST_VEC_WR_DIS
+ str r5, [r4]
+
+ bl psci_get_cpu_id @ CPU ID => r0
+
+ adr r5, _sys_clock_freq
+ cmp r0, #0
+
+ mrceq p15, 0, r7, c14, c0, 0 @ read CNTFRQ from CPU0
+ streq r7, [r5]
+
+ ldrne r7, [r5]
+ mcrne p15, 0, r7, c14, c0, 0 @ write CNTFRQ to CPU1..3
+
+ bl psci_get_cpu_stack_top @ stack top => r0
+ mov sp, r0
+
+ bx r6
+ENDPROC(psci_arch_init)
+
+_sys_clock_freq:
+ .word 0
+
+ENTRY(psci_cpu_off)
+ bl psci_cpu_off_common
+
+ bl psci_get_cpu_id @ CPU ID => r0
+
+ get_csr_reg r0, r2, r3
+
+ ldr r6, =TEGRA_FLOW_CTRL_BASE
+ mov r5, #(CSR_ENABLE)
+ mov r4, #(1 << CSR_WAIT_WFI_SHIFT)
+ add r5, r4, lsl r0
+ str r5, [r6, r2]
+
+_loop: wfi
+ b _loop
+ENDPROC(psci_cpu_off)
+
+ENTRY(psci_cpu_on)
+ push {lr}
+
+ mov r0, r1
+ bl psci_get_cpu_stack_top @ get stack top of target CPU
+ str r2, [r0] @ store target PC at stack top
+ dsb
+
+ ldr r6, =TEGRA_RESET_EXCEPTION_VECTOR
+ ldr r5, =psci_cpu_entry
+ str r5, [r6]
+
+ get_csr_reg r1, r2, r3
+
+ ldr r6, =TEGRA_FLOW_CTRL_BASE
+ mov r5, #(CSR_IMMEDIATE_WAKE | CSR_ENABLE)
+ str r5, [r6, r2]
+
+ mov r0, #ARM_PSCI_RET_SUCCESS @ Return PSCI_RET_SUCCESS
+ pop {pc}
+ENDPROC(psci_cpu_on)
+
+ .globl psci_text_end
+psci_text_end:
+ .popsection
diff --git a/arch/arm/mach-tegra/tegra20/pwm.c b/arch/arm/mach-tegra/pwm.c
index 5b88636..1c38fc1 100644
--- a/arch/arm/mach-tegra/tegra20/pwm.c
+++ b/arch/arm/mach-tegra/pwm.c
@@ -1,5 +1,5 @@
/*
- * Tegra2 pulse width frequency modulator definitions
+ * Tegra pulse width frequency modulator definitions
*
* Copyright (c) 2011 The Chromium OS Authors.
*
@@ -24,7 +24,10 @@ void pwm_enable(unsigned channel, int rate, int pulse_width, int freq_divider)
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);
+ if (rate) {
+ clock_start_periph_pll(PERIPH_ID_PWM, CLOCK_ID_SFROM32KHZ,
+ rate);
+ }
reg = PWM_ENABLE_MASK;
reg |= pulse_width << PWM_WIDTH_SHIFT;
diff --git a/arch/arm/mach-tegra/tegra124/Kconfig b/arch/arm/mach-tegra/tegra124/Kconfig
index 36bb636..6579e3f 100644
--- a/arch/arm/mach-tegra/tegra124/Kconfig
+++ b/arch/arm/mach-tegra/tegra124/Kconfig
@@ -6,6 +6,8 @@ choice
config TARGET_JETSON_TK1
bool "NVIDIA Tegra124 Jetson TK1 board"
+ select CPU_V7_HAS_NONSEC if !SPL_BUILD
+ select CPU_V7_HAS_VIRT if !SPL_BUILD
config TARGET_NYAN_BIG
bool "Google/NVIDIA Nyan-big Chrombook"
diff --git a/arch/arm/mach-tegra/tegra124/Makefile b/arch/arm/mach-tegra/tegra124/Makefile
index ef2da29..f577f45 100644
--- a/arch/arm/mach-tegra/tegra124/Makefile
+++ b/arch/arm/mach-tegra/tegra124/Makefile
@@ -11,3 +11,7 @@ obj-y += clock.o
obj-y += funcmux.o
obj-y += pinmux.o
obj-y += xusb-padctl.o
+
+ifndef CONFIG_SPL_BUILD
+obj-$(CONFIG_ARMV7_NONSEC) += psci.o
+endif
diff --git a/arch/arm/mach-tegra/tegra124/clock.c b/arch/arm/mach-tegra/tegra124/clock.c
index fc8bd19..2d17550 100644
--- a/arch/arm/mach-tegra/tegra124/clock.c
+++ b/arch/arm/mach-tegra/tegra124/clock.c
@@ -42,6 +42,7 @@ enum clock_type_id {
CLOCK_TYPE_ASPTE,
CLOCK_TYPE_PMDACD2T,
CLOCK_TYPE_PCST,
+ CLOCK_TYPE_DP,
CLOCK_TYPE_PC2CC3M,
CLOCK_TYPE_PC2CC3S_T,
@@ -101,6 +102,10 @@ static enum clock_id clock_source[CLOCK_TYPE_COUNT][CLOCK_MAX_MUX+1] = {
{ CLK(PERIPH), CLK(CGENERAL), CLK(SFROM32KHZ), CLK(OSC),
CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE),
MASK_BITS_31_28},
+ /* CLOCK_TYPE_DP */
+ { CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE),
+ CLK(NONE), CLK(NONE), CLK(NONE), CLK(NONE),
+ MASK_BITS_31_28},
/* Additional clock types on Tegra114+ */
/* CLOCK_TYPE_PC2CC3M */
@@ -259,7 +264,7 @@ static enum clock_type_id clock_periph_type[PERIPHC_COUNT] = {
/* 0x58 */
TYPE(PERIPHC_58h, CLOCK_TYPE_NONE),
- TYPE(PERIPHC_59h, CLOCK_TYPE_NONE),
+ TYPE(PERIPHC_SOR, CLOCK_TYPE_NONE),
TYPE(PERIPHC_5ah, CLOCK_TYPE_NONE),
TYPE(PERIPHC_5bh, CLOCK_TYPE_NONE),
TYPE(PERIPHC_SATAOOB, CLOCK_TYPE_PCMT),
@@ -546,7 +551,7 @@ static s8 periph_id_to_internal_id[PERIPH_ID_COUNT] = {
NONE(X_RESERVED19),
NONE(ADX1),
NONE(DPAUX),
- NONE(SOR0),
+ PERIPHC_SOR,
NONE(X_RESERVED23),
/* 184 */
@@ -594,7 +599,10 @@ u32 *get_periph_source_reg(enum periph_id periph_id)
assert(periph_id >= PERIPH_ID_FIRST && periph_id < PERIPH_ID_COUNT);
internal_id = periph_id_to_internal_id[periph_id];
assert(internal_id != -1);
- if (internal_id >= PERIPHC_VW_FIRST) {
+ if (internal_id >= PERIPHC_X_FIRST) {
+ internal_id -= PERIPHC_X_FIRST;
+ return &clkrst->crc_clk_src_x[internal_id];
+ } else if (internal_id >= PERIPHC_VW_FIRST) {
internal_id -= PERIPHC_VW_FIRST;
return &clkrst->crc_clk_src_vw[internal_id];
} else {
@@ -657,8 +665,10 @@ void clock_set_enable(enum periph_id periph_id, int enable)
assert(clock_periph_id_isvalid(periph_id));
if ((int)periph_id < (int)PERIPH_ID_VW_FIRST)
clk = &clkrst->crc_clk_out_enb[PERIPH_REG(periph_id)];
- else
+ else if ((int)periph_id < PERIPH_ID_X_FIRST)
clk = &clkrst->crc_clk_out_enb_vw[PERIPH_REG(periph_id)];
+ else
+ clk = &clkrst->crc_clk_out_enb_x;
reg = readl(clk);
if (enable)
reg |= PERIPH_MASK(periph_id);
@@ -678,8 +688,10 @@ void reset_set_enable(enum periph_id periph_id, int enable)
assert(clock_periph_id_isvalid(periph_id));
if (periph_id < PERIPH_ID_VW_FIRST)
reset = &clkrst->crc_rst_dev[PERIPH_REG(periph_id)];
- else
+ else if ((int)periph_id < PERIPH_ID_X_FIRST)
reset = &clkrst->crc_rst_dev_vw[PERIPH_REG(periph_id)];
+ else
+ reset = &clkrst->crc_rst_devices_x;
reg = readl(reset);
if (enable)
reg |= PERIPH_MASK(periph_id);
@@ -933,3 +945,122 @@ int tegra_plle_enable(void)
return 0;
}
+
+void clock_sor_enable_edp_clock(void)
+{
+ u32 *reg;
+
+ /* uses PLLP, has a non-standard bit layout. */
+ reg = get_periph_source_reg(PERIPH_ID_SOR0);
+ setbits_le32(reg, SOR0_CLK_SEL0);
+}
+
+u32 clock_set_display_rate(u32 frequency)
+{
+ /**
+ * plld (fo) = vco >> p, where 500MHz < vco < 1000MHz
+ * = (cf * n) >> p, where 1MHz < cf < 6MHz
+ * = ((ref / m) * n) >> p
+ *
+ * Iterate the possible values of p (3 bits, 2^7) to find out a minimum
+ * safe vco, then find best (m, n). since m has only 5 bits, we can
+ * iterate all possible values. Note Tegra 124 supports 11 bits for n,
+ * but our pll_fields has only 10 bits for n.
+ *
+ * Note values undershoot or overshoot target output frequency may not
+ * work if the values are not in "safe" range by panel specification.
+ */
+ u32 ref = clock_get_rate(CLOCK_ID_OSC);
+ u32 divm, divn, divp, cpcon;
+ u32 cf, vco, rounded_rate = frequency;
+ u32 diff, best_diff, best_m = 0, best_n = 0, best_p;
+ const u32 max_m = 1 << 5, max_n = 1 << 10, max_p = 1 << 3,
+ mhz = 1000 * 1000, min_vco = 500 * mhz, max_vco = 1000 * mhz,
+ min_cf = 1 * mhz, max_cf = 6 * mhz;
+ int mux_bits, divider_bits, source;
+
+ for (divp = 0, vco = frequency; vco < min_vco && divp < max_p; divp++)
+ vco <<= 1;
+
+ if (vco < min_vco || vco > max_vco) {
+ printf("%s: Cannot find out a supported VCO for Frequency (%u)\n",
+ __func__, frequency);
+ return 0;
+ }
+
+ best_p = divp;
+ best_diff = vco;
+
+ for (divm = 1; divm < max_m && best_diff; divm++) {
+ cf = ref / divm;
+ if (cf < min_cf)
+ break;
+ if (cf > max_cf)
+ continue;
+
+ divn = vco / cf;
+ if (divn >= max_n)
+ continue;
+
+ diff = vco - divn * cf;
+ if (divn + 1 < max_n && diff > cf / 2) {
+ divn++;
+ diff = cf - diff;
+ }
+
+ if (diff >= best_diff)
+ continue;
+
+ best_diff = diff;
+ best_m = divm;
+ best_n = divn;
+ }
+
+ if (best_n < 50)
+ cpcon = 2;
+ else if (best_n < 300)
+ cpcon = 3;
+ else if (best_n < 600)
+ cpcon = 8;
+ else
+ cpcon = 12;
+
+ if (best_diff) {
+ printf("%s: Failed to match output frequency %u, best difference is %u\n",
+ __func__, frequency, best_diff);
+ rounded_rate = (ref / best_m * best_n) >> best_p;
+ }
+
+ debug("%s: PLLD=%u ref=%u, m/n/p/cpcon=%u/%u/%u/%u\n",
+ __func__, rounded_rate, ref, best_m, best_n, best_p, cpcon);
+
+ source = get_periph_clock_source(PERIPH_ID_DISP1, CLOCK_ID_DISPLAY,
+ &mux_bits, &divider_bits);
+ clock_ll_set_source_bits(PERIPH_ID_DISP1, mux_bits, source);
+ clock_set_rate(CLOCK_ID_DISPLAY, best_n, best_m, best_p, cpcon);
+
+ return rounded_rate;
+}
+
+void clock_set_up_plldp(void)
+{
+ struct clk_rst_ctlr *clkrst =
+ (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+ u32 value;
+
+ value = PLLDP_SS_CFG_UNDOCUMENTED | PLLDP_SS_CFG_DITHER;
+ writel(value | PLLDP_SS_CFG_CLAMP, &clkrst->crc_plldp_ss_cfg);
+ clock_start_pll(CLOCK_ID_DP, 1, 90, 3, 0, 0);
+ writel(value, &clkrst->crc_plldp_ss_cfg);
+}
+
+struct clk_pll_simple *clock_get_simple_pll(enum clock_id clkid)
+{
+ struct clk_rst_ctlr *clkrst =
+ (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+
+ if (clkid == CLOCK_ID_DP)
+ return &clkrst->plldp;
+
+ return NULL;
+}
diff --git a/arch/arm/mach-tegra/tegra124/psci.c b/arch/arm/mach-tegra/tegra124/psci.c
new file mode 100644
index 0000000..16d1965
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra124/psci.c
@@ -0,0 +1,59 @@
+/*
+ * (C) Copyright 2015, Siemens AG
+ * Author: Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/psci.h>
+#include <asm/arch/flow.h>
+#include <asm/arch/powergate.h>
+#include <asm/arch-tegra/ap.h>
+#include <asm/arch-tegra/pmc.h>
+
+static void park_cpu(void)
+{
+ while (1)
+ asm volatile("wfi");
+}
+
+/**
+ * Initialize power management for application processors
+ */
+void psci_board_init(void)
+{
+ struct flow_ctlr *flow = (struct flow_ctlr *)NV_PA_FLOW_BASE;
+
+ writel((u32)park_cpu, EXCEP_VECTOR_CPU_RESET_VECTOR);
+
+ /*
+ * The naturally expected order of putting these CPUs under Flow
+ * Controller regime would be
+ * - configure the Flow Controller
+ * - power up the CPUs
+ * - wait for the CPUs to hit wfi and be powered down again
+ *
+ * However, this doesn't work in practice. We rather need to power them
+ * up first and park them in wfi. While they are waiting there, we can
+ * indeed program the Flow Controller to powergate them on wfi, which
+ * will then happen immediately as they are already in that state.
+ */
+ tegra_powergate_power_on(TEGRA_POWERGATE_CPU1);
+ tegra_powergate_power_on(TEGRA_POWERGATE_CPU2);
+ tegra_powergate_power_on(TEGRA_POWERGATE_CPU3);
+
+ writel((2 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu1_csr);
+ writel((4 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu2_csr);
+ writel((8 << CSR_WAIT_WFI_SHIFT) | CSR_ENABLE, &flow->cpu3_csr);
+
+ writel(EVENT_MODE_STOP, &flow->halt_cpu1_events);
+ writel(EVENT_MODE_STOP, &flow->halt_cpu2_events);
+ writel(EVENT_MODE_STOP, &flow->halt_cpu3_events);
+
+ while (!(readl(&flow->cpu1_csr) & CSR_PWR_OFF_STS) ||
+ !(readl(&flow->cpu2_csr) & CSR_PWR_OFF_STS) ||
+ !(readl(&flow->cpu3_csr) & CSR_PWR_OFF_STS))
+ /* wait */;
+}
diff --git a/arch/arm/mach-tegra/tegra20/Makefile b/arch/arm/mach-tegra/tegra20/Makefile
index d48f9bb..fc3fb4a 100644
--- a/arch/arm/mach-tegra/tegra20/Makefile
+++ b/arch/arm/mach-tegra/tegra20/Makefile
@@ -7,7 +7,6 @@
ifdef CONFIG_SPL_BUILD
obj-y += cpu.o
else
-obj-$(CONFIG_PWM_TEGRA) += pwm.o
obj-$(CONFIG_VIDEO_TEGRA) += display.o
endif
diff --git a/arch/arm/mach-tegra/tegra20/display.c b/arch/arm/mach-tegra/tegra20/display.c
index 61efed6..b7605ff 100644
--- a/arch/arm/mach-tegra/tegra20/display.c
+++ b/arch/arm/mach-tegra/tegra20/display.c
@@ -10,7 +10,7 @@
#include <asm/arch/clock.h>
#include <asm/arch/tegra.h>
#include <asm/arch/display.h>
-#include <asm/arch/dc.h>
+#include <asm/arch-tegra/dc.h>
#include <asm/arch-tegra/clk_rst.h>
#include <asm/arch-tegra/timer.h>