kernel-fxtec-pro1x/arch/powerpc/platforms/cell/spufs/file.c

597 lines
13 KiB
C
Raw Normal View History

/*
* SPU file system -- file contents
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/semaphore.h>
#include <asm/spu.h>
#include <asm/uaccess.h>
#include "spufs.h"
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;
return 0;
}
static ssize_t
spufs_mem_read(struct file *file, char __user *buffer,
size_t size, loff_t *pos)
{
struct spu *spu;
struct spu_context *ctx;
int ret;
ctx = file->private_data;
spu = ctx->spu;
down_read(&ctx->backing_sema);
if (spu->number & 0/*1*/) {
ret = generic_file_read(file, buffer, size, pos);
goto out;
}
ret = simple_read_from_buffer(buffer, size, pos,
spu->local_store, LS_SIZE);
out:
up_read(&ctx->backing_sema);
return ret;
}
static ssize_t
spufs_mem_write(struct file *file, const char __user *buffer,
size_t size, loff_t *pos)
{
struct spu_context *ctx = file->private_data;
struct spu *spu = ctx->spu;
if (spu->number & 0) //1)
return generic_file_write(file, buffer, size, pos);
size = min_t(ssize_t, LS_SIZE - *pos, size);
if (size <= 0)
return -EFBIG;
*pos += size;
return copy_from_user(spu->local_store + *pos - size,
buffer, size) ? -EFAULT : size;
}
static int
spufs_mem_mmap(struct file *file, struct vm_area_struct *vma)
{
struct spu_context *ctx = file->private_data;
struct spu *spu = ctx->spu;
unsigned long pfn;
if (spu->number & 0) //1)
return generic_file_mmap(file, vma);
vma->vm_flags |= VM_RESERVED;
vma->vm_page_prot = __pgprot(pgprot_val (vma->vm_page_prot)
| _PAGE_NO_CACHE);
pfn = spu->local_store_phys >> PAGE_SHIFT;
/*
* This will work for actual SPUs, but not for vmalloc memory:
*/
if (remap_pfn_range(vma, vma->vm_start, pfn,
vma->vm_end-vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
}
static struct file_operations spufs_mem_fops = {
.open = spufs_mem_open,
.read = spufs_mem_read,
.write = spufs_mem_write,
.mmap = spufs_mem_mmap,
.llseek = generic_file_llseek,
};
/* generic open function for all pipe-like files */
static int spufs_pipe_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 ssize_t spufs_mbox_read(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
struct spu_problem __iomem *prob;
u32 mbox_stat;
u32 mbox_data;
if (len < 4)
return -EINVAL;
ctx = file->private_data;
prob = ctx->spu->problem;
mbox_stat = in_be32(&prob->mb_stat_R);
if (!(mbox_stat & 0x0000ff))
return -EAGAIN;
mbox_data = in_be32(&prob->pu_mb_R);
if (copy_to_user(buf, &mbox_data, sizeof mbox_data))
return -EFAULT;
return 4;
}
static struct file_operations spufs_mbox_fops = {
.open = spufs_pipe_open,
.read = spufs_mbox_read,
};
static ssize_t spufs_mbox_stat_read(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
u32 mbox_stat;
if (len < 4)
return -EINVAL;
ctx = file->private_data;
mbox_stat = in_be32(&ctx->spu->problem->mb_stat_R) & 0xff;
if (copy_to_user(buf, &mbox_stat, sizeof mbox_stat))
return -EFAULT;
return 4;
}
static struct file_operations spufs_mbox_stat_fops = {
.open = spufs_pipe_open,
.read = spufs_mbox_stat_read,
};
/* low-level ibox access function */
size_t spu_ibox_read(struct spu *spu, u32 *data)
{
int ret;
spin_lock_irq(&spu->register_lock);
if (in_be32(&spu->problem->mb_stat_R) & 0xff0000) {
/* read the first available word */
*data = in_be64(&spu->priv2->puint_mb_R);
ret = 4;
} else {
/* make sure we get woken up by the interrupt */
out_be64(&spu->priv1->int_mask_class2_RW,
in_be64(&spu->priv1->int_mask_class2_RW) | 0x1);
ret = 0;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
EXPORT_SYMBOL(spu_ibox_read);
static int spufs_ibox_fasync(int fd, struct file *file, int on)
{
struct spu_context *ctx;
ctx = file->private_data;
return fasync_helper(fd, file, on, &ctx->spu->ibox_fasync);
}
static ssize_t spufs_ibox_read(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
u32 ibox_data;
ssize_t ret;
if (len < 4)
return -EINVAL;
ctx = file->private_data;
ret = 0;
if (file->f_flags & O_NONBLOCK) {
if (!spu_ibox_read(ctx->spu, &ibox_data))
ret = -EAGAIN;
} else {
ret = wait_event_interruptible(ctx->spu->ibox_wq,
spu_ibox_read(ctx->spu, &ibox_data));
}
if (ret)
return ret;
ret = 4;
if (copy_to_user(buf, &ibox_data, sizeof ibox_data))
ret = -EFAULT;
return ret;
}
static unsigned int spufs_ibox_poll(struct file *file, poll_table *wait)
{
struct spu_context *ctx;
struct spu_problem __iomem *prob;
u32 mbox_stat;
unsigned int mask;
ctx = file->private_data;
prob = ctx->spu->problem;
mbox_stat = in_be32(&prob->mb_stat_R);
poll_wait(file, &ctx->spu->ibox_wq, wait);
mask = 0;
if (mbox_stat & 0xff0000)
mask |= POLLIN | POLLRDNORM;
return mask;
}
static struct file_operations spufs_ibox_fops = {
.open = spufs_pipe_open,
.read = spufs_ibox_read,
.poll = spufs_ibox_poll,
.fasync = spufs_ibox_fasync,
};
static ssize_t spufs_ibox_stat_read(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
u32 ibox_stat;
if (len < 4)
return -EINVAL;
ctx = file->private_data;
ibox_stat = (in_be32(&ctx->spu->problem->mb_stat_R) >> 16) & 0xff;
if (copy_to_user(buf, &ibox_stat, sizeof ibox_stat))
return -EFAULT;
return 4;
}
static struct file_operations spufs_ibox_stat_fops = {
.open = spufs_pipe_open,
.read = spufs_ibox_stat_read,
};
/* low-level mailbox write */
size_t spu_wbox_write(struct spu *spu, u32 data)
{
int ret;
spin_lock_irq(&spu->register_lock);
if (in_be32(&spu->problem->mb_stat_R) & 0x00ff00) {
/* we have space to write wbox_data to */
out_be32(&spu->problem->spu_mb_W, data);
ret = 4;
} else {
/* make sure we get woken up by the interrupt when space
becomes available */
out_be64(&spu->priv1->int_mask_class2_RW,
in_be64(&spu->priv1->int_mask_class2_RW) | 0x10);
ret = 0;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
EXPORT_SYMBOL(spu_wbox_write);
static int spufs_wbox_fasync(int fd, struct file *file, int on)
{
struct spu_context *ctx;
ctx = file->private_data;
return fasync_helper(fd, file, on, &ctx->spu->wbox_fasync);
}
static ssize_t spufs_wbox_write(struct file *file, const char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
u32 wbox_data;
int ret;
if (len < 4)
return -EINVAL;
ctx = file->private_data;
if (copy_from_user(&wbox_data, buf, sizeof wbox_data))
return -EFAULT;
ret = 0;
if (file->f_flags & O_NONBLOCK) {
if (!spu_wbox_write(ctx->spu, wbox_data))
ret = -EAGAIN;
} else {
ret = wait_event_interruptible(ctx->spu->wbox_wq,
spu_wbox_write(ctx->spu, wbox_data));
}
return ret ? ret : sizeof wbox_data;
}
static unsigned int spufs_wbox_poll(struct file *file, poll_table *wait)
{
struct spu_context *ctx;
struct spu_problem __iomem *prob;
u32 mbox_stat;
unsigned int mask;
ctx = file->private_data;
prob = ctx->spu->problem;
mbox_stat = in_be32(&prob->mb_stat_R);
poll_wait(file, &ctx->spu->wbox_wq, wait);
mask = 0;
if (mbox_stat & 0x00ff00)
mask = POLLOUT | POLLWRNORM;
return mask;
}
static struct file_operations spufs_wbox_fops = {
.open = spufs_pipe_open,
.write = spufs_wbox_write,
.poll = spufs_wbox_poll,
.fasync = spufs_wbox_fasync,
};
static ssize_t spufs_wbox_stat_read(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
u32 wbox_stat;
if (len < 4)
return -EINVAL;
ctx = file->private_data;
wbox_stat = (in_be32(&ctx->spu->problem->mb_stat_R) >> 8) & 0xff;
if (copy_to_user(buf, &wbox_stat, sizeof wbox_stat))
return -EFAULT;
return 4;
}
static struct file_operations spufs_wbox_stat_fops = {
.open = spufs_pipe_open,
.read = spufs_wbox_stat_read,
};
long spufs_run_spu(struct file *file, struct spu_context *ctx,
u32 *npc, u32 *status)
{
struct spu_problem __iomem *prob;
int ret;
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
if (!down_write_trylock(&ctx->backing_sema))
goto out;
} else {
down_write(&ctx->backing_sema);
}
prob = ctx->spu->problem;
out_be32(&prob->spu_npc_RW, *npc);
ret = spu_run(ctx->spu);
*status = in_be32(&prob->spu_status_R);
*npc = in_be32(&prob->spu_npc_RW);
up_write(&ctx->backing_sema);
out:
return ret;
}
static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
struct spu_problem *prob;
u32 data;
ctx = file->private_data;
prob = ctx->spu->problem;
if (len < 4)
return -EINVAL;
data = in_be32(&prob->signal_notify1);
if (copy_to_user(buf, &data, 4))
return -EFAULT;
return 4;
}
static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
struct spu_problem *prob;
u32 data;
ctx = file->private_data;
prob = ctx->spu->problem;
if (len < 4)
return -EINVAL;
if (copy_from_user(&data, buf, 4))
return -EFAULT;
out_be32(&prob->signal_notify1, data);
return 4;
}
static struct file_operations spufs_signal1_fops = {
.open = spufs_pipe_open,
.read = spufs_signal1_read,
.write = spufs_signal1_write,
};
static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
struct spu_problem *prob;
u32 data;
ctx = file->private_data;
prob = ctx->spu->problem;
if (len < 4)
return -EINVAL;
data = in_be32(&prob->signal_notify2);
if (copy_to_user(buf, &data, 4))
return -EFAULT;
return 4;
}
static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
size_t len, loff_t *pos)
{
struct spu_context *ctx;
struct spu_problem *prob;
u32 data;
ctx = file->private_data;
prob = ctx->spu->problem;
if (len < 4)
return -EINVAL;
if (copy_from_user(&data, buf, 4))
return -EFAULT;
out_be32(&prob->signal_notify2, data);
return 4;
}
static struct file_operations spufs_signal2_fops = {
.open = spufs_pipe_open,
.read = spufs_signal2_read,
.write = spufs_signal2_write,
};
static void spufs_signal1_type_set(void *data, u64 val)
{
struct spu_context *ctx = data;
struct spu_priv2 *priv2 = ctx->spu->priv2;
u64 tmp;
spin_lock_irq(&ctx->spu->register_lock);
tmp = in_be64(&priv2->spu_cfg_RW);
if (val)
tmp |= 1;
else
tmp &= ~1;
out_be64(&priv2->spu_cfg_RW, tmp);
spin_unlock_irq(&ctx->spu->register_lock);
}
static u64 spufs_signal1_type_get(void *data)
{
struct spu_context *ctx = data;
return (in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0;
}
DEFINE_SIMPLE_ATTRIBUTE(spufs_signal1_type, spufs_signal1_type_get,
spufs_signal1_type_set, "%llu");
static void spufs_signal2_type_set(void *data, u64 val)
{
struct spu_context *ctx = data;
struct spu_priv2 *priv2 = ctx->spu->priv2;
u64 tmp;
spin_lock_irq(&ctx->spu->register_lock);
tmp = in_be64(&priv2->spu_cfg_RW);
if (val)
tmp |= 2;
else
tmp &= ~2;
out_be64(&priv2->spu_cfg_RW, tmp);
spin_unlock_irq(&ctx->spu->register_lock);
}
static u64 spufs_signal2_type_get(void *data)
{
struct spu_context *ctx = data;
return (in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0;
}
DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
spufs_signal2_type_set, "%llu");
static void spufs_npc_set(void *data, u64 val)
{
struct spu_context *ctx = data;
out_be32(&ctx->spu->problem->spu_npc_RW, val);
}
static u64 spufs_npc_get(void *data)
{
struct spu_context *ctx = data;
u64 ret;
ret = in_be32(&ctx->spu->problem->spu_npc_RW);
return ret;
}
DEFINE_SIMPLE_ATTRIBUTE(spufs_npc_ops, spufs_npc_get, spufs_npc_set, "%llx\n")
struct tree_descr spufs_dir_contents[] = {
{ "mem", &spufs_mem_fops, 0666, },
{ "mbox", &spufs_mbox_fops, 0444, },
{ "ibox", &spufs_ibox_fops, 0444, },
{ "wbox", &spufs_wbox_fops, 0222, },
{ "mbox_stat", &spufs_mbox_stat_fops, 0444, },
{ "ibox_stat", &spufs_ibox_stat_fops, 0444, },
{ "wbox_stat", &spufs_wbox_stat_fops, 0444, },
{ "signal1", &spufs_signal1_fops, 0666, },
{ "signal2", &spufs_signal2_fops, 0666, },
{ "signal1_type", &spufs_signal1_type, 0666, },
{ "signal2_type", &spufs_signal2_type, 0666, },
{ "npc", &spufs_npc_ops, 0666, },
{},
};