www.pudn.com > drivers.rar > drv.c


/****************************************************************************** 
 * Flash File System (ffs) 
 * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com 
 * 
 * ffs low level flash driver 
 * 
 * $Id: drv.c 1.30.1.6.1.51.1.1.1.13.1.11 Tue, 06 Jan 2004 14:36:52 +0100 tsj $ 
 * 
 ******************************************************************************/ 
  
#ifndef TARGET 
#include "ffs.cfg" 
#endif 
 
#include "ffs/ffs.h" 
#include "ffs/board/drv.h" 
#include "ffstrace.h" 
#include "ffs/board/task.h" 
 
#if (TARGET == 0) 
#include "tffs.h" 
#include "core.h" 
 
/*Add by ZhangTing for PC simulator 2006-08-26*/ 
#define WIN32 
/*End*/ 
 
#ifdef WIN32 
#include "windows.h" 
#else  // WIN32 
#include "sys/mman.h" 
#include "unistd.h" 
#endif // WIN32 
 
#include "stdio.h" 
#include  
#include "sys/types.h" 
#include "sys/stat.h" 
#include "fcntl.h" 
#endif 
 
// Note that all code notes and comments pertaining to single-bank flash 
// drivers are located in the AMD SB driver (amdsbdrv.c). Consider this as 
// the reference. 
 
/****************************************************************************** 
 * Globals 
 ******************************************************************************/ 
 
#define FLASH_STATUS_CHECK 1 
#define FLASH_WRITE_CHECK 0 
//#define FLASH_WRITE(addr, data) (*(volatile UINT16 *) (addr)) = data 
#define AMD_FLASH 
 
#if (TARGET == 1) 
// NOTE: This is the size in bytes of the single-bank driver code that is 
// copied to RAM. The only way to determine the amount of memory needed is 
// to look into the linker output file (.map) or the assembler output of 
// both amdsbdrv.obj and intelsbdrv.obj files. 
#define FFSDRV_CODE_SIZE     (0x200) 
 
uint8 ffsdrv_code[FFSDRV_CODE_SIZE]; 
#endif 
 
#define INTEL_UNLOCK_SLOW 1 
struct dev_s dev; 
struct ffsdrv_s ffsdrv; 
extern OS_MUTEX ffs_write_mutex; 
 
/****************************************************************************** 
 * Function Prototypes 
 ******************************************************************************/ 
 
uint32 int_disable(void); 
void int_enable(uint32 tmp); 
 
int ffsdrv_write_check(char *addr, int size); 
void ffsdrv_unlock_sectors(void); 
void ffsdrv_amd_fg_write_halfword_local(volatile uint16 *addr, uint16 value); 
void ffsdrv_amd_fg_write_halfword_unlock_bypass(volatile uint16 *addr, uint16 value); 
void ffsdrv_amd_mb_buffer_write(void *dst, const void *src, uint16 size); 
void ffsdrv_amd_mb_erase_sector(void *dst); 
void ffsdrv_amd_mb_buffer_write_new(volatile UINT16 *dst_addr, const UINT8 *src_addr, UINT16 size); 
/*Add by ZhangTing for PC simulator 2006-08-26*/ 
void ffsdrv_amd_fg_erase(uint8 block); 
void ffsdrv_null_write_buffer(volatile uint16 *addr, volatile uint16 *src, uint16 words2write); 
void ffsdrv_amd_fg_write(void *dst, const void *src, uint16 size); 
void ffsdrv_amd_fg_write_suspend(void); 
void ffsdrv_amd_fg_write_halfword(volatile uint16 *addr, uint16 value); 
void ffsdrv_amd_fg_erase_sector(void *dst); 
void ffsdrv_amd_fg_erase_sector_pseudo_sb(void *dst); 
void ffsdrv_amd_mb_erase(uint8 block); 
void ffsdrv_amd_mb_write_halfword(volatile uint16 *addr, uint16 value); 
/*End*/ 
/****************************************************************************** 
 * Macros 
 ******************************************************************************/ 
 
#define addr2offset(address) ( (int) (address) - (int) dev.base ) 
#define blockoffsetaddr(address)	((int)address&(int)(dev.blocksize-1))	//fangcj added 
 
#define FLASH_READ(addr)        (*(volatile UINT16 *) (addr)) 
#define FLASH_WRITE(addr, data) (*(volatile UINT16 *) (addr)) = data 
 
 
/****************************************************************************** 
 * Generic Driver Functions 
 ******************************************************************************/ 
 
void ffsdrv_generic_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
 
    if (size > 0) 
    { 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
        while (size >= 2) { 
            ffsdrv.write_halfword((uint16 *) mydst, 
                                  mysrc[0] | (mysrc[1] << 8)); 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
} 
 
 
void ffsdrv_generic_buffer_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
    uint16 bytes2write; 
 
    tw(tr(TR_BEGIN, TrDrvWrite, "test_generic_buffer_write(0x%05x, 0x%05x, 0x%x)" 
          "{\n", addr2offset(dst), src, size)); 
 
    while (size > 0) { 
        // If buffer is aligned set it to max else align to buffer boundary. 
        bytes2write = dev.write_buffersize - 
            ((int) mydst & (dev.write_buffersize - 1)); 
 
        // Saturate to size 
        if (bytes2write > size) 
            bytes2write = size; 
 
        if ((unsigned int) mydst & 1 || bytes2write == 1) { 
            bytes2write = 1; 
            ffsdrv_write_byte(mydst, *mysrc); 
        } 
        // The amount: '3 bytes2write' is a special case as if it not is 
        // written by the write_halfword() the buffer_write() will write 2 
        // bytes and the write_byte() will write the last byte.. (less 
        // efficient) 
        else if (bytes2write == 2 || bytes2write == 3) { 
            bytes2write = 2; 
            ffsdrv.write_halfword((uint16 *) mydst, mysrc[0] | (mysrc[1] << 8)); 
        } 
        else { 
            // TODO: make a loop around the below function (speed optimization) 
            uint16 wbuf[FFS_WR_BUF_MAX_WORDS]; 
#if 0 
	     uint16	 currentBlkLeftSize = dev.blocksize-blockoffsetaddr(dst); 
            
		if(currentBlkLeftSize< bytes2write ) // beyond block boundary, turncate! fangcj added 
			bytes2write = currentBlkLeftSize; 
#endif 
            // Truncate to an even number of bytes 
            bytes2write = bytes2write & (uint16) ~1; 
 
            // The write_buffer() function is not able to make a 
            // flash-to-flash copy thus the source most be copied to RAM. It 
            // is the operations: rename, inode and data reclaim that makes 
            // a flash-to-flash copy. The write_buffer() function cannot 
            // fill the 'flash write buffer' with data from the flash 
            // because the flash is in 'read status' mode ad the time the 
            // buffer is filled. Furthermore the write_buffer() function 
            // presume that src is half word aligned 
            memcpy(wbuf, mysrc, bytes2write); 
 
            ffsdrv.write_buffer((uint16 *) mydst, (uint16 *) wbuf, 
                                bytes2write/2); 
        } 
 
        size -= bytes2write; 
        mysrc += bytes2write; 
        mydst += bytes2write; 
    } 
    tw(tr(TR_END, TrDrvWrite, "}\n")); 
} 
 
/****************************************************************************** 
 * AMD Single Bank Driver Functions 
 ******************************************************************************/ 
 
#if (TARGET == 1) 
 
// Forward declaration of functions in file amdsbdrv.c 
void ffsdrv_ram_amd_sb_write_halfword(volatile uint16 *addr, uint16 value); 
void ffsdrv_ram_amd_sb_erase(uint8 block); 
void ffsdrv_ram_amd_sb_erase_sector(void *dst); 
 
#else // (TARGET == 0) 
 
// On PC these functions are empty 
void ffsdrv_ram_amd_sb_write_halfword(volatile uint16 *addr, uint16 value) {} 
void ffsdrv_ram_amd_sb_erase(uint8 block) {} 
void ffsdrv_ram_amd_sb_erase_sector(void *dst) {} 
 
#endif // (TARGET == 1) 
 
 
/****************************************************************************** 
 * AMD Pseudo Single Bank Driver Functions 
 ******************************************************************************/ 
 
// This is a pseudo single-bank flash driver. It simulates a single-bank 
// flash device on a dual-bank device. 
 
#ifdef AMD_FLASH 
#if (TARGET == 1) 
 
void ffsdrv_amd_pseudo_sb_write_halfword(volatile uint16 *addr, uint16 value) 
{ 
    volatile char *flash = dev.base; 
    uint32 cpsr, i, x; 
 
    ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value)); 
 
    if (~*addr & value) { 
        ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value)); 
        return; 
    } 
 
    cpsr = int_disable(); 
    tlw(led_on(LED_WRITE)); 
 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    flash[0xAAAA] = 0xA0; 
    *addr         = value; 
 
    while ((*dev.addr ^ dev.data) & 0x80) 
        ; 
 
    tlw(led_off(LED_WRITE)); 
    int_enable(cpsr); 
} 
 
// This VERY simple way of erase suspending only works because we run under 
// a pre-emptive operating system, so whenever an interrupt occurs, another 
// task takes the CPU, and at the end of the interrupt, FFS gets the CPU 
// again. 
void ffsdrv_amd_pseudo_sb_erase(uint8 block) 
{ 
    char *addr = (char *)block2addr(block); 
    ffsdrv.erase_sector(addr); 
} 
 
void ffsdrv_amd_pseudo_sb_erase_sector(void *dst) 
{ 
    volatile char *flash = dev.base; 
    volatile char *addr = (char *)dst; 
    uint32 cpsr; 
    uint16 flashpoll; 
 
    ttw(ttr(TTrDrvErase, "e(%d)" NL, dst)); 
 
    cpsr = int_disable(); 
    tlw(led_on(LED_ERASE)); 
 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    flash[0xAAAA] = 0x80; 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    *addr         = 0x30; // AMD erase sector command 
 
    // Wait for erase to finish. 
    while ((*addr & 0x80) == 0) { 
        tlw(led_toggle(LED_ERASE)); 
        // Poll interrupts, taking interrupt mask into account. 
        if (INT_REQUESTED) 
        { 
            // 1. suspend erase 
            // 2. enable interrupts 
            // .. now the interrupt code executes 
            // 3. disable interrupts 
            // 4. resume erase 
 
            tlw(led_on(LED_ERASE_SUSPEND)); 
            *addr = 0xB0; 
 
            // wait for erase suspend to finish 
            while ((*addr & 0x80) == 0) 
                ; 
 
            tlw(led_off(LED_ERASE_SUSPEND)); 
            int_enable(cpsr); 
 
            // Other interrupts and tasks run now... 
 
            cpsr = int_disable(); 
            tlw(led_on(LED_ERASE_SUSPEND)); 
 
            // Before resuming erase we must? check if the erase is really 
            // suspended or if it did finish 
            flashpoll = *addr; 
            *addr = 0x30; 
 
            tlw(led_off(LED_ERASE_SUSPEND)); 
        } 
    } 
 
    tlw(led_on(LED_ERASE)); 
    tlw(led_off(LED_ERASE)); 
    int_enable(cpsr); 
} 
 
#else // (TARGET == 0) 
 
void ffsdrv_amd_pseudo_sb_write_halfword(volatile uint16 *addr, uint16 value) {} 
void ffsdrv_amd_pseudo_sb_erase(uint8 block) {} 
void ffsdrv_amd_pseudo_sb_erase_sector(void *dst) {} 
 
#endif // (TARGET == 1) 
 
 
/****************************************************************************** 
 * AMD Dual/Multi Bank Driver Functions 
 ******************************************************************************/ 
 
// All erase and write operations are performed atomically (interrupts 
// disabled). Otherwise we cannot trust the value of dev.state and we cannot 
// determine exactly how many of the command words have already been 
// written. 
 
// in ffs_end() when we resume an erasure that was previously suspended, how 
// does that affect multiple tasks doing that simultaneously? 
 
void ffsdrv_amd_write_end(void); 
void ffsdrv_amd_erase_end(void); 
 
