diff options
Diffstat (limited to 'arch/arm/mach-tegra')
24 files changed, 781 insertions, 357 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index f6b46ae2b7f..e40326d0e29 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -10,6 +10,7 @@ obj-y					+= pm.o  obj-y					+= reset.o  obj-y					+= reset-handler.o  obj-y					+= sleep.o +obj-y					+= tegra.o  obj-$(CONFIG_CPU_IDLE)			+= cpuidle.o  obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra20_speedo.o  obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= tegra2_emc.o @@ -27,9 +28,7 @@ obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o  obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o  obj-$(CONFIG_TEGRA_PCI)			+= pcie.o -obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= board-dt-tegra20.o -obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= board-dt-tegra30.o -obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= board-dt-tegra114.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= tegra114_speedo.o  ifeq ($(CONFIG_CPU_IDLE),y)  obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= cpuidle-tegra114.o  endif diff --git a/arch/arm/mach-tegra/board-dt-tegra114.c b/arch/arm/mach-tegra/board-dt-tegra114.c deleted file mode 100644 index 085d63637b6..00000000000 --- a/arch/arm/mach-tegra/board-dt-tegra114.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * NVIDIA Tegra114 device tree board support - * - * Copyright (C) 2013 NVIDIA Corporation - * - * 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/of.h> -#include <linux/of_platform.h> -#include <linux/clocksource.h> - -#include <asm/mach/arch.h> - -#include "board.h" -#include "common.h" - -static void __init tegra114_dt_init(void) -{ -	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -} - -static const char * const tegra114_dt_board_compat[] = { -	"nvidia,tegra114", -	NULL, -}; - -DT_MACHINE_START(TEGRA114_DT, "NVIDIA Tegra114 (Flattened Device Tree)") -	.smp		= smp_ops(tegra_smp_ops), -	.map_io		= tegra_map_common_io, -	.init_early	= tegra114_init_early, -	.init_irq	= tegra_dt_init_irq, -	.init_time	= clocksource_of_init, -	.init_machine	= tegra114_dt_init, -	.init_late	= tegra_init_late, -	.restart	= tegra_assert_system_reset, -	.dt_compat	= tegra114_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c deleted file mode 100644 index bf68567e549..00000000000 --- a/arch/arm/mach-tegra/board-dt-tegra30.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * arch/arm/mach-tegra/board-dt-tegra30.c - * - * NVIDIA Tegra30 device tree board support - * - * Copyright (C) 2011 NVIDIA Corporation - * - * Derived from: - * - * arch/arm/mach-tegra/board-dt-tegra20.c - * - * Copyright (C) 2010 Secret Lab Technologies, Ltd. - * Copyright (C) 2010 Google, Inc. - * - * 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/clocksource.h> -#include <linux/kernel.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_fdt.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> - -#include <asm/mach/arch.h> - -#include "board.h" -#include "common.h" -#include "iomap.h" - -static void __init tegra30_dt_init(void) -{ -	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -} - -static const char *tegra30_dt_board_compat[] = { -	"nvidia,tegra30", -	NULL -}; - -DT_MACHINE_START(TEGRA30_DT, "NVIDIA Tegra30 (Flattened Device Tree)") -	.smp		= smp_ops(tegra_smp_ops), -	.map_io		= tegra_map_common_io, -	.init_early	= tegra30_init_early, -	.init_irq	= tegra_dt_init_irq, -	.init_time	= clocksource_of_init, -	.init_machine	= tegra30_dt_init, -	.init_late	= tegra_init_late, -	.restart	= tegra_assert_system_reset, -	.dt_compat	= tegra30_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c index 3cdc1bb8254..035b240b9e1 100644 --- a/arch/arm/mach-tegra/board-harmony-pcie.c +++ b/arch/arm/mach-tegra/board-harmony-pcie.c @@ -56,13 +56,17 @@ int __init harmony_pcie_init(void)  	gpio_direction_output(en_vdd_1v05, 1);  	regulator = regulator_get(NULL, "vdd_ldo0,vddio_pex_clk"); -	if (IS_ERR_OR_NULL(regulator)) { -		pr_err("%s: regulator_get failed: %d\n", __func__, -		       (int)PTR_ERR(regulator)); +	if (IS_ERR(regulator)) { +		err = PTR_ERR(regulator); +		pr_err("%s: regulator_get failed: %d\n", __func__, err);  		goto err_reg;  	} -	regulator_enable(regulator); +	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) { @@ -74,6 +78,7 @@ int __init harmony_pcie_init(void)  err_pcie:  	regulator_disable(regulator); +err_en:  	regulator_put(regulator);  err_reg:  	gpio_free(en_vdd_1v05); diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 86851c81a35..60431de585c 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -26,9 +26,7 @@  void tegra_assert_system_reset(char mode, const char *cmd); -void __init tegra20_init_early(void); -void __init tegra30_init_early(void); -void __init tegra114_init_early(void); +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); diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 5449a3f2977..eb1f3c8c74c 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -33,6 +33,7 @@  #include "common.h"  #include "fuse.h"  #include "iomap.h" +#include "irq.h"  #include "pmc.h"  #include "apbio.h"  #include "sleep.h" @@ -61,8 +62,10 @@ u32 tegra_uart_config[4] = {  void __init tegra_dt_init_irq(void)  {  	tegra_clocks_init(); +	tegra_pmc_init();  	tegra_init_irq();  	irqchip_init(); +	tegra_legacy_irq_syscore_init();  }  #endif @@ -94,40 +97,18 @@ static void __init tegra_init_cache(void)  } -static void __init tegra_init_early(void) +void __init tegra_init_early(void)  {  	tegra_cpu_reset_handler_init();  	tegra_apb_io_init();  	tegra_init_fuse();  	tegra_init_cache(); -	tegra_pmc_init();  	tegra_powergate_init(); +	tegra_hotplug_init();  } -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -void __init tegra20_init_early(void) -{ -	tegra_init_early(); -	tegra20_hotplug_init(); -} -#endif - -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -void __init tegra30_init_early(void) -{ -	tegra_init_early(); -	tegra30_hotplug_init(); -} -#endif - -#ifdef CONFIG_ARCH_TEGRA_114_SOC -void __init tegra114_init_early(void) -{ -	tegra_init_early(); -} -#endif -  void __init tegra_init_late(void)  { +	tegra_init_suspend();  	tegra_powergate_debugfs_init();  } diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 825ced4f7a4..8bbbdebed88 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -130,10 +130,6 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,  					   struct cpuidle_driver *drv,  					   int index)  { -	struct cpuidle_state *state = &drv->states[index]; -	u32 cpu_on_time = state->exit_latency; -	u32 cpu_off_time = state->target_residency - state->exit_latency; -  	while (tegra20_cpu_is_resettable_soon())  		cpu_relax(); @@ -142,7 +138,7 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,  	clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); -	tegra_idle_lp2_last(cpu_on_time, cpu_off_time); +	tegra_idle_lp2_last();  	clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index 8b50cf4ddd6..c0931c8bb3e 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -72,10 +72,6 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,  					   struct cpuidle_driver *drv,  					   int index)  { -	struct cpuidle_state *state = &drv->states[index]; -	u32 cpu_on_time = state->exit_latency; -	u32 cpu_off_time = state->target_residency - state->exit_latency; -  	/* All CPUs entering LP2 is not working.  	 * Don't let CPU0 enter LP2 when any secondary CPU is online.  	 */ @@ -86,7 +82,7 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,  	clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); -	tegra_idle_lp2_last(cpu_on_time, cpu_off_time); +	tegra_idle_lp2_last();  	clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); @@ -102,12 +98,8 @@ static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,  	smp_wmb(); -	save_cpu_arch_register(); -  	cpu_suspend(0, tegra30_sleep_cpu_secondary_finish); -	restore_cpu_arch_register(); -  	clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);  	return true; diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index f7db0782a6b..e035cd284a6 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -2,6 +2,7 @@   * arch/arm/mach-tegra/fuse.c   *   * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.   *   * Author:   *	Colin Cross <ccross@android.com> @@ -137,6 +138,9 @@ void tegra_init_fuse(void)  		tegra_fuse_spare_bit = TEGRA30_FUSE_SPARE_BIT;  		tegra_init_speedo_data = &tegra30_init_speedo_data;  		break; +	case TEGRA114: +		tegra_init_speedo_data = &tegra114_init_speedo_data; +		break;  	default:  		pr_warn("Tegra: unknown chip id %d\n", tegra_chip_id);  		tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT; diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index da78434678c..aacc00d0598 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -1,5 +1,6 @@  /*   * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.   *   * Author:   *	Colin Cross <ccross@android.com> @@ -66,4 +67,10 @@ void tegra30_init_speedo_data(void);  static inline void tegra30_init_speedo_data(void) {}  #endif +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_init_speedo_data(void); +#else +static inline void tegra114_init_speedo_data(void) {} +#endif +  #endif diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S index fd473f2b4c3..045c16f2dd5 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -7,8 +7,5 @@  ENTRY(tegra_secondary_startup)          bl      v7_invalidate_l1 -	/* Enable coresight */ -	mov32	r0, 0xC5ACCE55 -	mcr	p14, 0, r0, c7, c12, 6          b       secondary_startup  ENDPROC(tegra_secondary_startup) diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index a599f6e36de..8da9f78475d 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -1,8 +1,7 @@  /* - *   *  Copyright (C) 2002 ARM Ltd.   *  All Rights Reserved - *  Copyright (c) 2010, 2012 NVIDIA Corporation. All rights reserved. + *  Copyright (c) 2010, 2012-2013, NVIDIA Corporation. All rights reserved.   *   * This program is free software; you can redistribute it and/or modify   * it under the terms of the GNU General Public License version 2 as @@ -15,6 +14,7 @@  #include <asm/cacheflush.h>  #include <asm/smp_plat.h> +#include "fuse.h"  #include "sleep.h"  static void (*tegra_hotplug_shutdown)(void); @@ -56,18 +56,13 @@ int tegra_cpu_disable(unsigned int cpu)  	return cpu == 0 ? -EPERM : 0;  } -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -extern void tegra20_hotplug_shutdown(void); -void __init tegra20_hotplug_init(void) +void __init tegra_hotplug_init(void)  { -	tegra_hotplug_shutdown = tegra20_hotplug_shutdown; -} -#endif +	if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) +		return; -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -extern void tegra30_hotplug_shutdown(void); -void __init tegra30_hotplug_init(void) -{ -	tegra_hotplug_shutdown = tegra30_hotplug_shutdown; +	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) +		tegra_hotplug_shutdown = tegra20_hotplug_shutdown; +	if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) +		tegra_hotplug_shutdown = tegra30_hotplug_shutdown;  } -#endif diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index 1952e82797c..0de4eed1493 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -4,7 +4,7 @@   * Author:   *	Colin Cross <ccross@android.com>   * - * Copyright (C) 2010, NVIDIA Corporation + * Copyright (C) 2010,2013, NVIDIA Corporation   *   * This software is licensed under the terms of the GNU General Public   * License version 2, as published by the Free Software Foundation, and @@ -23,6 +23,7 @@  #include <linux/io.h>  #include <linux/of.h>  #include <linux/irqchip/arm-gic.h> +#include <linux/syscore_ops.h>  #include "board.h"  #include "iomap.h" @@ -43,6 +44,7 @@  #define ICTLR_COP_IEP_CLASS	0x3c  #define FIRST_LEGACY_IRQ 32 +#define TEGRA_MAX_NUM_ICTLRS	5  #define SGI_MASK 0xFFFF @@ -56,6 +58,15 @@ static void __iomem *ictlr_reg_base[] = {  	IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE),  }; +#ifdef CONFIG_PM_SLEEP +static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; +static u32 cop_iep[TEGRA_MAX_NUM_ICTLRS]; +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]; +#endif +  bool tegra_pending_sgi(void)  {  	u32 pending_set; @@ -125,6 +136,87 @@ static int tegra_retrigger(struct irq_data *d)  	return 1;  } +#ifdef CONFIG_PM_SLEEP +static int tegra_set_wake(struct irq_data *d, unsigned int enable) +{ +	u32 irq = d->irq; +	u32 index, mask; + +	if (irq < FIRST_LEGACY_IRQ || +		irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32) +		return -EINVAL; + +	index = ((irq - FIRST_LEGACY_IRQ) / 32); +	mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); +	if (enable) +		ictlr_wake_mask[index] |= mask; +	else +		ictlr_wake_mask[index] &= ~mask; + +	return 0; +} + +static int tegra_legacy_irq_suspend(void) +{ +	unsigned long flags; +	int i; + +	local_irq_save(flags); +	for (i = 0; i < num_ictlrs; i++) { +		void __iomem *ictlr = ictlr_reg_base[i]; +		/* Save interrupt state */ +		cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER); +		cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS); +		cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER); +		cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS); + +		/* Disable COP interrupts */ +		writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + +		/* Disable CPU interrupts */ +		writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + +		/* Enable the wakeup sources of ictlr */ +		writel_relaxed(ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET); +	} +	local_irq_restore(flags); + +	return 0; +} + +static void tegra_legacy_irq_resume(void) +{ +	unsigned long flags; +	int i; + +	local_irq_save(flags); +	for (i = 0; i < num_ictlrs; i++) { +		void __iomem *ictlr = ictlr_reg_base[i]; +		writel_relaxed(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); +		writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); +		writel_relaxed(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); +		writel_relaxed(cop_iep[i], ictlr + ICTLR_COP_IEP_CLASS); +		writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); +		writel_relaxed(cop_ier[i], ictlr + ICTLR_COP_IER_SET); +	} +	local_irq_restore(flags); +} + +static struct syscore_ops tegra_legacy_irq_syscore_ops = { +	.suspend = tegra_legacy_irq_suspend, +	.resume = tegra_legacy_irq_resume, +}; + +int tegra_legacy_irq_syscore_init(void) +{ +	register_syscore_ops(&tegra_legacy_irq_syscore_ops); + +	return 0; +} +#else +#define tegra_set_wake NULL +#endif +  void __init tegra_init_irq(void)  {  	int i; @@ -150,6 +242,8 @@ void __init tegra_init_irq(void)  	gic_arch_extn.irq_mask = tegra_mask;  	gic_arch_extn.irq_unmask = tegra_unmask;  	gic_arch_extn.irq_retrigger = tegra_retrigger; +	gic_arch_extn.irq_set_wake = tegra_set_wake; +	gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND;  	/*  	 * Check if there is a devicetree present, since the GIC will be diff --git a/arch/arm/mach-tegra/irq.h b/arch/arm/mach-tegra/irq.h index 5142649bba0..bc05ce5613f 100644 --- a/arch/arm/mach-tegra/irq.h +++ b/arch/arm/mach-tegra/irq.h @@ -19,4 +19,10 @@  bool tegra_pending_sgi(void); +#ifdef CONFIG_PM_SLEEP +int tegra_legacy_irq_syscore_init(void); +#else +static inline int tegra_legacy_irq_syscore_init(void) { return 0; } +#endif +  #endif diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 2c6b3d55213..516aab28fe3 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -26,22 +26,16 @@  #include <asm/smp_scu.h>  #include <asm/smp_plat.h> -#include <mach/powergate.h> -  #include "fuse.h"  #include "flowctrl.h"  #include "reset.h" +#include "pmc.h"  #include "common.h"  #include "iomap.h" -extern void tegra_secondary_startup(void); -  static cpumask_t tegra_cpu_init_mask; -#define EVP_CPU_RESET_VECTOR \ -	(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) -  static void __cpuinit tegra_secondary_init(unsigned int cpu)  {  	/* @@ -54,25 +48,43 @@ static void __cpuinit tegra_secondary_init(unsigned int cpu)  	cpumask_set_cpu(cpu, &tegra_cpu_init_mask);  } -static int tegra20_power_up_cpu(unsigned int cpu) + +static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle)  { -	/* Enable the CPU clock. */ -	tegra_enable_cpu_clock(cpu); +	cpu = cpu_logical_map(cpu); + +	/* +	 * Force the CPU into reset. The CPU must remain in reset when +	 * the flow controller state is cleared (which will cause the +	 * flow controller to stop driving reset if the CPU has been +	 * power-gated via the flow controller). This will have no +	 * effect on first boot of the CPU since it should already be +	 * in reset. +	 */ +	tegra_put_cpu_in_reset(cpu); -	/* Clear flow controller CSR. */ -	flowctrl_write_cpu_csr(cpu, 0); +	/* +	 * Unhalt the CPU. If the flow controller was used to +	 * power-gate the CPU this will cause the flow controller to +	 * stop driving reset. The CPU will remain in reset because the +	 * clock and reset block is now driving reset. +	 */ +	flowctrl_write_cpu_halt(cpu, 0); +	tegra_enable_cpu_clock(cpu); +	flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ +	tegra_cpu_out_of_reset(cpu);  	return 0;  } -static int tegra30_power_up_cpu(unsigned int cpu) +static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle)  { -	int ret, pwrgateid; +	int ret;  	unsigned long timeout; -	pwrgateid = tegra_cpu_powergate_id(cpu); -	if (pwrgateid < 0) -		return pwrgateid; +	cpu = cpu_logical_map(cpu); +	tegra_put_cpu_in_reset(cpu); +	flowctrl_write_cpu_halt(cpu, 0);  	/*  	 * The power up sequence of cold boot CPU and warm boot CPU @@ -85,13 +97,13 @@ static int tegra30_power_up_cpu(unsigned int cpu)  	 * the IO clamps.  	 * For cold boot CPU, do not wait. After the cold boot CPU be  	 * booted, it will run to tegra_secondary_init() and set -	 * tegra_cpu_init_mask which influences what tegra30_power_up_cpu() +	 * tegra_cpu_init_mask which influences what tegra30_boot_secondary()  	 * next time around.  	 */  	if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) {  		timeout = jiffies + msecs_to_jiffies(50);  		do { -			if (!tegra_powergate_is_powered(pwrgateid)) +			if (tegra_pmc_cpu_is_powered(cpu))  				goto remove_clamps;  			udelay(10);  		} while (time_before(jiffies, timeout)); @@ -103,14 +115,14 @@ static int tegra30_power_up_cpu(unsigned int cpu)  	 * be un-gated by un-toggling the power gate register  	 * manually.  	 */ -	if (!tegra_powergate_is_powered(pwrgateid)) { -		ret = tegra_powergate_power_on(pwrgateid); +	if (!tegra_pmc_cpu_is_powered(cpu)) { +		ret = tegra_pmc_cpu_power_on(cpu);  		if (ret)  			return ret;  		/* Wait for the power to come up. */  		timeout = jiffies + msecs_to_jiffies(100); -		while (tegra_powergate_is_powered(pwrgateid)) { +		while (tegra_pmc_cpu_is_powered(cpu)) {  			if (time_after(jiffies, timeout))  				return -ETIMEDOUT;  			udelay(10); @@ -123,57 +135,34 @@ remove_clamps:  	udelay(10);  	/* Remove I/O clamps. */ -	ret = tegra_powergate_remove_clamping(pwrgateid); -	udelay(10); +	ret = tegra_pmc_cpu_remove_clamping(cpu); +	if (ret) +		return ret; -	/* Clear flow controller CSR. */ -	flowctrl_write_cpu_csr(cpu, 0); +	udelay(10); +	flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ +	tegra_cpu_out_of_reset(cpu);  	return 0;  } -static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle) +static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle)  { -	int status; -  	cpu = cpu_logical_map(cpu); +	return tegra_pmc_cpu_power_on(cpu); +} -	/* -	 * Force the CPU into reset. The CPU must remain in reset when the -	 * flow controller state is cleared (which will cause the flow -	 * controller to stop driving reset if the CPU has been power-gated -	 * via the flow controller). This will have no effect on first boot -	 * of the CPU since it should already be in reset. -	 */ -	tegra_put_cpu_in_reset(cpu); - -	/* -	 * Unhalt the CPU. If the flow controller was used to power-gate the -	 * CPU this will cause the flow controller to stop driving reset. -	 * The CPU will remain in reset because the clock and reset block -	 * is now driving reset. -	 */ -	flowctrl_write_cpu_halt(cpu, 0); - -	switch (tegra_chip_id) { -	case TEGRA20: -		status = tegra20_power_up_cpu(cpu); -		break; -	case TEGRA30: -		status = tegra30_power_up_cpu(cpu); -		break; -	default: -		status = -EINVAL; -		break; -	} - -	if (status) -		goto done; +static int __cpuinit tegra_boot_secondary(unsigned int cpu, +					  struct task_struct *idle) +{ +	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) +		return tegra20_boot_secondary(cpu, idle); +	if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) +		return tegra30_boot_secondary(cpu, idle); +	if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) +		return tegra114_boot_secondary(cpu, idle); -	/* Take the CPU out of reset. */ -	tegra_cpu_out_of_reset(cpu); -done: -	return status; +	return -EINVAL;  }  static void __init tegra_smp_prepare_cpus(unsigned int max_cpus) diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 523604de666..d0b7400e460 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -22,7 +22,7 @@  #include <linux/cpumask.h>  #include <linux/delay.h>  #include <linux/cpu_pm.h> -#include <linux/clk.h> +#include <linux/suspend.h>  #include <linux/err.h>  #include <linux/clk/tegra.h> @@ -37,67 +37,13 @@  #include "reset.h"  #include "flowctrl.h"  #include "fuse.h" +#include "pmc.h"  #include "sleep.h" -#define TEGRA_POWER_CPU_PWRREQ_OE	(1 << 16)  /* CPU pwr req enable */ - -#define PMC_CTRL		0x0 -#define PMC_CPUPWRGOOD_TIMER	0xc8 -#define PMC_CPUPWROFF_TIMER	0xcc -  #ifdef CONFIG_PM_SLEEP -static unsigned int g_diag_reg;  static DEFINE_SPINLOCK(tegra_lp2_lock); -static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); -static struct clk *tegra_pclk;  void (*tegra_tear_down_cpu)(void); -void save_cpu_arch_register(void) -{ -	/* read diagnostic register */ -	asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); -	return; -} - -void restore_cpu_arch_register(void) -{ -	/* write diagnostic register */ -	asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); -	return; -} - -static void set_power_timers(unsigned long us_on, unsigned long us_off) -{ -	unsigned long long ticks; -	unsigned long long pclk; -	unsigned long rate; -	static unsigned long tegra_last_pclk; - -	if (tegra_pclk == NULL) { -		tegra_pclk = clk_get_sys(NULL, "pclk"); -		WARN_ON(IS_ERR(tegra_pclk)); -	} - -	rate = clk_get_rate(tegra_pclk); - -	if (WARN_ON_ONCE(rate <= 0)) -		pclk = 100000000; -	else -		pclk = rate; - -	if ((rate != tegra_last_pclk)) { -		ticks = (us_on * pclk) + 999999ull; -		do_div(ticks, 1000000); -		writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER); - -		ticks = (us_off * pclk) + 999999ull; -		do_div(ticks, 1000000); -		writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER); -		wmb(); -	} -	tegra_last_pclk = pclk; -} -  /*   * restore_cpu_complex   * @@ -119,8 +65,6 @@ static void restore_cpu_complex(void)  	tegra_cpu_clock_resume();  	flowctrl_cpu_suspend_exit(cpu); - -	restore_cpu_arch_register();  }  /* @@ -145,8 +89,6 @@ static void suspend_cpu_complex(void)  	tegra_cpu_clock_suspend();  	flowctrl_cpu_suspend_enter(cpu); - -	save_cpu_arch_register();  }  void tegra_clear_cpu_in_lp2(int phy_cpu_id) @@ -197,16 +139,9 @@ static int tegra_sleep_cpu(unsigned long v2p)  	return 0;  } -void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) +void tegra_idle_lp2_last(void)  { -	u32 mode; - -	/* Only the last cpu down does the final suspend steps */ -	mode = readl(pmc + PMC_CTRL); -	mode |= TEGRA_POWER_CPU_PWRREQ_OE; -	writel(mode, pmc + PMC_CTRL); - -	set_power_timers(cpu_on_time, cpu_off_time); +	tegra_pmc_pm_set(TEGRA_SUSPEND_LP2);  	cpu_cluster_pm_enter();  	suspend_cpu_complex(); @@ -216,4 +151,81 @@ void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time)  	restore_cpu_complex();  	cpu_cluster_pm_exit();  } + +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. +	 */ +	if (mode > TEGRA_SUSPEND_LP2) +		return TEGRA_SUSPEND_LP2; + +	return mode; +} + +static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { +	[TEGRA_SUSPEND_NONE] = "none", +	[TEGRA_SUSPEND_LP2] = "LP2", +	[TEGRA_SUSPEND_LP1] = "LP1", +	[TEGRA_SUSPEND_LP0] = "LP0", +}; + +static int __cpuinit tegra_suspend_enter(suspend_state_t state) +{ +	enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); + +	if (WARN_ON(mode < TEGRA_SUSPEND_NONE || +		    mode >= TEGRA_MAX_SUSPEND_MODE)) +		return -EINVAL; + +	pr_info("Entering suspend state %s\n", lp_state[mode]); + +	tegra_pmc_pm_set(mode); + +	local_fiq_disable(); + +	suspend_cpu_complex(); +	switch (mode) { +	case TEGRA_SUSPEND_LP2: +		tegra_set_cpu_in_lp2(0); +		break; +	default: +		break; +	} + +	cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); + +	switch (mode) { +	case TEGRA_SUSPEND_LP2: +		tegra_clear_cpu_in_lp2(0); +		break; +	default: +		break; +	} +	restore_cpu_complex(); + +	local_fiq_enable(); + +	return 0; +} + +static const struct platform_suspend_ops tegra_suspend_ops = { +	.valid		= suspend_valid_only_mem, +	.enter		= tegra_suspend_enter, +}; + +void __init tegra_init_suspend(void) +{ +	if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) +		return; + +	tegra_pmc_suspend_init(); + +	suspend_set_ops(&tegra_suspend_ops); +}  #endif diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 787335cc964..9d2d038bf12 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -21,6 +21,8 @@  #ifndef _MACH_TEGRA_PM_H_  #define _MACH_TEGRA_PM_H_ +#include "pmc.h" +  extern unsigned long l2x0_saved_regs_addr;  void save_cpu_arch_register(void); @@ -29,7 +31,20 @@ void restore_cpu_arch_register(void);  void tegra_clear_cpu_in_lp2(int phy_cpu_id);  bool tegra_set_cpu_in_lp2(int phy_cpu_id); -void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time); +void tegra_idle_lp2_last(void);  extern void (*tegra_tear_down_cpu)(void); +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode tegra_pm_validate_suspend_mode( +				enum tegra_suspend_mode mode); +void tegra_init_suspend(void); +#else +enum tegra_suspend_mode tegra_pm_validate_suspend_mode( +				enum tegra_suspend_mode mode) +{ +	return TEGRA_SUSPEND_NONE; +} +static inline void tegra_init_suspend(void) {} +#endif +  #endif /* _MACH_TEGRA_PM_H_ */ diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index d4fdb5fcec2..32360e540ce 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -1,5 +1,5 @@  /* - * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2012,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, @@ -16,59 +16,313 @@   */  #include <linux/kernel.h> +#include <linux/clk.h>  #include <linux/io.h>  #include <linux/of.h> +#include <linux/of_address.h> -#include "iomap.h" +#include "fuse.h" +#include "pm.h" +#include "pmc.h" +#include "sleep.h" -#define PMC_CTRL		0x0 -#define PMC_CTRL_INTR_LOW	(1 << 17) +#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 */ + +#define PMC_CTRL			0x0 +#define PMC_CTRL_INTR_LOW		(1 << 17) +#define PMC_PWRGATE_TOGGLE		0x30 +#define PMC_PWRGATE_TOGGLE_START	(1 << 8) +#define PMC_REMOVE_CLAMPING		0x34 +#define PMC_PWRGATE_STATUS		0x38 + +#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, +	TEGRA_POWERGATE_CPU2, +	TEGRA_POWERGATE_CPU3, +}; +static DEFINE_SPINLOCK(tegra_powergate_lock); + +static void __iomem *tegra_pmc_base; +static bool tegra_pmc_invert_interrupt; +static struct clk *tegra_pclk; + +struct pmc_pm_data { +	u32 cpu_good_time;	/* CPU power good time in uS */ +	u32 cpu_off_time;	/* CPU power off time in uS */ +	u32 core_osc_time;	/* Core power good osc time in uS */ +	u32 core_pmu_time;	/* Core power good pmu time in uS */ +	u32 core_off_time;	/* Core power off time in uS */ +	bool corereq_high;	/* Core power request active-high */ +	bool sysclkreq_high;	/* System clock request active-high */ +	bool combined_req;	/* Combined pwr req for CPU & Core */ +	bool cpu_pwr_good_en;	/* CPU power good signal is enabled */ +	u32 lp0_vec_phy_addr;	/* The phy addr of LP0 warm boot code */ +	u32 lp0_vec_size;	/* The size of LP0 warm boot code */ +	enum tegra_suspend_mode suspend_mode; +}; +static struct pmc_pm_data pmc_pm_data;  static inline u32 tegra_pmc_readl(u32 reg)  { -	return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg)); +	return readl(tegra_pmc_base + reg);  }  static inline void tegra_pmc_writel(u32 val, u32 reg)  { -	writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg)); +	writel(val, tegra_pmc_base + reg); +} + +static int tegra_pmc_get_cpu_powerdomain_id(int cpuid) +{ +	if (cpuid <= 0 || cpuid >= num_possible_cpus()) +		return -EINVAL; +	return tegra_cpu_domains[cpuid]; +} + +static bool tegra_pmc_powergate_is_powered(int id) +{ +	return (tegra_pmc_readl(PMC_PWRGATE_STATUS) >> id) & 1; +} + +static int tegra_pmc_powergate_set(int id, bool new_state) +{ +	bool old_state; +	unsigned long flags; + +	spin_lock_irqsave(&tegra_powergate_lock, flags); + +	old_state = tegra_pmc_powergate_is_powered(id); +	WARN_ON(old_state == new_state); + +	tegra_pmc_writel(PMC_PWRGATE_TOGGLE_START | id, PMC_PWRGATE_TOGGLE); + +	spin_unlock_irqrestore(&tegra_powergate_lock, flags); + +	return 0; +} + +static int tegra_pmc_powergate_remove_clamping(int id) +{ +	u32 mask; + +	/* +	 * Tegra has a bug where PCIE and VDE clamping masks are +	 * swapped relatively to the partition ids. +	 */ +	if (id ==  TEGRA_POWERGATE_VDEC) +		mask = (1 << TEGRA_POWERGATE_PCIE); +	else if	(id == TEGRA_POWERGATE_PCIE) +		mask = (1 << TEGRA_POWERGATE_VDEC); +	else +		mask = (1 << id); + +	tegra_pmc_writel(mask, PMC_REMOVE_CLAMPING); + +	return 0; +} + +bool tegra_pmc_cpu_is_powered(int cpuid) +{ +	int id; + +	id = tegra_pmc_get_cpu_powerdomain_id(cpuid); +	if (id < 0) +		return false; +	return tegra_pmc_powergate_is_powered(id);  } -#ifdef CONFIG_OF +int tegra_pmc_cpu_power_on(int cpuid) +{ +	int id; + +	id = tegra_pmc_get_cpu_powerdomain_id(cpuid); +	if (id < 0) +		return id; +	return tegra_pmc_powergate_set(id, true); +} + +int tegra_pmc_cpu_remove_clamping(int cpuid) +{ +	int id; + +	id = tegra_pmc_get_cpu_powerdomain_id(cpuid); +	if (id < 0) +		return id; +	return tegra_pmc_powergate_remove_clamping(id); +} + +#ifdef CONFIG_PM_SLEEP +static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate) +{ +	unsigned long long ticks; +	unsigned long long pclk; +	static unsigned long tegra_last_pclk; + +	if (WARN_ON_ONCE(rate <= 0)) +		pclk = 100000000; +	else +		pclk = rate; + +	if ((rate != tegra_last_pclk)) { +		ticks = (us_on * pclk) + 999999ull; +		do_div(ticks, 1000000); +		tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWRGOOD_TIMER); + +		ticks = (us_off * pclk) + 999999ull; +		do_div(ticks, 1000000); +		tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWROFF_TIMER); +		wmb(); +	} +	tegra_last_pclk = pclk; +} + +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) +{ +	return pmc_pm_data.suspend_mode; +} + +void tegra_pmc_pm_set(enum tegra_suspend_mode mode) +{ +	u32 reg; +	unsigned long rate = 0; + +	reg = tegra_pmc_readl(PMC_CTRL); +	reg |= TEGRA_POWER_CPU_PWRREQ_OE; +	reg &= ~TEGRA_POWER_EFFECT_LP0; + +	switch (mode) { +	case TEGRA_SUSPEND_LP2: +		rate = clk_get_rate(tegra_pclk); +		break; +	default: +		break; +	} + +	set_power_timers(pmc_pm_data.cpu_good_time, pmc_pm_data.cpu_off_time, +			 rate); + +	tegra_pmc_writel(reg, PMC_CTRL); +} + +void tegra_pmc_suspend_init(void) +{ +	u32 reg; + +	/* Always enable CPU power request */ +	reg = tegra_pmc_readl(PMC_CTRL); +	reg |= TEGRA_POWER_CPU_PWRREQ_OE; +	tegra_pmc_writel(reg, PMC_CTRL); +} +#endif +  static const struct of_device_id matches[] __initconst = { +	{ .compatible = "nvidia,tegra114-pmc" }, +	{ .compatible = "nvidia,tegra30-pmc" },  	{ .compatible = "nvidia,tegra20-pmc" },  	{ }  }; -#endif -void __init tegra_pmc_init(void) +static void tegra_pmc_parse_dt(void)  { -	/* -	 * For now, Harmony is the only board that uses the PMC, and it wants -	 * the signal inverted. Seaboard would too if it used the PMC. -	 * Hopefully by the time other boards want to use the PMC, everything -	 * will be device-tree, or they also want it inverted. -	 */ -	bool invert_interrupt = true; -	u32 val; +	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}; -#ifdef CONFIG_OF -	if (of_have_populated_dt()) { -		struct device_node *np; +	np = of_find_matching_node(NULL, matches); +	BUG_ON(!np); -		invert_interrupt = false; +	tegra_pmc_base = of_iomap(np, 0); -		np = of_find_matching_node(NULL, matches); -		if (np) { -			if (of_find_property(np, "nvidia,invert-interrupt", -						NULL)) -				invert_interrupt = true; +	tegra_pmc_invert_interrupt = of_property_read_bool(np, +				     "nvidia,invert-interrupt"); +	tegra_pclk = of_clk_get_by_name(np, "pclk"); +	WARN_ON(IS_ERR(tegra_pclk)); + +	/* Grabbing the power management configurations */ +	if (of_property_read_u32(np, "nvidia,suspend-mode", &prop)) { +		suspend_mode = TEGRA_SUSPEND_NONE; +	} else { +		switch (prop) { +		case 0: +			suspend_mode = TEGRA_SUSPEND_LP0; +			break; +		case 1: +			suspend_mode = TEGRA_SUSPEND_LP1; +			break; +		case 2: +			suspend_mode = TEGRA_SUSPEND_LP2; +			break; +		default: +			suspend_mode = TEGRA_SUSPEND_NONE; +			break;  		}  	} -#endif +	suspend_mode = tegra_pm_validate_suspend_mode(suspend_mode); + +	if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) +		suspend_mode = TEGRA_SUSPEND_NONE; +	pmc_pm_data.cpu_good_time = prop; + +	if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &prop)) +		suspend_mode = TEGRA_SUSPEND_NONE; +	pmc_pm_data.cpu_off_time = prop; + +	if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time", +			core_good_time, ARRAY_SIZE(core_good_time))) +		suspend_mode = TEGRA_SUSPEND_NONE; +	pmc_pm_data.core_osc_time = core_good_time[0]; +	pmc_pm_data.core_pmu_time = core_good_time[1]; + +	if (of_property_read_u32(np, "nvidia,core-pwr-off-time", +				 &prop)) +		suspend_mode = TEGRA_SUSPEND_NONE; +	pmc_pm_data.core_off_time = prop; + +	pmc_pm_data.corereq_high = of_property_read_bool(np, +				"nvidia,core-power-req-active-high"); + +	pmc_pm_data.sysclkreq_high = of_property_read_bool(np, +				"nvidia,sys-clock-req-active-high"); + +	pmc_pm_data.combined_req = of_property_read_bool(np, +				"nvidia,combined-power-req"); + +	pmc_pm_data.cpu_pwr_good_en = of_property_read_bool(np, +				"nvidia,cpu-pwr-good-en"); + +	if (of_property_read_u32_array(np, "nvidia,lp0-vec", lp0_vec, +				       ARRAY_SIZE(lp0_vec))) +		if (suspend_mode == TEGRA_SUSPEND_LP0) +			suspend_mode = TEGRA_SUSPEND_LP1; + +	pmc_pm_data.lp0_vec_phy_addr = lp0_vec[0]; +	pmc_pm_data.lp0_vec_size = lp0_vec[1]; + +	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 (invert_interrupt) +	if (tegra_pmc_invert_interrupt)  		val |= PMC_CTRL_INTR_LOW;  	else  		val &= ~PMC_CTRL_INTR_LOW; diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 8995ee4a876..e1c2df272f7 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -18,6 +18,24 @@  #ifndef __MACH_TEGRA_PMC_H  #define __MACH_TEGRA_PMC_H +enum tegra_suspend_mode { +	TEGRA_SUSPEND_NONE = 0, +	TEGRA_SUSPEND_LP2,	/* CPU voltage off */ +	TEGRA_SUSPEND_LP1,	/* CPU voltage off, DRAM self-refresh */ +	TEGRA_SUSPEND_LP0,      /* CPU + core voltage off, DRAM self-refresh */ +	TEGRA_MAX_SUSPEND_MODE, +}; + +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); +void tegra_pmc_pm_set(enum tegra_suspend_mode mode); +void tegra_pmc_suspend_init(void); +#endif + +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_init(void);  #endif diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index 54382ceade4..1676aba5e7b 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -41,9 +41,6 @@   */  ENTRY(tegra_resume)  	bl	v7_invalidate_l1 -	/* Enable coresight */ -	mov32	r0, 0xC5ACCE55 -	mcr	p14, 0, r0, c7, c12, 6  	cpu_id	r0  	cmp	r0, #0				@ CPU0? @@ -99,6 +96,8 @@ ENTRY(__tegra_cpu_reset_handler_start)   *   * Register usage within the reset handler:   * + *      Others: scratch + *      R6  = SoC ID << 8   *      R7  = CPU present (to the OS) mask   *      R8  = CPU in LP1 state mask   *      R9  = CPU in LP2 state mask @@ -114,6 +113,40 @@ ENTRY(__tegra_cpu_reset_handler_start)  ENTRY(__tegra_cpu_reset_handler)  	cpsid	aif, 0x13			@ SVC mode, interrupts disabled + +	mov32	r6, TEGRA_APB_MISC_BASE +	ldr	r6, [r6, #APB_MISC_GP_HIDREV] +	and	r6, r6, #0xff00 +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +t20_check: +	cmp	r6, #(0x20 << 8) +	bne	after_t20_check +t20_errata: +	# Tegra20 is a Cortex-A9 r1p1 +	mrc	p15, 0, r0, c1, c0, 0   @ read system control register +	orr	r0, r0, #1 << 14        @ erratum 716044 +	mcr	p15, 0, r0, c1, c0, 0   @ write system control register +	mrc	p15, 0, r0, c15, c0, 1  @ read diagnostic register +	orr	r0, r0, #1 << 4         @ erratum 742230 +	orr	r0, r0, #1 << 11        @ erratum 751472 +	mcr	p15, 0, r0, c15, c0, 1  @ write diagnostic register +	b	after_errata +after_t20_check: +#endif +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +t30_check: +	cmp	r6, #(0x30 << 8) +	bne	after_t30_check +t30_errata: +	# Tegra30 is a Cortex-A9 r2p9 +	mrc	p15, 0, r0, c15, c0, 1  @ read diagnostic register +	orr	r0, r0, #1 << 6         @ erratum 743622 +	orr	r0, r0, #1 << 11        @ erratum 751472 +	mcr	p15, 0, r0, c15, c0, 1  @ write diagnostic register +	b	after_errata +after_t30_check: +#endif +after_errata:  	mrc	p15, 0, r10, c0, c0, 5		@ MPIDR  	and	r10, r10, #0x3			@ R10 = CPU number  	mov	r11, #1 @@ -129,16 +162,13 @@ ENTRY(__tegra_cpu_reset_handler)  #ifdef CONFIG_ARCH_TEGRA_2x_SOC  	/* Are we on Tegra20? */ -	mov32	r6, TEGRA_APB_MISC_BASE -	ldr	r0, [r6, #APB_MISC_GP_HIDREV] -	and	r0, r0, #0xff00 -	cmp	r0, #(0x20 << 8) +	cmp	r6, #(0x20 << 8)  	bne	1f  	/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ -	mov32	r6, TEGRA_PMC_BASE +	mov32	r5, TEGRA_PMC_BASE  	mov	r0, #0  	cmp	r10, #0 -	strne	r0, [r6, #PMC_SCRATCH41] +	strne	r0, [r5, #PMC_SCRATCH41]  1:  #endif diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index 4ffae541726..970ebd5138b 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2010-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, @@ -124,11 +124,11 @@ int tegra_sleep_cpu_finish(unsigned long);  void tegra_disable_clean_inv_dcache(void);  #ifdef CONFIG_HOTPLUG_CPU -void tegra20_hotplug_init(void); -void tegra30_hotplug_init(void); +void tegra20_hotplug_shutdown(void); +void tegra30_hotplug_shutdown(void); +void tegra_hotplug_init(void);  #else -static inline void tegra20_hotplug_init(void) {} -static inline void tegra30_hotplug_init(void) {} +static inline void tegra_hotplug_init(void) {}  #endif  void tegra20_cpu_shutdown(int cpu); diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/tegra.c index a0edf251028..61749e2d811 100644 --- a/arch/arm/mach-tegra/board-dt-tegra20.c +++ b/arch/arm/mach-tegra/tegra.c @@ -1,6 +1,7 @@  /* - * nVidia Tegra device tree board support + * NVIDIA Tegra SoC device tree board support   * + * Copyright (C) 2011, 2013, NVIDIA Corporation   * Copyright (C) 2010 Secret Lab Technologies, Ltd.   * Copyright (C) 2010 Google, Inc.   * @@ -32,7 +33,10 @@  #include <linux/io.h>  #include <linux/i2c.h>  #include <linux/i2c-tegra.h> +#include <linux/slab.h> +#include <linux/sys_soc.h>  #include <linux/usb/tegra_usb_phy.h> +#include <linux/clk/tegra.h>  #include <asm/mach-types.h>  #include <asm/mach/arch.h> @@ -41,6 +45,7 @@  #include "board.h"  #include "common.h" +#include "fuse.h"  #include "iomap.h"  static struct tegra_ehci_platform_data tegra_ehci1_pdata = { @@ -79,12 +84,38 @@ static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {  static void __init tegra_dt_init(void)  { +	struct soc_device_attribute *soc_dev_attr; +	struct soc_device *soc_dev; +	struct device *parent = NULL; + +	tegra_clocks_apply_init_table(); + +	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); +	if (!soc_dev_attr) +		goto out; + +	soc_dev_attr->family = kasprintf(GFP_KERNEL, "Tegra"); +	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_revision); +	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", tegra_chip_id); + +	soc_dev = soc_device_register(soc_dev_attr); +	if (IS_ERR(soc_dev)) { +		kfree(soc_dev_attr->family); +		kfree(soc_dev_attr->revision); +		kfree(soc_dev_attr->soc_id); +		kfree(soc_dev_attr); +		goto out; +	} + +	parent = soc_device_to_device(soc_dev); +  	/*  	 * Finished with the static registrations now; fill in the missing  	 * devices  	 */ +out:  	of_platform_populate(NULL, of_default_bus_match_table, -				tegra20_auxdata_lookup, NULL); +				tegra20_auxdata_lookup, parent);  }  static void __init trimslice_init(void) @@ -111,7 +142,8 @@ static void __init harmony_init(void)  static void __init paz00_init(void)  { -	tegra_paz00_wifikill_init(); +	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) +		tegra_paz00_wifikill_init();  }  static struct { @@ -137,19 +169,21 @@ static void __init tegra_dt_init_late(void)  	}  } -static const char *tegra20_dt_board_compat[] = { +static const char * const tegra_dt_board_compat[] = { +	"nvidia,tegra114", +	"nvidia,tegra30",  	"nvidia,tegra20",  	NULL  }; -DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)") +DT_MACHINE_START(TEGRA_DT, "NVIDIA Tegra SoC (Flattened Device Tree)")  	.map_io		= tegra_map_common_io,  	.smp		= smp_ops(tegra_smp_ops), -	.init_early	= tegra20_init_early, +	.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, -	.dt_compat	= tegra20_dt_board_compat, +	.dt_compat	= tegra_dt_board_compat,  MACHINE_END diff --git a/arch/arm/mach-tegra/tegra114_speedo.c b/arch/arm/mach-tegra/tegra114_speedo.c new file mode 100644 index 00000000000..5218d4853cd --- /dev/null +++ b/arch/arm/mach-tegra/tegra114_speedo.c @@ -0,0 +1,104 @@ +/* + * 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 <linux/bug.h> + +#include "fuse.h" + +#define CORE_PROCESS_CORNERS_NUM	2 +#define CPU_PROCESS_CORNERS_NUM		2 + +enum { +	THRESHOLD_INDEX_0, +	THRESHOLD_INDEX_1, +	THRESHOLD_INDEX_COUNT, +}; + +static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { +	{1123,     UINT_MAX}, +	{0,        UINT_MAX}, +}; + +static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { +	{1695,     UINT_MAX}, +	{0,        UINT_MAX}, +}; + +static void rev_sku_to_speedo_ids(int rev, int sku, int *threshold) +{ +	u32 tmp; + +	switch (sku) { +	case 0x00: +	case 0x10: +	case 0x05: +	case 0x06: +		tegra_cpu_speedo_id = 1; +		tegra_soc_speedo_id = 0; +		*threshold = THRESHOLD_INDEX_0; +		break; + +	case 0x03: +	case 0x04: +		tegra_cpu_speedo_id = 2; +		tegra_soc_speedo_id = 1; +		*threshold = THRESHOLD_INDEX_1; +		break; + +	default: +		pr_err("Tegra114 Unknown SKU %d\n", sku); +		tegra_cpu_speedo_id = 0; +		tegra_soc_speedo_id = 0; +		*threshold = THRESHOLD_INDEX_0; +		break; +	} + +	if (rev == TEGRA_REVISION_A01) { +		tmp = tegra_fuse_readl(0x270) << 1; +		tmp |= tegra_fuse_readl(0x26c); +		if (!tmp) +			tegra_cpu_speedo_id = 0; +	} +} + +void tegra114_init_speedo_data(void) +{ +	u32 cpu_speedo_val; +	u32 core_speedo_val; +	int threshold; +	int i; + +	BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != +			THRESHOLD_INDEX_COUNT); +	BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != +			THRESHOLD_INDEX_COUNT); + +	rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id, &threshold); + +	cpu_speedo_val = tegra_fuse_readl(0x12c) + 1024; +	core_speedo_val = tegra_fuse_readl(0x134); + +	for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) +		if (cpu_speedo_val < cpu_process_speedos[threshold][i]) +			break; +	tegra_cpu_process_id = i; + +	for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) +		if (core_speedo_val < core_process_speedos[threshold][i]) +			break; +	tegra_core_process_id = i; +} diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index ce7ce42a1ac..9e8bdfa2b36 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -276,7 +276,7 @@ static struct tegra_emc_pdata *tegra_emc_fill_pdata(struct platform_device *pdev  	int i;  	WARN_ON(pdev->dev.platform_data); -	BUG_ON(IS_ERR_OR_NULL(c)); +	BUG_ON(IS_ERR(c));  	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);  	pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables),  |