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> 3267207b96SArnd Bergmann 3367207b96SArnd Bergmann #include <asm/io.h> 3467207b96SArnd Bergmann #include <asm/semaphore.h> 3567207b96SArnd Bergmann #include <asm/spu.h> 36b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h> 3767207b96SArnd Bergmann #include <asm/uaccess.h> 3867207b96SArnd Bergmann 3967207b96SArnd Bergmann #include "spufs.h" 4067207b96SArnd Bergmann 4127d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) 4227d5bf2aSBenjamin Herrenschmidt 43197b1a82SChristoph Hellwig /* Simple attribute files */ 44197b1a82SChristoph Hellwig struct spufs_attr { 45197b1a82SChristoph Hellwig int (*get)(void *, u64 *); 46197b1a82SChristoph Hellwig int (*set)(void *, u64); 47197b1a82SChristoph Hellwig char get_buf[24]; /* enough to store a u64 and "\n\0" */ 48197b1a82SChristoph Hellwig char set_buf[24]; 49197b1a82SChristoph Hellwig void *data; 50197b1a82SChristoph Hellwig const char *fmt; /* format for read operation */ 51197b1a82SChristoph Hellwig struct mutex mutex; /* protects access to these buffers */ 52197b1a82SChristoph Hellwig }; 53197b1a82SChristoph Hellwig 54197b1a82SChristoph Hellwig static int spufs_attr_open(struct inode *inode, struct file *file, 55197b1a82SChristoph Hellwig int (*get)(void *, u64 *), int (*set)(void *, u64), 56197b1a82SChristoph Hellwig const char *fmt) 57197b1a82SChristoph Hellwig { 58197b1a82SChristoph Hellwig struct spufs_attr *attr; 59197b1a82SChristoph Hellwig 60197b1a82SChristoph Hellwig attr = kmalloc(sizeof(*attr), GFP_KERNEL); 61197b1a82SChristoph Hellwig if (!attr) 62197b1a82SChristoph Hellwig return -ENOMEM; 63197b1a82SChristoph Hellwig 64197b1a82SChristoph Hellwig attr->get = get; 65197b1a82SChristoph Hellwig attr->set = set; 66197b1a82SChristoph Hellwig attr->data = inode->i_private; 67197b1a82SChristoph Hellwig attr->fmt = fmt; 68197b1a82SChristoph Hellwig mutex_init(&attr->mutex); 69197b1a82SChristoph Hellwig file->private_data = attr; 70197b1a82SChristoph Hellwig 71197b1a82SChristoph Hellwig return nonseekable_open(inode, file); 72197b1a82SChristoph Hellwig } 73197b1a82SChristoph Hellwig 74197b1a82SChristoph Hellwig static int spufs_attr_release(struct inode *inode, struct file *file) 75197b1a82SChristoph Hellwig { 76197b1a82SChristoph Hellwig kfree(file->private_data); 77197b1a82SChristoph Hellwig return 0; 78197b1a82SChristoph Hellwig } 79197b1a82SChristoph Hellwig 80197b1a82SChristoph Hellwig static ssize_t spufs_attr_read(struct file *file, char __user *buf, 81197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 82197b1a82SChristoph Hellwig { 83197b1a82SChristoph Hellwig struct spufs_attr *attr; 84197b1a82SChristoph Hellwig size_t size; 85197b1a82SChristoph Hellwig ssize_t ret; 86197b1a82SChristoph Hellwig 87197b1a82SChristoph Hellwig attr = file->private_data; 88197b1a82SChristoph Hellwig if (!attr->get) 89197b1a82SChristoph Hellwig return -EACCES; 90197b1a82SChristoph Hellwig 91197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 92197b1a82SChristoph Hellwig if (ret) 93197b1a82SChristoph Hellwig return ret; 94197b1a82SChristoph Hellwig 95197b1a82SChristoph Hellwig if (*ppos) { /* continued read */ 96197b1a82SChristoph Hellwig size = strlen(attr->get_buf); 97197b1a82SChristoph Hellwig } else { /* first read */ 98197b1a82SChristoph Hellwig u64 val; 99197b1a82SChristoph Hellwig ret = attr->get(attr->data, &val); 100197b1a82SChristoph Hellwig if (ret) 101197b1a82SChristoph Hellwig goto out; 102197b1a82SChristoph Hellwig 103197b1a82SChristoph Hellwig size = scnprintf(attr->get_buf, sizeof(attr->get_buf), 104197b1a82SChristoph Hellwig attr->fmt, (unsigned long long)val); 105197b1a82SChristoph Hellwig } 106197b1a82SChristoph Hellwig 107197b1a82SChristoph Hellwig ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size); 108197b1a82SChristoph Hellwig out: 109197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 110197b1a82SChristoph Hellwig return ret; 111197b1a82SChristoph Hellwig } 112197b1a82SChristoph Hellwig 113197b1a82SChristoph Hellwig static ssize_t spufs_attr_write(struct file *file, const char __user *buf, 114197b1a82SChristoph Hellwig size_t len, loff_t *ppos) 115197b1a82SChristoph Hellwig { 116197b1a82SChristoph Hellwig struct spufs_attr *attr; 117197b1a82SChristoph Hellwig u64 val; 118197b1a82SChristoph Hellwig size_t size; 119197b1a82SChristoph Hellwig ssize_t ret; 120197b1a82SChristoph Hellwig 121197b1a82SChristoph Hellwig attr = file->private_data; 122197b1a82SChristoph Hellwig if (!attr->set) 123197b1a82SChristoph Hellwig return -EACCES; 124197b1a82SChristoph Hellwig 125197b1a82SChristoph Hellwig ret = mutex_lock_interruptible(&attr->mutex); 126197b1a82SChristoph Hellwig if (ret) 127197b1a82SChristoph Hellwig return ret; 128197b1a82SChristoph Hellwig 129197b1a82SChristoph Hellwig ret = -EFAULT; 130197b1a82SChristoph Hellwig size = min(sizeof(attr->set_buf) - 1, len); 131197b1a82SChristoph Hellwig if (copy_from_user(attr->set_buf, buf, size)) 132197b1a82SChristoph Hellwig goto out; 133197b1a82SChristoph Hellwig 134197b1a82SChristoph Hellwig ret = len; /* claim we got the whole input */ 135197b1a82SChristoph Hellwig attr->set_buf[size] = '\0'; 136197b1a82SChristoph Hellwig val = simple_strtol(attr->set_buf, NULL, 0); 137197b1a82SChristoph Hellwig attr->set(attr->data, val); 138197b1a82SChristoph Hellwig out: 139197b1a82SChristoph Hellwig mutex_unlock(&attr->mutex); 140197b1a82SChristoph Hellwig return ret; 141197b1a82SChristoph Hellwig } 142197b1a82SChristoph Hellwig 143197b1a82SChristoph Hellwig #define DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__fops, __get, __set, __fmt) \ 144197b1a82SChristoph Hellwig static int __fops ## _open(struct inode *inode, struct file *file) \ 145197b1a82SChristoph Hellwig { \ 146197b1a82SChristoph Hellwig __simple_attr_check_format(__fmt, 0ull); \ 147197b1a82SChristoph Hellwig return spufs_attr_open(inode, file, __get, __set, __fmt); \ 148197b1a82SChristoph Hellwig } \ 149197b1a82SChristoph Hellwig static struct file_operations __fops = { \ 150197b1a82SChristoph Hellwig .owner = THIS_MODULE, \ 151197b1a82SChristoph Hellwig .open = __fops ## _open, \ 152197b1a82SChristoph Hellwig .release = spufs_attr_release, \ 153197b1a82SChristoph Hellwig .read = spufs_attr_read, \ 154197b1a82SChristoph Hellwig .write = spufs_attr_write, \ 155197b1a82SChristoph Hellwig }; 156197b1a82SChristoph Hellwig 157cbe709c1SBenjamin Herrenschmidt 15867207b96SArnd Bergmann static int 15967207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file) 16067207b96SArnd Bergmann { 16167207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1626df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 16343c2bbd9SChristoph Hellwig 16447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1656df10a82SMark Nutter file->private_data = ctx; 16643c2bbd9SChristoph Hellwig if (!i->i_openers++) 1676df10a82SMark Nutter ctx->local_store = inode->i_mapping; 16847d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 16943c2bbd9SChristoph Hellwig return 0; 17043c2bbd9SChristoph Hellwig } 17143c2bbd9SChristoph Hellwig 17243c2bbd9SChristoph Hellwig static int 17343c2bbd9SChristoph Hellwig spufs_mem_release(struct inode *inode, struct file *file) 17443c2bbd9SChristoph Hellwig { 17543c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 17643c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 17743c2bbd9SChristoph Hellwig 17847d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 17943c2bbd9SChristoph Hellwig if (!--i->i_openers) 18043c2bbd9SChristoph Hellwig ctx->local_store = NULL; 18147d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 18267207b96SArnd Bergmann return 0; 18367207b96SArnd Bergmann } 18467207b96SArnd Bergmann 18567207b96SArnd Bergmann static ssize_t 186bf1ab978SDwayne Grant McConnell __spufs_mem_read(struct spu_context *ctx, char __user *buffer, 187bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 188bf1ab978SDwayne Grant McConnell { 189bf1ab978SDwayne Grant McConnell char *local_store = ctx->ops->get_ls(ctx); 190bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, local_store, 191bf1ab978SDwayne Grant McConnell LS_SIZE); 192bf1ab978SDwayne Grant McConnell } 193bf1ab978SDwayne Grant McConnell 194bf1ab978SDwayne Grant McConnell static ssize_t 19567207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer, 19667207b96SArnd Bergmann size_t size, loff_t *pos) 19767207b96SArnd Bergmann { 198bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 199aa0ed2bdSArnd Bergmann ssize_t ret; 20067207b96SArnd Bergmann 2018b3d6663SArnd Bergmann spu_acquire(ctx); 202bf1ab978SDwayne Grant McConnell ret = __spufs_mem_read(ctx, buffer, size, pos); 2038b3d6663SArnd Bergmann spu_release(ctx); 20467207b96SArnd Bergmann return ret; 20567207b96SArnd Bergmann } 20667207b96SArnd Bergmann 20767207b96SArnd Bergmann static ssize_t 20867207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer, 209aa0ed2bdSArnd Bergmann size_t size, loff_t *ppos) 21067207b96SArnd Bergmann { 21167207b96SArnd Bergmann struct spu_context *ctx = file->private_data; 2128b3d6663SArnd Bergmann char *local_store; 213aa0ed2bdSArnd Bergmann loff_t pos = *ppos; 2148b3d6663SArnd Bergmann int ret; 21567207b96SArnd Bergmann 216aa0ed2bdSArnd Bergmann if (pos < 0) 217aa0ed2bdSArnd Bergmann return -EINVAL; 218aa0ed2bdSArnd Bergmann if (pos > LS_SIZE) 21967207b96SArnd Bergmann return -EFBIG; 220aa0ed2bdSArnd Bergmann if (size > LS_SIZE - pos) 221aa0ed2bdSArnd Bergmann size = LS_SIZE - pos; 2228b3d6663SArnd Bergmann 2238b3d6663SArnd Bergmann spu_acquire(ctx); 2248b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 225aa0ed2bdSArnd Bergmann ret = copy_from_user(local_store + pos, buffer, size); 2268b3d6663SArnd Bergmann spu_release(ctx); 227aa0ed2bdSArnd Bergmann 228aa0ed2bdSArnd Bergmann if (ret) 229aa0ed2bdSArnd Bergmann return -EFAULT; 230aa0ed2bdSArnd Bergmann *ppos = pos + size; 231aa0ed2bdSArnd Bergmann return size; 23267207b96SArnd Bergmann } 23367207b96SArnd Bergmann 23478bde53eSBenjamin Herrenschmidt static unsigned long spufs_mem_mmap_nopfn(struct vm_area_struct *vma, 23578bde53eSBenjamin Herrenschmidt unsigned long address) 2368b3d6663SArnd Bergmann { 2378b3d6663SArnd Bergmann struct spu_context *ctx = vma->vm_file->private_data; 238f1fa74f4SBenjamin Herrenschmidt unsigned long pfn, offset, addr0 = address; 239f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 240f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 241f1fa74f4SBenjamin Herrenschmidt int psize; 24278bde53eSBenjamin Herrenschmidt 243f1fa74f4SBenjamin Herrenschmidt /* Check what page size we are using */ 244f1fa74f4SBenjamin Herrenschmidt psize = get_slice_psize(vma->vm_mm, address); 2458b3d6663SArnd Bergmann 246f1fa74f4SBenjamin Herrenschmidt /* Some sanity checking */ 247f1fa74f4SBenjamin Herrenschmidt BUG_ON(csa->use_big_pages != (psize == MMU_PAGE_64K)); 248f1fa74f4SBenjamin Herrenschmidt 249f1fa74f4SBenjamin Herrenschmidt /* Wow, 64K, cool, we need to align the address though */ 250f1fa74f4SBenjamin Herrenschmidt if (csa->use_big_pages) { 251f1fa74f4SBenjamin Herrenschmidt BUG_ON(vma->vm_start & 0xffff); 252f1fa74f4SBenjamin Herrenschmidt address &= ~0xfffful; 253f1fa74f4SBenjamin Herrenschmidt } 254f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 255f1fa74f4SBenjamin Herrenschmidt 256f1fa74f4SBenjamin Herrenschmidt offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); 257128b8546SMasato Noguchi if (offset >= LS_SIZE) 258128b8546SMasato Noguchi return NOPFN_SIGBUS; 259128b8546SMasato Noguchi 260f1fa74f4SBenjamin Herrenschmidt pr_debug("spufs_mem_mmap_nopfn address=0x%lx -> 0x%lx, offset=0x%lx\n", 261f1fa74f4SBenjamin Herrenschmidt addr0, address, offset); 262f1fa74f4SBenjamin Herrenschmidt 2638b3d6663SArnd Bergmann spu_acquire(ctx); 2648b3d6663SArnd Bergmann 265ac91cb8dSArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 266ac91cb8dSArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 267932f535dSArnd Bergmann & ~_PAGE_NO_CACHE); 26878bde53eSBenjamin Herrenschmidt pfn = vmalloc_to_pfn(ctx->csa.lscsa->ls + offset); 269ac91cb8dSArnd Bergmann } else { 270ac91cb8dSArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 271932f535dSArnd Bergmann | _PAGE_NO_CACHE); 27278bde53eSBenjamin Herrenschmidt pfn = (ctx->spu->local_store_phys + offset) >> PAGE_SHIFT; 273ac91cb8dSArnd Bergmann } 27478bde53eSBenjamin Herrenschmidt vm_insert_pfn(vma, address, pfn); 27578bde53eSBenjamin Herrenschmidt 2768b3d6663SArnd Bergmann spu_release(ctx); 2778b3d6663SArnd Bergmann 27878bde53eSBenjamin Herrenschmidt return NOPFN_REFAULT; 2798b3d6663SArnd Bergmann } 2808b3d6663SArnd Bergmann 28178bde53eSBenjamin Herrenschmidt 2828b3d6663SArnd Bergmann static struct vm_operations_struct spufs_mem_mmap_vmops = { 28378bde53eSBenjamin Herrenschmidt .nopfn = spufs_mem_mmap_nopfn, 2848b3d6663SArnd Bergmann }; 2858b3d6663SArnd Bergmann 286f1fa74f4SBenjamin Herrenschmidt static int spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 28767207b96SArnd Bergmann { 288f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 289f1fa74f4SBenjamin Herrenschmidt struct spu_context *ctx = file->private_data; 290f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 291f1fa74f4SBenjamin Herrenschmidt 292f1fa74f4SBenjamin Herrenschmidt /* Sanity check VMA alignment */ 293f1fa74f4SBenjamin Herrenschmidt if (csa->use_big_pages) { 294f1fa74f4SBenjamin Herrenschmidt pr_debug("spufs_mem_mmap 64K, start=0x%lx, end=0x%lx," 295f1fa74f4SBenjamin Herrenschmidt " pgoff=0x%lx\n", vma->vm_start, vma->vm_end, 296f1fa74f4SBenjamin Herrenschmidt vma->vm_pgoff); 297f1fa74f4SBenjamin Herrenschmidt if (vma->vm_start & 0xffff) 298f1fa74f4SBenjamin Herrenschmidt return -EINVAL; 299f1fa74f4SBenjamin Herrenschmidt if (vma->vm_pgoff & 0xf) 300f1fa74f4SBenjamin Herrenschmidt return -EINVAL; 301f1fa74f4SBenjamin Herrenschmidt } 302f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 303f1fa74f4SBenjamin Herrenschmidt 3048b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 3058b3d6663SArnd Bergmann return -EINVAL; 30667207b96SArnd Bergmann 30778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 30867207b96SArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 30967207b96SArnd Bergmann | _PAGE_NO_CACHE); 3108b3d6663SArnd Bergmann 3118b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 31267207b96SArnd Bergmann return 0; 31367207b96SArnd Bergmann } 31467207b96SArnd Bergmann 315f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 3161238819aSSebastian Siewior static unsigned long spufs_get_unmapped_area(struct file *file, 3171238819aSSebastian Siewior unsigned long addr, unsigned long len, unsigned long pgoff, 318f1fa74f4SBenjamin Herrenschmidt unsigned long flags) 319f1fa74f4SBenjamin Herrenschmidt { 320f1fa74f4SBenjamin Herrenschmidt struct spu_context *ctx = file->private_data; 321f1fa74f4SBenjamin Herrenschmidt struct spu_state *csa = &ctx->csa; 322f1fa74f4SBenjamin Herrenschmidt 323f1fa74f4SBenjamin Herrenschmidt /* If not using big pages, fallback to normal MM g_u_a */ 324f1fa74f4SBenjamin Herrenschmidt if (!csa->use_big_pages) 325f1fa74f4SBenjamin Herrenschmidt return current->mm->get_unmapped_area(file, addr, len, 326f1fa74f4SBenjamin Herrenschmidt pgoff, flags); 327f1fa74f4SBenjamin Herrenschmidt 328f1fa74f4SBenjamin Herrenschmidt /* Else, try to obtain a 64K pages slice */ 329f1fa74f4SBenjamin Herrenschmidt return slice_get_unmapped_area(addr, len, flags, 330f1fa74f4SBenjamin Herrenschmidt MMU_PAGE_64K, 1, 0); 331f1fa74f4SBenjamin Herrenschmidt } 332f1fa74f4SBenjamin Herrenschmidt #endif /* CONFIG_SPU_FS_64K_LS */ 333f1fa74f4SBenjamin Herrenschmidt 3345dfe4c96SArjan van de Ven static const struct file_operations spufs_mem_fops = { 33567207b96SArnd Bergmann .open = spufs_mem_open, 336ce92987bSChristoph Hellwig .release = spufs_mem_release, 33767207b96SArnd Bergmann .read = spufs_mem_read, 33867207b96SArnd Bergmann .write = spufs_mem_write, 3398b3d6663SArnd Bergmann .llseek = generic_file_llseek, 34067207b96SArnd Bergmann .mmap = spufs_mem_mmap, 341f1fa74f4SBenjamin Herrenschmidt #ifdef CONFIG_SPU_FS_64K_LS 342f1fa74f4SBenjamin Herrenschmidt .get_unmapped_area = spufs_get_unmapped_area, 343f1fa74f4SBenjamin Herrenschmidt #endif 3448b3d6663SArnd Bergmann }; 3458b3d6663SArnd Bergmann 34678bde53eSBenjamin Herrenschmidt static unsigned long spufs_ps_nopfn(struct vm_area_struct *vma, 3476df10a82SMark Nutter unsigned long address, 34878bde53eSBenjamin Herrenschmidt unsigned long ps_offs, 34927d5bf2aSBenjamin Herrenschmidt unsigned long ps_size) 3506df10a82SMark Nutter { 3516df10a82SMark Nutter struct spu_context *ctx = vma->vm_file->private_data; 35278bde53eSBenjamin Herrenschmidt unsigned long area, offset = address - vma->vm_start; 3536df10a82SMark Nutter 3546df10a82SMark Nutter offset += vma->vm_pgoff << PAGE_SHIFT; 35527d5bf2aSBenjamin Herrenschmidt if (offset >= ps_size) 35678bde53eSBenjamin Herrenschmidt return NOPFN_SIGBUS; 3576df10a82SMark Nutter 35833bfd7a7SArnd Bergmann /* 35933bfd7a7SArnd Bergmann * We have to wait for context to be loaded before we have 36033bfd7a7SArnd Bergmann * pages to hand out to the user, but we don't want to wait 36133bfd7a7SArnd Bergmann * with the mmap_sem held. 36233bfd7a7SArnd Bergmann * It is possible to drop the mmap_sem here, but then we need 36333bfd7a7SArnd Bergmann * to return NOPFN_REFAULT because the mappings may have 36433bfd7a7SArnd Bergmann * hanged. 36578bde53eSBenjamin Herrenschmidt */ 36633bfd7a7SArnd Bergmann spu_acquire(ctx); 36733bfd7a7SArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 36833bfd7a7SArnd Bergmann up_read(¤t->mm->mmap_sem); 36933bfd7a7SArnd Bergmann spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 37033bfd7a7SArnd Bergmann down_read(¤t->mm->mmap_sem); 37133bfd7a7SArnd Bergmann goto out; 37233bfd7a7SArnd Bergmann } 3736df10a82SMark Nutter 3746df10a82SMark Nutter area = ctx->spu->problem_phys + ps_offs; 37578bde53eSBenjamin Herrenschmidt vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); 37633bfd7a7SArnd Bergmann 37733bfd7a7SArnd Bergmann out: 3786df10a82SMark Nutter spu_release(ctx); 3796df10a82SMark Nutter 38078bde53eSBenjamin Herrenschmidt return NOPFN_REFAULT; 3816df10a82SMark Nutter } 3826df10a82SMark Nutter 38327d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 38478bde53eSBenjamin Herrenschmidt static unsigned long spufs_cntl_mmap_nopfn(struct vm_area_struct *vma, 38578bde53eSBenjamin Herrenschmidt unsigned long address) 3866df10a82SMark Nutter { 38778bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x4000, 0x1000); 3886df10a82SMark Nutter } 3896df10a82SMark Nutter 3906df10a82SMark Nutter static struct vm_operations_struct spufs_cntl_mmap_vmops = { 39178bde53eSBenjamin Herrenschmidt .nopfn = spufs_cntl_mmap_nopfn, 3926df10a82SMark Nutter }; 3936df10a82SMark Nutter 3946df10a82SMark Nutter /* 3956df10a82SMark Nutter * mmap support for problem state control area [0x4000 - 0x4fff]. 3966df10a82SMark Nutter */ 3976df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) 3986df10a82SMark Nutter { 3996df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 4006df10a82SMark Nutter return -EINVAL; 4016df10a82SMark Nutter 40278bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 4036df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 40423cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 4056df10a82SMark Nutter 4066df10a82SMark Nutter vma->vm_ops = &spufs_cntl_mmap_vmops; 4076df10a82SMark Nutter return 0; 4086df10a82SMark Nutter } 40927d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 41027d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL 41127d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 4126df10a82SMark Nutter 413197b1a82SChristoph Hellwig static int spufs_cntl_get(void *data, u64 *val) 414e1dbff2bSArnd Bergmann { 415e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 416e1dbff2bSArnd Bergmann 417e1dbff2bSArnd Bergmann spu_acquire(ctx); 418197b1a82SChristoph Hellwig *val = ctx->ops->status_read(ctx); 419e1dbff2bSArnd Bergmann spu_release(ctx); 420e1dbff2bSArnd Bergmann 421197b1a82SChristoph Hellwig return 0; 422e1dbff2bSArnd Bergmann } 423e1dbff2bSArnd Bergmann 424197b1a82SChristoph Hellwig static int spufs_cntl_set(void *data, u64 val) 425e1dbff2bSArnd Bergmann { 426e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 427e1dbff2bSArnd Bergmann 428e1dbff2bSArnd Bergmann spu_acquire(ctx); 429e1dbff2bSArnd Bergmann ctx->ops->runcntl_write(ctx, val); 430e1dbff2bSArnd Bergmann spu_release(ctx); 431197b1a82SChristoph Hellwig 432197b1a82SChristoph Hellwig return 0; 433e1dbff2bSArnd Bergmann } 434e1dbff2bSArnd Bergmann 4356df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file) 4366df10a82SMark Nutter { 4376df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 4386df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 4396df10a82SMark Nutter 44047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 4416df10a82SMark Nutter file->private_data = ctx; 44243c2bbd9SChristoph Hellwig if (!i->i_openers++) 4436df10a82SMark Nutter ctx->cntl = inode->i_mapping; 44447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 445197b1a82SChristoph Hellwig return spufs_attr_open(inode, file, spufs_cntl_get, 446e1dbff2bSArnd Bergmann spufs_cntl_set, "0x%08lx"); 4476df10a82SMark Nutter } 4486df10a82SMark Nutter 44943c2bbd9SChristoph Hellwig static int 45043c2bbd9SChristoph Hellwig spufs_cntl_release(struct inode *inode, struct file *file) 45143c2bbd9SChristoph Hellwig { 45243c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 45343c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 45443c2bbd9SChristoph Hellwig 455197b1a82SChristoph Hellwig spufs_attr_release(inode, file); 45643c2bbd9SChristoph Hellwig 45747d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 45843c2bbd9SChristoph Hellwig if (!--i->i_openers) 45943c2bbd9SChristoph Hellwig ctx->cntl = NULL; 46047d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 46143c2bbd9SChristoph Hellwig return 0; 46243c2bbd9SChristoph Hellwig } 46343c2bbd9SChristoph Hellwig 4645dfe4c96SArjan van de Ven static const struct file_operations spufs_cntl_fops = { 4656df10a82SMark Nutter .open = spufs_cntl_open, 46643c2bbd9SChristoph Hellwig .release = spufs_cntl_release, 467197b1a82SChristoph Hellwig .read = spufs_attr_read, 468197b1a82SChristoph Hellwig .write = spufs_attr_write, 4696df10a82SMark Nutter .mmap = spufs_cntl_mmap, 4706df10a82SMark Nutter }; 4716df10a82SMark Nutter 4728b3d6663SArnd Bergmann static int 4738b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 4748b3d6663SArnd Bergmann { 4758b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 4768b3d6663SArnd Bergmann file->private_data = i->i_ctx; 4778b3d6663SArnd Bergmann return 0; 4788b3d6663SArnd Bergmann } 4798b3d6663SArnd Bergmann 4808b3d6663SArnd Bergmann static ssize_t 481bf1ab978SDwayne Grant McConnell __spufs_regs_read(struct spu_context *ctx, char __user *buffer, 482bf1ab978SDwayne Grant McConnell size_t size, loff_t *pos) 483bf1ab978SDwayne Grant McConnell { 484bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 485bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 486bf1ab978SDwayne Grant McConnell lscsa->gprs, sizeof lscsa->gprs); 487bf1ab978SDwayne Grant McConnell } 488bf1ab978SDwayne Grant McConnell 489bf1ab978SDwayne Grant McConnell static ssize_t 4908b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 4918b3d6663SArnd Bergmann size_t size, loff_t *pos) 4928b3d6663SArnd Bergmann { 4938b3d6663SArnd Bergmann int ret; 494bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 4958b3d6663SArnd Bergmann 4968b3d6663SArnd Bergmann spu_acquire_saved(ctx); 497bf1ab978SDwayne Grant McConnell ret = __spufs_regs_read(ctx, buffer, size, pos); 49827b1ea09SChristoph Hellwig spu_release_saved(ctx); 4998b3d6663SArnd Bergmann return ret; 5008b3d6663SArnd Bergmann } 5018b3d6663SArnd Bergmann 5028b3d6663SArnd Bergmann static ssize_t 5038b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 5048b3d6663SArnd Bergmann size_t size, loff_t *pos) 5058b3d6663SArnd Bergmann { 5068b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5078b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5088b3d6663SArnd Bergmann int ret; 5098b3d6663SArnd Bergmann 5108b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size); 5118b3d6663SArnd Bergmann if (size <= 0) 5128b3d6663SArnd Bergmann return -EFBIG; 5138b3d6663SArnd Bergmann *pos += size; 5148b3d6663SArnd Bergmann 5158b3d6663SArnd Bergmann spu_acquire_saved(ctx); 5168b3d6663SArnd Bergmann 5178b3d6663SArnd Bergmann ret = copy_from_user(lscsa->gprs + *pos - size, 5188b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 5198b3d6663SArnd Bergmann 52027b1ea09SChristoph Hellwig spu_release_saved(ctx); 5218b3d6663SArnd Bergmann return ret; 5228b3d6663SArnd Bergmann } 5238b3d6663SArnd Bergmann 5245dfe4c96SArjan van de Ven static const struct file_operations spufs_regs_fops = { 5258b3d6663SArnd Bergmann .open = spufs_regs_open, 5268b3d6663SArnd Bergmann .read = spufs_regs_read, 5278b3d6663SArnd Bergmann .write = spufs_regs_write, 5288b3d6663SArnd Bergmann .llseek = generic_file_llseek, 5298b3d6663SArnd Bergmann }; 5308b3d6663SArnd Bergmann 5318b3d6663SArnd Bergmann static ssize_t 532bf1ab978SDwayne Grant McConnell __spufs_fpcr_read(struct spu_context *ctx, char __user * buffer, 533bf1ab978SDwayne Grant McConnell size_t size, loff_t * pos) 534bf1ab978SDwayne Grant McConnell { 535bf1ab978SDwayne Grant McConnell struct spu_lscsa *lscsa = ctx->csa.lscsa; 536bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buffer, size, pos, 537bf1ab978SDwayne Grant McConnell &lscsa->fpcr, sizeof(lscsa->fpcr)); 538bf1ab978SDwayne Grant McConnell } 539bf1ab978SDwayne Grant McConnell 540bf1ab978SDwayne Grant McConnell static ssize_t 5418b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 5428b3d6663SArnd Bergmann size_t size, loff_t * pos) 5438b3d6663SArnd Bergmann { 5448b3d6663SArnd Bergmann int ret; 545bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 5468b3d6663SArnd Bergmann 5478b3d6663SArnd Bergmann spu_acquire_saved(ctx); 548bf1ab978SDwayne Grant McConnell ret = __spufs_fpcr_read(ctx, buffer, size, pos); 54927b1ea09SChristoph Hellwig spu_release_saved(ctx); 5508b3d6663SArnd Bergmann return ret; 5518b3d6663SArnd Bergmann } 5528b3d6663SArnd Bergmann 5538b3d6663SArnd Bergmann static ssize_t 5548b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 5558b3d6663SArnd Bergmann size_t size, loff_t * pos) 5568b3d6663SArnd Bergmann { 5578b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5588b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 5598b3d6663SArnd Bergmann int ret; 5608b3d6663SArnd Bergmann 5618b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); 5628b3d6663SArnd Bergmann if (size <= 0) 5638b3d6663SArnd Bergmann return -EFBIG; 5648b3d6663SArnd Bergmann *pos += size; 5658b3d6663SArnd Bergmann 5668b3d6663SArnd Bergmann spu_acquire_saved(ctx); 5678b3d6663SArnd Bergmann 5688b3d6663SArnd Bergmann ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, 5698b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 5708b3d6663SArnd Bergmann 57127b1ea09SChristoph Hellwig spu_release_saved(ctx); 5728b3d6663SArnd Bergmann return ret; 5738b3d6663SArnd Bergmann } 5748b3d6663SArnd Bergmann 5755dfe4c96SArjan van de Ven static const struct file_operations spufs_fpcr_fops = { 5768b3d6663SArnd Bergmann .open = spufs_regs_open, 5778b3d6663SArnd Bergmann .read = spufs_fpcr_read, 5788b3d6663SArnd Bergmann .write = spufs_fpcr_write, 57967207b96SArnd Bergmann .llseek = generic_file_llseek, 58067207b96SArnd Bergmann }; 58167207b96SArnd Bergmann 58267207b96SArnd Bergmann /* generic open function for all pipe-like files */ 58367207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 58467207b96SArnd Bergmann { 58567207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 58667207b96SArnd Bergmann file->private_data = i->i_ctx; 58767207b96SArnd Bergmann 58867207b96SArnd Bergmann return nonseekable_open(inode, file); 58967207b96SArnd Bergmann } 59067207b96SArnd Bergmann 591cdcc89bbSArnd Bergmann /* 592cdcc89bbSArnd Bergmann * Read as many bytes from the mailbox as possible, until 593cdcc89bbSArnd Bergmann * one of the conditions becomes true: 594cdcc89bbSArnd Bergmann * 595cdcc89bbSArnd Bergmann * - no more data available in the mailbox 596cdcc89bbSArnd Bergmann * - end of the user provided buffer 597cdcc89bbSArnd Bergmann * - end of the mapped area 598cdcc89bbSArnd Bergmann */ 59967207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 60067207b96SArnd Bergmann size_t len, loff_t *pos) 60167207b96SArnd Bergmann { 6028b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 603cdcc89bbSArnd Bergmann u32 mbox_data, __user *udata; 604cdcc89bbSArnd Bergmann ssize_t count; 60567207b96SArnd Bergmann 60667207b96SArnd Bergmann if (len < 4) 60767207b96SArnd Bergmann return -EINVAL; 60867207b96SArnd Bergmann 609cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 61067207b96SArnd Bergmann return -EFAULT; 61167207b96SArnd Bergmann 612cdcc89bbSArnd Bergmann udata = (void __user *)buf; 613cdcc89bbSArnd Bergmann 614cdcc89bbSArnd Bergmann spu_acquire(ctx); 615274cef5eSArnd Bergmann for (count = 0; (count + 4) <= len; count += 4, udata++) { 616cdcc89bbSArnd Bergmann int ret; 617cdcc89bbSArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 618cdcc89bbSArnd Bergmann if (ret == 0) 619cdcc89bbSArnd Bergmann break; 620cdcc89bbSArnd Bergmann 621cdcc89bbSArnd Bergmann /* 622cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 623cdcc89bbSArnd Bergmann * but still need to return the data we have 624cdcc89bbSArnd Bergmann * read successfully so far. 625cdcc89bbSArnd Bergmann */ 626cdcc89bbSArnd Bergmann ret = __put_user(mbox_data, udata); 627cdcc89bbSArnd Bergmann if (ret) { 628cdcc89bbSArnd Bergmann if (!count) 629cdcc89bbSArnd Bergmann count = -EFAULT; 630cdcc89bbSArnd Bergmann break; 631cdcc89bbSArnd Bergmann } 632cdcc89bbSArnd Bergmann } 633cdcc89bbSArnd Bergmann spu_release(ctx); 634cdcc89bbSArnd Bergmann 635cdcc89bbSArnd Bergmann if (!count) 636cdcc89bbSArnd Bergmann count = -EAGAIN; 637cdcc89bbSArnd Bergmann 638cdcc89bbSArnd Bergmann return count; 63967207b96SArnd Bergmann } 64067207b96SArnd Bergmann 6415dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_fops = { 64267207b96SArnd Bergmann .open = spufs_pipe_open, 64367207b96SArnd Bergmann .read = spufs_mbox_read, 64467207b96SArnd Bergmann }; 64567207b96SArnd Bergmann 64667207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 64767207b96SArnd Bergmann size_t len, loff_t *pos) 64867207b96SArnd Bergmann { 6498b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 65067207b96SArnd Bergmann u32 mbox_stat; 65167207b96SArnd Bergmann 65267207b96SArnd Bergmann if (len < 4) 65367207b96SArnd Bergmann return -EINVAL; 65467207b96SArnd Bergmann 6558b3d6663SArnd Bergmann spu_acquire(ctx); 6568b3d6663SArnd Bergmann 6578b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 6588b3d6663SArnd Bergmann 6598b3d6663SArnd Bergmann spu_release(ctx); 66067207b96SArnd Bergmann 66167207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 66267207b96SArnd Bergmann return -EFAULT; 66367207b96SArnd Bergmann 66467207b96SArnd Bergmann return 4; 66567207b96SArnd Bergmann } 66667207b96SArnd Bergmann 6675dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_stat_fops = { 66867207b96SArnd Bergmann .open = spufs_pipe_open, 66967207b96SArnd Bergmann .read = spufs_mbox_stat_read, 67067207b96SArnd Bergmann }; 67167207b96SArnd Bergmann 67267207b96SArnd Bergmann /* low-level ibox access function */ 6738b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 67467207b96SArnd Bergmann { 6758b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 67667207b96SArnd Bergmann } 67767207b96SArnd Bergmann 67867207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on) 67967207b96SArnd Bergmann { 6808b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 6818b3d6663SArnd Bergmann 6828b3d6663SArnd Bergmann return fasync_helper(fd, file, on, &ctx->ibox_fasync); 6838b3d6663SArnd Bergmann } 6848b3d6663SArnd Bergmann 6858b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 6868b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 6878b3d6663SArnd Bergmann { 6888b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 6898b3d6663SArnd Bergmann 690e65c2f6fSLuke Browning if (!ctx) 691e65c2f6fSLuke Browning return; 692e65c2f6fSLuke Browning 6938b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 6948b3d6663SArnd Bergmann kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); 69567207b96SArnd Bergmann } 69667207b96SArnd Bergmann 697cdcc89bbSArnd Bergmann /* 698cdcc89bbSArnd Bergmann * Read as many bytes from the interrupt mailbox as possible, until 699cdcc89bbSArnd Bergmann * one of the conditions becomes true: 700cdcc89bbSArnd Bergmann * 701cdcc89bbSArnd Bergmann * - no more data available in the mailbox 702cdcc89bbSArnd Bergmann * - end of the user provided buffer 703cdcc89bbSArnd Bergmann * - end of the mapped area 704cdcc89bbSArnd Bergmann * 705cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 706cdcc89bbSArnd Bergmann * any data is available, but return when we have been able to 707cdcc89bbSArnd Bergmann * read something. 708cdcc89bbSArnd Bergmann */ 70967207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 71067207b96SArnd Bergmann size_t len, loff_t *pos) 71167207b96SArnd Bergmann { 7128b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 713cdcc89bbSArnd Bergmann u32 ibox_data, __user *udata; 714cdcc89bbSArnd Bergmann ssize_t count; 71567207b96SArnd Bergmann 71667207b96SArnd Bergmann if (len < 4) 71767207b96SArnd Bergmann return -EINVAL; 71867207b96SArnd Bergmann 719cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 720cdcc89bbSArnd Bergmann return -EFAULT; 721cdcc89bbSArnd Bergmann 722cdcc89bbSArnd Bergmann udata = (void __user *)buf; 723cdcc89bbSArnd Bergmann 7248b3d6663SArnd Bergmann spu_acquire(ctx); 72567207b96SArnd Bergmann 726cdcc89bbSArnd Bergmann /* wait only for the first element */ 727cdcc89bbSArnd Bergmann count = 0; 72867207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 7298b3d6663SArnd Bergmann if (!spu_ibox_read(ctx, &ibox_data)) 730cdcc89bbSArnd Bergmann count = -EAGAIN; 73167207b96SArnd Bergmann } else { 732cdcc89bbSArnd Bergmann count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 733cdcc89bbSArnd Bergmann } 734cdcc89bbSArnd Bergmann if (count) 735cdcc89bbSArnd Bergmann goto out; 736cdcc89bbSArnd Bergmann 737cdcc89bbSArnd Bergmann /* if we can't write at all, return -EFAULT */ 738cdcc89bbSArnd Bergmann count = __put_user(ibox_data, udata); 739cdcc89bbSArnd Bergmann if (count) 740cdcc89bbSArnd Bergmann goto out; 741cdcc89bbSArnd Bergmann 742cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 743cdcc89bbSArnd Bergmann int ret; 744cdcc89bbSArnd Bergmann ret = ctx->ops->ibox_read(ctx, &ibox_data); 745cdcc89bbSArnd Bergmann if (ret == 0) 746cdcc89bbSArnd Bergmann break; 747cdcc89bbSArnd Bergmann /* 748cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 749cdcc89bbSArnd Bergmann * but still need to return the data we have 750cdcc89bbSArnd Bergmann * read successfully so far. 751cdcc89bbSArnd Bergmann */ 752cdcc89bbSArnd Bergmann ret = __put_user(ibox_data, udata); 753cdcc89bbSArnd Bergmann if (ret) 754cdcc89bbSArnd Bergmann break; 75567207b96SArnd Bergmann } 75667207b96SArnd Bergmann 757cdcc89bbSArnd Bergmann out: 7588b3d6663SArnd Bergmann spu_release(ctx); 7598b3d6663SArnd Bergmann 760cdcc89bbSArnd Bergmann return count; 76167207b96SArnd Bergmann } 76267207b96SArnd Bergmann 76367207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) 76467207b96SArnd Bergmann { 7658b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 76667207b96SArnd Bergmann unsigned int mask; 76767207b96SArnd Bergmann 7688b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 76967207b96SArnd Bergmann 7703a843d7cSArnd Bergmann spu_acquire(ctx); 7713a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); 7723a843d7cSArnd Bergmann spu_release(ctx); 77367207b96SArnd Bergmann 77467207b96SArnd Bergmann return mask; 77567207b96SArnd Bergmann } 77667207b96SArnd Bergmann 7775dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_fops = { 77867207b96SArnd Bergmann .open = spufs_pipe_open, 77967207b96SArnd Bergmann .read = spufs_ibox_read, 78067207b96SArnd Bergmann .poll = spufs_ibox_poll, 78167207b96SArnd Bergmann .fasync = spufs_ibox_fasync, 78267207b96SArnd Bergmann }; 78367207b96SArnd Bergmann 78467207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 78567207b96SArnd Bergmann size_t len, loff_t *pos) 78667207b96SArnd Bergmann { 7878b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 78867207b96SArnd Bergmann u32 ibox_stat; 78967207b96SArnd Bergmann 79067207b96SArnd Bergmann if (len < 4) 79167207b96SArnd Bergmann return -EINVAL; 79267207b96SArnd Bergmann 7938b3d6663SArnd Bergmann spu_acquire(ctx); 7948b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 7958b3d6663SArnd Bergmann spu_release(ctx); 79667207b96SArnd Bergmann 79767207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 79867207b96SArnd Bergmann return -EFAULT; 79967207b96SArnd Bergmann 80067207b96SArnd Bergmann return 4; 80167207b96SArnd Bergmann } 80267207b96SArnd Bergmann 8035dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_stat_fops = { 80467207b96SArnd Bergmann .open = spufs_pipe_open, 80567207b96SArnd Bergmann .read = spufs_ibox_stat_read, 80667207b96SArnd Bergmann }; 80767207b96SArnd Bergmann 80867207b96SArnd Bergmann /* low-level mailbox write */ 8098b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 81067207b96SArnd Bergmann { 8118b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 81267207b96SArnd Bergmann } 81367207b96SArnd Bergmann 81467207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on) 81567207b96SArnd Bergmann { 8168b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 8178b3d6663SArnd Bergmann int ret; 8188b3d6663SArnd Bergmann 8198b3d6663SArnd Bergmann ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); 8208b3d6663SArnd Bergmann 8218b3d6663SArnd Bergmann return ret; 8228b3d6663SArnd Bergmann } 8238b3d6663SArnd Bergmann 8248b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 8258b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 8268b3d6663SArnd Bergmann { 8278b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 8288b3d6663SArnd Bergmann 829e65c2f6fSLuke Browning if (!ctx) 830e65c2f6fSLuke Browning return; 831e65c2f6fSLuke Browning 8328b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 8338b3d6663SArnd Bergmann kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); 83467207b96SArnd Bergmann } 83567207b96SArnd Bergmann 836cdcc89bbSArnd Bergmann /* 837cdcc89bbSArnd Bergmann * Write as many bytes to the interrupt mailbox as possible, until 838cdcc89bbSArnd Bergmann * one of the conditions becomes true: 839cdcc89bbSArnd Bergmann * 840cdcc89bbSArnd Bergmann * - the mailbox is full 841cdcc89bbSArnd Bergmann * - end of the user provided buffer 842cdcc89bbSArnd Bergmann * - end of the mapped area 843cdcc89bbSArnd Bergmann * 844cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 845cdcc89bbSArnd Bergmann * space is availabyl, but return when we have been able to 846cdcc89bbSArnd Bergmann * write something. 847cdcc89bbSArnd Bergmann */ 84867207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 84967207b96SArnd Bergmann size_t len, loff_t *pos) 85067207b96SArnd Bergmann { 8518b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 852cdcc89bbSArnd Bergmann u32 wbox_data, __user *udata; 853cdcc89bbSArnd Bergmann ssize_t count; 85467207b96SArnd Bergmann 85567207b96SArnd Bergmann if (len < 4) 85667207b96SArnd Bergmann return -EINVAL; 85767207b96SArnd Bergmann 858cdcc89bbSArnd Bergmann udata = (void __user *)buf; 859cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_READ, buf, len)) 860cdcc89bbSArnd Bergmann return -EFAULT; 861cdcc89bbSArnd Bergmann 862cdcc89bbSArnd Bergmann if (__get_user(wbox_data, udata)) 86367207b96SArnd Bergmann return -EFAULT; 86467207b96SArnd Bergmann 8658b3d6663SArnd Bergmann spu_acquire(ctx); 8668b3d6663SArnd Bergmann 867cdcc89bbSArnd Bergmann /* 868cdcc89bbSArnd Bergmann * make sure we can at least write one element, by waiting 869cdcc89bbSArnd Bergmann * in case of !O_NONBLOCK 870cdcc89bbSArnd Bergmann */ 871cdcc89bbSArnd Bergmann count = 0; 87267207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 8738b3d6663SArnd Bergmann if (!spu_wbox_write(ctx, wbox_data)) 874cdcc89bbSArnd Bergmann count = -EAGAIN; 87567207b96SArnd Bergmann } else { 876cdcc89bbSArnd Bergmann count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 87767207b96SArnd Bergmann } 87867207b96SArnd Bergmann 879cdcc89bbSArnd Bergmann if (count) 880cdcc89bbSArnd Bergmann goto out; 8818b3d6663SArnd Bergmann 88296de0e25SJan Engelhardt /* write as much as possible */ 883cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 884cdcc89bbSArnd Bergmann int ret; 885cdcc89bbSArnd Bergmann ret = __get_user(wbox_data, udata); 886cdcc89bbSArnd Bergmann if (ret) 887cdcc89bbSArnd Bergmann break; 888cdcc89bbSArnd Bergmann 889cdcc89bbSArnd Bergmann ret = spu_wbox_write(ctx, wbox_data); 890cdcc89bbSArnd Bergmann if (ret == 0) 891cdcc89bbSArnd Bergmann break; 892cdcc89bbSArnd Bergmann } 893cdcc89bbSArnd Bergmann 894cdcc89bbSArnd Bergmann out: 895cdcc89bbSArnd Bergmann spu_release(ctx); 896cdcc89bbSArnd Bergmann return count; 89767207b96SArnd Bergmann } 89867207b96SArnd Bergmann 89967207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) 90067207b96SArnd Bergmann { 9018b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 90267207b96SArnd Bergmann unsigned int mask; 90367207b96SArnd Bergmann 9048b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 90567207b96SArnd Bergmann 9063a843d7cSArnd Bergmann spu_acquire(ctx); 9073a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); 9083a843d7cSArnd Bergmann spu_release(ctx); 90967207b96SArnd Bergmann 91067207b96SArnd Bergmann return mask; 91167207b96SArnd Bergmann } 91267207b96SArnd Bergmann 9135dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_fops = { 91467207b96SArnd Bergmann .open = spufs_pipe_open, 91567207b96SArnd Bergmann .write = spufs_wbox_write, 91667207b96SArnd Bergmann .poll = spufs_wbox_poll, 91767207b96SArnd Bergmann .fasync = spufs_wbox_fasync, 91867207b96SArnd Bergmann }; 91967207b96SArnd Bergmann 92067207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 92167207b96SArnd Bergmann size_t len, loff_t *pos) 92267207b96SArnd Bergmann { 9238b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 92467207b96SArnd Bergmann u32 wbox_stat; 92567207b96SArnd Bergmann 92667207b96SArnd Bergmann if (len < 4) 92767207b96SArnd Bergmann return -EINVAL; 92867207b96SArnd Bergmann 9298b3d6663SArnd Bergmann spu_acquire(ctx); 9308b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 9318b3d6663SArnd Bergmann spu_release(ctx); 93267207b96SArnd Bergmann 93367207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 93467207b96SArnd Bergmann return -EFAULT; 93567207b96SArnd Bergmann 93667207b96SArnd Bergmann return 4; 93767207b96SArnd Bergmann } 93867207b96SArnd Bergmann 9395dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_stat_fops = { 94067207b96SArnd Bergmann .open = spufs_pipe_open, 94167207b96SArnd Bergmann .read = spufs_wbox_stat_read, 94267207b96SArnd Bergmann }; 94367207b96SArnd Bergmann 9446df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file) 9456df10a82SMark Nutter { 9466df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 9476df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 94843c2bbd9SChristoph Hellwig 94947d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 9506df10a82SMark Nutter file->private_data = ctx; 95143c2bbd9SChristoph Hellwig if (!i->i_openers++) 9526df10a82SMark Nutter ctx->signal1 = inode->i_mapping; 95347d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 9546df10a82SMark Nutter return nonseekable_open(inode, file); 9556df10a82SMark Nutter } 9566df10a82SMark Nutter 95743c2bbd9SChristoph Hellwig static int 95843c2bbd9SChristoph Hellwig spufs_signal1_release(struct inode *inode, struct file *file) 95943c2bbd9SChristoph Hellwig { 96043c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 96143c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 96243c2bbd9SChristoph Hellwig 96347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 96443c2bbd9SChristoph Hellwig if (!--i->i_openers) 96543c2bbd9SChristoph Hellwig ctx->signal1 = NULL; 96647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 96743c2bbd9SChristoph Hellwig return 0; 96843c2bbd9SChristoph Hellwig } 96943c2bbd9SChristoph Hellwig 970bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal1_read(struct spu_context *ctx, char __user *buf, 97167207b96SArnd Bergmann size_t len, loff_t *pos) 97267207b96SArnd Bergmann { 97317f88cebSDwayne Grant McConnell int ret = 0; 97467207b96SArnd Bergmann u32 data; 97567207b96SArnd Bergmann 97667207b96SArnd Bergmann if (len < 4) 97767207b96SArnd Bergmann return -EINVAL; 97867207b96SArnd Bergmann 97917f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[3]) { 98017f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[3]; 98117f88cebSDwayne Grant McConnell ret = 4; 98217f88cebSDwayne Grant McConnell } 9838b3d6663SArnd Bergmann 98417f88cebSDwayne Grant McConnell if (!ret) 98517f88cebSDwayne Grant McConnell goto out; 98617f88cebSDwayne Grant McConnell 98767207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 98867207b96SArnd Bergmann return -EFAULT; 98967207b96SArnd Bergmann 99017f88cebSDwayne Grant McConnell out: 99117f88cebSDwayne Grant McConnell return ret; 99267207b96SArnd Bergmann } 99367207b96SArnd Bergmann 994bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 995bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 996bf1ab978SDwayne Grant McConnell { 997bf1ab978SDwayne Grant McConnell int ret; 998bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 999bf1ab978SDwayne Grant McConnell 1000bf1ab978SDwayne Grant McConnell spu_acquire_saved(ctx); 1001bf1ab978SDwayne Grant McConnell ret = __spufs_signal1_read(ctx, buf, len, pos); 100227b1ea09SChristoph Hellwig spu_release_saved(ctx); 1003bf1ab978SDwayne Grant McConnell 1004bf1ab978SDwayne Grant McConnell return ret; 1005bf1ab978SDwayne Grant McConnell } 1006bf1ab978SDwayne Grant McConnell 100767207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 100867207b96SArnd Bergmann size_t len, loff_t *pos) 100967207b96SArnd Bergmann { 101067207b96SArnd Bergmann struct spu_context *ctx; 101167207b96SArnd Bergmann u32 data; 101267207b96SArnd Bergmann 101367207b96SArnd Bergmann ctx = file->private_data; 101467207b96SArnd Bergmann 101567207b96SArnd Bergmann if (len < 4) 101667207b96SArnd Bergmann return -EINVAL; 101767207b96SArnd Bergmann 101867207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 101967207b96SArnd Bergmann return -EFAULT; 102067207b96SArnd Bergmann 10218b3d6663SArnd Bergmann spu_acquire(ctx); 10228b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 10238b3d6663SArnd Bergmann spu_release(ctx); 102467207b96SArnd Bergmann 102567207b96SArnd Bergmann return 4; 102667207b96SArnd Bergmann } 102767207b96SArnd Bergmann 102878bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal1_mmap_nopfn(struct vm_area_struct *vma, 102978bde53eSBenjamin Herrenschmidt unsigned long address) 10306df10a82SMark Nutter { 103127d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000 103278bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x14000, 0x1000); 103327d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000 103427d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 103527d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 103627d5bf2aSBenjamin Herrenschmidt */ 103778bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x10000, 0x10000); 103827d5bf2aSBenjamin Herrenschmidt #else 103927d5bf2aSBenjamin Herrenschmidt #error unsupported page size 104027d5bf2aSBenjamin Herrenschmidt #endif 10416df10a82SMark Nutter } 10426df10a82SMark Nutter 10436df10a82SMark Nutter static struct vm_operations_struct spufs_signal1_mmap_vmops = { 104478bde53eSBenjamin Herrenschmidt .nopfn = spufs_signal1_mmap_nopfn, 10456df10a82SMark Nutter }; 10466df10a82SMark Nutter 10476df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) 10486df10a82SMark Nutter { 10496df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 10506df10a82SMark Nutter return -EINVAL; 10516df10a82SMark Nutter 105278bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 10536df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 105423cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 10556df10a82SMark Nutter 10566df10a82SMark Nutter vma->vm_ops = &spufs_signal1_mmap_vmops; 10576df10a82SMark Nutter return 0; 10586df10a82SMark Nutter } 10596df10a82SMark Nutter 10605dfe4c96SArjan van de Ven static const struct file_operations spufs_signal1_fops = { 10616df10a82SMark Nutter .open = spufs_signal1_open, 106243c2bbd9SChristoph Hellwig .release = spufs_signal1_release, 106367207b96SArnd Bergmann .read = spufs_signal1_read, 106467207b96SArnd Bergmann .write = spufs_signal1_write, 10656df10a82SMark Nutter .mmap = spufs_signal1_mmap, 106667207b96SArnd Bergmann }; 106767207b96SArnd Bergmann 1068d054b36fSJeremy Kerr static const struct file_operations spufs_signal1_nosched_fops = { 1069d054b36fSJeremy Kerr .open = spufs_signal1_open, 1070d054b36fSJeremy Kerr .release = spufs_signal1_release, 1071d054b36fSJeremy Kerr .write = spufs_signal1_write, 1072d054b36fSJeremy Kerr .mmap = spufs_signal1_mmap, 1073d054b36fSJeremy Kerr }; 1074d054b36fSJeremy Kerr 10756df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file) 10766df10a82SMark Nutter { 10776df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 10786df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 107943c2bbd9SChristoph Hellwig 108047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 10816df10a82SMark Nutter file->private_data = ctx; 108243c2bbd9SChristoph Hellwig if (!i->i_openers++) 10836df10a82SMark Nutter ctx->signal2 = inode->i_mapping; 108447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 10856df10a82SMark Nutter return nonseekable_open(inode, file); 10866df10a82SMark Nutter } 10876df10a82SMark Nutter 108843c2bbd9SChristoph Hellwig static int 108943c2bbd9SChristoph Hellwig spufs_signal2_release(struct inode *inode, struct file *file) 109043c2bbd9SChristoph Hellwig { 109143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 109243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 109343c2bbd9SChristoph Hellwig 109447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 109543c2bbd9SChristoph Hellwig if (!--i->i_openers) 109643c2bbd9SChristoph Hellwig ctx->signal2 = NULL; 109747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 109843c2bbd9SChristoph Hellwig return 0; 109943c2bbd9SChristoph Hellwig } 110043c2bbd9SChristoph Hellwig 1101bf1ab978SDwayne Grant McConnell static ssize_t __spufs_signal2_read(struct spu_context *ctx, char __user *buf, 110267207b96SArnd Bergmann size_t len, loff_t *pos) 110367207b96SArnd Bergmann { 110417f88cebSDwayne Grant McConnell int ret = 0; 110567207b96SArnd Bergmann u32 data; 110667207b96SArnd Bergmann 110767207b96SArnd Bergmann if (len < 4) 110867207b96SArnd Bergmann return -EINVAL; 110967207b96SArnd Bergmann 111017f88cebSDwayne Grant McConnell if (ctx->csa.spu_chnlcnt_RW[4]) { 111117f88cebSDwayne Grant McConnell data = ctx->csa.spu_chnldata_RW[4]; 111217f88cebSDwayne Grant McConnell ret = 4; 111317f88cebSDwayne Grant McConnell } 11148b3d6663SArnd Bergmann 111517f88cebSDwayne Grant McConnell if (!ret) 111617f88cebSDwayne Grant McConnell goto out; 111717f88cebSDwayne Grant McConnell 111867207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 111967207b96SArnd Bergmann return -EFAULT; 112067207b96SArnd Bergmann 112117f88cebSDwayne Grant McConnell out: 1122bf1ab978SDwayne Grant McConnell return ret; 1123bf1ab978SDwayne Grant McConnell } 1124bf1ab978SDwayne Grant McConnell 1125bf1ab978SDwayne Grant McConnell static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 1126bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 1127bf1ab978SDwayne Grant McConnell { 1128bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1129bf1ab978SDwayne Grant McConnell int ret; 1130bf1ab978SDwayne Grant McConnell 1131bf1ab978SDwayne Grant McConnell spu_acquire_saved(ctx); 1132bf1ab978SDwayne Grant McConnell ret = __spufs_signal2_read(ctx, buf, len, pos); 113327b1ea09SChristoph Hellwig spu_release_saved(ctx); 1134bf1ab978SDwayne Grant McConnell 1135bf1ab978SDwayne Grant McConnell return ret; 113667207b96SArnd Bergmann } 113767207b96SArnd Bergmann 113867207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 113967207b96SArnd Bergmann size_t len, loff_t *pos) 114067207b96SArnd Bergmann { 114167207b96SArnd Bergmann struct spu_context *ctx; 114267207b96SArnd Bergmann u32 data; 114367207b96SArnd Bergmann 114467207b96SArnd Bergmann ctx = file->private_data; 114567207b96SArnd Bergmann 114667207b96SArnd Bergmann if (len < 4) 114767207b96SArnd Bergmann return -EINVAL; 114867207b96SArnd Bergmann 114967207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 115067207b96SArnd Bergmann return -EFAULT; 115167207b96SArnd Bergmann 11528b3d6663SArnd Bergmann spu_acquire(ctx); 11538b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 11548b3d6663SArnd Bergmann spu_release(ctx); 115567207b96SArnd Bergmann 115667207b96SArnd Bergmann return 4; 115767207b96SArnd Bergmann } 115867207b96SArnd Bergmann 115927d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 116078bde53eSBenjamin Herrenschmidt static unsigned long spufs_signal2_mmap_nopfn(struct vm_area_struct *vma, 116178bde53eSBenjamin Herrenschmidt unsigned long address) 11626df10a82SMark Nutter { 116327d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000 116478bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x1c000, 0x1000); 116527d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000 116627d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 116727d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 116827d5bf2aSBenjamin Herrenschmidt */ 116978bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x10000, 0x10000); 117027d5bf2aSBenjamin Herrenschmidt #else 117127d5bf2aSBenjamin Herrenschmidt #error unsupported page size 117227d5bf2aSBenjamin Herrenschmidt #endif 11736df10a82SMark Nutter } 11746df10a82SMark Nutter 11756df10a82SMark Nutter static struct vm_operations_struct spufs_signal2_mmap_vmops = { 117678bde53eSBenjamin Herrenschmidt .nopfn = spufs_signal2_mmap_nopfn, 11776df10a82SMark Nutter }; 11786df10a82SMark Nutter 11796df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) 11806df10a82SMark Nutter { 11816df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 11826df10a82SMark Nutter return -EINVAL; 11836df10a82SMark Nutter 118478bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 11856df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 118623cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 11876df10a82SMark Nutter 11886df10a82SMark Nutter vma->vm_ops = &spufs_signal2_mmap_vmops; 11896df10a82SMark Nutter return 0; 11906df10a82SMark Nutter } 119127d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 119227d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL 119327d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 11946df10a82SMark Nutter 11955dfe4c96SArjan van de Ven static const struct file_operations spufs_signal2_fops = { 11966df10a82SMark Nutter .open = spufs_signal2_open, 119743c2bbd9SChristoph Hellwig .release = spufs_signal2_release, 119867207b96SArnd Bergmann .read = spufs_signal2_read, 119967207b96SArnd Bergmann .write = spufs_signal2_write, 12006df10a82SMark Nutter .mmap = spufs_signal2_mmap, 120167207b96SArnd Bergmann }; 120267207b96SArnd Bergmann 1203d054b36fSJeremy Kerr static const struct file_operations spufs_signal2_nosched_fops = { 1204d054b36fSJeremy Kerr .open = spufs_signal2_open, 1205d054b36fSJeremy Kerr .release = spufs_signal2_release, 1206d054b36fSJeremy Kerr .write = spufs_signal2_write, 1207d054b36fSJeremy Kerr .mmap = spufs_signal2_mmap, 1208d054b36fSJeremy Kerr }; 1209d054b36fSJeremy Kerr 1210104f0cc2SMichael Ellerman /* 1211104f0cc2SMichael Ellerman * This is a wrapper around DEFINE_SIMPLE_ATTRIBUTE which does the 1212104f0cc2SMichael Ellerman * work of acquiring (or not) the SPU context before calling through 1213104f0cc2SMichael Ellerman * to the actual get routine. The set routine is called directly. 1214104f0cc2SMichael Ellerman */ 1215104f0cc2SMichael Ellerman #define SPU_ATTR_NOACQUIRE 0 1216104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE 1 1217104f0cc2SMichael Ellerman #define SPU_ATTR_ACQUIRE_SAVED 2 1218104f0cc2SMichael Ellerman 1219104f0cc2SMichael Ellerman #define DEFINE_SPUFS_ATTRIBUTE(__name, __get, __set, __fmt, __acquire) \ 1220197b1a82SChristoph Hellwig static int __##__get(void *data, u64 *val) \ 1221104f0cc2SMichael Ellerman { \ 1222104f0cc2SMichael Ellerman struct spu_context *ctx = data; \ 1223104f0cc2SMichael Ellerman \ 1224104f0cc2SMichael Ellerman if (__acquire == SPU_ATTR_ACQUIRE) { \ 1225104f0cc2SMichael Ellerman spu_acquire(ctx); \ 1226197b1a82SChristoph Hellwig *val = __get(ctx); \ 1227104f0cc2SMichael Ellerman spu_release(ctx); \ 1228104f0cc2SMichael Ellerman } else if (__acquire == SPU_ATTR_ACQUIRE_SAVED) { \ 1229104f0cc2SMichael Ellerman spu_acquire_saved(ctx); \ 1230197b1a82SChristoph Hellwig *val = __get(ctx); \ 1231104f0cc2SMichael Ellerman spu_release_saved(ctx); \ 1232104f0cc2SMichael Ellerman } else \ 1233197b1a82SChristoph Hellwig *val = __get(ctx); \ 1234104f0cc2SMichael Ellerman \ 1235197b1a82SChristoph Hellwig return 0; \ 1236104f0cc2SMichael Ellerman } \ 1237197b1a82SChristoph Hellwig DEFINE_SPUFS_SIMPLE_ATTRIBUTE(__name, __##__get, __set, __fmt); 1238104f0cc2SMichael Ellerman 1239197b1a82SChristoph Hellwig static int spufs_signal1_type_set(void *data, u64 val) 124067207b96SArnd Bergmann { 124167207b96SArnd Bergmann struct spu_context *ctx = data; 124267207b96SArnd Bergmann 12438b3d6663SArnd Bergmann spu_acquire(ctx); 12448b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 12458b3d6663SArnd Bergmann spu_release(ctx); 1246197b1a82SChristoph Hellwig 1247197b1a82SChristoph Hellwig return 0; 124867207b96SArnd Bergmann } 124967207b96SArnd Bergmann 1250104f0cc2SMichael Ellerman static u64 spufs_signal1_type_get(struct spu_context *ctx) 1251bf1ab978SDwayne Grant McConnell { 1252bf1ab978SDwayne Grant McConnell return ctx->ops->signal1_type_get(ctx); 1253bf1ab978SDwayne Grant McConnell } 1254104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 1255104f0cc2SMichael Ellerman spufs_signal1_type_set, "%llu", SPU_ATTR_ACQUIRE); 1256bf1ab978SDwayne Grant McConnell 125767207b96SArnd Bergmann 1258197b1a82SChristoph Hellwig static int spufs_signal2_type_set(void *data, u64 val) 125967207b96SArnd Bergmann { 126067207b96SArnd Bergmann struct spu_context *ctx = data; 126167207b96SArnd Bergmann 12628b3d6663SArnd Bergmann spu_acquire(ctx); 12638b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 12648b3d6663SArnd Bergmann spu_release(ctx); 1265197b1a82SChristoph Hellwig 1266197b1a82SChristoph Hellwig return 0; 126767207b96SArnd Bergmann } 126867207b96SArnd Bergmann 1269104f0cc2SMichael Ellerman static u64 spufs_signal2_type_get(struct spu_context *ctx) 1270bf1ab978SDwayne Grant McConnell { 1271bf1ab978SDwayne Grant McConnell return ctx->ops->signal2_type_get(ctx); 1272bf1ab978SDwayne Grant McConnell } 1273104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 1274104f0cc2SMichael Ellerman spufs_signal2_type_set, "%llu", SPU_ATTR_ACQUIRE); 127567207b96SArnd Bergmann 127627d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 127778bde53eSBenjamin Herrenschmidt static unsigned long spufs_mss_mmap_nopfn(struct vm_area_struct *vma, 127878bde53eSBenjamin Herrenschmidt unsigned long address) 1279d9379c4bSarnd@arndb.de { 128078bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x0000, 0x1000); 1281d9379c4bSarnd@arndb.de } 1282d9379c4bSarnd@arndb.de 1283d9379c4bSarnd@arndb.de static struct vm_operations_struct spufs_mss_mmap_vmops = { 128478bde53eSBenjamin Herrenschmidt .nopfn = spufs_mss_mmap_nopfn, 1285d9379c4bSarnd@arndb.de }; 1286d9379c4bSarnd@arndb.de 1287d9379c4bSarnd@arndb.de /* 1288d9379c4bSarnd@arndb.de * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 1289d9379c4bSarnd@arndb.de */ 1290d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) 1291d9379c4bSarnd@arndb.de { 1292d9379c4bSarnd@arndb.de if (!(vma->vm_flags & VM_SHARED)) 1293d9379c4bSarnd@arndb.de return -EINVAL; 1294d9379c4bSarnd@arndb.de 129578bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 1296d9379c4bSarnd@arndb.de vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 129723cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 1298d9379c4bSarnd@arndb.de 1299d9379c4bSarnd@arndb.de vma->vm_ops = &spufs_mss_mmap_vmops; 1300d9379c4bSarnd@arndb.de return 0; 1301d9379c4bSarnd@arndb.de } 130227d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 130327d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL 130427d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1305d9379c4bSarnd@arndb.de 1306d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file) 1307d9379c4bSarnd@arndb.de { 1308d9379c4bSarnd@arndb.de struct spufs_inode_info *i = SPUFS_I(inode); 130917e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 1310d9379c4bSarnd@arndb.de 1311d9379c4bSarnd@arndb.de file->private_data = i->i_ctx; 131243c2bbd9SChristoph Hellwig 131347d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 131443c2bbd9SChristoph Hellwig if (!i->i_openers++) 131517e0e270SBenjamin Herrenschmidt ctx->mss = inode->i_mapping; 131647d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1317d9379c4bSarnd@arndb.de return nonseekable_open(inode, file); 1318d9379c4bSarnd@arndb.de } 1319d9379c4bSarnd@arndb.de 132043c2bbd9SChristoph Hellwig static int 132143c2bbd9SChristoph Hellwig spufs_mss_release(struct inode *inode, struct file *file) 132243c2bbd9SChristoph Hellwig { 132343c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 132443c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 132543c2bbd9SChristoph Hellwig 132647d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 132743c2bbd9SChristoph Hellwig if (!--i->i_openers) 132843c2bbd9SChristoph Hellwig ctx->mss = NULL; 132947d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 133043c2bbd9SChristoph Hellwig return 0; 133143c2bbd9SChristoph Hellwig } 133243c2bbd9SChristoph Hellwig 13335dfe4c96SArjan van de Ven static const struct file_operations spufs_mss_fops = { 1334d9379c4bSarnd@arndb.de .open = spufs_mss_open, 133543c2bbd9SChristoph Hellwig .release = spufs_mss_release, 1336d9379c4bSarnd@arndb.de .mmap = spufs_mss_mmap, 133727d5bf2aSBenjamin Herrenschmidt }; 133827d5bf2aSBenjamin Herrenschmidt 133978bde53eSBenjamin Herrenschmidt static unsigned long spufs_psmap_mmap_nopfn(struct vm_area_struct *vma, 134078bde53eSBenjamin Herrenschmidt unsigned long address) 134127d5bf2aSBenjamin Herrenschmidt { 134278bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x0000, 0x20000); 134327d5bf2aSBenjamin Herrenschmidt } 134427d5bf2aSBenjamin Herrenschmidt 134527d5bf2aSBenjamin Herrenschmidt static struct vm_operations_struct spufs_psmap_mmap_vmops = { 134678bde53eSBenjamin Herrenschmidt .nopfn = spufs_psmap_mmap_nopfn, 134727d5bf2aSBenjamin Herrenschmidt }; 134827d5bf2aSBenjamin Herrenschmidt 134927d5bf2aSBenjamin Herrenschmidt /* 135027d5bf2aSBenjamin Herrenschmidt * mmap support for full problem state area [0x00000 - 0x1ffff]. 135127d5bf2aSBenjamin Herrenschmidt */ 135227d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) 135327d5bf2aSBenjamin Herrenschmidt { 135427d5bf2aSBenjamin Herrenschmidt if (!(vma->vm_flags & VM_SHARED)) 135527d5bf2aSBenjamin Herrenschmidt return -EINVAL; 135627d5bf2aSBenjamin Herrenschmidt 135778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 135827d5bf2aSBenjamin Herrenschmidt vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 135927d5bf2aSBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 136027d5bf2aSBenjamin Herrenschmidt 136127d5bf2aSBenjamin Herrenschmidt vma->vm_ops = &spufs_psmap_mmap_vmops; 136227d5bf2aSBenjamin Herrenschmidt return 0; 136327d5bf2aSBenjamin Herrenschmidt } 136427d5bf2aSBenjamin Herrenschmidt 136527d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file) 136627d5bf2aSBenjamin Herrenschmidt { 136727d5bf2aSBenjamin Herrenschmidt struct spufs_inode_info *i = SPUFS_I(inode); 136817e0e270SBenjamin Herrenschmidt struct spu_context *ctx = i->i_ctx; 136927d5bf2aSBenjamin Herrenschmidt 137047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 137127d5bf2aSBenjamin Herrenschmidt file->private_data = i->i_ctx; 137243c2bbd9SChristoph Hellwig if (!i->i_openers++) 137317e0e270SBenjamin Herrenschmidt ctx->psmap = inode->i_mapping; 137447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 137527d5bf2aSBenjamin Herrenschmidt return nonseekable_open(inode, file); 137627d5bf2aSBenjamin Herrenschmidt } 137727d5bf2aSBenjamin Herrenschmidt 137843c2bbd9SChristoph Hellwig static int 137943c2bbd9SChristoph Hellwig spufs_psmap_release(struct inode *inode, struct file *file) 138043c2bbd9SChristoph Hellwig { 138143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 138243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 138343c2bbd9SChristoph Hellwig 138447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 138543c2bbd9SChristoph Hellwig if (!--i->i_openers) 138643c2bbd9SChristoph Hellwig ctx->psmap = NULL; 138747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 138843c2bbd9SChristoph Hellwig return 0; 138943c2bbd9SChristoph Hellwig } 139043c2bbd9SChristoph Hellwig 13915dfe4c96SArjan van de Ven static const struct file_operations spufs_psmap_fops = { 139227d5bf2aSBenjamin Herrenschmidt .open = spufs_psmap_open, 139343c2bbd9SChristoph Hellwig .release = spufs_psmap_release, 139427d5bf2aSBenjamin Herrenschmidt .mmap = spufs_psmap_mmap, 1395d9379c4bSarnd@arndb.de }; 1396d9379c4bSarnd@arndb.de 1397d9379c4bSarnd@arndb.de 139827d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 139978bde53eSBenjamin Herrenschmidt static unsigned long spufs_mfc_mmap_nopfn(struct vm_area_struct *vma, 140078bde53eSBenjamin Herrenschmidt unsigned long address) 14016df10a82SMark Nutter { 140278bde53eSBenjamin Herrenschmidt return spufs_ps_nopfn(vma, address, 0x3000, 0x1000); 14036df10a82SMark Nutter } 14046df10a82SMark Nutter 14056df10a82SMark Nutter static struct vm_operations_struct spufs_mfc_mmap_vmops = { 140678bde53eSBenjamin Herrenschmidt .nopfn = spufs_mfc_mmap_nopfn, 14076df10a82SMark Nutter }; 14086df10a82SMark Nutter 14096df10a82SMark Nutter /* 14106df10a82SMark Nutter * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 14116df10a82SMark Nutter */ 14126df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) 14136df10a82SMark Nutter { 14146df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 14156df10a82SMark Nutter return -EINVAL; 14166df10a82SMark Nutter 141778bde53eSBenjamin Herrenschmidt vma->vm_flags |= VM_IO | VM_PFNMAP; 14186df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 141923cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 14206df10a82SMark Nutter 14216df10a82SMark Nutter vma->vm_ops = &spufs_mfc_mmap_vmops; 14226df10a82SMark Nutter return 0; 14236df10a82SMark Nutter } 142427d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 142527d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL 142627d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1427a33a7d73SArnd Bergmann 1428a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 1429a33a7d73SArnd Bergmann { 1430a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1431a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 1432a33a7d73SArnd Bergmann 1433a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 1434a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 1435a33a7d73SArnd Bergmann return -EINVAL; 1436a33a7d73SArnd Bergmann 1437a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 1438a33a7d73SArnd Bergmann return -EBUSY; 1439a33a7d73SArnd Bergmann 144047d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 1441a33a7d73SArnd Bergmann file->private_data = ctx; 144243c2bbd9SChristoph Hellwig if (!i->i_openers++) 144317e0e270SBenjamin Herrenschmidt ctx->mfc = inode->i_mapping; 144447d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 1445a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 1446a33a7d73SArnd Bergmann } 1447a33a7d73SArnd Bergmann 144843c2bbd9SChristoph Hellwig static int 144943c2bbd9SChristoph Hellwig spufs_mfc_release(struct inode *inode, struct file *file) 145043c2bbd9SChristoph Hellwig { 145143c2bbd9SChristoph Hellwig struct spufs_inode_info *i = SPUFS_I(inode); 145243c2bbd9SChristoph Hellwig struct spu_context *ctx = i->i_ctx; 145343c2bbd9SChristoph Hellwig 145447d3a5faSChristoph Hellwig mutex_lock(&ctx->mapping_lock); 145543c2bbd9SChristoph Hellwig if (!--i->i_openers) 145643c2bbd9SChristoph Hellwig ctx->mfc = NULL; 145747d3a5faSChristoph Hellwig mutex_unlock(&ctx->mapping_lock); 145843c2bbd9SChristoph Hellwig return 0; 145943c2bbd9SChristoph Hellwig } 146043c2bbd9SChristoph Hellwig 1461a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 1462a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 1463a33a7d73SArnd Bergmann { 1464a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 1465a33a7d73SArnd Bergmann 1466e65c2f6fSLuke Browning if (!ctx) 1467e65c2f6fSLuke Browning return; 1468e65c2f6fSLuke Browning 1469a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 1470a33a7d73SArnd Bergmann 1471a33a7d73SArnd Bergmann pr_debug("%s %s\n", __FUNCTION__, spu->name); 1472a33a7d73SArnd Bergmann if (ctx->mfc_fasync) { 1473a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1474a33a7d73SArnd Bergmann unsigned int mask; 1475a33a7d73SArnd Bergmann 1476a33a7d73SArnd Bergmann /* no need for spu_acquire in interrupt context */ 1477a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1478a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1479a33a7d73SArnd Bergmann 1480a33a7d73SArnd Bergmann mask = 0; 1481a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1482a33a7d73SArnd Bergmann mask |= POLLOUT; 1483a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1484a33a7d73SArnd Bergmann mask |= POLLIN; 1485a33a7d73SArnd Bergmann 1486a33a7d73SArnd Bergmann kill_fasync(&ctx->mfc_fasync, SIGIO, mask); 1487a33a7d73SArnd Bergmann } 1488a33a7d73SArnd Bergmann } 1489a33a7d73SArnd Bergmann 1490a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 1491a33a7d73SArnd Bergmann { 1492a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 1493a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 1494a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 1495a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 1496a33a7d73SArnd Bergmann if (*status) 1497a33a7d73SArnd Bergmann return 1; 1498a33a7d73SArnd Bergmann 1499a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 1500a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 1501a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1502a33a7d73SArnd Bergmann return 0; 1503a33a7d73SArnd Bergmann } 1504a33a7d73SArnd Bergmann 1505a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 1506a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1507a33a7d73SArnd Bergmann { 1508a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1509a33a7d73SArnd Bergmann int ret = -EINVAL; 1510a33a7d73SArnd Bergmann u32 status; 1511a33a7d73SArnd Bergmann 1512a33a7d73SArnd Bergmann if (size != 4) 1513a33a7d73SArnd Bergmann goto out; 1514a33a7d73SArnd Bergmann 1515a33a7d73SArnd Bergmann spu_acquire(ctx); 1516a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1517a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 1518a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 1519a33a7d73SArnd Bergmann ret = -EAGAIN; 1520a33a7d73SArnd Bergmann else 1521a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 1522a33a7d73SArnd Bergmann } else { 1523a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1524a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 1525a33a7d73SArnd Bergmann } 1526a33a7d73SArnd Bergmann spu_release(ctx); 1527a33a7d73SArnd Bergmann 1528a33a7d73SArnd Bergmann if (ret) 1529a33a7d73SArnd Bergmann goto out; 1530a33a7d73SArnd Bergmann 1531a33a7d73SArnd Bergmann ret = 4; 1532a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 1533a33a7d73SArnd Bergmann ret = -EFAULT; 1534a33a7d73SArnd Bergmann 1535a33a7d73SArnd Bergmann out: 1536a33a7d73SArnd Bergmann return ret; 1537a33a7d73SArnd Bergmann } 1538a33a7d73SArnd Bergmann 1539a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 1540a33a7d73SArnd Bergmann { 1541a33a7d73SArnd Bergmann pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, 1542a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 1543a33a7d73SArnd Bergmann 1544a33a7d73SArnd Bergmann switch (cmd->cmd) { 1545a33a7d73SArnd Bergmann case MFC_PUT_CMD: 1546a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 1547a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 1548a33a7d73SArnd Bergmann case MFC_GET_CMD: 1549a33a7d73SArnd Bergmann case MFC_GETF_CMD: 1550a33a7d73SArnd Bergmann case MFC_GETB_CMD: 1551a33a7d73SArnd Bergmann break; 1552a33a7d73SArnd Bergmann default: 1553a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 1554a33a7d73SArnd Bergmann return -EIO; 1555a33a7d73SArnd Bergmann } 1556a33a7d73SArnd Bergmann 1557a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 1558a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment, ea %lx lsa %x\n", 1559a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 1560a33a7d73SArnd Bergmann return -EIO; 1561a33a7d73SArnd Bergmann } 1562a33a7d73SArnd Bergmann 1563a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 1564a33a7d73SArnd Bergmann case 1: 1565a33a7d73SArnd Bergmann break; 1566a33a7d73SArnd Bergmann case 2: 1567a33a7d73SArnd Bergmann if (cmd->lsa & 1) 1568a33a7d73SArnd Bergmann goto error; 1569a33a7d73SArnd Bergmann break; 1570a33a7d73SArnd Bergmann case 4: 1571a33a7d73SArnd Bergmann if (cmd->lsa & 3) 1572a33a7d73SArnd Bergmann goto error; 1573a33a7d73SArnd Bergmann break; 1574a33a7d73SArnd Bergmann case 8: 1575a33a7d73SArnd Bergmann if (cmd->lsa & 7) 1576a33a7d73SArnd Bergmann goto error; 1577a33a7d73SArnd Bergmann break; 1578a33a7d73SArnd Bergmann case 0: 1579a33a7d73SArnd Bergmann if (cmd->lsa & 15) 1580a33a7d73SArnd Bergmann goto error; 1581a33a7d73SArnd Bergmann break; 1582a33a7d73SArnd Bergmann error: 1583a33a7d73SArnd Bergmann default: 1584a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 1585a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 1586a33a7d73SArnd Bergmann return -EIO; 1587a33a7d73SArnd Bergmann } 1588a33a7d73SArnd Bergmann 1589a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 1590a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 1591a33a7d73SArnd Bergmann return -EIO; 1592a33a7d73SArnd Bergmann } 1593a33a7d73SArnd Bergmann 1594a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 1595a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 1596a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 1597a33a7d73SArnd Bergmann return -EIO; 1598a33a7d73SArnd Bergmann } 1599a33a7d73SArnd Bergmann 1600a33a7d73SArnd Bergmann if (cmd->class) { 1601a33a7d73SArnd Bergmann /* not supported in this version */ 1602a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 1603a33a7d73SArnd Bergmann return -EIO; 1604a33a7d73SArnd Bergmann } 1605a33a7d73SArnd Bergmann 1606a33a7d73SArnd Bergmann return 0; 1607a33a7d73SArnd Bergmann } 1608a33a7d73SArnd Bergmann 1609a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 1610a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 1611a33a7d73SArnd Bergmann int *error) 1612a33a7d73SArnd Bergmann { 1613a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1614a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 1615a33a7d73SArnd Bergmann /* wait for any tag group to complete 1616a33a7d73SArnd Bergmann so we have space for the new command */ 1617a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1618a33a7d73SArnd Bergmann /* try again, because the queue might be 1619a33a7d73SArnd Bergmann empty again */ 1620a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1621a33a7d73SArnd Bergmann if (*error == -EAGAIN) 1622a33a7d73SArnd Bergmann return 0; 1623a33a7d73SArnd Bergmann } 1624a33a7d73SArnd Bergmann return 1; 1625a33a7d73SArnd Bergmann } 1626a33a7d73SArnd Bergmann 1627a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 1628a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1629a33a7d73SArnd Bergmann { 1630a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1631a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 1632a33a7d73SArnd Bergmann int ret = -EINVAL; 1633a33a7d73SArnd Bergmann 1634a33a7d73SArnd Bergmann if (size != sizeof cmd) 1635a33a7d73SArnd Bergmann goto out; 1636a33a7d73SArnd Bergmann 1637a33a7d73SArnd Bergmann ret = -EFAULT; 1638a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 1639a33a7d73SArnd Bergmann goto out; 1640a33a7d73SArnd Bergmann 1641a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 1642a33a7d73SArnd Bergmann if (ret) 1643a33a7d73SArnd Bergmann goto out; 1644a33a7d73SArnd Bergmann 164533bfd7a7SArnd Bergmann spu_acquire(ctx); 164633bfd7a7SArnd Bergmann ret = spufs_wait(ctx->run_wq, ctx->state == SPU_STATE_RUNNABLE); 1647577f8f10SAkinobu Mita if (ret) 1648577f8f10SAkinobu Mita goto out; 1649577f8f10SAkinobu Mita 1650a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1651a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 1652a33a7d73SArnd Bergmann } else { 1653a33a7d73SArnd Bergmann int status; 1654a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1655a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 1656a33a7d73SArnd Bergmann if (status) 1657a33a7d73SArnd Bergmann ret = status; 1658a33a7d73SArnd Bergmann } 1659a33a7d73SArnd Bergmann 1660a33a7d73SArnd Bergmann if (ret) 1661933b0e35SKazunori Asayama goto out_unlock; 1662a33a7d73SArnd Bergmann 1663a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 16643692dc66SMasato Noguchi ret = size; 1665a33a7d73SArnd Bergmann 1666933b0e35SKazunori Asayama out_unlock: 1667933b0e35SKazunori Asayama spu_release(ctx); 1668a33a7d73SArnd Bergmann out: 1669a33a7d73SArnd Bergmann return ret; 1670a33a7d73SArnd Bergmann } 1671a33a7d73SArnd Bergmann 1672a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) 1673a33a7d73SArnd Bergmann { 1674a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1675a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1676a33a7d73SArnd Bergmann unsigned int mask; 1677a33a7d73SArnd Bergmann 1678933b0e35SKazunori Asayama poll_wait(file, &ctx->mfc_wq, wait); 1679933b0e35SKazunori Asayama 1680a33a7d73SArnd Bergmann spu_acquire(ctx); 1681a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 1682a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1683a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1684a33a7d73SArnd Bergmann spu_release(ctx); 1685a33a7d73SArnd Bergmann 1686a33a7d73SArnd Bergmann mask = 0; 1687a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1688a33a7d73SArnd Bergmann mask |= POLLOUT | POLLWRNORM; 1689a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1690a33a7d73SArnd Bergmann mask |= POLLIN | POLLRDNORM; 1691a33a7d73SArnd Bergmann 1692a33a7d73SArnd Bergmann pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__, 1693a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 1694a33a7d73SArnd Bergmann 1695a33a7d73SArnd Bergmann return mask; 1696a33a7d73SArnd Bergmann } 1697a33a7d73SArnd Bergmann 169873b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id) 1699a33a7d73SArnd Bergmann { 1700a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1701a33a7d73SArnd Bergmann int ret; 1702a33a7d73SArnd Bergmann 1703a33a7d73SArnd Bergmann spu_acquire(ctx); 1704a33a7d73SArnd Bergmann #if 0 1705a33a7d73SArnd Bergmann /* this currently hangs */ 1706a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1707a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 1708a33a7d73SArnd Bergmann if (ret) 1709a33a7d73SArnd Bergmann goto out; 1710a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1711a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 1712a33a7d73SArnd Bergmann out: 1713a33a7d73SArnd Bergmann #else 1714a33a7d73SArnd Bergmann ret = 0; 1715a33a7d73SArnd Bergmann #endif 1716a33a7d73SArnd Bergmann spu_release(ctx); 1717a33a7d73SArnd Bergmann 1718a33a7d73SArnd Bergmann return ret; 1719a33a7d73SArnd Bergmann } 1720a33a7d73SArnd Bergmann 1721a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, 1722a33a7d73SArnd Bergmann int datasync) 1723a33a7d73SArnd Bergmann { 172473b6af8aSAl Viro return spufs_mfc_flush(file, NULL); 1725a33a7d73SArnd Bergmann } 1726a33a7d73SArnd Bergmann 1727a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on) 1728a33a7d73SArnd Bergmann { 1729a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1730a33a7d73SArnd Bergmann 1731a33a7d73SArnd Bergmann return fasync_helper(fd, file, on, &ctx->mfc_fasync); 1732a33a7d73SArnd Bergmann } 1733a33a7d73SArnd Bergmann 17345dfe4c96SArjan van de Ven static const struct file_operations spufs_mfc_fops = { 1735a33a7d73SArnd Bergmann .open = spufs_mfc_open, 173643c2bbd9SChristoph Hellwig .release = spufs_mfc_release, 1737a33a7d73SArnd Bergmann .read = spufs_mfc_read, 1738a33a7d73SArnd Bergmann .write = spufs_mfc_write, 1739a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 1740a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 1741a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 1742a33a7d73SArnd Bergmann .fasync = spufs_mfc_fasync, 17436df10a82SMark Nutter .mmap = spufs_mfc_mmap, 1744a33a7d73SArnd Bergmann }; 1745a33a7d73SArnd Bergmann 1746197b1a82SChristoph Hellwig static int spufs_npc_set(void *data, u64 val) 174767207b96SArnd Bergmann { 174867207b96SArnd Bergmann struct spu_context *ctx = data; 17498b3d6663SArnd Bergmann spu_acquire(ctx); 17508b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 17518b3d6663SArnd Bergmann spu_release(ctx); 1752197b1a82SChristoph Hellwig 1753197b1a82SChristoph Hellwig return 0; 175467207b96SArnd Bergmann } 175567207b96SArnd Bergmann 1756104f0cc2SMichael Ellerman static u64 spufs_npc_get(struct spu_context *ctx) 175778810ff6SMichael Ellerman { 175878810ff6SMichael Ellerman return ctx->ops->npc_read(ctx); 175978810ff6SMichael Ellerman } 1760104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, 1761104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE); 176267207b96SArnd Bergmann 1763197b1a82SChristoph Hellwig static int spufs_decr_set(void *data, u64 val) 17648b3d6663SArnd Bergmann { 17658b3d6663SArnd Bergmann struct spu_context *ctx = data; 17668b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 17678b3d6663SArnd Bergmann spu_acquire_saved(ctx); 17688b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 176927b1ea09SChristoph Hellwig spu_release_saved(ctx); 1770197b1a82SChristoph Hellwig 1771197b1a82SChristoph Hellwig return 0; 17728b3d6663SArnd Bergmann } 17738b3d6663SArnd Bergmann 1774104f0cc2SMichael Ellerman static u64 spufs_decr_get(struct spu_context *ctx) 17758b3d6663SArnd Bergmann { 17768b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1777bf1ab978SDwayne Grant McConnell return lscsa->decr.slot[0]; 1778bf1ab978SDwayne Grant McConnell } 1779104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 1780104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED); 17818b3d6663SArnd Bergmann 1782197b1a82SChristoph Hellwig static int spufs_decr_status_set(void *data, u64 val) 17838b3d6663SArnd Bergmann { 17848b3d6663SArnd Bergmann struct spu_context *ctx = data; 17858b3d6663SArnd Bergmann spu_acquire_saved(ctx); 1786d40a01d4SMasato Noguchi if (val) 1787d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_DECREMENTER_RUNNING; 1788d40a01d4SMasato Noguchi else 1789d40a01d4SMasato Noguchi ctx->csa.priv2.mfc_control_RW &= ~MFC_CNTL_DECREMENTER_RUNNING; 179027b1ea09SChristoph Hellwig spu_release_saved(ctx); 1791197b1a82SChristoph Hellwig 1792197b1a82SChristoph Hellwig return 0; 17938b3d6663SArnd Bergmann } 17948b3d6663SArnd Bergmann 1795104f0cc2SMichael Ellerman static u64 spufs_decr_status_get(struct spu_context *ctx) 17968b3d6663SArnd Bergmann { 1797d40a01d4SMasato Noguchi if (ctx->csa.priv2.mfc_control_RW & MFC_CNTL_DECREMENTER_RUNNING) 1798d40a01d4SMasato Noguchi return SPU_DECR_STATUS_RUNNING; 1799d40a01d4SMasato Noguchi else 1800d40a01d4SMasato Noguchi return 0; 1801bf1ab978SDwayne Grant McConnell } 1802104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 1803104f0cc2SMichael Ellerman spufs_decr_status_set, "0x%llx\n", 1804104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 18058b3d6663SArnd Bergmann 1806197b1a82SChristoph Hellwig static int spufs_event_mask_set(void *data, u64 val) 18078b3d6663SArnd Bergmann { 18088b3d6663SArnd Bergmann struct spu_context *ctx = data; 18098b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 18108b3d6663SArnd Bergmann spu_acquire_saved(ctx); 18118b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 181227b1ea09SChristoph Hellwig spu_release_saved(ctx); 1813197b1a82SChristoph Hellwig 1814197b1a82SChristoph Hellwig return 0; 18158b3d6663SArnd Bergmann } 18168b3d6663SArnd Bergmann 1817104f0cc2SMichael Ellerman static u64 spufs_event_mask_get(struct spu_context *ctx) 18188b3d6663SArnd Bergmann { 18198b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1820bf1ab978SDwayne Grant McConnell return lscsa->event_mask.slot[0]; 1821bf1ab978SDwayne Grant McConnell } 1822bf1ab978SDwayne Grant McConnell 1823104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 1824104f0cc2SMichael Ellerman spufs_event_mask_set, "0x%llx\n", 1825104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 18268b3d6663SArnd Bergmann 1827104f0cc2SMichael Ellerman static u64 spufs_event_status_get(struct spu_context *ctx) 1828b9e3bd77SDwayne Grant McConnell { 1829b9e3bd77SDwayne Grant McConnell struct spu_state *state = &ctx->csa; 1830b9e3bd77SDwayne Grant McConnell u64 stat; 1831b9e3bd77SDwayne Grant McConnell stat = state->spu_chnlcnt_RW[0]; 1832b9e3bd77SDwayne Grant McConnell if (stat) 1833bf1ab978SDwayne Grant McConnell return state->spu_chnldata_RW[0]; 1834bf1ab978SDwayne Grant McConnell return 0; 1835bf1ab978SDwayne Grant McConnell } 1836104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get, 1837104f0cc2SMichael Ellerman NULL, "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 1838b9e3bd77SDwayne Grant McConnell 1839197b1a82SChristoph Hellwig static int spufs_srr0_set(void *data, u64 val) 18408b3d6663SArnd Bergmann { 18418b3d6663SArnd Bergmann struct spu_context *ctx = data; 18428b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 18438b3d6663SArnd Bergmann spu_acquire_saved(ctx); 18448b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 184527b1ea09SChristoph Hellwig spu_release_saved(ctx); 1846197b1a82SChristoph Hellwig 1847197b1a82SChristoph Hellwig return 0; 18488b3d6663SArnd Bergmann } 18498b3d6663SArnd Bergmann 1850104f0cc2SMichael Ellerman static u64 spufs_srr0_get(struct spu_context *ctx) 18518b3d6663SArnd Bergmann { 18528b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1853104f0cc2SMichael Ellerman return lscsa->srr0.slot[0]; 18548b3d6663SArnd Bergmann } 1855104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 1856104f0cc2SMichael Ellerman "0x%llx\n", SPU_ATTR_ACQUIRE_SAVED) 18578b3d6663SArnd Bergmann 1858104f0cc2SMichael Ellerman static u64 spufs_id_get(struct spu_context *ctx) 18597b1a7014Sarnd@arndb.de { 18607b1a7014Sarnd@arndb.de u64 num; 18617b1a7014Sarnd@arndb.de 18627b1a7014Sarnd@arndb.de if (ctx->state == SPU_STATE_RUNNABLE) 18637b1a7014Sarnd@arndb.de num = ctx->spu->number; 18647b1a7014Sarnd@arndb.de else 18657b1a7014Sarnd@arndb.de num = (unsigned int)-1; 18667b1a7014Sarnd@arndb.de 18677b1a7014Sarnd@arndb.de return num; 18687b1a7014Sarnd@arndb.de } 1869104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n", 1870104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE) 18717b1a7014Sarnd@arndb.de 1872104f0cc2SMichael Ellerman static u64 spufs_object_id_get(struct spu_context *ctx) 1873bf1ab978SDwayne Grant McConnell { 1874bf1ab978SDwayne Grant McConnell /* FIXME: Should there really be no locking here? */ 1875104f0cc2SMichael Ellerman return ctx->object_id; 1876bf1ab978SDwayne Grant McConnell } 1877bf1ab978SDwayne Grant McConnell 1878197b1a82SChristoph Hellwig static int spufs_object_id_set(void *data, u64 id) 187986767277SArnd Bergmann { 188086767277SArnd Bergmann struct spu_context *ctx = data; 188186767277SArnd Bergmann ctx->object_id = id; 1882197b1a82SChristoph Hellwig 1883197b1a82SChristoph Hellwig return 0; 188486767277SArnd Bergmann } 188586767277SArnd Bergmann 1886104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, 1887104f0cc2SMichael Ellerman spufs_object_id_set, "0x%llx\n", SPU_ATTR_NOACQUIRE); 188886767277SArnd Bergmann 1889104f0cc2SMichael Ellerman static u64 spufs_lslr_get(struct spu_context *ctx) 1890bf1ab978SDwayne Grant McConnell { 1891bf1ab978SDwayne Grant McConnell return ctx->csa.priv2.spu_lslr_RW; 1892bf1ab978SDwayne Grant McConnell } 1893104f0cc2SMichael Ellerman DEFINE_SPUFS_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n", 1894104f0cc2SMichael Ellerman SPU_ATTR_ACQUIRE_SAVED); 1895b9e3bd77SDwayne Grant McConnell 1896b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file) 1897b9e3bd77SDwayne Grant McConnell { 1898b9e3bd77SDwayne Grant McConnell struct spufs_inode_info *i = SPUFS_I(inode); 1899b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = i->i_ctx; 1900b9e3bd77SDwayne Grant McConnell file->private_data = ctx; 1901b9e3bd77SDwayne Grant McConnell return 0; 1902b9e3bd77SDwayne Grant McConnell } 1903b9e3bd77SDwayne Grant McConnell 1904cbe709c1SBenjamin Herrenschmidt static int spufs_caps_show(struct seq_file *s, void *private) 1905cbe709c1SBenjamin Herrenschmidt { 1906cbe709c1SBenjamin Herrenschmidt struct spu_context *ctx = s->private; 1907cbe709c1SBenjamin Herrenschmidt 1908cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_NOSCHED)) 1909cbe709c1SBenjamin Herrenschmidt seq_puts(s, "sched\n"); 1910cbe709c1SBenjamin Herrenschmidt if (!(ctx->flags & SPU_CREATE_ISOLATE)) 1911cbe709c1SBenjamin Herrenschmidt seq_puts(s, "step\n"); 1912cbe709c1SBenjamin Herrenschmidt return 0; 1913cbe709c1SBenjamin Herrenschmidt } 1914cbe709c1SBenjamin Herrenschmidt 1915cbe709c1SBenjamin Herrenschmidt static int spufs_caps_open(struct inode *inode, struct file *file) 1916cbe709c1SBenjamin Herrenschmidt { 1917cbe709c1SBenjamin Herrenschmidt return single_open(file, spufs_caps_show, SPUFS_I(inode)->i_ctx); 1918cbe709c1SBenjamin Herrenschmidt } 1919cbe709c1SBenjamin Herrenschmidt 1920cbe709c1SBenjamin Herrenschmidt static const struct file_operations spufs_caps_fops = { 1921cbe709c1SBenjamin Herrenschmidt .open = spufs_caps_open, 1922cbe709c1SBenjamin Herrenschmidt .read = seq_read, 1923cbe709c1SBenjamin Herrenschmidt .llseek = seq_lseek, 1924cbe709c1SBenjamin Herrenschmidt .release = single_release, 1925cbe709c1SBenjamin Herrenschmidt }; 1926cbe709c1SBenjamin Herrenschmidt 1927bf1ab978SDwayne Grant McConnell static ssize_t __spufs_mbox_info_read(struct spu_context *ctx, 1928bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 1929bf1ab978SDwayne Grant McConnell { 1930bf1ab978SDwayne Grant McConnell u32 mbox_stat; 1931bf1ab978SDwayne Grant McConnell u32 data; 1932bf1ab978SDwayne Grant McConnell 1933bf1ab978SDwayne Grant McConnell mbox_stat = ctx->csa.prob.mb_stat_R; 1934bf1ab978SDwayne Grant McConnell if (mbox_stat & 0x0000ff) { 1935bf1ab978SDwayne Grant McConnell data = ctx->csa.prob.pu_mb_R; 1936bf1ab978SDwayne Grant McConnell } 1937bf1ab978SDwayne Grant McConnell 1938bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 1939bf1ab978SDwayne Grant McConnell } 1940bf1ab978SDwayne Grant McConnell 194169a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf, 194269a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 194369a2f00cSDwayne Grant McConnell { 1944bf1ab978SDwayne Grant McConnell int ret; 194569a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 194669a2f00cSDwayne Grant McConnell 194769a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 194869a2f00cSDwayne Grant McConnell return -EFAULT; 194969a2f00cSDwayne Grant McConnell 195069a2f00cSDwayne Grant McConnell spu_acquire_saved(ctx); 195169a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 1952bf1ab978SDwayne Grant McConnell ret = __spufs_mbox_info_read(ctx, buf, len, pos); 195369a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 195427b1ea09SChristoph Hellwig spu_release_saved(ctx); 195569a2f00cSDwayne Grant McConnell 1956bf1ab978SDwayne Grant McConnell return ret; 195769a2f00cSDwayne Grant McConnell } 195869a2f00cSDwayne Grant McConnell 19595dfe4c96SArjan van de Ven static const struct file_operations spufs_mbox_info_fops = { 196069a2f00cSDwayne Grant McConnell .open = spufs_info_open, 196169a2f00cSDwayne Grant McConnell .read = spufs_mbox_info_read, 196269a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 196369a2f00cSDwayne Grant McConnell }; 196469a2f00cSDwayne Grant McConnell 1965bf1ab978SDwayne Grant McConnell static ssize_t __spufs_ibox_info_read(struct spu_context *ctx, 1966bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 1967bf1ab978SDwayne Grant McConnell { 1968bf1ab978SDwayne Grant McConnell u32 ibox_stat; 1969bf1ab978SDwayne Grant McConnell u32 data; 1970bf1ab978SDwayne Grant McConnell 1971bf1ab978SDwayne Grant McConnell ibox_stat = ctx->csa.prob.mb_stat_R; 1972bf1ab978SDwayne Grant McConnell if (ibox_stat & 0xff0000) { 1973bf1ab978SDwayne Grant McConnell data = ctx->csa.priv2.puint_mb_R; 1974bf1ab978SDwayne Grant McConnell } 1975bf1ab978SDwayne Grant McConnell 1976bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 1977bf1ab978SDwayne Grant McConnell } 1978bf1ab978SDwayne Grant McConnell 197969a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf, 198069a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 198169a2f00cSDwayne Grant McConnell { 198269a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1983bf1ab978SDwayne Grant McConnell int ret; 198469a2f00cSDwayne Grant McConnell 198569a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 198669a2f00cSDwayne Grant McConnell return -EFAULT; 198769a2f00cSDwayne Grant McConnell 198869a2f00cSDwayne Grant McConnell spu_acquire_saved(ctx); 198969a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 1990bf1ab978SDwayne Grant McConnell ret = __spufs_ibox_info_read(ctx, buf, len, pos); 199169a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 199227b1ea09SChristoph Hellwig spu_release_saved(ctx); 199369a2f00cSDwayne Grant McConnell 1994bf1ab978SDwayne Grant McConnell return ret; 199569a2f00cSDwayne Grant McConnell } 199669a2f00cSDwayne Grant McConnell 19975dfe4c96SArjan van de Ven static const struct file_operations spufs_ibox_info_fops = { 199869a2f00cSDwayne Grant McConnell .open = spufs_info_open, 199969a2f00cSDwayne Grant McConnell .read = spufs_ibox_info_read, 200069a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 200169a2f00cSDwayne Grant McConnell }; 200269a2f00cSDwayne Grant McConnell 2003bf1ab978SDwayne Grant McConnell static ssize_t __spufs_wbox_info_read(struct spu_context *ctx, 2004bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2005bf1ab978SDwayne Grant McConnell { 2006bf1ab978SDwayne Grant McConnell int i, cnt; 2007bf1ab978SDwayne Grant McConnell u32 data[4]; 2008bf1ab978SDwayne Grant McConnell u32 wbox_stat; 2009bf1ab978SDwayne Grant McConnell 2010bf1ab978SDwayne Grant McConnell wbox_stat = ctx->csa.prob.mb_stat_R; 2011bf1ab978SDwayne Grant McConnell cnt = 4 - ((wbox_stat & 0x00ff00) >> 8); 2012bf1ab978SDwayne Grant McConnell for (i = 0; i < cnt; i++) { 2013bf1ab978SDwayne Grant McConnell data[i] = ctx->csa.spu_mailbox_data[i]; 2014bf1ab978SDwayne Grant McConnell } 2015bf1ab978SDwayne Grant McConnell 2016bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, 2017bf1ab978SDwayne Grant McConnell cnt * sizeof(u32)); 2018bf1ab978SDwayne Grant McConnell } 2019bf1ab978SDwayne Grant McConnell 202069a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf, 202169a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 202269a2f00cSDwayne Grant McConnell { 202369a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2024bf1ab978SDwayne Grant McConnell int ret; 202569a2f00cSDwayne Grant McConnell 202669a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 202769a2f00cSDwayne Grant McConnell return -EFAULT; 202869a2f00cSDwayne Grant McConnell 202969a2f00cSDwayne Grant McConnell spu_acquire_saved(ctx); 203069a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2031bf1ab978SDwayne Grant McConnell ret = __spufs_wbox_info_read(ctx, buf, len, pos); 203269a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 203327b1ea09SChristoph Hellwig spu_release_saved(ctx); 203469a2f00cSDwayne Grant McConnell 2035bf1ab978SDwayne Grant McConnell return ret; 203669a2f00cSDwayne Grant McConnell } 203769a2f00cSDwayne Grant McConnell 20385dfe4c96SArjan van de Ven static const struct file_operations spufs_wbox_info_fops = { 203969a2f00cSDwayne Grant McConnell .open = spufs_info_open, 204069a2f00cSDwayne Grant McConnell .read = spufs_wbox_info_read, 204169a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 204269a2f00cSDwayne Grant McConnell }; 204369a2f00cSDwayne Grant McConnell 2044bf1ab978SDwayne Grant McConnell static ssize_t __spufs_dma_info_read(struct spu_context *ctx, 2045bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2046b9e3bd77SDwayne Grant McConnell { 2047b9e3bd77SDwayne Grant McConnell struct spu_dma_info info; 2048b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *spuqp; 2049b9e3bd77SDwayne Grant McConnell int i; 2050b9e3bd77SDwayne Grant McConnell 2051b9e3bd77SDwayne Grant McConnell info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW; 2052b9e3bd77SDwayne Grant McConnell info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0]; 2053b9e3bd77SDwayne Grant McConnell info.dma_info_status = ctx->csa.spu_chnldata_RW[24]; 2054b9e3bd77SDwayne Grant McConnell info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25]; 2055b9e3bd77SDwayne Grant McConnell info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27]; 2056b9e3bd77SDwayne Grant McConnell for (i = 0; i < 16; i++) { 2057b9e3bd77SDwayne Grant McConnell qp = &info.dma_info_command_data[i]; 2058b9e3bd77SDwayne Grant McConnell spuqp = &ctx->csa.priv2.spuq[i]; 2059b9e3bd77SDwayne Grant McConnell 2060b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW; 2061b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW; 2062b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW; 2063b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW; 2064b9e3bd77SDwayne Grant McConnell } 2065b9e3bd77SDwayne Grant McConnell 2066b9e3bd77SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2067b9e3bd77SDwayne Grant McConnell sizeof info); 2068b9e3bd77SDwayne Grant McConnell } 2069b9e3bd77SDwayne Grant McConnell 2070bf1ab978SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, 2071bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2072bf1ab978SDwayne Grant McConnell { 2073bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2074bf1ab978SDwayne Grant McConnell int ret; 2075bf1ab978SDwayne Grant McConnell 2076bf1ab978SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2077bf1ab978SDwayne Grant McConnell return -EFAULT; 2078bf1ab978SDwayne Grant McConnell 2079bf1ab978SDwayne Grant McConnell spu_acquire_saved(ctx); 2080bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2081bf1ab978SDwayne Grant McConnell ret = __spufs_dma_info_read(ctx, buf, len, pos); 2082bf1ab978SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 208327b1ea09SChristoph Hellwig spu_release_saved(ctx); 2084bf1ab978SDwayne Grant McConnell 2085bf1ab978SDwayne Grant McConnell return ret; 2086bf1ab978SDwayne Grant McConnell } 2087bf1ab978SDwayne Grant McConnell 20885dfe4c96SArjan van de Ven static const struct file_operations spufs_dma_info_fops = { 2089b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2090b9e3bd77SDwayne Grant McConnell .read = spufs_dma_info_read, 2091b9e3bd77SDwayne Grant McConnell }; 2092b9e3bd77SDwayne Grant McConnell 2093bf1ab978SDwayne Grant McConnell static ssize_t __spufs_proxydma_info_read(struct spu_context *ctx, 2094bf1ab978SDwayne Grant McConnell char __user *buf, size_t len, loff_t *pos) 2095b9e3bd77SDwayne Grant McConnell { 2096b9e3bd77SDwayne Grant McConnell struct spu_proxydma_info info; 2097b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *puqp; 2098bf1ab978SDwayne Grant McConnell int ret = sizeof info; 2099b9e3bd77SDwayne Grant McConnell int i; 2100b9e3bd77SDwayne Grant McConnell 2101b9e3bd77SDwayne Grant McConnell if (len < ret) 2102b9e3bd77SDwayne Grant McConnell return -EINVAL; 2103b9e3bd77SDwayne Grant McConnell 2104b9e3bd77SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 2105b9e3bd77SDwayne Grant McConnell return -EFAULT; 2106b9e3bd77SDwayne Grant McConnell 2107b9e3bd77SDwayne Grant McConnell info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW; 2108b9e3bd77SDwayne Grant McConnell info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW; 2109b9e3bd77SDwayne Grant McConnell info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R; 2110b9e3bd77SDwayne Grant McConnell for (i = 0; i < 8; i++) { 2111b9e3bd77SDwayne Grant McConnell qp = &info.proxydma_info_command_data[i]; 2112b9e3bd77SDwayne Grant McConnell puqp = &ctx->csa.priv2.puq[i]; 2113b9e3bd77SDwayne Grant McConnell 2114b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW; 2115b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW; 2116b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW; 2117b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW; 2118b9e3bd77SDwayne Grant McConnell } 2119bf1ab978SDwayne Grant McConnell 2120bf1ab978SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 2121bf1ab978SDwayne Grant McConnell sizeof info); 2122bf1ab978SDwayne Grant McConnell } 2123bf1ab978SDwayne Grant McConnell 2124bf1ab978SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, 2125bf1ab978SDwayne Grant McConnell size_t len, loff_t *pos) 2126bf1ab978SDwayne Grant McConnell { 2127bf1ab978SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 2128bf1ab978SDwayne Grant McConnell int ret; 2129bf1ab978SDwayne Grant McConnell 2130bf1ab978SDwayne Grant McConnell spu_acquire_saved(ctx); 2131bf1ab978SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 2132bf1ab978SDwayne Grant McConnell ret = __spufs_proxydma_info_read(ctx, buf, len, pos); 2133b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 213427b1ea09SChristoph Hellwig spu_release_saved(ctx); 2135b9e3bd77SDwayne Grant McConnell 2136b9e3bd77SDwayne Grant McConnell return ret; 2137b9e3bd77SDwayne Grant McConnell } 2138b9e3bd77SDwayne Grant McConnell 21395dfe4c96SArjan van de Ven static const struct file_operations spufs_proxydma_info_fops = { 2140b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 2141b9e3bd77SDwayne Grant McConnell .read = spufs_proxydma_info_read, 2142b9e3bd77SDwayne Grant McConnell }; 2143b9e3bd77SDwayne Grant McConnell 2144476273adSChristoph Hellwig static int spufs_show_tid(struct seq_file *s, void *private) 2145476273adSChristoph Hellwig { 2146476273adSChristoph Hellwig struct spu_context *ctx = s->private; 2147476273adSChristoph Hellwig 2148476273adSChristoph Hellwig seq_printf(s, "%d\n", ctx->tid); 2149476273adSChristoph Hellwig return 0; 2150476273adSChristoph Hellwig } 2151476273adSChristoph Hellwig 2152476273adSChristoph Hellwig static int spufs_tid_open(struct inode *inode, struct file *file) 2153476273adSChristoph Hellwig { 2154476273adSChristoph Hellwig return single_open(file, spufs_show_tid, SPUFS_I(inode)->i_ctx); 2155476273adSChristoph Hellwig } 2156476273adSChristoph Hellwig 2157476273adSChristoph Hellwig static const struct file_operations spufs_tid_fops = { 2158476273adSChristoph Hellwig .open = spufs_tid_open, 2159476273adSChristoph Hellwig .read = seq_read, 2160476273adSChristoph Hellwig .llseek = seq_lseek, 2161476273adSChristoph Hellwig .release = single_release, 2162476273adSChristoph Hellwig }; 2163476273adSChristoph Hellwig 2164e9f8a0b6SChristoph Hellwig static const char *ctx_state_names[] = { 2165e9f8a0b6SChristoph Hellwig "user", "system", "iowait", "loaded" 2166e9f8a0b6SChristoph Hellwig }; 2167e9f8a0b6SChristoph Hellwig 2168e9f8a0b6SChristoph Hellwig static unsigned long long spufs_acct_time(struct spu_context *ctx, 216927ec41d3SAndre Detsch enum spu_utilization_state state) 2170e9f8a0b6SChristoph Hellwig { 217127ec41d3SAndre Detsch struct timespec ts; 217227ec41d3SAndre Detsch unsigned long long time = ctx->stats.times[state]; 2173e9f8a0b6SChristoph Hellwig 217427ec41d3SAndre Detsch /* 217527ec41d3SAndre Detsch * In general, utilization statistics are updated by the controlling 217627ec41d3SAndre Detsch * thread as the spu context moves through various well defined 217727ec41d3SAndre Detsch * state transitions, but if the context is lazily loaded its 217827ec41d3SAndre Detsch * utilization statistics are not updated as the controlling thread 217927ec41d3SAndre Detsch * is not tightly coupled with the execution of the spu context. We 218027ec41d3SAndre Detsch * calculate and apply the time delta from the last recorded state 218127ec41d3SAndre Detsch * of the spu context. 218227ec41d3SAndre Detsch */ 218327ec41d3SAndre Detsch if (ctx->spu && ctx->stats.util_state == state) { 218427ec41d3SAndre Detsch ktime_get_ts(&ts); 218527ec41d3SAndre Detsch time += timespec_to_ns(&ts) - ctx->stats.tstamp; 218627ec41d3SAndre Detsch } 2187e9f8a0b6SChristoph Hellwig 218827ec41d3SAndre Detsch return time / NSEC_PER_MSEC; 2189e9f8a0b6SChristoph Hellwig } 2190e9f8a0b6SChristoph Hellwig 2191e9f8a0b6SChristoph Hellwig static unsigned long long spufs_slb_flts(struct spu_context *ctx) 2192e9f8a0b6SChristoph Hellwig { 2193e9f8a0b6SChristoph Hellwig unsigned long long slb_flts = ctx->stats.slb_flt; 2194e9f8a0b6SChristoph Hellwig 2195e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2196e9f8a0b6SChristoph Hellwig slb_flts += (ctx->spu->stats.slb_flt - 2197e9f8a0b6SChristoph Hellwig ctx->stats.slb_flt_base); 2198e9f8a0b6SChristoph Hellwig } 2199e9f8a0b6SChristoph Hellwig 2200e9f8a0b6SChristoph Hellwig return slb_flts; 2201e9f8a0b6SChristoph Hellwig } 2202e9f8a0b6SChristoph Hellwig 2203e9f8a0b6SChristoph Hellwig static unsigned long long spufs_class2_intrs(struct spu_context *ctx) 2204e9f8a0b6SChristoph Hellwig { 2205e9f8a0b6SChristoph Hellwig unsigned long long class2_intrs = ctx->stats.class2_intr; 2206e9f8a0b6SChristoph Hellwig 2207e9f8a0b6SChristoph Hellwig if (ctx->state == SPU_STATE_RUNNABLE) { 2208e9f8a0b6SChristoph Hellwig class2_intrs += (ctx->spu->stats.class2_intr - 2209e9f8a0b6SChristoph Hellwig ctx->stats.class2_intr_base); 2210e9f8a0b6SChristoph Hellwig } 2211e9f8a0b6SChristoph Hellwig 2212e9f8a0b6SChristoph Hellwig return class2_intrs; 2213e9f8a0b6SChristoph Hellwig } 2214e9f8a0b6SChristoph Hellwig 2215e9f8a0b6SChristoph Hellwig 2216e9f8a0b6SChristoph Hellwig static int spufs_show_stat(struct seq_file *s, void *private) 2217e9f8a0b6SChristoph Hellwig { 2218e9f8a0b6SChristoph Hellwig struct spu_context *ctx = s->private; 2219e9f8a0b6SChristoph Hellwig 2220e9f8a0b6SChristoph Hellwig spu_acquire(ctx); 2221e9f8a0b6SChristoph Hellwig seq_printf(s, "%s %llu %llu %llu %llu " 2222e9f8a0b6SChristoph Hellwig "%llu %llu %llu %llu %llu %llu %llu %llu\n", 222327ec41d3SAndre Detsch ctx_state_names[ctx->stats.util_state], 222427ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_USER), 222527ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_SYSTEM), 222627ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IOWAIT), 222727ec41d3SAndre Detsch spufs_acct_time(ctx, SPU_UTIL_IDLE_LOADED), 2228e9f8a0b6SChristoph Hellwig ctx->stats.vol_ctx_switch, 2229e9f8a0b6SChristoph Hellwig ctx->stats.invol_ctx_switch, 2230e9f8a0b6SChristoph Hellwig spufs_slb_flts(ctx), 2231e9f8a0b6SChristoph Hellwig ctx->stats.hash_flt, 2232e9f8a0b6SChristoph Hellwig ctx->stats.min_flt, 2233e9f8a0b6SChristoph Hellwig ctx->stats.maj_flt, 2234e9f8a0b6SChristoph Hellwig spufs_class2_intrs(ctx), 2235e9f8a0b6SChristoph Hellwig ctx->stats.libassist); 2236e9f8a0b6SChristoph Hellwig spu_release(ctx); 2237e9f8a0b6SChristoph Hellwig return 0; 2238e9f8a0b6SChristoph Hellwig } 2239e9f8a0b6SChristoph Hellwig 2240e9f8a0b6SChristoph Hellwig static int spufs_stat_open(struct inode *inode, struct file *file) 2241e9f8a0b6SChristoph Hellwig { 2242e9f8a0b6SChristoph Hellwig return single_open(file, spufs_show_stat, SPUFS_I(inode)->i_ctx); 2243e9f8a0b6SChristoph Hellwig } 2244e9f8a0b6SChristoph Hellwig 2245e9f8a0b6SChristoph Hellwig static const struct file_operations spufs_stat_fops = { 2246e9f8a0b6SChristoph Hellwig .open = spufs_stat_open, 2247e9f8a0b6SChristoph Hellwig .read = seq_read, 2248e9f8a0b6SChristoph Hellwig .llseek = seq_lseek, 2249e9f8a0b6SChristoph Hellwig .release = single_release, 2250e9f8a0b6SChristoph Hellwig }; 2251e9f8a0b6SChristoph Hellwig 2252e9f8a0b6SChristoph Hellwig 225367207b96SArnd Bergmann struct tree_descr spufs_dir_contents[] = { 2254cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 225567207b96SArnd Bergmann { "mem", &spufs_mem_fops, 0666, }, 22568b3d6663SArnd Bergmann { "regs", &spufs_regs_fops, 0666, }, 225767207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 225867207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 225967207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 226067207b96SArnd Bergmann { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, 226167207b96SArnd Bergmann { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, 226267207b96SArnd Bergmann { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, 2263603c4612SJeremy Kerr { "signal1", &spufs_signal1_fops, 0666, }, 2264603c4612SJeremy Kerr { "signal2", &spufs_signal2_fops, 0666, }, 226567207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 226667207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 22676df10a82SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 22688b3d6663SArnd Bergmann { "fpcr", &spufs_fpcr_fops, 0666, }, 2269b9e3bd77SDwayne Grant McConnell { "lslr", &spufs_lslr_ops, 0444, }, 2270b9e3bd77SDwayne Grant McConnell { "mfc", &spufs_mfc_fops, 0666, }, 2271b9e3bd77SDwayne Grant McConnell { "mss", &spufs_mss_fops, 0666, }, 2272b9e3bd77SDwayne Grant McConnell { "npc", &spufs_npc_ops, 0666, }, 2273b9e3bd77SDwayne Grant McConnell { "srr0", &spufs_srr0_ops, 0666, }, 22748b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 22758b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 22768b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 2277b9e3bd77SDwayne Grant McConnell { "event_status", &spufs_event_status_ops, 0444, }, 227827d5bf2aSBenjamin Herrenschmidt { "psmap", &spufs_psmap_fops, 0666, }, 227986767277SArnd Bergmann { "phys-id", &spufs_id_ops, 0666, }, 228086767277SArnd Bergmann { "object-id", &spufs_object_id_ops, 0666, }, 228169a2f00cSDwayne Grant McConnell { "mbox_info", &spufs_mbox_info_fops, 0444, }, 228269a2f00cSDwayne Grant McConnell { "ibox_info", &spufs_ibox_info_fops, 0444, }, 228369a2f00cSDwayne Grant McConnell { "wbox_info", &spufs_wbox_info_fops, 0444, }, 2284b9e3bd77SDwayne Grant McConnell { "dma_info", &spufs_dma_info_fops, 0444, }, 2285b9e3bd77SDwayne Grant McConnell { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, 2286476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2287e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 228867207b96SArnd Bergmann {}, 228967207b96SArnd Bergmann }; 22905737edd1SMark Nutter 22915737edd1SMark Nutter struct tree_descr spufs_dir_nosched_contents[] = { 2292cbe709c1SBenjamin Herrenschmidt { "capabilities", &spufs_caps_fops, 0444, }, 22935737edd1SMark Nutter { "mem", &spufs_mem_fops, 0666, }, 22945737edd1SMark Nutter { "mbox", &spufs_mbox_fops, 0444, }, 22955737edd1SMark Nutter { "ibox", &spufs_ibox_fops, 0444, }, 22965737edd1SMark Nutter { "wbox", &spufs_wbox_fops, 0222, }, 22975737edd1SMark Nutter { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, 22985737edd1SMark Nutter { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, 22995737edd1SMark Nutter { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, 2300d054b36fSJeremy Kerr { "signal1", &spufs_signal1_nosched_fops, 0222, }, 2301d054b36fSJeremy Kerr { "signal2", &spufs_signal2_nosched_fops, 0222, }, 23025737edd1SMark Nutter { "signal1_type", &spufs_signal1_type, 0666, }, 23035737edd1SMark Nutter { "signal2_type", &spufs_signal2_type, 0666, }, 23045737edd1SMark Nutter { "mss", &spufs_mss_fops, 0666, }, 23055737edd1SMark Nutter { "mfc", &spufs_mfc_fops, 0666, }, 23065737edd1SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 23075737edd1SMark Nutter { "npc", &spufs_npc_ops, 0666, }, 23085737edd1SMark Nutter { "psmap", &spufs_psmap_fops, 0666, }, 23095737edd1SMark Nutter { "phys-id", &spufs_id_ops, 0666, }, 23105737edd1SMark Nutter { "object-id", &spufs_object_id_ops, 0666, }, 2311476273adSChristoph Hellwig { "tid", &spufs_tid_fops, 0444, }, 2312e9f8a0b6SChristoph Hellwig { "stat", &spufs_stat_fops, 0444, }, 23135737edd1SMark Nutter {}, 23145737edd1SMark Nutter }; 2315bf1ab978SDwayne Grant McConnell 2316bf1ab978SDwayne Grant McConnell struct spufs_coredump_reader spufs_coredump_read[] = { 23174fca9c42SMichael Ellerman { "regs", __spufs_regs_read, NULL, sizeof(struct spu_reg128[128])}, 23184fca9c42SMichael Ellerman { "fpcr", __spufs_fpcr_read, NULL, sizeof(struct spu_reg128) }, 2319104f0cc2SMichael Ellerman { "lslr", NULL, spufs_lslr_get, 19 }, 2320104f0cc2SMichael Ellerman { "decr", NULL, spufs_decr_get, 19 }, 2321104f0cc2SMichael Ellerman { "decr_status", NULL, spufs_decr_status_get, 19 }, 23224fca9c42SMichael Ellerman { "mem", __spufs_mem_read, NULL, LS_SIZE, }, 23234fca9c42SMichael Ellerman { "signal1", __spufs_signal1_read, NULL, sizeof(u32) }, 2324104f0cc2SMichael Ellerman { "signal1_type", NULL, spufs_signal1_type_get, 19 }, 23254fca9c42SMichael Ellerman { "signal2", __spufs_signal2_read, NULL, sizeof(u32) }, 2326104f0cc2SMichael Ellerman { "signal2_type", NULL, spufs_signal2_type_get, 19 }, 2327104f0cc2SMichael Ellerman { "event_mask", NULL, spufs_event_mask_get, 19 }, 2328104f0cc2SMichael Ellerman { "event_status", NULL, spufs_event_status_get, 19 }, 23294fca9c42SMichael Ellerman { "mbox_info", __spufs_mbox_info_read, NULL, sizeof(u32) }, 23304fca9c42SMichael Ellerman { "ibox_info", __spufs_ibox_info_read, NULL, sizeof(u32) }, 23314fca9c42SMichael Ellerman { "wbox_info", __spufs_wbox_info_read, NULL, 4 * sizeof(u32)}, 23324fca9c42SMichael Ellerman { "dma_info", __spufs_dma_info_read, NULL, sizeof(struct spu_dma_info)}, 23334fca9c42SMichael Ellerman { "proxydma_info", __spufs_proxydma_info_read, 23344fca9c42SMichael Ellerman NULL, sizeof(struct spu_proxydma_info)}, 2335104f0cc2SMichael Ellerman { "object-id", NULL, spufs_object_id_get, 19 }, 2336104f0cc2SMichael Ellerman { "npc", NULL, spufs_npc_get, 19 }, 2337936d5bf1SMichael Ellerman { NULL }, 2338bf1ab978SDwayne Grant McConnell }; 2339