static int transferToFlash(void* buffer, int size) { int controller = 0; int channel = 0; if((((uint32_t)buffer) & 0x3) != 0) { // the buffer needs to be aligned for DMA, last two bits have to be clear return ERROR_ALIGN; } SET_REG(NAND + FMCTRL0, GET_REG(NAND + FMCTRL0) | (1 << FMCTRL0_DMASETTINGSHIFT)); SET_REG(NAND + FMDNUM, size - 1); SET_REG(NAND + FMCTRL1, 0x7F4); CleanCPUDataCache(); dma_request(DMA_MEMORY, 4, 4, DMA_NAND, 4, 4, &controller, &channel, NULL); dma_perform((uint32_t)buffer, DMA_NAND, size, 0, &controller, &channel); if(dma_finish(controller, channel, 500) != 0) { bufferPrintf("nand: dma timed out\r\n"); return ERROR_TIMEOUT; } if(wait_for_transfer_done(500) != 0) { bufferPrintf("nand: waiting for transfer done timed out\r\n"); return ERROR_TIMEOUT; } SET_REG(NAND + FMCTRL1, FMCTRL1_FLUSHFIFOS); CleanAndInvalidateCPUDataCache(); return 0; }
static int wait_for_nand_bank_ready(int bank) { u32 toTest; u64 startTime; writel(((WEHighHoldTime & FMCTRL_TWH_MASK) << FMCTRL_TWH_SHIFT) | ((WPPulseTime & FMCTRL_TWP_MASK) << FMCTRL_TWP_SHIFT) | (1 << (banksTable[bank] + 1)) | FMCTRL0_ON | FMCTRL0_WPB, NAND + FMCTRL0); toTest = 1 << (bank + 4); if((readl(NAND + FMCSTAT) & toTest) != 0) { writel(toTest, NAND + FMCSTAT); } writel(FMCTRL1_FLUSHFIFOS, NAND + FMCTRL1); writel(NAND_CMD_READSTATUS, NAND + NAND_CMD); wait_for_ready(500); startTime = iphone_microtime(); while(true) { u32 data; writel(0, NAND + FMDNUM); writel(FMCTRL1_DOREADDATA, NAND + FMCTRL1); if(wait_for_transfer_done(500) != 0) { LOG("nand: wait_for_nand_bank_ready: wait for transfer done timed out\n"); return -ETIMEDOUT; } data = readl(NAND + FMFIFO); writel(FMCTRL1_FLUSHRXFIFO, NAND + FMCTRL1); if((data & (1 << 6)) == 0) { if(iphone_has_elapsed(startTime, 500 * 1000)) { LOG("nand: wait_for_nand_bank_ready: wait for bit 6 of DMA timed out\n"); return -ETIMEDOUT; } } else { break; } } writel(0, NAND + NAND_CMD); wait_for_ready(500); #ifdef FTL_PROFILE if(InWrite) Time_wait_for_nand_bank_ready += iphone_microtime() - startTime; #endif return 0; }
static int transferToFlash(void* buffer, int size) { int controller = 0; int channel = 0; dma_addr_t dma; #ifdef FTL_PROFILE u64 startTime; #endif if((((u32)buffer) & 0x3) != 0) { // the buffer needs to be aligned for DMA, last two bits have to be clear return -EINVAL; } writel(readl(NAND + FMCTRL0) | (1 << FMCTRL0_DMASETTINGSHIFT), NAND + FMCTRL0); writel(size - 1, NAND + FMDNUM); writel(0x7F4, NAND + FMCTRL1); dma = dma_map_single(nand_dev, buffer, size, DMA_TO_DEVICE); iphone_dma_request(IPHONE_DMA_MEMORY, 4, 4, IPHONE_DMA_NAND, 4, 4, &controller, &channel); iphone_dma_perform((u32)dma, IPHONE_DMA_NAND, size, 0, &controller, &channel); #ifdef FTL_PROFILE startTime = iphone_microtime(); #endif if(iphone_dma_finish(controller, channel, 500) != 0) { LOG("nand: dma timed out\n"); return -ETIMEDOUT; } #ifdef FTL_PROFILE if(InWrite) Time_iphone_dma_finish += iphone_microtime() - startTime; #endif if(wait_for_transfer_done(500) != 0) { LOG("nand: waiting for transfer done timed out\n"); return -ETIMEDOUT; } writel(FMCTRL1_FLUSHFIFOS, NAND + FMCTRL1); dma_unmap_single(nand_dev, dma, size, DMA_TO_DEVICE); return 0; }
int nand_read_status() { int status; SET_REG(NAND + NAND_REG_44, GET_REG(NAND + NAND_REG_44) & ~(1 << 4)); SET_REG(NAND + FMCTRL1, FMCTRL1_CLEARALL); SET_REG(NAND + NAND_CMD, NAND_CMD_READSTATUS); SET_REG(NAND + FMDNUM, 0); SET_REG(NAND + FMCTRL1, FMCTRL1_CLEARALL | FMCTRL1_DOREADDATA); wait_for_transfer_done(500); status = GET_REG(NAND + FMFIFO); SET_REG(NAND + FMCTRL1, FMCTRL1_CLEARALL); SET_REG(NAND + FMCSTAT, 1 << 3); SET_REG(NAND + NAND_REG_44, GET_REG(NAND + NAND_REG_44) | (1 << 2)); return status; }
int nand_read_status(void) { int status; writel(readl(NAND + NAND_REG_44) & ~(1 << 4), NAND + NAND_REG_44); writel(FMCTRL1_CLEARALL, NAND + FMCTRL1); writel(NAND_CMD_READSTATUS, NAND + NAND_CMD); writel(0, NAND + FMDNUM); writel(FMCTRL1_CLEARALL | FMCTRL1_DOREADDATA, NAND + FMCTRL1); wait_for_transfer_done(500); status = readl(NAND + FMFIFO); writel(FMCTRL1_CLEARALL, NAND + FMCTRL1); writel(1 << 3, NAND + FMCSTAT); writel(readl(NAND + NAND_REG_44) | (1 << 2), NAND + NAND_REG_44); return status; }
static int wait_for_nand_bank_ready(int bank) { SET_REG(NAND + FMCTRL0, ((WEHighHoldTime & FMCTRL_TWH_MASK) << FMCTRL_TWH_SHIFT) | ((WPPulseTime & FMCTRL_TWP_MASK) << FMCTRL_TWP_SHIFT) | (1 << (banksTable[bank] + 1)) | FMCTRL0_ON | FMCTRL0_WPB); uint32_t toTest = 1 << (bank + 4); if((GET_REG(NAND + FMCSTAT) & toTest) != 0) { SET_REG(NAND + FMCSTAT, toTest); } SET_REG(NAND + FMCTRL1, FMCTRL1_FLUSHFIFOS); SET_REG(NAND + NAND_CMD, NAND_CMD_READSTATUS); wait_for_ready(500); uint64_t startTime = timer_get_system_microtime(); while(TRUE) { SET_REG(NAND + FMDNUM, 0); SET_REG(NAND + FMCTRL1, FMCTRL1_DOREADDATA); if(wait_for_transfer_done(500) != 0) { bufferPrintf("nand: wait_for_nand_bank_ready: wait for transfer done timed out\r\n"); return ERROR_TIMEOUT; } uint32_t data = GET_REG(NAND + FMFIFO); SET_REG(NAND + FMCTRL1, FMCTRL1_FLUSHRXFIFO); if((data & (1 << 6)) == 0) { if(has_elapsed(startTime, 500 * 1000)) { bufferPrintf("nand: wait_for_nand_bank_ready: wait for bit 6 of DMA timed out\r\n"); return ERROR_TIMEOUT; } } else { break; } } SET_REG(NAND + NAND_CMD, 0); wait_for_ready(500); return 0; }
int nand_setup() { if(HasNANDInit) return 0; WEHighHoldTime = 7; WPPulseTime = 7; NANDSetting3 = 7; NANDSetting4 = 7; bufferPrintf("nand: Probing flash controller...\r\n"); clock_gate_switch(NAND_CLOCK_GATE1, ON); clock_gate_switch(NAND_CLOCK_GATE2, ON); int bank; for(bank = 0; bank < NAND_NUM_BANKS; bank++) { banksTable[bank] = bank; } NumValidBanks = 0; const NANDDeviceType* nandType = NULL; SET_REG(NAND + RSCTRL, 0); SET_REG(NAND + RSCTRL, GET_REG(NAND + RSCTRL) | (ECCType << 4)); for(bank = 0; bank < NAND_NUM_BANKS; bank++) { nand_bank_reset(bank, 100); SET_REG(NAND + FMCTRL1, FMCTRL1_FLUSHFIFOS); SET_REG(NAND + FMCTRL0, ((WEHighHoldTime & FMCTRL_TWH_MASK) << FMCTRL_TWH_SHIFT) | ((WPPulseTime & FMCTRL_TWP_MASK) << FMCTRL_TWP_SHIFT) | (1 << (banksTable[bank] + 1)) | FMCTRL0_ON | FMCTRL0_WPB); SET_REG(NAND + NAND_CMD, NAND_CMD_ID); wait_for_ready(500); SET_REG(NAND + FMANUM, 0); SET_REG(NAND + FMADDR0, 0); SET_REG(NAND + FMCTRL1, FMCTRL1_DOTRANSADDR); wait_for_address_done(500); wait_for_command_done(bank, 100); SET_REG(NAND + FMDNUM, 8); SET_REG(NAND + FMCTRL1, FMCTRL1_DOREADDATA); wait_for_transfer_done(500); uint32_t id = GET_REG(NAND + FMFIFO); const NANDDeviceType* candidate = SupportedDevices; while(candidate->id != 0) { if(candidate->id == id) { if(nandType == NULL) { nandType = candidate; } else if(nandType != candidate) { bufferPrintf("nand: Mismatched device IDs (0x%08x after 0x%08x)\r\n", id, nandType->id); return ERROR_ARG; } banksTable[NumValidBanks++] = bank; } candidate++; } SET_REG(NAND + FMCTRL1, FMCTRL1_FLUSHFIFOS); } if(nandType == NULL) { bufferPrintf("nand: No supported NAND found\r\n"); return ERROR_ARG; } Geometry.DeviceID = nandType->id; Geometry.banksTable = banksTable; WPPulseTime = (((clock_get_frequency(FrequencyBaseBus) * (nandType->WPPulseTime + 1)) + 99999999)/100000000) - 1; WEHighHoldTime = (((clock_get_frequency(FrequencyBaseBus) * (nandType->WEHighHoldTime + 1)) + 99999999)/100000000) - 1; NANDSetting3 = (((clock_get_frequency(FrequencyBaseBus) * (nandType->NANDSetting3 + 1)) + 99999999)/100000000) - 1; NANDSetting4 = (((clock_get_frequency(FrequencyBaseBus) * (nandType->NANDSetting4 + 1)) + 99999999)/100000000) - 1; if(WPPulseTime > 7) WPPulseTime = 7; if(WEHighHoldTime > 7) WEHighHoldTime = 7; if(NANDSetting3 > 7) NANDSetting3 = 7; if(NANDSetting4 > 7) NANDSetting4 = 7; Geometry.blocksPerBank = nandType->blocksPerBank; Geometry.banksTotal = NumValidBanks; Geometry.sectorsPerPage = nandType->sectorsPerPage; Geometry.userSuBlksTotal = nandType->userSuBlksTotal; Geometry.bytesPerSpare = nandType->bytesPerSpare; Geometry.field_2E = 4; Geometry.field_2F = 3; Geometry.pagesPerBlock = nandType->pagesPerBlock; if(Geometry.sectorsPerPage > 4) { LargePages = TRUE; } else { LargePages = FALSE; } if(nandType->ecc1 == 6) { ECCType = 4; TotalECCDataSize = Geometry.sectorsPerPage * 15; } else if(nandType->ecc1 == 8) { ECCType = 8; TotalECCDataSize = Geometry.sectorsPerPage * 20; } else if(nandType->ecc1 == 4) { ECCType = 0; TotalECCDataSize = Geometry.sectorsPerPage * 10; } if(nandType->ecc2 == 6) { ECCType2 = 4; } else if(nandType->ecc2 == 8) { ECCType2 = 8; } else if(nandType->ecc2 == 4) { ECCType2 = 0; } Geometry.field_4 = 5; Geometry.bytesPerPage = SECTOR_SIZE * Geometry.sectorsPerPage; Geometry.pagesPerBank = Geometry.pagesPerBlock * Geometry.blocksPerBank; Geometry.pagesTotal = Geometry.pagesPerBank * Geometry.banksTotal; Geometry.pagesPerSuBlk = Geometry.pagesPerBlock * Geometry.banksTotal; Geometry.userPagesTotal = Geometry.userSuBlksTotal * Geometry.pagesPerSuBlk; Geometry.suBlksTotal = (Geometry.banksTotal * Geometry.blocksPerBank) / Geometry.banksTotal; FTLData.field_2 = Geometry.suBlksTotal - Geometry.userSuBlksTotal - 28; FTLData.sysSuBlks = FTLData.field_2 + 4; FTLData.field_4 = FTLData.field_2 + 5; FTLData.field_6 = 3; FTLData.field_8 = 23; if(FTLData.field_8 == 0) Geometry.field_22 = 0; int bits = 0; int i = FTLData.field_8; while((i <<= 1) != 0) { bits++; } Geometry.field_22 = bits; bufferPrintf("nand: DEVICE: %08x\r\n", Geometry.DeviceID); bufferPrintf("nand: BANKS_TOTAL: %d\r\n", Geometry.banksTotal); bufferPrintf("nand: BLOCKS_PER_BANK: %d\r\n", Geometry.blocksPerBank); bufferPrintf("nand: SUBLKS_TOTAL: %d\r\n", Geometry.suBlksTotal); bufferPrintf("nand: USER_SUBLKS_TOTAL: %d\r\n", Geometry.userSuBlksTotal); bufferPrintf("nand: PAGES_PER_SUBLK: %d\r\n", Geometry.pagesPerSuBlk); bufferPrintf("nand: PAGES_PER_BANK: %d\r\n", Geometry.pagesPerBank); bufferPrintf("nand: SECTORS_PER_PAGE: %d\r\n", Geometry.sectorsPerPage); bufferPrintf("nand: BYTES_PER_SPARE: %d\r\n", Geometry.bytesPerSpare); bufferPrintf("nand: BYTES_PER_PAGE: %d\r\n", Geometry.bytesPerPage); bufferPrintf("nand: PAGES_PER_BLOCK: %d\r\n", Geometry.pagesPerBlock); aTemporaryReadEccBuf = (uint8_t*) malloc(Geometry.bytesPerPage); memset(aTemporaryReadEccBuf, 0xFF, SECTOR_SIZE); aTemporarySBuf = (uint8_t*) malloc(Geometry.bytesPerSpare); HasNANDInit = TRUE; return 0; }