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
47static 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
62static 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
84static u32 spu_backing_mbox_stat_read(struct spu_context *ctx)
85{
86	return ctx->csa.prob.mb_stat_R;
87}
88
89static 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
128static 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
152static 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
183static u32 spu_backing_signal1_read(struct spu_context *ctx)
184{
185	return ctx->csa.spu_chnldata_RW[3];
186}
187
188static 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
200static u32 spu_backing_signal2_read(struct spu_context *ctx)
201{
202	return ctx->csa.spu_chnldata_RW[4];
203}
204
205static 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
217static 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
231static u64 spu_backing_signal1_type_get(struct spu_context *ctx)
232{
233	return ((ctx->csa.priv2.spu_cfg_RW & 1) != 0);
234}
235
236static 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
250static u64 spu_backing_signal2_type_get(struct spu_context *ctx)
251{
252	return ((ctx->csa.priv2.spu_cfg_RW & 2) != 0);
253}
254
255static u32 spu_backing_npc_read(struct spu_context *ctx)
256{
257	return ctx->csa.prob.spu_npc_RW;
258}
259
260static void spu_backing_npc_write(struct spu_context *ctx, u32 val)
261{
262	ctx->csa.prob.spu_npc_RW = val;
263}
264
265static u32 spu_backing_status_read(struct spu_context *ctx)
266{
267	return ctx->csa.prob.spu_status_R;
268}
269
270static char *spu_backing_get_ls(struct spu_context *ctx)
271{
272	return ctx->csa.lscsa->ls;
273}
274
275static void spu_backing_privcntl_write(struct spu_context *ctx, u64 val)
276{
277	ctx->csa.priv2.spu_privcntl_RW = val;
278}
279
280static u32 spu_backing_runcntl_read(struct spu_context *ctx)
281{
282	return ctx->csa.prob.spu_runcntl_RW;
283}
284
285static 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
303static void spu_backing_runcntl_stop(struct spu_context *ctx)
304{
305	spu_backing_runcntl_write(ctx, SPU_RUNCNTL_STOP);
306}
307
308static 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
319static 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
330static 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;
350out:
351	spin_unlock(&ctx->csa.register_lock);
352
353	return ret;
354}
355
356static u32 spu_backing_read_mfc_tagstatus(struct spu_context * ctx)
357{
358	return ctx->csa.prob.dma_tagstatus_R;
359}
360
361static u32 spu_backing_get_mfc_free_elements(struct spu_context *ctx)
362{
363	return ctx->csa.prob.dma_qstatus_R;
364}
365
366static 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
379static void spu_backing_restart_dma(struct spu_context *ctx)
380{
381	ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_RESTART_DMA_COMMAND;
382}
383
384struct 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