www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / shared / opensource / flash / cfiflash.c
diff --git a/shared/opensource/flash/cfiflash.c b/shared/opensource/flash/cfiflash.c
new file mode 100755 (executable)
index 0000000..e5cc0e9
--- /dev/null
@@ -0,0 +1,993 @@
+/************************************************************************/
+/*                                                                      */
+/*  AMD CFI Enabled Flash Memory Drivers                                */
+/*  File name: CFIFLASH.C                                               */
+/*  Revision:  1.0  5/07/98                                             */
+/*                                                                      */
+/* Copyright (c) 1998 ADVANCED MICRO DEVICES, INC. All Rights Reserved. */
+/* This software is unpublished and contains the trade secrets and      */
+/* confidential proprietary information of AMD. Unless otherwise        */
+/* provided in the Software Agreement associated herewith, it is        */
+/* licensed in confidence "AS IS" and is not to be reproduced in whole  */
+/* or part by any means except for backup. Use, duplication, or         */
+/* disclosure by the Government is subject to the restrictions in       */
+/* paragraph (b) (3) (B) of the Rights in Technical Data and Computer   */
+/* Software clause in DFAR 52.227-7013 (a) (Oct 1988).                  */
+/* Software owned by                                                    */
+/* Advanced Micro Devices, Inc.,                                        */
+/* One AMD Place,                                                       */
+/* P.O. Box 3453                                                        */
+/* Sunnyvale, CA 94088-3453.                                            */
+/************************************************************************/
+/*  This software constitutes a basic shell of source code for          */
+/*  programming all AMD Flash components. AMD                           */
+/*  will not be responsible for misuse or illegal use of this           */
+/*  software for devices not supported herein. AMD is providing         */
+/*  this source code "AS IS" and will not be responsible for            */
+/*  issues arising from incorrect user implementation of the            */
+/*  source code herein. It is the user's responsibility to              */
+/*  properly design-in this source code.                                */
+/*                                                                      */ 
+/************************************************************************/                        
+
+/** 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/param.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <asm/delay.h>
+#include <bcm_map_part.h>       
+#define CFI_USLEEP(x) udelay(x)
+#endif
+
+#include "bcmtypes.h"
+#include "board.h"
+#include "flash_api.h"
+
+/** Defines. **/
+#ifndef NULL
+#define NULL 0
+#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 */
+
+/* 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
+// add SST and ST flash device id 
+#define ID_SST39VF3202  0x235A
+#define ID_M29W320ET    0x2256
+#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   0x0017
+
+#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_SST39VF3202, "SST39VF3202"},  \
+          {ID_M29W320ET,   "M29W320ET"},    \
+          {ID_SST39VF6401, "SST39VF6401"},  \
+          {ID_I28F160C3T, "I28F160C3T"},    \
+          {ID_I28F160C3B, "I28F160C3B"},    \
+          {ID_I28F320C3T, "I28F320C3T"},    \
+          {ID_I28F320C3B, "I28F320C3B"},    \
+          {ID_I28F640J3,  "I28F640J3"},     \
+          {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 */
+};
+
+/*
+ * 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 */
+};
+
+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);
+static int cfi_flash_write(unsigned short sector, int offset, unsigned char *buf,
+    int nbytes);
+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);
+
+
+/** Variables. **/
+static flash_device_info_t flash_cfi_dev =
+    {
+        0xffff,
+        "",
+        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,
+        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
+};
+
+
+/*********************************************************************/
+/* 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[] = CFI_FLASH_DEVICES;
+    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);
+
+    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:
+    // add ST flash device id  here
+        case ID_M29W320ET: 
+    // add ST flash device id  here
+            flashFamily = FLASH_AMD;
+            break;
+        case ID_SST39VF200A:
+        case ID_SST39VF400A:
+        case ID_SST39VF800A:
+        case ID_SST39VF1601:
+        case ID_SST39VF3201:
+    // add SST flash device id here
+        case ID_SST39VF3202:
+    // add SST flash device id here
+        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:
+            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:
+    // add SST and ST flash device id here
+        case ID_SST39VF3202:
+        case ID_M29W320ET: 
+    // add SST and ST flash device id here
+            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;
+            }
+        }
+    }
+
+    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;
+
+    for( i = 0; i < 3; i++ ) {
+        cfi_flash_command(FLASH_SERASE, sector, 0, 0);
+        if (cfi_flash_wait(sector, 0, 0xffff) == STATUS_READY)
+            break;
+    }
+    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;
+
+    fwp = (unsigned char *) cfi_flash_get_memptr(sector);
+
+    while (numbytes) {
+        *buffer++ = *(fwp + offset);
+        numbytes--;
+        fwp++;
+    }
+
+    return (FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* 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;
+#ifdef _CFE_                                                
+    printk("Flash writing sector %d %x\n", sector, p);
+#endif
+    /* 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++ ) {
+        ret = cfi_flash_write(sector, offset, buffer, numbytes);
+        if( !memcmp( p, buffer, numbytes ) )
+            break;
+        /* Erase and try again */
+        flash_sector_erase_int(sector);
+        ret = FLASH_API_ERROR;
+    }
+    // printk( "%c", i==0 ? '*' : ' ' );
+
+    if( ret == FLASH_API_ERROR )
+        printk( "Flash write sector %d error.  Verify failed\n", sector );
+
+    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)
+{
+    volatile unsigned short *flashptr;
+    volatile unsigned short *flashbase;
+
+    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;
+        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; Block Unlock is not required.
+            //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;
+
+    if ((nbytes | offset) & 1) {
+        return FLASH_API_ERROR;
+    }
+
+    cfi_flash_command(FLASH_UB, 0, 0, 0);
+    while (nbytes > 0) {
+        cfi_flash_command(FLASH_PROG, sector, offset, *src);
+        if (cfi_flash_wait(sector, offset, *src) != STATUS_READY)
+            break;
+        offset +=2;
+        nbytes -=2;
+        src++;
+    }
+    cfi_flash_command(FLASH_UBRESET, 0, 0, 0);
+    
+    return (unsigned char*)src - buf;
+}
+
+/*********************************************************************/
+/* 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) {
+/* 6338 and 6358 for different reasons do a 'double read' when reading 16 bit from EBI */
+#if defined(_BCM96338_) || defined(CONFIG_BCM96338) || defined(_BCM96358_) || defined(CONFIG_BCM96358)
+        do {
+            d1 = flashptr[offset/2];
+            if (d1 == data)
+                return STATUS_READY;
+        } while (!(d1 & 0x20));
+
+        d1 = flashptr[offset/2];
+
+        if (d1 != data) {
+
+            /* If the word written does not yet compare, try for another 100ms.
+             * This check is done for SST39VF800A.
+             */
+            int i;
+            for( i = 0; i < 10000; i++ ) {
+                d1 = flashptr[offset/2];
+                if (d1 == data)
+                    return STATUS_READY;
+                CFI_USLEEP(10);
+            }
+
+            d1 = flashptr[offset/2];
+
+            if (d1 != data) {
+                cfi_flash_command(FLASH_RESET, 0, 0, 0);
+                return STATUS_TIMEOUT;
+            }
+        }
+#else
+        do {
+            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);
+            return STATUS_TIMEOUT;
+        }
+#endif
+    } else if (flashFamily == FLASH_INTEL) {
+        flashptr[0] = 0x70;
+        /* Wait for completion */
+        while(!(*flashptr & 0x80));
+        if (*flashptr & 0x30) {
+            flashptr[0] = 0x50;
+            printk("CFI: Wait error &d\n", *flashptr);
+            cfi_flash_command(FLASH_RESET, 0, 0, 0);
+            return STATUS_TIMEOUT;
+        }
+        flashptr[0] = 0x50;
+        cfi_flash_command(FLASH_RESET, 0, 0, 0);
+    }
+    
+    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);
+    answer = *(fwp + 1);
+    if (answer == ID_AM29LV320M) {
+        answer = *(fwp + 0xe);
+        answer = *(fwp + 0xf);
+    }
+    
+    cfi_flash_command(FLASH_RESET, 0, 0, 0);
+    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);
+    
+    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);
+        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))]);
+    }
+    
+    cfi_flash_command(FLASH_RESET, 0, 0, 0);
+    return(FLASH_API_OK);
+}
+