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> 3867207b96SArnd Bergmann #include <asm/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 address = (unsigned long)vmf->virtual_address; 240b1e2270fSNick Piggin unsigned long pfn, offset; 241b1e2270fSNick Piggin 242b1e2270fSNick Piggin offset = vmf->pgoff << PAGE_SHIFT; 243128b8546SMasato Noguchi if (offset >= LS_SIZE) 244b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 245128b8546SMasato Noguchi 246b1e2270fSNick Piggin pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n", 247b1e2270fSNick Piggin address, offset); 248f1fa74f4SBenjamin Herrenschmidt 249c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 250b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2518b3d6663SArnd Bergmann 252ac91cb8dSArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 25364b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); 25478bde53eSBenjamin Herrenschmidt pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset); 255ac91cb8dSArnd Bergmann } else { 25664b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 25778bde53eSBenjamin Herrenschmidt pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT; 258ac91cb8dSArnd Bergmann } 25978bde53eSBenjamin Herrenschmidt vm_insert_pfn(vma, address, pfn); 26078bde53eSBenjamin Herrenschmidt 2618b3d6663SArnd Bergmann spu_release(ctx); 2628b3d6663SArnd Bergmann 263b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2648b3d6663SArnd Bergmann } 2658b3d6663SArnd Bergmann 266a352894dSBenjamin Herrenschmidt static int spufs_mem_mmap_access(struct vm_area_struct *vma, 267a352894dSBenjamin Herrenschmidt unsigned long address, 268a352894dSBenjamin Herrenschmidt void *buf, int len, int write) 269a352894dSBenjamin Herrenschmidt { 270a352894dSBenjamin Herrenschmidt struct spu_context *ctx = vma->vm_file->private_data; 271a352894dSBenjamin Herrenschmidt unsigned long offset = address - vma->vm_start; 272a352894dSBenjamin Herrenschmidt char *local_store; 273a352894dSBenjamin Herrenschmidt 274a352894dSBenjamin Herrenschmidt if (write && !(vma->vm_flags & VM_WRITE)) 275a352894dSBenjamin Herrenschmidt return -EACCES; 276a352894dSBenjamin Herrenschmidt if (spu_acquire(ctx)) 277a352894dSBenjamin Herrenschmidt return -EINTR; 278a352894dSBenjamin Herrenschmidt if ((offset + len) > vma->vm_end) 279a352894dSBenjamin Herrenschmidt len = vma->vm_end - offset; 280a352894dSBenjamin Herrenschmidt local_store = ctx->ops->get_ls(ctx); 281a352894dSBenjamin Herrenschmidt if (write) 282a352894dSBenjamin Herrenschmidt memcpy_toio(local_store + offset, buf, len); 283a352894dSBenjamin Herrenschmidt else 284a352894dSBenjamin Herrenschmidt memcpy_fromio(buf, local_store + offset, len); 285a352894dSBenjamin Herrenschmidt spu_release(ctx); 286a352894dSBenjamin Herrenschmidt return len; 287a352894dSBenjamin Herrenschmidt } 28878bde53eSBenjamin Herrenschmidt 289f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mem_mmap_vmops = { 290b1e2270fSNick Piggin .fault = spufs_mem_mmap_fault, 291a352894dSBenjamin Herrenschmidt .access = spufs_mem_mmap_access, 2928b3d6663SArnd Bergmann }; 2938b3d6663SArnd Bergmann 294f1fa74f4SBenjamin Herrenschmidt static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 29567207b96SArnd Bergmann { 2968b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 2978b3d6663SArnd Bergmann return -EINVAL; 29867207b96SArnd Bergmann 29978bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 30064b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 3018b3d6663SArnd Bergmann 3028b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 30367207b96SArnd Bergmann return 0; 30467207b96SArnd Bergmann } 30567207b96SArnd Bergmann 3065dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = { 30767207b96SArnd Bergmann .open = spufs_mem_open, 308ce92987bSChristoph Hellwig .release = spufs_mem_release, 30967207b96SArnd Bergmann .read = spufs_mem_read, 31067207b96SArnd Bergmann .write = spufs_mem_write, 3118b3d6663SArnd Bergmann .llseek = generic_file_llseek, 31267207b96SArnd Bergmann .mmap = spufs_mem_mmap, 3138b3d6663SArnd Bergmann }; 3148b3d6663SArnd Bergmann 315b1e2270fSNick Piggin static int spufs_ps_fault(struct vm_area_struct *vma, 316b1e2270fSNick Piggin struct vm_fault *vmf, 31778bde53eSBenjamin Herrenschmidt unsigned long ps_offs, 31827d5bf2aSBenjamin Herrenschmidt unsigned long ps_size) 3196df10a82SMark Nutter { 3206df10a82SMark Nutter struct spu_context *ctx = vma->vm_file->private_data; 321b1e2270fSNick Piggin unsigned long area, offset = vmf->pgoff << PAGE_SHIFT; 322eebead5bSChristoph Hellwig int ret = 0; 3236df10a82SMark Nutter 324b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__enter, ctx); 325038200cfSChristoph Hellwig 32627d5bf2aSBenjamin Herrenschmidt if (offset >= ps_size) 327b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 3286df10a82SMark Nutter 32960657263SJeremy Kerr if (fatal_signal_pending(current)) 33060657263SJeremy Kerr return VM_FAULT_SIGBUS; 33160657263SJeremy Kerr 33233bfd7a7SArnd Bergmann /* 333d5883137SJeremy Kerr * Because we release the mmap_sem, the context may be destroyed while 334d5883137SJeremy Kerr * we're in spu_wait. Grab an extra reference so it isn't destroyed 335d5883137SJeremy Kerr * in the meantime. 336d5883137SJeremy Kerr */ 337d5883137SJeremy Kerr get_spu_context(ctx); 338d5883137SJeremy Kerr 339d5883137SJeremy Kerr /* 34033bfd7a7SArnd Bergmann * We have to wait for context to be loaded before we have 34133bfd7a7SArnd Bergmann * pages to hand out to the user, but we don't want to wait 34233bfd7a7SArnd Bergmann * with the mmap_sem held. 34333bfd7a7SArnd Bergmann * It is possible to drop the mmap_sem here, but then we need 344b1e2270fSNick Piggin * to return VM_FAULT_NOPAGE because the mappings may have 34533bfd7a7SArnd Bergmann * hanged. 34678bde53eSBenjamin Herrenschmidt */ 347c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 348d5883137SJeremy Kerr goto refault; 349c9101bdbSChristoph Hellwig 35033bfd7a7SArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 35133bfd7a7SArnd Bergmann up_read(¤t->mm->mmap_sem); 352b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__sleep, ctx); 353eebead5bSChristoph Hellwig ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 354b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu); 35533bfd7a7SArnd Bergmann down_read(¤t->mm->mmap_sem); 356c9101bdbSChristoph Hellwig } else { 3576df10a82SMark Nutter area = ctx->spu->problem_phys + ps_offs; 358b1e2270fSNick Piggin vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, 359b1e2270fSNick Piggin (area + offset) >> PAGE_SHIFT); 360b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu); 361c9101bdbSChristoph Hellwig } 36233bfd7a7SArnd Bergmann 363eebead5bSChristoph Hellwig if (!ret) 3646df10a82SMark Nutter spu_release(ctx); 365d5883137SJeremy Kerr 366d5883137SJeremy Kerr refault: 367d5883137SJeremy Kerr put_spu_context(ctx); 368b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 3696df10a82SMark Nutter } 3706df10a82SMark Nutter 37127d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 372b1e2270fSNick Piggin static int spufs_cntl_mmap_fault(struct vm_area_struct *vma, 373b1e2270fSNick Piggin struct vm_fault *vmf) 3746df10a82SMark Nutter { 37587ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x4000, SPUFS_CNTL_MAP_SIZE); 3766df10a82SMark Nutter } 3776df10a82SMark Nutter 378f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_cntl_mmap_vmops = { 379b1e2270fSNick Piggin .fault = spufs_cntl_mmap_fault, 3806df10a82SMark Nutter }; 3816df10a82SMark Nutter 3826df10a82SMark Nutter /* 3836df10a82SMark Nutter * mmap support for problem state control area [0x4000 - 0x4fff]. 3846df10a82SMark Nutter */ 3856df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) 3866df10a82SMark Nutter { 3876df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 3886df10a82SMark Nutter return -EINVAL; 3896df10a82SMark Nutter 39078bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 39164b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 3926df10a82SMark Nutter 3936df10a82SMark Nutter vma->vm_ops = &spufs_cntl_mmap_vmops; 3946df10a82SMark Nutter return 0; 3956df10a82SMark Nutter } 39627d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 39727d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL 39827d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 3996df10a82SMark Nutter 400197b1a82SChristoph Hellwig static int spufs_cntl_get(void *data, u64 *val) 401e1dbff2bSArnd Bergmann { 402e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 403c9101bdbSChristoph Hellwig int ret; 404e1dbff2bSArnd Bergmann 405c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 406c9101bdbSChristoph Hellwig if (ret) 407c9101bdbSChristoph Hellwig return ret; 408197b1a82SChristoph Hellwig *val = ctx->ops->status_read(ctx); 409e1dbff2bSArnd Bergmann spu_release(ctx); 410e1dbff2bSArnd Bergmann 411197b1a82SChristoph Hellwig return 0; 412e1dbff2bSArnd Bergmann } 413e1dbff2bSArnd Bergmann 414197b1a82SChristoph Hellwig static int spufs_cntl_set(void *data, u64 val) 415e1dbff2bSArnd Bergmann { 416e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 417c9101bdbSChristoph Hellwig int ret; 418e1dbff2bSArnd Bergmann 419c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 420c9101bdbSChristoph Hellwig if (ret) 421c9101bdbSChristoph Hellwig return ret; 422e1dbff2bSArnd Bergmann ctx->ops->runcntl_write(ctx, val); 423e1dbff2bSArnd Bergmann spu_release(ctx); 424197b1a82SChristoph Hellwig 425197b1a82SChristoph Hellwig return 0; 426e1dbff2bSArnd Bergmann } 427e1dbff2bSArnd Bergmann 4286df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file) 4296df10a82SMark Nutter { 4306df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 4316df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 4326df10a82SMark Nutter 43347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 4346df10a82SMark Nutter file->private_data = ctx; 43543c2bbd9SChristoph Hellwig if (!i->i_openers++) 4366df10a82SMark Nutter ctx->cntl = inode->i_mapping; 43747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 4388b88b099SChristoph Hellwig return simple_attr_open(inode, file, spufs_cntl_get, 439e1dbff2bSArnd Bergmann spufs_cntl_set, "0x%08lx"); 4406df10a82SMark Nutter } 4416df10a82SMark Nutter 44243c2bbd9SChristoph Hellwig static int 44343c2bbd9SChristoph Hellwig spufs_cntl_release(struct inode *inode, struct file *file) 44443c2bbd9SChristoph Hellwig { 44543c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 44643c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 44743c2bbd9SChristoph Hellwig 44874bedc4dSChristoph Hellwig simple_attr_release(inode, file); 44943c2bbd9SChristoph Hellwig 45047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 45143c2bbd9SChristoph Hellwig if (!--i->i_openers) 45243c2bbd9SChristoph Hellwig ctx->cntl = NULL; 45347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 45443c2bbd9SChristoph Hellwig return 0; 45543c2bbd9SChristoph Hellwig } 45643c2bbd9SChristoph Hellwig 4575dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = { 4586df10a82SMark Nutter .open = spufs_cntl_open, 45943c2bbd9SChristoph Hellwig .release = spufs_cntl_release, 4608b88b099SChristoph Hellwig .read = simple_attr_read, 4618b88b099SChristoph Hellwig .write = simple_attr_write, 462fc15351dSArnd Bergmann .llseek = generic_file_llseek, 4636df10a82SMark Nutter .mmap = spufs_cntl_mmap, 4646df10a82SMark Nutter }; 4656df10a82SMark Nutter 4668b3d6663SArnd Bergmann static int 4678b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 4688b3d6663SArnd Bergmann { 4698b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 4708b3d6663SArnd Bergmann file->private_data = i->i_ctx; 4718b3d6663SArnd Bergmann return 0; 4728b3d6663SArnd Bergmann } 4738b3d6663SArnd Bergmann 4748b3d6663SArnd Bergmann static ssize_t 475bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer, 476bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 477bf1ab978SDwayne Grant McConnell { 478bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 479bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 480bf1ab978SDwayne Grant McConnell lscsa->gprs, sizeof lscsa->gprs); 481bf1ab978SDwayne Grant McConnell } 482bf1ab978SDwayne Grant McConnell 483bf1ab978SDwayne Grant McConnell static ssize_t 4848b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 4858b3d6663SArnd Bergmann size_t size, loff_t *pos) 4868b3d6663SArnd Bergmann { 4878b3d6663SArnd Bergmann int ret; 488bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 4898b3d6663SArnd Bergmann 490f027faa2SJeremy Kerr /* pre-check for file position: if we'd return EOF, there's no point 491f027faa2SJeremy Kerr * causing a deschedule */ 492f027faa2SJeremy Kerr if (*pos >= sizeof(ctx->csa.lscsa->gprs)) 493f027faa2SJeremy Kerr return 0; 494f027faa2SJeremy Kerr 495c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 496c9101bdbSChristoph Hellwig if (ret) 497c9101bdbSChristoph Hellwig return ret; 498bf1ab978SDwayne Grant McConnell ret = __spufs_regs_read(ctx, buffer, size, pos); 49927b1ea09SChristoph Hellwig spu_release_saved(ctx); 5008b3d6663SArnd Bergmann return ret; 5018b3d6663SArnd Bergmann } 5028b3d6663SArnd Bergmann 5038b3d6663SArnd Bergmann static ssize_t 5048b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 5058b3d6663SArnd Bergmann size_t size, loff_t *pos) 5068b3d6663SArnd Bergmann { 5078b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5088b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5098b3d6663SArnd Bergmann int ret; 5108b3d6663SArnd Bergmann 511d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->gprs)) 5128b3d6663SArnd Bergmann return -EFBIG; 513d219889bSJeremy Kerr 514c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 515c9101bdbSChristoph Hellwig if (ret) 516c9101bdbSChristoph Hellwig return ret; 5178b3d6663SArnd Bergmann 51863c3b9d7SAkinobu Mita size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos, 51963c3b9d7SAkinobu Mita buffer, size); 5208b3d6663SArnd Bergmann 52127b1ea09SChristoph Hellwig spu_release_saved(ctx); 52263c3b9d7SAkinobu Mita return size; 5238b3d6663SArnd Bergmann } 5248b3d6663SArnd Bergmann 5255dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = { 5268b3d6663SArnd Bergmann .open = spufs_regs_open, 5278b3d6663SArnd Bergmann .read = spufs_regs_read, 5288b3d6663SArnd Bergmann .write = spufs_regs_write, 5298b3d6663SArnd Bergmann .llseek = generic_file_llseek, 5308b3d6663SArnd Bergmann }; 5318b3d6663SArnd Bergmann 5328b3d6663SArnd Bergmann static ssize_t 533bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer, 534bf1ab978SDwayne Grant McConnell size_t size, loff_t * pos) 535bf1ab978SDwayne Grant McConnell { 536bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 537bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 538bf1ab978SDwayne Grant McConnell &lscsa->fpcr, sizeof(lscsa->fpcr)); 539bf1ab978SDwayne Grant McConnell } 540bf1ab978SDwayne Grant McConnell 541bf1ab978SDwayne Grant McConnell static ssize_t 5428b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 5438b3d6663SArnd Bergmann size_t size, loff_t * pos) 5448b3d6663SArnd Bergmann { 5458b3d6663SArnd Bergmann int ret; 546bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 5478b3d6663SArnd Bergmann 548c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 549c9101bdbSChristoph Hellwig if (ret) 550c9101bdbSChristoph Hellwig return ret; 551bf1ab978SDwayne Grant McConnell ret = __spufs_fpcr_read(ctx, buffer, size, pos); 55227b1ea09SChristoph Hellwig spu_release_saved(ctx); 5538b3d6663SArnd Bergmann return ret; 5548b3d6663SArnd Bergmann } 5558b3d6663SArnd Bergmann 5568b3d6663SArnd Bergmann static ssize_t 5578b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 5588b3d6663SArnd Bergmann size_t size, loff_t * pos) 5598b3d6663SArnd Bergmann { 5608b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5618b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5628b3d6663SArnd Bergmann int ret; 5638b3d6663SArnd Bergmann 564d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->fpcr)) 5658b3d6663SArnd Bergmann return -EFBIG; 566c9101bdbSChristoph Hellwig 567c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 568c9101bdbSChristoph Hellwig if (ret) 569c9101bdbSChristoph Hellwig return ret; 570c9101bdbSChristoph Hellwig 57163c3b9d7SAkinobu Mita size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos, 57263c3b9d7SAkinobu Mita buffer, size); 5738b3d6663SArnd Bergmann 57427b1ea09SChristoph Hellwig spu_release_saved(ctx); 57563c3b9d7SAkinobu Mita return size; 5768b3d6663SArnd Bergmann } 5778b3d6663SArnd Bergmann 5785dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = { 5798b3d6663SArnd Bergmann .open = spufs_regs_open, 5808b3d6663SArnd Bergmann .read = spufs_fpcr_read, 5818b3d6663SArnd Bergmann .write = spufs_fpcr_write, 58267207b96SArnd Bergmann .llseek = generic_file_llseek, 58367207b96SArnd Bergmann }; 58467207b96SArnd Bergmann 58567207b96SArnd Bergmann /* generic open function for all pipe-like files */ 58667207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 58767207b96SArnd Bergmann { 58867207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 58967207b96SArnd Bergmann file->private_data = i->i_ctx; 59067207b96SArnd Bergmann 59167207b96SArnd Bergmann return nonseekable_open(inode, file); 59267207b96SArnd Bergmann } 59367207b96SArnd Bergmann 594cdcc89bbSArnd Bergmann /* 595cdcc89bbSArnd Bergmann * Read as many bytes from the mailbox as possible, until 596cdcc89bbSArnd Bergmann * one of the conditions becomes true: 597cdcc89bbSArnd Bergmann * 598cdcc89bbSArnd Bergmann * - no more data available in the mailbox 599cdcc89bbSArnd Bergmann * - end of the user provided buffer 600cdcc89bbSArnd Bergmann * - end of the mapped area 601cdcc89bbSArnd Bergmann */ 60267207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 60367207b96SArnd Bergmann size_t len, loff_t *pos) 60467207b96SArnd Bergmann { 6058b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 606cdcc89bbSArnd Bergmann u32 mbox_data, __user *udata; 607cdcc89bbSArnd Bergmann ssize_t count; 60867207b96SArnd Bergmann 60967207b96SArnd Bergmann if (len < 4) 61067207b96SArnd Bergmann return -EINVAL; 61167207b96SArnd Bergmann 612cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 61367207b96SArnd Bergmann return -EFAULT; 61467207b96SArnd Bergmann 615cdcc89bbSArnd Bergmann udata = (void __user *)buf; 616cdcc89bbSArnd Bergmann 617c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 618c9101bdbSChristoph Hellwig if (count) 619c9101bdbSChristoph Hellwig return count; 620c9101bdbSChristoph Hellwig 621274cef5eSArnd Bergmann for (count = 0; (count + 4) <= len; count += 4, udata++) { 622cdcc89bbSArnd Bergmann int ret; 623cdcc89bbSArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 624cdcc89bbSArnd Bergmann if (ret == 0) 625cdcc89bbSArnd Bergmann break; 626cdcc89bbSArnd Bergmann 627cdcc89bbSArnd Bergmann /* 628cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 629cdcc89bbSArnd Bergmann * but still need to return the data we have 630cdcc89bbSArnd Bergmann * read successfully so far. 631cdcc89bbSArnd Bergmann */ 632cdcc89bbSArnd Bergmann ret = __put_user(mbox_data, udata); 633cdcc89bbSArnd Bergmann if (ret) { 634cdcc89bbSArnd Bergmann if (!count) 635cdcc89bbSArnd Bergmann count = -EFAULT; 636cdcc89bbSArnd Bergmann break; 637cdcc89bbSArnd Bergmann } 638cdcc89bbSArnd Bergmann } 639cdcc89bbSArnd Bergmann spu_release(ctx); 640cdcc89bbSArnd Bergmann 641cdcc89bbSArnd Bergmann if (!count) 642cdcc89bbSArnd Bergmann count = -EAGAIN; 643cdcc89bbSArnd Bergmann 644cdcc89bbSArnd Bergmann return count; 64567207b96SArnd Bergmann } 64667207b96SArnd Bergmann 6475dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = { 64867207b96SArnd Bergmann .open = spufs_pipe_open, 64967207b96SArnd Bergmann .read = spufs_mbox_read, 650fc15351dSArnd Bergmann .llseek = no_llseek, 65167207b96SArnd Bergmann }; 65267207b96SArnd Bergmann 65367207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 65467207b96SArnd Bergmann size_t len, loff_t *pos) 65567207b96SArnd Bergmann { 6568b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 657c9101bdbSChristoph Hellwig ssize_t ret; 65867207b96SArnd Bergmann u32 mbox_stat; 65967207b96SArnd Bergmann 66067207b96SArnd Bergmann if (len < 4) 66167207b96SArnd Bergmann return -EINVAL; 66267207b96SArnd Bergmann 663c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 664c9101bdbSChristoph Hellwig if (ret) 665c9101bdbSChristoph Hellwig return ret; 6668b3d6663SArnd Bergmann 6678b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 6688b3d6663SArnd Bergmann 6698b3d6663SArnd Bergmann spu_release(ctx); 67067207b96SArnd Bergmann 67167207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 67267207b96SArnd Bergmann return -EFAULT; 67367207b96SArnd Bergmann 67467207b96SArnd Bergmann return 4; 67567207b96SArnd Bergmann } 67667207b96SArnd Bergmann 6775dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = { 67867207b96SArnd Bergmann .open = spufs_pipe_open, 67967207b96SArnd Bergmann .read = spufs_mbox_stat_read, 680fc15351dSArnd Bergmann .llseek = no_llseek, 68167207b96SArnd Bergmann }; 68267207b96SArnd Bergmann 68367207b96SArnd Bergmann /* low-level ibox access function */ 6848b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 68567207b96SArnd Bergmann { 6868b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 68767207b96SArnd Bergmann } 68867207b96SArnd Bergmann 68967207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on) 69067207b96SArnd Bergmann { 6918b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 6928b3d6663SArnd Bergmann 6938b3d6663SArnd Bergmann return fasync_helper(fd, file, on, &ctx->ibox_fasync); 6948b3d6663SArnd Bergmann } 6958b3d6663SArnd Bergmann 6968b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 6978b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 6988b3d6663SArnd Bergmann { 6998b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 7008b3d6663SArnd Bergmann 701e65c2f6fSLuke Browning if (!ctx) 702e65c2f6fSLuke Browning return; 703e65c2f6fSLuke Browning 7048b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 7058b3d6663SArnd Bergmann kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); 70667207b96SArnd Bergmann } 70767207b96SArnd Bergmann 708cdcc89bbSArnd Bergmann /* 709cdcc89bbSArnd Bergmann * Read as many bytes from the interrupt mailbox as possible, until 710cdcc89bbSArnd Bergmann * one of the conditions becomes true: 711cdcc89bbSArnd Bergmann * 712cdcc89bbSArnd Bergmann * - no more data available in the mailbox 713cdcc89bbSArnd Bergmann * - end of the user provided buffer 714cdcc89bbSArnd Bergmann * - end of the mapped area 715cdcc89bbSArnd Bergmann * 716cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 717cdcc89bbSArnd Bergmann * any data is available, but return when we have been able to 718cdcc89bbSArnd Bergmann * read something. 719cdcc89bbSArnd Bergmann */ 72067207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 72167207b96SArnd Bergmann size_t len, loff_t *pos) 72267207b96SArnd Bergmann { 7238b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 724cdcc89bbSArnd Bergmann u32 ibox_data, __user *udata; 725cdcc89bbSArnd Bergmann ssize_t count; 72667207b96SArnd Bergmann 72767207b96SArnd Bergmann if (len < 4) 72867207b96SArnd Bergmann return -EINVAL; 72967207b96SArnd Bergmann 730cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 731cdcc89bbSArnd Bergmann return -EFAULT; 732cdcc89bbSArnd Bergmann 733cdcc89bbSArnd Bergmann udata = (void __user *)buf; 734cdcc89bbSArnd Bergmann 735c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 736c9101bdbSChristoph Hellwig if (count) 737eebead5bSChristoph Hellwig goto out; 73867207b96SArnd Bergmann 739cdcc89bbSArnd Bergmann /* wait only for the first element */ 740cdcc89bbSArnd Bergmann count = 0; 74167207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 742eebead5bSChristoph Hellwig if (!spu_ibox_read(ctx, &ibox_data)) { 743cdcc89bbSArnd Bergmann count = -EAGAIN; 744eebead5bSChristoph Hellwig goto out_unlock; 745eebead5bSChristoph Hellwig } 74667207b96SArnd Bergmann } else { 747cdcc89bbSArnd Bergmann count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 748cdcc89bbSArnd Bergmann if (count) 749cdcc89bbSArnd Bergmann goto out; 750eebead5bSChristoph Hellwig } 751cdcc89bbSArnd Bergmann 752cdcc89bbSArnd Bergmann /* if we can't write at all, return -EFAULT */ 753cdcc89bbSArnd Bergmann count = __put_user(ibox_data, udata); 754cdcc89bbSArnd Bergmann if (count) 755eebead5bSChristoph Hellwig goto out_unlock; 756cdcc89bbSArnd Bergmann 757cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 758cdcc89bbSArnd Bergmann int ret; 759cdcc89bbSArnd Bergmann ret = ctx->ops->ibox_read(ctx, &ibox_data); 760cdcc89bbSArnd Bergmann if (ret == 0) 761cdcc89bbSArnd Bergmann break; 762cdcc89bbSArnd Bergmann /* 763cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 764cdcc89bbSArnd Bergmann * but still need to return the data we have 765cdcc89bbSArnd Bergmann * read successfully so far. 766cdcc89bbSArnd Bergmann */ 767cdcc89bbSArnd Bergmann ret = __put_user(ibox_data, udata); 768cdcc89bbSArnd Bergmann if (ret) 769cdcc89bbSArnd Bergmann break; 77067207b96SArnd Bergmann } 77167207b96SArnd Bergmann 772eebead5bSChristoph Hellwig out_unlock: 7738b3d6663SArnd Bergmann spu_release(ctx); 774eebead5bSChristoph Hellwig out: 775cdcc89bbSArnd Bergmann return count; 77667207b96SArnd Bergmann } 77767207b96SArnd Bergmann 77867207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) 77967207b96SArnd Bergmann { 7808b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 78167207b96SArnd Bergmann unsigned int mask; 78267207b96SArnd Bergmann 7838b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 78467207b96SArnd Bergmann 785c9101bdbSChristoph Hellwig /* 786c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 787c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 788c9101bdbSChristoph Hellwig */ 789c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 7903a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); 7913a843d7cSArnd Bergmann spu_release(ctx); 79267207b96SArnd Bergmann 79367207b96SArnd Bergmann return mask; 79467207b96SArnd Bergmann } 79567207b96SArnd Bergmann 7965dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = { 79767207b96SArnd Bergmann .open = spufs_pipe_open, 79867207b96SArnd Bergmann .read = spufs_ibox_read, 79967207b96SArnd Bergmann .poll = spufs_ibox_poll, 80067207b96SArnd Bergmann .fasync = spufs_ibox_fasync, 801fc15351dSArnd Bergmann .llseek = no_llseek, 80267207b96SArnd Bergmann }; 80367207b96SArnd Bergmann 80467207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 80567207b96SArnd Bergmann size_t len, loff_t *pos) 80667207b96SArnd Bergmann { 8078b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 808c9101bdbSChristoph Hellwig ssize_t ret; 80967207b96SArnd Bergmann u32 ibox_stat; 81067207b96SArnd Bergmann 81167207b96SArnd Bergmann if (len < 4) 81267207b96SArnd Bergmann return -EINVAL; 81367207b96SArnd Bergmann 814c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 815c9101bdbSChristoph Hellwig if (ret) 816c9101bdbSChristoph Hellwig return ret; 8178b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 8188b3d6663SArnd Bergmann spu_release(ctx); 81967207b96SArnd Bergmann 82067207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 82167207b96SArnd Bergmann return -EFAULT; 82267207b96SArnd Bergmann 82367207b96SArnd Bergmann return 4; 82467207b96SArnd Bergmann } 82567207b96SArnd Bergmann 8265dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = { 82767207b96SArnd Bergmann .open = spufs_pipe_open, 82867207b96SArnd Bergmann .read = spufs_ibox_stat_read, 829fc15351dSArnd Bergmann .llseek = no_llseek, 83067207b96SArnd Bergmann }; 83167207b96SArnd Bergmann 83267207b96SArnd Bergmann /* low-level mailbox write */ 8338b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 83467207b96SArnd Bergmann { 8358b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 83667207b96SArnd Bergmann } 83767207b96SArnd Bergmann 83867207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on) 83967207b96SArnd Bergmann { 8408b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 8418b3d6663SArnd Bergmann int ret; 8428b3d6663SArnd Bergmann 8438b3d6663SArnd Bergmann ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); 8448b3d6663SArnd Bergmann 8458b3d6663SArnd Bergmann return ret; 8468b3d6663SArnd Bergmann } 8478b3d6663SArnd Bergmann 8488b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 8498b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 8508b3d6663SArnd Bergmann { 8518b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 8528b3d6663SArnd Bergmann 853e65c2f6fSLuke Browning if (!ctx) 854e65c2f6fSLuke Browning return; 855e65c2f6fSLuke Browning 8568b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 8578b3d6663SArnd Bergmann kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); 85867207b96SArnd Bergmann } 85967207b96SArnd Bergmann 860cdcc89bbSArnd Bergmann /* 861cdcc89bbSArnd Bergmann * Write as many bytes to the interrupt mailbox as possible, until 862cdcc89bbSArnd Bergmann * one of the conditions becomes true: 863cdcc89bbSArnd Bergmann * 864cdcc89bbSArnd Bergmann * - the mailbox is full 865cdcc89bbSArnd Bergmann * - end of the user provided buffer 866cdcc89bbSArnd Bergmann * - end of the mapped area 867cdcc89bbSArnd Bergmann * 868cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 869027dfac6SMichael Ellerman * space is available, but return when we have been able to 870cdcc89bbSArnd Bergmann * write something. 871cdcc89bbSArnd Bergmann */ 87267207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 87367207b96SArnd Bergmann size_t len, loff_t *pos) 87467207b96SArnd Bergmann { 8758b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 876cdcc89bbSArnd Bergmann u32 wbox_data, __user *udata; 877cdcc89bbSArnd Bergmann ssize_t count; 87867207b96SArnd Bergmann 87967207b96SArnd Bergmann if (len < 4) 88067207b96SArnd Bergmann return -EINVAL; 88167207b96SArnd Bergmann 882cdcc89bbSArnd Bergmann udata = (void __user *)buf; 883cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_READ, buf, len)) 884cdcc89bbSArnd Bergmann return -EFAULT; 885cdcc89bbSArnd Bergmann 886cdcc89bbSArnd Bergmann if (__get_user(wbox_data, udata)) 88767207b96SArnd Bergmann return -EFAULT; 88867207b96SArnd Bergmann 889c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 890c9101bdbSChristoph Hellwig if (count) 891eebead5bSChristoph Hellwig goto out; 8928b3d6663SArnd Bergmann 893cdcc89bbSArnd Bergmann /* 894cdcc89bbSArnd Bergmann * make sure we can at least write one element, by waiting 895cdcc89bbSArnd Bergmann * in case of !O_NONBLOCK 896cdcc89bbSArnd Bergmann */ 897cdcc89bbSArnd Bergmann count = 0; 89867207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 899eebead5bSChristoph Hellwig if (!spu_wbox_write(ctx, wbox_data)) { 900cdcc89bbSArnd Bergmann count = -EAGAIN; 901eebead5bSChristoph Hellwig goto out_unlock; 902eebead5bSChristoph Hellwig } 90367207b96SArnd Bergmann } else { 904cdcc89bbSArnd Bergmann count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 905cdcc89bbSArnd Bergmann if (count) 906cdcc89bbSArnd Bergmann goto out; 907eebead5bSChristoph Hellwig } 908eebead5bSChristoph Hellwig 9098b3d6663SArnd Bergmann 91096de0e25SJan Engelhardt /* write as much as possible */ 911cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 912cdcc89bbSArnd Bergmann int ret; 913cdcc89bbSArnd Bergmann ret = __get_user(wbox_data, udata); 914cdcc89bbSArnd Bergmann if (ret) 915cdcc89bbSArnd Bergmann break; 916cdcc89bbSArnd Bergmann 917cdcc89bbSArnd Bergmann ret = spu_wbox_write(ctx, wbox_data); 918cdcc89bbSArnd Bergmann if (ret == 0) 919cdcc89bbSArnd Bergmann break; 920cdcc89bbSArnd Bergmann } 921cdcc89bbSArnd Bergmann 922eebead5bSChristoph Hellwig out_unlock: 923cdcc89bbSArnd Bergmann spu_release(ctx); 924eebead5bSChristoph Hellwig out: 925cdcc89bbSArnd Bergmann return count; 92667207b96SArnd Bergmann } 92767207b96SArnd Bergmann 92867207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) 92967207b96SArnd Bergmann { 9308b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 93167207b96SArnd Bergmann unsigned int mask; 93267207b96SArnd Bergmann 9338b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 93467207b96SArnd Bergmann 935c9101bdbSChristoph Hellwig /* 936c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 937c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 938c9101bdbSChristoph Hellwig */ 939c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 9403a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); 9413a843d7cSArnd Bergmann spu_release(ctx); 94267207b96SArnd Bergmann 94367207b96SArnd Bergmann return mask; 94467207b96SArnd Bergmann } 94567207b96SArnd Bergmann 9465dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = { 94767207b96SArnd Bergmann .open = spufs_pipe_open, 94867207b96SArnd Bergmann .write = spufs_wbox_write, 94967207b96SArnd Bergmann .poll = spufs_wbox_poll, 95067207b96SArnd Bergmann .fasync = spufs_wbox_fasync, 951fc15351dSArnd Bergmann .llseek = no_llseek, 95267207b96SArnd Bergmann }; 95367207b96SArnd Bergmann 95467207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 95567207b96SArnd Bergmann size_t len, loff_t *pos) 95667207b96SArnd Bergmann { 9578b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 958c9101bdbSChristoph Hellwig ssize_t ret; 95967207b96SArnd Bergmann u32 wbox_stat; 96067207b96SArnd Bergmann 96167207b96SArnd Bergmann if (len < 4) 96267207b96SArnd Bergmann return -EINVAL; 96367207b96SArnd Bergmann 964c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 965c9101bdbSChristoph Hellwig if (ret) 966c9101bdbSChristoph Hellwig return ret; 9678b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 9688b3d6663SArnd Bergmann spu_release(ctx); 96967207b96SArnd Bergmann 97067207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 97167207b96SArnd Bergmann return -EFAULT; 97267207b96SArnd Bergmann 97367207b96SArnd Bergmann return 4; 97467207b96SArnd Bergmann } 97567207b96SArnd Bergmann 9765dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = { 97767207b96SArnd Bergmann .open = spufs_pipe_open, 97867207b96SArnd Bergmann .read = spufs_wbox_stat_read, 979fc15351dSArnd Bergmann .llseek = no_llseek, 98067207b96SArnd Bergmann }; 98167207b96SArnd Bergmann 9826df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file) 9836df10a82SMark Nutter { 9846df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 9856df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 98643c2bbd9SChristoph Hellwig 98747d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 9886df10a82SMark Nutter file->private_data = ctx; 98943c2bbd9SChristoph Hellwig if (!i->i_openers++) 9906df10a82SMark Nutter ctx->signal1 = inode->i_mapping; 99147d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 9926df10a82SMark Nutter return nonseekable_open(inode, file); 9936df10a82SMark Nutter } 9946df10a82SMark Nutter 99543c2bbd9SChristoph Hellwig static int 99643c2bbd9SChristoph Hellwig spufs_signal1_release(struct inode *inode, struct file *file) 99743c2bbd9SChristoph Hellwig { 99843c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 99943c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 100043c2bbd9SChristoph Hellwig 100147d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 100243c2bbd9SChristoph Hellwig if (!--i->i_openers) 100343c2bbd9SChristoph Hellwig ctx->signal1 = NULL; 100447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 100543c2bbd9SChristoph Hellwig return 0; 100643c2bbd9SChristoph Hellwig } 100743c2bbd9SChristoph Hellwig 1008bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf, 100967207b96SArnd Bergmann size_t len, loff_t *pos) 101067207b96SArnd Bergmann { 101117f88cebSDwayne Grant McConnell int ret = 0; 101267207b96SArnd Bergmann u32 data; 101367207b96SArnd Bergmann 101467207b96SArnd Bergmann if (len < 4) 101567207b96SArnd Bergmann return -EINVAL; 101667207b96SArnd Bergmann 101717f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[3]) { 101817f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[3]; 101917f88cebSDwayne Grant McConnell ret = 4; 102017f88cebSDwayne Grant McConnell } 10218b3d6663SArnd Bergmann 102217f88cebSDwayne Grant McConnell if (!ret) 102317f88cebSDwayne Grant McConnell goto out; 102417f88cebSDwayne Grant McConnell 102567207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 102667207b96SArnd Bergmann return -EFAULT; 102767207b96SArnd Bergmann 102817f88cebSDwayne Grant McConnell out: 102917f88cebSDwayne Grant McConnell return ret; 103067207b96SArnd Bergmann } 103167207b96SArnd Bergmann 1032bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 1033bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1034bf1ab978SDwayne Grant McConnell { 1035bf1ab978SDwayne Grant McConnell int ret; 1036bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1037bf1ab978SDwayne Grant McConnell 1038c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1039c9101bdbSChristoph Hellwig if (ret) 1040c9101bdbSChristoph Hellwig return ret; 1041bf1ab978SDwayne Grant McConnell ret = __spufs_signal1_read(ctx, buf, len, pos); 104227b1ea09SChristoph Hellwig spu_release_saved(ctx); 1043bf1ab978SDwayne Grant McConnell 1044bf1ab978SDwayne Grant McConnell return ret; 1045bf1ab978SDwayne Grant McConnell } 1046bf1ab978SDwayne Grant McConnell 104767207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 104867207b96SArnd Bergmann size_t len, loff_t *pos) 104967207b96SArnd Bergmann { 105067207b96SArnd Bergmann struct spu_context *ctx; 1051c9101bdbSChristoph Hellwig ssize_t ret; 105267207b96SArnd Bergmann u32 data; 105367207b96SArnd Bergmann 105467207b96SArnd Bergmann ctx = file->private_data; 105567207b96SArnd Bergmann 105667207b96SArnd Bergmann if (len < 4) 105767207b96SArnd Bergmann return -EINVAL; 105867207b96SArnd Bergmann 105967207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 106067207b96SArnd Bergmann return -EFAULT; 106167207b96SArnd Bergmann 1062c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1063c9101bdbSChristoph Hellwig if (ret) 1064c9101bdbSChristoph Hellwig return ret; 10658b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 10668b3d6663SArnd Bergmann spu_release(ctx); 106767207b96SArnd Bergmann 106867207b96SArnd Bergmann return 4; 106967207b96SArnd Bergmann } 107067207b96SArnd Bergmann 1071b1e2270fSNick Piggin static int 1072b1e2270fSNick Piggin spufs_signal1_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 10736df10a82SMark Nutter { 107487ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 107587ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE); 107687ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 107727d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 107827d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 107927d5bf2aSBenjamin Herrenschmidt */ 108087ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 108127d5bf2aSBenjamin Herrenschmidt #else 108227d5bf2aSBenjamin Herrenschmidt #error unsupported page size 108327d5bf2aSBenjamin Herrenschmidt #endif 10846df10a82SMark Nutter } 10856df10a82SMark Nutter 1086f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal1_mmap_vmops = { 1087b1e2270fSNick Piggin .fault = spufs_signal1_mmap_fault, 10886df10a82SMark Nutter }; 10896df10a82SMark Nutter 10906df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) 10916df10a82SMark Nutter { 10926df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 10936df10a82SMark Nutter return -EINVAL; 10946df10a82SMark Nutter 109578bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 109664b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 10976df10a82SMark Nutter 10986df10a82SMark Nutter vma->vm_ops = &spufs_signal1_mmap_vmops; 10996df10a82SMark Nutter return 0; 11006df10a82SMark Nutter } 11016df10a82SMark Nutter 11025dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = { 11036df10a82SMark Nutter .open = spufs_signal1_open, 110443c2bbd9SChristoph Hellwig .release = spufs_signal1_release, 110567207b96SArnd Bergmann .read = spufs_signal1_read, 110667207b96SArnd Bergmann .write = spufs_signal1_write, 11076df10a82SMark Nutter .mmap = spufs_signal1_mmap, 1108fc15351dSArnd Bergmann .llseek = no_llseek, 110967207b96SArnd Bergmann }; 111067207b96SArnd Bergmann 1111d054b36fSJeremy Kerr static const struct file_operations spufs_signal1_nosched_fops = { 1112d054b36fSJeremy Kerr .open = spufs_signal1_open, 1113d054b36fSJeremy Kerr .release = spufs_signal1_release, 1114d054b36fSJeremy Kerr .write = spufs_signal1_write, 1115d054b36fSJeremy Kerr .mmap = spufs_signal1_mmap, 1116fc15351dSArnd Bergmann .llseek = no_llseek, 1117d054b36fSJeremy Kerr }; 1118d054b36fSJeremy Kerr 11196df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file) 11206df10a82SMark Nutter { 11216df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 11226df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 112343c2bbd9SChristoph Hellwig 112447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 11256df10a82SMark Nutter file->private_data = ctx; 112643c2bbd9SChristoph Hellwig if (!i->i_openers++) 11276df10a82SMark Nutter ctx->signal2 = inode->i_mapping; 112847d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 11296df10a82SMark Nutter return nonseekable_open(inode, file); 11306df10a82SMark Nutter } 11316df10a82SMark Nutter 113243c2bbd9SChristoph Hellwig static int 113343c2bbd9SChristoph Hellwig spufs_signal2_release(struct inode *inode, struct file *file) 113443c2bbd9SChristoph Hellwig { 113543c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 113643c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 113743c2bbd9SChristoph Hellwig 113847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 113943c2bbd9SChristoph Hellwig if (!--i->i_openers) 114043c2bbd9SChristoph Hellwig ctx->signal2 = NULL; 114147d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 114243c2bbd9SChristoph Hellwig return 0; 114343c2bbd9SChristoph Hellwig } 114443c2bbd9SChristoph Hellwig 1145bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf, 114667207b96SArnd Bergmann size_t len, loff_t *pos) 114767207b96SArnd Bergmann { 114817f88cebSDwayne Grant McConnell int ret = 0; 114967207b96SArnd Bergmann u32 data; 115067207b96SArnd Bergmann 115167207b96SArnd Bergmann if (len < 4) 115267207b96SArnd Bergmann return -EINVAL; 115367207b96SArnd Bergmann 115417f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[4]) { 115517f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[4]; 115617f88cebSDwayne Grant McConnell ret = 4; 115717f88cebSDwayne Grant McConnell } 11588b3d6663SArnd Bergmann 115917f88cebSDwayne Grant McConnell if (!ret) 116017f88cebSDwayne Grant McConnell goto out; 116117f88cebSDwayne Grant McConnell 116267207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 116367207b96SArnd Bergmann return -EFAULT; 116467207b96SArnd Bergmann 116517f88cebSDwayne Grant McConnell out: 1166bf1ab978SDwayne Grant McConnell return ret; 1167bf1ab978SDwayne Grant McConnell } 1168bf1ab978SDwayne Grant McConnell 1169bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 1170bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1171bf1ab978SDwayne Grant McConnell { 1172bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1173bf1ab978SDwayne Grant McConnell int ret; 1174bf1ab978SDwayne Grant McConnell 1175c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1176c9101bdbSChristoph Hellwig if (ret) 1177c9101bdbSChristoph Hellwig return ret; 1178bf1ab978SDwayne Grant McConnell ret = __spufs_signal2_read(ctx, buf, len, pos); 117927b1ea09SChristoph Hellwig spu_release_saved(ctx); 1180bf1ab978SDwayne Grant McConnell 1181bf1ab978SDwayne Grant McConnell return ret; 118267207b96SArnd Bergmann } 118367207b96SArnd Bergmann 118467207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 118567207b96SArnd Bergmann size_t len, loff_t *pos) 118667207b96SArnd Bergmann { 118767207b96SArnd Bergmann struct spu_context *ctx; 1188c9101bdbSChristoph Hellwig ssize_t ret; 118967207b96SArnd Bergmann u32 data; 119067207b96SArnd Bergmann 119167207b96SArnd Bergmann ctx = file->private_data; 119267207b96SArnd Bergmann 119367207b96SArnd Bergmann if (len < 4) 119467207b96SArnd Bergmann return -EINVAL; 119567207b96SArnd Bergmann 119667207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 119767207b96SArnd Bergmann return -EFAULT; 119867207b96SArnd Bergmann 1199c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1200c9101bdbSChristoph Hellwig if (ret) 1201c9101bdbSChristoph Hellwig return ret; 12028b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 12038b3d6663SArnd Bergmann spu_release(ctx); 120467207b96SArnd Bergmann 120567207b96SArnd Bergmann return 4; 120667207b96SArnd Bergmann } 120767207b96SArnd Bergmann 120827d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1209b1e2270fSNick Piggin static int 1210b1e2270fSNick Piggin spufs_signal2_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 12116df10a82SMark Nutter { 121287ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 121387ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE); 121487ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 121527d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 121627d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 121727d5bf2aSBenjamin Herrenschmidt */ 121887ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 121927d5bf2aSBenjamin Herrenschmidt #else 122027d5bf2aSBenjamin Herrenschmidt #error unsupported page size 122127d5bf2aSBenjamin Herrenschmidt #endif 12226df10a82SMark Nutter } 12236df10a82SMark Nutter 1224f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal2_mmap_vmops = { 1225b1e2270fSNick Piggin .fault = spufs_signal2_mmap_fault, 12266df10a82SMark Nutter }; 12276df10a82SMark Nutter 12286df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) 12296df10a82SMark Nutter { 12306df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 12316df10a82SMark Nutter return -EINVAL; 12326df10a82SMark Nutter 123378bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 123464b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 12356df10a82SMark Nutter 12366df10a82SMark Nutter vma->vm_ops = &spufs_signal2_mmap_vmops; 12376df10a82SMark Nutter return 0; 12386df10a82SMark Nutter } 123927d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 124027d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL 124127d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 12426df10a82SMark Nutter 12435dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = { 12446df10a82SMark Nutter .open = spufs_signal2_open, 124543c2bbd9SChristoph Hellwig .release = spufs_signal2_release, 124667207b96SArnd Bergmann .read = spufs_signal2_read, 124767207b96SArnd Bergmann .write = spufs_signal2_write, 12486df10a82SMark Nutter .mmap = spufs_signal2_mmap, 1249fc15351dSArnd Bergmann .llseek = no_llseek, 125067207b96SArnd Bergmann }; 125167207b96SArnd Bergmann 1252d054b36fSJeremy Kerr static const struct file_operations spufs_signal2_nosched_fops = { 1253d054b36fSJeremy Kerr .open = spufs_signal2_open, 1254d054b36fSJeremy Kerr .release = spufs_signal2_release, 1255d054b36fSJeremy Kerr .write = spufs_signal2_write, 1256d054b36fSJeremy Kerr .mmap = spufs_signal2_mmap, 1257fc15351dSArnd Bergmann .llseek = no_llseek, 1258d054b36fSJeremy Kerr }; 1259d054b36fSJeremy Kerr 1260104f0cc2SMichael Ellerman /* 1261104f0cc2SMichael Ellerman * This is a wrapper around DEFINE_SIMPLE_ATTRIBUTE which does the 1262104f0cc2SMichael Ellerman * work of acquiring (or not) the SPU context before calling through 1263104f0cc2SMichael Ellerman * to the actual get routine. The set routine is called directly. 1264104f0cc2SMichael Ellerman */ 1265104f0cc2SMichael Ellerman #define SPU_ATTR_NOACQUIRE 0 1266104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE 1 1267104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE_SAVED 2 1268104f0cc2SMichael Ellerman 1269104f0cc2SMichael Ellerman #define DEFINE_SPUFS_ATTRIBUTE(__name, __get, __set, __fmt, __acquire) \ 1270197b1a82SChristoph Hellwig static int __##__get(void *data, u64 *val) \ 1271104f0cc2SMichael Ellerman { \ 1272104f0cc2SMichael Ellerman struct spu_context *ctx = data; \ 1273c9101bdbSChristoph Hellwig int ret = 0; \ 1274104f0cc2SMichael Ellerman \ 1275104f0cc2SMichael Ellerman if (__acquire == SPU_ATTR_ACQUIRE) { \ 1276c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); \ 1277c9101bdbSChristoph Hellwig if (ret) \ 1278c9101bdbSChristoph Hellwig return ret; \ 1279197b1a82SChristoph Hellwig *val = __get(ctx); \ 1280104f0cc2SMichael Ellerman spu_release(ctx); \ 1281104f0cc2SMichael Ellerman } else if (__acquire == SPU_ATTR_ACQUIRE_SAVED) { \ 1282c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); \ 1283c9101bdbSChristoph Hellwig if (ret) \ 1284c9101bdbSChristoph Hellwig return ret; \ 1285197b1a82SChristoph Hellwig *val = __get(ctx); \ 1286104f0cc2SMichael Ellerman spu_release_saved(ctx); \ 1287104f0cc2SMichael Ellerman } else \ 1288197b1a82SChristoph Hellwig *val = __get(ctx); \ 1289104f0cc2SMichael Ellerman \ 1290197b1a82SChristoph Hellwig return 0; \ 1291104f0cc2SMichael Ellerman } \ 1292197b1a82SChristoph Hellwig DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__name, __##__get, __set, __fmt); 1293104f0cc2SMichael Ellerman 1294197b1a82SChristoph Hellwig static int spufs_signal1_type_set(void *data, u64 val) 129567207b96SArnd Bergmann { 129667207b96SArnd Bergmann struct spu_context *ctx = data; 1297c9101bdbSChristoph Hellwig int ret; 129867207b96SArnd Bergmann 1299c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1300c9101bdbSChristoph Hellwig if (ret) 1301c9101bdbSChristoph Hellwig return ret; 13028b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 13038b3d6663SArnd Bergmann spu_release(ctx); 1304197b1a82SChristoph Hellwig 1305197b1a82SChristoph Hellwig return 0; 130667207b96SArnd Bergmann } 130767207b96SArnd Bergmann 1308104f0cc2SMichael Ellerman static u64 spufs_signal1_type_get(struct spu_context *ctx) 1309bf1ab978SDwayne Grant McConnell { 1310bf1ab978SDwayne Grant McConnell return ctx->ops->signal1_type_get(ctx); 1311bf1ab978SDwayne Grant McConnell } 1312104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 1313af8b44e0SJeremy Kerr spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 1314bf1ab978SDwayne Grant McConnell 131567207b96SArnd Bergmann 1316197b1a82SChristoph Hellwig static int spufs_signal2_type_set(void *data, u64 val) 131767207b96SArnd Bergmann { 131867207b96SArnd Bergmann struct spu_context *ctx = data; 1319c9101bdbSChristoph Hellwig int ret; 132067207b96SArnd Bergmann 1321c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1322c9101bdbSChristoph Hellwig if (ret) 1323c9101bdbSChristoph Hellwig return ret; 13248b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 13258b3d6663SArnd Bergmann spu_release(ctx); 1326197b1a82SChristoph Hellwig 1327197b1a82SChristoph Hellwig return 0; 132867207b96SArnd Bergmann } 132967207b96SArnd Bergmann 1330104f0cc2SMichael Ellerman static u64 spufs_signal2_type_get(struct spu_context *ctx) 1331bf1ab978SDwayne Grant McConnell { 1332bf1ab978SDwayne Grant McConnell return ctx->ops->signal2_type_get(ctx); 1333bf1ab978SDwayne Grant McConnell } 1334104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 1335af8b44e0SJeremy Kerr spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 133667207b96SArnd Bergmann 133727d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1338b1e2270fSNick Piggin static int 1339b1e2270fSNick Piggin spufs_mss_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 1340d9379c4bSarnd@arndb.de { 134187ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_MSS_MAP_SIZE); 1342d9379c4bSarnd@arndb.de } 1343d9379c4bSarnd@arndb.de 1344f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mss_mmap_vmops = { 1345b1e2270fSNick Piggin .fault = spufs_mss_mmap_fault, 1346d9379c4bSarnd@arndb.de }; 1347d9379c4bSarnd@arndb.de 1348d9379c4bSarnd@arndb.de /* 1349d9379c4bSarnd@arndb.de * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 1350d9379c4bSarnd@arndb.de */ 1351d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) 1352d9379c4bSarnd@arndb.de { 1353d9379c4bSarnd@arndb.de if (!(vma->vm_flags & VM_SHARED)) 1354d9379c4bSarnd@arndb.de return -EINVAL; 1355d9379c4bSarnd@arndb.de 135678bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 135764b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 1358d9379c4bSarnd@arndb.de 1359d9379c4bSarnd@arndb.de vma->vm_ops = &spufs_mss_mmap_vmops; 1360d9379c4bSarnd@arndb.de return 0; 1361d9379c4bSarnd@arndb.de } 136227d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 136327d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL 136427d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1365d9379c4bSarnd@arndb.de 1366d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file) 1367d9379c4bSarnd@arndb.de { 1368d9379c4bSarnd@arndb.de struct spufs_inode_info *i = SPUFS_I(inode); 136917e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 1370d9379c4bSarnd@arndb.de 1371d9379c4bSarnd@arndb.de file->private_data = i->i_ctx; 137243c2bbd9SChristoph Hellwig 137347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 137443c2bbd9SChristoph Hellwig if (!i->i_openers++) 137517e0e270SBenjamin Herrenschmidt ctx->mss = inode->i_mapping; 137647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1377d9379c4bSarnd@arndb.de return nonseekable_open(inode, file); 1378d9379c4bSarnd@arndb.de } 1379d9379c4bSarnd@arndb.de 138043c2bbd9SChristoph Hellwig static int 138143c2bbd9SChristoph Hellwig spufs_mss_release(struct inode *inode, struct file *file) 138243c2bbd9SChristoph Hellwig { 138343c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 138443c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 138543c2bbd9SChristoph Hellwig 138647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 138743c2bbd9SChristoph Hellwig if (!--i->i_openers) 138843c2bbd9SChristoph Hellwig ctx->mss = NULL; 138947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 139043c2bbd9SChristoph Hellwig return 0; 139143c2bbd9SChristoph Hellwig } 139243c2bbd9SChristoph Hellwig 13935dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = { 1394d9379c4bSarnd@arndb.de .open = spufs_mss_open, 139543c2bbd9SChristoph Hellwig .release = spufs_mss_release, 1396d9379c4bSarnd@arndb.de .mmap = spufs_mss_mmap, 1397fc15351dSArnd Bergmann .llseek = no_llseek, 139827d5bf2aSBenjamin Herrenschmidt }; 139927d5bf2aSBenjamin Herrenschmidt 1400b1e2270fSNick Piggin static int 1401b1e2270fSNick Piggin spufs_psmap_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 140227d5bf2aSBenjamin Herrenschmidt { 140387ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_PS_MAP_SIZE); 140427d5bf2aSBenjamin Herrenschmidt } 140527d5bf2aSBenjamin Herrenschmidt 1406f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_psmap_mmap_vmops = { 1407b1e2270fSNick Piggin .fault = spufs_psmap_mmap_fault, 140827d5bf2aSBenjamin Herrenschmidt }; 140927d5bf2aSBenjamin Herrenschmidt 141027d5bf2aSBenjamin Herrenschmidt /* 141127d5bf2aSBenjamin Herrenschmidt * mmap support for full problem state area [0x00000 - 0x1ffff]. 141227d5bf2aSBenjamin Herrenschmidt */ 141327d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) 141427d5bf2aSBenjamin Herrenschmidt { 141527d5bf2aSBenjamin Herrenschmidt if (!(vma->vm_flags & VM_SHARED)) 141627d5bf2aSBenjamin Herrenschmidt return -EINVAL; 141727d5bf2aSBenjamin Herrenschmidt 141878bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 141964b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 142027d5bf2aSBenjamin Herrenschmidt 142127d5bf2aSBenjamin Herrenschmidt vma->vm_ops = &spufs_psmap_mmap_vmops; 142227d5bf2aSBenjamin Herrenschmidt return 0; 142327d5bf2aSBenjamin Herrenschmidt } 142427d5bf2aSBenjamin Herrenschmidt 142527d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file) 142627d5bf2aSBenjamin Herrenschmidt { 142727d5bf2aSBenjamin Herrenschmidt struct spufs_inode_info *i = SPUFS_I(inode); 142817e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 142927d5bf2aSBenjamin Herrenschmidt 143047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 143127d5bf2aSBenjamin Herrenschmidt file->private_data = i->i_ctx; 143243c2bbd9SChristoph Hellwig if (!i->i_openers++) 143317e0e270SBenjamin Herrenschmidt ctx->psmap = inode->i_mapping; 143447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 143527d5bf2aSBenjamin Herrenschmidt return nonseekable_open(inode, file); 143627d5bf2aSBenjamin Herrenschmidt } 143727d5bf2aSBenjamin Herrenschmidt 143843c2bbd9SChristoph Hellwig static int 143943c2bbd9SChristoph Hellwig spufs_psmap_release(struct inode *inode, struct file *file) 144043c2bbd9SChristoph Hellwig { 144143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 144243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 144343c2bbd9SChristoph Hellwig 144447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 144543c2bbd9SChristoph Hellwig if (!--i->i_openers) 144643c2bbd9SChristoph Hellwig ctx->psmap = NULL; 144747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 144843c2bbd9SChristoph Hellwig return 0; 144943c2bbd9SChristoph Hellwig } 145043c2bbd9SChristoph Hellwig 14515dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = { 145227d5bf2aSBenjamin Herrenschmidt .open = spufs_psmap_open, 145343c2bbd9SChristoph Hellwig .release = spufs_psmap_release, 145427d5bf2aSBenjamin Herrenschmidt .mmap = spufs_psmap_mmap, 1455fc15351dSArnd Bergmann .llseek = no_llseek, 1456d9379c4bSarnd@arndb.de }; 1457d9379c4bSarnd@arndb.de 1458d9379c4bSarnd@arndb.de 145927d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1460b1e2270fSNick Piggin static int 1461b1e2270fSNick Piggin spufs_mfc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 14626df10a82SMark Nutter { 146387ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x3000, SPUFS_MFC_MAP_SIZE); 14646df10a82SMark Nutter } 14656df10a82SMark Nutter 1466f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mfc_mmap_vmops = { 1467b1e2270fSNick Piggin .fault = spufs_mfc_mmap_fault, 14686df10a82SMark Nutter }; 14696df10a82SMark Nutter 14706df10a82SMark Nutter /* 14716df10a82SMark Nutter * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 14726df10a82SMark Nutter */ 14736df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) 14746df10a82SMark Nutter { 14756df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 14766df10a82SMark Nutter return -EINVAL; 14776df10a82SMark Nutter 147878bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 147964b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 14806df10a82SMark Nutter 14816df10a82SMark Nutter vma->vm_ops = &spufs_mfc_mmap_vmops; 14826df10a82SMark Nutter return 0; 14836df10a82SMark Nutter } 148427d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 148527d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL 148627d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1487a33a7d73SArnd Bergmann 1488a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 1489a33a7d73SArnd Bergmann { 1490a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1491a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 1492a33a7d73SArnd Bergmann 1493a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 1494a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 1495a33a7d73SArnd Bergmann return -EINVAL; 1496a33a7d73SArnd Bergmann 1497a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 1498a33a7d73SArnd Bergmann return -EBUSY; 1499a33a7d73SArnd Bergmann 150047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1501a33a7d73SArnd Bergmann file->private_data = ctx; 150243c2bbd9SChristoph Hellwig if (!i->i_openers++) 150317e0e270SBenjamin Herrenschmidt ctx->mfc = inode->i_mapping; 150447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1505a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 1506a33a7d73SArnd Bergmann } 1507a33a7d73SArnd Bergmann 150843c2bbd9SChristoph Hellwig static int 150943c2bbd9SChristoph Hellwig spufs_mfc_release(struct inode *inode, struct file *file) 151043c2bbd9SChristoph Hellwig { 151143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 151243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 151343c2bbd9SChristoph Hellwig 151447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 151543c2bbd9SChristoph Hellwig if (!--i->i_openers) 151643c2bbd9SChristoph Hellwig ctx->mfc = NULL; 151747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 151843c2bbd9SChristoph Hellwig return 0; 151943c2bbd9SChristoph Hellwig } 152043c2bbd9SChristoph Hellwig 1521a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 1522a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 1523a33a7d73SArnd Bergmann { 1524a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 1525a33a7d73SArnd Bergmann 1526e65c2f6fSLuke Browning if (!ctx) 1527e65c2f6fSLuke Browning return; 1528e65c2f6fSLuke Browning 1529a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 1530a33a7d73SArnd Bergmann 1531e48b1b45SHarvey Harrison pr_debug("%s %s\n", __func__, spu->name); 1532a33a7d73SArnd Bergmann if (ctx->mfc_fasync) { 1533a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1534a33a7d73SArnd Bergmann unsigned int mask; 1535a33a7d73SArnd Bergmann 1536a33a7d73SArnd Bergmann /* no need for spu_acquire in interrupt context */ 1537a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1538a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1539a33a7d73SArnd Bergmann 1540a33a7d73SArnd Bergmann mask = 0; 1541a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1542a33a7d73SArnd Bergmann mask |= POLLOUT; 1543a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1544a33a7d73SArnd Bergmann mask |= POLLIN; 1545a33a7d73SArnd Bergmann 1546a33a7d73SArnd Bergmann kill_fasync(&ctx->mfc_fasync, SIGIO, mask); 1547a33a7d73SArnd Bergmann } 1548a33a7d73SArnd Bergmann } 1549a33a7d73SArnd Bergmann 1550a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 1551a33a7d73SArnd Bergmann { 1552a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 1553a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 1554a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 1555a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 1556a33a7d73SArnd Bergmann if (*status) 1557a33a7d73SArnd Bergmann return 1; 1558a33a7d73SArnd Bergmann 1559a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 1560a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 1561a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1562a33a7d73SArnd Bergmann return 0; 1563a33a7d73SArnd Bergmann } 1564a33a7d73SArnd Bergmann 1565a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 1566a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1567a33a7d73SArnd Bergmann { 1568a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1569a33a7d73SArnd Bergmann int ret = -EINVAL; 1570a33a7d73SArnd Bergmann u32 status; 1571a33a7d73SArnd Bergmann 1572a33a7d73SArnd Bergmann if (size != 4) 1573a33a7d73SArnd Bergmann goto out; 1574a33a7d73SArnd Bergmann 1575c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1576c9101bdbSChristoph Hellwig if (ret) 1577c9101bdbSChristoph Hellwig return ret; 1578c9101bdbSChristoph Hellwig 1579c9101bdbSChristoph Hellwig ret = -EINVAL; 1580a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1581a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 1582a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 1583a33a7d73SArnd Bergmann ret = -EAGAIN; 1584a33a7d73SArnd Bergmann else 1585c9101bdbSChristoph Hellwig /* XXX(hch): shouldn't we clear ret here? */ 1586a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 1587a33a7d73SArnd Bergmann } else { 1588a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1589a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 1590a33a7d73SArnd Bergmann if (ret) 1591a33a7d73SArnd Bergmann goto out; 1592eebead5bSChristoph Hellwig } 1593eebead5bSChristoph Hellwig spu_release(ctx); 1594a33a7d73SArnd Bergmann 1595a33a7d73SArnd Bergmann ret = 4; 1596a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 1597a33a7d73SArnd Bergmann ret = -EFAULT; 1598a33a7d73SArnd Bergmann 1599a33a7d73SArnd Bergmann out: 1600a33a7d73SArnd Bergmann return ret; 1601a33a7d73SArnd Bergmann } 1602a33a7d73SArnd Bergmann 1603a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 1604a33a7d73SArnd Bergmann { 16059477e455SStephen Rothwell pr_debug("queueing DMA %x %llx %x %x %x\n", cmd->lsa, 1606a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 1607a33a7d73SArnd Bergmann 1608a33a7d73SArnd Bergmann switch (cmd->cmd) { 1609a33a7d73SArnd Bergmann case MFC_PUT_CMD: 1610a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 1611a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 1612a33a7d73SArnd Bergmann case MFC_GET_CMD: 1613a33a7d73SArnd Bergmann case MFC_GETF_CMD: 1614a33a7d73SArnd Bergmann case MFC_GETB_CMD: 1615a33a7d73SArnd Bergmann break; 1616a33a7d73SArnd Bergmann default: 1617a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 1618a33a7d73SArnd Bergmann return -EIO; 1619a33a7d73SArnd Bergmann } 1620a33a7d73SArnd Bergmann 1621a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 16229477e455SStephen Rothwell pr_debug("invalid DMA alignment, ea %llx lsa %x\n", 1623a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 1624a33a7d73SArnd Bergmann return -EIO; 1625a33a7d73SArnd Bergmann } 1626a33a7d73SArnd Bergmann 1627a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 1628a33a7d73SArnd Bergmann case 1: 1629a33a7d73SArnd Bergmann break; 1630a33a7d73SArnd Bergmann case 2: 1631a33a7d73SArnd Bergmann if (cmd->lsa & 1) 1632a33a7d73SArnd Bergmann goto error; 1633a33a7d73SArnd Bergmann break; 1634a33a7d73SArnd Bergmann case 4: 1635a33a7d73SArnd Bergmann if (cmd->lsa & 3) 1636a33a7d73SArnd Bergmann goto error; 1637a33a7d73SArnd Bergmann break; 1638a33a7d73SArnd Bergmann case 8: 1639a33a7d73SArnd Bergmann if (cmd->lsa & 7) 1640a33a7d73SArnd Bergmann goto error; 1641a33a7d73SArnd Bergmann break; 1642a33a7d73SArnd Bergmann case 0: 1643a33a7d73SArnd Bergmann if (cmd->lsa & 15) 1644a33a7d73SArnd Bergmann goto error; 1645a33a7d73SArnd Bergmann break; 1646a33a7d73SArnd Bergmann error: 1647a33a7d73SArnd Bergmann default: 1648a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 1649a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 1650a33a7d73SArnd Bergmann return -EIO; 1651a33a7d73SArnd Bergmann } 1652a33a7d73SArnd Bergmann 1653a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 1654a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 1655a33a7d73SArnd Bergmann return -EIO; 1656a33a7d73SArnd Bergmann } 1657a33a7d73SArnd Bergmann 1658a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 1659a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 1660a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 1661a33a7d73SArnd Bergmann return -EIO; 1662a33a7d73SArnd Bergmann } 1663a33a7d73SArnd Bergmann 1664a33a7d73SArnd Bergmann if (cmd->class) { 1665a33a7d73SArnd Bergmann /* not supported in this version */ 1666a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 1667a33a7d73SArnd Bergmann return -EIO; 1668a33a7d73SArnd Bergmann } 1669a33a7d73SArnd Bergmann 1670a33a7d73SArnd Bergmann return 0; 1671a33a7d73SArnd Bergmann } 1672a33a7d73SArnd Bergmann 1673a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 1674a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 1675a33a7d73SArnd Bergmann int *error) 1676a33a7d73SArnd Bergmann { 1677a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1678a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 1679a33a7d73SArnd Bergmann /* wait for any tag group to complete 1680a33a7d73SArnd Bergmann so we have space for the new command */ 1681a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1682a33a7d73SArnd Bergmann /* try again, because the queue might be 1683a33a7d73SArnd Bergmann empty again */ 1684a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1685a33a7d73SArnd Bergmann if (*error == -EAGAIN) 1686a33a7d73SArnd Bergmann return 0; 1687a33a7d73SArnd Bergmann } 1688a33a7d73SArnd Bergmann return 1; 1689a33a7d73SArnd Bergmann } 1690a33a7d73SArnd Bergmann 1691a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 1692a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1693a33a7d73SArnd Bergmann { 1694a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1695a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 1696a33a7d73SArnd Bergmann int ret = -EINVAL; 1697a33a7d73SArnd Bergmann 1698a33a7d73SArnd Bergmann if (size != sizeof cmd) 1699a33a7d73SArnd Bergmann goto out; 1700a33a7d73SArnd Bergmann 1701a33a7d73SArnd Bergmann ret = -EFAULT; 1702a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 1703a33a7d73SArnd Bergmann goto out; 1704a33a7d73SArnd Bergmann 1705a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 1706a33a7d73SArnd Bergmann if (ret) 1707a33a7d73SArnd Bergmann goto out; 1708a33a7d73SArnd Bergmann 1709c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1710c9101bdbSChristoph Hellwig if (ret) 1711c9101bdbSChristoph Hellwig goto out; 1712c9101bdbSChristoph Hellwig 171333bfd7a7SArnd Bergmann ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 1714577f8f10SAkinobu Mita if (ret) 1715577f8f10SAkinobu Mita goto out; 1716577f8f10SAkinobu Mita 1717a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1718a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 1719a33a7d73SArnd Bergmann } else { 1720a33a7d73SArnd Bergmann int status; 1721a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1722a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 1723eebead5bSChristoph Hellwig if (ret) 1724eebead5bSChristoph Hellwig goto out; 1725a33a7d73SArnd Bergmann if (status) 1726a33a7d73SArnd Bergmann ret = status; 1727a33a7d73SArnd Bergmann } 1728a33a7d73SArnd Bergmann 1729a33a7d73SArnd Bergmann if (ret) 1730933b0e35SKazunori Asayama goto out_unlock; 1731a33a7d73SArnd Bergmann 1732a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 17333692dc66SMasato Noguchi ret = size; 1734a33a7d73SArnd Bergmann 1735933b0e35SKazunori Asayama out_unlock: 1736933b0e35SKazunori Asayama spu_release(ctx); 1737a33a7d73SArnd Bergmann out: 1738a33a7d73SArnd Bergmann return ret; 1739a33a7d73SArnd Bergmann } 1740a33a7d73SArnd Bergmann 1741a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) 1742a33a7d73SArnd Bergmann { 1743a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1744a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1745a33a7d73SArnd Bergmann unsigned int mask; 1746a33a7d73SArnd Bergmann 1747933b0e35SKazunori Asayama poll_wait(file, &ctx->mfc_wq, wait); 1748933b0e35SKazunori Asayama 1749c9101bdbSChristoph Hellwig /* 1750c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 1751c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 1752c9101bdbSChristoph Hellwig */ 1753c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 1754a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 1755a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1756a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1757a33a7d73SArnd Bergmann spu_release(ctx); 1758a33a7d73SArnd Bergmann 1759a33a7d73SArnd Bergmann mask = 0; 1760a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1761a33a7d73SArnd Bergmann mask |= POLLOUT | POLLWRNORM; 1762a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1763a33a7d73SArnd Bergmann mask |= POLLIN | POLLRDNORM; 1764a33a7d73SArnd Bergmann 1765e48b1b45SHarvey Harrison pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__, 1766a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 1767a33a7d73SArnd Bergmann 1768a33a7d73SArnd Bergmann return mask; 1769a33a7d73SArnd Bergmann } 1770a33a7d73SArnd Bergmann 177173b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id) 1772a33a7d73SArnd Bergmann { 1773a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1774a33a7d73SArnd Bergmann int ret; 1775a33a7d73SArnd Bergmann 1776c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1777c9101bdbSChristoph Hellwig if (ret) 1778eebead5bSChristoph Hellwig goto out; 1779a33a7d73SArnd Bergmann #if 0 1780a33a7d73SArnd Bergmann /* this currently hangs */ 1781a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1782a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 1783a33a7d73SArnd Bergmann if (ret) 1784a33a7d73SArnd Bergmann goto out; 1785a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1786a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 1787eebead5bSChristoph Hellwig if (ret) 1788eebead5bSChristoph Hellwig goto out; 1789a33a7d73SArnd Bergmann #else 1790a33a7d73SArnd Bergmann ret = 0; 1791a33a7d73SArnd Bergmann #endif 1792a33a7d73SArnd Bergmann spu_release(ctx); 1793eebead5bSChristoph Hellwig out: 1794a33a7d73SArnd Bergmann return ret; 1795a33a7d73SArnd Bergmann } 1796a33a7d73SArnd Bergmann 179702c24a82SJosef Bacik static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync) 1798a33a7d73SArnd Bergmann { 1799496ad9aaSAl Viro struct inode *inode = file_inode(file); 180002c24a82SJosef Bacik int err = filemap_write_and_wait_range(inode->i_mapping, start, end); 180102c24a82SJosef Bacik if (!err) { 18025955102cSAl Viro inode_lock(inode); 180302c24a82SJosef Bacik err = spufs_mfc_flush(file, NULL); 18045955102cSAl Viro inode_unlock(inode); 180502c24a82SJosef Bacik } 180602c24a82SJosef Bacik return err; 1807a33a7d73SArnd Bergmann } 1808a33a7d73SArnd Bergmann 1809a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on) 1810a33a7d73SArnd Bergmann { 1811a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1812a33a7d73SArnd Bergmann 1813a33a7d73SArnd Bergmann return fasync_helper(fd, file, on, &ctx->mfc_fasync); 1814a33a7d73SArnd Bergmann } 1815a33a7d73SArnd Bergmann 18165dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = { 1817a33a7d73SArnd Bergmann .open = spufs_mfc_open, 181843c2bbd9SChristoph Hellwig .release = spufs_mfc_release, 1819a33a7d73SArnd Bergmann .read = spufs_mfc_read, 1820a33a7d73SArnd Bergmann .write = spufs_mfc_write, 1821a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 1822a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 1823a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 1824a33a7d73SArnd Bergmann .fasync = spufs_mfc_fasync, 18256df10a82SMark Nutter .mmap = spufs_mfc_mmap, 1826fc15351dSArnd Bergmann .llseek = no_llseek, 1827a33a7d73SArnd Bergmann }; 1828a33a7d73SArnd Bergmann 1829197b1a82SChristoph Hellwig static int spufs_npc_set(void *data, u64 val) 183067207b96SArnd Bergmann { 183167207b96SArnd Bergmann struct spu_context *ctx = data; 1832c9101bdbSChristoph Hellwig int ret; 1833c9101bdbSChristoph Hellwig 1834c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1835c9101bdbSChristoph Hellwig if (ret) 1836c9101bdbSChristoph Hellwig return ret; 18378b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 18388b3d6663SArnd Bergmann spu_release(ctx); 1839197b1a82SChristoph Hellwig 1840197b1a82SChristoph Hellwig return 0; 184167207b96SArnd Bergmann } 184267207b96SArnd Bergmann 1843104f0cc2SMichael Ellerman static u64 spufs_npc_get(struct spu_context *ctx) 184478810ff6SMichael Ellerman { 184578810ff6SMichael Ellerman return ctx->ops->npc_read(ctx); 184678810ff6SMichael Ellerman } 1847104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, 1848104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE); 184967207b96SArnd Bergmann 1850197b1a82SChristoph Hellwig static int spufs_decr_set(void *data, u64 val) 18518b3d6663SArnd Bergmann { 18528b3d6663SArnd Bergmann struct spu_context *ctx = data; 18538b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1854c9101bdbSChristoph Hellwig int ret; 1855c9101bdbSChristoph Hellwig 1856c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1857c9101bdbSChristoph Hellwig if (ret) 1858c9101bdbSChristoph Hellwig return ret; 18598b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 186027b1ea09SChristoph Hellwig spu_release_saved(ctx); 1861197b1a82SChristoph Hellwig 1862197b1a82SChristoph Hellwig return 0; 18638b3d6663SArnd Bergmann } 18648b3d6663SArnd Bergmann 1865104f0cc2SMichael Ellerman static u64 spufs_decr_get(struct spu_context *ctx) 18668b3d6663SArnd Bergmann { 18678b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1868bf1ab978SDwayne Grant McConnell return lscsa->decr.slot[0]; 1869bf1ab978SDwayne Grant McConnell } 1870104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 1871104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED); 18728b3d6663SArnd Bergmann 1873197b1a82SChristoph Hellwig static int spufs_decr_status_set(void *data, u64 val) 18748b3d6663SArnd Bergmann { 18758b3d6663SArnd Bergmann struct spu_context *ctx = data; 1876c9101bdbSChristoph Hellwig int ret; 1877c9101bdbSChristoph Hellwig 1878c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1879c9101bdbSChristoph Hellwig if (ret) 1880c9101bdbSChristoph Hellwig return ret; 1881d40a01d4SMasato Noguchi if (val) 1882d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING; 1883d40a01d4SMasato Noguchi else 1884d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW &= ~MFC_CNTL_DECREMENTER_RUNNING; 188527b1ea09SChristoph Hellwig spu_release_saved(ctx); 1886197b1a82SChristoph Hellwig 1887197b1a82SChristoph Hellwig return 0; 18888b3d6663SArnd Bergmann } 18898b3d6663SArnd Bergmann 1890104f0cc2SMichael Ellerman static u64 spufs_decr_status_get(struct spu_context *ctx) 18918b3d6663SArnd Bergmann { 1892d40a01d4SMasato Noguchi if (ctx->csa.priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) 1893d40a01d4SMasato Noguchi return SPU_DECR_STATUS_RUNNING; 1894d40a01d4SMasato Noguchi else 1895d40a01d4SMasato Noguchi return 0; 1896bf1ab978SDwayne Grant McConnell } 1897104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 1898104f0cc2SMichael Ellerman spufs_decr_status_set, "0x%llx\n", 1899104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 19008b3d6663SArnd Bergmann 1901197b1a82SChristoph Hellwig static int spufs_event_mask_set(void *data, u64 val) 19028b3d6663SArnd Bergmann { 19038b3d6663SArnd Bergmann struct spu_context *ctx = data; 19048b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1905c9101bdbSChristoph Hellwig int ret; 1906c9101bdbSChristoph Hellwig 1907c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1908c9101bdbSChristoph Hellwig if (ret) 1909c9101bdbSChristoph Hellwig return ret; 19108b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 191127b1ea09SChristoph Hellwig spu_release_saved(ctx); 1912197b1a82SChristoph Hellwig 1913197b1a82SChristoph Hellwig return 0; 19148b3d6663SArnd Bergmann } 19158b3d6663SArnd Bergmann 1916104f0cc2SMichael Ellerman static u64 spufs_event_mask_get(struct spu_context *ctx) 19178b3d6663SArnd Bergmann { 19188b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1919bf1ab978SDwayne Grant McConnell return lscsa->event_mask.slot[0]; 1920bf1ab978SDwayne Grant McConnell } 1921bf1ab978SDwayne Grant McConnell 1922104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 1923104f0cc2SMichael Ellerman spufs_event_mask_set, "0x%llx\n", 1924104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 19258b3d6663SArnd Bergmann 1926104f0cc2SMichael Ellerman static u64 spufs_event_status_get(struct spu_context *ctx) 1927b9e3bd77SDwayne Grant McConnell { 1928b9e3bd77SDwayne Grant McConnell struct spu_state *state = &ctx->csa; 1929b9e3bd77SDwayne Grant McConnell u64 stat; 1930b9e3bd77SDwayne Grant McConnell stat = state->spu_chnlcnt_RW[0]; 1931b9e3bd77SDwayne Grant McConnell if (stat) 1932bf1ab978SDwayne Grant McConnell return state->spu_chnldata_RW[0]; 1933bf1ab978SDwayne Grant McConnell return 0; 1934bf1ab978SDwayne Grant McConnell } 1935104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get, 1936104f0cc2SMichael Ellerman NULL, "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 1937b9e3bd77SDwayne Grant McConnell 1938197b1a82SChristoph Hellwig static int spufs_srr0_set(void *data, u64 val) 19398b3d6663SArnd Bergmann { 19408b3d6663SArnd Bergmann struct spu_context *ctx = data; 19418b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1942c9101bdbSChristoph Hellwig int ret; 1943c9101bdbSChristoph Hellwig 1944c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1945c9101bdbSChristoph Hellwig if (ret) 1946c9101bdbSChristoph Hellwig return ret; 19478b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 194827b1ea09SChristoph Hellwig spu_release_saved(ctx); 1949197b1a82SChristoph Hellwig 1950197b1a82SChristoph Hellwig return 0; 19518b3d6663SArnd Bergmann } 19528b3d6663SArnd Bergmann 1953104f0cc2SMichael Ellerman static u64 spufs_srr0_get(struct spu_context *ctx) 19548b3d6663SArnd Bergmann { 19558b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1956104f0cc2SMichael Ellerman return lscsa->srr0.slot[0]; 19578b3d6663SArnd Bergmann } 1958104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 1959104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 19608b3d6663SArnd Bergmann 1961104f0cc2SMichael Ellerman static u64 spufs_id_get(struct spu_context *ctx) 19627b1a7014Sarnd@arndb.de { 19637b1a7014Sarnd@arndb.de u64 num; 19647b1a7014Sarnd@arndb.de 19657b1a7014Sarnd@arndb.de if (ctx->state == SPU_STATE_RUNNABLE) 19667b1a7014Sarnd@arndb.de num = ctx->spu->number; 19677b1a7014Sarnd@arndb.de else 19687b1a7014Sarnd@arndb.de num = (unsigned int)-1; 19697b1a7014Sarnd@arndb.de 19707b1a7014Sarnd@arndb.de return num; 19717b1a7014Sarnd@arndb.de } 1972104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n", 1973104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE) 19747b1a7014Sarnd@arndb.de 1975104f0cc2SMichael Ellerman static u64 spufs_object_id_get(struct spu_context *ctx) 1976bf1ab978SDwayne Grant McConnell { 1977bf1ab978SDwayne Grant McConnell /* FIXME: Should there really be no locking here? */ 1978104f0cc2SMichael Ellerman return ctx->object_id; 1979bf1ab978SDwayne Grant McConnell } 1980bf1ab978SDwayne Grant McConnell 1981197b1a82SChristoph Hellwig static int spufs_object_id_set(void *data, u64 id) 198286767277SArnd Bergmann { 198386767277SArnd Bergmann struct spu_context *ctx = data; 198486767277SArnd Bergmann ctx->object_id = id; 1985197b1a82SChristoph Hellwig 1986197b1a82SChristoph Hellwig return 0; 198786767277SArnd Bergmann } 198886767277SArnd Bergmann 1989104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, 1990104f0cc2SMichael Ellerman spufs_object_id_set, "0x%llx\n", SPU_ATTR_NOACQUIRE); 199186767277SArnd Bergmann 1992104f0cc2SMichael Ellerman static u64 spufs_lslr_get(struct spu_context *ctx) 1993bf1ab978SDwayne Grant McConnell { 1994bf1ab978SDwayne Grant McConnell return ctx->csa.priv2.spu_lslr_RW; 1995bf1ab978SDwayne Grant McConnell } 1996104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n", 1997104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 1998b9e3bd77SDwayne Grant McConnell 1999b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file) 2000b9e3bd77SDwayne Grant McConnell { 2001b9e3bd77SDwayne Grant McConnell struct spufs_inode_info *i = SPUFS_I(inode); 2002b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = i->i_ctx; 2003b9e3bd77SDwayne Grant McConnell file->private_data = ctx; 2004b9e3bd77SDwayne Grant McConnell return 0; 2005b9e3bd77SDwayne Grant McConnell } 2006b9e3bd77SDwayne Grant McConnell 2007cbe709c1SBenjamin Herrenschmidt static int spufs_caps_show(struct seq_file *s, void *private) 2008cbe709c1SBenjamin Herrenschmidt { 2009cbe709c1SBenjamin Herrenschmidt struct spu_context *ctx = s->private; 2010cbe709c1SBenjamin Herrenschmidt 2011cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_NOSCHED)) 2012cbe709c1SBenjamin Herrenschmidt seq_puts(s, "sched\n"); 2013cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_ISOLATE)) 2014cbe709c1SBenjamin Herrenschmidt seq_puts(s, "step\n"); 2015cbe709c1SBenjamin Herrenschmidt return 0; 2016cbe709c1SBenjamin Herrenschmidt } 2017cbe709c1SBenjamin Herrenschmidt 2018cbe709c1SBenjamin Herrenschmidt static int spufs_caps_open(struct inode *inode, struct file *file) 2019cbe709c1SBenjamin Herrenschmidt { 2020cbe709c1SBenjamin Herrenschmidt return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); 2021cbe709c1SBenjamin Herrenschmidt } 2022cbe709c1SBenjamin Herrenschmidt 2023cbe709c1SBenjamin Herrenschmidt static const struct file_operations spufs_caps_fops = { 2024cbe709c1SBenjamin Herrenschmidt .open = spufs_caps_open, 2025cbe709c1SBenjamin Herrenschmidt .read = seq_read, 2026cbe709c1SBenjamin Herrenschmidt .llseek = seq_lseek, 2027cbe709c1SBenjamin Herrenschmidt .release = single_release, 2028cbe709c1SBenjamin Herrenschmidt }; 2029cbe709c1SBenjamin Herrenschmidt 2030bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, 2031bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2032bf1ab978SDwayne Grant McConnell { 2033bf1ab978SDwayne Grant McConnell u32 data; 2034bf1ab978SDwayne Grant McConnell 2035cbea9238SJeremy Kerr /* EOF if there's no entry in the mbox */ 2036cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0x0000ff)) 2037cbea9238SJeremy Kerr return 0; 2038cbea9238SJeremy Kerr 2039bf1ab978SDwayne Grant McConnell data = ctx->csa.prob.pu_mb_R; 2040bf1ab978SDwayne Grant McConnell 2041bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2042bf1ab978SDwayne Grant McConnell } 2043bf1ab978SDwayne Grant McConnell 204469a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf, 204569a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 204669a2f00cSDwayne Grant McConnell { 2047bf1ab978SDwayne Grant McConnell int ret; 204869a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 204969a2f00cSDwayne Grant McConnell 205069a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 205169a2f00cSDwayne Grant McConnell return -EFAULT; 205269a2f00cSDwayne Grant McConnell 2053c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2054c9101bdbSChristoph Hellwig if (ret) 2055c9101bdbSChristoph Hellwig return ret; 205669a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2057bf1ab978SDwayne Grant McConnell ret = __spufs_mbox_info_read(ctx, buf, len, pos); 205869a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 205927b1ea09SChristoph Hellwig spu_release_saved(ctx); 206069a2f00cSDwayne Grant McConnell 2061bf1ab978SDwayne Grant McConnell return ret; 206269a2f00cSDwayne Grant McConnell } 206369a2f00cSDwayne Grant McConnell 20645dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = { 206569a2f00cSDwayne Grant McConnell .open = spufs_info_open, 206669a2f00cSDwayne Grant McConnell .read = spufs_mbox_info_read, 206769a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 206869a2f00cSDwayne Grant McConnell }; 206969a2f00cSDwayne Grant McConnell 2070bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx, 2071bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2072bf1ab978SDwayne Grant McConnell { 2073bf1ab978SDwayne Grant McConnell u32 data; 2074bf1ab978SDwayne Grant McConnell 2075cbea9238SJeremy Kerr /* EOF if there's no entry in the ibox */ 2076cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0xff0000)) 2077cbea9238SJeremy Kerr return 0; 2078cbea9238SJeremy Kerr 2079bf1ab978SDwayne Grant McConnell data = ctx->csa.priv2.puint_mb_R; 2080bf1ab978SDwayne Grant McConnell 2081bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2082bf1ab978SDwayne Grant McConnell } 2083bf1ab978SDwayne Grant McConnell 208469a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf, 208569a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 208669a2f00cSDwayne Grant McConnell { 208769a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2088bf1ab978SDwayne Grant McConnell int ret; 208969a2f00cSDwayne Grant McConnell 209069a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 209169a2f00cSDwayne Grant McConnell return -EFAULT; 209269a2f00cSDwayne Grant McConnell 2093c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2094c9101bdbSChristoph Hellwig if (ret) 2095c9101bdbSChristoph Hellwig return ret; 209669a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2097bf1ab978SDwayne Grant McConnell ret = __spufs_ibox_info_read(ctx, buf, len, pos); 209869a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 209927b1ea09SChristoph Hellwig spu_release_saved(ctx); 210069a2f00cSDwayne Grant McConnell 2101bf1ab978SDwayne Grant McConnell return ret; 210269a2f00cSDwayne Grant McConnell } 210369a2f00cSDwayne Grant McConnell 21045dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = { 210569a2f00cSDwayne Grant McConnell .open = spufs_info_open, 210669a2f00cSDwayne Grant McConnell .read = spufs_ibox_info_read, 210769a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 210869a2f00cSDwayne Grant McConnell }; 210969a2f00cSDwayne Grant McConnell 2110bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx, 2111bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2112bf1ab978SDwayne Grant McConnell { 2113bf1ab978SDwayne Grant McConnell int i, cnt; 2114bf1ab978SDwayne Grant McConnell u32 data[4]; 2115bf1ab978SDwayne Grant McConnell u32 wbox_stat; 2116bf1ab978SDwayne Grant McConnell 2117bf1ab978SDwayne Grant McConnell wbox_stat = ctx->csa.prob.mb_stat_R; 2118bf1ab978SDwayne Grant McConnell cnt = 4 - ((wbox_stat & 0x00ff00) >> 8); 2119bf1ab978SDwayne Grant McConnell for (i = 0; i < cnt; i++) { 2120bf1ab978SDwayne Grant McConnell data[i] = ctx->csa.spu_mailbox_data[i]; 2121bf1ab978SDwayne Grant McConnell } 2122bf1ab978SDwayne Grant McConnell 2123bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, 2124bf1ab978SDwayne Grant McConnell cnt * sizeof(u32)); 2125bf1ab978SDwayne Grant McConnell } 2126bf1ab978SDwayne Grant McConnell 212769a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf, 212869a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 212969a2f00cSDwayne Grant McConnell { 213069a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2131bf1ab978SDwayne Grant McConnell int ret; 213269a2f00cSDwayne Grant McConnell 213369a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 213469a2f00cSDwayne Grant McConnell return -EFAULT; 213569a2f00cSDwayne Grant McConnell 2136c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2137c9101bdbSChristoph Hellwig if (ret) 2138c9101bdbSChristoph Hellwig return ret; 213969a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2140bf1ab978SDwayne Grant McConnell ret = __spufs_wbox_info_read(ctx, buf, len, pos); 214169a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 214227b1ea09SChristoph Hellwig spu_release_saved(ctx); 214369a2f00cSDwayne Grant McConnell 2144bf1ab978SDwayne Grant McConnell return ret; 214569a2f00cSDwayne Grant McConnell } 214669a2f00cSDwayne Grant McConnell 21475dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = { 214869a2f00cSDwayne Grant McConnell .open = spufs_info_open, 214969a2f00cSDwayne Grant McConnell .read = spufs_wbox_info_read, 215069a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 215169a2f00cSDwayne Grant McConnell }; 215269a2f00cSDwayne Grant McConnell 2153bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx, 2154bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2155b9e3bd77SDwayne Grant McConnell { 2156b9e3bd77SDwayne Grant McConnell struct spu_dma_info info; 2157b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *spuqp; 2158b9e3bd77SDwayne Grant McConnell int i; 2159b9e3bd77SDwayne Grant McConnell 2160b9e3bd77SDwayne Grant McConnell info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW; 2161b9e3bd77SDwayne Grant McConnell info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0]; 2162b9e3bd77SDwayne Grant McConnell info.dma_info_status = ctx->csa.spu_chnldata_RW[24]; 2163b9e3bd77SDwayne Grant McConnell info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25]; 2164b9e3bd77SDwayne Grant McConnell info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27]; 2165b9e3bd77SDwayne Grant McConnell for (i = 0; i < 16; i++) { 2166b9e3bd77SDwayne Grant McConnell qp = &info.dma_info_command_data[i]; 2167b9e3bd77SDwayne Grant McConnell spuqp = &ctx->csa.priv2.spuq[i]; 2168b9e3bd77SDwayne Grant McConnell 2169b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW; 2170b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW; 2171b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW; 2172b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW; 2173b9e3bd77SDwayne Grant McConnell } 2174b9e3bd77SDwayne Grant McConnell 2175b9e3bd77SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2176b9e3bd77SDwayne Grant McConnell sizeof info); 2177b9e3bd77SDwayne Grant McConnell } 2178b9e3bd77SDwayne Grant McConnell 2179bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, 2180bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2181bf1ab978SDwayne Grant McConnell { 2182bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2183bf1ab978SDwayne Grant McConnell int ret; 2184bf1ab978SDwayne Grant McConnell 2185bf1ab978SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2186bf1ab978SDwayne Grant McConnell return -EFAULT; 2187bf1ab978SDwayne Grant McConnell 2188c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2189c9101bdbSChristoph Hellwig if (ret) 2190c9101bdbSChristoph Hellwig return ret; 2191bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2192bf1ab978SDwayne Grant McConnell ret = __spufs_dma_info_read(ctx, buf, len, pos); 2193bf1ab978SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 219427b1ea09SChristoph Hellwig spu_release_saved(ctx); 2195bf1ab978SDwayne Grant McConnell 2196bf1ab978SDwayne Grant McConnell return ret; 2197bf1ab978SDwayne Grant McConnell } 2198bf1ab978SDwayne Grant McConnell 21995dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = { 2200b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2201b9e3bd77SDwayne Grant McConnell .read = spufs_dma_info_read, 2202fc15351dSArnd Bergmann .llseek = no_llseek, 2203b9e3bd77SDwayne Grant McConnell }; 2204b9e3bd77SDwayne Grant McConnell 2205bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, 2206bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2207b9e3bd77SDwayne Grant McConnell { 2208b9e3bd77SDwayne Grant McConnell struct spu_proxydma_info info; 2209b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *puqp; 2210bf1ab978SDwayne Grant McConnell int ret = sizeof info; 2211b9e3bd77SDwayne Grant McConnell int i; 2212b9e3bd77SDwayne Grant McConnell 2213b9e3bd77SDwayne Grant McConnell if (len < ret) 2214b9e3bd77SDwayne Grant McConnell return -EINVAL; 2215b9e3bd77SDwayne Grant McConnell 2216b9e3bd77SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2217b9e3bd77SDwayne Grant McConnell return -EFAULT; 2218b9e3bd77SDwayne Grant McConnell 2219b9e3bd77SDwayne Grant McConnell info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW; 2220b9e3bd77SDwayne Grant McConnell info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW; 2221b9e3bd77SDwayne Grant McConnell info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R; 2222b9e3bd77SDwayne Grant McConnell for (i = 0; i < 8; i++) { 2223b9e3bd77SDwayne Grant McConnell qp = &info.proxydma_info_command_data[i]; 2224b9e3bd77SDwayne Grant McConnell puqp = &ctx->csa.priv2.puq[i]; 2225b9e3bd77SDwayne Grant McConnell 2226b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW; 2227b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW; 2228b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW; 2229b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW; 2230b9e3bd77SDwayne Grant McConnell } 2231bf1ab978SDwayne Grant McConnell 2232bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2233bf1ab978SDwayne Grant McConnell sizeof info); 2234bf1ab978SDwayne Grant McConnell } 2235bf1ab978SDwayne Grant McConnell 2236bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, 2237bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2238bf1ab978SDwayne Grant McConnell { 2239bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2240bf1ab978SDwayne Grant McConnell int ret; 2241bf1ab978SDwayne Grant McConnell 2242c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2243c9101bdbSChristoph Hellwig if (ret) 2244c9101bdbSChristoph Hellwig return ret; 2245bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2246bf1ab978SDwayne Grant McConnell ret = __spufs_proxydma_info_read(ctx, buf, len, pos); 2247b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 224827b1ea09SChristoph Hellwig spu_release_saved(ctx); 2249b9e3bd77SDwayne Grant McConnell 2250b9e3bd77SDwayne Grant McConnell return ret; 2251b9e3bd77SDwayne Grant McConnell } 2252b9e3bd77SDwayne Grant McConnell 22535dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = { 2254b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2255b9e3bd77SDwayne Grant McConnell .read = spufs_proxydma_info_read, 2256fc15351dSArnd Bergmann .llseek = no_llseek, 2257b9e3bd77SDwayne Grant McConnell }; 2258b9e3bd77SDwayne Grant McConnell 2259476273adSChristoph Hellwig static int spufs_show_tid(struct seq_file *s, void *private) 2260476273adSChristoph Hellwig { 2261476273adSChristoph Hellwig struct spu_context *ctx = s->private; 2262476273adSChristoph Hellwig 2263476273adSChristoph Hellwig seq_printf(s, "%d\n", ctx->tid); 2264476273adSChristoph Hellwig return 0; 2265476273adSChristoph Hellwig } 2266476273adSChristoph Hellwig 2267476273adSChristoph Hellwig static int spufs_tid_open(struct inode *inode, struct file *file) 2268476273adSChristoph Hellwig { 2269476273adSChristoph Hellwig return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); 2270476273adSChristoph Hellwig } 2271476273adSChristoph Hellwig 2272476273adSChristoph Hellwig static const struct file_operations spufs_tid_fops = { 2273476273adSChristoph Hellwig .open = spufs_tid_open, 2274476273adSChristoph Hellwig .read = seq_read, 2275476273adSChristoph Hellwig .llseek = seq_lseek, 2276476273adSChristoph Hellwig .release = single_release, 2277476273adSChristoph Hellwig }; 2278476273adSChristoph Hellwig 2279e9f8a0b6SChristoph Hellwig static const char *ctx_state_names[] = { 2280e9f8a0b6SChristoph Hellwig "user", "system", "iowait", "loaded" 2281e9f8a0b6SChristoph Hellwig }; 2282e9f8a0b6SChristoph Hellwig 2283e9f8a0b6SChristoph Hellwig static unsigned long long spufs_acct_time(struct spu_context *ctx, 228427ec41d3SAndre Detsch enum spu_utilization_state state) 2285e9f8a0b6SChristoph Hellwig { 228627ec41d3SAndre Detsch unsigned long long time = ctx->stats.times[state]; 2287e9f8a0b6SChristoph Hellwig 228827ec41d3SAndre Detsch /* 228927ec41d3SAndre Detsch * In general, utilization statistics are updated by the controlling 229027ec41d3SAndre Detsch * thread as the spu context moves through various well defined 229127ec41d3SAndre Detsch * state transitions, but if the context is lazily loaded its 229227ec41d3SAndre Detsch * utilization statistics are not updated as the controlling thread 229327ec41d3SAndre Detsch * is not tightly coupled with the execution of the spu context. We 229427ec41d3SAndre Detsch * calculate and apply the time delta from the last recorded state 229527ec41d3SAndre Detsch * of the spu context. 229627ec41d3SAndre Detsch */ 229727ec41d3SAndre Detsch if (ctx->spu && ctx->stats.util_state == state) { 2298f2dec1eaSThomas Gleixner time += ktime_get_ns() - ctx->stats.tstamp; 229927ec41d3SAndre Detsch } 2300e9f8a0b6SChristoph Hellwig 230127ec41d3SAndre Detsch return time / NSEC_PER_MSEC; 2302e9f8a0b6SChristoph Hellwig } 2303e9f8a0b6SChristoph Hellwig 2304e9f8a0b6SChristoph Hellwig static unsigned long long spufs_slb_flts(struct spu_context *ctx) 2305e9f8a0b6SChristoph Hellwig { 2306e9f8a0b6SChristoph Hellwig unsigned long long slb_flts = ctx->stats.slb_flt; 2307e9f8a0b6SChristoph Hellwig 2308e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2309e9f8a0b6SChristoph Hellwig slb_flts += (ctx->spu->stats.slb_flt - 2310e9f8a0b6SChristoph Hellwig ctx->stats.slb_flt_base); 2311e9f8a0b6SChristoph Hellwig } 2312e9f8a0b6SChristoph Hellwig 2313e9f8a0b6SChristoph Hellwig return slb_flts; 2314e9f8a0b6SChristoph Hellwig } 2315e9f8a0b6SChristoph Hellwig 2316e9f8a0b6SChristoph Hellwig static unsigned long long spufs_class2_intrs(struct spu_context *ctx) 2317e9f8a0b6SChristoph Hellwig { 2318e9f8a0b6SChristoph Hellwig unsigned long long class2_intrs = ctx->stats.class2_intr; 2319e9f8a0b6SChristoph Hellwig 2320e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2321e9f8a0b6SChristoph Hellwig class2_intrs += (ctx->spu->stats.class2_intr - 2322e9f8a0b6SChristoph Hellwig ctx->stats.class2_intr_base); 2323e9f8a0b6SChristoph Hellwig } 2324e9f8a0b6SChristoph Hellwig 2325e9f8a0b6SChristoph Hellwig return class2_intrs; 2326e9f8a0b6SChristoph Hellwig } 2327e9f8a0b6SChristoph Hellwig 2328e9f8a0b6SChristoph Hellwig 2329e9f8a0b6SChristoph Hellwig static int spufs_show_stat(struct seq_file *s, void *private) 2330e9f8a0b6SChristoph Hellwig { 2331e9f8a0b6SChristoph Hellwig struct spu_context *ctx = s->private; 2332c9101bdbSChristoph Hellwig int ret; 2333e9f8a0b6SChristoph Hellwig 2334c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 2335c9101bdbSChristoph Hellwig if (ret) 2336c9101bdbSChristoph Hellwig return ret; 2337c9101bdbSChristoph Hellwig 2338e9f8a0b6SChristoph Hellwig seq_printf(s, "%s %llu %llu %llu %llu " 2339e9f8a0b6SChristoph Hellwig "%llu %llu %llu %llu %llu %llu %llu %llu\n", 234027ec41d3SAndre Detsch ctx_state_names[ctx->stats.util_state], 234127ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_USER), 234227ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_SYSTEM), 234327ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IOWAIT), 234427ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED), 2345e9f8a0b6SChristoph Hellwig ctx->stats.vol_ctx_switch, 2346e9f8a0b6SChristoph Hellwig ctx->stats.invol_ctx_switch, 2347e9f8a0b6SChristoph Hellwig spufs_slb_flts(ctx), 2348e9f8a0b6SChristoph Hellwig ctx->stats.hash_flt, 2349e9f8a0b6SChristoph Hellwig ctx->stats.min_flt, 2350e9f8a0b6SChristoph Hellwig ctx->stats.maj_flt, 2351e9f8a0b6SChristoph Hellwig spufs_class2_intrs(ctx), 2352e9f8a0b6SChristoph Hellwig ctx->stats.libassist); 2353e9f8a0b6SChristoph Hellwig spu_release(ctx); 2354e9f8a0b6SChristoph Hellwig return 0; 2355e9f8a0b6SChristoph Hellwig } 2356e9f8a0b6SChristoph Hellwig 2357e9f8a0b6SChristoph Hellwig static int spufs_stat_open(struct inode *inode, struct file *file) 2358e9f8a0b6SChristoph Hellwig { 2359e9f8a0b6SChristoph Hellwig return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); 2360e9f8a0b6SChristoph Hellwig } 2361e9f8a0b6SChristoph Hellwig 2362e9f8a0b6SChristoph Hellwig static const struct file_operations spufs_stat_fops = { 2363e9f8a0b6SChristoph Hellwig .open = spufs_stat_open, 2364e9f8a0b6SChristoph Hellwig .read = seq_read, 2365e9f8a0b6SChristoph Hellwig .llseek = seq_lseek, 2366e9f8a0b6SChristoph Hellwig .release = single_release, 2367e9f8a0b6SChristoph Hellwig }; 2368e9f8a0b6SChristoph Hellwig 23695158e9b5SChristoph Hellwig static inline int spufs_switch_log_used(struct spu_context *ctx) 23705158e9b5SChristoph Hellwig { 23715158e9b5SChristoph Hellwig return (ctx->switch_log->head - ctx->switch_log->tail) % 23725158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 23735158e9b5SChristoph Hellwig } 23745158e9b5SChristoph Hellwig 23755158e9b5SChristoph Hellwig static inline int spufs_switch_log_avail(struct spu_context *ctx) 23765158e9b5SChristoph Hellwig { 23775158e9b5SChristoph Hellwig return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); 23785158e9b5SChristoph Hellwig } 23795158e9b5SChristoph Hellwig 23805158e9b5SChristoph Hellwig static int spufs_switch_log_open(struct inode *inode, struct file *file) 23815158e9b5SChristoph Hellwig { 23825158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2383f5ed0eb6SJeremy Kerr int rc; 23845158e9b5SChristoph Hellwig 2385f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2386f5ed0eb6SJeremy Kerr if (rc) 2387f5ed0eb6SJeremy Kerr return rc; 2388f5ed0eb6SJeremy Kerr 23895158e9b5SChristoph Hellwig if (ctx->switch_log) { 2390f5ed0eb6SJeremy Kerr rc = -EBUSY; 2391f5ed0eb6SJeremy Kerr goto out; 2392f5ed0eb6SJeremy Kerr } 2393f5ed0eb6SJeremy Kerr 2394837ef884SJeremy Kerr ctx->switch_log = kmalloc(sizeof(struct switch_log) + 23955158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry), 23965158e9b5SChristoph Hellwig GFP_KERNEL); 2397f5ed0eb6SJeremy Kerr 2398f5ed0eb6SJeremy Kerr if (!ctx->switch_log) { 2399f5ed0eb6SJeremy Kerr rc = -ENOMEM; 24005158e9b5SChristoph Hellwig goto out; 24015158e9b5SChristoph Hellwig } 2402f5ed0eb6SJeremy Kerr 2403837ef884SJeremy Kerr ctx->switch_log->head = ctx->switch_log->tail = 0; 2404f5ed0eb6SJeremy Kerr init_waitqueue_head(&ctx->switch_log->wait); 2405f5ed0eb6SJeremy Kerr rc = 0; 2406f5ed0eb6SJeremy Kerr 2407f5ed0eb6SJeremy Kerr out: 2408f5ed0eb6SJeremy Kerr spu_release(ctx); 2409f5ed0eb6SJeremy Kerr return rc; 2410f5ed0eb6SJeremy Kerr } 2411f5ed0eb6SJeremy Kerr 2412f5ed0eb6SJeremy Kerr static int spufs_switch_log_release(struct inode *inode, struct file *file) 2413f5ed0eb6SJeremy Kerr { 2414f5ed0eb6SJeremy Kerr struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2415f5ed0eb6SJeremy Kerr int rc; 2416f5ed0eb6SJeremy Kerr 2417f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2418f5ed0eb6SJeremy Kerr if (rc) 2419f5ed0eb6SJeremy Kerr return rc; 2420f5ed0eb6SJeremy Kerr 2421f5ed0eb6SJeremy Kerr kfree(ctx->switch_log); 2422f5ed0eb6SJeremy Kerr ctx->switch_log = NULL; 2423f5ed0eb6SJeremy Kerr spu_release(ctx); 24245158e9b5SChristoph Hellwig 24255158e9b5SChristoph Hellwig return 0; 24265158e9b5SChristoph Hellwig } 24275158e9b5SChristoph Hellwig 24285158e9b5SChristoph Hellwig static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) 24295158e9b5SChristoph Hellwig { 24305158e9b5SChristoph Hellwig struct switch_log_entry *p; 24315158e9b5SChristoph Hellwig 24325158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE; 24335158e9b5SChristoph Hellwig 24345158e9b5SChristoph Hellwig return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n", 24355158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_sec, 24365158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_nsec, 24375158e9b5SChristoph Hellwig p->spu_id, 24385158e9b5SChristoph Hellwig (unsigned int) p->type, 24395158e9b5SChristoph Hellwig (unsigned int) p->val, 24405158e9b5SChristoph Hellwig (unsigned long long) p->timebase); 24415158e9b5SChristoph Hellwig } 24425158e9b5SChristoph Hellwig 24435158e9b5SChristoph Hellwig static ssize_t spufs_switch_log_read(struct file *file, char __user *buf, 24445158e9b5SChristoph Hellwig size_t len, loff_t *ppos) 24455158e9b5SChristoph Hellwig { 2446496ad9aaSAl Viro struct inode *inode = file_inode(file); 24475158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 24485158e9b5SChristoph Hellwig int error = 0, cnt = 0; 24495158e9b5SChristoph Hellwig 245017e37675Sroel kluin if (!buf) 24515158e9b5SChristoph Hellwig return -EINVAL; 24525158e9b5SChristoph Hellwig 2453f5ed0eb6SJeremy Kerr error = spu_acquire(ctx); 2454f5ed0eb6SJeremy Kerr if (error) 2455f5ed0eb6SJeremy Kerr return error; 2456f5ed0eb6SJeremy Kerr 24575158e9b5SChristoph Hellwig while (cnt < len) { 24585158e9b5SChristoph Hellwig char tbuf[128]; 24595158e9b5SChristoph Hellwig int width; 24605158e9b5SChristoph Hellwig 2461f5ed0eb6SJeremy Kerr if (spufs_switch_log_used(ctx) == 0) { 246214f693eeSJeremy Kerr if (cnt > 0) { 246314f693eeSJeremy Kerr /* If there's data ready to go, we can 246414f693eeSJeremy Kerr * just return straight away */ 246514f693eeSJeremy Kerr break; 246614f693eeSJeremy Kerr 246714f693eeSJeremy Kerr } else if (file->f_flags & O_NONBLOCK) { 2468f5ed0eb6SJeremy Kerr error = -EAGAIN; 24695158e9b5SChristoph Hellwig break; 247014f693eeSJeremy Kerr 2471f5ed0eb6SJeremy Kerr } else { 247214f693eeSJeremy Kerr /* spufs_wait will drop the mutex and 247314f693eeSJeremy Kerr * re-acquire, but since we're in read(), the 247414f693eeSJeremy Kerr * file cannot be _released (and so 247514f693eeSJeremy Kerr * ctx->switch_log is stable). 2476f5ed0eb6SJeremy Kerr */ 2477f5ed0eb6SJeremy Kerr error = spufs_wait(ctx->switch_log->wait, 2478f5ed0eb6SJeremy Kerr spufs_switch_log_used(ctx) > 0); 24795158e9b5SChristoph Hellwig 2480f5ed0eb6SJeremy Kerr /* On error, spufs_wait returns without the 2481f5ed0eb6SJeremy Kerr * state mutex held */ 2482f5ed0eb6SJeremy Kerr if (error) 2483f5ed0eb6SJeremy Kerr return error; 24845158e9b5SChristoph Hellwig 248514f693eeSJeremy Kerr /* We may have had entries read from underneath 248614f693eeSJeremy Kerr * us while we dropped the mutex in spufs_wait, 248714f693eeSJeremy Kerr * so re-check */ 248814f693eeSJeremy Kerr if (spufs_switch_log_used(ctx) == 0) 2489f5ed0eb6SJeremy Kerr continue; 249014f693eeSJeremy Kerr } 249114f693eeSJeremy Kerr } 2492f5ed0eb6SJeremy Kerr 24935158e9b5SChristoph Hellwig width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); 2494f5ed0eb6SJeremy Kerr if (width < len) 24955158e9b5SChristoph Hellwig ctx->switch_log->tail = 24965158e9b5SChristoph Hellwig (ctx->switch_log->tail + 1) % 24975158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 2498f5ed0eb6SJeremy Kerr else 2499f5ed0eb6SJeremy Kerr /* If the record is greater than space available return 2500f5ed0eb6SJeremy Kerr * partial buffer (so far) */ 25015158e9b5SChristoph Hellwig break; 25025158e9b5SChristoph Hellwig 25035158e9b5SChristoph Hellwig error = copy_to_user(buf + cnt, tbuf, width); 25045158e9b5SChristoph Hellwig if (error) 25055158e9b5SChristoph Hellwig break; 25065158e9b5SChristoph Hellwig cnt += width; 25075158e9b5SChristoph Hellwig } 25085158e9b5SChristoph Hellwig 2509f5ed0eb6SJeremy Kerr spu_release(ctx); 2510f5ed0eb6SJeremy Kerr 25115158e9b5SChristoph Hellwig return cnt == 0 ? error : cnt; 25125158e9b5SChristoph Hellwig } 25135158e9b5SChristoph Hellwig 25145158e9b5SChristoph Hellwig static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait) 25155158e9b5SChristoph Hellwig { 2516496ad9aaSAl Viro struct inode *inode = file_inode(file); 25175158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 25185158e9b5SChristoph Hellwig unsigned int mask = 0; 2519f5ed0eb6SJeremy Kerr int rc; 25205158e9b5SChristoph Hellwig 25215158e9b5SChristoph Hellwig poll_wait(file, &ctx->switch_log->wait, wait); 25225158e9b5SChristoph Hellwig 2523f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2524f5ed0eb6SJeremy Kerr if (rc) 2525f5ed0eb6SJeremy Kerr return rc; 2526f5ed0eb6SJeremy Kerr 25275158e9b5SChristoph Hellwig if (spufs_switch_log_used(ctx) > 0) 25285158e9b5SChristoph Hellwig mask |= POLLIN; 25295158e9b5SChristoph Hellwig 2530f5ed0eb6SJeremy Kerr spu_release(ctx); 2531f5ed0eb6SJeremy Kerr 25325158e9b5SChristoph Hellwig return mask; 25335158e9b5SChristoph Hellwig } 25345158e9b5SChristoph Hellwig 25355158e9b5SChristoph Hellwig static const struct file_operations spufs_switch_log_fops = { 25365158e9b5SChristoph Hellwig .open = spufs_switch_log_open, 25375158e9b5SChristoph Hellwig .read = spufs_switch_log_read, 25385158e9b5SChristoph Hellwig .poll = spufs_switch_log_poll, 2539f5ed0eb6SJeremy Kerr .release = spufs_switch_log_release, 2540fc15351dSArnd Bergmann .llseek = no_llseek, 25415158e9b5SChristoph Hellwig }; 25425158e9b5SChristoph Hellwig 2543f5ed0eb6SJeremy Kerr /** 2544f5ed0eb6SJeremy Kerr * Log a context switch event to a switch log reader. 2545f5ed0eb6SJeremy Kerr * 2546f5ed0eb6SJeremy Kerr * Must be called with ctx->state_mutex held. 2547f5ed0eb6SJeremy Kerr */ 25485158e9b5SChristoph Hellwig void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, 25495158e9b5SChristoph Hellwig u32 type, u32 val) 25505158e9b5SChristoph Hellwig { 25515158e9b5SChristoph Hellwig if (!ctx->switch_log) 25525158e9b5SChristoph Hellwig return; 25535158e9b5SChristoph Hellwig 25545158e9b5SChristoph Hellwig if (spufs_switch_log_avail(ctx) > 1) { 25555158e9b5SChristoph Hellwig struct switch_log_entry *p; 25565158e9b5SChristoph Hellwig 25575158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->head; 25585158e9b5SChristoph Hellwig ktime_get_ts(&p->tstamp); 25595158e9b5SChristoph Hellwig p->timebase = get_tb(); 25605158e9b5SChristoph Hellwig p->spu_id = spu ? spu->number : -1; 25615158e9b5SChristoph Hellwig p->type = type; 25625158e9b5SChristoph Hellwig p->val = val; 25635158e9b5SChristoph Hellwig 25645158e9b5SChristoph Hellwig ctx->switch_log->head = 25655158e9b5SChristoph Hellwig (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; 25665158e9b5SChristoph Hellwig } 25675158e9b5SChristoph Hellwig 25685158e9b5SChristoph Hellwig wake_up(&ctx->switch_log->wait); 25695158e9b5SChristoph Hellwig } 2570e9f8a0b6SChristoph Hellwig 257146deed69SLuke Browning static int spufs_show_ctx(struct seq_file *s, void *private) 257246deed69SLuke Browning { 257346deed69SLuke Browning struct spu_context *ctx = s->private; 257446deed69SLuke Browning u64 mfc_control_RW; 257546deed69SLuke Browning 257646deed69SLuke Browning mutex_lock(&ctx->state_mutex); 257746deed69SLuke Browning if (ctx->spu) { 257846deed69SLuke Browning struct spu *spu = ctx->spu; 257946deed69SLuke Browning struct spu_priv2 __iomem *priv2 = spu->priv2; 258046deed69SLuke Browning 258146deed69SLuke Browning spin_lock_irq(&spu->register_lock); 258246deed69SLuke Browning mfc_control_RW = in_be64(&priv2->mfc_control_RW); 258346deed69SLuke Browning spin_unlock_irq(&spu->register_lock); 258446deed69SLuke Browning } else { 258546deed69SLuke Browning struct spu_state *csa = &ctx->csa; 258646deed69SLuke Browning 258746deed69SLuke Browning mfc_control_RW = csa->priv2.mfc_control_RW; 258846deed69SLuke Browning } 258946deed69SLuke Browning 259046deed69SLuke Browning seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)" 25919477e455SStephen Rothwell " %c %llx %llx %llx %llx %x %x\n", 259246deed69SLuke Browning ctx->state == SPU_STATE_SAVED ? 'S' : 'R', 259346deed69SLuke Browning ctx->flags, 259446deed69SLuke Browning ctx->sched_flags, 259546deed69SLuke Browning ctx->prio, 259646deed69SLuke Browning ctx->time_slice, 259746deed69SLuke Browning ctx->spu ? ctx->spu->number : -1, 259846deed69SLuke Browning !list_empty(&ctx->rq) ? 'q' : ' ', 259946deed69SLuke Browning ctx->csa.class_0_pending, 260046deed69SLuke Browning ctx->csa.class_0_dar, 260146deed69SLuke Browning ctx->csa.class_1_dsisr, 260246deed69SLuke Browning mfc_control_RW, 260346deed69SLuke Browning ctx->ops->runcntl_read(ctx), 260446deed69SLuke Browning ctx->ops->status_read(ctx)); 260546deed69SLuke Browning 260646deed69SLuke Browning mutex_unlock(&ctx->state_mutex); 260746deed69SLuke Browning 260846deed69SLuke Browning return 0; 260946deed69SLuke Browning } 261046deed69SLuke Browning 261146deed69SLuke Browning static int spufs_ctx_open(struct inode *inode, struct file *file) 261246deed69SLuke Browning { 261346deed69SLuke Browning return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx); 261446deed69SLuke Browning } 261546deed69SLuke Browning 261646deed69SLuke Browning static const struct file_operations spufs_ctx_fops = { 261746deed69SLuke Browning .open = spufs_ctx_open, 261846deed69SLuke Browning .read = seq_read, 261946deed69SLuke Browning .llseek = seq_lseek, 262046deed69SLuke Browning .release = single_release, 262146deed69SLuke Browning }; 262246deed69SLuke Browning 262374254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_contents[] = { 2624cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 26256f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 26266f7dde81SJeremy Kerr { "regs", &spufs_regs_fops, 0666, sizeof(struct spu_reg128[128]), }, 262767207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 262867207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 262967207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 26306f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 26316f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 26326f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2633603c4612SJeremy Kerr { "signal1", &spufs_signal1_fops, 0666, }, 2634603c4612SJeremy Kerr { "signal2", &spufs_signal2_fops, 0666, }, 263567207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 263667207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 26376df10a82SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26386f7dde81SJeremy Kerr { "fpcr", &spufs_fpcr_fops, 0666, sizeof(struct spu_reg128), }, 2639b9e3bd77SDwayne Grant McConnell { "lslr", &spufs_lslr_ops, 0444, }, 2640b9e3bd77SDwayne Grant McConnell { "mfc", &spufs_mfc_fops, 0666, }, 2641b9e3bd77SDwayne Grant McConnell { "mss", &spufs_mss_fops, 0666, }, 2642b9e3bd77SDwayne Grant McConnell { "npc", &spufs_npc_ops, 0666, }, 2643b9e3bd77SDwayne Grant McConnell { "srr0", &spufs_srr0_ops, 0666, }, 26448b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 26458b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 26468b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 2647b9e3bd77SDwayne Grant McConnell { "event_status", &spufs_event_status_ops, 0444, }, 26486f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 264986767277SArnd Bergmann { "phys-id", &spufs_id_ops, 0666, }, 265086767277SArnd Bergmann { "object-id", &spufs_object_id_ops, 0666, }, 26516f7dde81SJeremy Kerr { "mbox_info", &spufs_mbox_info_fops, 0444, sizeof(u32), }, 26526f7dde81SJeremy Kerr { "ibox_info", &spufs_ibox_info_fops, 0444, sizeof(u32), }, 26536f7dde81SJeremy Kerr { "wbox_info", &spufs_wbox_info_fops, 0444, sizeof(u32), }, 26546f7dde81SJeremy Kerr { "dma_info", &spufs_dma_info_fops, 0444, 26556f7dde81SJeremy Kerr sizeof(struct spu_dma_info), }, 26566f7dde81SJeremy Kerr { "proxydma_info", &spufs_proxydma_info_fops, 0444, 26576f7dde81SJeremy Kerr sizeof(struct spu_proxydma_info)}, 2658476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2659e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 26605158e9b5SChristoph Hellwig { "switch_log", &spufs_switch_log_fops, 0444 }, 266167207b96SArnd Bergmann {}, 266267207b96SArnd Bergmann }; 26635737edd1SMark Nutter 266474254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_nosched_contents[] = { 2665cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 26666f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 26675737edd1SMark Nutter { "mbox", &spufs_mbox_fops, 0444, }, 26685737edd1SMark Nutter { "ibox", &spufs_ibox_fops, 0444, }, 26695737edd1SMark Nutter { "wbox", &spufs_wbox_fops, 0222, }, 26706f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 26716f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 26726f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2673d054b36fSJeremy Kerr { "signal1", &spufs_signal1_nosched_fops, 0222, }, 2674d054b36fSJeremy Kerr { "signal2", &spufs_signal2_nosched_fops, 0222, }, 26755737edd1SMark Nutter { "signal1_type", &spufs_signal1_type, 0666, }, 26765737edd1SMark Nutter { "signal2_type", &spufs_signal2_type, 0666, }, 26775737edd1SMark Nutter { "mss", &spufs_mss_fops, 0666, }, 26785737edd1SMark Nutter { "mfc", &spufs_mfc_fops, 0666, }, 26795737edd1SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26805737edd1SMark Nutter { "npc", &spufs_npc_ops, 0666, }, 26816f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 26825737edd1SMark Nutter { "phys-id", &spufs_id_ops, 0666, }, 26835737edd1SMark Nutter { "object-id", &spufs_object_id_ops, 0666, }, 2684476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2685e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 26862c3e4787SJeremy Kerr {}, 26872c3e4787SJeremy Kerr }; 26882c3e4787SJeremy Kerr 268974254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_debug_contents[] = { 269046deed69SLuke Browning { ".ctx", &spufs_ctx_fops, 0444, }, 26915737edd1SMark Nutter {}, 26925737edd1SMark Nutter }; 2693bf1ab978SDwayne Grant McConnell 269474254647SJeremy Kerr const struct spufs_coredump_reader spufs_coredump_read[] = { 26954fca9c42SMichael Ellerman { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])}, 26964fca9c42SMichael Ellerman { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) }, 2697104f0cc2SMichael Ellerman { "lslr", NULL, spufs_lslr_get, 19 }, 2698104f0cc2SMichael Ellerman { "decr", NULL, spufs_decr_get, 19 }, 2699104f0cc2SMichael Ellerman { "decr_status", NULL, spufs_decr_status_get, 19 }, 27004fca9c42SMichael Ellerman { "mem", __spufs_mem_read, NULL, LS_SIZE, }, 27014fca9c42SMichael Ellerman { "signal1", __spufs_signal1_read, NULL, sizeof(u32) }, 2702104f0cc2SMichael Ellerman { "signal1_type", NULL, spufs_signal1_type_get, 19 }, 27034fca9c42SMichael Ellerman { "signal2", __spufs_signal2_read, NULL, sizeof(u32) }, 2704104f0cc2SMichael Ellerman { "signal2_type", NULL, spufs_signal2_type_get, 19 }, 2705104f0cc2SMichael Ellerman { "event_mask", NULL, spufs_event_mask_get, 19 }, 2706104f0cc2SMichael Ellerman { "event_status", NULL, spufs_event_status_get, 19 }, 27074fca9c42SMichael Ellerman { "mbox_info", __spufs_mbox_info_read, NULL, sizeof(u32) }, 27084fca9c42SMichael Ellerman { "ibox_info", __spufs_ibox_info_read, NULL, sizeof(u32) }, 27094fca9c42SMichael Ellerman { "wbox_info", __spufs_wbox_info_read, NULL, 4 * sizeof(u32)}, 27104fca9c42SMichael Ellerman { "dma_info", __spufs_dma_info_read, NULL, sizeof(struct spu_dma_info)}, 27114fca9c42SMichael Ellerman { "proxydma_info", __spufs_proxydma_info_read, 27124fca9c42SMichael Ellerman NULL, sizeof(struct spu_proxydma_info)}, 2713104f0cc2SMichael Ellerman { "object-id", NULL, spufs_object_id_get, 19 }, 2714104f0cc2SMichael Ellerman { "npc", NULL, spufs_npc_get, 19 }, 2715936d5bf1SMichael Ellerman { NULL }, 2716bf1ab978SDwayne Grant McConnell }; 2717