rp2040_sdio.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Implementation of SDIO communication for RP2040
  2. //
  3. // The RP2040 official work-in-progress code at
  4. // https://github.com/raspberrypi/pico-extras/tree/master/src/rp2_common/pico_sd_card
  5. // may be useful reference, but this is independent implementation.
  6. //
  7. // For official SDIO specifications, refer to:
  8. // https://www.sdcard.org/downloads/pls/
  9. // "SDIO Physical Layer Simplified Specification Version 8.00"
  10. #include "rp2040_sdio.h"
  11. #include "rp2040_sdio.pio.h"
  12. #include <hardware/pio.h>
  13. #include <hardware/gpio.h>
  14. #include <ZuluSCSI_platform.h>
  15. #include <ZuluSCSI_log.h>
  16. #define SDIO_PIO pio1
  17. #define SDIO_CMD_SM 0
  18. #define SDIO_DATA_SM 1
  19. static struct {
  20. uint32_t pio_cmd_clk_offset;
  21. } g_sdio;
  22. // Table lookup for calculating CRC-7 checksum that is used in SDIO command packets.
  23. // Usage:
  24. // uint8_t crc = 0;
  25. // crc = crc7_table[crc ^ byte];
  26. // .. repeat for every byte ..
  27. static const uint8_t crc7_table[256] = {
  28. 0x00, 0x12, 0x24, 0x36, 0x48, 0x5a, 0x6c, 0x7e, 0x90, 0x82, 0xb4, 0xa6, 0xd8, 0xca, 0xfc, 0xee,
  29. 0x32, 0x20, 0x16, 0x04, 0x7a, 0x68, 0x5e, 0x4c, 0xa2, 0xb0, 0x86, 0x94, 0xea, 0xf8, 0xce, 0xdc,
  30. 0x64, 0x76, 0x40, 0x52, 0x2c, 0x3e, 0x08, 0x1a, 0xf4, 0xe6, 0xd0, 0xc2, 0xbc, 0xae, 0x98, 0x8a,
  31. 0x56, 0x44, 0x72, 0x60, 0x1e, 0x0c, 0x3a, 0x28, 0xc6, 0xd4, 0xe2, 0xf0, 0x8e, 0x9c, 0xaa, 0xb8,
  32. 0xc8, 0xda, 0xec, 0xfe, 0x80, 0x92, 0xa4, 0xb6, 0x58, 0x4a, 0x7c, 0x6e, 0x10, 0x02, 0x34, 0x26,
  33. 0xfa, 0xe8, 0xde, 0xcc, 0xb2, 0xa0, 0x96, 0x84, 0x6a, 0x78, 0x4e, 0x5c, 0x22, 0x30, 0x06, 0x14,
  34. 0xac, 0xbe, 0x88, 0x9a, 0xe4, 0xf6, 0xc0, 0xd2, 0x3c, 0x2e, 0x18, 0x0a, 0x74, 0x66, 0x50, 0x42,
  35. 0x9e, 0x8c, 0xba, 0xa8, 0xd6, 0xc4, 0xf2, 0xe0, 0x0e, 0x1c, 0x2a, 0x38, 0x46, 0x54, 0x62, 0x70,
  36. 0x82, 0x90, 0xa6, 0xb4, 0xca, 0xd8, 0xee, 0xfc, 0x12, 0x00, 0x36, 0x24, 0x5a, 0x48, 0x7e, 0x6c,
  37. 0xb0, 0xa2, 0x94, 0x86, 0xf8, 0xea, 0xdc, 0xce, 0x20, 0x32, 0x04, 0x16, 0x68, 0x7a, 0x4c, 0x5e,
  38. 0xe6, 0xf4, 0xc2, 0xd0, 0xae, 0xbc, 0x8a, 0x98, 0x76, 0x64, 0x52, 0x40, 0x3e, 0x2c, 0x1a, 0x08,
  39. 0xd4, 0xc6, 0xf0, 0xe2, 0x9c, 0x8e, 0xb8, 0xaa, 0x44, 0x56, 0x60, 0x72, 0x0c, 0x1e, 0x28, 0x3a,
  40. 0x4a, 0x58, 0x6e, 0x7c, 0x02, 0x10, 0x26, 0x34, 0xda, 0xc8, 0xfe, 0xec, 0x92, 0x80, 0xb6, 0xa4,
  41. 0x78, 0x6a, 0x5c, 0x4e, 0x30, 0x22, 0x14, 0x06, 0xe8, 0xfa, 0xcc, 0xde, 0xa0, 0xb2, 0x84, 0x96,
  42. 0x2e, 0x3c, 0x0a, 0x18, 0x66, 0x74, 0x42, 0x50, 0xbe, 0xac, 0x9a, 0x88, 0xf6, 0xe4, 0xd2, 0xc0,
  43. 0x1c, 0x0e, 0x38, 0x2a, 0x54, 0x46, 0x70, 0x62, 0x8c, 0x9e, 0xa8, 0xba, 0xc4, 0xd6, 0xe0, 0xf2
  44. };
  45. sdio_status_t rp2040_sdio_command_R1(uint8_t command, uint32_t arg, uint32_t *response)
  46. {
  47. azdbg("Command: ", command, " arg ", arg);
  48. // Format the arguments in the way expected by the PIO code.
  49. uint32_t word0 =
  50. (47 << 24) | // Number of bits in command minus one
  51. ( 1 << 22) | // Transfer direction from host to card
  52. (command << 16) | // Command byte
  53. (((arg >> 24) & 0xFF) << 8) | // MSB byte of argument
  54. (((arg >> 16) & 0xFF) << 0);
  55. uint32_t word1 =
  56. (((arg >> 8) & 0xFF) << 24) |
  57. (((arg >> 0) & 0xFF) << 16) | // LSB byte of argument
  58. ( 1 << 8); // End bit
  59. // Set number of bits in response minus one, or leave at 0 if no response expected
  60. if (response)
  61. {
  62. word1 |= (47 << 0);
  63. }
  64. // Calculate checksum in the order that the bytes will be transmitted (big-endian)
  65. uint8_t crc = 0;
  66. crc = crc7_table[crc ^ ((word0 >> 16) & 0xFF)];
  67. crc = crc7_table[crc ^ ((word0 >> 8) & 0xFF)];
  68. crc = crc7_table[crc ^ ((word0 >> 0) & 0xFF)];
  69. crc = crc7_table[crc ^ ((word1 >> 24) & 0xFF)];
  70. crc = crc7_table[crc ^ ((word1 >> 16) & 0xFF)];
  71. word1 |= crc << 8;
  72. // Transmit command
  73. pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
  74. pio_sm_put(SDIO_PIO, SDIO_CMD_SM, word0);
  75. pio_sm_put(SDIO_PIO, SDIO_CMD_SM, word1);
  76. // Wait for response
  77. uint32_t start = millis();
  78. uint32_t wait_words = response ? 2 : 1;
  79. while (pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM) < wait_words)
  80. {
  81. if ((uint32_t)(millis() - start) > 2)
  82. {
  83. azdbg("Timeout waiting for response in rp2040_sdio_command_R1(), ",
  84. "PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)g_sdio.pio_cmd_clk_offset,
  85. " RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
  86. " TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
  87. // Reset the state machine program
  88. pio_sm_clear_fifos(SDIO_PIO, SDIO_CMD_SM);
  89. pio_sm_exec(SDIO_PIO, SDIO_CMD_SM, pio_encode_jmp(g_sdio.pio_cmd_clk_offset));
  90. return SDIO_ERR_RESPONSE_TIMEOUT;
  91. }
  92. }
  93. delay(1);
  94. azdbg("PIO PC: ", (int)pio_sm_get_pc(SDIO_PIO, SDIO_CMD_SM) - (int)g_sdio.pio_cmd_clk_offset,
  95. " RXF: ", (int)pio_sm_get_rx_fifo_level(SDIO_PIO, SDIO_CMD_SM),
  96. " TXF: ", (int)pio_sm_get_tx_fifo_level(SDIO_PIO, SDIO_CMD_SM));
  97. if (response)
  98. {
  99. // Read out response packet
  100. uint32_t resp0 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
  101. uint32_t resp1 = pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
  102. azdbg(resp0, " ", resp1);
  103. // Calculate response checksum
  104. crc = 0;
  105. crc = crc7_table[crc ^ ((resp0 >> 24) & 0xFF)];
  106. crc = crc7_table[crc ^ ((resp0 >> 16) & 0xFF)];
  107. crc = crc7_table[crc ^ ((resp0 >> 8) & 0xFF)];
  108. crc = crc7_table[crc ^ ((resp0 >> 0) & 0xFF)];
  109. crc = crc7_table[crc ^ ((resp1 >> 8) & 0xFF)];
  110. uint8_t actual_crc = ((resp1 >> 0) & 0xFE);
  111. if (crc != actual_crc)
  112. {
  113. azdbg("CRC error in rp2040_sdio_command_R1(): calculated ", crc, " packet has ", actual_crc);
  114. return SDIO_ERR_CRC;
  115. }
  116. *response = ((resp0 & 0xFFFFFF) << 8) | ((resp1 >> 8) & 0xFF);
  117. }
  118. else
  119. {
  120. // Read out dummy marker
  121. pio_sm_get(SDIO_PIO, SDIO_CMD_SM);
  122. }
  123. return SDIO_OK;
  124. }
  125. void rp2040_sdio_init()
  126. {
  127. azdbg("rp2040_sdio_init()");
  128. // Mark resources as being in use, unless it has been done already.
  129. static bool resources_claimed = false;
  130. if (!resources_claimed)
  131. {
  132. pio_sm_claim(SDIO_PIO, SDIO_CMD_SM);
  133. pio_sm_claim(SDIO_PIO, SDIO_DATA_SM);
  134. resources_claimed = true;
  135. }
  136. // Load PIO programs
  137. pio_clear_instruction_memory(SDIO_PIO);
  138. // Command & clock state machine
  139. g_sdio.pio_cmd_clk_offset = pio_add_program(SDIO_PIO, &sdio_cmd_clk_program);
  140. pio_sm_config cfg = sdio_cmd_clk_program_get_default_config(g_sdio.pio_cmd_clk_offset);
  141. sm_config_set_out_pins(&cfg, SDIO_CMD, 1);
  142. sm_config_set_in_pins(&cfg, SDIO_CMD);
  143. sm_config_set_set_pins(&cfg, SDIO_CMD, 1);
  144. sm_config_set_jmp_pin(&cfg, SDIO_CMD);
  145. sm_config_set_sideset_pins(&cfg, SDIO_CLK);
  146. sm_config_set_out_shift(&cfg, false, true, 32);
  147. sm_config_set_in_shift(&cfg, false, true, 32);
  148. sm_config_set_clkdiv_int_frac(&cfg, 5, 0);
  149. sm_config_set_mov_status(&cfg, STATUS_TX_LESSTHAN, 2);
  150. pio_sm_init(SDIO_PIO, SDIO_CMD_SM, g_sdio.pio_cmd_clk_offset, &cfg);
  151. pio_sm_set_consecutive_pindirs(SDIO_PIO, SDIO_CMD_SM, SDIO_CLK, 1, true);
  152. pio_sm_set_enabled(SDIO_PIO, SDIO_CMD_SM, true);
  153. // Redirect GPIOs to PIO
  154. gpio_set_function(SDIO_CMD, GPIO_FUNC_PIO1);
  155. gpio_set_function(SDIO_CLK, GPIO_FUNC_PIO1);
  156. }