[MIPS] MT: Improved multithreading support.
    
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/kernel/smtc-asm.S b/arch/mips/kernel/smtc-asm.S
new file mode 100644
index 0000000..c9d6519
--- /dev/null
+++ b/arch/mips/kernel/smtc-asm.S
@@ -0,0 +1,130 @@
+/*
+ * Assembly Language Functions for MIPS MT SMTC support
+ */
+
+/*
+ * This file should be built into the kernel only if CONFIG_MIPS_MT_SMTC is set. */
+
+#include <asm/regdef.h>
+#include <asm/asmmacro.h>
+#include <asm/stackframe.h>
+#include <asm/stackframe.h>
+
+/*
+ * "Software Interrupt" linkage.
+ *
+ * This is invoked when an "Interrupt" is sent from one TC to another,
+ * where the TC to be interrupted is halted, has it's Restart address
+ * and Status values saved by the "remote control" thread, then modified
+ * to cause execution to begin here, in kenel mode. This code then
+ * disguises the TC state as that of an exception and transfers
+ * control to the general exception or vectored interrupt handler.
+ */
+	.set noreorder
+
+/*
+The __smtc_ipi_vector would use k0 and k1 as temporaries and
+1) Set EXL (this is per-VPE, so this can't be done by proxy!)
+2) Restore the K/CU and IXMT bits to the pre "exception" state
+   (EXL means no interrupts and access to the kernel map).
+3) Set EPC to be the saved value of TCRestart.
+4) Jump to the exception handler entry point passed by the sender.
+
+CAN WE PROVE THAT WE WON'T DO THIS IF INTS DISABLED??
+*/
+
+/*
+ * Reviled and slandered vision: Set EXL and restore K/CU/IXMT
+ * state of pre-halt thread, then save everything and call
+ * thought some function pointer to imaginary_exception, which
+ * will parse a register value or memory message queue to
+ * deliver things like interprocessor interrupts. On return
+ * from that function, jump to the global ret_from_irq code
+ * to invoke the scheduler and return as appropriate.
+ */
+
+#define PT_PADSLOT4 (PT_R0-8)
+#define PT_PADSLOT5 (PT_R0-4)
+
+	.text
+	.align 5
+FEXPORT(__smtc_ipi_vector)
+	.set	noat
+	/* Disable thread scheduling to make Status update atomic */
+	DMT	27					# dmt	k1
+	ehb
+	/* Set EXL */
+	mfc0	k0,CP0_STATUS
+	ori	k0,k0,ST0_EXL
+	mtc0	k0,CP0_STATUS
+	ehb
+	/* Thread scheduling now inhibited by EXL. Restore TE state. */
+	andi	k1,k1,VPECONTROL_TE
+	beqz	k1,1f
+	emt
+1:
+	/*
+	 * The IPI sender has put some information on the anticipated
+	 * kernel stack frame.  If we were in user mode, this will be
+	 * built above the saved kernel SP.  If we were already in the
+	 * kernel, it will be built above the current CPU SP.
+	 *
+	 * Were we in kernel mode, as indicated by CU0?
+	 */
+	sll	k1,k0,3
+	.set noreorder
+	bltz	k1,2f
+	move	k1,sp
+	.set reorder
+	/*
+	 * If previously in user mode, set CU0 and use kernel stack.
+	 */
+	li	k1,ST0_CU0
+	or	k1,k1,k0
+	mtc0	k1,CP0_STATUS
+	ehb
+	get_saved_sp
+	/* Interrupting TC will have pre-set values in slots in the new frame */
+2:	subu	k1,k1,PT_SIZE
+	/* Load TCStatus Value */
+	lw	k0,PT_TCSTATUS(k1)
+	/* Write it to TCStatus to restore CU/KSU/IXMT state */
+	mtc0	k0,$2,1
+	ehb
+	lw	k0,PT_EPC(k1)
+	mtc0	k0,CP0_EPC
+	/* Save all will redundantly recompute the SP, but use it for now */
+	SAVE_ALL
+	CLI
+	move	a0,sp
+	/* Function to be invoked passed stack pad slot 5 */
+	lw	t0,PT_PADSLOT5(sp)
+	/* Argument from sender passed in stack pad slot 4 */
+	lw	a1,PT_PADSLOT4(sp)
+	jalr	t0
+	nop
+	j	ret_from_irq
+	nop
+
+/*
+ * Called from idle loop to provoke processing of queued IPIs
+ * First IPI message in queue passed as argument.
+ */
+
+LEAF(self_ipi)
+	/* Before anything else, block interrupts */
+	mfc0	t0,CP0_TCSTATUS
+	ori	t1,t0,TCSTATUS_IXMT
+	mtc0	t1,CP0_TCSTATUS
+	ehb
+	/* We know we're in kernel mode, so prepare stack frame */
+	subu	t1,sp,PT_SIZE
+	sw	ra,PT_EPC(t1)
+	sw	a0,PT_PADSLOT4(t1)
+	la	t2,ipi_decode
+	sw	t2,PT_PADSLOT5(t1)
+	/* Save pre-disable value of TCStatus */
+	sw	t0,PT_TCSTATUS(t1)
+	j	__smtc_ipi_vector
+	nop
+END(self_ipi)