Small compatibility improvements, and added scsi2sd-monitor test program
[SCSI2SD-V6.git] / software / SCSI2SD / src / scsiPhy.c
CommitLineData
75de1226
MM
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
95b51978
MM
17#pragma GCC push_options\r
18#pragma GCC optimize("-flto")\r
75de1226
MM
19\r
20#include "device.h"\r
21#include "scsi.h"\r
22#include "scsiPhy.h"\r
23#include "bits.h"\r
24\r
030fc25f
MM
25#define scsiTarget_AUX_CTL (* (reg8 *) scsiTarget_datapath__DP_AUX_CTL_REG)\r
26\r
5bcd0c3a
MM
27// DMA controller can't handle any more bytes.\r
28#define MAX_DMA_BYTES 4095\r
29\r
30// Private DMA variables.\r
31static int dmaInProgress = 0;\r
32// used when transferring > MAX_DMA_BYTES.\r
33static uint8_t* dmaBuffer = NULL;\r
34static uint32_t dmaSentCount = 0;\r
35static uint32_t dmaTotalCount = 0;\r
36\r
37static uint8 scsiDmaRxChan = CY_DMA_INVALID_CHANNEL;\r
38static uint8 scsiDmaTxChan = CY_DMA_INVALID_CHANNEL;\r
39\r
40// DMA descriptors\r
41static uint8 scsiDmaRxTd[1] = { CY_DMA_INVALID_TD };\r
42static uint8 scsiDmaTxTd[1] = { CY_DMA_INVALID_TD };\r
43\r
44// Source of dummy bytes for DMA reads\r
45static uint8 dummyBuffer = 0xFF;\r
46\r
95b51978
MM
47volatile uint8_t scsiRxDMAComplete;\r
48volatile uint8_t scsiTxDMAComplete;\r
5bcd0c3a
MM
49\r
50CY_ISR_PROTO(scsiRxCompleteISR);\r
51CY_ISR(scsiRxCompleteISR)\r
52{\r
95b51978 53 scsiRxDMAComplete = 1;\r
5bcd0c3a
MM
54}\r
55\r
56CY_ISR_PROTO(scsiTxCompleteISR);\r
57CY_ISR(scsiTxCompleteISR)\r
58{\r
95b51978 59 scsiTxDMAComplete = 1;\r
5bcd0c3a
MM
60}\r
61\r
cc6921e4
MM
62CY_ISR_PROTO(scsiResetISR);\r
63CY_ISR(scsiResetISR)\r
64{\r
65 scsiDev.resetFlag = 1;\r
cc6921e4
MM
66}\r
67\r
5bcd0c3a
MM
68uint8_t\r
69scsiReadDBxPins()\r
c693c7fa
MM
70{\r
71 return\r
72 (SCSI_ReadPin(SCSI_In_DBx_DB7) << 7) |\r
73 (SCSI_ReadPin(SCSI_In_DBx_DB6) << 6) |\r
74 (SCSI_ReadPin(SCSI_In_DBx_DB5) << 5) |\r
75 (SCSI_ReadPin(SCSI_In_DBx_DB4) << 4) |\r
76 (SCSI_ReadPin(SCSI_In_DBx_DB3) << 3) |\r
77 (SCSI_ReadPin(SCSI_In_DBx_DB2) << 2) |\r
78 (SCSI_ReadPin(SCSI_In_DBx_DB1) << 1) |\r
5bcd0c3a 79 SCSI_ReadPin(SCSI_In_DBx_DB0);\r
c693c7fa
MM
80}\r
81\r
5bcd0c3a
MM
82uint8_t\r
83scsiReadByte(void)\r
75de1226 84{\r
95b51978 85 while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}\r
5bcd0c3a
MM
86 scsiPhyTx(0);\r
87\r
95b51978 88 while (scsiPhyRxFifoEmpty() && likely(!scsiDev.resetFlag)) {}\r
5bcd0c3a 89 uint8_t val = scsiPhyRx();\r
7db82a4e 90 scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
5bcd0c3a 91\r
95b51978 92 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
5bcd0c3a
MM
93\r
94 return val;\r
75de1226
MM
95}\r
96\r
5bcd0c3a
MM
97static void\r
98scsiReadPIO(uint8* data, uint32 count)\r
75de1226 99{\r
b9ed3652
MM
100 int prep = 0;\r
101 int i = 0;\r
102\r
95b51978 103 while (i < count && likely(!scsiDev.resetFlag))\r
75de1226 104 {\r
5bcd0c3a
MM
105 uint8_t status = scsiPhyStatus();\r
106\r
107 if (prep < count && (status & SCSI_PHY_TX_FIFO_NOT_FULL))\r
b9ed3652 108 {\r
5bcd0c3a 109 scsiPhyTx(0);\r
b9ed3652
MM
110 ++prep;\r
111 }\r
5bcd0c3a 112 if (status & SCSI_PHY_RX_FIFO_NOT_EMPTY)\r
b9ed3652 113 {\r
5bcd0c3a 114 data[i] = scsiPhyRx();\r
b9ed3652
MM
115 ++i;\r
116 }\r
75de1226 117 }\r
7db82a4e 118 scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
95b51978 119 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
75de1226
MM
120}\r
121\r
5bcd0c3a
MM
122static void\r
123doRxSingleDMA(uint8* data, uint32 count)\r
75de1226 124{\r
5bcd0c3a
MM
125 // Prepare DMA transfer\r
126 dmaInProgress = 1;\r
75de1226 127\r
5bcd0c3a
MM
128 CyDmaTdSetConfiguration(\r
129 scsiDmaTxTd[0],\r
130 count,\r
131 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes\r
132 SCSI_TX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete\r
133 );\r
134 CyDmaTdSetConfiguration(\r
135 scsiDmaRxTd[0],\r
136 count,\r
137 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes\r
138 TD_INC_DST_ADR |\r
139 SCSI_RX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete\r
140 );\r
95b51978 141\r
5bcd0c3a
MM
142 CyDmaTdSetAddress(\r
143 scsiDmaTxTd[0],\r
144 LO16((uint32)&dummyBuffer),\r
145 LO16((uint32)scsiTarget_datapath__F0_REG));\r
146 CyDmaTdSetAddress(\r
147 scsiDmaRxTd[0],\r
148 LO16((uint32)scsiTarget_datapath__F1_REG),\r
149 LO16((uint32)data)\r
150 );\r
95b51978 151\r
5bcd0c3a
MM
152 CyDmaChSetInitialTd(scsiDmaTxChan, scsiDmaTxTd[0]);\r
153 CyDmaChSetInitialTd(scsiDmaRxChan, scsiDmaRxTd[0]);\r
95b51978 154\r
5bcd0c3a
MM
155 // The DMA controller is a bit trigger-happy. It will retain\r
156 // a drq request that was triggered while the channel was\r
157 // disabled.\r
158 CyDmaClearPendingDrq(scsiDmaTxChan);\r
159 CyDmaClearPendingDrq(scsiDmaRxChan);\r
160\r
95b51978
MM
161 scsiTxDMAComplete = 0;\r
162 scsiRxDMAComplete = 0;\r
5bcd0c3a
MM
163\r
164 CyDmaChEnable(scsiDmaRxChan, 1);\r
165 CyDmaChEnable(scsiDmaTxChan, 1);\r
166}\r
167\r
168void\r
169scsiReadDMA(uint8* data, uint32 count)\r
170{\r
171 dmaSentCount = 0;\r
172 dmaTotalCount = count;\r
173 dmaBuffer = data;\r
174\r
175 uint32_t singleCount = (count > MAX_DMA_BYTES) ? MAX_DMA_BYTES : count;\r
176 doRxSingleDMA(data, singleCount);\r
177 dmaSentCount += count;\r
178}\r
179\r
180int\r
181scsiReadDMAPoll()\r
182{\r
95b51978 183 if (scsiTxDMAComplete && scsiRxDMAComplete)\r
5bcd0c3a 184 {\r
95b51978
MM
185 // Wait until our scsi signals are consistent. This should only be\r
186 // a few cycles.\r
187 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE)) {}\r
188\r
189 if (likely(dmaSentCount == dmaTotalCount))\r
5bcd0c3a
MM
190 {\r
191 dmaInProgress = 0;\r
7db82a4e 192 scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();\r
5bcd0c3a
MM
193 return 1;\r
194 }\r
195 else\r
196 {\r
197 // Transfer was too large for a single DMA transfer. Continue\r
198 // to send remaining bytes.\r
199 uint32_t count = dmaTotalCount - dmaSentCount;\r
95b51978 200 if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;\r
5bcd0c3a
MM
201 doRxSingleDMA(dmaBuffer + dmaSentCount, count);\r
202 dmaSentCount += count;\r
203 return 0;\r
204 }\r
205 }\r
206 else\r
207 {\r
208 return 0;\r
209 }\r
210}\r
211\r
212void\r
213scsiRead(uint8_t* data, uint32_t count)\r
214{\r
215 if (count < 8)\r
216 {\r
217 scsiReadPIO(data, count);\r
218 }\r
219 else\r
220 {\r
221 scsiReadDMA(data, count);\r
95b51978
MM
222 \r
223 // Wait for the next DMA interrupt (or the 1ms systick)\r
224 // It's beneficial to halt the processor to\r
225 // give the DMA controller more memory bandwidth to work with.\r
226 __WFI();\r
227 \r
228 while (!scsiReadDMAPoll() && likely(!scsiDev.resetFlag)) {};\r
5bcd0c3a
MM
229 }\r
230}\r
231\r
232void\r
233scsiWriteByte(uint8 value)\r
234{\r
95b51978 235 while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}\r
5bcd0c3a
MM
236 scsiPhyTx(value);\r
237\r
95b51978 238 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
5bcd0c3a 239 scsiPhyRxFifoClear();\r
75de1226
MM
240}\r
241\r
5bcd0c3a 242static void\r
95b51978 243scsiWritePIO(const uint8_t* data, uint32_t count)\r
75de1226 244{\r
b9ed3652
MM
245 int i = 0;\r
246\r
95b51978 247 while (i < count && likely(!scsiDev.resetFlag))\r
75de1226 248 {\r
5bcd0c3a 249 if (!scsiPhyTxFifoFull())\r
b9ed3652 250 {\r
5bcd0c3a
MM
251 scsiPhyTx(data[i]);\r
252 ++i;\r
b9ed3652 253 }\r
5bcd0c3a
MM
254 }\r
255\r
95b51978 256 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}\r
5bcd0c3a
MM
257 scsiPhyRxFifoClear();\r
258}\r
259\r
260static void\r
95b51978 261doTxSingleDMA(const uint8* data, uint32 count)\r
5bcd0c3a
MM
262{\r
263 // Prepare DMA transfer\r
264 dmaInProgress = 1;\r
265\r
266 CyDmaTdSetConfiguration(\r
267 scsiDmaTxTd[0],\r
268 count,\r
269 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes\r
270 TD_INC_SRC_ADR |\r
271 SCSI_TX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete\r
272 );\r
273 CyDmaTdSetAddress(\r
274 scsiDmaTxTd[0],\r
275 LO16((uint32)data),\r
276 LO16((uint32)scsiTarget_datapath__F0_REG));\r
277 CyDmaChSetInitialTd(scsiDmaTxChan, scsiDmaTxTd[0]);\r
278\r
279 // The DMA controller is a bit trigger-happy. It will retain\r
280 // a drq request that was triggered while the channel was\r
281 // disabled.\r
282 CyDmaClearPendingDrq(scsiDmaTxChan);\r
283\r
95b51978
MM
284 scsiTxDMAComplete = 0;\r
285 scsiRxDMAComplete = 1;\r
5bcd0c3a
MM
286\r
287 CyDmaChEnable(scsiDmaTxChan, 1);\r
288}\r
289\r
290void\r
95b51978 291scsiWriteDMA(const uint8* data, uint32 count)\r
5bcd0c3a
MM
292{\r
293 dmaSentCount = 0;\r
294 dmaTotalCount = count;\r
295 dmaBuffer = data;\r
296\r
297 uint32_t singleCount = (count > MAX_DMA_BYTES) ? MAX_DMA_BYTES : count;\r
298 doTxSingleDMA(data, singleCount);\r
299 dmaSentCount += count;\r
300}\r
301\r
302int\r
303scsiWriteDMAPoll()\r
304{\r
95b51978 305 if (scsiTxDMAComplete)\r
5bcd0c3a 306 {\r
95b51978
MM
307 // Wait until our scsi signals are consistent. This should only be\r
308 // a few cycles.\r
309 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE)) {}\r
310\r
311 if (likely(dmaSentCount == dmaTotalCount))\r
b9ed3652 312 {\r
5bcd0c3a
MM
313 scsiPhyRxFifoClear();\r
314 dmaInProgress = 0;\r
5bcd0c3a
MM
315 return 1;\r
316 }\r
317 else\r
318 {\r
319 // Transfer was too large for a single DMA transfer. Continue\r
320 // to send remaining bytes.\r
321 uint32_t count = dmaTotalCount - dmaSentCount;\r
95b51978 322 if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;\r
5bcd0c3a
MM
323 doTxSingleDMA(dmaBuffer + dmaSentCount, count);\r
324 dmaSentCount += count;\r
325 return 0;\r
b9ed3652 326 }\r
75de1226 327 }\r
5bcd0c3a
MM
328 else\r
329 {\r
330 return 0;\r
331 }\r
332}\r
333\r
334void\r
95b51978 335scsiWrite(const uint8_t* data, uint32_t count)\r
5bcd0c3a
MM
336{\r
337 if (count < 8)\r
338 {\r
339 scsiWritePIO(data, count);\r
340 }\r
341 else\r
342 {\r
343 scsiWriteDMA(data, count);\r
95b51978
MM
344 \r
345 // Wait for the next DMA interrupt (or the 1ms systick)\r
346 // It's beneficial to halt the processor to\r
347 // give the DMA controller more memory bandwidth to work with.\r
348 __WFI();\r
349\r
350 while (!scsiWriteDMAPoll() && likely(!scsiDev.resetFlag)) {};\r
5bcd0c3a 351 }\r
75de1226
MM
352}\r
353\r
95b51978 354static inline void busSettleDelay(void)\r
75de1226
MM
355{\r
356 // Data Release time (switching IO) = 400ns\r
357 // + Bus Settle time (switching phase) = 400ns.\r
358 CyDelayUs(1); // Close enough.\r
359}\r
360\r
361void scsiEnterPhase(int phase)\r
362{\r
3762d599
MM
363 int newPhase = phase > 0 ? phase : 0;\r
364 if (newPhase != SCSI_CTL_PHASE_Read())\r
75de1226 365 {\r
3762d599
MM
366 SCSI_CTL_PHASE_Write(phase > 0 ? phase : 0);\r
367 busSettleDelay();\r
75de1226 368 }\r
75de1226
MM
369}\r
370\r
030fc25f
MM
371void scsiPhyReset()\r
372{\r
5bcd0c3a
MM
373 if (dmaInProgress)\r
374 {\r
375 dmaInProgress = 0;\r
376 dmaBuffer = NULL;\r
377 dmaSentCount = 0;\r
378 dmaTotalCount = 0;\r
379 CyDmaChSetRequest(scsiDmaTxChan, CY_DMA_CPU_TERM_CHAIN);\r
380 CyDmaChSetRequest(scsiDmaRxChan, CY_DMA_CPU_TERM_CHAIN);\r
95b51978 381 while (!(scsiTxDMAComplete && scsiRxDMAComplete)) {}\r
5bcd0c3a
MM
382\r
383 CyDmaChDisable(scsiDmaTxChan);\r
384 CyDmaChDisable(scsiDmaRxChan);\r
385 }\r
386\r
030fc25f
MM
387 // Set the Clear bits for both SCSI device FIFOs\r
388 scsiTarget_AUX_CTL = scsiTarget_AUX_CTL | 0x03;\r
389\r
390 // Trigger RST outselves. It is connected to the datapath and will\r
391 // ensure it returns to the idle state. The datapath runs at the BUS clk\r
392 // speed (ie. same as the CPU), so we can be sure it is active for a sufficient\r
393 // duration.\r
394 SCSI_SetPin(SCSI_Out_RST);\r
395\r
3762d599 396 SCSI_CTL_PHASE_Write(0);\r
030fc25f
MM
397 SCSI_ClearPin(SCSI_Out_ATN);\r
398 SCSI_ClearPin(SCSI_Out_BSY);\r
399 SCSI_ClearPin(SCSI_Out_ACK);\r
400 SCSI_ClearPin(SCSI_Out_RST);\r
401 SCSI_ClearPin(SCSI_Out_SEL);\r
402 SCSI_ClearPin(SCSI_Out_REQ);\r
030fc25f
MM
403\r
404 // Allow the FIFOs to fill up again.\r
405 SCSI_ClearPin(SCSI_Out_RST);\r
406 scsiTarget_AUX_CTL = scsiTarget_AUX_CTL & ~(0x03);\r
7db82a4e
MM
407\r
408 SCSI_Parity_Error_Read(); // clear sticky bits\r
030fc25f
MM
409}\r
410\r
5bcd0c3a
MM
411static void scsiPhyInitDMA()\r
412{\r
413 // One-time init only.\r
414 if (scsiDmaTxChan == CY_DMA_INVALID_CHANNEL)\r
415 {\r
416 scsiDmaRxChan =\r
417 SCSI_RX_DMA_DmaInitialize(\r
418 1, // Bytes per burst\r
419 1, // request per burst\r
420 HI16(CYDEV_PERIPH_BASE),\r
421 HI16(CYDEV_SRAM_BASE)\r
422 );\r
7db82a4e 423\r
5bcd0c3a
MM
424 scsiDmaTxChan =\r
425 SCSI_TX_DMA_DmaInitialize(\r
426 1, // Bytes per burst\r
427 1, // request per burst\r
428 HI16(CYDEV_SRAM_BASE),\r
429 HI16(CYDEV_PERIPH_BASE)\r
430 );\r
95b51978 431 \r
5bcd0c3a
MM
432 CyDmaChDisable(scsiDmaRxChan);\r
433 CyDmaChDisable(scsiDmaTxChan);\r
434\r
435 scsiDmaRxTd[0] = CyDmaTdAllocate();\r
436 scsiDmaTxTd[0] = CyDmaTdAllocate();\r
7db82a4e 437\r
5bcd0c3a
MM
438 SCSI_RX_DMA_COMPLETE_StartEx(scsiRxCompleteISR);\r
439 SCSI_TX_DMA_COMPLETE_StartEx(scsiTxCompleteISR);\r
440 }\r
441}\r
442\r
443\r
cc6921e4
MM
444void scsiPhyInit()\r
445{\r
5bcd0c3a
MM
446 scsiPhyInitDMA();\r
447\r
cc6921e4 448 SCSI_RST_ISR_StartEx(scsiResetISR);\r
b9ed3652 449}\r
9ad7cc15
MM
450\r
451// 1 = DBx error\r
452// 2 = Parity error\r
453// 4 = MSG error\r
454// 8 = CD error\r
455// 16 = IO error\r
456// 32 = other error\r
457int scsiSelfTest()\r
458{\r
459 int result = 0;\r
460\r
461 // TEST DBx and DBp\r
462 int i;\r
463 SCSI_Out_Ctl_Write(1); // Write bits manually.\r
464 SCSI_CTL_PHASE_Write(__scsiphase_io); // Needed for parity generation\r
465 for (i = 0; i < 256; ++i)\r
466 {\r
467 SCSI_Out_Bits_Write(i);\r
468 scsiDeskewDelay();\r
469 if (scsiReadDBxPins() != (i & 0xff))\r
470 {\r
471 result |= 1;\r
472 }\r
473 if (Lookup_OddParity[i & 0xff] != SCSI_ReadPin(SCSI_In_DBP))\r
474 {\r
475 result |= 2;\r
476 }\r
477 }\r
478 SCSI_Out_Ctl_Write(0); // Write bits normally.\r
479\r
480 // TEST MSG, CD, IO\r
481 for (i = 0; i < 8; ++i)\r
482 {\r
483 SCSI_CTL_PHASE_Write(i);\r
484 scsiDeskewDelay();\r
485\r
486 if (SCSI_ReadPin(SCSI_In_MSG) != !!(i & __scsiphase_msg))\r
487 {\r
488 result |= 4;\r
489 }\r
490 if (SCSI_ReadPin(SCSI_In_CD) != !!(i & __scsiphase_cd))\r
491 {\r
492 result |= 8;\r
493 }\r
494 if (SCSI_ReadPin(SCSI_In_IO) != !!(i & __scsiphase_io))\r
495 {\r
496 result |= 16;\r
497 }\r
498 }\r
499 SCSI_CTL_PHASE_Write(0);\r
500\r
501 uint32_t signalsOut[] = { SCSI_Out_ATN, SCSI_Out_BSY, SCSI_Out_RST, SCSI_Out_SEL };\r
502 uint32_t signalsIn[] = { SCSI_Filt_ATN, SCSI_Filt_BSY, SCSI_Filt_RST, SCSI_Filt_SEL };\r
503\r
504 for (i = 0; i < 4; ++i)\r
505 {\r
506 SCSI_SetPin(signalsOut[i]);\r
507 scsiDeskewDelay();\r
508\r
509 int j;\r
510 for (j = 0; j < 4; ++j)\r
511 {\r
512 if (i == j)\r
513 {\r
514 if (! SCSI_ReadFilt(signalsIn[j]))\r
515 {\r
516 result |= 32;\r
517 }\r
518 }\r
519 else\r
520 {\r
521 if (SCSI_ReadFilt(signalsIn[j]))\r
522 {\r
523 result |= 32;\r
524 }\r
525 }\r
526 }\r
527 SCSI_ClearPin(signalsOut[i]);\r
528 }\r
529 return result;\r
530}\r
531\r
532\r
95b51978 533#pragma GCC pop_options\r