kernel-ark/arch/powerpc/platforms/cell/spufs/spu_restore.c
Mark Nutter 05b841174c [PATCH] spufs: add spu-side context switch code
Add the source code that is used to generate spu_save_dump.h and
spu_restore_dump.h. Since a full spu tool chain is needed to
generate these files, the default remains to use the shipped
versions in order to keep the number of tools for building the
kernel down.

Signed-off-by: Arnd Bergmann <arndb@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
2006-01-09 14:49:25 +11:00

337 lines
9.0 KiB
C

/*
* spu_restore.c
*
* (C) Copyright IBM Corp. 2005
*
* SPU-side context restore sequence outlined in
* Synergistic Processor Element Book IV
*
* Author: Mark Nutter <mnutter@us.ibm.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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef LS_SIZE
#define LS_SIZE 0x40000 /* 256K (in bytes) */
#endif
typedef unsigned int u32;
typedef unsigned long long u64;
#include <spu_intrinsics.h>
#include <asm/spu_csa.h>
#include "spu_utils.h"
#define BR_INSTR 0x327fff80 /* br -4 */
#define NOP_INSTR 0x40200000 /* nop */
#define HEQ_INSTR 0x7b000000 /* heq $0, $0 */
#define STOP_INSTR 0x00000000 /* stop 0x0 */
#define ILLEGAL_INSTR 0x00800000 /* illegal instr */
#define RESTORE_COMPLETE 0x00003ffc /* stop 0x3ffc */
static inline void fetch_regs_from_mem(addr64 lscsa_ea)
{
unsigned int ls = (unsigned int)&regs_spill[0];
unsigned int size = sizeof(regs_spill);
unsigned int tag_id = 0;
unsigned int cmd = 0x40; /* GET */
spu_writech(MFC_LSA, ls);
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
spu_writech(MFC_EAL, lscsa_ea.ui[1]);
spu_writech(MFC_Size, size);
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void restore_upper_240kb(addr64 lscsa_ea)
{
unsigned int ls = 16384;
unsigned int list = (unsigned int)&dma_list[0];
unsigned int size = sizeof(dma_list);
unsigned int tag_id = 0;
unsigned int cmd = 0x44; /* GETL */
/* Restore, Step 4:
* Enqueue the GETL command (tag 0) to the MFC SPU command
* queue to transfer the upper 240 kb of LS from CSA.
*/
spu_writech(MFC_LSA, ls);
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
spu_writech(MFC_EAL, list);
spu_writech(MFC_Size, size);
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void restore_decr(void)
{
unsigned int offset;
unsigned int decr_running;
unsigned int decr;
/* Restore, Step 6:
* If the LSCSA "decrementer running" flag is set
* then write the SPU_WrDec channel with the
* decrementer value from LSCSA.
*/
offset = LSCSA_QW_OFFSET(decr_status);
decr_running = regs_spill[offset].slot[0];
if (decr_running) {
offset = LSCSA_QW_OFFSET(decr);
decr = regs_spill[offset].slot[0];
spu_writech(SPU_WrDec, decr);
}
}
static inline void write_ppu_mb(void)
{
unsigned int offset;
unsigned int data;
/* Restore, Step 11:
* Write the MFC_WrOut_MB channel with the PPU_MB
* data from LSCSA.
*/
offset = LSCSA_QW_OFFSET(ppu_mb);
data = regs_spill[offset].slot[0];
spu_writech(SPU_WrOutMbox, data);
}
static inline void write_ppuint_mb(void)
{
unsigned int offset;
unsigned int data;
/* Restore, Step 12:
* Write the MFC_WrInt_MB channel with the PPUINT_MB
* data from LSCSA.
*/
offset = LSCSA_QW_OFFSET(ppuint_mb);
data = regs_spill[offset].slot[0];
spu_writech(SPU_WrOutIntrMbox, data);
}
static inline void restore_fpcr(void)
{
unsigned int offset;
vector unsigned int fpcr;
/* Restore, Step 13:
* Restore the floating-point status and control
* register from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(fpcr);
fpcr = regs_spill[offset].v;
spu_mtfpscr(fpcr);
}
static inline void restore_srr0(void)
{
unsigned int offset;
unsigned int srr0;
/* Restore, Step 14:
* Restore the SPU SRR0 data from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(srr0);
srr0 = regs_spill[offset].slot[0];
spu_writech(SPU_WrSRR0, srr0);
}
static inline void restore_event_mask(void)
{
unsigned int offset;
unsigned int event_mask;
/* Restore, Step 15:
* Restore the SPU_RdEventMsk data from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(event_mask);
event_mask = regs_spill[offset].slot[0];
spu_writech(SPU_WrEventMask, event_mask);
}
static inline void restore_tag_mask(void)
{
unsigned int offset;
unsigned int tag_mask;
/* Restore, Step 16:
* Restore the SPU_RdTagMsk data from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(tag_mask);
tag_mask = regs_spill[offset].slot[0];
spu_writech(MFC_WrTagMask, tag_mask);
}
static inline void restore_complete(void)
{
extern void exit_fini(void);
unsigned int *exit_instrs = (unsigned int *)exit_fini;
unsigned int offset;
unsigned int stopped_status;
unsigned int stopped_code;
/* Restore, Step 18:
* Issue a stop-and-signal instruction with
* "good context restore" signal value.
*
* Restore, Step 19:
* There may be additional instructions placed
* here by the PPE Sequence for SPU Context
* Restore in order to restore the correct
* "stopped state".
*
* This step is handled here by analyzing the
* LSCSA.stopped_status and then modifying the
* exit() function to behave appropriately.
*/
offset = LSCSA_QW_OFFSET(stopped_status);
stopped_status = regs_spill[offset].slot[0];
stopped_code = regs_spill[offset].slot[1];
switch (stopped_status) {
case SPU_STOPPED_STATUS_P_I:
/* SPU_Status[P,I]=1. Add illegal instruction
* followed by stop-and-signal instruction after
* end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = ILLEGAL_INSTR;
exit_instrs[2] = STOP_INSTR | stopped_code;
break;
case SPU_STOPPED_STATUS_P_H:
/* SPU_Status[P,H]=1. Add 'heq $0, $0' followed
* by stop-and-signal instruction after end of
* restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = HEQ_INSTR;
exit_instrs[2] = STOP_INSTR | stopped_code;
break;
case SPU_STOPPED_STATUS_S_P:
/* SPU_Status[S,P]=1. Add nop instruction
* followed by 'br -4' after end of restore
* code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = STOP_INSTR | stopped_code;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_S_I:
/* SPU_Status[S,I]=1. Add illegal instruction
* followed by 'br -4' after end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = ILLEGAL_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_I:
/* SPU_Status[I]=1. Add illegal instruction followed
* by infinite loop after end of restore sequence.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = ILLEGAL_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_S:
/* SPU_Status[S]=1. Add two 'nop' instructions. */
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = NOP_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_H:
/* SPU_Status[H]=1. Add 'heq $0, $0' instruction
* after end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = HEQ_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_P:
/* SPU_Status[P]=1. Add stop-and-signal instruction
* after end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = STOP_INSTR | stopped_code;
break;
case SPU_STOPPED_STATUS_R:
/* SPU_Status[I,S,H,P,R]=0. Add infinite loop. */
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = NOP_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
default:
/* SPU_Status[R]=1. No additonal instructions. */
break;
}
spu_sync();
}
/**
* main - entry point for SPU-side context restore.
*
* This code deviates from the documented sequence in the
* following aspects:
*
* 1. The EA for LSCSA is passed from PPE in the
* signal notification channels.
* 2. The register spill area is pulled by SPU
* into LS, rather than pushed by PPE.
* 3. All 128 registers are restored by exit().
* 4. The exit() function is modified at run
* time in order to properly restore the
* SPU_Status register.
*/
int main()
{
addr64 lscsa_ea;
lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1);
lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2);
fetch_regs_from_mem(lscsa_ea);
set_event_mask(); /* Step 1. */
set_tag_mask(); /* Step 2. */
build_dma_list(lscsa_ea); /* Step 3. */
restore_upper_240kb(lscsa_ea); /* Step 4. */
/* Step 5: done by 'exit'. */
restore_decr(); /* Step 6. */
enqueue_putllc(lscsa_ea); /* Step 7. */
set_tag_update(); /* Step 8. */
read_tag_status(); /* Step 9. */
read_llar_status(); /* Step 10. */
write_ppu_mb(); /* Step 11. */
write_ppuint_mb(); /* Step 12. */
restore_fpcr(); /* Step 13. */
restore_srr0(); /* Step 14. */
restore_event_mask(); /* Step 15. */
restore_tag_mask(); /* Step 16. */
/* Step 17. done by 'exit'. */
restore_complete(); /* Step 18. */
return 0;
}