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 {
66bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
67aa0ed2bdSArnd Bergmann 	ssize_t ret;
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,
77aa0ed2bdSArnd Bergmann 					size_t size, loff_t *ppos)
7867207b96SArnd Bergmann {
7967207b96SArnd Bergmann 	struct spu_context *ctx = file->private_data;
808b3d6663SArnd Bergmann 	char *local_store;
81aa0ed2bdSArnd Bergmann 	loff_t pos = *ppos;
828b3d6663SArnd Bergmann 	int ret;
8367207b96SArnd Bergmann 
84aa0ed2bdSArnd Bergmann 	if (pos < 0)
85aa0ed2bdSArnd Bergmann 		return -EINVAL;
86aa0ed2bdSArnd Bergmann 	if (pos > LS_SIZE)
8767207b96SArnd Bergmann 		return -EFBIG;
88aa0ed2bdSArnd Bergmann 	if (size > LS_SIZE - pos)
89aa0ed2bdSArnd Bergmann 		size = LS_SIZE - pos;
908b3d6663SArnd Bergmann 
918b3d6663SArnd Bergmann 	spu_acquire(ctx);
928b3d6663SArnd Bergmann 	local_store = ctx->ops->get_ls(ctx);
93aa0ed2bdSArnd Bergmann 	ret = copy_from_user(local_store + pos, buffer, size);
948b3d6663SArnd Bergmann 	spu_release(ctx);
95aa0ed2bdSArnd Bergmann 
96aa0ed2bdSArnd Bergmann 	if (ret)
97aa0ed2bdSArnd Bergmann 		return -EFAULT;
98aa0ed2bdSArnd Bergmann 	*ppos = pos + size;
99aa0ed2bdSArnd Bergmann 	return size;
10067207b96SArnd Bergmann }
10167207b96SArnd Bergmann 
10278bde53eSBenjamin Herrenschmidt static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma,
10378bde53eSBenjamin Herrenschmidt 					  unsigned long address)
1048b3d6663SArnd Bergmann {
1058b3d6663SArnd Bergmann 	struct spu_context *ctx = vma->vm_file->private_data;
10678bde53eSBenjamin Herrenschmidt 	unsigned long pfn, offset = address - vma->vm_start;
10778bde53eSBenjamin Herrenschmidt 
1088b3d6663SArnd Bergmann 	offset += vma->vm_pgoff << PAGE_SHIFT;
1098b3d6663SArnd Bergmann 
110128b8546SMasato Noguchi 	if (offset >= LS_SIZE)
111128b8546SMasato Noguchi 		return NOPFN_SIGBUS;
112128b8546SMasato Noguchi 
1138b3d6663SArnd Bergmann 	spu_acquire(ctx);
1148b3d6663SArnd Bergmann 
115ac91cb8dSArnd Bergmann 	if (ctx->state == SPU_STATE_SAVED) {
116ac91cb8dSArnd Bergmann 		vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
117932f535dSArnd Bergmann 							& ~_PAGE_NO_CACHE);
11878bde53eSBenjamin Herrenschmidt 		pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset);
119ac91cb8dSArnd Bergmann 	} else {
120ac91cb8dSArnd Bergmann 		vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
121932f535dSArnd Bergmann 					     | _PAGE_NO_CACHE);
12278bde53eSBenjamin Herrenschmidt 		pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT;
123ac91cb8dSArnd Bergmann 	}
12478bde53eSBenjamin Herrenschmidt 	vm_insert_pfn(vma, address, pfn);
12578bde53eSBenjamin Herrenschmidt 
1268b3d6663SArnd Bergmann 	spu_release(ctx);
1278b3d6663SArnd Bergmann 
12878bde53eSBenjamin Herrenschmidt 	return NOPFN_REFAULT;
1298b3d6663SArnd Bergmann }
1308b3d6663SArnd Bergmann 
13178bde53eSBenjamin Herrenschmidt 
1328b3d6663SArnd Bergmann static struct vm_operations_struct spufs_mem_mmap_vmops = {
13378bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mem_mmap_nopfn,
1348b3d6663SArnd Bergmann };
1358b3d6663SArnd Bergmann 
13667207b96SArnd Bergmann static int
13767207b96SArnd Bergmann spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
13867207b96SArnd Bergmann {
1398b3d6663SArnd Bergmann 	if (!(vma->vm_flags & VM_SHARED))
1408b3d6663SArnd Bergmann 		return -EINVAL;
14167207b96SArnd Bergmann 
14278bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
14367207b96SArnd Bergmann 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
14467207b96SArnd Bergmann 				     | _PAGE_NO_CACHE);
1458b3d6663SArnd Bergmann 
1468b3d6663SArnd Bergmann 	vma->vm_ops = &spufs_mem_mmap_vmops;
14767207b96SArnd Bergmann 	return 0;
14867207b96SArnd Bergmann }
14967207b96SArnd Bergmann 
1505dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = {
15167207b96SArnd Bergmann 	.open	 = spufs_mem_open,
15267207b96SArnd Bergmann 	.read    = spufs_mem_read,
15367207b96SArnd Bergmann 	.write   = spufs_mem_write,
1548b3d6663SArnd Bergmann 	.llseek  = generic_file_llseek,
15567207b96SArnd Bergmann 	.mmap    = spufs_mem_mmap,
1568b3d6663SArnd Bergmann };
1578b3d6663SArnd Bergmann 
15878bde53eSBenjamin Herrenschmidt static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma,
1596df10a82SMark Nutter 				    unsigned long address,
16078bde53eSBenjamin Herrenschmidt 				    unsigned long ps_offs,
16127d5bf2aSBenjamin Herrenschmidt 				    unsigned long ps_size)
1626df10a82SMark Nutter {
1636df10a82SMark Nutter 	struct spu_context *ctx = vma->vm_file->private_data;
16478bde53eSBenjamin Herrenschmidt 	unsigned long area, offset = address - vma->vm_start;
1656df10a82SMark Nutter 	int ret;
1666df10a82SMark Nutter 
1676df10a82SMark Nutter 	offset += vma->vm_pgoff << PAGE_SHIFT;
16827d5bf2aSBenjamin Herrenschmidt 	if (offset >= ps_size)
16978bde53eSBenjamin Herrenschmidt 		return NOPFN_SIGBUS;
1706df10a82SMark Nutter 
17178bde53eSBenjamin Herrenschmidt 	/* error here usually means a signal.. we might want to test
17278bde53eSBenjamin Herrenschmidt 	 * the error code more precisely though
17378bde53eSBenjamin Herrenschmidt 	 */
17426bec673SChristoph Hellwig 	ret = spu_acquire_runnable(ctx, 0);
1756df10a82SMark Nutter 	if (ret)
17678bde53eSBenjamin Herrenschmidt 		return NOPFN_REFAULT;
1776df10a82SMark Nutter 
1786df10a82SMark Nutter 	area = ctx->spu->problem_phys + ps_offs;
17978bde53eSBenjamin Herrenschmidt 	vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT);
1806df10a82SMark Nutter 	spu_release(ctx);
1816df10a82SMark Nutter 
18278bde53eSBenjamin Herrenschmidt 	return NOPFN_REFAULT;
1836df10a82SMark Nutter }
1846df10a82SMark Nutter 
18527d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
18678bde53eSBenjamin Herrenschmidt static unsigned long spufs_cntl_mmap_nopfn(struct vm_area_struct *vma,
18778bde53eSBenjamin Herrenschmidt 					   unsigned long address)
1886df10a82SMark Nutter {
18978bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x4000, 0x1000);
1906df10a82SMark Nutter }
1916df10a82SMark Nutter 
1926df10a82SMark Nutter static struct vm_operations_struct spufs_cntl_mmap_vmops = {
19378bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_cntl_mmap_nopfn,
1946df10a82SMark Nutter };
1956df10a82SMark Nutter 
1966df10a82SMark Nutter /*
1976df10a82SMark Nutter  * mmap support for problem state control area [0x4000 - 0x4fff].
1986df10a82SMark Nutter  */
1996df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
2006df10a82SMark Nutter {
2016df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
2026df10a82SMark Nutter 		return -EINVAL;
2036df10a82SMark Nutter 
20478bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
2056df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
20623cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
2076df10a82SMark Nutter 
2086df10a82SMark Nutter 	vma->vm_ops = &spufs_cntl_mmap_vmops;
2096df10a82SMark Nutter 	return 0;
2106df10a82SMark Nutter }
21127d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
21227d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL
21327d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
2146df10a82SMark Nutter 
215e1dbff2bSArnd Bergmann static u64 spufs_cntl_get(void *data)
216e1dbff2bSArnd Bergmann {
217e1dbff2bSArnd Bergmann 	struct spu_context *ctx = data;
218e1dbff2bSArnd Bergmann 	u64 val;
219e1dbff2bSArnd Bergmann 
220e1dbff2bSArnd Bergmann 	spu_acquire(ctx);
221e1dbff2bSArnd Bergmann 	val = ctx->ops->status_read(ctx);
222e1dbff2bSArnd Bergmann 	spu_release(ctx);
223e1dbff2bSArnd Bergmann 
224e1dbff2bSArnd Bergmann 	return val;
225e1dbff2bSArnd Bergmann }
226e1dbff2bSArnd Bergmann 
227e1dbff2bSArnd Bergmann static void spufs_cntl_set(void *data, u64 val)
228e1dbff2bSArnd Bergmann {
229e1dbff2bSArnd Bergmann 	struct spu_context *ctx = data;
230e1dbff2bSArnd Bergmann 
231e1dbff2bSArnd Bergmann 	spu_acquire(ctx);
232e1dbff2bSArnd Bergmann 	ctx->ops->runcntl_write(ctx, val);
233e1dbff2bSArnd Bergmann 	spu_release(ctx);
234e1dbff2bSArnd Bergmann }
235e1dbff2bSArnd Bergmann 
2366df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file)
2376df10a82SMark Nutter {
2386df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
2396df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
2406df10a82SMark Nutter 
2416df10a82SMark Nutter 	file->private_data = ctx;
2426df10a82SMark Nutter 	ctx->cntl = inode->i_mapping;
24317e0e270SBenjamin Herrenschmidt 	smp_wmb();
244e1dbff2bSArnd Bergmann 	return simple_attr_open(inode, file, spufs_cntl_get,
245e1dbff2bSArnd Bergmann 					spufs_cntl_set, "0x%08lx");
2466df10a82SMark Nutter }
2476df10a82SMark Nutter 
2485dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = {
2496df10a82SMark Nutter 	.open = spufs_cntl_open,
250654e4aeeSNoguchi, Masato 	.release = simple_attr_close,
251e1dbff2bSArnd Bergmann 	.read = simple_attr_read,
252e1dbff2bSArnd Bergmann 	.write = simple_attr_write,
2536df10a82SMark Nutter 	.mmap = spufs_cntl_mmap,
2546df10a82SMark Nutter };
2556df10a82SMark Nutter 
2568b3d6663SArnd Bergmann static int
2578b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file)
2588b3d6663SArnd Bergmann {
2598b3d6663SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
2608b3d6663SArnd Bergmann 	file->private_data = i->i_ctx;
2618b3d6663SArnd Bergmann 	return 0;
2628b3d6663SArnd Bergmann }
2638b3d6663SArnd Bergmann 
2648b3d6663SArnd Bergmann static ssize_t
265bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer,
266bf1ab978SDwayne Grant McConnell 			size_t size, loff_t *pos)
267bf1ab978SDwayne Grant McConnell {
268bf1ab978SDwayne Grant McConnell 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
269bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos,
270bf1ab978SDwayne Grant McConnell 				      lscsa->gprs, sizeof lscsa->gprs);
271bf1ab978SDwayne Grant McConnell }
272bf1ab978SDwayne Grant McConnell 
273bf1ab978SDwayne Grant McConnell static ssize_t
2748b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer,
2758b3d6663SArnd Bergmann 		size_t size, loff_t *pos)
2768b3d6663SArnd Bergmann {
2778b3d6663SArnd Bergmann 	int ret;
278bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
2798b3d6663SArnd Bergmann 
2808b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
281bf1ab978SDwayne Grant McConnell 	ret = __spufs_regs_read(ctx, buffer, size, pos);
2828b3d6663SArnd Bergmann 	spu_release(ctx);
2838b3d6663SArnd Bergmann 	return ret;
2848b3d6663SArnd Bergmann }
2858b3d6663SArnd Bergmann 
2868b3d6663SArnd Bergmann static ssize_t
2878b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer,
2888b3d6663SArnd Bergmann 		 size_t size, loff_t *pos)
2898b3d6663SArnd Bergmann {
2908b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
2918b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
2928b3d6663SArnd Bergmann 	int ret;
2938b3d6663SArnd Bergmann 
2948b3d6663SArnd Bergmann 	size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size);
2958b3d6663SArnd Bergmann 	if (size <= 0)
2968b3d6663SArnd Bergmann 		return -EFBIG;
2978b3d6663SArnd Bergmann 	*pos += size;
2988b3d6663SArnd Bergmann 
2998b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
3008b3d6663SArnd Bergmann 
3018b3d6663SArnd Bergmann 	ret = copy_from_user(lscsa->gprs + *pos - size,
3028b3d6663SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
3038b3d6663SArnd Bergmann 
3048b3d6663SArnd Bergmann 	spu_release(ctx);
3058b3d6663SArnd Bergmann 	return ret;
3068b3d6663SArnd Bergmann }
3078b3d6663SArnd Bergmann 
3085dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = {
3098b3d6663SArnd Bergmann 	.open	 = spufs_regs_open,
3108b3d6663SArnd Bergmann 	.read    = spufs_regs_read,
3118b3d6663SArnd Bergmann 	.write   = spufs_regs_write,
3128b3d6663SArnd Bergmann 	.llseek  = generic_file_llseek,
3138b3d6663SArnd Bergmann };
3148b3d6663SArnd Bergmann 
3158b3d6663SArnd Bergmann static ssize_t
316bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer,
317bf1ab978SDwayne Grant McConnell 			size_t size, loff_t * pos)
318bf1ab978SDwayne Grant McConnell {
319bf1ab978SDwayne Grant McConnell 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
320bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos,
321bf1ab978SDwayne Grant McConnell 				      &lscsa->fpcr, sizeof(lscsa->fpcr));
322bf1ab978SDwayne Grant McConnell }
323bf1ab978SDwayne Grant McConnell 
324bf1ab978SDwayne Grant McConnell static ssize_t
3258b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer,
3268b3d6663SArnd Bergmann 		size_t size, loff_t * pos)
3278b3d6663SArnd Bergmann {
3288b3d6663SArnd Bergmann 	int ret;
329bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
3308b3d6663SArnd Bergmann 
3318b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
332bf1ab978SDwayne Grant McConnell 	ret = __spufs_fpcr_read(ctx, buffer, size, pos);
3338b3d6663SArnd Bergmann 	spu_release(ctx);
3348b3d6663SArnd Bergmann 	return ret;
3358b3d6663SArnd Bergmann }
3368b3d6663SArnd Bergmann 
3378b3d6663SArnd Bergmann static ssize_t
3388b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer,
3398b3d6663SArnd Bergmann 		 size_t size, loff_t * pos)
3408b3d6663SArnd Bergmann {
3418b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
3428b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
3438b3d6663SArnd Bergmann 	int ret;
3448b3d6663SArnd Bergmann 
3458b3d6663SArnd Bergmann 	size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
3468b3d6663SArnd Bergmann 	if (size <= 0)
3478b3d6663SArnd Bergmann 		return -EFBIG;
3488b3d6663SArnd Bergmann 	*pos += size;
3498b3d6663SArnd Bergmann 
3508b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
3518b3d6663SArnd Bergmann 
3528b3d6663SArnd Bergmann 	ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
3538b3d6663SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
3548b3d6663SArnd Bergmann 
3558b3d6663SArnd Bergmann 	spu_release(ctx);
3568b3d6663SArnd Bergmann 	return ret;
3578b3d6663SArnd Bergmann }
3588b3d6663SArnd Bergmann 
3595dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = {
3608b3d6663SArnd Bergmann 	.open = spufs_regs_open,
3618b3d6663SArnd Bergmann 	.read = spufs_fpcr_read,
3628b3d6663SArnd Bergmann 	.write = spufs_fpcr_write,
36367207b96SArnd Bergmann 	.llseek = generic_file_llseek,
36467207b96SArnd Bergmann };
36567207b96SArnd Bergmann 
36667207b96SArnd Bergmann /* generic open function for all pipe-like files */
36767207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file)
36867207b96SArnd Bergmann {
36967207b96SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
37067207b96SArnd Bergmann 	file->private_data = i->i_ctx;
37167207b96SArnd Bergmann 
37267207b96SArnd Bergmann 	return nonseekable_open(inode, file);
37367207b96SArnd Bergmann }
37467207b96SArnd Bergmann 
375cdcc89bbSArnd Bergmann /*
376cdcc89bbSArnd Bergmann  * Read as many bytes from the mailbox as possible, until
377cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
378cdcc89bbSArnd Bergmann  *
379cdcc89bbSArnd Bergmann  * - no more data available in the mailbox
380cdcc89bbSArnd Bergmann  * - end of the user provided buffer
381cdcc89bbSArnd Bergmann  * - end of the mapped area
382cdcc89bbSArnd Bergmann  */
38367207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
38467207b96SArnd Bergmann 			size_t len, loff_t *pos)
38567207b96SArnd Bergmann {
3868b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
387cdcc89bbSArnd Bergmann 	u32 mbox_data, __user *udata;
388cdcc89bbSArnd Bergmann 	ssize_t count;
38967207b96SArnd Bergmann 
39067207b96SArnd Bergmann 	if (len < 4)
39167207b96SArnd Bergmann 		return -EINVAL;
39267207b96SArnd Bergmann 
393cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_WRITE, buf, len))
39467207b96SArnd Bergmann 		return -EFAULT;
39567207b96SArnd Bergmann 
396cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
397cdcc89bbSArnd Bergmann 
398cdcc89bbSArnd Bergmann 	spu_acquire(ctx);
399274cef5eSArnd Bergmann 	for (count = 0; (count + 4) <= len; count += 4, udata++) {
400cdcc89bbSArnd Bergmann 		int ret;
401cdcc89bbSArnd Bergmann 		ret = ctx->ops->mbox_read(ctx, &mbox_data);
402cdcc89bbSArnd Bergmann 		if (ret == 0)
403cdcc89bbSArnd Bergmann 			break;
404cdcc89bbSArnd Bergmann 
405cdcc89bbSArnd Bergmann 		/*
406cdcc89bbSArnd Bergmann 		 * at the end of the mapped area, we can fault
407cdcc89bbSArnd Bergmann 		 * but still need to return the data we have
408cdcc89bbSArnd Bergmann 		 * read successfully so far.
409cdcc89bbSArnd Bergmann 		 */
410cdcc89bbSArnd Bergmann 		ret = __put_user(mbox_data, udata);
411cdcc89bbSArnd Bergmann 		if (ret) {
412cdcc89bbSArnd Bergmann 			if (!count)
413cdcc89bbSArnd Bergmann 				count = -EFAULT;
414cdcc89bbSArnd Bergmann 			break;
415cdcc89bbSArnd Bergmann 		}
416cdcc89bbSArnd Bergmann 	}
417cdcc89bbSArnd Bergmann 	spu_release(ctx);
418cdcc89bbSArnd Bergmann 
419cdcc89bbSArnd Bergmann 	if (!count)
420cdcc89bbSArnd Bergmann 		count = -EAGAIN;
421cdcc89bbSArnd Bergmann 
422cdcc89bbSArnd Bergmann 	return count;
42367207b96SArnd Bergmann }
42467207b96SArnd Bergmann 
4255dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = {
42667207b96SArnd Bergmann 	.open	= spufs_pipe_open,
42767207b96SArnd Bergmann 	.read	= spufs_mbox_read,
42867207b96SArnd Bergmann };
42967207b96SArnd Bergmann 
43067207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
43167207b96SArnd Bergmann 			size_t len, loff_t *pos)
43267207b96SArnd Bergmann {
4338b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
43467207b96SArnd Bergmann 	u32 mbox_stat;
43567207b96SArnd Bergmann 
43667207b96SArnd Bergmann 	if (len < 4)
43767207b96SArnd Bergmann 		return -EINVAL;
43867207b96SArnd Bergmann 
4398b3d6663SArnd Bergmann 	spu_acquire(ctx);
4408b3d6663SArnd Bergmann 
4418b3d6663SArnd Bergmann 	mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff;
4428b3d6663SArnd Bergmann 
4438b3d6663SArnd Bergmann 	spu_release(ctx);
44467207b96SArnd Bergmann 
44567207b96SArnd Bergmann 	if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat))
44667207b96SArnd Bergmann 		return -EFAULT;
44767207b96SArnd Bergmann 
44867207b96SArnd Bergmann 	return 4;
44967207b96SArnd Bergmann }
45067207b96SArnd Bergmann 
4515dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = {
45267207b96SArnd Bergmann 	.open	= spufs_pipe_open,
45367207b96SArnd Bergmann 	.read	= spufs_mbox_stat_read,
45467207b96SArnd Bergmann };
45567207b96SArnd Bergmann 
45667207b96SArnd Bergmann /* low-level ibox access function */
4578b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data)
45867207b96SArnd Bergmann {
4598b3d6663SArnd Bergmann 	return ctx->ops->ibox_read(ctx, data);
46067207b96SArnd Bergmann }
46167207b96SArnd Bergmann 
46267207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on)
46367207b96SArnd Bergmann {
4648b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
4658b3d6663SArnd Bergmann 
4668b3d6663SArnd Bergmann 	return fasync_helper(fd, file, on, &ctx->ibox_fasync);
4678b3d6663SArnd Bergmann }
4688b3d6663SArnd Bergmann 
4698b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */
4708b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu)
4718b3d6663SArnd Bergmann {
4728b3d6663SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
4738b3d6663SArnd Bergmann 
4748b3d6663SArnd Bergmann 	wake_up_all(&ctx->ibox_wq);
4758b3d6663SArnd Bergmann 	kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN);
47667207b96SArnd Bergmann }
47767207b96SArnd Bergmann 
478cdcc89bbSArnd Bergmann /*
479cdcc89bbSArnd Bergmann  * Read as many bytes from the interrupt mailbox as possible, until
480cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
481cdcc89bbSArnd Bergmann  *
482cdcc89bbSArnd Bergmann  * - no more data available in the mailbox
483cdcc89bbSArnd Bergmann  * - end of the user provided buffer
484cdcc89bbSArnd Bergmann  * - end of the mapped area
485cdcc89bbSArnd Bergmann  *
486cdcc89bbSArnd Bergmann  * If the file is opened without O_NONBLOCK, we wait here until
487cdcc89bbSArnd Bergmann  * any data is available, but return when we have been able to
488cdcc89bbSArnd Bergmann  * read something.
489cdcc89bbSArnd Bergmann  */
49067207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
49167207b96SArnd Bergmann 			size_t len, loff_t *pos)
49267207b96SArnd Bergmann {
4938b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
494cdcc89bbSArnd Bergmann 	u32 ibox_data, __user *udata;
495cdcc89bbSArnd Bergmann 	ssize_t count;
49667207b96SArnd Bergmann 
49767207b96SArnd Bergmann 	if (len < 4)
49867207b96SArnd Bergmann 		return -EINVAL;
49967207b96SArnd Bergmann 
500cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_WRITE, buf, len))
501cdcc89bbSArnd Bergmann 		return -EFAULT;
502cdcc89bbSArnd Bergmann 
503cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
504cdcc89bbSArnd Bergmann 
5058b3d6663SArnd Bergmann 	spu_acquire(ctx);
50667207b96SArnd Bergmann 
507cdcc89bbSArnd Bergmann 	/* wait only for the first element */
508cdcc89bbSArnd Bergmann 	count = 0;
50967207b96SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
5108b3d6663SArnd Bergmann 		if (!spu_ibox_read(ctx, &ibox_data))
511cdcc89bbSArnd Bergmann 			count = -EAGAIN;
51267207b96SArnd Bergmann 	} else {
513cdcc89bbSArnd Bergmann 		count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
514cdcc89bbSArnd Bergmann 	}
515cdcc89bbSArnd Bergmann 	if (count)
516cdcc89bbSArnd Bergmann 		goto out;
517cdcc89bbSArnd Bergmann 
518cdcc89bbSArnd Bergmann 	/* if we can't write at all, return -EFAULT */
519cdcc89bbSArnd Bergmann 	count = __put_user(ibox_data, udata);
520cdcc89bbSArnd Bergmann 	if (count)
521cdcc89bbSArnd Bergmann 		goto out;
522cdcc89bbSArnd Bergmann 
523cdcc89bbSArnd Bergmann 	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
524cdcc89bbSArnd Bergmann 		int ret;
525cdcc89bbSArnd Bergmann 		ret = ctx->ops->ibox_read(ctx, &ibox_data);
526cdcc89bbSArnd Bergmann 		if (ret == 0)
527cdcc89bbSArnd Bergmann 			break;
528cdcc89bbSArnd Bergmann 		/*
529cdcc89bbSArnd Bergmann 		 * at the end of the mapped area, we can fault
530cdcc89bbSArnd Bergmann 		 * but still need to return the data we have
531cdcc89bbSArnd Bergmann 		 * read successfully so far.
532cdcc89bbSArnd Bergmann 		 */
533cdcc89bbSArnd Bergmann 		ret = __put_user(ibox_data, udata);
534cdcc89bbSArnd Bergmann 		if (ret)
535cdcc89bbSArnd Bergmann 			break;
53667207b96SArnd Bergmann 	}
53767207b96SArnd Bergmann 
538cdcc89bbSArnd Bergmann out:
5398b3d6663SArnd Bergmann 	spu_release(ctx);
5408b3d6663SArnd Bergmann 
541cdcc89bbSArnd Bergmann 	return count;
54267207b96SArnd Bergmann }
54367207b96SArnd Bergmann 
54467207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait)
54567207b96SArnd Bergmann {
5468b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
54767207b96SArnd Bergmann 	unsigned int mask;
54867207b96SArnd Bergmann 
5498b3d6663SArnd Bergmann 	poll_wait(file, &ctx->ibox_wq, wait);
55067207b96SArnd Bergmann 
5513a843d7cSArnd Bergmann 	spu_acquire(ctx);
5523a843d7cSArnd Bergmann 	mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM);
5533a843d7cSArnd Bergmann 	spu_release(ctx);
55467207b96SArnd Bergmann 
55567207b96SArnd Bergmann 	return mask;
55667207b96SArnd Bergmann }
55767207b96SArnd Bergmann 
5585dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = {
55967207b96SArnd Bergmann 	.open	= spufs_pipe_open,
56067207b96SArnd Bergmann 	.read	= spufs_ibox_read,
56167207b96SArnd Bergmann 	.poll	= spufs_ibox_poll,
56267207b96SArnd Bergmann 	.fasync	= spufs_ibox_fasync,
56367207b96SArnd Bergmann };
56467207b96SArnd Bergmann 
56567207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
56667207b96SArnd Bergmann 			size_t len, loff_t *pos)
56767207b96SArnd Bergmann {
5688b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
56967207b96SArnd Bergmann 	u32 ibox_stat;
57067207b96SArnd Bergmann 
57167207b96SArnd Bergmann 	if (len < 4)
57267207b96SArnd Bergmann 		return -EINVAL;
57367207b96SArnd Bergmann 
5748b3d6663SArnd Bergmann 	spu_acquire(ctx);
5758b3d6663SArnd Bergmann 	ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff;
5768b3d6663SArnd Bergmann 	spu_release(ctx);
57767207b96SArnd Bergmann 
57867207b96SArnd Bergmann 	if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat))
57967207b96SArnd Bergmann 		return -EFAULT;
58067207b96SArnd Bergmann 
58167207b96SArnd Bergmann 	return 4;
58267207b96SArnd Bergmann }
58367207b96SArnd Bergmann 
5845dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = {
58567207b96SArnd Bergmann 	.open	= spufs_pipe_open,
58667207b96SArnd Bergmann 	.read	= spufs_ibox_stat_read,
58767207b96SArnd Bergmann };
58867207b96SArnd Bergmann 
58967207b96SArnd Bergmann /* low-level mailbox write */
5908b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data)
59167207b96SArnd Bergmann {
5928b3d6663SArnd Bergmann 	return ctx->ops->wbox_write(ctx, data);
59367207b96SArnd Bergmann }
59467207b96SArnd Bergmann 
59567207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on)
59667207b96SArnd Bergmann {
5978b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
5988b3d6663SArnd Bergmann 	int ret;
5998b3d6663SArnd Bergmann 
6008b3d6663SArnd Bergmann 	ret = fasync_helper(fd, file, on, &ctx->wbox_fasync);
6018b3d6663SArnd Bergmann 
6028b3d6663SArnd Bergmann 	return ret;
6038b3d6663SArnd Bergmann }
6048b3d6663SArnd Bergmann 
6058b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */
6068b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu)
6078b3d6663SArnd Bergmann {
6088b3d6663SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
6098b3d6663SArnd Bergmann 
6108b3d6663SArnd Bergmann 	wake_up_all(&ctx->wbox_wq);
6118b3d6663SArnd Bergmann 	kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT);
61267207b96SArnd Bergmann }
61367207b96SArnd Bergmann 
614cdcc89bbSArnd Bergmann /*
615cdcc89bbSArnd Bergmann  * Write as many bytes to the interrupt mailbox as possible, until
616cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
617cdcc89bbSArnd Bergmann  *
618cdcc89bbSArnd Bergmann  * - the mailbox is full
619cdcc89bbSArnd Bergmann  * - end of the user provided buffer
620cdcc89bbSArnd Bergmann  * - end of the mapped area
621cdcc89bbSArnd Bergmann  *
622cdcc89bbSArnd Bergmann  * If the file is opened without O_NONBLOCK, we wait here until
623cdcc89bbSArnd Bergmann  * space is availabyl, but return when we have been able to
624cdcc89bbSArnd Bergmann  * write something.
625cdcc89bbSArnd Bergmann  */
62667207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
62767207b96SArnd Bergmann 			size_t len, loff_t *pos)
62867207b96SArnd Bergmann {
6298b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
630cdcc89bbSArnd Bergmann 	u32 wbox_data, __user *udata;
631cdcc89bbSArnd Bergmann 	ssize_t count;
63267207b96SArnd Bergmann 
63367207b96SArnd Bergmann 	if (len < 4)
63467207b96SArnd Bergmann 		return -EINVAL;
63567207b96SArnd Bergmann 
636cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
637cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_READ, buf, len))
638cdcc89bbSArnd Bergmann 		return -EFAULT;
639cdcc89bbSArnd Bergmann 
640cdcc89bbSArnd Bergmann 	if (__get_user(wbox_data, udata))
64167207b96SArnd Bergmann 		return -EFAULT;
64267207b96SArnd Bergmann 
6438b3d6663SArnd Bergmann 	spu_acquire(ctx);
6448b3d6663SArnd Bergmann 
645cdcc89bbSArnd Bergmann 	/*
646cdcc89bbSArnd Bergmann 	 * make sure we can at least write one element, by waiting
647cdcc89bbSArnd Bergmann 	 * in case of !O_NONBLOCK
648cdcc89bbSArnd Bergmann 	 */
649cdcc89bbSArnd Bergmann 	count = 0;
65067207b96SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
6518b3d6663SArnd Bergmann 		if (!spu_wbox_write(ctx, wbox_data))
652cdcc89bbSArnd Bergmann 			count = -EAGAIN;
65367207b96SArnd Bergmann 	} else {
654cdcc89bbSArnd Bergmann 		count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
65567207b96SArnd Bergmann 	}
65667207b96SArnd Bergmann 
657cdcc89bbSArnd Bergmann 	if (count)
658cdcc89bbSArnd Bergmann 		goto out;
6598b3d6663SArnd Bergmann 
660cdcc89bbSArnd Bergmann 	/* write aѕ much as possible */
661cdcc89bbSArnd Bergmann 	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
662cdcc89bbSArnd Bergmann 		int ret;
663cdcc89bbSArnd Bergmann 		ret = __get_user(wbox_data, udata);
664cdcc89bbSArnd Bergmann 		if (ret)
665cdcc89bbSArnd Bergmann 			break;
666cdcc89bbSArnd Bergmann 
667cdcc89bbSArnd Bergmann 		ret = spu_wbox_write(ctx, wbox_data);
668cdcc89bbSArnd Bergmann 		if (ret == 0)
669cdcc89bbSArnd Bergmann 			break;
670cdcc89bbSArnd Bergmann 	}
671cdcc89bbSArnd Bergmann 
672cdcc89bbSArnd Bergmann out:
673cdcc89bbSArnd Bergmann 	spu_release(ctx);
674cdcc89bbSArnd Bergmann 	return count;
67567207b96SArnd Bergmann }
67667207b96SArnd Bergmann 
67767207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait)
67867207b96SArnd Bergmann {
6798b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
68067207b96SArnd Bergmann 	unsigned int mask;
68167207b96SArnd Bergmann 
6828b3d6663SArnd Bergmann 	poll_wait(file, &ctx->wbox_wq, wait);
68367207b96SArnd Bergmann 
6843a843d7cSArnd Bergmann 	spu_acquire(ctx);
6853a843d7cSArnd Bergmann 	mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM);
6863a843d7cSArnd Bergmann 	spu_release(ctx);
68767207b96SArnd Bergmann 
68867207b96SArnd Bergmann 	return mask;
68967207b96SArnd Bergmann }
69067207b96SArnd Bergmann 
6915dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = {
69267207b96SArnd Bergmann 	.open	= spufs_pipe_open,
69367207b96SArnd Bergmann 	.write	= spufs_wbox_write,
69467207b96SArnd Bergmann 	.poll	= spufs_wbox_poll,
69567207b96SArnd Bergmann 	.fasync	= spufs_wbox_fasync,
69667207b96SArnd Bergmann };
69767207b96SArnd Bergmann 
69867207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
69967207b96SArnd Bergmann 			size_t len, loff_t *pos)
70067207b96SArnd Bergmann {
7018b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
70267207b96SArnd Bergmann 	u32 wbox_stat;
70367207b96SArnd Bergmann 
70467207b96SArnd Bergmann 	if (len < 4)
70567207b96SArnd Bergmann 		return -EINVAL;
70667207b96SArnd Bergmann 
7078b3d6663SArnd Bergmann 	spu_acquire(ctx);
7088b3d6663SArnd Bergmann 	wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff;
7098b3d6663SArnd Bergmann 	spu_release(ctx);
71067207b96SArnd Bergmann 
71167207b96SArnd Bergmann 	if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat))
71267207b96SArnd Bergmann 		return -EFAULT;
71367207b96SArnd Bergmann 
71467207b96SArnd Bergmann 	return 4;
71567207b96SArnd Bergmann }
71667207b96SArnd Bergmann 
7175dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = {
71867207b96SArnd Bergmann 	.open	= spufs_pipe_open,
71967207b96SArnd Bergmann 	.read	= spufs_wbox_stat_read,
72067207b96SArnd Bergmann };
72167207b96SArnd Bergmann 
7226df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file)
7236df10a82SMark Nutter {
7246df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
7256df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
7266df10a82SMark Nutter 	file->private_data = ctx;
7276df10a82SMark Nutter 	ctx->signal1 = inode->i_mapping;
72817e0e270SBenjamin Herrenschmidt 	smp_wmb();
7296df10a82SMark Nutter 	return nonseekable_open(inode, file);
7306df10a82SMark Nutter }
7316df10a82SMark Nutter 
732bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
73367207b96SArnd Bergmann 			size_t len, loff_t *pos)
73467207b96SArnd Bergmann {
73517f88cebSDwayne Grant McConnell 	int ret = 0;
73667207b96SArnd Bergmann 	u32 data;
73767207b96SArnd Bergmann 
73867207b96SArnd Bergmann 	if (len < 4)
73967207b96SArnd Bergmann 		return -EINVAL;
74067207b96SArnd Bergmann 
74117f88cebSDwayne Grant McConnell 	if (ctx->csa.spu_chnlcnt_RW[3]) {
74217f88cebSDwayne Grant McConnell 		data = ctx->csa.spu_chnldata_RW[3];
74317f88cebSDwayne Grant McConnell 		ret = 4;
74417f88cebSDwayne Grant McConnell 	}
7458b3d6663SArnd Bergmann 
74617f88cebSDwayne Grant McConnell 	if (!ret)
74717f88cebSDwayne Grant McConnell 		goto out;
74817f88cebSDwayne Grant McConnell 
74967207b96SArnd Bergmann 	if (copy_to_user(buf, &data, 4))
75067207b96SArnd Bergmann 		return -EFAULT;
75167207b96SArnd Bergmann 
75217f88cebSDwayne Grant McConnell out:
75317f88cebSDwayne Grant McConnell 	return ret;
75467207b96SArnd Bergmann }
75567207b96SArnd Bergmann 
756bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
757bf1ab978SDwayne Grant McConnell 			size_t len, loff_t *pos)
758bf1ab978SDwayne Grant McConnell {
759bf1ab978SDwayne Grant McConnell 	int ret;
760bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
761bf1ab978SDwayne Grant McConnell 
762bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
763bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal1_read(ctx, buf, len, pos);
764bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
765bf1ab978SDwayne Grant McConnell 
766bf1ab978SDwayne Grant McConnell 	return ret;
767bf1ab978SDwayne Grant McConnell }
768bf1ab978SDwayne Grant McConnell 
76967207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
77067207b96SArnd Bergmann 			size_t len, loff_t *pos)
77167207b96SArnd Bergmann {
77267207b96SArnd Bergmann 	struct spu_context *ctx;
77367207b96SArnd Bergmann 	u32 data;
77467207b96SArnd Bergmann 
77567207b96SArnd Bergmann 	ctx = file->private_data;
77667207b96SArnd Bergmann 
77767207b96SArnd Bergmann 	if (len < 4)
77867207b96SArnd Bergmann 		return -EINVAL;
77967207b96SArnd Bergmann 
78067207b96SArnd Bergmann 	if (copy_from_user(&data, buf, 4))
78167207b96SArnd Bergmann 		return -EFAULT;
78267207b96SArnd Bergmann 
7838b3d6663SArnd Bergmann 	spu_acquire(ctx);
7848b3d6663SArnd Bergmann 	ctx->ops->signal1_write(ctx, data);
7858b3d6663SArnd Bergmann 	spu_release(ctx);
78667207b96SArnd Bergmann 
78767207b96SArnd Bergmann 	return 4;
78867207b96SArnd Bergmann }
78967207b96SArnd Bergmann 
79078bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal1_mmap_nopfn(struct vm_area_struct *vma,
79178bde53eSBenjamin Herrenschmidt 					      unsigned long address)
7926df10a82SMark Nutter {
79327d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000
79478bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x14000, 0x1000);
79527d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000
79627d5bf2aSBenjamin Herrenschmidt 	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
79727d5bf2aSBenjamin Herrenschmidt 	 * signal 1 and 2 area
79827d5bf2aSBenjamin Herrenschmidt 	 */
79978bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
80027d5bf2aSBenjamin Herrenschmidt #else
80127d5bf2aSBenjamin Herrenschmidt #error unsupported page size
80227d5bf2aSBenjamin Herrenschmidt #endif
8036df10a82SMark Nutter }
8046df10a82SMark Nutter 
8056df10a82SMark Nutter static struct vm_operations_struct spufs_signal1_mmap_vmops = {
80678bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_signal1_mmap_nopfn,
8076df10a82SMark Nutter };
8086df10a82SMark Nutter 
8096df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
8106df10a82SMark Nutter {
8116df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
8126df10a82SMark Nutter 		return -EINVAL;
8136df10a82SMark Nutter 
81478bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
8156df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
81623cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
8176df10a82SMark Nutter 
8186df10a82SMark Nutter 	vma->vm_ops = &spufs_signal1_mmap_vmops;
8196df10a82SMark Nutter 	return 0;
8206df10a82SMark Nutter }
8216df10a82SMark Nutter 
8225dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = {
8236df10a82SMark Nutter 	.open = spufs_signal1_open,
82467207b96SArnd Bergmann 	.read = spufs_signal1_read,
82567207b96SArnd Bergmann 	.write = spufs_signal1_write,
8266df10a82SMark Nutter 	.mmap = spufs_signal1_mmap,
82767207b96SArnd Bergmann };
82867207b96SArnd Bergmann 
8296df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file)
8306df10a82SMark Nutter {
8316df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
8326df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
8336df10a82SMark Nutter 	file->private_data = ctx;
8346df10a82SMark Nutter 	ctx->signal2 = inode->i_mapping;
83517e0e270SBenjamin Herrenschmidt 	smp_wmb();
8366df10a82SMark Nutter 	return nonseekable_open(inode, file);
8376df10a82SMark Nutter }
8386df10a82SMark Nutter 
839bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
84067207b96SArnd Bergmann 			size_t len, loff_t *pos)
84167207b96SArnd Bergmann {
84217f88cebSDwayne Grant McConnell 	int ret = 0;
84367207b96SArnd Bergmann 	u32 data;
84467207b96SArnd Bergmann 
84567207b96SArnd Bergmann 	if (len < 4)
84667207b96SArnd Bergmann 		return -EINVAL;
84767207b96SArnd Bergmann 
84817f88cebSDwayne Grant McConnell 	if (ctx->csa.spu_chnlcnt_RW[4]) {
84917f88cebSDwayne Grant McConnell 		data =  ctx->csa.spu_chnldata_RW[4];
85017f88cebSDwayne Grant McConnell 		ret = 4;
85117f88cebSDwayne Grant McConnell 	}
8528b3d6663SArnd Bergmann 
85317f88cebSDwayne Grant McConnell 	if (!ret)
85417f88cebSDwayne Grant McConnell 		goto out;
85517f88cebSDwayne Grant McConnell 
85667207b96SArnd Bergmann 	if (copy_to_user(buf, &data, 4))
85767207b96SArnd Bergmann 		return -EFAULT;
85867207b96SArnd Bergmann 
85917f88cebSDwayne Grant McConnell out:
860bf1ab978SDwayne Grant McConnell 	return ret;
861bf1ab978SDwayne Grant McConnell }
862bf1ab978SDwayne Grant McConnell 
863bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
864bf1ab978SDwayne Grant McConnell 			size_t len, loff_t *pos)
865bf1ab978SDwayne Grant McConnell {
866bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
867bf1ab978SDwayne Grant McConnell 	int ret;
868bf1ab978SDwayne Grant McConnell 
869bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
870bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal2_read(ctx, buf, len, pos);
871bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
872bf1ab978SDwayne Grant McConnell 
873bf1ab978SDwayne Grant McConnell 	return ret;
87467207b96SArnd Bergmann }
87567207b96SArnd Bergmann 
87667207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
87767207b96SArnd Bergmann 			size_t len, loff_t *pos)
87867207b96SArnd Bergmann {
87967207b96SArnd Bergmann 	struct spu_context *ctx;
88067207b96SArnd Bergmann 	u32 data;
88167207b96SArnd Bergmann 
88267207b96SArnd Bergmann 	ctx = file->private_data;
88367207b96SArnd Bergmann 
88467207b96SArnd Bergmann 	if (len < 4)
88567207b96SArnd Bergmann 		return -EINVAL;
88667207b96SArnd Bergmann 
88767207b96SArnd Bergmann 	if (copy_from_user(&data, buf, 4))
88867207b96SArnd Bergmann 		return -EFAULT;
88967207b96SArnd Bergmann 
8908b3d6663SArnd Bergmann 	spu_acquire(ctx);
8918b3d6663SArnd Bergmann 	ctx->ops->signal2_write(ctx, data);
8928b3d6663SArnd Bergmann 	spu_release(ctx);
89367207b96SArnd Bergmann 
89467207b96SArnd Bergmann 	return 4;
89567207b96SArnd Bergmann }
89667207b96SArnd Bergmann 
89727d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
89878bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal2_mmap_nopfn(struct vm_area_struct *vma,
89978bde53eSBenjamin Herrenschmidt 					      unsigned long address)
9006df10a82SMark Nutter {
90127d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000
90278bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x1c000, 0x1000);
90327d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000
90427d5bf2aSBenjamin Herrenschmidt 	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
90527d5bf2aSBenjamin Herrenschmidt 	 * signal 1 and 2 area
90627d5bf2aSBenjamin Herrenschmidt 	 */
90778bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
90827d5bf2aSBenjamin Herrenschmidt #else
90927d5bf2aSBenjamin Herrenschmidt #error unsupported page size
91027d5bf2aSBenjamin Herrenschmidt #endif
9116df10a82SMark Nutter }
9126df10a82SMark Nutter 
9136df10a82SMark Nutter static struct vm_operations_struct spufs_signal2_mmap_vmops = {
91478bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_signal2_mmap_nopfn,
9156df10a82SMark Nutter };
9166df10a82SMark Nutter 
9176df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
9186df10a82SMark Nutter {
9196df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
9206df10a82SMark Nutter 		return -EINVAL;
9216df10a82SMark Nutter 
92278bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
9236df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
92423cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
9256df10a82SMark Nutter 
9266df10a82SMark Nutter 	vma->vm_ops = &spufs_signal2_mmap_vmops;
9276df10a82SMark Nutter 	return 0;
9286df10a82SMark Nutter }
92927d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
93027d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL
93127d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
9326df10a82SMark Nutter 
9335dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = {
9346df10a82SMark Nutter 	.open = spufs_signal2_open,
93567207b96SArnd Bergmann 	.read = spufs_signal2_read,
93667207b96SArnd Bergmann 	.write = spufs_signal2_write,
9376df10a82SMark Nutter 	.mmap = spufs_signal2_mmap,
93867207b96SArnd Bergmann };
93967207b96SArnd Bergmann 
94067207b96SArnd Bergmann static void spufs_signal1_type_set(void *data, u64 val)
94167207b96SArnd Bergmann {
94267207b96SArnd Bergmann 	struct spu_context *ctx = data;
94367207b96SArnd Bergmann 
9448b3d6663SArnd Bergmann 	spu_acquire(ctx);
9458b3d6663SArnd Bergmann 	ctx->ops->signal1_type_set(ctx, val);
9468b3d6663SArnd Bergmann 	spu_release(ctx);
94767207b96SArnd Bergmann }
94867207b96SArnd Bergmann 
949bf1ab978SDwayne Grant McConnell static u64 __spufs_signal1_type_get(void *data)
950bf1ab978SDwayne Grant McConnell {
951bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
952bf1ab978SDwayne Grant McConnell 	return ctx->ops->signal1_type_get(ctx);
953bf1ab978SDwayne Grant McConnell }
954bf1ab978SDwayne Grant McConnell 
95567207b96SArnd Bergmann static u64 spufs_signal1_type_get(void *data)
95667207b96SArnd Bergmann {
95767207b96SArnd Bergmann 	struct spu_context *ctx = data;
9588b3d6663SArnd Bergmann 	u64 ret;
9598b3d6663SArnd Bergmann 
9608b3d6663SArnd Bergmann 	spu_acquire(ctx);
961bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal1_type_get(data);
9628b3d6663SArnd Bergmann 	spu_release(ctx);
9638b3d6663SArnd Bergmann 
9648b3d6663SArnd Bergmann 	return ret;
96567207b96SArnd Bergmann }
96667207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
96767207b96SArnd Bergmann 					spufs_signal1_type_set, "%llu");
96867207b96SArnd Bergmann 
96967207b96SArnd Bergmann static void spufs_signal2_type_set(void *data, u64 val)
97067207b96SArnd Bergmann {
97167207b96SArnd Bergmann 	struct spu_context *ctx = data;
97267207b96SArnd Bergmann 
9738b3d6663SArnd Bergmann 	spu_acquire(ctx);
9748b3d6663SArnd Bergmann 	ctx->ops->signal2_type_set(ctx, val);
9758b3d6663SArnd Bergmann 	spu_release(ctx);
97667207b96SArnd Bergmann }
97767207b96SArnd Bergmann 
978bf1ab978SDwayne Grant McConnell static u64 __spufs_signal2_type_get(void *data)
979bf1ab978SDwayne Grant McConnell {
980bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
981bf1ab978SDwayne Grant McConnell 	return ctx->ops->signal2_type_get(ctx);
982bf1ab978SDwayne Grant McConnell }
983bf1ab978SDwayne Grant McConnell 
98467207b96SArnd Bergmann static u64 spufs_signal2_type_get(void *data)
98567207b96SArnd Bergmann {
98667207b96SArnd Bergmann 	struct spu_context *ctx = data;
9878b3d6663SArnd Bergmann 	u64 ret;
9888b3d6663SArnd Bergmann 
9898b3d6663SArnd Bergmann 	spu_acquire(ctx);
990bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal2_type_get(data);
9918b3d6663SArnd Bergmann 	spu_release(ctx);
9928b3d6663SArnd Bergmann 
9938b3d6663SArnd Bergmann 	return ret;
99467207b96SArnd Bergmann }
99567207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
99667207b96SArnd Bergmann 					spufs_signal2_type_set, "%llu");
99767207b96SArnd Bergmann 
99827d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
99978bde53eSBenjamin Herrenschmidt static unsigned long spufs_mss_mmap_nopfn(struct vm_area_struct *vma,
100078bde53eSBenjamin Herrenschmidt 					  unsigned long address)
1001d9379c4bSarnd@arndb.de {
100278bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x0000, 0x1000);
1003d9379c4bSarnd@arndb.de }
1004d9379c4bSarnd@arndb.de 
1005d9379c4bSarnd@arndb.de static struct vm_operations_struct spufs_mss_mmap_vmops = {
100678bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mss_mmap_nopfn,
1007d9379c4bSarnd@arndb.de };
1008d9379c4bSarnd@arndb.de 
1009d9379c4bSarnd@arndb.de /*
1010d9379c4bSarnd@arndb.de  * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
1011d9379c4bSarnd@arndb.de  */
1012d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma)
1013d9379c4bSarnd@arndb.de {
1014d9379c4bSarnd@arndb.de 	if (!(vma->vm_flags & VM_SHARED))
1015d9379c4bSarnd@arndb.de 		return -EINVAL;
1016d9379c4bSarnd@arndb.de 
101778bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
1018d9379c4bSarnd@arndb.de 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
101923cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
1020d9379c4bSarnd@arndb.de 
1021d9379c4bSarnd@arndb.de 	vma->vm_ops = &spufs_mss_mmap_vmops;
1022d9379c4bSarnd@arndb.de 	return 0;
1023d9379c4bSarnd@arndb.de }
102427d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
102527d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL
102627d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
1027d9379c4bSarnd@arndb.de 
1028d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file)
1029d9379c4bSarnd@arndb.de {
1030d9379c4bSarnd@arndb.de 	struct spufs_inode_info *i = SPUFS_I(inode);
103117e0e270SBenjamin Herrenschmidt 	struct spu_context *ctx = i->i_ctx;
1032d9379c4bSarnd@arndb.de 
1033d9379c4bSarnd@arndb.de 	file->private_data = i->i_ctx;
103417e0e270SBenjamin Herrenschmidt 	ctx->mss = inode->i_mapping;
103517e0e270SBenjamin Herrenschmidt 	smp_wmb();
1036d9379c4bSarnd@arndb.de 	return nonseekable_open(inode, file);
1037d9379c4bSarnd@arndb.de }
1038d9379c4bSarnd@arndb.de 
10395dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = {
1040d9379c4bSarnd@arndb.de 	.open	 = spufs_mss_open,
1041d9379c4bSarnd@arndb.de 	.mmap	 = spufs_mss_mmap,
104227d5bf2aSBenjamin Herrenschmidt };
104327d5bf2aSBenjamin Herrenschmidt 
104478bde53eSBenjamin Herrenschmidt static unsigned long spufs_psmap_mmap_nopfn(struct vm_area_struct *vma,
104578bde53eSBenjamin Herrenschmidt 					    unsigned long address)
104627d5bf2aSBenjamin Herrenschmidt {
104778bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x0000, 0x20000);
104827d5bf2aSBenjamin Herrenschmidt }
104927d5bf2aSBenjamin Herrenschmidt 
105027d5bf2aSBenjamin Herrenschmidt static struct vm_operations_struct spufs_psmap_mmap_vmops = {
105178bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_psmap_mmap_nopfn,
105227d5bf2aSBenjamin Herrenschmidt };
105327d5bf2aSBenjamin Herrenschmidt 
105427d5bf2aSBenjamin Herrenschmidt /*
105527d5bf2aSBenjamin Herrenschmidt  * mmap support for full problem state area [0x00000 - 0x1ffff].
105627d5bf2aSBenjamin Herrenschmidt  */
105727d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
105827d5bf2aSBenjamin Herrenschmidt {
105927d5bf2aSBenjamin Herrenschmidt 	if (!(vma->vm_flags & VM_SHARED))
106027d5bf2aSBenjamin Herrenschmidt 		return -EINVAL;
106127d5bf2aSBenjamin Herrenschmidt 
106278bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
106327d5bf2aSBenjamin Herrenschmidt 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
106427d5bf2aSBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
106527d5bf2aSBenjamin Herrenschmidt 
106627d5bf2aSBenjamin Herrenschmidt 	vma->vm_ops = &spufs_psmap_mmap_vmops;
106727d5bf2aSBenjamin Herrenschmidt 	return 0;
106827d5bf2aSBenjamin Herrenschmidt }
106927d5bf2aSBenjamin Herrenschmidt 
107027d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file)
107127d5bf2aSBenjamin Herrenschmidt {
107227d5bf2aSBenjamin Herrenschmidt 	struct spufs_inode_info *i = SPUFS_I(inode);
107317e0e270SBenjamin Herrenschmidt 	struct spu_context *ctx = i->i_ctx;
107427d5bf2aSBenjamin Herrenschmidt 
107527d5bf2aSBenjamin Herrenschmidt 	file->private_data = i->i_ctx;
107617e0e270SBenjamin Herrenschmidt 	ctx->psmap = inode->i_mapping;
107717e0e270SBenjamin Herrenschmidt 	smp_wmb();
107827d5bf2aSBenjamin Herrenschmidt 	return nonseekable_open(inode, file);
107927d5bf2aSBenjamin Herrenschmidt }
108027d5bf2aSBenjamin Herrenschmidt 
10815dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = {
108227d5bf2aSBenjamin Herrenschmidt 	.open	 = spufs_psmap_open,
108327d5bf2aSBenjamin Herrenschmidt 	.mmap	 = spufs_psmap_mmap,
1084d9379c4bSarnd@arndb.de };
1085d9379c4bSarnd@arndb.de 
1086d9379c4bSarnd@arndb.de 
108727d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
108878bde53eSBenjamin Herrenschmidt static unsigned long spufs_mfc_mmap_nopfn(struct vm_area_struct *vma,
108978bde53eSBenjamin Herrenschmidt 					  unsigned long address)
10906df10a82SMark Nutter {
109178bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x3000, 0x1000);
10926df10a82SMark Nutter }
10936df10a82SMark Nutter 
10946df10a82SMark Nutter static struct vm_operations_struct spufs_mfc_mmap_vmops = {
109578bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mfc_mmap_nopfn,
10966df10a82SMark Nutter };
10976df10a82SMark Nutter 
10986df10a82SMark Nutter /*
10996df10a82SMark Nutter  * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
11006df10a82SMark Nutter  */
11016df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
11026df10a82SMark Nutter {
11036df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
11046df10a82SMark Nutter 		return -EINVAL;
11056df10a82SMark Nutter 
110678bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
11076df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
110823cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
11096df10a82SMark Nutter 
11106df10a82SMark Nutter 	vma->vm_ops = &spufs_mfc_mmap_vmops;
11116df10a82SMark Nutter 	return 0;
11126df10a82SMark Nutter }
111327d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
111427d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL
111527d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
1116a33a7d73SArnd Bergmann 
1117a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file)
1118a33a7d73SArnd Bergmann {
1119a33a7d73SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
1120a33a7d73SArnd Bergmann 	struct spu_context *ctx = i->i_ctx;
1121a33a7d73SArnd Bergmann 
1122a33a7d73SArnd Bergmann 	/* we don't want to deal with DMA into other processes */
1123a33a7d73SArnd Bergmann 	if (ctx->owner != current->mm)
1124a33a7d73SArnd Bergmann 		return -EINVAL;
1125a33a7d73SArnd Bergmann 
1126a33a7d73SArnd Bergmann 	if (atomic_read(&inode->i_count) != 1)
1127a33a7d73SArnd Bergmann 		return -EBUSY;
1128a33a7d73SArnd Bergmann 
1129a33a7d73SArnd Bergmann 	file->private_data = ctx;
113017e0e270SBenjamin Herrenschmidt 	ctx->mfc = inode->i_mapping;
113117e0e270SBenjamin Herrenschmidt 	smp_wmb();
1132a33a7d73SArnd Bergmann 	return nonseekable_open(inode, file);
1133a33a7d73SArnd Bergmann }
1134a33a7d73SArnd Bergmann 
1135a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */
1136a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu)
1137a33a7d73SArnd Bergmann {
1138a33a7d73SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
1139a33a7d73SArnd Bergmann 
1140a33a7d73SArnd Bergmann 	wake_up_all(&ctx->mfc_wq);
1141a33a7d73SArnd Bergmann 
1142a33a7d73SArnd Bergmann 	pr_debug("%s %s\n", __FUNCTION__, spu->name);
1143a33a7d73SArnd Bergmann 	if (ctx->mfc_fasync) {
1144a33a7d73SArnd Bergmann 		u32 free_elements, tagstatus;
1145a33a7d73SArnd Bergmann 		unsigned int mask;
1146a33a7d73SArnd Bergmann 
1147a33a7d73SArnd Bergmann 		/* no need for spu_acquire in interrupt context */
1148a33a7d73SArnd Bergmann 		free_elements = ctx->ops->get_mfc_free_elements(ctx);
1149a33a7d73SArnd Bergmann 		tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1150a33a7d73SArnd Bergmann 
1151a33a7d73SArnd Bergmann 		mask = 0;
1152a33a7d73SArnd Bergmann 		if (free_elements & 0xffff)
1153a33a7d73SArnd Bergmann 			mask |= POLLOUT;
1154a33a7d73SArnd Bergmann 		if (tagstatus & ctx->tagwait)
1155a33a7d73SArnd Bergmann 			mask |= POLLIN;
1156a33a7d73SArnd Bergmann 
1157a33a7d73SArnd Bergmann 		kill_fasync(&ctx->mfc_fasync, SIGIO, mask);
1158a33a7d73SArnd Bergmann 	}
1159a33a7d73SArnd Bergmann }
1160a33a7d73SArnd Bergmann 
1161a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status)
1162a33a7d73SArnd Bergmann {
1163a33a7d73SArnd Bergmann 	/* See if there is one tag group is complete */
1164a33a7d73SArnd Bergmann 	/* FIXME we need locking around tagwait */
1165a33a7d73SArnd Bergmann 	*status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait;
1166a33a7d73SArnd Bergmann 	ctx->tagwait &= ~*status;
1167a33a7d73SArnd Bergmann 	if (*status)
1168a33a7d73SArnd Bergmann 		return 1;
1169a33a7d73SArnd Bergmann 
1170a33a7d73SArnd Bergmann 	/* enable interrupt waiting for any tag group,
1171a33a7d73SArnd Bergmann 	   may silently fail if interrupts are already enabled */
1172a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1173a33a7d73SArnd Bergmann 	return 0;
1174a33a7d73SArnd Bergmann }
1175a33a7d73SArnd Bergmann 
1176a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer,
1177a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1178a33a7d73SArnd Bergmann {
1179a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1180a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1181a33a7d73SArnd Bergmann 	u32 status;
1182a33a7d73SArnd Bergmann 
1183a33a7d73SArnd Bergmann 	if (size != 4)
1184a33a7d73SArnd Bergmann 		goto out;
1185a33a7d73SArnd Bergmann 
1186a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1187a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1188a33a7d73SArnd Bergmann 		status = ctx->ops->read_mfc_tagstatus(ctx);
1189a33a7d73SArnd Bergmann 		if (!(status & ctx->tagwait))
1190a33a7d73SArnd Bergmann 			ret = -EAGAIN;
1191a33a7d73SArnd Bergmann 		else
1192a33a7d73SArnd Bergmann 			ctx->tagwait &= ~status;
1193a33a7d73SArnd Bergmann 	} else {
1194a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1195a33a7d73SArnd Bergmann 			   spufs_read_mfc_tagstatus(ctx, &status));
1196a33a7d73SArnd Bergmann 	}
1197a33a7d73SArnd Bergmann 	spu_release(ctx);
1198a33a7d73SArnd Bergmann 
1199a33a7d73SArnd Bergmann 	if (ret)
1200a33a7d73SArnd Bergmann 		goto out;
1201a33a7d73SArnd Bergmann 
1202a33a7d73SArnd Bergmann 	ret = 4;
1203a33a7d73SArnd Bergmann 	if (copy_to_user(buffer, &status, 4))
1204a33a7d73SArnd Bergmann 		ret = -EFAULT;
1205a33a7d73SArnd Bergmann 
1206a33a7d73SArnd Bergmann out:
1207a33a7d73SArnd Bergmann 	return ret;
1208a33a7d73SArnd Bergmann }
1209a33a7d73SArnd Bergmann 
1210a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd)
1211a33a7d73SArnd Bergmann {
1212a33a7d73SArnd Bergmann 	pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa,
1213a33a7d73SArnd Bergmann 		 cmd->ea, cmd->size, cmd->tag, cmd->cmd);
1214a33a7d73SArnd Bergmann 
1215a33a7d73SArnd Bergmann 	switch (cmd->cmd) {
1216a33a7d73SArnd Bergmann 	case MFC_PUT_CMD:
1217a33a7d73SArnd Bergmann 	case MFC_PUTF_CMD:
1218a33a7d73SArnd Bergmann 	case MFC_PUTB_CMD:
1219a33a7d73SArnd Bergmann 	case MFC_GET_CMD:
1220a33a7d73SArnd Bergmann 	case MFC_GETF_CMD:
1221a33a7d73SArnd Bergmann 	case MFC_GETB_CMD:
1222a33a7d73SArnd Bergmann 		break;
1223a33a7d73SArnd Bergmann 	default:
1224a33a7d73SArnd Bergmann 		pr_debug("invalid DMA opcode %x\n", cmd->cmd);
1225a33a7d73SArnd Bergmann 		return -EIO;
1226a33a7d73SArnd Bergmann 	}
1227a33a7d73SArnd Bergmann 
1228a33a7d73SArnd Bergmann 	if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) {
1229a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment, ea %lx lsa %x\n",
1230a33a7d73SArnd Bergmann 				cmd->ea, cmd->lsa);
1231a33a7d73SArnd Bergmann 		return -EIO;
1232a33a7d73SArnd Bergmann 	}
1233a33a7d73SArnd Bergmann 
1234a33a7d73SArnd Bergmann 	switch (cmd->size & 0xf) {
1235a33a7d73SArnd Bergmann 	case 1:
1236a33a7d73SArnd Bergmann 		break;
1237a33a7d73SArnd Bergmann 	case 2:
1238a33a7d73SArnd Bergmann 		if (cmd->lsa & 1)
1239a33a7d73SArnd Bergmann 			goto error;
1240a33a7d73SArnd Bergmann 		break;
1241a33a7d73SArnd Bergmann 	case 4:
1242a33a7d73SArnd Bergmann 		if (cmd->lsa & 3)
1243a33a7d73SArnd Bergmann 			goto error;
1244a33a7d73SArnd Bergmann 		break;
1245a33a7d73SArnd Bergmann 	case 8:
1246a33a7d73SArnd Bergmann 		if (cmd->lsa & 7)
1247a33a7d73SArnd Bergmann 			goto error;
1248a33a7d73SArnd Bergmann 		break;
1249a33a7d73SArnd Bergmann 	case 0:
1250a33a7d73SArnd Bergmann 		if (cmd->lsa & 15)
1251a33a7d73SArnd Bergmann 			goto error;
1252a33a7d73SArnd Bergmann 		break;
1253a33a7d73SArnd Bergmann 	error:
1254a33a7d73SArnd Bergmann 	default:
1255a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment %x for size %x\n",
1256a33a7d73SArnd Bergmann 			cmd->lsa & 0xf, cmd->size);
1257a33a7d73SArnd Bergmann 		return -EIO;
1258a33a7d73SArnd Bergmann 	}
1259a33a7d73SArnd Bergmann 
1260a33a7d73SArnd Bergmann 	if (cmd->size > 16 * 1024) {
1261a33a7d73SArnd Bergmann 		pr_debug("invalid DMA size %x\n", cmd->size);
1262a33a7d73SArnd Bergmann 		return -EIO;
1263a33a7d73SArnd Bergmann 	}
1264a33a7d73SArnd Bergmann 
1265a33a7d73SArnd Bergmann 	if (cmd->tag & 0xfff0) {
1266a33a7d73SArnd Bergmann 		/* we reserve the higher tag numbers for kernel use */
1267a33a7d73SArnd Bergmann 		pr_debug("invalid DMA tag\n");
1268a33a7d73SArnd Bergmann 		return -EIO;
1269a33a7d73SArnd Bergmann 	}
1270a33a7d73SArnd Bergmann 
1271a33a7d73SArnd Bergmann 	if (cmd->class) {
1272a33a7d73SArnd Bergmann 		/* not supported in this version */
1273a33a7d73SArnd Bergmann 		pr_debug("invalid DMA class\n");
1274a33a7d73SArnd Bergmann 		return -EIO;
1275a33a7d73SArnd Bergmann 	}
1276a33a7d73SArnd Bergmann 
1277a33a7d73SArnd Bergmann 	return 0;
1278a33a7d73SArnd Bergmann }
1279a33a7d73SArnd Bergmann 
1280a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx,
1281a33a7d73SArnd Bergmann 				struct mfc_dma_command cmd,
1282a33a7d73SArnd Bergmann 				int *error)
1283a33a7d73SArnd Bergmann {
1284a33a7d73SArnd Bergmann 	*error = ctx->ops->send_mfc_command(ctx, &cmd);
1285a33a7d73SArnd Bergmann 	if (*error == -EAGAIN) {
1286a33a7d73SArnd Bergmann 		/* wait for any tag group to complete
1287a33a7d73SArnd Bergmann 		   so we have space for the new command */
1288a33a7d73SArnd Bergmann 		ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1289a33a7d73SArnd Bergmann 		/* try again, because the queue might be
1290a33a7d73SArnd Bergmann 		   empty again */
1291a33a7d73SArnd Bergmann 		*error = ctx->ops->send_mfc_command(ctx, &cmd);
1292a33a7d73SArnd Bergmann 		if (*error == -EAGAIN)
1293a33a7d73SArnd Bergmann 			return 0;
1294a33a7d73SArnd Bergmann 	}
1295a33a7d73SArnd Bergmann 	return 1;
1296a33a7d73SArnd Bergmann }
1297a33a7d73SArnd Bergmann 
1298a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer,
1299a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1300a33a7d73SArnd Bergmann {
1301a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1302a33a7d73SArnd Bergmann 	struct mfc_dma_command cmd;
1303a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1304a33a7d73SArnd Bergmann 
1305a33a7d73SArnd Bergmann 	if (size != sizeof cmd)
1306a33a7d73SArnd Bergmann 		goto out;
1307a33a7d73SArnd Bergmann 
1308a33a7d73SArnd Bergmann 	ret = -EFAULT;
1309a33a7d73SArnd Bergmann 	if (copy_from_user(&cmd, buffer, sizeof cmd))
1310a33a7d73SArnd Bergmann 		goto out;
1311a33a7d73SArnd Bergmann 
1312a33a7d73SArnd Bergmann 	ret = spufs_check_valid_dma(&cmd);
1313a33a7d73SArnd Bergmann 	if (ret)
1314a33a7d73SArnd Bergmann 		goto out;
1315a33a7d73SArnd Bergmann 
131626bec673SChristoph Hellwig 	spu_acquire_runnable(ctx, 0);
1317a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1318a33a7d73SArnd Bergmann 		ret = ctx->ops->send_mfc_command(ctx, &cmd);
1319a33a7d73SArnd Bergmann 	} else {
1320a33a7d73SArnd Bergmann 		int status;
1321a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1322a33a7d73SArnd Bergmann 				 spu_send_mfc_command(ctx, cmd, &status));
1323a33a7d73SArnd Bergmann 		if (status)
1324a33a7d73SArnd Bergmann 			ret = status;
1325a33a7d73SArnd Bergmann 	}
1326a33a7d73SArnd Bergmann 	spu_release(ctx);
1327a33a7d73SArnd Bergmann 
1328a33a7d73SArnd Bergmann 	if (ret)
1329a33a7d73SArnd Bergmann 		goto out;
1330a33a7d73SArnd Bergmann 
1331a33a7d73SArnd Bergmann 	ctx->tagwait |= 1 << cmd.tag;
13323692dc66SMasato Noguchi 	ret = size;
1333a33a7d73SArnd Bergmann 
1334a33a7d73SArnd Bergmann out:
1335a33a7d73SArnd Bergmann 	return ret;
1336a33a7d73SArnd Bergmann }
1337a33a7d73SArnd Bergmann 
1338a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait)
1339a33a7d73SArnd Bergmann {
1340a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1341a33a7d73SArnd Bergmann 	u32 free_elements, tagstatus;
1342a33a7d73SArnd Bergmann 	unsigned int mask;
1343a33a7d73SArnd Bergmann 
1344a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1345a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2);
1346a33a7d73SArnd Bergmann 	free_elements = ctx->ops->get_mfc_free_elements(ctx);
1347a33a7d73SArnd Bergmann 	tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1348a33a7d73SArnd Bergmann 	spu_release(ctx);
1349a33a7d73SArnd Bergmann 
1350a33a7d73SArnd Bergmann 	poll_wait(file, &ctx->mfc_wq, wait);
1351a33a7d73SArnd Bergmann 
1352a33a7d73SArnd Bergmann 	mask = 0;
1353a33a7d73SArnd Bergmann 	if (free_elements & 0xffff)
1354a33a7d73SArnd Bergmann 		mask |= POLLOUT | POLLWRNORM;
1355a33a7d73SArnd Bergmann 	if (tagstatus & ctx->tagwait)
1356a33a7d73SArnd Bergmann 		mask |= POLLIN | POLLRDNORM;
1357a33a7d73SArnd Bergmann 
1358a33a7d73SArnd Bergmann 	pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__,
1359a33a7d73SArnd Bergmann 		free_elements, tagstatus, ctx->tagwait);
1360a33a7d73SArnd Bergmann 
1361a33a7d73SArnd Bergmann 	return mask;
1362a33a7d73SArnd Bergmann }
1363a33a7d73SArnd Bergmann 
136473b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id)
1365a33a7d73SArnd Bergmann {
1366a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1367a33a7d73SArnd Bergmann 	int ret;
1368a33a7d73SArnd Bergmann 
1369a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1370a33a7d73SArnd Bergmann #if 0
1371a33a7d73SArnd Bergmann /* this currently hangs */
1372a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1373a33a7d73SArnd Bergmann 			 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2));
1374a33a7d73SArnd Bergmann 	if (ret)
1375a33a7d73SArnd Bergmann 		goto out;
1376a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1377a33a7d73SArnd Bergmann 			 ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait);
1378a33a7d73SArnd Bergmann out:
1379a33a7d73SArnd Bergmann #else
1380a33a7d73SArnd Bergmann 	ret = 0;
1381a33a7d73SArnd Bergmann #endif
1382a33a7d73SArnd Bergmann 	spu_release(ctx);
1383a33a7d73SArnd Bergmann 
1384a33a7d73SArnd Bergmann 	return ret;
1385a33a7d73SArnd Bergmann }
1386a33a7d73SArnd Bergmann 
1387a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry,
1388a33a7d73SArnd Bergmann 			   int datasync)
1389a33a7d73SArnd Bergmann {
139073b6af8aSAl Viro 	return spufs_mfc_flush(file, NULL);
1391a33a7d73SArnd Bergmann }
1392a33a7d73SArnd Bergmann 
1393a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on)
1394a33a7d73SArnd Bergmann {
1395a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1396a33a7d73SArnd Bergmann 
1397a33a7d73SArnd Bergmann 	return fasync_helper(fd, file, on, &ctx->mfc_fasync);
1398a33a7d73SArnd Bergmann }
1399a33a7d73SArnd Bergmann 
14005dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = {
1401a33a7d73SArnd Bergmann 	.open	 = spufs_mfc_open,
1402a33a7d73SArnd Bergmann 	.read	 = spufs_mfc_read,
1403a33a7d73SArnd Bergmann 	.write	 = spufs_mfc_write,
1404a33a7d73SArnd Bergmann 	.poll	 = spufs_mfc_poll,
1405a33a7d73SArnd Bergmann 	.flush	 = spufs_mfc_flush,
1406a33a7d73SArnd Bergmann 	.fsync	 = spufs_mfc_fsync,
1407a33a7d73SArnd Bergmann 	.fasync	 = spufs_mfc_fasync,
14086df10a82SMark Nutter 	.mmap	 = spufs_mfc_mmap,
1409a33a7d73SArnd Bergmann };
1410a33a7d73SArnd Bergmann 
141167207b96SArnd Bergmann static void spufs_npc_set(void *data, u64 val)
141267207b96SArnd Bergmann {
141367207b96SArnd Bergmann 	struct spu_context *ctx = data;
14148b3d6663SArnd Bergmann 	spu_acquire(ctx);
14158b3d6663SArnd Bergmann 	ctx->ops->npc_write(ctx, val);
14168b3d6663SArnd Bergmann 	spu_release(ctx);
141767207b96SArnd Bergmann }
141867207b96SArnd Bergmann 
141967207b96SArnd Bergmann static u64 spufs_npc_get(void *data)
142067207b96SArnd Bergmann {
142167207b96SArnd Bergmann 	struct spu_context *ctx = data;
142267207b96SArnd Bergmann 	u64 ret;
14238b3d6663SArnd Bergmann 	spu_acquire(ctx);
14248b3d6663SArnd Bergmann 	ret = ctx->ops->npc_read(ctx);
14258b3d6663SArnd Bergmann 	spu_release(ctx);
142667207b96SArnd Bergmann 	return ret;
142767207b96SArnd Bergmann }
14289b5047e2SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set,
14299b5047e2SDwayne Grant McConnell 			"0x%llx\n")
143067207b96SArnd Bergmann 
14318b3d6663SArnd Bergmann static void spufs_decr_set(void *data, u64 val)
14328b3d6663SArnd Bergmann {
14338b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14348b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14358b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14368b3d6663SArnd Bergmann 	lscsa->decr.slot[0] = (u32) val;
14378b3d6663SArnd Bergmann 	spu_release(ctx);
14388b3d6663SArnd Bergmann }
14398b3d6663SArnd Bergmann 
1440bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_get(void *data)
14418b3d6663SArnd Bergmann {
14428b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14438b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1444bf1ab978SDwayne Grant McConnell 	return lscsa->decr.slot[0];
1445bf1ab978SDwayne Grant McConnell }
1446bf1ab978SDwayne Grant McConnell 
1447bf1ab978SDwayne Grant McConnell static u64 spufs_decr_get(void *data)
1448bf1ab978SDwayne Grant McConnell {
1449bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14508b3d6663SArnd Bergmann 	u64 ret;
14518b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1452bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_get(data);
14538b3d6663SArnd Bergmann 	spu_release(ctx);
14548b3d6663SArnd Bergmann 	return ret;
14558b3d6663SArnd Bergmann }
14568b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set,
14579b5047e2SDwayne Grant McConnell 			"0x%llx\n")
14588b3d6663SArnd Bergmann 
14598b3d6663SArnd Bergmann static void spufs_decr_status_set(void *data, u64 val)
14608b3d6663SArnd Bergmann {
14618b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14628b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14638b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14648b3d6663SArnd Bergmann 	lscsa->decr_status.slot[0] = (u32) val;
14658b3d6663SArnd Bergmann 	spu_release(ctx);
14668b3d6663SArnd Bergmann }
14678b3d6663SArnd Bergmann 
1468bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_status_get(void *data)
14698b3d6663SArnd Bergmann {
14708b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14718b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1472bf1ab978SDwayne Grant McConnell 	return lscsa->decr_status.slot[0];
1473bf1ab978SDwayne Grant McConnell }
1474bf1ab978SDwayne Grant McConnell 
1475bf1ab978SDwayne Grant McConnell static u64 spufs_decr_status_get(void *data)
1476bf1ab978SDwayne Grant McConnell {
1477bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14788b3d6663SArnd Bergmann 	u64 ret;
14798b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1480bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_status_get(data);
14818b3d6663SArnd Bergmann 	spu_release(ctx);
14828b3d6663SArnd Bergmann 	return ret;
14838b3d6663SArnd Bergmann }
14848b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get,
14859b5047e2SDwayne Grant McConnell 			spufs_decr_status_set, "0x%llx\n")
14868b3d6663SArnd Bergmann 
14878b3d6663SArnd Bergmann static void spufs_event_mask_set(void *data, u64 val)
14888b3d6663SArnd Bergmann {
14898b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14908b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14918b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14928b3d6663SArnd Bergmann 	lscsa->event_mask.slot[0] = (u32) val;
14938b3d6663SArnd Bergmann 	spu_release(ctx);
14948b3d6663SArnd Bergmann }
14958b3d6663SArnd Bergmann 
1496bf1ab978SDwayne Grant McConnell static u64 __spufs_event_mask_get(void *data)
14978b3d6663SArnd Bergmann {
14988b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14998b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1500bf1ab978SDwayne Grant McConnell 	return lscsa->event_mask.slot[0];
1501bf1ab978SDwayne Grant McConnell }
1502bf1ab978SDwayne Grant McConnell 
1503bf1ab978SDwayne Grant McConnell static u64 spufs_event_mask_get(void *data)
1504bf1ab978SDwayne Grant McConnell {
1505bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
15068b3d6663SArnd Bergmann 	u64 ret;
15078b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1508bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_mask_get(data);
15098b3d6663SArnd Bergmann 	spu_release(ctx);
15108b3d6663SArnd Bergmann 	return ret;
15118b3d6663SArnd Bergmann }
15128b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get,
15139b5047e2SDwayne Grant McConnell 			spufs_event_mask_set, "0x%llx\n")
15148b3d6663SArnd Bergmann 
1515bf1ab978SDwayne Grant McConnell static u64 __spufs_event_status_get(void *data)
1516b9e3bd77SDwayne Grant McConnell {
1517b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1518b9e3bd77SDwayne Grant McConnell 	struct spu_state *state = &ctx->csa;
1519b9e3bd77SDwayne Grant McConnell 	u64 stat;
1520b9e3bd77SDwayne Grant McConnell 	stat = state->spu_chnlcnt_RW[0];
1521b9e3bd77SDwayne Grant McConnell 	if (stat)
1522bf1ab978SDwayne Grant McConnell 		return state->spu_chnldata_RW[0];
1523bf1ab978SDwayne Grant McConnell 	return 0;
1524bf1ab978SDwayne Grant McConnell }
1525bf1ab978SDwayne Grant McConnell 
1526bf1ab978SDwayne Grant McConnell static u64 spufs_event_status_get(void *data)
1527bf1ab978SDwayne Grant McConnell {
1528bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1529bf1ab978SDwayne Grant McConnell 	u64 ret = 0;
1530bf1ab978SDwayne Grant McConnell 
1531bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1532bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_status_get(data);
1533b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1534b9e3bd77SDwayne Grant McConnell 	return ret;
1535b9e3bd77SDwayne Grant McConnell }
1536b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get,
1537b9e3bd77SDwayne Grant McConnell 			NULL, "0x%llx\n")
1538b9e3bd77SDwayne Grant McConnell 
15398b3d6663SArnd Bergmann static void spufs_srr0_set(void *data, u64 val)
15408b3d6663SArnd Bergmann {
15418b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15428b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15438b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15448b3d6663SArnd Bergmann 	lscsa->srr0.slot[0] = (u32) val;
15458b3d6663SArnd Bergmann 	spu_release(ctx);
15468b3d6663SArnd Bergmann }
15478b3d6663SArnd Bergmann 
15488b3d6663SArnd Bergmann static u64 spufs_srr0_get(void *data)
15498b3d6663SArnd Bergmann {
15508b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15518b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15528b3d6663SArnd Bergmann 	u64 ret;
15538b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15548b3d6663SArnd Bergmann 	ret = lscsa->srr0.slot[0];
15558b3d6663SArnd Bergmann 	spu_release(ctx);
15568b3d6663SArnd Bergmann 	return ret;
15578b3d6663SArnd Bergmann }
15588b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
15599b5047e2SDwayne Grant McConnell 			"0x%llx\n")
15608b3d6663SArnd Bergmann 
15617b1a7014Sarnd@arndb.de static u64 spufs_id_get(void *data)
15627b1a7014Sarnd@arndb.de {
15637b1a7014Sarnd@arndb.de 	struct spu_context *ctx = data;
15647b1a7014Sarnd@arndb.de 	u64 num;
15657b1a7014Sarnd@arndb.de 
15667b1a7014Sarnd@arndb.de 	spu_acquire(ctx);
15677b1a7014Sarnd@arndb.de 	if (ctx->state == SPU_STATE_RUNNABLE)
15687b1a7014Sarnd@arndb.de 		num = ctx->spu->number;
15697b1a7014Sarnd@arndb.de 	else
15707b1a7014Sarnd@arndb.de 		num = (unsigned int)-1;
15717b1a7014Sarnd@arndb.de 	spu_release(ctx);
15727b1a7014Sarnd@arndb.de 
15737b1a7014Sarnd@arndb.de 	return num;
15747b1a7014Sarnd@arndb.de }
1575e45d6634SAl Viro DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n")
15767b1a7014Sarnd@arndb.de 
1577bf1ab978SDwayne Grant McConnell static u64 __spufs_object_id_get(void *data)
157886767277SArnd Bergmann {
157986767277SArnd Bergmann 	struct spu_context *ctx = data;
158086767277SArnd Bergmann 	return ctx->object_id;
158186767277SArnd Bergmann }
158286767277SArnd Bergmann 
1583bf1ab978SDwayne Grant McConnell static u64 spufs_object_id_get(void *data)
1584bf1ab978SDwayne Grant McConnell {
1585bf1ab978SDwayne Grant McConnell 	/* FIXME: Should there really be no locking here? */
1586bf1ab978SDwayne Grant McConnell 	return __spufs_object_id_get(data);
1587bf1ab978SDwayne Grant McConnell }
1588bf1ab978SDwayne Grant McConnell 
158986767277SArnd Bergmann static void spufs_object_id_set(void *data, u64 id)
159086767277SArnd Bergmann {
159186767277SArnd Bergmann 	struct spu_context *ctx = data;
159286767277SArnd Bergmann 	ctx->object_id = id;
159386767277SArnd Bergmann }
159486767277SArnd Bergmann 
159586767277SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get,
159686767277SArnd Bergmann 		spufs_object_id_set, "0x%llx\n");
159786767277SArnd Bergmann 
1598bf1ab978SDwayne Grant McConnell static u64 __spufs_lslr_get(void *data)
1599bf1ab978SDwayne Grant McConnell {
1600bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1601bf1ab978SDwayne Grant McConnell 	return ctx->csa.priv2.spu_lslr_RW;
1602bf1ab978SDwayne Grant McConnell }
1603bf1ab978SDwayne Grant McConnell 
1604b9e3bd77SDwayne Grant McConnell static u64 spufs_lslr_get(void *data)
1605b9e3bd77SDwayne Grant McConnell {
1606b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1607b9e3bd77SDwayne Grant McConnell 	u64 ret;
1608b9e3bd77SDwayne Grant McConnell 
1609b9e3bd77SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1610bf1ab978SDwayne Grant McConnell 	ret = __spufs_lslr_get(data);
1611b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1612b9e3bd77SDwayne Grant McConnell 
1613b9e3bd77SDwayne Grant McConnell 	return ret;
1614b9e3bd77SDwayne Grant McConnell }
1615b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n")
1616b9e3bd77SDwayne Grant McConnell 
1617b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file)
1618b9e3bd77SDwayne Grant McConnell {
1619b9e3bd77SDwayne Grant McConnell 	struct spufs_inode_info *i = SPUFS_I(inode);
1620b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = i->i_ctx;
1621b9e3bd77SDwayne Grant McConnell 	file->private_data = ctx;
1622b9e3bd77SDwayne Grant McConnell 	return 0;
1623b9e3bd77SDwayne Grant McConnell }
1624b9e3bd77SDwayne Grant McConnell 
1625bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx,
1626bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1627bf1ab978SDwayne Grant McConnell {
1628bf1ab978SDwayne Grant McConnell 	u32 mbox_stat;
1629bf1ab978SDwayne Grant McConnell 	u32 data;
1630bf1ab978SDwayne Grant McConnell 
1631bf1ab978SDwayne Grant McConnell 	mbox_stat = ctx->csa.prob.mb_stat_R;
1632bf1ab978SDwayne Grant McConnell 	if (mbox_stat & 0x0000ff) {
1633bf1ab978SDwayne Grant McConnell 		data = ctx->csa.prob.pu_mb_R;
1634bf1ab978SDwayne Grant McConnell 	}
1635bf1ab978SDwayne Grant McConnell 
1636bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1637bf1ab978SDwayne Grant McConnell }
1638bf1ab978SDwayne Grant McConnell 
163969a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
164069a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
164169a2f00cSDwayne Grant McConnell {
1642bf1ab978SDwayne Grant McConnell 	int ret;
164369a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
164469a2f00cSDwayne Grant McConnell 
164569a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
164669a2f00cSDwayne Grant McConnell 		return -EFAULT;
164769a2f00cSDwayne Grant McConnell 
164869a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
164969a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1650bf1ab978SDwayne Grant McConnell 	ret = __spufs_mbox_info_read(ctx, buf, len, pos);
165169a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
165269a2f00cSDwayne Grant McConnell 	spu_release(ctx);
165369a2f00cSDwayne Grant McConnell 
1654bf1ab978SDwayne Grant McConnell 	return ret;
165569a2f00cSDwayne Grant McConnell }
165669a2f00cSDwayne Grant McConnell 
16575dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = {
165869a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
165969a2f00cSDwayne Grant McConnell 	.read = spufs_mbox_info_read,
166069a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
166169a2f00cSDwayne Grant McConnell };
166269a2f00cSDwayne Grant McConnell 
1663bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx,
1664bf1ab978SDwayne Grant McConnell 				char __user *buf, size_t len, loff_t *pos)
1665bf1ab978SDwayne Grant McConnell {
1666bf1ab978SDwayne Grant McConnell 	u32 ibox_stat;
1667bf1ab978SDwayne Grant McConnell 	u32 data;
1668bf1ab978SDwayne Grant McConnell 
1669bf1ab978SDwayne Grant McConnell 	ibox_stat = ctx->csa.prob.mb_stat_R;
1670bf1ab978SDwayne Grant McConnell 	if (ibox_stat & 0xff0000) {
1671bf1ab978SDwayne Grant McConnell 		data = ctx->csa.priv2.puint_mb_R;
1672bf1ab978SDwayne Grant McConnell 	}
1673bf1ab978SDwayne Grant McConnell 
1674bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1675bf1ab978SDwayne Grant McConnell }
1676bf1ab978SDwayne Grant McConnell 
167769a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
167869a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
167969a2f00cSDwayne Grant McConnell {
168069a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1681bf1ab978SDwayne Grant McConnell 	int ret;
168269a2f00cSDwayne Grant McConnell 
168369a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
168469a2f00cSDwayne Grant McConnell 		return -EFAULT;
168569a2f00cSDwayne Grant McConnell 
168669a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
168769a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1688bf1ab978SDwayne Grant McConnell 	ret = __spufs_ibox_info_read(ctx, buf, len, pos);
168969a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
169069a2f00cSDwayne Grant McConnell 	spu_release(ctx);
169169a2f00cSDwayne Grant McConnell 
1692bf1ab978SDwayne Grant McConnell 	return ret;
169369a2f00cSDwayne Grant McConnell }
169469a2f00cSDwayne Grant McConnell 
16955dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = {
169669a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
169769a2f00cSDwayne Grant McConnell 	.read = spufs_ibox_info_read,
169869a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
169969a2f00cSDwayne Grant McConnell };
170069a2f00cSDwayne Grant McConnell 
1701bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx,
1702bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1703bf1ab978SDwayne Grant McConnell {
1704bf1ab978SDwayne Grant McConnell 	int i, cnt;
1705bf1ab978SDwayne Grant McConnell 	u32 data[4];
1706bf1ab978SDwayne Grant McConnell 	u32 wbox_stat;
1707bf1ab978SDwayne Grant McConnell 
1708bf1ab978SDwayne Grant McConnell 	wbox_stat = ctx->csa.prob.mb_stat_R;
1709bf1ab978SDwayne Grant McConnell 	cnt = 4 - ((wbox_stat & 0x00ff00) >> 8);
1710bf1ab978SDwayne Grant McConnell 	for (i = 0; i < cnt; i++) {
1711bf1ab978SDwayne Grant McConnell 		data[i] = ctx->csa.spu_mailbox_data[i];
1712bf1ab978SDwayne Grant McConnell 	}
1713bf1ab978SDwayne Grant McConnell 
1714bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data,
1715bf1ab978SDwayne Grant McConnell 				cnt * sizeof(u32));
1716bf1ab978SDwayne Grant McConnell }
1717bf1ab978SDwayne Grant McConnell 
171869a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
171969a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
172069a2f00cSDwayne Grant McConnell {
172169a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1722bf1ab978SDwayne Grant McConnell 	int ret;
172369a2f00cSDwayne Grant McConnell 
172469a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
172569a2f00cSDwayne Grant McConnell 		return -EFAULT;
172669a2f00cSDwayne Grant McConnell 
172769a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
172869a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1729bf1ab978SDwayne Grant McConnell 	ret = __spufs_wbox_info_read(ctx, buf, len, pos);
173069a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
173169a2f00cSDwayne Grant McConnell 	spu_release(ctx);
173269a2f00cSDwayne Grant McConnell 
1733bf1ab978SDwayne Grant McConnell 	return ret;
173469a2f00cSDwayne Grant McConnell }
173569a2f00cSDwayne Grant McConnell 
17365dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = {
173769a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
173869a2f00cSDwayne Grant McConnell 	.read = spufs_wbox_info_read,
173969a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
174069a2f00cSDwayne Grant McConnell };
174169a2f00cSDwayne Grant McConnell 
1742bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx,
1743bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1744b9e3bd77SDwayne Grant McConnell {
1745b9e3bd77SDwayne Grant McConnell 	struct spu_dma_info info;
1746b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *spuqp;
1747b9e3bd77SDwayne Grant McConnell 	int i;
1748b9e3bd77SDwayne Grant McConnell 
1749b9e3bd77SDwayne Grant McConnell 	info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
1750b9e3bd77SDwayne Grant McConnell 	info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
1751b9e3bd77SDwayne Grant McConnell 	info.dma_info_status = ctx->csa.spu_chnldata_RW[24];
1752b9e3bd77SDwayne Grant McConnell 	info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
1753b9e3bd77SDwayne Grant McConnell 	info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
1754b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 16; i++) {
1755b9e3bd77SDwayne Grant McConnell 		qp = &info.dma_info_command_data[i];
1756b9e3bd77SDwayne Grant McConnell 		spuqp = &ctx->csa.priv2.spuq[i];
1757b9e3bd77SDwayne Grant McConnell 
1758b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW;
1759b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW;
1760b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW;
1761b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW;
1762b9e3bd77SDwayne Grant McConnell 	}
1763b9e3bd77SDwayne Grant McConnell 
1764b9e3bd77SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1765b9e3bd77SDwayne Grant McConnell 				sizeof info);
1766b9e3bd77SDwayne Grant McConnell }
1767b9e3bd77SDwayne Grant McConnell 
1768bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
1769bf1ab978SDwayne Grant McConnell 			      size_t len, loff_t *pos)
1770bf1ab978SDwayne Grant McConnell {
1771bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1772bf1ab978SDwayne Grant McConnell 	int ret;
1773bf1ab978SDwayne Grant McConnell 
1774bf1ab978SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1775bf1ab978SDwayne Grant McConnell 		return -EFAULT;
1776bf1ab978SDwayne Grant McConnell 
1777bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1778bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1779bf1ab978SDwayne Grant McConnell 	ret = __spufs_dma_info_read(ctx, buf, len, pos);
1780bf1ab978SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1781bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
1782bf1ab978SDwayne Grant McConnell 
1783bf1ab978SDwayne Grant McConnell 	return ret;
1784bf1ab978SDwayne Grant McConnell }
1785bf1ab978SDwayne Grant McConnell 
17865dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = {
1787b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1788b9e3bd77SDwayne Grant McConnell 	.read = spufs_dma_info_read,
1789b9e3bd77SDwayne Grant McConnell };
1790b9e3bd77SDwayne Grant McConnell 
1791bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
1792bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1793b9e3bd77SDwayne Grant McConnell {
1794b9e3bd77SDwayne Grant McConnell 	struct spu_proxydma_info info;
1795b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *puqp;
1796bf1ab978SDwayne Grant McConnell 	int ret = sizeof info;
1797b9e3bd77SDwayne Grant McConnell 	int i;
1798b9e3bd77SDwayne Grant McConnell 
1799b9e3bd77SDwayne Grant McConnell 	if (len < ret)
1800b9e3bd77SDwayne Grant McConnell 		return -EINVAL;
1801b9e3bd77SDwayne Grant McConnell 
1802b9e3bd77SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1803b9e3bd77SDwayne Grant McConnell 		return -EFAULT;
1804b9e3bd77SDwayne Grant McConnell 
1805b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
1806b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
1807b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
1808b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 8; i++) {
1809b9e3bd77SDwayne Grant McConnell 		qp = &info.proxydma_info_command_data[i];
1810b9e3bd77SDwayne Grant McConnell 		puqp = &ctx->csa.priv2.puq[i];
1811b9e3bd77SDwayne Grant McConnell 
1812b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
1813b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
1814b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
1815b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
1816b9e3bd77SDwayne Grant McConnell 	}
1817bf1ab978SDwayne Grant McConnell 
1818bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1819bf1ab978SDwayne Grant McConnell 				sizeof info);
1820bf1ab978SDwayne Grant McConnell }
1821bf1ab978SDwayne Grant McConnell 
1822bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
1823bf1ab978SDwayne Grant McConnell 				   size_t len, loff_t *pos)
1824bf1ab978SDwayne Grant McConnell {
1825bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1826bf1ab978SDwayne Grant McConnell 	int ret;
1827bf1ab978SDwayne Grant McConnell 
1828bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1829bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1830bf1ab978SDwayne Grant McConnell 	ret = __spufs_proxydma_info_read(ctx, buf, len, pos);
1831b9e3bd77SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1832b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1833b9e3bd77SDwayne Grant McConnell 
1834b9e3bd77SDwayne Grant McConnell 	return ret;
1835b9e3bd77SDwayne Grant McConnell }
1836b9e3bd77SDwayne Grant McConnell 
18375dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = {
1838b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1839b9e3bd77SDwayne Grant McConnell 	.read = spufs_proxydma_info_read,
1840b9e3bd77SDwayne Grant McConnell };
1841b9e3bd77SDwayne Grant McConnell 
184267207b96SArnd Bergmann struct tree_descr spufs_dir_contents[] = {
184367207b96SArnd Bergmann 	{ "mem",  &spufs_mem_fops,  0666, },
18448b3d6663SArnd Bergmann 	{ "regs", &spufs_regs_fops,  0666, },
184567207b96SArnd Bergmann 	{ "mbox", &spufs_mbox_fops, 0444, },
184667207b96SArnd Bergmann 	{ "ibox", &spufs_ibox_fops, 0444, },
184767207b96SArnd Bergmann 	{ "wbox", &spufs_wbox_fops, 0222, },
184867207b96SArnd Bergmann 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
184967207b96SArnd Bergmann 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
185067207b96SArnd Bergmann 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
185167207b96SArnd Bergmann 	{ "signal1", &spufs_signal1_fops, 0666, },
185267207b96SArnd Bergmann 	{ "signal2", &spufs_signal2_fops, 0666, },
185367207b96SArnd Bergmann 	{ "signal1_type", &spufs_signal1_type, 0666, },
185467207b96SArnd Bergmann 	{ "signal2_type", &spufs_signal2_type, 0666, },
18556df10a82SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18568b3d6663SArnd Bergmann 	{ "fpcr", &spufs_fpcr_fops, 0666, },
1857b9e3bd77SDwayne Grant McConnell 	{ "lslr", &spufs_lslr_ops, 0444, },
1858b9e3bd77SDwayne Grant McConnell 	{ "mfc", &spufs_mfc_fops, 0666, },
1859b9e3bd77SDwayne Grant McConnell 	{ "mss", &spufs_mss_fops, 0666, },
1860b9e3bd77SDwayne Grant McConnell 	{ "npc", &spufs_npc_ops, 0666, },
1861b9e3bd77SDwayne Grant McConnell 	{ "srr0", &spufs_srr0_ops, 0666, },
18628b3d6663SArnd Bergmann 	{ "decr", &spufs_decr_ops, 0666, },
18638b3d6663SArnd Bergmann 	{ "decr_status", &spufs_decr_status_ops, 0666, },
18648b3d6663SArnd Bergmann 	{ "event_mask", &spufs_event_mask_ops, 0666, },
1865b9e3bd77SDwayne Grant McConnell 	{ "event_status", &spufs_event_status_ops, 0444, },
186627d5bf2aSBenjamin Herrenschmidt 	{ "psmap", &spufs_psmap_fops, 0666, },
186786767277SArnd Bergmann 	{ "phys-id", &spufs_id_ops, 0666, },
186886767277SArnd Bergmann 	{ "object-id", &spufs_object_id_ops, 0666, },
186969a2f00cSDwayne Grant McConnell 	{ "mbox_info", &spufs_mbox_info_fops, 0444, },
187069a2f00cSDwayne Grant McConnell 	{ "ibox_info", &spufs_ibox_info_fops, 0444, },
187169a2f00cSDwayne Grant McConnell 	{ "wbox_info", &spufs_wbox_info_fops, 0444, },
1872b9e3bd77SDwayne Grant McConnell 	{ "dma_info", &spufs_dma_info_fops, 0444, },
1873b9e3bd77SDwayne Grant McConnell 	{ "proxydma_info", &spufs_proxydma_info_fops, 0444, },
187467207b96SArnd Bergmann 	{},
187567207b96SArnd Bergmann };
18765737edd1SMark Nutter 
18775737edd1SMark Nutter struct tree_descr spufs_dir_nosched_contents[] = {
18785737edd1SMark Nutter 	{ "mem",  &spufs_mem_fops,  0666, },
18795737edd1SMark Nutter 	{ "mbox", &spufs_mbox_fops, 0444, },
18805737edd1SMark Nutter 	{ "ibox", &spufs_ibox_fops, 0444, },
18815737edd1SMark Nutter 	{ "wbox", &spufs_wbox_fops, 0222, },
18825737edd1SMark Nutter 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
18835737edd1SMark Nutter 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
18845737edd1SMark Nutter 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
18855737edd1SMark Nutter 	{ "signal1", &spufs_signal1_fops, 0666, },
18865737edd1SMark Nutter 	{ "signal2", &spufs_signal2_fops, 0666, },
18875737edd1SMark Nutter 	{ "signal1_type", &spufs_signal1_type, 0666, },
18885737edd1SMark Nutter 	{ "signal2_type", &spufs_signal2_type, 0666, },
18895737edd1SMark Nutter 	{ "mss", &spufs_mss_fops, 0666, },
18905737edd1SMark Nutter 	{ "mfc", &spufs_mfc_fops, 0666, },
18915737edd1SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18925737edd1SMark Nutter 	{ "npc", &spufs_npc_ops, 0666, },
18935737edd1SMark Nutter 	{ "psmap", &spufs_psmap_fops, 0666, },
18945737edd1SMark Nutter 	{ "phys-id", &spufs_id_ops, 0666, },
18955737edd1SMark Nutter 	{ "object-id", &spufs_object_id_ops, 0666, },
18965737edd1SMark Nutter 	{},
18975737edd1SMark Nutter };
1898bf1ab978SDwayne Grant McConnell 
1899bf1ab978SDwayne Grant McConnell struct spufs_coredump_reader spufs_coredump_read[] = {
1900bf1ab978SDwayne Grant McConnell 	{ "regs", __spufs_regs_read, NULL, 128 * 16 },
1901bf1ab978SDwayne Grant McConnell 	{ "fpcr", __spufs_fpcr_read, NULL, 16 },
1902bf1ab978SDwayne Grant McConnell 	{ "lslr", NULL, __spufs_lslr_get, 11 },
1903bf1ab978SDwayne Grant McConnell 	{ "decr", NULL, __spufs_decr_get, 11 },
1904bf1ab978SDwayne Grant McConnell 	{ "decr_status", NULL, __spufs_decr_status_get, 11 },
1905bf1ab978SDwayne Grant McConnell 	{ "mem", __spufs_mem_read, NULL, 256 * 1024, },
1906bf1ab978SDwayne Grant McConnell 	{ "signal1", __spufs_signal1_read, NULL, 4 },
1907bf1ab978SDwayne Grant McConnell 	{ "signal1_type", NULL, __spufs_signal1_type_get, 2 },
1908bf1ab978SDwayne Grant McConnell 	{ "signal2", __spufs_signal2_read, NULL, 4 },
1909bf1ab978SDwayne Grant McConnell 	{ "signal2_type", NULL, __spufs_signal2_type_get, 2 },
1910bf1ab978SDwayne Grant McConnell 	{ "event_mask", NULL, __spufs_event_mask_get, 8 },
1911bf1ab978SDwayne Grant McConnell 	{ "event_status", NULL, __spufs_event_status_get, 8 },
1912bf1ab978SDwayne Grant McConnell 	{ "mbox_info", __spufs_mbox_info_read, NULL, 4 },
1913bf1ab978SDwayne Grant McConnell 	{ "ibox_info", __spufs_ibox_info_read, NULL, 4 },
1914bf1ab978SDwayne Grant McConnell 	{ "wbox_info", __spufs_wbox_info_read, NULL, 16 },
1915bf1ab978SDwayne Grant McConnell 	{ "dma_info", __spufs_dma_info_read, NULL, 69 * 8 },
1916bf1ab978SDwayne Grant McConnell 	{ "proxydma_info", __spufs_proxydma_info_read, NULL, 35 * 8 },
1917bf1ab978SDwayne Grant McConnell 	{ "object-id", NULL, __spufs_object_id_get, 19 },
1918bf1ab978SDwayne Grant McConnell 	{ },
1919bf1ab978SDwayne Grant McConnell };
1920bf1ab978SDwayne Grant McConnell int spufs_coredump_num_notes = ARRAY_SIZE(spufs_coredump_read) - 1;
1921bf1ab978SDwayne Grant McConnell 
1922