dma.c
Go to the documentation of this file.
/*
* libspe2 - A wrapper library to adapt the JSRE SPU usage model to SPUFS
* Copyright (C) 2005 IBM Corp.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License,
* or (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <sys/poll.h>
#include "create.h"
#include "dma.h"
static int spe_read_tag_status_block(spe_context_ptr_t spectx, unsigned int mask, unsigned int *tag_status);
static int spe_read_tag_status_noblock(spe_context_ptr_t spectx, unsigned int mask, unsigned int *tag_status);
static int issue_mfc_command(spe_context_ptr_t spectx, unsigned lsa, void *ea,
unsigned size, unsigned tag, unsigned tid, unsigned rid,
enum mfc_cmd cmd)
{
int ret;
DEBUG_PRINTF("queuing DMA %x %lx %x %x %x %x %x\n", lsa,
(unsigned long)ea, size, tag, tid, rid, (unsigned)cmd);
/* tag 16-31 are reserved by kernel */
if (tag > 0x0f || tid > 0xff || rid > 0xff) {
errno = EINVAL;
return -1;
}
if (spectx->base_private->flags & SPE_MAP_PS) {
volatile struct spe_mfc_command_area *cmd_area =
spectx->base_private->mfc_mmap_base;
unsigned int eal = (uintptr_t) ea & 0xFFFFFFFF;
unsigned int eah = (unsigned long long)(uintptr_t) ea >> 32;
_base_spe_context_lock(spectx, FD_MFC);
spectx->base_private->active_tagmask |= 1 << tag;
DEBUG_PRINTF("set active tagmask = 0x%04x, tag=%i\n",spectx->base_private->active_tagmask,tag);
while ((cmd_area->MFC_QStatus & 0x0000FFFF) == 0) ;
do {
cmd_area->MFC_LSA = lsa;
cmd_area->MFC_EAH = eah;
cmd_area->MFC_EAL = eal;
cmd_area->MFC_Size_Tag = (size << 16) | tag;
cmd_area->MFC_ClassID_CMD = (tid << 24) | (rid << 16) | cmd;
ret = cmd_area->MFC_CMDStatus & 0x00000003;
} while (ret); // at least one of the two bits is set
_base_spe_context_unlock(spectx, FD_MFC);
return 0;
}
else {
int fd;
fd = _base_spe_open_if_closed(spectx, FD_MFC, 0);
if (fd != -1) {
struct mfc_command_parameter_area parm = {
.lsa = lsa,
.ea = (unsigned long) ea,
.size = size,
.tag = tag,
.class = (tid << 8) | rid,
.cmd = cmd,
};
ret = write(fd, &parm, sizeof (parm));
if ((ret < 0) && (errno != EIO)) {
perror("spe_do_mfc_put: internal error");
}
return ret < 0 ? -1 : 0;
}
}
/* the kernel does not support DMA */
return 1;
}
static int spe_do_mfc_put(spe_context_ptr_t spectx, unsigned src, void *dst,
unsigned size, unsigned tag, unsigned tid, unsigned rid,
enum mfc_cmd cmd)
{
int ret;
ret = issue_mfc_command(spectx, src, dst, size, tag, tid, rid, cmd);
if (ret <= 0) {
return ret;
}
else {
/* the kernel does not support DMA, so just copy directly */
memcpy(dst, spectx->base_private->mem_mmap_base + src, size);
return 0;
}
}
static int spe_do_mfc_get(spe_context_ptr_t spectx, unsigned int dst, void *src,
unsigned int size, unsigned int tag, unsigned tid, unsigned rid,
enum mfc_cmd cmd)
{
int ret;
ret = issue_mfc_command(spectx, dst, src, size, tag, tid, rid, cmd);
if (ret <= 0) {
return ret;
}
else {
/* the kernel does not support DMA, so just copy directly */
memcpy(spectx->base_private->mem_mmap_base + dst, src, size);
return 0;
}
}
int _base_spe_mfcio_put(spe_context_ptr_t spectx,
unsigned int ls,
void *ea,
unsigned int size,
unsigned int tag,
unsigned int tid,
unsigned int rid)
{
return spe_do_mfc_put(spectx, ls, ea, size, tag, tid, rid, MFC_CMD_PUT);
}
int _base_spe_mfcio_putb(spe_context_ptr_t spectx,
unsigned int ls,
void *ea,
unsigned int size,
unsigned int tag,
unsigned int tid,
unsigned int rid)
{
return spe_do_mfc_put(spectx, ls, ea, size, tag, tid, rid, MFC_CMD_PUTB);
}
int _base_spe_mfcio_putf(spe_context_ptr_t spectx,
unsigned int ls,
void *ea,
unsigned int size,
unsigned int tag,
unsigned int tid,
unsigned int rid)
{
return spe_do_mfc_put(spectx, ls, ea, size, tag, tid, rid, MFC_CMD_PUTF);
}
int _base_spe_mfcio_get(spe_context_ptr_t spectx,
unsigned int ls,
void *ea,
unsigned int size,
unsigned int tag,
unsigned int tid,
unsigned int rid)
{
return spe_do_mfc_get(spectx, ls, ea, size, tag, tid, rid, MFC_CMD_GET);
}
int _base_spe_mfcio_getb(spe_context_ptr_t spectx,
unsigned int ls,
void *ea,
unsigned int size,
unsigned int tag,
unsigned int tid,
unsigned int rid)
{
return spe_do_mfc_get(spectx, ls, ea, size, tag, rid, rid, MFC_CMD_GETB);
}
int _base_spe_mfcio_getf(spe_context_ptr_t spectx,
unsigned int ls,
void *ea,
unsigned int size,
unsigned int tag,
unsigned int tid,
unsigned int rid)
{
return spe_do_mfc_get(spectx, ls, ea, size, tag, tid, rid, MFC_CMD_GETF);
}
static int spe_mfcio_tag_status_read_all(spe_context_ptr_t spectx,
unsigned int mask, unsigned int *tag_status)
{
int fd;
if (spectx->base_private->flags & SPE_MAP_PS) {
return spe_read_tag_status_block(spectx, mask, tag_status);
} else {
fd = _base_spe_open_if_closed(spectx, FD_MFC, 0);
if (fd == -1) {
return -1;
}
if (fsync(fd) != 0) {
return -1;
}
return spe_read_tag_status_block(spectx, mask, tag_status);
}
}
static int spe_mfcio_tag_status_read_any(spe_context_ptr_t spectx,
unsigned int mask, unsigned int *tag_status)
{
return spe_read_tag_status_block(spectx, mask, tag_status);
}
static int spe_mfcio_tag_status_read_immediate(spe_context_ptr_t spectx,
unsigned int mask, unsigned int *tag_status)
{
return spe_read_tag_status_noblock(spectx, mask, tag_status);
}
/* MFC Read tag status functions
*
*/
static int spe_read_tag_status_block(spe_context_ptr_t spectx, unsigned int mask, unsigned int *tag_status)
{
if (spectx->base_private->flags & SPE_MAP_PS) {
volatile struct spe_mfc_command_area *cmd_area =
spectx->base_private->mfc_mmap_base;
_base_spe_context_lock(spectx, FD_MFC);
cmd_area->Prxy_QueryMask = mask;
__asm__ ("eieio");
do {
*tag_status = cmd_area->Prxy_TagStatus;
spectx->base_private->active_tagmask ^= *tag_status;
DEBUG_PRINTF("unset active tagmask = 0x%04x, tag_status = 0x%04x\n",
spectx->base_private->active_tagmask,*tag_status);
} while (*tag_status ^ mask);
_base_spe_context_unlock(spectx, FD_MFC);
return 0;
} else {
int fd;
fd = _base_spe_open_if_closed(spectx, FD_MFC, 0);
if (fd == -1) {
return -1;
}
if (read(fd,tag_status,4) == 4) {
return 0;
}
}
return -1;
}
static int spe_read_tag_status_noblock(spe_context_ptr_t spectx, unsigned int mask, unsigned int *tag_status)
{
unsigned int ret;
if (spectx->base_private->flags & SPE_MAP_PS) {
volatile struct spe_mfc_command_area *cmd_area =
spectx->base_private->mfc_mmap_base;
_base_spe_context_lock(spectx, FD_MFC);
cmd_area->Prxy_QueryMask = mask;
__asm__ ("eieio");
*tag_status = cmd_area->Prxy_TagStatus;
spectx->base_private->active_tagmask ^= *tag_status;
DEBUG_PRINTF("unset active tagmask = 0x%04x, tag_status = 0x%04x\n",
spectx->base_private->active_tagmask,*tag_status);
_base_spe_context_unlock(spectx, FD_MFC);
return 0;
} else {
struct pollfd poll_fd;
int fd;
fd = _base_spe_open_if_closed(spectx, FD_MFC, 0);
if (fd == -1) {
return -1;
}
poll_fd.fd = fd;
poll_fd.events = POLLIN;
ret = poll(&poll_fd, 1, 0);
if (ret < 0)
return -1;
if (ret == 0 || !(poll_fd.revents & POLLIN)) {
*tag_status = 0;
return 0;
}
if (read(fd,tag_status,4) == 4) {
return 0;
}
}
return -1;
}
int _base_spe_mfcio_tag_status_read(spe_context_ptr_t spectx, unsigned int mask, unsigned int behavior, unsigned int *tag_status)
{
if ( mask != 0 ) {
if (!(spectx->base_private->flags & SPE_MAP_PS))
mask = 0;
} else {
if ((spectx->base_private->flags & SPE_MAP_PS))
mask = spectx->base_private->active_tagmask;
}
if (!tag_status) {
errno = EINVAL;
return -1;
}
switch (behavior) {
case SPE_TAG_ALL:
return spe_mfcio_tag_status_read_all(spectx, mask, tag_status);
case SPE_TAG_ANY:
return spe_mfcio_tag_status_read_any(spectx, mask, tag_status);
case SPE_TAG_IMMEDIATE:
return spe_mfcio_tag_status_read_immediate(spectx, mask, tag_status);
default:
errno = EINVAL;
return -1;
}
}
int _base_spe_mssync_start(spe_context_ptr_t spectx)
{
int ret, fd;
unsigned int data = 1; /* Any value can be written here */
volatile struct spe_mssync_area *mss_area =
spectx->base_private->mssync_mmap_base;
if (spectx->base_private->flags & SPE_MAP_PS) {
mss_area->MFC_MSSync = data;
return 0;
} else {
fd = _base_spe_open_if_closed(spectx, FD_MSS, 0);
if (fd != -1) {
ret = write(fd, &data, sizeof (data));
if ((ret < 0) && (errno != EIO)) {
perror("spe_mssync_start: internal error");
}
return ret < 0 ? -1 : 0;
} else
return -1;
}
}
int _base_spe_mssync_status(spe_context_ptr_t spectx)
{
int ret, fd;
unsigned int data;
volatile struct spe_mssync_area *mss_area =
spectx->base_private->mssync_mmap_base;
if (spectx->base_private->flags & SPE_MAP_PS) {
return mss_area->MFC_MSSync;
} else {
fd = _base_spe_open_if_closed(spectx, FD_MSS, 0);
if (fd != -1) {
ret = read(fd, &data, sizeof (data));
if ((ret < 0) && (errno != EIO)) {
perror("spe_mssync_start: internal error");
}
return ret < 0 ? -1 : data;
} else
return -1;
}
}