blob: e2c9d48a68047730a4039748916fdd6adda55bdc [file] [log] [blame]
Arnd Bergmann67207b92005-11-15 15:53:48 -05001/*
2 * SPU file system -- file contents
3 *
4 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
5 *
6 * Author: Arnd Bergmann <arndb@de.ibm.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
Arnd Bergmanna33a7d72006-03-23 00:00:11 +010023#undef DEBUG
24
Arnd Bergmann67207b92005-11-15 15:53:48 -050025#include <linux/fs.h>
26#include <linux/ioctl.h>
27#include <linux/module.h>
Arnd Bergmannd88cfff2005-12-05 22:52:22 -050028#include <linux/pagemap.h>
Arnd Bergmann67207b92005-11-15 15:53:48 -050029#include <linux/poll.h>
Arnd Bergmann51104592005-12-05 22:52:25 -050030#include <linux/ptrace.h>
Arnd Bergmann67207b92005-11-15 15:53:48 -050031
32#include <asm/io.h>
33#include <asm/semaphore.h>
34#include <asm/spu.h>
35#include <asm/uaccess.h>
36
37#include "spufs.h"
38
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +020039#define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000)
40
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050041
Arnd Bergmann67207b92005-11-15 15:53:48 -050042static int
43spufs_mem_open(struct inode *inode, struct file *file)
44{
45 struct spufs_inode_info *i = SPUFS_I(inode);
Mark Nutter6df10a82006-03-23 00:00:12 +010046 struct spu_context *ctx = i->i_ctx;
47 file->private_data = ctx;
48 file->f_mapping = inode->i_mapping;
49 ctx->local_store = inode->i_mapping;
Arnd Bergmann67207b92005-11-15 15:53:48 -050050 return 0;
51}
52
53static ssize_t
54spufs_mem_read(struct file *file, char __user *buffer,
55 size_t size, loff_t *pos)
56{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050057 struct spu_context *ctx = file->private_data;
58 char *local_store;
Arnd Bergmann67207b92005-11-15 15:53:48 -050059 int ret;
60
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050061 spu_acquire(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -050062
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050063 local_store = ctx->ops->get_ls(ctx);
64 ret = simple_read_from_buffer(buffer, size, pos, local_store, LS_SIZE);
Arnd Bergmann67207b92005-11-15 15:53:48 -050065
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050066 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -050067 return ret;
68}
69
70static ssize_t
71spufs_mem_write(struct file *file, const char __user *buffer,
72 size_t size, loff_t *pos)
73{
74 struct spu_context *ctx = file->private_data;
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050075 char *local_store;
76 int ret;
Arnd Bergmann67207b92005-11-15 15:53:48 -050077
78 size = min_t(ssize_t, LS_SIZE - *pos, size);
79 if (size <= 0)
80 return -EFBIG;
81 *pos += size;
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050082
83 spu_acquire(ctx);
84
85 local_store = ctx->ops->get_ls(ctx);
86 ret = copy_from_user(local_store + *pos - size,
87 buffer, size) ? -EFAULT : size;
88
89 spu_release(ctx);
90 return ret;
Arnd Bergmann67207b92005-11-15 15:53:48 -050091}
92
Arnd Bergmann8b3d6662005-11-15 15:53:52 -050093static struct page *
94spufs_mem_mmap_nopage(struct vm_area_struct *vma,
95 unsigned long address, int *type)
96{
97 struct page *page = NOPAGE_SIGBUS;
98
99 struct spu_context *ctx = vma->vm_file->private_data;
100 unsigned long offset = address - vma->vm_start;
101 offset += vma->vm_pgoff << PAGE_SHIFT;
102
103 spu_acquire(ctx);
104
Arnd Bergmannac91cb82006-10-04 17:26:16 +0200105 if (ctx->state == SPU_STATE_SAVED) {
106 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
107 & ~(_PAGE_NO_CACHE | _PAGE_GUARDED));
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500108 page = vmalloc_to_page(ctx->csa.lscsa->ls + offset);
Arnd Bergmannac91cb82006-10-04 17:26:16 +0200109 } else {
110 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
111 | _PAGE_NO_CACHE | _PAGE_GUARDED);
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500112 page = pfn_to_page((ctx->spu->local_store_phys + offset)
113 >> PAGE_SHIFT);
Arnd Bergmannac91cb82006-10-04 17:26:16 +0200114 }
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500115 spu_release(ctx);
116
117 if (type)
118 *type = VM_FAULT_MINOR;
119
Arnd Bergmannd88cfff2005-12-05 22:52:22 -0500120 page_cache_get(page);
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500121 return page;
122}
123
124static struct vm_operations_struct spufs_mem_mmap_vmops = {
125 .nopage = spufs_mem_mmap_nopage,
126};
127
Arnd Bergmann67207b92005-11-15 15:53:48 -0500128static int
129spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
130{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500131 if (!(vma->vm_flags & VM_SHARED))
132 return -EINVAL;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500133
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500134 /* FIXME: */
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500135 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
136 | _PAGE_NO_CACHE);
137
138 vma->vm_ops = &spufs_mem_mmap_vmops;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500139 return 0;
140}
141
142static struct file_operations spufs_mem_fops = {
143 .open = spufs_mem_open,
144 .read = spufs_mem_read,
145 .write = spufs_mem_write,
Arnd Bergmann67207b92005-11-15 15:53:48 -0500146 .llseek = generic_file_llseek,
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500147 .mmap = spufs_mem_mmap,
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500148};
149
Mark Nutter6df10a82006-03-23 00:00:12 +0100150static struct page *spufs_ps_nopage(struct vm_area_struct *vma,
151 unsigned long address,
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200152 int *type, unsigned long ps_offs,
153 unsigned long ps_size)
Mark Nutter6df10a82006-03-23 00:00:12 +0100154{
155 struct page *page = NOPAGE_SIGBUS;
156 int fault_type = VM_FAULT_SIGBUS;
157 struct spu_context *ctx = vma->vm_file->private_data;
158 unsigned long offset = address - vma->vm_start;
159 unsigned long area;
160 int ret;
161
162 offset += vma->vm_pgoff << PAGE_SHIFT;
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200163 if (offset >= ps_size)
Mark Nutter6df10a82006-03-23 00:00:12 +0100164 goto out;
165
166 ret = spu_acquire_runnable(ctx);
167 if (ret)
168 goto out;
169
170 area = ctx->spu->problem_phys + ps_offs;
171 page = pfn_to_page((area + offset) >> PAGE_SHIFT);
172 fault_type = VM_FAULT_MINOR;
173 page_cache_get(page);
174
175 spu_release(ctx);
176
177 out:
178 if (type)
179 *type = fault_type;
180
181 return page;
182}
183
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200184#if SPUFS_MMAP_4K
Mark Nutter6df10a82006-03-23 00:00:12 +0100185static struct page *spufs_cntl_mmap_nopage(struct vm_area_struct *vma,
186 unsigned long address, int *type)
187{
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200188 return spufs_ps_nopage(vma, address, type, 0x4000, 0x1000);
Mark Nutter6df10a82006-03-23 00:00:12 +0100189}
190
191static struct vm_operations_struct spufs_cntl_mmap_vmops = {
192 .nopage = spufs_cntl_mmap_nopage,
193};
194
195/*
196 * mmap support for problem state control area [0x4000 - 0x4fff].
Mark Nutter6df10a82006-03-23 00:00:12 +0100197 */
198static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
199{
200 if (!(vma->vm_flags & VM_SHARED))
201 return -EINVAL;
202
Mark Nutter6df10a82006-03-23 00:00:12 +0100203 vma->vm_flags |= VM_RESERVED;
204 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
Benjamin Herrenschmidt23cc7702006-06-23 20:57:47 +0200205 | _PAGE_NO_CACHE | _PAGE_GUARDED);
Mark Nutter6df10a82006-03-23 00:00:12 +0100206
207 vma->vm_ops = &spufs_cntl_mmap_vmops;
208 return 0;
209}
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200210#else /* SPUFS_MMAP_4K */
211#define spufs_cntl_mmap NULL
212#endif /* !SPUFS_MMAP_4K */
Mark Nutter6df10a82006-03-23 00:00:12 +0100213
Arnd Bergmanne1dbff22006-10-04 17:26:19 +0200214static u64 spufs_cntl_get(void *data)
215{
216 struct spu_context *ctx = data;
217 u64 val;
218
219 spu_acquire(ctx);
220 val = ctx->ops->status_read(ctx);
221 spu_release(ctx);
222
223 return val;
224}
225
226static void spufs_cntl_set(void *data, u64 val)
227{
228 struct spu_context *ctx = data;
229
230 spu_acquire(ctx);
231 ctx->ops->runcntl_write(ctx, val);
232 spu_release(ctx);
233}
234
Mark Nutter6df10a82006-03-23 00:00:12 +0100235static int spufs_cntl_open(struct inode *inode, struct file *file)
236{
237 struct spufs_inode_info *i = SPUFS_I(inode);
238 struct spu_context *ctx = i->i_ctx;
239
240 file->private_data = ctx;
241 file->f_mapping = inode->i_mapping;
242 ctx->cntl = inode->i_mapping;
Arnd Bergmanne1dbff22006-10-04 17:26:19 +0200243 return simple_attr_open(inode, file, spufs_cntl_get,
244 spufs_cntl_set, "0x%08lx");
Mark Nutter6df10a82006-03-23 00:00:12 +0100245}
246
247static struct file_operations spufs_cntl_fops = {
248 .open = spufs_cntl_open,
Arnd Bergmanne1dbff22006-10-04 17:26:19 +0200249 .read = simple_attr_read,
250 .write = simple_attr_write,
Mark Nutter6df10a82006-03-23 00:00:12 +0100251 .mmap = spufs_cntl_mmap,
Mark Nutter6df10a82006-03-23 00:00:12 +0100252};
253
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500254static int
255spufs_regs_open(struct inode *inode, struct file *file)
256{
257 struct spufs_inode_info *i = SPUFS_I(inode);
258 file->private_data = i->i_ctx;
259 return 0;
260}
261
262static ssize_t
263spufs_regs_read(struct file *file, char __user *buffer,
264 size_t size, loff_t *pos)
265{
266 struct spu_context *ctx = file->private_data;
267 struct spu_lscsa *lscsa = ctx->csa.lscsa;
268 int ret;
269
270 spu_acquire_saved(ctx);
271
272 ret = simple_read_from_buffer(buffer, size, pos,
273 lscsa->gprs, sizeof lscsa->gprs);
274
275 spu_release(ctx);
276 return ret;
277}
278
279static ssize_t
280spufs_regs_write(struct file *file, const char __user *buffer,
281 size_t size, loff_t *pos)
282{
283 struct spu_context *ctx = file->private_data;
284 struct spu_lscsa *lscsa = ctx->csa.lscsa;
285 int ret;
286
287 size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size);
288 if (size <= 0)
289 return -EFBIG;
290 *pos += size;
291
292 spu_acquire_saved(ctx);
293
294 ret = copy_from_user(lscsa->gprs + *pos - size,
295 buffer, size) ? -EFAULT : size;
296
297 spu_release(ctx);
298 return ret;
299}
300
301static struct file_operations spufs_regs_fops = {
302 .open = spufs_regs_open,
303 .read = spufs_regs_read,
304 .write = spufs_regs_write,
305 .llseek = generic_file_llseek,
306};
307
308static ssize_t
309spufs_fpcr_read(struct file *file, char __user * buffer,
310 size_t size, loff_t * pos)
311{
312 struct spu_context *ctx = file->private_data;
313 struct spu_lscsa *lscsa = ctx->csa.lscsa;
314 int ret;
315
316 spu_acquire_saved(ctx);
317
318 ret = simple_read_from_buffer(buffer, size, pos,
319 &lscsa->fpcr, sizeof(lscsa->fpcr));
320
321 spu_release(ctx);
322 return ret;
323}
324
325static ssize_t
326spufs_fpcr_write(struct file *file, const char __user * buffer,
327 size_t size, loff_t * pos)
328{
329 struct spu_context *ctx = file->private_data;
330 struct spu_lscsa *lscsa = ctx->csa.lscsa;
331 int ret;
332
333 size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
334 if (size <= 0)
335 return -EFBIG;
336 *pos += size;
337
338 spu_acquire_saved(ctx);
339
340 ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
341 buffer, size) ? -EFAULT : size;
342
343 spu_release(ctx);
344 return ret;
345}
346
347static struct file_operations spufs_fpcr_fops = {
348 .open = spufs_regs_open,
349 .read = spufs_fpcr_read,
350 .write = spufs_fpcr_write,
351 .llseek = generic_file_llseek,
Arnd Bergmann67207b92005-11-15 15:53:48 -0500352};
353
354/* generic open function for all pipe-like files */
355static int spufs_pipe_open(struct inode *inode, struct file *file)
356{
357 struct spufs_inode_info *i = SPUFS_I(inode);
358 file->private_data = i->i_ctx;
359
360 return nonseekable_open(inode, file);
361}
362
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200363/*
364 * Read as many bytes from the mailbox as possible, until
365 * one of the conditions becomes true:
366 *
367 * - no more data available in the mailbox
368 * - end of the user provided buffer
369 * - end of the mapped area
370 */
Arnd Bergmann67207b92005-11-15 15:53:48 -0500371static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
372 size_t len, loff_t *pos)
373{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500374 struct spu_context *ctx = file->private_data;
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200375 u32 mbox_data, __user *udata;
376 ssize_t count;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500377
378 if (len < 4)
379 return -EINVAL;
380
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200381 if (!access_ok(VERIFY_WRITE, buf, len))
Arnd Bergmann67207b92005-11-15 15:53:48 -0500382 return -EFAULT;
383
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200384 udata = (void __user *)buf;
385
386 spu_acquire(ctx);
387 for (count = 0; count <= len; count += 4, udata++) {
388 int ret;
389 ret = ctx->ops->mbox_read(ctx, &mbox_data);
390 if (ret == 0)
391 break;
392
393 /*
394 * at the end of the mapped area, we can fault
395 * but still need to return the data we have
396 * read successfully so far.
397 */
398 ret = __put_user(mbox_data, udata);
399 if (ret) {
400 if (!count)
401 count = -EFAULT;
402 break;
403 }
404 }
405 spu_release(ctx);
406
407 if (!count)
408 count = -EAGAIN;
409
410 return count;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500411}
412
413static struct file_operations spufs_mbox_fops = {
414 .open = spufs_pipe_open,
415 .read = spufs_mbox_read,
416};
417
418static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
419 size_t len, loff_t *pos)
420{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500421 struct spu_context *ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500422 u32 mbox_stat;
423
424 if (len < 4)
425 return -EINVAL;
426
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500427 spu_acquire(ctx);
428
429 mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff;
430
431 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500432
433 if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat))
434 return -EFAULT;
435
436 return 4;
437}
438
439static struct file_operations spufs_mbox_stat_fops = {
440 .open = spufs_pipe_open,
441 .read = spufs_mbox_stat_read,
442};
443
444/* low-level ibox access function */
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500445size_t spu_ibox_read(struct spu_context *ctx, u32 *data)
Arnd Bergmann67207b92005-11-15 15:53:48 -0500446{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500447 return ctx->ops->ibox_read(ctx, data);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500448}
Arnd Bergmann67207b92005-11-15 15:53:48 -0500449
450static int spufs_ibox_fasync(int fd, struct file *file, int on)
451{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500452 struct spu_context *ctx = file->private_data;
453
454 return fasync_helper(fd, file, on, &ctx->ibox_fasync);
455}
456
457/* interrupt-level ibox callback function. */
458void spufs_ibox_callback(struct spu *spu)
459{
460 struct spu_context *ctx = spu->ctx;
461
462 wake_up_all(&ctx->ibox_wq);
463 kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500464}
465
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200466/*
467 * Read as many bytes from the interrupt mailbox as possible, until
468 * one of the conditions becomes true:
469 *
470 * - no more data available in the mailbox
471 * - end of the user provided buffer
472 * - end of the mapped area
473 *
474 * If the file is opened without O_NONBLOCK, we wait here until
475 * any data is available, but return when we have been able to
476 * read something.
477 */
Arnd Bergmann67207b92005-11-15 15:53:48 -0500478static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
479 size_t len, loff_t *pos)
480{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500481 struct spu_context *ctx = file->private_data;
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200482 u32 ibox_data, __user *udata;
483 ssize_t count;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500484
485 if (len < 4)
486 return -EINVAL;
487
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200488 if (!access_ok(VERIFY_WRITE, buf, len))
489 return -EFAULT;
490
491 udata = (void __user *)buf;
492
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500493 spu_acquire(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500494
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200495 /* wait only for the first element */
496 count = 0;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500497 if (file->f_flags & O_NONBLOCK) {
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500498 if (!spu_ibox_read(ctx, &ibox_data))
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200499 count = -EAGAIN;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500500 } else {
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200501 count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
502 }
503 if (count)
504 goto out;
505
506 /* if we can't write at all, return -EFAULT */
507 count = __put_user(ibox_data, udata);
508 if (count)
509 goto out;
510
511 for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
512 int ret;
513 ret = ctx->ops->ibox_read(ctx, &ibox_data);
514 if (ret == 0)
515 break;
516 /*
517 * at the end of the mapped area, we can fault
518 * but still need to return the data we have
519 * read successfully so far.
520 */
521 ret = __put_user(ibox_data, udata);
522 if (ret)
523 break;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500524 }
525
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200526out:
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500527 spu_release(ctx);
528
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200529 return count;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500530}
531
532static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait)
533{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500534 struct spu_context *ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500535 unsigned int mask;
536
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500537 poll_wait(file, &ctx->ibox_wq, wait);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500538
Arnd Bergmann3a843d72005-12-05 22:52:27 -0500539 spu_acquire(ctx);
540 mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM);
541 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500542
543 return mask;
544}
545
546static struct file_operations spufs_ibox_fops = {
547 .open = spufs_pipe_open,
548 .read = spufs_ibox_read,
549 .poll = spufs_ibox_poll,
550 .fasync = spufs_ibox_fasync,
551};
552
553static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
554 size_t len, loff_t *pos)
555{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500556 struct spu_context *ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500557 u32 ibox_stat;
558
559 if (len < 4)
560 return -EINVAL;
561
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500562 spu_acquire(ctx);
563 ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff;
564 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500565
566 if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat))
567 return -EFAULT;
568
569 return 4;
570}
571
572static struct file_operations spufs_ibox_stat_fops = {
573 .open = spufs_pipe_open,
574 .read = spufs_ibox_stat_read,
575};
576
577/* low-level mailbox write */
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500578size_t spu_wbox_write(struct spu_context *ctx, u32 data)
Arnd Bergmann67207b92005-11-15 15:53:48 -0500579{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500580 return ctx->ops->wbox_write(ctx, data);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500581}
Arnd Bergmann67207b92005-11-15 15:53:48 -0500582
583static int spufs_wbox_fasync(int fd, struct file *file, int on)
584{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500585 struct spu_context *ctx = file->private_data;
586 int ret;
587
588 ret = fasync_helper(fd, file, on, &ctx->wbox_fasync);
589
590 return ret;
591}
592
593/* interrupt-level wbox callback function. */
594void spufs_wbox_callback(struct spu *spu)
595{
596 struct spu_context *ctx = spu->ctx;
597
598 wake_up_all(&ctx->wbox_wq);
599 kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500600}
601
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200602/*
603 * Write as many bytes to the interrupt mailbox as possible, until
604 * one of the conditions becomes true:
605 *
606 * - the mailbox is full
607 * - end of the user provided buffer
608 * - end of the mapped area
609 *
610 * If the file is opened without O_NONBLOCK, we wait here until
611 * space is availabyl, but return when we have been able to
612 * write something.
613 */
Arnd Bergmann67207b92005-11-15 15:53:48 -0500614static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
615 size_t len, loff_t *pos)
616{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500617 struct spu_context *ctx = file->private_data;
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200618 u32 wbox_data, __user *udata;
619 ssize_t count;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500620
621 if (len < 4)
622 return -EINVAL;
623
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200624 udata = (void __user *)buf;
625 if (!access_ok(VERIFY_READ, buf, len))
626 return -EFAULT;
627
628 if (__get_user(wbox_data, udata))
Arnd Bergmann67207b92005-11-15 15:53:48 -0500629 return -EFAULT;
630
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500631 spu_acquire(ctx);
632
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200633 /*
634 * make sure we can at least write one element, by waiting
635 * in case of !O_NONBLOCK
636 */
637 count = 0;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500638 if (file->f_flags & O_NONBLOCK) {
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500639 if (!spu_wbox_write(ctx, wbox_data))
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200640 count = -EAGAIN;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500641 } else {
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200642 count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
Arnd Bergmann67207b92005-11-15 15:53:48 -0500643 }
644
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200645 if (count)
646 goto out;
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500647
Arnd Bergmanncdcc89b2006-10-04 17:26:17 +0200648 /* write aѕ much as possible */
649 for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
650 int ret;
651 ret = __get_user(wbox_data, udata);
652 if (ret)
653 break;
654
655 ret = spu_wbox_write(ctx, wbox_data);
656 if (ret == 0)
657 break;
658 }
659
660out:
661 spu_release(ctx);
662 return count;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500663}
664
665static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait)
666{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500667 struct spu_context *ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500668 unsigned int mask;
669
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500670 poll_wait(file, &ctx->wbox_wq, wait);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500671
Arnd Bergmann3a843d72005-12-05 22:52:27 -0500672 spu_acquire(ctx);
673 mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM);
674 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500675
676 return mask;
677}
678
679static struct file_operations spufs_wbox_fops = {
680 .open = spufs_pipe_open,
681 .write = spufs_wbox_write,
682 .poll = spufs_wbox_poll,
683 .fasync = spufs_wbox_fasync,
684};
685
686static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
687 size_t len, loff_t *pos)
688{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500689 struct spu_context *ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500690 u32 wbox_stat;
691
692 if (len < 4)
693 return -EINVAL;
694
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500695 spu_acquire(ctx);
696 wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff;
697 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500698
699 if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat))
700 return -EFAULT;
701
702 return 4;
703}
704
705static struct file_operations spufs_wbox_stat_fops = {
706 .open = spufs_pipe_open,
707 .read = spufs_wbox_stat_read,
708};
709
Mark Nutter6df10a82006-03-23 00:00:12 +0100710static int spufs_signal1_open(struct inode *inode, struct file *file)
711{
712 struct spufs_inode_info *i = SPUFS_I(inode);
713 struct spu_context *ctx = i->i_ctx;
714 file->private_data = ctx;
715 file->f_mapping = inode->i_mapping;
716 ctx->signal1 = inode->i_mapping;
717 return nonseekable_open(inode, file);
718}
719
Arnd Bergmann67207b92005-11-15 15:53:48 -0500720static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
721 size_t len, loff_t *pos)
722{
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500723 struct spu_context *ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500724 u32 data;
725
Arnd Bergmann67207b92005-11-15 15:53:48 -0500726 if (len < 4)
727 return -EINVAL;
728
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500729 spu_acquire(ctx);
730 data = ctx->ops->signal1_read(ctx);
731 spu_release(ctx);
732
Arnd Bergmann67207b92005-11-15 15:53:48 -0500733 if (copy_to_user(buf, &data, 4))
734 return -EFAULT;
735
736 return 4;
737}
738
739static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
740 size_t len, loff_t *pos)
741{
742 struct spu_context *ctx;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500743 u32 data;
744
745 ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500746
747 if (len < 4)
748 return -EINVAL;
749
750 if (copy_from_user(&data, buf, 4))
751 return -EFAULT;
752
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500753 spu_acquire(ctx);
754 ctx->ops->signal1_write(ctx, data);
755 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500756
757 return 4;
758}
759
Mark Nutter6df10a82006-03-23 00:00:12 +0100760static struct page *spufs_signal1_mmap_nopage(struct vm_area_struct *vma,
761 unsigned long address, int *type)
762{
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200763#if PAGE_SIZE == 0x1000
764 return spufs_ps_nopage(vma, address, type, 0x14000, 0x1000);
765#elif PAGE_SIZE == 0x10000
766 /* For 64k pages, both signal1 and signal2 can be used to mmap the whole
767 * signal 1 and 2 area
768 */
769 return spufs_ps_nopage(vma, address, type, 0x10000, 0x10000);
770#else
771#error unsupported page size
772#endif
Mark Nutter6df10a82006-03-23 00:00:12 +0100773}
774
775static struct vm_operations_struct spufs_signal1_mmap_vmops = {
776 .nopage = spufs_signal1_mmap_nopage,
777};
778
779static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
780{
781 if (!(vma->vm_flags & VM_SHARED))
782 return -EINVAL;
783
784 vma->vm_flags |= VM_RESERVED;
785 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
Benjamin Herrenschmidt23cc7702006-06-23 20:57:47 +0200786 | _PAGE_NO_CACHE | _PAGE_GUARDED);
Mark Nutter6df10a82006-03-23 00:00:12 +0100787
788 vma->vm_ops = &spufs_signal1_mmap_vmops;
789 return 0;
790}
Mark Nutter6df10a82006-03-23 00:00:12 +0100791
Arnd Bergmann67207b92005-11-15 15:53:48 -0500792static struct file_operations spufs_signal1_fops = {
Mark Nutter6df10a82006-03-23 00:00:12 +0100793 .open = spufs_signal1_open,
Arnd Bergmann67207b92005-11-15 15:53:48 -0500794 .read = spufs_signal1_read,
795 .write = spufs_signal1_write,
Mark Nutter6df10a82006-03-23 00:00:12 +0100796 .mmap = spufs_signal1_mmap,
Arnd Bergmann67207b92005-11-15 15:53:48 -0500797};
798
Mark Nutter6df10a82006-03-23 00:00:12 +0100799static int spufs_signal2_open(struct inode *inode, struct file *file)
800{
801 struct spufs_inode_info *i = SPUFS_I(inode);
802 struct spu_context *ctx = i->i_ctx;
803 file->private_data = ctx;
804 file->f_mapping = inode->i_mapping;
805 ctx->signal2 = inode->i_mapping;
806 return nonseekable_open(inode, file);
807}
808
Arnd Bergmann67207b92005-11-15 15:53:48 -0500809static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
810 size_t len, loff_t *pos)
811{
812 struct spu_context *ctx;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500813 u32 data;
814
815 ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500816
817 if (len < 4)
818 return -EINVAL;
819
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500820 spu_acquire(ctx);
821 data = ctx->ops->signal2_read(ctx);
822 spu_release(ctx);
823
Arnd Bergmann67207b92005-11-15 15:53:48 -0500824 if (copy_to_user(buf, &data, 4))
825 return -EFAULT;
826
827 return 4;
828}
829
830static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
831 size_t len, loff_t *pos)
832{
833 struct spu_context *ctx;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500834 u32 data;
835
836 ctx = file->private_data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500837
838 if (len < 4)
839 return -EINVAL;
840
841 if (copy_from_user(&data, buf, 4))
842 return -EFAULT;
843
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500844 spu_acquire(ctx);
845 ctx->ops->signal2_write(ctx, data);
846 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500847
848 return 4;
849}
850
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200851#if SPUFS_MMAP_4K
Mark Nutter6df10a82006-03-23 00:00:12 +0100852static struct page *spufs_signal2_mmap_nopage(struct vm_area_struct *vma,
853 unsigned long address, int *type)
854{
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200855#if PAGE_SIZE == 0x1000
856 return spufs_ps_nopage(vma, address, type, 0x1c000, 0x1000);
857#elif PAGE_SIZE == 0x10000
858 /* For 64k pages, both signal1 and signal2 can be used to mmap the whole
859 * signal 1 and 2 area
860 */
861 return spufs_ps_nopage(vma, address, type, 0x10000, 0x10000);
862#else
863#error unsupported page size
864#endif
Mark Nutter6df10a82006-03-23 00:00:12 +0100865}
866
867static struct vm_operations_struct spufs_signal2_mmap_vmops = {
868 .nopage = spufs_signal2_mmap_nopage,
869};
870
871static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
872{
873 if (!(vma->vm_flags & VM_SHARED))
874 return -EINVAL;
875
876 /* FIXME: */
877 vma->vm_flags |= VM_RESERVED;
878 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
Benjamin Herrenschmidt23cc7702006-06-23 20:57:47 +0200879 | _PAGE_NO_CACHE | _PAGE_GUARDED);
Mark Nutter6df10a82006-03-23 00:00:12 +0100880
881 vma->vm_ops = &spufs_signal2_mmap_vmops;
882 return 0;
883}
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200884#else /* SPUFS_MMAP_4K */
885#define spufs_signal2_mmap NULL
886#endif /* !SPUFS_MMAP_4K */
Mark Nutter6df10a82006-03-23 00:00:12 +0100887
Arnd Bergmann67207b92005-11-15 15:53:48 -0500888static struct file_operations spufs_signal2_fops = {
Mark Nutter6df10a82006-03-23 00:00:12 +0100889 .open = spufs_signal2_open,
Arnd Bergmann67207b92005-11-15 15:53:48 -0500890 .read = spufs_signal2_read,
891 .write = spufs_signal2_write,
Mark Nutter6df10a82006-03-23 00:00:12 +0100892 .mmap = spufs_signal2_mmap,
Arnd Bergmann67207b92005-11-15 15:53:48 -0500893};
894
895static void spufs_signal1_type_set(void *data, u64 val)
896{
897 struct spu_context *ctx = data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500898
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500899 spu_acquire(ctx);
900 ctx->ops->signal1_type_set(ctx, val);
901 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500902}
903
904static u64 spufs_signal1_type_get(void *data)
905{
906 struct spu_context *ctx = data;
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500907 u64 ret;
908
909 spu_acquire(ctx);
910 ret = ctx->ops->signal1_type_get(ctx);
911 spu_release(ctx);
912
913 return ret;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500914}
915DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
916 spufs_signal1_type_set, "%llu");
917
918static void spufs_signal2_type_set(void *data, u64 val)
919{
920 struct spu_context *ctx = data;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500921
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500922 spu_acquire(ctx);
923 ctx->ops->signal2_type_set(ctx, val);
924 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -0500925}
926
927static u64 spufs_signal2_type_get(void *data)
928{
929 struct spu_context *ctx = data;
Arnd Bergmann8b3d6662005-11-15 15:53:52 -0500930 u64 ret;
931
932 spu_acquire(ctx);
933 ret = ctx->ops->signal2_type_get(ctx);
934 spu_release(ctx);
935
936 return ret;
Arnd Bergmann67207b92005-11-15 15:53:48 -0500937}
938DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
939 spufs_signal2_type_set, "%llu");
940
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200941#if SPUFS_MMAP_4K
arnd@arndb.ded9379c42006-06-19 20:33:21 +0200942static struct page *spufs_mss_mmap_nopage(struct vm_area_struct *vma,
943 unsigned long address, int *type)
944{
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200945 return spufs_ps_nopage(vma, address, type, 0x0000, 0x1000);
arnd@arndb.ded9379c42006-06-19 20:33:21 +0200946}
947
948static struct vm_operations_struct spufs_mss_mmap_vmops = {
949 .nopage = spufs_mss_mmap_nopage,
950};
951
952/*
953 * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
arnd@arndb.ded9379c42006-06-19 20:33:21 +0200954 */
955static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma)
956{
957 if (!(vma->vm_flags & VM_SHARED))
958 return -EINVAL;
959
arnd@arndb.ded9379c42006-06-19 20:33:21 +0200960 vma->vm_flags |= VM_RESERVED;
961 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
Benjamin Herrenschmidt23cc7702006-06-23 20:57:47 +0200962 | _PAGE_NO_CACHE | _PAGE_GUARDED);
arnd@arndb.ded9379c42006-06-19 20:33:21 +0200963
964 vma->vm_ops = &spufs_mss_mmap_vmops;
965 return 0;
966}
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200967#else /* SPUFS_MMAP_4K */
968#define spufs_mss_mmap NULL
969#endif /* !SPUFS_MMAP_4K */
arnd@arndb.ded9379c42006-06-19 20:33:21 +0200970
971static int spufs_mss_open(struct inode *inode, struct file *file)
972{
973 struct spufs_inode_info *i = SPUFS_I(inode);
974
975 file->private_data = i->i_ctx;
976 return nonseekable_open(inode, file);
977}
978
979static struct file_operations spufs_mss_fops = {
980 .open = spufs_mss_open,
arnd@arndb.ded9379c42006-06-19 20:33:21 +0200981 .mmap = spufs_mss_mmap,
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +0200982};
983
984static struct page *spufs_psmap_mmap_nopage(struct vm_area_struct *vma,
985 unsigned long address, int *type)
986{
987 return spufs_ps_nopage(vma, address, type, 0x0000, 0x20000);
988}
989
990static struct vm_operations_struct spufs_psmap_mmap_vmops = {
991 .nopage = spufs_psmap_mmap_nopage,
992};
993
994/*
995 * mmap support for full problem state area [0x00000 - 0x1ffff].
996 */
997static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
998{
999 if (!(vma->vm_flags & VM_SHARED))
1000 return -EINVAL;
1001
1002 vma->vm_flags |= VM_RESERVED;
1003 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
1004 | _PAGE_NO_CACHE | _PAGE_GUARDED);
1005
1006 vma->vm_ops = &spufs_psmap_mmap_vmops;
1007 return 0;
1008}
1009
1010static int spufs_psmap_open(struct inode *inode, struct file *file)
1011{
1012 struct spufs_inode_info *i = SPUFS_I(inode);
1013
1014 file->private_data = i->i_ctx;
1015 return nonseekable_open(inode, file);
1016}
1017
1018static struct file_operations spufs_psmap_fops = {
1019 .open = spufs_psmap_open,
1020 .mmap = spufs_psmap_mmap,
arnd@arndb.ded9379c42006-06-19 20:33:21 +02001021};
1022
1023
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +02001024#if SPUFS_MMAP_4K
Mark Nutter6df10a82006-03-23 00:00:12 +01001025static struct page *spufs_mfc_mmap_nopage(struct vm_area_struct *vma,
1026 unsigned long address, int *type)
1027{
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +02001028 return spufs_ps_nopage(vma, address, type, 0x3000, 0x1000);
Mark Nutter6df10a82006-03-23 00:00:12 +01001029}
1030
1031static struct vm_operations_struct spufs_mfc_mmap_vmops = {
1032 .nopage = spufs_mfc_mmap_nopage,
1033};
1034
1035/*
1036 * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
Mark Nutter6df10a82006-03-23 00:00:12 +01001037 */
1038static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
1039{
1040 if (!(vma->vm_flags & VM_SHARED))
1041 return -EINVAL;
1042
Mark Nutter6df10a82006-03-23 00:00:12 +01001043 vma->vm_flags |= VM_RESERVED;
1044 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
Benjamin Herrenschmidt23cc7702006-06-23 20:57:47 +02001045 | _PAGE_NO_CACHE | _PAGE_GUARDED);
Mark Nutter6df10a82006-03-23 00:00:12 +01001046
1047 vma->vm_ops = &spufs_mfc_mmap_vmops;
1048 return 0;
1049}
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +02001050#else /* SPUFS_MMAP_4K */
1051#define spufs_mfc_mmap NULL
1052#endif /* !SPUFS_MMAP_4K */
Arnd Bergmanna33a7d72006-03-23 00:00:11 +01001053
1054static int spufs_mfc_open(struct inode *inode, struct file *file)
1055{
1056 struct spufs_inode_info *i = SPUFS_I(inode);
1057 struct spu_context *ctx = i->i_ctx;
1058
1059 /* we don't want to deal with DMA into other processes */
1060 if (ctx->owner != current->mm)
1061 return -EINVAL;
1062
1063 if (atomic_read(&inode->i_count) != 1)
1064 return -EBUSY;
1065
1066 file->private_data = ctx;
1067 return nonseekable_open(inode, file);
1068}
1069
1070/* interrupt-level mfc callback function. */
1071void spufs_mfc_callback(struct spu *spu)
1072{
1073 struct spu_context *ctx = spu->ctx;
1074
1075 wake_up_all(&ctx->mfc_wq);
1076
1077 pr_debug("%s %s\n", __FUNCTION__, spu->name);
1078 if (ctx->mfc_fasync) {
1079 u32 free_elements, tagstatus;
1080 unsigned int mask;
1081
1082 /* no need for spu_acquire in interrupt context */
1083 free_elements = ctx->ops->get_mfc_free_elements(ctx);
1084 tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1085
1086 mask = 0;
1087 if (free_elements & 0xffff)
1088 mask |= POLLOUT;
1089 if (tagstatus & ctx->tagwait)
1090 mask |= POLLIN;
1091
1092 kill_fasync(&ctx->mfc_fasync, SIGIO, mask);
1093 }
1094}
1095
1096static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status)
1097{
1098 /* See if there is one tag group is complete */
1099 /* FIXME we need locking around tagwait */
1100 *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait;
1101 ctx->tagwait &= ~*status;
1102 if (*status)
1103 return 1;
1104
1105 /* enable interrupt waiting for any tag group,
1106 may silently fail if interrupts are already enabled */
1107 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1108 return 0;
1109}
1110
1111static ssize_t spufs_mfc_read(struct file *file, char __user *buffer,
1112 size_t size, loff_t *pos)
1113{
1114 struct spu_context *ctx = file->private_data;
1115 int ret = -EINVAL;
1116 u32 status;
1117
1118 if (size != 4)
1119 goto out;
1120
1121 spu_acquire(ctx);
1122 if (file->f_flags & O_NONBLOCK) {
1123 status = ctx->ops->read_mfc_tagstatus(ctx);
1124 if (!(status & ctx->tagwait))
1125 ret = -EAGAIN;
1126 else
1127 ctx->tagwait &= ~status;
1128 } else {
1129 ret = spufs_wait(ctx->mfc_wq,
1130 spufs_read_mfc_tagstatus(ctx, &status));
1131 }
1132 spu_release(ctx);
1133
1134 if (ret)
1135 goto out;
1136
1137 ret = 4;
1138 if (copy_to_user(buffer, &status, 4))
1139 ret = -EFAULT;
1140
1141out:
1142 return ret;
1143}
1144
1145static int spufs_check_valid_dma(struct mfc_dma_command *cmd)
1146{
1147 pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa,
1148 cmd->ea, cmd->size, cmd->tag, cmd->cmd);
1149
1150 switch (cmd->cmd) {
1151 case MFC_PUT_CMD:
1152 case MFC_PUTF_CMD:
1153 case MFC_PUTB_CMD:
1154 case MFC_GET_CMD:
1155 case MFC_GETF_CMD:
1156 case MFC_GETB_CMD:
1157 break;
1158 default:
1159 pr_debug("invalid DMA opcode %x\n", cmd->cmd);
1160 return -EIO;
1161 }
1162
1163 if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) {
1164 pr_debug("invalid DMA alignment, ea %lx lsa %x\n",
1165 cmd->ea, cmd->lsa);
1166 return -EIO;
1167 }
1168
1169 switch (cmd->size & 0xf) {
1170 case 1:
1171 break;
1172 case 2:
1173 if (cmd->lsa & 1)
1174 goto error;
1175 break;
1176 case 4:
1177 if (cmd->lsa & 3)
1178 goto error;
1179 break;
1180 case 8:
1181 if (cmd->lsa & 7)
1182 goto error;
1183 break;
1184 case 0:
1185 if (cmd->lsa & 15)
1186 goto error;
1187 break;
1188 error:
1189 default:
1190 pr_debug("invalid DMA alignment %x for size %x\n",
1191 cmd->lsa & 0xf, cmd->size);
1192 return -EIO;
1193 }
1194
1195 if (cmd->size > 16 * 1024) {
1196 pr_debug("invalid DMA size %x\n", cmd->size);
1197 return -EIO;
1198 }
1199
1200 if (cmd->tag & 0xfff0) {
1201 /* we reserve the higher tag numbers for kernel use */
1202 pr_debug("invalid DMA tag\n");
1203 return -EIO;
1204 }
1205
1206 if (cmd->class) {
1207 /* not supported in this version */
1208 pr_debug("invalid DMA class\n");
1209 return -EIO;
1210 }
1211
1212 return 0;
1213}
1214
1215static int spu_send_mfc_command(struct spu_context *ctx,
1216 struct mfc_dma_command cmd,
1217 int *error)
1218{
1219 *error = ctx->ops->send_mfc_command(ctx, &cmd);
1220 if (*error == -EAGAIN) {
1221 /* wait for any tag group to complete
1222 so we have space for the new command */
1223 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1224 /* try again, because the queue might be
1225 empty again */
1226 *error = ctx->ops->send_mfc_command(ctx, &cmd);
1227 if (*error == -EAGAIN)
1228 return 0;
1229 }
1230 return 1;
1231}
1232
1233static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer,
1234 size_t size, loff_t *pos)
1235{
1236 struct spu_context *ctx = file->private_data;
1237 struct mfc_dma_command cmd;
1238 int ret = -EINVAL;
1239
1240 if (size != sizeof cmd)
1241 goto out;
1242
1243 ret = -EFAULT;
1244 if (copy_from_user(&cmd, buffer, sizeof cmd))
1245 goto out;
1246
1247 ret = spufs_check_valid_dma(&cmd);
1248 if (ret)
1249 goto out;
1250
1251 spu_acquire_runnable(ctx);
1252 if (file->f_flags & O_NONBLOCK) {
1253 ret = ctx->ops->send_mfc_command(ctx, &cmd);
1254 } else {
1255 int status;
1256 ret = spufs_wait(ctx->mfc_wq,
1257 spu_send_mfc_command(ctx, cmd, &status));
1258 if (status)
1259 ret = status;
1260 }
1261 spu_release(ctx);
1262
1263 if (ret)
1264 goto out;
1265
1266 ctx->tagwait |= 1 << cmd.tag;
1267
1268out:
1269 return ret;
1270}
1271
1272static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait)
1273{
1274 struct spu_context *ctx = file->private_data;
1275 u32 free_elements, tagstatus;
1276 unsigned int mask;
1277
1278 spu_acquire(ctx);
1279 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2);
1280 free_elements = ctx->ops->get_mfc_free_elements(ctx);
1281 tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1282 spu_release(ctx);
1283
1284 poll_wait(file, &ctx->mfc_wq, wait);
1285
1286 mask = 0;
1287 if (free_elements & 0xffff)
1288 mask |= POLLOUT | POLLWRNORM;
1289 if (tagstatus & ctx->tagwait)
1290 mask |= POLLIN | POLLRDNORM;
1291
1292 pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__,
1293 free_elements, tagstatus, ctx->tagwait);
1294
1295 return mask;
1296}
1297
Al Viro73b6af82006-06-25 16:42:33 -07001298static int spufs_mfc_flush(struct file *file, fl_owner_t id)
Arnd Bergmanna33a7d72006-03-23 00:00:11 +01001299{
1300 struct spu_context *ctx = file->private_data;
1301 int ret;
1302
1303 spu_acquire(ctx);
1304#if 0
1305/* this currently hangs */
1306 ret = spufs_wait(ctx->mfc_wq,
1307 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2));
1308 if (ret)
1309 goto out;
1310 ret = spufs_wait(ctx->mfc_wq,
1311 ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait);
1312out:
1313#else
1314 ret = 0;
1315#endif
1316 spu_release(ctx);
1317
1318 return ret;
1319}
1320
1321static int spufs_mfc_fsync(struct file *file, struct dentry *dentry,
1322 int datasync)
1323{
Al Viro73b6af82006-06-25 16:42:33 -07001324 return spufs_mfc_flush(file, NULL);
Arnd Bergmanna33a7d72006-03-23 00:00:11 +01001325}
1326
1327static int spufs_mfc_fasync(int fd, struct file *file, int on)
1328{
1329 struct spu_context *ctx = file->private_data;
1330
1331 return fasync_helper(fd, file, on, &ctx->mfc_fasync);
1332}
1333
1334static struct file_operations spufs_mfc_fops = {
1335 .open = spufs_mfc_open,
1336 .read = spufs_mfc_read,
1337 .write = spufs_mfc_write,
1338 .poll = spufs_mfc_poll,
1339 .flush = spufs_mfc_flush,
1340 .fsync = spufs_mfc_fsync,
1341 .fasync = spufs_mfc_fasync,
Mark Nutter6df10a82006-03-23 00:00:12 +01001342 .mmap = spufs_mfc_mmap,
Arnd Bergmanna33a7d72006-03-23 00:00:11 +01001343};
1344
Arnd Bergmann67207b92005-11-15 15:53:48 -05001345static void spufs_npc_set(void *data, u64 val)
1346{
1347 struct spu_context *ctx = data;
Arnd Bergmann8b3d6662005-11-15 15:53:52 -05001348 spu_acquire(ctx);
1349 ctx->ops->npc_write(ctx, val);
1350 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -05001351}
1352
1353static u64 spufs_npc_get(void *data)
1354{
1355 struct spu_context *ctx = data;
1356 u64 ret;
Arnd Bergmann8b3d6662005-11-15 15:53:52 -05001357 spu_acquire(ctx);
1358 ret = ctx->ops->npc_read(ctx);
1359 spu_release(ctx);
Arnd Bergmann67207b92005-11-15 15:53:48 -05001360 return ret;
1361}
1362DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, "%llx\n")
1363
Arnd Bergmann8b3d6662005-11-15 15:53:52 -05001364static void spufs_decr_set(void *data, u64 val)
1365{
1366 struct spu_context *ctx = data;
1367 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1368 spu_acquire_saved(ctx);
1369 lscsa->decr.slot[0] = (u32) val;
1370 spu_release(ctx);
1371}
1372
1373static u64 spufs_decr_get(void *data)
1374{
1375 struct spu_context *ctx = data;
1376 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1377 u64 ret;
1378 spu_acquire_saved(ctx);
1379 ret = lscsa->decr.slot[0];
1380 spu_release(ctx);
1381 return ret;
1382}
1383DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set,
1384 "%llx\n")
1385
1386static void spufs_decr_status_set(void *data, u64 val)
1387{
1388 struct spu_context *ctx = data;
1389 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1390 spu_acquire_saved(ctx);
1391 lscsa->decr_status.slot[0] = (u32) val;
1392 spu_release(ctx);
1393}
1394
1395static u64 spufs_decr_status_get(void *data)
1396{
1397 struct spu_context *ctx = data;
1398 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1399 u64 ret;
1400 spu_acquire_saved(ctx);
1401 ret = lscsa->decr_status.slot[0];
1402 spu_release(ctx);
1403 return ret;
1404}
1405DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get,
1406 spufs_decr_status_set, "%llx\n")
1407
1408static void spufs_spu_tag_mask_set(void *data, u64 val)
1409{
1410 struct spu_context *ctx = data;
1411 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1412 spu_acquire_saved(ctx);
1413 lscsa->tag_mask.slot[0] = (u32) val;
1414 spu_release(ctx);
1415}
1416
1417static u64 spufs_spu_tag_mask_get(void *data)
1418{
1419 struct spu_context *ctx = data;
1420 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1421 u64 ret;
1422 spu_acquire_saved(ctx);
1423 ret = lscsa->tag_mask.slot[0];
1424 spu_release(ctx);
1425 return ret;
1426}
1427DEFINE_SIMPLE_ATTRIBUTE(spufs_spu_tag_mask_ops, spufs_spu_tag_mask_get,
1428 spufs_spu_tag_mask_set, "%llx\n")
1429
1430static void spufs_event_mask_set(void *data, u64 val)
1431{
1432 struct spu_context *ctx = data;
1433 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1434 spu_acquire_saved(ctx);
1435 lscsa->event_mask.slot[0] = (u32) val;
1436 spu_release(ctx);
1437}
1438
1439static u64 spufs_event_mask_get(void *data)
1440{
1441 struct spu_context *ctx = data;
1442 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1443 u64 ret;
1444 spu_acquire_saved(ctx);
1445 ret = lscsa->event_mask.slot[0];
1446 spu_release(ctx);
1447 return ret;
1448}
1449DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get,
1450 spufs_event_mask_set, "%llx\n")
1451
1452static void spufs_srr0_set(void *data, u64 val)
1453{
1454 struct spu_context *ctx = data;
1455 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1456 spu_acquire_saved(ctx);
1457 lscsa->srr0.slot[0] = (u32) val;
1458 spu_release(ctx);
1459}
1460
1461static u64 spufs_srr0_get(void *data)
1462{
1463 struct spu_context *ctx = data;
1464 struct spu_lscsa *lscsa = ctx->csa.lscsa;
1465 u64 ret;
1466 spu_acquire_saved(ctx);
1467 ret = lscsa->srr0.slot[0];
1468 spu_release(ctx);
1469 return ret;
1470}
1471DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
1472 "%llx\n")
1473
arnd@arndb.de7b1a7012006-06-19 20:33:24 +02001474static u64 spufs_id_get(void *data)
1475{
1476 struct spu_context *ctx = data;
1477 u64 num;
1478
1479 spu_acquire(ctx);
1480 if (ctx->state == SPU_STATE_RUNNABLE)
1481 num = ctx->spu->number;
1482 else
1483 num = (unsigned int)-1;
1484 spu_release(ctx);
1485
1486 return num;
1487}
Al Viroe45d6632006-09-23 01:37:41 +01001488DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n")
arnd@arndb.de7b1a7012006-06-19 20:33:24 +02001489
Arnd Bergmann67207b92005-11-15 15:53:48 -05001490struct tree_descr spufs_dir_contents[] = {
1491 { "mem", &spufs_mem_fops, 0666, },
Arnd Bergmann8b3d6662005-11-15 15:53:52 -05001492 { "regs", &spufs_regs_fops, 0666, },
Arnd Bergmann67207b92005-11-15 15:53:48 -05001493 { "mbox", &spufs_mbox_fops, 0444, },
1494 { "ibox", &spufs_ibox_fops, 0444, },
1495 { "wbox", &spufs_wbox_fops, 0222, },
1496 { "mbox_stat", &spufs_mbox_stat_fops, 0444, },
1497 { "ibox_stat", &spufs_ibox_stat_fops, 0444, },
1498 { "wbox_stat", &spufs_wbox_stat_fops, 0444, },
1499 { "signal1", &spufs_signal1_fops, 0666, },
1500 { "signal2", &spufs_signal2_fops, 0666, },
1501 { "signal1_type", &spufs_signal1_type, 0666, },
1502 { "signal2_type", &spufs_signal2_type, 0666, },
arnd@arndb.ded9379c42006-06-19 20:33:21 +02001503 { "mss", &spufs_mss_fops, 0666, },
Arnd Bergmanna33a7d72006-03-23 00:00:11 +01001504 { "mfc", &spufs_mfc_fops, 0666, },
Mark Nutter6df10a82006-03-23 00:00:12 +01001505 { "cntl", &spufs_cntl_fops, 0666, },
Arnd Bergmann67207b92005-11-15 15:53:48 -05001506 { "npc", &spufs_npc_ops, 0666, },
Arnd Bergmann8b3d6662005-11-15 15:53:52 -05001507 { "fpcr", &spufs_fpcr_fops, 0666, },
1508 { "decr", &spufs_decr_ops, 0666, },
1509 { "decr_status", &spufs_decr_status_ops, 0666, },
1510 { "spu_tag_mask", &spufs_spu_tag_mask_ops, 0666, },
1511 { "event_mask", &spufs_event_mask_ops, 0666, },
1512 { "srr0", &spufs_srr0_ops, 0666, },
arnd@arndb.de7b1a7012006-06-19 20:33:24 +02001513 { "phys-id", &spufs_id_ops, 0666, },
Benjamin Herrenschmidt27d5bf22006-10-04 17:26:11 +02001514 { "psmap", &spufs_psmap_fops, 0666, },
Arnd Bergmann67207b92005-11-15 15:53:48 -05001515 {},
1516};