nwl_engine_buffers.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. #define NWL_RING_GET(data, offset) *(uint32_t*)(((char*)(data)) + (offset))
  2. #define NWL_RING_SET(data, offset, val) *(uint32_t*)(((char*)(data)) + (offset)) = (val)
  3. #define NWL_RING_UPDATE(data, offset, mask, val) *(uint32_t*)(((char*)(data)) + (offset)) = ((*(uint32_t*)(((char*)(data)) + (offset)))&(mask))|(val)
  4. static int dma_nwl_compute_read_s2c_pointers(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx, unsigned char *ring, uint32_t ring_pa) {
  5. uint32_t val;
  6. const char *base = ectx->base_addr;
  7. nwl_read_register(val, ctx, base, REG_SW_NEXT_BD);
  8. if ((val < ring_pa)||((val - ring_pa) % PCILIB_NWL_DMA_DESCRIPTOR_SIZE)) {
  9. if (val < ring_pa) pcilib_warning("Inconsistent S2C DMA Ring buffer is found (REG_SW_NEXT_BD register value (%lx) is below start of ring [%lx,%lx])", val, ring_pa, PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  10. else pcilib_warning("Inconsistent S2C DMA Ring buffer is found (REG_SW_NEXT_BD register value (%zu / %u) is fractal)", val - ring_pa, PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  11. return PCILIB_ERROR_INVALID_STATE;
  12. }
  13. ectx->head = (val - ring_pa) / PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  14. if (ectx->head >= PCILIB_NWL_DMA_PAGES) {
  15. pcilib_warning("Inconsistent S2C DMA Ring buffer is found (REG_SW_NEXT_BD register value (%zu) out of range)", ectx->head);
  16. return PCILIB_ERROR_INVALID_STATE;
  17. }
  18. nwl_read_register(val, ctx, base, REG_DMA_ENG_NEXT_BD);
  19. if ((val < ring_pa)||((val - ring_pa) % PCILIB_NWL_DMA_DESCRIPTOR_SIZE)) {
  20. if (val < ring_pa) pcilib_warning("Inconsistent S2C DMA Ring buffer is found (REG_DMA_ENG_NEXT_BD register value (%lx) is below start of ring [%lx,%lx])", val, ring_pa, PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  21. else pcilib_warning("Inconsistent S2C DMA Ring buffer is found (REG_DMA_ENG_NEXT_BD register value (%zu / %u) is fractal)", val - ring_pa, PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  22. return PCILIB_ERROR_INVALID_STATE;
  23. }
  24. ectx->tail = (val - ring_pa) / PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  25. if (ectx->tail >= PCILIB_NWL_DMA_PAGES) {
  26. pcilib_warning("Inconsistent S2C DMA Ring buffer is found (REG_DMA_ENG_NEXT_BD register value (%zu) out of range)", ectx->tail);
  27. return PCILIB_ERROR_INVALID_STATE;
  28. }
  29. pcilib_debug(DMA, "S2C: %lu %lu\n", ectx->tail, ectx->head);
  30. return 0;
  31. }
  32. static int dma_nwl_compute_read_c2s_pointers(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx, unsigned char *ring, uint32_t ring_pa) {
  33. uint32_t val;
  34. const char *base = ectx->base_addr;
  35. nwl_read_register(val, ctx, base, REG_SW_NEXT_BD);
  36. if ((val < ring_pa)||((val - ring_pa) % PCILIB_NWL_DMA_DESCRIPTOR_SIZE)) {
  37. if (val < ring_pa) pcilib_warning("Inconsistent C2S DMA Ring buffer is found (REG_SW_NEXT_BD register value (%lx) is below start of the ring [%lx,%lx])", val, ring_pa, PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  38. else pcilib_warning("Inconsistent C2S DMA Ring buffer is found (REG_SW_NEXT_BD register value (%zu / %u) is fractal)", val - ring_pa, PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  39. return PCILIB_ERROR_INVALID_STATE;
  40. }
  41. ectx->head = (val - ring_pa) / PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  42. if (ectx->head >= PCILIB_NWL_DMA_PAGES) {
  43. pcilib_warning("Inconsistent C2S DMA Ring buffer is found (REG_SW_NEXT_BD register value (%zu) out of range)", ectx->head);
  44. return PCILIB_ERROR_INVALID_STATE;
  45. }
  46. ectx->tail = ectx->head + 1;
  47. if (ectx->tail == PCILIB_NWL_DMA_PAGES) ectx->tail = 0;
  48. pcilib_debug(DMA, "C2S: %lu %lu\n", ectx->tail, ectx->head);
  49. return 0;
  50. }
  51. static int dma_nwl_allocate_engine_buffers(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx) {
  52. int err = 0;
  53. int i;
  54. int preserve = 0;
  55. uint16_t sub_use;
  56. uint32_t val;
  57. uint32_t buf_sz;
  58. uint64_t buf_pa;
  59. pcilib_kmem_reuse_state_t reuse_ring, reuse_pages;
  60. pcilib_kmem_flags_t flags;
  61. pcilib_kmem_type_t type;
  62. char *base = ectx->base_addr;
  63. if (ectx->pages) return 0;
  64. // Or bidirectional specified by 0x0|addr, or read 0x0|addr and write 0x80|addr
  65. type = (ectx->desc->direction == PCILIB_DMA_TO_DEVICE)?PCILIB_KMEM_TYPE_DMA_S2C_PAGE:PCILIB_KMEM_TYPE_DMA_C2S_PAGE;
  66. sub_use = ectx->desc->addr|((ectx->desc->direction == PCILIB_DMA_TO_DEVICE)?0x80:0x00);
  67. flags = PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_EXCLUSIVE|PCILIB_KMEM_FLAG_HARDWARE|(ectx->preserve?PCILIB_KMEM_FLAG_PERSISTENT:0);
  68. pcilib_kmem_handle_t *ring = pcilib_alloc_kernel_memory(ctx->dmactx.pcilib, PCILIB_KMEM_TYPE_CONSISTENT, 1, PCILIB_NWL_DMA_PAGES * PCILIB_NWL_DMA_DESCRIPTOR_SIZE, PCILIB_NWL_ALIGNMENT, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_RING, sub_use), flags);
  69. pcilib_kmem_handle_t *pages = pcilib_alloc_kernel_memory(ctx->dmactx.pcilib, type, PCILIB_NWL_DMA_PAGES, 0, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_DMA_PAGES, sub_use), flags);
  70. if (!ring||!pages) {
  71. if (pages) pcilib_free_kernel_memory(ctx->dmactx.pcilib, pages, 0);
  72. if (ring) pcilib_free_kernel_memory(ctx->dmactx.pcilib, ring, 0);
  73. return PCILIB_ERROR_MEMORY;
  74. }
  75. reuse_ring = pcilib_kmem_is_reused(ctx->dmactx.pcilib, ring);
  76. reuse_pages = pcilib_kmem_is_reused(ctx->dmactx.pcilib, pages);
  77. // I guess idea here was that we not need to check all that stuff during the second iteration
  78. // which is basicaly true (shall we expect any driver-triggered changes or parallel accesses?)
  79. // but still we need to set preserve flag (and that if we enforcing preservation --start-dma).
  80. // Probably having checks anyway is not harming...
  81. // if (!ectx->preserve) {
  82. if (reuse_ring == reuse_pages) {
  83. if (reuse_ring & PCILIB_KMEM_REUSE_PARTIAL) pcilib_warning("Inconsistent DMA buffers are found (only part of required buffers is available), reinitializing...");
  84. else if (reuse_ring & PCILIB_KMEM_REUSE_REUSED) {
  85. if ((reuse_ring & PCILIB_KMEM_REUSE_PERSISTENT) == 0) pcilib_warning("Lost DMA buffers are found (non-persistent mode), reinitializing...");
  86. else if ((reuse_ring & PCILIB_KMEM_REUSE_HARDWARE) == 0) pcilib_warning("Lost DMA buffers are found (missing HW reference), reinitializing...");
  87. else {
  88. nwl_read_register(val, ctx, ectx->base_addr, REG_DMA_ENG_CTRL_STATUS);
  89. if ((val&DMA_ENG_RUNNING) == 0) pcilib_warning("Lost DMA buffers are found (DMA engine is stopped), reinitializing...");
  90. else preserve = 1;
  91. }
  92. }
  93. } else pcilib_warning("Inconsistent DMA buffers (modes of ring and page buffers does not match), reinitializing....");
  94. // }
  95. unsigned char *data = (unsigned char*)pcilib_kmem_get_ua(ctx->dmactx.pcilib, ring);
  96. uint32_t ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ring);
  97. if (preserve) {
  98. if (ectx->desc->direction == PCILIB_DMA_FROM_DEVICE) err = dma_nwl_compute_read_c2s_pointers(ctx, ectx, data, ring_pa);
  99. else err = dma_nwl_compute_read_s2c_pointers(ctx, ectx, data, ring_pa);
  100. if (err) preserve = 0;
  101. }
  102. if (preserve) {
  103. ectx->reused = 1;
  104. buf_sz = pcilib_kmem_get_block_size(ctx->dmactx.pcilib, pages, 0);
  105. } else {
  106. ectx->reused = 0;
  107. memset(data, 0, PCILIB_NWL_DMA_PAGES * PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  108. for (i = 0; i < PCILIB_NWL_DMA_PAGES; i++, data += PCILIB_NWL_DMA_DESCRIPTOR_SIZE) {
  109. buf_pa = pcilib_kmem_get_block_pa(ctx->dmactx.pcilib, pages, i);
  110. buf_sz = pcilib_kmem_get_block_size(ctx->dmactx.pcilib, pages, i);
  111. NWL_RING_SET(data, DMA_BD_NDESC_OFFSET, ring_pa + ((i + 1) % PCILIB_NWL_DMA_PAGES) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE);
  112. NWL_RING_SET(data, DMA_BD_BUFAL_OFFSET, buf_pa&0xFFFFFFFF);
  113. NWL_RING_SET(data, DMA_BD_BUFAH_OFFSET, buf_pa>>32);
  114. #ifdef NWL_GENERATE_DMA_IRQ
  115. NWL_RING_SET(data, DMA_BD_BUFL_CTRL_OFFSET, buf_sz | DMA_BD_INT_ERROR_MASK | DMA_BD_INT_COMP_MASK);
  116. #else /* NWL_GENERATE_DMA_IRQ */
  117. NWL_RING_SET(data, DMA_BD_BUFL_CTRL_OFFSET, buf_sz);
  118. #endif /* NWL_GENERATE_DMA_IRQ */
  119. }
  120. val = ring_pa;
  121. nwl_write_register(val, ctx, base, REG_DMA_ENG_NEXT_BD);
  122. nwl_write_register(val, ctx, base, REG_SW_NEXT_BD);
  123. ectx->head = 0;
  124. ectx->tail = 0;
  125. }
  126. ectx->ring = ring;
  127. ectx->pages = pages;
  128. ectx->page_size = buf_sz;
  129. ectx->ring_size = PCILIB_NWL_DMA_PAGES;
  130. return 0;
  131. }
  132. static size_t dma_nwl_clean_buffers(nwl_dma_t * ctx, pcilib_nwl_engine_context_t *ectx) {
  133. size_t res = 0;
  134. uint32_t status;
  135. volatile unsigned char *ring = pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
  136. ring += ectx->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  137. next_buffer:
  138. status = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET)&DMA_BD_STATUS_MASK;
  139. // control = NWL_RING_GET(ring, DMA_BD_BUFL_CTRL_OFFSET)&DMA_BD_CTRL_MASK;
  140. if (status & DMA_BD_ERROR_MASK) {
  141. pcilib_error("NWL DMA Engine reported error in ring descriptor");
  142. return (size_t)-1;
  143. }
  144. if (status & DMA_BD_SHORT_MASK) {
  145. pcilib_error("NWL DMA Engine reported short error");
  146. return (size_t)-1;
  147. }
  148. if (status & DMA_BD_COMP_MASK) {
  149. ectx->tail++;
  150. if (ectx->tail == ectx->ring_size) {
  151. ring -= (ectx->tail - 1) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  152. ectx->tail = 0;
  153. } else {
  154. ring += PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  155. }
  156. res++;
  157. if (ectx->tail != ectx->head) goto next_buffer;
  158. }
  159. // printf("====> Cleaned: %i\n", res);
  160. return res;
  161. }
  162. static size_t dma_nwl_get_next_buffer(nwl_dma_t * ctx, pcilib_nwl_engine_context_t *ectx, size_t n_buffers, pcilib_timeout_t timeout) {
  163. struct timeval start, cur;
  164. size_t res, n = 0;
  165. size_t head;
  166. for (head = ectx->head; (((head + 1)%ectx->ring_size) != ectx->tail)&&(n < n_buffers); head++, n++);
  167. if (n == n_buffers) return ectx->head;
  168. gettimeofday(&start, NULL);
  169. res = dma_nwl_clean_buffers(ctx, ectx);
  170. if (res == (size_t)-1) return PCILIB_DMA_BUFFER_INVALID;
  171. else n += res;
  172. while (n < n_buffers) {
  173. if (timeout != PCILIB_TIMEOUT_INFINITE) {
  174. gettimeofday(&cur, NULL);
  175. if (((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) > timeout) break;
  176. }
  177. usleep (10);
  178. res = dma_nwl_clean_buffers(ctx, ectx);
  179. if (res == (size_t)-1) return PCILIB_DMA_BUFFER_INVALID;
  180. else if (res > 0) {
  181. gettimeofday(&start, NULL);
  182. n += res;
  183. }
  184. }
  185. if (n < n_buffers) return PCILIB_DMA_BUFFER_INVALID;
  186. return ectx->head;
  187. }
  188. static int dma_nwl_push_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx, size_t size, int eop, pcilib_timeout_t timeout) {
  189. int flags = 0;
  190. uint32_t val;
  191. volatile unsigned char *ring = pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
  192. uint32_t ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ectx->ring);
  193. ring += ectx->head * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  194. if (!ectx->writting) {
  195. flags |= DMA_BD_SOP_MASK;
  196. ectx->writting = 1;
  197. }
  198. if (eop) {
  199. flags |= DMA_BD_EOP_MASK;
  200. ectx->writting = 0;
  201. }
  202. NWL_RING_SET(ring, DMA_BD_BUFL_CTRL_OFFSET, size|flags);
  203. NWL_RING_SET(ring, DMA_BD_BUFL_STATUS_OFFSET, size);
  204. ectx->head++;
  205. if (ectx->head == ectx->ring_size) ectx->head = 0;
  206. val = ring_pa + ectx->head * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  207. nwl_write_register(val, ctx, ectx->base_addr, REG_SW_NEXT_BD);
  208. return 0;
  209. }
  210. static size_t dma_nwl_wait_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx, size_t *size, int *eop, pcilib_timeout_t timeout) {
  211. struct timeval start, cur;
  212. uint32_t status_size, status;
  213. volatile unsigned char *ring = pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
  214. ring += ectx->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  215. gettimeofday(&start, NULL);
  216. do {
  217. status_size = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET);
  218. status = status_size & DMA_BD_STATUS_MASK;
  219. if (status & DMA_BD_ERROR_MASK) {
  220. pcilib_error("NWL DMA Engine reported error in ring descriptor");
  221. return (size_t)-1;
  222. }
  223. if (status & DMA_BD_COMP_MASK) {
  224. if (status & DMA_BD_EOP_MASK) *eop = 1;
  225. else *eop = 0;
  226. *size = status_size & DMA_BD_BUFL_MASK;
  227. /*
  228. if (mrd) {
  229. if ((ectx->tail + 1) == ectx->ring_size) ring -= ectx->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  230. else ring += PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  231. *mrd = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET)&DMA_BD_COMP_MASK;
  232. }
  233. */
  234. return ectx->tail;
  235. }
  236. usleep(10);
  237. gettimeofday(&cur, NULL);
  238. } while ((timeout == PCILIB_TIMEOUT_INFINITE)||(((cur.tv_sec - start.tv_sec)*1000000 + (cur.tv_usec - start.tv_usec)) < timeout));
  239. return (size_t)-1;
  240. }
  241. /*
  242. // This function is not used now, but we may need it in the future
  243. static int dma_nwl_is_overflown(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx) {
  244. uint32_t status;
  245. unsigned char *ring = pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
  246. if (ectx->tail > 0) ring += (ectx->tail - 1) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  247. else ring += (ectx->ring_size - 1) * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  248. status = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET);
  249. return status&DMA_BD_COMP_MASK?1:0;
  250. }
  251. */
  252. static int dma_nwl_return_buffer(nwl_dma_t *ctx, pcilib_nwl_engine_context_t *ectx) {
  253. uint32_t val;
  254. volatile unsigned char *ring = pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
  255. uint32_t ring_pa = pcilib_kmem_get_ba(ctx->dmactx.pcilib, ectx->ring);
  256. size_t bufsz = pcilib_kmem_get_block_size(ctx->dmactx.pcilib, ectx->pages, ectx->tail);
  257. ring += ectx->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  258. #ifdef NWL_GENERATE_DMA_IRQ
  259. NWL_RING_SET(ring, DMA_BD_BUFL_CTRL_OFFSET, bufsz | DMA_BD_INT_ERROR_MASK | DMA_BD_INT_COMP_MASK);
  260. #else /* NWL_GENERATE_DMA_IRQ */
  261. NWL_RING_SET(ring, DMA_BD_BUFL_CTRL_OFFSET, bufsz);
  262. #endif /* NWL_GENERATE_DMA_IRQ */
  263. NWL_RING_SET(ring, DMA_BD_BUFL_STATUS_OFFSET, 0);
  264. val = ring_pa + ectx->tail * PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  265. nwl_write_register(val, ctx, ectx->base_addr, REG_SW_NEXT_BD);
  266. ectx->tail++;
  267. if (ectx->tail == ectx->ring_size) ectx->tail = 0;
  268. return 0;
  269. }
  270. int dma_nwl_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) {
  271. size_t i;
  272. uint32_t bstatus;
  273. nwl_dma_t *ctx = (nwl_dma_t*)vctx;
  274. pcilib_nwl_engine_context_t *ectx = ctx->engines + dma;
  275. unsigned char *ring = (unsigned char*)pcilib_kmem_get_ua(ctx->dmactx.pcilib, ectx->ring);
  276. if (!status) return -1;
  277. status->started = ectx->started;
  278. status->ring_size = ectx->ring_size;
  279. status->buffer_size = ectx->page_size;
  280. status->ring_tail = ectx->tail;
  281. status->written_buffers = 0;
  282. status->written_bytes = 0;
  283. if (ectx->desc->direction == PCILIB_DMA_FROM_DEVICE) {
  284. size_t pos = 0;
  285. for (i = 0; i < ectx->ring_size; i++) {
  286. pos = status->ring_tail + i;
  287. if (pos >= ectx->ring_size) pos -= ectx->ring_size;
  288. bstatus = NWL_RING_GET(ring + pos * PCILIB_NWL_DMA_DESCRIPTOR_SIZE, DMA_BD_BUFL_STATUS_OFFSET);
  289. if ((bstatus&(DMA_BD_ERROR_MASK|DMA_BD_COMP_MASK)) == 0) break;
  290. }
  291. status->ring_head = pos;
  292. } else {
  293. status->ring_head = ectx->head;
  294. }
  295. if (buffers) {
  296. for (i = 0; (i < ectx->ring_size)&&(i < n_buffers); i++) {
  297. bstatus = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET);
  298. buffers[i].error = bstatus & (DMA_BD_ERROR_MASK/*|DMA_BD_SHORT_MASK*/);
  299. buffers[i].used = bstatus & DMA_BD_COMP_MASK;
  300. buffers[i].size = bstatus & DMA_BD_BUFL_MASK;
  301. buffers[i].first = bstatus & DMA_BD_SOP_MASK;
  302. buffers[i].last = bstatus & DMA_BD_EOP_MASK;
  303. ring += PCILIB_NWL_DMA_DESCRIPTOR_SIZE;
  304. }
  305. }
  306. for (i = 0; (i < ectx->ring_size)&&(i < n_buffers); i++) {
  307. bstatus = NWL_RING_GET(ring, DMA_BD_BUFL_STATUS_OFFSET);
  308. if (bstatus & DMA_BD_COMP_MASK) {
  309. status->written_buffers++;
  310. if ((bstatus & (DMA_BD_ERROR_MASK)) == 0)
  311. status->written_bytes += bstatus & DMA_BD_BUFL_MASK;
  312. }
  313. }
  314. return 0;
  315. }