diff options
author | root <root@lamia.panaceas.james.local> | 2015-12-19 13:13:57 +0000 |
---|---|---|
committer | root <root@lamia.panaceas.james.local> | 2015-12-19 14:18:03 +0000 |
commit | 1a2238d1bddc823df06f67312d96ccf9de2893cc (patch) | |
tree | c58a3944d674a667f133ea5a730f5037e57d3d2e /shared/opensource/flash | |
download | bootloader-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-x | shared/opensource/flash/Makefile | 41 | ||||
-rwxr-xr-x | shared/opensource/flash/cfiflash.c | 1112 | ||||
-rwxr-xr-x | shared/opensource/flash/flash_api.c | 350 | ||||
-rwxr-xr-x | shared/opensource/flash/flash_common.c | 280 | ||||
-rwxr-xr-x | shared/opensource/flash/nandflash.c | 1260 | ||||
-rwxr-xr-x | shared/opensource/flash/spiflash.c | 969 |
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 + |