diff options
author | Olof Johansson | 2013-02-05 12:10:18 -0800 |
---|---|---|
committer | Olof Johansson | 2013-02-05 12:10:18 -0800 |
commit | f0390669d13b0a1592fc7906445b4f6c8ec5af23 (patch) | |
tree | 4a27ffbd016d24f54ba1e3a38d46a386ab924bc6 /arch/arm/mach-tegra | |
parent | b60decad7791694a36ad218cede77a407c1475ce (diff) | |
parent | 24e30c9417230b359bf6dfeb923e90138df7c112 (diff) |
Merge tag 'tegra-for-3.9-cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/cleanup
From Stephen Warren:
ARM: tegra: cleanup
This pull request contains various cleanup and minor changes to core
Tegra code:
* Tegra clocksource driver is moved to drivers/clocksource.
* Various typos, warning cleanup, statics cleanup, section mismatch
fixes, etc.
* Various small fixes/cleanups for CPU/hotplug/SMP code.
This pull request is based on a merge of v3.8-rc3 and at least part of
arm-soc's timer/cleanup branch.
* tag 'tegra-for-3.9-cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra:
ARM: tegra: fix compile error when disable CPU_IDLE
ARM: tegra30: make the wait time of CPU power up to proportional to HZ
ARM: tegra: make device can run on UP
ARM: tegra: clean up the CPUINIT section
ARM: tegra: moving the clock gating procedure to tegra_cpu_kill
ARM: tegra: update the cache maintenance order for CPU shutdown
ARM: tegra30: fix power up sequence for boot_secondary
ARM: tegra: cpufreq: move clk_get/put out of function tegra_cpu_init/exit
ARM: tegra: fix Kconfig warnings when !SMP
ARM: tegra: Make variables static
clocksource: tegra: cosmetic: Fix error message
ARM: tegra: move timer.c to drivers/clocksource/
Signed-off-by: Olof Johansson <olof@lixom.net>
Trivial conflicts:
arch/arm/mach-tegra/board-dt-tegra20.c
arch/arm/mach-tegra/board-dt-tegra30.c
drivers/clocksource/Makefile
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 6 | ||||
-rw-r--r-- | arch/arm/mach-tegra/apbio.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-dt-tegra20.c | 13 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-dt-tegra30.c | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/common.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/common.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpu-tegra.c | 40 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cpuidle-tegra30.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-tegra/flowctrl.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/headsmp.S | 221 | ||||
-rw-r--r-- | arch/arm/mach-tegra/hotplug.c | 21 | ||||
-rw-r--r-- | arch/arm/mach-tegra/platsmp.c | 43 | ||||
-rw-r--r-- | arch/arm/mach-tegra/pm.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/reset-handler.S | 239 | ||||
-rw-r--r-- | arch/arm/mach-tegra/reset.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-tegra20.S | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-tegra30.S | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.S | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra30_clocks_data.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-tegra/timer.c | 284 |
23 files changed, 347 insertions, 569 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index b442f15fd01a..1ec7f80e2af5 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -6,9 +6,9 @@ config ARCH_TEGRA_2x_SOC bool "Enable support for Tegra20 family" select ARCH_REQUIRE_GPIOLIB select ARM_ERRATA_720789 - select ARM_ERRATA_742230 + select ARM_ERRATA_742230 if SMP select ARM_ERRATA_751472 - select ARM_ERRATA_754327 + select ARM_ERRATA_754327 if SMP select ARM_ERRATA_764369 if SMP select ARM_GIC select CPU_FREQ_TABLE if CPU_FREQ diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 0979e8bba78a..f0520961bafe 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -2,15 +2,16 @@ obj-y += common.o obj-y += io.o obj-y += irq.o obj-y += clock.o -obj-y += timer.o obj-y += fuse.o obj-y += pmc.o obj-y += flowctrl.o obj-y += powergate.o obj-y += apbio.o obj-y += pm.o +obj-y += reset.o +obj-y += reset-handler.o +obj-y += sleep.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o -obj-$(CONFIG_CPU_IDLE) += sleep.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_clocks_data.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o @@ -27,7 +28,6 @@ ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o endif obj-$(CONFIG_SMP) += platsmp.o headsmp.o -obj-$(CONFIG_SMP) += reset.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-$(CONFIG_TEGRA_PCI) += pcie.o diff --git a/arch/arm/mach-tegra/apbio.c b/arch/arm/mach-tegra/apbio.c index d091675ba376..d7aa52ea6cfc 100644 --- a/arch/arm/mach-tegra/apbio.c +++ b/arch/arm/mach-tegra/apbio.c @@ -38,7 +38,7 @@ static void tegra_apb_writel_direct(u32 value, unsigned long offset); static struct dma_chan *tegra_apb_dma_chan; static struct dma_slave_config dma_sconfig; -bool tegra_apb_dma_init(void) +static bool tegra_apb_dma_init(void) { dma_cap_mask_t mask; diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c index 5ed81bab2d4b..d320f7ad7350 100644 --- a/arch/arm/mach-tegra/board-dt-tegra20.c +++ b/arch/arm/mach-tegra/board-dt-tegra20.c @@ -15,6 +15,7 @@ * */ +#include <linux/clocksource.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> @@ -43,31 +44,31 @@ #include "common.h" #include "iomap.h" -struct tegra_ehci_platform_data tegra_ehci1_pdata = { +static struct tegra_ehci_platform_data tegra_ehci1_pdata = { .operating_mode = TEGRA_USB_OTG, .power_down_on_bus_suspend = 1, .vbus_gpio = -1, }; -struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = { +static struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = { .reset_gpio = -1, .clk = "cdev2", }; -struct tegra_ehci_platform_data tegra_ehci2_pdata = { +static struct tegra_ehci_platform_data tegra_ehci2_pdata = { .phy_config = &tegra_ehci2_ulpi_phy_config, .operating_mode = TEGRA_USB_HOST, .power_down_on_bus_suspend = 1, .vbus_gpio = -1, }; -struct tegra_ehci_platform_data tegra_ehci3_pdata = { +static struct tegra_ehci_platform_data tegra_ehci3_pdata = { .operating_mode = TEGRA_USB_HOST, .power_down_on_bus_suspend = 1, .vbus_gpio = -1, }; -struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { +static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC1_BASE, "sdhci-tegra.0", NULL), OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC2_BASE, "sdhci-tegra.1", NULL), OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC3_BASE, "sdhci-tegra.2", NULL), @@ -200,7 +201,7 @@ DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)") .smp = smp_ops(tegra_smp_ops), .init_early = tegra20_init_early, .init_irq = tegra_dt_init_irq, - .init_time = tegra_init_timer, + .init_time = clocksource_of_init, .init_machine = tegra_dt_init, .init_late = tegra_dt_init_late, .restart = tegra_assert_system_reset, diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c index 12dc2ddeca64..97e1f67fc31d 100644 --- a/arch/arm/mach-tegra/board-dt-tegra30.c +++ b/arch/arm/mach-tegra/board-dt-tegra30.c @@ -23,6 +23,7 @@ * */ +#include <linux/clocksource.h> #include <linux/kernel.h> #include <linux/of.h> #include <linux/of_address.h> @@ -37,7 +38,7 @@ #include "common.h" #include "iomap.h" -struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = { +static struct of_dev_auxdata tegra30_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("nvidia,tegra20-sdhci", 0x78000000, "sdhci-tegra.0", NULL), OF_DEV_AUXDATA("nvidia,tegra20-sdhci", 0x78000200, "sdhci-tegra.1", NULL), OF_DEV_AUXDATA("nvidia,tegra20-sdhci", 0x78000400, "sdhci-tegra.2", NULL), @@ -111,7 +112,7 @@ DT_MACHINE_START(TEGRA30_DT, "NVIDIA Tegra30 (Flattened Device Tree)") .map_io = tegra_map_common_io, .init_early = tegra30_init_early, .init_irq = tegra_dt_init_irq, - .init_time = tegra_init_timer, + .init_time = clocksource_of_init, .init_machine = tegra30_dt_init, .init_late = tegra_init_late, .restart = tegra_assert_system_reset, diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 744cdd246f6a..da8f5a3c4240 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -55,5 +55,4 @@ static inline int harmony_pcie_init(void) { return 0; } void __init tegra_paz00_wifikill_init(void); -extern void tegra_init_timer(void); #endif diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 3599959517b3..8f0ffe97ffee 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -36,6 +36,7 @@ #include "apbio.h" #include "sleep.h" #include "pm.h" +#include "reset.h" /* * Storage for debug-macro.S's state. @@ -131,6 +132,7 @@ static void __init tegra_init_cache(void) #ifdef CONFIG_ARCH_TEGRA_2x_SOC void __init tegra20_init_early(void) { + tegra_cpu_reset_handler_init(); tegra_apb_io_init(); tegra_init_fuse(); tegra2_init_clocks(); @@ -144,6 +146,7 @@ void __init tegra20_init_early(void) #ifdef CONFIG_ARCH_TEGRA_3x_SOC void __init tegra30_init_early(void) { + tegra_cpu_reset_handler_init(); tegra_apb_io_init(); tegra_init_fuse(); tegra30_init_clocks(); diff --git a/arch/arm/mach-tegra/common.h b/arch/arm/mach-tegra/common.h index 02f71b4f1e51..32f8eb3fe344 100644 --- a/arch/arm/mach-tegra/common.h +++ b/arch/arm/mach-tegra/common.h @@ -1,4 +1,5 @@ extern struct smp_operations tegra_smp_ops; +extern int tegra_cpu_kill(unsigned int cpu); extern void tegra_cpu_die(unsigned int cpu); extern int tegra_cpu_disable(unsigned int cpu); diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index a74d3c7d2e26..85d4a23bba03 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -214,24 +214,6 @@ static int tegra_cpu_init(struct cpufreq_policy *policy) if (policy->cpu >= NUM_CPUS) return -EINVAL; - cpu_clk = clk_get_sys(NULL, "cpu"); - if (IS_ERR(cpu_clk)) - return PTR_ERR(cpu_clk); - - pll_x_clk = clk_get_sys(NULL, "pll_x"); - if (IS_ERR(pll_x_clk)) - return PTR_ERR(pll_x_clk); - - pll_p_clk = clk_get_sys(NULL, "pll_p"); - if (IS_ERR(pll_p_clk)) - return PTR_ERR(pll_p_clk); - - emc_clk = clk_get_sys("cpu", "emc"); - if (IS_ERR(emc_clk)) { - clk_put(cpu_clk); - return PTR_ERR(emc_clk); - } - clk_prepare_enable(emc_clk); clk_prepare_enable(cpu_clk); @@ -256,8 +238,6 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy) { cpufreq_frequency_table_cpuinfo(policy, freq_table); clk_disable_unprepare(emc_clk); - clk_put(emc_clk); - clk_put(cpu_clk); return 0; } @@ -278,12 +258,32 @@ static struct cpufreq_driver tegra_cpufreq_driver = { static int __init tegra_cpufreq_init(void) { + cpu_clk = clk_get_sys(NULL, "cpu"); + if (IS_ERR(cpu_clk)) + return PTR_ERR(cpu_clk); + + pll_x_clk = clk_get_sys(NULL, "pll_x"); + if (IS_ERR(pll_x_clk)) + return PTR_ERR(pll_x_clk); + + pll_p_clk = clk_get_sys(NULL, "pll_p"); + if (IS_ERR(pll_p_clk)) + return PTR_ERR(pll_p_clk); + + emc_clk = clk_get_sys("cpu", "emc"); + if (IS_ERR(emc_clk)) { + clk_put(cpu_clk); + return PTR_ERR(emc_clk); + } + return cpufreq_register_driver(&tegra_cpufreq_driver); } static void __exit tegra_cpufreq_exit(void) { cpufreq_unregister_driver(&tegra_cpufreq_driver); + clk_put(emc_clk); + clk_put(cpu_clk); } diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index 5e8cbf5b799f..82530bd9b8c2 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -121,9 +121,9 @@ static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev, } #endif -static int __cpuinit tegra30_idle_lp2(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) +static int tegra30_idle_lp2(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) { u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu; bool entered_lp2 = false; diff --git a/arch/arm/mach-tegra/flowctrl.c b/arch/arm/mach-tegra/flowctrl.c index a2250ddae797..5393eb2cae21 100644 --- a/arch/arm/mach-tegra/flowctrl.c +++ b/arch/arm/mach-tegra/flowctrl.c @@ -26,14 +26,14 @@ #include "flowctrl.h" #include "iomap.h" -u8 flowctrl_offset_halt_cpu[] = { +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, }; -u8 flowctrl_offset_cpu_csr[] = { +static u8 flowctrl_offset_cpu_csr[] = { FLOW_CTRL_CPU0_CSR, FLOW_CTRL_CPU1_CSR, FLOW_CTRL_CPU1_CSR + 8, diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S index 4a317fae6860..b2834810b02b 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -1,22 +1,9 @@ #include <linux/linkage.h> #include <linux/init.h> -#include <asm/cache.h> -#include <asm/asm-offsets.h> -#include <asm/hardware/cache-l2x0.h> - -#include "flowctrl.h" -#include "iomap.h" -#include "reset.h" #include "sleep.h" -#define APB_MISC_GP_HIDREV 0x804 -#define PMC_SCRATCH41 0x140 - -#define RESET_DATA(x) ((TEGRA_RESET_##x)*4) - .section ".text.head", "ax" - __CPUINIT /* * Tegra specific entry point for secondary CPUs. @@ -61,7 +48,6 @@ ENTRY(v7_invalidate_l1) mov pc, lr ENDPROC(v7_invalidate_l1) - ENTRY(tegra_secondary_startup) bl v7_invalidate_l1 /* Enable coresight */ @@ -69,210 +55,3 @@ ENTRY(tegra_secondary_startup) mcr p14, 0, r0, c7, c12, 6 b secondary_startup ENDPROC(tegra_secondary_startup) - -#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. - */ -ENTRY(tegra_resume) - bl v7_invalidate_l1 - /* Enable coresight */ - mov32 r0, 0xC5ACCE55 - mcr p14, 0, r0, c7, c12, 6 - - cpu_id r0 - cmp r0, #0 @ CPU0? - bne cpu_resume @ no - -#ifdef CONFIG_ARCH_TEGRA_3x_SOC - /* Are we on Tegra20? */ - mov32 r6, TEGRA_APB_MISC_BASE - ldr r0, [r6, #APB_MISC_GP_HIDREV] - and r0, r0, #0xff00 - cmp r0, #(0x20 << 8) - beq 1f @ Yes - /* Clear the flow controller flags for this CPU. */ - mov32 r2, TEGRA_FLOW_CTRL_BASE + FLOW_CTRL_CPU0_CSR @ CPU0 CSR - ldr r1, [r2] - /* Clear event & intr flag */ - orr r1, r1, \ - #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG - movw r0, #0x0FFD @ enable, cluster_switch, immed, & bitmaps - bic r1, r1, r0 - str r1, [r2] -1: -#endif - -#ifdef CONFIG_HAVE_ARM_SCU - /* enable SCU */ - mov32 r0, TEGRA_ARM_PERIF_BASE - ldr r1, [r0] - orr r1, r1, #1 - str r1, [r0] -#endif - - /* L2 cache resume & re-enable */ - l2_cache_resume r0, r1, r2, l2x0_saved_regs_addr - - b cpu_resume -ENDPROC(tegra_resume) -#endif - -#ifdef CONFIG_CACHE_L2X0 - .globl l2x0_saved_regs_addr -l2x0_saved_regs_addr: - .long 0 -#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: - * - * 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 - 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? */ - mov32 r6, TEGRA_APB_MISC_BASE - ldr r0, [r6, #APB_MISC_GP_HIDREV] - and r0, r0, #0xff00 - cmp r0, #(0x20 << 8) - bne 1f - /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ - mov32 r6, TEGRA_PMC_BASE - mov r0, #0 - cmp r10, #0 - strne r0, [r6, #PMC_SCRATCH41] -1: -#endif - - /* 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) but CPU 0 - * cannot be here. - */ - cmp r10, #0 - bleq __die @ CPU0 cannot be here - 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? */ - mov32 r6, TEGRA_APB_MISC_BASE - ldr r0, [r6, #APB_MISC_GP_HIDREV] - and r0, r0, #0xff00 - cmp r0, #(0x20 << 8) - 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) diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index dca5141a2c31..6a27de4001ee 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -19,6 +19,17 @@ 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 * @@ -26,18 +37,12 @@ static void (*tegra_hotplug_shutdown)(void); */ void __ref tegra_cpu_die(unsigned int cpu) { - cpu = cpu_logical_map(cpu); - - /* Flush the L1 data cache. */ - flush_cache_all(); + /* Clean L1 data cache */ + tegra_disable_clean_inv_dcache(); /* Shut down the current CPU. */ tegra_hotplug_shutdown(); - /* Clock gate the CPU */ - tegra_wait_cpu_in_reset(cpu); - tegra_disable_cpu_clock(cpu); - /* Should never return here. */ BUG(); } diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 18d7290cf93b..3c4a43c892a5 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -23,6 +23,7 @@ #include <asm/cacheflush.h> #include <asm/mach-types.h> #include <asm/smp_scu.h> +#include <asm/smp_plat.h> #include <mach/powergate.h> @@ -36,6 +37,7 @@ extern void tegra_secondary_startup(void); +static cpumask_t tegra_cpu_init_mask; static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE); #define EVP_CPU_RESET_VECTOR \ @@ -50,6 +52,7 @@ static void __cpuinit tegra_secondary_init(unsigned int cpu) */ gic_secondary_init(0); + cpumask_set_cpu(cpu, &tegra_cpu_init_mask); } static int tegra20_power_up_cpu(unsigned int cpu) @@ -72,14 +75,42 @@ static int tegra30_power_up_cpu(unsigned int cpu) if (pwrgateid < 0) return pwrgateid; - /* If this is the first boot, toggle powergates directly. */ + /* + * 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_power_up_cpu() + * next time around. + */ + if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { + timeout = jiffies + msecs_to_jiffies(50); + do { + if (!tegra_powergate_is_powered(pwrgateid)) + 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_powergate_is_powered(pwrgateid)) { ret = tegra_powergate_power_on(pwrgateid); if (ret) return ret; /* Wait for the power to come up. */ - timeout = jiffies + 10*HZ; + timeout = jiffies + msecs_to_jiffies(100); while (tegra_powergate_is_powered(pwrgateid)) { if (time_after(jiffies, timeout)) return -ETIMEDOUT; @@ -87,6 +118,7 @@ static int tegra30_power_up_cpu(unsigned int cpu) } } +remove_clamps: /* CPU partition is powered. Enable the CPU clock. */ tegra_enable_cpu_clock(cpu); udelay(10); @@ -105,6 +137,8 @@ static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct * { int status; + 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 @@ -163,7 +197,9 @@ static void __init tegra_smp_init_cpus(void) static void __init tegra_smp_prepare_cpus(unsigned int max_cpus) { - tegra_cpu_reset_handler_init(); + /* Always mark the boot CPU (CPU0) as initialized. */ + cpumask_set_cpu(0, &tegra_cpu_init_mask); + scu_enable(scu_base); } @@ -173,6 +209,7 @@ struct smp_operations tegra_smp_ops __initdata = { .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, .cpu_disable = tegra_cpu_disable, #endif diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 1b11707eaca0..498d70b33775 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -148,7 +148,7 @@ static void suspend_cpu_complex(void) save_cpu_arch_register(); } -void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id) +void tegra_clear_cpu_in_lp2(int phy_cpu_id) { u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; @@ -160,7 +160,7 @@ void __cpuinit tegra_clear_cpu_in_lp2(int phy_cpu_id) spin_unlock(&tegra_lp2_lock); } -bool __cpuinit tegra_set_cpu_in_lp2(int phy_cpu_id) +bool tegra_set_cpu_in_lp2(int phy_cpu_id) { bool last_cpu = false; cpumask_t *cpu_lp2_mask = tegra_cpu_lp2_mask; diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S new file mode 100644 index 000000000000..54382ceade4a --- /dev/null +++ b/arch/arm/mach-tegra/reset-handler.S @@ -0,0 +1,239 @@ +/* + * 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 <linux/init.h> + +#include <asm/cache.h> +#include <asm/asm-offsets.h> +#include <asm/hardware/cache-l2x0.h> + +#include "flowctrl.h" +#include "iomap.h" +#include "reset.h" +#include "sleep.h" + +#define APB_MISC_GP_HIDREV 0x804 +#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. + */ +ENTRY(tegra_resume) + bl v7_invalidate_l1 + /* Enable coresight */ + mov32 r0, 0xC5ACCE55 + mcr p14, 0, r0, c7, c12, 6 + + cpu_id r0 + cmp r0, #0 @ CPU0? + bne cpu_resume @ no + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC + /* Are we on Tegra20? */ + mov32 r6, TEGRA_APB_MISC_BASE + ldr r0, [r6, #APB_MISC_GP_HIDREV] + and r0, r0, #0xff00 + cmp r0, #(0x20 << 8) + beq 1f @ Yes + /* Clear the flow controller flags for this CPU. */ + mov32 r2, TEGRA_FLOW_CTRL_BASE + FLOW_CTRL_CPU0_CSR @ CPU0 CSR + ldr r1, [r2] + /* Clear event & intr flag */ + orr r1, r1, \ + #FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG + movw r0, #0x0FFD @ enable, cluster_switch, immed, & bitmaps + bic r1, r1, r0 + str r1, [r2] +1: +#endif + +#ifdef CONFIG_HAVE_ARM_SCU + /* enable SCU */ + mov32 r0, TEGRA_ARM_PERIF_BASE + ldr r1, [r0] + orr r1, r1, #1 + str r1, [r0] +#endif + + /* L2 cache resume & re-enable */ + l2_cache_resume r0, r1, r2, l2x0_saved_regs_addr + + b cpu_resume +ENDPROC(tegra_resume) +#endif + +#ifdef CONFIG_CACHE_L2X0 + .globl l2x0_saved_regs_addr +l2x0_saved_regs_addr: + .long 0 +#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: + * + * 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 + 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? */ + mov32 r6, TEGRA_APB_MISC_BASE + ldr r0, [r6, #APB_MISC_GP_HIDREV] + and r0, r0, #0xff00 + cmp r0, #(0x20 << 8) + bne 1f + /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ + mov32 r6, TEGRA_PMC_BASE + mov r0, #0 + cmp r10, #0 + strne r0, [r6, #PMC_SCRATCH41] +1: +#endif + + /* 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) but CPU 0 + * cannot be here. + */ + cmp r10, #0 + bleq __die @ CPU0 cannot be here + 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? */ + mov32 r6, TEGRA_APB_MISC_BASE + ldr r0, [r6, #APB_MISC_GP_HIDREV] + and r0, r0, #0xff00 + cmp r0, #(0x20 << 8) + 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) diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c index 3fd89ecd158e..1ac434e0068f 100644 --- a/arch/arm/mach-tegra/reset.c +++ b/arch/arm/mach-tegra/reset.c @@ -75,7 +75,7 @@ void __init tegra_cpu_reset_handler_init(void) #ifdef CONFIG_SMP __tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_PRESENT] = - *((u32 *)cpu_present_mask); + *((u32 *)cpu_possible_mask); __tegra_cpu_reset_handler_data[TEGRA_RESET_STARTUP_SECONDARY] = virt_to_phys((void *)tegra_secondary_startup); #endif diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S index 72ce709799da..ad2ca07d0578 100644 --- a/arch/arm/mach-tegra/sleep-tegra20.S +++ b/arch/arm/mach-tegra/sleep-tegra20.S @@ -33,9 +33,6 @@ * should never return */ ENTRY(tegra20_hotplug_shutdown) - /* Turn off SMP coherency */ - exit_smp r4, r5 - /* Put this CPU down */ cpu_id r0 bl tegra20_cpu_shutdown diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S index 562a8e7e413d..63a15bd9b653 100644 --- a/arch/arm/mach-tegra/sleep-tegra30.S +++ b/arch/arm/mach-tegra/sleep-tegra30.S @@ -32,9 +32,6 @@ * Should never return. */ ENTRY(tegra30_hotplug_shutdown) - /* Turn off SMP coherency */ - exit_smp r4, r5 - /* Powergate this CPU */ mov r0, #TEGRA30_POWER_HOTPLUG_SHUTDOWN bl tegra30_cpu_shutdown diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S index 26afa7cbed11..addae357da3f 100644 --- a/arch/arm/mach-tegra/sleep.S +++ b/arch/arm/mach-tegra/sleep.S @@ -34,7 +34,7 @@ #include "flowctrl.h" #include "sleep.h" -#ifdef CONFIG_PM_SLEEP +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) /* * tegra_disable_clean_inv_dcache * @@ -60,7 +60,9 @@ ENTRY(tegra_disable_clean_inv_dcache) ldmfd sp!, {r0, r4-r5, r7, r9-r11, pc} ENDPROC(tegra_disable_clean_inv_dcache) +#endif +#ifdef CONFIG_PM_SLEEP /* * tegra_sleep_cpu_finish(unsigned long v2p) * diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index 9821ee725420..56505c381ea8 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -106,6 +106,7 @@ exit_l2_resume: #else void tegra_resume(void); int tegra_sleep_cpu_finish(unsigned long); +void tegra_disable_clean_inv_dcache(void); #ifdef CONFIG_HOTPLUG_CPU void tegra20_hotplug_init(void); diff --git a/arch/arm/mach-tegra/tegra30_clocks_data.c b/arch/arm/mach-tegra/tegra30_clocks_data.c index 6942c7add3bb..741d264d5ecb 100644 --- a/arch/arm/mach-tegra/tegra30_clocks_data.c +++ b/arch/arm/mach-tegra/tegra30_clocks_data.c @@ -1183,7 +1183,7 @@ static struct clk tegra_dsib = { .num_parents = ARRAY_SIZE(mux_plld_out0_plld2_out0), }; -struct clk *tegra_list_clks[] = { +static struct clk *tegra_list_clks[] = { &tegra_apbdma, &tegra_rtc, &tegra_kbc, @@ -1289,7 +1289,7 @@ struct clk *tegra_list_clks[] = { * configuration. List those here to register them twice in the clock lookup * table under two names. */ -struct clk_duplicate tegra_clk_duplicates[] = { +static struct clk_duplicate tegra_clk_duplicates[] = { CLK_DUPLICATE("uarta", "serial8250.0", NULL), CLK_DUPLICATE("uartb", "serial8250.1", NULL), CLK_DUPLICATE("uartc", "serial8250.2", NULL), @@ -1340,7 +1340,7 @@ struct clk_duplicate tegra_clk_duplicates[] = { CLK_DUPLICATE("pll_d2_out0", "hdmi", "parent"), }; -struct clk *tegra_ptr_clks[] = { +static struct clk *tegra_ptr_clks[] = { &tegra_clk_32k, &tegra_clk_m, &tegra_clk_m_div2, diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c deleted file mode 100644 index eba0969ded19..000000000000 --- a/arch/arm/mach-tegra/timer.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * arch/arch/mach-tegra/timer.c - * - * Copyright (C) 2010 Google, Inc. - * - * Author: - * Colin Cross <ccross@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/err.h> -#include <linux/time.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/clockchips.h> -#include <linux/clocksource.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> - -#include <asm/mach/time.h> -#include <asm/smp_twd.h> -#include <asm/sched_clock.h> - -#include "board.h" - -#define RTC_SECONDS 0x08 -#define RTC_SHADOW_SECONDS 0x0c -#define RTC_MILLISECONDS 0x10 - -#define TIMERUS_CNTR_1US 0x10 -#define TIMERUS_USEC_CFG 0x14 -#define TIMERUS_CNTR_FREEZE 0x4c - -#define TIMER1_BASE 0x0 -#define TIMER2_BASE 0x8 -#define TIMER3_BASE 0x50 -#define TIMER4_BASE 0x58 - -#define TIMER_PTV 0x0 -#define TIMER_PCR 0x4 - -static void __iomem *timer_reg_base; -static void __iomem *rtc_base; - -static struct timespec persistent_ts; -static u64 persistent_ms, last_persistent_ms; - -#define timer_writel(value, reg) \ - __raw_writel(value, timer_reg_base + (reg)) -#define timer_readl(reg) \ - __raw_readl(timer_reg_base + (reg)) - -static int tegra_timer_set_next_event(unsigned long cycles, - struct clock_event_device *evt) -{ - u32 reg; - - reg = 0x80000000 | ((cycles > 1) ? (cycles-1) : 0); - timer_writel(reg, TIMER3_BASE + TIMER_PTV); - - return 0; -} - -static void tegra_timer_set_mode(enum clock_event_mode mode, - struct clock_event_device *evt) -{ - u32 reg; - - timer_writel(0, TIMER3_BASE + TIMER_PTV); - - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - reg = 0xC0000000 | ((1000000/HZ)-1); - timer_writel(reg, TIMER3_BASE + TIMER_PTV); - break; - case CLOCK_EVT_MODE_ONESHOT: - break; - case CLOCK_EVT_MODE_UNUSED: - case CLOCK_EVT_MODE_SHUTDOWN: - case CLOCK_EVT_MODE_RESUME: - break; - } -} - -static struct clock_event_device tegra_clockevent = { - .name = "timer0", - .rating = 300, - .features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC, - .set_next_event = tegra_timer_set_next_event, - .set_mode = tegra_timer_set_mode, -}; - -static u32 notrace tegra_read_sched_clock(void) -{ - return timer_readl(TIMERUS_CNTR_1US); -} - -/* - * tegra_rtc_read - Reads the Tegra RTC registers - * Care must be taken that this funciton is not called while the - * tegra_rtc driver could be executing to avoid race conditions - * on the RTC shadow register - */ -static u64 tegra_rtc_read_ms(void) -{ - u32 ms = readl(rtc_base + RTC_MILLISECONDS); - u32 s = readl(rtc_base + RTC_SHADOW_SECONDS); - return (u64)s * MSEC_PER_SEC + ms; -} - -/* - * tegra_read_persistent_clock - Return time from a persistent clock. - * - * Reads the time from a source which isn't disabled during PM, the - * 32k sync timer. Convert the cycles elapsed since last read into - * nsecs and adds to a monotonically increasing timespec. - * Care must be taken that this funciton is not called while the - * tegra_rtc driver could be executing to avoid race conditions - * on the RTC shadow register - */ -static void tegra_read_persistent_clock(struct timespec *ts) -{ - u64 delta; - struct timespec *tsp = &persistent_ts; - - last_persistent_ms = persistent_ms; - persistent_ms = tegra_rtc_read_ms(); - delta = persistent_ms - last_persistent_ms; - - timespec_add_ns(tsp, delta * NSEC_PER_MSEC); - *ts = *tsp; -} - -static irqreturn_t tegra_timer_interrupt(int irq, void *dev_id) -{ - struct clock_event_device *evt = (struct clock_event_device *)dev_id; - timer_writel(1<<30, TIMER3_BASE + TIMER_PCR); - evt->event_handler(evt); - return IRQ_HANDLED; -} - -static struct irqaction tegra_timer_irq = { - .name = "timer0", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_HIGH, - .handler = tegra_timer_interrupt, - .dev_id = &tegra_clockevent, -}; - -static const struct of_device_id timer_match[] __initconst = { - { .compatible = "nvidia,tegra20-timer" }, - {} -}; - -static const struct of_device_id rtc_match[] __initconst = { - { .compatible = "nvidia,tegra20-rtc" }, - {} -}; - -void __init tegra_init_timer(void) -{ - struct device_node *np; - struct clk *clk; - unsigned long rate; - int ret; - - np = of_find_matching_node(NULL, timer_match); - if (!np) { - pr_err("Failed to find timer DT node\n"); - BUG(); - } - - timer_reg_base = of_iomap(np, 0); - if (!timer_reg_base) { - pr_err("Can't map timer registers"); - BUG(); - } - - tegra_timer_irq.irq = irq_of_parse_and_map(np, 2); - if (tegra_timer_irq.irq <= 0) { - pr_err("Failed to map timer IRQ\n"); - BUG(); - } - - clk = clk_get_sys("timer", NULL); - if (IS_ERR(clk)) { - pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n"); - rate = 12000000; - } else { - clk_prepare_enable(clk); - rate = clk_get_rate(clk); - } - - of_node_put(np); - - np = of_find_matching_node(NULL, rtc_match); - if (!np) { - pr_err("Failed to find RTC DT node\n"); - BUG(); - } - - rtc_base = of_iomap(np, 0); - if (!rtc_base) { - pr_err("Can't map RTC registers"); - BUG(); - } - - /* - * rtc registers are used by read_persistent_clock, keep the rtc clock - * enabled - */ - clk = clk_get_sys("rtc-tegra", NULL); - if (IS_ERR(clk)) - pr_warn("Unable to get rtc-tegra clock\n"); - else - clk_prepare_enable(clk); - - of_node_put(np); - - switch (rate) { - case 12000000: - timer_writel(0x000b, TIMERUS_USEC_CFG); - break; - case 13000000: - timer_writel(0x000c, TIMERUS_USEC_CFG); - break; - case 19200000: - timer_writel(0x045f, TIMERUS_USEC_CFG); - break; - case 26000000: - timer_writel(0x0019, TIMERUS_USEC_CFG); - break; - default: - WARN(1, "Unknown clock rate"); - } - - setup_sched_clock(tegra_read_sched_clock, 32, 1000000); - - if (clocksource_mmio_init(timer_reg_base + TIMERUS_CNTR_1US, - "timer_us", 1000000, 300, 32, clocksource_mmio_readl_up)) { - pr_err("Failed to register clocksource\n"); - BUG(); - } - - ret = setup_irq(tegra_timer_irq.irq, &tegra_timer_irq); - if (ret) { - pr_err("Failed to register timer IRQ: %d\n", ret); - BUG(); - } - - tegra_clockevent.cpumask = cpu_all_mask; - tegra_clockevent.irq = tegra_timer_irq.irq; - clockevents_config_and_register(&tegra_clockevent, 1000000, - 0x1, 0x1fffffff); -#ifdef CONFIG_HAVE_ARM_TWD - twd_local_timer_of_register(); -#endif - register_persistent_clock(NULL, tegra_read_persistent_clock); -} - -#ifdef CONFIG_PM -static u32 usec_config; - -void tegra_timer_suspend(void) -{ - usec_config = timer_readl(TIMERUS_USEC_CFG); -} - -void tegra_timer_resume(void) -{ - timer_writel(usec_config, TIMERUS_USEC_CFG); -} -#endif |