scsi_accel_dma.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. #include "scsi_accel_dma.h"
  2. #include <BlueSCSI_log.h>
  3. #include <gd32f20x_timer.h>
  4. #include <gd32f20x_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. static void greenpak_refill_dmabuf();
  16. static void greenpak_start_dma();
  17. static void greenpak_stop_dma();
  18. enum greenpak_state_t { GREENPAK_IO1_LOW = 0, GREENPAK_IO1_HIGH, GREENPAK_STOP};
  19. #define DMA_BUF_SIZE 256
  20. #define DMA_BUF_MASK (DMA_BUF_SIZE - 1)
  21. static struct {
  22. uint8_t *app_buf; // Buffer provided by application
  23. uint32_t dma_buf[DMA_BUF_SIZE]; // Buffer of data formatted for GPIO BOP register
  24. uint32_t dma_idx; // Write index to DMA buffer
  25. uint32_t dma_fillto; // Point up to which DMA buffer is available for refilling
  26. uint32_t timer_buf; // Control value for timer SWEVG register
  27. uint32_t bytes_app; // Bytes available in application buffer
  28. uint32_t bytes_dma; // Bytes (words) written so far to DMA buffer
  29. uint32_t scheduled_dma; // Bytes (words) that DMA data count was last set to
  30. greenpak_state_t greenpak_state; // Toggle signal state for greenpak
  31. uint8_t *next_app_buf; // Next buffer from application after current one finishes
  32. uint32_t next_app_bytes; // Bytes in next buffer
  33. } g_scsi_dma;
  34. enum scsidma_state_t { SCSIDMA_IDLE = 0, SCSIDMA_WRITE };
  35. static volatile scsidma_state_t g_scsi_dma_state;
  36. static bool g_scsi_dma_use_greenpak;
  37. void scsi_accel_timer_dma_init()
  38. {
  39. g_scsi_dma_state = SCSIDMA_IDLE;
  40. g_scsi_dma_use_greenpak = false;
  41. rcu_periph_clock_enable(SCSI_TIMER_RCU);
  42. rcu_periph_clock_enable(SCSI_TIMER_DMA_RCU);
  43. // DMA Channel A: data copy
  44. // GPIO DMA copies data from memory buffer to GPIO BOP register.
  45. // The memory buffer is filled by interrupt routine.
  46. dma_parameter_struct gpio_dma_config =
  47. {
  48. .periph_addr = (uint32_t)&GPIO_BOP(SCSI_OUT_PORT),
  49. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  50. .memory_addr = 0, // Filled before transfer
  51. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  52. .number = DMA_BUF_SIZE,
  53. .priority = DMA_PRIORITY_ULTRA_HIGH,
  54. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  55. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  56. .direction = DMA_MEMORY_TO_PERIPHERAL
  57. };
  58. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA, &gpio_dma_config);
  59. dma_circulation_enable(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA);
  60. NVIC_SetPriority(SCSI_TIMER_DMACHA_IRQn, 1);
  61. NVIC_EnableIRQ(SCSI_TIMER_DMACHA_IRQn);
  62. // DMA Channel B: timer update
  63. // Timer DMA causes update event to restart timer after
  64. // GPIO DMA operation is done.
  65. dma_parameter_struct timer_dma_config =
  66. {
  67. .periph_addr = (uint32_t)&TIMER_SWEVG(SCSI_TIMER),
  68. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  69. .memory_addr = (uint32_t)&g_scsi_dma.timer_buf,
  70. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  71. .number = DMA_BUF_SIZE,
  72. .priority = DMA_PRIORITY_HIGH,
  73. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  74. .memory_inc = DMA_PERIPH_INCREASE_DISABLE,
  75. .direction = DMA_MEMORY_TO_PERIPHERAL
  76. };
  77. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB, &timer_dma_config);
  78. NVIC_SetPriority(SCSI_TIMER_DMACHB_IRQn, 2);
  79. NVIC_EnableIRQ(SCSI_TIMER_DMACHB_IRQn);
  80. g_scsi_dma.timer_buf = TIMER_SWEVG_UPG;
  81. // Timer is used to toggle the request signal based on external trigger input.
  82. // OUT_REQ is driven by timer output.
  83. // 1. On timer update event, REQ is set low.
  84. // 2. When ACK goes low, timer counts and OUT_REQ is set high.
  85. // Simultaneously a DMA request is triggered to write next data to GPIO.
  86. // 3. When ACK goes high, a DMA request is triggered to cause timer update event.
  87. // The DMA request priority is set so that 2. always completes before it.
  88. TIMER_CTL0(SCSI_TIMER) = 0;
  89. TIMER_SMCFG(SCSI_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0F_ED;
  90. TIMER_CAR(SCSI_TIMER) = 65535;
  91. TIMER_PSC(SCSI_TIMER) = 0;
  92. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  93. TIMER_CHCTL0(SCSI_TIMER) = 0x6001; // CH0 as input, CH1 as DMA trigger
  94. TIMER_CHCTL1(SCSI_TIMER) = 0x6074; // CH2 as fast PWM output, CH3 as DMA trigger
  95. TIMER_CHCTL2(SCSI_TIMER) = TIMER_CHCTL2_CH2NEN;
  96. TIMER_CCHP(SCSI_TIMER) = TIMER_CCHP_POEN;
  97. TIMER_CH1CV(SCSI_TIMER) = 1; // Copy data when ACK goes low
  98. TIMER_CH2CV(SCSI_TIMER) = 1; // REQ is low until ACK goes low
  99. TIMER_CH3CV(SCSI_TIMER) = 2; // Reset timer after ACK goes high & previous DMA is complete
  100. gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
  101. scsi_accel_dma_stopWrite();
  102. }
  103. // Select whether OUT_REQ is connected to timer or GPIO port
  104. static void scsi_dma_gpio_config(bool enable)
  105. {
  106. if (enable)
  107. {
  108. gpio_init(SCSI_OUT_PORT, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
  109. if (g_scsi_dma_use_greenpak)
  110. {
  111. GPIO_BC(SCSI_OUT_PORT) = GREENPAK_PLD_IO1;
  112. GPIO_BOP(SCSI_OUT_PORT) = GREENPAK_PLD_IO2;
  113. }
  114. else
  115. {
  116. gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
  117. }
  118. }
  119. else
  120. {
  121. GPIO_BC(SCSI_OUT_PORT) = GREENPAK_PLD_IO2;
  122. gpio_init(SCSI_TIMER_OUT_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SCSI_TIMER_OUT_PIN);
  123. gpio_init(SCSI_OUT_PORT, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, SCSI_OUT_REQ);
  124. }
  125. }
  126. // Convert input bytes into BOP values in the DMA buffer
  127. static void refill_dmabuf()
  128. {
  129. if (g_scsi_dma_use_greenpak)
  130. {
  131. greenpak_refill_dmabuf();
  132. return;
  133. }
  134. // Check how many bytes we have available from the application
  135. uint32_t count = g_scsi_dma.bytes_app - g_scsi_dma.bytes_dma;
  136. // Check amount of free space in DMA buffer
  137. uint32_t max = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  138. if (count > max) count = max;
  139. if (count == 0) return;
  140. uint8_t *src = g_scsi_dma.app_buf + g_scsi_dma.bytes_dma;
  141. uint32_t *dst = g_scsi_dma.dma_buf;
  142. uint32_t pos = g_scsi_dma.dma_idx;
  143. uint32_t end = pos + count;
  144. g_scsi_dma.dma_idx = end;
  145. g_scsi_dma.bytes_dma += count;
  146. while (pos + 4 <= end)
  147. {
  148. uint32_t input = *(uint32_t*)src;
  149. src += 4;
  150. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 0) & 0xFF];
  151. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 8) & 0xFF];
  152. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 16) & 0xFF];
  153. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[(input >> 24) & 0xFF];
  154. }
  155. while (pos < end)
  156. {
  157. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[*src++];
  158. }
  159. if (end < g_scsi_dma.dma_fillto)
  160. {
  161. // Partial buffer fill, this will get refilled from interrupt if we
  162. // get more data. Set next byte to an invalid parity value so that
  163. // any race conditions will get caught as parity error.
  164. dst[pos & DMA_BUF_MASK] = g_scsi_out_byte_to_bop[0] ^ SCSI_OUT_DBP;
  165. }
  166. }
  167. // Start DMA transfer
  168. static void start_dma()
  169. {
  170. if (g_scsi_dma_use_greenpak)
  171. {
  172. greenpak_start_dma();
  173. return;
  174. }
  175. // Disable channels while configuring
  176. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  177. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  178. TIMER_CTL0(SCSI_TIMER) = 0;
  179. // Set new buffer address and size
  180. // CHA / Data channel is in circular mode and always has DMA_BUF_SIZE buffer size.
  181. // CHB / Update channel limits the number of data.
  182. DMA_CHMADDR(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = (uint32_t)g_scsi_dma.dma_buf;
  183. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = DMA_BUF_SIZE;
  184. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  185. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  186. g_scsi_dma.scheduled_dma += dma_to_schedule;
  187. // Clear pending DMA events
  188. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  189. TIMER_DMAINTEN(SCSI_TIMER) = TIMER_DMAINTEN_CH1DEN | TIMER_DMAINTEN_CH3DEN;
  190. // Clear and enable interrupt
  191. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHA);
  192. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHB);
  193. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE;
  194. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_FTFIE;
  195. // Enable channels
  196. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_CHEN;
  197. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  198. // Make sure REQ is initially high
  199. TIMER_CNT(SCSI_TIMER) = 16;
  200. TIMER_CHCTL1(SCSI_TIMER) = 0x6050;
  201. TIMER_CHCTL1(SCSI_TIMER) = 0x6074;
  202. // Enable timer
  203. TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN;
  204. // Generate first events
  205. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  206. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH3G;
  207. }
  208. // Stop DMA transfer
  209. static void stop_dma()
  210. {
  211. greenpak_stop_dma();
  212. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  213. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  214. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~(DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE);
  215. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_FTFIE;
  216. TIMER_CTL0(SCSI_TIMER) &= ~TIMER_CTL0_CEN;
  217. g_scsi_dma_state = SCSIDMA_IDLE;
  218. SCSI_RELEASE_DATA_REQ();
  219. }
  220. static void check_dma_next_buffer()
  221. {
  222. // Check if we are at the end of the application buffer
  223. if (g_scsi_dma.next_app_buf && g_scsi_dma.bytes_dma == g_scsi_dma.bytes_app)
  224. {
  225. // Switch to next buffer
  226. assert(g_scsi_dma.scheduled_dma == g_scsi_dma.bytes_app);
  227. g_scsi_dma.app_buf = g_scsi_dma.next_app_buf;
  228. g_scsi_dma.bytes_app = g_scsi_dma.next_app_bytes;
  229. g_scsi_dma.bytes_dma = 0;
  230. g_scsi_dma.scheduled_dma = 0;
  231. g_scsi_dma.next_app_buf = 0;
  232. g_scsi_dma.next_app_bytes = 0;
  233. refill_dmabuf();
  234. }
  235. }
  236. // Convert new data from application buffer to DMA buffer
  237. extern "C" void SCSI_TIMER_DMACHA_IRQ()
  238. {
  239. // bluedbg("DMA irq A, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  240. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  241. // TIMER_CNT(SCSI_TIMER));
  242. uint32_t intf = DMA_INTF(SCSI_TIMER_DMA);
  243. const uint32_t half_flag = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  244. const uint32_t full_flag = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  245. if (intf & half_flag)
  246. {
  247. if (intf & full_flag)
  248. {
  249. bluelog("ERROR: SCSI DMA overrun: ", intf,
  250. " bytes_app: ", g_scsi_dma.bytes_app,
  251. " bytes_dma: ", g_scsi_dma.bytes_dma,
  252. " dma_idx: ", g_scsi_dma.dma_idx,
  253. " sched_dma: ", g_scsi_dma.scheduled_dma);
  254. stop_dma();
  255. return;
  256. }
  257. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF, SCSI_TIMER_DMACHA);
  258. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  259. }
  260. else if (intf & full_flag)
  261. {
  262. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHA);
  263. g_scsi_dma.dma_fillto += DMA_BUF_SIZE / 2;
  264. }
  265. // Fill DMA buffer with data from current application buffer
  266. refill_dmabuf();
  267. check_dma_next_buffer();
  268. }
  269. // Check if enough data is available to continue DMA transfer
  270. extern "C" void SCSI_TIMER_DMACHB_IRQ()
  271. {
  272. // bluedbg("DMA irq B, counts: ", DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  273. // DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  274. // TIMER_CNT(SCSI_TIMER));
  275. uint32_t intf = DMA_INTF(SCSI_TIMER_DMA);
  276. if (intf & DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB))
  277. {
  278. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_FTF, SCSI_TIMER_DMACHB);
  279. if (g_scsi_dma.bytes_app > g_scsi_dma.scheduled_dma)
  280. {
  281. if (g_scsi_dma.dma_idx < g_scsi_dma.dma_fillto)
  282. {
  283. // Previous request didn't have a complete buffer worth of data.
  284. // Refill the buffer and ensure that the first byte of the new data gets
  285. // written to outputs.
  286. __disable_irq();
  287. refill_dmabuf();
  288. __enable_irq();
  289. }
  290. // Verify the first byte of the new data has been written to outputs
  291. // It may have been updated after the DMA write occurred.
  292. __disable_irq();
  293. uint32_t first_data_idx = g_scsi_dma.dma_idx - (g_scsi_dma.bytes_dma - g_scsi_dma.scheduled_dma);
  294. uint32_t first_data = g_scsi_dma.dma_buf[first_data_idx & DMA_BUF_MASK];
  295. GPIO_BOP(SCSI_OUT_PORT) = first_data;
  296. __enable_irq();
  297. // Update the total number of bytes available for DMA
  298. uint32_t dma_to_schedule = g_scsi_dma.bytes_app - g_scsi_dma.scheduled_dma;
  299. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  300. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) = dma_to_schedule;
  301. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) |= DMA_CHXCTL_CHEN;
  302. g_scsi_dma.scheduled_dma += dma_to_schedule;
  303. }
  304. else
  305. {
  306. // No more data available
  307. stop_dma();
  308. }
  309. }
  310. }
  311. void scsi_accel_dma_startWrite(const uint8_t* data, uint32_t count, volatile int *resetFlag)
  312. {
  313. __disable_irq();
  314. if (g_scsi_dma_state == SCSIDMA_WRITE)
  315. {
  316. if (!g_scsi_dma.next_app_buf && data == g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  317. {
  318. // Combine with currently running request
  319. g_scsi_dma.bytes_app += count;
  320. count = 0;
  321. }
  322. else if (data == g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  323. {
  324. // Combine with queued request
  325. g_scsi_dma.next_app_bytes += count;
  326. count = 0;
  327. }
  328. else if (!g_scsi_dma.next_app_buf)
  329. {
  330. // Add as queued request
  331. g_scsi_dma.next_app_buf = (uint8_t*)data;
  332. g_scsi_dma.next_app_bytes = count;
  333. count = 0;
  334. }
  335. }
  336. __enable_irq();
  337. // Check if the request was combined
  338. if (count == 0) return;
  339. if (g_scsi_dma_state != SCSIDMA_IDLE)
  340. {
  341. // Wait for previous request to finish
  342. scsi_accel_dma_finishWrite(resetFlag);
  343. if (*resetFlag)
  344. {
  345. return;
  346. }
  347. }
  348. // bluedbg("Starting DMA write of ", (int)count, " bytes");
  349. scsi_dma_gpio_config(true);
  350. g_scsi_dma_state = SCSIDMA_WRITE;
  351. g_scsi_dma.app_buf = (uint8_t*)data;
  352. g_scsi_dma.dma_idx = 0;
  353. g_scsi_dma.dma_fillto = DMA_BUF_SIZE;
  354. g_scsi_dma.bytes_app = count;
  355. g_scsi_dma.bytes_dma = 0;
  356. g_scsi_dma.scheduled_dma = 0;
  357. g_scsi_dma.next_app_buf = NULL;
  358. g_scsi_dma.next_app_bytes = 0;
  359. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  360. refill_dmabuf();
  361. start_dma();
  362. }
  363. bool scsi_accel_dma_isWriteFinished(const uint8_t* data)
  364. {
  365. // Check if everything has completed
  366. if (g_scsi_dma_state == SCSIDMA_IDLE)
  367. {
  368. return true;
  369. }
  370. if (!data)
  371. return false;
  372. // Check if this data item is still in queue.
  373. __disable_irq();
  374. bool finished = true;
  375. if (data >= g_scsi_dma.app_buf + g_scsi_dma.bytes_dma &&
  376. data < g_scsi_dma.app_buf + g_scsi_dma.bytes_app)
  377. {
  378. finished = false; // In current transfer
  379. }
  380. else if (data >= g_scsi_dma.next_app_buf &&
  381. data < g_scsi_dma.next_app_buf + g_scsi_dma.next_app_bytes)
  382. {
  383. finished = false; // In queued transfer
  384. }
  385. __enable_irq();
  386. return finished;
  387. }
  388. void scsi_accel_dma_stopWrite()
  389. {
  390. stop_dma();
  391. scsi_dma_gpio_config(false);
  392. }
  393. void scsi_accel_dma_finishWrite(volatile int *resetFlag)
  394. {
  395. uint32_t start = millis();
  396. while (g_scsi_dma_state != SCSIDMA_IDLE && !*resetFlag)
  397. {
  398. if ((uint32_t)(millis() - start) > 5000)
  399. {
  400. bluelog("scsi_accel_dma_finishWrite() timeout, DMA counts ",
  401. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA), " ",
  402. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB), " ",
  403. TIMER_CNT(SCSI_TIMER));
  404. *resetFlag = 1;
  405. break;
  406. }
  407. }
  408. scsi_accel_dma_stopWrite();
  409. }
  410. /************************************************/
  411. /* Functions using external GreenPAK logic chip */
  412. /************************************************/
  413. void scsi_accel_greenpak_dma_init()
  414. {
  415. g_scsi_dma_state = SCSIDMA_IDLE;
  416. g_scsi_dma_use_greenpak = true;
  417. rcu_periph_clock_enable(SCSI_TIMER_RCU);
  418. rcu_periph_clock_enable(SCSI_TIMER_DMA_RCU);
  419. // DMA Channel A: data copy
  420. // GPIO DMA copies data from memory buffer to GPIO BOP register.
  421. // The memory buffer is filled by interrupt routine.
  422. dma_parameter_struct gpio_dma_config =
  423. {
  424. .periph_addr = (uint32_t)&GPIO_BOP(SCSI_OUT_PORT),
  425. .periph_width = DMA_PERIPHERAL_WIDTH_32BIT,
  426. .memory_addr = (uint32_t)g_scsi_dma.dma_buf,
  427. .memory_width = DMA_MEMORY_WIDTH_32BIT,
  428. .number = DMA_BUF_SIZE,
  429. .priority = DMA_PRIORITY_ULTRA_HIGH,
  430. .periph_inc = DMA_PERIPH_INCREASE_DISABLE,
  431. .memory_inc = DMA_MEMORY_INCREASE_ENABLE,
  432. .direction = DMA_MEMORY_TO_PERIPHERAL
  433. };
  434. dma_init(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA, &gpio_dma_config);
  435. dma_circulation_enable(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA);
  436. NVIC_SetPriority(SCSI_TIMER_DMACHA_IRQn, 2);
  437. NVIC_EnableIRQ(SCSI_TIMER_DMACHA_IRQn);
  438. NVIC_DisableIRQ(SCSI_TIMER_DMACHB_IRQn);
  439. // EXTI channel is used to trigger when we reach end of the transfer.
  440. // Because the main DMA is circular and transfer size may not be even
  441. // multiple of it, we cannot trigger the end at the DMA interrupt.
  442. gpio_exti_source_select(GREENPAK_PLD_IO2_EXTI_SOURCE_PORT, GREENPAK_PLD_IO2_EXTI_SOURCE_PIN);
  443. exti_init(GREENPAK_PLD_IO2_EXTI, EXTI_INTERRUPT, EXTI_TRIG_FALLING);
  444. exti_interrupt_flag_clear(GREENPAK_PLD_IO2_EXTI);
  445. exti_interrupt_disable(GREENPAK_PLD_IO2_EXTI);
  446. NVIC_SetPriority(GREENPAK_IRQn, 1);
  447. NVIC_EnableIRQ(GREENPAK_IRQn);
  448. // Timer is used to trigger DMA requests
  449. // OUT_REQ is driven by timer output.
  450. // 1. On timer update event, REQ is set low.
  451. // 2. When ACK goes low, timer counts and OUT_REQ is set high.
  452. // Simultaneously a DMA request is triggered to write next data to GPIO.
  453. // 3. When ACK goes high, a DMA request is triggered to cause timer update event.
  454. // The DMA request priority is set so that 2. always completes before it.
  455. TIMER_CTL0(SCSI_TIMER) = 0;
  456. TIMER_SMCFG(SCSI_TIMER) = TIMER_SLAVE_MODE_EXTERNAL0 | TIMER_SMCFG_TRGSEL_CI0F_ED;
  457. TIMER_CAR(SCSI_TIMER) = 1;
  458. TIMER_PSC(SCSI_TIMER) = 0;
  459. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  460. TIMER_CHCTL0(SCSI_TIMER) = 0x6001; // CH0 as input, CH1 as DMA trigger
  461. TIMER_CHCTL1(SCSI_TIMER) = 0;
  462. TIMER_CHCTL2(SCSI_TIMER) = 0;
  463. TIMER_CCHP(SCSI_TIMER) = 0;
  464. TIMER_CH1CV(SCSI_TIMER) = 1; // Copy data when ACK goes low
  465. gpio_init(SCSI_TIMER_IN_PORT, GPIO_MODE_IN_FLOATING, 0, SCSI_TIMER_IN_PIN);
  466. }
  467. extern const uint32_t g_scsi_out_byte_to_bop_pld1hi[256];
  468. extern const uint32_t g_scsi_out_byte_to_bop_pld1lo[256];
  469. static void greenpak_refill_dmabuf()
  470. {
  471. if (g_scsi_dma.greenpak_state == GREENPAK_STOP)
  472. {
  473. // Wait for previous DMA block to end first
  474. return;
  475. }
  476. // Check how many bytes we have available from the application
  477. uint32_t count = g_scsi_dma.bytes_app - g_scsi_dma.bytes_dma;
  478. // Check amount of free space in DMA buffer
  479. uint32_t max = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  480. if (count > max) count = max;
  481. uint8_t *src = g_scsi_dma.app_buf + g_scsi_dma.bytes_dma;
  482. uint32_t *dst = g_scsi_dma.dma_buf;
  483. uint32_t pos = g_scsi_dma.dma_idx;
  484. uint32_t end = pos + count;
  485. g_scsi_dma.dma_idx = end;
  486. g_scsi_dma.bytes_dma += count;
  487. g_scsi_dma.scheduled_dma = g_scsi_dma.bytes_dma;
  488. if (pos < end && g_scsi_dma.greenpak_state == GREENPAK_IO1_HIGH)
  489. {
  490. // Fix alignment so that main loop begins with PLD1HI
  491. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[*src++];
  492. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  493. }
  494. while (pos + 4 <= end)
  495. {
  496. uint32_t input = *(uint32_t*)src;
  497. src += 4;
  498. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1hi[(input >> 0) & 0xFF];
  499. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[(input >> 8) & 0xFF];
  500. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1hi[(input >> 16) & 0xFF];
  501. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[(input >> 24) & 0xFF];
  502. }
  503. while (pos < end)
  504. {
  505. if (g_scsi_dma.greenpak_state == GREENPAK_IO1_HIGH)
  506. {
  507. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1lo[*src++];
  508. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  509. }
  510. else
  511. {
  512. dst[(pos++) & DMA_BUF_MASK] = g_scsi_out_byte_to_bop_pld1hi[*src++];
  513. g_scsi_dma.greenpak_state = GREENPAK_IO1_HIGH;
  514. }
  515. }
  516. uint32_t remain = g_scsi_dma.dma_fillto - g_scsi_dma.dma_idx;
  517. if (!g_scsi_dma.next_app_buf && remain > 0)
  518. {
  519. // Mark the end of transfer by turning PD2 off
  520. dst[(pos++) & DMA_BUF_MASK] = (GREENPAK_PLD_IO2 << 16) | (GREENPAK_PLD_IO1 << 16);
  521. g_scsi_dma.dma_idx = pos;
  522. g_scsi_dma.greenpak_state = GREENPAK_STOP;
  523. }
  524. }
  525. extern "C" void GREENPAK_IRQ()
  526. {
  527. if (EXTI_PD & GREENPAK_PLD_IO2_EXTI)
  528. {
  529. EXTI_PD = GREENPAK_PLD_IO2_EXTI;
  530. if (g_scsi_dma.bytes_app > g_scsi_dma.bytes_dma || g_scsi_dma.next_app_buf)
  531. {
  532. assert(g_scsi_dma.greenpak_state == GREENPAK_STOP);
  533. g_scsi_dma.greenpak_state = GREENPAK_IO1_LOW;
  534. // More data is available
  535. check_dma_next_buffer();
  536. refill_dmabuf();
  537. // Continue transferring
  538. GPIO_BOP(SCSI_OUT_PORT) = GREENPAK_PLD_IO2;
  539. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  540. }
  541. else
  542. {
  543. stop_dma();
  544. }
  545. }
  546. }
  547. static void greenpak_start_dma()
  548. {
  549. // Disable channels while configuring
  550. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) &= ~DMA_CHXCTL_CHEN;
  551. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHB) &= ~DMA_CHXCTL_CHEN;
  552. TIMER_CTL0(SCSI_TIMER) = 0;
  553. // Set buffer address and size
  554. DMA_CHMADDR(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = (uint32_t)g_scsi_dma.dma_buf;
  555. DMA_CHCNT(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) = DMA_BUF_SIZE;
  556. // Clear pending DMA events
  557. TIMER_DMAINTEN(SCSI_TIMER) = 0;
  558. TIMER_DMAINTEN(SCSI_TIMER) = TIMER_DMAINTEN_CH1DEN | TIMER_DMAINTEN_CH3DEN;
  559. // Clear and enable interrupt
  560. DMA_INTC(SCSI_TIMER_DMA) = DMA_FLAG_ADD(DMA_FLAG_HTF | DMA_FLAG_FTF | DMA_FLAG_ERR, SCSI_TIMER_DMACHA);
  561. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_FTFIE | DMA_CHXCTL_HTFIE;
  562. exti_interrupt_flag_clear(GREENPAK_PLD_IO2_EXTI);
  563. exti_interrupt_enable(GREENPAK_PLD_IO2_EXTI);
  564. // Enable channels
  565. DMA_CHCTL(SCSI_TIMER_DMA, SCSI_TIMER_DMACHA) |= DMA_CHXCTL_CHEN;
  566. // Enable timer
  567. TIMER_CNT(SCSI_TIMER) = 0;
  568. TIMER_CTL0(SCSI_TIMER) |= TIMER_CTL0_CEN;
  569. // Generate first event
  570. TIMER_SWEVG(SCSI_TIMER) = TIMER_SWEVG_CH1G;
  571. }
  572. static void greenpak_stop_dma()
  573. {
  574. exti_interrupt_disable(GREENPAK_PLD_IO2_EXTI);
  575. }
  576. #endif