summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/cell/spufs/spu_restore.c
blob: 72c905f1ee7a5b29902b98f0ccbd9745d0a646ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
 * 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(moved):
	 *    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] & SPU_DECR_STATUS_RUNNING;
	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 additional 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'. */
	enqueue_putllc(lscsa_ea);	/* Step 7. */
	set_tag_update();		/* Step 8. */
	read_tag_status();		/* Step 9. */
	restore_decr();			/* moved Step 6. */
	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;
}