diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-29 11:28:30 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-29 11:28:30 -0800 |
commit | 76babde121d2ffef04ca692ce64ef9f8a9866086 (patch) | |
tree | 294923bbb4974258d86d223e35eee691abacdfb1 /arch/powerpc/platforms/cell/spufs | |
parent | e71ac6032edf77a1e4a81f3e3b260807e94b37a5 (diff) | |
parent | 15e812ad849e142e3dfc984d33c4d8042389f148 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc: (67 commits)
[PATCH] powerpc: Remove oprofile spinlock backtrace code
[PATCH] powerpc: Add oprofile calltrace support to all powerpc cpus
[PATCH] powerpc: Add oprofile calltrace support
[PATCH] for_each_possible_cpu: ppc
[PATCH] for_each_possible_cpu: powerpc
[PATCH] lock PTE before updating it in 440/BookE page fault handler
[PATCH] powerpc: Kill _machine and hard-coded platform numbers
ppc: Fix compile error in arch/ppc/lib/strcase.c
[PATCH] git-powerpc: WARN was a dumb idea
[PATCH] powerpc: a couple of trivial compile warning fixes
powerpc: remove OCP references
powerpc: Make uImage default build output for MPC8540 ADS
powerpc: move math-emu over to arch/powerpc
powerpc: use memparse() for mem= command line parsing
ppc: fix strncasecmp prototype
[PATCH] powerpc: make ISA floppies work again
[PATCH] powerpc: Fix some initcall return values
[PATCH] powerpc: Workaround for pSeries RTAS bug
[PATCH] spufs: fix __init/__exit annotations
[PATCH] powerpc: add hvc backend for rtas
...
Diffstat (limited to 'arch/powerpc/platforms/cell/spufs')
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/backing_ops.c | 47 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/context.c | 24 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/file.c | 523 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/hw_ops.c | 57 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/inode.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/run.c | 91 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sched.c | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/spufs.h | 28 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/switch.c | 3 |
9 files changed, 764 insertions, 17 deletions
diff --git a/arch/powerpc/platforms/cell/spufs/backing_ops.c b/arch/powerpc/platforms/cell/spufs/backing_ops.c index a5c489a53c6..f1d35ddc9df 100644 --- a/arch/powerpc/platforms/cell/spufs/backing_ops.c +++ b/arch/powerpc/platforms/cell/spufs/backing_ops.c @@ -285,6 +285,49 @@ static void spu_backing_runcntl_stop(struct spu_context *ctx) spu_backing_runcntl_write(ctx, SPU_RUNCNTL_STOP); } +static int spu_backing_set_mfc_query(struct spu_context * ctx, u32 mask, + u32 mode) +{ + struct spu_problem_collapsed *prob = &ctx->csa.prob; + int ret; + + spin_lock(&ctx->csa.register_lock); + ret = -EAGAIN; + if (prob->dma_querytype_RW) + goto out; + ret = 0; + /* FIXME: what are the side-effects of this? */ + prob->dma_querymask_RW = mask; + prob->dma_querytype_RW = mode; +out: + spin_unlock(&ctx->csa.register_lock); + + return ret; +} + +static u32 spu_backing_read_mfc_tagstatus(struct spu_context * ctx) +{ + return ctx->csa.prob.dma_tagstatus_R; +} + +static u32 spu_backing_get_mfc_free_elements(struct spu_context *ctx) +{ + return ctx->csa.prob.dma_qstatus_R; +} + +static int spu_backing_send_mfc_command(struct spu_context *ctx, + struct mfc_dma_command *cmd) +{ + int ret; + + spin_lock(&ctx->csa.register_lock); + ret = -EAGAIN; + /* FIXME: set up priv2->puq */ + spin_unlock(&ctx->csa.register_lock); + + return ret; +} + struct spu_context_ops spu_backing_ops = { .mbox_read = spu_backing_mbox_read, .mbox_stat_read = spu_backing_mbox_stat_read, @@ -305,4 +348,8 @@ struct spu_context_ops spu_backing_ops = { .get_ls = spu_backing_get_ls, .runcntl_write = spu_backing_runcntl_write, .runcntl_stop = spu_backing_runcntl_stop, + .set_mfc_query = spu_backing_set_mfc_query, + .read_mfc_tagstatus = spu_backing_read_mfc_tagstatus, + .get_mfc_free_elements = spu_backing_get_mfc_free_elements, + .send_mfc_command = spu_backing_send_mfc_command, }; diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 336f238102f..8bb33abfad1 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -27,7 +27,7 @@ #include <asm/spu_csa.h> #include "spufs.h" -struct spu_context *alloc_spu_context(struct address_space *local_store) +struct spu_context *alloc_spu_context(void) { struct spu_context *ctx; ctx = kmalloc(sizeof *ctx, GFP_KERNEL); @@ -47,10 +47,17 @@ struct spu_context *alloc_spu_context(struct address_space *local_store) init_waitqueue_head(&ctx->ibox_wq); init_waitqueue_head(&ctx->wbox_wq); init_waitqueue_head(&ctx->stop_wq); + init_waitqueue_head(&ctx->mfc_wq); ctx->ibox_fasync = NULL; ctx->wbox_fasync = NULL; + ctx->mfc_fasync = NULL; + ctx->mfc = NULL; + ctx->tagwait = 0; ctx->state = SPU_STATE_SAVED; - ctx->local_store = local_store; + ctx->local_store = NULL; + ctx->cntl = NULL; + ctx->signal1 = NULL; + ctx->signal2 = NULL; ctx->spu = NULL; ctx->ops = &spu_backing_ops; ctx->owner = get_task_mm(current); @@ -68,8 +75,6 @@ void destroy_spu_context(struct kref *kref) ctx = container_of(kref, struct spu_context, kref); down_write(&ctx->state_sema); spu_deactivate(ctx); - ctx->ibox_fasync = NULL; - ctx->wbox_fasync = NULL; up_write(&ctx->state_sema); spu_fini_csa(&ctx->csa); kfree(ctx); @@ -109,7 +114,16 @@ void spu_release(struct spu_context *ctx) void spu_unmap_mappings(struct spu_context *ctx) { - unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1); + if (ctx->local_store) + unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1); + if (ctx->mfc) + unmap_mapping_range(ctx->mfc, 0, 0x4000, 1); + if (ctx->cntl) + unmap_mapping_range(ctx->cntl, 0, 0x4000, 1); + if (ctx->signal1) + unmap_mapping_range(ctx->signal1, 0, 0x4000, 1); + if (ctx->signal2) + unmap_mapping_range(ctx->signal2, 0, 0x4000, 1); } int spu_acquire_runnable(struct spu_context *ctx) diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index dfa649c9b95..366185e9266 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -20,6 +20,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#undef DEBUG + #include <linux/fs.h> #include <linux/ioctl.h> #include <linux/module.h> @@ -39,8 +41,10 @@ static int spufs_mem_open(struct inode *inode, struct file *file) { struct spufs_inode_info *i = SPUFS_I(inode); - file->private_data = i->i_ctx; - file->f_mapping = i->i_ctx->local_store; + struct spu_context *ctx = i->i_ctx; + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->local_store = inode->i_mapping; return 0; } @@ -84,7 +88,7 @@ spufs_mem_write(struct file *file, const char __user *buffer, return ret; } -#ifdef CONFIG_SPARSEMEM +#ifdef CONFIG_SPUFS_MMAP static struct page * spufs_mem_mmap_nopage(struct vm_area_struct *vma, unsigned long address, int *type) @@ -136,11 +140,113 @@ static struct file_operations spufs_mem_fops = { .read = spufs_mem_read, .write = spufs_mem_write, .llseek = generic_file_llseek, -#ifdef CONFIG_SPARSEMEM +#ifdef CONFIG_SPUFS_MMAP .mmap = spufs_mem_mmap, #endif }; +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_ps_nopage(struct vm_area_struct *vma, + unsigned long address, + int *type, unsigned long ps_offs) +{ + struct page *page = NOPAGE_SIGBUS; + int fault_type = VM_FAULT_SIGBUS; + struct spu_context *ctx = vma->vm_file->private_data; + unsigned long offset = address - vma->vm_start; + unsigned long area; + int ret; + + offset += vma->vm_pgoff << PAGE_SHIFT; + if (offset >= 0x4000) + goto out; + + ret = spu_acquire_runnable(ctx); + if (ret) + goto out; + + area = ctx->spu->problem_phys + ps_offs; + page = pfn_to_page((area + offset) >> PAGE_SHIFT); + fault_type = VM_FAULT_MINOR; + page_cache_get(page); + + spu_release(ctx); + + out: + if (type) + *type = fault_type; + + return page; +} + +static struct page *spufs_cntl_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x4000); +} + +static struct vm_operations_struct spufs_cntl_mmap_vmops = { + .nopage = spufs_cntl_mmap_nopage, +}; + +/* + * mmap support for problem state control area [0x4000 - 0x4fff]. + * Mapping this area requires that the application have CAP_SYS_RAWIO, + * as these registers require special care when read/writing. + */ +static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_cntl_mmap_vmops; + return 0; +} +#endif + +static int spufs_cntl_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->cntl = inode->i_mapping; + return 0; +} + +static ssize_t +spufs_cntl_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + /* FIXME: read from spu status */ + return -EINVAL; +} + +static ssize_t +spufs_cntl_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + /* FIXME: write to runctl bit */ + return -EINVAL; +} + +static struct file_operations spufs_cntl_fops = { + .open = spufs_cntl_open, + .read = spufs_cntl_read, + .write = spufs_cntl_write, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_cntl_mmap, +#endif +}; + static int spufs_regs_open(struct inode *inode, struct file *file) { @@ -501,6 +607,16 @@ static struct file_operations spufs_wbox_stat_fops = { .read = spufs_wbox_stat_read, }; +static int spufs_signal1_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->signal1 = inode->i_mapping; + return nonseekable_open(inode, file); +} + static ssize_t spufs_signal1_read(struct file *file, char __user *buf, size_t len, loff_t *pos) { @@ -541,12 +657,50 @@ static ssize_t spufs_signal1_write(struct file *file, const char __user *buf, return 4; } +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_signal1_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x14000); +} + +static struct vm_operations_struct spufs_signal1_mmap_vmops = { + .nopage = spufs_signal1_mmap_nopage, +}; + +static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_signal1_mmap_vmops; + return 0; +} +#endif + static struct file_operations spufs_signal1_fops = { - .open = spufs_pipe_open, + .open = spufs_signal1_open, .read = spufs_signal1_read, .write = spufs_signal1_write, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_signal1_mmap, +#endif }; +static int spufs_signal2_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + file->private_data = ctx; + file->f_mapping = inode->i_mapping; + ctx->signal2 = inode->i_mapping; + return nonseekable_open(inode, file); +} + static ssize_t spufs_signal2_read(struct file *file, char __user *buf, size_t len, loff_t *pos) { @@ -589,10 +743,39 @@ static ssize_t spufs_signal2_write(struct file *file, const char __user *buf, return 4; } +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_signal2_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x1c000); +} + +static struct vm_operations_struct spufs_signal2_mmap_vmops = { + .nopage = spufs_signal2_mmap_nopage, +}; + +static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + /* FIXME: */ + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_signal2_mmap_vmops; + return 0; +} +#endif + static struct file_operations spufs_signal2_fops = { - .open = spufs_pipe_open, + .open = spufs_signal2_open, .read = spufs_signal2_read, .write = spufs_signal2_write, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_signal2_mmap, +#endif }; static void spufs_signal1_type_set(void *data, u64 val) @@ -641,6 +824,332 @@ static u64 spufs_signal2_type_get(void *data) DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, spufs_signal2_type_set, "%llu"); +#ifdef CONFIG_SPUFS_MMAP +static struct page *spufs_mfc_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return spufs_ps_nopage(vma, address, type, 0x3000); +} + +static struct vm_operations_struct spufs_mfc_mmap_vmops = { + .nopage = spufs_mfc_mmap_nopage, +}; + +/* + * mmap support for problem state MFC DMA area [0x0000 - 0x0fff]. + * Mapping this area requires that the application have CAP_SYS_RAWIO, + * as these registers require special care when read/writing. + */ +static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma) +{ + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + vma->vm_flags |= VM_RESERVED; + vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot) + | _PAGE_NO_CACHE); + + vma->vm_ops = &spufs_mfc_mmap_vmops; + return 0; +} +#endif + +static int spufs_mfc_open(struct inode *inode, struct file *file) +{ + struct spufs_inode_info *i = SPUFS_I(inode); + struct spu_context *ctx = i->i_ctx; + + /* we don't want to deal with DMA into other processes */ + if (ctx->owner != current->mm) + return -EINVAL; + + if (atomic_read(&inode->i_count) != 1) + return -EBUSY; + + file->private_data = ctx; + return nonseekable_open(inode, file); +} + +/* interrupt-level mfc callback function. */ +void spufs_mfc_callback(struct spu *spu) +{ + struct spu_context *ctx = spu->ctx; + + wake_up_all(&ctx->mfc_wq); + + pr_debug("%s %s\n", __FUNCTION__, spu->name); + if (ctx->mfc_fasync) { + u32 free_elements, tagstatus; + unsigned int mask; + + /* no need for spu_acquire in interrupt context */ + free_elements = ctx->ops->get_mfc_free_elements(ctx); + tagstatus = ctx->ops->read_mfc_tagstatus(ctx); + + mask = 0; + if (free_elements & 0xffff) + mask |= POLLOUT; + if (tagstatus & ctx->tagwait) + mask |= POLLIN; + + kill_fasync(&ctx->mfc_fasync, SIGIO, mask); + } +} + +static int spufs_read_mfc_tagstatus(struct spu_context *ctx, u32 *status) +{ + /* See if there is one tag group is complete */ + /* FIXME we need locking around tagwait */ + *status = ctx->ops->read_mfc_tagstatus(ctx) & ctx->tagwait; + ctx->tagwait &= ~*status; + if (*status) + return 1; + + /* enable interrupt waiting for any tag group, + may silently fail if interrupts are already enabled */ + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); + return 0; +} + +static ssize_t spufs_mfc_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + int ret = -EINVAL; + u32 status; + + if (size != 4) + goto out; + + spu_acquire(ctx); + if (file->f_flags & O_NONBLOCK) { + status = ctx->ops->read_mfc_tagstatus(ctx); + if (!(status & ctx->tagwait)) + ret = -EAGAIN; + else + ctx->tagwait &= ~status; + } else { + ret = spufs_wait(ctx->mfc_wq, + spufs_read_mfc_tagstatus(ctx, &status)); + } + spu_release(ctx); + + if (ret) + goto out; + + ret = 4; + if (copy_to_user(buffer, &status, 4)) + ret = -EFAULT; + +out: + return ret; +} + +static int spufs_check_valid_dma(struct mfc_dma_command *cmd) +{ + pr_debug("queueing DMA %x %lx %x %x %x\n", cmd->lsa, + cmd->ea, cmd->size, cmd->tag, cmd->cmd); + + switch (cmd->cmd) { + case MFC_PUT_CMD: + case MFC_PUTF_CMD: + case MFC_PUTB_CMD: + case MFC_GET_CMD: + case MFC_GETF_CMD: + case MFC_GETB_CMD: + break; + default: + pr_debug("invalid DMA opcode %x\n", cmd->cmd); + return -EIO; + } + + if ((cmd->lsa & 0xf) != (cmd->ea &0xf)) { + pr_debug("invalid DMA alignment, ea %lx lsa %x\n", + cmd->ea, cmd->lsa); + return -EIO; + } + + switch (cmd->size & 0xf) { + case 1: + break; + case 2: + if (cmd->lsa & 1) + goto error; + break; + case 4: + if (cmd->lsa & 3) + goto error; + break; + case 8: + if (cmd->lsa & 7) + goto error; + break; + case 0: + if (cmd->lsa & 15) + goto error; + break; + error: + default: + pr_debug("invalid DMA alignment %x for size %x\n", + cmd->lsa & 0xf, cmd->size); + return -EIO; + } + + if (cmd->size > 16 * 1024) { + pr_debug("invalid DMA size %x\n", cmd->size); + return -EIO; + } + + if (cmd->tag & 0xfff0) { + /* we reserve the higher tag numbers for kernel use */ + pr_debug("invalid DMA tag\n"); + return -EIO; + } + + if (cmd->class) { + /* not supported in this version */ + pr_debug("invalid DMA class\n"); + return -EIO; + } + + return 0; +} + +static int spu_send_mfc_command(struct spu_context *ctx, + struct mfc_dma_command cmd, + int *error) +{ + *error = ctx->ops->send_mfc_command(ctx, &cmd); + if (*error == -EAGAIN) { + /* wait for any tag group to complete + so we have space for the new command */ + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 1); + /* try again, because the queue might be + empty again */ + *error = ctx->ops->send_mfc_command(ctx, &cmd); + if (*error == -EAGAIN) + return 0; + } + return 1; +} + +static ssize_t spufs_mfc_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct spu_context *ctx = file->private_data; + struct mfc_dma_command cmd; + int ret = -EINVAL; + + if (size != sizeof cmd) + goto out; + + ret = -EFAULT; + if (copy_from_user(&cmd, buffer, sizeof cmd)) + goto out; + + ret = spufs_check_valid_dma(&cmd); + if (ret) + goto out; + + spu_acquire_runnable(ctx); + if (file->f_flags & O_NONBLOCK) { + ret = ctx->ops->send_mfc_command(ctx, &cmd); + } else { + int status; + ret = spufs_wait(ctx->mfc_wq, + spu_send_mfc_command(ctx, cmd, &status)); + if (status) + ret = status; + } + spu_release(ctx); + + if (ret) + goto out; + + ctx->tagwait |= 1 << cmd.tag; + +out: + return ret; +} + +static unsigned int spufs_mfc_poll(struct file *file,poll_table *wait) +{ + struct spu_context *ctx = file->private_data; + u32 free_elements, tagstatus; + unsigned int mask; + + spu_acquire(ctx); + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2); + free_elements = ctx->ops->get_mfc_free_elements(ctx); + tagstatus = ctx->ops->read_mfc_tagstatus(ctx); + spu_release(ctx); + + poll_wait(file, &ctx->mfc_wq, wait); + + mask = 0; + if (free_elements & 0xffff) + mask |= POLLOUT | POLLWRNORM; + if (tagstatus & ctx->tagwait) + mask |= POLLIN | POLLRDNORM; + + pr_debug("%s: free %d tagstatus %d tagwait %d\n", __FUNCTION__, + free_elements, tagstatus, ctx->tagwait); + + return mask; +} + +static int spufs_mfc_flush(struct file *file) +{ + struct spu_context *ctx = file->private_data; + int ret; + + spu_acquire(ctx); +#if 0 +/* this currently hangs */ + ret = spufs_wait(ctx->mfc_wq, + ctx->ops->set_mfc_query(ctx, ctx->tagwait, 2)); + if (ret) + goto out; + ret = spufs_wait(ctx->mfc_wq, + ctx->ops->read_mfc_tagstatus(ctx) == ctx->tagwait); +out: +#else + ret = 0; +#endif + spu_release(ctx); + + return ret; +} + +static int spufs_mfc_fsync(struct file *file, struct dentry *dentry, + int datasync) +{ + return spufs_mfc_flush(file); +} + +static int spufs_mfc_fasync(int fd, struct file *file, int on) +{ + struct spu_context *ctx = file->private_data; + + return fasync_helper(fd, file, on, &ctx->mfc_fasync); +} + +static struct file_operations spufs_mfc_fops = { + .open = spufs_mfc_open, + .read = spufs_mfc_read, + .write = spufs_mfc_write, + .poll = spufs_mfc_poll, + .flush = spufs_mfc_flush, + .fsync = spufs_mfc_fsync, + .fasync = spufs_mfc_fasync, +#ifdef CONFIG_SPUFS_MMAP + .mmap = spufs_mfc_mmap, +#endif +}; + static void spufs_npc_set(void *data, u64 val) { struct spu_context *ctx = data; @@ -783,6 +1292,8 @@ struct tree_descr spufs_dir_contents[] = { { "signal2", &spufs_signal2_fops, 0666, }, { "signal1_type", &spufs_signal1_type, 0666, }, { "signal2_type", &spufs_signal2_type, 0666, }, + { "mfc", &spufs_mfc_fops, 0666, }, + { "cntl", &spufs_cntl_fops, 0666, }, { "npc", &spufs_npc_ops, 0666, }, { "fpcr", &spufs_fpcr_fops, 0666, }, { "decr", &spufs_decr_ops, 0666, }, diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c index 5445719bff7..a13a8b5a014 100644 --- a/arch/powerpc/platforms/cell/spufs/hw_ops.c +++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c @@ -232,6 +232,59 @@ static void spu_hw_runcntl_stop(struct spu_context *ctx) spin_unlock_irq(&ctx->spu->register_lock); } +static int spu_hw_set_mfc_query(struct spu_context * ctx, u32 mask, u32 mode) +{ + struct spu_problem *prob = ctx->spu->problem; + int ret; + + spin_lock_irq(&ctx->spu->register_lock); + ret = -EAGAIN; + if (in_be32(&prob->dma_querytype_RW)) + goto out; + ret = 0; + out_be32(&prob->dma_querymask_RW, mask); + out_be32(&prob->dma_querytype_RW, mode); +out: + spin_unlock_irq(&ctx->spu->register_lock); + return ret; +} + +static u32 spu_hw_read_mfc_tagstatus(struct spu_context * ctx) +{ + return in_be32(&ctx->spu->problem->dma_tagstatus_R); +} + +static u32 spu_hw_get_mfc_free_elements(struct spu_context *ctx) +{ + return in_be32(&ctx->spu->problem->dma_qstatus_R); +} + +static int spu_hw_send_mfc_command(struct spu_context *ctx, + struct mfc_dma_command *cmd) +{ + u32 status; + struct spu_problem *prob = ctx->spu->problem; + + spin_lock_irq(&ctx->spu->register_lock); + out_be32(&prob->mfc_lsa_W, cmd->lsa); + out_be64(&prob->mfc_ea_W, cmd->ea); + out_be32(&prob->mfc_union_W.by32.mfc_size_tag32, + cmd->size << 16 | cmd->tag); + out_be32(&prob->mfc_union_W.by32.mfc_class_cmd32, + cmd->class << 16 | cmd->cmd); + status = in_be32(&prob->mfc_union_W.by32.mfc_class_cmd32); + spin_unlock_irq(&ctx->spu->register_lock); + + switch (status & 0xffff) { + case 0: + return 0; + case 2: + return -EAGAIN; + default: + return -EINVAL; + } +} + struct spu_context_ops spu_hw_ops = { .mbox_read = spu_hw_mbox_read, .mbox_stat_read = spu_hw_mbox_stat_read, @@ -252,4 +305,8 @@ struct spu_context_ops spu_hw_ops = { .get_ls = spu_hw_get_ls, .runcntl_write = spu_hw_runcntl_write, .runcntl_stop = spu_hw_runcntl_stop, + .set_mfc_query = spu_hw_set_mfc_query, + .read_mfc_tagstatus = spu_hw_read_mfc_tagstatus, + .get_mfc_free_elements = spu_hw_get_mfc_free_elements, + .send_mfc_command = spu_hw_send_mfc_command, }; diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 5be40aa483f..d9554199afa 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -241,7 +241,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) inode->i_gid = dir->i_gid; inode->i_mode &= S_ISGID; } - ctx = alloc_spu_context(inode->i_mapping); + ctx = alloc_spu_context(); SPUFS_I(inode)->i_ctx = ctx; if (!ctx) goto out_iput; @@ -442,7 +442,7 @@ static struct file_system_type spufs_type = { .kill_sb = kill_litter_super, }; -static int spufs_init(void) +static int __init spufs_init(void) { int ret; ret = -ENOMEM; @@ -472,7 +472,7 @@ out: } module_init(spufs_init); -static void spufs_exit(void) +static void __exit spufs_exit(void) { spu_sched_exit(); unregister_spu_syscalls(&spufs_calls); diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 18ea8866c61..c04e078c0fe 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -76,6 +76,90 @@ static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc, return 0; } +/* + * SPU syscall restarting is tricky because we violate the basic + * assumption that the signal handler is running on the interrupted + * thread. Here instead, the handler runs on PowerPC user space code, + * while the syscall was called from the SPU. + * This means we can only do a very rough approximation of POSIX + * signal semantics. + */ +int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret, + unsigned int *npc) +{ + int ret; + + switch (*spu_ret) { + case -ERESTARTSYS: + case -ERESTARTNOINTR: + /* + * Enter the regular syscall restarting for + * sys_spu_run, then restart the SPU syscall + * callback. + */ + *npc -= 8; + ret = -ERESTARTSYS; + break; + case -ERESTARTNOHAND: + case -ERESTART_RESTARTBLOCK: + /* + * Restart block is too hard for now, just return -EINTR + * to the SPU. + * ERESTARTNOHAND comes from sys_pause, we also return + * -EINTR from there. + * Assume that we need to be restarted ourselves though. + */ + *spu_ret = -EINTR; + ret = -ERESTARTSYS; + break; + default: + printk(KERN_WARNING "%s: unexpected return code %ld\n", + __FUNCTION__, *spu_ret); + ret = 0; + } + return ret; +} + +int spu_process_callback(struct spu_context *ctx) +{ + struct spu_syscall_block s; + u32 ls_pointer, npc; + char *ls; + long spu_ret; + int ret; + + /* get syscall block from local store */ + npc = ctx->ops->npc_read(ctx); + ls = ctx->ops->get_ls(ctx); + ls_pointer = *(u32*)(ls + npc); + if (ls_pointer > (LS_SIZE - sizeof(s))) + return -EFAULT; + memcpy(&s, ls + ls_pointer, sizeof (s)); + + /* do actual syscall without pinning the spu */ + ret = 0; + spu_ret = -ENOSYS; + npc += 4; + + if (s.nr_ret < __NR_syscalls) { + spu_release(ctx); + /* do actual system call from here */ + spu_ret = spu_sys_callback(&s); + if (spu_ret <= -ERESTARTSYS) { + ret = spu_handle_restartsys(ctx, &spu_ret, &npc); + } + spu_acquire(ctx); + if (ret == -ERESTARTSYS) + return ret; + } + + /* write result, jump over indirect pointer */ + memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret)); + ctx->ops->npc_write(ctx, npc); + ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); + return ret; +} + static inline int spu_process_events(struct spu_context *ctx) { struct spu *spu = ctx->spu; @@ -107,6 +191,13 @@ long spufs_run_spu(struct file *file, struct spu_context *ctx, ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status)); if (unlikely(ret)) break; + if ((*status & SPU_STATUS_STOPPED_BY_STOP) && + (*status >> SPU_STOP_STATUS_SHIFT == 0x2104)) { + ret = spu_process_callback(ctx); + if (ret) + break; + *status &= ~SPU_STATUS_STOPPED_BY_STOP; + } if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) { ret = spu_reacquire_runnable(ctx, npc, status); if (ret) diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 963182fbd1a..bf652cd7700 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -180,6 +180,7 @@ static inline void bind_context(struct spu *spu, struct spu_context *ctx) spu->ibox_callback = spufs_ibox_callback; spu->wbox_callback = spufs_wbox_callback; spu->stop_callback = spufs_stop_callback; + spu->mfc_callback = spufs_mfc_callback; mb(); spu_unmap_mappings(ctx); spu_restore(&ctx->csa, spu); @@ -197,6 +198,7 @@ static inline void unbind_context(struct spu *spu, struct spu_context *ctx) spu->ibox_callback = NULL; spu->wbox_callback = NULL; spu->stop_callback = NULL; + spu->mfc_callback = NULL; spu->mm = NULL; spu->pid = 0; spu->prio = MAX_PRIO; diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index db2601f0abd..4485738e210 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -43,7 +43,11 @@ struct spu_context { struct spu *spu; /* pointer to a physical SPU */ struct spu_state csa; /* SPU context save area. */ spinlock_t mmio_lock; /* protects mmio access */ - struct address_space *local_store;/* local store backing store */ + struct address_space *local_store; /* local store mapping. */ + struct address_space *mfc; /* 'mfc' area mappings. */ + struct address_space *cntl; /* 'control' area mappings. */ + struct address_space *signal1; /* 'signal1' area mappings. */ + struct address_space *signal2; /* 'signal2' area mappings. */ enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state; struct rw_semaphore state_sema; @@ -55,13 +59,27 @@ struct spu_context { wait_queue_head_t ibox_wq; wait_queue_head_t wbox_wq; wait_queue_head_t stop_wq; + wait_queue_head_t mfc_wq; struct fasync_struct *ibox_fasync; struct fasync_struct *wbox_fasync; + struct fasync_struct *mfc_fasync; + u32 tagwait; struct spu_context_ops *ops; struct work_struct reap_work; u64 flags; }; +struct mfc_dma_command { + int32_t pad; /* reserved */ + uint32_t lsa; /* local storage address */ + uint64_t ea; /* effective address */ + uint16_t size; /* transfer size */ + uint16_t tag; /* command tag */ + uint16_t class; /* class ID */ + uint16_t cmd; /* command opcode */ +}; + + /* SPU context query/set operations. */ struct spu_context_ops { int (*mbox_read) (struct spu_context * ctx, u32 * data); @@ -84,6 +102,11 @@ struct spu_context_ops { char*(*get_ls) (struct spu_context * ctx); void (*runcntl_write) (struct spu_context * ctx, u32 data); void (*runcntl_stop) (struct spu_context * ctx); + int (*set_mfc_query)(struct spu_context * ctx, u32 mask, u32 mode); + u32 (*read_mfc_tagstatus)(struct spu_context * ctx); + u32 (*get_mfc_free_elements)(struct spu_context *ctx); + int (*send_mfc_command)(struct spu_context *ctx, + struct mfc_dma_command *cmd); }; extern struct spu_context_ops spu_hw_ops; @@ -106,7 +129,7 @@ long spufs_create_thread(struct nameidata *nd, extern struct file_operations spufs_context_fops; /* context management */ -struct spu_context * alloc_spu_context(struct address_space *local_store); +struct spu_context * alloc_spu_context(void); void destroy_spu_context(struct kref *kref); struct spu_context * get_spu_context(struct spu_context *ctx); int put_spu_context(struct spu_context *ctx); @@ -159,5 +182,6 @@ size_t spu_ibox_read(struct spu_context *ctx, u32 *data); void spufs_ibox_callback(struct spu *spu); void spufs_wbox_callback(struct spu *spu); void spufs_stop_callback(struct spu *spu); +void spufs_mfc_callback(struct spu *spu); #endif diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index 212db28531f..97898d5d34e 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -2145,7 +2145,8 @@ static void init_priv1(struct spu_state *csa) csa->priv1.int_mask_class1_RW = CLASS1_ENABLE_SEGMENT_FAULT_INTR | CLASS1_ENABLE_STORAGE_FAULT_INTR; csa->priv1.int_mask_class2_RW = CLASS2_ENABLE_SPU_STOP_INTR | - CLASS2_ENABLE_SPU_HALT_INTR; + CLASS2_ENABLE_SPU_HALT_INTR | + CLASS2_ENABLE_SPU_DMA_TAG_GROUP_COMPLETE_INTR; } static void init_priv2(struct spu_state *csa) |