1ce8ab854SArnd Bergmann #include <linux/wait.h>
2ce8ab854SArnd Bergmann #include <linux/ptrace.h>
3ce8ab854SArnd Bergmann 
4ce8ab854SArnd Bergmann #include <asm/spu.h>
5ce8ab854SArnd Bergmann 
6ce8ab854SArnd Bergmann #include "spufs.h"
7ce8ab854SArnd Bergmann 
8ce8ab854SArnd Bergmann /* interrupt-level stop callback function. */
9ce8ab854SArnd Bergmann void spufs_stop_callback(struct spu *spu)
10ce8ab854SArnd Bergmann {
11ce8ab854SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
12ce8ab854SArnd Bergmann 
13ce8ab854SArnd Bergmann 	wake_up_all(&ctx->stop_wq);
14ce8ab854SArnd Bergmann }
15ce8ab854SArnd Bergmann 
16ce8ab854SArnd Bergmann static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
17ce8ab854SArnd Bergmann {
18ce8ab854SArnd Bergmann 	struct spu *spu;
19ce8ab854SArnd Bergmann 	u64 pte_fault;
20ce8ab854SArnd Bergmann 
21ce8ab854SArnd Bergmann 	*stat = ctx->ops->status_read(ctx);
22ce8ab854SArnd Bergmann 	if (ctx->state != SPU_STATE_RUNNABLE)
23ce8ab854SArnd Bergmann 		return 1;
24ce8ab854SArnd Bergmann 	spu = ctx->spu;
25ce8ab854SArnd Bergmann 	pte_fault = spu->dsisr &
26ce8ab854SArnd Bergmann 	    (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
27ce8ab854SArnd Bergmann 	return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
28ce8ab854SArnd Bergmann }
29ce8ab854SArnd Bergmann 
30ce8ab854SArnd Bergmann static inline int spu_run_init(struct spu_context *ctx, u32 * npc,
31ce8ab854SArnd Bergmann 			       u32 * status)
32ce8ab854SArnd Bergmann {
33ce8ab854SArnd Bergmann 	int ret;
34ce8ab854SArnd Bergmann 
35ce8ab854SArnd Bergmann 	if ((ret = spu_acquire_runnable(ctx)) != 0)
36ce8ab854SArnd Bergmann 		return ret;
37ce8ab854SArnd Bergmann 	ctx->ops->npc_write(ctx, *npc);
38ce8ab854SArnd Bergmann 	ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
39ce8ab854SArnd Bergmann 	return 0;
40ce8ab854SArnd Bergmann }
41ce8ab854SArnd Bergmann 
42ce8ab854SArnd Bergmann static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
43ce8ab854SArnd Bergmann 			       u32 * status)
44ce8ab854SArnd Bergmann {
45ce8ab854SArnd Bergmann 	int ret = 0;
46ce8ab854SArnd Bergmann 
47ce8ab854SArnd Bergmann 	*status = ctx->ops->status_read(ctx);
48ce8ab854SArnd Bergmann 	*npc = ctx->ops->npc_read(ctx);
49ce8ab854SArnd Bergmann 	spu_release(ctx);
50ce8ab854SArnd Bergmann 
51ce8ab854SArnd Bergmann 	if (signal_pending(current))
52ce8ab854SArnd Bergmann 		ret = -ERESTARTSYS;
53ce8ab854SArnd Bergmann 	if (unlikely(current->ptrace & PT_PTRACED)) {
54ce8ab854SArnd Bergmann 		if ((*status & SPU_STATUS_STOPPED_BY_STOP)
55ce8ab854SArnd Bergmann 		    && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
56ce8ab854SArnd Bergmann 			force_sig(SIGTRAP, current);
57ce8ab854SArnd Bergmann 			ret = -ERESTARTSYS;
58ce8ab854SArnd Bergmann 		}
59ce8ab854SArnd Bergmann 	}
60ce8ab854SArnd Bergmann 	return ret;
61ce8ab854SArnd Bergmann }
62ce8ab854SArnd Bergmann 
63ce8ab854SArnd Bergmann static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
64ce8ab854SArnd Bergmann 				         u32 *status)
65ce8ab854SArnd Bergmann {
66ce8ab854SArnd Bergmann 	int ret;
67ce8ab854SArnd Bergmann 
68ce8ab854SArnd Bergmann 	if ((ret = spu_run_fini(ctx, npc, status)) != 0)
69ce8ab854SArnd Bergmann 		return ret;
70ce8ab854SArnd Bergmann 	if (*status & (SPU_STATUS_STOPPED_BY_STOP |
71ce8ab854SArnd Bergmann 		       SPU_STATUS_STOPPED_BY_HALT)) {
72ce8ab854SArnd Bergmann 		return *status;
73ce8ab854SArnd Bergmann 	}
74ce8ab854SArnd Bergmann 	if ((ret = spu_run_init(ctx, npc, status)) != 0)
75ce8ab854SArnd Bergmann 		return ret;
76ce8ab854SArnd Bergmann 	return 0;
77ce8ab854SArnd Bergmann }
78ce8ab854SArnd Bergmann 
79ce8ab854SArnd Bergmann static inline int spu_process_events(struct spu_context *ctx)
80ce8ab854SArnd Bergmann {
81ce8ab854SArnd Bergmann 	struct spu *spu = ctx->spu;
82ce8ab854SArnd Bergmann 	u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
83ce8ab854SArnd Bergmann 	int ret = 0;
84ce8ab854SArnd Bergmann 
85ce8ab854SArnd Bergmann 	if (spu->dsisr & pte_fault)
86ce8ab854SArnd Bergmann 		ret = spu_irq_class_1_bottom(spu);
87ce8ab854SArnd Bergmann 	if (spu->class_0_pending)
88ce8ab854SArnd Bergmann 		ret = spu_irq_class_0_bottom(spu);
89ce8ab854SArnd Bergmann 	if (!ret && signal_pending(current))
90ce8ab854SArnd Bergmann 		ret = -ERESTARTSYS;
91ce8ab854SArnd Bergmann 	return ret;
92ce8ab854SArnd Bergmann }
93ce8ab854SArnd Bergmann 
94ce8ab854SArnd Bergmann long spufs_run_spu(struct file *file, struct spu_context *ctx,
95ce8ab854SArnd Bergmann 		   u32 * npc, u32 * status)
96ce8ab854SArnd Bergmann {
97ce8ab854SArnd Bergmann 	int ret;
98ce8ab854SArnd Bergmann 
99ce8ab854SArnd Bergmann 	if (down_interruptible(&ctx->run_sema))
100ce8ab854SArnd Bergmann 		return -ERESTARTSYS;
101ce8ab854SArnd Bergmann 
102ce8ab854SArnd Bergmann 	ret = spu_run_init(ctx, npc, status);
103ce8ab854SArnd Bergmann 	if (ret)
104ce8ab854SArnd Bergmann 		goto out;
105ce8ab854SArnd Bergmann 
106ce8ab854SArnd Bergmann 	do {
107ce8ab854SArnd Bergmann 		ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status));
108ce8ab854SArnd Bergmann 		if (unlikely(ret))
109ce8ab854SArnd Bergmann 			break;
110ce8ab854SArnd Bergmann 		if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
111ce8ab854SArnd Bergmann 			ret = spu_reacquire_runnable(ctx, npc, status);
112ce8ab854SArnd Bergmann 			if (ret)
113ce8ab854SArnd Bergmann 				goto out;
114ce8ab854SArnd Bergmann 			continue;
115ce8ab854SArnd Bergmann 		}
116ce8ab854SArnd Bergmann 		ret = spu_process_events(ctx);
117ce8ab854SArnd Bergmann 
118ce8ab854SArnd Bergmann 	} while (!ret && !(*status & (SPU_STATUS_STOPPED_BY_STOP |
119ce8ab854SArnd Bergmann 				      SPU_STATUS_STOPPED_BY_HALT)));
120ce8ab854SArnd Bergmann 
121ce8ab854SArnd Bergmann 	ctx->ops->runcntl_stop(ctx);
122ce8ab854SArnd Bergmann 	ret = spu_run_fini(ctx, npc, status);
123ce8ab854SArnd Bergmann 	if (!ret)
124ce8ab854SArnd Bergmann 		ret = *status;
125ce8ab854SArnd Bergmann 	spu_yield(ctx);
126ce8ab854SArnd Bergmann 
127ce8ab854SArnd Bergmann out:
128ce8ab854SArnd Bergmann 	up(&ctx->run_sema);
129ce8ab854SArnd Bergmann 	return ret;
130ce8ab854SArnd Bergmann }
131ce8ab854SArnd Bergmann 
132