void ffsdrv_amd_write_halfword(volatile uint16 *addr, uint16 value) 
{ 
    volatile char *flash = dev.base; 
    uint32 cpsr; 
    volatile uint16 a; 
 
    tlw(led_on(LED_WRITE)); 
    ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value)); 
 
    dev.addr = addr; 
    dev.data = value; 
 
    a=value; 
    a=*addr; 
 
    if (~*addr & value) { 
        ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value)); 
        return; 
    } 
 
    cpsr = int_disable(); 
    tlw(led_toggle(LED_WRITE_SUSPEND)); 
    dev.state = DEV_WRITE; 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    flash[0xAAAA] = 0xA0; 
    *addr         = value; 
    int_enable(cpsr); 
    tlw(led_toggle(LED_WRITE_SUSPEND)); 
 
    ffsdrv_amd_write_end(); 
} 
 
void ffsdrv_amd_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
 
    if (size > 0) 
    { 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
        while (size >= 2) { 
            ffsdrv_amd_write_halfword((uint16 *) mydst, 
                                      mysrc[0] | (mysrc[1] << 8)); 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
} 
 
void ffsdrv_amd_write_end(void) 
{ 
    while ((*dev.addr ^ dev.data) & 0x80) 
        tlw(led_toggle(LED_WRITE_SUSPEND)); 
 
    dev.state = DEV_READ; 
 
    tlw(led_off(LED_WRITE)); 
} 
 
void ffsdrv_amd_erase(uint8 block) 
{ 
    uint16 *addr = (uint16 *) block2addr(block); 
    ffsdrv.erase_sector(addr); 
} 
 
void ffsdrv_amd_erase_sector(void *dst) 
{ 
    volatile char *flash = dev.base; 
    uint32 cpsr; 
 
    tlw(led_on(LED_ERASE)); 
    ttw(ttr(TTrDrvErase, "e(%d)" NL, dst)); 
 
    dev.addr = (uint16 *) dst; 
 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE; 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    flash[0xAAAA] = 0x80; 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    *dev.addr = 0x30; // AMD erase sector command 
    int_enable(cpsr); 
 
    ffsdrv_amd_erase_end(); 
} 
 
void ffsdrv_amd_erase_end(void) 
{ 
    while ((*dev.addr & 0x80) == 0) 
        ; 
 
    dev.state = DEV_READ; 
 
    tlw(led_off(LED_ERASE)); 
} 
 
void ffsdrv_amd_erase_suspend(void) 
{ 
    uint32 cpsr; 
 
    tlw(led_on(LED_ERASE_SUSPEND)); 
    ttw(str(TTrDrvErase, "es" NL)); 
 
    // if erase has finished then all is ok 
    if (*dev.addr & 0x80) { 
        ffsdrv_amd_erase_end(); 
        tlw(led_off(LED_ERASE_SUSPEND)); 
        return; 
    } 
 
    // NOTEME: As there is no way to be absolutely certain that erase 
    // doesn't finish between last poll and the following erase suspend 
    // command, we assume that the erase suspend is safe even though the 
    // erase IS actually already finished. 
 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE_SUSPEND; 
    *dev.addr = 0xB0; 
 
    // Wait for erase suspend to finish 
    while ((*dev.addr & 0x80) == 0)     /* Enabling the interrupt should be after this while, */ 
                                        /* because DQ7==1 on entering to erase suspend mode */ 
        ; 
 
    int_enable(cpsr); 
} 
 
void ffsdrv_amd_erase_resume(void) 
{ 
    uint32 cpsr; 
 
    ttw(str(TTrDrvErase, "er" NL)); 
 
    // NOTEME: See note in erase_suspend()... We assume that the erase 
    // resume is safe even though the erase IS actually already finished. 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE; 
    *dev.addr = 0x30; 
    int_enable(cpsr); 
 
    tlw(led_off(LED_ERASE_SUSPEND)); 
} 
 
#endif 
 
/****************************************************************************** 
 * SST Dual/Multi Bank Driver Functions 
 ******************************************************************************/ 
 
// SST flashes use almost same command set as AMD flashes. Only the command 
// addresses (4 more bits) and erase command data (0x50 instead of 0x30) are 
// different. SST flashes have no erase suspend/resume commands because they 
// are so fast at erasing! 
 
void ffsdrv_sst_write_end(void); 
void ffsdrv_sst_erase_end(void); 
 
void ffsdrv_sst_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
 
    if (size > 0) 
    { 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
        while (size >= 2) { 
            ffsdrv_amd_write_halfword((uint16 *) mydst, 
                                      mysrc[0] | (mysrc[1] << 8)); 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
} 
 
// Note that SST flashes have smaller sectors than other flash families. 
// Fortunately they support erasure of several of these sectors in a logical 
// unit called a "block". 
void ffsdrv_sst_erase(uint8 block) 
{ 
    uint16 *addr = (uint16 *) block2addr(block); 
    ffsdrv.erase_sector(addr); 
} 
 
void ffsdrv_sst_erase_sector(void *dst) 
{ 
    volatile char *flash = dev.base; 
    uint32 cpsr; 
 
    tlw(led_on(LED_ERASE)); 
    ttw(ttr(TTrDrvErase, "e(%d)" NL, dst)); 
 
    dev.addr = dst; 
 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE; 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    flash[0xAAAA] = 0x80; 
    flash[0xAAAA] = 0xAA; // unlock cycle 1 
    flash[0x5555] = 0x55; // unlock cycle 2 
    *dev.addr = 0x50; // SST erase block command 
    int_enable(cpsr); 
 
    ffsdrv_sst_erase_end(); 
} 
 
void ffsdrv_sst_erase_end(void) 
{ 
    // Wait for erase end 
    while ((*dev.addr & 0x80) == 0) 
        ; 
 
    dev.state = DEV_READ; 
 
    tlw(led_off(LED_ERASE)); 
} 
 
// Erase suspend/resume commands do not exist for SST flashes, so we just 
// poll for the end of the erase operation... 
 
void ffsdrv_sst_erase_suspend(void) 
{ 
    ttw(str(TTrDrvErase, "es" NL)); 
 
    ffsdrv_sst_erase_end(); 
} 
 
 
/****************************************************************************** 
 * Intel Single Bank Driver Functions 
 ******************************************************************************/ 
#ifdef INTEL_FLASH 
#if (TARGET == 1) 
 
// Forward declaration of functions in file intelsbdrv.c 
int  ffsdrv_ram_intel_sb_init(void); 
void ffsdrv_ram_intel_sb_write_halfword(volatile uint16 *addr, uint16 value); 
void ffsdrv_ram_intel_sb_erase(uint8 block); 
void ffsdrv_ram_intel_sb_erase_sector(void *dst); 
void ffsdrv_ram_intel_erase(uint8 block); 
void ffsdrv_ram_intel_erase_sector(void *dst); 
 
#else // (TARGET == 0) 
 
// On PC these functions are empty 
void ffsdrv_ram_intel_sb_write_halfword(volatile uint16 *addr, uint16 value) {} 
void ffsdrv_ram_intel_sb_erase(uint8 block) {} 
void ffsdrv_ram_intel_sb_erase_sector(void *dst) {} 
void ffsdrv_ram_intel_erase(uint8 block) {} 
void ffsdrv_ram_intel_erase_sector(void *dst); 
 
#endif // (TARGET == 1) 
 
 
/****************************************************************************** 
 * Intel Dual/Multi Bank Driver Functions 
 ******************************************************************************/ 
 
void ffsdrv_intel_write_end(void); 
void ffsdrv_intel_erase_end(void); 
 
void ffsdrv_intel_init(void) 
{ 
	volatile uint16	*flash = (volatile uint16*)dev.base; 
	flash[0] = INTEL_READ_ARRAY;//reset intel flash status 
	 
} 
// ffsdrv_intel_write_halfword and ffsdrv_intel_write_end is not used 
// because of the bug in the intel flash device. Instead is the functions 
// ffsdrv_ram_intel_sb_write_halfword and ffsdrv_ram_intel_erase used. 
void ffsdrv_intel_write_halfword(volatile uint16 *addr, uint16 value) 
{ 
    uint32 cpsr; 
 
    tlw(led_on(LED_WRITE)); 
    ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value)); 
 
    dev.addr = addr; 
	 
#if (FLASH_WRITE_CHECK == 1) 
    if (~*addr & value) { 
        ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value)); 
        return; 
    } 
#endif 
    cpsr = int_disable(); 
    dev.state = DEV_WRITE; 
 
#if (INTEL_UNLOCK_SLOW == 1) 
    *addr = 0x60; // Intel Config setup 
    *addr = 0xD0; // Intel Unlock block 
    *addr = 0x50; // Intel Clear Status Register 
#endif 
 
    *addr = 0x40; // Intel program byte/word 
    *addr = value; 
 
    int_enable(cpsr); 
 
    ffsdrv_intel_write_end(); 
} 
 
// IMPORTANT: This function presupposes that the size not is above the flash 
// write buffer size and the src is half word aligned. 
void ffsdrv_intel_write_buffer_mode(volatile uint16 *addr, volatile uint16 *src, 
                                    uint16 words2write) 
{ 
    uint32 cpsr; 
    uint16 i, status; 
 
    ttw(ttr(TTrDrvWrite, "wbuf(%x, %x, %x)" NL, addr, src, words2write)); 
 
#if (FLASH_WRITE_CHECK == 1) 
    // Make sure we don't try to flip a '0' to '1'. 
    for (i = 0; i < words2write; i++) { 
        if (~(int) addr[i] & (int) src[i]) { 
            ttw(ttr(TTrFatal, "wbuf(%x,%x->%x) fatal" NL, addr, addr[i], src[i])); 
            return; 
        } 
    } 
#endif 
 
    cpsr = int_disable(); // FIXME can we disable interrupt for all this time? 
    dev.addr = addr; 
    dev.state = DEV_WRITE; 
 
    *dev.addr = INTEL_CLR_STATUS; 
    *dev.addr = INTEL_LOCK_SETUP; 
    *dev.addr = INTEL_UNLOCK_BLK; 
 
    // Wait for buffer ready 
    do { 
        *dev.addr = INTEL_BUFFER_PRG; 
        status = *dev.addr; 
    } while (!(status & INTEL_STATE_MACHINE_DONE)); 
 
    // Write the word count - 1 
    *dev.addr = words2write - 1; 
 
    // Fill the write buffer 
    while (words2write > 0) { 
        *addr = *src; 
        words2write--; 
        addr++; 
        src++; 
    } 
 
    // Start the write buffer process 
    *dev.addr = INTEL_BUFFER_PRG_CONFIRM; 
    *dev.addr = INTEL_READ_STATUS; 
 
    int_enable(cpsr); 
    ffsdrv_intel_write_end(); 
    // NOTEME Verify the write? 
} 
 
void ffsdrv_intel_write_end(void) 
{ 
    uint32 cpsr; 
    uint16 status; 
 
    // We can be interrupted and reentered thus the state can have been changed. 
    while (1) { 
        if (dev.state == DEV_WRITE) { 
            // write completed? 
            if ((*dev.addr & INTEL_STATE_MACHINE_DONE) > 0) 
                break; // write completed 
            continue; 
        } 
        else if (dev.state == DEV_WRITE_SUSPEND) 
            continue; // Wait until the write is resumed 
        else if (dev.state == DEV_READ) 
            break; // write completed in write_suspend() or a re-entered 
                   // write_end() 
 
        ttw(ttr(TTrFatal, "FATAL: invalid dev.state %d" NL, dev.state)); 
        return; 
    } 
 
    // The flash state and dev.state must be in sync thus the stat changes 
    // must be protected from being interrupted 
    cpsr = int_disable(); 
 
#if (FLASH_STATUS_CHECK == 1) 
    *dev.addr = INTEL_READ_STATUS; 
    status = *dev.addr; 
    if (status != INTEL_STATE_MACHINE_DONE) 
        ttw(ttr(TTrFatal, "FATAL write error 0x%x" NL, status)); 
#endif 
 
    dev.state = DEV_READ; 
    *dev.addr = INTEL_READ_ARRAY; 
    int_enable(cpsr); 
 
    tlw(led_off(LED_WRITE)); 
} 
 
