/* ********************************************************************* * 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; }