rp2040.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. #include "rp2040.h"
  2. #include <furi.h>
  3. #include "target.h"
  4. // Most of the below code is heavily inspired by or taken directly from:
  5. // Blackmagic: https://github.com/blackmagic-debug/blackmagic
  6. // Pico-bootrom: https://github.com/raspberrypi/pico-bootrom
  7. #define RP_REG_ACCESS_NORMAL 0x0000U
  8. #define RP_REG_ACCESS_WRITE_XOR 0x1000U
  9. #define RP_REG_ACCESS_WRITE_ATOMIC_BITSET 0x2000U
  10. #define RP_REG_ACCESS_WRITE_ATOMIC_BITCLR 0x3000U
  11. #define RP_CLOCKS_BASE_ADDR 0x40008000U
  12. #define RP_CLOCKS_WAKE_EN0 (RP_CLOCKS_BASE_ADDR + 0xa0U)
  13. #define RP_CLOCKS_WAKE_EN1 (RP_CLOCKS_BASE_ADDR + 0xa4U)
  14. #define RP_CLOCKS_WAKE_EN0_MASK 0xff0c0f19U
  15. #define RP_CLOCKS_WAKE_EN1_MASK 0x00002007U
  16. #define RP_GPIO_QSPI_BASE_ADDR 0x40018000U
  17. #define RP_GPIO_QSPI_SCLK_CTRL (RP_GPIO_QSPI_BASE_ADDR + 0x04U)
  18. #define RP_GPIO_QSPI_CS_CTRL (RP_GPIO_QSPI_BASE_ADDR + 0x0cU)
  19. #define RP_GPIO_QSPI_SD0_CTRL (RP_GPIO_QSPI_BASE_ADDR + 0x14U)
  20. #define RP_GPIO_QSPI_SD1_CTRL (RP_GPIO_QSPI_BASE_ADDR + 0x1cU)
  21. #define RP_GPIO_QSPI_SD2_CTRL (RP_GPIO_QSPI_BASE_ADDR + 0x24U)
  22. #define RP_GPIO_QSPI_SD3_CTRL (RP_GPIO_QSPI_BASE_ADDR + 0x2cU)
  23. #define RP_GPIO_QSPI_CS_DRIVE_NORMAL (0U << 8U)
  24. #define RP_GPIO_QSPI_CS_DRIVE_INVERT (1U << 8U)
  25. #define RP_GPIO_QSPI_CS_DRIVE_LOW (2U << 8U)
  26. #define RP_GPIO_QSPI_CS_DRIVE_HIGH (3U << 8U)
  27. #define RP_GPIO_QSPI_CS_DRIVE_MASK 0x00000300U
  28. #define RP_GPIO_QSPI_SD1_CTRL_INOVER_BITS 0x00030000U
  29. #define RP_GPIO_QSPI_SCLK_POR 0x0000001fU
  30. #define RP_SSI_BASE_ADDR 0x18000000U
  31. #define RP_SSI_CTRL0 (RP_SSI_BASE_ADDR + 0x00U)
  32. #define RP_SSI_CTRL1 (RP_SSI_BASE_ADDR + 0x04U)
  33. #define RP_SSI_ENABLE (RP_SSI_BASE_ADDR + 0x08U)
  34. #define RP_SSI_SER (RP_SSI_BASE_ADDR + 0x10U)
  35. #define RP_SSI_BAUD (RP_SSI_BASE_ADDR + 0x14U)
  36. #define RP_SSI_TXFLR (RP_SSI_BASE_ADDR + 0x20U)
  37. #define RP_SSI_RXFLR (RP_SSI_BASE_ADDR + 0x24U)
  38. #define RP_SSI_SR (RP_SSI_BASE_ADDR + 0x28U)
  39. #define RP_SSI_ICR (RP_SSI_BASE_ADDR + 0x48U)
  40. #define RP_SSI_DR0 (RP_SSI_BASE_ADDR + 0x60U)
  41. #define RP_SSI_XIP_SPI_CTRL0 (RP_SSI_BASE_ADDR + 0xf4U)
  42. #define RP_SSI_CTRL0_FRF_MASK 0x00600000U
  43. #define RP_SSI_CTRL0_FRF_SERIAL (0U << 21U)
  44. #define RP_SSI_CTRL0_FRF_DUAL (1U << 21U)
  45. #define RP_SSI_CTRL0_FRF_QUAD (2U << 21U)
  46. #define RP_SSI_CTRL0_TMOD_MASK 0x00000300U
  47. #define RP_SSI_CTRL0_TMOD_BIDI (0U << 8U)
  48. #define RP_SSI_CTRL0_TMOD_TX_ONLY (1U << 8U)
  49. #define RP_SSI_CTRL0_TMOD_RX_ONLY (2U << 8U)
  50. #define RP_SSI_CTRL0_TMOD_EEPROM (3U << 8U)
  51. #define RP_SSI_CTRL0_DATA_BIT_MASK 0x001f0000U
  52. #define RP_SSI_CTRL0_DATA_BIT_SHIFT 16U
  53. #define RP_SSI_CTRL0_DATA_BITS(x) (((x) - 1U) << RP_SSI_CTRL0_DATA_BIT_SHIFT)
  54. #define RP_SSI_CTRL0_MASK \
  55. (RP_SSI_CTRL0_FRF_MASK | RP_SSI_CTRL0_TMOD_MASK | RP_SSI_CTRL0_DATA_BIT_MASK)
  56. #define RP_SSI_ENABLE_SSI (1U << 0U)
  57. #define RP_SSI_XIP_SPI_CTRL0_FORMAT_STD_SPI (0U << 0U)
  58. #define RP_SSI_XIP_SPI_CTRL0_FORMAT_SPLIT (1U << 0U)
  59. #define RP_SSI_XIP_SPI_CTRL0_FORMAT_FRF (2U << 0U)
  60. #define RP_SSI_XIP_SPI_CTRL0_ADDRESS_LENGTH(x) (((x) * 2U) << 2U)
  61. #define RP_SSI_XIP_SPI_CTRL0_INSTR_LENGTH_8b (2U << 8U)
  62. #define RP_SSI_XIP_SPI_CTRL0_WAIT_CYCLES(x) (((x) * 8U) << 11U)
  63. #define RP_SSI_XIP_SPI_CTRL0_XIP_CMD_SHIFT 24U
  64. #define RP_SSI_XIP_SPI_CTRL0_XIP_CMD(x) ((x) << RP_SSI_XIP_SPI_CTRL0_XIP_CMD_SHIFT)
  65. #define RP_SSI_XIP_SPI_CTRL0_TRANS_1C1A (0U << 0U)
  66. #define RP_SSI_XIP_SPI_CTRL0_TRANS_1C2A (1U << 0U)
  67. #define RP_SSI_XIP_SPI_CTRL0_TRANS_2C2A (2U << 0U)
  68. #define RP_PADS_QSPI_BASE_ADDR 0x40020000U
  69. #define RP_PADS_QSPI_GPIO_SCLK (RP_PADS_QSPI_BASE_ADDR + 0x04U)
  70. #define RP_PADS_QSPI_GPIO_SD0 (RP_PADS_QSPI_BASE_ADDR + 0x08U)
  71. #define RP_PADS_QSPI_GPIO_SD1 (RP_PADS_QSPI_BASE_ADDR + 0x0cU)
  72. #define RP_PADS_QSPI_GPIO_SD2 (RP_PADS_QSPI_BASE_ADDR + 0x10U)
  73. #define RP_PADS_QSPI_GPIO_SD3 (RP_PADS_QSPI_BASE_ADDR + 0x14U)
  74. #define RP_PADS_QSPI_GPIO_SCLK_FAST_SLEW 0x00000001U
  75. #define RP_PADS_QSPI_GPIO_SCLK_8mA_DRIVE 0x00000020U
  76. #define RP_PADS_QSPI_GPIO_SCLK_IE 0x00000040U
  77. #define RP_PADS_QSPI_GPIO_SD0_OD_BITS 0x00000080U
  78. #define RP_PADS_QSPI_GPIO_SD0_PUE_BITS 0x00000008U
  79. #define RP_PADS_QSPI_GPIO_SD0_PDE_BITS 0x00000004U
  80. #define RP_RESETS_BASE_ADDR 0x4000c000U
  81. #define RP_RESETS_RESET (RP_RESETS_BASE_ADDR + 0x00U)
  82. #define RP_RESETS_RESET_DONE (RP_RESETS_BASE_ADDR + 0x08U)
  83. #define RP_RESETS_RESET_IO_QSPI_BITS 0x00000040U
  84. #define RP_RESETS_RESET_PADS_QSPI_BITS 0x00000200U
  85. // SPI Flash defines
  86. #define SPI_FLASH_OPCODE_MASK 0x00ffU
  87. #define SPI_FLASH_OPCODE(x) ((x) & SPI_FLASH_OPCODE_MASK)
  88. #define SPI_FLASH_DUMMY_MASK 0x0700U
  89. #define SPI_FLASH_DUMMY_SHIFT 8U
  90. #define SPI_FLASH_DUMMY_LEN(x) (((x) << SPI_FLASH_DUMMY_SHIFT) & SPI_FLASH_DUMMY_MASK)
  91. #define SPI_FLASH_OPCODE_MODE_MASK 0x0800U
  92. #define SPI_FLASH_OPCODE_ONLY (0U << 11U)
  93. #define SPI_FLASH_OPCODE_3B_ADDR (1U << 11U)
  94. #define SPI_FLASH_DATA_MASK 0x1000U
  95. #define SPI_FLASH_DATA_SHIFT 12U
  96. #define SPI_FLASH_DATA_IN (0U << SPI_FLASH_DATA_SHIFT)
  97. #define SPI_FLASH_DATA_OUT (1U << SPI_FLASH_DATA_SHIFT)
  98. #define SPI_FLASH_CMD_WRITE_ENABLE \
  99. (SPI_FLASH_OPCODE_ONLY | SPI_FLASH_DUMMY_LEN(0) | SPI_FLASH_OPCODE(0x06U))
  100. #define SPI_FLASH_CMD_PAGE_PROGRAM \
  101. (SPI_FLASH_OPCODE_3B_ADDR | SPI_FLASH_DATA_OUT | SPI_FLASH_DUMMY_LEN(0) | \
  102. SPI_FLASH_OPCODE(0x02))
  103. #define SPI_FLASH_CMD_SECTOR_ERASE \
  104. (SPI_FLASH_OPCODE_3B_ADDR | SPI_FLASH_DUMMY_LEN(0) | SPI_FLASH_OPCODE(0x20U))
  105. #define SPI_FLASH_CMD_CHIP_ERASE \
  106. (SPI_FLASH_OPCODE_ONLY | SPI_FLASH_DUMMY_LEN(0) | SPI_FLASH_OPCODE(0x60U))
  107. #define SPI_FLASH_CMD_READ_STATUS \
  108. (SPI_FLASH_OPCODE_ONLY | SPI_FLASH_DATA_IN | SPI_FLASH_DUMMY_LEN(0) | SPI_FLASH_OPCODE(0x05U))
  109. #define SPI_FLASH_CMD_READ_JEDEC_ID \
  110. (SPI_FLASH_OPCODE_ONLY | SPI_FLASH_DATA_IN | SPI_FLASH_DUMMY_LEN(0) | SPI_FLASH_OPCODE(0x9FU))
  111. #define SPI_FLASH_CMD_READ_SFDP \
  112. (SPI_FLASH_OPCODE_3B_ADDR | SPI_FLASH_DATA_IN | SPI_FLASH_DUMMY_LEN(1) | \
  113. SPI_FLASH_OPCODE(0x5AU))
  114. #define SPI_FLASH_CMD_WAKE_UP \
  115. (SPI_FLASH_OPCODE_ONLY | SPI_FLASH_DUMMY_LEN(0) | SPI_FLASH_OPCODE(0xABU))
  116. #define SPI_FLASH_CMD_READ_DATA \
  117. (SPI_FLASH_OPCODE_3B_ADDR | SPI_FLASH_DATA_IN | SPI_FLASH_DUMMY_LEN(0) | \
  118. SPI_FLASH_OPCODE(0x03U))
  119. #define SPI_FLASH_STATUS_BUSY 0x01U
  120. #define SPI_FLASH_STATUS_WRITE_ENABLED 0x02U
  121. #define RP2040_IO_PADS_BITS (RP_RESETS_RESET_IO_QSPI_BITS | RP_RESETS_RESET_PADS_QSPI_BITS)
  122. #define W25X_CMD_RESET_ENABLE (0x66U)
  123. #define W25X_CMD_RESET (0x99U)
  124. #define TAG "VgmRp2040"
  125. static bool rp2040_spi_gpio_init(void) {
  126. bool success = false;
  127. do {
  128. if(!target_write_memory_32(
  129. RP_RESETS_RESET | RP_REG_ACCESS_WRITE_ATOMIC_BITSET, RP2040_IO_PADS_BITS))
  130. break;
  131. if(!target_write_memory_32(
  132. RP_RESETS_RESET | RP_REG_ACCESS_WRITE_ATOMIC_BITCLR, RP2040_IO_PADS_BITS))
  133. break;
  134. uint32_t reset_done = 0;
  135. while((reset_done & RP2040_IO_PADS_BITS) != RP2040_IO_PADS_BITS) {
  136. if(!target_read_memory_32(RP_RESETS_RESET_DONE, &reset_done)) break;
  137. }
  138. if(reset_done == 0) break;
  139. if(!target_write_memory_32(RP_GPIO_QSPI_SCLK_CTRL, 0)) break;
  140. if(!target_write_memory_32(RP_GPIO_QSPI_CS_CTRL, 0)) break;
  141. if(!target_write_memory_32(RP_GPIO_QSPI_SD0_CTRL, 0)) break;
  142. if(!target_write_memory_32(RP_GPIO_QSPI_SD1_CTRL, 0)) break;
  143. if(!target_write_memory_32(RP_GPIO_QSPI_SD2_CTRL, 0)) break;
  144. if(!target_write_memory_32(RP_GPIO_QSPI_SD3_CTRL, 0)) break;
  145. success = true;
  146. } while(false);
  147. return success;
  148. }
  149. // Configure SSI in regular SPI mode
  150. static bool rp2040_spi_init(void) {
  151. bool success = false;
  152. do {
  153. // Disable SSI
  154. if(!target_write_memory_32(RP_SSI_ENABLE, 0)) break;
  155. // Clear error all flags
  156. if(!target_read_memory_32(RP_SSI_SR, NULL)) break;
  157. // Clear all pending interrupts
  158. if(!target_read_memory_32(RP_SSI_ICR, NULL)) break;
  159. // Set SPI clock divisor (Fclk_out = Fssi_clk / RP_SSI_BAUD)
  160. if(!target_write_memory_32(RP_SSI_BAUD, 6UL)) break;
  161. // Set SPI configuration:
  162. // - Regular 1-bit SPI frame format,
  163. // - Frame size = 8 bit,
  164. // - Both transmit and receive
  165. if(!target_write_memory_32(
  166. RP_SSI_CTRL0,
  167. RP_SSI_CTRL0_FRF_SERIAL | RP_SSI_CTRL0_DATA_BITS(8) | RP_SSI_CTRL0_TMOD_BIDI))
  168. break;
  169. if(!target_write_memory_32(RP_SSI_SER, 1)) break;
  170. // Enable SSI
  171. if(!target_write_memory_32(RP_SSI_ENABLE, 1)) break;
  172. success = true;
  173. } while(false);
  174. return success;
  175. }
  176. // Force CS pin to a chosen state
  177. static bool rp2040_spi_chip_select(uint32_t state) {
  178. bool success = false;
  179. do {
  180. uint32_t cs_value;
  181. // Read GPIO control register
  182. if(!target_read_memory_32(RP_GPIO_QSPI_CS_CTRL, &cs_value)) break;
  183. // Modify GPIO control register
  184. if(!target_write_memory_32(
  185. RP_GPIO_QSPI_CS_CTRL, (cs_value & (~RP_GPIO_QSPI_CS_DRIVE_MASK)) | state))
  186. break;
  187. success = true;
  188. } while(false);
  189. return success;
  190. }
  191. // Perform an SPI transaction (transmit one byte, receive one byte at the same time)
  192. static bool rp2040_spi_txrx(uint8_t tx_data, uint8_t* rx_data) {
  193. bool success = false;
  194. do {
  195. // Write to SSI data register 0
  196. if(!target_write_memory_32(RP_SSI_DR0, tx_data)) break;
  197. uint32_t value;
  198. // Read from SSI data register 0
  199. if(!target_read_memory_32(RP_SSI_DR0, &value)) break;
  200. if(rx_data) {
  201. *rx_data = value;
  202. }
  203. success = true;
  204. } while(false);
  205. return success;
  206. }
  207. // Prepare SPI flash operation
  208. static bool rp2040_spi_setup_txrx(uint16_t command, uint32_t address, size_t data_size) {
  209. bool success = false;
  210. do {
  211. // Number of data frames = data_size
  212. if(!target_write_memory_32(RP_SSI_CTRL1, data_size)) break;
  213. // Select flash chip
  214. if(!rp2040_spi_chip_select(RP_GPIO_QSPI_CS_DRIVE_LOW)) break;
  215. // Transmit command
  216. const uint8_t opcode = command & SPI_FLASH_OPCODE_MASK;
  217. if(!rp2040_spi_txrx(opcode, NULL)) break;
  218. // Transmit 24-bit address for commands that require it
  219. if((command & SPI_FLASH_OPCODE_MODE_MASK) == SPI_FLASH_OPCODE_3B_ADDR) {
  220. if(!rp2040_spi_txrx((address >> 16U) & 0xFFUL, NULL)) break;
  221. if(!rp2040_spi_txrx((address >> 8U) & 0xFFUL, NULL)) break;
  222. if(!rp2040_spi_txrx(address & 0xFFUL, NULL)) break;
  223. }
  224. const size_t inter_length = (command & SPI_FLASH_DUMMY_MASK) >> SPI_FLASH_DUMMY_SHIFT;
  225. size_t i;
  226. for(i = 0; i < inter_length; ++i) {
  227. if(!rp2040_spi_txrx(0, NULL)) break;
  228. }
  229. if(i < inter_length) break;
  230. success = true;
  231. } while(false);
  232. return success;
  233. }
  234. static bool rp2040_spi_read(uint16_t command, uint32_t address, void* data, size_t data_size) {
  235. bool success = false;
  236. do {
  237. if(!rp2040_spi_setup_txrx(command, address, data_size)) break;
  238. uint8_t* rx_data = data;
  239. size_t rx_data_size;
  240. for(rx_data_size = 0; rx_data_size < data_size; ++rx_data_size) {
  241. if(!rp2040_spi_txrx(0, &rx_data[rx_data_size])) break;
  242. }
  243. if(rx_data_size < data_size) break;
  244. rp2040_spi_chip_select(RP_GPIO_QSPI_CS_DRIVE_HIGH);
  245. success = true;
  246. } while(false);
  247. return success;
  248. }
  249. static bool
  250. rp2040_spi_write(uint16_t command, uint32_t address, const void* data, const size_t data_size) {
  251. bool success = false;
  252. do {
  253. if(!rp2040_spi_setup_txrx(command, address, data_size)) break;
  254. const uint8_t* tx_data = data;
  255. size_t tx_data_size;
  256. for(tx_data_size = 0; tx_data_size < data_size; ++tx_data_size) {
  257. if(!rp2040_spi_txrx(tx_data[tx_data_size], NULL)) break;
  258. }
  259. if(tx_data_size < data_size) break;
  260. if(!rp2040_spi_chip_select(RP_GPIO_QSPI_CS_DRIVE_HIGH)) break;
  261. success = true;
  262. } while(false);
  263. return success;
  264. }
  265. static bool rp2040_spi_run_command(uint16_t command, uint32_t address) {
  266. return rp2040_spi_write(command, address, NULL, 0);
  267. }
  268. // Custom procedure to reset the W25X SPI flash
  269. static bool rp2040_w25xx_flash_reset(void) {
  270. bool success = false;
  271. do {
  272. if(!rp2040_spi_txrx(W25X_CMD_RESET_ENABLE, NULL)) break;
  273. if(!rp2040_spi_txrx(W25X_CMD_RESET, NULL)) break;
  274. furi_delay_us(50);
  275. success = true;
  276. } while(false);
  277. return success;
  278. }
  279. bool rp2040_init(void) {
  280. bool success = false;
  281. do {
  282. if(!rp2040_spi_gpio_init()) {
  283. FURI_LOG_E(TAG, "Failed to initialize SPI pins");
  284. break;
  285. }
  286. if(!rp2040_spi_init()) {
  287. FURI_LOG_E(TAG, "Failed to configure SPI hardware");
  288. break;
  289. }
  290. if(!rp2040_w25xx_flash_reset()) {
  291. FURI_LOG_E(TAG, "Failed to reset SPI flash");
  292. break;
  293. }
  294. success = true;
  295. } while(false);
  296. return success;
  297. }
  298. bool rp2040_flash_read_data(uint32_t address, void* data, size_t data_size) {
  299. bool success = false;
  300. do {
  301. if(!rp2040_spi_read(SPI_FLASH_CMD_READ_DATA, address, data, data_size)) {
  302. FURI_LOG_E(TAG, "Failed to read data");
  303. break;
  304. }
  305. success = true;
  306. } while(false);
  307. return success;
  308. }
  309. bool rp2040_flash_erase_sector(uint32_t address) {
  310. bool success = false;
  311. do {
  312. if(!rp2040_spi_run_command(SPI_FLASH_CMD_WRITE_ENABLE, 0)) {
  313. FURI_LOG_E(TAG, "Failed to issue WRITE_ENABLE command");
  314. break;
  315. }
  316. uint8_t status;
  317. if(!rp2040_spi_read(SPI_FLASH_CMD_READ_STATUS, 0U, &status, sizeof(status))) {
  318. FURI_LOG_E(TAG, "Failed to issue READ_STATUS command");
  319. break;
  320. }
  321. if((status & SPI_FLASH_STATUS_WRITE_ENABLED) == 0) {
  322. FURI_LOG_E(TAG, "Failed to enable write mode, status byte: 0x%02X", status);
  323. break;
  324. }
  325. if(!rp2040_spi_run_command(SPI_FLASH_CMD_SECTOR_ERASE, address)) {
  326. FURI_LOG_E(TAG, "Failed to issue SECTOR_ERASE command");
  327. break;
  328. }
  329. do {
  330. if(!rp2040_spi_read(SPI_FLASH_CMD_READ_STATUS, 0U, &status, sizeof(status))) {
  331. FURI_LOG_E(TAG, "Failed to issue READ_STATUS command");
  332. break;
  333. }
  334. } while(status & SPI_FLASH_STATUS_BUSY);
  335. if(status & SPI_FLASH_STATUS_BUSY) break;
  336. success = true;
  337. } while(false);
  338. return success;
  339. }
  340. bool rp2040_flash_program_page(uint32_t address, const void* data, size_t data_size) {
  341. bool success = false;
  342. do {
  343. if(!rp2040_spi_run_command(SPI_FLASH_CMD_WRITE_ENABLE, 0)) {
  344. FURI_LOG_E(TAG, "Failed to issue WRITE_ENABLE command");
  345. break;
  346. }
  347. uint8_t status;
  348. if(!rp2040_spi_read(SPI_FLASH_CMD_READ_STATUS, 0U, &status, sizeof(status))) {
  349. FURI_LOG_E(TAG, "Failed to issue READ_STATUS command");
  350. break;
  351. }
  352. if((status & SPI_FLASH_STATUS_WRITE_ENABLED) == 0) {
  353. FURI_LOG_E(TAG, "Failed to enable write mode, status byte: 0x%02X", status);
  354. break;
  355. }
  356. if(!rp2040_spi_write(SPI_FLASH_CMD_PAGE_PROGRAM, address, data, data_size)) {
  357. FURI_LOG_E(TAG, "Failed to issue PAGE_PROGRAM command");
  358. break;
  359. }
  360. do {
  361. if(!rp2040_spi_read(SPI_FLASH_CMD_READ_STATUS, 0U, &status, sizeof(status))) {
  362. FURI_LOG_E(TAG, "Failed to issue READ_STATUS command");
  363. break;
  364. }
  365. } while(status & SPI_FLASH_STATUS_BUSY);
  366. if(status & SPI_FLASH_STATUS_BUSY) break;
  367. success = true;
  368. } while(false);
  369. return success;
  370. }