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