scsi_accel_dma.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. #include "scsi_accel_dma.h"
  2. #include <ZuluSCSI_log.h>
  3. #include <gd32f4xx_timer.h>
  4. #include <gd32f4xx_rcu.h>
  5. #include <assert.h>
  6. #include <string.h>
  7. #ifndef SCSI_ACCEL_DMA_AVAILABLE
  8. void scsi_accel_timer_dma_init() {}
  9. void scsi_accel_greenpak_dma_init() {}
  10. void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag) {}
  11. void scsi_accel_dma_stopWrite() {}
  12. void scsi_accel_dma_finishWrite(volatile int *resetFlag) {}
  13. bool scsi_accel_dma_isWriteFinished(const uint8_t* data) { return true; }
  14. #else
  15. #define DMA_BUF_SIZE 256
  16. #define DMA_BUF_MASK (DMA_BUF_SIZE - 1)
  17. static struct {
  18. uint8_t *app_buf; // Buffer provided by application
  19. uint32_t dma_buf[DMA_BUF_SIZE]; // Buffer of data formatted for GPIO BOP register
  20. uint32_t dma_idx; // Write index to DMA buffer
  21. uint32_t dma_fillto; // Point up to which DMA buffer is available for refilling
  22. uint32_t timer_buf; // Control value for timer SWEVG register
  23. uint32_t bytes_app; // Bytes available in application buffer
  24. uint32_t bytes_dma; // Bytes (words) written so far to DMA buffer
  25. uint32_t scheduled_dma; // Bytes (words) that DMA data count was last set to
  26. uint8_t *next_app_buf; // Next buffer from application after current one finishes
  27. uint32_t next_app_bytes; // Bytes in next buffer
  28. } g_scsi_dma;
  29. enum scsidma_state_t { SCSIDMA_IDLE = 0, SCSIDMA_WRITE };
  30. static volatile scsidma_state_t g_scsi_dma_state;
  31. void scsi_accel_timer_dma_init()
  32. {
  33. g_scsi_dma_state = SCSIDMA_IDLE;
  34. rcu_periph_clock_enable(SCSI_TIMER_RCU);
  35. rcu_periph_clock_enable(SCSI_TIMER_DMA_RCU);
  36. // DMA Channel A: data copy
  37. // GPIO DMA copies data from memory buffer to GPIO BOP register.
  38. // The memory buffer is filled by interrupt routine.
  39. dma_single_data_parameter_struct gpio_dma_config =
  40. {
  41. .periph_addr = (uint32_t)&GPIO_BOP(SCSI_OUT_PORT),
  42. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  43. .memory0_addr = 0, // Filled before transfer
  44. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  45. .periph_memory_width = DMA_PERIPH_WIDTH_32BIT,
  46. // @TODO Check if circular mode is needed or not
  47. .circular_mode = DMA_CIRCULAR_MODE_ENABLE,
  48. .direction = DMA_MEMORY_TO_PERIPH,
  49. .number = DMA_BUF_SIZE,
  50. .priority = DMA_PRIORITY_ULTRA_HIGH
  51. };
  52. dma_single_data_mode_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA, &gpio_dma_config);
  53. dma_circulation_enable(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA);
  54. NVIC_SetPriority(SCSI_TIMER_DMACHA_IRQn, 1);
  55. NVIC_EnableIRQ(SCSI_TIMER_DMACHA_IRQn);
  56. // DMA Channel B: timer update
  57. // Timer DMA causes update event to restart timer after
  58. // GPIO DMA operation is done.
  59. dma_single_data_parameter_struct timer_dma_config =
  60. {
  61. .periph_addr = (uint32_t)&TIMER_SWEVG(SCSI_TIMER),
  62. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  63. .memory0_addr = (uint32_t)&g_scsi_dma.timer_buf,
  64. .memory_inc = DMA_PERIPH_INCREASE_DISABLE,
  65. .periph_memory_width = DMA_PERIPH_WIDTH_32BIT,
  66. .circular_mode = DMA_CIRCULAR_MODE_DISABLE,
  67. .direction = DMA_MEMORY_TO_PERIPH,
  68. .number = DMA_BUF_SIZE,
  69. .priority = DMA_PRIORITY_HIGH
  70. };
  71. dma_single_data_mode_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB, &timer_dma_config);
  72. NVIC_SetPriority(SCSI_TIMER_DMACHB_IRQn, 2);
  73. NVIC_EnableIRQ(SCSI_TIMER_DMACHB_IRQn);
  74. g_scsi_dma.timer_buf = TIMER_SWEVG_UPG;
  75. // Timer is used to toggle the request signal based on external trigger input.
  76. // OUT_REQ is driven by timer output.
  77. // 1. On timer update event, REQ is set low.
  78. // 2. When ACK goes low, timer counts and OUT_REQ is set high.
  79. // Simultaneously a DMA request is triggered to write next data to GPIO.
  80. // 3. When ACK goes high, a DMA request is triggered to cause timer update event.
  81. // The DMA request priority is set so that 2. always completes before it.
  82. TIMER_CTL0(SCSI_TIMER) = 0;
  83. TIMER_SMCFG(SCSI_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0F_ED;
  84. TIMER_CAR(SCSI_TIMER) = 65535;
  85. TIMER_PSC(SCSI_TIMER) = 0;
  86. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  87. TIMER_CHCTL0(SCSI_TIMER) = 0x6001; // CH0 as input, CH1 as DMA trigger
  88. TIMER_CHCTL1(SCSI_TIMER) = 0x6074; // CH2 as fast PWM output, CH3 as DMA trigger
  89. TIMER_CHCTL2(SCSI_TIMER) = TIMER_CHCTL2_CH2NEN;
  90. TIMER_CCHP(SCSI_TIMER) = TIMER_CCHP_POEN;
  91. TIMER_CH1CV(SCSI_TIMER) = 1; // Copy data when ACK goes low
  92. TIMER_CH2CV(SCSI_TIMER) = 1; // REQ is low until ACK goes low
  93. TIMER_CH3CV(SCSI_TIMER) = 2; // Reset timer after ACK goes high & previous DMA is complete
  94. gpio_mode_set(SCSI_TIMER_IN_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SCSI_TIMER_IN_PIN);
  95. gpio_af_set(SCSI_TIMER_IN_PORT, GPIO_AF_3, SCSI_TIMER_IN_PIN);
  96. scsi_accel_dma_stopWrite();
  97. }
  98. // Select whether OUT_REQ is connected to timer or GPIO port
  99. static void scsi_dma_gpio_config(bool enable)
  100. {
  101. if (enable)
  102. {
  103. gpio_mode_set(SCSI_OUT_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, SCSI_OUT_REQ);
  104. gpio_mode_set(SCSI_TIMER_OUT_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, SCSI_TIMER_OUT_PIN);
  105. // @TODO determine if the output should be set to 200MHZ instead of 50MHZ
  106. gpio_output_options_set(SCSI_TIMER_OUT_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
  107. // @TODO find if TIMER2_CH3 (AF2) is the correct AF
  108. gpio_af_set(SCSI_TIMER_OUT_PORT, GPIO_AF_2, SCSI_TIMER_OUT_PIN);
  109. }
  110. else
  111. {
  112. // @ DELETE this line shouldn't be needed?
  113. // GPIO_BC(SCSI_OUT_PORT) = GREENPAK_PLD_IO2;
  114. gpio_mode_set(SCSI_TIMER_OUT_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SCSI_TIMER_OUT_PIN);
  115. gpio_output_options_set(SCSI_OUT_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
  116. gpio_mode_set(SCSI_OUT_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SCSI_OUT_REQ);
  117. }
  118. }
  119. // Convert input bytes into BOP values in the DMA buffer
  120. static void refill_dmabuf()
  121. {
  122. // Check how many bytes we have available from the application
  123. uint32_t count = g_scsi_dma.bytes_app - g_scsi_dma.bytes_dma;
  124. // Check amount of free space in DMA buffer
  125. uint32_t max = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  126. if (count > max) count = max;
  127. if (count == 0) return;
  128. uint8_t *src = g_scsi_dma.app_buf + g_scsi_dma.bytes_dma;
  129. uint32_t *dst = g_scsi_dma.dma_buf;
  130. uint32_t pos = g_scsi_dma.dma_idx;
  131. uint32_t end = pos + count;
  132. g_scsi_dma.dma_idx = end;
  133. g_scsi_dma.bytes_dma += count;
  134. while (pos + 4 <= end)
  135. {
  136. uint32_t input = *(uint32_t*)src;
  137. src += 4;
  138. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 0) & 0xFF];
  139. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 8) & 0xFF];
  140. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 16) & 0xFF];
  141. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 24) & 0xFF];
  142. }
  143. while (pos < end)
  144. {
  145. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[*src++];
  146. }
  147. if (end < g_scsi_dma.dma_fillto)
  148. {
  149. // Partial buffer fill, this will get refilled from interrupt if we
  150. // get more data. Set next byte to an invalid parity value so that
  151. // any race conditions will get caught as parity error.
  152. dst[pos & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[0] ^ SCSI_OUT_DBP;
  153. }
  154. }
  155. // Start DMA transfer
  156. static void start_dma()
  157. {
  158. // Disable channels while configuring
  159. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  160. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  161. TIMER_CTL0(SCSI_TIMER) = 0;
  162. // Set new buffer address and size
  163. // CHA / Data channel is in circular mode and always has DMA_BUF_SIZE buffer size.
  164. // CHB / Update channel limits the number of data.
  165. DMA_CHM0ADDR(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = (uint32_t)g_scsi_dma.dma_buf;
  166. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = DMA_BUF_SIZE;
  167. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  168. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  169. g_scsi_dma.scheduled_dma += dma_to_schedule;
  170. // Clear pending DMA events
  171. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  172. TIMER_DMAINTEN(SCSI_TIMER) = TIMER_DMAINTEN_CH1DEN | TIMER_DMAINTEN_CH3DEN;
  173. // Clear and enable interrupt
  174. DMA_INTC1(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_FEE, SCSI_TIMER_DMACHA);
  175. DMA_INTC0(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_FEE, SCSI_TIMER_DMACHB);
  176. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE;
  177. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_FTFIE;
  178. // Enable channels
  179. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_CHEN;
  180. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  181. // Make sure REQ is initially high
  182. TIMER_CNT(SCSI_TIMER) = 16;
  183. TIMER_CHCTL1(SCSI_TIMER) = 0x6050;
  184. TIMER_CHCTL1(SCSI_TIMER) = 0x6074;
  185. // Enable timer
  186. TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN;
  187. // Generate first events
  188. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  189. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH3G;
  190. }
  191. // Stop DMA transfer
  192. static void stop_dma()
  193. {
  194. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  195. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  196. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~(DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE);
  197. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_FTFIE;
  198. TIMER_CTL0(SCSI_TIMER) &= ~TIMER_CTL0_CEN;
  199. g_scsi_dma_state = SCSIDMA_IDLE;
  200. SCSI_RELEASE_DATA_REQ();
  201. }
  202. static void check_dma_next_buffer()
  203. {
  204. // Check if we are at the end of the application buffer
  205. if (g_scsi_dma.next_app_buf && g_scsi_dma.bytes_dma == g_scsi_dma.bytes_app)
  206. {
  207. // Switch to next buffer
  208. assert(g_scsi_dma.scheduled_dma == g_scsi_dma.bytes_app);
  209. g_scsi_dma.app_buf = g_scsi_dma.next_app_buf;
  210. g_scsi_dma.bytes_app = g_scsi_dma.next_app_bytes;
  211. g_scsi_dma.bytes_dma = 0;
  212. g_scsi_dma.scheduled_dma = 0;
  213. g_scsi_dma.next_app_buf = 0;
  214. g_scsi_dma.next_app_bytes = 0;
  215. refill_dmabuf();
  216. }
  217. }
  218. // Convert new data from application buffer to DMA buffer
  219. extern "C" void SCSI_TIMER_DMACHA_IRQ()
  220. {
  221. // azdbg("DMA irq A, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  222. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  223. // TIMER_CNT(SCSI_TIMER));
  224. uint32_t intf = DMA_INTF1(SCSI_TIMER_DMA);
  225. const uint32_t half_flag = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  226. const uint32_t full_flag = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  227. if (intf & half_flag)
  228. {
  229. if (intf & full_flag)
  230. {
  231. azlog("ERROR: SCSI DMA overrun: ", intf,
  232. " bytes_app: ", g_scsi_dma.bytes_app,
  233. " bytes_dma: ", g_scsi_dma.bytes_dma,
  234. " dma_idx: ", g_scsi_dma.dma_idx,
  235. " sched_dma: ", g_scsi_dma.scheduled_dma);
  236. stop_dma();
  237. return;
  238. }
  239. DMA_INTC1(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  240. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  241. }
  242. else if (intf & full_flag)
  243. {
  244. DMA_INTC1(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  245. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  246. }
  247. // Fill DMA buffer with data from current application buffer
  248. refill_dmabuf();
  249. check_dma_next_buffer();
  250. }
  251. // Check if enough data is available to continue DMA transfer
  252. extern "C" void SCSI_TIMER_DMACHB_IRQ()
  253. {
  254. // azdbg("DMA irq B, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  255. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  256. // TIMER_CNT(SCSI_TIMER));
  257. uint32_t intf = DMA_INTF0(SCSI_TIMER_DMA);
  258. if (intf & DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB))
  259. {
  260. DMA_INTC0(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB);
  261. if (g_scsi_dma.bytes_app > g_scsi_dma.scheduled_dma)
  262. {
  263. if (g_scsi_dma.dma_idx < g_scsi_dma.dma_fillto)
  264. {
  265. // Previous request didn't have a complete buffer worth of data.
  266. // Refill the buffer and ensure that the first byte of the new data gets
  267. // written to outputs.
  268. __disable_irq();
  269. refill_dmabuf();
  270. __enable_irq();
  271. }
  272. // Verify the first byte of the new data has been written to outputs
  273. // It may have been updated after the DMA write occurred.
  274. __disable_irq();
  275. uint32_t first_data_idx = g_scsi_dma.dma_idx - (g_scsi_dma.bytes_dma - g_scsi_dma.scheduled_dma);
  276. uint32_t first_data = g_scsi_dma.dma_buf[first_data_idx & DMA_BUF_MASK];
  277. GPIO_BOP(SCSI_OUT_PORT) = first_data;
  278. __enable_irq();
  279. // Update the total number of bytes available for DMA
  280. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  281. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  282. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  283. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  284. g_scsi_dma.scheduled_dma += dma_to_schedule;
  285. }
  286. else
  287. {
  288. // No more data available
  289. stop_dma();
  290. }
  291. }
  292. }
  293. void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag)
  294. {
  295. __disable_irq();
  296. if (g_scsi_dma_state == SCSIDMA_WRITE)
  297. {
  298. if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  299. {
  300. // Combine with currently running request
  301. g_scsi_dma.bytes_app += count;
  302. count = 0;
  303. }
  304. else if (data == g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  305. {
  306. // Combine with queued request
  307. g_scsi_dma.next_app_bytes += count;
  308. count = 0;
  309. }
  310. else if (!g_scsi_dma.next_app_buf)
  311. {
  312. // Add as queued request
  313. g_scsi_dma.next_app_buf = (uint8_t*)data;
  314. g_scsi_dma.next_app_bytes = count;
  315. count = 0;
  316. }
  317. }
  318. __enable_irq();
  319. // Check if the request was combined
  320. if (count == 0) return;
  321. if (g_scsi_dma_state != SCSIDMA_IDLE)
  322. {
  323. // Wait for previous request to finish
  324. scsi_accel_dma_finishWrite(resetFlag);
  325. if (*resetFlag)
  326. {
  327. return;
  328. }
  329. }
  330. // azdbg("Starting DMA write of ", (int)count, " bytes");
  331. scsi_dma_gpio_config(true);
  332. g_scsi_dma_state = SCSIDMA_WRITE;
  333. g_scsi_dma.app_buf = (uint8_t*)data;
  334. g_scsi_dma.dma_idx = 0;
  335. g_scsi_dma.dma_fillto = DMA_BUF_SIZE;
  336. g_scsi_dma.bytes_app = count;
  337. g_scsi_dma.bytes_dma = 0;
  338. g_scsi_dma.scheduled_dma = 0;
  339. g_scsi_dma.next_app_buf = NULL;
  340. g_scsi_dma.next_app_bytes = 0;
  341. refill_dmabuf();
  342. start_dma();
  343. }
  344. bool scsi_accel_dma_isWriteFinished(const uint8_t* data)
  345. {
  346. // Check if everything has completed
  347. if (g_scsi_dma_state == SCSIDMA_IDLE)
  348. {
  349. return true;
  350. }
  351. if (!data)
  352. return false;
  353. // Check if this data item is still in queue.
  354. __disable_irq();
  355. bool finished = true;
  356. if (data >= g_scsi_dma.app_buf + g_scsi_dma.bytes_dma &&
  357. data < g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  358. {
  359. finished = false; // In current transfer
  360. }
  361. else if (data >= g_scsi_dma.next_app_buf &&
  362. data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  363. {
  364. finished = false; // In queued transfer
  365. }
  366. __enable_irq();
  367. return finished;
  368. }
  369. void scsi_accel_dma_stopWrite()
  370. {
  371. stop_dma();
  372. scsi_dma_gpio_config(false);
  373. }
  374. void scsi_accel_dma_finishWrite(volatile int *resetFlag)
  375. {
  376. uint32_t start = millis();
  377. while (g_scsi_dma_state != SCSIDMA_IDLE && !*resetFlag)
  378. {
  379. if ((uint32_t)(millis() - start) > 5000)
  380. {
  381. azlog("scsi_accel_dma_finishWrite() timeout, DMA counts ",
  382. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  383. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  384. TIMER_CNT(SCSI_TIMER));
  385. *resetFlag = 1;
  386. break;
  387. }
  388. }
  389. scsi_accel_dma_stopWrite();
  390. }
  391. // @TODO check if these externs are need without greenpak
  392. extern const uint32_t g_scsi_out_byte_to_bop_pld1hi[256];
  393. extern const uint32_t g_scsi_out_byte_to_bop_pld1lo[256];
  394. #endif