diff options
Diffstat (limited to 'ecos/packages/devs/flash/intel/stratav2/current/src/strata.c')
-rw-r--r-- | ecos/packages/devs/flash/intel/stratav2/current/src/strata.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/ecos/packages/devs/flash/intel/stratav2/current/src/strata.c b/ecos/packages/devs/flash/intel/stratav2/current/src/strata.c new file mode 100644 index 0000000..1cbdc40 --- /dev/null +++ b/ecos/packages/devs/flash/intel/stratav2/current/src/strata.c @@ -0,0 +1,476 @@ +//========================================================================== +// +// strata.c +// +// Flash driver for the Intel Strata family +// +//========================================================================== +// ####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005, 2006 Free Software Foundation, Inc. +// +// 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 +// Software Foundation; either version 2 or (at your option) any later +// version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License +// along with eCos; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// As a special exception, if other files instantiate templates or use +// macros or inline functions from this file, or you compile this file +// and link it with other works to produce a work based on this file, +// this file does not by itself cause the resulting work to be covered by +// the GNU General Public License. However the source code for this file +// must still be made available in accordance with section (3) of the GNU +// General Public License v2. +// +// This exception does not invalidate any other reasons why a work based +// on this file might be covered by the GNU General Public License. +// ------------------------------------------- +// ####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): bartv +// Contributors: +// Date: 2005-06-11 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include <pkgconf/devs_flash_strata_v2.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/diag.h> +#include <cyg/io/flash.h> +#include <cyg/io/flash_dev.h> +#include <cyg/io/strata_dev.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_intr.h> +#include <cyg/hal/hal_cache.h> +#include <cyg/hal/hal_io.h> +#include <string.h> + +// This driver supports multiple banks of Intel Strata flash devices +// or compatibles. These are NOR-flash devices, requiring explicit +// erase operations with an erase value of 0xff. +// +// The devices may be 8-bit, 16-bit, or 32-bit (64-bit devices are not +// yet supported). Most but not all 16-bit devices can also be +// accessed as 8-bit, in which case the chip may be hooked up to an +// 8-bit bus. A bank of flash may involve just a single chip, or there +// may be several chips in parallel. Typical combinations are 88 to +// get 16-bit, 8888 for 32-bit, and 1616 for 32-bit. It is assumed +// that all chips within a bank are the same device. There may also be +// several banks of flash, and different banks may use different +// devices. +// +// This driver instantiates support for the various bus +// configurations: 8, 16, 16AS8, 32, 88, 8888, and 1616. On any given +// platform only one or two of these combinations will be of interest, +// but the remainder will be eliminated via linker garbage collection. +// To avoid excessive duplication an auxiliary file contains the +// actual implementations. Compiler optimization should eliminate any +// unnecessary code. + +// A flash driver is supposed to provide the following functions: +// int (*init)(...) +// size_t (*query)(...) +// int (*erase)(...) +// int (*program)(...) +// int (*hwr_map_error)(...) +// int (*block_lock)(...) +// int (*block_unlock)(...) +// +// The devices do not need any special initialization. However a given +// board may be manufactured with any one of several devices, which +// complicates things. The main complication is that there may be +// different bootsector layouts. The primary job of the init function +// is to check the device id, possibly fill in the bootsector info, +// or even to use the CFI support to get the bootsector info from the +// device itself. There may be other complications, e.g. minor variations +// of a given board design. These can be handled by h/w specific init +// functions in the platform HAL. +// +// The query function need not do anything useful, it is +// driver-defined. +// +// No read function need be supplied because the flash memory is +// always directly accessible to the cpu. +// +// The hwr_map_error is a no-op. +// +// Erase, program, and the locking functions need real +// implementations. + +// ---------------------------------------------------------------------------- +// The protocol understood by Strata flash chips and compatibles. The +// STRATA_PARALLEL() macro is used in bus configurations with multiple +// devices in parallel, to issue commands to all the devices in a +// single write. For CFI only one of the chips is queried. For READ_ID +// when getting the manufacturer and device id only a single device +// has to be queried, but when checking lock status all devices have +// to be checked. +#define STRATA_COMMAND_READ_ARRAY STRATA_SWAP(STRATA_PARALLEL(0x00FF)) +#define STRATA_COMMAND_READ_ID STRATA_SWAP(STRATA_PARALLEL(0x0090)) +#define STRATA_COMMAND_READ_STATUS STRATA_SWAP(STRATA_PARALLEL(0x0070)) +#define STRATA_COMMAND_CLEAR_STATUS STRATA_SWAP(STRATA_PARALLEL(0x0050)) +#define STRATA_COMMAND_PROGRAM_WORD STRATA_SWAP(STRATA_PARALLEL(0x0040)) +#define STRATA_COMMAND_WRITE_BUFFER STRATA_SWAP(STRATA_PARALLEL(0x00E8)) +#define STRATA_COMMAND_WRITE_CONFIRM STRATA_SWAP(STRATA_PARALLEL(0x00D0)) +#define STRATA_COMMAND_ERASE STRATA_SWAP(STRATA_PARALLEL(0x0020)) +#define STRATA_COMMAND_ERASE_CONFIRM STRATA_SWAP(STRATA_PARALLEL(0x00D0)) +#define STRATA_COMMAND_CFI STRATA_SWAP((0x0098)) +#define STRATA_COMMAND_CONFIGURATION STRATA_SWAP(STRATA_PARALLEL(0x00B8)) +#define STRATA_COMMAND_LOCK_BLOCK_0 STRATA_SWAP(STRATA_PARALLEL(0x0060)) +#define STRATA_COMMAND_LOCK_BLOCK_1 STRATA_SWAP(STRATA_PARALLEL(0x0001)) +#define STRATA_COMMAND_UNLOCK_BLOCK_0 STRATA_SWAP(STRATA_PARALLEL(0x0060)) +#define STRATA_COMMAND_UNLOCK_BLOCK_1 STRATA_SWAP(STRATA_PARALLEL(0x00D0)) +#define STRATA_COMMAND_UNLOCK_ALL_0 STRATA_SWAP(STRATA_PARALLEL(0x0060)) +#define STRATA_COMMAND_UNLOCK_ALL_1 STRATA_SWAP(STRATA_PARALLEL(0x00D0)) + +// CFI offsets of interest. This assumes that the standard query table +// has not been replaced by the extended query table, although the +// CFI standard allows that behaviour. +#define STRATA_OFFSET_CFI_Q STRATA_OFFSET_CFI_DATA(0x0010) +#define STRATA_OFFSET_CFI_SIZE STRATA_OFFSET_CFI_DATA(0x0027) +#define STRATA_OFFSET_CFI_WRITE_BUFFER_LSB STRATA_OFFSET_CFI_DATA(0x002A) +#define STRATA_OFFSET_CFI_WRITE_BUFFER_MSB STRATA_OFFSET_CFI_DATA(0x002B) +#define STRATA_OFFSET_CFI_BLOCK_REGIONS STRATA_OFFSET_CFI_DATA(0x002C) +#define STRATA_OFFSET_CFI_BLOCK_COUNT_LSB(_i_) STRATA_OFFSET_CFI_DATA(0x002D + (4 * (_i_))) +#define STRATA_OFFSET_CFI_BLOCK_COUNT_MSB(_i_) STRATA_OFFSET_CFI_DATA(0x002E + (4 * (_i_))) +#define STRATA_OFFSET_CFI_BLOCK_SIZE_LSB(_i_) STRATA_OFFSET_CFI_DATA(0x002F + (4 * (_i_))) +#define STRATA_OFFSET_CFI_BLOCK_SIZE_MSB(_i_) STRATA_OFFSET_CFI_DATA(0x0030 + (4 * (_i_))) + +#define STRATA_STATUS_SR7 STRATA_SWAP(STRATA_PARALLEL(0x0080)) +#define STRATA_STATUS_SR6 STRATA_SWAP(STRATA_PARALLEL(0x0040)) +#define STRATA_STATUS_SR5 STRATA_SWAP(STRATA_PARALLEL(0x0020)) +#define STRATA_STATUS_SR4 STRATA_SWAP(STRATA_PARALLEL(0x0010)) +#define STRATA_STATUS_SR3 STRATA_SWAP(STRATA_PARALLEL(0x0008)) +#define STRATA_STATUS_SR2 STRATA_SWAP(STRATA_PARALLEL(0x0004)) +#define STRATA_STATUS_SR1 STRATA_SWAP(STRATA_PARALLEL(0x0002)) +#define STRATA_STATUS_SR0 STRATA_SWAP(STRATA_PARALLEL(0x0001)) +#define STRATA_ID_LOCKED STRATA_SWAP(STRATA_PARALLEL(0x01)) + +// When programming the flash the source data may not be aligned +// correctly (although usually it will be). Hence it is necessary to +// construct the 16-bit or 32-bit numbers to be written to the flash +// from individual bytes, allowing for endianness. +#define STRATA_NEXT_DATUM_8(_ptr_) (*_ptr_++) +#if CYG_BYTEORDER == CYG_LSBFIRST +# define STRATA_NEXT_DATUM_16(_ptr_) \ + ({ \ + cyg_uint16 _result_; \ + _result_ = (_ptr_[1] << 8) | _ptr_[0]; \ + _ptr_ += 2; \ + _result_; }) + +# define STRATA_NEXT_DATUM_32(_ptr_) \ + ({ \ + cyg_uint32 _result_; \ + _result_ = (_ptr_[3] << 24) | (_ptr_[2] << 16) | (_ptr_[1] << 8) | _ptr_[0]; \ + _ptr_ += 4; \ + _result_; }) +#else +# define STRATA_NEXT_DATUM_16(_ptr_) \ + ({ \ + cyg_uint16 _result_; \ + _result_ = (_ptr_[0] << 8) | _ptr_[1]; \ + _ptr_ += 2; \ + _result_; }) + +# define STRATA_NEXT_DATUM_32(_ptr_) \ + ({ \ + cyg_uint32 _result_; \ + _result_ = (_ptr_[0] << 24) | (_ptr_[1] << 16) | (_ptr_[2] << 8) | _ptr_[3]; \ + _ptr_ += 4; \ + _result_; }) + +#endif + +// The addresses used for programming the flash may be different from +// the ones used to read the flash. The macro +// HAL_STRATA_UNCACHED_ADDRESS() can be supplied by one of the HAL +// packages. Otherwise if CYGHWR_DEVS_FLASH_STRATA_V2_CACHED_ONLY +// is not implemented then the macro CYGARC_UNCACHED_ADDRESS() +// will be used. If there is no way of bypassing the cache then +// the addresses will remain unchanged and instead the INTSCACHE +// macros will disable the cache. +#if defined(HAL_STRATA_UNCACHED_ADDRESS) +# define STRATA_UNCACHED_ADDRESS(_addr_) (volatile STRATA_TYPE*)HAL_STRATA_UNCACHED_ADDRESS(_addr_) +#elif !defined(CYGHWR_DEVS_FLASH_STRATA_V2_CACHED_ONLY) +# ifndef CYGARC_UNCACHED_ADDRESS +# error Cache should be bypassed but CYGARC_UNCACHED_ADDRESS is not defined. +# endif +# define STRATA_UNCACHED_ADDRESS(_addr_) (volatile STRATA_TYPE*)CYGARC_UNCACHED_ADDRESS(_addr_) +#else +# define STRATA_UNCACHED_ADDRESS(_addr_) (volatile STRATA_TYPE*)(_addr_) +#endif + +// The bits on the data bus may need swapping, either because of +// endianness issues or because some lines are just wired wrong. +// SWAP is for commands going to the flash chip. UNSWAP is for +// data coming back from the flash chip. The swapping takes +// effect after allowing for STRATA_PARALLEL(). Data is never +// swapped, it does not matter if bit 5 of a datum is actually +// stored in bit 3 of the flash as long as the data reads back +// right. +#if defined(HAL_STRATA_SWAP) +# define STRATA_SWAP(_data_) HAL_STRATA_SWAP(_data_) +#else +# define STRATA_SWAP(_data_) (_data_) +#endif +#if defined(HAL_STRATA_UNSWAP) +# define STRATA_UNSWAP(_data_) HAL_STRATA_UNSWAP(_data_) +#else +# define STRATA_UNSWAP(_data_) (_data_) +#endif + +// Cache and interrupt manipulation. This driver supports fine-grained +// control over interrupts and the cache, using three macros. These may +// be provided by the platform HAL, or by defaults here. There are +// three variants: +// +// 1) control both interrupts and cache, needed if +// CYGHWR_DEVS_FLASH_STRATA_V2_CACHED_ONLY is implemented i.e. if it +// is necessary to disable the cache to get direct access to the flash. +// 2) control interrupts only, the default if the cache can be bypassed +// when accessing the flash. +// 3) do nothing, if the cache can be bypassed and the application +// guarantees that the flash will not be accessed by any interrupt +// handlers or other threads. + +#if defined(CYGHWR_DEVS_FLASH_STRATA_V2_CACHED_ONLY) + +// First, the amount of state that should be preserved. By default +// this means the interrupt state and the data cache state. +# define STRATA_INTSCACHE_DEFAULT_STATE int _saved_ints_, _saved_dcache_ + +// Start an operation on the flash. Make sure that interrupts are +// disabled and then save the current state of the data cache. The +// actual flash manipulation should happen with the cache disabled. +// There may still be data in the cache that has not yet been flushed +// to memory, so take care of that first. Then invalidate the cache +// lines so that when the cache is re-enabled later on the processor +// gets everything from memory, rather than reusing old data in the +// cache. +# define STRATA_INTSCACHE_DEFAULT_BEGIN() \ + CYG_MACRO_START \ + HAL_DISABLE_INTERRUPTS(_saved_ints_); \ + HAL_DCACHE_IS_ENABLED(_saved_dcache_); \ + HAL_DCACHE_SYNC(); \ + if (_saved_dcache_) { \ + HAL_DCACHE_DISABLE(); \ + } \ + HAL_DCACHE_INVALIDATE_ALL(); \ + CYG_MACRO_END + +// A flash operation has completed. Restore the situation to what it +// was before. Because of suspend/resume support interrupt handlers +// and other threads may have run, filling various cache lines with +// useful data. However it is assumed that none of those cache +// lines contain any of the data that has been manipulated by this +// flash operation (the stack and the flash block), so there is +// no need for another sync or invalidate. It is also assumed that +// we have not been executing any code out of the block of flash +// that has just been erased or programmed, so no need to worry +// about the icache. +#define STRATA_INTSCACHE_DEFAULT_END() \ + CYG_MACRO_START \ + if (_saved_dcache_) { \ + HAL_DCACHE_ENABLE(); \ + } \ + HAL_RESTORE_INTERRUPTS(_saved_ints_); \ + CYG_MACRO_END + +#elif !defined(CYGIMP_DEVS_FLASH_STRATA_V2_LEAVE_INTERRUPTS_ENABLED) + +# define STRATA_INTSCACHE_DEFAULT_STATE int _saved_ints_ +# define STRATA_INTSCACHE_DEFAULT_BEGIN() HAL_DISABLE_INTERRUPTS(_saved_ints_) +// The following blips the interrupt enable to allow pending interrupts +// to run, which will reduce interrupt latency given the dcache sync/invalidate +// may be relatively lengthy. +# define STRATA_INTSCACHE_DEFAULT_END() \ + CYG_MACRO_START \ + HAL_RESTORE_INTERRUPTS(_saved_ints_); \ + HAL_DISABLE_INTERRUPTS(_saved_ints_); \ + HAL_DCACHE_SYNC(); \ + HAL_DCACHE_INVALIDATE_ALL(); \ + HAL_RESTORE_INTERRUPTS(_saved_ints_); \ + CYG_MACRO_END + +#else + +# define STRATA_INTSCACHE_DEFAULT_STATE CYG_EMPTY_STATEMENT +# define STRATA_INTSCACHE_DEFAULT_BEGIN() CYG_EMPTY_STATEMENT +# define STRATA_INTSCACHE_DEFAULT_END() \ + CYG_MACRO_START \ + int _saved_ints_; \ + HAL_DISABLE_INTERRUPTS(_saved_ints_); \ + HAL_DCACHE_SYNC(); \ + HAL_DCACHE_INVALIDATE_ALL(); \ + HAL_RESTORE_INTERRUPTS(_saved_ints_); \ + CYG_MACRO_END + +#endif + +#ifdef HAL_STRATA_INTSCACHE_STATE +# define STRATA_INTSCACHE_STATE HAL_STRATA_INTSCACHE_STATE +#else +# define STRATA_INTSCACHE_STATE STRATA_INTSCACHE_DEFAULT_STATE +#endif +#ifdef HAL_STRATA_INTSCACHE_BEGIN +# define STRATA_INTSCACHE_BEGIN HAL_STRATA_INTSCACHE_BEGIN +#else +# define STRATA_INTSCACHE_BEGIN STRATA_INTSCACHE_DEFAULT_BEGIN +#endif +#ifdef HAL_STRATA_INTSCACHE_END +# define STRATA_INTSCACHE_END HAL_STRATA_INTSCACHE_END +#else +# define STRATA_INTSCACHE_END STRATA_INTSCACHE_DEFAULT_END +#endif + +// Some HALs require a special instruction to flush write buffers. +// Not all HALs do though, so we define it empty if it isn't already present. +#ifndef HAL_MEMORY_BARRIER +# define HAL_MEMORY_BARRIER() CYG_EMPTY_STATEMENT +#endif + +// ---------------------------------------------------------------------------- +// Generic code. + +// Get info about the current block, i.e. base and size. +static void +strata_get_block_info(struct cyg_flash_dev* dev, const cyg_flashaddr_t addr, cyg_flashaddr_t* block_start, size_t* block_size) +{ + cyg_uint32 i; + size_t offset = addr - dev->start; + cyg_flashaddr_t result; + + result = dev->start; + + for (i = 0; i < dev->num_block_infos; i++) { + if (offset < (dev->block_info[i].blocks * dev->block_info[i].block_size)) { + offset -= (offset % dev->block_info[i].block_size); + *block_start = result + offset; + *block_size = dev->block_info[i].block_size; + return; + } + result += (dev->block_info[i].blocks * dev->block_info[i].block_size); + offset -= (dev->block_info[i].blocks * dev->block_info[i].block_size); + } + CYG_FAIL("Address out of range of selected flash device"); +} + +// ---------------------------------------------------------------------------- +// Instantiate all of the h/w functions appropriate for the various +// configurations. +// The suffix is used to construct the function names. +// Types for the width of the bus, controlling the granularity of access. +// devcount specifies the number of devices in parallel, and is used for looping +// The NEXT_DATUM() macro allows for misaligned source data. +// The PARALLEL macro, if defined, is used for sending commands and reading +// status bits from all devices in the bank in one operation. + +// A single 8-bit device on an 8-bit bus. +#define STRATA_SUFFIX 8 +#define STRATA_TYPE cyg_uint8 +#define STRATA_DEVCOUNT 1 +#define STRATA_NEXT_DATUM(_ptr_) STRATA_NEXT_DATUM_8(_ptr_) + +#include "strata_aux.c" + +#undef STRATA_SUFFIX +#undef STRATA_TYPE +#undef STRATA_DEVCOUNT +#undef STRATA_NEXT_DATUM + +// A single 16-bit device. +#define STRATA_SUFFIX 16 +#define STRATA_TYPE cyg_uint16 +#define STRATA_DEVCOUNT 1 +#define STRATA_NEXT_DATUM(_ptr_) STRATA_NEXT_DATUM_16(_ptr_) + +#include "strata_aux.c" + +#undef STRATA_SUFFIX +#undef STRATA_TYPE +#undef STRATA_DEVCOUNT +#undef STRATA_NEXT_DATUM + +// A single 32-bit device. +#define STRATA_SUFFIX 32 +#define STRATA_TYPE cyg_uint32 +#define STRATA_DEVCOUNT 1 +#define STRATA_NEXT_DATUM(_ptr_) STRATA_NEXT_DATUM_32(_ptr_) + +#include "strata_aux.c" + +#undef STRATA_SUFFIX +#undef STRATA_TYPE +#undef STRATA_DEVCOUNT +#undef STRATA_NEXT_DATUM + +// Two 8-bit devices, giving a 16-bit bus. +#define STRATA_SUFFIX 88 +#define STRATA_TYPE cyg_uint16 +#define STRATA_DEVCOUNT 2 +#define STRATA_NEXT_DATUM(_ptr_) STRATA_NEXT_DATUM_16(_ptr_) +#define STRATA_PARALLEL(_cmd_) ((_cmd_ << 8) | _cmd_) + +#include "strata_aux.c" + +#undef STRATA_SUFFIX +#undef STRATA_TYPE +#undef STRATA_DEVCOUNT +#undef STRATA_NEXT_DATUM + +// Four 8-bit devices, giving a 32-bit bus. +#define STRATA_SUFFIX 8888 +#define STRATA_TYPE cyg_uint32 +#define STRATA_DEVCOUNT 4 +#define STRATA_NEXT_DATUM(_ptr_) STRATA_NEXT_DATUM_32(_ptr_) +#define STRATA_PARALLEL(_cmd_) ((_cmd_ << 24) | (_cmd_ << 16) | (_cmd_ << 8) | _cmd_) + +#include "strata_aux.c" + +#undef STRATA_SUFFIX +#undef STRATA_TYPE +#undef STRATA_DEVCOUNT +#undef STRATA_NEXT_DATUM + +// Two 16-bit devices, giving a 32-bit bus. +#define STRATA_SUFFIX 1616 +#define STRATA_TYPE cyg_uint32 +#define STRATA_DEVCOUNT 2 +#define STRATA_NEXT_DATUM(_ptr_) STRATA_NEXT_DATUM_32(_ptr_) +#define STRATA_PARALLEL(_cmd_) ((_cmd_ << 16) | _cmd_) + +#include "strata_aux.c" + +#undef STRATA_SUFFIX +#undef STRATA_TYPE +#undef STRATA_DEVCOUNT +#undef STRATA_NEXT_DATUM + +// 16AS8. A 16-bit device hooked up so that only byte accesses are +// allowed. This requires unusual offsets for the CFI and query data. +#define STRATA_SUFFIX 16as8 +#define STRATA_TYPE cyg_uint8 +#define STRATA_DEVCOUNT 1 +#define STRATA_NEXT_DATUM(_ptr_) STRATA_NEXT_DATUM_8(_ptr_) +#define STRATA_OFFSET_MANUFACTURER_ID 00 +#define STRATA_OFFSET_DEVICE_ID 02 +#define STRATA_OFFSET_LOCK_STATUS 04 +#define STRATA_OFFSET_CFI_DATA(_idx_) (2 * (_idx_)) + +#include "strata_aux.c" |