void ffsdrv_intel_write_suspend(void) 
{ 
    uint32 cpsr; 
    uint16 poll; 
 
    ttw(str(TTrDrvWrite, "wrs" NL)); 
 
    // The flash state and dev.state must be in sync thus the stat changes 
    // must be protected from being interrupted 
    cpsr = int_disable(); 
    dev.state = DEV_WRITE_SUSPEND; 
 
    *dev.addr = INTEL_SUSPEND; 
    *dev.addr = INTEL_READ_STATUS; 
 
    while (((poll = *dev.addr) & INTEL_STATE_MACHINE_DONE) == 0) 
        ; 
 
    if ((poll & INTEL_PROGRAM_SUSPEND) == 0) { 
        // Write has completed 
        dev.state = DEV_READ; 
    } 
    // The write is finíshed or suspended, prepare to read data from the flash 
    *dev.addr = INTEL_READ_ARRAY; 
    int_enable(cpsr); 
} 
 
void ffsdrv_intel_write_resume(void) 
{ 
    uint32 cpsr; 
 
    ttw(str(TTrDrvWrite, "wrr" NL)); 
 
    // The flash state and dev.state must be in sync thus the stat changes 
    // must be protected from being interrupted 
    cpsr = int_disable(); 
    dev.state = DEV_WRITE; 
    *dev.addr = INTEL_RESUME; 
    // FFS always expect that the flash is in READ status mode while the 
    // dev.state is DEV_WRITE 
    *dev.addr = INTEL_READ_STATUS; 
    int_enable(cpsr); 
} 
 
void ffsdrv_intel_erase(uint8 block)  
{ 
    uint16 *addr = (uint16 *) block2addr(block); 
    ffsdrv.erase_sector(addr); 
} 
 
void ffsdrv_intel_erase_sector(void *dst) 
{ 
    uint32 cpsr; 
 
    ttw(ttr(TTrDrvErase, "e(0x%x)" NL, dst)); 
    tlw(led_on(LED_ERASE)); 
 
    dev.addr = (uint16 *)dst; 
 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE; 
 
#if (INTEL_UNLOCK_SLOW == 1) 
    *dev.addr = INTEL_CLR_STATUS; // Intel clear status register (not really necessary) 
    *dev.addr = INTEL_LOCK_SETUP; // Intel Config setup 
    *dev.addr = INTEL_UNLOCK_BLK; // Intel Unlock block 
#endif 
 
    *dev.addr = INTEL_BLOCK_ERASE_SETUP; // Intel erase setup 
    *dev.addr = INTEL_ERASE_CONFIRM; // Intel erase confirm 
 
     *dev.addr = INTEL_READ_STATUS;//fangcj added 
    int_enable(cpsr); 
 
    ffsdrv_intel_erase_end(); 
} 
 
void ffsdrv_intel_erase_end(void) 
{ 
    uint16 status; 
#if (BOARD == 46) 
    while ((*dev.addr & 0x80) == 0 && dev.state == DEV_ERASE) 
    ; 
#else 
    while ((*dev.addr & 0x80) == 0 && 
           (dev.state == DEV_ERASE || dev.state == DEV_ERASE_SUSPEND)) 
        ; 
#endif 
 
#if (FLASH_STATUS_CHECK == 1) 
   *dev.addr = INTEL_READ_STATUS; 
    status = *dev.addr; 
    if (status != INTEL_STATE_MACHINE_DONE) 
        ttw(ttr(TTrFatal, "FATAL erase error 0x%x" NL, status)); 
#endif 
 
 
    *dev.addr = INTEL_READ_ARRAY; // Intel read array 
    dev.state = DEV_READ; 
    tlw(led_off(LED_ERASE)); 
} 
 
void ffsdrv_intel_erase_suspend(void) 
{ 
    uint32 cpsr; 
    uint16 poll; 
 
    ttw(str(TTrDrvErase, "es" NL)); 
    tlw(led_on(LED_ERASE_SUSPEND)); 
 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE_SUSPEND; 
    *dev.addr = 0xB0; // Intel Erase Suspend 
    *dev.addr = 0x70; // Intel Read Status Register 
    while (((poll = *dev.addr) & 0x80) == 0) 
        ; 
 
    if ((poll & 0x40) == 0) { 
        // Block erase has completed 
        tlw(led_off(LED_ERASE_SUSPEND)); 
        dev.state = DEV_READ; 
        tlw(led_off(LED_ERASE)); 
    } 
    *dev.addr = 0xFF; // Intel read array 
    int_enable(cpsr); 
} 
 
void ffsdrv_intel_erase_resume(void) 
{ 
    uint32 cpsr; 
 
    ttw(str(TTrDrvErase, "er" NL)); 
 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE; 
    *dev.addr = 0xD0; // Intel erase resume 
 
// The following "extra" Read Status command is required because Intel has 
// changed the specification of the W30 flash! (See "1.8 Volt Intel? 
// Wireless Flash Memory with 3 Volt I/O 28F6408W30, 28F640W30, 28F320W30 
// Specification Update") 
    *dev.addr = 0x70; // Intel Read Status Register 
 
    int_enable(cpsr); 
 
    tlw(led_off(LED_ERASE_SUSPEND)); 
} 
 
