/*
* linux/arch/alpha/kernel/smp.c
*
* 2001-07-09 Phil Ezolt (Phillip.Ezolt@compaq.com)
* Renamed modified smp_call_function to smp_call_function_on_cpu()
* Created an function that conforms to the old calling convention
* of smp_call_function().
*
* This is helpful for DCPI.
*
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/kernel_stat.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/threads.h>
#include <linux/smp.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/cache.h>
#include <linux/profile.h>
#include <linux/bitops.h>
#include <asm/hwrpb.h>
#include <asm/ptrace.h>
#include <asm/atomic.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/mmu_context.h>
#include <asm/tlbflush.h>
#include "proto.h"
#include "irq_impl.h"
#define DEBUG_SMP 0
#if DEBUG_SMP
#define DBGS(args) printk args
#else
#define DBGS(args)
#endif
/* A collection of per-processor data. */
struct cpuinfo_alpha cpu_data[NR_CPUS];
EXPORT_SYMBOL(cpu_data);
/* A collection of single bit ipi messages. */
static struct {
unsigned long bits ____cacheline_aligned;
} ipi_data[NR_CPUS] __cacheline_aligned;
enum ipi_message_type {
IPI_RESCHEDULE,
IPI_CALL_FUNC,
IPI_CPU_STOP,
};
/* Set to a secondary's cpuid when it comes online. */
static int smp_secondary_alive __devinitdata = 0;
/* Which cpus ids came online. */
cpumask_t cpu_online_map;
EXPORT_SYMBOL(cpu_online_map);
int smp_num_probed; /* Internal processor count */
int smp_num_cpus = 1; /* Number that came online. */
EXPORT_SYMBOL(smp_num_cpus);
extern void calibrate_delay(void);
/*
* Called by both boot and secondaries to move global data into
* per-processor storage.
*/
static inline void __init
smp_store_cpu_info(int cpuid)
{
cpu_data[cpuid].loops_per_jiffy = loops_per_jiffy;
cpu_data[cpuid].last_asn = ASN_FIRST_VERSION;
cpu_data[cpuid].need_new_asn = 0;
cpu_data[cpuid].asn_lock = 0;
}
/*
* Ideally sets up per-cpu profiling hooks. Doesn't do much now...
*/
static inline void __init
smp_setup_percpu_timer(int cpuid)
{
cpu_data[cpuid].prof_counter = 1;
cpu_data[cpuid].prof_multiplier = 1;
}
static void __init
wait_boot_cpu_to_stop(int cpuid)
{
unsigned long stop = jiffies + 10*HZ;
while (time_before(jiffies, stop)) {
if (!smp_secondary_alive)
return;
barrier();
}
printk("wait_boot_cpu_to_stop: FAILED on CPU %d, hanging now\n", cpuid);
for (;;)
barrier();
}
/*
* Where secondaries begin a life of C.
*/
void __init
smp_callin(void)
{
int cpuid = hard_smp_processor_id();
if (cpu_test_and_set(cpuid, cpu_online_map)) {
printk("??, cpu 0x%x already present??\n", cpuid);
BUG();
}
/* Turn on machine checks. */
wrmces(7);
/* Set trap vectors. */
trap_init();
/* Set interrupt vector. */
wrent(entInt, 0);
/* Get our local ticker going. */
smp_setup_percpu_timer(cpuid);
/* Call platform-specific callin, if specified */
if (alpha_mv.smp_callin) alpha_mv.smp_callin();
/* All kernel threads share the same mm context. */
atomic_inc(&init_mm.mm_count);
current->active_mm = &init_mm;
/* Must have completely accurate bogos. */
local_irq_enable();
/* Wait boot CPU to stop with irq enabled before running
calibrate_delay. */
wait_boot_cpu_to_stop(cpuid);
mb();
calibrate_delay();
smp_store_cpu_info(cpuid);
/* Allow master to continue only after we written loops_per_jiffy. */
wmb();
smp_secondary_alive = 1;
DBGS(("smp_callin: commencing CPU %d current %p active_mm %p\n",
cpuid, current, current->active_mm));
/* Do nothing. */
cpu_idle();
}
/* Wait until hwrpb->txrdy is clear for cpu. Return -1 on timeout. */
static int __devinit
wait_for_txrdy (unsigned long cpumask)
{
unsigned long timeout;
if (!(hwrpb->txrdy & cpumask))
return 0;
timeout = jiffies + 10*HZ;
while (time_before(jiffies, timeout)) {
if (!(hwrpb->txrdy & cpumask))
return 0;
udelay(10);
barrier();
}
return -1;
}
/*
* Send a message to a secondary's console. "START" is one such
* interesting message. ;-)
*/
static void __init
send_secondary_console_msg(char *str, int cpuid)
{
struct percpu_struct *cpu;
register char *cp1, *cp2;
unsigned long cpumask;
size_t len;
cpu = (struct percpu_struct *)
((char*)hwrpb
+ hwrpb->processor_offset
+ cpuid * hwrpb->processor_size);
cpumask = (1UL << cpuid);
if (wait_for_txrdy(cpumask))
goto timeout;
cp2 = str;
len = strlen(cp2);
*(unsigned int *)&cpu->ipc_buffer[0] = len;
cp1