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> 3567207b96SArnd Bergmann #include <asm/uaccess.h> 3667207b96SArnd Bergmann 3767207b96SArnd Bergmann #include "spufs.h" 3867207b96SArnd Bergmann 398b3d6663SArnd Bergmann 4067207b96SArnd Bergmann static int 4167207b96SArnd Bergmann spufs_mem_open(struct inode *inode, struct file *file) 4267207b96SArnd Bergmann { 4367207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 4467207b96SArnd Bergmann file->private_data = i->i_ctx; 458b3d6663SArnd Bergmann file->f_mapping = i->i_ctx->local_store; 4667207b96SArnd Bergmann return 0; 4767207b96SArnd Bergmann } 4867207b96SArnd Bergmann 4967207b96SArnd Bergmann static ssize_t 5067207b96SArnd Bergmann spufs_mem_read(struct file *file, char __user *buffer, 5167207b96SArnd Bergmann size_t size, loff_t *pos) 5267207b96SArnd Bergmann { 538b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 548b3d6663SArnd Bergmann char *local_store; 5567207b96SArnd Bergmann int ret; 5667207b96SArnd Bergmann 578b3d6663SArnd Bergmann spu_acquire(ctx); 5867207b96SArnd Bergmann 598b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 608b3d6663SArnd Bergmann ret = simple_read_from_buffer(buffer, size, pos, local_store, LS_SIZE); 6167207b96SArnd Bergmann 628b3d6663SArnd Bergmann spu_release(ctx); 6367207b96SArnd Bergmann return ret; 6467207b96SArnd Bergmann } 6567207b96SArnd Bergmann 6667207b96SArnd Bergmann static ssize_t 6767207b96SArnd Bergmann spufs_mem_write(struct file *file, const char __user *buffer, 6867207b96SArnd Bergmann size_t size, loff_t *pos) 6967207b96SArnd Bergmann { 7067207b96SArnd Bergmann struct spu_context *ctx = file->private_data; 718b3d6663SArnd Bergmann char *local_store; 728b3d6663SArnd Bergmann int ret; 7367207b96SArnd Bergmann 7467207b96SArnd Bergmann size = min_t(ssize_t, LS_SIZE - *pos, size); 7567207b96SArnd Bergmann if (size <= 0) 7667207b96SArnd Bergmann return -EFBIG; 7767207b96SArnd Bergmann *pos += size; 788b3d6663SArnd Bergmann 798b3d6663SArnd Bergmann spu_acquire(ctx); 808b3d6663SArnd Bergmann 818b3d6663SArnd Bergmann local_store = ctx->ops->get_ls(ctx); 828b3d6663SArnd Bergmann ret = copy_from_user(local_store + *pos - size, 8367207b96SArnd Bergmann buffer, size) ? -EFAULT : size; 848b3d6663SArnd Bergmann 858b3d6663SArnd Bergmann spu_release(ctx); 868b3d6663SArnd Bergmann return ret; 8767207b96SArnd Bergmann } 8867207b96SArnd Bergmann 898b3d6663SArnd Bergmann #ifdef CONFIG_SPARSEMEM 908b3d6663SArnd Bergmann static struct page * 918b3d6663SArnd Bergmann spufs_mem_mmap_nopage(struct vm_area_struct *vma, 928b3d6663SArnd Bergmann unsigned long address, int *type) 938b3d6663SArnd Bergmann { 948b3d6663SArnd Bergmann struct page *page = NOPAGE_SIGBUS; 958b3d6663SArnd Bergmann 968b3d6663SArnd Bergmann struct spu_context *ctx = vma->vm_file->private_data; 978b3d6663SArnd Bergmann unsigned long offset = address - vma->vm_start; 988b3d6663SArnd Bergmann offset += vma->vm_pgoff << PAGE_SHIFT; 998b3d6663SArnd Bergmann 1008b3d6663SArnd Bergmann spu_acquire(ctx); 1018b3d6663SArnd Bergmann 1028b3d6663SArnd Bergmann if (ctx->state == SPU_STATE_SAVED) 1038b3d6663SArnd Bergmann page = vmalloc_to_page(ctx->csa.lscsa->ls + offset); 1048b3d6663SArnd Bergmann else 1058b3d6663SArnd Bergmann page = pfn_to_page((ctx->spu->local_store_phys + offset) 1068b3d6663SArnd Bergmann >> PAGE_SHIFT); 1078b3d6663SArnd Bergmann 1088b3d6663SArnd Bergmann spu_release(ctx); 1098b3d6663SArnd Bergmann 1108b3d6663SArnd Bergmann if (type) 1118b3d6663SArnd Bergmann *type = VM_FAULT_MINOR; 1128b3d6663SArnd Bergmann 113d88cfffaSArnd Bergmann page_cache_get(page); 1148b3d6663SArnd Bergmann return page; 1158b3d6663SArnd Bergmann } 1168b3d6663SArnd Bergmann 1178b3d6663SArnd Bergmann static struct vm_operations_struct spufs_mem_mmap_vmops = { 1188b3d6663SArnd Bergmann .nopage = spufs_mem_mmap_nopage, 1198b3d6663SArnd Bergmann }; 1208b3d6663SArnd Bergmann 12167207b96SArnd Bergmann static int 12267207b96SArnd Bergmann spufs_mem_mmap(struct file *file, struct vm_area_struct *vma) 12367207b96SArnd Bergmann { 1248b3d6663SArnd Bergmann if (!(vma->vm_flags & VM_SHARED)) 1258b3d6663SArnd Bergmann return -EINVAL; 12667207b96SArnd Bergmann 1278b3d6663SArnd Bergmann /* FIXME: */ 12867207b96SArnd Bergmann vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) 12967207b96SArnd Bergmann | _PAGE_NO_CACHE); 1308b3d6663SArnd Bergmann 1318b3d6663SArnd Bergmann vma->vm_ops = &spufs_mem_mmap_vmops; 13267207b96SArnd Bergmann return 0; 13367207b96SArnd Bergmann } 1348b3d6663SArnd Bergmann #endif 13567207b96SArnd Bergmann 13667207b96SArnd Bergmann static struct file_operations spufs_mem_fops = { 13767207b96SArnd Bergmann .open = spufs_mem_open, 13867207b96SArnd Bergmann .read = spufs_mem_read, 13967207b96SArnd Bergmann .write = spufs_mem_write, 1408b3d6663SArnd Bergmann .llseek = generic_file_llseek, 1418b3d6663SArnd Bergmann #ifdef CONFIG_SPARSEMEM 14267207b96SArnd Bergmann .mmap = spufs_mem_mmap, 1438b3d6663SArnd Bergmann #endif 1448b3d6663SArnd Bergmann }; 1458b3d6663SArnd Bergmann 1468b3d6663SArnd Bergmann static int 1478b3d6663SArnd Bergmann spufs_regs_open(struct inode *inode, struct file *file) 1488b3d6663SArnd Bergmann { 1498b3d6663SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 1508b3d6663SArnd Bergmann file->private_data = i->i_ctx; 1518b3d6663SArnd Bergmann return 0; 1528b3d6663SArnd Bergmann } 1538b3d6663SArnd Bergmann 1548b3d6663SArnd Bergmann static ssize_t 1558b3d6663SArnd Bergmann spufs_regs_read(struct file *file, char __user *buffer, 1568b3d6663SArnd Bergmann size_t size, loff_t *pos) 1578b3d6663SArnd Bergmann { 1588b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 1598b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1608b3d6663SArnd Bergmann int ret; 1618b3d6663SArnd Bergmann 1628b3d6663SArnd Bergmann spu_acquire_saved(ctx); 1638b3d6663SArnd Bergmann 1648b3d6663SArnd Bergmann ret = simple_read_from_buffer(buffer, size, pos, 1658b3d6663SArnd Bergmann lscsa->gprs, sizeof lscsa->gprs); 1668b3d6663SArnd Bergmann 1678b3d6663SArnd Bergmann spu_release(ctx); 1688b3d6663SArnd Bergmann return ret; 1698b3d6663SArnd Bergmann } 1708b3d6663SArnd Bergmann 1718b3d6663SArnd Bergmann static ssize_t 1728b3d6663SArnd Bergmann spufs_regs_write(struct file *file, const char __user *buffer, 1738b3d6663SArnd Bergmann size_t size, loff_t *pos) 1748b3d6663SArnd Bergmann { 1758b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 1768b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 1778b3d6663SArnd Bergmann int ret; 1788b3d6663SArnd Bergmann 1798b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof lscsa->gprs - *pos, size); 1808b3d6663SArnd Bergmann if (size <= 0) 1818b3d6663SArnd Bergmann return -EFBIG; 1828b3d6663SArnd Bergmann *pos += size; 1838b3d6663SArnd Bergmann 1848b3d6663SArnd Bergmann spu_acquire_saved(ctx); 1858b3d6663SArnd Bergmann 1868b3d6663SArnd Bergmann ret = copy_from_user(lscsa->gprs + *pos - size, 1878b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 1888b3d6663SArnd Bergmann 1898b3d6663SArnd Bergmann spu_release(ctx); 1908b3d6663SArnd Bergmann return ret; 1918b3d6663SArnd Bergmann } 1928b3d6663SArnd Bergmann 1938b3d6663SArnd Bergmann static struct file_operations spufs_regs_fops = { 1948b3d6663SArnd Bergmann .open = spufs_regs_open, 1958b3d6663SArnd Bergmann .read = spufs_regs_read, 1968b3d6663SArnd Bergmann .write = spufs_regs_write, 1978b3d6663SArnd Bergmann .llseek = generic_file_llseek, 1988b3d6663SArnd Bergmann }; 1998b3d6663SArnd Bergmann 2008b3d6663SArnd Bergmann static ssize_t 2018b3d6663SArnd Bergmann spufs_fpcr_read(struct file *file, char __user * buffer, 2028b3d6663SArnd Bergmann size_t size, loff_t * pos) 2038b3d6663SArnd Bergmann { 2048b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 2058b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 2068b3d6663SArnd Bergmann int ret; 2078b3d6663SArnd Bergmann 2088b3d6663SArnd Bergmann spu_acquire_saved(ctx); 2098b3d6663SArnd Bergmann 2108b3d6663SArnd Bergmann ret = simple_read_from_buffer(buffer, size, pos, 2118b3d6663SArnd Bergmann &lscsa->fpcr, sizeof(lscsa->fpcr)); 2128b3d6663SArnd Bergmann 2138b3d6663SArnd Bergmann spu_release(ctx); 2148b3d6663SArnd Bergmann return ret; 2158b3d6663SArnd Bergmann } 2168b3d6663SArnd Bergmann 2178b3d6663SArnd Bergmann static ssize_t 2188b3d6663SArnd Bergmann spufs_fpcr_write(struct file *file, const char __user * buffer, 2198b3d6663SArnd Bergmann size_t size, loff_t * pos) 2208b3d6663SArnd Bergmann { 2218b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 2228b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 2238b3d6663SArnd Bergmann int ret; 2248b3d6663SArnd Bergmann 2258b3d6663SArnd Bergmann size = min_t(ssize_t, sizeof(lscsa->fpcr) - *pos, size); 2268b3d6663SArnd Bergmann if (size <= 0) 2278b3d6663SArnd Bergmann return -EFBIG; 2288b3d6663SArnd Bergmann *pos += size; 2298b3d6663SArnd Bergmann 2308b3d6663SArnd Bergmann spu_acquire_saved(ctx); 2318b3d6663SArnd Bergmann 2328b3d6663SArnd Bergmann ret = copy_from_user((char *)&lscsa->fpcr + *pos - size, 2338b3d6663SArnd Bergmann buffer, size) ? -EFAULT : size; 2348b3d6663SArnd Bergmann 2358b3d6663SArnd Bergmann spu_release(ctx); 2368b3d6663SArnd Bergmann return ret; 2378b3d6663SArnd Bergmann } 2388b3d6663SArnd Bergmann 2398b3d6663SArnd Bergmann static struct file_operations spufs_fpcr_fops = { 2408b3d6663SArnd Bergmann .open = spufs_regs_open, 2418b3d6663SArnd Bergmann .read = spufs_fpcr_read, 2428b3d6663SArnd Bergmann .write = spufs_fpcr_write, 24367207b96SArnd Bergmann .llseek = generic_file_llseek, 24467207b96SArnd Bergmann }; 24567207b96SArnd Bergmann 24667207b96SArnd Bergmann /* generic open function for all pipe-like files */ 24767207b96SArnd Bergmann static int spufs_pipe_open(struct inode *inode, struct file *file) 24867207b96SArnd Bergmann { 24967207b96SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 25067207b96SArnd Bergmann file->private_data = i->i_ctx; 25167207b96SArnd Bergmann 25267207b96SArnd Bergmann return nonseekable_open(inode, file); 25367207b96SArnd Bergmann } 25467207b96SArnd Bergmann 25567207b96SArnd Bergmann static ssize_t spufs_mbox_read(struct file *file, char __user *buf, 25667207b96SArnd Bergmann size_t len, loff_t *pos) 25767207b96SArnd Bergmann { 2588b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 25967207b96SArnd Bergmann u32 mbox_data; 2608b3d6663SArnd Bergmann int ret; 26167207b96SArnd Bergmann 26267207b96SArnd Bergmann if (len < 4) 26367207b96SArnd Bergmann return -EINVAL; 26467207b96SArnd Bergmann 2658b3d6663SArnd Bergmann spu_acquire(ctx); 2668b3d6663SArnd Bergmann ret = ctx->ops->mbox_read(ctx, &mbox_data); 2678b3d6663SArnd Bergmann spu_release(ctx); 26867207b96SArnd Bergmann 2698b3d6663SArnd Bergmann if (!ret) 2708b3d6663SArnd Bergmann return -EAGAIN; 27167207b96SArnd Bergmann 27267207b96SArnd Bergmann if (copy_to_user(buf, &mbox_data, sizeof mbox_data)) 27367207b96SArnd Bergmann return -EFAULT; 27467207b96SArnd Bergmann 27567207b96SArnd Bergmann return 4; 27667207b96SArnd Bergmann } 27767207b96SArnd Bergmann 27867207b96SArnd Bergmann static struct file_operations spufs_mbox_fops = { 27967207b96SArnd Bergmann .open = spufs_pipe_open, 28067207b96SArnd Bergmann .read = spufs_mbox_read, 28167207b96SArnd Bergmann }; 28267207b96SArnd Bergmann 28367207b96SArnd Bergmann static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf, 28467207b96SArnd Bergmann size_t len, loff_t *pos) 28567207b96SArnd Bergmann { 2868b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 28767207b96SArnd Bergmann u32 mbox_stat; 28867207b96SArnd Bergmann 28967207b96SArnd Bergmann if (len < 4) 29067207b96SArnd Bergmann return -EINVAL; 29167207b96SArnd Bergmann 2928b3d6663SArnd Bergmann spu_acquire(ctx); 2938b3d6663SArnd Bergmann 2948b3d6663SArnd Bergmann mbox_stat = ctx->ops->mbox_stat_read(ctx) & 0xff; 2958b3d6663SArnd Bergmann 2968b3d6663SArnd Bergmann spu_release(ctx); 29767207b96SArnd Bergmann 29867207b96SArnd Bergmann if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat)) 29967207b96SArnd Bergmann return -EFAULT; 30067207b96SArnd Bergmann 30167207b96SArnd Bergmann return 4; 30267207b96SArnd Bergmann } 30367207b96SArnd Bergmann 30467207b96SArnd Bergmann static struct file_operations spufs_mbox_stat_fops = { 30567207b96SArnd Bergmann .open = spufs_pipe_open, 30667207b96SArnd Bergmann .read = spufs_mbox_stat_read, 30767207b96SArnd Bergmann }; 30867207b96SArnd Bergmann 30967207b96SArnd Bergmann /* low-level ibox access function */ 3108b3d6663SArnd Bergmann size_t spu_ibox_read(struct spu_context *ctx, u32 *data) 31167207b96SArnd Bergmann { 3128b3d6663SArnd Bergmann return ctx->ops->ibox_read(ctx, data); 31367207b96SArnd Bergmann } 31467207b96SArnd Bergmann 31567207b96SArnd Bergmann static int spufs_ibox_fasync(int fd, struct file *file, int on) 31667207b96SArnd Bergmann { 3178b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 3188b3d6663SArnd Bergmann 3198b3d6663SArnd Bergmann return fasync_helper(fd, file, on, &ctx->ibox_fasync); 3208b3d6663SArnd Bergmann } 3218b3d6663SArnd Bergmann 3228b3d6663SArnd Bergmann /* interrupt-level ibox callback function. */ 3238b3d6663SArnd Bergmann void spufs_ibox_callback(struct spu *spu) 3248b3d6663SArnd Bergmann { 3258b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 3268b3d6663SArnd Bergmann 3278b3d6663SArnd Bergmann wake_up_all(&ctx->ibox_wq); 3288b3d6663SArnd Bergmann kill_fasync(&ctx->ibox_fasync, SIGIO, POLLIN); 32967207b96SArnd Bergmann } 33067207b96SArnd Bergmann 33167207b96SArnd Bergmann static ssize_t spufs_ibox_read(struct file *file, char __user *buf, 33267207b96SArnd Bergmann size_t len, loff_t *pos) 33367207b96SArnd Bergmann { 3348b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 33567207b96SArnd Bergmann u32 ibox_data; 33667207b96SArnd Bergmann ssize_t ret; 33767207b96SArnd Bergmann 33867207b96SArnd Bergmann if (len < 4) 33967207b96SArnd Bergmann return -EINVAL; 34067207b96SArnd Bergmann 3418b3d6663SArnd Bergmann spu_acquire(ctx); 34267207b96SArnd Bergmann 34367207b96SArnd Bergmann ret = 0; 34467207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 3458b3d6663SArnd Bergmann if (!spu_ibox_read(ctx, &ibox_data)) 34667207b96SArnd Bergmann ret = -EAGAIN; 34767207b96SArnd Bergmann } else { 3488b3d6663SArnd Bergmann ret = spufs_wait(ctx->ibox_wq, spu_ibox_read(ctx, &ibox_data)); 34967207b96SArnd Bergmann } 35067207b96SArnd Bergmann 3518b3d6663SArnd Bergmann spu_release(ctx); 3528b3d6663SArnd Bergmann 35367207b96SArnd Bergmann if (ret) 35467207b96SArnd Bergmann return ret; 35567207b96SArnd Bergmann 35667207b96SArnd Bergmann ret = 4; 35767207b96SArnd Bergmann if (copy_to_user(buf, &ibox_data, sizeof ibox_data)) 35867207b96SArnd Bergmann ret = -EFAULT; 35967207b96SArnd Bergmann 36067207b96SArnd Bergmann return ret; 36167207b96SArnd Bergmann } 36267207b96SArnd Bergmann 36367207b96SArnd Bergmann static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait) 36467207b96SArnd Bergmann { 3658b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 36667207b96SArnd Bergmann unsigned int mask; 36767207b96SArnd Bergmann 3688b3d6663SArnd Bergmann poll_wait(file, &ctx->ibox_wq, wait); 36967207b96SArnd Bergmann 3703a843d7cSArnd Bergmann spu_acquire(ctx); 3713a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLIN | POLLRDNORM); 3723a843d7cSArnd Bergmann spu_release(ctx); 37367207b96SArnd Bergmann 37467207b96SArnd Bergmann return mask; 37567207b96SArnd Bergmann } 37667207b96SArnd Bergmann 37767207b96SArnd Bergmann static struct file_operations spufs_ibox_fops = { 37867207b96SArnd Bergmann .open = spufs_pipe_open, 37967207b96SArnd Bergmann .read = spufs_ibox_read, 38067207b96SArnd Bergmann .poll = spufs_ibox_poll, 38167207b96SArnd Bergmann .fasync = spufs_ibox_fasync, 38267207b96SArnd Bergmann }; 38367207b96SArnd Bergmann 38467207b96SArnd Bergmann static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf, 38567207b96SArnd Bergmann size_t len, loff_t *pos) 38667207b96SArnd Bergmann { 3878b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 38867207b96SArnd Bergmann u32 ibox_stat; 38967207b96SArnd Bergmann 39067207b96SArnd Bergmann if (len < 4) 39167207b96SArnd Bergmann return -EINVAL; 39267207b96SArnd Bergmann 3938b3d6663SArnd Bergmann spu_acquire(ctx); 3948b3d6663SArnd Bergmann ibox_stat = (ctx->ops->mbox_stat_read(ctx) >> 16) & 0xff; 3958b3d6663SArnd Bergmann spu_release(ctx); 39667207b96SArnd Bergmann 39767207b96SArnd Bergmann if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat)) 39867207b96SArnd Bergmann return -EFAULT; 39967207b96SArnd Bergmann 40067207b96SArnd Bergmann return 4; 40167207b96SArnd Bergmann } 40267207b96SArnd Bergmann 40367207b96SArnd Bergmann static struct file_operations spufs_ibox_stat_fops = { 40467207b96SArnd Bergmann .open = spufs_pipe_open, 40567207b96SArnd Bergmann .read = spufs_ibox_stat_read, 40667207b96SArnd Bergmann }; 40767207b96SArnd Bergmann 40867207b96SArnd Bergmann /* low-level mailbox write */ 4098b3d6663SArnd Bergmann size_t spu_wbox_write(struct spu_context *ctx, u32 data) 41067207b96SArnd Bergmann { 4118b3d6663SArnd Bergmann return ctx->ops->wbox_write(ctx, data); 41267207b96SArnd Bergmann } 41367207b96SArnd Bergmann 41467207b96SArnd Bergmann static int spufs_wbox_fasync(int fd, struct file *file, int on) 41567207b96SArnd Bergmann { 4168b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 4178b3d6663SArnd Bergmann int ret; 4188b3d6663SArnd Bergmann 4198b3d6663SArnd Bergmann ret = fasync_helper(fd, file, on, &ctx->wbox_fasync); 4208b3d6663SArnd Bergmann 4218b3d6663SArnd Bergmann return ret; 4228b3d6663SArnd Bergmann } 4238b3d6663SArnd Bergmann 4248b3d6663SArnd Bergmann /* interrupt-level wbox callback function. */ 4258b3d6663SArnd Bergmann void spufs_wbox_callback(struct spu *spu) 4268b3d6663SArnd Bergmann { 4278b3d6663SArnd Bergmann struct spu_context *ctx = spu->ctx; 4288b3d6663SArnd Bergmann 4298b3d6663SArnd Bergmann wake_up_all(&ctx->wbox_wq); 4308b3d6663SArnd Bergmann kill_fasync(&ctx->wbox_fasync, SIGIO, POLLOUT); 43167207b96SArnd Bergmann } 43267207b96SArnd Bergmann 43367207b96SArnd Bergmann static ssize_t spufs_wbox_write(struct file *file, const char __user *buf, 43467207b96SArnd Bergmann size_t len, loff_t *pos) 43567207b96SArnd Bergmann { 4368b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 43767207b96SArnd Bergmann u32 wbox_data; 43867207b96SArnd Bergmann int ret; 43967207b96SArnd Bergmann 44067207b96SArnd Bergmann if (len < 4) 44167207b96SArnd Bergmann return -EINVAL; 44267207b96SArnd Bergmann 44367207b96SArnd Bergmann if (copy_from_user(&wbox_data, buf, sizeof wbox_data)) 44467207b96SArnd Bergmann return -EFAULT; 44567207b96SArnd Bergmann 4468b3d6663SArnd Bergmann spu_acquire(ctx); 4478b3d6663SArnd Bergmann 44867207b96SArnd Bergmann ret = 0; 44967207b96SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 4508b3d6663SArnd Bergmann if (!spu_wbox_write(ctx, wbox_data)) 45167207b96SArnd Bergmann ret = -EAGAIN; 45267207b96SArnd Bergmann } else { 4538b3d6663SArnd Bergmann ret = spufs_wait(ctx->wbox_wq, spu_wbox_write(ctx, wbox_data)); 45467207b96SArnd Bergmann } 45567207b96SArnd Bergmann 4568b3d6663SArnd Bergmann spu_release(ctx); 4578b3d6663SArnd Bergmann 45867207b96SArnd Bergmann return ret ? ret : sizeof wbox_data; 45967207b96SArnd Bergmann } 46067207b96SArnd Bergmann 46167207b96SArnd Bergmann static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait) 46267207b96SArnd Bergmann { 4638b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 46467207b96SArnd Bergmann unsigned int mask; 46567207b96SArnd Bergmann 4668b3d6663SArnd Bergmann poll_wait(file, &ctx->wbox_wq, wait); 46767207b96SArnd Bergmann 4683a843d7cSArnd Bergmann spu_acquire(ctx); 4693a843d7cSArnd Bergmann mask = ctx->ops->mbox_stat_poll(ctx, POLLOUT | POLLWRNORM); 4703a843d7cSArnd Bergmann spu_release(ctx); 47167207b96SArnd Bergmann 47267207b96SArnd Bergmann return mask; 47367207b96SArnd Bergmann } 47467207b96SArnd Bergmann 47567207b96SArnd Bergmann static struct file_operations spufs_wbox_fops = { 47667207b96SArnd Bergmann .open = spufs_pipe_open, 47767207b96SArnd Bergmann .write = spufs_wbox_write, 47867207b96SArnd Bergmann .poll = spufs_wbox_poll, 47967207b96SArnd Bergmann .fasync = spufs_wbox_fasync, 48067207b96SArnd Bergmann }; 48167207b96SArnd Bergmann 48267207b96SArnd Bergmann static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf, 48367207b96SArnd Bergmann size_t len, loff_t *pos) 48467207b96SArnd Bergmann { 4858b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 48667207b96SArnd Bergmann u32 wbox_stat; 48767207b96SArnd Bergmann 48867207b96SArnd Bergmann if (len < 4) 48967207b96SArnd Bergmann return -EINVAL; 49067207b96SArnd Bergmann 4918b3d6663SArnd Bergmann spu_acquire(ctx); 4928b3d6663SArnd Bergmann wbox_stat = (ctx->ops->mbox_stat_read(ctx) >> 8) & 0xff; 4938b3d6663SArnd Bergmann spu_release(ctx); 49467207b96SArnd Bergmann 49567207b96SArnd Bergmann if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat)) 49667207b96SArnd Bergmann return -EFAULT; 49767207b96SArnd Bergmann 49867207b96SArnd Bergmann return 4; 49967207b96SArnd Bergmann } 50067207b96SArnd Bergmann 50167207b96SArnd Bergmann static struct file_operations spufs_wbox_stat_fops = { 50267207b96SArnd Bergmann .open = spufs_pipe_open, 50367207b96SArnd Bergmann .read = spufs_wbox_stat_read, 50467207b96SArnd Bergmann }; 50567207b96SArnd Bergmann 50667207b96SArnd Bergmann static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 50767207b96SArnd Bergmann size_t len, loff_t *pos) 50867207b96SArnd Bergmann { 5098b3d6663SArnd Bergmann struct spu_context *ctx = file->private_data; 51067207b96SArnd Bergmann u32 data; 51167207b96SArnd Bergmann 51267207b96SArnd Bergmann if (len < 4) 51367207b96SArnd Bergmann return -EINVAL; 51467207b96SArnd Bergmann 5158b3d6663SArnd Bergmann spu_acquire(ctx); 5168b3d6663SArnd Bergmann data = ctx->ops->signal1_read(ctx); 5178b3d6663SArnd Bergmann spu_release(ctx); 5188b3d6663SArnd Bergmann 51967207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 52067207b96SArnd Bergmann return -EFAULT; 52167207b96SArnd Bergmann 52267207b96SArnd Bergmann return 4; 52367207b96SArnd Bergmann } 52467207b96SArnd Bergmann 52567207b96SArnd Bergmann static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, 52667207b96SArnd Bergmann size_t len, loff_t *pos) 52767207b96SArnd Bergmann { 52867207b96SArnd Bergmann struct spu_context *ctx; 52967207b96SArnd Bergmann u32 data; 53067207b96SArnd Bergmann 53167207b96SArnd Bergmann ctx = file->private_data; 53267207b96SArnd Bergmann 53367207b96SArnd Bergmann if (len < 4) 53467207b96SArnd Bergmann return -EINVAL; 53567207b96SArnd Bergmann 53667207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 53767207b96SArnd Bergmann return -EFAULT; 53867207b96SArnd Bergmann 5398b3d6663SArnd Bergmann spu_acquire(ctx); 5408b3d6663SArnd Bergmann ctx->ops->signal1_write(ctx, data); 5418b3d6663SArnd Bergmann spu_release(ctx); 54267207b96SArnd Bergmann 54367207b96SArnd Bergmann return 4; 54467207b96SArnd Bergmann } 54567207b96SArnd Bergmann 54667207b96SArnd Bergmann static struct file_operations spufs_signal1_fops = { 54767207b96SArnd Bergmann .open = spufs_pipe_open, 54867207b96SArnd Bergmann .read = spufs_signal1_read, 54967207b96SArnd Bergmann .write = spufs_signal1_write, 55067207b96SArnd Bergmann }; 55167207b96SArnd Bergmann 55267207b96SArnd Bergmann static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 55367207b96SArnd Bergmann size_t len, loff_t *pos) 55467207b96SArnd Bergmann { 55567207b96SArnd Bergmann struct spu_context *ctx; 55667207b96SArnd Bergmann u32 data; 55767207b96SArnd Bergmann 55867207b96SArnd Bergmann ctx = file->private_data; 55967207b96SArnd Bergmann 56067207b96SArnd Bergmann if (len < 4) 56167207b96SArnd Bergmann return -EINVAL; 56267207b96SArnd Bergmann 5638b3d6663SArnd Bergmann spu_acquire(ctx); 5648b3d6663SArnd Bergmann data = ctx->ops->signal2_read(ctx); 5658b3d6663SArnd Bergmann spu_release(ctx); 5668b3d6663SArnd Bergmann 56767207b96SArnd Bergmann if (copy_to_user(buf, &data, 4)) 56867207b96SArnd Bergmann return -EFAULT; 56967207b96SArnd Bergmann 57067207b96SArnd Bergmann return 4; 57167207b96SArnd Bergmann } 57267207b96SArnd Bergmann 57367207b96SArnd Bergmann static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, 57467207b96SArnd Bergmann size_t len, loff_t *pos) 57567207b96SArnd Bergmann { 57667207b96SArnd Bergmann struct spu_context *ctx; 57767207b96SArnd Bergmann u32 data; 57867207b96SArnd Bergmann 57967207b96SArnd Bergmann ctx = file->private_data; 58067207b96SArnd Bergmann 58167207b96SArnd Bergmann if (len < 4) 58267207b96SArnd Bergmann return -EINVAL; 58367207b96SArnd Bergmann 58467207b96SArnd Bergmann if (copy_from_user(&data, buf, 4)) 58567207b96SArnd Bergmann return -EFAULT; 58667207b96SArnd Bergmann 5878b3d6663SArnd Bergmann spu_acquire(ctx); 5888b3d6663SArnd Bergmann ctx->ops->signal2_write(ctx, data); 5898b3d6663SArnd Bergmann spu_release(ctx); 59067207b96SArnd Bergmann 59167207b96SArnd Bergmann return 4; 59267207b96SArnd Bergmann } 59367207b96SArnd Bergmann 59467207b96SArnd Bergmann static struct file_operations spufs_signal2_fops = { 59567207b96SArnd Bergmann .open = spufs_pipe_open, 59667207b96SArnd Bergmann .read = spufs_signal2_read, 59767207b96SArnd Bergmann .write = spufs_signal2_write, 59867207b96SArnd Bergmann }; 59967207b96SArnd Bergmann 60067207b96SArnd Bergmann static void spufs_signal1_type_set(void *data, u64 val) 60167207b96SArnd Bergmann { 60267207b96SArnd Bergmann struct spu_context *ctx = data; 60367207b96SArnd Bergmann 6048b3d6663SArnd Bergmann spu_acquire(ctx); 6058b3d6663SArnd Bergmann ctx->ops->signal1_type_set(ctx, val); 6068b3d6663SArnd Bergmann spu_release(ctx); 60767207b96SArnd Bergmann } 60867207b96SArnd Bergmann 60967207b96SArnd Bergmann static u64 spufs_signal1_type_get(void *data) 61067207b96SArnd Bergmann { 61167207b96SArnd Bergmann struct spu_context *ctx = data; 6128b3d6663SArnd Bergmann u64 ret; 6138b3d6663SArnd Bergmann 6148b3d6663SArnd Bergmann spu_acquire(ctx); 6158b3d6663SArnd Bergmann ret = ctx->ops->signal1_type_get(ctx); 6168b3d6663SArnd Bergmann spu_release(ctx); 6178b3d6663SArnd Bergmann 6188b3d6663SArnd Bergmann return ret; 61967207b96SArnd Bergmann } 62067207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get, 62167207b96SArnd Bergmann spufs_signal1_type_set, "%llu"); 62267207b96SArnd Bergmann 62367207b96SArnd Bergmann static void spufs_signal2_type_set(void *data, u64 val) 62467207b96SArnd Bergmann { 62567207b96SArnd Bergmann struct spu_context *ctx = data; 62667207b96SArnd Bergmann 6278b3d6663SArnd Bergmann spu_acquire(ctx); 6288b3d6663SArnd Bergmann ctx->ops->signal2_type_set(ctx, val); 6298b3d6663SArnd Bergmann spu_release(ctx); 63067207b96SArnd Bergmann } 63167207b96SArnd Bergmann 63267207b96SArnd Bergmann static u64 spufs_signal2_type_get(void *data) 63367207b96SArnd Bergmann { 63467207b96SArnd Bergmann struct spu_context *ctx = data; 6358b3d6663SArnd Bergmann u64 ret; 6368b3d6663SArnd Bergmann 6378b3d6663SArnd Bergmann spu_acquire(ctx); 6388b3d6663SArnd Bergmann ret = ctx->ops->signal2_type_get(ctx); 6398b3d6663SArnd Bergmann spu_release(ctx); 6408b3d6663SArnd Bergmann 6418b3d6663SArnd Bergmann return ret; 64267207b96SArnd Bergmann } 64367207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 64467207b96SArnd Bergmann spufs_signal2_type_set, "%llu"); 64567207b96SArnd Bergmann 646a33a7d73SArnd Bergmann 647a33a7d73SArnd Bergmann static int spufs_mfc_open(struct inode *inode, struct file *file) 648a33a7d73SArnd Bergmann { 649a33a7d73SArnd Bergmann struct spufs_inode_info *i = SPUFS_I(inode); 650a33a7d73SArnd Bergmann struct spu_context *ctx = i->i_ctx; 651a33a7d73SArnd Bergmann 652a33a7d73SArnd Bergmann /* we don't want to deal with DMA into other processes */ 653a33a7d73SArnd Bergmann if (ctx->owner != current->mm) 654a33a7d73SArnd Bergmann return -EINVAL; 655a33a7d73SArnd Bergmann 656a33a7d73SArnd Bergmann if (atomic_read(&inode->i_count) != 1) 657a33a7d73SArnd Bergmann return -EBUSY; 658a33a7d73SArnd Bergmann 659a33a7d73SArnd Bergmann file->private_data = ctx; 660a33a7d73SArnd Bergmann return nonseekable_open(inode, file); 661a33a7d73SArnd Bergmann } 662a33a7d73SArnd Bergmann 663a33a7d73SArnd Bergmann /* interrupt-level mfc callback function. */ 664a33a7d73SArnd Bergmann void spufs_mfc_callback(struct spu *spu) 665a33a7d73SArnd Bergmann { 666a33a7d73SArnd Bergmann struct spu_context *ctx = spu->ctx; 667a33a7d73SArnd Bergmann 668a33a7d73SArnd Bergmann wake_up_all(&ctx->mfc_wq); 669a33a7d73SArnd Bergmann 670a33a7d73SArnd Bergmann pr_debug("%s %s\n", __FUNCTION__, spu->name); 671a33a7d73SArnd Bergmann if (ctx->mfc_fasync) { 672a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 673a33a7d73SArnd Bergmann unsigned int mask; 674a33a7d73SArnd Bergmann 675a33a7d73SArnd Bergmann /* no need for spu_acquire in interrupt context */ 676a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 677a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 678a33a7d73SArnd Bergmann 679a33a7d73SArnd Bergmann mask = 0; 680a33a7d73SArnd Bergmann if (free_elements & 0xffff) 681a33a7d73SArnd Bergmann mask |= POLLOUT; 682a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 683a33a7d73SArnd Bergmann mask |= POLLIN; 684a33a7d73SArnd Bergmann 685a33a7d73SArnd Bergmann kill_fasync(&ctx->mfc_fasync, SIGIO, mask); 686a33a7d73SArnd Bergmann } 687a33a7d73SArnd Bergmann } 688a33a7d73SArnd Bergmann 689a33a7d73SArnd Bergmann static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) 690a33a7d73SArnd Bergmann { 691a33a7d73SArnd Bergmann /* See if there is one tag group is complete */ 692a33a7d73SArnd Bergmann /* FIXME we need locking around tagwait */ 693a33a7d73SArnd Bergmann *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; 694a33a7d73SArnd Bergmann ctx->tagwait &= ~*status; 695a33a7d73SArnd Bergmann if (*status) 696a33a7d73SArnd Bergmann return 1; 697a33a7d73SArnd Bergmann 698a33a7d73SArnd Bergmann /* enable interrupt waiting for any tag group, 699a33a7d73SArnd Bergmann may silently fail if interrupts are already enabled */ 700a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 701a33a7d73SArnd Bergmann return 0; 702a33a7d73SArnd Bergmann } 703a33a7d73SArnd Bergmann 704a33a7d73SArnd Bergmann static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, 705a33a7d73SArnd Bergmann size_t size, loff_t *pos) 706a33a7d73SArnd Bergmann { 707a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 708a33a7d73SArnd Bergmann int ret = -EINVAL; 709a33a7d73SArnd Bergmann u32 status; 710a33a7d73SArnd Bergmann 711a33a7d73SArnd Bergmann if (size != 4) 712a33a7d73SArnd Bergmann goto out; 713a33a7d73SArnd Bergmann 714a33a7d73SArnd Bergmann spu_acquire(ctx); 715a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 716a33a7d73SArnd Bergmann status = ctx->ops->read_mfc_tagstatus(ctx); 717a33a7d73SArnd Bergmann if (!(status & ctx->tagwait)) 718a33a7d73SArnd Bergmann ret = -EAGAIN; 719a33a7d73SArnd Bergmann else 720a33a7d73SArnd Bergmann ctx->tagwait &= ~status; 721a33a7d73SArnd Bergmann } else { 722a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 723a33a7d73SArnd Bergmann spufs_read_mfc_tagstatus(ctx, &status)); 724a33a7d73SArnd Bergmann } 725a33a7d73SArnd Bergmann spu_release(ctx); 726a33a7d73SArnd Bergmann 727a33a7d73SArnd Bergmann if (ret) 728a33a7d73SArnd Bergmann goto out; 729a33a7d73SArnd Bergmann 730a33a7d73SArnd Bergmann ret = 4; 731a33a7d73SArnd Bergmann if (copy_to_user(buffer, &status, 4)) 732a33a7d73SArnd Bergmann ret = -EFAULT; 733a33a7d73SArnd Bergmann 734a33a7d73SArnd Bergmann out: 735a33a7d73SArnd Bergmann return ret; 736a33a7d73SArnd Bergmann } 737a33a7d73SArnd Bergmann 738a33a7d73SArnd Bergmann static int spufs_check_valid_dma(struct mfc_dma_command *cmd) 739a33a7d73SArnd Bergmann { 740a33a7d73SArnd Bergmann pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, 741a33a7d73SArnd Bergmann cmd->ea, cmd->size, cmd->tag, cmd->cmd); 742a33a7d73SArnd Bergmann 743a33a7d73SArnd Bergmann switch (cmd->cmd) { 744a33a7d73SArnd Bergmann case MFC_PUT_CMD: 745a33a7d73SArnd Bergmann case MFC_PUTF_CMD: 746a33a7d73SArnd Bergmann case MFC_PUTB_CMD: 747a33a7d73SArnd Bergmann case MFC_GET_CMD: 748a33a7d73SArnd Bergmann case MFC_GETF_CMD: 749a33a7d73SArnd Bergmann case MFC_GETB_CMD: 750a33a7d73SArnd Bergmann break; 751a33a7d73SArnd Bergmann default: 752a33a7d73SArnd Bergmann pr_debug("invalid DMA opcode %x\n", cmd->cmd); 753a33a7d73SArnd Bergmann return -EIO; 754a33a7d73SArnd Bergmann } 755a33a7d73SArnd Bergmann 756a33a7d73SArnd Bergmann if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { 757a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment, ea %lx lsa %x\n", 758a33a7d73SArnd Bergmann cmd->ea, cmd->lsa); 759a33a7d73SArnd Bergmann return -EIO; 760a33a7d73SArnd Bergmann } 761a33a7d73SArnd Bergmann 762a33a7d73SArnd Bergmann switch (cmd->size & 0xf) { 763a33a7d73SArnd Bergmann case 1: 764a33a7d73SArnd Bergmann break; 765a33a7d73SArnd Bergmann case 2: 766a33a7d73SArnd Bergmann if (cmd->lsa & 1) 767a33a7d73SArnd Bergmann goto error; 768a33a7d73SArnd Bergmann break; 769a33a7d73SArnd Bergmann case 4: 770a33a7d73SArnd Bergmann if (cmd->lsa & 3) 771a33a7d73SArnd Bergmann goto error; 772a33a7d73SArnd Bergmann break; 773a33a7d73SArnd Bergmann case 8: 774a33a7d73SArnd Bergmann if (cmd->lsa & 7) 775a33a7d73SArnd Bergmann goto error; 776a33a7d73SArnd Bergmann break; 777a33a7d73SArnd Bergmann case 0: 778a33a7d73SArnd Bergmann if (cmd->lsa & 15) 779a33a7d73SArnd Bergmann goto error; 780a33a7d73SArnd Bergmann break; 781a33a7d73SArnd Bergmann error: 782a33a7d73SArnd Bergmann default: 783a33a7d73SArnd Bergmann pr_debug("invalid DMA alignment %x for size %x\n", 784a33a7d73SArnd Bergmann cmd->lsa & 0xf, cmd->size); 785a33a7d73SArnd Bergmann return -EIO; 786a33a7d73SArnd Bergmann } 787a33a7d73SArnd Bergmann 788a33a7d73SArnd Bergmann if (cmd->size > 16 * 1024) { 789a33a7d73SArnd Bergmann pr_debug("invalid DMA size %x\n", cmd->size); 790a33a7d73SArnd Bergmann return -EIO; 791a33a7d73SArnd Bergmann } 792a33a7d73SArnd Bergmann 793a33a7d73SArnd Bergmann if (cmd->tag & 0xfff0) { 794a33a7d73SArnd Bergmann /* we reserve the higher tag numbers for kernel use */ 795a33a7d73SArnd Bergmann pr_debug("invalid DMA tag\n"); 796a33a7d73SArnd Bergmann return -EIO; 797a33a7d73SArnd Bergmann } 798a33a7d73SArnd Bergmann 799a33a7d73SArnd Bergmann if (cmd->class) { 800a33a7d73SArnd Bergmann /* not supported in this version */ 801a33a7d73SArnd Bergmann pr_debug("invalid DMA class\n"); 802a33a7d73SArnd Bergmann return -EIO; 803a33a7d73SArnd Bergmann } 804a33a7d73SArnd Bergmann 805a33a7d73SArnd Bergmann return 0; 806a33a7d73SArnd Bergmann } 807a33a7d73SArnd Bergmann 808a33a7d73SArnd Bergmann static int spu_send_mfc_command(struct spu_context *ctx, 809a33a7d73SArnd Bergmann struct mfc_dma_command cmd, 810a33a7d73SArnd Bergmann int *error) 811a33a7d73SArnd Bergmann { 812a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 813a33a7d73SArnd Bergmann if (*error == -EAGAIN) { 814a33a7d73SArnd Bergmann /* wait for any tag group to complete 815a33a7d73SArnd Bergmann so we have space for the new command */ 816a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); 817a33a7d73SArnd Bergmann /* try again, because the queue might be 818a33a7d73SArnd Bergmann empty again */ 819a33a7d73SArnd Bergmann *error = ctx->ops->send_mfc_command(ctx, &cmd); 820a33a7d73SArnd Bergmann if (*error == -EAGAIN) 821a33a7d73SArnd Bergmann return 0; 822a33a7d73SArnd Bergmann } 823a33a7d73SArnd Bergmann return 1; 824a33a7d73SArnd Bergmann } 825a33a7d73SArnd Bergmann 826a33a7d73SArnd Bergmann static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, 827a33a7d73SArnd Bergmann size_t size, loff_t *pos) 828a33a7d73SArnd Bergmann { 829a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 830a33a7d73SArnd Bergmann struct mfc_dma_command cmd; 831a33a7d73SArnd Bergmann int ret = -EINVAL; 832a33a7d73SArnd Bergmann 833a33a7d73SArnd Bergmann if (size != sizeof cmd) 834a33a7d73SArnd Bergmann goto out; 835a33a7d73SArnd Bergmann 836a33a7d73SArnd Bergmann ret = -EFAULT; 837a33a7d73SArnd Bergmann if (copy_from_user(&cmd, buffer, sizeof cmd)) 838a33a7d73SArnd Bergmann goto out; 839a33a7d73SArnd Bergmann 840a33a7d73SArnd Bergmann ret = spufs_check_valid_dma(&cmd); 841a33a7d73SArnd Bergmann if (ret) 842a33a7d73SArnd Bergmann goto out; 843a33a7d73SArnd Bergmann 844a33a7d73SArnd Bergmann spu_acquire_runnable(ctx); 845a33a7d73SArnd Bergmann if (file->f_flags & O_NONBLOCK) { 846a33a7d73SArnd Bergmann ret = ctx->ops->send_mfc_command(ctx, &cmd); 847a33a7d73SArnd Bergmann } else { 848a33a7d73SArnd Bergmann int status; 849a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 850a33a7d73SArnd Bergmann spu_send_mfc_command(ctx, cmd, &status)); 851a33a7d73SArnd Bergmann if (status) 852a33a7d73SArnd Bergmann ret = status; 853a33a7d73SArnd Bergmann } 854a33a7d73SArnd Bergmann spu_release(ctx); 855a33a7d73SArnd Bergmann 856a33a7d73SArnd Bergmann if (ret) 857a33a7d73SArnd Bergmann goto out; 858a33a7d73SArnd Bergmann 859a33a7d73SArnd Bergmann ctx->tagwait |= 1 << cmd.tag; 860a33a7d73SArnd Bergmann 861a33a7d73SArnd Bergmann out: 862a33a7d73SArnd Bergmann return ret; 863a33a7d73SArnd Bergmann } 864a33a7d73SArnd Bergmann 865a33a7d73SArnd Bergmann static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) 866a33a7d73SArnd Bergmann { 867a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 868a33a7d73SArnd Bergmann u32 free_elements, tagstatus; 869a33a7d73SArnd Bergmann unsigned int mask; 870a33a7d73SArnd Bergmann 871a33a7d73SArnd Bergmann spu_acquire(ctx); 872a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); 873a33a7d73SArnd Bergmann free_elements = ctx->ops->get_mfc_free_elements(ctx); 874a33a7d73SArnd Bergmann tagstatus = ctx->ops->read_mfc_tagstatus(ctx); 875a33a7d73SArnd Bergmann spu_release(ctx); 876a33a7d73SArnd Bergmann 877a33a7d73SArnd Bergmann poll_wait(file, &ctx->mfc_wq, wait); 878a33a7d73SArnd Bergmann 879a33a7d73SArnd Bergmann mask = 0; 880a33a7d73SArnd Bergmann if (free_elements & 0xffff) 881a33a7d73SArnd Bergmann mask |= POLLOUT | POLLWRNORM; 882a33a7d73SArnd Bergmann if (tagstatus & ctx->tagwait) 883a33a7d73SArnd Bergmann mask |= POLLIN | POLLRDNORM; 884a33a7d73SArnd Bergmann 885a33a7d73SArnd Bergmann pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__, 886a33a7d73SArnd Bergmann free_elements, tagstatus, ctx->tagwait); 887a33a7d73SArnd Bergmann 888a33a7d73SArnd Bergmann return mask; 889a33a7d73SArnd Bergmann } 890a33a7d73SArnd Bergmann 891a33a7d73SArnd Bergmann static int spufs_mfc_flush(struct file *file) 892a33a7d73SArnd Bergmann { 893a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 894a33a7d73SArnd Bergmann int ret; 895a33a7d73SArnd Bergmann 896a33a7d73SArnd Bergmann spu_acquire(ctx); 897a33a7d73SArnd Bergmann #if 0 898a33a7d73SArnd Bergmann /* this currently hangs */ 899a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 900a33a7d73SArnd Bergmann ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); 901a33a7d73SArnd Bergmann if (ret) 902a33a7d73SArnd Bergmann goto out; 903a33a7d73SArnd Bergmann ret = spufs_wait(ctx->mfc_wq, 904a33a7d73SArnd Bergmann ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); 905a33a7d73SArnd Bergmann out: 906a33a7d73SArnd Bergmann #else 907a33a7d73SArnd Bergmann ret = 0; 908a33a7d73SArnd Bergmann #endif 909a33a7d73SArnd Bergmann spu_release(ctx); 910a33a7d73SArnd Bergmann 911a33a7d73SArnd Bergmann return ret; 912a33a7d73SArnd Bergmann } 913a33a7d73SArnd Bergmann 914a33a7d73SArnd Bergmann static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, 915a33a7d73SArnd Bergmann int datasync) 916a33a7d73SArnd Bergmann { 917a33a7d73SArnd Bergmann return spufs_mfc_flush(file); 918a33a7d73SArnd Bergmann } 919a33a7d73SArnd Bergmann 920a33a7d73SArnd Bergmann static int spufs_mfc_fasync(int fd, struct file *file, int on) 921a33a7d73SArnd Bergmann { 922a33a7d73SArnd Bergmann struct spu_context *ctx = file->private_data; 923a33a7d73SArnd Bergmann 924a33a7d73SArnd Bergmann return fasync_helper(fd, file, on, &ctx->mfc_fasync); 925a33a7d73SArnd Bergmann } 926a33a7d73SArnd Bergmann 927a33a7d73SArnd Bergmann static struct file_operations spufs_mfc_fops = { 928a33a7d73SArnd Bergmann .open = spufs_mfc_open, 929a33a7d73SArnd Bergmann .read = spufs_mfc_read, 930a33a7d73SArnd Bergmann .write = spufs_mfc_write, 931a33a7d73SArnd Bergmann .poll = spufs_mfc_poll, 932a33a7d73SArnd Bergmann .flush = spufs_mfc_flush, 933a33a7d73SArnd Bergmann .fsync = spufs_mfc_fsync, 934a33a7d73SArnd Bergmann .fasync = spufs_mfc_fasync, 935a33a7d73SArnd Bergmann }; 936a33a7d73SArnd Bergmann 93767207b96SArnd Bergmann static void spufs_npc_set(void *data, u64 val) 93867207b96SArnd Bergmann { 93967207b96SArnd Bergmann struct spu_context *ctx = data; 9408b3d6663SArnd Bergmann spu_acquire(ctx); 9418b3d6663SArnd Bergmann ctx->ops->npc_write(ctx, val); 9428b3d6663SArnd Bergmann spu_release(ctx); 94367207b96SArnd Bergmann } 94467207b96SArnd Bergmann 94567207b96SArnd Bergmann static u64 spufs_npc_get(void *data) 94667207b96SArnd Bergmann { 94767207b96SArnd Bergmann struct spu_context *ctx = data; 94867207b96SArnd Bergmann u64 ret; 9498b3d6663SArnd Bergmann spu_acquire(ctx); 9508b3d6663SArnd Bergmann ret = ctx->ops->npc_read(ctx); 9518b3d6663SArnd Bergmann spu_release(ctx); 95267207b96SArnd Bergmann return ret; 95367207b96SArnd Bergmann } 95467207b96SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, "%llx\n") 95567207b96SArnd Bergmann 9568b3d6663SArnd Bergmann static void spufs_decr_set(void *data, u64 val) 9578b3d6663SArnd Bergmann { 9588b3d6663SArnd Bergmann struct spu_context *ctx = data; 9598b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 9608b3d6663SArnd Bergmann spu_acquire_saved(ctx); 9618b3d6663SArnd Bergmann lscsa->decr.slot[0] = (u32) val; 9628b3d6663SArnd Bergmann spu_release(ctx); 9638b3d6663SArnd Bergmann } 9648b3d6663SArnd Bergmann 9658b3d6663SArnd Bergmann static u64 spufs_decr_get(void *data) 9668b3d6663SArnd Bergmann { 9678b3d6663SArnd Bergmann struct spu_context *ctx = data; 9688b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 9698b3d6663SArnd Bergmann u64 ret; 9708b3d6663SArnd Bergmann spu_acquire_saved(ctx); 9718b3d6663SArnd Bergmann ret = lscsa->decr.slot[0]; 9728b3d6663SArnd Bergmann spu_release(ctx); 9738b3d6663SArnd Bergmann return ret; 9748b3d6663SArnd Bergmann } 9758b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_ops, spufs_decr_get, spufs_decr_set, 9768b3d6663SArnd Bergmann "%llx\n") 9778b3d6663SArnd Bergmann 9788b3d6663SArnd Bergmann static void spufs_decr_status_set(void *data, u64 val) 9798b3d6663SArnd Bergmann { 9808b3d6663SArnd Bergmann struct spu_context *ctx = data; 9818b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 9828b3d6663SArnd Bergmann spu_acquire_saved(ctx); 9838b3d6663SArnd Bergmann lscsa->decr_status.slot[0] = (u32) val; 9848b3d6663SArnd Bergmann spu_release(ctx); 9858b3d6663SArnd Bergmann } 9868b3d6663SArnd Bergmann 9878b3d6663SArnd Bergmann static u64 spufs_decr_status_get(void *data) 9888b3d6663SArnd Bergmann { 9898b3d6663SArnd Bergmann struct spu_context *ctx = data; 9908b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 9918b3d6663SArnd Bergmann u64 ret; 9928b3d6663SArnd Bergmann spu_acquire_saved(ctx); 9938b3d6663SArnd Bergmann ret = lscsa->decr_status.slot[0]; 9948b3d6663SArnd Bergmann spu_release(ctx); 9958b3d6663SArnd Bergmann return ret; 9968b3d6663SArnd Bergmann } 9978b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_decr_status_ops, spufs_decr_status_get, 9988b3d6663SArnd Bergmann spufs_decr_status_set, "%llx\n") 9998b3d6663SArnd Bergmann 10008b3d6663SArnd Bergmann static void spufs_spu_tag_mask_set(void *data, u64 val) 10018b3d6663SArnd Bergmann { 10028b3d6663SArnd Bergmann struct spu_context *ctx = data; 10038b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 10048b3d6663SArnd Bergmann spu_acquire_saved(ctx); 10058b3d6663SArnd Bergmann lscsa->tag_mask.slot[0] = (u32) val; 10068b3d6663SArnd Bergmann spu_release(ctx); 10078b3d6663SArnd Bergmann } 10088b3d6663SArnd Bergmann 10098b3d6663SArnd Bergmann static u64 spufs_spu_tag_mask_get(void *data) 10108b3d6663SArnd Bergmann { 10118b3d6663SArnd Bergmann struct spu_context *ctx = data; 10128b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 10138b3d6663SArnd Bergmann u64 ret; 10148b3d6663SArnd Bergmann spu_acquire_saved(ctx); 10158b3d6663SArnd Bergmann ret = lscsa->tag_mask.slot[0]; 10168b3d6663SArnd Bergmann spu_release(ctx); 10178b3d6663SArnd Bergmann return ret; 10188b3d6663SArnd Bergmann } 10198b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_spu_tag_mask_ops, spufs_spu_tag_mask_get, 10208b3d6663SArnd Bergmann spufs_spu_tag_mask_set, "%llx\n") 10218b3d6663SArnd Bergmann 10228b3d6663SArnd Bergmann static void spufs_event_mask_set(void *data, u64 val) 10238b3d6663SArnd Bergmann { 10248b3d6663SArnd Bergmann struct spu_context *ctx = data; 10258b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 10268b3d6663SArnd Bergmann spu_acquire_saved(ctx); 10278b3d6663SArnd Bergmann lscsa->event_mask.slot[0] = (u32) val; 10288b3d6663SArnd Bergmann spu_release(ctx); 10298b3d6663SArnd Bergmann } 10308b3d6663SArnd Bergmann 10318b3d6663SArnd Bergmann static u64 spufs_event_mask_get(void *data) 10328b3d6663SArnd Bergmann { 10338b3d6663SArnd Bergmann struct spu_context *ctx = data; 10348b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 10358b3d6663SArnd Bergmann u64 ret; 10368b3d6663SArnd Bergmann spu_acquire_saved(ctx); 10378b3d6663SArnd Bergmann ret = lscsa->event_mask.slot[0]; 10388b3d6663SArnd Bergmann spu_release(ctx); 10398b3d6663SArnd Bergmann return ret; 10408b3d6663SArnd Bergmann } 10418b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_event_mask_ops, spufs_event_mask_get, 10428b3d6663SArnd Bergmann spufs_event_mask_set, "%llx\n") 10438b3d6663SArnd Bergmann 10448b3d6663SArnd Bergmann static void spufs_srr0_set(void *data, u64 val) 10458b3d6663SArnd Bergmann { 10468b3d6663SArnd Bergmann struct spu_context *ctx = data; 10478b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 10488b3d6663SArnd Bergmann spu_acquire_saved(ctx); 10498b3d6663SArnd Bergmann lscsa->srr0.slot[0] = (u32) val; 10508b3d6663SArnd Bergmann spu_release(ctx); 10518b3d6663SArnd Bergmann } 10528b3d6663SArnd Bergmann 10538b3d6663SArnd Bergmann static u64 spufs_srr0_get(void *data) 10548b3d6663SArnd Bergmann { 10558b3d6663SArnd Bergmann struct spu_context *ctx = data; 10568b3d6663SArnd Bergmann struct spu_lscsa *lscsa = ctx->csa.lscsa; 10578b3d6663SArnd Bergmann u64 ret; 10588b3d6663SArnd Bergmann spu_acquire_saved(ctx); 10598b3d6663SArnd Bergmann ret = lscsa->srr0.slot[0]; 10608b3d6663SArnd Bergmann spu_release(ctx); 10618b3d6663SArnd Bergmann return ret; 10628b3d6663SArnd Bergmann } 10638b3d6663SArnd Bergmann DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set, 10648b3d6663SArnd Bergmann "%llx\n") 10658b3d6663SArnd Bergmann 106667207b96SArnd Bergmann struct tree_descr spufs_dir_contents[] = { 106767207b96SArnd Bergmann { "mem", &spufs_mem_fops, 0666, }, 10688b3d6663SArnd Bergmann { "regs", &spufs_regs_fops, 0666, }, 106967207b96SArnd Bergmann { "mbox", &spufs_mbox_fops, 0444, }, 107067207b96SArnd Bergmann { "ibox", &spufs_ibox_fops, 0444, }, 107167207b96SArnd Bergmann { "wbox", &spufs_wbox_fops, 0222, }, 107267207b96SArnd Bergmann { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, 107367207b96SArnd Bergmann { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, 107467207b96SArnd Bergmann { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, 107567207b96SArnd Bergmann { "signal1", &spufs_signal1_fops, 0666, }, 107667207b96SArnd Bergmann { "signal2", &spufs_signal2_fops, 0666, }, 107767207b96SArnd Bergmann { "signal1_type", &spufs_signal1_type, 0666, }, 107867207b96SArnd Bergmann { "signal2_type", &spufs_signal2_type, 0666, }, 1079a33a7d73SArnd Bergmann { "mfc", &spufs_mfc_fops, 0666, }, 108067207b96SArnd Bergmann { "npc", &spufs_npc_ops, 0666, }, 10818b3d6663SArnd Bergmann { "fpcr", &spufs_fpcr_fops, 0666, }, 10828b3d6663SArnd Bergmann { "decr", &spufs_decr_ops, 0666, }, 10838b3d6663SArnd Bergmann { "decr_status", &spufs_decr_status_ops, 0666, }, 10848b3d6663SArnd Bergmann { "spu_tag_mask", &spufs_spu_tag_mask_ops, 0666, }, 10858b3d6663SArnd Bergmann { "event_mask", &spufs_event_mask_ops, 0666, }, 10868b3d6663SArnd Bergmann { "srr0", &spufs_srr0_ops, 0666, }, 108767207b96SArnd Bergmann {}, 108867207b96SArnd Bergmann }; 1089