// NOTE this function has a little better performance than the 
// generic_write() because it use less function pointers 
void ffsdrv_intel_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
 
    if (size > 0) 
    { 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
        while (size >= 2) { 
            ffsdrv_intel_write_halfword((uint16 *) mydst, 
                                        mysrc[0] | (mysrc[1] << 8)); 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
} 
 
#endif 
/****************************************************************************** 
 * RAM Family Functions 
 ******************************************************************************/ 
 
void ffsdrv_ram_write_halfword(volatile uint16 *dst, uint16 value) 
{ 
  *dst = value; 
} 
 
void ffsdrv_ram_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
 
    if (size == 0) 
        return; 
    else if (size == 1) 
        ffsdrv_write_byte(mydst, *mysrc); 
    else { 
        if ((int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
        while (size >= 2) { 
            ffsdrv_ram_write_halfword((uint16 *) mydst, mysrc[0]|(mysrc[1] << 8)); 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
} 
 
void ffsdrv_ram_erase(uint8 block) 
{ 
    int i; 
    char *addr; 
 
    addr = (char *)block2addr(block);; 
    for (i = 0; i < 1 << dev.binfo[block].size_ld; i++)  
    { 
        *addr++ = 0xFF; 
    } 
} 
 
void ffsdrv_ram_erase_sector(void *dst)  
{ 
    uint8 block; 
 
    for(block=0; block last) { 
        ttw(ttr(TTrAll, "ffsdrv_write_check() failed (addr = 0x%x, size = %d)" 
                NL, (int) addr, size)); 
        return -1; 
    } 
    return EFFS_OK; 
} 
 
// FIXME update this to work in target 
void ffsdrv_write_error(uint16 old, uint16 new) {} 
 
#else 
 
static char *image_addr = 0; 
static int image_size = 0; 
 
#ifdef WIN32 
HANDLE image_fd, map_fd; 
#else //WIN32 
static int image_fd = 0; 
#endif //WIN32 
 
/*Modified by ZhangTing for PC simulator 2006-08-26*/ 
#ifndef _LEGEND_EMULATOR_ 
extern int   arg_removeimage; 
extern char *arg_imagename; 
#else 
int   arg_removeimage; 
char *arg_imagename = "ffs.bin"; 
#endif 
/*End*/ 
 
extern void test_fatal_printf(char *format, ...); 
 
int ffsdrv_write_check(char *addr, int size) 
{ 
    offset_t offset, last; 
 
    offset = addr2offset(addr); 
    last = dev.binfo[dev.numblocks-1].offset 
        + (1 << dev.binfo[dev.numblocks-1].size_ld); 
 
    if (offset < 0 || (offset + size) > last) { 
        fprintf(stderr, "ffsdrv_write_check() failed (addr = 0x%x, size = %d)\n", 
                (int) addr, size); 
        fprintf(stdout, "ffsdrv_write_check() failed (addr = 0x%x, size = %d)\n", 
                (int) addr, size); 
        exit (1); 
    } 
 
    return 0; 
} 
 
 
void ffsdrv_write_error(uint16 old, uint16 new) 
{ 
    #ifndef _LEGEND_EMULATOR_    /*Modified by ZhangTing for PC simulator 2006-06-15*/ 
 
    test_fatal_printf("FATAL: Attempt to rewrite 0 to 1 bit " 
                      "(old:0x%x/%c new:0x%x/%c)\n", 
                      old, (old < ' ' ? '?' : old), 
                      new, (new < ' ' ? '?' : new)); 
    #endif 
} 
 
void ffsdrv_test_write_halfword(volatile uint16 *addr, uint16 value) 
{ 
    tw(tr(TR_FUNC, TrDrvWrite, "test_write_halfword(0x%05x, 0x%x)\n", 
          addr2offset(addr), value)); 
 
    if (POWERFAIL_ENABLED) 
        POWERFAIL_WRITE(addr, value); 
 
    ffsdrv_write_check((uint8 *) addr, 2); 
 
    if (~*addr & value) 
        ffsdrv_write_error(*addr, value); 
 
    *addr = value; 
} 
 
void ffsdrv_test_write_buffer(volatile uint16 *addr, volatile uint16 *src, 
                              uint16 words2write) 
{ 
    int i , num_bytes = words2write * 2; 
    uint8 *mysrc = (uint8 *) src; 
    uint8 *mydst = (uint8 *) addr; 
 
    tw(tr(TR_FUNC, TrDrvWrite, "test_write_buffer(0x%x, 0x%05x, 0x%x)\n", 
          addr2offset(addr), src, words2write)); 
 
    if (words2write <= 1 || words2write * 2 > dev.write_buffersize) 
        tw(tr(TR_FUNC, TrAll, "ERROR wrong number of words (%d)\n", words2write)); 
 
    ffsdrv_write_check((uint8 *) addr, words2write * 2); 
 
    // NOTEME this can be made a lot more simpel as the last input arg have 
    // changed from num_bytes to words2write 
    for (i = 0; i < num_bytes; i++) { 
        if (~*(mydst + i) & *(mysrc + i)) 
            ffsdrv_write_error(*(mydst + i), *(mysrc + i)); 
 
        // Trigger only the powerfail framework at halfword aligned addresses 
        if (POWERFAIL_ENABLED && !((int) (mydst + i) & 1)) 
            // Note POWERFAIL_WRITE() write halfwords! 
            POWERFAIL_WRITE((uint16 *) mydst + i/2, 
                            *(mysrc + i/2) | *(mysrc + i/2 + 1) << 8); 
 
        *(mydst + i) = *(mysrc + i); 
    } 
} 
 
void ffsdrv_test_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
 
    tw(tr(TR_FUNC, TrDrvWrite, "test_write(0x%05x, 0x%x, %d)\n", 
          addr2offset(mydst), mysrc, size)); 
 
    if (size > 0) 
    { 
        if ((int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
        while (size >= 2) { 
            ffsdrv_test_write_halfword((uint16 *) mydst, mysrc[0]|(mysrc[1] << 8)); 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
} 
 
void ffsdrv_test_erase(uint8 block) 
{ 
    int i; 
    uint8 *addr; 
 
    if (POWERFAIL_ENABLED) 
        POWERFAIL_ERASE(block); 
 
    addr = (uint8 *)block2addr(block); 
 
    tw(tr(TR_FUNC, TrDrvErase, "ffsdrv_test_erase(%d)\n", block)); 
 
    for (i = 0; i < 1 << dev.binfo[block].size_ld; i++) { 
        *addr++ = 0xFF; 
    } 
} 
 
void ffsdrv_test_erase_sector(void *dst) 
{ 
    uint8 block; 
 
    for(block=0; block 0) { 
        tw(tr(TR_FUNC, TrDrvInit, "re-opening '%s' file of size %d\n", 
              arg_imagename, image_size)); 
    } 
    else { 
        tw(tr(TR_FUNC, TrDrvInit, "opening '%s' file of size %d\n", 
              arg_imagename, image_size)); 
 
#ifdef WIN32 
        image_fd = OpenFile( arg_imagename, &lpReOpenBuff, OF_READWRITE); 
        map_fd = CreateFileMapping (image_fd, 
                                    &lpAttributes, 
                                    PAGE_READWRITE, 
                                    0, 
                                    0, 
                                    arg_imagename); 
#else //WIN32 
        image_fd = open(arg_imagename, O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO); 
#endif //WIN32 
        if (image_fd == -1) { 
            perror("Failed to open flash image"); 
            exit(1); 
        } 
 
        // memory map the file and update block addresses of binfo array 
#ifdef WIN32 
        image_addr = MapViewOfFile( map_fd, 
                                    FILE_MAP_ALL_ACCESS, 
                                    0, 
                                    0, 
                                    0); 
#else //WIN32 
        image_addr = mmap(0, image_size, PROT_READ|PROT_WRITE, 
                          MAP_FILE|MAP_SHARED, image_fd, 0); 
#endif //WIN32 
    } 
 
    tw(tr(TR_END, TrDrvInit, "}\n")); 
 
    return image_addr; 
} 
 
#endif // (TARGET == 0) 
 
 
/****************************************************************************** 
 * Device Detection and Copying of Driver to RAM 
 ******************************************************************************/ 
 
#if (TARGET == 1) 
 
// Note that this function reads device code of any of the three known flash 
// families; Intel, AMD and SST. This works because Intel and AMD use 
// the same command data for entering READ_IDENTIFIER mode (0x90). 
// The function should be copied and executed from RAM! 
#if (CHIPSET != 15) 
void ffsdrv_device_id_read(uint32 base_addr, uint16 *manufact, uint16 *device) 
{ 
    int addr, i; 
 
    // This silly looking code has one purpose; to set addr = 0xAAAA. It is 
    // necessary in order to force the compiler NOT to produce code that 
    // uses LDR opcode(s) with PC-relative addressing. The assember code 
    // produced from this C code is completely relocatable! 
    for (i = 0, addr = 0; i < 2; i++) 
        addr |= addr << 8 | 0xAA; 
 
    FLASH_WRITE_HALFWORD (base_addr + addr,        0xAA); 
    FLASH_WRITE_HALFWORD (base_addr + (addr >> 1), 0x55); 
    // Intel/AMD read id command 
    FLASH_WRITE_HALFWORD (base_addr + addr,        0x90); 
 
    *manufact = FLASH_READ_HALFWORD (base_addr);     // flash a0 = 0 
    device[0] = FLASH_READ_HALFWORD (base_addr + 2); // flash a0 = 1 
 
    // Read extended id 
    device[1] = FLASH_READ_HALFWORD (base_addr + (0xE << 1)); 
    device[2] = FLASH_READ_HALFWORD (base_addr + (0xF << 1)); 
 
    FLASH_WRITE_HALFWORD (base_addr, 0xFF); // Intel read-array command 
 
    // AMD devices do not need the two unlock cycles but SST devices do, 
    // even though the SST datasheets states otherwise ;-) 
    FLASH_WRITE_HALFWORD (base_addr + addr,        0xAA); 
    FLASH_WRITE_HALFWORD (base_addr + (addr >> 1), 0x55); 
    // AMD read-array/reset command 
    FLASH_WRITE_HALFWORD (base_addr + addr,        0xF0); 
} 
#else 
 
/* for CHIPSET == 15 */ 
// Convert a offset_t value to a block index 
 
void ffsdrv_device_id_read(uint32 base_addr, uint16 *manufact, uint16 *device) 
{ 
#ifdef INTEL_FLASH 
   /* unlock cycle */ 
    FLASH_WRITE_HALFWORD (base_addr, 0x60); 
    FLASH_WRITE_HALFWORD (base_addr, 0xD0); 
 
    //Enter into auto select command 
    FLASH_WRITE_HALFWORD (base_addr, 0x90); 
 
    *manufact = FLASH_READ_HALFWORD (base_addr); 
    device[0]=FLASH_READ_HALFWORD ((base_addr+0x02)); 
 
    // Read extended id 
    device[1]=FLASH_READ_HALFWORD ((base_addr+0x0A)); 
    device[2]=0; //FLASH_READ_HALFWORD ((base_addr+0xF)); 
 
   /* Exit from auto select --- reset */ 
    FLASH_WRITE_HALFWORD (base_addr, 0xFF); 
#else 
    int addr, i; 
 
    // This silly looking code has one purpose; to set addr = 0x555. It is 
    // necessary in order to force the compiler NOT to produce code that 
    // uses LDR opcode(s) with PC-relative addressing. The assember code 
    // produced from this C code is completely relocatable! 
    for (i = 0, addr = 0; i < 2; i++) 
        addr |= addr << 4 | 0xAA; 
 
   /* unlock cycle */ 
    FLASH_WRITE_HALFWORD ((base_addr+addr), 0xAA); 
 
    FLASH_WRITE_HALFWORD ((base_addr+(addr>>1)), 0x55); 
 
    //Enter into auto select command 
    FLASH_WRITE_HALFWORD ((base_addr+addr),        0x90); 
 
    *manufact = FLASH_READ_HALFWORD (base_addr); 
    device[0]=FLASH_READ_HALFWORD ((base_addr+0x02)); 
 
    // Read extended id 
    device[1]=FLASH_READ_HALFWORD ((base_addr+(0x0E<<1))); 
    device[2]=FLASH_READ_HALFWORD ((base_addr+(0xF<<1))); 
 
   /* Exit from auto select --- reset */ 
    FLASH_WRITE_HALFWORD (base_addr, 0xF0); 
#endif 
} 
#endif 
 
#define offset2block(offset) (((uint32) offset) >> dev.binfo[0].size_ld) 
 
#ifdef AMD_FLASH 
void ffsdrv_amd_fg_write_halfword(volatile uint16 *addr, uint16 value) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint32 cpsr; 
    uint8 * offset; 
    uint32 unlockOffset; 
    static int badFlashCount=0; 
 
   offset = (uint8*)addr - (uint32)(dev.base); 
   unlockOffset = (dev.binfo[offset2block(offset)].offset+ 0x80)>>1; 
 
   /* device state is write state */ 
   OS_LOCK_MUTEX(&ffs_write_mutex); 
   dev.state=DEV_WRITE; 
    /* Dissable the interrupts */ 
   cpsr=int_disable(); 
 
   /*LOCOSTO-Live Unlock Fix */ 
   flash[0]=0x0f0; 
   flash[0]= 0x0060; // sector unlock sequence 
   flash[0]= 0x0060; // sector unlock sequence 
   flash[unlockOffset]= 0x0060; // sector unlock sequence (address 6 = VIH) 
 
redowrite: 
   flash[0]=0x0f0; 
 
    /* two unlock cycles */ 
   flash[0x555]=0xAA; 
   flash[0x2AA]=0x55; 
 
   /* issue the program command now */ 
   flash[0x555]=0xA0; 
 
   dev.addr=addr;    /* record the last write */ 
   dev.data=value; 
 
   *addr = value;   /* this will be base+offset */ 
 
   /* Enable the interrupts */ 
 
   int_enable(cpsr); 
 
   /* Wait for the write to complete */ 
   while (1) 
   { 
        unsigned short temp= (*addr); 
        badFlashCount++; 
        if(!((temp ^ dev.data) & 0x80)) 
            break; 
        else if ((temp & 0x20)) 
        { 
            if((((*addr) ^ dev.data) & 0x80)) 
                goto redowrite; 
            break; 
        } 
        else 
            continue; 
   }/* Data polling */ 
 
   /* change the state to default read mode */ 
   dev.state=DEV_READ; 
 
   OS_UNLOCK_MUTEX(&ffs_write_mutex); 
 
} 
 
void ffsdrv_amd_fg_write_halfword_local(volatile uint16 *addr, uint16 value) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint32 cpsr; 
    uint8 * offset; 
    uint32 unlockOffset; 
    static int badFlashCount=0; 
 
//   offset = (uint8*)addr - (uint32)(dev.base); 
//   unlockOffset = (dev.binfo[offset2block(offset)].offset+ 0x80)>>1; 
 
   /* device state is write state */ 
   OS_LOCK_MUTEX(&ffs_write_mutex); 
   dev.state=DEV_WRITE; 
    /* Dissable the interrupts */ 
   cpsr=int_disable(); 
 
   /*LOCOSTO-Live Unlock Fix */ 
//   flash[0]=0x0f0; 
//   flash[0]= 0x0060; // sector unlock sequence 
//   flash[0]= 0x0060; // sector unlock sequence 
//   flash[unlockOffset]= 0x0060; // sector unlock sequence (address 6 = VIH) 
 
redowrite: 
   flash[0]=0x0f0; 
 
    /* two unlock cycles */ 
   flash[0x555]=0xAA; 
   flash[0x2AA]=0x55; 
 
   /* issue the program command now */ 
   flash[0x555]=0xA0; 
 
   dev.addr=addr;    /* record the last write */ 
   dev.data=value; 
 
   *addr = value;   /* this will be base+offset */ 
 
   /* Enable the interrupts */ 
 
   int_enable(cpsr); 
 
   /* Wait for the write to complete */ 
   while (1) 
   { 
        unsigned short temp= (*addr); 
        badFlashCount++; 
        if(!((temp ^ dev.data) & 0x80)) 
            break; 
        else if ((temp & 0x20)) 
        { 
            if((((*addr) ^ dev.data) & 0x80)) 
                goto redowrite; 
            break; 
        } 
        else 
            continue; 
   }/* Data polling */ 
 
   /* change the state to default read mode */ 
   dev.state=DEV_READ; 
 
   OS_UNLOCK_MUTEX(&ffs_write_mutex); 
 
} 
 
void ffsdrv_amd_fg_write_halfword_unlock_bypass(volatile uint16 *addr, uint16 value) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint32 cpsr; 
    uint8 * offset; 
    uint32 unlockOffset; 
    static int badFlashCount=0; 
 
//   offset = (uint8*)addr - (uint32)(dev.base); 
//   unlockOffset = (dev.binfo[offset2block(offset)].offset+ 0x80)>>1; 
 
   /* device state is write state */ 
   OS_LOCK_MUTEX(&ffs_write_mutex); 
   dev.state=DEV_WRITE; 
    /* Dissable the interrupts */ 
   cpsr=int_disable(); 
 
   /*LOCOSTO-Live Unlock Fix */ 
//   flash[0]=0x0f0; 
//   flash[0]= 0x0060; // sector unlock sequence 
//   flash[0]= 0x0060; // sector unlock sequence 
//   flash[unlockOffset]= 0x0060; // sector unlock sequence (address 6 = VIH) 
 
redowrite: 
   flash[0]=0x0f0; 
 
    /* two unlock cycles */ 
//   flash[0x555]=0xAA; 
//   flash[0x2AA]=0x55; 
 
   /* issue the program command now */ 
   flash[0x555]=0xA0; 
 
   dev.addr=addr;    /* record the last write */ 
   dev.data=value; 
 
   *addr = value;   /* this will be base+offset */ 
 
   /* Enable the interrupts */ 
 
   int_enable(cpsr); 
 
   /* Wait for the write to complete */ 
   while (1) 
   { 
        unsigned short temp= (*addr); 
        badFlashCount++; 
        if(!((temp ^ dev.data) & 0x80)) 
            break; 
        else if ((temp & 0x20)) 
        { 
            if((((*addr) ^ dev.data) & 0x80)) 
                goto redowrite; 
            break; 
        } 
        else 
            continue; 
   }/* Data polling */ 
 
   /* change the state to default read mode */ 
   dev.state=DEV_READ; 
 
   OS_UNLOCK_MUTEX(&ffs_write_mutex); 
 
} 
 
void ffsdrv_amd_fg_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint8 * offset; 
    uint32 unlockOffset;	 
    uint32 unlock_bypass=0x0; 
    uint32 cpsr; 
 
/*#ifdef WCP_PROF 
	prf_LogFunctionEntry((unsigned long) ffsdrv_amd_fg_write); 
#endif */ 
 
    if (size > 0) 
    { 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
    if (size >=2) 
    	{ 
	   offset = (uint8 *)dst - (uint32)(dev.base); 
	   unlockOffset = (dev.binfo[offset2block(offset)].offset+ 0x80)>>1; 
	   cpsr=int_disable(); 
	   	flash[0]=0x0f0; 
	   	flash[0]= 0x0060; // sector unlock sequence 
	   	flash[0]= 0x0060; // sector unlock sequence 
	   	flash[unlockOffset]= 0x0060; // sector unlock sequence (address 6 = VIH) 
	   int_enable(cpsr); 
 
	   cpsr=int_disable(); 
	   // two unlock cycles 
	   flash[0x555]=0xAA; 
	   flash[0x2AA]=0x55; 
	   // issue the program command now 
	   flash[0x555]=0x20; 
	   int_enable(cpsr); 
	   unlock_bypass=1;	    
  
    	} 
//	   cpsr=int_disable();	 
        while (size >= 2) { 
//            ffsdrv_amd_fg_write_halfword_local((uint16 *) mydst,  mysrc[0] | (mysrc[1] << 8)); 
            ffsdrv_amd_fg_write_halfword_unlock_bypass((uint16 *) mydst, mysrc[0] | (mysrc[1] << 8)); 
 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
//	   int_enable(cpsr); 
//#if 0 
	if(unlock_bypass==1) 
	{ 
		offset = (uint8*)dev.addr - (uint32)(dev.base); 
		cpsr=int_disable(); 
		flash[dev.binfo[offset2block(offset)].offset] = 0x90; 
		*(dev.addr) = 0x00; 
//		flash[0]=0x0f0;		 
	   	int_enable(cpsr); 
	} 
//#endif 
		 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
/*#ifdef WCP_PROF 
	prf_LogFunctionExit((unsigned long) ffsdrv_amd_fg_write); 
#endif */ 
 
} 
 
 
 
 
void ffsdrv_amd_fg_erase(uint8 block) 
{ 
    uint16 *addr = (uint16 *) block2addr(block); 
    ffsdrv.erase_sector(addr); 
} 
 
void ffsdrv_amd_fg_erase_sector(void *dst) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint32 cpsr; 
    uint32 unlockOffset; 
 
    dev.addr = (uint16 *) dst; 
    unlockOffset = ((uint32)(dev.addr)-(uint32)(dev.base) + 0x80) >> 1; 
 
    /* change the device status */ 
    dev.state=DEV_ERASE; 
 
   /* disable the interrupts */ 
   cpsr=int_disable(); 
   /*LOCOSTO-Live Unlock Fix */ 
   flash[0]=0x0f0; 
   flash[0]= 0x0060; // sector unlock sequence 
   flash[0]= 0x0060; // sector unlock sequence 
   flash[unlockOffset]= 0x0060; // sector unlock sequence (address 6 = VIH) 
   flash[0]=0x0f0; 
 
   /* sector erase command -- 6 cycles */ 
   flash[0x555] =0xAA; 
   flash[0x2AA]=0x55; 
   flash[0x555]=0x80; 
   flash[0x555]= 0xAA; 
   flash[0x2AA]=0x55; 
   *((volatile uint16*)(dev.addr))=0x30;   /* write the command to sector address */ 
 
   /* Enable the interrupts */ 
   int_enable(cpsr); 
 
 /* Wait for the erase to complete  */ 
    while ((*dev.addr & 0x80) == 0);   /* DQ7 will have 1 on completion of erase */  /* Data polling */ 
 
 /* change the device state  to default READ mode */ 
    dev.state=DEV_READ; 
 
} 
 
 
 
//OMAPS62129 Create NEW pseudo SB erase function for AMD_NOR (Isample v1.x) 
//This version allows the 3Mb of Flash not used by 1Mb FFS in Block C to 
//be reused for other code/data. 
void ffsdrv_amd_fg_erase_sector_pseudo_sb(void *dst) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint32 cpsr; 
    uint32 unlockOffset; 
 
 
    dev.addr = (uint16 *) dst; 
    unlockOffset = ((uint32)(dev.addr)-(uint32)(dev.base) + 0x80) >> 1; 
 
    /* change the device status */ 
    dev.state=DEV_ERASE; 
 
   ttw(ttr(TTrDrvErase, "e(%d){" NL, dst)); 
   /* disable the interrupts */ 
   cpsr=int_disable(); 
   /*LOCOSTO-Live Unlock Fix */ 
   flash[0]=0x0f0; 
   flash[0]= 0x0060; // sector unlock sequence 
   flash[0]= 0x0060; // sector unlock sequence 
   flash[unlockOffset]= 0x0060; // sector unlock sequence (address 6 = VIH) 
   flash[0]=0x0f0; 
 
   /* sector erase command -- 6 cycles */ 
   flash[0x555] =0xAA; 
   flash[0x2AA]=0x55; 
   flash[0x555]=0x80; 
   flash[0x555]= 0xAA; 
   flash[0x2AA]=0x55; 
   *((volatile uint16*)(dev.addr))=0x30;   /* write the command to sector address */ 
 
   /* Enable the interrupts */ 
 //CQ62129 start - new while loop added 
 
 // delay re-enabling until after sector erased or we have an int to service 
 
 /* Wait for the erase to complete  */ 
 while ((*dev.addr & 0x80) == 0)   /* DQ7 will have 1 on completion of erase */  /* Data polling */ 
 // add option to suspend if a frame interrupt occurs, same as old AMD single bank driver 
 
 {  	 // Poll interrupts, taking interrupt mask into account and check if IRQ was actually enabled 
        if ((INT_REQUESTED) && !(cpsr & ~0x80)) 
        { 
            // 1. suspend erase 
            // 2. enable interrupts 
            // .. now the interrupt code executes 
            // 3. disable interrupts 
            // 4. resume erase 
 
            // Suspend erase 
            *((volatile uint16*)(dev.addr)) = 0xB0; 
 
            // wait for erase suspend to finish 
            while ((*((volatile uint16*)(dev.addr)) & 0x80) == 0); 
 
            dev.state = DEV_ERASE_SUSPEND; 
 
 
   int_enable(cpsr); 
 
            // Other interrupts and tasks run now... 
 
            cpsr=int_disable(); 
 
             // Before resuming erase we must? check if the erase is really 
            // suspended or if it did finish 
            *((volatile uint16*)(dev.addr)) = 0x30; 
            dev.state = DEV_ERASE; 
 
        } 
 
 
 
 
 
 } 
 int_enable(cpsr); 
//CQ62129 end 
 
 ttw(ttr(TTrDrvErase, "}" NL)); 
 
 /* change the device state  to default READ mode */ 
    dev.state=DEV_READ; 
 
} 
 
void ffsdrv_amd_fg_write_suspend(void) 
{ 
    OS_LOCK_MUTEX(&ffs_write_mutex); 
 
    /* Got the Mutex Write Got over */ 
    OS_UNLOCK_MUTEX(&ffs_write_mutex); 
} 
 
void ffsdrv_amd_mb_write_halfword(volatile uint16 *addr, uint16 value) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint32 cpsr; 
	uint8 * offset; 
	uint32 unlockOffset; 
	static int badFlashCount=0; 
	unsigned short temp1; 
 
   offset = (uint8*)addr - (uint32)(dev.base); 
   unlockOffset = (dev.binfo[offset2block(offset)].offset+ 0x80)>>1; 
 
   /* device state is write state */ 
   OS_LOCK_MUTEX(&ffs_write_mutex); 
   dev.state=DEV_WRITE; 
    /* Dissable the interrupts */ 
   cpsr=int_disable(); 
 
	 
	temp1 = (*addr); 
redowrite: 
   flash[0]=0x0f0; 
 
    /* two unlock cycles */ 
   flash[0x555]=0xAA; 
   flash[0x2AA]=0x55; 
 
   /* issue the program command now */ 
   flash[0x555]=0xA0; 
 
   dev.addr=addr;    /* record the last write */ 
   dev.data=value; 
 
   *addr = value;   /* this will be base+offset */ 
 
   /* Enable the interrupts */ 
 
   int_enable(cpsr); 
 
   /* Wait for the write to complete */ 
   while (1) 
   { 
   		unsigned short temp= (*addr); 
		badFlashCount++; 
		if(!((temp ^ dev.data) & 0x80)) 
			break; 
		else if ((temp & 0x20)) 
		{ 
			 if((((*addr) ^ dev.data) & 0x80)) 
			 	goto redowrite; 
			 break; 
		} 
		else 
			continue; 
   }/* Data polling */ 
 
   /* change the state to default read mode */ 
   dev.state=DEV_READ; 
 
   OS_UNLOCK_MUTEX(&ffs_write_mutex); 
 
} 
 
void ffsdrv_amd_mb_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
 
    if (size > 0) 
    { 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
        while (size >= 2) { 
            ffsdrv_amd_mb_write_halfword((uint16 *) mydst, 
                                      mysrc[0] | (mysrc[1] << 8)); 
            size -= 2; 
            mysrc += 2; 
            mydst += 2; 
        } 
        if (size == 1) 
            ffsdrv_write_byte(mydst++, *mysrc++); 
    } 
 
} 
 
 
 
 
void ffsdrv_amd_mb_erase(uint8 block) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    uint32 cpsr; 
	uint32 unlockOffset; 
 
    dev.addr = (uint16 *) block2addr(block); 
    unlockOffset = ((uint32)(dev.addr)-(uint32)(dev.base) + 0x80) >> 1; 
 
    /* change the device status */ 
    dev.state=DEV_ERASE; 
 
   /* disable the interrupts */ 
   cpsr=int_disable(); 
 
   /* sector erase command -- 6 cycles */ 
   flash[0x555] =0xAA; 
   flash[0x2AA]=0x55; 
   flash[0x555]=0x80; 
   flash[0x555]= 0xAA; 
   flash[0x2AA]=0x55; 
   *((volatile uint16*)(dev.addr))=0x30;   /* write the command to (short) sector address */ 
 
   /* Enable the interrupts */ 
 //CQ72069 start - new while loop added 
 
 // delay re-enabling until after sector erased or we have an int to service 
 
 /* Wait for the erase to complete  */ 
 while ((*dev.addr & 0x80) == 0)   /* DQ7 will have 1 on completion of erase */  /* Data polling */ 
 // add option to suspend if a frame interrupt occurs, same as old AMD single bank driver 
 
 {  	 // Poll interrupts, taking interrupt mask into account and check if IRQ was actually enabled 
        if ((INT_REQUESTED) && !(cpsr & ~0x80)) 
        { 
            // 1. suspend erase 
            // 2. enable interrupts 
            // .. now the interrupt code executes 
            // 3. disable interrupts 
            // 4. resume erase 
 
            // Suspend erase 
            *((volatile uint16*)(dev.addr)) = 0xB0; 
 
            // wait for erase suspend to finish 
            while ((*((volatile uint16*)(dev.addr)) & 0x80) == 0); 
 
            dev.state = DEV_ERASE_SUSPEND; 
   		int_enable(cpsr); 
 
            // Other interrupts and tasks run now... 
            cpsr=int_disable(); 
 
             // Before resuming erase we must? check if the erase is really 
            // suspended or if it did finish 
            *((volatile uint16*)(dev.addr)) = 0x30; 
            dev.state = DEV_ERASE; 
 
        } 
 } 
 int_enable(cpsr); 
//CQ72069 end 
 
 ttw(ttr(TTrDrvErase, "}" NL)); 
 
 /* change the device state  to default READ mode */ 
    dev.state=DEV_READ; 
} 
 
void ffsdrv_amd_mb_erase_sector(void *dst) 
{ 
    volatile uint16 *flash; 
    uint32 cpsr; 
 
    dev.addr = (uint16 *) dst; 
	flash= dev.addr; 
 
    /* change the device status */ 
    dev.state=DEV_ERASE; 
 
    OS_LOCK_MUTEX(&ffs_write_mutex); 
  /* disable the interrupts */ 
   cpsr=int_disable(); 
 
   /* sector erase command -- 6 cycles */ 
   flash[0x555] =0xAA; 
   flash[0x2AA]=0x55; 
   flash[0x555]=0x80; 
   flash[0x555]= 0xAA; 
   flash[0x2AA]=0x55; 
   *((volatile uint16*)(dev.addr))=0x30;   /* write the command to sector address */ 
 
   /* Enable the interrupts */ 
   int_enable(cpsr); 
 
 /* Wait for the erase to complete  */ 
    while ((*dev.addr & 0x80) == 0);   /* DQ7 will have 1 on completion of erase */  /* Data polling */ 
 
 /* change the device state  to default READ mode */ 
    dev.state=DEV_READ; 
 } 
 
void ffsdrv_amd_mb_write_erase_suspend(void) 
{ 
    uint32 cpsr; 
 
    tlw(led_on(LED_ERASE_SUSPEND)); 
    ttw(str(TTrDrvErase, "es" NL)); 
 
    // if erase has finished then all is ok 
    if (*dev.addr & 0x80) { 
        ffsdrv_amd_erase_end(); 
        tlw(led_off(LED_ERASE_SUSPEND)); 
        return; 
    } 
 
    // NOTEME: As there is no way to be absolutely certain that erase 
    // doesn't finish between last poll and the following erase suspend 
    // command, we assume that the erase suspend is safe even though the 
    // erase IS actually already finished. 
 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE_SUSPEND; 
    *dev.addr = 0xB0; 
 
    // Wait for erase suspend to finish 
    while ((*dev.addr & 0x80) == 0)     /* Enabling the interrupt should be after this while, */ 
		                                            /* becose DQ7==1 on entering to erase suspend mode */ 
        ; 
 
 
    int_enable(cpsr); 
} 
 
