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 	file->f_mapping = inode->i_mapping;
496df10a82SMark Nutter 	ctx->local_store = inode->i_mapping;
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 	file->f_mapping = inode->i_mapping;
2366df10a82SMark Nutter 	ctx->cntl = inode->i_mapping;
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 	file->f_mapping = inode->i_mapping;
7216df10a82SMark Nutter 	ctx->signal1 = inode->i_mapping;
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 	file->f_mapping = inode->i_mapping;
8286df10a82SMark Nutter 	ctx->signal2 = inode->i_mapping;
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);
1024d9379c4bSarnd@arndb.de 
1025d9379c4bSarnd@arndb.de 	file->private_data = i->i_ctx;
1026d9379c4bSarnd@arndb.de 	return nonseekable_open(inode, file);
1027d9379c4bSarnd@arndb.de }
1028d9379c4bSarnd@arndb.de 
10295dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = {
1030d9379c4bSarnd@arndb.de 	.open	 = spufs_mss_open,
1031d9379c4bSarnd@arndb.de 	.mmap	 = spufs_mss_mmap,
103227d5bf2aSBenjamin Herrenschmidt };
103327d5bf2aSBenjamin Herrenschmidt 
103478bde53eSBenjamin Herrenschmidt static unsigned long spufs_psmap_mmap_nopfn(struct vm_area_struct *vma,
103578bde53eSBenjamin Herrenschmidt 					    unsigned long address)
103627d5bf2aSBenjamin Herrenschmidt {
103778bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x0000, 0x20000);
103827d5bf2aSBenjamin Herrenschmidt }
103927d5bf2aSBenjamin Herrenschmidt 
104027d5bf2aSBenjamin Herrenschmidt static struct vm_operations_struct spufs_psmap_mmap_vmops = {
104178bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_psmap_mmap_nopfn,
104227d5bf2aSBenjamin Herrenschmidt };
104327d5bf2aSBenjamin Herrenschmidt 
104427d5bf2aSBenjamin Herrenschmidt /*
104527d5bf2aSBenjamin Herrenschmidt  * mmap support for full problem state area [0x00000 - 0x1ffff].
104627d5bf2aSBenjamin Herrenschmidt  */
104727d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
104827d5bf2aSBenjamin Herrenschmidt {
104927d5bf2aSBenjamin Herrenschmidt 	if (!(vma->vm_flags & VM_SHARED))
105027d5bf2aSBenjamin Herrenschmidt 		return -EINVAL;
105127d5bf2aSBenjamin Herrenschmidt 
105278bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
105327d5bf2aSBenjamin Herrenschmidt 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
105427d5bf2aSBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
105527d5bf2aSBenjamin Herrenschmidt 
105627d5bf2aSBenjamin Herrenschmidt 	vma->vm_ops = &spufs_psmap_mmap_vmops;
105727d5bf2aSBenjamin Herrenschmidt 	return 0;
105827d5bf2aSBenjamin Herrenschmidt }
105927d5bf2aSBenjamin Herrenschmidt 
106027d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file)
106127d5bf2aSBenjamin Herrenschmidt {
106227d5bf2aSBenjamin Herrenschmidt 	struct spufs_inode_info *i = SPUFS_I(inode);
106327d5bf2aSBenjamin Herrenschmidt 
106427d5bf2aSBenjamin Herrenschmidt 	file->private_data = i->i_ctx;
106527d5bf2aSBenjamin Herrenschmidt 	return nonseekable_open(inode, file);
106627d5bf2aSBenjamin Herrenschmidt }
106727d5bf2aSBenjamin Herrenschmidt 
10685dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = {
106927d5bf2aSBenjamin Herrenschmidt 	.open	 = spufs_psmap_open,
107027d5bf2aSBenjamin Herrenschmidt 	.mmap	 = spufs_psmap_mmap,
1071d9379c4bSarnd@arndb.de };
1072d9379c4bSarnd@arndb.de 
1073d9379c4bSarnd@arndb.de 
107427d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K
107578bde53eSBenjamin Herrenschmidt static unsigned long spufs_mfc_mmap_nopfn(struct vm_area_struct *vma,
107678bde53eSBenjamin Herrenschmidt 					  unsigned long address)
10776df10a82SMark Nutter {
107878bde53eSBenjamin Herrenschmidt 	return spufs_ps_nopfn(vma, address, 0x3000, 0x1000);
10796df10a82SMark Nutter }
10806df10a82SMark Nutter 
10816df10a82SMark Nutter static struct vm_operations_struct spufs_mfc_mmap_vmops = {
108278bde53eSBenjamin Herrenschmidt 	.nopfn = spufs_mfc_mmap_nopfn,
10836df10a82SMark Nutter };
10846df10a82SMark Nutter 
10856df10a82SMark Nutter /*
10866df10a82SMark Nutter  * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
10876df10a82SMark Nutter  */
10886df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
10896df10a82SMark Nutter {
10906df10a82SMark Nutter 	if (!(vma->vm_flags & VM_SHARED))
10916df10a82SMark Nutter 		return -EINVAL;
10926df10a82SMark Nutter 
109378bde53eSBenjamin Herrenschmidt 	vma->vm_flags |= VM_IO | VM_PFNMAP;
10946df10a82SMark Nutter 	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
109523cc7701SBenjamin Herrenschmidt 				     | _PAGE_NO_CACHE | _PAGE_GUARDED);
10966df10a82SMark Nutter 
10976df10a82SMark Nutter 	vma->vm_ops = &spufs_mfc_mmap_vmops;
10986df10a82SMark Nutter 	return 0;
10996df10a82SMark Nutter }
110027d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */
110127d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL
110227d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */
1103a33a7d73SArnd Bergmann 
1104a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file)
1105a33a7d73SArnd Bergmann {
1106a33a7d73SArnd Bergmann 	struct spufs_inode_info *i = SPUFS_I(inode);
1107a33a7d73SArnd Bergmann 	struct spu_context *ctx = i->i_ctx;
1108a33a7d73SArnd Bergmann 
1109a33a7d73SArnd Bergmann 	/* we don't want to deal with DMA into other processes */
1110a33a7d73SArnd Bergmann 	if (ctx->owner != current->mm)
1111a33a7d73SArnd Bergmann 		return -EINVAL;
1112a33a7d73SArnd Bergmann 
1113a33a7d73SArnd Bergmann 	if (atomic_read(&inode->i_count) != 1)
1114a33a7d73SArnd Bergmann 		return -EBUSY;
1115a33a7d73SArnd Bergmann 
1116a33a7d73SArnd Bergmann 	file->private_data = ctx;
1117a33a7d73SArnd Bergmann 	return nonseekable_open(inode, file);
1118a33a7d73SArnd Bergmann }
1119a33a7d73SArnd Bergmann 
1120a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */
1121a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu)
1122a33a7d73SArnd Bergmann {
1123a33a7d73SArnd Bergmann 	struct spu_context *ctx = spu->ctx;
1124a33a7d73SArnd Bergmann 
1125a33a7d73SArnd Bergmann 	wake_up_all(&ctx->mfc_wq);
1126a33a7d73SArnd Bergmann 
1127a33a7d73SArnd Bergmann 	pr_debug("%s %s\n", __FUNCTION__, spu->name);
1128a33a7d73SArnd Bergmann 	if (ctx->mfc_fasync) {
1129a33a7d73SArnd Bergmann 		u32 free_elements, tagstatus;
1130a33a7d73SArnd Bergmann 		unsigned int mask;
1131a33a7d73SArnd Bergmann 
1132a33a7d73SArnd Bergmann 		/* no need for spu_acquire in interrupt context */
1133a33a7d73SArnd Bergmann 		free_elements = ctx->ops->get_mfc_free_elements(ctx);
1134a33a7d73SArnd Bergmann 		tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1135a33a7d73SArnd Bergmann 
1136a33a7d73SArnd Bergmann 		mask = 0;
1137a33a7d73SArnd Bergmann 		if (free_elements & 0xffff)
1138a33a7d73SArnd Bergmann 			mask |= POLLOUT;
1139a33a7d73SArnd Bergmann 		if (tagstatus & ctx->tagwait)
1140a33a7d73SArnd Bergmann 			mask |= POLLIN;
1141a33a7d73SArnd Bergmann 
1142a33a7d73SArnd Bergmann 		kill_fasync(&ctx->mfc_fasync, SIGIO, mask);
1143a33a7d73SArnd Bergmann 	}
1144a33a7d73SArnd Bergmann }
1145a33a7d73SArnd Bergmann 
1146a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status)
1147a33a7d73SArnd Bergmann {
1148a33a7d73SArnd Bergmann 	/* See if there is one tag group is complete */
1149a33a7d73SArnd Bergmann 	/* FIXME we need locking around tagwait */
1150a33a7d73SArnd Bergmann 	*status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait;
1151a33a7d73SArnd Bergmann 	ctx->tagwait &= ~*status;
1152a33a7d73SArnd Bergmann 	if (*status)
1153a33a7d73SArnd Bergmann 		return 1;
1154a33a7d73SArnd Bergmann 
1155a33a7d73SArnd Bergmann 	/* enable interrupt waiting for any tag group,
1156a33a7d73SArnd Bergmann 	   may silently fail if interrupts are already enabled */
1157a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1158a33a7d73SArnd Bergmann 	return 0;
1159a33a7d73SArnd Bergmann }
1160a33a7d73SArnd Bergmann 
1161a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer,
1162a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1163a33a7d73SArnd Bergmann {
1164a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1165a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1166a33a7d73SArnd Bergmann 	u32 status;
1167a33a7d73SArnd Bergmann 
1168a33a7d73SArnd Bergmann 	if (size != 4)
1169a33a7d73SArnd Bergmann 		goto out;
1170a33a7d73SArnd Bergmann 
1171a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1172a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1173a33a7d73SArnd Bergmann 		status = ctx->ops->read_mfc_tagstatus(ctx);
1174a33a7d73SArnd Bergmann 		if (!(status & ctx->tagwait))
1175a33a7d73SArnd Bergmann 			ret = -EAGAIN;
1176a33a7d73SArnd Bergmann 		else
1177a33a7d73SArnd Bergmann 			ctx->tagwait &= ~status;
1178a33a7d73SArnd Bergmann 	} else {
1179a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1180a33a7d73SArnd Bergmann 			   spufs_read_mfc_tagstatus(ctx, &status));
1181a33a7d73SArnd Bergmann 	}
1182a33a7d73SArnd Bergmann 	spu_release(ctx);
1183a33a7d73SArnd Bergmann 
1184a33a7d73SArnd Bergmann 	if (ret)
1185a33a7d73SArnd Bergmann 		goto out;
1186a33a7d73SArnd Bergmann 
1187a33a7d73SArnd Bergmann 	ret = 4;
1188a33a7d73SArnd Bergmann 	if (copy_to_user(buffer, &status, 4))
1189a33a7d73SArnd Bergmann 		ret = -EFAULT;
1190a33a7d73SArnd Bergmann 
1191a33a7d73SArnd Bergmann out:
1192a33a7d73SArnd Bergmann 	return ret;
1193a33a7d73SArnd Bergmann }
1194a33a7d73SArnd Bergmann 
1195a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd)
1196a33a7d73SArnd Bergmann {
1197a33a7d73SArnd Bergmann 	pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa,
1198a33a7d73SArnd Bergmann 		 cmd->ea, cmd->size, cmd->tag, cmd->cmd);
1199a33a7d73SArnd Bergmann 
1200a33a7d73SArnd Bergmann 	switch (cmd->cmd) {
1201a33a7d73SArnd Bergmann 	case MFC_PUT_CMD:
1202a33a7d73SArnd Bergmann 	case MFC_PUTF_CMD:
1203a33a7d73SArnd Bergmann 	case MFC_PUTB_CMD:
1204a33a7d73SArnd Bergmann 	case MFC_GET_CMD:
1205a33a7d73SArnd Bergmann 	case MFC_GETF_CMD:
1206a33a7d73SArnd Bergmann 	case MFC_GETB_CMD:
1207a33a7d73SArnd Bergmann 		break;
1208a33a7d73SArnd Bergmann 	default:
1209a33a7d73SArnd Bergmann 		pr_debug("invalid DMA opcode %x\n", cmd->cmd);
1210a33a7d73SArnd Bergmann 		return -EIO;
1211a33a7d73SArnd Bergmann 	}
1212a33a7d73SArnd Bergmann 
1213a33a7d73SArnd Bergmann 	if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) {
1214a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment, ea %lx lsa %x\n",
1215a33a7d73SArnd Bergmann 				cmd->ea, cmd->lsa);
1216a33a7d73SArnd Bergmann 		return -EIO;
1217a33a7d73SArnd Bergmann 	}
1218a33a7d73SArnd Bergmann 
1219a33a7d73SArnd Bergmann 	switch (cmd->size & 0xf) {
1220a33a7d73SArnd Bergmann 	case 1:
1221a33a7d73SArnd Bergmann 		break;
1222a33a7d73SArnd Bergmann 	case 2:
1223a33a7d73SArnd Bergmann 		if (cmd->lsa & 1)
1224a33a7d73SArnd Bergmann 			goto error;
1225a33a7d73SArnd Bergmann 		break;
1226a33a7d73SArnd Bergmann 	case 4:
1227a33a7d73SArnd Bergmann 		if (cmd->lsa & 3)
1228a33a7d73SArnd Bergmann 			goto error;
1229a33a7d73SArnd Bergmann 		break;
1230a33a7d73SArnd Bergmann 	case 8:
1231a33a7d73SArnd Bergmann 		if (cmd->lsa & 7)
1232a33a7d73SArnd Bergmann 			goto error;
1233a33a7d73SArnd Bergmann 		break;
1234a33a7d73SArnd Bergmann 	case 0:
1235a33a7d73SArnd Bergmann 		if (cmd->lsa & 15)
1236a33a7d73SArnd Bergmann 			goto error;
1237a33a7d73SArnd Bergmann 		break;
1238a33a7d73SArnd Bergmann 	error:
1239a33a7d73SArnd Bergmann 	default:
1240a33a7d73SArnd Bergmann 		pr_debug("invalid DMA alignment %x for size %x\n",
1241a33a7d73SArnd Bergmann 			cmd->lsa & 0xf, cmd->size);
1242a33a7d73SArnd Bergmann 		return -EIO;
1243a33a7d73SArnd Bergmann 	}
1244a33a7d73SArnd Bergmann 
1245a33a7d73SArnd Bergmann 	if (cmd->size > 16 * 1024) {
1246a33a7d73SArnd Bergmann 		pr_debug("invalid DMA size %x\n", cmd->size);
1247a33a7d73SArnd Bergmann 		return -EIO;
1248a33a7d73SArnd Bergmann 	}
1249a33a7d73SArnd Bergmann 
1250a33a7d73SArnd Bergmann 	if (cmd->tag & 0xfff0) {
1251a33a7d73SArnd Bergmann 		/* we reserve the higher tag numbers for kernel use */
1252a33a7d73SArnd Bergmann 		pr_debug("invalid DMA tag\n");
1253a33a7d73SArnd Bergmann 		return -EIO;
1254a33a7d73SArnd Bergmann 	}
1255a33a7d73SArnd Bergmann 
1256a33a7d73SArnd Bergmann 	if (cmd->class) {
1257a33a7d73SArnd Bergmann 		/* not supported in this version */
1258a33a7d73SArnd Bergmann 		pr_debug("invalid DMA class\n");
1259a33a7d73SArnd Bergmann 		return -EIO;
1260a33a7d73SArnd Bergmann 	}
1261a33a7d73SArnd Bergmann 
1262a33a7d73SArnd Bergmann 	return 0;
1263a33a7d73SArnd Bergmann }
1264a33a7d73SArnd Bergmann 
1265a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx,
1266a33a7d73SArnd Bergmann 				struct mfc_dma_command cmd,
1267a33a7d73SArnd Bergmann 				int *error)
1268a33a7d73SArnd Bergmann {
1269a33a7d73SArnd Bergmann 	*error = ctx->ops->send_mfc_command(ctx, &cmd);
1270a33a7d73SArnd Bergmann 	if (*error == -EAGAIN) {
1271a33a7d73SArnd Bergmann 		/* wait for any tag group to complete
1272a33a7d73SArnd Bergmann 		   so we have space for the new command */
1273a33a7d73SArnd Bergmann 		ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
1274a33a7d73SArnd Bergmann 		/* try again, because the queue might be
1275a33a7d73SArnd Bergmann 		   empty again */
1276a33a7d73SArnd Bergmann 		*error = ctx->ops->send_mfc_command(ctx, &cmd);
1277a33a7d73SArnd Bergmann 		if (*error == -EAGAIN)
1278a33a7d73SArnd Bergmann 			return 0;
1279a33a7d73SArnd Bergmann 	}
1280a33a7d73SArnd Bergmann 	return 1;
1281a33a7d73SArnd Bergmann }
1282a33a7d73SArnd Bergmann 
1283a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer,
1284a33a7d73SArnd Bergmann 			size_t size, loff_t *pos)
1285a33a7d73SArnd Bergmann {
1286a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1287a33a7d73SArnd Bergmann 	struct mfc_dma_command cmd;
1288a33a7d73SArnd Bergmann 	int ret = -EINVAL;
1289a33a7d73SArnd Bergmann 
1290a33a7d73SArnd Bergmann 	if (size != sizeof cmd)
1291a33a7d73SArnd Bergmann 		goto out;
1292a33a7d73SArnd Bergmann 
1293a33a7d73SArnd Bergmann 	ret = -EFAULT;
1294a33a7d73SArnd Bergmann 	if (copy_from_user(&cmd, buffer, sizeof cmd))
1295a33a7d73SArnd Bergmann 		goto out;
1296a33a7d73SArnd Bergmann 
1297a33a7d73SArnd Bergmann 	ret = spufs_check_valid_dma(&cmd);
1298a33a7d73SArnd Bergmann 	if (ret)
1299a33a7d73SArnd Bergmann 		goto out;
1300a33a7d73SArnd Bergmann 
1301a33a7d73SArnd Bergmann 	spu_acquire_runnable(ctx);
1302a33a7d73SArnd Bergmann 	if (file->f_flags & O_NONBLOCK) {
1303a33a7d73SArnd Bergmann 		ret = ctx->ops->send_mfc_command(ctx, &cmd);
1304a33a7d73SArnd Bergmann 	} else {
1305a33a7d73SArnd Bergmann 		int status;
1306a33a7d73SArnd Bergmann 		ret = spufs_wait(ctx->mfc_wq,
1307a33a7d73SArnd Bergmann 				 spu_send_mfc_command(ctx, cmd, &status));
1308a33a7d73SArnd Bergmann 		if (status)
1309a33a7d73SArnd Bergmann 			ret = status;
1310a33a7d73SArnd Bergmann 	}
1311a33a7d73SArnd Bergmann 	spu_release(ctx);
1312a33a7d73SArnd Bergmann 
1313a33a7d73SArnd Bergmann 	if (ret)
1314a33a7d73SArnd Bergmann 		goto out;
1315a33a7d73SArnd Bergmann 
1316a33a7d73SArnd Bergmann 	ctx->tagwait |= 1 << cmd.tag;
13173692dc66SMasato Noguchi 	ret = size;
1318a33a7d73SArnd Bergmann 
1319a33a7d73SArnd Bergmann out:
1320a33a7d73SArnd Bergmann 	return ret;
1321a33a7d73SArnd Bergmann }
1322a33a7d73SArnd Bergmann 
1323a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait)
1324a33a7d73SArnd Bergmann {
1325a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1326a33a7d73SArnd Bergmann 	u32 free_elements, tagstatus;
1327a33a7d73SArnd Bergmann 	unsigned int mask;
1328a33a7d73SArnd Bergmann 
1329a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1330a33a7d73SArnd Bergmann 	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2);
1331a33a7d73SArnd Bergmann 	free_elements = ctx->ops->get_mfc_free_elements(ctx);
1332a33a7d73SArnd Bergmann 	tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
1333a33a7d73SArnd Bergmann 	spu_release(ctx);
1334a33a7d73SArnd Bergmann 
1335a33a7d73SArnd Bergmann 	poll_wait(file, &ctx->mfc_wq, wait);
1336a33a7d73SArnd Bergmann 
1337a33a7d73SArnd Bergmann 	mask = 0;
1338a33a7d73SArnd Bergmann 	if (free_elements & 0xffff)
1339a33a7d73SArnd Bergmann 		mask |= POLLOUT | POLLWRNORM;
1340a33a7d73SArnd Bergmann 	if (tagstatus & ctx->tagwait)
1341a33a7d73SArnd Bergmann 		mask |= POLLIN | POLLRDNORM;
1342a33a7d73SArnd Bergmann 
1343a33a7d73SArnd Bergmann 	pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__,
1344a33a7d73SArnd Bergmann 		free_elements, tagstatus, ctx->tagwait);
1345a33a7d73SArnd Bergmann 
1346a33a7d73SArnd Bergmann 	return mask;
1347a33a7d73SArnd Bergmann }
1348a33a7d73SArnd Bergmann 
134973b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id)
1350a33a7d73SArnd Bergmann {
1351a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1352a33a7d73SArnd Bergmann 	int ret;
1353a33a7d73SArnd Bergmann 
1354a33a7d73SArnd Bergmann 	spu_acquire(ctx);
1355a33a7d73SArnd Bergmann #if 0
1356a33a7d73SArnd Bergmann /* this currently hangs */
1357a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1358a33a7d73SArnd Bergmann 			 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2));
1359a33a7d73SArnd Bergmann 	if (ret)
1360a33a7d73SArnd Bergmann 		goto out;
1361a33a7d73SArnd Bergmann 	ret = spufs_wait(ctx->mfc_wq,
1362a33a7d73SArnd Bergmann 			 ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait);
1363a33a7d73SArnd Bergmann out:
1364a33a7d73SArnd Bergmann #else
1365a33a7d73SArnd Bergmann 	ret = 0;
1366a33a7d73SArnd Bergmann #endif
1367a33a7d73SArnd Bergmann 	spu_release(ctx);
1368a33a7d73SArnd Bergmann 
1369a33a7d73SArnd Bergmann 	return ret;
1370a33a7d73SArnd Bergmann }
1371a33a7d73SArnd Bergmann 
1372a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry,
1373a33a7d73SArnd Bergmann 			   int datasync)
1374a33a7d73SArnd Bergmann {
137573b6af8aSAl Viro 	return spufs_mfc_flush(file, NULL);
1376a33a7d73SArnd Bergmann }
1377a33a7d73SArnd Bergmann 
1378a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on)
1379a33a7d73SArnd Bergmann {
1380a33a7d73SArnd Bergmann 	struct spu_context *ctx = file->private_data;
1381a33a7d73SArnd Bergmann 
1382a33a7d73SArnd Bergmann 	return fasync_helper(fd, file, on, &ctx->mfc_fasync);
1383a33a7d73SArnd Bergmann }
1384a33a7d73SArnd Bergmann 
13855dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = {
1386a33a7d73SArnd Bergmann 	.open	 = spufs_mfc_open,
1387a33a7d73SArnd Bergmann 	.read	 = spufs_mfc_read,
1388a33a7d73SArnd Bergmann 	.write	 = spufs_mfc_write,
1389a33a7d73SArnd Bergmann 	.poll	 = spufs_mfc_poll,
1390a33a7d73SArnd Bergmann 	.flush	 = spufs_mfc_flush,
1391a33a7d73SArnd Bergmann 	.fsync	 = spufs_mfc_fsync,
1392a33a7d73SArnd Bergmann 	.fasync	 = spufs_mfc_fasync,
13936df10a82SMark Nutter 	.mmap	 = spufs_mfc_mmap,
1394a33a7d73SArnd Bergmann };
1395a33a7d73SArnd Bergmann 
139667207b96SArnd Bergmann static void spufs_npc_set(void *data, u64 val)
139767207b96SArnd Bergmann {
139867207b96SArnd Bergmann 	struct spu_context *ctx = data;
13998b3d6663SArnd Bergmann 	spu_acquire(ctx);
14008b3d6663SArnd Bergmann 	ctx->ops->npc_write(ctx, val);
14018b3d6663SArnd Bergmann 	spu_release(ctx);
140267207b96SArnd Bergmann }
140367207b96SArnd Bergmann 
140467207b96SArnd Bergmann static u64 spufs_npc_get(void *data)
140567207b96SArnd Bergmann {
140667207b96SArnd Bergmann 	struct spu_context *ctx = data;
140767207b96SArnd Bergmann 	u64 ret;
14088b3d6663SArnd Bergmann 	spu_acquire(ctx);
14098b3d6663SArnd Bergmann 	ret = ctx->ops->npc_read(ctx);
14108b3d6663SArnd Bergmann 	spu_release(ctx);
141167207b96SArnd Bergmann 	return ret;
141267207b96SArnd Bergmann }
14139b5047e2SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set,
14149b5047e2SDwayne Grant McConnell 			"0x%llx\n")
141567207b96SArnd Bergmann 
14168b3d6663SArnd Bergmann static void spufs_decr_set(void *data, u64 val)
14178b3d6663SArnd Bergmann {
14188b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14198b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14208b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14218b3d6663SArnd Bergmann 	lscsa->decr.slot[0] = (u32) val;
14228b3d6663SArnd Bergmann 	spu_release(ctx);
14238b3d6663SArnd Bergmann }
14248b3d6663SArnd Bergmann 
1425bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_get(void *data)
14268b3d6663SArnd Bergmann {
14278b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14288b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1429bf1ab978SDwayne Grant McConnell 	return lscsa->decr.slot[0];
1430bf1ab978SDwayne Grant McConnell }
1431bf1ab978SDwayne Grant McConnell 
1432bf1ab978SDwayne Grant McConnell static u64 spufs_decr_get(void *data)
1433bf1ab978SDwayne Grant McConnell {
1434bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14358b3d6663SArnd Bergmann 	u64 ret;
14368b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1437bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_get(data);
14388b3d6663SArnd Bergmann 	spu_release(ctx);
14398b3d6663SArnd Bergmann 	return ret;
14408b3d6663SArnd Bergmann }
14418b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set,
14429b5047e2SDwayne Grant McConnell 			"0x%llx\n")
14438b3d6663SArnd Bergmann 
14448b3d6663SArnd Bergmann static void spufs_decr_status_set(void *data, u64 val)
14458b3d6663SArnd Bergmann {
14468b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14478b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14488b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14498b3d6663SArnd Bergmann 	lscsa->decr_status.slot[0] = (u32) val;
14508b3d6663SArnd Bergmann 	spu_release(ctx);
14518b3d6663SArnd Bergmann }
14528b3d6663SArnd Bergmann 
1453bf1ab978SDwayne Grant McConnell static u64 __spufs_decr_status_get(void *data)
14548b3d6663SArnd Bergmann {
14558b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14568b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1457bf1ab978SDwayne Grant McConnell 	return lscsa->decr_status.slot[0];
1458bf1ab978SDwayne Grant McConnell }
1459bf1ab978SDwayne Grant McConnell 
1460bf1ab978SDwayne Grant McConnell static u64 spufs_decr_status_get(void *data)
1461bf1ab978SDwayne Grant McConnell {
1462bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14638b3d6663SArnd Bergmann 	u64 ret;
14648b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1465bf1ab978SDwayne Grant McConnell 	ret = __spufs_decr_status_get(data);
14668b3d6663SArnd Bergmann 	spu_release(ctx);
14678b3d6663SArnd Bergmann 	return ret;
14688b3d6663SArnd Bergmann }
14698b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get,
14709b5047e2SDwayne Grant McConnell 			spufs_decr_status_set, "0x%llx\n")
14718b3d6663SArnd Bergmann 
14728b3d6663SArnd Bergmann static void spufs_event_mask_set(void *data, u64 val)
14738b3d6663SArnd Bergmann {
14748b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14758b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
14768b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
14778b3d6663SArnd Bergmann 	lscsa->event_mask.slot[0] = (u32) val;
14788b3d6663SArnd Bergmann 	spu_release(ctx);
14798b3d6663SArnd Bergmann }
14808b3d6663SArnd Bergmann 
1481bf1ab978SDwayne Grant McConnell static u64 __spufs_event_mask_get(void *data)
14828b3d6663SArnd Bergmann {
14838b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
14848b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
1485bf1ab978SDwayne Grant McConnell 	return lscsa->event_mask.slot[0];
1486bf1ab978SDwayne Grant McConnell }
1487bf1ab978SDwayne Grant McConnell 
1488bf1ab978SDwayne Grant McConnell static u64 spufs_event_mask_get(void *data)
1489bf1ab978SDwayne Grant McConnell {
1490bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
14918b3d6663SArnd Bergmann 	u64 ret;
14928b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
1493bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_mask_get(data);
14948b3d6663SArnd Bergmann 	spu_release(ctx);
14958b3d6663SArnd Bergmann 	return ret;
14968b3d6663SArnd Bergmann }
14978b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get,
14989b5047e2SDwayne Grant McConnell 			spufs_event_mask_set, "0x%llx\n")
14998b3d6663SArnd Bergmann 
1500bf1ab978SDwayne Grant McConnell static u64 __spufs_event_status_get(void *data)
1501b9e3bd77SDwayne Grant McConnell {
1502b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1503b9e3bd77SDwayne Grant McConnell 	struct spu_state *state = &ctx->csa;
1504b9e3bd77SDwayne Grant McConnell 	u64 stat;
1505b9e3bd77SDwayne Grant McConnell 	stat = state->spu_chnlcnt_RW[0];
1506b9e3bd77SDwayne Grant McConnell 	if (stat)
1507bf1ab978SDwayne Grant McConnell 		return state->spu_chnldata_RW[0];
1508bf1ab978SDwayne Grant McConnell 	return 0;
1509bf1ab978SDwayne Grant McConnell }
1510bf1ab978SDwayne Grant McConnell 
1511bf1ab978SDwayne Grant McConnell static u64 spufs_event_status_get(void *data)
1512bf1ab978SDwayne Grant McConnell {
1513bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1514bf1ab978SDwayne Grant McConnell 	u64 ret = 0;
1515bf1ab978SDwayne Grant McConnell 
1516bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1517bf1ab978SDwayne Grant McConnell 	ret = __spufs_event_status_get(data);
1518b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1519b9e3bd77SDwayne Grant McConnell 	return ret;
1520b9e3bd77SDwayne Grant McConnell }
1521b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get,
1522b9e3bd77SDwayne Grant McConnell 			NULL, "0x%llx\n")
1523b9e3bd77SDwayne Grant McConnell 
15248b3d6663SArnd Bergmann static void spufs_srr0_set(void *data, u64 val)
15258b3d6663SArnd Bergmann {
15268b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15278b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15288b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15298b3d6663SArnd Bergmann 	lscsa->srr0.slot[0] = (u32) val;
15308b3d6663SArnd Bergmann 	spu_release(ctx);
15318b3d6663SArnd Bergmann }
15328b3d6663SArnd Bergmann 
15338b3d6663SArnd Bergmann static u64 spufs_srr0_get(void *data)
15348b3d6663SArnd Bergmann {
15358b3d6663SArnd Bergmann 	struct spu_context *ctx = data;
15368b3d6663SArnd Bergmann 	struct spu_lscsa *lscsa = ctx->csa.lscsa;
15378b3d6663SArnd Bergmann 	u64 ret;
15388b3d6663SArnd Bergmann 	spu_acquire_saved(ctx);
15398b3d6663SArnd Bergmann 	ret = lscsa->srr0.slot[0];
15408b3d6663SArnd Bergmann 	spu_release(ctx);
15418b3d6663SArnd Bergmann 	return ret;
15428b3d6663SArnd Bergmann }
15438b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
15449b5047e2SDwayne Grant McConnell 			"0x%llx\n")
15458b3d6663SArnd Bergmann 
15467b1a7014Sarnd@arndb.de static u64 spufs_id_get(void *data)
15477b1a7014Sarnd@arndb.de {
15487b1a7014Sarnd@arndb.de 	struct spu_context *ctx = data;
15497b1a7014Sarnd@arndb.de 	u64 num;
15507b1a7014Sarnd@arndb.de 
15517b1a7014Sarnd@arndb.de 	spu_acquire(ctx);
15527b1a7014Sarnd@arndb.de 	if (ctx->state == SPU_STATE_RUNNABLE)
15537b1a7014Sarnd@arndb.de 		num = ctx->spu->number;
15547b1a7014Sarnd@arndb.de 	else
15557b1a7014Sarnd@arndb.de 		num = (unsigned int)-1;
15567b1a7014Sarnd@arndb.de 	spu_release(ctx);
15577b1a7014Sarnd@arndb.de 
15587b1a7014Sarnd@arndb.de 	return num;
15597b1a7014Sarnd@arndb.de }
1560e45d6634SAl Viro DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n")
15617b1a7014Sarnd@arndb.de 
1562bf1ab978SDwayne Grant McConnell static u64 __spufs_object_id_get(void *data)
156386767277SArnd Bergmann {
156486767277SArnd Bergmann 	struct spu_context *ctx = data;
156586767277SArnd Bergmann 	return ctx->object_id;
156686767277SArnd Bergmann }
156786767277SArnd Bergmann 
1568bf1ab978SDwayne Grant McConnell static u64 spufs_object_id_get(void *data)
1569bf1ab978SDwayne Grant McConnell {
1570bf1ab978SDwayne Grant McConnell 	/* FIXME: Should there really be no locking here? */
1571bf1ab978SDwayne Grant McConnell 	return __spufs_object_id_get(data);
1572bf1ab978SDwayne Grant McConnell }
1573bf1ab978SDwayne Grant McConnell 
157486767277SArnd Bergmann static void spufs_object_id_set(void *data, u64 id)
157586767277SArnd Bergmann {
157686767277SArnd Bergmann 	struct spu_context *ctx = data;
157786767277SArnd Bergmann 	ctx->object_id = id;
157886767277SArnd Bergmann }
157986767277SArnd Bergmann 
158086767277SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get,
158186767277SArnd Bergmann 		spufs_object_id_set, "0x%llx\n");
158286767277SArnd Bergmann 
1583bf1ab978SDwayne Grant McConnell static u64 __spufs_lslr_get(void *data)
1584bf1ab978SDwayne Grant McConnell {
1585bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = data;
1586bf1ab978SDwayne Grant McConnell 	return ctx->csa.priv2.spu_lslr_RW;
1587bf1ab978SDwayne Grant McConnell }
1588bf1ab978SDwayne Grant McConnell 
1589b9e3bd77SDwayne Grant McConnell static u64 spufs_lslr_get(void *data)
1590b9e3bd77SDwayne Grant McConnell {
1591b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = data;
1592b9e3bd77SDwayne Grant McConnell 	u64 ret;
1593b9e3bd77SDwayne Grant McConnell 
1594b9e3bd77SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1595bf1ab978SDwayne Grant McConnell 	ret = __spufs_lslr_get(data);
1596b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1597b9e3bd77SDwayne Grant McConnell 
1598b9e3bd77SDwayne Grant McConnell 	return ret;
1599b9e3bd77SDwayne Grant McConnell }
1600b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n")
1601b9e3bd77SDwayne Grant McConnell 
1602b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file)
1603b9e3bd77SDwayne Grant McConnell {
1604b9e3bd77SDwayne Grant McConnell 	struct spufs_inode_info *i = SPUFS_I(inode);
1605b9e3bd77SDwayne Grant McConnell 	struct spu_context *ctx = i->i_ctx;
1606b9e3bd77SDwayne Grant McConnell 	file->private_data = ctx;
1607b9e3bd77SDwayne Grant McConnell 	return 0;
1608b9e3bd77SDwayne Grant McConnell }
1609b9e3bd77SDwayne Grant McConnell 
1610bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx,
1611bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1612bf1ab978SDwayne Grant McConnell {
1613bf1ab978SDwayne Grant McConnell 	u32 mbox_stat;
1614bf1ab978SDwayne Grant McConnell 	u32 data;
1615bf1ab978SDwayne Grant McConnell 
1616bf1ab978SDwayne Grant McConnell 	mbox_stat = ctx->csa.prob.mb_stat_R;
1617bf1ab978SDwayne Grant McConnell 	if (mbox_stat & 0x0000ff) {
1618bf1ab978SDwayne Grant McConnell 		data = ctx->csa.prob.pu_mb_R;
1619bf1ab978SDwayne Grant McConnell 	}
1620bf1ab978SDwayne Grant McConnell 
1621bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1622bf1ab978SDwayne Grant McConnell }
1623bf1ab978SDwayne Grant McConnell 
162469a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
162569a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
162669a2f00cSDwayne Grant McConnell {
1627bf1ab978SDwayne Grant McConnell 	int ret;
162869a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
162969a2f00cSDwayne Grant McConnell 
163069a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
163169a2f00cSDwayne Grant McConnell 		return -EFAULT;
163269a2f00cSDwayne Grant McConnell 
163369a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
163469a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1635bf1ab978SDwayne Grant McConnell 	ret = __spufs_mbox_info_read(ctx, buf, len, pos);
163669a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
163769a2f00cSDwayne Grant McConnell 	spu_release(ctx);
163869a2f00cSDwayne Grant McConnell 
1639bf1ab978SDwayne Grant McConnell 	return ret;
164069a2f00cSDwayne Grant McConnell }
164169a2f00cSDwayne Grant McConnell 
16425dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = {
164369a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
164469a2f00cSDwayne Grant McConnell 	.read = spufs_mbox_info_read,
164569a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
164669a2f00cSDwayne Grant McConnell };
164769a2f00cSDwayne Grant McConnell 
1648bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx,
1649bf1ab978SDwayne Grant McConnell 				char __user *buf, size_t len, loff_t *pos)
1650bf1ab978SDwayne Grant McConnell {
1651bf1ab978SDwayne Grant McConnell 	u32 ibox_stat;
1652bf1ab978SDwayne Grant McConnell 	u32 data;
1653bf1ab978SDwayne Grant McConnell 
1654bf1ab978SDwayne Grant McConnell 	ibox_stat = ctx->csa.prob.mb_stat_R;
1655bf1ab978SDwayne Grant McConnell 	if (ibox_stat & 0xff0000) {
1656bf1ab978SDwayne Grant McConnell 		data = ctx->csa.priv2.puint_mb_R;
1657bf1ab978SDwayne Grant McConnell 	}
1658bf1ab978SDwayne Grant McConnell 
1659bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data, sizeof data);
1660bf1ab978SDwayne Grant McConnell }
1661bf1ab978SDwayne Grant McConnell 
166269a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
166369a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
166469a2f00cSDwayne Grant McConnell {
166569a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1666bf1ab978SDwayne Grant McConnell 	int ret;
166769a2f00cSDwayne Grant McConnell 
166869a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
166969a2f00cSDwayne Grant McConnell 		return -EFAULT;
167069a2f00cSDwayne Grant McConnell 
167169a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
167269a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1673bf1ab978SDwayne Grant McConnell 	ret = __spufs_ibox_info_read(ctx, buf, len, pos);
167469a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
167569a2f00cSDwayne Grant McConnell 	spu_release(ctx);
167669a2f00cSDwayne Grant McConnell 
1677bf1ab978SDwayne Grant McConnell 	return ret;
167869a2f00cSDwayne Grant McConnell }
167969a2f00cSDwayne Grant McConnell 
16805dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = {
168169a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
168269a2f00cSDwayne Grant McConnell 	.read = spufs_ibox_info_read,
168369a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
168469a2f00cSDwayne Grant McConnell };
168569a2f00cSDwayne Grant McConnell 
1686bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx,
1687bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1688bf1ab978SDwayne Grant McConnell {
1689bf1ab978SDwayne Grant McConnell 	int i, cnt;
1690bf1ab978SDwayne Grant McConnell 	u32 data[4];
1691bf1ab978SDwayne Grant McConnell 	u32 wbox_stat;
1692bf1ab978SDwayne Grant McConnell 
1693bf1ab978SDwayne Grant McConnell 	wbox_stat = ctx->csa.prob.mb_stat_R;
1694bf1ab978SDwayne Grant McConnell 	cnt = 4 - ((wbox_stat & 0x00ff00) >> 8);
1695bf1ab978SDwayne Grant McConnell 	for (i = 0; i < cnt; i++) {
1696bf1ab978SDwayne Grant McConnell 		data[i] = ctx->csa.spu_mailbox_data[i];
1697bf1ab978SDwayne Grant McConnell 	}
1698bf1ab978SDwayne Grant McConnell 
1699bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &data,
1700bf1ab978SDwayne Grant McConnell 				cnt * sizeof(u32));
1701bf1ab978SDwayne Grant McConnell }
1702bf1ab978SDwayne Grant McConnell 
170369a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
170469a2f00cSDwayne Grant McConnell 				   size_t len, loff_t *pos)
170569a2f00cSDwayne Grant McConnell {
170669a2f00cSDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1707bf1ab978SDwayne Grant McConnell 	int ret;
170869a2f00cSDwayne Grant McConnell 
170969a2f00cSDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
171069a2f00cSDwayne Grant McConnell 		return -EFAULT;
171169a2f00cSDwayne Grant McConnell 
171269a2f00cSDwayne Grant McConnell 	spu_acquire_saved(ctx);
171369a2f00cSDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1714bf1ab978SDwayne Grant McConnell 	ret = __spufs_wbox_info_read(ctx, buf, len, pos);
171569a2f00cSDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
171669a2f00cSDwayne Grant McConnell 	spu_release(ctx);
171769a2f00cSDwayne Grant McConnell 
1718bf1ab978SDwayne Grant McConnell 	return ret;
171969a2f00cSDwayne Grant McConnell }
172069a2f00cSDwayne Grant McConnell 
17215dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = {
172269a2f00cSDwayne Grant McConnell 	.open = spufs_info_open,
172369a2f00cSDwayne Grant McConnell 	.read = spufs_wbox_info_read,
172469a2f00cSDwayne Grant McConnell 	.llseek  = generic_file_llseek,
172569a2f00cSDwayne Grant McConnell };
172669a2f00cSDwayne Grant McConnell 
1727bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx,
1728bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1729b9e3bd77SDwayne Grant McConnell {
1730b9e3bd77SDwayne Grant McConnell 	struct spu_dma_info info;
1731b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *spuqp;
1732b9e3bd77SDwayne Grant McConnell 	int i;
1733b9e3bd77SDwayne Grant McConnell 
1734b9e3bd77SDwayne Grant McConnell 	info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
1735b9e3bd77SDwayne Grant McConnell 	info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
1736b9e3bd77SDwayne Grant McConnell 	info.dma_info_status = ctx->csa.spu_chnldata_RW[24];
1737b9e3bd77SDwayne Grant McConnell 	info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
1738b9e3bd77SDwayne Grant McConnell 	info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
1739b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 16; i++) {
1740b9e3bd77SDwayne Grant McConnell 		qp = &info.dma_info_command_data[i];
1741b9e3bd77SDwayne Grant McConnell 		spuqp = &ctx->csa.priv2.spuq[i];
1742b9e3bd77SDwayne Grant McConnell 
1743b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW;
1744b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW;
1745b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW;
1746b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW;
1747b9e3bd77SDwayne Grant McConnell 	}
1748b9e3bd77SDwayne Grant McConnell 
1749b9e3bd77SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1750b9e3bd77SDwayne Grant McConnell 				sizeof info);
1751b9e3bd77SDwayne Grant McConnell }
1752b9e3bd77SDwayne Grant McConnell 
1753bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
1754bf1ab978SDwayne Grant McConnell 			      size_t len, loff_t *pos)
1755bf1ab978SDwayne Grant McConnell {
1756bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1757bf1ab978SDwayne Grant McConnell 	int ret;
1758bf1ab978SDwayne Grant McConnell 
1759bf1ab978SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1760bf1ab978SDwayne Grant McConnell 		return -EFAULT;
1761bf1ab978SDwayne Grant McConnell 
1762bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1763bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1764bf1ab978SDwayne Grant McConnell 	ret = __spufs_dma_info_read(ctx, buf, len, pos);
1765bf1ab978SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1766bf1ab978SDwayne Grant McConnell 	spu_release(ctx);
1767bf1ab978SDwayne Grant McConnell 
1768bf1ab978SDwayne Grant McConnell 	return ret;
1769bf1ab978SDwayne Grant McConnell }
1770bf1ab978SDwayne Grant McConnell 
17715dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = {
1772b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1773b9e3bd77SDwayne Grant McConnell 	.read = spufs_dma_info_read,
1774b9e3bd77SDwayne Grant McConnell };
1775b9e3bd77SDwayne Grant McConnell 
1776bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx,
1777bf1ab978SDwayne Grant McConnell 			char __user *buf, size_t len, loff_t *pos)
1778b9e3bd77SDwayne Grant McConnell {
1779b9e3bd77SDwayne Grant McConnell 	struct spu_proxydma_info info;
1780b9e3bd77SDwayne Grant McConnell 	struct mfc_cq_sr *qp, *puqp;
1781bf1ab978SDwayne Grant McConnell 	int ret = sizeof info;
1782b9e3bd77SDwayne Grant McConnell 	int i;
1783b9e3bd77SDwayne Grant McConnell 
1784b9e3bd77SDwayne Grant McConnell 	if (len < ret)
1785b9e3bd77SDwayne Grant McConnell 		return -EINVAL;
1786b9e3bd77SDwayne Grant McConnell 
1787b9e3bd77SDwayne Grant McConnell 	if (!access_ok(VERIFY_WRITE, buf, len))
1788b9e3bd77SDwayne Grant McConnell 		return -EFAULT;
1789b9e3bd77SDwayne Grant McConnell 
1790b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
1791b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
1792b9e3bd77SDwayne Grant McConnell 	info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
1793b9e3bd77SDwayne Grant McConnell 	for (i = 0; i < 8; i++) {
1794b9e3bd77SDwayne Grant McConnell 		qp = &info.proxydma_info_command_data[i];
1795b9e3bd77SDwayne Grant McConnell 		puqp = &ctx->csa.priv2.puq[i];
1796b9e3bd77SDwayne Grant McConnell 
1797b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
1798b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
1799b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
1800b9e3bd77SDwayne Grant McConnell 		qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
1801b9e3bd77SDwayne Grant McConnell 	}
1802bf1ab978SDwayne Grant McConnell 
1803bf1ab978SDwayne Grant McConnell 	return simple_read_from_buffer(buf, len, pos, &info,
1804bf1ab978SDwayne Grant McConnell 				sizeof info);
1805bf1ab978SDwayne Grant McConnell }
1806bf1ab978SDwayne Grant McConnell 
1807bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
1808bf1ab978SDwayne Grant McConnell 				   size_t len, loff_t *pos)
1809bf1ab978SDwayne Grant McConnell {
1810bf1ab978SDwayne Grant McConnell 	struct spu_context *ctx = file->private_data;
1811bf1ab978SDwayne Grant McConnell 	int ret;
1812bf1ab978SDwayne Grant McConnell 
1813bf1ab978SDwayne Grant McConnell 	spu_acquire_saved(ctx);
1814bf1ab978SDwayne Grant McConnell 	spin_lock(&ctx->csa.register_lock);
1815bf1ab978SDwayne Grant McConnell 	ret = __spufs_proxydma_info_read(ctx, buf, len, pos);
1816b9e3bd77SDwayne Grant McConnell 	spin_unlock(&ctx->csa.register_lock);
1817b9e3bd77SDwayne Grant McConnell 	spu_release(ctx);
1818b9e3bd77SDwayne Grant McConnell 
1819b9e3bd77SDwayne Grant McConnell 	return ret;
1820b9e3bd77SDwayne Grant McConnell }
1821b9e3bd77SDwayne Grant McConnell 
18225dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = {
1823b9e3bd77SDwayne Grant McConnell 	.open = spufs_info_open,
1824b9e3bd77SDwayne Grant McConnell 	.read = spufs_proxydma_info_read,
1825b9e3bd77SDwayne Grant McConnell };
1826b9e3bd77SDwayne Grant McConnell 
182767207b96SArnd Bergmann struct tree_descr spufs_dir_contents[] = {
182867207b96SArnd Bergmann 	{ "mem",  &spufs_mem_fops,  0666, },
18298b3d6663SArnd Bergmann 	{ "regs", &spufs_regs_fops,  0666, },
183067207b96SArnd Bergmann 	{ "mbox", &spufs_mbox_fops, 0444, },
183167207b96SArnd Bergmann 	{ "ibox", &spufs_ibox_fops, 0444, },
183267207b96SArnd Bergmann 	{ "wbox", &spufs_wbox_fops, 0222, },
183367207b96SArnd Bergmann 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
183467207b96SArnd Bergmann 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
183567207b96SArnd Bergmann 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
183667207b96SArnd Bergmann 	{ "signal1", &spufs_signal1_fops, 0666, },
183767207b96SArnd Bergmann 	{ "signal2", &spufs_signal2_fops, 0666, },
183867207b96SArnd Bergmann 	{ "signal1_type", &spufs_signal1_type, 0666, },
183967207b96SArnd Bergmann 	{ "signal2_type", &spufs_signal2_type, 0666, },
18406df10a82SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18418b3d6663SArnd Bergmann 	{ "fpcr", &spufs_fpcr_fops, 0666, },
1842b9e3bd77SDwayne Grant McConnell 	{ "lslr", &spufs_lslr_ops, 0444, },
1843b9e3bd77SDwayne Grant McConnell 	{ "mfc", &spufs_mfc_fops, 0666, },
1844b9e3bd77SDwayne Grant McConnell 	{ "mss", &spufs_mss_fops, 0666, },
1845b9e3bd77SDwayne Grant McConnell 	{ "npc", &spufs_npc_ops, 0666, },
1846b9e3bd77SDwayne Grant McConnell 	{ "srr0", &spufs_srr0_ops, 0666, },
18478b3d6663SArnd Bergmann 	{ "decr", &spufs_decr_ops, 0666, },
18488b3d6663SArnd Bergmann 	{ "decr_status", &spufs_decr_status_ops, 0666, },
18498b3d6663SArnd Bergmann 	{ "event_mask", &spufs_event_mask_ops, 0666, },
1850b9e3bd77SDwayne Grant McConnell 	{ "event_status", &spufs_event_status_ops, 0444, },
185127d5bf2aSBenjamin Herrenschmidt 	{ "psmap", &spufs_psmap_fops, 0666, },
185286767277SArnd Bergmann 	{ "phys-id", &spufs_id_ops, 0666, },
185386767277SArnd Bergmann 	{ "object-id", &spufs_object_id_ops, 0666, },
185469a2f00cSDwayne Grant McConnell 	{ "mbox_info", &spufs_mbox_info_fops, 0444, },
185569a2f00cSDwayne Grant McConnell 	{ "ibox_info", &spufs_ibox_info_fops, 0444, },
185669a2f00cSDwayne Grant McConnell 	{ "wbox_info", &spufs_wbox_info_fops, 0444, },
1857b9e3bd77SDwayne Grant McConnell 	{ "dma_info", &spufs_dma_info_fops, 0444, },
1858b9e3bd77SDwayne Grant McConnell 	{ "proxydma_info", &spufs_proxydma_info_fops, 0444, },
185967207b96SArnd Bergmann 	{},
186067207b96SArnd Bergmann };
18615737edd1SMark Nutter 
18625737edd1SMark Nutter struct tree_descr spufs_dir_nosched_contents[] = {
18635737edd1SMark Nutter 	{ "mem",  &spufs_mem_fops,  0666, },
18645737edd1SMark Nutter 	{ "mbox", &spufs_mbox_fops, 0444, },
18655737edd1SMark Nutter 	{ "ibox", &spufs_ibox_fops, 0444, },
18665737edd1SMark Nutter 	{ "wbox", &spufs_wbox_fops, 0222, },
18675737edd1SMark Nutter 	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
18685737edd1SMark Nutter 	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
18695737edd1SMark Nutter 	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
18705737edd1SMark Nutter 	{ "signal1", &spufs_signal1_fops, 0666, },
18715737edd1SMark Nutter 	{ "signal2", &spufs_signal2_fops, 0666, },
18725737edd1SMark Nutter 	{ "signal1_type", &spufs_signal1_type, 0666, },
18735737edd1SMark Nutter 	{ "signal2_type", &spufs_signal2_type, 0666, },
18745737edd1SMark Nutter 	{ "mss", &spufs_mss_fops, 0666, },
18755737edd1SMark Nutter 	{ "mfc", &spufs_mfc_fops, 0666, },
18765737edd1SMark Nutter 	{ "cntl", &spufs_cntl_fops,  0666, },
18775737edd1SMark Nutter 	{ "npc", &spufs_npc_ops, 0666, },
18785737edd1SMark Nutter 	{ "psmap", &spufs_psmap_fops, 0666, },
18795737edd1SMark Nutter 	{ "phys-id", &spufs_id_ops, 0666, },
18805737edd1SMark Nutter 	{ "object-id", &spufs_object_id_ops, 0666, },
18815737edd1SMark Nutter 	{},
18825737edd1SMark Nutter };
1883bf1ab978SDwayne Grant McConnell 
1884bf1ab978SDwayne Grant McConnell struct spufs_coredump_reader spufs_coredump_read[] = {
1885bf1ab978SDwayne Grant McConnell 	{ "regs", __spufs_regs_read, NULL, 128 * 16 },
1886bf1ab978SDwayne Grant McConnell 	{ "fpcr", __spufs_fpcr_read, NULL, 16 },
1887bf1ab978SDwayne Grant McConnell 	{ "lslr", NULL, __spufs_lslr_get, 11 },
1888bf1ab978SDwayne Grant McConnell 	{ "decr", NULL, __spufs_decr_get, 11 },
1889bf1ab978SDwayne Grant McConnell 	{ "decr_status", NULL, __spufs_decr_status_get, 11 },
1890bf1ab978SDwayne Grant McConnell 	{ "mem", __spufs_mem_read, NULL, 256 * 1024, },
1891bf1ab978SDwayne Grant McConnell 	{ "signal1", __spufs_signal1_read, NULL, 4 },
1892bf1ab978SDwayne Grant McConnell 	{ "signal1_type", NULL, __spufs_signal1_type_get, 2 },
1893bf1ab978SDwayne Grant McConnell 	{ "signal2", __spufs_signal2_read, NULL, 4 },
1894bf1ab978SDwayne Grant McConnell 	{ "signal2_type", NULL, __spufs_signal2_type_get, 2 },
1895bf1ab978SDwayne Grant McConnell 	{ "event_mask", NULL, __spufs_event_mask_get, 8 },
1896bf1ab978SDwayne Grant McConnell 	{ "event_status", NULL, __spufs_event_status_get, 8 },
1897bf1ab978SDwayne Grant McConnell 	{ "mbox_info", __spufs_mbox_info_read, NULL, 4 },
1898bf1ab978SDwayne Grant McConnell 	{ "ibox_info", __spufs_ibox_info_read, NULL, 4 },
1899bf1ab978SDwayne Grant McConnell 	{ "wbox_info", __spufs_wbox_info_read, NULL, 16 },
1900bf1ab978SDwayne Grant McConnell 	{ "dma_info", __spufs_dma_info_read, NULL, 69 * 8 },
1901bf1ab978SDwayne Grant McConnell 	{ "proxydma_info", __spufs_proxydma_info_read, NULL, 35 * 8 },
1902bf1ab978SDwayne Grant McConnell 	{ "object-id", NULL, __spufs_object_id_get, 19 },
1903bf1ab978SDwayne Grant McConnell 	{ },
1904bf1ab978SDwayne Grant McConnell };
1905bf1ab978SDwayne Grant McConnell int spufs_coredump_num_notes = ARRAY_SIZE(spufs_coredump_read) - 1;
1906bf1ab978SDwayne Grant McConnell 
1907