summaryrefslogtreecommitdiffstats
path: root/shared/opensource/flash/cfiflash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/opensource/flash/cfiflash.c')
-rwxr-xr-xshared/opensource/flash/cfiflash.c1112
1 files changed, 1112 insertions, 0 deletions
diff --git a/shared/opensource/flash/cfiflash.c b/shared/opensource/flash/cfiflash.c
new file mode 100755
index 0000000..efd08de
--- /dev/null
+++ b/shared/opensource/flash/cfiflash.c
@@ -0,0 +1,1112 @@
+/*
+ Copyright 2000-2010 Broadcom Corporation
+
+ Unless you and Broadcom execute a separate written software license
+ agreement governing use of this software, this software is licensed
+ to you under the terms of the GNU General Public License version 2
+ (the “GPL?, available at http://www.broadcom.com/licenses/GPLv2.php,
+ with the following added to such license:
+
+ As a special exception, the copyright holders of this software give
+ you permission to link this software with independent modules, and to
+ copy and distribute the resulting executable under terms of your
+ choice, provided that you also meet, for each linked independent
+ module, the terms and conditions of the license of that module.
+ An independent module is a module which is not derived from this
+ software. The special exception does not apply to any modifications
+ of the software.
+
+ Notwithstanding the above, under no circumstances may you combine this
+ software in any way with any other Broadcom software provided under a
+ license other than the GPL, without Broadcom's express prior written
+ consent.
+*/
+
+/** Includes. */
+#ifdef _CFE_
+#include "lib_types.h"
+#include "lib_printf.h"
+#include "lib_string.h"
+#include "cfe_timer.h"
+#include "bcm_map.h"
+#define CFI_USLEEP(x) cfe_usleep(x)
+#define printk printf
+#else // linux
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+#include <linux/semaphore.h>
+#else
+#include <asm/semaphore.h>
+#endif
+#include <asm/delay.h>
+#include <bcm_map_part.h>
+#define CFI_USLEEP(x) udelay(x)
+#endif
+
+#include "bcmtypes.h"
+#include "bcm_hwdefs.h"
+#include "flash_api.h"
+
+/** Defines. **/
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef _CFE_
+static DECLARE_MUTEX(cfi_flash_lock);
+#define CF_FLASH_SCHED_BYTES 512
+static int cfi_flash_sched = 0; /* voluntary schedule() disabled by default */
+#endif
+
+#define MAXSECTORS 1024 /* maximum number of sectors supported */
+
+/* Standard Boolean declarations */
+#define TRUE 1
+#define FALSE 0
+
+/* Define different type of flash */
+#define FLASH_UNDEFINED 0
+#define FLASH_AMD 1
+#define FLASH_INTEL 2
+#define FLASH_SST 3
+
+/* Command codes for the flash_command routine */
+#define FLASH_RESET 0 /* reset to read mode */
+#define FLASH_READ_ID 1 /* read device ID */
+#define FLASH_CFIQUERY 2 /* CFI query */
+#define FLASH_UB 3 /* go into unlock bypass mode */
+#define FLASH_PROG 4 /* program a unsigned short */
+#define FLASH_UBRESET 5 /* reset to read mode from unlock bypass mode */
+#define FLASH_SERASE 6 /* sector erase */
+#define FLASH_WRITE_BUF 7 /* write to buffer */
+
+/* Return codes from flash_status */
+#define STATUS_READY 0 /* ready for action */
+#define STATUS_TIMEOUT 1 /* operation timed out */
+
+/* A list of AMD compatible device ID's - add others as needed */
+#define ID_AM29DL800T 0x224A
+#define ID_AM29DL800B 0x22CB
+#define ID_AM29LV800T 0x22DA
+#define ID_AM29LV800B 0x225B
+#define ID_AM29LV400B 0x22BA
+#define ID_AM29LV200BT 0x223B
+
+#define ID_AM29LV160B 0x2249
+#define ID_AM29LV160T 0x22C4
+
+#define ID_AM29LV320T 0x22F6
+#define ID_MX29LV320AT 0x22A7
+#define ID_AM29LV320B 0x22F9
+#define ID_MX29LV320AB 0x22A8
+#define ID_MX29LV640BT 0x22C9
+
+#define ID_AM29LV320M 0x227E
+#define ID_AM29LV320MB 0x2200
+#define ID_AM29LV320MT 0x2201
+
+#define ID_SST39VF200A 0x2789
+#define ID_SST39VF400A 0x2780
+#define ID_SST39VF800A 0x2781
+#define ID_SST39VF1601 0x234B
+#define ID_SST39VF3201 0x235B
+#define ID_SST39VF6401 0x236B
+
+/* A list of Intel compatible device ID's - add others as needed */
+#define ID_I28F160C3T 0x88C2
+#define ID_I28F160C3B 0x88C3
+#define ID_I28F320C3T 0x88C4
+#define ID_I28F320C3B 0x88C5
+#define ID_I28F640J3 0x8916
+
+#define ID_M29W640FB 0x22FD
+
+#define CFI_FLASH_DEVICES \
+ {{ID_AM29DL800T, "AM29DL800T"}, \
+ {ID_AM29DL800B, "AM29DL800B"}, \
+ {ID_AM29LV800T, "AM29LV800T"}, \
+ {ID_AM29LV800B, "AM29LV800B"}, \
+ {ID_AM29LV400B, "AM29LV400B"}, \
+ {ID_AM29LV200BT, "AM29LV200BT"}, \
+ {ID_AM29LV160B, "AM29LV160B"}, \
+ {ID_AM29LV160T, "AM29LV160T"}, \
+ {ID_AM29LV320T, "AM29LV320T"}, \
+ {ID_MX29LV320AT, "MX29LV320AT"}, \
+ {ID_AM29LV320B, "AM29LV320B"}, \
+ {ID_MX29LV320AB, "MX29LV320AB"}, \
+ {ID_AM29LV320M, "AM29LV320M"}, \
+ {ID_AM29LV320MB, "AM29LV320MB"}, \
+ {ID_AM29LV320MT, "AM29LV320MT"}, \
+ {ID_MX29LV640BT, "MX29LV640BT"}, \
+ {ID_SST39VF200A, "SST39VF200A"}, \
+ {ID_SST39VF400A, "SST39VF400A"}, \
+ {ID_SST39VF800A, "SST39VF800A"}, \
+ {ID_SST39VF1601, "SST39VF1601"}, \
+ {ID_SST39VF3201, "SST39VF3201"}, \
+ {ID_SST39VF6401, "SST39VF6401"}, \
+ {ID_I28F160C3T, "I28F160C3T"}, \
+ {ID_I28F160C3B, "I28F160C3B"}, \
+ {ID_I28F320C3T, "I28F320C3T"}, \
+ {ID_I28F320C3B, "I28F320C3B"}, \
+ {ID_I28F640J3, "I28F640J3"}, \
+ {ID_M29W640FB, "STM29W640FB"}, \
+ {0, ""} \
+ }
+
+/** Structs. **/
+/* A structure for identifying a flash part. There is one for each
+ * of the flash part definitions. We need to keep track of the
+ * sector organization, the address register used, and the size
+ * of the sectors.
+ */
+struct flashinfo {
+ char *name; /* "Am29DL800T", etc. */
+ unsigned long addr; /* physical address, once translated */
+ int areg; /* Can be set to zero for all parts */
+ int nsect; /* # of sectors -- 19 in LV, 22 in DL */
+ int bank1start; /* first sector # in bank 1 */
+ int bank2start; /* first sector # in bank 2, if DL part */
+ struct {
+ long size; /* # of bytes in this sector */
+ long base; /* offset from beginning of device */
+ int bank; /* 1 or 2 for DL; 1 for LV */
+ } sec[MAXSECTORS]; /* per-sector info */
+ int write_buffer_size; /* max size of multi byte write */
+};
+
+/*
+ * This structure holds all CFI query information as defined
+ * in the JEDEC standard. All information up to
+ * primary_extended_query is standard among all manufactures
+ * with CFI enabled devices.
+ */
+
+struct cfi_query {
+ int num_erase_blocks; /* Number of sector defs. */
+ long device_size; /* Device size in bytes */
+ struct {
+ unsigned long sector_size; /* byte size of sector */
+ int num_sectors; /* Num sectors of this size */
+ } erase_block[8]; /* Max of 256, but 8 is good */
+ int write_buffer_size; /* max size of multi byte write */
+};
+
+struct flash_name_from_id {
+ unsigned short fnfi_id;
+ char fnfi_name[30];
+};
+
+
+/** Prototypes. **/
+int cfi_flash_init(flash_device_info_t **flash_info);
+static int cfi_flash_sector_erase_int(unsigned short sector);
+static int cfi_flash_read_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int numbytes);
+static int cfi_flash_write_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int numbytes);
+static int cfi_flash_get_numsectors(void);
+static int cfi_flash_get_sector_size(unsigned short sector);
+static unsigned char *cfi_flash_get_memptr(unsigned short sector);
+static int cfi_flash_get_blk(int addr);
+static int cfi_flash_get_total_size(void);
+static void cfi_flash_command(int command, unsigned short sector, int offset,
+ unsigned short data, unsigned short *);
+static int cfi_flash_write(unsigned short sector, int offset, unsigned char *buf,
+ int nbytes);
+static int cfi_flash_write_to_buffer(unsigned short sector, unsigned char *buf);
+static int cfi_flash_wait(unsigned short sector, int offset,unsigned short data);
+static unsigned short cfi_flash_get_device_id(void);
+static int cfi_flash_get_cfi(struct cfi_query *query, unsigned short *cfi_struct,
+ int flashFamily);
+static int cfi_memcmp_sched(unsigned char *s1, unsigned char *s2, int nbytes);
+
+/** Variables. **/
+static flash_device_info_t flash_cfi_dev =
+ {
+ 0xffff,
+ FLASH_IFC_PARALLEL,
+ "",
+ cfi_flash_sector_erase_int,
+ cfi_flash_read_buf,
+ cfi_flash_write_buf,
+ cfi_flash_get_numsectors,
+ cfi_flash_get_sector_size,
+ cfi_flash_get_memptr,
+ cfi_flash_get_blk,
+ cfi_flash_get_total_size
+ };
+
+/*********************************************************************/
+/* 'meminfo' should be a pointer, but most C compilers will not */
+/* allocate static storage for a pointer without calling */
+/* non-portable functions such as 'new'. We also want to avoid */
+/* the overhead of passing this pointer for every driver call. */
+/* Systems with limited heap space will need to do this. */
+/*********************************************************************/
+static struct flashinfo meminfo; /* Flash information structure */
+static int flashFamily = FLASH_UNDEFINED;
+static int totalSize = 0;
+static struct cfi_query query;
+
+static unsigned short cfi_data_struct_29W160[] = {
+ 0x0020, 0x0049, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0x0051, 0x0052, 0x0059, 0x0002, 0x0000, 0x0040, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0027, 0x0036, 0x0000, 0x0000, 0x0004,
+ 0x0000, 0x000a, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0015,
+ 0x0002, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0000, 0x0040,
+ 0x0000, 0x0001, 0x0000, 0x0020, 0x0000, 0x0000, 0x0000, 0x0080,
+ 0x0000, 0x001e, 0x0000, 0x0000, 0x0001, 0xffff, 0xffff, 0xffff,
+ 0x0050, 0x0052, 0x0049, 0x0031, 0x0030, 0x0000, 0x0002, 0x0001,
+ 0x0001, 0x0004, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0x0002,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x0888, 0x252b, 0x8c84, 0x7dbc, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+};
+
+static UINT16 cfi_data_struct_29W200[] = {
+ 0x0020, 0x0049, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0x0051, 0x0052, 0x0059, 0x0002, 0x0000, 0x0040, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0027, 0x0036, 0x0000, 0x0000, 0x0004,
+ 0x0000, 0x000a, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0015,
+ 0x0002, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0000, 0x0040,
+ 0x0000, 0x0001, 0x0000, 0x0020, 0x0000, 0x0000, 0x0000, 0x0080,
+ 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0xffff, 0xffff, 0xffff,
+ 0x0050, 0x0052, 0x0049, 0x0031, 0x0030, 0x0000, 0x0002, 0x0001,
+ 0x0001, 0x0004, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0x0002,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x0888, 0x252b, 0x8c84, 0x7dbc, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+};
+
+static UINT16 cfi_data_struct_26LV800B[] = {
+ 0x0020, 0x0049, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0x0051, 0x0052, 0x0059, 0x0002, 0x0000, 0x0040, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0027, 0x0036, 0x0000, 0x0000, 0x0004,
+ 0x0000, 0x000a, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0015,
+ 0x0002, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0000, 0x0040,
+ 0x0000, 0x0001, 0x0000, 0x0020, 0x0000, 0x0000, 0x0000, 0x0080,
+ 0x0000, 0x000e, 0x0000, 0x0000, 0x0001, 0xffff, 0xffff, 0xffff,
+ 0x0050, 0x0052, 0x0049, 0x0031, 0x0030, 0x0000, 0x0002, 0x0001,
+ 0x0001, 0x0004, 0x0000, 0x0000, 0x0000, 0xffff, 0xffff, 0x0002,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0x0888, 0x252b, 0x8c84, 0x7dbc, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
+ 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
+};
+
+static struct flash_name_from_id fnfi[] = CFI_FLASH_DEVICES;
+
+/*********************************************************************/
+/* Init_flash is used to build a sector table from the information */
+/* provided through the CFI query. This information is translated */
+/* from erase_block information to base:offset information for each */
+/* individual sector. This information is then stored in the meminfo */
+/* structure, and used throughout the driver to access sector */
+/* information. */
+/* */
+/* This is more efficient than deriving the sector base:offset */
+/* information every time the memory map switches (since on the */
+/* development platform can only map 64k at a time). If the entire */
+/* flash memory array can be mapped in, then the addition static */
+/* allocation for the meminfo structure can be eliminated, but the */
+/* drivers will have to be re-written. */
+/* */
+/* The meminfo struct occupies 653 bytes of heap space, depending */
+/* on the value of the define MAXSECTORS. Adjust to suit */
+/* application */
+/*********************************************************************/
+int cfi_flash_init(flash_device_info_t **flash_info)
+{
+ struct flash_name_from_id *fnfi_ptr;
+ int i=0, j=0, count=0;
+ int basecount=0L;
+ unsigned short device_id;
+ int flipCFIGeometry = FALSE;
+
+ *flash_info = &flash_cfi_dev;
+
+ /* First, assume
+ * a single 8k sector for sector 0. This is to allow
+ * the system to perform memory mapping to the device,
+ * even though the actual physical layout is unknown.
+ * Once mapped in, the CFI query will produce all
+ * relevant information.
+ */
+ meminfo.addr = 0L;
+ meminfo.areg = 0;
+ meminfo.nsect = 1;
+ meminfo.bank1start = 0;
+ meminfo.bank2start = 0;
+
+ meminfo.sec[0].size = 8192;
+ meminfo.sec[0].base = 0x00000;
+ meminfo.sec[0].bank = 1;
+
+ cfi_flash_command(FLASH_RESET, 0, 0, 0, NULL);
+
+ flash_cfi_dev.flash_device_id = device_id = cfi_flash_get_device_id();
+ flash_cfi_dev.flash_device_name[0] = '\0';
+ switch (device_id) {
+ case ID_I28F160C3B:
+ case ID_I28F320C3B:
+ case ID_I28F160C3T:
+ case ID_I28F320C3T:
+ case ID_I28F640J3:
+ flashFamily = FLASH_INTEL;
+ break;
+ case ID_AM29DL800B:
+ case ID_AM29LV800B:
+ case ID_AM29LV400B:
+ case ID_AM29LV160B:
+ case ID_AM29LV320B:
+ case ID_MX29LV320AB:
+ case ID_AM29LV320MB:
+ case ID_AM29DL800T:
+ case ID_AM29LV800T:
+ case ID_AM29LV160T:
+ case ID_AM29LV320T:
+ case ID_MX29LV320AT:
+ case ID_AM29LV320MT:
+ case ID_AM29LV200BT:
+ case ID_MX29LV640BT:
+ case ID_M29W640FB:
+ flashFamily = FLASH_AMD;
+ break;
+ case ID_SST39VF200A:
+ case ID_SST39VF400A:
+ case ID_SST39VF800A:
+ case ID_SST39VF1601:
+ case ID_SST39VF3201:
+ case ID_SST39VF6401:
+ flashFamily = FLASH_SST;
+ break;
+ default:
+ return FLASH_API_ERROR;
+ }
+
+ if (cfi_flash_get_cfi(&query, 0, flashFamily) == -1) {
+ switch(device_id) {
+ case ID_AM29LV160T:
+ case ID_AM29LV160B:
+ cfi_flash_get_cfi(&query, cfi_data_struct_29W160, flashFamily);
+ break;
+ case ID_AM29LV200BT:
+ cfi_flash_get_cfi(&query, cfi_data_struct_29W200, flashFamily);
+ break;
+ case ID_AM29LV800B:
+ cfi_flash_get_cfi(&query, cfi_data_struct_26LV800B, flashFamily);
+ strcpy( flash_cfi_dev.flash_device_name, "MX26LV800B" );
+ break;
+ default:
+ return FLASH_API_ERROR;
+ }
+ }
+
+ // need to determine if it top or bottom boot here
+ switch (device_id)
+ {
+ case ID_AM29DL800B:
+ case ID_AM29LV800B:
+ case ID_AM29LV400B:
+ case ID_AM29LV160B:
+ case ID_AM29LV320B:
+ case ID_MX29LV320AB:
+ case ID_AM29LV320MB:
+ case ID_I28F160C3B:
+ case ID_I28F320C3B:
+ case ID_I28F640J3:
+ case ID_I28F160C3T:
+ case ID_I28F320C3T:
+ case ID_SST39VF3201:
+ case ID_SST39VF6401:
+ case ID_SST39VF200A:
+ case ID_SST39VF400A:
+ case ID_SST39VF800A:
+ case ID_M29W640FB:
+ flipCFIGeometry = FALSE;
+ break;
+ case ID_AM29DL800T:
+ case ID_AM29LV800T:
+ case ID_AM29LV160T:
+ case ID_AM29LV320T:
+ case ID_MX29LV320AT:
+ case ID_AM29LV320MT:
+ case ID_AM29LV200BT:
+ case ID_SST39VF1601:
+ case ID_MX29LV640BT:
+ flipCFIGeometry = TRUE;
+ break;
+ default:
+ return FLASH_API_ERROR;
+ }
+
+ count=0;basecount=0L;
+
+ if (!flipCFIGeometry)
+ {
+
+ for (i=0; i<query.num_erase_blocks && basecount < query.device_size; i++) {
+ for(j=0; j<query.erase_block[i].num_sectors; j++) {
+ meminfo.sec[count].size = (int) query.erase_block[i].sector_size;
+ meminfo.sec[count].base = (int) basecount;
+ basecount += (int) query.erase_block[i].sector_size;
+ count++;
+ }
+ }
+ }
+ else
+ {
+ for (i = (query.num_erase_blocks - 1); i >= 0 && basecount < query.device_size; i--) {
+ for(j=0; j<query.erase_block[i].num_sectors; j++) {
+ meminfo.sec[count].size = (int) query.erase_block[i].sector_size;
+ meminfo.sec[count].base = (int) basecount;
+ basecount += (int) query.erase_block[i].sector_size;
+ count++;
+ }
+ }
+ }
+
+ meminfo.nsect = count;
+ totalSize = meminfo.sec[count-1].base + meminfo.sec[count-1].size;
+
+ if( flash_cfi_dev.flash_device_name[0] == '\0' ) {
+ for( fnfi_ptr = fnfi; fnfi_ptr->fnfi_id != 0; fnfi_ptr++ ) {
+ if( fnfi_ptr->fnfi_id == device_id ) {
+ strcpy( flash_cfi_dev.flash_device_name, fnfi_ptr->fnfi_name );
+ break;
+ }
+ }
+ }
+
+ meminfo.write_buffer_size = query.write_buffer_size;
+
+ return (FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* Flash_sector_erase_int() is identical to flash_sector_erase(), */
+/* except it will wait until the erase is completed before returning */
+/* control to the calling function. This can be used in cases which */
+/* require the program to hold until a sector is erased, without */
+/* adding the wait check external to this function. */
+/*********************************************************************/
+static int cfi_flash_sector_erase_int(unsigned short sector)
+{
+ int i;
+
+#ifndef _CFE_
+ down(&cfi_flash_lock);
+#endif
+
+ for( i = 0; i < 3; i++ ) {
+ cfi_flash_command(FLASH_SERASE, sector, 0, 0, NULL);
+ if (cfi_flash_wait(sector, 0, 0xffff) == STATUS_READY)
+ break;
+ }
+
+#ifndef _CFE_
+ up(&cfi_flash_lock);
+#endif
+
+ return(FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* flash_read_buf() reads buffer of data from the specified */
+/* offset from the sector parameter. */
+/*********************************************************************/
+static int cfi_flash_read_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int numbytes)
+{
+ unsigned char *fwp;
+#ifndef _CFE_
+ unsigned int bytes_read = CF_FLASH_SCHED_BYTES;
+
+ down(&cfi_flash_lock);
+#endif
+
+ fwp = (unsigned char *) cfi_flash_get_memptr(sector);
+
+ while (numbytes) {
+ *buffer++ = *(fwp + offset);
+ numbytes--;
+ fwp++;
+#ifndef _CFE_
+ if (!in_interrupt()) {
+ /* Voluntary schedule() if task tagged as need_sched or 512 bytes read */
+ /* Must force need_resched or else schedule() is in-effective */
+ if ( cfi_flash_sched ) {
+ if ( (need_resched()) || (bytes_read >= CF_FLASH_SCHED_BYTES) ) {
+ bytes_read=0;
+ set_tsk_need_resched(current); /* fake need_resched() to force schedule() */
+ schedule();
+ }
+ else
+ bytes_read++;
+ }
+ }
+#endif
+ }
+
+#ifndef _CFE_
+ up(&cfi_flash_lock);
+#endif
+
+ return (FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* cfi_memcmp_sched: invokes memcmp with schedule() invocations */
+/*********************************************************************/
+static int cfi_memcmp_sched(unsigned char *s1, unsigned char *s2, int nb)
+{
+#ifndef _CFE_
+ const unsigned int sched_chunk = 4 * 1024;
+ size_t nbytes;
+ int ret = 0;
+
+ while ( nb > 0 )
+ {
+ if (!in_interrupt()) {
+ set_tsk_need_resched(current);
+ schedule();
+ }
+
+ nbytes = (nb > sched_chunk) ? sched_chunk : nb;
+
+ if ( (ret = memcmp( s1, s2, nbytes )) != 0 )
+ break;
+
+ s1 += sched_chunk;
+ s2 += sched_chunk;
+ nb -= sched_chunk;
+ }
+
+ return ret;
+#else
+ return memcmp( s1, s2, nb );
+#endif
+}
+
+/*********************************************************************/
+/* flash_write_buf() utilizes */
+/* the unlock bypass mode of the flash device. This can remove */
+/* significant overhead from the bulk programming operation, and */
+/* when programming bulk data a sizeable performance increase can be */
+/* observed. */
+/*********************************************************************/
+static int cfi_flash_write_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int numbytes)
+{
+ int ret = FLASH_API_ERROR;
+ int i;
+ unsigned char *p = cfi_flash_get_memptr(sector) + offset;
+ int write_buf_method = 0;
+
+ if( meminfo.write_buffer_size != 0 && offset == 0 &&
+ (ID_M29W640FB != cfi_flash_get_device_id()) &&
+ numbytes == cfi_flash_get_sector_size(sector) )
+ {
+ write_buf_method = 1;
+ }
+
+ /* After writing the flash block, compare the contents to the source
+ * buffer. Try to write the sector successfully up to three times.
+ */
+ for( i = 0; i < 3; i++ ) {
+ if( write_buf_method )
+ ret = cfi_flash_write_to_buffer(sector, buffer);
+ else
+ ret = cfi_flash_write(sector, offset, buffer, numbytes);
+ if( !cfi_memcmp_sched( p, buffer, numbytes ) )
+ break;
+ /* Erase and try again */
+ cfi_flash_sector_erase_int(sector);
+ ret = FLASH_API_ERROR;
+ }
+
+ if( ret == FLASH_API_ERROR )
+ printk( "Flash write error. Verify failed\n" );
+
+ return( ret );
+}
+
+/*********************************************************************/
+/* Usefull funtion to return the number of sectors in the device. */
+/* Can be used for functions which need to loop among all the */
+/* sectors, or wish to know the number of the last sector. */
+/*********************************************************************/
+static int cfi_flash_get_numsectors(void)
+{
+ return meminfo.nsect;
+}
+
+/*********************************************************************/
+/* flash_get_sector_size() is provided for cases in which the size */
+/* of a sector is required by a host application. The sector size */
+/* (in bytes) is returned in the data location pointed to by the */
+/* 'size' parameter. */
+/*********************************************************************/
+static int cfi_flash_get_sector_size(unsigned short sector)
+{
+ return meminfo.sec[sector].size;
+}
+
+/*********************************************************************/
+/* The purpose of flash_get_memptr() is to return a memory pointer */
+/* which points to the beginning of memory space allocated for the */
+/* flash. All function pointers are then referenced from this */
+/* pointer. */
+/* */
+/* Different systems will implement this in different ways: */
+/* possibilities include: */
+/* - A direct memory pointer */
+/* - A pointer to a memory map */
+/* - A pointer to a hardware port from which the linear */
+/* address is translated */
+/* - Output of an MMU function / service */
+/* */
+/* Also note that this function expects the pointer to a specific */
+/* sector of the device. This can be provided by dereferencing */
+/* the pointer from a translated offset of the sector from a */
+/* global base pointer (e.g. flashptr = base_pointer + sector_offset)*/
+/* */
+/* Important: Many AMD flash devices need both bank and or sector */
+/* address bits to be correctly set (bank address bits are A18-A16, */
+/* and sector address bits are A18-A12, or A12-A15). Flash parts */
+/* which do not need these bits will ignore them, so it is safe to */
+/* assume that every part will require these bits to be set. */
+/*********************************************************************/
+static unsigned char *cfi_flash_get_memptr(unsigned short sector)
+{
+ unsigned char *memptr = (unsigned char*)
+ (FLASH_BASE + meminfo.sec[sector].base);
+
+ return (memptr);
+}
+
+/*********************************************************************/
+/* The purpose of flash_get_blk() is to return a the block number */
+/* for a given memory address. */
+/*********************************************************************/
+static int cfi_flash_get_blk(int addr)
+{
+ int blk_start, i;
+ int last_blk = cfi_flash_get_numsectors();
+ int relative_addr = addr - (int) FLASH_BASE;
+
+ for(blk_start=0, i=0; i < relative_addr && blk_start < last_blk; blk_start++)
+ i += cfi_flash_get_sector_size(blk_start);
+
+ if( i > relative_addr )
+ {
+ blk_start--; // last blk, dec by 1
+ }
+ else
+ if( blk_start == last_blk )
+ {
+ printk("Address is too big.\n");
+ blk_start = -1;
+ }
+
+ return( blk_start );
+}
+
+/************************************************************************/
+/* The purpose of flash_get_total_size() is to return the total size of */
+/* the flash */
+/************************************************************************/
+static int cfi_flash_get_total_size(void)
+{
+ return totalSize;
+}
+
+/*********************************************************************/
+/* Flash_command() is the main driver function. It performs */
+/* every possible command available to AMD B revision */
+/* flash parts. Note that this command is not used directly, but */
+/* rather called through the API wrapper functions provided below. */
+/*********************************************************************/
+static void cfi_flash_command(int command, unsigned short sector, int offset,
+ unsigned short data, unsigned short *dataptr)
+{
+ volatile unsigned short *flashptr;
+ volatile unsigned short *flashbase;
+ unsigned short i, len;
+
+ flashptr = (unsigned short *) cfi_flash_get_memptr(sector);
+ flashbase = (unsigned short *) cfi_flash_get_memptr(0);
+
+ switch (flashFamily) {
+ case FLASH_UNDEFINED:
+ /* These commands should work for AMD, Intel and SST flashes */
+ switch (command) {
+ case FLASH_RESET:
+ flashptr[0] = 0xF0;
+ flashptr[0] = 0xFF;
+ break;
+ case FLASH_READ_ID:
+ flashptr[0x5555] = 0xAA; /* unlock 1 */
+ flashptr[0x2AAA] = 0x55; /* unlock 2 */
+ flashptr[0x5555] = 0x90;
+ break;
+ case FLASH_CFIQUERY:
+ flashbase[0x5555] = 0xAA; /* unlock 1 */
+ flashbase[0x2AAA] = 0x55; /* unlock 2 */
+ flashbase[0x5555] = 0x90;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FLASH_AMD:
+ switch (command) {
+ case FLASH_RESET:
+ flashptr[0] = 0xF0;
+ break;
+ case FLASH_READ_ID:
+ flashptr[0x555] = 0xAA; /* unlock 1 */
+ flashptr[0x2AA] = 0x55; /* unlock 2 */
+ flashptr[0x555] = 0x90;
+ break;
+ case FLASH_CFIQUERY:
+ flashptr[0x55] = 0x98;
+ break;
+ case FLASH_UB:
+ flashptr[0x555] = 0xAA; /* unlock 1 */
+ flashptr[0x2AA] = 0x55; /* unlock 2 */
+ flashptr[0x555] = 0x20;
+ break;
+ case FLASH_PROG:
+ flashptr[0] = 0xA0;
+ flashptr[offset/2] = data;
+ break;
+ case FLASH_UBRESET:
+ flashptr[0] = 0x90;
+ flashptr[0] = 0x00;
+ break;
+ case FLASH_SERASE:
+ flashptr[0x555] = 0xAA; /* unlock 1 */
+ flashptr[0x2AA] = 0x55; /* unlock 2 */
+ flashptr[0x555] = 0x80;
+ flashptr[0x555] = 0xAA;
+ flashptr[0x2AA] = 0x55;
+ flashptr[0] = 0x30;
+ break;
+ case FLASH_WRITE_BUF:
+ flashptr[0x555] = 0xAA; /* unlock 1 */
+ flashptr[0x2AA] = 0x55; /* unlock 2 */
+ flashptr[0] = 0x25;
+ offset /= 2;
+ len = data / 2; /* data is bytes to program */
+ flashptr[0] = len - 1;
+ for( i = 0; i < len; i++ )
+ flashptr[offset + i] = *dataptr++;
+ flashptr[0] = 0x29;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FLASH_INTEL:
+ switch (command) {
+ case FLASH_RESET:
+ flashptr[0] = 0xFF;
+ break;
+ case FLASH_READ_ID:
+ flashptr[0] = 0x90;
+ break;
+ case FLASH_CFIQUERY:
+ flashptr[0] = 0x98;
+ break;
+ case FLASH_PROG:
+ flashptr[0] = 0x40;
+ flashptr[offset/2] = data;
+ break;
+ case FLASH_SERASE:
+ flashptr[0] = 0x60;
+ flashptr[0] = 0xD0;
+ flashptr[0] = 0x20;
+ flashptr[0] = 0xD0;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FLASH_SST:
+ switch (command) {
+ case FLASH_RESET:
+ flashbase[0x5555] = 0xAA; /* unlock 1 */
+ flashbase[0x2AAA] = 0x55; /* unlock 2 */
+ flashbase[0x5555] = 0xf0;
+ break;
+ case FLASH_READ_ID:
+ flashbase[0x5555] = 0xAA; /* unlock 1 */
+ flashbase[0x2AAA] = 0x55; /* unlock 2 */
+ flashbase[0x5555] = 0x90;
+ break;
+ case FLASH_CFIQUERY:
+ flashbase[0x5555] = 0xAA; /* unlock 1 */
+ flashbase[0x2AAA] = 0x55; /* unlock 2 */
+ flashbase[0x5555] = 0x98;
+ break;
+ case FLASH_UB:
+ break;
+ case FLASH_PROG:
+ flashbase[0x5555] = 0xAA; /* unlock 1 */
+ flashbase[0x2AAA] = 0x55; /* unlock 2 */
+ flashbase[0x5555] = 0xa0;
+ flashptr[offset/2] = data;
+ break;
+ case FLASH_UBRESET:
+ break;
+ case FLASH_SERASE:
+ flashbase[0x5555] = 0xAA; /* unlock 1 */
+ flashbase[0x2AAA] = 0x55; /* unlock 2 */
+ flashbase[0x5555] = 0x80;
+ flashbase[0x5555] = 0xAA;
+ flashbase[0x2AAA] = 0x55;
+ flashptr[0] = 0x30;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*********************************************************************/
+/* flash_write extends the functionality of flash_program() by */
+/* providing an faster way to program multiple data words, without */
+/* needing the function overhead of looping algorithms which */
+/* program word by word. This function utilizes fast pointers */
+/* to quickly loop through bulk data. */
+/*********************************************************************/
+static int cfi_flash_write(unsigned short sector, int offset, unsigned char *buf,
+ int nbytes)
+{
+ unsigned short *src;
+ src = (unsigned short *)buf;
+
+#ifndef _CFE_
+ down(&cfi_flash_lock);
+#endif
+
+ if ((nbytes | offset) & 1) {
+#ifndef _CFE_
+ up(&cfi_flash_lock);
+#endif
+ return FLASH_API_ERROR;
+ }
+
+ cfi_flash_command(FLASH_UB, 0, 0, 0, NULL);
+ while (nbytes > 0) {
+ cfi_flash_command(FLASH_PROG, sector, offset, *src, NULL);
+ if (cfi_flash_wait(sector, offset, *src) != STATUS_READY)
+ break;
+ offset +=2;
+ nbytes -=2;
+ src++;
+ }
+ cfi_flash_command(FLASH_UBRESET, 0, 0, 0, NULL);
+
+#ifndef _CFE_
+ up(&cfi_flash_lock);
+#endif
+
+ return (unsigned char*)src - buf;
+}
+
+/*********************************************************************/
+/* flash_write_to_buffer */
+/*********************************************************************/
+static int cfi_flash_write_to_buffer(unsigned short sector, unsigned char *buf)
+{
+ int nbytes = cfi_flash_get_sector_size(sector);
+ int offset;
+
+#ifndef _CFE_
+ down(&cfi_flash_lock);
+#endif
+
+ for( offset = 0; offset < nbytes; offset += meminfo.write_buffer_size ) {
+ cfi_flash_command(FLASH_WRITE_BUF, sector, offset, (unsigned short)
+ meminfo.write_buffer_size, (unsigned short *) &buf[offset]);
+ if (cfi_flash_wait(sector, 0, 0) != STATUS_READY)
+ break;
+ }
+
+#ifndef _CFE_
+ up(&cfi_flash_lock);
+#endif
+
+ return offset;
+}
+
+/*********************************************************************/
+/* flash_wait utilizes the DQ6, DQ5, and DQ2 polling algorithms */
+/* described in the flash data book. It can quickly ascertain the */
+/* operational status of the flash device, and return an */
+/* appropriate status code (defined in flash.h) */
+/*********************************************************************/
+static int cfi_flash_wait(unsigned short sector, int offset, unsigned short data)
+{
+ volatile unsigned short *flashptr; /* flash window */
+ unsigned short d1;
+
+ flashptr = (unsigned short *) cfi_flash_get_memptr(sector);
+
+ if (flashFamily == FLASH_AMD || flashFamily == FLASH_SST) {
+ do {
+#ifndef _CFE_
+ if (!in_interrupt()) {
+ set_tsk_need_resched(current); /* fake need_resched() to force schedule() */
+ schedule();
+ }
+#endif
+ d1 = *flashptr; /* read data */
+ d1 ^= *flashptr; /* read it again and see what toggled */
+ if (d1 == 0) /* no toggles, nothing's happening */
+ return STATUS_READY;
+ } while (!(d1 & 0x20));
+
+ d1 = *flashptr; /* read data */
+ d1 ^= *flashptr; /* read it again and see what toggled */
+
+ if (d1 != 0) {
+ cfi_flash_command(FLASH_RESET, 0, 0, 0, NULL);
+ return STATUS_TIMEOUT;
+ }
+ } else if (flashFamily == FLASH_INTEL) {
+ flashptr[0] = 0x70;
+ /* Wait for completion */
+
+ do {
+#ifndef _CFE_
+ if (!in_interrupt()) {
+ set_tsk_need_resched(current); /* fake need_resched() to force schedule() */
+ schedule();
+ }
+#endif
+ } while (!(*flashptr & 0x80));
+ if (*flashptr & 0x30) {
+ flashptr[0] = 0x50;
+ cfi_flash_command(FLASH_RESET, 0, 0, 0, NULL);
+ return STATUS_TIMEOUT;
+ }
+ flashptr[0] = 0x50;
+ cfi_flash_command(FLASH_RESET, 0, 0, 0, NULL);
+ }
+
+ return STATUS_READY;
+}
+
+/*********************************************************************/
+/* flash_get_device_id() will perform an autoselect sequence on the */
+/* flash device, and return the device id of the component. */
+/* This function automatically resets to read mode. */
+/*********************************************************************/
+static unsigned short cfi_flash_get_device_id(void)
+{
+ volatile unsigned short *fwp; /* flash window */
+ unsigned short answer;
+
+ fwp = (unsigned short *) cfi_flash_get_memptr(0);
+
+ cfi_flash_command(FLASH_READ_ID, 0, 0, 0, NULL);
+ answer = *(fwp + 1);
+ if (answer == ID_AM29LV320M) {
+ answer = *(fwp + 0xe);
+ answer = *(fwp + 0xf);
+ }
+
+ cfi_flash_command(FLASH_RESET, 0, 0, 0, NULL);
+ return( (unsigned short) answer );
+}
+
+/*********************************************************************/
+/* flash_get_cfi() is the main CFI workhorse function. Due to it's */
+/* complexity and size it need only be called once upon */
+/* initializing the flash system. Once it is called, all operations */
+/* are performed by looking at the meminfo structure. */
+/* All possible care was made to make this algorithm as efficient as */
+/* possible. 90% of all operations are memory reads, and all */
+/* calculations are done using bit-shifts when possible */
+/*********************************************************************/
+static int cfi_flash_get_cfi(struct cfi_query *query, unsigned short *cfi_struct,
+ int flashFamily)
+{
+ volatile unsigned short *fwp; /* flash window */
+ int i=0, temp=0;
+
+ cfi_flash_command(FLASH_CFIQUERY, 0, 0, 0, NULL);
+
+ if (cfi_struct == 0)
+ fwp = (unsigned short *) cfi_flash_get_memptr(0);
+ else
+ fwp = cfi_struct;
+
+ /* Initial house-cleaning */
+ for(i=0; i < 8; i++) {
+ query->erase_block[i].sector_size = 0;
+ query->erase_block[i].num_sectors = 0;
+ }
+
+ /* If not 'QRY', then we dont have a CFI enabled device in the socket */
+ if( fwp[0x10] != 'Q' &&
+ fwp[0x11] != 'R' &&
+ fwp[0x12] != 'Y') {
+ cfi_flash_command(FLASH_RESET, 0, 0, 0, NULL);
+ return(FLASH_API_ERROR);
+ }
+
+ temp = fwp[0x27];
+ query->device_size = (int) (((int)1) << temp);
+
+ query->num_erase_blocks = fwp[0x2C];
+ if(flashFamily == FLASH_SST)
+ query->num_erase_blocks = 1;
+
+ for(i=0; i < query->num_erase_blocks; i++) {
+ query->erase_block[i].num_sectors =
+ fwp[(0x2D+(4*i))] + (fwp[0x2E + (4*i)] << 8);
+ query->erase_block[i].num_sectors++;
+ query->erase_block[i].sector_size =
+ 256 * (256 * fwp[(0x30+(4*i))] + fwp[(0x2F+(4*i))]);
+ }
+
+ /* TBD. Add support for other flash families. */
+ if(flashFamily == FLASH_AMD)
+ query->write_buffer_size = (1 << fwp[0x2a]);
+ else
+ query->write_buffer_size = 0;
+
+ cfi_flash_command(FLASH_RESET, 0, 0, 0, NULL);
+ return(FLASH_API_OK);
+}
+
+#ifndef _CFE_
+static int __init cfi_flash_sched_init(void) {
+ cfi_flash_sched = 1; /* voluntary schedule() enabled */
+ return 0;
+}
+late_initcall(cfi_flash_sched_init);
+#endif