void ffsdrv_amd_write_erase_resume(void) 
{ 
    uint32 cpsr; 
 
    ttw(str(TTrDrvErase, "er" NL)); 
 
    // NOTEME: See note in erase_suspend()... We assume that the erase 
    // resume is safe even though the erase IS actually already finished. 
    cpsr = int_disable(); 
    dev.state = DEV_ERASE; 
    *dev.addr = 0x30; 
    int_enable(cpsr); 
 
    tlw(led_off(LED_ERASE_SUSPEND)); 
} 
 
void ffsdrv_amd_mb_buffer_write(void *dst, const void *src, uint16 size) 
{ 
    uint8 *mydst = dst; 
    const uint8 *mysrc = src; 
    uint8 unalignedbytes = 0;	 
    uint16 sizeEven; 
 
// use halfword write for size less than 32 bytes	 
    if (size <= 32) 
    { 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
	while (size >= 2)  
	{ 
            ffsdrv_amd_mb_write_halfword((uint16 *) mydst, mysrc[0] | (mysrc[1] << 8)); 
	            size -= 2; 
	            mysrc += 2; 
	            mydst += 2; 
	} 
	if (size == 1) 
	{ 
		ffsdrv_write_byte(mydst++, *mysrc++); 
		size--; 
	 } 
    } 
// If size is more than 32 bytes then go for write buffer feature 
    else if (size > 32) 
    { 
	// if the destination addredd is odd, write first byte in that. 
        if ((unsigned int) mydst & 1) { 
            ffsdrv_write_byte(mydst++, *mysrc++); 
            size--; 
        } 
	// write unaligned bytes in the write buffer page, since write buffer feature does not allow  
	// write across write buffer boundaries and write can not start in the midded of the write buffer page. 
	unalignedbytes = (0x20 - (unsigned int) mydst & 0x0000001F); 
	if(unalignedbytes > 0) 
	{ 
	       while (unalignedbytes > 0) 
		{ 
				ffsdrv_amd_mb_write_halfword((uint16 *) mydst, mysrc[0] | (mysrc[1] << 8)); 
				size -= 2; 
				mysrc += 2; 
				mydst += 2; 
				unalignedbytes -= 2;			 
			} 
	} 
 
	// Write data using buffer write function 
        if (size >=2) 
        { 
	     // Truncate to an even number of bytes 
            sizeEven = size & (uint16) ~1; 
	// write the data using write buffer feature 
	ffsdrv_amd_mb_buffer_write_new((uint16 *) mydst, mysrc, sizeEven); 
		size -=sizeEven; 
		mysrc += sizeEven; 
		mydst += sizeEven; 
		 
        } 
//	Write Last byte 
 
	if(size==1 ) 
	{ 
		ffsdrv_write_byte(mydst++, *mysrc++); 
            	size--; 
	} 
    } 
} 
 
void ffsdrv_amd_mb_buffer_write_new(volatile UINT16 *dst_addr, const UINT8 *src_addr, UINT16 size) 
{ 
    volatile uint16 *flash = (volatile uint16*)dev.base; 
    volatile UINT16 *dst_ptr;  
    const UINT8 *src_ptr;  
    volatile UINT16 *last_loaded_addr; 
    UINT16 write_data; 
    UINT32 word_count; 
    UINT32 word_size = size >> 1; 
    uint32 cpsr; 
    static int badFlashCount=0; 
	 
	dst_ptr=dst_addr; 
	src_ptr=src_addr; 
 
OS_LOCK_MUTEX(&ffs_write_mutex); 
   dev.state=DEV_WRITE; 
 
   cpsr=int_disable(); 
       // two unlock cycles 
	  flash[0x555]=0xAA; 
	   flash[0x2AA]=0x55; 
	   // Unlock Bypass 
	  flash[0x555]=0x20; 
    int_enable(cpsr); 
  
    while (word_size >0) 
    { 
       cpsr=int_disable(); 
	// maximum 16 words can be written using write buffer 
       word_count = 16;		 
 
        if (word_count > word_size) 
        { 
            word_count = word_size; 
        } 
        word_size -= word_count; 
    
	 
	    
    
redowrite_buf: 
   	flash[0]=0x0f0; 
 
	/* Write to Buffer Command */ 
        FLASH_WRITE(dst_ptr, 0x25); 
 
	dev.addr=dst_ptr; 
	dev.data=src_ptr[0] | (src_ptr[1] << 8); 
		 
        /* Write number of locations to program */ 
        FLASH_WRITE(dst_ptr, word_count - 1); 
 
	 
        /* Load data into buffer */ 
        while (word_count > 0) 
        { 
            /* Store last loaded address and data value (for polling) */ 
            last_loaded_addr = dst_ptr; 
            write_data = src_ptr[0] | (src_ptr[1] << 8); 
 
            /* Write Data */ 
		*dst_ptr=write_data; 
            dst_ptr++; 
            src_ptr +=2; 
            word_count--; 
        } 
 
        /* Program Buffer to Flash Command */ 
        FLASH_WRITE(last_loaded_addr, 0x29); 
        int_enable(cpsr); 
		 
 
        /* Poll for completion, then check result */ 
	   while (1) 
	   { 
	   		unsigned short temp= (*last_loaded_addr); 
			badFlashCount++; 
			if(!((temp ^ write_data) & 0x80)) 
				break; 
			else if ((temp & 0x20)) 
			{ 
				 if((((*last_loaded_addr) ^ write_data) & 0x80)) 
				 	goto redowrite_buf; 
				 break; 
			} 
			else 
				continue; 
	   } 
 
    } 
 
FLASH_WRITE(dst_ptr, 0x90); 
FLASH_WRITE(dst_ptr, 0x00); 
	 
   /* change the state to default read mode */ 
   dev.state=DEV_READ; 
 
   OS_UNLOCK_MUTEX(&ffs_write_mutex); 
 
} 
#endif 
 
void ffsdrv_query_cfi_data(uint32 base_addr, uint16 addr, uint16* data) 
{ 
    volatile char *flash = (char*) base_addr; 
 
    flash[0xAA] = 0x98; // Set CFI query mode 
 
    *data = *((volatile uint16 *) (base_addr + addr)); // Query data 
 
    flash[0xAA] = 0xF0; // AMD read-array/reset command 
    flash[0] = 0xFF;    // Intel read-array/reset command 
} 
 
void ffsdrv_copy_code_to_ram(uint16 *dst, uint16 *src, int size) 
{ 
    // The ARM7TDMI compiler sets bit 0 for thumb mode function pointers, so 
    // we need to clear this in order to copy *all* bytes. Otherwise we 
    // exclude first byte and the resulting copy becomes garbage 
    src = (uint16 *) (~1 & (int) src); 
    size /= 2; 
 
    while (size--) 
        *dst++ = *src++; 
} 
 
 
// Copy ffsdrv_xxx_sb_erase() and ffsdrv_xxx_sb_write_halfword() functions 
// to RAM. The only known way to determine the size of the code is to look 
// either in the linker-generated map file or in the assember output file. 
int ffsdrv_driver_copy_to_ram(int type) 
{ 
    int size; 
    uint16 *src, *dst; 
    extern uint16 ffsdrv_ram_amd_begin[]; 
    extern uint16 ffsdrv_ram_intel_begin[]; 
    uint32 offset_of_init; 
    uint32 offset_of_erase; 
    uint32 offset_of_write_halfword; 
 
    ttw(ttr(TTrDrvOther, "ffsdrv_driver_copy_to_ram() {" NL)); 
 
    switch (type) { 
#ifdef AMD_FLASH 
    case FFS_DRIVER_AMD: 
    case FFS_DRIVER_AMD_SB: 
        src = ffsdrv_ram_amd_begin; 
        offset_of_erase = 
            (uint32) ffsdrv_ram_amd_sb_erase_sector - (uint32) src; 
        offset_of_write_halfword = 
            (uint32) ffsdrv_ram_amd_sb_write_halfword - (uint32) src; 
        break; 
#endif		 
#ifdef INTEL_FLASH 
    case FFS_DRIVER_INTEL: 
    case FFS_DRIVER_INTEL_SB: 
    case FFS_DRIVER_INTEL_BW: 
        src = ffsdrv_ram_intel_begin; 
        offset_of_init = 
            (uint32) ffsdrv_ram_intel_sb_init - (uint32) src; 
        offset_of_erase = 
            (uint32) ffsdrv_ram_intel_sb_erase_sector - (uint32) src; 
        offset_of_write_halfword = 
            (uint32) ffsdrv_ram_intel_sb_write_halfword - (uint32) src; 
        break; 
#endif		 
    default: 
        ttw(ttr(TTrDrvOther, "}" NL)); 
        return 0; 
    } 
 
    // Make sure we are handling a half-word aligned address (Thumb mode 
    // function pointers have lsb set!) 
    src = (uint16 *) (~1 & (int) src); 
 
    // If we detect that the linker allocated the driver to RUN in RAM, the 
    // user has obviously NOT removed those linker lines and we bail out! 
    if (offset_of_erase > FFSDRV_CODE_SIZE) 
        return EFFS_DRIVER; 
 
    dst = (uint16 *) &ffsdrv_code; 
 
    // Code size in halfwords 
    size = FFSDRV_CODE_SIZE / 2; 
 
    // Rebind the two changed driver functions 
    if (type == FFS_DRIVER_AMD_SB || type == FFS_DRIVER_INTEL_SB) { 
        ffsdrv.erase_sector = 
            (void (*)(void *)) 
            (offset_of_erase + (uint32) dst); 
        ffsdrv.write_halfword = 
            (void (*)(volatile uint16 *, uint16)) 
            (offset_of_write_halfword + (uint32) dst); 
    } 
    if (type == FFS_DRIVER_INTEL_SB) { 
        ffsdrv.init = 
            (int (*)(void)) 
            (offset_of_init + (uint32) dst); 
    } 
 
    ttw(ttr(TTrDrvOther, "ffsdrv_code, init, write, erase_sector = 0x%07x, 0x%07x, 0x%07x, 0x%07x" NL, 
            dst, (uint32) ffsdrv.init, 
            (uint32) ffsdrv.write_halfword, (uint32) ffsdrv.erase_sector)); 
 
#ifdef AMD_FLASH 
    ttw(ttr(TTrDrvOther, "amd_begin,   init, write, erase_sector = 0x%07x, 0x%07x, 0x%07x, 0x%07x" NL, 
            ffsdrv_ram_amd_begin, ffsdrv_null_init, 
            ffsdrv_ram_amd_sb_write_halfword, ffsdrv_ram_amd_sb_erase_sector)); 
#endif 
 
#if 0 //fangcj removed them 
#ifdef INTEL_FLASH 
    ttw(ttr(TTrDrvOther, "intel_begin, init, write, erase_sector = 0x%07x, 0x%07x, 0x%07x, 0x%07x" NL, 
            ffsdrv_ram_intel_begin, ffsdrv_ram_intel_sb_init, 
            ffsdrv_ram_intel_sb_write_halfword, ffsdrv_ram_intel_sb_erase_sector)); 
#endif 
#endif 
    // Copy the code to RAM 
    while (size--) 
        *dst++ = *src++; 
 
    ttw(ttr(TTrDrvOther, "}" NL)); 
 
    return 0; 
} 
 
