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> 3167207b96SArnd Bergmann 3267207b96SArnd Bergmann #include <asm/io.h> 3367207b96SArnd Bergmann #include <asm/semaphore.h> 3467207b96SArnd Bergmann #include <asm/spu.h> 35b9e3bd77SDwayne Grant McConnell #include <asm/spu_info.h> 3667207b96SArnd Bergmann #include <asm/uaccess.h> 3767207b96SArnd Bergmann 3867207b96SArnd Bergmann #include "spufs.h" 3967207b96SArnd Bergmann 4027d5bf2aSBenjamin Herrenschmidt #define SPUFS_MMAP_4K (PAGE_SIZE == 0x1000) 4127d5bf2aSBenjamin Herrenschmidt 428b3d6663SArnd Bergmann 4367207b96SArnd Bergmann static int 4467207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file) 4567207b96SArnd Bergmann { 4667207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 476df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 486df10a82SMark Nutter file->private_data = ctx; 496df10a82SMark Nutter file->f_mapping = inode->i_mapping; 506df10a82SMark Nutter ctx->local_store = inode->i_mapping; 5167207b96SArnd Bergmann return 0; 5267207b96SArnd Bergmann } 5367207b96SArnd Bergmann 5467207b96SArnd Bergmann static ssize_t 5567207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer, 5667207b96SArnd Bergmann size_t size, loff_t *pos) 5767207b96SArnd Bergmann { 588b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 598b3d6663SArnd Bergmann char *local_store; 6067207b96SArnd Bergmann int ret; 6167207b96SArnd Bergmann 628b3d6663SArnd Bergmann spu_acquire(ctx); 6367207b96SArnd Bergmann 648b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 658b3d6663SArnd Bergmann ret = simple_read_from_buffer(buffer, size, pos, local_store, LS_SIZE); 6667207b96SArnd Bergmann 678b3d6663SArnd Bergmann spu_release(ctx); 6867207b96SArnd Bergmann return ret; 6967207b96SArnd Bergmann } 7067207b96SArnd Bergmann 7167207b96SArnd Bergmann static ssize_t 7267207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer, 7367207b96SArnd Bergmann size_t size, loff_t *pos) 7467207b96SArnd Bergmann { 7567207b96SArnd Bergmann struct spu_context *ctx = file->private_data; 768b3d6663SArnd Bergmann char *local_store; 778b3d6663SArnd Bergmann int ret; 7867207b96SArnd Bergmann 7967207b96SArnd Bergmann size = min_t(ssize_t, LS_SIZE - *pos, size); 8067207b96SArnd Bergmann if (size <= 0) 8167207b96SArnd Bergmann return -EFBIG; 8267207b96SArnd Bergmann *pos += size; 838b3d6663SArnd Bergmann 848b3d6663SArnd Bergmann spu_acquire(ctx); 858b3d6663SArnd Bergmann 868b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 878b3d6663SArnd Bergmann ret = copy_from_user(local_store + *pos - size, 8867207b96SArnd Bergmann buffer, size) ? -EFAULT : size; 898b3d6663SArnd Bergmann 908b3d6663SArnd Bergmann spu_release(ctx); 918b3d6663SArnd Bergmann return ret; 9267207b96SArnd Bergmann } 9367207b96SArnd Bergmann 948b3d6663SArnd Bergmann static struct page * 958b3d6663SArnd Bergmann spufs_mem_mmap_nopage(struct vm_area_struct *vma, 968b3d6663SArnd Bergmann unsigned long address, int *type) 978b3d6663SArnd Bergmann { 988b3d6663SArnd Bergmann struct page *page = NOPAGE_SIGBUS; 998b3d6663SArnd Bergmann 1008b3d6663SArnd Bergmann struct spu_context *ctx = vma->vm_file->private_data; 1018b3d6663SArnd Bergmann unsigned long offset = address - vma->vm_start; 1028b3d6663SArnd Bergmann offset += vma->vm_pgoff << PAGE_SHIFT; 1038b3d6663SArnd Bergmann 1048b3d6663SArnd Bergmann spu_acquire(ctx); 1058b3d6663SArnd Bergmann 106ac91cb8dSArnd Bergmann if (ctx->state == SPU_STATE_SAVED) { 107ac91cb8dSArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 108ac91cb8dSArnd Bergmann & ~(_PAGE_NO_CACHE | _PAGE_GUARDED)); 1098b3d6663SArnd Bergmann page = vmalloc_to_page(ctx->csa.lscsa->ls + offset); 110ac91cb8dSArnd Bergmann } else { 111ac91cb8dSArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 112ac91cb8dSArnd Bergmann | _PAGE_NO_CACHE | _PAGE_GUARDED); 1138b3d6663SArnd Bergmann page = pfn_to_page((ctx->spu->local_store_phys + offset) 1148b3d6663SArnd Bergmann >> PAGE_SHIFT); 115ac91cb8dSArnd Bergmann } 1168b3d6663SArnd Bergmann spu_release(ctx); 1178b3d6663SArnd Bergmann 1188b3d6663SArnd Bergmann if (type) 1198b3d6663SArnd Bergmann *type = VM_FAULT_MINOR; 1208b3d6663SArnd Bergmann 121d88cfffaSArnd Bergmann page_cache_get(page); 1228b3d6663SArnd Bergmann return page; 1238b3d6663SArnd Bergmann } 1248b3d6663SArnd Bergmann 1258b3d6663SArnd Bergmann static struct vm_operations_struct spufs_mem_mmap_vmops = { 1268b3d6663SArnd Bergmann .nopage = spufs_mem_mmap_nopage, 1278b3d6663SArnd Bergmann }; 1288b3d6663SArnd Bergmann 12967207b96SArnd Bergmann static int 13067207b96SArnd Bergmann spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 13167207b96SArnd Bergmann { 1328b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 1338b3d6663SArnd Bergmann return -EINVAL; 13467207b96SArnd Bergmann 1358b3d6663SArnd Bergmann /* FIXME: */ 13667207b96SArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 13767207b96SArnd Bergmann | _PAGE_NO_CACHE); 1388b3d6663SArnd Bergmann 1398b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 14067207b96SArnd Bergmann return 0; 14167207b96SArnd Bergmann } 14267207b96SArnd Bergmann 14367207b96SArnd Bergmann static struct file_operations spufs_mem_fops = { 14467207b96SArnd Bergmann .open = spufs_mem_open, 14567207b96SArnd Bergmann .read = spufs_mem_read, 14667207b96SArnd Bergmann .write = spufs_mem_write, 1478b3d6663SArnd Bergmann .llseek = generic_file_llseek, 14867207b96SArnd Bergmann .mmap = spufs_mem_mmap, 1498b3d6663SArnd Bergmann }; 1508b3d6663SArnd Bergmann 1516df10a82SMark Nutter static struct page *spufs_ps_nopage(struct vm_area_struct *vma, 1526df10a82SMark Nutter unsigned long address, 15327d5bf2aSBenjamin Herrenschmidt int *type, unsigned long ps_offs, 15427d5bf2aSBenjamin Herrenschmidt unsigned long ps_size) 1556df10a82SMark Nutter { 1566df10a82SMark Nutter struct page *page = NOPAGE_SIGBUS; 1576df10a82SMark Nutter int fault_type = VM_FAULT_SIGBUS; 1586df10a82SMark Nutter struct spu_context *ctx = vma->vm_file->private_data; 1596df10a82SMark Nutter unsigned long offset = address - vma->vm_start; 1606df10a82SMark Nutter unsigned long area; 1616df10a82SMark Nutter int ret; 1626df10a82SMark Nutter 1636df10a82SMark Nutter offset += vma->vm_pgoff << PAGE_SHIFT; 16427d5bf2aSBenjamin Herrenschmidt if (offset >= ps_size) 1656df10a82SMark Nutter goto out; 1666df10a82SMark Nutter 1676df10a82SMark Nutter ret = spu_acquire_runnable(ctx); 1686df10a82SMark Nutter if (ret) 1696df10a82SMark Nutter goto out; 1706df10a82SMark Nutter 1716df10a82SMark Nutter area = ctx->spu->problem_phys + ps_offs; 1726df10a82SMark Nutter page = pfn_to_page((area + offset) >> PAGE_SHIFT); 1736df10a82SMark Nutter fault_type = VM_FAULT_MINOR; 1746df10a82SMark Nutter page_cache_get(page); 1756df10a82SMark Nutter 1766df10a82SMark Nutter spu_release(ctx); 1776df10a82SMark Nutter 1786df10a82SMark Nutter out: 1796df10a82SMark Nutter if (type) 1806df10a82SMark Nutter *type = fault_type; 1816df10a82SMark Nutter 1826df10a82SMark Nutter return page; 1836df10a82SMark Nutter } 1846df10a82SMark Nutter 18527d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 1866df10a82SMark Nutter static struct page *spufs_cntl_mmap_nopage(struct vm_area_struct *vma, 1876df10a82SMark Nutter unsigned long address, int *type) 1886df10a82SMark Nutter { 18927d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x4000, 0x1000); 1906df10a82SMark Nutter } 1916df10a82SMark Nutter 1926df10a82SMark Nutter static struct vm_operations_struct spufs_cntl_mmap_vmops = { 1936df10a82SMark Nutter .nopage = spufs_cntl_mmap_nopage, 1946df10a82SMark Nutter }; 1956df10a82SMark Nutter 1966df10a82SMark Nutter /* 1976df10a82SMark Nutter * mmap support for problem state control area [0x4000 - 0x4fff]. 1986df10a82SMark Nutter */ 1996df10a82SMark Nutter static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) 2006df10a82SMark Nutter { 2016df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 2026df10a82SMark Nutter return -EINVAL; 2036df10a82SMark Nutter 2046df10a82SMark Nutter vma->vm_flags |= VM_RESERVED; 2056df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 20623cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 2076df10a82SMark Nutter 2086df10a82SMark Nutter vma->vm_ops = &spufs_cntl_mmap_vmops; 2096df10a82SMark Nutter return 0; 2106df10a82SMark Nutter } 21127d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 21227d5bf2aSBenjamin Herrenschmidt #define spufs_cntl_mmap NULL 21327d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 2146df10a82SMark Nutter 215e1dbff2bSArnd Bergmann static u64 spufs_cntl_get(void *data) 216e1dbff2bSArnd Bergmann { 217e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 218e1dbff2bSArnd Bergmann u64 val; 219e1dbff2bSArnd Bergmann 220e1dbff2bSArnd Bergmann spu_acquire(ctx); 221e1dbff2bSArnd Bergmann val = ctx->ops->status_read(ctx); 222e1dbff2bSArnd Bergmann spu_release(ctx); 223e1dbff2bSArnd Bergmann 224e1dbff2bSArnd Bergmann return val; 225e1dbff2bSArnd Bergmann } 226e1dbff2bSArnd Bergmann 227e1dbff2bSArnd Bergmann static void spufs_cntl_set(void *data, u64 val) 228e1dbff2bSArnd Bergmann { 229e1dbff2bSArnd Bergmann struct spu_context *ctx = data; 230e1dbff2bSArnd Bergmann 231e1dbff2bSArnd Bergmann spu_acquire(ctx); 232e1dbff2bSArnd Bergmann ctx->ops->runcntl_write(ctx, val); 233e1dbff2bSArnd Bergmann spu_release(ctx); 234e1dbff2bSArnd Bergmann } 235e1dbff2bSArnd Bergmann 2366df10a82SMark Nutter static int spufs_cntl_open(struct inode *inode, struct file *file) 2376df10a82SMark Nutter { 2386df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 2396df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 2406df10a82SMark Nutter 2416df10a82SMark Nutter file->private_data = ctx; 2426df10a82SMark Nutter file->f_mapping = inode->i_mapping; 2436df10a82SMark Nutter ctx->cntl = inode->i_mapping; 244e1dbff2bSArnd Bergmann return simple_attr_open(inode, file, spufs_cntl_get, 245e1dbff2bSArnd Bergmann spufs_cntl_set, "0x%08lx"); 2466df10a82SMark Nutter } 2476df10a82SMark Nutter 2486df10a82SMark Nutter static struct file_operations spufs_cntl_fops = { 2496df10a82SMark Nutter .open = spufs_cntl_open, 250654e4aeeSNoguchi, Masato .release = simple_attr_close, 251e1dbff2bSArnd Bergmann .read = simple_attr_read, 252e1dbff2bSArnd Bergmann .write = simple_attr_write, 2536df10a82SMark Nutter .mmap = spufs_cntl_mmap, 2546df10a82SMark Nutter }; 2556df10a82SMark Nutter 2568b3d6663SArnd Bergmann static int 2578b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 2588b3d6663SArnd Bergmann { 2598b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 2608b3d6663SArnd Bergmann file->private_data = i->i_ctx; 2618b3d6663SArnd Bergmann return 0; 2628b3d6663SArnd Bergmann } 2638b3d6663SArnd Bergmann 2648b3d6663SArnd Bergmann static ssize_t 2658b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 2668b3d6663SArnd Bergmann size_t size, loff_t *pos) 2678b3d6663SArnd Bergmann { 2688b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 2698b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 2708b3d6663SArnd Bergmann int ret; 2718b3d6663SArnd Bergmann 2728b3d6663SArnd Bergmann spu_acquire_saved(ctx); 2738b3d6663SArnd Bergmann 2748b3d6663SArnd Bergmann ret = simple_read_from_buffer(buffer, size, pos, 2758b3d6663SArnd Bergmann lscsa->gprs, sizeof lscsa->gprs); 2768b3d6663SArnd Bergmann 2778b3d6663SArnd Bergmann spu_release(ctx); 2788b3d6663SArnd Bergmann return ret; 2798b3d6663SArnd Bergmann } 2808b3d6663SArnd Bergmann 2818b3d6663SArnd Bergmann static ssize_t 2828b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 2838b3d6663SArnd Bergmann size_t size, loff_t *pos) 2848b3d6663SArnd Bergmann { 2858b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 2868b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 2878b3d6663SArnd Bergmann int ret; 2888b3d6663SArnd Bergmann 2898b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size); 2908b3d6663SArnd Bergmann if (size <= 0) 2918b3d6663SArnd Bergmann return -EFBIG; 2928b3d6663SArnd Bergmann *pos += size; 2938b3d6663SArnd Bergmann 2948b3d6663SArnd Bergmann spu_acquire_saved(ctx); 2958b3d6663SArnd Bergmann 2968b3d6663SArnd Bergmann ret = copy_from_user(lscsa->gprs + *pos - size, 2978b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 2988b3d6663SArnd Bergmann 2998b3d6663SArnd Bergmann spu_release(ctx); 3008b3d6663SArnd Bergmann return ret; 3018b3d6663SArnd Bergmann } 3028b3d6663SArnd Bergmann 3038b3d6663SArnd Bergmann static struct file_operations spufs_regs_fops = { 3048b3d6663SArnd Bergmann .open = spufs_regs_open, 3058b3d6663SArnd Bergmann .read = spufs_regs_read, 3068b3d6663SArnd Bergmann .write = spufs_regs_write, 3078b3d6663SArnd Bergmann .llseek = generic_file_llseek, 3088b3d6663SArnd Bergmann }; 3098b3d6663SArnd Bergmann 3108b3d6663SArnd Bergmann static ssize_t 3118b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 3128b3d6663SArnd Bergmann size_t size, loff_t * pos) 3138b3d6663SArnd Bergmann { 3148b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 3158b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 3168b3d6663SArnd Bergmann int ret; 3178b3d6663SArnd Bergmann 3188b3d6663SArnd Bergmann spu_acquire_saved(ctx); 3198b3d6663SArnd Bergmann 3208b3d6663SArnd Bergmann ret = simple_read_from_buffer(buffer, size, pos, 3218b3d6663SArnd Bergmann &lscsa->fpcr, sizeof(lscsa->fpcr)); 3228b3d6663SArnd Bergmann 3238b3d6663SArnd Bergmann spu_release(ctx); 3248b3d6663SArnd Bergmann return ret; 3258b3d6663SArnd Bergmann } 3268b3d6663SArnd Bergmann 3278b3d6663SArnd Bergmann static ssize_t 3288b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 3298b3d6663SArnd Bergmann size_t size, loff_t * pos) 3308b3d6663SArnd Bergmann { 3318b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 3328b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 3338b3d6663SArnd Bergmann int ret; 3348b3d6663SArnd Bergmann 3358b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); 3368b3d6663SArnd Bergmann if (size <= 0) 3378b3d6663SArnd Bergmann return -EFBIG; 3388b3d6663SArnd Bergmann *pos += size; 3398b3d6663SArnd Bergmann 3408b3d6663SArnd Bergmann spu_acquire_saved(ctx); 3418b3d6663SArnd Bergmann 3428b3d6663SArnd Bergmann ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, 3438b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 3448b3d6663SArnd Bergmann 3458b3d6663SArnd Bergmann spu_release(ctx); 3468b3d6663SArnd Bergmann return ret; 3478b3d6663SArnd Bergmann } 3488b3d6663SArnd Bergmann 3498b3d6663SArnd Bergmann static struct file_operations spufs_fpcr_fops = { 3508b3d6663SArnd Bergmann .open = spufs_regs_open, 3518b3d6663SArnd Bergmann .read = spufs_fpcr_read, 3528b3d6663SArnd Bergmann .write = spufs_fpcr_write, 35367207b96SArnd Bergmann .llseek = generic_file_llseek, 35467207b96SArnd Bergmann }; 35567207b96SArnd Bergmann 35667207b96SArnd Bergmann /* generic open function for all pipe-like files */ 35767207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 35867207b96SArnd Bergmann { 35967207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 36067207b96SArnd Bergmann file->private_data = i->i_ctx; 36167207b96SArnd Bergmann 36267207b96SArnd Bergmann return nonseekable_open(inode, file); 36367207b96SArnd Bergmann } 36467207b96SArnd Bergmann 365cdcc89bbSArnd Bergmann /* 366cdcc89bbSArnd Bergmann * Read as many bytes from the mailbox as possible, until 367cdcc89bbSArnd Bergmann * one of the conditions becomes true: 368cdcc89bbSArnd Bergmann * 369cdcc89bbSArnd Bergmann * - no more data available in the mailbox 370cdcc89bbSArnd Bergmann * - end of the user provided buffer 371cdcc89bbSArnd Bergmann * - end of the mapped area 372cdcc89bbSArnd Bergmann */ 37367207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 37467207b96SArnd Bergmann size_t len, loff_t *pos) 37567207b96SArnd Bergmann { 3768b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 377cdcc89bbSArnd Bergmann u32 mbox_data, __user *udata; 378cdcc89bbSArnd Bergmann ssize_t count; 37967207b96SArnd Bergmann 38067207b96SArnd Bergmann if (len < 4) 38167207b96SArnd Bergmann return -EINVAL; 38267207b96SArnd Bergmann 383cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 38467207b96SArnd Bergmann return -EFAULT; 38567207b96SArnd Bergmann 386cdcc89bbSArnd Bergmann udata = (void __user *)buf; 387cdcc89bbSArnd Bergmann 388cdcc89bbSArnd Bergmann spu_acquire(ctx); 389274cef5eSArnd Bergmann for (count = 0; (count + 4) <= len; count += 4, udata++) { 390cdcc89bbSArnd Bergmann int ret; 391cdcc89bbSArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 392cdcc89bbSArnd Bergmann if (ret == 0) 393cdcc89bbSArnd Bergmann break; 394cdcc89bbSArnd Bergmann 395cdcc89bbSArnd Bergmann /* 396cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 397cdcc89bbSArnd Bergmann * but still need to return the data we have 398cdcc89bbSArnd Bergmann * read successfully so far. 399cdcc89bbSArnd Bergmann */ 400cdcc89bbSArnd Bergmann ret = __put_user(mbox_data, udata); 401cdcc89bbSArnd Bergmann if (ret) { 402cdcc89bbSArnd Bergmann if (!count) 403cdcc89bbSArnd Bergmann count = -EFAULT; 404cdcc89bbSArnd Bergmann break; 405cdcc89bbSArnd Bergmann } 406cdcc89bbSArnd Bergmann } 407cdcc89bbSArnd Bergmann spu_release(ctx); 408cdcc89bbSArnd Bergmann 409cdcc89bbSArnd Bergmann if (!count) 410cdcc89bbSArnd Bergmann count = -EAGAIN; 411cdcc89bbSArnd Bergmann 412cdcc89bbSArnd Bergmann return count; 41367207b96SArnd Bergmann } 41467207b96SArnd Bergmann 41567207b96SArnd Bergmann static struct file_operations spufs_mbox_fops = { 41667207b96SArnd Bergmann .open = spufs_pipe_open, 41767207b96SArnd Bergmann .read = spufs_mbox_read, 41867207b96SArnd Bergmann }; 41967207b96SArnd Bergmann 42067207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 42167207b96SArnd Bergmann size_t len, loff_t *pos) 42267207b96SArnd Bergmann { 4238b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 42467207b96SArnd Bergmann u32 mbox_stat; 42567207b96SArnd Bergmann 42667207b96SArnd Bergmann if (len < 4) 42767207b96SArnd Bergmann return -EINVAL; 42867207b96SArnd Bergmann 4298b3d6663SArnd Bergmann spu_acquire(ctx); 4308b3d6663SArnd Bergmann 4318b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 4328b3d6663SArnd Bergmann 4338b3d6663SArnd Bergmann spu_release(ctx); 43467207b96SArnd Bergmann 43567207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 43667207b96SArnd Bergmann return -EFAULT; 43767207b96SArnd Bergmann 43867207b96SArnd Bergmann return 4; 43967207b96SArnd Bergmann } 44067207b96SArnd Bergmann 44167207b96SArnd Bergmann static struct file_operations spufs_mbox_stat_fops = { 44267207b96SArnd Bergmann .open = spufs_pipe_open, 44367207b96SArnd Bergmann .read = spufs_mbox_stat_read, 44467207b96SArnd Bergmann }; 44567207b96SArnd Bergmann 44667207b96SArnd Bergmann /* low-level ibox access function */ 4478b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 44867207b96SArnd Bergmann { 4498b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 45067207b96SArnd Bergmann } 45167207b96SArnd Bergmann 45267207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on) 45367207b96SArnd Bergmann { 4548b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 4558b3d6663SArnd Bergmann 4568b3d6663SArnd Bergmann return fasync_helper(fd, file, on, &ctx->ibox_fasync); 4578b3d6663SArnd Bergmann } 4588b3d6663SArnd Bergmann 4598b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 4608b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 4618b3d6663SArnd Bergmann { 4628b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 4638b3d6663SArnd Bergmann 4648b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 4658b3d6663SArnd Bergmann kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); 46667207b96SArnd Bergmann } 46767207b96SArnd Bergmann 468cdcc89bbSArnd Bergmann /* 469cdcc89bbSArnd Bergmann * Read as many bytes from the interrupt mailbox as possible, until 470cdcc89bbSArnd Bergmann * one of the conditions becomes true: 471cdcc89bbSArnd Bergmann * 472cdcc89bbSArnd Bergmann * - no more data available in the mailbox 473cdcc89bbSArnd Bergmann * - end of the user provided buffer 474cdcc89bbSArnd Bergmann * - end of the mapped area 475cdcc89bbSArnd Bergmann * 476cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 477cdcc89bbSArnd Bergmann * any data is available, but return when we have been able to 478cdcc89bbSArnd Bergmann * read something. 479cdcc89bbSArnd Bergmann */ 48067207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 48167207b96SArnd Bergmann size_t len, loff_t *pos) 48267207b96SArnd Bergmann { 4838b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 484cdcc89bbSArnd Bergmann u32 ibox_data, __user *udata; 485cdcc89bbSArnd Bergmann ssize_t count; 48667207b96SArnd Bergmann 48767207b96SArnd Bergmann if (len < 4) 48867207b96SArnd Bergmann return -EINVAL; 48967207b96SArnd Bergmann 490cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_WRITE, buf, len)) 491cdcc89bbSArnd Bergmann return -EFAULT; 492cdcc89bbSArnd Bergmann 493cdcc89bbSArnd Bergmann udata = (void __user *)buf; 494cdcc89bbSArnd Bergmann 4958b3d6663SArnd Bergmann spu_acquire(ctx); 49667207b96SArnd Bergmann 497cdcc89bbSArnd Bergmann /* wait only for the first element */ 498cdcc89bbSArnd Bergmann count = 0; 49967207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 5008b3d6663SArnd Bergmann if (!spu_ibox_read(ctx, &ibox_data)) 501cdcc89bbSArnd Bergmann count = -EAGAIN; 50267207b96SArnd Bergmann } else { 503cdcc89bbSArnd Bergmann count = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 504cdcc89bbSArnd Bergmann } 505cdcc89bbSArnd Bergmann if (count) 506cdcc89bbSArnd Bergmann goto out; 507cdcc89bbSArnd Bergmann 508cdcc89bbSArnd Bergmann /* if we can't write at all, return -EFAULT */ 509cdcc89bbSArnd Bergmann count = __put_user(ibox_data, udata); 510cdcc89bbSArnd Bergmann if (count) 511cdcc89bbSArnd Bergmann goto out; 512cdcc89bbSArnd Bergmann 513cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 514cdcc89bbSArnd Bergmann int ret; 515cdcc89bbSArnd Bergmann ret = ctx->ops->ibox_read(ctx, &ibox_data); 516cdcc89bbSArnd Bergmann if (ret == 0) 517cdcc89bbSArnd Bergmann break; 518cdcc89bbSArnd Bergmann /* 519cdcc89bbSArnd Bergmann * at the end of the mapped area, we can fault 520cdcc89bbSArnd Bergmann * but still need to return the data we have 521cdcc89bbSArnd Bergmann * read successfully so far. 522cdcc89bbSArnd Bergmann */ 523cdcc89bbSArnd Bergmann ret = __put_user(ibox_data, udata); 524cdcc89bbSArnd Bergmann if (ret) 525cdcc89bbSArnd Bergmann break; 52667207b96SArnd Bergmann } 52767207b96SArnd Bergmann 528cdcc89bbSArnd Bergmann out: 5298b3d6663SArnd Bergmann spu_release(ctx); 5308b3d6663SArnd Bergmann 531cdcc89bbSArnd Bergmann return count; 53267207b96SArnd Bergmann } 53367207b96SArnd Bergmann 53467207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) 53567207b96SArnd Bergmann { 5368b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 53767207b96SArnd Bergmann unsigned int mask; 53867207b96SArnd Bergmann 5398b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 54067207b96SArnd Bergmann 5413a843d7cSArnd Bergmann spu_acquire(ctx); 5423a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); 5433a843d7cSArnd Bergmann spu_release(ctx); 54467207b96SArnd Bergmann 54567207b96SArnd Bergmann return mask; 54667207b96SArnd Bergmann } 54767207b96SArnd Bergmann 54867207b96SArnd Bergmann static struct file_operations spufs_ibox_fops = { 54967207b96SArnd Bergmann .open = spufs_pipe_open, 55067207b96SArnd Bergmann .read = spufs_ibox_read, 55167207b96SArnd Bergmann .poll = spufs_ibox_poll, 55267207b96SArnd Bergmann .fasync = spufs_ibox_fasync, 55367207b96SArnd Bergmann }; 55467207b96SArnd Bergmann 55567207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 55667207b96SArnd Bergmann size_t len, loff_t *pos) 55767207b96SArnd Bergmann { 5588b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 55967207b96SArnd Bergmann u32 ibox_stat; 56067207b96SArnd Bergmann 56167207b96SArnd Bergmann if (len < 4) 56267207b96SArnd Bergmann return -EINVAL; 56367207b96SArnd Bergmann 5648b3d6663SArnd Bergmann spu_acquire(ctx); 5658b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 5668b3d6663SArnd Bergmann spu_release(ctx); 56767207b96SArnd Bergmann 56867207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 56967207b96SArnd Bergmann return -EFAULT; 57067207b96SArnd Bergmann 57167207b96SArnd Bergmann return 4; 57267207b96SArnd Bergmann } 57367207b96SArnd Bergmann 57467207b96SArnd Bergmann static struct file_operations spufs_ibox_stat_fops = { 57567207b96SArnd Bergmann .open = spufs_pipe_open, 57667207b96SArnd Bergmann .read = spufs_ibox_stat_read, 57767207b96SArnd Bergmann }; 57867207b96SArnd Bergmann 57967207b96SArnd Bergmann /* low-level mailbox write */ 5808b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 58167207b96SArnd Bergmann { 5828b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 58367207b96SArnd Bergmann } 58467207b96SArnd Bergmann 58567207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on) 58667207b96SArnd Bergmann { 5878b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 5888b3d6663SArnd Bergmann int ret; 5898b3d6663SArnd Bergmann 5908b3d6663SArnd Bergmann ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); 5918b3d6663SArnd Bergmann 5928b3d6663SArnd Bergmann return ret; 5938b3d6663SArnd Bergmann } 5948b3d6663SArnd Bergmann 5958b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 5968b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 5978b3d6663SArnd Bergmann { 5988b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 5998b3d6663SArnd Bergmann 6008b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 6018b3d6663SArnd Bergmann kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); 60267207b96SArnd Bergmann } 60367207b96SArnd Bergmann 604cdcc89bbSArnd Bergmann /* 605cdcc89bbSArnd Bergmann * Write as many bytes to the interrupt mailbox as possible, until 606cdcc89bbSArnd Bergmann * one of the conditions becomes true: 607cdcc89bbSArnd Bergmann * 608cdcc89bbSArnd Bergmann * - the mailbox is full 609cdcc89bbSArnd Bergmann * - end of the user provided buffer 610cdcc89bbSArnd Bergmann * - end of the mapped area 611cdcc89bbSArnd Bergmann * 612cdcc89bbSArnd Bergmann * If the file is opened without O_NONBLOCK, we wait here until 613cdcc89bbSArnd Bergmann * space is availabyl, but return when we have been able to 614cdcc89bbSArnd Bergmann * write something. 615cdcc89bbSArnd Bergmann */ 61667207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 61767207b96SArnd Bergmann size_t len, loff_t *pos) 61867207b96SArnd Bergmann { 6198b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 620cdcc89bbSArnd Bergmann u32 wbox_data, __user *udata; 621cdcc89bbSArnd Bergmann ssize_t count; 62267207b96SArnd Bergmann 62367207b96SArnd Bergmann if (len < 4) 62467207b96SArnd Bergmann return -EINVAL; 62567207b96SArnd Bergmann 626cdcc89bbSArnd Bergmann udata = (void __user *)buf; 627cdcc89bbSArnd Bergmann if (!access_ok(VERIFY_READ, buf, len)) 628cdcc89bbSArnd Bergmann return -EFAULT; 629cdcc89bbSArnd Bergmann 630cdcc89bbSArnd Bergmann if (__get_user(wbox_data, udata)) 63167207b96SArnd Bergmann return -EFAULT; 63267207b96SArnd Bergmann 6338b3d6663SArnd Bergmann spu_acquire(ctx); 6348b3d6663SArnd Bergmann 635cdcc89bbSArnd Bergmann /* 636cdcc89bbSArnd Bergmann * make sure we can at least write one element, by waiting 637cdcc89bbSArnd Bergmann * in case of !O_NONBLOCK 638cdcc89bbSArnd Bergmann */ 639cdcc89bbSArnd Bergmann count = 0; 64067207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 6418b3d6663SArnd Bergmann if (!spu_wbox_write(ctx, wbox_data)) 642cdcc89bbSArnd Bergmann count = -EAGAIN; 64367207b96SArnd Bergmann } else { 644cdcc89bbSArnd Bergmann count = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 64567207b96SArnd Bergmann } 64667207b96SArnd Bergmann 647cdcc89bbSArnd Bergmann if (count) 648cdcc89bbSArnd Bergmann goto out; 6498b3d6663SArnd Bergmann 650cdcc89bbSArnd Bergmann /* write aѕ much as possible */ 651cdcc89bbSArnd Bergmann for (count = 4, udata++; (count + 4) <= len; count += 4, udata++) { 652cdcc89bbSArnd Bergmann int ret; 653cdcc89bbSArnd Bergmann ret = __get_user(wbox_data, udata); 654cdcc89bbSArnd Bergmann if (ret) 655cdcc89bbSArnd Bergmann break; 656cdcc89bbSArnd Bergmann 657cdcc89bbSArnd Bergmann ret = spu_wbox_write(ctx, wbox_data); 658cdcc89bbSArnd Bergmann if (ret == 0) 659cdcc89bbSArnd Bergmann break; 660cdcc89bbSArnd Bergmann } 661cdcc89bbSArnd Bergmann 662cdcc89bbSArnd Bergmann out: 663cdcc89bbSArnd Bergmann spu_release(ctx); 664cdcc89bbSArnd Bergmann return count; 66567207b96SArnd Bergmann } 66667207b96SArnd Bergmann 66767207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) 66867207b96SArnd Bergmann { 6698b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 67067207b96SArnd Bergmann unsigned int mask; 67167207b96SArnd Bergmann 6728b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 67367207b96SArnd Bergmann 6743a843d7cSArnd Bergmann spu_acquire(ctx); 6753a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); 6763a843d7cSArnd Bergmann spu_release(ctx); 67767207b96SArnd Bergmann 67867207b96SArnd Bergmann return mask; 67967207b96SArnd Bergmann } 68067207b96SArnd Bergmann 68167207b96SArnd Bergmann static struct file_operations spufs_wbox_fops = { 68267207b96SArnd Bergmann .open = spufs_pipe_open, 68367207b96SArnd Bergmann .write = spufs_wbox_write, 68467207b96SArnd Bergmann .poll = spufs_wbox_poll, 68567207b96SArnd Bergmann .fasync = spufs_wbox_fasync, 68667207b96SArnd Bergmann }; 68767207b96SArnd Bergmann 68867207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 68967207b96SArnd Bergmann size_t len, loff_t *pos) 69067207b96SArnd Bergmann { 6918b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 69267207b96SArnd Bergmann u32 wbox_stat; 69367207b96SArnd Bergmann 69467207b96SArnd Bergmann if (len < 4) 69567207b96SArnd Bergmann return -EINVAL; 69667207b96SArnd Bergmann 6978b3d6663SArnd Bergmann spu_acquire(ctx); 6988b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 6998b3d6663SArnd Bergmann spu_release(ctx); 70067207b96SArnd Bergmann 70167207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 70267207b96SArnd Bergmann return -EFAULT; 70367207b96SArnd Bergmann 70467207b96SArnd Bergmann return 4; 70567207b96SArnd Bergmann } 70667207b96SArnd Bergmann 70767207b96SArnd Bergmann static struct file_operations spufs_wbox_stat_fops = { 70867207b96SArnd Bergmann .open = spufs_pipe_open, 70967207b96SArnd Bergmann .read = spufs_wbox_stat_read, 71067207b96SArnd Bergmann }; 71167207b96SArnd Bergmann 7126df10a82SMark Nutter static int spufs_signal1_open(struct inode *inode, struct file *file) 7136df10a82SMark Nutter { 7146df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 7156df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 7166df10a82SMark Nutter file->private_data = ctx; 7176df10a82SMark Nutter file->f_mapping = inode->i_mapping; 7186df10a82SMark Nutter ctx->signal1 = inode->i_mapping; 7196df10a82SMark Nutter return nonseekable_open(inode, file); 7206df10a82SMark Nutter } 7216df10a82SMark Nutter 72267207b96SArnd Bergmann static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 72367207b96SArnd Bergmann size_t len, loff_t *pos) 72467207b96SArnd Bergmann { 7258b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 72667207b96SArnd Bergmann u32 data; 72767207b96SArnd Bergmann 72867207b96SArnd Bergmann if (len < 4) 72967207b96SArnd Bergmann return -EINVAL; 73067207b96SArnd Bergmann 7318b3d6663SArnd Bergmann spu_acquire(ctx); 7328b3d6663SArnd Bergmann data = ctx->ops->signal1_read(ctx); 7338b3d6663SArnd Bergmann spu_release(ctx); 7348b3d6663SArnd Bergmann 73567207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 73667207b96SArnd Bergmann return -EFAULT; 73767207b96SArnd Bergmann 73867207b96SArnd Bergmann return 4; 73967207b96SArnd Bergmann } 74067207b96SArnd Bergmann 74167207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 74267207b96SArnd Bergmann size_t len, loff_t *pos) 74367207b96SArnd Bergmann { 74467207b96SArnd Bergmann struct spu_context *ctx; 74567207b96SArnd Bergmann u32 data; 74667207b96SArnd Bergmann 74767207b96SArnd Bergmann ctx = file->private_data; 74867207b96SArnd Bergmann 74967207b96SArnd Bergmann if (len < 4) 75067207b96SArnd Bergmann return -EINVAL; 75167207b96SArnd Bergmann 75267207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 75367207b96SArnd Bergmann return -EFAULT; 75467207b96SArnd Bergmann 7558b3d6663SArnd Bergmann spu_acquire(ctx); 7568b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 7578b3d6663SArnd Bergmann spu_release(ctx); 75867207b96SArnd Bergmann 75967207b96SArnd Bergmann return 4; 76067207b96SArnd Bergmann } 76167207b96SArnd Bergmann 7626df10a82SMark Nutter static struct page *spufs_signal1_mmap_nopage(struct vm_area_struct *vma, 7636df10a82SMark Nutter unsigned long address, int *type) 7646df10a82SMark Nutter { 76527d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000 76627d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x14000, 0x1000); 76727d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000 76827d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 76927d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 77027d5bf2aSBenjamin Herrenschmidt */ 77127d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x10000, 0x10000); 77227d5bf2aSBenjamin Herrenschmidt #else 77327d5bf2aSBenjamin Herrenschmidt #error unsupported page size 77427d5bf2aSBenjamin Herrenschmidt #endif 7756df10a82SMark Nutter } 7766df10a82SMark Nutter 7776df10a82SMark Nutter static struct vm_operations_struct spufs_signal1_mmap_vmops = { 7786df10a82SMark Nutter .nopage = spufs_signal1_mmap_nopage, 7796df10a82SMark Nutter }; 7806df10a82SMark Nutter 7816df10a82SMark Nutter static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) 7826df10a82SMark Nutter { 7836df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 7846df10a82SMark Nutter return -EINVAL; 7856df10a82SMark Nutter 7866df10a82SMark Nutter vma->vm_flags |= VM_RESERVED; 7876df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 78823cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 7896df10a82SMark Nutter 7906df10a82SMark Nutter vma->vm_ops = &spufs_signal1_mmap_vmops; 7916df10a82SMark Nutter return 0; 7926df10a82SMark Nutter } 7936df10a82SMark Nutter 79467207b96SArnd Bergmann static struct file_operations spufs_signal1_fops = { 7956df10a82SMark Nutter .open = spufs_signal1_open, 79667207b96SArnd Bergmann .read = spufs_signal1_read, 79767207b96SArnd Bergmann .write = spufs_signal1_write, 7986df10a82SMark Nutter .mmap = spufs_signal1_mmap, 79967207b96SArnd Bergmann }; 80067207b96SArnd Bergmann 8016df10a82SMark Nutter static int spufs_signal2_open(struct inode *inode, struct file *file) 8026df10a82SMark Nutter { 8036df10a82SMark Nutter struct spufs_inode_info *i = SPUFS_I(inode); 8046df10a82SMark Nutter struct spu_context *ctx = i->i_ctx; 8056df10a82SMark Nutter file->private_data = ctx; 8066df10a82SMark Nutter file->f_mapping = inode->i_mapping; 8076df10a82SMark Nutter ctx->signal2 = inode->i_mapping; 8086df10a82SMark Nutter return nonseekable_open(inode, file); 8096df10a82SMark Nutter } 8106df10a82SMark Nutter 81167207b96SArnd Bergmann static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 81267207b96SArnd Bergmann size_t len, loff_t *pos) 81367207b96SArnd Bergmann { 81467207b96SArnd Bergmann struct spu_context *ctx; 81567207b96SArnd Bergmann u32 data; 81667207b96SArnd Bergmann 81767207b96SArnd Bergmann ctx = file->private_data; 81867207b96SArnd Bergmann 81967207b96SArnd Bergmann if (len < 4) 82067207b96SArnd Bergmann return -EINVAL; 82167207b96SArnd Bergmann 8228b3d6663SArnd Bergmann spu_acquire(ctx); 8238b3d6663SArnd Bergmann data = ctx->ops->signal2_read(ctx); 8248b3d6663SArnd Bergmann spu_release(ctx); 8258b3d6663SArnd Bergmann 82667207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 82767207b96SArnd Bergmann return -EFAULT; 82867207b96SArnd Bergmann 82967207b96SArnd Bergmann return 4; 83067207b96SArnd Bergmann } 83167207b96SArnd Bergmann 83267207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 83367207b96SArnd Bergmann size_t len, loff_t *pos) 83467207b96SArnd Bergmann { 83567207b96SArnd Bergmann struct spu_context *ctx; 83667207b96SArnd Bergmann u32 data; 83767207b96SArnd Bergmann 83867207b96SArnd Bergmann ctx = file->private_data; 83967207b96SArnd Bergmann 84067207b96SArnd Bergmann if (len < 4) 84167207b96SArnd Bergmann return -EINVAL; 84267207b96SArnd Bergmann 84367207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 84467207b96SArnd Bergmann return -EFAULT; 84567207b96SArnd Bergmann 8468b3d6663SArnd Bergmann spu_acquire(ctx); 8478b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 8488b3d6663SArnd Bergmann spu_release(ctx); 84967207b96SArnd Bergmann 85067207b96SArnd Bergmann return 4; 85167207b96SArnd Bergmann } 85267207b96SArnd Bergmann 85327d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 8546df10a82SMark Nutter static struct page *spufs_signal2_mmap_nopage(struct vm_area_struct *vma, 8556df10a82SMark Nutter unsigned long address, int *type) 8566df10a82SMark Nutter { 85727d5bf2aSBenjamin Herrenschmidt #if PAGE_SIZE == 0x1000 85827d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x1c000, 0x1000); 85927d5bf2aSBenjamin Herrenschmidt #elif PAGE_SIZE == 0x10000 86027d5bf2aSBenjamin Herrenschmidt /* For 64k pages, both signal1 and signal2 can be used to mmap the whole 86127d5bf2aSBenjamin Herrenschmidt * signal 1 and 2 area 86227d5bf2aSBenjamin Herrenschmidt */ 86327d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x10000, 0x10000); 86427d5bf2aSBenjamin Herrenschmidt #else 86527d5bf2aSBenjamin Herrenschmidt #error unsupported page size 86627d5bf2aSBenjamin Herrenschmidt #endif 8676df10a82SMark Nutter } 8686df10a82SMark Nutter 8696df10a82SMark Nutter static struct vm_operations_struct spufs_signal2_mmap_vmops = { 8706df10a82SMark Nutter .nopage = spufs_signal2_mmap_nopage, 8716df10a82SMark Nutter }; 8726df10a82SMark Nutter 8736df10a82SMark Nutter static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) 8746df10a82SMark Nutter { 8756df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 8766df10a82SMark Nutter return -EINVAL; 8776df10a82SMark Nutter 8786df10a82SMark Nutter /* FIXME: */ 8796df10a82SMark Nutter vma->vm_flags |= VM_RESERVED; 8806df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 88123cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 8826df10a82SMark Nutter 8836df10a82SMark Nutter vma->vm_ops = &spufs_signal2_mmap_vmops; 8846df10a82SMark Nutter return 0; 8856df10a82SMark Nutter } 88627d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 88727d5bf2aSBenjamin Herrenschmidt #define spufs_signal2_mmap NULL 88827d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 8896df10a82SMark Nutter 89067207b96SArnd Bergmann static struct file_operations spufs_signal2_fops = { 8916df10a82SMark Nutter .open = spufs_signal2_open, 89267207b96SArnd Bergmann .read = spufs_signal2_read, 89367207b96SArnd Bergmann .write = spufs_signal2_write, 8946df10a82SMark Nutter .mmap = spufs_signal2_mmap, 89567207b96SArnd Bergmann }; 89667207b96SArnd Bergmann 89767207b96SArnd Bergmann static void spufs_signal1_type_set(void *data, u64 val) 89867207b96SArnd Bergmann { 89967207b96SArnd Bergmann struct spu_context *ctx = data; 90067207b96SArnd Bergmann 9018b3d6663SArnd Bergmann spu_acquire(ctx); 9028b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 9038b3d6663SArnd Bergmann spu_release(ctx); 90467207b96SArnd Bergmann } 90567207b96SArnd Bergmann 90667207b96SArnd Bergmann static u64 spufs_signal1_type_get(void *data) 90767207b96SArnd Bergmann { 90867207b96SArnd Bergmann struct spu_context *ctx = data; 9098b3d6663SArnd Bergmann u64 ret; 9108b3d6663SArnd Bergmann 9118b3d6663SArnd Bergmann spu_acquire(ctx); 9128b3d6663SArnd Bergmann ret = ctx->ops->signal1_type_get(ctx); 9138b3d6663SArnd Bergmann spu_release(ctx); 9148b3d6663SArnd Bergmann 9158b3d6663SArnd Bergmann return ret; 91667207b96SArnd Bergmann } 91767207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 91867207b96SArnd Bergmann spufs_signal1_type_set, "%llu"); 91967207b96SArnd Bergmann 92067207b96SArnd Bergmann static void spufs_signal2_type_set(void *data, u64 val) 92167207b96SArnd Bergmann { 92267207b96SArnd Bergmann struct spu_context *ctx = data; 92367207b96SArnd Bergmann 9248b3d6663SArnd Bergmann spu_acquire(ctx); 9258b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 9268b3d6663SArnd Bergmann spu_release(ctx); 92767207b96SArnd Bergmann } 92867207b96SArnd Bergmann 92967207b96SArnd Bergmann static u64 spufs_signal2_type_get(void *data) 93067207b96SArnd Bergmann { 93167207b96SArnd Bergmann struct spu_context *ctx = data; 9328b3d6663SArnd Bergmann u64 ret; 9338b3d6663SArnd Bergmann 9348b3d6663SArnd Bergmann spu_acquire(ctx); 9358b3d6663SArnd Bergmann ret = ctx->ops->signal2_type_get(ctx); 9368b3d6663SArnd Bergmann spu_release(ctx); 9378b3d6663SArnd Bergmann 9388b3d6663SArnd Bergmann return ret; 93967207b96SArnd Bergmann } 94067207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 94167207b96SArnd Bergmann spufs_signal2_type_set, "%llu"); 94267207b96SArnd Bergmann 94327d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 944d9379c4bSarnd@arndb.de static struct page *spufs_mss_mmap_nopage(struct vm_area_struct *vma, 945d9379c4bSarnd@arndb.de unsigned long address, int *type) 946d9379c4bSarnd@arndb.de { 94727d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x0000, 0x1000); 948d9379c4bSarnd@arndb.de } 949d9379c4bSarnd@arndb.de 950d9379c4bSarnd@arndb.de static struct vm_operations_struct spufs_mss_mmap_vmops = { 951d9379c4bSarnd@arndb.de .nopage = spufs_mss_mmap_nopage, 952d9379c4bSarnd@arndb.de }; 953d9379c4bSarnd@arndb.de 954d9379c4bSarnd@arndb.de /* 955d9379c4bSarnd@arndb.de * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 956d9379c4bSarnd@arndb.de */ 957d9379c4bSarnd@arndb.de static int spufs_mss_mmap(struct file *file, struct vm_area_struct *vma) 958d9379c4bSarnd@arndb.de { 959d9379c4bSarnd@arndb.de if (!(vma->vm_flags & VM_SHARED)) 960d9379c4bSarnd@arndb.de return -EINVAL; 961d9379c4bSarnd@arndb.de 962d9379c4bSarnd@arndb.de vma->vm_flags |= VM_RESERVED; 963d9379c4bSarnd@arndb.de vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 96423cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 965d9379c4bSarnd@arndb.de 966d9379c4bSarnd@arndb.de vma->vm_ops = &spufs_mss_mmap_vmops; 967d9379c4bSarnd@arndb.de return 0; 968d9379c4bSarnd@arndb.de } 96927d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 97027d5bf2aSBenjamin Herrenschmidt #define spufs_mss_mmap NULL 97127d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 972d9379c4bSarnd@arndb.de 973d9379c4bSarnd@arndb.de static int spufs_mss_open(struct inode *inode, struct file *file) 974d9379c4bSarnd@arndb.de { 975d9379c4bSarnd@arndb.de struct spufs_inode_info *i = SPUFS_I(inode); 976d9379c4bSarnd@arndb.de 977d9379c4bSarnd@arndb.de file->private_data = i->i_ctx; 978d9379c4bSarnd@arndb.de return nonseekable_open(inode, file); 979d9379c4bSarnd@arndb.de } 980d9379c4bSarnd@arndb.de 981d9379c4bSarnd@arndb.de static struct file_operations spufs_mss_fops = { 982d9379c4bSarnd@arndb.de .open = spufs_mss_open, 983d9379c4bSarnd@arndb.de .mmap = spufs_mss_mmap, 98427d5bf2aSBenjamin Herrenschmidt }; 98527d5bf2aSBenjamin Herrenschmidt 98627d5bf2aSBenjamin Herrenschmidt static struct page *spufs_psmap_mmap_nopage(struct vm_area_struct *vma, 98727d5bf2aSBenjamin Herrenschmidt unsigned long address, int *type) 98827d5bf2aSBenjamin Herrenschmidt { 98927d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x0000, 0x20000); 99027d5bf2aSBenjamin Herrenschmidt } 99127d5bf2aSBenjamin Herrenschmidt 99227d5bf2aSBenjamin Herrenschmidt static struct vm_operations_struct spufs_psmap_mmap_vmops = { 99327d5bf2aSBenjamin Herrenschmidt .nopage = spufs_psmap_mmap_nopage, 99427d5bf2aSBenjamin Herrenschmidt }; 99527d5bf2aSBenjamin Herrenschmidt 99627d5bf2aSBenjamin Herrenschmidt /* 99727d5bf2aSBenjamin Herrenschmidt * mmap support for full problem state area [0x00000 - 0x1ffff]. 99827d5bf2aSBenjamin Herrenschmidt */ 99927d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_mmap(struct file *file, struct vm_area_struct *vma) 100027d5bf2aSBenjamin Herrenschmidt { 100127d5bf2aSBenjamin Herrenschmidt if (!(vma->vm_flags & VM_SHARED)) 100227d5bf2aSBenjamin Herrenschmidt return -EINVAL; 100327d5bf2aSBenjamin Herrenschmidt 100427d5bf2aSBenjamin Herrenschmidt vma->vm_flags |= VM_RESERVED; 100527d5bf2aSBenjamin Herrenschmidt vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 100627d5bf2aSBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 100727d5bf2aSBenjamin Herrenschmidt 100827d5bf2aSBenjamin Herrenschmidt vma->vm_ops = &spufs_psmap_mmap_vmops; 100927d5bf2aSBenjamin Herrenschmidt return 0; 101027d5bf2aSBenjamin Herrenschmidt } 101127d5bf2aSBenjamin Herrenschmidt 101227d5bf2aSBenjamin Herrenschmidt static int spufs_psmap_open(struct inode *inode, struct file *file) 101327d5bf2aSBenjamin Herrenschmidt { 101427d5bf2aSBenjamin Herrenschmidt struct spufs_inode_info *i = SPUFS_I(inode); 101527d5bf2aSBenjamin Herrenschmidt 101627d5bf2aSBenjamin Herrenschmidt file->private_data = i->i_ctx; 101727d5bf2aSBenjamin Herrenschmidt return nonseekable_open(inode, file); 101827d5bf2aSBenjamin Herrenschmidt } 101927d5bf2aSBenjamin Herrenschmidt 102027d5bf2aSBenjamin Herrenschmidt static struct file_operations spufs_psmap_fops = { 102127d5bf2aSBenjamin Herrenschmidt .open = spufs_psmap_open, 102227d5bf2aSBenjamin Herrenschmidt .mmap = spufs_psmap_mmap, 1023d9379c4bSarnd@arndb.de }; 1024d9379c4bSarnd@arndb.de 1025d9379c4bSarnd@arndb.de 102627d5bf2aSBenjamin Herrenschmidt #if SPUFS_MMAP_4K 10276df10a82SMark Nutter static struct page *spufs_mfc_mmap_nopage(struct vm_area_struct *vma, 10286df10a82SMark Nutter unsigned long address, int *type) 10296df10a82SMark Nutter { 103027d5bf2aSBenjamin Herrenschmidt return spufs_ps_nopage(vma, address, type, 0x3000, 0x1000); 10316df10a82SMark Nutter } 10326df10a82SMark Nutter 10336df10a82SMark Nutter static struct vm_operations_struct spufs_mfc_mmap_vmops = { 10346df10a82SMark Nutter .nopage = spufs_mfc_mmap_nopage, 10356df10a82SMark Nutter }; 10366df10a82SMark Nutter 10376df10a82SMark Nutter /* 10386df10a82SMark Nutter * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. 10396df10a82SMark Nutter */ 10406df10a82SMark Nutter static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) 10416df10a82SMark Nutter { 10426df10a82SMark Nutter if (!(vma->vm_flags & VM_SHARED)) 10436df10a82SMark Nutter return -EINVAL; 10446df10a82SMark Nutter 10456df10a82SMark Nutter vma->vm_flags |= VM_RESERVED; 10466df10a82SMark Nutter vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 104723cc7701SBenjamin Herrenschmidt | _PAGE_NO_CACHE | _PAGE_GUARDED); 10486df10a82SMark Nutter 10496df10a82SMark Nutter vma->vm_ops = &spufs_mfc_mmap_vmops; 10506df10a82SMark Nutter return 0; 10516df10a82SMark Nutter } 105227d5bf2aSBenjamin Herrenschmidt #else /* SPUFS_MMAP_4K */ 105327d5bf2aSBenjamin Herrenschmidt #define spufs_mfc_mmap NULL 105427d5bf2aSBenjamin Herrenschmidt #endif /* !SPUFS_MMAP_4K */ 1055a33a7d73SArnd Bergmann 1056a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 1057a33a7d73SArnd Bergmann { 1058a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1059a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 1060a33a7d73SArnd Bergmann 1061a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 1062a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 1063a33a7d73SArnd Bergmann return -EINVAL; 1064a33a7d73SArnd Bergmann 1065a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 1066a33a7d73SArnd Bergmann return -EBUSY; 1067a33a7d73SArnd Bergmann 1068a33a7d73SArnd Bergmann file->private_data = ctx; 1069a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 1070a33a7d73SArnd Bergmann } 1071a33a7d73SArnd Bergmann 1072a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 1073a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 1074a33a7d73SArnd Bergmann { 1075a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 1076a33a7d73SArnd Bergmann 1077a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 1078a33a7d73SArnd Bergmann 1079a33a7d73SArnd Bergmann pr_debug("%s %s\n", __FUNCTION__, spu->name); 1080a33a7d73SArnd Bergmann if (ctx->mfc_fasync) { 1081a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1082a33a7d73SArnd Bergmann unsigned int mask; 1083a33a7d73SArnd Bergmann 1084a33a7d73SArnd Bergmann /* no need for spu_acquire in interrupt context */ 1085a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1086a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1087a33a7d73SArnd Bergmann 1088a33a7d73SArnd Bergmann mask = 0; 1089a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1090a33a7d73SArnd Bergmann mask |= POLLOUT; 1091a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1092a33a7d73SArnd Bergmann mask |= POLLIN; 1093a33a7d73SArnd Bergmann 1094a33a7d73SArnd Bergmann kill_fasync(&ctx->mfc_fasync, SIGIO, mask); 1095a33a7d73SArnd Bergmann } 1096a33a7d73SArnd Bergmann } 1097a33a7d73SArnd Bergmann 1098a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 1099a33a7d73SArnd Bergmann { 1100a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 1101a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 1102a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 1103a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 1104a33a7d73SArnd Bergmann if (*status) 1105a33a7d73SArnd Bergmann return 1; 1106a33a7d73SArnd Bergmann 1107a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 1108a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 1109a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1110a33a7d73SArnd Bergmann return 0; 1111a33a7d73SArnd Bergmann } 1112a33a7d73SArnd Bergmann 1113a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 1114a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1115a33a7d73SArnd Bergmann { 1116a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1117a33a7d73SArnd Bergmann int ret = -EINVAL; 1118a33a7d73SArnd Bergmann u32 status; 1119a33a7d73SArnd Bergmann 1120a33a7d73SArnd Bergmann if (size != 4) 1121a33a7d73SArnd Bergmann goto out; 1122a33a7d73SArnd Bergmann 1123a33a7d73SArnd Bergmann spu_acquire(ctx); 1124a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1125a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 1126a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 1127a33a7d73SArnd Bergmann ret = -EAGAIN; 1128a33a7d73SArnd Bergmann else 1129a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 1130a33a7d73SArnd Bergmann } else { 1131a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1132a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 1133a33a7d73SArnd Bergmann } 1134a33a7d73SArnd Bergmann spu_release(ctx); 1135a33a7d73SArnd Bergmann 1136a33a7d73SArnd Bergmann if (ret) 1137a33a7d73SArnd Bergmann goto out; 1138a33a7d73SArnd Bergmann 1139a33a7d73SArnd Bergmann ret = 4; 1140a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 1141a33a7d73SArnd Bergmann ret = -EFAULT; 1142a33a7d73SArnd Bergmann 1143a33a7d73SArnd Bergmann out: 1144a33a7d73SArnd Bergmann return ret; 1145a33a7d73SArnd Bergmann } 1146a33a7d73SArnd Bergmann 1147a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 1148a33a7d73SArnd Bergmann { 1149a33a7d73SArnd Bergmann pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, 1150a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 1151a33a7d73SArnd Bergmann 1152a33a7d73SArnd Bergmann switch (cmd->cmd) { 1153a33a7d73SArnd Bergmann case MFC_PUT_CMD: 1154a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 1155a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 1156a33a7d73SArnd Bergmann case MFC_GET_CMD: 1157a33a7d73SArnd Bergmann case MFC_GETF_CMD: 1158a33a7d73SArnd Bergmann case MFC_GETB_CMD: 1159a33a7d73SArnd Bergmann break; 1160a33a7d73SArnd Bergmann default: 1161a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 1162a33a7d73SArnd Bergmann return -EIO; 1163a33a7d73SArnd Bergmann } 1164a33a7d73SArnd Bergmann 1165a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 1166a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment, ea %lx lsa %x\n", 1167a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 1168a33a7d73SArnd Bergmann return -EIO; 1169a33a7d73SArnd Bergmann } 1170a33a7d73SArnd Bergmann 1171a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 1172a33a7d73SArnd Bergmann case 1: 1173a33a7d73SArnd Bergmann break; 1174a33a7d73SArnd Bergmann case 2: 1175a33a7d73SArnd Bergmann if (cmd->lsa & 1) 1176a33a7d73SArnd Bergmann goto error; 1177a33a7d73SArnd Bergmann break; 1178a33a7d73SArnd Bergmann case 4: 1179a33a7d73SArnd Bergmann if (cmd->lsa & 3) 1180a33a7d73SArnd Bergmann goto error; 1181a33a7d73SArnd Bergmann break; 1182a33a7d73SArnd Bergmann case 8: 1183a33a7d73SArnd Bergmann if (cmd->lsa & 7) 1184a33a7d73SArnd Bergmann goto error; 1185a33a7d73SArnd Bergmann break; 1186a33a7d73SArnd Bergmann case 0: 1187a33a7d73SArnd Bergmann if (cmd->lsa & 15) 1188a33a7d73SArnd Bergmann goto error; 1189a33a7d73SArnd Bergmann break; 1190a33a7d73SArnd Bergmann error: 1191a33a7d73SArnd Bergmann default: 1192a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 1193a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 1194a33a7d73SArnd Bergmann return -EIO; 1195a33a7d73SArnd Bergmann } 1196a33a7d73SArnd Bergmann 1197a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 1198a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 1199a33a7d73SArnd Bergmann return -EIO; 1200a33a7d73SArnd Bergmann } 1201a33a7d73SArnd Bergmann 1202a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 1203a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 1204a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 1205a33a7d73SArnd Bergmann return -EIO; 1206a33a7d73SArnd Bergmann } 1207a33a7d73SArnd Bergmann 1208a33a7d73SArnd Bergmann if (cmd->class) { 1209a33a7d73SArnd Bergmann /* not supported in this version */ 1210a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 1211a33a7d73SArnd Bergmann return -EIO; 1212a33a7d73SArnd Bergmann } 1213a33a7d73SArnd Bergmann 1214a33a7d73SArnd Bergmann return 0; 1215a33a7d73SArnd Bergmann } 1216a33a7d73SArnd Bergmann 1217a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 1218a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 1219a33a7d73SArnd Bergmann int *error) 1220a33a7d73SArnd Bergmann { 1221a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1222a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 1223a33a7d73SArnd Bergmann /* wait for any tag group to complete 1224a33a7d73SArnd Bergmann so we have space for the new command */ 1225a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 1226a33a7d73SArnd Bergmann /* try again, because the queue might be 1227a33a7d73SArnd Bergmann empty again */ 1228a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 1229a33a7d73SArnd Bergmann if (*error == -EAGAIN) 1230a33a7d73SArnd Bergmann return 0; 1231a33a7d73SArnd Bergmann } 1232a33a7d73SArnd Bergmann return 1; 1233a33a7d73SArnd Bergmann } 1234a33a7d73SArnd Bergmann 1235a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 1236a33a7d73SArnd Bergmann size_t size, loff_t *pos) 1237a33a7d73SArnd Bergmann { 1238a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1239a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 1240a33a7d73SArnd Bergmann int ret = -EINVAL; 1241a33a7d73SArnd Bergmann 1242a33a7d73SArnd Bergmann if (size != sizeof cmd) 1243a33a7d73SArnd Bergmann goto out; 1244a33a7d73SArnd Bergmann 1245a33a7d73SArnd Bergmann ret = -EFAULT; 1246a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 1247a33a7d73SArnd Bergmann goto out; 1248a33a7d73SArnd Bergmann 1249a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 1250a33a7d73SArnd Bergmann if (ret) 1251a33a7d73SArnd Bergmann goto out; 1252a33a7d73SArnd Bergmann 1253a33a7d73SArnd Bergmann spu_acquire_runnable(ctx); 1254a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 1255a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 1256a33a7d73SArnd Bergmann } else { 1257a33a7d73SArnd Bergmann int status; 1258a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1259a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 1260a33a7d73SArnd Bergmann if (status) 1261a33a7d73SArnd Bergmann ret = status; 1262a33a7d73SArnd Bergmann } 1263a33a7d73SArnd Bergmann spu_release(ctx); 1264a33a7d73SArnd Bergmann 1265a33a7d73SArnd Bergmann if (ret) 1266a33a7d73SArnd Bergmann goto out; 1267a33a7d73SArnd Bergmann 1268a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 1269a33a7d73SArnd Bergmann 1270a33a7d73SArnd Bergmann out: 1271a33a7d73SArnd Bergmann return ret; 1272a33a7d73SArnd Bergmann } 1273a33a7d73SArnd Bergmann 1274a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) 1275a33a7d73SArnd Bergmann { 1276a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1277a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 1278a33a7d73SArnd Bergmann unsigned int mask; 1279a33a7d73SArnd Bergmann 1280a33a7d73SArnd Bergmann spu_acquire(ctx); 1281a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 1282a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 1283a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 1284a33a7d73SArnd Bergmann spu_release(ctx); 1285a33a7d73SArnd Bergmann 1286a33a7d73SArnd Bergmann poll_wait(file, &ctx->mfc_wq, wait); 1287a33a7d73SArnd Bergmann 1288a33a7d73SArnd Bergmann mask = 0; 1289a33a7d73SArnd Bergmann if (free_elements & 0xffff) 1290a33a7d73SArnd Bergmann mask |= POLLOUT | POLLWRNORM; 1291a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 1292a33a7d73SArnd Bergmann mask |= POLLIN | POLLRDNORM; 1293a33a7d73SArnd Bergmann 1294a33a7d73SArnd Bergmann pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__, 1295a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 1296a33a7d73SArnd Bergmann 1297a33a7d73SArnd Bergmann return mask; 1298a33a7d73SArnd Bergmann } 1299a33a7d73SArnd Bergmann 130073b6af8aSAl Viro static int spufs_mfc_flush(struct file *file, fl_owner_t id) 1301a33a7d73SArnd Bergmann { 1302a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1303a33a7d73SArnd Bergmann int ret; 1304a33a7d73SArnd Bergmann 1305a33a7d73SArnd Bergmann spu_acquire(ctx); 1306a33a7d73SArnd Bergmann #if 0 1307a33a7d73SArnd Bergmann /* this currently hangs */ 1308a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1309a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 1310a33a7d73SArnd Bergmann if (ret) 1311a33a7d73SArnd Bergmann goto out; 1312a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 1313a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 1314a33a7d73SArnd Bergmann out: 1315a33a7d73SArnd Bergmann #else 1316a33a7d73SArnd Bergmann ret = 0; 1317a33a7d73SArnd Bergmann #endif 1318a33a7d73SArnd Bergmann spu_release(ctx); 1319a33a7d73SArnd Bergmann 1320a33a7d73SArnd Bergmann return ret; 1321a33a7d73SArnd Bergmann } 1322a33a7d73SArnd Bergmann 1323a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, 1324a33a7d73SArnd Bergmann int datasync) 1325a33a7d73SArnd Bergmann { 132673b6af8aSAl Viro return spufs_mfc_flush(file, NULL); 1327a33a7d73SArnd Bergmann } 1328a33a7d73SArnd Bergmann 1329a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on) 1330a33a7d73SArnd Bergmann { 1331a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 1332a33a7d73SArnd Bergmann 1333a33a7d73SArnd Bergmann return fasync_helper(fd, file, on, &ctx->mfc_fasync); 1334a33a7d73SArnd Bergmann } 1335a33a7d73SArnd Bergmann 1336a33a7d73SArnd Bergmann static struct file_operations spufs_mfc_fops = { 1337a33a7d73SArnd Bergmann .open = spufs_mfc_open, 1338a33a7d73SArnd Bergmann .read = spufs_mfc_read, 1339a33a7d73SArnd Bergmann .write = spufs_mfc_write, 1340a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 1341a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 1342a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 1343a33a7d73SArnd Bergmann .fasync = spufs_mfc_fasync, 13446df10a82SMark Nutter .mmap = spufs_mfc_mmap, 1345a33a7d73SArnd Bergmann }; 1346a33a7d73SArnd Bergmann 1347099814bbSJeremy Kerr 1348099814bbSJeremy Kerr static int spufs_recycle_open(struct inode *inode, struct file *file) 1349099814bbSJeremy Kerr { 1350099814bbSJeremy Kerr file->private_data = SPUFS_I(inode)->i_ctx; 1351099814bbSJeremy Kerr return nonseekable_open(inode, file); 1352099814bbSJeremy Kerr } 1353099814bbSJeremy Kerr 1354099814bbSJeremy Kerr static ssize_t spufs_recycle_write(struct file *file, 1355099814bbSJeremy Kerr const char __user *buffer, size_t size, loff_t *pos) 1356099814bbSJeremy Kerr { 1357099814bbSJeremy Kerr struct spu_context *ctx = file->private_data; 1358099814bbSJeremy Kerr int ret; 1359099814bbSJeremy Kerr 1360099814bbSJeremy Kerr if (!(ctx->flags & SPU_CREATE_ISOLATE)) 1361099814bbSJeremy Kerr return -EINVAL; 1362099814bbSJeremy Kerr 1363099814bbSJeremy Kerr if (size < 1) 1364099814bbSJeremy Kerr return -EINVAL; 1365099814bbSJeremy Kerr 1366099814bbSJeremy Kerr ret = spu_recycle_isolated(ctx); 1367099814bbSJeremy Kerr 1368099814bbSJeremy Kerr if (ret) 1369099814bbSJeremy Kerr return ret; 1370099814bbSJeremy Kerr return size; 1371099814bbSJeremy Kerr } 1372099814bbSJeremy Kerr 1373099814bbSJeremy Kerr static struct file_operations spufs_recycle_fops = { 1374099814bbSJeremy Kerr .open = spufs_recycle_open, 1375099814bbSJeremy Kerr .write = spufs_recycle_write, 1376099814bbSJeremy Kerr }; 1377099814bbSJeremy Kerr 137867207b96SArnd Bergmann static void spufs_npc_set(void *data, u64 val) 137967207b96SArnd Bergmann { 138067207b96SArnd Bergmann struct spu_context *ctx = data; 13818b3d6663SArnd Bergmann spu_acquire(ctx); 13828b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 13838b3d6663SArnd Bergmann spu_release(ctx); 138467207b96SArnd Bergmann } 138567207b96SArnd Bergmann 138667207b96SArnd Bergmann static u64 spufs_npc_get(void *data) 138767207b96SArnd Bergmann { 138867207b96SArnd Bergmann struct spu_context *ctx = data; 138967207b96SArnd Bergmann u64 ret; 13908b3d6663SArnd Bergmann spu_acquire(ctx); 13918b3d6663SArnd Bergmann ret = ctx->ops->npc_read(ctx); 13928b3d6663SArnd Bergmann spu_release(ctx); 139367207b96SArnd Bergmann return ret; 139467207b96SArnd Bergmann } 13959b5047e2SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, 13969b5047e2SDwayne Grant McConnell "0x%llx\n") 139767207b96SArnd Bergmann 13988b3d6663SArnd Bergmann static void spufs_decr_set(void *data, u64 val) 13998b3d6663SArnd Bergmann { 14008b3d6663SArnd Bergmann struct spu_context *ctx = data; 14018b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14028b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14038b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 14048b3d6663SArnd Bergmann spu_release(ctx); 14058b3d6663SArnd Bergmann } 14068b3d6663SArnd Bergmann 14078b3d6663SArnd Bergmann static u64 spufs_decr_get(void *data) 14088b3d6663SArnd Bergmann { 14098b3d6663SArnd Bergmann struct spu_context *ctx = data; 14108b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14118b3d6663SArnd Bergmann u64 ret; 14128b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14138b3d6663SArnd Bergmann ret = lscsa->decr.slot[0]; 14148b3d6663SArnd Bergmann spu_release(ctx); 14158b3d6663SArnd Bergmann return ret; 14168b3d6663SArnd Bergmann } 14178b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 14189b5047e2SDwayne Grant McConnell "0x%llx\n") 14198b3d6663SArnd Bergmann 14208b3d6663SArnd Bergmann static void spufs_decr_status_set(void *data, u64 val) 14218b3d6663SArnd Bergmann { 14228b3d6663SArnd Bergmann struct spu_context *ctx = data; 14238b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14248b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14258b3d6663SArnd Bergmann lscsa->decr_status.slot[0] = (u32) val; 14268b3d6663SArnd Bergmann spu_release(ctx); 14278b3d6663SArnd Bergmann } 14288b3d6663SArnd Bergmann 14298b3d6663SArnd Bergmann static u64 spufs_decr_status_get(void *data) 14308b3d6663SArnd Bergmann { 14318b3d6663SArnd Bergmann struct spu_context *ctx = data; 14328b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14338b3d6663SArnd Bergmann u64 ret; 14348b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14358b3d6663SArnd Bergmann ret = lscsa->decr_status.slot[0]; 14368b3d6663SArnd Bergmann spu_release(ctx); 14378b3d6663SArnd Bergmann return ret; 14388b3d6663SArnd Bergmann } 14398b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 14409b5047e2SDwayne Grant McConnell spufs_decr_status_set, "0x%llx\n") 14418b3d6663SArnd Bergmann 14428b3d6663SArnd Bergmann static void spufs_event_mask_set(void *data, u64 val) 14438b3d6663SArnd Bergmann { 14448b3d6663SArnd Bergmann struct spu_context *ctx = data; 14458b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14468b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14478b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 14488b3d6663SArnd Bergmann spu_release(ctx); 14498b3d6663SArnd Bergmann } 14508b3d6663SArnd Bergmann 14518b3d6663SArnd Bergmann static u64 spufs_event_mask_get(void *data) 14528b3d6663SArnd Bergmann { 14538b3d6663SArnd Bergmann struct spu_context *ctx = data; 14548b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14558b3d6663SArnd Bergmann u64 ret; 14568b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14578b3d6663SArnd Bergmann ret = lscsa->event_mask.slot[0]; 14588b3d6663SArnd Bergmann spu_release(ctx); 14598b3d6663SArnd Bergmann return ret; 14608b3d6663SArnd Bergmann } 14618b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 14629b5047e2SDwayne Grant McConnell spufs_event_mask_set, "0x%llx\n") 14638b3d6663SArnd Bergmann 1464b9e3bd77SDwayne Grant McConnell static u64 spufs_event_status_get(void *data) 1465b9e3bd77SDwayne Grant McConnell { 1466b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = data; 1467b9e3bd77SDwayne Grant McConnell struct spu_state *state = &ctx->csa; 1468b9e3bd77SDwayne Grant McConnell u64 ret = 0; 1469b9e3bd77SDwayne Grant McConnell u64 stat; 1470b9e3bd77SDwayne Grant McConnell 1471b9e3bd77SDwayne Grant McConnell spu_acquire_saved(ctx); 1472b9e3bd77SDwayne Grant McConnell stat = state->spu_chnlcnt_RW[0]; 1473b9e3bd77SDwayne Grant McConnell if (stat) 1474b9e3bd77SDwayne Grant McConnell ret = state->spu_chnldata_RW[0]; 1475b9e3bd77SDwayne Grant McConnell spu_release(ctx); 1476b9e3bd77SDwayne Grant McConnell return ret; 1477b9e3bd77SDwayne Grant McConnell } 1478b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_event_status_ops, spufs_event_status_get, 1479b9e3bd77SDwayne Grant McConnell NULL, "0x%llx\n") 1480b9e3bd77SDwayne Grant McConnell 14818b3d6663SArnd Bergmann static void spufs_srr0_set(void *data, u64 val) 14828b3d6663SArnd Bergmann { 14838b3d6663SArnd Bergmann struct spu_context *ctx = data; 14848b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14858b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14868b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 14878b3d6663SArnd Bergmann spu_release(ctx); 14888b3d6663SArnd Bergmann } 14898b3d6663SArnd Bergmann 14908b3d6663SArnd Bergmann static u64 spufs_srr0_get(void *data) 14918b3d6663SArnd Bergmann { 14928b3d6663SArnd Bergmann struct spu_context *ctx = data; 14938b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 14948b3d6663SArnd Bergmann u64 ret; 14958b3d6663SArnd Bergmann spu_acquire_saved(ctx); 14968b3d6663SArnd Bergmann ret = lscsa->srr0.slot[0]; 14978b3d6663SArnd Bergmann spu_release(ctx); 14988b3d6663SArnd Bergmann return ret; 14998b3d6663SArnd Bergmann } 15008b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 15019b5047e2SDwayne Grant McConnell "0x%llx\n") 15028b3d6663SArnd Bergmann 15037b1a7014Sarnd@arndb.de static u64 spufs_id_get(void *data) 15047b1a7014Sarnd@arndb.de { 15057b1a7014Sarnd@arndb.de struct spu_context *ctx = data; 15067b1a7014Sarnd@arndb.de u64 num; 15077b1a7014Sarnd@arndb.de 15087b1a7014Sarnd@arndb.de spu_acquire(ctx); 15097b1a7014Sarnd@arndb.de if (ctx->state == SPU_STATE_RUNNABLE) 15107b1a7014Sarnd@arndb.de num = ctx->spu->number; 15117b1a7014Sarnd@arndb.de else 15127b1a7014Sarnd@arndb.de num = (unsigned int)-1; 15137b1a7014Sarnd@arndb.de spu_release(ctx); 15147b1a7014Sarnd@arndb.de 15157b1a7014Sarnd@arndb.de return num; 15167b1a7014Sarnd@arndb.de } 1517e45d6634SAl Viro DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, NULL, "0x%llx\n") 15187b1a7014Sarnd@arndb.de 151986767277SArnd Bergmann static u64 spufs_object_id_get(void *data) 152086767277SArnd Bergmann { 152186767277SArnd Bergmann struct spu_context *ctx = data; 152286767277SArnd Bergmann return ctx->object_id; 152386767277SArnd Bergmann } 152486767277SArnd Bergmann 152586767277SArnd Bergmann static void spufs_object_id_set(void *data, u64 id) 152686767277SArnd Bergmann { 152786767277SArnd Bergmann struct spu_context *ctx = data; 152886767277SArnd Bergmann ctx->object_id = id; 152986767277SArnd Bergmann } 153086767277SArnd Bergmann 153186767277SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_object_id_ops, spufs_object_id_get, 153286767277SArnd Bergmann spufs_object_id_set, "0x%llx\n"); 153386767277SArnd Bergmann 1534b9e3bd77SDwayne Grant McConnell static u64 spufs_lslr_get(void *data) 1535b9e3bd77SDwayne Grant McConnell { 1536b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = data; 1537b9e3bd77SDwayne Grant McConnell u64 ret; 1538b9e3bd77SDwayne Grant McConnell 1539b9e3bd77SDwayne Grant McConnell spu_acquire_saved(ctx); 1540b9e3bd77SDwayne Grant McConnell ret = ctx->csa.priv2.spu_lslr_RW; 1541b9e3bd77SDwayne Grant McConnell spu_release(ctx); 1542b9e3bd77SDwayne Grant McConnell 1543b9e3bd77SDwayne Grant McConnell return ret; 1544b9e3bd77SDwayne Grant McConnell } 1545b9e3bd77SDwayne Grant McConnell DEFINE_SIMPLE_ATTRIBUTE(spufs_lslr_ops, spufs_lslr_get, NULL, "0x%llx\n") 1546b9e3bd77SDwayne Grant McConnell 1547b9e3bd77SDwayne Grant McConnell static int spufs_info_open(struct inode *inode, struct file *file) 1548b9e3bd77SDwayne Grant McConnell { 1549b9e3bd77SDwayne Grant McConnell struct spufs_inode_info *i = SPUFS_I(inode); 1550b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = i->i_ctx; 1551b9e3bd77SDwayne Grant McConnell file->private_data = ctx; 1552b9e3bd77SDwayne Grant McConnell return 0; 1553b9e3bd77SDwayne Grant McConnell } 1554b9e3bd77SDwayne Grant McConnell 155569a2f00cSDwayne Grant McConnell static ssize_t spufs_mbox_info_read(struct file *file, char __user *buf, 155669a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 155769a2f00cSDwayne Grant McConnell { 155869a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 155969a2f00cSDwayne Grant McConnell u32 mbox_stat; 156069a2f00cSDwayne Grant McConnell u32 data; 156169a2f00cSDwayne Grant McConnell 156269a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 156369a2f00cSDwayne Grant McConnell return -EFAULT; 156469a2f00cSDwayne Grant McConnell 156569a2f00cSDwayne Grant McConnell spu_acquire_saved(ctx); 156669a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 156769a2f00cSDwayne Grant McConnell mbox_stat = ctx->csa.prob.mb_stat_R; 156869a2f00cSDwayne Grant McConnell if (mbox_stat & 0x0000ff) { 156969a2f00cSDwayne Grant McConnell data = ctx->csa.prob.pu_mb_R; 157069a2f00cSDwayne Grant McConnell } 157169a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 157269a2f00cSDwayne Grant McConnell spu_release(ctx); 157369a2f00cSDwayne Grant McConnell 157469a2f00cSDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 157569a2f00cSDwayne Grant McConnell } 157669a2f00cSDwayne Grant McConnell 157769a2f00cSDwayne Grant McConnell static struct file_operations spufs_mbox_info_fops = { 157869a2f00cSDwayne Grant McConnell .open = spufs_info_open, 157969a2f00cSDwayne Grant McConnell .read = spufs_mbox_info_read, 158069a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 158169a2f00cSDwayne Grant McConnell }; 158269a2f00cSDwayne Grant McConnell 158369a2f00cSDwayne Grant McConnell static ssize_t spufs_ibox_info_read(struct file *file, char __user *buf, 158469a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 158569a2f00cSDwayne Grant McConnell { 158669a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 158769a2f00cSDwayne Grant McConnell u32 ibox_stat; 158869a2f00cSDwayne Grant McConnell u32 data; 158969a2f00cSDwayne Grant McConnell 159069a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 159169a2f00cSDwayne Grant McConnell return -EFAULT; 159269a2f00cSDwayne Grant McConnell 159369a2f00cSDwayne Grant McConnell spu_acquire_saved(ctx); 159469a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 159569a2f00cSDwayne Grant McConnell ibox_stat = ctx->csa.prob.mb_stat_R; 159669a2f00cSDwayne Grant McConnell if (ibox_stat & 0xff0000) { 159769a2f00cSDwayne Grant McConnell data = ctx->csa.priv2.puint_mb_R; 159869a2f00cSDwayne Grant McConnell } 159969a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 160069a2f00cSDwayne Grant McConnell spu_release(ctx); 160169a2f00cSDwayne Grant McConnell 160269a2f00cSDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, sizeof data); 160369a2f00cSDwayne Grant McConnell } 160469a2f00cSDwayne Grant McConnell 160569a2f00cSDwayne Grant McConnell static struct file_operations spufs_ibox_info_fops = { 160669a2f00cSDwayne Grant McConnell .open = spufs_info_open, 160769a2f00cSDwayne Grant McConnell .read = spufs_ibox_info_read, 160869a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 160969a2f00cSDwayne Grant McConnell }; 161069a2f00cSDwayne Grant McConnell 161169a2f00cSDwayne Grant McConnell static ssize_t spufs_wbox_info_read(struct file *file, char __user *buf, 161269a2f00cSDwayne Grant McConnell size_t len, loff_t *pos) 161369a2f00cSDwayne Grant McConnell { 161469a2f00cSDwayne Grant McConnell struct spu_context *ctx = file->private_data; 161569a2f00cSDwayne Grant McConnell int i, cnt; 161669a2f00cSDwayne Grant McConnell u32 data[4]; 161769a2f00cSDwayne Grant McConnell u32 wbox_stat; 161869a2f00cSDwayne Grant McConnell 161969a2f00cSDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 162069a2f00cSDwayne Grant McConnell return -EFAULT; 162169a2f00cSDwayne Grant McConnell 162269a2f00cSDwayne Grant McConnell spu_acquire_saved(ctx); 162369a2f00cSDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 162469a2f00cSDwayne Grant McConnell wbox_stat = ctx->csa.prob.mb_stat_R; 162569a2f00cSDwayne Grant McConnell cnt = (wbox_stat & 0x00ff00) >> 8; 162669a2f00cSDwayne Grant McConnell for (i = 0; i < cnt; i++) { 162769a2f00cSDwayne Grant McConnell data[i] = ctx->csa.spu_mailbox_data[i]; 162869a2f00cSDwayne Grant McConnell } 162969a2f00cSDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 163069a2f00cSDwayne Grant McConnell spu_release(ctx); 163169a2f00cSDwayne Grant McConnell 163269a2f00cSDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &data, 163369a2f00cSDwayne Grant McConnell cnt * sizeof(u32)); 163469a2f00cSDwayne Grant McConnell } 163569a2f00cSDwayne Grant McConnell 163669a2f00cSDwayne Grant McConnell static struct file_operations spufs_wbox_info_fops = { 163769a2f00cSDwayne Grant McConnell .open = spufs_info_open, 163869a2f00cSDwayne Grant McConnell .read = spufs_wbox_info_read, 163969a2f00cSDwayne Grant McConnell .llseek = generic_file_llseek, 164069a2f00cSDwayne Grant McConnell }; 164169a2f00cSDwayne Grant McConnell 1642b9e3bd77SDwayne Grant McConnell static ssize_t spufs_dma_info_read(struct file *file, char __user *buf, 1643b9e3bd77SDwayne Grant McConnell size_t len, loff_t *pos) 1644b9e3bd77SDwayne Grant McConnell { 1645b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1646b9e3bd77SDwayne Grant McConnell struct spu_dma_info info; 1647b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *spuqp; 1648b9e3bd77SDwayne Grant McConnell int i; 1649b9e3bd77SDwayne Grant McConnell 1650b9e3bd77SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 1651b9e3bd77SDwayne Grant McConnell return -EFAULT; 1652b9e3bd77SDwayne Grant McConnell 1653b9e3bd77SDwayne Grant McConnell spu_acquire_saved(ctx); 1654b9e3bd77SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 1655b9e3bd77SDwayne Grant McConnell info.dma_info_type = ctx->csa.priv2.spu_tag_status_query_RW; 1656b9e3bd77SDwayne Grant McConnell info.dma_info_mask = ctx->csa.lscsa->tag_mask.slot[0]; 1657b9e3bd77SDwayne Grant McConnell info.dma_info_status = ctx->csa.spu_chnldata_RW[24]; 1658b9e3bd77SDwayne Grant McConnell info.dma_info_stall_and_notify = ctx->csa.spu_chnldata_RW[25]; 1659b9e3bd77SDwayne Grant McConnell info.dma_info_atomic_command_status = ctx->csa.spu_chnldata_RW[27]; 1660b9e3bd77SDwayne Grant McConnell for (i = 0; i < 16; i++) { 1661b9e3bd77SDwayne Grant McConnell qp = &info.dma_info_command_data[i]; 1662b9e3bd77SDwayne Grant McConnell spuqp = &ctx->csa.priv2.spuq[i]; 1663b9e3bd77SDwayne Grant McConnell 1664b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = spuqp->mfc_cq_data0_RW; 1665b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = spuqp->mfc_cq_data1_RW; 1666b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = spuqp->mfc_cq_data2_RW; 1667b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = spuqp->mfc_cq_data3_RW; 1668b9e3bd77SDwayne Grant McConnell } 1669b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 1670b9e3bd77SDwayne Grant McConnell spu_release(ctx); 1671b9e3bd77SDwayne Grant McConnell 1672b9e3bd77SDwayne Grant McConnell return simple_read_from_buffer(buf, len, pos, &info, 1673b9e3bd77SDwayne Grant McConnell sizeof info); 1674b9e3bd77SDwayne Grant McConnell } 1675b9e3bd77SDwayne Grant McConnell 1676b9e3bd77SDwayne Grant McConnell static struct file_operations spufs_dma_info_fops = { 1677b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 1678b9e3bd77SDwayne Grant McConnell .read = spufs_dma_info_read, 1679b9e3bd77SDwayne Grant McConnell }; 1680b9e3bd77SDwayne Grant McConnell 1681b9e3bd77SDwayne Grant McConnell static ssize_t spufs_proxydma_info_read(struct file *file, char __user *buf, 1682b9e3bd77SDwayne Grant McConnell size_t len, loff_t *pos) 1683b9e3bd77SDwayne Grant McConnell { 1684b9e3bd77SDwayne Grant McConnell struct spu_context *ctx = file->private_data; 1685b9e3bd77SDwayne Grant McConnell struct spu_proxydma_info info; 1686b9e3bd77SDwayne Grant McConnell int ret = sizeof info; 1687b9e3bd77SDwayne Grant McConnell struct mfc_cq_sr *qp, *puqp; 1688b9e3bd77SDwayne Grant McConnell int i; 1689b9e3bd77SDwayne Grant McConnell 1690b9e3bd77SDwayne Grant McConnell if (len < ret) 1691b9e3bd77SDwayne Grant McConnell return -EINVAL; 1692b9e3bd77SDwayne Grant McConnell 1693b9e3bd77SDwayne Grant McConnell if (!access_ok(VERIFY_WRITE, buf, len)) 1694b9e3bd77SDwayne Grant McConnell return -EFAULT; 1695b9e3bd77SDwayne Grant McConnell 1696b9e3bd77SDwayne Grant McConnell spu_acquire_saved(ctx); 1697b9e3bd77SDwayne Grant McConnell spin_lock(&ctx->csa.register_lock); 1698b9e3bd77SDwayne Grant McConnell info.proxydma_info_type = ctx->csa.prob.dma_querytype_RW; 1699b9e3bd77SDwayne Grant McConnell info.proxydma_info_mask = ctx->csa.prob.dma_querymask_RW; 1700b9e3bd77SDwayne Grant McConnell info.proxydma_info_status = ctx->csa.prob.dma_tagstatus_R; 1701b9e3bd77SDwayne Grant McConnell for (i = 0; i < 8; i++) { 1702b9e3bd77SDwayne Grant McConnell qp = &info.proxydma_info_command_data[i]; 1703b9e3bd77SDwayne Grant McConnell puqp = &ctx->csa.priv2.puq[i]; 1704b9e3bd77SDwayne Grant McConnell 1705b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data0_RW = puqp->mfc_cq_data0_RW; 1706b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data1_RW = puqp->mfc_cq_data1_RW; 1707b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data2_RW = puqp->mfc_cq_data2_RW; 1708b9e3bd77SDwayne Grant McConnell qp->mfc_cq_data3_RW = puqp->mfc_cq_data3_RW; 1709b9e3bd77SDwayne Grant McConnell } 1710b9e3bd77SDwayne Grant McConnell spin_unlock(&ctx->csa.register_lock); 1711b9e3bd77SDwayne Grant McConnell spu_release(ctx); 1712b9e3bd77SDwayne Grant McConnell 1713b9e3bd77SDwayne Grant McConnell if (copy_to_user(buf, &info, sizeof info)) 1714b9e3bd77SDwayne Grant McConnell ret = -EFAULT; 1715b9e3bd77SDwayne Grant McConnell 1716b9e3bd77SDwayne Grant McConnell return ret; 1717b9e3bd77SDwayne Grant McConnell } 1718b9e3bd77SDwayne Grant McConnell 1719b9e3bd77SDwayne Grant McConnell static struct file_operations spufs_proxydma_info_fops = { 1720b9e3bd77SDwayne Grant McConnell .open = spufs_info_open, 1721b9e3bd77SDwayne Grant McConnell .read = spufs_proxydma_info_read, 1722b9e3bd77SDwayne Grant McConnell }; 1723b9e3bd77SDwayne Grant McConnell 172467207b96SArnd Bergmann struct tree_descr spufs_dir_contents[] = { 172567207b96SArnd Bergmann { "mem", &spufs_mem_fops, 0666, }, 17268b3d6663SArnd Bergmann { "regs", &spufs_regs_fops, 0666, }, 172767207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 172867207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 172967207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 173067207b96SArnd Bergmann { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, 173167207b96SArnd Bergmann { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, 173267207b96SArnd Bergmann { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, 173367207b96SArnd Bergmann { "signal1", &spufs_signal1_fops, 0666, }, 173467207b96SArnd Bergmann { "signal2", &spufs_signal2_fops, 0666, }, 173567207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 173667207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 17376df10a82SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 17388b3d6663SArnd Bergmann { "fpcr", &spufs_fpcr_fops, 0666, }, 1739b9e3bd77SDwayne Grant McConnell { "lslr", &spufs_lslr_ops, 0444, }, 1740b9e3bd77SDwayne Grant McConnell { "mfc", &spufs_mfc_fops, 0666, }, 1741b9e3bd77SDwayne Grant McConnell { "mss", &spufs_mss_fops, 0666, }, 1742b9e3bd77SDwayne Grant McConnell { "npc", &spufs_npc_ops, 0666, }, 1743b9e3bd77SDwayne Grant McConnell { "srr0", &spufs_srr0_ops, 0666, }, 17448b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 17458b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 17468b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 1747b9e3bd77SDwayne Grant McConnell { "event_status", &spufs_event_status_ops, 0444, }, 174827d5bf2aSBenjamin Herrenschmidt { "psmap", &spufs_psmap_fops, 0666, }, 174986767277SArnd Bergmann { "phys-id", &spufs_id_ops, 0666, }, 175086767277SArnd Bergmann { "object-id", &spufs_object_id_ops, 0666, }, 175169a2f00cSDwayne Grant McConnell { "mbox_info", &spufs_mbox_info_fops, 0444, }, 175269a2f00cSDwayne Grant McConnell { "ibox_info", &spufs_ibox_info_fops, 0444, }, 175369a2f00cSDwayne Grant McConnell { "wbox_info", &spufs_wbox_info_fops, 0444, }, 1754b9e3bd77SDwayne Grant McConnell { "dma_info", &spufs_dma_info_fops, 0444, }, 1755b9e3bd77SDwayne Grant McConnell { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, 175667207b96SArnd Bergmann {}, 175767207b96SArnd Bergmann }; 17585737edd1SMark Nutter 17595737edd1SMark Nutter struct tree_descr spufs_dir_nosched_contents[] = { 17605737edd1SMark Nutter { "mem", &spufs_mem_fops, 0666, }, 17615737edd1SMark Nutter { "mbox", &spufs_mbox_fops, 0444, }, 17625737edd1SMark Nutter { "ibox", &spufs_ibox_fops, 0444, }, 17635737edd1SMark Nutter { "wbox", &spufs_wbox_fops, 0222, }, 17645737edd1SMark Nutter { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, 17655737edd1SMark Nutter { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, 17665737edd1SMark Nutter { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, 17675737edd1SMark Nutter { "signal1", &spufs_signal1_fops, 0666, }, 17685737edd1SMark Nutter { "signal2", &spufs_signal2_fops, 0666, }, 17695737edd1SMark Nutter { "signal1_type", &spufs_signal1_type, 0666, }, 17705737edd1SMark Nutter { "signal2_type", &spufs_signal2_type, 0666, }, 17715737edd1SMark Nutter { "mss", &spufs_mss_fops, 0666, }, 17725737edd1SMark Nutter { "mfc", &spufs_mfc_fops, 0666, }, 17735737edd1SMark Nutter { "cntl", &spufs_cntl_fops, 0666, }, 17745737edd1SMark Nutter { "npc", &spufs_npc_ops, 0666, }, 17755737edd1SMark Nutter { "psmap", &spufs_psmap_fops, 0666, }, 17765737edd1SMark Nutter { "phys-id", &spufs_id_ops, 0666, }, 17775737edd1SMark Nutter { "object-id", &spufs_object_id_ops, 0666, }, 1778099814bbSJeremy Kerr { "recycle", &spufs_recycle_fops, 0222, }, 17795737edd1SMark Nutter {}, 17805737edd1SMark Nutter }; 1781