167207b96SArnd Bergmann /*
267207b96SArnd Bergmann  * SPU file system -- file contents
367207b96SArnd Bergmann  *
467207b96SArnd Bergmann  * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
567207b96SArnd Bergmann  *
667207b96SArnd Bergmann  * Author: Arnd Bergmann <arndb@de.ibm.com>
767207b96SArnd Bergmann  *
867207b96SArnd Bergmann  * This program is free software; you can redistribute it and/or modify
967207b96SArnd Bergmann  * it under the terms of the GNU General Public License as published by
1067207b96SArnd Bergmann  * the Free Software Foundation; either version 2, or (at your option)
1167207b96SArnd Bergmann  * any later version.
1267207b96SArnd Bergmann  *
1367207b96SArnd Bergmann  * This program is distributed in the hope that it will be useful,
1467207b96SArnd Bergmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1567207b96SArnd Bergmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1667207b96SArnd Bergmann  * GNU General Public License for more details.
1767207b96SArnd Bergmann  *
1867207b96SArnd Bergmann  * You should have received a copy of the GNU General Public License
1967207b96SArnd Bergmann  * along with this program; if not, write to the Free Software
2067207b96SArnd Bergmann  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2167207b96SArnd Bergmann  */
2267207b96SArnd Bergmann 
23a33a7d73SArnd Bergmann #undef DEBUG
24a33a7d73SArnd Bergmann 
2567207b96SArnd Bergmann #include <linux/fs.h>
2667207b96SArnd Bergmann #include <linux/ioctl.h>
2767207b96SArnd Bergmann #include <linux/module.h>
28d88cfffaSArnd Bergmann #include <linux/pagemap.h>
2967207b96SArnd Bergmann #include <linux/poll.h>
305110459fSArnd Bergmann #include <linux/ptrace.h>
3167207b96SArnd Bergmann 
3267207b96SArnd Bergmann #include <asm/io.h>
3367207b96SArnd Bergmann #include <asm/semaphore.h>
3467207b96SArnd Bergmann #include <asm/spu.h>
35b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h>
3667207b96SArnd Bergmann #include <asm/uaccess.h>
3767207b96SArnd Bergmann 
3867207b96SArnd Bergmann #include "spufs.h"
3967207b96SArnd Bergmann 
4027d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000)
4127d5bf2aSBenjamin Herrenschmidt 
4267207b96SArnd Bergmann static int
4367207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file)
4467207b96SArnd Bergmann {
4567207b96SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
466df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
476df10a82SMark Nutter 	file->private_data = ctx;
486df10a82SMark Nutter 	ctx->local_store = inode->i_mapping;
4917e0e270SBenjamin Herrenschmidt 	smp_wmb();
5067207b96SArnd Bergmann 	return 0;
5167207b96SArnd Bergmann }
5267207b96SArnd Bergmann 
5367207b96SArnd Bergmann static ssize_t
54bf1ab978SDwayne Grant McConnell __spufs_mem_read(struct spu_context *ctx, char __user *buffer,
55bf1ab978SDwayne Grant McConnell 			size_t size, loff_t *pos)
56bf1ab978SDwayne Grant McConnell {
57bf1ab978SDwayne Grant McConnell 	char *local_store = ctx->ops->get_ls(ctx);
58bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos, local_store,
59bf1ab978SDwayne Grant McConnell 					LS_SIZE);
60bf1ab978SDwayne Grant McConnell }
61bf1ab978SDwayne Grant McConnell 
62bf1ab978SDwayne Grant McConnell static ssize_t
6367207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer,
6467207b96SArnd Bergmann 				size_t size, loff_t *pos)
6567207b96SArnd Bergmann {
6667207b96SArnd Bergmann 	int ret;
67bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
6867207b96SArnd Bergmann 
698b3d6663SArnd Bergmann 	spu_acquire(ctx);
70bf1ab978SDwayne Grant McConnell 	ret = __spufs_mem_read(ctx, buffer, size, pos);
718b3d6663SArnd Bergmann 	spu_release(ctx);
7267207b96SArnd Bergmann 	return ret;
7367207b96SArnd Bergmann }
7467207b96SArnd Bergmann 
7567207b96SArnd Bergmann static ssize_t
7667207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer,
7767207b96SArnd Bergmann 					size_t size, loff_t *pos)
7867207b96SArnd Bergmann {
7967207b96SArnd Bergmann 	struct spu_context *ctx = file->private_data;
808b3d6663SArnd Bergmann 	char *local_store;
818b3d6663SArnd Bergmann 	int ret;
8267207b96SArnd Bergmann 
8367207b96SArnd Bergmann 	size = min_t(ssize_t, LS_SIZE - *pos, size);
8467207b96SArnd Bergmann 	if (size <= 0)
8567207b96SArnd Bergmann 		return -EFBIG;
8667207b96SArnd Bergmann 	*pos += size;
878b3d6663SArnd Bergmann 
888b3d6663SArnd Bergmann 	spu_acquire(ctx);
898b3d6663SArnd Bergmann 
908b3d6663SArnd Bergmann 	local_store = ctx->ops->get_ls(ctx);
918b3d6663SArnd Bergmann 	ret = copy_from_user(local_store + *pos - size,
9267207b96SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
938b3d6663SArnd Bergmann 
948b3d6663SArnd Bergmann 	spu_release(ctx);
958b3d6663SArnd Bergmann 	return ret;
9667207b96SArnd Bergmann }
9767207b96SArnd Bergmann 
9878bde53eSBenjamin Herrenschmidt static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma,
9978bde53eSBenjamin Herrenschmidt 					  unsigned long address)
1008b3d6663SArnd Bergmann {
1018b3d6663SArnd Bergmann 	struct spu_context *ctx = vma->vm_file->private_data;
10278bde53eSBenjamin Herrenschmidt 	unsigned long pfn, offset = address - vma->vm_start;
10378bde53eSBenjamin Herrenschmidt 
1048b3d6663SArnd Bergmann 	offset += vma->vm_pgoff << PAGE_SHIFT;
1058b3d6663SArnd Bergmann 
1068b3d6663SArnd Bergmann 	spu_acquire(ctx);
1078b3d6663SArnd Bergmann 
108ac91cb8dSArnd Bergmann 	if (ctx->state == SPU_STATE_SAVED) {
109ac91cb8dSArnd Bergmann 		vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
110932f535dSArnd Bergmann 							& ~_PAGE_NO_CACHE);
11178bde53eSBenjamin Herrenschmidt 		pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset);
112ac91cb8dSArnd Bergmann 	} else {
113ac91cb8dSArnd Bergmann 		vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
114932f535dSArnd Bergmann 					     | _PAGE_NO_CACHE);
11578bde53eSBenjamin Herrenschmidt 		pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT;
116ac91cb8dSArnd Bergmann 	}
11778bde53eSBenjamin Herrenschmidt 	vm_insert_pfn(vma, address, pfn);
11878bde53eSBenjamin Herrenschmidt 
1198b3d6663SArnd Bergmann 	spu_release(ctx);
1208b3d6663SArnd Bergmann 
12178bde53eSBenjamin Herrenschmidt 	return NOPFN_REFAULT;
1228b3d6663SArnd Bergmann }
1238b3d6663SArnd Bergmann 
12478bde53eSBenjamin Herrenschmidt 
1258b3d6663SArnd Bergmann static struct vm_operations_struct spufs_mem_mmap_vmops = {
12678bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mem_mmap_nopfn,
1278b3d6663SArnd Bergmann };
1288b3d6663SArnd Bergmann 
12967207b96SArnd Bergmann static int
13067207b96SArnd Bergmann spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
13167207b96SArnd Bergmann {
1328b3d6663SArnd Bergmann 	if (!(vma->vm_flags & VM_SHARED))
1338b3d6663SArnd Bergmann 		return -EINVAL;
13467207b96SArnd Bergmann 
13578bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
13667207b96SArnd Bergmann 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
13767207b96SArnd Bergmann 				     | _PAGE_NO_CACHE);
1388b3d6663SArnd Bergmann 
1398b3d6663SArnd Bergmann 	vma->vm_ops = &spufs_mem_mmap_vmops;
14067207b96SArnd Bergmann 	return 0;
14167207b96SArnd Bergmann }
14267207b96SArnd Bergmann 
1435dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = {
14467207b96SArnd Bergmann 	.open	 = spufs_mem_open,
14567207b96SArnd Bergmann 	.read    = spufs_mem_read,
14667207b96SArnd Bergmann 	.write   = spufs_mem_write,
1478b3d6663SArnd Bergmann 	.llseek  = generic_file_llseek,
14867207b96SArnd Bergmann 	.mmap    = spufs_mem_mmap,
1498b3d6663SArnd Bergmann };
1508b3d6663SArnd Bergmann 
15178bde53eSBenjamin Herrenschmidt static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma,
1526df10a82SMark Nutter 				    unsigned long address,
15378bde53eSBenjamin Herrenschmidt 				    unsigned long ps_offs,
15427d5bf2aSBenjamin Herrenschmidt 				    unsigned long ps_size)
1556df10a82SMark Nutter {
1566df10a82SMark Nutter 	struct spu_context *ctx = vma->vm_file->private_data;
15778bde53eSBenjamin Herrenschmidt 	unsigned long area, offset = address - vma->vm_start;
1586df10a82SMark Nutter 	int ret;
1596df10a82SMark Nutter 
1606df10a82SMark Nutter 	offset += vma->vm_pgoff << PAGE_SHIFT;
16127d5bf2aSBenjamin Herrenschmidt 	if (offset >= ps_size)
16278bde53eSBenjamin Herrenschmidt 		return NOPFN_SIGBUS;
1636df10a82SMark Nutter 
16478bde53eSBenjamin Herrenschmidt 	/* error here usually means a signal.. we might want to test
16578bde53eSBenjamin Herrenschmidt 	 * the error code more precisely though
16678bde53eSBenjamin Herrenschmidt 	 */
1676df10a82SMark Nutter 	ret = spu_acquire_runnable(ctx);
1686df10a82SMark Nutter 	if (ret)
16978bde53eSBenjamin Herrenschmidt 		return NOPFN_REFAULT;
1706df10a82SMark Nutter 
1716df10a82SMark Nutter 	area = ctx->spu->problem_phys + ps_offs;
17278bde53eSBenjamin Herrenschmidt 	vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT);
1736df10a82SMark Nutter 	spu_release(ctx);
1746df10a82SMark Nutter 
17578bde53eSBenjamin Herrenschmidt 	return NOPFN_REFAULT;
1766df10a82SMark Nutter }
1776df10a82SMark Nutter 
17827d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
17978bde53eSBenjamin Herrenschmidt static unsigned long spufs_cntl_mmap_nopfn(struct vm_area_struct *vma,
18078bde53eSBenjamin Herrenschmidt 					   unsigned long address)
1816df10a82SMark Nutter {
18278bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x4000, 0x1000);
1836df10a82SMark Nutter }
1846df10a82SMark Nutter 
1856df10a82SMark Nutter static struct vm_operations_struct spufs_cntl_mmap_vmops = {
18678bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_cntl_mmap_nopfn,
1876df10a82SMark Nutter };
1886df10a82SMark Nutter 
1896df10a82SMark Nutter /*
1906df10a82SMark Nutter  * mmap support for problem state control area [0x4000 - 0x4fff].
1916df10a82SMark Nutter  */
1926df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
1936df10a82SMark Nutter {
1946df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
1956df10a82SMark Nutter 		return -EINVAL;
1966df10a82SMark Nutter 
19778bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
1986df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
19923cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
2006df10a82SMark Nutter 
2016df10a82SMark Nutter 	vma->vm_ops = &spufs_cntl_mmap_vmops;
2026df10a82SMark Nutter 	return 0;
2036df10a82SMark Nutter }
20427d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
20527d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL
20627d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
2076df10a82SMark Nutter 
208e1dbff2bSArnd Bergmann static u64 spufs_cntl_get(void *data)
209e1dbff2bSArnd Bergmann {
210e1dbff2bSArnd Bergmann 	struct spu_context *ctx = data;
211e1dbff2bSArnd Bergmann 	u64 val;
212e1dbff2bSArnd Bergmann 
213e1dbff2bSArnd Bergmann 	spu_acquire(ctx);
214e1dbff2bSArnd Bergmann 	val = ctx->ops->status_read(ctx);
215e1dbff2bSArnd Bergmann 	spu_release(ctx);
216e1dbff2bSArnd Bergmann 
217e1dbff2bSArnd Bergmann 	return val;
218e1dbff2bSArnd Bergmann }
219e1dbff2bSArnd Bergmann 
220e1dbff2bSArnd Bergmann static void spufs_cntl_set(void *data, u64 val)
221e1dbff2bSArnd Bergmann {
222e1dbff2bSArnd Bergmann 	struct spu_context *ctx = data;
223e1dbff2bSArnd Bergmann 
224e1dbff2bSArnd Bergmann 	spu_acquire(ctx);
225e1dbff2bSArnd Bergmann 	ctx->ops->runcntl_write(ctx, val);
226e1dbff2bSArnd Bergmann 	spu_release(ctx);
227e1dbff2bSArnd Bergmann }
228e1dbff2bSArnd Bergmann 
2296df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file)
2306df10a82SMark Nutter {
2316df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
2326df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
2336df10a82SMark Nutter 
2346df10a82SMark Nutter 	file->private_data = ctx;
2356df10a82SMark Nutter 	ctx->cntl = inode->i_mapping;
23617e0e270SBenjamin Herrenschmidt 	smp_wmb();
237e1dbff2bSArnd Bergmann 	return simple_attr_open(inode, file, spufs_cntl_get,
238e1dbff2bSArnd Bergmann 					spufs_cntl_set, "0x%08lx");
2396df10a82SMark Nutter }
2406df10a82SMark Nutter 
2415dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = {
2426df10a82SMark Nutter 	.open = spufs_cntl_open,
243654e4aeeSNoguchi, Masato 	.release = simple_attr_close,
244e1dbff2bSArnd Bergmann 	.read = simple_attr_read,
245e1dbff2bSArnd Bergmann 	.write = simple_attr_write,
2466df10a82SMark Nutter 	.mmap = spufs_cntl_mmap,
2476df10a82SMark Nutter };
2486df10a82SMark Nutter 
2498b3d6663SArnd Bergmann static int
2508b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file)
2518b3d6663SArnd Bergmann {
2528b3d6663SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
2538b3d6663SArnd Bergmann 	file->private_data = i->i_ctx;
2548b3d6663SArnd Bergmann 	return 0;
2558b3d6663SArnd Bergmann }
2568b3d6663SArnd Bergmann 
2578b3d6663SArnd Bergmann static ssize_t
258bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer,
259bf1ab978SDwayne Grant McConnell 			size_t size, loff_t *pos)
260bf1ab978SDwayne Grant McConnell {
261bf1ab978SDwayne Grant McConnell 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
262bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos,
263bf1ab978SDwayne Grant McConnell 				      lscsa->gprs, sizeof lscsa->gprs);
264bf1ab978SDwayne Grant McConnell }
265bf1ab978SDwayne Grant McConnell 
266bf1ab978SDwayne Grant McConnell static ssize_t
2678b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer,
2688b3d6663SArnd Bergmann 		size_t size, loff_t *pos)
2698b3d6663SArnd Bergmann {
2708b3d6663SArnd Bergmann 	int ret;
271bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
2728b3d6663SArnd Bergmann 
2738b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
274bf1ab978SDwayne Grant McConnell 	ret = __spufs_regs_read(ctx, buffer, size, pos);
2758b3d6663SArnd Bergmann 	spu_release(ctx);
2768b3d6663SArnd Bergmann 	return ret;
2778b3d6663SArnd Bergmann }
2788b3d6663SArnd Bergmann 
2798b3d6663SArnd Bergmann static ssize_t
2808b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer,
2818b3d6663SArnd Bergmann 		 size_t size, loff_t *pos)
2828b3d6663SArnd Bergmann {
2838b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
2848b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
2858b3d6663SArnd Bergmann 	int ret;
2868b3d6663SArnd Bergmann 
2878b3d6663SArnd Bergmann 	size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size);
2888b3d6663SArnd Bergmann 	if (size <= 0)
2898b3d6663SArnd Bergmann 		return -EFBIG;
2908b3d6663SArnd Bergmann 	*pos += size;
2918b3d6663SArnd Bergmann 
2928b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
2938b3d6663SArnd Bergmann 
2948b3d6663SArnd Bergmann 	ret = copy_from_user(lscsa->gprs + *pos - size,
2958b3d6663SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
2968b3d6663SArnd Bergmann 
2978b3d6663SArnd Bergmann 	spu_release(ctx);
2988b3d6663SArnd Bergmann 	return ret;
2998b3d6663SArnd Bergmann }
3008b3d6663SArnd Bergmann 
3015dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = {
3028b3d6663SArnd Bergmann 	.open	 = spufs_regs_open,
3038b3d6663SArnd Bergmann 	.read    = spufs_regs_read,
3048b3d6663SArnd Bergmann 	.write   = spufs_regs_write,
3058b3d6663SArnd Bergmann 	.llseek  = generic_file_llseek,
3068b3d6663SArnd Bergmann };
3078b3d6663SArnd Bergmann 
3088b3d6663SArnd Bergmann static ssize_t
309bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer,
310bf1ab978SDwayne Grant McConnell 			size_t size, loff_t * pos)
311bf1ab978SDwayne Grant McConnell {
312bf1ab978SDwayne Grant McConnell 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
313bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buffer, size, pos,
314bf1ab978SDwayne Grant McConnell 				      &lscsa->fpcr, sizeof(lscsa->fpcr));
315bf1ab978SDwayne Grant McConnell }
316bf1ab978SDwayne Grant McConnell 
317bf1ab978SDwayne Grant McConnell static ssize_t
3188b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer,
3198b3d6663SArnd Bergmann 		size_t size, loff_t * pos)
3208b3d6663SArnd Bergmann {
3218b3d6663SArnd Bergmann 	int ret;
322bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
3238b3d6663SArnd Bergmann 
3248b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
325bf1ab978SDwayne Grant McConnell 	ret = __spufs_fpcr_read(ctx, buffer, size, pos);
3268b3d6663SArnd Bergmann 	spu_release(ctx);
3278b3d6663SArnd Bergmann 	return ret;
3288b3d6663SArnd Bergmann }
3298b3d6663SArnd Bergmann 
3308b3d6663SArnd Bergmann static ssize_t
3318b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer,
3328b3d6663SArnd Bergmann 		 size_t size, loff_t * pos)
3338b3d6663SArnd Bergmann {
3348b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
3358b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
3368b3d6663SArnd Bergmann 	int ret;
3378b3d6663SArnd Bergmann 
3388b3d6663SArnd Bergmann 	size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size);
3398b3d6663SArnd Bergmann 	if (size <= 0)
3408b3d6663SArnd Bergmann 		return -EFBIG;
3418b3d6663SArnd Bergmann 	*pos += size;
3428b3d6663SArnd Bergmann 
3438b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
3448b3d6663SArnd Bergmann 
3458b3d6663SArnd Bergmann 	ret = copy_from_user((char *)&lscsa->fpcr + *pos - size,
3468b3d6663SArnd Bergmann 			     buffer, size) ? -EFAULT : size;
3478b3d6663SArnd Bergmann 
3488b3d6663SArnd Bergmann 	spu_release(ctx);
3498b3d6663SArnd Bergmann 	return ret;
3508b3d6663SArnd Bergmann }
3518b3d6663SArnd Bergmann 
3525dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = {
3538b3d6663SArnd Bergmann 	.open = spufs_regs_open,
3548b3d6663SArnd Bergmann 	.read = spufs_fpcr_read,
3558b3d6663SArnd Bergmann 	.write = spufs_fpcr_write,
35667207b96SArnd Bergmann 	.llseek = generic_file_llseek,
35767207b96SArnd Bergmann };
35867207b96SArnd Bergmann 
35967207b96SArnd Bergmann /* generic open function for all pipe-like files */
36067207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file)
36167207b96SArnd Bergmann {
36267207b96SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
36367207b96SArnd Bergmann 	file->private_data = i->i_ctx;
36467207b96SArnd Bergmann 
36567207b96SArnd Bergmann 	return nonseekable_open(inode, file);
36667207b96SArnd Bergmann }
36767207b96SArnd Bergmann 
368cdcc89bbSArnd Bergmann /*
369cdcc89bbSArnd Bergmann  * Read as many bytes from the mailbox as possible, until
370cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
371cdcc89bbSArnd Bergmann  *
372cdcc89bbSArnd Bergmann  * - no more data available in the mailbox
373cdcc89bbSArnd Bergmann  * - end of the user provided buffer
374cdcc89bbSArnd Bergmann  * - end of the mapped area
375cdcc89bbSArnd Bergmann  */
37667207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf,
37767207b96SArnd Bergmann 			size_t len, loff_t *pos)
37867207b96SArnd Bergmann {
3798b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
380cdcc89bbSArnd Bergmann 	u32 mbox_data, __user *udata;
381cdcc89bbSArnd Bergmann 	ssize_t count;
38267207b96SArnd Bergmann 
38367207b96SArnd Bergmann 	if (len < 4)
38467207b96SArnd Bergmann 		return -EINVAL;
38567207b96SArnd Bergmann 
386cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_WRITE, buf, len))
38767207b96SArnd Bergmann 		return -EFAULT;
38867207b96SArnd Bergmann 
389cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
390cdcc89bbSArnd Bergmann 
391cdcc89bbSArnd Bergmann 	spu_acquire(ctx);
392274cef5eSArnd Bergmann 	for (count = 0; (count + 4) <= len; count += 4, udata++) {
393cdcc89bbSArnd Bergmann 		int ret;
394cdcc89bbSArnd Bergmann 		ret = ctx->ops->mbox_read(ctx, &mbox_data);
395cdcc89bbSArnd Bergmann 		if (ret == 0)
396cdcc89bbSArnd Bergmann 			break;
397cdcc89bbSArnd Bergmann 
398cdcc89bbSArnd Bergmann 		/*
399cdcc89bbSArnd Bergmann 		 * at the end of the mapped area, we can fault
400cdcc89bbSArnd Bergmann 		 * but still need to return the data we have
401cdcc89bbSArnd Bergmann 		 * read successfully so far.
402cdcc89bbSArnd Bergmann 		 */
403cdcc89bbSArnd Bergmann 		ret = __put_user(mbox_data, udata);
404cdcc89bbSArnd Bergmann 		if (ret) {
405cdcc89bbSArnd Bergmann 			if (!count)
406cdcc89bbSArnd Bergmann 				count = -EFAULT;
407cdcc89bbSArnd Bergmann 			break;
408cdcc89bbSArnd Bergmann 		}
409cdcc89bbSArnd Bergmann 	}
410cdcc89bbSArnd Bergmann 	spu_release(ctx);
411cdcc89bbSArnd Bergmann 
412cdcc89bbSArnd Bergmann 	if (!count)
413cdcc89bbSArnd Bergmann 		count = -EAGAIN;
414cdcc89bbSArnd Bergmann 
415cdcc89bbSArnd Bergmann 	return count;
41667207b96SArnd Bergmann }
41767207b96SArnd Bergmann 
4185dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = {
41967207b96SArnd Bergmann 	.open	= spufs_pipe_open,
42067207b96SArnd Bergmann 	.read	= spufs_mbox_read,
42167207b96SArnd Bergmann };
42267207b96SArnd Bergmann 
42367207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
42467207b96SArnd Bergmann 			size_t len, loff_t *pos)
42567207b96SArnd Bergmann {
4268b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
42767207b96SArnd Bergmann 	u32 mbox_stat;
42867207b96SArnd Bergmann 
42967207b96SArnd Bergmann 	if (len < 4)
43067207b96SArnd Bergmann 		return -EINVAL;
43167207b96SArnd Bergmann 
4328b3d6663SArnd Bergmann 	spu_acquire(ctx);
4338b3d6663SArnd Bergmann 
4348b3d6663SArnd Bergmann 	mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff;
4358b3d6663SArnd Bergmann 
4368b3d6663SArnd Bergmann 	spu_release(ctx);
43767207b96SArnd Bergmann 
43867207b96SArnd Bergmann 	if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat))
43967207b96SArnd Bergmann 		return -EFAULT;
44067207b96SArnd Bergmann 
44167207b96SArnd Bergmann 	return 4;
44267207b96SArnd Bergmann }
44367207b96SArnd Bergmann 
4445dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = {
44567207b96SArnd Bergmann 	.open	= spufs_pipe_open,
44667207b96SArnd Bergmann 	.read	= spufs_mbox_stat_read,
44767207b96SArnd Bergmann };
44867207b96SArnd Bergmann 
44967207b96SArnd Bergmann /* low-level ibox access function */
4508b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data)
45167207b96SArnd Bergmann {
4528b3d6663SArnd Bergmann 	return ctx->ops->ibox_read(ctx, data);
45367207b96SArnd Bergmann }
45467207b96SArnd Bergmann 
45567207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on)
45667207b96SArnd Bergmann {
4578b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
4588b3d6663SArnd Bergmann 
4598b3d6663SArnd Bergmann 	return fasync_helper(fd, file, on, &ctx->ibox_fasync);
4608b3d6663SArnd Bergmann }
4618b3d6663SArnd Bergmann 
4628b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */
4638b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu)
4648b3d6663SArnd Bergmann {
4658b3d6663SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
4668b3d6663SArnd Bergmann 
4678b3d6663SArnd Bergmann 	wake_up_all(&ctx->ibox_wq);
4688b3d6663SArnd Bergmann 	kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN);
46967207b96SArnd Bergmann }
47067207b96SArnd Bergmann 
471cdcc89bbSArnd Bergmann /*
472cdcc89bbSArnd Bergmann  * Read as many bytes from the interrupt mailbox as possible, until
473cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
474cdcc89bbSArnd Bergmann  *
475cdcc89bbSArnd Bergmann  * - no more data available in the mailbox
476cdcc89bbSArnd Bergmann  * - end of the user provided buffer
477cdcc89bbSArnd Bergmann  * - end of the mapped area
478cdcc89bbSArnd Bergmann  *
479cdcc89bbSArnd Bergmann  * If the file is opened without O_NONBLOCK, we wait here until
480cdcc89bbSArnd Bergmann  * any data is available, but return when we have been able to
481cdcc89bbSArnd Bergmann  * read something.
482cdcc89bbSArnd Bergmann  */
48367207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
48467207b96SArnd Bergmann 			size_t len, loff_t *pos)
48567207b96SArnd Bergmann {
4868b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
487cdcc89bbSArnd Bergmann 	u32 ibox_data, __user *udata;
488cdcc89bbSArnd Bergmann 	ssize_t count;
48967207b96SArnd Bergmann 
49067207b96SArnd Bergmann 	if (len < 4)
49167207b96SArnd Bergmann 		return -EINVAL;
49267207b96SArnd Bergmann 
493cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_WRITE, buf, len))
494cdcc89bbSArnd Bergmann 		return -EFAULT;
495cdcc89bbSArnd Bergmann 
496cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
497cdcc89bbSArnd Bergmann 
4988b3d6663SArnd Bergmann 	spu_acquire(ctx);
49967207b96SArnd Bergmann 
500cdcc89bbSArnd Bergmann 	/* wait only for the first element */
501cdcc89bbSArnd Bergmann 	count = 0;
50267207b96SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
5038b3d6663SArnd Bergmann 		if (!spu_ibox_read(ctx, &ibox_data))
504cdcc89bbSArnd Bergmann 			count = -EAGAIN;
50567207b96SArnd Bergmann 	} else {
506cdcc89bbSArnd Bergmann 		count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
507cdcc89bbSArnd Bergmann 	}
508cdcc89bbSArnd Bergmann 	if (count)
509cdcc89bbSArnd Bergmann 		goto out;
510cdcc89bbSArnd Bergmann 
511cdcc89bbSArnd Bergmann 	/* if we can't write at all, return -EFAULT */
512cdcc89bbSArnd Bergmann 	count = __put_user(ibox_data, udata);
513cdcc89bbSArnd Bergmann 	if (count)
514cdcc89bbSArnd Bergmann 		goto out;
515cdcc89bbSArnd Bergmann 
516cdcc89bbSArnd Bergmann 	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
517cdcc89bbSArnd Bergmann 		int ret;
518cdcc89bbSArnd Bergmann 		ret = ctx->ops->ibox_read(ctx, &ibox_data);
519cdcc89bbSArnd Bergmann 		if (ret == 0)
520cdcc89bbSArnd Bergmann 			break;
521cdcc89bbSArnd Bergmann 		/*
522cdcc89bbSArnd Bergmann 		 * at the end of the mapped area, we can fault
523cdcc89bbSArnd Bergmann 		 * but still need to return the data we have
524cdcc89bbSArnd Bergmann 		 * read successfully so far.
525cdcc89bbSArnd Bergmann 		 */
526cdcc89bbSArnd Bergmann 		ret = __put_user(ibox_data, udata);
527cdcc89bbSArnd Bergmann 		if (ret)
528cdcc89bbSArnd Bergmann 			break;
52967207b96SArnd Bergmann 	}
53067207b96SArnd Bergmann 
531cdcc89bbSArnd Bergmann out:
5328b3d6663SArnd Bergmann 	spu_release(ctx);
5338b3d6663SArnd Bergmann 
534cdcc89bbSArnd Bergmann 	return count;
53567207b96SArnd Bergmann }
53667207b96SArnd Bergmann 
53767207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait)
53867207b96SArnd Bergmann {
5398b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
54067207b96SArnd Bergmann 	unsigned int mask;
54167207b96SArnd Bergmann 
5428b3d6663SArnd Bergmann 	poll_wait(file, &ctx->ibox_wq, wait);
54367207b96SArnd Bergmann 
5443a843d7cSArnd Bergmann 	spu_acquire(ctx);
5453a843d7cSArnd Bergmann 	mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM);
5463a843d7cSArnd Bergmann 	spu_release(ctx);
54767207b96SArnd Bergmann 
54867207b96SArnd Bergmann 	return mask;
54967207b96SArnd Bergmann }
55067207b96SArnd Bergmann 
5515dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = {
55267207b96SArnd Bergmann 	.open	= spufs_pipe_open,
55367207b96SArnd Bergmann 	.read	= spufs_ibox_read,
55467207b96SArnd Bergmann 	.poll	= spufs_ibox_poll,
55567207b96SArnd Bergmann 	.fasync	= spufs_ibox_fasync,
55667207b96SArnd Bergmann };
55767207b96SArnd Bergmann 
55867207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
55967207b96SArnd Bergmann 			size_t len, loff_t *pos)
56067207b96SArnd Bergmann {
5618b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
56267207b96SArnd Bergmann 	u32 ibox_stat;
56367207b96SArnd Bergmann 
56467207b96SArnd Bergmann 	if (len < 4)
56567207b96SArnd Bergmann 		return -EINVAL;
56667207b96SArnd Bergmann 
5678b3d6663SArnd Bergmann 	spu_acquire(ctx);
5688b3d6663SArnd Bergmann 	ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff;
5698b3d6663SArnd Bergmann 	spu_release(ctx);
57067207b96SArnd Bergmann 
57167207b96SArnd Bergmann 	if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat))
57267207b96SArnd Bergmann 		return -EFAULT;
57367207b96SArnd Bergmann 
57467207b96SArnd Bergmann 	return 4;
57567207b96SArnd Bergmann }
57667207b96SArnd Bergmann 
5775dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = {
57867207b96SArnd Bergmann 	.open	= spufs_pipe_open,
57967207b96SArnd Bergmann 	.read	= spufs_ibox_stat_read,
58067207b96SArnd Bergmann };
58167207b96SArnd Bergmann 
58267207b96SArnd Bergmann /* low-level mailbox write */
5838b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data)
58467207b96SArnd Bergmann {
5858b3d6663SArnd Bergmann 	return ctx->ops->wbox_write(ctx, data);
58667207b96SArnd Bergmann }
58767207b96SArnd Bergmann 
58867207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on)
58967207b96SArnd Bergmann {
5908b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
5918b3d6663SArnd Bergmann 	int ret;
5928b3d6663SArnd Bergmann 
5938b3d6663SArnd Bergmann 	ret = fasync_helper(fd, file, on, &ctx->wbox_fasync);
5948b3d6663SArnd Bergmann 
5958b3d6663SArnd Bergmann 	return ret;
5968b3d6663SArnd Bergmann }
5978b3d6663SArnd Bergmann 
5988b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */
5998b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu)
6008b3d6663SArnd Bergmann {
6018b3d6663SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
6028b3d6663SArnd Bergmann 
6038b3d6663SArnd Bergmann 	wake_up_all(&ctx->wbox_wq);
6048b3d6663SArnd Bergmann 	kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT);
60567207b96SArnd Bergmann }
60667207b96SArnd Bergmann 
607cdcc89bbSArnd Bergmann /*
608cdcc89bbSArnd Bergmann  * Write as many bytes to the interrupt mailbox as possible, until
609cdcc89bbSArnd Bergmann  * one of the conditions becomes true:
610cdcc89bbSArnd Bergmann  *
611cdcc89bbSArnd Bergmann  * - the mailbox is full
612cdcc89bbSArnd Bergmann  * - end of the user provided buffer
613cdcc89bbSArnd Bergmann  * - end of the mapped area
614cdcc89bbSArnd Bergmann  *
615cdcc89bbSArnd Bergmann  * If the file is opened without O_NONBLOCK, we wait here until
616cdcc89bbSArnd Bergmann  * space is availabyl, but return when we have been able to
617cdcc89bbSArnd Bergmann  * write something.
618cdcc89bbSArnd Bergmann  */
61967207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
62067207b96SArnd Bergmann 			size_t len, loff_t *pos)
62167207b96SArnd Bergmann {
6228b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
623cdcc89bbSArnd Bergmann 	u32 wbox_data, __user *udata;
624cdcc89bbSArnd Bergmann 	ssize_t count;
62567207b96SArnd Bergmann 
62667207b96SArnd Bergmann 	if (len < 4)
62767207b96SArnd Bergmann 		return -EINVAL;
62867207b96SArnd Bergmann 
629cdcc89bbSArnd Bergmann 	udata = (void __user *)buf;
630cdcc89bbSArnd Bergmann 	if (!access_ok(VERIFY_READ, buf, len))
631cdcc89bbSArnd Bergmann 		return -EFAULT;
632cdcc89bbSArnd Bergmann 
633cdcc89bbSArnd Bergmann 	if (__get_user(wbox_data, udata))
63467207b96SArnd Bergmann 		return -EFAULT;
63567207b96SArnd Bergmann 
6368b3d6663SArnd Bergmann 	spu_acquire(ctx);
6378b3d6663SArnd Bergmann 
638cdcc89bbSArnd Bergmann 	/*
639cdcc89bbSArnd Bergmann 	 * make sure we can at least write one element, by waiting
640cdcc89bbSArnd Bergmann 	 * in case of !O_NONBLOCK
641cdcc89bbSArnd Bergmann 	 */
642cdcc89bbSArnd Bergmann 	count = 0;
64367207b96SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
6448b3d6663SArnd Bergmann 		if (!spu_wbox_write(ctx, wbox_data))
645cdcc89bbSArnd Bergmann 			count = -EAGAIN;
64667207b96SArnd Bergmann 	} else {
647cdcc89bbSArnd Bergmann 		count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
64867207b96SArnd Bergmann 	}
64967207b96SArnd Bergmann 
650cdcc89bbSArnd Bergmann 	if (count)
651cdcc89bbSArnd Bergmann 		goto out;
6528b3d6663SArnd Bergmann 
653cdcc89bbSArnd Bergmann 	/* write aѕ much as possible */
654cdcc89bbSArnd Bergmann 	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
655cdcc89bbSArnd Bergmann 		int ret;
656cdcc89bbSArnd Bergmann 		ret = __get_user(wbox_data, udata);
657cdcc89bbSArnd Bergmann 		if (ret)
658cdcc89bbSArnd Bergmann 			break;
659cdcc89bbSArnd Bergmann 
660cdcc89bbSArnd Bergmann 		ret = spu_wbox_write(ctx, wbox_data);
661cdcc89bbSArnd Bergmann 		if (ret == 0)
662cdcc89bbSArnd Bergmann 			break;
663cdcc89bbSArnd Bergmann 	}
664cdcc89bbSArnd Bergmann 
665cdcc89bbSArnd Bergmann out:
666cdcc89bbSArnd Bergmann 	spu_release(ctx);
667cdcc89bbSArnd Bergmann 	return count;
66867207b96SArnd Bergmann }
66967207b96SArnd Bergmann 
67067207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait)
67167207b96SArnd Bergmann {
6728b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
67367207b96SArnd Bergmann 	unsigned int mask;
67467207b96SArnd Bergmann 
6758b3d6663SArnd Bergmann 	poll_wait(file, &ctx->wbox_wq, wait);
67667207b96SArnd Bergmann 
6773a843d7cSArnd Bergmann 	spu_acquire(ctx);
6783a843d7cSArnd Bergmann 	mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM);
6793a843d7cSArnd Bergmann 	spu_release(ctx);
68067207b96SArnd Bergmann 
68167207b96SArnd Bergmann 	return mask;
68267207b96SArnd Bergmann }
68367207b96SArnd Bergmann 
6845dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = {
68567207b96SArnd Bergmann 	.open	= spufs_pipe_open,
68667207b96SArnd Bergmann 	.write	= spufs_wbox_write,
68767207b96SArnd Bergmann 	.poll	= spufs_wbox_poll,
68867207b96SArnd Bergmann 	.fasync	= spufs_wbox_fasync,
68967207b96SArnd Bergmann };
69067207b96SArnd Bergmann 
69167207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
69267207b96SArnd Bergmann 			size_t len, loff_t *pos)
69367207b96SArnd Bergmann {
6948b3d6663SArnd Bergmann 	struct spu_context *ctx = file->private_data;
69567207b96SArnd Bergmann 	u32 wbox_stat;
69667207b96SArnd Bergmann 
69767207b96SArnd Bergmann 	if (len < 4)
69867207b96SArnd Bergmann 		return -EINVAL;
69967207b96SArnd Bergmann 
7008b3d6663SArnd Bergmann 	spu_acquire(ctx);
7018b3d6663SArnd Bergmann 	wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff;
7028b3d6663SArnd Bergmann 	spu_release(ctx);
70367207b96SArnd Bergmann 
70467207b96SArnd Bergmann 	if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat))
70567207b96SArnd Bergmann 		return -EFAULT;
70667207b96SArnd Bergmann 
70767207b96SArnd Bergmann 	return 4;
70867207b96SArnd Bergmann }
70967207b96SArnd Bergmann 
7105dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = {
71167207b96SArnd Bergmann 	.open	= spufs_pipe_open,
71267207b96SArnd Bergmann 	.read	= spufs_wbox_stat_read,
71367207b96SArnd Bergmann };
71467207b96SArnd Bergmann 
7156df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file)
7166df10a82SMark Nutter {
7176df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
7186df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
7196df10a82SMark Nutter 	file->private_data = ctx;
7206df10a82SMark Nutter 	ctx->signal1 = inode->i_mapping;
72117e0e270SBenjamin Herrenschmidt 	smp_wmb();
7226df10a82SMark Nutter 	return nonseekable_open(inode, file);
7236df10a82SMark Nutter }
7246df10a82SMark Nutter 
725bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
72667207b96SArnd Bergmann 			size_t len, loff_t *pos)
72767207b96SArnd Bergmann {
72817f88cebSDwayne Grant McConnell 	int ret = 0;
72967207b96SArnd Bergmann 	u32 data;
73067207b96SArnd Bergmann 
73167207b96SArnd Bergmann 	if (len < 4)
73267207b96SArnd Bergmann 		return -EINVAL;
73367207b96SArnd Bergmann 
73417f88cebSDwayne Grant McConnell 	if (ctx->csa.spu_chnlcnt_RW[3]) {
73517f88cebSDwayne Grant McConnell 		data = ctx->csa.spu_chnldata_RW[3];
73617f88cebSDwayne Grant McConnell 		ret = 4;
73717f88cebSDwayne Grant McConnell 	}
7388b3d6663SArnd Bergmann 
73917f88cebSDwayne Grant McConnell 	if (!ret)
74017f88cebSDwayne Grant McConnell 		goto out;
74117f88cebSDwayne Grant McConnell 
74267207b96SArnd Bergmann 	if (copy_to_user(buf, &data, 4))
74367207b96SArnd Bergmann 		return -EFAULT;
74467207b96SArnd Bergmann 
74517f88cebSDwayne Grant McConnell out:
74617f88cebSDwayne Grant McConnell 	return ret;
74767207b96SArnd Bergmann }
74867207b96SArnd Bergmann 
749bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
750bf1ab978SDwayne Grant McConnell 			size_t len, loff_t *pos)
751bf1ab978SDwayne Grant McConnell {
752bf1ab978SDwayne Grant McConnell 	int ret;
753bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
754bf1ab978SDwayne Grant McConnell 
755bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
756bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal1_read(ctx, buf, len, pos);
757bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
758bf1ab978SDwayne Grant McConnell 
759bf1ab978SDwayne Grant McConnell 	return ret;
760bf1ab978SDwayne Grant McConnell }
761bf1ab978SDwayne Grant McConnell 
76267207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
76367207b96SArnd Bergmann 			size_t len, loff_t *pos)
76467207b96SArnd Bergmann {
76567207b96SArnd Bergmann 	struct spu_context *ctx;
76667207b96SArnd Bergmann 	u32 data;
76767207b96SArnd Bergmann 
76867207b96SArnd Bergmann 	ctx = file->private_data;
76967207b96SArnd Bergmann 
77067207b96SArnd Bergmann 	if (len < 4)
77167207b96SArnd Bergmann 		return -EINVAL;
77267207b96SArnd Bergmann 
77367207b96SArnd Bergmann 	if (copy_from_user(&data, buf, 4))
77467207b96SArnd Bergmann 		return -EFAULT;
77567207b96SArnd Bergmann 
7768b3d6663SArnd Bergmann 	spu_acquire(ctx);
7778b3d6663SArnd Bergmann 	ctx->ops->signal1_write(ctx, data);
7788b3d6663SArnd Bergmann 	spu_release(ctx);
77967207b96SArnd Bergmann 
78067207b96SArnd Bergmann 	return 4;
78167207b96SArnd Bergmann }
78267207b96SArnd Bergmann 
78378bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal1_mmap_nopfn(struct vm_area_struct *vma,
78478bde53eSBenjamin Herrenschmidt 					      unsigned long address)
7856df10a82SMark Nutter {
78627d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000
78778bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x14000, 0x1000);
78827d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000
78927d5bf2aSBenjamin Herrenschmidt 	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
79027d5bf2aSBenjamin Herrenschmidt 	 * signal 1 and 2 area
79127d5bf2aSBenjamin Herrenschmidt 	 */
79278bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
79327d5bf2aSBenjamin Herrenschmidt #else
79427d5bf2aSBenjamin Herrenschmidt #error unsupported page size
79527d5bf2aSBenjamin Herrenschmidt #endif
7966df10a82SMark Nutter }
7976df10a82SMark Nutter 
7986df10a82SMark Nutter static struct vm_operations_struct spufs_signal1_mmap_vmops = {
79978bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_signal1_mmap_nopfn,
8006df10a82SMark Nutter };
8016df10a82SMark Nutter 
8026df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
8036df10a82SMark Nutter {
8046df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
8056df10a82SMark Nutter 		return -EINVAL;
8066df10a82SMark Nutter 
80778bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
8086df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
80923cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
8106df10a82SMark Nutter 
8116df10a82SMark Nutter 	vma->vm_ops = &spufs_signal1_mmap_vmops;
8126df10a82SMark Nutter 	return 0;
8136df10a82SMark Nutter }
8146df10a82SMark Nutter 
8155dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = {
8166df10a82SMark Nutter 	.open = spufs_signal1_open,
81767207b96SArnd Bergmann 	.read = spufs_signal1_read,
81867207b96SArnd Bergmann 	.write = spufs_signal1_write,
8196df10a82SMark Nutter 	.mmap = spufs_signal1_mmap,
82067207b96SArnd Bergmann };
82167207b96SArnd Bergmann 
8226df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file)
8236df10a82SMark Nutter {
8246df10a82SMark Nutter 	struct spufs_inode_info *i = SPUFS_I(inode);
8256df10a82SMark Nutter 	struct spu_context *ctx = i->i_ctx;
8266df10a82SMark Nutter 	file->private_data = ctx;
8276df10a82SMark Nutter 	ctx->signal2 = inode->i_mapping;
82817e0e270SBenjamin Herrenschmidt 	smp_wmb();
8296df10a82SMark Nutter 	return nonseekable_open(inode, file);
8306df10a82SMark Nutter }
8316df10a82SMark Nutter 
832bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
83367207b96SArnd Bergmann 			size_t len, loff_t *pos)
83467207b96SArnd Bergmann {
83517f88cebSDwayne Grant McConnell 	int ret = 0;
83667207b96SArnd Bergmann 	u32 data;
83767207b96SArnd Bergmann 
83867207b96SArnd Bergmann 	if (len < 4)
83967207b96SArnd Bergmann 		return -EINVAL;
84067207b96SArnd Bergmann 
84117f88cebSDwayne Grant McConnell 	if (ctx->csa.spu_chnlcnt_RW[4]) {
84217f88cebSDwayne Grant McConnell 		data =  ctx->csa.spu_chnldata_RW[4];
84317f88cebSDwayne Grant McConnell 		ret = 4;
84417f88cebSDwayne Grant McConnell 	}
8458b3d6663SArnd Bergmann 
84617f88cebSDwayne Grant McConnell 	if (!ret)
84717f88cebSDwayne Grant McConnell 		goto out;
84817f88cebSDwayne Grant McConnell 
84967207b96SArnd Bergmann 	if (copy_to_user(buf, &data, 4))
85067207b96SArnd Bergmann 		return -EFAULT;
85167207b96SArnd Bergmann 
85217f88cebSDwayne Grant McConnell out:
853bf1ab978SDwayne Grant McConnell 	return ret;
854bf1ab978SDwayne Grant McConnell }
855bf1ab978SDwayne Grant McConnell 
856bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
857bf1ab978SDwayne Grant McConnell 			size_t len, loff_t *pos)
858bf1ab978SDwayne Grant McConnell {
859bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
860bf1ab978SDwayne Grant McConnell 	int ret;
861bf1ab978SDwayne Grant McConnell 
862bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
863bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal2_read(ctx, buf, len, pos);
864bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
865bf1ab978SDwayne Grant McConnell 
866bf1ab978SDwayne Grant McConnell 	return ret;
86767207b96SArnd Bergmann }
86867207b96SArnd Bergmann 
86967207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
87067207b96SArnd Bergmann 			size_t len, loff_t *pos)
87167207b96SArnd Bergmann {
87267207b96SArnd Bergmann 	struct spu_context *ctx;
87367207b96SArnd Bergmann 	u32 data;
87467207b96SArnd Bergmann 
87567207b96SArnd Bergmann 	ctx = file->private_data;
87667207b96SArnd Bergmann 
87767207b96SArnd Bergmann 	if (len < 4)
87867207b96SArnd Bergmann 		return -EINVAL;
87967207b96SArnd Bergmann 
88067207b96SArnd Bergmann 	if (copy_from_user(&data, buf, 4))
88167207b96SArnd Bergmann 		return -EFAULT;
88267207b96SArnd Bergmann 
8838b3d6663SArnd Bergmann 	spu_acquire(ctx);
8848b3d6663SArnd Bergmann 	ctx->ops->signal2_write(ctx, data);
8858b3d6663SArnd Bergmann 	spu_release(ctx);
88667207b96SArnd Bergmann 
88767207b96SArnd Bergmann 	return 4;
88867207b96SArnd Bergmann }
88967207b96SArnd Bergmann 
89027d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
89178bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal2_mmap_nopfn(struct vm_area_struct *vma,
89278bde53eSBenjamin Herrenschmidt 					      unsigned long address)
8936df10a82SMark Nutter {
89427d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000
89578bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x1c000, 0x1000);
89627d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000
89727d5bf2aSBenjamin Herrenschmidt 	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
89827d5bf2aSBenjamin Herrenschmidt 	 * signal 1 and 2 area
89927d5bf2aSBenjamin Herrenschmidt 	 */
90078bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x10000, 0x10000);
90127d5bf2aSBenjamin Herrenschmidt #else
90227d5bf2aSBenjamin Herrenschmidt #error unsupported page size
90327d5bf2aSBenjamin Herrenschmidt #endif
9046df10a82SMark Nutter }
9056df10a82SMark Nutter 
9066df10a82SMark Nutter static struct vm_operations_struct spufs_signal2_mmap_vmops = {
90778bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_signal2_mmap_nopfn,
9086df10a82SMark Nutter };
9096df10a82SMark Nutter 
9106df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
9116df10a82SMark Nutter {
9126df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
9136df10a82SMark Nutter 		return -EINVAL;
9146df10a82SMark Nutter 
91578bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
9166df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
91723cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
9186df10a82SMark Nutter 
9196df10a82SMark Nutter 	vma->vm_ops = &spufs_signal2_mmap_vmops;
9206df10a82SMark Nutter 	return 0;
9216df10a82SMark Nutter }
92227d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
92327d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL
92427d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
9256df10a82SMark Nutter 
9265dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = {
9276df10a82SMark Nutter 	.open = spufs_signal2_open,
92867207b96SArnd Bergmann 	.read = spufs_signal2_read,
92967207b96SArnd Bergmann 	.write = spufs_signal2_write,
9306df10a82SMark Nutter 	.mmap = spufs_signal2_mmap,
93167207b96SArnd Bergmann };
93267207b96SArnd Bergmann 
93367207b96SArnd Bergmann static void spufs_signal1_type_set(void *data, u64 val)
93467207b96SArnd Bergmann {
93567207b96SArnd Bergmann 	struct spu_context *ctx = data;
93667207b96SArnd Bergmann 
9378b3d6663SArnd Bergmann 	spu_acquire(ctx);
9388b3d6663SArnd Bergmann 	ctx->ops->signal1_type_set(ctx, val);
9398b3d6663SArnd Bergmann 	spu_release(ctx);
94067207b96SArnd Bergmann }
94167207b96SArnd Bergmann 
942bf1ab978SDwayne Grant McConnell static u64 __spufs_signal1_type_get(void *data)
943bf1ab978SDwayne Grant McConnell {
944bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
945bf1ab978SDwayne Grant McConnell 	return ctx->ops->signal1_type_get(ctx);
946bf1ab978SDwayne Grant McConnell }
947bf1ab978SDwayne Grant McConnell 
94867207b96SArnd Bergmann static u64 spufs_signal1_type_get(void *data)
94967207b96SArnd Bergmann {
95067207b96SArnd Bergmann 	struct spu_context *ctx = data;
9518b3d6663SArnd Bergmann 	u64 ret;
9528b3d6663SArnd Bergmann 
9538b3d6663SArnd Bergmann 	spu_acquire(ctx);
954bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal1_type_get(data);
9558b3d6663SArnd Bergmann 	spu_release(ctx);
9568b3d6663SArnd Bergmann 
9578b3d6663SArnd Bergmann 	return ret;
95867207b96SArnd Bergmann }
95967207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
96067207b96SArnd Bergmann 					spufs_signal1_type_set, "%llu");
96167207b96SArnd Bergmann 
96267207b96SArnd Bergmann static void spufs_signal2_type_set(void *data, u64 val)
96367207b96SArnd Bergmann {
96467207b96SArnd Bergmann 	struct spu_context *ctx = data;
96567207b96SArnd Bergmann 
9668b3d6663SArnd Bergmann 	spu_acquire(ctx);
9678b3d6663SArnd Bergmann 	ctx->ops->signal2_type_set(ctx, val);
9688b3d6663SArnd Bergmann 	spu_release(ctx);
96967207b96SArnd Bergmann }
97067207b96SArnd Bergmann 
971bf1ab978SDwayne Grant McConnell static u64 __spufs_signal2_type_get(void *data)
972bf1ab978SDwayne Grant McConnell {
973bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
974bf1ab978SDwayne Grant McConnell 	return ctx->ops->signal2_type_get(ctx);
975bf1ab978SDwayne Grant McConnell }
976bf1ab978SDwayne Grant McConnell 
97767207b96SArnd Bergmann static u64 spufs_signal2_type_get(void *data)
97867207b96SArnd Bergmann {
97967207b96SArnd Bergmann 	struct spu_context *ctx = data;
9808b3d6663SArnd Bergmann 	u64 ret;
9818b3d6663SArnd Bergmann 
9828b3d6663SArnd Bergmann 	spu_acquire(ctx);
983bf1ab978SDwayne Grant McConnell 	ret = __spufs_signal2_type_get(data);
9848b3d6663SArnd Bergmann 	spu_release(ctx);
9858b3d6663SArnd Bergmann 
9868b3d6663SArnd Bergmann 	return ret;
98767207b96SArnd Bergmann }
98867207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
98967207b96SArnd Bergmann 					spufs_signal2_type_set, "%llu");
99067207b96SArnd Bergmann 
99127d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
99278bde53eSBenjamin Herrenschmidt static unsigned long spufs_mss_mmap_nopfn(struct vm_area_struct *vma,
99378bde53eSBenjamin Herrenschmidt 					  unsigned long address)
994d9379c4bSarnd@arndb.de {
99578bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x0000, 0x1000);
996d9379c4bSarnd@arndb.de }
997d9379c4bSarnd@arndb.de 
998d9379c4bSarnd@arndb.de static struct vm_operations_struct spufs_mss_mmap_vmops = {
99978bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mss_mmap_nopfn,
1000d9379c4bSarnd@arndb.de };
1001d9379c4bSarnd@arndb.de 
1002d9379c4bSarnd@arndb.de /*
1003d9379c4bSarnd@arndb.de  * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
1004d9379c4bSarnd@arndb.de  */
1005d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma)
1006d9379c4bSarnd@arndb.de {
1007d9379c4bSarnd@arndb.de 	if (!(vma->vm_flags & VM_SHARED))
1008d9379c4bSarnd@arndb.de 		return -EINVAL;
1009d9379c4bSarnd@arndb.de 
101078bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
1011d9379c4bSarnd@arndb.de 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
101223cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
1013d9379c4bSarnd@arndb.de 
1014d9379c4bSarnd@arndb.de 	vma->vm_ops = &spufs_mss_mmap_vmops;
1015d9379c4bSarnd@arndb.de 	return 0;
1016d9379c4bSarnd@arndb.de }
101727d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
101827d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL
101927d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
1020d9379c4bSarnd@arndb.de 
1021d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file)
1022d9379c4bSarnd@arndb.de {
1023d9379c4bSarnd@arndb.de 	struct spufs_inode_info *i = SPUFS_I(inode);
102417e0e270SBenjamin Herrenschmidt 	struct spu_context *ctx = i->i_ctx;
1025d9379c4bSarnd@arndb.de 
1026d9379c4bSarnd@arndb.de 	file->private_data = i->i_ctx;
102717e0e270SBenjamin Herrenschmidt 	ctx->mss = inode->i_mapping;
102817e0e270SBenjamin Herrenschmidt 	smp_wmb();
1029d9379c4bSarnd@arndb.de 	return nonseekable_open(inode, file);
1030d9379c4bSarnd@arndb.de }
1031d9379c4bSarnd@arndb.de 
10325dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = {
1033d9379c4bSarnd@arndb.de 	.open	 = spufs_mss_open,
1034d9379c4bSarnd@arndb.de 	.mmap	 = spufs_mss_mmap,
103527d5bf2aSBenjamin Herrenschmidt };
103627d5bf2aSBenjamin Herrenschmidt 
103778bde53eSBenjamin Herrenschmidt static unsigned long spufs_psmap_mmap_nopfn(struct vm_area_struct *vma,
103878bde53eSBenjamin Herrenschmidt 					    unsigned long address)
103927d5bf2aSBenjamin Herrenschmidt {
104078bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x0000, 0x20000);
104127d5bf2aSBenjamin Herrenschmidt }
104227d5bf2aSBenjamin Herrenschmidt 
104327d5bf2aSBenjamin Herrenschmidt static struct vm_operations_struct spufs_psmap_mmap_vmops = {
104478bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_psmap_mmap_nopfn,
104527d5bf2aSBenjamin Herrenschmidt };
104627d5bf2aSBenjamin Herrenschmidt 
104727d5bf2aSBenjamin Herrenschmidt /*
104827d5bf2aSBenjamin Herrenschmidt  * mmap support for full problem state area [0x00000 - 0x1ffff].
104927d5bf2aSBenjamin Herrenschmidt  */
105027d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
105127d5bf2aSBenjamin Herrenschmidt {
105227d5bf2aSBenjamin Herrenschmidt 	if (!(vma->vm_flags & VM_SHARED))
105327d5bf2aSBenjamin Herrenschmidt 		return -EINVAL;
105427d5bf2aSBenjamin Herrenschmidt 
105578bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
105627d5bf2aSBenjamin Herrenschmidt 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
105727d5bf2aSBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
105827d5bf2aSBenjamin Herrenschmidt 
105927d5bf2aSBenjamin Herrenschmidt 	vma->vm_ops = &spufs_psmap_mmap_vmops;
106027d5bf2aSBenjamin Herrenschmidt 	return 0;
106127d5bf2aSBenjamin Herrenschmidt }
106227d5bf2aSBenjamin Herrenschmidt 
106327d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file)
106427d5bf2aSBenjamin Herrenschmidt {
106527d5bf2aSBenjamin Herrenschmidt 	struct spufs_inode_info *i = SPUFS_I(inode);
106617e0e270SBenjamin Herrenschmidt 	struct spu_context *ctx = i->i_ctx;
106727d5bf2aSBenjamin Herrenschmidt 
106827d5bf2aSBenjamin Herrenschmidt 	file->private_data = i->i_ctx;
106917e0e270SBenjamin Herrenschmidt 	ctx->psmap = inode->i_mapping;
107017e0e270SBenjamin Herrenschmidt 	smp_wmb();
107127d5bf2aSBenjamin Herrenschmidt 	return nonseekable_open(inode, file);
107227d5bf2aSBenjamin Herrenschmidt }
107327d5bf2aSBenjamin Herrenschmidt 
10745dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = {
107527d5bf2aSBenjamin Herrenschmidt 	.open	 = spufs_psmap_open,
107627d5bf2aSBenjamin Herrenschmidt 	.mmap	 = spufs_psmap_mmap,
1077d9379c4bSarnd@arndb.de };
1078d9379c4bSarnd@arndb.de 
1079d9379c4bSarnd@arndb.de 
108027d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
108178bde53eSBenjamin Herrenschmidt static unsigned long spufs_mfc_mmap_nopfn(struct vm_area_struct *vma,
108278bde53eSBenjamin Herrenschmidt 					  unsigned long address)
10836df10a82SMark Nutter {
108478bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x3000, 0x1000);
10856df10a82SMark Nutter }
10866df10a82SMark Nutter 
10876df10a82SMark Nutter static struct vm_operations_struct spufs_mfc_mmap_vmops = {
108878bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mfc_mmap_nopfn,
10896df10a82SMark Nutter };
10906df10a82SMark Nutter 
10916df10a82SMark Nutter /*
10926df10a82SMark Nutter  * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
10936df10a82SMark Nutter  */
10946df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
10956df10a82SMark Nutter {
10966df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
10976df10a82SMark Nutter 		return -EINVAL;
10986df10a82SMark Nutter 
109978bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
11006df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
110123cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
11026df10a82SMark Nutter 
11036df10a82SMark Nutter 	vma->vm_ops = &spufs_mfc_mmap_vmops;
11046df10a82SMark Nutter 	return 0;
11056df10a82SMark Nutter }
110627d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
110727d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL
110827d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
1109a33a7d73SArnd Bergmann 
1110a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file)
1111a33a7d73SArnd Bergmann {
1112a33a7d73SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
1113a33a7d73SArnd Bergmann 	struct spu_context *ctx = i->i_ctx;
1114a33a7d73SArnd Bergmann 
1115a33a7d73SArnd Bergmann 	/* we don't want to deal with DMA into other processes */
1116a33a7d73SArnd Bergmann 	if (ctx->owner != current->mm)
1117a33a7d73SArnd Bergmann 		return -EINVAL;
1118a33a7d73SArnd Bergmann 
1119a33a7d73SArnd Bergmann 	if (atomic_read(&inode->i_count) != 1)
1120a33a7d73SArnd Bergmann 		return -EBUSY;
1121a33a7d73SArnd Bergmann 
1122a33a7d73SArnd Bergmann 	file->private_data = ctx;
112317e0e270SBenjamin Herrenschmidt 	ctx->mfc = inode->i_mapping;
112417e0e270SBenjamin Herrenschmidt 	smp_wmb();
1125a33a7d73SArnd Bergmann 	return nonseekable_open(inode, file);
1126a33a7d73SArnd Bergmann }
1127a33a7d73SArnd Bergmann 
1128a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */
1129a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu)
1130a33a7d73SArnd Bergmann {
1131a33a7d73SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
1132a33a7d73SArnd Bergmann 
1133a33a7d73SArnd Bergmann 	wake_up_all(&ctx->mfc_wq);
1134a33a7d73SArnd Bergmann 
1135a33a7d73SArnd Bergmann 	pr_debug("%s %s\n", __FUNCTION__, spu->name);
1136a33a7d73SArnd Bergmann 	if (ctx->mfc_fasync) {
1137a33a7d73SArnd Bergmann 		u32 free_elements, tagstatus;
1138a33a7d73SArnd Bergmann 		unsigned int mask;
1139a33a7d73SArnd Bergmann 
1140a33a7d73SArnd Bergmann 		/* no need for spu_acquire in interrupt context */
1141a33a7d73SArnd Bergmann 		free_elements = ctx->ops->get_mfc_free_elements(ctx);
1142a33a7d73SArnd Bergmann 		tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1143a33a7d73SArnd Bergmann 
1144a33a7d73SArnd Bergmann 		mask = 0;
1145a33a7d73SArnd Bergmann 		if (free_elements & 0xffff)
1146a33a7d73SArnd Bergmann 			mask |= POLLOUT;
1147a33a7d73SArnd Bergmann 		if (tagstatus & ctx->tagwait)
1148a33a7d73SArnd Bergmann 			mask |= POLLIN;
1149a33a7d73SArnd Bergmann 
1150a33a7d73SArnd Bergmann 		kill_fasync(&ctx->mfc_fasync, SIGIO, mask);
1151a33a7d73SArnd Bergmann 	}
1152a33a7d73SArnd Bergmann }
1153a33a7d73SArnd Bergmann 
1154a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status)
1155a33a7d73SArnd Bergmann {
1156a33a7d73SArnd Bergmann 	/* See if there is one tag group is complete */
1157a33a7d73SArnd Bergmann 	/* FIXME we need locking around tagwait */
1158a33a7d73SArnd Bergmann 	*status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait;
1159a33a7d73SArnd Bergmann 	ctx->tagwait &= ~*status;
1160a33a7d73SArnd Bergmann 	if (*status)
1161a33a7d73SArnd Bergmann 		return 1;
1162a33a7d73SArnd Bergmann 
1163a33a7d73SArnd Bergmann 	/* enable interrupt waiting for any tag group,
1164a33a7d73SArnd Bergmann 	   may silently fail if interrupts are already enabled */
1165a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1166a33a7d73SArnd Bergmann 	return 0;
1167a33a7d73SArnd Bergmann }
1168a33a7d73SArnd Bergmann 
1169a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer,
1170a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1171a33a7d73SArnd Bergmann {
1172a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1173a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1174a33a7d73SArnd Bergmann 	u32 status;
1175a33a7d73SArnd Bergmann 
1176a33a7d73SArnd Bergmann 	if (size != 4)
1177a33a7d73SArnd Bergmann 		goto out;
1178a33a7d73SArnd Bergmann 
1179a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1180a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1181a33a7d73SArnd Bergmann 		status = ctx->ops->read_mfc_tagstatus(ctx);
1182a33a7d73SArnd Bergmann 		if (!(status & ctx->tagwait))
1183a33a7d73SArnd Bergmann 			ret = -EAGAIN;
1184a33a7d73SArnd Bergmann 		else
1185a33a7d73SArnd Bergmann 			ctx->tagwait &= ~status;
1186a33a7d73SArnd Bergmann 	} else {
1187a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1188a33a7d73SArnd Bergmann 			   spufs_read_mfc_tagstatus(ctx, &status));
1189a33a7d73SArnd Bergmann 	}
1190a33a7d73SArnd Bergmann 	spu_release(ctx);
1191a33a7d73SArnd Bergmann 
1192a33a7d73SArnd Bergmann 	if (ret)
1193a33a7d73SArnd Bergmann 		goto out;
1194a33a7d73SArnd Bergmann 
1195a33a7d73SArnd Bergmann 	ret = 4;
1196a33a7d73SArnd Bergmann 	if (copy_to_user(buffer, &status, 4))
1197a33a7d73SArnd Bergmann 		ret = -EFAULT;
1198a33a7d73SArnd Bergmann 
1199a33a7d73SArnd Bergmann out:
1200a33a7d73SArnd Bergmann 	return ret;
1201a33a7d73SArnd Bergmann }
1202a33a7d73SArnd Bergmann 
1203a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd)
1204a33a7d73SArnd Bergmann {
1205a33a7d73SArnd Bergmann 	pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa,
1206a33a7d73SArnd Bergmann 		 cmd->ea, cmd->size, cmd->tag, cmd->cmd);
1207a33a7d73SArnd Bergmann 
1208a33a7d73SArnd Bergmann 	switch (cmd->cmd) {
1209a33a7d73SArnd Bergmann 	case MFC_PUT_CMD:
1210a33a7d73SArnd Bergmann 	case MFC_PUTF_CMD:
1211a33a7d73SArnd Bergmann 	case MFC_PUTB_CMD:
1212a33a7d73SArnd Bergmann 	case MFC_GET_CMD:
1213a33a7d73SArnd Bergmann 	case MFC_GETF_CMD:
1214a33a7d73SArnd Bergmann 	case MFC_GETB_CMD:
1215a33a7d73SArnd Bergmann 		break;
1216a33a7d73SArnd Bergmann 	default:
1217a33a7d73SArnd Bergmann 		pr_debug("invalid DMA opcode %x\n", cmd->cmd);
1218a33a7d73SArnd Bergmann 		return -EIO;
1219a33a7d73SArnd Bergmann 	}
1220a33a7d73SArnd Bergmann 
1221a33a7d73SArnd Bergmann 	if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) {
1222a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment, ea %lx lsa %x\n",
1223a33a7d73SArnd Bergmann 				cmd->ea, cmd->lsa);
1224a33a7d73SArnd Bergmann 		return -EIO;
1225a33a7d73SArnd Bergmann 	}
1226a33a7d73SArnd Bergmann 
1227a33a7d73SArnd Bergmann 	switch (cmd->size & 0xf) {
1228a33a7d73SArnd Bergmann 	case 1:
1229a33a7d73SArnd Bergmann 		break;
1230a33a7d73SArnd Bergmann 	case 2:
1231a33a7d73SArnd Bergmann 		if (cmd->lsa & 1)
1232a33a7d73SArnd Bergmann 			goto error;
1233a33a7d73SArnd Bergmann 		break;
1234a33a7d73SArnd Bergmann 	case 4:
1235a33a7d73SArnd Bergmann 		if (cmd->lsa & 3)
1236a33a7d73SArnd Bergmann 			goto error;
1237a33a7d73SArnd Bergmann 		break;
1238a33a7d73SArnd Bergmann 	case 8:
1239a33a7d73SArnd Bergmann 		if (cmd->lsa & 7)
1240a33a7d73SArnd Bergmann 			goto error;
1241a33a7d73SArnd Bergmann 		break;
1242a33a7d73SArnd Bergmann 	case 0:
1243a33a7d73SArnd Bergmann 		if (cmd->lsa & 15)
1244a33a7d73SArnd Bergmann 			goto error;
1245a33a7d73SArnd Bergmann 		break;
1246a33a7d73SArnd Bergmann 	error:
1247a33a7d73SArnd Bergmann 	default:
1248a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment %x for size %x\n",
1249a33a7d73SArnd Bergmann 			cmd->lsa & 0xf, cmd->size);
1250a33a7d73SArnd Bergmann 		return -EIO;
1251a33a7d73SArnd Bergmann 	}
1252a33a7d73SArnd Bergmann 
1253a33a7d73SArnd Bergmann 	if (cmd->size > 16 * 1024) {
1254a33a7d73SArnd Bergmann 		pr_debug("invalid DMA size %x\n", cmd->size);
1255a33a7d73SArnd Bergmann 		return -EIO;
1256a33a7d73SArnd Bergmann 	}
1257a33a7d73SArnd Bergmann 
1258a33a7d73SArnd Bergmann 	if (cmd->tag & 0xfff0) {
1259a33a7d73SArnd Bergmann 		/* we reserve the higher tag numbers for kernel use */
1260a33a7d73SArnd Bergmann 		pr_debug("invalid DMA tag\n");
1261a33a7d73SArnd Bergmann 		return -EIO;
1262a33a7d73SArnd Bergmann 	}
1263a33a7d73SArnd Bergmann 
1264a33a7d73SArnd Bergmann 	if (cmd->class) {
1265a33a7d73SArnd Bergmann 		/* not supported in this version */
1266a33a7d73SArnd Bergmann 		pr_debug("invalid DMA class\n");
1267a33a7d73SArnd Bergmann 		return -EIO;
1268a33a7d73SArnd Bergmann 	}
1269a33a7d73SArnd Bergmann 
1270a33a7d73SArnd Bergmann 	return 0;
1271a33a7d73SArnd Bergmann }
1272a33a7d73SArnd Bergmann 
1273a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx,
1274a33a7d73SArnd Bergmann 				struct mfc_dma_command cmd,
1275a33a7d73SArnd Bergmann 				int *error)
1276a33a7d73SArnd Bergmann {
1277a33a7d73SArnd Bergmann 	*error = ctx->ops->send_mfc_command(ctx, &cmd);
1278a33a7d73SArnd Bergmann 	if (*error == -EAGAIN) {
1279a33a7d73SArnd Bergmann 		/* wait for any tag group to complete
1280a33a7d73SArnd Bergmann 		   so we have space for the new command */
1281a33a7d73SArnd Bergmann 		ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1282a33a7d73SArnd Bergmann 		/* try again, because the queue might be
1283a33a7d73SArnd Bergmann 		   empty again */
1284a33a7d73SArnd Bergmann 		*error = ctx->ops->send_mfc_command(ctx, &cmd);
1285a33a7d73SArnd Bergmann 		if (*error == -EAGAIN)
1286a33a7d73SArnd Bergmann 			return 0;
1287a33a7d73SArnd Bergmann 	}
1288a33a7d73SArnd Bergmann 	return 1;
1289a33a7d73SArnd Bergmann }
1290a33a7d73SArnd Bergmann 
1291a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer,
1292a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1293a33a7d73SArnd Bergmann {
1294a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1295a33a7d73SArnd Bergmann 	struct mfc_dma_command cmd;
1296a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1297a33a7d73SArnd Bergmann 
1298a33a7d73SArnd Bergmann 	if (size != sizeof cmd)
1299a33a7d73SArnd Bergmann 		goto out;
1300a33a7d73SArnd Bergmann 
1301a33a7d73SArnd Bergmann 	ret = -EFAULT;
1302a33a7d73SArnd Bergmann 	if (copy_from_user(&cmd, buffer, sizeof cmd))
1303a33a7d73SArnd Bergmann 		goto out;
1304a33a7d73SArnd Bergmann 
1305a33a7d73SArnd Bergmann 	ret = spufs_check_valid_dma(&cmd);
1306a33a7d73SArnd Bergmann 	if (ret)
1307a33a7d73SArnd Bergmann 		goto out;
1308a33a7d73SArnd Bergmann 
1309a33a7d73SArnd Bergmann 	spu_acquire_runnable(ctx);
1310a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1311a33a7d73SArnd Bergmann 		ret = ctx->ops->send_mfc_command(ctx, &cmd);
1312a33a7d73SArnd Bergmann 	} else {
1313a33a7d73SArnd Bergmann 		int status;
1314a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1315a33a7d73SArnd Bergmann 				 spu_send_mfc_command(ctx, cmd, &status));
1316a33a7d73SArnd Bergmann 		if (status)
1317a33a7d73SArnd Bergmann 			ret = status;
1318a33a7d73SArnd Bergmann 	}
1319a33a7d73SArnd Bergmann 	spu_release(ctx);
1320a33a7d73SArnd Bergmann 
1321a33a7d73SArnd Bergmann 	if (ret)
1322a33a7d73SArnd Bergmann 		goto out;
1323a33a7d73SArnd Bergmann 
1324a33a7d73SArnd Bergmann 	ctx->tagwait |= 1 << cmd.tag;
13253692dc66SMasato Noguchi 	ret = size;
1326a33a7d73SArnd Bergmann 
1327a33a7d73SArnd Bergmann out:
1328a33a7d73SArnd Bergmann 	return ret;
1329a33a7d73SArnd Bergmann }
1330a33a7d73SArnd Bergmann 
1331a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait)
1332a33a7d73SArnd Bergmann {
1333a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1334a33a7d73SArnd Bergmann 	u32 free_elements, tagstatus;
1335a33a7d73SArnd Bergmann 	unsigned int mask;
1336a33a7d73SArnd Bergmann 
1337a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1338a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2);
1339a33a7d73SArnd Bergmann 	free_elements = ctx->ops->get_mfc_free_elements(ctx);
1340a33a7d73SArnd Bergmann 	tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1341a33a7d73SArnd Bergmann 	spu_release(ctx);
1342a33a7d73SArnd Bergmann 
1343a33a7d73SArnd Bergmann 	poll_wait(file, &ctx->mfc_wq, wait);
1344a33a7d73SArnd Bergmann 
1345a33a7d73SArnd Bergmann 	mask = 0;
1346a33a7d73SArnd Bergmann 	if (free_elements & 0xffff)
1347a33a7d73SArnd Bergmann 		mask |= POLLOUT | POLLWRNORM;
1348a33a7d73SArnd Bergmann 	if (tagstatus & ctx->tagwait)
1349a33a7d73SArnd Bergmann 		mask |= POLLIN | POLLRDNORM;
1350a33a7d73SArnd Bergmann 
1351a33a7d73SArnd Bergmann 	pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__,
1352a33a7d73SArnd Bergmann 		free_elements, tagstatus, ctx->tagwait);
1353a33a7d73SArnd Bergmann 
1354a33a7d73SArnd Bergmann 	return mask;
1355a33a7d73SArnd Bergmann }
1356a33a7d73SArnd Bergmann 
135773b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id)
1358a33a7d73SArnd Bergmann {
1359a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1360a33a7d73SArnd Bergmann 	int ret;
1361a33a7d73SArnd Bergmann 
1362a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1363a33a7d73SArnd Bergmann #if 0
1364a33a7d73SArnd Bergmann /* this currently hangs */
1365a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1366a33a7d73SArnd Bergmann 			 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2));
1367a33a7d73SArnd Bergmann 	if (ret)
1368a33a7d73SArnd Bergmann 		goto out;
1369a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1370a33a7d73SArnd Bergmann 			 ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait);
1371a33a7d73SArnd Bergmann out:
1372a33a7d73SArnd Bergmann #else
1373a33a7d73SArnd Bergmann 	ret = 0;
1374a33a7d73SArnd Bergmann #endif
1375a33a7d73SArnd Bergmann 	spu_release(ctx);
1376a33a7d73SArnd Bergmann 
1377a33a7d73SArnd Bergmann 	return ret;
1378a33a7d73SArnd Bergmann }
1379a33a7d73SArnd Bergmann 
1380a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry,
1381a33a7d73SArnd Bergmann 			   int datasync)
1382a33a7d73SArnd Bergmann {
138373b6af8aSAl Viro 	return spufs_mfc_flush(file, NULL);
1384a33a7d73SArnd Bergmann }
1385a33a7d73SArnd Bergmann 
1386a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on)
1387a33a7d73SArnd Bergmann {
1388a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1389a33a7d73SArnd Bergmann 
1390a33a7d73SArnd Bergmann 	return fasync_helper(fd, file, on, &ctx->mfc_fasync);
1391a33a7d73SArnd Bergmann }
1392a33a7d73SArnd Bergmann 
13935dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = {
1394a33a7d73SArnd Bergmann 	.open	 = spufs_mfc_open,
1395a33a7d73SArnd Bergmann 	.read	 = spufs_mfc_read,
1396a33a7d73SArnd Bergmann 	.write	 = spufs_mfc_write,
1397a33a7d73SArnd Bergmann 	.poll	 = spufs_mfc_poll,
1398a33a7d73SArnd Bergmann 	.flush	 = spufs_mfc_flush,
1399a33a7d73SArnd Bergmann 	.fsync	 = spufs_mfc_fsync,
1400a33a7d73SArnd Bergmann 	.fasync	 = spufs_mfc_fasync,
14016df10a82SMark Nutter 	.mmap	 = spufs_mfc_mmap,
1402a33a7d73SArnd Bergmann };
1403a33a7d73SArnd Bergmann 
140467207b96SArnd Bergmann static void spufs_npc_set(void *data, u64 val)
140567207b96SArnd Bergmann {
140667207b96SArnd Bergmann 	struct spu_context *ctx = data;
14078b3d6663SArnd Bergmann 	spu_acquire(ctx);
14088b3d6663SArnd Bergmann 	ctx->ops->npc_write(ctx, val);
14098b3d6663SArnd Bergmann 	spu_release(ctx);
141067207b96SArnd Bergmann }
141167207b96SArnd Bergmann 
141267207b96SArnd Bergmann static u64 spufs_npc_get(void *data)
141367207b96SArnd Bergmann {
141467207b96SArnd Bergmann 	struct spu_context *ctx = data;
141567207b96SArnd Bergmann 	u64 ret;
14168b3d6663SArnd Bergmann 	spu_acquire(ctx);
14178b3d6663SArnd Bergmann 	ret = ctx->ops->npc_read(ctx);
14188b3d6663SArnd Bergmann 	spu_release(ctx);
141967207b96SArnd Bergmann 	return ret;
142067207b96SArnd Bergmann }
14219b5047e2SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set,
14229b5047e2SDwayne Grant McConnell 			"0x%llx\n")
142367207b96SArnd Bergmann 
14248b3d6663SArnd Bergmann static void spufs_decr_set(void *data, u64 val)
14258b3d6663SArnd Bergmann {
14268b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14278b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14288b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14298b3d6663SArnd Bergmann 	lscsa->decr.slot[0] = (u32) val;
14308b3d6663SArnd Bergmann 	spu_release(ctx);
14318b3d6663SArnd Bergmann }
14328b3d6663SArnd Bergmann 
1433bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_get(void *data)
14348b3d6663SArnd Bergmann {
14358b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14368b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1437bf1ab978SDwayne Grant McConnell 	return lscsa->decr.slot[0];
1438bf1ab978SDwayne Grant McConnell }
1439bf1ab978SDwayne Grant McConnell 
1440bf1ab978SDwayne Grant McConnell static u64 spufs_decr_get(void *data)
1441bf1ab978SDwayne Grant McConnell {
1442bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14438b3d6663SArnd Bergmann 	u64 ret;
14448b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1445bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_get(data);
14468b3d6663SArnd Bergmann 	spu_release(ctx);
14478b3d6663SArnd Bergmann 	return ret;
14488b3d6663SArnd Bergmann }
14498b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set,
14509b5047e2SDwayne Grant McConnell 			"0x%llx\n")
14518b3d6663SArnd Bergmann 
14528b3d6663SArnd Bergmann static void spufs_decr_status_set(void *data, u64 val)
14538b3d6663SArnd Bergmann {
14548b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14558b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14568b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14578b3d6663SArnd Bergmann 	lscsa->decr_status.slot[0] = (u32) val;
14588b3d6663SArnd Bergmann 	spu_release(ctx);
14598b3d6663SArnd Bergmann }
14608b3d6663SArnd Bergmann 
1461bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_status_get(void *data)
14628b3d6663SArnd Bergmann {
14638b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14648b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1465bf1ab978SDwayne Grant McConnell 	return lscsa->decr_status.slot[0];
1466bf1ab978SDwayne Grant McConnell }
1467bf1ab978SDwayne Grant McConnell 
1468bf1ab978SDwayne Grant McConnell static u64 spufs_decr_status_get(void *data)
1469bf1ab978SDwayne Grant McConnell {
1470bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14718b3d6663SArnd Bergmann 	u64 ret;
14728b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1473bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_status_get(data);
14748b3d6663SArnd Bergmann 	spu_release(ctx);
14758b3d6663SArnd Bergmann 	return ret;
14768b3d6663SArnd Bergmann }
14778b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get,
14789b5047e2SDwayne Grant McConnell 			spufs_decr_status_set, "0x%llx\n")
14798b3d6663SArnd Bergmann 
14808b3d6663SArnd Bergmann static void spufs_event_mask_set(void *data, u64 val)
14818b3d6663SArnd Bergmann {
14828b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14838b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14848b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14858b3d6663SArnd Bergmann 	lscsa->event_mask.slot[0] = (u32) val;
14868b3d6663SArnd Bergmann 	spu_release(ctx);
14878b3d6663SArnd Bergmann }
14888b3d6663SArnd Bergmann 
1489bf1ab978SDwayne Grant McConnell static u64 __spufs_event_mask_get(void *data)
14908b3d6663SArnd Bergmann {
14918b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14928b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1493bf1ab978SDwayne Grant McConnell 	return lscsa->event_mask.slot[0];
1494bf1ab978SDwayne Grant McConnell }
1495bf1ab978SDwayne Grant McConnell 
1496bf1ab978SDwayne Grant McConnell static u64 spufs_event_mask_get(void *data)
1497bf1ab978SDwayne Grant McConnell {
1498bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14998b3d6663SArnd Bergmann 	u64 ret;
15008b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1501bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_mask_get(data);
15028b3d6663SArnd Bergmann 	spu_release(ctx);
15038b3d6663SArnd Bergmann 	return ret;
15048b3d6663SArnd Bergmann }
15058b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get,
15069b5047e2SDwayne Grant McConnell 			spufs_event_mask_set, "0x%llx\n")
15078b3d6663SArnd Bergmann 
1508bf1ab978SDwayne Grant McConnell static u64 __spufs_event_status_get(void *data)
1509b9e3bd77SDwayne Grant McConnell {
1510b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1511b9e3bd77SDwayne Grant McConnell 	struct spu_state *state = &ctx->csa;
1512b9e3bd77SDwayne Grant McConnell 	u64 stat;
1513b9e3bd77SDwayne Grant McConnell 	stat = state->spu_chnlcnt_RW[0];
1514b9e3bd77SDwayne Grant McConnell 	if (stat)
1515bf1ab978SDwayne Grant McConnell 		return state->spu_chnldata_RW[0];
1516bf1ab978SDwayne Grant McConnell 	return 0;
1517bf1ab978SDwayne Grant McConnell }
1518bf1ab978SDwayne Grant McConnell 
1519bf1ab978SDwayne Grant McConnell static u64 spufs_event_status_get(void *data)
1520bf1ab978SDwayne Grant McConnell {
1521bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1522bf1ab978SDwayne Grant McConnell 	u64 ret = 0;
1523bf1ab978SDwayne Grant McConnell 
1524bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1525bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_status_get(data);
1526b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1527b9e3bd77SDwayne Grant McConnell 	return ret;
1528b9e3bd77SDwayne Grant McConnell }
1529b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get,
1530b9e3bd77SDwayne Grant McConnell 			NULL, "0x%llx\n")
1531b9e3bd77SDwayne Grant McConnell 
15328b3d6663SArnd Bergmann static void spufs_srr0_set(void *data, u64 val)
15338b3d6663SArnd Bergmann {
15348b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15358b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15368b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15378b3d6663SArnd Bergmann 	lscsa->srr0.slot[0] = (u32) val;
15388b3d6663SArnd Bergmann 	spu_release(ctx);
15398b3d6663SArnd Bergmann }
15408b3d6663SArnd Bergmann 
15418b3d6663SArnd Bergmann static u64 spufs_srr0_get(void *data)
15428b3d6663SArnd Bergmann {
15438b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15448b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15458b3d6663SArnd Bergmann 	u64 ret;
15468b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15478b3d6663SArnd Bergmann 	ret = lscsa->srr0.slot[0];
15488b3d6663SArnd Bergmann 	spu_release(ctx);
15498b3d6663SArnd Bergmann 	return ret;
15508b3d6663SArnd Bergmann }
15518b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
15529b5047e2SDwayne Grant McConnell 			"0x%llx\n")
15538b3d6663SArnd Bergmann 
15547b1a7014Sarnd@arndb.de static u64 spufs_id_get(void *data)
15557b1a7014Sarnd@arndb.de {
15567b1a7014Sarnd@arndb.de 	struct spu_context *ctx = data;
15577b1a7014Sarnd@arndb.de 	u64 num;
15587b1a7014Sarnd@arndb.de 
15597b1a7014Sarnd@arndb.de 	spu_acquire(ctx);
15607b1a7014Sarnd@arndb.de 	if (ctx->state == SPU_STATE_RUNNABLE)
15617b1a7014Sarnd@arndb.de 		num = ctx->spu->number;
15627b1a7014Sarnd@arndb.de 	else
15637b1a7014Sarnd@arndb.de 		num = (unsigned int)-1;
15647b1a7014Sarnd@arndb.de 	spu_release(ctx);
15657b1a7014Sarnd@arndb.de 
15667b1a7014Sarnd@arndb.de 	return num;
15677b1a7014Sarnd@arndb.de }
1568e45d6634SAl Viro DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n")
15697b1a7014Sarnd@arndb.de 
1570bf1ab978SDwayne Grant McConnell static u64 __spufs_object_id_get(void *data)
157186767277SArnd Bergmann {
157286767277SArnd Bergmann 	struct spu_context *ctx = data;
157386767277SArnd Bergmann 	return ctx->object_id;
157486767277SArnd Bergmann }
157586767277SArnd Bergmann 
1576bf1ab978SDwayne Grant McConnell static u64 spufs_object_id_get(void *data)
1577bf1ab978SDwayne Grant McConnell {
1578bf1ab978SDwayne Grant McConnell 	/* FIXME: Should there really be no locking here? */
1579bf1ab978SDwayne Grant McConnell 	return __spufs_object_id_get(data);
1580bf1ab978SDwayne Grant McConnell }
1581bf1ab978SDwayne Grant McConnell 
158286767277SArnd Bergmann static void spufs_object_id_set(void *data, u64 id)
158386767277SArnd Bergmann {
158486767277SArnd Bergmann 	struct spu_context *ctx = data;
158586767277SArnd Bergmann 	ctx->object_id = id;
158686767277SArnd Bergmann }
158786767277SArnd Bergmann 
158886767277SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get,
158986767277SArnd Bergmann 		spufs_object_id_set, "0x%llx\n");
159086767277SArnd Bergmann 
1591bf1ab978SDwayne Grant McConnell static u64 __spufs_lslr_get(void *data)
1592bf1ab978SDwayne Grant McConnell {
1593bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1594bf1ab978SDwayne Grant McConnell 	return ctx->csa.priv2.spu_lslr_RW;
1595bf1ab978SDwayne Grant McConnell }
1596bf1ab978SDwayne Grant McConnell 
1597b9e3bd77SDwayne Grant McConnell static u64 spufs_lslr_get(void *data)
1598b9e3bd77SDwayne Grant McConnell {
1599b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1600b9e3bd77SDwayne Grant McConnell 	u64 ret;
1601b9e3bd77SDwayne Grant McConnell 
1602b9e3bd77SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1603bf1ab978SDwayne Grant McConnell 	ret = __spufs_lslr_get(data);
1604b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1605b9e3bd77SDwayne Grant McConnell 
1606b9e3bd77SDwayne Grant McConnell 	return ret;
1607b9e3bd77SDwayne Grant McConnell }
1608b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n")
1609b9e3bd77SDwayne Grant McConnell 
1610b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file)
1611b9e3bd77SDwayne Grant McConnell {
1612b9e3bd77SDwayne Grant McConnell 	struct spufs_inode_info *i = SPUFS_I(inode);
1613b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = i->i_ctx;
1614b9e3bd77SDwayne Grant McConnell 	file->private_data = ctx;
1615b9e3bd77SDwayne Grant McConnell 	return 0;
1616b9e3bd77SDwayne Grant McConnell }
1617b9e3bd77SDwayne Grant McConnell 
1618bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx,
1619bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1620bf1ab978SDwayne Grant McConnell {
1621bf1ab978SDwayne Grant McConnell 	u32 mbox_stat;
1622bf1ab978SDwayne Grant McConnell 	u32 data;
1623bf1ab978SDwayne Grant McConnell 
1624bf1ab978SDwayne Grant McConnell 	mbox_stat = ctx->csa.prob.mb_stat_R;
1625bf1ab978SDwayne Grant McConnell 	if (mbox_stat & 0x0000ff) {
1626bf1ab978SDwayne Grant McConnell 		data = ctx->csa.prob.pu_mb_R;
1627bf1ab978SDwayne Grant McConnell 	}
1628bf1ab978SDwayne Grant McConnell 
1629bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1630bf1ab978SDwayne Grant McConnell }
1631bf1ab978SDwayne Grant McConnell 
163269a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
163369a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
163469a2f00cSDwayne Grant McConnell {
1635bf1ab978SDwayne Grant McConnell 	int ret;
163669a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
163769a2f00cSDwayne Grant McConnell 
163869a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
163969a2f00cSDwayne Grant McConnell 		return -EFAULT;
164069a2f00cSDwayne Grant McConnell 
164169a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
164269a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1643bf1ab978SDwayne Grant McConnell 	ret = __spufs_mbox_info_read(ctx, buf, len, pos);
164469a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
164569a2f00cSDwayne Grant McConnell 	spu_release(ctx);
164669a2f00cSDwayne Grant McConnell 
1647bf1ab978SDwayne Grant McConnell 	return ret;
164869a2f00cSDwayne Grant McConnell }
164969a2f00cSDwayne Grant McConnell 
16505dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = {
165169a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
165269a2f00cSDwayne Grant McConnell 	.read = spufs_mbox_info_read,
165369a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
165469a2f00cSDwayne Grant McConnell };
165569a2f00cSDwayne Grant McConnell 
1656bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx,
1657bf1ab978SDwayne Grant McConnell 				char __user *buf, size_t len, loff_t *pos)
1658bf1ab978SDwayne Grant McConnell {
1659bf1ab978SDwayne Grant McConnell 	u32 ibox_stat;
1660bf1ab978SDwayne Grant McConnell 	u32 data;
1661bf1ab978SDwayne Grant McConnell 
1662bf1ab978SDwayne Grant McConnell 	ibox_stat = ctx->csa.prob.mb_stat_R;
1663bf1ab978SDwayne Grant McConnell 	if (ibox_stat & 0xff0000) {
1664bf1ab978SDwayne Grant McConnell 		data = ctx->csa.priv2.puint_mb_R;
1665bf1ab978SDwayne Grant McConnell 	}
1666bf1ab978SDwayne Grant McConnell 
1667bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1668bf1ab978SDwayne Grant McConnell }
1669bf1ab978SDwayne Grant McConnell 
167069a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
167169a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
167269a2f00cSDwayne Grant McConnell {
167369a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1674bf1ab978SDwayne Grant McConnell 	int ret;
167569a2f00cSDwayne Grant McConnell 
167669a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
167769a2f00cSDwayne Grant McConnell 		return -EFAULT;
167869a2f00cSDwayne Grant McConnell 
167969a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
168069a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1681bf1ab978SDwayne Grant McConnell 	ret = __spufs_ibox_info_read(ctx, buf, len, pos);
168269a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
168369a2f00cSDwayne Grant McConnell 	spu_release(ctx);
168469a2f00cSDwayne Grant McConnell 
1685bf1ab978SDwayne Grant McConnell 	return ret;
168669a2f00cSDwayne Grant McConnell }
168769a2f00cSDwayne Grant McConnell 
16885dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = {
168969a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
169069a2f00cSDwayne Grant McConnell 	.read = spufs_ibox_info_read,
169169a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
169269a2f00cSDwayne Grant McConnell };
169369a2f00cSDwayne Grant McConnell 
1694bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx,
1695bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1696bf1ab978SDwayne Grant McConnell {
1697bf1ab978SDwayne Grant McConnell 	int i, cnt;
1698bf1ab978SDwayne Grant McConnell 	u32 data[4];
1699bf1ab978SDwayne Grant McConnell 	u32 wbox_stat;
1700bf1ab978SDwayne Grant McConnell 
1701bf1ab978SDwayne Grant McConnell 	wbox_stat = ctx->csa.prob.mb_stat_R;
1702bf1ab978SDwayne Grant McConnell 	cnt = 4 - ((wbox_stat & 0x00ff00) >> 8);
1703bf1ab978SDwayne Grant McConnell 	for (i = 0; i < cnt; i++) {
1704bf1ab978SDwayne Grant McConnell 		data[i] = ctx->csa.spu_mailbox_data[i];
1705bf1ab978SDwayne Grant McConnell 	}
1706bf1ab978SDwayne Grant McConnell 
1707bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data,
1708bf1ab978SDwayne Grant McConnell 				cnt * sizeof(u32));
1709bf1ab978SDwayne Grant McConnell }
1710bf1ab978SDwayne Grant McConnell 
171169a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
171269a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
171369a2f00cSDwayne Grant McConnell {
171469a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1715bf1ab978SDwayne Grant McConnell 	int ret;
171669a2f00cSDwayne Grant McConnell 
171769a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
171869a2f00cSDwayne Grant McConnell 		return -EFAULT;
171969a2f00cSDwayne Grant McConnell 
172069a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
172169a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1722bf1ab978SDwayne Grant McConnell 	ret = __spufs_wbox_info_read(ctx, buf, len, pos);
172369a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
172469a2f00cSDwayne Grant McConnell 	spu_release(ctx);
172569a2f00cSDwayne Grant McConnell 
1726bf1ab978SDwayne Grant McConnell 	return ret;
172769a2f00cSDwayne Grant McConnell }
172869a2f00cSDwayne Grant McConnell 
17295dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = {
173069a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
173169a2f00cSDwayne Grant McConnell 	.read = spufs_wbox_info_read,
173269a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
173369a2f00cSDwayne Grant McConnell };
173469a2f00cSDwayne Grant McConnell 
1735bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx,
1736bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1737b9e3bd77SDwayne Grant McConnell {
1738b9e3bd77SDwayne Grant McConnell 	struct spu_dma_info info;
1739b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *spuqp;
1740b9e3bd77SDwayne Grant McConnell 	int i;
1741b9e3bd77SDwayne Grant McConnell 
1742b9e3bd77SDwayne Grant McConnell 	info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
1743b9e3bd77SDwayne Grant McConnell 	info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
1744b9e3bd77SDwayne Grant McConnell 	info.dma_info_status = ctx->csa.spu_chnldata_RW[24];
1745b9e3bd77SDwayne Grant McConnell 	info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
1746b9e3bd77SDwayne Grant McConnell 	info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
1747b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 16; i++) {
1748b9e3bd77SDwayne Grant McConnell 		qp = &info.dma_info_command_data[i];
1749b9e3bd77SDwayne Grant McConnell 		spuqp = &ctx->csa.priv2.spuq[i];
1750b9e3bd77SDwayne Grant McConnell 
1751b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW;
1752b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW;
1753b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW;
1754b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW;
1755b9e3bd77SDwayne Grant McConnell 	}
1756b9e3bd77SDwayne Grant McConnell 
1757b9e3bd77SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1758b9e3bd77SDwayne Grant McConnell 				sizeof info);
1759b9e3bd77SDwayne Grant McConnell }
1760b9e3bd77SDwayne Grant McConnell 
1761bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
1762bf1ab978SDwayne Grant McConnell 			      size_t len, loff_t *pos)
1763bf1ab978SDwayne Grant McConnell {
1764bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1765bf1ab978SDwayne Grant McConnell 	int ret;
1766bf1ab978SDwayne Grant McConnell 
1767bf1ab978SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1768bf1ab978SDwayne Grant McConnell 		return -EFAULT;
1769bf1ab978SDwayne Grant McConnell 
1770bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1771bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1772bf1ab978SDwayne Grant McConnell 	ret = __spufs_dma_info_read(ctx, buf, len, pos);
1773bf1ab978SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1774bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
1775bf1ab978SDwayne Grant McConnell 
1776bf1ab978SDwayne Grant McConnell 	return ret;
1777bf1ab978SDwayne Grant McConnell }
1778bf1ab978SDwayne Grant McConnell 
17795dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = {
1780b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1781b9e3bd77SDwayne Grant McConnell 	.read = spufs_dma_info_read,
1782b9e3bd77SDwayne Grant McConnell };
1783b9e3bd77SDwayne Grant McConnell 
1784bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
1785bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1786b9e3bd77SDwayne Grant McConnell {
1787b9e3bd77SDwayne Grant McConnell 	struct spu_proxydma_info info;
1788b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *puqp;
1789bf1ab978SDwayne Grant McConnell 	int ret = sizeof info;
1790b9e3bd77SDwayne Grant McConnell 	int i;
1791b9e3bd77SDwayne Grant McConnell 
1792b9e3bd77SDwayne Grant McConnell 	if (len < ret)
1793b9e3bd77SDwayne Grant McConnell 		return -EINVAL;
1794b9e3bd77SDwayne Grant McConnell 
1795b9e3bd77SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1796b9e3bd77SDwayne Grant McConnell 		return -EFAULT;
1797b9e3bd77SDwayne Grant McConnell 
1798b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
1799b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
1800b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
1801b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 8; i++) {
1802b9e3bd77SDwayne Grant McConnell 		qp = &info.proxydma_info_command_data[i];
1803b9e3bd77SDwayne Grant McConnell 		puqp = &ctx->csa.priv2.puq[i];
1804b9e3bd77SDwayne Grant McConnell 
1805b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
1806b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
1807b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
1808b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
1809b9e3bd77SDwayne Grant McConnell 	}
1810bf1ab978SDwayne Grant McConnell 
1811bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1812bf1ab978SDwayne Grant McConnell 				sizeof info);
1813bf1ab978SDwayne Grant McConnell }
1814bf1ab978SDwayne Grant McConnell 
1815bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
1816bf1ab978SDwayne Grant McConnell 				   size_t len, loff_t *pos)
1817bf1ab978SDwayne Grant McConnell {
1818bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1819bf1ab978SDwayne Grant McConnell 	int ret;
1820bf1ab978SDwayne Grant McConnell 
1821bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1822bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1823bf1ab978SDwayne Grant McConnell 	ret = __spufs_proxydma_info_read(ctx, buf, len, pos);
1824b9e3bd77SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1825b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1826b9e3bd77SDwayne Grant McConnell 
1827b9e3bd77SDwayne Grant McConnell 	return ret;
1828b9e3bd77SDwayne Grant McConnell }
1829b9e3bd77SDwayne Grant McConnell 
18305dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = {
1831b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1832b9e3bd77SDwayne Grant McConnell 	.read = spufs_proxydma_info_read,
1833b9e3bd77SDwayne Grant McConnell };
1834b9e3bd77SDwayne Grant McConnell 
183567207b96SArnd Bergmann struct tree_descr spufs_dir_contents[] = {
183667207b96SArnd Bergmann 	{ "mem",  &spufs_mem_fops,  0666, },
18378b3d6663SArnd Bergmann 	{ "regs", &spufs_regs_fops,  0666, },
183867207b96SArnd Bergmann 	{ "mbox", &spufs_mbox_fops, 0444, },
183967207b96SArnd Bergmann 	{ "ibox", &spufs_ibox_fops, 0444, },
184067207b96SArnd Bergmann 	{ "wbox", &spufs_wbox_fops, 0222, },
184167207b96SArnd Bergmann 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
184267207b96SArnd Bergmann 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
184367207b96SArnd Bergmann 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
184467207b96SArnd Bergmann 	{ "signal1", &spufs_signal1_fops, 0666, },
184567207b96SArnd Bergmann 	{ "signal2", &spufs_signal2_fops, 0666, },
184667207b96SArnd Bergmann 	{ "signal1_type", &spufs_signal1_type, 0666, },
184767207b96SArnd Bergmann 	{ "signal2_type", &spufs_signal2_type, 0666, },
18486df10a82SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18498b3d6663SArnd Bergmann 	{ "fpcr", &spufs_fpcr_fops, 0666, },
1850b9e3bd77SDwayne Grant McConnell 	{ "lslr", &spufs_lslr_ops, 0444, },
1851b9e3bd77SDwayne Grant McConnell 	{ "mfc", &spufs_mfc_fops, 0666, },
1852b9e3bd77SDwayne Grant McConnell 	{ "mss", &spufs_mss_fops, 0666, },
1853b9e3bd77SDwayne Grant McConnell 	{ "npc", &spufs_npc_ops, 0666, },
1854b9e3bd77SDwayne Grant McConnell 	{ "srr0", &spufs_srr0_ops, 0666, },
18558b3d6663SArnd Bergmann 	{ "decr", &spufs_decr_ops, 0666, },
18568b3d6663SArnd Bergmann 	{ "decr_status", &spufs_decr_status_ops, 0666, },
18578b3d6663SArnd Bergmann 	{ "event_mask", &spufs_event_mask_ops, 0666, },
1858b9e3bd77SDwayne Grant McConnell 	{ "event_status", &spufs_event_status_ops, 0444, },
185927d5bf2aSBenjamin Herrenschmidt 	{ "psmap", &spufs_psmap_fops, 0666, },
186086767277SArnd Bergmann 	{ "phys-id", &spufs_id_ops, 0666, },
186186767277SArnd Bergmann 	{ "object-id", &spufs_object_id_ops, 0666, },
186269a2f00cSDwayne Grant McConnell 	{ "mbox_info", &spufs_mbox_info_fops, 0444, },
186369a2f00cSDwayne Grant McConnell 	{ "ibox_info", &spufs_ibox_info_fops, 0444, },
186469a2f00cSDwayne Grant McConnell 	{ "wbox_info", &spufs_wbox_info_fops, 0444, },
1865b9e3bd77SDwayne Grant McConnell 	{ "dma_info", &spufs_dma_info_fops, 0444, },
1866b9e3bd77SDwayne Grant McConnell 	{ "proxydma_info", &spufs_proxydma_info_fops, 0444, },
186767207b96SArnd Bergmann 	{},
186867207b96SArnd Bergmann };
18695737edd1SMark Nutter 
18705737edd1SMark Nutter struct tree_descr spufs_dir_nosched_contents[] = {
18715737edd1SMark Nutter 	{ "mem",  &spufs_mem_fops,  0666, },
18725737edd1SMark Nutter 	{ "mbox", &spufs_mbox_fops, 0444, },
18735737edd1SMark Nutter 	{ "ibox", &spufs_ibox_fops, 0444, },
18745737edd1SMark Nutter 	{ "wbox", &spufs_wbox_fops, 0222, },
18755737edd1SMark Nutter 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
18765737edd1SMark Nutter 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
18775737edd1SMark Nutter 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
18785737edd1SMark Nutter 	{ "signal1", &spufs_signal1_fops, 0666, },
18795737edd1SMark Nutter 	{ "signal2", &spufs_signal2_fops, 0666, },
18805737edd1SMark Nutter 	{ "signal1_type", &spufs_signal1_type, 0666, },
18815737edd1SMark Nutter 	{ "signal2_type", &spufs_signal2_type, 0666, },
18825737edd1SMark Nutter 	{ "mss", &spufs_mss_fops, 0666, },
18835737edd1SMark Nutter 	{ "mfc", &spufs_mfc_fops, 0666, },
18845737edd1SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18855737edd1SMark Nutter 	{ "npc", &spufs_npc_ops, 0666, },
18865737edd1SMark Nutter 	{ "psmap", &spufs_psmap_fops, 0666, },
18875737edd1SMark Nutter 	{ "phys-id", &spufs_id_ops, 0666, },
18885737edd1SMark Nutter 	{ "object-id", &spufs_object_id_ops, 0666, },
18895737edd1SMark Nutter 	{},
18905737edd1SMark Nutter };
1891bf1ab978SDwayne Grant McConnell 
1892bf1ab978SDwayne Grant McConnell struct spufs_coredump_reader spufs_coredump_read[] = {
1893bf1ab978SDwayne Grant McConnell 	{ "regs", __spufs_regs_read, NULL, 128 * 16 },
1894bf1ab978SDwayne Grant McConnell 	{ "fpcr", __spufs_fpcr_read, NULL, 16 },
1895bf1ab978SDwayne Grant McConnell 	{ "lslr", NULL, __spufs_lslr_get, 11 },
1896bf1ab978SDwayne Grant McConnell 	{ "decr", NULL, __spufs_decr_get, 11 },
1897bf1ab978SDwayne Grant McConnell 	{ "decr_status", NULL, __spufs_decr_status_get, 11 },
1898bf1ab978SDwayne Grant McConnell 	{ "mem", __spufs_mem_read, NULL, 256 * 1024, },
1899bf1ab978SDwayne Grant McConnell 	{ "signal1", __spufs_signal1_read, NULL, 4 },
1900bf1ab978SDwayne Grant McConnell 	{ "signal1_type", NULL, __spufs_signal1_type_get, 2 },
1901bf1ab978SDwayne Grant McConnell 	{ "signal2", __spufs_signal2_read, NULL, 4 },
1902bf1ab978SDwayne Grant McConnell 	{ "signal2_type", NULL, __spufs_signal2_type_get, 2 },
1903bf1ab978SDwayne Grant McConnell 	{ "event_mask", NULL, __spufs_event_mask_get, 8 },
1904bf1ab978SDwayne Grant McConnell 	{ "event_status", NULL, __spufs_event_status_get, 8 },
1905bf1ab978SDwayne Grant McConnell 	{ "mbox_info", __spufs_mbox_info_read, NULL, 4 },
1906bf1ab978SDwayne Grant McConnell 	{ "ibox_info", __spufs_ibox_info_read, NULL, 4 },
1907bf1ab978SDwayne Grant McConnell 	{ "wbox_info", __spufs_wbox_info_read, NULL, 16 },
1908bf1ab978SDwayne Grant McConnell 	{ "dma_info", __spufs_dma_info_read, NULL, 69 * 8 },
1909bf1ab978SDwayne Grant McConnell 	{ "proxydma_info", __spufs_proxydma_info_read, NULL, 35 * 8 },
1910bf1ab978SDwayne Grant McConnell 	{ "object-id", NULL, __spufs_object_id_get, 19 },
1911bf1ab978SDwayne Grant McConnell 	{ },
1912bf1ab978SDwayne Grant McConnell };
1913bf1ab978SDwayne Grant McConnell int spufs_coredump_num_notes = ARRAY_SIZE(spufs_coredump_read) - 1;
1914bf1ab978SDwayne Grant McConnell 
1915