74 lines
1.7 KiB
C
74 lines
1.7 KiB
C
/*
|
|
* Edu PCI device.
|
|
*
|
|
* Copyright (C) 2016 Red Hat, Inc.
|
|
*
|
|
* Authors:
|
|
* Peter Xu <peterx@redhat.com>,
|
|
*
|
|
* This work is licensed under the terms of the GNU LGPL, version 2 or
|
|
* later.
|
|
*/
|
|
|
|
#include "pci-edu.h"
|
|
#include "asm/barrier.h"
|
|
|
|
/* Return true if alive */
|
|
static inline bool edu_check_alive(struct pci_edu_dev *dev)
|
|
{
|
|
static uint32_t live_count = 1;
|
|
uint32_t value;
|
|
|
|
edu_reg_writel(dev, EDU_REG_ALIVE, live_count++);
|
|
value = edu_reg_readl(dev, EDU_REG_ALIVE);
|
|
return (live_count - 1 == ~value);
|
|
}
|
|
|
|
bool edu_init(struct pci_edu_dev *dev)
|
|
{
|
|
pcidevaddr_t dev_addr;
|
|
|
|
dev_addr = pci_find_dev(PCI_VENDOR_ID_QEMU, PCI_DEVICE_ID_EDU);
|
|
if (dev_addr == PCIDEVADDR_INVALID)
|
|
return false;
|
|
|
|
pci_dev_init(&dev->pci_dev, dev_addr);
|
|
pci_enable_defaults(&dev->pci_dev);
|
|
dev->reg_base = ioremap(dev->pci_dev.resource[EDU_BAR], PAGE_SIZE);
|
|
assert(edu_check_alive(dev));
|
|
return true;
|
|
}
|
|
|
|
void edu_dma(struct pci_edu_dev *dev, iova_t iova,
|
|
size_t size, unsigned int dev_offset, bool from_device)
|
|
{
|
|
uint64_t from, to;
|
|
uint32_t cmd = EDU_CMD_DMA_START;
|
|
|
|
assert(size <= EDU_DMA_SIZE_MAX);
|
|
assert(dev_offset < EDU_DMA_SIZE_MAX);
|
|
|
|
printf("edu device DMA start %s addr %#" PRIx64 " size %lu off %#x\n",
|
|
from_device ? "FROM" : "TO",
|
|
iova, (ulong)size, dev_offset);
|
|
|
|
if (from_device) {
|
|
from = dev_offset + EDU_DMA_START;
|
|
to = iova;
|
|
cmd |= EDU_CMD_DMA_FROM;
|
|
} else {
|
|
from = iova;
|
|
to = EDU_DMA_START + dev_offset;
|
|
cmd |= EDU_CMD_DMA_TO;
|
|
}
|
|
|
|
edu_reg_writeq(dev, EDU_REG_DMA_SRC, from);
|
|
edu_reg_writeq(dev, EDU_REG_DMA_DST, to);
|
|
edu_reg_writeq(dev, EDU_REG_DMA_COUNT, size);
|
|
edu_reg_writel(dev, EDU_REG_DMA_CMD, cmd);
|
|
|
|
/* Wait until DMA finished */
|
|
while (edu_reg_readl(dev, EDU_REG_DMA_CMD) & EDU_CMD_DMA_START)
|
|
cpu_relax();
|
|
}
|