Skip to content

Commit

Permalink
Merge tag 'tegra-for-3.7-cpu-hotplug' of git://git.kernel.org/pub/scm…
Browse files Browse the repository at this point in the history
…/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
  • Loading branch information
Olof Johansson committed Sep 17, 2012
2 parents ea2abb6 + 453689e commit cccc277
Show file tree
Hide file tree
Showing 15 changed files with 556 additions and 147 deletions.
2 changes: 2 additions & 0 deletions arch/arm/mach-tegra/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions arch/arm/mach-tegra/clock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions arch/arm/mach-tegra/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "fuse.h"
#include "pmc.h"
#include "apbio.h"
#include "sleep.h"

/*
* Storage for debug-macro.S's state.
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
6 changes: 1 addition & 5 deletions arch/arm/mach-tegra/headsmp.S
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
118 changes: 32 additions & 86 deletions arch/arm/mach-tegra/hotplug.c
Original file line number Diff line number Diff line change
@@ -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)
{
Expand All @@ -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)
Expand All @@ -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
29 changes: 5 additions & 24 deletions arch/arm/mach-tegra/platsmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,14 @@
#include "fuse.h"
#include "flowctrl.h"
#include "reset.h"
#include "tegra_cpu_car.h"

extern void tegra_secondary_startup(void);

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)
{
Expand All @@ -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);
Expand All @@ -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;

Expand All @@ -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. */
Expand All @@ -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
Expand All @@ -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;
}
Expand Down
82 changes: 82 additions & 0 deletions arch/arm/mach-tegra/sleep-t20.S
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit cccc277

Please sign in to comment.