FreeCalypso > hg > fc-magnetite
diff src/cs/drivers/drv_app/ffs/board/drv.c @ 0:945cf7f506b2
src/cs: chipsetsw import from tcs211-fcmodem
binary blobs and LCD demo files have been excluded,
all line endings are LF only
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 25 Sep 2016 22:50:11 +0000 |
parents | |
children | 204d6866901b |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cs/drivers/drv_app/ffs/board/drv.c Sun Sep 25 22:50:11 2016 +0000 @@ -0,0 +1,1452 @@ +/****************************************************************************** + * Flash File System (ffs) + * Idea, design and coding by Mads Meisner-Jensen, mmj@ti.com + * + * ffs low level flash driver + * + * $Id: drv.c 1.30.1.6.1.51.1.1.1.13.1.11 Tue, 06 Jan 2004 14:36:52 +0100 tsj $ + * + ******************************************************************************/ + +#ifndef TARGET +#include "ffs.cfg" +#endif + +#include "ffs/ffs.h" +#include "ffs/board/drv.h" +#include "ffs/board/ffstrace.h" + +#if (TARGET == 0) + +#ifdef WIN32 +#include "windows.h" +#else //WIN32 +#include "sys/mman.h" +#include "unistd.h" +#endif //WIN32 + +#include "stdio.h" +#include "sys/types.h" +#include "sys/stat.h" +#include "fcntl.h" +#else +#include "nucleus.h" + +#endif + + +// Note that all code notes and comments pertaining to single-bank flash +// drivers are located in the AMD SB driver (amdsbdrv.c). Consider this as +// the reference. + + +/****************************************************************************** + * Globals + ******************************************************************************/ + +#if (TARGET == 1) +// NOTE: This is the size in bytes of the single-bank driver code that is +// copied to RAM. The only way to determine the amount of memory needed is +// to look into the linker output file (.map) or the assembler output of +// both amdsbdrv.obj and intelsbdrv.obj files. +#define FFSDRV_CODE_SIZE (0x200) + +uint8 ffsdrv_code[FFSDRV_CODE_SIZE]; + +#endif + +#define INTEL_UNLOCK_SLOW 1 + +struct dev_s dev; +struct ffsdrv_s ffsdrv; + +uint32 int_disable(void); +void int_enable(uint32 tmp); + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define addr2offset(address) ( (int) (address) - (int) dev.base ) + + +/****************************************************************************** + * Generic Driver Functions + ******************************************************************************/ + +void ffsdrv_generic_write(void *dst, const void *src, uint16 size) +{ + uint8 *mydst = dst; + const uint8 *mysrc = src; + + if (size > 0) + { + if ((unsigned int) mydst & 1) { + ffsdrv_write_byte(mydst++, *mysrc++); + size--; + } + while (size >= 2) { + ffsdrv.write_halfword((uint16 *) mydst, + mysrc[0] | (mysrc[1] << 8)); + size -= 2; + mysrc += 2; + mydst += 2; + } + if (size == 1) + ffsdrv_write_byte(mydst++, *mysrc++); + } +} + + +/****************************************************************************** + * AMD Single Bank Driver Functions + ******************************************************************************/ + +#if (TARGET == 1) + +// Forward declaration of functions in file amdsbdrv.c +void ffsdrv_ram_amd_sb_write_halfword(volatile uint16 *addr, uint16 value); +void ffsdrv_ram_amd_sb_erase(uint8 block); + +#else // (TARGET == 0) + +// On PC these functions are empty +void ffsdrv_ram_amd_sb_write_halfword(volatile uint16 *addr, uint16 value) {} +void ffsdrv_ram_amd_sb_erase(uint8 block) {} + +#endif // (TARGET == 1) + + +/****************************************************************************** + * AMD Pseudo Single Bank Driver Functions + ******************************************************************************/ + +// This is a pseudo single-bank flash driver. It simulates a single-bank +// flash device on a dual-bank device. + +#if (TARGET == 1) + +void ffsdrv_amd_pseudo_sb_write_halfword(volatile uint16 *addr, uint16 value) +{ + volatile char *flash = dev.base; + uint32 cpsr, i, x; + + ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value)); + + if (~*addr & value) { + ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value)); + return; + } + + cpsr = int_disable(); + tlw(led_on(LED_WRITE)); + + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + flash[0xAAAA] = 0xA0; + *addr = value; + + while ((*dev.addr ^ dev.data) & 0x80) + ; + + tlw(led_off(LED_WRITE)); + int_enable(cpsr); +} + +// This VERY simple way of erase suspending only works because we run under +// a pre-emptive operating system, so whenever an interrupt occurs, another +// task takes the CPU, and at the end of the interrupt, FFS gets the CPU +// again. +void ffsdrv_amd_pseudo_sb_erase(uint8 block) +{ + volatile char *flash = dev.base; + volatile char *addr; + uint32 cpsr; + uint16 flashpoll; + + addr = block2addr(block); + + ttw(ttr(TTrDrvErase, "e(%d)" NL, block)); + + cpsr = int_disable(); + tlw(led_on(LED_ERASE)); + + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + flash[0xAAAA] = 0x80; + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + *addr = 0x30; // AMD erase sector command + + // Wait for erase to finish. + while ((*addr & 0x80) == 0) { + tlw(led_toggle(LED_ERASE)); + // Poll interrupts, taking interrupt mask into account. + if (INT_REQUESTED) + { + // 1. suspend erase + // 2. enable interrupts + // .. now the interrupt code executes + // 3. disable interrupts + // 4. resume erase + + tlw(led_on(LED_ERASE_SUSPEND)); + *addr = 0xB0; + + // wait for erase suspend to finish + while ((*addr & 0x80) == 0) + ; + + tlw(led_off(LED_ERASE_SUSPEND)); + int_enable(cpsr); + + // Other interrupts and tasks run now... + + cpsr = int_disable(); + tlw(led_on(LED_ERASE_SUSPEND)); + + // Before resuming erase we must? check if the erase is really + // suspended or if it did finish + flashpoll = *addr; + *addr = 0x30; + + tlw(led_off(LED_ERASE_SUSPEND)); + } + } + + tlw(led_on(LED_ERASE)); + tlw(led_off(LED_ERASE)); + int_enable(cpsr); +} + +#else // (TARGET == 0) + +void ffsdrv_amd_pseudo_sb_write_halfword(volatile uint16 *addr, uint16 value) {} +void ffsdrv_amd_pseudo_sb_erase(uint8 block) {} + +#endif // (TARGET == 1) + + +/****************************************************************************** + * AMD Dual/Multi Bank Driver Functions + ******************************************************************************/ + +// All erase and write operations are performed atomically (interrupts +// disabled). Otherwise we cannot trust the value of dev.state and we cannot +// determine exactly how many of the command words have already been +// written. + +// in ffs_end() when we resume an erasure that was previously suspended, how +// does that affect multiple tasks doing that simultaneously? + +void ffsdrv_amd_write_end(void); +void ffsdrv_amd_erase_end(void); + +void ffsdrv_amd_write_halfword(volatile uint16 *addr, uint16 value) +{ + volatile char *flash = dev.base; + uint32 cpsr; + + tlw(led_on(LED_WRITE)); + ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value)); + + dev.addr = addr; + dev.data = value; + + if (~*addr & value) { + ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value)); + return; + } + + cpsr = int_disable(); + tlw(led_toggle(LED_WRITE_SUSPEND)); + dev.state = DEV_WRITE; + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + flash[0xAAAA] = 0xA0; + *addr = value; + int_enable(cpsr); + tlw(led_toggle(LED_WRITE_SUSPEND)); + + ffsdrv_amd_write_end(); +} + +void ffsdrv_amd_write(void *dst, const void *src, uint16 size) +{ + uint8 *mydst = dst; + const uint8 *mysrc = src; + + if (size > 0) + { + if ((unsigned int) mydst & 1) { + ffsdrv_write_byte(mydst++, *mysrc++); + size--; + } + while (size >= 2) { + ffsdrv_amd_write_halfword((uint16 *) mydst, + mysrc[0] | (mysrc[1] << 8)); + size -= 2; + mysrc += 2; + mydst += 2; + } + if (size == 1) + ffsdrv_write_byte(mydst++, *mysrc++); + } +} + +void ffsdrv_amd_write_end(void) +{ + while ((*dev.addr ^ dev.data) & 0x80) + tlw(led_toggle(LED_WRITE_SUSPEND)); + + dev.state = DEV_READ; + + tlw(led_off(LED_WRITE)); +} + +void ffsdrv_amd_erase(uint8 block) +{ + volatile char *flash = dev.base; + uint32 cpsr; + + tlw(led_on(LED_ERASE)); + ttw(ttr(TTrDrvErase, "e(%d)" NL, block)); + + dev.addr = (uint16 *) block2addr(block); + + cpsr = int_disable(); + dev.state = DEV_ERASE; + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + flash[0xAAAA] = 0x80; + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + *dev.addr = 0x30; // AMD erase sector command + int_enable(cpsr); + + ffsdrv_amd_erase_end(); +} + +void ffsdrv_amd_erase_end(void) +{ + while ((*dev.addr & 0x80) == 0) + ; + + dev.state = DEV_READ; + + tlw(led_off(LED_ERASE)); +} + +void ffsdrv_amd_erase_suspend(void) +{ + uint32 cpsr; + + tlw(led_on(LED_ERASE_SUSPEND)); + ttw(str(TTrDrvErase, "es" NL)); + + // if erase has finished then all is ok + if (*dev.addr & 0x80) { + ffsdrv_amd_erase_end(); + tlw(led_off(LED_ERASE_SUSPEND)); + return; + } + + // NOTEME: As there is no way to be absolutely certain that erase + // doesn't finish between last poll and the following erase suspend + // command, we assume that the erase suspend is safe even though the + // erase IS actually already finished. + + cpsr = int_disable(); + dev.state = DEV_ERASE_SUSPEND; + *dev.addr = 0xB0; + + // Wait for erase suspend to finish + while ((*dev.addr & 0x80) == 0) + ; + + int_enable(cpsr); +} + +void ffsdrv_amd_erase_resume(void) +{ + uint32 cpsr; + + ttw(str(TTrDrvErase, "er" NL)); + + // NOTEME: See note in erase_suspend()... We assume that the erase + // resume is safe even though the erase IS actually already finished. + cpsr = int_disable(); + dev.state = DEV_ERASE; + *dev.addr = 0x30; + int_enable(cpsr); + + tlw(led_off(LED_ERASE_SUSPEND)); +} + + +/****************************************************************************** + * SST Dual/Multi Bank Driver Functions + ******************************************************************************/ + +// SST flashes use almost same command set as AMD flashes. Only the command +// addresses (4 more bits) and erase command data (0x50 instead of 0x30) are +// different. SST flashes have no erase suspend/resume commands because they +// are so fast at erasing! + +void ffsdrv_sst_write_end(void); +void ffsdrv_sst_erase_end(void); + +void ffsdrv_sst_write(void *dst, const void *src, uint16 size) +{ + uint8 *mydst = dst; + const uint8 *mysrc = src; + + if (size > 0) + { + if ((unsigned int) mydst & 1) { + ffsdrv_write_byte(mydst++, *mysrc++); + size--; + } + while (size >= 2) { + ffsdrv_amd_write_halfword((uint16 *) mydst, + mysrc[0] | (mysrc[1] << 8)); + size -= 2; + mysrc += 2; + mydst += 2; + } + if (size == 1) + ffsdrv_write_byte(mydst++, *mysrc++); + } +} + +// Note that SST flashes have smaller sectors than other flash families. +// Fortunately they support erasure of several of these sectors in a logical +// unit called a "block". +void ffsdrv_sst_erase(uint8 block) +{ + volatile char *flash = dev.base; + uint32 cpsr; + + tlw(led_on(LED_ERASE)); + ttw(ttr(TTrDrvErase, "e(%d)" NL, block)); + + dev.addr = (uint16 *) block2addr(block); + + cpsr = int_disable(); + dev.state = DEV_ERASE; + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + flash[0xAAAA] = 0x80; + flash[0xAAAA] = 0xAA; // unlock cycle 1 + flash[0x5555] = 0x55; // unlock cycle 2 + *dev.addr = 0x50; // SST erase block command + int_enable(cpsr); + + ffsdrv_sst_erase_end(); +} + +void ffsdrv_sst_erase_end(void) +{ + // Wait for erase end + while ((*dev.addr & 0x80) == 0) + ; + + dev.state = DEV_READ; + + tlw(led_off(LED_ERASE)); +} + +// Erase suspend/resume commands do not exist for SST flashes, so we just +// poll for the end of the erase operation... + +void ffsdrv_sst_erase_suspend(void) +{ + ttw(str(TTrDrvErase, "es" NL)); + + ffsdrv_sst_erase_end(); +} + + +/****************************************************************************** + * Intel Single Bank Driver Functions + ******************************************************************************/ + +#if (TARGET == 1) + +// Forward declaration of functions in file intelsbdrv.c +int ffsdrv_ram_intel_sb_init(void); +void ffsdrv_ram_intel_sb_write_halfword(volatile uint16 *addr, uint16 value); +void ffsdrv_ram_intel_sb_erase(uint8 block); +void ffsdrv_ram_intel_erase(uint8 block); + +#else // (TARGET == 0) + +// On PC these functions are empty +void ffsdrv_ram_intel_sb_write_halfword(volatile uint16 *addr, uint16 value) {} +void ffsdrv_ram_intel_sb_erase(uint8 block) {} +void ffsdrv_ram_intel_erase(uint8 block) {} + +#endif // (TARGET == 1) + + +/****************************************************************************** + * Intel Dual/Multi Bank Driver Functions + ******************************************************************************/ + +void ffsdrv_intel_write_end(void); +void ffsdrv_intel_erase_end(void); + + +// ffsdrv_intel_write_halfword and ffsdrv_intel_write_end is not used +// because of the bug in the intel flash device. Instead is the functions +// ffsdrv_ram_intel_sb_write_halfword and ffsdrv_ram_intel_erase used. +void ffsdrv_intel_write_halfword(volatile uint16 *addr, uint16 value) +{ + uint32 cpsr; + + tlw(led_on(LED_WRITE)); + ttw(ttr(TTrDrvWrite, "wh(%x,%x)" NL, addr, value)); + + dev.addr = addr; + + if (~*addr & value) { + ttw(ttr(TTrFatal, "wh(%x,%x->%x) fatal" NL, addr, *addr, value)); + return; + } + + cpsr = int_disable(); + dev.state = DEV_WRITE; + +#if (INTEL_UNLOCK_SLOW == 1) + *addr = 0x60; // Intel Config setup + *addr = 0xD0; // Intel Unlock block + *addr = 0x50; // Intel Clear Status Register +#endif + + *addr = 0x40; // Intel program byte/word + *addr = value; + + int_enable(cpsr); + + ffsdrv_intel_write_end(); +} + +void ffsdrv_intel_write(void *dst, const void *src, uint16 size) +{ + uint8 *mydst = dst; + const uint8 *mysrc = src; + + if (size > 0) + { + if ((unsigned int) mydst & 1) { + ffsdrv_write_byte(mydst++, *mysrc++); + size--; + } + while (size >= 2) { + ffsdrv_intel_write_halfword((uint16 *) mydst, + mysrc[0] | (mysrc[1] << 8)); + size -= 2; + mysrc += 2; + mydst += 2; + } + if (size == 1) + ffsdrv_write_byte(mydst++, *mysrc++); + } +} + +void ffsdrv_intel_write_end(void) +{ + uint32 cpsr; + // We can be interrupted and reentered thus the state can have been changed. + while ((*dev.addr & 0x80) == 0 && dev.state == DEV_WRITE) + ; + + // The flash state and dev.state must be in sync thus the stat changes + // must be protect from being interrupted + cpsr = int_disable(); + *dev.addr = 0xFF; // Intel read array + dev.state = DEV_READ; + int_enable(cpsr); + + tlw(led_off(LED_WRITE)); +} + + +void ffsdrv_intel_erase(uint8 block) +{ + uint32 cpsr; + + ttw(ttr(TTrDrvErase, "e(%d)" NL, block)); + tlw(led_on(LED_ERASE)); + + dev.addr = (uint16 *) block2addr(block); + + cpsr = int_disable(); + dev.state = DEV_ERASE; + +#if (INTEL_UNLOCK_SLOW == 1) + *dev.addr = 0x60; // Intel Config setup + *dev.addr = 0xD0; // Intel Unlock block +#endif + + *dev.addr = 0x50; // Intel clear status register (not really necessary) + *dev.addr = 0x20; // Intel erase setup + *dev.addr = 0xD0; // Intel erase confirm + + int_enable(cpsr); + + ffsdrv_intel_erase_end(); +} + +void ffsdrv_intel_erase_end(void) +{ + while ((*dev.addr & 0x80) == 0 && dev.state == DEV_ERASE) + ; + + *dev.addr = 0xFF; // Intel read array + + dev.state = DEV_READ; + tlw(led_off(LED_ERASE)); +} + +void ffsdrv_intel_erase_suspend(void) +{ + uint32 cpsr; + uint16 poll; + + ttw(str(TTrDrvErase, "es" NL)); + tlw(led_on(LED_ERASE_SUSPEND)); + + cpsr = int_disable(); + dev.state = DEV_ERASE_SUSPEND; + *dev.addr = 0xB0; // Intel Erase Suspend + *dev.addr = 0x70; // Intel Read Status Register + while (((poll = *dev.addr) & 0x80) == 0) + ; + + if ((poll & 0x40) == 0) { + // Block erase has completed + tlw(led_off(LED_ERASE_SUSPEND)); + dev.state = DEV_READ; + tlw(led_off(LED_ERASE)); + } + *dev.addr = 0xFF; // Intel read array + int_enable(cpsr); +} + +void ffsdrv_intel_erase_resume(void) +{ + uint32 cpsr; + + ttw(str(TTrDrvErase, "er" NL)); + + cpsr = int_disable(); + dev.state = DEV_ERASE; + *dev.addr = 0xD0; // Intel erase resume + + // The following "extra" Read Status command is required because Intel + // has changed the specification of the W30 flash! (See "1.8 Volt Intel® + // Wireless Flash Memory with 3 Volt I/O 28F6408W30, 28F640W30, + // 28F320W30 Specification Update") + *dev.addr = 0x70; // Intel Read Status Register + + int_enable(cpsr); + + tlw(led_off(LED_ERASE_SUSPEND)); +} + + +/****************************************************************************** + * RAM Family Functions + ******************************************************************************/ + +void ffsdrv_ram_write_halfword(volatile uint16 *dst, uint16 value) +{ + *dst = value; +} + +void ffsdrv_ram_write(void *dst, const void *src, uint16 size) +{ + uint8 *mydst = dst; + const uint8 *mysrc = src; + + if (size == 0) + return; + else if (size == 1) + ffsdrv_write_byte(mydst, *mysrc); + else { + if ((int) mydst & 1) { + ffsdrv_write_byte(mydst++, *mysrc++); + size--; + } + while (size >= 2) { + ffsdrv_ram_write_halfword((uint16 *) mydst, mysrc[0]|(mysrc[1] << 8)); + size -= 2; + mysrc += 2; + mydst += 2; + } + if (size == 1) + ffsdrv_write_byte(mydst++, *mysrc++); + } +} + +void ffsdrv_ram_erase(uint8 block) +{ + int i; + char *addr; + + addr = block2addr(block); + + for (i = 0; i < (1 << dev.binfo[block].size_ld); i++) { + *addr++ = 0xFF; + } +} + +/****************************************************************************** + * Void Functions + ******************************************************************************/ + +int ffsdrv_null_init(void) +{ + ttw(ttr(TTrDrvOther, "ffsdrv_null_init()" NL)); + + return 0; +} + +void ffsdrv_null_erase(uint8 block) +{ + ttw(ttr(TTrDrvErase, "ffsdrv_null_erase(%d)" NL, block)); +} + +void ffsdrv_null_write_halfword(volatile uint16 *addr, uint16 value) +{ + ttw(ttr(TTrDrvWrite, "ffsdrv_null_write_halfword(0x%x, 0x%x)" NL, addr, value)); +} + +void ffsdrv_null_write(void *dst, const void *src, uint16 size) +{ + ttw(ttr(TTrDrvWrite, "ffsdrv_null_write(0x%x, 0x%x, %d)" NL, dst, src, size)); +} + +void ffsdrv_null_erase_suspend(void) +{ + ttw(str(TTrDrvErase, "ffsdrv_null_erase_suspend()" NL)); +} + +void ffsdrv_null_erase_resume(void) +{ + ttw(str(TTrDrvErase, "ffsdrv_null_erase_resume()" NL)); +} + +void ffsdrv_null_write_end(void) +{ + ttw(str(TTrDrvWrite, "ffsdrv_null_write_end()" NL)); +} + +void ffsdrv_null_erase_end(void) +{ + ttw(str(TTrDrvErase, "ffsdrv_null_erase_end()" NL)); +} + + +/****************************************************************************** + * Test Driver Functions + ******************************************************************************/ + +#if (TARGET == 0) + +static char *image_addr = 0; +static int image_size = 0; +#ifdef WIN32 +HANDLE image_fd, map_fd; +#else //WIN32 +static int image_fd = 0; +#endif //WIN32 + +extern int arg_removeimage; +extern char *arg_imagename; + +extern void test_fatal_printf(char *format, ...); + +void ffsdrv_write_check(char *addr, int size) +{ + offset_t offset, last; + + offset = addr2offset(addr); + last = dev.binfo[dev.numblocks-1].offset + + (1 << dev.binfo[dev.numblocks-1].size_ld); + + if (offset < 0 || (offset + size) > last) { + fprintf(stderr, "ffsdrv_write_check() failed (addr = 0x%x, size = %d)\n", + (int) addr, size); + fprintf(stdout, "ffsdrv_write_check() failed (addr = 0x%x, size = %d)\n", + (int) addr, size); + exit (1); + } +} + + +void ffsdrv_write_error(uint16 old, uint16 new) +{ + test_fatal_printf("FATAL: Attempt to rewrite 0 to 1 bit " + "(old:0x%x/%c new:0x%x/%c)\n", + old, (old < ' ' ? '?' : old), + new, (new < ' ' ? '?' : new)); +} + +void ffsdrv_test_write_halfword(volatile uint16 *addr, uint16 value) +{ + tw(tr(TR_FUNC, TrDrvWrite, "test_write_halfword(0x%05x, 0x%x)\n", + addr2offset(addr), value)); + + ffsdrv_write_check((uint8 *) addr, 2); + + if (~*addr & value) + ffsdrv_write_error(*addr, value); + + *addr = value; +} + +void ffsdrv_test_write(void *dst, const void *src, uint16 size) +{ + uint8 *mydst = dst; + const uint8 *mysrc = src; + + tw(tr(TR_FUNC, TrDrvWrite, "test_write(0x%05x, 0x%x, %d)\n", + addr2offset(mydst), mysrc, size)); + + if (size > 0) + { + if ((int) mydst & 1) { + ffsdrv_write_byte(mydst++, *mysrc++); + size--; + } + while (size >= 2) { + ffsdrv_test_write_halfword((uint16 *) mydst, mysrc[0]|(mysrc[1] << 8)); + size -= 2; + mysrc += 2; + mydst += 2; + } + if (size == 1) + ffsdrv_write_byte(mydst++, *mysrc++); + } +} + +void ffsdrv_test_erase(uint8 block) +{ + int i; + uint8 *addr; + + addr = block2addr(block); + + tw(tr(TR_FUNC, TrDrvErase, "ffsdrv_test_erase(%d)\n", block)); + + for (i = 0; i < 1 << dev.binfo[block].size_ld; i++) { + *addr++ = 0xFF; + } +} + +char *ffsdrv_test_create(void) +{ + // If flash image file already exists, open the file, and mmap it. + // Otherwise, create file, fill file with 1's, then mmap it. + + int i; + struct stat statbuf; +#ifdef WIN32 + OFSTRUCT lpReOpenBuff; + DWORD last_error; + SECURITY_ATTRIBUTES lpAttributes; + + lpAttributes.nLength = sizeof (lpAttributes); + lpAttributes.lpSecurityDescriptor = NULL; + lpAttributes.bInheritHandle = TRUE; +#endif + image_size = (int) dev.binfo[dev.numblocks - 1].offset + + (1 << dev.binfo[dev.numblocks - 1].size_ld); + + tw(tr(TR_BEGIN, TrDrvInit, "ffsdrv_test_create() {\n")); + tw(tr(TR_FUNC, TrDrvInit, "%s image: '%s', size = %d\n", + arg_removeimage ? "new" : "current", arg_imagename, image_size)); + + // create file if it does not exist +#ifdef WIN32 + if( arg_removeimage || OpenFile( arg_imagename, &lpReOpenBuff, OF_EXIST) == HFILE_ERROR ) +#else + if (arg_removeimage || lstat(arg_imagename, &statbuf) == -1) +#endif + { + char data[64]; +#ifdef WIN32 + DWORD bwritten; +#endif + // only the first run should remove the flash image file + arg_removeimage = 0; + + tw(tr(TR_FUNC, TrDrvInit, "creating new flash image file '%s'\n", + arg_imagename)); +#ifdef WIN32 + image_fd = CreateFile(arg_imagename, + GENERIC_WRITE | GENERIC_READ, + 0, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL ); +#else + image_fd = open(arg_imagename , O_RDWR|O_CREAT, + (S_IRWXU & ~S_IXUSR) | + ( S_IRWXG & ~S_IXGRP) | (S_IRWXO & ~S_IXOTH)); +#endif + if (image_fd == -1) { + perror("Failed to create flash image"); + exit(1); + } + + // write 1's to the file. + for (i = 0; i < 64; i++) + data[i] = 0xff; + +#ifdef WIN32 + for (i = 0; i < image_size/64; i++) + WriteFile(image_fd, data, 64, &bwritten, NULL); + CloseHandle(image_fd); +#else + for (i = 0; i < image_size/64; i++) + write(image_fd, data, 64); + + close(image_fd); +#endif + image_fd = 0; + tw(tr(TR_FUNC, TrDrvInit, "flash image file created\n")); + } + + // only open image file if this is the first initialization. + if (image_fd > 0) { + tw(tr(TR_FUNC, TrDrvInit, "re-opening '%s' file of size %d\n", + arg_imagename, image_size)); + } + else { + tw(tr(TR_FUNC, TrDrvInit, "opening '%s' file of size %d\n", + arg_imagename, image_size)); + +#ifdef WIN32 + image_fd = OpenFile( arg_imagename, &lpReOpenBuff, OF_READWRITE); + map_fd = CreateFileMapping (image_fd, + &lpAttributes, + PAGE_READWRITE, + 0, + 0, + arg_imagename); +#else + image_fd = open(arg_imagename, O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO); +#endif + if (image_fd == -1) { + perror("Failed to open flash image"); + exit(1); + } + + // memory map the file and update block addresses of binfo array +#ifdef WIN32 + image_addr = MapViewOfFile( map_fd, + FILE_MAP_ALL_ACCESS, + 0, + 0, + 0); +#else + image_addr = mmap(0, image_size, PROT_READ|PROT_WRITE, + MAP_FILE|MAP_SHARED, image_fd, 0); +#endif + } + + tw(tr(TR_END, TrDrvInit, "}\n")); + + return image_addr; +} + +#endif // (TARGET == 0) + + +/****************************************************************************** + * Device Detection and Copying of Driver to RAM + ******************************************************************************/ + +#if (TARGET == 1) + +// Note that this function reads device code of any of the three known flash +// families; Intel, AMD and SST. This works because Intel and AMD use +// the same command data for entering READ_IDENTIFIER mode (0x90). +// The function should be copied and executed from RAM! +void ffsdrv_device_id_read(uint16 *manufact, uint16 *device) +{ + int addr, i; + + // This silly looking code has one purpose; to set addr = 0xAAAA. It is + // necessary in order to force the compiler NOT to produce code that + // uses LDR opcode(s) with PC-relative addressing. The assember code + // produced from this C code is completely relocatable! + for (i = 0, addr = 0; i < 2; i++) + addr |= addr << 8 | 0xAA; + + FLASH_WRITE_HALFWORD (addr, 0xAA); + FLASH_WRITE_HALFWORD (addr >> 1, 0x55); + FLASH_WRITE_HALFWORD (addr, 0x90); // Intel/AMD read id command + + *manufact = FLASH_READ_HALFWORD (0); // flash a0 = 0 + *device = FLASH_READ_HALFWORD (2); // flash a0 = 1 + + // Read extended id + device[1] = FLASH_READ_HALFWORD (0xE << 1); + device[2] = FLASH_READ_HALFWORD (0xF << 1); + FLASH_WRITE_HALFWORD (0, 0xFF); // Intel read-array command + + // AMD devices do not need the two unlock cycles but SST devices do, + // even though the SST datasheets states otherwise ;-) + FLASH_WRITE_HALFWORD (addr, 0xAA); + FLASH_WRITE_HALFWORD (addr >> 1, 0x55); + FLASH_WRITE_HALFWORD (addr, 0xF0); // AMD read-array/reset command +} + +// Copy ffsdrv_device_id_read() function code to RAM. The only known way to +// determine the size of the code is to look either in the linker-generated +// map file or in the assember output file. +void ffsdrv_device_id_read_copy_to_ram(uint16 *dst, int size) +{ + uint16 *src = (uint16 *) &ffsdrv_device_id_read; + + // The ARM7TDMI compiler sets bit 0 for thumb mode function pointers, so + // we need to clear this in order to copy *all* bytes. Otherwise we + // exclude first byte and the resulting copy becomes garbage + src = (uint16 *) (~1 & (int) src); + size /= 2; + + while (size--) + *dst++ = *src++; +} + +// Copy ffsdrv_xxx_sb_erase() and ffsdrv_xxx_sb_write_halfword() functions +// to RAM. The only known way to determine the size of the code is to look +// either in the linker-generated map file or in the assember output file. +int ffsdrv_driver_copy_to_ram(int type) +{ + int size; + uint16 *src, *dst; + extern uint16 ffsdrv_ram_amd_begin[]; + extern uint16 ffsdrv_ram_intel_begin[]; + uint32 offset_of_init; + uint32 offset_of_erase; + uint32 offset_of_write_halfword; + + ttw(ttr(TTrDrvOther, "ffsdrv_driver_copy_to_ram() {" NL)); + + switch (type) { + case FFS_DRIVER_AMD: + case FFS_DRIVER_AMD_SB: + src = ffsdrv_ram_amd_begin; + offset_of_erase = + (uint32) ffsdrv_ram_amd_sb_erase - (uint32) src; + offset_of_write_halfword = + (uint32) ffsdrv_ram_amd_sb_write_halfword - (uint32) src; + break; + case FFS_DRIVER_INTEL_SB: + src = ffsdrv_ram_intel_begin; + offset_of_init = + (uint32) ffsdrv_ram_intel_sb_init - (uint32) src; + offset_of_erase = + (uint32) ffsdrv_ram_intel_sb_erase - (uint32) src; + offset_of_write_halfword = + (uint32) ffsdrv_ram_intel_sb_write_halfword - (uint32) src; + break; + case FFS_DRIVER_INTEL: + src = ffsdrv_ram_intel_begin; + offset_of_init = + (uint32) ffsdrv_ram_intel_sb_init - (uint32) src; + offset_of_erase = + (uint32) ffsdrv_ram_intel_erase - (uint32) src; + offset_of_write_halfword = + (uint32) ffsdrv_ram_intel_sb_write_halfword - (uint32) src; + break; + default: + ttw(ttr(TTrDrvOther, "}" NL)); + return 0; + } + + // Make sure we are handling a half-word aligned address (Thumb mode + // function pointers have lsb set!) + src = (uint16 *) (~1 & (int) src); + + // If we detect that the linker allocated the driver to RUN in RAM, the + // user has obviously NOT removed those linker lines and we bail out! + if (offset_of_erase > FFSDRV_CODE_SIZE) + return EFFS_DRIVER; + + dst = (uint16 *) &ffsdrv_code; + + // Code size in halfwords + size = FFSDRV_CODE_SIZE / 2; + + // Rebind the two changed driver functions + if (type == FFS_DRIVER_AMD_SB || type == FFS_DRIVER_INTEL_SB) { + ffsdrv.erase = + (void (*)(uint8)) + (offset_of_erase + (uint32) dst); + ffsdrv.write_halfword = + (void (*)(volatile uint16 *, uint16)) + (offset_of_write_halfword + (uint32) dst); + } + if (type == FFS_DRIVER_INTEL_SB) { + ffsdrv.init = + (int (*)(void)) + (offset_of_init + (uint32) dst); + } + + ttw(ttr(TTrDrvOther, "ffsdrv_code, init, write, erase = 0x%07x, 0x%07x, 0x%07x, 0x%07x" NL, + dst, (uint32) ffsdrv.init, + (uint32) ffsdrv.write_halfword, (uint32) ffsdrv.erase)); + + ttw(ttr(TTrDrvOther, "amd_begin, init, write, erase = 0x%07x, 0x%07x, 0x%07x, 0x%07x" NL, + ffsdrv_ram_amd_begin, ffsdrv_null_init, + ffsdrv_ram_amd_sb_write_halfword, ffsdrv_ram_amd_sb_erase)); + + ttw(ttr(TTrDrvOther, "intel_begin, init, write, erase = 0x%07x, 0x%07x, 0x%07x, 0x%07x" NL, + ffsdrv_ram_intel_begin, ffsdrv_ram_intel_sb_init, + ffsdrv_ram_intel_sb_write_halfword, ffsdrv_ram_intel_sb_erase)); + + // Copy the code to RAM + while (size--) + *dst++ = *src++; + + ttw(ttr(TTrDrvOther, "}" NL)); + + return 0; +} + +#else // (TARGET == 0) + +void ffsdrv_device_id_read(uint16 *manufact, uint16 *device) {} +int ffsdrv_driver_copy_to_ram(int type) { return 0; } + +#endif // (TARGET == 1) + + +/****************************************************************************** + * Initialization + ******************************************************************************/ + +const struct ffsdrv_s ffsdrv_amd = { + ffsdrv_null_init, + ffsdrv_amd_erase, + ffsdrv_amd_write_halfword, + ffsdrv_amd_write, + ffsdrv_amd_write_end, + ffsdrv_amd_erase_suspend, + ffsdrv_amd_erase_resume +}; + +const struct ffsdrv_s ffsdrv_amd_sb = { + ffsdrv_null_init, + ffsdrv_ram_amd_sb_erase, + ffsdrv_ram_amd_sb_write_halfword, + ffsdrv_generic_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; + +const struct ffsdrv_s ffsdrv_sst = { + ffsdrv_null_init, + ffsdrv_sst_erase, + ffsdrv_amd_write_halfword, // Use AMD driver function + ffsdrv_sst_write, + ffsdrv_amd_write_end, // Use AMD driver function + ffsdrv_sst_erase_suspend, + ffsdrv_null_erase_resume +}; + +const struct ffsdrv_s ffsdrv_sst_sb = { + ffsdrv_null_init, + ffsdrv_null_erase, + ffsdrv_null_write_halfword, + ffsdrv_null_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; + +// We use the functions ffsdrv_ram_intel_sb_write_halfword and +// ffsdrv_ram_intel_erase due to the bug in the intel wireless flash +// device. See 28F640W30.pdf specification Errata 5. +const struct ffsdrv_s ffsdrv_intel = { + ffsdrv_null_init, + ffsdrv_intel_erase, + ffsdrv_intel_write_halfword, + ffsdrv_generic_write, + ffsdrv_intel_write_end, + ffsdrv_intel_erase_suspend, + ffsdrv_intel_erase_resume +}; + +const struct ffsdrv_s ffsdrv_intel_sb = { + ffsdrv_null_init, + ffsdrv_ram_intel_sb_erase, + ffsdrv_ram_intel_sb_write_halfword, + ffsdrv_generic_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; + +const struct ffsdrv_s ffsdrv_null = { + ffsdrv_null_init, + ffsdrv_null_erase, + ffsdrv_null_write_halfword, + ffsdrv_null_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; + +const struct ffsdrv_s ffsdrv_amd_pseudo_sb = { + ffsdrv_null_init, + ffsdrv_amd_pseudo_sb_erase, + ffsdrv_amd_pseudo_sb_write_halfword, + ffsdrv_generic_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; + +const struct ffsdrv_s ffsdrv_ram = { + ffsdrv_null_init, + ffsdrv_ram_erase, + ffsdrv_ram_write_halfword, + ffsdrv_ram_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; + +#if (TARGET == 0) +const struct ffsdrv_s ffsdrv_test = { + ffsdrv_null_init, + ffsdrv_test_erase, + ffsdrv_test_write_halfword, + ffsdrv_test_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; +#endif + +// Note: This function is designed for little-endian memory addressing! +void ffsdrv_write_byte(void *dst, uint8 value) +{ + uint16 halfword; + + tw(tr(TR_FUNC, TrDrvWrite, "ffsdrv_write_byte(0x%05x, 0x%x)\n", + (int) (addr2offset(dst)), value)); + ttw(str(TTrDrvWrite, "wb" NL)); + + if ((int) dst & 1) + halfword = (value << 8) | *((uint8 *) dst - 1); + else + halfword = (*((uint8 *) dst + 1) << 8) | (value); + + ffsdrv.write_halfword((uint16 *) ((int) dst & ~1), halfword); +} + + +extern uint16 ffs_flash_manufact; +extern uint16 ffs_flash_device; + +effs_t ffsdrv_init(void) +{ + const struct ffsdrv_s *p; + const struct flash_info_s *flash = &flash_info[0]; + int error; + + tw(tr(TR_BEGIN, TrDrvInit, "drv_init() {\n")); + ttw(str(TTrDrvOther, "ffsdrv_init() {" NL)); + + dev.state = DEV_READ; + dev.binfo = 0; + dev.base = 0; + dev.numblocks = 0; + + // If ffs_flash_device is zero, detect device automatically by copying + // the detect function into RAM and execute it from there... + if (ffs_flash_manufact == 0 && ffs_flash_device == 0) + { +#if (TARGET == 1) + char detect_code[80]; + typedef (*pf_t)(uint16 *, uint16 *); + pf_t myfp; + uint16 device_id[3]; + + ffsdrv_device_id_read_copy_to_ram((uint16 *) detect_code, + sizeof(detect_code)); + // Combine bit 0 of the thumb mode function pointer with the address + // of the code in RAM. Then call the detect function in RAM. + myfp = (pf_t) (((int) &ffsdrv_device_id_read & 1) | (int) detect_code); + (*myfp)(&dev.manufact, device_id); + + if ((dev.manufact == MANUFACT_AMD || dev.manufact == MANUFACT_FUJITSU) && + device_id[0] == 0x227E) { + // This is a multi-id device + dev.device = (device_id[1] << 8) | (device_id[2] & 0xFF); + } + else + dev.device = device_id[0]; +#endif + } + else { + dev.manufact = ffs_flash_manufact; + dev.device = ffs_flash_device; + } + + tw(tr(TR_FUNC, TrDrvInit, "TARGET = %d\n", TARGET)); + tw(tr(TR_FUNC, TrDrvInit, "Looking up device (0x%2x,0x%4x): ", + dev.manufact, dev.device)); + while (flash->manufact) { + tw(tr(TR_NULL, TrDrvInit, "(0x%02x,0x%04x) ", + flash->manufact, flash->device)); + if (dev.manufact == flash->manufact && dev.device == flash->device) { + tw(tr(TR_NULL, TrDrvInit, "FOUND ")); + break; + } + flash++; + } + tw(tr(TR_NULL, TrDrvInit, "\n")); + + if (flash->manufact == 0) { + tw(tr(TR_END, TrDrvInit, "} (%d)\n", EFFS_NODEVICE)); + return EFFS_NODEVICE; + } + + dev.binfo = (struct block_info_s *) flash->binfo; + + if (flash->driver == FFS_DRIVER_RAM && flash->base == 0) { + if (ffs_ram_image_address <= 0) { + tw(tr(TR_END, TrDrvInit, "} (%d)\n", EFFS_DRIVER)); + return EFFS_DRIVER; + } + dev.base = (char *) ffs_ram_image_address; + } + else + dev.base = (char *) flash->base; + + dev.numblocks = flash->numblocks; + dev.driver = flash->driver; + + // We assume that ALL blocks are of equal size + dev.blocksize_ld = dev.binfo[0].size_ld; + dev.blocksize = (1 << dev.blocksize_ld); + + dev.atomlog2 = FFS_ATOM_LOG2; + dev.atomsize = 1 << dev.atomlog2; + dev.atomnotmask = dev.atomsize - 1; + +#if (TARGET == 0) + if (dev.manufact == MANUFACT_TEST) + dev.base = ffsdrv_test_create(); + + p = &ffsdrv_test; + +#else // (TARGET == 1) + + // Initialize hardware independent driver functions array + switch (dev.driver) { + case FFS_DRIVER_AMD: p = &ffsdrv_amd; break; + case FFS_DRIVER_AMD_SB: p = &ffsdrv_amd_sb; break; + case FFS_DRIVER_SST: p = &ffsdrv_sst; break; + case FFS_DRIVER_SST_SB: p = &ffsdrv_sst_sb; break; + case FFS_DRIVER_INTEL: p = &ffsdrv_intel; break; + case FFS_DRIVER_INTEL_SB: p = &ffsdrv_intel_sb; break; + case FFS_DRIVER_AMD_PSEUDO_SB: p = &ffsdrv_amd_pseudo_sb; break; + case FFS_DRIVER_RAM: p = &ffsdrv_ram; break; + default: p = &ffsdrv_null; break; + } + +#endif // (TARGET == 0) + + // Bind the driver functions + ffsdrv.init = p->init; + ffsdrv.erase = p->erase; + ffsdrv.write_halfword = p->write_halfword; + ffsdrv.write = p->write; + ffsdrv.write_end = p->write_end; + ffsdrv.erase_suspend = p->erase_suspend; + ffsdrv.erase_resume = p->erase_resume; + + // Copy single bank driver code to RAM (and possibly re-bind some of the + // driver functions) + error = ffsdrv_driver_copy_to_ram(dev.driver); + + if (error >= 0) + error = ffsdrv.init(); + + tw(tr(TR_FUNC, TrDrvInit, "dev.binfo = 0x%x\n", (unsigned int) dev.binfo)); + tw(tr(TR_FUNC, TrDrvInit, "dev.base = 0x%x\n", (unsigned int) dev.base)); + tw(tr(TR_FUNC, TrDrvInit, "dev.numblocks = %d\n", dev.numblocks)); + tw(tr(TR_FUNC, TrDrvInit, "dev.blocksize = %d\n", dev.blocksize)); + tw(tr(TR_FUNC, TrDrvInit, "dev.atomlog2/atomsize/atomnotmask = %d/%d/%x\n", + dev.atomlog2, dev.atomsize, dev.atomnotmask)); + tw(tr(TR_END, TrDrvInit, "} %d\n", error)); + ttw(ttr(TTrDrvOther, "} %d" NL, error)); + + return error; +} + + +/****************************************************************************** + * Interrupt Enable/Disable + ******************************************************************************/ + +// IMPORTANT NOTE! Apparently, locating this ARM assembly code at the top of +// this file will make the compiler trash the A1 register between the calls +// of arm_int_disable and arm_int_enable() thus crashing the whole system. +// If the code is placed AFTER the usage of the functions, the compiler +// saves the A1 register. Strange but true. + +// IMPORTANT NOTE! Apparently, another strange thing is that if the +// functions are declared static, they don't work! + +// Executing code from RAM is NOT trivial when we need to jump between ROM +// (flash) and RAM memory. The ARM only supports 26-bit relative branch +// offsets. This is the reason why we have a local copy of the +// arm_int_disable/enable() functions in this file plus each of the +// single-bank drivers. + +#if (TARGET == 1) +// Note that we use our own interrupt disable/enable function because +// Nucleus allegedly should have a bug in its implementation for this. + +uint32 int_disable(void) +{ + asm(" .state16"); + asm(" mov A1, #0xC0"); + asm(" ldr A2, tct_disable"); + asm(" bx A2 "); + + asm("tct_disable .field _TCT_Control_Interrupts+0,32"); + asm(" .global _TCT_Control_Interrupts"); +} + +void int_enable(uint32 cpsr) +{ + asm(" .state16"); + asm(" ldr A2, tct_enable"); + asm(" bx A2 "); + + asm("tct_enable .field _TCT_Control_Interrupts+0,32"); + asm(" .global _TCT_Control_Interrupts"); +} + +#else + +uint32 int_disable(void) { return 0; } +void int_enable(uint32 tmp) {} + +#endif // (TARGET == 1) + +