/* vxbSp25SpiFlash.c - Spansion S25XX(and similar) serials SPI Flash Driver */ /* * Copyright (c) 2012-2014 Wind River Systems, Inc. * * The right to copy, distribute, modify or otherwise make use * of this software may be licensed only pursuant to the terms * of an applicable Wind River license agreement. */ /* modification history -------------------- 03jun14,wyt Update manuId, devId and extId in spS25JedecProbe. 01h,04nov13,e_d Fix prevent issue. (WIND00440963) 01g,14Oct13,d_l Change function's argument to FLASH macro. (WIND00438547) eliminate warnings. 01f,11oct13,sye Updated n25q128a with JEDECID_NO_EXT. (WIND00438393) 01e,15aug13,sye Added support for n25q128a device. 01d,01feb13,ylu Fix Null pointer in pDrvCtrl. 01c,24jan13,y_y Added support for some SST chips. 01b,14jan13,y_y fix pWrBuf allocate error in spS25SpiFlashInstInit2(). 01a,14sep12,y_y created. */ /* DESCRIPTION This module implements a vxBus driver for most SPI verdor serial Flash chips. Series supported include Atmel AT25, Spansion S25, SST 25xx, ST M25P, EON EN25 and Winbond W25X, etc. See the device list described by spiFlashList[] for all the supported chips. SUPPORT This driver can support the SPI Flash device which have the following instruction sets: \cs SPI_RDID_9F_CMD (0x9F) /@ Read Identification @/ SPI_READ_CMD (0x03) /@ Read Data Bytes @/ SPI_FAST_READ_CMD (0x0B) /@ Read Data Bytes at Higher Speed @/ SPI_PP_CMD (0x02) /@ Page Program @/ SPI_BE_CMD (0xC7) /@ Chip Erase @/ SPI_SE_CMD (0xD8) /@ Sector(block) Erase @/ \ce Note: 1. If one SPI Flash device has the same command set above, but has not been listed in spiFlashList[], user can just add it with the required information to the array, and then the driver can be used for the device. 2. Not all of the devices listed in spiFlashList[] have been verified. This module could be divided into three parts: a) SPI flash auto probe. This driver can automatically identify the SPI NOR Flash chip by CFI method, JEDEC method, device name method and dynamic probe method. CFI(Common Flash memory Interface) is supported by some SPI NOR Flash devices, and can be used to obtain device information and topology, such as flash pagesize, sectorsize, chipsize, erase block region information, operation voltage and timing, interface type, etc. JEDEC method is supported by most SPI NOR Flash chips, and can be used to obtain Manufacturer, Device and Extend ID, then search one pre-defined table (spiFlashList[]) to retrieve the flash information if has match one. Device name match method is used for the SPI Flash devices which don't support the 2 methods above, and can only make used of the device name to identify the Flash devices. Dynamic probe retrieves key parameters from hwconf.c, user needs to supply the right pagesize, chipsize, sectorsize based on the device datasheet to driver used. However, the weakness of dynamic probe, is ignore the ID information. The below block diagram desribes the process of automatical probe. \cs +---------------+ | Name Match? |----> No (Probe failed) +---------------+ WITH JEDEC ID | YES NO JEDEC ID <-----------------------------------------> | | +------------+ | | CFI Probe |---YES(Probe OK) | +------------+ | | No | | | +------------+ | |JEDEC Probe |---YES(Probe OK) +----------------+ +------------+ | NO JEDEC Probe |---YES(Probe OK) | No +----------------+ |------------------------------------------| No | +---------------+ | Dynamic Probe |---YES(Probe OK) +---------------+ |--No +---------------+ | Probe Failed | +---------------+ \ce b) SPI Flash operation implementation This driver implementations SPI Flash standard operation: spS25spiFlashRead(), spS25spiFlashProgram() and spS25spiFlashSectorErase(), if the totol erase size is equals to chipsize, spS25spiFlashChipErase() will be instead. c) Fill the mtd struct for filesystem use The Flash information and operation routines are composed into one Flash chip information structure, and can be obtained the method - vxbFlashChipInfoGet(), which will be used by MTD layer. EXTERNAL INTERFACE The driver provides the standard vxbus external interface vxbSpS25SpiFlashRegister(). This function registers the driver with the vxbus subsystem, and instances will be created as needed. The driver also provides vxbSpiFlashRead(), vxbSpiFlashWrite(), and vxbSpiFlashErase routine for convenience to access the flash. To add the driver to your vxWorks image, add the following component to the kernel configuration, or define the following macro in config.h. \cs vxprj component add DRV_SPIFLASH_SP25 \ce \cs #define DRV_SPIFLASH_SP25 \ce Then add device to the hwconf.c SPI device table with the name in spiFlashList. For example: \cs struct vxbSpiDevInfo spiDevTbl[] = { /@ Name chipSelect bitWidth, Freq, mode @/ { "spiFlash_sp25probe", 0, 8, FREQ_50_MHZ, SPI_MODE_0}, { "spiFlash_m25p40-nonjedec", 1, 8, FREQ_50_MHZ, SPI_MODE_0}, }; \ce The driver recommends that use "sp25probe" as the device name if you want to use CFI/JEDEC/DYNAMIC method. SEE ALSO: vxBus, vxbSpiLib.c, sysTffs.c */ /* includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vxbSp25SpiFlash.h" #include /* defines */ #undef SPI_FLASH_DBG #ifdef SPI_FLASH_DBG #ifdef LOCAL #undef LOCAL #define LOCAL #endif IMPORT FUNCPTR _func_logMsg; LOCAL int debugLevel = 100; # define PRINTF_DEBUG # ifdef PRINTF_DEBUG # define SPI_FLASH_LOG(lvl,fmt, X1, X2, X3, X4, X5, X6) \ if (debugLevel >= lvl) \ printf(fmt, X1, X2, X3, X4, X5, X6) # else /* PRINTF_DEBUG */ # define SPI_FLASH_LOG(lvl, fmt, X1, X2, X3, X4, X5, X6) \ if (debugLevel >= lvl) \ { \ if (_func_logMsg != NULL) \ _func_logMsg (fmt, (int)X1, (int)X2, (int)X3,\ (int)X4, (int)X5, (int)X6); \ } # endif /* PRINTF_DEBUG */ #else /* SPI_FLASH_DBG */ # undef SPI_FLASH_LOG # define SPI_FLASH_LOG(lvl,fmt,a,b,c,d,e,f) #endif /* SPI_FLASH_DBG */ /* * Instruction set: * Here lists the most important instructions used by all supported SPI flash, * others can be found in vxbSp25FLSpiFlash.h */ /* Read command */ #define SPI_RDID_9F_CMD (0x9F) /* Read Identification */ #define SPI_READ_CMD (0x03) /* Read Data Bytes */ #define SPI_FAST_READ_CMD (0x0B) /* Read Data Bytes at Higher Speed */ /* Write Enable */ #define SPI_WREN_CMD (0x06) /* Write Enable */ #define SPI_WRDI_CMD (0x04) /* Write Disable */ #define SPI_RDSR_CMD (0x05) /* Read Status Register */ #define SPI_WRSR_CMD (0x01) /* Write Status Register */ /* Page Program */ #define SPI_PP_CMD (0x02) /* Page Program */ /* Erase */ #define SPI_BE_CMD (0xC7) /* Chip Erase */ #define SPI_SE_CMD (0xD8) /* Sector(block) Erase */ /* Enter/Exit 4-byte mode */ #define SPI_EN4B_CMD (0xB7) /* Enter 4-byte mode */ #define SPI_EX4B_CMD (0xE9) /* Exit 4-byte mode */ /* locals */ LOCAL BOOL spS25Probe (VXB_DEVICE_ID pDev); LOCAL int s25NameMatch (VXB_DEVICE_ID pDev); LOCAL STATUS enter4BMode (VXB_DEVICE_ID pDev, int enable); LOCAL void cfiShow (struct cfi_ident * cfi, int verbose); LOCAL void spS25spiFlashShow (VXB_DEVICE_ID pDev, int verbose); LOCAL FLASH_CHIP_ID vxbFlashChipInfoGet(VXB_DEVICE_ID pDev, UINT32 chipId); LOCAL int vxbFlashRead (FLASH_CHIP_ID, FLASH_ADDR_T, UINT32, FLASH_SIZE_T, UINT8 **, void *); LOCAL int vxbFlashRead2 (FLASH_CHIP_ID, FLASH_ADDR_T, UINT32, FLASH_SIZE_T, UINT8 **, void *); LOCAL int vxbFlashWrite (FLASH_CHIP_ID, FLASH_ADDR_T, UINT32, FLASH_SIZE_T, UINT8 **, void *); LOCAL int vxbFlashSstWrite (FLASH_CHIP_ID, FLASH_ADDR_T, UINT32, FLASH_SIZE_T, UINT8 **, void *); LOCAL int vxbFlashErase(FLASH_CHIP_ID, FLASH_ADDR_T, UINT32); /* VxBus methods */ LOCAL void spS25SpiFlashInstInit (VXB_DEVICE_ID pDev); LOCAL void spS25SpiFlashInstInit2 (VXB_DEVICE_ID pDev); LOCAL void spS25SpiFlashInstConnect (VXB_DEVICE_ID pDev); LOCAL STATUS spS25SpiFlashInstUnlink (VXB_DEVICE_ID pDev, void * unused); /* externs */ IMPORT void vxbUsDelay (int delayTime); /* Structs */ LOCAL struct drvBusFuncs spS25SpiFlashFuncs = { spS25SpiFlashInstInit, /* devInstanceInit */ spS25SpiFlashInstInit2, /* devInstanceInit2 */ spS25SpiFlashInstConnect /* devConnect */ }; /* Publish the methods for the resources controlled with this file */ LOCAL struct vxbDeviceMethod spS25SpiFlashMethods[] = { DEVMETHOD (vxbFlashChipInfoGet, vxbFlashChipInfoGet), DEVMETHOD (busDevShow, spS25spiFlashShow), DEVMETHOD (vxbDrvUnlink, spS25SpiFlashInstUnlink), { 0, 0 } }; LOCAL VXB_PARAMETERS spiFlashParamDefaults[] = { {"ppTime", VXB_PARAM_INT32, {(void *)DEFAULT_PP_TIME}}, {"chipSize", VXB_PARAM_INT32, {(void *)0}}, {"pageSize", VXB_PARAM_INT32, {(void *)0}}, {"sectorSize", VXB_PARAM_INT32, {(void *)0}}, {NULL, VXB_PARAM_END_OF_LIST, {NULL}} }; LOCAL struct vxbSpiRegister spS25SpiFlashRegister = { { NULL, /* pNext */ VXB_DEVID_DEVICE, /* devID */ VXB_BUSID_SPI, /* busID = SPI */ VXB_VER_4_0_0, /* vxbVersion */ "spiFlash", /* drvName */ &spS25SpiFlashFuncs, /* pDrvBusFuncs */ spS25SpiFlashMethods, /* pMethods */ spS25Probe, /* devProbe */ spiFlashParamDefaults, /* pParamDefaults */ }, }; /* * NOTE: double check command sets and memory organization when you add * more flash chips. */ LOCAL const struct spiFlash_info spiFlashList[] = { /* name, manuId, devId, extId, pageSize, sectorSize, sectorNum, flags */ /* CFI & JEDEC */ { "sp25probe", 0, 0, 0, 0, 0, 0, 0}, /* KM02 use this flash */ { "SM25QU256E", 0x20, 0x7019, 0, 256, 64 * 1024, 512, JEDECID_NO_EXT}, { "GD25LQ256" , 0xc8, 0x6019, 0, 256, 64 * 1024, 512, JEDECID_NO_EXT}, /* SPANSION S25FLXX serials */ { "s25sl004a", 0x01, 0x0212, 0, 256, 64 * 1024, 8, 0}, { "s25sl008a", 0x01, 0x0213, 0, 256, 64 * 1024, 16, 0}, { "s25sl016a", 0x01, 0x0214, 0, 256, 64 * 1024, 32, 0}, { "s25sl032a", 0x01, 0x0215, 0, 256, 64 * 1024, 64, 0}, { "s25sl032p", 0x01, 0x0215, 0x4d00, 256, 64 * 1024, 64, 0}, { "s25sl064a", 0x01, 0x0216, 0, 256, 64 * 1024, 128, 0}, { "s25sl064p", 0x01, 0x0216, 0x4d00, 256, 64 * 1024, 128, 0}, { "s25sl128p0", 0x01, 0x2018, 0x0300, 256, 256 * 1024, 64, 0}, { "s25sl128p1", 0x01, 0x2018, 0x0301, 256, 64 * 1024, 256, 0}, { "s25fl040a", 0x01, 0x0212, 0, 256, 64 * 1024, 8, 0}, { "s25fl129p0", 0x01, 0x2018, 0x4d00, 256, 256 * 1024, 64, 0}, { "s25fl129p1", 0x01, 0x2018, 0x4d01, 256, 64 * 1024, 256, 0}, { "s25fs256s", 0x01, 0x0219, 0x4d01, 256, 64 * 1024, 512, 0}, /* MICRO M25PXXX serials */ { "m25p05", 0x20, 0x2010, 0, 256, 32 * 1024, 2, 0}, { "m25p10", 0x20, 0x2011, 0, 256, 32 * 1024, 4, 0}, { "m25p16", 0x20, 0x2015, 0, 256, 64 * 1024, 32, 0}, { "m25p20", 0x20, 0x2012, 0, 256, 64 * 1024, 4, 0}, { "m25p32", 0x20, 0x2016, 0, 256, 64 * 1024, 64, 0}, { "m25p40", 0x20, 0x2013, 0, 256, 64 * 1024, 8, 0}, { "m25p64", 0x20, 0x2017, 0, 256, 64 * 1024, 128, 0}, { "m25p64", 0x20, 0x2017, 0x1004, 256, 64 * 1024, 128, 0}, { "m25p80", 0x20, 0x2014, 0, 256, 64 * 1024, 16, 0}, { "m25p128", 0x20, 0x2018, 0, 256, 256 * 1024, 64, 0}, { "m25pe10", 0x20, 0x8011, 0, 256, 64 * 1024, 2, 0}, { "m25pe16", 0x20, 0x8015, 0, 256, 64 * 1024, 32, 0}, { "m25pe20", 0x20, 0x8012, 0, 256, 64 * 1024, 4, 0}, { "m25pe40", 0x20, 0x8013, 0, 256, 64 * 1024, 8, 0}, { "m25pe80", 0x20, 0x8014, 0, 256, 64 * 1024, 16, 0}, { "m25px16", 0x20, 0x7115, 0, 256, 64 * 1024, 32, 0}, { "m25px32", 0x20, 0x7116, 0, 256, 64 * 1024, 64, 0}, { "m25px32-s0", 0x20, 0x7316, 0, 256, 64 * 1024, 64, 0}, { "m25px32-s1", 0x20, 0x6316, 0, 256, 64 * 1024, 64, 0}, { "m25px64", 0x20, 0x7117, 0, 256, 64 * 1024, 128, 0}, { "m25px80", 0x20, 0x7114, 0, 256, 64 * 1024, 16, 0}, { "m45pe10", 0x20, 0x4011, 0, 256, 64 * 1024, 2, 0}, { "m45pe16", 0x20, 0x4015, 0, 256, 64 * 1024, 32, 0}, { "m45pe20", 0x20, 0x4012, 0, 256, 64 * 1024, 4, 0}, { "m45pe40", 0x20, 0x4013, 0, 256, 64 * 1024, 8, 0}, { "m45pe80", 0x20, 0x4014, 0, 256, 64 * 1024, 16, 0}, { "n25q00", 0x20, 0xba21, 0x1000, 256, 64 * 1024, 512, 0}, { "n25q32", 0x20, 0xbb16, 0x1000, 256, 64 * 1024, 64, 0}, { "n25q64", 0x20, 0xbb17, 0x1000, 256, 64 * 1024, 128, 0}, { "n25q128", 0x20, 0xba18, 0x1000, 256, 64 * 1024, 256, 0}, { "n25q128a", 0x20, 0xbb18, 0, 256, 64 * 1024, 256, JEDECID_NO_EXT}, { "n25q256", 0x20, 0xba19, 0x1000, 256, 64 * 1024, 512, 0}, { "n25q512", 0x20, 0xbb20, 0x1000, 256, 64 * 1024, 1024, 0}, /* WINBOND W25XXXX serials */ { "w25x05", 0xef, 0x3010, 0, 256, 64 * 1024, 1, 0}, { "w25x10", 0xef, 0x3011, 0, 256, 64 * 1024, 2, 0}, { "w25x20", 0xef, 0x3012, 0, 256, 64 * 1024, 4, 0}, { "w25x40", 0xef, 0x3013, 0, 256, 64 * 1024, 8, 0}, { "w25x80", 0xef, 0x3014, 0, 256, 64 * 1024, 16, 0}, { "w25x16", 0xef, 0x3015, 0, 256, 64 * 1024, 32, 0}, { "w25x32", 0xef, 0x3016, 0, 256, 64 * 1024, 64, 0}, { "w25x64", 0xef, 0x3017, 0, 256, 64 * 1024, 128, 0}, { "w25q32", 0xef, 0x4016, 0, 256, 64 * 1024, 64, 0}, { "w25q64", 0xef, 0x4017, 0, 256, 64 * 1024, 128, 0}, { "w25q128", 0xef, 0x4018, 0, 256, 64 * 1024, 256, 0}, { "w25q256", 0xef, 0x4019, 0, 256, 64 * 1024, 512, 0}, /* SST W25XXXX serials */ { "sst25wf020", 0xbf, 0x2503, 0, 1, 64 * 1024, 4, JEDECID_NO_EXT}, { "sst25wf040", 0xbf, 0x2504, 0, 1, 64 * 1024, 8, JEDECID_NO_EXT}, { "sst25vf016b", 0xbf, 0x2541, 0, 1, 64 * 1024, 32, JEDECID_NO_EXT}, { "sst25vf032b", 0xbf, 0x254a, 0, 1, 64 * 1024, 64, JEDECID_NO_EXT}, { "sst25vf064c", 0xbf, 0x254b, 0, 1, 64 * 1024, 128, JEDECID_NO_EXT}, { "sst25pf020b", 0xbf, 0x258c, 0, 1, 64 * 1024, 4, JEDECID_NO_EXT}, { "sst25pf040b", 0xbf, 0x258d, 0, 1, 64 * 1024, 8, JEDECID_NO_EXT}, { "sst25pf080b", 0xbf, 0x258e, 0, 1, 64 * 1024, 8, JEDECID_NO_EXT}, { "sst26vf016", 0xbf, 0x2601, 0, 256, 64 * 1024, 32, 0}, { "sst26vf032", 0xbf, 0x2602, 0, 256, 64 * 1024, 64, 0}, /* * ATMEL AT25F/AT25FSxxx series * The ATMEL 45 series instructions different with the 25 series sets. * See the drvier vxbAt45SpiFlash.c */ { "at25df021a", 0x1f, 0x4300, 0, 256, 64 * 1024, 4, 0}, { "at25df041a", 0x1f, 0x4401, 0, 256, 64 * 1024, 8, 0}, { "at25df081a", 0x1f, 0x4501, 0x0100, 256, 64 * 1024, 16, 0}, { "at25dl081", 0x1f, 0x4502, 0, 256, 64 * 1024, 16, 0}, { "at25df161", 0x1f, 0x4602, 0, 256, 64 * 1024, 32, 0}, { "at25dq161", 0x1f, 0x8600, 0x0100, 256, 64 * 1024, 32, 0}, { "at25df321", 0x1f, 0x4701, 0, 256, 64 * 1024, 64, 0}, { "at25dq321", 0x1f, 0x8700, 0, 256, 64 * 1024, 64, 0}, { "at25df641", 0x1f, 0x4800, 0, 256, 64 * 1024, 128, 0}, { "at25df641a", 0x1f, 0x4800, 0x0100, 256, 64 * 1024, 128, 0}, { "at25f512b", 0x1f, 0x6500, 0, 256, 32 * 1024, 2, 0}, { "at25fs010", 0x1f, 0x6601, 0, 256, 32 * 1024, 4, 0}, { "at25fs040", 0x1f, 0x6604, 0, 256, 64 * 1024, 8, 0}, /* MXIC MX25LXX serials */ { "mx25l512e", 0xc2, 0x2010, 0, 256, 4 * 1024, 16, 0}, { "mx25l1006e", 0xc2, 0x2011, 0, 256, 64 * 1024, 2, 0}, { "mx25l2006e", 0xc2, 0x2012, 0, 256, 64 * 1024, 4, 0}, { "mx25l4006e", 0xc2, 0x2013, 0, 256, 64 * 1024, 8, 0}, { "mx25l8006e", 0xc2, 0x2014, 0, 256, 64 * 1024, 16, 0}, { "mx25l160", 0xc2, 0x2015, 0, 256, 64 * 1024, 32, 0}, { "mx25l320", 0xc2, 0x2016, 0, 256, 64 * 1024, 64, 0}, { "mx25l640", 0xc2, 0x2017, 0, 256, 64 * 1024, 128, 0}, { "mx25l12805d", 0xc2, 0x2018, 0, 256, 64 * 1024, 256, 0}, { "mx25l12855e", 0xc2, 0x2618, 0, 256, 64 * 1024, 256, 0}, { "mx25l25635e", 0xc2, 0x2019, 0, 256, 64 * 1024, 512, 0}, /* EON EN25LXX serials */ { "en25f05", 0x1c, 0x3110, 0, 256, 32 * 1024, 2, 0}, { "en25f10", 0x1c, 0x3111, 0, 256, 32 * 1024, 4, 0}, { "en25f20", 0x1c, 0x3112, 0, 256, 64 * 1024, 4, 0}, { "en25f32", 0x1c, 0x3116, 0, 256, 64 * 1024, 64, 0}, { "en25p32", 0x1c, 0x2016, 0, 256, 64 * 1024, 64, 0}, { "en25f40", 0x1c, 0x3113, 0, 256, 64 * 1024, 8, 0}, { "en25f80", 0x1c, 0x3114, 0, 256, 64 * 1024, 16, 0}, { "en25q16", 0x1c, 0x3015, 0, 256, 64 * 1024, 32, 0}, { "en25q32", 0x1c, 0x3016, 0, 256, 64 * 1024, 64, 0}, { "en25q64", 0x1c, 0x3017, 0, 256, 64 * 1024, 128, 0}, { "en25q128", 0x1c, 0x3018, 0, 256, 64 * 1024, 256, 0}, { "en25q256", 0x1c, 0x7019, 0, 256, 64 * 1024, 512, 0}, /* NO-ID serials */ { "m25p40-nonjedec", 0, 0, 0, 256, 64 * 1024, 8, NO_JEDEC_ID}, }; #define spiFlashTypeNum NELEMENTS(spiFlashList) /******************************************************************************* * * vxbSpS25SpiFlashRegister - register with the VxBus subsystem * * This routine registers the driver to VxBus Systems. * * RETURNS: N/A * * ERRNO: N/A * * \NOMANUAL */ void vxbSp25SpiFlashRegister (void) { /* Need not check return status */ (void)vxbDevRegister ((struct vxbDevRegInfo *) &spS25SpiFlashRegister); return; } /******************************************************************************* * * spS25SpiFlashInstInit - first level initialization routine of spi flash device * * This routine creates pDrvCtrl structure, saves the index in spiFlashList[] * for the specified Flash device name, and finds the next SPI Flash instance. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void spS25SpiFlashInstInit ( VXB_DEVICE_ID pDev ) { VXB_INST_PARAM_VALUE val; SPI_FLASH_DEV * pDrvCtrl; /* Check for vaild parameter */ VXB_ASSERT_NONNULL_V (pDev); pDrvCtrl = (SPI_FLASH_DEV *) hwMemAlloc (sizeof (SPI_FLASH_DEV)); if (pDrvCtrl == NULL) { return; } /* save instance ID */ pDrvCtrl->pDev = pDev; pDev->pDrvCtrl = pDrvCtrl; /* save the name match result */ if ((pDrvCtrl->index = s25NameMatch (pDev)) == ERROR) { return; } /* * paramDesc { * The ppTime parameter specifies how many microseconds the SPI FLASH * need delay after Page Program cycle. */ if (vxbInstParamByNameGet (pDev, "ppTime", VXB_PARAM_INT32, &val) == ERROR) pDrvCtrl->ppTime = DEFAULT_PP_TIME; else pDrvCtrl->ppTime = val.int32Val; /* find the next instance */ vxbNextUnitGet (pDev); return; } /******************************************************************************* * * vendorGet - transform the manufacture ID to vendor name * * This routine transforms the manufacture ID to the corresponding vendor name. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL char * vendorGet ( UINT8 manId ) { switch (manId) { case SPANSION: return "Spansion"; case FUJITSU: return "Fujitsu"; case EON: return "Eon"; case ATMEL: return "Atmel"; case MICRO: return "MICRO"; case AMIC: return "AMIC"; case ESI: return "ESI"; case INTEL: return "Intel"; case ESMT: return "ESMT"; case TOSHIBA: return "Toshiba"; case PMC: return "PMC"; case HYUNDAI: return "Hyundai"; case SHARP: return "Sharp"; case SST: return "SST"; case MXIC: return "MXIC"; case WINBOND: return "Winbond"; case SAMSUNG: return "Samsung"; case GIGADEVICE: return "GigaDevice"; default: return "Unknown"; } } /******************************************************************************* * * vxbFlashChipInfoGet - get Flash chip information * * This routine returns the Flash chip information for the upper level software. * * RETURNS: the Flash chip information. * * ERRNO: N/A */ LOCAL FLASH_CHIP_ID vxbFlashChipInfoGet ( VXB_DEVICE_ID pDev, UINT32 chipId /* chip index (unused) */ ) { /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, NULL) SPI_FLASH_DEV * pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; return (&pDrvCtrl->mtd); } /******************************************************************************* * * s25NameMatch - SPI Flash name match routine * * This routine compare the device name with the drvier supported devices in * spiFlashList[]. If match, return the location in array, otherwise return * -1. * * RETURNS: the index or -1 * * ERRNO: N/A */ LOCAL int s25NameMatch ( VXB_DEVICE_ID pDev ) { char modelName[MAX_DRV_NAME_LEN]; int drvLen = strlen ("spiFlash_"); int index = 0; int status = FALSE; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) memset (modelName, 0, sizeof (modelName)); memcpy (modelName, (pDev->pName + drvLen), strlen (pDev->pName) - drvLen); while (index < spiFlashTypeNum) { if (strcmp (spiFlashList[index].name, modelName) == 0) { status = TRUE; break; } index++; } if (status == FALSE) { index = -1; } SPI_FLASH_LOG (1000, "index: %d, match: %s\n", index, (_Vx_usr_arg_t) ((index >= 0) ? "yes" : "no"), 3, 4, 5, 6); return index; } /******************************************************************************* * * spS25Probe - vxbus sub probe routine * * This function is vxbus sub-probe routine, it will return the name match * result, which means whether the SPI flash is supported by this drvier. * * RETURNS: TRUE/FALSE * * ERRNO: N/A */ LOCAL BOOL spS25Probe ( VXB_DEVICE_ID pDev ) { /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, FALSE) if (s25NameMatch (pDev) != ERROR) { return TRUE; } else { SPI_FLASH_LOG (0, "spS25Probe failed\n", 1, 2, 3, 4, 5, 6); return FALSE; } } /******************************************************************************* * * spS25GetCFIFromRawData - analyse and get cfi data * * This routine get CFI info from raw data got from the Flash. * * RETURNS: N/A. * * ERRNO: N/A */ LOCAL void spS25GetCFIFromRawData ( UINT8 * ptr, UINT8 len, void * dest ) { UINT16 word1 = 0; UINT32 word2 = 0; switch (len) { case 1: *(UINT8 *)dest = *ptr; break; case 2: word1 = (*ptr) | (*(ptr + 1) << 8); *(UINT16 *)dest = word1; break; case 4: word2 = (*ptr) | (*(ptr + 1) << 8) | (*(ptr + 2) << 16) | (*(ptr + 3) << 24); *(UINT32 *)dest = word2; break; default: break; } } /******************************************************************************* * * spS25CfiProbe - CFI probe routine * * This routine use CFI interface to identifie the Flash, and save the SPI flash * infos to the corresponding fields in pDrvCtrl. * * RETURNS: OK or ERROR if failed. * * ERRNO: N/A */ LOCAL STATUS spS25CfiProbe ( VXB_DEVICE_ID pDev, int verbose ) { UINT8 tmp; UINT16 max; UINT8 id[SPI_CFI_OFFSET + SPI_CFI_LEN]; UINT8 * rawPtr = &id[SPI_CFI_OFFSET]; struct cfi_ident cfi; struct cfi_region region[SPI_CFI_ERASE_REGIONS]; SPI_FLASH_DEV * pDrvCtrl; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; SPI_FLASH_LOG (1000, "spS25CfiProbe(0x08%x) called\n", (_Vx_usr_arg_t)pDev, 2, 3, 4, 5, 6); /* clear id buf */ memset(id, 0, SPI_CFI_OFFSET + SPI_CFI_LEN); /* retrieve ID from the Flash */ JEDEC_ID_GET (pDev, id, sizeof (id)); memcpy ((struct cfi_ident *) &cfi, &id[SPI_CFI_OFFSET], 3); /* try obtaining the params via CFI instead. */ if (cfi.qryStr[0] != 'Q' || cfi.qryStr[1] != 'R' || cfi.qryStr[2] != 'Y') { SPI_FLASH_LOG (100, "No CFI \n", 1, 2, 3, 4, 5, 6); return ERROR; } spS25GetCFIFromRawData (rawPtr + OFF_VCCMIN, 1, (void *)&(cfi.vccMin)); spS25GetCFIFromRawData (rawPtr + OFF_VCCMAX, 1, (void *)&(cfi.vccMax)); spS25GetCFIFromRawData (rawPtr + OFF_VPPMIN, 1, (void *)&(cfi.vppMin)); spS25GetCFIFromRawData (rawPtr + OFF_VPPMAX, 1, (void *)&(cfi.vppMax)); spS25GetCFIFromRawData (rawPtr + OFF_WTO , 1, (void *)&(cfi.wrTimeout)); spS25GetCFIFromRawData (rawPtr + OFF_WBTO , 1, (void *)&(cfi.wrBufTimeout)); spS25GetCFIFromRawData (rawPtr + OFF_BETO , 1, (void *)&(cfi.blkEraseTimeout)); spS25GetCFIFromRawData (rawPtr + OFF_CETO , 1, (void *)&(cfi.chipEraseTimeout)); spS25GetCFIFromRawData (rawPtr + OFF_WTOM , 1, (void *)&(cfi.wrTimeoutMax)); spS25GetCFIFromRawData (rawPtr + OFF_WBTOM , 1, (void *)&(cfi.wrBufTimeoutMax)); spS25GetCFIFromRawData (rawPtr + OFF_BETOM , 1, (void *)&(cfi.blkEraseTimeoutMax)); spS25GetCFIFromRawData (rawPtr + OFF_CETOM , 1, (void *)&(cfi.chipEraseTimeoutMax)); spS25GetCFIFromRawData (rawPtr + OFF_DS , 1, (void *)&(cfi.devSize)); spS25GetCFIFromRawData (rawPtr + OFF_RN , 1, (void *)&(cfi.regionNum)); spS25GetCFIFromRawData (rawPtr + OFF_PCS , 2, (void *)&(cfi.priCmdSet)); spS25GetCFIFromRawData (rawPtr + OFF_PETA, 2, (void *)&(cfi.priExtTblAdrs)); spS25GetCFIFromRawData (rawPtr + OFF_ACS , 2, (void *)&(cfi.altCmdSet)); spS25GetCFIFromRawData (rawPtr + OFF_AETA, 2, (void *)&(cfi.altExtTblAdrs)); spS25GetCFIFromRawData (rawPtr + OFF_ID , 2, (void *)&(cfi.ifDesc)); spS25GetCFIFromRawData (rawPtr + OFF_WBL , 2, (void *)&(cfi.wrBufLen)); spS25GetCFIFromRawData (rawPtr + OFF_ERI0, 4, (void *)&(cfi.EraseRegionInfo[0])); spS25GetCFIFromRawData (rawPtr + OFF_ERI1, 4, (void *)&(cfi.EraseRegionInfo[1])); spS25GetCFIFromRawData (rawPtr + OFF_ERI2, 4, (void *)&(cfi.EraseRegionInfo[2])); spS25GetCFIFromRawData (rawPtr + OFF_ERI3, 4, (void *)&(cfi.EraseRegionInfo[3])); pDrvCtrl->chipSize = 1 << cfi.devSize; PageSize (pDrvCtrl) = 1 << cfi.wrBufLen; cfi.regionNum = min (cfi.regionNum, (UINT8) SPI_CFI_ERASE_REGIONS); cfiShow (&cfi, verbose); for (tmp = 0; tmp < cfi.regionNum; tmp++) { region[tmp].blkSize = (UINT16) (cfi.EraseRegionInfo[tmp] >> 16); } tmp = 0; max = region[0].blkSize; while (tmp < cfi.regionNum) { if (region[tmp].blkSize > max) { max = region[tmp].blkSize; } tmp++; } SectorSize (pDrvCtrl) = CFI_BLK_SIZE (max); SectorNum (pDrvCtrl) = pDrvCtrl->chipSize / SectorSize (pDrvCtrl); strncpy (pDrvCtrl->type, "CFI", MAX_DRV_NAME_LEN); SPI_FLASH_LOG (10, "CFI Probe: found CFI device %dKB, %dKB, %dB, %dRegions\n", pDrvCtrl->chipSize >> 10, SectorSize(pDrvCtrl) >> 10, PageSize(pDrvCtrl), cfi.regionNum, 5 , 6); return OK; } /******************************************************************************* * * spS25NoJedecProbe - probe device without JEDEC ID * * This function saves the information for the SPI flash without jedec ID * to the corresponding fileds in pDrvCtrl. * * RETURNS: OK * * ERRNO: N/A */ LOCAL STATUS spS25NoJedecProbe ( VXB_DEVICE_ID pDev ) { SPI_FLASH_DEV * pDrvCtrl; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; SectorSize (pDrvCtrl) = spiFlashList[pDrvCtrl->index].sectorSize; PageSize (pDrvCtrl) = spiFlashList[pDrvCtrl->index].pageSize; SectorNum (pDrvCtrl) = spiFlashList[pDrvCtrl->index].sectorNum; pDrvCtrl->chipSize = SectorNum (pDrvCtrl) * SectorSize (pDrvCtrl); SPI_FLASH_LOG (100, "spS25NoJedecProbe: identified known device %s (%d KB)\n", spiFlashList[pDrvCtrl->index].name, (pDrvCtrl->chipSize >> 10), 3, 4, 5, 6); return OK; } /******************************************************************************* * * spS25JedecProbe - probe device with JEDEC ID * * This function reads the JEDEC ID from the flash, then compared with the * supported device in spiFlashList[]. If match one, return the pageSize, * sectorSize and chipSize info to the corresponding filed in pDrvCtrl. * Otherwise, return ERROR. * * RETURNS: OK/ERRROR * * ERRNO: N/A */ LOCAL STATUS spS25JedecProbe ( VXB_DEVICE_ID pDev ) { SPI_FLASH_DEV * pDrvCtrl; UINT8 jedecId[5]; UINT16 index; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; SPI_FLASH_LOG (1000, "MyspS25JedecProbe(0x08%x) called\n", (_Vx_usr_arg_t)pDev, 2, 3, 4, 5, 6); memset (jedecId, 0, 5); JEDEC_ID_GET (pDev, jedecId, sizeof (jedecId)); SPI_FLASH_LOG (100, "JEDEC Probeing......Device ID: 0x%02x 0x%04x 0x%04x\r\n", jedecId[0], (jedecId[1] << 8 | jedecId[2]), (jedecId[3] << 8 | jedecId[4]), 4, 5, 6); pDrvCtrl->flash.manuId = jedecId[0]; pDrvCtrl->flash.devId = (jedecId[1] << 8 | jedecId[2]); pDrvCtrl->flash.extId = (jedecId[3] << 8 | jedecId[4]); /* * Spansion JEDEC ID info: * 1. Byte 0 is Manufacturer ID. * 2. Byte 1 and 2 is Device Id. * 3. Byte 3 is Extended Device Information String Length, to indicate * how many Extended Device Information bytes will follow. * 4. Byte 4 indicates uniform 64 KB sector or uniform 256 KB sector device. * 5. Bytes 5 and 6 are reserved (do not use). * 6. For Bytes 07h-0Fh and 3Dh-3Fh, the data will be read as 0xFF. * 7. Bytes 10h-50h are factory programmed per JEDEC standard. */ if ((0 == jedecId[0]) && (0 == (jedecId[1] << 8 | jedecId[2])) && (0 == (jedecId[3] << 8 | jedecId[4]))) { SPI_FLASH_LOG (100, "No match item in JEDECID table \n", 1, 2, 3, 4, 5, 6); return ERROR; } for (index = 1; index < spiFlashTypeNum; index++) { if ((spiFlashList[index].manuId == jedecId[0]) && (spiFlashList[index].devId == (jedecId[1] << 8 | jedecId[2]))) { if (!(spiFlashList[index].flags & JEDECID_NO_EXT)) { if (!(spiFlashList[index].extId == (jedecId[3] << 8 | jedecId[4]))) { continue; } } pDrvCtrl->index = index; strncpy (pDrvCtrl->type, spiFlashList[pDrvCtrl->index].name, MAX_DRV_NAME_LEN - 1); SPI_FLASH_LOG (100, "****** Found %s flash chip [%s] match ******\r\n", vendorGet (spiFlashList[index].manuId), spiFlashList[index].name, 3, 4, 5, 6); break; } else { SPI_FLASH_LOG (100, "probing for %s %s, %d KB, ManId 0x%02x, " "DevId 0x%04x, ExtId 0x%04x\r\n", vendorGet (spiFlashList[index].manuId), spiFlashList[index].name, (spiFlashList[index].sectorNum * spiFlashList[index].sectorSize) >> 10, spiFlashList[index].manuId, spiFlashList[index].devId, spiFlashList[index].extId); } } if (index == spiFlashTypeNum) { SectorSize (pDrvCtrl) = 0; PageSize (pDrvCtrl) = 0; pDrvCtrl->chipSize = 0; SPI_FLASH_LOG (100, "No match item in supported JEDECID table\r\n", 1, 2, 3, 4, 5, 6); return ERROR; } else { SectorSize (pDrvCtrl) = spiFlashList[pDrvCtrl->index].sectorSize; PageSize (pDrvCtrl) = spiFlashList[pDrvCtrl->index].pageSize; SectorNum (pDrvCtrl) = spiFlashList[pDrvCtrl->index].sectorNum; pDrvCtrl->chipSize = SectorNum (pDrvCtrl) * SectorSize (pDrvCtrl); SPI_FLASH_LOG (10, "JedecProbe: identified known JEDEC " "device %s %s (%d KB) \n", vendorGet (spiFlashList[pDrvCtrl->index].manuId), spiFlashList[pDrvCtrl->index].name, (pDrvCtrl->chipSize >> 10), 4, 5, 6); return OK; } } /******************************************************************************* * * spS25DynamicProbe - probe device dynamically * * This routine retrieves SPI Flash parameters from hwConf.c dynamically, usr * can transmit the right chipSize, sectorSize and pageSize based on datasheet * for driver use. * * RETURNS: OK/ERRROR */ LOCAL STATUS spS25DynamicProbe ( VXB_DEVICE_ID pDev ) { SPI_FLASH_DEV * pDrvCtrl; VXB_INST_PARAM_VALUE val; pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; /* * paramDesc { * The chipSize parameter specifies the SPI Flash device totol Chip Size in * Byte. } */ if (OK == vxbInstParamByNameGet (pDev, "chipSize", VXB_PARAM_INT32, &val)) pDrvCtrl->chipSize = val.int32Val; else return ERROR; /* * paramDesc { * The sectorSize parameter specifies the SPI Flash device erase Sector * Size in byte. } */ if (OK == vxbInstParamByNameGet (pDev, "sectorSize", VXB_PARAM_INT32, &val)) SectorSize(pDrvCtrl) = val.int32Val; else return ERROR; /* * paramDesc { * The pageSize parameter specifies the SPI Flash write page size in byte.} */ if (OK == vxbInstParamByNameGet (pDev, "pageSize", VXB_PARAM_INT32, &val)) PageSize(pDrvCtrl) = val.int32Val; else return ERROR; if ((PageSize(pDrvCtrl) == 0) || (SectorSize(pDrvCtrl) == 0) || (pDrvCtrl->chipSize == 0)) return ERROR; SectorNum (pDrvCtrl) = pDrvCtrl->chipSize / SectorSize (pDrvCtrl); return (OK); } /******************************************************************************* * * spS25SpiFlashProbe - spi flash probe * * This routine probe SPI Flash with the CFI, JEDEC, NO-JEDEC and Dynamic methods. * * RETURNS: OK or ERROR if probe failed * * ERRNO: N/A */ LOCAL STATUS spS25SpiFlashProbe ( VXB_DEVICE_ID pDev, int verbose ) { int index = 0xffff; STATUS sts = ERROR; SPI_FLASH_DEV * pDrvCtrl; /* Check for vaild parameter */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; index = s25NameMatch (pDev); if (index == ERROR) return ERROR; /* If match, continue */ do { /* * Firstly, CFI probe, then JEDEC probe, if no device match, probing * failure. Note: auto probe only used the SPI NOR flash has the * JEDEC ID. */ if (!((spiFlashList[index].flags) & (NO_JEDEC_ID))) { /* CFI probe */ sts = spS25CfiProbe (pDev, verbose); SPI_FLASH_LOG (50, "CFI Probe %s \n", (_Vx_usr_arg_t)((sts == ERROR) ? "ERROR" : "OK"), 2, 3, 4, 5, 6); if (sts == OK) break; /* JEDEC probe */ sts = spS25JedecProbe (pDev); SPI_FLASH_LOG (50, "JEDEC Probe %s \n", (_Vx_usr_arg_t)((sts == ERROR) ? "ERROR" : "OK"), 2, 3, 4, 5, 6); } else { /* No Id probe */ pDrvCtrl->index = index; sts = spS25NoJedecProbe (pDev); SPI_FLASH_LOG (50, "%s NoJedecProbe OK \n", spiFlashList[index].name, 2, 3, 4, 5, 6); } if (sts == OK) break; /* Dynamic Probe via device-specific parameters retrieved from hwConf */ sts = spS25DynamicProbe(pDev); SPI_FLASH_LOG (50, "DYNAMIC Probe %s \n", (_Vx_usr_arg_t) ((sts == ERROR) ? "ERROR" : "OK"), 2, 3, 4, 5, 6); } while (FALSE); if (sts == OK) { /* If the chipSize exceeds 128M bits, the addrWidth is 4 */ if (pDrvCtrl->chipSize > SPI_3B_MAX) { pDrvCtrl->addrWidth = 4; enter4BMode (pDev, TRUE); } else pDrvCtrl->addrWidth = 3; } return sts; } /******************************************************************************* * * spS25spiFlashEnable - SPI flash enable routine * * This function can finish some initialize work used by mtd layer, it should be * implemented in driver. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashEnable ( VXB_DEVICE_ID pDev ) { /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) return OK; } /******************************************************************************* * * spS25spiFlashReset - flash reset routine * * This function reset flash if needed used by mtd layer, it should be * implemented in driver. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashReset ( VXB_DEVICE_ID pDev ) { /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) return OK; } /******************************************************************************* * * spS25SpiFlashInstInit2 - second level initialization routine of spi flash * * This function implements the VxBus instInit2 handler for a SPI Flash device * instance. Once we reach this stage of initialization, it's safe for us to * allocate semaphore, so we can create the mutex semaphore. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void spS25SpiFlashInstInit2 ( VXB_DEVICE_ID pDev ) { UINT8 jedecId[5]; FUNCPTR pFunc; int bufSize; SPI_FLASH_DEV * pDrvCtrl; /* Check for vaild parameter */ VXB_ASSERT_NONNULL_V (pDev); pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; /* Mutex semaphore is initialized and necessary at this point */ pDrvCtrl->muteSem = semMCreate (SPIFLASH_MUTEX_OPT); pFunc = vxbDevMethodGet (vxbDevParent(pDev), (VXB_METHOD_ID) vxbSpiSpecialGet_desc); /* Retrieve the SPI master special information */ if (pFunc != NULL) (*pFunc) (vxbDevParent(pDev), &pDrvCtrl->specialInfo); /* * Note: the following code start to read or write the SPI flash, the low * layer SPI controller should work well, otherwise the Systems maybe hang. */ /* Check if the controller done */ memset (jedecId, 0, 5); JEDEC_ID_GET (pDev, jedecId, sizeof (jedecId)); SPI_FLASH_LOG (1000, "JEDEC Probeing......Device ID: 0x%02x 0x%04x 0x%04x\r\n", jedecId[0], (jedecId[1] << 8 | jedecId[2]), (jedecId[3] << 8 | jedecId[4]), 4, 5, 6); pDrvCtrl->flash.manuId = jedecId[0]; pDrvCtrl->flash.devId = (jedecId[1] << 8 | jedecId[2]); pDrvCtrl->flash.extId = (jedecId[3] << 8 | jedecId[4]); if (spS25SpiFlashProbe (pDev, 0) == ERROR) { SPI_FLASH_LOG (0, "spS25SpiFlashProbe ERROR!\n", 1, 2, 3, 4, 5, 6); pDrvCtrl->isProbeOk = FALSE; return; } else pDrvCtrl->isProbeOk = TRUE; bufSize = MAX_CMD_SIZE + PageSize(pDrvCtrl); pDrvCtrl->pWrBuf = (UINT8 *)malloc (bufSize); if (pDrvCtrl->pWrBuf == NULL) { SPI_FLASH_LOG (0, "Malloc pWrBuf failed!\r\n", 1, 2, 3, 4, 5, 6); return; } memset(pDrvCtrl->pWrBuf, 0, sizeof(bufSize)); /* Setup Flash information for FS used */ pDrvCtrl->mtd.pDev = (void *) pDev; strncpy ((char *)pDrvCtrl->mtd.chipName, pDrvCtrl->type, MAX_DRV_NAME_LEN); pDrvCtrl->mtd.uChipSize = pDrvCtrl->chipSize; pDrvCtrl->mtd.uEraseSize = SectorSize (pDrvCtrl); pDrvCtrl->mtd.uFlashType = FLASH_CHIP_TYPE_SPI; pDrvCtrl->mtd.uCapability = FLASH_CHIP_CAP_RD | FLASH_CHIP_CAP_WR | FLASH_CHIP_CAP_BLKERA; pDrvCtrl->mtd.flashOPs.ena = (void *) spS25spiFlashEnable; pDrvCtrl->mtd.flashOPs.rst = (void *) spS25spiFlashReset; if ((pDrvCtrl->specialInfo != NULL) && (pDrvCtrl->specialInfo->flag & SPI_TRANS_LIMIT)) pDrvCtrl->mtd.flashOPs.read = (void *) vxbFlashRead2; else pDrvCtrl->mtd.flashOPs.read = (void *) vxbFlashRead; pDrvCtrl->mtd.flashOPs.write = (void *) vxbFlashWrite; if (pDrvCtrl->flash.manuId == SST) pDrvCtrl->mtd.flashOPs.write = (void *) vxbFlashSstWrite; pDrvCtrl->mtd.flashOPs.blkErase = (void *) vxbFlashErase; return; } /******************************************************************************* * * spS25SpiFlashInstUnlink - VxBus unlink handler * * This function shuts down a SPI Flash device instance in response to an * unlink event from VxBus. This may occur if our VxBus instance has been * terminated, or if the driver has been unloaded. * * RETURNS: OK always. * * ERRNO: N/A */ LOCAL STATUS spS25SpiFlashInstUnlink ( VXB_DEVICE_ID pDev, void * unused ) { SPI_FLASH_DEV * pDrvCtrl; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; /* Destroy the adapter context. */ if (pDrvCtrl->muteSem != NULL) (void)semDelete (pDrvCtrl->muteSem); #ifndef _VXBUS_BASIC_HWMEMLIB hwMemFree ((char *) pDrvCtrl); #endif /* _VXBUS_BASIC_HWMEMLIB */ pDev->pDrvCtrl = NULL; /* Goodbye cruel world. */ return (OK); } /******************************************************************************* * * spS25spiFlashWrite - VxBus SPI device write by name support routine * * This routine firstly finds the VXB_DEVICE_ID for a given instance * identified by name and unit number, then call vxbI2cDevWrite() routine to * write the device. * * RETURNS: OK/ERROR * * ERRNO : N/A */ LOCAL STATUS spS25spiFlashWrite ( VXB_DEVICE_ID pDev, UINT8 * txBuf, UINT32 txLen, UINT32 usDelay ) { /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) SPI_TRANSFER transInfo; /* check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) memset(&transInfo, 0, sizeof(SPI_TRANSFER)); transInfo.txBuf = txBuf; transInfo.txLen = txLen; transInfo.usDelay = usDelay; return (vxbSpiTransfer (pDev, &transInfo)); } /******************************************************************************* * * spS25spiFlashRegRead - read register routine * * This is the SPI flash status /config register read out routine. * * RETURNS: status register value. * * ERRNO: N/A */ LOCAL INT32 spS25spiFlashRegRead ( VXB_DEVICE_ID pDev, UINT8 cmd ) { UINT8 buffer; SPI_TRANSFER transInfo; /* check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) memset(&transInfo, 0, sizeof(SPI_TRANSFER)); transInfo.txBuf = &cmd; transInfo.txLen = sizeof (cmd); transInfo.rxBuf = &buffer; transInfo.rxLen = sizeof (buffer); if (vxbSpiTransfer (pDev, &transInfo) != OK) return ERROR; else return (buffer); } /******************************************************************************* * * spS25spiFlashWaitReady - wait the status ready * * This routine reads the status register, according to the Write-In-Process bit * to judge the transfer finished or not. If the status still didn't finish in * timeout, return ERROR. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashWaitReady ( VXB_DEVICE_ID pDev, UINT32 timeout ) { /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) while (timeout--) { if ((spS25spiFlashRegRead (pDev, SPI_RDSR_CMD) & SR_WIP) == 0) { break; } vxbUsDelay(1); } /* Timed out */ if (timeout == 0) { return ERROR; } return OK; } /******************************************************************************* * * spS25flxxspS25spiFlashWriteEnable - write enable * * The Write Enable (WREN) sets the Write Enable Latch (WEL) bit to a 1, which * enables the device to accept a Write Status Register, program, or erase * command. The WEL bit must be set prior to every Page Program (PP), * Quad Page Program (QPP), Parameter Sector Erase (P4E, P8E), Erase(SE or BE), * Write Registers (WRR) and OTP Program (OTPP) command * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashWriteEnable ( VXB_DEVICE_ID pDev, UINT8 cmd ) { /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) /* Send the WREN command */ if (spS25spiFlashWrite (pDev, &cmd, sizeof (cmd), 0) != OK) { return ERROR; } if (spS25spiFlashWaitReady (pDev, TIMEOUT) != OK) { return ERROR; } return OK; } /******************************************************************************* * * spS25spiFlashRegWrite - write register * * This is the SPI flash status register write routine. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashRegWrite ( VXB_DEVICE_ID pDev, UINT8 status ) { UINT8 cmd[2]; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) memset(cmd, 0, 2); /* Write enable is required */ if (spS25spiFlashWriteEnable (pDev, SPI_WREN_CMD) != OK) { return ERROR; } cmd[0] = SPI_WRSR_CMD; cmd[1] = status; if (spS25spiFlashWrite (pDev, cmd, sizeof (cmd), 0) != OK) { return ERROR; } if (spS25spiFlashWaitReady (pDev, TIMEOUT) != OK) { return ERROR; } return OK; } /******************************************************************************* * * spS25SpiFlashInstConnect - third level initialization routine of spi flash * * This function implements the VxBus instConnect handler for a SPI Flash * device instance. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void spS25SpiFlashInstConnect ( VXB_DEVICE_ID pDev ) { SPI_FLASH_DEV * pDrvCtrl; /* Check for vaild parameter */ VXB_ASSERT_NONNULL_V (pDev); pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; switch (pDrvCtrl->flash.manuId) { /* * For SST chips, the default value of the SR is 0x1c, which means the * block protect enabled. So clear it before any operation. */ case SST: spS25spiFlashRegWrite (pDev, 0x0); break; default: break; } return; } /******************************************************************************* * * enter4BMode - eEnable/disable 4-byte addressing mode * * The EN4B instruction enables accessing the address length of 32-bit for the * memory area of higher density (larger than 128Mb). The device default is in * 24-bit address mode; after sending out the EN4B instruction, the bit2 * (4BTYE bit) of security register will be automatically set to "1" to indicate * the 4-byte address mode has been enabled. Once the 4-byte address mode is * enabled, the address length becomes 32-bit instead of the default 24-bit. * There are three methods to exit the 4-byte mode: writing exit 4-byte mode * (EX4B) instruction, Hardware Reset or power-off. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS enter4BMode ( VXB_DEVICE_ID pDev, int enable ) { SPI_FLASH_DEV * pDrvCtrl; UINT8 cmd[MAX_CMD_SIZE]; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; /* Clear the cmd buffer */ memset (cmd, 0, MAX_CMD_SIZE); switch (pDrvCtrl->flash.manuId) { /* sending EN4B instruction to enter 4-byte mode */ case MICRO: /* Write enable */ if (spS25spiFlashWriteEnable (pDev, SPI_WREN_CMD) == OK) break; else return ERROR; case GIGADEVICE: case MXIC: case EON: case WINBOND: break; case SPANSION: if(pDrvCtrl->flash.devId != 0x0219) { cmd[0] = SPI_WBNK_CMD; /* Bank register write */ cmd[1] = enable << 7; return spS25spiFlashWrite (pDev, cmd, 2, 0); } break; default: return ERROR; } cmd[0] = enable ? SPI_EN4B_CMD : SPI_EX4B_CMD; return (spS25spiFlashWrite (pDev, cmd, 1, 0)); } /******************************************************************************* * * spS25spiFlashChipErase - chip erase * * This is the chip erase routine. The Bulk Erase (BE) command sets * all the bits within the entire memory array to logic 1. A WREN * command is required prior to writing the BE command. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashChipErase ( VXB_DEVICE_ID pDev ) { UINT8 status = 0; UINT32 count = 0; UINT8 cmd = SPI_BE_CMD; UINT32 timeout = 0x1000; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) /* Check whether the status is right */ while (timeout--) { if ((spS25spiFlashRegRead (pDev, SPI_RDSR_CMD) & SR_SRWD) == 0) { break; } else { /* * try to recover the status if needed. Here 0x0 for Spansion * SPI flash, please note the value differ from device to device. */ spS25spiFlashRegWrite (pDev, 0x0); SPI_FLASH_LOG (10, "Flash status(0x%x) wrong, try to recover..\r\n", spS25spiFlashRegRead (pDev, SPI_RDSR_CMD), 2, 3, 4, 5, 6); } }; /* Write enable */ if (spS25spiFlashWriteEnable (pDev, SPI_WREN_CMD) != OK) { return ERROR; } if (spS25spiFlashWrite (pDev, &cmd, sizeof (cmd), 0) != OK) { return ERROR; } do { /* Read the status register */ status = spS25spiFlashRegRead (pDev, SPI_RDSR_CMD); SPI_FLASH_LOG (10, "BulkErase: %d s, Status: 0x%x(%s) \r", count, status, (_Vx_usr_arg_t) ((status & SR_WIP)?"Busy" : "Idle"), 0, 0, 0); /* Delay 1 second */ taskDelay (sysClkRateGet ()); /* Write in progress */ if ((status & SR_WIP) == 0) { break; } count++; } while (count < (TIMEOUT / 10000)); /* Timeout */ if (count >= (TIMEOUT / 10000)) { return ERROR; } return OK; } /******************************************************************************* * * spS25spiFlashSectorErase - sector erase * * This is the sector erase routine. The Sector Erase (SE) command sets * all bits at all addresses within a specified sector to a logic 1. * A WREN command is required prior to writing the SE command. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashSectorErase ( VXB_DEVICE_ID pDev, UINT32 startAddr, UINT32 numOfErasableBlocks ) { SPI_FLASH_DEV * pDrvCtrl; UINT8 cmd[MAX_CMD_SIZE]; UINT32 offset = 0; UINT32 timeout = 0x100; UINT32 i = 0; STATUS sts = OK; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; SPI_FLASH_LOG (500, "Erase %d unit from %d unit\r\n", numOfErasableBlocks, startAddr/SectorSize (pDrvCtrl), 3, 4, 5, 6); /* Sanity checks */ if (startAddr + numOfErasableBlocks * SectorSize (pDrvCtrl) > ChipSize(pDrvCtrl)) { SPI_FLASH_LOG (0, "Erase exceed range. \r\n", 1, 2, 3, 4, 5, 6); return ERROR; } if (numOfErasableBlocks == 0) return ERROR; /* Mutex semaphore */ if (SPIFLASH_LOCK(pDrvCtrl->muteSem) != OK) return (ERROR); /* Whole chip erase */ if ((startAddr == 0) && (numOfErasableBlocks == SectorNum (pDrvCtrl))) { if (spS25spiFlashChipErase (pDev) != OK) { SPI_FLASH_LOG (10, "ChipErase failed, try to sector erase\r\n", 1, 2, 3, 4, 5, 6); } else { /* Release semaphore */ if (SPIFLASH_UNLOCK (pDrvCtrl->muteSem) != OK) return ERROR; return OK; } } /* Clear the cmd buffer */ memset (cmd, 0, MAX_CMD_SIZE); /* Check whether the status is right */ for (i = 0; i < numOfErasableBlocks; i++) { while (timeout--) { if ((spS25spiFlashRegRead (pDev, SPI_RDSR_CMD) & SR_SRWD) == 0) { break; } else { /* * Try to recover the status if needed. Here 0x0 for Spansion * SPI flash, note the value differ from device to device. */ spS25spiFlashRegWrite (pDev, 0); SPI_FLASH_LOG (10, "Flash status(0x%x) wrong, try to recover\r\n", spS25spiFlashRegRead (pDev, SPI_RDSR_CMD), 2, 3, 4, 5, 6); } }; /* set erase command */ cmd[0] = SPI_SE_CMD; cmd[1] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 8)); cmd[2] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 16)); cmd[3] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 24)); if (pDrvCtrl->addrWidth == 4) { cmd[4] = (UINT8) (offset); } /* Write enable */ if (spS25spiFlashWriteEnable (pDev, SPI_WREN_CMD) != OK) { sts = ERROR; break; } /* Send the SE command */ if (spS25spiFlashWrite (pDev, cmd, (pDrvCtrl->addrWidth + 1), 0) != OK) { sts = ERROR; break; } /* wait until the device is not busy */ if (spS25spiFlashWaitReady (pDev, TIMEOUT) != OK) { sts = ERROR; break; } SPI_FLASH_LOG (100, "Formatted %d of %d sectors = %d.%01d %%\r", (i + 1), numOfErasableBlocks, 100 * (i + 1) / numOfErasableBlocks, 1000 * (i + 1) / numOfErasableBlocks % 10, 5, 6); startAddr += SectorSize (pDrvCtrl); } /* Release semaphore */ if (SPIFLASH_UNLOCK (pDrvCtrl->muteSem) != OK) { sts = ERROR; } return sts; } /******************************************************************************* * * spS25spiFlashProgram - SPI flash program * * This is the SPI flash page program routine. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashProgram ( VXB_DEVICE_ID pDev, UINT32 startAddr, UINT32 len, UINT8 * buf ) { SPI_FLASH_DEV * pDrvCtrl; UINT32 dataLen = 0; UINT32 offset = 0; STATUS rc = ERROR; UINT8 * pWrBuf; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; /* Make sure that we do not exceed address range */ if ((startAddr >= pDrvCtrl->chipSize) || (len == 0) || ((startAddr + len) > pDrvCtrl->chipSize)) return rc; /* Mutex semaphore */ if (SPIFLASH_LOCK(pDrvCtrl->muteSem) != OK) { return (ERROR); } do { pWrBuf = pDrvCtrl->pWrBuf; /* Data must be written in "page Size" chunks */ while (offset < len) { /* Compute the number of data ready to write for one write cycle */ dataLen = min (len - offset, PageSize(pDrvCtrl) - ((startAddr + offset) % PageSize(pDrvCtrl))); /* Set page program command */ pWrBuf[0] = SPI_PP_CMD; pWrBuf[1] = (UINT8)((offset + startAddr) >> (pDrvCtrl->addrWidth * 8 - 8)); pWrBuf[2] = (UINT8)((offset + startAddr) >> (pDrvCtrl->addrWidth * 8 - 16)); pWrBuf[3] = (UINT8)((offset + startAddr) >> (pDrvCtrl->addrWidth * 8 - 24)); if (pDrvCtrl->addrWidth == 4) pWrBuf[4] = (UINT8)(offset + startAddr); memcpy ((void*)(pWrBuf + (pDrvCtrl->addrWidth + 1)), (void*)(buf + offset), dataLen); if (spS25spiFlashWriteEnable (pDev, SPI_WREN_CMD) != OK) break; /* Program one page */ if (spS25spiFlashWrite (pDev, pWrBuf, (dataLen + pDrvCtrl->addrWidth + 1), pDrvCtrl->ppTime) != OK) break; if (spS25spiFlashWaitReady (pDev, TIMEOUT) != OK) break; memset ((void *)pWrBuf, 0, sizeof(PageSize(pDrvCtrl)) + pDrvCtrl->addrWidth); offset += dataLen; } rc = OK; }while(FALSE); /* Release resources */ if (SPIFLASH_UNLOCK (pDrvCtrl->muteSem) != OK) { rc = ERROR; } return rc; } /******************************************************************************* * * write1Byte - Write one byte to SPI flash * * This routine writes one byte data to SPI flash. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS write1Byte ( VXB_DEVICE_ID pDev, UINT32 addr, UINT8 data ) { UINT8 cmd[MAX_CMD_SIZE]; memset(cmd, 0, sizeof(cmd)); cmd[0] = SPI_PP_CMD; cmd[1] = addr >> 16; cmd[2] = addr >> 8; cmd[3] = addr; cmd[4] = data; if (spS25spiFlashWriteEnable (pDev, SPI_WREN_CMD) != OK) return ERROR; /* Program one byte */ if (spS25spiFlashWrite (pDev, cmd, MAX_CMD_SIZE, 0) != OK) return ERROR; if (spS25spiFlashWaitReady (pDev, TIMEOUT) != OK) return ERROR; return OK; } /******************************************************************************* * * sst25AAIProgram - SST SPI flash auto address increment word-program * * This is the SST SPI flash auto address increment(AAI) word-program routine. * The AAI program instruction allows multiple bytes of data to be programmed * without re-issuing the next sequential address location. Note: The first byte * of data (D0) is programmed into the initial address [A23-A1] with A0=0, the * second byte of Data (D1) is programmed into the initial address [A23-A1] * with A0=1. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS sst25AAIProgram ( VXB_DEVICE_ID pDev, UINT32 startAddr, UINT32 len, UINT8 * buf ) { SPI_FLASH_DEV * pDrvCtrl; UINT32 offset = 0; STATUS rc = ERROR; UINT8 cmd[6]; UINT32 actual = startAddr % 2; UINT32 i =0; /* Check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; /* Make sure that we do not exceed address range */ if ((startAddr >= pDrvCtrl->chipSize) || (len == 0) || ((startAddr + len) > pDrvCtrl->chipSize)) return rc; /* Mutex semaphore */ if (SPIFLASH_LOCK(pDrvCtrl->muteSem) != OK) return rc; do { if ((len == 1) || (len == 2)) { for (i = 0; i < len; i++) { if (write1Byte (pDev, (startAddr + i), buf[i]) != OK) break; } rc = OK; break; } /* Start write from odd address. */ if (actual) { if (write1Byte (pDev, startAddr, buf[0]) != OK) break; } /* Write the remaining bytes using auto address increment mode */ cmd[0] = SST_AAI_WORD_CMD; cmd[1] = (startAddr + actual) >> 16; cmd[2] = (startAddr + actual) >> 8; cmd[3] = (startAddr + actual); cmd[4] = buf[actual]; cmd[5] = buf[actual + 1]; if (spS25spiFlashWriteEnable (pDev, SPI_WREN_CMD) != OK) break; if (spS25spiFlashWrite (pDev, cmd, sizeof(cmd), 2) != OK) break; if (spS25spiFlashWaitReady (pDev, TIMEOUT) != OK) break; offset = (2 + actual); for (i = 0; i < (len - 2 - actual) / 2; i++, offset += 2) { cmd[1] = buf[offset]; cmd[2] = buf[offset + 1]; if (spS25spiFlashWrite (pDev, cmd, 3, 2) != OK) break; if (spS25spiFlashWaitReady (pDev, TIMEOUT) != OK) break; } /* Quit AAI */ if (spS25spiFlashWriteEnable (pDev, SPI_WRDI_CMD) != OK) break; if (offset != len) { SPI_FLASH_LOG (10000, "Write the last data %d @ addr %d\r\n", buf[len - 1], (offset + startAddr), 3, 4, 5, 6); if (write1Byte (pDev, (offset + startAddr), buf[len - 1]) != OK) break; } rc = OK; }while(FALSE); SPI_FLASH_LOG (100000, "Write Done! Status = 0x%x \r\n", spS25spiFlashRegRead (pDev, SPI_RDSR_CMD), 2, 3, 4, 5, 6); /* Release resources */ if (SPIFLASH_UNLOCK (pDrvCtrl->muteSem) != OK) { rc = ERROR; } return rc; } /******************************************************************************* * * spS25spiFlashRead - read SPI flash data * * This is the SPI flash data read out routine. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashRead ( VXB_DEVICE_ID pDev, UINT32 startAddr, UINT32 dataLen, UINT8 * dest ) { SPI_FLASH_DEV * pDrvCtrl; UINT8 cmd[MAX_CMD_SIZE]; SPI_TRANSFER transInfo; /* check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; if ((startAddr >= pDrvCtrl->chipSize) || (dataLen == 0) || ((startAddr + dataLen) > pDrvCtrl->chipSize)) return ERROR; memset (cmd, 0, MAX_CMD_SIZE); memset (&transInfo, 0, sizeof(SPI_TRANSFER)); /* Set read command */ cmd[0] = SPI_READ_OPCODE; cmd[1] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 8)); cmd[2] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 16)); cmd[3] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 24)); if (pDrvCtrl->addrWidth == 4) cmd[4] = (UINT8)startAddr; /* Mutex semaphore */ if (SPIFLASH_LOCK(pDrvCtrl->muteSem) != OK) { return (ERROR); } transInfo.txBuf = cmd; transInfo.txLen = READ_CMD_BYTE; transInfo.rxBuf = dest; transInfo.rxLen = dataLen; SPI_FLASH_LOG (10000, "READ len[0x%x] from 0x%x to buf 0x%x, " "READ cmd: 0x%x cmdLen %d\r\n", transInfo.rxLen, startAddr, transInfo.rxBuf, *(UINT32*)transInfo.txBuf, transInfo.txLen, 6); if (vxbSpiTransfer (pDev, &transInfo) != OK) { (void)SPIFLASH_UNLOCK (pDrvCtrl->muteSem); return ERROR; } /* Release resources */ if (SPIFLASH_UNLOCK (pDrvCtrl->muteSem) != OK) return ERROR; return OK; } /******************************************************************************* * * spS25spiFlashRead2 - read SPI flash data by window size * * Some controller has transaction length limitation such as the Freescale's * eSPI controller can only trasmit 0xFFFF bytes one time, so we have to read * (0xFFFF) by "window" if the len is more than the limitation. * * RETURNS: OK or ERROR if there is an error. * * ERRNO: N/A */ LOCAL STATUS spS25spiFlashRead2 ( VXB_DEVICE_ID pDev, UINT32 startAddr, UINT32 dataLen, UINT8 * dest ) { SPI_FLASH_DEV * pDrvCtrl; UINT8 cmd[MAX_CMD_SIZE]; SPI_TRANSFER transInfo; UINT32 loop =0; UINT32 count = 0; UINT32 bytesToRead = 0; UINT32 max_transLen = 0; /* check if the pDev pointer is valid */ VXB_ASSERT (pDev != NULL, ERROR) pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; if ((startAddr >= pDrvCtrl->chipSize) || (dataLen == 0) || ((startAddr + dataLen) > pDrvCtrl->chipSize)) return ERROR; memset (cmd, 0, MAX_CMD_SIZE); memset (&transInfo, 0, sizeof(SPI_TRANSFER)); max_transLen = pDrvCtrl->specialInfo->windowSize; loop = (dataLen + READ_CMD_BYTE + startAddr) / max_transLen + ((dataLen + READ_CMD_BYTE + startAddr) % max_transLen ? 1 : 0); /* Mutex semaphore */ if (SPIFLASH_LOCK(pDrvCtrl->muteSem) != OK) { return (ERROR); } do { /* Set read command */ cmd[0] = SPI_READ_OPCODE; cmd[1] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 8)); cmd[2] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 16)); cmd[3] = (UINT8) (startAddr >> (pDrvCtrl->addrWidth * 8 - 24)); if (pDrvCtrl->addrWidth == 4) cmd[4] = (UINT8)startAddr; /* Caculate transaction length in a frame */ bytesToRead = min (dataLen, max_transLen - READ_CMD_BYTE); transInfo.txBuf = cmd; transInfo.txLen = READ_CMD_BYTE; transInfo.rxBuf = dest; transInfo.rxLen = bytesToRead; SPI_FLASH_LOG (10000, "%d times READ len[0x%x] from 0x%x to buf 0x%x, " "READ cmd: 0x%x cmdLen %d\r\n", count, transInfo.rxLen, startAddr, transInfo.rxBuf, *(UINT32*)transInfo.txBuf, transInfo.txLen); if (vxbSpiTransfer (pDev, &transInfo) != OK) { (void)SPIFLASH_UNLOCK (pDrvCtrl->muteSem); return ERROR; } startAddr = startAddr + bytesToRead; dest += bytesToRead; dataLen -= bytesToRead; count++; } while(count < loop); /* Release resources */ if (SPIFLASH_UNLOCK (pDrvCtrl->muteSem) != OK) return ERROR; return OK; } /******************************************************************************* * * vxbFlashRead - read data from Flash chip * * This routine reads the data from the Flash chip used by upper(MTD) layer. * * RETURNS: OK or ERROR if NULL buffer pointer, or out of Flash chip. * * ERRNO: N/A */ LOCAL int vxbFlashRead ( FLASH_CHIP_ID pFlashChip, FLASH_ADDR_T uAddr, /* offset to Flash */ UINT32 uBufCount, /* buffer count */ FLASH_SIZE_T uBufLen, /* length for every buffer */ UINT8 ** ppBuf, /* pointer to buffer array */ void * op /* opinion */ ) { /* check if the pDev pointer is valid */ VXB_ASSERT (pFlashChip->pDev != NULL, ERROR); if ((ppBuf == NULL) || (ppBuf[0] == NULL)) { SPI_FLASH_LOG (0, "NULL buffer pointer %d\n", 0, 0, 0, 0, 0, 0); return (ERROR); } return (spS25spiFlashRead (pFlashChip->pDev, (UINT32)uAddr, (UINT32)uBufLen, ppBuf[0])); } /******************************************************************************* * * vxbFlashRead2 - read data from Flash chip * * This routine reads the data from the Flash chip used by upper(MTD) layer. * * RETURNS: OK or ERROR if NULL buffer pointer, or out of Flash chip. * * ERRNO: N/A */ LOCAL int vxbFlashRead2 ( FLASH_CHIP_ID pFlashChip, FLASH_ADDR_T uAddr, /* offset to Flash */ UINT32 uBufCount, /* buffer count */ FLASH_SIZE_T uBufLen, /* length for every buffer */ UINT8 ** ppBuf, /* pointer to buffer array */ void * op /* opinion */ ) { /* check if the pDev pointer is valid */ VXB_ASSERT (pFlashChip->pDev != NULL, ERROR); if ((ppBuf == NULL) || (ppBuf[0] == NULL)) { SPI_FLASH_LOG (0, "NULL buffer pointer %d\n", 0, 0, 0, 0, 0, 0); return (ERROR); } return (spS25spiFlashRead2 (pFlashChip->pDev, (UINT32)uAddr, (UINT32)uBufLen, ppBuf[0])); } /******************************************************************************* * * vxbFlashWrite - write data to Flash chip * * This routine writes the data from the Flash chip used by upper(MTD) layer. * * RETURNS: OK or ERROR. * * ERRNO: N/A */ LOCAL int vxbFlashWrite ( FLASH_CHIP_ID pFlashChip, FLASH_ADDR_T uAddr, /* offset to Flash */ UINT32 uBufCount, /* buffer count */ FLASH_SIZE_T uBufLen, /* length for every buffer */ UINT8 ** ppBuf, /* pointer to buffer array */ void * op /* opinion */ ) { /* check if the pDev pointer is valid */ VXB_ASSERT (pFlashChip->pDev != NULL, ERROR); if ((ppBuf == NULL) || (ppBuf[0] == NULL)) { SPI_FLASH_LOG (0, "NULL buffer pointer %d\n", 0, 0, 0, 0, 0, 0); return (ERROR); } return (spS25spiFlashProgram (pFlashChip->pDev, (UINT32)uAddr, (UINT32)uBufLen, ppBuf[0])); } /******************************************************************************* * * vxbFlashSstWrite - write data to SST Flash chip * * This routine writes the data from the SST Flash chip used by upper(MTD) layer. * * RETURNS: OK or ERROR. * * ERRNO: N/A */ LOCAL int vxbFlashSstWrite ( FLASH_CHIP_ID pFlashChip, FLASH_ADDR_T uAddr, /* offset to Flash */ UINT32 uBufCount, /* buffer count */ FLASH_SIZE_T uBufLen, /* length for every buffer */ UINT8 ** ppBuf, /* pointer to buffer array */ void * op /* opinion */ ) { /* check if the pDev pointer is valid */ VXB_ASSERT (pFlashChip->pDev != NULL, ERROR); if ((ppBuf == NULL) || (ppBuf[0] == NULL)) { SPI_FLASH_LOG (0, "NULL buffer pointer %d\n", 0, 0, 0, 0, 0, 0); return (ERROR); } return (sst25AAIProgram (pFlashChip->pDev, (UINT32)uAddr, (UINT32)uBufLen, ppBuf[0])); } /******************************************************************************* * * vxbFlashErase - erase blocks * * This routine erases the blocks of the Flash chip used by upper(MTD) layer. * * RETURNS: OK or ERROR. * * ERRNO: N/A */ LOCAL int vxbFlashErase ( FLASH_CHIP_ID pFlashChip, FLASH_ADDR_T uBlkAdrs, /* block address */ UINT32 uBlks /* block number */ ) { /* check if the pDev pointer is valid */ VXB_ASSERT (pFlashChip->pDev != NULL, ERROR); return (spS25spiFlashSectorErase (pFlashChip->pDev, (UINT32)uBlkAdrs, uBlks)); } /******************************************************************************* * * spS25spiFlashShow - show the SPI flash info. * * This routine show the SPI flash info by vxBusShow. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void spS25spiFlashShow ( VXB_DEVICE_ID pDev, int verbose ) { SPI_FLASH_DEV * pDrvCtrl; /* Check for vaild parameter */ VXB_ASSERT_NONNULL_V (pDev); pDrvCtrl = (SPI_FLASH_DEV *) pDev->pDrvCtrl; printf (" %s unit %d on %s @ 0x%08x", pDev->pName, pDev->unitNumber, vxbBusTypeString (pDev->busID), pDev); printf (" with busInfo %p\n", pDev->u.pSubordinateBus); if (verbose >= 1) { printf(" BAR0 @ 0x%08x (memory mapped)\n", pDev->pRegBase[0]); printf(" pDrvCtrl @ 0x%08x\n", pDev->pDrvCtrl); } if (verbose >= 100) { printf (" ManufacturerID:0x%02x\n", pDrvCtrl->flash.manuId); printf (" Device ID: 0x%04x \n", pDrvCtrl->flash.devId); printf (" Extend ID: 0x%04x \n", pDrvCtrl->flash.extId); } /* continue probe if failed */ pDrvCtrl->isProbeOk = (spS25SpiFlashProbe(pDev, verbose) == OK) ? (TRUE) : (FALSE); if (verbose >= 100) { printf (" Probe: %s \r\n", (TRUE == pDrvCtrl->isProbeOk) ? "[OK]":"[FAIL]"); if (pDrvCtrl->isProbeOk) { printf (" Type: %s - %s\n", vendorGet(pDrvCtrl->flash.manuId), pDrvCtrl->type); printf (" PageSize: %dB \r\n", PageSize(pDrvCtrl)); printf (" SectorSize: %dKB\r\n", SectorSize(pDrvCtrl) >> 10); if (pDrvCtrl->chipSize >> 20) printf (" ChipSize: %dMB\r\n", pDrvCtrl->chipSize >> 20); else printf (" ChipSize: %dKB\r\n", pDrvCtrl->chipSize >> 10); } } return; } /******************************************************************************* * * cfiShow - show Flash driver deatiled information * * This routine prints the Flash driver CFI deatiled information. * * RETURNS: N/A * * ERRNO: N/A */ LOCAL void cfiShow ( struct cfi_ident * cfi, int verbose ) { int i; if (verbose >= 10000) { printf(" CFI Information Show: \r\n"); if (cfi->qryStr[0] != 'Q' || cfi->qryStr[1] != 'R' || cfi->qryStr[2] != 'Y') { printf (" Invalid CFI ident structure.\n"); return; } printf (" Primary OEM Command Set: %4.4X \n", cfi->priCmdSet); if (cfi->priExtTblAdrs) { printf (" Address for Primary Extended Table at %4.4Xh\n", cfi->priExtTblAdrs); } else { printf (" No Primary Extended Table\n"); } printf (" Alternate OEM Command Set: %4.4X (%s)\n", cfi->altCmdSet, vendorGet(cfi->altCmdSet)); if (cfi->altExtTblAdrs) { printf (" Address for Alternate OEM Extended Table at " "%4.4Xh\n", cfi->altExtTblAdrs); } else { printf (" No Alternate Extended Table\n"); } printf (" Vcc Min: %2d.%d V\n", cfi->vccMin >> 4, cfi->vccMin & 0xf); printf (" Vcc Max: %2d.%d V\n", cfi->vccMax >> 4, cfi->vccMax & 0xf); if (cfi->vppMin) { printf (" Vpp Min: %2d.%d V\n", cfi->vppMin >> 4, cfi->vppMin & 0xf); printf (" Vpp Max: %2d.%d V\n", cfi->vppMax >> 4, cfi->vppMax & 0xf); } else { printf (" No Vpp line\n"); } printf (" Typical timeout per single byte program: %d us\n", 1 << cfi->wrTimeout); printf (" Maximum timeout per single byte program: %d us\n", (1 << cfi->wrTimeoutMax) * (1 << cfi->wrTimeout)); if (cfi->wrBufTimeout || cfi->wrBufTimeoutMax) { printf (" Typical timeout for page program: %d us\n", 1 << cfi->wrBufTimeout); printf (" Maximum timeout for page program: %d us\n", (1 << cfi->wrBufTimeoutMax) * (1 << cfi->wrBufTimeout)); } else { printf (" Page program not supported\n"); } printf (" Typical individual sector erase timeout: %d ms\n", 1 << cfi->blkEraseTimeout); printf (" Maximum individual sector erase timeout: %d ms\n", (1 << cfi->blkEraseTimeoutMax) * (1 << cfi->blkEraseTimeout)); if (cfi->chipEraseTimeout || cfi->chipEraseTimeoutMax) { printf (" Typical full chip erase timeout: %d ms\n", 1 << cfi->chipEraseTimeout); printf (" Maximum full chip erase timeout: %d ms\n", (1 << cfi->chipEraseTimeoutMax) * (1 << cfi->chipEraseTimeout)); } else { printf (" Chip erase not supported\n"); } printf (" Device size: 0x%X bytes (%d MB)\n", 1 << cfi->devSize, 1 << (cfi->devSize - 20)); printf (" Flash Device Interface Description: 0x%4.4X\n", cfi->ifDesc); switch ((UINT8) cfi->ifDesc) { case CFI_INTERFACE_X8_ASYNC: printf (" - x8-only interface\n"); break; case CFI_INTERFACE_X16_ASYNC: printf (" - x16-only interface\n"); break; case CFI_INTERFACE_X8_BY_X16_ASYNC: printf (" - supports x8 and x16 interface\n"); break; case CFI_INTERFACE_X32_ASYNC: printf (" - x32-only interface\n"); break; case CFI_INTERFACE_SINGLE_IO_SPI_ASYNC: printf (" - single I/O SPI, 3-byte address\n"); break; case CFI_INTERFACE_MULTI_IO_SPI_ASYNC: printf (" - Multi I/O SPI, 3-byte address\n"); break; default: printf (" - Unknown\n"); break; } printf (" Max number of bytes in multi-byte write: %d bytes\n", 1 << cfi->wrBufLen); printf (" Number of Erase Block Regions within device: %d\n", cfi->regionNum); for (i = 0; i < cfi->regionNum; i++) { printf (" Region %d - blkSize: %3.3dKB" "blkNum: %3.3d range: 0x0-0x%x \r\n", i, CFI_BLK_SIZE ((UINT16) (cfi->EraseRegionInfo[i]>>16)) >> 10, (UINT16)cfi->EraseRegionInfo[i] + 1, CFI_BLK_SIZE ((UINT16) (cfi->EraseRegionInfo[i] >> 16)) * ((UINT16) cfi->EraseRegionInfo[i] + 1)); } } if (verbose >= 100000) { printf (" cfi->qryStr[0] - 0x%02x\r\n", cfi->qryStr[0]); printf (" cfi->qryStr[1] - 0x%02x\r\n", cfi->qryStr[1]); printf (" cfi->qryStr[2] - 0x%02x\r\n", cfi->qryStr[2]); printf (" cfi->priCmdSet - 0x%04x\r\n", cfi->priCmdSet); printf (" cfi->priExtTblAdrs - 0x%04x\r\n", cfi->priExtTblAdrs); printf (" cfi->altCmdSet - 0x%04x\r\n", cfi->altCmdSet); printf (" cfi->altExtTblAdrs - 0x%04x\r\n", cfi->altExtTblAdrs); printf (" cfi->vccMin - 0x%02x\r\n", cfi->vccMin); printf (" cfi->vccMax - 0x%02x\r\n", cfi->vccMax); printf (" cfi->vppMin - 0x%02x\r\n", cfi->vppMin); printf (" cfi->vppMax - 0x%02x\r\n", cfi->vppMax); printf (" cfi->wrTimeout - 0x%02x\r\n", cfi->wrTimeout); printf (" cfi->wrBufTimeout - 0x%02x\r\n", cfi->wrBufTimeout); printf (" cfi->blkEraseTimeout - 0x%02x\r\n", cfi->blkEraseTimeout); printf (" cfi->chipEraseTimeout - 0x%02x\r\n", cfi->chipEraseTimeout); printf (" cfi->wrTimeoutMax - 0x%02x\r\n", cfi->wrTimeoutMax); printf (" cfi->wrBufTimeoutMax - 0x%02x\r\n", cfi->wrBufTimeoutMax); printf (" cfi->blkEraseTimeoutMax - 0x%02x\r\n", cfi->blkEraseTimeoutMax); printf (" cfi->chipEraseTimeoutMax - 0x%02x\r\n", cfi->chipEraseTimeoutMax); printf (" cfi->devSize - 0x%02x\r\n", cfi->devSize); printf (" cfi->ifDesc - 0x%02x\r\n", cfi->ifDesc); printf (" cfi->wrBufLen - 0x%02x\r\n", cfi->wrBufLen); printf (" cfi->regionNum - 0x%02x\r\n", cfi->regionNum); printf (" cfi->EraseRegionInfo[0] - 0x%08x\r\n", cfi->EraseRegionInfo[0]); printf (" cfi->EraseRegionInfo[1] - 0x%08x\r\n", cfi->EraseRegionInfo[1]); } return; } #define SP25FLASH_TEST #ifdef SP25FLASH_TEST void sp25test_erase(uint32_t addr) { STATUS st; VXB_DEVICE_ID pDev = vxbInstByNameFind("spiFlash_s25fs256s",0); if(NULL == pDev) { printf("can not find dev: spiFlash_s25fs256s!\r\n"); return; } st = spS25spiFlashSectorErase(pDev,addr,1); if(OK ==st) { printf("successfully erase FLASH.\r\n"); } else { printf("Failed to erase FLASH!\r\n"); } } void sp25test_write(uint32_t addr, uint32_t len) { int i = 0; UINT8 buf[1024] = {0}; STATUS st; VXB_DEVICE_ID pDev = vxbInstByNameFind("spiFlash_s25fs256s",0); if(len >sizeof(buf)) { printf("Only write max %d bytes. len %d too big!\r\n", sizeof(buf), len); return; } if(NULL == pDev) { printf("can not find dev: spiFlash_s25fs256s!\r\n"); return; } for(i = 0; i< len; i++) { buf[i] = i; } printf("The buffer content:\r\n"); d((void*)buf, len, 1); st = spS25spiFlashProgram(pDev,addr,len,buf); if(OK ==st) { printf("successfully write FLASH.\r\n"); } else { printf("Failed to write FLASH!\r\n"); } } void sp25test_read(uint32_t addr, uint32_t len) { STATUS st; UINT8 recv[1024] = {0}; VXB_DEVICE_ID pDev = vxbInstByNameFind("spiFlash_s25fs256s",0); if(len >sizeof(recv)) { printf("Only read max %d bytes. len %d too big!\r\n", sizeof(recv), len); return; } if(NULL == pDev) { printf("can not find dev: spiFlash_s25fs256s!\r\n"); return; } st = spS25spiFlashRead(pDev,addr,len,recv); if(OK ==st) { printf("read %d bytes from 0x%x: ",len, addr); d((void*)recv, len, 1); } else { printf("Failed to read FLASH!\r\n"); } } void sp25test(UINT32 addr, UINT32 len) { sp25test_erase(addr); sp25test_write(addr,len); sp25test_read(addr,len); } #endif