167207b96SArnd Bergmann /* 267207b96SArnd Bergmann * SPU file system -- file contents 367207b96SArnd Bergmann * 467207b96SArnd Bergmann * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 567207b96SArnd Bergmann * 667207b96SArnd Bergmann * Author: Arnd Bergmann <arndb@de.ibm.com> 767207b96SArnd Bergmann * 867207b96SArnd Bergmann * This program is free software; you can redistribute it and/or modify 967207b96SArnd Bergmann * it under the terms of the GNU General Public License as published by 1067207b96SArnd Bergmann * the Free Software Foundation; either version 2, or (at your option) 1167207b96SArnd Bergmann * any later version. 1267207b96SArnd Bergmann * 1367207b96SArnd Bergmann * This program is distributed in the hope that it will be useful, 1467207b96SArnd Bergmann * but WITHOUT ANY WARRANTY; without even the implied warranty of 1567207b96SArnd Bergmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1667207b96SArnd Bergmann * GNU General Public License for more details. 1767207b96SArnd Bergmann * 1867207b96SArnd Bergmann * You should have received a copy of the GNU General Public License 1967207b96SArnd Bergmann * along with this program; if not, write to the Free Software 2067207b96SArnd Bergmann * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2167207b96SArnd Bergmann */ 2267207b96SArnd Bergmann 23a33a7d73SArnd Bergmann #undef DEBUG 24a33a7d73SArnd Bergmann 2567207b96SArnd Bergmann #include <linux/fs.h> 2667207b96SArnd Bergmann #include <linux/ioctl.h> 274b16f8e2SPaul Gortmaker #include <linux/export.h> 28d88cfffaSArnd Bergmann #include <linux/pagemap.h> 2967207b96SArnd Bergmann #include <linux/poll.h> 305110459fSArnd Bergmann #include <linux/ptrace.h> 31cbe709c1SBenjamin Herrenschmidt #include <linux/seq_file.h> 325a0e3ad6STejun Heo #include <linux/slab.h> 3367207b96SArnd Bergmann 3467207b96SArnd Bergmann #include <asm/io.h> 35dfe1e09fSFUJITA Tomonori #include <asm/time.h> 3667207b96SArnd Bergmann #include <asm/spu.h> 37b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h> 387c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 3967207b96SArnd Bergmann 4067207b96SArnd Bergmann #include "spufs.h" 41ae142e0cSChristoph Hellwig #include "sputrace.h" 4267207b96SArnd Bergmann 4327d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) 4427d5bf2aSBenjamin Herrenschmidt 45197b1a82SChristoph Hellwig /* Simple attribute files */ 46197b1a82SChristoph Hellwig struct spufs_attr { 47197b1a82SChristoph Hellwig int (*get)(void *, u64 *); 48197b1a82SChristoph Hellwig int (*set)(void *, u64); 49197b1a82SChristoph Hellwig char get_buf[24]; /* enough to store a u64 and "\n\0" */ 50197b1a82SChristoph Hellwig char set_buf[24]; 51197b1a82SChristoph Hellwig void *data; 52197b1a82SChristoph Hellwig const char *fmt; /* format for read operation */ 53197b1a82SChristoph Hellwig struct mutex mutex; /* protects access to these buffers */ 54197b1a82SChristoph Hellwig }; 55197b1a82SChristoph Hellwig 56197b1a82SChristoph Hellwig static int spufs_attr_open(struct inode *inode, struct file *file, 57197b1a82SChristoph Hellwig int (*get)(void *, u64 *), int (*set)(void *, u64), 58197b1a82SChristoph Hellwig const char *fmt) 59197b1a82SChristoph Hellwig { 60197b1a82SChristoph Hellwig struct spufs_attr *attr; 61197b1a82SChristoph Hellwig 62197b1a82SChristoph Hellwig attr = kmalloc(sizeof(*attr), GFP_KERNEL); 63197b1a82SChristoph Hellwig if (!attr) 64197b1a82SChristoph Hellwig return -ENOMEM; 65197b1a82SChristoph Hellwig 66197b1a82SChristoph Hellwig attr->get = get; 67197b1a82SChristoph Hellwig attr->set = set; 68197b1a82SChristoph Hellwig attr->data = inode->i_private; 69197b1a82SChristoph Hellwig attr->fmt = fmt; 70197b1a82SChristoph Hellwig mutex_init(&attr->mutex); 71197b1a82SChristoph Hellwig file->private_data = attr; 72197b1a82SChristoph Hellwig 73197b1a82SChristoph Hellwig return nonseekable_open(inode, file); 74197b1a82SChristoph Hellwig } 75197b1a82SChristoph Hellwig 76197b1a82SChristoph Hellwig static int spufs_attr_release(struct inode *inode, struct file *file) 77197b1a82SChristoph Hellwig { 78197b1a82SChristoph Hellwig kfree(file->private_data); 79197b1a82SChristoph Hellwig return 0; 80197b1a82SChristoph Hellwig } 81197b1a82SChristoph Hellwig 82197b1a82SChristoph Hellwig static ssize_t spufs_attr_read(struct file *file, char __user *buf, 83197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 84197b1a82SChristoph Hellwig { 85197b1a82SChristoph Hellwig struct spufs_attr *attr; 86197b1a82SChristoph Hellwig size_t size; 87197b1a82SChristoph Hellwig ssize_t ret; 88197b1a82SChristoph Hellwig 89197b1a82SChristoph Hellwig attr = file->private_data; 90197b1a82SChristoph Hellwig if (!attr->get) 91197b1a82SChristoph Hellwig return -EACCES; 92197b1a82SChristoph Hellwig 93197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 94197b1a82SChristoph Hellwig if (ret) 95197b1a82SChristoph Hellwig return ret; 96197b1a82SChristoph Hellwig 97197b1a82SChristoph Hellwig if (*ppos) { /* continued read */ 98197b1a82SChristoph Hellwig size = strlen(attr->get_buf); 99197b1a82SChristoph Hellwig } else { /* first read */ 100197b1a82SChristoph Hellwig u64 val; 101197b1a82SChristoph Hellwig ret = attr->get(attr->data, &val); 102197b1a82SChristoph Hellwig if (ret) 103197b1a82SChristoph Hellwig goto out; 104197b1a82SChristoph Hellwig 105197b1a82SChristoph Hellwig size = scnprintf(attr->get_buf, sizeof(attr->get_buf), 106197b1a82SChristoph Hellwig attr->fmt, (unsigned long long)val); 107197b1a82SChristoph Hellwig } 108197b1a82SChristoph Hellwig 109197b1a82SChristoph Hellwig ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size); 110197b1a82SChristoph Hellwig out: 111197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 112197b1a82SChristoph Hellwig return ret; 113197b1a82SChristoph Hellwig } 114197b1a82SChristoph Hellwig 115197b1a82SChristoph Hellwig static ssize_t spufs_attr_write(struct file *file, const char __user *buf, 116197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 117197b1a82SChristoph Hellwig { 118197b1a82SChristoph Hellwig struct spufs_attr *attr; 119197b1a82SChristoph Hellwig u64 val; 120197b1a82SChristoph Hellwig size_t size; 121197b1a82SChristoph Hellwig ssize_t ret; 122197b1a82SChristoph Hellwig 123197b1a82SChristoph Hellwig attr = file->private_data; 124197b1a82SChristoph Hellwig if (!attr->set) 125197b1a82SChristoph Hellwig return -EACCES; 126197b1a82SChristoph Hellwig 127197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 128197b1a82SChristoph Hellwig if (ret) 129197b1a82SChristoph Hellwig return ret; 130197b1a82SChristoph Hellwig 131197b1a82SChristoph Hellwig ret = -EFAULT; 132197b1a82SChristoph Hellwig size = min(sizeof(attr->set_buf) - 1, len); 133197b1a82SChristoph Hellwig if (copy_from_user(attr->set_buf, buf, size)) 134197b1a82SChristoph Hellwig goto out; 135197b1a82SChristoph Hellwig 136197b1a82SChristoph Hellwig ret = len; /* claim we got the whole input */ 137197b1a82SChristoph Hellwig attr->set_buf[size] = '\0'; 138197b1a82SChristoph Hellwig val = simple_strtol(attr->set_buf, NULL, 0); 139197b1a82SChristoph Hellwig attr->set(attr->data, val); 140197b1a82SChristoph Hellwig out: 141197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 142197b1a82SChristoph Hellwig return ret; 143197b1a82SChristoph Hellwig } 144197b1a82SChristoph Hellwig 145197b1a82SChristoph Hellwig #define DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \ 146197b1a82SChristoph Hellwig static int __fops ## _open(struct inode *inode, struct file *file) \ 147197b1a82SChristoph Hellwig { \ 148197b1a82SChristoph Hellwig __simple_attr_check_format(__fmt, 0ull); \ 149197b1a82SChristoph Hellwig return spufs_attr_open(inode, file, __get, __set, __fmt); \ 150197b1a82SChristoph Hellwig } \ 151828c0950SAlexey Dobriyan static const struct file_operations __fops = { \ 152197b1a82SChristoph Hellwig .open = __fops ## _open, \ 153197b1a82SChristoph Hellwig .release = spufs_attr_release, \ 154197b1a82SChristoph Hellwig .read = spufs_attr_read, \ 155197b1a82SChristoph Hellwig .write = spufs_attr_write, \ 156fc15351dSArnd Bergmann .llseek = generic_file_llseek, \ 157197b1a82SChristoph Hellwig }; 158197b1a82SChristoph Hellwig 159cbe709c1SBenjamin Herrenschmidt 16067207b96SArnd Bergmann static int 16167207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file) 16267207b96SArnd Bergmann { 16367207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1646df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 16543c2bbd9SChristoph Hellwig 16647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1676df10a82SMark Nutter file->private_data = ctx; 16843c2bbd9SChristoph Hellwig if (!i->i_openers++) 1696df10a82SMark Nutter ctx->local_store = inode->i_mapping; 17047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 17143c2bbd9SChristoph Hellwig return 0; 17243c2bbd9SChristoph Hellwig } 17343c2bbd9SChristoph Hellwig 17443c2bbd9SChristoph Hellwig static int 17543c2bbd9SChristoph Hellwig spufs_mem_release(struct inode *inode, struct file *file) 17643c2bbd9SChristoph Hellwig { 17743c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 17843c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 17943c2bbd9SChristoph Hellwig 18047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 18143c2bbd9SChristoph Hellwig if (!--i->i_openers) 18243c2bbd9SChristoph Hellwig ctx->local_store = NULL; 18347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 18467207b96SArnd Bergmann return 0; 18567207b96SArnd Bergmann } 18667207b96SArnd Bergmann 18767207b96SArnd Bergmann static ssize_t 188bf1ab978SDwayne Grant McConnell __spufs_mem_read(struct spu_context *ctx, char __user *buffer, 189bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 190bf1ab978SDwayne Grant McConnell { 191bf1ab978SDwayne Grant McConnell char *local_store = ctx->ops->get_ls(ctx); 192bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, local_store, 193bf1ab978SDwayne Grant McConnell LS_SIZE); 194bf1ab978SDwayne Grant McConnell } 195bf1ab978SDwayne Grant McConnell 196bf1ab978SDwayne Grant McConnell static ssize_t 19767207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer, 19867207b96SArnd Bergmann size_t size, loff_t *pos) 19967207b96SArnd Bergmann { 200bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 201aa0ed2bdSArnd Bergmann ssize_t ret; 20267207b96SArnd Bergmann 203c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 204c9101bdbSChristoph Hellwig if (ret) 205c9101bdbSChristoph Hellwig return ret; 206bf1ab978SDwayne Grant McConnell ret = __spufs_mem_read(ctx, buffer, size, pos); 2078b3d6663SArnd Bergmann spu_release(ctx); 208c9101bdbSChristoph Hellwig 20967207b96SArnd Bergmann return ret; 21067207b96SArnd Bergmann } 21167207b96SArnd Bergmann 21267207b96SArnd Bergmann static ssize_t 21367207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer, 214aa0ed2bdSArnd Bergmann size_t size, loff_t *ppos) 21567207b96SArnd Bergmann { 21667207b96SArnd Bergmann struct spu_context *ctx = file->private_data; 2178b3d6663SArnd Bergmann char *local_store; 218aa0ed2bdSArnd Bergmann loff_t pos = *ppos; 2198b3d6663SArnd Bergmann int ret; 22067207b96SArnd Bergmann 221aa0ed2bdSArnd Bergmann if (pos > LS_SIZE) 22267207b96SArnd Bergmann return -EFBIG; 2238b3d6663SArnd Bergmann 224c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 225c9101bdbSChristoph Hellwig if (ret) 226c9101bdbSChristoph Hellwig return ret; 227c9101bdbSChristoph Hellwig 2288b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 22963c3b9d7SAkinobu Mita size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size); 2308b3d6663SArnd Bergmann spu_release(ctx); 231aa0ed2bdSArnd Bergmann 232aa0ed2bdSArnd Bergmann return size; 23367207b96SArnd Bergmann } 23467207b96SArnd Bergmann 235b1e2270fSNick Piggin static int 236b1e2270fSNick Piggin spufs_mem_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 2378b3d6663SArnd Bergmann { 2388b3d6663SArnd Bergmann struct spu_context *ctx = vma->vm_file->private_data; 239b1e2270fSNick Piggin unsigned long pfn, offset; 240b1e2270fSNick Piggin 241b1e2270fSNick Piggin offset = vmf->pgoff << PAGE_SHIFT; 242128b8546SMasato Noguchi if (offset >= LS_SIZE) 243b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 244128b8546SMasato Noguchi 245b1e2270fSNick Piggin pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n", 2461a29d85eSJan Kara vmf->address, offset); 247f1fa74f4SBenjamin Herrenschmidt 248c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 249b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2508b3d6663SArnd Bergmann 251ac91cb8dSArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 25264b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); 25378bde53eSBenjamin Herrenschmidt pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset); 254ac91cb8dSArnd Bergmann } else { 25564b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 25678bde53eSBenjamin Herrenschmidt pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT; 257ac91cb8dSArnd Bergmann } 2581a29d85eSJan Kara vm_insert_pfn(vma, vmf->address, pfn); 25978bde53eSBenjamin Herrenschmidt 2608b3d6663SArnd Bergmann spu_release(ctx); 2618b3d6663SArnd Bergmann 262b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2638b3d6663SArnd Bergmann } 2648b3d6663SArnd Bergmann 265a352894dSBenjamin Herrenschmidt static int spufs_mem_mmap_access(struct vm_area_struct *vma, 266a352894dSBenjamin Herrenschmidt unsigned long address, 267a352894dSBenjamin Herrenschmidt void *buf, int len, int write) 268a352894dSBenjamin Herrenschmidt { 269a352894dSBenjamin Herrenschmidt struct spu_context *ctx = vma->vm_file->private_data; 270a352894dSBenjamin Herrenschmidt unsigned long offset = address - vma->vm_start; 271a352894dSBenjamin Herrenschmidt char *local_store; 272a352894dSBenjamin Herrenschmidt 273a352894dSBenjamin Herrenschmidt if (write && !(vma->vm_flags & VM_WRITE)) 274a352894dSBenjamin Herrenschmidt return -EACCES; 275a352894dSBenjamin Herrenschmidt if (spu_acquire(ctx)) 276a352894dSBenjamin Herrenschmidt return -EINTR; 277a352894dSBenjamin Herrenschmidt if ((offset + len) > vma->vm_end) 278a352894dSBenjamin Herrenschmidt len = vma->vm_end - offset; 279a352894dSBenjamin Herrenschmidt local_store = ctx->ops->get_ls(ctx); 280a352894dSBenjamin Herrenschmidt if (write) 281a352894dSBenjamin Herrenschmidt memcpy_toio(local_store + offset, buf, len); 282a352894dSBenjamin Herrenschmidt else 283a352894dSBenjamin Herrenschmidt memcpy_fromio(buf, local_store + offset, len); 284a352894dSBenjamin Herrenschmidt spu_release(ctx); 285a352894dSBenjamin Herrenschmidt return len; 286a352894dSBenjamin Herrenschmidt } 28778bde53eSBenjamin Herrenschmidt 288f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mem_mmap_vmops = { 289b1e2270fSNick Piggin .fault = spufs_mem_mmap_fault, 290a352894dSBenjamin Herrenschmidt .access = spufs_mem_mmap_access, 2918b3d6663SArnd Bergmann }; 2928b3d6663SArnd Bergmann 293f1fa74f4SBenjamin Herrenschmidt static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 29467207b96SArnd Bergmann { 2958b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 2968b3d6663SArnd Bergmann return -EINVAL; 29767207b96SArnd Bergmann 29878bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 29964b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 3008b3d6663SArnd Bergmann 3018b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 30267207b96SArnd Bergmann return 0; 30367207b96SArnd Bergmann } 30467207b96SArnd Bergmann 3055dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = { 30667207b96SArnd Bergmann .open = spufs_mem_open, 307ce92987bSChristoph Hellwig .release = spufs_mem_release, 30867207b96SArnd Bergmann .read = spufs_mem_read, 30967207b96SArnd Bergmann .write = spufs_mem_write, 3108b3d6663SArnd Bergmann .llseek = generic_file_llseek, 31167207b96SArnd Bergmann .mmap = spufs_mem_mmap, 3128b3d6663SArnd Bergmann }; 3138b3d6663SArnd Bergmann 314b1e2270fSNick Piggin static int spufs_ps_fault(struct vm_area_struct *vma, 315b1e2270fSNick Piggin struct vm_fault *vmf, 31678bde53eSBenjamin Herrenschmidt unsigned long ps_offs, 31727d5bf2aSBenjamin Herrenschmidt unsigned long ps_size) 3186df10a82SMark Nutter { 3196df10a82SMark Nutter struct spu_context *ctx = vma->vm_file->private_data; 320b1e2270fSNick Piggin unsigned long area, offset = vmf->pgoff << PAGE_SHIFT; 321eebead5bSChristoph Hellwig int ret = 0; 3226df10a82SMark Nutter 323b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__enter, ctx); 324038200cfSChristoph Hellwig 32527d5bf2aSBenjamin Herrenschmidt if (offset >= ps_size) 326b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 3276df10a82SMark Nutter 32860657263SJeremy Kerr if (fatal_signal_pending(current)) 32960657263SJeremy Kerr return VM_FAULT_SIGBUS; 33060657263SJeremy Kerr 33133bfd7a7SArnd Bergmann /* 332d5883137SJeremy Kerr * Because we release the mmap_sem, the context may be destroyed while 333d5883137SJeremy Kerr * we're in spu_wait. Grab an extra reference so it isn't destroyed 334d5883137SJeremy Kerr * in the meantime. 335d5883137SJeremy Kerr */ 336d5883137SJeremy Kerr get_spu_context(ctx); 337d5883137SJeremy Kerr 338d5883137SJeremy Kerr /* 33933bfd7a7SArnd Bergmann * We have to wait for context to be loaded before we have 34033bfd7a7SArnd Bergmann * pages to hand out to the user, but we don't want to wait 34133bfd7a7SArnd Bergmann * with the mmap_sem held. 34233bfd7a7SArnd Bergmann * It is possible to drop the mmap_sem here, but then we need 343b1e2270fSNick Piggin * to return VM_FAULT_NOPAGE because the mappings may have 34433bfd7a7SArnd Bergmann * hanged. 34578bde53eSBenjamin Herrenschmidt */ 346c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 347d5883137SJeremy Kerr goto refault; 348c9101bdbSChristoph Hellwig 34933bfd7a7SArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 35033bfd7a7SArnd Bergmann up_read(¤t->mm->mmap_sem); 351b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__sleep, ctx); 352eebead5bSChristoph Hellwig ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 353b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu); 35433bfd7a7SArnd Bergmann down_read(¤t->mm->mmap_sem); 355c9101bdbSChristoph Hellwig } else { 3566df10a82SMark Nutter area = ctx->spu->problem_phys + ps_offs; 3571a29d85eSJan Kara vm_insert_pfn(vma, vmf->address, (area + offset) >> PAGE_SHIFT); 358b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu); 359c9101bdbSChristoph Hellwig } 36033bfd7a7SArnd Bergmann 361eebead5bSChristoph Hellwig if (!ret) 3626df10a82SMark Nutter spu_release(ctx); 363d5883137SJeremy Kerr 364d5883137SJeremy Kerr refault: 365d5883137SJeremy Kerr put_spu_context(ctx); 366b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 3676df10a82SMark Nutter } 3686df10a82SMark Nutter 36927d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 370b1e2270fSNick Piggin static int spufs_cntl_mmap_fault(struct vm_area_struct *vma, 371b1e2270fSNick Piggin struct vm_fault *vmf) 3726df10a82SMark Nutter { 37387ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x4000, SPUFS_CNTL_MAP_SIZE); 3746df10a82SMark Nutter } 3756df10a82SMark Nutter 376f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_cntl_mmap_vmops = { 377b1e2270fSNick Piggin .fault = spufs_cntl_mmap_fault, 3786df10a82SMark Nutter }; 3796df10a82SMark Nutter 3806df10a82SMark Nutter /* 3816df10a82SMark Nutter * mmap support for problem state control area [0x4000 - 0x4fff]. 3826df10a82SMark Nutter */ 3836df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) 3846df10a82SMark Nutter { 3856df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 3866df10a82SMark Nutter return -EINVAL; 3876df10a82SMark Nutter 38878bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 38964b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 3906df10a82SMark Nutter 3916df10a82SMark Nutter vma->vm_ops = &spufs_cntl_mmap_vmops; 3926df10a82SMark Nutter return 0; 3936df10a82SMark Nutter } 39427d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 39527d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL 39627d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 3976df10a82SMark Nutter 398197b1a82SChristoph Hellwig static int spufs_cntl_get(void *data, u64 *val) 399e1dbff2bSArnd Bergmann { 400e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 401c9101bdbSChristoph Hellwig int ret; 402e1dbff2bSArnd Bergmann 403c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 404c9101bdbSChristoph Hellwig if (ret) 405c9101bdbSChristoph Hellwig return ret; 406197b1a82SChristoph Hellwig *val = ctx->ops->status_read(ctx); 407e1dbff2bSArnd Bergmann spu_release(ctx); 408e1dbff2bSArnd Bergmann 409197b1a82SChristoph Hellwig return 0; 410e1dbff2bSArnd Bergmann } 411e1dbff2bSArnd Bergmann 412197b1a82SChristoph Hellwig static int spufs_cntl_set(void *data, u64 val) 413e1dbff2bSArnd Bergmann { 414e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 415c9101bdbSChristoph Hellwig int ret; 416e1dbff2bSArnd Bergmann 417c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 418c9101bdbSChristoph Hellwig if (ret) 419c9101bdbSChristoph Hellwig return ret; 420e1dbff2bSArnd Bergmann ctx->ops->runcntl_write(ctx, val); 421e1dbff2bSArnd Bergmann spu_release(ctx); 422197b1a82SChristoph Hellwig 423197b1a82SChristoph Hellwig return 0; 424e1dbff2bSArnd Bergmann } 425e1dbff2bSArnd Bergmann 4266df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file) 4276df10a82SMark Nutter { 4286df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 4296df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 4306df10a82SMark Nutter 43147d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 4326df10a82SMark Nutter file->private_data = ctx; 43343c2bbd9SChristoph Hellwig if (!i->i_openers++) 4346df10a82SMark Nutter ctx->cntl = inode->i_mapping; 43547d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 4368b88b099SChristoph Hellwig return simple_attr_open(inode, file, spufs_cntl_get, 437e1dbff2bSArnd Bergmann spufs_cntl_set, "0x%08lx"); 4386df10a82SMark Nutter } 4396df10a82SMark Nutter 44043c2bbd9SChristoph Hellwig static int 44143c2bbd9SChristoph Hellwig spufs_cntl_release(struct inode *inode, struct file *file) 44243c2bbd9SChristoph Hellwig { 44343c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 44443c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 44543c2bbd9SChristoph Hellwig 44674bedc4dSChristoph Hellwig simple_attr_release(inode, file); 44743c2bbd9SChristoph Hellwig 44847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 44943c2bbd9SChristoph Hellwig if (!--i->i_openers) 45043c2bbd9SChristoph Hellwig ctx->cntl = NULL; 45147d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 45243c2bbd9SChristoph Hellwig return 0; 45343c2bbd9SChristoph Hellwig } 45443c2bbd9SChristoph Hellwig 4555dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = { 4566df10a82SMark Nutter .open = spufs_cntl_open, 45743c2bbd9SChristoph Hellwig .release = spufs_cntl_release, 4588b88b099SChristoph Hellwig .read = simple_attr_read, 4598b88b099SChristoph Hellwig .write = simple_attr_write, 460fc15351dSArnd Bergmann .llseek = generic_file_llseek, 4616df10a82SMark Nutter .mmap = spufs_cntl_mmap, 4626df10a82SMark Nutter }; 4636df10a82SMark Nutter 4648b3d6663SArnd Bergmann static int 4658b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 4668b3d6663SArnd Bergmann { 4678b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 4688b3d6663SArnd Bergmann file->private_data = i->i_ctx; 4698b3d6663SArnd Bergmann return 0; 4708b3d6663SArnd Bergmann } 4718b3d6663SArnd Bergmann 4728b3d6663SArnd Bergmann static ssize_t 473bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer, 474bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 475bf1ab978SDwayne Grant McConnell { 476bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 477bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 478bf1ab978SDwayne Grant McConnell lscsa->gprs, sizeof lscsa->gprs); 479bf1ab978SDwayne Grant McConnell } 480bf1ab978SDwayne Grant McConnell 481bf1ab978SDwayne Grant McConnell static ssize_t 4828b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 4838b3d6663SArnd Bergmann size_t size, loff_t *pos) 4848b3d6663SArnd Bergmann { 4858b3d6663SArnd Bergmann int ret; 486bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 4878b3d6663SArnd Bergmann 488f027faa2SJeremy Kerr /* pre-check for file position: if we'd return EOF, there's no point 489f027faa2SJeremy Kerr * causing a deschedule */ 490f027faa2SJeremy Kerr if (*pos >= sizeof(ctx->csa.lscsa->gprs)) 491f027faa2SJeremy Kerr return 0; 492f027faa2SJeremy Kerr 493c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 494c9101bdbSChristoph Hellwig if (ret) 495c9101bdbSChristoph Hellwig return ret; 496bf1ab978SDwayne Grant McConnell ret = __spufs_regs_read(ctx, buffer, size, pos); 49727b1ea09SChristoph Hellwig spu_release_saved(ctx); 4988b3d6663SArnd Bergmann return ret; 4998b3d6663SArnd Bergmann } 5008b3d6663SArnd Bergmann 5018b3d6663SArnd Bergmann static ssize_t 5028b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 5038b3d6663SArnd Bergmann size_t size, loff_t *pos) 5048b3d6663SArnd Bergmann { 5058b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5068b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5078b3d6663SArnd Bergmann int ret; 5088b3d6663SArnd Bergmann 509d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->gprs)) 5108b3d6663SArnd Bergmann return -EFBIG; 511d219889bSJeremy Kerr 512c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 513c9101bdbSChristoph Hellwig if (ret) 514c9101bdbSChristoph Hellwig return ret; 5158b3d6663SArnd Bergmann 51663c3b9d7SAkinobu Mita size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos, 51763c3b9d7SAkinobu Mita buffer, size); 5188b3d6663SArnd Bergmann 51927b1ea09SChristoph Hellwig spu_release_saved(ctx); 52063c3b9d7SAkinobu Mita return size; 5218b3d6663SArnd Bergmann } 5228b3d6663SArnd Bergmann 5235dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = { 5248b3d6663SArnd Bergmann .open = spufs_regs_open, 5258b3d6663SArnd Bergmann .read = spufs_regs_read, 5268b3d6663SArnd Bergmann .write = spufs_regs_write, 5278b3d6663SArnd Bergmann .llseek = generic_file_llseek, 5288b3d6663SArnd Bergmann }; 5298b3d6663SArnd Bergmann 5308b3d6663SArnd Bergmann static ssize_t 531bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer, 532bf1ab978SDwayne Grant McConnell size_t size, loff_t * pos) 533bf1ab978SDwayne Grant McConnell { 534bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 535bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 536bf1ab978SDwayne Grant McConnell &lscsa->fpcr, sizeof(lscsa->fpcr)); 537bf1ab978SDwayne Grant McConnell } 538bf1ab978SDwayne Grant McConnell 539bf1ab978SDwayne Grant McConnell static ssize_t 5408b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 5418b3d6663SArnd Bergmann size_t size, loff_t * pos) 5428b3d6663SArnd Bergmann { 5438b3d6663SArnd Bergmann int ret; 544bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 5458b3d6663SArnd Bergmann 546c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 547c9101bdbSChristoph Hellwig if (ret) 548c9101bdbSChristoph Hellwig return ret; 549bf1ab978SDwayne Grant McConnell ret = __spufs_fpcr_read(ctx, buffer, size, pos); 55027b1ea09SChristoph Hellwig spu_release_saved(ctx); 5518b3d6663SArnd Bergmann return ret; 5528b3d6663SArnd Bergmann } 5538b3d6663SArnd Bergmann 5548b3d6663SArnd Bergmann static ssize_t 5558b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 5568b3d6663SArnd Bergmann size_t size, loff_t * pos) 5578b3d6663SArnd Bergmann { 5588b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5598b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5608b3d6663SArnd Bergmann int ret; 5618b3d6663SArnd Bergmann 562d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->fpcr)) 5638b3d6663SArnd Bergmann return -EFBIG; 564c9101bdbSChristoph Hellwig 565c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 566c9101bdbSChristoph Hellwig if (ret) 567c9101bdbSChristoph Hellwig return ret; 568c9101bdbSChristoph Hellwig 56963c3b9d7SAkinobu Mita size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos, 57063c3b9d7SAkinobu Mita buffer, size); 5718b3d6663SArnd Bergmann 57227b1ea09SChristoph Hellwig spu_release_saved(ctx); 57363c3b9d7SAkinobu Mita return size; 5748b3d6663SArnd Bergmann } 5758b3d6663SArnd Bergmann 5765dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = { 5778b3d6663SArnd Bergmann .open = spufs_regs_open, 5788b3d6663SArnd Bergmann .read = spufs_fpcr_read, 5798b3d6663SArnd Bergmann .write = spufs_fpcr_write, 58067207b96SArnd Bergmann .llseek = generic_file_llseek, 58167207b96SArnd Bergmann }; 58267207b96SArnd Bergmann 58367207b96SArnd Bergmann /* generic open function for all pipe-like files */ 58467207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 58567207b96SArnd Bergmann { 58667207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 58767207b96SArnd Bergmann file->private_data = i->i_ctx; 58867207b96SArnd Bergmann 58967207b96SArnd Bergmann return nonseekable_open(inode, file); 59067207b96SArnd Bergmann } 59167207b96SArnd Bergmann 592cdcc89bbSArnd Bergmann /* 593cdcc89bbSArnd Bergmann * Read as many bytes from the mailbox as possible, until 594cdcc89bbSArnd Bergmann * one of the conditions becomes true: 595cdcc89bbSArnd Bergmann * 596cdcc89bbSArnd Bergmann * - no more data available in the mailbox 597cdcc89bbSArnd Bergmann * - end of the user provided buffer 598cdcc89bbSArnd Bergmann * - end of the mapped area 599cdcc89bbSArnd Bergmann */ 60067207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 60167207b96SArnd Bergmann size_t len, loff_t *pos) 60267207b96SArnd Bergmann { 6038b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 604cdcc89bbSArnd Bergmann u32 mbox_data, __user *udata; 605cdcc89bbSArnd Bergmann ssize_t count; 60667207b96SArnd Bergmann 60767207b96SArnd Bergmann if (len < 4) 60867207b96SArnd Bergmann return -EINVAL; 60967207b96SArnd Bergmann 610cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 61167207b96SArnd Bergmann return -EFAULT; 61267207b96SArnd Bergmann 613cdcc89bbSArnd Bergmann udata = (void __user *)buf; 614cdcc89bbSArnd Bergmann 615c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 616c9101bdbSChristoph Hellwig if (count) 617c9101bdbSChristoph Hellwig return count; 618c9101bdbSChristoph Hellwig 619274cef5eSArnd Bergmann for (count = 0; (count + 4) <= len; count += 4, udata++) { 620cdcc89bbSArnd Bergmann int ret; 621cdcc89bbSArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 622cdcc89bbSArnd Bergmann if (ret == 0) 623cdcc89bbSArnd Bergmann break; 624cdcc89bbSArnd Bergmann 625cdcc89bbSArnd Bergmann /* 626cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 627cdcc89bbSArnd Bergmann * but still need to return the data we have 628cdcc89bbSArnd Bergmann * read successfully so far. 629cdcc89bbSArnd Bergmann */ 630cdcc89bbSArnd Bergmann ret = __put_user(mbox_data, udata); 631cdcc89bbSArnd Bergmann if (ret) { 632cdcc89bbSArnd Bergmann if (!count) 633cdcc89bbSArnd Bergmann count = -EFAULT; 634cdcc89bbSArnd Bergmann break; 635cdcc89bbSArnd Bergmann } 636cdcc89bbSArnd Bergmann } 637cdcc89bbSArnd Bergmann spu_release(ctx); 638cdcc89bbSArnd Bergmann 639cdcc89bbSArnd Bergmann if (!count) 640cdcc89bbSArnd Bergmann count = -EAGAIN; 641cdcc89bbSArnd Bergmann 642cdcc89bbSArnd Bergmann return count; 64367207b96SArnd Bergmann } 64467207b96SArnd Bergmann 6455dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = { 64667207b96SArnd Bergmann .open = spufs_pipe_open, 64767207b96SArnd Bergmann .read = spufs_mbox_read, 648fc15351dSArnd Bergmann .llseek = no_llseek, 64967207b96SArnd Bergmann }; 65067207b96SArnd Bergmann 65167207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 65267207b96SArnd Bergmann size_t len, loff_t *pos) 65367207b96SArnd Bergmann { 6548b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 655c9101bdbSChristoph Hellwig ssize_t ret; 65667207b96SArnd Bergmann u32 mbox_stat; 65767207b96SArnd Bergmann 65867207b96SArnd Bergmann if (len < 4) 65967207b96SArnd Bergmann return -EINVAL; 66067207b96SArnd Bergmann 661c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 662c9101bdbSChristoph Hellwig if (ret) 663c9101bdbSChristoph Hellwig return ret; 6648b3d6663SArnd Bergmann 6658b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 6668b3d6663SArnd Bergmann 6678b3d6663SArnd Bergmann spu_release(ctx); 66867207b96SArnd Bergmann 66967207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 67067207b96SArnd Bergmann return -EFAULT; 67167207b96SArnd Bergmann 67267207b96SArnd Bergmann return 4; 67367207b96SArnd Bergmann } 67467207b96SArnd Bergmann 6755dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = { 67667207b96SArnd Bergmann .open = spufs_pipe_open, 67767207b96SArnd Bergmann .read = spufs_mbox_stat_read, 678fc15351dSArnd Bergmann .llseek = no_llseek, 67967207b96SArnd Bergmann }; 68067207b96SArnd Bergmann 68167207b96SArnd Bergmann /* low-level ibox access function */ 6828b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 68367207b96SArnd Bergmann { 6848b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 68567207b96SArnd Bergmann } 68667207b96SArnd Bergmann 68767207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on) 68867207b96SArnd Bergmann { 6898b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 6908b3d6663SArnd Bergmann 6918b3d6663SArnd Bergmann return fasync_helper(fd, file, on, &ctx->ibox_fasync); 6928b3d6663SArnd Bergmann } 6938b3d6663SArnd Bergmann 6948b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 6958b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 6968b3d6663SArnd Bergmann { 6978b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 6988b3d6663SArnd Bergmann 699e65c2f6fSLuke Browning if (!ctx) 700e65c2f6fSLuke Browning return; 701e65c2f6fSLuke Browning 7028b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 7038b3d6663SArnd Bergmann kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); 70467207b96SArnd Bergmann } 70567207b96SArnd Bergmann 706cdcc89bbSArnd Bergmann /* 707cdcc89bbSArnd Bergmann * Read as many bytes from the interrupt mailbox as possible, until 708cdcc89bbSArnd Bergmann * one of the conditions becomes true: 709cdcc89bbSArnd Bergmann * 710cdcc89bbSArnd Bergmann * - no more data available in the mailbox 711cdcc89bbSArnd Bergmann * - end of the user provided buffer 712cdcc89bbSArnd Bergmann * - end of the mapped area 713cdcc89bbSArnd Bergmann * 714cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 715cdcc89bbSArnd Bergmann * any data is available, but return when we have been able to 716cdcc89bbSArnd Bergmann * read something. 717cdcc89bbSArnd Bergmann */ 71867207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 71967207b96SArnd Bergmann size_t len, loff_t *pos) 72067207b96SArnd Bergmann { 7218b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 722cdcc89bbSArnd Bergmann u32 ibox_data, __user *udata; 723cdcc89bbSArnd Bergmann ssize_t count; 72467207b96SArnd Bergmann 72567207b96SArnd Bergmann if (len < 4) 72667207b96SArnd Bergmann return -EINVAL; 72767207b96SArnd Bergmann 728cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 729cdcc89bbSArnd Bergmann return -EFAULT; 730cdcc89bbSArnd Bergmann 731cdcc89bbSArnd Bergmann udata = (void __user *)buf; 732cdcc89bbSArnd Bergmann 733c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 734c9101bdbSChristoph Hellwig if (count) 735eebead5bSChristoph Hellwig goto out; 73667207b96SArnd Bergmann 737cdcc89bbSArnd Bergmann /* wait only for the first element */ 738cdcc89bbSArnd Bergmann count = 0; 73967207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 740eebead5bSChristoph Hellwig if (!spu_ibox_read(ctx, &ibox_data)) { 741cdcc89bbSArnd Bergmann count = -EAGAIN; 742eebead5bSChristoph Hellwig goto out_unlock; 743eebead5bSChristoph Hellwig } 74467207b96SArnd Bergmann } else { 745cdcc89bbSArnd Bergmann count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 746cdcc89bbSArnd Bergmann if (count) 747cdcc89bbSArnd Bergmann goto out; 748eebead5bSChristoph Hellwig } 749cdcc89bbSArnd Bergmann 750cdcc89bbSArnd Bergmann /* if we can't write at all, return -EFAULT */ 751cdcc89bbSArnd Bergmann count = __put_user(ibox_data, udata); 752cdcc89bbSArnd Bergmann if (count) 753eebead5bSChristoph Hellwig goto out_unlock; 754cdcc89bbSArnd Bergmann 755cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 756cdcc89bbSArnd Bergmann int ret; 757cdcc89bbSArnd Bergmann ret = ctx->ops->ibox_read(ctx, &ibox_data); 758cdcc89bbSArnd Bergmann if (ret == 0) 759cdcc89bbSArnd Bergmann break; 760cdcc89bbSArnd Bergmann /* 761cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 762cdcc89bbSArnd Bergmann * but still need to return the data we have 763cdcc89bbSArnd Bergmann * read successfully so far. 764cdcc89bbSArnd Bergmann */ 765cdcc89bbSArnd Bergmann ret = __put_user(ibox_data, udata); 766cdcc89bbSArnd Bergmann if (ret) 767cdcc89bbSArnd Bergmann break; 76867207b96SArnd Bergmann } 76967207b96SArnd Bergmann 770eebead5bSChristoph Hellwig out_unlock: 7718b3d6663SArnd Bergmann spu_release(ctx); 772eebead5bSChristoph Hellwig out: 773cdcc89bbSArnd Bergmann return count; 77467207b96SArnd Bergmann } 77567207b96SArnd Bergmann 77667207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) 77767207b96SArnd Bergmann { 7788b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 77967207b96SArnd Bergmann unsigned int mask; 78067207b96SArnd Bergmann 7818b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 78267207b96SArnd Bergmann 783c9101bdbSChristoph Hellwig /* 784c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 785c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 786c9101bdbSChristoph Hellwig */ 787c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 7883a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); 7893a843d7cSArnd Bergmann spu_release(ctx); 79067207b96SArnd Bergmann 79167207b96SArnd Bergmann return mask; 79267207b96SArnd Bergmann } 79367207b96SArnd Bergmann 7945dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = { 79567207b96SArnd Bergmann .open = spufs_pipe_open, 79667207b96SArnd Bergmann .read = spufs_ibox_read, 79767207b96SArnd Bergmann .poll = spufs_ibox_poll, 79867207b96SArnd Bergmann .fasync = spufs_ibox_fasync, 799fc15351dSArnd Bergmann .llseek = no_llseek, 80067207b96SArnd Bergmann }; 80167207b96SArnd Bergmann 80267207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 80367207b96SArnd Bergmann size_t len, loff_t *pos) 80467207b96SArnd Bergmann { 8058b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 806c9101bdbSChristoph Hellwig ssize_t ret; 80767207b96SArnd Bergmann u32 ibox_stat; 80867207b96SArnd Bergmann 80967207b96SArnd Bergmann if (len < 4) 81067207b96SArnd Bergmann return -EINVAL; 81167207b96SArnd Bergmann 812c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 813c9101bdbSChristoph Hellwig if (ret) 814c9101bdbSChristoph Hellwig return ret; 8158b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 8168b3d6663SArnd Bergmann spu_release(ctx); 81767207b96SArnd Bergmann 81867207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 81967207b96SArnd Bergmann return -EFAULT; 82067207b96SArnd Bergmann 82167207b96SArnd Bergmann return 4; 82267207b96SArnd Bergmann } 82367207b96SArnd Bergmann 8245dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = { 82567207b96SArnd Bergmann .open = spufs_pipe_open, 82667207b96SArnd Bergmann .read = spufs_ibox_stat_read, 827fc15351dSArnd Bergmann .llseek = no_llseek, 82867207b96SArnd Bergmann }; 82967207b96SArnd Bergmann 83067207b96SArnd Bergmann /* low-level mailbox write */ 8318b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 83267207b96SArnd Bergmann { 8338b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 83467207b96SArnd Bergmann } 83567207b96SArnd Bergmann 83667207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on) 83767207b96SArnd Bergmann { 8388b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 8398b3d6663SArnd Bergmann int ret; 8408b3d6663SArnd Bergmann 8418b3d6663SArnd Bergmann ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); 8428b3d6663SArnd Bergmann 8438b3d6663SArnd Bergmann return ret; 8448b3d6663SArnd Bergmann } 8458b3d6663SArnd Bergmann 8468b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 8478b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 8488b3d6663SArnd Bergmann { 8498b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 8508b3d6663SArnd Bergmann 851e65c2f6fSLuke Browning if (!ctx) 852e65c2f6fSLuke Browning return; 853e65c2f6fSLuke Browning 8548b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 8558b3d6663SArnd Bergmann kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); 85667207b96SArnd Bergmann } 85767207b96SArnd Bergmann 858cdcc89bbSArnd Bergmann /* 859cdcc89bbSArnd Bergmann * Write as many bytes to the interrupt mailbox as possible, until 860cdcc89bbSArnd Bergmann * one of the conditions becomes true: 861cdcc89bbSArnd Bergmann * 862cdcc89bbSArnd Bergmann * - the mailbox is full 863cdcc89bbSArnd Bergmann * - end of the user provided buffer 864cdcc89bbSArnd Bergmann * - end of the mapped area 865cdcc89bbSArnd Bergmann * 866cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 867027dfac6SMichael Ellerman * space is available, but return when we have been able to 868cdcc89bbSArnd Bergmann * write something. 869cdcc89bbSArnd Bergmann */ 87067207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 87167207b96SArnd Bergmann size_t len, loff_t *pos) 87267207b96SArnd Bergmann { 8738b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 874cdcc89bbSArnd Bergmann u32 wbox_data, __user *udata; 875cdcc89bbSArnd Bergmann ssize_t count; 87667207b96SArnd Bergmann 87767207b96SArnd Bergmann if (len < 4) 87867207b96SArnd Bergmann return -EINVAL; 87967207b96SArnd Bergmann 880cdcc89bbSArnd Bergmann udata = (void __user *)buf; 881cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_READ, buf, len)) 882cdcc89bbSArnd Bergmann return -EFAULT; 883cdcc89bbSArnd Bergmann 884cdcc89bbSArnd Bergmann if (__get_user(wbox_data, udata)) 88567207b96SArnd Bergmann return -EFAULT; 88667207b96SArnd Bergmann 887c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 888c9101bdbSChristoph Hellwig if (count) 889eebead5bSChristoph Hellwig goto out; 8908b3d6663SArnd Bergmann 891cdcc89bbSArnd Bergmann /* 892cdcc89bbSArnd Bergmann * make sure we can at least write one element, by waiting 893cdcc89bbSArnd Bergmann * in case of !O_NONBLOCK 894cdcc89bbSArnd Bergmann */ 895cdcc89bbSArnd Bergmann count = 0; 89667207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 897eebead5bSChristoph Hellwig if (!spu_wbox_write(ctx, wbox_data)) { 898cdcc89bbSArnd Bergmann count = -EAGAIN; 899eebead5bSChristoph Hellwig goto out_unlock; 900eebead5bSChristoph Hellwig } 90167207b96SArnd Bergmann } else { 902cdcc89bbSArnd Bergmann count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 903cdcc89bbSArnd Bergmann if (count) 904cdcc89bbSArnd Bergmann goto out; 905eebead5bSChristoph Hellwig } 906eebead5bSChristoph Hellwig 9078b3d6663SArnd Bergmann 90896de0e25SJan Engelhardt /* write as much as possible */ 909cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 910cdcc89bbSArnd Bergmann int ret; 911cdcc89bbSArnd Bergmann ret = __get_user(wbox_data, udata); 912cdcc89bbSArnd Bergmann if (ret) 913cdcc89bbSArnd Bergmann break; 914cdcc89bbSArnd Bergmann 915cdcc89bbSArnd Bergmann ret = spu_wbox_write(ctx, wbox_data); 916cdcc89bbSArnd Bergmann if (ret == 0) 917cdcc89bbSArnd Bergmann break; 918cdcc89bbSArnd Bergmann } 919cdcc89bbSArnd Bergmann 920eebead5bSChristoph Hellwig out_unlock: 921cdcc89bbSArnd Bergmann spu_release(ctx); 922eebead5bSChristoph Hellwig out: 923cdcc89bbSArnd Bergmann return count; 92467207b96SArnd Bergmann } 92567207b96SArnd Bergmann 92667207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) 92767207b96SArnd Bergmann { 9288b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 92967207b96SArnd Bergmann unsigned int mask; 93067207b96SArnd Bergmann 9318b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 93267207b96SArnd Bergmann 933c9101bdbSChristoph Hellwig /* 934c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 935c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 936c9101bdbSChristoph Hellwig */ 937c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 9383a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); 9393a843d7cSArnd Bergmann spu_release(ctx); 94067207b96SArnd Bergmann 94167207b96SArnd Bergmann return mask; 94267207b96SArnd Bergmann } 94367207b96SArnd Bergmann 9445dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = { 94567207b96SArnd Bergmann .open = spufs_pipe_open, 94667207b96SArnd Bergmann .write = spufs_wbox_write, 94767207b96SArnd Bergmann .poll = spufs_wbox_poll, 94867207b96SArnd Bergmann .fasync = spufs_wbox_fasync, 949fc15351dSArnd Bergmann .llseek = no_llseek, 95067207b96SArnd Bergmann }; 95167207b96SArnd Bergmann 95267207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 95367207b96SArnd Bergmann size_t len, loff_t *pos) 95467207b96SArnd Bergmann { 9558b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 956c9101bdbSChristoph Hellwig ssize_t ret; 95767207b96SArnd Bergmann u32 wbox_stat; 95867207b96SArnd Bergmann 95967207b96SArnd Bergmann if (len < 4) 96067207b96SArnd Bergmann return -EINVAL; 96167207b96SArnd Bergmann 962c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 963c9101bdbSChristoph Hellwig if (ret) 964c9101bdbSChristoph Hellwig return ret; 9658b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 9668b3d6663SArnd Bergmann spu_release(ctx); 96767207b96SArnd Bergmann 96867207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 96967207b96SArnd Bergmann return -EFAULT; 97067207b96SArnd Bergmann 97167207b96SArnd Bergmann return 4; 97267207b96SArnd Bergmann } 97367207b96SArnd Bergmann 9745dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = { 97567207b96SArnd Bergmann .open = spufs_pipe_open, 97667207b96SArnd Bergmann .read = spufs_wbox_stat_read, 977fc15351dSArnd Bergmann .llseek = no_llseek, 97867207b96SArnd Bergmann }; 97967207b96SArnd Bergmann 9806df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file) 9816df10a82SMark Nutter { 9826df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 9836df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 98443c2bbd9SChristoph Hellwig 98547d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 9866df10a82SMark Nutter file->private_data = ctx; 98743c2bbd9SChristoph Hellwig if (!i->i_openers++) 9886df10a82SMark Nutter ctx->signal1 = inode->i_mapping; 98947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 9906df10a82SMark Nutter return nonseekable_open(inode, file); 9916df10a82SMark Nutter } 9926df10a82SMark Nutter 99343c2bbd9SChristoph Hellwig static int 99443c2bbd9SChristoph Hellwig spufs_signal1_release(struct inode *inode, struct file *file) 99543c2bbd9SChristoph Hellwig { 99643c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 99743c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 99843c2bbd9SChristoph Hellwig 99947d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 100043c2bbd9SChristoph Hellwig if (!--i->i_openers) 100143c2bbd9SChristoph Hellwig ctx->signal1 = NULL; 100247d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 100343c2bbd9SChristoph Hellwig return 0; 100443c2bbd9SChristoph Hellwig } 100543c2bbd9SChristoph Hellwig 1006bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf, 100767207b96SArnd Bergmann size_t len, loff_t *pos) 100867207b96SArnd Bergmann { 100917f88cebSDwayne Grant McConnell int ret = 0; 101067207b96SArnd Bergmann u32 data; 101167207b96SArnd Bergmann 101267207b96SArnd Bergmann if (len < 4) 101367207b96SArnd Bergmann return -EINVAL; 101467207b96SArnd Bergmann 101517f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[3]) { 101617f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[3]; 101717f88cebSDwayne Grant McConnell ret = 4; 101817f88cebSDwayne Grant McConnell } 10198b3d6663SArnd Bergmann 102017f88cebSDwayne Grant McConnell if (!ret) 102117f88cebSDwayne Grant McConnell goto out; 102217f88cebSDwayne Grant McConnell 102367207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 102467207b96SArnd Bergmann return -EFAULT; 102567207b96SArnd Bergmann 102617f88cebSDwayne Grant McConnell out: 102717f88cebSDwayne Grant McConnell return ret; 102867207b96SArnd Bergmann } 102967207b96SArnd Bergmann 1030bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 1031bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1032bf1ab978SDwayne Grant McConnell { 1033bf1ab978SDwayne Grant McConnell int ret; 1034bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1035bf1ab978SDwayne Grant McConnell 1036c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1037c9101bdbSChristoph Hellwig if (ret) 1038c9101bdbSChristoph Hellwig return ret; 1039bf1ab978SDwayne Grant McConnell ret = __spufs_signal1_read(ctx, buf, len, pos); 104027b1ea09SChristoph Hellwig spu_release_saved(ctx); 1041bf1ab978SDwayne Grant McConnell 1042bf1ab978SDwayne Grant McConnell return ret; 1043bf1ab978SDwayne Grant McConnell } 1044bf1ab978SDwayne Grant McConnell 104567207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 104667207b96SArnd Bergmann size_t len, loff_t *pos) 104767207b96SArnd Bergmann { 104867207b96SArnd Bergmann struct spu_context *ctx; 1049c9101bdbSChristoph Hellwig ssize_t ret; 105067207b96SArnd Bergmann u32 data; 105167207b96SArnd Bergmann 105267207b96SArnd Bergmann ctx = file->private_data; 105367207b96SArnd Bergmann 105467207b96SArnd Bergmann if (len < 4) 105567207b96SArnd Bergmann return -EINVAL; 105667207b96SArnd Bergmann 105767207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 105867207b96SArnd Bergmann return -EFAULT; 105967207b96SArnd Bergmann 1060c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1061c9101bdbSChristoph Hellwig if (ret) 1062c9101bdbSChristoph Hellwig return ret; 10638b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 10648b3d6663SArnd Bergmann spu_release(ctx); 106567207b96SArnd Bergmann 106667207b96SArnd Bergmann return 4; 106767207b96SArnd Bergmann } 106867207b96SArnd Bergmann 1069b1e2270fSNick Piggin static int 1070b1e2270fSNick Piggin spufs_signal1_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 10716df10a82SMark Nutter { 107287ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 107387ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE); 107487ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 107527d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 107627d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 107727d5bf2aSBenjamin Herrenschmidt */ 107887ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 107927d5bf2aSBenjamin Herrenschmidt #else 108027d5bf2aSBenjamin Herrenschmidt #error unsupported page size 108127d5bf2aSBenjamin Herrenschmidt #endif 10826df10a82SMark Nutter } 10836df10a82SMark Nutter 1084f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal1_mmap_vmops = { 1085b1e2270fSNick Piggin .fault = spufs_signal1_mmap_fault, 10866df10a82SMark Nutter }; 10876df10a82SMark Nutter 10886df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) 10896df10a82SMark Nutter { 10906df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 10916df10a82SMark Nutter return -EINVAL; 10926df10a82SMark Nutter 109378bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 109464b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 10956df10a82SMark Nutter 10966df10a82SMark Nutter vma->vm_ops = &spufs_signal1_mmap_vmops; 10976df10a82SMark Nutter return 0; 10986df10a82SMark Nutter } 10996df10a82SMark Nutter 11005dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = { 11016df10a82SMark Nutter .open = spufs_signal1_open, 110243c2bbd9SChristoph Hellwig .release = spufs_signal1_release, 110367207b96SArnd Bergmann .read = spufs_signal1_read, 110467207b96SArnd Bergmann .write = spufs_signal1_write, 11056df10a82SMark Nutter .mmap = spufs_signal1_mmap, 1106fc15351dSArnd Bergmann .llseek = no_llseek, 110767207b96SArnd Bergmann }; 110867207b96SArnd Bergmann 1109d054b36fSJeremy Kerr static const struct file_operations spufs_signal1_nosched_fops = { 1110d054b36fSJeremy Kerr .open = spufs_signal1_open, 1111d054b36fSJeremy Kerr .release = spufs_signal1_release, 1112d054b36fSJeremy Kerr .write = spufs_signal1_write, 1113d054b36fSJeremy Kerr .mmap = spufs_signal1_mmap, 1114fc15351dSArnd Bergmann .llseek = no_llseek, 1115d054b36fSJeremy Kerr }; 1116d054b36fSJeremy Kerr 11176df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file) 11186df10a82SMark Nutter { 11196df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 11206df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 112143c2bbd9SChristoph Hellwig 112247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 11236df10a82SMark Nutter file->private_data = ctx; 112443c2bbd9SChristoph Hellwig if (!i->i_openers++) 11256df10a82SMark Nutter ctx->signal2 = inode->i_mapping; 112647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 11276df10a82SMark Nutter return nonseekable_open(inode, file); 11286df10a82SMark Nutter } 11296df10a82SMark Nutter 113043c2bbd9SChristoph Hellwig static int 113143c2bbd9SChristoph Hellwig spufs_signal2_release(struct inode *inode, struct file *file) 113243c2bbd9SChristoph Hellwig { 113343c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 113443c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 113543c2bbd9SChristoph Hellwig 113647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 113743c2bbd9SChristoph Hellwig if (!--i->i_openers) 113843c2bbd9SChristoph Hellwig ctx->signal2 = NULL; 113947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 114043c2bbd9SChristoph Hellwig return 0; 114143c2bbd9SChristoph Hellwig } 114243c2bbd9SChristoph Hellwig 1143bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf, 114467207b96SArnd Bergmann size_t len, loff_t *pos) 114567207b96SArnd Bergmann { 114617f88cebSDwayne Grant McConnell int ret = 0; 114767207b96SArnd Bergmann u32 data; 114867207b96SArnd Bergmann 114967207b96SArnd Bergmann if (len < 4) 115067207b96SArnd Bergmann return -EINVAL; 115167207b96SArnd Bergmann 115217f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[4]) { 115317f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[4]; 115417f88cebSDwayne Grant McConnell ret = 4; 115517f88cebSDwayne Grant McConnell } 11568b3d6663SArnd Bergmann 115717f88cebSDwayne Grant McConnell if (!ret) 115817f88cebSDwayne Grant McConnell goto out; 115917f88cebSDwayne Grant McConnell 116067207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 116167207b96SArnd Bergmann return -EFAULT; 116267207b96SArnd Bergmann 116317f88cebSDwayne Grant McConnell out: 1164bf1ab978SDwayne Grant McConnell return ret; 1165bf1ab978SDwayne Grant McConnell } 1166bf1ab978SDwayne Grant McConnell 1167bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 1168bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1169bf1ab978SDwayne Grant McConnell { 1170bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1171bf1ab978SDwayne Grant McConnell int ret; 1172bf1ab978SDwayne Grant McConnell 1173c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1174c9101bdbSChristoph Hellwig if (ret) 1175c9101bdbSChristoph Hellwig return ret; 1176bf1ab978SDwayne Grant McConnell ret = __spufs_signal2_read(ctx, buf, len, pos); 117727b1ea09SChristoph Hellwig spu_release_saved(ctx); 1178bf1ab978SDwayne Grant McConnell 1179bf1ab978SDwayne Grant McConnell return ret; 118067207b96SArnd Bergmann } 118167207b96SArnd Bergmann 118267207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 118367207b96SArnd Bergmann size_t len, loff_t *pos) 118467207b96SArnd Bergmann { 118567207b96SArnd Bergmann struct spu_context *ctx; 1186c9101bdbSChristoph Hellwig ssize_t ret; 118767207b96SArnd Bergmann u32 data; 118867207b96SArnd Bergmann 118967207b96SArnd Bergmann ctx = file->private_data; 119067207b96SArnd Bergmann 119167207b96SArnd Bergmann if (len < 4) 119267207b96SArnd Bergmann return -EINVAL; 119367207b96SArnd Bergmann 119467207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 119567207b96SArnd Bergmann return -EFAULT; 119667207b96SArnd Bergmann 1197c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1198c9101bdbSChristoph Hellwig if (ret) 1199c9101bdbSChristoph Hellwig return ret; 12008b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 12018b3d6663SArnd Bergmann spu_release(ctx); 120267207b96SArnd Bergmann 120367207b96SArnd Bergmann return 4; 120467207b96SArnd Bergmann } 120567207b96SArnd Bergmann 120627d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1207b1e2270fSNick Piggin static int 1208b1e2270fSNick Piggin spufs_signal2_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 12096df10a82SMark Nutter { 121087ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 121187ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE); 121287ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 121327d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 121427d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 121527d5bf2aSBenjamin Herrenschmidt */ 121687ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 121727d5bf2aSBenjamin Herrenschmidt #else 121827d5bf2aSBenjamin Herrenschmidt #error unsupported page size 121927d5bf2aSBenjamin Herrenschmidt #endif 12206df10a82SMark Nutter } 12216df10a82SMark Nutter 1222f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal2_mmap_vmops = { 1223b1e2270fSNick Piggin .fault = spufs_signal2_mmap_fault, 12246df10a82SMark Nutter }; 12256df10a82SMark Nutter 12266df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) 12276df10a82SMark Nutter { 12286df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 12296df10a82SMark Nutter return -EINVAL; 12306df10a82SMark Nutter 123178bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 123264b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 12336df10a82SMark Nutter 12346df10a82SMark Nutter vma->vm_ops = &spufs_signal2_mmap_vmops; 12356df10a82SMark Nutter return 0; 12366df10a82SMark Nutter } 123727d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 123827d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL 123927d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 12406df10a82SMark Nutter 12415dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = { 12426df10a82SMark Nutter .open = spufs_signal2_open, 124343c2bbd9SChristoph Hellwig .release = spufs_signal2_release, 124467207b96SArnd Bergmann .read = spufs_signal2_read, 124567207b96SArnd Bergmann .write = spufs_signal2_write, 12466df10a82SMark Nutter .mmap = spufs_signal2_mmap, 1247fc15351dSArnd Bergmann .llseek = no_llseek, 124867207b96SArnd Bergmann }; 124967207b96SArnd Bergmann 1250d054b36fSJeremy Kerr static const struct file_operations spufs_signal2_nosched_fops = { 1251d054b36fSJeremy Kerr .open = spufs_signal2_open, 1252d054b36fSJeremy Kerr .release = spufs_signal2_release, 1253d054b36fSJeremy Kerr .write = spufs_signal2_write, 1254d054b36fSJeremy Kerr .mmap = spufs_signal2_mmap, 1255fc15351dSArnd Bergmann .llseek = no_llseek, 1256d054b36fSJeremy Kerr }; 1257d054b36fSJeremy Kerr 1258104f0cc2SMichael Ellerman /* 1259104f0cc2SMichael Ellerman * This is a wrapper around DEFINE_SIMPLE_ATTRIBUTE which does the 1260104f0cc2SMichael Ellerman * work of acquiring (or not) the SPU context before calling through 1261104f0cc2SMichael Ellerman * to the actual get routine. The set routine is called directly. 1262104f0cc2SMichael Ellerman */ 1263104f0cc2SMichael Ellerman #define SPU_ATTR_NOACQUIRE 0 1264104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE 1 1265104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE_SAVED 2 1266104f0cc2SMichael Ellerman 1267104f0cc2SMichael Ellerman #define DEFINE_SPUFS_ATTRIBUTE(__name, __get, __set, __fmt, __acquire) \ 1268197b1a82SChristoph Hellwig static int __##__get(void *data, u64 *val) \ 1269104f0cc2SMichael Ellerman { \ 1270104f0cc2SMichael Ellerman struct spu_context *ctx = data; \ 1271c9101bdbSChristoph Hellwig int ret = 0; \ 1272104f0cc2SMichael Ellerman \ 1273104f0cc2SMichael Ellerman if (__acquire == SPU_ATTR_ACQUIRE) { \ 1274c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); \ 1275c9101bdbSChristoph Hellwig if (ret) \ 1276c9101bdbSChristoph Hellwig return ret; \ 1277197b1a82SChristoph Hellwig *val = __get(ctx); \ 1278104f0cc2SMichael Ellerman spu_release(ctx); \ 1279104f0cc2SMichael Ellerman } else if (__acquire == SPU_ATTR_ACQUIRE_SAVED) { \ 1280c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); \ 1281c9101bdbSChristoph Hellwig if (ret) \ 1282c9101bdbSChristoph Hellwig return ret; \ 1283197b1a82SChristoph Hellwig *val = __get(ctx); \ 1284104f0cc2SMichael Ellerman spu_release_saved(ctx); \ 1285104f0cc2SMichael Ellerman } else \ 1286197b1a82SChristoph Hellwig *val = __get(ctx); \ 1287104f0cc2SMichael Ellerman \ 1288197b1a82SChristoph Hellwig return 0; \ 1289104f0cc2SMichael Ellerman } \ 1290197b1a82SChristoph Hellwig DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__name, __##__get, __set, __fmt); 1291104f0cc2SMichael Ellerman 1292197b1a82SChristoph Hellwig static int spufs_signal1_type_set(void *data, u64 val) 129367207b96SArnd Bergmann { 129467207b96SArnd Bergmann struct spu_context *ctx = data; 1295c9101bdbSChristoph Hellwig int ret; 129667207b96SArnd Bergmann 1297c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1298c9101bdbSChristoph Hellwig if (ret) 1299c9101bdbSChristoph Hellwig return ret; 13008b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 13018b3d6663SArnd Bergmann spu_release(ctx); 1302197b1a82SChristoph Hellwig 1303197b1a82SChristoph Hellwig return 0; 130467207b96SArnd Bergmann } 130567207b96SArnd Bergmann 1306104f0cc2SMichael Ellerman static u64 spufs_signal1_type_get(struct spu_context *ctx) 1307bf1ab978SDwayne Grant McConnell { 1308bf1ab978SDwayne Grant McConnell return ctx->ops->signal1_type_get(ctx); 1309bf1ab978SDwayne Grant McConnell } 1310104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 1311af8b44e0SJeremy Kerr spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 1312bf1ab978SDwayne Grant McConnell 131367207b96SArnd Bergmann 1314197b1a82SChristoph Hellwig static int spufs_signal2_type_set(void *data, u64 val) 131567207b96SArnd Bergmann { 131667207b96SArnd Bergmann struct spu_context *ctx = data; 1317c9101bdbSChristoph Hellwig int ret; 131867207b96SArnd Bergmann 1319c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1320c9101bdbSChristoph Hellwig if (ret) 1321c9101bdbSChristoph Hellwig return ret; 13228b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 13238b3d6663SArnd Bergmann spu_release(ctx); 1324197b1a82SChristoph Hellwig 1325197b1a82SChristoph Hellwig return 0; 132667207b96SArnd Bergmann } 132767207b96SArnd Bergmann 1328104f0cc2SMichael Ellerman static u64 spufs_signal2_type_get(struct spu_context *ctx) 1329bf1ab978SDwayne Grant McConnell { 1330bf1ab978SDwayne Grant McConnell return ctx->ops->signal2_type_get(ctx); 1331bf1ab978SDwayne Grant McConnell } 1332104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 1333af8b44e0SJeremy Kerr spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 133467207b96SArnd Bergmann 133527d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1336b1e2270fSNick Piggin static int 1337b1e2270fSNick Piggin spufs_mss_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 1338d9379c4bSarnd@arndb.de { 133987ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_MSS_MAP_SIZE); 1340d9379c4bSarnd@arndb.de } 1341d9379c4bSarnd@arndb.de 1342f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mss_mmap_vmops = { 1343b1e2270fSNick Piggin .fault = spufs_mss_mmap_fault, 1344d9379c4bSarnd@arndb.de }; 1345d9379c4bSarnd@arndb.de 1346d9379c4bSarnd@arndb.de /* 1347d9379c4bSarnd@arndb.de * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 1348d9379c4bSarnd@arndb.de */ 1349d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) 1350d9379c4bSarnd@arndb.de { 1351d9379c4bSarnd@arndb.de if (!(vma->vm_flags & VM_SHARED)) 1352d9379c4bSarnd@arndb.de return -EINVAL; 1353d9379c4bSarnd@arndb.de 135478bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 135564b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 1356d9379c4bSarnd@arndb.de 1357d9379c4bSarnd@arndb.de vma->vm_ops = &spufs_mss_mmap_vmops; 1358d9379c4bSarnd@arndb.de return 0; 1359d9379c4bSarnd@arndb.de } 136027d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 136127d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL 136227d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1363d9379c4bSarnd@arndb.de 1364d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file) 1365d9379c4bSarnd@arndb.de { 1366d9379c4bSarnd@arndb.de struct spufs_inode_info *i = SPUFS_I(inode); 136717e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 1368d9379c4bSarnd@arndb.de 1369d9379c4bSarnd@arndb.de file->private_data = i->i_ctx; 137043c2bbd9SChristoph Hellwig 137147d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 137243c2bbd9SChristoph Hellwig if (!i->i_openers++) 137317e0e270SBenjamin Herrenschmidt ctx->mss = inode->i_mapping; 137447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1375d9379c4bSarnd@arndb.de return nonseekable_open(inode, file); 1376d9379c4bSarnd@arndb.de } 1377d9379c4bSarnd@arndb.de 137843c2bbd9SChristoph Hellwig static int 137943c2bbd9SChristoph Hellwig spufs_mss_release(struct inode *inode, struct file *file) 138043c2bbd9SChristoph Hellwig { 138143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 138243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 138343c2bbd9SChristoph Hellwig 138447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 138543c2bbd9SChristoph Hellwig if (!--i->i_openers) 138643c2bbd9SChristoph Hellwig ctx->mss = NULL; 138747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 138843c2bbd9SChristoph Hellwig return 0; 138943c2bbd9SChristoph Hellwig } 139043c2bbd9SChristoph Hellwig 13915dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = { 1392d9379c4bSarnd@arndb.de .open = spufs_mss_open, 139343c2bbd9SChristoph Hellwig .release = spufs_mss_release, 1394d9379c4bSarnd@arndb.de .mmap = spufs_mss_mmap, 1395fc15351dSArnd Bergmann .llseek = no_llseek, 139627d5bf2aSBenjamin Herrenschmidt }; 139727d5bf2aSBenjamin Herrenschmidt 1398b1e2270fSNick Piggin static int 1399b1e2270fSNick Piggin spufs_psmap_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 140027d5bf2aSBenjamin Herrenschmidt { 140187ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_PS_MAP_SIZE); 140227d5bf2aSBenjamin Herrenschmidt } 140327d5bf2aSBenjamin Herrenschmidt 1404f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_psmap_mmap_vmops = { 1405b1e2270fSNick Piggin .fault = spufs_psmap_mmap_fault, 140627d5bf2aSBenjamin Herrenschmidt }; 140727d5bf2aSBenjamin Herrenschmidt 140827d5bf2aSBenjamin Herrenschmidt /* 140927d5bf2aSBenjamin Herrenschmidt * mmap support for full problem state area [0x00000 - 0x1ffff]. 141027d5bf2aSBenjamin Herrenschmidt */ 141127d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) 141227d5bf2aSBenjamin Herrenschmidt { 141327d5bf2aSBenjamin Herrenschmidt if (!(vma->vm_flags & VM_SHARED)) 141427d5bf2aSBenjamin Herrenschmidt return -EINVAL; 141527d5bf2aSBenjamin Herrenschmidt 141678bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 141764b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 141827d5bf2aSBenjamin Herrenschmidt 141927d5bf2aSBenjamin Herrenschmidt vma->vm_ops = &spufs_psmap_mmap_vmops; 142027d5bf2aSBenjamin Herrenschmidt return 0; 142127d5bf2aSBenjamin Herrenschmidt } 142227d5bf2aSBenjamin Herrenschmidt 142327d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file) 142427d5bf2aSBenjamin Herrenschmidt { 142527d5bf2aSBenjamin Herrenschmidt struct spufs_inode_info *i = SPUFS_I(inode); 142617e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 142727d5bf2aSBenjamin Herrenschmidt 142847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 142927d5bf2aSBenjamin Herrenschmidt file->private_data = i->i_ctx; 143043c2bbd9SChristoph Hellwig if (!i->i_openers++) 143117e0e270SBenjamin Herrenschmidt ctx->psmap = inode->i_mapping; 143247d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 143327d5bf2aSBenjamin Herrenschmidt return nonseekable_open(inode, file); 143427d5bf2aSBenjamin Herrenschmidt } 143527d5bf2aSBenjamin Herrenschmidt 143643c2bbd9SChristoph Hellwig static int 143743c2bbd9SChristoph Hellwig spufs_psmap_release(struct inode *inode, struct file *file) 143843c2bbd9SChristoph Hellwig { 143943c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 144043c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 144143c2bbd9SChristoph Hellwig 144247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 144343c2bbd9SChristoph Hellwig if (!--i->i_openers) 144443c2bbd9SChristoph Hellwig ctx->psmap = NULL; 144547d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 144643c2bbd9SChristoph Hellwig return 0; 144743c2bbd9SChristoph Hellwig } 144843c2bbd9SChristoph Hellwig 14495dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = { 145027d5bf2aSBenjamin Herrenschmidt .open = spufs_psmap_open, 145143c2bbd9SChristoph Hellwig .release = spufs_psmap_release, 145227d5bf2aSBenjamin Herrenschmidt .mmap = spufs_psmap_mmap, 1453fc15351dSArnd Bergmann .llseek = no_llseek, 1454d9379c4bSarnd@arndb.de }; 1455d9379c4bSarnd@arndb.de 1456d9379c4bSarnd@arndb.de 145727d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1458b1e2270fSNick Piggin static int 1459b1e2270fSNick Piggin spufs_mfc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 14606df10a82SMark Nutter { 146187ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x3000, SPUFS_MFC_MAP_SIZE); 14626df10a82SMark Nutter } 14636df10a82SMark Nutter 1464f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mfc_mmap_vmops = { 1465b1e2270fSNick Piggin .fault = spufs_mfc_mmap_fault, 14666df10a82SMark Nutter }; 14676df10a82SMark Nutter 14686df10a82SMark Nutter /* 14696df10a82SMark Nutter * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 14706df10a82SMark Nutter */ 14716df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) 14726df10a82SMark Nutter { 14736df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 14746df10a82SMark Nutter return -EINVAL; 14756df10a82SMark Nutter 147678bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 147764b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 14786df10a82SMark Nutter 14796df10a82SMark Nutter vma->vm_ops = &spufs_mfc_mmap_vmops; 14806df10a82SMark Nutter return 0; 14816df10a82SMark Nutter } 148227d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 148327d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL 148427d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1485a33a7d73SArnd Bergmann 1486a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 1487a33a7d73SArnd Bergmann { 1488a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1489a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 1490a33a7d73SArnd Bergmann 1491a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 1492a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 1493a33a7d73SArnd Bergmann return -EINVAL; 1494a33a7d73SArnd Bergmann 1495a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 1496a33a7d73SArnd Bergmann return -EBUSY; 1497a33a7d73SArnd Bergmann 149847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1499a33a7d73SArnd Bergmann file->private_data = ctx; 150043c2bbd9SChristoph Hellwig if (!i->i_openers++) 150117e0e270SBenjamin Herrenschmidt ctx->mfc = inode->i_mapping; 150247d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1503a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 1504a33a7d73SArnd Bergmann } 1505a33a7d73SArnd Bergmann 150643c2bbd9SChristoph Hellwig static int 150743c2bbd9SChristoph Hellwig spufs_mfc_release(struct inode *inode, struct file *file) 150843c2bbd9SChristoph Hellwig { 150943c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 151043c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 151143c2bbd9SChristoph Hellwig 151247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 151343c2bbd9SChristoph Hellwig if (!--i->i_openers) 151443c2bbd9SChristoph Hellwig ctx->mfc = NULL; 151547d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 151643c2bbd9SChristoph Hellwig return 0; 151743c2bbd9SChristoph Hellwig } 151843c2bbd9SChristoph Hellwig 1519a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 1520a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 1521a33a7d73SArnd Bergmann { 1522a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 1523a33a7d73SArnd Bergmann 1524e65c2f6fSLuke Browning if (!ctx) 1525e65c2f6fSLuke Browning return; 1526e65c2f6fSLuke Browning 1527a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 1528a33a7d73SArnd Bergmann 1529e48b1b45SHarvey Harrison pr_debug("%s %s\n", __func__, spu->name); 1530a33a7d73SArnd Bergmann if (ctx->mfc_fasync) { 1531a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1532a33a7d73SArnd Bergmann unsigned int mask; 1533a33a7d73SArnd Bergmann 1534a33a7d73SArnd Bergmann /* no need for spu_acquire in interrupt context */ 1535a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1536a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1537a33a7d73SArnd Bergmann 1538a33a7d73SArnd Bergmann mask = 0; 1539a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1540a33a7d73SArnd Bergmann mask |= POLLOUT; 1541a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1542a33a7d73SArnd Bergmann mask |= POLLIN; 1543a33a7d73SArnd Bergmann 1544a33a7d73SArnd Bergmann kill_fasync(&ctx->mfc_fasync, SIGIO, mask); 1545a33a7d73SArnd Bergmann } 1546a33a7d73SArnd Bergmann } 1547a33a7d73SArnd Bergmann 1548a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 1549a33a7d73SArnd Bergmann { 1550a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 1551a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 1552a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 1553a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 1554a33a7d73SArnd Bergmann if (*status) 1555a33a7d73SArnd Bergmann return 1; 1556a33a7d73SArnd Bergmann 1557a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 1558a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 1559a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1560a33a7d73SArnd Bergmann return 0; 1561a33a7d73SArnd Bergmann } 1562a33a7d73SArnd Bergmann 1563a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 1564a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1565a33a7d73SArnd Bergmann { 1566a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1567a33a7d73SArnd Bergmann int ret = -EINVAL; 1568a33a7d73SArnd Bergmann u32 status; 1569a33a7d73SArnd Bergmann 1570a33a7d73SArnd Bergmann if (size != 4) 1571a33a7d73SArnd Bergmann goto out; 1572a33a7d73SArnd Bergmann 1573c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1574c9101bdbSChristoph Hellwig if (ret) 1575c9101bdbSChristoph Hellwig return ret; 1576c9101bdbSChristoph Hellwig 1577c9101bdbSChristoph Hellwig ret = -EINVAL; 1578a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1579a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 1580a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 1581a33a7d73SArnd Bergmann ret = -EAGAIN; 1582a33a7d73SArnd Bergmann else 1583c9101bdbSChristoph Hellwig /* XXX(hch): shouldn't we clear ret here? */ 1584a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 1585a33a7d73SArnd Bergmann } else { 1586a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1587a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 1588a33a7d73SArnd Bergmann if (ret) 1589a33a7d73SArnd Bergmann goto out; 1590eebead5bSChristoph Hellwig } 1591eebead5bSChristoph Hellwig spu_release(ctx); 1592a33a7d73SArnd Bergmann 1593a33a7d73SArnd Bergmann ret = 4; 1594a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 1595a33a7d73SArnd Bergmann ret = -EFAULT; 1596a33a7d73SArnd Bergmann 1597a33a7d73SArnd Bergmann out: 1598a33a7d73SArnd Bergmann return ret; 1599a33a7d73SArnd Bergmann } 1600a33a7d73SArnd Bergmann 1601a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 1602a33a7d73SArnd Bergmann { 16039477e455SStephen Rothwell pr_debug("queueing DMA %x %llx %x %x %x\n", cmd->lsa, 1604a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 1605a33a7d73SArnd Bergmann 1606a33a7d73SArnd Bergmann switch (cmd->cmd) { 1607a33a7d73SArnd Bergmann case MFC_PUT_CMD: 1608a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 1609a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 1610a33a7d73SArnd Bergmann case MFC_GET_CMD: 1611a33a7d73SArnd Bergmann case MFC_GETF_CMD: 1612a33a7d73SArnd Bergmann case MFC_GETB_CMD: 1613a33a7d73SArnd Bergmann break; 1614a33a7d73SArnd Bergmann default: 1615a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 1616a33a7d73SArnd Bergmann return -EIO; 1617a33a7d73SArnd Bergmann } 1618a33a7d73SArnd Bergmann 1619a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 16209477e455SStephen Rothwell pr_debug("invalid DMA alignment, ea %llx lsa %x\n", 1621a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 1622a33a7d73SArnd Bergmann return -EIO; 1623a33a7d73SArnd Bergmann } 1624a33a7d73SArnd Bergmann 1625a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 1626a33a7d73SArnd Bergmann case 1: 1627a33a7d73SArnd Bergmann break; 1628a33a7d73SArnd Bergmann case 2: 1629a33a7d73SArnd Bergmann if (cmd->lsa & 1) 1630a33a7d73SArnd Bergmann goto error; 1631a33a7d73SArnd Bergmann break; 1632a33a7d73SArnd Bergmann case 4: 1633a33a7d73SArnd Bergmann if (cmd->lsa & 3) 1634a33a7d73SArnd Bergmann goto error; 1635a33a7d73SArnd Bergmann break; 1636a33a7d73SArnd Bergmann case 8: 1637a33a7d73SArnd Bergmann if (cmd->lsa & 7) 1638a33a7d73SArnd Bergmann goto error; 1639a33a7d73SArnd Bergmann break; 1640a33a7d73SArnd Bergmann case 0: 1641a33a7d73SArnd Bergmann if (cmd->lsa & 15) 1642a33a7d73SArnd Bergmann goto error; 1643a33a7d73SArnd Bergmann break; 1644a33a7d73SArnd Bergmann error: 1645a33a7d73SArnd Bergmann default: 1646a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 1647a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 1648a33a7d73SArnd Bergmann return -EIO; 1649a33a7d73SArnd Bergmann } 1650a33a7d73SArnd Bergmann 1651a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 1652a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 1653a33a7d73SArnd Bergmann return -EIO; 1654a33a7d73SArnd Bergmann } 1655a33a7d73SArnd Bergmann 1656a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 1657a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 1658a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 1659a33a7d73SArnd Bergmann return -EIO; 1660a33a7d73SArnd Bergmann } 1661a33a7d73SArnd Bergmann 1662a33a7d73SArnd Bergmann if (cmd->class) { 1663a33a7d73SArnd Bergmann /* not supported in this version */ 1664a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 1665a33a7d73SArnd Bergmann return -EIO; 1666a33a7d73SArnd Bergmann } 1667a33a7d73SArnd Bergmann 1668a33a7d73SArnd Bergmann return 0; 1669a33a7d73SArnd Bergmann } 1670a33a7d73SArnd Bergmann 1671a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 1672a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 1673a33a7d73SArnd Bergmann int *error) 1674a33a7d73SArnd Bergmann { 1675a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1676a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 1677a33a7d73SArnd Bergmann /* wait for any tag group to complete 1678a33a7d73SArnd Bergmann so we have space for the new command */ 1679a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1680a33a7d73SArnd Bergmann /* try again, because the queue might be 1681a33a7d73SArnd Bergmann empty again */ 1682a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1683a33a7d73SArnd Bergmann if (*error == -EAGAIN) 1684a33a7d73SArnd Bergmann return 0; 1685a33a7d73SArnd Bergmann } 1686a33a7d73SArnd Bergmann return 1; 1687a33a7d73SArnd Bergmann } 1688a33a7d73SArnd Bergmann 1689a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 1690a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1691a33a7d73SArnd Bergmann { 1692a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1693a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 1694a33a7d73SArnd Bergmann int ret = -EINVAL; 1695a33a7d73SArnd Bergmann 1696a33a7d73SArnd Bergmann if (size != sizeof cmd) 1697a33a7d73SArnd Bergmann goto out; 1698a33a7d73SArnd Bergmann 1699a33a7d73SArnd Bergmann ret = -EFAULT; 1700a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 1701a33a7d73SArnd Bergmann goto out; 1702a33a7d73SArnd Bergmann 1703a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 1704a33a7d73SArnd Bergmann if (ret) 1705a33a7d73SArnd Bergmann goto out; 1706a33a7d73SArnd Bergmann 1707c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1708c9101bdbSChristoph Hellwig if (ret) 1709c9101bdbSChristoph Hellwig goto out; 1710c9101bdbSChristoph Hellwig 171133bfd7a7SArnd Bergmann ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 1712577f8f10SAkinobu Mita if (ret) 1713577f8f10SAkinobu Mita goto out; 1714577f8f10SAkinobu Mita 1715a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1716a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 1717a33a7d73SArnd Bergmann } else { 1718a33a7d73SArnd Bergmann int status; 1719a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1720a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 1721eebead5bSChristoph Hellwig if (ret) 1722eebead5bSChristoph Hellwig goto out; 1723a33a7d73SArnd Bergmann if (status) 1724a33a7d73SArnd Bergmann ret = status; 1725a33a7d73SArnd Bergmann } 1726a33a7d73SArnd Bergmann 1727a33a7d73SArnd Bergmann if (ret) 1728933b0e35SKazunori Asayama goto out_unlock; 1729a33a7d73SArnd Bergmann 1730a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 17313692dc66SMasato Noguchi ret = size; 1732a33a7d73SArnd Bergmann 1733933b0e35SKazunori Asayama out_unlock: 1734933b0e35SKazunori Asayama spu_release(ctx); 1735a33a7d73SArnd Bergmann out: 1736a33a7d73SArnd Bergmann return ret; 1737a33a7d73SArnd Bergmann } 1738a33a7d73SArnd Bergmann 1739a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) 1740a33a7d73SArnd Bergmann { 1741a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1742a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1743a33a7d73SArnd Bergmann unsigned int mask; 1744a33a7d73SArnd Bergmann 1745933b0e35SKazunori Asayama poll_wait(file, &ctx->mfc_wq, wait); 1746933b0e35SKazunori Asayama 1747c9101bdbSChristoph Hellwig /* 1748c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 1749c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 1750c9101bdbSChristoph Hellwig */ 1751c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 1752a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 1753a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1754a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1755a33a7d73SArnd Bergmann spu_release(ctx); 1756a33a7d73SArnd Bergmann 1757a33a7d73SArnd Bergmann mask = 0; 1758a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1759a33a7d73SArnd Bergmann mask |= POLLOUT | POLLWRNORM; 1760a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1761a33a7d73SArnd Bergmann mask |= POLLIN | POLLRDNORM; 1762a33a7d73SArnd Bergmann 1763e48b1b45SHarvey Harrison pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__, 1764a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 1765a33a7d73SArnd Bergmann 1766a33a7d73SArnd Bergmann return mask; 1767a33a7d73SArnd Bergmann } 1768a33a7d73SArnd Bergmann 176973b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id) 1770a33a7d73SArnd Bergmann { 1771a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1772a33a7d73SArnd Bergmann int ret; 1773a33a7d73SArnd Bergmann 1774c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1775c9101bdbSChristoph Hellwig if (ret) 1776eebead5bSChristoph Hellwig goto out; 1777a33a7d73SArnd Bergmann #if 0 1778a33a7d73SArnd Bergmann /* this currently hangs */ 1779a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1780a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 1781a33a7d73SArnd Bergmann if (ret) 1782a33a7d73SArnd Bergmann goto out; 1783a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1784a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 1785eebead5bSChristoph Hellwig if (ret) 1786eebead5bSChristoph Hellwig goto out; 1787a33a7d73SArnd Bergmann #else 1788a33a7d73SArnd Bergmann ret = 0; 1789a33a7d73SArnd Bergmann #endif 1790a33a7d73SArnd Bergmann spu_release(ctx); 1791eebead5bSChristoph Hellwig out: 1792a33a7d73SArnd Bergmann return ret; 1793a33a7d73SArnd Bergmann } 1794a33a7d73SArnd Bergmann 179502c24a82SJosef Bacik static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync) 1796a33a7d73SArnd Bergmann { 1797496ad9aaSAl Viro struct inode *inode = file_inode(file); 179802c24a82SJosef Bacik int err = filemap_write_and_wait_range(inode->i_mapping, start, end); 179902c24a82SJosef Bacik if (!err) { 18005955102cSAl Viro inode_lock(inode); 180102c24a82SJosef Bacik err = spufs_mfc_flush(file, NULL); 18025955102cSAl Viro inode_unlock(inode); 180302c24a82SJosef Bacik } 180402c24a82SJosef Bacik return err; 1805a33a7d73SArnd Bergmann } 1806a33a7d73SArnd Bergmann 1807a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on) 1808a33a7d73SArnd Bergmann { 1809a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1810a33a7d73SArnd Bergmann 1811a33a7d73SArnd Bergmann return fasync_helper(fd, file, on, &ctx->mfc_fasync); 1812a33a7d73SArnd Bergmann } 1813a33a7d73SArnd Bergmann 18145dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = { 1815a33a7d73SArnd Bergmann .open = spufs_mfc_open, 181643c2bbd9SChristoph Hellwig .release = spufs_mfc_release, 1817a33a7d73SArnd Bergmann .read = spufs_mfc_read, 1818a33a7d73SArnd Bergmann .write = spufs_mfc_write, 1819a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 1820a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 1821a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 1822a33a7d73SArnd Bergmann .fasync = spufs_mfc_fasync, 18236df10a82SMark Nutter .mmap = spufs_mfc_mmap, 1824fc15351dSArnd Bergmann .llseek = no_llseek, 1825a33a7d73SArnd Bergmann }; 1826a33a7d73SArnd Bergmann 1827197b1a82SChristoph Hellwig static int spufs_npc_set(void *data, u64 val) 182867207b96SArnd Bergmann { 182967207b96SArnd Bergmann struct spu_context *ctx = data; 1830c9101bdbSChristoph Hellwig int ret; 1831c9101bdbSChristoph Hellwig 1832c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1833c9101bdbSChristoph Hellwig if (ret) 1834c9101bdbSChristoph Hellwig return ret; 18358b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 18368b3d6663SArnd Bergmann spu_release(ctx); 1837197b1a82SChristoph Hellwig 1838197b1a82SChristoph Hellwig return 0; 183967207b96SArnd Bergmann } 184067207b96SArnd Bergmann 1841104f0cc2SMichael Ellerman static u64 spufs_npc_get(struct spu_context *ctx) 184278810ff6SMichael Ellerman { 184378810ff6SMichael Ellerman return ctx->ops->npc_read(ctx); 184478810ff6SMichael Ellerman } 1845104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, 1846104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE); 184767207b96SArnd Bergmann 1848197b1a82SChristoph Hellwig static int spufs_decr_set(void *data, u64 val) 18498b3d6663SArnd Bergmann { 18508b3d6663SArnd Bergmann struct spu_context *ctx = data; 18518b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1852c9101bdbSChristoph Hellwig int ret; 1853c9101bdbSChristoph Hellwig 1854c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1855c9101bdbSChristoph Hellwig if (ret) 1856c9101bdbSChristoph Hellwig return ret; 18578b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 185827b1ea09SChristoph Hellwig spu_release_saved(ctx); 1859197b1a82SChristoph Hellwig 1860197b1a82SChristoph Hellwig return 0; 18618b3d6663SArnd Bergmann } 18628b3d6663SArnd Bergmann 1863104f0cc2SMichael Ellerman static u64 spufs_decr_get(struct spu_context *ctx) 18648b3d6663SArnd Bergmann { 18658b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1866bf1ab978SDwayne Grant McConnell return lscsa->decr.slot[0]; 1867bf1ab978SDwayne Grant McConnell } 1868104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 1869104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED); 18708b3d6663SArnd Bergmann 1871197b1a82SChristoph Hellwig static int spufs_decr_status_set(void *data, u64 val) 18728b3d6663SArnd Bergmann { 18738b3d6663SArnd Bergmann struct spu_context *ctx = data; 1874c9101bdbSChristoph Hellwig int ret; 1875c9101bdbSChristoph Hellwig 1876c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1877c9101bdbSChristoph Hellwig if (ret) 1878c9101bdbSChristoph Hellwig return ret; 1879d40a01d4SMasato Noguchi if (val) 1880d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING; 1881d40a01d4SMasato Noguchi else 1882d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW &= ~MFC_CNTL_DECREMENTER_RUNNING; 188327b1ea09SChristoph Hellwig spu_release_saved(ctx); 1884197b1a82SChristoph Hellwig 1885197b1a82SChristoph Hellwig return 0; 18868b3d6663SArnd Bergmann } 18878b3d6663SArnd Bergmann 1888104f0cc2SMichael Ellerman static u64 spufs_decr_status_get(struct spu_context *ctx) 18898b3d6663SArnd Bergmann { 1890d40a01d4SMasato Noguchi if (ctx->csa.priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) 1891d40a01d4SMasato Noguchi return SPU_DECR_STATUS_RUNNING; 1892d40a01d4SMasato Noguchi else 1893d40a01d4SMasato Noguchi return 0; 1894bf1ab978SDwayne Grant McConnell } 1895104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 1896104f0cc2SMichael Ellerman spufs_decr_status_set, "0x%llx\n", 1897104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 18988b3d6663SArnd Bergmann 1899197b1a82SChristoph Hellwig static int spufs_event_mask_set(void *data, u64 val) 19008b3d6663SArnd Bergmann { 19018b3d6663SArnd Bergmann struct spu_context *ctx = data; 19028b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1903c9101bdbSChristoph Hellwig int ret; 1904c9101bdbSChristoph Hellwig 1905c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1906c9101bdbSChristoph Hellwig if (ret) 1907c9101bdbSChristoph Hellwig return ret; 19088b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 190927b1ea09SChristoph Hellwig spu_release_saved(ctx); 1910197b1a82SChristoph Hellwig 1911197b1a82SChristoph Hellwig return 0; 19128b3d6663SArnd Bergmann } 19138b3d6663SArnd Bergmann 1914104f0cc2SMichael Ellerman static u64 spufs_event_mask_get(struct spu_context *ctx) 19158b3d6663SArnd Bergmann { 19168b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1917bf1ab978SDwayne Grant McConnell return lscsa->event_mask.slot[0]; 1918bf1ab978SDwayne Grant McConnell } 1919bf1ab978SDwayne Grant McConnell 1920104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 1921104f0cc2SMichael Ellerman spufs_event_mask_set, "0x%llx\n", 1922104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 19238b3d6663SArnd Bergmann 1924104f0cc2SMichael Ellerman static u64 spufs_event_status_get(struct spu_context *ctx) 1925b9e3bd77SDwayne Grant McConnell { 1926b9e3bd77SDwayne Grant McConnell struct spu_state *state = &ctx->csa; 1927b9e3bd77SDwayne Grant McConnell u64 stat; 1928b9e3bd77SDwayne Grant McConnell stat = state->spu_chnlcnt_RW[0]; 1929b9e3bd77SDwayne Grant McConnell if (stat) 1930bf1ab978SDwayne Grant McConnell return state->spu_chnldata_RW[0]; 1931bf1ab978SDwayne Grant McConnell return 0; 1932bf1ab978SDwayne Grant McConnell } 1933104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get, 1934104f0cc2SMichael Ellerman NULL, "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 1935b9e3bd77SDwayne Grant McConnell 1936197b1a82SChristoph Hellwig static int spufs_srr0_set(void *data, u64 val) 19378b3d6663SArnd Bergmann { 19388b3d6663SArnd Bergmann struct spu_context *ctx = data; 19398b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1940c9101bdbSChristoph Hellwig int ret; 1941c9101bdbSChristoph Hellwig 1942c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1943c9101bdbSChristoph Hellwig if (ret) 1944c9101bdbSChristoph Hellwig return ret; 19458b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 194627b1ea09SChristoph Hellwig spu_release_saved(ctx); 1947197b1a82SChristoph Hellwig 1948197b1a82SChristoph Hellwig return 0; 19498b3d6663SArnd Bergmann } 19508b3d6663SArnd Bergmann 1951104f0cc2SMichael Ellerman static u64 spufs_srr0_get(struct spu_context *ctx) 19528b3d6663SArnd Bergmann { 19538b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1954104f0cc2SMichael Ellerman return lscsa->srr0.slot[0]; 19558b3d6663SArnd Bergmann } 1956104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 1957104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 19588b3d6663SArnd Bergmann 1959104f0cc2SMichael Ellerman static u64 spufs_id_get(struct spu_context *ctx) 19607b1a7014Sarnd@arndb.de { 19617b1a7014Sarnd@arndb.de u64 num; 19627b1a7014Sarnd@arndb.de 19637b1a7014Sarnd@arndb.de if (ctx->state == SPU_STATE_RUNNABLE) 19647b1a7014Sarnd@arndb.de num = ctx->spu->number; 19657b1a7014Sarnd@arndb.de else 19667b1a7014Sarnd@arndb.de num = (unsigned int)-1; 19677b1a7014Sarnd@arndb.de 19687b1a7014Sarnd@arndb.de return num; 19697b1a7014Sarnd@arndb.de } 1970104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n", 1971104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE) 19727b1a7014Sarnd@arndb.de 1973104f0cc2SMichael Ellerman static u64 spufs_object_id_get(struct spu_context *ctx) 1974bf1ab978SDwayne Grant McConnell { 1975bf1ab978SDwayne Grant McConnell /* FIXME: Should there really be no locking here? */ 1976104f0cc2SMichael Ellerman return ctx->object_id; 1977bf1ab978SDwayne Grant McConnell } 1978bf1ab978SDwayne Grant McConnell 1979197b1a82SChristoph Hellwig static int spufs_object_id_set(void *data, u64 id) 198086767277SArnd Bergmann { 198186767277SArnd Bergmann struct spu_context *ctx = data; 198286767277SArnd Bergmann ctx->object_id = id; 1983197b1a82SChristoph Hellwig 1984197b1a82SChristoph Hellwig return 0; 198586767277SArnd Bergmann } 198686767277SArnd Bergmann 1987104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, 1988104f0cc2SMichael Ellerman spufs_object_id_set, "0x%llx\n", SPU_ATTR_NOACQUIRE); 198986767277SArnd Bergmann 1990104f0cc2SMichael Ellerman static u64 spufs_lslr_get(struct spu_context *ctx) 1991bf1ab978SDwayne Grant McConnell { 1992bf1ab978SDwayne Grant McConnell return ctx->csa.priv2.spu_lslr_RW; 1993bf1ab978SDwayne Grant McConnell } 1994104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n", 1995104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 1996b9e3bd77SDwayne Grant McConnell 1997b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file) 1998b9e3bd77SDwayne Grant McConnell { 1999b9e3bd77SDwayne Grant McConnell struct spufs_inode_info *i = SPUFS_I(inode); 2000b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = i->i_ctx; 2001b9e3bd77SDwayne Grant McConnell file->private_data = ctx; 2002b9e3bd77SDwayne Grant McConnell return 0; 2003b9e3bd77SDwayne Grant McConnell } 2004b9e3bd77SDwayne Grant McConnell 2005cbe709c1SBenjamin Herrenschmidt static int spufs_caps_show(struct seq_file *s, void *private) 2006cbe709c1SBenjamin Herrenschmidt { 2007cbe709c1SBenjamin Herrenschmidt struct spu_context *ctx = s->private; 2008cbe709c1SBenjamin Herrenschmidt 2009cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_NOSCHED)) 2010cbe709c1SBenjamin Herrenschmidt seq_puts(s, "sched\n"); 2011cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_ISOLATE)) 2012cbe709c1SBenjamin Herrenschmidt seq_puts(s, "step\n"); 2013cbe709c1SBenjamin Herrenschmidt return 0; 2014cbe709c1SBenjamin Herrenschmidt } 2015cbe709c1SBenjamin Herrenschmidt 2016cbe709c1SBenjamin Herrenschmidt static int spufs_caps_open(struct inode *inode, struct file *file) 2017cbe709c1SBenjamin Herrenschmidt { 2018cbe709c1SBenjamin Herrenschmidt return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); 2019cbe709c1SBenjamin Herrenschmidt } 2020cbe709c1SBenjamin Herrenschmidt 2021cbe709c1SBenjamin Herrenschmidt static const struct file_operations spufs_caps_fops = { 2022cbe709c1SBenjamin Herrenschmidt .open = spufs_caps_open, 2023cbe709c1SBenjamin Herrenschmidt .read = seq_read, 2024cbe709c1SBenjamin Herrenschmidt .llseek = seq_lseek, 2025cbe709c1SBenjamin Herrenschmidt .release = single_release, 2026cbe709c1SBenjamin Herrenschmidt }; 2027cbe709c1SBenjamin Herrenschmidt 2028bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, 2029bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2030bf1ab978SDwayne Grant McConnell { 2031bf1ab978SDwayne Grant McConnell u32 data; 2032bf1ab978SDwayne Grant McConnell 2033cbea9238SJeremy Kerr /* EOF if there's no entry in the mbox */ 2034cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0x0000ff)) 2035cbea9238SJeremy Kerr return 0; 2036cbea9238SJeremy Kerr 2037bf1ab978SDwayne Grant McConnell data = ctx->csa.prob.pu_mb_R; 2038bf1ab978SDwayne Grant McConnell 2039bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2040bf1ab978SDwayne Grant McConnell } 2041bf1ab978SDwayne Grant McConnell 204269a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf, 204369a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 204469a2f00cSDwayne Grant McConnell { 2045bf1ab978SDwayne Grant McConnell int ret; 204669a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 204769a2f00cSDwayne Grant McConnell 204869a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 204969a2f00cSDwayne Grant McConnell return -EFAULT; 205069a2f00cSDwayne Grant McConnell 2051c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2052c9101bdbSChristoph Hellwig if (ret) 2053c9101bdbSChristoph Hellwig return ret; 205469a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2055bf1ab978SDwayne Grant McConnell ret = __spufs_mbox_info_read(ctx, buf, len, pos); 205669a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 205727b1ea09SChristoph Hellwig spu_release_saved(ctx); 205869a2f00cSDwayne Grant McConnell 2059bf1ab978SDwayne Grant McConnell return ret; 206069a2f00cSDwayne Grant McConnell } 206169a2f00cSDwayne Grant McConnell 20625dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = { 206369a2f00cSDwayne Grant McConnell .open = spufs_info_open, 206469a2f00cSDwayne Grant McConnell .read = spufs_mbox_info_read, 206569a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 206669a2f00cSDwayne Grant McConnell }; 206769a2f00cSDwayne Grant McConnell 2068bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx, 2069bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2070bf1ab978SDwayne Grant McConnell { 2071bf1ab978SDwayne Grant McConnell u32 data; 2072bf1ab978SDwayne Grant McConnell 2073cbea9238SJeremy Kerr /* EOF if there's no entry in the ibox */ 2074cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0xff0000)) 2075cbea9238SJeremy Kerr return 0; 2076cbea9238SJeremy Kerr 2077bf1ab978SDwayne Grant McConnell data = ctx->csa.priv2.puint_mb_R; 2078bf1ab978SDwayne Grant McConnell 2079bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2080bf1ab978SDwayne Grant McConnell } 2081bf1ab978SDwayne Grant McConnell 208269a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf, 208369a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 208469a2f00cSDwayne Grant McConnell { 208569a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2086bf1ab978SDwayne Grant McConnell int ret; 208769a2f00cSDwayne Grant McConnell 208869a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 208969a2f00cSDwayne Grant McConnell return -EFAULT; 209069a2f00cSDwayne Grant McConnell 2091c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2092c9101bdbSChristoph Hellwig if (ret) 2093c9101bdbSChristoph Hellwig return ret; 209469a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2095bf1ab978SDwayne Grant McConnell ret = __spufs_ibox_info_read(ctx, buf, len, pos); 209669a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 209727b1ea09SChristoph Hellwig spu_release_saved(ctx); 209869a2f00cSDwayne Grant McConnell 2099bf1ab978SDwayne Grant McConnell return ret; 210069a2f00cSDwayne Grant McConnell } 210169a2f00cSDwayne Grant McConnell 21025dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = { 210369a2f00cSDwayne Grant McConnell .open = spufs_info_open, 210469a2f00cSDwayne Grant McConnell .read = spufs_ibox_info_read, 210569a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 210669a2f00cSDwayne Grant McConnell }; 210769a2f00cSDwayne Grant McConnell 2108bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx, 2109bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2110bf1ab978SDwayne Grant McConnell { 2111bf1ab978SDwayne Grant McConnell int i, cnt; 2112bf1ab978SDwayne Grant McConnell u32 data[4]; 2113bf1ab978SDwayne Grant McConnell u32 wbox_stat; 2114bf1ab978SDwayne Grant McConnell 2115bf1ab978SDwayne Grant McConnell wbox_stat = ctx->csa.prob.mb_stat_R; 2116bf1ab978SDwayne Grant McConnell cnt = 4 - ((wbox_stat & 0x00ff00) >> 8); 2117bf1ab978SDwayne Grant McConnell for (i = 0; i < cnt; i++) { 2118bf1ab978SDwayne Grant McConnell data[i] = ctx->csa.spu_mailbox_data[i]; 2119bf1ab978SDwayne Grant McConnell } 2120bf1ab978SDwayne Grant McConnell 2121bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, 2122bf1ab978SDwayne Grant McConnell cnt * sizeof(u32)); 2123bf1ab978SDwayne Grant McConnell } 2124bf1ab978SDwayne Grant McConnell 212569a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf, 212669a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 212769a2f00cSDwayne Grant McConnell { 212869a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2129bf1ab978SDwayne Grant McConnell int ret; 213069a2f00cSDwayne Grant McConnell 213169a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 213269a2f00cSDwayne Grant McConnell return -EFAULT; 213369a2f00cSDwayne Grant McConnell 2134c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2135c9101bdbSChristoph Hellwig if (ret) 2136c9101bdbSChristoph Hellwig return ret; 213769a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2138bf1ab978SDwayne Grant McConnell ret = __spufs_wbox_info_read(ctx, buf, len, pos); 213969a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 214027b1ea09SChristoph Hellwig spu_release_saved(ctx); 214169a2f00cSDwayne Grant McConnell 2142bf1ab978SDwayne Grant McConnell return ret; 214369a2f00cSDwayne Grant McConnell } 214469a2f00cSDwayne Grant McConnell 21455dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = { 214669a2f00cSDwayne Grant McConnell .open = spufs_info_open, 214769a2f00cSDwayne Grant McConnell .read = spufs_wbox_info_read, 214869a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 214969a2f00cSDwayne Grant McConnell }; 215069a2f00cSDwayne Grant McConnell 2151bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx, 2152bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2153b9e3bd77SDwayne Grant McConnell { 2154b9e3bd77SDwayne Grant McConnell struct spu_dma_info info; 2155b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *spuqp; 2156b9e3bd77SDwayne Grant McConnell int i; 2157b9e3bd77SDwayne Grant McConnell 2158b9e3bd77SDwayne Grant McConnell info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW; 2159b9e3bd77SDwayne Grant McConnell info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0]; 2160b9e3bd77SDwayne Grant McConnell info.dma_info_status = ctx->csa.spu_chnldata_RW[24]; 2161b9e3bd77SDwayne Grant McConnell info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25]; 2162b9e3bd77SDwayne Grant McConnell info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27]; 2163b9e3bd77SDwayne Grant McConnell for (i = 0; i < 16; i++) { 2164b9e3bd77SDwayne Grant McConnell qp = &info.dma_info_command_data[i]; 2165b9e3bd77SDwayne Grant McConnell spuqp = &ctx->csa.priv2.spuq[i]; 2166b9e3bd77SDwayne Grant McConnell 2167b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW; 2168b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW; 2169b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW; 2170b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW; 2171b9e3bd77SDwayne Grant McConnell } 2172b9e3bd77SDwayne Grant McConnell 2173b9e3bd77SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2174b9e3bd77SDwayne Grant McConnell sizeof info); 2175b9e3bd77SDwayne Grant McConnell } 2176b9e3bd77SDwayne Grant McConnell 2177bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, 2178bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2179bf1ab978SDwayne Grant McConnell { 2180bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2181bf1ab978SDwayne Grant McConnell int ret; 2182bf1ab978SDwayne Grant McConnell 2183bf1ab978SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2184bf1ab978SDwayne Grant McConnell return -EFAULT; 2185bf1ab978SDwayne Grant McConnell 2186c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2187c9101bdbSChristoph Hellwig if (ret) 2188c9101bdbSChristoph Hellwig return ret; 2189bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2190bf1ab978SDwayne Grant McConnell ret = __spufs_dma_info_read(ctx, buf, len, pos); 2191bf1ab978SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 219227b1ea09SChristoph Hellwig spu_release_saved(ctx); 2193bf1ab978SDwayne Grant McConnell 2194bf1ab978SDwayne Grant McConnell return ret; 2195bf1ab978SDwayne Grant McConnell } 2196bf1ab978SDwayne Grant McConnell 21975dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = { 2198b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2199b9e3bd77SDwayne Grant McConnell .read = spufs_dma_info_read, 2200fc15351dSArnd Bergmann .llseek = no_llseek, 2201b9e3bd77SDwayne Grant McConnell }; 2202b9e3bd77SDwayne Grant McConnell 2203bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, 2204bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2205b9e3bd77SDwayne Grant McConnell { 2206b9e3bd77SDwayne Grant McConnell struct spu_proxydma_info info; 2207b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *puqp; 2208bf1ab978SDwayne Grant McConnell int ret = sizeof info; 2209b9e3bd77SDwayne Grant McConnell int i; 2210b9e3bd77SDwayne Grant McConnell 2211b9e3bd77SDwayne Grant McConnell if (len < ret) 2212b9e3bd77SDwayne Grant McConnell return -EINVAL; 2213b9e3bd77SDwayne Grant McConnell 2214b9e3bd77SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2215b9e3bd77SDwayne Grant McConnell return -EFAULT; 2216b9e3bd77SDwayne Grant McConnell 2217b9e3bd77SDwayne Grant McConnell info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW; 2218b9e3bd77SDwayne Grant McConnell info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW; 2219b9e3bd77SDwayne Grant McConnell info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R; 2220b9e3bd77SDwayne Grant McConnell for (i = 0; i < 8; i++) { 2221b9e3bd77SDwayne Grant McConnell qp = &info.proxydma_info_command_data[i]; 2222b9e3bd77SDwayne Grant McConnell puqp = &ctx->csa.priv2.puq[i]; 2223b9e3bd77SDwayne Grant McConnell 2224b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW; 2225b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW; 2226b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW; 2227b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW; 2228b9e3bd77SDwayne Grant McConnell } 2229bf1ab978SDwayne Grant McConnell 2230bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2231bf1ab978SDwayne Grant McConnell sizeof info); 2232bf1ab978SDwayne Grant McConnell } 2233bf1ab978SDwayne Grant McConnell 2234bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, 2235bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2236bf1ab978SDwayne Grant McConnell { 2237bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2238bf1ab978SDwayne Grant McConnell int ret; 2239bf1ab978SDwayne Grant McConnell 2240c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2241c9101bdbSChristoph Hellwig if (ret) 2242c9101bdbSChristoph Hellwig return ret; 2243bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2244bf1ab978SDwayne Grant McConnell ret = __spufs_proxydma_info_read(ctx, buf, len, pos); 2245b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 224627b1ea09SChristoph Hellwig spu_release_saved(ctx); 2247b9e3bd77SDwayne Grant McConnell 2248b9e3bd77SDwayne Grant McConnell return ret; 2249b9e3bd77SDwayne Grant McConnell } 2250b9e3bd77SDwayne Grant McConnell 22515dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = { 2252b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2253b9e3bd77SDwayne Grant McConnell .read = spufs_proxydma_info_read, 2254fc15351dSArnd Bergmann .llseek = no_llseek, 2255b9e3bd77SDwayne Grant McConnell }; 2256b9e3bd77SDwayne Grant McConnell 2257476273adSChristoph Hellwig static int spufs_show_tid(struct seq_file *s, void *private) 2258476273adSChristoph Hellwig { 2259476273adSChristoph Hellwig struct spu_context *ctx = s->private; 2260476273adSChristoph Hellwig 2261476273adSChristoph Hellwig seq_printf(s, "%d\n", ctx->tid); 2262476273adSChristoph Hellwig return 0; 2263476273adSChristoph Hellwig } 2264476273adSChristoph Hellwig 2265476273adSChristoph Hellwig static int spufs_tid_open(struct inode *inode, struct file *file) 2266476273adSChristoph Hellwig { 2267476273adSChristoph Hellwig return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); 2268476273adSChristoph Hellwig } 2269476273adSChristoph Hellwig 2270476273adSChristoph Hellwig static const struct file_operations spufs_tid_fops = { 2271476273adSChristoph Hellwig .open = spufs_tid_open, 2272476273adSChristoph Hellwig .read = seq_read, 2273476273adSChristoph Hellwig .llseek = seq_lseek, 2274476273adSChristoph Hellwig .release = single_release, 2275476273adSChristoph Hellwig }; 2276476273adSChristoph Hellwig 2277e9f8a0b6SChristoph Hellwig static const char *ctx_state_names[] = { 2278e9f8a0b6SChristoph Hellwig "user", "system", "iowait", "loaded" 2279e9f8a0b6SChristoph Hellwig }; 2280e9f8a0b6SChristoph Hellwig 2281e9f8a0b6SChristoph Hellwig static unsigned long long spufs_acct_time(struct spu_context *ctx, 228227ec41d3SAndre Detsch enum spu_utilization_state state) 2283e9f8a0b6SChristoph Hellwig { 228427ec41d3SAndre Detsch unsigned long long time = ctx->stats.times[state]; 2285e9f8a0b6SChristoph Hellwig 228627ec41d3SAndre Detsch /* 228727ec41d3SAndre Detsch * In general, utilization statistics are updated by the controlling 228827ec41d3SAndre Detsch * thread as the spu context moves through various well defined 228927ec41d3SAndre Detsch * state transitions, but if the context is lazily loaded its 229027ec41d3SAndre Detsch * utilization statistics are not updated as the controlling thread 229127ec41d3SAndre Detsch * is not tightly coupled with the execution of the spu context. We 229227ec41d3SAndre Detsch * calculate and apply the time delta from the last recorded state 229327ec41d3SAndre Detsch * of the spu context. 229427ec41d3SAndre Detsch */ 229527ec41d3SAndre Detsch if (ctx->spu && ctx->stats.util_state == state) { 2296f2dec1eaSThomas Gleixner time += ktime_get_ns() - ctx->stats.tstamp; 229727ec41d3SAndre Detsch } 2298e9f8a0b6SChristoph Hellwig 229927ec41d3SAndre Detsch return time / NSEC_PER_MSEC; 2300e9f8a0b6SChristoph Hellwig } 2301e9f8a0b6SChristoph Hellwig 2302e9f8a0b6SChristoph Hellwig static unsigned long long spufs_slb_flts(struct spu_context *ctx) 2303e9f8a0b6SChristoph Hellwig { 2304e9f8a0b6SChristoph Hellwig unsigned long long slb_flts = ctx->stats.slb_flt; 2305e9f8a0b6SChristoph Hellwig 2306e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2307e9f8a0b6SChristoph Hellwig slb_flts += (ctx->spu->stats.slb_flt - 2308e9f8a0b6SChristoph Hellwig ctx->stats.slb_flt_base); 2309e9f8a0b6SChristoph Hellwig } 2310e9f8a0b6SChristoph Hellwig 2311e9f8a0b6SChristoph Hellwig return slb_flts; 2312e9f8a0b6SChristoph Hellwig } 2313e9f8a0b6SChristoph Hellwig 2314e9f8a0b6SChristoph Hellwig static unsigned long long spufs_class2_intrs(struct spu_context *ctx) 2315e9f8a0b6SChristoph Hellwig { 2316e9f8a0b6SChristoph Hellwig unsigned long long class2_intrs = ctx->stats.class2_intr; 2317e9f8a0b6SChristoph Hellwig 2318e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2319e9f8a0b6SChristoph Hellwig class2_intrs += (ctx->spu->stats.class2_intr - 2320e9f8a0b6SChristoph Hellwig ctx->stats.class2_intr_base); 2321e9f8a0b6SChristoph Hellwig } 2322e9f8a0b6SChristoph Hellwig 2323e9f8a0b6SChristoph Hellwig return class2_intrs; 2324e9f8a0b6SChristoph Hellwig } 2325e9f8a0b6SChristoph Hellwig 2326e9f8a0b6SChristoph Hellwig 2327e9f8a0b6SChristoph Hellwig static int spufs_show_stat(struct seq_file *s, void *private) 2328e9f8a0b6SChristoph Hellwig { 2329e9f8a0b6SChristoph Hellwig struct spu_context *ctx = s->private; 2330c9101bdbSChristoph Hellwig int ret; 2331e9f8a0b6SChristoph Hellwig 2332c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 2333c9101bdbSChristoph Hellwig if (ret) 2334c9101bdbSChristoph Hellwig return ret; 2335c9101bdbSChristoph Hellwig 2336e9f8a0b6SChristoph Hellwig seq_printf(s, "%s %llu %llu %llu %llu " 2337e9f8a0b6SChristoph Hellwig "%llu %llu %llu %llu %llu %llu %llu %llu\n", 233827ec41d3SAndre Detsch ctx_state_names[ctx->stats.util_state], 233927ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_USER), 234027ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_SYSTEM), 234127ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IOWAIT), 234227ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED), 2343e9f8a0b6SChristoph Hellwig ctx->stats.vol_ctx_switch, 2344e9f8a0b6SChristoph Hellwig ctx->stats.invol_ctx_switch, 2345e9f8a0b6SChristoph Hellwig spufs_slb_flts(ctx), 2346e9f8a0b6SChristoph Hellwig ctx->stats.hash_flt, 2347e9f8a0b6SChristoph Hellwig ctx->stats.min_flt, 2348e9f8a0b6SChristoph Hellwig ctx->stats.maj_flt, 2349e9f8a0b6SChristoph Hellwig spufs_class2_intrs(ctx), 2350e9f8a0b6SChristoph Hellwig ctx->stats.libassist); 2351e9f8a0b6SChristoph Hellwig spu_release(ctx); 2352e9f8a0b6SChristoph Hellwig return 0; 2353e9f8a0b6SChristoph Hellwig } 2354e9f8a0b6SChristoph Hellwig 2355e9f8a0b6SChristoph Hellwig static int spufs_stat_open(struct inode *inode, struct file *file) 2356e9f8a0b6SChristoph Hellwig { 2357e9f8a0b6SChristoph Hellwig return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); 2358e9f8a0b6SChristoph Hellwig } 2359e9f8a0b6SChristoph Hellwig 2360e9f8a0b6SChristoph Hellwig static const struct file_operations spufs_stat_fops = { 2361e9f8a0b6SChristoph Hellwig .open = spufs_stat_open, 2362e9f8a0b6SChristoph Hellwig .read = seq_read, 2363e9f8a0b6SChristoph Hellwig .llseek = seq_lseek, 2364e9f8a0b6SChristoph Hellwig .release = single_release, 2365e9f8a0b6SChristoph Hellwig }; 2366e9f8a0b6SChristoph Hellwig 23675158e9b5SChristoph Hellwig static inline int spufs_switch_log_used(struct spu_context *ctx) 23685158e9b5SChristoph Hellwig { 23695158e9b5SChristoph Hellwig return (ctx->switch_log->head - ctx->switch_log->tail) % 23705158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 23715158e9b5SChristoph Hellwig } 23725158e9b5SChristoph Hellwig 23735158e9b5SChristoph Hellwig static inline int spufs_switch_log_avail(struct spu_context *ctx) 23745158e9b5SChristoph Hellwig { 23755158e9b5SChristoph Hellwig return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); 23765158e9b5SChristoph Hellwig } 23775158e9b5SChristoph Hellwig 23785158e9b5SChristoph Hellwig static int spufs_switch_log_open(struct inode *inode, struct file *file) 23795158e9b5SChristoph Hellwig { 23805158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2381f5ed0eb6SJeremy Kerr int rc; 23825158e9b5SChristoph Hellwig 2383f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2384f5ed0eb6SJeremy Kerr if (rc) 2385f5ed0eb6SJeremy Kerr return rc; 2386f5ed0eb6SJeremy Kerr 23875158e9b5SChristoph Hellwig if (ctx->switch_log) { 2388f5ed0eb6SJeremy Kerr rc = -EBUSY; 2389f5ed0eb6SJeremy Kerr goto out; 2390f5ed0eb6SJeremy Kerr } 2391f5ed0eb6SJeremy Kerr 2392837ef884SJeremy Kerr ctx->switch_log = kmalloc(sizeof(struct switch_log) + 23935158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry), 23945158e9b5SChristoph Hellwig GFP_KERNEL); 2395f5ed0eb6SJeremy Kerr 2396f5ed0eb6SJeremy Kerr if (!ctx->switch_log) { 2397f5ed0eb6SJeremy Kerr rc = -ENOMEM; 23985158e9b5SChristoph Hellwig goto out; 23995158e9b5SChristoph Hellwig } 2400f5ed0eb6SJeremy Kerr 2401837ef884SJeremy Kerr ctx->switch_log->head = ctx->switch_log->tail = 0; 2402f5ed0eb6SJeremy Kerr init_waitqueue_head(&ctx->switch_log->wait); 2403f5ed0eb6SJeremy Kerr rc = 0; 2404f5ed0eb6SJeremy Kerr 2405f5ed0eb6SJeremy Kerr out: 2406f5ed0eb6SJeremy Kerr spu_release(ctx); 2407f5ed0eb6SJeremy Kerr return rc; 2408f5ed0eb6SJeremy Kerr } 2409f5ed0eb6SJeremy Kerr 2410f5ed0eb6SJeremy Kerr static int spufs_switch_log_release(struct inode *inode, struct file *file) 2411f5ed0eb6SJeremy Kerr { 2412f5ed0eb6SJeremy Kerr struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2413f5ed0eb6SJeremy Kerr int rc; 2414f5ed0eb6SJeremy Kerr 2415f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2416f5ed0eb6SJeremy Kerr if (rc) 2417f5ed0eb6SJeremy Kerr return rc; 2418f5ed0eb6SJeremy Kerr 2419f5ed0eb6SJeremy Kerr kfree(ctx->switch_log); 2420f5ed0eb6SJeremy Kerr ctx->switch_log = NULL; 2421f5ed0eb6SJeremy Kerr spu_release(ctx); 24225158e9b5SChristoph Hellwig 24235158e9b5SChristoph Hellwig return 0; 24245158e9b5SChristoph Hellwig } 24255158e9b5SChristoph Hellwig 24265158e9b5SChristoph Hellwig static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) 24275158e9b5SChristoph Hellwig { 24285158e9b5SChristoph Hellwig struct switch_log_entry *p; 24295158e9b5SChristoph Hellwig 24305158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE; 24315158e9b5SChristoph Hellwig 24325158e9b5SChristoph Hellwig return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n", 24335158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_sec, 24345158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_nsec, 24355158e9b5SChristoph Hellwig p->spu_id, 24365158e9b5SChristoph Hellwig (unsigned int) p->type, 24375158e9b5SChristoph Hellwig (unsigned int) p->val, 24385158e9b5SChristoph Hellwig (unsigned long long) p->timebase); 24395158e9b5SChristoph Hellwig } 24405158e9b5SChristoph Hellwig 24415158e9b5SChristoph Hellwig static ssize_t spufs_switch_log_read(struct file *file, char __user *buf, 24425158e9b5SChristoph Hellwig size_t len, loff_t *ppos) 24435158e9b5SChristoph Hellwig { 2444496ad9aaSAl Viro struct inode *inode = file_inode(file); 24455158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 24465158e9b5SChristoph Hellwig int error = 0, cnt = 0; 24475158e9b5SChristoph Hellwig 244817e37675Sroel kluin if (!buf) 24495158e9b5SChristoph Hellwig return -EINVAL; 24505158e9b5SChristoph Hellwig 2451f5ed0eb6SJeremy Kerr error = spu_acquire(ctx); 2452f5ed0eb6SJeremy Kerr if (error) 2453f5ed0eb6SJeremy Kerr return error; 2454f5ed0eb6SJeremy Kerr 24555158e9b5SChristoph Hellwig while (cnt < len) { 24565158e9b5SChristoph Hellwig char tbuf[128]; 24575158e9b5SChristoph Hellwig int width; 24585158e9b5SChristoph Hellwig 2459f5ed0eb6SJeremy Kerr if (spufs_switch_log_used(ctx) == 0) { 246014f693eeSJeremy Kerr if (cnt > 0) { 246114f693eeSJeremy Kerr /* If there's data ready to go, we can 246214f693eeSJeremy Kerr * just return straight away */ 246314f693eeSJeremy Kerr break; 246414f693eeSJeremy Kerr 246514f693eeSJeremy Kerr } else if (file->f_flags & O_NONBLOCK) { 2466f5ed0eb6SJeremy Kerr error = -EAGAIN; 24675158e9b5SChristoph Hellwig break; 246814f693eeSJeremy Kerr 2469f5ed0eb6SJeremy Kerr } else { 247014f693eeSJeremy Kerr /* spufs_wait will drop the mutex and 247114f693eeSJeremy Kerr * re-acquire, but since we're in read(), the 247214f693eeSJeremy Kerr * file cannot be _released (and so 247314f693eeSJeremy Kerr * ctx->switch_log is stable). 2474f5ed0eb6SJeremy Kerr */ 2475f5ed0eb6SJeremy Kerr error = spufs_wait(ctx->switch_log->wait, 2476f5ed0eb6SJeremy Kerr spufs_switch_log_used(ctx) > 0); 24775158e9b5SChristoph Hellwig 2478f5ed0eb6SJeremy Kerr /* On error, spufs_wait returns without the 2479f5ed0eb6SJeremy Kerr * state mutex held */ 2480f5ed0eb6SJeremy Kerr if (error) 2481f5ed0eb6SJeremy Kerr return error; 24825158e9b5SChristoph Hellwig 248314f693eeSJeremy Kerr /* We may have had entries read from underneath 248414f693eeSJeremy Kerr * us while we dropped the mutex in spufs_wait, 248514f693eeSJeremy Kerr * so re-check */ 248614f693eeSJeremy Kerr if (spufs_switch_log_used(ctx) == 0) 2487f5ed0eb6SJeremy Kerr continue; 248814f693eeSJeremy Kerr } 248914f693eeSJeremy Kerr } 2490f5ed0eb6SJeremy Kerr 24915158e9b5SChristoph Hellwig width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); 2492f5ed0eb6SJeremy Kerr if (width < len) 24935158e9b5SChristoph Hellwig ctx->switch_log->tail = 24945158e9b5SChristoph Hellwig (ctx->switch_log->tail + 1) % 24955158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 2496f5ed0eb6SJeremy Kerr else 2497f5ed0eb6SJeremy Kerr /* If the record is greater than space available return 2498f5ed0eb6SJeremy Kerr * partial buffer (so far) */ 24995158e9b5SChristoph Hellwig break; 25005158e9b5SChristoph Hellwig 25015158e9b5SChristoph Hellwig error = copy_to_user(buf + cnt, tbuf, width); 25025158e9b5SChristoph Hellwig if (error) 25035158e9b5SChristoph Hellwig break; 25045158e9b5SChristoph Hellwig cnt += width; 25055158e9b5SChristoph Hellwig } 25065158e9b5SChristoph Hellwig 2507f5ed0eb6SJeremy Kerr spu_release(ctx); 2508f5ed0eb6SJeremy Kerr 25095158e9b5SChristoph Hellwig return cnt == 0 ? error : cnt; 25105158e9b5SChristoph Hellwig } 25115158e9b5SChristoph Hellwig 25125158e9b5SChristoph Hellwig static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait) 25135158e9b5SChristoph Hellwig { 2514496ad9aaSAl Viro struct inode *inode = file_inode(file); 25155158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 25165158e9b5SChristoph Hellwig unsigned int mask = 0; 2517f5ed0eb6SJeremy Kerr int rc; 25185158e9b5SChristoph Hellwig 25195158e9b5SChristoph Hellwig poll_wait(file, &ctx->switch_log->wait, wait); 25205158e9b5SChristoph Hellwig 2521f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2522f5ed0eb6SJeremy Kerr if (rc) 2523f5ed0eb6SJeremy Kerr return rc; 2524f5ed0eb6SJeremy Kerr 25255158e9b5SChristoph Hellwig if (spufs_switch_log_used(ctx) > 0) 25265158e9b5SChristoph Hellwig mask |= POLLIN; 25275158e9b5SChristoph Hellwig 2528f5ed0eb6SJeremy Kerr spu_release(ctx); 2529f5ed0eb6SJeremy Kerr 25305158e9b5SChristoph Hellwig return mask; 25315158e9b5SChristoph Hellwig } 25325158e9b5SChristoph Hellwig 25335158e9b5SChristoph Hellwig static const struct file_operations spufs_switch_log_fops = { 25345158e9b5SChristoph Hellwig .open = spufs_switch_log_open, 25355158e9b5SChristoph Hellwig .read = spufs_switch_log_read, 25365158e9b5SChristoph Hellwig .poll = spufs_switch_log_poll, 2537f5ed0eb6SJeremy Kerr .release = spufs_switch_log_release, 2538fc15351dSArnd Bergmann .llseek = no_llseek, 25395158e9b5SChristoph Hellwig }; 25405158e9b5SChristoph Hellwig 2541f5ed0eb6SJeremy Kerr /** 2542f5ed0eb6SJeremy Kerr * Log a context switch event to a switch log reader. 2543f5ed0eb6SJeremy Kerr * 2544f5ed0eb6SJeremy Kerr * Must be called with ctx->state_mutex held. 2545f5ed0eb6SJeremy Kerr */ 25465158e9b5SChristoph Hellwig void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, 25475158e9b5SChristoph Hellwig u32 type, u32 val) 25485158e9b5SChristoph Hellwig { 25495158e9b5SChristoph Hellwig if (!ctx->switch_log) 25505158e9b5SChristoph Hellwig return; 25515158e9b5SChristoph Hellwig 25525158e9b5SChristoph Hellwig if (spufs_switch_log_avail(ctx) > 1) { 25535158e9b5SChristoph Hellwig struct switch_log_entry *p; 25545158e9b5SChristoph Hellwig 25555158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->head; 25565158e9b5SChristoph Hellwig ktime_get_ts(&p->tstamp); 25575158e9b5SChristoph Hellwig p->timebase = get_tb(); 25585158e9b5SChristoph Hellwig p->spu_id = spu ? spu->number : -1; 25595158e9b5SChristoph Hellwig p->type = type; 25605158e9b5SChristoph Hellwig p->val = val; 25615158e9b5SChristoph Hellwig 25625158e9b5SChristoph Hellwig ctx->switch_log->head = 25635158e9b5SChristoph Hellwig (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; 25645158e9b5SChristoph Hellwig } 25655158e9b5SChristoph Hellwig 25665158e9b5SChristoph Hellwig wake_up(&ctx->switch_log->wait); 25675158e9b5SChristoph Hellwig } 2568e9f8a0b6SChristoph Hellwig 256946deed69SLuke Browning static int spufs_show_ctx(struct seq_file *s, void *private) 257046deed69SLuke Browning { 257146deed69SLuke Browning struct spu_context *ctx = s->private; 257246deed69SLuke Browning u64 mfc_control_RW; 257346deed69SLuke Browning 257446deed69SLuke Browning mutex_lock(&ctx->state_mutex); 257546deed69SLuke Browning if (ctx->spu) { 257646deed69SLuke Browning struct spu *spu = ctx->spu; 257746deed69SLuke Browning struct spu_priv2 __iomem *priv2 = spu->priv2; 257846deed69SLuke Browning 257946deed69SLuke Browning spin_lock_irq(&spu->register_lock); 258046deed69SLuke Browning mfc_control_RW = in_be64(&priv2->mfc_control_RW); 258146deed69SLuke Browning spin_unlock_irq(&spu->register_lock); 258246deed69SLuke Browning } else { 258346deed69SLuke Browning struct spu_state *csa = &ctx->csa; 258446deed69SLuke Browning 258546deed69SLuke Browning mfc_control_RW = csa->priv2.mfc_control_RW; 258646deed69SLuke Browning } 258746deed69SLuke Browning 258846deed69SLuke Browning seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)" 25899477e455SStephen Rothwell " %c %llx %llx %llx %llx %x %x\n", 259046deed69SLuke Browning ctx->state == SPU_STATE_SAVED ? 'S' : 'R', 259146deed69SLuke Browning ctx->flags, 259246deed69SLuke Browning ctx->sched_flags, 259346deed69SLuke Browning ctx->prio, 259446deed69SLuke Browning ctx->time_slice, 259546deed69SLuke Browning ctx->spu ? ctx->spu->number : -1, 259646deed69SLuke Browning !list_empty(&ctx->rq) ? 'q' : ' ', 259746deed69SLuke Browning ctx->csa.class_0_pending, 259846deed69SLuke Browning ctx->csa.class_0_dar, 259946deed69SLuke Browning ctx->csa.class_1_dsisr, 260046deed69SLuke Browning mfc_control_RW, 260146deed69SLuke Browning ctx->ops->runcntl_read(ctx), 260246deed69SLuke Browning ctx->ops->status_read(ctx)); 260346deed69SLuke Browning 260446deed69SLuke Browning mutex_unlock(&ctx->state_mutex); 260546deed69SLuke Browning 260646deed69SLuke Browning return 0; 260746deed69SLuke Browning } 260846deed69SLuke Browning 260946deed69SLuke Browning static int spufs_ctx_open(struct inode *inode, struct file *file) 261046deed69SLuke Browning { 261146deed69SLuke Browning return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx); 261246deed69SLuke Browning } 261346deed69SLuke Browning 261446deed69SLuke Browning static const struct file_operations spufs_ctx_fops = { 261546deed69SLuke Browning .open = spufs_ctx_open, 261646deed69SLuke Browning .read = seq_read, 261746deed69SLuke Browning .llseek = seq_lseek, 261846deed69SLuke Browning .release = single_release, 261946deed69SLuke Browning }; 262046deed69SLuke Browning 262174254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_contents[] = { 2622cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 26236f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 26246f7dde81SJeremy Kerr { "regs", &spufs_regs_fops, 0666, sizeof(struct spu_reg128[128]), }, 262567207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 262667207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 262767207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 26286f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 26296f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 26306f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2631603c4612SJeremy Kerr { "signal1", &spufs_signal1_fops, 0666, }, 2632603c4612SJeremy Kerr { "signal2", &spufs_signal2_fops, 0666, }, 263367207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 263467207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 26356df10a82SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26366f7dde81SJeremy Kerr { "fpcr", &spufs_fpcr_fops, 0666, sizeof(struct spu_reg128), }, 2637b9e3bd77SDwayne Grant McConnell { "lslr", &spufs_lslr_ops, 0444, }, 2638b9e3bd77SDwayne Grant McConnell { "mfc", &spufs_mfc_fops, 0666, }, 2639b9e3bd77SDwayne Grant McConnell { "mss", &spufs_mss_fops, 0666, }, 2640b9e3bd77SDwayne Grant McConnell { "npc", &spufs_npc_ops, 0666, }, 2641b9e3bd77SDwayne Grant McConnell { "srr0", &spufs_srr0_ops, 0666, }, 26428b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 26438b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 26448b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 2645b9e3bd77SDwayne Grant McConnell { "event_status", &spufs_event_status_ops, 0444, }, 26466f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 264786767277SArnd Bergmann { "phys-id", &spufs_id_ops, 0666, }, 264886767277SArnd Bergmann { "object-id", &spufs_object_id_ops, 0666, }, 26496f7dde81SJeremy Kerr { "mbox_info", &spufs_mbox_info_fops, 0444, sizeof(u32), }, 26506f7dde81SJeremy Kerr { "ibox_info", &spufs_ibox_info_fops, 0444, sizeof(u32), }, 26516f7dde81SJeremy Kerr { "wbox_info", &spufs_wbox_info_fops, 0444, sizeof(u32), }, 26526f7dde81SJeremy Kerr { "dma_info", &spufs_dma_info_fops, 0444, 26536f7dde81SJeremy Kerr sizeof(struct spu_dma_info), }, 26546f7dde81SJeremy Kerr { "proxydma_info", &spufs_proxydma_info_fops, 0444, 26556f7dde81SJeremy Kerr sizeof(struct spu_proxydma_info)}, 2656476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2657e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 26585158e9b5SChristoph Hellwig { "switch_log", &spufs_switch_log_fops, 0444 }, 265967207b96SArnd Bergmann {}, 266067207b96SArnd Bergmann }; 26615737edd1SMark Nutter 266274254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_nosched_contents[] = { 2663cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 26646f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 26655737edd1SMark Nutter { "mbox", &spufs_mbox_fops, 0444, }, 26665737edd1SMark Nutter { "ibox", &spufs_ibox_fops, 0444, }, 26675737edd1SMark Nutter { "wbox", &spufs_wbox_fops, 0222, }, 26686f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 26696f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 26706f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2671d054b36fSJeremy Kerr { "signal1", &spufs_signal1_nosched_fops, 0222, }, 2672d054b36fSJeremy Kerr { "signal2", &spufs_signal2_nosched_fops, 0222, }, 26735737edd1SMark Nutter { "signal1_type", &spufs_signal1_type, 0666, }, 26745737edd1SMark Nutter { "signal2_type", &spufs_signal2_type, 0666, }, 26755737edd1SMark Nutter { "mss", &spufs_mss_fops, 0666, }, 26765737edd1SMark Nutter { "mfc", &spufs_mfc_fops, 0666, }, 26775737edd1SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26785737edd1SMark Nutter { "npc", &spufs_npc_ops, 0666, }, 26796f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 26805737edd1SMark Nutter { "phys-id", &spufs_id_ops, 0666, }, 26815737edd1SMark Nutter { "object-id", &spufs_object_id_ops, 0666, }, 2682476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2683e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 26842c3e4787SJeremy Kerr {}, 26852c3e4787SJeremy Kerr }; 26862c3e4787SJeremy Kerr 268774254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_debug_contents[] = { 268846deed69SLuke Browning { ".ctx", &spufs_ctx_fops, 0444, }, 26895737edd1SMark Nutter {}, 26905737edd1SMark Nutter }; 2691bf1ab978SDwayne Grant McConnell 269274254647SJeremy Kerr const struct spufs_coredump_reader spufs_coredump_read[] = { 26934fca9c42SMichael Ellerman { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])}, 26944fca9c42SMichael Ellerman { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) }, 2695104f0cc2SMichael Ellerman { "lslr", NULL, spufs_lslr_get, 19 }, 2696104f0cc2SMichael Ellerman { "decr", NULL, spufs_decr_get, 19 }, 2697104f0cc2SMichael Ellerman { "decr_status", NULL, spufs_decr_status_get, 19 }, 26984fca9c42SMichael Ellerman { "mem", __spufs_mem_read, NULL, LS_SIZE, }, 26994fca9c42SMichael Ellerman { "signal1", __spufs_signal1_read, NULL, sizeof(u32) }, 2700104f0cc2SMichael Ellerman { "signal1_type", NULL, spufs_signal1_type_get, 19 }, 27014fca9c42SMichael Ellerman { "signal2", __spufs_signal2_read, NULL, sizeof(u32) }, 2702104f0cc2SMichael Ellerman { "signal2_type", NULL, spufs_signal2_type_get, 19 }, 2703104f0cc2SMichael Ellerman { "event_mask", NULL, spufs_event_mask_get, 19 }, 2704104f0cc2SMichael Ellerman { "event_status", NULL, spufs_event_status_get, 19 }, 27054fca9c42SMichael Ellerman { "mbox_info", __spufs_mbox_info_read, NULL, sizeof(u32) }, 27064fca9c42SMichael Ellerman { "ibox_info", __spufs_ibox_info_read, NULL, sizeof(u32) }, 27074fca9c42SMichael Ellerman { "wbox_info", __spufs_wbox_info_read, NULL, 4 * sizeof(u32)}, 27084fca9c42SMichael Ellerman { "dma_info", __spufs_dma_info_read, NULL, sizeof(struct spu_dma_info)}, 27094fca9c42SMichael Ellerman { "proxydma_info", __spufs_proxydma_info_read, 27104fca9c42SMichael Ellerman NULL, sizeof(struct spu_proxydma_info)}, 2711104f0cc2SMichael Ellerman { "object-id", NULL, spufs_object_id_get, 19 }, 2712104f0cc2SMichael Ellerman { "npc", NULL, spufs_npc_get, 19 }, 2713936d5bf1SMichael Ellerman { NULL }, 2714bf1ab978SDwayne Grant McConnell }; 2715