diff options
Diffstat (limited to 'cfe/cfe/dev/dev_newflash.c')
-rw-r--r-- | cfe/cfe/dev/dev_newflash.c | 1428 |
1 files changed, 1428 insertions, 0 deletions
diff --git a/cfe/cfe/dev/dev_newflash.c b/cfe/cfe/dev/dev_newflash.c new file mode 100644 index 0000000..d196e92 --- /dev/null +++ b/cfe/cfe/dev/dev_newflash.c @@ -0,0 +1,1428 @@ +/* ********************************************************************* + * Broadcom Common Firmware Environment (CFE) + * + * "New" Flash device driver File: dev_newflash.c + * + * This driver supports various types of flash + * parts. + * + * 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 "bsp_config.h" +#include "dev_newflash.h" + +/* ********************************************************************* + * Macros + ********************************************************************* */ + +#define GETCFIBYTE(arr,x) (arr[(x)*2]) + +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define max(a,b) ((a) > (b) ? (a) : (b)) + +/* + * Get the address of the flash sector buffer from the + * config file. Addresses are PHYSICAL. + */ + +#ifndef CFG_FLASH_SECTOR_BUFFER_ADDR +#define CFG_FLASH_SECTOR_BUFFER_ADDR (100*1024*1024-128*1024) +#endif + +#ifndef CFG_FLASH_SECTOR_BUFFER_SIZE +#define CFG_FLASH_SECTOR_BUFFER_SIZE (128*1024) +#endif + + +/*#define _NEWFLASH_DEBUG_ */ + +/* ********************************************************************* + * 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 newflashdrv = { + "New CFI flash", + "flash", + CFE_DEV_FLASH, + &flashdrv_dispatch, + flashdrv_probe +}; + + + +/* ********************************************************************* + * Externs + ********************************************************************* */ + +extern void *flashop_engine_ptr; +extern int flashop_engine_len; + +extern void _cfe_flushcache(int); + +static int flash_sector_query(flashdev_t *softc,flash_sector_t *sector); + +/* ********************************************************************* + * Globals + ********************************************************************* */ + +/* + * This is a pointer to a DRAM version of our flash subroutines. + * We make a global here so that it doesn't get copied multiple + * times for each flash we instantiate. + */ + +static int (*flashop_engine_ram)(flashinstr_t *prog) = NULL; +static uint8_t *flash_sector_buffer = NULL; + +/* ********************************************************************* + * FLASH_OP_BEGIN(softc) + * + * Reset the pointer to the flash operations so that we can + * begin filling in new instructions to execute + * + * Input parameters: + * softc - our softc. + * + * Return value: + * nothing + ********************************************************************* */ + +#define flash_op_begin(softc) softc->fd_iptr = 0; + +/* ********************************************************************* + * FLASH_OP_ADD(softc,op,dest,src,cnt) + * + * Add an instruction to the flashop table + * + * Input parameters: + * softc - our flash + * op,dest,src,cnt - data for the opcode + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_op_add(flashdev_t *softc,long base,long op,long dest,long src,long cnt) +{ + flashinstr_t *fi = &(softc->fd_inst[softc->fd_iptr]); + + fi->fi_op = op; + fi->fi_base = base; + fi->fi_dest = dest; + fi->fi_src = src; + fi->fi_cnt = cnt; + + softc->fd_iptr++; +} + + +/* ********************************************************************* + * FLASH_OP_EXECUTE(softc) + * + * Execute the stored "flash operations" + * + * Input parameters: + * softc - our flash + * + * Return value: + * 0 if ok, else # of failures (less than zero) + ********************************************************************* */ + +static int flash_op_execute(flashdev_t *softc) +{ + flash_op_add(softc,softc->fd_probe.flash_phys,FEOP_RETURN,0,0,0); + +#ifdef _NEWFLASH_DEBUG_ + if (1) { + int idx; + printf("---------------\nCalling engine @ %08X\n",flashop_engine_ram); + for (idx = 0; idx < softc->fd_iptr; idx++) { + printf("%2d %08X %08X %08X %08X\n", + softc->fd_inst[idx].fi_op, + softc->fd_inst[idx].fi_base, + softc->fd_inst[idx].fi_dest, + softc->fd_inst[idx].fi_src, + softc->fd_inst[idx].fi_cnt); + } + } +#endif + + /* + * If someone hooked the flashop engine, call the hook. + */ + if (softc->fd_probe.flash_engine_hook) { + return (*(softc->fd_probe.flash_engine_hook))(&(softc->fd_inst[0])); + } + + /* + * Otherwise, call the standard one. + */ + if (!flashop_engine_ram) return CFE_ERR_UNSUPPORTED; + return (*flashop_engine_ram)(&(softc->fd_inst[0])); +} + + +/* ********************************************************************* + * FLASH_SETUP_ENGINE() + * + * Set up the "flash engine", copying the routine to DRAM + * and flushing the cache so we can call it. + * + * Input parameters: + * nothing + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_setup_engine(void) +{ +#if ((CFG_RAMAPP) || (CFG_EMBEDDED_PIC)) + /* CFE is relocated, no need to copy flash engine to heap */ + flashop_engine_ram = (void *) flashop_engine_ptr; +#else + /* Copy flash engine to heap */ + uint32_t *dst,*src; + int idx; + + if (flashop_engine_ram) return; /* already done */ + + /* + * Allocate space for engine + */ + + flashop_engine_ram = (void *) KMALLOC(flashop_engine_len,0); + if (!flashop_engine_ram) return; + + /* + * Copy engine to RAM - do it 32-bits at a time to avoid + * a certain platform with broken byte reads (no, not the 1250) + */ + + dst = (uint32_t *) flashop_engine_ram; + src = (uint32_t *) flashop_engine_ptr; + for (idx = 0; idx < flashop_engine_len/sizeof(uint32_t); idx++) { + *dst++ = *src++; + } + + /* + * Flush the d-cache, invalidate the I-cache. + */ + + _cfe_flushcache(1); + _cfe_flushcache(2); +#endif +} + +/* ********************************************************************* + * 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(flashdev_t *softc,flash_range_t *range) +{ + flash_sector_t sector; + int res; + + if (softc->fd_probe.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + if (range->range_base+range->range_length > softc->fd_probe.flash_size) { + 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))) { + + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + softc->fd_erasefunc, + sector.flash_sector_offset, + 0,0); + res = flash_op_execute(softc); + + 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(flashdev_t *softc) +{ + flash_range_t range; + + range.range_base = 0; + range.range_length = softc->fd_probe.flash_size * + softc->fd_probe.flash_nchips; + + return flash_erase_range(softc,&range); +} + + +/* ********************************************************************* + * flash_range_intersection(sector,inrange,outrange) + * + * Compute the intersection between a flash range and a + * sector. + * + * Input parameters: + * sector - sector to examine + * range - range we are checking + * outrange - where to put resulting intersection range + * + * Return value: + * 1 - range is an entire sector + * 0 - range is a partial sector + * -1 - range has no intersection + ********************************************************************* */ + +static int flash_range_intersection(flash_sector_t *sector, + flash_range_t *inrange, + flash_range_t *outrange) +{ + int start,end; + + /* + * compute the start and end pointers + */ + + start = (int) (max(sector->flash_sector_offset, + inrange->range_base)); + + end = (int) (min((sector->flash_sector_offset+sector->flash_sector_size), + (inrange->range_base+inrange->range_length))); + + /* + * if the end is in the right place wrt the start, + * there is an intersection. + */ + + if (end > start) { + outrange->range_base = (unsigned int) start; + outrange->range_length = (unsigned int) (end-start); + + if ((sector->flash_sector_offset == outrange->range_base) && + (sector->flash_sector_size == outrange->range_length)) { + return 1; /* instersection: entire sector */ + } + else { + return 0; /* intersection: partial sector */ + } + } + else { + outrange->range_base = (unsigned int) start; + outrange->range_length = 0; + return -1; /* intersection: none */ + } +} + + +/* ********************************************************************* + * 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(flashdev_t *softc,flash_sector_t *sector) +{ + int idx; + int nblks; + int blksiz; + unsigned int offset; + int whichchip; + int secidx; + int curblk; + + if (softc->fd_probe.flash_type != FLASH_TYPE_FLASH) { + return CFE_ERR_UNSUPPORTED; + } + + if (softc->fd_probe.flash_nsectors == 0) { + return CFE_ERR_UNSUPPORTED; + } + + /* Figure out which chip */ + whichchip = sector->flash_sector_idx / softc->fd_ttlsect; + if (whichchip >= softc->fd_probe.flash_nchips) { + sector->flash_sector_status = FLASH_SECTOR_INVALID; + return 0; + } + + /* Within that chip, get sector info */ + offset = softc->fd_probe.flash_size * whichchip; + secidx = sector->flash_sector_idx % softc->fd_ttlsect; + curblk = 0; + + for (idx = 0; idx < softc->fd_probe.flash_nsectors; idx++) { + nblks = FLASH_SECTOR_NBLKS(softc->fd_probe.flash_sectors[idx]); + blksiz = FLASH_SECTOR_SIZE(softc->fd_probe.flash_sectors[idx]); + if (secidx < curblk+nblks) { + sector->flash_sector_status = FLASH_SECTOR_OK; + sector->flash_sector_offset = + offset + (secidx-curblk)*blksiz; + sector->flash_sector_size = blksiz; + break; + } + + offset += (nblks)*blksiz; + curblk += nblks; + } + + + if (idx == softc->fd_probe.flash_nsectors) { + sector->flash_sector_status = FLASH_SECTOR_INVALID; + } + + return 0; +} + + +/* ********************************************************************* + * FLASH_SET_CMDSET(softc,cmdset,bus16,dev16) + * + * Set the command-set that we'll honor for this flash. + * + * Input parameters: + * softc - our flash + * cmdset - FLASH_CFI_CMDSET_xxx + * bus16 - true if bus is 16 bits wide + * dev16 - true if device supports 16-bit operation + * + * So: bus16 && dev16 -> 16-bit commands + * !bus16 && dev16 -> 8-bit commands to 16-bit flash with BYTE# + * !bus16 && !dev16 -> 8-bit commands + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_set_cmdset(flashdev_t *softc,int cmdset,int bus16,int dev16) +{ + switch (cmdset) { +#if (FLASH_DRIVERS & FLASH_DRIVER_INTEL) + case FLASH_CFI_CMDSET_INTEL_ECS: + case FLASH_CFI_CMDSET_INTEL_STD: + if (bus16) { + softc->fd_erasefunc = FEOP_INTEL_ERASE16; + softc->fd_pgmfunc = FEOP_INTEL_PGM16; + softc->fd_readfunc = FEOP_READ16; + } + else { + softc->fd_erasefunc = FEOP_INTEL_ERASE8; + softc->fd_pgmfunc = FEOP_INTEL_PGM8; + softc->fd_readfunc = FEOP_READ8; + } + break; +#endif +#if (FLASH_DRIVERS & FLASH_DRIVER_AMD) + case FLASH_CFI_CMDSET_AMD_STD: + case FLASH_CFI_CMDSET_AMD_ECS: + if (!bus16 && !dev16) { /* 8-bit bus, 8-bit flash */ + softc->fd_erasefunc = FEOP_AMD_ERASE8; + softc->fd_pgmfunc = FEOP_AMD_PGM8; + softc->fd_readfunc = FEOP_READ8; + } + else if (bus16 && dev16) { /* 16-bit bus, 16-bit flash */ + softc->fd_erasefunc = FEOP_AMD_ERASE16; + softc->fd_pgmfunc = FEOP_AMD_PGM16; + softc->fd_readfunc = FEOP_READ16; + } + else { /* 8-bit bus, 16-bit flash w/BYTE# */ + softc->fd_erasefunc = FEOP_AMD_ERASE16B; + softc->fd_pgmfunc = FEOP_AMD_PGM16B; + softc->fd_readfunc = FEOP_READ8; + } + break; +#endif + default: + /* we don't understand the command set - treat it like ROM */ + softc->fd_erasefunc = FEOP_RETURN; + softc->fd_pgmfunc = FEOP_RETURN; + softc->fd_readfunc = bus16 ? FEOP_READ16 : FEOP_READ8; + break; + } +} + +#if (FLASH_DRIVERS & FLASH_DRIVER_CFI) +/* ********************************************************************* + * 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(flashdev_t *softc) +{ + uint8_t cfidata[FLASH_MAX_CFIDATA]; + unsigned int cmdset; + unsigned int devif; + int bus16 = 0; + int dev16 = 0; + int idx; + int found = 0; + int regcnt; + int nblks; + int blksiz; + int insane = 0; + uint8_t insaner = 0; + + if (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) { + bus16 = 1; + } + + /* + * Do a CFI query (16-bit) + */ + + idx = FEOP_CFIQUERY8; + if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) { + idx = (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) ? + FEOP_CFIQUERY16 : FEOP_CFIQUERY16B; + } + + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + idx,(long)cfidata,0,FLASH_MAX_CFIDATA); + flash_op_execute(softc); + + /* + * Look for signature. + */ + + + if ((GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+0) == 'Q') && + (GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+1) == 'R') && + (GETCFIBYTE(cfidata,FLASH_CFI_SIGNATURE+2) == 'Y')) { + found = 1; + } + + + /* + * No CFI, bail. First, set the command set to an invalid + * value so that we'll use default routines to read but not do programming + */ + + if (!found) { + flash_set_cmdset(softc,-1,bus16,dev16); + return CFE_ERR_UNSUPPORTED; + } + + softc->fd_probe.flash_type = FLASH_TYPE_FLASH; + + /* + * Gather info from flash + */ + + cmdset = ((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_COMMAND_SET))) + + (((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_COMMAND_SET+1))) << 8); + + + devif = ((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_DEVICE_INTERFACE))) + + (((unsigned int) (GETCFIBYTE(cfidata,FLASH_CFI_DEVICE_INTERFACE+1))) << 8); + + + softc->fd_probe.flash_size = (1 << (unsigned int)(GETCFIBYTE(cfidata,FLASH_CFI_DEVICE_SIZE))); + + /* + * It's a 16-bit device if it is either always 16 bits or can be. + * we'll use "bus16" to decide if the BYTE# pin was strapped + */ + + dev16 = 0; + if ((devif == FLASH_CFI_DEVIF_X16) || (devif == FLASH_CFI_DEVIF_X8X16)) dev16 = 1; + + regcnt = GETCFIBYTE(cfidata,FLASH_CFI_REGION_COUNT); + + softc->fd_probe.flash_nsectors = regcnt; + +#if 1 + /* + * Hiss! Some AMD top-boot flash parts have broken CFI tables - they are backwards! + * Do some extra probing to find it. + */ + + if (cmdset == FLASH_CFI_CMDSET_AMD_STD) { + idx = FEOP_AMD_DEVCODE8; + if (softc->fd_probe.flash_flags & FLASH_FLG_DEV16) { + idx = (softc->fd_probe.flash_flags & FLASH_FLG_BUS16) ? + FEOP_AMD_DEVCODE16 : FEOP_AMD_DEVCODE16B; + } + + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + idx,(long)&insaner,0,0); + flash_op_execute(softc); +#ifdef _NEWFLASH_DEBUG_ + xprintf("Insaner = 0x%x\n", insaner); +#endif /* _NEWFLASH_DEBUG_ */ + if(((insaner & 0xFF) == 0xC4)||((insaner & 0xFF) == 0xF6)){ + insane = 1; +#ifdef _NEWFLASH_DEBUG_ + xprintf("Warning: insane AMD part, backwards CFI table!\n"); +#endif /* _NEWFLASH_DEBUG_ */ + } + } +#else + insane = insaner = 1; +#endif /* 0 */ + + + for (idx = 0; idx < regcnt; idx++) { + nblks = ((int)GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+0+idx*4) + + (int)(GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+1+idx*4)<<8)) + 1; + blksiz = ((int)GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+2+idx*4) + + (int)(GETCFIBYTE(cfidata,FLASH_CFI_REGION_TABLE+3+idx*4)<<8)) * 256; + + if (insane) { + /* Insane */ + softc->fd_probe.flash_sectors[((regcnt-1)-idx)] = + FLASH_SECTOR_RANGE(nblks,blksiz); + } + else { + /* Sane */ + softc->fd_probe.flash_sectors[idx] = + FLASH_SECTOR_RANGE(nblks,blksiz); + } + } + + /* + * Set the command set we're going to use. + */ + + flash_set_cmdset(softc,cmdset,bus16,dev16); + + return 0; + +} + + + +/* ********************************************************************* + * FLASH_DO_PROBE(softc) + * + * Probe to see if we're ROM or RAM. If ROM, see if we're flash. + * If flash, do CFI query. + * + * Input parameters: + * softc - our structure + * + * Return value: + * FLASH_TYPE_xxx + ********************************************************************* */ +static int flash_do_probe(flashdev_t *softc) +{ + uint8_t test_byte0,test_byte1; + uint8_t save0,save1; + volatile uint8_t *ptr; + + /* + * flash_do_probe is called before we open the device, so we + * need to allocate space for instructions so the flashop + * engine will work. + */ + + softc->fd_inst = KMALLOC(FLASH_MAX_INST*sizeof(flashinstr_t),0); + if (!softc->fd_inst) return FLASH_TYPE_ROM; + + /* + * 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->fd_probe.flash_phys); + save0 = *ptr; /* save old value */ + save1 = *(ptr+1); /* save old value */ + + test_byte0 = (save0 != 0x88) ? 0x88 : 0x89; + test_byte1 = (save1 != 0x99) ? 0x99 : 0x91; + + *(ptr) = test_byte0; + *(ptr+1) = test_byte1; + + if ((*ptr == test_byte0) && (*(ptr+1) == test_byte1)) { + softc->fd_probe.flash_type = FLASH_TYPE_SRAM; + + /*Only write back saved values if it's RAM*/ + *(ptr) = save0; + *(ptr+1) = save1; + +#ifdef _NEWFLASH_DEBUG_ + xprintf("Flash type SRAM\n"); +#endif + + } + else { + softc->fd_probe.flash_type = FLASH_TYPE_ROM; + +#ifdef _NEWFLASH_DEBUG_ + xprintf("Flash type ROM\n"); +#endif + + } + + /* + * 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 (softc->fd_probe.flash_type == FLASH_TYPE_ROM) { + flash_cfi_probe(softc); + } + + + KFREE(softc->fd_inst); + softc->fd_inst = NULL; + + return softc->fd_probe.flash_type; +} + +#endif /* (FLASH_DRIVERS & FLASH_DRIVER_CFI) */ + + +/* ********************************************************************* + * flash_do_parts(probe,parts) + * + * Partition the flash into the sizes specified. We use + * the sizes in the table to generate a table of {offset,size} + * pairs that eventually become partitions. + * + * The only thing magical about this is that size "0" means + * "fill to max" and that partitions beyond the "0" are aligned + * to the top of the flash. Therefore, if you had a 4MB + * flash and listed four partitions, 512K, 0, 512K, 512K, + * then there would be a 2.5M partition in the middle and two + * 512K partitions at the top. + * + * Input parameters: + * probe - flash probe data (user-supplied table) + * parts - our partition table (output) + * + * Return value: + * nothing + ********************************************************************* */ + +static void flash_do_parts(flashdev_t *softc) +{ + int idx; + int middlepart = -1; + int lobound = 0; + newflash_probe_t *probe = &(softc->fd_probe); + flashpart_t *parts = &(softc->fd_parts[0]); + int hibound = probe->flash_size*probe->flash_nchips; + + for (idx = 0; idx < probe->flash_nparts; idx++) { + if (probe->flash_parts[idx].fp_size == 0) { + middlepart = idx; + break; + } + parts[idx].fp_offset = lobound; + parts[idx].fp_size = probe->flash_parts[idx].fp_size; + lobound += probe->flash_parts[idx].fp_size; + } + + if (idx != probe->flash_nparts) { + for (idx = probe->flash_nparts - 1; idx > middlepart; + idx--) { + parts[idx].fp_size = probe->flash_parts[idx].fp_size; + hibound -= probe->flash_parts[idx].fp_size; + parts[idx].fp_offset = hibound; + } + } + + if (middlepart != -1) { + parts[middlepart].fp_offset = lobound; + parts[middlepart].fp_size = hibound - lobound; + } + +#ifdef _NEWFLASH_DEBUG_ + printf("Partition information:\n"); + for (idx = 0; idx < probe->flash_nparts;idx++) { + printf("#%02d %08X -> %08X (%d)\n",idx, + parts[idx].fp_offset,parts[idx].fp_offset+parts[idx].fp_size-1, + parts[idx].fp_size); + } +#endif +} + + +/* ********************************************************************* + * flashdrv_allocbuf(dev) + * + * Allocate sector buffer for flash programming. Use a global + * buffer for all devices. + * + * Input parameters: + * dev - our device + * + * Return value: + * nothing + ********************************************************************* */ +static void flashdrv_allocbuf(flashdev_t *softc) +{ + if (!flash_sector_buffer) { +#if CFG_FLASH_ALLOC_SECTOR_BUFFER + flash_sector_buffer = KMALLOC(CFG_FLASH_SECTOR_BUFFER_SIZE,0); + if (!flash_sector_buffer) { + printf("FLASH: Could not allocate sector buffer, using default\n"); + flash_sector_buffer = (uint8_t *) KERNADDR(CFG_FLASH_SECTOR_BUFFER_SIZE); + } +#else + flash_sector_buffer = (uint8_t *) KERNADDR(CFG_FLASH_SECTOR_BUFFER_ADDR); +#endif + } + + softc->fd_sectorbuffer = flash_sector_buffer; +} + +/* ********************************************************************* + * 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) +{ + flashdev_t *softc; + newflash_probe_t *probe; + int idx; + char descr[80]; + static int flashidx = 0; + char *x; + + /* + * First thing to do is configure the flashop engine + * if not already done. Basically we copy a bit of + * position-independent code into the DRAM. + */ + + flash_setup_engine(); + + /* + * Now, on with the probing. + */ + + probe = (newflash_probe_t *) probe_ptr; + + /* + * probe_a is the flash base address + * probe_b is the size of the flash + * probe_ptr is unused. + */ + + softc = (flashdev_t *) KMALLOC(sizeof(flashdev_t),0); + if (softc) { + memset(softc,0,sizeof(flashdev_t)); + + flashdrv_allocbuf(softc); + + if (probe) { + /* + * Passed probe structure, do fancy stuff + */ + memcpy(&(softc->fd_probe),probe,sizeof(newflash_probe_t)); + if (softc->fd_probe.flash_nchips == 0) { + softc->fd_probe.flash_nchips = 1; + } + } + else { + /* Didn't pass probe structure, do the compatible thing */ + softc->fd_probe.flash_phys = probe_a; + softc->fd_probe.flash_size = (probe_b & FLASH_SIZE_MASK); + softc->fd_probe.flash_flags = (probe_b & FLASH_FLG_MASK); + softc->fd_probe.flash_nchips = 1; + } + + if (softc->fd_probe.flash_flags & FLASH_FLG_MANUAL) { + /* Manual probing, just set the command set. */ + flash_set_cmdset(softc,softc->fd_probe.flash_cmdset, + ((softc->fd_probe.flash_flags & FLASH_FLG_BUS16) ? 1 : 0), + ((softc->fd_probe.flash_flags & FLASH_FLG_DEV16) ? 1 : 0)); + } + else { + /* Do automatic probing */ +#if (FLASH_DRIVERS & FLASH_DRIVER_CFI) + flash_do_probe(softc); +#else + return; /* No automatic probing, bail! */ +#endif + } + + /* Remember total size of all devices */ + softc->fd_ttlsize = softc->fd_probe.flash_nchips * softc->fd_probe.flash_size; + + /* Set description */ + x = descr; + x += xsprintf(x,"%s at %08X size %uKB",drv->drv_description, + softc->fd_probe.flash_phys, + softc->fd_ttlsize/1024); + if (softc->fd_probe.flash_nchips > 1) { + xsprintf(x," (%d chips)",softc->fd_probe.flash_nchips); + } + + /* + * If flash is not partitioned, just instantiate one + * device. Otherwise, instantiate multiple flashes + * to cover the entire device. + */ + + if (softc->fd_probe.flash_nparts == 0) { + softc->fd_parts[0].fp_dev = softc; + softc->fd_parts[0].fp_offset = 0; + softc->fd_parts[0].fp_size = softc->fd_probe.flash_size; + cfe_attach(drv,&(softc->fd_parts[0]),NULL,descr); + } + else { + /* + * Partition flash into chunks + */ + flash_do_parts(softc); + + /* + * Instantiate devices for each piece + */ + + for (idx = 0; idx < softc->fd_probe.flash_nparts; idx++) { + char name[32]; + char *nptr; + + xsprintf(descr,"%s at %08X offset %08X size %uKB", + drv->drv_description, + softc->fd_probe.flash_phys, + softc->fd_parts[idx].fp_offset, + (softc->fd_parts[idx].fp_size+1023)/1024); + + softc->fd_parts[idx].fp_dev = softc; + if (softc->fd_probe.flash_parts[idx].fp_name == NULL) { + sprintf(name,"%d",idx); + nptr = name; + } + else { + nptr = softc->fd_probe.flash_parts[idx].fp_name; + } + cfe_attach_idx(drv, + flashidx, + &(softc->fd_parts[idx]), + nptr, + descr); + } + } + + flashidx++; + + /* Count total sectors on the device */ + + softc->fd_ttlsect = 0; + for (idx = 0; idx < softc->fd_probe.flash_nsectors; idx++) { + softc->fd_ttlsect += FLASH_SECTOR_NBLKS(softc->fd_probe.flash_sectors[idx]); + } + + } + +} + + +/* ********************************************************************* + * 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) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + int ttlsect = softc->fd_ttlsect; + + /* + * Calculate number of flashop instructions we'll need at most. + * This will be two for each sector plus two more for the first + * and last sectors, plus two extra + */ + + ttlsect = (ttlsect * 2 * softc->fd_probe.flash_nchips) + 6; + + /* + * Allocate memory for instructions. + */ + +#ifdef _NEWFLASH_DEBUG_ + printf("%s: allocating %d instructions\n",cfe_device_name(ctx),ttlsect); +#endif + + softc->fd_inst = KMALLOC(ttlsect*sizeof(flashinstr_t),0); + if (!softc->fd_inst) return CFE_ERR_NOMEM; + + 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) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + int blen; + int offset; + + blen = buffer->buf_length; + offset = (long)buffer->buf_offset; + + if ((offset + blen) > part->fp_size) { + blen = part->fp_size - offset; + } + + offset += part->fp_offset; + + if (blen > 0) { + flash_op_begin(softc); + flash_op_add(softc,softc->fd_probe.flash_phys, + softc->fd_readfunc,(long)buffer->buf_ptr,offset,blen); + flash_op_execute(softc); + } + + 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) +{ + inpstat->inp_status = 1; + return 0; +} + + +/* ********************************************************************* + * 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_write2(cfe_devctx_t *ctx,iocb_buffer_t *buffer,int reboot) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + int blen; + int res; + int offset; + int whichchip; + long chipbase; + flash_range_t outrange; + flash_range_t inrange; + flash_sector_t sector; + + blen = buffer->buf_length; + offset = (long)buffer->buf_offset; + + /* Compute range within physical flash */ + + if ((offset + blen) > part->fp_size) { + blen = part->fp_size - offset; + } + + offset += part->fp_offset; + + /* Handle case of writing nothing */ + + if (blen == 0) { + buffer->buf_retlen = blen; + return (buffer->buf_length == blen) ? 0 : CFE_ERR_IOERR; + } + + /* now, offset/blen forms the range we want to write to. */ + + inrange.range_base = offset; + inrange.range_length = blen; + + sector.flash_sector_idx = 0; + + flash_op_begin(softc); + + for (;;) { + res = flash_sector_query(softc,§or); + if (res != 0) break; + if (sector.flash_sector_status == FLASH_SECTOR_INVALID) { + break; + } + + whichchip = sector.flash_sector_idx / softc->fd_ttlsect; + chipbase = softc->fd_probe.flash_phys + + (long) (whichchip * softc->fd_probe.flash_size); + + res = flash_range_intersection(§or,&inrange,&outrange); + + switch (res) { + case 1: /* Erease/program entire sector */ + flash_op_add(softc,chipbase, + softc->fd_erasefunc, + sector.flash_sector_offset, + 0,0); + flash_op_add(softc,chipbase, + softc->fd_pgmfunc, + outrange.range_base, + ((long)buffer->buf_ptr)+(outrange.range_base-inrange.range_base), + outrange.range_length); + break; + + case 0: /* Erase/reprogram partial sector */ + /* Save old sector */ + flash_op_add(softc,chipbase, + softc->fd_readfunc, + (long)(softc->fd_sectorbuffer), + sector.flash_sector_offset, + sector.flash_sector_size); + /* Copy in new stuff */ + flash_op_add(softc,chipbase, + FEOP_MEMCPY, + ((long)(softc->fd_sectorbuffer))+(outrange.range_base-sector.flash_sector_offset), + ((long)buffer->buf_ptr)+(outrange.range_base-inrange.range_base), + outrange.range_length); + /* Erase sector */ + flash_op_add(softc,chipbase, + softc->fd_erasefunc, + sector.flash_sector_offset, + 0,0); + /* Program sector */ + flash_op_add(softc,chipbase, + softc->fd_pgmfunc, + sector.flash_sector_offset, + (long)(softc->fd_sectorbuffer), + sector.flash_sector_size); + break; + + case -1: /* No intersection */ + break; + } + + sector.flash_sector_idx++; + + } + + if (reboot) { + flash_op_add(softc,softc->fd_probe.flash_phys,FEOP_REBOOT,0,0,0); + } + + res = flash_op_execute(softc); + + buffer->buf_retlen = blen; + + return (res == 0) ? 0 : CFE_ERR_IOERR; +} + +static int flashdrv_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) +{ + return flashdrv_write2(ctx,buffer,0); +} + + +/* ********************************************************************* + * 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) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + nvram_info_t *nvinfo; + flash_info_t *info; + flash_range_t range; + int offset; + + switch ((int)buffer->buf_ioctlcmd) { + case IOCTL_NVRAM_GETINFO: + /* + * We only support NVRAM on flashes that have been partitioned + * into at least two partitions. Every partition supports + * being an NVRAM in that case, but we'll only attach one + * of them to the environment subsystem. + */ + if (softc->fd_probe.flash_nparts <= 1) { + return CFE_ERR_UNSUPPORTED; + } + nvinfo = (nvram_info_t *) buffer->buf_ptr; + if (buffer->buf_length != sizeof(nvram_info_t)) return CFE_ERR_INV_PARAM; + + nvinfo->nvram_offset = 0; + nvinfo->nvram_size = part->fp_size; + nvinfo->nvram_eraseflg = 1; + buffer->buf_retlen = sizeof(nvram_info_t); + return 0; + break; + + case IOCTL_FLASH_ERASE_SECTOR: + offset = (int) buffer->buf_offset; + offset += part->fp_offset; + if (offset >= softc->fd_probe.flash_size) return -1; + + flash_op_begin(softc); + flash_op_add(softc, + softc->fd_probe.flash_phys, + softc->fd_erasefunc, + offset, + 0,0); + flash_op_execute(softc); + 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: + /* Write file and reboot */ + flashdrv_write2(ctx,buffer,1); + return -1; /* should not return */ + + case IOCTL_FLASH_GETINFO: + info = (flash_info_t *) buffer->buf_ptr; + info->flash_base = softc->fd_probe.flash_phys; + info->flash_size = softc->fd_probe.flash_size; + info->flash_type = softc->fd_probe.flash_type; + info->flash_flags = FLASH_FLAG_NOERASE; + return 0; + + case IOCTL_FLASH_GETSECTORS: + return flash_sector_query(softc,(flash_sector_t *) buffer->buf_ptr); + + case IOCTL_FLASH_ERASE_RANGE: + memcpy(&range,buffer->buf_ptr,sizeof(flash_range_t)); + range.range_base += part->fp_offset; + if (range.range_length > part->fp_size) { + range.range_length = part->fp_size; + } + return flash_erase_range(softc,&range); + + default: + /* Call hook if present. */ + if (softc->fd_probe.flash_ioctl_hook) { + return (*(softc->fd_probe.flash_ioctl_hook))(ctx,buffer); + } + 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) +{ + flashpart_t *part = ctx->dev_softc; + flashdev_t *softc = part->fp_dev; + + if (softc->fd_inst) { + KFREE(softc->fd_inst); + } + + softc->fd_inst = NULL; + + /* XXX Invalidate the cache ?!?! */ + + return 0; +} + + |