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