📄 flash_nor_spansion.c
字号:
/********************************************************************** * flash_nor_spansion.c * * Spansion specific flash support * * This file implements Spansion specific functions and data * * Copyright (C) 2004-2006 Qualcomm, Inc. All rights Reserved * **********************************************************************//*=========================================================================== EDIT HISTORY FOR MODULE$Header: //depot/asic/msmshared/drivers/flash/MSM_FLASH.01.04/flash_nor_spansion.c#3 $ $DateTime: 2006/05/26 10:31:03 $ $Author: dhamimp $when who what, where, why-------- --- ----------------------------------------------------------2005-12-05 dp NOR Flash Driver Unification===========================================================================*//*=========================================================================== INCLUDE FILES FOR MODULE===========================================================================*/#include "customer.h"#include "comdef.h"#include <memory.h>#include "flash_nor_device.h"#include "flash_nor_msm.h"#include "clk.h"#include "flash_msg.h"/*lint -e613 -e668 upper layers already check for null pointers *///lint -e526 Don't warn about undefined extern symbols/* Checks every write and erase operation and make sures the flash contains the * correct data after each operation is finished. Slows down performance of the flash * driver but good to check it up especially when testing a new device */#undef FLASH_CHECK /* This should always be defined as the AMD part is broken with regards * to both fast write and suspend */#define FEATURE_EFS_AMD_DISABLE_FAST_WRITE#define FEATURE_EFS_AMD_SUSPEND_FETCH_BUG/* Spansion specifics */#define SPANSION_BUFFER_WRITE_SIZE 64#define SPANSION_MAX_TIMEOUT_CNT 20/* Low level status bits for AMD. */#define FS_AMD_DQ7 0x80#define FS_AMD_DQ6 0x40#define FS_AMD_DQ5 0x20#define FS_AMD_DQ3 0x08/* This function is located in flash_ram.c. */extern void flash_spansion_get_id_codes(volatile word *baseaddr, word *dest);extern void flash_peek_twice(volatile word *wptr, word *prior, word *current);/* Forward function prototypes */LOCAL flash_status fsi_spansion_configure (fsi_nor_device * nor_device, flash_ptr_type baseaddr);LOCAL flash_status fsi_spansion_erase_start (flash_ptr_type baseaddr, dword offset);LOCAL flash_status fsi_spansion_erase_status (flash_ptr_type eraseaddr);LOCAL flash_status fsi_spansion_suspend (flash_ptr_type wptr);LOCAL flash_status fsi_spansion_resume (flash_ptr_type eraseaddr);LOCAL flash_status fsi_spansion_write (byte *buffer, flash_ptr_type baseaddr, dword offset, dword count);LOCAL flash_status fsi_spansion_byte_write (byte *buffer, flash_ptr_type baseaddr, dword offset, dword count);LOCAL flash_status fsi_spansion_buffer_write (byte *buffer, flash_ptr_type baseaddr, dword offset, dword count);struct fsi_dev_ops flash_spansion_op_functions ={ fsi_spansion_configure, /* Configuration of hardware */ fs_nor_device_worded_read, /* Read operation. */ fsi_spansion_write, /* Write for Spansion. */ fsi_spansion_erase_start, /* Erase for Spansion. */ fsi_spansion_erase_status, /* Status for Spansion. */ fsi_spansion_suspend, fsi_spansion_resume,}; /* The fsi_nor_device structure will have minimal configuration information such as * number of manufacture codes, actual manufacture codes, word offset to NV area, * size in words of NV and the necessary function pointers. The other data such as * number of sectors and the sector layout, etc are computed at run time depending * on the flash used. So they have intialized value set as ZERO */ flash_geometry_info S29WS256N0SB_geometry ={ FLASH_SPANSION_FAMILY, FLASH_SIZE_32MB, FLASH_XIFACE_16, FLASH_WBUF_64, 3, { {4, 32768}, {254, 131072}, {4, 32768} }}; fsi_nor_device S29WS256N0SB = { "SPANSION S29WS256N0SB", 4, /* # of codes to match */ {1, 0x227e, 0x2230, 0x2200 }, /* Manufacture codes. */ 0, { // flash_efs_info FLASH_NOR_EFS2_START_BYTE_OFFSET, /* Byte offset to EFS2 area. */ (FLASH_NOR_EFS2_END_BYTE_OFFSET - FLASH_NOR_EFS2_START_BYTE_OFFSET)+1, /* EFS2 Size */ FLASH_NOR_EFS2_SECTOR_BYTE_SIZE, FLASH_NOR_EFS2_NUM_SECTORS, FS_DEVICE_WRITES_SIMPLE }, //&S29WS256N0SB_geometry, /* Use CFI to initialize flash geometry */ INIT_USING_CFI_AT_RUNTIME, &flash_spansion_op_functions}; /* Buffer initialized to value of erased data to use with memcmp() */ static uint8 erase_verify_buf[ERASE_VERIFY_BLK];/*===========================================================================FUNCTION SPANSION_PROBEDESCRIPTION Detect the presence of an SPANSION flash device.DEPENDENCIES RETURN VALUE NULL - No SPANSION device detected. otherwise - Pointer to fsi_nor_deviceSIDE EFFECTS None===========================================================================*/fsi_nor_device *spansion_probe (volatile word *baseaddr){ fsi_nor_device const **dev; word saved_int; word codes[4]; int ids, ids_found; /*----------------------------------------------------------------*/ /* Perform the ARM specific probe. */ INTLOCK_SAV (saved_int); /* With ARM/THUMB, calling a pointer determins ARM or Thumb mode by the low bit of the address. Adding one here causes it to call this as thumb code. */ /* Call function to go get the codes from the device */ flash_spansion_get_id_codes(baseaddr, codes); INTFREE_SAV (saved_int); /* Scan for the codes. Check indicated number of ids for each component in the list. Stop when we find a match. our indicator of no match. */ for (dev = spansion_parts; *dev != NULL; dev++) { ids_found = 0; for (ids = 0; ids < (*dev)->num_ids; ids++) { if (codes[ids] == (*dev)->codes[ids]) { /* Every time we find a match, increment ids_found. * When we exit the loop, if ids_found equals the number * of IDs we had to match we found the part */ ids_found++; } } if (ids_found == (*dev)->num_ids) { break; } } return (fsi_nor_device *) *dev;}/* spansion_probe *//*===========================================================================FUNCTION wait_data_pollingDESCRIPTION This function uses DQ7 polling to detect a program completion. to complete.DEPENDENCIES Uses perm.RETURN VALUE Returns : FLASH_SUCCESS if the operation succeeds FLASH_TIMEOUT if it fails.SIDE EFFECTS The watchdog is reset. The transport mechanism is checked for data while waiting. The device is left in normal (Read Array) ROM mode. This routine may take anywhere from microseconds to many seconds to execute, depending on the device operation.===========================================================================*/LOCAL flash_statuswait_data_polling (flash_ptr_type baseaddr, flash_ptr_type dest_wptr, word val){ int timeout_count = 0; word temp; /* Start a new vote. No toggles or timeouts seen so far. */ for (;;) { KICK_DOG_AND_CHECK_DATA(); temp = *dest_wptr; /* Wait for the write to complete. When it does, the Data Polling Bit will become equal to data bit #7 */ if ((temp & 0x0080) != (val & 0x0080)) { if ((temp & 0x0020) != 0) { timeout_count++; if (timeout_count >= 20) { /* Reset the device to get out of timeout mode */ *(baseaddr + 0x0) = 0x00F0; DPRINTF (("wdp: Timeout @ addr 0x%x \n", dest_wptr)); return FLASH_TIMEOUT; /* Device timed out. */ } } } else return FLASH_SUCCESS; } /* end for inner-loop */ DPRINTF (("wdp: end exit fail\n")); return FLASH_TIMEOUT; /*lint !e527 */}/*===========================================================================FUNCTION FSI_SPANSION_ERASEDESCRIPTION Initiate erase operation for an SPANSION part.DEPENDENCIES RETURN VALUE FLASH_SUCCESS - If erase operation was successfully initiatedSIDE EFFECTS None===========================================================================*/LOCAL flash_statusfsi_spansion_erase_start (flash_ptr_type baseaddr, dword offset){ volatile word *wptr; wptr = baseaddr + BYTE_TO_WORD_OFFSET(offset); wptr[0x555] = 0xAA; wptr[0x2AA] = 0x55; wptr[0x555] = 0x80; wptr[0x555] = 0xAA; wptr[0x2AA] = 0x55; wptr[0x555] = 0x30; /* Do not wait for DQ3 based on Spansion recommendation */ while((*wptr & FS_AMD_DQ3) == 0); //lint !e722 no stmts in while loop return FLASH_SUCCESS;}/*===========================================================================FUNCTION FSI_SPANSION_ERASE_STATUSDESCRIPTION Erase status for SPANSION components.DEPENDENCIES The device must be in the erasing state.RETURN VALUE FLASH_SUCCESS - The erase is finished, component is not in read state FLASH_OP_NOT_COMPLETE - The erase is still happening. ERR_FATAL - Something went wrong with the erase.SIDE EFFECTS None===========================================================================*/LOCAL flash_statusfsi_spansion_erase_status (flash_ptr_type eraseaddr ){ word prior, current; word failure_bit_seen=0; /*-----------------------------------------------------------------------*/ reread: /* * The SPANSION Erase Status bits are sensitive to read access from * ANY area of the flash, not just the EFS area (contrary to the data sheet). * * For this reason, it's critical that we mask interrupts, to prevent * other tasks from executing from flash, and also execute from RAM * to prevent this code itself to be fetched from ROM in between the reads. * * It's important that these two back-to-back accesses to flash be the * ONLY accesses to flash. -SH 6-May-2005 */ flash_peek_twice(eraseaddr, &prior, ¤t); /* Check the "toggling bit". */ if (((prior ^ current) & FS_AMD_DQ6) != 0) { /* It is toggling, check bit 5 to see if we hit an Erase Error */ if ((current & FS_AMD_DQ5) != 0) { if (failure_bit_seen) { /**************************************************************** This indicates that we saw DQ5 set, verified that the toggle bits are still toggling, and DQ5 continues to be set for a second time. This can only mean the flash device has failed and we can not recover. ****************************************************************/ ERR_FATAL ("Spansion Flash Device reported hard Erase Failure (DQ5).", 0,0,0); } else { /* The first time we see the DQ5 bit, we must check the toggle bits a the erase is still "running" and we did not just finish the erase */ failure_bit_seen = 1; goto reread; } } /* It is still busy. */ return FLASH_OP_NOT_COMPLETE; } else { /* * Here the DQ6 bit isn't toggling any more, indicating that Erase is * complete. (p49) * * There have been problems with the SPANSION part accidentally * indicating Erase Complete when it was not, in fact, complete. * * Rather than let the erase finish and corrupt the subsequent write * operation in chaotic ways, we trap any such failures here by * confirming that the block is readable and not still erasing. */ current = *eraseaddr; /* Read from erased block */ if (current != 0xFFFF) { ERR_FATAL ("SPANSION Erase completed prematurely! %x", current, 0, 0); }#ifdef FLASH_CHECK { dword i; #if defined(BUILD_JFLASH) || defined(BUILD_ARMPRG) extern dword curr_erase_sector_bsize; dword size= curr_erase_sector_bsize >> 1; #else extern fsi_nor_device *nor_device; dword size=(nor_device->efs_info.efs_blk_bsize >> 1); #endif for (i = 0; i < size; i++) { if(eraseaddr[i] != 0xFFFF) ERR_FATAL("Erase verify failed",0,0,0); if ((i % 16)==0) KICK_DOG_AND_CHECK_DATA(); } }#endif return FLASH_SUCCESS; }}/*====================================================================== * FUNCTION FSI_AMD_PEEK * * Read a single word from a given address. The read itself is performe * by code executing out of ram. This works around a problem with * consecutive read cycles messing up the bit toggle in the AMD. */static word amd_peek_code[] = { 0x8800, /* ldrh r0, [r0] */ 0x46F7, /* mov pc, lr */};#define FSI_AMD_PEEK(addr) \ (((word (*)()) ((unsigned char *) amd_peek_code + 1)) (addr))/*=========================================================================== * FUNCTION FSI_AMD_POKE * * Write a single word to a given address. The write itself is performed * by code executing out of RAM. This works around a ??? problem. */#ifdef FEATURE_EFS_AMD_SUSPEND_FETCH_BUGstatic word amd_poke_code[] = { 0x8001, /* strh r1, [r0] */ 0x46F7, /* mov pc, lr */};#define FSI_AMD_POKE(addr, value) \ (((word (*)()) ((unsigned char *) amd_poke_code + 1)) (addr, value))#endif/*===========================================================================FUNCTION FSI_SPANSION_SUSPENDDESCRIPTION Suspend an erase operation on SPANSION. Correctly handles the race condition of the suspend finishing near the time of this call.DEPENDENCIES The device be in erasing state and the fsi_erase_location has been set properly.RETURN VALUE FLASH_SUCCESS - The erase has completed. FLASH_OP_NOT_COMPLETE - The erase was suspended, reads may be done.SIDE EFFECTS None===========================================================================*/int retry_count = 0;dword wait_amd_count = 20;LOCAL flash_statusfsi_spansion_suspend (flash_ptr_type eraseaddr){ flash_status result = FLASH_SUCCESS; word tmp, tmp2; int done = 0; retry_count = 0; /* Issue the suspend erase command. */ // *wptr = 0xB0;#ifdef FEATURE_EFS_AMD_SUSPEND_FETCH_BUG INTLOCK (); FSI_AMD_POKE (eraseaddr, 0xB0); INTFREE ();#else *eraseaddr = 0xB0;#endif /* Wait 20 micro s for status to be valid. */ clk_busy_wait(wait_amd_count); /* Read the status register. */ retry: tmp = FSI_AMD_PEEK (eraseaddr); /* Now look checking for status indicating that the suspend either happened, or that the part was already done erasing. */ while (!done) { tmp2 = tmp; tmp = FSI_AMD_PEEK (eraseaddr); /* Possible conditions. */ switch ((tmp ^ tmp2) & 0x44) { case 0x40: case 0x44: /* Bit 6 changing indicates that the erase hasn't suspended yet. */ break; case 0x04: /* The suspend was successful. */ result = FLASH_OP_NOT_COMPLETE; done = 1; break; case 0x00: /* The erase has finished. The suspend didn't really happen. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -