FreeCalypso > hg > freecalypso-sw
diff gsm-fw/services/ffs/drv.c @ 209:6f4a12b4582f
gsm-fw FFS: starting to integrate C code
author | Michael Spacefalcon <msokolov@ivan.Harhan.ORG> |
---|---|
date | Thu, 26 Dec 2013 03:59:59 +0000 |
parents | |
children | fee45482aa2a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gsm-fw/services/ffs/drv.c Thu Dec 26 03:59:59 2013 +0000 @@ -0,0 +1,423 @@ +/****************************************************************************** + * 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 $ + * + ******************************************************************************/ + +#include "../../include/config.h" +#include "ffs.h" +#include "drv.h" +#include "ffstrace.h" +#include "intctl.h" +#include "ramffs.h" +#include <string.h> + + +/****************************************************************************** + * Macros + ******************************************************************************/ + +#define addr2offset(address) ( (int) (address) - (int) dev.base ) + + +/****************************************************************************** + * Generic Driver Functions + ******************************************************************************/ + +// 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); +} + +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++); + } +} + +/****************************************************************************** + * Dummy 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)); +} + +/* + * FreeCalypso change from TI: we only compile one flash "driver" type + * based on the build configuration. + */ + +#if FFS_IN_RAM + +/****************************************************************************** + * RAM Family Functions + ******************************************************************************/ + +struct block_info_s ramffs_block_info[RAMFFS_NBLOCKS]; + +int ffsdrv_ram_init(void) +{ + unsigned i; + uint32 offset; + + ttw(ttr(TTrDrvOther, "ffsdrv_ram_init()" NL)); + memset(_RAMFFS_area, 0xFF, RAMFFS_TOTAL_SIZE); + offset = 0; + for (i = 0; i < RAMFFS_NBLOCKS; i++) { + ramffs_block_info[i].offset = offset; + ramffs_block_info[i].size_ld = RAMFFS_BLKSIZE_LOG2; + offset += RAMFFS_BLKSIZE_BYTES; + } + return 0; +} + +void ffsdrv_ram_write_halfword(volatile uint16 *dst, uint16 value) +{ + *dst = value; +} + +#if 0 +/* duplicates ffsdrv_generic_write */ +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++); + } +} +#endif + +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; + } +} + +const struct ffsdrv_s ffsdrv = { + ffsdrv_ram_init, + ffsdrv_ram_erase, + ffsdrv_ram_write_halfword, + ffsdrv_generic_write, + ffsdrv_null_write_end, + ffsdrv_null_erase_suspend, + ffsdrv_null_erase_resume +}; + +#elif CONFIG_FLASH_WRITE + +/****************************************************************************** + * 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(); +} + +#if 0 +/* duplicates ffsdrv_generic_write */ +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++); + } +} +#endif + +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)); +} + +const struct ffsdrv_s ffsdrv = { + ffsdrv_null_init, + ffsdrv_amd_erase, + ffsdrv_amd_write_halfword, + ffsdrv_generic_write, + ffsdrv_amd_write_end, + ffsdrv_amd_erase_suspend, + ffsdrv_amd_erase_resume +}; + +#else + +/* + * This part will get compiled if we have real FFS (FFS_IN_RAM=0), + * but not allowed to write to flash (CONFIG_FLASH_WRITE=0). + */ + +const struct ffsdrv_s ffsdrv = { + 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 +}; + +#endif + + +/****************************************************************************** + * Initialization + * + * Significantly simplified in FreeCalypso. + * + ******************************************************************************/ + +effs_t ffsdrv_init(void) +{ + int error; + + tw(tr(TR_BEGIN, TrDrvInit, "drv_init() {\n")); + ttw(str(TTrDrvOther, "ffsdrv_init() {" NL)); + + dev.state = DEV_READ; + dev.atomlog2 = FFS_ATOM_LOG2; + dev.atomsize = 1 << dev.atomlog2; + dev.atomnotmask = dev.atomsize - 1; + + 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; +}