static BOOL32 check_format_mark(void) { // This function reads a flash page from (bank #0, block #0) in order to check whether the SSD is formatted or not. #ifdef __GNUC__ extern UINT32 size_of_firmware_image; UINT32 firmware_image_pages = (((UINT32) (&size_of_firmware_image)) + BYTES_PER_FW_PAGE - 1) / BYTES_PER_FW_PAGE; #else extern UINT32 Image$$ER_CODE$$RO$$Length; extern UINT32 Image$$ER_RW$$RW$$Length; UINT32 firmware_image_bytes = ((UINT32) &Image$$ER_CODE$$RO$$Length) + ((UINT32) &Image$$ER_RW$$RW$$Length); UINT32 firmware_image_pages = (firmware_image_bytes + BYTES_PER_FW_PAGE - 1) / BYTES_PER_FW_PAGE; #endif UINT32 format_mark_page_offset = FW_PAGE_OFFSET + firmware_image_pages; UINT32 temp; flash_clear_irq(); // clear any flash interrupt flags that might have been set SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_BANK, REAL_BANK(0)); SETREG(FCP_OPTION, FO_E); SETREG(FCP_DMA_ADDR, FTL_BUF_ADDR); // flash -> DRAM SETREG(FCP_DMA_CNT, BYTES_PER_SECTOR); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(0), format_mark_page_offset); SETREG(FCP_ROW_H(0), format_mark_page_offset); // At this point, we do not have to check Waiting Room status before issuing a command, // because scan list loading has been completed just before this function is called. SETREG(FCP_ISSUE, NULL); // wait for the FC_COL_ROW_READ_OUT command to be accepted by bank #0 while ((GETREG(WR_STAT) & 0x00000001) != 0); // wait until bank #0 finishes the read operation while (BSP_FSM(0) != BANK_IDLE); // Now that the read operation is complete, we can check interrupt flags. temp = BSP_INTR(0) & FIRQ_ALL_FF; // clear interrupt flags CLR_BSP_INTR(0, 0xFF); if (temp != 0) { return FALSE; // the page contains all-0xFF (the format mark does not exist.) } else { return TRUE; // the page contains something other than 0xFF (it must be the format mark) } }
void ftl_isr(void) { // interrupt service routine for flash interrupts UINT32 bank; UINT32 bsp_intr_flag; for (bank = 0; bank < NUM_BANKS; bank++) { while (BSP_FSM(bank) != BANK_IDLE); bsp_intr_flag = BSP_INTR(bank); if (bsp_intr_flag == 0) { continue; } UINT32 fc = GETREG(BSP_CMD(bank)); CLR_BSP_INTR(bank, bsp_intr_flag); if (bsp_intr_flag & FIRQ_DATA_CORRUPT) { g_read_fail_count++; } if (bsp_intr_flag & (FIRQ_BADBLK_H | FIRQ_BADBLK_L)) { if (fc == FC_COL_ROW_IN_PROG || fc == FC_IN_PROG || fc == FC_PROG) { g_program_fail_count++; } else { ASSERT(fc == FC_ERASE); g_erase_fail_count++; } } } // clear the flash interrupt flag at the interrupt controller SETREG(APB_INT_STS, INTR_FLASH); }
// BSP interrupt service routine void ftl_isr(void) { UINT32 bank; UINT32 bsp_intr_flag; uart_print("BSP interrupt occured..."); // interrupt pending clear (ICU) SETREG(APB_INT_STS, INTR_FLASH); for (bank = 0; bank < NUM_BANKS; bank++) { while (BSP_FSM(bank) != BANK_IDLE); // get interrupt flag from BSP bsp_intr_flag = BSP_INTR(bank); if (bsp_intr_flag == 0) { continue; } UINT32 fc = GETREG(BSP_CMD(bank)); // BSP clear CLR_BSP_INTR(bank, bsp_intr_flag); // interrupt handling if (bsp_intr_flag & FIRQ_DATA_CORRUPT) { uart_printf("BSP interrupt at bank: 0x%x", bank); uart_print("FIRQ_DATA_CORRUPT occured..."); } if (bsp_intr_flag & (FIRQ_BADBLK_H | FIRQ_BADBLK_L)) { uart_printf("BSP interrupt at bank: 0x%x", bank); if (fc == FC_COL_ROW_IN_PROG || fc == FC_IN_PROG || fc == FC_PROG) { uart_print("find runtime bad block when block program..."); } else { uart_printf("find runtime bad block when block erase...vblock #: %d", GETREG(BSP_ROW_H(bank)) / PAGES_PER_BLK); ASSERT(fc == FC_ERASE); } } } }
void loading_misc_meta() { /*int i; flash_finish(); disable_irq(); flash_clear_irq(); for(i = 0 ;i < NUM_BANKS;i++){ SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_DMA_CNT, sizeof(misc_metadata)); SETREG(FCP_COL, 0); SETREG(FCP_DMA_ADDR, FTL_BUF_ADDR); //SETREG(FCP_DMA_ADDR, &(g_misc_meta[i])); SETREG(FCP_OPTION, FO_P | FO_E ); SETREG(FCP_ROW_L(i), PAGES_PER_VBLK); SETREG(FCP_ROW_H(i), PAGES_PER_VBLK); flash_issue_cmd(i, RETURN_ON_ISSUE); flash_finish(); CLR_BSP_INTR(i,0xff); mem_copy(&(g_misc_meta[i]),FTL_BUF_ADDR,sizeof(misc_metadata)); } enable_irq();*/ UINT32 load_flag = 0; UINT32 bank, page_num; UINT32 load_cnt = 0; flash_finish(); disable_irq(); flash_clear_irq(); // clear any flash interrupt flags that might have been set // scan valid metadata in descending order from last page offset for (page_num = PAGES_PER_VBLK - 1; page_num != ((UINT32) -1); page_num--) { for (bank = 0; bank < NUM_BANKS; bank++) { if (load_flag & (0x1 << bank)) { continue; } // read valid metadata from misc. metadata area nand_page_ptread(bank, 1, page_num, 0, ((sizeof(misc_metadata) + BYTES_PER_SECTOR -1 ) / BYTES_PER_SECTOR), FTL_BUF_ADDR, RETURN_ON_ISSUE); flash_finish(); mem_copy(&g_misc_meta[bank], FTL_BUF_ADDR, sizeof(misc_metadata)); } for (bank = 0; bank < NUM_BANKS; bank++) { if (!(load_flag & (0x1 << bank)) && !(BSP_INTR(bank) & FIRQ_ALL_FF)) { load_flag = load_flag | (0x1 << bank); load_cnt++; } CLR_BSP_INTR(bank, 0xFF); } } ASSERT(load_cnt == NUM_BANKS); enable_irq(); }
// misc + VCOUNT static void load_misc_metadata(void) { UINT32 misc_meta_bytes = NUM_MISC_META_SECT * BYTES_PER_SECTOR; UINT32 vcount_bytes = NUM_VCOUNT_SECT * BYTES_PER_SECTOR; UINT32 vcount_addr = VCOUNT_ADDR; UINT32 vcount_boundary = VCOUNT_ADDR + VCOUNT_BYTES; UINT32 load_flag = 0; UINT32 bank, page_num; UINT32 load_cnt = 0; flash_finish(); disable_irq(); flash_clear_irq(); // clear any flash interrupt flags that might have been set // scan valid metadata in descending order from last page offset for (page_num = PAGES_PER_BLK - 1; page_num != ((UINT32) -1); page_num--) { for (bank = 0; bank < NUM_BANKS; bank++) { if (load_flag & (0x1 << bank)) { continue; } // read valid metadata from misc. metadata area nand_page_ptread(bank, MISCBLK_VBN, page_num, 0, NUM_MISC_META_SECT + NUM_VCOUNT_SECT, FTL_BUF(bank), RETURN_ON_ISSUE); } flash_finish(); for (bank = 0; bank < NUM_BANKS; bank++) { if (!(load_flag & (0x1 << bank)) && !(BSP_INTR(bank) & FIRQ_ALL_FF)) { load_flag = load_flag | (0x1 << bank); load_cnt++; } CLR_BSP_INTR(bank, 0xFF); } } ASSERT(load_cnt == NUM_BANKS); for (bank = 0; bank < NUM_BANKS; bank++) { // misc. metadata mem_copy(&g_misc_meta[bank], FTL_BUF(bank), sizeof(misc_metadata)); // vcount metadata if (vcount_addr <= vcount_boundary) { mem_copy(vcount_addr, FTL_BUF(bank) + misc_meta_bytes, vcount_bytes); vcount_addr += vcount_bytes; } } enable_irq(); }
void test_nand_blocks(void) { // This function is a utility that writes random data to flash pages and verifies them. // This function takes a long time to complete. UINT32 bank, vblk_offset, page_offset, data, bad; #define write_buffer_base DRAM_BASE #define read_buffer_base (DRAM_BASE + BYTES_PER_VBLK) disable_irq(); flash_clear_irq(); mem_set_sram(g_test_result, 0, sizeof(g_test_result)); // Configure the flash controller so that any FIRQ_* does not lead to pause state. SETREG(FCONF_PAUSE, 0); // STEP 1 - prepare random data srand(10); for (page_offset = 0; page_offset < PAGES_PER_VBLK; page_offset++) { data = (rand() & 0xFFFF) | (rand() << 16); mem_set_dram(write_buffer_base + page_offset * BYTES_PER_PAGE, data, BYTES_PER_PAGE); } for (vblk_offset = 1; vblk_offset < VBLKS_PER_BANK; vblk_offset++) { // STEP 2 - erase a block at each bank for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); SETREG(FCP_CMD, FC_ERASE); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } // STEP 3 - write to every pages of the erased block for (page_offset = 0; page_offset < PAGES_PER_VBLK; page_offset++) { for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); SETREG(FCP_CMD, FC_COL_ROW_IN_PROG); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P | FO_E | FO_B_W_DRDY); SETREG(FCP_DMA_ADDR, write_buffer_base + page_offset * BYTES_PER_PAGE); SETREG(FCP_DMA_CNT, BYTES_PER_PAGE); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK + page_offset); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK + page_offset); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } } // STEP 4 - check the FC_ERASE and FC_COL_ROW_IN_PROG results. bad = 0; while (GETREG(MON_CHABANKIDLE) != 0); for (bank = 0; bank < NUM_BANKS; bank++) { if (BSP_INTR(bank) & (FIRQ_BADBLK_H | FIRQ_BADBLK_L)) { bad |= (1 << bank); CLR_BSP_INTR(bank, 0xFF); g_test_result[bank].erase_prog_fail++; } } // STEP 5 - read and verify // We check ECC/CRC results for verification. for (page_offset = 0; page_offset < PAGES_PER_VBLK; page_offset++) { for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); if (bad & (1 << bank)) continue; SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P | FO_E); SETREG(FCP_DMA_ADDR, read_buffer_base + bank * BYTES_PER_PAGE); SETREG(FCP_DMA_CNT, BYTES_PER_PAGE); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK + page_offset); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK + page_offset); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } } // STEP 6 - check the FC_COL_ROW_READ_OUT results while (GETREG(MON_CHABANKIDLE) != 0); for (bank = 0; bank < NUM_BANKS; bank++) { if (BSP_INTR(bank) & FIRQ_DATA_CORRUPT) { bad |= (1 << bank); CLR_BSP_INTR(bank, 0xFF); g_test_result[bank].read_fail++; } } // STEP 7 - erase the blocks, but not the bad ones for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); if (bad & (1 << bank)) continue; SETREG(FCP_CMD, FC_ERASE); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } } // Now that bad blocks contain non-0xFF data, it is a good time to use install.exe to scan bad blocks. }