www.pudn.com > SMSC USB2.0.zip > nand.c
/*============================================================================
____________________________________________________________________________
______________________________________________
SSSS M M CCCC Standard Microsystems Corporation
S MM MM SSSS C Austin Design Center
SSS M M M S C 11000 N. Mopac Expressway
S M M SSS C Stonelake Bldg. 6, Suite 500
SSSS M M S CCCC Austin, Texas 78759
SSSS ______________________________________________
____________________________________________________________________________
Copyright(C) 1999, Standard Microsystems Corporation
All Rights Reserved.
This program code listing is proprietary to SMSC and may not be copied,
distributed, or used without a license to do so. Such license may have
Limited or Restricted Rights. Please refer to the license for further
clarification.
____________________________________________________________________________
Notice: The program contained in this listing is a proprietary trade
secret of SMSC, Hauppauge, New York, and is copyrighted
under the United States Copyright Act of 1976 as an unpublished work,
pursuant to Section 104 and Section 408 of Title XVII of the United
States code. Unauthorized copying, adaption, distribution, use, or
display is prohibited by this law.
____________________________________________________________________________
Use, duplication, or disclosure by the Government is subject to
restrictions as set forth in subparagraph(c)(1)(ii) of the Rights
in Technical Data and Computer Software clause at DFARS 52.227-7013.
Contractor/Manufacturer is Standard Microsystems Corporation,
80 Arkay Drive, Hauppauge, New York, 1178-8847.
____________________________________________________________________________
____________________________________________________________________________
nand.c - nand flash fixed media mass storage class implementation
____________________________________________________________________________
comments tbd
____________________________________________________________________________
Revision History
Date Who Comment
________ ___ _____________________________________________________________
06/21/02 cds initial version - implements the methods for a raw nand-flash
fixed media lun. The NAND lun is derived from the removable
smart media lun, but should be able to co-exist with smart
media as a separate physical lun.
06/25/02 cds incorporated boot block reading/writing for device usb config
info
06/26/02 cds fixed multi-card reads/writes by adjusting when the card select
is called in the fmc xfers, and fixed the chip select to
multiply the # of chips by the max zone for the particular type
of chip detected, instead of assuming that it was 8 zones per chip.
06/27/02 cds - implemented nand-specific set_read/write/erase addr functions
that can be called vcalled from the smart media base class. these
functions calculate the chip-based zone from the global g_addr_zone.
- fixed select_chip equation to correctly select a chip from g_addr_zone
- added new variables g_nand_num_chips and g_nand_max_zones_per_chip
to allow future chip auto-detection.
07/02/02 cds added auto-detection for 1-chip (no or-gate)/n-chip configurations.
After detecting # of chips, detects flash configuration from
smart media card-select code.
07/08/02 cds updated nand_dfa_write to include write-protect updates that
affected smart media.
07/09/02 cds - renamed WorkBuf to g_sector_buffer
- during read_begin_split, waited until after busy before
enabling ecc detection
07/10/02 cds updated nand_read_begin_split() to use mcu_register_wr(...)
instead of mcu_register_set_bits(...) on the smc_stat register
to clear the ecc status bits.
07/19/02 cds - protected password-specific code with #ifdef k_opt_password
- added definition of g_ix_media_nand, instead of using an array
to swap media instances. (similar to sd/mmc lun dynamic subclassing)
07/19/02 baw Added initialization of lockable bit in nand_initialize_controller
07/23/02 baw moved initialization of removable media from nand_initialize_controller
to dev_validate_nvstore
07/29/02 cds - added nand_select_media() to switch between nand_media and n2k_media
objects based on what was detected
- updated nand_identify_media_format() to select appropriate media class
before setting any media instance variables
- updated card id 0xf1 with Toshiba parameters. (this will have to
be updated again to determine the capacity of the chips)
- updated 2k map parameters to be a single zone of 1024 blocks.
- updated nand_read_begin_xfer to only do single block read xfers,
to prevent sequential reads from freezing the fmc_xfer (to be debugged)
- updated nand_read_begin_split to begin reads at the appropriate
column offset for 2k page sized chips.
- added entire new dfa_write to handle fmc xfers to 2k paged media
- removed bogus init_unit()
07/31/02 cds called media_set_active(...) instead of setting g_active_media
to select a new media object.
08/06/02 cds - fixed a bug in the n2k_write_begin_xfer() by doing a chip select
before calling the copy_head function.
- added extra smarts to distinguish between Toshiba & Samsung
2k page nand chips.
08/09/02 cds - used attribute flag to determine whether to force Smart Media
compatible rw cycle timing for a data xfer.
08/13/02 cds - identify Samsung vs Toshiba chips.
- identify & setup 2 Mbit chips
- removed some debug code which erased cards on boot-up... whoops
08/20/02 cds added functions to test read cache functions of 2k page media types
08/21/02 cds altered nand select chip to use detect the mcu and pick correct gpiob scheme
08/27/02 cds - fixed a glitch where 2k page nands were not correctly initializing
the read cycle speed bit.
- added code to report media change whenever a test-unit-ready or
write command comes down from the host and the write-protect bit
has changed. This forces the host to detect the new state of the bit
- added attribute bit to determine how the device should enumerate.
if set, always enumerate as a removable-media type drive
if clear & write protected, enumerate as removable media drive
if clear & not write protected, enumerate as normal fixed media drive
09/04/02 cds - address name & usage change-over
g_addr_sector => g_addr_page,
g_addr_page => g_addr_segment,
_media_data(sectors_per_block) => _media_data(pages_per_block)
_media_data(cis_phy_block) => _media_data(boot_block)
_media_data(cis_sector) => _media_data(boot_sector)
(new item) => _media_data(segments_per_page)
- nand_select_card moved to sm.c to allow sm to switch between
nand cards and smart media slot when necessary
09/05/02 cds - n2k read & writes working again (but code bloated up to 50K)
- added password check to determine if lun reports itself as removable or fixed
before the inquiry data.
09/10/02 cds combined duplicated calculation code to one spot to save code space
09/12/02 tbh replaced strncpy() with memcpy() to save data space.
09/19/02 cds - unified n2k & nand fmc write code
- set location to enable media optimization options (erase caching... currently off)
fixed nand corruption (same place as sm corruption)
09/22/02 cds - added idle_process hook to flush cache on an idle message,
- added call to flush cache on nand_dfa_read, and on idle processing
- added flag to enable/disable whether write-caching (independently of erase-caching)
- enabled erase caching by default for nand devices
- defined buffer for use by the erase cache code for this lun
- #if'd out calls to write-caching functions to save code space
10/01/02 cds - surrounded nand_erase_block() with critical section to avoid
an intervening isr from causing a stack overrun.
10/11/02 cds - fixed check_wp() to only look at write-protect bit when determining if the wp has changed.
(this fixes a bug in WinME/Win98 that was failed to unlock the media after validating a password)
10/15/02 cds - added new code for Samsung 2GB chips so that they are properly identified.
10/17/02 cds - project-wide lun data & vtbl paging to reduce code space.
- removed g_active_media from _lun_data, _lun_() virtual functions
- added _lun_data_rd() and _lun_data_wr() macros to bypass lun paging
- added lun_set_active(log_lun) function to switch luns
10/29/02 cds - checked data status bits to see if data is good on all lbas before starting a split
11/13/02 ds intialized g_addr_segment to zero in order to select the right chip for interleave demo.
11/19/02 ds fixed the corner case with reads crossing block boundaries.
11/19/02 cds created polling loop in read_begin_split to quickly start fmc_xfers w/o fmc loop overhead for interleaving
created polling loop in write_begin_split to quickly start fmc_xfers w/o fmc loop overhead for interleaving
11/20/02 ds Fixed a bug in read_begin_split where the wrong chip was selected on a zone change.
11/20/02 ds Fixed a minor bug in read_begin_split where it does not return immediately on a abort conditions.
11/20/02 ds Ensured proper chip select for nand_int.
Enabled interrupts around data xfer loop.
============================================================================*/
#define __nand_dot_c__
#include "project.h"
#include "dev.h"
// provide vtable definition 'nand'
code _vtbl_defn(nand) ;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
xdata uint8 g_ix_media_nand ;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static xdata uint8 _nand_media_status ;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void n2k_test_read_sequential() reentrant;
//-----------------------------------------------------------------------------
// smart media 'media layer" routines
//-----------------------------------------------------------------------------
t_result sm_media_check_format(void)reentrant;
t_result sm_media_seek_cis(void) reentrant;
t_result sm_read_cis_sect(uint8 *)reentrant;
t_result sm_validate_cis(uint8 *) reentrant ;
t_result sm_media_erase_block(void)reentrant;
t_result sm_media_erase_card(void)reentrant;
t_result sm_wait_rdy_with_timeout(uint16 ) reentrant;
//-----------------------------------------------------------------------------
// read fmc xfer callbacks
//-----------------------------------------------------------------------------
t_result sm_read_begin_xfer(void) reentrant;
t_result sm_read_end_xfer(void) reentrant ;
t_result sm_read_end_split(void) reentrant ;
t_result sm_read_end_burst(void) reentrant ;
//-----------------------------------------------------------------------------
// write fmc xfer callbacks
//-----------------------------------------------------------------------------
t_result sm_write_begin_xfer(void) reentrant;
t_result sm_write_end_xfer(void) reentrant ;
t_result sm_write_begin_split(void) reentrant;
t_result sm_write_begin_first_split() reentrant ;
t_result sm_write_end_split(void) reentrant ;
t_result sm_write_begin_burst(void) reentrant;
t_result sm_write_end_burst(void) reentrant ;
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
void util_trace_media() reentrant;
void util_trace_map_media() reentrant;
void util_trace_phy_block() reentrant;
void util_trace_log_media() reentrant;
void util_safe_erase_media() reentrant;
void util_test_hardware_ecc() reentrant;
void util_test_copy_double_bit_ecc(uint32 lba) reentrant;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
xdata uint16 _nand_log2phy[k_nand_max_zones_per_table*k_nand_max_lb_per_zone] ;
xdata uint8 _nand_assign_map[k_nand_max_zones_per_table*k_nand_max_pb_per_zone/8] ;
xdata uint8 _nand_erase_cache_map[k_nand_max_zones_per_table*k_nand_max_pb_per_zone/8] ;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void nand_select_media(t_ix_media sel)
{
TRACE1(361, nand, 0, "selecting new nand media media class:%d", sel) ;
g_ix_media_nand = sel ;
media_set_active(g_ix_media_nand) ;
}
//+-----------------------------------------------------------------------------
// Name:
// nand_identify_media_format()
//
// Declaration:
// t_result nand_erase_all(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_identify_media_format(void) reentrant
{
uint8 id ;
uint8 id_test ;
uint8 addr_segment;
TRACE0(362, nand, 0, "nand_identify_media_format()") ;
g_addr_zone=0 ;
g_nand_zones_per_chip = 1 ;
//ds set to segment 0 to start up
g_addr_segment = 0;
nand_int_select_chip(g_addr_segment);
id = sm_read_id() ;
// check to see if this is a one-chip, which no chip-select hanging off gpiob
// if we disable all chips, then issue the command on a one-chip board, and
// match the first id, either we see none, or one.
// read-id command,
//#ifndef k_interleave_demo
if (_mcu_register_rd(x_chip_id) == 0x18)
{
//this has to change depending on chip!! for 242 $$$
_mcu_register_wr(x_gpiob_out, 0x00) ;
}
else
{
//this has to change depending on chip!! for 211/210 $$$
_mcu_register_wr(x_gpiob_out, 0xff) ;
}
//#endif
id_test=sm_read_id() ;
if (id == id_test)
{
TRACE0(363, nand, 0, "Single NAND Flash version detected.") ;
g_nand_num_chips=1 ;
}
else
{
// loop through cards until we don't have a match for the ID
for (g_addr_zone=1;g_addr_zone<8;g_addr_zone++)
{
nand_select_card() ;
id_test=sm_read_id() ;
TRACE2(364, nand, 0, "chip %d id: %02x", g_addr_zone, id_test) ;
if ( id_test != id )
break;
}
g_nand_num_chips=g_addr_zone ;
/* for (addr_segment=1;addr_segment<3;addr_segment++)
{
nand_int_select_chip(addr_segment);
id_test=sm_read_id() ;
TRACE2(365, nand, 0, "chip %d id: %02x", addr_segment, id_test) ;
if ( id_test != id )
break;
}
g_nand_num_chips=addr_segment;*/
}
TRACE1(366, nand, 0, "detected %d chips", g_nand_num_chips) ;
g_addr_zone=0 ;
nand_int_select_chip(0);
sm_read_id() ;
switch (nand_id_maker)
{
case 0x98:
{
TRACE0(367, nand, 0, "Toshiba NAND Flash Chip") ;
switch (nand_id_device)
{
case 0xf1:
{
nand_select_media(k_ix_media_nand_2k) ;
TRACE0(368, nand, 0, "128MB Capacity - Zones:1, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Bytes/Page:2112") ;
_media_data(options)=kbm_media_data_opt_none;
_media_data(num_zones) = 1;
_media_data(physical_blocks_per_zone) = 1024;
_media_data(logical_blocks_per_zone) = 1000;
_media_data(logical_blocks_per_boot_zone) = 1000;
_media_data(pages_per_block) = 64;
_media_data(segments_per_page) = 4 ;
_mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ;
}
break;
case 0xAA:
case 0xDA:
{
TRACE0(369, nand, 0, "256MB Capacity - Zones:2, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Bytes/Page:2112") ;
nand_select_media(k_ix_media_nand_2k) ;
_media_data(options)=kbm_media_data_opt_none;
_media_data(num_zones) = 2;
_media_data(physical_blocks_per_zone) = 1024;
_media_data(logical_blocks_per_zone) = 1000;
_media_data(logical_blocks_per_boot_zone) = 1000;
_media_data(pages_per_block) = 64;
_media_data(segments_per_page) = 4 ;
_mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ;
}
break;
default:
{
nand_select_media(k_ix_media_nand) ;
sm_identify_media_format() ;
}
break;
}
}
break;
case 0xec:
{
TRACE0(370, nand, 0, "Samsung NAND Flash Chip") ;
switch (nand_id_device)
{
case 0xf1:
{
nand_select_media(k_ix_media_nand_2k) ;
TRACE0(371, nand, 0, "128MB Capacity - Zones:1, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Seg/Page:4") ;
_media_data(options)=kbm_media_data_opt_none;
_media_data(num_zones) = 1;
_media_data(physical_blocks_per_zone) = 1024;
_media_data(logical_blocks_per_zone) = 1000;
_media_data(logical_blocks_per_boot_zone) = 1000;
_media_data(pages_per_block) = 64;
_media_data(segments_per_page) = 4;
_mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ;
break;
}
case 0xAA:
case 0xDA:
{
nand_select_media(k_ix_media_nand_2k) ;
TRACE0(372, nand, 0, "256MB Capacity - Zones:2, PB/Zone:1024, LB/Zone:1000, Pages/Blk:64 Seg/Page:4") ;
_media_data(options)=kbm_media_data_opt_none;
_media_data(num_zones) = 2;
_media_data(physical_blocks_per_zone) = 1024;
_media_data(logical_blocks_per_zone) = 1000;
_media_data(logical_blocks_per_boot_zone) = 1000;
_media_data(pages_per_block) = 64;
_media_data(segments_per_page) = 4;
_mcu_register_clr_bits(x_media_sts, kbm_media_sts_sm_256_page) ;
break;
}
default:
{
nand_select_media(k_ix_media_nand_int) ;
sm_identify_media_format() ;
break;
}
}
}
break;
default:
nand_select_media(k_ix_media_nand_int) ;
sm_identify_media_format() ;
break;
}
if (x_media_sts & kbm_media_sts_sm_wp )
_lun_data(media) |= kbm_lun_media_wrprot ;
else
_lun_data(media) &= ~kbm_lun_media_wrprot ;
// #ifdef k_interleave_demo
_media_data(segments_per_page) = g_nand_num_chips;
g_nand_num_chips = 1;
// #else
// update the # of zones, assuming all chips are the same at this point
g_nand_zones_per_chip = _media_data(num_zones) ;
_media_data(num_zones) *= g_nand_num_chips ;
// #endif
// setup the map tables (now that the media, and it's instance are known)
_media_data(log2phy_map) = (t_log2phy_map_ref) _nand_log2phy ;
_media_data(assign_map) = (t_assign_map_ref) _nand_assign_map ;
_media_data(erase_cache) = (t_erase_cache_ref) _nand_erase_cache_map;
// enable & init caching mechanisms on per-media basis here
_media_data(options) |= kbm_media_data_opt_erase_cache;
// _media_data(options) |= kbm_media_data_opt_write_cache;
TRACE1(373, nand, 0, "flash chips: %d", g_nand_num_chips) ;
TRACE1(374, nand, 0, "zones per chip: %d", g_nand_zones_per_chip) ;
TRACE1(375, nand, 0, "total zones: %d", (_media_data(num_zones))) ;
nand_cmd_reset_device();
TRACE0(376, nand, 0, "Got here") ;
return(k_success);
}
//+-----------------------------------------------------------------------------
// Name:
// nand_check_format()
//
// Declaration:
// t_result nand_check_format(void) reentrant ;
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
// this is a function
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_check_format(void) reentrant
{
TRACE0(377, nand, 0, "nand_check_format()") ;
#if 0
TRACE0(378, nand, 0, "ERASING ALL FLASH CARDS") ;
util_safe_erase_media() ;
#endif
if ( _lun_is_media_known(g_active_lun) )
return(k_success);
// media already identified, but sector map not built yet
g_addr_rd_phy_blk=0;
g_addr_page=0;
_media_data(boot_block)=0;
_media_data(boot_page)=0;
_media_data(assign_zone)=0;
g_addr_zone=0;
// note: building a sector map could take 100s of ms
if ( k_success != map_build_sector_map())
{
TRACE0(379, nand, 0, "failed to build sector map") ;
return(k_error );
}
// induce an error bit here under cbw protection, after a sector map has been built
// util_test_copy_double_bit_ecc(33) ;
nand_cmd_reset_device() ;
TRACE0(380, nand, 0, "media identified") ;
_lun_data(media) &= ~kbm_lun_media_unknown ;
return(k_success);
}
//+-----------------------------------------------------------------------------
// Name:
// nand_cmd_check_status()
//
// Declaration:
// t_result nand_cmd_check_status(void)
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-2.0
//------------------------------------------------------------------------------
t_result nand_cmd_check_status(void) reentrant
{
uint8 status ;
_sm_set_rd_cmd(k_sm_read_status);
status = _sm_data_rd() ;
trace1(0, sm, 0, "nand_cmd_check_status(): %02x", status ) ;
if (status&kbm_sm_status_write_failed )
return(k_error );
return(k_success );
}
//+-----------------------------------------------------------------------------
// Name:
// nand_cmd_check_status()
//
// Declaration:
// t_result nand_cmd_check_status(void)
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-2.0
//------------------------------------------------------------------------------
t_result nand_cmd_check_status_new(void) reentrant
{
uint8 status ;
_sm_set_wr_cmd(k_sm_read_status);
status = _sm_data_rd() ;
trace1(0, sm, 0, "nand_cmd_check_status(): %02x", status ) ;
if (status&kbm_sm_status_write_failed )
return(k_error );
return(k_success );
}
//+-----------------------------------------------------------------------------
// Name:
// sm_media_erase_all()
//
// Declaration:
// t_result sm_media_erase_all(void) reentrant
//
// Purpose:
// erase all physical blocks on media card, then
// rebuild sector mapping table
//
// Arguments:
// none
//
// Return:
// k_success - on successful completion
//
// Notes:
// currently, not a dfa
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_cmd_reset_device(void) reentrant
{
// reset the chip
trace0(0, sm, 0, "nand_cmd_reset_device()") ;
_sm_rd_cmd_begin(k_nand_cmd_reset) ;
sm_wait_rdy_with_timeout(k_nand_reset_timeout) ;
_sm_hw_set_rd_standby();
return(k_success);
}
//+-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result nand_cmd_read_id(void) reentrant
{
TRACE0(381, nand, 0, "nand_cmd_read_id()");
_sm_hw_set_rd_cmd();
_sm_data_wr(k_sm_read_id);
_sm_hw_set_rd_addr();
_sm_data_wr(0x00);
_sm_hw_set_rd_data(0);
return(k_success);
}
//+-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result nand_cmd_read_id_ex(void) reentrant
{
TRACE0(382, nand, 0, "nand_cmd_read_id_ex()");
_sm_hw_set_rd_cmd();
_sm_data_wr(k_nand_cmd_read_id_ex);
_sm_hw_set_rd_addr();
_sm_data_wr(0x00);
_sm_hw_set_rd_data(0);
return(k_success);
}
//+-----------------------------------------------------------------------------
// Name:
// nand_erase_block()
//
// Declaration:
// t_result nand_erase_block(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_erase_block(void) reentrant
{
TRACE4(383, nand, 0, "nand_erase_block() - zone:%d wr_phy:%d log:%d page:%d", g_addr_zone, g_addr_wr_phy_blk, g_addr_log_blk, g_addr_page) ;
nand_select_card() ;
return(sm_media_erase_block());
}
//------------------------------------------------------------------------------
// read for serial-input.
//------------------------------------------------------------------------------
t_result nand_page_read_for_data(void) reentrant
{
TRACE4(384, nand, 0, "nand_page_read_for_data() - zone:%d blk:%d log_blk:%d page:%d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page) ;
_mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ;
_sm_set_rd_cmd(k_sm_read) ;
_media_set_read_addr();
_sm_set_rd_cmd(k_nand_cmd_read_for_data) ;
if (k_success != sm_wait_rdy_with_timeout(k_sm_busy_read_timeout))
{
TRACE0(385, nand, 0, "error waiting for data") ;
nand_cmd_reset_device();
return(k_error );
}
return(k_success );
}
//------------------------------------------------------------------------------
// read for copyback
//------------------------------------------------------------------------------
t_result n2k_page_read_for_copy() reentrant
{
trace4(0, n2k, 0, "n2k_page_read_for_copy() - zone:%d blk:%d log_blk:%d page:%d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page) ;
_mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ;
_sm_set_rd_cmd(k_sm_read) ;
_media_set_read_addr();
_sm_set_rd_cmd(k_nand_cmd_read_for_copy) ;
if (k_success != sm_wait_rdy_with_timeout(k_sm_busy_read_timeout))
{
trace0(0, n2k, 0, "error waiting for data") ;
nand_cmd_reset_device();
return(k_error );
}
// g_addr_rd_phy_blk is now in the chip's page cache register
return(k_success );
}
//------------------------------------------------------------------------------
// jump to offset within page in chip cache for data source... page_rd_for_data
// must have previously been called.
//------------------------------------------------------------------------------
t_result n2k_page_set_rd_offset( uint16 offset ) reentrant
{
trace1(0, n2k, 0, "n2k_page_set_rd_offset() - column:%04X", offset) ;
_sm_set_rd_cmd(k_nand_cmd_read_offset_addr) ;
_sm_hw_set_rd_addr();
_sm_data_wr( _l(offset) ) ;
_sm_data_wr( _h(offset) ) ;
_sm_set_rd_cmd(k_nand_cmd_read_offset_data) ;
_sm_hw_set_rd_data(g_nand_rw_speed);
return(k_success );
}
//------------------------------------------------------------------------------
// jump to offset within page in chip cache for data dest
//------------------------------------------------------------------------------
t_result n2k_page_set_wr_offset( uint16 offset ) reentrant
{
trace1(0, n2k, 0, "n2k_page_set_wr_offset() - column:%02X", offset) ;
_sm_set_wr_cmd(k_nand_cmd_write_cache_data) ;
_sm_hw_set_wr_addr() ;
_sm_data_wr( _l(offset) ) ;
_sm_data_wr( _h(offset) ) ;
_sm_hw_set_wr_data(g_nand_rw_speed);
return(k_success );
}
//------------------------------------------------------------------------------
// program page with serial data input
//------------------------------------------------------------------------------
t_result nand_cmd_page_write_cache_data() reentrant
{
_sm_set_wr_cmd(k_nand_cmd_write_cache_data) ;
nand_2k_set_write_addr() ;
return(k_success );
}
//------------------------------------------------------------------------------
// program page with serial data input
//------------------------------------------------------------------------------
t_result nand_cmd_page_program() reentrant
{
trace0(0, n2k, 0, "nand_cmd_page_program()") ;
_mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ;
_sm_set_wr_cmd(k_nand_cmd_write_page) ;
if (k_success != sm_wait_rdy_with_timeout(k_nand_write_page_timeout))
{
trace0(0, n2k, 0, "error writing data") ;
nand_cmd_reset_device();
return(k_error );
}
if (k_success != nand_cmd_check_status())
{
trace0(0, n2k, 0, "error failed status check") ;
nand_cmd_reset_device() ;
return(k_error );
}
trace0(0, n2k, 0, "success") ;
return(k_success );
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result nand_cmd_cache_program() reentrant
{
trace0(0, n2k, 0, "nand_cmd_cache_program()") ;
_mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ;
_sm_set_wr_cmd(k_nand_cmd_write_cache) ;
if (k_success != sm_wait_rdy_with_timeout(k_nand_write_page_timeout))
{
trace0(0, n2k, 0, "error waiting for device busy") ;
nand_cmd_reset_device();
return(k_error );
}
trace0(0, n2k, 0, "success") ;
return(k_success );
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result n2k_read_cache_next() reentrant
{
trace0(0, n2k, 0, "n2k_read_cache_next()") ;
_mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ;
_sm_set_rd_cmd(k_nand_cmd_read_cache_next) ;
if (k_success!=sm_wait_rdy_with_timeout(k_sm_busy_read_timeout))
{
trace0(0, n2k, 0, "error waiting for data") ;
nand_cmd_reset_device();
return(k_error );
}
return(k_success );
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result n2k_read_cache_final() reentrant
{
trace0(0, n2k, 0, "n2k_read_cache_final()") ;
_mcu_register_wr(x_smc_stat, kbm_smc_stat_rdy) ;
_sm_set_rd_cmd(k_nand_cmd_read_cache_final) ;
if (k_success!=sm_wait_rdy_with_timeout(k_sm_busy_read_timeout))
{
trace0(0, n2k, 0, "error waiting for data") ;
nand_cmd_reset_device();
return(k_error );
}
return(k_success );
}
//-----------------------------------------------------------------------------
// read fmc xfer callbacks
//-----------------------------------------------------------------------------
extern t_result nand_read_begin_split(void) reentrant;
extern t_result nand_read_end_first_split(void) reentrant ;
//+-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result nand_read_begin_xfer() reentrant
{
t_result result;
TRACE4(386, nand, 10, "nand_read_begin_xfer() - start:0x%04X%04X count:0x%04X%04X" , _hw(_fmc_get_start_lb_32()), _lw(_fmc_get_start_lb_32()), _hw(_fmc_get_lb_count_32()), _lw(_fmc_get_lb_count_32()));
_lun_data(sensep) = &sense_read_error;
if (g_ix_media_nand == k_ix_media_nand)
{
return(sm_read_begin_xfer() );
}
//nand interleaved
if (g_ix_media_nand == k_ix_media_nand_int)
{
result = sm_read_begin_xfer() ;
if (!g_addr_page)
{
_lun_data(max_lb_per_split) = _min((_media_data(segments_per_page)*_media_data(pages_per_block)), _fmc_get_lb_count_32());
trace5(0, sm, 10, "-----read begin xfer - zone:%02d phy:0x%04X log:0x%04X page:%02d count:%02d - first in block", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split) );
}
else
{
_lun_data(max_lb_per_split) = _min((((_media_data(pages_per_block) - g_addr_page)*_media_data(segments_per_page)) - g_addr_segment), _fmc_get_lb_count_32());
trace5(0, sm, 10, "-----read begin xfer - zone:%02d phy:0x%04X log:0x%04X page:%02d count:%02d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split));
}
_lun_data(max_lb_per_burst) = 1 ; //just one burst at a time
return(result);
}
if (k_success != map_lba2addr_rd( _fmc_get_start_lb_32() ) )
{
_lun_data(sensep)=&sense_illegal_address ;
trace0(0, sm, 0, "error in lba2addr") ;
return(k_error );
}
_lun_data(max_lb_per_split) = 4-g_addr_segment ;
_lun_data(max_lb_per_burst) = 4-g_addr_segment ;
return(k_success );
}
//+-----------------------------------------------------------------------------
// Name:
// sm_read_begin_next_split
//
// Declaration:
// t_result sm_read_begin_next_split(void);
//
// Purpose:
// Issue the read command to subsequent splits after the first.
//
// Arguments:
// None.
//
// Return:
// A t_result indicating:
// k_success - command completed.
//
// Notes:
// This is a FUNCTION, not a DFA.
// Do not yeild, or run a DFA, from within this callback.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_read_begin_split(void) reentrant
{
t_result result ;
t_sync sync;
uint8 addr_segment = g_addr_segment;
uint8 cnt;
TRACE0(387, nand, 1, "nand_read_begin_split()") ;
if (k_success != map_lba2addr_rd( _fmc_get_start_lb_32() ) )
{
_lun_data(sensep)=&sense_illegal_address ;
return(k_error );
}
addr_segment = g_addr_segment;
// start the read
result=k_success;
nand_int_select_chip(addr_segment);
if (map_log_blk_has_bad_data())
{
if (sm_check_data_status(g_addr_page, (uint8) _fmc_get_lb_count_32()))
{
trace0(0, sm, 0, "read error detected in lba range") ;
nand_cmd_reset_device();
return(k_error);
}
nand_cmd_reset_device();
trace0(0, sm, 0, "read in log blk that has bad data is OK");
}
TRACE5(388, nand, 0, "beginning split - zone:%d phy:%d log:%d page:%d segment:%d", g_addr_zone, g_addr_rd_phy_blk, g_addr_log_blk, g_addr_page, g_addr_segment) ;
nand_rd_va2pa() ;
if (g_ix_media_nand == k_ix_media_nand_2k)
{
TRACE0(389, nand, 0, "using large nand flash chips") ;
// g_fmc_begin_burst_callback=(t_fmc_callback) nand_read_begin_burst ;
result = nand_page_read_for_data() ;
if (g_addr_segment)
n2k_page_set_rd_offset( _n2k_data_offset(g_addr_segment)) ;
// return k_success;
}
else
{
// issue the read commands to all chips to be used (could be < number in set)
cnt = _min(_lun_data(max_lb_per_split), _media_data(segments_per_page));
while (cnt)
{
nand_int_select_chip(addr_segment);
_sm_rd_cmd_begin(k_sm_read) ;
_media_set_read_addr() ;
// update counters here, while device is "busy"
--cnt;
++addr_segment;
if (addr_segment >= _media_data(segments_per_page))
{
addr_segment=0;
nand_incr_addr();
}
// now wait for rdy... should be done by now... only takes a few usec
result = sm_wait_rdy_with_timeout(k_sm_busy_read_timeout);
if (k_success != result)
{
TRACE1(390, nand, 0, "timeout waiting for busy - smc_stat:%02x", x_smc_stat) ;
nand_cmd_reset_device();
return(k_error );
}
}
// do single lba bursting here in w/ a poller for use by the interleaved read/write code
cnt=(uint8) _fmc_get_lb_count_32();
addr_segment=g_addr_segment;
// enable ecc hardware
_mcu_register_wr(x_smc_stat, (kbm_smc_stat_ecc_err_d_a|kbm_smc_stat_ecc_err_d_b|kbm_smc_stat_ecc_err_c_a|kbm_smc_stat_ecc_err_c_b));
_mcu_register_clr_bits(x_smc_stat_msk, (kbm_smc_stat_ecc_err_d_a|kbm_smc_stat_ecc_err_d_b|kbm_smc_stat_ecc_err_c_a|kbm_smc_stat_ecc_err_c_b));
_mcu_register_clr_bits(x_imr0, kbm_isr0_fmc_irq);
_mcu_register_set_bits(sm_mode_ctl, kbm_sm_mode_ecc_blk_xfer_en) ;
// clear blk_xfer_complete irq.
_mcu_register_wr(x_isr0, kbm_isr0_blk_xfer_complete);
// write the count registers which won't change
_mcu_register_wr(x_fmc_cnt3, 0);
_mcu_register_wr(x_fmc_cnt2, 0);
_mcu_register_wr(x_fmc_cnt0, 0);
// prime the loop to "resume" transfer
result = k_success;
/////////////////////////////////////////////////
while (cnt)
{
nand_int_select_chip(addr_segment);
// set cnt1 to 0x02 to get a 512 byte xfer
_mcu_register_wr(x_fmc_cnt1, 0x02);
// enable blk xfer
_mcu_register_set_bits(x_fmc_ctl, kbm_fmc_ctl_blk_xfer_en);
// do loop calculations
cnt--;
addr_segment=(addr_segment+1)%_media_data(segments_per_page);
thread_set_timer(g_fmc_timeout);
do
{
sync = thread_got_sync(kbm_sync_abort |kbm_sync_usbrst |kbm_sync_timer|kbm_sync_fmc_irq);
_thread_clr_sync(sync);
if (sync & kbm_sync_usbrst)
{
trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: kbm_sync_usbrst");
result = k_usbreset;
break;
}
if (sync & kbm_sync_abort)
{
trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: kbm_sync_abort");
result = k_aborted;
break;
}
if (sync & kbm_sync_timer)
{
trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: timeout awaiting irq");
result = k_timeout;
break;
}
if (sync & kbm_sync_fmc_irq)
{
trace0(0, sm, 1, "optimized fmc_wait_blk_irq_with_timeout() - error: kbm_sync_fmc_irq") ;
// set to success, since we don't know the cause of the error yet... the specific end-burst
// function should determine the actual status fot eh fmc xfer
result = k_success;
break;
}
// AAA: added this to poll instead of taking the interrupt
// cds: added isr bit fmc_irq to break out of loop for fmc error (ecc or otherwise) detection
} while (!(_mcu_register_rd(x_isr0) & (kbm_isr0_blk_xfer_complete|kbm_isr0_fmc_irq) ));
if (k_success!=result)
{
trace0(0, sm, 1, "optimized read begin split detected error... aborting") ;
return(result);
}
if (_mcu_register_rd(x_smc_stat)&(kbm_smc_stat_ecc_err_c_a|kbm_smc_stat_ecc_err_c_b|kbm_smc_stat_ecc_err_d_a|kbm_smc_stat_ecc_err_d_b))
{
result=sm_read_end_burst();
if (k_resume==result)
{
result=k_success;
}
if (k_success!=result)
{
trace0(0, sm, 1, "optimized read begin split detected error... aborting") ;
break;
}
}
}
// hack fmc registers to prevent fmc split loop from being entered upon our callback
g_start_lb_this_xfer.u32 += g_n_lb_this_split.u32;
// decrement the number of logical blocks remaining in the transfer
g_n_lb_this_xfer.u32 -= g_n_lb_this_split.u32;
// track the mscbot byte residue
_mscbot_decr_residue( (g_n_lb_this_split.u32*512L) );
g_n_lb_this_split.u32 = 0;
}
return(result);
}
#if 0
//------------------------------------------------------------------------------
// only for n2k media, that doesn't have read-sequential capabilities.
//------------------------------------------------------------------------------
t_result nand_read_begin_burst() reentrant
{
if ( g_addr_page <= (_media_data(pages_per_block)-2))
{
TRACE0(391, nand, 0, "issue read next cached page here") ;
n2k_read_cache_next() ;
}
else if ( g_addr_page == (_media_data(pages_per_block)-2))
{
TRACE0(392, nand, 0, "issue read final cached page here") ;
n2k_read_cache_final() ;
}
_lun_data(max_lb_per_burst) = 4 ;
// setup hardware
_mcu_register_wr(x_smc_stat, kbm_smc_stat_ecc_err_d_a | kbm_smc_stat_ecc_err_d_b | kbm_smc_stat_ecc_err_c_a | kbm_smc_stat_ecc_err_c_b) ;
_mcu_register_clr_bits(x_smc_stat_msk, kbm_smc_stat_ecc_err_d_a | kbm_smc_stat_ecc_err_d_b | kbm_smc_stat_ecc_err_c_a | kbm_smc_stat_ecc_err_c_b) ;
_mcu_register_wr(x_isr0, kbm_isr0_fmc_irq) ;
_mcu_register_clr_bits(x_imr0, kbm_isr0_fmc_irq) ;
_mcu_register_set_bits(sm_mode_ctl,kbm_sm_mode_ecc_blk_xfer_en);
return(k_success );
}
#endif
//+-----------------------------------------------------------------------------
// Name:
// nand_read_end_first_split
//
// Declaration:
// t_result nand_read_end_first_split(void);
//
// Purpose:
//
//
// Arguments:
// None.
//
// Return:
// A t_result indicating:
// k_success - command completed.
//
// Notes:
// This is a FUNCTION, not a DFA.
// Do not yeild, or run a DFA, from within this callback.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_read_end_first_split(void) reentrant
{
TRACE0(393, nand, 0, "nand_read_end_first_split()") ;
// go into overdrive
TRACE2(394, nand, 0, "adjusting split/burst size. before: max_lb_per_split:%d max_lb_per_burst: %d", _lun_data(max_lb_per_split), _lun_data(max_lb_per_burst)) ;
if (g_active_media==k_ix_media_nand)
{
_lun_data(max_lb_per_split) = _min( (_media_data(pages_per_block)*_media_data(segments_per_page)), g_n_lb_this_xfer.u32);
_lun_data(max_lb_per_burst) = _lun_data(max_lb_per_split) ;
}
else if (g_active_media==k_ix_media_nand_int)
{
_lun_data(max_lb_per_split) = _min( (_media_data(pages_per_block)*_media_data(segments_per_page)), g_n_lb_this_xfer.u32);
_lun_data(max_lb_per_burst) = 1 ;
}
else
{
_lun_data(max_lb_per_split) = _min( 4, g_n_lb_this_xfer.u32);
_lun_data(max_lb_per_burst) = _min( 4, g_n_lb_this_xfer.u32);
}
TRACE2(395, nand, 0, "adjusting split/burst size. after: max_lb_per_split:%d max_lb_per_burst: %d", _lun_data(max_lb_per_split), _lun_data(max_lb_per_burst)) ;
// no need to callback here any more
g_fmc_end_split_callback = (t_fmc_callback) sm_read_end_split ;
_sm_hw_set_rd_standby() ;
return(k_success );
}
//+-----------------------------------------------------------------------------
// Name:
// dfa_sm_read
//
// Declaration:
// void dfa_sm_read(void) reentrant
//
// Purpose:
// Read data from sm media
//
// Arguments:
// None.
// Uses _lun_data and g_bot_cbw to get its parameters.
//
// Return:
// No return value.
// However, on exit the DFA's argument pointer is written with a t_csw_status indicating:
// k_command_passed - command completed.
// k_command_failed - an error occurred.
//
// Notes:
// This is the start STATE of the dfa_ata_read DFA.
// It overrides dfa_lun_read.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_dfa_read(void) reentrant
{
TRACE0(396, nand, 0, "nand_dfa_read()");
_fmc_set_options(0);
_fmc_set_timeout(10000);
if ( g_ix_media_nand == k_ix_media_nand_int)
{
_fmc_set_callback(nand_read_begin_xfer, sm_read_end_xfer,
nand_read_begin_split, nand_read_end_first_split,
fmc_dflt_callback, fmc_dflt_callback, nand_int_read_end_burst);
}
else
{
_fmc_set_callback(nand_read_begin_xfer, sm_read_end_xfer,
nand_read_begin_split, nand_read_end_first_split,
fmc_dflt_callback, fmc_dflt_callback, sm_read_end_burst);
}
#ifdef k_enable_write_caching
// flush write cache
if (_media_data(options)&kbm_media_data_opt_write_cache)
map_write_cache_flush_all() ;
#endif
dfa_lun_read();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//+-----------------------------------------------------------------------------
// Name:
// nand_write_begin_first_split
//
// Declaration:
// t_result nand_write_begin_first_split(void);
//
// Purpose:
// Issue the write command to the smart media device.
//
// Arguments:
// None.
//
// Return:
// A t_result indicating:
// k_success - command completed.
//
// Notes:
// This is a FUNCTION, not a DFA.
// Do not yeild, or run a DFA, from within this callback.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_write_begin_first_split() reentrant
{
TRACE0(397, nand, 1, "nand_write_begin_first_split()");
// compute phy addresses in case this is the first sector
if (k_ix_media_nand_int != g_active_media)
nand_select_card() ;
nand_wr_va2pa() ;
nand_rd_va2pa() ;
if (k_ix_media_nand==g_active_media)
{
g_fmc_begin_split_callback = (t_fmc_callback) sm_write_begin_split;
_sm_rd_cmd_begin(k_sm_reset_chip);
sm_wait_rdy_with_timeout(k_sm_busy_reset_timeout);
}
else if (k_ix_media_nand_int == g_active_media)
{
g_fmc_begin_split_callback = (t_fmc_callback) nand_int_write_begin_quick_split;
nand_int_select_chip(_media_data(segments_per_page)-1);
_sm_rd_cmd_begin(k_sm_reset_chip);
sm_wait_rdy_with_timeout(k_sm_busy_reset_timeout);
return(nand_int_write_begin_quick_split());
trace2(0, nand_int, 0, "beginning split at page:%d segment:%d", g_addr_page, g_addr_segment) ;
}
else
{
g_fmc_begin_split_callback = (t_fmc_callback) n2k_write_begin_split;
trace2(0, n2k, 0, "beginning split at page:%d segment:%d", g_addr_page, g_addr_segment) ;
// check to see if we are starting in middle of a page
if (g_addr_segment)
{
// pre-read the old page into chip cache
n2k_page_read_for_copy() ;
// write the full destination address
nand_cmd_page_write_cache_data() ;
n2k_page_set_wr_offset( _n2k_data_offset( g_addr_segment ) ) ;
}
return(k_success);
}
return(k_success);
}
//+-----------------------------------------------------------------------------
// Name:
// n2k_write_begin_xfer
//
// Declaration:
// void n2k_write_begin_xfer(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result n2k_write_begin_xfer() reentrant
{
t_result result ;
uint16 max_split_size ;
trace4(0, sm, 10, "n2k_write_begin_xfer() - start:0x%04X%04X count:0x%04X%04X" , _hw(_fmc_get_start_lb_32()), _lw(_fmc_get_start_lb_32()), _hw(_fmc_get_lb_count_32()), _lw(_fmc_get_lb_count_32()));
_lun_data(sensep) = &sense_write_error;
// get virtual addr
map_lba2addr_rd(_fmc_get_start_lb_32()) ;
// enable the correct card
nand_select_card() ;
// copy head
result = map_write_begin( _fmc_get_start_lb_32() ) ;
// make sure we're in the right mode - fixes a bug where
// the first burst of the first split of the xfer begins
// on a zone that needed to be paged in...
sm_set_write_mode_page_data();
// precompute max split size
max_split_size = (_media_data(pages_per_block)-g_addr_page)*_media_data(segments_per_page)-g_addr_segment ;
if (!g_addr_page)
{
_lun_data(max_lb_per_split) = _min( max_split_size, _fmc_get_lb_count_32());
trace5(0, n2k, 10, "-----write begin xfer - zone:%d phy:%d log:%d page:%d count:%d - first in block", g_addr_zone, g_addr_wr_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split) );
}
else
{
_lun_data(max_lb_per_split) = _min( max_split_size, _fmc_get_lb_count_32());
trace5(0, n2k, 10, "-----write begin xfer - zone:%d phy:%d log:%d page:%d count:%d", g_addr_zone, g_addr_wr_phy_blk, g_addr_log_blk, g_addr_page, _lun_data(max_lb_per_split));
}
// burst size needs to be 1, because the sm controller can't
// handle more than 512 bytes out at a time.
_lun_data(max_lb_per_burst) = 1;
return(result );
}
//+-----------------------------------------------------------------------------
// Name:
// n2k_write_begin_split
//
// Declaration:
// t_result n2k_write_begin_split(void);
//
// Purpose:
// Issue the write command to the smart media device.
//
// Arguments:
// None.
//
// Return:
// A t_result indicating:
// k_success - command completed.
//
// Notes:
// This is a FUNCTION, not a DFA.
// Do not yeild, or run a DFA, from within this callback.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result n2k_write_begin_split() reentrant
{
uint8 zone_before ;
trace0(0, n2k, 1, "sm_write_begin_split()");
trace1(0, n2k, 0, "g_addr_rd_phy_blk: %d", g_addr_rd_phy_blk) ;
zone_before = g_addr_zone ;
// if a new physical block, tell the mapper to go git us one.
if (k_success != map_lba2addr_rd(_fmc_get_start_lb_32()))
return(k_error);
if (g_addr_zone != zone_before)
{
// we've crossed a boundry on a split, do a reset
trace0(0, n2k, 0, "zone-change detected.") ;
nand_select_card() ;
sm_set_write_mode_page_data() ;
}
if (!g_addr_page)
{
if (k_success != map_alloc_wr_blk())
return(k_error);
}
// convert virtual address zone/block/sector into page/offsets
nand_rd_va2pa() ;
nand_wr_va2pa() ;
trace0(0, sm, 0, "ready to begin writing sectors") ;
return(k_success);
}
//+-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result n2k_write_end_split(void) reentrant
{
trace0(0, n2k, 0, "n2k_write_end_split()") ;
trace2(0, n2k, 0, "split ended on to page %d segment %d", g_addr_page, g_addr_segment) ;
if ( g_addr_segment )
{
trace0(0, n2k, 0, "last split. need to flush current page cache") ;
if ( k_success != nand_cmd_page_program() )
{
trace0(0, n2k, 0, "nand_cmd_cache_program failed") ;
return(k_error );
}
g_addr_page++;
g_addr_segment=0;
nand_incr_addr() ;
}
return(sm_write_end_split() );
}
//+-----------------------------------------------------------------------------
// Name:
// sm_write_begin_burst
//
// Declaration:
// t_result sm_write_begin_burst(void);
//
// Purpose:
// Issue the write command to the smart media device.
//
// Arguments:
// None.
//
// Return:
// A t_result indicating:
// k_success - command completed.
//
// Notes:
// This is a FUNCTION, not a DFA.
// Do not yeild, or run a DFA, from within this callback.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result n2k_write_begin_burst() reentrant
{
trace5(0, n2k, 0, "n2k_write_begin_burst() - zone:%d log:%d-phy:%d page:%d seg:%d", g_addr_zone, g_addr_log_blk, g_addr_wr_phy_blk, g_addr_page, g_addr_segment) ;
trace1(0, n2k, 0, " - n_lbs_this_xfer:%d", g_n_lb_this_xfer.u32) ;
// check to see if we are starting in middle of a page
if (!g_addr_segment)
{
// pre-read the old page into chip cache
n2k_page_read_for_copy() ;
// write data into the on-chip cache, starting at the beginning of the page
nand_cmd_page_write_cache_data() ;
}
_mcu_register_set_bits(sm_mode_ctl,kbm_sm_mode_ecc_blk_xfer_en) ;
_sm_hw_ecc_wr_start() ;
trace0(0, n2k, 0, "bursting") ;
return(k_success);
}
//+-----------------------------------------------------------------------------
// Name:
// sm_write_end_burst
//
// Declaration:
// t_result sm_write_end_burst(void);
//
// Purpose:
// TBD
//
// Arguments:
// None.
//
// Return:
// A t_result indicating:
// k_success - command completed.
//
// Notes:
// This is a FUNCTION, not a DFA.
// Do not yeild, or run a DFA, from within this callback.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result n2k_write_end_burst(void) reentrant
{
trace0(0, sm, 0, "n2k_write_end_burst()") ;
_sm_hw_ecc_wr_stop() ;
sm_mode_ctl &= ~kbm_sm_mode_ecc_blk_xfer_en;
trace0(0, sm, 0, "ecc_en off") ;
g_addr_segment++ ;
if ( g_addr_segment==_media_data(segments_per_page) )
{
trace0(0, n2k, 0, "last burst in the page. program the page cache") ;
if ( k_success != nand_cmd_page_program() )
{
trace0(0, sm, 0, "nand_cmd_cache_program failed") ;
return(k_error );
}
g_addr_page++;
g_addr_segment=0;
nand_incr_addr() ;
}
return(k_success );
}
//+-----------------------------------------------------------------------------
// Name:
// dfa_sm_write
//
// Declaration:
// void dfa_sm_write(void) reentrant
//
// Purpose:
// Read data from sm media
//
// Arguments:
// None.
// Uses _lun_data and g_bot_cbw to get its parameters.
//
// Return:
// No return value.
// However, on exit the DFA's argument pointer is written with a t_csw_status indicating:
// k_command_passed - command completed.
// k_command_failed - an error occurred.
//
// Notes:
// This is the start STATE of the dfa_ata_write DFA.
// It overrides dfa_lun_write.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_dfa_write(void) reentrant
{
TRACE0(398, nand, 0, "nand_dfa_write()");
// check for write protection change
if ( nand_check_wp() )
{
TRACE0(399, nand, 0, "error: write protection state changed") ;
__thread_return_dfa(k_error) ;
}
// check for write protection state
if ( _mcu_register_rd(x_media_sts)&kbm_media_sts_sm_wp)
{
TRACE0(400, nand, 0, "error: write protection enabled") ;
_lun_data(sensep) = &sense_media_change ;
__thread_return_dfa(k_error) ;
}
_fmc_set_options(0);
_fmc_set_timeout(10000);
if (k_ix_media_nand == g_ix_media_nand)
{
TRACE0(401, nand, 0, "preparing for 512-byte per page writes") ;
_fmc_set_callback(sm_write_begin_xfer, sm_write_end_xfer,
nand_write_begin_first_split, sm_write_end_split,
sm_write_begin_burst, fmc_dflt_callback, sm_write_end_burst);
}
else if (k_ix_media_nand_int == g_ix_media_nand)
{
_fmc_set_callback(nand_int_write_begin_xfer, nand_int_write_end_xfer,
nand_write_begin_first_split, nand_int_write_end_split,
nand_int_write_begin_burst, fmc_dflt_callback, nand_int_write_end_burst);
}
else
{
trace0(0, n2k, 0, "preparing for 2048-byte-per-page writes") ;
_fmc_set_callback(n2k_write_begin_xfer, sm_write_end_xfer,
nand_write_begin_first_split, n2k_write_end_split,
n2k_write_begin_burst, fmc_dflt_callback, n2k_write_end_burst);
}
dfa_lun_write();
}
//+-----------------------------------------------------------------------------
// Name:
// sm_power_dn()
//
// Declaration:
// t_result sm_power_dn() reentrant
//
// Purpose:
// erase all physical blocks on media card, then
// rebuild sector mapping table
//
// Arguments:
// none
//
// Return:
// k_success - on successful completion
//
// Notes:
// currently, not a dfa
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_power_dn() reentrant
{
TRACE0(402, nand, 0, "nand_power_dn()") ;
_sm_hw_set_rd_standby();
return(k_success );
}
//+-----------------------------------------------------------------------------
// Name:
// sm_power_up()
//
// Declaration:
// t_result sm_power_up(void) reentrant
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
t_result nand_power_up(void) reentrant
{
TRACE0(403, nand, 0, "nand_power_up()");
return(k_success );
}
//+-----------------------------------------------------------------------------
// Name:
// sm_reset_controller()
//
// Declaration:
// void sm_reset_controller(void) reentrant
//
// Purpose:
// erase all physical blocks on media card, then
// rebuild sector mapping table
//
// Arguments:
// none
//
// Return:
//
// Notes:
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_reset_controller(void) reentrant
{
TRACE0(404, nand, 0, "nand_reset_controller()");
}
//+-----------------------------------------------------------------------------
// Name:
// dfa_sm_identify_media
//
// Declaration:
// void dfa_sm_identify_media(void);
//
// Purpose:
// determine if smart media is present the device and if so,
// determine max lba and lb size
//
// Arguments:
// none
//
// Return:
// none
//
// Notes:
// This is a DFA
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_dfa_identify_media(void) reentrant
{
TRACE0(405, nand, 0, "nand_dfa_identify_media()");
_lun_data(media) |= kbm_lun_media_unknown ;
if (k_success != nand_check_format())
{
TRACE0(406, nand, 0, "nand_check_format() returned an error!") ;
_thread_return_dfa(k_error) ;
}
_lun_data(capacity.lba_max.u32) = _media_data(num_zones) * (uint32) _media_data(logical_blocks_per_zone) * (uint32) _media_data(pages_per_block)* (uint32) _media_data(segments_per_page) ;
_lun_data(capacity.lba_sz.u32) = 512 ;
if ( x_media_sts & kbm_media_sts_sm_256_page )
_lun_data(capacity.lba_max.u32) /= 2 ;
if ( k_ix_media_nand_2k == g_active_media)
_lun_data(capacity.lba_max.u32) *= 4 ;
_lun_data(capacity.lba_max.u32) -= (uint32) 1 ;
// media is now known
_lun_data(media) &= ~kbm_lun_media_unknown;
_lun_data(sensep) = &sense_media_change;
if (x_media_sts & kbm_media_sts_sm_wp )
_lun_data(media) |= kbm_lun_media_wrprot ;
else
_lun_data(media) &= ~kbm_lun_media_wrprot ;
#if 1
// check attributes
if (g_dev_attr_lo&kbm_attr_lo_sm_timing)
{
TRACE0(407, nand, 0, "Forcing Smart Media-Compatible Cycle Timing on NAND flash") ;
g_nand_rw_speed=0;
}
else
{
TRACE0(408, nand, 0, "Enabling fast cycle time on NAND flash") ;
g_nand_rw_speed=kbm_sm_mode_fast_cycle_time;
}
#else
g_nand_rw_speed=0;
#endif
TRACE2(409, nand, 0, "nand capacity: lba_max:%04x%04x lba_sz:00000200", _hw(_lun_data(capacity.lba_max.u32)), _lw(_lun_data(capacity.lba_max.u32))) ;
thread_return_dfa(k_success) ;
}
//+-----------------------------------------------------------------------------
// Name:
// TBD
//
// Declaration:
// TBD
//
// Purpose:
// TBD
//
// Arguments:
// TBD
//
// Return:
// TBD
//
// Notes:
// This is a DFA, not a FUNCTION.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_dfa_initialize_media() reentrant
{
TRACE0(410, nand, 0, "nand_dfa_initialize_media()");
_lun_data(media) &= ~kbm_lun_media_changed;
TRACE2(411, lun, 0, "_lun_data(%d, media)=%02x", g_active_media, _lun_data(media));
_thread_return_dfa(k_success);
}
//+-----------------------------------------------------------------------------
// Name:
// TBD
//
// Declaration:
// TBD
//
// Purpose:
// TBD
//
// Arguments:
// TBD
//
// Return:
// TBD
//
// Notes:
// This is a DFA, not a FUNCTION.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_dfa_reset_media() reentrant
{
TRACE0(412, nand, 0, "nand_dfa_reset_media()");
_thread_return_dfa(k_success);
}
//+-----------------------------------------------------------------------------
// Name:
// sm_initialize_controller()
//
// Declaration:
// void sm_initialize_controller(void) reentrant
//
// Purpose:
// TBD
//
// Arguments:
// TBD
//
// Return:
// TBD
//
// Notes:
// TBD
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_initialize_controller(void) reentrant
{
uint16 i;
TRACE0(413, nand, 0, "nand_initialize_controller()") ;
// initialize _lun_data
memcpy(_lun_data(device_id), "HD", k_lun_max_devid_sz) ;
_lun_data(media) &=~ kbm_lun_media_unknown ;
g_nand_rw_speed=0 ;
#ifdef k_opt_password
_lun_data(media) |= kbm_lun_media_lockable;
#endif
// by default, it is thus. However, we must check at run-time
// whether or not to keep it such. The removable bit is first used
// checked on Inquiry processing
_lun_data(media) &=~ kbm_lun_media_removable;
_lun_data(device_type) = k_device_type_nand ;
// we have to make sure we wait at least 1 ms before accessing the device,
// but we don't have a timer running yet
i=1000 ;
while (i--) ;
// try to identify the media configuration
fmc_select_nand() ;
if ( k_success != nand_identify_media_format() )
{
TRACE0(414, nand, 0, "failed to identify media format") ;
return;
}
// $$$ intercepting
// util_trace_media() ;
// util_trace_map_media() ;
// read the boot block to see if out custom boot structure exists.
nand_boot_seek() ;
#ifdef k_enable_write_caching
// initialize the write head/tail cache
map_write_cache_init() ;
#endif
// safe erase the media here... for utilitarian purposes
// util_safe_erase_media() ;
// util_trace_map_media();
/*
for(g_addr_rd_phy_blk=0;g_addr_rd_phy_blk<8;g_addr_rd_phy_blk++)
{
util_trace_phy_block();
}
*/
// util_trace_log_media();
/*
g_addr_rd_phy_blk=0;
g_addr_page=0;
_media_data(boot_block)=0;
_media_data(boot_page)=0;
_media_data(assign_zone)=0;
g_addr_zone=0;
map_build_sector_map();
util_test_copy_double_bit_ecc(33) ;
*/
// store initial state of the wp bit.
_nand_media_status = _mcu_register_rd(x_media_sts) ;
}
//------------------------------------------------------------------------------
void nand_dfa_inquiry(void) reentrant
{
TRACE0(415, nand, 0, "nand_dfa_inquiry()") ;
// check attribute bit... will be valid by this point
if (g_dev_attr_lo&kbm_attr_lo_hd_as_rm)
{
TRACE0(416, nand, 0, "kbm_attr_lo_hd_as_rm bit set. Reporting lun as Removable") ;
_lun_data(media)|=kbm_lun_media_removable ;
}
else if (x_media_sts&kbm_media_sts_sm_wp )
{
TRACE0(417, nand, 0, "nand write protected. Reporting lun as Removable") ;
_lun_data(media)|=kbm_lun_media_removable ;
}
else if (!g_password_validated)
{
TRACE0(418, nand, 0, "Password exists, and is not validated. Make removable") ;
_lun_data(media)|=kbm_lun_media_removable;
}
else
{
TRACE0(419, nand, 0, "reporting lun as Fixed Media") ;
_lun_data(media)&=~kbm_lun_media_removable ;
}
// call base class
dfa_lun_inquiry() ;
}
//+-----------------------------------------------------------------------------
//------------------------------------------------------------------------------
t_result nand_check_wp() reentrant
{
uint8 cs ;
TRACE0(420, nand, 0, "nand_check_wp()") ;
if ( _lun_data(media)&kbm_lun_media_removable)
{
// check to see if there's been a change
cs=_mcu_register_rd(x_media_sts)&kbm_media_sts_sm_wp;
_nand_media_status&= kbm_media_sts_sm_wp;
if (_nand_media_status^cs)
{
TRACE2(421, nand, 0, "media_sts sm_wp bit change detected. _nand_media_status:%02X x_media_sts:%02X", _nand_media_status, x_media_sts) ;
_lun_data(sensep) = &sense_media_change ;
_lun_data(media) |= kbm_lun_media_unknown;
_nand_media_status=cs;
// change
return(k_true );
}
}
// no change in wp status
return(k_false );
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void nand_process_idle() reentrant
{
TRACE0(422, nand, 0, "nand_dfa_process_idle()") ;
#if 0
if ( _media_data(options)&kbm_media_data_opt_write_cache)
{
TRACE0(423, nand, 0, "flushing write cache()") ;
map_write_cache_flush_all();
}
#endif
if ( _media_data(options)&kbm_media_data_opt_erase_cache)
{
TRACE0(424, nand, 0, "flushing erase cache()") ;
map_erase_cache_flush_all();
}
_lun_data(media)&=~kbm_lun_media_process_idle;
}
//+-----------------------------------------------------------------------------
// Name:
// nand_dfa_test_unit_ready
//
// Declaration:
// void dfa_lun_test_unit_ready(void);
//
// Purpose:
//
// Arguments:
//
// Return:
//
// Notes:
// This allows us to check if the wp status has 'changed' since previous cmd.
// if so, then report a media change.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_dfa_test_unit_ready(void)reentrant
{
TRACE0(425, nand, 0, "nand_dfa_test_unit_ready()");
// if happy, check wp status. this function will update sensep
if ( _lun_data(sensep) == &sense_none)
nand_check_wp() ;
// call base-class
dfa_lun_test_unit_ready() ;
// report sense as normal.
_thread_return_dfa((_lun_data(sensep) == &sense_none) ? k_command_passed : k_command_failed);
}
//+-----------------------------------------------------------------------------
// Name:
// sm_dfa_verify()
//
// Declaration:
// void sm_dfa_verify(void);
//
// Purpose:
// Verify that the data on the drive matches the data sent down.
//
// Arguments:
// None.
// Uses _lun_data and g_bot_cbw to get its parameters.
//
// Return:
// No return value.
// However, on exit the DFA's argument pointer is written with a t_csw_status indicating:
// k_command_passed - command completed.
// k_command_failed - an error occurred.
//
// Notes:
// This is the start STATE of the dfa_ata_verify DFA.
// It overrides dfa_lun_verify.
//
// Since:
// fmc-1.0
//------------------------------------------------------------------------------
void nand_dfa_verify() reentrant
{
TRACE0(426, nand, 0, "dfa_ata_verify");
// _fmc_set_callback(sm_read_begin_xfer, sm_read_end_xfer, sm_read_begin_split, sm_read_end_split, sm_read_begin_burst, fmc_dflt_callback, sm_read_end_burst);
dfa_lun_verify();
}