18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SPU file system -- file contents
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Arnd Bergmann <arndb@de.ibm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#undef DEBUG
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/coredump.h>
138c2ecf20Sopenharmony_ci#include <linux/fs.h>
148c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
158c2ecf20Sopenharmony_ci#include <linux/export.h>
168c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
178c2ecf20Sopenharmony_ci#include <linux/poll.h>
188c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
198c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/io.h>
238c2ecf20Sopenharmony_ci#include <asm/time.h>
248c2ecf20Sopenharmony_ci#include <asm/spu.h>
258c2ecf20Sopenharmony_ci#include <asm/spu_info.h>
268c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "spufs.h"
298c2ecf20Sopenharmony_ci#include "sputrace.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000)
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Simple attribute files */
348c2ecf20Sopenharmony_cistruct spufs_attr {
358c2ecf20Sopenharmony_ci	int (*get)(void *, u64 *);
368c2ecf20Sopenharmony_ci	int (*set)(void *, u64);
378c2ecf20Sopenharmony_ci	char get_buf[24];       /* enough to store a u64 and "\n\0" */
388c2ecf20Sopenharmony_ci	char set_buf[24];
398c2ecf20Sopenharmony_ci	void *data;
408c2ecf20Sopenharmony_ci	const char *fmt;        /* format for read operation */
418c2ecf20Sopenharmony_ci	struct mutex mutex;     /* protects access to these buffers */
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int spufs_attr_open(struct inode *inode, struct file *file,
458c2ecf20Sopenharmony_ci		int (*get)(void *, u64 *), int (*set)(void *, u64),
468c2ecf20Sopenharmony_ci		const char *fmt)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct spufs_attr *attr;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	attr = kmalloc(sizeof(*attr), GFP_KERNEL);
518c2ecf20Sopenharmony_ci	if (!attr)
528c2ecf20Sopenharmony_ci		return -ENOMEM;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	attr->get = get;
558c2ecf20Sopenharmony_ci	attr->set = set;
568c2ecf20Sopenharmony_ci	attr->data = inode->i_private;
578c2ecf20Sopenharmony_ci	attr->fmt = fmt;
588c2ecf20Sopenharmony_ci	mutex_init(&attr->mutex);
598c2ecf20Sopenharmony_ci	file->private_data = attr;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic int spufs_attr_release(struct inode *inode, struct file *file)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci       kfree(file->private_data);
678c2ecf20Sopenharmony_ci	return 0;
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic ssize_t spufs_attr_read(struct file *file, char __user *buf,
718c2ecf20Sopenharmony_ci		size_t len, loff_t *ppos)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct spufs_attr *attr;
748c2ecf20Sopenharmony_ci	size_t size;
758c2ecf20Sopenharmony_ci	ssize_t ret;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	attr = file->private_data;
788c2ecf20Sopenharmony_ci	if (!attr->get)
798c2ecf20Sopenharmony_ci		return -EACCES;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	ret = mutex_lock_interruptible(&attr->mutex);
828c2ecf20Sopenharmony_ci	if (ret)
838c2ecf20Sopenharmony_ci		return ret;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (*ppos) {		/* continued read */
868c2ecf20Sopenharmony_ci		size = strlen(attr->get_buf);
878c2ecf20Sopenharmony_ci	} else {		/* first read */
888c2ecf20Sopenharmony_ci		u64 val;
898c2ecf20Sopenharmony_ci		ret = attr->get(attr->data, &val);
908c2ecf20Sopenharmony_ci		if (ret)
918c2ecf20Sopenharmony_ci			goto out;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		size = scnprintf(attr->get_buf, sizeof(attr->get_buf),
948c2ecf20Sopenharmony_ci				 attr->fmt, (unsigned long long)val);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size);
988c2ecf20Sopenharmony_ciout:
998c2ecf20Sopenharmony_ci	mutex_unlock(&attr->mutex);
1008c2ecf20Sopenharmony_ci	return ret;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic ssize_t spufs_attr_write(struct file *file, const char __user *buf,
1048c2ecf20Sopenharmony_ci		size_t len, loff_t *ppos)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct spufs_attr *attr;
1078c2ecf20Sopenharmony_ci	u64 val;
1088c2ecf20Sopenharmony_ci	size_t size;
1098c2ecf20Sopenharmony_ci	ssize_t ret;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	attr = file->private_data;
1128c2ecf20Sopenharmony_ci	if (!attr->set)
1138c2ecf20Sopenharmony_ci		return -EACCES;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	ret = mutex_lock_interruptible(&attr->mutex);
1168c2ecf20Sopenharmony_ci	if (ret)
1178c2ecf20Sopenharmony_ci		return ret;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	ret = -EFAULT;
1208c2ecf20Sopenharmony_ci	size = min(sizeof(attr->set_buf) - 1, len);
1218c2ecf20Sopenharmony_ci	if (copy_from_user(attr->set_buf, buf, size))
1228c2ecf20Sopenharmony_ci		goto out;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	ret = len; /* claim we got the whole input */
1258c2ecf20Sopenharmony_ci	attr->set_buf[size] = '\0';
1268c2ecf20Sopenharmony_ci	val = simple_strtol(attr->set_buf, NULL, 0);
1278c2ecf20Sopenharmony_ci	attr->set(attr->data, val);
1288c2ecf20Sopenharmony_ciout:
1298c2ecf20Sopenharmony_ci	mutex_unlock(&attr->mutex);
1308c2ecf20Sopenharmony_ci	return ret;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic ssize_t spufs_dump_emit(struct coredump_params *cprm, void *buf,
1348c2ecf20Sopenharmony_ci		size_t size)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	if (!dump_emit(cprm, buf, size))
1378c2ecf20Sopenharmony_ci		return -EIO;
1388c2ecf20Sopenharmony_ci	return size;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#define DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt)	\
1428c2ecf20Sopenharmony_cistatic int __fops ## _open(struct inode *inode, struct file *file)	\
1438c2ecf20Sopenharmony_ci{									\
1448c2ecf20Sopenharmony_ci	__simple_attr_check_format(__fmt, 0ull);			\
1458c2ecf20Sopenharmony_ci	return spufs_attr_open(inode, file, __get, __set, __fmt);	\
1468c2ecf20Sopenharmony_ci}									\
1478c2ecf20Sopenharmony_cistatic const struct file_operations __fops = {				\
1488c2ecf20Sopenharmony_ci	.open	 = __fops ## _open,					\
1498c2ecf20Sopenharmony_ci	.release = spufs_attr_release,					\
1508c2ecf20Sopenharmony_ci	.read	 = spufs_attr_read,					\
1518c2ecf20Sopenharmony_ci	.write	 = spufs_attr_write,					\
1528c2ecf20Sopenharmony_ci	.llseek  = generic_file_llseek,					\
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic int
1578c2ecf20Sopenharmony_cispufs_mem_open(struct inode *inode, struct file *file)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
1608c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
1638c2ecf20Sopenharmony_ci	file->private_data = ctx;
1648c2ecf20Sopenharmony_ci	if (!i->i_openers++)
1658c2ecf20Sopenharmony_ci		ctx->local_store = inode->i_mapping;
1668c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic int
1718c2ecf20Sopenharmony_cispufs_mem_release(struct inode *inode, struct file *file)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
1748c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
1778c2ecf20Sopenharmony_ci	if (!--i->i_openers)
1788c2ecf20Sopenharmony_ci		ctx->local_store = NULL;
1798c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic ssize_t
1848c2ecf20Sopenharmony_cispufs_mem_dump(struct spu_context *ctx, struct coredump_params *cprm)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, ctx->ops->get_ls(ctx), LS_SIZE);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic ssize_t
1908c2ecf20Sopenharmony_cispufs_mem_read(struct file *file, char __user *buffer,
1918c2ecf20Sopenharmony_ci				size_t size, loff_t *pos)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
1948c2ecf20Sopenharmony_ci	ssize_t ret;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
1978c2ecf20Sopenharmony_ci	if (ret)
1988c2ecf20Sopenharmony_ci		return ret;
1998c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(buffer, size, pos, ctx->ops->get_ls(ctx),
2008c2ecf20Sopenharmony_ci				      LS_SIZE);
2018c2ecf20Sopenharmony_ci	spu_release(ctx);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return ret;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic ssize_t
2078c2ecf20Sopenharmony_cispufs_mem_write(struct file *file, const char __user *buffer,
2088c2ecf20Sopenharmony_ci					size_t size, loff_t *ppos)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
2118c2ecf20Sopenharmony_ci	char *local_store;
2128c2ecf20Sopenharmony_ci	loff_t pos = *ppos;
2138c2ecf20Sopenharmony_ci	int ret;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	if (pos > LS_SIZE)
2168c2ecf20Sopenharmony_ci		return -EFBIG;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
2198c2ecf20Sopenharmony_ci	if (ret)
2208c2ecf20Sopenharmony_ci		return ret;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	local_store = ctx->ops->get_ls(ctx);
2238c2ecf20Sopenharmony_ci	size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size);
2248c2ecf20Sopenharmony_ci	spu_release(ctx);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	return size;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic vm_fault_t
2308c2ecf20Sopenharmony_cispufs_mem_mmap_fault(struct vm_fault *vmf)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct vm_area_struct *vma = vmf->vma;
2338c2ecf20Sopenharmony_ci	struct spu_context *ctx	= vma->vm_file->private_data;
2348c2ecf20Sopenharmony_ci	unsigned long pfn, offset;
2358c2ecf20Sopenharmony_ci	vm_fault_t ret;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	offset = vmf->pgoff << PAGE_SHIFT;
2388c2ecf20Sopenharmony_ci	if (offset >= LS_SIZE)
2398c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n",
2428c2ecf20Sopenharmony_ci			vmf->address, offset);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (spu_acquire(ctx))
2458c2ecf20Sopenharmony_ci		return VM_FAULT_NOPAGE;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	if (ctx->state == SPU_STATE_SAVED) {
2488c2ecf20Sopenharmony_ci		vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
2498c2ecf20Sopenharmony_ci		pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset);
2508c2ecf20Sopenharmony_ci	} else {
2518c2ecf20Sopenharmony_ci		vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot);
2528c2ecf20Sopenharmony_ci		pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci	ret = vmf_insert_pfn(vma, vmf->address, pfn);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	spu_release(ctx);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	return ret;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int spufs_mem_mmap_access(struct vm_area_struct *vma,
2628c2ecf20Sopenharmony_ci				unsigned long address,
2638c2ecf20Sopenharmony_ci				void *buf, int len, int write)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	struct spu_context *ctx = vma->vm_file->private_data;
2668c2ecf20Sopenharmony_ci	unsigned long offset = address - vma->vm_start;
2678c2ecf20Sopenharmony_ci	char *local_store;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (write && !(vma->vm_flags & VM_WRITE))
2708c2ecf20Sopenharmony_ci		return -EACCES;
2718c2ecf20Sopenharmony_ci	if (spu_acquire(ctx))
2728c2ecf20Sopenharmony_ci		return -EINTR;
2738c2ecf20Sopenharmony_ci	if ((offset + len) > vma->vm_end)
2748c2ecf20Sopenharmony_ci		len = vma->vm_end - offset;
2758c2ecf20Sopenharmony_ci	local_store = ctx->ops->get_ls(ctx);
2768c2ecf20Sopenharmony_ci	if (write)
2778c2ecf20Sopenharmony_ci		memcpy_toio(local_store + offset, buf, len);
2788c2ecf20Sopenharmony_ci	else
2798c2ecf20Sopenharmony_ci		memcpy_fromio(buf, local_store + offset, len);
2808c2ecf20Sopenharmony_ci	spu_release(ctx);
2818c2ecf20Sopenharmony_ci	return len;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic const struct vm_operations_struct spufs_mem_mmap_vmops = {
2858c2ecf20Sopenharmony_ci	.fault = spufs_mem_mmap_fault,
2868c2ecf20Sopenharmony_ci	.access = spufs_mem_mmap_access,
2878c2ecf20Sopenharmony_ci};
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
2928c2ecf20Sopenharmony_ci		return -EINVAL;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
2958c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	vma->vm_ops = &spufs_mem_mmap_vmops;
2988c2ecf20Sopenharmony_ci	return 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic const struct file_operations spufs_mem_fops = {
3028c2ecf20Sopenharmony_ci	.open			= spufs_mem_open,
3038c2ecf20Sopenharmony_ci	.release		= spufs_mem_release,
3048c2ecf20Sopenharmony_ci	.read			= spufs_mem_read,
3058c2ecf20Sopenharmony_ci	.write			= spufs_mem_write,
3068c2ecf20Sopenharmony_ci	.llseek			= generic_file_llseek,
3078c2ecf20Sopenharmony_ci	.mmap			= spufs_mem_mmap,
3088c2ecf20Sopenharmony_ci};
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic vm_fault_t spufs_ps_fault(struct vm_fault *vmf,
3118c2ecf20Sopenharmony_ci				    unsigned long ps_offs,
3128c2ecf20Sopenharmony_ci				    unsigned long ps_size)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	struct spu_context *ctx = vmf->vma->vm_file->private_data;
3158c2ecf20Sopenharmony_ci	unsigned long area, offset = vmf->pgoff << PAGE_SHIFT;
3168c2ecf20Sopenharmony_ci	int err = 0;
3178c2ecf20Sopenharmony_ci	vm_fault_t ret = VM_FAULT_NOPAGE;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	spu_context_nospu_trace(spufs_ps_fault__enter, ctx);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (offset >= ps_size)
3228c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (fatal_signal_pending(current))
3258c2ecf20Sopenharmony_ci		return VM_FAULT_SIGBUS;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	/*
3288c2ecf20Sopenharmony_ci	 * Because we release the mmap_lock, the context may be destroyed while
3298c2ecf20Sopenharmony_ci	 * we're in spu_wait. Grab an extra reference so it isn't destroyed
3308c2ecf20Sopenharmony_ci	 * in the meantime.
3318c2ecf20Sopenharmony_ci	 */
3328c2ecf20Sopenharmony_ci	get_spu_context(ctx);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	/*
3358c2ecf20Sopenharmony_ci	 * We have to wait for context to be loaded before we have
3368c2ecf20Sopenharmony_ci	 * pages to hand out to the user, but we don't want to wait
3378c2ecf20Sopenharmony_ci	 * with the mmap_lock held.
3388c2ecf20Sopenharmony_ci	 * It is possible to drop the mmap_lock here, but then we need
3398c2ecf20Sopenharmony_ci	 * to return VM_FAULT_NOPAGE because the mappings may have
3408c2ecf20Sopenharmony_ci	 * hanged.
3418c2ecf20Sopenharmony_ci	 */
3428c2ecf20Sopenharmony_ci	if (spu_acquire(ctx))
3438c2ecf20Sopenharmony_ci		goto refault;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (ctx->state == SPU_STATE_SAVED) {
3468c2ecf20Sopenharmony_ci		mmap_read_unlock(current->mm);
3478c2ecf20Sopenharmony_ci		spu_context_nospu_trace(spufs_ps_fault__sleep, ctx);
3488c2ecf20Sopenharmony_ci		err = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE);
3498c2ecf20Sopenharmony_ci		spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu);
3508c2ecf20Sopenharmony_ci		mmap_read_lock(current->mm);
3518c2ecf20Sopenharmony_ci	} else {
3528c2ecf20Sopenharmony_ci		area = ctx->spu->problem_phys + ps_offs;
3538c2ecf20Sopenharmony_ci		ret = vmf_insert_pfn(vmf->vma, vmf->address,
3548c2ecf20Sopenharmony_ci				(area + offset) >> PAGE_SHIFT);
3558c2ecf20Sopenharmony_ci		spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (!err)
3598c2ecf20Sopenharmony_ci		spu_release(ctx);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cirefault:
3628c2ecf20Sopenharmony_ci	put_spu_context(ctx);
3638c2ecf20Sopenharmony_ci	return ret;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci#if SPUFS_MMAP_4K
3678c2ecf20Sopenharmony_cistatic vm_fault_t spufs_cntl_mmap_fault(struct vm_fault *vmf)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x4000, SPUFS_CNTL_MAP_SIZE);
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic const struct vm_operations_struct spufs_cntl_mmap_vmops = {
3738c2ecf20Sopenharmony_ci	.fault = spufs_cntl_mmap_fault,
3748c2ecf20Sopenharmony_ci};
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/*
3778c2ecf20Sopenharmony_ci * mmap support for problem state control area [0x4000 - 0x4fff].
3788c2ecf20Sopenharmony_ci */
3798c2ecf20Sopenharmony_cistatic int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
3828c2ecf20Sopenharmony_ci		return -EINVAL;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
3858c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	vma->vm_ops = &spufs_cntl_mmap_vmops;
3888c2ecf20Sopenharmony_ci	return 0;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci#else /* SPUFS_MMAP_4K */
3918c2ecf20Sopenharmony_ci#define spufs_cntl_mmap NULL
3928c2ecf20Sopenharmony_ci#endif /* !SPUFS_MMAP_4K */
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int spufs_cntl_get(void *data, u64 *val)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
3978c2ecf20Sopenharmony_ci	int ret;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
4008c2ecf20Sopenharmony_ci	if (ret)
4018c2ecf20Sopenharmony_ci		return ret;
4028c2ecf20Sopenharmony_ci	*val = ctx->ops->status_read(ctx);
4038c2ecf20Sopenharmony_ci	spu_release(ctx);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	return 0;
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_cistatic int spufs_cntl_set(void *data, u64 val)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
4118c2ecf20Sopenharmony_ci	int ret;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
4148c2ecf20Sopenharmony_ci	if (ret)
4158c2ecf20Sopenharmony_ci		return ret;
4168c2ecf20Sopenharmony_ci	ctx->ops->runcntl_write(ctx, val);
4178c2ecf20Sopenharmony_ci	spu_release(ctx);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return 0;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_cistatic int spufs_cntl_open(struct inode *inode, struct file *file)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
4258c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
4288c2ecf20Sopenharmony_ci	file->private_data = ctx;
4298c2ecf20Sopenharmony_ci	if (!i->i_openers++)
4308c2ecf20Sopenharmony_ci		ctx->cntl = inode->i_mapping;
4318c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
4328c2ecf20Sopenharmony_ci	return simple_attr_open(inode, file, spufs_cntl_get,
4338c2ecf20Sopenharmony_ci					spufs_cntl_set, "0x%08lx");
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic int
4378c2ecf20Sopenharmony_cispufs_cntl_release(struct inode *inode, struct file *file)
4388c2ecf20Sopenharmony_ci{
4398c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
4408c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	simple_attr_release(inode, file);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
4458c2ecf20Sopenharmony_ci	if (!--i->i_openers)
4468c2ecf20Sopenharmony_ci		ctx->cntl = NULL;
4478c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic const struct file_operations spufs_cntl_fops = {
4528c2ecf20Sopenharmony_ci	.open = spufs_cntl_open,
4538c2ecf20Sopenharmony_ci	.release = spufs_cntl_release,
4548c2ecf20Sopenharmony_ci	.read = simple_attr_read,
4558c2ecf20Sopenharmony_ci	.write = simple_attr_write,
4568c2ecf20Sopenharmony_ci	.llseek	= no_llseek,
4578c2ecf20Sopenharmony_ci	.mmap = spufs_cntl_mmap,
4588c2ecf20Sopenharmony_ci};
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic int
4618c2ecf20Sopenharmony_cispufs_regs_open(struct inode *inode, struct file *file)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
4648c2ecf20Sopenharmony_ci	file->private_data = i->i_ctx;
4658c2ecf20Sopenharmony_ci	return 0;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic ssize_t
4698c2ecf20Sopenharmony_cispufs_regs_dump(struct spu_context *ctx, struct coredump_params *cprm)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, ctx->csa.lscsa->gprs,
4728c2ecf20Sopenharmony_ci			       sizeof(ctx->csa.lscsa->gprs));
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic ssize_t
4768c2ecf20Sopenharmony_cispufs_regs_read(struct file *file, char __user *buffer,
4778c2ecf20Sopenharmony_ci		size_t size, loff_t *pos)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	int ret;
4808c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/* pre-check for file position: if we'd return EOF, there's no point
4838c2ecf20Sopenharmony_ci	 * causing a deschedule */
4848c2ecf20Sopenharmony_ci	if (*pos >= sizeof(ctx->csa.lscsa->gprs))
4858c2ecf20Sopenharmony_ci		return 0;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
4888c2ecf20Sopenharmony_ci	if (ret)
4898c2ecf20Sopenharmony_ci		return ret;
4908c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(buffer, size, pos, ctx->csa.lscsa->gprs,
4918c2ecf20Sopenharmony_ci				      sizeof(ctx->csa.lscsa->gprs));
4928c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
4938c2ecf20Sopenharmony_ci	return ret;
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_cistatic ssize_t
4978c2ecf20Sopenharmony_cispufs_regs_write(struct file *file, const char __user *buffer,
4988c2ecf20Sopenharmony_ci		 size_t size, loff_t *pos)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
5018c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
5028c2ecf20Sopenharmony_ci	int ret;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (*pos >= sizeof(lscsa->gprs))
5058c2ecf20Sopenharmony_ci		return -EFBIG;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
5088c2ecf20Sopenharmony_ci	if (ret)
5098c2ecf20Sopenharmony_ci		return ret;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos,
5128c2ecf20Sopenharmony_ci					buffer, size);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
5158c2ecf20Sopenharmony_ci	return size;
5168c2ecf20Sopenharmony_ci}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_cistatic const struct file_operations spufs_regs_fops = {
5198c2ecf20Sopenharmony_ci	.open	 = spufs_regs_open,
5208c2ecf20Sopenharmony_ci	.read    = spufs_regs_read,
5218c2ecf20Sopenharmony_ci	.write   = spufs_regs_write,
5228c2ecf20Sopenharmony_ci	.llseek  = generic_file_llseek,
5238c2ecf20Sopenharmony_ci};
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic ssize_t
5268c2ecf20Sopenharmony_cispufs_fpcr_dump(struct spu_context *ctx, struct coredump_params *cprm)
5278c2ecf20Sopenharmony_ci{
5288c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &ctx->csa.lscsa->fpcr,
5298c2ecf20Sopenharmony_ci			       sizeof(ctx->csa.lscsa->fpcr));
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic ssize_t
5338c2ecf20Sopenharmony_cispufs_fpcr_read(struct file *file, char __user * buffer,
5348c2ecf20Sopenharmony_ci		size_t size, loff_t * pos)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	int ret;
5378c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
5408c2ecf20Sopenharmony_ci	if (ret)
5418c2ecf20Sopenharmony_ci		return ret;
5428c2ecf20Sopenharmony_ci	ret = simple_read_from_buffer(buffer, size, pos, &ctx->csa.lscsa->fpcr,
5438c2ecf20Sopenharmony_ci				      sizeof(ctx->csa.lscsa->fpcr));
5448c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
5458c2ecf20Sopenharmony_ci	return ret;
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic ssize_t
5498c2ecf20Sopenharmony_cispufs_fpcr_write(struct file *file, const char __user * buffer,
5508c2ecf20Sopenharmony_ci		 size_t size, loff_t * pos)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
5538c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
5548c2ecf20Sopenharmony_ci	int ret;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (*pos >= sizeof(lscsa->fpcr))
5578c2ecf20Sopenharmony_ci		return -EFBIG;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
5608c2ecf20Sopenharmony_ci	if (ret)
5618c2ecf20Sopenharmony_ci		return ret;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos,
5648c2ecf20Sopenharmony_ci					buffer, size);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
5678c2ecf20Sopenharmony_ci	return size;
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic const struct file_operations spufs_fpcr_fops = {
5718c2ecf20Sopenharmony_ci	.open = spufs_regs_open,
5728c2ecf20Sopenharmony_ci	.read = spufs_fpcr_read,
5738c2ecf20Sopenharmony_ci	.write = spufs_fpcr_write,
5748c2ecf20Sopenharmony_ci	.llseek = generic_file_llseek,
5758c2ecf20Sopenharmony_ci};
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/* generic open function for all pipe-like files */
5788c2ecf20Sopenharmony_cistatic int spufs_pipe_open(struct inode *inode, struct file *file)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
5818c2ecf20Sopenharmony_ci	file->private_data = i->i_ctx;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	return stream_open(inode, file);
5848c2ecf20Sopenharmony_ci}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci/*
5878c2ecf20Sopenharmony_ci * Read as many bytes from the mailbox as possible, until
5888c2ecf20Sopenharmony_ci * one of the conditions becomes true:
5898c2ecf20Sopenharmony_ci *
5908c2ecf20Sopenharmony_ci * - no more data available in the mailbox
5918c2ecf20Sopenharmony_ci * - end of the user provided buffer
5928c2ecf20Sopenharmony_ci * - end of the mapped area
5938c2ecf20Sopenharmony_ci */
5948c2ecf20Sopenharmony_cistatic ssize_t spufs_mbox_read(struct file *file, char __user *buf,
5958c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
5988c2ecf20Sopenharmony_ci	u32 mbox_data, __user *udata = (void __user *)buf;
5998c2ecf20Sopenharmony_ci	ssize_t count;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (len < 4)
6028c2ecf20Sopenharmony_ci		return -EINVAL;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	count = spu_acquire(ctx);
6058c2ecf20Sopenharmony_ci	if (count)
6068c2ecf20Sopenharmony_ci		return count;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	for (count = 0; (count + 4) <= len; count += 4, udata++) {
6098c2ecf20Sopenharmony_ci		int ret;
6108c2ecf20Sopenharmony_ci		ret = ctx->ops->mbox_read(ctx, &mbox_data);
6118c2ecf20Sopenharmony_ci		if (ret == 0)
6128c2ecf20Sopenharmony_ci			break;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci		/*
6158c2ecf20Sopenharmony_ci		 * at the end of the mapped area, we can fault
6168c2ecf20Sopenharmony_ci		 * but still need to return the data we have
6178c2ecf20Sopenharmony_ci		 * read successfully so far.
6188c2ecf20Sopenharmony_ci		 */
6198c2ecf20Sopenharmony_ci		ret = put_user(mbox_data, udata);
6208c2ecf20Sopenharmony_ci		if (ret) {
6218c2ecf20Sopenharmony_ci			if (!count)
6228c2ecf20Sopenharmony_ci				count = -EFAULT;
6238c2ecf20Sopenharmony_ci			break;
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci	spu_release(ctx);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (!count)
6298c2ecf20Sopenharmony_ci		count = -EAGAIN;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	return count;
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic const struct file_operations spufs_mbox_fops = {
6358c2ecf20Sopenharmony_ci	.open	= spufs_pipe_open,
6368c2ecf20Sopenharmony_ci	.read	= spufs_mbox_read,
6378c2ecf20Sopenharmony_ci	.llseek	= no_llseek,
6388c2ecf20Sopenharmony_ci};
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
6418c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
6448c2ecf20Sopenharmony_ci	ssize_t ret;
6458c2ecf20Sopenharmony_ci	u32 mbox_stat;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	if (len < 4)
6488c2ecf20Sopenharmony_ci		return -EINVAL;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
6518c2ecf20Sopenharmony_ci	if (ret)
6528c2ecf20Sopenharmony_ci		return ret;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	spu_release(ctx);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat))
6598c2ecf20Sopenharmony_ci		return -EFAULT;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	return 4;
6628c2ecf20Sopenharmony_ci}
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_cistatic const struct file_operations spufs_mbox_stat_fops = {
6658c2ecf20Sopenharmony_ci	.open	= spufs_pipe_open,
6668c2ecf20Sopenharmony_ci	.read	= spufs_mbox_stat_read,
6678c2ecf20Sopenharmony_ci	.llseek = no_llseek,
6688c2ecf20Sopenharmony_ci};
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci/* low-level ibox access function */
6718c2ecf20Sopenharmony_cisize_t spu_ibox_read(struct spu_context *ctx, u32 *data)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	return ctx->ops->ibox_read(ctx, data);
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci/* interrupt-level ibox callback function. */
6778c2ecf20Sopenharmony_civoid spufs_ibox_callback(struct spu *spu)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct spu_context *ctx = spu->ctx;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (ctx)
6828c2ecf20Sopenharmony_ci		wake_up_all(&ctx->ibox_wq);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci/*
6868c2ecf20Sopenharmony_ci * Read as many bytes from the interrupt mailbox as possible, until
6878c2ecf20Sopenharmony_ci * one of the conditions becomes true:
6888c2ecf20Sopenharmony_ci *
6898c2ecf20Sopenharmony_ci * - no more data available in the mailbox
6908c2ecf20Sopenharmony_ci * - end of the user provided buffer
6918c2ecf20Sopenharmony_ci * - end of the mapped area
6928c2ecf20Sopenharmony_ci *
6938c2ecf20Sopenharmony_ci * If the file is opened without O_NONBLOCK, we wait here until
6948c2ecf20Sopenharmony_ci * any data is available, but return when we have been able to
6958c2ecf20Sopenharmony_ci * read something.
6968c2ecf20Sopenharmony_ci */
6978c2ecf20Sopenharmony_cistatic ssize_t spufs_ibox_read(struct file *file, char __user *buf,
6988c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
7018c2ecf20Sopenharmony_ci	u32 ibox_data, __user *udata = (void __user *)buf;
7028c2ecf20Sopenharmony_ci	ssize_t count;
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	if (len < 4)
7058c2ecf20Sopenharmony_ci		return -EINVAL;
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	count = spu_acquire(ctx);
7088c2ecf20Sopenharmony_ci	if (count)
7098c2ecf20Sopenharmony_ci		goto out;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	/* wait only for the first element */
7128c2ecf20Sopenharmony_ci	count = 0;
7138c2ecf20Sopenharmony_ci	if (file->f_flags & O_NONBLOCK) {
7148c2ecf20Sopenharmony_ci		if (!spu_ibox_read(ctx, &ibox_data)) {
7158c2ecf20Sopenharmony_ci			count = -EAGAIN;
7168c2ecf20Sopenharmony_ci			goto out_unlock;
7178c2ecf20Sopenharmony_ci		}
7188c2ecf20Sopenharmony_ci	} else {
7198c2ecf20Sopenharmony_ci		count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data));
7208c2ecf20Sopenharmony_ci		if (count)
7218c2ecf20Sopenharmony_ci			goto out;
7228c2ecf20Sopenharmony_ci	}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	/* if we can't write at all, return -EFAULT */
7258c2ecf20Sopenharmony_ci	count = put_user(ibox_data, udata);
7268c2ecf20Sopenharmony_ci	if (count)
7278c2ecf20Sopenharmony_ci		goto out_unlock;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
7308c2ecf20Sopenharmony_ci		int ret;
7318c2ecf20Sopenharmony_ci		ret = ctx->ops->ibox_read(ctx, &ibox_data);
7328c2ecf20Sopenharmony_ci		if (ret == 0)
7338c2ecf20Sopenharmony_ci			break;
7348c2ecf20Sopenharmony_ci		/*
7358c2ecf20Sopenharmony_ci		 * at the end of the mapped area, we can fault
7368c2ecf20Sopenharmony_ci		 * but still need to return the data we have
7378c2ecf20Sopenharmony_ci		 * read successfully so far.
7388c2ecf20Sopenharmony_ci		 */
7398c2ecf20Sopenharmony_ci		ret = put_user(ibox_data, udata);
7408c2ecf20Sopenharmony_ci		if (ret)
7418c2ecf20Sopenharmony_ci			break;
7428c2ecf20Sopenharmony_ci	}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ciout_unlock:
7458c2ecf20Sopenharmony_ci	spu_release(ctx);
7468c2ecf20Sopenharmony_ciout:
7478c2ecf20Sopenharmony_ci	return count;
7488c2ecf20Sopenharmony_ci}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_cistatic __poll_t spufs_ibox_poll(struct file *file, poll_table *wait)
7518c2ecf20Sopenharmony_ci{
7528c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
7538c2ecf20Sopenharmony_ci	__poll_t mask;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	poll_wait(file, &ctx->ibox_wq, wait);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	/*
7588c2ecf20Sopenharmony_ci	 * For now keep this uninterruptible and also ignore the rule
7598c2ecf20Sopenharmony_ci	 * that poll should not sleep.  Will be fixed later.
7608c2ecf20Sopenharmony_ci	 */
7618c2ecf20Sopenharmony_ci	mutex_lock(&ctx->state_mutex);
7628c2ecf20Sopenharmony_ci	mask = ctx->ops->mbox_stat_poll(ctx, EPOLLIN | EPOLLRDNORM);
7638c2ecf20Sopenharmony_ci	spu_release(ctx);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	return mask;
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_cistatic const struct file_operations spufs_ibox_fops = {
7698c2ecf20Sopenharmony_ci	.open	= spufs_pipe_open,
7708c2ecf20Sopenharmony_ci	.read	= spufs_ibox_read,
7718c2ecf20Sopenharmony_ci	.poll	= spufs_ibox_poll,
7728c2ecf20Sopenharmony_ci	.llseek = no_llseek,
7738c2ecf20Sopenharmony_ci};
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_cistatic ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
7768c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
7778c2ecf20Sopenharmony_ci{
7788c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
7798c2ecf20Sopenharmony_ci	ssize_t ret;
7808c2ecf20Sopenharmony_ci	u32 ibox_stat;
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (len < 4)
7838c2ecf20Sopenharmony_ci		return -EINVAL;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
7868c2ecf20Sopenharmony_ci	if (ret)
7878c2ecf20Sopenharmony_ci		return ret;
7888c2ecf20Sopenharmony_ci	ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff;
7898c2ecf20Sopenharmony_ci	spu_release(ctx);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat))
7928c2ecf20Sopenharmony_ci		return -EFAULT;
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	return 4;
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_cistatic const struct file_operations spufs_ibox_stat_fops = {
7988c2ecf20Sopenharmony_ci	.open	= spufs_pipe_open,
7998c2ecf20Sopenharmony_ci	.read	= spufs_ibox_stat_read,
8008c2ecf20Sopenharmony_ci	.llseek = no_llseek,
8018c2ecf20Sopenharmony_ci};
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci/* low-level mailbox write */
8048c2ecf20Sopenharmony_cisize_t spu_wbox_write(struct spu_context *ctx, u32 data)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	return ctx->ops->wbox_write(ctx, data);
8078c2ecf20Sopenharmony_ci}
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci/* interrupt-level wbox callback function. */
8108c2ecf20Sopenharmony_civoid spufs_wbox_callback(struct spu *spu)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	struct spu_context *ctx = spu->ctx;
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	if (ctx)
8158c2ecf20Sopenharmony_ci		wake_up_all(&ctx->wbox_wq);
8168c2ecf20Sopenharmony_ci}
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci/*
8198c2ecf20Sopenharmony_ci * Write as many bytes to the interrupt mailbox as possible, until
8208c2ecf20Sopenharmony_ci * one of the conditions becomes true:
8218c2ecf20Sopenharmony_ci *
8228c2ecf20Sopenharmony_ci * - the mailbox is full
8238c2ecf20Sopenharmony_ci * - end of the user provided buffer
8248c2ecf20Sopenharmony_ci * - end of the mapped area
8258c2ecf20Sopenharmony_ci *
8268c2ecf20Sopenharmony_ci * If the file is opened without O_NONBLOCK, we wait here until
8278c2ecf20Sopenharmony_ci * space is available, but return when we have been able to
8288c2ecf20Sopenharmony_ci * write something.
8298c2ecf20Sopenharmony_ci */
8308c2ecf20Sopenharmony_cistatic ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
8318c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
8348c2ecf20Sopenharmony_ci	u32 wbox_data, __user *udata = (void __user *)buf;
8358c2ecf20Sopenharmony_ci	ssize_t count;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	if (len < 4)
8388c2ecf20Sopenharmony_ci		return -EINVAL;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	if (get_user(wbox_data, udata))
8418c2ecf20Sopenharmony_ci		return -EFAULT;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	count = spu_acquire(ctx);
8448c2ecf20Sopenharmony_ci	if (count)
8458c2ecf20Sopenharmony_ci		goto out;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	/*
8488c2ecf20Sopenharmony_ci	 * make sure we can at least write one element, by waiting
8498c2ecf20Sopenharmony_ci	 * in case of !O_NONBLOCK
8508c2ecf20Sopenharmony_ci	 */
8518c2ecf20Sopenharmony_ci	count = 0;
8528c2ecf20Sopenharmony_ci	if (file->f_flags & O_NONBLOCK) {
8538c2ecf20Sopenharmony_ci		if (!spu_wbox_write(ctx, wbox_data)) {
8548c2ecf20Sopenharmony_ci			count = -EAGAIN;
8558c2ecf20Sopenharmony_ci			goto out_unlock;
8568c2ecf20Sopenharmony_ci		}
8578c2ecf20Sopenharmony_ci	} else {
8588c2ecf20Sopenharmony_ci		count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data));
8598c2ecf20Sopenharmony_ci		if (count)
8608c2ecf20Sopenharmony_ci			goto out;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* write as much as possible */
8658c2ecf20Sopenharmony_ci	for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) {
8668c2ecf20Sopenharmony_ci		int ret;
8678c2ecf20Sopenharmony_ci		ret = get_user(wbox_data, udata);
8688c2ecf20Sopenharmony_ci		if (ret)
8698c2ecf20Sopenharmony_ci			break;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci		ret = spu_wbox_write(ctx, wbox_data);
8728c2ecf20Sopenharmony_ci		if (ret == 0)
8738c2ecf20Sopenharmony_ci			break;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ciout_unlock:
8778c2ecf20Sopenharmony_ci	spu_release(ctx);
8788c2ecf20Sopenharmony_ciout:
8798c2ecf20Sopenharmony_ci	return count;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic __poll_t spufs_wbox_poll(struct file *file, poll_table *wait)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
8858c2ecf20Sopenharmony_ci	__poll_t mask;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	poll_wait(file, &ctx->wbox_wq, wait);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	/*
8908c2ecf20Sopenharmony_ci	 * For now keep this uninterruptible and also ignore the rule
8918c2ecf20Sopenharmony_ci	 * that poll should not sleep.  Will be fixed later.
8928c2ecf20Sopenharmony_ci	 */
8938c2ecf20Sopenharmony_ci	mutex_lock(&ctx->state_mutex);
8948c2ecf20Sopenharmony_ci	mask = ctx->ops->mbox_stat_poll(ctx, EPOLLOUT | EPOLLWRNORM);
8958c2ecf20Sopenharmony_ci	spu_release(ctx);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	return mask;
8988c2ecf20Sopenharmony_ci}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_cistatic const struct file_operations spufs_wbox_fops = {
9018c2ecf20Sopenharmony_ci	.open	= spufs_pipe_open,
9028c2ecf20Sopenharmony_ci	.write	= spufs_wbox_write,
9038c2ecf20Sopenharmony_ci	.poll	= spufs_wbox_poll,
9048c2ecf20Sopenharmony_ci	.llseek = no_llseek,
9058c2ecf20Sopenharmony_ci};
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cistatic ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
9088c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
9118c2ecf20Sopenharmony_ci	ssize_t ret;
9128c2ecf20Sopenharmony_ci	u32 wbox_stat;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (len < 4)
9158c2ecf20Sopenharmony_ci		return -EINVAL;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
9188c2ecf20Sopenharmony_ci	if (ret)
9198c2ecf20Sopenharmony_ci		return ret;
9208c2ecf20Sopenharmony_ci	wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff;
9218c2ecf20Sopenharmony_ci	spu_release(ctx);
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat))
9248c2ecf20Sopenharmony_ci		return -EFAULT;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	return 4;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cistatic const struct file_operations spufs_wbox_stat_fops = {
9308c2ecf20Sopenharmony_ci	.open	= spufs_pipe_open,
9318c2ecf20Sopenharmony_ci	.read	= spufs_wbox_stat_read,
9328c2ecf20Sopenharmony_ci	.llseek = no_llseek,
9338c2ecf20Sopenharmony_ci};
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_cistatic int spufs_signal1_open(struct inode *inode, struct file *file)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
9388c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
9418c2ecf20Sopenharmony_ci	file->private_data = ctx;
9428c2ecf20Sopenharmony_ci	if (!i->i_openers++)
9438c2ecf20Sopenharmony_ci		ctx->signal1 = inode->i_mapping;
9448c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
9458c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
9468c2ecf20Sopenharmony_ci}
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic int
9498c2ecf20Sopenharmony_cispufs_signal1_release(struct inode *inode, struct file *file)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
9528c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
9558c2ecf20Sopenharmony_ci	if (!--i->i_openers)
9568c2ecf20Sopenharmony_ci		ctx->signal1 = NULL;
9578c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
9588c2ecf20Sopenharmony_ci	return 0;
9598c2ecf20Sopenharmony_ci}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_cistatic ssize_t spufs_signal1_dump(struct spu_context *ctx,
9628c2ecf20Sopenharmony_ci		struct coredump_params *cprm)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	if (!ctx->csa.spu_chnlcnt_RW[3])
9658c2ecf20Sopenharmony_ci		return 0;
9668c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &ctx->csa.spu_chnldata_RW[3],
9678c2ecf20Sopenharmony_ci			       sizeof(ctx->csa.spu_chnldata_RW[3]));
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_cistatic ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf,
9718c2ecf20Sopenharmony_ci			size_t len)
9728c2ecf20Sopenharmony_ci{
9738c2ecf20Sopenharmony_ci	if (len < sizeof(ctx->csa.spu_chnldata_RW[3]))
9748c2ecf20Sopenharmony_ci		return -EINVAL;
9758c2ecf20Sopenharmony_ci	if (!ctx->csa.spu_chnlcnt_RW[3])
9768c2ecf20Sopenharmony_ci		return 0;
9778c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &ctx->csa.spu_chnldata_RW[3],
9788c2ecf20Sopenharmony_ci			 sizeof(ctx->csa.spu_chnldata_RW[3])))
9798c2ecf20Sopenharmony_ci		return -EFAULT;
9808c2ecf20Sopenharmony_ci	return sizeof(ctx->csa.spu_chnldata_RW[3]);
9818c2ecf20Sopenharmony_ci}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_cistatic ssize_t spufs_signal1_read(struct file *file, char __user *buf,
9848c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
9858c2ecf20Sopenharmony_ci{
9868c2ecf20Sopenharmony_ci	int ret;
9878c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
9908c2ecf20Sopenharmony_ci	if (ret)
9918c2ecf20Sopenharmony_ci		return ret;
9928c2ecf20Sopenharmony_ci	ret = __spufs_signal1_read(ctx, buf, len);
9938c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	return ret;
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_cistatic ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
9998c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
10008c2ecf20Sopenharmony_ci{
10018c2ecf20Sopenharmony_ci	struct spu_context *ctx;
10028c2ecf20Sopenharmony_ci	ssize_t ret;
10038c2ecf20Sopenharmony_ci	u32 data;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	ctx = file->private_data;
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (len < 4)
10088c2ecf20Sopenharmony_ci		return -EINVAL;
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_ci	if (copy_from_user(&data, buf, 4))
10118c2ecf20Sopenharmony_ci		return -EFAULT;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
10148c2ecf20Sopenharmony_ci	if (ret)
10158c2ecf20Sopenharmony_ci		return ret;
10168c2ecf20Sopenharmony_ci	ctx->ops->signal1_write(ctx, data);
10178c2ecf20Sopenharmony_ci	spu_release(ctx);
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci	return 4;
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_cistatic vm_fault_t
10238c2ecf20Sopenharmony_cispufs_signal1_mmap_fault(struct vm_fault *vmf)
10248c2ecf20Sopenharmony_ci{
10258c2ecf20Sopenharmony_ci#if SPUFS_SIGNAL_MAP_SIZE == 0x1000
10268c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE);
10278c2ecf20Sopenharmony_ci#elif SPUFS_SIGNAL_MAP_SIZE == 0x10000
10288c2ecf20Sopenharmony_ci	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
10298c2ecf20Sopenharmony_ci	 * signal 1 and 2 area
10308c2ecf20Sopenharmony_ci	 */
10318c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE);
10328c2ecf20Sopenharmony_ci#else
10338c2ecf20Sopenharmony_ci#error unsupported page size
10348c2ecf20Sopenharmony_ci#endif
10358c2ecf20Sopenharmony_ci}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_cistatic const struct vm_operations_struct spufs_signal1_mmap_vmops = {
10388c2ecf20Sopenharmony_ci	.fault = spufs_signal1_mmap_fault,
10398c2ecf20Sopenharmony_ci};
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
10448c2ecf20Sopenharmony_ci		return -EINVAL;
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
10478c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	vma->vm_ops = &spufs_signal1_mmap_vmops;
10508c2ecf20Sopenharmony_ci	return 0;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic const struct file_operations spufs_signal1_fops = {
10548c2ecf20Sopenharmony_ci	.open = spufs_signal1_open,
10558c2ecf20Sopenharmony_ci	.release = spufs_signal1_release,
10568c2ecf20Sopenharmony_ci	.read = spufs_signal1_read,
10578c2ecf20Sopenharmony_ci	.write = spufs_signal1_write,
10588c2ecf20Sopenharmony_ci	.mmap = spufs_signal1_mmap,
10598c2ecf20Sopenharmony_ci	.llseek = no_llseek,
10608c2ecf20Sopenharmony_ci};
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_cistatic const struct file_operations spufs_signal1_nosched_fops = {
10638c2ecf20Sopenharmony_ci	.open = spufs_signal1_open,
10648c2ecf20Sopenharmony_ci	.release = spufs_signal1_release,
10658c2ecf20Sopenharmony_ci	.write = spufs_signal1_write,
10668c2ecf20Sopenharmony_ci	.mmap = spufs_signal1_mmap,
10678c2ecf20Sopenharmony_ci	.llseek = no_llseek,
10688c2ecf20Sopenharmony_ci};
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic int spufs_signal2_open(struct inode *inode, struct file *file)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
10738c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
10768c2ecf20Sopenharmony_ci	file->private_data = ctx;
10778c2ecf20Sopenharmony_ci	if (!i->i_openers++)
10788c2ecf20Sopenharmony_ci		ctx->signal2 = inode->i_mapping;
10798c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
10808c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic int
10848c2ecf20Sopenharmony_cispufs_signal2_release(struct inode *inode, struct file *file)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
10878c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
10908c2ecf20Sopenharmony_ci	if (!--i->i_openers)
10918c2ecf20Sopenharmony_ci		ctx->signal2 = NULL;
10928c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
10938c2ecf20Sopenharmony_ci	return 0;
10948c2ecf20Sopenharmony_ci}
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_cistatic ssize_t spufs_signal2_dump(struct spu_context *ctx,
10978c2ecf20Sopenharmony_ci		struct coredump_params *cprm)
10988c2ecf20Sopenharmony_ci{
10998c2ecf20Sopenharmony_ci	if (!ctx->csa.spu_chnlcnt_RW[4])
11008c2ecf20Sopenharmony_ci		return 0;
11018c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &ctx->csa.spu_chnldata_RW[4],
11028c2ecf20Sopenharmony_ci			       sizeof(ctx->csa.spu_chnldata_RW[4]));
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_cistatic ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf,
11068c2ecf20Sopenharmony_ci			size_t len)
11078c2ecf20Sopenharmony_ci{
11088c2ecf20Sopenharmony_ci	if (len < sizeof(ctx->csa.spu_chnldata_RW[4]))
11098c2ecf20Sopenharmony_ci		return -EINVAL;
11108c2ecf20Sopenharmony_ci	if (!ctx->csa.spu_chnlcnt_RW[4])
11118c2ecf20Sopenharmony_ci		return 0;
11128c2ecf20Sopenharmony_ci	if (copy_to_user(buf, &ctx->csa.spu_chnldata_RW[4],
11138c2ecf20Sopenharmony_ci			 sizeof(ctx->csa.spu_chnldata_RW[4])))
11148c2ecf20Sopenharmony_ci		return -EFAULT;
11158c2ecf20Sopenharmony_ci	return sizeof(ctx->csa.spu_chnldata_RW[4]);
11168c2ecf20Sopenharmony_ci}
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_cistatic ssize_t spufs_signal2_read(struct file *file, char __user *buf,
11198c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
11208c2ecf20Sopenharmony_ci{
11218c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
11228c2ecf20Sopenharmony_ci	int ret;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
11258c2ecf20Sopenharmony_ci	if (ret)
11268c2ecf20Sopenharmony_ci		return ret;
11278c2ecf20Sopenharmony_ci	ret = __spufs_signal2_read(ctx, buf, len);
11288c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	return ret;
11318c2ecf20Sopenharmony_ci}
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_cistatic ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
11348c2ecf20Sopenharmony_ci			size_t len, loff_t *pos)
11358c2ecf20Sopenharmony_ci{
11368c2ecf20Sopenharmony_ci	struct spu_context *ctx;
11378c2ecf20Sopenharmony_ci	ssize_t ret;
11388c2ecf20Sopenharmony_ci	u32 data;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	ctx = file->private_data;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (len < 4)
11438c2ecf20Sopenharmony_ci		return -EINVAL;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	if (copy_from_user(&data, buf, 4))
11468c2ecf20Sopenharmony_ci		return -EFAULT;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
11498c2ecf20Sopenharmony_ci	if (ret)
11508c2ecf20Sopenharmony_ci		return ret;
11518c2ecf20Sopenharmony_ci	ctx->ops->signal2_write(ctx, data);
11528c2ecf20Sopenharmony_ci	spu_release(ctx);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	return 4;
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci#if SPUFS_MMAP_4K
11588c2ecf20Sopenharmony_cistatic vm_fault_t
11598c2ecf20Sopenharmony_cispufs_signal2_mmap_fault(struct vm_fault *vmf)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci#if SPUFS_SIGNAL_MAP_SIZE == 0x1000
11628c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE);
11638c2ecf20Sopenharmony_ci#elif SPUFS_SIGNAL_MAP_SIZE == 0x10000
11648c2ecf20Sopenharmony_ci	/* For 64k pages, both signal1 and signal2 can be used to mmap the whole
11658c2ecf20Sopenharmony_ci	 * signal 1 and 2 area
11668c2ecf20Sopenharmony_ci	 */
11678c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE);
11688c2ecf20Sopenharmony_ci#else
11698c2ecf20Sopenharmony_ci#error unsupported page size
11708c2ecf20Sopenharmony_ci#endif
11718c2ecf20Sopenharmony_ci}
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_cistatic const struct vm_operations_struct spufs_signal2_mmap_vmops = {
11748c2ecf20Sopenharmony_ci	.fault = spufs_signal2_mmap_fault,
11758c2ecf20Sopenharmony_ci};
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_cistatic int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
11788c2ecf20Sopenharmony_ci{
11798c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
11808c2ecf20Sopenharmony_ci		return -EINVAL;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
11838c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci	vma->vm_ops = &spufs_signal2_mmap_vmops;
11868c2ecf20Sopenharmony_ci	return 0;
11878c2ecf20Sopenharmony_ci}
11888c2ecf20Sopenharmony_ci#else /* SPUFS_MMAP_4K */
11898c2ecf20Sopenharmony_ci#define spufs_signal2_mmap NULL
11908c2ecf20Sopenharmony_ci#endif /* !SPUFS_MMAP_4K */
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic const struct file_operations spufs_signal2_fops = {
11938c2ecf20Sopenharmony_ci	.open = spufs_signal2_open,
11948c2ecf20Sopenharmony_ci	.release = spufs_signal2_release,
11958c2ecf20Sopenharmony_ci	.read = spufs_signal2_read,
11968c2ecf20Sopenharmony_ci	.write = spufs_signal2_write,
11978c2ecf20Sopenharmony_ci	.mmap = spufs_signal2_mmap,
11988c2ecf20Sopenharmony_ci	.llseek = no_llseek,
11998c2ecf20Sopenharmony_ci};
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_cistatic const struct file_operations spufs_signal2_nosched_fops = {
12028c2ecf20Sopenharmony_ci	.open = spufs_signal2_open,
12038c2ecf20Sopenharmony_ci	.release = spufs_signal2_release,
12048c2ecf20Sopenharmony_ci	.write = spufs_signal2_write,
12058c2ecf20Sopenharmony_ci	.mmap = spufs_signal2_mmap,
12068c2ecf20Sopenharmony_ci	.llseek = no_llseek,
12078c2ecf20Sopenharmony_ci};
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci/*
12108c2ecf20Sopenharmony_ci * This is a wrapper around DEFINE_SIMPLE_ATTRIBUTE which does the
12118c2ecf20Sopenharmony_ci * work of acquiring (or not) the SPU context before calling through
12128c2ecf20Sopenharmony_ci * to the actual get routine. The set routine is called directly.
12138c2ecf20Sopenharmony_ci */
12148c2ecf20Sopenharmony_ci#define SPU_ATTR_NOACQUIRE	0
12158c2ecf20Sopenharmony_ci#define SPU_ATTR_ACQUIRE	1
12168c2ecf20Sopenharmony_ci#define SPU_ATTR_ACQUIRE_SAVED	2
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci#define DEFINE_SPUFS_ATTRIBUTE(__name, __get, __set, __fmt, __acquire)	\
12198c2ecf20Sopenharmony_cistatic int __##__get(void *data, u64 *val)				\
12208c2ecf20Sopenharmony_ci{									\
12218c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;					\
12228c2ecf20Sopenharmony_ci	int ret = 0;							\
12238c2ecf20Sopenharmony_ci									\
12248c2ecf20Sopenharmony_ci	if (__acquire == SPU_ATTR_ACQUIRE) {				\
12258c2ecf20Sopenharmony_ci		ret = spu_acquire(ctx);					\
12268c2ecf20Sopenharmony_ci		if (ret)						\
12278c2ecf20Sopenharmony_ci			return ret;					\
12288c2ecf20Sopenharmony_ci		*val = __get(ctx);					\
12298c2ecf20Sopenharmony_ci		spu_release(ctx);					\
12308c2ecf20Sopenharmony_ci	} else if (__acquire == SPU_ATTR_ACQUIRE_SAVED)	{		\
12318c2ecf20Sopenharmony_ci		ret = spu_acquire_saved(ctx);				\
12328c2ecf20Sopenharmony_ci		if (ret)						\
12338c2ecf20Sopenharmony_ci			return ret;					\
12348c2ecf20Sopenharmony_ci		*val = __get(ctx);					\
12358c2ecf20Sopenharmony_ci		spu_release_saved(ctx);					\
12368c2ecf20Sopenharmony_ci	} else								\
12378c2ecf20Sopenharmony_ci		*val = __get(ctx);					\
12388c2ecf20Sopenharmony_ci									\
12398c2ecf20Sopenharmony_ci	return 0;							\
12408c2ecf20Sopenharmony_ci}									\
12418c2ecf20Sopenharmony_ciDEFINE_SPUFS_SIMPLE_ATTRIBUTE(__name, __##__get, __set, __fmt);
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_cistatic int spufs_signal1_type_set(void *data, u64 val)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
12468c2ecf20Sopenharmony_ci	int ret;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
12498c2ecf20Sopenharmony_ci	if (ret)
12508c2ecf20Sopenharmony_ci		return ret;
12518c2ecf20Sopenharmony_ci	ctx->ops->signal1_type_set(ctx, val);
12528c2ecf20Sopenharmony_ci	spu_release(ctx);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	return 0;
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_cistatic u64 spufs_signal1_type_get(struct spu_context *ctx)
12588c2ecf20Sopenharmony_ci{
12598c2ecf20Sopenharmony_ci	return ctx->ops->signal1_type_get(ctx);
12608c2ecf20Sopenharmony_ci}
12618c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
12628c2ecf20Sopenharmony_ci		       spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE);
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_cistatic int spufs_signal2_type_set(void *data, u64 val)
12668c2ecf20Sopenharmony_ci{
12678c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
12688c2ecf20Sopenharmony_ci	int ret;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
12718c2ecf20Sopenharmony_ci	if (ret)
12728c2ecf20Sopenharmony_ci		return ret;
12738c2ecf20Sopenharmony_ci	ctx->ops->signal2_type_set(ctx, val);
12748c2ecf20Sopenharmony_ci	spu_release(ctx);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	return 0;
12778c2ecf20Sopenharmony_ci}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_cistatic u64 spufs_signal2_type_get(struct spu_context *ctx)
12808c2ecf20Sopenharmony_ci{
12818c2ecf20Sopenharmony_ci	return ctx->ops->signal2_type_get(ctx);
12828c2ecf20Sopenharmony_ci}
12838c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
12848c2ecf20Sopenharmony_ci		       spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE);
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci#if SPUFS_MMAP_4K
12878c2ecf20Sopenharmony_cistatic vm_fault_t
12888c2ecf20Sopenharmony_cispufs_mss_mmap_fault(struct vm_fault *vmf)
12898c2ecf20Sopenharmony_ci{
12908c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x0000, SPUFS_MSS_MAP_SIZE);
12918c2ecf20Sopenharmony_ci}
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cistatic const struct vm_operations_struct spufs_mss_mmap_vmops = {
12948c2ecf20Sopenharmony_ci	.fault = spufs_mss_mmap_fault,
12958c2ecf20Sopenharmony_ci};
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci/*
12988c2ecf20Sopenharmony_ci * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
12998c2ecf20Sopenharmony_ci */
13008c2ecf20Sopenharmony_cistatic int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma)
13018c2ecf20Sopenharmony_ci{
13028c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
13038c2ecf20Sopenharmony_ci		return -EINVAL;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
13068c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	vma->vm_ops = &spufs_mss_mmap_vmops;
13098c2ecf20Sopenharmony_ci	return 0;
13108c2ecf20Sopenharmony_ci}
13118c2ecf20Sopenharmony_ci#else /* SPUFS_MMAP_4K */
13128c2ecf20Sopenharmony_ci#define spufs_mss_mmap NULL
13138c2ecf20Sopenharmony_ci#endif /* !SPUFS_MMAP_4K */
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_cistatic int spufs_mss_open(struct inode *inode, struct file *file)
13168c2ecf20Sopenharmony_ci{
13178c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
13188c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	file->private_data = i->i_ctx;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
13238c2ecf20Sopenharmony_ci	if (!i->i_openers++)
13248c2ecf20Sopenharmony_ci		ctx->mss = inode->i_mapping;
13258c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
13268c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
13278c2ecf20Sopenharmony_ci}
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_cistatic int
13308c2ecf20Sopenharmony_cispufs_mss_release(struct inode *inode, struct file *file)
13318c2ecf20Sopenharmony_ci{
13328c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
13338c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
13368c2ecf20Sopenharmony_ci	if (!--i->i_openers)
13378c2ecf20Sopenharmony_ci		ctx->mss = NULL;
13388c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
13398c2ecf20Sopenharmony_ci	return 0;
13408c2ecf20Sopenharmony_ci}
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_cistatic const struct file_operations spufs_mss_fops = {
13438c2ecf20Sopenharmony_ci	.open	 = spufs_mss_open,
13448c2ecf20Sopenharmony_ci	.release = spufs_mss_release,
13458c2ecf20Sopenharmony_ci	.mmap	 = spufs_mss_mmap,
13468c2ecf20Sopenharmony_ci	.llseek  = no_llseek,
13478c2ecf20Sopenharmony_ci};
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_cistatic vm_fault_t
13508c2ecf20Sopenharmony_cispufs_psmap_mmap_fault(struct vm_fault *vmf)
13518c2ecf20Sopenharmony_ci{
13528c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x0000, SPUFS_PS_MAP_SIZE);
13538c2ecf20Sopenharmony_ci}
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_cistatic const struct vm_operations_struct spufs_psmap_mmap_vmops = {
13568c2ecf20Sopenharmony_ci	.fault = spufs_psmap_mmap_fault,
13578c2ecf20Sopenharmony_ci};
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci/*
13608c2ecf20Sopenharmony_ci * mmap support for full problem state area [0x00000 - 0x1ffff].
13618c2ecf20Sopenharmony_ci */
13628c2ecf20Sopenharmony_cistatic int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma)
13638c2ecf20Sopenharmony_ci{
13648c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
13658c2ecf20Sopenharmony_ci		return -EINVAL;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
13688c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	vma->vm_ops = &spufs_psmap_mmap_vmops;
13718c2ecf20Sopenharmony_ci	return 0;
13728c2ecf20Sopenharmony_ci}
13738c2ecf20Sopenharmony_ci
13748c2ecf20Sopenharmony_cistatic int spufs_psmap_open(struct inode *inode, struct file *file)
13758c2ecf20Sopenharmony_ci{
13768c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
13778c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
13808c2ecf20Sopenharmony_ci	file->private_data = i->i_ctx;
13818c2ecf20Sopenharmony_ci	if (!i->i_openers++)
13828c2ecf20Sopenharmony_ci		ctx->psmap = inode->i_mapping;
13838c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
13848c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
13858c2ecf20Sopenharmony_ci}
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_cistatic int
13888c2ecf20Sopenharmony_cispufs_psmap_release(struct inode *inode, struct file *file)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
13918c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
13948c2ecf20Sopenharmony_ci	if (!--i->i_openers)
13958c2ecf20Sopenharmony_ci		ctx->psmap = NULL;
13968c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
13978c2ecf20Sopenharmony_ci	return 0;
13988c2ecf20Sopenharmony_ci}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_cistatic const struct file_operations spufs_psmap_fops = {
14018c2ecf20Sopenharmony_ci	.open	 = spufs_psmap_open,
14028c2ecf20Sopenharmony_ci	.release = spufs_psmap_release,
14038c2ecf20Sopenharmony_ci	.mmap	 = spufs_psmap_mmap,
14048c2ecf20Sopenharmony_ci	.llseek  = no_llseek,
14058c2ecf20Sopenharmony_ci};
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci#if SPUFS_MMAP_4K
14098c2ecf20Sopenharmony_cistatic vm_fault_t
14108c2ecf20Sopenharmony_cispufs_mfc_mmap_fault(struct vm_fault *vmf)
14118c2ecf20Sopenharmony_ci{
14128c2ecf20Sopenharmony_ci	return spufs_ps_fault(vmf, 0x3000, SPUFS_MFC_MAP_SIZE);
14138c2ecf20Sopenharmony_ci}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_cistatic const struct vm_operations_struct spufs_mfc_mmap_vmops = {
14168c2ecf20Sopenharmony_ci	.fault = spufs_mfc_mmap_fault,
14178c2ecf20Sopenharmony_ci};
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci/*
14208c2ecf20Sopenharmony_ci * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
14218c2ecf20Sopenharmony_ci */
14228c2ecf20Sopenharmony_cistatic int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
14238c2ecf20Sopenharmony_ci{
14248c2ecf20Sopenharmony_ci	if (!(vma->vm_flags & VM_SHARED))
14258c2ecf20Sopenharmony_ci		return -EINVAL;
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
14288c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci	vma->vm_ops = &spufs_mfc_mmap_vmops;
14318c2ecf20Sopenharmony_ci	return 0;
14328c2ecf20Sopenharmony_ci}
14338c2ecf20Sopenharmony_ci#else /* SPUFS_MMAP_4K */
14348c2ecf20Sopenharmony_ci#define spufs_mfc_mmap NULL
14358c2ecf20Sopenharmony_ci#endif /* !SPUFS_MMAP_4K */
14368c2ecf20Sopenharmony_ci
14378c2ecf20Sopenharmony_cistatic int spufs_mfc_open(struct inode *inode, struct file *file)
14388c2ecf20Sopenharmony_ci{
14398c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
14408c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
14418c2ecf20Sopenharmony_ci
14428c2ecf20Sopenharmony_ci	/* we don't want to deal with DMA into other processes */
14438c2ecf20Sopenharmony_ci	if (ctx->owner != current->mm)
14448c2ecf20Sopenharmony_ci		return -EINVAL;
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ci	if (atomic_read(&inode->i_count) != 1)
14478c2ecf20Sopenharmony_ci		return -EBUSY;
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
14508c2ecf20Sopenharmony_ci	file->private_data = ctx;
14518c2ecf20Sopenharmony_ci	if (!i->i_openers++)
14528c2ecf20Sopenharmony_ci		ctx->mfc = inode->i_mapping;
14538c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
14548c2ecf20Sopenharmony_ci	return nonseekable_open(inode, file);
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_cistatic int
14588c2ecf20Sopenharmony_cispufs_mfc_release(struct inode *inode, struct file *file)
14598c2ecf20Sopenharmony_ci{
14608c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
14618c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	mutex_lock(&ctx->mapping_lock);
14648c2ecf20Sopenharmony_ci	if (!--i->i_openers)
14658c2ecf20Sopenharmony_ci		ctx->mfc = NULL;
14668c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->mapping_lock);
14678c2ecf20Sopenharmony_ci	return 0;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci/* interrupt-level mfc callback function. */
14718c2ecf20Sopenharmony_civoid spufs_mfc_callback(struct spu *spu)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	struct spu_context *ctx = spu->ctx;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	if (ctx)
14768c2ecf20Sopenharmony_ci		wake_up_all(&ctx->mfc_wq);
14778c2ecf20Sopenharmony_ci}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_cistatic int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status)
14808c2ecf20Sopenharmony_ci{
14818c2ecf20Sopenharmony_ci	/* See if there is one tag group is complete */
14828c2ecf20Sopenharmony_ci	/* FIXME we need locking around tagwait */
14838c2ecf20Sopenharmony_ci	*status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait;
14848c2ecf20Sopenharmony_ci	ctx->tagwait &= ~*status;
14858c2ecf20Sopenharmony_ci	if (*status)
14868c2ecf20Sopenharmony_ci		return 1;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	/* enable interrupt waiting for any tag group,
14898c2ecf20Sopenharmony_ci	   may silently fail if interrupts are already enabled */
14908c2ecf20Sopenharmony_ci	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
14918c2ecf20Sopenharmony_ci	return 0;
14928c2ecf20Sopenharmony_ci}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_cistatic ssize_t spufs_mfc_read(struct file *file, char __user *buffer,
14958c2ecf20Sopenharmony_ci			size_t size, loff_t *pos)
14968c2ecf20Sopenharmony_ci{
14978c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
14988c2ecf20Sopenharmony_ci	int ret = -EINVAL;
14998c2ecf20Sopenharmony_ci	u32 status;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	if (size != 4)
15028c2ecf20Sopenharmony_ci		goto out;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
15058c2ecf20Sopenharmony_ci	if (ret)
15068c2ecf20Sopenharmony_ci		return ret;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	ret = -EINVAL;
15098c2ecf20Sopenharmony_ci	if (file->f_flags & O_NONBLOCK) {
15108c2ecf20Sopenharmony_ci		status = ctx->ops->read_mfc_tagstatus(ctx);
15118c2ecf20Sopenharmony_ci		if (!(status & ctx->tagwait))
15128c2ecf20Sopenharmony_ci			ret = -EAGAIN;
15138c2ecf20Sopenharmony_ci		else
15148c2ecf20Sopenharmony_ci			/* XXX(hch): shouldn't we clear ret here? */
15158c2ecf20Sopenharmony_ci			ctx->tagwait &= ~status;
15168c2ecf20Sopenharmony_ci	} else {
15178c2ecf20Sopenharmony_ci		ret = spufs_wait(ctx->mfc_wq,
15188c2ecf20Sopenharmony_ci			   spufs_read_mfc_tagstatus(ctx, &status));
15198c2ecf20Sopenharmony_ci		if (ret)
15208c2ecf20Sopenharmony_ci			goto out;
15218c2ecf20Sopenharmony_ci	}
15228c2ecf20Sopenharmony_ci	spu_release(ctx);
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	ret = 4;
15258c2ecf20Sopenharmony_ci	if (copy_to_user(buffer, &status, 4))
15268c2ecf20Sopenharmony_ci		ret = -EFAULT;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ciout:
15298c2ecf20Sopenharmony_ci	return ret;
15308c2ecf20Sopenharmony_ci}
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_cistatic int spufs_check_valid_dma(struct mfc_dma_command *cmd)
15338c2ecf20Sopenharmony_ci{
15348c2ecf20Sopenharmony_ci	pr_debug("queueing DMA %x %llx %x %x %x\n", cmd->lsa,
15358c2ecf20Sopenharmony_ci		 cmd->ea, cmd->size, cmd->tag, cmd->cmd);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	switch (cmd->cmd) {
15388c2ecf20Sopenharmony_ci	case MFC_PUT_CMD:
15398c2ecf20Sopenharmony_ci	case MFC_PUTF_CMD:
15408c2ecf20Sopenharmony_ci	case MFC_PUTB_CMD:
15418c2ecf20Sopenharmony_ci	case MFC_GET_CMD:
15428c2ecf20Sopenharmony_ci	case MFC_GETF_CMD:
15438c2ecf20Sopenharmony_ci	case MFC_GETB_CMD:
15448c2ecf20Sopenharmony_ci		break;
15458c2ecf20Sopenharmony_ci	default:
15468c2ecf20Sopenharmony_ci		pr_debug("invalid DMA opcode %x\n", cmd->cmd);
15478c2ecf20Sopenharmony_ci		return -EIO;
15488c2ecf20Sopenharmony_ci	}
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) {
15518c2ecf20Sopenharmony_ci		pr_debug("invalid DMA alignment, ea %llx lsa %x\n",
15528c2ecf20Sopenharmony_ci				cmd->ea, cmd->lsa);
15538c2ecf20Sopenharmony_ci		return -EIO;
15548c2ecf20Sopenharmony_ci	}
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	switch (cmd->size & 0xf) {
15578c2ecf20Sopenharmony_ci	case 1:
15588c2ecf20Sopenharmony_ci		break;
15598c2ecf20Sopenharmony_ci	case 2:
15608c2ecf20Sopenharmony_ci		if (cmd->lsa & 1)
15618c2ecf20Sopenharmony_ci			goto error;
15628c2ecf20Sopenharmony_ci		break;
15638c2ecf20Sopenharmony_ci	case 4:
15648c2ecf20Sopenharmony_ci		if (cmd->lsa & 3)
15658c2ecf20Sopenharmony_ci			goto error;
15668c2ecf20Sopenharmony_ci		break;
15678c2ecf20Sopenharmony_ci	case 8:
15688c2ecf20Sopenharmony_ci		if (cmd->lsa & 7)
15698c2ecf20Sopenharmony_ci			goto error;
15708c2ecf20Sopenharmony_ci		break;
15718c2ecf20Sopenharmony_ci	case 0:
15728c2ecf20Sopenharmony_ci		if (cmd->lsa & 15)
15738c2ecf20Sopenharmony_ci			goto error;
15748c2ecf20Sopenharmony_ci		break;
15758c2ecf20Sopenharmony_ci	error:
15768c2ecf20Sopenharmony_ci	default:
15778c2ecf20Sopenharmony_ci		pr_debug("invalid DMA alignment %x for size %x\n",
15788c2ecf20Sopenharmony_ci			cmd->lsa & 0xf, cmd->size);
15798c2ecf20Sopenharmony_ci		return -EIO;
15808c2ecf20Sopenharmony_ci	}
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci	if (cmd->size > 16 * 1024) {
15838c2ecf20Sopenharmony_ci		pr_debug("invalid DMA size %x\n", cmd->size);
15848c2ecf20Sopenharmony_ci		return -EIO;
15858c2ecf20Sopenharmony_ci	}
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	if (cmd->tag & 0xfff0) {
15888c2ecf20Sopenharmony_ci		/* we reserve the higher tag numbers for kernel use */
15898c2ecf20Sopenharmony_ci		pr_debug("invalid DMA tag\n");
15908c2ecf20Sopenharmony_ci		return -EIO;
15918c2ecf20Sopenharmony_ci	}
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_ci	if (cmd->class) {
15948c2ecf20Sopenharmony_ci		/* not supported in this version */
15958c2ecf20Sopenharmony_ci		pr_debug("invalid DMA class\n");
15968c2ecf20Sopenharmony_ci		return -EIO;
15978c2ecf20Sopenharmony_ci	}
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	return 0;
16008c2ecf20Sopenharmony_ci}
16018c2ecf20Sopenharmony_ci
16028c2ecf20Sopenharmony_cistatic int spu_send_mfc_command(struct spu_context *ctx,
16038c2ecf20Sopenharmony_ci				struct mfc_dma_command cmd,
16048c2ecf20Sopenharmony_ci				int *error)
16058c2ecf20Sopenharmony_ci{
16068c2ecf20Sopenharmony_ci	*error = ctx->ops->send_mfc_command(ctx, &cmd);
16078c2ecf20Sopenharmony_ci	if (*error == -EAGAIN) {
16088c2ecf20Sopenharmony_ci		/* wait for any tag group to complete
16098c2ecf20Sopenharmony_ci		   so we have space for the new command */
16108c2ecf20Sopenharmony_ci		ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1);
16118c2ecf20Sopenharmony_ci		/* try again, because the queue might be
16128c2ecf20Sopenharmony_ci		   empty again */
16138c2ecf20Sopenharmony_ci		*error = ctx->ops->send_mfc_command(ctx, &cmd);
16148c2ecf20Sopenharmony_ci		if (*error == -EAGAIN)
16158c2ecf20Sopenharmony_ci			return 0;
16168c2ecf20Sopenharmony_ci	}
16178c2ecf20Sopenharmony_ci	return 1;
16188c2ecf20Sopenharmony_ci}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_cistatic ssize_t spufs_mfc_write(struct file *file, const char __user *buffer,
16218c2ecf20Sopenharmony_ci			size_t size, loff_t *pos)
16228c2ecf20Sopenharmony_ci{
16238c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
16248c2ecf20Sopenharmony_ci	struct mfc_dma_command cmd;
16258c2ecf20Sopenharmony_ci	int ret = -EINVAL;
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci	if (size != sizeof cmd)
16288c2ecf20Sopenharmony_ci		goto out;
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci	ret = -EFAULT;
16318c2ecf20Sopenharmony_ci	if (copy_from_user(&cmd, buffer, sizeof cmd))
16328c2ecf20Sopenharmony_ci		goto out;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	ret = spufs_check_valid_dma(&cmd);
16358c2ecf20Sopenharmony_ci	if (ret)
16368c2ecf20Sopenharmony_ci		goto out;
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
16398c2ecf20Sopenharmony_ci	if (ret)
16408c2ecf20Sopenharmony_ci		goto out;
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE);
16438c2ecf20Sopenharmony_ci	if (ret)
16448c2ecf20Sopenharmony_ci		goto out;
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci	if (file->f_flags & O_NONBLOCK) {
16478c2ecf20Sopenharmony_ci		ret = ctx->ops->send_mfc_command(ctx, &cmd);
16488c2ecf20Sopenharmony_ci	} else {
16498c2ecf20Sopenharmony_ci		int status;
16508c2ecf20Sopenharmony_ci		ret = spufs_wait(ctx->mfc_wq,
16518c2ecf20Sopenharmony_ci				 spu_send_mfc_command(ctx, cmd, &status));
16528c2ecf20Sopenharmony_ci		if (ret)
16538c2ecf20Sopenharmony_ci			goto out;
16548c2ecf20Sopenharmony_ci		if (status)
16558c2ecf20Sopenharmony_ci			ret = status;
16568c2ecf20Sopenharmony_ci	}
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci	if (ret)
16598c2ecf20Sopenharmony_ci		goto out_unlock;
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	ctx->tagwait |= 1 << cmd.tag;
16628c2ecf20Sopenharmony_ci	ret = size;
16638c2ecf20Sopenharmony_ci
16648c2ecf20Sopenharmony_ciout_unlock:
16658c2ecf20Sopenharmony_ci	spu_release(ctx);
16668c2ecf20Sopenharmony_ciout:
16678c2ecf20Sopenharmony_ci	return ret;
16688c2ecf20Sopenharmony_ci}
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_cistatic __poll_t spufs_mfc_poll(struct file *file,poll_table *wait)
16718c2ecf20Sopenharmony_ci{
16728c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
16738c2ecf20Sopenharmony_ci	u32 free_elements, tagstatus;
16748c2ecf20Sopenharmony_ci	__poll_t mask;
16758c2ecf20Sopenharmony_ci
16768c2ecf20Sopenharmony_ci	poll_wait(file, &ctx->mfc_wq, wait);
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	/*
16798c2ecf20Sopenharmony_ci	 * For now keep this uninterruptible and also ignore the rule
16808c2ecf20Sopenharmony_ci	 * that poll should not sleep.  Will be fixed later.
16818c2ecf20Sopenharmony_ci	 */
16828c2ecf20Sopenharmony_ci	mutex_lock(&ctx->state_mutex);
16838c2ecf20Sopenharmony_ci	ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2);
16848c2ecf20Sopenharmony_ci	free_elements = ctx->ops->get_mfc_free_elements(ctx);
16858c2ecf20Sopenharmony_ci	tagstatus = ctx->ops->read_mfc_tagstatus(ctx);
16868c2ecf20Sopenharmony_ci	spu_release(ctx);
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	mask = 0;
16898c2ecf20Sopenharmony_ci	if (free_elements & 0xffff)
16908c2ecf20Sopenharmony_ci		mask |= EPOLLOUT | EPOLLWRNORM;
16918c2ecf20Sopenharmony_ci	if (tagstatus & ctx->tagwait)
16928c2ecf20Sopenharmony_ci		mask |= EPOLLIN | EPOLLRDNORM;
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__,
16958c2ecf20Sopenharmony_ci		free_elements, tagstatus, ctx->tagwait);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	return mask;
16988c2ecf20Sopenharmony_ci}
16998c2ecf20Sopenharmony_ci
17008c2ecf20Sopenharmony_cistatic int spufs_mfc_flush(struct file *file, fl_owner_t id)
17018c2ecf20Sopenharmony_ci{
17028c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
17038c2ecf20Sopenharmony_ci	int ret;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
17068c2ecf20Sopenharmony_ci	if (ret)
17078c2ecf20Sopenharmony_ci		goto out;
17088c2ecf20Sopenharmony_ci#if 0
17098c2ecf20Sopenharmony_ci/* this currently hangs */
17108c2ecf20Sopenharmony_ci	ret = spufs_wait(ctx->mfc_wq,
17118c2ecf20Sopenharmony_ci			 ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2));
17128c2ecf20Sopenharmony_ci	if (ret)
17138c2ecf20Sopenharmony_ci		goto out;
17148c2ecf20Sopenharmony_ci	ret = spufs_wait(ctx->mfc_wq,
17158c2ecf20Sopenharmony_ci			 ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait);
17168c2ecf20Sopenharmony_ci	if (ret)
17178c2ecf20Sopenharmony_ci		goto out;
17188c2ecf20Sopenharmony_ci#else
17198c2ecf20Sopenharmony_ci	ret = 0;
17208c2ecf20Sopenharmony_ci#endif
17218c2ecf20Sopenharmony_ci	spu_release(ctx);
17228c2ecf20Sopenharmony_ciout:
17238c2ecf20Sopenharmony_ci	return ret;
17248c2ecf20Sopenharmony_ci}
17258c2ecf20Sopenharmony_ci
17268c2ecf20Sopenharmony_cistatic int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
17298c2ecf20Sopenharmony_ci	int err = file_write_and_wait_range(file, start, end);
17308c2ecf20Sopenharmony_ci	if (!err) {
17318c2ecf20Sopenharmony_ci		inode_lock(inode);
17328c2ecf20Sopenharmony_ci		err = spufs_mfc_flush(file, NULL);
17338c2ecf20Sopenharmony_ci		inode_unlock(inode);
17348c2ecf20Sopenharmony_ci	}
17358c2ecf20Sopenharmony_ci	return err;
17368c2ecf20Sopenharmony_ci}
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_cistatic const struct file_operations spufs_mfc_fops = {
17398c2ecf20Sopenharmony_ci	.open	 = spufs_mfc_open,
17408c2ecf20Sopenharmony_ci	.release = spufs_mfc_release,
17418c2ecf20Sopenharmony_ci	.read	 = spufs_mfc_read,
17428c2ecf20Sopenharmony_ci	.write	 = spufs_mfc_write,
17438c2ecf20Sopenharmony_ci	.poll	 = spufs_mfc_poll,
17448c2ecf20Sopenharmony_ci	.flush	 = spufs_mfc_flush,
17458c2ecf20Sopenharmony_ci	.fsync	 = spufs_mfc_fsync,
17468c2ecf20Sopenharmony_ci	.mmap	 = spufs_mfc_mmap,
17478c2ecf20Sopenharmony_ci	.llseek  = no_llseek,
17488c2ecf20Sopenharmony_ci};
17498c2ecf20Sopenharmony_ci
17508c2ecf20Sopenharmony_cistatic int spufs_npc_set(void *data, u64 val)
17518c2ecf20Sopenharmony_ci{
17528c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
17538c2ecf20Sopenharmony_ci	int ret;
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
17568c2ecf20Sopenharmony_ci	if (ret)
17578c2ecf20Sopenharmony_ci		return ret;
17588c2ecf20Sopenharmony_ci	ctx->ops->npc_write(ctx, val);
17598c2ecf20Sopenharmony_ci	spu_release(ctx);
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	return 0;
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_ci
17648c2ecf20Sopenharmony_cistatic u64 spufs_npc_get(struct spu_context *ctx)
17658c2ecf20Sopenharmony_ci{
17668c2ecf20Sopenharmony_ci	return ctx->ops->npc_read(ctx);
17678c2ecf20Sopenharmony_ci}
17688c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set,
17698c2ecf20Sopenharmony_ci		       "0x%llx\n", SPU_ATTR_ACQUIRE);
17708c2ecf20Sopenharmony_ci
17718c2ecf20Sopenharmony_cistatic int spufs_decr_set(void *data, u64 val)
17728c2ecf20Sopenharmony_ci{
17738c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
17748c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
17758c2ecf20Sopenharmony_ci	int ret;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
17788c2ecf20Sopenharmony_ci	if (ret)
17798c2ecf20Sopenharmony_ci		return ret;
17808c2ecf20Sopenharmony_ci	lscsa->decr.slot[0] = (u32) val;
17818c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	return 0;
17848c2ecf20Sopenharmony_ci}
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_cistatic u64 spufs_decr_get(struct spu_context *ctx)
17878c2ecf20Sopenharmony_ci{
17888c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
17898c2ecf20Sopenharmony_ci	return lscsa->decr.slot[0];
17908c2ecf20Sopenharmony_ci}
17918c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set,
17928c2ecf20Sopenharmony_ci		       "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED);
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_cistatic int spufs_decr_status_set(void *data, u64 val)
17958c2ecf20Sopenharmony_ci{
17968c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
17978c2ecf20Sopenharmony_ci	int ret;
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
18008c2ecf20Sopenharmony_ci	if (ret)
18018c2ecf20Sopenharmony_ci		return ret;
18028c2ecf20Sopenharmony_ci	if (val)
18038c2ecf20Sopenharmony_ci		ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING;
18048c2ecf20Sopenharmony_ci	else
18058c2ecf20Sopenharmony_ci		ctx->csa.priv2.mfc_control_RW &= ~MFC_CNTL_DECREMENTER_RUNNING;
18068c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	return 0;
18098c2ecf20Sopenharmony_ci}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_cistatic u64 spufs_decr_status_get(struct spu_context *ctx)
18128c2ecf20Sopenharmony_ci{
18138c2ecf20Sopenharmony_ci	if (ctx->csa.priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING)
18148c2ecf20Sopenharmony_ci		return SPU_DECR_STATUS_RUNNING;
18158c2ecf20Sopenharmony_ci	else
18168c2ecf20Sopenharmony_ci		return 0;
18178c2ecf20Sopenharmony_ci}
18188c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get,
18198c2ecf20Sopenharmony_ci		       spufs_decr_status_set, "0x%llx\n",
18208c2ecf20Sopenharmony_ci		       SPU_ATTR_ACQUIRE_SAVED);
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_cistatic int spufs_event_mask_set(void *data, u64 val)
18238c2ecf20Sopenharmony_ci{
18248c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
18258c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
18268c2ecf20Sopenharmony_ci	int ret;
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
18298c2ecf20Sopenharmony_ci	if (ret)
18308c2ecf20Sopenharmony_ci		return ret;
18318c2ecf20Sopenharmony_ci	lscsa->event_mask.slot[0] = (u32) val;
18328c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
18338c2ecf20Sopenharmony_ci
18348c2ecf20Sopenharmony_ci	return 0;
18358c2ecf20Sopenharmony_ci}
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_cistatic u64 spufs_event_mask_get(struct spu_context *ctx)
18388c2ecf20Sopenharmony_ci{
18398c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
18408c2ecf20Sopenharmony_ci	return lscsa->event_mask.slot[0];
18418c2ecf20Sopenharmony_ci}
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get,
18448c2ecf20Sopenharmony_ci		       spufs_event_mask_set, "0x%llx\n",
18458c2ecf20Sopenharmony_ci		       SPU_ATTR_ACQUIRE_SAVED);
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_cistatic u64 spufs_event_status_get(struct spu_context *ctx)
18488c2ecf20Sopenharmony_ci{
18498c2ecf20Sopenharmony_ci	struct spu_state *state = &ctx->csa;
18508c2ecf20Sopenharmony_ci	u64 stat;
18518c2ecf20Sopenharmony_ci	stat = state->spu_chnlcnt_RW[0];
18528c2ecf20Sopenharmony_ci	if (stat)
18538c2ecf20Sopenharmony_ci		return state->spu_chnldata_RW[0];
18548c2ecf20Sopenharmony_ci	return 0;
18558c2ecf20Sopenharmony_ci}
18568c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get,
18578c2ecf20Sopenharmony_ci		       NULL, "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED)
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_cistatic int spufs_srr0_set(void *data, u64 val)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
18628c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
18638c2ecf20Sopenharmony_ci	int ret;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
18668c2ecf20Sopenharmony_ci	if (ret)
18678c2ecf20Sopenharmony_ci		return ret;
18688c2ecf20Sopenharmony_ci	lscsa->srr0.slot[0] = (u32) val;
18698c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	return 0;
18728c2ecf20Sopenharmony_ci}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_cistatic u64 spufs_srr0_get(struct spu_context *ctx)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	struct spu_lscsa *lscsa = ctx->csa.lscsa;
18778c2ecf20Sopenharmony_ci	return lscsa->srr0.slot[0];
18788c2ecf20Sopenharmony_ci}
18798c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
18808c2ecf20Sopenharmony_ci		       "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED)
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_cistatic u64 spufs_id_get(struct spu_context *ctx)
18838c2ecf20Sopenharmony_ci{
18848c2ecf20Sopenharmony_ci	u64 num;
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	if (ctx->state == SPU_STATE_RUNNABLE)
18878c2ecf20Sopenharmony_ci		num = ctx->spu->number;
18888c2ecf20Sopenharmony_ci	else
18898c2ecf20Sopenharmony_ci		num = (unsigned int)-1;
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	return num;
18928c2ecf20Sopenharmony_ci}
18938c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n",
18948c2ecf20Sopenharmony_ci		       SPU_ATTR_ACQUIRE)
18958c2ecf20Sopenharmony_ci
18968c2ecf20Sopenharmony_cistatic u64 spufs_object_id_get(struct spu_context *ctx)
18978c2ecf20Sopenharmony_ci{
18988c2ecf20Sopenharmony_ci	/* FIXME: Should there really be no locking here? */
18998c2ecf20Sopenharmony_ci	return ctx->object_id;
19008c2ecf20Sopenharmony_ci}
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_cistatic int spufs_object_id_set(void *data, u64 id)
19038c2ecf20Sopenharmony_ci{
19048c2ecf20Sopenharmony_ci	struct spu_context *ctx = data;
19058c2ecf20Sopenharmony_ci	ctx->object_id = id;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	return 0;
19088c2ecf20Sopenharmony_ci}
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get,
19118c2ecf20Sopenharmony_ci		       spufs_object_id_set, "0x%llx\n", SPU_ATTR_NOACQUIRE);
19128c2ecf20Sopenharmony_ci
19138c2ecf20Sopenharmony_cistatic u64 spufs_lslr_get(struct spu_context *ctx)
19148c2ecf20Sopenharmony_ci{
19158c2ecf20Sopenharmony_ci	return ctx->csa.priv2.spu_lslr_RW;
19168c2ecf20Sopenharmony_ci}
19178c2ecf20Sopenharmony_ciDEFINE_SPUFS_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n",
19188c2ecf20Sopenharmony_ci		       SPU_ATTR_ACQUIRE_SAVED);
19198c2ecf20Sopenharmony_ci
19208c2ecf20Sopenharmony_cistatic int spufs_info_open(struct inode *inode, struct file *file)
19218c2ecf20Sopenharmony_ci{
19228c2ecf20Sopenharmony_ci	struct spufs_inode_info *i = SPUFS_I(inode);
19238c2ecf20Sopenharmony_ci	struct spu_context *ctx = i->i_ctx;
19248c2ecf20Sopenharmony_ci	file->private_data = ctx;
19258c2ecf20Sopenharmony_ci	return 0;
19268c2ecf20Sopenharmony_ci}
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_cistatic int spufs_caps_show(struct seq_file *s, void *private)
19298c2ecf20Sopenharmony_ci{
19308c2ecf20Sopenharmony_ci	struct spu_context *ctx = s->private;
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	if (!(ctx->flags & SPU_CREATE_NOSCHED))
19338c2ecf20Sopenharmony_ci		seq_puts(s, "sched\n");
19348c2ecf20Sopenharmony_ci	if (!(ctx->flags & SPU_CREATE_ISOLATE))
19358c2ecf20Sopenharmony_ci		seq_puts(s, "step\n");
19368c2ecf20Sopenharmony_ci	return 0;
19378c2ecf20Sopenharmony_ci}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic int spufs_caps_open(struct inode *inode, struct file *file)
19408c2ecf20Sopenharmony_ci{
19418c2ecf20Sopenharmony_ci	return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx);
19428c2ecf20Sopenharmony_ci}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_cistatic const struct file_operations spufs_caps_fops = {
19458c2ecf20Sopenharmony_ci	.open		= spufs_caps_open,
19468c2ecf20Sopenharmony_ci	.read		= seq_read,
19478c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
19488c2ecf20Sopenharmony_ci	.release	= single_release,
19498c2ecf20Sopenharmony_ci};
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_cistatic ssize_t spufs_mbox_info_dump(struct spu_context *ctx,
19528c2ecf20Sopenharmony_ci		struct coredump_params *cprm)
19538c2ecf20Sopenharmony_ci{
19548c2ecf20Sopenharmony_ci	if (!(ctx->csa.prob.mb_stat_R & 0x0000ff))
19558c2ecf20Sopenharmony_ci		return 0;
19568c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &ctx->csa.prob.pu_mb_R,
19578c2ecf20Sopenharmony_ci			       sizeof(ctx->csa.prob.pu_mb_R));
19588c2ecf20Sopenharmony_ci}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_cistatic ssize_t spufs_mbox_info_read(struct file *file, char __user *buf,
19618c2ecf20Sopenharmony_ci				   size_t len, loff_t *pos)
19628c2ecf20Sopenharmony_ci{
19638c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
19648c2ecf20Sopenharmony_ci	u32 stat, data;
19658c2ecf20Sopenharmony_ci	int ret;
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
19688c2ecf20Sopenharmony_ci	if (ret)
19698c2ecf20Sopenharmony_ci		return ret;
19708c2ecf20Sopenharmony_ci	spin_lock(&ctx->csa.register_lock);
19718c2ecf20Sopenharmony_ci	stat = ctx->csa.prob.mb_stat_R;
19728c2ecf20Sopenharmony_ci	data = ctx->csa.prob.pu_mb_R;
19738c2ecf20Sopenharmony_ci	spin_unlock(&ctx->csa.register_lock);
19748c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	/* EOF if there's no entry in the mbox */
19778c2ecf20Sopenharmony_ci	if (!(stat & 0x0000ff))
19788c2ecf20Sopenharmony_ci		return 0;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, pos, &data, sizeof(data));
19818c2ecf20Sopenharmony_ci}
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_cistatic const struct file_operations spufs_mbox_info_fops = {
19848c2ecf20Sopenharmony_ci	.open = spufs_info_open,
19858c2ecf20Sopenharmony_ci	.read = spufs_mbox_info_read,
19868c2ecf20Sopenharmony_ci	.llseek  = generic_file_llseek,
19878c2ecf20Sopenharmony_ci};
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_cistatic ssize_t spufs_ibox_info_dump(struct spu_context *ctx,
19908c2ecf20Sopenharmony_ci		struct coredump_params *cprm)
19918c2ecf20Sopenharmony_ci{
19928c2ecf20Sopenharmony_ci	if (!(ctx->csa.prob.mb_stat_R & 0xff0000))
19938c2ecf20Sopenharmony_ci		return 0;
19948c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &ctx->csa.priv2.puint_mb_R,
19958c2ecf20Sopenharmony_ci			       sizeof(ctx->csa.priv2.puint_mb_R));
19968c2ecf20Sopenharmony_ci}
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_cistatic ssize_t spufs_ibox_info_read(struct file *file, char __user *buf,
19998c2ecf20Sopenharmony_ci				   size_t len, loff_t *pos)
20008c2ecf20Sopenharmony_ci{
20018c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
20028c2ecf20Sopenharmony_ci	u32 stat, data;
20038c2ecf20Sopenharmony_ci	int ret;
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
20068c2ecf20Sopenharmony_ci	if (ret)
20078c2ecf20Sopenharmony_ci		return ret;
20088c2ecf20Sopenharmony_ci	spin_lock(&ctx->csa.register_lock);
20098c2ecf20Sopenharmony_ci	stat = ctx->csa.prob.mb_stat_R;
20108c2ecf20Sopenharmony_ci	data = ctx->csa.priv2.puint_mb_R;
20118c2ecf20Sopenharmony_ci	spin_unlock(&ctx->csa.register_lock);
20128c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_ci	/* EOF if there's no entry in the ibox */
20158c2ecf20Sopenharmony_ci	if (!(stat & 0xff0000))
20168c2ecf20Sopenharmony_ci		return 0;
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, pos, &data, sizeof(data));
20198c2ecf20Sopenharmony_ci}
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_cistatic const struct file_operations spufs_ibox_info_fops = {
20228c2ecf20Sopenharmony_ci	.open = spufs_info_open,
20238c2ecf20Sopenharmony_ci	.read = spufs_ibox_info_read,
20248c2ecf20Sopenharmony_ci	.llseek  = generic_file_llseek,
20258c2ecf20Sopenharmony_ci};
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_cistatic size_t spufs_wbox_info_cnt(struct spu_context *ctx)
20288c2ecf20Sopenharmony_ci{
20298c2ecf20Sopenharmony_ci	return (4 - ((ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8)) * sizeof(u32);
20308c2ecf20Sopenharmony_ci}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_cistatic ssize_t spufs_wbox_info_dump(struct spu_context *ctx,
20338c2ecf20Sopenharmony_ci		struct coredump_params *cprm)
20348c2ecf20Sopenharmony_ci{
20358c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &ctx->csa.spu_mailbox_data,
20368c2ecf20Sopenharmony_ci			spufs_wbox_info_cnt(ctx));
20378c2ecf20Sopenharmony_ci}
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_cistatic ssize_t spufs_wbox_info_read(struct file *file, char __user *buf,
20408c2ecf20Sopenharmony_ci				   size_t len, loff_t *pos)
20418c2ecf20Sopenharmony_ci{
20428c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
20438c2ecf20Sopenharmony_ci	u32 data[ARRAY_SIZE(ctx->csa.spu_mailbox_data)];
20448c2ecf20Sopenharmony_ci	int ret, count;
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
20478c2ecf20Sopenharmony_ci	if (ret)
20488c2ecf20Sopenharmony_ci		return ret;
20498c2ecf20Sopenharmony_ci	spin_lock(&ctx->csa.register_lock);
20508c2ecf20Sopenharmony_ci	count = spufs_wbox_info_cnt(ctx);
20518c2ecf20Sopenharmony_ci	memcpy(&data, &ctx->csa.spu_mailbox_data, sizeof(data));
20528c2ecf20Sopenharmony_ci	spin_unlock(&ctx->csa.register_lock);
20538c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, pos, &data,
20568c2ecf20Sopenharmony_ci				count * sizeof(u32));
20578c2ecf20Sopenharmony_ci}
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_cistatic const struct file_operations spufs_wbox_info_fops = {
20608c2ecf20Sopenharmony_ci	.open = spufs_info_open,
20618c2ecf20Sopenharmony_ci	.read = spufs_wbox_info_read,
20628c2ecf20Sopenharmony_ci	.llseek  = generic_file_llseek,
20638c2ecf20Sopenharmony_ci};
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_cistatic void spufs_get_dma_info(struct spu_context *ctx,
20668c2ecf20Sopenharmony_ci		struct spu_dma_info *info)
20678c2ecf20Sopenharmony_ci{
20688c2ecf20Sopenharmony_ci	int i;
20698c2ecf20Sopenharmony_ci
20708c2ecf20Sopenharmony_ci	info->dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW;
20718c2ecf20Sopenharmony_ci	info->dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0];
20728c2ecf20Sopenharmony_ci	info->dma_info_status = ctx->csa.spu_chnldata_RW[24];
20738c2ecf20Sopenharmony_ci	info->dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25];
20748c2ecf20Sopenharmony_ci	info->dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27];
20758c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
20768c2ecf20Sopenharmony_ci		struct mfc_cq_sr *qp = &info->dma_info_command_data[i];
20778c2ecf20Sopenharmony_ci		struct mfc_cq_sr *spuqp = &ctx->csa.priv2.spuq[i];
20788c2ecf20Sopenharmony_ci
20798c2ecf20Sopenharmony_ci		qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW;
20808c2ecf20Sopenharmony_ci		qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW;
20818c2ecf20Sopenharmony_ci		qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW;
20828c2ecf20Sopenharmony_ci		qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW;
20838c2ecf20Sopenharmony_ci	}
20848c2ecf20Sopenharmony_ci}
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_cistatic ssize_t spufs_dma_info_dump(struct spu_context *ctx,
20878c2ecf20Sopenharmony_ci		struct coredump_params *cprm)
20888c2ecf20Sopenharmony_ci{
20898c2ecf20Sopenharmony_ci	struct spu_dma_info info;
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	spufs_get_dma_info(ctx, &info);
20928c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &info, sizeof(info));
20938c2ecf20Sopenharmony_ci}
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_cistatic ssize_t spufs_dma_info_read(struct file *file, char __user *buf,
20968c2ecf20Sopenharmony_ci			      size_t len, loff_t *pos)
20978c2ecf20Sopenharmony_ci{
20988c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
20998c2ecf20Sopenharmony_ci	struct spu_dma_info info;
21008c2ecf20Sopenharmony_ci	int ret;
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
21038c2ecf20Sopenharmony_ci	if (ret)
21048c2ecf20Sopenharmony_ci		return ret;
21058c2ecf20Sopenharmony_ci	spin_lock(&ctx->csa.register_lock);
21068c2ecf20Sopenharmony_ci	spufs_get_dma_info(ctx, &info);
21078c2ecf20Sopenharmony_ci	spin_unlock(&ctx->csa.register_lock);
21088c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, pos, &info,
21118c2ecf20Sopenharmony_ci				sizeof(info));
21128c2ecf20Sopenharmony_ci}
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_cistatic const struct file_operations spufs_dma_info_fops = {
21158c2ecf20Sopenharmony_ci	.open = spufs_info_open,
21168c2ecf20Sopenharmony_ci	.read = spufs_dma_info_read,
21178c2ecf20Sopenharmony_ci	.llseek = no_llseek,
21188c2ecf20Sopenharmony_ci};
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_cistatic void spufs_get_proxydma_info(struct spu_context *ctx,
21218c2ecf20Sopenharmony_ci		struct spu_proxydma_info *info)
21228c2ecf20Sopenharmony_ci{
21238c2ecf20Sopenharmony_ci	int i;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	info->proxydma_info_type = ctx->csa.prob.dma_querytype_RW;
21268c2ecf20Sopenharmony_ci	info->proxydma_info_mask = ctx->csa.prob.dma_querymask_RW;
21278c2ecf20Sopenharmony_ci	info->proxydma_info_status = ctx->csa.prob.dma_tagstatus_R;
21288c2ecf20Sopenharmony_ci
21298c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
21308c2ecf20Sopenharmony_ci		struct mfc_cq_sr *qp = &info->proxydma_info_command_data[i];
21318c2ecf20Sopenharmony_ci		struct mfc_cq_sr *puqp = &ctx->csa.priv2.puq[i];
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci		qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW;
21348c2ecf20Sopenharmony_ci		qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW;
21358c2ecf20Sopenharmony_ci		qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW;
21368c2ecf20Sopenharmony_ci		qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW;
21378c2ecf20Sopenharmony_ci	}
21388c2ecf20Sopenharmony_ci}
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_cistatic ssize_t spufs_proxydma_info_dump(struct spu_context *ctx,
21418c2ecf20Sopenharmony_ci		struct coredump_params *cprm)
21428c2ecf20Sopenharmony_ci{
21438c2ecf20Sopenharmony_ci	struct spu_proxydma_info info;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	spufs_get_proxydma_info(ctx, &info);
21468c2ecf20Sopenharmony_ci	return spufs_dump_emit(cprm, &info, sizeof(info));
21478c2ecf20Sopenharmony_ci}
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_cistatic ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf,
21508c2ecf20Sopenharmony_ci				   size_t len, loff_t *pos)
21518c2ecf20Sopenharmony_ci{
21528c2ecf20Sopenharmony_ci	struct spu_context *ctx = file->private_data;
21538c2ecf20Sopenharmony_ci	struct spu_proxydma_info info;
21548c2ecf20Sopenharmony_ci	int ret;
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	if (len < sizeof(info))
21578c2ecf20Sopenharmony_ci		return -EINVAL;
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci	ret = spu_acquire_saved(ctx);
21608c2ecf20Sopenharmony_ci	if (ret)
21618c2ecf20Sopenharmony_ci		return ret;
21628c2ecf20Sopenharmony_ci	spin_lock(&ctx->csa.register_lock);
21638c2ecf20Sopenharmony_ci	spufs_get_proxydma_info(ctx, &info);
21648c2ecf20Sopenharmony_ci	spin_unlock(&ctx->csa.register_lock);
21658c2ecf20Sopenharmony_ci	spu_release_saved(ctx);
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	return simple_read_from_buffer(buf, len, pos, &info,
21688c2ecf20Sopenharmony_ci				sizeof(info));
21698c2ecf20Sopenharmony_ci}
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_cistatic const struct file_operations spufs_proxydma_info_fops = {
21728c2ecf20Sopenharmony_ci	.open = spufs_info_open,
21738c2ecf20Sopenharmony_ci	.read = spufs_proxydma_info_read,
21748c2ecf20Sopenharmony_ci	.llseek = no_llseek,
21758c2ecf20Sopenharmony_ci};
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_cistatic int spufs_show_tid(struct seq_file *s, void *private)
21788c2ecf20Sopenharmony_ci{
21798c2ecf20Sopenharmony_ci	struct spu_context *ctx = s->private;
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	seq_printf(s, "%d\n", ctx->tid);
21828c2ecf20Sopenharmony_ci	return 0;
21838c2ecf20Sopenharmony_ci}
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_cistatic int spufs_tid_open(struct inode *inode, struct file *file)
21868c2ecf20Sopenharmony_ci{
21878c2ecf20Sopenharmony_ci	return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx);
21888c2ecf20Sopenharmony_ci}
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_cistatic const struct file_operations spufs_tid_fops = {
21918c2ecf20Sopenharmony_ci	.open		= spufs_tid_open,
21928c2ecf20Sopenharmony_ci	.read		= seq_read,
21938c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
21948c2ecf20Sopenharmony_ci	.release	= single_release,
21958c2ecf20Sopenharmony_ci};
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_cistatic const char *ctx_state_names[] = {
21988c2ecf20Sopenharmony_ci	"user", "system", "iowait", "loaded"
21998c2ecf20Sopenharmony_ci};
22008c2ecf20Sopenharmony_ci
22018c2ecf20Sopenharmony_cistatic unsigned long long spufs_acct_time(struct spu_context *ctx,
22028c2ecf20Sopenharmony_ci		enum spu_utilization_state state)
22038c2ecf20Sopenharmony_ci{
22048c2ecf20Sopenharmony_ci	unsigned long long time = ctx->stats.times[state];
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci	/*
22078c2ecf20Sopenharmony_ci	 * In general, utilization statistics are updated by the controlling
22088c2ecf20Sopenharmony_ci	 * thread as the spu context moves through various well defined
22098c2ecf20Sopenharmony_ci	 * state transitions, but if the context is lazily loaded its
22108c2ecf20Sopenharmony_ci	 * utilization statistics are not updated as the controlling thread
22118c2ecf20Sopenharmony_ci	 * is not tightly coupled with the execution of the spu context.  We
22128c2ecf20Sopenharmony_ci	 * calculate and apply the time delta from the last recorded state
22138c2ecf20Sopenharmony_ci	 * of the spu context.
22148c2ecf20Sopenharmony_ci	 */
22158c2ecf20Sopenharmony_ci	if (ctx->spu && ctx->stats.util_state == state) {
22168c2ecf20Sopenharmony_ci		time += ktime_get_ns() - ctx->stats.tstamp;
22178c2ecf20Sopenharmony_ci	}
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	return time / NSEC_PER_MSEC;
22208c2ecf20Sopenharmony_ci}
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_cistatic unsigned long long spufs_slb_flts(struct spu_context *ctx)
22238c2ecf20Sopenharmony_ci{
22248c2ecf20Sopenharmony_ci	unsigned long long slb_flts = ctx->stats.slb_flt;
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	if (ctx->state == SPU_STATE_RUNNABLE) {
22278c2ecf20Sopenharmony_ci		slb_flts += (ctx->spu->stats.slb_flt -
22288c2ecf20Sopenharmony_ci			     ctx->stats.slb_flt_base);
22298c2ecf20Sopenharmony_ci	}
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	return slb_flts;
22328c2ecf20Sopenharmony_ci}
22338c2ecf20Sopenharmony_ci
22348c2ecf20Sopenharmony_cistatic unsigned long long spufs_class2_intrs(struct spu_context *ctx)
22358c2ecf20Sopenharmony_ci{
22368c2ecf20Sopenharmony_ci	unsigned long long class2_intrs = ctx->stats.class2_intr;
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	if (ctx->state == SPU_STATE_RUNNABLE) {
22398c2ecf20Sopenharmony_ci		class2_intrs += (ctx->spu->stats.class2_intr -
22408c2ecf20Sopenharmony_ci				 ctx->stats.class2_intr_base);
22418c2ecf20Sopenharmony_ci	}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	return class2_intrs;
22448c2ecf20Sopenharmony_ci}
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_cistatic int spufs_show_stat(struct seq_file *s, void *private)
22488c2ecf20Sopenharmony_ci{
22498c2ecf20Sopenharmony_ci	struct spu_context *ctx = s->private;
22508c2ecf20Sopenharmony_ci	int ret;
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	ret = spu_acquire(ctx);
22538c2ecf20Sopenharmony_ci	if (ret)
22548c2ecf20Sopenharmony_ci		return ret;
22558c2ecf20Sopenharmony_ci
22568c2ecf20Sopenharmony_ci	seq_printf(s, "%s %llu %llu %llu %llu "
22578c2ecf20Sopenharmony_ci		      "%llu %llu %llu %llu %llu %llu %llu %llu\n",
22588c2ecf20Sopenharmony_ci		ctx_state_names[ctx->stats.util_state],
22598c2ecf20Sopenharmony_ci		spufs_acct_time(ctx, SPU_UTIL_USER),
22608c2ecf20Sopenharmony_ci		spufs_acct_time(ctx, SPU_UTIL_SYSTEM),
22618c2ecf20Sopenharmony_ci		spufs_acct_time(ctx, SPU_UTIL_IOWAIT),
22628c2ecf20Sopenharmony_ci		spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED),
22638c2ecf20Sopenharmony_ci		ctx->stats.vol_ctx_switch,
22648c2ecf20Sopenharmony_ci		ctx->stats.invol_ctx_switch,
22658c2ecf20Sopenharmony_ci		spufs_slb_flts(ctx),
22668c2ecf20Sopenharmony_ci		ctx->stats.hash_flt,
22678c2ecf20Sopenharmony_ci		ctx->stats.min_flt,
22688c2ecf20Sopenharmony_ci		ctx->stats.maj_flt,
22698c2ecf20Sopenharmony_ci		spufs_class2_intrs(ctx),
22708c2ecf20Sopenharmony_ci		ctx->stats.libassist);
22718c2ecf20Sopenharmony_ci	spu_release(ctx);
22728c2ecf20Sopenharmony_ci	return 0;
22738c2ecf20Sopenharmony_ci}
22748c2ecf20Sopenharmony_ci
22758c2ecf20Sopenharmony_cistatic int spufs_stat_open(struct inode *inode, struct file *file)
22768c2ecf20Sopenharmony_ci{
22778c2ecf20Sopenharmony_ci	return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx);
22788c2ecf20Sopenharmony_ci}
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_cistatic const struct file_operations spufs_stat_fops = {
22818c2ecf20Sopenharmony_ci	.open		= spufs_stat_open,
22828c2ecf20Sopenharmony_ci	.read		= seq_read,
22838c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
22848c2ecf20Sopenharmony_ci	.release	= single_release,
22858c2ecf20Sopenharmony_ci};
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_cistatic inline int spufs_switch_log_used(struct spu_context *ctx)
22888c2ecf20Sopenharmony_ci{
22898c2ecf20Sopenharmony_ci	return (ctx->switch_log->head - ctx->switch_log->tail) %
22908c2ecf20Sopenharmony_ci		SWITCH_LOG_BUFSIZE;
22918c2ecf20Sopenharmony_ci}
22928c2ecf20Sopenharmony_ci
22938c2ecf20Sopenharmony_cistatic inline int spufs_switch_log_avail(struct spu_context *ctx)
22948c2ecf20Sopenharmony_ci{
22958c2ecf20Sopenharmony_ci	return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx);
22968c2ecf20Sopenharmony_ci}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_cistatic int spufs_switch_log_open(struct inode *inode, struct file *file)
22998c2ecf20Sopenharmony_ci{
23008c2ecf20Sopenharmony_ci	struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
23018c2ecf20Sopenharmony_ci	int rc;
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	rc = spu_acquire(ctx);
23048c2ecf20Sopenharmony_ci	if (rc)
23058c2ecf20Sopenharmony_ci		return rc;
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci	if (ctx->switch_log) {
23088c2ecf20Sopenharmony_ci		rc = -EBUSY;
23098c2ecf20Sopenharmony_ci		goto out;
23108c2ecf20Sopenharmony_ci	}
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	ctx->switch_log = kmalloc(struct_size(ctx->switch_log, log,
23138c2ecf20Sopenharmony_ci				  SWITCH_LOG_BUFSIZE), GFP_KERNEL);
23148c2ecf20Sopenharmony_ci
23158c2ecf20Sopenharmony_ci	if (!ctx->switch_log) {
23168c2ecf20Sopenharmony_ci		rc = -ENOMEM;
23178c2ecf20Sopenharmony_ci		goto out;
23188c2ecf20Sopenharmony_ci	}
23198c2ecf20Sopenharmony_ci
23208c2ecf20Sopenharmony_ci	ctx->switch_log->head = ctx->switch_log->tail = 0;
23218c2ecf20Sopenharmony_ci	init_waitqueue_head(&ctx->switch_log->wait);
23228c2ecf20Sopenharmony_ci	rc = 0;
23238c2ecf20Sopenharmony_ci
23248c2ecf20Sopenharmony_ciout:
23258c2ecf20Sopenharmony_ci	spu_release(ctx);
23268c2ecf20Sopenharmony_ci	return rc;
23278c2ecf20Sopenharmony_ci}
23288c2ecf20Sopenharmony_ci
23298c2ecf20Sopenharmony_cistatic int spufs_switch_log_release(struct inode *inode, struct file *file)
23308c2ecf20Sopenharmony_ci{
23318c2ecf20Sopenharmony_ci	struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
23328c2ecf20Sopenharmony_ci	int rc;
23338c2ecf20Sopenharmony_ci
23348c2ecf20Sopenharmony_ci	rc = spu_acquire(ctx);
23358c2ecf20Sopenharmony_ci	if (rc)
23368c2ecf20Sopenharmony_ci		return rc;
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	kfree(ctx->switch_log);
23398c2ecf20Sopenharmony_ci	ctx->switch_log = NULL;
23408c2ecf20Sopenharmony_ci	spu_release(ctx);
23418c2ecf20Sopenharmony_ci
23428c2ecf20Sopenharmony_ci	return 0;
23438c2ecf20Sopenharmony_ci}
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_cistatic int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n)
23468c2ecf20Sopenharmony_ci{
23478c2ecf20Sopenharmony_ci	struct switch_log_entry *p;
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci	p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE;
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	return snprintf(tbuf, n, "%llu.%09u %d %u %u %llu\n",
23528c2ecf20Sopenharmony_ci			(unsigned long long) p->tstamp.tv_sec,
23538c2ecf20Sopenharmony_ci			(unsigned int) p->tstamp.tv_nsec,
23548c2ecf20Sopenharmony_ci			p->spu_id,
23558c2ecf20Sopenharmony_ci			(unsigned int) p->type,
23568c2ecf20Sopenharmony_ci			(unsigned int) p->val,
23578c2ecf20Sopenharmony_ci			(unsigned long long) p->timebase);
23588c2ecf20Sopenharmony_ci}
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_cistatic ssize_t spufs_switch_log_read(struct file *file, char __user *buf,
23618c2ecf20Sopenharmony_ci			     size_t len, loff_t *ppos)
23628c2ecf20Sopenharmony_ci{
23638c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
23648c2ecf20Sopenharmony_ci	struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
23658c2ecf20Sopenharmony_ci	int error = 0, cnt = 0;
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ci	if (!buf)
23688c2ecf20Sopenharmony_ci		return -EINVAL;
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci	error = spu_acquire(ctx);
23718c2ecf20Sopenharmony_ci	if (error)
23728c2ecf20Sopenharmony_ci		return error;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	while (cnt < len) {
23758c2ecf20Sopenharmony_ci		char tbuf[128];
23768c2ecf20Sopenharmony_ci		int width;
23778c2ecf20Sopenharmony_ci
23788c2ecf20Sopenharmony_ci		if (spufs_switch_log_used(ctx) == 0) {
23798c2ecf20Sopenharmony_ci			if (cnt > 0) {
23808c2ecf20Sopenharmony_ci				/* If there's data ready to go, we can
23818c2ecf20Sopenharmony_ci				 * just return straight away */
23828c2ecf20Sopenharmony_ci				break;
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci			} else if (file->f_flags & O_NONBLOCK) {
23858c2ecf20Sopenharmony_ci				error = -EAGAIN;
23868c2ecf20Sopenharmony_ci				break;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci			} else {
23898c2ecf20Sopenharmony_ci				/* spufs_wait will drop the mutex and
23908c2ecf20Sopenharmony_ci				 * re-acquire, but since we're in read(), the
23918c2ecf20Sopenharmony_ci				 * file cannot be _released (and so
23928c2ecf20Sopenharmony_ci				 * ctx->switch_log is stable).
23938c2ecf20Sopenharmony_ci				 */
23948c2ecf20Sopenharmony_ci				error = spufs_wait(ctx->switch_log->wait,
23958c2ecf20Sopenharmony_ci						spufs_switch_log_used(ctx) > 0);
23968c2ecf20Sopenharmony_ci
23978c2ecf20Sopenharmony_ci				/* On error, spufs_wait returns without the
23988c2ecf20Sopenharmony_ci				 * state mutex held */
23998c2ecf20Sopenharmony_ci				if (error)
24008c2ecf20Sopenharmony_ci					return error;
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci				/* We may have had entries read from underneath
24038c2ecf20Sopenharmony_ci				 * us while we dropped the mutex in spufs_wait,
24048c2ecf20Sopenharmony_ci				 * so re-check */
24058c2ecf20Sopenharmony_ci				if (spufs_switch_log_used(ctx) == 0)
24068c2ecf20Sopenharmony_ci					continue;
24078c2ecf20Sopenharmony_ci			}
24088c2ecf20Sopenharmony_ci		}
24098c2ecf20Sopenharmony_ci
24108c2ecf20Sopenharmony_ci		width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
24118c2ecf20Sopenharmony_ci		if (width < len)
24128c2ecf20Sopenharmony_ci			ctx->switch_log->tail =
24138c2ecf20Sopenharmony_ci				(ctx->switch_log->tail + 1) %
24148c2ecf20Sopenharmony_ci				 SWITCH_LOG_BUFSIZE;
24158c2ecf20Sopenharmony_ci		else
24168c2ecf20Sopenharmony_ci			/* If the record is greater than space available return
24178c2ecf20Sopenharmony_ci			 * partial buffer (so far) */
24188c2ecf20Sopenharmony_ci			break;
24198c2ecf20Sopenharmony_ci
24208c2ecf20Sopenharmony_ci		error = copy_to_user(buf + cnt, tbuf, width);
24218c2ecf20Sopenharmony_ci		if (error)
24228c2ecf20Sopenharmony_ci			break;
24238c2ecf20Sopenharmony_ci		cnt += width;
24248c2ecf20Sopenharmony_ci	}
24258c2ecf20Sopenharmony_ci
24268c2ecf20Sopenharmony_ci	spu_release(ctx);
24278c2ecf20Sopenharmony_ci
24288c2ecf20Sopenharmony_ci	return cnt == 0 ? error : cnt;
24298c2ecf20Sopenharmony_ci}
24308c2ecf20Sopenharmony_ci
24318c2ecf20Sopenharmony_cistatic __poll_t spufs_switch_log_poll(struct file *file, poll_table *wait)
24328c2ecf20Sopenharmony_ci{
24338c2ecf20Sopenharmony_ci	struct inode *inode = file_inode(file);
24348c2ecf20Sopenharmony_ci	struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
24358c2ecf20Sopenharmony_ci	__poll_t mask = 0;
24368c2ecf20Sopenharmony_ci	int rc;
24378c2ecf20Sopenharmony_ci
24388c2ecf20Sopenharmony_ci	poll_wait(file, &ctx->switch_log->wait, wait);
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	rc = spu_acquire(ctx);
24418c2ecf20Sopenharmony_ci	if (rc)
24428c2ecf20Sopenharmony_ci		return rc;
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	if (spufs_switch_log_used(ctx) > 0)
24458c2ecf20Sopenharmony_ci		mask |= EPOLLIN;
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	spu_release(ctx);
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	return mask;
24508c2ecf20Sopenharmony_ci}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_cistatic const struct file_operations spufs_switch_log_fops = {
24538c2ecf20Sopenharmony_ci	.open		= spufs_switch_log_open,
24548c2ecf20Sopenharmony_ci	.read		= spufs_switch_log_read,
24558c2ecf20Sopenharmony_ci	.poll		= spufs_switch_log_poll,
24568c2ecf20Sopenharmony_ci	.release	= spufs_switch_log_release,
24578c2ecf20Sopenharmony_ci	.llseek		= no_llseek,
24588c2ecf20Sopenharmony_ci};
24598c2ecf20Sopenharmony_ci
24608c2ecf20Sopenharmony_ci/**
24618c2ecf20Sopenharmony_ci * Log a context switch event to a switch log reader.
24628c2ecf20Sopenharmony_ci *
24638c2ecf20Sopenharmony_ci * Must be called with ctx->state_mutex held.
24648c2ecf20Sopenharmony_ci */
24658c2ecf20Sopenharmony_civoid spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
24668c2ecf20Sopenharmony_ci		u32 type, u32 val)
24678c2ecf20Sopenharmony_ci{
24688c2ecf20Sopenharmony_ci	if (!ctx->switch_log)
24698c2ecf20Sopenharmony_ci		return;
24708c2ecf20Sopenharmony_ci
24718c2ecf20Sopenharmony_ci	if (spufs_switch_log_avail(ctx) > 1) {
24728c2ecf20Sopenharmony_ci		struct switch_log_entry *p;
24738c2ecf20Sopenharmony_ci
24748c2ecf20Sopenharmony_ci		p = ctx->switch_log->log + ctx->switch_log->head;
24758c2ecf20Sopenharmony_ci		ktime_get_ts64(&p->tstamp);
24768c2ecf20Sopenharmony_ci		p->timebase = get_tb();
24778c2ecf20Sopenharmony_ci		p->spu_id = spu ? spu->number : -1;
24788c2ecf20Sopenharmony_ci		p->type = type;
24798c2ecf20Sopenharmony_ci		p->val = val;
24808c2ecf20Sopenharmony_ci
24818c2ecf20Sopenharmony_ci		ctx->switch_log->head =
24828c2ecf20Sopenharmony_ci			(ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE;
24838c2ecf20Sopenharmony_ci	}
24848c2ecf20Sopenharmony_ci
24858c2ecf20Sopenharmony_ci	wake_up(&ctx->switch_log->wait);
24868c2ecf20Sopenharmony_ci}
24878c2ecf20Sopenharmony_ci
24888c2ecf20Sopenharmony_cistatic int spufs_show_ctx(struct seq_file *s, void *private)
24898c2ecf20Sopenharmony_ci{
24908c2ecf20Sopenharmony_ci	struct spu_context *ctx = s->private;
24918c2ecf20Sopenharmony_ci	u64 mfc_control_RW;
24928c2ecf20Sopenharmony_ci
24938c2ecf20Sopenharmony_ci	mutex_lock(&ctx->state_mutex);
24948c2ecf20Sopenharmony_ci	if (ctx->spu) {
24958c2ecf20Sopenharmony_ci		struct spu *spu = ctx->spu;
24968c2ecf20Sopenharmony_ci		struct spu_priv2 __iomem *priv2 = spu->priv2;
24978c2ecf20Sopenharmony_ci
24988c2ecf20Sopenharmony_ci		spin_lock_irq(&spu->register_lock);
24998c2ecf20Sopenharmony_ci		mfc_control_RW = in_be64(&priv2->mfc_control_RW);
25008c2ecf20Sopenharmony_ci		spin_unlock_irq(&spu->register_lock);
25018c2ecf20Sopenharmony_ci	} else {
25028c2ecf20Sopenharmony_ci		struct spu_state *csa = &ctx->csa;
25038c2ecf20Sopenharmony_ci
25048c2ecf20Sopenharmony_ci		mfc_control_RW = csa->priv2.mfc_control_RW;
25058c2ecf20Sopenharmony_ci	}
25068c2ecf20Sopenharmony_ci
25078c2ecf20Sopenharmony_ci	seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)"
25088c2ecf20Sopenharmony_ci		" %c %llx %llx %llx %llx %x %x\n",
25098c2ecf20Sopenharmony_ci		ctx->state == SPU_STATE_SAVED ? 'S' : 'R',
25108c2ecf20Sopenharmony_ci		ctx->flags,
25118c2ecf20Sopenharmony_ci		ctx->sched_flags,
25128c2ecf20Sopenharmony_ci		ctx->prio,
25138c2ecf20Sopenharmony_ci		ctx->time_slice,
25148c2ecf20Sopenharmony_ci		ctx->spu ? ctx->spu->number : -1,
25158c2ecf20Sopenharmony_ci		!list_empty(&ctx->rq) ? 'q' : ' ',
25168c2ecf20Sopenharmony_ci		ctx->csa.class_0_pending,
25178c2ecf20Sopenharmony_ci		ctx->csa.class_0_dar,
25188c2ecf20Sopenharmony_ci		ctx->csa.class_1_dsisr,
25198c2ecf20Sopenharmony_ci		mfc_control_RW,
25208c2ecf20Sopenharmony_ci		ctx->ops->runcntl_read(ctx),
25218c2ecf20Sopenharmony_ci		ctx->ops->status_read(ctx));
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	mutex_unlock(&ctx->state_mutex);
25248c2ecf20Sopenharmony_ci
25258c2ecf20Sopenharmony_ci	return 0;
25268c2ecf20Sopenharmony_ci}
25278c2ecf20Sopenharmony_ci
25288c2ecf20Sopenharmony_cistatic int spufs_ctx_open(struct inode *inode, struct file *file)
25298c2ecf20Sopenharmony_ci{
25308c2ecf20Sopenharmony_ci	return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx);
25318c2ecf20Sopenharmony_ci}
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_cistatic const struct file_operations spufs_ctx_fops = {
25348c2ecf20Sopenharmony_ci	.open           = spufs_ctx_open,
25358c2ecf20Sopenharmony_ci	.read           = seq_read,
25368c2ecf20Sopenharmony_ci	.llseek         = seq_lseek,
25378c2ecf20Sopenharmony_ci	.release        = single_release,
25388c2ecf20Sopenharmony_ci};
25398c2ecf20Sopenharmony_ci
25408c2ecf20Sopenharmony_ciconst struct spufs_tree_descr spufs_dir_contents[] = {
25418c2ecf20Sopenharmony_ci	{ "capabilities", &spufs_caps_fops, 0444, },
25428c2ecf20Sopenharmony_ci	{ "mem",  &spufs_mem_fops,  0666, LS_SIZE, },
25438c2ecf20Sopenharmony_ci	{ "regs", &spufs_regs_fops,  0666, sizeof(struct spu_reg128[128]), },
25448c2ecf20Sopenharmony_ci	{ "mbox", &spufs_mbox_fops, 0444, },
25458c2ecf20Sopenharmony_ci	{ "ibox", &spufs_ibox_fops, 0444, },
25468c2ecf20Sopenharmony_ci	{ "wbox", &spufs_wbox_fops, 0222, },
25478c2ecf20Sopenharmony_ci	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), },
25488c2ecf20Sopenharmony_ci	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), },
25498c2ecf20Sopenharmony_ci	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), },
25508c2ecf20Sopenharmony_ci	{ "signal1", &spufs_signal1_fops, 0666, },
25518c2ecf20Sopenharmony_ci	{ "signal2", &spufs_signal2_fops, 0666, },
25528c2ecf20Sopenharmony_ci	{ "signal1_type", &spufs_signal1_type, 0666, },
25538c2ecf20Sopenharmony_ci	{ "signal2_type", &spufs_signal2_type, 0666, },
25548c2ecf20Sopenharmony_ci	{ "cntl", &spufs_cntl_fops,  0666, },
25558c2ecf20Sopenharmony_ci	{ "fpcr", &spufs_fpcr_fops, 0666, sizeof(struct spu_reg128), },
25568c2ecf20Sopenharmony_ci	{ "lslr", &spufs_lslr_ops, 0444, },
25578c2ecf20Sopenharmony_ci	{ "mfc", &spufs_mfc_fops, 0666, },
25588c2ecf20Sopenharmony_ci	{ "mss", &spufs_mss_fops, 0666, },
25598c2ecf20Sopenharmony_ci	{ "npc", &spufs_npc_ops, 0666, },
25608c2ecf20Sopenharmony_ci	{ "srr0", &spufs_srr0_ops, 0666, },
25618c2ecf20Sopenharmony_ci	{ "decr", &spufs_decr_ops, 0666, },
25628c2ecf20Sopenharmony_ci	{ "decr_status", &spufs_decr_status_ops, 0666, },
25638c2ecf20Sopenharmony_ci	{ "event_mask", &spufs_event_mask_ops, 0666, },
25648c2ecf20Sopenharmony_ci	{ "event_status", &spufs_event_status_ops, 0444, },
25658c2ecf20Sopenharmony_ci	{ "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, },
25668c2ecf20Sopenharmony_ci	{ "phys-id", &spufs_id_ops, 0666, },
25678c2ecf20Sopenharmony_ci	{ "object-id", &spufs_object_id_ops, 0666, },
25688c2ecf20Sopenharmony_ci	{ "mbox_info", &spufs_mbox_info_fops, 0444, sizeof(u32), },
25698c2ecf20Sopenharmony_ci	{ "ibox_info", &spufs_ibox_info_fops, 0444, sizeof(u32), },
25708c2ecf20Sopenharmony_ci	{ "wbox_info", &spufs_wbox_info_fops, 0444, sizeof(u32), },
25718c2ecf20Sopenharmony_ci	{ "dma_info", &spufs_dma_info_fops, 0444,
25728c2ecf20Sopenharmony_ci		sizeof(struct spu_dma_info), },
25738c2ecf20Sopenharmony_ci	{ "proxydma_info", &spufs_proxydma_info_fops, 0444,
25748c2ecf20Sopenharmony_ci		sizeof(struct spu_proxydma_info)},
25758c2ecf20Sopenharmony_ci	{ "tid", &spufs_tid_fops, 0444, },
25768c2ecf20Sopenharmony_ci	{ "stat", &spufs_stat_fops, 0444, },
25778c2ecf20Sopenharmony_ci	{ "switch_log", &spufs_switch_log_fops, 0444 },
25788c2ecf20Sopenharmony_ci	{},
25798c2ecf20Sopenharmony_ci};
25808c2ecf20Sopenharmony_ci
25818c2ecf20Sopenharmony_ciconst struct spufs_tree_descr spufs_dir_nosched_contents[] = {
25828c2ecf20Sopenharmony_ci	{ "capabilities", &spufs_caps_fops, 0444, },
25838c2ecf20Sopenharmony_ci	{ "mem",  &spufs_mem_fops,  0666, LS_SIZE, },
25848c2ecf20Sopenharmony_ci	{ "mbox", &spufs_mbox_fops, 0444, },
25858c2ecf20Sopenharmony_ci	{ "ibox", &spufs_ibox_fops, 0444, },
25868c2ecf20Sopenharmony_ci	{ "wbox", &spufs_wbox_fops, 0222, },
25878c2ecf20Sopenharmony_ci	{ "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), },
25888c2ecf20Sopenharmony_ci	{ "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), },
25898c2ecf20Sopenharmony_ci	{ "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), },
25908c2ecf20Sopenharmony_ci	{ "signal1", &spufs_signal1_nosched_fops, 0222, },
25918c2ecf20Sopenharmony_ci	{ "signal2", &spufs_signal2_nosched_fops, 0222, },
25928c2ecf20Sopenharmony_ci	{ "signal1_type", &spufs_signal1_type, 0666, },
25938c2ecf20Sopenharmony_ci	{ "signal2_type", &spufs_signal2_type, 0666, },
25948c2ecf20Sopenharmony_ci	{ "mss", &spufs_mss_fops, 0666, },
25958c2ecf20Sopenharmony_ci	{ "mfc", &spufs_mfc_fops, 0666, },
25968c2ecf20Sopenharmony_ci	{ "cntl", &spufs_cntl_fops,  0666, },
25978c2ecf20Sopenharmony_ci	{ "npc", &spufs_npc_ops, 0666, },
25988c2ecf20Sopenharmony_ci	{ "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, },
25998c2ecf20Sopenharmony_ci	{ "phys-id", &spufs_id_ops, 0666, },
26008c2ecf20Sopenharmony_ci	{ "object-id", &spufs_object_id_ops, 0666, },
26018c2ecf20Sopenharmony_ci	{ "tid", &spufs_tid_fops, 0444, },
26028c2ecf20Sopenharmony_ci	{ "stat", &spufs_stat_fops, 0444, },
26038c2ecf20Sopenharmony_ci	{},
26048c2ecf20Sopenharmony_ci};
26058c2ecf20Sopenharmony_ci
26068c2ecf20Sopenharmony_ciconst struct spufs_tree_descr spufs_dir_debug_contents[] = {
26078c2ecf20Sopenharmony_ci	{ ".ctx", &spufs_ctx_fops, 0444, },
26088c2ecf20Sopenharmony_ci	{},
26098c2ecf20Sopenharmony_ci};
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ciconst struct spufs_coredump_reader spufs_coredump_read[] = {
26128c2ecf20Sopenharmony_ci	{ "regs", spufs_regs_dump, NULL, sizeof(struct spu_reg128[128])},
26138c2ecf20Sopenharmony_ci	{ "fpcr", spufs_fpcr_dump, NULL, sizeof(struct spu_reg128) },
26148c2ecf20Sopenharmony_ci	{ "lslr", NULL, spufs_lslr_get, 19 },
26158c2ecf20Sopenharmony_ci	{ "decr", NULL, spufs_decr_get, 19 },
26168c2ecf20Sopenharmony_ci	{ "decr_status", NULL, spufs_decr_status_get, 19 },
26178c2ecf20Sopenharmony_ci	{ "mem", spufs_mem_dump, NULL, LS_SIZE, },
26188c2ecf20Sopenharmony_ci	{ "signal1", spufs_signal1_dump, NULL, sizeof(u32) },
26198c2ecf20Sopenharmony_ci	{ "signal1_type", NULL, spufs_signal1_type_get, 19 },
26208c2ecf20Sopenharmony_ci	{ "signal2", spufs_signal2_dump, NULL, sizeof(u32) },
26218c2ecf20Sopenharmony_ci	{ "signal2_type", NULL, spufs_signal2_type_get, 19 },
26228c2ecf20Sopenharmony_ci	{ "event_mask", NULL, spufs_event_mask_get, 19 },
26238c2ecf20Sopenharmony_ci	{ "event_status", NULL, spufs_event_status_get, 19 },
26248c2ecf20Sopenharmony_ci	{ "mbox_info", spufs_mbox_info_dump, NULL, sizeof(u32) },
26258c2ecf20Sopenharmony_ci	{ "ibox_info", spufs_ibox_info_dump, NULL, sizeof(u32) },
26268c2ecf20Sopenharmony_ci	{ "wbox_info", spufs_wbox_info_dump, NULL, 4 * sizeof(u32)},
26278c2ecf20Sopenharmony_ci	{ "dma_info", spufs_dma_info_dump, NULL, sizeof(struct spu_dma_info)},
26288c2ecf20Sopenharmony_ci	{ "proxydma_info", spufs_proxydma_info_dump,
26298c2ecf20Sopenharmony_ci			   NULL, sizeof(struct spu_proxydma_info)},
26308c2ecf20Sopenharmony_ci	{ "object-id", NULL, spufs_object_id_get, 19 },
26318c2ecf20Sopenharmony_ci	{ "npc", NULL, spufs_npc_get, 19 },
26328c2ecf20Sopenharmony_ci	{ NULL },
26338c2ecf20Sopenharmony_ci};
2634