diff options
Diffstat (limited to 'cfe/cfe/dev/dev_flash.c')
-rw-r--r-- | cfe/cfe/dev/dev_flash.c | 1367 |
1 files changed, 1367 insertions, 0 deletions
diff --git a/cfe/cfe/dev/dev_flash.c b/cfe/cfe/dev/dev_flash.c new file mode 100644 index 0000000..98ad3e7 --- /dev/null +++ b/cfe/cfe/dev/dev_flash.c @@ -0,0 +1,1367 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * Flash device driver File: dev_flash.c + * + * This driver supports various types of flash + * parts. You can also put the environment storage in + * the flash - the top sector is reserved for that purpose. + * + * Author: Mitch Lichtenberg (mpl@broadcom.com) + * + ********************************************************************* + * + * Copyright 2000,2001,2002,2003 + * Broadcom Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and + * conditions. Subject to these conditions, you may download, + * copy, install, use, modify and distribute modified or unmodified + * copies of this software in source and/or binary form. No title + * or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions + * as they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or + * logo of Broadcom Corporation. The "Broadcom Corporation" + * name may not be used to endorse or promote products derived + * from this software without the prior written permission of + * Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT + * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN + * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + ********************************************************************* */ + + +#include "lib_types.h" +#include "lib_malloc.h" +#include "lib_printf.h" +#include "lib_string.h" +#include "addrspace.h" +#include "cfe_iocb.h" +#include "cfe_device.h" +#include "cfe_ioctl.h" +#include "cfe_error.h" + +#include "dev_flash.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define FLASHCMD(sc,x,y) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+ \ + ((x)<<(sc)->flashdrv_widemode))) = (y) +#define FLASHSTATUS(sc,x) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+ \ + ((x)<<(sc)->flashdrv_widemode))) + +#define WRITEFLASH_K1(sc,x,y) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+(x))) = (y) +#define READFLASH_K1(sc,x) *((volatile unsigned char *)(sc->flashdrv_cmdaddr+(x))) + +#define WRITEFLASH_K1W(sc,x,y) *((volatile unsigned short *)(sc->flashdrv_cmdaddr+(x))) = (y) +#define READFLASH_K1W(sc,x) *((volatile unsigned short *)(sc->flashdrv_cmdaddr+(x))) + + +#define GETCFIBYTE(softc,offset) READFLASH_K1(softc,((offset) << (softc->flashdrv_widemode))) + +/* ********************************************************************* + * Forward declarations + ********************************************************************* */ + + +static void flashdrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr); + + +static int flashdrv_open(cfe_devctx_t *ctx); +static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); +static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); +static int flashdrv_close(cfe_devctx_t *ctx); + +/* ********************************************************************* + * Device dispatch + ********************************************************************* */ + +const static cfe_devdisp_t flashdrv_dispatch = { + flashdrv_open, + flashdrv_read, + flashdrv_inpstat, + flashdrv_write, + flashdrv_ioctl, + flashdrv_close, + NULL, + NULL +}; + +const cfe_driver_t flashdrv = { + "CFI flash", + "flash", + CFE_DEV_FLASH, + &flashdrv_dispatch, + flashdrv_probe +}; + + +/* ********************************************************************* + * Structures + ********************************************************************* */ + +typedef struct flash_cfidata_s { + unsigned int cfidata_cmdset; /* ID of primary command set */ + unsigned int cfidata_devif; /* device interface byte */ + unsigned int cfidata_size; /* probed device size */ +} flash_cfidata_t; + +typedef struct flashops_s flashops_t; + +typedef struct flashdrv_s { + flash_probe_t flashdrv_probe; /* data from probe */ + int flashdrv_devsize; /* size reported by driver */ + unsigned char *flashdrv_cmdaddr; /* virtual address (K1) */ + int flashdrv_widemode; /* 1=wide flash in byte mode, 0=narrow flash */ + int flashdrv_initialized; /* true if we've probed already */ + flash_info_t flashdrv_info; + int flashdrv_nvram_ok; /* true if we can use as NVRAM */ + int flashdrv_unlocked; /* true if we can r/w past devsize */ + nvram_info_t flashdrv_nvraminfo; + flashops_t *flashdrv_ops; + flash_cfidata_t flashdrv_cfidata; +} flashdrv_t; + +struct flashops_s { + int (*erasesector)(flashdrv_t *f,int offset); + int (*writeblk)(flashdrv_t *f,int offset,void *buf,int len); +}; + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define FLASHOP_ERASE_SECTOR(softc,sect) (*((softc)->flashdrv_ops->erasesector))((softc),(sect)) +#define FLASHOP_WRITE_BLOCK(softc,off,buf,len) (*((softc)->flashdrv_ops->writeblk))((softc),(off),(buf),(len)) + +/* ********************************************************************* + * forward declarations + ********************************************************************* */ + + +static int flash_sector_query(flashdrv_t *softc,flash_sector_t *sector); + +static int amd_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len); +static int amd_flash_erase_sector(flashdrv_t *softc,int offset); + +static int intel_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len); +static int intel_flash_erase_sector(flashdrv_t *softc,int offset); + +static flashops_t amd_flashops = { + amd_flash_erase_sector, + amd_flash_write_block, +}; + +static flashops_t intel_flashops = { + intel_flash_erase_sector, + intel_flash_write_block, +}; + +#define FLASHOPS_DEFAULT amd_flashops + + + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +extern void *flash_write_all_ptr; +extern int flash_write_all_len; + +extern void _cfe_flushcache(int); + + +#if 0 +/* ********************************************************************* + * jedec_flash_maufacturer(softc) + * + * Return the manufacturer ID for this flash part. + * + * Input parameters: + * softc - flash context + * + * Return value: + * nothing + ********************************************************************* */ + +static unsigned int jedec_flash_manufacturer(flashdrv_t *softc) +{ + unsigned int res; + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_AUTOSEL); + + res = FLASHSTATUS(softc,FLASH_JEDEC_OFFSET_MFR) & 0xFF; + + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_RESET); + + return res; +} + +/* ********************************************************************* + * jedec_flash_type(softc) + * + * Return the manufacturer's type for the flash + * + * Input parameters: + * softc - flash context + * + * Return value: + * nothing + ********************************************************************* */ +static unsigned int jedec_flash_type(flashdrv_t *softc) +{ + unsigned int res; + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_AUTOSEL); + + res = FLASHSTATUS(softc,FLASH_JEDEC_OFFSET_DEV) & 0xFF; + + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_RESET); + + return res; +} + +#endif + +/* ********************************************************************* + * amd_flash_write_byte(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * val - byte to write + * + * Return value: + * 0 if ok + * else if flash could not be written + ********************************************************************* */ +static inline int amd_flash_write_byte(flashdrv_t *softc,int offset, unsigned char val) +{ + unsigned int value; + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + + /* Send a program command */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_PROGRAM); + + /* Write a byte */ + WRITEFLASH_K1(softc,offset,val); + + for (;;) { + value = READFLASH_K1(softc,offset) & 0xFF; + + if ((value & 0x80) == (val & 0x80)) { + return 0; + } + if ((value & 0x20) != 0x20) { + continue; + } + + if ((READFLASH_K1(softc,offset) & 0x80) == (val & 0x80)) { + return 0; + } + else { + return -1; + } + } +} + + +/* ********************************************************************* + * amd_flash_write_block(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * buf - buffer of bytes to write + * len - number of bytes to write + * + * Return value: + * number of bytes written + ********************************************************************* */ +static int amd_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len) +{ + unsigned char *ptr; + + ptr = buf; + + while (len) { + if (amd_flash_write_byte(softc,offset,*ptr) < 0) break; + len--; + ptr++; + offset++; + + } + + return (ptr - (unsigned char *)buf); +} + + +/* ********************************************************************* + * amd_flash_erase_sector(softc,offset) + * + * Erase a single sector in the flash device + * + * Input parameters: + * softc - device context + * offset - offset in flash of sector to erase + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int amd_flash_erase_sector(flashdrv_t *softc,int offset) +{ + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); /* cycles 1-2 */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + + /* send the erase command */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_ERASE_3); /* cycle 3 */ + + /* Do an "unlock write" sequence */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_1,AMD_FLASH_MAGIC_1); /* cycles 4-5 */ + FLASHCMD(softc,AMD_FLASH_MAGIC_ADDR_2,AMD_FLASH_MAGIC_2); + + /* + * Send the "erase sector" qualifier - don't use FLASHCMD + * because it changes the offset. + */ + WRITEFLASH_K1(softc,offset,AMD_FLASH_ERASE_SEC_6); + + while ((READFLASH_K1(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + + return 0; +} + + + +/* ********************************************************************* + * intel_flash_write_byte(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * val - byte to write + * + * Return value: + * 0 if ok + * else if flash could not be written + ********************************************************************* */ +static inline int intel_flash_write_byte(flashdrv_t *softc, + int offset, unsigned char val) +{ + unsigned int value; + + /* Send a program command */ + WRITEFLASH_K1(softc,offset,INTEL_FLASH_PROGRAM); + + /* Write a byte */ + WRITEFLASH_K1(softc,offset,val); + + + while ((READFLASH_K1(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + + value = READFLASH_K1(softc,offset) & 0xFF; + + if (value & (0x01|0x08|0x10)) return -1; + return 0; +} + +/* ********************************************************************* + * intel_flash_write_word(softc,offset,val) + * + * Write a single word to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * val - word to write + * + * Return value: + * 0 if ok + * else if flash could not be written + ********************************************************************* */ +static inline int intel_flash_write_word(flashdrv_t *softc, + int offset, unsigned short val) +{ + unsigned int value; + + + /* Send a program command */ + WRITEFLASH_K1W(softc,offset,INTEL_FLASH_PROGRAM); + + /* Write a byte */ + WRITEFLASH_K1W(softc,offset,val); + + + while ((READFLASH_K1W(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + + value = READFLASH_K1W(softc,offset) & 0xFF; + + if (value & (0x01|0x08|0x10)) return -1; + return 0; +} + +/* ********************************************************************* + * intel_flash_write_block(softc,offset,val) + * + * Write a single byte to the flash. The sector that the flash + * byte is in should have been previously erased, or else this + * routine may hang. + * + * Input parameters: + * softc - flash context + * offset - distance in bytes into the flash + * buf - buffer of bytes to write + * len - number of bytes to write + * + * Return value: + * number of bytes written + ********************************************************************* */ +static int intel_flash_write_block(flashdrv_t *softc,int offset,void *buf,int len) +{ + unsigned char *ptr; + unsigned short *ptrw; + + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_16BIT) { + ptrw = buf; + offset &= ~1; /* offset must be even */ + while (len > 0) { + if (intel_flash_write_word(softc,offset,*ptrw) < 0) break; + len-=2; + ptrw++; + offset+=2; + } + WRITEFLASH_K1(softc,offset,INTEL_FLASH_READ_MODE); + return ((unsigned char *) ptrw - (unsigned char *)buf); + } + else { + ptr = buf; + while (len) { + if (intel_flash_write_byte(softc,offset,*ptr) < 0) break; + len--; + ptr++; + offset++; + } + WRITEFLASH_K1(softc,offset,INTEL_FLASH_READ_MODE); + return (ptr - (unsigned char *)buf); + } + +} + + +/* ********************************************************************* + * intel_flash_erase_sector(softc,offset) + * + * Erase a single sector on the flash device + * + * Input parameters: + * softc - device context + * offset - offset in flash of sector to erase + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ +static int intel_flash_erase_sector(flashdrv_t *softc,int offset) +{ + WRITEFLASH_K1(softc,offset,INTEL_FLASH_ERASE_BLOCK); + WRITEFLASH_K1(softc,offset,INTEL_FLASH_ERASE_CONFIRM); + + while ((READFLASH_K1(softc,offset) & 0x80) != 0x80) { + /* NULL LOOP */ + } + WRITEFLASH_K1(softc,offset,INTEL_FLASH_READ_MODE); + + return 0; +} + + + + + +/* ********************************************************************* + * FLASH_ERASE_RANGE(softc,range) + * + * Erase a range of sectors + * + * Input parameters: + * softc - our flash + * range - range structure + * + * Return value: + * 0 if ok + * else error + ********************************************************************* */ + +static int flash_erase_range(flashdrv_t *softc,flash_range_t *range) +{ + flash_sector_t sector; + int res; + + if (softc->flashdrv_info.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + + if (range->range_base+range->range_length > softc->flashdrv_devsize) { + return CFE_ERR_INV_PARAM; + } + + res = 0; + + sector.flash_sector_idx = 0; + + for (;;) { + res = flash_sector_query(softc,§or); + if (res != 0) break; + if (sector.flash_sector_status == FLASH_SECTOR_INVALID) { + break; + } + + if ((sector.flash_sector_offset >= range->range_base) && + (sector.flash_sector_offset < + (range->range_base+range->range_length-1))) { + + if (softc->flashdrv_nvram_ok && + (sector.flash_sector_offset >= softc->flashdrv_nvraminfo.nvram_offset)) { + break; + } + res = FLASHOP_ERASE_SECTOR(softc,sector.flash_sector_offset); + if (res != 0) break; + } + sector.flash_sector_idx++; + } + + return res; + +} + +/* ********************************************************************* + * FLASH_ERASE_ALL(softc) + * + * Erase the entire flash device, except the NVRAM area, + * sector-by-sector. + * + * Input parameters: + * softc - our flash + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_erase_all(flashdrv_t *softc) +{ + flash_range_t range; + + range.range_base = 0; + range.range_length = softc->flashdrv_devsize; + + return flash_erase_range(softc,&range); +} + +/* ********************************************************************* + * FLASH_CFI_GETSECTORS(softc) + * + * Query the CFI information and store the sector info in our + * private probe structure. + * + * Input parameters: + * softc - our flash info + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_cfi_getsectors(flashdrv_t *softc) +{ + int idx; + int regcnt; + int nblks; + int blksiz; + + regcnt = GETCFIBYTE(softc,FLASH_CFI_REGION_COUNT); + + softc->flashdrv_probe.flash_nsectors = regcnt; + + for (idx = 0; idx < regcnt; idx++) { + nblks = ((int)GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+0+idx*4) + + (int)(GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+1+idx*4)<<8)) + 1; + blksiz = ((int)GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+2+idx*4) + + (int)(GETCFIBYTE(softc,FLASH_CFI_REGION_TABLE+3+idx*4)<<8)) * 256; + softc->flashdrv_probe.flash_sectors[idx] = + FLASH_SECTOR_RANGE(nblks,blksiz); + } + + + return 0; +} + +/* ********************************************************************* + * FLASH_SECTOR_QUERY(softc,sector) + * + * Query the sector information about a particular sector. You can + * call this iteratively to find out about all of the sectors. + * + * Input parameters: + * softc - our flash info + * sector - structure to receive sector information + * + * Return value: + * 0 if ok + * else error code + ********************************************************************* */ + +static int flash_sector_query(flashdrv_t *softc,flash_sector_t *sector) +{ + int idx; + int nblks; + int blksiz; + unsigned int offset; + int curblk; + + if (softc->flashdrv_info.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + if (softc->flashdrv_probe.flash_nsectors == 0) { + return CFE_ERR_UNSUPPORTED; + } + + offset = 0; + curblk = 0; + for (idx = 0; idx < softc->flashdrv_probe.flash_nsectors; idx++) { + nblks = FLASH_SECTOR_NBLKS(softc->flashdrv_probe.flash_sectors[idx]); + blksiz = FLASH_SECTOR_SIZE(softc->flashdrv_probe.flash_sectors[idx]); + if (sector->flash_sector_idx < curblk+nblks) { + sector->flash_sector_status = FLASH_SECTOR_OK; + sector->flash_sector_offset = + offset + (sector->flash_sector_idx-curblk)*blksiz; + sector->flash_sector_size = blksiz; + break; + } + + offset += (nblks)*blksiz; + curblk += nblks; + } + + + if (idx == softc->flashdrv_probe.flash_nsectors) { + sector->flash_sector_status = FLASH_SECTOR_INVALID; + } + + return 0; +} + + +/* ********************************************************************* + * FLASH_SET_CMDSET(softc,cmdset) + * + * Set the command-set that we'll honor for this flash. + * + * Input parameters: + * softc - our flash + * cmdset - FLASH_CFI_CMDSET_xxx + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_set_cmdset(flashdrv_t *softc,int cmdset) +{ + switch (cmdset) { + case FLASH_CFI_CMDSET_INTEL_ECS: + case FLASH_CFI_CMDSET_INTEL_STD: + softc->flashdrv_ops = &intel_flashops; + /* XXX: Intel flashes don't have the "a-1" line. Yay. */ + softc->flashdrv_widemode = 0; + break; + case FLASH_CFI_CMDSET_AMD_STD: + case FLASH_CFI_CMDSET_AMD_ECS: + softc->flashdrv_ops = &amd_flashops; + break; + default: + /* we don't understand the command set - treat it like ROM */ + softc->flashdrv_info.flash_type = FLASH_TYPE_ROM; + } +} + + +/* ********************************************************************* + * FLASH_CFI_PROBE(softc) + * + * Try to do a CFI query on this device. If we find the m + * magic signature, extract some useful information from the + * query structure. + * + * Input parameters: + * softc - out flash + * + * Return value: + * 0 if successful, <0 if error + ********************************************************************* */ +static int flash_cfi_probe(flashdrv_t *softc) +{ + FLASHCMD(softc,FLASH_CFI_QUERY_ADDR,FLASH_CFI_QUERY_MODE); + + if (!((GETCFIBYTE(softc,FLASH_CFI_SIGNATURE+0) == 'Q') && + (GETCFIBYTE(softc,FLASH_CFI_SIGNATURE+1) == 'R') && + (GETCFIBYTE(softc,FLASH_CFI_SIGNATURE+2) == 'Y'))) { + + FLASHCMD(softc,FLASH_CFI_QUERY_ADDR,FLASH_CFI_QUERY_EXIT); + return CFE_ERR_UNSUPPORTED; + } + + /* + * Gather info from flash + */ + + softc->flashdrv_cfidata.cfidata_cmdset = + ((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_COMMAND_SET))) + + (((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_COMMAND_SET+1))) << 8); + + softc->flashdrv_cfidata.cfidata_devif = + ((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_DEVICE_INTERFACE))) + + (((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_DEVICE_INTERFACE+1))) << 8); + + softc->flashdrv_cfidata.cfidata_size = + 1 << ((unsigned int) (GETCFIBYTE(softc,FLASH_CFI_DEVICE_SIZE))); + + flash_cfi_getsectors(softc); + + /* + * Don't need to be in query mode anymore. + */ + + FLASHCMD(softc,FLASH_CFI_QUERY_ADDR,FLASH_CFI_QUERY_EXIT); + + softc->flashdrv_info.flash_type = FLASH_TYPE_FLASH; + + flash_set_cmdset(softc,softc->flashdrv_cfidata.cfidata_cmdset); + + return 0; + +} + +/* ********************************************************************* + * FLASH_GETWIDTH(softc,info) + * + * Try to determine the width of the flash. This is needed for + * management purposes, since some 16-bit flash parts in 8-bit mode + * have an "A-1" (address line -1) wire to select bytes within + * a 16-bit word. When this is present, the flash commands + * will have different offsets. + * + * Input parameters: + * softc - our flash + * info - flash info structure + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_getwidth(flashdrv_t *softc,flash_info_t *info) +{ + softc->flashdrv_widemode = 0; /* first try narrow */ + + if (flash_cfi_probe(softc) == 0) { + return; + } + + softc->flashdrv_widemode = 1; /* then wide */ + + if (flash_cfi_probe(softc) == 0) { + return; + } + + /* Just return, assume not wide if no CFI interface */ + softc->flashdrv_widemode = 0; + + softc->flashdrv_info.flash_type = FLASH_TYPE_ROM; /* no CFI: treat as ROM */ +} + +/* ********************************************************************* + * flash_getinfo(softc) + * + * Try to determine if the specified region is flash, ROM, SRAM, + * or something else. + * + * Input parameters: + * softc - our context + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_getinfo(flashdrv_t *softc) +{ + uint8_t save0,save1; + volatile uint8_t *ptr; + flash_info_t *info = &(softc->flashdrv_info); + + /* + * Set up some defaults based on the probe data + */ + + softc->flashdrv_widemode = 0; + info->flash_base = softc->flashdrv_probe.flash_phys; + info->flash_size = softc->flashdrv_probe.flash_size; + softc->flashdrv_devsize = softc->flashdrv_probe.flash_size; + info->flash_type = FLASH_TYPE_UNKNOWN; + info->flash_flags = 0; + + /* + * If we've been told not to try probing, just assume + * we're a flash part. + */ + + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_MANUAL) { + info->flash_type = FLASH_TYPE_FLASH; + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_WIDE) { + softc->flashdrv_widemode = TRUE; + } + if (softc->flashdrv_probe.flash_cmdset) { + flash_set_cmdset(softc,softc->flashdrv_probe.flash_cmdset); + } + return; + } + + /* + * Attempt to read/write byte zero. If it is changable, + * this is SRAM (or maybe a ROM emulator with the write line hooked up) + */ + + ptr = (volatile uint8_t *) UNCADDR(softc->flashdrv_probe.flash_phys); + save0 = *ptr; /* save old value */ + save1 = *(ptr+1); /* save old value */ + *(ptr) = 0x55; + if ((*ptr) == 0x55) { + *(ptr) = 0xAA; + if ((*ptr) == 0xAA) { + info->flash_type = FLASH_TYPE_SRAM; + } + } + + if (*ptr == save0) info->flash_type = FLASH_TYPE_ROM; + else (*ptr) = save0; /* restore old value */ + + /* + * If we thought it was ROM, try doing a CFI query + * to see if it was flash. This check is kind of kludgey + * but should work. + */ + + if (info->flash_type == FLASH_TYPE_ROM) { + flash_getwidth(softc,info); + if (info->flash_type == FLASH_TYPE_FLASH) { + } + } +} + +/* ********************************************************************* + * flashdrv_setup_nvram(softc) + * + * If we're going to be using a sector of the flash for NVRAM, + * go find that sector and set it up. + * + * Input parameters: + * softc - our flash + * + * Return value: + * nothing. flashdrv_nvram_ok might change though. + ********************************************************************* */ + +static void flashdrv_setup_nvram(flashdrv_t *softc) +{ + flash_sector_t sector; + int res; + + softc->flashdrv_nvram_ok = FALSE; + + if (softc->flashdrv_info.flash_type != FLASH_TYPE_FLASH) { + return; + } + + sector.flash_sector_idx = 0; + for (;;) { + res = flash_sector_query(softc,§or); + if (res == CFE_ERR_UNSUPPORTED) break; + if (res == 0) { + if (sector.flash_sector_status != FLASH_SECTOR_INVALID) { + sector.flash_sector_idx++; + continue; + } + } + break; + } + + /* The sector offset will still contain the value at the end + of the last successful call. That's the last sector, so + we can now use this to fill in the NVRAM info structure */ + + if (res != CFE_ERR_UNSUPPORTED) { + softc->flashdrv_nvraminfo.nvram_offset = sector.flash_sector_offset; + softc->flashdrv_nvraminfo.nvram_size = sector.flash_sector_size; + softc->flashdrv_nvraminfo.nvram_eraseflg = TRUE; /* needs erase */ + softc->flashdrv_nvram_ok = TRUE; + /* + * Set the flash's size as reported in the flash_info structure + * to be the size without the NVRAM sector at the end. + */ + softc->flashdrv_info.flash_size = sector.flash_sector_offset; + softc->flashdrv_devsize = sector.flash_sector_offset; + } + +} + + +/* ********************************************************************* + * flashdrv_probe(drv,probe_a,probe_b,probe_ptr) + * + * Device probe routine. Attach the flash device to + * CFE's device table. + * + * Input parameters: + * drv - driver descriptor + * probe_a - physical address of flash + * probe_b - size of flash (bytes) + * probe_ptr - unused + * + * Return value: + * nothing + ********************************************************************* */ + +static void flashdrv_probe(cfe_driver_t *drv, + unsigned long probe_a, unsigned long probe_b, + void *probe_ptr) +{ + flashdrv_t *softc; + flash_probe_t *probe; + char descr[80]; + + probe = (flash_probe_t *) probe_ptr; + + /* + * probe_a is the flash base address + * probe_b is the size of the flash + * probe_ptr is unused. + */ + + softc = (flashdrv_t *) KMALLOC(sizeof(flashdrv_t),0); + if (softc) { + memset(softc,0,sizeof(flashdrv_t)); + + if (probe) { + /* Passed probe structure, do fancy stuff */ + memcpy(&(softc->flashdrv_probe),probe,sizeof(flash_probe_t)); + if (softc->flashdrv_probe.flash_prog_phys == 0) { + softc->flashdrv_probe.flash_prog_phys = + softc->flashdrv_probe.flash_phys; + } + } + else { + /* Didn't pass probe structure, do the compatible thing */ + softc->flashdrv_probe.flash_phys = probe_a; + softc->flashdrv_probe.flash_prog_phys = probe_a; + softc->flashdrv_probe.flash_size = probe_b; + softc->flashdrv_probe.flash_flags = FLASH_FLG_NVRAM; + } + + softc->flashdrv_cmdaddr = (char *) UNCADDR(softc->flashdrv_probe.flash_prog_phys); + softc->flashdrv_initialized = 0; + softc->flashdrv_ops = &FLASHOPS_DEFAULT; + xsprintf(descr,"%s at %08X size %uKB",drv->drv_description, + softc->flashdrv_probe.flash_phys, + softc->flashdrv_probe.flash_size/1024); + cfe_attach(drv,softc,NULL,descr); + } + +} + + +/* ********************************************************************* + * flashdrv_open(ctx) + * + * Called when the flash device is opened. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 if ok else error code + ********************************************************************* */ + +static int flashdrv_open(cfe_devctx_t *ctx) +{ + flashdrv_t *softc = ctx->dev_softc; + + /* + * do initialization + */ + + if (!softc->flashdrv_initialized) { + + /* + * Assume it's not an NVRAM-capable flash + */ + + softc->flashdrv_nvram_ok = FALSE; + + /* + * Probe flash for geometry + */ + flash_getinfo(softc); + + /* + * Find the last sector if in NVRAM mode + */ + + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_NVRAM) { + flashdrv_setup_nvram(softc); + } + + softc->flashdrv_initialized = TRUE; + } + + return 0; +} + + +/* ********************************************************************* + * flashdrv_read(ctx,buffer) + * + * Read data from the flash device. The flash device is + * considered to be like a disk (you need to specify the offset). + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashdrv_t *softc = ctx->dev_softc; + unsigned char *bptr; + unsigned char *flashbase; + int offset; + int blen; + + /* + * For now, read the flash from K1 (always). Eventually + * we need to flush the cache after a write. + */ + + flashbase = (unsigned char *) UNCADDR(softc->flashdrv_probe.flash_phys); + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = (int) buffer->buf_offset; + + if (!(softc->flashdrv_unlocked)) { + if ((offset + blen) > softc->flashdrv_devsize) { + blen = softc->flashdrv_devsize - offset; + } + } + +#ifdef _FLASH_BROKEN_BYTEREAD_ + /* + * BCM1250 users: don't worry about this. This hack is for + * something else and should not be used with the BCM1250. + */ + if (softc->flashdrv_probe.flash_flags & FLASH_FLG_16BIT) { + uint16_t *src; + int len = blen; + int idx = 0; + union { + uint16_t x; + char b[2]; + } u; + + src = (uint16_t *) flashbase; + while (len > 0) { + u.x = src[(idx+offset)>>1]; + *bptr++ = u.b[(idx+offset)&1]; + len--; + idx++; + } + } + else { + memcpy(bptr,flashbase + offset, blen); + } +#else + memcpy(bptr,flashbase + offset, blen); +#endif + + buffer->buf_retlen = blen; + + return 0; +} + +/* ********************************************************************* + * flashdrv_inpstat(ctx,inpstat) + * + * Return "input status". For flash devices, we always return true. + * + * Input parameters: + * ctx - device context + * inpstat - input status structure + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) +{ + /* flashdrv_t *softc = ctx->dev_softc; */ + + inpstat->inp_status = 1; + return 0; +} + + +/* ********************************************************************* + * flash_writeall(softc,buffer) + * + * Write the entire flash and reboot. This is a special case + * used for when the flash currently being used for CFE's + * execution is updated. A small assembly routine is relocated + * to DRAM to do the update (so that the programming routine is + * not erased while we're running it), and then the update + * is done. When completed, CFE is restarted. + * + * (we could get really sleazy here and touch the routine first + * so it will stay in the cache, thereby eliminating the need + * to relocate it, but that's dangerous) + * + * Input parameters: + * softc - our context + * buffer - buffer descriptor + * + * Return value: + * does not return + ********************************************************************* */ + +static int flash_writeall(flashdrv_t *softc,iocb_buffer_t *buffer) +{ + void *rptr; + void (*routine)(unsigned char *data,unsigned int flashbase, + unsigned int size,unsigned int secsize); + + rptr = KMALLOC(flash_write_all_len,0); + + if (!rptr) return CFE_ERR_NOMEM; + + memcpy(rptr,flash_write_all_ptr,flash_write_all_len); + + _cfe_flushcache(0); + + routine = rptr; + + (*routine)(buffer->buf_ptr, + softc->flashdrv_probe.flash_phys, + buffer->buf_length, + 65536); + + return -1; +} + + +/* ********************************************************************* + * flashdrv_write(ctx,buffer) + * + * Write data to the flash device. The flash device is + * considered to be like a disk (you need to specify the offset). + * + * Input parameters: + * ctx - device context + * buffer - buffer descriptor + * + * Return value: + * 0 if ok, else error code + ********************************************************************* */ + +static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashdrv_t *softc = ctx->dev_softc; + unsigned char *bptr; + int offset; + int blen; + int res; + + bptr = buffer->buf_ptr; + blen = buffer->buf_length; + offset = (int) buffer->buf_offset; + + if (!(softc->flashdrv_unlocked)) { + if ((offset + blen) > softc->flashdrv_devsize) { + blen = softc->flashdrv_devsize - offset; + } + } + + res = FLASHOP_WRITE_BLOCK(softc,offset,bptr,blen); + + buffer->buf_retlen = res; + + /* XXX flush the cache here? */ + + return (res == blen) ? 0 : CFE_ERR_IOERR; +} + +/* ********************************************************************* + * flashdrv_ioctl(ctx,buffer) + * + * Handle special IOCTL functions for the flash. Flash devices + * support NVRAM information, sector and chip erase, and a + * special IOCTL for updating the running copy of CFE. + * + * Input parameters: + * ctx - device context + * buffer - descriptor for IOCTL parameters + * + * Return value: + * 0 if ok else error + ********************************************************************* */ +static int flashdrv_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + flashdrv_t *softc = ctx->dev_softc; + nvram_info_t *info; + int offset; + + /* + * If using flash to store environment, only the last sector + * is used for environment stuff. + */ + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_NVRAM_ERASE: + if (softc->flashdrv_nvram_ok == FALSE) return CFE_ERR_UNSUPPORTED; + FLASHOP_ERASE_SECTOR(softc,softc->flashdrv_nvraminfo.nvram_offset); + return 0; + + case IOCTL_NVRAM_GETINFO: + info = (nvram_info_t *) buffer->buf_ptr; + if (buffer->buf_length != sizeof(nvram_info_t)) return CFE_ERR_INV_PARAM; + if (softc->flashdrv_nvram_ok == FALSE) return CFE_ERR_UNSUPPORTED; + info->nvram_offset = softc->flashdrv_nvraminfo.nvram_offset; + info->nvram_size = softc->flashdrv_nvraminfo.nvram_size; + info->nvram_eraseflg = softc->flashdrv_nvraminfo.nvram_eraseflg; + buffer->buf_retlen = sizeof(nvram_info_t); + return 0; + + case IOCTL_FLASH_ERASE_SECTOR: + offset = (int) buffer->buf_offset; + if (!(softc->flashdrv_unlocked)) { + if (offset >= softc->flashdrv_devsize) return -1; + } + FLASHOP_ERASE_SECTOR(softc,offset); + return 0; + + case IOCTL_FLASH_ERASE_ALL: + offset = (int) buffer->buf_offset; + if (offset != 0) return -1; + flash_erase_all(softc); + return 0; + + case IOCTL_FLASH_WRITE_ALL: + flash_writeall(softc,buffer); + return -1; /* should not return */ + + case IOCTL_FLASH_GETINFO: + memcpy(buffer->buf_ptr,&(softc->flashdrv_info),sizeof(flash_info_t)); + return 0; + + case IOCTL_FLASH_GETSECTORS: + return flash_sector_query(softc,(flash_sector_t *) buffer->buf_ptr); + + + case IOCTL_FLASH_ERASE_RANGE: + return flash_erase_range(softc,(flash_range_t *) buffer->buf_ptr); + + case IOCTL_NVRAM_UNLOCK: + softc->flashdrv_unlocked = TRUE; + break; + + default: + return -1; + } + + return -1; +} + + +/* ********************************************************************* + * flashdrv_close(ctx) + * + * Close the flash device. + * + * Input parameters: + * ctx - device context + * + * Return value: + * 0 + ********************************************************************* */ +static int flashdrv_close(cfe_devctx_t *ctx) +{ + /* flashdrv_t *softc = ctx->dev_softc; */ + + /* XXX Invalidate the cache */ + + return 0; +} + + |