scsi_accel_dma.cpp 19 KB

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