]>
Commit | Line | Data |
---|---|---|
67207b96 AB |
1 | /* |
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 | ||
a33a7d73 AB |
23 | #undef DEBUG |
24 | ||
67207b96 AB |
25 | #include <linux/fs.h> |
26 | #include <linux/ioctl.h> | |
27 | #include <linux/module.h> | |
d88cfffa | 28 | #include <linux/pagemap.h> |
67207b96 | 29 | #include <linux/poll.h> |
5110459f | 30 | #include <linux/ptrace.h> |
67207b96 AB |
31 | |
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 | ||
8b3d6663 | 39 | |
67207b96 AB |
40 | static int |
41 | spufs_mem_open(struct inode *inode, struct file *file) | |
42 | { | |
43 | struct spufs_inode_info *i = SPUFS_I(inode); | |
44 | file->private_data = i->i_ctx; | |
8b3d6663 | 45 | file->f_mapping = i->i_ctx->local_store; |
67207b96 AB |
46 | return 0; |
47 | } | |
48 | ||
49 | static ssize_t | |
50 | spufs_mem_read(struct file *file, char __user *buffer, | |
51 | size_t size, loff_t *pos) | |
52 | { | |
8b3d6663 AB |
53 | struct spu_context *ctx = file->private_data; |
54 | char *local_store; | |
67207b96 AB |
55 | int ret; |
56 | ||
8b3d6663 | 57 | spu_acquire(ctx); |
67207b96 | 58 | |
8b3d6663 AB |
59 | local_store = ctx->ops->get_ls(ctx); |
60 | ret = simple_read_from_buffer(buffer, size, pos, local_store, LS_SIZE); | |
67207b96 | 61 | |
8b3d6663 | 62 | spu_release(ctx); |
67207b96 AB |
63 | return ret; |
64 | } | |
65 | ||
66 | static ssize_t | |
67 | spufs_mem_write(struct file *file, const char __user *buffer, | |
68 | size_t size, loff_t *pos) | |
69 | { | |
70 | struct spu_context *ctx = file->private_data; | |
8b3d6663 AB |
71 | char *local_store; |
72 | int ret; | |
67207b96 AB |
73 | |
74 | size = min_t(ssize_t, LS_SIZE - *pos, size); | |
75 | if (size <= 0) | |
76 | return -EFBIG; | |
77 | *pos += size; | |
8b3d6663 AB |
78 | |
79 | spu_acquire(ctx); | |
80 | ||
81 | local_store = ctx->ops->get_ls(ctx); | |
82 | ret = copy_from_user(local_store + *pos - size, | |
83 | buffer, size) ? -EFAULT : size; | |
84 | ||
85 | spu_release(ctx); | |
86 | return ret; | |
67207b96 AB |
87 | } |
88 | ||
8b3d6663 AB |
89 | #ifdef CONFIG_SPARSEMEM |
90 | static struct page * | |
91 | spufs_mem_mmap_nopage(struct vm_area_struct *vma, | |
92 | unsigned long address, int *type) | |
93 | { | |
94 | struct page *page = NOPAGE_SIGBUS; | |
95 | ||
96 | struct spu_context *ctx = vma->vm_file->private_data; | |
97 | unsigned long offset = address - vma->vm_start; | |
98 | offset += vma->vm_pgoff << PAGE_SHIFT; | |
99 | ||
100 | spu_acquire(ctx); | |
101 | ||
102 | if (ctx->state == SPU_STATE_SAVED) | |
103 | page = vmalloc_to_page(ctx->csa.lscsa->ls + offset); | |
104 | else | |
105 | page = pfn_to_page((ctx->spu->local_store_phys + offset) | |
106 | >> PAGE_SHIFT); | |
107 | ||
108 | spu_release(ctx); | |
109 | ||
110 | if (type) | |
111 | *type = VM_FAULT_MINOR; | |
112 | ||
d88cfffa | 113 | page_cache_get(page); |
8b3d6663 AB |
114 | return page; |
115 | } | |
116 | ||
117 | static struct vm_operations_struct spufs_mem_mmap_vmops = { | |
118 | .nopage = spufs_mem_mmap_nopage, | |
119 | }; | |
120 | ||
67207b96 AB |
121 | static int |
122 | spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) | |
123 | { | |
8b3d6663 AB |
124 | if (!(vma->vm_flags & VM_SHARED)) |
125 | return -EINVAL; | |
67207b96 | 126 | |
8b3d6663 | 127 | /* FIXME: */ |
8b3d6663 AB |
128 | vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) |
129 | | _PAGE_NO_CACHE); | |
130 | ||
131 | vma->vm_ops = &spufs_mem_mmap_vmops; | |
67207b96 AB |
132 | return 0; |
133 | } | |
8b3d6663 | 134 | #endif |
67207b96 AB |
135 | |
136 | static struct file_operations spufs_mem_fops = { | |
137 | .open = spufs_mem_open, | |
138 | .read = spufs_mem_read, | |
139 | .write = spufs_mem_write, | |
8b3d6663 AB |
140 | .llseek = generic_file_llseek, |
141 | #ifdef CONFIG_SPARSEMEM | |
67207b96 | 142 | .mmap = spufs_mem_mmap, |
8b3d6663 AB |
143 | #endif |
144 | }; | |
145 | ||
146 | static int | |
147 | spufs_regs_open(struct inode *inode, struct file *file) | |
148 | { | |
149 | struct spufs_inode_info *i = SPUFS_I(inode); | |
150 | file->private_data = i->i_ctx; | |
151 | return 0; | |
152 | } | |
153 | ||
154 | static ssize_t | |
155 | spufs_regs_read(struct file *file, char __user *buffer, | |
156 | size_t size, loff_t *pos) | |
157 | { | |
158 | struct spu_context *ctx = file->private_data; | |
159 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
160 | int ret; | |
161 | ||
162 | spu_acquire_saved(ctx); | |
163 | ||
164 | ret = simple_read_from_buffer(buffer, size, pos, | |
165 | lscsa->gprs, sizeof lscsa->gprs); | |
166 | ||
167 | spu_release(ctx); | |
168 | return ret; | |
169 | } | |
170 | ||
171 | static ssize_t | |
172 | spufs_regs_write(struct file *file, const char __user *buffer, | |
173 | size_t size, loff_t *pos) | |
174 | { | |
175 | struct spu_context *ctx = file->private_data; | |
176 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
177 | int ret; | |
178 | ||
179 | size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size); | |
180 | if (size <= 0) | |
181 | return -EFBIG; | |
182 | *pos += size; | |
183 | ||
184 | spu_acquire_saved(ctx); | |
185 | ||
186 | ret = copy_from_user(lscsa->gprs + *pos - size, | |
187 | buffer, size) ? -EFAULT : size; | |
188 | ||
189 | spu_release(ctx); | |
190 | return ret; | |
191 | } | |
192 | ||
193 | static struct file_operations spufs_regs_fops = { | |
194 | .open = spufs_regs_open, | |
195 | .read = spufs_regs_read, | |
196 | .write = spufs_regs_write, | |
67207b96 AB |
197 | .llseek = generic_file_llseek, |
198 | }; | |
199 | ||
8b3d6663 AB |
200 | static ssize_t |
201 | spufs_fpcr_read(struct file *file, char __user * buffer, | |
202 | size_t size, loff_t * pos) | |
203 | { | |
204 | struct spu_context *ctx = file->private_data; | |
205 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
206 | int ret; | |
207 | ||
208 | spu_acquire_saved(ctx); | |
209 | ||
210 | ret = simple_read_from_buffer(buffer, size, pos, | |
211 | &lscsa->fpcr, sizeof(lscsa->fpcr)); | |
212 | ||
213 | spu_release(ctx); | |
214 | return ret; | |
215 | } | |
216 | ||
217 | static ssize_t | |
218 | spufs_fpcr_write(struct file *file, const char __user * buffer, | |
219 | size_t size, loff_t * pos) | |
220 | { | |
221 | struct spu_context *ctx = file->private_data; | |
222 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
223 | int ret; | |
224 | ||
225 | size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); | |
226 | if (size <= 0) | |
227 | return -EFBIG; | |
228 | *pos += size; | |
229 | ||
230 | spu_acquire_saved(ctx); | |
231 | ||
232 | ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, | |
233 | buffer, size) ? -EFAULT : size; | |
234 | ||
235 | spu_release(ctx); | |
236 | return ret; | |
237 | } | |
238 | ||
239 | static struct file_operations spufs_fpcr_fops = { | |
240 | .open = spufs_regs_open, | |
241 | .read = spufs_fpcr_read, | |
242 | .write = spufs_fpcr_write, | |
243 | .llseek = generic_file_llseek, | |
244 | }; | |
245 | ||
67207b96 AB |
246 | /* generic open function for all pipe-like files */ |
247 | static int spufs_pipe_open(struct inode *inode, struct file *file) | |
248 | { | |
249 | struct spufs_inode_info *i = SPUFS_I(inode); | |
250 | file->private_data = i->i_ctx; | |
251 | ||
252 | return nonseekable_open(inode, file); | |
253 | } | |
254 | ||
255 | static ssize_t spufs_mbox_read(struct file *file, char __user *buf, | |
256 | size_t len, loff_t *pos) | |
257 | { | |
8b3d6663 | 258 | struct spu_context *ctx = file->private_data; |
67207b96 | 259 | u32 mbox_data; |
8b3d6663 | 260 | int ret; |
67207b96 AB |
261 | |
262 | if (len < 4) | |
263 | return -EINVAL; | |
264 | ||
8b3d6663 AB |
265 | spu_acquire(ctx); |
266 | ret = ctx->ops->mbox_read(ctx, &mbox_data); | |
267 | spu_release(ctx); | |
67207b96 | 268 | |
8b3d6663 AB |
269 | if (!ret) |
270 | return -EAGAIN; | |
67207b96 AB |
271 | |
272 | if (copy_to_user(buf, &mbox_data, sizeof mbox_data)) | |
273 | return -EFAULT; | |
274 | ||
275 | return 4; | |
276 | } | |
277 | ||
278 | static struct file_operations spufs_mbox_fops = { | |
279 | .open = spufs_pipe_open, | |
280 | .read = spufs_mbox_read, | |
281 | }; | |
282 | ||
283 | static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, | |
284 | size_t len, loff_t *pos) | |
285 | { | |
8b3d6663 | 286 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
287 | u32 mbox_stat; |
288 | ||
289 | if (len < 4) | |
290 | return -EINVAL; | |
291 | ||
8b3d6663 AB |
292 | spu_acquire(ctx); |
293 | ||
294 | mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; | |
295 | ||
296 | spu_release(ctx); | |
67207b96 AB |
297 | |
298 | if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) | |
299 | return -EFAULT; | |
300 | ||
301 | return 4; | |
302 | } | |
303 | ||
304 | static struct file_operations spufs_mbox_stat_fops = { | |
305 | .open = spufs_pipe_open, | |
306 | .read = spufs_mbox_stat_read, | |
307 | }; | |
308 | ||
309 | /* low-level ibox access function */ | |
8b3d6663 | 310 | size_t spu_ibox_read(struct spu_context *ctx, u32 *data) |
67207b96 | 311 | { |
8b3d6663 AB |
312 | return ctx->ops->ibox_read(ctx, data); |
313 | } | |
67207b96 | 314 | |
8b3d6663 AB |
315 | static int spufs_ibox_fasync(int fd, struct file *file, int on) |
316 | { | |
317 | struct spu_context *ctx = file->private_data; | |
67207b96 | 318 | |
8b3d6663 | 319 | return fasync_helper(fd, file, on, &ctx->ibox_fasync); |
67207b96 | 320 | } |
67207b96 | 321 | |
8b3d6663 AB |
322 | /* interrupt-level ibox callback function. */ |
323 | void spufs_ibox_callback(struct spu *spu) | |
67207b96 | 324 | { |
8b3d6663 AB |
325 | struct spu_context *ctx = spu->ctx; |
326 | ||
327 | wake_up_all(&ctx->ibox_wq); | |
328 | kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); | |
67207b96 AB |
329 | } |
330 | ||
331 | static ssize_t spufs_ibox_read(struct file *file, char __user *buf, | |
332 | size_t len, loff_t *pos) | |
333 | { | |
8b3d6663 | 334 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
335 | u32 ibox_data; |
336 | ssize_t ret; | |
337 | ||
338 | if (len < 4) | |
339 | return -EINVAL; | |
340 | ||
8b3d6663 | 341 | spu_acquire(ctx); |
67207b96 AB |
342 | |
343 | ret = 0; | |
344 | if (file->f_flags & O_NONBLOCK) { | |
8b3d6663 | 345 | if (!spu_ibox_read(ctx, &ibox_data)) |
67207b96 AB |
346 | ret = -EAGAIN; |
347 | } else { | |
8b3d6663 | 348 | ret = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); |
67207b96 AB |
349 | } |
350 | ||
8b3d6663 AB |
351 | spu_release(ctx); |
352 | ||
67207b96 AB |
353 | if (ret) |
354 | return ret; | |
355 | ||
356 | ret = 4; | |
357 | if (copy_to_user(buf, &ibox_data, sizeof ibox_data)) | |
358 | ret = -EFAULT; | |
359 | ||
360 | return ret; | |
361 | } | |
362 | ||
363 | static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) | |
364 | { | |
8b3d6663 | 365 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
366 | unsigned int mask; |
367 | ||
8b3d6663 | 368 | poll_wait(file, &ctx->ibox_wq, wait); |
67207b96 | 369 | |
3a843d7c AB |
370 | spu_acquire(ctx); |
371 | mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); | |
372 | spu_release(ctx); | |
67207b96 AB |
373 | |
374 | return mask; | |
375 | } | |
376 | ||
377 | static struct file_operations spufs_ibox_fops = { | |
378 | .open = spufs_pipe_open, | |
379 | .read = spufs_ibox_read, | |
380 | .poll = spufs_ibox_poll, | |
381 | .fasync = spufs_ibox_fasync, | |
382 | }; | |
383 | ||
384 | static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, | |
385 | size_t len, loff_t *pos) | |
386 | { | |
8b3d6663 | 387 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
388 | u32 ibox_stat; |
389 | ||
390 | if (len < 4) | |
391 | return -EINVAL; | |
392 | ||
8b3d6663 AB |
393 | spu_acquire(ctx); |
394 | ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; | |
395 | spu_release(ctx); | |
67207b96 AB |
396 | |
397 | if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) | |
398 | return -EFAULT; | |
399 | ||
400 | return 4; | |
401 | } | |
402 | ||
403 | static struct file_operations spufs_ibox_stat_fops = { | |
404 | .open = spufs_pipe_open, | |
405 | .read = spufs_ibox_stat_read, | |
406 | }; | |
407 | ||
408 | /* low-level mailbox write */ | |
8b3d6663 | 409 | size_t spu_wbox_write(struct spu_context *ctx, u32 data) |
67207b96 | 410 | { |
8b3d6663 AB |
411 | return ctx->ops->wbox_write(ctx, data); |
412 | } | |
67207b96 | 413 | |
8b3d6663 AB |
414 | static int spufs_wbox_fasync(int fd, struct file *file, int on) |
415 | { | |
416 | struct spu_context *ctx = file->private_data; | |
417 | int ret; | |
67207b96 | 418 | |
8b3d6663 | 419 | ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); |
67207b96 | 420 | |
67207b96 AB |
421 | return ret; |
422 | } | |
67207b96 | 423 | |
8b3d6663 AB |
424 | /* interrupt-level wbox callback function. */ |
425 | void spufs_wbox_callback(struct spu *spu) | |
67207b96 | 426 | { |
8b3d6663 AB |
427 | struct spu_context *ctx = spu->ctx; |
428 | ||
429 | wake_up_all(&ctx->wbox_wq); | |
430 | kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); | |
67207b96 AB |
431 | } |
432 | ||
433 | static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, | |
434 | size_t len, loff_t *pos) | |
435 | { | |
8b3d6663 | 436 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
437 | u32 wbox_data; |
438 | int ret; | |
439 | ||
440 | if (len < 4) | |
441 | return -EINVAL; | |
442 | ||
67207b96 AB |
443 | if (copy_from_user(&wbox_data, buf, sizeof wbox_data)) |
444 | return -EFAULT; | |
445 | ||
8b3d6663 AB |
446 | spu_acquire(ctx); |
447 | ||
67207b96 AB |
448 | ret = 0; |
449 | if (file->f_flags & O_NONBLOCK) { | |
8b3d6663 | 450 | if (!spu_wbox_write(ctx, wbox_data)) |
67207b96 AB |
451 | ret = -EAGAIN; |
452 | } else { | |
8b3d6663 | 453 | ret = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); |
67207b96 AB |
454 | } |
455 | ||
8b3d6663 AB |
456 | spu_release(ctx); |
457 | ||
67207b96 AB |
458 | return ret ? ret : sizeof wbox_data; |
459 | } | |
460 | ||
461 | static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) | |
462 | { | |
8b3d6663 | 463 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
464 | unsigned int mask; |
465 | ||
8b3d6663 | 466 | poll_wait(file, &ctx->wbox_wq, wait); |
67207b96 | 467 | |
3a843d7c AB |
468 | spu_acquire(ctx); |
469 | mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); | |
470 | spu_release(ctx); | |
67207b96 AB |
471 | |
472 | return mask; | |
473 | } | |
474 | ||
475 | static struct file_operations spufs_wbox_fops = { | |
476 | .open = spufs_pipe_open, | |
477 | .write = spufs_wbox_write, | |
478 | .poll = spufs_wbox_poll, | |
479 | .fasync = spufs_wbox_fasync, | |
480 | }; | |
481 | ||
482 | static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, | |
483 | size_t len, loff_t *pos) | |
484 | { | |
8b3d6663 | 485 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
486 | u32 wbox_stat; |
487 | ||
488 | if (len < 4) | |
489 | return -EINVAL; | |
490 | ||
8b3d6663 AB |
491 | spu_acquire(ctx); |
492 | wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; | |
493 | spu_release(ctx); | |
67207b96 AB |
494 | |
495 | if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) | |
496 | return -EFAULT; | |
497 | ||
498 | return 4; | |
499 | } | |
500 | ||
501 | static struct file_operations spufs_wbox_stat_fops = { | |
502 | .open = spufs_pipe_open, | |
503 | .read = spufs_wbox_stat_read, | |
504 | }; | |
505 | ||
67207b96 AB |
506 | static ssize_t spufs_signal1_read(struct file *file, char __user *buf, |
507 | size_t len, loff_t *pos) | |
508 | { | |
8b3d6663 | 509 | struct spu_context *ctx = file->private_data; |
67207b96 AB |
510 | u32 data; |
511 | ||
67207b96 AB |
512 | if (len < 4) |
513 | return -EINVAL; | |
514 | ||
8b3d6663 AB |
515 | spu_acquire(ctx); |
516 | data = ctx->ops->signal1_read(ctx); | |
517 | spu_release(ctx); | |
518 | ||
67207b96 AB |
519 | if (copy_to_user(buf, &data, 4)) |
520 | return -EFAULT; | |
521 | ||
522 | return 4; | |
523 | } | |
524 | ||
525 | static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, | |
526 | size_t len, loff_t *pos) | |
527 | { | |
528 | struct spu_context *ctx; | |
67207b96 AB |
529 | u32 data; |
530 | ||
531 | ctx = file->private_data; | |
67207b96 AB |
532 | |
533 | if (len < 4) | |
534 | return -EINVAL; | |
535 | ||
536 | if (copy_from_user(&data, buf, 4)) | |
537 | return -EFAULT; | |
538 | ||
8b3d6663 AB |
539 | spu_acquire(ctx); |
540 | ctx->ops->signal1_write(ctx, data); | |
541 | spu_release(ctx); | |
67207b96 AB |
542 | |
543 | return 4; | |
544 | } | |
545 | ||
546 | static struct file_operations spufs_signal1_fops = { | |
547 | .open = spufs_pipe_open, | |
548 | .read = spufs_signal1_read, | |
549 | .write = spufs_signal1_write, | |
550 | }; | |
551 | ||
552 | static ssize_t spufs_signal2_read(struct file *file, char __user *buf, | |
553 | size_t len, loff_t *pos) | |
554 | { | |
555 | struct spu_context *ctx; | |
67207b96 AB |
556 | u32 data; |
557 | ||
558 | ctx = file->private_data; | |
67207b96 AB |
559 | |
560 | if (len < 4) | |
561 | return -EINVAL; | |
562 | ||
8b3d6663 AB |
563 | spu_acquire(ctx); |
564 | data = ctx->ops->signal2_read(ctx); | |
565 | spu_release(ctx); | |
566 | ||
67207b96 AB |
567 | if (copy_to_user(buf, &data, 4)) |
568 | return -EFAULT; | |
569 | ||
570 | return 4; | |
571 | } | |
572 | ||
573 | static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, | |
574 | size_t len, loff_t *pos) | |
575 | { | |
576 | struct spu_context *ctx; | |
67207b96 AB |
577 | u32 data; |
578 | ||
579 | ctx = file->private_data; | |
67207b96 AB |
580 | |
581 | if (len < 4) | |
582 | return -EINVAL; | |
583 | ||
584 | if (copy_from_user(&data, buf, 4)) | |
585 | return -EFAULT; | |
586 | ||
8b3d6663 AB |
587 | spu_acquire(ctx); |
588 | ctx->ops->signal2_write(ctx, data); | |
589 | spu_release(ctx); | |
67207b96 AB |
590 | |
591 | return 4; | |
592 | } | |
593 | ||
594 | static struct file_operations spufs_signal2_fops = { | |
595 | .open = spufs_pipe_open, | |
596 | .read = spufs_signal2_read, | |
597 | .write = spufs_signal2_write, | |
598 | }; | |
599 | ||
600 | static void spufs_signal1_type_set(void *data, u64 val) | |
601 | { | |
602 | struct spu_context *ctx = data; | |
67207b96 | 603 | |
8b3d6663 AB |
604 | spu_acquire(ctx); |
605 | ctx->ops->signal1_type_set(ctx, val); | |
606 | spu_release(ctx); | |
67207b96 AB |
607 | } |
608 | ||
609 | static u64 spufs_signal1_type_get(void *data) | |
610 | { | |
611 | struct spu_context *ctx = data; | |
8b3d6663 AB |
612 | u64 ret; |
613 | ||
614 | spu_acquire(ctx); | |
615 | ret = ctx->ops->signal1_type_get(ctx); | |
616 | spu_release(ctx); | |
617 | ||
618 | return ret; | |
67207b96 AB |
619 | } |
620 | DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, | |
621 | spufs_signal1_type_set, "%llu"); | |
622 | ||
623 | static void spufs_signal2_type_set(void *data, u64 val) | |
624 | { | |
625 | struct spu_context *ctx = data; | |
67207b96 | 626 | |
8b3d6663 AB |
627 | spu_acquire(ctx); |
628 | ctx->ops->signal2_type_set(ctx, val); | |
629 | spu_release(ctx); | |
67207b96 AB |
630 | } |
631 | ||
632 | static u64 spufs_signal2_type_get(void *data) | |
633 | { | |
634 | struct spu_context *ctx = data; | |
8b3d6663 AB |
635 | u64 ret; |
636 | ||
637 | spu_acquire(ctx); | |
638 | ret = ctx->ops->signal2_type_get(ctx); | |
639 | spu_release(ctx); | |
640 | ||
641 | return ret; | |
67207b96 AB |
642 | } |
643 | DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, | |
644 | spufs_signal2_type_set, "%llu"); | |
645 | ||
a33a7d73 AB |
646 | |
647 | static int spufs_mfc_open(struct inode *inode, struct file *file) | |
648 | { | |
649 | struct spufs_inode_info *i = SPUFS_I(inode); | |
650 | struct spu_context *ctx = i->i_ctx; | |
651 | ||
652 | /* we don't want to deal with DMA into other processes */ | |
653 | if (ctx->owner != current->mm) | |
654 | return -EINVAL; | |
655 | ||
656 | if (atomic_read(&inode->i_count) != 1) | |
657 | return -EBUSY; | |
658 | ||
659 | file->private_data = ctx; | |
660 | return nonseekable_open(inode, file); | |
661 | } | |
662 | ||
663 | /* interrupt-level mfc callback function. */ | |
664 | void spufs_mfc_callback(struct spu *spu) | |
665 | { | |
666 | struct spu_context *ctx = spu->ctx; | |
667 | ||
668 | wake_up_all(&ctx->mfc_wq); | |
669 | ||
670 | pr_debug("%s %s\n", __FUNCTION__, spu->name); | |
671 | if (ctx->mfc_fasync) { | |
672 | u32 free_elements, tagstatus; | |
673 | unsigned int mask; | |
674 | ||
675 | /* no need for spu_acquire in interrupt context */ | |
676 | free_elements = ctx->ops->get_mfc_free_elements(ctx); | |
677 | tagstatus = ctx->ops->read_mfc_tagstatus(ctx); | |
678 | ||
679 | mask = 0; | |
680 | if (free_elements & 0xffff) | |
681 | mask |= POLLOUT; | |
682 | if (tagstatus & ctx->tagwait) | |
683 | mask |= POLLIN; | |
684 | ||
685 | kill_fasync(&ctx->mfc_fasync, SIGIO, mask); | |
686 | } | |
687 | } | |
688 | ||
689 | static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) | |
690 | { | |
691 | /* See if there is one tag group is complete */ | |
692 | /* FIXME we need locking around tagwait */ | |
693 | *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; | |
694 | ctx->tagwait &= ~*status; | |
695 | if (*status) | |
696 | return 1; | |
697 | ||
698 | /* enable interrupt waiting for any tag group, | |
699 | may silently fail if interrupts are already enabled */ | |
700 | ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); | |
701 | return 0; | |
702 | } | |
703 | ||
704 | static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, | |
705 | size_t size, loff_t *pos) | |
706 | { | |
707 | struct spu_context *ctx = file->private_data; | |
708 | int ret = -EINVAL; | |
709 | u32 status; | |
710 | ||
711 | if (size != 4) | |
712 | goto out; | |
713 | ||
714 | spu_acquire(ctx); | |
715 | if (file->f_flags & O_NONBLOCK) { | |
716 | status = ctx->ops->read_mfc_tagstatus(ctx); | |
717 | if (!(status & ctx->tagwait)) | |
718 | ret = -EAGAIN; | |
719 | else | |
720 | ctx->tagwait &= ~status; | |
721 | } else { | |
722 | ret = spufs_wait(ctx->mfc_wq, | |
723 | spufs_read_mfc_tagstatus(ctx, &status)); | |
724 | } | |
725 | spu_release(ctx); | |
726 | ||
727 | if (ret) | |
728 | goto out; | |
729 | ||
730 | ret = 4; | |
731 | if (copy_to_user(buffer, &status, 4)) | |
732 | ret = -EFAULT; | |
733 | ||
734 | out: | |
735 | return ret; | |
736 | } | |
737 | ||
738 | static int spufs_check_valid_dma(struct mfc_dma_command *cmd) | |
739 | { | |
740 | pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, | |
741 | cmd->ea, cmd->size, cmd->tag, cmd->cmd); | |
742 | ||
743 | switch (cmd->cmd) { | |
744 | case MFC_PUT_CMD: | |
745 | case MFC_PUTF_CMD: | |
746 | case MFC_PUTB_CMD: | |
747 | case MFC_GET_CMD: | |
748 | case MFC_GETF_CMD: | |
749 | case MFC_GETB_CMD: | |
750 | break; | |
751 | default: | |
752 | pr_debug("invalid DMA opcode %x\n", cmd->cmd); | |
753 | return -EIO; | |
754 | } | |
755 | ||
756 | if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { | |
757 | pr_debug("invalid DMA alignment, ea %lx lsa %x\n", | |
758 | cmd->ea, cmd->lsa); | |
759 | return -EIO; | |
760 | } | |
761 | ||
762 | switch (cmd->size & 0xf) { | |
763 | case 1: | |
764 | break; | |
765 | case 2: | |
766 | if (cmd->lsa & 1) | |
767 | goto error; | |
768 | break; | |
769 | case 4: | |
770 | if (cmd->lsa & 3) | |
771 | goto error; | |
772 | break; | |
773 | case 8: | |
774 | if (cmd->lsa & 7) | |
775 | goto error; | |
776 | break; | |
777 | case 0: | |
778 | if (cmd->lsa & 15) | |
779 | goto error; | |
780 | break; | |
781 | error: | |
782 | default: | |
783 | pr_debug("invalid DMA alignment %x for size %x\n", | |
784 | cmd->lsa & 0xf, cmd->size); | |
785 | return -EIO; | |
786 | } | |
787 | ||
788 | if (cmd->size > 16 * 1024) { | |
789 | pr_debug("invalid DMA size %x\n", cmd->size); | |
790 | return -EIO; | |
791 | } | |
792 | ||
793 | if (cmd->tag & 0xfff0) { | |
794 | /* we reserve the higher tag numbers for kernel use */ | |
795 | pr_debug("invalid DMA tag\n"); | |
796 | return -EIO; | |
797 | } | |
798 | ||
799 | if (cmd->class) { | |
800 | /* not supported in this version */ | |
801 | pr_debug("invalid DMA class\n"); | |
802 | return -EIO; | |
803 | } | |
804 | ||
805 | return 0; | |
806 | } | |
807 | ||
808 | static int spu_send_mfc_command(struct spu_context *ctx, | |
809 | struct mfc_dma_command cmd, | |
810 | int *error) | |
811 | { | |
812 | *error = ctx->ops->send_mfc_command(ctx, &cmd); | |
813 | if (*error == -EAGAIN) { | |
814 | /* wait for any tag group to complete | |
815 | so we have space for the new command */ | |
816 | ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); | |
817 | /* try again, because the queue might be | |
818 | empty again */ | |
819 | *error = ctx->ops->send_mfc_command(ctx, &cmd); | |
820 | if (*error == -EAGAIN) | |
821 | return 0; | |
822 | } | |
823 | return 1; | |
824 | } | |
825 | ||
826 | static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, | |
827 | size_t size, loff_t *pos) | |
828 | { | |
829 | struct spu_context *ctx = file->private_data; | |
830 | struct mfc_dma_command cmd; | |
831 | int ret = -EINVAL; | |
832 | ||
833 | if (size != sizeof cmd) | |
834 | goto out; | |
835 | ||
836 | ret = -EFAULT; | |
837 | if (copy_from_user(&cmd, buffer, sizeof cmd)) | |
838 | goto out; | |
839 | ||
840 | ret = spufs_check_valid_dma(&cmd); | |
841 | if (ret) | |
842 | goto out; | |
843 | ||
844 | spu_acquire_runnable(ctx); | |
845 | if (file->f_flags & O_NONBLOCK) { | |
846 | ret = ctx->ops->send_mfc_command(ctx, &cmd); | |
847 | } else { | |
848 | int status; | |
849 | ret = spufs_wait(ctx->mfc_wq, | |
850 | spu_send_mfc_command(ctx, cmd, &status)); | |
851 | if (status) | |
852 | ret = status; | |
853 | } | |
854 | spu_release(ctx); | |
855 | ||
856 | if (ret) | |
857 | goto out; | |
858 | ||
859 | ctx->tagwait |= 1 << cmd.tag; | |
860 | ||
861 | out: | |
862 | return ret; | |
863 | } | |
864 | ||
865 | static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) | |
866 | { | |
867 | struct spu_context *ctx = file->private_data; | |
868 | u32 free_elements, tagstatus; | |
869 | unsigned int mask; | |
870 | ||
871 | spu_acquire(ctx); | |
872 | ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); | |
873 | free_elements = ctx->ops->get_mfc_free_elements(ctx); | |
874 | tagstatus = ctx->ops->read_mfc_tagstatus(ctx); | |
875 | spu_release(ctx); | |
876 | ||
877 | poll_wait(file, &ctx->mfc_wq, wait); | |
878 | ||
879 | mask = 0; | |
880 | if (free_elements & 0xffff) | |
881 | mask |= POLLOUT | POLLWRNORM; | |
882 | if (tagstatus & ctx->tagwait) | |
883 | mask |= POLLIN | POLLRDNORM; | |
884 | ||
885 | pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__, | |
886 | free_elements, tagstatus, ctx->tagwait); | |
887 | ||
888 | return mask; | |
889 | } | |
890 | ||
891 | static int spufs_mfc_flush(struct file *file) | |
892 | { | |
893 | struct spu_context *ctx = file->private_data; | |
894 | int ret; | |
895 | ||
896 | spu_acquire(ctx); | |
897 | #if 0 | |
898 | /* this currently hangs */ | |
899 | ret = spufs_wait(ctx->mfc_wq, | |
900 | ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); | |
901 | if (ret) | |
902 | goto out; | |
903 | ret = spufs_wait(ctx->mfc_wq, | |
904 | ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); | |
905 | out: | |
906 | #else | |
907 | ret = 0; | |
908 | #endif | |
909 | spu_release(ctx); | |
910 | ||
911 | return ret; | |
912 | } | |
913 | ||
914 | static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, | |
915 | int datasync) | |
916 | { | |
917 | return spufs_mfc_flush(file); | |
918 | } | |
919 | ||
920 | static int spufs_mfc_fasync(int fd, struct file *file, int on) | |
921 | { | |
922 | struct spu_context *ctx = file->private_data; | |
923 | ||
924 | return fasync_helper(fd, file, on, &ctx->mfc_fasync); | |
925 | } | |
926 | ||
927 | static struct file_operations spufs_mfc_fops = { | |
928 | .open = spufs_mfc_open, | |
929 | .read = spufs_mfc_read, | |
930 | .write = spufs_mfc_write, | |
931 | .poll = spufs_mfc_poll, | |
932 | .flush = spufs_mfc_flush, | |
933 | .fsync = spufs_mfc_fsync, | |
934 | .fasync = spufs_mfc_fasync, | |
935 | }; | |
936 | ||
67207b96 AB |
937 | static void spufs_npc_set(void *data, u64 val) |
938 | { | |
939 | struct spu_context *ctx = data; | |
8b3d6663 AB |
940 | spu_acquire(ctx); |
941 | ctx->ops->npc_write(ctx, val); | |
942 | spu_release(ctx); | |
67207b96 AB |
943 | } |
944 | ||
945 | static u64 spufs_npc_get(void *data) | |
946 | { | |
947 | struct spu_context *ctx = data; | |
948 | u64 ret; | |
8b3d6663 AB |
949 | spu_acquire(ctx); |
950 | ret = ctx->ops->npc_read(ctx); | |
951 | spu_release(ctx); | |
67207b96 AB |
952 | return ret; |
953 | } | |
954 | DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, "%llx\n") | |
955 | ||
8b3d6663 AB |
956 | static void spufs_decr_set(void *data, u64 val) |
957 | { | |
958 | struct spu_context *ctx = data; | |
959 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
960 | spu_acquire_saved(ctx); | |
961 | lscsa->decr.slot[0] = (u32) val; | |
962 | spu_release(ctx); | |
963 | } | |
964 | ||
965 | static u64 spufs_decr_get(void *data) | |
966 | { | |
967 | struct spu_context *ctx = data; | |
968 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
969 | u64 ret; | |
970 | spu_acquire_saved(ctx); | |
971 | ret = lscsa->decr.slot[0]; | |
972 | spu_release(ctx); | |
973 | return ret; | |
974 | } | |
975 | DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, | |
976 | "%llx\n") | |
977 | ||
978 | static void spufs_decr_status_set(void *data, u64 val) | |
979 | { | |
980 | struct spu_context *ctx = data; | |
981 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
982 | spu_acquire_saved(ctx); | |
983 | lscsa->decr_status.slot[0] = (u32) val; | |
984 | spu_release(ctx); | |
985 | } | |
986 | ||
987 | static u64 spufs_decr_status_get(void *data) | |
988 | { | |
989 | struct spu_context *ctx = data; | |
990 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
991 | u64 ret; | |
992 | spu_acquire_saved(ctx); | |
993 | ret = lscsa->decr_status.slot[0]; | |
994 | spu_release(ctx); | |
995 | return ret; | |
996 | } | |
997 | DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, | |
998 | spufs_decr_status_set, "%llx\n") | |
999 | ||
1000 | static void spufs_spu_tag_mask_set(void *data, u64 val) | |
1001 | { | |
1002 | struct spu_context *ctx = data; | |
1003 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
1004 | spu_acquire_saved(ctx); | |
1005 | lscsa->tag_mask.slot[0] = (u32) val; | |
1006 | spu_release(ctx); | |
1007 | } | |
1008 | ||
1009 | static u64 spufs_spu_tag_mask_get(void *data) | |
1010 | { | |
1011 | struct spu_context *ctx = data; | |
1012 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
1013 | u64 ret; | |
1014 | spu_acquire_saved(ctx); | |
1015 | ret = lscsa->tag_mask.slot[0]; | |
1016 | spu_release(ctx); | |
1017 | return ret; | |
1018 | } | |
1019 | DEFINE_SIMPLE_ATTRIBUTE(spufs_spu_tag_mask_ops, spufs_spu_tag_mask_get, | |
1020 | spufs_spu_tag_mask_set, "%llx\n") | |
1021 | ||
1022 | static void spufs_event_mask_set(void *data, u64 val) | |
1023 | { | |
1024 | struct spu_context *ctx = data; | |
1025 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
1026 | spu_acquire_saved(ctx); | |
1027 | lscsa->event_mask.slot[0] = (u32) val; | |
1028 | spu_release(ctx); | |
1029 | } | |
1030 | ||
1031 | static u64 spufs_event_mask_get(void *data) | |
1032 | { | |
1033 | struct spu_context *ctx = data; | |
1034 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
1035 | u64 ret; | |
1036 | spu_acquire_saved(ctx); | |
1037 | ret = lscsa->event_mask.slot[0]; | |
1038 | spu_release(ctx); | |
1039 | return ret; | |
1040 | } | |
1041 | DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, | |
1042 | spufs_event_mask_set, "%llx\n") | |
1043 | ||
1044 | static void spufs_srr0_set(void *data, u64 val) | |
1045 | { | |
1046 | struct spu_context *ctx = data; | |
1047 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
1048 | spu_acquire_saved(ctx); | |
1049 | lscsa->srr0.slot[0] = (u32) val; | |
1050 | spu_release(ctx); | |
1051 | } | |
1052 | ||
1053 | static u64 spufs_srr0_get(void *data) | |
1054 | { | |
1055 | struct spu_context *ctx = data; | |
1056 | struct spu_lscsa *lscsa = ctx->csa.lscsa; | |
1057 | u64 ret; | |
1058 | spu_acquire_saved(ctx); | |
1059 | ret = lscsa->srr0.slot[0]; | |
1060 | spu_release(ctx); | |
1061 | return ret; | |
1062 | } | |
1063 | DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, | |
1064 | "%llx\n") | |
1065 | ||
67207b96 AB |
1066 | struct tree_descr spufs_dir_contents[] = { |
1067 | { "mem", &spufs_mem_fops, 0666, }, | |
8b3d6663 | 1068 | { "regs", &spufs_regs_fops, 0666, }, |
67207b96 AB |
1069 | { "mbox", &spufs_mbox_fops, 0444, }, |
1070 | { "ibox", &spufs_ibox_fops, 0444, }, | |
1071 | { "wbox", &spufs_wbox_fops, 0222, }, | |
1072 | { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, | |
1073 | { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, | |
1074 | { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, | |
1075 | { "signal1", &spufs_signal1_fops, 0666, }, | |
1076 | { "signal2", &spufs_signal2_fops, 0666, }, | |
1077 | { "signal1_type", &spufs_signal1_type, 0666, }, | |
1078 | { "signal2_type", &spufs_signal2_type, 0666, }, | |
a33a7d73 | 1079 | { "mfc", &spufs_mfc_fops, 0666, }, |
67207b96 | 1080 | { "npc", &spufs_npc_ops, 0666, }, |
8b3d6663 AB |
1081 | { "fpcr", &spufs_fpcr_fops, 0666, }, |
1082 | { "decr", &spufs_decr_ops, 0666, }, | |
1083 | { "decr_status", &spufs_decr_status_ops, 0666, }, | |
1084 | { "spu_tag_mask", &spufs_spu_tag_mask_ops, 0666, }, | |
1085 | { "event_mask", &spufs_event_mask_ops, 0666, }, | |
1086 | { "srr0", &spufs_srr0_ops, 0666, }, | |
67207b96 AB |
1087 | {}, |
1088 | }; |