ARM: tegra: implement CPU hotplug

This branch implements CPU hot-plugging support for both Tegra20 and
 Tegra30. Portions of the implementation are contained in the clock
 driver, hence this branch is based on the common clock conversion in
 order to avoid duplicating work.
 
 This branch is based on previous pull request tegra-for-3.7-common-clk.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.10 (GNU/Linux)
 
 iQIcBAABAgAGBQJQU4DzAAoJEMzrak5tbycxwvIP/1nINaoG27CRz/SwsbhDw4N2
 1ESEbEAynnAsdmZCpmIpvK3Xr/aE8hBfKc0EUgi7intThjFcLwzFnH7yEKzdxdJM
 ZgY7JawMML7J0yXrLuJaISyHn9Kbp1A0B8bJo8cld56h8rd99vC6OZxPGvVuaY8y
 I28nXhvkiiHS1h7ilT/bf4aa7Z3u4xiS46hsiCUu1fWCM//JtpMDChCUKIshaGxU
 G5j/o/oivGShjamQRcbBCnwo5LdMUiaAXotszVNnJC/17p1FMWViMGjH6fmSN1KJ
 G8+jX70mJ3PCRH4ivE+KZBpddG/anNviXc6cVYZrW+/nkJwvD61OEKs3p7siLyuF
 igJeXGwXHZwTBDm5rYqbguR0c8VD1TriAibk72lz/jTHWLy0gvqnZXPerp2cbC37
 4BHfKFczeB2gJiHIKnmlawYIk2ckAd+gahI19X/JGcbhiXDQSJWUIK9SCKQBtxZk
 eD1nOpH/nvPkh414xkLz44UheYk8u+6blYZ5St4wiJM+35ngbU1Fh1oOziH7PZG6
 EslqoAKxJ79igUGNc9mcvKFlNZSU7ItlXMDWTlW0GWI8jubsxIrfeNr5bjBLGL3t
 1LHSO1FDnr87ha8jGXlZtSyNvcY8cLVILjMcZNUha0wE9hllm5naBDnaDrFlC0CQ
 vFvNodvbqZns96ZK05yw
 =kTup
 -----END PGP SIGNATURE-----

Merge tag 'tegra-for-3.7-cpu-hotplug' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/soc

From Stephen Warren:

ARM: tegra: implement CPU hotplug

This branch implements CPU hot-plugging support for both Tegra20 and
Tegra30. Portions of the implementation are contained in the clock
driver, hence this branch is based on the common clock conversion in
order to avoid duplicating work.

By Joseph Lo
via Stephen Warren
* tag 'tegra-for-3.7-cpu-hotplug' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra:
  ARM: tegra20: add CPU hotplug support
  ARM: tegra30: add CPU hotplug support
  ARM: tegra: clean up the common assembly macros into sleep.h
  ARM: tegra: replace the CPU CAR access code by tegra_cpu_car_ops
  ARM: tegra: introduce tegra_cpu_car_ops structures
This commit is contained in:
Olof Johansson 2012-09-16 18:33:23 -07:00
commit cccc277ba8
15 changed files with 556 additions and 147 deletions

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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;
#include "sleep.h"
#include "tegra_cpu_car.h"
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");
/*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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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 */