summaryrefslogtreecommitdiffstats
path: root/shared/opensource/flash/spiflash.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/opensource/flash/spiflash.c')
-rwxr-xr-xshared/opensource/flash/spiflash.c969
1 files changed, 969 insertions, 0 deletions
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
+