#else // (TARGET == 0) 
 
void ffsdrv_device_id_read(volatile uint32 base_addr, uint16 *manufact, uint16 *device) {} 
/*Add by ZhangTing for PC simulator 2006-06-15*/ 
#ifdef AMD_FLASH 
void ffsdrv_amd_fg_write_halfword(volatile uint16 *addr, uint16 value){}; 
void ffsdrv_amd_fg_write_halfword_local(volatile uint16 *addr, uint16 value){}; 
void ffsdrv_amd_fg_write_halfword_unlock_bypass(volatile uint16 *addr, uint16 value){}; 
void ffsdrv_amd_fg_write(void *dst, const void *src, uint16 size){}; 
void ffsdrv_amd_fg_erase(uint8 block){}; 
void ffsdrv_amd_fg_erase_sector(void *dst){}; 
void ffsdrv_amd_fg_erase_sector_pseudo_sb(void *dst){}; 
void ffsdrv_amd_fg_write_suspend(void){}; 
void ffsdrv_amd_mb_write_halfword(volatile uint16 *addr, uint16 value){}; 
void ffsdrv_amd_mb_write(void *dst, const void *src, uint16 size){}; 
void ffsdrv_amd_mb_erase(uint8 block){}; 
void ffsdrv_amd_mb_erase_sector(void *dst){}; 
void ffsdrv_amd_mb_write_erase_suspend(void){}; 
void ffsdrv_amd_write_erase_resume(void){}; 
void ffsdrv_amd_mb_buffer_write(void *dst, const void *src, uint16 size){}; 
void ffsdrv_amd_mb_buffer_write_new(volatile UINT16 *dst_addr, UINT16 *src_addr, UINT16 size){}; 
#endif 
/*End*/ 
void ffsdrv_copy_code_to_ram(uint16 *dst, uint16 *src, int size) {} 
int  ffsdrv_driver_copy_to_ram(int type) { return 0; } 
void ffsdrv_query_cfi_data(uint32 base_addr, uint16 addr, uint16* data) {} 
 
#endif // (TARGET == 1) 
 
 
/****************************************************************************** 
 * Initialization 
 ******************************************************************************/ 
 
#ifdef AMD_FLASH 
const struct ffsdrv_s ffsdrv_amd_nor = { 
    ffsdrv_null_init, 
    ffsdrv_amd_fg_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_amd_fg_write_halfword, 
    ffsdrv_amd_fg_write, 
    ffsdrv_amd_fg_write_suspend,              /* use amd_write_end */ 
    ffsdrv_null_write_resume, 
    ffsdrv_amd_erase_suspend,     /* use amd_erase_suspend  */ 
    ffsdrv_amd_erase_resume,             /*  use amd_erase_resueme */ 
    ffsdrv_amd_fg_erase_sector 
}; 
 
//OMAPS62129 This is pseudo single bank driver for AMD_NOR, 3Mb in bank C in Flash can be reused for 
// code/data and so there is no wasted space in flash 
 
