Skip to content

Commit

Permalink
[PATCH] powerpc: create a new arch/powerpc/platforms/cell/smp.c
Browse files Browse the repository at this point in the history
During the conversion to the merge tree, the Cell specific
SMP initialization was removed from the pSeries code.

This creates a new Cell specific SMP implementation file.

Signed-off-by: Arnd Bergmann <arndb@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
Arnd Bergmann authored and Paul Mackerras committed Nov 1, 2005
1 parent f3f66f5 commit 19fe047
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 0 deletions.
1 change: 1 addition & 0 deletions arch/powerpc/platforms/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ obj-$(CONFIG_85xx) += 85xx/
obj-$(CONFIG_PPC_PSERIES) += pseries/
obj-$(CONFIG_PPC_ISERIES) += iseries/
obj-$(CONFIG_PPC_MAPLE) += maple/
obj-$(CONFIG_PPC_CELL) += cell/
1 change: 1 addition & 0 deletions arch/powerpc/platforms/cell/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
obj-$(CONFIG_SMP) += smp.o
230 changes: 230 additions & 0 deletions arch/powerpc/platforms/cell/smp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* SMP support for BPA machines.
*
* Dave Engebretsen, Peter Bergner, and
* Mike Corrigan {engebret|bergner|mikec}@us.ibm.com
*
* Plus various changes from other IBM teams...
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/

#undef DEBUG

#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/cache.h>
#include <linux/err.h>
#include <linux/sysdev.h>
#include <linux/cpu.h>

#include <asm/ptrace.h>
#include <asm/atomic.h>
#include <asm/irq.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/smp.h>
#include <asm/paca.h>
#include <asm/time.h>
#include <asm/machdep.h>
#include <asm/cputable.h>
#include <asm/firmware.h>
#include <asm/system.h>
#include <asm/rtas.h>

#include "interrupt.h"

#ifdef DEBUG
#define DBG(fmt...) udbg_printf(fmt)
#else
#define DBG(fmt...)
#endif

/*
* The primary thread of each non-boot processor is recorded here before
* smp init.
*/
static cpumask_t of_spin_map;

extern void pSeries_secondary_smp_init(unsigned long);

/**
* smp_startup_cpu() - start the given cpu
*
* At boot time, there is nothing to do for primary threads which were
* started from Open Firmware. For anything else, call RTAS with the
* appropriate start location.
*
* Returns:
* 0 - failure
* 1 - success
*/
static inline int __devinit smp_startup_cpu(unsigned int lcpu)
{
int status;
unsigned long start_here = __pa((u32)*((unsigned long *)
pSeries_secondary_smp_init));
unsigned int pcpu;
int start_cpu;

if (cpu_isset(lcpu, of_spin_map))
/* Already started by OF and sitting in spin loop */
return 1;

pcpu = get_hard_smp_processor_id(lcpu);

/* Fixup atomic count: it exited inside IRQ handler. */
paca[lcpu].__current->thread_info->preempt_count = 0;

/*
* If the RTAS start-cpu token does not exist then presume the
* cpu is already spinning.
*/
start_cpu = rtas_token("start-cpu");
if (start_cpu == RTAS_UNKNOWN_SERVICE)
return 1;

status = rtas_call(start_cpu, 3, 1, NULL, pcpu, start_here, lcpu);
if (status != 0) {
printk(KERN_ERR "start-cpu failed: %i\n", status);
return 0;
}

return 1;
}

static void smp_iic_message_pass(int target, int msg)
{
unsigned int i;

if (target < NR_CPUS) {
iic_cause_IPI(target, msg);
} else {
for_each_online_cpu(i) {
if (target == MSG_ALL_BUT_SELF
&& i == smp_processor_id())
continue;
iic_cause_IPI(i, msg);
}
}
}

static int __init smp_iic_probe(void)
{
iic_request_IPIs();

return cpus_weight(cpu_possible_map);
}

static void __devinit smp_iic_setup_cpu(int cpu)
{
if (cpu != boot_cpuid)
iic_setup_cpu();
}

static DEFINE_SPINLOCK(timebase_lock);
static unsigned long timebase = 0;

static void __devinit cell_give_timebase(void)
{
spin_lock(&timebase_lock);
rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL);
timebase = get_tb();
spin_unlock(&timebase_lock);

while (timebase)
barrier();
rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL);
}

static void __devinit cell_take_timebase(void)
{
while (!timebase)
barrier();
spin_lock(&timebase_lock);
set_tb(timebase >> 32, timebase & 0xffffffff);
timebase = 0;
spin_unlock(&timebase_lock);
}

static void __devinit smp_cell_kick_cpu(int nr)
{
BUG_ON(nr < 0 || nr >= NR_CPUS);

if (!smp_startup_cpu(nr))
return;

/*
* The processor is currently spinning, waiting for the
* cpu_start field to become non-zero After we set cpu_start,
* the processor will continue on to secondary_start
*/
paca[nr].cpu_start = 1;
}

static int smp_cell_cpu_bootable(unsigned int nr)
{
/* Special case - we inhibit secondary thread startup
* during boot if the user requests it. Odd-numbered
* cpus are assumed to be secondary threads.
*/
if (system_state < SYSTEM_RUNNING &&
cpu_has_feature(CPU_FTR_SMT) &&
!smt_enabled_at_boot && nr % 2 != 0)
return 0;

return 1;
}
static struct smp_ops_t bpa_iic_smp_ops = {
.message_pass = smp_iic_message_pass,
.probe = smp_iic_probe,
.kick_cpu = smp_cell_kick_cpu,
.setup_cpu = smp_iic_setup_cpu,
.cpu_bootable = smp_cell_cpu_bootable,
};

/* This is called very early */
void __init smp_init_cell(void)
{
int i;

DBG(" -> smp_init_cell()\n");

smp_ops = &bpa_iic_smp_ops;

/* Mark threads which are still spinning in hold loops. */
if (cpu_has_feature(CPU_FTR_SMT)) {
for_each_present_cpu(i) {
if (i % 2 == 0)
/*
* Even-numbered logical cpus correspond to
* primary threads.
*/
cpu_set(i, of_spin_map);
}
} else {
of_spin_map = cpu_present_map;
}

cpu_clear(boot_cpuid, of_spin_map);

/* Non-lpar has additional take/give timebase */
if (rtas_token("freeze-time-base") != RTAS_UNKNOWN_SERVICE) {
smp_ops->give_timebase = cell_give_timebase;
smp_ops->take_timebase = cell_take_timebase;
}

DBG(" <- smp_init_cell()\n");
}
1 change: 1 addition & 0 deletions include/asm-ppc64/smp.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ extern cpumask_t cpu_sibling_map[NR_CPUS];

void smp_init_iSeries(void);
void smp_init_pSeries(void);
void smp_init_cell(void);

extern int __cpu_disable(void);
extern void __cpu_die(unsigned int cpu);
Expand Down

0 comments on commit 19fe047

Please sign in to comment.