71640f4ff74b47e30f36df43bf735dc7b185e452
[SCSI2SD-V6.git] / src / firmware / scsiPhy.c
1 //      Copyright (C) 2013 Michael McMaster <michael@codesrc.com>\r
2 //\r
3 //      This file is part of SCSI2SD.\r
4 //\r
5 //      SCSI2SD is free software: you can redistribute it and/or modify\r
6 //      it under the terms of the GNU General Public License as published by\r
7 //      the Free Software Foundation, either version 3 of the License, or\r
8 //      (at your option) any later version.\r
9 //\r
10 //      SCSI2SD is distributed in the hope that it will be useful,\r
11 //      but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 //      GNU General Public License for more details.\r
14 //\r
15 //      You should have received a copy of the GNU General Public License\r
16 //      along with SCSI2SD.  If not, see <http://www.gnu.org/licenses/>.\r
17 \r
18 #include "stm32f2xx.h"\r
19 #include "stm32f2xx_hal.h"\r
20 #include "stm32f2xx_hal_dma.h"\r
21 \r
22 #include "scsi.h"\r
23 #include "scsiPhy.h"\r
24 #include "trace.h"\r
25 #include "time.h"\r
26 #include "fpga.h"\r
27 #include "led.h"\r
28 \r
29 #include <string.h>\r
30 \r
31 // Assumes a 60MHz fpga clock.\r
32 // 7:6 Hold count, 45ns\r
33 // 5:3 Assertion count, 90ns\r
34 // 2:0 Deskew count, 55ns\r
35 #define SCSI_DEFAULT_TIMING ((0x3 << 6) | (0x6 << 3) | 0x4)\r
36 \r
37 // 7:6 Hold count, 10ns\r
38 // 5:3 Assertion count, 30ns\r
39 // 2:0 Deskew count, 25ns\r
40 #define SCSI_FAST_TIMING ((0x1 << 6) | (0x2 << 3) | 0x2)\r
41 \r
42 // Private DMA variables.\r
43 static int dmaInProgress = 0;\r
44 \r
45 static DMA_HandleTypeDef memToFSMC;\r
46 static DMA_HandleTypeDef fsmcToMem;\r
47 \r
48 \r
49 volatile uint8_t scsiRxDMAComplete;\r
50 volatile uint8_t scsiTxDMAComplete;\r
51 \r
52 #if 0\r
53 CY_ISR_PROTO(scsiRxCompleteISR);\r
54 CY_ISR(scsiRxCompleteISR)\r
55 {\r
56         traceIrq(trace_scsiRxCompleteISR);\r
57         scsiRxDMAComplete = 1;\r
58 }\r
59 \r
60 CY_ISR_PROTO(scsiTxCompleteISR);\r
61 CY_ISR(scsiTxCompleteISR)\r
62 {\r
63         traceIrq(trace_scsiTxCompleteISR);\r
64         scsiTxDMAComplete = 1;\r
65 }\r
66 #endif\r
67 \r
68 uint8_t scsiPhyFifoSel = 0; // global\r
69 \r
70 // scsi IRQ handler is initialised by the STM32 HAL. Connected to\r
71 // PE4\r
72 // Note: naming is important to ensure this function is listed in the\r
73 // vector table.\r
74 void EXTI4_IRQHandler()\r
75 {\r
76         traceIrq(trace_scsiResetISR);\r
77 \r
78         // Make sure that interrupt flag is set\r
79         if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_4) != RESET) {\r
80 \r
81                 // Clear interrupt flag\r
82                 __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_4);\r
83 \r
84                 scsiDev.resetFlag = scsiDev.resetFlag || scsiStatusRST();\r
85                 // TODO grab SEL status as well\r
86 \r
87         }\r
88 }\r
89 \r
90 static void assertFail()\r
91 {\r
92         while (1)\r
93         {\r
94                 s2s_ledOn();\r
95                 s2s_delay_ms(100);\r
96                 s2s_ledOff();\r
97                 s2s_delay_ms(100);\r
98         }\r
99 }\r
100 \r
101 static void\r
102 startScsiRx(uint32_t count)\r
103 {\r
104         *SCSI_DATA_CNT_HI = count >> 8;\r
105         *SCSI_DATA_CNT_LO = count & 0xff;\r
106         *SCSI_DATA_CNT_SET = 1;\r
107 }\r
108 \r
109 uint8_t\r
110 scsiReadByte(void)\r
111 {\r
112 #if FIFODEBUG\r
113         if (!scsiPhyFifoAltEmpty()) {\r
114                 // Force a lock-up.\r
115                 assertFail();\r
116         }\r
117 #endif\r
118         startScsiRx(1);\r
119 \r
120         trace(trace_spinPhyRxFifo);\r
121         while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) {}\r
122         scsiPhyFifoFlip();\r
123         uint8_t val = scsiPhyRx();\r
124         // TODO scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
125 \r
126 #if FIFODEBUG\r
127         if (!scsiPhyFifoEmpty()) {\r
128                 int j = 0;\r
129                 uint8_t k __attribute((unused));\r
130                 while (!scsiPhyFifoEmpty()) { k = scsiPhyRx(); ++j; }\r
131 \r
132                 // Force a lock-up.\r
133                 assertFail();\r
134         }\r
135 #endif\r
136         return val;\r
137 }\r
138 \r
139 \r
140 static void\r
141 scsiReadPIO(uint8_t* data, uint32_t count)\r
142 {\r
143         for (int i = 0; i < count; ++i)\r
144         {\r
145                 data[i] = scsiPhyRx();\r
146         }\r
147         // TODO scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
148 }\r
149 \r
150 void\r
151 scsiReadDMA(uint8_t* data, uint32_t count)\r
152 {\r
153         // Prepare DMA transfer\r
154         dmaInProgress = 1;\r
155         trace(trace_doRxSingleDMA);\r
156 \r
157         scsiTxDMAComplete = 1; // TODO not used much\r
158         scsiRxDMAComplete = 0; // TODO not used much\r
159 \r
160         HAL_DMA_Start(&fsmcToMem, (uint32_t) SCSI_FIFO_DATA, (uint32_t) data, count);\r
161 }\r
162 \r
163 int\r
164 scsiReadDMAPoll()\r
165 {\r
166         int complete = __HAL_DMA_GET_COUNTER(&fsmcToMem) == 0;\r
167         complete = complete && (HAL_DMA_PollForTransfer(&fsmcToMem, HAL_DMA_FULL_TRANSFER, 0xffffffff) == HAL_OK);\r
168         if (complete)\r
169         {\r
170                 scsiTxDMAComplete = 1; // TODO MM FIX IRQ\r
171                 scsiRxDMAComplete = 1;\r
172 \r
173                 dmaInProgress = 0;\r
174 #if 0\r
175                 // TODO MM scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
176 #endif\r
177                 return 1;\r
178 \r
179         }\r
180         else\r
181         {\r
182                 return 0;\r
183         }\r
184 }\r
185 \r
186 void\r
187 scsiRead(uint8_t* data, uint32_t count)\r
188 {\r
189         int i = 0;\r
190 \r
191 \r
192         uint32_t chunk = ((count - i) > SCSI_FIFO_DEPTH)\r
193                 ? SCSI_FIFO_DEPTH : (count - i);\r
194         if (chunk >= 16)\r
195         {\r
196                 // DMA is doing 32bit transfers.\r
197                 chunk = chunk & 0xFFFFFFF8;\r
198         }\r
199         startScsiRx(chunk);\r
200 \r
201         while (i < count && likely(!scsiDev.resetFlag))\r
202         {\r
203                 while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) {}\r
204                 scsiPhyFifoFlip();\r
205 \r
206                 uint32_t nextChunk = ((count - i - chunk) > SCSI_FIFO_DEPTH)\r
207                         ? SCSI_FIFO_DEPTH : (count - i - chunk);\r
208                 if (nextChunk >= 16)\r
209                 {\r
210                         nextChunk = nextChunk & 0xFFFFFFF8;\r
211                 }\r
212                 if (nextChunk > 0)\r
213                 {\r
214                         startScsiRx(nextChunk);\r
215                 }\r
216 \r
217                 if (chunk < 16)\r
218                 {\r
219                         scsiReadPIO(data + i, chunk);\r
220                 }\r
221                 else\r
222                 {\r
223                         scsiReadDMA(data + i, chunk);\r
224 \r
225                         trace(trace_spinReadDMAPoll);\r
226 \r
227                         while (!scsiReadDMAPoll() && likely(!scsiDev.resetFlag))\r
228                         {\r
229                         };\r
230                 }\r
231 \r
232 \r
233                 i += chunk;\r
234                 chunk = nextChunk;\r
235         }\r
236 #if 1\r
237                 if (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty()) {\r
238                         int j = 0;\r
239                         while (!scsiPhyFifoEmpty()) { scsiPhyRx(); ++j; }\r
240                         scsiPhyFifoFlip();\r
241                         int k = 0;\r
242                         while (!scsiPhyFifoEmpty()) { scsiPhyRx(); ++k; }\r
243                         // Force a lock-up.\r
244                         assertFail();\r
245                 }\r
246 #endif\r
247 }\r
248 \r
249 void\r
250 scsiWriteByte(uint8_t value)\r
251 {\r
252 #if FIFODEBUG\r
253         if (!scsiPhyFifoEmpty()) {\r
254                 // Force a lock-up.\r
255                 assertFail();\r
256         }\r
257 #endif\r
258         trace(trace_spinPhyTxFifo);\r
259         scsiPhyTx(value);\r
260         scsiPhyFifoFlip();\r
261 \r
262         trace(trace_spinTxComplete);\r
263         while (!scsiPhyComplete() && likely(!scsiDev.resetFlag)) {}\r
264 \r
265 #if FIFODEBUG\r
266         if (!scsiPhyFifoAltEmpty()) {\r
267                 // Force a lock-up.\r
268                 assertFail();\r
269         }\r
270 #endif\r
271 }\r
272 \r
273 static void\r
274 scsiWritePIO(const uint8_t* data, uint32_t count)\r
275 {\r
276         for (int i = 0; i < count; ++i)\r
277         {\r
278                 scsiPhyTx(data[i]);\r
279         }\r
280 }\r
281 \r
282 void\r
283 scsiWriteDMA(const uint8_t* data, uint32_t count)\r
284 {\r
285         // Prepare DMA transfer\r
286         dmaInProgress = 1;\r
287         trace(trace_doTxSingleDMA);\r
288 \r
289         scsiTxDMAComplete = 0;\r
290         scsiRxDMAComplete = 1;\r
291 \r
292         HAL_DMA_Start(\r
293                 &memToFSMC,\r
294                 (uint32_t) data,\r
295                 (uint32_t) SCSI_FIFO_DATA,\r
296                 count / 4);\r
297 }\r
298 \r
299 int\r
300 scsiWriteDMAPoll()\r
301 {\r
302         int complete = __HAL_DMA_GET_COUNTER(&memToFSMC) == 0;\r
303         complete = complete && (HAL_DMA_PollForTransfer(&memToFSMC, HAL_DMA_FULL_TRANSFER, 0xffffffff) == HAL_OK);\r
304         if (complete)\r
305         {\r
306                 scsiTxDMAComplete = 1; // TODO MM FIX IRQ\r
307                 scsiRxDMAComplete = 1;\r
308 \r
309                 dmaInProgress = 0;\r
310                 return 1;\r
311         }\r
312         else\r
313         {\r
314                 return 0;\r
315         }\r
316 }\r
317 \r
318 void\r
319 scsiWrite(const uint8_t* data, uint32_t count)\r
320 {\r
321         int i = 0;\r
322         while (i < count && likely(!scsiDev.resetFlag))\r
323         {\r
324                 uint32_t chunk = ((count - i) > SCSI_FIFO_DEPTH)\r
325                         ? SCSI_FIFO_DEPTH : (count - i);\r
326 \r
327 #if FIFODEBUG\r
328                 if (!scsiPhyFifoEmpty()) {\r
329                         // Force a lock-up.\r
330                         assertFail();\r
331                 }\r
332 #endif\r
333 \r
334                 if (chunk < 16)\r
335                 {\r
336                         scsiWritePIO(data + i, chunk);\r
337                 }\r
338                 else\r
339                 {\r
340                         // DMA is doing 32bit transfers.\r
341                         chunk = chunk & 0xFFFFFFF8;\r
342                         scsiWriteDMA(data + i, chunk);\r
343 \r
344                         trace(trace_spinReadDMAPoll);\r
345 \r
346                         while (!scsiWriteDMAPoll() && likely(!scsiDev.resetFlag))\r
347                         {\r
348                         }\r
349                 }\r
350 \r
351                 while (!scsiPhyComplete() && likely(!scsiDev.resetFlag))\r
352                 {\r
353                 }\r
354 \r
355 #if FIFODEBUG\r
356                 if (!scsiPhyFifoAltEmpty()) {\r
357                         // Force a lock-up.\r
358                         assertFail();\r
359                 }\r
360 #endif\r
361 \r
362                 scsiPhyFifoFlip();\r
363                 i += chunk;\r
364         }\r
365         while (!scsiPhyComplete() && likely(!scsiDev.resetFlag))\r
366         {\r
367         }\r
368 \r
369 #if FIFODEBUG\r
370         if (!scsiPhyFifoAltEmpty()) {\r
371                 // Force a lock-up.\r
372                 assertFail();\r
373         }\r
374 #endif\r
375 }\r
376 \r
377 static inline void busSettleDelay(void)\r
378 {\r
379         // Data Release time (switching IO) = 400ns\r
380         // + Bus Settle time (switching phase) = 400ns.\r
381         s2s_delay_us(1); // Close enough.\r
382 }\r
383 \r
384 void scsiEnterBusFree()\r
385 {\r
386         *SCSI_CTRL_BSY = 0x00;\r
387         // We now have a Bus Clear Delay of 800ns to release remaining signals.\r
388         *SCSI_CTRL_PHASE = 0;\r
389 }\r
390 \r
391 void scsiEnterPhase(int phase)\r
392 {\r
393         // ANSI INCITS 362-2002 SPI-3 10.7.1:\r
394         // Phase changes are not allowed while REQ or ACK is asserted.\r
395         while (likely(!scsiDev.resetFlag) && scsiStatusACK()) {}\r
396 \r
397         int newPhase = phase > 0 ? phase : 0;\r
398         int oldPhase = *SCSI_CTRL_PHASE;\r
399 \r
400         if (!scsiDev.resetFlag && (!scsiPhyFifoEmpty() || !scsiPhyFifoAltEmpty())) {\r
401                 // Force a lock-up.\r
402                 assertFail();\r
403         }\r
404         if (newPhase != oldPhase)\r
405         {\r
406                 if ((newPhase == DATA_IN || newPhase == DATA_OUT) &&\r
407                         scsiDev.target->syncOffset)\r
408                 {\r
409                         if (scsiDev.target->syncPeriod == 25)\r
410                         {\r
411                                 // SCSI2 FAST Timing. 10MB/s.\r
412                                 *SCSI_CTRL_TIMING = SCSI_FAST_TIMING;\r
413                         } else {\r
414                                 // 5MB/s Timing\r
415                                 *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
416                         }\r
417                         *SCSI_CTRL_SYNC_OFFSET = scsiDev.target->syncOffset;\r
418                 } else {\r
419                         *SCSI_CTRL_SYNC_OFFSET = 0;\r
420                         *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
421                 }\r
422 \r
423                 *SCSI_CTRL_PHASE = newPhase;\r
424                 busSettleDelay();\r
425 \r
426                 if (scsiDev.compatMode < COMPAT_SCSI2)\r
427                 {\r
428                         s2s_delay_us(100);\r
429                 }\r
430 \r
431         }\r
432 }\r
433 \r
434 void scsiPhyReset()\r
435 {\r
436         trace(trace_scsiPhyReset);\r
437         if (dmaInProgress)\r
438         {\r
439                 trace(trace_spinDMAReset);\r
440                 HAL_DMA_Abort(&memToFSMC);\r
441                 HAL_DMA_Abort(&fsmcToMem);\r
442 \r
443                 dmaInProgress = 0;\r
444         }\r
445 #if 0\r
446 \r
447         // Set the Clear bits for both SCSI device FIFOs\r
448         scsiTarget_AUX_CTL = scsiTarget_AUX_CTL | 0x03;\r
449 \r
450         // Trigger RST outselves.  It is connected to the datapath and will\r
451         // ensure it returns to the idle state.  The datapath runs at the BUS clk\r
452         // speed (ie. same as the CPU), so we can be sure it is active for a sufficient\r
453         // duration.\r
454         SCSI_RST_ISR_Disable();\r
455         SCSI_SetPin(SCSI_Out_RST);\r
456 \r
457         SCSI_CTL_PHASE_Write(0);\r
458         SCSI_ClearPin(SCSI_Out_ATN);\r
459         SCSI_ClearPin(SCSI_Out_BSY);\r
460         SCSI_ClearPin(SCSI_Out_ACK);\r
461         SCSI_ClearPin(SCSI_Out_RST);\r
462         SCSI_ClearPin(SCSI_Out_SEL);\r
463         SCSI_ClearPin(SCSI_Out_REQ);\r
464 \r
465         // Allow the FIFOs to fill up again.\r
466         SCSI_ClearPin(SCSI_Out_RST);\r
467         SCSI_RST_ISR_Enable();\r
468         scsiTarget_AUX_CTL = scsiTarget_AUX_CTL & ~(0x03);\r
469 \r
470         SCSI_Parity_Error_Read(); // clear sticky bits\r
471 #endif\r
472 \r
473         *SCSI_CTRL_PHASE = 0x00;\r
474         *SCSI_CTRL_BSY = 0x00;\r
475         s2s_fpgaReset(); // Clears fifos etc.\r
476 \r
477         scsiPhyFifoSel = 0;\r
478         *SCSI_FIFO_SEL = 0;\r
479         *SCSI_CTRL_DBX = 0;\r
480 \r
481         *SCSI_CTRL_SYNC_OFFSET = 0;\r
482         *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
483 \r
484         // DMA Benchmark code\r
485         // Currently 10MB/s. Assume 20MB/s is achievable with 16 bits.\r
486         #ifdef DMA_BENCHMARK\r
487         while(1)\r
488         {\r
489                 s2s_ledOn();\r
490                 // 100MB\r
491                 for (int i = 0; i < (100LL * 1024 * 1024 / SCSI_FIFO_DEPTH); ++i)\r
492                 {\r
493                         HAL_DMA_Start(\r
494                                 &memToFSMC,\r
495                                 (uint32_t) &scsiDev.data[0],\r
496                                 (uint32_t) SCSI_FIFO_DATA,\r
497                                 SCSI_FIFO_DEPTH / 4);\r
498 \r
499                         HAL_DMA_PollForTransfer(\r
500                                 &memToFSMC,\r
501                                 HAL_DMA_FULL_TRANSFER,\r
502                                 0xffffffff);\r
503 \r
504                         s2s_fpgaReset();\r
505                 }\r
506                 s2s_ledOff();\r
507 \r
508                 for(int i = 0; i < 10; ++i) s2s_delay_ms(1000);\r
509         }\r
510         #endif\r
511 \r
512         // FPGA comms test code\r
513         #ifdef FPGA_TEST\r
514         while(1)\r
515         {\r
516                 for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)\r
517                 {\r
518                         scsiDev.data[j] = j;\r
519                 }\r
520 \r
521                 if (!scsiPhyFifoEmpty())\r
522                 {\r
523                         assertFail();\r
524                 }\r
525 \r
526                 *SCSI_CTRL_PHASE = DATA_IN;\r
527                 HAL_DMA_Start(\r
528                         &memToFSMC,\r
529                         (uint32_t) &scsiDev.data[0],\r
530                         (uint32_t) SCSI_FIFO_DATA,\r
531                         SCSI_FIFO_DEPTH / 4);\r
532 \r
533                 HAL_DMA_PollForTransfer(\r
534                         &memToFSMC,\r
535                         HAL_DMA_FULL_TRANSFER,\r
536                         0xffffffff);\r
537 \r
538                 if (!scsiPhyFifoFull())\r
539                 {\r
540                         assertFail();\r
541                 }\r
542 \r
543                 memset(&scsiDev.data[0], 0, SCSI_FIFO_DEPTH);\r
544 \r
545                 *SCSI_CTRL_PHASE = DATA_OUT;\r
546                 HAL_DMA_Start(\r
547                         &fsmcToMem,\r
548                         (uint32_t) SCSI_FIFO_DATA,\r
549                         (uint32_t) &scsiDev.data[0],\r
550                         SCSI_FIFO_DEPTH);\r
551 \r
552                 HAL_DMA_PollForTransfer(\r
553                         &fsmcToMem,\r
554                         HAL_DMA_FULL_TRANSFER,\r
555                         0xffffffff);\r
556 \r
557                 if (!scsiPhyFifoEmpty())\r
558                 {\r
559                         assertFail();\r
560                 }\r
561 \r
562 \r
563                 for (int j = 0; j < SCSI_FIFO_DEPTH; ++j)\r
564                 {\r
565                         if (scsiDev.data[j] != (uint8_t) j)\r
566                         {\r
567                                 assertFail();\r
568                         }\r
569                 }\r
570 \r
571                 s2s_fpgaReset();\r
572 \r
573         }\r
574         #endif\r
575 \r
576 }\r
577 \r
578 static void scsiPhyInitDMA()\r
579 {\r
580         // One-time init only.\r
581         static uint8_t init = 0;\r
582         if (init == 0)\r
583         {\r
584                 init = 1;\r
585 \r
586                 // Memory to memory transfers can only be done using DMA2\r
587                 __DMA2_CLK_ENABLE();\r
588 \r
589                 // Transmit SCSI data. The source data is treated as the\r
590                 // peripheral (even though this is memory-to-memory)\r
591                 memToFSMC.Instance = DMA2_Stream0;\r
592                 memToFSMC.Init.Channel = DMA_CHANNEL_0;\r
593                 memToFSMC.Init.Direction = DMA_MEMORY_TO_MEMORY;\r
594                 memToFSMC.Init.PeriphInc = DMA_PINC_ENABLE;\r
595                 memToFSMC.Init.MemInc = DMA_MINC_DISABLE;\r
596                 memToFSMC.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;\r
597                 memToFSMC.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;\r
598                 memToFSMC.Init.Mode = DMA_NORMAL;\r
599                 memToFSMC.Init.Priority = DMA_PRIORITY_LOW;\r
600                 // FIFO mode is needed to allow conversion from 32bit words to the\r
601                 // 8bit FSMC interface.\r
602                 memToFSMC.Init.FIFOMode = DMA_FIFOMODE_ENABLE;\r
603 \r
604                 // We only use 1 word (4 bytes) in the fifo at a time. Normally it's\r
605                 // better to let the DMA fifo fill up then do burst transfers, but\r
606                 // bursting out the FSMC interface will be very slow and may starve\r
607                 // other (faster) transfers. We don't want to risk the SDIO transfers\r
608                 // from overrun/underrun conditions.\r
609                 memToFSMC.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;\r
610                 memToFSMC.Init.MemBurst = DMA_MBURST_SINGLE;\r
611                 memToFSMC.Init.PeriphBurst = DMA_PBURST_SINGLE;\r
612                 HAL_DMA_Init(&memToFSMC);\r
613 \r
614                 // Receive SCSI data. The source data (fsmc) is treated as the\r
615                 // peripheral (even though this is memory-to-memory)\r
616                 fsmcToMem.Instance = DMA2_Stream1;\r
617                 fsmcToMem.Init.Channel = DMA_CHANNEL_0;\r
618                 fsmcToMem.Init.Direction = DMA_MEMORY_TO_MEMORY;\r
619                 fsmcToMem.Init.PeriphInc = DMA_PINC_DISABLE;\r
620                 fsmcToMem.Init.MemInc = DMA_MINC_ENABLE;\r
621                 fsmcToMem.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;\r
622                 fsmcToMem.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;\r
623                 fsmcToMem.Init.Mode = DMA_NORMAL;\r
624                 fsmcToMem.Init.Priority = DMA_PRIORITY_LOW;\r
625                 fsmcToMem.Init.FIFOMode = DMA_FIFOMODE_ENABLE;\r
626                 fsmcToMem.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;\r
627                 fsmcToMem.Init.MemBurst = DMA_MBURST_SINGLE;\r
628                 fsmcToMem.Init.PeriphBurst = DMA_PBURST_SINGLE;\r
629                 HAL_DMA_Init(&fsmcToMem);\r
630 \r
631                 // TODO configure IRQs\r
632         }\r
633 }\r
634 \r
635 \r
636 void scsiPhyInit()\r
637 {\r
638         scsiPhyInitDMA();\r
639 \r
640         *SCSI_CTRL_IDMASK = 0x00; // Reset in scsiPhyConfig\r
641         *SCSI_CTRL_PHASE = 0x00;\r
642         *SCSI_CTRL_BSY = 0x00;\r
643         scsiPhyFifoSel = 0;\r
644         *SCSI_FIFO_SEL = 0;\r
645         *SCSI_CTRL_DBX = 0;\r
646 \r
647         *SCSI_CTRL_SYNC_OFFSET = 0;\r
648         *SCSI_CTRL_TIMING = SCSI_DEFAULT_TIMING;\r
649 \r
650 }\r
651 \r
652 void scsiPhyConfig()\r
653 {\r
654         if (scsiDev.boardCfg.flags6 & S2S_CFG_ENABLE_TERMINATOR)\r
655         {\r
656                 HAL_GPIO_WritePin(nTERM_EN_GPIO_Port, nTERM_EN_Pin, GPIO_PIN_RESET);\r
657         }\r
658         else\r
659         {\r
660                 HAL_GPIO_WritePin(nTERM_EN_GPIO_Port, nTERM_EN_Pin, GPIO_PIN_SET);\r
661         }\r
662 \r
663 \r
664         uint8_t idMask = 0;\r
665         for (int i = 0; i < 8; ++i)\r
666         {\r
667                 const S2S_TargetCfg* cfg = s2s_getConfigById(i);\r
668                 if (cfg && (cfg->scsiId & S2S_CFG_TARGET_ENABLED))\r
669                 {\r
670                         idMask |= (1 << i);\r
671                 }\r
672         }\r
673         *SCSI_CTRL_IDMASK = idMask;\r
674 }\r
675 \r
676 \r
677 // 1 = DBx error\r
678 // 2 = Parity error\r
679 // 4 = MSG error\r
680 // 8 = CD error\r
681 // 16 = IO error\r
682 // 32 = other error\r
683 int scsiSelfTest()\r
684 {\r
685         if (scsiDev.phase != BUS_FREE)\r
686         {\r
687                 return 32;\r
688         }\r
689 \r
690         // Acquire the SCSI bus.\r
691         for (int i = 0; i < 100; ++i)\r
692         {\r
693                 if (scsiStatusBSY())\r
694                 {\r
695                         s2s_delay_ms(1);\r
696                 }\r
697         }\r
698         if (scsiStatusBSY())\r
699         {\r
700                 // Error, couldn't acquire scsi bus\r
701                 return 32;\r
702         }\r
703         *SCSI_CTRL_BSY = 1;\r
704         if (! scsiStatusBSY())\r
705         {\r
706                 // Error, BSY doesn't work.\r
707                 return 32;\r
708         }\r
709 \r
710         // Should be safe to use the bus now.\r
711 \r
712         int result = 0;\r
713 \r
714         // TEST DBx\r
715         // TODO test DBp\r
716         int i;\r
717         for (i = 0; i < 256; ++i)\r
718         {\r
719                 *SCSI_CTRL_DBX = i;\r
720                 busSettleDelay();\r
721                 if (*SCSI_STS_DBX != (i & 0xff))\r
722                 {\r
723                         result |= 1;\r
724                 }\r
725                 /*if (Lookup_OddParity[i & 0xff] != SCSI_ReadPin(SCSI_In_DBP))\r
726                 {\r
727                         result |= 2;\r
728                 }*/\r
729         }\r
730         *SCSI_CTRL_DBX = 0;\r
731 \r
732         // TEST MSG, CD, IO\r
733         /* TODO\r
734         for (i = 0; i < 8; ++i)\r
735         {\r
736                 SCSI_CTL_PHASE_Write(i);\r
737                 scsiDeskewDelay();\r
738 \r
739                 if (SCSI_ReadPin(SCSI_In_MSG) != !!(i & __scsiphase_msg))\r
740                 {\r
741                         result |= 4;\r
742                 }\r
743                 if (SCSI_ReadPin(SCSI_In_CD) != !!(i & __scsiphase_cd))\r
744                 {\r
745                         result |= 8;\r
746                 }\r
747                 if (SCSI_ReadPin(SCSI_In_IO) != !!(i & __scsiphase_io))\r
748                 {\r
749                         result |= 16;\r
750                 }\r
751         }\r
752         SCSI_CTL_PHASE_Write(0);\r
753 \r
754         uint32_t signalsOut[] = { SCSI_Out_ATN, SCSI_Out_BSY, SCSI_Out_RST, SCSI_Out_SEL };\r
755         uint32_t signalsIn[] = { SCSI_Filt_ATN, SCSI_Filt_BSY, SCSI_Filt_RST, SCSI_Filt_SEL };\r
756 \r
757         for (i = 0; i < 4; ++i)\r
758         {\r
759                 SCSI_SetPin(signalsOut[i]);\r
760                 scsiDeskewDelay();\r
761 \r
762                 int j;\r
763                 for (j = 0; j < 4; ++j)\r
764                 {\r
765                         if (i == j)\r
766                         {\r
767                                 if (! SCSI_ReadFilt(signalsIn[j]))\r
768                                 {\r
769                                         result |= 32;\r
770                                 }\r
771                         }\r
772                         else\r
773                         {\r
774                                 if (SCSI_ReadFilt(signalsIn[j]))\r
775                                 {\r
776                                         result |= 32;\r
777                                 }\r
778                         }\r
779                 }\r
780                 SCSI_ClearPin(signalsOut[i]);\r
781         }\r
782         */\r
783 \r
784         *SCSI_CTRL_BSY = 0;\r
785         return result;\r
786 }\r
787 \r