Skip to content

Commit

Permalink
[ARM] tegra: SMP support
Browse files Browse the repository at this point in the history
Signed-off-by: Colin Cross <ccross@android.com>
Signed-off-by: Erik Gilling <konkers@android.com>
  • Loading branch information
Colin Cross authored and Erik Gilling committed Aug 5, 2010
1 parent d861196 commit 1cea732
Show file tree
Hide file tree
Showing 7 changed files with 420 additions and 4 deletions.
10 changes: 6 additions & 4 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1112,10 +1112,11 @@ config SMP
bool "Symmetric Multi-Processing (EXPERIMENTAL)"
depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || REALVIEW_EB_A9MP ||\
MACH_REALVIEW_PB11MP || MACH_REALVIEW_PBX || ARCH_OMAP4 ||\
ARCH_U8500 || ARCH_VEXPRESS_CA9X4)
ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
depends on GENERIC_CLOCKEVENTS
select USE_GENERIC_SMP_HELPERS
select HAVE_ARM_SCU if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500 || ARCH_VEXPRESS_CA9X4)
select HAVE_ARM_SCU if (ARCH_REALVIEW || ARCH_OMAP4 || ARCH_U8500 || \
ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
help
This enables support for systems with more than one CPU. If you have
a system with only one CPU, like most personal computers, say N. If
Expand Down Expand Up @@ -1185,9 +1186,10 @@ config LOCAL_TIMERS
bool "Use local timer interrupts"
depends on SMP && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || \
REALVIEW_EB_A9MP || MACH_REALVIEW_PBX || ARCH_OMAP4 || \
ARCH_U8500 || ARCH_VEXPRESS_CA9X4)
ARCH_U8500 || ARCH_VEXPRESS_CA9X4 || ARCH_TEGRA)
default y
select HAVE_ARM_TWD if (ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_OMAP4 || ARCH_U8500)
select HAVE_ARM_TWD if (ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_OMAP4 || \\
ARCH_U8500 || ARCH_TEGRA
help
Enable support for local timers on SMP platforms, rather then the
legacy IPI broadcast method. Local timers allows the system
Expand Down
2 changes: 2 additions & 0 deletions arch/arm/mach-tegra/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ obj-y += io.o
obj-y += irq.o
obj-y += clock.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o
obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
61 changes: 61 additions & 0 deletions arch/arm/mach-tegra/headsmp.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include <linux/linkage.h>
#include <linux/init.h>

.section ".text.head", "ax"
__CPUINIT

/*
* Tegra specific entry point for secondary CPUs.
* The secondary kernel init calls v7_flush_dcache_all before it enables
* the L1; however, the L1 comes out of reset in an undefined state, so
* the clean + invalidate performed by v7_flush_dcache_all causes a bunch
* of cache lines with uninitialized data and uninitialized tags to get
* written out to memory, which does really unpleasant things to the main
* processor. We fix this by performing an invalidate, rather than a
* clean + invalidate, before jumping into the kernel.
*/
ENTRY(v7_invalidate_l1)
mov r0, #0
mcr p15, 2, r0, c0, c0, 0
mrc p15, 1, r0, c0, c0, 0

ldr r1, =0x7fff
and r2, r1, r0, lsr #13

ldr r1, =0x3ff

and r3, r1, r0, lsr #3 @ NumWays - 1
add r2, r2, #1 @ NumSets

and r0, r0, #0x7
add r0, r0, #4 @ SetShift

clz r1, r3 @ WayShift
add r4, r3, #1 @ NumWays
1: sub r2, r2, #1 @ NumSets--
mov r3, r4 @ Temp = NumWays
2: subs r3, r3, #1 @ Temp--
mov r5, r3, lsl r1
mov r6, r2, lsl r0
orr r5, r5, r6 @ Reg = (Temp<<WayShift)|(NumSets<<SetShift)
mcr p15, 0, r5, c7, c6, 2
bgt 2b
cmp r2, #0
bgt 1b
dsb
isb
mov pc, lr
ENDPROC(v7_invalidate_l1)

ENTRY(tegra_secondary_startup)
msr cpsr_fsxc, #0xd3
bl v7_invalidate_l1
mrc p15, 0, r0, c0, c0, 5
and r0, r0, #15
ldr r1, =0x6000f100
str r0, [r1]
1: ldr r2, [r1]
cmp r0, r2
beq 1b
b secondary_startup
ENDPROC(tegra_secondary_startup)
140 changes: 140 additions & 0 deletions arch/arm/mach-tegra/hotplug.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* linux/arch/arm/mach-realview/hotplug.c
*
* Copyright (C) 2002 ARM Ltd.
* 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 <linux/completion.h>

#include <asm/cacheflush.h>

static DECLARE_COMPLETION(cpu_killed);

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, #0x04\n"
" mcr p15, 0, %0, c1, c0, 0\n"
: "=&r" (v)
: "r" (0)
: "cc");
}

