scsiHostPhy.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #include "scsiHostPhy.h"
  2. #include "ZuluSCSI_platform.h"
  3. #include "ZuluSCSI_log.h"
  4. #include "ZuluSCSI_log_trace.h"
  5. #include <assert.h>
  6. #include <scsi2sd.h>
  7. extern "C" {
  8. #include <scsi.h>
  9. }
  10. volatile bool g_scsiHostPhyReset;
  11. // Release bus and pulse RST signal, initialize PHY to host mode.
  12. void scsiHostPhyReset(void)
  13. {
  14. SCSI_RELEASE_OUTPUTS();
  15. SCSI_ENABLE_INITIATOR();
  16. SCSI_OUT(RST, 1);
  17. delay(2);
  18. SCSI_OUT(RST, 0);
  19. delay(250);
  20. g_scsiHostPhyReset = false;
  21. }
  22. // Select a device, id 0-7.
  23. // Returns true if the target answers to selection request.
  24. bool scsiHostPhySelect(int target_id)
  25. {
  26. SCSI_RELEASE_OUTPUTS();
  27. // We can't write individual data bus bits, so use a bit modified
  28. // arbitration scheme. We always yield to any other initiator on
  29. // the bus.
  30. scsiLogInitiatorPhaseChange(BUS_BUSY);
  31. SCSI_OUT(BSY, 1);
  32. for (int wait = 0; wait < 10; wait++)
  33. {
  34. delayMicroseconds(1);
  35. if (SCSI_IN_DATA() != 0)
  36. {
  37. azdbg("scsiHostPhySelect: bus is busy");
  38. scsiLogInitiatorPhaseChange(BUS_FREE);
  39. SCSI_RELEASE_OUTPUTS();
  40. return false;
  41. }
  42. }
  43. // Selection phase
  44. scsiLogInitiatorPhaseChange(SELECTION);
  45. azdbg("------ SELECTING ", target_id);
  46. SCSI_OUT(SEL, 1);
  47. delayMicroseconds(5);
  48. SCSI_OUT_DATA(1 << target_id);
  49. delayMicroseconds(5);
  50. SCSI_OUT(BSY, 0);
  51. // Wait for target to respond
  52. for (int wait = 0; wait < 250; wait++)
  53. {
  54. delayMicroseconds(1000);
  55. if (SCSI_IN(BSY))
  56. {
  57. break;
  58. }
  59. }
  60. if (!SCSI_IN(BSY))
  61. {
  62. // No response
  63. SCSI_RELEASE_OUTPUTS();
  64. return false;
  65. }
  66. // We need to assert OUT_BSY to enable IO buffer U105 to read status signals.
  67. SCSI_OUT(BSY, 1);
  68. SCSI_OUT(SEL, 0);
  69. return true;
  70. }
  71. // Read the current communication phase as signaled by the target
  72. int scsiHostPhyGetPhase()
  73. {
  74. static absolute_time_t last_online_time;
  75. int phase = 0;
  76. if (SCSI_IN(CD)) phase |= __scsiphase_cd;
  77. if (SCSI_IN(IO)) phase |= __scsiphase_io;
  78. if (SCSI_IN(MSG)) phase |= __scsiphase_msg;
  79. if (phase == 0 && absolute_time_diff_us(last_online_time, get_absolute_time()) > 100)
  80. {
  81. // Disable OUT_BSY for a short time to see if the target is still on line
  82. SCSI_OUT(BSY, 0);
  83. delay_100ns();
  84. if (!SCSI_IN(BSY))
  85. {
  86. scsiLogInitiatorPhaseChange(BUS_FREE);
  87. return BUS_FREE;
  88. }
  89. // Still online, re-enable OUT_BSY
  90. SCSI_OUT(BSY, 1);
  91. }
  92. last_online_time = get_absolute_time();
  93. scsiLogInitiatorPhaseChange(phase);
  94. return phase;
  95. }
  96. bool scsiHostRequestWaiting()
  97. {
  98. return SCSI_IN(REQ);
  99. }
  100. // Blocking data transfer
  101. #define SCSIHOST_WAIT_ACTIVE(pin) \
  102. if (!SCSI_IN(pin)) { \
  103. if (!SCSI_IN(pin)) { \
  104. while(!SCSI_IN(pin) && !g_scsiHostPhyReset); \
  105. } \
  106. }
  107. #define SCSIHOST_WAIT_INACTIVE(pin) \
  108. if (SCSI_IN(pin)) { \
  109. if (SCSI_IN(pin)) { \
  110. while(SCSI_IN(pin) && !g_scsiHostPhyReset); \
  111. } \
  112. }
  113. // Write one byte to SCSI target using the handshake mechanism
  114. static inline void scsiHostWriteOneByte(uint8_t value)
  115. {
  116. SCSIHOST_WAIT_ACTIVE(REQ);
  117. SCSI_OUT_DATA(value);
  118. delay_100ns(); // DB setup time before ACK
  119. SCSI_OUT(ACK, 1);
  120. SCSIHOST_WAIT_INACTIVE(REQ);
  121. SCSI_RELEASE_DATA_REQ();
  122. SCSI_OUT(ACK, 0);
  123. }
  124. // Read one byte from SCSI target using the handshake mechanism.
  125. static inline uint8_t scsiHostReadOneByte(int* parityError)
  126. {
  127. SCSIHOST_WAIT_ACTIVE(REQ);
  128. uint16_t r = SCSI_IN_DATA();
  129. SCSI_OUT(ACK, 1);
  130. SCSIHOST_WAIT_INACTIVE(REQ);
  131. SCSI_OUT(ACK, 0);
  132. if (parityError && r != (g_scsi_parity_lookup[r & 0xFF] ^ SCSI_IO_DATA_MASK))
  133. {
  134. azlog("Parity error in scsiReadOneByte(): ", (uint32_t)r);
  135. *parityError = 1;
  136. }
  137. return (uint8_t)r;
  138. }
  139. bool scsiHostWrite(const uint8_t *data, uint32_t count)
  140. {
  141. scsiLogDataOut(data, count);
  142. for (uint32_t i = 0; i < count; i++)
  143. {
  144. if (g_scsiHostPhyReset) return false;
  145. scsiHostWriteOneByte(data[i]);
  146. }
  147. return true;
  148. }
  149. bool scsiHostRead(uint8_t *data, uint32_t count)
  150. {
  151. for (uint32_t i = 0; i < count; i++)
  152. {
  153. if (g_scsiHostPhyReset) return false;
  154. data[i] = scsiHostReadOneByte(NULL);
  155. }
  156. scsiLogDataIn(data, count);
  157. return true;
  158. }
  159. // Release all bus signals
  160. void scsiHostPhyRelease()
  161. {
  162. scsiLogInitiatorPhaseChange(BUS_FREE);
  163. SCSI_RELEASE_OUTPUTS();
  164. }