spu_restore.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /*
  2. * spu_restore.c
  3. *
  4. * (C) Copyright IBM Corp. 2005
  5. *
  6. * SPU-side context restore sequence outlined in
  7. * Synergistic Processor Element Book IV
  8. *
  9. * Author: Mark Nutter <mnutter@us.ibm.com>
  10. *
  11. * This program is free software; you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation; either version 2, or (at your option)
  14. * any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, write to the Free Software
  23. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24. *
  25. */
  26. #ifndef LS_SIZE
  27. #define LS_SIZE 0x40000 /* 256K (in bytes) */
  28. #endif
  29. typedef unsigned int u32;
  30. typedef unsigned long long u64;
  31. #include <spu_intrinsics.h>
  32. #include <asm/spu_csa.h>
  33. #include "spu_utils.h"
  34. #define BR_INSTR 0x327fff80 /* br -4 */
  35. #define NOP_INSTR 0x40200000 /* nop */
  36. #define HEQ_INSTR 0x7b000000 /* heq $0, $0 */
  37. #define STOP_INSTR 0x00000000 /* stop 0x0 */
  38. #define ILLEGAL_INSTR 0x00800000 /* illegal instr */
  39. #define RESTORE_COMPLETE 0x00003ffc /* stop 0x3ffc */
  40. static inline void fetch_regs_from_mem(addr64 lscsa_ea)
  41. {
  42. unsigned int ls = (unsigned int)&regs_spill[0];
  43. unsigned int size = sizeof(regs_spill);
  44. unsigned int tag_id = 0;
  45. unsigned int cmd = 0x40; /* GET */
  46. spu_writech(MFC_LSA, ls);
  47. spu_writech(MFC_EAH, lscsa_ea.ui[0]);
  48. spu_writech(MFC_EAL, lscsa_ea.ui[1]);
  49. spu_writech(MFC_Size, size);
  50. spu_writech(MFC_TagID, tag_id);
  51. spu_writech(MFC_Cmd, cmd);
  52. }
  53. static inline void restore_upper_240kb(addr64 lscsa_ea)
  54. {
  55. unsigned int ls = 16384;
  56. unsigned int list = (unsigned int)&dma_list[0];
  57. unsigned int size = sizeof(dma_list);
  58. unsigned int tag_id = 0;
  59. unsigned int cmd = 0x44; /* GETL */
  60. /* Restore, Step 4:
  61. * Enqueue the GETL command (tag 0) to the MFC SPU command
  62. * queue to transfer the upper 240 kb of LS from CSA.
  63. */
  64. spu_writech(MFC_LSA, ls);
  65. spu_writech(MFC_EAH, lscsa_ea.ui[0]);
  66. spu_writech(MFC_EAL, list);
  67. spu_writech(MFC_Size, size);
  68. spu_writech(MFC_TagID, tag_id);
  69. spu_writech(MFC_Cmd, cmd);
  70. }
  71. static inline void restore_decr(void)
  72. {
  73. unsigned int offset;
  74. unsigned int decr_running;
  75. unsigned int decr;
  76. /* Restore, Step 6(moved):
  77. * If the LSCSA "decrementer running" flag is set
  78. * then write the SPU_WrDec channel with the
  79. * decrementer value from LSCSA.
  80. */
  81. offset = LSCSA_QW_OFFSET(decr_status);
  82. decr_running = regs_spill[offset].slot[0] & SPU_DECR_STATUS_RUNNING;
  83. if (decr_running) {
  84. offset = LSCSA_QW_OFFSET(decr);
  85. decr = regs_spill[offset].slot[0];
  86. spu_writech(SPU_WrDec, decr);
  87. }
  88. }
  89. static inline void write_ppu_mb(void)
  90. {
  91. unsigned int offset;
  92. unsigned int data;
  93. /* Restore, Step 11:
  94. * Write the MFC_WrOut_MB channel with the PPU_MB
  95. * data from LSCSA.
  96. */
  97. offset = LSCSA_QW_OFFSET(ppu_mb);
  98. data = regs_spill[offset].slot[0];
  99. spu_writech(SPU_WrOutMbox, data);
  100. }
  101. static inline void write_ppuint_mb(void)
  102. {
  103. unsigned int offset;
  104. unsigned int data;
  105. /* Restore, Step 12:
  106. * Write the MFC_WrInt_MB channel with the PPUINT_MB
  107. * data from LSCSA.
  108. */
  109. offset = LSCSA_QW_OFFSET(ppuint_mb);
  110. data = regs_spill[offset].slot[0];
  111. spu_writech(SPU_WrOutIntrMbox, data);
  112. }
  113. static inline void restore_fpcr(void)
  114. {
  115. unsigned int offset;
  116. vector unsigned int fpcr;
  117. /* Restore, Step 13:
  118. * Restore the floating-point status and control
  119. * register from the LSCSA.
  120. */
  121. offset = LSCSA_QW_OFFSET(fpcr);
  122. fpcr = regs_spill[offset].v;
  123. spu_mtfpscr(fpcr);
  124. }
  125. static inline void restore_srr0(void)
  126. {
  127. unsigned int offset;
  128. unsigned int srr0;
  129. /* Restore, Step 14:
  130. * Restore the SPU SRR0 data from the LSCSA.
  131. */
  132. offset = LSCSA_QW_OFFSET(srr0);
  133. srr0 = regs_spill[offset].slot[0];
  134. spu_writech(SPU_WrSRR0, srr0);
  135. }
  136. static inline void restore_event_mask(void)
  137. {
  138. unsigned int offset;
  139. unsigned int event_mask;
  140. /* Restore, Step 15:
  141. * Restore the SPU_RdEventMsk data from the LSCSA.
  142. */
  143. offset = LSCSA_QW_OFFSET(event_mask);
  144. event_mask = regs_spill[offset].slot[0];
  145. spu_writech(SPU_WrEventMask, event_mask);
  146. }
  147. static inline void restore_tag_mask(void)
  148. {
  149. unsigned int offset;
  150. unsigned int tag_mask;
  151. /* Restore, Step 16:
  152. * Restore the SPU_RdTagMsk data from the LSCSA.
  153. */
  154. offset = LSCSA_QW_OFFSET(tag_mask);
  155. tag_mask = regs_spill[offset].slot[0];
  156. spu_writech(MFC_WrTagMask, tag_mask);
  157. }
  158. static inline void restore_complete(void)
  159. {
  160. extern void exit_fini(void);
  161. unsigned int *exit_instrs = (unsigned int *)exit_fini;
  162. unsigned int offset;
  163. unsigned int stopped_status;
  164. unsigned int stopped_code;
  165. /* Restore, Step 18:
  166. * Issue a stop-and-signal instruction with
  167. * "good context restore" signal value.
  168. *
  169. * Restore, Step 19:
  170. * There may be additional instructions placed
  171. * here by the PPE Sequence for SPU Context
  172. * Restore in order to restore the correct
  173. * "stopped state".
  174. *
  175. * This step is handled here by analyzing the
  176. * LSCSA.stopped_status and then modifying the
  177. * exit() function to behave appropriately.
  178. */
  179. offset = LSCSA_QW_OFFSET(stopped_status);
  180. stopped_status = regs_spill[offset].slot[0];
  181. stopped_code = regs_spill[offset].slot[1];
  182. switch (stopped_status) {
  183. case SPU_STOPPED_STATUS_P_I:
  184. /* SPU_Status[P,I]=1. Add illegal instruction
  185. * followed by stop-and-signal instruction after
  186. * end of restore code.
  187. */
  188. exit_instrs[0] = RESTORE_COMPLETE;
  189. exit_instrs[1] = ILLEGAL_INSTR;
  190. exit_instrs[2] = STOP_INSTR | stopped_code;
  191. break;
  192. case SPU_STOPPED_STATUS_P_H:
  193. /* SPU_Status[P,H]=1. Add 'heq $0, $0' followed
  194. * by stop-and-signal instruction after end of
  195. * restore code.
  196. */
  197. exit_instrs[0] = RESTORE_COMPLETE;
  198. exit_instrs[1] = HEQ_INSTR;
  199. exit_instrs[2] = STOP_INSTR | stopped_code;
  200. break;
  201. case SPU_STOPPED_STATUS_S_P:
  202. /* SPU_Status[S,P]=1. Add nop instruction
  203. * followed by 'br -4' after end of restore
  204. * code.
  205. */
  206. exit_instrs[0] = RESTORE_COMPLETE;
  207. exit_instrs[1] = STOP_INSTR | stopped_code;
  208. exit_instrs[2] = NOP_INSTR;
  209. exit_instrs[3] = BR_INSTR;
  210. break;
  211. case SPU_STOPPED_STATUS_S_I:
  212. /* SPU_Status[S,I]=1. Add illegal instruction
  213. * followed by 'br -4' after end of restore code.
  214. */
  215. exit_instrs[0] = RESTORE_COMPLETE;
  216. exit_instrs[1] = ILLEGAL_INSTR;
  217. exit_instrs[2] = NOP_INSTR;
  218. exit_instrs[3] = BR_INSTR;
  219. break;
  220. case SPU_STOPPED_STATUS_I:
  221. /* SPU_Status[I]=1. Add illegal instruction followed
  222. * by infinite loop after end of restore sequence.
  223. */
  224. exit_instrs[0] = RESTORE_COMPLETE;
  225. exit_instrs[1] = ILLEGAL_INSTR;
  226. exit_instrs[2] = NOP_INSTR;
  227. exit_instrs[3] = BR_INSTR;
  228. break;
  229. case SPU_STOPPED_STATUS_S:
  230. /* SPU_Status[S]=1. Add two 'nop' instructions. */
  231. exit_instrs[0] = RESTORE_COMPLETE;
  232. exit_instrs[1] = NOP_INSTR;
  233. exit_instrs[2] = NOP_INSTR;
  234. exit_instrs[3] = BR_INSTR;
  235. break;
  236. case SPU_STOPPED_STATUS_H:
  237. /* SPU_Status[H]=1. Add 'heq $0, $0' instruction
  238. * after end of restore code.
  239. */
  240. exit_instrs[0] = RESTORE_COMPLETE;
  241. exit_instrs[1] = HEQ_INSTR;
  242. exit_instrs[2] = NOP_INSTR;
  243. exit_instrs[3] = BR_INSTR;
  244. break;
  245. case SPU_STOPPED_STATUS_P:
  246. /* SPU_Status[P]=1. Add stop-and-signal instruction
  247. * after end of restore code.
  248. */
  249. exit_instrs[0] = RESTORE_COMPLETE;
  250. exit_instrs[1] = STOP_INSTR | stopped_code;
  251. break;
  252. case SPU_STOPPED_STATUS_R:
  253. /* SPU_Status[I,S,H,P,R]=0. Add infinite loop. */
  254. exit_instrs[0] = RESTORE_COMPLETE;
  255. exit_instrs[1] = NOP_INSTR;
  256. exit_instrs[2] = NOP_INSTR;
  257. exit_instrs[3] = BR_INSTR;
  258. break;
  259. default:
  260. /* SPU_Status[R]=1. No additional instructions. */
  261. break;
  262. }
  263. spu_sync();
  264. }
  265. /**
  266. * main - entry point for SPU-side context restore.
  267. *
  268. * This code deviates from the documented sequence in the
  269. * following aspects:
  270. *
  271. * 1. The EA for LSCSA is passed from PPE in the
  272. * signal notification channels.
  273. * 2. The register spill area is pulled by SPU
  274. * into LS, rather than pushed by PPE.
  275. * 3. All 128 registers are restored by exit().
  276. * 4. The exit() function is modified at run
  277. * time in order to properly restore the
  278. * SPU_Status register.
  279. */
  280. int main()
  281. {
  282. addr64 lscsa_ea;
  283. lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1);
  284. lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2);
  285. fetch_regs_from_mem(lscsa_ea);
  286. set_event_mask(); /* Step 1. */
  287. set_tag_mask(); /* Step 2. */
  288. build_dma_list(lscsa_ea); /* Step 3. */
  289. restore_upper_240kb(lscsa_ea); /* Step 4. */
  290. /* Step 5: done by 'exit'. */
  291. enqueue_putllc(lscsa_ea); /* Step 7. */
  292. set_tag_update(); /* Step 8. */
  293. read_tag_status(); /* Step 9. */
  294. restore_decr(); /* moved Step 6. */
  295. read_llar_status(); /* Step 10. */
  296. write_ppu_mb(); /* Step 11. */
  297. write_ppuint_mb(); /* Step 12. */
  298. restore_fpcr(); /* Step 13. */
  299. restore_srr0(); /* Step 14. */
  300. restore_event_mask(); /* Step 15. */
  301. restore_tag_mask(); /* Step 16. */
  302. /* Step 17. done by 'exit'. */
  303. restore_complete(); /* Step 18. */
  304. return 0;
  305. }