From a447e861fb365609bdc2c121e7df030385621f76 Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@b871894f-2f95-9b40-918c-086798483c85> Date: Mon, 2 Feb 2009 11:01:50 +0000 Subject: [PATCH] =?UTF-8?q?NAND=E3=83=89=E3=83=A9=E3=82=A4=E3=83=90?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=80=81=20NAND=E3=83=87=E3=83=90=E3=82=A4?= =?UTF-8?q?=E3=82=B9=E3=83=AC=E3=83=99=E3=83=AB=E3=83=95=E3=82=A9=E3=83=BC?= =?UTF-8?q?=E3=83=9E=E3=83=83=E3=82=BF=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-09-30%20-%20paladin.7z/paladin/ctr_firmware@245 b871894f-2f95-9b40-918c-086798483c85 --- .../build/libraries/nand/ARM11/Makefile | 51 + .../build/libraries/nand/ARM11/src/ne1/crc.c | 47 + .../build/libraries/nand/ARM11/src/ne1/crc.h | 26 + .../build/libraries/nand/ARM11/src/ne1/nand.c | 1611 +++++++++++++++++ .../build/libraries/nand/ARM11/src/ne1/nand.h | 121 ++ .../libraries/nand/ARM11/src/ne1/nandif.c | 398 ++++ .../libraries/nand/ARM11/src/ne1/nandif_ip.h | 86 + .../libraries/nand/ARM11/src/ne1/nandif_reg.h | 125 ++ .../build/libraries/nand/ARM9/Makefile | 55 + trunk/bootrom/build/libraries/nand/Makefile | 34 + .../tools/bootrom/ne1tb/nand_deviceformat.axf | Bin 0 -> 211004 bytes trunk/tools/bootrom/ne1tb/readme.txt | 3 + 12 files changed, 2557 insertions(+) create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/Makefile create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.c create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.h create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.c create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.h create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif.c create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_ip.h create mode 100644 trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_reg.h create mode 100644 trunk/bootrom/build/libraries/nand/ARM9/Makefile create mode 100644 trunk/bootrom/build/libraries/nand/Makefile create mode 100644 trunk/tools/bootrom/ne1tb/nand_deviceformat.axf diff --git a/trunk/bootrom/build/libraries/nand/ARM11/Makefile b/trunk/bootrom/build/libraries/nand/ARM11/Makefile new file mode 100644 index 0000000..3c5a491 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/Makefile @@ -0,0 +1,51 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: CtrBrom - libraries - mi +# File: Makefile +# +# Copyright 2008-2009 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: $ +# $Rev$ +# $Author$ +#---------------------------------------------------------------------------- + +SUBDIRS = +SUBMAKES = + + +#---------------------------------------------------------------------------- + +# build ARM & THUMB libraries +BROM_CODEGEN_ALL ?= TRUE + +#(NE1)------------------------------------------- +ifeq ($(BROM_PLATFORM),NE1EMU) +SRCDIR = . ./src/ne1 ../common/ne1 +SRCS = \ + nandif.c \ + nand.c \ + crc.c +endif +#------------------------------------------------ + +TARGET_LIB = libnand$(BROM_LIBSUFFIX).a + +include $(CTRBROM_ROOT)/build/buildtools/commondefs + +INSTALL_TARGETS = $(TARGETS) +INSTALL_DIR = $(BROM_INSTALL_LIBDIR) + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + +include $(CTRBROM_ROOT)/build/buildtools/modulerules + +#===== End of Makefile ===== diff --git a/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.c b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.c new file mode 100644 index 0000000..1805a51 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.c @@ -0,0 +1,47 @@ + +#include +#include "crc.h" + + +/*---------------------------------------------------------------------------* + global変数 + *---------------------------------------------------------------------------*/ +static const u16 cr16_table[16] = { + 0x0000, 0xCC01, 0xD801, 0x1400, + 0xF001, 0x3C00, 0x2800, 0xE401, + 0xA001, 0x6C00, 0x7800, 0xB401, + 0x5000, 0x9C01, 0x8801, 0x4400, +}; + + +/*---------------------------------------------------------------------------* + Name: crcCalc + + Description: CRC(CRC-ANSI, 別名CRC16)を算出する + + Arguments: data : + len : + + Returns: + *---------------------------------------------------------------------------*/ +u16 crcCalc( vu8* data, vu16 len) +{ + vu16 r1, total; + + total = 0; + while( len-- > 0) { + /*下位4bit*/ + r1 = cr16_table[ total & 0xf]; + total = (total >> 4) & 0x0FFF; + total = total ^ r1 ^ cr16_table[ *data & 0xf]; + + /*上位4bit*/ + r1 = cr16_table[ total & 0xf]; + total = (total >> 4) & 0x0fff; + total = total ^ r1 ^ cr16_table[ (*data >> 4) & 0xf]; + + /*次のバイトへ*/ + data++; + } + return total; +} diff --git a/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.h b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.h new file mode 100644 index 0000000..17e1596 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/crc.h @@ -0,0 +1,26 @@ + +#ifndef __CRC_H__ +#define __CRC_H__ + + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + + +/*---------------------------------------------------------------------------* + API + *---------------------------------------------------------------------------*/ +u16 crcCalc( vu8* data, vu16 len); + + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*__CRC_H__*/ diff --git a/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.c b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.c new file mode 100644 index 0000000..e0e1b13 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.c @@ -0,0 +1,1611 @@ +/*---------------------------------------------------------------------------* + Project: CTR - NAND driver + File: nandif.c + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#include +#include +#include "nand.h" + +#include "nandif_ip.h" +#include "nandif_reg.h" + +//#define PRINTDEBUG osTPrintf +//#define PRINTLEVEL2 osTPrintf +#define PRINTDEBUG( ...) ((void)0) +#define PRINTLEVEL2( ...) ((void)0) + + +/*********************************************************************** + +***********************************************************************/ +#define NAND_STACK_SIZE (8192) +#define NAND_THREAD_PRIO (10) + +//NAND_OPERATION_INITはいらない(Initでタスクを立てるため) +#define NAND_OPERATION_ENABLE (0) +#define NAND_OPERATION_FORMAT (1) +#define NAND_OPERATION_READ (2) +#define NAND_OPERATION_WRITE (3) +#define NAND_OPERATION_FLUSH (4) + +#define NAND_FLUSH_RETRY_COUNT (8) + +/*i_nandFlushPageCache関数の返り値*/ +#define I_NAND_ERR_SUCCESS (0) //成功 +#define I_NAND_ERR_VERIFY_ERR (-1) //マージで助かる +#define I_NAND_ERR_FATAL_ERR (-2) //不正番号 +#define I_NAND_ERR_IMPOSSIBLE_ERR (-3) //freeブロックが見つからない + +/**/ +#define I_NAND_FLUSH_FALSE (-1) +//#define I_NAND_FLUSH_TRUE (1) +#define I_NAND_FLUSH_CASHED (-2) + +extern u32 nand_ecc[4]; + + +/*---------------------------------------------------------------------------* + API(上位層タスク) - nandタスク間通信用構造体 + *---------------------------------------------------------------------------*/ +typedef struct { + NandPageCacheFormat* cache; + void* buf; + u32 sector; + u32 sector_count; + u32 operation; +// ID tskid; /*nandAPIを呼んだタスクのID*/ +} NandMsg; + + +/*---------------------------------------------------------------------------* + 論理ブロック-物理ブロック 変換テーブル + *---------------------------------------------------------------------------*/ +u16 nandBlkTbl[1024]; /*physical_blk = nandBlkTbl[logical_blk]*/ + +//i_nandWritePage,i_nandEraseBlockの返り値でBadBlockを認識するには、 +//スペア領域以外の場所でBadBlockを管理する必要がある +//NandSysFormat nandSys[1024]; + + +/*---------------------------------------------------------------------------* + 物理ブロックステータスのリンクリスト + *---------------------------------------------------------------------------*/ +NandBlockStatInfo NandBlockStat[1024]; +NandStatInfo NandStat; + + +/*---------------------------------------------------------------------------* + globalだがアプリケーションには非公開の関数 + *---------------------------------------------------------------------------*/ +void i_nandEnable( NandPageCacheFormat* NandPageCache); +void i_nandFormat( void); +void i_nandReset( void); +void i_nandReadSector( NandPageCacheFormat* NandPageCache, u32* dest, u32 logical_sector); +void i_nandWriteSector( NandPageCacheFormat* NandPageCache, u32* src, u32 logical_sector); +BOOL i_nandFlush( NandPageCacheFormat* NandPageCache); + +int i_nandFlushPageCache( NandPageCacheFormat* NandPageCache); +void i_nandMarkingBadBlock( u16 physical_blk, NandSpareFormat* NandSpareOrg); + + +/*---------------------------------------------------------------------------* + static関数 + *---------------------------------------------------------------------------*/ +static u8 i_nandCheckBlock( u16 physical_blk, u16* logical_adr, u32* erase_count); +static BOOL i_nandCheckFollowCluster( u16 physical_blk, u16* logical_adr, u32* erase_count); +static u8 i_nandCountBitDifferent( u8 dat1, u8 dat2); + +void i_nandInsertStatList( NandBlockStatInfo** NandBlockStatList, NandBlockStatInfo* NandBlockStat); +void i_nandRemoveStatList( NandBlockStatInfo** NandBlockStatList, NandBlockStatInfo* NandBlockStat); +static BOOL i_nandCheckECC( u32* data, u32* stored_ecc, u32 new_ecc); + +void nandFillPageCache( NandPageCacheFormat* NandPageCache, u32 physical_page); +static void nandReadPageCache( NandPageCacheFormat* NandPageCache, u32* dest, u16 cache_sect); +static void nandWritePageCache( NandPageCacheFormat* NandPageCache, u32* src, u16 cache_sect); + +BOOL i_nandMergeBlock( NandPageCacheFormat* NandPageCache); + +static void i_nandThread( void* arg); + + +/*---------------------------------------------------------------------------* + static変数 + *---------------------------------------------------------------------------*/ +static BOOL nand_tsk_created = FALSE; + +/*---------------------------------------------------------------------------* + global変数 + *---------------------------------------------------------------------------*/ +OSThread nand_tsk; +OSMessageQueue nand_dtq; +OSMessage nand_dtq_array[1]; +OSMessageQueue nand_result_dtq; +OSMessage nand_result_dtq_array[1]; + +u64 nand_stack[NAND_STACK_SIZE / sizeof(u64)]; + +NandPageCacheFormat* g_nandPageCache; + + +//u64 nand_stack[NAND_STACK_SIZE / sizeof(u64)]; + + + + + +/*---------------------------------------------------------------------------* + Name: STD_CompareString + + Description: compare strings. same to strcmp + + Arguments: str1, str2 : strings + + Returns: 0 if same + *---------------------------------------------------------------------------*/ +int stdCompareString(const char *str1, const char *str2); +int stdCompareString(const char *str1, const char *str2) +{ + while (*str1 == *str2 && *str1) + { + str1++; + str2++; + } + return (*str1 - *str2); +} + + +/*---------------------------------------------------------------------------* + Name: nandInit + + Description: + + Arguments: + + Returns: + *---------------------------------------------------------------------------*/ +void nandInit( void) +{ + NandMsg nandMsg; + u32 init_msg; + + if( nand_tsk_created == FALSE) { + /*---------- OS準備 ----------*/ + osInitMessageQueue( &nand_dtq, &nand_dtq_array[0], 1); + osInitMessageQueue( &nand_result_dtq, &nand_result_dtq_array[0], 1); + + osCreateThread( &nand_tsk, i_nandThread, NULL, (nand_stack+NAND_STACK_SIZE / sizeof(u64)), + NAND_STACK_SIZE, NAND_THREAD_PRIO); + + osWakeupThreadDirect( &nand_tsk); + /*----------------------------*/ + nand_tsk_created = TRUE; + } + + i_nandReset(); +} + + +/*---------------------------------------------------------------------------* + Name: i_nandEnable + + Description: ブロックテーブルを作成する + + Arguments: + + Returns: + *---------------------------------------------------------------------------*/ +void i_nandEnable( NandPageCacheFormat* NandPageCache) +{ + u16 i; + u16 physical_blk; + u16 logical_blk; + NandPageFormat NandPage; + u16 ctrdg_reg; + u8 block_stat; + u32 erase_count; + u16 free_blk_count = 0; + u16 busy_blk_count = 0; + u16 bad_blk_count = 0; + + miCpuFill8( nandBlkTbl, 0xFF, 2048); //全て未割り当て状態に + NandStat.busy = NULL; + NandStat.free = NULL; + + osTPrintf( "nand block checking\n"); + for( i=0; i<1024; i++) { + if( (i % 16) == 0) { + osTPrintf( "."); + } + block_stat = i_nandCheckBlock( i, &logical_blk, &erase_count); + physical_blk = i; + + /*テーブルの構築*/ + if( (logical_blk < 1024)&&(block_stat == 0xFF)) {//未割り当て(0xFFFF)をはじく + nandBlkTbl[logical_blk] = physical_blk; + if( logical_blk == 0) { + PRINTDEBUG( "nandEnable : physical:0x%x, logical:0x%x\n", physical_blk, logical_blk); + } + } + + /*ステータスのリンクリスト構築*/ + NandBlockStat[physical_blk].physical_blk = physical_blk; + NandBlockStat[physical_blk].block_stat_bad = block_stat; + NandBlockStat[physical_blk].erase_count = erase_count; + NandBlockStat[physical_blk].next = NULL; + if( (logical_blk < 1024)&&(block_stat == 0xFF)) { + i_nandInsertStatList( &(NandStat.busy), &(NandBlockStat[physical_blk])); + PRINTLEVEL2( "busy [physical_blk:0x%x, erase_count:0x%x]\n", physical_blk, NandBlockStat[physical_blk].erase_count); + busy_blk_count++; + }else{ + if( block_stat == 0xFF) { + i_nandInsertStatList( &(NandStat.free), &(NandBlockStat[physical_blk])); + PRINTLEVEL2( " free [physical_blk:0x%x, erase_count:0x%x]\n", physical_blk, NandBlockStat[physical_blk].erase_count); + free_blk_count++; + }else{ + bad_blk_count++; + PRINTLEVEL2( "%s : bad block found ... blk 0x%x(physical)\n", __FUNCTION__, physical_blk); + } + } + } + osTPrintf( "\n"); + PRINTLEVEL2( "%s : NandStat(LinkList) busy:0x%x, free:0x%x, and bad:0x%x\n", + __FUNCTION__, busy_blk_count, free_blk_count, bad_blk_count); + + /**/ + for( i=0; i<64; i++) { + NandPageCache->page_flushed[i] = I_NAND_FLUSH_FALSE; + } + NandPageCache->page_flushed_count = 0; + + + NandPageCache->valid = FALSE; + + NandPageCache->OnTheFlush = 0xFFFF; + NandPageCache->BlockDirty = FALSE; + + /*global変数に登録*/ + g_nandPageCache = NandPageCache; +} + +/*リンクリストにNandBlockStatを挿入する(消去回数順)*/ +void i_nandInsertStatList( NandBlockStatInfo** NandBlockStatList, NandBlockStatInfo* NandBlockStat) +{ + NandBlockStatInfo NandBlockStatDmy; + NandBlockStatInfo* CurrentNandBlockStat; + + /*前処理*/ + NandBlockStat->next = NULL; + + if( *NandBlockStatList == NULL) { + *NandBlockStatList = NandBlockStat; + return; + }else{ + NandBlockStatDmy.next = *NandBlockStatList; + CurrentNandBlockStat = &NandBlockStatDmy; + while( CurrentNandBlockStat->next != NULL) { + if( NandBlockStat->erase_count < CurrentNandBlockStat->next->erase_count) { + break; + } + CurrentNandBlockStat = CurrentNandBlockStat->next; + } + NandBlockStat->next = CurrentNandBlockStat->next; + CurrentNandBlockStat->next = NandBlockStat; + } + + /*先頭に挿入した場合は、リンクリストの開始位置自体を変える*/ + if( CurrentNandBlockStat == &NandBlockStatDmy) { + *NandBlockStatList = CurrentNandBlockStat->next; + } +} + +/*リンクリストからNandBlockStatを抜き出す*/ +void i_nandRemoveStatList( NandBlockStatInfo** NandBlockStatList, NandBlockStatInfo* NandBlockStat) +{ + NandBlockStatInfo NandBlockStatDmy; + NandBlockStatInfo* CurrentNandBlockStat; + + if( *NandBlockStatList == NULL) { + return; + } + NandBlockStatDmy.next = *NandBlockStatList; + CurrentNandBlockStat = &NandBlockStatDmy; + while( CurrentNandBlockStat->next != NandBlockStat) { + if( CurrentNandBlockStat->next == NULL) { + return; + }else{ + CurrentNandBlockStat = CurrentNandBlockStat->next; + } + } + CurrentNandBlockStat->next = CurrentNandBlockStat->next->next; + *NandBlockStatList = NandBlockStatDmy.next; +} + +/*---------------------------------------------------------------------------* + Name: i_nandFormat + + Description: フォーマット(スペア領域を構築)する + + Arguments: + + Returns: + *---------------------------------------------------------------------------*/ +void i_nandFormat( void) +{ + u16 i, j; + u32 physical_page_addr; + NandPageFormat NandPage; /*書き込み用作業場所*/ + u16 good_blk_count; + u8 block_stat; + u16 logical_blk; + u32 erase_count; + + miCpuFill8( &(NandPage.data.data[0][0]), 0xFF, NAND_PAGE_DATASIZE); + miCpuFill8( &(NandPage.spare), 0xFF, NAND_PAGE_SPARESIZE); + miCpuCopy8( "CTR\0", &(NandPage.spare.sig0[0]), 4); + miCpuCopy8( "CTR\0", &(NandPage.spare.sig1[0]), 4); + + good_blk_count = 0; + osTPrintf( "formatting"); + for( i=0; i<1024; i++) { + if( (i % 16) == 0) { + osTPrintf( "."); + } + /*バッドブロックかどうかチェックする*/ + block_stat = i_nandCheckBlock( i, &logical_blk, &erase_count); + if( block_stat == 0xFF) { + NandPage.spare.block_stat_bad = block_stat; + NandPage.spare.block_erase_count0 = erase_count; + NandPage.spare.block_erase_count1 = erase_count; +// if( good_blk_count > NAND_GUARANTEED_BLOCKS) { + NandPage.spare.logical_adr0 = 0xFFFF; //未割り当て + NandPage.spare.logical_adr1 = 0xFFFF; +// }else{ +// NandPage.spare.logical_adr0 = good_blk_count; +// NandPage.spare.logical_adr1 = good_blk_count; +// } + + if( i_nandEraseBlock( i) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandMarkingBadBlock( i, &(NandPage.spare)); + }else{ + good_blk_count++; + for( j=0; j<64; j++) { + physical_page_addr = ((u32)(i)<<6) + j; + //TODO:i_nandWritePageのエラーコード見る + if( i_nandWritePage( &NandPage, physical_page_addr, 1) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandMarkingBadBlock( i, &(NandPage.spare)); + good_blk_count--; + break; + } + } + } + } + } + + osTPrintf( "\n"); + osTPrintf( "%s : good_blk_count = 0x%x\n", __FUNCTION__, good_blk_count); +} + + +/*---------------------------------------------------------------------------* + Name: i_nandCheckBlock + + Description: ブロックをチェックする + + Arguments: physical_blk : チェックしたい物理ブロック番号 + + Returns: 返り値が0xFF(good block)のとき、logical_adrおよび + erase_count値が有効。返り値が0x00(bad block)のときは + どちらも無効値となります。 + *---------------------------------------------------------------------------*/ +u8 i_nandCheckBlock( u16 physical_blk, u16* logical_adr, u32* erase_count) +{ + u32 physical_page_addr; + NandPageFormat NandDevPage1; /*リードしたページ0のデータ*/ + NandSpareFormat NandDevSpare0; /*リードしたページ1のスペア領域*/ + u8 new_block_stat; + u32 new_erase_count = 0; + + /**/ + physical_page_addr = (u32)((u32)(physical_blk) << 6); + i_nandReadPage( &NandDevPage1, physical_page_addr, 1); + miCpuCopy8( &(NandDevPage1.spare), &NandDevSpare0, NAND_PAGE_SPARESIZE); + i_nandReadPage( &NandDevPage1, (physical_page_addr+1), 1); + + /*----- BBフラグが0xFFのとき -----*/ + if( (NandDevSpare0.block_stat_bad == 0xFF) && + (NandDevPage1.spare.block_stat_bad == 0xFF)) { + + new_block_stat = 0xFF; + if( (stdCompareString( NandDevSpare0.sig0, "CTR\0") == 0) || + (stdCompareString( NandDevSpare0.sig1, "CTR\0") == 0)) { + new_erase_count = NandDevSpare0.block_erase_count0; //todo:parity check + }else{/*--- pre slip write済みでpost slip writeされてないときに対応 ---*/ + if( i_nandCheckFollowCluster( physical_blk, logical_adr, + erase_count) == TRUE) { + return( new_block_stat); + } + } + }else{ + /*----- BBフラグに1bitだけ0があるとき -----*/ + if( (i_nandCountBitDifferent( NandDevSpare0.block_stat_bad, 0xFF) <= 1) && + (i_nandCountBitDifferent( NandDevPage1.spare.block_stat_bad, 0xFF) <= 1)) { + if( (stdCompareString( NandDevSpare0.sig0, "CTR\0") == 0) || + (stdCompareString( NandDevSpare0.sig1, "CTR\0") == 0)) { + new_block_stat = 0xFF; + new_erase_count = NandDevSpare0.block_erase_count0; //todo:parity check + }else{/*--- pre slip write済みでpost slip writeされてないときに対応 ---*/ + if( i_nandCheckFollowCluster( physical_blk, logical_adr, + erase_count) == TRUE) { + new_block_stat = 0xFF; + return( new_block_stat); + }else{ + //signatureがなければバッドブロック判定 + new_block_stat = 0x00; + } + } + }else{/*----- BBフラグに2bit以上0があるとき -----*/ + new_block_stat = 0x00; + } + } + + *logical_adr = NandDevSpare0.logical_adr0; //todo:parity check + *erase_count = new_erase_count; + return( new_block_stat); +} + +/*---------------------------------------------------------------------------* + Name: i_nandCheckFollowCluster + + Description: 指定ブロック内におけるクラスタ1〜7のsignatureをチェックする + + Arguments: physical_blk : チェックしたい物理ブロック番号 + + Returns: 返り値が0xFF(good block)のとき、logical_adrおよび + erase_count値が有効。返り値が0x00(bad block)のときは + どちらも無効値となります。 + *---------------------------------------------------------------------------*/ +static BOOL i_nandCheckFollowCluster( u16 physical_blk, u16* logical_adr, u32* erase_count) +{ + u16 i; + u32 physical_page_addr; + NandPageFormat NandDevPage; + + for( i=1; i<8; i++) { + physical_page_addr = (u32)(((u32)(physical_blk) << 6) + (i * 8)); + i_nandReadPage( &NandDevPage, physical_page_addr, 1); + + if( (stdCompareString( NandDevPage.spare.sig0, "CTR\0") == 0) || + (stdCompareString( NandDevPage.spare.sig1, "CTR\0") == 0)) { + *logical_adr = NandDevPage.spare.logical_adr0; //todo:parity check + *erase_count = NandDevPage.spare.block_erase_count0; //todo:parity check + return( TRUE); + } + } + + return( FALSE); +} + +/* 8bitデータを比較して、異なるビットの数を返す */ +static u8 i_nandCountBitDifferent( u8 dat1, u8 dat2) +{ + u8 dif_count; + u8 i; + + dif_count = 0; + for( i=0; i<8; i++) { + if( ((dat1>>i)&0x01) != ((dat2>>i)&0x01)) { + dif_count++; + } + } + + return( dif_count); +} + +/*---------------------------------------------------------------------------* + Name: i_nandReadSector + + Description: 論理セクタを読み出す + + Arguments: logical_sector : 論理ブロック番号 + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +void i_nandReadSector( NandPageCacheFormat* NandPageCache, u32* dest, u32 logical_sector) +{ + u16 logical_blk; + u16 physical_blk; + u32 logical_page; + u32 physical_page; + u16 cache_sect; /*PageCache上のセクタ番号*/ + u16 cache_offset; + u32* cache_sect_adr; + int result; + NandBlockStatInfo* AssignNandBlockStat; + + logical_blk = (u16)(logical_sector / 256); //1blk = 64*4 sectors + logical_page = (u32)(logical_sector / 4); + cache_sect = (u16)(logical_sector & 0x3); //0〜3 + + /*----- マージ制御 -----*/ + if( (NandPageCache->valid == TRUE)&&(NandPageCache->dirty == TRUE)) { + result = i_nandFlushPageCache( NandPageCache); + if( (result == I_NAND_ERR_VERIFY_ERR) || + (result == I_NAND_ERR_SUCCESS)) { + i_nandMergeBlock( NandPageCache); + }else{ + PRINTDEBUG( "%s(%d) : fatal error!\n", __FUNCTION__, __LINE__); + while( 1) {}; + } + }else{ + if( NandPageCache->BlockDirty == TRUE) { + i_nandMergeBlock( NandPageCache); + }else{ + if( NandPageCache->page_flushed[(logical_page & 0x3F)] != I_NAND_FLUSH_FALSE) { + PRINTDEBUG( "%s(%d) : fatal error!\n", __FUNCTION__, __LINE__); + while( 1) {}; + } + } + } + /*----------------------*/ + + /*マージでテーブルが更新されているかも知れないのでマージの後で計算*/ + physical_blk = nandBlkTbl[logical_blk]; + physical_page = (u32)(((u32)(physical_blk) << 6) + (logical_page & 0x3F)); + + /*----- 物理アドレスが未割り当てのとき -----*/ + //miCpuFill8( dest, 0xFF, 512);だけでも問題ないかも + if( physical_blk == 0xFFFF) { + if( (NandStat.free)&& //フリーブロックがあれば割り当て + (logical_blk < NAND_GUARANTEED_BLOCKS)) { + AssignNandBlockStat = NandStat.free; + nandBlkTbl[logical_blk] = NandStat.free->physical_blk; + + physical_blk = nandBlkTbl[logical_blk]; + physical_page = (u32)(((u32)(physical_blk) << 6) + (logical_page & 0x3F)); + + i_nandRemoveStatList( &(NandStat.free), AssignNandBlockStat); + i_nandInsertStatList( &(NandStat.busy), AssignNandBlockStat); + }else{ + return; + } + }/*-----------------------------------------*/ + + /*PageCacheが無効の場合*/ + if( NandPageCache->valid == FALSE) { + nandFillPageCache( NandPageCache, physical_page); + NandPageCache->logical_page = logical_page; //PageCacheの論理ページ番号更新 + }else{ + /*PageCacheが有効かつヒットしたとき*/ + if( (NandPageCache->valid == TRUE) && + (NandPageCache->logical_page == logical_page)) { + PRINTDEBUG( "%s : PageCache Hit! (physical:0x%x, logical:0x%x)\n", __FUNCTION__, physical_page, logical_page); + }else{ + /*PageCacheが有効だがヒットしないとき*/ + if( NandPageCache->dirty == TRUE) { + result = i_nandFlushPageCache( NandPageCache); //今までのぶんをFlush + if( (result == I_NAND_ERR_SUCCESS)||(result == I_NAND_ERR_VERIFY_ERR)) { + i_nandMergeBlock( NandPageCache); //マージ + /*マージでテーブルが更新されているかも知れないのでマージの後で計算*/ + physical_blk = nandBlkTbl[logical_blk]; + physical_page = (u32)(((u32)(physical_blk) << 6) + (logical_page & 0x3F)); + }else{ + while( 1) {}; + } + } + nandFillPageCache( NandPageCache, physical_page); + NandPageCache->logical_page = logical_page; //PageCacheの論理ページ番号更新 + } + } + + /*ECC比較 & 訂正*/ + cache_offset = cache_sect;//(u16)(cache_sect % 4); + cache_sect_adr = (u32*)((u32)&(NandPageCache->NandPage[0].data.data[cache_offset][0])); + + if( i_nandCheckECC( cache_sect_adr, + &(NandPageCache->NandPage[0].spare.ecc[cache_offset]), + NandPageCache->ecc[0][cache_offset]) == FALSE) { + PRINTLEVEL2( "ECC error : bad block generated. %s, %d\n", __FUNCTION__, __LINE__); + /*エラー発生時(訂正できたかどうかに関わらず)*/ + NandPageCache->NandSpareWriteBack.block_stat_bad = 0; /*バッドブロック・マーキング*/ + NandBlockStat[physical_blk].block_stat_bad = 0; + NandPageCache->dirty = TRUE; /*dirtyにしておかないとバッドブロック・マーキングが書き戻されない*/ + } + + /*訂正後のPageCacheデータを読み出し*/ + nandReadPageCache( NandPageCache, dest, cache_sect); + + return; +} + +/*---------------------------------------------------------------------------* + Name: i_nandCheckECC + + Description: ECCを用いてデータの誤りを訂正する + + Arguments: data : 512バイトデータのアドレス + stored_ecc : + new_ecc : + + Returns: TRUE : good ( No Error) + FALSE : bad ( ECC correction or Uncorrectable Error) + *---------------------------------------------------------------------------*/ +static BOOL i_nandCheckECC( u32* data, u32* stored_ecc, u32 new_ecc) +{ +#if 0 + return TRUE; +#else + u32 xored_ecc; + u16 i; + u16 bit_count; + u16 bit_adr; + u16 word_adr; + + /*一致しないビットの数をカウント*/ + xored_ecc = (*stored_ecc) ^ new_ecc; + if( xored_ecc == 0) { + PRINTDEBUG( "%s : no err\n", __FUNCTION__); + return TRUE; /*エラーなし*/ + } + bit_count = 0; + for( i=0; i<24; i++) { + if( xored_ecc & ( (u32)(1)<> 1) & 0x01) | /* CP9,CP8,CP7,CP6,CP5,CP4,CP3,CP2,CP1 */ + ((xored_ecc >> 2) & 0x02) | /* CP9,CP8,CP7,CP6,CP5,CP4,CP3,CP2 */ + ((xored_ecc >> 3) & 0x04) | /* CP9,CP8,CP7,CP6,CP5,CP4,CP3 */ + ((xored_ecc >> 4) & 0x08) | /* CP9,CP8,CP7,CP6,CP5,CP4 */ + ((xored_ecc >> 5) & 0x10) ); /* CP9,CP8,CP7,CP6,CP5 */ + word_adr = (u16)( + ((xored_ecc >> 11) & 0x01) | /* LP13,LP12,LP11,LP10,LP9,LP8,LP7,LP6,LP5,LP4,LP3,LP2,LP1 */ + ((xored_ecc >> 12) & 0x02) | /* LP13,LP12,LP11,LP10,LP9,LP8,LP7,LP6,LP5,LP4,LP3,LP2 */ + ((xored_ecc >> 13) & 0x04) | /* LP13,LP12,LP11,LP10,LP9,LP8,LP7,LP6,LP5,LP4,LP3 */ + ((xored_ecc >> 14) & 0x08) | /* LP13,LP12,LP11,LP10,LP9,LP8,LP7,LP6,LP5,LP4 */ + ((xored_ecc >> 15) & 0x10) | /* LP13,LP12,LP11,LP10,LP9,LP8,LP7,LP6,LP5 */ + ((xored_ecc >> 16) & 0x20) | /* LP13,LP12,LP11,LP10,LP9,LP8,LP7,LP6 */ + ((xored_ecc >> 17) & 0x40) ); /* LP13,LP12,LP11,LP10,LP9,LP8,LP7 */ + + data[word_adr] = data[word_adr] ^ (0x01 << bit_adr); + *(u32*)stored_ecc = new_ecc; + PRINTDEBUG( "%s : error corrected !!!\n", __FUNCTION__); + return FALSE; + } + /*ECCコード自体のエラー*/ + if( bit_count == 1) { + *(u32*)stored_ecc = new_ecc; + PRINTDEBUG( "%s : err of ecc own!!!\n", __FUNCTION__); + return FALSE; + } + /*訂正不可能な場合*/ + PRINTDEBUG( "%s : fatal error!!!\n", __FUNCTION__); + return FALSE; +#endif +} + +/*---------------------------------------------------------------------------* + Name: i_nandWriteSector + + Description: 論理セクタを書き込む + + Arguments: logical_sector : 論理ブロック番号 + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +void i_nandWriteSector( NandPageCacheFormat* NandPageCache, u32* src, u32 logical_sector) +{ + u16 logical_blk; + u16 physical_blk; + u32 logical_page; + u32 physical_page; + u16 cache_sect; + int result; + NandBlockStatInfo* AssignNandBlockStat; + + logical_blk = (u16)(logical_sector / 256); //1blk = 64*4 sectors + logical_page = (u32)(logical_sector / 4); + cache_sect = (u16)(logical_sector & 0x3); //0〜255 + + /*----- マージ制御 -----*/ + if( (NandPageCache->page_flushed[(logical_page & 0x3F)] != I_NAND_FLUSH_FALSE) || + ((NandPageCache->logical_page >> 6) != logical_blk)) { + + if( (NandPageCache->valid == TRUE)&&(NandPageCache->dirty == TRUE)) { //まずFlushする + result = i_nandFlushPageCache( NandPageCache); //今までのぶんをFlush + if( (result == I_NAND_ERR_VERIFY_ERR) || + (result == I_NAND_ERR_SUCCESS)) { + i_nandMergeBlock( NandPageCache); + }else{ + PRINTDEBUG( "%s : fatal error!\n", __FUNCTION__); + while( 1) {}; + } + }else{ + if( NandPageCache->BlockDirty == TRUE) { + i_nandMergeBlock( NandPageCache); + } + } + } + /*----------------------*/ + + /*マージでテーブルが更新されているかも知れないのでマージの後で計算*/ + physical_blk = nandBlkTbl[logical_blk]; + physical_page = (u32)(((u32)(physical_blk) << 6) + (logical_page & 0x3F)); + + /*----- 物理アドレスが未割り当てのとき -----*/ + if( physical_blk == 0xFFFF) { + if( (NandStat.free)&& //フリーブロックがあれば割り当て + (logical_blk < NAND_GUARANTEED_BLOCKS)) { + AssignNandBlockStat = NandStat.free; + nandBlkTbl[logical_blk] = NandStat.free->physical_blk; + + physical_blk = nandBlkTbl[logical_blk]; + physical_page = (u32)(((u32)(physical_blk) << 6) + (logical_page & 0x3F)); + + i_nandRemoveStatList( &(NandStat.free), AssignNandBlockStat); + i_nandInsertStatList( &(NandStat.busy), AssignNandBlockStat); + }else{ + return; + } + }/*-----------------------------------------*/ + + /*PageCacheが無効の場合*/ + if( NandPageCache->valid == FALSE) { + nandFillPageCache( NandPageCache, physical_page); + NandPageCache->logical_page = logical_page; //PageCacheの論理ブロック番号更新 + }else{ + /*PageCacheが有効かつヒットしたとき*/ + if( (NandPageCache->valid == TRUE) && + (NandPageCache->logical_page == logical_page)) { + PRINTDEBUG( "%s : PageCache Hit! (physical:0x%x, logical:0x%x)\n", __FUNCTION__, physical_page, logical_page); + }else{ + /*PageCacheが有効だがヒットしないとき*/ + if( NandPageCache->dirty == TRUE) { + result = i_nandFlushPageCache( NandPageCache); //今までのぶんをFlush + if( (result == I_NAND_ERR_FATAL_ERR)|| + (result == I_NAND_ERR_IMPOSSIBLE_ERR)) { + while( 1) {}; + } + if( result == I_NAND_ERR_VERIFY_ERR) { //Flush失敗時はすぐにマージしてリカバリ + i_nandMergeBlock( NandPageCache); + /*マージでテーブルが更新されているかも知れないのでマージの後で計算*/ + physical_blk = nandBlkTbl[logical_blk]; + physical_page = (u32)(((u32)(physical_blk) << 6) + (logical_page & 0x3F)); + } + } + nandFillPageCache( NandPageCache, physical_page); + NandPageCache->logical_page = logical_page; //PageCacheの論理ブロック番号更新 + } + } + + nandWritePageCache( NandPageCache, src, cache_sect); + + NandPageCache->dirty = TRUE; + return; +} + +/*---------------------------------------------------------------------------* + Name: nandFillPageCache + + Description: 物理ブロックをPageCacheに読み出す + + Arguments: physical_blk : 物理ブロック番号 + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +void nandFillPageCache( NandPageCacheFormat* NandPageCache, u32 physical_page) +{ + u16 i; + u32 physical_page_addr; + + PRINTDEBUG( "%s : Fill PageCache! (physical_page:0x%x)\n", __FUNCTION__, physical_page); + + for( i=0; iNandPage[i]), physical_page_addr, 1); + + /*自動生成されたECCを別の領域に退避しておく*/ + miCpuCopy8( &(nand_ecc[0]), &(NandPageCache->ecc[i][0]), 4); + miCpuCopy8( &(nand_ecc[1]), &(NandPageCache->ecc[i][1]), 4); + miCpuCopy8( &(nand_ecc[2]), &(NandPageCache->ecc[i][2]), 4); + miCpuCopy8( &(nand_ecc[3]), &(NandPageCache->ecc[i][3]), 4); + } + + /*oldブロックにライトバックするときのためのスペア領域をつくる*/ + miCpuCopy8( &(NandPageCache->NandPage[0].spare), &(NandPageCache->NandSpareWriteBack), + NAND_PAGE_SPARESIZE); + + NandPageCache->valid = TRUE; + NandPageCache->dirty = FALSE; + /*呼び出し元でNandPageCache->logical_blkを更新すること*/ +} + + +/*---------------------------------------------------------------------------* + Name: i_nandFlush + + Description: + + Arguments: + + Returns: + *---------------------------------------------------------------------------*/ +BOOL i_nandFlush( NandPageCacheFormat* NandPageCache) +{ + int result; + + result = 0; + /*--- ページキャッシュのフラッシュ ---*/ + if( (NandPageCache->valid == TRUE) && (NandPageCache->dirty == TRUE)) { + result = i_nandFlushPageCache( NandPageCache); + if( (result == I_NAND_ERR_FATAL_ERR)|| + (result == I_NAND_ERR_IMPOSSIBLE_ERR)) { + PRINTDEBUG( "%s : fatal error!\n", __FUNCTION__); + while( 1) {}; +// return( FALSE); + }else{ + i_nandMergeBlock( NandPageCache); + } + }/*----------------------------------*/ + + /*--- Blockのマージ ---*/ + if( NandPageCache->BlockDirty == TRUE) { + if( i_nandMergeBlock( NandPageCache) == FALSE) { + PRINTDEBUG( "%s : fatal error!\n", __FUNCTION__); + while( 1) {}; +// return( FALSE); + } + }/*--------------------*/ + + return( TRUE); +} + + +/*---------------------------------------------------------------------------* + Name: i_nandFlushPageCache + + Description: PageCacheから物理ブロックに書き出す + + Arguments: physical_blk : 物理ブロック番号 + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +int i_nandFlushPageCache( NandPageCacheFormat* NandPageCache) +{ + u16 i, k; + u16 new_physical_blk; + u32 new_physical_page_addr; +// u16 old_physical_blk; + u32 old_physical_page_addr; + NandPageFormat NandPage; + BOOL rslt_writepage; + + PRINTDEBUG( "%s\n", __FUNCTION__); + + /**/ +// miCpuFill8( bad_physical_blk, 0xFF, (NAND_FLUSH_RETRY_COUNT*sizeof(bad_physical_blk))); + + /*---------- 初回Flush時はNewBlockをサーチする ----------*/ + if( NandPageCache->OnTheFlush == 0xFFFF) { + if( NandStat.free) { + do { + NandPageCache->TargetNandBlockStat = NandStat.free; + new_physical_blk = NandStat.free->physical_blk; + if( new_physical_blk >= 1024) { + PRINTDEBUG( "%s : fatal error! illegal block number.\n", __FUNCTION__); + return( I_NAND_ERR_FATAL_ERR); //不正番号 + } + /*-----*/ + /*newブロックの消去回数読み出し(TODO:パリティチェック)*/ + new_physical_page_addr = (u32)(new_physical_blk << 6); + i_nandReadPage( &NandPage, new_physical_page_addr, 1); + /*消去回数をインクリメントしてWriteForwardバッファに適用,論理アドレスも適用*/ + miCpuCopy8( &(NandPage.spare), &(NandPageCache->NandSpareWriteForward), NAND_PAGE_SPARESIZE); +/* NandPageCache->NandSpareWriteForward.logical_adr0 = 0xFFFF;//(u16)(NandPageCache->logical_page >> 6); + NandPageCache->NandSpareWriteForward.logical_adr1 = 0xFFFF;//(u16)(NandPageCache->logical_page >> 6); + NandPageCache->NandSpareWriteForward.block_erase_count0 = NandPage.spare.block_erase_count0 + 1; + NandPageCache->NandSpareWriteForward.block_erase_count1 = NandPage.spare.block_erase_count1 + 1; +*/ + /*newブロック消去(TODO:eraseエラーチェック)*/ + if( i_nandEraseBlock( new_physical_blk) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandMarkingBadBlock( new_physical_blk, &(NandPageCache->NandSpareWriteForward)); + i_nandRemoveStatList( &(NandStat.free), NandPageCache->TargetNandBlockStat); + }else{ + break; + } + }while( 1); + + /*Flush用ブロックとしてnew_physical_blkを登録*/ + NandPageCache->OnTheFlush = new_physical_blk; + + for( i=0; i<64; i++) { + NandPageCache->page_flushed[i] = I_NAND_FLUSH_FALSE; + } + NandPageCache->page_flushed_count = 0; + + /*merge関数でmerge用ブロックをfreeリストから探すので、移動しておく*/ + //TODO:移動せずに、merge関数で探すときによけるべき? + i_nandRemoveStatList( &(NandStat.free), NandPageCache->TargetNandBlockStat); + i_nandInsertStatList( &(NandStat.busy), NandPageCache->TargetNandBlockStat); + + /*-----*/ + }else{ /*freeブロックがない場合は書き込み不可能なのでエラー*/ + PRINTDEBUG( "%s : fatal error! cannot found free block.\n", __FUNCTION__); + return( I_NAND_ERR_IMPOSSIBLE_ERR); + } + }else{ /* 2回目以降のFlush時 */ + new_physical_blk = NandPageCache->OnTheFlush; + if( new_physical_blk >= 1024) { + PRINTDEBUG( "%s : fatal error! illegal block number.\n", __FUNCTION__); + return( I_NAND_ERR_FATAL_ERR); //不正番号 + } + } + /*-------------------------------------------------------*/ + + PRINTDEBUG( "new_physical_blk : 0x%x, flushed_count : 0x%x\n", new_physical_blk, + NandPageCache->page_flushed_count); + + /*newブロックの該当ページにPageCacheを書き込み*/ + for( i=0; ilogical_page) & 0x3F)); + //先頭ページから順に書いていく(ランダムページ書き込み禁止のため) + new_physical_page_addr = (u32)((new_physical_blk << 6) + NandPageCache->page_flushed_count); + /*キャッシュのスペア領域にnewブロックの消去回数と論理アドレスを適用*/ + miCpuCopy8( &(NandPageCache->NandSpareWriteForward), &(NandPageCache->NandPage[i].spare), NAND_PAGE_SPARESIZE); + //TODO:i_nandWritePageのエラーコード見る + rslt_writepage = i_nandWritePage( &(NandPageCache->NandPage[i]), new_physical_page_addr, 1); + + /*Flushedフラグ*/ + if( NandPageCache->page_flushed[((NandPageCache->logical_page) & 0x3F)] != I_NAND_FLUSH_FALSE) { + PRINTDEBUG( "%s : kabutta!\n", __FUNCTION__); + while( 1) {}; + } + NandPageCache->page_flushed[((NandPageCache->logical_page) & 0x3F)] = NandPageCache->page_flushed_count; + NandPageCache->page_flushed_count++; + } + + + /*簡易ベリファイ(今回のFLUSHでバッドブロック化しなかったかチェック)*/ + for( i=0; ilogical_page) & 0x3F)); + i_nandReadPage( &NandPage, new_physical_page_addr, 1); + + for( k=0; k<4; k++) { + if( (i_nandCheckECC( (u32*)(&(NandPage.data.data[k][0])), &(NandPage.spare.ecc[k]), + *(u32*)((u32)(&(nand_ecc[0]))+(k*4))) == FALSE) || + ( rslt_writepage == FALSE)) { + //(フラッシュできなかったブロックをバッドブロック化するのはmerge関数) + //ここではマーキングのみ +// i_nandRemoveStatList( &(NandStat.busy), NandPageCache->TargetNandBlockStat); //BadBlockはfreeリストから外す + NandPageCache->NandSpareWriteForward.block_stat_bad = 0; + NandPageCache->TargetNandBlockStat->block_stat_bad = 0; +// NandPageCache->OnTheFlush = 0xFFFF; +// NandPageCache->BlockDirty = FALSE; + NandPageCache->page_flushed[((NandPageCache->logical_page) & 0x3F)] = I_NAND_FLUSH_CASHED; + PRINTLEVEL2( "%s : FLUSH failed!(bad block generated)\n", __FUNCTION__); + //Flush失敗によりnewブロックにライトが発生しないので、BlockDirtyはTRUEにしない + return( I_NAND_ERR_VERIFY_ERR); + } + } + } + + /*PageCacheを無効化*/ + NandPageCache->valid = FALSE; + NandPageCache->dirty = FALSE; + + NandPageCache->BlockDirty = TRUE; + +// PRINTDEBUG( "old : 0x%x, new : 0x%x\n", old_physical_blk, new_physical_blk); + return( I_NAND_ERR_SUCCESS); +} + + +/*---------------------------------------------------------------------------* + Name: i_nandMergeBlock + + Description: + + Arguments: + + Returns: None + *---------------------------------------------------------------------------*/ +BOOL i_nandMergeBlock( NandPageCacheFormat* NandPageCache) +{ + u16 i, k; + u16 retry_count = 0; + u16 logical_blk; + u16 old_physical_blk; + u32 old_physical_page_addr; + u16 new_physical_blk; + u32 new_physical_page_addr; + u16 merge_physical_blk; + u32 merge_physical_page_addr; + NandPageFormat NandPage; + NandBlockStatInfo* MergeNandBlockStat; + u32 bad_physical_page_addr; + u16 bad_physical_blk[NAND_FLUSH_RETRY_COUNT]; + BOOL rslt_writepage; + + /*NewBlockのECCチェックはFLUSHで逐一実行済み*/ + + PRINTDEBUG( "%s\n", __FUNCTION__); + + if( NandPageCache->OnTheFlush == 0xFFFF) { + return( FALSE); + } + + do { + /*----- Merge用にNewBlockをサーチする -----*/ + do { + MergeNandBlockStat = NandStat.free; + do { + if( MergeNandBlockStat == NULL) { /*freeブロックがない場合*/ + PRINTDEBUG( "%s : fatal error! cannot found free block.\n", __FUNCTION__); + while( 1) {}; //return( FALSE); + }else{ + break; /*見つかった*/ + } + // MergeNandBlockStat = MergeNandBlockStat->next; + }while( 1); + + merge_physical_blk = MergeNandBlockStat->physical_blk; + if( merge_physical_blk >= 1024) { + PRINTDEBUG( "%s : fatal error! illegal block number.\n", __FUNCTION__); + while( 1) {}; //return( FALSE); //不正番号 + } + /*mergeブロックの消去回数読み出し(TODO:パリティチェック)*/ + merge_physical_page_addr = (u32)(merge_physical_blk << 6); + i_nandReadPage( &NandPage, merge_physical_page_addr,1 ); + /*消去回数をインクリメントしてWriteMergeバッファに適用,論理アドレスも適用*/ + miCpuCopy8( &(NandPage.spare), &(NandPageCache->NandSpareWriteMerge), NAND_PAGE_SPARESIZE); + NandPageCache->NandSpareWriteMerge.logical_adr0 = (u16)(NandPageCache->logical_page >> 6); + NandPageCache->NandSpareWriteMerge.logical_adr1 = (u16)(NandPageCache->logical_page >> 6); + NandPageCache->NandSpareWriteMerge.block_erase_count0 = NandPage.spare.block_erase_count0 + 1; + NandPageCache->NandSpareWriteMerge.block_erase_count1 = NandPage.spare.block_erase_count1 + 1; + + /*mergeブロック消去*/ + if( i_nandEraseBlock( merge_physical_blk) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandMarkingBadBlock( merge_physical_blk, &(NandPageCache->NandSpareWriteMerge)); + i_nandRemoveStatList( &(NandStat.free), MergeNandBlockStat); + }else{ + break; + } + }while( 1); + + + /*oldブロック算出(newブロックとは必ず異なっている)*/ + logical_blk = (u16)((NandPageCache->logical_page) >> 6); + old_physical_blk = nandBlkTbl[logical_blk]; + + for( i=0; i<64; i++) { + merge_physical_page_addr = (u32)((merge_physical_blk << 6) + i); + switch( NandPageCache->page_flushed[i]) { + case I_NAND_FLUSH_FALSE:/*OldBlockからマージするデータをMergeBlockにコピーする*/ + old_physical_page_addr = (u32)((old_physical_blk<<6) + i); + i_nandReadPage( &NandPage, old_physical_page_addr, 1); + /*spare領域(消去回数,論理アドレス)変更*/ + miCpuCopy8( &(NandPageCache->NandSpareWriteMerge), &(NandPage.spare), NAND_PAGE_SPARESIZE); + //TODO:i_nandWritePageのエラーコード見る + rslt_writepage = i_nandWritePage( &NandPage, merge_physical_page_addr, 1); + break; + case I_NAND_FLUSH_CASHED:/*キャッシュからマージするデータをMergeBlockにコピーする*/ + /*spare領域(消去回数,論理アドレス)変更*/ + miCpuCopy8( &(NandPageCache->NandSpareWriteMerge), &(NandPageCache->NandPage[0].spare), + NAND_PAGE_SPARESIZE); + //TODO:i_nandWritePageのエラーコード見る + rslt_writepage = i_nandWritePage( &(NandPageCache->NandPage[0]), merge_physical_page_addr, 1); + break; + default:/*NewBlockからマージするデータをMergeBlockにコピーする*/ + if( NandPageCache->page_flushed[i] >= 0) { + new_physical_page_addr = (u32)(((NandPageCache->OnTheFlush)<<6) + + NandPageCache->page_flushed[i]); + i_nandReadPage( &NandPage, new_physical_page_addr, 1); + /*spare領域(消去回数,論理アドレス)変更*/ + miCpuCopy8( &(NandPageCache->NandSpareWriteMerge), &(NandPage.spare), NAND_PAGE_SPARESIZE); + //TODO:i_nandWritePageのエラーコード見る + rslt_writepage = i_nandWritePage( &NandPage, merge_physical_page_addr, 1); + break; + }else{ + PRINTDEBUG( "%s : fatal error! invalid status(0x%x).\n", NandPageCache->page_flushed[i]); + while( 1) {}; + } + } + if( rslt_writepage == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandRemoveStatList( &(NandStat.free), MergeNandBlockStat); //BadBlockはfreeリストから外す + bad_physical_blk[retry_count] = merge_physical_blk; + goto retry; + } + } + + /* mergeをretryするとき再度freeリストから探すので、busyに移動しておく*/ + i_nandRemoveStatList( &(NandStat.free), MergeNandBlockStat); + i_nandInsertStatList( &(NandStat.busy), MergeNandBlockStat); + + /*簡易ベリファイ(今回のmergeでバッドブロック化しなかったかチェック)*/ + for( i=0; i<64; i++) { + merge_physical_page_addr = (u32)((merge_physical_blk << 6) + i); + i_nandReadPage( &NandPage, merge_physical_page_addr, 1); + for( k=0; k<4; k++) { + if( i_nandCheckECC( (u32*)(&(NandPage.data.data[k][0])), &(NandPage.spare.ecc[k]), + *(u32*)((u32)(&(nand_ecc[0]))+(k*4))) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandRemoveStatList( &(NandStat.busy), MergeNandBlockStat); //BadBlockはfreeリストから外す + bad_physical_blk[retry_count] = merge_physical_blk; + goto retry; + } + } + } + break; /* Merge成功 */ +retry: + retry_count++; + if( retry_count >= NAND_FLUSH_RETRY_COUNT) { + break; + } + PRINTDEBUG( "%s : retry merge!( blk 0x%x is bad.)\n", __FUNCTION__, + merge_physical_blk); + }while( 1); + + /*PageCacheを無効化*/ + NandPageCache->valid = FALSE; + NandPageCache->dirty = FALSE; + + //TODO:newblockを消去し、ブロック情報を書き戻す + //(TODO:テーブルをNAND上に持つようにすればこの処理は必要なし) + + /*newブロック消去*/ + new_physical_blk = NandPageCache->OnTheFlush; + if( i_nandEraseBlock( new_physical_blk) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + NandPageCache->TargetNandBlockStat->block_stat_bad = 0; + NandPageCache->NandSpareWriteForward.block_stat_bad = 0; + } + /*newブロックに書き直すスペア領域構築*/ + NandPageCache->NandSpareWriteForward.logical_adr0 = 0xFFFF; //未使用マーキング + NandPageCache->NandSpareWriteForward.logical_adr1 = 0xFFFF; + NandPageCache->NandSpareWriteForward.block_erase_count0++; + NandPageCache->NandSpareWriteForward.block_erase_count1++; + /*newブロックに書き直すデータをNandPageに作成*/ + miCpuFill8( &(NandPage.data), 0xFF, NAND_PAGE_DATASIZE); + miCpuCopy8( &(NandPageCache->NandSpareWriteForward), &(NandPage.spare), NAND_PAGE_SPARESIZE); + /*newブロックに書き込み*/ + for( i=0; i<64; i++) { + new_physical_page_addr = (u32)((new_physical_blk<<6) + i); + //TODO:i_nandWritePageのエラーコード見る + if( i_nandWritePage( &NandPage, new_physical_page_addr, 1) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandMarkingBadBlock( new_physical_blk, &(NandPage.spare)); + break; + } + } + /*-----------------------------------------------------*/ + + + /*----- OldBlockを消去し、ブロック情報を書き戻す -----*/ + //(TODO:テーブルをNAND上に持つようにすればこの処理は必要なし) + + /*oldブロック消去*/ + if( i_nandEraseBlock( old_physical_blk) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + NandBlockStat[old_physical_blk].block_stat_bad = 0; + NandPageCache->NandSpareWriteBack.block_stat_bad = 0; + } + /*oldブロックに書き直すスペア領域構築*/ + NandPageCache->NandSpareWriteBack.logical_adr0 = 0xFFFF; //未使用マーキング + NandPageCache->NandSpareWriteBack.logical_adr1 = 0xFFFF; + NandPageCache->NandSpareWriteBack.block_erase_count0++; + NandPageCache->NandSpareWriteBack.block_erase_count1++; + /*oldブロックに書き直すデータをNandPageに作成*/ + miCpuFill8( &(NandPage.data), 0xFF, NAND_PAGE_DATASIZE); + miCpuCopy8( &(NandPageCache->NandSpareWriteBack), &(NandPage.spare), NAND_PAGE_SPARESIZE); + /*oldブロックに書き込み*/ + for( i=0; i<64; i++) { + old_physical_page_addr = (u32)((old_physical_blk<<6) + i); + //TODO:i_nandWritePageのエラーコード見る + if( i_nandWritePage( &NandPage, old_physical_page_addr, 1) == FALSE) { + PRINTLEVEL2( "bad block generated : %s, %d\n", __FUNCTION__, __LINE__); + i_nandMarkingBadBlock( old_physical_page_addr, &(NandPage.spare)); + break; + } + } + /*-----------------------------------------------------*/ + + /*フラッシュできなかったmergeブロックを一気にバッドブロック化*/ + for( i=0; iOnTheFlush); + PRINTDEBUG( "merge_physical_blk:0x%x\n", merge_physical_blk); + /*---------------------------------*/ + + { + //u16 reload_logical_adr; + //u32 reload_erase_count; + + /*BlockStatリンクリスト更新*/ + /*new_physical_blkは消去回数が更新されたので、busyリストから外してfreeリストに入れ直す*/ + //memo:NandPageCache->TargetNandBlockStatはNandBlockStat[NandPageCache->OnTheFlush]でも良いはず + i_nandRemoveStatList( &(NandStat.busy), NandPageCache->TargetNandBlockStat); + NandPageCache->TargetNandBlockStat->erase_count++; + if( NandPageCache->TargetNandBlockStat->block_stat_bad == 0xFF) { //BadBlockでなければ + i_nandInsertStatList( &(NandStat.free), NandPageCache->TargetNandBlockStat); + } + + /*merge_physical_blkをbusyリストから外してbusyリストに入れ直す(ソートし直し)*/ + i_nandRemoveStatList( &(NandStat.busy), MergeNandBlockStat); + MergeNandBlockStat->erase_count++; //リンクリスト上の消去回数も更新する + i_nandInsertStatList( &(NandStat.busy), MergeNandBlockStat); + + /*old_physical_blkをbusyリストから外してfreeリストに入れる*/ + //i_nandCheckBlock( old_physical_blk, &reload_logical_adr, &reload_erase_count); + i_nandRemoveStatList( &(NandStat.busy), &(NandBlockStat[old_physical_blk])); + NandBlockStat[old_physical_blk].erase_count++; + if( NandBlockStat[old_physical_blk].block_stat_bad == 0xFF) { //BadBlockでなければ + i_nandInsertStatList( &(NandStat.free), &(NandBlockStat[old_physical_blk])); + } + } + + /**/ + for( i=0; i<64; i++) { + NandPageCache->page_flushed[i] = I_NAND_FLUSH_FALSE; + } + NandPageCache->page_flushed_count = 0; + + + NandPageCache->OnTheFlush = 0xFFFF; + NandPageCache->BlockDirty = FALSE; + + return( TRUE); +} + + +/*後発BadBlockにマーキングする(未使用、TODO:各部から呼び出す)*/ +void i_nandMarkingBadBlock( u16 physical_blk, NandSpareFormat* NandSpareOrg) +{ + u16 i; + u32 physical_page_addr; + NandPageFormat NandPage; + + miCpuFill8( &(NandPage.data.data[0][0]), 0xFF, NAND_PAGE_DATASIZE); + miCpuFill8( &(NandPage.spare), 0xFF, NAND_PAGE_SPARESIZE); + miCpuCopy8( "CTR\0", &(NandPage.spare.sig0[0]), 4); + miCpuCopy8( "CTR\0", &(NandPage.spare.sig1[0]), 4); + + NandPage.spare.block_stat_bad = 0x00; /* Bad Marking */ + NandPage.spare.block_erase_count0 = NandSpareOrg->block_erase_count0; + NandPage.spare.block_erase_count1 = NandSpareOrg->block_erase_count1; + NandPage.spare.logical_adr0 = 0xFFFF; /* 未割り当て */ + NandPage.spare.logical_adr1 = 0xFFFF; /* 未割り当て */ + + i_nandEraseBlock( physical_blk); + for( i=0; i<64; i++) { + physical_page_addr = ((u32)(physical_blk)<<6) + i; + i_nandWritePage( &NandPage, physical_page_addr, 1); + } +} + + +/*---------------------------------------------------------------------------* + Name: nandReadPageCache + + Description: PageCacheからセクタを読み出す + + Arguments: + + Returns: None + *---------------------------------------------------------------------------*/ +static void nandReadPageCache( NandPageCacheFormat* NandPageCache, u32* dest, u16 cache_sect) +{ + u16 cache_offset; + u32* cache_sect_adr; + + cache_offset = (u16)(cache_sect % 4); //PageCacheのページ内でのオフセット + + cache_sect_adr = (u32*)((u32)&(NandPageCache->NandPage[0].data.data[cache_offset][0])); + + miCpuCopy8( cache_sect_adr, dest, 512); +} + +/*---------------------------------------------------------------------------* + Name: nandWritePageCache + + Description: PageCacheにセクタを書く + + Arguments: physical_blk : 物理ブロック番号 + + Returns: None + *---------------------------------------------------------------------------*/ +static void nandWritePageCache( NandPageCacheFormat* NandPageCache, u32* src, u16 cache_sect) +{ +// u16 cache_page; + u16 cache_offset; + u32* cache_sect_adr; + +// cache_page = (u16)(cache_sect / 4); //PageCache内でのページ番号 + cache_offset = (u16)(cache_sect % 4); //PageCacheのページ内でのオフセット + + cache_sect_adr = (u32*)((u32)&(NandPageCache->NandPage[0].data.data[cache_offset][0])); + + miCpuCopy8( src, cache_sect_adr, 512); + + //ECC再計算 +} + + + + + +/*---------------------------------------------------------------------------* + Name: nandEnable + + Description: + + Arguments: + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +void nandEnable( NandPageCacheFormat* NandPageCache) +{ + NandMsg nandMsg; + u32 recv_dat; + + g_nandPageCache = NandPageCache; +// i_nandEnable( NandPageCache); + nandMsg.cache = NandPageCache; + nandMsg.buf = NULL; + nandMsg.sector = 0; + nandMsg.sector_count = 0; + nandMsg.operation = NAND_OPERATION_ENABLE; + +// PRINTDEBUG( "%s(%d) : snd_dtq begin\n", __FUNCTION__, __LINE__); + osSendMessage( &nand_dtq, (OSMessage*)&nandMsg, OS_MESSAGE_BLOCK); + +// PRINTDEBUG( "%s(%d) : rcv_dtq begin\n", __FUNCTION__, __LINE__); + osReceiveMessage( &nand_result_dtq, (OSMessage*)&recv_dat, OS_MESSAGE_BLOCK); + + return; +} + +/*---------------------------------------------------------------------------* + Name: nandFormat + + Description: フォーマット(スペア領域を構築)する + + Arguments: + + Returns: + *---------------------------------------------------------------------------*/ +void nandFormat( void) +{ + NandMsg nandMsg; + u32 recv_dat; + + nandMsg.cache = g_nandPageCache; + nandMsg.buf = NULL; + nandMsg.sector = 0; + nandMsg.sector_count = 0; + nandMsg.operation = NAND_OPERATION_FORMAT; + +// PRINTDEBUG( "%s(%d) : snd_dtq begin\n", __FUNCTION__, __LINE__); + osSendMessage( &nand_dtq, (OSMessage*)&nandMsg, OS_MESSAGE_BLOCK); + +// PRINTDEBUG( "%s(%d) : rcv_dtq begin\n", __FUNCTION__, __LINE__); + osReceiveMessage( &nand_result_dtq, (OSMessage*)&recv_dat, OS_MESSAGE_BLOCK); + + return; +} + +/*---------------------------------------------------------------------------* + Name: nandReadSector + + Description: 論理セクタを読み出す + + Arguments: logical_sector : 論理ブロック番号 + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +void nandReadSector( u32* dest, u32 logical_sector, u32 sector_count) +{ + NandMsg nandMsg; + u32 recv_dat; + + nandMsg.cache = g_nandPageCache; + nandMsg.buf = dest; + nandMsg.sector = logical_sector; + nandMsg.sector_count = sector_count; + nandMsg.operation = NAND_OPERATION_READ; + +// PRINTDEBUG( "%s(%d) : snd_dtq begin\n", __FUNCTION__, __LINE__); + osSendMessage( &nand_dtq, (OSMessage*)&nandMsg, OS_MESSAGE_BLOCK); + +// PRINTDEBUG( "%s(%d) : rcv_dtq begin\n", __FUNCTION__, __LINE__); + osReceiveMessage( &nand_result_dtq, (OSMessage*)&recv_dat, OS_MESSAGE_BLOCK); + + return; +} + +/*---------------------------------------------------------------------------* + Name: nandWriteSector + + Description: 論理セクタを書き込む + + Arguments: logical_sector : 論理ブロック番号 + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +void nandWriteSector( u32* src, u32 logical_sector, u32 sector_count) +{ + NandMsg nandMsg; + u32 recv_dat; +// ID tskid; + + nandMsg.cache = g_nandPageCache; + nandMsg.buf = src; + nandMsg.sector = logical_sector; + nandMsg.sector_count = sector_count; + nandMsg.operation = NAND_OPERATION_WRITE; + /* + if( get_tid( &tskid) == E_OK) { + nandMsg.tskid = tskid; + }else{ + nandMsg.tskid = 0; + }*/ + +// PRINTDEBUG( "%s(%d) : snd_dtq begin\n", __FUNCTION__, __LINE__); + osSendMessage( &nand_dtq, (OSMessage*)&nandMsg, OS_MESSAGE_BLOCK); + +// PRINTDEBUG( "%s(%d) : rcv_dtq begin\n", __FUNCTION__, __LINE__); + osReceiveMessage( &nand_result_dtq, (OSMessage*)&recv_dat, OS_MESSAGE_BLOCK); + + return; +} + +/*---------------------------------------------------------------------------* + Name: nandFlush + + Description: + + Arguments: + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +BOOL nandFlush( void) +{ + NandMsg nandMsg; + u32 recv_dat; + + nandMsg.cache = g_nandPageCache; + nandMsg.buf = NULL; + nandMsg.sector = 0; + nandMsg.sector_count = 0; + nandMsg.operation = NAND_OPERATION_FLUSH; + +// PRINTDEBUG( "%s(%d) : snd_dtq begin\n", __FUNCTION__, __LINE__); + osSendMessage( &nand_dtq, (OSMessage*)&nandMsg, OS_MESSAGE_BLOCK); + +// PRINTDEBUG( "%s(%d) : rcv_dtq begin\n", __FUNCTION__, __LINE__); + osReceiveMessage( &nand_result_dtq, (OSMessage*)&recv_dat, OS_MESSAGE_BLOCK); + if( recv_dat == 1) { + return( TRUE); + }else{ + return( FALSE); + } +} + +/*タスク化してないので隠し関数*/ +void nandEraseChip( void) +{ + u16 i; + for( i=0; i<1024; i++) { + i_nandEraseBlock( i); + } +} + +/*---------------------------------------------------------------------------* + Name: i_nandThread + + Description: + + Arguments: + + Returns: None + *---------------------------------------------------------------------------*/ +static void i_nandThread( void* arg) +{ + NandMsg* nandMsg; + BOOL result; + u32 current_dat; + u32 i; + u32* buf_pointer; + + result = TRUE; + while( TRUE) { + osReceiveMessage( &nand_dtq, (OSMessage*)¤t_dat, OS_MESSAGE_BLOCK); + nandMsg = (NandMsg*)current_dat; + PRINTDEBUG( "nand task : receive command : %d\n", nandMsg->operation); + + switch( nandMsg->operation) { + case NAND_OPERATION_ENABLE: + i_nandEnable( nandMsg->cache); + break; + case NAND_OPERATION_FORMAT: + i_nandFormat(); + break; + case NAND_OPERATION_READ: + PRINTDEBUG( "from:%x, sectors:%x\n", nandMsg->sector, nandMsg->sector_count); + for( i=0; isector_count; i++) { + buf_pointer = (u32*)((u32)(nandMsg->buf) + (512 * i)); + i_nandReadSector( nandMsg->cache, buf_pointer, (nandMsg->sector) + i); + } + break; + case NAND_OPERATION_WRITE: + PRINTDEBUG( "from:%x, sectors:%x\n", nandMsg->sector, nandMsg->sector_count); + /*---------- 通常の書き込み ----------*/ + for( i=0; isector_count; i++) { + buf_pointer = (u32*)((u32)(nandMsg->buf) + (512 * i)); + i_nandWriteSector( nandMsg->cache, buf_pointer, (nandMsg->sector) + i); + }/*-----------------------------------*/ + break; + case NAND_OPERATION_FLUSH: + result = i_nandFlush( nandMsg->cache); + break; + default: + break; + } + + PRINTDEBUG( "nand task : operation ends, send message begin\n"); + //メッセージ返送 + if( result) { + current_dat = 1; + }else{ + current_dat = 0; + } + osSendMessage( &nand_result_dtq, (OSMessage)current_dat, OS_MESSAGE_BLOCK); + } +} + diff --git a/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.h b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.h new file mode 100644 index 0000000..eb91da7 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nand.h @@ -0,0 +1,121 @@ +/*---------------------------------------------------------------------------* + Project: CTR - NAND driver + File: nand.h + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef __NAND_H__ +#define __NAND_H__ + + +#include "nandif_ip.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +/*---------------------------------------------------------------------------* + 定義 + *---------------------------------------------------------------------------*/ +#define NAND_GUARANTEED_BLOCKS (999) +#define NAND_GUARANTEED_SECTORS (NAND_GUARANTEED_BLOCKS*256) //保証されたセクタ数(=使える容量) + +#define NAND_CACHE_PAGES (1) /* 1以外は設定禁止 */ +#define NAND_ANTI_READDISTURB (1) /* TODO */ + +/*---------------------------------------------------------------------------* + 構造体 + *---------------------------------------------------------------------------*/ +typedef struct NandPageCacheFormat { //ブロックキャッシュ + u16 valid; /*有効 or 無効*/ + u16 dirty; /*ダーティ or クリーン*/ + u16 OnTheFlush; /*Flush進行中のNewPhysicalBlk番号*/ + u16 reserve0; + struct NandBlockStatInfo* TargetNandBlockStat; /*Flush進行中のNewPhysicalBlk情報へのポインタ*/ + BOOL BlockDirty; /*ブロック・ダーティ or クリーン*/ +// u16 logical_blk; /*論理ブロック番号*/ + u32 logical_page; /*論理ページ番号*/ + u8 page_flushed_count; /*Flush先の物理ページオフセット(0からカウントアップ)*/ + u8 reserve1[3]; + int page_flushed[64]; /*Flushによって書き込まれた物理ページのオフセット*/ + struct NandPageFormat NandPage[NAND_CACHE_PAGES]; /*1ページ分のキャッシュメモリ*/ + u32 ecc[NAND_CACHE_PAGES][4]; /*読み出し時に自動生成されたECC*/ + struct NandSpareFormat NandSpareWriteForward; /*newブロックにライトするスペア領域*/ + struct NandSpareFormat NandSpareWriteBack; /*oldブロックにライトバックするスペア領域*/ + struct NandSpareFormat NandSpareWriteMerge; /*mergeブロックにライトするスペア領域*/ +} NandPageCacheFormat; + + +typedef struct NandBlockStatInfo { //ブロック状態リンクリスト + u16 physical_blk; + u16 block_stat_bad; + u32 erase_count; + struct NandBlockStatInfo* next; +} NandBlockStatInfo; + + +typedef struct NandStatInfo { + struct NandBlockStatInfo* busy; /*論理blk番号が割り当て済み*/ + struct NandBlockStatInfo* free; /*論理blk番号が未割り当て*/ +} NandStatInfo; + + + +/*RTFS用 FATパラメータ*/ +typedef struct { + u32 device_capacity; //デバイス全体のサイズ(512Byte単位) + u32 adjusted_device_capacity; + + u32 memory_capacity; //data areaのサイズ(512Byte単位) + u32 adjusted_memory_capacity; //memory_capacityをシリンダ(heads*secptrack)の倍数に調整したサイズ(cylinders*heads*secptrackになる) + u16 volume_cylinders; + + u16 heads; + u16 secptrack; + u16 cylinders; + u16 SC; //sectors per cluster + u16 BU; + u16 RDE; //number of root dir entries(512 fix) + u32 SS; //sector size(512 fix) + u32 RSC; //reserved sector count(1 fix) + u16 FATBITS; //16 or 32 + u16 SF; //sectors per FAT + u32 SSA; //sectors in system area + u32 NOM; //sectors in master boot record + + u32 begin_sect; +} NandSpec; + + + +/*---------------------------------------------------------------------------* + API + *---------------------------------------------------------------------------*/ +BOOL nandRtfsAttach( int driveno, int partition); + +void nandInit( void); +void nandEnable( NandPageCacheFormat* NandPageCache); +void nandFormat( void); + +void nandReadSector( u32* dest, u32 logical_sector, u32 sector_count); +void nandWriteSector( u32* src, u32 logical_sector, u32 sector_count); + +BOOL nandFlush( void); +BOOL nandCheckMedia( void); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /*__NAND_H__*/ diff --git a/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif.c b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif.c new file mode 100644 index 0000000..41dcb1e --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif.c @@ -0,0 +1,398 @@ +/*---------------------------------------------------------------------------* + Project: CTR - NAND driver + File: nandif.c + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#include +#include "nandif_ip.h" +#include "nandif_reg.h" + +#if 0 +#define PRINTDEBUG osTPrintf +#else +#define PRINTDEBUG( ...) ((void)0) +#endif + + +/*---------------------------------------------------------------------------* + static関数 + *---------------------------------------------------------------------------*/ +static void i_nandWait( void); +static void i_nandStart( u16 mode); +static void i_nandSetEccDirection( u32 direction); +static void i_nandChangeBuffer( void); + +static u32 CalcECC( u32* buf); +static u32 CalcCP( u32* buf, u32 st, u32 cmp, u32 logic); +static u32 CalcLP( u32* buf, u32 st, u32 cmp, u32 logic); + +u32 nand_ecc[4]; + + +/*NAND低レベル層制御*/ +/*---------------------------------------------------------------------------* + Name: i_nandWait + + Description: オペレーションが終了するまで待つ + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void i_nandWait( void) +{ + u32 stat; + + while( 1) { + stat = *(vu16*)REG_NAND_COMMAND; + if( (stat & NAND_BUSY) == 0) { + return; + } + } +} + +/*---------------------------------------------------------------------------* + Name: i_nandStart + + Description: 指定したモードをスタートする + + Arguments: mode : + + Returns: None + *---------------------------------------------------------------------------*/ +static void i_nandStart( u16 mode) +{ + i_nandWait(); + *(vu32*)REG_NAND_COMMAND = (mode | NAND_START); +} + +/*---------------------------------------------------------------------------* + Name: i_nandSetEccDirection + + Description: ECCが算出される条件を指定する + + Arguments: direction : NAND_CTRL_ECC_WRITE or NAND_CTRL_ECC_READ + + Returns: None + *---------------------------------------------------------------------------*/ +static void i_nandSetEccDirection( u32 direction) +{ +} + +/*---------------------------------------------------------------------------* + Name: i_nandChangeBuffer + + Description: バッファの裏表を切り替える + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +static void i_nandChangeBuffer( void) +{ +} + +/*---------------------------------------------------------------------------* + Name: i_nandReset + + Description: HW-IPを初期状態にする + + Arguments: None + + Returns: None + *---------------------------------------------------------------------------*/ +void i_nandReset( void) +{ + /*--- HW ECC OFF ---*/ + *(vu16*)REG_NAND_ECC &= ~NAND_ENABLE_ECC; + + i_nandWait(); + *(u16*)REG_NAND_COMMAND = (NAND_RESET | NAND_START); + i_nandWait(); +} + +/*---------------------------------------------------------------------------* + Name: i_nandEraseBlock + + Description: 物理ブロックを消去する + + Arguments: physical_blk : 物理ブロック番号 + + Returns: FALSE : 失敗 + TRUE : 成功 + *---------------------------------------------------------------------------*/ +BOOL i_nandEraseBlock( u16 physical_blk) +{ +// PRINTDEBUG( ">>>@ erase physical blk : 0x%x\n", physical_blk); + + i_nandWait(); /*デバイス待ち*/ + + /*イレースするためのレジスタ設定*/ + *(u16*)REG_NAND_ADR_LO = 0; + *(u16*)REG_NAND_ADR_HI = (physical_blk << 1); + + /*書き込み開始*/ + *(u16*)REG_NAND_COMMAND = (NAND_ERASE_BLOCK | NAND_START); + +// osSleep( 1); /* 90nm(typ:2ms, max:3ms),60nm実測1.2ms */ + + i_nandWait(); /*デバイス内部消去動作中*/ +#if 0 + /*READY状態になったらPASSかFAILかを判定する*/ + while( 1) { + /*ステータスリード*/ + *(vu32*)NAND_IP_CTRL = NAND_CTRL_READSTAT | NAND_CTRL_ECC_WRITE | + (*(vu32*)NAND_IP_CTRL & NAND_CTRL_PAGE_MASK) | NAND_CTRL_SPEED_HI; + *(u32*)NAND_IP_CTRL |= NAND_CTRL_START; + /*ステータスリード*/ + i_nandWait(); + /*ステータスレジスタチェック*/ + if( *(vu16*)NAND_IP_STAT & NAND_STAT_READY) { + if( *(vu16*)NAND_IP_STAT & NAND_STAT_FAIL) { + return FALSE; + }else{ + return TRUE; + } + } + } +#else + return TRUE; +#endif +} + +/*---------------------------------------------------------------------------* + Name: i_nandReadPage + + Description: 物理ページをリードする + + Arguments: physical_page_addr : 物理ページ番号 + + Returns: + *---------------------------------------------------------------------------*/ +BOOL i_nandReadPage( NandPageFormat* dest, u32 physical_page_addr, BOOL sync) +{ + u32 byte_addr; + PRINTDEBUG( ">>>@ read physical page : 0x%x\n", physical_page_addr); + + /*IP動作中*/ + i_nandWait(); + + /*リードするためのレジスタ設定*/ + byte_addr = (physical_page_addr * NAND_PAGE_DATASIZE); + *(u16*)REG_NAND_ADR_LO = (u16)(byte_addr); + *(u16*)REG_NAND_ADR_HI = (u16)(((u32)byte_addr >> 16)); + + /*最初の1ページをリードする*/ + *(u16*)REG_NAND_COMMAND = (NAND_READ_PAGE | NAND_START); + + /*IP動作中*/ + i_nandWait(); + + if( sync) { + /*IP動作中*/ + i_nandWait(); + //ここでページバッファにデータが入った + } + + miCpuCopy16( (u16*)REG_NAND_DATA, &(dest->data.data[0][0]), NAND_PAGE_DATASIZE); + miCpuCopy16( (u16*)REG_NAND_SPARE, (u32*)&(dest->spare), NAND_PAGE_SPARESIZE); + + /*---------- ソフトウェアECC ----------*/ + { + u32 q_addr = (u32)(&(dest->data.data[0][0])); + nand_ecc[0] = CalcECC( (u32*)q_addr); + nand_ecc[1] = CalcECC( (u32*)(q_addr + 512)); + nand_ecc[2] = CalcECC( (u32*)(q_addr + 1024)); + nand_ecc[3] = CalcECC( (u32*)(q_addr + 1536)); + } + /*-------------------------------------*/ + + return TRUE; +} + + +/*---------------------------------------------------------------------------* + Name: i_nandWritePage + + Description: 物理ページに書き込む + 予め該当ページをイレースしておくこと。 + + Arguments: physical_page_addr : 物理ページ番号 + + Returns: + *---------------------------------------------------------------------------*/ +BOOL i_nandWritePage( NandPageFormat* src, u32 physical_page_addr, BOOL sync) +{ + u32 byte_addr; + PRINTDEBUG( ">>>@ write physical page : 0x%x\n", physical_page_addr); + + /*IP動作中*/ + i_nandWait(); + + /*DataとSpare*/ + miCpuCopy16( &(src->data.data[0][0]), (u16*)REG_NAND_DATA, NAND_PAGE_DATASIZE); //2KB + /*---------- ソフトウェアECC ----------*/ + { + u32 q_addr = (u32)(&(src->data.data[0][0])); + nand_ecc[0] = CalcECC( (u32*)(q_addr)); + nand_ecc[1] = CalcECC( (u32*)(q_addr + 512)); + nand_ecc[2] = CalcECC( (u32*)(q_addr + 1024)); + nand_ecc[3] = CalcECC( (u32*)(q_addr + 1536)); + } + /*-------------------------------------*/ + miCpuCopy16( &(nand_ecc[0]), &(src->spare.ecc[0]), 4); + miCpuCopy16( &(nand_ecc[1]), &(src->spare.ecc[1]), 4); + miCpuCopy16( &(nand_ecc[2]), &(src->spare.ecc[2]), 4); + miCpuCopy16( &(nand_ecc[3]), &(src->spare.ecc[3]), 4); + miCpuCopy16( &(src->spare), (u16*)REG_NAND_SPARE, NAND_PAGE_SPARESIZE); + + /*ライトするためのレジスタ設定*/ + byte_addr = (physical_page_addr * NAND_PAGE_DATASIZE); + *(u16*)REG_NAND_ADR_LO = (u16)(byte_addr); + *(u16*)REG_NAND_ADR_HI = (u16)(((u32)byte_addr >> 16)); + + *(u16*)REG_NAND_COMMAND = (NAND_PROGRAM_PAGE | NAND_START); + + if( sync) { + /*IP動作中*/ + i_nandWait(); +#if 0 + /*READY状態になったらPASSかFAILかを判定する*/ + while( 1) { + /*ステータスリード*/ + *(vu32*)NAND_IP_CTRL = NAND_CTRL_READSTAT | NAND_CTRL_ECC_WRITE | + (*(vu32*)NAND_IP_CTRL & NAND_CTRL_PAGE_MASK) | NAND_CTRL_SPEED_HI; + *(u32*)NAND_IP_CTRL |= NAND_CTRL_START; + + /*IP動作中*/ + i_nandWait(); + /*ステータスレジスタチェック*/ + if( *(vu16*)NAND_IP_STAT & NAND_STAT_READY) { + if( *(vu16*)NAND_IP_STAT & NAND_STAT_FAIL) { + return FALSE; + }else{ + return TRUE; + } + } + } +#else + return TRUE; +#endif + }else{ + return TRUE; + } +} + + +static u32 CalcECC( u32* buf) +{ + u32 cp[10]; + u32 lp[14]; + u32 cym; + + cp[0] = CalcCP( buf, 0, 0x1, 0); + cp[1] = CalcCP( buf, 1, 0x1, 1); + cp[2] = CalcCP( buf, 0, 0x2, 0); + cp[3] = CalcCP( buf, 2, 0x2, 1); + cp[4] = CalcCP( buf, 0, 0x4, 0); + cp[5] = CalcCP( buf, 4, 0x4, 1); + cp[6] = CalcCP( buf, 0, 0x8, 0); + cp[7] = CalcCP( buf, 8, 0x8, 1); + cp[8] = CalcCP( buf, 0x00, 0x10, 0); + cp[9] = CalcCP( buf, 0x10, 0x10, 1); + + lp[0] = CalcLP( buf, 0, 0x1, 0); + lp[1] = CalcLP( buf, 1, 0x1, 1); + lp[2] = CalcLP( buf, 0, 0x2, 0); + lp[3] = CalcLP( buf, 2, 0x2, 1); + lp[4] = CalcLP( buf, 0, 0x4, 0); + lp[5] = CalcLP( buf, 4, 0x4, 1); + lp[6] = CalcLP( buf, 0, 0x8, 0); + lp[7] = CalcLP( buf, 8, 0x8, 1); + lp[8] = CalcLP( buf, 0x00, 0x10, 0); + lp[9] = CalcLP( buf, 0x10, 0x10, 1); + lp[10] = CalcLP( buf, 0x00, 0x20, 0); + lp[11] = CalcLP( buf, 0x20, 0x20, 1); + lp[12] = CalcLP( buf, 0x00, 0x40, 0); + lp[13] = CalcLP( buf, 0x40, 0x40, 1); + + cym = ((lp[13] & 0x1) << 23) + + ((lp[12] & 0x1) << 22) + + ((lp[11] & 0x1) << 21) + + ((lp[10] & 0x1) << 20) + + ((lp[ 9] & 0x1) << 19) + + ((lp[ 8] & 0x1) << 18) + + ((lp[ 7] & 0x1) << 17) + + ((lp[ 6] & 0x1) << 16) + + ((lp[ 5] & 0x1) << 15) + + ((lp[ 4] & 0x1) << 14) + + ((lp[ 3] & 0x1) << 13) + + ((lp[ 2] & 0x1) << 12) + + ((lp[ 1] & 0x1) << 11) + + ((lp[ 0] & 0x1) << 10) + + ((cp[ 9] & 0x1) << 9) + + ((cp[ 8] & 0x1) << 8) + + ((cp[ 7] & 0x1) << 7) + + ((cp[ 6] & 0x1) << 6) + + ((cp[ 5] & 0x1) << 5) + + ((cp[ 4] & 0x1) << 4) + + ((cp[ 3] & 0x1) << 3) + + ((cp[ 2] & 0x1) << 2) + + ((cp[ 1] & 0x1) << 1) + + ((cp[ 0] & 0x1) << 0); +// osTPrintf( "cym:0x%x\n", cym); + return( cym); +} + +static u32 CalcCP( u32* buf, u32 st, u32 cmp, u32 logic) +{ + u32 cp; + u32 cmp0; + u32 i, j; + + cp = 0; + for( j=st; j<32; j++) { + if( (j & cmp) == 0) { + cmp0 = 0; + }else{ + cmp0 = 1; + } + if( cmp0 == logic) { + for( i=0; i<128; i++) { + cp ^= ((buf[i] >> j) & 0x1); + } + } + } + return cp; +} + +static u32 CalcLP( u32* buf, u32 st, u32 cmp, u32 logic) +{ + u32 lp; + u32 cmp0; + u32 i, j; + + lp = 0; + for( i=st; i<128; i++) { + if( (i & cmp) == 0) { + cmp0 = 0; + }else{ + cmp0 = 1; + } + if( cmp0 == logic) { + for( j=0; j<32; j++) { + lp ^= ((buf[i] >> j) & 0x1); + } + } + } + return lp; +} + diff --git a/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_ip.h b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_ip.h new file mode 100644 index 0000000..eec85f4 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_ip.h @@ -0,0 +1,86 @@ +/*---------------------------------------------------------------------------* + Project: CTR - NAND driver + File: nandif_ip.h + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef __NAND_IF_IP_H__ +#define __NAND_IF_IP_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +/*---------------------------------------------------------------------------* + 定数定義 + *---------------------------------------------------------------------------*/ +#define NAND_PAGE_DATASIZE (2048) +#define NAND_PAGE_SPARESIZE (64) +#define NAND_PAGE_ALLSIZE (NAND_PAGE_DATASIZE + NAND_PAGE_SPARESIZE) + + +/*---------------------------------------------------------------------------* + 構造体 + *---------------------------------------------------------------------------*/ +typedef struct NandSpareFormat {/*スペア領域 64バイト*/ + u8 block_stat_bad; // 1 ブロックステータス(バッドブロック) + u8 block_stat_warning; // 1 ブロックステータス(注意ブロック) + u8 page_stat; // 1 ページステータス + u8 pad0; // 1 + u32 block_erase_count0; // 4 ブロック消去回数(パリティ付き) + u16 logical_adr0; // 2 論理アドレス(パリティ付き) + u16 pad1; // 2 + u32 ecc[4]; //16 ECC 0〜3 + u32 block_erase_count1; // 4 ブロック消去回数(パリティ付き)予備 + u16 logical_adr1; // 2 論理アドレス(パリティ付き)予備 + u16 pad2; // 2 + char sig0[4]; // 4 Signature"CTR\0" + char sig1[4]; // 4 Signature"CTR\0" + u8 reserve[20]; //28 予約 +} NandSpareFormat; + +typedef struct NandDataFormat { /*データ領域 2048バイト*/ + u32 data[4][512/4]; +/* u32 data0[512/4]; //データ領域1/4(512バイト) + u32 data1[512/4]; //データ領域2/4(512バイト) + u32 data2[512/4]; //データ領域3/4(512バイト) + u32 data3[512/4]; //データ領域4/4(512バイト)*/ +} NandDataFormat; + +typedef struct NandPageFormat { /*ページ全体*/ + struct NandDataFormat data; + struct NandSpareFormat spare; //スペア領域(64バイト) +} NandPageFormat; + + +/* +typedef struct NandSysFormat { + u16 logical_adr; + u16 read_count; + u32 erase_count; +} NandSysFormat;*/ + + + +/*---------------------------------------------------------------------------* + API + *---------------------------------------------------------------------------*/ +BOOL i_nandEraseBlock( u16 physical_blk); +BOOL i_nandReadPage( NandPageFormat* dest, u32 physical_page_addr, BOOL sync); +BOOL i_nandWritePage( NandPageFormat* src, u32 physical_page_addr, BOOL sync); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /*__NAND_IF_IP_H__*/ diff --git a/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_reg.h b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_reg.h new file mode 100644 index 0000000..3ae363c --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM11/src/ne1/nandif_reg.h @@ -0,0 +1,125 @@ +/*---------------------------------------------------------------------------* + Project: CTR - NAND driver + File: nandif_reg.h + + Copyright 2006 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + *---------------------------------------------------------------------------*/ + +#ifndef __NAND_IP_REG_H__ +#define __NAND_IP_REG_H__ + + + + +//REG_NAND_COMMAND +#define NAND_START (0x0100) +#define NAND_BUSY NAND_START +#define NAND_RESET 6 +#define NAND_READ_STATUS 5 +#define NAND_ERASE_BLOCK 4 +#define NAND_PROGRAM_PAGE 3 +#define NAND_READ_PAGE 1 + +//REG_NAND_ECC +#define NAND_ENABLE_ECC 1 + +#define REG_NAND_ADR_LO (0x18019840) +#define REG_NAND_ADR_HI (0x18019842) +#define REG_NAND_COMMAND (0x1801984E) + +#define REG_NAND_ECC (0x18019850) + +#define REG_NAND_DATA (0x18019000) +#define REG_NAND_SPARE (0x18019800) + + +#define NAND_IP_ECC_0 (0x0100) //ダミー + +#if 0 +/********************************************* + NAND IPレジスタ + + (R/W) : readable and writable + (RO) : read only +*********************************************/ +#if (CTR_DEF_ENVIRONMENT_DSEMU == 1) +#define NAND_IP_BASE (0x08020000) +#else +#define NAND_IP_BASE (0x400D0000) // IOP実機設定 +#endif + + +#define NAND_IP_CADDR (NAND_IP_BASE + 0x0000) //16bit +#define NAND_IP_PADDR (NAND_IP_BASE + 0x0004) //24bit + dummy8bit +#define NAND_IP_COMMAND (NAND_IP_BASE + 0x0008) //0,1,2,3 +#define NAND_IP_CTRL (NAND_IP_BASE + 0x000C) //32bit +#define NAND_IP_ID (NAND_IP_BASE + 0x0010) //48bit +#define NAND_IP_ID_H (NAND_IP_BASE + 0x0014) +#define NAND_IP_STAT (NAND_IP_BASE + 0x0018) //16bit +#define NAND_IP_ECC_0 (NAND_IP_BASE + 0x0100) +#define NAND_IP_ECC_1 (NAND_IP_BASE + 0x0104) +#define NAND_IP_ECC_2 (NAND_IP_BASE + 0x0108) +#define NAND_IP_ECC_3 (NAND_IP_BASE + 0x010C) +#define NAND_IP_SPARE (NAND_IP_BASE + 0x0400) // 64BYTE +#define NAND_IP_DATA (NAND_IP_BASE + 0x0800) //2048BYTE + + + +/*コマンドレジスタに入れる値の詳細値*/ +#define NAND_COM_WRITE (0x00701080) //0x70:StatRead, 0x10:AutoProgram, 0x80:DataInput +#define NAND_COM_READ (0x00003000) //0x30:ReadStart, 0x00:ReadAdrInput +#define NAND_COM_ERASE (0x0070D060) //0x70:StatRead, 0xD0:EraseStart, 0x60:EraseSetup +#define NAND_COM_READID (0x00000090) + +/*コントロールレジスタに入れる値の詳細値*/ +#define NAND_CTRL_READSTAT (0x0010) //10000b +#define NAND_CTRL_READID (0x0011) +#define NAND_CTRL_WRITE (0x0012) +#define NAND_CTRL_ERASE (0x0013) +#define NAND_CTRL_READ_SETUP (0x0014) +#define NAND_CTRL_READ_2112 (0x000d) + +#define NAND_CTRL_RDY_MASK ((u32)0x1<<15) + +#define NAND_CTRL_ECC_WRITE (0) +#define NAND_CTRL_ECC_READ ((u32)0x1<<24) + +#define NAND_CTRL_PAGE_0 (0) +#define NAND_CTRL_PAGE_1 ((u32)1<<25) +#define NAND_CTRL_PAGE_MASK ((u32)1<<25) +#define NAND_CTRL_PAGE_SHIFT (25) + +#define NAND_CTRL_W8MHZ (0) +#define NAND_CTRL_W11MHZ ((u32)0x1<<26) +#define NAND_CTRL_W16MHZ ((u32)0x2<<26) + +#define NAND_CTRL_R8MHZ (0) +#define NAND_CTRL_R11MHZ ((u32)0x1<<28) + +#define NAND_CTRL_START ((u32)0x1<<31) +#define NAND_CTRL_START_MASK ((u32)0x1<<31) + + +/**/ +#define NAND_CTRL_SPEED_LO (NAND_CTRL_W8MHZ | NAND_CTRL_R8MHZ) +#define NAND_CTRL_SPEED_MIDDLE (NAND_CTRL_W11MHZ | NAND_CTRL_R11MHZ) +#define NAND_CTRL_SPEED_HI (NAND_CTRL_W16MHZ | NAND_CTRL_R11MHZ) + + +/*ステータスレジスタの詳細値*/ +#define NAND_STAT_FAIL (1) //NAND_STAT_READY状態で有効 +#define NAND_STAT_READY (1<<6) //60nm品対応 +#define NAND_STAT_NOPROTECT (1<<7) + + +/*ID_Hレジスタに入れる値の詳細値*/ +#define NAND_ID_H_FIFTH_MASK (0x8000) +#endif + +#endif /*__NAND_IP_REG_H__*/ diff --git a/trunk/bootrom/build/libraries/nand/ARM9/Makefile b/trunk/bootrom/build/libraries/nand/ARM9/Makefile new file mode 100644 index 0000000..be1a642 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/ARM9/Makefile @@ -0,0 +1,55 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: CtrBrom - libraries_sp - mi +# File: Makefile +# +# Copyright 2008-2009 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: $ +# $Rev$ +# $Author$ +#---------------------------------------------------------------------------- + +SUBDIRS = +#SUBMAKES = Makefile.CALLTRACE \ +# Makefile.FUNCTIONCOST + +#---------------------------------------------------------------------------- + +# build ARM & THUMB libraries +BROM_CODEGEN_ALL ?= TRUE + +# Codegen for sub processer +BROM_PROC = ARM9 + +SRCDIR = . ../common + +SRCS = \ + mi_memory.c \ + mi_exclusive.c \ + +TARGET_LIB = libmi_sp$(BROM_LIBSUFFIX).a + + +#---------------------------------------------------------------------------- + +include $(CTRBROM_ROOT)/build/buildtools/commondefs + +INSTALL_TARGETS = $(TARGETS) +INSTALL_DIR = $(BROM_INSTALL_LIBDIR) + + +#---------------------------------------------------------------------------- + +do-build: $(TARGETS) + +include $(CTRBROM_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/trunk/bootrom/build/libraries/nand/Makefile b/trunk/bootrom/build/libraries/nand/Makefile new file mode 100644 index 0000000..4c959d5 --- /dev/null +++ b/trunk/bootrom/build/libraries/nand/Makefile @@ -0,0 +1,34 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: CtrBrom - libraries - mi +# File: Makefile +# +# Copyright 2008 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: $ +# $Rev$ +# $Author$ +#---------------------------------------------------------------------------- + +include $(CTRBROM_ROOT)/build/buildtools/commondefs + +#---------------------------------------------------------------------------- + +SUBDIRS = ARM11 + +#ifdef CTR_WITH_ARM9 +SUBDIRS += ARM9 +#endif + +#---------------------------------------------------------------------------- + +include $(CTRBROM_ROOT)/build/buildtools/modulerules + + +#===== End of Makefile ===== diff --git a/trunk/tools/bootrom/ne1tb/nand_deviceformat.axf b/trunk/tools/bootrom/ne1tb/nand_deviceformat.axf new file mode 100644 index 0000000000000000000000000000000000000000..f09e52afce2a34c3506af17f0e2a3b6644502f76 GIT binary patch literal 211004 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 U7SL{nlO68P&e`5=AzQ%rUwSK_=l}o! literal 0 HcmV?d00001 diff --git a/trunk/tools/bootrom/ne1tb/readme.txt b/trunk/tools/bootrom/ne1tb/readme.txt index 986b9f2..856f1f0 100644 --- a/trunk/tools/bootrom/ne1tb/readme.txt +++ b/trunk/tools/bootrom/ne1tb/readme.txt @@ -10,3 +10,6 @@ NE1 ・"export CTR_PLATFORM=NE1EMU" または "make CTR_PLATFORM=NE1EMU" でビルドして下さい。 ・"export CTR_DEBUGGER=KMC" または "make CTR_DEBUGGER=KMC" でビルドして下さい。 + +・PARTNERを使ってNE1ボードへnand_deviceformat.axfをロードして実行すると、 + NANDがデバイスレベルでフォーマットされます(ファイルシステムのフォーマットではなく)。