5952b8553b011970d98efb9f228f222ef2b8c198
[SCSI2SD-V6.git] / software / SCSI2SD / src / scsiPhy.c
1 // Copyright (C) 2013 Michael McMaster <michael@codesrc.com>
2 //
3 // This file is part of SCSI2SD.
4 //
5 // SCSI2SD is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // SCSI2SD is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with SCSI2SD. If not, see <http://www.gnu.org/licenses/>.
17 #pragma GCC push_options
18 #pragma GCC optimize("-flto")
19
20 #include "device.h"
21 #include "scsi.h"
22 #include "scsiPhy.h"
23 #include "bits.h"
24
25 #define scsiTarget_AUX_CTL (* (reg8 *) scsiTarget_datapath__DP_AUX_CTL_REG)
26
27 // DMA controller can't handle any more bytes.
28 #define MAX_DMA_BYTES 4095
29
30 // Private DMA variables.
31 static int dmaInProgress = 0;
32 // used when transferring > MAX_DMA_BYTES.
33 static uint8_t* dmaBuffer = NULL;
34 static uint32_t dmaSentCount = 0;
35 static uint32_t dmaTotalCount = 0;
36
37 static uint8 scsiDmaRxChan = CY_DMA_INVALID_CHANNEL;
38 static uint8 scsiDmaTxChan = CY_DMA_INVALID_CHANNEL;
39
40 // DMA descriptors
41 static uint8 scsiDmaRxTd[1] = { CY_DMA_INVALID_TD };
42 static uint8 scsiDmaTxTd[1] = { CY_DMA_INVALID_TD };
43
44 // Source of dummy bytes for DMA reads
45 static uint8 dummyBuffer = 0xFF;
46
47 volatile uint8_t scsiRxDMAComplete;
48 volatile uint8_t scsiTxDMAComplete;
49
50 CY_ISR_PROTO(scsiRxCompleteISR);
51 CY_ISR(scsiRxCompleteISR)
52 {
53 scsiRxDMAComplete = 1;
54 }
55
56 CY_ISR_PROTO(scsiTxCompleteISR);
57 CY_ISR(scsiTxCompleteISR)
58 {
59 scsiTxDMAComplete = 1;
60 }
61
62 CY_ISR_PROTO(scsiResetISR);
63 CY_ISR(scsiResetISR)
64 {
65 scsiDev.resetFlag = 1;
66 }
67
68 uint8_t
69 scsiReadDBxPins()
70 {
71 return
72 (SCSI_ReadPin(SCSI_In_DBx_DB7) << 7) |
73 (SCSI_ReadPin(SCSI_In_DBx_DB6) << 6) |
74 (SCSI_ReadPin(SCSI_In_DBx_DB5) << 5) |
75 (SCSI_ReadPin(SCSI_In_DBx_DB4) << 4) |
76 (SCSI_ReadPin(SCSI_In_DBx_DB3) << 3) |
77 (SCSI_ReadPin(SCSI_In_DBx_DB2) << 2) |
78 (SCSI_ReadPin(SCSI_In_DBx_DB1) << 1) |
79 SCSI_ReadPin(SCSI_In_DBx_DB0);
80 }
81
82 uint8_t
83 scsiReadByte(void)
84 {
85 while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}
86 scsiPhyTx(0);
87
88 while (scsiPhyRxFifoEmpty() && likely(!scsiDev.resetFlag)) {}
89 uint8_t val = scsiPhyRx();
90 scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();
91
92 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}
93
94 return val;
95 }
96
97 static void
98 scsiReadPIO(uint8* data, uint32 count)
99 {
100 int prep = 0;
101 int i = 0;
102
103 while (i < count && likely(!scsiDev.resetFlag))
104 {
105 uint8_t status = scsiPhyStatus();
106
107 if (prep < count && (status & SCSI_PHY_TX_FIFO_NOT_FULL))
108 {
109 scsiPhyTx(0);
110 ++prep;
111 }
112 if (status & SCSI_PHY_RX_FIFO_NOT_EMPTY)
113 {
114 data[i] = scsiPhyRx();
115 ++i;
116 }
117 }
118 scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();
119 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}
120 }
121
122 static void
123 doRxSingleDMA(uint8* data, uint32 count)
124 {
125 // Prepare DMA transfer
126 dmaInProgress = 1;
127
128 CyDmaTdSetConfiguration(
129 scsiDmaTxTd[0],
130 count,
131 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes
132 SCSI_TX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete
133 );
134 CyDmaTdSetConfiguration(
135 scsiDmaRxTd[0],
136 count,
137 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes
138 TD_INC_DST_ADR |
139 SCSI_RX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete
140 );
141
142 CyDmaTdSetAddress(
143 scsiDmaTxTd[0],
144 LO16((uint32)&dummyBuffer),
145 LO16((uint32)scsiTarget_datapath__F0_REG));
146 CyDmaTdSetAddress(
147 scsiDmaRxTd[0],
148 LO16((uint32)scsiTarget_datapath__F1_REG),
149 LO16((uint32)data)
150 );
151
152 CyDmaChSetInitialTd(scsiDmaTxChan, scsiDmaTxTd[0]);
153 CyDmaChSetInitialTd(scsiDmaRxChan, scsiDmaRxTd[0]);
154
155 // The DMA controller is a bit trigger-happy. It will retain
156 // a drq request that was triggered while the channel was
157 // disabled.
158 CyDmaClearPendingDrq(scsiDmaTxChan);
159 CyDmaClearPendingDrq(scsiDmaRxChan);
160
161 scsiTxDMAComplete = 0;
162 scsiRxDMAComplete = 0;
163
164 CyDmaChEnable(scsiDmaRxChan, 1);
165 CyDmaChEnable(scsiDmaTxChan, 1);
166 }
167
168 void
169 scsiReadDMA(uint8* data, uint32 count)
170 {
171 dmaSentCount = 0;
172 dmaTotalCount = count;
173 dmaBuffer = data;
174
175 uint32_t singleCount = (count > MAX_DMA_BYTES) ? MAX_DMA_BYTES : count;
176 doRxSingleDMA(data, singleCount);
177 dmaSentCount += count;
178 }
179
180 int
181 scsiReadDMAPoll()
182 {
183 if (scsiTxDMAComplete && scsiRxDMAComplete)
184 {
185 // Wait until our scsi signals are consistent. This should only be
186 // a few cycles.
187 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE)) {}
188
189 if (likely(dmaSentCount == dmaTotalCount))
190 {
191 dmaInProgress = 0;
192 scsiDev.parityError = scsiDev.parityError || SCSI_Parity_Error_Read();
193 return 1;
194 }
195 else
196 {
197 // Transfer was too large for a single DMA transfer. Continue
198 // to send remaining bytes.
199 uint32_t count = dmaTotalCount - dmaSentCount;
200 if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;
201 doRxSingleDMA(dmaBuffer + dmaSentCount, count);
202 dmaSentCount += count;
203 return 0;
204 }
205 }
206 else
207 {
208 return 0;
209 }
210 }
211
212 void
213 scsiRead(uint8_t* data, uint32_t count)
214 {
215 if (count < 8)
216 {
217 scsiReadPIO(data, count);
218 }
219 else
220 {
221 scsiReadDMA(data, count);
222
223 // Wait for the next DMA interrupt (or the 1ms systick)
224 // It's beneficial to halt the processor to
225 // give the DMA controller more memory bandwidth to work with.
226 __WFI();
227
228 while (!scsiReadDMAPoll() && likely(!scsiDev.resetFlag)) {};
229 }
230 }
231
232 void
233 scsiWriteByte(uint8 value)
234 {
235 while (unlikely(scsiPhyTxFifoFull()) && likely(!scsiDev.resetFlag)) {}
236 scsiPhyTx(value);
237
238 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}
239 scsiPhyRxFifoClear();
240 }
241
242 static void
243 scsiWritePIO(const uint8_t* data, uint32_t count)
244 {
245 int i = 0;
246
247 while (i < count && likely(!scsiDev.resetFlag))
248 {
249 if (!scsiPhyTxFifoFull())
250 {
251 scsiPhyTx(data[i]);
252 ++i;
253 }
254 }
255
256 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE) && likely(!scsiDev.resetFlag)) {}
257 scsiPhyRxFifoClear();
258 }
259
260 static void
261 doTxSingleDMA(const uint8* data, uint32 count)
262 {
263 // Prepare DMA transfer
264 dmaInProgress = 1;
265
266 CyDmaTdSetConfiguration(
267 scsiDmaTxTd[0],
268 count,
269 CY_DMA_DISABLE_TD, // Disable the DMA channel when TD completes count bytes
270 TD_INC_SRC_ADR |
271 SCSI_TX_DMA__TD_TERMOUT_EN // Trigger interrupt when complete
272 );
273 CyDmaTdSetAddress(
274 scsiDmaTxTd[0],
275 LO16((uint32)data),
276 LO16((uint32)scsiTarget_datapath__F0_REG));
277 CyDmaChSetInitialTd(scsiDmaTxChan, scsiDmaTxTd[0]);
278
279 // The DMA controller is a bit trigger-happy. It will retain
280 // a drq request that was triggered while the channel was
281 // disabled.
282 CyDmaClearPendingDrq(scsiDmaTxChan);
283
284 scsiTxDMAComplete = 0;
285 scsiRxDMAComplete = 1;
286
287 CyDmaChEnable(scsiDmaTxChan, 1);
288 }
289
290 void
291 scsiWriteDMA(const uint8* data, uint32 count)
292 {
293 dmaSentCount = 0;
294 dmaTotalCount = count;
295 dmaBuffer = data;
296
297 uint32_t singleCount = (count > MAX_DMA_BYTES) ? MAX_DMA_BYTES : count;
298 doTxSingleDMA(data, singleCount);
299 dmaSentCount += count;
300 }
301
302 int
303 scsiWriteDMAPoll()
304 {
305 if (scsiTxDMAComplete)
306 {
307 // Wait until our scsi signals are consistent. This should only be
308 // a few cycles.
309 while (!(scsiPhyStatus() & SCSI_PHY_TX_COMPLETE)) {}
310
311 if (likely(dmaSentCount == dmaTotalCount))
312 {
313 scsiPhyRxFifoClear();
314 dmaInProgress = 0;
315 return 1;
316 }
317 else
318 {
319 // Transfer was too large for a single DMA transfer. Continue
320 // to send remaining bytes.
321 uint32_t count = dmaTotalCount - dmaSentCount;
322 if (unlikely(count > MAX_DMA_BYTES)) count = MAX_DMA_BYTES;
323 doTxSingleDMA(dmaBuffer + dmaSentCount, count);
324 dmaSentCount += count;
325 return 0;
326 }
327 }
328 else
329 {
330 return 0;
331 }
332 }
333
334 void
335 scsiWrite(const uint8_t* data, uint32_t count)
336 {
337 if (count < 8)
338 {
339 scsiWritePIO(data, count);
340 }
341 else
342 {
343 scsiWriteDMA(data, count);
344
345 // Wait for the next DMA interrupt (or the 1ms systick)
346 // It's beneficial to halt the processor to
347 // give the DMA controller more memory bandwidth to work with.
348 __WFI();
349
350 while (!scsiWriteDMAPoll() && likely(!scsiDev.resetFlag)) {};
351 }
352 }
353
354 static inline void busSettleDelay(void)
355 {
356 // Data Release time (switching IO) = 400ns
357 // + Bus Settle time (switching phase) = 400ns.
358 CyDelayUs(1); // Close enough.
359 }
360
361 void scsiEnterPhase(int phase)
362 {
363 int newPhase = phase > 0 ? phase : 0;
364 if (newPhase != SCSI_CTL_PHASE_Read())
365 {
366 SCSI_CTL_PHASE_Write(phase > 0 ? phase : 0);
367 busSettleDelay();
368 }
369 }
370
371 void scsiPhyReset()
372 {
373 if (dmaInProgress)
374 {
375 dmaInProgress = 0;
376 dmaBuffer = NULL;
377 dmaSentCount = 0;
378 dmaTotalCount = 0;
379 CyDmaChSetRequest(scsiDmaTxChan, CY_DMA_CPU_TERM_CHAIN);
380 CyDmaChSetRequest(scsiDmaRxChan, CY_DMA_CPU_TERM_CHAIN);
381 while (!(scsiTxDMAComplete && scsiRxDMAComplete)) {}
382
383 CyDmaChDisable(scsiDmaTxChan);
384 CyDmaChDisable(scsiDmaRxChan);
385 }
386
387 // Set the Clear bits for both SCSI device FIFOs
388 scsiTarget_AUX_CTL = scsiTarget_AUX_CTL | 0x03;
389
390 // Trigger RST outselves. It is connected to the datapath and will
391 // ensure it returns to the idle state. The datapath runs at the BUS clk
392 // speed (ie. same as the CPU), so we can be sure it is active for a sufficient
393 // duration.
394 SCSI_SetPin(SCSI_Out_RST);
395
396 SCSI_CTL_PHASE_Write(0);
397 SCSI_ClearPin(SCSI_Out_ATN);
398 SCSI_ClearPin(SCSI_Out_BSY);
399 SCSI_ClearPin(SCSI_Out_ACK);
400 SCSI_ClearPin(SCSI_Out_RST);
401 SCSI_ClearPin(SCSI_Out_SEL);
402 SCSI_ClearPin(SCSI_Out_REQ);
403
404 // Allow the FIFOs to fill up again.
405 SCSI_ClearPin(SCSI_Out_RST);
406 scsiTarget_AUX_CTL = scsiTarget_AUX_CTL & ~(0x03);
407
408 SCSI_Parity_Error_Read(); // clear sticky bits
409 }
410
411 static void scsiPhyInitDMA()
412 {
413 // One-time init only.
414 if (scsiDmaTxChan == CY_DMA_INVALID_CHANNEL)
415 {
416 scsiDmaRxChan =
417 SCSI_RX_DMA_DmaInitialize(
418 1, // Bytes per burst
419 1, // request per burst
420 HI16(CYDEV_PERIPH_BASE),
421 HI16(CYDEV_SRAM_BASE)
422 );
423
424 scsiDmaTxChan =
425 SCSI_TX_DMA_DmaInitialize(
426 1, // Bytes per burst
427 1, // request per burst
428 HI16(CYDEV_SRAM_BASE),
429 HI16(CYDEV_PERIPH_BASE)
430 );
431
432 CyDmaChDisable(scsiDmaRxChan);
433 CyDmaChDisable(scsiDmaTxChan);
434
435 scsiDmaRxTd[0] = CyDmaTdAllocate();
436 scsiDmaTxTd[0] = CyDmaTdAllocate();
437
438 SCSI_RX_DMA_COMPLETE_StartEx(scsiRxCompleteISR);
439 SCSI_TX_DMA_COMPLETE_StartEx(scsiTxCompleteISR);
440 }
441 }
442
443
444 void scsiPhyInit()
445 {
446 scsiPhyInitDMA();
447
448 SCSI_RST_ISR_StartEx(scsiResetISR);
449 }
450 #pragma GCC pop_options