iBoot/drivers/apple/h2fmi/H2fmi_erase.c

304 lines
10 KiB
C

// *****************************************************************************
//
// File: H2fmi_erase.c
//
// *****************************************************************************
//
// Notes:
//
// *****************************************************************************
//
// Copyright (C) 2008 Apple Computer, Inc. All rights reserved.
//
// This document is the property of Apple Computer, Inc.
// It is considered confidential and proprietary.
//
// This document may not be reproduced or transmitted in any form,
// in whole or in part, without the express written permission of
// Apple Computer, Inc.
//
// *****************************************************************************
#include "H2fmi_private.h"
/**
* Define the following to simulate erase status failures.
*/
//??#define SIMULATE_ERASE_FAILURES
#if defined(SIMULATE_ERASE_FAILURES)
#define ERASE_FAILURE_MAGIC_NUMBER 1301
static unsigned long ulFailureCount = 0;
#endif
// =============================================================================
// private implementation function declarations
// =============================================================================
static void h2fmi_start_nand_block_erase(h2fmi_t* fmi, UInt32 page);
static void h2fmi_config_erase_page_addr(h2fmi_t* fmi, UInt32 page);
/**
* Same as h2fmi_wait_status except also tests to ensure
* the write protect bit is off. Used for erase mode as a
* sanity check as some NAND have been known to fail with
* 'stuck' write protects.
*
* @param fmi
*
* @return CE_STATE
*/
static BOOL32 h2fmi_wait_erase_status(h2fmi_t* fmi,
UInt8 io_mask,
UInt8 io_cond,
UInt8* status)
{
UInt8 bStatus;
BOOL32 bResult = h2fmi_wait_status(fmi,io_mask,io_cond,&bStatus);
if ( bResult )
{
const UInt32 bWPDisabled = (bStatus & NAND_STATUS__WRITE_PROTECT);
WMR_ASSERT(bWPDisabled);
}
if ( NULL!=status )
{
*status = bStatus;
}
return bResult;
}
// =============================================================================
// public interface function definitions
// =============================================================================
BOOL32 h2fmi_erase_blocks(h2fmi_t* fmi,
UInt32 num_elements,
UInt16* ce,
UInt32* block,
BOOL32* status_failed)
{
enum {
st_IDLE,
st_ERASING,
st_TIMEOUT,
st_ERROR
} ceState[ H2FMI_MAX_CE_TOTAL ];
UInt8 nand_status;
UInt32 k;
UInt32 wIndex;
BOOL32 result = TRUE32;
for ( k=0; k<( sizeof(ceState)/sizeof(ceState[0]) ); k++ )
{
ceState[k] = st_IDLE;
}
// remember initial FMC_IF_CTRL setting
const UInt32 fmc_if_ctrl = h2fmi_rd(fmi, FMC_IF_CTRL);
// disable write protect
h2fmi_set_if_ctrl(fmi, FMC_IF_CTRL__WPB | fmc_if_ctrl);
fmi->failureDetails.wNumCE_Executed = fmi->failureDetails.wOverallOperationFailure = 0;
fmi->failureDetails.wFirstFailingCE = ~0;
for ( wIndex=0; wIndex<num_elements; )
{
const UInt32 wCurrentCE = ce[wIndex];
// convert block address to page address
if ( st_ERASING==ceState[wCurrentCE] )
{
/**
* Wrapped around -- must wait for this to finish before
* continuing. :(
*/
// enable specified nand device
h2fmi_fmc_enable_ce(fmi, wCurrentCE);
// Increment wIndex -- we're done with this one whether it errors out or not.
wIndex++;
// wait for the device to indicate ready
if (!h2fmi_wait_erase_status(fmi,
NAND_STATUS__DATA_CACHE_RB,
NAND_STATUS__DATA_CACHE_RB_READY,
&nand_status))
{
ceState[wCurrentCE] = st_TIMEOUT;
h2fmi_fail(result);
fmi->failureDetails.wFirstFailingCE = wCurrentCE;
/**
* Early failure -- break out of this loop as soon as we see a
* failure occur.
*/
break;
}
else
{
const UInt32 status1 = (nand_status & NAND_STATUS__CHIP_STATUS1);
ceState[wCurrentCE] = ( 0==status1 ? st_IDLE : st_ERROR );
#if defined(SIMULATE_ERASE_FAILURES)
if ( (ulFailureCount++)>=ERASE_FAILURE_MAGIC_NUMBER )
{
ulFailureCount = 0;
ceState[wCurrentCE] = st_ERROR;
WMR_PRINT(ALWAYS,"Simulated erase failure on ce %d\n",wCurrentCE);
}
#endif
if ( st_ERROR==ceState[wCurrentCE] )
{
h2fmi_fail(result);
fmi->failureDetails.wFirstFailingCE = wCurrentCE;
break;
}
}
}
else
{
/**
* Kick off as many as we can -- we know the first one will work
* given the above test (if it were in an error state we would
* have already exited the top-level for loop).
*/
for ( k=wIndex; k<num_elements; k++ )
{
const UInt32 wThisCE = ce[k];
if ( st_IDLE!=ceState[wThisCE] )
{
/**
* Break out of this loop as soon as we hit a non-idle CE.
* This will cause us to go back and wait on the most recent
* wIndex one above, as it will not be in the st_IDLE state.
*/
break;
}
else
{
const UInt32 wThisPage = block[k] * fmi->pages_per_block;
// enable specified nand device
h2fmi_fmc_enable_ce(fmi, wThisCE);
// start nand erase operation
h2fmi_start_nand_block_erase(fmi, wThisPage);
ceState[wThisCE] = st_ERASING;
fmi->failureDetails.wNumCE_Executed++;
}
}
}
}
/**
* We're out of the above loop -- for better or for worse.
* Ensure we wait until all CE's have completed.
*/
for ( k=wIndex; k<num_elements; k++ )
{
const UInt32 wThisCE = ce[k];
if ( st_ERASING==ceState[wThisCE] )
{
// enable specified nand device
h2fmi_fmc_enable_ce(fmi, wThisCE);
// wait for the device to indicate ready
if (!h2fmi_wait_erase_status(fmi,
NAND_STATUS__DATA_CACHE_RB,
NAND_STATUS__DATA_CACHE_RB_READY,
&nand_status))
{
ceState[wThisCE] = st_TIMEOUT;
h2fmi_fail(result);
if ( (UInt32)~0 == fmi->failureDetails.wFirstFailingCE )
{
fmi->failureDetails.wFirstFailingCE = wThisCE;
}
}
else
{
const UInt32 status1 = (nand_status & NAND_STATUS__CHIP_STATUS1);
ceState[wThisCE] = ( 0==status1 ? st_IDLE : st_ERROR );
#if defined(SIMULATE_ERASE_FAILURES)
if ( (ulFailureCount++)>=ERASE_FAILURE_MAGIC_NUMBER )
{
ulFailureCount = 0;
ceState[wThisCE] = st_ERROR;
WMR_PRINT(ALWAYS,"Simulated erase failure on ce %d\n",wThisCE);
}
#endif
if ( st_ERROR==ceState[wThisCE] )
{
h2fmi_fail(result);
if ( (UInt32)~0 == fmi->failureDetails.wFirstFailingCE )
{
fmi->failureDetails.wFirstFailingCE = wThisCE;
}
}
}
}
}
// return write protect to previous state
h2fmi_set_if_ctrl(fmi, fmc_if_ctrl);
// disable all nand devices
h2fmi_fmc_disable_all_ces(fmi);
// possibly return by reference whether Read Status indicated failure
if (NULL != status_failed)
{
*status_failed = ( TRUE32==result ? FALSE32 : TRUE32 );
}
return result;
}
// =============================================================================
// private implementation function definitions
// =============================================================================
static void h2fmi_start_nand_block_erase(h2fmi_t* fmi,
UInt32 page)
{
const UInt32 cmd1_addr_cmd2_done = (FMC_STATUS__ADDRESSDONE |
FMC_STATUS__CMD2DONE |
FMC_STATUS__CMD1DONE);
// configure FMC for cmd1/addr/cmd2 sequence
h2fmi_config_erase_page_addr(fmi, page);
h2fmi_wr(fmi, FMC_CMD, (FMC_CMD__CMD1(NAND_CMD__ERASE) |
FMC_CMD__CMD2(NAND_CMD__ERASE_CONFIRM)));
// start cmd1/addr/cmd2 sequence
h2fmi_wr(fmi, FMC_RW_CTRL, (FMC_RW_CTRL__ADDR_MODE |
FMC_RW_CTRL__CMD2_MODE |
FMC_RW_CTRL__CMD1_MODE));
// busy wait until cmd1/addr/cmd2 sequence completed
h2fmi_busy_wait(fmi, FMC_STATUS, cmd1_addr_cmd2_done, cmd1_addr_cmd2_done);
h2fmi_wr(fmi, FMC_STATUS, cmd1_addr_cmd2_done);
}
static void h2fmi_config_erase_page_addr(h2fmi_t* fmi,
UInt32 page)
{
const UInt32 page_addr_size = 3;
const UInt32 page_addr_2 = (page >> 16) & 0xFF;
const UInt32 page_addr_1 = (page >> 8) & 0xFF;
const UInt32 page_addr_0 = (page >> 0) & 0xFF;
h2fmi_wr(fmi, FMC_ADDR0, (FMC_ADDR0__SEQ3(0x00) |
FMC_ADDR0__SEQ2(page_addr_2) |
FMC_ADDR0__SEQ1(page_addr_1) |
FMC_ADDR0__SEQ0(page_addr_0)));
h2fmi_wr(fmi, FMC_ADDRNUM, FMC_ADDRNUM__NUM(page_addr_size - 1));
}
// ********************************** EOF **************************************