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