linux/arch/powerpc/platforms/cell/spufs/spu_restore.c
<<
>>
Prefs
   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(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
 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        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}
 337