summaryrefslogtreecommitdiffstats
path: root/cfe/cfe/dev/dev_newflash.c
diff options
context:
space:
mode:
Diffstat (limited to 'cfe/cfe/dev/dev_newflash.c')
-rw-r--r--cfe/cfe/dev/dev_newflash.c1428
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,&sector);
+ 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,&sector);
+ 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(&sector,&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;
+}
+
+