diff options
Diffstat (limited to 'dma/nwl.c')
-rw-r--r-- | dma/nwl.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/dma/nwl.c b/dma/nwl.c new file mode 100644 index 0000000..5a1a719 --- /dev/null +++ b/dma/nwl.c @@ -0,0 +1,178 @@ +#define _PCILIB_DMA_NWL_C + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "pci.h" +#include "pcilib.h" +#include "error.h" +#include "tools.h" +#include "nwl.h" + +/* Common DMA registers */ +#define REG_DMA_CTRL_STATUS 0x4000 /**< DMA Common Ctrl & Status */ + +/* These engine registers are applicable to both S2C and C2S channels. + * Register field mask and shift definitions are later in this file. + */ + +#define REG_DMA_ENG_CAP 0x00000000 /**< DMA Engine Capabilities */ +#define REG_DMA_ENG_CTRL_STATUS 0x00000004 /**< DMA Engine Control */ +#define REG_DMA_ENG_NEXT_BD 0x00000008 /**< HW Next desc pointer */ +#define REG_SW_NEXT_BD 0x0000000C /**< SW Next desc pointer */ +#define REG_DMA_ENG_LAST_BD 0x00000010 /**< HW Last completed pointer */ +#define REG_DMA_ENG_ACTIVE_TIME 0x00000014 /**< DMA Engine Active Time */ +#define REG_DMA_ENG_WAIT_TIME 0x00000018 /**< DMA Engine Wait Time */ +#define REG_DMA_ENG_COMP_BYTES 0x0000001C /**< DMA Engine Completed Bytes */ + +/* Register masks. The following constants define bit locations of various + * control bits in the registers. For further information on the meaning of + * the various bit masks, refer to the hardware spec. + * + * Masks have been written assuming HW bits 0-31 correspond to SW bits 0-31 + */ + +/** @name Bitmasks of REG_DMA_CTRL_STATUS register. + * @{ + */ +#define DMA_INT_ENABLE 0x00000001 /**< Enable global interrupts */ +#define DMA_INT_DISABLE 0x00000000 /**< Disable interrupts */ +#define DMA_INT_ACTIVE_MASK 0x00000002 /**< Interrupt active? */ +#define DMA_INT_PENDING_MASK 0x00000004 /**< Engine interrupt pending */ +#define DMA_INT_MSI_MODE 0x00000008 /**< MSI or Legacy mode? */ +#define DMA_USER_INT_ENABLE 0x00000010 /**< Enable user interrupts */ +#define DMA_USER_INT_ACTIVE_MASK 0x00000020 /**< Int - user interrupt */ +#define DMA_USER_INT_ACK 0x00000020 /**< Acknowledge */ +#define DMA_MPS_USED 0x00000700 /**< MPS Used */ +#define DMA_MRRS_USED 0x00007000 /**< MRRS Used */ +#define DMA_S2C_ENG_INT_VAL 0x00FF0000 /**< IRQ value of 1st 8 S2Cs */ +#define DMA_C2S_ENG_INT_VAL 0xFF000000 /**< IRQ value of 1st 8 C2Ss */ + +/** @name Bitmasks of REG_DMA_ENG_CAP register. + * @{ + */ +/* DMA engine characteristics */ +#define DMA_ENG_PRESENT_MASK 0x00000001 /**< DMA engine present? */ +#define DMA_ENG_DIRECTION_MASK 0x00000002 /**< DMA engine direction */ +#define DMA_ENG_C2S 0x00000002 /**< DMA engine - C2S */ +#define DMA_ENG_S2C 0x00000000 /**< DMA engine - S2C */ +#define DMA_ENG_TYPE_MASK 0x00000030 /**< DMA engine type */ +#define DMA_ENG_BLOCK 0x00000000 /**< DMA engine - Block type */ +#define DMA_ENG_PACKET 0x00000010 /**< DMA engine - Packet type */ +#define DMA_ENG_NUMBER 0x0000FF00 /**< DMA engine number */ +#define DMA_ENG_BD_MAX_BC 0x3F000000 /**< DMA engine max buffer size */ + +/* Shift constants for selected masks */ +#define DMA_ENG_NUMBER_SHIFT 8 +#define DMA_ENG_BD_MAX_BC_SHIFT 24 + +#define DMA_ENGINE_PER_SIZE 0x100 /**< Separation between engine regs */ +#define DMA_OFFSET 0 /**< Starting register offset */ + /**< Size of DMA engine reg space */ +#define DMA_SIZE (MAX_DMA_ENGINES * DMA_ENGINE_PER_SIZE) + +/* +pcilib_register_bank_description_t ipecamera_register_banks[] = { + { PCILIB_REGISTER_DMABANK0, PCILIB_BAR0, 128, PCILIB_DEFAULT_PROTOCOL, DMA_NWL_OFFSET, DMA_NWL_OFFSET, PCILIB_LITTLE_ENDIAN, 32, PCILIB_LITTLE_ENDIAN, "%lx", "dma", "NorthWest Logick DMA Engine" }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL } +}; + +pcilib_register_description_t dma_nwl_registers[] = { + {0, 0, 32, 0, PCILIB_REGISTER_R , PCILIB_REGISTER_DMABANK, "dma_capabilities", ""}, + {1, 0, 32, 0, PCILIB_REGISTER_RW, PCILIB_REGISTER_DMABANK, "dma_control", ""}, +}; +*/ + +typedef struct { + pcilib_dma_engine_description_t desc; + char *base_addr; +} pcilib_nwl_engine_description_t; + + +struct nwl_dma_s { + pcilib_t *pcilib; + + pcilib_register_bank_description_t *dma_bank; + char *base_addr; + + pcilib_nwl_engine_description_t engines[PCILIB_MAX_DMA_ENGINES + 1]; +}; + +#define nwl_read_register(var, ctx, base, reg) pcilib_datacpy(&var, base + reg, 4, 1, ctx->dma_bank->raw_endianess) +#define nwl_write_register(var, ctx, base, reg) pcilib_datacpy(base + reg, &var, 4, 1, ctx->dma_bank->raw_endianess) + +static int nwl_read_engine_config(nwl_dma_t *ctx, pcilib_nwl_engine_description_t *info, char *base) { + uint32_t val; + + nwl_read_register(val, ctx, base, REG_DMA_ENG_CAP); + + if ((val & DMA_ENG_PRESENT_MASK) == 0) return PCILIB_ERROR_NOTAVAILABLE; + + info->desc.addr = (val & DMA_ENG_NUMBER) >> DMA_ENG_NUMBER_SHIFT; + + switch (val & DMA_ENG_DIRECTION_MASK) { + case DMA_ENG_C2S: + info->desc.direction = PCILIB_DMA_FROM_DEVICE; + break; + default: + info->desc.direction = PCILIB_DMA_TO_DEVICE; + } + + switch (val & DMA_ENG_TYPE_MASK) { + case DMA_ENG_BLOCK: + info->desc.type = PCILIB_DMA_TYPE_BLOCK; + break; + case DMA_ENG_PACKET: + info->desc.type = PCILIB_DMA_TYPE_PACKET; + break; + default: + info->desc.type = PCILIB_DMA_TYPE_UNKNOWN; + } + + info->desc.addr_bits = (val & DMA_ENG_BD_MAX_BC) >> DMA_ENG_BD_MAX_BC_SHIFT; + + return 0; +} + +pcilib_dma_context_t *dma_nwl_init(pcilib_t *pcilib) { + int i; + int err; + pcilib_dma_t n_engines; + + pcilib_model_description_t *model_info = pcilib_get_model_description(pcilib); + + nwl_dma_t *ctx = malloc(sizeof(nwl_dma_t)); + if (ctx) { + memset(ctx, 0, sizeof(nwl_dma_t)); + ctx->pcilib = pcilib; + pcilib_register_bank_t dma_bank = pcilib_find_bank_by_addr(pcilib, PCILIB_REGISTER_BANK_DMA); + + if (dma_bank == PCILIB_REGISTER_BANK_INVALID) { + 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); + + for (i = 0, n_engines = 0; i < 2 * PCILIB_MAX_DMA_ENGINES; i++) { + char *addr = ctx->base_addr + DMA_OFFSET + i * DMA_ENGINE_PER_SIZE; + err = nwl_read_engine_config(ctx, ctx->engines + n_engines, addr); + if (!err) { + ctx->engines[n_engines].base_addr = addr; + pcilib_set_dma_engine_description(pcilib, n_engines, (pcilib_dma_engine_description_t*)(ctx->engines + n_engines)); + ++n_engines; + } + } + pcilib_set_dma_engine_description(pcilib, n_engines, NULL); + } + return (pcilib_dma_context_t*)ctx; +} + +void dma_nwl_free(pcilib_dma_context_t *vctx) { + nwl_dma_t *ctx = (nwl_dma_t*)vctx; + if (ctx) { + free(ctx); + } +} |