LCOV - code coverage report
Current view: top level - AP_Logger - AP_Logger_Flash_JEDEC.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 106 155 68.4 %
Date: 2026-09-09 11:02:08 Functions: 13 14 92.9 %

          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

Generated by: LCOV version 1.14