main.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. /**
  2. * Copyright (C) 2021, Mate Kukri <km@mkukri.xyz>
  3. * Copyright (C) 2023, 2024, Riku Viitanen <riku.viitanen@protonmail.com>
  4. * Based on "pico-serprog" by Thomas Roth <code@stacksmashing.net>
  5. *
  6. * Licensed under GPLv3
  7. *
  8. * Also based on stm32-vserprog:
  9. * https://github.com/dword1511/stm32-vserprog
  10. */
  11. #define DESCRIPTION "SPI flash chip programmer using Flashprog's serprog protocol"
  12. #define WEBSITE "https://codeberg.org/Riku_V/pico-serprog/"
  13. #include "pico/stdlib.h"
  14. #include "pico/binary_info.h"
  15. #include "hardware/spi.h"
  16. #include "tusb.h"
  17. #include "serprog.h"
  18. #define CDC_ITF 0 /* USB CDC interface no */
  19. #define SPI_IF spi0 /* Which PL022 to use */
  20. #define SPI_CS_0 5 /* The default CS pin */
  21. #define SPI_MISO 4
  22. #define SPI_MOSI 3
  23. #define SPI_SCK 2
  24. uint8_t spi_enabled = 0;
  25. uint cs_pin = SPI_CS_0;
  26. #define NUM_CS_AVAILABLE 4 /* Number of usable chip selects */
  27. uint baud = 12000000; /* Default to 12MHz */
  28. static const char progname[16] = "pico-serprog";
  29. /* Map of supported serprog commands */
  30. static const uint32_t cmdmap[8] = {
  31. (1 << S_CMD_NOP) |
  32. (1 << S_CMD_Q_IFACE) |
  33. (1 << S_CMD_Q_CMDMAP) |
  34. (1 << S_CMD_Q_PGMNAME) |
  35. (1 << S_CMD_Q_SERBUF) |
  36. (1 << S_CMD_Q_BUSTYPE) |
  37. (1 << S_CMD_SYNCNOP) |
  38. (1 << S_CMD_O_SPIOP) |
  39. (1 << S_CMD_S_BUSTYPE) |
  40. (1 << S_CMD_S_SPI_FREQ) |
  41. (1 << S_CMD_S_PIN_STATE) |
  42. (1 << S_CMD_S_SPI_CS)
  43. };
  44. static void use_cs(uint pin) {
  45. gpio_put(pin, 1);
  46. gpio_set_dir(pin, GPIO_OUT);
  47. gpio_set_drive_strength(pin, GPIO_DRIVE_STRENGTH_12MA);
  48. }
  49. static void pullup_cs(uint pin) {
  50. gpio_set_dir(pin, GPIO_IN);
  51. gpio_pull_up(pin);
  52. }
  53. static void enable_spi() {
  54. #ifdef PICO_DEFAULT_LED_PIN
  55. /* Setup status LED */
  56. gpio_init(PICO_DEFAULT_LED_PIN);
  57. gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
  58. #endif
  59. /* Setup default CS as output, others as inputs with pull-ups */
  60. for (uint8_t i = SPI_CS_0+1; i<SPI_CS_0+NUM_CS_AVAILABLE; i++) {
  61. gpio_init(i);
  62. pullup_cs(i);
  63. }
  64. gpio_init(cs_pin);
  65. use_cs(cs_pin);
  66. /* Setup PL022 */
  67. spi_init(SPI_IF, baud);
  68. gpio_set_function(SPI_MISO, GPIO_FUNC_SPI);
  69. gpio_set_function(SPI_MOSI, GPIO_FUNC_SPI);
  70. gpio_set_function(SPI_SCK, GPIO_FUNC_SPI);
  71. gpio_set_drive_strength(SPI_MISO, GPIO_DRIVE_STRENGTH_12MA);
  72. gpio_set_drive_strength(SPI_MOSI, GPIO_DRIVE_STRENGTH_12MA);
  73. gpio_set_drive_strength(SPI_SCK, GPIO_DRIVE_STRENGTH_12MA);
  74. spi_enabled = 1;
  75. }
  76. static void disable_pin(uint pin) {
  77. gpio_init(pin); /* Set pin to SIO input */
  78. gpio_set_pulls(pin, 0, 0); /* Disable all pulls */
  79. }
  80. static void disable_spi() {
  81. for (uint8_t i=0; i<NUM_CS_AVAILABLE; i++)
  82. disable_pin(SPI_CS_0 + i);
  83. disable_pin(SPI_MISO);
  84. disable_pin(SPI_MOSI);
  85. disable_pin(SPI_SCK);
  86. /* Disable SPI peripheral */
  87. spi_deinit(SPI_IF);
  88. spi_enabled = 0;
  89. }
  90. static inline void cs_select(uint cs_pin) {
  91. asm volatile("nop \n nop \n nop"); /* FIXME */
  92. gpio_put(cs_pin, 0);
  93. asm volatile("nop \n nop \n nop"); /* FIXME */
  94. }
  95. static inline void cs_deselect(uint cs_pin) {
  96. asm volatile("nop \n nop \n nop"); /* FIXME */
  97. gpio_put(cs_pin, 1);
  98. asm volatile("nop \n nop \n nop"); /* FIXME */
  99. }
  100. static void wait_for_read(void) {
  101. do
  102. tud_task();
  103. while (!tud_cdc_n_available(CDC_ITF));
  104. }
  105. static inline void readbytes_blocking(void *b, uint32_t len) {
  106. while (len) {
  107. wait_for_read();
  108. uint32_t r = tud_cdc_n_read(CDC_ITF, b, len);
  109. b += r;
  110. len -= r;
  111. }
  112. }
  113. static inline uint8_t readbyte_blocking(void) {
  114. wait_for_read();
  115. uint8_t b;
  116. tud_cdc_n_read(CDC_ITF, &b, 1);
  117. return b;
  118. }
  119. static void wait_for_write(void) {
  120. do {
  121. tud_task();
  122. } while (!tud_cdc_n_write_available(CDC_ITF));
  123. }
  124. static inline void sendbytes_blocking(const void *b, uint32_t len) {
  125. while (len) {
  126. wait_for_write();
  127. uint32_t w = tud_cdc_n_write(CDC_ITF, b, len);
  128. b += w;
  129. len -= w;
  130. }
  131. }
  132. static inline void sendbyte_blocking(uint8_t b) {
  133. wait_for_write();
  134. tud_cdc_n_write(CDC_ITF, &b, 1);
  135. }
  136. void s_cmd_s_bustype() {
  137. /* If SPI is among the requsted bus types we succeed,
  138. * fail otherwise */
  139. if ((uint8_t) readbyte_blocking() & (1 << 3))
  140. sendbyte_blocking(S_ACK);
  141. else
  142. sendbyte_blocking(S_NAK);
  143. }
  144. void s_cmd_o_spiop() {
  145. static uint8_t buf[4096];
  146. uint32_t wlen, rlen;
  147. readbytes_blocking(&wlen, 3);
  148. readbytes_blocking(&rlen, 3);
  149. cs_select(cs_pin);
  150. while (wlen) {
  151. uint32_t cur = MIN(wlen, sizeof buf);
  152. readbytes_blocking(buf, cur);
  153. spi_write_blocking(SPI_IF, buf, cur);
  154. wlen -= cur;
  155. }
  156. sendbyte_blocking(S_ACK);
  157. while (rlen) {
  158. uint32_t cur = MIN(rlen, sizeof buf);
  159. spi_read_blocking(SPI_IF, 0, buf, cur);
  160. sendbytes_blocking(buf, cur);
  161. rlen -= cur;
  162. }
  163. cs_deselect(cs_pin);
  164. }
  165. void s_cmd_s_spi_freq() {
  166. uint32_t want_baud;
  167. readbytes_blocking(&want_baud, 4);
  168. if (want_baud) {
  169. /* Set frequency */
  170. baud = spi_set_baudrate(SPI_IF, want_baud);
  171. /* Send back actual value */
  172. sendbyte_blocking(S_ACK);
  173. sendbytes_blocking(&baud, 4);
  174. } else {
  175. /* 0 Hz is reserved */
  176. sendbyte_blocking(S_NAK);
  177. }
  178. }
  179. void s_cmd_s_pin_state() {
  180. if (readbyte_blocking())
  181. enable_spi();
  182. else
  183. disable_spi();
  184. sendbyte_blocking(S_ACK);
  185. }
  186. void s_cmd_s_spi_cs() {
  187. uint8_t cs = readbyte_blocking();
  188. if (cs >= NUM_CS_AVAILABLE)
  189. sendbyte_blocking(S_NAK);
  190. return;
  191. cs += SPI_CS_0;
  192. if (spi_enabled) {
  193. if (cs_pin != cs) {
  194. pullup_cs(cs_pin);
  195. use_cs(cs);
  196. }
  197. }
  198. cs_pin = cs;
  199. sendbyte_blocking(S_ACK);
  200. }
  201. static void command_loop() {
  202. while (1) {
  203. uint8_t cmd = readbyte_blocking();
  204. #ifdef PICO_DEFAULT_LED_PIN
  205. gpio_put(PICO_DEFAULT_LED_PIN, 1);
  206. #endif
  207. switch (cmd) {
  208. case S_CMD_NOP:
  209. sendbyte_blocking(S_ACK);
  210. break;
  211. case S_CMD_Q_IFACE:
  212. sendbyte_blocking(S_ACK);
  213. sendbyte_blocking(0x01);
  214. sendbyte_blocking(0x00);
  215. break;
  216. case S_CMD_Q_CMDMAP:
  217. sendbyte_blocking(S_ACK);
  218. sendbytes_blocking((uint8_t *)cmdmap, sizeof(cmdmap));
  219. break;
  220. case S_CMD_Q_PGMNAME:
  221. sendbyte_blocking(S_ACK);
  222. sendbytes_blocking(progname, sizeof(progname));
  223. break;
  224. case S_CMD_Q_SERBUF:
  225. sendbyte_blocking(S_ACK);
  226. sendbyte_blocking(0xFF);
  227. sendbyte_blocking(0xFF);
  228. break;
  229. case S_CMD_Q_BUSTYPE:
  230. sendbyte_blocking(S_ACK);
  231. sendbyte_blocking((1 << 3)); /* SPI */
  232. break;
  233. case S_CMD_SYNCNOP:
  234. sendbyte_blocking(S_NAK);
  235. sendbyte_blocking(S_ACK);
  236. break;
  237. case S_CMD_S_BUSTYPE:
  238. s_cmd_s_bustype();
  239. break;
  240. case S_CMD_O_SPIOP:
  241. s_cmd_o_spiop();
  242. break;
  243. case S_CMD_S_SPI_FREQ:
  244. s_cmd_s_spi_freq();
  245. break;
  246. case S_CMD_S_PIN_STATE:
  247. s_cmd_s_pin_state();
  248. break;
  249. case S_CMD_S_SPI_CS:
  250. s_cmd_s_spi_cs();
  251. break;
  252. default:
  253. sendbyte_blocking(S_NAK);
  254. }
  255. tud_cdc_n_write_flush(CDC_ITF);
  256. #ifdef PICO_DEFAULT_LED_PIN
  257. gpio_put(PICO_DEFAULT_LED_PIN, 0);
  258. #endif
  259. }
  260. }
  261. int main() {
  262. /* Metadata for picotool */
  263. bi_decl(bi_program_description(DESCRIPTION));
  264. bi_decl(bi_program_url(WEBSITE));
  265. #ifdef PICO_DEFAULT_LED_PIN
  266. bi_decl(bi_1pin_with_name(PICO_DEFAULT_LED_PIN, "Activity LED"));
  267. #endif
  268. bi_decl(bi_1pin_with_name(SPI_MISO, "MISO"));
  269. bi_decl(bi_1pin_with_name(SPI_MOSI, "MOSI"));
  270. bi_decl(bi_1pin_with_name(SPI_SCK, "SCK"));
  271. bi_decl(bi_1pin_with_name(SPI_CS_0, "CS_0 (default)"));
  272. bi_decl(bi_1pin_with_name(SPI_CS_0+1, "CS_1"));
  273. bi_decl(bi_1pin_with_name(SPI_CS_0+2, "CS_2"));
  274. bi_decl(bi_1pin_with_name(SPI_CS_0+3, "CS_3"));
  275. /* Setup USB */
  276. tusb_init();
  277. /* Setup PL022 SPI */
  278. enable_spi(baud);
  279. command_loop();
  280. }