static inline void cpu_leave_lowpower(void)
{
unsigned int v;

asm volatile(
"mrc p15, 0, %0, c1, c0, 0\n"
" orr %0, %0, #0x04\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)
:
: "cc");
}

static inline void platform_do_lowpower(unsigned int cpu)
{
/*
* 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
*
* The trouble is, letting people know about this is not really
* possible, since we are currently running incoherently, and
* therefore cannot safely call printk() or anything else
*/
#ifdef DEBUG
printk(KERN_WARN "CPU%u: spurious wakeup call\n", cpu);
#endif
}
}

int platform_cpu_kill(unsigned int cpu)
{
return wait_for_completion_timeout(&cpu_killed, 5000);
}

/*
* platform-specific code to shutdown a CPU
*
* Called with IRQs disabled
*/
void platform_cpu_die(unsigned int cpu)
{
#ifdef DEBUG
unsigned int this_cpu = hard_smp_processor_id();

if (cpu != this_cpu) {
printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
this_cpu, cpu);
BUG();
}
#endif

printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
complete(&cpu_killed);

/*
* we're ready for shutdown now, so do it
*/
cpu_enter_lowpower();
platform_do_lowpower(cpu);

/*
* bring this CPU back into the world of cache
* coherency, and then restore interrupts
*/
cpu_leave_lowpower();
}

int platform_cpu_disable(unsigned int cpu)
{
/*
* we don't allow CPU 0 to be shutdown (it is still too special
* e.g. clock tick interrupts)
*/
return cpu == 0 ? -EPERM : 0;
}
30 changes: 30 additions & 0 deletions arch/arm/mach-tegra/include/mach/smp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef ASMARM_ARCH_SMP_H
#define ASMARM_ARCH_SMP_H


#include <asm/hardware/gic.h>

#define hard_smp_processor_id() \
({ \
unsigned int cpunum; \
__asm__("mrc p15, 0, %0, c0, c0, 5" \
: "=r" (cpunum)); \
cpunum &= 0x0F; \
})

/*
* We use IRQ1 as the IPI
*/
static inline void smp_cross_call(const struct cpumask *mask)
{
gic_raise_softirq(mask, 1);
}

/*
* Do nothing on MPcore.
*/
static inline void smp_cross_call_done(cpumask_t callmap)
{
}

#endif
25 changes: 25 additions & 0 deletions arch/arm/mach-tegra/localtimer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* arch/arm/mach-tegra/localtimer.c
*
* Copyright (C) 2002 ARM Ltd.
* 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/init.h>
#include <linux/smp.h>
#include <linux/clockchips.h>
#include <asm/irq.h>
#include <asm/smp_twd.h>
#include <asm/localtimer.h>

/*
* Setup the local clock events for a CPU.
*/
void __cpuinit local_timer_setup(struct clock_event_device *evt)
{
evt->irq = IRQ_LOCALTIMER;
twd_timer_setup(evt);
}
Loading

0 comments on commit 1cea732

Please sign in to comment.