diff options
Diffstat (limited to 'shared/opensource/flash/spiflash.c')
-rwxr-xr-x | shared/opensource/flash/spiflash.c | 969 |
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 + |