mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
70
arch/arm/mach-tegra/Kconfig
Normal file
70
arch/arm/mach-tegra/Kconfig
Normal file
|
@ -0,0 +1,70 @@
|
|||
menuconfig ARCH_TEGRA
|
||||
bool "NVIDIA Tegra" if ARCH_MULTI_V7
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select ARCH_SUPPORTS_TRUSTED_FOUNDATIONS
|
||||
select ARM_GIC
|
||||
select CLKSRC_MMIO
|
||||
select HAVE_ARM_SCU if SMP
|
||||
select HAVE_ARM_TWD if SMP
|
||||
select PINCTRL
|
||||
select ARCH_HAS_RESET_CONTROLLER
|
||||
select RESET_CONTROLLER
|
||||
select SOC_BUS
|
||||
select USB_ULPI if USB_PHY
|
||||
select USB_ULPI_VIEWPORT if USB_PHY
|
||||
help
|
||||
This enables support for NVIDIA Tegra based systems.
|
||||
|
||||
if ARCH_TEGRA
|
||||
|
||||
config ARCH_TEGRA_2x_SOC
|
||||
bool "Enable support for Tegra20 family"
|
||||
select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
|
||||
select ARM_ERRATA_720789
|
||||
select ARM_ERRATA_754327 if SMP
|
||||
select ARM_ERRATA_764369 if SMP
|
||||
select PINCTRL_TEGRA20
|
||||
select PL310_ERRATA_727915 if CACHE_L2X0
|
||||
select PL310_ERRATA_769419 if CACHE_L2X0
|
||||
help
|
||||
Support for NVIDIA Tegra AP20 and T20 processors, based on the
|
||||
ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
|
||||
|
||||
config ARCH_TEGRA_3x_SOC
|
||||
bool "Enable support for Tegra30 family"
|
||||
select ARM_ERRATA_754322
|
||||
select ARM_ERRATA_764369 if SMP
|
||||
select PINCTRL_TEGRA30
|
||||
select PL310_ERRATA_769419 if CACHE_L2X0
|
||||
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 ARM_ERRATA_798181 if SMP
|
||||
select ARM_L1_CACHE_SHIFT_6
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select PINCTRL_TEGRA114
|
||||
help
|
||||
Support for NVIDIA Tegra T114 processor family, based on the
|
||||
ARM CortexA15MP CPU
|
||||
|
||||
config ARCH_TEGRA_124_SOC
|
||||
bool "Enable support for Tegra124 family"
|
||||
select ARM_L1_CACHE_SHIFT_6
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select PINCTRL_TEGRA124
|
||||
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"
|
||||
default y
|
||||
help
|
||||
Adds AHB configuration functionality for NVIDIA Tegra SoCs,
|
||||
which controls AHB bus master arbitration and some
|
||||
performance parameters(priority, prefech size).
|
||||
|
||||
endif
|
36
arch/arm/mach-tegra/Makefile
Normal file
36
arch/arm/mach-tegra/Makefile
Normal file
|
@ -0,0 +1,36 @@
|
|||
asflags-y += -march=armv7-a
|
||||
|
||||
obj-y += io.o
|
||||
obj-y += irq.o
|
||||
obj-y += flowctrl.o
|
||||
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) += 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) += 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_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_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
|
52
arch/arm/mach-tegra/board-paz00.c
Normal file
52
arch/arm/mach-tegra/board-paz00.c
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/board-paz00.c
|
||||
*
|
||||
* Copyright (C) 2011 Marc Dietrich <marvin24@gmx.de>
|
||||
*
|
||||
* Based on board-harmony.c
|
||||
* 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/gpio/machine.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rfkill-gpio.h>
|
||||
|
||||
#include "board.h"
|
||||
|
||||
static struct rfkill_gpio_platform_data wifi_rfkill_platform_data = {
|
||||
.name = "wifi_rfkill",
|
||||
.type = RFKILL_TYPE_WLAN,
|
||||
};
|
||||
|
||||
static struct platform_device wifi_rfkill_device = {
|
||||
.name = "rfkill_gpio",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &wifi_rfkill_platform_data,
|
||||
},
|
||||
};
|
||||
|
||||
static struct gpiod_lookup_table wifi_gpio_lookup = {
|
||||
.dev_id = "rfkill_gpio",
|
||||
.table = {
|
||||
GPIO_LOOKUP_IDX("tegra-gpio", 25, NULL, 0, 0),
|
||||
GPIO_LOOKUP_IDX("tegra-gpio", 85, NULL, 1, 0),
|
||||
{ },
|
||||
},
|
||||
};
|
||||
|
||||
void __init tegra_paz00_wifikill_init(void)
|
||||
{
|
||||
gpiod_add_lookup_table(&wifi_gpio_lookup);
|
||||
platform_device_register(&wifi_rfkill_device);
|
||||
}
|
33
arch/arm/mach-tegra/board.h
Normal file
33
arch/arm/mach-tegra/board.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/board.h
|
||||
*
|
||||
* Copyright (c) 2013 NVIDIA Corporation. All rights reserved.
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
* 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_BOARD_H
|
||||
#define __MACH_TEGRA_BOARD_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
void __init tegra_map_common_io(void);
|
||||
void __init tegra_init_irq(void);
|
||||
|
||||
void __init tegra_paz00_wifikill_init(void);
|
||||
|
||||
#endif
|
4
arch/arm/mach-tegra/common.h
Normal file
4
arch/arm/mach-tegra/common.h
Normal file
|
@ -0,0 +1,4 @@
|
|||
extern struct smp_operations tegra_smp_ops;
|
||||
|
||||
extern int tegra_cpu_kill(unsigned int cpu);
|
||||
extern void tegra_cpu_die(unsigned int cpu);
|
89
arch/arm/mach-tegra/cpuidle-tegra114.c
Normal file
89
arch/arm/mach-tegra/cpuidle-tegra114.c
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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 <asm/firmware.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.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);
|
||||
|
||||
call_firmware_op(prepare_idle);
|
||||
|
||||
/* Do suspend by ourselves if the firmware does not implement it */
|
||||
if (call_firmware_op(do_idle) == -ENOSYS)
|
||||
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 = 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
|
||||
},
|
||||
};
|
||||
|
||||
int __init tegra114_cpuidle_init(void)
|
||||
{
|
||||
return cpuidle_register(&tegra_idle_driver, NULL);
|
||||
}
|
229
arch/arm/mach-tegra/cpuidle-tegra20.c
Normal file
229
arch/arm/mach-tegra/cpuidle-tegra20.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* CPU idle driver for Tegra CPUs
|
||||
*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation.
|
||||
* Copyright (c) 2011 Google, Inc.
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
* Gary King <gking@nvidia.com>
|
||||
*
|
||||
* Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include "flowctrl.h"
|
||||
#include "iomap.h"
|
||||
#include "irq.h"
|
||||
#include "pm.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static bool abort_flag;
|
||||
static atomic_t abort_barrier;
|
||||
static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index);
|
||||
#define TEGRA20_MAX_STATES 2
|
||||
#else
|
||||
#define TEGRA20_MAX_STATES 1
|
||||
#endif
|
||||
|
||||
static struct cpuidle_driver tegra_idle_driver = {
|
||||
.name = "tegra_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.states = {
|
||||
ARM_CPUIDLE_WFI_STATE_PWR(600),
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
{
|
||||
.enter = tegra20_idle_lp2_coupled,
|
||||
.exit_latency = 5000,
|
||||
.target_residency = 10000,
|
||||
.power_usage = 0,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_COUPLED,
|
||||
.name = "powered-down",
|
||||
.desc = "CPU power gated",
|
||||
},
|
||||
#endif
|
||||
},
|
||||
.state_count = TEGRA20_MAX_STATES,
|
||||
.safe_state_index = 0,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_SMP
|
||||
static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
|
||||
|
||||
static int tegra20_reset_sleeping_cpu_1(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
tegra_pen_lock();
|
||||
|
||||
if (readl(pmc + PMC_SCRATCH41) == CPU_RESETTABLE)
|
||||
tegra20_cpu_shutdown(1);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
tegra_pen_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tegra20_wake_cpu1_from_reset(void)
|
||||
{
|
||||
tegra_pen_lock();
|
||||
|
||||
tegra20_cpu_clear_resettable();
|
||||
|
||||
/* enable cpu clock on cpu */
|
||||
tegra_enable_cpu_clock(1);
|
||||
|
||||
/* take the CPU out of reset */
|
||||
tegra_cpu_out_of_reset(1);
|
||||
|
||||
/* unhalt the cpu */
|
||||
flowctrl_write_cpu_halt(1, 0);
|
||||
|
||||
tegra_pen_unlock();
|
||||
}
|
||||
|
||||
static int tegra20_reset_cpu_1(void)
|
||||
{
|
||||
if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1())
|
||||
return 0;
|
||||
|
||||
tegra20_wake_cpu1_from_reset();
|
||||
return -EBUSY;
|
||||
}
|
||||
#else
|
||||
static inline void tegra20_wake_cpu1_from_reset(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int tegra20_reset_cpu_1(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
while (tegra20_cpu_is_resettable_soon())
|
||||
cpu_relax();
|
||||
|
||||
if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready())
|
||||
return false;
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
|
||||
|
||||
tegra_idle_lp2_last();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
|
||||
|
||||
if (cpu_online(1))
|
||||
tegra20_wake_cpu1_from_reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
|
||||
|
||||
cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
|
||||
|
||||
tegra20_cpu_clear_resettable();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
bool entered_lp2 = false;
|
||||
|
||||
if (tegra_pending_sgi())
|
||||
ACCESS_ONCE(abort_flag) = true;
|
||||
|
||||
cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
|
||||
|
||||
if (abort_flag) {
|
||||
cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
|
||||
abort_flag = false; /* clean flag for next coming */
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
local_fiq_disable();
|
||||
|
||||
tegra_set_cpu_in_lp2();
|
||||
cpu_pm_enter();
|
||||
|
||||
if (dev->cpu == 0)
|
||||
entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index);
|
||||
else
|
||||
entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
|
||||
|
||||
cpu_pm_exit();
|
||||
tegra_clear_cpu_in_lp2();
|
||||
|
||||
local_fiq_enable();
|
||||
|
||||
smp_rmb();
|
||||
|
||||
return entered_lp2 ? index : 0;
|
||||
}
|
||||
#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);
|
||||
}
|
149
arch/arm/mach-tegra/cpuidle-tegra30.c
Normal file
149
arch/arm/mach-tegra/cpuidle-tegra30.c
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* CPU idle driver for Tegra CPUs
|
||||
*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation.
|
||||
* Copyright (c) 2011 Google, Inc.
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
* Gary King <gking@nvidia.com>
|
||||
*
|
||||
* Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
#include "pm.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra30_idle_lp2(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index);
|
||||
#endif
|
||||
|
||||
static struct cpuidle_driver tegra_idle_driver = {
|
||||
.name = "tegra_idle",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.state_count = 2,
|
||||
#else
|
||||
.state_count = 1,
|
||||
#endif
|
||||
.states = {
|
||||
[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
[1] = {
|
||||
.enter = tegra30_idle_lp2,
|
||||
.exit_latency = 2000,
|
||||
.target_residency = 2200,
|
||||
.power_usage = 0,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "powered-down",
|
||||
.desc = "CPU power gated",
|
||||
},
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
/* All CPUs entering LP2 is not working.
|
||||
* Don't let CPU0 enter LP2 when any secondary CPU is online.
|
||||
*/
|
||||
if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) {
|
||||
cpu_do_idle();
|
||||
return false;
|
||||
}
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
|
||||
|
||||
tegra_idle_lp2_last();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
|
||||
|
||||
smp_wmb();
|
||||
|
||||
cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int tegra30_idle_lp2(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
bool entered_lp2 = false;
|
||||
bool last_cpu;
|
||||
|
||||
local_fiq_disable();
|
||||
|
||||
last_cpu = tegra_set_cpu_in_lp2();
|
||||
cpu_pm_enter();
|
||||
|
||||
if (dev->cpu == 0) {
|
||||
if (last_cpu)
|
||||
entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv,
|
||||
index);
|
||||
else
|
||||
cpu_do_idle();
|
||||
} else {
|
||||
entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
|
||||
}
|
||||
|
||||
cpu_pm_exit();
|
||||
tegra_clear_cpu_in_lp2();
|
||||
|
||||
local_fiq_enable();
|
||||
|
||||
smp_rmb();
|
||||
|
||||
return (entered_lp2) ? index : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int __init tegra30_cpuidle_init(void)
|
||||
{
|
||||
return cpuidle_register(&tegra_idle_driver, NULL);
|
||||
}
|
59
arch/arm/mach-tegra/cpuidle.c
Normal file
59
arch/arm/mach-tegra/cpuidle.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/cpuidle.c
|
||||
*
|
||||
* CPU idle driver for Tegra CPUs
|
||||
*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation.
|
||||
* Copyright (c) 2011 Google, Inc.
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
* Gary King <gking@nvidia.com>
|
||||
*
|
||||
* Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include "cpuidle.h"
|
||||
|
||||
void __init tegra_cpuidle_init(void)
|
||||
{
|
||||
switch (tegra_get_chip_id()) {
|
||||
case TEGRA20:
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
|
||||
tegra20_cpuidle_init();
|
||||
break;
|
||||
case TEGRA30:
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC))
|
||||
tegra30_cpuidle_init();
|
||||
break;
|
||||
case TEGRA114:
|
||||
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_get_chip_id()) {
|
||||
case TEGRA20:
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
|
||||
tegra20_cpuidle_pcie_irqs_in_use();
|
||||
break;
|
||||
}
|
||||
}
|
30
arch/arm/mach-tegra/cpuidle.h
Normal file
30
arch/arm/mach-tegra/cpuidle.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_CPUIDLE_H
|
||||
#define __MACH_TEGRA_CPUIDLE_H
|
||||
|
||||
#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);
|
||||
#else
|
||||
static inline void tegra_cpuidle_init(void) {}
|
||||
#endif
|
||||
|
||||
#endif
|
171
arch/arm/mach-tegra/flowctrl.c
Normal file
171
arch/arm/mach-tegra/flowctrl.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/flowctrl.c
|
||||
*
|
||||
* functions and macros to control the flowcontroller
|
||||
*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include "flowctrl.h"
|
||||
|
||||
static u8 flowctrl_offset_halt_cpu[] = {
|
||||
FLOW_CTRL_HALT_CPU0_EVENTS,
|
||||
FLOW_CTRL_HALT_CPU1_EVENTS,
|
||||
FLOW_CTRL_HALT_CPU1_EVENTS + 8,
|
||||
FLOW_CTRL_HALT_CPU1_EVENTS + 16,
|
||||
};
|
||||
|
||||
static u8 flowctrl_offset_cpu_csr[] = {
|
||||
FLOW_CTRL_CPU0_CSR,
|
||||
FLOW_CTRL_CPU1_CSR,
|
||||
FLOW_CTRL_CPU1_CSR + 8,
|
||||
FLOW_CTRL_CPU1_CSR + 16,
|
||||
};
|
||||
|
||||
static void __iomem *tegra_flowctrl_base;
|
||||
|
||||
static void flowctrl_update(u8 offset, u32 value)
|
||||
{
|
||||
writel(value, tegra_flowctrl_base + offset);
|
||||
|
||||
/* ensure the update has reached the flow controller */
|
||||
wmb();
|
||||
readl_relaxed(tegra_flowctrl_base + offset);
|
||||
}
|
||||
|
||||
u32 flowctrl_read_cpu_csr(unsigned int cpuid)
|
||||
{
|
||||
u8 offset = flowctrl_offset_cpu_csr[cpuid];
|
||||
|
||||
return readl(tegra_flowctrl_base + offset);
|
||||
}
|
||||
|
||||
void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value)
|
||||
{
|
||||
return flowctrl_update(flowctrl_offset_cpu_csr[cpuid], value);
|
||||
}
|
||||
|
||||
void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value)
|
||||
{
|
||||
return flowctrl_update(flowctrl_offset_halt_cpu[cpuid], value);
|
||||
}
|
||||
|
||||
void flowctrl_cpu_suspend_enter(unsigned int cpuid)
|
||||
{
|
||||
unsigned int reg;
|
||||
int i;
|
||||
|
||||
reg = flowctrl_read_cpu_csr(cpuid);
|
||||
switch (tegra_get_chip_id()) {
|
||||
case TEGRA20:
|
||||
/* clear wfe bitmap */
|
||||
reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
|
||||
/* clear wfi bitmap */
|
||||
reg &= ~TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP;
|
||||
/* pwr gating on wfe */
|
||||
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 */
|
||||
reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
|
||||
/* pwr gating on wfi */
|
||||
reg |= TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 << cpuid;
|
||||
break;
|
||||
}
|
||||
reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr flag */
|
||||
reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event flag */
|
||||
reg |= FLOW_CTRL_CSR_ENABLE; /* pwr gating */
|
||||
flowctrl_write_cpu_csr(cpuid, reg);
|
||||
|
||||
for (i = 0; i < num_possible_cpus(); i++) {
|
||||
if (i == cpuid)
|
||||
continue;
|
||||
reg = flowctrl_read_cpu_csr(i);
|
||||
reg |= FLOW_CTRL_CSR_EVENT_FLAG;
|
||||
reg |= FLOW_CTRL_CSR_INTR_FLAG;
|
||||
flowctrl_write_cpu_csr(i, reg);
|
||||
}
|
||||
}
|
||||
|
||||
void flowctrl_cpu_suspend_exit(unsigned int cpuid)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
/* Disable powergating via flow controller for CPU0 */
|
||||
reg = flowctrl_read_cpu_csr(cpuid);
|
||||
switch (tegra_get_chip_id()) {
|
||||
case TEGRA20:
|
||||
/* clear wfe bitmap */
|
||||
reg &= ~TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP;
|
||||
/* clear wfi bitmap */
|
||||
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 */
|
||||
reg &= ~TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP;
|
||||
break;
|
||||
}
|
||||
reg &= ~FLOW_CTRL_CSR_ENABLE; /* clear enable */
|
||||
reg |= FLOW_CTRL_CSR_INTR_FLAG; /* clear intr */
|
||||
reg |= FLOW_CTRL_CSR_EVENT_FLAG; /* clear event */
|
||||
flowctrl_write_cpu_csr(cpuid, reg);
|
||||
}
|
||||
|
||||
static const struct of_device_id matches[] __initconst = {
|
||||
{ .compatible = "nvidia,tegra124-flowctrl" },
|
||||
{ .compatible = "nvidia,tegra114-flowctrl" },
|
||||
{ .compatible = "nvidia,tegra30-flowctrl" },
|
||||
{ .compatible = "nvidia,tegra20-flowctrl" },
|
||||
{ }
|
||||
};
|
||||
|
||||
void __init tegra_flowctrl_init(void)
|
||||
{
|
||||
/* hardcoded fallback if device tree node is missing */
|
||||
unsigned long base = 0x60007000;
|
||||
unsigned long size = SZ_4K;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_matching_node(NULL, matches);
|
||||
if (np) {
|
||||
struct resource res;
|
||||
|
||||
if (of_address_to_resource(np, 0, &res) == 0) {
|
||||
size = resource_size(&res);
|
||||
base = res.start;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
}
|
||||
|
||||
tegra_flowctrl_base = ioremap_nocache(base, size);
|
||||
}
|
66
arch/arm/mach-tegra/flowctrl.h
Normal file
66
arch/arm/mach-tegra/flowctrl.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/flowctrl.h
|
||||
*
|
||||
* functions and macros to control the flowcontroller
|
||||
*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_FLOWCTRL_H
|
||||
#define __MACH_TEGRA_FLOWCTRL_H
|
||||
|
||||
#define FLOW_CTRL_HALT_CPU0_EVENTS 0x0
|
||||
#define FLOW_CTRL_WAITEVENT (2 << 29)
|
||||
#define FLOW_CTRL_WAIT_FOR_INTERRUPT (4 << 29)
|
||||
#define FLOW_CTRL_JTAG_RESUME (1 << 28)
|
||||
#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
|
||||
|
||||
#define TEGRA20_FLOW_CTRL_CSR_WFE_CPU0 (1 << 4)
|
||||
#define TEGRA20_FLOW_CTRL_CSR_WFE_BITMAP (3 << 4)
|
||||
#define TEGRA20_FLOW_CTRL_CSR_WFI_BITMAP 0
|
||||
|
||||
#define TEGRA30_FLOW_CTRL_CSR_WFI_CPU0 (1 << 8)
|
||||
#define TEGRA30_FLOW_CTRL_CSR_WFE_BITMAP (0xF << 4)
|
||||
#define TEGRA30_FLOW_CTRL_CSR_WFI_BITMAP (0xF << 8)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
u32 flowctrl_read_cpu_csr(unsigned int cpuid);
|
||||
void flowctrl_write_cpu_csr(unsigned int cpuid, u32 value);
|
||||
void flowctrl_write_cpu_halt(unsigned int cpuid, u32 value);
|
||||
|
||||
void flowctrl_cpu_suspend_enter(unsigned int cpuid);
|
||||
void flowctrl_cpu_suspend_exit(unsigned int cpuid);
|
||||
|
||||
void tegra_flowctrl_init(void);
|
||||
#endif
|
||||
|
||||
#endif
|
12
arch/arm/mach-tegra/headsmp.S
Normal file
12
arch/arm/mach-tegra/headsmp.S
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include "sleep.h"
|
||||
|
||||
.section ".text.head", "ax"
|
||||
|
||||
ENTRY(tegra_secondary_startup)
|
||||
check_cpu_part_num 0xc09, r8, r9
|
||||
bleq v7_invalidate_l1
|
||||
b secondary_startup
|
||||
ENDPROC(tegra_secondary_startup)
|
76
arch/arm/mach-tegra/hotplug.c
Normal file
76
arch/arm/mach-tegra/hotplug.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2002 ARM Ltd.
|
||||
* 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
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
#include "sleep.h"
|
||||
|
||||
static void (*tegra_hotplug_shutdown)(void);
|
||||
|
||||
int tegra_cpu_kill(unsigned cpu)
|
||||
{
|
||||
cpu = cpu_logical_map(cpu);
|
||||
|
||||
/* Clock gate the CPU */
|
||||
tegra_wait_cpu_in_reset(cpu);
|
||||
tegra_disable_cpu_clock(cpu);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* platform-specific code to shutdown a CPU
|
||||
*
|
||||
* Called with IRQs disabled
|
||||
*/
|
||||
void __ref tegra_cpu_die(unsigned int cpu)
|
||||
{
|
||||
if (!tegra_hotplug_shutdown) {
|
||||
WARN(1, "hotplug is not yet initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clean L1 data cache */
|
||||
tegra_disable_clean_inv_dcache(TEGRA_FLUSH_CACHE_LOUIS);
|
||||
|
||||
/* Shut down the current CPU. */
|
||||
tegra_hotplug_shutdown();
|
||||
|
||||
/* Should never return here. */
|
||||
BUG();
|
||||
}
|
||||
|
||||
static int __init tegra_hotplug_init(void)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
|
||||
return 0;
|
||||
|
||||
if (!soc_is_tegra())
|
||||
return 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_get_chip_id() == TEGRA20)
|
||||
tegra_hotplug_shutdown = tegra20_hotplug_shutdown;
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_get_chip_id() == TEGRA30)
|
||||
tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_get_chip_id() == TEGRA114)
|
||||
tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_get_chip_id() == TEGRA124)
|
||||
tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
|
||||
|
||||
return 0;
|
||||
}
|
||||
pure_initcall(tegra_hotplug_init);
|
64
arch/arm/mach-tegra/io.c
Normal file
64
arch/arm/mach-tegra/io.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/io.c
|
||||
*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "iomap.h"
|
||||
|
||||
static struct map_desc tegra_io_desc[] __initdata = {
|
||||
{
|
||||
.virtual = (unsigned long)IO_PPSB_VIRT,
|
||||
.pfn = __phys_to_pfn(IO_PPSB_PHYS),
|
||||
.length = IO_PPSB_SIZE,
|
||||
.type = MT_DEVICE,
|
||||
},
|
||||
{
|
||||
.virtual = (unsigned long)IO_APB_VIRT,
|
||||
.pfn = __phys_to_pfn(IO_APB_PHYS),
|
||||
.length = IO_APB_SIZE,
|
||||
.type = MT_DEVICE,
|
||||
},
|
||||
{
|
||||
.virtual = (unsigned long)IO_CPU_VIRT,
|
||||
.pfn = __phys_to_pfn(IO_CPU_PHYS),
|
||||
.length = IO_CPU_SIZE,
|
||||
.type = MT_DEVICE,
|
||||
},
|
||||
{
|
||||
.virtual = (unsigned long)IO_IRAM_VIRT,
|
||||
.pfn = __phys_to_pfn(IO_IRAM_PHYS),
|
||||
.length = IO_IRAM_SIZE,
|
||||
.type = MT_DEVICE,
|
||||
},
|
||||
};
|
||||
|
||||
void __init tegra_map_common_io(void)
|
||||
{
|
||||
debug_ll_io_init();
|
||||
iotable_init(tegra_io_desc, ARRAY_SIZE(tegra_io_desc));
|
||||
}
|
156
arch/arm/mach-tegra/iomap.h
Normal file
156
arch/arm/mach-tegra/iomap.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
* 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_IOMAP_H
|
||||
#define __MACH_TEGRA_IOMAP_H
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/sizes.h>
|
||||
|
||||
#define TEGRA_IRAM_BASE 0x40000000
|
||||
#define TEGRA_IRAM_SIZE SZ_256K
|
||||
|
||||
#define TEGRA_ARM_PERIF_BASE 0x50040000
|
||||
#define TEGRA_ARM_PERIF_SIZE SZ_8K
|
||||
|
||||
#define TEGRA_ARM_INT_DIST_BASE 0x50041000
|
||||
#define TEGRA_ARM_INT_DIST_SIZE SZ_4K
|
||||
|
||||
#define TEGRA_PRIMARY_ICTLR_BASE 0x60004000
|
||||
#define TEGRA_PRIMARY_ICTLR_SIZE SZ_64
|
||||
|
||||
#define TEGRA_SECONDARY_ICTLR_BASE 0x60004100
|
||||
#define TEGRA_SECONDARY_ICTLR_SIZE SZ_64
|
||||
|
||||
#define TEGRA_TERTIARY_ICTLR_BASE 0x60004200
|
||||
#define TEGRA_TERTIARY_ICTLR_SIZE SZ_64
|
||||
|
||||
#define TEGRA_QUATERNARY_ICTLR_BASE 0x60004300
|
||||
#define TEGRA_QUATERNARY_ICTLR_SIZE SZ_64
|
||||
|
||||
#define TEGRA_QUINARY_ICTLR_BASE 0x60004400
|
||||
#define TEGRA_QUINARY_ICTLR_SIZE SZ_64
|
||||
|
||||
#define TEGRA_TMR1_BASE 0x60005000
|
||||
#define TEGRA_TMR1_SIZE SZ_8
|
||||
|
||||
#define TEGRA_TMR2_BASE 0x60005008
|
||||
#define TEGRA_TMR2_SIZE SZ_8
|
||||
|
||||
#define TEGRA_TMRUS_BASE 0x60005010
|
||||
#define TEGRA_TMRUS_SIZE SZ_64
|
||||
|
||||
#define TEGRA_TMR3_BASE 0x60005050
|
||||
#define TEGRA_TMR3_SIZE SZ_8
|
||||
|
||||
#define TEGRA_TMR4_BASE 0x60005058
|
||||
#define TEGRA_TMR4_SIZE SZ_8
|
||||
|
||||
#define TEGRA_CLK_RESET_BASE 0x60006000
|
||||
#define TEGRA_CLK_RESET_SIZE SZ_4K
|
||||
|
||||
#define TEGRA_FLOW_CTRL_BASE 0x60007000
|
||||
#define TEGRA_FLOW_CTRL_SIZE 20
|
||||
|
||||
#define TEGRA_SB_BASE 0x6000C200
|
||||
#define TEGRA_SB_SIZE 256
|
||||
|
||||
#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_UARTA_BASE 0x70006000
|
||||
#define TEGRA_UARTA_SIZE SZ_64
|
||||
|
||||
#define TEGRA_UARTB_BASE 0x70006040
|
||||
#define TEGRA_UARTB_SIZE SZ_64
|
||||
|
||||
#define TEGRA_UARTC_BASE 0x70006200
|
||||
#define TEGRA_UARTC_SIZE SZ_256
|
||||
|
||||
#define TEGRA_UARTD_BASE 0x70006300
|
||||
#define TEGRA_UARTD_SIZE SZ_256
|
||||
|
||||
#define TEGRA_UARTE_BASE 0x70006400
|
||||
#define TEGRA_UARTE_SIZE SZ_256
|
||||
|
||||
#define TEGRA_PMC_BASE 0x7000E400
|
||||
#define TEGRA_PMC_SIZE SZ_256
|
||||
|
||||
#define TEGRA_EMC_BASE 0x7000F400
|
||||
#define TEGRA_EMC_SIZE SZ_1K
|
||||
|
||||
#define TEGRA_FUSE_BASE 0x7000F800
|
||||
#define TEGRA_FUSE_SIZE SZ_1K
|
||||
|
||||
#define TEGRA_EMC0_BASE 0x7001A000
|
||||
#define TEGRA_EMC0_SIZE SZ_2K
|
||||
|
||||
#define TEGRA_EMC1_BASE 0x7001A800
|
||||
#define TEGRA_EMC1_SIZE SZ_2K
|
||||
|
||||
#define TEGRA124_EMC_BASE 0x7001B000
|
||||
#define TEGRA124_EMC_SIZE SZ_2K
|
||||
|
||||
#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
|
||||
* at the start of each).
|
||||
*
|
||||
* We will just map the first MMU section of each window (to minimize
|
||||
* pt entries needed) and provide a macro to transform physical
|
||||
* io addresses to an appropriate void __iomem *.
|
||||
*/
|
||||
|
||||
#define IO_IRAM_PHYS 0x40000000
|
||||
#define IO_IRAM_VIRT IOMEM(0xFE400000)
|
||||
#define IO_IRAM_SIZE SZ_256K
|
||||
|
||||
#define IO_CPU_PHYS 0x50040000
|
||||
#define IO_CPU_VIRT IOMEM(0xFE440000)
|
||||
#define IO_CPU_SIZE SZ_16K
|
||||
|
||||
#define IO_PPSB_PHYS 0x60000000
|
||||
#define IO_PPSB_VIRT IOMEM(0xFE200000)
|
||||
#define IO_PPSB_SIZE SECTION_SIZE
|
||||
|
||||
#define IO_APB_PHYS 0x70000000
|
||||
#define IO_APB_VIRT IOMEM(0xFE000000)
|
||||
#define IO_APB_SIZE SECTION_SIZE
|
||||
|
||||
#define IO_TO_VIRT_BETWEEN(p, st, sz) ((p) >= (st) && (p) < ((st) + (sz)))
|
||||
#define IO_TO_VIRT_XLATE(p, pst, vst) (((p) - (pst) + (vst)))
|
||||
|
||||
#define IO_TO_VIRT(n) ( \
|
||||
IO_TO_VIRT_BETWEEN((n), IO_PPSB_PHYS, IO_PPSB_SIZE) ? \
|
||||
IO_TO_VIRT_XLATE((n), IO_PPSB_PHYS, IO_PPSB_VIRT) : \
|
||||
IO_TO_VIRT_BETWEEN((n), IO_APB_PHYS, IO_APB_SIZE) ? \
|
||||
IO_TO_VIRT_XLATE((n), IO_APB_PHYS, IO_APB_VIRT) : \
|
||||
IO_TO_VIRT_BETWEEN((n), IO_CPU_PHYS, IO_CPU_SIZE) ? \
|
||||
IO_TO_VIRT_XLATE((n), IO_CPU_PHYS, IO_CPU_VIRT) : \
|
||||
IO_TO_VIRT_BETWEEN((n), IO_IRAM_PHYS, IO_IRAM_SIZE) ? \
|
||||
IO_TO_VIRT_XLATE((n), IO_IRAM_PHYS, IO_IRAM_VIRT) : \
|
||||
NULL)
|
||||
|
||||
#define IO_ADDRESS(n) (IO_TO_VIRT(n))
|
||||
|
||||
#endif
|
32
arch/arm/mach-tegra/irammap.h
Normal file
32
arch/arm/mach-tegra/irammap.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_IRAMMAP_H
|
||||
#define __MACH_TEGRA_IRAMMAP_H
|
||||
|
||||
#include <asm/sizes.h>
|
||||
|
||||
/* The first 1K of IRAM is permanently reserved for the CPU reset handler */
|
||||
#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
|
295
arch/arm/mach-tegra/irq.c
Normal file
295
arch/arm/mach-tegra/irq.c
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@android.com>
|
||||
*
|
||||
* 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
|
||||
* 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/cpu_pm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "iomap.h"
|
||||
|
||||
#define ICTLR_CPU_IEP_VFIQ 0x08
|
||||
#define ICTLR_CPU_IEP_FIR 0x14
|
||||
#define ICTLR_CPU_IEP_FIR_SET 0x18
|
||||
#define ICTLR_CPU_IEP_FIR_CLR 0x1c
|
||||
|
||||
#define ICTLR_CPU_IER 0x20
|
||||
#define ICTLR_CPU_IER_SET 0x24
|
||||
#define ICTLR_CPU_IER_CLR 0x28
|
||||
#define ICTLR_CPU_IEP_CLASS 0x2C
|
||||
|
||||
#define ICTLR_COP_IER 0x30
|
||||
#define ICTLR_COP_IER_SET 0x34
|
||||
#define ICTLR_COP_IER_CLR 0x38
|
||||
#define ICTLR_COP_IEP_CLASS 0x3c
|
||||
|
||||
#define FIRST_LEGACY_IRQ 32
|
||||
#define TEGRA_MAX_NUM_ICTLRS 5
|
||||
|
||||
#define SGI_MASK 0xFFFF
|
||||
|
||||
static int num_ictlrs;
|
||||
|
||||
static void __iomem *ictlr_reg_base[] = {
|
||||
IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE),
|
||||
IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
|
||||
IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE),
|
||||
IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_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];
|
||||
static void __iomem *tegra_gic_cpu_base;
|
||||
#endif
|
||||
|
||||
bool tegra_pending_sgi(void)
|
||||
{
|
||||
u32 pending_set;
|
||||
void __iomem *distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
|
||||
|
||||
pending_set = readl_relaxed(distbase + GIC_DIST_PENDING_SET);
|
||||
|
||||
if (pending_set & SGI_MASK)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg)
|
||||
{
|
||||
void __iomem *base;
|
||||
u32 mask;
|
||||
|
||||
BUG_ON(irq < FIRST_LEGACY_IRQ ||
|
||||
irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32);
|
||||
|
||||
base = ictlr_reg_base[(irq - FIRST_LEGACY_IRQ) / 32];
|
||||
mask = BIT((irq - FIRST_LEGACY_IRQ) % 32);
|
||||
|
||||
__raw_writel(mask, base + reg);
|
||||
}
|
||||
|
||||
static void tegra_mask(struct irq_data *d)
|
||||
{
|
||||
if (d->hwirq < FIRST_LEGACY_IRQ)
|
||||
return;
|
||||
|
||||
tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_CLR);
|
||||
}
|
||||
|
||||
static void tegra_unmask(struct irq_data *d)
|
||||
{
|
||||
if (d->hwirq < FIRST_LEGACY_IRQ)
|
||||
return;
|
||||
|
||||
tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IER_SET);
|
||||
}
|
||||
|
||||
static void tegra_ack(struct irq_data *d)
|
||||
{
|
||||
if (d->hwirq < FIRST_LEGACY_IRQ)
|
||||
return;
|
||||
|
||||
tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR);
|
||||
}
|
||||
|
||||
static void tegra_eoi(struct irq_data *d)
|
||||
{
|
||||
if (d->hwirq < FIRST_LEGACY_IRQ)
|
||||
return;
|
||||
|
||||
tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_CLR);
|
||||
}
|
||||
|
||||
static int tegra_retrigger(struct irq_data *d)
|
||||
{
|
||||
if (d->hwirq < FIRST_LEGACY_IRQ)
|
||||
return 0;
|
||||
|
||||
tegra_irq_write_mask(d->hwirq, ICTLR_CPU_IEP_FIR_SET);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_set_wake(struct irq_data *d, unsigned int enable)
|
||||
{
|
||||
u32 irq = d->hwirq;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
void __iomem *distbase;
|
||||
|
||||
distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
|
||||
num_ictlrs = readl_relaxed(distbase + GIC_DIST_CTR) & 0x1f;
|
||||
|
||||
if (num_ictlrs > ARRAY_SIZE(ictlr_reg_base)) {
|
||||
WARN(1, "Too many (%d) interrupt controllers found. Maximum is %d.",
|
||||
num_ictlrs, ARRAY_SIZE(ictlr_reg_base));
|
||||
num_ictlrs = ARRAY_SIZE(ictlr_reg_base);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_ictlrs; i++) {
|
||||
void __iomem *ictlr = ictlr_reg_base[i];
|
||||
writel(~0, ictlr + ICTLR_CPU_IER_CLR);
|
||||
writel(0, ictlr + ICTLR_CPU_IEP_CLASS);
|
||||
}
|
||||
|
||||
gic_arch_extn.irq_ack = tegra_ack;
|
||||
gic_arch_extn.irq_eoi = tegra_eoi;
|
||||
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
|
||||
* initialized elsewhere under DT.
|
||||
*/
|
||||
if (!of_have_populated_dt())
|
||||
gic_init(0, 29, distbase,
|
||||
IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
|
||||
|
||||
tegra114_gic_cpu_pm_registration();
|
||||
}
|
28
arch/arm/mach-tegra/irq.h
Normal file
28
arch/arm/mach-tegra/irq.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __TEGRA_IRQ_H
|
||||
#define __TEGRA_IRQ_H
|
||||
|
||||
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
|
203
arch/arm/mach-tegra/platsmp.c
Normal file
203
arch/arm/mach-tegra/platsmp.c
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* linux/arch/arm/mach-tegra/platsmp.c
|
||||
*
|
||||
* Copyright (C) 2002 ARM Ltd.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Copyright (C) 2009 Palm
|
||||
* 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
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/smp_scu.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "flowctrl.h"
|
||||
#include "iomap.h"
|
||||
#include "reset.h"
|
||||
|
||||
static cpumask_t tegra_cpu_init_mask;
|
||||
|
||||
static void tegra_secondary_init(unsigned int cpu)
|
||||
{
|
||||
cpumask_set_cpu(cpu, &tegra_cpu_init_mask);
|
||||
}
|
||||
|
||||
|
||||
static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
{
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
{
|
||||
int ret;
|
||||
unsigned long timeout;
|
||||
|
||||
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
|
||||
* was different.
|
||||
*
|
||||
* For warm boot CPU that was resumed from CPU hotplug, the
|
||||
* power will be resumed automatically after un-halting the
|
||||
* flow controller of the warm boot CPU. We need to wait for
|
||||
* the confirmaiton that the CPU is powered then removing
|
||||
* 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_boot_secondary()
|
||||
* next time around.
|
||||
*/
|
||||
if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) {
|
||||
timeout = jiffies + msecs_to_jiffies(50);
|
||||
do {
|
||||
if (tegra_pmc_cpu_is_powered(cpu))
|
||||
goto remove_clamps;
|
||||
udelay(10);
|
||||
} while (time_before(jiffies, timeout));
|
||||
}
|
||||
|
||||
/*
|
||||
* The power status of the cold boot CPU is power gated as
|
||||
* default. To power up the cold boot CPU, the power should
|
||||
* be un-gated by un-toggling the power gate register
|
||||
* manually.
|
||||
*/
|
||||
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_pmc_cpu_is_powered(cpu)) {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIMEDOUT;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
remove_clamps:
|
||||
/* CPU partition is powered. Enable the CPU clock. */
|
||||
tegra_enable_cpu_clock(cpu);
|
||||
udelay(10);
|
||||
|
||||
/* Remove I/O clamps. */
|
||||
ret = tegra_pmc_cpu_remove_clamping(cpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
udelay(10);
|
||||
|
||||
flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */
|
||||
tegra_cpu_out_of_reset(cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
cpu = cpu_logical_map(cpu);
|
||||
|
||||
if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) {
|
||||
/*
|
||||
* Warm boot flow
|
||||
* The flow controller in charge of the power state and
|
||||
* control for each CPU.
|
||||
*/
|
||||
/* set SCLK as event trigger for flow controller */
|
||||
flowctrl_write_cpu_csr(cpu, 1);
|
||||
flowctrl_write_cpu_halt(cpu,
|
||||
FLOW_CTRL_WAITEVENT | FLOW_CTRL_SCLK_RESUME);
|
||||
} else {
|
||||
/*
|
||||
* Cold boot flow
|
||||
* The CPU is powered up by toggling PMC directly. It will
|
||||
* also initial power state in flow controller. After that,
|
||||
* the CPU's power state is maintained by flow controller.
|
||||
*/
|
||||
ret = tegra_pmc_cpu_power_on(cpu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_boot_secondary(unsigned int cpu,
|
||||
struct task_struct *idle)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_get_chip_id() == TEGRA20)
|
||||
return tegra20_boot_secondary(cpu, idle);
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_get_chip_id() == TEGRA30)
|
||||
return tegra30_boot_secondary(cpu, idle);
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_get_chip_id() == TEGRA114)
|
||||
return tegra114_boot_secondary(cpu, idle);
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_get_chip_id() == TEGRA124)
|
||||
return tegra114_boot_secondary(cpu, idle);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
/* Always mark the boot CPU (CPU0) as initialized. */
|
||||
cpumask_set_cpu(0, &tegra_cpu_init_mask);
|
||||
|
||||
if (scu_a9_has_base())
|
||||
scu_enable(IO_ADDRESS(scu_a9_get_base()));
|
||||
}
|
||||
|
||||
struct smp_operations tegra_smp_ops __initdata = {
|
||||
.smp_prepare_cpus = tegra_smp_prepare_cpus,
|
||||
.smp_secondary_init = tegra_secondary_init,
|
||||
.smp_boot_secondary = tegra_boot_secondary,
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
.cpu_kill = tegra_cpu_kill,
|
||||
.cpu_die = tegra_cpu_die,
|
||||
#endif
|
||||
};
|
35
arch/arm/mach-tegra/pm-tegra20.c
Normal file
35
arch/arm/mach-tegra/pm-tegra20.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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
|
35
arch/arm/mach-tegra/pm-tegra30.c
Normal file
35
arch/arm/mach-tegra/pm-tegra30.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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
|
400
arch/arm/mach-tegra/pm.c
Normal file
400
arch/arm/mach-tegra/pm.c
Normal file
|
@ -0,0 +1,400 @@
|
|||
/*
|
||||
* CPU complex suspend & resume functions for Tegra SoCs
|
||||
*
|
||||
* Copyright (c) 2009-2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
#include <soc/tegra/pm.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/idmap.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
#include "flowctrl.h"
|
||||
#include "iomap.h"
|
||||
#include "pm.h"
|
||||
#include "reset.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)
|
||||
{
|
||||
switch (tegra_get_chip_id()) {
|
||||
case TEGRA20:
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
|
||||
tegra_tear_down_cpu = tegra20_tear_down_cpu;
|
||||
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))
|
||||
tegra_tear_down_cpu = tegra30_tear_down_cpu;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* restore_cpu_complex
|
||||
*
|
||||
* restores cpu clock setting, clears flow controller
|
||||
*
|
||||
* Always called on CPU 0.
|
||||
*/
|
||||
static void restore_cpu_complex(void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
BUG_ON(cpu != 0);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
cpu = cpu_logical_map(cpu);
|
||||
#endif
|
||||
|
||||
/* Restore the CPU clock settings */
|
||||
tegra_cpu_clock_resume();
|
||||
|
||||
flowctrl_cpu_suspend_exit(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* suspend_cpu_complex
|
||||
*
|
||||
* saves pll state for use by restart_plls, prepares flow controller for
|
||||
* transition to suspend state
|
||||
*
|
||||
* Must always be called on cpu 0.
|
||||
*/
|
||||
static void suspend_cpu_complex(void)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
BUG_ON(cpu != 0);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
cpu = cpu_logical_map(cpu);
|
||||
#endif
|
||||
|
||||
/* Save the CPU clock settings */
|
||||
tegra_cpu_clock_suspend();
|
||||
|
||||
flowctrl_cpu_suspend_enter(cpu);
|
||||
}
|
||||
|
||||
void tegra_clear_cpu_in_lp2(void)
|
||||
{
|
||||
int phy_cpu_id = cpu_logical_map(smp_processor_id());
|
||||
u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
|
||||
|
||||
spin_lock(&tegra_lp2_lock);
|
||||
|
||||
BUG_ON(!(*cpu_in_lp2 & BIT(phy_cpu_id)));
|
||||
*cpu_in_lp2 &= ~BIT(phy_cpu_id);
|
||||
|
||||
spin_unlock(&tegra_lp2_lock);
|
||||
}
|
||||
|
||||
bool tegra_set_cpu_in_lp2(void)
|
||||
{
|
||||
int phy_cpu_id = cpu_logical_map(smp_processor_id());
|
||||
bool last_cpu = false;
|
||||
cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask;
|
||||
u32 *cpu_in_lp2 = tegra_cpu_lp2_mask;
|
||||
|
||||
spin_lock(&tegra_lp2_lock);
|
||||
|
||||
BUG_ON((*cpu_in_lp2 & BIT(phy_cpu_id)));
|
||||
*cpu_in_lp2 |= BIT(phy_cpu_id);
|
||||
|
||||
if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
|
||||
last_cpu = true;
|
||||
else if (tegra_get_chip_id() == TEGRA20 && phy_cpu_id == 1)
|
||||
tegra20_cpu_set_resettable_soon();
|
||||
|
||||
spin_unlock(&tegra_lp2_lock);
|
||||
return last_cpu;
|
||||
}
|
||||
|
||||
int tegra_cpu_do_idle(void)
|
||||
{
|
||||
return cpu_do_idle();
|
||||
}
|
||||
|
||||
static int tegra_sleep_cpu(unsigned long v2p)
|
||||
{
|
||||
setup_mm_for_reboot();
|
||||
tegra_sleep_cpu_finish(v2p);
|
||||
|
||||
/* should never here */
|
||||
BUG();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_pm_set(enum tegra_suspend_mode mode)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
switch (tegra_get_chip_id()) {
|
||||
case TEGRA20:
|
||||
case TEGRA30:
|
||||
break;
|
||||
default:
|
||||
/* Turn off CRAIL */
|
||||
value = flowctrl_read_cpu_csr(0);
|
||||
value &= ~FLOW_CTRL_CSR_ENABLE_EXT_MASK;
|
||||
value |= FLOW_CTRL_CSR_ENABLE_EXT_CRAIL;
|
||||
flowctrl_write_cpu_csr(0, value);
|
||||
break;
|
||||
}
|
||||
|
||||
tegra_pmc_enter_suspend_mode(mode);
|
||||
}
|
||||
|
||||
void tegra_idle_lp2_last(void)
|
||||
{
|
||||
tegra_pm_set(TEGRA_SUSPEND_LP2);
|
||||
|
||||
cpu_cluster_pm_enter();
|
||||
suspend_cpu_complex();
|
||||
|
||||
cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu);
|
||||
|
||||
restore_cpu_complex();
|
||||
cpu_cluster_pm_exit();
|
||||
}
|
||||
|
||||
enum tegra_suspend_mode tegra_pm_validate_suspend_mode(
|
||||
enum tegra_suspend_mode mode)
|
||||
{
|
||||
/*
|
||||
* The Tegra devices support suspending to LP1 or lower currently.
|
||||
*/
|
||||
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_get_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_get_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)
|
||||
{
|
||||
/* 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)
|
||||
{
|
||||
/* 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",
|
||||
[TEGRA_SUSPEND_LP1] = "LP1",
|
||||
[TEGRA_SUSPEND_LP0] = "LP0",
|
||||
};
|
||||
|
||||
static int 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_pm_set(mode);
|
||||
|
||||
local_fiq_disable();
|
||||
|
||||
suspend_cpu_complex();
|
||||
switch (mode) {
|
||||
case TEGRA_SUSPEND_LP1:
|
||||
tegra_suspend_enter_lp1();
|
||||
break;
|
||||
case TEGRA_SUSPEND_LP2:
|
||||
tegra_set_cpu_in_lp2();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode();
|
||||
|
||||
if (mode == TEGRA_SUSPEND_NONE)
|
||||
return;
|
||||
|
||||
tegra_tear_down_cpu_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
|
49
arch/arm/mach-tegra/pm.h
Normal file
49
arch/arm/mach-tegra/pm.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Copyright (c) 2010-2012 NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* Author:
|
||||
* Colin Cross <ccross@google.com>
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef _MACH_TEGRA_PM_H_
|
||||
#define _MACH_TEGRA_PM_H_
|
||||
|
||||
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 tegra_clear_cpu_in_lp2(void);
|
||||
bool tegra_set_cpu_in_lp2(void);
|
||||
|
||||
void tegra_idle_lp2_last(void);
|
||||
extern void (*tegra_tear_down_cpu)(void);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
void tegra_init_suspend(void);
|
||||
#else
|
||||
static inline void tegra_init_suspend(void) {}
|
||||
#endif
|
||||
|
||||
#endif /* _MACH_TEGRA_PM_H_ */
|
286
arch/arm/mach-tegra/reset-handler.S
Normal file
286
arch/arm/mach-tegra/reset-handler.S
Normal file
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/cache.h>
|
||||
|
||||
#include "flowctrl.h"
|
||||
#include "iomap.h"
|
||||
#include "reset.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#define PMC_SCRATCH41 0x140
|
||||
|
||||
#define RESET_DATA(x) ((TEGRA_RESET_##x)*4)
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* tegra_resume
|
||||
*
|
||||
* CPU boot vector when restarting the a CPU following
|
||||
* an LP2 transition. Also branched to by LP0 and LP1 resume after
|
||||
* re-enabling sdram.
|
||||
*
|
||||
* r6: SoC ID
|
||||
* r8: CPU part number
|
||||
*/
|
||||
ENTRY(tegra_resume)
|
||||
check_cpu_part_num 0xc09, r8, r9
|
||||
bleq v7_invalidate_l1
|
||||
|
||||
cpu_id r0
|
||||
cmp r0, #0 @ CPU0?
|
||||
THUMB( it ne )
|
||||
bne cpu_resume @ no
|
||||
|
||||
tegra_get_soc_id TEGRA_APB_MISC_BASE, r6
|
||||
/* Are we on Tegra20? */
|
||||
cmp r6, #TEGRA20
|
||||
beq 1f @ Yes
|
||||
/* Clear the flow controller flags for this CPU. */
|
||||
cpu_to_csr_reg r1, r0
|
||||
mov32 r2, TEGRA_FLOW_CTRL_BASE
|
||||
ldr r1, [r2, r1]
|
||||
/* Clear event & intr flag */
|
||||
orr r1, r1, \
|
||||
#FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
|
||||
movw r0, #0x3FFD @ enable, cluster_switch, immed, bitmaps
|
||||
@ & ext flags for CPU power mgnt
|
||||
bic r1, r1, r0
|
||||
str r1, [r2]
|
||||
1:
|
||||
|
||||
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
|
||||
ldr r1, [r0]
|
||||
orr r1, r1, #1
|
||||
str r1, [r0]
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* L2 cache resume & re-enable */
|
||||
bl l2c310_early_resume
|
||||
#endif
|
||||
end_ca9_scu_l2_resume:
|
||||
mov32 r9, 0xc0f
|
||||
cmp r8, r9
|
||||
bleq tegra_init_l2_for_a15
|
||||
|
||||
b cpu_resume
|
||||
ENDPROC(tegra_resume)
|
||||
#endif
|
||||
|
||||
.align L1_CACHE_SHIFT
|
||||
ENTRY(__tegra_cpu_reset_handler_start)
|
||||
|
||||
/*
|
||||
* __tegra_cpu_reset_handler:
|
||||
*
|
||||
* Common handler for all CPU reset events.
|
||||
*
|
||||
* Register usage within the reset handler:
|
||||
*
|
||||
* Others: scratch
|
||||
* R6 = SoC ID
|
||||
* R7 = CPU present (to the OS) mask
|
||||
* R8 = CPU in LP1 state mask
|
||||
* R9 = CPU in LP2 state mask
|
||||
* R10 = CPU number
|
||||
* R11 = CPU mask
|
||||
* R12 = pointer to reset handler data
|
||||
*
|
||||
* NOTE: This code is copied to IRAM. All code and data accesses
|
||||
* must be position-independent.
|
||||
*/
|
||||
|
||||
.align L1_CACHE_SHIFT
|
||||
ENTRY(__tegra_cpu_reset_handler)
|
||||
|
||||
cpsid aif, 0x13 @ SVC mode, interrupts disabled
|
||||
|
||||
tegra_get_soc_id TEGRA_APB_MISC_BASE, r6
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
t20_check:
|
||||
cmp r6, #TEGRA20
|
||||
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, #TEGRA30
|
||||
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
|
||||
mov r11, r11, lsl r10 @ R11 = CPU mask
|
||||
adr r12, __tegra_cpu_reset_handler_data
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/* Does the OS know about this CPU? */
|
||||
ldr r7, [r12, #RESET_DATA(MASK_PRESENT)]
|
||||
tst r7, r11 @ if !present
|
||||
bleq __die @ CPU not present (to OS)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
/* Are we on Tegra20? */
|
||||
cmp r6, #TEGRA20
|
||||
bne 1f
|
||||
/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
|
||||
mov32 r5, TEGRA_PMC_BASE
|
||||
mov r0, #0
|
||||
cmp r10, #0
|
||||
strne r0, [r5, #PMC_SCRATCH41]
|
||||
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
|
||||
beq __is_not_lp2
|
||||
ldr lr, [r12, #RESET_DATA(STARTUP_LP2)]
|
||||
cmp lr, #0
|
||||
bleq __die @ no LP2 startup handler
|
||||
bx lr
|
||||
|
||||
__is_not_lp2:
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Can only be secondary boot (initial or hotplug)
|
||||
* CPU0 can't be here for Tegra20/30
|
||||
*/
|
||||
cmp r6, #TEGRA114
|
||||
beq __no_cpu0_chk
|
||||
cmp r10, #0
|
||||
bleq __die @ CPU0 cannot be here
|
||||
__no_cpu0_chk:
|
||||
ldr lr, [r12, #RESET_DATA(STARTUP_SECONDARY)]
|
||||
cmp lr, #0
|
||||
bleq __die @ no secondary startup handler
|
||||
bx lr
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We don't know why the CPU reset. Just kill it.
|
||||
* The LR register will contain the address we died at + 4.
|
||||
*/
|
||||
|
||||
__die:
|
||||
sub lr, lr, #4
|
||||
mov32 r7, TEGRA_PMC_BASE
|
||||
str lr, [r7, #PMC_SCRATCH41]
|
||||
|
||||
mov32 r7, TEGRA_CLK_RESET_BASE
|
||||
|
||||
/* Are we on Tegra20? */
|
||||
cmp r6, #TEGRA20
|
||||
bne 1f
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
mov32 r0, 0x1111
|
||||
mov r1, r0, lsl r10
|
||||
str r1, [r7, #0x340] @ CLK_RST_CPU_CMPLX_SET
|
||||
#endif
|
||||
1:
|
||||
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
||||
mov32 r6, TEGRA_FLOW_CTRL_BASE
|
||||
|
||||
cmp r10, #0
|
||||
moveq r1, #FLOW_CTRL_HALT_CPU0_EVENTS
|
||||
moveq r2, #FLOW_CTRL_CPU0_CSR
|
||||
movne r1, r10, lsl #3
|
||||
addne r2, r1, #(FLOW_CTRL_CPU1_CSR-8)
|
||||
addne r1, r1, #(FLOW_CTRL_HALT_CPU1_EVENTS-8)
|
||||
|
||||
/* Clear CPU "event" and "interrupt" flags and power gate
|
||||
it when halting but not before it is in the "WFI" state. */
|
||||
ldr r0, [r6, +r2]
|
||||
orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
|
||||
orr r0, r0, #FLOW_CTRL_CSR_ENABLE
|
||||
str r0, [r6, +r2]
|
||||
|
||||
/* Unconditionally halt this CPU */
|
||||
mov r0, #FLOW_CTRL_WAITEVENT
|
||||
str r0, [r6, +r1]
|
||||
ldr r0, [r6, +r1] @ memory barrier
|
||||
|
||||
dsb
|
||||
isb
|
||||
wfi @ CPU should be power gated here
|
||||
|
||||
/* If the CPU didn't power gate above just kill it's clock. */
|
||||
|
||||
mov r0, r11, lsl #8
|
||||
str r0, [r7, #348] @ CLK_CPU_CMPLX_SET
|
||||
#endif
|
||||
|
||||
/* If the CPU still isn't dead, just spin here. */
|
||||
b .
|
||||
ENDPROC(__tegra_cpu_reset_handler)
|
||||
|
||||
.align L1_CACHE_SHIFT
|
||||
.type __tegra_cpu_reset_handler_data, %object
|
||||
.globl __tegra_cpu_reset_handler_data
|
||||
__tegra_cpu_reset_handler_data:
|
||||
.rept TEGRA_RESET_DATA_SIZE
|
||||
.long 0
|
||||
.endr
|
||||
.align L1_CACHE_SHIFT
|
||||
|
||||
ENTRY(__tegra_cpu_reset_handler_end)
|
108
arch/arm/mach-tegra/reset.c
Normal file
108
arch/arm/mach-tegra/reset.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/reset.c
|
||||
*
|
||||
* Copyright (C) 2011,2012 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/bitops.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/firmware.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
|
||||
#include "iomap.h"
|
||||
#include "irammap.h"
|
||||
#include "reset.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \
|
||||
TEGRA_IRAM_RESET_HANDLER_OFFSET)
|
||||
|
||||
static bool is_enabled;
|
||||
|
||||
static void __init tegra_cpu_reset_handler_set(const u32 reset_address)
|
||||
{
|
||||
void __iomem *evp_cpu_reset =
|
||||
IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE + 0x100);
|
||||
void __iomem *sb_ctrl = IO_ADDRESS(TEGRA_SB_BASE);
|
||||
u32 reg;
|
||||
|
||||
/*
|
||||
* NOTE: This must be the one and only write to the EVP CPU reset
|
||||
* vector in the entire system.
|
||||
*/
|
||||
writel(reset_address, evp_cpu_reset);
|
||||
wmb();
|
||||
reg = readl(evp_cpu_reset);
|
||||
|
||||
/*
|
||||
* Prevent further modifications to the physical reset vector.
|
||||
* NOTE: Has no effect on chips prior to Tegra30.
|
||||
*/
|
||||
reg = readl(sb_ctrl);
|
||||
reg |= 2;
|
||||
writel(reg, sb_ctrl);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void __init tegra_cpu_reset_handler_enable(void)
|
||||
{
|
||||
void __iomem *iram_base = IO_ADDRESS(TEGRA_IRAM_RESET_BASE);
|
||||
const u32 reset_address = TEGRA_IRAM_RESET_BASE +
|
||||
tegra_cpu_reset_handler_offset;
|
||||
int err;
|
||||
|
||||
BUG_ON(is_enabled);
|
||||
BUG_ON(tegra_cpu_reset_handler_size > TEGRA_IRAM_RESET_HANDLER_SIZE);
|
||||
|
||||
memcpy(iram_base, (void *)__tegra_cpu_reset_handler_start,
|
||||
tegra_cpu_reset_handler_size);
|
||||
|
||||
err = call_firmware_op(set_cpu_boot_addr, 0, reset_address);
|
||||
switch (err) {
|
||||
case -ENOSYS:
|
||||
tegra_cpu_reset_handler_set(reset_address);
|
||||
/* pass-through */
|
||||
case 0:
|
||||
is_enabled = true;
|
||||
break;
|
||||
default:
|
||||
pr_crit("Cannot set CPU reset handler: %d\n", err);
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
void __init tegra_cpu_reset_handler_init(void)
|
||||
{
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] =
|
||||
*((u32 *)cpu_possible_mask);
|
||||
__tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] =
|
||||
virt_to_phys((void *)tegra_secondary_startup);
|
||||
#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
|
||||
|
||||
tegra_cpu_reset_handler_enable();
|
||||
}
|
63
arch/arm/mach-tegra/reset.h
Normal file
63
arch/arm/mach-tegra/reset.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/reset.h
|
||||
*
|
||||
* CPU reset dispatcher.
|
||||
*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_RESET_H
|
||||
#define __MACH_TEGRA_RESET_H
|
||||
|
||||
#define TEGRA_RESET_MASK_PRESENT 0
|
||||
#define TEGRA_RESET_MASK_LP1 1
|
||||
#define TEGRA_RESET_MASK_LP2 2
|
||||
#define TEGRA_RESET_STARTUP_SECONDARY 3
|
||||
#define TEGRA_RESET_STARTUP_LP2 4
|
||||
#define TEGRA_RESET_STARTUP_LP1 5
|
||||
#define TEGRA_RESET_DATA_SIZE 6
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include "irammap.h"
|
||||
|
||||
extern unsigned long __tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE];
|
||||
|
||||
void __tegra_cpu_reset_handler_start(void);
|
||||
void __tegra_cpu_reset_handler(void);
|
||||
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] - \
|
||||
(u32)__tegra_cpu_reset_handler_start)))
|
||||
#endif
|
||||
|
||||
#define tegra_cpu_reset_handler_offset \
|
||||
((u32)__tegra_cpu_reset_handler - \
|
||||
(u32)__tegra_cpu_reset_handler_start)
|
||||
|
||||
#define tegra_cpu_reset_handler_size \
|
||||
(__tegra_cpu_reset_handler_end - \
|
||||
__tegra_cpu_reset_handler_start)
|
||||
|
||||
void __init tegra_cpu_reset_handler_init(void);
|
||||
|
||||
#endif
|
||||
#endif
|
575
arch/arm/mach-tegra/sleep-tegra20.S
Normal file
575
arch/arm/mach-tegra/sleep-tegra20.S
Normal file
|
@ -0,0 +1,575 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved.
|
||||
* Copyright (c) 2011, Google, Inc.
|
||||
*
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
* Gary King <gking@nvidia.com>
|
||||
*
|
||||
* 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/linkage.h>
|
||||
|
||||
#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)
|
||||
*
|
||||
* puts the current cpu in reset
|
||||
* should never return
|
||||
*/
|
||||
ENTRY(tegra20_hotplug_shutdown)
|
||||
/* Put this CPU down */
|
||||
cpu_id r0
|
||||
bl tegra20_cpu_shutdown
|
||||
ret lr @ should never get here
|
||||
ENDPROC(tegra20_hotplug_shutdown)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_shutdown(int cpu)
|
||||
*
|
||||
* r0 is cpu to reset
|
||||
*
|
||||
* puts the specified CPU in wait-for-event mode on the flow controller
|
||||
* and puts the CPU in reset
|
||||
* can be called on the current cpu or another cpu
|
||||
* if called on the current cpu, does not return
|
||||
* MUST NOT BE CALLED FOR CPU 0.
|
||||
*
|
||||
* corrupts r0-r3, r12
|
||||
*/
|
||||
ENTRY(tegra20_cpu_shutdown)
|
||||
cmp r0, #0
|
||||
reteq lr @ must not be called for CPU 0
|
||||
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
|
||||
mov r12, #CPU_RESETTABLE
|
||||
str r12, [r1]
|
||||
|
||||
cpu_to_halt_reg r1, r0
|
||||
ldr r3, =TEGRA_FLOW_CTRL_VIRT
|
||||
mov r2, #FLOW_CTRL_WAITEVENT | FLOW_CTRL_JTAG_RESUME
|
||||
str r2, [r3, r1] @ put flow controller in wait event mode
|
||||
ldr r2, [r3, r1]
|
||||
isb
|
||||
dsb
|
||||
movw r1, 0x1011
|
||||
mov r1, r1, lsl r0
|
||||
ldr r3, =TEGRA_CLK_RESET_VIRT
|
||||
str r1, [r3, #0x340] @ put slave CPU in reset
|
||||
isb
|
||||
dsb
|
||||
cpu_id r3
|
||||
cmp r3, r0
|
||||
beq .
|
||||
ret lr
|
||||
ENDPROC(tegra20_cpu_shutdown)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* tegra_pen_lock
|
||||
*
|
||||
* spinlock implementation with no atomic test-and-set and no coherence
|
||||
* using Peterson's algorithm on strongly-ordered registers
|
||||
* used to synchronize a cpu waking up from wfi with entering lp2 on idle
|
||||
*
|
||||
* The reference link of Peterson's algorithm:
|
||||
* http://en.wikipedia.org/wiki/Peterson's_algorithm
|
||||
*
|
||||
* SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
|
||||
* on cpu 0:
|
||||
* r2 = flag[0] (in SCRATCH38)
|
||||
* r3 = flag[1] (in SCRATCH39)
|
||||
* on cpu1:
|
||||
* r2 = flag[1] (in SCRATCH39)
|
||||
* r3 = flag[0] (in SCRATCH38)
|
||||
*
|
||||
* must be called with MMU on
|
||||
* corrupts r0-r3, r12
|
||||
*/
|
||||
ENTRY(tegra_pen_lock)
|
||||
mov32 r3, TEGRA_PMC_VIRT
|
||||
cpu_id r0
|
||||
add r1, r3, #PMC_SCRATCH37
|
||||
cmp r0, #0
|
||||
addeq r2, r3, #PMC_SCRATCH38
|
||||
addeq r3, r3, #PMC_SCRATCH39
|
||||
addne r2, r3, #PMC_SCRATCH39
|
||||
addne r3, r3, #PMC_SCRATCH38
|
||||
|
||||
mov r12, #1
|
||||
str r12, [r2] @ flag[cpu] = 1
|
||||
dsb
|
||||
str r12, [r1] @ !turn = cpu
|
||||
1: dsb
|
||||
ldr r12, [r3]
|
||||
cmp r12, #1 @ flag[!cpu] == 1?
|
||||
ldreq r12, [r1]
|
||||
cmpeq r12, r0 @ !turn == cpu?
|
||||
beq 1b @ while !turn == cpu && flag[!cpu] == 1
|
||||
|
||||
ret lr @ locked
|
||||
ENDPROC(tegra_pen_lock)
|
||||
|
||||
ENTRY(tegra_pen_unlock)
|
||||
dsb
|
||||
mov32 r3, TEGRA_PMC_VIRT
|
||||
cpu_id r0
|
||||
cmp r0, #0
|
||||
addeq r2, r3, #PMC_SCRATCH38
|
||||
addne r2, r3, #PMC_SCRATCH39
|
||||
mov r12, #0
|
||||
str r12, [r2]
|
||||
ret lr
|
||||
ENDPROC(tegra_pen_unlock)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_clear_resettable(void)
|
||||
*
|
||||
* Called to clear the "resettable soon" flag in PMC_SCRATCH41 when
|
||||
* it is expected that the secondary CPU will be idle soon.
|
||||
*/
|
||||
ENTRY(tegra20_cpu_clear_resettable)
|
||||
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
|
||||
mov r12, #CPU_NOT_RESETTABLE
|
||||
str r12, [r1]
|
||||
ret lr
|
||||
ENDPROC(tegra20_cpu_clear_resettable)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_set_resettable_soon(void)
|
||||
*
|
||||
* Called to set the "resettable soon" flag in PMC_SCRATCH41 when
|
||||
* it is expected that the secondary CPU will be idle soon.
|
||||
*/
|
||||
ENTRY(tegra20_cpu_set_resettable_soon)
|
||||
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
|
||||
mov r12, #CPU_RESETTABLE_SOON
|
||||
str r12, [r1]
|
||||
ret lr
|
||||
ENDPROC(tegra20_cpu_set_resettable_soon)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_is_resettable_soon(void)
|
||||
*
|
||||
* Returns true if the "resettable soon" flag in PMC_SCRATCH41 has been
|
||||
* set because it is expected that the secondary CPU will be idle soon.
|
||||
*/
|
||||
ENTRY(tegra20_cpu_is_resettable_soon)
|
||||
mov32 r1, TEGRA_PMC_VIRT + PMC_SCRATCH41
|
||||
ldr r12, [r1]
|
||||
cmp r12, #CPU_RESETTABLE_SOON
|
||||
moveq r0, #1
|
||||
movne r0, #0
|
||||
ret lr
|
||||
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
|
||||
|
||||
ret r3
|
||||
ENDPROC(tegra20_sleep_core_finish)
|
||||
|
||||
/*
|
||||
* tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
|
||||
*
|
||||
* Enters WFI on secondary CPU by exiting coherency.
|
||||
*/
|
||||
ENTRY(tegra20_sleep_cpu_secondary_finish)
|
||||
stmfd sp!, {r4-r11, lr}
|
||||
|
||||
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
|
||||
mov r3, #CPU_RESETTABLE
|
||||
str r3, [r0]
|
||||
|
||||
bl tegra_cpu_do_idle
|
||||
|
||||
/*
|
||||
* cpu may be reset while in wfi, which will return through
|
||||
* tegra_resume to cpu_resume
|
||||
* or interrupt may wake wfi, which will return here
|
||||
* cpu state is unchanged - MMU is on, cache is on, coherency
|
||||
* is off, and the data cache is off
|
||||
*
|
||||
* r11 contains the original actlr
|
||||
*/
|
||||
|
||||
bl tegra_pen_lock
|
||||
|
||||
mov32 r3, TEGRA_PMC_VIRT
|
||||
add r0, r3, #PMC_SCRATCH41
|
||||
mov r3, #CPU_NOT_RESETTABLE
|
||||
str r3, [r0]
|
||||
|
||||
bl tegra_pen_unlock
|
||||
|
||||
/* Re-enable the data cache */
|
||||
mrc p15, 0, r10, c1, c0, 0
|
||||
orr r10, r10, #CR_C
|
||||
mcr p15, 0, r10, c1, c0, 0
|
||||
isb
|
||||
|
||||
mcr p15, 0, r11, c1, c0, 1 @ reenable coherency
|
||||
|
||||
/* Invalidate the TLBs & BTAC */
|
||||
mov r1, #0
|
||||
mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs
|
||||
mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* the cpu was running with coherency disabled,
|
||||
* caches may be out of date */
|
||||
bl v7_flush_kern_cache_louis
|
||||
|
||||
ldmfd sp!, {r4 - r11, pc}
|
||||
ENDPROC(tegra20_sleep_cpu_secondary_finish)
|
||||
|
||||
/*
|
||||
* tegra20_tear_down_cpu
|
||||
*
|
||||
* Switches the CPU cluster to PLL-P and enters sleep.
|
||||
*/
|
||||
ENTRY(tegra20_tear_down_cpu)
|
||||
bl tegra_switch_cpu_to_pllp
|
||||
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]
|
||||
ret 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]
|
||||
|
||||
ret lr
|
||||
|
||||
/*
|
||||
* tegra20_enter_sleep
|
||||
*
|
||||
* uses flow controller to enter sleep state
|
||||
* executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
|
||||
* executes from SDRAM with target state is LP2
|
||||
*/
|
||||
tegra20_enter_sleep:
|
||||
mov32 r6, TEGRA_FLOW_CTRL_BASE
|
||||
|
||||
mov r0, #FLOW_CTRL_WAIT_FOR_INTERRUPT
|
||||
orr r0, r0, #FLOW_CTRL_HALT_CPU_IRQ | FLOW_CTRL_HALT_CPU_FIQ
|
||||
cpu_id r1
|
||||
cpu_to_halt_reg r1, r1
|
||||
str r0, [r6, r1]
|
||||
dsb
|
||||
ldr r0, [r6, r1] /* memory barrier */
|
||||
|
||||
halted:
|
||||
dsb
|
||||
wfe /* CPU should be power gated here */
|
||||
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
|
||||
ret 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
|
831
arch/arm/mach-tegra/sleep-tegra30.S
Normal file
831
arch/arm/mach-tegra/sleep-tegra30.S
Normal file
|
@ -0,0 +1,831 @@
|
|||
/*
|
||||
* Copyright (c) 2012, NVIDIA Corporation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/cache.h>
|
||||
|
||||
#include "flowctrl.h"
|
||||
#include "irammap.h"
|
||||
#include "sleep.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)
|
||||
*
|
||||
* Powergates the current CPU.
|
||||
* Should never return.
|
||||
*/
|
||||
ENTRY(tegra30_hotplug_shutdown)
|
||||
/* Powergate this CPU */
|
||||
mov r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
|
||||
bl tegra30_cpu_shutdown
|
||||
ret lr @ should never get here
|
||||
ENDPROC(tegra30_hotplug_shutdown)
|
||||
|
||||
/*
|
||||
* tegra30_cpu_shutdown(unsigned long flags)
|
||||
*
|
||||
* Puts the current CPU in wait-for-event mode on the flow controller
|
||||
* and powergates it -- flags (in R0) indicate the request type.
|
||||
*
|
||||
* r10 = SoC ID
|
||||
* corrupts r0-r4, r10-r12
|
||||
*/
|
||||
ENTRY(tegra30_cpu_shutdown)
|
||||
cpu_id r3
|
||||
tegra_get_soc_id TEGRA_APB_MISC_VIRT, r10
|
||||
cmp r10, #TEGRA30
|
||||
bne _no_cpu0_chk @ It's not Tegra30
|
||||
|
||||
cmp r3, #0
|
||||
reteq lr @ Must never be called for CPU 0
|
||||
_no_cpu0_chk:
|
||||
|
||||
ldr r12, =TEGRA_FLOW_CTRL_VIRT
|
||||
cpu_to_csr_reg r1, r3
|
||||
add r1, r1, r12 @ virtual CSR address for this CPU
|
||||
cpu_to_halt_reg r2, r3
|
||||
add r2, r2, r12 @ virtual HALT_EVENTS address for this CPU
|
||||
|
||||
/*
|
||||
* Clear this CPU's "event" and "interrupt" flags and power gate
|
||||
* it when halting but not before it is in the "WFE" state.
|
||||
*/
|
||||
movw r12, \
|
||||
FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | \
|
||||
FLOW_CTRL_CSR_ENABLE
|
||||
cmp r10, #TEGRA30
|
||||
moveq r4, #(1 << 4) @ wfe bitmap
|
||||
movne r4, #(1 << 8) @ wfi bitmap
|
||||
ARM( orr r12, r12, r4, lsl r3 )
|
||||
THUMB( lsl r4, r4, r3 )
|
||||
THUMB( orr r12, r12, r4 )
|
||||
str r12, [r1]
|
||||
|
||||
/* Halt this CPU. */
|
||||
mov r3, #0x400
|
||||
delay_1:
|
||||
subs r3, r3, #1 @ delay as a part of wfe war.
|
||||
bge delay_1;
|
||||
cpsid a @ disable imprecise aborts.
|
||||
ldr r3, [r1] @ read CSR
|
||||
str r3, [r1] @ clear CSR
|
||||
|
||||
tst r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN
|
||||
beq flow_ctrl_setting_for_lp2
|
||||
|
||||
/* flow controller set up for hotplug */
|
||||
mov r3, #FLOW_CTRL_WAITEVENT @ For hotplug
|
||||
b flow_ctrl_done
|
||||
flow_ctrl_setting_for_lp2:
|
||||
/* flow controller set up 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]
|
||||
ldr r0, [r2]
|
||||
b wfe_war
|
||||
|
||||
__cpu_reset_again:
|
||||
dsb
|
||||
.align 5
|
||||
wfeeq @ CPU should be power gated here
|
||||
wfine
|
||||
wfe_war:
|
||||
b __cpu_reset_again
|
||||
|
||||
/*
|
||||
* 38 nop's, which fills reset of wfe cache line and
|
||||
* 4 more cachelines with nop
|
||||
*/
|
||||
.rept 38
|
||||
nop
|
||||
.endr
|
||||
b . @ should never get here
|
||||
|
||||
ENDPROC(tegra30_cpu_shutdown)
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
ret 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.
|
||||
*/
|
||||
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. */
|
||||
mov r0, #0 @ power mode flags (!hotplug)
|
||||
bl tegra30_cpu_shutdown
|
||||
mov r0, #1 @ never return here
|
||||
ret r7
|
||||
ENDPROC(tegra30_sleep_cpu_secondary_finish)
|
||||
|
||||
/*
|
||||
* tegra30_tear_down_cpu
|
||||
*
|
||||
* Switches the CPU to enter sleep.
|
||||
*/
|
||||
ENTRY(tegra30_tear_down_cpu)
|
||||
mov32 r6, TEGRA_FLOW_CTRL_BASE
|
||||
|
||||
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]
|
||||
ret 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]
|
||||
|
||||
ret lr
|
||||
|
||||
/*
|
||||
* tegra30_enter_sleep
|
||||
*
|
||||
* uses flow controller to enter sleep state
|
||||
* executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
|
||||
* executes from SDRAM with target state is LP2
|
||||
* r6 = TEGRA_FLOW_CTRL_BASE
|
||||
*/
|
||||
tegra30_enter_sleep:
|
||||
cpu_id r1
|
||||
|
||||
cpu_to_csr_reg r2, r1
|
||||
ldr r0, [r6, r2]
|
||||
orr r0, r0, #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG
|
||||
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
|
||||
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
|
||||
ldr r0, [r6, r2] /* memory barrier */
|
||||
|
||||
halted:
|
||||
isb
|
||||
dsb
|
||||
wfi /* CPU should be power gated here */
|
||||
|
||||
/* !!!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
|
||||
|
||||
ret lr
|
||||
|
||||
.ltorg
|
||||
/* dummy symbol for end of IRAM */
|
||||
.align L1_CACHE_SHIFT
|
||||
.global tegra30_iram_end
|
||||
tegra30_iram_end:
|
||||
b .
|
||||
#endif
|
161
arch/arm/mach-tegra/sleep.S
Normal file
161
arch/arm/mach-tegra/sleep.S
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* arch/arm/mach-tegra/sleep.S
|
||||
*
|
||||
* Copyright (c) 2010-2011, NVIDIA Corporation.
|
||||
* Copyright (c) 2011, Google, Inc.
|
||||
*
|
||||
* Author: Colin Cross <ccross@android.com>
|
||||
* Gary King <gking@nvidia.com>
|
||||
*
|
||||
* 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/linkage.h>
|
||||
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/cp15.h>
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
|
||||
#include "iomap.h"
|
||||
|
||||
#include "flowctrl.h"
|
||||
#include "sleep.h"
|
||||
|
||||
#define CLK_RESET_CCLK_BURST 0x20
|
||||
#define CLK_RESET_CCLK_DIVIDER 0x24
|
||||
|
||||
#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
|
||||
/*
|
||||
* tegra_disable_clean_inv_dcache
|
||||
*
|
||||
* disable, clean & invalidate the D-cache
|
||||
*
|
||||
* Corrupted registers: r1-r3, r6, r8, r9-r11
|
||||
*/
|
||||
ENTRY(tegra_disable_clean_inv_dcache)
|
||||
stmfd sp!, {r0, r4-r5, r7, r9-r11, lr}
|
||||
dmb @ ensure ordering
|
||||
|
||||
/* Disable the D-cache */
|
||||
mrc p15, 0, r2, c1, c0, 0
|
||||
bic r2, r2, #CR_C
|
||||
mcr p15, 0, r2, c1, c0, 0
|
||||
isb
|
||||
|
||||
/* Flush the D-cache */
|
||||
cmp r0, #TEGRA_FLUSH_CACHE_ALL
|
||||
blne v7_flush_dcache_louis
|
||||
bleq v7_flush_dcache_all
|
||||
|
||||
/* Trun off coherency */
|
||||
exit_smp r4, r5
|
||||
|
||||
ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc}
|
||||
ENDPROC(tegra_disable_clean_inv_dcache)
|
||||
#endif
|
||||
|
||||
#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:
|
||||
|
||||
ret 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
|
||||
|
||||
mov32 r3, tegra_shut_off_mmu
|
||||
add r3, r3, r0
|
||||
mov r0, r1
|
||||
|
||||
ret r3
|
||||
ENDPROC(tegra_sleep_cpu_finish)
|
||||
|
||||
/*
|
||||
* tegra_shut_off_mmu
|
||||
*
|
||||
* r0 = physical address to jump to with mmu off
|
||||
*
|
||||
* called with VA=PA mapping
|
||||
* turns off MMU, icache, dcache and branch prediction
|
||||
*/
|
||||
.align L1_CACHE_SHIFT
|
||||
.pushsection .idmap.text, "ax"
|
||||
ENTRY(tegra_shut_off_mmu)
|
||||
mrc p15, 0, r3, c1, c0, 0
|
||||
movw r2, #CR_I | CR_Z | CR_C | CR_M
|
||||
bic r3, r3, r2
|
||||
dsb
|
||||
mcr p15, 0, r3, c1, c0, 0
|
||||
isb
|
||||
#ifdef CONFIG_CACHE_L2X0
|
||||
/* Disable L2 cache */
|
||||
check_cpu_part_num 0xc09, r9, r10
|
||||
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
|
||||
ret r0
|
||||
ENDPROC(tegra_shut_off_mmu)
|
||||
.popsection
|
||||
|
||||
/*
|
||||
* tegra_switch_cpu_to_pllp
|
||||
*
|
||||
* In LP2 the normal cpu clock pllx will be turned off. Switch the CPU to pllp
|
||||
*/
|
||||
ENTRY(tegra_switch_cpu_to_pllp)
|
||||
/* in LP2 idle (SDRAM active), set the CPU burst policy to PLLP */
|
||||
mov32 r5, TEGRA_CLK_RESET_BASE
|
||||
mov r0, #(2 << 28) @ burst policy = run mode
|
||||
orr r0, r0, #(4 << 4) @ use PLLP in run mode burst
|
||||
str r0, [r5, #CLK_RESET_CCLK_BURST]
|
||||
mov r0, #0
|
||||
str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
|
||||
ret lr
|
||||
ENDPROC(tegra_switch_cpu_to_pllp)
|
||||
#endif
|
150
arch/arm/mach-tegra/sleep.h
Normal file
150
arch/arm/mach-tegra/sleep.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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,
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_TEGRA_SLEEP_H
|
||||
#define __MACH_TEGRA_SLEEP_H
|
||||
|
||||
#include "iomap.h"
|
||||
|
||||
#define TEGRA_ARM_PERIF_VIRT (TEGRA_ARM_PERIF_BASE - IO_CPU_PHYS \
|
||||
+ IO_CPU_VIRT)
|
||||
#define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS \
|
||||
+ IO_PPSB_VIRT)
|
||||
#define TEGRA_CLK_RESET_VIRT (TEGRA_CLK_RESET_BASE - IO_PPSB_PHYS \
|
||||
+ IO_PPSB_VIRT)
|
||||
#define TEGRA_APB_MISC_VIRT (TEGRA_APB_MISC_BASE - IO_APB_PHYS \
|
||||
+ IO_APB_VIRT)
|
||||
#define TEGRA_PMC_VIRT (TEGRA_PMC_BASE - IO_APB_PHYS + IO_APB_VIRT)
|
||||
|
||||
/* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock and idle */
|
||||
#define PMC_SCRATCH37 0x130
|
||||
#define PMC_SCRATCH38 0x134
|
||||
#define PMC_SCRATCH39 0x138
|
||||
#define PMC_SCRATCH41 0x140
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
#define CPU_RESETTABLE 2
|
||||
#define CPU_RESETTABLE_SOON 1
|
||||
#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
|
||||
subne \rd, \rcpu, #1
|
||||
movne \rd, \rd, lsl #3
|
||||
addne \rd, \rd, #0x14
|
||||
moveq \rd, #0
|
||||
.endm
|
||||
|
||||
/* returns the offset of the flow controller csr register for a cpu */
|
||||
.macro cpu_to_csr_reg rd, rcpu
|
||||
cmp \rcpu, #0
|
||||
subne \rd, \rcpu, #1
|
||||
movne \rd, \rd, lsl #3
|
||||
addne \rd, \rd, #0x18
|
||||
moveq \rd, #8
|
||||
.endm
|
||||
|
||||
/* returns the ID of the current processor */
|
||||
.macro cpu_id, rd
|
||||
mrc p15, 0, \rd, c0, c0, 5
|
||||
and \rd, \rd, #0xF
|
||||
.endm
|
||||
|
||||
/* loads a 32-bit value into a register without a data access */
|
||||
.macro mov32, reg, val
|
||||
movw \reg, #:lower16:\val
|
||||
movt \reg, #:upper16:\val
|
||||
.endm
|
||||
|
||||
/* Marco to check CPU part num */
|
||||
.macro check_cpu_part_num part_num, tmp1, tmp2
|
||||
mrc p15, 0, \tmp1, c0, c0, 0
|
||||
ubfx \tmp1, \tmp1, #4, #12
|
||||
mov32 \tmp2, \part_num
|
||||
cmp \tmp1, \tmp2
|
||||
.endm
|
||||
|
||||
/* Macro to exit SMP coherency. */
|
||||
.macro exit_smp, tmp1, tmp2
|
||||
mrc p15, 0, \tmp1, c1, c0, 1 @ ACTLR
|
||||
bic \tmp1, \tmp1, #(1<<6) | (1<<0) @ clear ACTLR.SMP | ACTLR.FW
|
||||
mcr p15, 0, \tmp1, c1, c0, 1 @ ACTLR
|
||||
isb
|
||||
#ifdef CONFIG_HAVE_ARM_SCU
|
||||
check_cpu_part_num 0xc09, \tmp1, \tmp2
|
||||
mrceq p15, 0, \tmp1, c0, c0, 5
|
||||
andeq \tmp1, \tmp1, #0xF
|
||||
moveq \tmp1, \tmp1, lsl #2
|
||||
moveq \tmp2, #0xf
|
||||
moveq \tmp2, \tmp2, lsl \tmp1
|
||||
ldreq \tmp1, =(TEGRA_ARM_PERIF_VIRT + 0xC)
|
||||
streq \tmp2, [\tmp1] @ invalidate SCU tags for CPU
|
||||
dsb
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/* Macro to check Tegra revision */
|
||||
#define APB_MISC_GP_HIDREV 0x804
|
||||
.macro tegra_get_soc_id base, tmp1
|
||||
mov32 \tmp1, \base
|
||||
ldr \tmp1, [\tmp1, #APB_MISC_GP_HIDREV]
|
||||
and \tmp1, \tmp1, #0xff00
|
||||
mov \tmp1, \tmp1, lsr #8
|
||||
.endm
|
||||
|
||||
#else
|
||||
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(u32 flag);
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
void tegra20_hotplug_shutdown(void);
|
||||
void tegra30_hotplug_shutdown(void);
|
||||
#endif
|
||||
|
||||
void tegra20_cpu_shutdown(int cpu);
|
||||
int tegra20_cpu_is_resettable_soon(void);
|
||||
void tegra20_cpu_clear_resettable(void);
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
void tegra20_cpu_set_resettable_soon(void);
|
||||
#else
|
||||
static inline void tegra20_cpu_set_resettable_soon(void) {}
|
||||
#endif
|
||||
|
||||
int tegra20_sleep_cpu_secondary_finish(unsigned long);
|
||||
void tegra20_tear_down_cpu(void);
|
||||
int tegra30_sleep_cpu_secondary_finish(unsigned long);
|
||||
void tegra30_tear_down_cpu(void);
|
||||
|
||||
#endif
|
||||
#endif
|
171
arch/arm/mach-tegra/tegra.c
Normal file
171
arch/arm/mach-tegra/tegra.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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/clk.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irqchip.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pda_power.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/serial_8250.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sys_soc.h>
|
||||
#include <linux/usb/tegra_usb_phy.h>
|
||||
|
||||
#include <soc/tegra/fuse.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
#include <asm/hardware/cache-l2x0.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/time.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/trusted_foundations.h>
|
||||
|
||||
#include "board.h"
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
#include "flowctrl.h"
|
||||
#include "iomap.h"
|
||||
#include "irq.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[3] = {
|
||||
/* Debug UART initialization required */
|
||||
1,
|
||||
/* Debug UART physical address */
|
||||
0,
|
||||
/* Debug UART virtual address */
|
||||
0,
|
||||
};
|
||||
|
||||
static void __init tegra_init_early(void)
|
||||
{
|
||||
of_register_trusted_foundations();
|
||||
tegra_cpu_reset_handler_init();
|
||||
tegra_flowctrl_init();
|
||||
}
|
||||
|
||||
static void __init tegra_dt_init_irq(void)
|
||||
{
|
||||
tegra_init_irq();
|
||||
irqchip_init();
|
||||
tegra_legacy_irq_syscore_init();
|
||||
}
|
||||
|
||||
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_sku_info.revision);
|
||||
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%u", tegra_get_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, NULL, parent);
|
||||
}
|
||||
|
||||
static void __init paz00_init(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC))
|
||||
tegra_paz00_wifikill_init();
|
||||
}
|
||||
|
||||
static struct {
|
||||
char *machine;
|
||||
void (*init)(void);
|
||||
} board_init_funcs[] = {
|
||||
{ "compal,paz00", paz00_init },
|
||||
};
|
||||
|
||||
static void __init tegra_dt_init_late(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
tegra_init_suspend();
|
||||
tegra_cpuidle_init();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(board_init_funcs); i++) {
|
||||
if (of_machine_is_compatible(board_init_funcs[i].machine)) {
|
||||
board_init_funcs[i].init();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const tegra_dt_board_compat[] = {
|
||||
"nvidia,tegra124",
|
||||
"nvidia,tegra114",
|
||||
"nvidia,tegra30",
|
||||
"nvidia,tegra20",
|
||||
NULL
|
||||
};
|
||||
|
||||
DT_MACHINE_START(TEGRA_DT, "NVIDIA Tegra SoC (Flattened Device Tree)")
|
||||
.l2c_aux_val = 0x3c400001,
|
||||
.l2c_aux_mask = 0xc20fc3fe,
|
||||
.smp = smp_ops(tegra_smp_ops),
|
||||
.map_io = tegra_map_common_io,
|
||||
.init_early = tegra_init_early,
|
||||
.init_irq = tegra_dt_init_irq,
|
||||
.init_machine = tegra_dt_init,
|
||||
.init_late = tegra_dt_init_late,
|
||||
.restart = tegra_pmc_restart,
|
||||
.dt_compat = tegra_dt_board_compat,
|
||||
MACHINE_END
|
Loading…
Add table
Add a link
Reference in a new issue