protocol_spi.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /* Copyright 2020-2023 Espressif Systems (Shanghai) CO LTD
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. #include "protocol.h"
  16. #include "protocol_prv.h"
  17. #include "esp_loader_io.h"
  18. #include <stddef.h>
  19. #include <assert.h>
  20. typedef struct __attribute__((packed)) {
  21. uint8_t cmd;
  22. uint8_t addr;
  23. uint8_t dummy;
  24. } transaction_preamble_t;
  25. typedef enum {
  26. TRANS_CMD_WRBUF = 0x01,
  27. TRANS_CMD_RDBUF = 0x02,
  28. TRANS_CMD_WRDMA = 0x03,
  29. TRANS_CMD_RDDMA = 0x04,
  30. TRANS_CMD_SEG_DONE = 0x05,
  31. TRANS_CMD_ENQPI = 0x06,
  32. TRANS_CMD_WR_DONE = 0x07,
  33. TRANS_CMD_CMD8 = 0x08,
  34. TRANS_CMD_CMD9 = 0x09,
  35. TRANS_CMD_CMDA = 0x0A,
  36. TRANS_CMD_EXQPI = 0xDD,
  37. } transaction_cmd_t;
  38. /* Slave protocol registers */
  39. typedef enum {
  40. SLAVE_REGISTER_VER = 0,
  41. SLAVE_REGISTER_RXSTA = 4,
  42. SLAVE_REGISTER_TXSTA = 8,
  43. SLAVE_REGISTER_CMD = 12,
  44. } slave_register_addr_t;
  45. #define SLAVE_STA_TOGGLE_BIT (0x01U << 0)
  46. #define SLAVE_STA_INIT_BIT (0x01U << 1)
  47. #define SLAVE_STA_BUF_LENGTH_POS 2U
  48. typedef enum {
  49. SLAVE_STATE_INIT = SLAVE_STA_TOGGLE_BIT | SLAVE_STA_INIT_BIT,
  50. SLAVE_STATE_FIRST_PACKET = SLAVE_STA_INIT_BIT,
  51. } slave_state_t;
  52. typedef enum {
  53. /* Target to host */
  54. SLAVE_CMD_IDLE = 0xAA,
  55. SLAVE_CMD_READY = 0xA5,
  56. /* Host to target */
  57. SLAVE_CMD_REBOOT = 0xFE,
  58. SLAVE_CMD_COMM_REINIT = 0x5A,
  59. SLAVE_CMD_DONE = 0x55,
  60. } slave_cmd_t;
  61. static uint8_t s_slave_seq_tx;
  62. static uint8_t s_slave_seq_rx;
  63. static esp_loader_error_t write_slave_reg(const uint8_t *data, const uint32_t addr,
  64. const uint8_t size);
  65. static esp_loader_error_t read_slave_reg(uint8_t *out_data, const uint32_t addr,
  66. const uint8_t size);
  67. static esp_loader_error_t handle_slave_state(const uint32_t status_reg_addr, uint8_t *seq_state,
  68. bool *slave_ready, uint32_t *buf_size);
  69. static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value);
  70. esp_loader_error_t loader_initialize_conn(esp_loader_connect_args_t *connect_args)
  71. {
  72. for (uint8_t trial = 0; trial < connect_args->trials; trial++) {
  73. uint8_t slave_ready_flag;
  74. RETURN_ON_ERROR(read_slave_reg(&slave_ready_flag, SLAVE_REGISTER_CMD,
  75. sizeof(slave_ready_flag)));
  76. if (slave_ready_flag != SLAVE_CMD_IDLE) {
  77. loader_port_debug_print("Waiting for Slave to be idle...\n");
  78. loader_port_delay_ms(100);
  79. } else {
  80. break;
  81. }
  82. }
  83. const uint8_t reg_val = SLAVE_CMD_READY;
  84. RETURN_ON_ERROR(write_slave_reg(&reg_val, SLAVE_REGISTER_CMD, sizeof(reg_val)));
  85. for (uint8_t trial = 0; trial < connect_args->trials; trial++) {
  86. uint8_t slave_ready_flag;
  87. RETURN_ON_ERROR(read_slave_reg(&slave_ready_flag, SLAVE_REGISTER_CMD,
  88. sizeof(slave_ready_flag)));
  89. if (slave_ready_flag != SLAVE_CMD_READY) {
  90. loader_port_debug_print("Waiting for Slave to be ready...\n");
  91. loader_port_delay_ms(100);
  92. } else {
  93. break;
  94. }
  95. }
  96. return ESP_LOADER_SUCCESS;
  97. }
  98. esp_loader_error_t send_cmd(const void *cmd_data, uint32_t size, uint32_t *reg_value)
  99. {
  100. command_t command = ((const command_common_t *)cmd_data)->command;
  101. uint32_t buf_size;
  102. bool slave_ready = false;
  103. while (!slave_ready) {
  104. RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_RXSTA, &s_slave_seq_rx, &slave_ready,
  105. &buf_size));
  106. }
  107. if (size > buf_size) {
  108. return ESP_LOADER_ERROR_INVALID_PARAM;
  109. }
  110. /* Start and write the command */
  111. transaction_preamble_t preamble = {.cmd = TRANS_CMD_WRDMA};
  112. loader_port_spi_set_cs(0);
  113. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  114. loader_port_remaining_time()));
  115. RETURN_ON_ERROR(loader_port_write((const uint8_t *)cmd_data, size,
  116. loader_port_remaining_time()));
  117. loader_port_spi_set_cs(1);
  118. /* Terminate the write */
  119. loader_port_spi_set_cs(0);
  120. preamble.cmd = TRANS_CMD_WR_DONE;
  121. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  122. loader_port_remaining_time()));
  123. loader_port_spi_set_cs(1);
  124. return check_response(command, reg_value);
  125. }
  126. esp_loader_error_t send_cmd_with_data(const void *cmd_data, size_t cmd_size,
  127. const void *data, size_t data_size)
  128. {
  129. uint32_t buf_size;
  130. bool slave_ready = false;
  131. while (!slave_ready) {
  132. RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_RXSTA, &s_slave_seq_rx, &slave_ready,
  133. &buf_size));
  134. }
  135. if (cmd_size + data_size > buf_size) {
  136. return ESP_LOADER_ERROR_INVALID_PARAM;
  137. }
  138. /* Start and write the command and the data */
  139. transaction_preamble_t preamble = {.cmd = TRANS_CMD_WRDMA};
  140. loader_port_spi_set_cs(0);
  141. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  142. loader_port_remaining_time()));
  143. RETURN_ON_ERROR(loader_port_write((const uint8_t *)cmd_data, cmd_size,
  144. loader_port_remaining_time()));
  145. RETURN_ON_ERROR(loader_port_write((const uint8_t *)data, data_size,
  146. loader_port_remaining_time()));
  147. loader_port_spi_set_cs(1);
  148. /* Terminate the write */
  149. loader_port_spi_set_cs(0);
  150. preamble.cmd = TRANS_CMD_WR_DONE;
  151. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  152. loader_port_remaining_time()));
  153. loader_port_spi_set_cs(1);
  154. command_t command = ((const command_common_t *)cmd_data)->command;
  155. return check_response(command, NULL);
  156. }
  157. static esp_loader_error_t read_slave_reg(uint8_t *out_data, const uint32_t addr,
  158. const uint8_t size)
  159. {
  160. transaction_preamble_t preamble = {
  161. .cmd = TRANS_CMD_RDBUF,
  162. .addr = addr,
  163. };
  164. loader_port_spi_set_cs(0);
  165. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  166. loader_port_remaining_time()));
  167. RETURN_ON_ERROR(loader_port_read(out_data, size, loader_port_remaining_time()));
  168. loader_port_spi_set_cs(1);
  169. return ESP_LOADER_SUCCESS;
  170. }
  171. static esp_loader_error_t write_slave_reg(const uint8_t *data, const uint32_t addr,
  172. const uint8_t size)
  173. {
  174. transaction_preamble_t preamble = {
  175. .cmd = TRANS_CMD_WRBUF,
  176. .addr = addr,
  177. };
  178. loader_port_spi_set_cs(0);
  179. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  180. loader_port_remaining_time()));
  181. RETURN_ON_ERROR(loader_port_write(data, size, loader_port_remaining_time()));
  182. loader_port_spi_set_cs(1);
  183. return ESP_LOADER_SUCCESS;
  184. }
  185. static esp_loader_error_t handle_slave_state(const uint32_t status_reg_addr, uint8_t *seq_state,
  186. bool *slave_ready, uint32_t *buf_size)
  187. {
  188. uint32_t status_reg;
  189. RETURN_ON_ERROR(read_slave_reg((uint8_t *)&status_reg, status_reg_addr,
  190. sizeof(status_reg)));
  191. const slave_state_t state = status_reg & (SLAVE_STA_TOGGLE_BIT | SLAVE_STA_INIT_BIT);
  192. switch(state) {
  193. case SLAVE_STATE_INIT: {
  194. const uint32_t initial = 0U;
  195. RETURN_ON_ERROR(write_slave_reg((uint8_t *)&initial, status_reg_addr, sizeof(initial)));
  196. break;
  197. }
  198. case SLAVE_STATE_FIRST_PACKET: {
  199. *seq_state = state & SLAVE_STA_TOGGLE_BIT;
  200. *buf_size = status_reg >> SLAVE_STA_BUF_LENGTH_POS;
  201. *slave_ready = true;
  202. break;
  203. }
  204. default: {
  205. const uint8_t new_seq = state & SLAVE_STA_TOGGLE_BIT;
  206. if (new_seq != *seq_state) {
  207. *seq_state = new_seq;
  208. *buf_size = status_reg >> SLAVE_STA_BUF_LENGTH_POS;
  209. *slave_ready = true;
  210. }
  211. break;
  212. }
  213. }
  214. return ESP_LOADER_SUCCESS;
  215. }
  216. static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value)
  217. {
  218. response_t resp;
  219. uint32_t buf_size;
  220. bool slave_ready = false;
  221. while (!slave_ready) {
  222. RETURN_ON_ERROR(handle_slave_state(SLAVE_REGISTER_TXSTA, &s_slave_seq_tx, &slave_ready,
  223. &buf_size));
  224. }
  225. if (sizeof(resp) > buf_size) {
  226. return ESP_LOADER_ERROR_INVALID_PARAM;
  227. }
  228. transaction_preamble_t preamble = {
  229. .cmd = TRANS_CMD_RDDMA,
  230. };
  231. loader_port_spi_set_cs(0);
  232. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  233. loader_port_remaining_time()));
  234. RETURN_ON_ERROR(loader_port_read((uint8_t *)&resp, sizeof(resp),
  235. loader_port_remaining_time()));
  236. loader_port_spi_set_cs(1);
  237. /* Terminate the read */
  238. loader_port_spi_set_cs(0);
  239. preamble.cmd = TRANS_CMD_CMD8;
  240. RETURN_ON_ERROR(loader_port_write((const uint8_t *)&preamble, sizeof(preamble),
  241. loader_port_remaining_time()));
  242. loader_port_spi_set_cs(1);
  243. common_response_t *common = (common_response_t *)&resp;
  244. if ((common->direction != READ_DIRECTION) || (common->command != cmd)) {
  245. return ESP_LOADER_ERROR_INVALID_RESPONSE;
  246. }
  247. response_status_t *status =
  248. (response_status_t *)((uint8_t *)&resp + sizeof(resp) - sizeof(response_status_t));
  249. if (status->failed) {
  250. log_loader_internal_error(status->error);
  251. return ESP_LOADER_ERROR_INVALID_RESPONSE;
  252. }
  253. if (reg_value != NULL) {
  254. *reg_value = common->value;
  255. }
  256. return ESP_LOADER_SUCCESS;
  257. }