summaryrefslogtreecommitdiffstats
path: root/shared/opensource/flash
diff options
context:
space:
mode:
authorroot <root@lamia.panaceas.james.local>2015-12-19 13:13:57 +0000
committerroot <root@lamia.panaceas.james.local>2015-12-19 14:18:03 +0000
commit1a2238d1bddc823df06f67312d96ccf9de2893cc (patch)
treec58a3944d674a667f133ea5a730f5037e57d3d2e /shared/opensource/flash
downloadbootloader-1a2238d1bddc823df06f67312d96ccf9de2893cc.tar.gz
bootloader-1a2238d1bddc823df06f67312d96ccf9de2893cc.tar.bz2
bootloader-1a2238d1bddc823df06f67312d96ccf9de2893cc.zip
CFE from danitool [without hostTools dir]: https://mega.nz/#!mwZyFK7a!CPT3BKC8dEw29kubtdYxhB91G9vIIismTkgzQ3iUy3k
Diffstat (limited to 'shared/opensource/flash')
-rwxr-xr-xshared/opensource/flash/Makefile41
-rwxr-xr-xshared/opensource/flash/cfiflash.c1112
-rwxr-xr-xshared/opensource/flash/flash_api.c350
-rwxr-xr-xshared/opensource/flash/flash_common.c280
-rwxr-xr-xshared/opensource/flash/nandflash.c1260
-rwxr-xr-xshared/opensource/flash/spiflash.c969
6 files changed, 4012 insertions, 0 deletions
diff --git a/shared/opensource/flash/Makefile b/shared/opensource/flash/Makefile
new file mode 100755
index 0000000..b7a9812
--- /dev/null
+++ b/shared/opensource/flash/Makefile
@@ -0,0 +1,41 @@
+
+ifeq ($(CONFIG_MIPS_BRCM),y)
+
+# Linux
+
+obj-y += flash_api.o flash_common.o
+
+ifeq ($(strip $(BRCM_CHIP)),6368)
+INC_CFI_FLASH_DRIVER=1
+INC_SPI_FLASH_DRIVER=1
+endif
+ifeq ($(strip $(BRCM_CHIP)),6816)
+INC_CFI_FLASH_DRIVER=1
+INC_SPI_FLASH_DRIVER=1
+endif
+ifeq ($(strip $(BRCM_CHIP)),6362)
+INC_CFI_FLASH_DRIVER=0
+INC_SPI_FLASH_DRIVER=1
+endif
+ifeq ($(strip $(BRCM_CHIP)),6328)
+INC_CFI_FLASH_DRIVER=0
+INC_SPI_FLASH_DRIVER=1
+endif
+
+ifeq ($(strip $(INC_CFI_FLASH_DRIVER)),1)
+obj-y += cfiflash.o
+endif
+ifeq ($(strip $(INC_SPI_FLASH_DRIVER)),1)
+obj-y += spiflash.o
+endif
+
+EXTRA_CFLAGS += -DCONFIG_BCM9$(BRCM_CHIP) -I$(INC_BRCMDRIVER_PUB_PATH)/$(BRCM_BOARD) -I$(INC_BRCMSHARED_PUB_PATH)/$(BRCM_BOARD) -DINC_CFI_FLASH_DRIVER=$(INC_CFI_FLASH_DRIVER) -DINC_SPI_FLASH_DRIVER=$(INC_SPI_FLASH_DRIVER)
+
+endif
+
+
+
+
+
+
+
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
diff --git a/shared/opensource/flash/flash_api.c b/shared/opensource/flash/flash_api.c
new file mode 100755
index 0000000..5b0ca76
--- /dev/null
+++ b/shared/opensource/flash/flash_api.c
@@ -0,0 +1,350 @@
+/*
+ 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.
+*/
+
+/***************************************************************************
+ * File Name : flash_api.c
+ *
+ * Description: This file contains the implementation of the wrapper functions
+ * for the flash device interface.
+ ***************************************************************************/
+
+/** Includes. */
+#ifdef _CFE_
+#include "lib_types.h"
+#include "lib_printf.h"
+#include "lib_string.h"
+#include "bcm_map.h"
+#define printk printf
+#else // Linux
+#include <linux/kernel.h>
+#include "bcm_map_part.h"
+#endif
+
+#include "bcmtypes.h"
+#include "bcm_hwdefs.h"
+#include "flash_api.h"
+
+/** Externs. **/
+#if !defined(INC_CFI_FLASH_DRIVER)
+#define INC_CFI_FLASH_DRIVER 0
+#endif
+
+#if !defined(INC_SPI_FLASH_DRIVER)
+#define INC_SPI_FLASH_DRIVER 0
+#endif
+
+#if !defined(INC_NAND_FLASH_DRIVER)
+#define INC_NAND_FLASH_DRIVER 0
+#endif
+
+#if !defined(INC_SPI_PROG_NAND)
+#define INC_SPI_PROG_NAND 0
+#endif
+
+#if (INC_CFI_FLASH_DRIVER==1)
+extern int cfi_flash_init(flash_device_info_t **flash_info);
+#else
+#define cfi_flash_init(x) FLASH_API_ERROR
+#endif
+
+#if (INC_SPI_FLASH_DRIVER==1)
+extern int spi_flash_init(flash_device_info_t **flash_info);
+#else
+#define spi_flash_init(x) FLASH_API_ERROR
+#endif
+
+#if (INC_NAND_FLASH_DRIVER==1) || (INC_SPI_PROG_NAND==1)
+extern int nand_flash_init(flash_device_info_t **flash_info);
+#else
+#define nand_flash_init(x) FLASH_API_ERROR
+#endif
+
+/** Variables. **/
+static flash_device_info_t *g_flash_info = NULL;
+#if (INC_SPI_PROG_NAND==1)
+static flash_device_info_t *g_nand_flash_info = NULL;
+static flash_device_info_t *g_spi_flash_info = NULL;
+#endif
+
+/***************************************************************************
+ * Function Name: display_flash_info
+ * Description : Displays information about the flash part.
+ * Returns : None.
+ ***************************************************************************/
+static void display_flash_info(int ret, flash_device_info_t *flash_info)
+{
+ switch (flash_info->flash_type) {
+ case FLASH_IFC_PARALLEL:
+ printk( "Parallel flash device");
+ break;
+
+ case FLASH_IFC_SPI:
+ printk( "Serial flash device");
+ break;
+
+ case FLASH_IFC_HS_SPI:
+ printk( "HS Serial flash device");
+ break;
+
+ case FLASH_IFC_NAND:
+ printk( "NAND flash device");
+ break;
+ }
+
+ if( ret == FLASH_API_OK ) {
+ printk(": name %s, id 0x%4.4x",
+ flash_info->flash_device_name, flash_info->flash_device_id);
+#if (INC_SPI_PROG_NAND==1)
+ printk(" block %dKB", flash_info->fn_flash_get_sector_size(0) / 1024);
+ printk(" size %dKB", (*flash_info->fn_flash_get_total_size) () / 1024);
+#else
+ if (flash_info->flash_type == FLASH_IFC_NAND)
+ printk(" block %dKB", flash_get_sector_size(0) / 1024);
+ printk(" size %dKB", flash_get_total_size() / 1024);
+#endif
+ printk("\n");
+ }
+ else {
+ printk( " id %4.4x is not supported.\n", flash_info->flash_device_id );
+ }
+} /* display_flash_info */
+
+/***************************************************************************
+ * Function Name: flash_init
+ * Description : Initialize flash part.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+int flash_init(void)
+{
+ int type = FLASH_IFC_UNKNOWN;
+ int ret = FLASH_API_ERROR;
+#if (INC_SPI_PROG_NAND==1)
+ int ret_nand = FLASH_API_ERROR;
+#endif
+
+ /* If available, use bootstrap to decide which flash to use */
+#if defined(_BCM96816_) || defined(CONFIG_BCM96816) || defined(_BCM96368_) || defined(CONFIG_BCM96368)
+ unsigned int bootsel;
+#if defined(_BCM96816_) || defined(CONFIG_BCM96816)
+ bootsel = MISC->miscStrapBus;
+#elif defined(_BCM96368_) || defined(CONFIG_BCM96368)
+ bootsel = GPIO->StrapBus;
+#endif
+ switch ((bootsel & MISC_STRAP_BUS_BOOT_SEL_MASK)>>MISC_STRAP_BUS_BOOT_SEL_SHIFT) {
+ case MISC_STRAP_BUS_BOOT_PARALLEL:
+ type = FLASH_IFC_PARALLEL;
+ break;
+
+ case MISC_STRAP_BUS_BOOT_SERIAL:
+ type = FLASH_IFC_SPI;
+ break;
+
+ case MISC_STRAP_BUS_BOOT_NAND:
+ type = FLASH_IFC_NAND;
+ break;
+
+ }
+#elif defined(_BCM96362_) || defined(CONFIG_BCM96362) || defined(_BCM96328_) || defined(CONFIG_BCM96328)
+ if( ((MISC->miscStrapBus & MISC_STRAP_BUS_BOOT_SEL_MASK) >>
+ MISC_STRAP_BUS_BOOT_SEL_SHIFT) == MISC_STRAP_BUS_BOOT_SERIAL )
+ type = FLASH_IFC_SPI;
+ else
+ type = FLASH_IFC_NAND;
+#endif
+
+ switch (type) {
+ case FLASH_IFC_PARALLEL:
+ ret = cfi_flash_init( &g_flash_info );
+ break;
+
+ case FLASH_IFC_SPI:
+ ret = spi_flash_init( &g_flash_info );
+#if (INC_SPI_PROG_NAND==1)
+ ret_nand = nand_flash_init( &g_nand_flash_info );
+#endif
+
+ break;
+
+ case FLASH_IFC_NAND:
+ ret = nand_flash_init( &g_flash_info );
+ break;
+
+ case FLASH_IFC_UNKNOWN:
+ /* Try to detect flash chips, give priority to parallel flash */
+ /* Our reference design has both, and we usually use parallel. */
+ ret = cfi_flash_init( &g_flash_info );
+ if (ret != FLASH_API_OK) {
+ ret = spi_flash_init( &g_flash_info );
+ }
+ break;
+ }
+
+ if (g_flash_info != NULL) {
+ display_flash_info(ret, g_flash_info);
+#if (INC_SPI_PROG_NAND==1)
+ display_flash_info(ret_nand, g_nand_flash_info );
+#endif
+ }
+ else {
+ printk( "BCM Flash API. Flash device is not found.\n" );
+ }
+
+ return( ret );
+} /* flash_init */
+
+/***************************************************************************
+ * Function Name: flash_sector_erase_int
+ * Description : Erase the specfied flash sector.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+int flash_sector_erase_int(unsigned short sector)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_sector_erase_int) (sector)
+ : FLASH_API_ERROR );
+} /* flash_sector_erase_int */
+
+/***************************************************************************
+ * Function Name: flash_read_buf
+ * Description : Reads from flash memory.
+ * Returns : number of bytes read or FLASH_API_ERROR
+ ***************************************************************************/
+int flash_read_buf(unsigned short sector, int offset, unsigned char *buffer,
+ int numbytes)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_read_buf) (sector, offset, buffer, numbytes)
+ : FLASH_API_ERROR );
+} /* flash_read_buf */
+
+/***************************************************************************
+ * Function Name: flash_write_buf
+ * Description : Writes to flash memory.
+ * Returns : number of bytes written or FLASH_API_ERROR
+ ***************************************************************************/
+int flash_write_buf(unsigned short sector, int offset, unsigned char *buffer,
+ int numbytes)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_write_buf) (sector, offset, buffer, numbytes)
+ : FLASH_API_ERROR );
+} /* flash_write_buf */
+
+/***************************************************************************
+ * Function Name: flash_get_numsectors
+ * Description : Returns the number of sectors in the flash device.
+ * Returns : Number of sectors in the flash device.
+ ***************************************************************************/
+int flash_get_numsectors(void)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_get_numsectors) ()
+ : FLASH_API_ERROR );
+} /* flash_get_numsectors */
+
+/***************************************************************************
+ * Function Name: flash_get_sector_size
+ * Description : Returns the number of bytes in the specfied flash sector.
+ * Returns : Number of bytes in the specfied flash sector.
+ ***************************************************************************/
+int flash_get_sector_size(unsigned short sector)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_get_sector_size) (sector)
+ : FLASH_API_ERROR );
+} /* flash_get_sector_size */
+
+/***************************************************************************
+ * Function Name: flash_get_memptr
+ * Description : Returns the base MIPS memory address for the specfied flash
+ * sector.
+ * Returns : Base MIPS memory address for the specfied flash sector.
+ ***************************************************************************/
+unsigned char *flash_get_memptr(unsigned short sector)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_get_memptr) (sector)
+ : NULL );
+} /* flash_get_memptr */
+
+/***************************************************************************
+ * Function Name: flash_get_blk
+ * Description : Returns the flash sector for the specfied MIPS address.
+ * Returns : Flash sector for the specfied MIPS address.
+ ***************************************************************************/
+int flash_get_blk(int addr)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_get_blk) (addr)
+ : FLASH_API_ERROR );
+} /* flash_get_blk */
+
+/***************************************************************************
+ * Function Name: flash_get_total_size
+ * Description : Returns the number of bytes in the flash device.
+ * Returns : Number of bytes in the flash device.
+ ***************************************************************************/
+int flash_get_total_size(void)
+{
+ return( (g_flash_info)
+ ? (*g_flash_info->fn_flash_get_total_size) ()
+ : FLASH_API_ERROR );
+} /* flash_get_total_size */
+
+/***************************************************************************
+ * Function Name: flash_get_flash_type
+ * Description : Returns type of the flash memory.
+ * Returns : Type of the flash memory.
+ ***************************************************************************/
+int flash_get_flash_type(void)
+{
+ return( (g_flash_info)
+ ? (g_flash_info->flash_type)
+ : FLASH_API_ERROR );
+} /* flash_get_flash_type */
+
+#if (INC_SPI_PROG_NAND==1)
+/***************************************************************************
+ * Function Name: flash_change_flash_type
+ * Description : change type of the flash memory.
+ * Returns : none
+ ***************************************************************************/
+void flash_change_flash_type(int type)
+{
+
+ if (type == FLASH_IFC_NAND)
+ {
+ if (g_spi_flash_info == NULL)
+ g_spi_flash_info = g_flash_info;
+ g_flash_info = g_nand_flash_info;
+ }
+ else
+ {
+ if (g_spi_flash_info != NULL)
+ g_flash_info = g_spi_flash_info;
+ }
+} /* flash_change_flash_type */
+
+#endif
+
diff --git a/shared/opensource/flash/flash_common.c b/shared/opensource/flash/flash_common.c
new file mode 100755
index 0000000..0d4a75d
--- /dev/null
+++ b/shared/opensource/flash/flash_common.c
@@ -0,0 +1,280 @@
+/*
+ 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.
+*/
+
+/*!\file flash_common.c
+ * \brief This file contains NOR flash related functions used by both
+ * CFE and kernel.
+ *
+ */
+
+/** Includes. */
+#ifdef _CFE_
+#include "lib_types.h"
+#include "lib_printf.h"
+#include "lib_string.h"
+#include "bcm_map.h"
+#define printk printf
+#else // Linux
+#include <linux/kernel.h>
+#include "bcm_map_part.h"
+#endif
+
+#include "bcmtypes.h"
+#include "bcm_hwdefs.h"
+#include "flash_api.h"
+#include "flash_common.h"
+
+// #define DEBUG_FLASH
+
+void flash_init_info(const NVRAM_DATA *nvRam, FLASH_ADDR_INFO *fInfo)
+{
+ int i = 0;
+ int totalBlks = 0;
+ int totalSize = 0;
+ int psiStartAddr = 0;
+ int spStartAddr = 0;
+ int usedBlkSize = 0;
+ int needBytes = 0;
+
+ if (flash_get_flash_type() == FLASH_IFC_NAND)
+ {
+ /* When using NAND flash disable Bcm_flash */
+ totalSize = 0;
+ }
+ else {
+ totalBlks = flash_get_numsectors();
+ totalSize = flash_get_total_size();
+ printk("Total Flash size: %dK with %d sectors\n", totalSize/1024, totalBlks);
+ }
+
+ if (totalSize <= FLASH_LENGTH_BOOT_ROM) {
+ /* NAND flash settings. NAND flash does not use raw flash partitioins
+ * to store psi, backup psi, scratch pad and syslog. These data items
+ * are stored as files on a JFFS2 file system.
+ */
+ if ((nvRam->ulPsiSize != -1) && (nvRam->ulPsiSize != 0))
+ fInfo->flash_persistent_length = nvRam->ulPsiSize * ONEK;
+ else
+ fInfo->flash_persistent_length = DEFAULT_PSI_SIZE * ONEK;
+
+ fInfo->flash_persistent_start_blk = 0;
+ fInfo->flash_rootfs_start_offset = 0;
+ fInfo->flash_scratch_pad_length = SP_MAX_LEN;
+ fInfo->flash_syslog_length = nvRam->ulSyslogSize * 1024;
+
+ /* This is a boolean field for NAND flash. */
+ fInfo->flash_backup_psi_number_blk = nvRam->backupPsi;
+ return;
+ }
+
+ /*
+ * calculate mandatory primary PSI size and set its fInfo parameters.
+ */
+ if ((nvRam->ulPsiSize != -1) && (nvRam->ulPsiSize != 0))
+ fInfo->flash_persistent_length = nvRam->ulPsiSize * ONEK;
+ else
+ fInfo->flash_persistent_length = DEFAULT_PSI_SIZE * ONEK;
+
+ psiStartAddr = totalSize - fInfo->flash_persistent_length;
+ fInfo->flash_persistent_start_blk = flash_get_blk(FLASH_BASE+psiStartAddr);
+ fInfo->flash_persistent_number_blk = totalBlks - fInfo->flash_persistent_start_blk;
+
+ usedBlkSize = 0;
+ for (i = fInfo->flash_persistent_start_blk;
+ i < (fInfo->flash_persistent_start_blk + fInfo->flash_persistent_number_blk); i++)
+ {
+ usedBlkSize += flash_get_sector_size((unsigned short) i);
+ }
+ fInfo->flash_persistent_blk_offset = usedBlkSize - fInfo->flash_persistent_length;
+ fInfo->flash_meta_start_blk = fInfo->flash_persistent_start_blk;
+
+ /*
+ * Next is the optional scratch pad, which is on top of the primary PSI.
+ * Old code allowed scratch pad to share a sector with primary PSI.
+ * That is retained for backward compatibility. (Although depending on your
+ * NOR flash sector sizes, they may still be in different sectors.)
+ * If you have a new deployment, consider forcing separate sectors.
+ */
+ if ((fInfo->flash_persistent_blk_offset > 0) &&
+ (fInfo->flash_persistent_blk_offset < SP_MAX_LEN))
+ {
+ /*
+ * there is some room left in the first persistent sector, but it is
+ * not big enough for the scratch pad. (Use this line unconditionally
+ * if you want to guarentee scratch pad and primary PSI are on different
+ * sectors.)
+ */
+ spStartAddr = psiStartAddr - fInfo->flash_persistent_blk_offset - SP_MAX_LEN;
+ }
+ else
+ {
+ /* either the primary PSI starts on a sector boundary, or there is
+ * enough room at the top of the first sector for the scratch pad. */
+ spStartAddr = psiStartAddr - SP_MAX_LEN ;
+ }
+
+ fInfo->flash_scratch_pad_start_blk = flash_get_blk(FLASH_BASE+spStartAddr);
+ fInfo->flash_scratch_pad_length = SP_MAX_LEN;
+
+ if (fInfo->flash_persistent_start_blk == fInfo->flash_scratch_pad_start_blk) // share blk
+ {
+#if 0 /* do not used scratch pad unless it's in its own sector */
+ printk("Scratch pad is not used for this flash part.\n");
+ fInfo->flash_scratch_pad_length = 0; // no sp
+#else /* allow scratch pad to share a sector with another section such as PSI */
+ fInfo->flash_scratch_pad_number_blk = 1;
+ fInfo->flash_scratch_pad_blk_offset = fInfo->flash_persistent_blk_offset - fInfo->flash_scratch_pad_length;
+#endif
+ }
+ else // on different blk
+ {
+ fInfo->flash_scratch_pad_number_blk = fInfo->flash_persistent_start_blk - fInfo->flash_scratch_pad_start_blk;
+ // find out the offset in the start_blk
+ usedBlkSize = 0;
+ for (i = fInfo->flash_scratch_pad_start_blk;
+ i < (fInfo->flash_scratch_pad_start_blk + fInfo->flash_scratch_pad_number_blk); i++)
+ usedBlkSize += flash_get_sector_size((unsigned short) i);
+ fInfo->flash_scratch_pad_blk_offset = usedBlkSize - fInfo->flash_scratch_pad_length;
+ }
+
+ if (fInfo->flash_scratch_pad_length > 0) {
+
+ fInfo->flash_meta_start_blk = fInfo->flash_scratch_pad_start_blk;
+ }
+
+ /*
+ * Next is the optional backup PSI.
+ */
+ if (nvRam->backupPsi == 0x01)
+ {
+ needBytes = fInfo->flash_persistent_length;
+ i = fInfo->flash_meta_start_blk;
+ while (needBytes > 0)
+ {
+ i--;
+ needBytes -= flash_get_sector_size((unsigned short) i);
+ }
+ fInfo->flash_backup_psi_start_blk = i;
+ /* calclate how many blocks we actually consumed */
+ needBytes = fInfo->flash_persistent_length;
+ fInfo->flash_backup_psi_number_blk = 0;
+ while (needBytes > 0)
+ {
+ needBytes -= flash_get_sector_size((unsigned short) i);
+ i++;
+ fInfo->flash_backup_psi_number_blk++;
+ }
+
+ fInfo->flash_meta_start_blk = fInfo->flash_backup_psi_start_blk;
+ }
+ else
+ {
+ fInfo->flash_backup_psi_number_blk = 0;
+ }
+
+ /*
+ * Next is the optional persistent syslog.
+ */
+ if (nvRam->ulSyslogSize != 0 && nvRam->ulSyslogSize != -1)
+ {
+ fInfo->flash_syslog_length = nvRam->ulSyslogSize * 1024;
+ needBytes = fInfo->flash_syslog_length;
+ i = fInfo->flash_meta_start_blk;
+ while (needBytes > 0)
+ {
+ i--;
+ needBytes -= flash_get_sector_size((unsigned short) i);
+ }
+ fInfo->flash_syslog_start_blk = i;
+ /* calclate how many blocks we actually consumed */
+ needBytes = fInfo->flash_syslog_length;
+ fInfo->flash_syslog_number_blk = 0;
+ while (needBytes > 0)
+ {
+ needBytes -= flash_get_sector_size((unsigned short) i);
+ i++;
+ fInfo->flash_syslog_number_blk++;
+ }
+
+ fInfo->flash_meta_start_blk = fInfo->flash_syslog_start_blk;
+ }
+ else
+ {
+ fInfo->flash_syslog_length = 0;
+ fInfo->flash_syslog_number_blk = 0;
+ }
+
+#ifdef DEBUG_FLASH_TOO_MUCH
+ /* dump sizes of all sectors in flash */
+ for (i=0; i<totalBlks; i++)
+ printk("blk %03d: %d\n", i, flash_get_sector_size((unsigned short) i));
+#endif
+
+#if defined(DEBUG_FLASH)
+ printk("FLASH_BASE =0x%08x\n\n", (unsigned int)FLASH_BASE);
+
+ printk("fInfo->flash_rootfs_start_offset =0x%08x\n\n", (unsigned int)fInfo->flash_rootfs_start_offset);
+
+ printk("fInfo->flash_meta_start_blk = %d\n\n", fInfo->flash_meta_start_blk);
+
+ printk("fInfo->flash_syslog_start_blk = %d\n", fInfo->flash_syslog_start_blk);
+ printk("fInfo->flash_syslog_number_blk = %d\n", fInfo->flash_syslog_number_blk);
+ printk("fInfo->flash_syslog_length=0x%x\n\n", (unsigned int)fInfo->flash_syslog_length);
+
+ printk("fInfo->flash_backup_psi_start_blk = %d\n", fInfo->flash_backup_psi_start_blk);
+ printk("fInfo->flash_backup_psi_number_blk = %d\n\n", fInfo->flash_backup_psi_number_blk);
+
+ printk("sp startAddr = %x\n", (unsigned int) (FLASH_BASE+spStartAddr));
+ printk("fInfo->flash_scratch_pad_start_blk = %d\n", fInfo->flash_scratch_pad_start_blk);
+ printk("fInfo->flash_scratch_pad_number_blk = %d\n", fInfo->flash_scratch_pad_number_blk);
+ printk("fInfo->flash_scratch_pad_length = 0x%x\n", fInfo->flash_scratch_pad_length);
+ printk("fInfo->flash_scratch_pad_blk_offset = 0x%x\n\n", (unsigned int)fInfo->flash_scratch_pad_blk_offset);
+
+ printk("psi startAddr = %x\n", (unsigned int) (FLASH_BASE+psiStartAddr));
+ printk("fInfo->flash_persistent_start_blk = %d\n", fInfo->flash_persistent_start_blk);
+ printk("fInfo->flash_persistent_number_blk = %d\n", fInfo->flash_persistent_number_blk);
+ printk("fInfo->flash_persistent_length=0x%x\n", (unsigned int)fInfo->flash_persistent_length);
+ printk("fInfo->flash_persistent_blk_offset = 0x%x\n\n", (unsigned int)fInfo->flash_persistent_blk_offset);
+#endif
+}
+
+unsigned int flash_get_reserved_bytes_at_end(const FLASH_ADDR_INFO *fInfo)
+{
+ unsigned int reserved=0;
+ int i = fInfo->flash_meta_start_blk;
+ int totalBlks = flash_get_numsectors();
+
+ while (i < totalBlks)
+ {
+ reserved += flash_get_sector_size((unsigned short) i);
+ i++;
+ }
+
+#if defined(DEBUG_FLASH)
+ printk("reserved at bottom=%dKB\n", reserved/1024);
+#endif
+
+ return reserved;
+}
+
diff --git a/shared/opensource/flash/nandflash.c b/shared/opensource/flash/nandflash.c
new file mode 100755
index 0000000..b90ad7b
--- /dev/null
+++ b/shared/opensource/flash/nandflash.c
@@ -0,0 +1,1260 @@
+/*
+ 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.
+*/
+
+/***************************************************************************
+ * File Name : nandflash.c
+ *
+ * Description: This file implements the Broadcom DSL defined flash api for
+ * for NAND flash parts.
+ ***************************************************************************/
+
+/** Includes. **/
+
+#include "lib_types.h"
+#include "lib_printf.h"
+#include "lib_string.h"
+#include "lib_malloc.h"
+#include "bcm_map.h"
+#include "bcmtypes.h"
+#include "bcm_hwdefs.h"
+#include "flash_api.h"
+#include "jffs2.h"
+#if defined(CFG_RAMAPP) && (INC_SPI_FLASH_DRIVER==1)
+#include "cfe_timer.h"
+#endif
+
+/* for debugging in jtag */
+#if !defined(CFG_RAMAPP)
+#define static
+#endif
+
+
+/** Defines. **/
+
+#define NR_OOB_SCAN_PAGES 4
+#define SPARE_MAX_SIZE 64
+#define PAGE_MAX_SIZE 2048
+#define CTRLR_SPARE_SIZE 16
+#define CTRLR_CACHE_SIZE 512
+
+/* Flash manufacturers. */
+#define FLASHTYPE_SAMSUNG 0xec
+#define FLASHTYPE_ST 0x20
+#define FLASHTYPE_MICRON 0x2c
+
+/* Samsung flash parts. */
+#define SAMSUNG_K9F5608U0A 0x55
+
+/* ST flash parts. */
+#define ST_NAND512W3A2CN6 0x76
+#define ST_NAND01GW3B2CN6 0xf1
+
+/* Micron flash parts. */
+#define MICRON_MT29F1G08AAC 0xf1
+
+/* Flash id to name mapping. */
+#define NAND_MAKE_ID(A,B) \
+ (((unsigned short) (A) << 8) | ((unsigned short) B & 0xff))
+
+#define NAND_FLASH_DEVICES \
+ {{NAND_MAKE_ID(FLASHTYPE_SAMSUNG,SAMSUNG_K9F5608U0A),"Samsung K9F5608U0"}, \
+ {NAND_MAKE_ID(FLASHTYPE_ST,ST_NAND512W3A2CN6),"ST NAND512W3A2CN6"}, \
+ {NAND_MAKE_ID(FLASHTYPE_ST,ST_NAND01GW3B2CN6),"ST NAND01GW3B2CN6"}, \
+ {NAND_MAKE_ID(FLASHTYPE_MICRON,MICRON_MT29F1G08AAC),"Micron MT29F1G08AAC"},\
+ {0,""} \
+ }
+
+/* One byte for small page NAND flash parts. */
+#define SPARE_SP_BI_INDEX_1 5
+#define SPARE_SP_BI_INDEX_2 5
+
+/* Two bytes for small page NAND flash parts. */
+#define SPARE_LP_BI_INDEX_1 0
+#define SPARE_LP_BI_INDEX_2 1
+
+#define SPARE_BI_MARKER 0
+#define SPARE_BI_ECC_MASK \
+ {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, \
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}
+
+#define JFFS2_CLEANMARKER {JFFS2_MAGIC_BITMASK, \
+ JFFS2_NODETYPE_CLEANMARKER, 0x0000, 0x0008}
+
+#undef DEBUG_NAND
+#if defined(DEBUG_NAND) && defined(CFG_RAMAPP)
+#define DBG_PRINTF printf
+#else
+#define DBG_PRINTF(...)
+#endif
+
+
+/** Externs. **/
+
+extern void board_setleds(unsigned long);
+
+
+/** Structs. **/
+
+typedef struct CfeNandChip
+{
+ char *chip_name;
+ unsigned long chip_device_id;
+ unsigned long chip_base;
+ unsigned long chip_total_size;
+ unsigned long chip_block_size;
+ unsigned long chip_page_size;
+ unsigned long chip_spare_size;
+ unsigned char *chip_spare_mask;
+ unsigned long chip_bi_index_1;
+ unsigned long chip_bi_index_2;
+} CFE_NAND_CHIP, *PCFE_NAND_CHIP;
+
+struct flash_name_from_id
+{
+ unsigned short fnfi_id;
+ char fnfi_name[30];
+};
+
+
+#if defined(CFG_RAMAPP)
+/** Prototypes for CFE RAM. **/
+int nand_flash_init(flash_device_info_t **flash_info);
+int mpinand_flash_init(flash_device_info_t **flash_info);
+static void nand_init_cleanmarker(PCFE_NAND_CHIP pchip);
+static void nand_read_cfg(PCFE_NAND_CHIP pchip);
+static int nand_is_blk_cleanmarker(PCFE_NAND_CHIP pchip,
+ unsigned long start_addr, int write_if_not);
+static int nand_initialize_spare_area(PCFE_NAND_CHIP pchip);
+static void nand_mark_bad_blk(PCFE_NAND_CHIP pchip, unsigned long page_addr);
+static int nand_flash_sector_erase_int(unsigned short blk);
+static int nand_flash_read_buf(unsigned short blk, int offset,
+ unsigned char *buffer, int len);
+static int nand_flash_write_buf(unsigned short blk, int offset,
+ unsigned char *buffer, int numbytes);
+static int nand_flash_get_numsectors(void);
+static int nand_flash_get_sector_size(unsigned short sector);
+static unsigned char *nand_flash_get_memptr(unsigned short sector);
+static int nand_flash_get_blk(int addr);
+static int nand_flash_get_total_size(void);
+static int nandflash_wait_status(unsigned long status_mask);
+static int nandflash_read_spare_area(PCFE_NAND_CHIP pchip,
+ unsigned long page_addr, unsigned char *buffer, int len);
+static int nandflash_write_spare_area(PCFE_NAND_CHIP pchip,
+ unsigned long page_addr, unsigned char *buffer, int len);
+static int nandflash_read_page(PCFE_NAND_CHIP pchip,
+ unsigned long start_addr, unsigned char *buffer, int len);
+static int nandflash_write_page(PCFE_NAND_CHIP pchip, unsigned long start_addr,
+ unsigned char *buffer, int len);
+static int nandflash_block_erase(PCFE_NAND_CHIP pchip, unsigned long blk_addr);
+#else
+/** Prototypes for CFE ROM. **/
+void rom_nand_flash_init(void);
+static int nand_is_blk_cleanmarker(PCFE_NAND_CHIP pchip,
+ unsigned long start_addr, int write_if_not);
+static void nand_read_cfg(PCFE_NAND_CHIP pchip);
+int nand_flash_get_sector_size(unsigned short sector);
+int nand_flash_get_numsectors(void);
+static int nandflash_wait_status(unsigned long status_mask);
+static int nandflash_read_spare_area(PCFE_NAND_CHIP pchip,
+ unsigned long page_addr, unsigned char *buffer, int len);
+static int nandflash_read_page(PCFE_NAND_CHIP pchip, unsigned long start_addr,
+ unsigned char *buffer, int len);
+int nand_flash_read_buf(unsigned short blk, int offset,
+ unsigned char *buffer, int len);
+static inline void nandflash_copy_from_cache(unsigned char *buffer,
+ int offset, int numbytes);
+static inline void nandflash_copy_from_spare(unsigned char *buffer,
+ int numbytes);
+static int nandflash_wait_status(unsigned long status_mask);
+static inline int nandflash_wait_cmd(void);
+static inline int nandflash_wait_device(void);
+static inline int nandflash_wait_cache(void);
+static inline int nandflash_wait_spare(void);
+static int nandflash_check_ecc(void);
+#endif
+
+
+#if defined(CFG_RAMAPP)
+/** Variables for CFE RAM. **/
+CFE_NAND_CHIP g_chip = {NULL,0,0,0,0,0,0};
+static unsigned char g_spare_mask[] = SPARE_BI_ECC_MASK;
+static unsigned char g_spare_cleanmarker[SPARE_MAX_SIZE];
+
+static flash_device_info_t flash_nand_dev =
+ {
+ 0xffff,
+ FLASH_IFC_NAND,
+ "",
+ nand_flash_sector_erase_int,
+ nand_flash_read_buf,
+ nand_flash_write_buf,
+ nand_flash_get_numsectors,
+ nand_flash_get_sector_size,
+ nand_flash_get_memptr,
+ nand_flash_get_blk,
+ nand_flash_get_total_size
+ };
+
+#else
+/** Variables for CFE ROM. **/
+CFE_NAND_CHIP g_chip;
+static unsigned char g_spare_mask[] = SPARE_BI_ECC_MASK;
+#endif
+
+
+#if defined(CFG_RAMAPP)
+/***************************************************************************
+ * Function Name: nand_flash_init
+ * Description : Initialize flash part.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+int nand_flash_init(flash_device_info_t **flash_info)
+{
+ static int initialized = 0;
+ int ret = FLASH_API_OK;
+
+ if( initialized == 0 )
+ {
+ PCFE_NAND_CHIP pchip = &g_chip;
+ static struct flash_name_from_id fnfi[] = NAND_FLASH_DEVICES;
+ struct flash_name_from_id *fnfi_ptr;
+
+ DBG_PRINTF(">> nand_flash_init - entry\n");
+
+ /* Enable NAND data on MII ports. */
+#if !defined(_BCM96328_)
+ PERF->blkEnables |= NAND_CLK_EN;
+#endif
+#if defined(_BCM96362_) && (INC_SPI_FLASH_DRIVER==1)
+ GPIO->GPIOBaseMode |= NAND_GPIO_OVERRIDE;
+#endif
+ NAND->NandNandBootConfig = NBC_AUTO_DEV_ID_CFG | 2;
+#if (INC_SPI_FLASH_DRIVER==1)
+ cfe_usleep(1000);
+#endif
+ /* Read the NAND flash chip id. Only use the most signficant 16 bits.*/
+ pchip->chip_device_id = NAND->NandFlashDeviceId >> 16;
+ flash_nand_dev.flash_device_id = pchip->chip_device_id;
+
+ for( fnfi_ptr = fnfi; fnfi_ptr->fnfi_id != 0; fnfi_ptr++ )
+ {
+ if( fnfi_ptr->fnfi_id == pchip->chip_device_id )
+ {
+ strcpy(flash_nand_dev.flash_device_name, fnfi_ptr->fnfi_name);
+ break;
+ }
+ }
+
+ /* If NAND chip is not in the list of NAND chips, the correct
+ * configuration maybe still have been set by the NAND controller.
+ */
+ if( flash_nand_dev.flash_device_name[0] == '\0' )
+ strcpy(flash_nand_dev.flash_device_name, "<not identified>");
+
+ *flash_info = &flash_nand_dev;
+
+ NAND->NandCsNandXor = 0;
+ pchip->chip_base = 0;
+ nand_read_cfg(pchip);
+ nand_init_cleanmarker(pchip);
+
+ /* If the first block's spare area is not a JFFS2 cleanmarker,
+ * initialize all block's spare area to a cleanmarker.
+ */
+ if( !nand_is_blk_cleanmarker(pchip, 0, 0) )
+ ret = nand_initialize_spare_area(pchip);
+
+ DBG_PRINTF(">> nand_flash_init - return %d\n", ret);
+
+ initialized = 1;
+ }
+ else
+ *flash_info = &flash_nand_dev;
+
+ return( ret );
+} /* nand_flash_init */
+
+/***************************************************************************
+ * Function Name: nand_init_cleanmarker
+ * Description : Initializes the JFFS2 clean marker buffer.
+ * Returns : None.
+ ***************************************************************************/
+static void nand_init_cleanmarker(PCFE_NAND_CHIP pchip)
+{
+ unsigned short cleanmarker[] = JFFS2_CLEANMARKER;
+ unsigned char *pcm = (unsigned char *) cleanmarker;
+ int i, j;
+
+ /* Skip spare area offsets reserved for ECC bytes. */
+ for( i = 0, j = 0; i < pchip->chip_spare_size; i++ )
+ {
+ if( pchip->chip_spare_mask[i] == 0 && j < sizeof(cleanmarker))
+ g_spare_cleanmarker[i] = pcm[j++];
+ else
+ g_spare_cleanmarker[i] = 0xff;
+ }
+} /* nand_init_cleanmarker */
+
+#else
+/***************************************************************************
+ * Function Name: rom_nand_flash_init
+ * Description : Initialize flash part just enough to read blocks.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+void rom_nand_flash_init(void)
+{
+ PCFE_NAND_CHIP pchip = &g_chip;
+
+ /* Enable NAND data on MII ports. */
+#if !defined(_BCM96328_)
+ PERF->blkEnables |= NAND_CLK_EN;
+#endif
+ NAND->NandNandBootConfig = NBC_AUTO_DEV_ID_CFG | 2;
+
+ pchip->chip_base = 0;
+
+ /* Read the chip id. Only use the most signficant 16 bits. */
+ pchip->chip_device_id = NAND->NandFlashDeviceId >> 16;
+
+ if( pchip->chip_device_id > 0 && pchip->chip_device_id < 0xffff )
+ nand_read_cfg(pchip);
+ else
+ board_setleds(0x4d494532);
+} /* nand_flash_init */
+#endif
+
+/***************************************************************************
+ * Function Name: nand_read_cfg
+ * Description : Reads and stores the chip configuration.
+ * Returns : None.
+ ***************************************************************************/
+static void nand_read_cfg(PCFE_NAND_CHIP pchip)
+{
+ /* Read chip configuration. */
+ unsigned long cfg = NAND->NandConfig;
+
+ pchip->chip_total_size =
+ (4 * (1 << ((cfg & NC_DEV_SIZE_MASK) >> NC_DEV_SIZE_SHIFT))) << 20;
+
+ switch( (cfg & NC_BLK_SIZE_MASK) )
+ {
+ case NC_BLK_SIZE_512K:
+ pchip->chip_block_size = 512 * 1024;
+ break;
+
+ case NC_BLK_SIZE_128K:
+ pchip->chip_block_size = 128 * 1024;
+ break;
+
+ case NC_BLK_SIZE_16K:
+ pchip->chip_block_size = 16 * 1024;
+ break;
+
+ case NC_BLK_SIZE_8K:
+ pchip->chip_block_size = 8 * 1024;
+ break;
+ }
+
+ if( (cfg & NC_PG_SIZE_MASK) == NC_PG_SIZE_512B )
+ {
+ pchip->chip_page_size = 512;
+ pchip->chip_bi_index_1 = SPARE_SP_BI_INDEX_1;
+ pchip->chip_bi_index_2 = SPARE_SP_BI_INDEX_2;
+ }
+ else
+ {
+ pchip->chip_page_size = 2048;
+ pchip->chip_bi_index_1 = SPARE_LP_BI_INDEX_1;
+ pchip->chip_bi_index_2 = SPARE_LP_BI_INDEX_2;
+ }
+
+ pchip->chip_spare_mask = g_spare_mask;
+ pchip->chip_spare_mask[pchip->chip_bi_index_1] = 1;
+ pchip->chip_spare_mask[pchip->chip_bi_index_2] = 1;
+
+ pchip->chip_spare_size = pchip->chip_page_size >> 5;
+
+ DBG_PRINTF(">> nand_read_cfg - size=%luMB, block=%luKB, page=%luB, "
+ "spare=%lu\n", pchip->chip_total_size / (1024 * 1024),
+ pchip->chip_block_size / 1024, pchip->chip_page_size,
+ pchip->chip_spare_size);
+} /* nand_read_cfg */
+
+/***************************************************************************
+ * Function Name: nand_is_blk_cleanmarker
+ * Description : Compares a buffer to see if it a JFFS2 cleanmarker.
+ * Returns : 1 - is cleanmarker, 0 - is not cleanmarker
+ ***************************************************************************/
+static int nand_is_blk_cleanmarker(PCFE_NAND_CHIP pchip,
+ unsigned long start_addr, int write_if_not)
+{
+ unsigned short cleanmarker[] = JFFS2_CLEANMARKER;
+ unsigned char *pcm = (unsigned char *) cleanmarker;
+ unsigned char spare[SPARE_MAX_SIZE], comparebuf[SPARE_MAX_SIZE];
+ unsigned long i, j;
+ int ret = 0;
+
+ if( nandflash_read_spare_area( pchip, start_addr, spare,
+ pchip->chip_spare_size) == FLASH_API_OK )
+ {
+ /* Skip spare offsets that are reserved for the ECC. Make spare data
+ * bytes contiguous in the spare buffer.
+ */
+ for( i = 0, j = 0; i < pchip->chip_spare_size; i++ )
+ if( pchip->chip_spare_mask[i] == 0 )
+ comparebuf[j++] = spare[i];
+
+ /* Compare spare area data to the JFFS2 cleanmarker. */
+ for( i = 0, ret = 1; i < sizeof(cleanmarker) && ret == 1; i++ )
+ if( comparebuf[i] != pcm[i])
+ ret = 0;
+ }
+
+#if defined(CFG_RAMAPP)
+ if( ret == 0 && spare[pchip->chip_bi_index_1] != SPARE_BI_MARKER &&
+ spare[pchip->chip_bi_index_2] != SPARE_BI_MARKER && write_if_not )
+ {
+ /* The spare area is not a clean marker but the block is not bad.
+ * Write a clean marker to this block. (Assumes the block is erased.)
+ */
+ if( nandflash_write_spare_area(pchip, start_addr, (unsigned char *)
+ g_spare_cleanmarker, pchip->chip_spare_size) == FLASH_API_OK )
+ {
+ ret = nand_is_blk_cleanmarker(pchip, start_addr, 0);
+ }
+ }
+#endif
+
+ return( ret );
+} /* nand_is_blk_cleanmarker */
+
+#if defined(CFG_RAMAPP)
+/***************************************************************************
+ * Function Name: nand_initialize_spare_area
+ * Description : Initializes the spare area of the first page of each block
+ * to a cleanmarker.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nand_initialize_spare_area(PCFE_NAND_CHIP pchip)
+{
+ unsigned char spare[SPARE_MAX_SIZE];
+ unsigned long i;
+ int ret;
+
+ DBG_PRINTF(">> nand_initialize_spare_area - entry\n");
+
+ for( i = 0; i < pchip->chip_total_size; i += pchip->chip_block_size )
+ {
+ /* Read the current spare area. */
+ ret = nandflash_read_spare_area(pchip,0,spare,pchip->chip_spare_size);
+ if(ret == FLASH_API_OK
+ /*&& spare[pchip->chip_bi_index_1] != SPARE_BI_MARKER*/
+ /*&& spare[pchip->chip_bi_index_2] != SPARE_BI_MARKER*/)
+ {
+ if( nandflash_block_erase(pchip, i) == FLASH_API_OK )
+ {
+ nandflash_write_spare_area(pchip, i, (unsigned char *)
+ g_spare_cleanmarker, pchip->chip_spare_size);
+ }
+ }
+ }
+
+ return( FLASH_API_OK );
+} /* nand_initialize_spare_area */
+
+
+/***************************************************************************
+ * Function Name: nand_mark_bad_blk
+ * Description : Marks the specified block as bad by writing 0xFFs to the
+ * spare area and updating the in memory bad block table.
+ * Returns : None.
+ ***************************************************************************/
+static void nand_mark_bad_blk(PCFE_NAND_CHIP pchip, unsigned long page_addr)
+{
+ static int marking_bad_blk = 0;
+
+ unsigned char spare[SPARE_MAX_SIZE];
+
+ if( marking_bad_blk == 0 )
+ {
+ marking_bad_blk = 1;
+ DBG_PRINTF(">> nand_mark_bad_blk - addr=0x%8.8lx, block=0x%8.8lx\n",
+ page_addr, page_addr / pchip->chip_block_size);
+
+ nandflash_block_erase(pchip, page_addr);
+ memset(spare, 0xff, pchip->chip_spare_size);
+ spare[pchip->chip_bi_index_1] = SPARE_BI_MARKER;
+ spare[pchip->chip_bi_index_2] = SPARE_BI_MARKER;
+ nandflash_write_spare_area(pchip,page_addr,spare,pchip->chip_spare_size);
+ marking_bad_blk = 0;
+ }
+} /* nand_mark_bad_blk */
+
+
+/***************************************************************************
+ * Function Name: nand_flash_sector_erase_int
+ * Description : Erase the specfied flash sector.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nand_flash_sector_erase_int(unsigned short blk)
+{
+ int ret = FLASH_API_OK;
+ PCFE_NAND_CHIP pchip = &g_chip;
+
+ if( blk == NAND_REINIT_FLASH )
+ nand_initialize_spare_area(pchip);
+ else
+ {
+ unsigned long page_addr = blk * pchip->chip_block_size;
+
+ /* Only erase the block if the spare area is a JFFS2 cleanmarker.
+ * Assume that the only the CFE boot loader only touches JFFS2 blocks.
+ * This check prevents the Linux NAND MTD driver bad block block table
+ * from being erased. The NAND_REINIT_FLASH option unconditionally
+ * erases all NAND flash blocks.
+ */
+ if( nand_is_blk_cleanmarker(pchip, page_addr, 0) )
+ {
+ ret = nandflash_block_erase(pchip, page_addr);
+ nandflash_write_spare_area(pchip, page_addr, g_spare_cleanmarker,
+ pchip->chip_spare_size);
+ }
+
+ DBG_PRINTF(">> nand_flash_sector_erase_int - blk=0x%8.8lx, ret=%d\n",
+ blk, ret);
+ }
+
+ return( ret );
+} /* nand_flash_sector_erase_int */
+#endif
+
+/***************************************************************************
+ * Function Name: nand_flash_read_buf
+ * Description : Reads from flash memory.
+ * Returns : number of bytes read or FLASH_API_ERROR
+ ***************************************************************************/
+#if defined(CFG_RAMAPP)
+static
+#endif
+int nand_flash_read_buf(unsigned short blk, int offset, unsigned char *buffer,
+ int len)
+{
+ int ret = len;
+ PCFE_NAND_CHIP pchip = &g_chip;
+ UINT32 start_addr;
+ UINT32 blk_addr;
+ UINT32 blk_offset;
+ UINT32 size;
+
+ DBG_PRINTF(">> nand_flash_read_buf - 1 blk=0x%8.8lx, offset=%d, len=%lu\n",
+ blk, offset, len);
+
+ start_addr = (blk * pchip->chip_block_size) + offset;
+ blk_addr = start_addr & ~(pchip->chip_block_size - 1);
+ blk_offset = start_addr - blk_addr;
+ size = pchip->chip_block_size - blk_offset;
+
+ if(size > len)
+ size = len;
+
+ do
+ {
+ if(nandflash_read_page(pchip,start_addr,buffer,size) != FLASH_API_OK)
+ {
+ ret = FLASH_API_ERROR;
+ break;
+ }
+
+ len -= size;
+ if( len )
+ {
+ blk++;
+
+ DBG_PRINTF(">> nand_flash_read_buf - 2 blk=0x%8.8lx, len=%lu\n",
+ blk, len);
+
+ start_addr = blk * pchip->chip_block_size;
+ buffer += size;
+ if(len > pchip->chip_block_size)
+ size = pchip->chip_block_size;
+ else
+ size = len;
+ }
+ } while(len);
+
+ DBG_PRINTF(">> nand_flash_read_buf - ret=%d\n", ret);
+
+ return( ret ) ;
+} /* nand_flash_read_buf */
+
+#if defined(CFG_RAMAPP)
+/***************************************************************************
+ * Function Name: nand_flash_write_buf
+ * Description : Writes to flash memory.
+ * Returns : number of bytes written or FLASH_API_ERROR
+ ***************************************************************************/
+static int nand_flash_write_buf(unsigned short blk, int offset,
+ unsigned char *buffer, int len)
+{
+ int ret = len;
+ PCFE_NAND_CHIP pchip = &g_chip;
+ UINT32 start_addr;
+ UINT32 blk_addr;
+ UINT32 blk_offset;
+ UINT32 size;
+
+ DBG_PRINTF(">> nand_flash_write_buf - 1 blk=0x%8.8lx, offset=%d, len=%d\n",
+ blk, offset, len);
+
+ start_addr = (blk * pchip->chip_block_size) + offset;
+ blk_addr = start_addr & ~(pchip->chip_block_size - 1);
+ blk_offset = start_addr - blk_addr;
+ size = pchip->chip_block_size - blk_offset;
+
+ if(size > len)
+ size = len;
+
+ do
+ {
+ if(nandflash_write_page(pchip,start_addr,buffer,size) != FLASH_API_OK)
+ {
+ ret = ret - len;
+ break;
+ }
+ else
+ {
+ len -= size;
+ if( len )
+ {
+ blk++;
+
+ DBG_PRINTF(">> nand_flash_write_buf- 2 blk=0x%8.8lx, len=%d\n",
+ blk, len);
+
+ offset = 0;
+ start_addr = blk * pchip->chip_block_size;
+ buffer += size;
+ if(len > pchip->chip_block_size)
+ size = pchip->chip_block_size;
+ else
+ size = len;
+ }
+ }
+ } while(len);
+
+ DBG_PRINTF(">> nand_flash_write_buf - ret=%d\n", ret);
+
+ return( ret ) ;
+} /* nand_flash_write_buf */
+
+/***************************************************************************
+ * Function Name: nand_flash_get_memptr
+ * Description : Returns the base MIPS memory address for the specfied flash
+ * sector.
+ * Returns : Base MIPS memory address for the specfied flash sector.
+ ***************************************************************************/
+static unsigned char *nand_flash_get_memptr(unsigned short sector)
+{
+ /* Bad things will happen if this pointer is referenced. But it can
+ * be used for pointer arithmetic to deterine sizes.
+ */
+ return((unsigned char *) (FLASH_BASE + (sector * g_chip.chip_block_size)));
+} /* nand_flash_get_memptr */
+
+/***************************************************************************
+ * Function Name: nand_flash_get_blk
+ * Description : Returns the flash sector for the specfied MIPS address.
+ * Returns : Flash sector for the specfied MIPS address.
+ ***************************************************************************/
+static int nand_flash_get_blk(int addr)
+{
+ return( (addr - FLASH_BASE) / g_chip.chip_block_size );
+} /* nand_flash_get_blk */
+
+/***************************************************************************
+ * Function Name: nand_flash_get_total_size
+ * Description : Returns the number of bytes in the "CFE Linux code"
+ * partition.
+ * Returns : Number of bytes
+ ***************************************************************************/
+static int nand_flash_get_total_size(void)
+{
+ return(g_chip.chip_total_size);
+} /* nand_flash_get_total_size */
+#endif
+
+/***************************************************************************
+ * Function Name: nand_flash_get_sector_size
+ * Description : Returns the number of bytes in the specfied flash sector.
+ * Returns : Number of bytes in the specfied flash sector.
+ ***************************************************************************/
+#if defined(CFG_RAMAPP)
+static
+#endif
+int nand_flash_get_sector_size(unsigned short sector)
+{
+ return(g_chip.chip_block_size);
+} /* nand_flash_get_sector_size */
+
+/***************************************************************************
+ * Function Name: nand_flash_get_numsectors
+ * Description : Returns the number of blocks in the "CFE Linux code"
+ * partition.
+ * Returns : Number of blocks
+ ***************************************************************************/
+#if defined(CFG_RAMAPP)
+static
+#endif
+int nand_flash_get_numsectors(void)
+{
+ return(g_chip.chip_total_size / g_chip.chip_block_size);
+} /* nand_flash_get_numsectors */
+
+
+/***************************************************************************
+ * NAND Flash Implementation Functions
+ ***************************************************************************/
+
+/***************************************************************************
+ * Function Name: nandflash_copy_from_cache
+ * Description : Copies data from the chip NAND cache to a local memory
+ * buffer.
+ * Returns : None.
+ ***************************************************************************/
+static inline void nandflash_copy_from_cache(unsigned char *buffer,
+ int offset, int numbytes)
+{
+ unsigned char *cache = (unsigned char *) NAND_CACHE;
+
+ /* XXX memcpy will only work for 32-bit aligned data */
+ memcpy(buffer, &cache[offset], numbytes);
+} /* nandflash_copy_from_cache */
+
+/***************************************************************************
+ * Function Name: nandflash_copy_from_spare
+ * Description : Copies data from the chip NAND spare registers to a local
+ * memory buffer.
+ * Returns : None.
+ ***************************************************************************/
+static inline void nandflash_copy_from_spare(unsigned char *buffer,
+ int numbytes)
+{
+ unsigned long *spare_area = (unsigned long *) &NAND->NandSpareAreaReadOfs0;
+
+ /* XXX memcpy will only work for 32-bit aligned data */
+ memcpy(buffer, spare_area, numbytes);
+} /* nandflash_copy_from_spare */
+
+#if defined(CFG_RAMAPP)
+/***************************************************************************
+ * Function Name: nandflash_copy_to_cache
+ * Description : Copies data from a local memory buffer to the the chip NAND
+ * cache.
+ * Returns : None.
+ ***************************************************************************/
+static inline void nandflash_copy_to_cache(unsigned char *buffer, int offset,
+ int numbytes)
+{
+ unsigned char *cache = (unsigned char *) NAND_CACHE;
+ unsigned long i;
+
+ for( i = 0; i < numbytes; i += sizeof(long) )
+ *(unsigned long *) &cache[i] =
+ ((unsigned long) buffer[i + 0] << 24) |
+ ((unsigned long) buffer[i + 1] << 16) |
+ ((unsigned long) buffer[i + 2] << 8) |
+ ((unsigned long) buffer[i + 3] << 0);
+} /* nandflash_copy_to_cache */
+
+/***************************************************************************
+ * Function Name: nandflash_copy_to_spare
+ * Description : Copies data from a local memory buffer to the the chip NAND
+ * spare registers.
+ * Returns : None.
+ ***************************************************************************/
+static inline void nandflash_copy_to_spare(unsigned char *buffer,int numbytes)
+{
+ unsigned long *spare_area = (unsigned long *) &NAND->NandSpareAreaWriteOfs0;
+ unsigned long *pbuff = (unsigned long *)buffer;
+ int i;
+
+ for(i=0; i< numbytes / sizeof(unsigned long); ++i)
+ spare_area[i] = pbuff[i];
+} /* nandflash_copy_to_spare */
+#endif
+
+/***************************************************************************
+ * Function Name: nandflash_wait_status
+ * Description : Polls the NAND status register waiting for a condition.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nandflash_wait_status(unsigned long status_mask)
+{
+
+ const unsigned long nand_poll_max = 1000000;
+ unsigned long data;
+ unsigned long poll_count = 0;
+ int ret = FLASH_API_OK;
+
+ do
+ {
+ data = NAND->NandIntfcStatus;
+ } while(!(status_mask & data) && (++poll_count < nand_poll_max));
+
+ if(poll_count >= nand_poll_max)
+ {
+ printf("Status wait timeout: nandsts=0x%8.8lx mask=0x%8.8lx, count="
+ "%lu\n", NAND->NandIntfcStatus, status_mask, poll_count);
+ ret = FLASH_API_ERROR;
+ }
+
+ return( ret );
+} /* nandflash_wait_status */
+
+static inline int nandflash_wait_cmd(void)
+{
+ return nandflash_wait_status(NIS_CTLR_READY);
+} /* nandflash_wait_cmd */
+
+static inline int nandflash_wait_device(void)
+{
+ return nandflash_wait_status(NIS_FLASH_READY);
+} /* nandflash_wait_device */
+
+static inline int nandflash_wait_cache(void)
+{
+ return nandflash_wait_status(NIS_CACHE_VALID);
+} /* nandflash_wait_cache */
+
+static inline int nandflash_wait_spare(void)
+{
+ return nandflash_wait_status(NIS_SPARE_VALID);
+} /* nandflash_wait_spare */
+
+#if defined(CFG_RAMAPP)
+/***************************************************************************
+ * Function Name: nandflash_check_ecc
+ * Description : Reads ECC status.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nandflash_check_ecc(void)
+{
+ int ret = FLASH_API_OK;
+ UINT32 intrCtrl;
+ UINT32 accessCtrl;
+
+ /* read interrupt status */
+ intrCtrl = NAND_INTR->NandInterrupt;
+ accessCtrl = NAND->NandAccControl;
+
+
+ if( (intrCtrl & NINT_ECC_ERROR_UNC) != 0 )
+ {
+ printf("Uncorrectable ECC Error detected: addr=0x%8.8lx, intrCtrl=0x"
+ "%08X, accessCtrl=0x%08X\n", NAND->NandEccUncAddr, (UINT)intrCtrl,
+ (UINT)accessCtrl);
+ ret = FLASH_API_ERROR;
+ }
+
+ if( (intrCtrl & NINT_ECC_ERROR_CORR) != 0 )
+ {
+ printf("Correctable ECC Error detected: addr=0x%8.8lx, intrCtrl=0x"
+ "%08X, accessCtrl=0x%08X\n", NAND->NandEccCorrAddr, (UINT)intrCtrl,
+ (UINT)accessCtrl);
+ }
+
+ return( ret );
+}
+#else
+static int nandflash_check_ecc(void)
+{
+ return( FLASH_API_OK );
+}
+#endif
+
+/***************************************************************************
+ * Function Name: nandflash_read_spare_area
+ * Description : Reads the spare area for the specified page.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nandflash_read_spare_area(PCFE_NAND_CHIP pchip,
+ unsigned long page_addr, unsigned char *buffer, int len)
+{
+ int ret = FLASH_API_ERROR;
+
+ if( len >= pchip->chip_spare_size )
+ {
+ UINT32 steps = pchip->chip_spare_size / CTRLR_SPARE_SIZE;
+ UINT32 i;
+
+ for( i = 0; i < steps; i++ )
+ {
+ NAND->NandCmdAddr = pchip->chip_base + page_addr +
+ (i * CTRLR_CACHE_SIZE);
+ NAND->NandCmdExtAddr = 0;
+ NAND->NandCmdStart = NCMD_SPARE_READ;
+
+ if( (ret = nandflash_wait_cmd()) == FLASH_API_OK )
+ {
+ /* wait until data is available in the spare area registers */
+ if( (ret = nandflash_wait_spare()) == FLASH_API_OK )
+ nandflash_copy_from_spare(buffer + (i * CTRLR_SPARE_SIZE),
+ CTRLR_SPARE_SIZE);
+ else
+ break;
+ }
+ else
+ break;
+ }
+ }
+
+ return ret;
+} /* nandflash_read_spare_area */
+
+#if defined(CFG_RAMAPP)
+/***************************************************************************
+ * Function Name: nandflash_write_spare_area
+ * Description : Reads the spare area for the specified page.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nandflash_write_spare_area(PCFE_NAND_CHIP pchip,
+ unsigned long page_addr, unsigned char *buffer, int len)
+{
+ int ret = FLASH_API_OK;
+ unsigned char spare[SPARE_MAX_SIZE];
+
+ if( len <= pchip->chip_spare_size )
+ {
+ UINT32 steps = pchip->chip_spare_size / CTRLR_SPARE_SIZE;
+ UINT32 i;
+
+ memset(spare, 0xff, pchip->chip_spare_size);
+ memcpy(spare, buffer, len);
+
+ for( i = 0; i < steps; i++ )
+ {
+ NAND->NandCmdAddr = pchip->chip_base + page_addr +
+ (i * CTRLR_CACHE_SIZE);
+ NAND->NandCmdExtAddr = 0;
+
+ nandflash_copy_to_spare(spare + (i * CTRLR_SPARE_SIZE),
+ CTRLR_SPARE_SIZE);
+
+ NAND->NandCmdStart = NCMD_PROGRAM_SPARE;
+ if( (ret = nandflash_wait_cmd()) == FLASH_API_OK )
+ {
+ unsigned long sts = NAND->NandIntfcStatus;
+
+ if( (sts & NIS_PGM_ERASE_ERROR) != 0 )
+ {
+ printf("Error writing to spare area, sts=0x%8.8lx\n", sts);
+ nand_mark_bad_blk(pchip, page_addr);
+ ret = FLASH_API_ERROR;
+ }
+ }
+ }
+ }
+ else
+ ret = FLASH_API_ERROR;
+
+ /* Reset spare area to default value. */
+ memset(spare, 0xff, CTRLR_SPARE_SIZE);
+ nandflash_copy_to_spare(spare, CTRLR_SPARE_SIZE);
+
+ return( ret );
+} /* nandflash_write_spare_area */
+#endif
+
+/***************************************************************************
+ * Function Name: nandflash_read_page
+ * Description : Reads up to a NAND block of pages into the specified buffer.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nandflash_read_page(PCFE_NAND_CHIP pchip, unsigned long start_addr,
+ unsigned char *buffer, int len)
+{
+ int ret = FLASH_API_ERROR;
+
+ if( len <= pchip->chip_block_size )
+ {
+ UINT32 page_addr = start_addr & ~(pchip->chip_page_size - 1);
+ UINT32 page_offset = start_addr - page_addr;
+ UINT32 size = pchip->chip_page_size - page_offset;
+ UINT32 index = 0;
+
+ /* Verify that the spare area contains a JFFS2 cleanmarker. */
+ if( nand_is_blk_cleanmarker(pchip, page_addr, 0) )
+ {
+ UINT32 i;
+
+ if(size > len)
+ size = len;
+
+ do
+ {
+ for( i = 0, ret = FLASH_API_OK; i < pchip->chip_page_size &&
+ ret == FLASH_API_OK; i += CTRLR_CACHE_SIZE)
+ {
+ /* clear interrupts, so we can check later for ECC errors */
+ NAND_INTR->NandInterrupt = NINT_STS_MASK;
+
+ /* send command */
+ NAND->NandCmdAddr = pchip->chip_base + page_addr + i;
+ NAND->NandCmdExtAddr = 0;
+ NAND->NandCmdStart = NCMD_PAGE_READ;
+
+ if( (ret = nandflash_wait_cmd()) == FLASH_API_OK )
+ {
+ /* wait until data is available in the cache */
+ if( (ret = nandflash_wait_cache()) == FLASH_API_OK )
+ {
+ /* TBD. get return status for ECC errors and
+ * process them.
+ */
+ nandflash_check_ecc(); /* check for ECC errors */
+ }
+
+ if( ret == FLASH_API_OK )
+ {
+ if( i < size )
+ {
+ UINT32 copy_size =
+ (i + CTRLR_CACHE_SIZE <= size)
+ ? CTRLR_CACHE_SIZE : size - i;
+
+ nandflash_copy_from_cache(&buffer[index + i],
+ page_offset, copy_size);
+ }
+ }
+ else
+ {
+ /* TBD. Do something. */
+ }
+ }
+ }
+
+ if(ret != FLASH_API_OK)
+ break;
+
+ page_offset = 0;
+ page_addr += pchip->chip_page_size;
+ index += size;
+ len -= size;
+ if(len > pchip->chip_page_size)
+ size = pchip->chip_page_size;
+ else
+ size = len;
+ } while(len);
+ }
+ else
+ {
+ DBG_PRINTF("nandflash_read_page: cleanmarker not found at 0x%8.8lx\n",
+ page_addr);
+ }
+ }
+
+ return( ret ) ;
+} /* nandflash_read_page */
+
+#if defined(CFG_RAMAPP)
+/***************************************************************************
+ * Function Name: nandflash_write_page
+ * Description : Writes up to a NAND block of pages from the specified buffer.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nandflash_write_page(PCFE_NAND_CHIP pchip, unsigned long start_addr,
+ unsigned char *buffer, int len)
+{
+ int ret = FLASH_API_ERROR;
+
+ if( len <= pchip->chip_block_size )
+ {
+ unsigned char xfer_buf[CTRLR_CACHE_SIZE];
+ UINT32 page_addr = start_addr & ~(pchip->chip_page_size - 1);
+ UINT32 page_offset = start_addr - page_addr;
+ UINT32 size = pchip->chip_page_size - page_offset;
+ UINT32 index = 0;
+
+ /* Verify that the spare area contains a JFFS2 cleanmarker. */
+ if( nand_is_blk_cleanmarker(pchip, page_addr, 1) )
+ {
+ UINT32 steps = pchip->chip_page_size / CTRLR_CACHE_SIZE;
+ UINT32 i, xfer_ofs, xfer_size;
+
+ if(size > len)
+ size = len;
+
+ do
+ {
+ for( i = 0, xfer_ofs = 0, xfer_size = 0, ret = FLASH_API_OK;
+ i < steps && ret==FLASH_API_OK; i++)
+ {
+ memset(xfer_buf, 0xff, sizeof(xfer_buf));
+
+ if(size - xfer_ofs > CTRLR_CACHE_SIZE)
+ xfer_size = CTRLR_CACHE_SIZE;
+ else
+ xfer_size = size - xfer_ofs;
+
+ if( xfer_size )
+ memcpy(xfer_buf + page_offset, buffer + index +
+ xfer_ofs, xfer_size);
+
+ xfer_ofs += xfer_size;
+
+ NAND->NandCmdAddr = pchip->chip_base + page_addr +
+ (i * CTRLR_CACHE_SIZE);
+ NAND->NandCmdExtAddr = 0;
+
+ nandflash_copy_to_cache(xfer_buf, 0, CTRLR_CACHE_SIZE);
+
+ NAND->NandCmdStart = NCMD_PROGRAM_PAGE;
+ if( (ret = nandflash_wait_cmd()) == FLASH_API_OK )
+ {
+ unsigned long sts = NAND->NandIntfcStatus;
+
+ if( (sts & NIS_PGM_ERASE_ERROR) != 0 )
+ {
+ printf("Error writing to block, sts=0x%8.8lx\n", sts);
+ nand_mark_bad_blk(pchip,
+ start_addr & ~(pchip->chip_page_size - 1));
+ ret = FLASH_API_ERROR;
+ }
+ }
+ }
+
+ if(ret != FLASH_API_OK)
+ break;
+
+ page_offset = 0;
+ page_addr += pchip->chip_page_size;
+ index += size;
+ len -= size;
+ if(len > pchip->chip_page_size)
+ size = pchip->chip_page_size;
+ else
+ size = len;
+ } while(len);
+ }
+ else
+ DBG_PRINTF("nandflash_write_page: cleanmarker not found at 0x%8.8lx\n",
+ page_addr);
+ }
+
+ return( ret );
+} /* nandflash_write_page */
+
+/***************************************************************************
+ * Function Name: nandflash_block_erase
+ * Description : Erases a block.
+ * Returns : FLASH_API_OK or FLASH_API_ERROR
+ ***************************************************************************/
+static int nandflash_block_erase(PCFE_NAND_CHIP pchip, unsigned long blk_addr )
+{
+
+ int ret = FLASH_API_OK;
+
+ /* send command */
+ NAND->NandCmdAddr = pchip->chip_base + blk_addr;
+ NAND->NandCmdExtAddr = 0;
+ NAND->NandCmdStart = NCMD_BLOCK_ERASE;
+ if( (ret = nandflash_wait_cmd()) == FLASH_API_OK )
+ {
+ unsigned long sts = NAND->NandIntfcStatus;
+
+ if( (sts & NIS_PGM_ERASE_ERROR) != 0 )
+ {
+ printf("Error erasing block 0x%8.8lx, sts=0x%8.8lx\n",
+ blk_addr, sts);
+ nand_mark_bad_blk(pchip, blk_addr);
+ ret = FLASH_API_ERROR;
+ }
+ }
+
+ DBG_PRINTF(">> nandflash_block_erase - addr=0x%8.8lx, ret=%d\n", blk_addr,
+ ret);
+
+ return( ret );
+} /* nandflash_block_erase */
+
+void dump_spare(void);
+void dump_spare(void)
+{
+ PCFE_NAND_CHIP pchip = &g_chip;
+ unsigned char spare[SPARE_MAX_SIZE];
+ unsigned long i;
+
+ for( i = 0; i < pchip->chip_total_size; i += pchip->chip_block_size )
+ {
+ if( nandflash_read_spare_area(pchip, i, spare,
+ pchip->chip_spare_size) == FLASH_API_OK )
+ {
+ printf("%8.8lx: %8.8lx %8.8lx %8.8lx %8.8lx\n", i,
+ *(unsigned long *) &spare[0], *(unsigned long *) &spare[4],
+ *(unsigned long *) &spare[8], *(unsigned long *) &spare[12]);
+ if( pchip->chip_spare_size == SPARE_MAX_SIZE )
+ {
+ printf("%8.8lx: %8.8lx %8.8lx %8.8lx %8.8lx\n", i,
+ *(unsigned long *)&spare[16],*(unsigned long *)&spare[20],
+ *(unsigned long *)&spare[24],*(unsigned long *)&spare[28]);
+ printf("%8.8lx: %8.8lx %8.8lx %8.8lx %8.8lx\n", i,
+ *(unsigned long *)&spare[32],*(unsigned long *)&spare[36],
+ *(unsigned long *)&spare[40],*(unsigned long *)&spare[44]);
+ printf("%8.8lx: %8.8lx %8.8lx %8.8lx %8.8lx\n", i,
+ *(unsigned long *)&spare[48],*(unsigned long *)&spare[52],
+ *(unsigned long *)&spare[56],*(unsigned long *)&spare[60]);
+ }
+ }
+ else
+ printf("Error reading spare 0x%8.8lx\n", i);
+ }
+}
+
+int read_spare_data(int blk, unsigned char *buf, int bufsize);
+int read_spare_data(int blk, unsigned char *buf, int bufsize)
+{
+ PCFE_NAND_CHIP pchip = &g_chip;
+ unsigned char spare[SPARE_MAX_SIZE];
+ unsigned long page_addr = blk * pchip->chip_block_size;
+ unsigned long i, j;
+ int ret;
+
+ if( (ret = nandflash_read_spare_area( pchip, page_addr, spare,
+ pchip->chip_spare_size)) == FLASH_API_OK )
+ {
+ /* Skip spare offsets that are reserved for the ECC. Make spare data
+ * bytes contiguous in the spare buffer.
+ */
+ for( i = 0, j = 0; i < pchip->chip_spare_size; i++ )
+ if( pchip->chip_spare_mask[i] == 0 && j < bufsize )
+ buf[j++] = spare[i];
+ }
+
+ return(ret);
+}
+
+#endif
+
diff --git a/shared/opensource/flash/spiflash.c b/shared/opensource/flash/spiflash.c
new file mode 100755
index 0000000..38f4648
--- /dev/null
+++ b/shared/opensource/flash/spiflash.c
@@ -0,0 +1,969 @@
+/*
+ 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 "bcm_map.h"
+#define printk printf
+#else // linux
+#include <linux/version.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
+#include <linux/semaphore.h>
+#endif
+#include <linux/hardirq.h>
+
+#include <bcm_map_part.h>
+#endif
+
+#include "bcmtypes.h"
+#include "bcm_hwdefs.h"
+#include "flash_api.h"
+#include "bcmSpiRes.h"
+
+
+/** Defines. **/
+#define OSL_DELAY(X) \
+ do { { int i; for( i = 0; i < (X) * 500; i++ ) ; } } while(0)
+
+#define MAX_RETRY 3
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#define MAXSECTORS 8192 /* maximum number of sectors supported */
+
+#define FLASH_PAGE_256 256
+#define SECTOR_SIZE_4K (4 * 1024)
+#define SECTOR_SIZE_64K (64 * 1024)
+
+/* Standard Boolean declarations */
+#define TRUE 1
+#define FALSE 0
+
+/* Command codes for the flash_command routine */
+#define FLASH_WRST 0x01 /* write status register */
+#define FLASH_PROG 0x02 /* program data into memory array */
+#define FLASH_READ 0x03 /* read data from memory array */
+#define FLASH_WRDI 0x04 /* reset write enable latch */
+#define FLASH_RDSR 0x05 /* read status register */
+#define FLASH_WREN 0x06 /* set write enable latch */
+#define FLASH_READ_FAST 0x0B /* read data from memory array */
+#define FLASH_SERASE 0x20 /* erase one sector in memory array */
+#define FLASH_BERASE 0xD8 /* erase one block in memory array */
+#define FLASH_RDID 0x9F /* read manufacturer and product id */
+#define FLASH_EN4B 0xB7 /* Enable 4 byte address mode */
+
+/* RDSR return status bit definition */
+#define SR_WPEN 0x80
+#define SR_BP3 0x20
+#define SR_BP2 0x10
+#define SR_BP1 0x08
+#define SR_BP0 0x04
+#define SR_WEN 0x02
+#define SR_RDY 0x01
+
+/* Return codes from flash_status */
+#define STATUS_READY 0 /* ready for action */
+#define STATUS_BUSY 1 /* operation in progress */
+#define STATUS_TIMEOUT 2 /* operation timed out */
+#define STATUS_ERROR 3 /* unclassified but unhappy status */
+
+/* Define different type of flash */
+#define FLASH_UNDEFINED 0
+#define FLASH_SPAN 2
+
+/* SST's manufacturer ID */
+#define SSTPART 0xBF
+/* A list of SST device IDs */
+#define ID_SST25VF016 0x41
+#define ID_SST25VF032 0x4A
+#define ID_SST25VF064 0x4B
+
+/* SPANSION manufacturer IDs */
+#define SPANPART 0x01
+/* SPANSION device ID's */
+#define ID_SPAN25FL016 0x14
+#define ID_SPAN25FL032 0x15
+#define ID_SPAN25FL064 0x16
+#define ID_SPAN25FL128 0x18
+
+/* EON manufacturer ID */
+#define EONPART 0x1C
+/* NUMONYX manufacturer ID */
+#define NUMONYXPART 0x20
+/* AMIC manufacturer ID */
+#define AMICPART 0x37
+/* Macronix manufacturer ID */
+#define MACRONIXPART 0xC2
+/* Winbond's manufacturer ID */
+#define WBPART 0xEF
+
+/* JEDEC device IDs */
+#define ID_M25P16 0x15
+#define ID_M25P32 0x16
+#define ID_M25P64 0x17
+#define ID_M25P128 0x18
+#define ID_M25P256 0x19
+
+#define SPI_MAKE_ID(A,B) \
+ (((unsigned short) (A) << 8) | ((unsigned short) B & 0xff))
+
+#define SPI_FLASH_DEVICES \
+ {{SPI_MAKE_ID(SSTPART, ID_SST25VF016), "SST25VF016"}, \
+ {SPI_MAKE_ID(SSTPART, ID_SST25VF032), "SST25VF032"}, \
+ {SPI_MAKE_ID(SSTPART, ID_SST25VF064), "SST25VF064"}, \
+ {SPI_MAKE_ID(SPANPART, ID_SPAN25FL016), "S25FL016"}, \
+ {SPI_MAKE_ID(SPANPART, ID_SPAN25FL032), "S25FL032"}, \
+ {SPI_MAKE_ID(SPANPART, ID_SPAN25FL064), "S25FL064"}, \
+ {SPI_MAKE_ID(SPANPART, ID_SPAN25FL128), "S25FL128"}, \
+ {SPI_MAKE_ID(WBPART, ID_M25P16), "ID_W25X16"}, \
+ {SPI_MAKE_ID(WBPART, ID_M25P32), "ID_W25X32"}, \
+ {SPI_MAKE_ID(WBPART, ID_M25P64), "ID_W25X64"}, \
+ {SPI_MAKE_ID(WBPART, ID_M25P128), "ID_W25X128"}, \
+ {SPI_MAKE_ID(EONPART, ID_M25P16), "EN25P16"}, \
+ {SPI_MAKE_ID(EONPART, ID_M25P32), "EN25P32"}, \
+ {SPI_MAKE_ID(EONPART, ID_M25P64), "EN25P64"}, \
+ {SPI_MAKE_ID(EONPART, ID_M25P128), "EN25P128"}, \
+ {SPI_MAKE_ID(AMICPART, ID_M25P16), "A25L016"}, \
+ {SPI_MAKE_ID(AMICPART, ID_M25P32), "A25L032"}, \
+ {SPI_MAKE_ID(NUMONYXPART, ID_M25P16), "NMNXM25P16"}, \
+ {SPI_MAKE_ID(NUMONYXPART, ID_M25P32), "NMNXM25P32"}, \
+ {SPI_MAKE_ID(NUMONYXPART, ID_M25P64), "NMNXM25P64"}, \
+ {SPI_MAKE_ID(NUMONYXPART, ID_M25P128), "NMNXM25P128"}, \
+ {SPI_MAKE_ID(MACRONIXPART, ID_M25P16), "MX25L16"}, \
+ {SPI_MAKE_ID(MACRONIXPART, ID_M25P32), "MX25L32"}, \
+ {SPI_MAKE_ID(MACRONIXPART, ID_M25P64), "MX25L64"}, \
+ {SPI_MAKE_ID(MACRONIXPART, ID_M25P128), "MX25L128"}, \
+ {SPI_MAKE_ID(MACRONIXPART, ID_M25P256), "MX25L256"}, \
+ {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; /* "AT25F512", etc. */
+ unsigned long addr; /* physical address, once translated */
+ int nsect; /* # of sectors */
+ struct {
+ long size; /* # of bytes in this sector */
+ long base; /* offset from beginning of device */
+ } sec[MAXSECTORS]; /* per-sector info */
+};
+
+struct flash_name_from_id {
+ unsigned short fnfi_id;
+ char fnfi_name[30];
+};
+
+
+/** Prototypes. **/
+static int my_spi_read( unsigned char *msg_buf, int prependcnt, int nbytes );
+static int my_spi_write( unsigned char *msg_buf, int nbytes );
+
+int spi_flash_init(flash_device_info_t **flash_info);
+static int spi_flash_sector_erase_int(unsigned short sector);
+static int spi_flash_reset(void);
+static int spi_flash_read_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int nbytes);
+static int spi_flash_ub(unsigned short sector);
+static int spi_flash_write(unsigned short sector, int offset,
+ unsigned char *buffer, int nbytes);
+static int spi_flash_write_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int nbytes);
+static int spi_flash_get_numsectors(void);
+static int spi_flash_get_sector_size(unsigned short sector);
+static unsigned char *spi_get_flash_memptr(unsigned short sector);
+static unsigned char *spi_flash_get_memptr(unsigned short sector);
+static int spi_flash_status(void);
+static unsigned short spi_flash_get_device_id(unsigned short sector);
+static int spi_flash_get_blk(int addr);
+static int spi_flash_get_total_size(void);
+static int spi_flash_en4b(void);
+
+/** Variables. **/
+static flash_device_info_t flash_spi_dev =
+ {
+ 0xffff,
+ FLASH_IFC_SPI,
+ "",
+ spi_flash_sector_erase_int,
+ spi_flash_read_buf,
+ spi_flash_write_buf,
+ spi_flash_get_numsectors,
+ spi_flash_get_sector_size,
+ spi_flash_get_memptr,
+ spi_flash_get_blk,
+ spi_flash_get_total_size
+ };
+
+static struct flash_name_from_id fnfi[] = SPI_FLASH_DEVICES;
+
+/* the controller will handle operati0ns that are greater than the FIFO size
+ code that relies on READ_BUF_LEN_MAX, READ_BUF_LEN_MIN or spi_max_op_len
+ could be changed */
+#define READ_BUF_LEN_MAX 544 /* largest of the maximum transaction sizes for SPI */
+#define READ_BUF_LEN_MIN 60 /* smallest of the maximum transaction sizes for SPI */
+/* this is the slave ID of the SPI flash for use with the SPI controller */
+#define SPI_FLASH_SLAVE_DEV_ID 0
+/* clock defines for the flash */
+#define SPI_FLASH_DEF_CLOCK 781000
+
+/* default to smallest transaction size - updated later */
+static int spi_max_op_len = READ_BUF_LEN_MIN;
+static int fastRead = TRUE;
+static int flash_page_size = FLASH_PAGE_256;
+
+/* default to legacy controller - updated later */
+static int spi_flash_clock = SPI_FLASH_DEF_CLOCK;
+static int spi_flash_busnum = LEG_SPI_BUS_NUM;
+
+#ifndef _CFE_
+static DECLARE_MUTEX(spi_flash_lock);
+static bool bSpiFlashSlaveRes = FALSE;
+#endif
+
+static struct flashinfo meminfo; /* Flash information structure */
+static int totalSize = 0;
+static int addr32 = FALSE;
+
+static int my_spi_read(unsigned char *msg_buf, int prependcnt, int nbytes)
+{
+ int status;
+
+#ifndef _CFE_
+ if ( FALSE == bSpiFlashSlaveRes )
+#endif
+ {
+ status = BcmSpi_Read(msg_buf, prependcnt, nbytes, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID, spi_flash_clock);
+ }
+#ifndef _CFE_
+ else
+ {
+ /* the Linux SPI framework provides a non blocking mechanism for SPI transfers. While waiting for a spi
+ transaction to complete the kernel will look to see if another process can run. This scheduling
+ can only occur if kernel preemption is active. The SPI flash interfaces can be run when kernel
+ preemption is enabled or disabled. When kernel preemption is disabled we cannot use the framework */
+ if ( in_atomic() )
+ status = BcmSpi_Read(msg_buf, prependcnt, nbytes, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID, spi_flash_clock);
+ else
+ status = BcmSpiSyncTrans(NULL, msg_buf, prependcnt, nbytes, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID);
+ }
+#endif
+
+ return status;
+}
+
+static int my_spi_write(unsigned char *msg_buf, int nbytes)
+{
+ int status;
+
+#ifndef _CFE_
+ if ( FALSE == bSpiFlashSlaveRes )
+#endif
+ {
+ status = BcmSpi_Write(msg_buf, nbytes, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID, spi_flash_clock);
+ }
+#ifndef _CFE_
+ else
+ {
+ /* the Linux SPI framework provides a non blocking mechanism for SPI transfers. While waiting for a spi
+ transaction to complete the kernel will look to see if another process can run. This scheduling
+ can only occur if kernel preemtion is active. The SPI flash interfaces can be run when kernel
+ preemption is enabled or disabled. When kernel preemption is disabled we cannot use the framework */
+ if ( in_atomic() )
+ status = BcmSpi_Write(msg_buf, nbytes, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID, spi_flash_clock);
+ else
+ status = BcmSpiSyncTrans(msg_buf, NULL, 0, nbytes, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID);
+ }
+#endif
+ return status;
+}
+
+
+/*********************************************************************/
+/* Init_flash is used to build a sector table. 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 44 bytes of heap space, depending */
+/* on the value of the define MAXSECTORS. Adjust to suit */
+/* application */
+/*********************************************************************/
+
+int spi_flash_init(flash_device_info_t **flash_info)
+{
+ struct flash_name_from_id *fnfi_ptr;
+ int i=0, count=0;
+ int basecount=0L;
+ unsigned short device_id;
+ int sectorsize = 0;
+ int numsector = 0;
+
+#if defined(_BCM96816_) || defined(CONFIG_BCM96816)
+ uint32 miscStrapBus = MISC->miscStrapBus;
+
+ if ( miscStrapBus & MISC_STRAP_BUS_LS_SPIM_ENABLED )
+ {
+ spi_flash_busnum = LEG_SPI_BUS_NUM;
+ if ( miscStrapBus & MISC_STRAP_BUS_SPI_CLK_FAST )
+ {
+ spi_flash_clock = 20000000;
+ }
+ else
+ {
+ spi_flash_clock = 781000;
+ }
+ }
+ else
+ {
+ spi_flash_busnum = HS_SPI_BUS_NUM;
+ if ( miscStrapBus & MISC_STRAP_BUS_SPI_CLK_FAST )
+ {
+ spi_flash_clock = 40000000;
+ }
+ else
+ {
+ spi_flash_clock = 20000000;
+ }
+ }
+#endif
+#if defined(_BCM96328_) || defined(CONFIG_BCM96328) || defined(_BCM96362_) || defined(CONFIG_BCM96362)
+ spi_flash_busnum = HS_SPI_BUS_NUM;
+ spi_flash_clock = 40000000;
+#endif
+#if defined(_BCM96368_) || defined(CONFIG_BCM96368)
+ uint32 miscStrapBus = GPIO->StrapBus;
+
+ if ( miscStrapBus & (1 << 6) )
+ spi_flash_clock = 20000000;
+ else
+ spi_flash_clock = 781000;
+#endif
+
+ /* retrieve the maximum read/write transaction length from the SPI controller */
+ spi_max_op_len = BcmSpi_GetMaxRWSize( spi_flash_busnum );
+
+ if (HS_SPI_BUS_NUM == spi_flash_busnum)
+ flash_spi_dev.flash_type = FLASH_IFC_HS_SPI;
+
+ *flash_info = &flash_spi_dev;
+
+#if 0
+ /*
+ * in case of flash corrupt, the following steps can erase the flash
+ * 1. jumper USE_SPI_SLAVE to make SPI in slave mode
+ * 2. start up JTAG debuger and remove the USE_SPI_SLAVE jumper
+ * 3. run the following code to erase the flash
+ */
+ flash_sector_erase_int(0);
+ flash_sector_erase_int(1);
+ printk("flash_init: erase all sectors\n");
+ return FLASH_API_OK;
+#endif
+
+ flash_spi_dev.flash_device_id = device_id = spi_flash_get_device_id(0);
+
+ switch( device_id >> 8 ) {
+ case SSTPART:
+ sectorsize = SECTOR_SIZE_4K;
+ switch ((unsigned char)(device_id & 0x00ff)) {
+ case ID_SST25VF016:
+ numsector = 512;
+ break;
+ case ID_SST25VF032:
+ numsector = 1024;
+ break;
+ case ID_SST25VF064:
+ numsector = 2048;
+ break;
+ }
+ break;
+
+ case SPANPART:
+ sectorsize = SECTOR_SIZE_64K;
+ switch ((unsigned short)(device_id & 0x00ff)) {
+ case ID_SPAN25FL016:
+ numsector = 32;
+ break;
+ case ID_SPAN25FL032:
+ numsector = 64;
+ break;
+ case ID_SPAN25FL064:
+ numsector = 128;
+ break;
+ case ID_SPAN25FL128:
+ numsector = 256;
+ break;
+ }
+ break;
+
+ case EONPART:
+ sectorsize = SECTOR_SIZE_64K;
+ switch ((unsigned short)(device_id & 0x00ff)) {
+ case ID_M25P16:
+ numsector = 32;
+ break;
+ case ID_M25P32:
+ numsector = 64;
+ break;
+ case ID_M25P64:
+ numsector = 128;
+ break;
+ case ID_M25P128:
+ numsector = 256;
+ break;
+ }
+ break;
+
+ case NUMONYXPART:
+ case MACRONIXPART:
+ case WBPART:
+ case AMICPART:
+ sectorsize = SECTOR_SIZE_4K;
+ switch ((unsigned short)(device_id & 0x00ff)) {
+ case ID_M25P16:
+ numsector = 512;
+ break;
+ case ID_M25P32:
+ numsector = 1024;
+ break;
+ case ID_M25P64:
+ numsector = 2048;
+ break;
+ case ID_M25P128:
+ numsector = 4096;
+ break;
+ case ID_M25P256:
+ addr32 = TRUE;
+ numsector = 8192;
+ break;
+ }
+ break;
+
+ default:
+ meminfo.addr = 0L;
+ meminfo.nsect = 1;
+ meminfo.sec[0].size = SECTOR_SIZE_4K;
+ meminfo.sec[0].base = 0x00000;
+ return FLASH_API_ERROR;
+ }
+
+ if ( addr32 ) {
+ /* Enable 4 byte mode */
+ spi_flash_en4b();
+ }
+
+ meminfo.addr = 0L;
+ meminfo.nsect = numsector;
+ for (i = 0; i < numsector; i++) {
+ meminfo.sec[i].size = sectorsize;
+ meminfo.sec[i].base = basecount;
+ basecount += meminfo.sec[i].size;
+ count++;
+ }
+ totalSize = meminfo.sec[count-1].base + meminfo.sec[count-1].size;
+
+ for( fnfi_ptr = fnfi; fnfi_ptr->fnfi_id != 0; fnfi_ptr++ ) {
+ if( fnfi_ptr->fnfi_id == device_id ) {
+ strcpy( flash_spi_dev.flash_device_name, fnfi_ptr->fnfi_name );
+ break;
+ }
+ }
+
+ if ( fastRead )
+ BcmSpi_SetFlashCtrl(FLASH_READ_FAST, 1, 1, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID);
+ else
+ BcmSpi_SetFlashCtrl(FLASH_READ, 1, 0, spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID);
+
+ return (FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* Flash_sector_erase_int() 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 spi_flash_sector_erase_int(unsigned short sector)
+{
+ unsigned char buf[6];
+ unsigned int cmd_length;
+ unsigned int addr;
+
+#ifndef _CFE_
+ down(&spi_flash_lock);
+#endif
+
+ /* set device to write enabled */
+ spi_flash_ub(sector);
+
+ /* erase the sector */
+ addr = (unsigned int) spi_get_flash_memptr(sector);
+
+ cmd_length = 0;
+ if (meminfo.sec[sector].size == SECTOR_SIZE_4K)
+ buf[cmd_length++] = FLASH_SERASE;
+ else
+ buf[cmd_length++] = FLASH_BERASE;
+
+ if ( addr32 )
+ buf[cmd_length++] = (unsigned char)((addr & 0xff000000) >> 24);
+ buf[cmd_length++] = (unsigned char)((addr & 0x00ff0000) >> 16);
+ buf[cmd_length++] = (unsigned char)((addr & 0x0000ff00) >> 8);
+ buf[cmd_length++] = (unsigned char)(addr & 0x000000ff);
+
+ /* check device is ready */
+ if (my_spi_write(buf, cmd_length) == SPI_STATUS_OK) {
+ while (spi_flash_status() != STATUS_READY);
+ }
+
+ spi_flash_reset();
+
+#ifndef _CFE_
+ up(&spi_flash_lock);
+#endif
+
+ return(FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* flash_reset() will reset the flash device to reading array data. */
+/* It is good practice to call this function after autoselect */
+/* sequences had been performed. */
+/*********************************************************************/
+
+static int spi_flash_en4b(void)
+{
+ unsigned char buf[4];
+
+ /* set device to write disabled */
+ buf[0] = FLASH_EN4B;
+ my_spi_write(buf, 1);
+ while (spi_flash_status() != STATUS_READY);
+
+ return(FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* flash_reset() will reset the flash device to reading array data. */
+/* It is good practice to call this function after autoselect */
+/* sequences had been performed. */
+/*********************************************************************/
+
+static int spi_flash_reset(void)
+{
+ unsigned char buf[4];
+
+ /* set device to write disabled */
+ buf[0] = FLASH_WRDI;
+ my_spi_write(buf, 1);
+ while (spi_flash_status() != STATUS_READY);
+
+ return(FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* flash_read_buf() reads buffer of data from the specified */
+/* offset from the sector parameter. */
+/*********************************************************************/
+
+static int spi_flash_read_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int nbytes)
+{
+ unsigned char buf[READ_BUF_LEN_MAX];
+ unsigned int cmd_length;
+ unsigned int addr;
+ int maxread;
+
+#ifndef _CFE_
+ down(&spi_flash_lock);
+#endif
+
+ addr = (unsigned int) spi_get_flash_memptr(sector);
+ addr += offset;
+
+ while (nbytes > 0) {
+ maxread = (nbytes < spi_max_op_len) ? nbytes : spi_max_op_len;
+
+ cmd_length = 0;
+ if ( fastRead )
+ buf[cmd_length++] = FLASH_READ_FAST;
+ else
+ buf[cmd_length++] = FLASH_READ;
+ if ( addr32 )
+ buf[cmd_length++] = (unsigned char)((addr & 0xff000000) >> 24);
+ buf[cmd_length++] = (unsigned char)((addr & 0x00ff0000) >> 16);
+ buf[cmd_length++] = (unsigned char)((addr & 0x0000ff00) >> 8);
+ buf[cmd_length++] = (unsigned char)(addr & 0x000000ff);
+
+ /* Send dummy byte for Fast Read */
+ if ( fastRead )
+ buf[cmd_length++] = (unsigned char)0xff;
+
+ my_spi_read(buf, cmd_length, maxread);
+
+ while (spi_flash_status() != STATUS_READY);
+
+ memcpy(buffer, buf, maxread);
+ buffer += maxread;
+ nbytes -= maxread;
+ addr += maxread;
+ }
+
+#ifndef _CFE_
+ up(&spi_flash_lock);
+#endif
+
+ return (FLASH_API_OK);
+}
+
+/*********************************************************************/
+/* flash_ub() places the flash into unlock bypass mode. This */
+/* is REQUIRED to be called before any of the other unlock bypass */
+/* commands will become valid (most will be ignored without first */
+/* calling this function. */
+/*********************************************************************/
+
+static int spi_flash_ub(unsigned short sector)
+{
+ unsigned char buf[4];
+
+ do {
+ buf[0] = FLASH_RDSR;
+ if (my_spi_read(buf, 1, 1) == SPI_STATUS_OK) {
+ while (spi_flash_status() != STATUS_READY);
+ if (buf[0] & (SR_BP3|SR_BP2|SR_BP1|SR_BP0)) {
+ /* Sector is write protected. Unprotect it */
+ buf[0] = FLASH_WREN;
+ if (my_spi_write(buf, 1) == SPI_STATUS_OK) {
+ buf[0] = FLASH_WRST;
+ buf[1] = 0;
+ if (my_spi_write(buf, 2) == SPI_STATUS_OK)
+ while (spi_flash_status() != STATUS_READY);
+ }
+ }
+ else {
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ } while (1);
+
+ /* set device to write enabled */
+ buf[0] = FLASH_WREN;
+
+ /* check device is ready */
+ if (my_spi_write(buf, 1) == SPI_STATUS_OK) {
+ while (spi_flash_status() != STATUS_READY);
+ do {
+ buf[0] = FLASH_RDSR;
+ if (my_spi_read(buf, 1, 1) == SPI_STATUS_OK) {
+ while (spi_flash_status() != STATUS_READY);
+ if (buf[0] & SR_WEN) {
+ break;
+ }
+ }
+ else {
+ break;
+ }
+ } while (1);
+ }
+
+ 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 spi_flash_write(unsigned short sector, int offset,
+ unsigned char *buffer, int nbytes)
+{
+ unsigned char buf[FLASH_PAGE_256 + 6];
+ unsigned int cmd_length;
+ unsigned int addr;
+ int maxwrite;
+ int pagelimit;
+ int bytes_written = 0;
+
+#ifndef _CFE_
+ down(&spi_flash_lock);
+#endif
+
+ addr = (unsigned int) spi_get_flash_memptr(sector);
+ addr += offset;
+
+ while (nbytes > 0) {
+ spi_flash_ub(sector); /* enable write */
+
+ cmd_length = 0;
+ buf[cmd_length++] = FLASH_PROG;
+ if ( addr32 )
+ buf[cmd_length++] = (unsigned char)((addr & 0xff000000) >> 24);
+ buf[cmd_length++] = (unsigned char)((addr & 0x00ff0000) >> 16);
+ buf[cmd_length++] = (unsigned char)((addr & 0x0000ff00) >> 8);
+ buf[cmd_length++] = (unsigned char)(addr & 0x000000ff);
+
+ /* set length to the smaller of controller limit (spi_max_op_len) or nbytes
+ spi_max_op_len considers both controllers */
+ maxwrite = (nbytes < (spi_max_op_len - cmd_length)) ? nbytes : (spi_max_op_len - cmd_length);
+ /* maxwrite is limit to page boundary */
+ pagelimit = flash_page_size - (addr & (flash_page_size - 1));
+ maxwrite = (maxwrite < pagelimit) ? maxwrite : pagelimit;
+
+ memcpy(&buf[cmd_length], buffer, maxwrite);
+ my_spi_write(buf, maxwrite + cmd_length);
+
+ while (spi_flash_status() != STATUS_READY);
+
+ buffer += maxwrite;
+ nbytes -= maxwrite;
+ addr += maxwrite;
+ bytes_written += maxwrite;
+ }
+
+ spi_flash_reset();
+
+#ifndef _CFE_
+ up(&spi_flash_lock);
+#endif
+
+ return( bytes_written );
+}
+
+/*********************************************************************/
+/* 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 spi_flash_write_buf(unsigned short sector, int offset,
+ unsigned char *buffer, int nbytes)
+{
+ int ret;
+
+ ret = spi_flash_write(sector, offset, buffer, nbytes);
+
+ 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 spi_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 spi_flash_get_sector_size(unsigned short sector)
+{
+ return meminfo.sec[sector].size;
+}
+
+/*********************************************************************/
+/* The purpose of get_flash_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 *spi_get_flash_memptr(unsigned short sector)
+{
+ unsigned char *memptr = (unsigned char*)
+ (FLASH_BASE + meminfo.sec[sector].base);
+
+ return (memptr);
+}
+
+static unsigned char *spi_flash_get_memptr(unsigned short sector)
+{
+ return( spi_get_flash_memptr(sector) );
+}
+
+/*********************************************************************/
+/* Flash_status return an appropriate status code */
+/*********************************************************************/
+
+static int spi_flash_status(void)
+{
+ unsigned char buf[4];
+ int retry = 10;
+
+ /* check device is ready */
+ do {
+ buf[0] = FLASH_RDSR;
+ if (my_spi_read(buf, 1, 1) == SPI_STATUS_OK) {
+ if (!(buf[0] & SR_RDY)) {
+ return STATUS_READY;
+ }
+ } else {
+ return STATUS_ERROR;
+ }
+ OSL_DELAY(10);
+ } while (retry--);
+
+ return STATUS_TIMEOUT;
+}
+
+/*********************************************************************/
+/* flash_get_device_id() return the device id of the component. */
+/*********************************************************************/
+
+static unsigned short spi_flash_get_device_id(unsigned short sector)
+{
+ unsigned char buf[4];
+
+ buf[0] = FLASH_RDID;
+ my_spi_read(buf, 1, 3);
+ while (spi_flash_status() != STATUS_READY);
+ buf[1] = buf[2];
+
+ /* return manufacturer code and device code */
+ return( *(unsigned short *)&buf[0] );
+}
+
+/*********************************************************************/
+/* The purpose of flash_get_blk() is to return a the block number */
+/* for a given memory address. */
+/*********************************************************************/
+
+static int spi_flash_get_blk(int addr)
+{
+ int blk_start, i;
+ int last_blk = spi_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 += spi_flash_get_sector_size(blk_start);
+
+ if( (unsigned int)i > (unsigned int)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 spi_flash_get_total_size(void)
+{
+ return totalSize;
+}
+
+
+#ifndef _CFE_
+static int __init BcmSpiflash_init(void)
+{
+ int flashType;
+
+ /* If serial flash is present then register the device. Otherwise do nothing */
+ flashType = flash_get_flash_type();
+ if ((FLASH_IFC_SPI == flashType) || (FLASH_IFC_HS_SPI == flashType))
+ {
+ /* register the device */
+ BcmSpiReserveSlave(spi_flash_busnum, SPI_FLASH_SLAVE_DEV_ID, spi_flash_clock);
+ bSpiFlashSlaveRes = TRUE;
+ }
+
+ return 0;
+}
+module_init(BcmSpiflash_init);
+
+static void __exit BcmSpiflash_exit(void)
+{
+ bSpiFlashSlaveRes = FALSE;
+}
+module_exit(BcmSpiflash_exit);
+#endif
+