1de6cc651SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 267207b96SArnd Bergmann /* 367207b96SArnd Bergmann * SPU file system -- file contents 467207b96SArnd Bergmann * 567207b96SArnd Bergmann * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 667207b96SArnd Bergmann * 767207b96SArnd Bergmann * Author: Arnd Bergmann <arndb@de.ibm.com> 867207b96SArnd Bergmann */ 967207b96SArnd Bergmann 10a33a7d73SArnd Bergmann #undef DEBUG 11a33a7d73SArnd Bergmann 1267207b96SArnd Bergmann #include <linux/fs.h> 1367207b96SArnd Bergmann #include <linux/ioctl.h> 144b16f8e2SPaul Gortmaker #include <linux/export.h> 15d88cfffaSArnd Bergmann #include <linux/pagemap.h> 1667207b96SArnd Bergmann #include <linux/poll.h> 175110459fSArnd Bergmann #include <linux/ptrace.h> 18cbe709c1SBenjamin Herrenschmidt #include <linux/seq_file.h> 195a0e3ad6STejun Heo #include <linux/slab.h> 2067207b96SArnd Bergmann 2167207b96SArnd Bergmann #include <asm/io.h> 22dfe1e09fSFUJITA Tomonori #include <asm/time.h> 2367207b96SArnd Bergmann #include <asm/spu.h> 24b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h> 257c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 2667207b96SArnd Bergmann 2767207b96SArnd Bergmann #include "spufs.h" 28ae142e0cSChristoph Hellwig #include "sputrace.h" 2967207b96SArnd Bergmann 3027d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) 3127d5bf2aSBenjamin Herrenschmidt 32197b1a82SChristoph Hellwig /* Simple attribute files */ 33197b1a82SChristoph Hellwig struct spufs_attr { 34197b1a82SChristoph Hellwig int (*get)(void *, u64 *); 35197b1a82SChristoph Hellwig int (*set)(void *, u64); 36197b1a82SChristoph Hellwig char get_buf[24]; /* enough to store a u64 and "\n\0" */ 37197b1a82SChristoph Hellwig char set_buf[24]; 38197b1a82SChristoph Hellwig void *data; 39197b1a82SChristoph Hellwig const char *fmt; /* format for read operation */ 40197b1a82SChristoph Hellwig struct mutex mutex; /* protects access to these buffers */ 41197b1a82SChristoph Hellwig }; 42197b1a82SChristoph Hellwig 43197b1a82SChristoph Hellwig static int spufs_attr_open(struct inode *inode, struct file *file, 44197b1a82SChristoph Hellwig int (*get)(void *, u64 *), int (*set)(void *, u64), 45197b1a82SChristoph Hellwig const char *fmt) 46197b1a82SChristoph Hellwig { 47197b1a82SChristoph Hellwig struct spufs_attr *attr; 48197b1a82SChristoph Hellwig 49197b1a82SChristoph Hellwig attr = kmalloc(sizeof(*attr), GFP_KERNEL); 50197b1a82SChristoph Hellwig if (!attr) 51197b1a82SChristoph Hellwig return -ENOMEM; 52197b1a82SChristoph Hellwig 53197b1a82SChristoph Hellwig attr->get = get; 54197b1a82SChristoph Hellwig attr->set = set; 55197b1a82SChristoph Hellwig attr->data = inode->i_private; 56197b1a82SChristoph Hellwig attr->fmt = fmt; 57197b1a82SChristoph Hellwig mutex_init(&attr->mutex); 58197b1a82SChristoph Hellwig file->private_data = attr; 59197b1a82SChristoph Hellwig 60197b1a82SChristoph Hellwig return nonseekable_open(inode, file); 61197b1a82SChristoph Hellwig } 62197b1a82SChristoph Hellwig 63197b1a82SChristoph Hellwig static int spufs_attr_release(struct inode *inode, struct file *file) 64197b1a82SChristoph Hellwig { 65197b1a82SChristoph Hellwig kfree(file->private_data); 66197b1a82SChristoph Hellwig return 0; 67197b1a82SChristoph Hellwig } 68197b1a82SChristoph Hellwig 69197b1a82SChristoph Hellwig static ssize_t spufs_attr_read(struct file *file, char __user *buf, 70197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 71197b1a82SChristoph Hellwig { 72197b1a82SChristoph Hellwig struct spufs_attr *attr; 73197b1a82SChristoph Hellwig size_t size; 74197b1a82SChristoph Hellwig ssize_t ret; 75197b1a82SChristoph Hellwig 76197b1a82SChristoph Hellwig attr = file->private_data; 77197b1a82SChristoph Hellwig if (!attr->get) 78197b1a82SChristoph Hellwig return -EACCES; 79197b1a82SChristoph Hellwig 80197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 81197b1a82SChristoph Hellwig if (ret) 82197b1a82SChristoph Hellwig return ret; 83197b1a82SChristoph Hellwig 84197b1a82SChristoph Hellwig if (*ppos) { /* continued read */ 85197b1a82SChristoph Hellwig size = strlen(attr->get_buf); 86197b1a82SChristoph Hellwig } else { /* first read */ 87197b1a82SChristoph Hellwig u64 val; 88197b1a82SChristoph Hellwig ret = attr->get(attr->data, &val); 89197b1a82SChristoph Hellwig if (ret) 90197b1a82SChristoph Hellwig goto out; 91197b1a82SChristoph Hellwig 92197b1a82SChristoph Hellwig size = scnprintf(attr->get_buf, sizeof(attr->get_buf), 93197b1a82SChristoph Hellwig attr->fmt, (unsigned long long)val); 94197b1a82SChristoph Hellwig } 95197b1a82SChristoph Hellwig 96197b1a82SChristoph Hellwig ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size); 97197b1a82SChristoph Hellwig out: 98197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 99197b1a82SChristoph Hellwig return ret; 100197b1a82SChristoph Hellwig } 101197b1a82SChristoph Hellwig 102197b1a82SChristoph Hellwig static ssize_t spufs_attr_write(struct file *file, const char __user *buf, 103197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 104197b1a82SChristoph Hellwig { 105197b1a82SChristoph Hellwig struct spufs_attr *attr; 106197b1a82SChristoph Hellwig u64 val; 107197b1a82SChristoph Hellwig size_t size; 108197b1a82SChristoph Hellwig ssize_t ret; 109197b1a82SChristoph Hellwig 110197b1a82SChristoph Hellwig attr = file->private_data; 111197b1a82SChristoph Hellwig if (!attr->set) 112197b1a82SChristoph Hellwig return -EACCES; 113197b1a82SChristoph Hellwig 114197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 115197b1a82SChristoph Hellwig if (ret) 116197b1a82SChristoph Hellwig return ret; 117197b1a82SChristoph Hellwig 118197b1a82SChristoph Hellwig ret = -EFAULT; 119197b1a82SChristoph Hellwig size = min(sizeof(attr->set_buf) - 1, len); 120197b1a82SChristoph Hellwig if (copy_from_user(attr->set_buf, buf, size)) 121197b1a82SChristoph Hellwig goto out; 122197b1a82SChristoph Hellwig 123197b1a82SChristoph Hellwig ret = len; /* claim we got the whole input */ 124197b1a82SChristoph Hellwig attr->set_buf[size] = '\0'; 125197b1a82SChristoph Hellwig val = simple_strtol(attr->set_buf, NULL, 0); 126197b1a82SChristoph Hellwig attr->set(attr->data, val); 127197b1a82SChristoph Hellwig out: 128197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 129197b1a82SChristoph Hellwig return ret; 130197b1a82SChristoph Hellwig } 131197b1a82SChristoph Hellwig 132197b1a82SChristoph Hellwig #define DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \ 133197b1a82SChristoph Hellwig static int __fops ## _open(struct inode *inode, struct file *file) \ 134197b1a82SChristoph Hellwig { \ 135197b1a82SChristoph Hellwig __simple_attr_check_format(__fmt, 0ull); \ 136197b1a82SChristoph Hellwig return spufs_attr_open(inode, file, __get, __set, __fmt); \ 137197b1a82SChristoph Hellwig } \ 138828c0950SAlexey Dobriyan static const struct file_operations __fops = { \ 139197b1a82SChristoph Hellwig .open = __fops ## _open, \ 140197b1a82SChristoph Hellwig .release = spufs_attr_release, \ 141197b1a82SChristoph Hellwig .read = spufs_attr_read, \ 142197b1a82SChristoph Hellwig .write = spufs_attr_write, \ 143fc15351dSArnd Bergmann .llseek = generic_file_llseek, \ 144197b1a82SChristoph Hellwig }; 145197b1a82SChristoph Hellwig 146cbe709c1SBenjamin Herrenschmidt 14767207b96SArnd Bergmann static int 14867207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file) 14967207b96SArnd Bergmann { 15067207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1516df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 15243c2bbd9SChristoph Hellwig 15347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1546df10a82SMark Nutter file->private_data = ctx; 15543c2bbd9SChristoph Hellwig if (!i->i_openers++) 1566df10a82SMark Nutter ctx->local_store = inode->i_mapping; 15747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 15843c2bbd9SChristoph Hellwig return 0; 15943c2bbd9SChristoph Hellwig } 16043c2bbd9SChristoph Hellwig 16143c2bbd9SChristoph Hellwig static int 16243c2bbd9SChristoph Hellwig spufs_mem_release(struct inode *inode, struct file *file) 16343c2bbd9SChristoph Hellwig { 16443c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 16543c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 16643c2bbd9SChristoph Hellwig 16747d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 16843c2bbd9SChristoph Hellwig if (!--i->i_openers) 16943c2bbd9SChristoph Hellwig ctx->local_store = NULL; 17047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 17167207b96SArnd Bergmann return 0; 17267207b96SArnd Bergmann } 17367207b96SArnd Bergmann 17467207b96SArnd Bergmann static ssize_t 175bf1ab978SDwayne Grant McConnell __spufs_mem_read(struct spu_context *ctx, char __user *buffer, 176bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 177bf1ab978SDwayne Grant McConnell { 178bf1ab978SDwayne Grant McConnell char *local_store = ctx->ops->get_ls(ctx); 179bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, local_store, 180bf1ab978SDwayne Grant McConnell LS_SIZE); 181bf1ab978SDwayne Grant McConnell } 182bf1ab978SDwayne Grant McConnell 183bf1ab978SDwayne Grant McConnell static ssize_t 18467207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer, 18567207b96SArnd Bergmann size_t size, loff_t *pos) 18667207b96SArnd Bergmann { 187bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 188aa0ed2bdSArnd Bergmann ssize_t ret; 18967207b96SArnd Bergmann 190c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 191c9101bdbSChristoph Hellwig if (ret) 192c9101bdbSChristoph Hellwig return ret; 193bf1ab978SDwayne Grant McConnell ret = __spufs_mem_read(ctx, buffer, size, pos); 1948b3d6663SArnd Bergmann spu_release(ctx); 195c9101bdbSChristoph Hellwig 19667207b96SArnd Bergmann return ret; 19767207b96SArnd Bergmann } 19867207b96SArnd Bergmann 19967207b96SArnd Bergmann static ssize_t 20067207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer, 201aa0ed2bdSArnd Bergmann size_t size, loff_t *ppos) 20267207b96SArnd Bergmann { 20367207b96SArnd Bergmann struct spu_context *ctx = file->private_data; 2048b3d6663SArnd Bergmann char *local_store; 205aa0ed2bdSArnd Bergmann loff_t pos = *ppos; 2068b3d6663SArnd Bergmann int ret; 20767207b96SArnd Bergmann 208aa0ed2bdSArnd Bergmann if (pos > LS_SIZE) 20967207b96SArnd Bergmann return -EFBIG; 2108b3d6663SArnd Bergmann 211c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 212c9101bdbSChristoph Hellwig if (ret) 213c9101bdbSChristoph Hellwig return ret; 214c9101bdbSChristoph Hellwig 2158b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 21663c3b9d7SAkinobu Mita size = simple_write_to_buffer(local_store, LS_SIZE, ppos, buffer, size); 2178b3d6663SArnd Bergmann spu_release(ctx); 218aa0ed2bdSArnd Bergmann 219aa0ed2bdSArnd Bergmann return size; 22067207b96SArnd Bergmann } 22167207b96SArnd Bergmann 222e807f02cSSouptick Joarder static vm_fault_t 22311bac800SDave Jiang spufs_mem_mmap_fault(struct vm_fault *vmf) 2248b3d6663SArnd Bergmann { 22511bac800SDave Jiang struct vm_area_struct *vma = vmf->vma; 2268b3d6663SArnd Bergmann struct spu_context *ctx = vma->vm_file->private_data; 227b1e2270fSNick Piggin unsigned long pfn, offset; 228e807f02cSSouptick Joarder vm_fault_t ret; 229b1e2270fSNick Piggin 230b1e2270fSNick Piggin offset = vmf->pgoff << PAGE_SHIFT; 231128b8546SMasato Noguchi if (offset >= LS_SIZE) 232b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 233128b8546SMasato Noguchi 234b1e2270fSNick Piggin pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n", 2351a29d85eSJan Kara vmf->address, offset); 236f1fa74f4SBenjamin Herrenschmidt 237c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 238b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2398b3d6663SArnd Bergmann 240ac91cb8dSArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 24164b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); 24278bde53eSBenjamin Herrenschmidt pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset); 243ac91cb8dSArnd Bergmann } else { 24464b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 24578bde53eSBenjamin Herrenschmidt pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT; 246ac91cb8dSArnd Bergmann } 247e807f02cSSouptick Joarder ret = vmf_insert_pfn(vma, vmf->address, pfn); 24878bde53eSBenjamin Herrenschmidt 2498b3d6663SArnd Bergmann spu_release(ctx); 2508b3d6663SArnd Bergmann 251e807f02cSSouptick Joarder return ret; 2528b3d6663SArnd Bergmann } 2538b3d6663SArnd Bergmann 254a352894dSBenjamin Herrenschmidt static int spufs_mem_mmap_access(struct vm_area_struct *vma, 255a352894dSBenjamin Herrenschmidt unsigned long address, 256a352894dSBenjamin Herrenschmidt void *buf, int len, int write) 257a352894dSBenjamin Herrenschmidt { 258a352894dSBenjamin Herrenschmidt struct spu_context *ctx = vma->vm_file->private_data; 259a352894dSBenjamin Herrenschmidt unsigned long offset = address - vma->vm_start; 260a352894dSBenjamin Herrenschmidt char *local_store; 261a352894dSBenjamin Herrenschmidt 262a352894dSBenjamin Herrenschmidt if (write && !(vma->vm_flags & VM_WRITE)) 263a352894dSBenjamin Herrenschmidt return -EACCES; 264a352894dSBenjamin Herrenschmidt if (spu_acquire(ctx)) 265a352894dSBenjamin Herrenschmidt return -EINTR; 266a352894dSBenjamin Herrenschmidt if ((offset + len) > vma->vm_end) 267a352894dSBenjamin Herrenschmidt len = vma->vm_end - offset; 268a352894dSBenjamin Herrenschmidt local_store = ctx->ops->get_ls(ctx); 269a352894dSBenjamin Herrenschmidt if (write) 270a352894dSBenjamin Herrenschmidt memcpy_toio(local_store + offset, buf, len); 271a352894dSBenjamin Herrenschmidt else 272a352894dSBenjamin Herrenschmidt memcpy_fromio(buf, local_store + offset, len); 273a352894dSBenjamin Herrenschmidt spu_release(ctx); 274a352894dSBenjamin Herrenschmidt return len; 275a352894dSBenjamin Herrenschmidt } 27678bde53eSBenjamin Herrenschmidt 277f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mem_mmap_vmops = { 278b1e2270fSNick Piggin .fault = spufs_mem_mmap_fault, 279a352894dSBenjamin Herrenschmidt .access = spufs_mem_mmap_access, 2808b3d6663SArnd Bergmann }; 2818b3d6663SArnd Bergmann 282f1fa74f4SBenjamin Herrenschmidt static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 28367207b96SArnd Bergmann { 2848b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 2858b3d6663SArnd Bergmann return -EINVAL; 28667207b96SArnd Bergmann 28778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 28864b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached_wc(vma->vm_page_prot); 2898b3d6663SArnd Bergmann 2908b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 29167207b96SArnd Bergmann return 0; 29267207b96SArnd Bergmann } 29367207b96SArnd Bergmann 2945dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = { 29567207b96SArnd Bergmann .open = spufs_mem_open, 296ce92987bSChristoph Hellwig .release = spufs_mem_release, 29767207b96SArnd Bergmann .read = spufs_mem_read, 29867207b96SArnd Bergmann .write = spufs_mem_write, 2998b3d6663SArnd Bergmann .llseek = generic_file_llseek, 30067207b96SArnd Bergmann .mmap = spufs_mem_mmap, 3018b3d6663SArnd Bergmann }; 3028b3d6663SArnd Bergmann 303e807f02cSSouptick Joarder static vm_fault_t spufs_ps_fault(struct vm_fault *vmf, 30478bde53eSBenjamin Herrenschmidt unsigned long ps_offs, 30527d5bf2aSBenjamin Herrenschmidt unsigned long ps_size) 3066df10a82SMark Nutter { 30711bac800SDave Jiang struct spu_context *ctx = vmf->vma->vm_file->private_data; 308b1e2270fSNick Piggin unsigned long area, offset = vmf->pgoff << PAGE_SHIFT; 309e807f02cSSouptick Joarder int err = 0; 310e807f02cSSouptick Joarder vm_fault_t ret = VM_FAULT_NOPAGE; 3116df10a82SMark Nutter 312b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__enter, ctx); 313038200cfSChristoph Hellwig 31427d5bf2aSBenjamin Herrenschmidt if (offset >= ps_size) 315b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 3166df10a82SMark Nutter 31760657263SJeremy Kerr if (fatal_signal_pending(current)) 31860657263SJeremy Kerr return VM_FAULT_SIGBUS; 31960657263SJeremy Kerr 32033bfd7a7SArnd Bergmann /* 321d5883137SJeremy Kerr * Because we release the mmap_sem, the context may be destroyed while 322d5883137SJeremy Kerr * we're in spu_wait. Grab an extra reference so it isn't destroyed 323d5883137SJeremy Kerr * in the meantime. 324d5883137SJeremy Kerr */ 325d5883137SJeremy Kerr get_spu_context(ctx); 326d5883137SJeremy Kerr 327d5883137SJeremy Kerr /* 32833bfd7a7SArnd Bergmann * We have to wait for context to be loaded before we have 32933bfd7a7SArnd Bergmann * pages to hand out to the user, but we don't want to wait 33033bfd7a7SArnd Bergmann * with the mmap_sem held. 33133bfd7a7SArnd Bergmann * It is possible to drop the mmap_sem here, but then we need 332b1e2270fSNick Piggin * to return VM_FAULT_NOPAGE because the mappings may have 33333bfd7a7SArnd Bergmann * hanged. 33478bde53eSBenjamin Herrenschmidt */ 335c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 336d5883137SJeremy Kerr goto refault; 337c9101bdbSChristoph Hellwig 33833bfd7a7SArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 33933bfd7a7SArnd Bergmann up_read(¤t->mm->mmap_sem); 340b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__sleep, ctx); 341e807f02cSSouptick Joarder err = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 342b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu); 34333bfd7a7SArnd Bergmann down_read(¤t->mm->mmap_sem); 344c9101bdbSChristoph Hellwig } else { 3456df10a82SMark Nutter area = ctx->spu->problem_phys + ps_offs; 346e807f02cSSouptick Joarder ret = vmf_insert_pfn(vmf->vma, vmf->address, 347e807f02cSSouptick Joarder (area + offset) >> PAGE_SHIFT); 348b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu); 349c9101bdbSChristoph Hellwig } 35033bfd7a7SArnd Bergmann 351e807f02cSSouptick Joarder if (!err) 3526df10a82SMark Nutter spu_release(ctx); 353d5883137SJeremy Kerr 354d5883137SJeremy Kerr refault: 355d5883137SJeremy Kerr put_spu_context(ctx); 356e807f02cSSouptick Joarder return ret; 3576df10a82SMark Nutter } 3586df10a82SMark Nutter 35927d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 360e807f02cSSouptick Joarder static vm_fault_t spufs_cntl_mmap_fault(struct vm_fault *vmf) 3616df10a82SMark Nutter { 36211bac800SDave Jiang return spufs_ps_fault(vmf, 0x4000, SPUFS_CNTL_MAP_SIZE); 3636df10a82SMark Nutter } 3646df10a82SMark Nutter 365f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_cntl_mmap_vmops = { 366b1e2270fSNick Piggin .fault = spufs_cntl_mmap_fault, 3676df10a82SMark Nutter }; 3686df10a82SMark Nutter 3696df10a82SMark Nutter /* 3706df10a82SMark Nutter * mmap support for problem state control area [0x4000 - 0x4fff]. 3716df10a82SMark Nutter */ 3726df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) 3736df10a82SMark Nutter { 3746df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 3756df10a82SMark Nutter return -EINVAL; 3766df10a82SMark Nutter 37778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 37864b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 3796df10a82SMark Nutter 3806df10a82SMark Nutter vma->vm_ops = &spufs_cntl_mmap_vmops; 3816df10a82SMark Nutter return 0; 3826df10a82SMark Nutter } 38327d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 38427d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL 38527d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 3866df10a82SMark Nutter 387197b1a82SChristoph Hellwig static int spufs_cntl_get(void *data, u64 *val) 388e1dbff2bSArnd Bergmann { 389e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 390c9101bdbSChristoph Hellwig int ret; 391e1dbff2bSArnd Bergmann 392c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 393c9101bdbSChristoph Hellwig if (ret) 394c9101bdbSChristoph Hellwig return ret; 395197b1a82SChristoph Hellwig *val = ctx->ops->status_read(ctx); 396e1dbff2bSArnd Bergmann spu_release(ctx); 397e1dbff2bSArnd Bergmann 398197b1a82SChristoph Hellwig return 0; 399e1dbff2bSArnd Bergmann } 400e1dbff2bSArnd Bergmann 401197b1a82SChristoph Hellwig static int spufs_cntl_set(void *data, u64 val) 402e1dbff2bSArnd Bergmann { 403e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 404c9101bdbSChristoph Hellwig int ret; 405e1dbff2bSArnd Bergmann 406c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 407c9101bdbSChristoph Hellwig if (ret) 408c9101bdbSChristoph Hellwig return ret; 409e1dbff2bSArnd Bergmann ctx->ops->runcntl_write(ctx, val); 410e1dbff2bSArnd Bergmann spu_release(ctx); 411197b1a82SChristoph Hellwig 412197b1a82SChristoph Hellwig return 0; 413e1dbff2bSArnd Bergmann } 414e1dbff2bSArnd Bergmann 4156df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file) 4166df10a82SMark Nutter { 4176df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 4186df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 4196df10a82SMark Nutter 42047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 4216df10a82SMark Nutter file->private_data = ctx; 42243c2bbd9SChristoph Hellwig if (!i->i_openers++) 4236df10a82SMark Nutter ctx->cntl = inode->i_mapping; 42447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 4258b88b099SChristoph Hellwig return simple_attr_open(inode, file, spufs_cntl_get, 426e1dbff2bSArnd Bergmann spufs_cntl_set, "0x%08lx"); 4276df10a82SMark Nutter } 4286df10a82SMark Nutter 42943c2bbd9SChristoph Hellwig static int 43043c2bbd9SChristoph Hellwig spufs_cntl_release(struct inode *inode, struct file *file) 43143c2bbd9SChristoph Hellwig { 43243c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 43343c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 43443c2bbd9SChristoph Hellwig 43574bedc4dSChristoph Hellwig simple_attr_release(inode, file); 43643c2bbd9SChristoph Hellwig 43747d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 43843c2bbd9SChristoph Hellwig if (!--i->i_openers) 43943c2bbd9SChristoph Hellwig ctx->cntl = NULL; 44047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 44143c2bbd9SChristoph Hellwig return 0; 44243c2bbd9SChristoph Hellwig } 44343c2bbd9SChristoph Hellwig 4445dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = { 4456df10a82SMark Nutter .open = spufs_cntl_open, 44643c2bbd9SChristoph Hellwig .release = spufs_cntl_release, 4478b88b099SChristoph Hellwig .read = simple_attr_read, 4488b88b099SChristoph Hellwig .write = simple_attr_write, 449658829dfSGeliang Tang .llseek = no_llseek, 4506df10a82SMark Nutter .mmap = spufs_cntl_mmap, 4516df10a82SMark Nutter }; 4526df10a82SMark Nutter 4538b3d6663SArnd Bergmann static int 4548b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 4558b3d6663SArnd Bergmann { 4568b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 4578b3d6663SArnd Bergmann file->private_data = i->i_ctx; 4588b3d6663SArnd Bergmann return 0; 4598b3d6663SArnd Bergmann } 4608b3d6663SArnd Bergmann 4618b3d6663SArnd Bergmann static ssize_t 462bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer, 463bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 464bf1ab978SDwayne Grant McConnell { 465bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 466bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 467bf1ab978SDwayne Grant McConnell lscsa->gprs, sizeof lscsa->gprs); 468bf1ab978SDwayne Grant McConnell } 469bf1ab978SDwayne Grant McConnell 470bf1ab978SDwayne Grant McConnell static ssize_t 4718b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 4728b3d6663SArnd Bergmann size_t size, loff_t *pos) 4738b3d6663SArnd Bergmann { 4748b3d6663SArnd Bergmann int ret; 475bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 4768b3d6663SArnd Bergmann 477f027faa2SJeremy Kerr /* pre-check for file position: if we'd return EOF, there's no point 478f027faa2SJeremy Kerr * causing a deschedule */ 479f027faa2SJeremy Kerr if (*pos >= sizeof(ctx->csa.lscsa->gprs)) 480f027faa2SJeremy Kerr return 0; 481f027faa2SJeremy Kerr 482c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 483c9101bdbSChristoph Hellwig if (ret) 484c9101bdbSChristoph Hellwig return ret; 485bf1ab978SDwayne Grant McConnell ret = __spufs_regs_read(ctx, buffer, size, pos); 48627b1ea09SChristoph Hellwig spu_release_saved(ctx); 4878b3d6663SArnd Bergmann return ret; 4888b3d6663SArnd Bergmann } 4898b3d6663SArnd Bergmann 4908b3d6663SArnd Bergmann static ssize_t 4918b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 4928b3d6663SArnd Bergmann size_t size, loff_t *pos) 4938b3d6663SArnd Bergmann { 4948b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 4958b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 4968b3d6663SArnd Bergmann int ret; 4978b3d6663SArnd Bergmann 498d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->gprs)) 4998b3d6663SArnd Bergmann return -EFBIG; 500d219889bSJeremy Kerr 501c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 502c9101bdbSChristoph Hellwig if (ret) 503c9101bdbSChristoph Hellwig return ret; 5048b3d6663SArnd Bergmann 50563c3b9d7SAkinobu Mita size = simple_write_to_buffer(lscsa->gprs, sizeof(lscsa->gprs), pos, 50663c3b9d7SAkinobu Mita buffer, size); 5078b3d6663SArnd Bergmann 50827b1ea09SChristoph Hellwig spu_release_saved(ctx); 50963c3b9d7SAkinobu Mita return size; 5108b3d6663SArnd Bergmann } 5118b3d6663SArnd Bergmann 5125dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = { 5138b3d6663SArnd Bergmann .open = spufs_regs_open, 5148b3d6663SArnd Bergmann .read = spufs_regs_read, 5158b3d6663SArnd Bergmann .write = spufs_regs_write, 5168b3d6663SArnd Bergmann .llseek = generic_file_llseek, 5178b3d6663SArnd Bergmann }; 5188b3d6663SArnd Bergmann 5198b3d6663SArnd Bergmann static ssize_t 520bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer, 521bf1ab978SDwayne Grant McConnell size_t size, loff_t * pos) 522bf1ab978SDwayne Grant McConnell { 523bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 524bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 525bf1ab978SDwayne Grant McConnell &lscsa->fpcr, sizeof(lscsa->fpcr)); 526bf1ab978SDwayne Grant McConnell } 527bf1ab978SDwayne Grant McConnell 528bf1ab978SDwayne Grant McConnell static ssize_t 5298b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 5308b3d6663SArnd Bergmann size_t size, loff_t * pos) 5318b3d6663SArnd Bergmann { 5328b3d6663SArnd Bergmann int ret; 533bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 5348b3d6663SArnd Bergmann 535c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 536c9101bdbSChristoph Hellwig if (ret) 537c9101bdbSChristoph Hellwig return ret; 538bf1ab978SDwayne Grant McConnell ret = __spufs_fpcr_read(ctx, buffer, size, pos); 53927b1ea09SChristoph Hellwig spu_release_saved(ctx); 5408b3d6663SArnd Bergmann return ret; 5418b3d6663SArnd Bergmann } 5428b3d6663SArnd Bergmann 5438b3d6663SArnd Bergmann static ssize_t 5448b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 5458b3d6663SArnd Bergmann size_t size, loff_t * pos) 5468b3d6663SArnd Bergmann { 5478b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5488b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5498b3d6663SArnd Bergmann int ret; 5508b3d6663SArnd Bergmann 551d219889bSJeremy Kerr if (*pos >= sizeof(lscsa->fpcr)) 5528b3d6663SArnd Bergmann return -EFBIG; 553c9101bdbSChristoph Hellwig 554c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 555c9101bdbSChristoph Hellwig if (ret) 556c9101bdbSChristoph Hellwig return ret; 557c9101bdbSChristoph Hellwig 55863c3b9d7SAkinobu Mita size = simple_write_to_buffer(&lscsa->fpcr, sizeof(lscsa->fpcr), pos, 55963c3b9d7SAkinobu Mita buffer, size); 5608b3d6663SArnd Bergmann 56127b1ea09SChristoph Hellwig spu_release_saved(ctx); 56263c3b9d7SAkinobu Mita return size; 5638b3d6663SArnd Bergmann } 5648b3d6663SArnd Bergmann 5655dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = { 5668b3d6663SArnd Bergmann .open = spufs_regs_open, 5678b3d6663SArnd Bergmann .read = spufs_fpcr_read, 5688b3d6663SArnd Bergmann .write = spufs_fpcr_write, 56967207b96SArnd Bergmann .llseek = generic_file_llseek, 57067207b96SArnd Bergmann }; 57167207b96SArnd Bergmann 57267207b96SArnd Bergmann /* generic open function for all pipe-like files */ 57367207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 57467207b96SArnd Bergmann { 57567207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 57667207b96SArnd Bergmann file->private_data = i->i_ctx; 57767207b96SArnd Bergmann 578c5bf68feSKirill Smelkov return stream_open(inode, file); 57967207b96SArnd Bergmann } 58067207b96SArnd Bergmann 581cdcc89bbSArnd Bergmann /* 582cdcc89bbSArnd Bergmann * Read as many bytes from the mailbox as possible, until 583cdcc89bbSArnd Bergmann * one of the conditions becomes true: 584cdcc89bbSArnd Bergmann * 585cdcc89bbSArnd Bergmann * - no more data available in the mailbox 586cdcc89bbSArnd Bergmann * - end of the user provided buffer 587cdcc89bbSArnd Bergmann * - end of the mapped area 588cdcc89bbSArnd Bergmann */ 58967207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 59067207b96SArnd Bergmann size_t len, loff_t *pos) 59167207b96SArnd Bergmann { 5928b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 593cdcc89bbSArnd Bergmann u32 mbox_data, __user *udata; 594cdcc89bbSArnd Bergmann ssize_t count; 59567207b96SArnd Bergmann 59667207b96SArnd Bergmann if (len < 4) 59767207b96SArnd Bergmann return -EINVAL; 59867207b96SArnd Bergmann 59996d4f267SLinus Torvalds if (!access_ok(buf, len)) 60067207b96SArnd Bergmann return -EFAULT; 60167207b96SArnd Bergmann 602cdcc89bbSArnd Bergmann udata = (void __user *)buf; 603cdcc89bbSArnd Bergmann 604c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 605c9101bdbSChristoph Hellwig if (count) 606c9101bdbSChristoph Hellwig return count; 607c9101bdbSChristoph Hellwig 608274cef5eSArnd Bergmann for (count = 0; (count + 4) <= len; count += 4, udata++) { 609cdcc89bbSArnd Bergmann int ret; 610cdcc89bbSArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 611cdcc89bbSArnd Bergmann if (ret == 0) 612cdcc89bbSArnd Bergmann break; 613cdcc89bbSArnd Bergmann 614cdcc89bbSArnd Bergmann /* 615cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 616cdcc89bbSArnd Bergmann * but still need to return the data we have 617cdcc89bbSArnd Bergmann * read successfully so far. 618cdcc89bbSArnd Bergmann */ 619cdcc89bbSArnd Bergmann ret = __put_user(mbox_data, udata); 620cdcc89bbSArnd Bergmann if (ret) { 621cdcc89bbSArnd Bergmann if (!count) 622cdcc89bbSArnd Bergmann count = -EFAULT; 623cdcc89bbSArnd Bergmann break; 624cdcc89bbSArnd Bergmann } 625cdcc89bbSArnd Bergmann } 626cdcc89bbSArnd Bergmann spu_release(ctx); 627cdcc89bbSArnd Bergmann 628cdcc89bbSArnd Bergmann if (!count) 629cdcc89bbSArnd Bergmann count = -EAGAIN; 630cdcc89bbSArnd Bergmann 631cdcc89bbSArnd Bergmann return count; 63267207b96SArnd Bergmann } 63367207b96SArnd Bergmann 6345dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = { 63567207b96SArnd Bergmann .open = spufs_pipe_open, 63667207b96SArnd Bergmann .read = spufs_mbox_read, 637fc15351dSArnd Bergmann .llseek = no_llseek, 63867207b96SArnd Bergmann }; 63967207b96SArnd Bergmann 64067207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 64167207b96SArnd Bergmann size_t len, loff_t *pos) 64267207b96SArnd Bergmann { 6438b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 644c9101bdbSChristoph Hellwig ssize_t ret; 64567207b96SArnd Bergmann u32 mbox_stat; 64667207b96SArnd Bergmann 64767207b96SArnd Bergmann if (len < 4) 64867207b96SArnd Bergmann return -EINVAL; 64967207b96SArnd Bergmann 650c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 651c9101bdbSChristoph Hellwig if (ret) 652c9101bdbSChristoph Hellwig return ret; 6538b3d6663SArnd Bergmann 6548b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 6558b3d6663SArnd Bergmann 6568b3d6663SArnd Bergmann spu_release(ctx); 65767207b96SArnd Bergmann 65867207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 65967207b96SArnd Bergmann return -EFAULT; 66067207b96SArnd Bergmann 66167207b96SArnd Bergmann return 4; 66267207b96SArnd Bergmann } 66367207b96SArnd Bergmann 6645dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = { 66567207b96SArnd Bergmann .open = spufs_pipe_open, 66667207b96SArnd Bergmann .read = spufs_mbox_stat_read, 667fc15351dSArnd Bergmann .llseek = no_llseek, 66867207b96SArnd Bergmann }; 66967207b96SArnd Bergmann 67067207b96SArnd Bergmann /* low-level ibox access function */ 6718b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 67267207b96SArnd Bergmann { 6738b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 67467207b96SArnd Bergmann } 67567207b96SArnd Bergmann 6768b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 6778b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 6788b3d6663SArnd Bergmann { 6798b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 6808b3d6663SArnd Bergmann 6817d7be3aaSAl Viro if (ctx) 6828b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 68367207b96SArnd Bergmann } 68467207b96SArnd Bergmann 685cdcc89bbSArnd Bergmann /* 686cdcc89bbSArnd Bergmann * Read as many bytes from the interrupt mailbox as possible, until 687cdcc89bbSArnd Bergmann * one of the conditions becomes true: 688cdcc89bbSArnd Bergmann * 689cdcc89bbSArnd Bergmann * - no more data available in the mailbox 690cdcc89bbSArnd Bergmann * - end of the user provided buffer 691cdcc89bbSArnd Bergmann * - end of the mapped area 692cdcc89bbSArnd Bergmann * 693cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 694cdcc89bbSArnd Bergmann * any data is available, but return when we have been able to 695cdcc89bbSArnd Bergmann * read something. 696cdcc89bbSArnd Bergmann */ 69767207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 69867207b96SArnd Bergmann size_t len, loff_t *pos) 69967207b96SArnd Bergmann { 7008b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 701cdcc89bbSArnd Bergmann u32 ibox_data, __user *udata; 702cdcc89bbSArnd Bergmann ssize_t count; 70367207b96SArnd Bergmann 70467207b96SArnd Bergmann if (len < 4) 70567207b96SArnd Bergmann return -EINVAL; 70667207b96SArnd Bergmann 70796d4f267SLinus Torvalds if (!access_ok(buf, len)) 708cdcc89bbSArnd Bergmann return -EFAULT; 709cdcc89bbSArnd Bergmann 710cdcc89bbSArnd Bergmann udata = (void __user *)buf; 711cdcc89bbSArnd Bergmann 712c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 713c9101bdbSChristoph Hellwig if (count) 714eebead5bSChristoph Hellwig goto out; 71567207b96SArnd Bergmann 716cdcc89bbSArnd Bergmann /* wait only for the first element */ 717cdcc89bbSArnd Bergmann count = 0; 71867207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 719eebead5bSChristoph Hellwig if (!spu_ibox_read(ctx, &ibox_data)) { 720cdcc89bbSArnd Bergmann count = -EAGAIN; 721eebead5bSChristoph Hellwig goto out_unlock; 722eebead5bSChristoph Hellwig } 72367207b96SArnd Bergmann } else { 724cdcc89bbSArnd Bergmann count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 725cdcc89bbSArnd Bergmann if (count) 726cdcc89bbSArnd Bergmann goto out; 727eebead5bSChristoph Hellwig } 728cdcc89bbSArnd Bergmann 729cdcc89bbSArnd Bergmann /* if we can't write at all, return -EFAULT */ 730cdcc89bbSArnd Bergmann count = __put_user(ibox_data, udata); 731cdcc89bbSArnd Bergmann if (count) 732eebead5bSChristoph Hellwig goto out_unlock; 733cdcc89bbSArnd Bergmann 734cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 735cdcc89bbSArnd Bergmann int ret; 736cdcc89bbSArnd Bergmann ret = ctx->ops->ibox_read(ctx, &ibox_data); 737cdcc89bbSArnd Bergmann if (ret == 0) 738cdcc89bbSArnd Bergmann break; 739cdcc89bbSArnd Bergmann /* 740cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 741cdcc89bbSArnd Bergmann * but still need to return the data we have 742cdcc89bbSArnd Bergmann * read successfully so far. 743cdcc89bbSArnd Bergmann */ 744cdcc89bbSArnd Bergmann ret = __put_user(ibox_data, udata); 745cdcc89bbSArnd Bergmann if (ret) 746cdcc89bbSArnd Bergmann break; 74767207b96SArnd Bergmann } 74867207b96SArnd Bergmann 749eebead5bSChristoph Hellwig out_unlock: 7508b3d6663SArnd Bergmann spu_release(ctx); 751eebead5bSChristoph Hellwig out: 752cdcc89bbSArnd Bergmann return count; 75367207b96SArnd Bergmann } 75467207b96SArnd Bergmann 7558153a5eaSAl Viro static __poll_t spufs_ibox_poll(struct file *file, poll_table *wait) 75667207b96SArnd Bergmann { 7578b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 7588153a5eaSAl Viro __poll_t mask; 75967207b96SArnd Bergmann 7608b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 76167207b96SArnd Bergmann 762c9101bdbSChristoph Hellwig /* 763c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 764c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 765c9101bdbSChristoph Hellwig */ 766c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 767a9a08845SLinus Torvalds mask = ctx->ops->mbox_stat_poll(ctx, EPOLLIN | EPOLLRDNORM); 7683a843d7cSArnd Bergmann spu_release(ctx); 76967207b96SArnd Bergmann 77067207b96SArnd Bergmann return mask; 77167207b96SArnd Bergmann } 77267207b96SArnd Bergmann 7735dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = { 77467207b96SArnd Bergmann .open = spufs_pipe_open, 77567207b96SArnd Bergmann .read = spufs_ibox_read, 77667207b96SArnd Bergmann .poll = spufs_ibox_poll, 777fc15351dSArnd Bergmann .llseek = no_llseek, 77867207b96SArnd Bergmann }; 77967207b96SArnd Bergmann 78067207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 78167207b96SArnd Bergmann size_t len, loff_t *pos) 78267207b96SArnd Bergmann { 7838b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 784c9101bdbSChristoph Hellwig ssize_t ret; 78567207b96SArnd Bergmann u32 ibox_stat; 78667207b96SArnd Bergmann 78767207b96SArnd Bergmann if (len < 4) 78867207b96SArnd Bergmann return -EINVAL; 78967207b96SArnd Bergmann 790c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 791c9101bdbSChristoph Hellwig if (ret) 792c9101bdbSChristoph Hellwig return ret; 7938b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 7948b3d6663SArnd Bergmann spu_release(ctx); 79567207b96SArnd Bergmann 79667207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 79767207b96SArnd Bergmann return -EFAULT; 79867207b96SArnd Bergmann 79967207b96SArnd Bergmann return 4; 80067207b96SArnd Bergmann } 80167207b96SArnd Bergmann 8025dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = { 80367207b96SArnd Bergmann .open = spufs_pipe_open, 80467207b96SArnd Bergmann .read = spufs_ibox_stat_read, 805fc15351dSArnd Bergmann .llseek = no_llseek, 80667207b96SArnd Bergmann }; 80767207b96SArnd Bergmann 80867207b96SArnd Bergmann /* low-level mailbox write */ 8098b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 81067207b96SArnd Bergmann { 8118b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 81267207b96SArnd Bergmann } 81367207b96SArnd Bergmann 8148b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 8158b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 8168b3d6663SArnd Bergmann { 8178b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 8188b3d6663SArnd Bergmann 8197d7be3aaSAl Viro if (ctx) 8208b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 82167207b96SArnd Bergmann } 82267207b96SArnd Bergmann 823cdcc89bbSArnd Bergmann /* 824cdcc89bbSArnd Bergmann * Write as many bytes to the interrupt mailbox as possible, until 825cdcc89bbSArnd Bergmann * one of the conditions becomes true: 826cdcc89bbSArnd Bergmann * 827cdcc89bbSArnd Bergmann * - the mailbox is full 828cdcc89bbSArnd Bergmann * - end of the user provided buffer 829cdcc89bbSArnd Bergmann * - end of the mapped area 830cdcc89bbSArnd Bergmann * 831cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 832027dfac6SMichael Ellerman * space is available, but return when we have been able to 833cdcc89bbSArnd Bergmann * write something. 834cdcc89bbSArnd Bergmann */ 83567207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 83667207b96SArnd Bergmann size_t len, loff_t *pos) 83767207b96SArnd Bergmann { 8388b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 839cdcc89bbSArnd Bergmann u32 wbox_data, __user *udata; 840cdcc89bbSArnd Bergmann ssize_t count; 84167207b96SArnd Bergmann 84267207b96SArnd Bergmann if (len < 4) 84367207b96SArnd Bergmann return -EINVAL; 84467207b96SArnd Bergmann 845cdcc89bbSArnd Bergmann udata = (void __user *)buf; 84696d4f267SLinus Torvalds if (!access_ok(buf, len)) 847cdcc89bbSArnd Bergmann return -EFAULT; 848cdcc89bbSArnd Bergmann 849cdcc89bbSArnd Bergmann if (__get_user(wbox_data, udata)) 85067207b96SArnd Bergmann return -EFAULT; 85167207b96SArnd Bergmann 852c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 853c9101bdbSChristoph Hellwig if (count) 854eebead5bSChristoph Hellwig goto out; 8558b3d6663SArnd Bergmann 856cdcc89bbSArnd Bergmann /* 857cdcc89bbSArnd Bergmann * make sure we can at least write one element, by waiting 858cdcc89bbSArnd Bergmann * in case of !O_NONBLOCK 859cdcc89bbSArnd Bergmann */ 860cdcc89bbSArnd Bergmann count = 0; 86167207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 862eebead5bSChristoph Hellwig if (!spu_wbox_write(ctx, wbox_data)) { 863cdcc89bbSArnd Bergmann count = -EAGAIN; 864eebead5bSChristoph Hellwig goto out_unlock; 865eebead5bSChristoph Hellwig } 86667207b96SArnd Bergmann } else { 867cdcc89bbSArnd Bergmann count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 868cdcc89bbSArnd Bergmann if (count) 869cdcc89bbSArnd Bergmann goto out; 870eebead5bSChristoph Hellwig } 871eebead5bSChristoph Hellwig 8728b3d6663SArnd Bergmann 87396de0e25SJan Engelhardt /* write as much as possible */ 874cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 875cdcc89bbSArnd Bergmann int ret; 876cdcc89bbSArnd Bergmann ret = __get_user(wbox_data, udata); 877cdcc89bbSArnd Bergmann if (ret) 878cdcc89bbSArnd Bergmann break; 879cdcc89bbSArnd Bergmann 880cdcc89bbSArnd Bergmann ret = spu_wbox_write(ctx, wbox_data); 881cdcc89bbSArnd Bergmann if (ret == 0) 882cdcc89bbSArnd Bergmann break; 883cdcc89bbSArnd Bergmann } 884cdcc89bbSArnd Bergmann 885eebead5bSChristoph Hellwig out_unlock: 886cdcc89bbSArnd Bergmann spu_release(ctx); 887eebead5bSChristoph Hellwig out: 888cdcc89bbSArnd Bergmann return count; 88967207b96SArnd Bergmann } 89067207b96SArnd Bergmann 8918153a5eaSAl Viro static __poll_t spufs_wbox_poll(struct file *file, poll_table *wait) 89267207b96SArnd Bergmann { 8938b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 8948153a5eaSAl Viro __poll_t mask; 89567207b96SArnd Bergmann 8968b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 89767207b96SArnd Bergmann 898c9101bdbSChristoph Hellwig /* 899c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 900c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 901c9101bdbSChristoph Hellwig */ 902c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 903a9a08845SLinus Torvalds mask = ctx->ops->mbox_stat_poll(ctx, EPOLLOUT | EPOLLWRNORM); 9043a843d7cSArnd Bergmann spu_release(ctx); 90567207b96SArnd Bergmann 90667207b96SArnd Bergmann return mask; 90767207b96SArnd Bergmann } 90867207b96SArnd Bergmann 9095dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = { 91067207b96SArnd Bergmann .open = spufs_pipe_open, 91167207b96SArnd Bergmann .write = spufs_wbox_write, 91267207b96SArnd Bergmann .poll = spufs_wbox_poll, 913fc15351dSArnd Bergmann .llseek = no_llseek, 91467207b96SArnd Bergmann }; 91567207b96SArnd Bergmann 91667207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 91767207b96SArnd Bergmann size_t len, loff_t *pos) 91867207b96SArnd Bergmann { 9198b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 920c9101bdbSChristoph Hellwig ssize_t ret; 92167207b96SArnd Bergmann u32 wbox_stat; 92267207b96SArnd Bergmann 92367207b96SArnd Bergmann if (len < 4) 92467207b96SArnd Bergmann return -EINVAL; 92567207b96SArnd Bergmann 926c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 927c9101bdbSChristoph Hellwig if (ret) 928c9101bdbSChristoph Hellwig return ret; 9298b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 9308b3d6663SArnd Bergmann spu_release(ctx); 93167207b96SArnd Bergmann 93267207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 93367207b96SArnd Bergmann return -EFAULT; 93467207b96SArnd Bergmann 93567207b96SArnd Bergmann return 4; 93667207b96SArnd Bergmann } 93767207b96SArnd Bergmann 9385dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = { 93967207b96SArnd Bergmann .open = spufs_pipe_open, 94067207b96SArnd Bergmann .read = spufs_wbox_stat_read, 941fc15351dSArnd Bergmann .llseek = no_llseek, 94267207b96SArnd Bergmann }; 94367207b96SArnd Bergmann 9446df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file) 9456df10a82SMark Nutter { 9466df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 9476df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 94843c2bbd9SChristoph Hellwig 94947d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 9506df10a82SMark Nutter file->private_data = ctx; 95143c2bbd9SChristoph Hellwig if (!i->i_openers++) 9526df10a82SMark Nutter ctx->signal1 = inode->i_mapping; 95347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 9546df10a82SMark Nutter return nonseekable_open(inode, file); 9556df10a82SMark Nutter } 9566df10a82SMark Nutter 95743c2bbd9SChristoph Hellwig static int 95843c2bbd9SChristoph Hellwig spufs_signal1_release(struct inode *inode, struct file *file) 95943c2bbd9SChristoph Hellwig { 96043c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 96143c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 96243c2bbd9SChristoph Hellwig 96347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 96443c2bbd9SChristoph Hellwig if (!--i->i_openers) 96543c2bbd9SChristoph Hellwig ctx->signal1 = NULL; 96647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 96743c2bbd9SChristoph Hellwig return 0; 96843c2bbd9SChristoph Hellwig } 96943c2bbd9SChristoph Hellwig 970bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf, 97167207b96SArnd Bergmann size_t len, loff_t *pos) 97267207b96SArnd Bergmann { 97317f88cebSDwayne Grant McConnell int ret = 0; 97467207b96SArnd Bergmann u32 data; 97567207b96SArnd Bergmann 97667207b96SArnd Bergmann if (len < 4) 97767207b96SArnd Bergmann return -EINVAL; 97867207b96SArnd Bergmann 97917f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[3]) { 98017f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[3]; 98117f88cebSDwayne Grant McConnell ret = 4; 98217f88cebSDwayne Grant McConnell } 9838b3d6663SArnd Bergmann 98417f88cebSDwayne Grant McConnell if (!ret) 98517f88cebSDwayne Grant McConnell goto out; 98617f88cebSDwayne Grant McConnell 98767207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 98867207b96SArnd Bergmann return -EFAULT; 98967207b96SArnd Bergmann 99017f88cebSDwayne Grant McConnell out: 99117f88cebSDwayne Grant McConnell return ret; 99267207b96SArnd Bergmann } 99367207b96SArnd Bergmann 994bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 995bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 996bf1ab978SDwayne Grant McConnell { 997bf1ab978SDwayne Grant McConnell int ret; 998bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 999bf1ab978SDwayne Grant McConnell 1000c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1001c9101bdbSChristoph Hellwig if (ret) 1002c9101bdbSChristoph Hellwig return ret; 1003bf1ab978SDwayne Grant McConnell ret = __spufs_signal1_read(ctx, buf, len, pos); 100427b1ea09SChristoph Hellwig spu_release_saved(ctx); 1005bf1ab978SDwayne Grant McConnell 1006bf1ab978SDwayne Grant McConnell return ret; 1007bf1ab978SDwayne Grant McConnell } 1008bf1ab978SDwayne Grant McConnell 100967207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 101067207b96SArnd Bergmann size_t len, loff_t *pos) 101167207b96SArnd Bergmann { 101267207b96SArnd Bergmann struct spu_context *ctx; 1013c9101bdbSChristoph Hellwig ssize_t ret; 101467207b96SArnd Bergmann u32 data; 101567207b96SArnd Bergmann 101667207b96SArnd Bergmann ctx = file->private_data; 101767207b96SArnd Bergmann 101867207b96SArnd Bergmann if (len < 4) 101967207b96SArnd Bergmann return -EINVAL; 102067207b96SArnd Bergmann 102167207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 102267207b96SArnd Bergmann return -EFAULT; 102367207b96SArnd Bergmann 1024c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1025c9101bdbSChristoph Hellwig if (ret) 1026c9101bdbSChristoph Hellwig return ret; 10278b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 10288b3d6663SArnd Bergmann spu_release(ctx); 102967207b96SArnd Bergmann 103067207b96SArnd Bergmann return 4; 103167207b96SArnd Bergmann } 103267207b96SArnd Bergmann 1033e807f02cSSouptick Joarder static vm_fault_t 103411bac800SDave Jiang spufs_signal1_mmap_fault(struct vm_fault *vmf) 10356df10a82SMark Nutter { 103687ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 103711bac800SDave Jiang return spufs_ps_fault(vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE); 103887ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 103927d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 104027d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 104127d5bf2aSBenjamin Herrenschmidt */ 104211bac800SDave Jiang return spufs_ps_fault(vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 104327d5bf2aSBenjamin Herrenschmidt #else 104427d5bf2aSBenjamin Herrenschmidt #error unsupported page size 104527d5bf2aSBenjamin Herrenschmidt #endif 10466df10a82SMark Nutter } 10476df10a82SMark Nutter 1048f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal1_mmap_vmops = { 1049b1e2270fSNick Piggin .fault = spufs_signal1_mmap_fault, 10506df10a82SMark Nutter }; 10516df10a82SMark Nutter 10526df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) 10536df10a82SMark Nutter { 10546df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 10556df10a82SMark Nutter return -EINVAL; 10566df10a82SMark Nutter 105778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 105864b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 10596df10a82SMark Nutter 10606df10a82SMark Nutter vma->vm_ops = &spufs_signal1_mmap_vmops; 10616df10a82SMark Nutter return 0; 10626df10a82SMark Nutter } 10636df10a82SMark Nutter 10645dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = { 10656df10a82SMark Nutter .open = spufs_signal1_open, 106643c2bbd9SChristoph Hellwig .release = spufs_signal1_release, 106767207b96SArnd Bergmann .read = spufs_signal1_read, 106867207b96SArnd Bergmann .write = spufs_signal1_write, 10696df10a82SMark Nutter .mmap = spufs_signal1_mmap, 1070fc15351dSArnd Bergmann .llseek = no_llseek, 107167207b96SArnd Bergmann }; 107267207b96SArnd Bergmann 1073d054b36fSJeremy Kerr static const struct file_operations spufs_signal1_nosched_fops = { 1074d054b36fSJeremy Kerr .open = spufs_signal1_open, 1075d054b36fSJeremy Kerr .release = spufs_signal1_release, 1076d054b36fSJeremy Kerr .write = spufs_signal1_write, 1077d054b36fSJeremy Kerr .mmap = spufs_signal1_mmap, 1078fc15351dSArnd Bergmann .llseek = no_llseek, 1079d054b36fSJeremy Kerr }; 1080d054b36fSJeremy Kerr 10816df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file) 10826df10a82SMark Nutter { 10836df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 10846df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 108543c2bbd9SChristoph Hellwig 108647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 10876df10a82SMark Nutter file->private_data = ctx; 108843c2bbd9SChristoph Hellwig if (!i->i_openers++) 10896df10a82SMark Nutter ctx->signal2 = inode->i_mapping; 109047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 10916df10a82SMark Nutter return nonseekable_open(inode, file); 10926df10a82SMark Nutter } 10936df10a82SMark Nutter 109443c2bbd9SChristoph Hellwig static int 109543c2bbd9SChristoph Hellwig spufs_signal2_release(struct inode *inode, struct file *file) 109643c2bbd9SChristoph Hellwig { 109743c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 109843c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 109943c2bbd9SChristoph Hellwig 110047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 110143c2bbd9SChristoph Hellwig if (!--i->i_openers) 110243c2bbd9SChristoph Hellwig ctx->signal2 = NULL; 110347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 110443c2bbd9SChristoph Hellwig return 0; 110543c2bbd9SChristoph Hellwig } 110643c2bbd9SChristoph Hellwig 1107bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf, 110867207b96SArnd Bergmann size_t len, loff_t *pos) 110967207b96SArnd Bergmann { 111017f88cebSDwayne Grant McConnell int ret = 0; 111167207b96SArnd Bergmann u32 data; 111267207b96SArnd Bergmann 111367207b96SArnd Bergmann if (len < 4) 111467207b96SArnd Bergmann return -EINVAL; 111567207b96SArnd Bergmann 111617f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[4]) { 111717f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[4]; 111817f88cebSDwayne Grant McConnell ret = 4; 111917f88cebSDwayne Grant McConnell } 11208b3d6663SArnd Bergmann 112117f88cebSDwayne Grant McConnell if (!ret) 112217f88cebSDwayne Grant McConnell goto out; 112317f88cebSDwayne Grant McConnell 112467207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 112567207b96SArnd Bergmann return -EFAULT; 112667207b96SArnd Bergmann 112717f88cebSDwayne Grant McConnell out: 1128bf1ab978SDwayne Grant McConnell return ret; 1129bf1ab978SDwayne Grant McConnell } 1130bf1ab978SDwayne Grant McConnell 1131bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 1132bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1133bf1ab978SDwayne Grant McConnell { 1134bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1135bf1ab978SDwayne Grant McConnell int ret; 1136bf1ab978SDwayne Grant McConnell 1137c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1138c9101bdbSChristoph Hellwig if (ret) 1139c9101bdbSChristoph Hellwig return ret; 1140bf1ab978SDwayne Grant McConnell ret = __spufs_signal2_read(ctx, buf, len, pos); 114127b1ea09SChristoph Hellwig spu_release_saved(ctx); 1142bf1ab978SDwayne Grant McConnell 1143bf1ab978SDwayne Grant McConnell return ret; 114467207b96SArnd Bergmann } 114567207b96SArnd Bergmann 114667207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 114767207b96SArnd Bergmann size_t len, loff_t *pos) 114867207b96SArnd Bergmann { 114967207b96SArnd Bergmann struct spu_context *ctx; 1150c9101bdbSChristoph Hellwig ssize_t ret; 115167207b96SArnd Bergmann u32 data; 115267207b96SArnd Bergmann 115367207b96SArnd Bergmann ctx = file->private_data; 115467207b96SArnd Bergmann 115567207b96SArnd Bergmann if (len < 4) 115667207b96SArnd Bergmann return -EINVAL; 115767207b96SArnd Bergmann 115867207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 115967207b96SArnd Bergmann return -EFAULT; 116067207b96SArnd Bergmann 1161c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1162c9101bdbSChristoph Hellwig if (ret) 1163c9101bdbSChristoph Hellwig return ret; 11648b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 11658b3d6663SArnd Bergmann spu_release(ctx); 116667207b96SArnd Bergmann 116767207b96SArnd Bergmann return 4; 116867207b96SArnd Bergmann } 116967207b96SArnd Bergmann 117027d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1171e807f02cSSouptick Joarder static vm_fault_t 117211bac800SDave Jiang spufs_signal2_mmap_fault(struct vm_fault *vmf) 11736df10a82SMark Nutter { 117487ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 117511bac800SDave Jiang return spufs_ps_fault(vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE); 117687ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 117727d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 117827d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 117927d5bf2aSBenjamin Herrenschmidt */ 118011bac800SDave Jiang return spufs_ps_fault(vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 118127d5bf2aSBenjamin Herrenschmidt #else 118227d5bf2aSBenjamin Herrenschmidt #error unsupported page size 118327d5bf2aSBenjamin Herrenschmidt #endif 11846df10a82SMark Nutter } 11856df10a82SMark Nutter 1186f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_signal2_mmap_vmops = { 1187b1e2270fSNick Piggin .fault = spufs_signal2_mmap_fault, 11886df10a82SMark Nutter }; 11896df10a82SMark Nutter 11906df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) 11916df10a82SMark Nutter { 11926df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 11936df10a82SMark Nutter return -EINVAL; 11946df10a82SMark Nutter 119578bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 119664b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 11976df10a82SMark Nutter 11986df10a82SMark Nutter vma->vm_ops = &spufs_signal2_mmap_vmops; 11996df10a82SMark Nutter return 0; 12006df10a82SMark Nutter } 120127d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 120227d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL 120327d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 12046df10a82SMark Nutter 12055dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = { 12066df10a82SMark Nutter .open = spufs_signal2_open, 120743c2bbd9SChristoph Hellwig .release = spufs_signal2_release, 120867207b96SArnd Bergmann .read = spufs_signal2_read, 120967207b96SArnd Bergmann .write = spufs_signal2_write, 12106df10a82SMark Nutter .mmap = spufs_signal2_mmap, 1211fc15351dSArnd Bergmann .llseek = no_llseek, 121267207b96SArnd Bergmann }; 121367207b96SArnd Bergmann 1214d054b36fSJeremy Kerr static const struct file_operations spufs_signal2_nosched_fops = { 1215d054b36fSJeremy Kerr .open = spufs_signal2_open, 1216d054b36fSJeremy Kerr .release = spufs_signal2_release, 1217d054b36fSJeremy Kerr .write = spufs_signal2_write, 1218d054b36fSJeremy Kerr .mmap = spufs_signal2_mmap, 1219fc15351dSArnd Bergmann .llseek = no_llseek, 1220d054b36fSJeremy Kerr }; 1221d054b36fSJeremy Kerr 1222104f0cc2SMichael Ellerman /* 1223104f0cc2SMichael Ellerman * This is a wrapper around DEFINE_SIMPLE_ATTRIBUTE which does the 1224104f0cc2SMichael Ellerman * work of acquiring (or not) the SPU context before calling through 1225104f0cc2SMichael Ellerman * to the actual get routine. The set routine is called directly. 1226104f0cc2SMichael Ellerman */ 1227104f0cc2SMichael Ellerman #define SPU_ATTR_NOACQUIRE 0 1228104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE 1 1229104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE_SAVED 2 1230104f0cc2SMichael Ellerman 1231104f0cc2SMichael Ellerman #define DEFINE_SPUFS_ATTRIBUTE(__name, __get, __set, __fmt, __acquire) \ 1232197b1a82SChristoph Hellwig static int __##__get(void *data, u64 *val) \ 1233104f0cc2SMichael Ellerman { \ 1234104f0cc2SMichael Ellerman struct spu_context *ctx = data; \ 1235c9101bdbSChristoph Hellwig int ret = 0; \ 1236104f0cc2SMichael Ellerman \ 1237104f0cc2SMichael Ellerman if (__acquire == SPU_ATTR_ACQUIRE) { \ 1238c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); \ 1239c9101bdbSChristoph Hellwig if (ret) \ 1240c9101bdbSChristoph Hellwig return ret; \ 1241197b1a82SChristoph Hellwig *val = __get(ctx); \ 1242104f0cc2SMichael Ellerman spu_release(ctx); \ 1243104f0cc2SMichael Ellerman } else if (__acquire == SPU_ATTR_ACQUIRE_SAVED) { \ 1244c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); \ 1245c9101bdbSChristoph Hellwig if (ret) \ 1246c9101bdbSChristoph Hellwig return ret; \ 1247197b1a82SChristoph Hellwig *val = __get(ctx); \ 1248104f0cc2SMichael Ellerman spu_release_saved(ctx); \ 1249104f0cc2SMichael Ellerman } else \ 1250197b1a82SChristoph Hellwig *val = __get(ctx); \ 1251104f0cc2SMichael Ellerman \ 1252197b1a82SChristoph Hellwig return 0; \ 1253104f0cc2SMichael Ellerman } \ 1254197b1a82SChristoph Hellwig DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__name, __##__get, __set, __fmt); 1255104f0cc2SMichael Ellerman 1256197b1a82SChristoph Hellwig static int spufs_signal1_type_set(void *data, u64 val) 125767207b96SArnd Bergmann { 125867207b96SArnd Bergmann struct spu_context *ctx = data; 1259c9101bdbSChristoph Hellwig int ret; 126067207b96SArnd Bergmann 1261c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1262c9101bdbSChristoph Hellwig if (ret) 1263c9101bdbSChristoph Hellwig return ret; 12648b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 12658b3d6663SArnd Bergmann spu_release(ctx); 1266197b1a82SChristoph Hellwig 1267197b1a82SChristoph Hellwig return 0; 126867207b96SArnd Bergmann } 126967207b96SArnd Bergmann 1270104f0cc2SMichael Ellerman static u64 spufs_signal1_type_get(struct spu_context *ctx) 1271bf1ab978SDwayne Grant McConnell { 1272bf1ab978SDwayne Grant McConnell return ctx->ops->signal1_type_get(ctx); 1273bf1ab978SDwayne Grant McConnell } 1274104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 1275af8b44e0SJeremy Kerr spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 1276bf1ab978SDwayne Grant McConnell 127767207b96SArnd Bergmann 1278197b1a82SChristoph Hellwig static int spufs_signal2_type_set(void *data, u64 val) 127967207b96SArnd Bergmann { 128067207b96SArnd Bergmann struct spu_context *ctx = data; 1281c9101bdbSChristoph Hellwig int ret; 128267207b96SArnd Bergmann 1283c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1284c9101bdbSChristoph Hellwig if (ret) 1285c9101bdbSChristoph Hellwig return ret; 12868b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 12878b3d6663SArnd Bergmann spu_release(ctx); 1288197b1a82SChristoph Hellwig 1289197b1a82SChristoph Hellwig return 0; 129067207b96SArnd Bergmann } 129167207b96SArnd Bergmann 1292104f0cc2SMichael Ellerman static u64 spufs_signal2_type_get(struct spu_context *ctx) 1293bf1ab978SDwayne Grant McConnell { 1294bf1ab978SDwayne Grant McConnell return ctx->ops->signal2_type_get(ctx); 1295bf1ab978SDwayne Grant McConnell } 1296104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 1297af8b44e0SJeremy Kerr spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 129867207b96SArnd Bergmann 129927d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1300e807f02cSSouptick Joarder static vm_fault_t 130111bac800SDave Jiang spufs_mss_mmap_fault(struct vm_fault *vmf) 1302d9379c4bSarnd@arndb.de { 130311bac800SDave Jiang return spufs_ps_fault(vmf, 0x0000, SPUFS_MSS_MAP_SIZE); 1304d9379c4bSarnd@arndb.de } 1305d9379c4bSarnd@arndb.de 1306f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mss_mmap_vmops = { 1307b1e2270fSNick Piggin .fault = spufs_mss_mmap_fault, 1308d9379c4bSarnd@arndb.de }; 1309d9379c4bSarnd@arndb.de 1310d9379c4bSarnd@arndb.de /* 1311d9379c4bSarnd@arndb.de * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 1312d9379c4bSarnd@arndb.de */ 1313d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) 1314d9379c4bSarnd@arndb.de { 1315d9379c4bSarnd@arndb.de if (!(vma->vm_flags & VM_SHARED)) 1316d9379c4bSarnd@arndb.de return -EINVAL; 1317d9379c4bSarnd@arndb.de 131878bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 131964b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 1320d9379c4bSarnd@arndb.de 1321d9379c4bSarnd@arndb.de vma->vm_ops = &spufs_mss_mmap_vmops; 1322d9379c4bSarnd@arndb.de return 0; 1323d9379c4bSarnd@arndb.de } 132427d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 132527d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL 132627d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1327d9379c4bSarnd@arndb.de 1328d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file) 1329d9379c4bSarnd@arndb.de { 1330d9379c4bSarnd@arndb.de struct spufs_inode_info *i = SPUFS_I(inode); 133117e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 1332d9379c4bSarnd@arndb.de 1333d9379c4bSarnd@arndb.de file->private_data = i->i_ctx; 133443c2bbd9SChristoph Hellwig 133547d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 133643c2bbd9SChristoph Hellwig if (!i->i_openers++) 133717e0e270SBenjamin Herrenschmidt ctx->mss = inode->i_mapping; 133847d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1339d9379c4bSarnd@arndb.de return nonseekable_open(inode, file); 1340d9379c4bSarnd@arndb.de } 1341d9379c4bSarnd@arndb.de 134243c2bbd9SChristoph Hellwig static int 134343c2bbd9SChristoph Hellwig spufs_mss_release(struct inode *inode, struct file *file) 134443c2bbd9SChristoph Hellwig { 134543c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 134643c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 134743c2bbd9SChristoph Hellwig 134847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 134943c2bbd9SChristoph Hellwig if (!--i->i_openers) 135043c2bbd9SChristoph Hellwig ctx->mss = NULL; 135147d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 135243c2bbd9SChristoph Hellwig return 0; 135343c2bbd9SChristoph Hellwig } 135443c2bbd9SChristoph Hellwig 13555dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = { 1356d9379c4bSarnd@arndb.de .open = spufs_mss_open, 135743c2bbd9SChristoph Hellwig .release = spufs_mss_release, 1358d9379c4bSarnd@arndb.de .mmap = spufs_mss_mmap, 1359fc15351dSArnd Bergmann .llseek = no_llseek, 136027d5bf2aSBenjamin Herrenschmidt }; 136127d5bf2aSBenjamin Herrenschmidt 1362e807f02cSSouptick Joarder static vm_fault_t 136311bac800SDave Jiang spufs_psmap_mmap_fault(struct vm_fault *vmf) 136427d5bf2aSBenjamin Herrenschmidt { 136511bac800SDave Jiang return spufs_ps_fault(vmf, 0x0000, SPUFS_PS_MAP_SIZE); 136627d5bf2aSBenjamin Herrenschmidt } 136727d5bf2aSBenjamin Herrenschmidt 1368f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_psmap_mmap_vmops = { 1369b1e2270fSNick Piggin .fault = spufs_psmap_mmap_fault, 137027d5bf2aSBenjamin Herrenschmidt }; 137127d5bf2aSBenjamin Herrenschmidt 137227d5bf2aSBenjamin Herrenschmidt /* 137327d5bf2aSBenjamin Herrenschmidt * mmap support for full problem state area [0x00000 - 0x1ffff]. 137427d5bf2aSBenjamin Herrenschmidt */ 137527d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) 137627d5bf2aSBenjamin Herrenschmidt { 137727d5bf2aSBenjamin Herrenschmidt if (!(vma->vm_flags & VM_SHARED)) 137827d5bf2aSBenjamin Herrenschmidt return -EINVAL; 137927d5bf2aSBenjamin Herrenschmidt 138078bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 138164b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 138227d5bf2aSBenjamin Herrenschmidt 138327d5bf2aSBenjamin Herrenschmidt vma->vm_ops = &spufs_psmap_mmap_vmops; 138427d5bf2aSBenjamin Herrenschmidt return 0; 138527d5bf2aSBenjamin Herrenschmidt } 138627d5bf2aSBenjamin Herrenschmidt 138727d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file) 138827d5bf2aSBenjamin Herrenschmidt { 138927d5bf2aSBenjamin Herrenschmidt struct spufs_inode_info *i = SPUFS_I(inode); 139017e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 139127d5bf2aSBenjamin Herrenschmidt 139247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 139327d5bf2aSBenjamin Herrenschmidt file->private_data = i->i_ctx; 139443c2bbd9SChristoph Hellwig if (!i->i_openers++) 139517e0e270SBenjamin Herrenschmidt ctx->psmap = inode->i_mapping; 139647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 139727d5bf2aSBenjamin Herrenschmidt return nonseekable_open(inode, file); 139827d5bf2aSBenjamin Herrenschmidt } 139927d5bf2aSBenjamin Herrenschmidt 140043c2bbd9SChristoph Hellwig static int 140143c2bbd9SChristoph Hellwig spufs_psmap_release(struct inode *inode, struct file *file) 140243c2bbd9SChristoph Hellwig { 140343c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 140443c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 140543c2bbd9SChristoph Hellwig 140647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 140743c2bbd9SChristoph Hellwig if (!--i->i_openers) 140843c2bbd9SChristoph Hellwig ctx->psmap = NULL; 140947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 141043c2bbd9SChristoph Hellwig return 0; 141143c2bbd9SChristoph Hellwig } 141243c2bbd9SChristoph Hellwig 14135dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = { 141427d5bf2aSBenjamin Herrenschmidt .open = spufs_psmap_open, 141543c2bbd9SChristoph Hellwig .release = spufs_psmap_release, 141627d5bf2aSBenjamin Herrenschmidt .mmap = spufs_psmap_mmap, 1417fc15351dSArnd Bergmann .llseek = no_llseek, 1418d9379c4bSarnd@arndb.de }; 1419d9379c4bSarnd@arndb.de 1420d9379c4bSarnd@arndb.de 142127d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1422e807f02cSSouptick Joarder static vm_fault_t 142311bac800SDave Jiang spufs_mfc_mmap_fault(struct vm_fault *vmf) 14246df10a82SMark Nutter { 142511bac800SDave Jiang return spufs_ps_fault(vmf, 0x3000, SPUFS_MFC_MAP_SIZE); 14266df10a82SMark Nutter } 14276df10a82SMark Nutter 1428f0f37e2fSAlexey Dobriyan static const struct vm_operations_struct spufs_mfc_mmap_vmops = { 1429b1e2270fSNick Piggin .fault = spufs_mfc_mmap_fault, 14306df10a82SMark Nutter }; 14316df10a82SMark Nutter 14326df10a82SMark Nutter /* 14336df10a82SMark Nutter * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 14346df10a82SMark Nutter */ 14356df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) 14366df10a82SMark Nutter { 14376df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 14386df10a82SMark Nutter return -EINVAL; 14396df10a82SMark Nutter 144078bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 144164b3d0e8SBenjamin Herrenschmidt vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 14426df10a82SMark Nutter 14436df10a82SMark Nutter vma->vm_ops = &spufs_mfc_mmap_vmops; 14446df10a82SMark Nutter return 0; 14456df10a82SMark Nutter } 144627d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 144727d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL 144827d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1449a33a7d73SArnd Bergmann 1450a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 1451a33a7d73SArnd Bergmann { 1452a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1453a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 1454a33a7d73SArnd Bergmann 1455a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 1456a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 1457a33a7d73SArnd Bergmann return -EINVAL; 1458a33a7d73SArnd Bergmann 1459a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 1460a33a7d73SArnd Bergmann return -EBUSY; 1461a33a7d73SArnd Bergmann 146247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1463a33a7d73SArnd Bergmann file->private_data = ctx; 146443c2bbd9SChristoph Hellwig if (!i->i_openers++) 146517e0e270SBenjamin Herrenschmidt ctx->mfc = inode->i_mapping; 146647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1467a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 1468a33a7d73SArnd Bergmann } 1469a33a7d73SArnd Bergmann 147043c2bbd9SChristoph Hellwig static int 147143c2bbd9SChristoph Hellwig spufs_mfc_release(struct inode *inode, struct file *file) 147243c2bbd9SChristoph Hellwig { 147343c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 147443c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 147543c2bbd9SChristoph Hellwig 147647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 147743c2bbd9SChristoph Hellwig if (!--i->i_openers) 147843c2bbd9SChristoph Hellwig ctx->mfc = NULL; 147947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 148043c2bbd9SChristoph Hellwig return 0; 148143c2bbd9SChristoph Hellwig } 148243c2bbd9SChristoph Hellwig 1483a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 1484a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 1485a33a7d73SArnd Bergmann { 1486a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 1487a33a7d73SArnd Bergmann 14887d7be3aaSAl Viro if (ctx) 1489a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 1490a33a7d73SArnd Bergmann } 1491a33a7d73SArnd Bergmann 1492a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 1493a33a7d73SArnd Bergmann { 1494a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 1495a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 1496a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 1497a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 1498a33a7d73SArnd Bergmann if (*status) 1499a33a7d73SArnd Bergmann return 1; 1500a33a7d73SArnd Bergmann 1501a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 1502a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 1503a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1504a33a7d73SArnd Bergmann return 0; 1505a33a7d73SArnd Bergmann } 1506a33a7d73SArnd Bergmann 1507a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 1508a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1509a33a7d73SArnd Bergmann { 1510a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1511a33a7d73SArnd Bergmann int ret = -EINVAL; 1512a33a7d73SArnd Bergmann u32 status; 1513a33a7d73SArnd Bergmann 1514a33a7d73SArnd Bergmann if (size != 4) 1515a33a7d73SArnd Bergmann goto out; 1516a33a7d73SArnd Bergmann 1517c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1518c9101bdbSChristoph Hellwig if (ret) 1519c9101bdbSChristoph Hellwig return ret; 1520c9101bdbSChristoph Hellwig 1521c9101bdbSChristoph Hellwig ret = -EINVAL; 1522a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1523a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 1524a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 1525a33a7d73SArnd Bergmann ret = -EAGAIN; 1526a33a7d73SArnd Bergmann else 1527c9101bdbSChristoph Hellwig /* XXX(hch): shouldn't we clear ret here? */ 1528a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 1529a33a7d73SArnd Bergmann } else { 1530a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1531a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 1532a33a7d73SArnd Bergmann if (ret) 1533a33a7d73SArnd Bergmann goto out; 1534eebead5bSChristoph Hellwig } 1535eebead5bSChristoph Hellwig spu_release(ctx); 1536a33a7d73SArnd Bergmann 1537a33a7d73SArnd Bergmann ret = 4; 1538a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 1539a33a7d73SArnd Bergmann ret = -EFAULT; 1540a33a7d73SArnd Bergmann 1541a33a7d73SArnd Bergmann out: 1542a33a7d73SArnd Bergmann return ret; 1543a33a7d73SArnd Bergmann } 1544a33a7d73SArnd Bergmann 1545a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 1546a33a7d73SArnd Bergmann { 15479477e455SStephen Rothwell pr_debug("queueing DMA %x %llx %x %x %x\n", cmd->lsa, 1548a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 1549a33a7d73SArnd Bergmann 1550a33a7d73SArnd Bergmann switch (cmd->cmd) { 1551a33a7d73SArnd Bergmann case MFC_PUT_CMD: 1552a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 1553a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 1554a33a7d73SArnd Bergmann case MFC_GET_CMD: 1555a33a7d73SArnd Bergmann case MFC_GETF_CMD: 1556a33a7d73SArnd Bergmann case MFC_GETB_CMD: 1557a33a7d73SArnd Bergmann break; 1558a33a7d73SArnd Bergmann default: 1559a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 1560a33a7d73SArnd Bergmann return -EIO; 1561a33a7d73SArnd Bergmann } 1562a33a7d73SArnd Bergmann 1563a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 15649477e455SStephen Rothwell pr_debug("invalid DMA alignment, ea %llx lsa %x\n", 1565a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 1566a33a7d73SArnd Bergmann return -EIO; 1567a33a7d73SArnd Bergmann } 1568a33a7d73SArnd Bergmann 1569a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 1570a33a7d73SArnd Bergmann case 1: 1571a33a7d73SArnd Bergmann break; 1572a33a7d73SArnd Bergmann case 2: 1573a33a7d73SArnd Bergmann if (cmd->lsa & 1) 1574a33a7d73SArnd Bergmann goto error; 1575a33a7d73SArnd Bergmann break; 1576a33a7d73SArnd Bergmann case 4: 1577a33a7d73SArnd Bergmann if (cmd->lsa & 3) 1578a33a7d73SArnd Bergmann goto error; 1579a33a7d73SArnd Bergmann break; 1580a33a7d73SArnd Bergmann case 8: 1581a33a7d73SArnd Bergmann if (cmd->lsa & 7) 1582a33a7d73SArnd Bergmann goto error; 1583a33a7d73SArnd Bergmann break; 1584a33a7d73SArnd Bergmann case 0: 1585a33a7d73SArnd Bergmann if (cmd->lsa & 15) 1586a33a7d73SArnd Bergmann goto error; 1587a33a7d73SArnd Bergmann break; 1588a33a7d73SArnd Bergmann error: 1589a33a7d73SArnd Bergmann default: 1590a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 1591a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 1592a33a7d73SArnd Bergmann return -EIO; 1593a33a7d73SArnd Bergmann } 1594a33a7d73SArnd Bergmann 1595a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 1596a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 1597a33a7d73SArnd Bergmann return -EIO; 1598a33a7d73SArnd Bergmann } 1599a33a7d73SArnd Bergmann 1600a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 1601a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 1602a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 1603a33a7d73SArnd Bergmann return -EIO; 1604a33a7d73SArnd Bergmann } 1605a33a7d73SArnd Bergmann 1606a33a7d73SArnd Bergmann if (cmd->class) { 1607a33a7d73SArnd Bergmann /* not supported in this version */ 1608a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 1609a33a7d73SArnd Bergmann return -EIO; 1610a33a7d73SArnd Bergmann } 1611a33a7d73SArnd Bergmann 1612a33a7d73SArnd Bergmann return 0; 1613a33a7d73SArnd Bergmann } 1614a33a7d73SArnd Bergmann 1615a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 1616a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 1617a33a7d73SArnd Bergmann int *error) 1618a33a7d73SArnd Bergmann { 1619a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1620a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 1621a33a7d73SArnd Bergmann /* wait for any tag group to complete 1622a33a7d73SArnd Bergmann so we have space for the new command */ 1623a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1624a33a7d73SArnd Bergmann /* try again, because the queue might be 1625a33a7d73SArnd Bergmann empty again */ 1626a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1627a33a7d73SArnd Bergmann if (*error == -EAGAIN) 1628a33a7d73SArnd Bergmann return 0; 1629a33a7d73SArnd Bergmann } 1630a33a7d73SArnd Bergmann return 1; 1631a33a7d73SArnd Bergmann } 1632a33a7d73SArnd Bergmann 1633a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 1634a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1635a33a7d73SArnd Bergmann { 1636a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1637a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 1638a33a7d73SArnd Bergmann int ret = -EINVAL; 1639a33a7d73SArnd Bergmann 1640a33a7d73SArnd Bergmann if (size != sizeof cmd) 1641a33a7d73SArnd Bergmann goto out; 1642a33a7d73SArnd Bergmann 1643a33a7d73SArnd Bergmann ret = -EFAULT; 1644a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 1645a33a7d73SArnd Bergmann goto out; 1646a33a7d73SArnd Bergmann 1647a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 1648a33a7d73SArnd Bergmann if (ret) 1649a33a7d73SArnd Bergmann goto out; 1650a33a7d73SArnd Bergmann 1651c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1652c9101bdbSChristoph Hellwig if (ret) 1653c9101bdbSChristoph Hellwig goto out; 1654c9101bdbSChristoph Hellwig 165533bfd7a7SArnd Bergmann ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 1656577f8f10SAkinobu Mita if (ret) 1657577f8f10SAkinobu Mita goto out; 1658577f8f10SAkinobu Mita 1659a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1660a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 1661a33a7d73SArnd Bergmann } else { 1662a33a7d73SArnd Bergmann int status; 1663a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1664a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 1665eebead5bSChristoph Hellwig if (ret) 1666eebead5bSChristoph Hellwig goto out; 1667a33a7d73SArnd Bergmann if (status) 1668a33a7d73SArnd Bergmann ret = status; 1669a33a7d73SArnd Bergmann } 1670a33a7d73SArnd Bergmann 1671a33a7d73SArnd Bergmann if (ret) 1672933b0e35SKazunori Asayama goto out_unlock; 1673a33a7d73SArnd Bergmann 1674a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 16753692dc66SMasato Noguchi ret = size; 1676a33a7d73SArnd Bergmann 1677933b0e35SKazunori Asayama out_unlock: 1678933b0e35SKazunori Asayama spu_release(ctx); 1679a33a7d73SArnd Bergmann out: 1680a33a7d73SArnd Bergmann return ret; 1681a33a7d73SArnd Bergmann } 1682a33a7d73SArnd Bergmann 16838153a5eaSAl Viro static __poll_t spufs_mfc_poll(struct file *file,poll_table *wait) 1684a33a7d73SArnd Bergmann { 1685a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1686a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 16878153a5eaSAl Viro __poll_t mask; 1688a33a7d73SArnd Bergmann 1689933b0e35SKazunori Asayama poll_wait(file, &ctx->mfc_wq, wait); 1690933b0e35SKazunori Asayama 1691c9101bdbSChristoph Hellwig /* 1692c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 1693c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 1694c9101bdbSChristoph Hellwig */ 1695c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 1696a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 1697a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1698a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1699a33a7d73SArnd Bergmann spu_release(ctx); 1700a33a7d73SArnd Bergmann 1701a33a7d73SArnd Bergmann mask = 0; 1702a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1703a9a08845SLinus Torvalds mask |= EPOLLOUT | EPOLLWRNORM; 1704a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1705a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; 1706a33a7d73SArnd Bergmann 1707e48b1b45SHarvey Harrison pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__, 1708a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 1709a33a7d73SArnd Bergmann 1710a33a7d73SArnd Bergmann return mask; 1711a33a7d73SArnd Bergmann } 1712a33a7d73SArnd Bergmann 171373b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id) 1714a33a7d73SArnd Bergmann { 1715a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1716a33a7d73SArnd Bergmann int ret; 1717a33a7d73SArnd Bergmann 1718c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1719c9101bdbSChristoph Hellwig if (ret) 1720eebead5bSChristoph Hellwig goto out; 1721a33a7d73SArnd Bergmann #if 0 1722a33a7d73SArnd Bergmann /* this currently hangs */ 1723a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1724a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 1725a33a7d73SArnd Bergmann if (ret) 1726a33a7d73SArnd Bergmann goto out; 1727a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1728a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 1729eebead5bSChristoph Hellwig if (ret) 1730eebead5bSChristoph Hellwig goto out; 1731a33a7d73SArnd Bergmann #else 1732a33a7d73SArnd Bergmann ret = 0; 1733a33a7d73SArnd Bergmann #endif 1734a33a7d73SArnd Bergmann spu_release(ctx); 1735eebead5bSChristoph Hellwig out: 1736a33a7d73SArnd Bergmann return ret; 1737a33a7d73SArnd Bergmann } 1738a33a7d73SArnd Bergmann 173902c24a82SJosef Bacik static int spufs_mfc_fsync(struct file *file, loff_t start, loff_t end, int datasync) 1740a33a7d73SArnd Bergmann { 1741496ad9aaSAl Viro struct inode *inode = file_inode(file); 17423b49c9a1SJeff Layton int err = file_write_and_wait_range(file, start, end); 174302c24a82SJosef Bacik if (!err) { 17445955102cSAl Viro inode_lock(inode); 174502c24a82SJosef Bacik err = spufs_mfc_flush(file, NULL); 17465955102cSAl Viro inode_unlock(inode); 174702c24a82SJosef Bacik } 174802c24a82SJosef Bacik return err; 1749a33a7d73SArnd Bergmann } 1750a33a7d73SArnd Bergmann 17515dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = { 1752a33a7d73SArnd Bergmann .open = spufs_mfc_open, 175343c2bbd9SChristoph Hellwig .release = spufs_mfc_release, 1754a33a7d73SArnd Bergmann .read = spufs_mfc_read, 1755a33a7d73SArnd Bergmann .write = spufs_mfc_write, 1756a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 1757a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 1758a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 17596df10a82SMark Nutter .mmap = spufs_mfc_mmap, 1760fc15351dSArnd Bergmann .llseek = no_llseek, 1761a33a7d73SArnd Bergmann }; 1762a33a7d73SArnd Bergmann 1763197b1a82SChristoph Hellwig static int spufs_npc_set(void *data, u64 val) 176467207b96SArnd Bergmann { 176567207b96SArnd Bergmann struct spu_context *ctx = data; 1766c9101bdbSChristoph Hellwig int ret; 1767c9101bdbSChristoph Hellwig 1768c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1769c9101bdbSChristoph Hellwig if (ret) 1770c9101bdbSChristoph Hellwig return ret; 17718b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 17728b3d6663SArnd Bergmann spu_release(ctx); 1773197b1a82SChristoph Hellwig 1774197b1a82SChristoph Hellwig return 0; 177567207b96SArnd Bergmann } 177667207b96SArnd Bergmann 1777104f0cc2SMichael Ellerman static u64 spufs_npc_get(struct spu_context *ctx) 177878810ff6SMichael Ellerman { 177978810ff6SMichael Ellerman return ctx->ops->npc_read(ctx); 178078810ff6SMichael Ellerman } 1781104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, 1782104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE); 178367207b96SArnd Bergmann 1784197b1a82SChristoph Hellwig static int spufs_decr_set(void *data, u64 val) 17858b3d6663SArnd Bergmann { 17868b3d6663SArnd Bergmann struct spu_context *ctx = data; 17878b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1788c9101bdbSChristoph Hellwig int ret; 1789c9101bdbSChristoph Hellwig 1790c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1791c9101bdbSChristoph Hellwig if (ret) 1792c9101bdbSChristoph Hellwig return ret; 17938b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 179427b1ea09SChristoph Hellwig spu_release_saved(ctx); 1795197b1a82SChristoph Hellwig 1796197b1a82SChristoph Hellwig return 0; 17978b3d6663SArnd Bergmann } 17988b3d6663SArnd Bergmann 1799104f0cc2SMichael Ellerman static u64 spufs_decr_get(struct spu_context *ctx) 18008b3d6663SArnd Bergmann { 18018b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1802bf1ab978SDwayne Grant McConnell return lscsa->decr.slot[0]; 1803bf1ab978SDwayne Grant McConnell } 1804104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 1805104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED); 18068b3d6663SArnd Bergmann 1807197b1a82SChristoph Hellwig static int spufs_decr_status_set(void *data, u64 val) 18088b3d6663SArnd Bergmann { 18098b3d6663SArnd Bergmann struct spu_context *ctx = data; 1810c9101bdbSChristoph Hellwig int ret; 1811c9101bdbSChristoph Hellwig 1812c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1813c9101bdbSChristoph Hellwig if (ret) 1814c9101bdbSChristoph Hellwig return ret; 1815d40a01d4SMasato Noguchi if (val) 1816d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING; 1817d40a01d4SMasato Noguchi else 1818d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW &= ~MFC_CNTL_DECREMENTER_RUNNING; 181927b1ea09SChristoph Hellwig spu_release_saved(ctx); 1820197b1a82SChristoph Hellwig 1821197b1a82SChristoph Hellwig return 0; 18228b3d6663SArnd Bergmann } 18238b3d6663SArnd Bergmann 1824104f0cc2SMichael Ellerman static u64 spufs_decr_status_get(struct spu_context *ctx) 18258b3d6663SArnd Bergmann { 1826d40a01d4SMasato Noguchi if (ctx->csa.priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) 1827d40a01d4SMasato Noguchi return SPU_DECR_STATUS_RUNNING; 1828d40a01d4SMasato Noguchi else 1829d40a01d4SMasato Noguchi return 0; 1830bf1ab978SDwayne Grant McConnell } 1831104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 1832104f0cc2SMichael Ellerman spufs_decr_status_set, "0x%llx\n", 1833104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 18348b3d6663SArnd Bergmann 1835197b1a82SChristoph Hellwig static int spufs_event_mask_set(void *data, u64 val) 18368b3d6663SArnd Bergmann { 18378b3d6663SArnd Bergmann struct spu_context *ctx = data; 18388b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1839c9101bdbSChristoph Hellwig int ret; 1840c9101bdbSChristoph Hellwig 1841c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1842c9101bdbSChristoph Hellwig if (ret) 1843c9101bdbSChristoph Hellwig return ret; 18448b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 184527b1ea09SChristoph Hellwig spu_release_saved(ctx); 1846197b1a82SChristoph Hellwig 1847197b1a82SChristoph Hellwig return 0; 18488b3d6663SArnd Bergmann } 18498b3d6663SArnd Bergmann 1850104f0cc2SMichael Ellerman static u64 spufs_event_mask_get(struct spu_context *ctx) 18518b3d6663SArnd Bergmann { 18528b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1853bf1ab978SDwayne Grant McConnell return lscsa->event_mask.slot[0]; 1854bf1ab978SDwayne Grant McConnell } 1855bf1ab978SDwayne Grant McConnell 1856104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 1857104f0cc2SMichael Ellerman spufs_event_mask_set, "0x%llx\n", 1858104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 18598b3d6663SArnd Bergmann 1860104f0cc2SMichael Ellerman static u64 spufs_event_status_get(struct spu_context *ctx) 1861b9e3bd77SDwayne Grant McConnell { 1862b9e3bd77SDwayne Grant McConnell struct spu_state *state = &ctx->csa; 1863b9e3bd77SDwayne Grant McConnell u64 stat; 1864b9e3bd77SDwayne Grant McConnell stat = state->spu_chnlcnt_RW[0]; 1865b9e3bd77SDwayne Grant McConnell if (stat) 1866bf1ab978SDwayne Grant McConnell return state->spu_chnldata_RW[0]; 1867bf1ab978SDwayne Grant McConnell return 0; 1868bf1ab978SDwayne Grant McConnell } 1869104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get, 1870104f0cc2SMichael Ellerman NULL, "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 1871b9e3bd77SDwayne Grant McConnell 1872197b1a82SChristoph Hellwig static int spufs_srr0_set(void *data, u64 val) 18738b3d6663SArnd Bergmann { 18748b3d6663SArnd Bergmann struct spu_context *ctx = data; 18758b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1876c9101bdbSChristoph Hellwig int ret; 1877c9101bdbSChristoph Hellwig 1878c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1879c9101bdbSChristoph Hellwig if (ret) 1880c9101bdbSChristoph Hellwig return ret; 18818b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 188227b1ea09SChristoph Hellwig spu_release_saved(ctx); 1883197b1a82SChristoph Hellwig 1884197b1a82SChristoph Hellwig return 0; 18858b3d6663SArnd Bergmann } 18868b3d6663SArnd Bergmann 1887104f0cc2SMichael Ellerman static u64 spufs_srr0_get(struct spu_context *ctx) 18888b3d6663SArnd Bergmann { 18898b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1890104f0cc2SMichael Ellerman return lscsa->srr0.slot[0]; 18918b3d6663SArnd Bergmann } 1892104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 1893104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 18948b3d6663SArnd Bergmann 1895104f0cc2SMichael Ellerman static u64 spufs_id_get(struct spu_context *ctx) 18967b1a7014Sarnd@arndb.de { 18977b1a7014Sarnd@arndb.de u64 num; 18987b1a7014Sarnd@arndb.de 18997b1a7014Sarnd@arndb.de if (ctx->state == SPU_STATE_RUNNABLE) 19007b1a7014Sarnd@arndb.de num = ctx->spu->number; 19017b1a7014Sarnd@arndb.de else 19027b1a7014Sarnd@arndb.de num = (unsigned int)-1; 19037b1a7014Sarnd@arndb.de 19047b1a7014Sarnd@arndb.de return num; 19057b1a7014Sarnd@arndb.de } 1906104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n", 1907104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE) 19087b1a7014Sarnd@arndb.de 1909104f0cc2SMichael Ellerman static u64 spufs_object_id_get(struct spu_context *ctx) 1910bf1ab978SDwayne Grant McConnell { 1911bf1ab978SDwayne Grant McConnell /* FIXME: Should there really be no locking here? */ 1912104f0cc2SMichael Ellerman return ctx->object_id; 1913bf1ab978SDwayne Grant McConnell } 1914bf1ab978SDwayne Grant McConnell 1915197b1a82SChristoph Hellwig static int spufs_object_id_set(void *data, u64 id) 191686767277SArnd Bergmann { 191786767277SArnd Bergmann struct spu_context *ctx = data; 191886767277SArnd Bergmann ctx->object_id = id; 1919197b1a82SChristoph Hellwig 1920197b1a82SChristoph Hellwig return 0; 192186767277SArnd Bergmann } 192286767277SArnd Bergmann 1923104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, 1924104f0cc2SMichael Ellerman spufs_object_id_set, "0x%llx\n", SPU_ATTR_NOACQUIRE); 192586767277SArnd Bergmann 1926104f0cc2SMichael Ellerman static u64 spufs_lslr_get(struct spu_context *ctx) 1927bf1ab978SDwayne Grant McConnell { 1928bf1ab978SDwayne Grant McConnell return ctx->csa.priv2.spu_lslr_RW; 1929bf1ab978SDwayne Grant McConnell } 1930104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n", 1931104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 1932b9e3bd77SDwayne Grant McConnell 1933b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file) 1934b9e3bd77SDwayne Grant McConnell { 1935b9e3bd77SDwayne Grant McConnell struct spufs_inode_info *i = SPUFS_I(inode); 1936b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = i->i_ctx; 1937b9e3bd77SDwayne Grant McConnell file->private_data = ctx; 1938b9e3bd77SDwayne Grant McConnell return 0; 1939b9e3bd77SDwayne Grant McConnell } 1940b9e3bd77SDwayne Grant McConnell 1941cbe709c1SBenjamin Herrenschmidt static int spufs_caps_show(struct seq_file *s, void *private) 1942cbe709c1SBenjamin Herrenschmidt { 1943cbe709c1SBenjamin Herrenschmidt struct spu_context *ctx = s->private; 1944cbe709c1SBenjamin Herrenschmidt 1945cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_NOSCHED)) 1946cbe709c1SBenjamin Herrenschmidt seq_puts(s, "sched\n"); 1947cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_ISOLATE)) 1948cbe709c1SBenjamin Herrenschmidt seq_puts(s, "step\n"); 1949cbe709c1SBenjamin Herrenschmidt return 0; 1950cbe709c1SBenjamin Herrenschmidt } 1951cbe709c1SBenjamin Herrenschmidt 1952cbe709c1SBenjamin Herrenschmidt static int spufs_caps_open(struct inode *inode, struct file *file) 1953cbe709c1SBenjamin Herrenschmidt { 1954cbe709c1SBenjamin Herrenschmidt return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); 1955cbe709c1SBenjamin Herrenschmidt } 1956cbe709c1SBenjamin Herrenschmidt 1957cbe709c1SBenjamin Herrenschmidt static const struct file_operations spufs_caps_fops = { 1958cbe709c1SBenjamin Herrenschmidt .open = spufs_caps_open, 1959cbe709c1SBenjamin Herrenschmidt .read = seq_read, 1960cbe709c1SBenjamin Herrenschmidt .llseek = seq_lseek, 1961cbe709c1SBenjamin Herrenschmidt .release = single_release, 1962cbe709c1SBenjamin Herrenschmidt }; 1963cbe709c1SBenjamin Herrenschmidt 1964bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, 1965bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 1966bf1ab978SDwayne Grant McConnell { 1967bf1ab978SDwayne Grant McConnell u32 data; 1968bf1ab978SDwayne Grant McConnell 1969cbea9238SJeremy Kerr /* EOF if there's no entry in the mbox */ 1970cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0x0000ff)) 1971cbea9238SJeremy Kerr return 0; 1972cbea9238SJeremy Kerr 1973bf1ab978SDwayne Grant McConnell data = ctx->csa.prob.pu_mb_R; 1974bf1ab978SDwayne Grant McConnell 1975bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 1976bf1ab978SDwayne Grant McConnell } 1977bf1ab978SDwayne Grant McConnell 197869a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf, 197969a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 198069a2f00cSDwayne Grant McConnell { 198169a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 198288413a6bSJeremy Kerr u32 stat, data; 198388413a6bSJeremy Kerr int ret; 198469a2f00cSDwayne Grant McConnell 198596d4f267SLinus Torvalds if (!access_ok(buf, len)) 198669a2f00cSDwayne Grant McConnell return -EFAULT; 198769a2f00cSDwayne Grant McConnell 1988c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1989c9101bdbSChristoph Hellwig if (ret) 1990c9101bdbSChristoph Hellwig return ret; 199169a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 199288413a6bSJeremy Kerr stat = ctx->csa.prob.mb_stat_R; 199388413a6bSJeremy Kerr data = ctx->csa.prob.pu_mb_R; 199469a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 199527b1ea09SChristoph Hellwig spu_release_saved(ctx); 199669a2f00cSDwayne Grant McConnell 199788413a6bSJeremy Kerr /* EOF if there's no entry in the mbox */ 199888413a6bSJeremy Kerr if (!(stat & 0x0000ff)) 199988413a6bSJeremy Kerr return 0; 200088413a6bSJeremy Kerr 200188413a6bSJeremy Kerr return simple_read_from_buffer(buf, len, pos, &data, sizeof(data)); 200269a2f00cSDwayne Grant McConnell } 200369a2f00cSDwayne Grant McConnell 20045dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = { 200569a2f00cSDwayne Grant McConnell .open = spufs_info_open, 200669a2f00cSDwayne Grant McConnell .read = spufs_mbox_info_read, 200769a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 200869a2f00cSDwayne Grant McConnell }; 200969a2f00cSDwayne Grant McConnell 2010bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx, 2011bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2012bf1ab978SDwayne Grant McConnell { 2013bf1ab978SDwayne Grant McConnell u32 data; 2014bf1ab978SDwayne Grant McConnell 2015cbea9238SJeremy Kerr /* EOF if there's no entry in the ibox */ 2016cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0xff0000)) 2017cbea9238SJeremy Kerr return 0; 2018cbea9238SJeremy Kerr 2019bf1ab978SDwayne Grant McConnell data = ctx->csa.priv2.puint_mb_R; 2020bf1ab978SDwayne Grant McConnell 2021bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2022bf1ab978SDwayne Grant McConnell } 2023bf1ab978SDwayne Grant McConnell 202469a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf, 202569a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 202669a2f00cSDwayne Grant McConnell { 202769a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 202888413a6bSJeremy Kerr u32 stat, data; 2029bf1ab978SDwayne Grant McConnell int ret; 203069a2f00cSDwayne Grant McConnell 203196d4f267SLinus Torvalds if (!access_ok(buf, len)) 203269a2f00cSDwayne Grant McConnell return -EFAULT; 203369a2f00cSDwayne Grant McConnell 2034c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2035c9101bdbSChristoph Hellwig if (ret) 2036c9101bdbSChristoph Hellwig return ret; 203769a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 203888413a6bSJeremy Kerr stat = ctx->csa.prob.mb_stat_R; 203988413a6bSJeremy Kerr data = ctx->csa.priv2.puint_mb_R; 204069a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 204127b1ea09SChristoph Hellwig spu_release_saved(ctx); 204269a2f00cSDwayne Grant McConnell 204388413a6bSJeremy Kerr /* EOF if there's no entry in the ibox */ 204488413a6bSJeremy Kerr if (!(stat & 0xff0000)) 204588413a6bSJeremy Kerr return 0; 204688413a6bSJeremy Kerr 204788413a6bSJeremy Kerr return simple_read_from_buffer(buf, len, pos, &data, sizeof(data)); 204869a2f00cSDwayne Grant McConnell } 204969a2f00cSDwayne Grant McConnell 20505dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = { 205169a2f00cSDwayne Grant McConnell .open = spufs_info_open, 205269a2f00cSDwayne Grant McConnell .read = spufs_ibox_info_read, 205369a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 205469a2f00cSDwayne Grant McConnell }; 205569a2f00cSDwayne Grant McConnell 205688413a6bSJeremy Kerr static size_t spufs_wbox_info_cnt(struct spu_context *ctx) 205788413a6bSJeremy Kerr { 205888413a6bSJeremy Kerr return (4 - ((ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8)) * sizeof(u32); 205988413a6bSJeremy Kerr } 206088413a6bSJeremy Kerr 2061bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx, 2062bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2063bf1ab978SDwayne Grant McConnell { 2064bf1ab978SDwayne Grant McConnell int i, cnt; 2065bf1ab978SDwayne Grant McConnell u32 data[4]; 2066bf1ab978SDwayne Grant McConnell u32 wbox_stat; 2067bf1ab978SDwayne Grant McConnell 2068bf1ab978SDwayne Grant McConnell wbox_stat = ctx->csa.prob.mb_stat_R; 206988413a6bSJeremy Kerr cnt = spufs_wbox_info_cnt(ctx); 2070bf1ab978SDwayne Grant McConnell for (i = 0; i < cnt; i++) { 2071bf1ab978SDwayne Grant McConnell data[i] = ctx->csa.spu_mailbox_data[i]; 2072bf1ab978SDwayne Grant McConnell } 2073bf1ab978SDwayne Grant McConnell 2074bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, 2075bf1ab978SDwayne Grant McConnell cnt * sizeof(u32)); 2076bf1ab978SDwayne Grant McConnell } 2077bf1ab978SDwayne Grant McConnell 207869a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf, 207969a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 208069a2f00cSDwayne Grant McConnell { 208169a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 208288413a6bSJeremy Kerr u32 data[ARRAY_SIZE(ctx->csa.spu_mailbox_data)]; 208388413a6bSJeremy Kerr int ret, count; 208469a2f00cSDwayne Grant McConnell 208596d4f267SLinus Torvalds if (!access_ok(buf, len)) 208669a2f00cSDwayne Grant McConnell return -EFAULT; 208769a2f00cSDwayne Grant McConnell 2088c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2089c9101bdbSChristoph Hellwig if (ret) 2090c9101bdbSChristoph Hellwig return ret; 209169a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 209288413a6bSJeremy Kerr count = spufs_wbox_info_cnt(ctx); 209388413a6bSJeremy Kerr memcpy(&data, &ctx->csa.spu_mailbox_data, sizeof(data)); 209469a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 209527b1ea09SChristoph Hellwig spu_release_saved(ctx); 209669a2f00cSDwayne Grant McConnell 209788413a6bSJeremy Kerr return simple_read_from_buffer(buf, len, pos, &data, 209888413a6bSJeremy Kerr count * sizeof(u32)); 209969a2f00cSDwayne Grant McConnell } 210069a2f00cSDwayne Grant McConnell 21015dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = { 210269a2f00cSDwayne Grant McConnell .open = spufs_info_open, 210369a2f00cSDwayne Grant McConnell .read = spufs_wbox_info_read, 210469a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 210569a2f00cSDwayne Grant McConnell }; 210669a2f00cSDwayne Grant McConnell 210788413a6bSJeremy Kerr static void spufs_get_dma_info(struct spu_context *ctx, 210888413a6bSJeremy Kerr struct spu_dma_info *info) 2109b9e3bd77SDwayne Grant McConnell { 2110b9e3bd77SDwayne Grant McConnell int i; 2111b9e3bd77SDwayne Grant McConnell 211288413a6bSJeremy Kerr info->dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW; 211388413a6bSJeremy Kerr info->dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0]; 211488413a6bSJeremy Kerr info->dma_info_status = ctx->csa.spu_chnldata_RW[24]; 211588413a6bSJeremy Kerr info->dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25]; 211688413a6bSJeremy Kerr info->dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27]; 2117b9e3bd77SDwayne Grant McConnell for (i = 0; i < 16; i++) { 211888413a6bSJeremy Kerr struct mfc_cq_sr *qp = &info->dma_info_command_data[i]; 211988413a6bSJeremy Kerr struct mfc_cq_sr *spuqp = &ctx->csa.priv2.spuq[i]; 2120b9e3bd77SDwayne Grant McConnell 2121b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW; 2122b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW; 2123b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW; 2124b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW; 2125b9e3bd77SDwayne Grant McConnell } 212688413a6bSJeremy Kerr } 212788413a6bSJeremy Kerr 212888413a6bSJeremy Kerr static ssize_t __spufs_dma_info_read(struct spu_context *ctx, 212988413a6bSJeremy Kerr char __user *buf, size_t len, loff_t *pos) 213088413a6bSJeremy Kerr { 213188413a6bSJeremy Kerr struct spu_dma_info info; 213288413a6bSJeremy Kerr 213388413a6bSJeremy Kerr spufs_get_dma_info(ctx, &info); 2134b9e3bd77SDwayne Grant McConnell 2135b9e3bd77SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2136b9e3bd77SDwayne Grant McConnell sizeof info); 2137b9e3bd77SDwayne Grant McConnell } 2138b9e3bd77SDwayne Grant McConnell 2139bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, 2140bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2141bf1ab978SDwayne Grant McConnell { 2142bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 214388413a6bSJeremy Kerr struct spu_dma_info info; 2144bf1ab978SDwayne Grant McConnell int ret; 2145bf1ab978SDwayne Grant McConnell 214696d4f267SLinus Torvalds if (!access_ok(buf, len)) 2147bf1ab978SDwayne Grant McConnell return -EFAULT; 2148bf1ab978SDwayne Grant McConnell 2149c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2150c9101bdbSChristoph Hellwig if (ret) 2151c9101bdbSChristoph Hellwig return ret; 2152bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 215388413a6bSJeremy Kerr spufs_get_dma_info(ctx, &info); 2154bf1ab978SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 215527b1ea09SChristoph Hellwig spu_release_saved(ctx); 2156bf1ab978SDwayne Grant McConnell 215788413a6bSJeremy Kerr return simple_read_from_buffer(buf, len, pos, &info, 215888413a6bSJeremy Kerr sizeof(info)); 2159bf1ab978SDwayne Grant McConnell } 2160bf1ab978SDwayne Grant McConnell 21615dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = { 2162b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2163b9e3bd77SDwayne Grant McConnell .read = spufs_dma_info_read, 2164fc15351dSArnd Bergmann .llseek = no_llseek, 2165b9e3bd77SDwayne Grant McConnell }; 2166b9e3bd77SDwayne Grant McConnell 216788413a6bSJeremy Kerr static void spufs_get_proxydma_info(struct spu_context *ctx, 216888413a6bSJeremy Kerr struct spu_proxydma_info *info) 216988413a6bSJeremy Kerr { 217088413a6bSJeremy Kerr int i; 217188413a6bSJeremy Kerr 217288413a6bSJeremy Kerr info->proxydma_info_type = ctx->csa.prob.dma_querytype_RW; 217388413a6bSJeremy Kerr info->proxydma_info_mask = ctx->csa.prob.dma_querymask_RW; 217488413a6bSJeremy Kerr info->proxydma_info_status = ctx->csa.prob.dma_tagstatus_R; 217588413a6bSJeremy Kerr 217688413a6bSJeremy Kerr for (i = 0; i < 8; i++) { 217788413a6bSJeremy Kerr struct mfc_cq_sr *qp = &info->proxydma_info_command_data[i]; 217888413a6bSJeremy Kerr struct mfc_cq_sr *puqp = &ctx->csa.priv2.puq[i]; 217988413a6bSJeremy Kerr 218088413a6bSJeremy Kerr qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW; 218188413a6bSJeremy Kerr qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW; 218288413a6bSJeremy Kerr qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW; 218388413a6bSJeremy Kerr qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW; 218488413a6bSJeremy Kerr } 218588413a6bSJeremy Kerr } 218688413a6bSJeremy Kerr 2187bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, 2188bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2189b9e3bd77SDwayne Grant McConnell { 2190b9e3bd77SDwayne Grant McConnell struct spu_proxydma_info info; 2191bf1ab978SDwayne Grant McConnell int ret = sizeof info; 2192b9e3bd77SDwayne Grant McConnell 2193b9e3bd77SDwayne Grant McConnell if (len < ret) 2194b9e3bd77SDwayne Grant McConnell return -EINVAL; 2195b9e3bd77SDwayne Grant McConnell 219696d4f267SLinus Torvalds if (!access_ok(buf, len)) 2197b9e3bd77SDwayne Grant McConnell return -EFAULT; 2198b9e3bd77SDwayne Grant McConnell 219988413a6bSJeremy Kerr spufs_get_proxydma_info(ctx, &info); 2200bf1ab978SDwayne Grant McConnell 2201bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2202bf1ab978SDwayne Grant McConnell sizeof info); 2203bf1ab978SDwayne Grant McConnell } 2204bf1ab978SDwayne Grant McConnell 2205bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, 2206bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2207bf1ab978SDwayne Grant McConnell { 2208bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 220988413a6bSJeremy Kerr struct spu_proxydma_info info; 2210bf1ab978SDwayne Grant McConnell int ret; 2211bf1ab978SDwayne Grant McConnell 2212c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2213c9101bdbSChristoph Hellwig if (ret) 2214c9101bdbSChristoph Hellwig return ret; 2215bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 221688413a6bSJeremy Kerr spufs_get_proxydma_info(ctx, &info); 2217b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 221827b1ea09SChristoph Hellwig spu_release_saved(ctx); 2219b9e3bd77SDwayne Grant McConnell 222088413a6bSJeremy Kerr return simple_read_from_buffer(buf, len, pos, &info, 222188413a6bSJeremy Kerr sizeof(info)); 2222b9e3bd77SDwayne Grant McConnell } 2223b9e3bd77SDwayne Grant McConnell 22245dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = { 2225b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2226b9e3bd77SDwayne Grant McConnell .read = spufs_proxydma_info_read, 2227fc15351dSArnd Bergmann .llseek = no_llseek, 2228b9e3bd77SDwayne Grant McConnell }; 2229b9e3bd77SDwayne Grant McConnell 2230476273adSChristoph Hellwig static int spufs_show_tid(struct seq_file *s, void *private) 2231476273adSChristoph Hellwig { 2232476273adSChristoph Hellwig struct spu_context *ctx = s->private; 2233476273adSChristoph Hellwig 2234476273adSChristoph Hellwig seq_printf(s, "%d\n", ctx->tid); 2235476273adSChristoph Hellwig return 0; 2236476273adSChristoph Hellwig } 2237476273adSChristoph Hellwig 2238476273adSChristoph Hellwig static int spufs_tid_open(struct inode *inode, struct file *file) 2239476273adSChristoph Hellwig { 2240476273adSChristoph Hellwig return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); 2241476273adSChristoph Hellwig } 2242476273adSChristoph Hellwig 2243476273adSChristoph Hellwig static const struct file_operations spufs_tid_fops = { 2244476273adSChristoph Hellwig .open = spufs_tid_open, 2245476273adSChristoph Hellwig .read = seq_read, 2246476273adSChristoph Hellwig .llseek = seq_lseek, 2247476273adSChristoph Hellwig .release = single_release, 2248476273adSChristoph Hellwig }; 2249476273adSChristoph Hellwig 2250e9f8a0b6SChristoph Hellwig static const char *ctx_state_names[] = { 2251e9f8a0b6SChristoph Hellwig "user", "system", "iowait", "loaded" 2252e9f8a0b6SChristoph Hellwig }; 2253e9f8a0b6SChristoph Hellwig 2254e9f8a0b6SChristoph Hellwig static unsigned long long spufs_acct_time(struct spu_context *ctx, 225527ec41d3SAndre Detsch enum spu_utilization_state state) 2256e9f8a0b6SChristoph Hellwig { 225727ec41d3SAndre Detsch unsigned long long time = ctx->stats.times[state]; 2258e9f8a0b6SChristoph Hellwig 225927ec41d3SAndre Detsch /* 226027ec41d3SAndre Detsch * In general, utilization statistics are updated by the controlling 226127ec41d3SAndre Detsch * thread as the spu context moves through various well defined 226227ec41d3SAndre Detsch * state transitions, but if the context is lazily loaded its 226327ec41d3SAndre Detsch * utilization statistics are not updated as the controlling thread 226427ec41d3SAndre Detsch * is not tightly coupled with the execution of the spu context. We 226527ec41d3SAndre Detsch * calculate and apply the time delta from the last recorded state 226627ec41d3SAndre Detsch * of the spu context. 226727ec41d3SAndre Detsch */ 226827ec41d3SAndre Detsch if (ctx->spu && ctx->stats.util_state == state) { 2269f2dec1eaSThomas Gleixner time += ktime_get_ns() - ctx->stats.tstamp; 227027ec41d3SAndre Detsch } 2271e9f8a0b6SChristoph Hellwig 227227ec41d3SAndre Detsch return time / NSEC_PER_MSEC; 2273e9f8a0b6SChristoph Hellwig } 2274e9f8a0b6SChristoph Hellwig 2275e9f8a0b6SChristoph Hellwig static unsigned long long spufs_slb_flts(struct spu_context *ctx) 2276e9f8a0b6SChristoph Hellwig { 2277e9f8a0b6SChristoph Hellwig unsigned long long slb_flts = ctx->stats.slb_flt; 2278e9f8a0b6SChristoph Hellwig 2279e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2280e9f8a0b6SChristoph Hellwig slb_flts += (ctx->spu->stats.slb_flt - 2281e9f8a0b6SChristoph Hellwig ctx->stats.slb_flt_base); 2282e9f8a0b6SChristoph Hellwig } 2283e9f8a0b6SChristoph Hellwig 2284e9f8a0b6SChristoph Hellwig return slb_flts; 2285e9f8a0b6SChristoph Hellwig } 2286e9f8a0b6SChristoph Hellwig 2287e9f8a0b6SChristoph Hellwig static unsigned long long spufs_class2_intrs(struct spu_context *ctx) 2288e9f8a0b6SChristoph Hellwig { 2289e9f8a0b6SChristoph Hellwig unsigned long long class2_intrs = ctx->stats.class2_intr; 2290e9f8a0b6SChristoph Hellwig 2291e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2292e9f8a0b6SChristoph Hellwig class2_intrs += (ctx->spu->stats.class2_intr - 2293e9f8a0b6SChristoph Hellwig ctx->stats.class2_intr_base); 2294e9f8a0b6SChristoph Hellwig } 2295e9f8a0b6SChristoph Hellwig 2296e9f8a0b6SChristoph Hellwig return class2_intrs; 2297e9f8a0b6SChristoph Hellwig } 2298e9f8a0b6SChristoph Hellwig 2299e9f8a0b6SChristoph Hellwig 2300e9f8a0b6SChristoph Hellwig static int spufs_show_stat(struct seq_file *s, void *private) 2301e9f8a0b6SChristoph Hellwig { 2302e9f8a0b6SChristoph Hellwig struct spu_context *ctx = s->private; 2303c9101bdbSChristoph Hellwig int ret; 2304e9f8a0b6SChristoph Hellwig 2305c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 2306c9101bdbSChristoph Hellwig if (ret) 2307c9101bdbSChristoph Hellwig return ret; 2308c9101bdbSChristoph Hellwig 2309e9f8a0b6SChristoph Hellwig seq_printf(s, "%s %llu %llu %llu %llu " 2310e9f8a0b6SChristoph Hellwig "%llu %llu %llu %llu %llu %llu %llu %llu\n", 231127ec41d3SAndre Detsch ctx_state_names[ctx->stats.util_state], 231227ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_USER), 231327ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_SYSTEM), 231427ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IOWAIT), 231527ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED), 2316e9f8a0b6SChristoph Hellwig ctx->stats.vol_ctx_switch, 2317e9f8a0b6SChristoph Hellwig ctx->stats.invol_ctx_switch, 2318e9f8a0b6SChristoph Hellwig spufs_slb_flts(ctx), 2319e9f8a0b6SChristoph Hellwig ctx->stats.hash_flt, 2320e9f8a0b6SChristoph Hellwig ctx->stats.min_flt, 2321e9f8a0b6SChristoph Hellwig ctx->stats.maj_flt, 2322e9f8a0b6SChristoph Hellwig spufs_class2_intrs(ctx), 2323e9f8a0b6SChristoph Hellwig ctx->stats.libassist); 2324e9f8a0b6SChristoph Hellwig spu_release(ctx); 2325e9f8a0b6SChristoph Hellwig return 0; 2326e9f8a0b6SChristoph Hellwig } 2327e9f8a0b6SChristoph Hellwig 2328e9f8a0b6SChristoph Hellwig static int spufs_stat_open(struct inode *inode, struct file *file) 2329e9f8a0b6SChristoph Hellwig { 2330e9f8a0b6SChristoph Hellwig return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); 2331e9f8a0b6SChristoph Hellwig } 2332e9f8a0b6SChristoph Hellwig 2333e9f8a0b6SChristoph Hellwig static const struct file_operations spufs_stat_fops = { 2334e9f8a0b6SChristoph Hellwig .open = spufs_stat_open, 2335e9f8a0b6SChristoph Hellwig .read = seq_read, 2336e9f8a0b6SChristoph Hellwig .llseek = seq_lseek, 2337e9f8a0b6SChristoph Hellwig .release = single_release, 2338e9f8a0b6SChristoph Hellwig }; 2339e9f8a0b6SChristoph Hellwig 23405158e9b5SChristoph Hellwig static inline int spufs_switch_log_used(struct spu_context *ctx) 23415158e9b5SChristoph Hellwig { 23425158e9b5SChristoph Hellwig return (ctx->switch_log->head - ctx->switch_log->tail) % 23435158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 23445158e9b5SChristoph Hellwig } 23455158e9b5SChristoph Hellwig 23465158e9b5SChristoph Hellwig static inline int spufs_switch_log_avail(struct spu_context *ctx) 23475158e9b5SChristoph Hellwig { 23485158e9b5SChristoph Hellwig return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); 23495158e9b5SChristoph Hellwig } 23505158e9b5SChristoph Hellwig 23515158e9b5SChristoph Hellwig static int spufs_switch_log_open(struct inode *inode, struct file *file) 23525158e9b5SChristoph Hellwig { 23535158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2354f5ed0eb6SJeremy Kerr int rc; 23555158e9b5SChristoph Hellwig 2356f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2357f5ed0eb6SJeremy Kerr if (rc) 2358f5ed0eb6SJeremy Kerr return rc; 2359f5ed0eb6SJeremy Kerr 23605158e9b5SChristoph Hellwig if (ctx->switch_log) { 2361f5ed0eb6SJeremy Kerr rc = -EBUSY; 2362f5ed0eb6SJeremy Kerr goto out; 2363f5ed0eb6SJeremy Kerr } 2364f5ed0eb6SJeremy Kerr 236500def713SGustavo A. R. Silva ctx->switch_log = kmalloc(struct_size(ctx->switch_log, log, 236600def713SGustavo A. R. Silva SWITCH_LOG_BUFSIZE), GFP_KERNEL); 2367f5ed0eb6SJeremy Kerr 2368f5ed0eb6SJeremy Kerr if (!ctx->switch_log) { 2369f5ed0eb6SJeremy Kerr rc = -ENOMEM; 23705158e9b5SChristoph Hellwig goto out; 23715158e9b5SChristoph Hellwig } 2372f5ed0eb6SJeremy Kerr 2373837ef884SJeremy Kerr ctx->switch_log->head = ctx->switch_log->tail = 0; 2374f5ed0eb6SJeremy Kerr init_waitqueue_head(&ctx->switch_log->wait); 2375f5ed0eb6SJeremy Kerr rc = 0; 2376f5ed0eb6SJeremy Kerr 2377f5ed0eb6SJeremy Kerr out: 2378f5ed0eb6SJeremy Kerr spu_release(ctx); 2379f5ed0eb6SJeremy Kerr return rc; 2380f5ed0eb6SJeremy Kerr } 2381f5ed0eb6SJeremy Kerr 2382f5ed0eb6SJeremy Kerr static int spufs_switch_log_release(struct inode *inode, struct file *file) 2383f5ed0eb6SJeremy Kerr { 2384f5ed0eb6SJeremy Kerr struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2385f5ed0eb6SJeremy Kerr int rc; 2386f5ed0eb6SJeremy Kerr 2387f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2388f5ed0eb6SJeremy Kerr if (rc) 2389f5ed0eb6SJeremy Kerr return rc; 2390f5ed0eb6SJeremy Kerr 2391f5ed0eb6SJeremy Kerr kfree(ctx->switch_log); 2392f5ed0eb6SJeremy Kerr ctx->switch_log = NULL; 2393f5ed0eb6SJeremy Kerr spu_release(ctx); 23945158e9b5SChristoph Hellwig 23955158e9b5SChristoph Hellwig return 0; 23965158e9b5SChristoph Hellwig } 23975158e9b5SChristoph Hellwig 23985158e9b5SChristoph Hellwig static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) 23995158e9b5SChristoph Hellwig { 24005158e9b5SChristoph Hellwig struct switch_log_entry *p; 24015158e9b5SChristoph Hellwig 24025158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE; 24035158e9b5SChristoph Hellwig 2404cef37ac1SArnd Bergmann return snprintf(tbuf, n, "%llu.%09u %d %u %u %llu\n", 2405cef37ac1SArnd Bergmann (unsigned long long) p->tstamp.tv_sec, 24065158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_nsec, 24075158e9b5SChristoph Hellwig p->spu_id, 24085158e9b5SChristoph Hellwig (unsigned int) p->type, 24095158e9b5SChristoph Hellwig (unsigned int) p->val, 24105158e9b5SChristoph Hellwig (unsigned long long) p->timebase); 24115158e9b5SChristoph Hellwig } 24125158e9b5SChristoph Hellwig 24135158e9b5SChristoph Hellwig static ssize_t spufs_switch_log_read(struct file *file, char __user *buf, 24145158e9b5SChristoph Hellwig size_t len, loff_t *ppos) 24155158e9b5SChristoph Hellwig { 2416496ad9aaSAl Viro struct inode *inode = file_inode(file); 24175158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 24185158e9b5SChristoph Hellwig int error = 0, cnt = 0; 24195158e9b5SChristoph Hellwig 242017e37675Sroel kluin if (!buf) 24215158e9b5SChristoph Hellwig return -EINVAL; 24225158e9b5SChristoph Hellwig 2423f5ed0eb6SJeremy Kerr error = spu_acquire(ctx); 2424f5ed0eb6SJeremy Kerr if (error) 2425f5ed0eb6SJeremy Kerr return error; 2426f5ed0eb6SJeremy Kerr 24275158e9b5SChristoph Hellwig while (cnt < len) { 24285158e9b5SChristoph Hellwig char tbuf[128]; 24295158e9b5SChristoph Hellwig int width; 24305158e9b5SChristoph Hellwig 2431f5ed0eb6SJeremy Kerr if (spufs_switch_log_used(ctx) == 0) { 243214f693eeSJeremy Kerr if (cnt > 0) { 243314f693eeSJeremy Kerr /* If there's data ready to go, we can 243414f693eeSJeremy Kerr * just return straight away */ 243514f693eeSJeremy Kerr break; 243614f693eeSJeremy Kerr 243714f693eeSJeremy Kerr } else if (file->f_flags & O_NONBLOCK) { 2438f5ed0eb6SJeremy Kerr error = -EAGAIN; 24395158e9b5SChristoph Hellwig break; 244014f693eeSJeremy Kerr 2441f5ed0eb6SJeremy Kerr } else { 244214f693eeSJeremy Kerr /* spufs_wait will drop the mutex and 244314f693eeSJeremy Kerr * re-acquire, but since we're in read(), the 244414f693eeSJeremy Kerr * file cannot be _released (and so 244514f693eeSJeremy Kerr * ctx->switch_log is stable). 2446f5ed0eb6SJeremy Kerr */ 2447f5ed0eb6SJeremy Kerr error = spufs_wait(ctx->switch_log->wait, 2448f5ed0eb6SJeremy Kerr spufs_switch_log_used(ctx) > 0); 24495158e9b5SChristoph Hellwig 2450f5ed0eb6SJeremy Kerr /* On error, spufs_wait returns without the 2451f5ed0eb6SJeremy Kerr * state mutex held */ 2452f5ed0eb6SJeremy Kerr if (error) 2453f5ed0eb6SJeremy Kerr return error; 24545158e9b5SChristoph Hellwig 245514f693eeSJeremy Kerr /* We may have had entries read from underneath 245614f693eeSJeremy Kerr * us while we dropped the mutex in spufs_wait, 245714f693eeSJeremy Kerr * so re-check */ 245814f693eeSJeremy Kerr if (spufs_switch_log_used(ctx) == 0) 2459f5ed0eb6SJeremy Kerr continue; 246014f693eeSJeremy Kerr } 246114f693eeSJeremy Kerr } 2462f5ed0eb6SJeremy Kerr 24635158e9b5SChristoph Hellwig width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); 2464f5ed0eb6SJeremy Kerr if (width < len) 24655158e9b5SChristoph Hellwig ctx->switch_log->tail = 24665158e9b5SChristoph Hellwig (ctx->switch_log->tail + 1) % 24675158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 2468f5ed0eb6SJeremy Kerr else 2469f5ed0eb6SJeremy Kerr /* If the record is greater than space available return 2470f5ed0eb6SJeremy Kerr * partial buffer (so far) */ 24715158e9b5SChristoph Hellwig break; 24725158e9b5SChristoph Hellwig 24735158e9b5SChristoph Hellwig error = copy_to_user(buf + cnt, tbuf, width); 24745158e9b5SChristoph Hellwig if (error) 24755158e9b5SChristoph Hellwig break; 24765158e9b5SChristoph Hellwig cnt += width; 24775158e9b5SChristoph Hellwig } 24785158e9b5SChristoph Hellwig 2479f5ed0eb6SJeremy Kerr spu_release(ctx); 2480f5ed0eb6SJeremy Kerr 24815158e9b5SChristoph Hellwig return cnt == 0 ? error : cnt; 24825158e9b5SChristoph Hellwig } 24835158e9b5SChristoph Hellwig 24848153a5eaSAl Viro static __poll_t spufs_switch_log_poll(struct file *file, poll_table *wait) 24855158e9b5SChristoph Hellwig { 2486496ad9aaSAl Viro struct inode *inode = file_inode(file); 24875158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 24888153a5eaSAl Viro __poll_t mask = 0; 2489f5ed0eb6SJeremy Kerr int rc; 24905158e9b5SChristoph Hellwig 24915158e9b5SChristoph Hellwig poll_wait(file, &ctx->switch_log->wait, wait); 24925158e9b5SChristoph Hellwig 2493f5ed0eb6SJeremy Kerr rc = spu_acquire(ctx); 2494f5ed0eb6SJeremy Kerr if (rc) 2495f5ed0eb6SJeremy Kerr return rc; 2496f5ed0eb6SJeremy Kerr 24975158e9b5SChristoph Hellwig if (spufs_switch_log_used(ctx) > 0) 2498a9a08845SLinus Torvalds mask |= EPOLLIN; 24995158e9b5SChristoph Hellwig 2500f5ed0eb6SJeremy Kerr spu_release(ctx); 2501f5ed0eb6SJeremy Kerr 25025158e9b5SChristoph Hellwig return mask; 25035158e9b5SChristoph Hellwig } 25045158e9b5SChristoph Hellwig 25055158e9b5SChristoph Hellwig static const struct file_operations spufs_switch_log_fops = { 25065158e9b5SChristoph Hellwig .open = spufs_switch_log_open, 25075158e9b5SChristoph Hellwig .read = spufs_switch_log_read, 25085158e9b5SChristoph Hellwig .poll = spufs_switch_log_poll, 2509f5ed0eb6SJeremy Kerr .release = spufs_switch_log_release, 2510fc15351dSArnd Bergmann .llseek = no_llseek, 25115158e9b5SChristoph Hellwig }; 25125158e9b5SChristoph Hellwig 2513f5ed0eb6SJeremy Kerr /** 2514f5ed0eb6SJeremy Kerr * Log a context switch event to a switch log reader. 2515f5ed0eb6SJeremy Kerr * 2516f5ed0eb6SJeremy Kerr * Must be called with ctx->state_mutex held. 2517f5ed0eb6SJeremy Kerr */ 25185158e9b5SChristoph Hellwig void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, 25195158e9b5SChristoph Hellwig u32 type, u32 val) 25205158e9b5SChristoph Hellwig { 25215158e9b5SChristoph Hellwig if (!ctx->switch_log) 25225158e9b5SChristoph Hellwig return; 25235158e9b5SChristoph Hellwig 25245158e9b5SChristoph Hellwig if (spufs_switch_log_avail(ctx) > 1) { 25255158e9b5SChristoph Hellwig struct switch_log_entry *p; 25265158e9b5SChristoph Hellwig 25275158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->head; 2528cef37ac1SArnd Bergmann ktime_get_ts64(&p->tstamp); 25295158e9b5SChristoph Hellwig p->timebase = get_tb(); 25305158e9b5SChristoph Hellwig p->spu_id = spu ? spu->number : -1; 25315158e9b5SChristoph Hellwig p->type = type; 25325158e9b5SChristoph Hellwig p->val = val; 25335158e9b5SChristoph Hellwig 25345158e9b5SChristoph Hellwig ctx->switch_log->head = 25355158e9b5SChristoph Hellwig (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; 25365158e9b5SChristoph Hellwig } 25375158e9b5SChristoph Hellwig 25385158e9b5SChristoph Hellwig wake_up(&ctx->switch_log->wait); 25395158e9b5SChristoph Hellwig } 2540e9f8a0b6SChristoph Hellwig 254146deed69SLuke Browning static int spufs_show_ctx(struct seq_file *s, void *private) 254246deed69SLuke Browning { 254346deed69SLuke Browning struct spu_context *ctx = s->private; 254446deed69SLuke Browning u64 mfc_control_RW; 254546deed69SLuke Browning 254646deed69SLuke Browning mutex_lock(&ctx->state_mutex); 254746deed69SLuke Browning if (ctx->spu) { 254846deed69SLuke Browning struct spu *spu = ctx->spu; 254946deed69SLuke Browning struct spu_priv2 __iomem *priv2 = spu->priv2; 255046deed69SLuke Browning 255146deed69SLuke Browning spin_lock_irq(&spu->register_lock); 255246deed69SLuke Browning mfc_control_RW = in_be64(&priv2->mfc_control_RW); 255346deed69SLuke Browning spin_unlock_irq(&spu->register_lock); 255446deed69SLuke Browning } else { 255546deed69SLuke Browning struct spu_state *csa = &ctx->csa; 255646deed69SLuke Browning 255746deed69SLuke Browning mfc_control_RW = csa->priv2.mfc_control_RW; 255846deed69SLuke Browning } 255946deed69SLuke Browning 256046deed69SLuke Browning seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)" 25619477e455SStephen Rothwell " %c %llx %llx %llx %llx %x %x\n", 256246deed69SLuke Browning ctx->state == SPU_STATE_SAVED ? 'S' : 'R', 256346deed69SLuke Browning ctx->flags, 256446deed69SLuke Browning ctx->sched_flags, 256546deed69SLuke Browning ctx->prio, 256646deed69SLuke Browning ctx->time_slice, 256746deed69SLuke Browning ctx->spu ? ctx->spu->number : -1, 256846deed69SLuke Browning !list_empty(&ctx->rq) ? 'q' : ' ', 256946deed69SLuke Browning ctx->csa.class_0_pending, 257046deed69SLuke Browning ctx->csa.class_0_dar, 257146deed69SLuke Browning ctx->csa.class_1_dsisr, 257246deed69SLuke Browning mfc_control_RW, 257346deed69SLuke Browning ctx->ops->runcntl_read(ctx), 257446deed69SLuke Browning ctx->ops->status_read(ctx)); 257546deed69SLuke Browning 257646deed69SLuke Browning mutex_unlock(&ctx->state_mutex); 257746deed69SLuke Browning 257846deed69SLuke Browning return 0; 257946deed69SLuke Browning } 258046deed69SLuke Browning 258146deed69SLuke Browning static int spufs_ctx_open(struct inode *inode, struct file *file) 258246deed69SLuke Browning { 258346deed69SLuke Browning return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx); 258446deed69SLuke Browning } 258546deed69SLuke Browning 258646deed69SLuke Browning static const struct file_operations spufs_ctx_fops = { 258746deed69SLuke Browning .open = spufs_ctx_open, 258846deed69SLuke Browning .read = seq_read, 258946deed69SLuke Browning .llseek = seq_lseek, 259046deed69SLuke Browning .release = single_release, 259146deed69SLuke Browning }; 259246deed69SLuke Browning 259374254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_contents[] = { 2594cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 25956f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 25966f7dde81SJeremy Kerr { "regs", &spufs_regs_fops, 0666, sizeof(struct spu_reg128[128]), }, 259767207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 259867207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 259967207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 26006f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 26016f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 26026f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2603603c4612SJeremy Kerr { "signal1", &spufs_signal1_fops, 0666, }, 2604603c4612SJeremy Kerr { "signal2", &spufs_signal2_fops, 0666, }, 260567207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 260667207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 26076df10a82SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26086f7dde81SJeremy Kerr { "fpcr", &spufs_fpcr_fops, 0666, sizeof(struct spu_reg128), }, 2609b9e3bd77SDwayne Grant McConnell { "lslr", &spufs_lslr_ops, 0444, }, 2610b9e3bd77SDwayne Grant McConnell { "mfc", &spufs_mfc_fops, 0666, }, 2611b9e3bd77SDwayne Grant McConnell { "mss", &spufs_mss_fops, 0666, }, 2612b9e3bd77SDwayne Grant McConnell { "npc", &spufs_npc_ops, 0666, }, 2613b9e3bd77SDwayne Grant McConnell { "srr0", &spufs_srr0_ops, 0666, }, 26148b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 26158b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 26168b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 2617b9e3bd77SDwayne Grant McConnell { "event_status", &spufs_event_status_ops, 0444, }, 26186f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 261986767277SArnd Bergmann { "phys-id", &spufs_id_ops, 0666, }, 262086767277SArnd Bergmann { "object-id", &spufs_object_id_ops, 0666, }, 26216f7dde81SJeremy Kerr { "mbox_info", &spufs_mbox_info_fops, 0444, sizeof(u32), }, 26226f7dde81SJeremy Kerr { "ibox_info", &spufs_ibox_info_fops, 0444, sizeof(u32), }, 26236f7dde81SJeremy Kerr { "wbox_info", &spufs_wbox_info_fops, 0444, sizeof(u32), }, 26246f7dde81SJeremy Kerr { "dma_info", &spufs_dma_info_fops, 0444, 26256f7dde81SJeremy Kerr sizeof(struct spu_dma_info), }, 26266f7dde81SJeremy Kerr { "proxydma_info", &spufs_proxydma_info_fops, 0444, 26276f7dde81SJeremy Kerr sizeof(struct spu_proxydma_info)}, 2628476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2629e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 26305158e9b5SChristoph Hellwig { "switch_log", &spufs_switch_log_fops, 0444 }, 263167207b96SArnd Bergmann {}, 263267207b96SArnd Bergmann }; 26335737edd1SMark Nutter 263474254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_nosched_contents[] = { 2635cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 26366f7dde81SJeremy Kerr { "mem", &spufs_mem_fops, 0666, LS_SIZE, }, 26375737edd1SMark Nutter { "mbox", &spufs_mbox_fops, 0444, }, 26385737edd1SMark Nutter { "ibox", &spufs_ibox_fops, 0444, }, 26395737edd1SMark Nutter { "wbox", &spufs_wbox_fops, 0222, }, 26406f7dde81SJeremy Kerr { "mbox_stat", &spufs_mbox_stat_fops, 0444, sizeof(u32), }, 26416f7dde81SJeremy Kerr { "ibox_stat", &spufs_ibox_stat_fops, 0444, sizeof(u32), }, 26426f7dde81SJeremy Kerr { "wbox_stat", &spufs_wbox_stat_fops, 0444, sizeof(u32), }, 2643d054b36fSJeremy Kerr { "signal1", &spufs_signal1_nosched_fops, 0222, }, 2644d054b36fSJeremy Kerr { "signal2", &spufs_signal2_nosched_fops, 0222, }, 26455737edd1SMark Nutter { "signal1_type", &spufs_signal1_type, 0666, }, 26465737edd1SMark Nutter { "signal2_type", &spufs_signal2_type, 0666, }, 26475737edd1SMark Nutter { "mss", &spufs_mss_fops, 0666, }, 26485737edd1SMark Nutter { "mfc", &spufs_mfc_fops, 0666, }, 26495737edd1SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26505737edd1SMark Nutter { "npc", &spufs_npc_ops, 0666, }, 26516f7dde81SJeremy Kerr { "psmap", &spufs_psmap_fops, 0666, SPUFS_PS_MAP_SIZE, }, 26525737edd1SMark Nutter { "phys-id", &spufs_id_ops, 0666, }, 26535737edd1SMark Nutter { "object-id", &spufs_object_id_ops, 0666, }, 2654476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2655e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 26562c3e4787SJeremy Kerr {}, 26572c3e4787SJeremy Kerr }; 26582c3e4787SJeremy Kerr 265974254647SJeremy Kerr const struct spufs_tree_descr spufs_dir_debug_contents[] = { 266046deed69SLuke Browning { ".ctx", &spufs_ctx_fops, 0444, }, 26615737edd1SMark Nutter {}, 26625737edd1SMark Nutter }; 2663bf1ab978SDwayne Grant McConnell 266474254647SJeremy Kerr const struct spufs_coredump_reader spufs_coredump_read[] = { 26654fca9c42SMichael Ellerman { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])}, 26664fca9c42SMichael Ellerman { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) }, 2667104f0cc2SMichael Ellerman { "lslr", NULL, spufs_lslr_get, 19 }, 2668104f0cc2SMichael Ellerman { "decr", NULL, spufs_decr_get, 19 }, 2669104f0cc2SMichael Ellerman { "decr_status", NULL, spufs_decr_status_get, 19 }, 26704fca9c42SMichael Ellerman { "mem", __spufs_mem_read, NULL, LS_SIZE, }, 26714fca9c42SMichael Ellerman { "signal1", __spufs_signal1_read, NULL, sizeof(u32) }, 2672104f0cc2SMichael Ellerman { "signal1_type", NULL, spufs_signal1_type_get, 19 }, 26734fca9c42SMichael Ellerman { "signal2", __spufs_signal2_read, NULL, sizeof(u32) }, 2674104f0cc2SMichael Ellerman { "signal2_type", NULL, spufs_signal2_type_get, 19 }, 2675104f0cc2SMichael Ellerman { "event_mask", NULL, spufs_event_mask_get, 19 }, 2676104f0cc2SMichael Ellerman { "event_status", NULL, spufs_event_status_get, 19 }, 26774fca9c42SMichael Ellerman { "mbox_info", __spufs_mbox_info_read, NULL, sizeof(u32) }, 26784fca9c42SMichael Ellerman { "ibox_info", __spufs_ibox_info_read, NULL, sizeof(u32) }, 26794fca9c42SMichael Ellerman { "wbox_info", __spufs_wbox_info_read, NULL, 4 * sizeof(u32)}, 26804fca9c42SMichael Ellerman { "dma_info", __spufs_dma_info_read, NULL, sizeof(struct spu_dma_info)}, 26814fca9c42SMichael Ellerman { "proxydma_info", __spufs_proxydma_info_read, 26824fca9c42SMichael Ellerman NULL, sizeof(struct spu_proxydma_info)}, 2683104f0cc2SMichael Ellerman { "object-id", NULL, spufs_object_id_get, 19 }, 2684104f0cc2SMichael Ellerman { "npc", NULL, spufs_npc_get, 19 }, 2685936d5bf1SMichael Ellerman { NULL }, 2686bf1ab978SDwayne Grant McConnell }; 2687