Mercurial > flash_v2
diff packages/io/flash/current/src/flash.c @ 1706:3845779eca10
* src/flash.c: Major rewrite to implement a new API
* src/legacy_api.c: Support the legacy API
* src/legacy_drv.c: Support the legacy driver API
* src/flashiodev.c: Support the new API and multiple devices
* include/flash_priv.h: Interface devices should use
* cdl/io_flash.cdl: Updates for new interface and multiple devices.
| author | asl |
|---|---|
| date | Thu, 05 Aug 2004 13:50:00 +0000 |
| parents | e1d094896914 |
| children | 868c3fa9315f |
line wrap: on
line diff
--- a/packages/io/flash/current/src/flash.c +++ b/packages/io/flash/current/src/flash.c @@ -10,6 +10,7 @@ // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // Copyright (C) 2003 Gary Thomas +// Copyright (C) 2004 Andrew Lunn // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free @@ -42,7 +43,7 @@ //#####DESCRIPTIONBEGIN#### // // Author(s): gthomas -// Contributors: gthomas +// Contributors: gthomas, Andrew Lunn // Date: 2000-07-26 // Purpose: // Description: @@ -53,503 +54,803 @@ #include <pkgconf/system.h> #include <pkgconf/io_flash.h> - +#ifdef CYGPKG_KERNEL +#include <cyg/kernel/kapi.h> +#endif #include <cyg/hal/hal_arch.h> #include <cyg/hal/hal_intr.h> #include <cyg/hal/hal_cache.h> +#include <cyg/hal/hal_tables.h> +#include <cyg/infra/cyg_ass.h> #include <string.h> #define _FLASH_PRIVATE_ #include <cyg/io/flash.h> // When this flag is set, do not actually jump to the relocated code. -// This can be used for running the function in place (RAM startup only), -// allowing calls to diag_printf() and similar. +// This can be used for running the function in place (RAM startup +// only), allowing calls to diag_printf() and similar. #undef RAM_FLASH_DEV_DEBUG #if !defined(CYG_HAL_STARTUP_RAM) && defined(RAM_FLASH_DEV_DEBUG) # warning "Can only enable the flash debugging when configured for RAM startup" #endif - -struct flash_info flash_info; - -// These are the functions in the HW specific driver we need to call. -typedef void code_fun(void*); +#ifndef MIN +#define MIN(x,y) ((x)<(y) ? (x) : (y)) +#endif -externC code_fun flash_query; -externC code_fun flash_erase_block; -externC code_fun flash_program_buf; -externC code_fun flash_read_buf; -externC code_fun flash_lock_block; -externC code_fun flash_unlock_block; +// Has the FLASH IO library been initialise? +static bool init = false; +// We have a linked list of flash drivers. This is the head of the +// list +static struct cyg_flash_dev *flash_head = NULL; + +// This array contains entries for all flash devices that are +// installed in the system. +__externC struct cyg_flash_dev cyg_flashdevtab[]; +CYG_HAL_TABLE_BEGIN(cyg_flashdevtab, cyg_flashdev); + +// end of the flashdev table +__externC struct cyg_flash_dev cyg_flashdevtab_end; +CYG_HAL_TABLE_END(cyg_flashdevtab_end, cyg_flashdev); -int -flash_init(_printf *pf) +// Sort the linked list into ascending order of flash address. Use a +// primitive ripple sort, but since we don't expect to have many +// devices this should be OK. +static void flash_sort(void) { - int err; + bool moved; + struct cyg_flash_dev *dev, **previous_next; + + // If there is zero or one device, short cut + if (flash_head == NULL || flash_head->next == NULL) + return; + + do { + moved=false; + for (dev=flash_head, previous_next=&flash_head; + dev->next; + previous_next = &dev->next, dev=dev->next ){ + if (dev->start > dev->next->start) { + *previous_next=dev->next; + dev->next = (*previous_next)->next; + (*previous_next)->next = dev; + moved=true; + break; + } + } + } while (moved); +} - if (flash_info.init) return FLASH_ERR_OK; - flash_info.pf = pf; // Do this before calling into the driver - if ((err = flash_hwr_init()) != FLASH_ERR_OK) { - return err; - } - flash_info.block_mask = ~(flash_info.block_size-1); - flash_info.init = 1; - return FLASH_ERR_OK; +// Walk the linked list and see if there are any overlaps in the +// addresses the devices claim to use using. +static bool flash_check_overlap(void) +{ + struct cyg_flash_dev *dev; + + // If there is zero or one device, short cut + if (flash_head == NULL || flash_head->next == NULL) + return false; + + for (dev=flash_head; dev->next; dev=dev->next){ + if (dev->end >= dev->next->start) + return true; + } + return false; } -// Use this function to make function pointers anonymous - forcing the -// compiler to use jumps instead of branches when calling driver -// services. -static void* __anonymizer(void* p) +// Initialise all registered device. Any device that fails to +// initialise we leave dev->init as false. Then sort the devices into +// ascending order of address and put them into a linked list. Lastly +// check if we have any overlap of the addresses. +__externC int +cyg_flash_init(cyg_flash_printf *pf) { - return p; + int err; + struct cyg_flash_dev * dev; + + if (init) return CYG_FLASH_ERR_OK; + init = true; + + for (dev = &cyg_flashdevtab[0]; dev != &cyg_flashdevtab_end; dev++) { + dev->pf = pf; +#ifdef CYGPKG_KERNEL + cyg_mutex_init(&dev->mutex); +#endif + + err = dev->funs->flash_init(dev); + if (err != CYG_FLASH_ERR_OK) { + continue; + } + CYG_ASSERT(dev->funs, "No flash functions"); + CYG_ASSERT(dev->num_block_infos, "No number of block infoss"); + CYG_ASSERT(dev->block_info, "No block infos"); + CYG_ASSERT(!(((cyg_flashaddr_t)dev->block_info >= dev->start) && + ((cyg_flashaddr_t)dev->block_info < dev->end)), + "Block info is in the flash"); + CYG_ASSERT(dev->funs->flash_erase_block, "No erase function"); + CYG_ASSERT(dev->funs->flash_program, "No program function"); + CYG_ASSERT(dev->funs->flash_hwr_map_error, "No hwr map error function"); + + dev->init = true; + dev->next = flash_head; + flash_head = dev; + } + + flash_sort(); + + if (flash_check_overlap()) { + return CYG_FLASH_ERR_INVALID; + } + return CYG_FLASH_ERR_OK; } -// FIXME: Want to change all drivers to use this function. But it may -// make sense to wait till device structure pointer arguments get -// added as well. -void -flash_dev_query(void* data) +// Is the address within one of the flash drivers? +__externC int +cyg_flash_verify_addr(const cyg_flashaddr_t address) { - typedef void code_fun(void*); - code_fun *_flash_query; - int d_cache, i_cache; - - _flash_query = (code_fun*) __anonymizer(&flash_query); - - HAL_FLASH_CACHES_OFF(d_cache, i_cache); - (*_flash_query)(data); - HAL_FLASH_CACHES_ON(d_cache, i_cache); + struct cyg_flash_dev * dev; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + + for (dev = flash_head; dev; dev=dev->next) { + if ((dev->start <= address) && (dev->end > address)) + return CYG_FLASH_ERR_OK; + } + return CYG_FLASH_ERR_INVALID; } -int -flash_verify_addr(void *target) +// See if a range of FLASH addresses overlaps currently running code +__externC bool cyg_flash_code_overlaps(const cyg_flashaddr_t start, + const cyg_flashaddr_t end) { - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } - if (((CYG_ADDRESS)target >= (CYG_ADDRESS)flash_info.start) && - ((CYG_ADDRESS)target <= ( ((CYG_ADDRESS)flash_info.end) - 1) )) { - return FLASH_ERR_OK; - } else { - return FLASH_ERR_INVALID; - } + extern char _stext[], _etext[]; + + return ((((unsigned long)&_stext >= (unsigned long)start) && + ((unsigned long)&_stext < (unsigned long)end)) || + (((unsigned long)&_etext >= (unsigned long)start) && + ((unsigned long)&_etext < (unsigned long)end))); } -int -flash_get_limits(void *target, void **start, void **end) +// Get the start and end address of the first driver +__externC int +cyg_flash_get_limits(cyg_flashaddr_t *start, cyg_flashaddr_t *end) { - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; + if (!init) { + return CYG_FLASH_ERR_NOT_INIT; } - *start = flash_info.start; - *end = flash_info.end; - return FLASH_ERR_OK; + if (flash_head) { + *start = flash_head->start; + *end = flash_head->end; + return CYG_FLASH_ERR_OK; + } + return CYG_FLASH_ERR_INVALID; } -int -flash_get_block_info(int *block_size, int *blocks) +// Get the block size and number of blocks for the first driver. For +// devices with boot blocks, consolidate the boot blocks into the +// figure. +__externC int +cyg_flash_get_block_info(size_t *block_size, cyg_uint32 *blocks) { - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; + size_t biggest_size=0; + cyg_uint32 i; + + + if (!init) { + return CYG_FLASH_ERR_NOT_INIT; + } + if (!flash_head) return CYG_FLASH_ERR_INVALID; + + // Find the biggest size of blocks + for (i=0; i < flash_head->num_block_infos; i++) { + if (flash_head->block_info[i].block_size > biggest_size) { + biggest_size = flash_head->block_info[i].block_size; } - *block_size = flash_info.block_size; - *blocks = flash_info.blocks; - return FLASH_ERR_OK; + } + + // Calculate the number of biggest size blocks + *block_size = biggest_size; + *blocks = 0; + for (i=0; i < flash_head->num_block_infos; i++) { + *blocks += (flash_head->block_info[i].block_size * + flash_head->block_info[i].blocks) / + biggest_size; + } + return CYG_FLASH_ERR_OK; +} + +// Return information about the Nth driver +__externC int +cyg_flash_get_info(cyg_uint32 Nth, cyg_flash_info_t * info) +{ + struct cyg_flash_dev * dev; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + + for (dev = flash_head; dev && Nth; dev=dev->next, Nth--) + ; + if (dev && !Nth) { + info->start = dev->start; + info->end = dev->end; + info->num_block_infos = dev->num_block_infos; + info->block_info = dev->block_info; + return CYG_FLASH_ERR_OK; + } + return CYG_FLASH_ERR_INVALID; } -int -flash_erase(void *addr, int len, void **err_addr) +// Return information about the flash at the given address +__externC int +cyg_flash_get_info_addr(cyg_flashaddr_t flash_base, cyg_flash_info_t * info) { - unsigned short *block, *end_addr; - int stat = 0; - typedef int code_fun(unsigned short *, unsigned int); - code_fun *_flash_erase_block; - int d_cache, i_cache; - - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; + struct cyg_flash_dev *dev; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + + for (dev = flash_head; dev ; dev=dev->next) { + if ((dev->start <= flash_base) && ( dev->end > flash_base)) { + info->start = dev->start; + info->end = dev->end; + info->num_block_infos = dev->num_block_infos; + info->block_info = dev->block_info; + return CYG_FLASH_ERR_OK; } - -#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT - if (plf_flash_query_soft_wp(addr,len)) - return FLASH_ERR_PROTECT; -#endif - - _flash_erase_block = (code_fun*) __anonymizer(&flash_erase_block); - - block = (unsigned short *)((CYG_ADDRESS)addr & flash_info.block_mask); - end_addr = (unsigned short *)((CYG_ADDRESS)addr+len); + } + return CYG_FLASH_ERR_INVALID; +} - /* Check to see if end_addr overflowed */ - if( (end_addr < block) && (len > 0) ){ - end_addr = (unsigned short *) ((CYG_ADDRESS) flash_info.end - 1); +#ifdef CYGPKG_KERNEL +//Lock the mutex's for a range of addresses +__externC int +cyg_flash_mutex_lock(const cyg_flashaddr_t from, const size_t len) +{ + struct cyg_flash_dev * dev; + int err; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + + for (dev = flash_head; dev ; dev=dev->next) { + if ((dev->start <= from) && ( dev->end > from)) { + cyg_mutex_lock(&dev->mutex); + if (dev->end > from+len) { + return CYG_FLASH_ERR_OK; + } else { // Check off by one? + //The region is bigger than this driver. Recurse + err = cyg_flash_mutex_lock(dev->end+1, (len - (dev->end - from))); + if (err != CYG_FLASH_ERR_OK) { + // Something went wrong, unlock what we just locked + cyg_mutex_unlock(&dev->mutex); + } + return err; + } } - -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Erase from %p-%p: ", (void*)block, (void*)end_addr); -#endif + } + return CYG_FLASH_ERR_INVALID; +} - HAL_FLASH_CACHES_OFF(d_cache, i_cache); - FLASH_Enable(block, end_addr); - while (block < end_addr) { - // Supply the blocksize for a gross check for erase success - unsigned short *tmp_block; -#if !defined(CYGSEM_IO_FLASH_READ_INDIRECT) - int i; - unsigned char *dp; - bool erased = true; - - dp = (unsigned char *)block; - for (i = 0; i < flash_info.block_size; i++) { - if (*dp++ != (unsigned char)0xFF) { - erased = false; - break; - } - } -#else - bool erased = false; +//Unlock the mutex's for a range of addresses +__externC int +cyg_flash_mutex_unlock(const cyg_flashaddr_t from, const size_t len) +{ + struct cyg_flash_dev * dev; + int err; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + + for (dev = flash_head; dev ; dev=dev->next) { + if ((dev->start <= from) && ( dev->end > from)) { + cyg_mutex_unlock(&dev->mutex); + if (dev->end > from+len) { + return CYG_FLASH_ERR_OK; + } else { // Check off by one? + //The region is bigger than this driver. Recurse + err = cyg_flash_mutex_unlock(dev->end+1, (len - (dev->end - from))); + return err; + } + } + } + return CYG_FLASH_ERR_INVALID; +} #endif - if (!erased) { - stat = (*_flash_erase_block)(block, flash_info.block_size); - stat = flash_hwr_map_error(stat); - } - if (stat) { - *err_addr = (void *)block; - break; - } +// Return the size of the block which is at the given address +static size_t +flash_block_size(struct cyg_flash_dev *dev, const cyg_flashaddr_t addr) +{ + int i; + size_t offset; + + + CYG_ASSERT((addr >= dev->start) && (addr <= dev->end), "Not inside device"); + + offset = addr - dev->start; + for (i=0; i < dev->num_block_infos; i++) { + if (offset < (dev->block_info[i].blocks * dev->block_info[i].block_size)) + return dev->block_info[i].block_size; + offset = offset - + (dev->block_info[i].blocks * dev->block_info[i].block_size); + } + CYG_FAIL("Programming error"); + return 0; +} + +// Return the size of the block which is at the given address +__externC size_t +cyg_flash_block_size(const cyg_flashaddr_t flash_base) +{ + struct cyg_flash_dev * dev; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + + for (dev = flash_head; + dev && !((dev->start <= flash_base) && ( dev->end > flash_base)); + dev=dev->next) + ; + if (!dev) return CYG_FLASH_ERR_INVALID; + + return flash_block_size(dev, flash_base); +} + - // Check to see if block will overflow - tmp_block = block + flash_info.block_size / sizeof(*block); - if(tmp_block < block){ - // If block address overflows, set block value to end on this loop - block = end_addr; - } - else{ - block = tmp_block; - } +__externC int +cyg_flash_erase(const cyg_flashaddr_t flash_base, + const size_t len, + cyg_flashaddr_t *err_address) +{ + cyg_flashaddr_t block, end_addr, addr; + struct cyg_flash_dev * dev; + size_t block_size; + int stat = CYG_FLASH_ERR_OK; + int d_cache, i_cache; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + +#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT + if (plf_flash_query_soft_wp(addr,len)) + return CYG_FLASH_ERR_PROTECT; +#endif + + for (dev = flash_head; + dev && !((dev->start <= flash_base) && ( dev->end > flash_base)); + dev=dev->next) + ; + if (!dev) return CYG_FLASH_ERR_INVALID; + +#ifdef CYGPKG_KERNEL + cyg_mutex_lock(&dev->mutex); +#endif + addr = flash_base; + end_addr = flash_base + len - 1; + if (end_addr > dev->end) { + end_addr = dev->end; + } + + block_size = flash_block_size(dev, addr); + block = (cyg_flashaddr_t)((size_t)addr & ~(block_size - 1)); + #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); + dev->pf("... Erase from %p-%p: ", (void*)block, (void*)end_addr); #endif + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(block, end_addr); + while (block <= end_addr) { + int i; + unsigned char *dp; + bool erased = true; + + // If there is a read function it probably means the flash + // cannot be read directly. + if (!dev->funs->flash_read) { + dp = (unsigned char *)block; + for (i = 0; i < block_size; i++) { + if (*dp++ != (unsigned char)0xFF) { + erased = false; + break; + } + } + } else { + erased=false; + } + if (!erased) { + stat = dev->funs->flash_erase_block(dev,block); + stat = dev->funs->flash_hwr_map_error(dev,stat); } - FLASH_Disable(block, end_addr); - HAL_FLASH_CACHES_ON(d_cache, i_cache); + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = block; + break; + } + block += flash_block_size(dev, block); +#ifdef CYGSEM_IO_FLASH_CHATTER + dev->pf("."); +#endif + } + FLASH_Disable(block, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); + dev->pf("\n"); +#endif +#ifdef CYGPKG_KERNEL + cyg_mutex_unlock(&dev->mutex); #endif - return (stat); + if (stat != CYG_FLASH_ERR_OK) { + return stat; + } + + if (flash_base + len - 1 > dev->end) { + // The region to erase if bigger than this driver handles. Recurse + return cyg_flash_erase(dev->end+1, + len - (dev->end - flash_base) - 1, + err_address); + } + return CYG_FLASH_ERR_OK; } -int -flash_program(void *_addr, void *_data, int len, void **err_addr) +__externC int +cyg_flash_program(const cyg_flashaddr_t flash_base, + void *ram_base, + const size_t len, + cyg_flashaddr_t *err_address) { - int stat = 0; - int size; - typedef int code_fun(void *, void *, int, unsigned long, int); - code_fun *_flash_program_buf; - unsigned char *addr = (unsigned char *)_addr; - unsigned char *data = (unsigned char *)_data; - CYG_ADDRESS tmp; - int d_cache, i_cache; - - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } - + struct cyg_flash_dev * dev; + cyg_flashaddr_t addr, end_addr; + unsigned char * ram = ram_base; + size_t block_size, size, length, offset; + int stat = CYG_FLASH_ERR_OK; + int d_cache, i_cache; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + #ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT - if (plf_flash_query_soft_wp(addr,len)) - return FLASH_ERR_PROTECT; + if (plf_flash_query_soft_wp(addr,len)) + return CYG_FLASH_ERR_PROTECT; #endif - - _flash_program_buf = (code_fun*) __anonymizer(&flash_program_buf); - + + for (dev = flash_head; + dev && !((dev->start <= flash_base) && ( dev->end > flash_base)); + dev=dev->next) + ; + if (!dev) return CYG_FLASH_ERR_INVALID; + +#ifdef CYGPKG_KERNEL + cyg_mutex_lock(&dev->mutex); +#endif + addr = flash_base; + end_addr = flash_base + len - 1; + if (end_addr > dev->end) { + end_addr = dev->end; + } + length = end_addr - addr + 1; + #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Program from %p-%p at %p: ", (void*)data, - (void*)(((CYG_ADDRESS)data)+len), (void*)addr); + dev->pf("... Program from %p-%p to %p: ", ram_base, + ((CYG_ADDRESS)ram_base)+length, addr); #endif - - HAL_FLASH_CACHES_OFF(d_cache, i_cache); - FLASH_Enable((unsigned short*)addr, (unsigned short *)(addr+len)); - while (len > 0) { - size = len; - if (size > flash_info.block_size) size = flash_info.block_size; - - tmp = (CYG_ADDRESS)addr & ~flash_info.block_mask; - if (tmp) { - tmp = flash_info.block_size - tmp; - if (size>tmp) size = tmp; - - } - - stat = (*_flash_program_buf)(addr, data, size, - flash_info.block_mask, flash_info.buffer_size); - stat = flash_hwr_map_error(stat); + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable((unsigned short*)addr, (unsigned short *)(addr+len)); + while (addr <= end_addr) { + block_size = flash_block_size(dev, addr); + size = length; + // Only one block at once + if (size > block_size) size = block_size; + + // Writing from the middle of a block? + offset = (size_t)addr & (block_size-1); + if (offset) + size = MIN(block_size - offset, size); + stat = dev->funs->flash_program(dev, addr, ram, size); + stat = dev->funs->flash_hwr_map_error(dev,stat); #ifdef CYGSEM_IO_FLASH_VERIFY_PROGRAM - if (0 == stat) // Claims to be OK - if (memcmp(addr, data, size) != 0) { - stat = 0x0BAD; + if (CYG_FLASH_ERR_OK == stat) // Claims to be OK + if (!dev->funs->flash_read && memcmp((void *)addr, ram, size) != 0) { + stat = CYG_FLASH_ERR_DRV_VERIFY; #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("V"); + dev->pf("V"); #endif - } + } #endif - if (stat) { - *err_addr = (void *)addr; - break; - } + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = addr; + break; + } #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); + dev->pf("."); #endif - len -= size; - addr += size/sizeof(*addr); - data += size/sizeof(*data); - } - FLASH_Disable((unsigned short*)addr, (unsigned short *)(addr+len)); - HAL_FLASH_CACHES_ON(d_cache, i_cache); + length -= size; + addr += size; + ram += size; + } + FLASH_Disable((unsigned short*)addr, (unsigned short *)(addr+len)); + HAL_FLASH_CACHES_ON(d_cache, i_cache); #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); + dev->pf("\n"); +#endif +#ifdef CYGPKG_KERNEL + cyg_mutex_unlock(&dev->mutex); #endif + if (stat != CYG_FLASH_ERR_OK) { return (stat); + } + if ( flash_base + len - 1 > dev->end) { + return cyg_flash_program(dev->end+1, ram, + len - (dev->end - flash_base) - 1, + err_address); + } + return CYG_FLASH_ERR_OK; } -int -flash_read(void *_addr, void *_data, int len, void **err_addr) +__externC int +cyg_flash_read(cyg_flashaddr_t flash_base, + const void *ram_base, + const size_t len, + cyg_flashaddr_t *err_address) { -#ifdef CYGSEM_IO_FLASH_READ_INDIRECT - int stat = 0; - int size; - typedef int code_fun(void *, void *, int, unsigned long, int); - code_fun *_flash_read_buf; - unsigned char *addr = (unsigned char *)_addr; - unsigned char *data = (unsigned char *)_data; - CYG_ADDRESS tmp; - int d_cache, i_cache; - - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } - - _flash_read_buf = (code_fun*) __anonymizer(&flash_read_buf); - -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Read from %p-%p at %p: ", (void*)data, - (void*)(((CYG_ADDRESS)data)+len), (void*)addr); -#endif - - HAL_FLASH_CACHES_OFF(d_cache, i_cache); - FLASH_Enable((unsigned short*)addr, (unsigned short *)(addr+len)); - while (len > 0) { - size = len; - if (size > flash_info.block_size) size = flash_info.block_size; - - tmp = (CYG_ADDRESS)addr & ~flash_info.block_mask; - if (tmp) { - tmp = flash_info.block_size - tmp; - if (size>tmp) size = tmp; - - } + struct cyg_flash_dev * dev; + cyg_flashaddr_t addr, end_addr; + unsigned char * ram = (unsigned char *)ram_base; + size_t block_size, size, length, offset; + int stat = CYG_FLASH_ERR_OK; + int d_cache, i_cache; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + + for (dev = flash_head; + dev && !((dev->start <= flash_base) && ( dev->end > flash_base)); + dev=dev->next) + ; + if (!dev) return CYG_FLASH_ERR_INVALID; - stat = (*_flash_read_buf)(addr, data, size, - flash_info.block_mask, flash_info.buffer_size); - stat = flash_hwr_map_error(stat); -#ifdef CYGSEM_IO_FLASH_VERIFY_PROGRAM_ - if (0 == stat) // Claims to be OK - if (memcmp(addr, data, size) != 0) { - stat = 0x0BAD; +#ifdef CYGPKG_KERNEL + cyg_mutex_lock(&dev->mutex); +#endif + addr = flash_base; + end_addr = flash_base + len - 1; + if (end_addr > dev->end) { + end_addr = dev->end; + } + length = end_addr - addr + 1; + #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("V"); -#endif - } -#endif - if (stat) { - *err_addr = (void *)addr; - break; - } -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); + dev->pf("... Read from %p-%p to %p: ", addr, addr+len, ram_base); #endif - len -= size; - addr += size/sizeof(*addr); - data += size/sizeof(*data); + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable((unsigned short*)addr, (unsigned short *)(addr+len)); + while (addr <= end_addr) { + block_size = flash_block_size(dev, addr); + size = length; + // Only one block at once + if (size > block_size) size = block_size; + + // Reading from the middle of a block? + offset = (size_t)addr & (block_size-1); + if (offset) + size = MIN(block_size - offset, size); + if (dev->funs->flash_read) { + stat = dev->funs->flash_read(dev, addr, ram, size); + stat = dev->funs->flash_hwr_map_error(dev,stat); + } else { + memcpy(ram, (void *)addr, size); + stat = CYG_FLASH_ERR_OK; } - FLASH_Disable((unsigned short*)addr, (unsigned short *)(addr+len)); - HAL_FLASH_CACHES_ON(d_cache, i_cache); + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = addr; + break; + } #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); + dev->pf("."); #endif + length -= size; + addr += size; + ram += size; + } + FLASH_Disable((unsigned short*)addr, (unsigned short *)(addr+len)); + HAL_FLASH_CACHES_ON(d_cache, i_cache); +#ifdef CYGSEM_IO_FLASH_CHATTER + dev->pf("\n"); +#endif +#ifdef CYGPKG_KERNEL + cyg_mutex_unlock(&dev->mutex); +#endif + if (stat != CYG_FLASH_ERR_OK) { return (stat); -#else // CYGSEM_IO_FLASH_READ_INDIRECT - // Direct access to FLASH memory is possible - just move the requested bytes - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } - memcpy(_data, _addr, len); - return FLASH_ERR_OK; -#endif + } + if ( flash_base + len - 1 > dev->end) { + return cyg_flash_read(dev->end+1, ram, + len - (dev->end - flash_base) - 1, + err_address); + } + return CYG_FLASH_ERR_OK; } #ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING - -int -flash_lock(void *addr, int len, void **err_addr) +__externC int +cyg_flash_lock(const cyg_flashaddr_t flash_base, + const size_t len, + cyg_flashaddr_t *err_address) { - unsigned short *block, *end_addr; - int stat = 0; - typedef int code_fun(unsigned short *); - code_fun *_flash_lock_block; - int d_cache, i_cache; - - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } - + cyg_flashaddr_t block, end_addr, addr; + struct cyg_flash_dev * dev; + size_t block_size; + int stat = CYG_FLASH_ERR_OK; + int d_cache, i_cache; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + #ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT - if (plf_flash_query_soft_wp(addr,len)) - return FLASH_ERR_PROTECT; + if (plf_flash_query_soft_wp(addr,len)) + return CYG_FLASH_ERR_PROTECT; #endif - - _flash_lock_block = (code_fun*) __anonymizer(&flash_lock_block); + + for (dev = flash_head; + dev && !((dev->start <= flash_base) && ( dev->end > flash_base)); + dev=dev->next) + ; + if (!dev) return CYG_FLASH_ERR_INVALID; + if (!dev->funs->flash_block_lock) return CYG_FLASH_ERR_INVALID; - block = (unsigned short *)((CYG_ADDRESS)addr & flash_info.block_mask); - end_addr = (unsigned short *)((CYG_ADDRESS)addr+len); - - /* Check to see if end_addr overflowed */ - if( (end_addr < block) && (len > 0) ){ - end_addr = (unsigned short *) ((CYG_ADDRESS) flash_info.end - 1); - } - -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Lock from %p-%p: ", block, end_addr); +#ifdef CYGPKG_KERNEL + cyg_mutex_lock(&dev->mutex); #endif - - HAL_FLASH_CACHES_OFF(d_cache, i_cache); - FLASH_Enable(block, end_addr); - while (block < end_addr) { - unsigned short *tmp_block; - stat = (*_flash_lock_block)(block); - stat = flash_hwr_map_error(stat); - if (stat) { - *err_addr = (void *)block; - break; - } - - // Check to see if block will overflow - tmp_block = block + flash_info.block_size / sizeof(*block); - if(tmp_block < block){ - // If block address overflows, set block value to end on this loop - block = end_addr; - } - else{ - block = tmp_block; - } + addr = flash_base; + end_addr = flash_base + len - 1; + if (end_addr > dev->end) { + end_addr = dev->end; + } + + block_size = flash_block_size(dev, addr); + block = (cyg_flashaddr_t)((size_t)addr & ~(block_size - 1)); + #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); + dev->pf("... Locking from %p-%p: ", (void*)block, (void*)end_addr); #endif + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(block, end_addr); + while (block <= end_addr) { + stat = dev->funs->flash_block_lock(dev,block); + stat = dev->funs->flash_hwr_map_error(dev,stat); + + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = (void *)block; + break; } - FLASH_Disable(block, end_addr); - HAL_FLASH_CACHES_ON(d_cache, i_cache); + block += flash_block_size(dev, addr); +#ifdef CYGSEM_IO_FLASH_CHATTER + dev->pf("."); +#endif + } + FLASH_Disable(block, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); + dev->pf("\n"); +#endif +#ifdef CYGPKG_KERNEL + cyg_mutex_unlock(&dev->mutex); #endif - return (stat); + if (stat != CYG_FLASH_ERR_OK) { + return stat; + } + + if (flash_base + len - 1 > dev->end) { // Off by one? + // The region to erase if bigger than this driver handles. Recurse + return cyg_flash_lock(dev->end+1, + len - (dev->end - flash_base) - 1, + err_address); + } + return CYG_FLASH_ERR_OK; } -int -flash_unlock(void *addr, int len, void **err_addr) +__externC int +cyg_flash_unlock(const cyg_flashaddr_t flash_base, + const size_t len, + cyg_flashaddr_t *err_address) { - unsigned short *block, *end_addr; - int stat = 0; - typedef int code_fun(unsigned short *, int, int); - code_fun *_flash_unlock_block; - int d_cache, i_cache; - - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } - + cyg_flashaddr_t block, end_addr, addr; + struct cyg_flash_dev * dev; + size_t block_size; + int stat = CYG_FLASH_ERR_OK; + int d_cache, i_cache; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; + #ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT - if (plf_flash_query_soft_wp(addr,len)) - return FLASH_ERR_PROTECT; + if (plf_flash_query_soft_wp(addr,len)) + return CYG_FLASH_ERR_PROTECT; #endif - - _flash_unlock_block = (code_fun*) __anonymizer(&flash_unlock_block); - - block = (unsigned short *)((CYG_ADDRESS)addr & flash_info.block_mask); - end_addr = (unsigned short *)((CYG_ADDRESS)addr+len); - - /* Check to see if end_addr overflowed */ - if( (end_addr < block) && (len > 0) ){ - end_addr = (unsigned short *) ((CYG_ADDRESS) flash_info.end - 1); - } - -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Unlock from %p-%p: ", block, end_addr); + + for (dev = flash_head; + dev && !((dev->start <= flash_base) && ( dev->end > flash_base)); + dev=dev->next) + ; + if (!dev) return CYG_FLASH_ERR_INVALID; + if (!dev->funs->flash_block_lock) return CYG_FLASH_ERR_INVALID; + +#ifdef CYGPKG_KERNEL + cyg_mutex_lock(&dev->mutex); #endif - - HAL_FLASH_CACHES_OFF(d_cache, i_cache); - FLASH_Enable(block, end_addr); - while (block < end_addr) { - unsigned short *tmp_block; - stat = (*_flash_unlock_block)(block, flash_info.block_size, flash_info.blocks); - stat = flash_hwr_map_error(stat); - if (stat) { - *err_addr = (void *)block; - break; - } - - tmp_block = block + flash_info.block_size / sizeof(*block); - if(tmp_block < block){ - // If block address overflows, set block value to end on this loop - block = end_addr; - } - else{ - block = tmp_block; - } + addr = flash_base; + end_addr = flash_base + len - 1; + if (end_addr > dev->end) { + end_addr = dev->end; + } + + block_size = flash_block_size(dev, addr); + block = (cyg_flashaddr_t)((size_t)addr & ~(block_size - 1)); + #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); + dev->pf("... Unlocking from %p-%p: ", (void*)block, (void*)end_addr); #endif + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(block, end_addr); + while (block <= end_addr) { + stat = dev->funs->flash_block_unlock(dev,block); + stat = dev->funs->flash_hwr_map_error(dev,stat); + + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = (void *)block; + break; } - FLASH_Disable(block, end_addr); - HAL_FLASH_CACHES_ON(d_cache, i_cache); + block += flash_block_size(dev, addr); +#ifdef CYGSEM_IO_FLASH_CHATTER + dev->pf("."); +#endif + } + FLASH_Disable(block, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); #ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); + dev->pf("\n"); +#endif +#ifdef CYGPKG_KERNEL + cyg_mutex_unlock(&dev->mutex); #endif - return (stat); + if (stat != CYG_FLASH_ERR_OK) { + return stat; + } + + if (flash_base + len - 1 > dev->end) { // Off by one? + // The region to erase if bigger than this driver handles. Recurse + return cyg_flash_lock(dev->end+1, + len - (dev->end - flash_base) - 1, + err_address); + } + return CYG_FLASH_ERR_OK; } #endif -char * -flash_errmsg(int err) +const char * +cyg_flash_errmsg(const int err) { switch (err) { - case FLASH_ERR_OK: + case CYG_FLASH_ERR_OK: return "No error - operation complete"; - case FLASH_ERR_ERASE_SUSPEND: + case CYG_FLASH_ERR_ERASE_SUSPEND: return "Device is in erase suspend state"; - case FLASH_ERR_PROGRAM_SUSPEND: + case CYG_FLASH_ERR_PROGRAM_SUSPEND: return "Device is in program suspend state"; - case FLASH_ERR_INVALID: + case CYG_FLASH_ERR_INVALID: return "Invalid FLASH address"; - case FLASH_ERR_ERASE: + case CYG_FLASH_ERR_ERASE: return "Error trying to erase"; - case FLASH_ERR_LOCK: + case CYG_FLASH_ERR_LOCK: return "Error trying to lock/unlock"; - case FLASH_ERR_PROGRAM: + case CYG_FLASH_ERR_PROGRAM: return "Error trying to program"; - case FLASH_ERR_PROTOCOL: + case CYG_FLASH_ERR_PROTOCOL: return "Generic error"; - case FLASH_ERR_PROTECT: + case CYG_FLASH_ERR_PROTECT: return "Device/region is write-protected"; - case FLASH_ERR_NOT_INIT: + case CYG_FLASH_ERR_NOT_INIT: return "FLASH sub-system not initialized"; - case FLASH_ERR_DRV_VERIFY: + case CYG_FLASH_ERR_DRV_VERIFY: return "Data verify failed after operation"; - case FLASH_ERR_DRV_TIMEOUT: + case CYG_FLASH_ERR_DRV_TIMEOUT: return "Driver timed out waiting for device"; - case FLASH_ERR_DRV_WRONG_PART: + case CYG_FLASH_ERR_DRV_WRONG_PART: return "Driver does not support device"; - case FLASH_ERR_LOW_VOLTAGE: + case CYG_FLASH_ERR_LOW_VOLTAGE: return "Device reports low voltage"; default: return "Unknown error";
