167207b96SArnd Bergmann /*
267207b96SArnd Bergmann  * SPU file system -- file contents
367207b96SArnd Bergmann  *
467207b96SArnd Bergmann  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
567207b96SArnd Bergmann  *
667207b96SArnd Bergmann  * Author: Arnd Bergmann <arndb@de.ibm.com>
767207b96SArnd Bergmann  *
867207b96SArnd Bergmann  * This program is free software; you can redistribute it and/or modify
967207b96SArnd Bergmann  * it under the terms of the GNU General Public License as published by
1067207b96SArnd Bergmann  * the Free Software Foundation; either version 2, or (at your option)
1167207b96SArnd Bergmann  * any later version.
1267207b96SArnd Bergmann  *
1367207b96SArnd Bergmann  * This program is distributed in the hope that it will be useful,
1467207b96SArnd Bergmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1567207b96SArnd Bergmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1667207b96SArnd Bergmann  * GNU General Public License for more details.
1767207b96SArnd Bergmann  *
1867207b96SArnd Bergmann  * You should have received a copy of the GNU General Public License
1967207b96SArnd Bergmann  * along with this program; if not, write to the Free Software
2067207b96SArnd Bergmann  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2167207b96SArnd Bergmann  */
2267207b96SArnd Bergmann 
23a33a7d73SArnd Bergmann #undef DEBUG
24a33a7d73SArnd Bergmann 
2567207b96SArnd Bergmann #include <linux/fs.h>
2667207b96SArnd Bergmann #include <linux/ioctl.h>
2767207b96SArnd Bergmann #include <linux/module.h>
28d88cfffaSArnd Bergmann #include <linux/pagemap.h>
2967207b96SArnd Bergmann #include <linux/poll.h>
305110459fSArnd Bergmann #include <linux/ptrace.h>
3167207b96SArnd Bergmann 
3267207b96SArnd Bergmann #include <asm/io.h>
3367207b96SArnd Bergmann #include <asm/semaphore.h>
3467207b96SArnd Bergmann #include <asm/spu.h>
35b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h>
3667207b96SArnd Bergmann #include <asm/uaccess.h>
3767207b96SArnd Bergmann 
3867207b96SArnd Bergmann #include "spufs.h"
3967207b96SArnd Bergmann 
4027d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000)
4127d5bf2aSBenjamin Herrenschmidt 
4267207b96SArnd Bergmann static int
4367207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file)
4467207b96SArnd Bergmann {
4567207b96SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
466df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
476df10a82SMark Nutter 	file->private_data = ctx;
486df10a82SMark Nutter 	ctx->local_store = inode->i_mapping;
4917e0e270SBenjamin Herrenschmidt 	smp_wmb();
5067207b96SArnd Bergmann 	return 0;
5167207b96SArnd Bergmann }
5267207b96SArnd Bergmann 
5367207b96SArnd Bergmann static ssize_t
54bf1ab978SDwayne Grant McConnell __spufs_mem_read(struct spu_context *ctx, char __user *buffer,
55bf1ab978SDwayne Grant McConnell 			size_t size, loff_t *pos)
56bf1ab978SDwayne Grant McConnell {
57bf1ab978SDwayne Grant McConnell 	char *local_store = ctx->ops->get_ls(ctx);
58bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos, local_store,
59bf1ab978SDwayne Grant McConnell 					LS_SIZE);
60bf1ab978SDwayne Grant McConnell }
61bf1ab978SDwayne Grant McConnell 
62bf1ab978SDwayne Grant McConnell static ssize_t
6367207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer,
6467207b96SArnd Bergmann 				size_t size, loff_t *pos)
6567207b96SArnd Bergmann {
6667207b96SArnd Bergmann 	int ret;
67bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
6867207b96SArnd Bergmann 
698b3d6663SArnd Bergmann 	spu_acquire(ctx);
70bf1ab978SDwayne Grant McConnell 	ret = __spufs_mem_read(ctx, buffer, size, pos);
718b3d6663SArnd Bergmann 	spu_release(ctx);
7267207b96SArnd Bergmann 	return ret;
7367207b96SArnd Bergmann }
7467207b96SArnd Bergmann 
7567207b96SArnd Bergmann static ssize_t
7667207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer,
7767207b96SArnd Bergmann 					size_t size, loff_t *pos)
7867207b96SArnd Bergmann {
7967207b96SArnd Bergmann 	struct spu_context *ctx = file->private_data;
808b3d6663SArnd Bergmann 	char *local_store;
818b3d6663SArnd Bergmann 	int ret;
8267207b96SArnd Bergmann 
8367207b96SArnd Bergmann 	size = min_t(ssize_t, LS_SIZE - *pos, size);
8467207b96SArnd Bergmann 	if (size <= 0)
8567207b96SArnd Bergmann 		return -EFBIG;
8667207b96SArnd Bergmann 	*pos += size;
878b3d6663SArnd Bergmann 
888b3d6663SArnd Bergmann 	spu_acquire(ctx);
898b3d6663SArnd Bergmann 
908b3d6663SArnd Bergmann 	local_store = ctx->ops->get_ls(ctx);
918b3d6663SArnd Bergmann 	ret = copy_from_user(local_store + *pos - size,
9267207b96SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
938b3d6663SArnd Bergmann 
948b3d6663SArnd Bergmann 	spu_release(ctx);
958b3d6663SArnd Bergmann 	return ret;
9667207b96SArnd Bergmann }
9767207b96SArnd Bergmann 
9878bde53eSBenjamin Herrenschmidt static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma,
9978bde53eSBenjamin Herrenschmidt 					  unsigned long address)
1008b3d6663SArnd Bergmann {
1018b3d6663SArnd Bergmann 	struct spu_context *ctx = vma->vm_file->private_data;
10278bde53eSBenjamin Herrenschmidt 	unsigned long pfn, offset = address - vma->vm_start;
10378bde53eSBenjamin Herrenschmidt 
1048b3d6663SArnd Bergmann 	offset += vma->vm_pgoff << PAGE_SHIFT;
1058b3d6663SArnd Bergmann 
106128b8546SMasato Noguchi 	if (offset >= LS_SIZE)
107128b8546SMasato Noguchi 		return NOPFN_SIGBUS;
108128b8546SMasato Noguchi 
1098b3d6663SArnd Bergmann 	spu_acquire(ctx);
1108b3d6663SArnd Bergmann 
111ac91cb8dSArnd Bergmann 	if (ctx->state == SPU_STATE_SAVED) {
112ac91cb8dSArnd Bergmann 		vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
113932f535dSArnd Bergmann 							& ~_PAGE_NO_CACHE);
11478bde53eSBenjamin Herrenschmidt 		pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset);
115ac91cb8dSArnd Bergmann 	} else {
116ac91cb8dSArnd Bergmann 		vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
117932f535dSArnd Bergmann 					     | _PAGE_NO_CACHE);
11878bde53eSBenjamin Herrenschmidt 		pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT;
119ac91cb8dSArnd Bergmann 	}
12078bde53eSBenjamin Herrenschmidt 	vm_insert_pfn(vma, address, pfn);
12178bde53eSBenjamin Herrenschmidt 
1228b3d6663SArnd Bergmann 	spu_release(ctx);
1238b3d6663SArnd Bergmann 
12478bde53eSBenjamin Herrenschmidt 	return NOPFN_REFAULT;
1258b3d6663SArnd Bergmann }
1268b3d6663SArnd Bergmann 
12778bde53eSBenjamin Herrenschmidt 
1288b3d6663SArnd Bergmann static struct vm_operations_struct spufs_mem_mmap_vmops = {
12978bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mem_mmap_nopfn,
1308b3d6663SArnd Bergmann };
1318b3d6663SArnd Bergmann 
13267207b96SArnd Bergmann static int
13367207b96SArnd Bergmann spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
13467207b96SArnd Bergmann {
1358b3d6663SArnd Bergmann 	if (!(vma->vm_flags & VM_SHARED))
1368b3d6663SArnd Bergmann 		return -EINVAL;
13767207b96SArnd Bergmann 
13878bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
13967207b96SArnd Bergmann 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
14067207b96SArnd Bergmann 				     | _PAGE_NO_CACHE);
1418b3d6663SArnd Bergmann 
1428b3d6663SArnd Bergmann 	vma->vm_ops = &spufs_mem_mmap_vmops;
14367207b96SArnd Bergmann 	return 0;
14467207b96SArnd Bergmann }
14567207b96SArnd Bergmann 
1465dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = {
14767207b96SArnd Bergmann 	.open	 = spufs_mem_open,
14867207b96SArnd Bergmann 	.read    = spufs_mem_read,
14967207b96SArnd Bergmann 	.write   = spufs_mem_write,
1508b3d6663SArnd Bergmann 	.llseek  = generic_file_llseek,
15167207b96SArnd Bergmann 	.mmap    = spufs_mem_mmap,
1528b3d6663SArnd Bergmann };
1538b3d6663SArnd Bergmann 
15478bde53eSBenjamin Herrenschmidt static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma,
1556df10a82SMark Nutter 				    unsigned long address,
15678bde53eSBenjamin Herrenschmidt 				    unsigned long ps_offs,
15727d5bf2aSBenjamin Herrenschmidt 				    unsigned long ps_size)
1586df10a82SMark Nutter {
1596df10a82SMark Nutter 	struct spu_context *ctx = vma->vm_file->private_data;
16078bde53eSBenjamin Herrenschmidt 	unsigned long area, offset = address - vma->vm_start;
1616df10a82SMark Nutter 	int ret;
1626df10a82SMark Nutter 
1636df10a82SMark Nutter 	offset += vma->vm_pgoff << PAGE_SHIFT;
16427d5bf2aSBenjamin Herrenschmidt 	if (offset >= ps_size)
16578bde53eSBenjamin Herrenschmidt 		return NOPFN_SIGBUS;
1666df10a82SMark Nutter 
16778bde53eSBenjamin Herrenschmidt 	/* error here usually means a signal.. we might want to test
16878bde53eSBenjamin Herrenschmidt 	 * the error code more precisely though
16978bde53eSBenjamin Herrenschmidt 	 */
17026bec673SChristoph Hellwig 	ret = spu_acquire_runnable(ctx, 0);
1716df10a82SMark Nutter 	if (ret)
17278bde53eSBenjamin Herrenschmidt 		return NOPFN_REFAULT;
1736df10a82SMark Nutter 
1746df10a82SMark Nutter 	area = ctx->spu->problem_phys + ps_offs;
17578bde53eSBenjamin Herrenschmidt 	vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT);
1766df10a82SMark Nutter 	spu_release(ctx);
1776df10a82SMark Nutter 
17878bde53eSBenjamin Herrenschmidt 	return NOPFN_REFAULT;
1796df10a82SMark Nutter }
1806df10a82SMark Nutter 
18127d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
18278bde53eSBenjamin Herrenschmidt static unsigned long spufs_cntl_mmap_nopfn(struct vm_area_struct *vma,
18378bde53eSBenjamin Herrenschmidt 					   unsigned long address)
1846df10a82SMark Nutter {
18578bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x4000, 0x1000);
1866df10a82SMark Nutter }
1876df10a82SMark Nutter 
1886df10a82SMark Nutter static struct vm_operations_struct spufs_cntl_mmap_vmops = {
18978bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_cntl_mmap_nopfn,
1906df10a82SMark Nutter };
1916df10a82SMark Nutter 
1926df10a82SMark Nutter /*
1936df10a82SMark Nutter  * mmap support for problem state control area [0x4000 - 0x4fff].
1946df10a82SMark Nutter  */
1956df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
1966df10a82SMark Nutter {
1976df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
1986df10a82SMark Nutter 		return -EINVAL;
1996df10a82SMark Nutter 
20078bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
2016df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
20223cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
2036df10a82SMark Nutter 
2046df10a82SMark Nutter 	vma->vm_ops = &spufs_cntl_mmap_vmops;
2056df10a82SMark Nutter 	return 0;
2066df10a82SMark Nutter }
20727d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
20827d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL
20927d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
2106df10a82SMark Nutter 
211e1dbff2bSArnd Bergmann static u64 spufs_cntl_get(void *data)
212e1dbff2bSArnd Bergmann {
213e1dbff2bSArnd Bergmann 	struct spu_context *ctx = data;
214e1dbff2bSArnd Bergmann 	u64 val;
215e1dbff2bSArnd Bergmann 
216e1dbff2bSArnd Bergmann 	spu_acquire(ctx);
217e1dbff2bSArnd Bergmann 	val = ctx->ops->status_read(ctx);
218e1dbff2bSArnd Bergmann 	spu_release(ctx);
219e1dbff2bSArnd Bergmann 
220e1dbff2bSArnd Bergmann 	return val;
221e1dbff2bSArnd Bergmann }
222e1dbff2bSArnd Bergmann 
223e1dbff2bSArnd Bergmann static void spufs_cntl_set(void *data, u64 val)
224e1dbff2bSArnd Bergmann {
225e1dbff2bSArnd Bergmann 	struct spu_context *ctx = data;
226e1dbff2bSArnd Bergmann 
227e1dbff2bSArnd Bergmann 	spu_acquire(ctx);
228e1dbff2bSArnd Bergmann 	ctx->ops->runcntl_write(ctx, val);
229e1dbff2bSArnd Bergmann 	spu_release(ctx);
230e1dbff2bSArnd Bergmann }
231e1dbff2bSArnd Bergmann 
2326df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file)
2336df10a82SMark Nutter {
2346df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
2356df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
2366df10a82SMark Nutter 
2376df10a82SMark Nutter 	file->private_data = ctx;
2386df10a82SMark Nutter 	ctx->cntl = inode->i_mapping;
23917e0e270SBenjamin Herrenschmidt 	smp_wmb();
240e1dbff2bSArnd Bergmann 	return simple_attr_open(inode, file, spufs_cntl_get,
241e1dbff2bSArnd Bergmann 					spufs_cntl_set, "0x%08lx");
2426df10a82SMark Nutter }
2436df10a82SMark Nutter 
2445dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = {
2456df10a82SMark Nutter 	.open = spufs_cntl_open,
246654e4aeeSNoguchi, Masato 	.release = simple_attr_close,
247e1dbff2bSArnd Bergmann 	.read = simple_attr_read,
248e1dbff2bSArnd Bergmann 	.write = simple_attr_write,
2496df10a82SMark Nutter 	.mmap = spufs_cntl_mmap,
2506df10a82SMark Nutter };
2516df10a82SMark Nutter 
2528b3d6663SArnd Bergmann static int
2538b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file)
2548b3d6663SArnd Bergmann {
2558b3d6663SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
2568b3d6663SArnd Bergmann 	file->private_data = i->i_ctx;
2578b3d6663SArnd Bergmann 	return 0;
2588b3d6663SArnd Bergmann }
2598b3d6663SArnd Bergmann 
2608b3d6663SArnd Bergmann static ssize_t
261bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer,
262bf1ab978SDwayne Grant McConnell 			size_t size, loff_t *pos)
263bf1ab978SDwayne Grant McConnell {
264bf1ab978SDwayne Grant McConnell 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
265bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos,
266bf1ab978SDwayne Grant McConnell 				      lscsa->gprs, sizeof lscsa->gprs);
267bf1ab978SDwayne Grant McConnell }
268bf1ab978SDwayne Grant McConnell 
269bf1ab978SDwayne Grant McConnell static ssize_t
2708b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer,
2718b3d6663SArnd Bergmann 		size_t size, loff_t *pos)
2728b3d6663SArnd Bergmann {
2738b3d6663SArnd Bergmann 	int ret;
274bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
2758b3d6663SArnd Bergmann 
2768b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
277bf1ab978SDwayne Grant McConnell 	ret = __spufs_regs_read(ctx, buffer, size, pos);
2788b3d6663SArnd Bergmann 	spu_release(ctx);
2798b3d6663SArnd Bergmann 	return ret;
2808b3d6663SArnd Bergmann }
2818b3d6663SArnd Bergmann 
2828b3d6663SArnd Bergmann static ssize_t
2838b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer,
2848b3d6663SArnd Bergmann 		 size_t size, loff_t *pos)
2858b3d6663SArnd Bergmann {
2868b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
2878b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
2888b3d6663SArnd Bergmann 	int ret;
2898b3d6663SArnd Bergmann 
2908b3d6663SArnd Bergmann 	size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size);
2918b3d6663SArnd Bergmann 	if (size <= 0)
2928b3d6663SArnd Bergmann 		return -EFBIG;
2938b3d6663SArnd Bergmann 	*pos += size;
2948b3d6663SArnd Bergmann 
2958b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
2968b3d6663SArnd Bergmann 
2978b3d6663SArnd Bergmann 	ret = copy_from_user(lscsa->gprs + *pos - size,
2988b3d6663SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
2998b3d6663SArnd Bergmann 
3008b3d6663SArnd Bergmann 	spu_release(ctx);
3018b3d6663SArnd Bergmann 	return ret;
3028b3d6663SArnd Bergmann }
3038b3d6663SArnd Bergmann 
3045dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = {
3058b3d6663SArnd Bergmann 	.open	 = spufs_regs_open,
3068b3d6663SArnd Bergmann 	.read    = spufs_regs_read,
3078b3d6663SArnd Bergmann 	.write   = spufs_regs_write,
3088b3d6663SArnd Bergmann 	.llseek  = generic_file_llseek,
3098b3d6663SArnd Bergmann };
3108b3d6663SArnd Bergmann 
3118b3d6663SArnd Bergmann static ssize_t
312bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer,
313bf1ab978SDwayne Grant McConnell 			size_t size, loff_t * pos)
314bf1ab978SDwayne Grant McConnell {
315bf1ab978SDwayne Grant McConnell 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
316bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos,
317bf1ab978SDwayne Grant McConnell 				      &lscsa->fpcr, sizeof(lscsa->fpcr));
318bf1ab978SDwayne Grant McConnell }
319bf1ab978SDwayne Grant McConnell 
320bf1ab978SDwayne Grant McConnell static ssize_t
3218b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer,
3228b3d6663SArnd Bergmann 		size_t size, loff_t * pos)
3238b3d6663SArnd Bergmann {
3248b3d6663SArnd Bergmann 	int ret;
325bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
3268b3d6663SArnd Bergmann 
3278b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
328bf1ab978SDwayne Grant McConnell 	ret = __spufs_fpcr_read(ctx, buffer, size, pos);
3298b3d6663SArnd Bergmann 	spu_release(ctx);
3308b3d6663SArnd Bergmann 	return ret;
3318b3d6663SArnd Bergmann }
3328b3d6663SArnd Bergmann 
3338b3d6663SArnd Bergmann static ssize_t
3348b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer,
3358b3d6663SArnd Bergmann 		 size_t size, loff_t * pos)
3368b3d6663SArnd Bergmann {
3378b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
3388b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
3398b3d6663SArnd Bergmann 	int ret;
3408b3d6663SArnd Bergmann 
3418b3d6663SArnd Bergmann 	size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
3428b3d6663SArnd Bergmann 	if (size <= 0)
3438b3d6663SArnd Bergmann 		return -EFBIG;
3448b3d6663SArnd Bergmann 	*pos += size;
3458b3d6663SArnd Bergmann 
3468b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
3478b3d6663SArnd Bergmann 
3488b3d6663SArnd Bergmann 	ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
3498b3d6663SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
3508b3d6663SArnd Bergmann 
3518b3d6663SArnd Bergmann 	spu_release(ctx);
3528b3d6663SArnd Bergmann 	return ret;
3538b3d6663SArnd Bergmann }
3548b3d6663SArnd Bergmann 
3555dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = {
3568b3d6663SArnd Bergmann 	.open = spufs_regs_open,
3578b3d6663SArnd Bergmann 	.read = spufs_fpcr_read,
3588b3d6663SArnd Bergmann 	.write = spufs_fpcr_write,
35967207b96SArnd Bergmann 	.llseek = generic_file_llseek,
36067207b96SArnd Bergmann };
36167207b96SArnd Bergmann 
36267207b96SArnd Bergmann /* generic open function for all pipe-like files */
36367207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file)
36467207b96SArnd Bergmann {
36567207b96SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
36667207b96SArnd Bergmann 	file->private_data = i->i_ctx;
36767207b96SArnd Bergmann 
36867207b96SArnd Bergmann 	return nonseekable_open(inode, file);
36967207b96SArnd Bergmann }
37067207b96SArnd Bergmann 
371cdcc89bbSArnd Bergmann /*
372cdcc89bbSArnd Bergmann  * Read as many bytes from the mailbox as possible, until
373cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
374cdcc89bbSArnd Bergmann  *
375cdcc89bbSArnd Bergmann  * - no more data available in the mailbox
376cdcc89bbSArnd Bergmann  * - end of the user provided buffer
377cdcc89bbSArnd Bergmann  * - end of the mapped area
378cdcc89bbSArnd Bergmann  */
37967207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
38067207b96SArnd Bergmann 			size_t len, loff_t *pos)
38167207b96SArnd Bergmann {
3828b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
383cdcc89bbSArnd Bergmann 	u32 mbox_data, __user *udata;
384cdcc89bbSArnd Bergmann 	ssize_t count;
38567207b96SArnd Bergmann 
38667207b96SArnd Bergmann 	if (len < 4)
38767207b96SArnd Bergmann 		return -EINVAL;
38867207b96SArnd Bergmann 
389cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_WRITE, buf, len))
39067207b96SArnd Bergmann 		return -EFAULT;
39167207b96SArnd Bergmann 
392cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
393cdcc89bbSArnd Bergmann 
394cdcc89bbSArnd Bergmann 	spu_acquire(ctx);
395274cef5eSArnd Bergmann 	for (count = 0; (count + 4) <= len; count += 4, udata++) {
396cdcc89bbSArnd Bergmann 		int ret;
397cdcc89bbSArnd Bergmann 		ret = ctx->ops->mbox_read(ctx, &mbox_data);
398cdcc89bbSArnd Bergmann 		if (ret == 0)
399cdcc89bbSArnd Bergmann 			break;
400cdcc89bbSArnd Bergmann 
401cdcc89bbSArnd Bergmann 		/*
402cdcc89bbSArnd Bergmann 		 * at the end of the mapped area, we can fault
403cdcc89bbSArnd Bergmann 		 * but still need to return the data we have
404cdcc89bbSArnd Bergmann 		 * read successfully so far.
405cdcc89bbSArnd Bergmann 		 */
406cdcc89bbSArnd Bergmann 		ret = __put_user(mbox_data, udata);
407cdcc89bbSArnd Bergmann 		if (ret) {
408cdcc89bbSArnd Bergmann 			if (!count)
409cdcc89bbSArnd Bergmann 				count = -EFAULT;
410cdcc89bbSArnd Bergmann 			break;
411cdcc89bbSArnd Bergmann 		}
412cdcc89bbSArnd Bergmann 	}
413cdcc89bbSArnd Bergmann 	spu_release(ctx);
414cdcc89bbSArnd Bergmann 
415cdcc89bbSArnd Bergmann 	if (!count)
416cdcc89bbSArnd Bergmann 		count = -EAGAIN;
417cdcc89bbSArnd Bergmann 
418cdcc89bbSArnd Bergmann 	return count;
41967207b96SArnd Bergmann }
42067207b96SArnd Bergmann 
4215dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = {
42267207b96SArnd Bergmann 	.open	= spufs_pipe_open,
42367207b96SArnd Bergmann 	.read	= spufs_mbox_read,
42467207b96SArnd Bergmann };
42567207b96SArnd Bergmann 
42667207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
42767207b96SArnd Bergmann 			size_t len, loff_t *pos)
42867207b96SArnd Bergmann {
4298b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
43067207b96SArnd Bergmann 	u32 mbox_stat;
43167207b96SArnd Bergmann 
43267207b96SArnd Bergmann 	if (len < 4)
43367207b96SArnd Bergmann 		return -EINVAL;
43467207b96SArnd Bergmann 
4358b3d6663SArnd Bergmann 	spu_acquire(ctx);
4368b3d6663SArnd Bergmann 
4378b3d6663SArnd Bergmann 	mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff;
4388b3d6663SArnd Bergmann 
4398b3d6663SArnd Bergmann 	spu_release(ctx);
44067207b96SArnd Bergmann 
44167207b96SArnd Bergmann 	if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat))
44267207b96SArnd Bergmann 		return -EFAULT;
44367207b96SArnd Bergmann 
44467207b96SArnd Bergmann 	return 4;
44567207b96SArnd Bergmann }
44667207b96SArnd Bergmann 
4475dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = {
44867207b96SArnd Bergmann 	.open	= spufs_pipe_open,
44967207b96SArnd Bergmann 	.read	= spufs_mbox_stat_read,
45067207b96SArnd Bergmann };
45167207b96SArnd Bergmann 
45267207b96SArnd Bergmann /* low-level ibox access function */
4538b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data)
45467207b96SArnd Bergmann {
4558b3d6663SArnd Bergmann 	return ctx->ops->ibox_read(ctx, data);
45667207b96SArnd Bergmann }
45767207b96SArnd Bergmann 
45867207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on)
45967207b96SArnd Bergmann {
4608b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
4618b3d6663SArnd Bergmann 
4628b3d6663SArnd Bergmann 	return fasync_helper(fd, file, on, &ctx->ibox_fasync);
4638b3d6663SArnd Bergmann }
4648b3d6663SArnd Bergmann 
4658b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */
4668b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu)
4678b3d6663SArnd Bergmann {
4688b3d6663SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
4698b3d6663SArnd Bergmann 
4708b3d6663SArnd Bergmann 	wake_up_all(&ctx->ibox_wq);
4718b3d6663SArnd Bergmann 	kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN);
47267207b96SArnd Bergmann }
47367207b96SArnd Bergmann 
474cdcc89bbSArnd Bergmann /*
475cdcc89bbSArnd Bergmann  * Read as many bytes from the interrupt mailbox as possible, until
476cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
477cdcc89bbSArnd Bergmann  *
478cdcc89bbSArnd Bergmann  * - no more data available in the mailbox
479cdcc89bbSArnd Bergmann  * - end of the user provided buffer
480cdcc89bbSArnd Bergmann  * - end of the mapped area
481cdcc89bbSArnd Bergmann  *
482cdcc89bbSArnd Bergmann  * If the file is opened without O_NONBLOCK, we wait here until
483cdcc89bbSArnd Bergmann  * any data is available, but return when we have been able to
484cdcc89bbSArnd Bergmann  * read something.
485cdcc89bbSArnd Bergmann  */
48667207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
48767207b96SArnd Bergmann 			size_t len, loff_t *pos)
48867207b96SArnd Bergmann {
4898b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
490cdcc89bbSArnd Bergmann 	u32 ibox_data, __user *udata;
491cdcc89bbSArnd Bergmann 	ssize_t count;
49267207b96SArnd Bergmann 
49367207b96SArnd Bergmann 	if (len < 4)
49467207b96SArnd Bergmann 		return -EINVAL;
49567207b96SArnd Bergmann 
496cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_WRITE, buf, len))
497cdcc89bbSArnd Bergmann 		return -EFAULT;
498cdcc89bbSArnd Bergmann 
499cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
500cdcc89bbSArnd Bergmann 
5018b3d6663SArnd Bergmann 	spu_acquire(ctx);
50267207b96SArnd Bergmann 
503cdcc89bbSArnd Bergmann 	/* wait only for the first element */
504cdcc89bbSArnd Bergmann 	count = 0;
50567207b96SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
5068b3d6663SArnd Bergmann 		if (!spu_ibox_read(ctx, &ibox_data))
507cdcc89bbSArnd Bergmann 			count = -EAGAIN;
50867207b96SArnd Bergmann 	} else {
509cdcc89bbSArnd Bergmann 		count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
510cdcc89bbSArnd Bergmann 	}
511cdcc89bbSArnd Bergmann 	if (count)
512cdcc89bbSArnd Bergmann 		goto out;
513cdcc89bbSArnd Bergmann 
514cdcc89bbSArnd Bergmann 	/* if we can't write at all, return -EFAULT */
515cdcc89bbSArnd Bergmann 	count = __put_user(ibox_data, udata);
516cdcc89bbSArnd Bergmann 	if (count)
517cdcc89bbSArnd Bergmann 		goto out;
518cdcc89bbSArnd Bergmann 
519cdcc89bbSArnd Bergmann 	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
520cdcc89bbSArnd Bergmann 		int ret;
521cdcc89bbSArnd Bergmann 		ret = ctx->ops->ibox_read(ctx, &ibox_data);
522cdcc89bbSArnd Bergmann 		if (ret == 0)
523cdcc89bbSArnd Bergmann 			break;
524cdcc89bbSArnd Bergmann 		/*
525cdcc89bbSArnd Bergmann 		 * at the end of the mapped area, we can fault
526cdcc89bbSArnd Bergmann 		 * but still need to return the data we have
527cdcc89bbSArnd Bergmann 		 * read successfully so far.
528cdcc89bbSArnd Bergmann 		 */
529cdcc89bbSArnd Bergmann 		ret = __put_user(ibox_data, udata);
530cdcc89bbSArnd Bergmann 		if (ret)
531cdcc89bbSArnd Bergmann 			break;
53267207b96SArnd Bergmann 	}
53367207b96SArnd Bergmann 
534cdcc89bbSArnd Bergmann out:
5358b3d6663SArnd Bergmann 	spu_release(ctx);
5368b3d6663SArnd Bergmann 
537cdcc89bbSArnd Bergmann 	return count;
53867207b96SArnd Bergmann }
53967207b96SArnd Bergmann 
54067207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait)
54167207b96SArnd Bergmann {
5428b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
54367207b96SArnd Bergmann 	unsigned int mask;
54467207b96SArnd Bergmann 
5458b3d6663SArnd Bergmann 	poll_wait(file, &ctx->ibox_wq, wait);
54667207b96SArnd Bergmann 
5473a843d7cSArnd Bergmann 	spu_acquire(ctx);
5483a843d7cSArnd Bergmann 	mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM);
5493a843d7cSArnd Bergmann 	spu_release(ctx);
55067207b96SArnd Bergmann 
55167207b96SArnd Bergmann 	return mask;
55267207b96SArnd Bergmann }
55367207b96SArnd Bergmann 
5545dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = {
55567207b96SArnd Bergmann 	.open	= spufs_pipe_open,
55667207b96SArnd Bergmann 	.read	= spufs_ibox_read,
55767207b96SArnd Bergmann 	.poll	= spufs_ibox_poll,
55867207b96SArnd Bergmann 	.fasync	= spufs_ibox_fasync,
55967207b96SArnd Bergmann };
56067207b96SArnd Bergmann 
56167207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
56267207b96SArnd Bergmann 			size_t len, loff_t *pos)
56367207b96SArnd Bergmann {
5648b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
56567207b96SArnd Bergmann 	u32 ibox_stat;
56667207b96SArnd Bergmann 
56767207b96SArnd Bergmann 	if (len < 4)
56867207b96SArnd Bergmann 		return -EINVAL;
56967207b96SArnd Bergmann 
5708b3d6663SArnd Bergmann 	spu_acquire(ctx);
5718b3d6663SArnd Bergmann 	ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff;
5728b3d6663SArnd Bergmann 	spu_release(ctx);
57367207b96SArnd Bergmann 
57467207b96SArnd Bergmann 	if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat))
57567207b96SArnd Bergmann 		return -EFAULT;
57667207b96SArnd Bergmann 
57767207b96SArnd Bergmann 	return 4;
57867207b96SArnd Bergmann }
57967207b96SArnd Bergmann 
5805dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = {
58167207b96SArnd Bergmann 	.open	= spufs_pipe_open,
58267207b96SArnd Bergmann 	.read	= spufs_ibox_stat_read,
58367207b96SArnd Bergmann };
58467207b96SArnd Bergmann 
58567207b96SArnd Bergmann /* low-level mailbox write */
5868b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data)
58767207b96SArnd Bergmann {
5888b3d6663SArnd Bergmann 	return ctx->ops->wbox_write(ctx, data);
58967207b96SArnd Bergmann }
59067207b96SArnd Bergmann 
59167207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on)
59267207b96SArnd Bergmann {
5938b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
5948b3d6663SArnd Bergmann 	int ret;
5958b3d6663SArnd Bergmann 
5968b3d6663SArnd Bergmann 	ret = fasync_helper(fd, file, on, &ctx->wbox_fasync);
5978b3d6663SArnd Bergmann 
5988b3d6663SArnd Bergmann 	return ret;
5998b3d6663SArnd Bergmann }
6008b3d6663SArnd Bergmann 
6018b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */
6028b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu)
6038b3d6663SArnd Bergmann {
6048b3d6663SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
6058b3d6663SArnd Bergmann 
6068b3d6663SArnd Bergmann 	wake_up_all(&ctx->wbox_wq);
6078b3d6663SArnd Bergmann 	kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT);
60867207b96SArnd Bergmann }
60967207b96SArnd Bergmann 
610cdcc89bbSArnd Bergmann /*
611cdcc89bbSArnd Bergmann  * Write as many bytes to the interrupt mailbox as possible, until
612cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
613cdcc89bbSArnd Bergmann  *
614cdcc89bbSArnd Bergmann  * - the mailbox is full
615cdcc89bbSArnd Bergmann  * - end of the user provided buffer
616cdcc89bbSArnd Bergmann  * - end of the mapped area
617cdcc89bbSArnd Bergmann  *
618cdcc89bbSArnd Bergmann  * If the file is opened without O_NONBLOCK, we wait here until
619cdcc89bbSArnd Bergmann  * space is availabyl, but return when we have been able to
620cdcc89bbSArnd Bergmann  * write something.
621cdcc89bbSArnd Bergmann  */
62267207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
62367207b96SArnd Bergmann 			size_t len, loff_t *pos)
62467207b96SArnd Bergmann {
6258b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
626cdcc89bbSArnd Bergmann 	u32 wbox_data, __user *udata;
627cdcc89bbSArnd Bergmann 	ssize_t count;
62867207b96SArnd Bergmann 
62967207b96SArnd Bergmann 	if (len < 4)
63067207b96SArnd Bergmann 		return -EINVAL;
63167207b96SArnd Bergmann 
632cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
633cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_READ, buf, len))
634cdcc89bbSArnd Bergmann 		return -EFAULT;
635cdcc89bbSArnd Bergmann 
636cdcc89bbSArnd Bergmann 	if (__get_user(wbox_data, udata))
63767207b96SArnd Bergmann 		return -EFAULT;
63867207b96SArnd Bergmann 
6398b3d6663SArnd Bergmann 	spu_acquire(ctx);
6408b3d6663SArnd Bergmann 
641cdcc89bbSArnd Bergmann 	/*
642cdcc89bbSArnd Bergmann 	 * make sure we can at least write one element, by waiting
643cdcc89bbSArnd Bergmann 	 * in case of !O_NONBLOCK
644cdcc89bbSArnd Bergmann 	 */
645cdcc89bbSArnd Bergmann 	count = 0;
64667207b96SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
6478b3d6663SArnd Bergmann 		if (!spu_wbox_write(ctx, wbox_data))
648cdcc89bbSArnd Bergmann 			count = -EAGAIN;
64967207b96SArnd Bergmann 	} else {
650cdcc89bbSArnd Bergmann 		count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
65167207b96SArnd Bergmann 	}
65267207b96SArnd Bergmann 
653cdcc89bbSArnd Bergmann 	if (count)
654cdcc89bbSArnd Bergmann 		goto out;
6558b3d6663SArnd Bergmann 
656cdcc89bbSArnd Bergmann 	/* write aѕ much as possible */
657cdcc89bbSArnd Bergmann 	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
658cdcc89bbSArnd Bergmann 		int ret;
659cdcc89bbSArnd Bergmann 		ret = __get_user(wbox_data, udata);
660cdcc89bbSArnd Bergmann 		if (ret)
661cdcc89bbSArnd Bergmann 			break;
662cdcc89bbSArnd Bergmann 
663cdcc89bbSArnd Bergmann 		ret = spu_wbox_write(ctx, wbox_data);
664cdcc89bbSArnd Bergmann 		if (ret == 0)
665cdcc89bbSArnd Bergmann 			break;
666cdcc89bbSArnd Bergmann 	}
667cdcc89bbSArnd Bergmann 
668cdcc89bbSArnd Bergmann out:
669cdcc89bbSArnd Bergmann 	spu_release(ctx);
670cdcc89bbSArnd Bergmann 	return count;
67167207b96SArnd Bergmann }
67267207b96SArnd Bergmann 
67367207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait)
67467207b96SArnd Bergmann {
6758b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
67667207b96SArnd Bergmann 	unsigned int mask;
67767207b96SArnd Bergmann 
6788b3d6663SArnd Bergmann 	poll_wait(file, &ctx->wbox_wq, wait);
67967207b96SArnd Bergmann 
6803a843d7cSArnd Bergmann 	spu_acquire(ctx);
6813a843d7cSArnd Bergmann 	mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM);
6823a843d7cSArnd Bergmann 	spu_release(ctx);
68367207b96SArnd Bergmann 
68467207b96SArnd Bergmann 	return mask;
68567207b96SArnd Bergmann }
68667207b96SArnd Bergmann 
6875dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = {
68867207b96SArnd Bergmann 	.open	= spufs_pipe_open,
68967207b96SArnd Bergmann 	.write	= spufs_wbox_write,
69067207b96SArnd Bergmann 	.poll	= spufs_wbox_poll,
69167207b96SArnd Bergmann 	.fasync	= spufs_wbox_fasync,
69267207b96SArnd Bergmann };
69367207b96SArnd Bergmann 
69467207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
69567207b96SArnd Bergmann 			size_t len, loff_t *pos)
69667207b96SArnd Bergmann {
6978b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
69867207b96SArnd Bergmann 	u32 wbox_stat;
69967207b96SArnd Bergmann 
70067207b96SArnd Bergmann 	if (len < 4)
70167207b96SArnd Bergmann 		return -EINVAL;
70267207b96SArnd Bergmann 
7038b3d6663SArnd Bergmann 	spu_acquire(ctx);
7048b3d6663SArnd Bergmann 	wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff;
7058b3d6663SArnd Bergmann 	spu_release(ctx);
70667207b96SArnd Bergmann 
70767207b96SArnd Bergmann 	if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat))
70867207b96SArnd Bergmann 		return -EFAULT;
70967207b96SArnd Bergmann 
71067207b96SArnd Bergmann 	return 4;
71167207b96SArnd Bergmann }
71267207b96SArnd Bergmann 
7135dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = {
71467207b96SArnd Bergmann 	.open	= spufs_pipe_open,
71567207b96SArnd Bergmann 	.read	= spufs_wbox_stat_read,
71667207b96SArnd Bergmann };
71767207b96SArnd Bergmann 
7186df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file)
7196df10a82SMark Nutter {
7206df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
7216df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
7226df10a82SMark Nutter 	file->private_data = ctx;
7236df10a82SMark Nutter 	ctx->signal1 = inode->i_mapping;
72417e0e270SBenjamin Herrenschmidt 	smp_wmb();
7256df10a82SMark Nutter 	return nonseekable_open(inode, file);
7266df10a82SMark Nutter }
7276df10a82SMark Nutter 
728bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
72967207b96SArnd Bergmann 			size_t len, loff_t *pos)
73067207b96SArnd Bergmann {
73117f88cebSDwayne Grant McConnell 	int ret = 0;
73267207b96SArnd Bergmann 	u32 data;
73367207b96SArnd Bergmann 
73467207b96SArnd Bergmann 	if (len < 4)
73567207b96SArnd Bergmann 		return -EINVAL;
73667207b96SArnd Bergmann 
73717f88cebSDwayne Grant McConnell 	if (ctx->csa.spu_chnlcnt_RW[3]) {
73817f88cebSDwayne Grant McConnell 		data = ctx->csa.spu_chnldata_RW[3];
73917f88cebSDwayne Grant McConnell 		ret = 4;
74017f88cebSDwayne Grant McConnell 	}
7418b3d6663SArnd Bergmann 
74217f88cebSDwayne Grant McConnell 	if (!ret)
74317f88cebSDwayne Grant McConnell 		goto out;
74417f88cebSDwayne Grant McConnell 
74567207b96SArnd Bergmann 	if (copy_to_user(buf, &data, 4))
74667207b96SArnd Bergmann 		return -EFAULT;
74767207b96SArnd Bergmann 
74817f88cebSDwayne Grant McConnell out:
74917f88cebSDwayne Grant McConnell 	return ret;
75067207b96SArnd Bergmann }
75167207b96SArnd Bergmann 
752bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
753bf1ab978SDwayne Grant McConnell 			size_t len, loff_t *pos)
754bf1ab978SDwayne Grant McConnell {
755bf1ab978SDwayne Grant McConnell 	int ret;
756bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
757bf1ab978SDwayne Grant McConnell 
758bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
759bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal1_read(ctx, buf, len, pos);
760bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
761bf1ab978SDwayne Grant McConnell 
762bf1ab978SDwayne Grant McConnell 	return ret;
763bf1ab978SDwayne Grant McConnell }
764bf1ab978SDwayne Grant McConnell 
76567207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
76667207b96SArnd Bergmann 			size_t len, loff_t *pos)
76767207b96SArnd Bergmann {
76867207b96SArnd Bergmann 	struct spu_context *ctx;
76967207b96SArnd Bergmann 	u32 data;
77067207b96SArnd Bergmann 
77167207b96SArnd Bergmann 	ctx = file->private_data;
77267207b96SArnd Bergmann 
77367207b96SArnd Bergmann 	if (len < 4)
77467207b96SArnd Bergmann 		return -EINVAL;
77567207b96SArnd Bergmann 
77667207b96SArnd Bergmann 	if (copy_from_user(&data, buf, 4))
77767207b96SArnd Bergmann 		return -EFAULT;
77867207b96SArnd Bergmann 
7798b3d6663SArnd Bergmann 	spu_acquire(ctx);
7808b3d6663SArnd Bergmann 	ctx->ops->signal1_write(ctx, data);
7818b3d6663SArnd Bergmann 	spu_release(ctx);
78267207b96SArnd Bergmann 
78367207b96SArnd Bergmann 	return 4;
78467207b96SArnd Bergmann }
78567207b96SArnd Bergmann 
78678bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal1_mmap_nopfn(struct vm_area_struct *vma,
78778bde53eSBenjamin Herrenschmidt 					      unsigned long address)
7886df10a82SMark Nutter {
78927d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000
79078bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x14000, 0x1000);
79127d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000
79227d5bf2aSBenjamin Herrenschmidt 	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
79327d5bf2aSBenjamin Herrenschmidt 	 * signal 1 and 2 area
79427d5bf2aSBenjamin Herrenschmidt 	 */
79578bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
79627d5bf2aSBenjamin Herrenschmidt #else
79727d5bf2aSBenjamin Herrenschmidt #error unsupported page size
79827d5bf2aSBenjamin Herrenschmidt #endif
7996df10a82SMark Nutter }
8006df10a82SMark Nutter 
8016df10a82SMark Nutter static struct vm_operations_struct spufs_signal1_mmap_vmops = {
80278bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_signal1_mmap_nopfn,
8036df10a82SMark Nutter };
8046df10a82SMark Nutter 
8056df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
8066df10a82SMark Nutter {
8076df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
8086df10a82SMark Nutter 		return -EINVAL;
8096df10a82SMark Nutter 
81078bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
8116df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
81223cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
8136df10a82SMark Nutter 
8146df10a82SMark Nutter 	vma->vm_ops = &spufs_signal1_mmap_vmops;
8156df10a82SMark Nutter 	return 0;
8166df10a82SMark Nutter }
8176df10a82SMark Nutter 
8185dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = {
8196df10a82SMark Nutter 	.open = spufs_signal1_open,
82067207b96SArnd Bergmann 	.read = spufs_signal1_read,
82167207b96SArnd Bergmann 	.write = spufs_signal1_write,
8226df10a82SMark Nutter 	.mmap = spufs_signal1_mmap,
82367207b96SArnd Bergmann };
82467207b96SArnd Bergmann 
8256df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file)
8266df10a82SMark Nutter {
8276df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
8286df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
8296df10a82SMark Nutter 	file->private_data = ctx;
8306df10a82SMark Nutter 	ctx->signal2 = inode->i_mapping;
83117e0e270SBenjamin Herrenschmidt 	smp_wmb();
8326df10a82SMark Nutter 	return nonseekable_open(inode, file);
8336df10a82SMark Nutter }
8346df10a82SMark Nutter 
835bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
83667207b96SArnd Bergmann 			size_t len, loff_t *pos)
83767207b96SArnd Bergmann {
83817f88cebSDwayne Grant McConnell 	int ret = 0;
83967207b96SArnd Bergmann 	u32 data;
84067207b96SArnd Bergmann 
84167207b96SArnd Bergmann 	if (len < 4)
84267207b96SArnd Bergmann 		return -EINVAL;
84367207b96SArnd Bergmann 
84417f88cebSDwayne Grant McConnell 	if (ctx->csa.spu_chnlcnt_RW[4]) {
84517f88cebSDwayne Grant McConnell 		data =  ctx->csa.spu_chnldata_RW[4];
84617f88cebSDwayne Grant McConnell 		ret = 4;
84717f88cebSDwayne Grant McConnell 	}
8488b3d6663SArnd Bergmann 
84917f88cebSDwayne Grant McConnell 	if (!ret)
85017f88cebSDwayne Grant McConnell 		goto out;
85117f88cebSDwayne Grant McConnell 
85267207b96SArnd Bergmann 	if (copy_to_user(buf, &data, 4))
85367207b96SArnd Bergmann 		return -EFAULT;
85467207b96SArnd Bergmann 
85517f88cebSDwayne Grant McConnell out:
856bf1ab978SDwayne Grant McConnell 	return ret;
857bf1ab978SDwayne Grant McConnell }
858bf1ab978SDwayne Grant McConnell 
859bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
860bf1ab978SDwayne Grant McConnell 			size_t len, loff_t *pos)
861bf1ab978SDwayne Grant McConnell {
862bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
863bf1ab978SDwayne Grant McConnell 	int ret;
864bf1ab978SDwayne Grant McConnell 
865bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
866bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal2_read(ctx, buf, len, pos);
867bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
868bf1ab978SDwayne Grant McConnell 
869bf1ab978SDwayne Grant McConnell 	return ret;
87067207b96SArnd Bergmann }
87167207b96SArnd Bergmann 
87267207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
87367207b96SArnd Bergmann 			size_t len, loff_t *pos)
87467207b96SArnd Bergmann {
87567207b96SArnd Bergmann 	struct spu_context *ctx;
87667207b96SArnd Bergmann 	u32 data;
87767207b96SArnd Bergmann 
87867207b96SArnd Bergmann 	ctx = file->private_data;
87967207b96SArnd Bergmann 
88067207b96SArnd Bergmann 	if (len < 4)
88167207b96SArnd Bergmann 		return -EINVAL;
88267207b96SArnd Bergmann 
88367207b96SArnd Bergmann 	if (copy_from_user(&data, buf, 4))
88467207b96SArnd Bergmann 		return -EFAULT;
88567207b96SArnd Bergmann 
8868b3d6663SArnd Bergmann 	spu_acquire(ctx);
8878b3d6663SArnd Bergmann 	ctx->ops->signal2_write(ctx, data);
8888b3d6663SArnd Bergmann 	spu_release(ctx);
88967207b96SArnd Bergmann 
89067207b96SArnd Bergmann 	return 4;
89167207b96SArnd Bergmann }
89267207b96SArnd Bergmann 
89327d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
89478bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal2_mmap_nopfn(struct vm_area_struct *vma,
89578bde53eSBenjamin Herrenschmidt 					      unsigned long address)
8966df10a82SMark Nutter {
89727d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000
89878bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x1c000, 0x1000);
89927d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000
90027d5bf2aSBenjamin Herrenschmidt 	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
90127d5bf2aSBenjamin Herrenschmidt 	 * signal 1 and 2 area
90227d5bf2aSBenjamin Herrenschmidt 	 */
90378bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
90427d5bf2aSBenjamin Herrenschmidt #else
90527d5bf2aSBenjamin Herrenschmidt #error unsupported page size
90627d5bf2aSBenjamin Herrenschmidt #endif
9076df10a82SMark Nutter }
9086df10a82SMark Nutter 
9096df10a82SMark Nutter static struct vm_operations_struct spufs_signal2_mmap_vmops = {
91078bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_signal2_mmap_nopfn,
9116df10a82SMark Nutter };
9126df10a82SMark Nutter 
9136df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
9146df10a82SMark Nutter {
9156df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
9166df10a82SMark Nutter 		return -EINVAL;
9176df10a82SMark Nutter 
91878bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
9196df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
92023cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
9216df10a82SMark Nutter 
9226df10a82SMark Nutter 	vma->vm_ops = &spufs_signal2_mmap_vmops;
9236df10a82SMark Nutter 	return 0;
9246df10a82SMark Nutter }
92527d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
92627d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL
92727d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
9286df10a82SMark Nutter 
9295dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = {
9306df10a82SMark Nutter 	.open = spufs_signal2_open,
93167207b96SArnd Bergmann 	.read = spufs_signal2_read,
93267207b96SArnd Bergmann 	.write = spufs_signal2_write,
9336df10a82SMark Nutter 	.mmap = spufs_signal2_mmap,
93467207b96SArnd Bergmann };
93567207b96SArnd Bergmann 
93667207b96SArnd Bergmann static void spufs_signal1_type_set(void *data, u64 val)
93767207b96SArnd Bergmann {
93867207b96SArnd Bergmann 	struct spu_context *ctx = data;
93967207b96SArnd Bergmann 
9408b3d6663SArnd Bergmann 	spu_acquire(ctx);
9418b3d6663SArnd Bergmann 	ctx->ops->signal1_type_set(ctx, val);
9428b3d6663SArnd Bergmann 	spu_release(ctx);
94367207b96SArnd Bergmann }
94467207b96SArnd Bergmann 
945bf1ab978SDwayne Grant McConnell static u64 __spufs_signal1_type_get(void *data)
946bf1ab978SDwayne Grant McConnell {
947bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
948bf1ab978SDwayne Grant McConnell 	return ctx->ops->signal1_type_get(ctx);
949bf1ab978SDwayne Grant McConnell }
950bf1ab978SDwayne Grant McConnell 
95167207b96SArnd Bergmann static u64 spufs_signal1_type_get(void *data)
95267207b96SArnd Bergmann {
95367207b96SArnd Bergmann 	struct spu_context *ctx = data;
9548b3d6663SArnd Bergmann 	u64 ret;
9558b3d6663SArnd Bergmann 
9568b3d6663SArnd Bergmann 	spu_acquire(ctx);
957bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal1_type_get(data);
9588b3d6663SArnd Bergmann 	spu_release(ctx);
9598b3d6663SArnd Bergmann 
9608b3d6663SArnd Bergmann 	return ret;
96167207b96SArnd Bergmann }
96267207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
96367207b96SArnd Bergmann 					spufs_signal1_type_set, "%llu");
96467207b96SArnd Bergmann 
96567207b96SArnd Bergmann static void spufs_signal2_type_set(void *data, u64 val)
96667207b96SArnd Bergmann {
96767207b96SArnd Bergmann 	struct spu_context *ctx = data;
96867207b96SArnd Bergmann 
9698b3d6663SArnd Bergmann 	spu_acquire(ctx);
9708b3d6663SArnd Bergmann 	ctx->ops->signal2_type_set(ctx, val);
9718b3d6663SArnd Bergmann 	spu_release(ctx);
97267207b96SArnd Bergmann }
97367207b96SArnd Bergmann 
974bf1ab978SDwayne Grant McConnell static u64 __spufs_signal2_type_get(void *data)
975bf1ab978SDwayne Grant McConnell {
976bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
977bf1ab978SDwayne Grant McConnell 	return ctx->ops->signal2_type_get(ctx);
978bf1ab978SDwayne Grant McConnell }
979bf1ab978SDwayne Grant McConnell 
98067207b96SArnd Bergmann static u64 spufs_signal2_type_get(void *data)
98167207b96SArnd Bergmann {
98267207b96SArnd Bergmann 	struct spu_context *ctx = data;
9838b3d6663SArnd Bergmann 	u64 ret;
9848b3d6663SArnd Bergmann 
9858b3d6663SArnd Bergmann 	spu_acquire(ctx);
986bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal2_type_get(data);
9878b3d6663SArnd Bergmann 	spu_release(ctx);
9888b3d6663SArnd Bergmann 
9898b3d6663SArnd Bergmann 	return ret;
99067207b96SArnd Bergmann }
99167207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
99267207b96SArnd Bergmann 					spufs_signal2_type_set, "%llu");
99367207b96SArnd Bergmann 
99427d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
99578bde53eSBenjamin Herrenschmidt static unsigned long spufs_mss_mmap_nopfn(struct vm_area_struct *vma,
99678bde53eSBenjamin Herrenschmidt 					  unsigned long address)
997d9379c4bSarnd@arndb.de {
99878bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x0000, 0x1000);
999d9379c4bSarnd@arndb.de }
1000d9379c4bSarnd@arndb.de 
1001d9379c4bSarnd@arndb.de static struct vm_operations_struct spufs_mss_mmap_vmops = {
100278bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mss_mmap_nopfn,
1003d9379c4bSarnd@arndb.de };
1004d9379c4bSarnd@arndb.de 
1005d9379c4bSarnd@arndb.de /*
1006d9379c4bSarnd@arndb.de  * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
1007d9379c4bSarnd@arndb.de  */
1008d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma)
1009d9379c4bSarnd@arndb.de {
1010d9379c4bSarnd@arndb.de 	if (!(vma->vm_flags & VM_SHARED))
1011d9379c4bSarnd@arndb.de 		return -EINVAL;
1012d9379c4bSarnd@arndb.de 
101378bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
1014d9379c4bSarnd@arndb.de 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
101523cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
1016d9379c4bSarnd@arndb.de 
1017d9379c4bSarnd@arndb.de 	vma->vm_ops = &spufs_mss_mmap_vmops;
1018d9379c4bSarnd@arndb.de 	return 0;
1019d9379c4bSarnd@arndb.de }
102027d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
102127d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL
102227d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
1023d9379c4bSarnd@arndb.de 
1024d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file)
1025d9379c4bSarnd@arndb.de {
1026d9379c4bSarnd@arndb.de 	struct spufs_inode_info *i = SPUFS_I(inode);
102717e0e270SBenjamin Herrenschmidt 	struct spu_context *ctx = i->i_ctx;
1028d9379c4bSarnd@arndb.de 
1029d9379c4bSarnd@arndb.de 	file->private_data = i->i_ctx;
103017e0e270SBenjamin Herrenschmidt 	ctx->mss = inode->i_mapping;
103117e0e270SBenjamin Herrenschmidt 	smp_wmb();
1032d9379c4bSarnd@arndb.de 	return nonseekable_open(inode, file);
1033d9379c4bSarnd@arndb.de }
1034d9379c4bSarnd@arndb.de 
10355dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = {
1036d9379c4bSarnd@arndb.de 	.open	 = spufs_mss_open,
1037d9379c4bSarnd@arndb.de 	.mmap	 = spufs_mss_mmap,
103827d5bf2aSBenjamin Herrenschmidt };
103927d5bf2aSBenjamin Herrenschmidt 
104078bde53eSBenjamin Herrenschmidt static unsigned long spufs_psmap_mmap_nopfn(struct vm_area_struct *vma,
104178bde53eSBenjamin Herrenschmidt 					    unsigned long address)
104227d5bf2aSBenjamin Herrenschmidt {
104378bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x0000, 0x20000);
104427d5bf2aSBenjamin Herrenschmidt }
104527d5bf2aSBenjamin Herrenschmidt 
104627d5bf2aSBenjamin Herrenschmidt static struct vm_operations_struct spufs_psmap_mmap_vmops = {
104778bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_psmap_mmap_nopfn,
104827d5bf2aSBenjamin Herrenschmidt };
104927d5bf2aSBenjamin Herrenschmidt 
105027d5bf2aSBenjamin Herrenschmidt /*
105127d5bf2aSBenjamin Herrenschmidt  * mmap support for full problem state area [0x00000 - 0x1ffff].
105227d5bf2aSBenjamin Herrenschmidt  */
105327d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
105427d5bf2aSBenjamin Herrenschmidt {
105527d5bf2aSBenjamin Herrenschmidt 	if (!(vma->vm_flags & VM_SHARED))
105627d5bf2aSBenjamin Herrenschmidt 		return -EINVAL;
105727d5bf2aSBenjamin Herrenschmidt 
105878bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
105927d5bf2aSBenjamin Herrenschmidt 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
106027d5bf2aSBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
106127d5bf2aSBenjamin Herrenschmidt 
106227d5bf2aSBenjamin Herrenschmidt 	vma->vm_ops = &spufs_psmap_mmap_vmops;
106327d5bf2aSBenjamin Herrenschmidt 	return 0;
106427d5bf2aSBenjamin Herrenschmidt }
106527d5bf2aSBenjamin Herrenschmidt 
106627d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file)
106727d5bf2aSBenjamin Herrenschmidt {
106827d5bf2aSBenjamin Herrenschmidt 	struct spufs_inode_info *i = SPUFS_I(inode);
106917e0e270SBenjamin Herrenschmidt 	struct spu_context *ctx = i->i_ctx;
107027d5bf2aSBenjamin Herrenschmidt 
107127d5bf2aSBenjamin Herrenschmidt 	file->private_data = i->i_ctx;
107217e0e270SBenjamin Herrenschmidt 	ctx->psmap = inode->i_mapping;
107317e0e270SBenjamin Herrenschmidt 	smp_wmb();
107427d5bf2aSBenjamin Herrenschmidt 	return nonseekable_open(inode, file);
107527d5bf2aSBenjamin Herrenschmidt }
107627d5bf2aSBenjamin Herrenschmidt 
10775dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = {
107827d5bf2aSBenjamin Herrenschmidt 	.open	 = spufs_psmap_open,
107927d5bf2aSBenjamin Herrenschmidt 	.mmap	 = spufs_psmap_mmap,
1080d9379c4bSarnd@arndb.de };
1081d9379c4bSarnd@arndb.de 
1082d9379c4bSarnd@arndb.de 
108327d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
108478bde53eSBenjamin Herrenschmidt static unsigned long spufs_mfc_mmap_nopfn(struct vm_area_struct *vma,
108578bde53eSBenjamin Herrenschmidt 					  unsigned long address)
10866df10a82SMark Nutter {
108778bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x3000, 0x1000);
10886df10a82SMark Nutter }
10896df10a82SMark Nutter 
10906df10a82SMark Nutter static struct vm_operations_struct spufs_mfc_mmap_vmops = {
109178bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mfc_mmap_nopfn,
10926df10a82SMark Nutter };
10936df10a82SMark Nutter 
10946df10a82SMark Nutter /*
10956df10a82SMark Nutter  * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
10966df10a82SMark Nutter  */
10976df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
10986df10a82SMark Nutter {
10996df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
11006df10a82SMark Nutter 		return -EINVAL;
11016df10a82SMark Nutter 
110278bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
11036df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
110423cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
11056df10a82SMark Nutter 
11066df10a82SMark Nutter 	vma->vm_ops = &spufs_mfc_mmap_vmops;
11076df10a82SMark Nutter 	return 0;
11086df10a82SMark Nutter }
110927d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
111027d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL
111127d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
1112a33a7d73SArnd Bergmann 
1113a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file)
1114a33a7d73SArnd Bergmann {
1115a33a7d73SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
1116a33a7d73SArnd Bergmann 	struct spu_context *ctx = i->i_ctx;
1117a33a7d73SArnd Bergmann 
1118a33a7d73SArnd Bergmann 	/* we don't want to deal with DMA into other processes */
1119a33a7d73SArnd Bergmann 	if (ctx->owner != current->mm)
1120a33a7d73SArnd Bergmann 		return -EINVAL;
1121a33a7d73SArnd Bergmann 
1122a33a7d73SArnd Bergmann 	if (atomic_read(&inode->i_count) != 1)
1123a33a7d73SArnd Bergmann 		return -EBUSY;
1124a33a7d73SArnd Bergmann 
1125a33a7d73SArnd Bergmann 	file->private_data = ctx;
112617e0e270SBenjamin Herrenschmidt 	ctx->mfc = inode->i_mapping;
112717e0e270SBenjamin Herrenschmidt 	smp_wmb();
1128a33a7d73SArnd Bergmann 	return nonseekable_open(inode, file);
1129a33a7d73SArnd Bergmann }
1130a33a7d73SArnd Bergmann 
1131a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */
1132a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu)
1133a33a7d73SArnd Bergmann {
1134a33a7d73SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
1135a33a7d73SArnd Bergmann 
1136a33a7d73SArnd Bergmann 	wake_up_all(&ctx->mfc_wq);
1137a33a7d73SArnd Bergmann 
1138a33a7d73SArnd Bergmann 	pr_debug("%s %s\n", __FUNCTION__, spu->name);
1139a33a7d73SArnd Bergmann 	if (ctx->mfc_fasync) {
1140a33a7d73SArnd Bergmann 		u32 free_elements, tagstatus;
1141a33a7d73SArnd Bergmann 		unsigned int mask;
1142a33a7d73SArnd Bergmann 
1143a33a7d73SArnd Bergmann 		/* no need for spu_acquire in interrupt context */
1144a33a7d73SArnd Bergmann 		free_elements = ctx->ops->get_mfc_free_elements(ctx);
1145a33a7d73SArnd Bergmann 		tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1146a33a7d73SArnd Bergmann 
1147a33a7d73SArnd Bergmann 		mask = 0;
1148a33a7d73SArnd Bergmann 		if (free_elements & 0xffff)
1149a33a7d73SArnd Bergmann 			mask |= POLLOUT;
1150a33a7d73SArnd Bergmann 		if (tagstatus & ctx->tagwait)
1151a33a7d73SArnd Bergmann 			mask |= POLLIN;
1152a33a7d73SArnd Bergmann 
1153a33a7d73SArnd Bergmann 		kill_fasync(&ctx->mfc_fasync, SIGIO, mask);
1154a33a7d73SArnd Bergmann 	}
1155a33a7d73SArnd Bergmann }
1156a33a7d73SArnd Bergmann 
1157a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status)
1158a33a7d73SArnd Bergmann {
1159a33a7d73SArnd Bergmann 	/* See if there is one tag group is complete */
1160a33a7d73SArnd Bergmann 	/* FIXME we need locking around tagwait */
1161a33a7d73SArnd Bergmann 	*status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait;
1162a33a7d73SArnd Bergmann 	ctx->tagwait &= ~*status;
1163a33a7d73SArnd Bergmann 	if (*status)
1164a33a7d73SArnd Bergmann 		return 1;
1165a33a7d73SArnd Bergmann 
1166a33a7d73SArnd Bergmann 	/* enable interrupt waiting for any tag group,
1167a33a7d73SArnd Bergmann 	   may silently fail if interrupts are already enabled */
1168a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1169a33a7d73SArnd Bergmann 	return 0;
1170a33a7d73SArnd Bergmann }
1171a33a7d73SArnd Bergmann 
1172a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer,
1173a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1174a33a7d73SArnd Bergmann {
1175a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1176a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1177a33a7d73SArnd Bergmann 	u32 status;
1178a33a7d73SArnd Bergmann 
1179a33a7d73SArnd Bergmann 	if (size != 4)
1180a33a7d73SArnd Bergmann 		goto out;
1181a33a7d73SArnd Bergmann 
1182a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1183a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1184a33a7d73SArnd Bergmann 		status = ctx->ops->read_mfc_tagstatus(ctx);
1185a33a7d73SArnd Bergmann 		if (!(status & ctx->tagwait))
1186a33a7d73SArnd Bergmann 			ret = -EAGAIN;
1187a33a7d73SArnd Bergmann 		else
1188a33a7d73SArnd Bergmann 			ctx->tagwait &= ~status;
1189a33a7d73SArnd Bergmann 	} else {
1190a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1191a33a7d73SArnd Bergmann 			   spufs_read_mfc_tagstatus(ctx, &status));
1192a33a7d73SArnd Bergmann 	}
1193a33a7d73SArnd Bergmann 	spu_release(ctx);
1194a33a7d73SArnd Bergmann 
1195a33a7d73SArnd Bergmann 	if (ret)
1196a33a7d73SArnd Bergmann 		goto out;
1197a33a7d73SArnd Bergmann 
1198a33a7d73SArnd Bergmann 	ret = 4;
1199a33a7d73SArnd Bergmann 	if (copy_to_user(buffer, &status, 4))
1200a33a7d73SArnd Bergmann 		ret = -EFAULT;
1201a33a7d73SArnd Bergmann 
1202a33a7d73SArnd Bergmann out:
1203a33a7d73SArnd Bergmann 	return ret;
1204a33a7d73SArnd Bergmann }
1205a33a7d73SArnd Bergmann 
1206a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd)
1207a33a7d73SArnd Bergmann {
1208a33a7d73SArnd Bergmann 	pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa,
1209a33a7d73SArnd Bergmann 		 cmd->ea, cmd->size, cmd->tag, cmd->cmd);
1210a33a7d73SArnd Bergmann 
1211a33a7d73SArnd Bergmann 	switch (cmd->cmd) {
1212a33a7d73SArnd Bergmann 	case MFC_PUT_CMD:
1213a33a7d73SArnd Bergmann 	case MFC_PUTF_CMD:
1214a33a7d73SArnd Bergmann 	case MFC_PUTB_CMD:
1215a33a7d73SArnd Bergmann 	case MFC_GET_CMD:
1216a33a7d73SArnd Bergmann 	case MFC_GETF_CMD:
1217a33a7d73SArnd Bergmann 	case MFC_GETB_CMD:
1218a33a7d73SArnd Bergmann 		break;
1219a33a7d73SArnd Bergmann 	default:
1220a33a7d73SArnd Bergmann 		pr_debug("invalid DMA opcode %x\n", cmd->cmd);
1221a33a7d73SArnd Bergmann 		return -EIO;
1222a33a7d73SArnd Bergmann 	}
1223a33a7d73SArnd Bergmann 
1224a33a7d73SArnd Bergmann 	if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) {
1225a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment, ea %lx lsa %x\n",
1226a33a7d73SArnd Bergmann 				cmd->ea, cmd->lsa);
1227a33a7d73SArnd Bergmann 		return -EIO;
1228a33a7d73SArnd Bergmann 	}
1229a33a7d73SArnd Bergmann 
1230a33a7d73SArnd Bergmann 	switch (cmd->size & 0xf) {
1231a33a7d73SArnd Bergmann 	case 1:
1232a33a7d73SArnd Bergmann 		break;
1233a33a7d73SArnd Bergmann 	case 2:
1234a33a7d73SArnd Bergmann 		if (cmd->lsa & 1)
1235a33a7d73SArnd Bergmann 			goto error;
1236a33a7d73SArnd Bergmann 		break;
1237a33a7d73SArnd Bergmann 	case 4:
1238a33a7d73SArnd Bergmann 		if (cmd->lsa & 3)
1239a33a7d73SArnd Bergmann 			goto error;
1240a33a7d73SArnd Bergmann 		break;
1241a33a7d73SArnd Bergmann 	case 8:
1242a33a7d73SArnd Bergmann 		if (cmd->lsa & 7)
1243a33a7d73SArnd Bergmann 			goto error;
1244a33a7d73SArnd Bergmann 		break;
1245a33a7d73SArnd Bergmann 	case 0:
1246a33a7d73SArnd Bergmann 		if (cmd->lsa & 15)
1247a33a7d73SArnd Bergmann 			goto error;
1248a33a7d73SArnd Bergmann 		break;
1249a33a7d73SArnd Bergmann 	error:
1250a33a7d73SArnd Bergmann 	default:
1251a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment %x for size %x\n",
1252a33a7d73SArnd Bergmann 			cmd->lsa & 0xf, cmd->size);
1253a33a7d73SArnd Bergmann 		return -EIO;
1254a33a7d73SArnd Bergmann 	}
1255a33a7d73SArnd Bergmann 
1256a33a7d73SArnd Bergmann 	if (cmd->size > 16 * 1024) {
1257a33a7d73SArnd Bergmann 		pr_debug("invalid DMA size %x\n", cmd->size);
1258a33a7d73SArnd Bergmann 		return -EIO;
1259a33a7d73SArnd Bergmann 	}
1260a33a7d73SArnd Bergmann 
1261a33a7d73SArnd Bergmann 	if (cmd->tag & 0xfff0) {
1262a33a7d73SArnd Bergmann 		/* we reserve the higher tag numbers for kernel use */
1263a33a7d73SArnd Bergmann 		pr_debug("invalid DMA tag\n");
1264a33a7d73SArnd Bergmann 		return -EIO;
1265a33a7d73SArnd Bergmann 	}
1266a33a7d73SArnd Bergmann 
1267a33a7d73SArnd Bergmann 	if (cmd->class) {
1268a33a7d73SArnd Bergmann 		/* not supported in this version */
1269a33a7d73SArnd Bergmann 		pr_debug("invalid DMA class\n");
1270a33a7d73SArnd Bergmann 		return -EIO;
1271a33a7d73SArnd Bergmann 	}
1272a33a7d73SArnd Bergmann 
1273a33a7d73SArnd Bergmann 	return 0;
1274a33a7d73SArnd Bergmann }
1275a33a7d73SArnd Bergmann 
1276a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx,
1277a33a7d73SArnd Bergmann 				struct mfc_dma_command cmd,
1278a33a7d73SArnd Bergmann 				int *error)
1279a33a7d73SArnd Bergmann {
1280a33a7d73SArnd Bergmann 	*error = ctx->ops->send_mfc_command(ctx, &cmd);
1281a33a7d73SArnd Bergmann 	if (*error == -EAGAIN) {
1282a33a7d73SArnd Bergmann 		/* wait for any tag group to complete
1283a33a7d73SArnd Bergmann 		   so we have space for the new command */
1284a33a7d73SArnd Bergmann 		ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1285a33a7d73SArnd Bergmann 		/* try again, because the queue might be
1286a33a7d73SArnd Bergmann 		   empty again */
1287a33a7d73SArnd Bergmann 		*error = ctx->ops->send_mfc_command(ctx, &cmd);
1288a33a7d73SArnd Bergmann 		if (*error == -EAGAIN)
1289a33a7d73SArnd Bergmann 			return 0;
1290a33a7d73SArnd Bergmann 	}
1291a33a7d73SArnd Bergmann 	return 1;
1292a33a7d73SArnd Bergmann }
1293a33a7d73SArnd Bergmann 
1294a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer,
1295a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1296a33a7d73SArnd Bergmann {
1297a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1298a33a7d73SArnd Bergmann 	struct mfc_dma_command cmd;
1299a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1300a33a7d73SArnd Bergmann 
1301a33a7d73SArnd Bergmann 	if (size != sizeof cmd)
1302a33a7d73SArnd Bergmann 		goto out;
1303a33a7d73SArnd Bergmann 
1304a33a7d73SArnd Bergmann 	ret = -EFAULT;
1305a33a7d73SArnd Bergmann 	if (copy_from_user(&cmd, buffer, sizeof cmd))
1306a33a7d73SArnd Bergmann 		goto out;
1307a33a7d73SArnd Bergmann 
1308a33a7d73SArnd Bergmann 	ret = spufs_check_valid_dma(&cmd);
1309a33a7d73SArnd Bergmann 	if (ret)
1310a33a7d73SArnd Bergmann 		goto out;
1311a33a7d73SArnd Bergmann 
131226bec673SChristoph Hellwig 	spu_acquire_runnable(ctx, 0);
1313a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1314a33a7d73SArnd Bergmann 		ret = ctx->ops->send_mfc_command(ctx, &cmd);
1315a33a7d73SArnd Bergmann 	} else {
1316a33a7d73SArnd Bergmann 		int status;
1317a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1318a33a7d73SArnd Bergmann 				 spu_send_mfc_command(ctx, cmd, &status));
1319a33a7d73SArnd Bergmann 		if (status)
1320a33a7d73SArnd Bergmann 			ret = status;
1321a33a7d73SArnd Bergmann 	}
1322a33a7d73SArnd Bergmann 	spu_release(ctx);
1323a33a7d73SArnd Bergmann 
1324a33a7d73SArnd Bergmann 	if (ret)
1325a33a7d73SArnd Bergmann 		goto out;
1326a33a7d73SArnd Bergmann 
1327a33a7d73SArnd Bergmann 	ctx->tagwait |= 1 << cmd.tag;
13283692dc66SMasato Noguchi 	ret = size;
1329a33a7d73SArnd Bergmann 
1330a33a7d73SArnd Bergmann out:
1331a33a7d73SArnd Bergmann 	return ret;
1332a33a7d73SArnd Bergmann }
1333a33a7d73SArnd Bergmann 
1334a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait)
1335a33a7d73SArnd Bergmann {
1336a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1337a33a7d73SArnd Bergmann 	u32 free_elements, tagstatus;
1338a33a7d73SArnd Bergmann 	unsigned int mask;
1339a33a7d73SArnd Bergmann 
1340a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1341a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2);
1342a33a7d73SArnd Bergmann 	free_elements = ctx->ops->get_mfc_free_elements(ctx);
1343a33a7d73SArnd Bergmann 	tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1344a33a7d73SArnd Bergmann 	spu_release(ctx);
1345a33a7d73SArnd Bergmann 
1346a33a7d73SArnd Bergmann 	poll_wait(file, &ctx->mfc_wq, wait);
1347a33a7d73SArnd Bergmann 
1348a33a7d73SArnd Bergmann 	mask = 0;
1349a33a7d73SArnd Bergmann 	if (free_elements & 0xffff)
1350a33a7d73SArnd Bergmann 		mask |= POLLOUT | POLLWRNORM;
1351a33a7d73SArnd Bergmann 	if (tagstatus & ctx->tagwait)
1352a33a7d73SArnd Bergmann 		mask |= POLLIN | POLLRDNORM;
1353a33a7d73SArnd Bergmann 
1354a33a7d73SArnd Bergmann 	pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__,
1355a33a7d73SArnd Bergmann 		free_elements, tagstatus, ctx->tagwait);
1356a33a7d73SArnd Bergmann 
1357a33a7d73SArnd Bergmann 	return mask;
1358a33a7d73SArnd Bergmann }
1359a33a7d73SArnd Bergmann 
136073b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id)
1361a33a7d73SArnd Bergmann {
1362a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1363a33a7d73SArnd Bergmann 	int ret;
1364a33a7d73SArnd Bergmann 
1365a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1366a33a7d73SArnd Bergmann #if 0
1367a33a7d73SArnd Bergmann /* this currently hangs */
1368a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1369a33a7d73SArnd Bergmann 			 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2));
1370a33a7d73SArnd Bergmann 	if (ret)
1371a33a7d73SArnd Bergmann 		goto out;
1372a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1373a33a7d73SArnd Bergmann 			 ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait);
1374a33a7d73SArnd Bergmann out:
1375a33a7d73SArnd Bergmann #else
1376a33a7d73SArnd Bergmann 	ret = 0;
1377a33a7d73SArnd Bergmann #endif
1378a33a7d73SArnd Bergmann 	spu_release(ctx);
1379a33a7d73SArnd Bergmann 
1380a33a7d73SArnd Bergmann 	return ret;
1381a33a7d73SArnd Bergmann }
1382a33a7d73SArnd Bergmann 
1383a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry,
1384a33a7d73SArnd Bergmann 			   int datasync)
1385a33a7d73SArnd Bergmann {
138673b6af8aSAl Viro 	return spufs_mfc_flush(file, NULL);
1387a33a7d73SArnd Bergmann }
1388a33a7d73SArnd Bergmann 
1389a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on)
1390a33a7d73SArnd Bergmann {
1391a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1392a33a7d73SArnd Bergmann 
1393a33a7d73SArnd Bergmann 	return fasync_helper(fd, file, on, &ctx->mfc_fasync);
1394a33a7d73SArnd Bergmann }
1395a33a7d73SArnd Bergmann 
13965dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = {
1397a33a7d73SArnd Bergmann 	.open	 = spufs_mfc_open,
1398a33a7d73SArnd Bergmann 	.read	 = spufs_mfc_read,
1399a33a7d73SArnd Bergmann 	.write	 = spufs_mfc_write,
1400a33a7d73SArnd Bergmann 	.poll	 = spufs_mfc_poll,
1401a33a7d73SArnd Bergmann 	.flush	 = spufs_mfc_flush,
1402a33a7d73SArnd Bergmann 	.fsync	 = spufs_mfc_fsync,
1403a33a7d73SArnd Bergmann 	.fasync	 = spufs_mfc_fasync,
14046df10a82SMark Nutter 	.mmap	 = spufs_mfc_mmap,
1405a33a7d73SArnd Bergmann };
1406a33a7d73SArnd Bergmann 
140767207b96SArnd Bergmann static void spufs_npc_set(void *data, u64 val)
140867207b96SArnd Bergmann {
140967207b96SArnd Bergmann 	struct spu_context *ctx = data;
14108b3d6663SArnd Bergmann 	spu_acquire(ctx);
14118b3d6663SArnd Bergmann 	ctx->ops->npc_write(ctx, val);
14128b3d6663SArnd Bergmann 	spu_release(ctx);
141367207b96SArnd Bergmann }
141467207b96SArnd Bergmann 
141567207b96SArnd Bergmann static u64 spufs_npc_get(void *data)
141667207b96SArnd Bergmann {
141767207b96SArnd Bergmann 	struct spu_context *ctx = data;
141867207b96SArnd Bergmann 	u64 ret;
14198b3d6663SArnd Bergmann 	spu_acquire(ctx);
14208b3d6663SArnd Bergmann 	ret = ctx->ops->npc_read(ctx);
14218b3d6663SArnd Bergmann 	spu_release(ctx);
142267207b96SArnd Bergmann 	return ret;
142367207b96SArnd Bergmann }
14249b5047e2SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set,
14259b5047e2SDwayne Grant McConnell 			"0x%llx\n")
142667207b96SArnd Bergmann 
14278b3d6663SArnd Bergmann static void spufs_decr_set(void *data, u64 val)
14288b3d6663SArnd Bergmann {
14298b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14308b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14318b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14328b3d6663SArnd Bergmann 	lscsa->decr.slot[0] = (u32) val;
14338b3d6663SArnd Bergmann 	spu_release(ctx);
14348b3d6663SArnd Bergmann }
14358b3d6663SArnd Bergmann 
1436bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_get(void *data)
14378b3d6663SArnd Bergmann {
14388b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14398b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1440bf1ab978SDwayne Grant McConnell 	return lscsa->decr.slot[0];
1441bf1ab978SDwayne Grant McConnell }
1442bf1ab978SDwayne Grant McConnell 
1443bf1ab978SDwayne Grant McConnell static u64 spufs_decr_get(void *data)
1444bf1ab978SDwayne Grant McConnell {
1445bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14468b3d6663SArnd Bergmann 	u64 ret;
14478b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1448bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_get(data);
14498b3d6663SArnd Bergmann 	spu_release(ctx);
14508b3d6663SArnd Bergmann 	return ret;
14518b3d6663SArnd Bergmann }
14528b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set,
14539b5047e2SDwayne Grant McConnell 			"0x%llx\n")
14548b3d6663SArnd Bergmann 
14558b3d6663SArnd Bergmann static void spufs_decr_status_set(void *data, u64 val)
14568b3d6663SArnd Bergmann {
14578b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14588b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14598b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14608b3d6663SArnd Bergmann 	lscsa->decr_status.slot[0] = (u32) val;
14618b3d6663SArnd Bergmann 	spu_release(ctx);
14628b3d6663SArnd Bergmann }
14638b3d6663SArnd Bergmann 
1464bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_status_get(void *data)
14658b3d6663SArnd Bergmann {
14668b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14678b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1468bf1ab978SDwayne Grant McConnell 	return lscsa->decr_status.slot[0];
1469bf1ab978SDwayne Grant McConnell }
1470bf1ab978SDwayne Grant McConnell 
1471bf1ab978SDwayne Grant McConnell static u64 spufs_decr_status_get(void *data)
1472bf1ab978SDwayne Grant McConnell {
1473bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14748b3d6663SArnd Bergmann 	u64 ret;
14758b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1476bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_status_get(data);
14778b3d6663SArnd Bergmann 	spu_release(ctx);
14788b3d6663SArnd Bergmann 	return ret;
14798b3d6663SArnd Bergmann }
14808b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get,
14819b5047e2SDwayne Grant McConnell 			spufs_decr_status_set, "0x%llx\n")
14828b3d6663SArnd Bergmann 
14838b3d6663SArnd Bergmann static void spufs_event_mask_set(void *data, u64 val)
14848b3d6663SArnd Bergmann {
14858b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14868b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14878b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14888b3d6663SArnd Bergmann 	lscsa->event_mask.slot[0] = (u32) val;
14898b3d6663SArnd Bergmann 	spu_release(ctx);
14908b3d6663SArnd Bergmann }
14918b3d6663SArnd Bergmann 
1492bf1ab978SDwayne Grant McConnell static u64 __spufs_event_mask_get(void *data)
14938b3d6663SArnd Bergmann {
14948b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14958b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1496bf1ab978SDwayne Grant McConnell 	return lscsa->event_mask.slot[0];
1497bf1ab978SDwayne Grant McConnell }
1498bf1ab978SDwayne Grant McConnell 
1499bf1ab978SDwayne Grant McConnell static u64 spufs_event_mask_get(void *data)
1500bf1ab978SDwayne Grant McConnell {
1501bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
15028b3d6663SArnd Bergmann 	u64 ret;
15038b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1504bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_mask_get(data);
15058b3d6663SArnd Bergmann 	spu_release(ctx);
15068b3d6663SArnd Bergmann 	return ret;
15078b3d6663SArnd Bergmann }
15088b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get,
15099b5047e2SDwayne Grant McConnell 			spufs_event_mask_set, "0x%llx\n")
15108b3d6663SArnd Bergmann 
1511bf1ab978SDwayne Grant McConnell static u64 __spufs_event_status_get(void *data)
1512b9e3bd77SDwayne Grant McConnell {
1513b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1514b9e3bd77SDwayne Grant McConnell 	struct spu_state *state = &ctx->csa;
1515b9e3bd77SDwayne Grant McConnell 	u64 stat;
1516b9e3bd77SDwayne Grant McConnell 	stat = state->spu_chnlcnt_RW[0];
1517b9e3bd77SDwayne Grant McConnell 	if (stat)
1518bf1ab978SDwayne Grant McConnell 		return state->spu_chnldata_RW[0];
1519bf1ab978SDwayne Grant McConnell 	return 0;
1520bf1ab978SDwayne Grant McConnell }
1521bf1ab978SDwayne Grant McConnell 
1522bf1ab978SDwayne Grant McConnell static u64 spufs_event_status_get(void *data)
1523bf1ab978SDwayne Grant McConnell {
1524bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1525bf1ab978SDwayne Grant McConnell 	u64 ret = 0;
1526bf1ab978SDwayne Grant McConnell 
1527bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1528bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_status_get(data);
1529b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1530b9e3bd77SDwayne Grant McConnell 	return ret;
1531b9e3bd77SDwayne Grant McConnell }
1532b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get,
1533b9e3bd77SDwayne Grant McConnell 			NULL, "0x%llx\n")
1534b9e3bd77SDwayne Grant McConnell 
15358b3d6663SArnd Bergmann static void spufs_srr0_set(void *data, u64 val)
15368b3d6663SArnd Bergmann {
15378b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15388b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15398b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15408b3d6663SArnd Bergmann 	lscsa->srr0.slot[0] = (u32) val;
15418b3d6663SArnd Bergmann 	spu_release(ctx);
15428b3d6663SArnd Bergmann }
15438b3d6663SArnd Bergmann 
15448b3d6663SArnd Bergmann static u64 spufs_srr0_get(void *data)
15458b3d6663SArnd Bergmann {
15468b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15478b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15488b3d6663SArnd Bergmann 	u64 ret;
15498b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15508b3d6663SArnd Bergmann 	ret = lscsa->srr0.slot[0];
15518b3d6663SArnd Bergmann 	spu_release(ctx);
15528b3d6663SArnd Bergmann 	return ret;
15538b3d6663SArnd Bergmann }
15548b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
15559b5047e2SDwayne Grant McConnell 			"0x%llx\n")
15568b3d6663SArnd Bergmann 
15577b1a7014Sarnd@arndb.de static u64 spufs_id_get(void *data)
15587b1a7014Sarnd@arndb.de {
15597b1a7014Sarnd@arndb.de 	struct spu_context *ctx = data;
15607b1a7014Sarnd@arndb.de 	u64 num;
15617b1a7014Sarnd@arndb.de 
15627b1a7014Sarnd@arndb.de 	spu_acquire(ctx);
15637b1a7014Sarnd@arndb.de 	if (ctx->state == SPU_STATE_RUNNABLE)
15647b1a7014Sarnd@arndb.de 		num = ctx->spu->number;
15657b1a7014Sarnd@arndb.de 	else
15667b1a7014Sarnd@arndb.de 		num = (unsigned int)-1;
15677b1a7014Sarnd@arndb.de 	spu_release(ctx);
15687b1a7014Sarnd@arndb.de 
15697b1a7014Sarnd@arndb.de 	return num;
15707b1a7014Sarnd@arndb.de }
1571e45d6634SAl Viro DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n")
15727b1a7014Sarnd@arndb.de 
1573bf1ab978SDwayne Grant McConnell static u64 __spufs_object_id_get(void *data)
157486767277SArnd Bergmann {
157586767277SArnd Bergmann 	struct spu_context *ctx = data;
157686767277SArnd Bergmann 	return ctx->object_id;
157786767277SArnd Bergmann }
157886767277SArnd Bergmann 
1579bf1ab978SDwayne Grant McConnell static u64 spufs_object_id_get(void *data)
1580bf1ab978SDwayne Grant McConnell {
1581bf1ab978SDwayne Grant McConnell 	/* FIXME: Should there really be no locking here? */
1582bf1ab978SDwayne Grant McConnell 	return __spufs_object_id_get(data);
1583bf1ab978SDwayne Grant McConnell }
1584bf1ab978SDwayne Grant McConnell 
158586767277SArnd Bergmann static void spufs_object_id_set(void *data, u64 id)
158686767277SArnd Bergmann {
158786767277SArnd Bergmann 	struct spu_context *ctx = data;
158886767277SArnd Bergmann 	ctx->object_id = id;
158986767277SArnd Bergmann }
159086767277SArnd Bergmann 
159186767277SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get,
159286767277SArnd Bergmann 		spufs_object_id_set, "0x%llx\n");
159386767277SArnd Bergmann 
1594bf1ab978SDwayne Grant McConnell static u64 __spufs_lslr_get(void *data)
1595bf1ab978SDwayne Grant McConnell {
1596bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1597bf1ab978SDwayne Grant McConnell 	return ctx->csa.priv2.spu_lslr_RW;
1598bf1ab978SDwayne Grant McConnell }
1599bf1ab978SDwayne Grant McConnell 
1600b9e3bd77SDwayne Grant McConnell static u64 spufs_lslr_get(void *data)
1601b9e3bd77SDwayne Grant McConnell {
1602b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1603b9e3bd77SDwayne Grant McConnell 	u64 ret;
1604b9e3bd77SDwayne Grant McConnell 
1605b9e3bd77SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1606bf1ab978SDwayne Grant McConnell 	ret = __spufs_lslr_get(data);
1607b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1608b9e3bd77SDwayne Grant McConnell 
1609b9e3bd77SDwayne Grant McConnell 	return ret;
1610b9e3bd77SDwayne Grant McConnell }
1611b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n")
1612b9e3bd77SDwayne Grant McConnell 
1613b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file)
1614b9e3bd77SDwayne Grant McConnell {
1615b9e3bd77SDwayne Grant McConnell 	struct spufs_inode_info *i = SPUFS_I(inode);
1616b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = i->i_ctx;
1617b9e3bd77SDwayne Grant McConnell 	file->private_data = ctx;
1618b9e3bd77SDwayne Grant McConnell 	return 0;
1619b9e3bd77SDwayne Grant McConnell }
1620b9e3bd77SDwayne Grant McConnell 
1621bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx,
1622bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1623bf1ab978SDwayne Grant McConnell {
1624bf1ab978SDwayne Grant McConnell 	u32 mbox_stat;
1625bf1ab978SDwayne Grant McConnell 	u32 data;
1626bf1ab978SDwayne Grant McConnell 
1627bf1ab978SDwayne Grant McConnell 	mbox_stat = ctx->csa.prob.mb_stat_R;
1628bf1ab978SDwayne Grant McConnell 	if (mbox_stat & 0x0000ff) {
1629bf1ab978SDwayne Grant McConnell 		data = ctx->csa.prob.pu_mb_R;
1630bf1ab978SDwayne Grant McConnell 	}
1631bf1ab978SDwayne Grant McConnell 
1632bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1633bf1ab978SDwayne Grant McConnell }
1634bf1ab978SDwayne Grant McConnell 
163569a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
163669a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
163769a2f00cSDwayne Grant McConnell {
1638bf1ab978SDwayne Grant McConnell 	int ret;
163969a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
164069a2f00cSDwayne Grant McConnell 
164169a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
164269a2f00cSDwayne Grant McConnell 		return -EFAULT;
164369a2f00cSDwayne Grant McConnell 
164469a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
164569a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1646bf1ab978SDwayne Grant McConnell 	ret = __spufs_mbox_info_read(ctx, buf, len, pos);
164769a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
164869a2f00cSDwayne Grant McConnell 	spu_release(ctx);
164969a2f00cSDwayne Grant McConnell 
1650bf1ab978SDwayne Grant McConnell 	return ret;
165169a2f00cSDwayne Grant McConnell }
165269a2f00cSDwayne Grant McConnell 
16535dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = {
165469a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
165569a2f00cSDwayne Grant McConnell 	.read = spufs_mbox_info_read,
165669a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
165769a2f00cSDwayne Grant McConnell };
165869a2f00cSDwayne Grant McConnell 
1659bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx,
1660bf1ab978SDwayne Grant McConnell 				char __user *buf, size_t len, loff_t *pos)
1661bf1ab978SDwayne Grant McConnell {
1662bf1ab978SDwayne Grant McConnell 	u32 ibox_stat;
1663bf1ab978SDwayne Grant McConnell 	u32 data;
1664bf1ab978SDwayne Grant McConnell 
1665bf1ab978SDwayne Grant McConnell 	ibox_stat = ctx->csa.prob.mb_stat_R;
1666bf1ab978SDwayne Grant McConnell 	if (ibox_stat & 0xff0000) {
1667bf1ab978SDwayne Grant McConnell 		data = ctx->csa.priv2.puint_mb_R;
1668bf1ab978SDwayne Grant McConnell 	}
1669bf1ab978SDwayne Grant McConnell 
1670bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1671bf1ab978SDwayne Grant McConnell }
1672bf1ab978SDwayne Grant McConnell 
167369a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
167469a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
167569a2f00cSDwayne Grant McConnell {
167669a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1677bf1ab978SDwayne Grant McConnell 	int ret;
167869a2f00cSDwayne Grant McConnell 
167969a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
168069a2f00cSDwayne Grant McConnell 		return -EFAULT;
168169a2f00cSDwayne Grant McConnell 
168269a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
168369a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1684bf1ab978SDwayne Grant McConnell 	ret = __spufs_ibox_info_read(ctx, buf, len, pos);
168569a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
168669a2f00cSDwayne Grant McConnell 	spu_release(ctx);
168769a2f00cSDwayne Grant McConnell 
1688bf1ab978SDwayne Grant McConnell 	return ret;
168969a2f00cSDwayne Grant McConnell }
169069a2f00cSDwayne Grant McConnell 
16915dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = {
169269a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
169369a2f00cSDwayne Grant McConnell 	.read = spufs_ibox_info_read,
169469a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
169569a2f00cSDwayne Grant McConnell };
169669a2f00cSDwayne Grant McConnell 
1697bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx,
1698bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1699bf1ab978SDwayne Grant McConnell {
1700bf1ab978SDwayne Grant McConnell 	int i, cnt;
1701bf1ab978SDwayne Grant McConnell 	u32 data[4];
1702bf1ab978SDwayne Grant McConnell 	u32 wbox_stat;
1703bf1ab978SDwayne Grant McConnell 
1704bf1ab978SDwayne Grant McConnell 	wbox_stat = ctx->csa.prob.mb_stat_R;
1705bf1ab978SDwayne Grant McConnell 	cnt = 4 - ((wbox_stat & 0x00ff00) >> 8);
1706bf1ab978SDwayne Grant McConnell 	for (i = 0; i < cnt; i++) {
1707bf1ab978SDwayne Grant McConnell 		data[i] = ctx->csa.spu_mailbox_data[i];
1708bf1ab978SDwayne Grant McConnell 	}
1709bf1ab978SDwayne Grant McConnell 
1710bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data,
1711bf1ab978SDwayne Grant McConnell 				cnt * sizeof(u32));
1712bf1ab978SDwayne Grant McConnell }
1713bf1ab978SDwayne Grant McConnell 
171469a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
171569a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
171669a2f00cSDwayne Grant McConnell {
171769a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1718bf1ab978SDwayne Grant McConnell 	int ret;
171969a2f00cSDwayne Grant McConnell 
172069a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
172169a2f00cSDwayne Grant McConnell 		return -EFAULT;
172269a2f00cSDwayne Grant McConnell 
172369a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
172469a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1725bf1ab978SDwayne Grant McConnell 	ret = __spufs_wbox_info_read(ctx, buf, len, pos);
172669a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
172769a2f00cSDwayne Grant McConnell 	spu_release(ctx);
172869a2f00cSDwayne Grant McConnell 
1729bf1ab978SDwayne Grant McConnell 	return ret;
173069a2f00cSDwayne Grant McConnell }
173169a2f00cSDwayne Grant McConnell 
17325dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = {
173369a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
173469a2f00cSDwayne Grant McConnell 	.read = spufs_wbox_info_read,
173569a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
173669a2f00cSDwayne Grant McConnell };
173769a2f00cSDwayne Grant McConnell 
1738bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx,
1739bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1740b9e3bd77SDwayne Grant McConnell {
1741b9e3bd77SDwayne Grant McConnell 	struct spu_dma_info info;
1742b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *spuqp;
1743b9e3bd77SDwayne Grant McConnell 	int i;
1744b9e3bd77SDwayne Grant McConnell 
1745b9e3bd77SDwayne Grant McConnell 	info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
1746b9e3bd77SDwayne Grant McConnell 	info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
1747b9e3bd77SDwayne Grant McConnell 	info.dma_info_status = ctx->csa.spu_chnldata_RW[24];
1748b9e3bd77SDwayne Grant McConnell 	info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
1749b9e3bd77SDwayne Grant McConnell 	info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
1750b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 16; i++) {
1751b9e3bd77SDwayne Grant McConnell 		qp = &info.dma_info_command_data[i];
1752b9e3bd77SDwayne Grant McConnell 		spuqp = &ctx->csa.priv2.spuq[i];
1753b9e3bd77SDwayne Grant McConnell 
1754b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW;
1755b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW;
1756b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW;
1757b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW;
1758b9e3bd77SDwayne Grant McConnell 	}
1759b9e3bd77SDwayne Grant McConnell 
1760b9e3bd77SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1761b9e3bd77SDwayne Grant McConnell 				sizeof info);
1762b9e3bd77SDwayne Grant McConnell }
1763b9e3bd77SDwayne Grant McConnell 
1764bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
1765bf1ab978SDwayne Grant McConnell 			      size_t len, loff_t *pos)
1766bf1ab978SDwayne Grant McConnell {
1767bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1768bf1ab978SDwayne Grant McConnell 	int ret;
1769bf1ab978SDwayne Grant McConnell 
1770bf1ab978SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1771bf1ab978SDwayne Grant McConnell 		return -EFAULT;
1772bf1ab978SDwayne Grant McConnell 
1773bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1774bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1775bf1ab978SDwayne Grant McConnell 	ret = __spufs_dma_info_read(ctx, buf, len, pos);
1776bf1ab978SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1777bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
1778bf1ab978SDwayne Grant McConnell 
1779bf1ab978SDwayne Grant McConnell 	return ret;
1780bf1ab978SDwayne Grant McConnell }
1781bf1ab978SDwayne Grant McConnell 
17825dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = {
1783b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1784b9e3bd77SDwayne Grant McConnell 	.read = spufs_dma_info_read,
1785b9e3bd77SDwayne Grant McConnell };
1786b9e3bd77SDwayne Grant McConnell 
1787bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
1788bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1789b9e3bd77SDwayne Grant McConnell {
1790b9e3bd77SDwayne Grant McConnell 	struct spu_proxydma_info info;
1791b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *puqp;
1792bf1ab978SDwayne Grant McConnell 	int ret = sizeof info;
1793b9e3bd77SDwayne Grant McConnell 	int i;
1794b9e3bd77SDwayne Grant McConnell 
1795b9e3bd77SDwayne Grant McConnell 	if (len < ret)
1796b9e3bd77SDwayne Grant McConnell 		return -EINVAL;
1797b9e3bd77SDwayne Grant McConnell 
1798b9e3bd77SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1799b9e3bd77SDwayne Grant McConnell 		return -EFAULT;
1800b9e3bd77SDwayne Grant McConnell 
1801b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
1802b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
1803b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
1804b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 8; i++) {
1805b9e3bd77SDwayne Grant McConnell 		qp = &info.proxydma_info_command_data[i];
1806b9e3bd77SDwayne Grant McConnell 		puqp = &ctx->csa.priv2.puq[i];
1807b9e3bd77SDwayne Grant McConnell 
1808b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
1809b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
1810b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
1811b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
1812b9e3bd77SDwayne Grant McConnell 	}
1813bf1ab978SDwayne Grant McConnell 
1814bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1815bf1ab978SDwayne Grant McConnell 				sizeof info);
1816bf1ab978SDwayne Grant McConnell }
1817bf1ab978SDwayne Grant McConnell 
1818bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
1819bf1ab978SDwayne Grant McConnell 				   size_t len, loff_t *pos)
1820bf1ab978SDwayne Grant McConnell {
1821bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1822bf1ab978SDwayne Grant McConnell 	int ret;
1823bf1ab978SDwayne Grant McConnell 
1824bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1825bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1826bf1ab978SDwayne Grant McConnell 	ret = __spufs_proxydma_info_read(ctx, buf, len, pos);
1827b9e3bd77SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1828b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1829b9e3bd77SDwayne Grant McConnell 
1830b9e3bd77SDwayne Grant McConnell 	return ret;
1831b9e3bd77SDwayne Grant McConnell }
1832b9e3bd77SDwayne Grant McConnell 
18335dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = {
1834b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1835b9e3bd77SDwayne Grant McConnell 	.read = spufs_proxydma_info_read,
1836b9e3bd77SDwayne Grant McConnell };
1837b9e3bd77SDwayne Grant McConnell 
183867207b96SArnd Bergmann struct tree_descr spufs_dir_contents[] = {
183967207b96SArnd Bergmann 	{ "mem",  &spufs_mem_fops,  0666, },
18408b3d6663SArnd Bergmann 	{ "regs", &spufs_regs_fops,  0666, },
184167207b96SArnd Bergmann 	{ "mbox", &spufs_mbox_fops, 0444, },
184267207b96SArnd Bergmann 	{ "ibox", &spufs_ibox_fops, 0444, },
184367207b96SArnd Bergmann 	{ "wbox", &spufs_wbox_fops, 0222, },
184467207b96SArnd Bergmann 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
184567207b96SArnd Bergmann 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
184667207b96SArnd Bergmann 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
184767207b96SArnd Bergmann 	{ "signal1", &spufs_signal1_fops, 0666, },
184867207b96SArnd Bergmann 	{ "signal2", &spufs_signal2_fops, 0666, },
184967207b96SArnd Bergmann 	{ "signal1_type", &spufs_signal1_type, 0666, },
185067207b96SArnd Bergmann 	{ "signal2_type", &spufs_signal2_type, 0666, },
18516df10a82SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18528b3d6663SArnd Bergmann 	{ "fpcr", &spufs_fpcr_fops, 0666, },
1853b9e3bd77SDwayne Grant McConnell 	{ "lslr", &spufs_lslr_ops, 0444, },
1854b9e3bd77SDwayne Grant McConnell 	{ "mfc", &spufs_mfc_fops, 0666, },
1855b9e3bd77SDwayne Grant McConnell 	{ "mss", &spufs_mss_fops, 0666, },
1856b9e3bd77SDwayne Grant McConnell 	{ "npc", &spufs_npc_ops, 0666, },
1857b9e3bd77SDwayne Grant McConnell 	{ "srr0", &spufs_srr0_ops, 0666, },
18588b3d6663SArnd Bergmann 	{ "decr", &spufs_decr_ops, 0666, },
18598b3d6663SArnd Bergmann 	{ "decr_status", &spufs_decr_status_ops, 0666, },
18608b3d6663SArnd Bergmann 	{ "event_mask", &spufs_event_mask_ops, 0666, },
1861b9e3bd77SDwayne Grant McConnell 	{ "event_status", &spufs_event_status_ops, 0444, },
186227d5bf2aSBenjamin Herrenschmidt 	{ "psmap", &spufs_psmap_fops, 0666, },
186386767277SArnd Bergmann 	{ "phys-id", &spufs_id_ops, 0666, },
186486767277SArnd Bergmann 	{ "object-id", &spufs_object_id_ops, 0666, },
186569a2f00cSDwayne Grant McConnell 	{ "mbox_info", &spufs_mbox_info_fops, 0444, },
186669a2f00cSDwayne Grant McConnell 	{ "ibox_info", &spufs_ibox_info_fops, 0444, },
186769a2f00cSDwayne Grant McConnell 	{ "wbox_info", &spufs_wbox_info_fops, 0444, },
1868b9e3bd77SDwayne Grant McConnell 	{ "dma_info", &spufs_dma_info_fops, 0444, },
1869b9e3bd77SDwayne Grant McConnell 	{ "proxydma_info", &spufs_proxydma_info_fops, 0444, },
187067207b96SArnd Bergmann 	{},
187167207b96SArnd Bergmann };
18725737edd1SMark Nutter 
18735737edd1SMark Nutter struct tree_descr spufs_dir_nosched_contents[] = {
18745737edd1SMark Nutter 	{ "mem",  &spufs_mem_fops,  0666, },
18755737edd1SMark Nutter 	{ "mbox", &spufs_mbox_fops, 0444, },
18765737edd1SMark Nutter 	{ "ibox", &spufs_ibox_fops, 0444, },
18775737edd1SMark Nutter 	{ "wbox", &spufs_wbox_fops, 0222, },
18785737edd1SMark Nutter 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
18795737edd1SMark Nutter 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
18805737edd1SMark Nutter 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
18815737edd1SMark Nutter 	{ "signal1", &spufs_signal1_fops, 0666, },
18825737edd1SMark Nutter 	{ "signal2", &spufs_signal2_fops, 0666, },
18835737edd1SMark Nutter 	{ "signal1_type", &spufs_signal1_type, 0666, },
18845737edd1SMark Nutter 	{ "signal2_type", &spufs_signal2_type, 0666, },
18855737edd1SMark Nutter 	{ "mss", &spufs_mss_fops, 0666, },
18865737edd1SMark Nutter 	{ "mfc", &spufs_mfc_fops, 0666, },
18875737edd1SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18885737edd1SMark Nutter 	{ "npc", &spufs_npc_ops, 0666, },
18895737edd1SMark Nutter 	{ "psmap", &spufs_psmap_fops, 0666, },
18905737edd1SMark Nutter 	{ "phys-id", &spufs_id_ops, 0666, },
18915737edd1SMark Nutter 	{ "object-id", &spufs_object_id_ops, 0666, },
18925737edd1SMark Nutter 	{},
18935737edd1SMark Nutter };
1894bf1ab978SDwayne Grant McConnell 
1895bf1ab978SDwayne Grant McConnell struct spufs_coredump_reader spufs_coredump_read[] = {
1896bf1ab978SDwayne Grant McConnell 	{ "regs", __spufs_regs_read, NULL, 128 * 16 },
1897bf1ab978SDwayne Grant McConnell 	{ "fpcr", __spufs_fpcr_read, NULL, 16 },
1898bf1ab978SDwayne Grant McConnell 	{ "lslr", NULL, __spufs_lslr_get, 11 },
1899bf1ab978SDwayne Grant McConnell 	{ "decr", NULL, __spufs_decr_get, 11 },
1900bf1ab978SDwayne Grant McConnell 	{ "decr_status", NULL, __spufs_decr_status_get, 11 },
1901bf1ab978SDwayne Grant McConnell 	{ "mem", __spufs_mem_read, NULL, 256 * 1024, },
1902bf1ab978SDwayne Grant McConnell 	{ "signal1", __spufs_signal1_read, NULL, 4 },
1903bf1ab978SDwayne Grant McConnell 	{ "signal1_type", NULL, __spufs_signal1_type_get, 2 },
1904bf1ab978SDwayne Grant McConnell 	{ "signal2", __spufs_signal2_read, NULL, 4 },
1905bf1ab978SDwayne Grant McConnell 	{ "signal2_type", NULL, __spufs_signal2_type_get, 2 },
1906bf1ab978SDwayne Grant McConnell 	{ "event_mask", NULL, __spufs_event_mask_get, 8 },
1907bf1ab978SDwayne Grant McConnell 	{ "event_status", NULL, __spufs_event_status_get, 8 },
1908bf1ab978SDwayne Grant McConnell 	{ "mbox_info", __spufs_mbox_info_read, NULL, 4 },
1909bf1ab978SDwayne Grant McConnell 	{ "ibox_info", __spufs_ibox_info_read, NULL, 4 },
1910bf1ab978SDwayne Grant McConnell 	{ "wbox_info", __spufs_wbox_info_read, NULL, 16 },
1911bf1ab978SDwayne Grant McConnell 	{ "dma_info", __spufs_dma_info_read, NULL, 69 * 8 },
1912bf1ab978SDwayne Grant McConnell 	{ "proxydma_info", __spufs_proxydma_info_read, NULL, 35 * 8 },
1913bf1ab978SDwayne Grant McConnell 	{ "object-id", NULL, __spufs_object_id_get, 19 },
1914bf1ab978SDwayne Grant McConnell 	{ },
1915bf1ab978SDwayne Grant McConnell };
1916bf1ab978SDwayne Grant McConnell int spufs_coredump_num_notes = ARRAY_SIZE(spufs_coredump_read) - 1;
1917bf1ab978SDwayne Grant McConnell 
1918