diff options
Diffstat (limited to 'arch/arm/mach-tegra')
40 files changed, 1680 insertions, 1679 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index ef3a8da49b2..09e740f58b2 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -2,18 +2,23 @@ config ARCH_TEGRA bool "NVIDIA Tegra" if ARCH_MULTI_V7 select ARCH_HAS_CPUFREQ select ARCH_REQUIRE_GPIOLIB - select CLKDEV_LOOKUP + select ARM_GIC select CLKSRC_MMIO select CLKSRC_OF select COMMON_CLK + select CPU_V7 select GENERIC_CLOCKEVENTS select HAVE_ARM_SCU if SMP - select HAVE_ARM_TWD if LOCAL_TIMERS - select HAVE_CLK + select HAVE_ARM_TWD if SMP select HAVE_SMP select MIGHT_HAVE_CACHE_L2X0 + select MIGHT_HAVE_PCI + select PINCTRL select SOC_BUS select SPARSE_IRQ + select USB_ARCH_HAS_EHCI if USB_SUPPORT + select USB_ULPI if USB_PHY + select USB_ULPI_VIEWPORT if USB_PHY select USE_OF help This enables support for NVIDIA Tegra based systems. @@ -27,15 +32,9 @@ config ARCH_TEGRA_2x_SOC select ARM_ERRATA_720789 select ARM_ERRATA_754327 if SMP select ARM_ERRATA_764369 if SMP - select ARM_GIC - select CPU_V7 - select PINCTRL select PINCTRL_TEGRA20 select PL310_ERRATA_727915 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0 - select USB_ARCH_HAS_EHCI if USB_SUPPORT - select USB_ULPI if USB_PHY - select USB_ULPI_VIEWPORT if USB_PHY help Support for NVIDIA Tegra AP20 and T20 processors, based on the ARM CortexA9MP CPU and the ARM PL310 L2 cache controller @@ -44,34 +43,29 @@ config ARCH_TEGRA_3x_SOC bool "Enable support for Tegra30 family" select ARM_ERRATA_754322 select ARM_ERRATA_764369 if SMP - select ARM_GIC - select CPU_V7 - select PINCTRL select PINCTRL_TEGRA30 select PL310_ERRATA_769419 if CACHE_L2X0 - select USB_ARCH_HAS_EHCI if USB_SUPPORT - select USB_ULPI if USB_PHY - select USB_ULPI_VIEWPORT if USB_PHY help Support for NVIDIA Tegra T30 processor family, based on the ARM CortexA9MP CPU and the ARM PL310 L2 cache controller config ARCH_TEGRA_114_SOC bool "Enable support for Tegra114 family" - select HAVE_ARM_ARCH_TIMER - select ARM_GIC + select ARM_ERRATA_798181 if SMP select ARM_L1_CACHE_SHIFT_6 - select CPU_V7 - select PINCTRL + select HAVE_ARM_ARCH_TIMER select PINCTRL_TEGRA114 help Support for NVIDIA Tegra T114 processor family, based on the ARM CortexA15MP CPU -config TEGRA_PCI - bool "PCI Express support" - depends on ARCH_TEGRA_2x_SOC - select PCI +config ARCH_TEGRA_124_SOC + bool "Enable support for Tegra124 family" + select ARM_L1_CACHE_SHIFT_6 + select HAVE_ARM_ARCH_TIMER + help + Support for NVIDIA Tegra T124 processor family, based on the + ARM CortexA15MP CPU config TEGRA_AHB bool "Enable AHB driver for NVIDIA Tegra SoCs" diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 98b184efc11..019bb175866 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -1,6 +1,5 @@ asflags-y += -march=armv7-a -obj-y += common.o obj-y += io.o obj-y += irq.o obj-y += fuse.o @@ -17,24 +16,29 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-tegra20.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o endif obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_speedo.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-tegra30.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o endif obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_TEGRA_PCI) += pcie.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114_speedo.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += sleep-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += pm-tegra30.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_114_SOC) += cpuidle-tegra114.o endif - -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-harmony-pcie.o +obj-$(CONFIG_ARCH_TEGRA_124_SOC) += sleep-tegra30.o +obj-$(CONFIG_ARCH_TEGRA_124_SOC) += pm-tegra30.o +ifeq ($(CONFIG_CPU_IDLE),y) +obj-$(CONFIG_ARCH_TEGRA_124_SOC) += cpuidle-tegra114.o +endif obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-paz00.o diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c index d7aa52ea6cf..bc471973cf0 100644 --- a/arch/arm/mach-tegra/apbio.c +++ b/arch/arm/mach-tegra/apbio.c @@ -114,7 +114,7 @@ static int do_dma_transfer(unsigned long apb_add, dma_desc->callback = apb_dma_complete; dma_desc->callback_param = NULL; - INIT_COMPLETION(tegra_apb_wait); + reinit_completion(&tegra_apb_wait); dmaengine_submit(dma_desc); dma_async_issue_pending(tegra_apb_dma_chan); diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c deleted file mode 100644 index 035b240b9e1..00000000000 --- a/arch/arm/mach-tegra/board-harmony-pcie.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * arch/arm/mach-tegra/board-harmony-pcie.c - * - * Copyright (C) 2010 CompuLab, Ltd. - * Mike Rapoport <mike@compulab.co.il> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/kernel.h> -#include <linux/gpio.h> -#include <linux/err.h> -#include <linux/of_gpio.h> -#include <linux/regulator/consumer.h> - -#include <asm/mach-types.h> - -#include "board.h" - -#ifdef CONFIG_TEGRA_PCI - -int __init harmony_pcie_init(void) -{ - struct device_node *np; - int en_vdd_1v05; - struct regulator *regulator = NULL; - int err; - - np = of_find_node_by_path("/regulators/regulator@3"); - if (!np) { - pr_err("%s: of_find_node_by_path failed\n", __func__); - return -ENODEV; - } - - en_vdd_1v05 = of_get_named_gpio(np, "gpio", 0); - if (en_vdd_1v05 < 0) { - pr_err("%s: of_get_named_gpio failed: %d\n", __func__, - en_vdd_1v05); - return en_vdd_1v05; - } - - err = gpio_request(en_vdd_1v05, "EN_VDD_1V05"); - if (err) { - pr_err("%s: gpio_request failed: %d\n", __func__, err); - return err; - } - - gpio_direction_output(en_vdd_1v05, 1); - - regulator = regulator_get(NULL, "vdd_ldo0,vddio_pex_clk"); - if (IS_ERR(regulator)) { - err = PTR_ERR(regulator); - pr_err("%s: regulator_get failed: %d\n", __func__, err); - goto err_reg; - } - - err = regulator_enable(regulator); - if (err) { - pr_err("%s: regulator_enable failed: %d\n", __func__, err); - goto err_en; - } - - err = tegra_pcie_init(true, true); - if (err) { - pr_err("%s: tegra_pcie_init failed: %d\n", __func__, err); - goto err_pcie; - } - - return 0; - -err_pcie: - regulator_disable(regulator); -err_en: - regulator_put(regulator); -err_reg: - gpio_free(en_vdd_1v05); - - return err; -} - -#endif diff --git a/arch/arm/mach-tegra/board-paz00.c b/arch/arm/mach-tegra/board-paz00.c index 740e16f6472..06f024070da 100644 --- a/arch/arm/mach-tegra/board-paz00.c +++ b/arch/arm/mach-tegra/board-paz00.c @@ -20,12 +20,11 @@ #include <linux/platform_device.h> #include <linux/rfkill-gpio.h> #include "board.h" -#include "board-paz00.h" static struct rfkill_gpio_platform_data wifi_rfkill_platform_data = { .name = "wifi_rfkill", - .reset_gpio = TEGRA_WIFI_RST, - .shutdown_gpio = TEGRA_WIFI_PWRN, + .reset_gpio = 25, /* PD1 */ + .shutdown_gpio = 85, /* PK5 */ .type = RFKILL_TYPE_WLAN, }; diff --git a/arch/arm/mach-tegra/board-paz00.h b/arch/arm/mach-tegra/board-paz00.h deleted file mode 100644 index 25c08ecef52..00000000000 --- a/arch/arm/mach-tegra/board-paz00.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * arch/arm/mach-tegra/board-paz00.h - * - * Copyright (C) 2010 Marc Dietrich <marvin24@gmx.de> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef _MACH_TEGRA_BOARD_PAZ00_H -#define _MACH_TEGRA_BOARD_PAZ00_H - -#include "gpio-names.h" - -#define TEGRA_WIFI_PWRN TEGRA_GPIO_PK5 -#define TEGRA_WIFI_RST TEGRA_GPIO_PD1 - -#endif diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 9a6659fe2dc..bcf5dbf69d5 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -25,21 +25,8 @@ #include <linux/types.h> #include <linux/reboot.h> -void tegra_assert_system_reset(enum reboot_mode mode, const char *cmd); - -void __init tegra_init_early(void); void __init tegra_map_common_io(void); void __init tegra_init_irq(void); -void __init tegra_dt_init_irq(void); -int __init tegra_pcie_init(bool init_port0, bool init_port1); - -void tegra_init_late(void); - -#ifdef CONFIG_DEBUG_FS -int tegra_clk_debugfs_init(void); -#else -static inline int tegra_clk_debugfs_init(void) { return 0; } -#endif int __init tegra_powergate_init(void); #if defined(CONFIG_ARCH_TEGRA_2x_SOC) && defined(CONFIG_DEBUG_FS) @@ -48,13 +35,6 @@ int __init tegra_powergate_debugfs_init(void); static inline int tegra_powergate_debugfs_init(void) { return 0; } #endif -int __init harmony_regulator_init(void); -#ifdef CONFIG_TEGRA_PCI -int __init harmony_pcie_init(void); -#else -static inline int harmony_pcie_init(void) { return 0; } -#endif - void __init tegra_paz00_wifikill_init(void); #endif diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c deleted file mode 100644 index 94a119a35af..00000000000 --- a/arch/arm/mach-tegra/common.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * arch/arm/mach-tegra/common.c - * - * Copyright (c) 2013 NVIDIA Corporation. All rights reserved. - * Copyright (C) 2010 Google, Inc. - * - * Author: - * Colin Cross <ccross@android.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/init.h> -#include <linux/io.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/reboot.h> -#include <linux/irqchip.h> -#include <linux/clk-provider.h> - -#include <asm/hardware/cache-l2x0.h> - -#include "board.h" -#include "common.h" -#include "cpuidle.h" -#include "fuse.h" -#include "iomap.h" -#include "irq.h" -#include "pmc.h" -#include "apbio.h" -#include "sleep.h" -#include "pm.h" -#include "reset.h" - -/* - * Storage for debug-macro.S's state. - * - * This must be in .data not .bss so that it gets initialized each time the - * kernel is loaded. The data is declared here rather than debug-macro.S so - * that multiple inclusions of debug-macro.S point at the same data. - */ -u32 tegra_uart_config[4] = { - /* Debug UART initialization required */ - 1, - /* Debug UART physical address */ - 0, - /* Debug UART virtual address */ - 0, - /* Scratch space for debug macro */ - 0, -}; - -#ifdef CONFIG_OF -void __init tegra_dt_init_irq(void) -{ - of_clk_init(NULL); - tegra_pmc_init(); - tegra_init_irq(); - irqchip_init(); - tegra_legacy_irq_syscore_init(); -} -#endif - -void tegra_assert_system_reset(enum reboot_mode mode, const char *cmd) -{ - void __iomem *reset = IO_ADDRESS(TEGRA_PMC_BASE + 0); - u32 reg; - - reg = readl_relaxed(reset); - reg |= 0x10; - writel_relaxed(reg, reset); -} - -static void __init tegra_init_cache(void) -{ -#ifdef CONFIG_CACHE_L2X0 - int ret; - void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; - u32 aux_ctrl, cache_type; - - cache_type = readl(p + L2X0_CACHE_TYPE); - aux_ctrl = (cache_type & 0x700) << (17-8); - aux_ctrl |= 0x7C400001; - - ret = l2x0_of_init(aux_ctrl, 0x8200c3fe); - if (!ret) - l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs); -#endif - -} - -void __init tegra_init_early(void) -{ - tegra_cpu_reset_handler_init(); - tegra_apb_io_init(); - tegra_init_fuse(); - tegra_init_cache(); - tegra_powergate_init(); - tegra_hotplug_init(); -} - -void __init tegra_init_late(void) -{ - tegra_init_suspend(); - tegra_cpuidle_init(); - tegra_powergate_debugfs_init(); -} diff --git a/arch/arm/mach-tegra/common.h b/arch/arm/mach-tegra/common.h index 32f8eb3fe34..5900cc44f78 100644 --- a/arch/arm/mach-tegra/common.h +++ b/arch/arm/mach-tegra/common.h @@ -2,4 +2,3 @@ extern struct smp_operations tegra_smp_ops; extern int tegra_cpu_kill(unsigned int cpu); extern void tegra_cpu_die(unsigned int cpu); -extern int tegra_cpu_disable(unsigned int cpu); diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c index 1d1c6023f4a..e0b87300243 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra114.c +++ b/arch/arm/mach-tegra/cpuidle-tegra114.c @@ -17,15 +17,64 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/cpuidle.h> +#include <linux/cpu_pm.h> +#include <linux/clockchips.h> #include <asm/cpuidle.h> +#include <asm/suspend.h> +#include <asm/smp_plat.h> + +#include "pm.h" +#include "sleep.h" + +#ifdef CONFIG_PM_SLEEP +#define TEGRA114_MAX_STATES 2 +#else +#define TEGRA114_MAX_STATES 1 +#endif + +#ifdef CONFIG_PM_SLEEP +static int tegra114_idle_power_down(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + local_fiq_disable(); + + tegra_set_cpu_in_lp2(); + cpu_pm_enter(); + + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); + + cpu_suspend(0, tegra30_sleep_cpu_secondary_finish); + + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); + + cpu_pm_exit(); + tegra_clear_cpu_in_lp2(); + + local_fiq_enable(); + + return index; +} +#endif static struct cpuidle_driver tegra_idle_driver = { .name = "tegra_idle", .owner = THIS_MODULE, - .state_count = 1, + .state_count = TEGRA114_MAX_STATES, .states = { [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), +#ifdef CONFIG_PM_SLEEP + [1] = { + .enter = tegra114_idle_power_down, + .exit_latency = 500, + .target_residency = 1000, + .power_usage = 0, + .flags = CPUIDLE_FLAG_TIME_VALID, + .name = "powered-down", + .desc = "CPU power gated", + }, +#endif }, }; diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 706aa4215c3..b82dcaee2ef 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -211,6 +211,18 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, } #endif +/* + * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether + * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around + * this, simply disable LP2 if the PCI driver and DT node are both enabled. + */ +void tegra20_cpuidle_pcie_irqs_in_use(void) +{ + pr_info_once( + "Disabling cpuidle LP2 state, since PCIe IRQs are in use\n"); + tegra_idle_driver.states[1].disabled = true; +} + int __init tegra20_cpuidle_init(void) { return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index e85973cef03..7bc5d8d667f 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -39,8 +39,20 @@ void __init tegra_cpuidle_init(void) tegra30_cpuidle_init(); break; case TEGRA114: - if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC)) + case TEGRA124: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)) tegra114_cpuidle_init(); break; } } + +void tegra_cpuidle_pcie_irqs_in_use(void) +{ + switch (tegra_chip_id) { + case TEGRA20: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra20_cpuidle_pcie_irqs_in_use(); + break; + } +} diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h index 9ec2c1ab0fa..c017dab60ff 100644 --- a/arch/arm/mach-tegra/cpuidle.h +++ b/arch/arm/mach-tegra/cpuidle.h @@ -19,6 +19,7 @@ #ifdef CONFIG_CPU_IDLE int tegra20_cpuidle_init(void); +void tegra20_cpuidle_pcie_irqs_in_use(void); int tegra30_cpuidle_init(void); int tegra114_cpuidle_init(void); void tegra_cpuidle_init(void); diff --git a/arch/arm/mach-tegra/flowctrl.c b/arch/arm/mach-tegra/flowctrl.c index b477ef310dc..ce8ab8abf06 100644 --- a/arch/arm/mach-tegra/flowctrl.c +++ b/arch/arm/mach-tegra/flowctrl.c @@ -86,6 +86,8 @@ void flowctrl_cpu_suspend_enter(unsigned int cpuid) reg |= TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 << cpuid; break; case TEGRA30: + case TEGRA114: + case TEGRA124: /* clear wfe bitmap */ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfi bitmap */ @@ -123,6 +125,8 @@ void flowctrl_cpu_suspend_exit(unsigned int cpuid) reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP; break; case TEGRA30: + case TEGRA114: + case TEGRA124: /* clear wfe bitmap */ reg &= ~TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP; /* clear wfi bitmap */ diff --git a/arch/arm/mach-tegra/flowctrl.h b/arch/arm/mach-tegra/flowctrl.h index 7a29bae799a..c89aac60a14 100644 --- a/arch/arm/mach-tegra/flowctrl.h +++ b/arch/arm/mach-tegra/flowctrl.h @@ -28,9 +28,18 @@ #define FLOW_CTRL_SCLK_RESUME (1 << 27) #define FLOW_CTRL_HALT_CPU_IRQ (1 << 10) #define FLOW_CTRL_HALT_CPU_FIQ (1 << 8) +#define FLOW_CTRL_HALT_LIC_IRQ (1 << 11) +#define FLOW_CTRL_HALT_LIC_FIQ (1 << 10) +#define FLOW_CTRL_HALT_GIC_IRQ (1 << 9) +#define FLOW_CTRL_HALT_GIC_FIQ (1 << 8) #define FLOW_CTRL_CPU0_CSR 0x8 #define FLOW_CTRL_CSR_INTR_FLAG (1 << 15) #define FLOW_CTRL_CSR_EVENT_FLAG (1 << 14) +#define FLOW_CTRL_CSR_ENABLE_EXT_CRAIL (1 << 13) +#define FLOW_CTRL_CSR_ENABLE_EXT_NCPU (1 << 12) +#define FLOW_CTRL_CSR_ENABLE_EXT_MASK ( \ + FLOW_CTRL_CSR_ENABLE_EXT_NCPU | \ + FLOW_CTRL_CSR_ENABLE_EXT_CRAIL) #define FLOW_CTRL_CSR_ENABLE (1 << 0) #define FLOW_CTRL_HALT_CPU1_EVENTS 0x14 #define FLOW_CTRL_CPU1_CSR 0x18 diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index e035cd284a6..3a9c1f1c219 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -21,14 +21,26 @@ #include <linux/kernel.h> #include <linux/io.h> #include <linux/export.h> +#include <linux/random.h> #include <linux/tegra-soc.h> #include "fuse.h" #include "iomap.h" #include "apbio.h" +/* Tegra20 only */ #define FUSE_UID_LOW 0x108 #define FUSE_UID_HIGH 0x10c + +/* Tegra30 and later */ +#define FUSE_VENDOR_CODE 0x200 +#define FUSE_FAB_CODE 0x204 +#define FUSE_LOT_CODE_0 0x208 +#define FUSE_LOT_CODE_1 0x20c +#define FUSE_WAFER_ID 0x210 +#define FUSE_X_COORDINATE 0x214 +#define FUSE_Y_COORDINATE 0x218 + #define FUSE_SKU_INFO 0x110 #define TEGRA20_FUSE_SPARE_BIT 0x200 @@ -112,21 +124,51 @@ u32 tegra_read_chipid(void) return readl_relaxed(IO_ADDRESS(TEGRA_APB_MISC_BASE) + 0x804); } -void tegra_init_fuse(void) +static void __init tegra20_fuse_init_randomness(void) +{ + u32 randomness[2]; + + randomness[0] = tegra_fuse_readl(FUSE_UID_LOW); + randomness[1] = tegra_fuse_readl(FUSE_UID_HIGH); + + add_device_randomness(randomness, sizeof(randomness)); +} + +/* Applies to Tegra30 or later */ +static void __init tegra30_fuse_init_randomness(void) +{ + u32 randomness[7]; + + randomness[0] = tegra_fuse_readl(FUSE_VENDOR_CODE); + randomness[1] = tegra_fuse_readl(FUSE_FAB_CODE); + randomness[2] = tegra_fuse_readl(FUSE_LOT_CODE_0); + randomness[3] = tegra_fuse_readl(FUSE_LOT_CODE_1); + randomness[4] = tegra_fuse_readl(FUSE_WAFER_ID); + randomness[5] = tegra_fuse_readl(FUSE_X_COORDINATE); + randomness[6] = tegra_fuse_readl(FUSE_Y_COORDINATE); + + add_device_randomness(randomness, sizeof(randomness)); +} + +void __init tegra_init_fuse(void) { u32 id; + u32 randomness[5]; u32 reg = readl(IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48)); reg |= 1 << 28; writel(reg, IO_ADDRESS(TEGRA_CLK_RESET_BASE + 0x48)); reg = tegra_fuse_readl(FUSE_SKU_INFO); + randomness[0] = reg; tegra_sku_id = reg & 0xFF; reg = tegra_apb_readl(TEGRA_APB_MISC_BASE + STRAP_OPT); + randomness[1] = reg; tegra_bct_strapping = (reg & RAM_ID_MASK) >> RAM_CODE_SHIFT; id = tegra_read_chipid(); + randomness[2] = id; tegra_chip_id = (id >> 8) & 0xff; switch (tegra_chip_id) { @@ -149,19 +191,23 @@ void tegra_init_fuse(void) tegra_revision = tegra_get_revision(id); tegra_init_speedo_data(); + randomness[3] = (tegra_cpu_process_id << 16) | tegra_core_process_id; + randomness[4] = (tegra_cpu_speedo_id << 16) | tegra_soc_speedo_id; + + add_device_randomness(randomness, sizeof(randomness)); + switch (tegra_chip_id) { + case TEGRA20: + tegra20_fuse_init_randomness(); + break; + case TEGRA30: + case TEGRA114: + default: + tegra30_fuse_init_randomness(); + break; + } pr_info("Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d\n", tegra_revision_name[tegra_revision], tegra_sku_id, tegra_cpu_process_id, tegra_core_process_id); } - -unsigned long long tegra_chip_uid(void) -{ - unsigned long long lo, hi; - - lo = tegra_fuse_readl(FUSE_UID_LOW); - hi = tegra_fuse_readl(FUSE_UID_HIGH); - return (hi << 32ull) | lo; -} -EXPORT_SYMBOL(tegra_chip_uid); diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index def79683bef..c01d04785d6 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -29,6 +29,7 @@ #define TEGRA20 0x20 #define TEGRA30 0x30 #define TEGRA114 0x35 +#define TEGRA124 0x40 #ifndef __ASSEMBLY__ enum tegra_revision { diff --git a/arch/arm/mach-tegra/gpio-names.h b/arch/arm/mach-tegra/gpio-names.h deleted file mode 100644 index f28220a641b..00000000000 --- a/arch/arm/mach-tegra/gpio-names.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * arch/arm/mach-tegra/include/mach/gpio-names.h - * - * Copyright (c) 2010 Google, Inc - * - * Author: - * Erik Gilling <konkers@google.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __MACH_TEGRA_GPIO_NAMES_H -#define __MACH_TEGRA_GPIO_NAMES_H - -#define TEGRA_GPIO_PA0 0 -#define TEGRA_GPIO_PA1 1 -#define TEGRA_GPIO_PA2 2 -#define TEGRA_GPIO_PA3 3 -#define TEGRA_GPIO_PA4 4 -#define TEGRA_GPIO_PA5 5 -#define TEGRA_GPIO_PA6 6 -#define TEGRA_GPIO_PA7 7 -#define TEGRA_GPIO_PB0 8 -#define TEGRA_GPIO_PB1 9 -#define TEGRA_GPIO_PB2 10 -#define TEGRA_GPIO_PB3 11 -#define TEGRA_GPIO_PB4 12 -#define TEGRA_GPIO_PB5 13 -#define TEGRA_GPIO_PB6 14 -#define TEGRA_GPIO_PB7 15 -#define TEGRA_GPIO_PC0 16 -#define TEGRA_GPIO_PC1 17 -#define TEGRA_GPIO_PC2 18 -#define TEGRA_GPIO_PC3 19 -#define TEGRA_GPIO_PC4 20 -#define TEGRA_GPIO_PC5 21 -#define TEGRA_GPIO_PC6 22 -#define TEGRA_GPIO_PC7 23 -#define TEGRA_GPIO_PD0 24 -#define TEGRA_GPIO_PD1 25 -#define TEGRA_GPIO_PD2 26 -#define TEGRA_GPIO_PD3 27 -#define TEGRA_GPIO_PD4 28 -#define TEGRA_GPIO_PD5 29 -#define TEGRA_GPIO_PD6 30 -#define TEGRA_GPIO_PD7 31 -#define TEGRA_GPIO_PE0 32 -#define TEGRA_GPIO_PE1 33 -#define TEGRA_GPIO_PE2 34 -#define TEGRA_GPIO_PE3 35 -#define TEGRA_GPIO_PE4 36 -#define TEGRA_GPIO_PE5 37 -#define TEGRA_GPIO_PE6 38 -#define TEGRA_GPIO_PE7 39 -#define TEGRA_GPIO_PF0 40 -#define TEGRA_GPIO_PF1 41 -#define TEGRA_GPIO_PF2 42 -#define TEGRA_GPIO_PF3 43 -#define TEGRA_GPIO_PF4 44 -#define TEGRA_GPIO_PF5 45 -#define TEGRA_GPIO_PF6 46 -#define TEGRA_GPIO_PF7 47 -#define TEGRA_GPIO_PG0 48 -#define TEGRA_GPIO_PG1 49 -#define TEGRA_GPIO_PG2 50 -#define TEGRA_GPIO_PG3 51 -#define TEGRA_GPIO_PG4 52 -#define TEGRA_GPIO_PG5 53 -#define TEGRA_GPIO_PG6 54 -#define TEGRA_GPIO_PG7 55 -#define TEGRA_GPIO_PH0 56 -#define TEGRA_GPIO_PH1 57 -#define TEGRA_GPIO_PH2 58 -#define TEGRA_GPIO_PH3 59 -#define TEGRA_GPIO_PH4 60 -#define TEGRA_GPIO_PH5 61 -#define TEGRA_GPIO_PH6 62 -#define TEGRA_GPIO_PH7 63 -#define TEGRA_GPIO_PI0 64 -#define TEGRA_GPIO_PI1 65 -#define TEGRA_GPIO_PI2 66 -#define TEGRA_GPIO_PI3 67 -#define TEGRA_GPIO_PI4 68 -#define TEGRA_GPIO_PI5 69 -#define TEGRA_GPIO_PI6 70 -#define TEGRA_GPIO_PI7 71 -#define TEGRA_GPIO_PJ0 72 -#define TEGRA_GPIO_PJ1 73 -#define TEGRA_GPIO_PJ2 74 -#define TEGRA_GPIO_PJ3 75 -#define TEGRA_GPIO_PJ4 76 -#define TEGRA_GPIO_PJ5 77 -#define TEGRA_GPIO_PJ6 78 -#define TEGRA_GPIO_PJ7 79 -#define TEGRA_GPIO_PK0 80 -#define TEGRA_GPIO_PK1 81 -#define TEGRA_GPIO_PK2 82 -#define TEGRA_GPIO_PK3 83 -#define TEGRA_GPIO_PK4 84 -#define TEGRA_GPIO_PK5 85 -#define TEGRA_GPIO_PK6 86 -#define TEGRA_GPIO_PK7 87 -#define TEGRA_GPIO_PL0 88 -#define TEGRA_GPIO_PL1 89 -#define TEGRA_GPIO_PL2 90 -#define TEGRA_GPIO_PL3 91 -#define TEGRA_GPIO_PL4 92 -#define TEGRA_GPIO_PL5 93 -#define TEGRA_GPIO_PL6 94 -#define TEGRA_GPIO_PL7 95 -#define TEGRA_GPIO_PM0 96 -#define TEGRA_GPIO_PM1 97 -#define TEGRA_GPIO_PM2 98 -#define TEGRA_GPIO_PM3 99 -#define TEGRA_GPIO_PM4 100 -#define TEGRA_GPIO_PM5 101 -#define TEGRA_GPIO_PM6 102 -#define TEGRA_GPIO_PM7 103 -#define TEGRA_GPIO_PN0 104 -#define TEGRA_GPIO_PN1 105 -#define TEGRA_GPIO_PN2 106 -#define TEGRA_GPIO_PN3 107 -#define TEGRA_GPIO_PN4 108 -#define TEGRA_GPIO_PN5 109 -#define TEGRA_GPIO_PN6 110 -#define TEGRA_GPIO_PN7 111 -#define TEGRA_GPIO_PO0 112 -#define TEGRA_GPIO_PO1 113 -#define TEGRA_GPIO_PO2 114 -#define TEGRA_GPIO_PO3 115 -#define TEGRA_GPIO_PO4 116 -#define TEGRA_GPIO_PO5 117 -#define TEGRA_GPIO_PO6 118 -#define TEGRA_GPIO_PO7 119 -#define TEGRA_GPIO_PP0 120 -#define TEGRA_GPIO_PP1 121 -#define TEGRA_GPIO_PP2 122 -#define TEGRA_GPIO_PP3 123 -#define TEGRA_GPIO_PP4 124 -#define TEGRA_GPIO_PP5 125 -#define TEGRA_GPIO_PP6 126 -#define TEGRA_GPIO_PP7 127 -#define TEGRA_GPIO_PQ0 128 -#define TEGRA_GPIO_PQ1 129 -#define TEGRA_GPIO_PQ2 130 -#define TEGRA_GPIO_PQ3 131 -#define TEGRA_GPIO_PQ4 132 -#define TEGRA_GPIO_PQ5 133 -#define TEGRA_GPIO_PQ6 134 -#define TEGRA_GPIO_PQ7 135 -#define TEGRA_GPIO_PR0 136 -#define TEGRA_GPIO_PR1 137 -#define TEGRA_GPIO_PR2 138 -#define TEGRA_GPIO_PR3 139 -#define TEGRA_GPIO_PR4 140 -#define TEGRA_GPIO_PR5 141 -#define TEGRA_GPIO_PR6 142 -#define TEGRA_GPIO_PR7 143 -#define TEGRA_GPIO_PS0 144 -#define TEGRA_GPIO_PS1 145 -#define TEGRA_GPIO_PS2 146 -#define TEGRA_GPIO_PS3 147 -#define TEGRA_GPIO_PS4 148 -#define TEGRA_GPIO_PS5 149 -#define TEGRA_GPIO_PS6 150 -#define TEGRA_GPIO_PS7 151 -#define TEGRA_GPIO_PT0 152 -#define TEGRA_GPIO_PT1 153 -#define TEGRA_GPIO_PT2 154 -#define TEGRA_GPIO_PT3 155 -#define TEGRA_GPIO_PT4 156 -#define TEGRA_GPIO_PT5 157 -#define TEGRA_GPIO_PT6 158 -#define TEGRA_GPIO_PT7 159 -#define TEGRA_GPIO_PU0 160 -#define TEGRA_GPIO_PU1 161 -#define TEGRA_GPIO_PU2 162 -#define TEGRA_GPIO_PU3 163 -#define TEGRA_GPIO_PU4 164 -#define TEGRA_GPIO_PU5 165 -#define TEGRA_GPIO_PU6 166 -#define TEGRA_GPIO_PU7 167 -#define TEGRA_GPIO_PV0 168 -#define TEGRA_GPIO_PV1 169 -#define TEGRA_GPIO_PV2 170 -#define TEGRA_GPIO_PV3 171 -#define TEGRA_GPIO_PV4 172 -#define TEGRA_GPIO_PV5 173 -#define TEGRA_GPIO_PV6 174 -#define TEGRA_GPIO_PV7 175 -#define TEGRA_GPIO_PW0 176 -#define TEGRA_GPIO_PW1 177 -#define TEGRA_GPIO_PW2 178 -#define TEGRA_GPIO_PW3 179 -#define TEGRA_GPIO_PW4 180 -#define TEGRA_GPIO_PW5 181 -#define TEGRA_GPIO_PW6 182 -#define TEGRA_GPIO_PW7 183 -#define TEGRA_GPIO_PX0 184 -#define TEGRA_GPIO_PX1 185 -#define TEGRA_GPIO_PX2 186 -#define TEGRA_GPIO_PX3 187 -#define TEGRA_GPIO_PX4 188 -#define TEGRA_GPIO_PX5 189 -#define TEGRA_GPIO_PX6 190 -#define TEGRA_GPIO_PX7 191 -#define TEGRA_GPIO_PY0 192 -#define TEGRA_GPIO_PY1 193 -#define TEGRA_GPIO_PY2 194 -#define TEGRA_GPIO_PY3 195 -#define TEGRA_GPIO_PY4 196 -#define TEGRA_GPIO_PY5 197 -#define TEGRA_GPIO_PY6 198 -#define TEGRA_GPIO_PY7 199 -#define TEGRA_GPIO_PZ0 200 -#define TEGRA_GPIO_PZ1 201 -#define TEGRA_GPIO_PZ2 202 -#define TEGRA_GPIO_PZ3 203 -#define TEGRA_GPIO_PZ4 204 -#define TEGRA_GPIO_PZ5 205 -#define TEGRA_GPIO_PZ6 206 -#define TEGRA_GPIO_PZ7 207 -#define TEGRA_GPIO_PAA0 208 -#define TEGRA_GPIO_PAA1 209 -#define TEGRA_GPIO_PAA2 210 -#define TEGRA_GPIO_PAA3 211 -#define TEGRA_GPIO_PAA4 212 -#define TEGRA_GPIO_PAA5 213 -#define TEGRA_GPIO_PAA6 214 -#define TEGRA_GPIO_PAA7 215 -#define TEGRA_GPIO_PBB0 216 -#define TEGRA_GPIO_PBB1 217 -#define TEGRA_GPIO_PBB2 218 -#define TEGRA_GPIO_PBB3 219 -#define TEGRA_GPIO_PBB4 220 -#define TEGRA_GPIO_PBB5 221 -#define TEGRA_GPIO_PBB6 222 -#define TEGRA_GPIO_PBB7 223 - -#endif diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S index 045c16f2dd5..2072e7322c3 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -6,6 +6,7 @@ .section ".text.head", "ax" ENTRY(tegra_secondary_startup) - bl v7_invalidate_l1 + check_cpu_part_num 0xc09, r8, r9 + bleq v7_invalidate_l1 b secondary_startup ENDPROC(tegra_secondary_startup) diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index a52c10e0a85..ff26af26bd0 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -37,7 +37,7 @@ int tegra_cpu_kill(unsigned cpu) void __ref tegra_cpu_die(unsigned int cpu) { /* Clean L1 data cache */ - tegra_disable_clean_inv_dcache(); + tegra_disable_clean_inv_dcache(TEGRA_FLUSH_CACHE_LOUIS); /* Shut down the current CPU. */ tegra_hotplug_shutdown(); @@ -46,17 +46,6 @@ void __ref tegra_cpu_die(unsigned int cpu) BUG(); } -int tegra_cpu_disable(unsigned int cpu) -{ - switch (tegra_chip_id) { - case TEGRA20: - case TEGRA30: - return cpu == 0 ? -EPERM : 0; - default: - return 0; - } -} - void __init tegra_hotplug_init(void) { if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) @@ -68,4 +57,6 @@ void __init tegra_hotplug_init(void) tegra_hotplug_shutdown = tegra30_hotplug_shutdown; if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) tegra_hotplug_shutdown = tegra30_hotplug_shutdown; + if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_chip_id == TEGRA124) + tegra_hotplug_shutdown = tegra30_hotplug_shutdown; } diff --git a/arch/arm/mach-tegra/iomap.h b/arch/arm/mach-tegra/iomap.h index 399fbca2710..26b1c2ad0ce 100644 --- a/arch/arm/mach-tegra/iomap.h +++ b/arch/arm/mach-tegra/iomap.h @@ -24,42 +24,12 @@ #define TEGRA_IRAM_BASE 0x40000000 #define TEGRA_IRAM_SIZE SZ_256K -#define TEGRA_HOST1X_BASE 0x50000000 -#define TEGRA_HOST1X_SIZE 0x24000 - #define TEGRA_ARM_PERIF_BASE 0x50040000 #define TEGRA_ARM_PERIF_SIZE SZ_8K -#define TEGRA_ARM_PL310_BASE 0x50043000 -#define TEGRA_ARM_PL310_SIZE SZ_4K - #define TEGRA_ARM_INT_DIST_BASE 0x50041000 #define TEGRA_ARM_INT_DIST_SIZE SZ_4K -#define TEGRA_MPE_BASE 0x54040000 -#define TEGRA_MPE_SIZE SZ_256K - -#define TEGRA_VI_BASE 0x54080000 -#define TEGRA_VI_SIZE SZ_256K - -#define TEGRA_ISP_BASE 0x54100000 -#define TEGRA_ISP_SIZE SZ_256K - -#define TEGRA_DISPLAY_BASE 0x54200000 -#define TEGRA_DISPLAY_SIZE SZ_256K - -#define TEGRA_DISPLAY2_BASE 0x54240000 -#define TEGRA_DISPLAY2_SIZE SZ_256K - -#define TEGRA_HDMI_BASE 0x54280000 -#define TEGRA_HDMI_SIZE SZ_256K - -#define TEGRA_GART_BASE 0x58000000 -#define TEGRA_GART_SIZE SZ_32M - -#define TEGRA_RES_SEMA_BASE 0x60001000 -#define TEGRA_RES_SEMA_SIZE SZ_4K - #define TEGRA_PRIMARY_ICTLR_BASE 0x60004000 #define TEGRA_PRIMARY_ICTLR_SIZE SZ_64 @@ -96,51 +66,15 @@ #define TEGRA_FLOW_CTRL_BASE 0x60007000 #define TEGRA_FLOW_CTRL_SIZE 20 -#define TEGRA_AHB_DMA_BASE 0x60008000 -#define TEGRA_AHB_DMA_SIZE SZ_4K - -#define TEGRA_AHB_DMA_CH0_BASE 0x60009000 -#define TEGRA_AHB_DMA_CH0_SIZE 32 - -#define TEGRA_APB_DMA_BASE 0x6000A000 -#define TEGRA_APB_DMA_SIZE SZ_4K - -#define TEGRA_APB_DMA_CH0_BASE 0x6000B000 -#define TEGRA_APB_DMA_CH0_SIZE 32 - -#define TEGRA_AHB_GIZMO_BASE 0x6000C004 -#define TEGRA_AHB_GIZMO_SIZE 0x10C - #define TEGRA_SB_BASE 0x6000C200 #define TEGRA_SB_SIZE 256 -#define TEGRA_STATMON_BASE 0x6000C400 -#define TEGRA_STATMON_SIZE SZ_1K - -#define TEGRA_GPIO_BASE 0x6000D000 -#define TEGRA_GPIO_SIZE SZ_4K - #define TEGRA_EXCEPTION_VECTORS_BASE 0x6000F000 #define TEGRA_EXCEPTION_VECTORS_SIZE SZ_4K #define TEGRA_APB_MISC_BASE 0x70000000 #define TEGRA_APB_MISC_SIZE SZ_4K -#define TEGRA_APB_MISC_DAS_BASE 0x70000c00 -#define TEGRA_APB_MISC_DAS_SIZE SZ_128 - -#define TEGRA_AC97_BASE 0x70002000 -#define TEGRA_AC97_SIZE SZ_512 - -#define TEGRA_SPDIF_BASE 0x70002400 -#define TEGRA_SPDIF_SIZE SZ_512 - -#define TEGRA_I2S1_BASE 0x70002800 -#define TEGRA_I2S1_SIZE SZ_256 - -#define TEGRA_I2S2_BASE 0x70002A00 -#define TEGRA_I2S2_SIZE SZ_256 - #define TEGRA_UARTA_BASE 0x70006000 #define TEGRA_UARTA_SIZE SZ_64 @@ -156,101 +90,26 @@ #define TEGRA_UARTE_BASE 0x70006400 #define TEGRA_UARTE_SIZE SZ_256 -#define TEGRA_NAND_BASE 0x70008000 -#define TEGRA_NAND_SIZE SZ_256 - -#define TEGRA_HSMMC_BASE 0x70008500 -#define TEGRA_HSMMC_SIZE SZ_256 - -#define TEGRA_SNOR_BASE 0x70009000 -#define TEGRA_SNOR_SIZE SZ_4K - -#define TEGRA_PWFM_BASE 0x7000A000 -#define TEGRA_PWFM_SIZE SZ_256 - -#define TEGRA_PWFM0_BASE 0x7000A000 -#define TEGRA_PWFM0_SIZE 4 - -#define TEGRA_PWFM1_BASE 0x7000A010 -#define TEGRA_PWFM1_SIZE 4 - -#define TEGRA_PWFM2_BASE 0x7000A020 -#define TEGRA_PWFM2_SIZE 4 - -#define TEGRA_PWFM3_BASE 0x7000A030 -#define TEGRA_PWFM3_SIZE 4 - -#define TEGRA_MIPI_BASE 0x7000B000 -#define TEGRA_MIPI_SIZE SZ_256 - -#define TEGRA_I2C_BASE 0x7000C000 -#define TEGRA_I2C_SIZE SZ_256 - -#define TEGRA_TWC_BASE 0x7000C100 -#define TEGRA_TWC_SIZE SZ_256 - -#define TEGRA_SPI_BASE 0x7000C380 -#define TEGRA_SPI_SIZE 48 - -#define TEGRA_I2C2_BASE 0x7000C400 -#define TEGRA_I2C2_SIZE SZ_256 - -#define TEGRA_I2C3_BASE 0x7000C500 -#define TEGRA_I2C3_SIZE SZ_256 - -#define TEGRA_OWR_BASE 0x7000C600 -#define TEGRA_OWR_SIZE 80 - -#define TEGRA_DVC_BASE 0x7000D000 -#define TEGRA_DVC_SIZE SZ_512 - -#define TEGRA_SPI1_BASE 0x7000D400 -#define TEGRA_SPI1_SIZE SZ_512 - -#define TEGRA_SPI2_BASE 0x7000D600 -#define TEGRA_SPI2_SIZE SZ_512 - -#define TEGRA_SPI3_BASE 0x7000D800 -#define TEGRA_SPI3_SIZE SZ_512 - -#define TEGRA_SPI4_BASE 0x7000DA00 -#define TEGRA_SPI4_SIZE SZ_512 - -#define TEGRA_RTC_BASE 0x7000E000 -#define TEGRA_RTC_SIZE SZ_256 - -#define TEGRA_KBC_BASE 0x7000E200 -#define TEGRA_KBC_SIZE SZ_256 - #define TEGRA_PMC_BASE 0x7000E400 #define TEGRA_PMC_SIZE SZ_256 -#define TEGRA_MC_BASE 0x7000F000 -#define TEGRA_MC_SIZE SZ_1K - #define TEGRA_EMC_BASE 0x7000F400 #define TEGRA_EMC_SIZE SZ_1K #define TEGRA_FUSE_BASE 0x7000F800 #define TEGRA_FUSE_SIZE SZ_1K -#define TEGRA_KFUSE_BASE 0x7000FC00 -#define TEGRA_KFUSE_SIZE SZ_1K +#define TEGRA_EMC0_BASE 0x7001A000 +#define TEGRA_EMC0_SIZE SZ_2K -#define TEGRA_CSITE_BASE 0x70040000 -#define TEGRA_CSITE_SIZE SZ_256K - -#define TEGRA_SDMMC1_BASE 0xC8000000 -#define TEGRA_SDMMC1_SIZE SZ_512 - -#define TEGRA_SDMMC2_BASE 0xC8000200 -#define TEGRA_SDMMC2_SIZE SZ_512 +#define TEGRA_EMC1_BASE 0x7001A800 +#define TEGRA_EMC1_SIZE SZ_2K -#define TEGRA_SDMMC3_BASE 0xC8000400 -#define TEGRA_SDMMC3_SIZE SZ_512 +#define TEGRA124_EMC_BASE 0x7001B000 +#define TEGRA124_EMC_SIZE SZ_2K -#define TEGRA_SDMMC4_BASE 0xC8000600 -#define TEGRA_SDMMC4_SIZE SZ_512 +#define TEGRA_CSITE_BASE 0x70040000 +#define TEGRA_CSITE_SIZE SZ_256K /* On TEGRA, many peripherals are very closely packed in * two 256MB io windows (that actually only use about 64KB @@ -278,9 +137,6 @@ #define IO_APB_VIRT IOMEM(0xFE300000) #define IO_APB_SIZE SZ_1M -#define TEGRA_PCIE_BASE 0x80000000 -#define TEGRA_PCIE_IO_BASE (TEGRA_PCIE_BASE + SZ_4M) - #define IO_TO_VIRT_BETWEEN(p, st, sz) ((p) >= (st) && (p) < ((st) + (sz))) #define IO_TO_VIRT_XLATE(p, pst, vst) (((p) - (pst) + (vst))) diff --git a/arch/arm/mach-tegra/irammap.h b/arch/arm/mach-tegra/irammap.h index 501952a8434..e32e1742c9a 100644 --- a/arch/arm/mach-tegra/irammap.h +++ b/arch/arm/mach-tegra/irammap.h @@ -23,4 +23,10 @@ #define TEGRA_IRAM_RESET_HANDLER_OFFSET 0 #define TEGRA_IRAM_RESET_HANDLER_SIZE SZ_1K +/* + * This area is used for LPx resume vector, only while LPx power state is + * active. At other times, the AVP may use this area for arbitrary purposes + */ +#define TEGRA_IRAM_LPx_RESUME_AREA (TEGRA_IRAM_BASE + SZ_4K) + #endif diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index 0de4eed1493..1a74d562dca 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -18,10 +18,12 @@ */ #include <linux/kernel.h> +#include <linux/cpu_pm.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/irqchip/arm-gic.h> #include <linux/syscore_ops.h> @@ -65,6 +67,7 @@ static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS]; static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS]; static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; +static void __iomem *tegra_gic_cpu_base; #endif bool tegra_pending_sgi(void) @@ -213,8 +216,43 @@ int tegra_legacy_irq_syscore_init(void) return 0; } + +static int tegra_gic_notifier(struct notifier_block *self, + unsigned long cmd, void *v) +{ + switch (cmd) { + case CPU_PM_ENTER: + writel_relaxed(0x1E0, tegra_gic_cpu_base + GIC_CPU_CTRL); + break; + } + + return NOTIFY_OK; +} + +static struct notifier_block tegra_gic_notifier_block = { + .notifier_call = tegra_gic_notifier, +}; + +static const struct of_device_id tegra114_dt_gic_match[] __initconst = { + { .compatible = "arm,cortex-a15-gic" }, + { } +}; + +static void tegra114_gic_cpu_pm_registration(void) +{ + struct device_node *dn; + + dn = of_find_matching_node(NULL, tegra114_dt_gic_match); + if (!dn) + return; + + tegra_gic_cpu_base = of_iomap(dn, 1); + + cpu_pm_register_notifier(&tegra_gic_notifier_block); +} #else #define tegra_set_wake NULL +static void tegra114_gic_cpu_pm_registration(void) { } #endif void __init tegra_init_irq(void) @@ -252,4 +290,6 @@ void __init tegra_init_irq(void) if (!of_have_populated_dt()) gic_init(0, 29, distbase, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); + + tegra114_gic_cpu_pm_registration(); } diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c deleted file mode 100644 index 46144a19a7e..00000000000 --- a/arch/arm/mach-tegra/pcie.c +++ /dev/null @@ -1,886 +0,0 @@ -/* - * arch/arm/mach-tegra/pci.c - * - * PCIe host controller driver for TEGRA(2) SOCs - * - * Copyright (c) 2010, CompuLab, Ltd. - * Author: Mike Rapoport <mike@compulab.co.il> - * - * Based on NVIDIA PCIe driver - * Copyright (c) 2008-2009, NVIDIA Corporation. - * - * Bits taken from arch/arm/mach-dove/pcie.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include <linux/kernel.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/export.h> -#include <linux/clk/tegra.h> -#include <linux/tegra-powergate.h> - -#include <asm/sizes.h> -#include <asm/mach/pci.h> - -#include "board.h" -#include "iomap.h" - -/* Hack - need to parse this from DT */ -#define INT_PCIE_INTR 130 - -/* register definitions */ -#define AFI_OFFSET 0x3800 -#define PADS_OFFSET 0x3000 -#define RP0_OFFSET 0x0000 -#define RP1_OFFSET 0x1000 - -#define AFI_AXI_BAR0_SZ 0x00 -#define AFI_AXI_BAR1_SZ 0x04 -#define AFI_AXI_BAR2_SZ 0x08 -#define AFI_AXI_BAR3_SZ 0x0c -#define AFI_AXI_BAR4_SZ 0x10 -#define AFI_AXI_BAR5_SZ 0x14 - -#define AFI_AXI_BAR0_START 0x18 -#define AFI_AXI_BAR1_START 0x1c -#define AFI_AXI_BAR2_START 0x20 -#define AFI_AXI_BAR3_START 0x24 -#define AFI_AXI_BAR4_START 0x28 -#define AFI_AXI_BAR5_START 0x2c - -#define AFI_FPCI_BAR0 0x30 -#define AFI_FPCI_BAR1 0x34 -#define AFI_FPCI_BAR2 0x38 -#define AFI_FPCI_BAR3 0x3c -#define AFI_FPCI_BAR4 0x40 -#define AFI_FPCI_BAR5 0x44 - -#define AFI_CACHE_BAR0_SZ 0x48 -#define AFI_CACHE_BAR0_ST 0x4c -#define AFI_CACHE_BAR1_SZ 0x50 -#define AFI_CACHE_BAR1_ST 0x54 - -#define AFI_MSI_BAR_SZ 0x60 -#define AFI_MSI_FPCI_BAR_ST 0x64 -#define AFI_MSI_AXI_BAR_ST 0x68 - -#define AFI_CONFIGURATION 0xac -#define AFI_CONFIGURATION_EN_FPCI (1 << 0) - -#define AFI_FPCI_ERROR_MASKS 0xb0 - -#define AFI_INTR_MASK 0xb4 -#define AFI_INTR_MASK_INT_MASK (1 << 0) -#define AFI_INTR_MASK_MSI_MASK (1 << 8) - -#define AFI_INTR_CODE 0xb8 -#define AFI_INTR_CODE_MASK 0xf -#define AFI_INTR_MASTER_ABORT 4 -#define AFI_INTR_LEGACY 6 - -#define AFI_INTR_SIGNATURE 0xbc -#define AFI_SM_INTR_ENABLE 0xc4 - -#define AFI_AFI_INTR_ENABLE 0xc8 -#define AFI_INTR_EN_INI_SLVERR (1 << 0) -#define AFI_INTR_EN_INI_DECERR (1 << 1) -#define AFI_INTR_EN_TGT_SLVERR (1 << 2) -#define AFI_INTR_EN_TGT_DECERR (1 << 3) -#define AFI_INTR_EN_TGT_WRERR (1 << 4) -#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) -#define AFI_INTR_EN_AXI_DECERR (1 << 6) -#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) - -#define AFI_PCIE_CONFIG 0x0f8 -#define AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE (1 << 1) -#define AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE (1 << 2) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) -#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) - -#define AFI_FUSE 0x104 -#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) - -#define AFI_PEX0_CTRL 0x110 -#define AFI_PEX1_CTRL 0x118 -#define AFI_PEX_CTRL_RST (1 << 0) -#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) - -#define RP_VEND_XP 0x00000F00 -#define RP_VEND_XP_DL_UP (1 << 30) - -#define RP_LINK_CONTROL_STATUS 0x00000090 -#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 - -#define PADS_CTL_SEL 0x0000009C - -#define PADS_CTL 0x000000A0 -#define PADS_CTL_IDDQ_1L (1 << 0) -#define PADS_CTL_TX_DATA_EN_1L (1 << 6) -#define PADS_CTL_RX_DATA_EN_1L (1 << 10) - -#define PADS_PLL_CTL 0x000000B8 -#define PADS_PLL_CTL_RST_B4SM (1 << 1) -#define PADS_PLL_CTL_LOCKDET (1 << 8) -#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) -#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16) -#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16) -#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16) -#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) -#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20) -#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20) - -/* PMC access is required for PCIE xclk (un)clamping */ -#define PMC_SCRATCH42 0x144 -#define PMC_SCRATCH42_PCX_CLAMP (1 << 0) - -static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); - -#define pmc_writel(value, reg) \ - __raw_writel(value, reg_pmc_base + (reg)) -#define pmc_readl(reg) \ - __raw_readl(reg_pmc_base + (reg)) - -/* - * Tegra2 defines 1GB in the AXI address map for PCIe. - * - * That address space is split into different regions, with sizes and - * offsets as follows: - * - * 0x80000000 - 0x80003fff - PCI controller registers - * 0x80004000 - 0x80103fff - PCI configuration space - * 0x80104000 - 0x80203fff - PCI extended configuration space - * 0x80203fff - 0x803fffff - unused - * 0x80400000 - 0x8040ffff - downstream IO - * 0x80410000 - 0x8fffffff - unused - * 0x90000000 - 0x9fffffff - non-prefetchable memory - * 0xa0000000 - 0xbfffffff - prefetchable memory - */ -#define PCIE_REGS_SZ SZ_16K -#define PCIE_CFG_OFF PCIE_REGS_SZ -#define PCIE_CFG_SZ SZ_1M -#define PCIE_EXT_CFG_OFF (PCIE_CFG_SZ + PCIE_CFG_OFF) -#define PCIE_EXT_CFG_SZ SZ_1M -#define PCIE_IOMAP_SZ (PCIE_REGS_SZ + PCIE_CFG_SZ + PCIE_EXT_CFG_SZ) - -#define MEM_BASE_0 (TEGRA_PCIE_BASE + SZ_256M) -#define MEM_SIZE_0 SZ_128M -#define MEM_BASE_1 (MEM_BASE_0 + MEM_SIZE_0) -#define MEM_SIZE_1 SZ_128M -#define PREFETCH_MEM_BASE_0 (MEM_BASE_1 + MEM_SIZE_1) -#define PREFETCH_MEM_SIZE_0 SZ_128M -#define PREFETCH_MEM_BASE_1 (PREFETCH_MEM_BASE_0 + PREFETCH_MEM_SIZE_0) -#define PREFETCH_MEM_SIZE_1 SZ_128M - -#define PCIE_CONF_BUS(b) ((b) << 16) -#define PCIE_CONF_DEV(d) ((d) << 11) -#define PCIE_CONF_FUNC(f) ((f) << 8) -#define PCIE_CONF_REG(r) \ - (((r) & ~0x3) | (((r) < 256) ? PCIE_CFG_OFF : PCIE_EXT_CFG_OFF)) - -struct tegra_pcie_port { - int index; - u8 root_bus_nr; - void __iomem *base; - - bool link_up; - - char mem_space_name[16]; - char prefetch_space_name[20]; - struct resource res[2]; -}; - -struct tegra_pcie_info { - struct tegra_pcie_port port[2]; - int num_ports; - - void __iomem *regs; - struct resource res_mmio; - - struct clk *pex_clk; - struct clk *afi_clk; - struct clk *pcie_xclk; - struct clk *pll_e; -}; - -static struct tegra_pcie_info tegra_pcie; - -static inline void afi_writel(u32 value, unsigned long offset) -{ - writel(value, offset + AFI_OFFSET + tegra_pcie.regs); -} - -static inline u32 afi_readl(unsigned long offset) -{ - return readl(offset + AFI_OFFSET + tegra_pcie.regs); -} - -static inline void pads_writel(u32 value, unsigned long offset) -{ - writel(value, offset + PADS_OFFSET + tegra_pcie.regs); -} - -static inline u32 pads_readl(unsigned long offset) -{ - return readl(offset + PADS_OFFSET + tegra_pcie.regs); -} - -static struct tegra_pcie_port *bus_to_port(int bus) -{ - int i; - - for (i = tegra_pcie.num_ports - 1; i >= 0; i--) { - int rbus = tegra_pcie.port[i].root_bus_nr; - if (rbus != -1 && rbus == bus) - break; - } - - return i >= 0 ? tegra_pcie.port + i : NULL; -} - -static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - struct tegra_pcie_port *pp = bus_to_port(bus->number); - void __iomem *addr; - - if (pp) { - if (devfn != 0) { - *val = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - addr = pp->base + (where & ~0x3); - } else { - addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + - PCIE_CONF_DEV(PCI_SLOT(devfn)) + - PCIE_CONF_FUNC(PCI_FUNC(devfn)) + - PCIE_CONF_REG(where)); - } - - *val = readl(addr); - - if (size == 1) - *val = (*val >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *val = (*val >> (8 * (where & 3))) & 0xffff; - - return PCIBIOS_SUCCESSFUL; -} - -static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - struct tegra_pcie_port *pp = bus_to_port(bus->number); - void __iomem *addr; - - u32 mask; - u32 tmp; - - if (pp) { - if (devfn != 0) - return PCIBIOS_DEVICE_NOT_FOUND; - - addr = pp->base + (where & ~0x3); - } else { - addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + - PCIE_CONF_DEV(PCI_SLOT(devfn)) + - PCIE_CONF_FUNC(PCI_FUNC(devfn)) + - PCIE_CONF_REG(where)); - } - - if (size == 4) { - writel(val, addr); - return PCIBIOS_SUCCESSFUL; - } - - if (size == 2) - mask = ~(0xffff << ((where & 0x3) * 8)); - else if (size == 1) - mask = ~(0xff << ((where & 0x3) * 8)); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - tmp = readl(addr) & mask; - tmp |= val << ((where & 0x3) * 8); - writel(tmp, addr); - - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops tegra_pcie_ops = { - .read = tegra_pcie_read_conf, - .write = tegra_pcie_write_conf, -}; - -static void tegra_pcie_fixup_bridge(struct pci_dev *dev) -{ - u16 reg; - - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { - pci_read_config_word(dev, PCI_COMMAND, ®); - reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR); - pci_write_config_word(dev, PCI_COMMAND, reg); - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); - -/* Tegra PCIE root complex wrongly reports device class */ -static void tegra_pcie_fixup_class(struct pci_dev *dev) -{ - dev->class = PCI_CLASS_BRIDGE_PCI << 8; -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class); -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); - -/* Tegra PCIE requires relaxed ordering */ -static void tegra_pcie_relax_enable(struct pci_dev *dev) -{ - pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); -} -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); - -static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) -{ - struct tegra_pcie_port *pp; - - if (nr >= tegra_pcie.num_ports) - return 0; - - pp = tegra_pcie.port + nr; - pp->root_bus_nr = sys->busnr; - - pci_ioremap_io(nr * SZ_64K, TEGRA_PCIE_IO_BASE); - - /* - * IORESOURCE_MEM - */ - snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), - "PCIe %d MEM", pp->index); - pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; - pp->res[0].name = pp->mem_space_name; - if (pp->index == 0) { - pp->res[0].start = MEM_BASE_0; - pp->res[0].end = pp->res[0].start + MEM_SIZE_0 - 1; - } else { - pp->res[0].start = MEM_BASE_1; - pp->res[0].end = pp->res[0].start + MEM_SIZE_1 - 1; - } - pp->res[0].flags = IORESOURCE_MEM; - if (request_resource(&iomem_resource, &pp->res[0])) - panic("Request PCIe Memory resource failed\n"); - pci_add_resource_offset(&sys->resources, &pp->res[0], sys->mem_offset); - - /* - * IORESOURCE_MEM | IORESOURCE_PREFETCH - */ - snprintf(pp->prefetch_space_name, sizeof(pp->prefetch_space_name), - "PCIe %d PREFETCH MEM", pp->index); - pp->prefetch_space_name[sizeof(pp->prefetch_space_name) - 1] = 0; - pp->res[1].name = pp->prefetch_space_name; - if (pp->index == 0) { - pp->res[1].start = PREFETCH_MEM_BASE_0; - pp->res[1].end = pp->res[1].start + PREFETCH_MEM_SIZE_0 - 1; - } else { - pp->res[1].start = PREFETCH_MEM_BASE_1; - pp->res[1].end = pp->res[1].start + PREFETCH_MEM_SIZE_1 - 1; - } - pp->res[1].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (request_resource(&iomem_resource, &pp->res[1])) - panic("Request PCIe Prefetch Memory resource failed\n"); - pci_add_resource_offset(&sys->resources, &pp->res[1], sys->mem_offset); - - return 1; -} - -static int tegra_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - return INT_PCIE_INTR; -} - -static struct pci_bus __init *tegra_pcie_scan_bus(int nr, - struct pci_sys_data *sys) -{ - struct tegra_pcie_port *pp; - - if (nr >= tegra_pcie.num_ports) - return NULL; - - pp = tegra_pcie.port + nr; - pp->root_bus_nr = sys->busnr; - - return pci_scan_root_bus(NULL, sys->busnr, &tegra_pcie_ops, sys, - &sys->resources); -} - -static struct hw_pci tegra_pcie_hw __initdata = { - .nr_controllers = 2, - .setup = tegra_pcie_setup, - .scan = tegra_pcie_scan_bus, - .map_irq = tegra_pcie_map_irq, -}; - - -static irqreturn_t tegra_pcie_isr(int irq, void *arg) -{ - const char *err_msg[] = { - "Unknown", - "AXI slave error", - "AXI decode error", - "Target abort", - "Master abort", - "Invalid write", - "Response decoding error", - "AXI response decoding error", - "Transcation timeout", - }; - - u32 code, signature; - - code = afi_readl(AFI_INTR_CODE) & AFI_INTR_CODE_MASK; - signature = afi_readl(AFI_INTR_SIGNATURE); - afi_writel(0, AFI_INTR_CODE); - - if (code == AFI_INTR_LEGACY) - return IRQ_NONE; - - if (code >= ARRAY_SIZE(err_msg)) - code = 0; - - /* - * do not pollute kernel log with master abort reports since they - * happen a lot during enumeration - */ - if (code == AFI_INTR_MASTER_ABORT) - pr_debug("PCIE: %s, signature: %08x\n", err_msg[code], signature); - else - pr_err("PCIE: %s, signature: %08x\n", err_msg[code], signature); - - return IRQ_HANDLED; -} - -static void tegra_pcie_setup_translations(void) -{ - u32 fpci_bar; - u32 size; - u32 axi_address; - - /* Bar 0: config Bar */ - fpci_bar = ((u32)0xfdff << 16); - size = PCIE_CFG_SZ; - axi_address = TEGRA_PCIE_BASE + PCIE_CFG_OFF; - afi_writel(axi_address, AFI_AXI_BAR0_START); - afi_writel(size >> 12, AFI_AXI_BAR0_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR0); - - /* Bar 1: extended config Bar */ - fpci_bar = ((u32)0xfe1 << 20); - size = PCIE_EXT_CFG_SZ; - axi_address = TEGRA_PCIE_BASE + PCIE_EXT_CFG_OFF; - afi_writel(axi_address, AFI_AXI_BAR1_START); - afi_writel(size >> 12, AFI_AXI_BAR1_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR1); - - /* Bar 2: downstream IO bar */ - fpci_bar = ((__u32)0xfdfc << 16); - size = SZ_128K; - axi_address = TEGRA_PCIE_IO_BASE; - afi_writel(axi_address, AFI_AXI_BAR2_START); - afi_writel(size >> 12, AFI_AXI_BAR2_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR2); - - /* Bar 3: prefetchable memory BAR */ - fpci_bar = (((PREFETCH_MEM_BASE_0 >> 12) & 0x0fffffff) << 4) | 0x1; - size = PREFETCH_MEM_SIZE_0 + PREFETCH_MEM_SIZE_1; - axi_address = PREFETCH_MEM_BASE_0; - afi_writel(axi_address, AFI_AXI_BAR3_START); - afi_writel(size >> 12, AFI_AXI_BAR3_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR3); - - /* Bar 4: non prefetchable memory BAR */ - fpci_bar = (((MEM_BASE_0 >> 12) & 0x0FFFFFFF) << 4) | 0x1; - size = MEM_SIZE_0 + MEM_SIZE_1; - axi_address = MEM_BASE_0; - afi_writel(axi_address, AFI_AXI_BAR4_START); - afi_writel(size >> 12, AFI_AXI_BAR4_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR4); - - /* Bar 5: NULL out the remaining BAR as it is not used */ - fpci_bar = 0; - size = 0; - axi_address = 0; - afi_writel(axi_address, AFI_AXI_BAR5_START); - afi_writel(size >> 12, AFI_AXI_BAR5_SZ); - afi_writel(fpci_bar, AFI_FPCI_BAR5); - - /* map all upstream transactions as uncached */ - afi_writel(PHYS_OFFSET, AFI_CACHE_BAR0_ST); - afi_writel(0, AFI_CACHE_BAR0_SZ); - afi_writel(0, AFI_CACHE_BAR1_ST); - afi_writel(0, AFI_CACHE_BAR1_SZ); - - /* No MSI */ - afi_writel(0, AFI_MSI_FPCI_BAR_ST); - afi_writel(0, AFI_MSI_BAR_SZ); - afi_writel(0, AFI_MSI_AXI_BAR_ST); - afi_writel(0, AFI_MSI_BAR_SZ); -} - -static int tegra_pcie_enable_controller(void) -{ - u32 val, reg; - int i, timeout; - - /* Enable slot clock and pulse the reset signals */ - for (i = 0, reg = AFI_PEX0_CTRL; i < 2; i++, reg += 0x8) { - val = afi_readl(reg) | AFI_PEX_CTRL_REFCLK_EN; - afi_writel(val, reg); - val &= ~AFI_PEX_CTRL_RST; - afi_writel(val, reg); - - val = afi_readl(reg) | AFI_PEX_CTRL_RST; - afi_writel(val, reg); - } - - /* Enable dual controller and both ports */ - val = afi_readl(AFI_PCIE_CONFIG); - val &= ~(AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE | - AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE | - AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK); - val |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; - afi_writel(val, AFI_PCIE_CONFIG); - - val = afi_readl(AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS; - afi_writel(val, AFI_FUSE); - - /* Initialze internal PHY, enable up to 16 PCIE lanes */ - pads_writel(0x0, PADS_CTL_SEL); - - /* override IDDQ to 1 on all 4 lanes */ - val = pads_readl(PADS_CTL) | PADS_CTL_IDDQ_1L; - pads_writel(val, PADS_CTL); - - /* - * set up PHY PLL inputs select PLLE output as refclock, - * set TX ref sel to div10 (not div5) - */ - val = pads_readl(PADS_PLL_CTL); - val &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); - val |= (PADS_PLL_CTL_REFCLK_INTERNAL_CML | PADS_PLL_CTL_TXCLKREF_DIV10); - pads_writel(val, PADS_PLL_CTL); - - /* take PLL out of reset */ - val = pads_readl(PADS_PLL_CTL) | PADS_PLL_CTL_RST_B4SM; - pads_writel(val, PADS_PLL_CTL); - - /* - * Hack, set the clock voltage to the DEFAULT provided by hw folks. - * This doesn't exist in the documentation - */ - pads_writel(0xfa5cfa5c, 0xc8); - - /* Wait for the PLL to lock */ - timeout = 300; - do { - val = pads_readl(PADS_PLL_CTL); - usleep_range(1000, 1000); - if (--timeout == 0) { - pr_err("Tegra PCIe error: timeout waiting for PLL\n"); - return -EBUSY; - } - } while (!(val & PADS_PLL_CTL_LOCKDET)); - - /* turn off IDDQ override */ - val = pads_readl(PADS_CTL) & ~PADS_CTL_IDDQ_1L; - pads_writel(val, PADS_CTL); - - /* enable TX/RX data */ - val = pads_readl(PADS_CTL); - val |= (PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); - pads_writel(val, PADS_CTL); - - /* Take the PCIe interface module out of reset */ - tegra_periph_reset_deassert(tegra_pcie.pcie_xclk); - - /* Finally enable PCIe */ - val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI; - afi_writel(val, AFI_CONFIGURATION); - - val = (AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | - AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | - AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR); - afi_writel(val, AFI_AFI_INTR_ENABLE); - afi_writel(0xffffffff, AFI_SM_INTR_ENABLE); - - /* FIXME: No MSI for now, only INT */ - afi_writel(AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); - - /* Disable all execptions */ - afi_writel(0, AFI_FPCI_ERROR_MASKS); - - return 0; -} - -static void tegra_pcie_xclk_clamp(bool clamp) -{ - u32 reg; - - reg = pmc_readl(PMC_SCRATCH42) & ~PMC_SCRATCH42_PCX_CLAMP; - - if (clamp) - reg |= PMC_SCRATCH42_PCX_CLAMP; - - pmc_writel(reg, PMC_SCRATCH42); -} - -static void tegra_pcie_power_off(void) -{ - tegra_periph_reset_assert(tegra_pcie.pcie_xclk); - tegra_periph_reset_assert(tegra_pcie.afi_clk); - tegra_periph_reset_assert(tegra_pcie.pex_clk); - - tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); - tegra_pcie_xclk_clamp(true); -} - -static int tegra_pcie_power_regate(void) -{ - int err; - - tegra_pcie_power_off(); - - tegra_pcie_xclk_clamp(true); - - tegra_periph_reset_assert(tegra_pcie.pcie_xclk); - tegra_periph_reset_assert(tegra_pcie.afi_clk); - - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, - tegra_pcie.pex_clk); - if (err) { - pr_err("PCIE: powerup sequence failed: %d\n", err); - return err; - } - - tegra_periph_reset_deassert(tegra_pcie.afi_clk); - - tegra_pcie_xclk_clamp(false); - - clk_prepare_enable(tegra_pcie.afi_clk); - clk_prepare_enable(tegra_pcie.pex_clk); - return clk_prepare_enable(tegra_pcie.pll_e); -} - -static int tegra_pcie_clocks_get(void) -{ - int err; - - tegra_pcie.pex_clk = clk_get(NULL, "pex"); - if (IS_ERR(tegra_pcie.pex_clk)) - return PTR_ERR(tegra_pcie.pex_clk); - - tegra_pcie.afi_clk = clk_get(NULL, "afi"); - if (IS_ERR(tegra_pcie.afi_clk)) { - err = PTR_ERR(tegra_pcie.afi_clk); - goto err_afi_clk; - } - - tegra_pcie.pcie_xclk = clk_get(NULL, "pcie_xclk"); - if (IS_ERR(tegra_pcie.pcie_xclk)) { - err = PTR_ERR(tegra_pcie.pcie_xclk); - goto err_pcie_xclk; - } - - tegra_pcie.pll_e = clk_get_sys(NULL, "pll_e"); - if (IS_ERR(tegra_pcie.pll_e)) { - err = PTR_ERR(tegra_pcie.pll_e); - goto err_pll_e; - } - - return 0; - -err_pll_e: - clk_put(tegra_pcie.pcie_xclk); -err_pcie_xclk: - clk_put(tegra_pcie.afi_clk); -err_afi_clk: - clk_put(tegra_pcie.pex_clk); - - return err; -} - -static void tegra_pcie_clocks_put(void) -{ - clk_put(tegra_pcie.pll_e); - clk_put(tegra_pcie.pcie_xclk); - clk_put(tegra_pcie.afi_clk); - clk_put(tegra_pcie.pex_clk); -} - -static int __init tegra_pcie_get_resources(void) -{ - int err; - - err = tegra_pcie_clocks_get(); - if (err) { - pr_err("PCIE: failed to get clocks: %d\n", err); - return err; - } - - err = tegra_pcie_power_regate(); - if (err) { - pr_err("PCIE: failed to power up: %d\n", err); - goto err_pwr_on; - } - - tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ); - if (tegra_pcie.regs == NULL) { - pr_err("PCIE: Failed to map PCI/AFI registers\n"); - err = -ENOMEM; - goto err_map_reg; - } - - err = request_irq(INT_PCIE_INTR, tegra_pcie_isr, - IRQF_SHARED, "PCIE", &tegra_pcie); - if (err) { - pr_err("PCIE: Failed to register IRQ: %d\n", err); - goto err_req_io; - } - set_irq_flags(INT_PCIE_INTR, IRQF_VALID); - - return 0; - -err_req_io: - iounmap(tegra_pcie.regs); -err_map_reg: - tegra_pcie_power_off(); -err_pwr_on: - tegra_pcie_clocks_put(); - - return err; -} - -/* - * FIXME: If there are no PCIe cards attached, then calling this function - * can result in the increase of the bootup time as there are big timeout - * loops. - */ -#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ -static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx, - u32 reset_reg) -{ - u32 reg; - int retries = 3; - int timeout; - - do { - timeout = TEGRA_PCIE_LINKUP_TIMEOUT; - while (timeout) { - reg = readl(pp->base + RP_VEND_XP); - - if (reg & RP_VEND_XP_DL_UP) - break; - - mdelay(1); - timeout--; - } - - if (!timeout) { - pr_err("PCIE: port %d: link down, retrying\n", idx); - goto retry; - } - - timeout = TEGRA_PCIE_LINKUP_TIMEOUT; - while (timeout) { - reg = readl(pp->base + RP_LINK_CONTROL_STATUS); - - if (reg & 0x20000000) - return true; - - mdelay(1); - timeout--; - } - -retry: - /* Pulse the PEX reset */ - reg = afi_readl(reset_reg) | AFI_PEX_CTRL_RST; - afi_writel(reg, reset_reg); - mdelay(1); - reg = afi_readl(reset_reg) & ~AFI_PEX_CTRL_RST; - afi_writel(reg, reset_reg); - - retries--; - } while (retries); - - return false; -} - -static void __init tegra_pcie_add_port(int index, u32 offset, u32 reset_reg) -{ - struct tegra_pcie_port *pp; - - pp = tegra_pcie.port + tegra_pcie.num_ports; - - pp->index = -1; - pp->base = tegra_pcie.regs + offset; - pp->link_up = tegra_pcie_check_link(pp, index, reset_reg); - - if (!pp->link_up) { - pp->base = NULL; - printk(KERN_INFO "PCIE: port %d: link down, ignoring\n", index); - return; - } - - tegra_pcie.num_ports++; - pp->index = index; - pp->root_bus_nr = -1; - memset(pp->res, 0, sizeof(pp->res)); -} - -int __init tegra_pcie_init(bool init_port0, bool init_port1) -{ - int err; - - if (!(init_port0 || init_port1)) - return -ENODEV; - - pcibios_min_mem = 0; - - err = tegra_pcie_get_resources(); - if (err) - return err; - - err = tegra_pcie_enable_controller(); - if (err) - return err; - - /* setup the AFI address translations */ - tegra_pcie_setup_translations(); - - if (init_port0) - tegra_pcie_add_port(0, RP0_OFFSET, AFI_PEX0_CTRL); - - if (init_port1) - tegra_pcie_add_port(1, RP1_OFFSET, AFI_PEX1_CTRL); - - pci_common_init(&tegra_pcie_hw); - - return 0; -} diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 97b33a2a2d7..eb72ae70912 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -176,6 +176,8 @@ static int tegra_boot_secondary(unsigned int cpu, return tegra30_boot_secondary(cpu, idle); if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) return tegra114_boot_secondary(cpu, idle); + if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_chip_id == TEGRA124) + return tegra114_boot_secondary(cpu, idle); return -EINVAL; } @@ -196,6 +198,5 @@ struct smp_operations tegra_smp_ops __initdata = { #ifdef CONFIG_HOTPLUG_CPU .cpu_kill = tegra_cpu_kill, .cpu_die = tegra_cpu_die, - .cpu_disable = tegra_cpu_disable, #endif }; diff --git a/arch/arm/mach-tegra/pm-tegra20.c b/arch/arm/mach-tegra/pm-tegra20.c new file mode 100644 index 00000000000..d65e1d78640 --- /dev/null +++ b/arch/arm/mach-tegra/pm-tegra20.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013, 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 <linux/kernel.h> + +#include "pm.h" + +#ifdef CONFIG_PM_SLEEP +extern u32 tegra20_iram_start, tegra20_iram_end; +extern void tegra20_sleep_core_finish(unsigned long); + +void tegra20_lp1_iram_hook(void) +{ + tegra_lp1_iram.start_addr = &tegra20_iram_start; + tegra_lp1_iram.end_addr = &tegra20_iram_end; +} + +void tegra20_sleep_core_init(void) +{ + tegra_sleep_core_finish = tegra20_sleep_core_finish; +} +#endif diff --git a/arch/arm/mach-tegra/pm-tegra30.c b/arch/arm/mach-tegra/pm-tegra30.c new file mode 100644 index 00000000000..8fa326d6ff1 --- /dev/null +++ b/arch/arm/mach-tegra/pm-tegra30.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013, 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 <linux/kernel.h> + +#include "pm.h" + +#ifdef CONFIG_PM_SLEEP +extern u32 tegra30_iram_start, tegra30_iram_end; +extern void tegra30_sleep_core_finish(unsigned long); + +void tegra30_lp1_iram_hook(void) +{ + tegra_lp1_iram.start_addr = &tegra30_iram_start; + tegra_lp1_iram.end_addr = &tegra30_iram_end; +} + +void tegra30_sleep_core_init(void) +{ + tegra_sleep_core_finish = tegra30_sleep_core_finish; +} +#endif diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 261fec140c0..4ae0286b468 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -37,12 +37,18 @@ #include "reset.h" #include "flowctrl.h" #include "fuse.h" +#include "pm.h" #include "pmc.h" #include "sleep.h" #ifdef CONFIG_PM_SLEEP static DEFINE_SPINLOCK(tegra_lp2_lock); +static u32 iram_save_size; +static void *iram_save_addr; +struct tegra_lp1_iram tegra_lp1_iram; void (*tegra_tear_down_cpu)(void); +void (*tegra_sleep_core_finish)(unsigned long v2p); +static int (*tegra_sleep_func)(unsigned long v2p); static void tegra_tear_down_cpu_init(void) { @@ -52,7 +58,11 @@ static void tegra_tear_down_cpu_init(void) tegra_tear_down_cpu = tegra20_tear_down_cpu; break; case TEGRA30: - if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC)) + case TEGRA114: + case TEGRA124: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)) tegra_tear_down_cpu = tegra30_tear_down_cpu; break; } @@ -171,19 +181,113 @@ void tegra_idle_lp2_last(void) enum tegra_suspend_mode tegra_pm_validate_suspend_mode( enum tegra_suspend_mode mode) { - /* Tegra114 didn't support any suspending mode yet. */ - if (tegra_chip_id == TEGRA114) - return TEGRA_SUSPEND_NONE; - /* - * The Tegra devices only support suspending to LP2 currently. + * The Tegra devices support suspending to LP1 or lower currently. */ - if (mode > TEGRA_SUSPEND_LP2) - return TEGRA_SUSPEND_LP2; + if (mode > TEGRA_SUSPEND_LP1) + return TEGRA_SUSPEND_LP1; return mode; } +static int tegra_sleep_core(unsigned long v2p) +{ + setup_mm_for_reboot(); + tegra_sleep_core_finish(v2p); + + /* should never here */ + BUG(); + + return 0; +} + +/* + * tegra_lp1_iram_hook + * + * Hooking the address of LP1 reset vector and SDRAM self-refresh code in + * SDRAM. These codes not be copied to IRAM in this fuction. We need to + * copy these code to IRAM before LP0/LP1 suspend and restore the content + * of IRAM after resume. + */ +static bool tegra_lp1_iram_hook(void) +{ + switch (tegra_chip_id) { + case TEGRA20: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra20_lp1_iram_hook(); + break; + case TEGRA30: + case TEGRA114: + case TEGRA124: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)) + tegra30_lp1_iram_hook(); + break; + default: + break; + } + + if (!tegra_lp1_iram.start_addr || !tegra_lp1_iram.end_addr) + return false; + + iram_save_size = tegra_lp1_iram.end_addr - tegra_lp1_iram.start_addr; + iram_save_addr = kmalloc(iram_save_size, GFP_KERNEL); + if (!iram_save_addr) + return false; + + return true; +} + +static bool tegra_sleep_core_init(void) +{ + switch (tegra_chip_id) { + case TEGRA20: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra20_sleep_core_init(); + break; + case TEGRA30: + case TEGRA114: + case TEGRA124: + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) || + IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC)) + tegra30_sleep_core_init(); + break; + default: + break; + } + + if (!tegra_sleep_core_finish) + return false; + + return true; +} + +static void tegra_suspend_enter_lp1(void) +{ + tegra_pmc_suspend(); + + /* copy the reset vector & SDRAM shutdown code into IRAM */ + memcpy(iram_save_addr, IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), + iram_save_size); + memcpy(IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), + tegra_lp1_iram.start_addr, iram_save_size); + + *((u32 *)tegra_cpu_lp1_mask) = 1; +} + +static void tegra_suspend_exit_lp1(void) +{ + tegra_pmc_resume(); + + /* restore IRAM */ + memcpy(IO_ADDRESS(TEGRA_IRAM_LPx_RESUME_AREA), iram_save_addr, + iram_save_size); + + *(u32 *)tegra_cpu_lp1_mask = 0; +} + static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { [TEGRA_SUSPEND_NONE] = "none", [TEGRA_SUSPEND_LP2] = "LP2", @@ -207,6 +311,9 @@ static int tegra_suspend_enter(suspend_state_t state) suspend_cpu_complex(); switch (mode) { + case TEGRA_SUSPEND_LP1: + tegra_suspend_enter_lp1(); + break; case TEGRA_SUSPEND_LP2: tegra_set_cpu_in_lp2(); break; @@ -214,9 +321,12 @@ static int tegra_suspend_enter(suspend_state_t state) break; } - cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); + cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, tegra_sleep_func); switch (mode) { + case TEGRA_SUSPEND_LP1: + tegra_suspend_exit_lp1(); + break; case TEGRA_SUSPEND_LP2: tegra_clear_cpu_in_lp2(); break; @@ -237,12 +347,36 @@ static const struct platform_suspend_ops tegra_suspend_ops = { void __init tegra_init_suspend(void) { - if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) + enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); + + if (mode == TEGRA_SUSPEND_NONE) return; tegra_tear_down_cpu_init(); tegra_pmc_suspend_init(); + if (mode >= TEGRA_SUSPEND_LP1) { + if (!tegra_lp1_iram_hook() || !tegra_sleep_core_init()) { + pr_err("%s: unable to allocate memory for SDRAM" + "self-refresh -- LP0/LP1 unavailable\n", + __func__); + tegra_pmc_set_suspend_mode(TEGRA_SUSPEND_LP2); + mode = TEGRA_SUSPEND_LP2; + } + } + + /* set up sleep function for cpu_suspend */ + switch (mode) { + case TEGRA_SUSPEND_LP1: + tegra_sleep_func = tegra_sleep_core; + break; + case TEGRA_SUSPEND_LP2: + tegra_sleep_func = tegra_sleep_cpu; + break; + default: + break; + } + suspend_set_ops(&tegra_suspend_ops); } #endif diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 94c4b9d9077..6e92a7c2ecb 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -23,10 +23,19 @@ #include "pmc.h" -extern unsigned long l2x0_saved_regs_addr; +struct tegra_lp1_iram { + void *start_addr; + void *end_addr; +}; +extern struct tegra_lp1_iram tegra_lp1_iram; +extern void (*tegra_sleep_core_finish)(unsigned long v2p); + +void tegra20_lp1_iram_hook(void); +void tegra20_sleep_core_init(void); +void tegra30_lp1_iram_hook(void); +void tegra30_sleep_core_init(void); -void save_cpu_arch_register(void); -void restore_cpu_arch_register(void); +extern unsigned long l2x0_saved_regs_addr; void tegra_clear_cpu_in_lp2(void); bool tegra_set_cpu_in_lp2(void); diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index eb3fa4aee0e..fb7920201ab 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -20,12 +20,16 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/tegra-powergate.h> +#include "flowctrl.h" #include "fuse.h" #include "pm.h" #include "pmc.h" #include "sleep.h" +#define TEGRA_POWER_SYSCLK_POLARITY (1 << 10) /* sys clk polarity */ +#define TEGRA_POWER_SYSCLK_OE (1 << 11) /* system clock enable */ #define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ #define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ #define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ @@ -40,12 +44,6 @@ #define PMC_CPUPWRGOOD_TIMER 0xc8 #define PMC_CPUPWROFF_TIMER 0xcc -#define TEGRA_POWERGATE_PCIE 3 -#define TEGRA_POWERGATE_VDEC 4 -#define TEGRA_POWERGATE_CPU1 9 -#define TEGRA_POWERGATE_CPU2 10 -#define TEGRA_POWERGATE_CPU3 11 - static u8 tegra_cpu_domains[] = { 0xFF, /* not available for CPU0 */ TEGRA_POWERGATE_CPU1, @@ -163,6 +161,15 @@ int tegra_pmc_cpu_remove_clamping(int cpuid) return tegra_pmc_powergate_remove_clamping(id); } +void tegra_pmc_restart(enum reboot_mode mode, const char *cmd) +{ + u32 val; + + val = tegra_pmc_readl(0); + val |= 0x10; + tegra_pmc_writel(val, 0); +} + #ifdef CONFIG_PM_SLEEP static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate) { @@ -193,16 +200,50 @@ enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) return pmc_pm_data.suspend_mode; } +void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) +{ + if (mode < TEGRA_SUSPEND_NONE || mode >= TEGRA_MAX_SUSPEND_MODE) + return; + + pmc_pm_data.suspend_mode = mode; +} + +void tegra_pmc_suspend(void) +{ + tegra_pmc_writel(virt_to_phys(tegra_resume), PMC_SCRATCH41); +} + +void tegra_pmc_resume(void) +{ + tegra_pmc_writel(0x0, PMC_SCRATCH41); +} + void tegra_pmc_pm_set(enum tegra_suspend_mode mode) { - u32 reg; + u32 reg, csr_reg; unsigned long rate = 0; reg = tegra_pmc_readl(PMC_CTRL); reg |= TEGRA_POWER_CPU_PWRREQ_OE; reg &= ~TEGRA_POWER_EFFECT_LP0; + switch (tegra_chip_id) { + case TEGRA20: + case TEGRA30: + break; + default: + /* Turn off CRAIL */ + csr_reg = flowctrl_read_cpu_csr(0); + csr_reg &= ~FLOW_CTRL_CSR_ENABLE_EXT_MASK; + csr_reg |= FLOW_CTRL_CSR_ENABLE_EXT_CRAIL; + flowctrl_write_cpu_csr(0, csr_reg); + break; + } + switch (mode) { + case TEGRA_SUSPEND_LP1: + rate = 32768; + break; case TEGRA_SUSPEND_LP2: rate = clk_get_rate(tegra_pclk); break; @@ -224,23 +265,35 @@ void tegra_pmc_suspend_init(void) reg = tegra_pmc_readl(PMC_CTRL); reg |= TEGRA_POWER_CPU_PWRREQ_OE; tegra_pmc_writel(reg, PMC_CTRL); + + reg = tegra_pmc_readl(PMC_CTRL); + + if (!pmc_pm_data.sysclkreq_high) + reg |= TEGRA_POWER_SYSCLK_POLARITY; + else + reg &= ~TEGRA_POWER_SYSCLK_POLARITY; + + /* configure the output polarity while the request is tristated */ + tegra_pmc_writel(reg, PMC_CTRL); + + /* now enable the request */ + reg |= TEGRA_POWER_SYSCLK_OE; + tegra_pmc_writel(reg, PMC_CTRL); } #endif static const struct of_device_id matches[] __initconst = { + { .compatible = "nvidia,tegra124-pmc" }, { .compatible = "nvidia,tegra114-pmc" }, { .compatible = "nvidia,tegra30-pmc" }, { .compatible = "nvidia,tegra20-pmc" }, { } }; -static void __init tegra_pmc_parse_dt(void) +void __init tegra_pmc_init_irq(void) { struct device_node *np; - u32 prop; - enum tegra_suspend_mode suspend_mode; - u32 core_good_time[2] = {0, 0}; - u32 lp0_vec[2] = {0, 0}; + u32 val; np = of_find_matching_node(NULL, matches); BUG_ON(!np); @@ -249,6 +302,26 @@ static void __init tegra_pmc_parse_dt(void) tegra_pmc_invert_interrupt = of_property_read_bool(np, "nvidia,invert-interrupt"); + + val = tegra_pmc_readl(PMC_CTRL); + if (tegra_pmc_invert_interrupt) + val |= PMC_CTRL_INTR_LOW; + else + val &= ~PMC_CTRL_INTR_LOW; + tegra_pmc_writel(val, PMC_CTRL); +} + +void __init tegra_pmc_init(void) +{ + struct device_node *np; + u32 prop; + enum tegra_suspend_mode suspend_mode; + u32 core_good_time[2] = {0, 0}; + u32 lp0_vec[2] = {0, 0}; + + np = of_find_matching_node(NULL, matches); + BUG_ON(!np); + tegra_pclk = of_clk_get_by_name(np, "pclk"); WARN_ON(IS_ERR(tegra_pclk)); @@ -314,17 +387,3 @@ static void __init tegra_pmc_parse_dt(void) pmc_pm_data.suspend_mode = suspend_mode; } - -void __init tegra_pmc_init(void) -{ - u32 val; - - tegra_pmc_parse_dt(); - - val = tegra_pmc_readl(PMC_CTRL); - if (tegra_pmc_invert_interrupt) - val |= PMC_CTRL_INTR_LOW; - else - val &= ~PMC_CTRL_INTR_LOW; - tegra_pmc_writel(val, PMC_CTRL); -} diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index e1c2df272f7..59e19c34429 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -18,6 +18,8 @@ #ifndef __MACH_TEGRA_PMC_H #define __MACH_TEGRA_PMC_H +#include <linux/reboot.h> + enum tegra_suspend_mode { TEGRA_SUSPEND_NONE = 0, TEGRA_SUSPEND_LP2, /* CPU voltage off */ @@ -28,6 +30,9 @@ enum tegra_suspend_mode { #ifdef CONFIG_PM_SLEEP enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); +void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); +void tegra_pmc_suspend(void); +void tegra_pmc_resume(void); void tegra_pmc_pm_set(enum tegra_suspend_mode mode); void tegra_pmc_suspend_init(void); #endif @@ -36,6 +41,9 @@ bool tegra_pmc_cpu_is_powered(int cpuid); int tegra_pmc_cpu_power_on(int cpuid); int tegra_pmc_cpu_remove_clamping(int cpuid); +void tegra_pmc_restart(enum reboot_mode mode, const char *cmd); + +void tegra_pmc_init_irq(void); void tegra_pmc_init(void); #endif diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index f076f0f80fc..85d28e756bb 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -42,8 +42,16 @@ static int tegra_num_powerdomains; static int tegra_num_cpu_domains; -static u8 *tegra_cpu_domains; -static u8 tegra30_cpu_domains[] = { +static const u8 *tegra_cpu_domains; + +static const u8 tegra30_cpu_domains[] = { + TEGRA_POWERGATE_CPU, + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; + +static const u8 tegra114_cpu_domains[] = { TEGRA_POWERGATE_CPU0, TEGRA_POWERGATE_CPU1, TEGRA_POWERGATE_CPU2, @@ -189,6 +197,11 @@ int __init tegra_powergate_init(void) tegra_num_cpu_domains = 4; tegra_cpu_domains = tegra30_cpu_domains; break; + case TEGRA114: + tegra_num_powerdomains = 23; + tegra_num_cpu_domains = 4; + tegra_cpu_domains = tegra114_cpu_domains; + break; default: /* Unknown Tegra variant. Disable powergating */ tegra_num_powerdomains = 0; @@ -229,6 +242,27 @@ static const char * const powergate_name_t30[] = { [TEGRA_POWERGATE_3D1] = "3d1", }; +static const char * const powergate_name_t114[] = { + [TEGRA_POWERGATE_CPU] = "cpu0", + [TEGRA_POWERGATE_3D] = "3d", + [TEGRA_POWERGATE_VENC] = "venc", + [TEGRA_POWERGATE_VDEC] = "vdec", + [TEGRA_POWERGATE_MPE] = "mpe", + [TEGRA_POWERGATE_HEG] = "heg", + [TEGRA_POWERGATE_CPU1] = "cpu1", + [TEGRA_POWERGATE_CPU2] = "cpu2", + [TEGRA_POWERGATE_CPU3] = "cpu3", + [TEGRA_POWERGATE_CELP] = "celp", + [TEGRA_POWERGATE_CPU0] = "cpu0", + [TEGRA_POWERGATE_C0NC] = "c0nc", + [TEGRA_POWERGATE_C1NC] = "c1nc", + [TEGRA_POWERGATE_DIS] = "dis", + [TEGRA_POWERGATE_DISB] = "disb", + [TEGRA_POWERGATE_XUSBA] = "xusba", + [TEGRA_POWERGATE_XUSBB] = "xusbb", + [TEGRA_POWERGATE_XUSBC] = "xusbc", +}; + static int powergate_show(struct seq_file *s, void *data) { int i; @@ -236,9 +270,14 @@ static int powergate_show(struct seq_file *s, void *data) seq_printf(s, " powergate powered\n"); seq_printf(s, "------------------\n"); - for (i = 0; i < tegra_num_powerdomains; i++) + for (i = 0; i < tegra_num_powerdomains; i++) { + if (!powergate_name[i]) + continue; + seq_printf(s, " %9s %7s\n", powergate_name[i], tegra_powergate_is_powered(i) ? "yes" : "no"); + } + return 0; } @@ -265,6 +304,9 @@ int __init tegra_powergate_debugfs_init(void) case TEGRA30: powergate_name = powergate_name_t30; break; + case TEGRA114: + powergate_name = powergate_name_t114; + break; } if (powergate_name) { diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index 39dc9e7834f..8c1ba4fea38 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -40,19 +40,16 @@ * re-enabling sdram. * * r6: SoC ID + * r8: CPU part number */ ENTRY(tegra_resume) - bl v7_invalidate_l1 + check_cpu_part_num 0xc09, r8, r9 + bleq v7_invalidate_l1 cpu_id r0 - tegra_get_soc_id TEGRA_APB_MISC_BASE, r6 - cmp r6, #TEGRA114 - beq no_cpu0_chk - cmp r0, #0 @ CPU0? THUMB( it ne ) bne cpu_resume @ no -no_cpu0_chk: /* Are we on Tegra20? */ cmp r6, #TEGRA20 @@ -70,8 +67,9 @@ no_cpu0_chk: str r1, [r2] 1: - check_cpu_part_num 0xc09, r8, r9 - bne not_ca9 + mov32 r9, 0xc09 + cmp r8, r9 + bne end_ca9_scu_l2_resume #ifdef CONFIG_HAVE_ARM_SCU /* enable SCU */ mov32 r0, TEGRA_ARM_PERIF_BASE @@ -82,7 +80,10 @@ no_cpu0_chk: /* L2 cache resume & re-enable */ l2_cache_resume r0, r1, r2, l2x0_saved_regs_addr -not_ca9: +end_ca9_scu_l2_resume: + mov32 r9, 0xc0f + cmp r8, r9 + bleq tegra_init_l2_for_a15 b cpu_resume ENDPROC(tegra_resume) @@ -178,6 +179,19 @@ after_errata: 1: #endif + /* Waking up from LP1? */ + ldr r8, [r12, #RESET_DATA(MASK_LP1)] + tst r8, r11 @ if in_lp1 + beq __is_not_lp1 + cmp r10, #0 + bne __die @ only CPU0 can be here + ldr lr, [r12, #RESET_DATA(STARTUP_LP1)] + cmp lr, #0 + bleq __die @ no LP1 startup handler + THUMB( add lr, lr, #1 ) @ switch to Thumb mode + bx lr +__is_not_lp1: + /* Waking up from LP2? */ ldr r9, [r12, #RESET_DATA(MASK_LP2)] tst r9, r11 @ if in_lp2 diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c index 1ac434e0068..568f5bbf979 100644 --- a/arch/arm/mach-tegra/reset.c +++ b/arch/arm/mach-tegra/reset.c @@ -81,6 +81,8 @@ void __init tegra_cpu_reset_handler_init(void) #endif #ifdef CONFIG_PM_SLEEP + __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP1] = + TEGRA_IRAM_LPx_RESUME_AREA; __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_LP2] = virt_to_phys((void *)tegra_resume); #endif diff --git a/arch/arm/mach-tegra/reset.h b/arch/arm/mach-tegra/reset.h index c90d8e9c4ad..76a93434c6e 100644 --- a/arch/arm/mach-tegra/reset.h +++ b/arch/arm/mach-tegra/reset.h @@ -39,6 +39,10 @@ void __tegra_cpu_reset_handler_end(void); void tegra_secondary_startup(void); #ifdef CONFIG_PM_SLEEP +#define tegra_cpu_lp1_mask \ + (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ + ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \ + (u32)__tegra_cpu_reset_handler_start))) #define tegra_cpu_lp2_mask \ (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \ diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S index e3f2417c420..aaaf3abd268 100644 --- a/arch/arm/mach-tegra/sleep-tegra20.S +++ b/arch/arm/mach-tegra/sleep-tegra20.S @@ -23,10 +23,50 @@ #include <asm/assembler.h> #include <asm/proc-fns.h> #include <asm/cp15.h> +#include <asm/cache.h> +#include "irammap.h" #include "sleep.h" #include "flowctrl.h" +#define EMC_CFG 0xc +#define EMC_ADR_CFG 0x10 +#define EMC_REFRESH 0x70 +#define EMC_NOP 0xdc +#define EMC_SELF_REF 0xe0 +#define EMC_REQ_CTRL 0x2b0 +#define EMC_EMC_STATUS 0x2b4 + +#define CLK_RESET_CCLK_BURST 0x20 +#define CLK_RESET_CCLK_DIVIDER 0x24 +#define CLK_RESET_SCLK_BURST 0x28 +#define CLK_RESET_SCLK_DIVIDER 0x2c +#define CLK_RESET_PLLC_BASE 0x80 +#define CLK_RESET_PLLM_BASE 0x90 +#define CLK_RESET_PLLP_BASE 0xa0 + +#define APB_MISC_XM2CFGCPADCTRL 0x8c8 +#define APB_MISC_XM2CFGDPADCTRL 0x8cc +#define APB_MISC_XM2CLKCFGPADCTRL 0x8d0 +#define APB_MISC_XM2COMPPADCTRL 0x8d4 +#define APB_MISC_XM2VTTGENPADCTRL 0x8d8 +#define APB_MISC_XM2CFGCPADCTRL2 0x8e4 +#define APB_MISC_XM2CFGDPADCTRL2 0x8e8 + +.macro pll_enable, rd, r_car_base, pll_base + ldr \rd, [\r_car_base, #\pll_base] + tst \rd, #(1 << 30) + orreq \rd, \rd, #(1 << 30) + streq \rd, [\r_car_base, #\pll_base] +.endm + +.macro emc_device_mask, rd, base + ldr \rd, [\base, #EMC_ADR_CFG] + tst \rd, #(0x3 << 24) + moveq \rd, #(0x1 << 8) @ just 1 device + movne \rd, #(0x3 << 8) @ 2 devices +.endm + #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) /* * tegra20_hotplug_shutdown(void) @@ -181,6 +221,28 @@ ENTRY(tegra20_cpu_is_resettable_soon) ENDPROC(tegra20_cpu_is_resettable_soon) /* + * tegra20_sleep_core_finish(unsigned long v2p) + * + * Enters suspend in LP0 or LP1 by turning off the mmu and jumping to + * tegra20_tear_down_core in IRAM + */ +ENTRY(tegra20_sleep_core_finish) + /* Flush, disable the L1 data cache and exit SMP */ + bl tegra_disable_clean_inv_dcache + + mov32 r3, tegra_shut_off_mmu + add r3, r3, r0 + + mov32 r0, tegra20_tear_down_core + mov32 r1, tegra20_iram_start + sub r0, r0, r1 + mov32 r1, TEGRA_IRAM_LPx_RESUME_AREA + add r0, r0, r1 + + mov pc, r3 +ENDPROC(tegra20_sleep_core_finish) + +/* * tegra20_sleep_cpu_secondary_finish(unsigned long v2p) * * Enters WFI on secondary CPU by exiting coherency. @@ -191,6 +253,7 @@ ENTRY(tegra20_sleep_cpu_secondary_finish) mrc p15, 0, r11, c1, c0, 1 @ save actlr before exiting coherency /* Flush and disable the L1 data cache */ + mov r0, #TEGRA_FLUSH_CACHE_LOUIS bl tegra_disable_clean_inv_dcache mov32 r0, TEGRA_PMC_VIRT + PMC_SCRATCH41 @@ -250,6 +313,150 @@ ENTRY(tegra20_tear_down_cpu) b tegra20_enter_sleep ENDPROC(tegra20_tear_down_cpu) +/* START OF ROUTINES COPIED TO IRAM */ + .align L1_CACHE_SHIFT + .globl tegra20_iram_start +tegra20_iram_start: + +/* + * tegra20_lp1_reset + * + * reset vector for LP1 restore; copied into IRAM during suspend. + * Brings the system back up to a safe staring point (SDRAM out of + * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP, + * system clock running on the same PLL that it suspended at), and + * jumps to tegra_resume to restore virtual addressing and PLLX. + * The physical address of tegra_resume expected to be stored in + * PMC_SCRATCH41. + * + * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_LPx_RESUME_AREA. + */ +ENTRY(tegra20_lp1_reset) + /* + * The CPU and system bus are running at 32KHz and executing from + * IRAM when this code is executed; immediately switch to CLKM and + * enable PLLM, PLLP, PLLC. + */ + mov32 r0, TEGRA_CLK_RESET_BASE + + mov r1, #(1 << 28) + str r1, [r0, #CLK_RESET_SCLK_BURST] + str r1, [r0, #CLK_RESET_CCLK_BURST] + mov r1, #0 + str r1, [r0, #CLK_RESET_CCLK_DIVIDER] + str r1, [r0, #CLK_RESET_SCLK_DIVIDER] + + pll_enable r1, r0, CLK_RESET_PLLM_BASE + pll_enable r1, r0, CLK_RESET_PLLP_BASE + pll_enable r1, r0, CLK_RESET_PLLC_BASE + + adr r2, tegra20_sdram_pad_address + adr r4, tegra20_sdram_pad_save + mov r5, #0 + + ldr r6, tegra20_sdram_pad_size +padload: + ldr r7, [r2, r5] @ r7 is the addr in the pad_address + + ldr r1, [r4, r5] + str r1, [r7] @ restore the value in pad_save + + add r5, r5, #4 + cmp r6, r5 + bne padload + +padload_done: + /* 255uS delay for PLL stabilization */ + mov32 r7, TEGRA_TMRUS_BASE + ldr r1, [r7] + add r1, r1, #0xff + wait_until r1, r7, r9 + + adr r4, tegra20_sclk_save + ldr r4, [r4] + str r4, [r0, #CLK_RESET_SCLK_BURST] + mov32 r4, ((1 << 28) | (4)) @ burst policy is PLLP + str r4, [r0, #CLK_RESET_CCLK_BURST] + + mov32 r0, TEGRA_EMC_BASE + ldr r1, [r0, #EMC_CFG] + bic r1, r1, #(1 << 31) @ disable DRAM_CLK_STOP + str r1, [r0, #EMC_CFG] + + mov r1, #0 + str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh + mov r1, #1 + str r1, [r0, #EMC_NOP] + str r1, [r0, #EMC_NOP] + str r1, [r0, #EMC_REFRESH] + + emc_device_mask r1, r0 + +exit_selfrefresh_loop: + ldr r2, [r0, #EMC_EMC_STATUS] + ands r2, r2, r1 + bne exit_selfrefresh_loop + + mov r1, #0 @ unstall all transactions + str r1, [r0, #EMC_REQ_CTRL] + + mov32 r0, TEGRA_PMC_BASE + ldr r0, [r0, #PMC_SCRATCH41] + mov pc, r0 @ jump to tegra_resume +ENDPROC(tegra20_lp1_reset) + +/* + * tegra20_tear_down_core + * + * copied into and executed from IRAM + * puts memory in self-refresh for LP0 and LP1 + */ +tegra20_tear_down_core: + bl tegra20_sdram_self_refresh + bl tegra20_switch_cpu_to_clk32k + b tegra20_enter_sleep + +/* + * tegra20_switch_cpu_to_clk32k + * + * In LP0 and LP1 all PLLs will be turned off. Switch the CPU and system clock + * to the 32KHz clock. + */ +tegra20_switch_cpu_to_clk32k: + /* + * start by switching to CLKM to safely disable PLLs, then switch to + * CLKS. + */ + mov r0, #(1 << 28) + str r0, [r5, #CLK_RESET_SCLK_BURST] + str r0, [r5, #CLK_RESET_CCLK_BURST] + mov r0, #0 + str r0, [r5, #CLK_RESET_CCLK_DIVIDER] + str r0, [r5, #CLK_RESET_SCLK_DIVIDER] + + /* 2uS delay delay between changing SCLK and disabling PLLs */ + mov32 r7, TEGRA_TMRUS_BASE + ldr r1, [r7] + add r1, r1, #2 + wait_until r1, r7, r9 + + /* disable PLLM, PLLP and PLLC */ + ldr r0, [r5, #CLK_RESET_PLLM_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLM_BASE] + ldr r0, [r5, #CLK_RESET_PLLP_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLP_BASE] + ldr r0, [r5, #CLK_RESET_PLLC_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLC_BASE] + + /* switch to CLKS */ + mov r0, #0 /* brust policy = 32KHz */ + str r0, [r5, #CLK_RESET_SCLK_BURST] + + mov pc, lr + /* * tegra20_enter_sleep * @@ -274,4 +481,95 @@ halted: isb b halted +/* + * tegra20_sdram_self_refresh + * + * called with MMU off and caches disabled + * puts sdram in self refresh + * must be executed from IRAM + */ +tegra20_sdram_self_refresh: + mov32 r1, TEGRA_EMC_BASE @ r1 reserved for emc base addr + + mov r2, #3 + str r2, [r1, #EMC_REQ_CTRL] @ stall incoming DRAM requests + +emcidle: + ldr r2, [r1, #EMC_EMC_STATUS] + tst r2, #4 + beq emcidle + + mov r2, #1 + str r2, [r1, #EMC_SELF_REF] + + emc_device_mask r2, r1 + +emcself: + ldr r3, [r1, #EMC_EMC_STATUS] + and r3, r3, r2 + cmp r3, r2 + bne emcself @ loop until DDR in self-refresh + + adr r2, tegra20_sdram_pad_address + adr r3, tegra20_sdram_pad_safe + adr r4, tegra20_sdram_pad_save + mov r5, #0 + + ldr r6, tegra20_sdram_pad_size +padsave: + ldr r0, [r2, r5] @ r0 is the addr in the pad_address + + ldr r1, [r0] + str r1, [r4, r5] @ save the content of the addr + + ldr r1, [r3, r5] + str r1, [r0] @ set the save val to the addr + + add r5, r5, #4 + cmp r6, r5 + bne padsave +padsave_done: + + mov32 r5, TEGRA_CLK_RESET_BASE + ldr r0, [r5, #CLK_RESET_SCLK_BURST] + adr r2, tegra20_sclk_save + str r0, [r2] + dsb + mov pc, lr + +tegra20_sdram_pad_address: + .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL + .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL + .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CLKCFGPADCTRL + .word TEGRA_APB_MISC_BASE + APB_MISC_XM2COMPPADCTRL + .word TEGRA_APB_MISC_BASE + APB_MISC_XM2VTTGENPADCTRL + .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGCPADCTRL2 + .word TEGRA_APB_MISC_BASE + APB_MISC_XM2CFGDPADCTRL2 + +tegra20_sdram_pad_size: + .word tegra20_sdram_pad_size - tegra20_sdram_pad_address + +tegra20_sdram_pad_safe: + .word 0x8 + .word 0x8 + .word 0x0 + .word 0x8 + .word 0x5500 + .word 0x08080040 + .word 0x0 + +tegra20_sclk_save: + .word 0x0 + +tegra20_sdram_pad_save: + .rept (tegra20_sdram_pad_size - tegra20_sdram_pad_address) / 4 + .long 0 + .endr + + .ltorg +/* dummy symbol for end of IRAM */ + .align L1_CACHE_SHIFT + .globl tegra20_iram_end +tegra20_iram_end: + b . #endif diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S index ada8821b48b..b16d4a57fa5 100644 --- a/arch/arm/mach-tegra/sleep-tegra30.S +++ b/arch/arm/mach-tegra/sleep-tegra30.S @@ -18,13 +18,119 @@ #include <asm/assembler.h> #include <asm/asm-offsets.h> +#include <asm/cache.h> +#include "irammap.h" #include "fuse.h" #include "sleep.h" #include "flowctrl.h" +#define EMC_CFG 0xc +#define EMC_ADR_CFG 0x10 +#define EMC_TIMING_CONTROL 0x28 +#define EMC_REFRESH 0x70 +#define EMC_NOP 0xdc +#define EMC_SELF_REF 0xe0 +#define EMC_MRW 0xe8 +#define EMC_FBIO_CFG5 0x104 +#define EMC_AUTO_CAL_CONFIG 0x2a4 +#define EMC_AUTO_CAL_INTERVAL 0x2a8 +#define EMC_AUTO_CAL_STATUS 0x2ac +#define EMC_REQ_CTRL 0x2b0 +#define EMC_CFG_DIG_DLL 0x2bc +#define EMC_EMC_STATUS 0x2b4 +#define EMC_ZCAL_INTERVAL 0x2e0 +#define EMC_ZQ_CAL 0x2ec +#define EMC_XM2VTTGENPADCTRL 0x310 +#define EMC_XM2VTTGENPADCTRL2 0x314 + +#define PMC_CTRL 0x0 +#define PMC_CTRL_SIDE_EFFECT_LP0 (1 << 14) /* enter LP0 when CPU pwr gated */ + +#define PMC_PLLP_WB0_OVERRIDE 0xf8 +#define PMC_IO_DPD_REQ 0x1b8 +#define PMC_IO_DPD_STATUS 0x1bc + +#define CLK_RESET_CCLK_BURST 0x20 +#define CLK_RESET_CCLK_DIVIDER 0x24 +#define CLK_RESET_SCLK_BURST 0x28 +#define CLK_RESET_SCLK_DIVIDER 0x2c + +#define CLK_RESET_PLLC_BASE 0x80 +#define CLK_RESET_PLLC_MISC 0x8c +#define CLK_RESET_PLLM_BASE 0x90 +#define CLK_RESET_PLLM_MISC 0x9c +#define CLK_RESET_PLLP_BASE 0xa0 +#define CLK_RESET_PLLP_MISC 0xac +#define CLK_RESET_PLLA_BASE 0xb0 +#define CLK_RESET_PLLA_MISC 0xbc +#define CLK_RESET_PLLX_BASE 0xe0 +#define CLK_RESET_PLLX_MISC 0xe4 +#define CLK_RESET_PLLX_MISC3 0x518 +#define CLK_RESET_PLLX_MISC3_IDDQ 3 +#define CLK_RESET_PLLM_MISC_IDDQ 5 +#define CLK_RESET_PLLC_MISC_IDDQ 26 + +#define CLK_RESET_CLK_SOURCE_MSELECT 0x3b4 + +#define MSELECT_CLKM (0x3 << 30) + +#define LOCK_DELAY 50 /* safety delay after lock is detected */ + #define TEGRA30_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */ +.macro emc_device_mask, rd, base + ldr \rd, [\base, #EMC_ADR_CFG] + tst \rd, #0x1 + moveq \rd, #(0x1 << 8) @ just 1 device + movne \rd, #(0x3 << 8) @ 2 devices +.endm + +.macro emc_timing_update, rd, base + mov \rd, #1 + str \rd, [\base, #EMC_TIMING_CONTROL] +1001: + ldr \rd, [\base, #EMC_EMC_STATUS] + tst \rd, #(0x1<<23) @ wait EMC_STATUS_TIMING_UPDATE_STALLED is clear + bne 1001b +.endm + +.macro pll_enable, rd, r_car_base, pll_base, pll_misc + ldr \rd, [\r_car_base, #\pll_base] + tst \rd, #(1 << 30) + orreq \rd, \rd, #(1 << 30) + streq \rd, [\r_car_base, #\pll_base] + /* Enable lock detector */ + .if \pll_misc + ldr \rd, [\r_car_base, #\pll_misc] + bic \rd, \rd, #(1 << 18) + str \rd, [\r_car_base, #\pll_misc] + ldr \rd, [\r_car_base, #\pll_misc] + ldr \rd, [\r_car_base, #\pll_misc] + orr \rd, \rd, #(1 << 18) + str \rd, [\r_car_base, #\pll_misc] + .endif +.endm + +.macro pll_locked, rd, r_car_base, pll_base +1: + ldr \rd, [\r_car_base, #\pll_base] + tst \rd, #(1 << 27) + beq 1b +.endm + +.macro pll_iddq_exit, rd, car, iddq, iddq_bit + ldr \rd, [\car, #\iddq] + bic \rd, \rd, #(1<<\iddq_bit) + str \rd, [\car, #\iddq] +.endm + +.macro pll_iddq_entry, rd, car, iddq, iddq_bit + ldr \rd, [\car, #\iddq] + orr \rd, \rd, #(1<<\iddq_bit) + str \rd, [\car, #\iddq] +.endm + #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) /* * tegra30_hotplug_shutdown(void) @@ -99,6 +205,8 @@ flow_ctrl_setting_for_lp2: cmp r10, #TEGRA30 moveq r3, #FLOW_CTRL_WAIT_FOR_INTERRUPT @ For LP2 movne r3, #FLOW_CTRL_WAITEVENT + orrne r3, r3, #FLOW_CTRL_HALT_GIC_IRQ + orrne r3, r3, #FLOW_CTRL_HALT_GIC_FIQ flow_ctrl_done: cmp r10, #TEGRA30 str r3, [r2] @@ -127,6 +235,41 @@ ENDPROC(tegra30_cpu_shutdown) #ifdef CONFIG_PM_SLEEP /* + * tegra30_sleep_core_finish(unsigned long v2p) + * + * Enters suspend in LP0 or LP1 by turning off the MMU and jumping to + * tegra30_tear_down_core in IRAM + */ +ENTRY(tegra30_sleep_core_finish) + /* Flush, disable the L1 data cache and exit SMP */ + bl tegra_disable_clean_inv_dcache + + /* + * Preload all the address literals that are needed for the + * CPU power-gating process, to avoid loading from SDRAM which + * are not supported once SDRAM is put into self-refresh. + * LP0 / LP1 use physical address, since the MMU needs to be + * disabled before putting SDRAM into self-refresh to avoid + * memory access due to page table walks. + */ + mov32 r4, TEGRA_PMC_BASE + mov32 r5, TEGRA_CLK_RESET_BASE + mov32 r6, TEGRA_FLOW_CTRL_BASE + mov32 r7, TEGRA_TMRUS_BASE + + mov32 r3, tegra_shut_off_mmu + add r3, r3, r0 + + mov32 r0, tegra30_tear_down_core + mov32 r1, tegra30_iram_start + sub r0, r0, r1 + mov32 r1, TEGRA_IRAM_LPx_RESUME_AREA + add r0, r0, r1 + + mov pc, r3 +ENDPROC(tegra30_sleep_core_finish) + +/* * tegra30_sleep_cpu_secondary_finish(unsigned long v2p) * * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU. @@ -135,6 +278,7 @@ ENTRY(tegra30_sleep_cpu_secondary_finish) mov r7, lr /* Flush and disable the L1 data cache */ + mov r0, #TEGRA_FLUSH_CACHE_LOUIS bl tegra_disable_clean_inv_dcache /* Powergate this CPU. */ @@ -155,6 +299,368 @@ ENTRY(tegra30_tear_down_cpu) b tegra30_enter_sleep ENDPROC(tegra30_tear_down_cpu) +/* START OF ROUTINES COPIED TO IRAM */ + .align L1_CACHE_SHIFT + .globl tegra30_iram_start +tegra30_iram_start: + +/* + * tegra30_lp1_reset + * + * reset vector for LP1 restore; copied into IRAM during suspend. + * Brings the system back up to a safe staring point (SDRAM out of + * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLX, + * system clock running on the same PLL that it suspended at), and + * jumps to tegra_resume to restore virtual addressing. + * The physical address of tegra_resume expected to be stored in + * PMC_SCRATCH41. + * + * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_LPx_RESUME_AREA. + */ +ENTRY(tegra30_lp1_reset) + /* + * The CPU and system bus are running at 32KHz and executing from + * IRAM when this code is executed; immediately switch to CLKM and + * enable PLLP, PLLM, PLLC, PLLA and PLLX. + */ + mov32 r0, TEGRA_CLK_RESET_BASE + + mov r1, #(1 << 28) + str r1, [r0, #CLK_RESET_SCLK_BURST] + str r1, [r0, #CLK_RESET_CCLK_BURST] + mov r1, #0 + str r1, [r0, #CLK_RESET_CCLK_DIVIDER] + str r1, [r0, #CLK_RESET_SCLK_DIVIDER] + + tegra_get_soc_id TEGRA_APB_MISC_BASE, r10 + cmp r10, #TEGRA30 + beq _no_pll_iddq_exit + + pll_iddq_exit r1, r0, CLK_RESET_PLLM_MISC, CLK_RESET_PLLM_MISC_IDDQ + pll_iddq_exit r1, r0, CLK_RESET_PLLC_MISC, CLK_RESET_PLLC_MISC_IDDQ + pll_iddq_exit r1, r0, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ + + mov32 r7, TEGRA_TMRUS_BASE + ldr r1, [r7] + add r1, r1, #2 + wait_until r1, r7, r3 + + /* enable PLLM via PMC */ + mov32 r2, TEGRA_PMC_BASE + ldr r1, [r2, #PMC_PLLP_WB0_OVERRIDE] + orr r1, r1, #(1 << 12) + str r1, [r2, #PMC_PLLP_WB0_OVERRIDE] + + pll_enable r1, r0, CLK_RESET_PLLM_BASE, 0 + pll_enable r1, r0, CLK_RESET_PLLC_BASE, 0 + pll_enable r1, r0, CLK_RESET_PLLX_BASE, 0 + + b _pll_m_c_x_done + +_no_pll_iddq_exit: + /* enable PLLM via PMC */ + mov32 r2, TEGRA_PMC_BASE + ldr r1, [r2, #PMC_PLLP_WB0_OVERRIDE] + orr r1, r1, #(1 << 12) + str r1, [r2, #PMC_PLLP_WB0_OVERRIDE] + + pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC + pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC + pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC + +_pll_m_c_x_done: + pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC + pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC + + pll_locked r1, r0, CLK_RESET_PLLM_BASE + pll_locked r1, r0, CLK_RESET_PLLP_BASE + pll_locked r1, r0, CLK_RESET_PLLA_BASE + pll_locked r1, r0, CLK_RESET_PLLC_BASE + pll_locked r1, r0, CLK_RESET_PLLX_BASE + + mov32 r7, TEGRA_TMRUS_BASE + ldr r1, [r7] + add r1, r1, #LOCK_DELAY + wait_until r1, r7, r3 + + adr r5, tegra_sdram_pad_save + + ldr r4, [r5, #0x18] @ restore CLK_SOURCE_MSELECT + str r4, [r0, #CLK_RESET_CLK_SOURCE_MSELECT] + + ldr r4, [r5, #0x1C] @ restore SCLK_BURST + str r4, [r0, #CLK_RESET_SCLK_BURST] + + cmp r10, #TEGRA30 + movweq r4, #:lower16:((1 << 28) | (0x8)) @ burst policy is PLLX + movteq r4, #:upper16:((1 << 28) | (0x8)) + movwne r4, #:lower16:((1 << 28) | (0xe)) + movtne r4, #:upper16:((1 << 28) | (0xe)) + str r4, [r0, #CLK_RESET_CCLK_BURST] + + /* Restore pad power state to normal */ + ldr r1, [r5, #0x14] @ PMC_IO_DPD_STATUS + mvn r1, r1 + bic r1, r1, #(1 << 31) + orr r1, r1, #(1 << 30) + str r1, [r2, #PMC_IO_DPD_REQ] @ DPD_OFF + + cmp r10, #TEGRA30 + movweq r0, #:lower16:TEGRA_EMC_BASE @ r0 reserved for emc base + movteq r0, #:upper16:TEGRA_EMC_BASE + cmp r10, #TEGRA114 + movweq r0, #:lower16:TEGRA_EMC0_BASE + movteq r0, #:upper16:TEGRA_EMC0_BASE + cmp r10, #TEGRA124 + movweq r0, #:lower16:TEGRA124_EMC_BASE + movteq r0, #:upper16:TEGRA124_EMC_BASE + +exit_self_refresh: + ldr r1, [r5, #0xC] @ restore EMC_XM2VTTGENPADCTRL + str r1, [r0, #EMC_XM2VTTGENPADCTRL] + ldr r1, [r5, #0x10] @ restore EMC_XM2VTTGENPADCTRL2 + str r1, [r0, #EMC_XM2VTTGENPADCTRL2] + ldr r1, [r5, #0x8] @ restore EMC_AUTO_CAL_INTERVAL + str r1, [r0, #EMC_AUTO_CAL_INTERVAL] + + /* Relock DLL */ + ldr r1, [r0, #EMC_CFG_DIG_DLL] + orr r1, r1, #(1 << 30) @ set DLL_RESET + str r1, [r0, #EMC_CFG_DIG_DLL] + + emc_timing_update r1, r0 + + cmp r10, #TEGRA114 + movweq r1, #:lower16:TEGRA_EMC1_BASE + movteq r1, #:upper16:TEGRA_EMC1_BASE + cmpeq r0, r1 + + ldr r1, [r0, #EMC_AUTO_CAL_CONFIG] + orr r1, r1, #(1 << 31) @ set AUTO_CAL_ACTIVE + orreq r1, r1, #(1 << 27) @ set slave mode for channel 1 + str r1, [r0, #EMC_AUTO_CAL_CONFIG] + +emc_wait_auto_cal_onetime: + ldr r1, [r0, #EMC_AUTO_CAL_STATUS] + tst r1, #(1 << 31) @ wait until AUTO_CAL_ACTIVE is cleared + bne emc_wait_auto_cal_onetime + + ldr r1, [r0, #EMC_CFG] + bic r1, r1, #(1 << 31) @ disable DRAM_CLK_STOP_PD + str r1, [r0, #EMC_CFG] + + mov r1, #0 + str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh + mov r1, #1 + cmp r10, #TEGRA30 + streq r1, [r0, #EMC_NOP] + streq r1, [r0, #EMC_NOP] + streq r1, [r0, #EMC_REFRESH] + + emc_device_mask r1, r0 + +exit_selfrefresh_loop: + ldr r2, [r0, #EMC_EMC_STATUS] + ands r2, r2, r1 + bne exit_selfrefresh_loop + + lsr r1, r1, #8 @ devSel, bit0:dev0, bit1:dev1 + + mov32 r7, TEGRA_TMRUS_BASE + ldr r2, [r0, #EMC_FBIO_CFG5] + + and r2, r2, #3 @ check DRAM_TYPE + cmp r2, #2 + beq emc_lpddr2 + + /* Issue a ZQ_CAL for dev0 - DDR3 */ + mov32 r2, 0x80000011 @ DEV_SELECTION=2, LENGTH=LONG, CMD=1 + str r2, [r0, #EMC_ZQ_CAL] + ldr r2, [r7] + add r2, r2, #10 + wait_until r2, r7, r3 + + tst r1, #2 + beq zcal_done + + /* Issue a ZQ_CAL for dev1 - DDR3 */ + mov32 r2, 0x40000011 @ DEV_SELECTION=1, LENGTH=LONG, CMD=1 + str r2, [r0, #EMC_ZQ_CAL] + ldr r2, [r7] + add r2, r2, #10 + wait_until r2, r7, r3 + b zcal_done + +emc_lpddr2: + /* Issue a ZQ_CAL for dev0 - LPDDR2 */ + mov32 r2, 0x800A00AB @ DEV_SELECTION=2, MA=10, OP=0xAB + str r2, [r0, #EMC_MRW] + ldr r2, [r7] + add r2, r2, #1 + wait_until r2, r7, r3 + + tst r1, #2 + beq zcal_done + + /* Issue a ZQ_CAL for dev0 - LPDDR2 */ + mov32 r2, 0x400A00AB @ DEV_SELECTION=1, MA=10, OP=0xAB + str r2, [r0, #EMC_MRW] + ldr r2, [r7] + add r2, r2, #1 + wait_until r2, r7, r3 + +zcal_done: + mov r1, #0 @ unstall all transactions + str r1, [r0, #EMC_REQ_CTRL] + ldr r1, [r5, #0x4] @ restore EMC_ZCAL_INTERVAL + str r1, [r0, #EMC_ZCAL_INTERVAL] + ldr r1, [r5, #0x0] @ restore EMC_CFG + str r1, [r0, #EMC_CFG] + + /* Tegra114 had dual EMC channel, now config the other one */ + cmp r10, #TEGRA114 + bne __no_dual_emc_chanl + mov32 r1, TEGRA_EMC1_BASE + cmp r0, r1 + movne r0, r1 + addne r5, r5, #0x20 + bne exit_self_refresh +__no_dual_emc_chanl: + + mov32 r0, TEGRA_PMC_BASE + ldr r0, [r0, #PMC_SCRATCH41] + mov pc, r0 @ jump to tegra_resume +ENDPROC(tegra30_lp1_reset) + + .align L1_CACHE_SHIFT +tegra30_sdram_pad_address: + .word TEGRA_EMC_BASE + EMC_CFG @0x0 + .word TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL @0x4 + .word TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL @0x8 + .word TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL @0xc + .word TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2 @0x10 + .word TEGRA_PMC_BASE + PMC_IO_DPD_STATUS @0x14 + .word TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT @0x18 + .word TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST @0x1c +tegra30_sdram_pad_address_end: + +tegra114_sdram_pad_address: + .word TEGRA_EMC0_BASE + EMC_CFG @0x0 + .word TEGRA_EMC0_BASE + EMC_ZCAL_INTERVAL @0x4 + .word TEGRA_EMC0_BASE + EMC_AUTO_CAL_INTERVAL @0x8 + .word TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL @0xc + .word TEGRA_EMC0_BASE + EMC_XM2VTTGENPADCTRL2 @0x10 + .word TEGRA_PMC_BASE + PMC_IO_DPD_STATUS @0x14 + .word TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT @0x18 + .word TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST @0x1c + .word TEGRA_EMC1_BASE + EMC_CFG @0x20 + .word TEGRA_EMC1_BASE + EMC_ZCAL_INTERVAL @0x24 + .word TEGRA_EMC1_BASE + EMC_AUTO_CAL_INTERVAL @0x28 + .word TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL @0x2c + .word TEGRA_EMC1_BASE + EMC_XM2VTTGENPADCTRL2 @0x30 +tegra114_sdram_pad_adress_end: + +tegra124_sdram_pad_address: + .word TEGRA124_EMC_BASE + EMC_CFG @0x0 + .word TEGRA124_EMC_BASE + EMC_ZCAL_INTERVAL @0x4 + .word TEGRA124_EMC_BASE + EMC_AUTO_CAL_INTERVAL @0x8 + .word TEGRA124_EMC_BASE + EMC_XM2VTTGENPADCTRL @0xc + .word TEGRA124_EMC_BASE + EMC_XM2VTTGENPADCTRL2 @0x10 + .word TEGRA_PMC_BASE + PMC_IO_DPD_STATUS @0x14 + .word TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT @0x18 + .word TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST @0x1c +tegra124_sdram_pad_address_end: + +tegra30_sdram_pad_size: + .word tegra30_sdram_pad_address_end - tegra30_sdram_pad_address + +tegra114_sdram_pad_size: + .word tegra114_sdram_pad_adress_end - tegra114_sdram_pad_address + + .type tegra_sdram_pad_save, %object +tegra_sdram_pad_save: + .rept (tegra114_sdram_pad_adress_end - tegra114_sdram_pad_address) / 4 + .long 0 + .endr + +/* + * tegra30_tear_down_core + * + * copied into and executed from IRAM + * puts memory in self-refresh for LP0 and LP1 + */ +tegra30_tear_down_core: + bl tegra30_sdram_self_refresh + bl tegra30_switch_cpu_to_clk32k + b tegra30_enter_sleep + +/* + * tegra30_switch_cpu_to_clk32k + * + * In LP0 and LP1 all PLLs will be turned off. Switching the CPU and System CLK + * to the 32KHz clock. + * r4 = TEGRA_PMC_BASE + * r5 = TEGRA_CLK_RESET_BASE + * r6 = TEGRA_FLOW_CTRL_BASE + * r7 = TEGRA_TMRUS_BASE + * r10= SoC ID + */ +tegra30_switch_cpu_to_clk32k: + /* + * start by jumping to CLKM to safely disable PLLs, then jump to + * CLKS. + */ + mov r0, #(1 << 28) + str r0, [r5, #CLK_RESET_SCLK_BURST] + /* 2uS delay delay between changing SCLK and CCLK */ + ldr r1, [r7] + add r1, r1, #2 + wait_until r1, r7, r9 + str r0, [r5, #CLK_RESET_CCLK_BURST] + mov r0, #0 + str r0, [r5, #CLK_RESET_CCLK_DIVIDER] + str r0, [r5, #CLK_RESET_SCLK_DIVIDER] + + /* switch the clock source of mselect to be CLK_M */ + ldr r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT] + orr r0, r0, #MSELECT_CLKM + str r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT] + + /* 2uS delay delay between changing SCLK and disabling PLLs */ + ldr r1, [r7] + add r1, r1, #2 + wait_until r1, r7, r9 + + /* disable PLLM via PMC in LP1 */ + ldr r0, [r4, #PMC_PLLP_WB0_OVERRIDE] + bic r0, r0, #(1 << 12) + str r0, [r4, #PMC_PLLP_WB0_OVERRIDE] + + /* disable PLLP, PLLA, PLLC and PLLX */ + ldr r0, [r5, #CLK_RESET_PLLP_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLP_BASE] + ldr r0, [r5, #CLK_RESET_PLLA_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLA_BASE] + ldr r0, [r5, #CLK_RESET_PLLC_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLC_BASE] + ldr r0, [r5, #CLK_RESET_PLLX_BASE] + bic r0, r0, #(1 << 30) + str r0, [r5, #CLK_RESET_PLLX_BASE] + + cmp r10, #TEGRA30 + beq _no_pll_in_iddq + pll_iddq_entry r1, r5, CLK_RESET_PLLX_MISC3, CLK_RESET_PLLX_MISC3_IDDQ +_no_pll_in_iddq: + + /* switch to CLKS */ + mov r0, #0 /* brust policy = 32KHz */ + str r0, [r5, #CLK_RESET_SCLK_BURST] + + mov pc, lr + /* * tegra30_enter_sleep * @@ -172,8 +678,12 @@ tegra30_enter_sleep: orr r0, r0, #FLOW_CTRL_CSR_ENABLE str r0, [r6, r2] + tegra_get_soc_id TEGRA_APB_MISC_BASE, r10 + cmp r10, #TEGRA30 mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT - orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ + orreq r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ + orrne r0, r0, #FLOW_CTRL_HALT_LIC_IRQ | FLOW_CTRL_HALT_LIC_FIQ + cpu_to_halt_reg r2, r1 str r0, [r6, r2] dsb @@ -187,4 +697,134 @@ halted: /* !!!FIXME!!! Implement halt failure handler */ b halted +/* + * tegra30_sdram_self_refresh + * + * called with MMU off and caches disabled + * must be executed from IRAM + * r4 = TEGRA_PMC_BASE + * r5 = TEGRA_CLK_RESET_BASE + * r6 = TEGRA_FLOW_CTRL_BASE + * r7 = TEGRA_TMRUS_BASE + * r10= SoC ID + */ +tegra30_sdram_self_refresh: + + adr r8, tegra_sdram_pad_save + tegra_get_soc_id TEGRA_APB_MISC_BASE, r10 + cmp r10, #TEGRA30 + adreq r2, tegra30_sdram_pad_address + ldreq r3, tegra30_sdram_pad_size + cmp r10, #TEGRA114 + adreq r2, tegra114_sdram_pad_address + ldreq r3, tegra114_sdram_pad_size + cmp r10, #TEGRA124 + adreq r2, tegra124_sdram_pad_address + ldreq r3, tegra30_sdram_pad_size + + mov r9, #0 + +padsave: + ldr r0, [r2, r9] @ r0 is the addr in the pad_address + + ldr r1, [r0] + str r1, [r8, r9] @ save the content of the addr + + add r9, r9, #4 + cmp r3, r9 + bne padsave +padsave_done: + + dsb + + cmp r10, #TEGRA30 + ldreq r0, =TEGRA_EMC_BASE @ r0 reserved for emc base addr + cmp r10, #TEGRA114 + ldreq r0, =TEGRA_EMC0_BASE + cmp r10, #TEGRA124 + ldreq r0, =TEGRA124_EMC_BASE + +enter_self_refresh: + cmp r10, #TEGRA30 + mov r1, #0 + str r1, [r0, #EMC_ZCAL_INTERVAL] + str r1, [r0, #EMC_AUTO_CAL_INTERVAL] + ldr r1, [r0, #EMC_CFG] + bic r1, r1, #(1 << 28) + bicne r1, r1, #(1 << 29) + str r1, [r0, #EMC_CFG] @ disable DYN_SELF_REF + + emc_timing_update r1, r0 + + ldr r1, [r7] + add r1, r1, #5 + wait_until r1, r7, r2 + +emc_wait_auto_cal: + ldr r1, [r0, #EMC_AUTO_CAL_STATUS] + tst r1, #(1 << 31) @ wait until AUTO_CAL_ACTIVE is cleared + bne emc_wait_auto_cal + + mov r1, #3 + str r1, [r0, #EMC_REQ_CTRL] @ stall incoming DRAM requests + +emcidle: + ldr r1, [r0, #EMC_EMC_STATUS] + tst r1, #4 + beq emcidle + + mov r1, #1 + str r1, [r0, #EMC_SELF_REF] + + emc_device_mask r1, r0 + +emcself: + ldr r2, [r0, #EMC_EMC_STATUS] + and r2, r2, r1 + cmp r2, r1 + bne emcself @ loop until DDR in self-refresh + + /* Put VTTGEN in the lowest power mode */ + ldr r1, [r0, #EMC_XM2VTTGENPADCTRL] + mov32 r2, 0xF8F8FFFF @ clear XM2VTTGEN_DRVUP and XM2VTTGEN_DRVDN + and r1, r1, r2 + str r1, [r0, #EMC_XM2VTTGENPADCTRL] + ldr r1, [r0, #EMC_XM2VTTGENPADCTRL2] + cmp r10, #TEGRA30 + orreq r1, r1, #7 @ set E_NO_VTTGEN + orrne r1, r1, #0x3f + str r1, [r0, #EMC_XM2VTTGENPADCTRL2] + + emc_timing_update r1, r0 + + /* Tegra114 had dual EMC channel, now config the other one */ + cmp r10, #TEGRA114 + bne no_dual_emc_chanl + mov32 r1, TEGRA_EMC1_BASE + cmp r0, r1 + movne r0, r1 + bne enter_self_refresh +no_dual_emc_chanl: + + ldr r1, [r4, #PMC_CTRL] + tst r1, #PMC_CTRL_SIDE_EFFECT_LP0 + bne pmc_io_dpd_skip + /* + * Put DDR_DATA, DISC_ADDR_CMD, DDR_ADDR_CMD, POP_ADDR_CMD, POP_CLK + * and COMP in the lowest power mode when LP1. + */ + mov32 r1, 0x8EC00000 + str r1, [r4, #PMC_IO_DPD_REQ] +pmc_io_dpd_skip: + + dsb + + mov pc, lr + + .ltorg +/* dummy symbol for end of IRAM */ + .align L1_CACHE_SHIFT + .global tegra30_iram_end +tegra30_iram_end: + b . #endif diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S index 9daaef26b0f..8d06213fbc4 100644 --- a/arch/arm/mach-tegra/sleep.S +++ b/arch/arm/mach-tegra/sleep.S @@ -56,7 +56,9 @@ ENTRY(tegra_disable_clean_inv_dcache) isb /* Flush the D-cache */ - bl v7_flush_dcache_louis + cmp r0, #TEGRA_FLUSH_CACHE_ALL + blne v7_flush_dcache_louis + bleq v7_flush_dcache_all /* Trun off coherency */ exit_smp r4, r5 @@ -67,15 +69,40 @@ ENDPROC(tegra_disable_clean_inv_dcache) #ifdef CONFIG_PM_SLEEP /* + * tegra_init_l2_for_a15 + * + * set up the correct L2 cache data RAM latency + */ +ENTRY(tegra_init_l2_for_a15) + mrc p15, 0, r0, c0, c0, 5 + ubfx r0, r0, #8, #4 + tst r0, #1 @ only need for cluster 0 + bne _exit_init_l2_a15 + + mrc p15, 0x1, r0, c9, c0, 2 + and r0, r0, #7 + cmp r0, #2 + bicne r0, r0, #7 + orrne r0, r0, #2 + mcrne p15, 0x1, r0, c9, c0, 2 +_exit_init_l2_a15: + + mov pc, lr +ENDPROC(tegra_init_l2_for_a15) + +/* * tegra_sleep_cpu_finish(unsigned long v2p) * * enters suspend in LP2 by turning off the mmu and jumping to * tegra?_tear_down_cpu */ ENTRY(tegra_sleep_cpu_finish) + mov r4, r0 /* Flush and disable the L1 data cache */ + mov r0, #TEGRA_FLUSH_CACHE_ALL bl tegra_disable_clean_inv_dcache + mov r0, r4 mov32 r6, tegra_tear_down_cpu ldr r1, [r6] add r1, r1, r0 @@ -107,10 +134,10 @@ ENTRY(tegra_shut_off_mmu) #ifdef CONFIG_CACHE_L2X0 /* Disable L2 cache */ check_cpu_part_num 0xc09, r9, r10 - movweq r4, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000) - movteq r4, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000) - moveq r5, #0 - streq r5, [r4, #L2X0_CTRL] + movweq r2, #:lower16:(TEGRA_ARM_PERIF_BASE + 0x3000) + movteq r2, #:upper16:(TEGRA_ARM_PERIF_BASE + 0x3000) + moveq r3, #0 + streq r3, [r2, #L2X0_CTRL] #endif mov pc, r0 ENDPROC(tegra_shut_off_mmu) diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index 98b7da698f2..a4edbb3abd3 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -41,7 +41,19 @@ #define CPU_NOT_RESETTABLE 0 #endif +/* flag of tegra_disable_clean_inv_dcache to do LoUIS or all */ +#define TEGRA_FLUSH_CACHE_LOUIS 0 +#define TEGRA_FLUSH_CACHE_ALL 1 + #ifdef __ASSEMBLY__ +/* waits until the microsecond counter (base) is > rn */ +.macro wait_until, rn, base, tmp + add \rn, \rn, #1 +1001: ldr \tmp, [\base] + cmp \tmp, \rn + bmi 1001b +.endm + /* returns the offset of the flow controller halt register for a cpu */ .macro cpu_to_halt_reg rd, rcpu cmp \rcpu, #0 @@ -144,7 +156,7 @@ void tegra_pen_lock(void); void tegra_pen_unlock(void); void tegra_resume(void); int tegra_sleep_cpu_finish(unsigned long); -void tegra_disable_clean_inv_dcache(void); +void tegra_disable_clean_inv_dcache(u32 flag); #ifdef CONFIG_HOTPLUG_CPU void tegra20_hotplug_shutdown(void); diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index fc97cfd5276..73368176c6e 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -16,7 +16,6 @@ * */ -#include <linux/clocksource.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> @@ -34,16 +33,78 @@ #include <linux/sys_soc.h> #include <linux/usb/tegra_usb_phy.h> #include <linux/clk/tegra.h> +#include <linux/irqchip.h> +#include <asm/hardware/cache-l2x0.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> #include <asm/mach/time.h> #include <asm/setup.h> +#include "apbio.h" #include "board.h" #include "common.h" +#include "cpuidle.h" #include "fuse.h" #include "iomap.h" +#include "irq.h" +#include "pmc.h" +#include "pm.h" +#include "reset.h" +#include "sleep.h" + +/* + * Storage for debug-macro.S's state. + * + * This must be in .data not .bss so that it gets initialized each time the + * kernel is loaded. The data is declared here rather than debug-macro.S so + * that multiple inclusions of debug-macro.S point at the same data. + */ +u32 tegra_uart_config[4] = { + /* Debug UART initialization required */ + 1, + /* Debug UART physical address */ + 0, + /* Debug UART virtual address */ + 0, + /* Scratch space for debug macro */ + 0, +}; + +static void __init tegra_init_cache(void) +{ +#ifdef CONFIG_CACHE_L2X0 + int ret; + void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000; + u32 aux_ctrl, cache_type; + + cache_type = readl(p + L2X0_CACHE_TYPE); + aux_ctrl = (cache_type & 0x700) << (17-8); + aux_ctrl |= 0x7C400001; + + ret = l2x0_of_init(aux_ctrl, 0x8200c3fe); + if (!ret) + l2x0_saved_regs_addr = virt_to_phys(&l2x0_saved_regs); +#endif +} + +static void __init tegra_init_early(void) +{ + tegra_apb_io_init(); + tegra_init_fuse(); + tegra_cpu_reset_handler_init(); + tegra_init_cache(); + tegra_powergate_init(); + tegra_hotplug_init(); +} + +static void __init tegra_dt_init_irq(void) +{ + tegra_pmc_init_irq(); + tegra_init_irq(); + irqchip_init(); + tegra_legacy_irq_syscore_init(); +} static void __init tegra_dt_init(void) { @@ -51,6 +112,8 @@ static void __init tegra_dt_init(void) struct soc_device *soc_dev; struct device *parent = NULL; + tegra_pmc_init(); + tegra_clocks_apply_init_table(); soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); @@ -80,28 +143,6 @@ out: of_platform_populate(NULL, of_default_bus_match_table, NULL, parent); } -static void __init trimslice_init(void) -{ -#ifdef CONFIG_TEGRA_PCI - int ret; - - ret = tegra_pcie_init(true, true); - if (ret) - pr_err("tegra_pci_init() failed: %d\n", ret); -#endif -} - -static void __init harmony_init(void) -{ -#ifdef CONFIG_TEGRA_PCI - int ret; - - ret = harmony_pcie_init(); - if (ret) - pr_err("harmony_pcie_init() failed: %d\n", ret); -#endif -} - static void __init paz00_init(void) { if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) @@ -112,8 +153,6 @@ static struct { char *machine; void (*init)(void); } board_init_funcs[] = { - { "compulab,trimslice", trimslice_init }, - { "nvidia,harmony", harmony_init }, { "compal,paz00", paz00_init }, }; @@ -121,7 +160,9 @@ static void __init tegra_dt_init_late(void) { int i; - tegra_init_late(); + tegra_init_suspend(); + tegra_cpuidle_init(); + tegra_powergate_debugfs_init(); for (i = 0; i < ARRAY_SIZE(board_init_funcs); i++) { if (of_machine_is_compatible(board_init_funcs[i].machine)) { @@ -132,6 +173,7 @@ static void __init tegra_dt_init_late(void) } static const char * const tegra_dt_board_compat[] = { + "nvidia,tegra124", "nvidia,tegra114", "nvidia,tegra30", "nvidia,tegra20", @@ -143,9 +185,8 @@ DT_MACHINE_START(TEGRA_DT, "NVIDIA Tegra SoC (Flattened Device Tree)") .smp = smp_ops(tegra_smp_ops), .init_early = tegra_init_early, .init_irq = tegra_dt_init_irq, - .init_time = clocksource_of_init, .init_machine = tegra_dt_init, .init_late = tegra_dt_init_late, - .restart = tegra_assert_system_reset, + .restart = tegra_pmc_restart, .dt_compat = tegra_dt_board_compat, MACHINE_END |