1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
|
#define _PCILIB_DMA_NWL_C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.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
/** @name Bitmasks of REG_DMA_ENG_CTRL_STATUS register.
* @{
*/
/* Interrupt activity and acknowledgement bits */
#define DMA_ENG_INT_ENABLE 0x00000001 /**< Enable interrupts */
#define DMA_ENG_INT_DISABLE 0x00000000 /**< Disable interrupts */
#define DMA_ENG_INT_ACTIVE_MASK 0x00000002 /**< Interrupt active? */
#define DMA_ENG_INT_ACK 0x00000002 /**< Interrupt ack */
#define DMA_ENG_INT_BDCOMP 0x00000004 /**< Int - BD completion */
#define DMA_ENG_INT_BDCOMP_ACK 0x00000004 /**< Acknowledge */
#define DMA_ENG_INT_ALERR 0x00000008 /**< Int - BD align error */
#define DMA_ENG_INT_ALERR_ACK 0x00000008 /**< Acknowledge */
#define DMA_ENG_INT_FETERR 0x00000010 /**< Int - BD fetch error */
#define DMA_ENG_INT_FETERR_ACK 0x00000010 /**< Acknowledge */
#define DMA_ENG_INT_ABORTERR 0x00000020 /**< Int - DMA abort error */
#define DMA_ENG_INT_ABORTERR_ACK 0x00000020 /**< Acknowledge */
#define DMA_ENG_INT_CHAINEND 0x00000080 /**< Int - BD chain ended */
#define DMA_ENG_INT_CHAINEND_ACK 0x00000080 /**< Acknowledge */
/* DMA engine control */
#define DMA_ENG_ENABLE_MASK 0x00000100 /**< DMA enabled? */
#define DMA_ENG_ENABLE 0x00000100 /**< Enable DMA */
#define DMA_ENG_DISABLE 0x00000000 /**< Disable DMA */
#define DMA_ENG_STATE_MASK 0x00000C00 /**< Current DMA state? */
#define DMA_ENG_RUNNING 0x00000400 /**< DMA running */
#define DMA_ENG_IDLE 0x00000000 /**< DMA idle */
#define DMA_ENG_WAITING 0x00000800 /**< DMA waiting */
#define DMA_ENG_STATE_WAITED 0x00001000 /**< DMA waited earlier */
#define DMA_ENG_WAITED_ACK 0x00001000 /**< Acknowledge */
#define DMA_ENG_USER_RESET 0x00004000 /**< Reset only user logic */
#define DMA_ENG_RESET 0x00008000 /**< Reset DMA engine + user */
#define DMA_ENG_ALLINT_MASK 0x000000BE /**< To get only int events */
#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_dma_t n_engines;
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;
info->base_addr = base;
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;
}
static int nwl_stop_engine(nwl_dma_t *ctx, pcilib_dma_t dma) {
uint32_t val;
struct timeval start, cur;
pcilib_nwl_engine_description_t *info = ctx->engines + dma;
char *base = ctx->engines[dma].base_addr;
// Disable IRQ
nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
val &= ~(DMA_ENG_INT_ENABLE);
nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
// Reseting
val = DMA_ENG_DISABLE|DMA_ENG_USER_RESET; nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
gettimeofday(&start, NULL);
do {
nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
gettimeofday(&cur, NULL);
} while ((val & (DMA_ENG_STATE_MASK|DMA_ENG_USER_RESET))&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_REGISTER_TIMEOUT));
if (val & DMA_ENG_RESET) {
pcilib_error("Timeout during reset of DMA engine %i", info->desc.addr);
return PCILIB_ERROR_TIMEOUT;
}
val = DMA_ENG_RESET; nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
gettimeofday(&start, NULL);
do {
nwl_read_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
gettimeofday(&cur, NULL);
} while ((val & DMA_ENG_RESET)&&(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < PCILIB_REGISTER_TIMEOUT));
if (val & DMA_ENG_RESET) {
pcilib_error("Timeout during reset of DMA engine %i", info->desc.addr);
return PCILIB_ERROR_TIMEOUT;
}
// Acknowledge asserted engine interrupts
if (val & DMA_ENG_INT_ACTIVE_MASK) {
val |= DMA_ENG_ALLINT_MASK;
nwl_write_register(val, ctx, base, REG_DMA_ENG_CTRL_STATUS);
}
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) err = nwl_stop_engine(ctx, n_engines);
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);
ctx->n_engines = n_engines;
}
return (pcilib_dma_context_t*)ctx;
}
void dma_nwl_free(pcilib_dma_context_t *vctx) {
pcilib_dma_t i;
nwl_dma_t *ctx = (nwl_dma_t*)vctx;
if (ctx) {
for (i = 0; i < ctx->n_engines; i++) nwl_stop_engine(vctx, i);
free(ctx);
}
}
int dma_nwl_read(pcilib_dma_context_t *vctx, pcilib_dma_t dma, size_t size, void *buf) {
nwl_dma_t *ctx = (nwl_dma_t*)vctx;
printf("Reading dma: %i\n", dma);
}
|