167207b96SArnd Bergmann /* 267207b96SArnd Bergmann * SPU file system -- file contents 367207b96SArnd Bergmann * 467207b96SArnd Bergmann * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 567207b96SArnd Bergmann * 667207b96SArnd Bergmann * Author: Arnd Bergmann <arndb@de.ibm.com> 767207b96SArnd Bergmann * 867207b96SArnd Bergmann * This program is free software; you can redistribute it and/or modify 967207b96SArnd Bergmann * it under the terms of the GNU General Public License as published by 1067207b96SArnd Bergmann * the Free Software Foundation; either version 2, or (at your option) 1167207b96SArnd Bergmann * any later version. 1267207b96SArnd Bergmann * 1367207b96SArnd Bergmann * This program is distributed in the hope that it will be useful, 1467207b96SArnd Bergmann * but WITHOUT ANY WARRANTY; without even the implied warranty of 1567207b96SArnd Bergmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1667207b96SArnd Bergmann * GNU General Public License for more details. 1767207b96SArnd Bergmann * 1867207b96SArnd Bergmann * You should have received a copy of the GNU General Public License 1967207b96SArnd Bergmann * along with this program; if not, write to the Free Software 2067207b96SArnd Bergmann * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 2167207b96SArnd Bergmann */ 2267207b96SArnd Bergmann 23a33a7d73SArnd Bergmann #undef DEBUG 24a33a7d73SArnd Bergmann 2567207b96SArnd Bergmann #include <linux/fs.h> 2667207b96SArnd Bergmann #include <linux/ioctl.h> 2767207b96SArnd Bergmann #include <linux/module.h> 28d88cfffaSArnd Bergmann #include <linux/pagemap.h> 2967207b96SArnd Bergmann #include <linux/poll.h> 305110459fSArnd Bergmann #include <linux/ptrace.h> 31cbe709c1SBenjamin Herrenschmidt #include <linux/seq_file.h> 32038200cfSChristoph Hellwig #include <linux/marker.h> 3367207b96SArnd Bergmann 3467207b96SArnd Bergmann #include <asm/io.h> 35dfe1e09fSFUJITA Tomonori #include <asm/time.h> 3667207b96SArnd Bergmann #include <asm/spu.h> 37b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h> 3867207b96SArnd Bergmann #include <asm/uaccess.h> 3967207b96SArnd Bergmann 4067207b96SArnd Bergmann #include "spufs.h" 4167207b96SArnd Bergmann 4227d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) 4327d5bf2aSBenjamin Herrenschmidt 44197b1a82SChristoph Hellwig /* Simple attribute files */ 45197b1a82SChristoph Hellwig struct spufs_attr { 46197b1a82SChristoph Hellwig int (*get)(void *, u64 *); 47197b1a82SChristoph Hellwig int (*set)(void *, u64); 48197b1a82SChristoph Hellwig char get_buf[24]; /* enough to store a u64 and "\n\0" */ 49197b1a82SChristoph Hellwig char set_buf[24]; 50197b1a82SChristoph Hellwig void *data; 51197b1a82SChristoph Hellwig const char *fmt; /* format for read operation */ 52197b1a82SChristoph Hellwig struct mutex mutex; /* protects access to these buffers */ 53197b1a82SChristoph Hellwig }; 54197b1a82SChristoph Hellwig 55197b1a82SChristoph Hellwig static int spufs_attr_open(struct inode *inode, struct file *file, 56197b1a82SChristoph Hellwig int (*get)(void *, u64 *), int (*set)(void *, u64), 57197b1a82SChristoph Hellwig const char *fmt) 58197b1a82SChristoph Hellwig { 59197b1a82SChristoph Hellwig struct spufs_attr *attr; 60197b1a82SChristoph Hellwig 61197b1a82SChristoph Hellwig attr = kmalloc(sizeof(*attr), GFP_KERNEL); 62197b1a82SChristoph Hellwig if (!attr) 63197b1a82SChristoph Hellwig return -ENOMEM; 64197b1a82SChristoph Hellwig 65197b1a82SChristoph Hellwig attr->get = get; 66197b1a82SChristoph Hellwig attr->set = set; 67197b1a82SChristoph Hellwig attr->data = inode->i_private; 68197b1a82SChristoph Hellwig attr->fmt = fmt; 69197b1a82SChristoph Hellwig mutex_init(&attr->mutex); 70197b1a82SChristoph Hellwig file->private_data = attr; 71197b1a82SChristoph Hellwig 72197b1a82SChristoph Hellwig return nonseekable_open(inode, file); 73197b1a82SChristoph Hellwig } 74197b1a82SChristoph Hellwig 75197b1a82SChristoph Hellwig static int spufs_attr_release(struct inode *inode, struct file *file) 76197b1a82SChristoph Hellwig { 77197b1a82SChristoph Hellwig kfree(file->private_data); 78197b1a82SChristoph Hellwig return 0; 79197b1a82SChristoph Hellwig } 80197b1a82SChristoph Hellwig 81197b1a82SChristoph Hellwig static ssize_t spufs_attr_read(struct file *file, char __user *buf, 82197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 83197b1a82SChristoph Hellwig { 84197b1a82SChristoph Hellwig struct spufs_attr *attr; 85197b1a82SChristoph Hellwig size_t size; 86197b1a82SChristoph Hellwig ssize_t ret; 87197b1a82SChristoph Hellwig 88197b1a82SChristoph Hellwig attr = file->private_data; 89197b1a82SChristoph Hellwig if (!attr->get) 90197b1a82SChristoph Hellwig return -EACCES; 91197b1a82SChristoph Hellwig 92197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 93197b1a82SChristoph Hellwig if (ret) 94197b1a82SChristoph Hellwig return ret; 95197b1a82SChristoph Hellwig 96197b1a82SChristoph Hellwig if (*ppos) { /* continued read */ 97197b1a82SChristoph Hellwig size = strlen(attr->get_buf); 98197b1a82SChristoph Hellwig } else { /* first read */ 99197b1a82SChristoph Hellwig u64 val; 100197b1a82SChristoph Hellwig ret = attr->get(attr->data, &val); 101197b1a82SChristoph Hellwig if (ret) 102197b1a82SChristoph Hellwig goto out; 103197b1a82SChristoph Hellwig 104197b1a82SChristoph Hellwig size = scnprintf(attr->get_buf, sizeof(attr->get_buf), 105197b1a82SChristoph Hellwig attr->fmt, (unsigned long long)val); 106197b1a82SChristoph Hellwig } 107197b1a82SChristoph Hellwig 108197b1a82SChristoph Hellwig ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size); 109197b1a82SChristoph Hellwig out: 110197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 111197b1a82SChristoph Hellwig return ret; 112197b1a82SChristoph Hellwig } 113197b1a82SChristoph Hellwig 114197b1a82SChristoph Hellwig static ssize_t spufs_attr_write(struct file *file, const char __user *buf, 115197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 116197b1a82SChristoph Hellwig { 117197b1a82SChristoph Hellwig struct spufs_attr *attr; 118197b1a82SChristoph Hellwig u64 val; 119197b1a82SChristoph Hellwig size_t size; 120197b1a82SChristoph Hellwig ssize_t ret; 121197b1a82SChristoph Hellwig 122197b1a82SChristoph Hellwig attr = file->private_data; 123197b1a82SChristoph Hellwig if (!attr->set) 124197b1a82SChristoph Hellwig return -EACCES; 125197b1a82SChristoph Hellwig 126197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 127197b1a82SChristoph Hellwig if (ret) 128197b1a82SChristoph Hellwig return ret; 129197b1a82SChristoph Hellwig 130197b1a82SChristoph Hellwig ret = -EFAULT; 131197b1a82SChristoph Hellwig size = min(sizeof(attr->set_buf) - 1, len); 132197b1a82SChristoph Hellwig if (copy_from_user(attr->set_buf, buf, size)) 133197b1a82SChristoph Hellwig goto out; 134197b1a82SChristoph Hellwig 135197b1a82SChristoph Hellwig ret = len; /* claim we got the whole input */ 136197b1a82SChristoph Hellwig attr->set_buf[size] = '\0'; 137197b1a82SChristoph Hellwig val = simple_strtol(attr->set_buf, NULL, 0); 138197b1a82SChristoph Hellwig attr->set(attr->data, val); 139197b1a82SChristoph Hellwig out: 140197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 141197b1a82SChristoph Hellwig return ret; 142197b1a82SChristoph Hellwig } 143197b1a82SChristoph Hellwig 144197b1a82SChristoph Hellwig #define DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \ 145197b1a82SChristoph Hellwig static int __fops ## _open(struct inode *inode, struct file *file) \ 146197b1a82SChristoph Hellwig { \ 147197b1a82SChristoph Hellwig __simple_attr_check_format(__fmt, 0ull); \ 148197b1a82SChristoph Hellwig return spufs_attr_open(inode, file, __get, __set, __fmt); \ 149197b1a82SChristoph Hellwig } \ 150197b1a82SChristoph Hellwig static struct file_operations __fops = { \ 151197b1a82SChristoph Hellwig .owner = THIS_MODULE, \ 152197b1a82SChristoph Hellwig .open = __fops ## _open, \ 153197b1a82SChristoph Hellwig .release = spufs_attr_release, \ 154197b1a82SChristoph Hellwig .read = spufs_attr_read, \ 155197b1a82SChristoph Hellwig .write = spufs_attr_write, \ 156197b1a82SChristoph Hellwig }; 157197b1a82SChristoph Hellwig 158cbe709c1SBenjamin Herrenschmidt 15967207b96SArnd Bergmann static int 16067207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file) 16167207b96SArnd Bergmann { 16267207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1636df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 16443c2bbd9SChristoph Hellwig 16547d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1666df10a82SMark Nutter file->private_data = ctx; 16743c2bbd9SChristoph Hellwig if (!i->i_openers++) 1686df10a82SMark Nutter ctx->local_store = inode->i_mapping; 16947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 17043c2bbd9SChristoph Hellwig return 0; 17143c2bbd9SChristoph Hellwig } 17243c2bbd9SChristoph Hellwig 17343c2bbd9SChristoph Hellwig static int 17443c2bbd9SChristoph Hellwig spufs_mem_release(struct inode *inode, struct file *file) 17543c2bbd9SChristoph Hellwig { 17643c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 17743c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 17843c2bbd9SChristoph Hellwig 17947d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 18043c2bbd9SChristoph Hellwig if (!--i->i_openers) 18143c2bbd9SChristoph Hellwig ctx->local_store = NULL; 18247d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 18367207b96SArnd Bergmann return 0; 18467207b96SArnd Bergmann } 18567207b96SArnd Bergmann 18667207b96SArnd Bergmann static ssize_t 187bf1ab978SDwayne Grant McConnell __spufs_mem_read(struct spu_context *ctx, char __user *buffer, 188bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 189bf1ab978SDwayne Grant McConnell { 190bf1ab978SDwayne Grant McConnell char *local_store = ctx->ops->get_ls(ctx); 191bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, local_store, 192bf1ab978SDwayne Grant McConnell LS_SIZE); 193bf1ab978SDwayne Grant McConnell } 194bf1ab978SDwayne Grant McConnell 195bf1ab978SDwayne Grant McConnell static ssize_t 19667207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer, 19767207b96SArnd Bergmann size_t size, loff_t *pos) 19867207b96SArnd Bergmann { 199bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 200aa0ed2bdSArnd Bergmann ssize_t ret; 20167207b96SArnd Bergmann 202c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 203c9101bdbSChristoph Hellwig if (ret) 204c9101bdbSChristoph Hellwig return ret; 205bf1ab978SDwayne Grant McConnell ret = __spufs_mem_read(ctx, buffer, size, pos); 2068b3d6663SArnd Bergmann spu_release(ctx); 207c9101bdbSChristoph Hellwig 20867207b96SArnd Bergmann return ret; 20967207b96SArnd Bergmann } 21067207b96SArnd Bergmann 21167207b96SArnd Bergmann static ssize_t 21267207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer, 213aa0ed2bdSArnd Bergmann size_t size, loff_t *ppos) 21467207b96SArnd Bergmann { 21567207b96SArnd Bergmann struct spu_context *ctx = file->private_data; 2168b3d6663SArnd Bergmann char *local_store; 217aa0ed2bdSArnd Bergmann loff_t pos = *ppos; 2188b3d6663SArnd Bergmann int ret; 21967207b96SArnd Bergmann 220aa0ed2bdSArnd Bergmann if (pos < 0) 221aa0ed2bdSArnd Bergmann return -EINVAL; 222aa0ed2bdSArnd Bergmann if (pos > LS_SIZE) 22367207b96SArnd Bergmann return -EFBIG; 224aa0ed2bdSArnd Bergmann if (size > LS_SIZE - pos) 225aa0ed2bdSArnd Bergmann size = LS_SIZE - pos; 2268b3d6663SArnd Bergmann 227c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 228c9101bdbSChristoph Hellwig if (ret) 229c9101bdbSChristoph Hellwig return ret; 230c9101bdbSChristoph Hellwig 2318b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 232aa0ed2bdSArnd Bergmann ret = copy_from_user(local_store + pos, buffer, size); 2338b3d6663SArnd Bergmann spu_release(ctx); 234aa0ed2bdSArnd Bergmann 235aa0ed2bdSArnd Bergmann if (ret) 236aa0ed2bdSArnd Bergmann return -EFAULT; 237aa0ed2bdSArnd Bergmann *ppos = pos + size; 238aa0ed2bdSArnd Bergmann return size; 23967207b96SArnd Bergmann } 24067207b96SArnd Bergmann 241b1e2270fSNick Piggin static int 242b1e2270fSNick Piggin spufs_mem_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 2438b3d6663SArnd Bergmann { 2448b3d6663SArnd Bergmann struct spu_context *ctx = vma->vm_file->private_data; 245b1e2270fSNick Piggin unsigned long address = (unsigned long)vmf->virtual_address; 246b1e2270fSNick Piggin unsigned long pfn, offset; 247b1e2270fSNick Piggin 248f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 249f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 250f1fa74f4SBenjamin Herrenschmidt int psize; 25178bde53eSBenjamin Herrenschmidt 252f1fa74f4SBenjamin Herrenschmidt /* Check what page size we are using */ 253f1fa74f4SBenjamin Herrenschmidt psize = get_slice_psize(vma->vm_mm, address); 2548b3d6663SArnd Bergmann 255f1fa74f4SBenjamin Herrenschmidt /* Some sanity checking */ 256f1fa74f4SBenjamin Herrenschmidt BUG_ON(csa->use_big_pages != (psize == MMU_PAGE_64K)); 257f1fa74f4SBenjamin Herrenschmidt 258f1fa74f4SBenjamin Herrenschmidt /* Wow, 64K, cool, we need to align the address though */ 259f1fa74f4SBenjamin Herrenschmidt if (csa->use_big_pages) { 260f1fa74f4SBenjamin Herrenschmidt BUG_ON(vma->vm_start & 0xffff); 261f1fa74f4SBenjamin Herrenschmidt address &= ~0xfffful; 262f1fa74f4SBenjamin Herrenschmidt } 263f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 264f1fa74f4SBenjamin Herrenschmidt 265b1e2270fSNick Piggin offset = vmf->pgoff << PAGE_SHIFT; 266128b8546SMasato Noguchi if (offset >= LS_SIZE) 267b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 268128b8546SMasato Noguchi 269b1e2270fSNick Piggin pr_debug("spufs_mem_mmap_fault address=0x%lx, offset=0x%lx\n", 270b1e2270fSNick Piggin address, offset); 271f1fa74f4SBenjamin Herrenschmidt 272c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 273b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2748b3d6663SArnd Bergmann 275ac91cb8dSArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 276ac91cb8dSArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 277932f535dSArnd Bergmann & ~_PAGE_NO_CACHE); 27878bde53eSBenjamin Herrenschmidt pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset); 279ac91cb8dSArnd Bergmann } else { 280ac91cb8dSArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 281932f535dSArnd Bergmann | _PAGE_NO_CACHE); 28278bde53eSBenjamin Herrenschmidt pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT; 283ac91cb8dSArnd Bergmann } 28478bde53eSBenjamin Herrenschmidt vm_insert_pfn(vma, address, pfn); 28578bde53eSBenjamin Herrenschmidt 2868b3d6663SArnd Bergmann spu_release(ctx); 2878b3d6663SArnd Bergmann 288b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 2898b3d6663SArnd Bergmann } 2908b3d6663SArnd Bergmann 29178bde53eSBenjamin Herrenschmidt 2928b3d6663SArnd Bergmann static struct vm_operations_struct spufs_mem_mmap_vmops = { 293b1e2270fSNick Piggin .fault = spufs_mem_mmap_fault, 2948b3d6663SArnd Bergmann }; 2958b3d6663SArnd Bergmann 296f1fa74f4SBenjamin Herrenschmidt static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 29767207b96SArnd Bergmann { 298f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 299f1fa74f4SBenjamin Herrenschmidt struct spu_context *ctx = file->private_data; 300f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 301f1fa74f4SBenjamin Herrenschmidt 302f1fa74f4SBenjamin Herrenschmidt /* Sanity check VMA alignment */ 303f1fa74f4SBenjamin Herrenschmidt if (csa->use_big_pages) { 304f1fa74f4SBenjamin Herrenschmidt pr_debug("spufs_mem_mmap 64K, start=0x%lx, end=0x%lx," 305f1fa74f4SBenjamin Herrenschmidt " pgoff=0x%lx\n", vma->vm_start, vma->vm_end, 306f1fa74f4SBenjamin Herrenschmidt vma->vm_pgoff); 307f1fa74f4SBenjamin Herrenschmidt if (vma->vm_start & 0xffff) 308f1fa74f4SBenjamin Herrenschmidt return -EINVAL; 309f1fa74f4SBenjamin Herrenschmidt if (vma->vm_pgoff & 0xf) 310f1fa74f4SBenjamin Herrenschmidt return -EINVAL; 311f1fa74f4SBenjamin Herrenschmidt } 312f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 313f1fa74f4SBenjamin Herrenschmidt 3148b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 3158b3d6663SArnd Bergmann return -EINVAL; 31667207b96SArnd Bergmann 31778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 31867207b96SArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 31967207b96SArnd Bergmann | _PAGE_NO_CACHE); 3208b3d6663SArnd Bergmann 3218b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 32267207b96SArnd Bergmann return 0; 32367207b96SArnd Bergmann } 32467207b96SArnd Bergmann 325f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 3261238819aSSebastian Siewior static unsigned long spufs_get_unmapped_area(struct file *file, 3271238819aSSebastian Siewior unsigned long addr, unsigned long len, unsigned long pgoff, 328f1fa74f4SBenjamin Herrenschmidt unsigned long flags) 329f1fa74f4SBenjamin Herrenschmidt { 330f1fa74f4SBenjamin Herrenschmidt struct spu_context *ctx = file->private_data; 331f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 332f1fa74f4SBenjamin Herrenschmidt 333f1fa74f4SBenjamin Herrenschmidt /* If not using big pages, fallback to normal MM g_u_a */ 334f1fa74f4SBenjamin Herrenschmidt if (!csa->use_big_pages) 335f1fa74f4SBenjamin Herrenschmidt return current->mm->get_unmapped_area(file, addr, len, 336f1fa74f4SBenjamin Herrenschmidt pgoff, flags); 337f1fa74f4SBenjamin Herrenschmidt 338f1fa74f4SBenjamin Herrenschmidt /* Else, try to obtain a 64K pages slice */ 339f1fa74f4SBenjamin Herrenschmidt return slice_get_unmapped_area(addr, len, flags, 340f1fa74f4SBenjamin Herrenschmidt MMU_PAGE_64K, 1, 0); 341f1fa74f4SBenjamin Herrenschmidt } 342f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 343f1fa74f4SBenjamin Herrenschmidt 3445dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = { 34567207b96SArnd Bergmann .open = spufs_mem_open, 346ce92987bSChristoph Hellwig .release = spufs_mem_release, 34767207b96SArnd Bergmann .read = spufs_mem_read, 34867207b96SArnd Bergmann .write = spufs_mem_write, 3498b3d6663SArnd Bergmann .llseek = generic_file_llseek, 35067207b96SArnd Bergmann .mmap = spufs_mem_mmap, 351f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 352f1fa74f4SBenjamin Herrenschmidt .get_unmapped_area = spufs_get_unmapped_area, 353f1fa74f4SBenjamin Herrenschmidt #endif 3548b3d6663SArnd Bergmann }; 3558b3d6663SArnd Bergmann 356b1e2270fSNick Piggin static int spufs_ps_fault(struct vm_area_struct *vma, 357b1e2270fSNick Piggin struct vm_fault *vmf, 35878bde53eSBenjamin Herrenschmidt unsigned long ps_offs, 35927d5bf2aSBenjamin Herrenschmidt unsigned long ps_size) 3606df10a82SMark Nutter { 3616df10a82SMark Nutter struct spu_context *ctx = vma->vm_file->private_data; 362b1e2270fSNick Piggin unsigned long area, offset = vmf->pgoff << PAGE_SHIFT; 363eebead5bSChristoph Hellwig int ret = 0; 3646df10a82SMark Nutter 365b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__enter, ctx); 366038200cfSChristoph Hellwig 36727d5bf2aSBenjamin Herrenschmidt if (offset >= ps_size) 368b1e2270fSNick Piggin return VM_FAULT_SIGBUS; 3696df10a82SMark Nutter 37033bfd7a7SArnd Bergmann /* 371d5883137SJeremy Kerr * Because we release the mmap_sem, the context may be destroyed while 372d5883137SJeremy Kerr * we're in spu_wait. Grab an extra reference so it isn't destroyed 373d5883137SJeremy Kerr * in the meantime. 374d5883137SJeremy Kerr */ 375d5883137SJeremy Kerr get_spu_context(ctx); 376d5883137SJeremy Kerr 377d5883137SJeremy Kerr /* 37833bfd7a7SArnd Bergmann * We have to wait for context to be loaded before we have 37933bfd7a7SArnd Bergmann * pages to hand out to the user, but we don't want to wait 38033bfd7a7SArnd Bergmann * with the mmap_sem held. 38133bfd7a7SArnd Bergmann * It is possible to drop the mmap_sem here, but then we need 382b1e2270fSNick Piggin * to return VM_FAULT_NOPAGE because the mappings may have 38333bfd7a7SArnd Bergmann * hanged. 38478bde53eSBenjamin Herrenschmidt */ 385c9101bdbSChristoph Hellwig if (spu_acquire(ctx)) 386d5883137SJeremy Kerr goto refault; 387c9101bdbSChristoph Hellwig 38833bfd7a7SArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 38933bfd7a7SArnd Bergmann up_read(¤t->mm->mmap_sem); 390b1e2270fSNick Piggin spu_context_nospu_trace(spufs_ps_fault__sleep, ctx); 391eebead5bSChristoph Hellwig ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 392b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__wake, ctx, ctx->spu); 39333bfd7a7SArnd Bergmann down_read(¤t->mm->mmap_sem); 394c9101bdbSChristoph Hellwig } else { 3956df10a82SMark Nutter area = ctx->spu->problem_phys + ps_offs; 396b1e2270fSNick Piggin vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, 397b1e2270fSNick Piggin (area + offset) >> PAGE_SHIFT); 398b1e2270fSNick Piggin spu_context_trace(spufs_ps_fault__insert, ctx, ctx->spu); 399c9101bdbSChristoph Hellwig } 40033bfd7a7SArnd Bergmann 401eebead5bSChristoph Hellwig if (!ret) 4026df10a82SMark Nutter spu_release(ctx); 403d5883137SJeremy Kerr 404d5883137SJeremy Kerr refault: 405d5883137SJeremy Kerr put_spu_context(ctx); 406b1e2270fSNick Piggin return VM_FAULT_NOPAGE; 4076df10a82SMark Nutter } 4086df10a82SMark Nutter 40927d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 410b1e2270fSNick Piggin static int spufs_cntl_mmap_fault(struct vm_area_struct *vma, 411b1e2270fSNick Piggin struct vm_fault *vmf) 4126df10a82SMark Nutter { 41387ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x4000, SPUFS_CNTL_MAP_SIZE); 4146df10a82SMark Nutter } 4156df10a82SMark Nutter 4166df10a82SMark Nutter static struct vm_operations_struct spufs_cntl_mmap_vmops = { 417b1e2270fSNick Piggin .fault = spufs_cntl_mmap_fault, 4186df10a82SMark Nutter }; 4196df10a82SMark Nutter 4206df10a82SMark Nutter /* 4216df10a82SMark Nutter * mmap support for problem state control area [0x4000 - 0x4fff]. 4226df10a82SMark Nutter */ 4236df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) 4246df10a82SMark Nutter { 4256df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 4266df10a82SMark Nutter return -EINVAL; 4276df10a82SMark Nutter 42878bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 4296df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 43023cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 4316df10a82SMark Nutter 4326df10a82SMark Nutter vma->vm_ops = &spufs_cntl_mmap_vmops; 4336df10a82SMark Nutter return 0; 4346df10a82SMark Nutter } 43527d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 43627d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL 43727d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 4386df10a82SMark Nutter 439197b1a82SChristoph Hellwig static int spufs_cntl_get(void *data, u64 *val) 440e1dbff2bSArnd Bergmann { 441e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 442c9101bdbSChristoph Hellwig int ret; 443e1dbff2bSArnd Bergmann 444c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 445c9101bdbSChristoph Hellwig if (ret) 446c9101bdbSChristoph Hellwig return ret; 447197b1a82SChristoph Hellwig *val = ctx->ops->status_read(ctx); 448e1dbff2bSArnd Bergmann spu_release(ctx); 449e1dbff2bSArnd Bergmann 450197b1a82SChristoph Hellwig return 0; 451e1dbff2bSArnd Bergmann } 452e1dbff2bSArnd Bergmann 453197b1a82SChristoph Hellwig static int spufs_cntl_set(void *data, u64 val) 454e1dbff2bSArnd Bergmann { 455e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 456c9101bdbSChristoph Hellwig int ret; 457e1dbff2bSArnd Bergmann 458c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 459c9101bdbSChristoph Hellwig if (ret) 460c9101bdbSChristoph Hellwig return ret; 461e1dbff2bSArnd Bergmann ctx->ops->runcntl_write(ctx, val); 462e1dbff2bSArnd Bergmann spu_release(ctx); 463197b1a82SChristoph Hellwig 464197b1a82SChristoph Hellwig return 0; 465e1dbff2bSArnd Bergmann } 466e1dbff2bSArnd Bergmann 4676df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file) 4686df10a82SMark Nutter { 4696df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 4706df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 4716df10a82SMark Nutter 47247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 4736df10a82SMark Nutter file->private_data = ctx; 47443c2bbd9SChristoph Hellwig if (!i->i_openers++) 4756df10a82SMark Nutter ctx->cntl = inode->i_mapping; 47647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 4778b88b099SChristoph Hellwig return simple_attr_open(inode, file, spufs_cntl_get, 478e1dbff2bSArnd Bergmann spufs_cntl_set, "0x%08lx"); 4796df10a82SMark Nutter } 4806df10a82SMark Nutter 48143c2bbd9SChristoph Hellwig static int 48243c2bbd9SChristoph Hellwig spufs_cntl_release(struct inode *inode, struct file *file) 48343c2bbd9SChristoph Hellwig { 48443c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 48543c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 48643c2bbd9SChristoph Hellwig 48774bedc4dSChristoph Hellwig simple_attr_release(inode, file); 48843c2bbd9SChristoph Hellwig 48947d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 49043c2bbd9SChristoph Hellwig if (!--i->i_openers) 49143c2bbd9SChristoph Hellwig ctx->cntl = NULL; 49247d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 49343c2bbd9SChristoph Hellwig return 0; 49443c2bbd9SChristoph Hellwig } 49543c2bbd9SChristoph Hellwig 4965dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = { 4976df10a82SMark Nutter .open = spufs_cntl_open, 49843c2bbd9SChristoph Hellwig .release = spufs_cntl_release, 4998b88b099SChristoph Hellwig .read = simple_attr_read, 5008b88b099SChristoph Hellwig .write = simple_attr_write, 5016df10a82SMark Nutter .mmap = spufs_cntl_mmap, 5026df10a82SMark Nutter }; 5036df10a82SMark Nutter 5048b3d6663SArnd Bergmann static int 5058b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 5068b3d6663SArnd Bergmann { 5078b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 5088b3d6663SArnd Bergmann file->private_data = i->i_ctx; 5098b3d6663SArnd Bergmann return 0; 5108b3d6663SArnd Bergmann } 5118b3d6663SArnd Bergmann 5128b3d6663SArnd Bergmann static ssize_t 513bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer, 514bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 515bf1ab978SDwayne Grant McConnell { 516bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 517bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 518bf1ab978SDwayne Grant McConnell lscsa->gprs, sizeof lscsa->gprs); 519bf1ab978SDwayne Grant McConnell } 520bf1ab978SDwayne Grant McConnell 521bf1ab978SDwayne Grant McConnell static ssize_t 5228b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 5238b3d6663SArnd Bergmann size_t size, loff_t *pos) 5248b3d6663SArnd Bergmann { 5258b3d6663SArnd Bergmann int ret; 526bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 5278b3d6663SArnd Bergmann 528c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 529c9101bdbSChristoph Hellwig if (ret) 530c9101bdbSChristoph Hellwig return ret; 531bf1ab978SDwayne Grant McConnell ret = __spufs_regs_read(ctx, buffer, size, pos); 53227b1ea09SChristoph Hellwig spu_release_saved(ctx); 5338b3d6663SArnd Bergmann return ret; 5348b3d6663SArnd Bergmann } 5358b3d6663SArnd Bergmann 5368b3d6663SArnd Bergmann static ssize_t 5378b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 5388b3d6663SArnd Bergmann size_t size, loff_t *pos) 5398b3d6663SArnd Bergmann { 5408b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5418b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5428b3d6663SArnd Bergmann int ret; 5438b3d6663SArnd Bergmann 5448b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size); 5458b3d6663SArnd Bergmann if (size <= 0) 5468b3d6663SArnd Bergmann return -EFBIG; 5478b3d6663SArnd Bergmann *pos += size; 5488b3d6663SArnd Bergmann 549c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 550c9101bdbSChristoph Hellwig if (ret) 551c9101bdbSChristoph Hellwig return ret; 5528b3d6663SArnd Bergmann 5538b3d6663SArnd Bergmann ret = copy_from_user(lscsa->gprs + *pos - size, 5548b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 5558b3d6663SArnd Bergmann 55627b1ea09SChristoph Hellwig spu_release_saved(ctx); 5578b3d6663SArnd Bergmann return ret; 5588b3d6663SArnd Bergmann } 5598b3d6663SArnd Bergmann 5605dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = { 5618b3d6663SArnd Bergmann .open = spufs_regs_open, 5628b3d6663SArnd Bergmann .read = spufs_regs_read, 5638b3d6663SArnd Bergmann .write = spufs_regs_write, 5648b3d6663SArnd Bergmann .llseek = generic_file_llseek, 5658b3d6663SArnd Bergmann }; 5668b3d6663SArnd Bergmann 5678b3d6663SArnd Bergmann static ssize_t 568bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer, 569bf1ab978SDwayne Grant McConnell size_t size, loff_t * pos) 570bf1ab978SDwayne Grant McConnell { 571bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 572bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 573bf1ab978SDwayne Grant McConnell &lscsa->fpcr, sizeof(lscsa->fpcr)); 574bf1ab978SDwayne Grant McConnell } 575bf1ab978SDwayne Grant McConnell 576bf1ab978SDwayne Grant McConnell static ssize_t 5778b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 5788b3d6663SArnd Bergmann size_t size, loff_t * pos) 5798b3d6663SArnd Bergmann { 5808b3d6663SArnd Bergmann int ret; 581bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 5828b3d6663SArnd Bergmann 583c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 584c9101bdbSChristoph Hellwig if (ret) 585c9101bdbSChristoph Hellwig return ret; 586bf1ab978SDwayne Grant McConnell ret = __spufs_fpcr_read(ctx, buffer, size, pos); 58727b1ea09SChristoph Hellwig spu_release_saved(ctx); 5888b3d6663SArnd Bergmann return ret; 5898b3d6663SArnd Bergmann } 5908b3d6663SArnd Bergmann 5918b3d6663SArnd Bergmann static ssize_t 5928b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 5938b3d6663SArnd Bergmann size_t size, loff_t * pos) 5948b3d6663SArnd Bergmann { 5958b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5968b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5978b3d6663SArnd Bergmann int ret; 5988b3d6663SArnd Bergmann 5998b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); 6008b3d6663SArnd Bergmann if (size <= 0) 6018b3d6663SArnd Bergmann return -EFBIG; 602c9101bdbSChristoph Hellwig 603c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 604c9101bdbSChristoph Hellwig if (ret) 605c9101bdbSChristoph Hellwig return ret; 606c9101bdbSChristoph Hellwig 6078b3d6663SArnd Bergmann *pos += size; 6088b3d6663SArnd Bergmann ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, 6098b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 6108b3d6663SArnd Bergmann 61127b1ea09SChristoph Hellwig spu_release_saved(ctx); 6128b3d6663SArnd Bergmann return ret; 6138b3d6663SArnd Bergmann } 6148b3d6663SArnd Bergmann 6155dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = { 6168b3d6663SArnd Bergmann .open = spufs_regs_open, 6178b3d6663SArnd Bergmann .read = spufs_fpcr_read, 6188b3d6663SArnd Bergmann .write = spufs_fpcr_write, 61967207b96SArnd Bergmann .llseek = generic_file_llseek, 62067207b96SArnd Bergmann }; 62167207b96SArnd Bergmann 62267207b96SArnd Bergmann /* generic open function for all pipe-like files */ 62367207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 62467207b96SArnd Bergmann { 62567207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 62667207b96SArnd Bergmann file->private_data = i->i_ctx; 62767207b96SArnd Bergmann 62867207b96SArnd Bergmann return nonseekable_open(inode, file); 62967207b96SArnd Bergmann } 63067207b96SArnd Bergmann 631cdcc89bbSArnd Bergmann /* 632cdcc89bbSArnd Bergmann * Read as many bytes from the mailbox as possible, until 633cdcc89bbSArnd Bergmann * one of the conditions becomes true: 634cdcc89bbSArnd Bergmann * 635cdcc89bbSArnd Bergmann * - no more data available in the mailbox 636cdcc89bbSArnd Bergmann * - end of the user provided buffer 637cdcc89bbSArnd Bergmann * - end of the mapped area 638cdcc89bbSArnd Bergmann */ 63967207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 64067207b96SArnd Bergmann size_t len, loff_t *pos) 64167207b96SArnd Bergmann { 6428b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 643cdcc89bbSArnd Bergmann u32 mbox_data, __user *udata; 644cdcc89bbSArnd Bergmann ssize_t count; 64567207b96SArnd Bergmann 64667207b96SArnd Bergmann if (len < 4) 64767207b96SArnd Bergmann return -EINVAL; 64867207b96SArnd Bergmann 649cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 65067207b96SArnd Bergmann return -EFAULT; 65167207b96SArnd Bergmann 652cdcc89bbSArnd Bergmann udata = (void __user *)buf; 653cdcc89bbSArnd Bergmann 654c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 655c9101bdbSChristoph Hellwig if (count) 656c9101bdbSChristoph Hellwig return count; 657c9101bdbSChristoph Hellwig 658274cef5eSArnd Bergmann for (count = 0; (count + 4) <= len; count += 4, udata++) { 659cdcc89bbSArnd Bergmann int ret; 660cdcc89bbSArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 661cdcc89bbSArnd Bergmann if (ret == 0) 662cdcc89bbSArnd Bergmann break; 663cdcc89bbSArnd Bergmann 664cdcc89bbSArnd Bergmann /* 665cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 666cdcc89bbSArnd Bergmann * but still need to return the data we have 667cdcc89bbSArnd Bergmann * read successfully so far. 668cdcc89bbSArnd Bergmann */ 669cdcc89bbSArnd Bergmann ret = __put_user(mbox_data, udata); 670cdcc89bbSArnd Bergmann if (ret) { 671cdcc89bbSArnd Bergmann if (!count) 672cdcc89bbSArnd Bergmann count = -EFAULT; 673cdcc89bbSArnd Bergmann break; 674cdcc89bbSArnd Bergmann } 675cdcc89bbSArnd Bergmann } 676cdcc89bbSArnd Bergmann spu_release(ctx); 677cdcc89bbSArnd Bergmann 678cdcc89bbSArnd Bergmann if (!count) 679cdcc89bbSArnd Bergmann count = -EAGAIN; 680cdcc89bbSArnd Bergmann 681cdcc89bbSArnd Bergmann return count; 68267207b96SArnd Bergmann } 68367207b96SArnd Bergmann 6845dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = { 68567207b96SArnd Bergmann .open = spufs_pipe_open, 68667207b96SArnd Bergmann .read = spufs_mbox_read, 68767207b96SArnd Bergmann }; 68867207b96SArnd Bergmann 68967207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 69067207b96SArnd Bergmann size_t len, loff_t *pos) 69167207b96SArnd Bergmann { 6928b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 693c9101bdbSChristoph Hellwig ssize_t ret; 69467207b96SArnd Bergmann u32 mbox_stat; 69567207b96SArnd Bergmann 69667207b96SArnd Bergmann if (len < 4) 69767207b96SArnd Bergmann return -EINVAL; 69867207b96SArnd Bergmann 699c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 700c9101bdbSChristoph Hellwig if (ret) 701c9101bdbSChristoph Hellwig return ret; 7028b3d6663SArnd Bergmann 7038b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 7048b3d6663SArnd Bergmann 7058b3d6663SArnd Bergmann spu_release(ctx); 70667207b96SArnd Bergmann 70767207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 70867207b96SArnd Bergmann return -EFAULT; 70967207b96SArnd Bergmann 71067207b96SArnd Bergmann return 4; 71167207b96SArnd Bergmann } 71267207b96SArnd Bergmann 7135dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = { 71467207b96SArnd Bergmann .open = spufs_pipe_open, 71567207b96SArnd Bergmann .read = spufs_mbox_stat_read, 71667207b96SArnd Bergmann }; 71767207b96SArnd Bergmann 71867207b96SArnd Bergmann /* low-level ibox access function */ 7198b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 72067207b96SArnd Bergmann { 7218b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 72267207b96SArnd Bergmann } 72367207b96SArnd Bergmann 72467207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on) 72567207b96SArnd Bergmann { 7268b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 7278b3d6663SArnd Bergmann 7288b3d6663SArnd Bergmann return fasync_helper(fd, file, on, &ctx->ibox_fasync); 7298b3d6663SArnd Bergmann } 7308b3d6663SArnd Bergmann 7318b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 7328b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 7338b3d6663SArnd Bergmann { 7348b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 7358b3d6663SArnd Bergmann 736e65c2f6fSLuke Browning if (!ctx) 737e65c2f6fSLuke Browning return; 738e65c2f6fSLuke Browning 7398b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 7408b3d6663SArnd Bergmann kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); 74167207b96SArnd Bergmann } 74267207b96SArnd Bergmann 743cdcc89bbSArnd Bergmann /* 744cdcc89bbSArnd Bergmann * Read as many bytes from the interrupt mailbox as possible, until 745cdcc89bbSArnd Bergmann * one of the conditions becomes true: 746cdcc89bbSArnd Bergmann * 747cdcc89bbSArnd Bergmann * - no more data available in the mailbox 748cdcc89bbSArnd Bergmann * - end of the user provided buffer 749cdcc89bbSArnd Bergmann * - end of the mapped area 750cdcc89bbSArnd Bergmann * 751cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 752cdcc89bbSArnd Bergmann * any data is available, but return when we have been able to 753cdcc89bbSArnd Bergmann * read something. 754cdcc89bbSArnd Bergmann */ 75567207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 75667207b96SArnd Bergmann size_t len, loff_t *pos) 75767207b96SArnd Bergmann { 7588b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 759cdcc89bbSArnd Bergmann u32 ibox_data, __user *udata; 760cdcc89bbSArnd Bergmann ssize_t count; 76167207b96SArnd Bergmann 76267207b96SArnd Bergmann if (len < 4) 76367207b96SArnd Bergmann return -EINVAL; 76467207b96SArnd Bergmann 765cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 766cdcc89bbSArnd Bergmann return -EFAULT; 767cdcc89bbSArnd Bergmann 768cdcc89bbSArnd Bergmann udata = (void __user *)buf; 769cdcc89bbSArnd Bergmann 770c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 771c9101bdbSChristoph Hellwig if (count) 772eebead5bSChristoph Hellwig goto out; 77367207b96SArnd Bergmann 774cdcc89bbSArnd Bergmann /* wait only for the first element */ 775cdcc89bbSArnd Bergmann count = 0; 77667207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 777eebead5bSChristoph Hellwig if (!spu_ibox_read(ctx, &ibox_data)) { 778cdcc89bbSArnd Bergmann count = -EAGAIN; 779eebead5bSChristoph Hellwig goto out_unlock; 780eebead5bSChristoph Hellwig } 78167207b96SArnd Bergmann } else { 782cdcc89bbSArnd Bergmann count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 783cdcc89bbSArnd Bergmann if (count) 784cdcc89bbSArnd Bergmann goto out; 785eebead5bSChristoph Hellwig } 786cdcc89bbSArnd Bergmann 787cdcc89bbSArnd Bergmann /* if we can't write at all, return -EFAULT */ 788cdcc89bbSArnd Bergmann count = __put_user(ibox_data, udata); 789cdcc89bbSArnd Bergmann if (count) 790eebead5bSChristoph Hellwig goto out_unlock; 791cdcc89bbSArnd Bergmann 792cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 793cdcc89bbSArnd Bergmann int ret; 794cdcc89bbSArnd Bergmann ret = ctx->ops->ibox_read(ctx, &ibox_data); 795cdcc89bbSArnd Bergmann if (ret == 0) 796cdcc89bbSArnd Bergmann break; 797cdcc89bbSArnd Bergmann /* 798cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 799cdcc89bbSArnd Bergmann * but still need to return the data we have 800cdcc89bbSArnd Bergmann * read successfully so far. 801cdcc89bbSArnd Bergmann */ 802cdcc89bbSArnd Bergmann ret = __put_user(ibox_data, udata); 803cdcc89bbSArnd Bergmann if (ret) 804cdcc89bbSArnd Bergmann break; 80567207b96SArnd Bergmann } 80667207b96SArnd Bergmann 807eebead5bSChristoph Hellwig out_unlock: 8088b3d6663SArnd Bergmann spu_release(ctx); 809eebead5bSChristoph Hellwig out: 810cdcc89bbSArnd Bergmann return count; 81167207b96SArnd Bergmann } 81267207b96SArnd Bergmann 81367207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) 81467207b96SArnd Bergmann { 8158b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 81667207b96SArnd Bergmann unsigned int mask; 81767207b96SArnd Bergmann 8188b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 81967207b96SArnd Bergmann 820c9101bdbSChristoph Hellwig /* 821c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 822c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 823c9101bdbSChristoph Hellwig */ 824c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 8253a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); 8263a843d7cSArnd Bergmann spu_release(ctx); 82767207b96SArnd Bergmann 82867207b96SArnd Bergmann return mask; 82967207b96SArnd Bergmann } 83067207b96SArnd Bergmann 8315dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = { 83267207b96SArnd Bergmann .open = spufs_pipe_open, 83367207b96SArnd Bergmann .read = spufs_ibox_read, 83467207b96SArnd Bergmann .poll = spufs_ibox_poll, 83567207b96SArnd Bergmann .fasync = spufs_ibox_fasync, 83667207b96SArnd Bergmann }; 83767207b96SArnd Bergmann 83867207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 83967207b96SArnd Bergmann size_t len, loff_t *pos) 84067207b96SArnd Bergmann { 8418b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 842c9101bdbSChristoph Hellwig ssize_t ret; 84367207b96SArnd Bergmann u32 ibox_stat; 84467207b96SArnd Bergmann 84567207b96SArnd Bergmann if (len < 4) 84667207b96SArnd Bergmann return -EINVAL; 84767207b96SArnd Bergmann 848c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 849c9101bdbSChristoph Hellwig if (ret) 850c9101bdbSChristoph Hellwig return ret; 8518b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 8528b3d6663SArnd Bergmann spu_release(ctx); 85367207b96SArnd Bergmann 85467207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 85567207b96SArnd Bergmann return -EFAULT; 85667207b96SArnd Bergmann 85767207b96SArnd Bergmann return 4; 85867207b96SArnd Bergmann } 85967207b96SArnd Bergmann 8605dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = { 86167207b96SArnd Bergmann .open = spufs_pipe_open, 86267207b96SArnd Bergmann .read = spufs_ibox_stat_read, 86367207b96SArnd Bergmann }; 86467207b96SArnd Bergmann 86567207b96SArnd Bergmann /* low-level mailbox write */ 8668b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 86767207b96SArnd Bergmann { 8688b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 86967207b96SArnd Bergmann } 87067207b96SArnd Bergmann 87167207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on) 87267207b96SArnd Bergmann { 8738b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 8748b3d6663SArnd Bergmann int ret; 8758b3d6663SArnd Bergmann 8768b3d6663SArnd Bergmann ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); 8778b3d6663SArnd Bergmann 8788b3d6663SArnd Bergmann return ret; 8798b3d6663SArnd Bergmann } 8808b3d6663SArnd Bergmann 8818b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 8828b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 8838b3d6663SArnd Bergmann { 8848b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 8858b3d6663SArnd Bergmann 886e65c2f6fSLuke Browning if (!ctx) 887e65c2f6fSLuke Browning return; 888e65c2f6fSLuke Browning 8898b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 8908b3d6663SArnd Bergmann kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); 89167207b96SArnd Bergmann } 89267207b96SArnd Bergmann 893cdcc89bbSArnd Bergmann /* 894cdcc89bbSArnd Bergmann * Write as many bytes to the interrupt mailbox as possible, until 895cdcc89bbSArnd Bergmann * one of the conditions becomes true: 896cdcc89bbSArnd Bergmann * 897cdcc89bbSArnd Bergmann * - the mailbox is full 898cdcc89bbSArnd Bergmann * - end of the user provided buffer 899cdcc89bbSArnd Bergmann * - end of the mapped area 900cdcc89bbSArnd Bergmann * 901cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 902cdcc89bbSArnd Bergmann * space is availabyl, but return when we have been able to 903cdcc89bbSArnd Bergmann * write something. 904cdcc89bbSArnd Bergmann */ 90567207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 90667207b96SArnd Bergmann size_t len, loff_t *pos) 90767207b96SArnd Bergmann { 9088b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 909cdcc89bbSArnd Bergmann u32 wbox_data, __user *udata; 910cdcc89bbSArnd Bergmann ssize_t count; 91167207b96SArnd Bergmann 91267207b96SArnd Bergmann if (len < 4) 91367207b96SArnd Bergmann return -EINVAL; 91467207b96SArnd Bergmann 915cdcc89bbSArnd Bergmann udata = (void __user *)buf; 916cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_READ, buf, len)) 917cdcc89bbSArnd Bergmann return -EFAULT; 918cdcc89bbSArnd Bergmann 919cdcc89bbSArnd Bergmann if (__get_user(wbox_data, udata)) 92067207b96SArnd Bergmann return -EFAULT; 92167207b96SArnd Bergmann 922c9101bdbSChristoph Hellwig count = spu_acquire(ctx); 923c9101bdbSChristoph Hellwig if (count) 924eebead5bSChristoph Hellwig goto out; 9258b3d6663SArnd Bergmann 926cdcc89bbSArnd Bergmann /* 927cdcc89bbSArnd Bergmann * make sure we can at least write one element, by waiting 928cdcc89bbSArnd Bergmann * in case of !O_NONBLOCK 929cdcc89bbSArnd Bergmann */ 930cdcc89bbSArnd Bergmann count = 0; 93167207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 932eebead5bSChristoph Hellwig if (!spu_wbox_write(ctx, wbox_data)) { 933cdcc89bbSArnd Bergmann count = -EAGAIN; 934eebead5bSChristoph Hellwig goto out_unlock; 935eebead5bSChristoph Hellwig } 93667207b96SArnd Bergmann } else { 937cdcc89bbSArnd Bergmann count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 938cdcc89bbSArnd Bergmann if (count) 939cdcc89bbSArnd Bergmann goto out; 940eebead5bSChristoph Hellwig } 941eebead5bSChristoph Hellwig 9428b3d6663SArnd Bergmann 94396de0e25SJan Engelhardt /* write as much as possible */ 944cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 945cdcc89bbSArnd Bergmann int ret; 946cdcc89bbSArnd Bergmann ret = __get_user(wbox_data, udata); 947cdcc89bbSArnd Bergmann if (ret) 948cdcc89bbSArnd Bergmann break; 949cdcc89bbSArnd Bergmann 950cdcc89bbSArnd Bergmann ret = spu_wbox_write(ctx, wbox_data); 951cdcc89bbSArnd Bergmann if (ret == 0) 952cdcc89bbSArnd Bergmann break; 953cdcc89bbSArnd Bergmann } 954cdcc89bbSArnd Bergmann 955eebead5bSChristoph Hellwig out_unlock: 956cdcc89bbSArnd Bergmann spu_release(ctx); 957eebead5bSChristoph Hellwig out: 958cdcc89bbSArnd Bergmann return count; 95967207b96SArnd Bergmann } 96067207b96SArnd Bergmann 96167207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) 96267207b96SArnd Bergmann { 9638b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 96467207b96SArnd Bergmann unsigned int mask; 96567207b96SArnd Bergmann 9668b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 96767207b96SArnd Bergmann 968c9101bdbSChristoph Hellwig /* 969c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 970c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 971c9101bdbSChristoph Hellwig */ 972c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 9733a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); 9743a843d7cSArnd Bergmann spu_release(ctx); 97567207b96SArnd Bergmann 97667207b96SArnd Bergmann return mask; 97767207b96SArnd Bergmann } 97867207b96SArnd Bergmann 9795dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = { 98067207b96SArnd Bergmann .open = spufs_pipe_open, 98167207b96SArnd Bergmann .write = spufs_wbox_write, 98267207b96SArnd Bergmann .poll = spufs_wbox_poll, 98367207b96SArnd Bergmann .fasync = spufs_wbox_fasync, 98467207b96SArnd Bergmann }; 98567207b96SArnd Bergmann 98667207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 98767207b96SArnd Bergmann size_t len, loff_t *pos) 98867207b96SArnd Bergmann { 9898b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 990c9101bdbSChristoph Hellwig ssize_t ret; 99167207b96SArnd Bergmann u32 wbox_stat; 99267207b96SArnd Bergmann 99367207b96SArnd Bergmann if (len < 4) 99467207b96SArnd Bergmann return -EINVAL; 99567207b96SArnd Bergmann 996c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 997c9101bdbSChristoph Hellwig if (ret) 998c9101bdbSChristoph Hellwig return ret; 9998b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 10008b3d6663SArnd Bergmann spu_release(ctx); 100167207b96SArnd Bergmann 100267207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 100367207b96SArnd Bergmann return -EFAULT; 100467207b96SArnd Bergmann 100567207b96SArnd Bergmann return 4; 100667207b96SArnd Bergmann } 100767207b96SArnd Bergmann 10085dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = { 100967207b96SArnd Bergmann .open = spufs_pipe_open, 101067207b96SArnd Bergmann .read = spufs_wbox_stat_read, 101167207b96SArnd Bergmann }; 101267207b96SArnd Bergmann 10136df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file) 10146df10a82SMark Nutter { 10156df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 10166df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 101743c2bbd9SChristoph Hellwig 101847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 10196df10a82SMark Nutter file->private_data = ctx; 102043c2bbd9SChristoph Hellwig if (!i->i_openers++) 10216df10a82SMark Nutter ctx->signal1 = inode->i_mapping; 102247d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 10236df10a82SMark Nutter return nonseekable_open(inode, file); 10246df10a82SMark Nutter } 10256df10a82SMark Nutter 102643c2bbd9SChristoph Hellwig static int 102743c2bbd9SChristoph Hellwig spufs_signal1_release(struct inode *inode, struct file *file) 102843c2bbd9SChristoph Hellwig { 102943c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 103043c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 103143c2bbd9SChristoph Hellwig 103247d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 103343c2bbd9SChristoph Hellwig if (!--i->i_openers) 103443c2bbd9SChristoph Hellwig ctx->signal1 = NULL; 103547d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 103643c2bbd9SChristoph Hellwig return 0; 103743c2bbd9SChristoph Hellwig } 103843c2bbd9SChristoph Hellwig 1039bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf, 104067207b96SArnd Bergmann size_t len, loff_t *pos) 104167207b96SArnd Bergmann { 104217f88cebSDwayne Grant McConnell int ret = 0; 104367207b96SArnd Bergmann u32 data; 104467207b96SArnd Bergmann 104567207b96SArnd Bergmann if (len < 4) 104667207b96SArnd Bergmann return -EINVAL; 104767207b96SArnd Bergmann 104817f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[3]) { 104917f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[3]; 105017f88cebSDwayne Grant McConnell ret = 4; 105117f88cebSDwayne Grant McConnell } 10528b3d6663SArnd Bergmann 105317f88cebSDwayne Grant McConnell if (!ret) 105417f88cebSDwayne Grant McConnell goto out; 105517f88cebSDwayne Grant McConnell 105667207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 105767207b96SArnd Bergmann return -EFAULT; 105867207b96SArnd Bergmann 105917f88cebSDwayne Grant McConnell out: 106017f88cebSDwayne Grant McConnell return ret; 106167207b96SArnd Bergmann } 106267207b96SArnd Bergmann 1063bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 1064bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1065bf1ab978SDwayne Grant McConnell { 1066bf1ab978SDwayne Grant McConnell int ret; 1067bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1068bf1ab978SDwayne Grant McConnell 1069c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1070c9101bdbSChristoph Hellwig if (ret) 1071c9101bdbSChristoph Hellwig return ret; 1072bf1ab978SDwayne Grant McConnell ret = __spufs_signal1_read(ctx, buf, len, pos); 107327b1ea09SChristoph Hellwig spu_release_saved(ctx); 1074bf1ab978SDwayne Grant McConnell 1075bf1ab978SDwayne Grant McConnell return ret; 1076bf1ab978SDwayne Grant McConnell } 1077bf1ab978SDwayne Grant McConnell 107867207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 107967207b96SArnd Bergmann size_t len, loff_t *pos) 108067207b96SArnd Bergmann { 108167207b96SArnd Bergmann struct spu_context *ctx; 1082c9101bdbSChristoph Hellwig ssize_t ret; 108367207b96SArnd Bergmann u32 data; 108467207b96SArnd Bergmann 108567207b96SArnd Bergmann ctx = file->private_data; 108667207b96SArnd Bergmann 108767207b96SArnd Bergmann if (len < 4) 108867207b96SArnd Bergmann return -EINVAL; 108967207b96SArnd Bergmann 109067207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 109167207b96SArnd Bergmann return -EFAULT; 109267207b96SArnd Bergmann 1093c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1094c9101bdbSChristoph Hellwig if (ret) 1095c9101bdbSChristoph Hellwig return ret; 10968b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 10978b3d6663SArnd Bergmann spu_release(ctx); 109867207b96SArnd Bergmann 109967207b96SArnd Bergmann return 4; 110067207b96SArnd Bergmann } 110167207b96SArnd Bergmann 1102b1e2270fSNick Piggin static int 1103b1e2270fSNick Piggin spufs_signal1_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 11046df10a82SMark Nutter { 110587ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 110687ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x14000, SPUFS_SIGNAL_MAP_SIZE); 110787ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 110827d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 110927d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 111027d5bf2aSBenjamin Herrenschmidt */ 111187ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 111227d5bf2aSBenjamin Herrenschmidt #else 111327d5bf2aSBenjamin Herrenschmidt #error unsupported page size 111427d5bf2aSBenjamin Herrenschmidt #endif 11156df10a82SMark Nutter } 11166df10a82SMark Nutter 11176df10a82SMark Nutter static struct vm_operations_struct spufs_signal1_mmap_vmops = { 1118b1e2270fSNick Piggin .fault = spufs_signal1_mmap_fault, 11196df10a82SMark Nutter }; 11206df10a82SMark Nutter 11216df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) 11226df10a82SMark Nutter { 11236df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 11246df10a82SMark Nutter return -EINVAL; 11256df10a82SMark Nutter 112678bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 11276df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 112823cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 11296df10a82SMark Nutter 11306df10a82SMark Nutter vma->vm_ops = &spufs_signal1_mmap_vmops; 11316df10a82SMark Nutter return 0; 11326df10a82SMark Nutter } 11336df10a82SMark Nutter 11345dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = { 11356df10a82SMark Nutter .open = spufs_signal1_open, 113643c2bbd9SChristoph Hellwig .release = spufs_signal1_release, 113767207b96SArnd Bergmann .read = spufs_signal1_read, 113867207b96SArnd Bergmann .write = spufs_signal1_write, 11396df10a82SMark Nutter .mmap = spufs_signal1_mmap, 114067207b96SArnd Bergmann }; 114167207b96SArnd Bergmann 1142d054b36fSJeremy Kerr static const struct file_operations spufs_signal1_nosched_fops = { 1143d054b36fSJeremy Kerr .open = spufs_signal1_open, 1144d054b36fSJeremy Kerr .release = spufs_signal1_release, 1145d054b36fSJeremy Kerr .write = spufs_signal1_write, 1146d054b36fSJeremy Kerr .mmap = spufs_signal1_mmap, 1147d054b36fSJeremy Kerr }; 1148d054b36fSJeremy Kerr 11496df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file) 11506df10a82SMark Nutter { 11516df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 11526df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 115343c2bbd9SChristoph Hellwig 115447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 11556df10a82SMark Nutter file->private_data = ctx; 115643c2bbd9SChristoph Hellwig if (!i->i_openers++) 11576df10a82SMark Nutter ctx->signal2 = inode->i_mapping; 115847d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 11596df10a82SMark Nutter return nonseekable_open(inode, file); 11606df10a82SMark Nutter } 11616df10a82SMark Nutter 116243c2bbd9SChristoph Hellwig static int 116343c2bbd9SChristoph Hellwig spufs_signal2_release(struct inode *inode, struct file *file) 116443c2bbd9SChristoph Hellwig { 116543c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 116643c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 116743c2bbd9SChristoph Hellwig 116847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 116943c2bbd9SChristoph Hellwig if (!--i->i_openers) 117043c2bbd9SChristoph Hellwig ctx->signal2 = NULL; 117147d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 117243c2bbd9SChristoph Hellwig return 0; 117343c2bbd9SChristoph Hellwig } 117443c2bbd9SChristoph Hellwig 1175bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf, 117667207b96SArnd Bergmann size_t len, loff_t *pos) 117767207b96SArnd Bergmann { 117817f88cebSDwayne Grant McConnell int ret = 0; 117967207b96SArnd Bergmann u32 data; 118067207b96SArnd Bergmann 118167207b96SArnd Bergmann if (len < 4) 118267207b96SArnd Bergmann return -EINVAL; 118367207b96SArnd Bergmann 118417f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[4]) { 118517f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[4]; 118617f88cebSDwayne Grant McConnell ret = 4; 118717f88cebSDwayne Grant McConnell } 11888b3d6663SArnd Bergmann 118917f88cebSDwayne Grant McConnell if (!ret) 119017f88cebSDwayne Grant McConnell goto out; 119117f88cebSDwayne Grant McConnell 119267207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 119367207b96SArnd Bergmann return -EFAULT; 119467207b96SArnd Bergmann 119517f88cebSDwayne Grant McConnell out: 1196bf1ab978SDwayne Grant McConnell return ret; 1197bf1ab978SDwayne Grant McConnell } 1198bf1ab978SDwayne Grant McConnell 1199bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 1200bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1201bf1ab978SDwayne Grant McConnell { 1202bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1203bf1ab978SDwayne Grant McConnell int ret; 1204bf1ab978SDwayne Grant McConnell 1205c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1206c9101bdbSChristoph Hellwig if (ret) 1207c9101bdbSChristoph Hellwig return ret; 1208bf1ab978SDwayne Grant McConnell ret = __spufs_signal2_read(ctx, buf, len, pos); 120927b1ea09SChristoph Hellwig spu_release_saved(ctx); 1210bf1ab978SDwayne Grant McConnell 1211bf1ab978SDwayne Grant McConnell return ret; 121267207b96SArnd Bergmann } 121367207b96SArnd Bergmann 121467207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 121567207b96SArnd Bergmann size_t len, loff_t *pos) 121667207b96SArnd Bergmann { 121767207b96SArnd Bergmann struct spu_context *ctx; 1218c9101bdbSChristoph Hellwig ssize_t ret; 121967207b96SArnd Bergmann u32 data; 122067207b96SArnd Bergmann 122167207b96SArnd Bergmann ctx = file->private_data; 122267207b96SArnd Bergmann 122367207b96SArnd Bergmann if (len < 4) 122467207b96SArnd Bergmann return -EINVAL; 122567207b96SArnd Bergmann 122667207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 122767207b96SArnd Bergmann return -EFAULT; 122867207b96SArnd Bergmann 1229c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1230c9101bdbSChristoph Hellwig if (ret) 1231c9101bdbSChristoph Hellwig return ret; 12328b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 12338b3d6663SArnd Bergmann spu_release(ctx); 123467207b96SArnd Bergmann 123567207b96SArnd Bergmann return 4; 123667207b96SArnd Bergmann } 123767207b96SArnd Bergmann 123827d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1239b1e2270fSNick Piggin static int 1240b1e2270fSNick Piggin spufs_signal2_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 12416df10a82SMark Nutter { 124287ff6090SJeremy Kerr #if SPUFS_SIGNAL_MAP_SIZE == 0x1000 124387ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x1c000, SPUFS_SIGNAL_MAP_SIZE); 124487ff6090SJeremy Kerr #elif SPUFS_SIGNAL_MAP_SIZE == 0x10000 124527d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 124627d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 124727d5bf2aSBenjamin Herrenschmidt */ 124887ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x10000, SPUFS_SIGNAL_MAP_SIZE); 124927d5bf2aSBenjamin Herrenschmidt #else 125027d5bf2aSBenjamin Herrenschmidt #error unsupported page size 125127d5bf2aSBenjamin Herrenschmidt #endif 12526df10a82SMark Nutter } 12536df10a82SMark Nutter 12546df10a82SMark Nutter static struct vm_operations_struct spufs_signal2_mmap_vmops = { 1255b1e2270fSNick Piggin .fault = spufs_signal2_mmap_fault, 12566df10a82SMark Nutter }; 12576df10a82SMark Nutter 12586df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) 12596df10a82SMark Nutter { 12606df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 12616df10a82SMark Nutter return -EINVAL; 12626df10a82SMark Nutter 126378bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 12646df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 126523cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 12666df10a82SMark Nutter 12676df10a82SMark Nutter vma->vm_ops = &spufs_signal2_mmap_vmops; 12686df10a82SMark Nutter return 0; 12696df10a82SMark Nutter } 127027d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 127127d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL 127227d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 12736df10a82SMark Nutter 12745dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = { 12756df10a82SMark Nutter .open = spufs_signal2_open, 127643c2bbd9SChristoph Hellwig .release = spufs_signal2_release, 127767207b96SArnd Bergmann .read = spufs_signal2_read, 127867207b96SArnd Bergmann .write = spufs_signal2_write, 12796df10a82SMark Nutter .mmap = spufs_signal2_mmap, 128067207b96SArnd Bergmann }; 128167207b96SArnd Bergmann 1282d054b36fSJeremy Kerr static const struct file_operations spufs_signal2_nosched_fops = { 1283d054b36fSJeremy Kerr .open = spufs_signal2_open, 1284d054b36fSJeremy Kerr .release = spufs_signal2_release, 1285d054b36fSJeremy Kerr .write = spufs_signal2_write, 1286d054b36fSJeremy Kerr .mmap = spufs_signal2_mmap, 1287d054b36fSJeremy Kerr }; 1288d054b36fSJeremy Kerr 1289104f0cc2SMichael Ellerman /* 1290104f0cc2SMichael Ellerman * This is a wrapper around DEFINE_SIMPLE_ATTRIBUTE which does the 1291104f0cc2SMichael Ellerman * work of acquiring (or not) the SPU context before calling through 1292104f0cc2SMichael Ellerman * to the actual get routine. The set routine is called directly. 1293104f0cc2SMichael Ellerman */ 1294104f0cc2SMichael Ellerman #define SPU_ATTR_NOACQUIRE 0 1295104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE 1 1296104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE_SAVED 2 1297104f0cc2SMichael Ellerman 1298104f0cc2SMichael Ellerman #define DEFINE_SPUFS_ATTRIBUTE(__name, __get, __set, __fmt, __acquire) \ 1299197b1a82SChristoph Hellwig static int __##__get(void *data, u64 *val) \ 1300104f0cc2SMichael Ellerman { \ 1301104f0cc2SMichael Ellerman struct spu_context *ctx = data; \ 1302c9101bdbSChristoph Hellwig int ret = 0; \ 1303104f0cc2SMichael Ellerman \ 1304104f0cc2SMichael Ellerman if (__acquire == SPU_ATTR_ACQUIRE) { \ 1305c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); \ 1306c9101bdbSChristoph Hellwig if (ret) \ 1307c9101bdbSChristoph Hellwig return ret; \ 1308197b1a82SChristoph Hellwig *val = __get(ctx); \ 1309104f0cc2SMichael Ellerman spu_release(ctx); \ 1310104f0cc2SMichael Ellerman } else if (__acquire == SPU_ATTR_ACQUIRE_SAVED) { \ 1311c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); \ 1312c9101bdbSChristoph Hellwig if (ret) \ 1313c9101bdbSChristoph Hellwig return ret; \ 1314197b1a82SChristoph Hellwig *val = __get(ctx); \ 1315104f0cc2SMichael Ellerman spu_release_saved(ctx); \ 1316104f0cc2SMichael Ellerman } else \ 1317197b1a82SChristoph Hellwig *val = __get(ctx); \ 1318104f0cc2SMichael Ellerman \ 1319197b1a82SChristoph Hellwig return 0; \ 1320104f0cc2SMichael Ellerman } \ 1321197b1a82SChristoph Hellwig DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__name, __##__get, __set, __fmt); 1322104f0cc2SMichael Ellerman 1323197b1a82SChristoph Hellwig static int spufs_signal1_type_set(void *data, u64 val) 132467207b96SArnd Bergmann { 132567207b96SArnd Bergmann struct spu_context *ctx = data; 1326c9101bdbSChristoph Hellwig int ret; 132767207b96SArnd Bergmann 1328c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1329c9101bdbSChristoph Hellwig if (ret) 1330c9101bdbSChristoph Hellwig return ret; 13318b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 13328b3d6663SArnd Bergmann spu_release(ctx); 1333197b1a82SChristoph Hellwig 1334197b1a82SChristoph Hellwig return 0; 133567207b96SArnd Bergmann } 133667207b96SArnd Bergmann 1337104f0cc2SMichael Ellerman static u64 spufs_signal1_type_get(struct spu_context *ctx) 1338bf1ab978SDwayne Grant McConnell { 1339bf1ab978SDwayne Grant McConnell return ctx->ops->signal1_type_get(ctx); 1340bf1ab978SDwayne Grant McConnell } 1341104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 1342af8b44e0SJeremy Kerr spufs_signal1_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 1343bf1ab978SDwayne Grant McConnell 134467207b96SArnd Bergmann 1345197b1a82SChristoph Hellwig static int spufs_signal2_type_set(void *data, u64 val) 134667207b96SArnd Bergmann { 134767207b96SArnd Bergmann struct spu_context *ctx = data; 1348c9101bdbSChristoph Hellwig int ret; 134967207b96SArnd Bergmann 1350c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1351c9101bdbSChristoph Hellwig if (ret) 1352c9101bdbSChristoph Hellwig return ret; 13538b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 13548b3d6663SArnd Bergmann spu_release(ctx); 1355197b1a82SChristoph Hellwig 1356197b1a82SChristoph Hellwig return 0; 135767207b96SArnd Bergmann } 135867207b96SArnd Bergmann 1359104f0cc2SMichael Ellerman static u64 spufs_signal2_type_get(struct spu_context *ctx) 1360bf1ab978SDwayne Grant McConnell { 1361bf1ab978SDwayne Grant McConnell return ctx->ops->signal2_type_get(ctx); 1362bf1ab978SDwayne Grant McConnell } 1363104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 1364af8b44e0SJeremy Kerr spufs_signal2_type_set, "%llu\n", SPU_ATTR_ACQUIRE); 136567207b96SArnd Bergmann 136627d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1367b1e2270fSNick Piggin static int 1368b1e2270fSNick Piggin spufs_mss_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 1369d9379c4bSarnd@arndb.de { 137087ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_MSS_MAP_SIZE); 1371d9379c4bSarnd@arndb.de } 1372d9379c4bSarnd@arndb.de 1373d9379c4bSarnd@arndb.de static struct vm_operations_struct spufs_mss_mmap_vmops = { 1374b1e2270fSNick Piggin .fault = spufs_mss_mmap_fault, 1375d9379c4bSarnd@arndb.de }; 1376d9379c4bSarnd@arndb.de 1377d9379c4bSarnd@arndb.de /* 1378d9379c4bSarnd@arndb.de * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 1379d9379c4bSarnd@arndb.de */ 1380d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) 1381d9379c4bSarnd@arndb.de { 1382d9379c4bSarnd@arndb.de if (!(vma->vm_flags & VM_SHARED)) 1383d9379c4bSarnd@arndb.de return -EINVAL; 1384d9379c4bSarnd@arndb.de 138578bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 1386d9379c4bSarnd@arndb.de vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 138723cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 1388d9379c4bSarnd@arndb.de 1389d9379c4bSarnd@arndb.de vma->vm_ops = &spufs_mss_mmap_vmops; 1390d9379c4bSarnd@arndb.de return 0; 1391d9379c4bSarnd@arndb.de } 139227d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 139327d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL 139427d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1395d9379c4bSarnd@arndb.de 1396d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file) 1397d9379c4bSarnd@arndb.de { 1398d9379c4bSarnd@arndb.de struct spufs_inode_info *i = SPUFS_I(inode); 139917e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 1400d9379c4bSarnd@arndb.de 1401d9379c4bSarnd@arndb.de file->private_data = i->i_ctx; 140243c2bbd9SChristoph Hellwig 140347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 140443c2bbd9SChristoph Hellwig if (!i->i_openers++) 140517e0e270SBenjamin Herrenschmidt ctx->mss = inode->i_mapping; 140647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1407d9379c4bSarnd@arndb.de return nonseekable_open(inode, file); 1408d9379c4bSarnd@arndb.de } 1409d9379c4bSarnd@arndb.de 141043c2bbd9SChristoph Hellwig static int 141143c2bbd9SChristoph Hellwig spufs_mss_release(struct inode *inode, struct file *file) 141243c2bbd9SChristoph Hellwig { 141343c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 141443c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 141543c2bbd9SChristoph Hellwig 141647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 141743c2bbd9SChristoph Hellwig if (!--i->i_openers) 141843c2bbd9SChristoph Hellwig ctx->mss = NULL; 141947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 142043c2bbd9SChristoph Hellwig return 0; 142143c2bbd9SChristoph Hellwig } 142243c2bbd9SChristoph Hellwig 14235dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = { 1424d9379c4bSarnd@arndb.de .open = spufs_mss_open, 142543c2bbd9SChristoph Hellwig .release = spufs_mss_release, 1426d9379c4bSarnd@arndb.de .mmap = spufs_mss_mmap, 142727d5bf2aSBenjamin Herrenschmidt }; 142827d5bf2aSBenjamin Herrenschmidt 1429b1e2270fSNick Piggin static int 1430b1e2270fSNick Piggin spufs_psmap_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 143127d5bf2aSBenjamin Herrenschmidt { 143287ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_PS_MAP_SIZE); 143327d5bf2aSBenjamin Herrenschmidt } 143427d5bf2aSBenjamin Herrenschmidt 143527d5bf2aSBenjamin Herrenschmidt static struct vm_operations_struct spufs_psmap_mmap_vmops = { 1436b1e2270fSNick Piggin .fault = spufs_psmap_mmap_fault, 143727d5bf2aSBenjamin Herrenschmidt }; 143827d5bf2aSBenjamin Herrenschmidt 143927d5bf2aSBenjamin Herrenschmidt /* 144027d5bf2aSBenjamin Herrenschmidt * mmap support for full problem state area [0x00000 - 0x1ffff]. 144127d5bf2aSBenjamin Herrenschmidt */ 144227d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) 144327d5bf2aSBenjamin Herrenschmidt { 144427d5bf2aSBenjamin Herrenschmidt if (!(vma->vm_flags & VM_SHARED)) 144527d5bf2aSBenjamin Herrenschmidt return -EINVAL; 144627d5bf2aSBenjamin Herrenschmidt 144778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 144827d5bf2aSBenjamin Herrenschmidt vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 144927d5bf2aSBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 145027d5bf2aSBenjamin Herrenschmidt 145127d5bf2aSBenjamin Herrenschmidt vma->vm_ops = &spufs_psmap_mmap_vmops; 145227d5bf2aSBenjamin Herrenschmidt return 0; 145327d5bf2aSBenjamin Herrenschmidt } 145427d5bf2aSBenjamin Herrenschmidt 145527d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file) 145627d5bf2aSBenjamin Herrenschmidt { 145727d5bf2aSBenjamin Herrenschmidt struct spufs_inode_info *i = SPUFS_I(inode); 145817e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 145927d5bf2aSBenjamin Herrenschmidt 146047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 146127d5bf2aSBenjamin Herrenschmidt file->private_data = i->i_ctx; 146243c2bbd9SChristoph Hellwig if (!i->i_openers++) 146317e0e270SBenjamin Herrenschmidt ctx->psmap = inode->i_mapping; 146447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 146527d5bf2aSBenjamin Herrenschmidt return nonseekable_open(inode, file); 146627d5bf2aSBenjamin Herrenschmidt } 146727d5bf2aSBenjamin Herrenschmidt 146843c2bbd9SChristoph Hellwig static int 146943c2bbd9SChristoph Hellwig spufs_psmap_release(struct inode *inode, struct file *file) 147043c2bbd9SChristoph Hellwig { 147143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 147243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 147343c2bbd9SChristoph Hellwig 147447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 147543c2bbd9SChristoph Hellwig if (!--i->i_openers) 147643c2bbd9SChristoph Hellwig ctx->psmap = NULL; 147747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 147843c2bbd9SChristoph Hellwig return 0; 147943c2bbd9SChristoph Hellwig } 148043c2bbd9SChristoph Hellwig 14815dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = { 148227d5bf2aSBenjamin Herrenschmidt .open = spufs_psmap_open, 148343c2bbd9SChristoph Hellwig .release = spufs_psmap_release, 148427d5bf2aSBenjamin Herrenschmidt .mmap = spufs_psmap_mmap, 1485d9379c4bSarnd@arndb.de }; 1486d9379c4bSarnd@arndb.de 1487d9379c4bSarnd@arndb.de 148827d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1489b1e2270fSNick Piggin static int 1490b1e2270fSNick Piggin spufs_mfc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 14916df10a82SMark Nutter { 149287ff6090SJeremy Kerr return spufs_ps_fault(vma, vmf, 0x3000, SPUFS_MFC_MAP_SIZE); 14936df10a82SMark Nutter } 14946df10a82SMark Nutter 14956df10a82SMark Nutter static struct vm_operations_struct spufs_mfc_mmap_vmops = { 1496b1e2270fSNick Piggin .fault = spufs_mfc_mmap_fault, 14976df10a82SMark Nutter }; 14986df10a82SMark Nutter 14996df10a82SMark Nutter /* 15006df10a82SMark Nutter * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 15016df10a82SMark Nutter */ 15026df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) 15036df10a82SMark Nutter { 15046df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 15056df10a82SMark Nutter return -EINVAL; 15066df10a82SMark Nutter 150778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 15086df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 150923cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 15106df10a82SMark Nutter 15116df10a82SMark Nutter vma->vm_ops = &spufs_mfc_mmap_vmops; 15126df10a82SMark Nutter return 0; 15136df10a82SMark Nutter } 151427d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 151527d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL 151627d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1517a33a7d73SArnd Bergmann 1518a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 1519a33a7d73SArnd Bergmann { 1520a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1521a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 1522a33a7d73SArnd Bergmann 1523a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 1524a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 1525a33a7d73SArnd Bergmann return -EINVAL; 1526a33a7d73SArnd Bergmann 1527a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 1528a33a7d73SArnd Bergmann return -EBUSY; 1529a33a7d73SArnd Bergmann 153047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1531a33a7d73SArnd Bergmann file->private_data = ctx; 153243c2bbd9SChristoph Hellwig if (!i->i_openers++) 153317e0e270SBenjamin Herrenschmidt ctx->mfc = inode->i_mapping; 153447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1535a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 1536a33a7d73SArnd Bergmann } 1537a33a7d73SArnd Bergmann 153843c2bbd9SChristoph Hellwig static int 153943c2bbd9SChristoph Hellwig spufs_mfc_release(struct inode *inode, struct file *file) 154043c2bbd9SChristoph Hellwig { 154143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 154243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 154343c2bbd9SChristoph Hellwig 154447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 154543c2bbd9SChristoph Hellwig if (!--i->i_openers) 154643c2bbd9SChristoph Hellwig ctx->mfc = NULL; 154747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 154843c2bbd9SChristoph Hellwig return 0; 154943c2bbd9SChristoph Hellwig } 155043c2bbd9SChristoph Hellwig 1551a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 1552a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 1553a33a7d73SArnd Bergmann { 1554a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 1555a33a7d73SArnd Bergmann 1556e65c2f6fSLuke Browning if (!ctx) 1557e65c2f6fSLuke Browning return; 1558e65c2f6fSLuke Browning 1559a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 1560a33a7d73SArnd Bergmann 1561e48b1b45SHarvey Harrison pr_debug("%s %s\n", __func__, spu->name); 1562a33a7d73SArnd Bergmann if (ctx->mfc_fasync) { 1563a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1564a33a7d73SArnd Bergmann unsigned int mask; 1565a33a7d73SArnd Bergmann 1566a33a7d73SArnd Bergmann /* no need for spu_acquire in interrupt context */ 1567a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1568a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1569a33a7d73SArnd Bergmann 1570a33a7d73SArnd Bergmann mask = 0; 1571a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1572a33a7d73SArnd Bergmann mask |= POLLOUT; 1573a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1574a33a7d73SArnd Bergmann mask |= POLLIN; 1575a33a7d73SArnd Bergmann 1576a33a7d73SArnd Bergmann kill_fasync(&ctx->mfc_fasync, SIGIO, mask); 1577a33a7d73SArnd Bergmann } 1578a33a7d73SArnd Bergmann } 1579a33a7d73SArnd Bergmann 1580a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 1581a33a7d73SArnd Bergmann { 1582a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 1583a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 1584a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 1585a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 1586a33a7d73SArnd Bergmann if (*status) 1587a33a7d73SArnd Bergmann return 1; 1588a33a7d73SArnd Bergmann 1589a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 1590a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 1591a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1592a33a7d73SArnd Bergmann return 0; 1593a33a7d73SArnd Bergmann } 1594a33a7d73SArnd Bergmann 1595a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 1596a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1597a33a7d73SArnd Bergmann { 1598a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1599a33a7d73SArnd Bergmann int ret = -EINVAL; 1600a33a7d73SArnd Bergmann u32 status; 1601a33a7d73SArnd Bergmann 1602a33a7d73SArnd Bergmann if (size != 4) 1603a33a7d73SArnd Bergmann goto out; 1604a33a7d73SArnd Bergmann 1605c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1606c9101bdbSChristoph Hellwig if (ret) 1607c9101bdbSChristoph Hellwig return ret; 1608c9101bdbSChristoph Hellwig 1609c9101bdbSChristoph Hellwig ret = -EINVAL; 1610a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1611a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 1612a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 1613a33a7d73SArnd Bergmann ret = -EAGAIN; 1614a33a7d73SArnd Bergmann else 1615c9101bdbSChristoph Hellwig /* XXX(hch): shouldn't we clear ret here? */ 1616a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 1617a33a7d73SArnd Bergmann } else { 1618a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1619a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 1620a33a7d73SArnd Bergmann if (ret) 1621a33a7d73SArnd Bergmann goto out; 1622eebead5bSChristoph Hellwig } 1623eebead5bSChristoph Hellwig spu_release(ctx); 1624a33a7d73SArnd Bergmann 1625a33a7d73SArnd Bergmann ret = 4; 1626a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 1627a33a7d73SArnd Bergmann ret = -EFAULT; 1628a33a7d73SArnd Bergmann 1629a33a7d73SArnd Bergmann out: 1630a33a7d73SArnd Bergmann return ret; 1631a33a7d73SArnd Bergmann } 1632a33a7d73SArnd Bergmann 1633a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 1634a33a7d73SArnd Bergmann { 1635a33a7d73SArnd Bergmann pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, 1636a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 1637a33a7d73SArnd Bergmann 1638a33a7d73SArnd Bergmann switch (cmd->cmd) { 1639a33a7d73SArnd Bergmann case MFC_PUT_CMD: 1640a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 1641a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 1642a33a7d73SArnd Bergmann case MFC_GET_CMD: 1643a33a7d73SArnd Bergmann case MFC_GETF_CMD: 1644a33a7d73SArnd Bergmann case MFC_GETB_CMD: 1645a33a7d73SArnd Bergmann break; 1646a33a7d73SArnd Bergmann default: 1647a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 1648a33a7d73SArnd Bergmann return -EIO; 1649a33a7d73SArnd Bergmann } 1650a33a7d73SArnd Bergmann 1651a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 1652a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment, ea %lx lsa %x\n", 1653a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 1654a33a7d73SArnd Bergmann return -EIO; 1655a33a7d73SArnd Bergmann } 1656a33a7d73SArnd Bergmann 1657a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 1658a33a7d73SArnd Bergmann case 1: 1659a33a7d73SArnd Bergmann break; 1660a33a7d73SArnd Bergmann case 2: 1661a33a7d73SArnd Bergmann if (cmd->lsa & 1) 1662a33a7d73SArnd Bergmann goto error; 1663a33a7d73SArnd Bergmann break; 1664a33a7d73SArnd Bergmann case 4: 1665a33a7d73SArnd Bergmann if (cmd->lsa & 3) 1666a33a7d73SArnd Bergmann goto error; 1667a33a7d73SArnd Bergmann break; 1668a33a7d73SArnd Bergmann case 8: 1669a33a7d73SArnd Bergmann if (cmd->lsa & 7) 1670a33a7d73SArnd Bergmann goto error; 1671a33a7d73SArnd Bergmann break; 1672a33a7d73SArnd Bergmann case 0: 1673a33a7d73SArnd Bergmann if (cmd->lsa & 15) 1674a33a7d73SArnd Bergmann goto error; 1675a33a7d73SArnd Bergmann break; 1676a33a7d73SArnd Bergmann error: 1677a33a7d73SArnd Bergmann default: 1678a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 1679a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 1680a33a7d73SArnd Bergmann return -EIO; 1681a33a7d73SArnd Bergmann } 1682a33a7d73SArnd Bergmann 1683a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 1684a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 1685a33a7d73SArnd Bergmann return -EIO; 1686a33a7d73SArnd Bergmann } 1687a33a7d73SArnd Bergmann 1688a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 1689a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 1690a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 1691a33a7d73SArnd Bergmann return -EIO; 1692a33a7d73SArnd Bergmann } 1693a33a7d73SArnd Bergmann 1694a33a7d73SArnd Bergmann if (cmd->class) { 1695a33a7d73SArnd Bergmann /* not supported in this version */ 1696a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 1697a33a7d73SArnd Bergmann return -EIO; 1698a33a7d73SArnd Bergmann } 1699a33a7d73SArnd Bergmann 1700a33a7d73SArnd Bergmann return 0; 1701a33a7d73SArnd Bergmann } 1702a33a7d73SArnd Bergmann 1703a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 1704a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 1705a33a7d73SArnd Bergmann int *error) 1706a33a7d73SArnd Bergmann { 1707a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1708a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 1709a33a7d73SArnd Bergmann /* wait for any tag group to complete 1710a33a7d73SArnd Bergmann so we have space for the new command */ 1711a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1712a33a7d73SArnd Bergmann /* try again, because the queue might be 1713a33a7d73SArnd Bergmann empty again */ 1714a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1715a33a7d73SArnd Bergmann if (*error == -EAGAIN) 1716a33a7d73SArnd Bergmann return 0; 1717a33a7d73SArnd Bergmann } 1718a33a7d73SArnd Bergmann return 1; 1719a33a7d73SArnd Bergmann } 1720a33a7d73SArnd Bergmann 1721a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 1722a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1723a33a7d73SArnd Bergmann { 1724a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1725a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 1726a33a7d73SArnd Bergmann int ret = -EINVAL; 1727a33a7d73SArnd Bergmann 1728a33a7d73SArnd Bergmann if (size != sizeof cmd) 1729a33a7d73SArnd Bergmann goto out; 1730a33a7d73SArnd Bergmann 1731a33a7d73SArnd Bergmann ret = -EFAULT; 1732a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 1733a33a7d73SArnd Bergmann goto out; 1734a33a7d73SArnd Bergmann 1735a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 1736a33a7d73SArnd Bergmann if (ret) 1737a33a7d73SArnd Bergmann goto out; 1738a33a7d73SArnd Bergmann 1739c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1740c9101bdbSChristoph Hellwig if (ret) 1741c9101bdbSChristoph Hellwig goto out; 1742c9101bdbSChristoph Hellwig 174333bfd7a7SArnd Bergmann ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 1744577f8f10SAkinobu Mita if (ret) 1745577f8f10SAkinobu Mita goto out; 1746577f8f10SAkinobu Mita 1747a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1748a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 1749a33a7d73SArnd Bergmann } else { 1750a33a7d73SArnd Bergmann int status; 1751a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1752a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 1753eebead5bSChristoph Hellwig if (ret) 1754eebead5bSChristoph Hellwig goto out; 1755a33a7d73SArnd Bergmann if (status) 1756a33a7d73SArnd Bergmann ret = status; 1757a33a7d73SArnd Bergmann } 1758a33a7d73SArnd Bergmann 1759a33a7d73SArnd Bergmann if (ret) 1760933b0e35SKazunori Asayama goto out_unlock; 1761a33a7d73SArnd Bergmann 1762a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 17633692dc66SMasato Noguchi ret = size; 1764a33a7d73SArnd Bergmann 1765933b0e35SKazunori Asayama out_unlock: 1766933b0e35SKazunori Asayama spu_release(ctx); 1767a33a7d73SArnd Bergmann out: 1768a33a7d73SArnd Bergmann return ret; 1769a33a7d73SArnd Bergmann } 1770a33a7d73SArnd Bergmann 1771a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) 1772a33a7d73SArnd Bergmann { 1773a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1774a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1775a33a7d73SArnd Bergmann unsigned int mask; 1776a33a7d73SArnd Bergmann 1777933b0e35SKazunori Asayama poll_wait(file, &ctx->mfc_wq, wait); 1778933b0e35SKazunori Asayama 1779c9101bdbSChristoph Hellwig /* 1780c9101bdbSChristoph Hellwig * For now keep this uninterruptible and also ignore the rule 1781c9101bdbSChristoph Hellwig * that poll should not sleep. Will be fixed later. 1782c9101bdbSChristoph Hellwig */ 1783c9101bdbSChristoph Hellwig mutex_lock(&ctx->state_mutex); 1784a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 1785a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1786a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1787a33a7d73SArnd Bergmann spu_release(ctx); 1788a33a7d73SArnd Bergmann 1789a33a7d73SArnd Bergmann mask = 0; 1790a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1791a33a7d73SArnd Bergmann mask |= POLLOUT | POLLWRNORM; 1792a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1793a33a7d73SArnd Bergmann mask |= POLLIN | POLLRDNORM; 1794a33a7d73SArnd Bergmann 1795e48b1b45SHarvey Harrison pr_debug("%s: free %d tagstatus %d tagwait %d\n", __func__, 1796a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 1797a33a7d73SArnd Bergmann 1798a33a7d73SArnd Bergmann return mask; 1799a33a7d73SArnd Bergmann } 1800a33a7d73SArnd Bergmann 180173b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id) 1802a33a7d73SArnd Bergmann { 1803a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1804a33a7d73SArnd Bergmann int ret; 1805a33a7d73SArnd Bergmann 1806c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1807c9101bdbSChristoph Hellwig if (ret) 1808eebead5bSChristoph Hellwig goto out; 1809a33a7d73SArnd Bergmann #if 0 1810a33a7d73SArnd Bergmann /* this currently hangs */ 1811a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1812a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 1813a33a7d73SArnd Bergmann if (ret) 1814a33a7d73SArnd Bergmann goto out; 1815a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1816a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 1817eebead5bSChristoph Hellwig if (ret) 1818eebead5bSChristoph Hellwig goto out; 1819a33a7d73SArnd Bergmann #else 1820a33a7d73SArnd Bergmann ret = 0; 1821a33a7d73SArnd Bergmann #endif 1822a33a7d73SArnd Bergmann spu_release(ctx); 1823eebead5bSChristoph Hellwig out: 1824a33a7d73SArnd Bergmann return ret; 1825a33a7d73SArnd Bergmann } 1826a33a7d73SArnd Bergmann 1827a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, 1828a33a7d73SArnd Bergmann int datasync) 1829a33a7d73SArnd Bergmann { 183073b6af8aSAl Viro return spufs_mfc_flush(file, NULL); 1831a33a7d73SArnd Bergmann } 1832a33a7d73SArnd Bergmann 1833a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on) 1834a33a7d73SArnd Bergmann { 1835a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1836a33a7d73SArnd Bergmann 1837a33a7d73SArnd Bergmann return fasync_helper(fd, file, on, &ctx->mfc_fasync); 1838a33a7d73SArnd Bergmann } 1839a33a7d73SArnd Bergmann 18405dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = { 1841a33a7d73SArnd Bergmann .open = spufs_mfc_open, 184243c2bbd9SChristoph Hellwig .release = spufs_mfc_release, 1843a33a7d73SArnd Bergmann .read = spufs_mfc_read, 1844a33a7d73SArnd Bergmann .write = spufs_mfc_write, 1845a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 1846a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 1847a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 1848a33a7d73SArnd Bergmann .fasync = spufs_mfc_fasync, 18496df10a82SMark Nutter .mmap = spufs_mfc_mmap, 1850a33a7d73SArnd Bergmann }; 1851a33a7d73SArnd Bergmann 1852197b1a82SChristoph Hellwig static int spufs_npc_set(void *data, u64 val) 185367207b96SArnd Bergmann { 185467207b96SArnd Bergmann struct spu_context *ctx = data; 1855c9101bdbSChristoph Hellwig int ret; 1856c9101bdbSChristoph Hellwig 1857c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 1858c9101bdbSChristoph Hellwig if (ret) 1859c9101bdbSChristoph Hellwig return ret; 18608b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 18618b3d6663SArnd Bergmann spu_release(ctx); 1862197b1a82SChristoph Hellwig 1863197b1a82SChristoph Hellwig return 0; 186467207b96SArnd Bergmann } 186567207b96SArnd Bergmann 1866104f0cc2SMichael Ellerman static u64 spufs_npc_get(struct spu_context *ctx) 186778810ff6SMichael Ellerman { 186878810ff6SMichael Ellerman return ctx->ops->npc_read(ctx); 186978810ff6SMichael Ellerman } 1870104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, 1871104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE); 187267207b96SArnd Bergmann 1873197b1a82SChristoph Hellwig static int spufs_decr_set(void *data, u64 val) 18748b3d6663SArnd Bergmann { 18758b3d6663SArnd Bergmann struct spu_context *ctx = data; 18768b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1877c9101bdbSChristoph Hellwig int ret; 1878c9101bdbSChristoph Hellwig 1879c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1880c9101bdbSChristoph Hellwig if (ret) 1881c9101bdbSChristoph Hellwig return ret; 18828b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 188327b1ea09SChristoph Hellwig spu_release_saved(ctx); 1884197b1a82SChristoph Hellwig 1885197b1a82SChristoph Hellwig return 0; 18868b3d6663SArnd Bergmann } 18878b3d6663SArnd Bergmann 1888104f0cc2SMichael Ellerman static u64 spufs_decr_get(struct spu_context *ctx) 18898b3d6663SArnd Bergmann { 18908b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1891bf1ab978SDwayne Grant McConnell return lscsa->decr.slot[0]; 1892bf1ab978SDwayne Grant McConnell } 1893104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 1894104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED); 18958b3d6663SArnd Bergmann 1896197b1a82SChristoph Hellwig static int spufs_decr_status_set(void *data, u64 val) 18978b3d6663SArnd Bergmann { 18988b3d6663SArnd Bergmann struct spu_context *ctx = data; 1899c9101bdbSChristoph Hellwig int ret; 1900c9101bdbSChristoph Hellwig 1901c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1902c9101bdbSChristoph Hellwig if (ret) 1903c9101bdbSChristoph Hellwig return ret; 1904d40a01d4SMasato Noguchi if (val) 1905d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING; 1906d40a01d4SMasato Noguchi else 1907d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW &= ~MFC_CNTL_DECREMENTER_RUNNING; 190827b1ea09SChristoph Hellwig spu_release_saved(ctx); 1909197b1a82SChristoph Hellwig 1910197b1a82SChristoph Hellwig return 0; 19118b3d6663SArnd Bergmann } 19128b3d6663SArnd Bergmann 1913104f0cc2SMichael Ellerman static u64 spufs_decr_status_get(struct spu_context *ctx) 19148b3d6663SArnd Bergmann { 1915d40a01d4SMasato Noguchi if (ctx->csa.priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) 1916d40a01d4SMasato Noguchi return SPU_DECR_STATUS_RUNNING; 1917d40a01d4SMasato Noguchi else 1918d40a01d4SMasato Noguchi return 0; 1919bf1ab978SDwayne Grant McConnell } 1920104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 1921104f0cc2SMichael Ellerman spufs_decr_status_set, "0x%llx\n", 1922104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 19238b3d6663SArnd Bergmann 1924197b1a82SChristoph Hellwig static int spufs_event_mask_set(void *data, u64 val) 19258b3d6663SArnd Bergmann { 19268b3d6663SArnd Bergmann struct spu_context *ctx = data; 19278b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1928c9101bdbSChristoph Hellwig int ret; 1929c9101bdbSChristoph Hellwig 1930c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1931c9101bdbSChristoph Hellwig if (ret) 1932c9101bdbSChristoph Hellwig return ret; 19338b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 193427b1ea09SChristoph Hellwig spu_release_saved(ctx); 1935197b1a82SChristoph Hellwig 1936197b1a82SChristoph Hellwig return 0; 19378b3d6663SArnd Bergmann } 19388b3d6663SArnd Bergmann 1939104f0cc2SMichael Ellerman static u64 spufs_event_mask_get(struct spu_context *ctx) 19408b3d6663SArnd Bergmann { 19418b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1942bf1ab978SDwayne Grant McConnell return lscsa->event_mask.slot[0]; 1943bf1ab978SDwayne Grant McConnell } 1944bf1ab978SDwayne Grant McConnell 1945104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 1946104f0cc2SMichael Ellerman spufs_event_mask_set, "0x%llx\n", 1947104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 19488b3d6663SArnd Bergmann 1949104f0cc2SMichael Ellerman static u64 spufs_event_status_get(struct spu_context *ctx) 1950b9e3bd77SDwayne Grant McConnell { 1951b9e3bd77SDwayne Grant McConnell struct spu_state *state = &ctx->csa; 1952b9e3bd77SDwayne Grant McConnell u64 stat; 1953b9e3bd77SDwayne Grant McConnell stat = state->spu_chnlcnt_RW[0]; 1954b9e3bd77SDwayne Grant McConnell if (stat) 1955bf1ab978SDwayne Grant McConnell return state->spu_chnldata_RW[0]; 1956bf1ab978SDwayne Grant McConnell return 0; 1957bf1ab978SDwayne Grant McConnell } 1958104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get, 1959104f0cc2SMichael Ellerman NULL, "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 1960b9e3bd77SDwayne Grant McConnell 1961197b1a82SChristoph Hellwig static int spufs_srr0_set(void *data, u64 val) 19628b3d6663SArnd Bergmann { 19638b3d6663SArnd Bergmann struct spu_context *ctx = data; 19648b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1965c9101bdbSChristoph Hellwig int ret; 1966c9101bdbSChristoph Hellwig 1967c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 1968c9101bdbSChristoph Hellwig if (ret) 1969c9101bdbSChristoph Hellwig return ret; 19708b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 197127b1ea09SChristoph Hellwig spu_release_saved(ctx); 1972197b1a82SChristoph Hellwig 1973197b1a82SChristoph Hellwig return 0; 19748b3d6663SArnd Bergmann } 19758b3d6663SArnd Bergmann 1976104f0cc2SMichael Ellerman static u64 spufs_srr0_get(struct spu_context *ctx) 19778b3d6663SArnd Bergmann { 19788b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1979104f0cc2SMichael Ellerman return lscsa->srr0.slot[0]; 19808b3d6663SArnd Bergmann } 1981104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 1982104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 19838b3d6663SArnd Bergmann 1984104f0cc2SMichael Ellerman static u64 spufs_id_get(struct spu_context *ctx) 19857b1a7014Sarnd@arndb.de { 19867b1a7014Sarnd@arndb.de u64 num; 19877b1a7014Sarnd@arndb.de 19887b1a7014Sarnd@arndb.de if (ctx->state == SPU_STATE_RUNNABLE) 19897b1a7014Sarnd@arndb.de num = ctx->spu->number; 19907b1a7014Sarnd@arndb.de else 19917b1a7014Sarnd@arndb.de num = (unsigned int)-1; 19927b1a7014Sarnd@arndb.de 19937b1a7014Sarnd@arndb.de return num; 19947b1a7014Sarnd@arndb.de } 1995104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n", 1996104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE) 19977b1a7014Sarnd@arndb.de 1998104f0cc2SMichael Ellerman static u64 spufs_object_id_get(struct spu_context *ctx) 1999bf1ab978SDwayne Grant McConnell { 2000bf1ab978SDwayne Grant McConnell /* FIXME: Should there really be no locking here? */ 2001104f0cc2SMichael Ellerman return ctx->object_id; 2002bf1ab978SDwayne Grant McConnell } 2003bf1ab978SDwayne Grant McConnell 2004197b1a82SChristoph Hellwig static int spufs_object_id_set(void *data, u64 id) 200586767277SArnd Bergmann { 200686767277SArnd Bergmann struct spu_context *ctx = data; 200786767277SArnd Bergmann ctx->object_id = id; 2008197b1a82SChristoph Hellwig 2009197b1a82SChristoph Hellwig return 0; 201086767277SArnd Bergmann } 201186767277SArnd Bergmann 2012104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, 2013104f0cc2SMichael Ellerman spufs_object_id_set, "0x%llx\n", SPU_ATTR_NOACQUIRE); 201486767277SArnd Bergmann 2015104f0cc2SMichael Ellerman static u64 spufs_lslr_get(struct spu_context *ctx) 2016bf1ab978SDwayne Grant McConnell { 2017bf1ab978SDwayne Grant McConnell return ctx->csa.priv2.spu_lslr_RW; 2018bf1ab978SDwayne Grant McConnell } 2019104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n", 2020104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 2021b9e3bd77SDwayne Grant McConnell 2022b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file) 2023b9e3bd77SDwayne Grant McConnell { 2024b9e3bd77SDwayne Grant McConnell struct spufs_inode_info *i = SPUFS_I(inode); 2025b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = i->i_ctx; 2026b9e3bd77SDwayne Grant McConnell file->private_data = ctx; 2027b9e3bd77SDwayne Grant McConnell return 0; 2028b9e3bd77SDwayne Grant McConnell } 2029b9e3bd77SDwayne Grant McConnell 2030cbe709c1SBenjamin Herrenschmidt static int spufs_caps_show(struct seq_file *s, void *private) 2031cbe709c1SBenjamin Herrenschmidt { 2032cbe709c1SBenjamin Herrenschmidt struct spu_context *ctx = s->private; 2033cbe709c1SBenjamin Herrenschmidt 2034cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_NOSCHED)) 2035cbe709c1SBenjamin Herrenschmidt seq_puts(s, "sched\n"); 2036cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_ISOLATE)) 2037cbe709c1SBenjamin Herrenschmidt seq_puts(s, "step\n"); 2038cbe709c1SBenjamin Herrenschmidt return 0; 2039cbe709c1SBenjamin Herrenschmidt } 2040cbe709c1SBenjamin Herrenschmidt 2041cbe709c1SBenjamin Herrenschmidt static int spufs_caps_open(struct inode *inode, struct file *file) 2042cbe709c1SBenjamin Herrenschmidt { 2043cbe709c1SBenjamin Herrenschmidt return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); 2044cbe709c1SBenjamin Herrenschmidt } 2045cbe709c1SBenjamin Herrenschmidt 2046cbe709c1SBenjamin Herrenschmidt static const struct file_operations spufs_caps_fops = { 2047cbe709c1SBenjamin Herrenschmidt .open = spufs_caps_open, 2048cbe709c1SBenjamin Herrenschmidt .read = seq_read, 2049cbe709c1SBenjamin Herrenschmidt .llseek = seq_lseek, 2050cbe709c1SBenjamin Herrenschmidt .release = single_release, 2051cbe709c1SBenjamin Herrenschmidt }; 2052cbe709c1SBenjamin Herrenschmidt 2053bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, 2054bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2055bf1ab978SDwayne Grant McConnell { 2056bf1ab978SDwayne Grant McConnell u32 data; 2057bf1ab978SDwayne Grant McConnell 2058cbea9238SJeremy Kerr /* EOF if there's no entry in the mbox */ 2059cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0x0000ff)) 2060cbea9238SJeremy Kerr return 0; 2061cbea9238SJeremy Kerr 2062bf1ab978SDwayne Grant McConnell data = ctx->csa.prob.pu_mb_R; 2063bf1ab978SDwayne Grant McConnell 2064bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2065bf1ab978SDwayne Grant McConnell } 2066bf1ab978SDwayne Grant McConnell 206769a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf, 206869a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 206969a2f00cSDwayne Grant McConnell { 2070bf1ab978SDwayne Grant McConnell int ret; 207169a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 207269a2f00cSDwayne Grant McConnell 207369a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 207469a2f00cSDwayne Grant McConnell return -EFAULT; 207569a2f00cSDwayne Grant McConnell 2076c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2077c9101bdbSChristoph Hellwig if (ret) 2078c9101bdbSChristoph Hellwig return ret; 207969a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2080bf1ab978SDwayne Grant McConnell ret = __spufs_mbox_info_read(ctx, buf, len, pos); 208169a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 208227b1ea09SChristoph Hellwig spu_release_saved(ctx); 208369a2f00cSDwayne Grant McConnell 2084bf1ab978SDwayne Grant McConnell return ret; 208569a2f00cSDwayne Grant McConnell } 208669a2f00cSDwayne Grant McConnell 20875dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = { 208869a2f00cSDwayne Grant McConnell .open = spufs_info_open, 208969a2f00cSDwayne Grant McConnell .read = spufs_mbox_info_read, 209069a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 209169a2f00cSDwayne Grant McConnell }; 209269a2f00cSDwayne Grant McConnell 2093bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx, 2094bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2095bf1ab978SDwayne Grant McConnell { 2096bf1ab978SDwayne Grant McConnell u32 data; 2097bf1ab978SDwayne Grant McConnell 2098cbea9238SJeremy Kerr /* EOF if there's no entry in the ibox */ 2099cbea9238SJeremy Kerr if (!(ctx->csa.prob.mb_stat_R & 0xff0000)) 2100cbea9238SJeremy Kerr return 0; 2101cbea9238SJeremy Kerr 2102bf1ab978SDwayne Grant McConnell data = ctx->csa.priv2.puint_mb_R; 2103bf1ab978SDwayne Grant McConnell 2104bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 2105bf1ab978SDwayne Grant McConnell } 2106bf1ab978SDwayne Grant McConnell 210769a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf, 210869a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 210969a2f00cSDwayne Grant McConnell { 211069a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2111bf1ab978SDwayne Grant McConnell int ret; 211269a2f00cSDwayne Grant McConnell 211369a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 211469a2f00cSDwayne Grant McConnell return -EFAULT; 211569a2f00cSDwayne Grant McConnell 2116c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2117c9101bdbSChristoph Hellwig if (ret) 2118c9101bdbSChristoph Hellwig return ret; 211969a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2120bf1ab978SDwayne Grant McConnell ret = __spufs_ibox_info_read(ctx, buf, len, pos); 212169a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 212227b1ea09SChristoph Hellwig spu_release_saved(ctx); 212369a2f00cSDwayne Grant McConnell 2124bf1ab978SDwayne Grant McConnell return ret; 212569a2f00cSDwayne Grant McConnell } 212669a2f00cSDwayne Grant McConnell 21275dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = { 212869a2f00cSDwayne Grant McConnell .open = spufs_info_open, 212969a2f00cSDwayne Grant McConnell .read = spufs_ibox_info_read, 213069a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 213169a2f00cSDwayne Grant McConnell }; 213269a2f00cSDwayne Grant McConnell 2133bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx, 2134bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2135bf1ab978SDwayne Grant McConnell { 2136bf1ab978SDwayne Grant McConnell int i, cnt; 2137bf1ab978SDwayne Grant McConnell u32 data[4]; 2138bf1ab978SDwayne Grant McConnell u32 wbox_stat; 2139bf1ab978SDwayne Grant McConnell 2140bf1ab978SDwayne Grant McConnell wbox_stat = ctx->csa.prob.mb_stat_R; 2141bf1ab978SDwayne Grant McConnell cnt = 4 - ((wbox_stat & 0x00ff00) >> 8); 2142bf1ab978SDwayne Grant McConnell for (i = 0; i < cnt; i++) { 2143bf1ab978SDwayne Grant McConnell data[i] = ctx->csa.spu_mailbox_data[i]; 2144bf1ab978SDwayne Grant McConnell } 2145bf1ab978SDwayne Grant McConnell 2146bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, 2147bf1ab978SDwayne Grant McConnell cnt * sizeof(u32)); 2148bf1ab978SDwayne Grant McConnell } 2149bf1ab978SDwayne Grant McConnell 215069a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf, 215169a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 215269a2f00cSDwayne Grant McConnell { 215369a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2154bf1ab978SDwayne Grant McConnell int ret; 215569a2f00cSDwayne Grant McConnell 215669a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 215769a2f00cSDwayne Grant McConnell return -EFAULT; 215869a2f00cSDwayne Grant McConnell 2159c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2160c9101bdbSChristoph Hellwig if (ret) 2161c9101bdbSChristoph Hellwig return ret; 216269a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2163bf1ab978SDwayne Grant McConnell ret = __spufs_wbox_info_read(ctx, buf, len, pos); 216469a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 216527b1ea09SChristoph Hellwig spu_release_saved(ctx); 216669a2f00cSDwayne Grant McConnell 2167bf1ab978SDwayne Grant McConnell return ret; 216869a2f00cSDwayne Grant McConnell } 216969a2f00cSDwayne Grant McConnell 21705dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = { 217169a2f00cSDwayne Grant McConnell .open = spufs_info_open, 217269a2f00cSDwayne Grant McConnell .read = spufs_wbox_info_read, 217369a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 217469a2f00cSDwayne Grant McConnell }; 217569a2f00cSDwayne Grant McConnell 2176bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx, 2177bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2178b9e3bd77SDwayne Grant McConnell { 2179b9e3bd77SDwayne Grant McConnell struct spu_dma_info info; 2180b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *spuqp; 2181b9e3bd77SDwayne Grant McConnell int i; 2182b9e3bd77SDwayne Grant McConnell 2183b9e3bd77SDwayne Grant McConnell info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW; 2184b9e3bd77SDwayne Grant McConnell info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0]; 2185b9e3bd77SDwayne Grant McConnell info.dma_info_status = ctx->csa.spu_chnldata_RW[24]; 2186b9e3bd77SDwayne Grant McConnell info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25]; 2187b9e3bd77SDwayne Grant McConnell info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27]; 2188b9e3bd77SDwayne Grant McConnell for (i = 0; i < 16; i++) { 2189b9e3bd77SDwayne Grant McConnell qp = &info.dma_info_command_data[i]; 2190b9e3bd77SDwayne Grant McConnell spuqp = &ctx->csa.priv2.spuq[i]; 2191b9e3bd77SDwayne Grant McConnell 2192b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW; 2193b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW; 2194b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW; 2195b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW; 2196b9e3bd77SDwayne Grant McConnell } 2197b9e3bd77SDwayne Grant McConnell 2198b9e3bd77SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2199b9e3bd77SDwayne Grant McConnell sizeof info); 2200b9e3bd77SDwayne Grant McConnell } 2201b9e3bd77SDwayne Grant McConnell 2202bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, 2203bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2204bf1ab978SDwayne Grant McConnell { 2205bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2206bf1ab978SDwayne Grant McConnell int ret; 2207bf1ab978SDwayne Grant McConnell 2208bf1ab978SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2209bf1ab978SDwayne Grant McConnell return -EFAULT; 2210bf1ab978SDwayne Grant McConnell 2211c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2212c9101bdbSChristoph Hellwig if (ret) 2213c9101bdbSChristoph Hellwig return ret; 2214bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2215bf1ab978SDwayne Grant McConnell ret = __spufs_dma_info_read(ctx, buf, len, pos); 2216bf1ab978SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 221727b1ea09SChristoph Hellwig spu_release_saved(ctx); 2218bf1ab978SDwayne Grant McConnell 2219bf1ab978SDwayne Grant McConnell return ret; 2220bf1ab978SDwayne Grant McConnell } 2221bf1ab978SDwayne Grant McConnell 22225dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = { 2223b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2224b9e3bd77SDwayne Grant McConnell .read = spufs_dma_info_read, 2225b9e3bd77SDwayne Grant McConnell }; 2226b9e3bd77SDwayne Grant McConnell 2227bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, 2228bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2229b9e3bd77SDwayne Grant McConnell { 2230b9e3bd77SDwayne Grant McConnell struct spu_proxydma_info info; 2231b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *puqp; 2232bf1ab978SDwayne Grant McConnell int ret = sizeof info; 2233b9e3bd77SDwayne Grant McConnell int i; 2234b9e3bd77SDwayne Grant McConnell 2235b9e3bd77SDwayne Grant McConnell if (len < ret) 2236b9e3bd77SDwayne Grant McConnell return -EINVAL; 2237b9e3bd77SDwayne Grant McConnell 2238b9e3bd77SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2239b9e3bd77SDwayne Grant McConnell return -EFAULT; 2240b9e3bd77SDwayne Grant McConnell 2241b9e3bd77SDwayne Grant McConnell info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW; 2242b9e3bd77SDwayne Grant McConnell info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW; 2243b9e3bd77SDwayne Grant McConnell info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R; 2244b9e3bd77SDwayne Grant McConnell for (i = 0; i < 8; i++) { 2245b9e3bd77SDwayne Grant McConnell qp = &info.proxydma_info_command_data[i]; 2246b9e3bd77SDwayne Grant McConnell puqp = &ctx->csa.priv2.puq[i]; 2247b9e3bd77SDwayne Grant McConnell 2248b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW; 2249b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW; 2250b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW; 2251b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW; 2252b9e3bd77SDwayne Grant McConnell } 2253bf1ab978SDwayne Grant McConnell 2254bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2255bf1ab978SDwayne Grant McConnell sizeof info); 2256bf1ab978SDwayne Grant McConnell } 2257bf1ab978SDwayne Grant McConnell 2258bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, 2259bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2260bf1ab978SDwayne Grant McConnell { 2261bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2262bf1ab978SDwayne Grant McConnell int ret; 2263bf1ab978SDwayne Grant McConnell 2264c9101bdbSChristoph Hellwig ret = spu_acquire_saved(ctx); 2265c9101bdbSChristoph Hellwig if (ret) 2266c9101bdbSChristoph Hellwig return ret; 2267bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2268bf1ab978SDwayne Grant McConnell ret = __spufs_proxydma_info_read(ctx, buf, len, pos); 2269b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 227027b1ea09SChristoph Hellwig spu_release_saved(ctx); 2271b9e3bd77SDwayne Grant McConnell 2272b9e3bd77SDwayne Grant McConnell return ret; 2273b9e3bd77SDwayne Grant McConnell } 2274b9e3bd77SDwayne Grant McConnell 22755dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = { 2276b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2277b9e3bd77SDwayne Grant McConnell .read = spufs_proxydma_info_read, 2278b9e3bd77SDwayne Grant McConnell }; 2279b9e3bd77SDwayne Grant McConnell 2280476273adSChristoph Hellwig static int spufs_show_tid(struct seq_file *s, void *private) 2281476273adSChristoph Hellwig { 2282476273adSChristoph Hellwig struct spu_context *ctx = s->private; 2283476273adSChristoph Hellwig 2284476273adSChristoph Hellwig seq_printf(s, "%d\n", ctx->tid); 2285476273adSChristoph Hellwig return 0; 2286476273adSChristoph Hellwig } 2287476273adSChristoph Hellwig 2288476273adSChristoph Hellwig static int spufs_tid_open(struct inode *inode, struct file *file) 2289476273adSChristoph Hellwig { 2290476273adSChristoph Hellwig return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); 2291476273adSChristoph Hellwig } 2292476273adSChristoph Hellwig 2293476273adSChristoph Hellwig static const struct file_operations spufs_tid_fops = { 2294476273adSChristoph Hellwig .open = spufs_tid_open, 2295476273adSChristoph Hellwig .read = seq_read, 2296476273adSChristoph Hellwig .llseek = seq_lseek, 2297476273adSChristoph Hellwig .release = single_release, 2298476273adSChristoph Hellwig }; 2299476273adSChristoph Hellwig 2300e9f8a0b6SChristoph Hellwig static const char *ctx_state_names[] = { 2301e9f8a0b6SChristoph Hellwig "user", "system", "iowait", "loaded" 2302e9f8a0b6SChristoph Hellwig }; 2303e9f8a0b6SChristoph Hellwig 2304e9f8a0b6SChristoph Hellwig static unsigned long long spufs_acct_time(struct spu_context *ctx, 230527ec41d3SAndre Detsch enum spu_utilization_state state) 2306e9f8a0b6SChristoph Hellwig { 230727ec41d3SAndre Detsch struct timespec ts; 230827ec41d3SAndre Detsch unsigned long long time = ctx->stats.times[state]; 2309e9f8a0b6SChristoph Hellwig 231027ec41d3SAndre Detsch /* 231127ec41d3SAndre Detsch * In general, utilization statistics are updated by the controlling 231227ec41d3SAndre Detsch * thread as the spu context moves through various well defined 231327ec41d3SAndre Detsch * state transitions, but if the context is lazily loaded its 231427ec41d3SAndre Detsch * utilization statistics are not updated as the controlling thread 231527ec41d3SAndre Detsch * is not tightly coupled with the execution of the spu context. We 231627ec41d3SAndre Detsch * calculate and apply the time delta from the last recorded state 231727ec41d3SAndre Detsch * of the spu context. 231827ec41d3SAndre Detsch */ 231927ec41d3SAndre Detsch if (ctx->spu && ctx->stats.util_state == state) { 232027ec41d3SAndre Detsch ktime_get_ts(&ts); 232127ec41d3SAndre Detsch time += timespec_to_ns(&ts) - ctx->stats.tstamp; 232227ec41d3SAndre Detsch } 2323e9f8a0b6SChristoph Hellwig 232427ec41d3SAndre Detsch return time / NSEC_PER_MSEC; 2325e9f8a0b6SChristoph Hellwig } 2326e9f8a0b6SChristoph Hellwig 2327e9f8a0b6SChristoph Hellwig static unsigned long long spufs_slb_flts(struct spu_context *ctx) 2328e9f8a0b6SChristoph Hellwig { 2329e9f8a0b6SChristoph Hellwig unsigned long long slb_flts = ctx->stats.slb_flt; 2330e9f8a0b6SChristoph Hellwig 2331e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2332e9f8a0b6SChristoph Hellwig slb_flts += (ctx->spu->stats.slb_flt - 2333e9f8a0b6SChristoph Hellwig ctx->stats.slb_flt_base); 2334e9f8a0b6SChristoph Hellwig } 2335e9f8a0b6SChristoph Hellwig 2336e9f8a0b6SChristoph Hellwig return slb_flts; 2337e9f8a0b6SChristoph Hellwig } 2338e9f8a0b6SChristoph Hellwig 2339e9f8a0b6SChristoph Hellwig static unsigned long long spufs_class2_intrs(struct spu_context *ctx) 2340e9f8a0b6SChristoph Hellwig { 2341e9f8a0b6SChristoph Hellwig unsigned long long class2_intrs = ctx->stats.class2_intr; 2342e9f8a0b6SChristoph Hellwig 2343e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2344e9f8a0b6SChristoph Hellwig class2_intrs += (ctx->spu->stats.class2_intr - 2345e9f8a0b6SChristoph Hellwig ctx->stats.class2_intr_base); 2346e9f8a0b6SChristoph Hellwig } 2347e9f8a0b6SChristoph Hellwig 2348e9f8a0b6SChristoph Hellwig return class2_intrs; 2349e9f8a0b6SChristoph Hellwig } 2350e9f8a0b6SChristoph Hellwig 2351e9f8a0b6SChristoph Hellwig 2352e9f8a0b6SChristoph Hellwig static int spufs_show_stat(struct seq_file *s, void *private) 2353e9f8a0b6SChristoph Hellwig { 2354e9f8a0b6SChristoph Hellwig struct spu_context *ctx = s->private; 2355c9101bdbSChristoph Hellwig int ret; 2356e9f8a0b6SChristoph Hellwig 2357c9101bdbSChristoph Hellwig ret = spu_acquire(ctx); 2358c9101bdbSChristoph Hellwig if (ret) 2359c9101bdbSChristoph Hellwig return ret; 2360c9101bdbSChristoph Hellwig 2361e9f8a0b6SChristoph Hellwig seq_printf(s, "%s %llu %llu %llu %llu " 2362e9f8a0b6SChristoph Hellwig "%llu %llu %llu %llu %llu %llu %llu %llu\n", 236327ec41d3SAndre Detsch ctx_state_names[ctx->stats.util_state], 236427ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_USER), 236527ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_SYSTEM), 236627ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IOWAIT), 236727ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED), 2368e9f8a0b6SChristoph Hellwig ctx->stats.vol_ctx_switch, 2369e9f8a0b6SChristoph Hellwig ctx->stats.invol_ctx_switch, 2370e9f8a0b6SChristoph Hellwig spufs_slb_flts(ctx), 2371e9f8a0b6SChristoph Hellwig ctx->stats.hash_flt, 2372e9f8a0b6SChristoph Hellwig ctx->stats.min_flt, 2373e9f8a0b6SChristoph Hellwig ctx->stats.maj_flt, 2374e9f8a0b6SChristoph Hellwig spufs_class2_intrs(ctx), 2375e9f8a0b6SChristoph Hellwig ctx->stats.libassist); 2376e9f8a0b6SChristoph Hellwig spu_release(ctx); 2377e9f8a0b6SChristoph Hellwig return 0; 2378e9f8a0b6SChristoph Hellwig } 2379e9f8a0b6SChristoph Hellwig 2380e9f8a0b6SChristoph Hellwig static int spufs_stat_open(struct inode *inode, struct file *file) 2381e9f8a0b6SChristoph Hellwig { 2382e9f8a0b6SChristoph Hellwig return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); 2383e9f8a0b6SChristoph Hellwig } 2384e9f8a0b6SChristoph Hellwig 2385e9f8a0b6SChristoph Hellwig static const struct file_operations spufs_stat_fops = { 2386e9f8a0b6SChristoph Hellwig .open = spufs_stat_open, 2387e9f8a0b6SChristoph Hellwig .read = seq_read, 2388e9f8a0b6SChristoph Hellwig .llseek = seq_lseek, 2389e9f8a0b6SChristoph Hellwig .release = single_release, 2390e9f8a0b6SChristoph Hellwig }; 2391e9f8a0b6SChristoph Hellwig 23925158e9b5SChristoph Hellwig static inline int spufs_switch_log_used(struct spu_context *ctx) 23935158e9b5SChristoph Hellwig { 23945158e9b5SChristoph Hellwig return (ctx->switch_log->head - ctx->switch_log->tail) % 23955158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 23965158e9b5SChristoph Hellwig } 23975158e9b5SChristoph Hellwig 23985158e9b5SChristoph Hellwig static inline int spufs_switch_log_avail(struct spu_context *ctx) 23995158e9b5SChristoph Hellwig { 24005158e9b5SChristoph Hellwig return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); 24015158e9b5SChristoph Hellwig } 24025158e9b5SChristoph Hellwig 24035158e9b5SChristoph Hellwig static int spufs_switch_log_open(struct inode *inode, struct file *file) 24045158e9b5SChristoph Hellwig { 24055158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 24065158e9b5SChristoph Hellwig 24075158e9b5SChristoph Hellwig /* 24085158e9b5SChristoph Hellwig * We (ab-)use the mapping_lock here because it serves the similar 24095158e9b5SChristoph Hellwig * purpose for synchronizing open/close elsewhere. Maybe it should 24105158e9b5SChristoph Hellwig * be renamed eventually. 24115158e9b5SChristoph Hellwig */ 24125158e9b5SChristoph Hellwig mutex_lock(&ctx->mapping_lock); 24135158e9b5SChristoph Hellwig if (ctx->switch_log) { 24145158e9b5SChristoph Hellwig spin_lock(&ctx->switch_log->lock); 24155158e9b5SChristoph Hellwig ctx->switch_log->head = 0; 24165158e9b5SChristoph Hellwig ctx->switch_log->tail = 0; 24175158e9b5SChristoph Hellwig spin_unlock(&ctx->switch_log->lock); 24185158e9b5SChristoph Hellwig } else { 24195158e9b5SChristoph Hellwig /* 24205158e9b5SChristoph Hellwig * We allocate the switch log data structures on first open. 24215158e9b5SChristoph Hellwig * They will never be free because we assume a context will 24225158e9b5SChristoph Hellwig * be traced until it goes away. 24235158e9b5SChristoph Hellwig */ 24245158e9b5SChristoph Hellwig ctx->switch_log = kzalloc(sizeof(struct switch_log) + 24255158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry), 24265158e9b5SChristoph Hellwig GFP_KERNEL); 24275158e9b5SChristoph Hellwig if (!ctx->switch_log) 24285158e9b5SChristoph Hellwig goto out; 24295158e9b5SChristoph Hellwig spin_lock_init(&ctx->switch_log->lock); 24305158e9b5SChristoph Hellwig init_waitqueue_head(&ctx->switch_log->wait); 24315158e9b5SChristoph Hellwig } 24325158e9b5SChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 24335158e9b5SChristoph Hellwig 24345158e9b5SChristoph Hellwig return 0; 24355158e9b5SChristoph Hellwig out: 24365158e9b5SChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 24375158e9b5SChristoph Hellwig return -ENOMEM; 24385158e9b5SChristoph Hellwig } 24395158e9b5SChristoph Hellwig 24405158e9b5SChristoph Hellwig static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) 24415158e9b5SChristoph Hellwig { 24425158e9b5SChristoph Hellwig struct switch_log_entry *p; 24435158e9b5SChristoph Hellwig 24445158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE; 24455158e9b5SChristoph Hellwig 24465158e9b5SChristoph Hellwig return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n", 24475158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_sec, 24485158e9b5SChristoph Hellwig (unsigned int) p->tstamp.tv_nsec, 24495158e9b5SChristoph Hellwig p->spu_id, 24505158e9b5SChristoph Hellwig (unsigned int) p->type, 24515158e9b5SChristoph Hellwig (unsigned int) p->val, 24525158e9b5SChristoph Hellwig (unsigned long long) p->timebase); 24535158e9b5SChristoph Hellwig } 24545158e9b5SChristoph Hellwig 24555158e9b5SChristoph Hellwig static ssize_t spufs_switch_log_read(struct file *file, char __user *buf, 24565158e9b5SChristoph Hellwig size_t len, loff_t *ppos) 24575158e9b5SChristoph Hellwig { 24585158e9b5SChristoph Hellwig struct inode *inode = file->f_path.dentry->d_inode; 24595158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 24605158e9b5SChristoph Hellwig int error = 0, cnt = 0; 24615158e9b5SChristoph Hellwig 24625158e9b5SChristoph Hellwig if (!buf || len < 0) 24635158e9b5SChristoph Hellwig return -EINVAL; 24645158e9b5SChristoph Hellwig 24655158e9b5SChristoph Hellwig while (cnt < len) { 24665158e9b5SChristoph Hellwig char tbuf[128]; 24675158e9b5SChristoph Hellwig int width; 24685158e9b5SChristoph Hellwig 24695158e9b5SChristoph Hellwig if (file->f_flags & O_NONBLOCK) { 24705158e9b5SChristoph Hellwig if (spufs_switch_log_used(ctx) <= 0) 24715158e9b5SChristoph Hellwig return cnt ? cnt : -EAGAIN; 24725158e9b5SChristoph Hellwig } else { 24735158e9b5SChristoph Hellwig /* Wait for data in buffer */ 24745158e9b5SChristoph Hellwig error = wait_event_interruptible(ctx->switch_log->wait, 24755158e9b5SChristoph Hellwig spufs_switch_log_used(ctx) > 0); 24765158e9b5SChristoph Hellwig if (error) 24775158e9b5SChristoph Hellwig break; 24785158e9b5SChristoph Hellwig } 24795158e9b5SChristoph Hellwig 24805158e9b5SChristoph Hellwig spin_lock(&ctx->switch_log->lock); 24815158e9b5SChristoph Hellwig if (ctx->switch_log->head == ctx->switch_log->tail) { 24825158e9b5SChristoph Hellwig /* multiple readers race? */ 24835158e9b5SChristoph Hellwig spin_unlock(&ctx->switch_log->lock); 24845158e9b5SChristoph Hellwig continue; 24855158e9b5SChristoph Hellwig } 24865158e9b5SChristoph Hellwig 24875158e9b5SChristoph Hellwig width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); 24885158e9b5SChristoph Hellwig if (width < len) { 24895158e9b5SChristoph Hellwig ctx->switch_log->tail = 24905158e9b5SChristoph Hellwig (ctx->switch_log->tail + 1) % 24915158e9b5SChristoph Hellwig SWITCH_LOG_BUFSIZE; 24925158e9b5SChristoph Hellwig } 24935158e9b5SChristoph Hellwig 24945158e9b5SChristoph Hellwig spin_unlock(&ctx->switch_log->lock); 24955158e9b5SChristoph Hellwig 24965158e9b5SChristoph Hellwig /* 24975158e9b5SChristoph Hellwig * If the record is greater than space available return 24985158e9b5SChristoph Hellwig * partial buffer (so far) 24995158e9b5SChristoph Hellwig */ 25005158e9b5SChristoph Hellwig if (width >= len) 25015158e9b5SChristoph Hellwig break; 25025158e9b5SChristoph Hellwig 25035158e9b5SChristoph Hellwig error = copy_to_user(buf + cnt, tbuf, width); 25045158e9b5SChristoph Hellwig if (error) 25055158e9b5SChristoph Hellwig break; 25065158e9b5SChristoph Hellwig cnt += width; 25075158e9b5SChristoph Hellwig } 25085158e9b5SChristoph Hellwig 25095158e9b5SChristoph Hellwig return cnt == 0 ? error : cnt; 25105158e9b5SChristoph Hellwig } 25115158e9b5SChristoph Hellwig 25125158e9b5SChristoph Hellwig static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait) 25135158e9b5SChristoph Hellwig { 25145158e9b5SChristoph Hellwig struct inode *inode = file->f_path.dentry->d_inode; 25155158e9b5SChristoph Hellwig struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 25165158e9b5SChristoph Hellwig unsigned int mask = 0; 25175158e9b5SChristoph Hellwig 25185158e9b5SChristoph Hellwig poll_wait(file, &ctx->switch_log->wait, wait); 25195158e9b5SChristoph Hellwig 25205158e9b5SChristoph Hellwig if (spufs_switch_log_used(ctx) > 0) 25215158e9b5SChristoph Hellwig mask |= POLLIN; 25225158e9b5SChristoph Hellwig 25235158e9b5SChristoph Hellwig return mask; 25245158e9b5SChristoph Hellwig } 25255158e9b5SChristoph Hellwig 25265158e9b5SChristoph Hellwig static const struct file_operations spufs_switch_log_fops = { 25275158e9b5SChristoph Hellwig .owner = THIS_MODULE, 25285158e9b5SChristoph Hellwig .open = spufs_switch_log_open, 25295158e9b5SChristoph Hellwig .read = spufs_switch_log_read, 25305158e9b5SChristoph Hellwig .poll = spufs_switch_log_poll, 25315158e9b5SChristoph Hellwig }; 25325158e9b5SChristoph Hellwig 25335158e9b5SChristoph Hellwig void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, 25345158e9b5SChristoph Hellwig u32 type, u32 val) 25355158e9b5SChristoph Hellwig { 25365158e9b5SChristoph Hellwig if (!ctx->switch_log) 25375158e9b5SChristoph Hellwig return; 25385158e9b5SChristoph Hellwig 25395158e9b5SChristoph Hellwig spin_lock(&ctx->switch_log->lock); 25405158e9b5SChristoph Hellwig if (spufs_switch_log_avail(ctx) > 1) { 25415158e9b5SChristoph Hellwig struct switch_log_entry *p; 25425158e9b5SChristoph Hellwig 25435158e9b5SChristoph Hellwig p = ctx->switch_log->log + ctx->switch_log->head; 25445158e9b5SChristoph Hellwig ktime_get_ts(&p->tstamp); 25455158e9b5SChristoph Hellwig p->timebase = get_tb(); 25465158e9b5SChristoph Hellwig p->spu_id = spu ? spu->number : -1; 25475158e9b5SChristoph Hellwig p->type = type; 25485158e9b5SChristoph Hellwig p->val = val; 25495158e9b5SChristoph Hellwig 25505158e9b5SChristoph Hellwig ctx->switch_log->head = 25515158e9b5SChristoph Hellwig (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; 25525158e9b5SChristoph Hellwig } 25535158e9b5SChristoph Hellwig spin_unlock(&ctx->switch_log->lock); 25545158e9b5SChristoph Hellwig 25555158e9b5SChristoph Hellwig wake_up(&ctx->switch_log->wait); 25565158e9b5SChristoph Hellwig } 2557e9f8a0b6SChristoph Hellwig 255846deed69SLuke Browning static int spufs_show_ctx(struct seq_file *s, void *private) 255946deed69SLuke Browning { 256046deed69SLuke Browning struct spu_context *ctx = s->private; 256146deed69SLuke Browning u64 mfc_control_RW; 256246deed69SLuke Browning 256346deed69SLuke Browning mutex_lock(&ctx->state_mutex); 256446deed69SLuke Browning if (ctx->spu) { 256546deed69SLuke Browning struct spu *spu = ctx->spu; 256646deed69SLuke Browning struct spu_priv2 __iomem *priv2 = spu->priv2; 256746deed69SLuke Browning 256846deed69SLuke Browning spin_lock_irq(&spu->register_lock); 256946deed69SLuke Browning mfc_control_RW = in_be64(&priv2->mfc_control_RW); 257046deed69SLuke Browning spin_unlock_irq(&spu->register_lock); 257146deed69SLuke Browning } else { 257246deed69SLuke Browning struct spu_state *csa = &ctx->csa; 257346deed69SLuke Browning 257446deed69SLuke Browning mfc_control_RW = csa->priv2.mfc_control_RW; 257546deed69SLuke Browning } 257646deed69SLuke Browning 257746deed69SLuke Browning seq_printf(s, "%c flgs(%lx) sflgs(%lx) pri(%d) ts(%d) spu(%02d)" 257846deed69SLuke Browning " %c %lx %lx %lx %lx %x %x\n", 257946deed69SLuke Browning ctx->state == SPU_STATE_SAVED ? 'S' : 'R', 258046deed69SLuke Browning ctx->flags, 258146deed69SLuke Browning ctx->sched_flags, 258246deed69SLuke Browning ctx->prio, 258346deed69SLuke Browning ctx->time_slice, 258446deed69SLuke Browning ctx->spu ? ctx->spu->number : -1, 258546deed69SLuke Browning !list_empty(&ctx->rq) ? 'q' : ' ', 258646deed69SLuke Browning ctx->csa.class_0_pending, 258746deed69SLuke Browning ctx->csa.class_0_dar, 258846deed69SLuke Browning ctx->csa.class_1_dsisr, 258946deed69SLuke Browning mfc_control_RW, 259046deed69SLuke Browning ctx->ops->runcntl_read(ctx), 259146deed69SLuke Browning ctx->ops->status_read(ctx)); 259246deed69SLuke Browning 259346deed69SLuke Browning mutex_unlock(&ctx->state_mutex); 259446deed69SLuke Browning 259546deed69SLuke Browning return 0; 259646deed69SLuke Browning } 259746deed69SLuke Browning 259846deed69SLuke Browning static int spufs_ctx_open(struct inode *inode, struct file *file) 259946deed69SLuke Browning { 260046deed69SLuke Browning return single_open(file, spufs_show_ctx, SPUFS_I(inode)->i_ctx); 260146deed69SLuke Browning } 260246deed69SLuke Browning 260346deed69SLuke Browning static const struct file_operations spufs_ctx_fops = { 260446deed69SLuke Browning .open = spufs_ctx_open, 260546deed69SLuke Browning .read = seq_read, 260646deed69SLuke Browning .llseek = seq_lseek, 260746deed69SLuke Browning .release = single_release, 260846deed69SLuke Browning }; 260946deed69SLuke Browning 2610*23d893f5SJeremy Kerr struct spufs_tree_descr spufs_dir_contents[] = { 2611cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 261267207b96SArnd Bergmann { "mem", &spufs_mem_fops, 0666, }, 26138b3d6663SArnd Bergmann { "regs", &spufs_regs_fops, 0666, }, 261467207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 261567207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 261667207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 261767207b96SArnd Bergmann { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, 261867207b96SArnd Bergmann { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, 261967207b96SArnd Bergmann { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, 2620603c4612SJeremy Kerr { "signal1", &spufs_signal1_fops, 0666, }, 2621603c4612SJeremy Kerr { "signal2", &spufs_signal2_fops, 0666, }, 262267207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 262367207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 26246df10a82SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26258b3d6663SArnd Bergmann { "fpcr", &spufs_fpcr_fops, 0666, }, 2626b9e3bd77SDwayne Grant McConnell { "lslr", &spufs_lslr_ops, 0444, }, 2627b9e3bd77SDwayne Grant McConnell { "mfc", &spufs_mfc_fops, 0666, }, 2628b9e3bd77SDwayne Grant McConnell { "mss", &spufs_mss_fops, 0666, }, 2629b9e3bd77SDwayne Grant McConnell { "npc", &spufs_npc_ops, 0666, }, 2630b9e3bd77SDwayne Grant McConnell { "srr0", &spufs_srr0_ops, 0666, }, 26318b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 26328b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 26338b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 2634b9e3bd77SDwayne Grant McConnell { "event_status", &spufs_event_status_ops, 0444, }, 263527d5bf2aSBenjamin Herrenschmidt { "psmap", &spufs_psmap_fops, 0666, }, 263686767277SArnd Bergmann { "phys-id", &spufs_id_ops, 0666, }, 263786767277SArnd Bergmann { "object-id", &spufs_object_id_ops, 0666, }, 263869a2f00cSDwayne Grant McConnell { "mbox_info", &spufs_mbox_info_fops, 0444, }, 263969a2f00cSDwayne Grant McConnell { "ibox_info", &spufs_ibox_info_fops, 0444, }, 264069a2f00cSDwayne Grant McConnell { "wbox_info", &spufs_wbox_info_fops, 0444, }, 2641b9e3bd77SDwayne Grant McConnell { "dma_info", &spufs_dma_info_fops, 0444, }, 2642b9e3bd77SDwayne Grant McConnell { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, 2643476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2644e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 26455158e9b5SChristoph Hellwig { "switch_log", &spufs_switch_log_fops, 0444 }, 264646deed69SLuke Browning { ".ctx", &spufs_ctx_fops, 0444, }, 264767207b96SArnd Bergmann {}, 264867207b96SArnd Bergmann }; 26495737edd1SMark Nutter 2650*23d893f5SJeremy Kerr struct spufs_tree_descr spufs_dir_nosched_contents[] = { 2651cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 26525737edd1SMark Nutter { "mem", &spufs_mem_fops, 0666, }, 26535737edd1SMark Nutter { "mbox", &spufs_mbox_fops, 0444, }, 26545737edd1SMark Nutter { "ibox", &spufs_ibox_fops, 0444, }, 26555737edd1SMark Nutter { "wbox", &spufs_wbox_fops, 0222, }, 26565737edd1SMark Nutter { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, 26575737edd1SMark Nutter { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, 26585737edd1SMark Nutter { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, 2659d054b36fSJeremy Kerr { "signal1", &spufs_signal1_nosched_fops, 0222, }, 2660d054b36fSJeremy Kerr { "signal2", &spufs_signal2_nosched_fops, 0222, }, 26615737edd1SMark Nutter { "signal1_type", &spufs_signal1_type, 0666, }, 26625737edd1SMark Nutter { "signal2_type", &spufs_signal2_type, 0666, }, 26635737edd1SMark Nutter { "mss", &spufs_mss_fops, 0666, }, 26645737edd1SMark Nutter { "mfc", &spufs_mfc_fops, 0666, }, 26655737edd1SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 26665737edd1SMark Nutter { "npc", &spufs_npc_ops, 0666, }, 26675737edd1SMark Nutter { "psmap", &spufs_psmap_fops, 0666, }, 26685737edd1SMark Nutter { "phys-id", &spufs_id_ops, 0666, }, 26695737edd1SMark Nutter { "object-id", &spufs_object_id_ops, 0666, }, 2670476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2671e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 267246deed69SLuke Browning { ".ctx", &spufs_ctx_fops, 0444, }, 26735737edd1SMark Nutter {}, 26745737edd1SMark Nutter }; 2675bf1ab978SDwayne Grant McConnell 2676bf1ab978SDwayne Grant McConnell struct spufs_coredump_reader spufs_coredump_read[] = { 26774fca9c42SMichael Ellerman { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])}, 26784fca9c42SMichael Ellerman { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) }, 2679104f0cc2SMichael Ellerman { "lslr", NULL, spufs_lslr_get, 19 }, 2680104f0cc2SMichael Ellerman { "decr", NULL, spufs_decr_get, 19 }, 2681104f0cc2SMichael Ellerman { "decr_status", NULL, spufs_decr_status_get, 19 }, 26824fca9c42SMichael Ellerman { "mem", __spufs_mem_read, NULL, LS_SIZE, }, 26834fca9c42SMichael Ellerman { "signal1", __spufs_signal1_read, NULL, sizeof(u32) }, 2684104f0cc2SMichael Ellerman { "signal1_type", NULL, spufs_signal1_type_get, 19 }, 26854fca9c42SMichael Ellerman { "signal2", __spufs_signal2_read, NULL, sizeof(u32) }, 2686104f0cc2SMichael Ellerman { "signal2_type", NULL, spufs_signal2_type_get, 19 }, 2687104f0cc2SMichael Ellerman { "event_mask", NULL, spufs_event_mask_get, 19 }, 2688104f0cc2SMichael Ellerman { "event_status", NULL, spufs_event_status_get, 19 }, 26894fca9c42SMichael Ellerman { "mbox_info", __spufs_mbox_info_read, NULL, sizeof(u32) }, 26904fca9c42SMichael Ellerman { "ibox_info", __spufs_ibox_info_read, NULL, sizeof(u32) }, 26914fca9c42SMichael Ellerman { "wbox_info", __spufs_wbox_info_read, NULL, 4 * sizeof(u32)}, 26924fca9c42SMichael Ellerman { "dma_info", __spufs_dma_info_read, NULL, sizeof(struct spu_dma_info)}, 26934fca9c42SMichael Ellerman { "proxydma_info", __spufs_proxydma_info_read, 26944fca9c42SMichael Ellerman NULL, sizeof(struct spu_proxydma_info)}, 2695104f0cc2SMichael Ellerman { "object-id", NULL, spufs_object_id_get, 19 }, 2696104f0cc2SMichael Ellerman { "npc", NULL, spufs_npc_get, 19 }, 2697936d5bf1SMichael Ellerman { NULL }, 2698bf1ab978SDwayne Grant McConnell }; 2699