Merge ../linus
[pandora-kernel.git] / arch / powerpc / platforms / cell / spufs / file.c
index 9738de7..58e794f 100644 (file)
@@ -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 | _PAGE_GUARDED);
+
+       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)
 {
@@ -304,34 +410,6 @@ static struct file_operations spufs_mbox_stat_fops = {
        .read   = spufs_mbox_stat_read,
 };
 
-/*
- * spufs_wait
- *     Same as wait_event_interruptible(), except that here
- *     we need to call spu_release(ctx) before sleeping, and
- *     then spu_acquire(ctx) when awoken.
- */
-
-#define spufs_wait(wq, condition)                                      \
-({                                                                     \
-       int __ret = 0;                                                  \
-       DEFINE_WAIT(__wait);                                            \
-       for (;;) {                                                      \
-               prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE);    \
-               if (condition)                                          \
-                       break;                                          \
-               if (!signal_pending(current)) {                         \
-                       spu_release(ctx);                               \
-                       schedule();                                     \
-                       spu_acquire(ctx);                               \
-                       continue;                                       \
-               }                                                       \
-               __ret = -ERESTARTSYS;                                   \
-               break;                                                  \
-       }                                                               \
-       finish_wait(&(wq), &__wait);                                    \
-       __ret;                                                          \
-})
-
 /* low-level ibox access function */
 size_t spu_ibox_read(struct spu_context *ctx, u32 *data)
 {
@@ -529,123 +607,14 @@ static struct file_operations spufs_wbox_stat_fops = {
        .read   = spufs_wbox_stat_read,
 };
 
-/* interrupt-level stop callback function. */
-void spufs_stop_callback(struct spu *spu)
-{
-       struct spu_context *ctx = spu->ctx;
-
-       wake_up_all(&ctx->stop_wq);
-}
-
-static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
-{
-       struct spu *spu;
-       u64 pte_fault;
-
-       *stat = ctx->ops->status_read(ctx);
-       if (ctx->state != SPU_STATE_RUNNABLE)
-               return 1;
-       spu = ctx->spu;
-       pte_fault = spu->dsisr &
-           (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
-       return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
-}
-
-static inline int spu_run_init(struct spu_context *ctx, u32 * npc,
-                              u32 * status)
-{
-       int ret;
-
-       if ((ret = spu_acquire_runnable(ctx)) != 0)
-               return ret;
-       ctx->ops->npc_write(ctx, *npc);
-       ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
-       return 0;
-}
-
-static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
-                              u32 * status)
-{
-       int ret = 0;
-
-       *status = ctx->ops->status_read(ctx);
-       *npc = ctx->ops->npc_read(ctx);
-       spu_release(ctx);
-
-       if (signal_pending(current))
-               ret = -ERESTARTSYS;
-       if (unlikely(current->ptrace & PT_PTRACED)) {
-               if ((*status & SPU_STATUS_STOPPED_BY_STOP)
-                   && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
-                       force_sig(SIGTRAP, current);
-                       ret = -ERESTARTSYS;
-               }
-       }
-       return ret;
-}
-
-static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
-                                        u32 *status)
+static int spufs_signal1_open(struct inode *inode, struct file *file)
 {
-       int ret;
-
-       if ((ret = spu_run_fini(ctx, npc, status)) != 0)
-               return ret;
-       if (*status & (SPU_STATUS_STOPPED_BY_STOP |
-                      SPU_STATUS_STOPPED_BY_HALT)) {
-               return *status;
-       }
-       if ((ret = spu_run_init(ctx, npc, status)) != 0)
-               return ret;
-       return 0;
-}
-
-static inline int spu_process_events(struct spu_context *ctx)
-{
-       struct spu *spu = ctx->spu;
-       u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
-       int ret = 0;
-
-       if (spu->dsisr & pte_fault)
-               ret = spu_irq_class_1_bottom(spu);
-       if (spu->class_0_pending)
-               ret = spu_irq_class_0_bottom(spu);
-       if (!ret && signal_pending(current))
-               ret = -ERESTARTSYS;
-       return ret;
-}
-
-long spufs_run_spu(struct file *file, struct spu_context *ctx,
-                  u32 * npc, u32 * status)
-{
-       int ret;
-
-       if ((ret = spu_run_init(ctx, npc, status)) != 0)
-               return ret;
-
-       do {
-               ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, status));
-               if (unlikely(ret))
-                       break;
-               if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
-                       ret = spu_reacquire_runnable(ctx, npc, status);
-                       if (ret) {
-                               return ret;
-                       }
-                       continue;
-               }
-               ret = spu_process_events(ctx);
-
-       } while (!ret && !(*status & (SPU_STATUS_STOPPED_BY_STOP |
-                                     SPU_STATUS_STOPPED_BY_HALT)));
-
-       ctx->ops->runcntl_stop(ctx);
-       ret = spu_run_fini(ctx, npc, status);
-       if (!ret)
-               ret = *status;
-       spu_yield(ctx);
-
-       return ret;
+       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,
@@ -688,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 | _PAGE_GUARDED);
+
+       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)
 {
@@ -736,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 | _PAGE_GUARDED);
+
+       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)
@@ -788,6 +824,381 @@ 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_mss_mmap_nopage(struct vm_area_struct *vma,
+                                          unsigned long address, int *type)
+{
+       return spufs_ps_nopage(vma, address, type, 0x0000);
+}
+
+static struct vm_operations_struct spufs_mss_mmap_vmops = {
+       .nopage = spufs_mss_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_mss_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 | _PAGE_GUARDED);
+
+       vma->vm_ops = &spufs_mss_mmap_vmops;
+       return 0;
+}
+#endif
+
+static int spufs_mss_open(struct inode *inode, struct file *file)
+{
+       struct spufs_inode_info *i = SPUFS_I(inode);
+
+       file->private_data = i->i_ctx;
+       return nonseekable_open(inode, file);
+}
+
+static struct file_operations spufs_mss_fops = {
+       .open    = spufs_mss_open,
+#ifdef CONFIG_SPUFS_MMAP
+       .mmap    = spufs_mss_mmap,
+#endif
+};
+
+
+#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 | _PAGE_GUARDED);
+
+       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, fl_owner_t id)
+{
+       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, NULL);
+}
+
+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;
@@ -917,6 +1328,22 @@ static u64 spufs_srr0_get(void *data)
 DEFINE_SIMPLE_ATTRIBUTE(spufs_srr0_ops, spufs_srr0_get, spufs_srr0_set,
                        "%llx\n")
 
