summaryrefslogtreecommitdiffstats
path: root/dma/ipe.c
diff options
context:
space:
mode:
authorSuren A. Chilingaryan <csa@suren.me>2014-12-19 00:14:21 +0100
committerSuren A. Chilingaryan <csa@suren.me>2014-12-19 00:14:21 +0100
commita640c40c6bcf4bad0b78e0ea6ea04f2a5f3f877f (patch)
tree7a6b899c130510e3cca87c23b26a5a1ffc033f20 /dma/ipe.c
parent0e16eeef08ed9b27f0fbc7f02a562a81e10c4119 (diff)
downloadpcitool-a640c40c6bcf4bad0b78e0ea6ea04f2a5f3f877f.tar.gz
pcitool-a640c40c6bcf4bad0b78e0ea6ea04f2a5f3f877f.tar.bz2
pcitool-a640c40c6bcf4bad0b78e0ea6ea04f2a5f3f877f.tar.xz
pcitool-a640c40c6bcf4bad0b78e0ea6ea04f2a5f3f877f.zip
Initial implementation of IPEDMA, dummy driver for KAPTURE, start of API changes
Diffstat (limited to 'dma/ipe.c')
-rw-r--r--dma/ipe.c412
1 files changed, 412 insertions, 0 deletions
diff --git a/dma/ipe.c b/dma/ipe.c
new file mode 100644
index 0000000..eb1ad6d
--- /dev/null
+++ b/dma/ipe.c
@@ -0,0 +1,412 @@
+#define _PCILIB_DMA_IPE_C
+#define _BSD_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include "pci.h"
+#include "pcilib.h"
+#include "error.h"
+#include "tools.h"
+
+#include "ipe.h"
+#include "ipe_private.h"
+#include "ipe_registers.h"
+
+
+#define WR(addr, value) { *(uint32_t*)(ctx->base_addr + addr) = value; }
+#define RD(addr, value) { value = *(uint32_t*)(ctx->base_addr + addr); }
+
+
+pcilib_dma_context_t *dma_ipe_init(pcilib_t *pcilib, pcilib_dma_modification_t type, void *arg) {
+ int err = 0;
+
+ pcilib_model_description_t *model_info = pcilib_get_model_description(pcilib);
+
+ ipe_dma_t *ctx = malloc(sizeof(ipe_dma_t));
+
+ if (ctx) {
+ memset(ctx, 0, sizeof(ipe_dma_t));
+ ctx->pcilib = pcilib;
+
+ memset(ctx->engine, 0, 2 * sizeof(pcilib_dma_engine_description_t));
+ ctx->engine[0].addr = 0;
+ ctx->engine[0].type = PCILIB_DMA_TYPE_PACKET;
+ ctx->engine[0].direction = PCILIB_DMA_FROM_DEVICE;
+ ctx->engine[0].addr_bits = 32;
+ pcilib_set_dma_engine_description(pcilib, 0, &ctx->engine[0]);
+ pcilib_set_dma_engine_description(pcilib, 1, NULL);
+
+ pcilib_register_bank_t dma_bank = pcilib_find_bank_by_addr(pcilib, PCILIB_REGISTER_BANK_DMA);
+ if (dma_bank == PCILIB_REGISTER_BANK_INVALID) {
+ free(ctx);
+ pcilib_error("DMA Register Bank could not be found");
+ return NULL;
+ }
+
+ ctx->dma_bank = model_info->banks + dma_bank;
+ ctx->base_addr = pcilib_resolve_register_address(pcilib, ctx->dma_bank->bar, ctx->dma_bank->read_addr);
+
+ err = pcilib_add_registers(ctx->pcilib, 0, ipe_dma_registers);
+ if (err) {
+ free(ctx);
+ pcilib_error("Error adding DMA registers");
+ return NULL;
+ }
+ }
+
+ return (pcilib_dma_context_t*)ctx;
+}
+
+void dma_ipe_free(pcilib_dma_context_t *vctx) {
+ ipe_dma_t *ctx = (ipe_dma_t*)vctx;
+
+ if (ctx) {
+ dma_ipe_stop(vctx, PCILIB_DMA_ENGINE_ALL, PCILIB_DMA_FLAGS_DEFAULT);
+ free(ctx);
+ }
+}
+
+
+int dma_ipe_start(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma_flags_t flags) {
+ size_t i;
+
+ ipe_dma_t *ctx = (ipe_dma_t*)vctx;
+
+ int preserve = 0;
+ pcilib_kmem_flags_t kflags;
+ pcilib_kmem_reuse_state_t reuse_desc, reuse_pages;
+
+ volatile void *desc_va;
+ volatile uint32_t *last_written_addr_ptr;
+
+ pcilib_register_value_t value;
+
+ if (dma == PCILIB_DMA_ENGINE_INVALID) return 0;
+ else if (dma > 1) return PCILIB_ERROR_INVALID_BANK;
+
+ if (!ctx->started) ctx->started = 1;
+
+ if (flags&PCILIB_DMA_FLAG_PERSISTENT) ctx->preserve = 1;
+
+ if (ctx->pages) return 0;
+
+ kflags = PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_EXCLUSIVE|PCILIB_KMEM_FLAG_HARDWARE|(ctx->preserve?PCILIB_KMEM_FLAG_PERSISTENT:0);
+ pcilib_kmem_handle_t *desc = pcilib_alloc_kernel_memory(ctx->pcilib, PCILIB_KMEM_TYPE_CONSISTENT, 1, IPEDMA_DESCRIPTOR_SIZE, IPEDMA_DESCRIPTOR_ALIGNMENT, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_RING, 0x00), kflags);
+ pcilib_kmem_handle_t *pages = pcilib_alloc_kernel_memory(ctx->pcilib, PCILIB_KMEM_TYPE_DMA_C2S_PAGE, IPEDMA_DMA_PAGES, 0, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_PAGES, 0x00), kflags);
+
+ if (!desc||!pages) {
+ if (pages) pcilib_free_kernel_memory(ctx->pcilib, pages, 0);
+ if (desc) pcilib_free_kernel_memory(ctx->pcilib, desc, 0);
+ return PCILIB_ERROR_MEMORY;
+ }
+ reuse_desc = pcilib_kmem_is_reused(ctx->pcilib, desc);
+ reuse_pages = pcilib_kmem_is_reused(ctx->pcilib, pages);
+
+ if (reuse_desc == reuse_pages) {
+ if (reuse_desc & PCILIB_KMEM_REUSE_PARTIAL) pcilib_warning("Inconsistent DMA buffers are found (only part of required buffers is available), reinitializing...");
+ else if (reuse_desc & PCILIB_KMEM_REUSE_REUSED) {
+ if ((reuse_desc & PCILIB_KMEM_REUSE_PERSISTENT) == 0) pcilib_warning("Lost DMA buffers are found (non-persistent mode), reinitializing...");
+ else if ((reuse_desc & PCILIB_KMEM_REUSE_HARDWARE) == 0) pcilib_warning("Lost DMA buffers are found (missing HW reference), reinitializing...");
+ else {
+#ifndef IPEDMA_BUG_DMARD
+ RD(IPEDMA_REG_PAGE_COUNT, value);
+
+ if ((value + 1) != IPEDMA_DMA_PAGES) pcilib_warning("Inconsistent DMA buffers are found (Number of allocated buffers does not match current request), reinitializing...");
+ else
+#endif /* IPEDMA_BUG_DMARD */
+ preserve = 1;
+ }
+ }
+ } else pcilib_warning("Inconsistent DMA buffers (modes of ring and page buffers does not match), reinitializing....");
+
+ desc_va = pcilib_kmem_get_ua(ctx->pcilib, desc);
+ if (ctx->mode64) last_written_addr_ptr = desc_va + 3 * sizeof(uint32_t);
+ else last_written_addr_ptr = desc_va + 4 * sizeof(uint32_t);
+
+ if (preserve) {
+ ctx->reused = 1;
+ ctx->preserve = 1;
+
+
+// usleep(100000);
+
+ // Detect the current state of DMA engine
+#ifdef IPEDMA_BUG_DMARD
+ FILE *f = fopen("/tmp/pcitool_lastread", "r");
+ if (!f) pcilib_error("Can't read current status");
+ fread(&value, 1, sizeof(pcilib_register_value_t), f);
+ fclose(f);
+#else /* IPEDMA_BUG_DMARD */
+ RD(IPEDMA_REG_LAST_READ, value);
+ if (value == IPEDMA_DMA_PAGES) value = 0;
+#endif /* IPEDMA_BUG_DMARD */
+
+ ctx->last_read = value;
+ } else {
+ ctx->reused = 0;
+
+ // Disable DMA
+ WR(IPEDMA_REG_CONTROL, 0x0);
+
+ // Reset DMA engine
+ WR(IPEDMA_REG_RESET, 0x1);
+ usleep(100000);
+ WR(IPEDMA_REG_RESET, 0x0);
+
+#ifndef IPEDMA_BUG_DMARD
+ // Verify PCIe link status
+ RD(IPEDMA_REG_RESET, value);
+ if (value != 0x14031700) pcilib_warning("PCIe is not ready");
+#endif /* IPEDMA_BUG_DMARD */
+
+ // Configuring TLP and PACKET sizes (40 bit mode can be used with big pre-allocated buffers later)
+ WR(IPEDMA_REG_TLP_SIZE, IPEDMA_TLP_SIZE);
+ WR(IPEDMA_REG_TLP_COUNT, IPEDMA_PAGE_SIZE / (4 * IPEDMA_TLP_SIZE * IPEDMA_CORES));
+
+ // Setting progress register threshold
+ WR(IPEDMA_REG_UPDATE_THRESHOLD, IPEDMA_DMA_PROGRESS_THRESHOLD);
+
+ // Reseting configured DMA pages
+ WR(IPEDMA_REG_PAGE_COUNT, 0);
+
+ // Setting current read position and configuring progress register
+ WR(IPEDMA_REG_LAST_READ, IPEDMA_DMA_PAGES - 2 + 1);
+ WR(IPEDMA_REG_UPDATE_ADDR, pcilib_kmem_get_block_ba(ctx->pcilib, desc, 0));
+
+ // Instructing DMA engine that writting should start from the first DMA page
+ *last_written_addr_ptr = 0;//htonl(pcilib_kmem_get_block_ba(ctx->pcilib, pages, IPEDMA_DMA_PAGES - 1));
+
+
+ for (i = 0; i < IPEDMA_DMA_PAGES; i++) {
+ uintptr_t bus_addr = pcilib_kmem_get_block_ba(ctx->pcilib, pages, i);
+ WR(IPEDMA_REG_PAGE_ADDR, bus_addr);
+ if (bus_addr%4096) printf("Bad address %lu: %lx\n", i, bus_addr);
+ usleep(1000);
+ }
+
+ // Enable DMA
+ WR(IPEDMA_REG_CONTROL, 0x1);
+
+ ctx->last_read = IPEDMA_DMA_PAGES - 1;
+
+#ifdef IPEDMA_BUG_DMARD
+ FILE *f = fopen("/tmp/pcitool_lastread", "w");
+ if (!f) pcilib_error("Can't write current status");
+ value = ctx->last_read;
+ fwrite(&value, 1, sizeof(pcilib_register_value_t), f);
+ fclose(f);
+#endif /* IPEDMA_BUG_DMARD */
+ }
+
+ ctx->last_read_addr = htonl(pcilib_kmem_get_block_ba(ctx->pcilib, pages, ctx->last_read));
+
+
+ ctx->desc = desc;
+ ctx->pages = pages;
+ ctx->page_size = pcilib_kmem_get_block_size(ctx->pcilib, pages, 0);;
+ ctx->ring_size = IPEDMA_DMA_PAGES;
+
+ return 0;
+}
+
+int dma_ipe_stop(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma_flags_t flags) {
+ pcilib_kmem_flags_t kflags;
+
+ ipe_dma_t *ctx = (ipe_dma_t*)vctx;
+
+ if (!ctx->started) return 0;
+
+ if ((dma != PCILIB_DMA_ENGINE_INVALID)&&(dma > 1)) return PCILIB_ERROR_INVALID_BANK;
+
+ // ignoring previous setting if flag specified
+ if (flags&PCILIB_DMA_FLAG_PERSISTENT) {
+ ctx->preserve = 0;
+ }
+
+ if (ctx->preserve) {
+ kflags = PCILIB_KMEM_FLAG_REUSE;
+ } else {
+ kflags = PCILIB_KMEM_FLAG_HARDWARE|PCILIB_KMEM_FLAG_PERSISTENT;
+
+ ctx->started = 0;
+
+ // Disable DMA
+ WR(IPEDMA_REG_CONTROL, 0);
+
+ // Reset DMA engine
+ WR(IPEDMA_REG_RESET, 0x1);
+ usleep(100000);
+ WR(IPEDMA_REG_RESET, 0x0);
+
+ // Reseting configured DMA pages
+ WR(IPEDMA_REG_PAGE_COUNT, 0);
+ }
+
+ // Clean buffers
+ if (ctx->desc) {
+ pcilib_free_kernel_memory(ctx->pcilib, ctx->desc, kflags);
+ ctx->desc = NULL;
+ }
+
+ if (ctx->pages) {
+ pcilib_free_kernel_memory(ctx->pcilib, ctx->pages, kflags);
+ ctx->pages = NULL;
+ }
+
+ return 0;
+}
+
+
+int dma_ipe_get_status(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, pcilib_dma_engine_status_t *status, size_t n_buffers, pcilib_dma_buffer_status_t *buffers) {
+ size_t i;
+ ipe_dma_t *ctx = (ipe_dma_t*)vctx;
+
+ void *desc_va = (void*)pcilib_kmem_get_ua(ctx->pcilib, ctx->desc);
+ uint32_t *last_written_addr_ptr;
+ uint32_t last_written_addr;
+
+
+ if (!status) return -1;
+
+ if (ctx->mode64) last_written_addr_ptr = desc_va + 3 * sizeof(uint32_t);
+ else last_written_addr_ptr = desc_va + 4 * sizeof(uint32_t);
+
+ last_written_addr = ntohl(*last_written_addr_ptr);
+
+ status->started = ctx->started;
+ status->ring_size = ctx->ring_size;
+ status->buffer_size = ctx->page_size;
+
+ status->ring_tail = ctx->last_read + 1;
+ if (status->ring_tail == status->ring_size) status->ring_tail = 0;
+
+ // Find where the ring head is actually are
+ for (i = 0; i < ctx->ring_size; i++) {
+ uintptr_t bus_addr = pcilib_kmem_get_block_ba(ctx->pcilib, ctx->pages, i);
+
+ if (bus_addr == last_written_addr) {
+ status->ring_head = bus_addr;
+ break;
+ }
+ }
+
+ if (i == ctx->ring_size) {
+ // ERROR
+ }
+
+ if (n_buffers > ctx->ring_size) n_buffers = ctx->ring_size;
+
+ memset(buffers, 0, n_buffers * sizeof(pcilib_dma_engine_status_t));
+
+ if (status->ring_head > status->ring_tail) {
+ for (i = status->ring_tail; i <= status->ring_head; i++) {
+ buffers[i].used = 1;
+ buffers[i].size = ctx->page_size;
+ buffers[i].first = 1;
+ buffers[i].last = 1;
+ }
+ } else {
+ for (i = 0; i <= status->ring_tail; i++) {
+ buffers[i].used = 1;
+ buffers[i].size = ctx->page_size;
+ buffers[i].first = 1;
+ buffers[i].last = 1;
+ }
+
+ for (i = status->ring_head; i < status->ring_size; i++) {
+ buffers[i].used = 1;
+ buffers[i].size = ctx->page_size;
+ buffers[i].first = 1;
+ buffers[i].last = 1;
+ }
+ }
+
+ return 0;
+}
+
+int dma_ipe_stream_read(pcilib_dma_context_t *vctx, pcilib_dma_engine_t dma, uintptr_t addr, size_t size, pcilib_dma_flags_t flags, pcilib_timeout_t timeout, pcilib_dma_callback_t cb, void *cbattr) {
+ int err, ret = PCILIB_STREAMING_REQ_PACKET;
+
+ pcilib_register_value_t value;
+
+ pcilib_timeout_t wait = 0;
+ struct timeval start, cur;
+
+ volatile void *desc_va;
+ volatile uint32_t *last_written_addr_ptr;
+
+ size_t cur_read;
+
+ ipe_dma_t *ctx = (ipe_dma_t*)vctx;
+
+ err = dma_ipe_start(vctx, dma, PCILIB_DMA_FLAGS_DEFAULT);
+ if (err) return err;
+
+ desc_va = (void*)pcilib_kmem_get_ua(ctx->pcilib, ctx->desc);
+
+ if (ctx->mode64) last_written_addr_ptr = desc_va + 3 * sizeof(uint32_t);
+ else last_written_addr_ptr = desc_va + 4 * sizeof(uint32_t);
+
+ do {
+ switch (ret&PCILIB_STREAMING_TIMEOUT_MASK) {
+ case PCILIB_STREAMING_CONTINUE: wait = PCILIB_DMA_TIMEOUT; break;
+ case PCILIB_STREAMING_WAIT: wait = timeout; break;
+// case PCILIB_STREAMING_CHECK: wait = 0; break;
+ }
+
+#ifdef IPEDMA_DEBUG
+ printf("Waiting for data: %u (last read) 0x%x (last read addr) 0x%x (last_written)\n", ctx->last_read, ctx->last_read_addr, *last_written_addr_ptr);
+#endif /* IPEDMA_DEBUG */
+
+ gettimeofday(&start, NULL);
+ while (((*last_written_addr_ptr == 0)||(ctx->last_read_addr == (*last_written_addr_ptr)))&&((wait == PCILIB_TIMEOUT_INFINITE)||(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < wait))) {
+ usleep(10);
+ gettimeofday(&cur, NULL);
+ }
+
+ // Failing out if we exited on timeout
+ if ((ctx->last_read_addr == (*last_written_addr_ptr))||(*last_written_addr_ptr == 0))
+ return (ret&PCILIB_STREAMING_FAIL)?PCILIB_ERROR_TIMEOUT:0;
+
+ // Getting next page to read
+ cur_read = ctx->last_read + 1;
+ if (cur_read == ctx->ring_size) cur_read = 0;
+
+#ifdef IPEDMA_DEBUG
+ printf("Reading: %u (last read) 0x%x (last read addr) 0x%x (last_written)\n", cur_read, ctx->last_read_addr, *last_written_addr_ptr);
+#endif /* IPEDMA_DEBUG */
+
+ pcilib_kmem_sync_block(ctx->pcilib, ctx->pages, PCILIB_KMEM_SYNC_FROMDEVICE, cur_read);
+ void *buf = pcilib_kmem_get_block_ua(ctx->pcilib, ctx->pages, cur_read);
+ ret = cb(cbattr, PCILIB_DMA_FLAG_EOP, ctx->page_size, buf);
+ if (ret < 0) return -ret;
+
+// DS: Fixme, it looks like we can avoid calling this for the sake of performance
+// pcilib_kmem_sync_block(ctx->pcilib, ctx->pages, PCILIB_KMEM_SYNC_TODEVICE, cur_read);
+
+ WR(IPEDMA_REG_LAST_READ, ctx->last_read + 1);
+
+ ctx->last_read = cur_read;
+ ctx->last_read_addr = htonl(pcilib_kmem_get_block_ba(ctx->pcilib, ctx->pages, cur_read));
+
+#ifdef IPEDMA_BUG_DMARD
+ FILE *f = fopen("/tmp/pcitool_lastread", "w");
+ if (!f) pcilib_error("Can't write current status");
+ value = cur_read;
+ fwrite(&value, 1, sizeof(pcilib_register_value_t), f);
+ fclose(f);
+#endif /* IPEDMA_BUG_DMARD */
+
+ } while (ret);
+
+ return 0;
+}
+
+double dma_ipe_benchmark(pcilib_dma_context_t *vctx, pcilib_dma_engine_addr_t dma, uintptr_t addr, size_t size, size_t iterations, pcilib_dma_direction_t direction) {
+ return 0;
+}