summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-03-28 01:35:12 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-03-28 01:35:12 +0000
commit0b9049739779f6052eb8069f3dde7c3a7f14a591 (patch)
tree9630c1ea481dd1c429a2692067316237957d6e07
parent12e00f34ea0db712ce70bc3eed334c81b3d6a344 (diff)
SMP for 32-bit kernel, support for Sibyte SB1. Patch from Justin
with minor changes by me.
-rw-r--r--arch/mips/kernel/Makefile1
-rw-r--r--arch/mips/kernel/entry.S1
-rw-r--r--arch/mips/kernel/head.S49
-rw-r--r--arch/mips/kernel/process.c30
-rw-r--r--arch/mips/kernel/r4k_misc.S17
-rw-r--r--arch/mips/kernel/r4k_switch.S12
-rw-r--r--arch/mips/kernel/smp.c403
-rw-r--r--arch/mips/kernel/traps.c3
-rw-r--r--include/asm-mips/hardirq.h49
-rw-r--r--include/asm-mips/mmu_context.h22
-rw-r--r--include/asm-mips/pgalloc.h2
-rw-r--r--include/asm-mips/processor.h1
-rw-r--r--include/asm-mips/smp.h30
-rw-r--r--include/asm-mips/spinlock.h3
-rw-r--r--include/asm-mips/stackframe.h24
-rw-r--r--include/asm-mips/system.h29
16 files changed, 636 insertions, 40 deletions
diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile
index ec2eae67e..10b8240aa 100644
--- a/arch/mips/kernel/Makefile
+++ b/arch/mips/kernel/Makefile
@@ -38,6 +38,7 @@ endif
endif
endif
+obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_MIPS_FPE_MODULE) += fpe.o
ifndef CONFIG_MIPS_FPU_EMULATOR
obj-y += softfp.o
diff --git a/arch/mips/kernel/entry.S b/arch/mips/kernel/entry.S
index bc56db51b..59bc8c2c9 100644
--- a/arch/mips/kernel/entry.S
+++ b/arch/mips/kernel/entry.S
@@ -12,6 +12,7 @@
#include <linux/config.h>
#include <linux/sys.h>
+#include <asm/addrspace.h>
#include <asm/asm.h>
#include <asm/current.h>
#include <asm/errno.h>
diff --git a/arch/mips/kernel/head.S b/arch/mips/kernel/head.S
index 529ebb57d..fb73d3db0 100644
--- a/arch/mips/kernel/head.S
+++ b/arch/mips/kernel/head.S
@@ -59,9 +59,19 @@
.set noat
LEAF(except_vec0_r4000)
.set mips3
+#ifdef CONFIG_SMP
+ mfc0 k1, CP0_CONTEXT
+ la k0, current_pgd
+ srl k1, 23
+ sll k1, 2
+ addu k1, k0, k1
+ lw k1, (k1)
+#else
+ lw k1, current_pgd # get pgd pointer
+#endif
mfc0 k0, CP0_BADVADDR # Get faulting address
srl k0, k0, 22 # get pgd only bits
- lw k1, current_pgd # get pgd pointer
+
sll k0, k0, 2
addu k1, k1, k0 # add in pgd offset
mfc0 k0, CP0_CONTEXT # get context reg
@@ -442,9 +452,9 @@ NESTED(kernel_entry, 16, sp)
*/
la $28, init_task_union
addiu t0, $28, KERNEL_STACK_SIZE-32
- sw t0, kernelsp
subu sp, t0, 4*SZREG
+ sw t0, kernelsp
/* The firmware/bootloader passes argc/argp/envp
* to us as arguments. But clear bss first because
* the romvec and other important info is stored there
@@ -462,6 +472,30 @@ NESTED(kernel_entry, 16, sp)
nop
END(kernel_entry)
+
+#ifdef CONFIG_SMP
+
+/*
+ * SMP slave cpus entry point. Board specific code
+ * for bootstrap calls this function after setting up
+ * the stack and gp registers.
+ */
+ LEAF(smp_bootstrap)
+ .set push
+ .set noreorder
+ mtc0 zero, CP0_WIRED
+ CLI
+ mfc0 t0, CP0_STATUS
+ li t1, ~(ST0_CU1|ST0_CU2|ST0_CU3|ST0_BEV);
+ and t0, t1
+ or t0, (ST0_CU0|ST0_KX|ST0_SX|ST0_FR);
+ addiu a0, zero, 0
+ jal start_secondary
+ mtc0 t0, CP0_STATUS
+ .set pop
+ END(smp_bootstrap)
+#endif
+
/*
* This buffer is reserved for the use of the cache error handler.
*/
@@ -469,12 +503,19 @@ NESTED(kernel_entry, 16, sp)
EXPORT(cache_error_buffer)
.fill 32*4,1,0
+#ifndef CONFIG_SMP
EXPORT(kernelsp)
PTR 0
EXPORT(current_pgd)
- PTR 0
+ PTR 0
+#else
+ /* There's almost certainly a better way to do this with the macros...*/
+ .globl kernelsp
+ .comm kernelsp, NR_CPUS * 8, 8
+ .globl current_pgd
+ .comm current_pgd, NR_CPUS * 8, 8
+#endif
.text
-
.org 0x1000
EXPORT(swapper_pg_dir)
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index 6cda842fd..9284db514 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -162,21 +162,21 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
long retval;
__asm__ __volatile__(
- ".set\tnoreorder\n\t"
- "move\t$6,$sp\n\t"
- "move\t$4,%5\n\t"
- "li\t$2,%1\n\t"
- "syscall\n\t"
- "beq\t$6,$sp,1f\n\t"
- "subu\t$sp,32\n\t" /* delay slot */
- "jalr\t%4\n\t"
- "move\t$4,%3\n\t" /* delay slot */
- "move\t$4,$2\n\t"
- "li\t$2,%2\n\t"
- "syscall\n"
- "1:\taddiu\t$sp,32\n\t"
- "move\t%0,$2\n\t"
- ".set\treorder"
+ ".set noreorder \n"
+ " move $6,$sp \n"
+ " move $4,%5 \n"
+ " li $2,%1 \n"
+ " syscall \n"
+ " beq $6,$sp,1f \n"
+ " subu $sp,32 \n" /* delay slot */
+ " jalr %4 \n"
+ " move $4,%3 \n" /* delay slot */
+ " move $4,$2 \n"
+ " li $2,%2 \n"
+ " syscall \n"
+ "1: addiu $sp,32 \n"
+ " move %0,$2 \n"
+ ".set reorder"
:"=r" (retval)
:"i" (__NR_clone), "i" (__NR_exit),
"r" (arg), "r" (fn),
diff --git a/arch/mips/kernel/r4k_misc.S b/arch/mips/kernel/r4k_misc.S
index bfa3cb82c..e16820f88 100644
--- a/arch/mips/kernel/r4k_misc.S
+++ b/arch/mips/kernel/r4k_misc.S
@@ -35,10 +35,25 @@
* in register PTE, a ptr into the table in which
* the pte belongs is in PTR.
*/
+
+#ifdef CONFIG_SMP
+#define GET_PGD(scratch, ptr) \
+ mfc0 ptr, CP0_CONTEXT; \
+ la scratch, current_pgd;\
+ srl ptr, 23; \
+ sll ptr, 2; \
+ addu ptr, scratch, ptr; \
+ lw ptr, (ptr);
+#else
+#define GET_PGD(scratch, ptr) \
+ lw ptr, current_pgd;
+#endif
+
+
#define LOAD_PTE(pte, ptr) \
+ GET_PGD(pte, ptr) \
mfc0 pte, CP0_BADVADDR; \
srl pte, pte, 22; \
- lw ptr, current_pgd; \
sll pte, pte, 2; \
addu ptr, ptr, pte; \
mfc0 pte, CP0_BADVADDR; \
diff --git a/arch/mips/kernel/r4k_switch.S b/arch/mips/kernel/r4k_switch.S
index e1d10f92d..10328f4ad 100644
--- a/arch/mips/kernel/r4k_switch.S
+++ b/arch/mips/kernel/r4k_switch.S
@@ -1,5 +1,4 @@
-/* $Id: r4k_switch.S,v 1.9 1999/08/18 23:37:44 ralf Exp $
- *
+/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
@@ -50,7 +49,16 @@
move $28, a1
CPU_RESTORE_NONSCRATCH($28)
addiu t0, $28, KERNEL_STACK_SIZE-32
+#ifdef CONFIG_SMP
+ mfc0 a3, CP0_CONTEXT
+ la t1, kernelsp
+ srl a3, 23
+ sll a3, 2
+ addu t1, a3, t1
+ sw t0, (t1)
+#else
sw t0, kernelsp
+#endif
mfc0 t1, CP0_STATUS /* Do we really need this? */
li a3, 0xff00
and t1, a3
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
new file mode 100644
index 000000000..12653eb87
--- /dev/null
+++ b/arch/mips/kernel/smp.c
@@ -0,0 +1,403 @@
+/*
+ *
+ * arch/mips/kernel/smp.c
+ *
+ * Copyright (C) 2000 Sibyte
+ *
+ * Written by Justin Carlson (carlson@sibyte.com)
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+
+#include <asm/atomic.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/hardirq.h>
+#include <asm/softirq.h>
+#include <asm/mmu_context.h>
+#include <asm/delay.h>
+#include <asm/smp.h>
+
+/*
+ * This was written with the BRCM12500 MP SOC in mind, but tries to
+ * be generic. It's modelled on the mips64 smp.c code, which is
+ * derived from Sparc, I'm guessing, which is derived from...
+ *
+ * It's probably horribly designed for very large ccNUMA systems
+ * as it doesn't take any node clustering into account.
+*/
+
+
+/* Ze Big Kernel Lock! */
+spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED;
+int smp_threads_ready; /* Not used */
+int smp_num_cpus;
+int global_irq_holder = NO_PROC_ID;
+spinlock_t global_irq_lock = SPIN_LOCK_UNLOCKED;
+struct mips_cpuinfo cpu_data[NR_CPUS];
+
+struct smp_fn_call_struct smp_fn_call =
+{ SPIN_LOCK_UNLOCKED, ATOMIC_INIT(0), NULL, NULL};
+
+static atomic_t cpus_booted = ATOMIC_INIT(0);
+
+
+/* These are defined by the board-specific code. */
+
+/* Cause the function described by smp_fn_call
+ to be executed on the passed cpu. When the function
+ has finished, increment the finished field of
+ smp_fn_call. */
+
+void core_call_function(int cpu);
+
+/*
+ Clear all undefined state in the cpu, set up sp and gp to the passed
+ values, and kick the cpu into smp_bootstrap();
+*/
+void prom_boot_secondary(int cpu, unsigned long sp, unsigned long gp);
+
+/*
+ * After we've done initial boot, this function is called to allow the
+ * board code to clean up state, if needed
+ */
+
+void prom_init_secondary(void);
+
+
+void cpu_idle(void);
+
+/* Do whatever setup needs to be done for SMP at the board level. Return
+ the number of cpus in the system, including this one */
+int prom_setup_smp(void);
+
+int start_secondary(void *unused)
+{
+ prom_init_secondary();
+ write_32bit_cp0_register(CP0_CONTEXT, smp_processor_id()<<23);
+ current_pgd[smp_processor_id()] = init_mm.pgd;
+ printk("Slave cpu booted successfully\n");
+ atomic_inc(&cpus_booted);
+ cpu_idle();
+ return 0;
+}
+
+void __init smp_boot_cpus(void)
+{
+ int i;
+ smp_num_cpus = prom_setup_smp();
+ init_new_context(current, &init_mm);
+ current->processor = 0;
+ atomic_set(&cpus_booted, 1); /* Master CPU is already booted... */
+ init_idle();
+ for (i = 1; i < smp_num_cpus; i++) {
+ struct task_struct *p;
+ struct pt_regs regs;
+ printk("Starting CPU %d... ", i);
+
+ /* Spawn a new process normally. Grab a pointer to
+ its task struct so we can mess with it */
+ do_fork(CLONE_VM|CLONE_PID, 0, &regs, 0);
+ p = init_task.prev_task;
+
+ /* Schedule the first task manually */
+
+ p->processor = i;
+ p->has_cpu = 1;
+
+ /* Attach to the address space of init_task. */
+ atomic_inc(&init_mm.mm_count);
+ p->active_mm = &init_mm;
+ init_tasks[i] = p;
+
+ del_from_runqueue(p);
+ unhash_process(p);
+
+ prom_boot_secondary(i,
+ (unsigned long)p + KERNEL_STACK_SIZE - 32,
+ (unsigned long)p);
+
+#if 0
+
+ /* This is copied from the ip-27 code in the mips64 tree */
+
+ struct task_struct *p;
+
+ /*
+ * The following code is purely to make sure
+ * Linux can schedule processes on this slave.
+ */
+ kernel_thread(0, NULL, CLONE_PID);
+ p = init_task.prev_task;
+ sprintf(p->comm, "%s%d", "Idle", i);
+ init_tasks[i] = p;
+ p->processor = i;
+ p->has_cpu = 1; /* we schedule the first task manually */
+ del_from_runqueue(p);
+ unhash_process(p);
+ /* Attach to the address space of init_task. */
+ atomic_inc(&init_mm.mm_count);
+ p->active_mm = &init_mm;
+ prom_boot_secondary(i,
+ (unsigned long)p + KERNEL_STACK_SIZE - 32,
+ (unsigned long)p);
+#endif
+ }
+ /* Wait for everyone to come up */
+ while (atomic_read(&cpus_booted) != smp_num_cpus) {}
+}
+
+void __init smp_commence(void)
+{
+ /* Not sure what to do here yet */
+}
+
+static void reschedule_this_cpu(void *dummy)
+{
+ current->need_resched = 1;
+}
+
+void FASTCALL(smp_send_reschedule(int cpu))
+{
+ smp_call_function(reschedule_this_cpu, NULL, 0, 0);
+}
+
+
+/* The caller of this wants the passed function to run on every cpu. If
+ wait is set, wait until all cpus have finished the function before
+ returning. The lock is here to protect the call structure. */
+int smp_call_function (void (*func) (void *info), void *info, int retry,
+ int wait)
+{
+ int i;
+ int cpus = smp_num_cpus - 1;
+
+// unsigned long flags;
+
+ if (smp_num_cpus < 2) {
+ return 0;
+ }
+
+ spin_lock_bh(&smp_fn_call.lock);
+
+ atomic_set(&smp_fn_call.finished, 0);
+ smp_fn_call.fn = func;
+ smp_fn_call.data = info;
+
+ for (i = 0; i < smp_num_cpus; i++) {
+ if (i != smp_processor_id()) {
+ /* Call the board specific routine */
+ core_call_function(i);
+ }
+ }
+
+ if (wait) {
+ while(atomic_read(&smp_fn_call.finished) != cpus) {}
+ }
+
+ spin_unlock_bh(&smp_fn_call.lock);
+ return 0;
+}
+
+void synchronize_irq(void)
+{
+ panic("synchronize_irq");
+}
+
+static void stop_this_cpu(void *dummy)
+{
+ printk("Cpu stopping\n");
+ for (;;);
+}
+
+void smp_send_stop(void)
+{
+ smp_call_function(stop_this_cpu, NULL, 1, 0);
+ smp_num_cpus = 1;
+}
+
+/* Not really SMP stuff ... */
+int setup_profiling_timer(unsigned int multiplier)
+{
+ return 0;
+}
+
+
+/* Most of this code is take from the mips64 tree (ip27-irq.c). It's virtually identical
+ to the i386 implentation in arh/i386/irq.c, with translations for the interrupt enable bit */
+
+
+#define MAXCOUNT 100000000
+#define SYNC_OTHER_CORES(x) udelay(x+1)
+
+
+
+static inline void wait_on_irq(int cpu)
+{
+ int count = MAXCOUNT;
+
+ for (;;) {
+
+ /*
+ * Wait until all interrupts are gone. Wait
+ * for bottom half handlers unless we're
+ * already executing in one..
+ */
+ if (!irqs_running())
+ if (local_bh_count(cpu) || !spin_is_locked(&global_bh_lock))
+ break;
+
+ /* Duh, we have to loop. Release the lock to avoid deadlocks */
+ spin_unlock(&global_irq_lock);
+
+ for (;;) {
+ if (!--count) {
+ printk("Count spun out. Huh?\n");
+ count = ~0;
+ }
+ __sti();
+ SYNC_OTHER_CORES(cpu);
+ __cli();
+ if (irqs_running())
+ continue;
+ if (spin_is_locked(&global_irq_lock))
+ continue;
+ if (!local_bh_count(cpu) && spin_is_locked(&global_bh_lock))
+ continue;
+ if (spin_trylock(&global_irq_lock))
+ break;
+ }
+ }
+}
+
+
+static inline void get_irqlock(int cpu)
+{
+ if (!spin_trylock(&global_irq_lock)) {
+ /* do we already hold the lock? */
+ if ((unsigned char) cpu == global_irq_holder)
+ return;
+ /* Uhhuh.. Somebody else got it. Wait.. */
+ spin_lock(&global_irq_lock);
+ }
+ /*
+ * We also to make sure that nobody else is running
+ * in an interrupt context.
+ */
+ wait_on_irq(cpu);
+
+ /*
+ * Ok, finally..
+ */
+ global_irq_holder = cpu;
+}
+
+
+/*
+ * A global "cli()" while in an interrupt context
+ * turns into just a local cli(). Interrupts
+ * should use spinlocks for the (very unlikely)
+ * case that they ever want to protect against
+ * each other.
+ *
+ * If we already have local interrupts disabled,
+ * this will not turn a local disable into a
+ * global one (problems with spinlocks: this makes
+ * save_flags+cli+sti usable inside a spinlock).
+ */
+void __global_cli(void)
+{
+ unsigned int flags;
+
+ __save_flags(flags);
+ if (flags & ST0_IE) {
+ int cpu = smp_processor_id();
+ __cli();
+ if (!local_irq_count(cpu))
+ get_irqlock(cpu);
+ }
+}
+
+void __global_sti(void)
+{
+ int cpu = smp_processor_id();
+
+ if (!local_irq_count(cpu))
+ release_irqlock(cpu);
+ __sti();
+}
+
+/*
+ * SMP flags value to restore to:
+ * 0 - global cli
+ * 1 - global sti
+ * 2 - local cli
+ * 3 - local sti
+ */
+unsigned long __global_save_flags(void)
+{
+ int retval;
+ int local_enabled;
+ unsigned long flags;
+ int cpu = smp_processor_id();
+
+ __save_flags(flags);
+ local_enabled = (flags & ST0_IE);
+ /* default to local */
+ retval = 2 + local_enabled;
+
+ /* check for global flags if we're not in an interrupt */
+ if (!local_irq_count(cpu)) {
+ if (local_enabled)
+ retval = 1;
+ if (global_irq_holder == cpu)
+ retval = 0;
+ }
+ return retval;
+}
+
+void __global_restore_flags(unsigned long flags)
+{
+ switch (flags) {
+ case 0:
+ __global_cli();
+ break;
+ case 1:
+ __global_sti();
+ break;
+ case 2:
+ __cli();
+ break;
+ case 3:
+ __sti();
+ break;
+ default:
+ printk("global_restore_flags: %08lx\n", flags);
+ }
+}
+
+
+
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index e2c3663cb..85767362e 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -938,5 +938,6 @@ void __init trap_init(void)
atomic_inc(&init_mm.mm_count); /* XXX UP? */
current->active_mm = &init_mm;
- current_pgd = init_mm.pgd;
+ write_32bit_cp0_register(CP0_CONTEXT, smp_processor_id()<<23);
+ current_pgd[0] = init_mm.pgd;
}
diff --git a/include/asm-mips/hardirq.h b/include/asm-mips/hardirq.h
index 3896706c7..28340dff3 100644
--- a/include/asm-mips/hardirq.h
+++ b/include/asm-mips/hardirq.h
@@ -12,6 +12,7 @@
#include <linux/config.h>
#include <linux/threads.h>
#include <linux/irq.h>
+#include <linux/spinlock.h>
/* entry.S is sensitive to the offsets of these fields */
typedef struct {
@@ -44,7 +45,53 @@ typedef struct {
#else
-#error No habla MIPS SMP
+#include <asm/atomic.h>
+#include <linux/spinlock.h>
+#include <asm/smp.h>
+
+extern int global_irq_holder;
+extern spinlock_t global_irq_lock;
+
+static inline int irqs_running (void)
+{
+ int i;
+
+ for (i = 0; i < smp_num_cpus; i++)
+ if (local_irq_count(i))
+ return 1;
+ return 0;
+}
+
+static inline void release_irqlock(int cpu)
+{
+ /* if we didn't own the irq lock, just ignore.. */
+ if (global_irq_holder == cpu) {
+ global_irq_holder = NO_PROC_ID;
+ spin_unlock(&global_irq_lock);
+ }
+}
+
+static inline int hardirq_trylock(int cpu)
+{
+ return !local_irq_count(cpu) && !spin_is_locked(&global_irq_lock);
+}
+
+#define hardirq_endlock(cpu) do { } while (0)
+
+static inline void irq_enter(int cpu, int irq)
+{
+ ++local_irq_count(cpu);
+
+ while (spin_is_locked(&global_irq_lock))
+ barrier();
+}
+
+static inline void irq_exit(int cpu, int irq)
+{
+ --local_irq_count(cpu);
+}
+
+extern void synchronize_irq(void);
#endif /* CONFIG_SMP */
#endif /* _ASM_HARDIRQ_H */
diff --git a/include/asm-mips/mmu_context.h b/include/asm-mips/mmu_context.h
index 5912b9966..775e2d370 100644
--- a/include/asm-mips/mmu_context.h
+++ b/include/asm-mips/mmu_context.h
@@ -1,5 +1,4 @@
-/* $Id: mmu_context.h,v 1.7 2000/02/04 07:40:53 ralf Exp $
- *
+/*
* Switch a MMU context.
*
* This file is subject to the terms and conditions of the GNU General Public
@@ -13,11 +12,12 @@
#define _ASM_MMU_CONTEXT_H
#include <linux/config.h>
+#include <linux/slab.h>
#include <asm/pgalloc.h>
/* Fuck. The f-word is here so you can grep for it :-) */
extern unsigned long asid_cache;
-extern pgd_t *current_pgd;
+extern pgd_t *current_pgd[];
#if defined(CONFIG_CPU_R3000) || defined(CONFIG_CPU_R3912)
@@ -60,7 +60,19 @@ get_new_mmu_context(struct mm_struct *mm, unsigned long asid)
extern inline int
init_new_context(struct task_struct *tsk, struct mm_struct *mm)
{
+#ifndef CONFIG_SMP
mm->context = 0;
+#else
+ mm->context = (unsigned long)kmalloc(smp_num_cpus *
+ sizeof(unsigned long), GFP_KERNEL);
+ /*
+ * Init the "context" values so that a tlbpid allocation
+ * happens on the first switch.
+ */
+ if (mm->context == 0)
+ return -ENOMEM;
+ memset((void *)mm->context, 0, smp_num_cpus * sizeof(unsigned long));
+#endif
return 0;
}
@@ -73,7 +85,7 @@ extern inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
if ((next->context ^ asid) & ASID_VERSION_MASK)
get_new_mmu_context(next, asid);
- current_pgd = next->pgd;
+ current_pgd[cpu] = next->pgd;
set_entryhi(next->context);
}
@@ -96,7 +108,7 @@ activate_mm(struct mm_struct *prev, struct mm_struct *next)
/* Unconditionally get a new ASID. */
get_new_mmu_context(next, asid_cache);
- current_pgd = next->pgd;
+ current_pgd[smp_processor_id()] = next->pgd;
set_entryhi(next->context);
}
diff --git a/include/asm-mips/pgalloc.h b/include/asm-mips/pgalloc.h
index 4f8c74dcf..0fa39f666 100644
--- a/include/asm-mips/pgalloc.h
+++ b/include/asm-mips/pgalloc.h
@@ -10,6 +10,7 @@
#define _ASM_PGALLOC_H
#include <linux/config.h>
+#include <linux/mm.h>
/* TLB flushing:
*
@@ -177,6 +178,7 @@ extern inline void pmd_free(pmd_t * pmd)
extern inline pmd_t * pmd_alloc(pgd_t * pgd, unsigned long address)
{
+ /* Two level page tables. This level is a nop */
return (pmd_t *) pgd;
}
diff --git a/include/asm-mips/processor.h b/include/asm-mips/processor.h
index d658a010e..b0273cca1 100644
--- a/include/asm-mips/processor.h
+++ b/include/asm-mips/processor.h
@@ -29,6 +29,7 @@
#include <asm/system.h>
struct mips_cpuinfo {
+ unsigned long udelay_val;
unsigned long *pgd_quick;
unsigned long *pte_quick;
unsigned long pgtable_cache_sz;
diff --git a/include/asm-mips/smp.h b/include/asm-mips/smp.h
index 601826429..831dccafd 100644
--- a/include/asm-mips/smp.h
+++ b/include/asm-mips/smp.h
@@ -1,6 +1,36 @@
#ifndef __ASM_MIPS_SMP_H
#define __ASM_MIPS_SMP_H
+#include <linux/config.h>
+#include <asm/spinlock.h>
+#include <asm/atomic.h>
+#include <asm/current.h>
+
+#ifdef CONFIG_SMP
+
+
+/* Mappings are straight across. If we want
+ to add support for disabling cpus and such,
+ we'll have to do what the mips64 port does here */
#define cpu_logical_map(cpu) (cpu)
+#define cpu_number_map(cpu) (cpu)
+
+#define smp_processor_id() (current->processor)
+
+
+/* I've no idea what the real meaning of this is */
+#define PROC_CHANGE_PENALTY 20
+
+#define NO_PROC_ID (-1)
+
+struct smp_fn_call_struct {
+ spinlock_t lock;
+ atomic_t finished;
+ void (*fn)(void *);
+ void *data;
+};
+
+extern struct smp_fn_call_struct smp_fn_call;
+#endif /* CONFIG_SMP */
#endif /* __ASM_MIPS_SMP_H */
diff --git a/include/asm-mips/spinlock.h b/include/asm-mips/spinlock.h
index 4ac282bbc..61ebfa660 100644
--- a/include/asm-mips/spinlock.h
+++ b/include/asm-mips/spinlock.h
@@ -21,6 +21,9 @@ typedef struct {
#define spin_lock_init(x) do { (x)->lock = 0; } while(0);
+#define spin_is_locked(x) ((x)->lock != 0)
+#define spin_unlock_wait(x) ({ do { barrier(); } while ((x)->lock); })
+
/*
* Simple spin lock operations. There are two variants, one clears IRQ's
* on the local processor, one does not.
diff --git a/include/asm-mips/stackframe.h b/include/asm-mips/stackframe.h
index 2f436e79f..127654aab 100644
--- a/include/asm-mips/stackframe.h
+++ b/include/asm-mips/stackframe.h
@@ -8,6 +8,9 @@
#ifndef __ASM_MIPS_STACKFRAME_H
#define __ASM_MIPS_STACKFRAME_H
+#include <asm/addrspace.h>
+#include <asm/mipsregs.h>
+#include <asm/processor.h>
#include <asm/asm.h>
#include <asm/offset.h>
#include <linux/config.h>
@@ -70,6 +73,22 @@ __asm__ ( \
/* Used in declaration of save_static functions. */
#define static_unused static __attribute__((unused))
+
+#ifdef CONFIG_SMP
+# define GET_SAVED_SP \
+ mfc0 k0, CP0_CONTEXT; \
+ lui k1, %hi(kernelsp); \
+ srl k0, k0, 23; \
+ sll k0, k0, 2; \
+ addu k1, k0; \
+ lw k1, %lo(kernelsp)(k1);
+
+#else
+# define GET_SAVED_SP \
+ lui k1, %hi(kernelsp); \
+ lw k1, %lo(kernelsp)(k1);
+#endif
+
#define SAVE_SOME \
.set push; \
.set reorder; \
@@ -80,13 +99,12 @@ __asm__ ( \
move k1, sp; \
.set reorder; \
/* Called from user mode, new stack. */ \
- lui k1, %hi(kernelsp); \
- lw k1, %lo(kernelsp)(k1); \
+ GET_SAVED_SP \
8: \
move k0, sp; \
subu sp, k1, PT_SIZE; \
sw k0, PT_R29(sp); \
- sw $3, PT_R3(sp); \
+ sw $3, PT_R3(sp); \
sw $0, PT_R0(sp); \
mfc0 v1, CP0_STATUS; \
sw $2, PT_R2(sp); \
diff --git a/include/asm-mips/system.h b/include/asm-mips/system.h
index 68117be89..1c1c8c575 100644
--- a/include/asm-mips/system.h
+++ b/include/asm-mips/system.h
@@ -115,14 +115,27 @@ do { \
: "$1", "memory"); \
} while(0)
-/*
- * Non-SMP versions ...
- */
-#define sti() __sti()
-#define cli() __cli()
-#define save_flags(x) __save_flags(x)
-#define save_and_cli(x) __save_and_cli(x)
-#define restore_flags(x) __restore_flags(x)
+#ifdef CONFIG_SMP
+
+extern void __global_sti(void);
+extern void __global_cli(void);
+extern unsigned long __global_save_flags(void);
+extern void __global_restore_flags(unsigned long);
+# define sti() __global_sti()
+# define cli() __global_cli()
+# define save_flags(x) do { x = __global_save_flags(); } while (0)
+# define restore_flags(x) __global_restore_flags(x)
+# define save_and_cli(x) do { save_flags(x); cli(); } while(0)
+
+#else /* Single processor */
+
+# define sti() __sti()
+# define cli() __cli()
+# define save_flags(x) __save_flags(x)
+# define save_and_cli(x) __save_and_cli(x)
+# define restore_flags(x) __restore_flags(x)
+
+#endif /* SMP */
/* For spinlocks etc */
#define local_irq_save(x) __save_and_cli(x);