const struct ffsdrv_s ffsdrv_amd_nor_pseudo_sb = { 
    ffsdrv_null_init, 
    ffsdrv_amd_fg_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_amd_fg_write_halfword, 
    ffsdrv_amd_fg_write, 
    ffsdrv_amd_fg_write_suspend,              /* This is a null function anyway */ 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend,            /* use null driver as code cannot execute and so require suspend when FFS is erasing  */ 
    ffsdrv_null_erase_resume,             /*  use amd_erase_resueme */ 
    ffsdrv_amd_fg_erase_sector_pseudo_sb 
}; 
const struct ffsdrv_s ffsdrv_amd_mirror_bit = { 
	ffsdrv_amd_mb_init, 
	ffsdrv_amd_mb_erase, 
	ffsdrv_null_write_buffer, 
	ffsdrv_amd_mb_write_halfword, 
	ffsdrv_amd_mb_buffer_write, 
// 	ffsdrv_amd_mb_write, 
	ffsdrv_amd_fg_write_suspend,              /* This is a null function anyway */ 
    	ffsdrv_null_write_resume, 
    	ffsdrv_null_erase_suspend,            /* use null driver as code cannot execute and so require suspend when FFS is erasing  */ 
    	ffsdrv_null_erase_resume,            /*  use amd_erase_resueme */ 
	ffsdrv_amd_mb_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_amd = { 
    ffsdrv_null_init, 
    ffsdrv_amd_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_amd_write_halfword, 
    ffsdrv_amd_write, 
    ffsdrv_amd_write_end, // Wait for write to finish instead of suspend it 
    ffsdrv_null_write_resume, 
    ffsdrv_amd_erase_suspend, 
    ffsdrv_amd_erase_resume, 
    ffsdrv_amd_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_amd_sb = { 
    ffsdrv_null_init, 
    ffsdrv_ram_amd_sb_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_ram_amd_sb_write_halfword, 
    ffsdrv_generic_write, 
    ffsdrv_null_write_end, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_ram_amd_sb_erase_sector 
}; 
 
#endif 
 
const struct ffsdrv_s ffsdrv_sst = { 
    ffsdrv_null_init, 
    ffsdrv_sst_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_amd_write_halfword, // Use AMD driver function 
    ffsdrv_sst_write, 
    ffsdrv_amd_write_end,      // Use AMD driver function 
    ffsdrv_null_write_resume, 
    ffsdrv_sst_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_sst_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_sst_sb = { 
    ffsdrv_null_init, 
    ffsdrv_null_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_null_write_halfword, 
    ffsdrv_null_write, 
    ffsdrv_null_write_end, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_null_erase_sector 
}; 
 
#ifdef INTEL_FLASH 
const struct ffsdrv_s ffsdrv_intel = { 
    ffsdrv_null_init, 
    ffsdrv_intel_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_intel_write_halfword, 
//    ffsdrv_generic_write, 
    ffsdrv_intel_write, 
    ffsdrv_intel_write_suspend, 
    ffsdrv_intel_write_resume, 
    ffsdrv_intel_erase_suspend, 
    ffsdrv_intel_erase_resume, 
    ffsdrv_intel_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_intel_bw = { 
//    ffsdrv_null_init,//fangcj revised 2006/06/28 
    ffsdrv_intel_init,		 
    ffsdrv_intel_erase, 
    ffsdrv_intel_write_buffer_mode, 
    ffsdrv_intel_write_halfword, 
    ffsdrv_generic_buffer_write, 
    ffsdrv_intel_write_suspend, 
    ffsdrv_intel_write_resume, 
    ffsdrv_intel_erase_suspend, 
    ffsdrv_intel_erase_resume, 
    ffsdrv_intel_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_intel_sb = { 
    ffsdrv_null_init, 
    ffsdrv_ram_intel_sb_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_ram_intel_sb_write_halfword, 
    ffsdrv_generic_write, 
    ffsdrv_null_write_end, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_ram_intel_sb_erase_sector 
}; 
 
#endif 
 
const struct ffsdrv_s ffsdrv_null = { 
    ffsdrv_null_init, 
    ffsdrv_null_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_null_write_halfword, 
    ffsdrv_null_write, 
    ffsdrv_null_write_suspend, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_null_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_amd_pseudo_sb = { 
    ffsdrv_null_init, 
    ffsdrv_amd_pseudo_sb_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_amd_pseudo_sb_write_halfword, 
    ffsdrv_generic_write, 
    ffsdrv_null_write_end, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_amd_pseudo_sb_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_ram = { 
    ffsdrv_null_init, 
    ffsdrv_ram_erase, 
    ffsdrv_null_write_buffer, 
    ffsdrv_ram_write_halfword, 
    ffsdrv_ram_write, 
    ffsdrv_null_write_end, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_ram_erase_sector 
}; 
 
#if (TARGET == 0) 
const struct ffsdrv_s ffsdrv_test = { 
    ffsdrv_null_init, 
    ffsdrv_test_erase, 
    ffsdrv_test_write_buffer, 
    ffsdrv_test_write_halfword, 
    ffsdrv_generic_write, 
//    ffsdrv_generic_buffer_write, 
    ffsdrv_null_write_end, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_test_erase_sector 
}; 
 
const struct ffsdrv_s ffsdrv_test_bw = { 
    ffsdrv_null_init, 
    ffsdrv_test_erase, 
    ffsdrv_test_write_buffer, 
    ffsdrv_test_write_halfword, 
//    ffsdrv_generic_write, 
    ffsdrv_generic_buffer_write, 
    ffsdrv_null_write_end, 
    ffsdrv_null_write_resume, 
    ffsdrv_null_erase_suspend, 
    ffsdrv_null_erase_resume, 
    ffsdrv_test_erase_sector 
}; 
 
#endif 
 
// Note: This function is designed for little-endian memory addressing! 
void ffsdrv_write_byte(void *dst, uint8 value) 
{ 
    uint16 halfword; 
 
    tw(tr(TR_FUNC, TrDrvWrite, "ffsdrv_write_byte(0x%05x, 0x%x)\n", 
       (int) (addr2offset(dst)), value)); 
    ttw(str(TTrDrvWrite, "wbyte" NL)); 
 
    if ((int) dst & 1) 
        halfword =                (value << 8) | *((uint8 *) dst - 1); 
    else 
        halfword = (*((uint8 *) dst + 1) << 8) | (value); 
 
    ffsdrv.write_halfword((uint16 *) ((int) dst & ~1), halfword); 
} 
 
 
extern uint16 ffs_flash_manufact; 
extern uint16 ffs_flash_device; 
 
effs_t ffsdrv_init(void) 
{ 
    const struct ffsdrv_s *p; 
    const struct flash_info_s *flash = &flash_info[0]; 
    int error; 
 
    tw(tr(TR_BEGIN, TrDrvInit, "drv_init() {\n")); 
    ttw(str(TTrDrvOther, "ffsdrv_init() {" NL)); 
 
    dev.state = DEV_READ; 
    dev.binfo = 0; 
    dev.base = 0; 
    dev.numblocks = 0; 
 
    // If ffs_flash_device is zero, detect device automatically by copying 
    // the detect function into RAM and execute it from there... 
    if (ffs_flash_manufact == 0 && ffs_flash_device == 0) 
    { 
        #if (TARGET == 1)/*Add by ZhangTing for PC simulator 2006-08-26*/ 
        char detect_code[80]; 
        typedef void (*pf_t)(uint32, uint16 *, uint16 *); 
        pf_t myfp; 
        uint16 device_id[3]; 
 
        // Copy ffsdrv_device_id_read() function code to RAM. The only known 
        // way to determine the size of the code is to look either in the 
        // linker-generated map file or in the assember output file. 
        ffsdrv_copy_code_to_ram((uint16 *) detect_code, 
                                (uint16 *) &ffsdrv_device_id_read, 
                                sizeof(detect_code)); 
 
        // Combine bit 0 of the thumb mode function pointer with the address 
        // of the code in RAM. Then call the detect function in RAM. 
        myfp = (pf_t) (((int) &ffsdrv_device_id_read & 1) | (int) detect_code); 
        (*myfp)(flash_base_addr, &dev.manufact, device_id); 
 
        if ((dev.manufact == MANUFACT_AMD || dev.manufact == MANUFACT_FUJITSU) && 
		(device_id[0] == 0x227E || device_id[0] == 0x2C7E|| device_id[0] == 0x2b7E))  
	{ 
            // This is a multi-id device 
            dev.device = (device_id[1] << 8) | (device_id[2] & 0xFF); 
		ttr(TTrAll, "*******dev.manufact = 0x%x, dev.device = 0x%x" NL, dev.manufact, dev.device);	 
 
        } 
        else 
        { 
            dev.device = device_id[0]; 
		ttr(TTrAll, "__________dev.manufact = 0x%x, dev.device = 0x%x" NL, dev.manufact, dev.device); 
        } 
        #endif/*End*/ 
    } 
    else { 
        dev.manufact = ffs_flash_manufact; 
        dev.device   = ffs_flash_device; 
    } 
 
    tw(tr(TR_FUNC, TrDrvInit, "TARGET = %d\n", TARGET)); 
    tw(tr(TR_FUNC, TrDrvInit, "Looking up device (0x%2x,0x%4x): ", 
          dev.manufact, dev.device)); 
    while (flash->manufact) { 
        tw(tr(TR_NULL, TrDrvInit, "(0x%02x,0x%04x) ", 
              flash->manufact, flash->device)); 
        if (dev.manufact == flash->manufact && dev.device == flash->device) { 
            tw(tr(TR_NULL, TrDrvInit, "FOUND ")); 
            break; 
        } 
        flash++; 
    } 
 
    tw(tr(TR_NULL, TrDrvInit, "\n")); 
 
    if (flash->manufact == 0) { 
        tw(tr(TR_END, TrDrvInit, "} (%d)\n", EFFS_NODEVICE)); 
        return EFFS_NODEVICE; 
    } 
 
    dev.binfo = (struct block_info_s *) flash->binfo; 
    dev.base = (char *) flash->base; 
 
    if (flash->driver == FFS_DRIVER_RAM) { 
        // The ram image base addr can be set in 2 ways: In dev.c as a flash 
        // base addr in the configuration or applied through the variable 
        // ffs_ram_image_address. 
        if (dev.base == 0) { 
            if (ffs_ram_image_address == 0) { 
                tw(tr(TR_END, TrDrvInit, "} (%d)\n", EFFS_DRIVER)); 
                ttw(ttr(TTrDrvOther, "} (%d)" NL, EFFS_DRIVER)); 
                return EFFS_DRIVER; 
            } 
            dev.base = (char *) ffs_ram_image_address; 
        } 
    } 
 
    // Query flash write buffer size 
    if ((flash->driver == FFS_DRIVER_INTEL_BW) || (flash->driver == FFS_DRIVER_AMD_MIRROR_BIT)) /*Need modifiy? ZhangTing 2006-08-26*/ 
	{ 
        char cfi_code[80]; 
        typedef void (*pf_t)(uint32, uint16, uint16 *); 
        pf_t myfp; 
 
        ffsdrv_copy_code_to_ram((uint16 *) cfi_code, 
                                (uint16 *)&ffsdrv_query_cfi_data, 
                                sizeof(cfi_code)); 
 
        myfp = (pf_t) (((int) &ffsdrv_query_cfi_data & 1) | (int) cfi_code); 
        (*myfp)(flash_base_addr, 0x2A<<1, &dev.write_buffersize); 
 
        dev.write_buffersize = 1 << dev.write_buffersize; // Read as 2^n 
    } 
    else if (flash->driver == FFS_DRIVER_TEST)//FFS_DRIVER_TEST_BW  /*Need modifiy? ZhangTing 2006-08-26*/ 
        dev.write_buffersize = 64;  // PC simulation 
    else 
        dev.write_buffersize = 2;   // Default 2 bytes 
 
    if (dev.write_buffersize / 2 > FFS_WR_BUF_MAX_WORDS) { 
        tw(tr(TR_END, TrDrvInit, "} (%d)\n", EFFS_FWBUF2BIG)); 
        ttw(ttr(TTrDrvOther, "} (%d)" NL, EFFS_FWBUF2BIG)); 
        return EFFS_FWBUF2BIG; 
    } 
    ttw(ttr(TTrDrvOther, "Buffer size: 0x%x" NL, dev.write_buffersize)); 
 
    dev.numblocks = flash->numblocks; 
    dev.driver    = flash->driver; 
 
    // We assume that ALL blocks are of equal size 
    dev.blocksize_ld = dev.binfo[0].size_ld; 
    dev.blocksize    = (1 << dev.blocksize_ld); 
 
    dev.atomlog2 = FFS_ATOM_LOG2; 
    dev.atomsize = 1 << dev.atomlog2; 
    dev.atomnotmask = dev.atomsize - 1; 
 
#if (TARGET == 0) 
    if (dev.manufact == MANUFACT_TEST) 
        dev.base = ffsdrv_test_create(); 
 
    if (dev.driver == FFS_DRIVER_TEST_BW) 
        p = &ffsdrv_test_bw; 
    else 
        p = &ffsdrv_test; 
 
#else // (TARGET == 1) 
 
    // Initialize hardware independent driver functions array 
    switch (dev.driver) { 
#ifdef AMD_FLASH 
    case FFS_DRIVER_AMD:           p = &ffsdrv_amd; break; 
    case FFS_DRIVER_AMD_SB:        p = &ffsdrv_amd_sb; break; 
    case FFS_DRIVER_AMD_NOR:       p = &ffsdrv_amd_nor; break; 
    //OMAPS62129 add new driver type 
    case FFS_DRIVER_AMD_NOR_PSEUDO_SB: 
                                   p = &ffsdrv_amd_nor_pseudo_sb; break; 
    //OMAPS62129 end 
    case FFS_DRIVER_AMD_MIRROR_BIT:  
		ttr(TTrFatal, "ffsdrv_amd_mirror_bit is selected."); 
		p=&ffsdrv_amd_mirror_bit; 
		break; 
#endif	 
    case FFS_DRIVER_SST:           p = &ffsdrv_sst; break; 
    case FFS_DRIVER_SST_SB:        p = &ffsdrv_sst_sb; break; 
#ifdef INTEL_FLASH 
    case FFS_DRIVER_INTEL:         p = &ffsdrv_intel; break; 
    case FFS_DRIVER_INTEL_SB:      p = &ffsdrv_intel_sb; break; 
    case FFS_DRIVER_INTEL_BW:       
		ttr(TTrFatal, "ffsdrv_intel_bw is selected."); 
		p = &ffsdrv_intel_bw;  
		break; 
#endif	 
    case FFS_DRIVER_AMD_PSEUDO_SB: p = &ffsdrv_amd_pseudo_sb; break; 
    case FFS_DRIVER_RAM:           p = &ffsdrv_ram; break; 
    default:                       p = &ffsdrv_null; break; 
    } 
 
#endif // (TARGET == 0) 
 
    // Bind the driver functions 
    ffsdrv.init           = p->init; 
    ffsdrv.erase          = p->erase; 
    ffsdrv.write_buffer   = p->write_buffer; 
    ffsdrv.write_halfword = p->write_halfword; 
    ffsdrv.write          = p->write; 
    ffsdrv.write_suspend  = p->write_suspend; 
    ffsdrv.write_resume   = p->write_resume; 
    ffsdrv.erase_suspend  = p->erase_suspend; 
    ffsdrv.erase_resume   = p->erase_resume; 
    ffsdrv.erase_sector   = p->erase_sector; 
 
    switch (dev.driver) { 
#ifdef INTEL_FLASH 
    case FFS_DRIVER_INTEL_BW: 
        if (dev.write_buffersize <= 1) 
            // If the device not support buffer write use ffsdrv_intel_write 
            // instead 
            ffsdrv.write = ffsdrv_intel_write; 
        break; 
#endif		 
    default: break; 
    } 
 
    // Copy single bank driver code to RAM (and possibly re-bind some of the 
    // driver functions) 
    if (flash->driver != FFS_DRIVER_RAM) 
        error = ffsdrv_driver_copy_to_ram(dev.driver); 
 
    if (error >= 0) 
        error = ffsdrv.init(); 
 
    tw(tr(TR_FUNC, TrDrvInit, "dev.binfo     = 0x%x\n", (unsigned int) dev.binfo)); 
    tw(tr(TR_FUNC, TrDrvInit, "dev.base      = 0x%x\n", (unsigned int) dev.base)); 
    tw(tr(TR_FUNC, TrDrvInit, "dev.numblocks = %d\n", dev.numblocks)); 
    tw(tr(TR_FUNC, TrDrvInit, "dev.blocksize = %d\n", dev.blocksize)); 
    tw(tr(TR_FUNC, TrDrvInit, "dev.atomlog2/atomsize/atomnotmask = %d/%d/%x\n", 
          dev.atomlog2, dev.atomsize, dev.atomnotmask)); 
    tw(tr(TR_END, TrDrvInit, "} %d\n", error)); 
    ttw(ttr(TTrDrvOther, "} %d" NL, error)); 
 
#ifdef INTEL_FLASH 
   /* unlock all the blocks */ 
    ffsdrv_unlock_sectors(); 
#endif 
    return error; 
} 
 
void ffsdrv_unlock_sectors(void) 
{ 
	volatile uint16 *flash = (volatile uint16*)dev.base;	 
	uint32 unlockOffset; 
	uint32 i; 
	uint32 cpsr; 
	 
	/* device state is write state */ 
//     	OS_LOCK_MUTEX(&ffs_write_mutex); 
   	dev.state=DEV_WRITE;	 
 
  cpsr=int_disable(); 
 
	for(i=0;i>1; 
		/* Dissable the interrupts */ 
 
	   	flash[0]=0x0f0; 
	   	flash[0]= 0x0060; // sector unlock sequence 
	   	flash[0]= 0x0060; // sector unlock sequence 
	   	flash[unlockOffset]= 0x0060; // sector unlock sequence (address 6 = VIH) 
 
            /*TI david-yan for fix http://xmyanfa/bugzilla/show_bug.cgi?id=4188 
             2006-8-21*/ 
            if(i==(dev.numblocks-1))//we are unlocking the last blcok, so we should exit the unlock sequence 
            { 
                flash[0]=0x0f0;//david-yan 2006-8-21 
            } 
            /*TI david-yan added end*/ 
 #else 
 
 
           
          flash[dev.binfo[i].offset>>1] = 0x60; // sector unlock sequence 
          flash[dev.binfo[i].offset>>1] = 0xD0; // sector unlock sequence (address6 = VIH) 
          flash[dev.binfo[i].offset>>1] = 0xff ; //read array 
 #endif 
	   	 /* Enable the interrupts */ 
	}		 
 
  #ifdef INTEL_FLASH 
         // flash[0] = 0xFF;    //read array 
 
  #endif 
 
  int_enable(cpsr); 
 
	/* change the state to default read mode */ 
   	dev.state=DEV_READ; 
//	OS_UNLOCK_MUTEX(&ffs_write_mutex); 
 
	return; 
 
} 
 
/****************************************************************************** 
 * Interrupt Enable/Disable 
 ******************************************************************************/ 
 
// IMPORTANT NOTE! Apparently, locating this ARM assembly code at the top of 
// this file will make the compiler trash the A1 register between the calls 
// of arm_int_disable and arm_int_enable() thus crashing the whole system. 
// If the code is placed AFTER the usage of the functions, the compiler 
// saves the A1 register. Strange but true. 
 
// IMPORTANT NOTE! Apparently, another strange thing is that if the 
// functions are declared static, they don't work! 
 
// Executing code from RAM is NOT trivial when we need to jump between ROM 
// (flash) and RAM memory. The ARM only supports 26-bit relative branch 
// offsets. This is the reason why we have a local copy of the 
// arm_int_disable/enable() functions in this file plus each of the 
// single-bank drivers. 
 
#if (TARGET == 1) 
uint32 int_disable(void) 
{ 
    asm("        .state16"); 
    asm("        mov       A1, #0xC0"); 
    asm("        ldr       A2, tct_disable"); 
    asm("        bx        A2      "); 
 
    asm("        .align");  // word align 
    asm("tct_disable    .field     _TCT_Control_Interrupts+0,32"); 
    asm("               .global    _TCT_Control_Interrupts"); 
} 
 
void int_enable(uint32 cpsr) 
{ 
    asm("        .state16"); 
    asm("        ldr       A2, tct_enable"); 
    asm("        bx        A2      "); 
 
    asm("tct_enable     .field     _TCT_Control_Interrupts+0,32"); 
    asm("               .global    _TCT_Control_Interrupts"); 
} 
 
#else 
 
uint32 int_disable(void) { return 0; } 
void int_enable(uint32 tmp) {} 
 
#endif // (TARGET == 1)