Commit ea4423c3 authored by Russell King's avatar Russell King

Merge with ../linux-2.6-smp

parents 36c5ed23 fe6ef2da
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
* linux/arch/arm/kernel/head.S * linux/arch/arm/kernel/head.S
* *
* Copyright (C) 1994-2002 Russell King * Copyright (C) 1994-2002 Russell King
* Copyright (c) 2003 ARM Limited
* All Rights Reserved
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -165,6 +167,48 @@ __mmap_switched: ...@@ -165,6 +167,48 @@ __mmap_switched:
stmia r6, {r0, r4} @ Save control register values stmia r6, {r0, r4} @ Save control register values
b start_kernel b start_kernel
#if defined(CONFIG_SMP)
.type secondary_startup, #function
ENTRY(secondary_startup)
/*
* Common entry point for secondary CPUs.
*
* Ensure that we're in SVC mode, and IRQs are disabled. Lookup
* the processor type - there is no need to check the machine type
* as it has already been validated by the primary processor.
*/
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC
bl __lookup_processor_type
movs r10, r5 @ invalid processor?
moveq r0, #'p' @ yes, error 'p'
beq __error
/*
* Use the page tables supplied from __cpu_up.
*/
adr r4, __secondary_data
ldmia r4, {r5, r6, r13} @ address to jump to after
sub r4, r4, r5 @ mmu has been enabled
ldr r4, [r6, r4] @ get secondary_data.pgdir
adr lr, __enable_mmu @ return address
add pc, r10, #12 @ initialise processor
@ (return control reg)
/*
* r6 = &secondary_data
*/
ENTRY(__secondary_switched)
ldr sp, [r6, #4] @ get secondary_data.stack
mov fp, #0
b secondary_start_kernel
.type __secondary_data, %object
__secondary_data:
.long .
.long secondary_data
.long __secondary_switched
#endif /* defined(CONFIG_SMP) */
/* /*
......
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
#include <asm/atomic.h> #include <asm/atomic.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
...@@ -36,6 +39,13 @@ ...@@ -36,6 +39,13 @@
cpumask_t cpu_present_mask; cpumask_t cpu_present_mask;
cpumask_t cpu_online_map; cpumask_t cpu_online_map;
/*
* as from 2.5, kernels no longer have an init_tasks structure
* so we need some other way of telling a new secondary core
* where to place its SVC stack
*/
struct secondary_data secondary_data;
/* /*
* structures for inter-processor calls * structures for inter-processor calls
* - A collection of single bit ipi messages. * - A collection of single bit ipi messages.
...@@ -71,6 +81,8 @@ static DEFINE_SPINLOCK(smp_call_function_lock); ...@@ -71,6 +81,8 @@ static DEFINE_SPINLOCK(smp_call_function_lock);
int __init __cpu_up(unsigned int cpu) int __init __cpu_up(unsigned int cpu)
{ {
struct task_struct *idle; struct task_struct *idle;
pgd_t *pgd;
pmd_t *pmd;
int ret; int ret;
/* /*
...@@ -83,10 +95,55 @@ int __init __cpu_up(unsigned int cpu) ...@@ -83,10 +95,55 @@ int __init __cpu_up(unsigned int cpu)
return PTR_ERR(idle); return PTR_ERR(idle);
} }
/*
* Allocate initial page tables to allow the new CPU to
* enable the MMU safely. This essentially means a set
* of our "standard" page tables, with the addition of
* a 1:1 mapping for the physical address of the kernel.
*/
pgd = pgd_alloc(&init_mm);
pmd = pmd_offset(pgd, PHYS_OFFSET);
*pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) |
PMD_TYPE_SECT | PMD_SECT_AP_WRITE);
/*
* We need to tell the secondary core where to find
* its stack and the page tables.
*/
secondary_data.stack = (void *)idle->thread_info + THREAD_SIZE - 8;
secondary_data.pgdir = virt_to_phys(pgd);
wmb();
/* /*
* Now bring the CPU into our world. * Now bring the CPU into our world.
*/ */
ret = boot_secondary(cpu, idle); ret = boot_secondary(cpu, idle);
if (ret == 0) {
unsigned long timeout;
/*
* CPU was successfully started, wait for it
* to come online or time out.
*/
timeout = jiffies + HZ;
while (time_before(jiffies, timeout)) {
if (cpu_online(cpu))
break;
udelay(10);
barrier();
}
if (!cpu_online(cpu))
ret = -EIO;
}
secondary_data.stack = 0;
secondary_data.pgdir = 0;
*pmd_offset(pgd, PHYS_OFFSET) = __pmd(0);
pgd_free(pgd);
if (ret) { if (ret) {
printk(KERN_CRIT "cpu_up: processor %d failed to boot\n", cpu); printk(KERN_CRIT "cpu_up: processor %d failed to boot\n", cpu);
/* /*
...@@ -97,6 +154,56 @@ int __init __cpu_up(unsigned int cpu) ...@@ -97,6 +154,56 @@ int __init __cpu_up(unsigned int cpu)
return ret; return ret;
} }
/*
* This is the secondary CPU boot entry. We're using this CPUs
* idle thread stack, but a set of temporary page tables.
*/
asmlinkage void __init secondary_start_kernel(void)
{
struct mm_struct *mm = &init_mm;
unsigned int cpu = smp_processor_id();
printk("CPU%u: Booted secondary processor\n", cpu);
/*
* All kernel threads share the same mm context; grab a
* reference and switch to it.
*/
atomic_inc(&mm->mm_users);
atomic_inc(&mm->mm_count);
current->active_mm = mm;
cpu_set(cpu, mm->cpu_vm_mask);
cpu_switch_mm(mm->pgd, mm);
enter_lazy_tlb(mm, current);
cpu_init();
/*
* Give the platform a chance to do its own initialisation.
*/
platform_secondary_init(cpu);
/*
* Enable local interrupts.
*/
local_irq_enable();
local_fiq_enable();
calibrate_delay();
smp_store_cpu_info(cpu);
/*
* OK, now it's safe to let the boot CPU continue
*/
cpu_set(cpu, cpu_online_map);
/*
* OK, it's off to the idle thread for us
*/
cpu_idle();
}
/* /*
* Called by both boot and secondaries to move global data into * Called by both boot and secondaries to move global data into
* per-processor storage. * per-processor storage.
......
...@@ -12,3 +12,4 @@ obj-$(CONFIG_LEDS) += leds.o ...@@ -12,3 +12,4 @@ obj-$(CONFIG_LEDS) += leds.o
obj-$(CONFIG_PCI) += pci_v3.o pci.o obj-$(CONFIG_PCI) += pci_v3.o pci.o
obj-$(CONFIG_CPU_FREQ_INTEGRATOR) += cpu.o obj-$(CONFIG_CPU_FREQ_INTEGRATOR) += cpu.o
obj-$(CONFIG_INTEGRATOR_IMPD1) += impd1.o obj-$(CONFIG_INTEGRATOR_IMPD1) += impd1.o
obj-$(CONFIG_SMP) += platsmp.o headsmp.o
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/smp.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -221,7 +222,24 @@ integrator_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -221,7 +222,24 @@ integrator_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
*/ */
timer1->TimerClear = 1; timer1->TimerClear = 1;
/*
* the clock tick routines are only processed on the
* primary CPU
*/
if (hard_smp_processor_id() == 0) {
nmi_tick();
timer_tick(regs); timer_tick(regs);
#ifdef CONFIG_SMP
smp_send_timer();
#endif
}
#ifdef CONFIG_SMP
/*
* this is the ARM equivalent of the APIC timer interrupt
*/
update_process_times(user_mode(regs));
#endif /* CONFIG_SMP */
write_sequnlock(&xtime_lock); write_sequnlock(&xtime_lock);
......
/*
* linux/arch/arm/mach-integrator/headsmp.S
*
* Copyright (c) 2003 ARM Limited
* 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/linkage.h>
#include <linux/init.h>
__INIT
/*
* Integrator specific entry point for secondary CPUs. This provides
* a "holding pen" into which all secondary cores are held until we're
* ready for them to initialise.
*/
ENTRY(integrator_secondary_startup)
adr r4, 1f
ldmia r4, {r5, r6}
sub r4, r4, r5
ldr r6, [r6, r4]
pen: ldr r7, [r6]
cmp r7, r0
bne pen
/*
* we've been released from the holding pen: secondary_stack
* should now contain the SVC stack for this core
*/
b secondary_startup
1: .long .
.long phys_pen_release
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -85,4 +87,4 @@ static int __init leds_init(void) ...@@ -85,4 +87,4 @@ static int __init leds_init(void)
return 0; return 0;
} }
__initcall(leds_init); core_initcall(leds_init);
/*
* linux/arch/arm/mach-cintegrator/platsmp.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/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <asm/atomic.h>
#include <asm/delay.h>
#include <asm/mmu_context.h>
#include <asm/procinfo.h>
#include <asm/ptrace.h>
#include <asm/smp.h>
extern void integrator_secondary_startup(void);
/*
* control for which core is the next to come out of the secondary
* boot "holding pen"
*/
volatile int __initdata pen_release = -1;
unsigned long __initdata phys_pen_release = 0;
static DEFINE_SPINLOCK(boot_lock);
void __init platform_secondary_init(unsigned int cpu)
{
/*
* the primary core may have used a "cross call" soft interrupt
* to get this processor out of WFI in the BootMonitor - make
* sure that we are no longer being sent this soft interrupt
*/
smp_cross_call_done(cpumask_of_cpu(cpu));
/*
* if any interrupts are already enabled for the primary
* core (e.g. timer irq), then they will not have been enabled
* for us: do so
*/
secondary_scan_irqs();
/*
* let the primary processor know we're out of the
* pen, then head off into the C entry point
*/
pen_release = -1;
/*
* Synchronise with the boot thread.
*/
spin_lock(&boot_lock);
spin_unlock(&boot_lock);
}
int __init boot_secondary(unsigned int cpu, struct task_struct *idle)
{
unsigned long timeout;
/*
* set synchronisation state between this boot processor
* and the secondary one
*/
spin_lock(&boot_lock);
/*
* The secondary processor is waiting to be released from
* the holding pen - release it, then wait for it to flag
* that it has been released by resetting pen_release.
*
* Note that "pen_release" is the hardware CPU ID, whereas
* "cpu" is Linux's internal ID.
*/
pen_release = cpu;
/*
* XXX
*
* This is a later addition to the booting protocol: the
* bootMonitor now puts secondary cores into WFI, so
* poke_milo() no longer gets the cores moving; we need
* to send a soft interrupt to wake the secondary core.
* Use smp_cross_call() for this, since there's little
* point duplicating the code here
*/
smp_cross_call(cpumask_of_cpu(cpu));
timeout = jiffies + (1 * HZ);
while (time_before(jiffies, timeout)) {
if (pen_release == -1)
break;
udelay(10);
}
/*
* now the secondary core is starting up let it run its
* calibrations, then wait for it to finish
*/
spin_unlock(&boot_lock);
return pen_release != -1 ? -ENOSYS : 0;
}
static void __init poke_milo(void)
{
extern void secondary_startup(void);
/* nobody is to be released from the pen yet */
pen_release = -1;
phys_pen_release = virt_to_phys(&pen_release);
/*
* write the address of secondary startup into the system-wide
* flags register, then clear the bottom two bits, which is what
* BootMonitor is waiting for
*/
#if 1
#define CINTEGRATOR_HDR_FLAGSS_OFFSET 0x30
__raw_writel(virt_to_phys(integrator_secondary_startup),
(IO_ADDRESS(INTEGRATOR_HDR_BASE) +
CINTEGRATOR_HDR_FLAGSS_OFFSET));
#define CINTEGRATOR_HDR_FLAGSC_OFFSET 0x34
__raw_writel(3,
(IO_ADDRESS(INTEGRATOR_HDR_BASE) +
CINTEGRATOR_HDR_FLAGSC_OFFSET));
#endif
mb();
}
void __init smp_prepare_cpus(unsigned int max_cpus)
{
unsigned int ncores = get_core_count();
unsigned int cpu = smp_processor_id();
int i;
/* sanity check */
if (ncores == 0) {
printk(KERN_ERR
"Integrator/CP: strange CM count of 0? Default to 1\n");
ncores = 1;
}
if (ncores > NR_CPUS) {
printk(KERN_WARNING
"Integrator/CP: no. of cores (%d) greater than configured "
"maximum of %d - clipping\n",
ncores, NR_CPUS);
ncores = NR_CPUS;
}
/*
* start with some more config for the Boot CPU, now that
* the world is a bit more alive (which was not the case
* when smp_prepare_boot_cpu() was called)
*/
smp_store_cpu_info(cpu);
/*
* are we trying to boot more cores than exist?
*/
if (max_cpus > ncores)
max_cpus = ncores;
/*
* Initialise the present mask - this tells us which CPUs should
* be present.
*/
for (i = 0; i < max_cpus; i++) {
cpu_set(i, cpu_present_mask);
}
/*
* Do we need any more CPUs? If so, then let them know where
* to start. Note that, on modern versions of MILO, the "poke"
* doesn't actually do anything until each individual core is
* sent a soft interrupt to get it out of WFI
*/
if (max_cpus > 1)
poke_milo();
}
#ifndef ASMARM_ARCH_SMP_H
#define ASMARM_ARCH_SMP_H
#include <linux/config.h>
#include <asm/arch/hardware.h>
#include <asm/io.h>
#define hard_smp_processor_id() \
({ \
unsigned int cpunum; \
__asm__("mrc p15, 0, %0, c0, c0, 5" \
: "=r" (cpunum)); \
cpunum &= 0x0F; \
})
extern void secondary_scan_irqs(void);
#endif
...@@ -55,4 +55,18 @@ extern void smp_cross_call(cpumask_t callmap); ...@@ -55,4 +55,18 @@ extern void smp_cross_call(cpumask_t callmap);
*/ */
extern int boot_secondary(unsigned int cpu, struct task_struct *); extern int boot_secondary(unsigned int cpu, struct task_struct *);
/*
* Perform platform specific initialisation of the specified CPU.
*/
extern void platform_secondary_init(unsigned int cpu);
/*
* Initial data for bringing up a secondary CPU.
*/
struct secondary_data {
unsigned long pgdir;
void *stack;
};
extern struct secondary_data secondary_data;
#endif /* ifndef __ASM_ARM_SMP_H */ #endif /* ifndef __ASM_ARM_SMP_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment