/arch/powerpc/platforms/cell/spufs/spu_restore.c

http://github.com/mirrors/linux · C · 322 lines · 192 code · 31 blank · 99 comment · 2 complexity · 4c3a1046b3497cee8bcf82ab150c9e83 MD5 · raw file

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