spi_flash.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * (c) danielinux 2019
  3. *
  4. * GPLv.2
  5. *
  6. * See LICENSE for details
  7. */
  8. #include "system.h"
  9. #include "spi_drv.h"
  10. #define SPI_FLASH_SECTOR_SIZE (4096)
  11. #define SPI_FLASH_PAGE_SIZE (256)
  12. #define MDID 0x90
  13. #define RDSR 0x05
  14. #define WRSR 0x01
  15. # define ST_BUSY (1 << 0)
  16. # define ST_WEL (1 << 1)
  17. # define ST_BP0 (1 << 2)
  18. # define ST_BP1 (1 << 3)
  19. # define ST_BP2 (1 << 4)
  20. # define ST_BP3 (1 << 5)
  21. # define ST_AAI (1 << 6)
  22. # define ST_BRO (1 << 7)
  23. #define WREN 0x06
  24. #define WRDI 0x04
  25. #define SECTOR_ERASE 0x20
  26. #define BYTE_READ 0x03
  27. #define BYTE_WRITE 0x02
  28. #define AUTOINC 0xAD
  29. #define EWSR 0x50
  30. #define EBSY 0x70
  31. #define DBSY 0x80
  32. static enum write_mode {
  33. WB_WRITEPAGE = 0x00,
  34. SST_AAI = 0x01
  35. } chip_write_mode = WB_WRITEPAGE;
  36. static void write_address(uint32_t address)
  37. {
  38. spi_write((address & 0xFF00) >> 8);
  39. spi_read();
  40. spi_write((address & 0xFF0000) >> 16);
  41. spi_read();
  42. spi_write((address & 0xFF000000) >> 24);
  43. spi_read();
  44. }
  45. static uint8_t read_status(void)
  46. {
  47. uint8_t status;
  48. int i;
  49. spi_cs_on();
  50. spi_write(RDSR);
  51. spi_read();
  52. spi_write(0xFF);
  53. status = spi_read();
  54. spi_cs_off();
  55. return status;
  56. }
  57. static void spi_cmd(uint8_t cmd)
  58. {
  59. spi_cs_on();
  60. spi_write(cmd);
  61. spi_read();
  62. spi_cs_off();
  63. }
  64. static inline void flash_aai_enable(void)
  65. {
  66. spi_cmd(EBSY);
  67. }
  68. static inline void flash_aai_disable(void)
  69. {
  70. spi_cmd(DBSY);
  71. }
  72. static void flash_write_enable(void)
  73. {
  74. uint8_t status;
  75. do {
  76. spi_cmd(WREN);
  77. status = read_status();
  78. } while ((status & ST_WEL) == 0);
  79. }
  80. static void flash_write_disable(void)
  81. {
  82. uint8_t status;
  83. spi_cmd(WRDI);
  84. }
  85. static void wait_busy(void)
  86. {
  87. uint8_t status;
  88. do {
  89. status = read_status();
  90. } while(status & ST_BUSY);
  91. }
  92. static int spi_flash_write_page(uint32_t address, const void *data, int len)
  93. {
  94. const uint8_t *buf = data;
  95. int j = 0;
  96. while (len > 0) {
  97. wait_busy();
  98. flash_write_enable();
  99. spi_cs_on();
  100. spi_write(BYTE_WRITE);
  101. spi_read();
  102. write_address(address);
  103. do {
  104. spi_write(buf[j++]);
  105. address++;
  106. spi_read();
  107. len--;
  108. } while ((address % SPI_FLASH_PAGE_SIZE) != 0);
  109. spi_cs_off();
  110. }
  111. wait_busy();
  112. return j;
  113. }
  114. static int spi_flash_write_aai(uint32_t address, const void *data, int len)
  115. {
  116. const uint8_t *buf = data;
  117. int j = 0;
  118. int cont = 0;
  119. wait_busy();
  120. if (len < 1)
  121. return -1;
  122. while (len > 0) {
  123. if ((address & 0x01) || (len < 2)) {
  124. flash_write_enable();
  125. spi_cs_on();
  126. spi_write(BYTE_WRITE);
  127. spi_read();
  128. write_address(address);
  129. spi_write(buf[j++]);
  130. spi_read();
  131. spi_cs_off();
  132. len--;
  133. address++;
  134. } else {
  135. if (!cont) {
  136. flash_aai_enable();
  137. flash_write_enable();
  138. }
  139. spi_cs_on();
  140. spi_write(AUTOINC);
  141. spi_read();
  142. if (!cont) {
  143. /* First AAI transaction, send address. */
  144. write_address(address);
  145. cont = 1;
  146. }
  147. spi_write(buf[j++]);
  148. spi_read();
  149. spi_write(buf[j++]);
  150. spi_read();
  151. spi_cs_off();
  152. len -= 2;
  153. address += 2;
  154. read_status();
  155. }
  156. }
  157. if (cont) {
  158. flash_write_disable();
  159. flash_aai_disable();
  160. }
  161. wait_busy();
  162. return j;
  163. }
  164. /* --- */
  165. uint16_t spi_flash_probe(void)
  166. {
  167. uint8_t manuf, product, b0;
  168. int i;
  169. wait_busy();
  170. spi_cs_on();
  171. spi_write(MDID);
  172. b0 = spi_read();
  173. write_address(0);
  174. spi_write(0xFF);
  175. manuf = spi_read();
  176. spi_write(0xFF);
  177. product = spi_read();
  178. spi_cs_off();
  179. if (manuf == 0xBF)
  180. chip_write_mode = SST_AAI;
  181. if (manuf == 0xEF)
  182. chip_write_mode = WB_WRITEPAGE;
  183. #ifndef READONLY
  184. spi_cmd(EWSR);
  185. spi_cs_on();
  186. spi_write(WRSR);
  187. spi_read();
  188. spi_write(0x00);
  189. spi_read();
  190. spi_cs_off();
  191. #endif
  192. return (uint16_t)(manuf << 8 | product);
  193. }
  194. void spi_flash_sector_erase(uint32_t address)
  195. {
  196. uint8_t status;
  197. address &= (~(SPI_FLASH_SECTOR_SIZE - 1));
  198. wait_busy();
  199. flash_write_enable();
  200. spi_cs_on();
  201. spi_write(SECTOR_ERASE);
  202. spi_read();
  203. write_address(address);
  204. spi_cs_off();
  205. wait_busy();
  206. }
  207. int spi_flash_read(uint32_t address, void *data, int len)
  208. {
  209. uint8_t *buf = data;
  210. int i = 0;
  211. wait_busy();
  212. spi_cs_on();
  213. spi_write(BYTE_READ);
  214. spi_read();
  215. write_address(address);
  216. while (len > 0) {
  217. spi_write(0xFF);
  218. buf[i++] = spi_read();
  219. len--;
  220. }
  221. spi_cs_off();
  222. return i;
  223. }
  224. int spi_flash_write(uint32_t address, const void *data, int len)
  225. {
  226. if (chip_write_mode == SST_AAI)
  227. return spi_flash_write_aai(address, data, len);
  228. if (chip_write_mode == WB_WRITEPAGE)
  229. return spi_flash_write_page(address, data, len);
  230. return -1;
  231. }