#include "core/config.h" #include "drivers/extmem_flash/extmem_flash.h" #include "core/gpio.h" #include "core/main.h" #include "core/config_pins.h" #if AT45DB321D_POWER_MANAGEMENT static void ExtMem_SetPinPowerOn(); static void ExtMem_SetPinPowerOff(); #endif #if AT45DB321D_RESET_MANAGEMENT static void ExtMem_ResetPinAssert(); static void ExtMem_ResetPinRelease(); #endif #if AT45DB321D_HW_WR_PROTECT static void ExtMem_HwWrProtPinAssert(); static void ExtMem_HwWrProtPinRelease(); #endif static bool ExtMem_Write( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ); static bool ExtMem_Read( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ); static bool ExtMem_RangeCheck_Read( flash_address_t address, flash_address_t size ); static bool ExtMem_RangeCheck_Write( flash_address_t address, flash_address_t size ); static bool ExtMem_BankProtect( eExtMem_Bank_t bankId, bool protectStatus ); static bool ExtMem_CheckBankProtect( eExtMem_Bank_t bankId, bool * pActualProtectStatus ); static void ExtMem_Pins_Init(); static void ExtMem_Pins_Deinit(); static bool ExtMem_DeInit( bool bForce ); static bool ExtMem_Init(); volatile bool bExtMemStatus = false; #define EXTMEM_BANK0_SIZE (0x100000) // do not forget to change ACMMemory_Bank0 #define EXTMEM_BANK1_SIZE (0x100000) // do not forget to change ACMMemory_Bank1 static const ExtMem_Bank_Properties_t BanksProperties = { .factoryBankSize = EXTMEM_BANK0_SIZE, .userBankSize = EXTMEM_BANK1_SIZE }; const ExtMemPins_Handle_t ExtMemPinsHandle = { #if AT45DB321D_POWER_MANAGEMENT .SetPinPowerOn = ExtMem_SetPinPowerOn, .SetPinPowerOff = ExtMem_SetPinPowerOff, #endif #if AT45DB321D_RESET_MANAGEMENT .ResetPinAssert = ExtMem_ResetPinAssert, .ResetPinRelease = ExtMem_ResetPinRelease, #endif #if AT45DB321D_HW_WR_PROTECT .HwWrProtPinAssert = ExtMem_HwWrProtPinAssert, .HwWrProtPinRelease = ExtMem_HwWrProtPinRelease, #endif }; const ExtMem_Handle_t ExtMemHandle = { .Init = ExtMem_Init, .Write = ExtMem_Write, .Read = ExtMem_Read, .RangeCheck_Read = ExtMem_RangeCheck_Read, .RangeCheck_Write = ExtMem_RangeCheck_Write, .BankProtect = ExtMem_BankProtect, .CheckBankProtect = ExtMem_CheckBankProtect, .DeInit = ExtMem_DeInit, .pFlashProperties = &FlashProperties, .pBanksProperties = &BanksProperties }; // Memory Banking Template // Divides the whole chip memory into the common banks // This structure describes the sectors mask included into the bank typedef struct { struct flash_sectors_st selectRegister; } sMemoryBankingTemplate_t; // // Current Model Memory Template: Bank 0 (Factory) const static sMemoryBankingTemplate_t ACMMemory_Bank0 = { .selectRegister = { .loprotect = 0x0001FFFE, //.loprotect = 0x000001FE, // "First" 8MBit --- bug found 29.05.19, each bit represent only 512kbit, not 1Mbit. .hiprotect = 0x00000000, } }; // 32MBit flash AT45DB321D is installed; // But the application supposes that only first 8MBit is presents // So it adresses first 4MBit as a factory bank, and the second as // a user bank. The rest part is useless. // Current Model Memory Template: Bank 1 (User) const static sMemoryBankingTemplate_t ACMMemory_Bank1 = { .selectRegister = { .loprotect = 0xFFFE0000, // "Second" 8MBit //.loprotect = 0x0001FE00, // "Second" 8MBit --- bug found 29.05.19, each bit represent only 512kbit, not 1Mbit. .hiprotect = 0x00000001, //.hiprotect = 0x00000000, --- bug found 29.05.19, each bit represent only 512kbit, not 1Mbit. } }; #define pBankTemplate_Bank0 (&ACMMemory_Bank0) #define pBankTemplate_Bank1 (&ACMMemory_Bank1) #define pSPIHandle (&SPIHandle) // ExtMem_DeInit() // De-Initializes external memory driver. // Makes the chip fall asleep. // @bForce: if 'false' the function will detect if the chip // is busy and interrupt the operation with error. // If 'true', the function ignores current chip state and makes it // fall asleep instantly. // Returns 'true' in success case, and 'false' otherwise. bool ExtMem_DeInit( bool bForce ) { // Clear status bExtMemStatus = false; #if EXTMEM_HIBERNATE_ENABLED // Make flash chip fall asleep if( FLASH_ERROR( flash_finalize( true, bForce ) ) ) #else // Deinitialize driver if( FLASH_ERROR( flash_finalize( false, bForce ) ) ) #endif { return false; } // de-initialize SPI driver pSPIHandle->DeInit(); // De-Initialize power- and reset- management pins ExtMem_Pins_Deinit(); // set status and return return true; } // ExtMem_Init() // Initializes external memory driver. // Wakes the memory chip up if it is necessary. // Returns 'true' in success case, and 'false' otherwise. bool ExtMem_Init() { // Clear status bExtMemStatus = false; if( ExtMemHandle.pFlashProperties->minAddress >= ExtMemHandle.pFlashProperties->maxAddress ) return false; if( ExtMemHandle.pFlashProperties->maxSectors == 0 || ExtMemHandle.pFlashProperties->sectorSize == 0 ) return false; // Check the Bank sizes: // the banks can not share one sector, the sector can be owned by only one bank. if( ((ExtMemHandle.pBanksProperties->factoryBankSize % ExtMemHandle.pFlashProperties->sectorSize > 0 )?(ExtMemHandle.pFlashProperties->sectorSize):(0)) +((ExtMemHandle.pBanksProperties->userBankSize % ExtMemHandle.pFlashProperties->sectorSize > 0 )?(ExtMemHandle.pFlashProperties->sectorSize):(0)) > ExtMemHandle.pFlashProperties->maxSectors * ExtMemHandle.pFlashProperties->sectorSize ) { return false; } // Initialize power- and reset- management pins ExtMem_Pins_Init(); // initialize SPI driver pSPIHandle->Init(); // Initialize flash driver if( FLASH_ERROR( flash_initialize() ) ) { // de-initialize SPI driver pSPIHandle->DeInit(); // flash driver initialization error return false; } // set status and return return (bExtMemStatus = true); } // Initialize ExtMem power- and reset- management GPIO-pins static void ExtMem_Pins_Init() { // Configure GPIO pins for AT45 IC : PB10=nWP (reset signal) GPIO_InitTypeDef GPIO_InitStruct = {0}; // Configure as input to detect actual level: GPIO_InitStruct.Pin = CONFIG_PIN__EXTMEM__RST; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(CONFIG_PORT__EXTMEM__RST, &GPIO_InitStruct); // read actual level GPIO_PinState xPreservedState = HAL_GPIO_ReadPin( CONFIG_PORT__EXTMEM__RST, CONFIG_PIN__EXTMEM__RST ); // Configure as analog GPIO_InitStruct.Pin = CONFIG_PIN__EXTMEM__RST; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(CONFIG_PORT__EXTMEM__RST, &GPIO_InitStruct); // prepare pin state: HAL_GPIO_WritePin( CONFIG_PORT__EXTMEM__RST, CONFIG_PIN__EXTMEM__RST, xPreservedState ); // Configure as output: GPIO_InitStruct.Pin = CONFIG_PIN__EXTMEM__RST; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(CONFIG_PORT__EXTMEM__RST, &GPIO_InitStruct); } // De-Initialize ExtMem power- and reset- management GPIO-pins static void ExtMem_Pins_Deinit() { // Configure GPIO pins for AT45 IC : PB10=nWP (reset signal) GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = CONFIG_PIN__EXTMEM__RST; GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(CONFIG_PORT__EXTMEM__RST, &GPIO_InitStruct); } typedef struct { // Actual protection bits for all sectors: __FLASH_DWORD actual_protection_mask_lo; __FLASH_DWORD actual_protection_mask_hi; // Desired protection bits for selected sectors: __FLASH_DWORD desired_protection_mask_lo; __FLASH_DWORD desired_protection_mask_hi; bool globalProtectionStatus; bool individualProtectionStatus; } sProtectionEntry_t; // Queries actual protection information from the flash-memory and fills the [sProtectionEntry_t] structure. // If required, fills the protection register that can be used to set the protection information // @bankId = memory bank identifier; // @desiredProtectStatus = desired protecion state // @protEntry = pointer to the [sProtectionEntry_t] structure to fill, mandatory. // @setProtectRegister = pointer to the set-protection register to fill, optional, it then can be used by flash_protect_ex() // Returns the operaion status, true if succeded, false otherwise. // The actual protection state will be returned in @protEntry. static bool ExtMem_Internal_FillProtectionEntry( eExtMem_Bank_t bankId, bool desiredProtectStatus, sProtectionEntry_t * pProtEntry, flash_api_protect_t * setProtectRegister ) { const sMemoryBankingTemplate_t * pBankTemplate = NULL; flash_api_getprotect_t getProtectRegister; if( NULL != pProtEntry ) { // This function uses individual sector protecton // switch( bankId ) { case extmem_bank_factory: { pBankTemplate = pBankTemplate_Bank0; } break; case extmem_bank_user: { pBankTemplate = pBankTemplate_Bank1; } break; } if( NULL != pBankTemplate ) { // Retrieve currect sector protection register: if( FLASH_SUCCESS( flash_getprotect( &getProtectRegister ) ) ) { // Get actual protection bits for all sectors: pProtEntry->actual_protection_mask_lo = getProtectRegister.sectors.loprotect; pProtEntry->actual_protection_mask_hi = getProtectRegister.sectors.hiprotect; // Initialize desired protection bits for selected sectors: pProtEntry->desired_protection_mask_lo = ((desiredProtectStatus)?0xFFFFFFFF:0x00000000); pProtEntry->desired_protection_mask_hi = ((desiredProtectStatus)?0xFFFFFFFF:0x00000000); // Filter only selected sectors (specified by selection register on memory bank template) pProtEntry->actual_protection_mask_lo &= pBankTemplate->selectRegister.loprotect; pProtEntry->actual_protection_mask_hi &= pBankTemplate->selectRegister.hiprotect; pProtEntry->desired_protection_mask_lo &= pBankTemplate->selectRegister.loprotect; pProtEntry->desired_protection_mask_hi &= pBankTemplate->selectRegister.hiprotect; // Compare actual and desired sector protection status: if( pProtEntry->actual_protection_mask_lo != pProtEntry->desired_protection_mask_lo || pProtEntry->actual_protection_mask_hi != pProtEntry->desired_protection_mask_hi ) { // PROTECTION STATUS: SOME SECTORS HAVE INCORRECT PROTECTION FLAGS pProtEntry->individualProtectionStatus = false; if( NULL != setProtectRegister ) { // Do not change COMMON PROTECTION FLAG: setProtectRegister->protection_modify = flash_sector_protect_nomodify; // Mark selected sectors to be protected: setProtectRegister->protection_enabled = flash_sector_protect_pick; // Copy sectors selection mask: setProtectRegister->sectors.loprotect = pBankTemplate->selectRegister.loprotect; setProtectRegister->sectors.hiprotect = pBankTemplate->selectRegister.hiprotect; } } else { // PROTECTION STATUS: ALL SECTORS HAVE CORRECT PROTECTION FLAGS pProtEntry->individualProtectionStatus = true; } // QUERY GLOBAL PROTECTION FLAG switch( flash_getprotect( NULL ) ) { case FLERR_PROTECT_ENABLED: pProtEntry->globalProtectionStatus = true; return true; case FLERR_PROTECT_DISABLED: pProtEntry->globalProtectionStatus = false; return true; } } } } return false; } // ExtMem_BankProtect() // Implements external memory protection by Memory Bank Identifier // @bankId = memory bank identifier; // @desiredProtectStatus - protection status (true=protected, false=unprotected) // Returns the result of operation static bool ExtMem_BankProtect( eExtMem_Bank_t bankId, bool desiredProtectStatus ) { #warning 31/05/19 Была изменена эта функция, поверхностные тесты прошла. Требуется тщательная проверка #warning 02/07/19 error->warning, будем проверять sProtectionEntry_t protEntry_Bank0; // protection information for Bank0 sProtectionEntry_t protEntry_Bank1; // protection information for Bank1 // -------------------- if( extmem_bank_factory != bankId && extmem_bank_user != bankId ) { return false; // error } // -------------------- // Query actual protection information for both banks // Argument ExtMem_Internal_FillProtectionEntry(... @desiredProtectStatus = true ...): // that ensures that all sectors are protected with individual protection flags. if( ExtMem_Internal_FillProtectionEntry( extmem_bank_factory, true, &protEntry_Bank0, NULL ) && ExtMem_Internal_FillProtectionEntry( extmem_bank_user, true, &protEntry_Bank1, NULL ) ) { // Actual bank's protection state is depends on the global protection state and the individual protection state for bank bool stateBank0 = ( protEntry_Bank0.individualProtectionStatus && protEntry_Bank0.globalProtectionStatus ); bool stateBank1 = ( protEntry_Bank1.individualProtectionStatus && protEntry_Bank1.globalProtectionStatus ); // Check if current bank's protection state matchs to desired state if( (( extmem_bank_factory == bankId ) && ( desiredProtectStatus == stateBank0 )) || (( extmem_bank_user == bankId ) && ( desiredProtectStatus == stateBank1 )) ) { // All bank's protection statuses are correct, no operations are required return true; // success } else // Only if current state and the desired state mismatch: { // The global protection state must be set if at least one bank must be protected. bool requiredGlobalState = (( extmem_bank_factory == bankId )?desiredProtectStatus:stateBank0) || (( extmem_bank_user == bankId )?desiredProtectStatus:stateBank1); if( requiredGlobalState ) { // If global protection state will be set // Ensure that all banks have correct individual sector protection flags: // Remember: @requiredGlobalState is true if( protEntry_Bank0.individualProtectionStatus != (( extmem_bank_factory == bankId )?desiredProtectStatus:stateBank0) ) { // @individualProtectionStatus does not match to the desired state flash_api_protect_t setProtectRegister; // set-protection register // new bank protection status stateBank0 = !protEntry_Bank0.individualProtectionStatus; // Query actual protection information for both banks // Specify argument ExtMem_Internal_FillProtectionEntry(... @desiredProtectStatus ...) to // the inverted @individualProtectionStatus state, because current @individualProtectionStatus value is wrong if( !ExtMem_Internal_FillProtectionEntry( extmem_bank_factory, stateBank0, &protEntry_Bank0, &setProtectRegister ) ) { // query error return false; } // Execute operation: modify only individual sectors protection marks to the desired state if( FLASH_ERROR( flash_protect_ex( &setProtectRegister, (( stateBank0 )?(flash_sector_protected):(flash_sector_unprotected)) ) ) ) { // Error: can not modify individual sector protection flags return false; } } if( protEntry_Bank1.individualProtectionStatus != (( extmem_bank_user == bankId )?desiredProtectStatus:stateBank1) ) { // @individualProtectionStatus does not match to the desired state flash_api_protect_t setProtectRegister; // set-protection register // new bank protection status stateBank1 = !protEntry_Bank1.individualProtectionStatus; // Query actual protection information for both banks // Specify argument ExtMem_Internal_FillProtectionEntry(... @desiredProtectStatus ...) to // the inverted @individualProtectionStatus state, because current @individualProtectionStatus value is wrong if( !ExtMem_Internal_FillProtectionEntry( extmem_bank_user, stateBank1, &protEntry_Bank1, &setProtectRegister ) ) { // query error return false; } // Execute operation: modify only individual sectors protection marks to the desired state if( FLASH_ERROR( flash_protect_ex( &setProtectRegister, (( stateBank1 )?(flash_sector_protected):(flash_sector_unprotected)) ) ) ) { // Error: can not modify individual sector protection flags return false; } } // Modify global write-protection flag: protected return FLASH_SUCCESS( flash_protect( NULL ) ); } else { // If global protection state will be reset: all banks must be unprotected // Modify global write-protection flag: unprotected return FLASH_SUCCESS( flash_unprotect( NULL ) ); } } } return false; // error } // ExtMem_CheckBankProtect() // Implements external memory protection by Memory Bank Identifier // @bankId = memory bank identifier; // @pActualProtectStatus - pointer to the boolean variable to receive actual protection status // Returns the result of operation static bool ExtMem_CheckBankProtect( eExtMem_Bank_t bankId, bool * pActualProtectStatus ) { sProtectionEntry_t protEntry; if( NULL != pActualProtectStatus ) { // Query current protection information if( ExtMem_Internal_FillProtectionEntry( bankId, true, &protEntry, NULL ) ) { *pActualProtectStatus = (protEntry.globalProtectionStatus) && (protEntry.individualProtectionStatus); return true; // success, no operations are required } } return false; // error } /* // ExtMem_BankProtect() // Implements external memory protection by Memory Bank Identifier // @bankId = memory bank identifier; // @protectStatus - protection status (true=protected, false=unprotected) // Returns the result of operation static bool ExtMem_BankProtect0( eExtMem_Bank_t bankId, bool protectStatus ) { flash_api_getprotect_t getProtectRegister; flash_api_protect_t setProtectRegister; flash_api_protect_bits mode = (( protectStatus )?(flash_sector_protected):(flash_sector_unprotected)); const sMemoryBankingTemplate_t * pBankTemplate = NULL; // This function uses individual sector protecton // switch( bankId ) { case extmem_bank_factory: { pBankTemplate = pBankTemplate_Bank0; } break; case extmem_bank_user: { pBankTemplate = pBankTemplate_Bank1; } break; } if( NULL != pBankTemplate ) { // Retrieve currect sector protection register: if( FLASH_SUCCESS( flash_getprotect( &getProtectRegister ) ) ) { // Get actual protection bits for all sectors: __FLASH_DWORD actual_protection_mask_lo = getProtectRegister.sectors.loprotect; __FLASH_DWORD actual_protection_mask_hi = getProtectRegister.sectors.hiprotect; // Initialize desired protection bits for selected sectors: __FLASH_DWORD desired_protection_mask_lo = ((protectStatus)?0xFFFFFFFF:0x00000000); __FLASH_DWORD desired_protection_mask_hi = ((protectStatus)?0xFFFFFFFF:0x00000000); // Filter only selected sectors (specified by selection register on memory bank template) actual_protection_mask_lo &= pBankTemplate->selectRegister.loprotect; actual_protection_mask_hi &= pBankTemplate->selectRegister.hiprotect; desired_protection_mask_lo &= pBankTemplate->selectRegister.loprotect; desired_protection_mask_hi &= pBankTemplate->selectRegister.hiprotect; // Compare actual and desired sector protection status: if( actual_protection_mask_lo != desired_protection_mask_lo || actual_protection_mask_hi != desired_protection_mask_hi ) { // Do not change COMMON PROTECTION FLAG: setProtectRegister.protection_modify = flash_sector_protect_nomodify; // Mark selected sectors to be protected: setProtectRegister.protection_enabled = flash_sector_protect_pick; // Copy sectors selection mask: setProtectRegister.sectors.loprotect = pBankTemplate->selectRegister.loprotect; setProtectRegister.sectors.hiprotect = pBankTemplate->selectRegister.hiprotect; // Execute operation: modify only individual sectors protection marks to the desired state if( FLASH_SUCCESS( flash_protect_ex( &setProtectRegister, mode ) ) ) { // Modify global write-protection flag: protected (for only selected sectors) return FLASH_SUCCESS( flash_protect( NULL ) ); } } else { // Modify global write-protection flag: protected (for only selected sectors) return FLASH_SUCCESS( flash_protect( NULL ) ); } } } return false; } */ #if AT45DB321D_POWER_MANAGEMENT // Implement ExtMem power-management: set power enable pin static void ExtMem_SetPinPowerOn() { #error Not-implemented } // Implement ExtMem power-management: set power disable pin static void ExtMem_SetPinPowerOff() { #error Not-implemented } #endif #if AT45DB321D_RESET_MANAGEMENT // Implement ExtMem reset-management: set reset enable pin static void ExtMem_ResetPinAssert() { HAL_GPIO_WritePin( CONFIG_PORT__EXTMEM__RST, CONFIG_PIN__EXTMEM__RST, GPIO_PIN_RESET ); } // Implement ExtMem reset-management: set reset disable pin static void ExtMem_ResetPinRelease() { HAL_GPIO_WritePin( CONFIG_PORT__EXTMEM__RST, CONFIG_PIN__EXTMEM__RST, GPIO_PIN_SET ); } #endif #if AT45DB321D_HW_WR_PROTECT // Implement ExtMem hardware-write-protection: enable write protection static void ExtMem_HwWrProtPinAssert() { #error Not-implemented } // Implement ExtMem hardware-write-protection: disable write protection static void ExtMem_HwWrProtPinRelease() { #error Not-implemented } #endif #if CONFIG_EXTMEM_EMULATEWRITE static bool ExtMem_Write( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ) { return true; } #else static bool ExtMem_Write( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ) { extern volatile bool bExtMemStatus; return bExtMemStatus && FLASH_SUCCESS( flash_write( ExtMemHandle.pFlashProperties->minAddress + address, pBuffer, size, fwm_safewrite ) ); } #endif #if CONFIG_EXTMEM_EMULATEREAD static bool ExtMem_Read( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ) { memset( pBuffer, 0xDE, size ); return true; } #else static bool ExtMem_Read( flash_address_t address, __FLASH_BYTE * pBuffer, flash_address_t size ) { extern volatile bool bExtMemStatus; return bExtMemStatus && FLASH_SUCCESS( flash_read( ExtMemHandle.pFlashProperties->minAddress + address, pBuffer, size ) ); } #endif static bool ExtMem_RangeCheck_Read( flash_address_t address, flash_address_t size ) { if( size <= (ExtMemHandle.pFlashProperties->maxAddress - ExtMemHandle.pFlashProperties->minAddress) ) { if( address < ExtMemHandle.pFlashProperties->maxAddress - ExtMemHandle.pFlashProperties->minAddress - size ) { return true; } } return false; } static bool ExtMem_RangeCheck_Write( flash_address_t address, flash_address_t size ) { if( size <= (ExtMemHandle.pFlashProperties->maxAddress - ExtMemHandle.pFlashProperties->minAddress) ) { if( address < ExtMemHandle.pFlashProperties->maxAddress - ExtMemHandle.pFlashProperties->minAddress - size ) { return true; } } return false; }