diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/clock.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-tegra/common.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/headsmp.S | 6 | ||||
-rw-r--r-- | arch/arm/mach-tegra/hotplug.c | 118 | ||||
-rw-r--r-- | arch/arm/mach-tegra/platsmp.c | 29 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-t20.S | 82 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep-t30.S | 107 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.S | 33 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 85 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20_clocks.c | 70 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra20_clocks_data.c | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra30_clocks.c | 72 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra30_clocks_data.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_cpu_car.h | 87 |
15 files changed, 556 insertions, 147 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index f07f99452a98..84d21f514418 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -15,8 +15,10 @@ 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) += tegra2_emc.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += sleep-t20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra30_clocks_data.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += sleep-t30.o obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_SMP) += reset.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 632133fc985b..fd82085eca5d 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -31,6 +31,10 @@ #include "board.h" #include "clock.h" +#include "tegra_cpu_car.h" + +/* Global data of Tegra CPU CAR ops */ +struct tegra_cpu_car_ops *tegra_cpu_car_ops; /* * Locking: diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index f3654f830991..0560538bf598 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -34,6 +34,7 @@ #include "fuse.h" #include "pmc.h" #include "apbio.h" +#include "sleep.h" /* * Storage for debug-macro.S's state. @@ -135,6 +136,7 @@ void __init tegra20_init_early(void) tegra_init_cache(0x331, 0x441); tegra_pmc_init(); tegra_powergate_init(); + tegra20_hotplug_init(); } #endif #ifdef CONFIG_ARCH_TEGRA_3x_SOC @@ -147,6 +149,7 @@ void __init tegra30_init_early(void) tegra_init_cache(0x441, 0x551); tegra_pmc_init(); tegra_powergate_init(); + tegra30_hotplug_init(); } #endif diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S index fef9c2c51370..6addc78cb6b2 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -7,17 +7,13 @@ #include "flowctrl.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) - .macro mov32, reg, val - movw \reg, #:lower16:\val - movt \reg, #:upper16:\val - .endm - .section ".text.head", "ax" __CPUINIT diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index d8dc9ddd6d18..d02a35476135 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -1,91 +1,23 @@ /* - * linux/arch/arm/mach-realview/hotplug.c * * Copyright (C) 2002 ARM Ltd. * All Rights Reserved + * 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 of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/kernel.h> -#include <linux/errno.h> #include <linux/smp.h> #include <asm/cacheflush.h> -#include <asm/cp15.h> +#include <asm/smp_plat.h> -static inline void cpu_enter_lowpower(void) -{ - unsigned int v; - - flush_cache_all(); - asm volatile( - " mcr p15, 0, %1, c7, c5, 0\n" - " mcr p15, 0, %1, c7, c10, 4\n" - /* - * Turn off coherency - */ - " mrc p15, 0, %0, c1, c0, 1\n" - " bic %0, %0, #0x20\n" - " mcr p15, 0, %0, c1, c0, 1\n" - " mrc p15, 0, %0, c1, c0, 0\n" - " bic %0, %0, %2\n" - " mcr p15, 0, %0, c1, c0, 0\n" - : "=&r" (v) - : "r" (0), "Ir" (CR_C) - : "cc"); -} - -static inline void cpu_leave_lowpower(void) -{ - unsigned int v; - - asm volatile( - "mrc p15, 0, %0, c1, c0, 0\n" - " orr %0, %0, %1\n" - " mcr p15, 0, %0, c1, c0, 0\n" - " mrc p15, 0, %0, c1, c0, 1\n" - " orr %0, %0, #0x20\n" - " mcr p15, 0, %0, c1, c0, 1\n" - : "=&r" (v) - : "Ir" (CR_C) - : "cc"); -} - -static inline void platform_do_lowpower(unsigned int cpu, int *spurious) -{ - /* - * there is no power-control hardware on this platform, so all - * we can do is put the core into WFI; this is safe as the calling - * code will have already disabled interrupts - */ - for (;;) { - /* - * here's the WFI - */ - asm(".word 0xe320f003\n" - : - : - : "memory", "cc"); +#include "sleep.h" +#include "tegra_cpu_car.h" - /*if (pen_release == cpu) {*/ - /* - * OK, proper wakeup, we're done - */ - break; - /*}*/ - - /* - * Getting here, means that we have come out of WFI without - * having been woken up - this shouldn't happen - * - * Just note it happening - when we're woken, we can report - * its occurrence. - */ - (*spurious)++; - } -} +static void (*tegra_hotplug_shutdown)(void); int platform_cpu_kill(unsigned int cpu) { @@ -99,22 +31,20 @@ int platform_cpu_kill(unsigned int cpu) */ void platform_cpu_die(unsigned int cpu) { - int spurious = 0; + cpu = cpu_logical_map(cpu); - /* - * we're ready for shutdown now, so do it - */ - cpu_enter_lowpower(); - platform_do_lowpower(cpu, &spurious); + /* Flush the L1 data cache. */ + flush_cache_all(); - /* - * bring this CPU back into the world of cache - * coherency, and then restore interrupts - */ - cpu_leave_lowpower(); + /* Shut down the current CPU. */ + tegra_hotplug_shutdown(); - if (spurious) - pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); + /* Clock gate the CPU */ + tegra_wait_cpu_in_reset(cpu); + tegra_disable_cpu_clock(cpu); + + /* Should never return here. */ + BUG(); } int platform_cpu_disable(unsigned int cpu) @@ -125,3 +55,19 @@ int platform_cpu_disable(unsigned int cpu) */ return cpu == 0 ? -EPERM : 0; } + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +extern void tegra20_hotplug_shutdown(void); +void __init tegra20_hotplug_init(void) +{ + tegra_hotplug_shutdown = tegra20_hotplug_shutdown; +} +#endif + +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +extern void tegra30_hotplug_shutdown(void); +void __init tegra30_hotplug_init(void) +{ + tegra_hotplug_shutdown = tegra30_hotplug_shutdown; +} +#endif diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 1a208dbf682f..96ed1718eef0 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -31,6 +31,7 @@ #include "fuse.h" #include "flowctrl.h" #include "reset.h" +#include "tegra_cpu_car.h" extern void tegra_secondary_startup(void); @@ -38,17 +39,6 @@ static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE); #define EVP_CPU_RESET_VECTOR \ (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) -#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \ - (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c) -#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \ - (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340) -#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \ - (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344) -#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \ - (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c) - -#define CPU_CLOCK(cpu) (0x1<<(8+cpu)) -#define CPU_RESET(cpu) (0x1111ul<<(cpu)) void __cpuinit platform_secondary_init(unsigned int cpu) { @@ -63,13 +53,8 @@ void __cpuinit platform_secondary_init(unsigned int cpu) static int tegra20_power_up_cpu(unsigned int cpu) { - u32 reg; - /* Enable the CPU clock. */ - reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); - writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX); - barrier(); - reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + tegra_enable_cpu_clock(cpu); /* Clear flow controller CSR. */ flowctrl_write_cpu_csr(cpu, 0); @@ -79,7 +64,6 @@ static int tegra20_power_up_cpu(unsigned int cpu) static int tegra30_power_up_cpu(unsigned int cpu) { - u32 reg; int ret, pwrgateid; unsigned long timeout; @@ -103,8 +87,7 @@ static int tegra30_power_up_cpu(unsigned int cpu) } /* CPU partition is powered. Enable the CPU clock. */ - writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); - reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); + tegra_enable_cpu_clock(cpu); udelay(10); /* Remove I/O clamps. */ @@ -128,8 +111,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) * via the flow controller). This will have no effect on first boot * of the CPU since it should already be in reset. */ - writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); - dmb(); + tegra_put_cpu_in_reset(cpu); /* * Unhalt the CPU. If the flow controller was used to power-gate the @@ -155,8 +137,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) goto done; /* Take the CPU out of reset. */ - writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); - wmb(); + tegra_cpu_out_of_reset(cpu); done: return status; } diff --git a/arch/arm/mach-tegra/sleep-t20.S b/arch/arm/mach-tegra/sleep-t20.S new file mode 100644 index 000000000000..a36ae413e2b8 --- /dev/null +++ b/arch/arm/mach-tegra/sleep-t20.S @@ -0,0 +1,82 @@ +/* + * 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 <mach/iomap.h> + +#include "sleep.h" +#include "flowctrl.h" + +#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) + /* Turn off SMP coherency */ + exit_smp r4, r5 + + /* Put this CPU down */ + cpu_id r0 + bl tegra20_cpu_shutdown + mov pc, 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 + moveq pc, lr @ must not be called for CPU 0 + + 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 . + mov pc, lr +ENDPROC(tegra20_cpu_shutdown) +#endif diff --git a/arch/arm/mach-tegra/sleep-t30.S b/arch/arm/mach-tegra/sleep-t30.S new file mode 100644 index 000000000000..777d9cee8b90 --- /dev/null +++ b/arch/arm/mach-tegra/sleep-t30.S @@ -0,0 +1,107 @@ +/* + * 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 <asm/assembler.h> + +#include <mach/iomap.h> + +#include "sleep.h" +#include "flowctrl.h" + +#define TEGRA30_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */ + +#if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP) +/* + * tegra30_hotplug_shutdown(void) + * + * Powergates the current CPU. + * 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 + mov pc, 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. + * Must never be called for CPU 0. + * + * corrupts r0-r4, r12 + */ +ENTRY(tegra30_cpu_shutdown) + cpu_id r3 + cmp r3, #0 + moveq pc, lr @ Must never be called for CPU 0 + + 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 + mov r4, #(1 << 4) + orr r12, r12, r4, lsl r3 + 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 + movne r3, #FLOW_CTRL_WAITEVENT @ For hotplug + str r3, [r2] + ldr r0, [r2] + b wfe_war + +__cpu_reset_again: + dsb + .align 5 + wfe @ CPU should be power gated here +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 diff --git a/arch/arm/mach-tegra/sleep.S b/arch/arm/mach-tegra/sleep.S index d29b156a8011..ea81554c4833 100644 --- a/arch/arm/mach-tegra/sleep.S +++ b/arch/arm/mach-tegra/sleep.S @@ -29,36 +29,5 @@ #include <mach/iomap.h> #include "flowctrl.h" +#include "sleep.h" -#define TEGRA_FLOW_CTRL_VIRT (TEGRA_FLOW_CTRL_BASE - IO_PPSB_PHYS \ - + IO_PPSB_VIRT) - -/* 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 diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h new file mode 100644 index 000000000000..e25a7cd703d9 --- /dev/null +++ b/arch/arm/mach-tegra/sleep.h @@ -0,0 +1,85 @@ +/* + * 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 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 <mach/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) + +#ifdef __ASSEMBLY__ +/* 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 + +/* 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 + cpu_id \tmp1 + mov \tmp1, \tmp1, lsl #2 + mov \tmp2, #0xf + mov \tmp2, \tmp2, lsl \tmp1 + mov32 \tmp1, TEGRA_ARM_PERIF_VIRT + 0xC + str \tmp2, [\tmp1] @ invalidate SCU tags for CPU + dsb +.endm +#else + +#ifdef CONFIG_HOTPLUG_CPU +void tegra20_hotplug_init(void); +void tegra30_hotplug_init(void); +#else +static inline void tegra20_hotplug_init(void) {} +static inline void tegra30_hotplug_init(void) {} +#endif + +#endif +#endif diff --git a/arch/arm/mach-tegra/tegra20_clocks.c b/arch/arm/mach-tegra/tegra20_clocks.c index 840ab262272a..9273b0dffc66 100644 --- a/arch/arm/mach-tegra/tegra20_clocks.c +++ b/arch/arm/mach-tegra/tegra20_clocks.c @@ -33,6 +33,7 @@ #include "clock.h" #include "fuse.h" #include "tegra2_emc.h" +#include "tegra_cpu_car.h" #define RST_DEVICES 0x004 #define RST_DEVICES_SET 0x300 @@ -152,6 +153,14 @@ #define PMC_BLINK_TIMER_DATA_OFF_SHIFT 16 #define PMC_BLINK_TIMER_DATA_OFF_MASK 0xffff +/* Tegra CPU clock and reset control regs */ +#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340 +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344 + +#define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) +#define CPU_RESET(cpu) (0x1111ul << (cpu)) + static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE); static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); @@ -1553,3 +1562,64 @@ struct clk_ops tegra_cdev_clk_ops = { .disable = tegra20_cdev_clk_disable, .recalc_rate = tegra20_cdev_recalc_rate, }; + +/* Tegra20 CPU clock and reset control functions */ +static void tegra20_wait_cpu_in_reset(u32 cpu) +{ + unsigned int reg; + + do { + reg = readl(reg_clk_base + + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + cpu_relax(); + } while (!(reg & (1 << cpu))); /* check CPU been reset or not */ + + return; +} + +static void tegra20_put_cpu_in_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + reg_clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + dmb(); +} + +static void tegra20_cpu_out_of_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + reg_clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); + wmb(); +} + +static void tegra20_enable_cpu_clock(u32 cpu) +{ + unsigned int reg; + + reg = readl(reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + writel(reg & ~CPU_CLOCK(cpu), + reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + barrier(); + reg = readl(reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); +} + +static void tegra20_disable_cpu_clock(u32 cpu) +{ + unsigned int reg; + + reg = readl(reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + writel(reg | CPU_CLOCK(cpu), + reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); +} + +static struct tegra_cpu_car_ops tegra20_cpu_car_ops = { + .wait_for_reset = tegra20_wait_cpu_in_reset, + .put_in_reset = tegra20_put_cpu_in_reset, + .out_of_reset = tegra20_cpu_out_of_reset, + .enable_clock = tegra20_enable_cpu_clock, + .disable_clock = tegra20_disable_cpu_clock, +}; + +void __init tegra20_cpu_car_ops_init(void) +{ + tegra_cpu_car_ops = &tegra20_cpu_car_ops; +} diff --git a/arch/arm/mach-tegra/tegra20_clocks_data.c b/arch/arm/mach-tegra/tegra20_clocks_data.c index 1a35c003fba8..e81dcd239c95 100644 --- a/arch/arm/mach-tegra/tegra20_clocks_data.c +++ b/arch/arm/mach-tegra/tegra20_clocks_data.c @@ -34,6 +34,7 @@ #include "fuse.h" #include "tegra2_emc.h" #include "tegra20_clocks.h" +#include "tegra_cpu_car.h" /* Clock definitions */ @@ -1139,4 +1140,5 @@ void __init tegra2_init_clocks(void) } init_audio_sync_clock_mux(); + tegra20_cpu_car_ops_init(); } diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c index 63615dadfbb2..5cd502c27163 100644 --- a/arch/arm/mach-tegra/tegra30_clocks.c +++ b/arch/arm/mach-tegra/tegra30_clocks.c @@ -35,6 +35,7 @@ #include "clock.h" #include "fuse.h" +#include "tegra_cpu_car.h" #define USE_PLL_LOCK_BITS 0 @@ -299,6 +300,16 @@ /* FIXME: recommended safety delay after lock is detected */ #define PLL_POST_LOCK_DELAY 100 +/* Tegra CPU clock and reset control regs */ +#define TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX 0x4c +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET 0x340 +#define TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR 0x344 +#define TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR 0x34c +#define TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS 0x470 + +#define CPU_CLOCK(cpu) (0x1 << (8 + cpu)) +#define CPU_RESET(cpu) (0x1111ul << (cpu)) + /** * Structure defining the fields for USB UTMI clocks Parameters. */ @@ -2221,3 +2232,64 @@ struct clk_ops tegra_cml_clk_ops = { struct clk_ops tegra_pciex_clk_ops = { .recalc_rate = tegra30_clk_fixed_recalc_rate, }; + +/* Tegra30 CPU clock and reset control functions */ +static void tegra30_wait_cpu_in_reset(u32 cpu) +{ + unsigned int reg; + + do { + reg = readl(reg_clk_base + + TEGRA30_CLK_RST_CONTROLLER_CPU_CMPLX_STATUS); + cpu_relax(); + } while (!(reg & (1 << cpu))); /* check CPU been reset or not */ + + return; +} + +static void tegra30_put_cpu_in_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + reg_clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + dmb(); +} + +static void tegra30_cpu_out_of_reset(u32 cpu) +{ + writel(CPU_RESET(cpu), + reg_clk_base + TEGRA_CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR); + wmb(); +} + +static void tegra30_enable_cpu_clock(u32 cpu) +{ + unsigned int reg; + + writel(CPU_CLOCK(cpu), + reg_clk_base + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); + reg = readl(reg_clk_base + + TEGRA30_CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR); +} + +static void tegra30_disable_cpu_clock(u32 cpu) +{ + + unsigned int reg; + + reg = readl(reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); + writel(reg | CPU_CLOCK(cpu), + reg_clk_base + TEGRA_CLK_RST_CONTROLLER_CLK_CPU_CMPLX); +} + +static struct tegra_cpu_car_ops tegra30_cpu_car_ops = { + .wait_for_reset = tegra30_wait_cpu_in_reset, + .put_in_reset = tegra30_put_cpu_in_reset, + .out_of_reset = tegra30_cpu_out_of_reset, + .enable_clock = tegra30_enable_cpu_clock, + .disable_clock = tegra30_disable_cpu_clock, +}; + +void __init tegra30_cpu_car_ops_init(void) +{ + tegra_cpu_car_ops = &tegra30_cpu_car_ops; +} diff --git a/arch/arm/mach-tegra/tegra30_clocks_data.c b/arch/arm/mach-tegra/tegra30_clocks_data.c index 34b61a4934a3..c10449603df0 100644 --- a/arch/arm/mach-tegra/tegra30_clocks_data.c +++ b/arch/arm/mach-tegra/tegra30_clocks_data.c @@ -32,6 +32,7 @@ #include "clock.h" #include "fuse.h" #include "tegra30_clocks.h" +#include "tegra_cpu_car.h" #define DEFINE_CLK_TEGRA(_name, _rate, _ops, _flags, \ _parent_names, _parents, _parent) \ @@ -1366,4 +1367,6 @@ void __init tegra30_init_clocks(void) for (i = 0; i < ARRAY_SIZE(tegra_clk_out_list); i++) tegra30_init_one_clock(tegra_clk_out_list[i]); + + tegra30_cpu_car_ops_init(); } diff --git a/arch/arm/mach-tegra/tegra_cpu_car.h b/arch/arm/mach-tegra/tegra_cpu_car.h new file mode 100644 index 000000000000..30d063ad2bef --- /dev/null +++ b/arch/arm/mach-tegra/tegra_cpu_car.h @@ -0,0 +1,87 @@ +/* + * 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_CPU_CAR_H +#define __MACH_TEGRA_CPU_CAR_H + +/* + * Tegra CPU clock and reset control ops + * + * wait_for_reset: + * keep waiting until the CPU in reset state + * put_in_reset: + * put the CPU in reset state + * out_of_reset: + * release the CPU from reset state + * enable_clock: + * CPU clock un-gate + * disable_clock: + * CPU clock gate + */ +struct tegra_cpu_car_ops { + void (*wait_for_reset)(u32 cpu); + void (*put_in_reset)(u32 cpu); + void (*out_of_reset)(u32 cpu); + void (*enable_clock)(u32 cpu); + void (*disable_clock)(u32 cpu); +}; + +extern struct tegra_cpu_car_ops *tegra_cpu_car_ops; + +static inline void tegra_wait_cpu_in_reset(u32 cpu) +{ + if (WARN_ON(!tegra_cpu_car_ops->wait_for_reset)) + return; + + tegra_cpu_car_ops->wait_for_reset(cpu); +} + +static inline void tegra_put_cpu_in_reset(u32 cpu) +{ + if (WARN_ON(!tegra_cpu_car_ops->put_in_reset)) + return; + + tegra_cpu_car_ops->put_in_reset(cpu); +} + +static inline void tegra_cpu_out_of_reset(u32 cpu) +{ + if (WARN_ON(!tegra_cpu_car_ops->out_of_reset)) + return; + + tegra_cpu_car_ops->out_of_reset(cpu); +} + +static inline void tegra_enable_cpu_clock(u32 cpu) +{ + if (WARN_ON(!tegra_cpu_car_ops->enable_clock)) + return; + + tegra_cpu_car_ops->enable_clock(cpu); +} + +static inline void tegra_disable_cpu_clock(u32 cpu) +{ + if (WARN_ON(!tegra_cpu_car_ops->disable_clock)) + return; + + tegra_cpu_car_ops->disable_clock(cpu); +} + +void tegra20_cpu_car_ops_init(void); +void tegra30_cpu_car_ops_init(void); + +#endif /* __MACH_TEGRA_CPU_CAR_H */ |