Line data Source code
1 : /*
2 : logging to a DataFlash block based storage device on SPI
3 : */
4 :
5 : #include "AP_Logger_config.h"
6 :
7 : #if HAL_LOGGING_FLASH_JEDEC_ENABLED
8 :
9 : #include <AP_HAL/AP_HAL.h>
10 :
11 : #include "AP_Logger_Flash_JEDEC.h"
12 :
13 : #include <stdio.h>
14 :
15 : extern const AP_HAL::HAL& hal;
16 :
17 : #define JEDEC_WRITE_ENABLE 0x06
18 : #define JEDEC_WRITE_DISABLE 0x04
19 : #define JEDEC_READ_STATUS 0x05
20 : #define JEDEC_WRITE_STATUS 0x01
21 : #define JEDEC_READ_DATA 0x03
22 : #define JEDEC_FAST_READ 0x0b
23 : #define JEDEC_DEVICE_ID 0x9F
24 : #define JEDEC_PAGE_WRITE 0x02
25 :
26 : #define JEDEC_BULK_ERASE 0xC7
27 : #define JEDEC_SECTOR4_ERASE 0x20 // 4k erase
28 : #define JEDEC_BLOCK32_ERASE 0x52 // 32K erase
29 : #define JEDEC_BLOCK64_ERASE 0xD8 // 64K erase
30 :
31 : #define JEDEC_STATUS_BUSY 0x01
32 : #define JEDEC_STATUS_WRITEPROTECT 0x02
33 : #define JEDEC_STATUS_BP0 0x04
34 : #define JEDEC_STATUS_BP1 0x08
35 : #define JEDEC_STATUS_BP2 0x10
36 : #define JEDEC_STATUS_TP 0x20
37 : #define JEDEC_STATUS_SEC 0x40
38 : #define JEDEC_STATUS_SRP0 0x80
39 :
40 : /*
41 : flash device IDs taken from betaflight flash_m25p16.c
42 :
43 : Format is manufacturer, memory type, then capacity
44 : */
45 : #define JEDEC_ID_MACRONIX_MX25L3206E 0xC22016
46 : #define JEDEC_ID_MACRONIX_MX25L6406E 0xC22017
47 : #define JEDEC_ID_MACRONIX_MX25L25635E 0xC22019
48 : #define JEDEC_ID_MICRON_M25P16 0x202015
49 : #define JEDEC_ID_MICRON_N25Q064 0x20BA17
50 : #define JEDEC_ID_MICRON_N25Q128 0x20ba18
51 : #define JEDEC_ID_WINBOND_W25Q16 0xEF4015
52 : #define JEDEC_ID_WINBOND_W25Q32 0xEF4016
53 : #define JEDEC_ID_WINBOND_W25X32 0xEF3016
54 : #define JEDEC_ID_WINBOND_W25Q64 0xEF4017
55 : #define JEDEC_ID_WINBOND_W25Q128 0xEF4018
56 : #define JEDEC_ID_WINBOND_W25Q256 0xEF4019
57 : #define JEDEC_ID_WINBOND_W25Q128_2 0xEF7018
58 : #define JEDEC_ID_CYPRESS_S25FL064L 0x016017
59 : #define JEDEC_ID_CYPRESS_S25FL128L 0x016018
60 : #define JEDEC_ID_GIGA_GD25Q16E 0xC84015
61 : #define JEDEC_ID_ZBIT_ZB25VQ128 0x5E4018
62 : #define JEDEC_ID_FMSH_FM25Q64 0xA14017
63 : #define JEDEC_ID_FMSH_FM25Q128A 0xA14018
64 : #define JEDEC_ID_FMSH_FM25Q256 0xA14019
65 : #define JEDEC_ID_XTX_XT25F128F 0x0B4018
66 :
67 5 : void AP_Logger_Flash_JEDEC::Init()
68 : {
69 5 : dev = hal.spi->get_device("dataflash");
70 5 : if (!dev) {
71 0 : AP_HAL::panic("PANIC: AP_Logger SPIDeviceDriver not found");
72 : return;
73 : }
74 :
75 5 : dev_sem = dev->get_semaphore();
76 :
77 5 : if (!getSectorCount()) {
78 0 : flash_died = true;
79 0 : return;
80 : }
81 :
82 5 : if (use_32bit_address) {
83 0 : Enter4ByteAddressMode();
84 : }
85 :
86 5 : flash_died = false;
87 :
88 5 : AP_Logger_Block::Init();
89 :
90 : //flash_test();
91 : }
92 :
93 : /*
94 : wait for busy flag to be cleared
95 : */
96 72998 : void AP_Logger_Flash_JEDEC::WaitReady()
97 : {
98 72998 : if (flash_died) {
99 0 : return;
100 : }
101 :
102 72998 : uint32_t t = AP_HAL::millis();
103 72998 : while (Busy()) {
104 0 : hal.scheduler->delay_microseconds(100);
105 0 : if (AP_HAL::millis() - t > 5000) {
106 0 : printf("DataFlash: flash_died\n");
107 0 : flash_died = true;
108 0 : break;
109 : }
110 : }
111 : }
112 :
113 5 : bool AP_Logger_Flash_JEDEC::getSectorCount(void)
114 : {
115 5 : WaitReady();
116 :
117 5 : WITH_SEMAPHORE(dev_sem);
118 :
119 : // Read manufacturer ID
120 5 : uint8_t cmd = JEDEC_DEVICE_ID;
121 : uint8_t buf[4]; // buffer not yet allocated
122 5 : dev->transfer(&cmd, 1, buf, 4);
123 :
124 5 : uint32_t id = buf[0] << 16 | buf[1] << 8 | buf[2];
125 :
126 5 : uint32_t blocks = 0;
127 :
128 5 : switch (id) {
129 0 : case JEDEC_ID_WINBOND_W25Q16:
130 : case JEDEC_ID_MICRON_M25P16:
131 : case JEDEC_ID_GIGA_GD25Q16E:
132 0 : blocks = 32;
133 0 : df_PagePerBlock = 256;
134 0 : df_PagePerSector = 16;
135 0 : break;
136 5 : case JEDEC_ID_WINBOND_W25Q32:
137 : case JEDEC_ID_WINBOND_W25X32:
138 : case JEDEC_ID_MACRONIX_MX25L3206E:
139 5 : blocks = 64;
140 5 : df_PagePerBlock = 256;
141 5 : df_PagePerSector = 16;
142 5 : break;
143 0 : case JEDEC_ID_MICRON_N25Q064:
144 : case JEDEC_ID_WINBOND_W25Q64:
145 : case JEDEC_ID_FMSH_FM25Q64:
146 : case JEDEC_ID_MACRONIX_MX25L6406E:
147 : case JEDEC_ID_CYPRESS_S25FL064L:
148 0 : blocks = 128;
149 0 : df_PagePerBlock = 256;
150 0 : df_PagePerSector = 16;
151 0 : break;
152 0 : case JEDEC_ID_MICRON_N25Q128:
153 : case JEDEC_ID_WINBOND_W25Q128:
154 : case JEDEC_ID_WINBOND_W25Q128_2:
155 : case JEDEC_ID_FMSH_FM25Q128A:
156 : case JEDEC_ID_CYPRESS_S25FL128L:
157 : case JEDEC_ID_ZBIT_ZB25VQ128:
158 : case JEDEC_ID_XTX_XT25F128F:
159 0 : blocks = 256;
160 0 : df_PagePerBlock = 256;
161 0 : df_PagePerSector = 16;
162 0 : break;
163 0 : case JEDEC_ID_WINBOND_W25Q256:
164 : case JEDEC_ID_FMSH_FM25Q256:
165 : case JEDEC_ID_MACRONIX_MX25L25635E:
166 0 : blocks = 512;
167 0 : df_PagePerBlock = 256;
168 0 : df_PagePerSector = 16;
169 0 : use_32bit_address = true;
170 0 : break;
171 0 : default:
172 0 : hal.scheduler->delay(2000);
173 0 : printf("Unknown SPI Flash 0x%08x\n", id);
174 0 : return false;
175 : }
176 :
177 5 : df_PageSize = 256;
178 5 : df_NumPages = blocks * df_PagePerBlock;
179 5 : erase_cmd = JEDEC_BLOCK64_ERASE;
180 :
181 5 : printf("SPI Flash 0x%08x found pages=%u erase=%uk\n",
182 5 : id, df_NumPages, (df_PagePerBlock * (uint32_t)df_PageSize)/1024);
183 5 : return true;
184 :
185 5 : }
186 :
187 : // Read the status register
188 73005 : uint8_t AP_Logger_Flash_JEDEC::ReadStatusReg()
189 : {
190 73005 : WITH_SEMAPHORE(dev_sem);
191 73005 : uint8_t cmd = JEDEC_READ_STATUS;
192 : uint8_t status;
193 73005 : dev->transfer(&cmd, 1, &status, 1);
194 146010 : return status;
195 73005 : }
196 :
197 73005 : bool AP_Logger_Flash_JEDEC::Busy()
198 : {
199 73005 : return (ReadStatusReg() & (JEDEC_STATUS_BUSY | JEDEC_STATUS_SRP0)) != 0;
200 : }
201 :
202 0 : void AP_Logger_Flash_JEDEC::Enter4ByteAddressMode(void)
203 : {
204 0 : WITH_SEMAPHORE(dev_sem);
205 :
206 0 : const uint8_t cmd = 0xB7;
207 0 : dev->transfer(&cmd, 1, nullptr, 0);
208 0 : }
209 :
210 : /*
211 : send a command with an address
212 : */
213 72986 : void AP_Logger_Flash_JEDEC::send_command_addr(uint8_t command, uint32_t PageAdr)
214 : {
215 : uint8_t cmd[5];
216 72986 : cmd[0] = command;
217 72986 : if (use_32bit_address) {
218 0 : cmd[1] = (PageAdr >> 24) & 0xff;
219 0 : cmd[2] = (PageAdr >> 16) & 0xff;
220 0 : cmd[3] = (PageAdr >> 8) & 0xff;
221 0 : cmd[4] = (PageAdr >> 0) & 0xff;
222 : } else {
223 72986 : cmd[1] = (PageAdr >> 16) & 0xff;
224 72986 : cmd[2] = (PageAdr >> 8) & 0xff;
225 72986 : cmd[3] = (PageAdr >> 0) & 0xff;
226 : }
227 :
228 72986 : dev->transfer(cmd, use_32bit_address?5:4, nullptr, 0);
229 72986 : }
230 :
231 :
232 103889 : void AP_Logger_Flash_JEDEC::PageToBuffer(uint32_t pageNum)
233 : {
234 103889 : if (pageNum == 0 || pageNum > df_NumPages+1) {
235 0 : printf("Invalid page read %u\n", pageNum);
236 0 : memset(buffer, 0xFF, df_PageSize);
237 0 : df_Read_PageAdr = pageNum;
238 73604 : return;
239 : }
240 :
241 : // we already just read this page
242 103889 : if (pageNum == df_Read_PageAdr && read_cache_valid) {
243 73604 : return;
244 : }
245 :
246 30285 : df_Read_PageAdr = pageNum;
247 :
248 30285 : WaitReady();
249 :
250 30285 : uint32_t PageAdr = (pageNum-1) * df_PageSize;
251 :
252 30285 : WITH_SEMAPHORE(dev_sem);
253 30285 : dev->set_chip_select(true);
254 30285 : send_command_addr(JEDEC_READ_DATA, PageAdr);
255 30285 : dev->transfer(nullptr, 0, buffer, df_PageSize);
256 30285 : dev->set_chip_select(false);
257 :
258 30285 : read_cache_valid = true;
259 30285 : }
260 :
261 42525 : void AP_Logger_Flash_JEDEC::BufferToPage(uint32_t pageNum)
262 : {
263 42525 : if (pageNum == 0 || pageNum > df_NumPages+1) {
264 0 : printf("Invalid page write %u\n", pageNum);
265 0 : return;
266 : }
267 :
268 : // about to write the cached page
269 42525 : if (pageNum != df_Read_PageAdr) {
270 42461 : read_cache_valid = false;
271 : }
272 :
273 42525 : WriteEnable();
274 :
275 42525 : WITH_SEMAPHORE(dev_sem);
276 :
277 42525 : uint32_t PageAdr = (pageNum-1) * df_PageSize;
278 :
279 42525 : dev->set_chip_select(true);
280 42525 : send_command_addr(JEDEC_PAGE_WRITE, PageAdr);
281 42525 : dev->transfer(buffer, df_PageSize, nullptr, 0);
282 42525 : dev->set_chip_select(false);
283 42525 : }
284 :
285 : /*
286 : erase one sector (sizes varies with hw)
287 : */
288 169 : void AP_Logger_Flash_JEDEC::SectorErase(uint32_t blockNum)
289 : {
290 169 : WriteEnable();
291 :
292 169 : WITH_SEMAPHORE(dev_sem);
293 :
294 169 : uint32_t PageAdr = blockNum * df_PageSize * df_PagePerBlock;
295 169 : send_command_addr(erase_cmd, PageAdr);
296 169 : }
297 :
298 : /*
299 : erase one 4k sector
300 : */
301 7 : void AP_Logger_Flash_JEDEC::Sector4kErase(uint32_t sectorNum)
302 : {
303 7 : WriteEnable();
304 :
305 7 : WITH_SEMAPHORE(dev_sem);
306 7 : uint32_t SectorAddr = sectorNum * df_PageSize * df_PagePerSector;
307 7 : send_command_addr(JEDEC_SECTOR4_ERASE, SectorAddr);
308 7 : }
309 :
310 7 : void AP_Logger_Flash_JEDEC::StartErase()
311 : {
312 7 : WriteEnable();
313 :
314 7 : WITH_SEMAPHORE(dev_sem);
315 :
316 7 : uint8_t cmd = JEDEC_BULK_ERASE;
317 7 : dev->transfer(&cmd, 1, nullptr, 0);
318 :
319 7 : erase_start_ms = AP_HAL::millis();
320 7 : printf("Dataflash: erase started\n");
321 7 : }
322 :
323 3563 : bool AP_Logger_Flash_JEDEC::InErase()
324 : {
325 3563 : if (erase_start_ms && !Busy()) {
326 7 : printf("Dataflash: erase done (%u ms)\n", AP_HAL::millis() - erase_start_ms);
327 7 : erase_start_ms = 0;
328 : }
329 3563 : return erase_start_ms != 0;
330 : }
331 :
332 42708 : void AP_Logger_Flash_JEDEC::WriteEnable(void)
333 : {
334 42708 : WaitReady();
335 42708 : WITH_SEMAPHORE(dev_sem);
336 42708 : uint8_t b = JEDEC_WRITE_ENABLE;
337 42708 : dev->transfer(&b, 1, nullptr, 0);
338 42708 : }
339 :
340 : #endif // HAL_LOGGING_FLASH_JEDEC_ENABLED
|