diff options
Diffstat (limited to 'thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Projects/Composite_Example/src/nand_if.c')
-rw-r--r-- | thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Projects/Composite_Example/src/nand_if.c | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Projects/Composite_Example/src/nand_if.c b/thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Projects/Composite_Example/src/nand_if.c new file mode 100644 index 0000000..7139219 --- /dev/null +++ b/thirdparty/STM32_USB-FS-Device_Lib_V4.0.0/Projects/Composite_Example/src/nand_if.c @@ -0,0 +1,557 @@ +/** + ****************************************************************************** + * @file nand_if.c + * @author MCD Application Team + * @version V4.0.0 + * @date 21-January-2013 + * @brief manage NAND operations state machine + ****************************************************************************** + * @attention + * + * <h2><center>© COPYRIGHT 2013 STMicroelectronics</center></h2> + * + * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.st.com/software_license_agreement_liberty_v2 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ****************************************************************************** + */ + +#include "platform_config.h" + +#ifdef USE_STM3210E_EVAL +/* Includes ------------------------------------------------------------------*/ +#include "nand_if.h" +#include "mass_mal.h" +#include "fsmc_nand.h" +#include "memory.h" +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* extern variables-----------------------------------------------------------*/ +extern uint32_t SCSI_LBA; +extern uint32_t SCSI_BlkLen; +/* Private variables ---------------------------------------------------------*/ +uint16_t LUT[1024]; //Look Up Table Buffer +WRITE_STATE Write_State; +BLOCK_STATE Block_State; +NAND_ADDRESS wAddress, fAddress; +uint16_t phBlock, LogAddress, Initial_Page, CurrentZone = 0; +uint16_t Written_Pages = 0; + +uint16_t LUT[1024]; //Look Up Table Buffer +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ +static uint16_t NAND_CleanLUT(uint8_t ZoneNbr); +static NAND_ADDRESS NAND_GetAddress(uint32_t Address); +static uint16_t NAND_GetFreeBlock(void); +static uint16_t NAND_Write_Cleanup(void); +SPARE_AREA ReadSpareArea(uint32_t address); +static uint16_t NAND_Copy(NAND_ADDRESS Address_Src, NAND_ADDRESS Address_Dest, uint16_t PageToCopy); +static NAND_ADDRESS NAND_ConvertPhyAddress(uint32_t Address); +static uint16_t NAND_BuildLUT(uint8_t ZoneNbr); + +/******************************************************************************* +* Function Name : NAND_Init +* Description : Init NAND Interface +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +uint16_t NAND_Init(void) +{ + uint16_t Status = NAND_OK; + + FSMC_NAND_Init(); + Status = NAND_BuildLUT(0); + Write_State = WRITE_IDLE; + return Status; +} + +/******************************************************************************* +* Function Name : NAND_Write +* Description : write one sector by once +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +uint16_t NAND_Write(uint32_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length) +{ + /* check block status and calculate start and end addresses */ + wAddress = NAND_GetAddress(Memory_Offset / 512); + + /*check Zone: if second zone is requested build second LUT*/ + if (wAddress.Zone != CurrentZone) + { + CurrentZone = wAddress.Zone; + NAND_BuildLUT(CurrentZone); + } + + phBlock = LUT[wAddress.Block]; /* Block Index + flags */ + LogAddress = wAddress.Block ; /* save logical block */ + + /* IDLE state */ + /****************/ + if ( Write_State == WRITE_IDLE) + {/* Idle state */ + + if (phBlock & USED_BLOCK) + { /* USED BLOCK */ + + Block_State = OLD_BLOCK; + /* Get a free Block for swap */ + fAddress.Block = NAND_GetFreeBlock(); + fAddress.Zone = wAddress.Zone; + Initial_Page = fAddress.Page = wAddress.Page; + + /* write the new page */ + FSMC_NAND_WriteSmallPage((uint8_t *)Writebuff, fAddress, PAGE_TO_WRITE); + Written_Pages++; + + /* get physical block */ + wAddress.Block = phBlock & 0x3FF; + + + if (Written_Pages == SCSI_BlkLen) + { + NAND_Write_Cleanup(); + Written_Pages = 0; + return NAND_OK; + } + else + { + if (wAddress.Page == (NAND_BLOCK_SIZE - 1)) + { + NAND_Write_Cleanup(); + return NAND_OK; + } + Write_State = WRITE_ONGOING; + return NAND_OK; + } + } + else + {/* UNUSED BLOCK */ + + Block_State = UNUSED_BLOCK; + /* write the new page */ + wAddress.Block = phBlock & 0x3FF; + FSMC_NAND_WriteSmallPage( (uint8_t *)Writebuff , wAddress, PAGE_TO_WRITE); + + Written_Pages++; + if (Written_Pages == SCSI_BlkLen) + { + Written_Pages = 0; + NAND_Write_Cleanup(); + return NAND_OK; + } + else + { + Write_State = WRITE_ONGOING; + return NAND_OK; + } + } + } + /* WRITE state */ + /***************/ + if ( Write_State == WRITE_ONGOING) + {/* Idle state */ + if (phBlock & USED_BLOCK) + { /* USED BLOCK */ + + wAddress.Block = phBlock & 0x3FF; + Block_State = OLD_BLOCK; + fAddress.Page = wAddress.Page; + + /* check if next pages are in next block */ + if (wAddress.Page == (NAND_BLOCK_SIZE - 1)) + { + /* write Last page */ + FSMC_NAND_WriteSmallPage( (uint8_t *)Writebuff , fAddress, PAGE_TO_WRITE); + Written_Pages++; + if (Written_Pages == SCSI_BlkLen) + { + Written_Pages = 0; + } + /* Clean up and Update the LUT */ + NAND_Write_Cleanup(); + Write_State = WRITE_IDLE; + return NAND_OK; + } + + /* write next page */ + FSMC_NAND_WriteSmallPage( (uint8_t *)Writebuff , fAddress, PAGE_TO_WRITE); + Written_Pages++; + if (Written_Pages == SCSI_BlkLen) + { + Write_State = WRITE_IDLE; + NAND_Write_Cleanup(); + Written_Pages = 0; + } + + } + else + {/* UNUSED BLOCK */ + wAddress.Block = phBlock & 0x3FF; + /* check if it is the last page in prev block */ + if (wAddress.Page == (NAND_BLOCK_SIZE - 1)) + { + /* write Last page */ + FSMC_NAND_WriteSmallPage( (uint8_t *)Writebuff , wAddress, PAGE_TO_WRITE); + Written_Pages++; + if (Written_Pages == SCSI_BlkLen) + { + Written_Pages = 0; + } + + /* Clean up and Update the LUT */ + NAND_Write_Cleanup(); + Write_State = WRITE_IDLE; + + + + return NAND_OK; + } + /* write next page in same block */ + FSMC_NAND_WriteSmallPage( (uint8_t *)Writebuff , wAddress, PAGE_TO_WRITE); + Written_Pages++; + if (Written_Pages == SCSI_BlkLen) + { + Write_State = WRITE_IDLE; + NAND_Write_Cleanup(); + Written_Pages = 0; + } + } + } + return NAND_OK; +} + +/******************************************************************************* +* Function Name : NAND_Read +* Description : Read sectors +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +uint16_t NAND_Read(uint32_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length) +{ + NAND_ADDRESS phAddress; + + phAddress = NAND_GetAddress(Memory_Offset / 512); + + if (phAddress.Zone != CurrentZone) + { + CurrentZone = phAddress.Zone; + NAND_BuildLUT(CurrentZone); + } + + if (LUT [phAddress.Block] & BAD_BLOCK) + { + return NAND_FAIL; + } + else + { + phAddress.Block = LUT [phAddress.Block] & ~ (USED_BLOCK | VALID_BLOCK); + FSMC_NAND_ReadSmallPage ( (uint8_t *)Readbuff , phAddress, Transfer_Length / 512); + } + return NAND_OK; +} + +/******************************************************************************* +* Function Name : NAND_CleanLUT +* Description : Erase old blocks & rebuild the look up table +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +static uint16_t NAND_CleanLUT (uint8_t ZoneNbr) +{ +#ifdef WEAR_LEVELLING_SUPPORT + uint16_t BlockIdx, LUT_Item; +#endif + /* Rebuild the LUT for the current zone */ + NAND_BuildLUT (ZoneNbr); + +#ifdef WEAR_LEVELLING_SUPPORT + /* Wear Leveling : circular use of free blocks */ + LUT_Item = LUT [BlockIdx] + for (BlockIdx == MAX_LOG_BLOCKS_PER_ZONE ; BlockIdx < MAX_LOG_BLOCKS_PER_ZONE + WEAR_DEPTH ; BlockIdx++) + { + LUT [BlockIdx] = LUT [BlockIdx + 1]; + } + LUT [ MAX_LOG_BLOCKS_PER_ZONE + WEAR_DEPTH - 1] = LUT_Item ; +#endif + + return NAND_OK; +} + +/******************************************************************************* +* Function Name : NAND_GetAddress +* Description : Translate logical address into a phy one +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +static NAND_ADDRESS NAND_GetAddress (uint32_t Address) +{ + NAND_ADDRESS Address_t; + + Address_t.Page = Address & (NAND_BLOCK_SIZE - 1); + Address_t.Block = Address / NAND_BLOCK_SIZE; + Address_t.Zone = 0; + + while (Address_t.Block >= MAX_LOG_BLOCKS_PER_ZONE) + { + Address_t.Block -= MAX_LOG_BLOCKS_PER_ZONE; + Address_t.Zone++; + } + return Address_t; +} + +/******************************************************************************* +* Function Name : NAND_GetFreeBlock +* Description : Look for a free block for data exchange +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +static uint16_t NAND_GetFreeBlock (void) +{ + return LUT[MAX_LOG_BLOCKS_PER_ZONE]& ~(USED_BLOCK | VALID_BLOCK); +} + +/******************************************************************************* +* Function Name : ReadSpareArea +* Description : Check used block +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +SPARE_AREA ReadSpareArea (uint32_t address) +{ + SPARE_AREA t; + uint8_t Buffer[16]; + NAND_ADDRESS address_s; + address_s = NAND_ConvertPhyAddress(address); + FSMC_NAND_ReadSpareArea(Buffer , address_s, 1) ; + + t = *(SPARE_AREA *)Buffer; + + return t; +} + +/******************************************************************************* +* Function Name : NAND_Copy +* Description : Copy page +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +static uint16_t NAND_Copy (NAND_ADDRESS Address_Src, NAND_ADDRESS Address_Dest, uint16_t PageToCopy) +{ + uint8_t Copybuff[512]; + for ( ; PageToCopy > 0 ; PageToCopy-- ) + { + FSMC_NAND_ReadSmallPage ((uint8_t *)Copybuff, Address_Src , 1 ); + FSMC_NAND_WriteSmallPage ((uint8_t *)Copybuff, Address_Dest, 1); + FSMC_NAND_AddressIncrement(&Address_Src); + FSMC_NAND_AddressIncrement(&Address_Dest); + } + + return NAND_OK; +} + +/******************************************************************************* +* Function Name : NAND_Format +* Description : Format the entire NAND flash +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +uint16_t NAND_Format (void) +{ + NAND_ADDRESS phAddress; + SPARE_AREA SpareArea; + uint32_t BlockIndex; + + for (BlockIndex = 0 ; BlockIndex < NAND_ZONE_SIZE * NAND_MAX_ZONE; BlockIndex++) + { + phAddress = NAND_ConvertPhyAddress(BlockIndex * NAND_BLOCK_SIZE ); + SpareArea = ReadSpareArea(BlockIndex * NAND_BLOCK_SIZE); + + if((SpareArea.DataStatus != 0)||(SpareArea.BlockStatus != 0)){ + FSMC_NAND_EraseBlock (phAddress); + } + } + NAND_BuildLUT(0); + return NAND_OK; +} + +/******************************************************************************* +* Function Name : NAND_Write_Cleanup +* Description : None +* Input : None +* Output : None +* Return : Status +*******************************************************************************/ +static uint16_t NAND_Write_Cleanup (void) +{ + uint16_t tempSpareArea [8]; + uint16_t Page_Back; + + if ( Block_State == OLD_BLOCK ) + { + /* precopy old first pages */ + if (Initial_Page != 0) + { + Page_Back = wAddress.Page; + fAddress.Page = wAddress.Page = 0; + NAND_Copy (wAddress, fAddress, Initial_Page); + wAddress.Page = Page_Back ; + } + + /* postcopy remaining pages */ + if ((NAND_BLOCK_SIZE - (wAddress.Page + 1)) != 0) + { + FSMC_NAND_AddressIncrement(&wAddress); + fAddress.Page = wAddress.Page; + NAND_Copy (wAddress, fAddress, NAND_BLOCK_SIZE - wAddress.Page); + } + + /* assign logical address to new block */ + tempSpareArea [0] = LogAddress | USED_BLOCK ; + tempSpareArea [1] = 0xFFFF; + tempSpareArea [2] = 0xFFFF; + + fAddress.Page = 0x00; + FSMC_NAND_WriteSpareArea( (uint8_t *)tempSpareArea , fAddress , 1); + + /* erase old block */ + FSMC_NAND_EraseBlock(wAddress); + NAND_CleanLUT(wAddress.Zone); + } + else + {/* unused block case */ + /* assign logical address to the new used block */ + tempSpareArea [0] = LogAddress | USED_BLOCK ; + tempSpareArea [1] = 0xFFFF; + tempSpareArea [2] = 0xFFFF; + + wAddress.Page = 0x00; + FSMC_NAND_WriteSpareArea((uint8_t *)tempSpareArea , wAddress, 1); + NAND_CleanLUT(wAddress.Zone); + } + return NAND_OK; +} + +/******************************************************************************* +* Function Name : NAND_ConvertPhyAddress +* Description : None +* Input : physical Address +* Output : None +* Return : Status +*******************************************************************************/ +static NAND_ADDRESS NAND_ConvertPhyAddress (uint32_t Address) +{ + NAND_ADDRESS Address_t; + + Address_t.Page = Address & (NAND_BLOCK_SIZE - 1); + Address_t.Block = Address / NAND_BLOCK_SIZE; + Address_t.Zone = 0; + + while (Address_t.Block >= MAX_PHY_BLOCKS_PER_ZONE) + { + Address_t.Block -= MAX_PHY_BLOCKS_PER_ZONE; + Address_t.Zone++; + } + return Address_t; +} + +/******************************************************************************* +* Function Name : NAND_BuildLUT +* Description : Build the look up table +* Input : None +* Output : None +* Return : Status +* !!!! NOTE : THIS ALGORITHM IS A SUBJECT OF PATENT FOR STMICROELECTRONICS !!!!! +*******************************************************************************/ +static uint16_t NAND_BuildLUT (uint8_t ZoneNbr) +{ + + uint16_t pBadBlock, pCurrentBlock, pFreeBlock; + SPARE_AREA SpareArea; + /***************************************************************************** + 1st step : Init. + *****************************************************************************/ + /*Init the LUT (assume all blocks free) */ + for (pCurrentBlock = 0 ; pCurrentBlock < MAX_PHY_BLOCKS_PER_ZONE ; pCurrentBlock++) + { + LUT[pCurrentBlock] = FREE_BLOCK; /* 12th bit is set to 1 */ + } + + /* Init Pointers */ + pBadBlock = MAX_PHY_BLOCKS_PER_ZONE - 1; + pCurrentBlock = 0; + + /***************************************************************************** + 2nd step : locate used and bad blocks + *****************************************************************************/ + + while (pCurrentBlock < MAX_PHY_BLOCKS_PER_ZONE) + { + + SpareArea = ReadSpareArea(pCurrentBlock * NAND_BLOCK_SIZE + (ZoneNbr * NAND_BLOCK_SIZE * MAX_PHY_BLOCKS_PER_ZONE)); + + if ((SpareArea.DataStatus == 0) || (SpareArea.BlockStatus == 0)) + { + + LUT[pBadBlock--] |= pCurrentBlock | (uint16_t)BAD_BLOCK ; + LUT[pCurrentBlock] &= (uint16_t)~FREE_BLOCK; + if (pBadBlock == MAX_LOG_BLOCKS_PER_ZONE) + { + return NAND_FAIL; + } + } + else if (SpareArea.LogicalIndex != 0xFFFF) + { + + LUT[SpareArea.LogicalIndex & 0x3FF] |= pCurrentBlock | VALID_BLOCK | USED_BLOCK; + LUT[pCurrentBlock] &= (uint16_t)( ~FREE_BLOCK); + } + pCurrentBlock++ ; + } + + /***************************************************************************** + 3rd step : locate Free Blocks by scanning the LUT already built partially + *****************************************************************************/ + pFreeBlock = 0; + for (pCurrentBlock = 0 ; pCurrentBlock < MAX_PHY_BLOCKS_PER_ZONE ; pCurrentBlock++ ) + { + + if ( !(LUT[pCurrentBlock]& USED_BLOCK)) + { + do + { + if (LUT[pFreeBlock] & FREE_BLOCK) + { + + LUT [pCurrentBlock] |= pFreeBlock; + LUT [pFreeBlock] &= ~FREE_BLOCK; + break; + } + pFreeBlock++; + } + while ( pFreeBlock < MAX_PHY_BLOCKS_PER_ZONE ); + } + } + return NAND_OK; +} +#endif + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |