xref: /linux/arch/powerpc/platforms/cell/spufs/backing_ops.c (revision 0d456bad36d42d16022be045c8a53ddbb59ee478)
1 /* backing_ops.c - query/set operations on saved SPU context.
2  *
3  * Copyright (C) IBM 2005
4  * Author: Mark Nutter <mnutter@us.ibm.com>
5  *
6  * These register operations allow SPUFS to operate on saved
7  * SPU contexts rather than hardware.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 
24 #include <linux/errno.h>
25 #include <linux/sched.h>
26 #include <linux/kernel.h>
27 #include <linux/mm.h>
28 #include <linux/vmalloc.h>
29 #include <linux/smp.h>
30 #include <linux/stddef.h>
31 #include <linux/unistd.h>
32 #include <linux/poll.h>
33 
34 #include <asm/io.h>
35 #include <asm/spu.h>
36 #include <asm/spu_csa.h>
37 #include <asm/spu_info.h>
38 #include <asm/mmu_context.h>
39 #include "spufs.h"
40 
41 /*
42  * Reads/writes to various problem and priv2 registers require
43  * state changes, i.e.  generate SPU events, modify channel
44  * counts, etc.
45  */
46 
47 static void gen_spu_event(struct spu_context *ctx, u32 event)
48 {
49 	u64 ch0_cnt;
50 	u64 ch0_data;
51 	u64 ch1_data;
52 
53 	ch0_cnt = ctx->csa.spu_chnlcnt_RW[0];
54 	ch0_data = ctx->csa.spu_chnldata_RW[0];
55 	ch1_data = ctx->csa.spu_chnldata_RW[1];
56 	ctx->csa.spu_chnldata_RW[0] |= event;
57 	if ((ch0_cnt == 0) && !(ch0_data & event) && (ch1_data & event)) {
58 		ctx->csa.spu_chnlcnt_RW[0] = 1;
59 	}
60 }
61 
62 static int spu_backing_mbox_read(struct spu_context *ctx, u32 * data)
63 {
64 	u32 mbox_stat;
65 	int ret = 0;
66 
67 	spin_lock(&ctx->csa.register_lock);
68 	mbox_stat = ctx->csa.prob.mb_stat_R;
69 	if (mbox_stat & 0x0000ff) {
70 		/* Read the first available word.
71 		 * Implementation note: the depth
72 		 * of pu_mb_R is currently 1.
73 		 */
74 		*data = ctx->csa.prob.pu_mb_R;
75 		ctx->csa.prob.mb_stat_R &= ~(0x0000ff);
76 		ctx->csa.spu_chnlcnt_RW[28] = 1;
77 		gen_spu_event(ctx, MFC_PU_MAILBOX_AVAILABLE_EVENT);
78 		ret = 4;
79 	}
80 	spin_unlock(&ctx->csa.register_lock);
81 	return ret;
82 }
83 
84 static u32 spu_backing_mbox_stat_read(struct spu_context *ctx)
85 {
86 	return ctx->csa.prob.mb_stat_R;
87 }
88 
89 static unsigned int spu_backing_mbox_stat_poll(struct spu_context *ctx,
90 					  unsigned int events)
91 {
92 	int ret;
93 	u32 stat;
94 
95 	ret = 0;
96 	spin_lock_irq(&ctx->csa.register_lock);
97 	stat = ctx->csa.prob.mb_stat_R;
98 
99 	/* if the requested event is there, return the poll
100 	   mask, otherwise enable the interrupt to get notified,
101 	   but first mark any pending interrupts as done so
102 	   we don't get woken up unnecessarily */
103 
104 	if (events & (POLLIN | POLLRDNORM)) {
105 		if (stat & 0xff0000)
106 			ret |= POLLIN | POLLRDNORM;
107 		else {
108 			ctx->csa.priv1.int_stat_class2_RW &=
109 				~CLASS2_MAILBOX_INTR;
110 			ctx->csa.priv1.int_mask_class2_RW |=
111 				CLASS2_ENABLE_MAILBOX_INTR;
112 		}
113 	}
114 	if (events & (POLLOUT | POLLWRNORM)) {
115 		if (stat & 0x00ff00)
116 			ret = POLLOUT | POLLWRNORM;
117 		else {
118 			ctx->csa.priv1.int_stat_class2_RW &=
119 				~CLASS2_MAILBOX_THRESHOLD_INTR;
120 			ctx->csa.priv1.int_mask_class2_RW |=
121 				CLASS2_ENABLE_MAILBOX_THRESHOLD_INTR;
122 		}
123 	}
124 	spin_unlock_irq(&ctx->csa.register_lock);
125 	return ret;
126 }
127 
128 static int spu_backing_ibox_read(struct spu_context *ctx, u32 * data)
129 {
130 	int ret;
131 
132 	spin_lock(&ctx->csa.register_lock);
133 	if (ctx->csa.prob.mb_stat_R & 0xff0000) {
134 		/* Read the first available word.
135 		 * Implementation note: the depth
136 		 * of puint_mb_R is currently 1.
137 		 */
138 		*data = ctx->csa.priv2.puint_mb_R;
139 		ctx->csa.prob.mb_stat_R &= ~(0xff0000);
140 		ctx->csa.spu_chnlcnt_RW[30] = 1;
141 		gen_spu_event(ctx, MFC_PU_INT_MAILBOX_AVAILABLE_EVENT);
142 		ret = 4;
143 	} else {
144 		/* make sure we get woken up by the interrupt */
145 		ctx->csa.priv1.int_mask_class2_RW |= CLASS2_ENABLE_MAILBOX_INTR;
146 		ret = 0;
147 	}
148 	spin_unlock(&ctx->csa.register_lock);
149 	return ret;
150 }
151 
152 static int spu_backing_wbox_write(struct spu_context *ctx, u32 data)
153 {
154 	int ret;
155 
156 	spin_lock(&ctx->csa.register_lock);
157 	if ((ctx->csa.prob.mb_stat_R) & 0x00ff00) {
158 		int slot = ctx->csa.spu_chnlcnt_RW[29];
159 		int avail = (ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8;
160 
161 		/* We have space to write wbox_data.
162 		 * Implementation note: the depth
163 		 * of spu_mb_W is currently 4.
164 		 */
165 		BUG_ON(avail != (4 - slot));
166 		ctx->csa.spu_mailbox_data[slot] = data;
167 		ctx->csa.spu_chnlcnt_RW[29] = ++slot;
168 		ctx->csa.prob.mb_stat_R &= ~(0x00ff00);
169 		ctx->csa.prob.mb_stat_R |= (((4 - slot) & 0xff) << 8);
170 		gen_spu_event(ctx, MFC_SPU_MAILBOX_WRITTEN_EVENT);
171 		ret = 4;
172 	} else {
173 		/* make sure we get woken up by the interrupt when space
174 		   becomes available */
175 		ctx->csa.priv1.int_mask_class2_RW |=
176 			CLASS2_ENABLE_MAILBOX_THRESHOLD_INTR;
177 		ret = 0;
178 	}
179 	spin_unlock(&ctx->csa.register_lock);
180 	return ret;
181 }
182 
183 static u32 spu_backing_signal1_read(struct spu_context *ctx)
184 {
185 	return ctx->csa.spu_chnldata_RW[3];
186 }
187 
188 static void spu_backing_signal1_write(struct spu_context *ctx, u32 data)
189 {
190 	spin_lock(&ctx->csa.register_lock);
191 	if (ctx->csa.priv2.spu_cfg_RW & 0x1)
192 		ctx->csa.spu_chnldata_RW[3] |= data;
193 	else
194 		ctx->csa.spu_chnldata_RW[3] = data;
195 	ctx->csa.spu_chnlcnt_RW[3] = 1;
196 	gen_spu_event(ctx, MFC_SIGNAL_1_EVENT);
197 	spin_unlock(&ctx->csa.register_lock);
198 }
199 
200 static u32 spu_backing_signal2_read(struct spu_context *ctx)
201 {
202 	return ctx->csa.spu_chnldata_RW[4];
203 }
204 
205 static void spu_backing_signal2_write(struct spu_context *ctx, u32 data)
206 {
207 	spin_lock(&ctx->csa.register_lock);
208 	if (ctx->csa.priv2.spu_cfg_RW & 0x2)
209 		ctx->csa.spu_chnldata_RW[4] |= data;
210 	else
211 		ctx->csa.spu_chnldata_RW[4] = data;
212 	ctx->csa.spu_chnlcnt_RW[4] = 1;
213 	gen_spu_event(ctx, MFC_SIGNAL_2_EVENT);
214 	spin_unlock(&ctx->csa.register_lock);
215 }
216 
217 static void spu_backing_signal1_type_set(struct spu_context *ctx, u64 val)
218 {
219 	u64 tmp;
220 
221 	spin_lock(&ctx->csa.register_lock);
222 	tmp = ctx->csa.priv2.spu_cfg_RW;
223 	if (val)
224 		tmp |= 1;
225 	else
226 		tmp &= ~1;
227 	ctx->csa.priv2.spu_cfg_RW = tmp;
228 	spin_unlock(&ctx->csa.register_lock);
229 }
230 
231 static u64 spu_backing_signal1_type_get(struct spu_context *ctx)
232 {
233 	return ((ctx->csa.priv2.spu_cfg_RW & 1) != 0);
234 }
235 
236 static void spu_backing_signal2_type_set(struct spu_context *ctx, u64 val)
237 {
238 	u64 tmp;
239 
240 	spin_lock(&ctx->csa.register_lock);
241 	tmp = ctx->csa.priv2.spu_cfg_RW;
242 	if (val)
243 		tmp |= 2;
244 	else
245 		tmp &= ~2;
246 	ctx->csa.priv2.spu_cfg_RW = tmp;
247 	spin_unlock(&ctx->csa.register_lock);
248 }
249 
250 static u64 spu_backing_signal2_type_get(struct spu_context *ctx)
251 {
252 	return ((ctx->csa.priv2.spu_cfg_RW & 2) != 0);
253 }
254 
255 static u32 spu_backing_npc_read(struct spu_context *ctx)
256 {
257 	return ctx->csa.prob.spu_npc_RW;
258 }
259 
260 static void spu_backing_npc_write(struct spu_context *ctx, u32 val)
261 {
262 	ctx->csa.prob.spu_npc_RW = val;
263 }
264 
265 static u32 spu_backing_status_read(struct spu_context *ctx)
266 {
267 	return ctx->csa.prob.spu_status_R;
268 }
269 
270 static char *spu_backing_get_ls(struct spu_context *ctx)
271 {
272 	return ctx->csa.lscsa->ls;
273 }
274 
275 static void spu_backing_privcntl_write(struct spu_context *ctx, u64 val)
276 {
277 	ctx->csa.priv2.spu_privcntl_RW = val;
278 }
279 
280 static u32 spu_backing_runcntl_read(struct spu_context *ctx)
281 {
282 	return ctx->csa.prob.spu_runcntl_RW;
283 }
284 
285 static void spu_backing_runcntl_write(struct spu_context *ctx, u32 val)
286 {
287 	spin_lock(&ctx->csa.register_lock);
288 	ctx->csa.prob.spu_runcntl_RW = val;
289 	if (val & SPU_RUNCNTL_RUNNABLE) {
290 		ctx->csa.prob.spu_status_R &=
291 			~SPU_STATUS_STOPPED_BY_STOP &
292 			~SPU_STATUS_STOPPED_BY_HALT &
293 			~SPU_STATUS_SINGLE_STEP &
294 			~SPU_STATUS_INVALID_INSTR &
295 			~SPU_STATUS_INVALID_CH;
296 		ctx->csa.prob.spu_status_R |= SPU_STATUS_RUNNING;
297 	} else {
298 		ctx->csa.prob.spu_status_R &= ~SPU_STATUS_RUNNING;
299 	}
300 	spin_unlock(&ctx->csa.register_lock);
301 }
302 
303 static void spu_backing_runcntl_stop(struct spu_context *ctx)
304 {
305 	spu_backing_runcntl_write(ctx, SPU_RUNCNTL_STOP);
306 }
307 
308 static void spu_backing_master_start(struct spu_context *ctx)
309 {
310 	struct spu_state *csa = &ctx->csa;
311 	u64 sr1;
312 
313 	spin_lock(&csa->register_lock);
314 	sr1 = csa->priv1.mfc_sr1_RW | MFC_STATE1_MASTER_RUN_CONTROL_MASK;
315 	csa->priv1.mfc_sr1_RW = sr1;
316 	spin_unlock(&csa->register_lock);
317 }
318 
319 static void spu_backing_master_stop(struct spu_context *ctx)
320 {
321 	struct spu_state *csa = &ctx->csa;
322 	u64 sr1;
323 
324 	spin_lock(&csa->register_lock);
325 	sr1 = csa->priv1.mfc_sr1_RW & ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
326 	csa->priv1.mfc_sr1_RW = sr1;
327 	spin_unlock(&csa->register_lock);
328 }
329 
330 static int spu_backing_set_mfc_query(struct spu_context * ctx, u32 mask,
331 					u32 mode)
332 {
333 	struct spu_problem_collapsed *prob = &ctx->csa.prob;
334 	int ret;
335 
336 	spin_lock(&ctx->csa.register_lock);
337 	ret = -EAGAIN;
338 	if (prob->dma_querytype_RW)
339 		goto out;
340 	ret = 0;
341 	/* FIXME: what are the side-effects of this? */
342 	prob->dma_querymask_RW = mask;
343 	prob->dma_querytype_RW = mode;
344 	/* In the current implementation, the SPU context is always
345 	 * acquired in runnable state when new bits are added to the
346 	 * mask (tagwait), so it's sufficient just to mask
347 	 * dma_tagstatus_R with the 'mask' parameter here.
348 	 */
349 	ctx->csa.prob.dma_tagstatus_R &= mask;
350 out:
351 	spin_unlock(&ctx->csa.register_lock);
352 
353 	return ret;
354 }
355 
356 static u32 spu_backing_read_mfc_tagstatus(struct spu_context * ctx)
357 {
358 	return ctx->csa.prob.dma_tagstatus_R;
359 }
360 
361 static u32 spu_backing_get_mfc_free_elements(struct spu_context *ctx)
362 {
363 	return ctx->csa.prob.dma_qstatus_R;
364 }
365 
366 static int spu_backing_send_mfc_command(struct spu_context *ctx,
367 					struct mfc_dma_command *cmd)
368 {
369 	int ret;
370 
371 	spin_lock(&ctx->csa.register_lock);
372 	ret = -EAGAIN;
373 	/* FIXME: set up priv2->puq */
374 	spin_unlock(&ctx->csa.register_lock);
375 
376 	return ret;
377 }
378 
379 static void spu_backing_restart_dma(struct spu_context *ctx)
380 {
381 	ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_RESTART_DMA_COMMAND;
382 }
383 
384 struct spu_context_ops spu_backing_ops = {
385 	.mbox_read = spu_backing_mbox_read,
386 	.mbox_stat_read = spu_backing_mbox_stat_read,
387 	.mbox_stat_poll = spu_backing_mbox_stat_poll,
388 	.ibox_read = spu_backing_ibox_read,
389 	.wbox_write = spu_backing_wbox_write,
390 	.signal1_read = spu_backing_signal1_read,
391 	.signal1_write = spu_backing_signal1_write,
392 	.signal2_read = spu_backing_signal2_read,
393 	.signal2_write = spu_backing_signal2_write,
394 	.signal1_type_set = spu_backing_signal1_type_set,
395 	.signal1_type_get = spu_backing_signal1_type_get,
396 	.signal2_type_set = spu_backing_signal2_type_set,
397 	.signal2_type_get = spu_backing_signal2_type_get,
398 	.npc_read = spu_backing_npc_read,
399 	.npc_write = spu_backing_npc_write,
400 	.status_read = spu_backing_status_read,
401 	.get_ls = spu_backing_get_ls,
402 	.privcntl_write = spu_backing_privcntl_write,
403 	.runcntl_read = spu_backing_runcntl_read,
404 	.runcntl_write = spu_backing_runcntl_write,
405 	.runcntl_stop = spu_backing_runcntl_stop,
406 	.master_start = spu_backing_master_start,
407 	.master_stop = spu_backing_master_stop,
408 	.set_mfc_query = spu_backing_set_mfc_query,
409 	.read_mfc_tagstatus = spu_backing_read_mfc_tagstatus,
410 	.get_mfc_free_elements = spu_backing_get_mfc_free_elements,
411 	.send_mfc_command = spu_backing_send_mfc_command,
412 	.restart_dma = spu_backing_restart_dma,
413 };
414