+static u64 spufs_id_get(void *data)
+{
+       struct spu_context *ctx = data;
+       u64 num;
+
+       spu_acquire(ctx);
+       if (ctx->state == SPU_STATE_RUNNABLE)
+               num = ctx->spu->number;
+       else
+               num = (unsigned int)-1;
+       spu_release(ctx);
+
+       return num;
+}
+DEFINE_SIMPLE_ATTRIBUTE(spufs_id_ops, spufs_id_get, 0, "0x%llx\n")
+
 struct tree_descr spufs_dir_contents[] = {
        { "mem",  &spufs_mem_fops,  0666, },
        { "regs", &spufs_regs_fops,  0666, },
@@ -930,6 +1357,9 @@ struct tree_descr spufs_dir_contents[] = {
        { "signal2", &spufs_signal2_fops, 0666, },
        { "signal1_type", &spufs_signal1_type, 0666, },
        { "signal2_type", &spufs_signal2_type, 0666, },
+       { "mss", &spufs_mss_fops, 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, },
@@ -937,5 +1367,6 @@ struct tree_descr spufs_dir_contents[] = {
        { "spu_tag_mask", &spufs_spu_tag_mask_ops, 0666, },
        { "event_mask", &spufs_event_mask_ops, 0666, },
        { "srr0", &spufs_srr0_ops, 0666, },
+       { "phys-id", &spufs_id_ops, 0666, },
        {},
 };