hwdef: RadioLinkPIX6 uses SPL06 driver
[ardupilot.git] / libraries / AP_Logger / AP_Logger_W25NXX.cpp
blobdf3e8bcea2d894348e71e1580f3f3b702575ba1d
1 /*
2 logging to a DataFlash block based storage device on SPI
3 */
6 #include <AP_HAL/AP_HAL.h>
8 #include "AP_Logger_W25NXX.h"
10 #if HAL_LOGGING_FLASH_W25NXX_ENABLED
12 #include <stdio.h>
14 extern const AP_HAL::HAL& hal;
16 #define JEDEC_WRITE_ENABLE 0x06
17 #define JEDEC_WRITE_DISABLE 0x04
18 #define JEDEC_READ_STATUS 0x05
19 #define JEDEC_WRITE_STATUS 0x01
20 #define JEDEC_READ_DATA 0x03
21 #define JEDEC_PAGE_DATA_READ 0x13
22 #define JEDEC_FAST_READ 0x0b
23 #define JEDEC_DEVICE_ID 0x9F
24 #define JEDEC_PAGE_WRITE 0x02
25 #define JEDEC_PROGRAM_EXECUTE 0x10
27 #define JEDEC_DEVICE_RESET 0xFF
28 #define JEDEC_BLOCK_ERASE 0xD8 // 128K erase
30 #define JEDEC_STATUS_BUSY 0x01
31 #define JEDEC_STATUS_WRITEPROTECT 0x02
33 #define W25NXX_STATUS_REG 0xC0
34 #define W25NXX_PROT_REG 0xA0
35 #define W25NXX_CONF_REG 0xB0
36 #define W25NXX_STATUS_EFAIL 0x04
37 #define W25NXX_STATUS_PFAIL 0x08
39 #define W25NXX_PROT_SRP1_ENABLE (1 << 0)
40 #define W25NXX_PROT_WP_E_ENABLE (1 << 1)
41 #define W25NXX_PROT_TB_ENABLE (1 << 2)
42 #define W25NXX_PROT_PB0_ENABLE (1 << 3)
43 #define W25NXX_PROT_PB1_ENABLE (1 << 4)
44 #define W25NXX_PROT_PB2_ENABLE (1 << 5)
45 #define W25NXX_PROT_PB3_ENABLE (1 << 6)
46 #define W25NXX_PROT_SRP2_ENABLE (1 << 7)
48 #define W25NXX_CONFIG_ECC_ENABLE (1 << 4)
49 #define W25NXX_CONFIG_BUFFER_READ_MODE (1 << 3)
51 #define W25NXX_TIMEOUT_PAGE_READ_US 60 // tREmax = 60us (ECC enabled)
52 #define W25NXX_TIMEOUT_PAGE_PROGRAM_US 700 // tPPmax = 700us
53 #define W25NXX_TIMEOUT_BLOCK_ERASE_MS 10 // tBEmax = 10ms
54 #define W25NXX_TIMEOUT_RESET_MS 500 // tRSTmax = 500ms
56 #define W25N01G_NUM_BLOCKS 1024
57 #define W25N02K_NUM_BLOCKS 2048
59 #define JEDEC_ID_WINBOND_W25N01GV 0xEFAA21
60 #define JEDEC_ID_WINBOND_W25N02KV 0xEFAA22
62 void AP_Logger_W25NXX::Init()
64 dev = hal.spi->get_device("dataflash");
65 if (!dev) {
66 AP_HAL::panic("PANIC: AP_Logger W25NXX device not found");
67 return;
70 dev_sem = dev->get_semaphore();
72 if (!getSectorCount()) {
73 flash_died = true;
74 return;
77 flash_died = false;
79 // reset the device
80 WaitReady();
82 WITH_SEMAPHORE(dev_sem);
83 uint8_t b = JEDEC_DEVICE_RESET;
84 dev->transfer(&b, 1, nullptr, 0);
86 hal.scheduler->delay(W25NXX_TIMEOUT_RESET_MS);
88 // disable write protection
89 WriteStatusReg(W25NXX_PROT_REG, 0);
90 // enable ECC and buffer mode
91 WriteStatusReg(W25NXX_CONF_REG, W25NXX_CONFIG_ECC_ENABLE|W25NXX_CONFIG_BUFFER_READ_MODE);
93 printf("W25NXX status: SR-1=0x%x, SR-2=0x%x, SR-3=0x%x\n",
94 ReadStatusRegBits(W25NXX_PROT_REG),
95 ReadStatusRegBits(W25NXX_CONF_REG),
96 ReadStatusRegBits(W25NXX_STATUS_REG));
98 AP_Logger_Block::Init();
102 wait for busy flag to be cleared
104 void AP_Logger_W25NXX::WaitReady()
106 if (flash_died) {
107 return;
110 uint32_t t = AP_HAL::millis();
111 while (Busy()) {
112 hal.scheduler->delay_microseconds(100);
113 if (AP_HAL::millis() - t > 5000) {
114 printf("DataFlash: flash_died\n");
115 flash_died = true;
116 break;
121 bool AP_Logger_W25NXX::getSectorCount(void)
123 WaitReady();
125 WITH_SEMAPHORE(dev_sem);
127 // Read manufacturer ID
128 uint8_t cmd = JEDEC_DEVICE_ID;
129 uint8_t buf[4]; // buffer not yet allocated
130 dev->transfer(&cmd, 1, buf, 4);
132 uint32_t id = buf[1] << 16 | buf[2] << 8 | buf[3];
134 switch (id) {
135 case JEDEC_ID_WINBOND_W25N01GV:
136 df_PageSize = 2048;
137 df_PagePerBlock = 64;
138 df_PagePerSector = 64; // make sectors equivalent to block
139 flash_blockNum = W25N01G_NUM_BLOCKS;
140 break;
141 case JEDEC_ID_WINBOND_W25N02KV:
142 df_PageSize = 2048;
143 df_PagePerBlock = 64;
144 df_PagePerSector = 64; // make sectors equivalent to block
145 flash_blockNum = W25N02K_NUM_BLOCKS;
146 break;
148 default:
149 hal.scheduler->delay(2000);
150 printf("Unknown SPI Flash 0x%08x\n", id);
151 return false;
154 df_NumPages = flash_blockNum * df_PagePerBlock;
156 printf("SPI Flash 0x%08x found pages=%u\n", id, df_NumPages);
157 return true;
160 // Read the status register bits
161 uint8_t AP_Logger_W25NXX::ReadStatusRegBits(uint8_t bits)
163 WITH_SEMAPHORE(dev_sem);
164 uint8_t cmd[2] { JEDEC_READ_STATUS, bits };
165 uint8_t status;
166 dev->transfer(cmd, 2, &status, 1);
167 return status;
170 void AP_Logger_W25NXX::WriteStatusReg(uint8_t reg, uint8_t bits)
172 WaitReady();
173 WITH_SEMAPHORE(dev_sem);
174 uint8_t cmd[3] = {JEDEC_WRITE_STATUS, reg, bits};
175 dev->transfer(cmd, 3, nullptr, 0);
178 bool AP_Logger_W25NXX::Busy()
180 uint8_t status = ReadStatusRegBits(W25NXX_STATUS_REG);
182 if ((status & W25NXX_STATUS_PFAIL) != 0) {
183 printf("Program failure!\n");
185 if ((status & W25NXX_STATUS_EFAIL) != 0) {
186 printf("Erase failure!\n");
189 return (status & JEDEC_STATUS_BUSY) != 0;
193 send a command with an address
195 void AP_Logger_W25NXX::send_command_addr(uint8_t command, uint32_t PageAdr)
197 uint8_t cmd[4];
198 cmd[0] = command;
199 cmd[1] = (PageAdr >> 16) & 0xff;
200 cmd[2] = (PageAdr >> 8) & 0xff;
201 cmd[3] = (PageAdr >> 0) & 0xff;
203 dev->transfer(cmd, 4, nullptr, 0);
206 void AP_Logger_W25NXX::PageToBuffer(uint32_t pageNum)
208 if (pageNum == 0 || pageNum > df_NumPages+1) {
209 printf("Invalid page read %u\n", pageNum);
210 memset(buffer, 0xFF, df_PageSize);
211 df_Read_PageAdr = pageNum;
212 return;
215 // we already just read this page
216 if (pageNum == df_Read_PageAdr && read_cache_valid) {
217 return;
220 df_Read_PageAdr = pageNum;
222 WaitReady();
224 uint32_t PageAdr = (pageNum-1);
227 WITH_SEMAPHORE(dev_sem);
228 // read page into internal buffer
229 send_command_addr(JEDEC_PAGE_DATA_READ, PageAdr);
232 // read from internal buffer into our buffer
233 WaitReady();
235 WITH_SEMAPHORE(dev_sem);
236 dev->set_chip_select(true);
237 uint8_t cmd[4];
238 cmd[0] = JEDEC_READ_DATA;
239 cmd[1] = (0 >> 8) & 0xff; // column address zero
240 cmd[2] = (0 >> 0) & 0xff; // column address zero
241 cmd[3] = 0; // dummy
242 dev->transfer(cmd, 4, nullptr, 0);
243 dev->transfer(nullptr, 0, buffer, df_PageSize);
244 dev->set_chip_select(false);
246 read_cache_valid = true;
250 void AP_Logger_W25NXX::BufferToPage(uint32_t pageNum)
252 if (pageNum == 0 || pageNum > df_NumPages+1) {
253 printf("Invalid page write %u\n", pageNum);
254 return;
257 // just wrote the cached page
258 if (pageNum != df_Read_PageAdr) {
259 read_cache_valid = false;
262 WriteEnable();
264 uint32_t PageAdr = (pageNum-1);
266 WITH_SEMAPHORE(dev_sem);
268 // write our buffer into internal buffer
269 dev->set_chip_select(true);
271 uint8_t cmd[3];
272 cmd[0] = JEDEC_PAGE_WRITE;
273 cmd[1] = (0 >> 8) & 0xff; // column address zero
274 cmd[2] = (0 >> 0) & 0xff; // column address zero
276 dev->transfer(cmd, 3, nullptr, 0);
277 dev->transfer(buffer, df_PageSize, nullptr, 0);
278 dev->set_chip_select(false);
281 // write from internal buffer into page
283 WITH_SEMAPHORE(dev_sem);
284 send_command_addr(JEDEC_PROGRAM_EXECUTE, PageAdr);
289 erase one sector (sizes varies with hw)
291 void AP_Logger_W25NXX::SectorErase(uint32_t blockNum)
293 WriteEnable();
294 WITH_SEMAPHORE(dev_sem);
296 uint32_t PageAdr = blockNum * df_PagePerBlock;
297 send_command_addr(JEDEC_BLOCK_ERASE, PageAdr);
301 erase one 4k sector
303 void AP_Logger_W25NXX::Sector4kErase(uint32_t sectorNum)
305 SectorErase(sectorNum);
308 void AP_Logger_W25NXX::StartErase()
310 WriteEnable();
312 WITH_SEMAPHORE(dev_sem);
314 // just erase the first block, others will follow in InErase
315 send_command_addr(JEDEC_BLOCK_ERASE, 0);
317 erase_block = 1;
318 erase_start_ms = AP_HAL::millis();
319 printf("Dataflash: erase started\n");
322 bool AP_Logger_W25NXX::InErase()
324 if (erase_start_ms && !Busy()) {
325 if (erase_block < flash_blockNum) {
326 SectorErase(erase_block++);
327 } else {
328 printf("Dataflash: erase done (%u ms)\n", AP_HAL::millis() - erase_start_ms);
329 erase_start_ms = 0;
330 erase_block = 0;
333 return erase_start_ms != 0;
336 void AP_Logger_W25NXX::WriteEnable(void)
338 WaitReady();
339 WITH_SEMAPHORE(dev_sem);
340 uint8_t b = JEDEC_WRITE_ENABLE;
341 dev->transfer(&b, 1, nullptr, 0);
344 #endif // HAL_LOGGING_FLASH_W25NXX_ENABLED