• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/src/linux/linux-2.6/arch/powerpc/platforms/cell/spufs/
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
27
28#ifndef LS_SIZE
29#define LS_SIZE                 0x40000	/* 256K (in bytes) */
30#endif
31
32typedef unsigned int u32;
33typedef 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
46static 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
61static 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
81static inline void restore_decr(void)
82{
83	unsigned int offset;
84	unsigned int decr_running;
85	unsigned int decr;
86
87	/* Restore, Step 6:
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];
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
101static 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
115static 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
129static 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
143static 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
156static 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
169static 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
182static 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 additonal instructions. */
288		break;
289	}
290	spu_sync();
291}
292
293/**
294 * main - entry point for SPU-side context restore.
295 *
296 * This code deviates from the documented sequence in the
297 * following aspects:
298 *
299 * 	1. The EA for LSCSA is passed from PPE in the
300 *	   signal notification channels.
301 *	2. The register spill area is pulled by SPU
302 *	   into LS, rather than pushed by PPE.
303 *	3. All 128 registers are restored by exit().
304 *	4. The exit() function is modified at run
305 *	   time in order to properly restore the
306 *	   SPU_Status register.
307 */
308int 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	restore_decr();			/* Step 6. */
322	enqueue_putllc(lscsa_ea);	/* Step 7. */
323	set_tag_update();		/* Step 8. */
324	read_tag_status();		/* Step 9. */
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}
337