# HG changeset patch # User jlarmour # Date 1226969764 0 # Node ID a2f5434df2cb79fd017284b6cb7de218e88b7499 # Parent e38c9880829526c222b999901ddc95318cd61e2b Merge flashv2 branch to trunk, with eCosCentric additions diff --git a/packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog b/packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog @@ -0,0 +1,215 @@ +2008-09-24 Bart Veer + + * src/am29xxxxx_aux.c (am29_hw_program): fix handling of parallel + chips. Get the debug diagnostics to build cleanly. + +2008-05-13 Jonathan Larmour + + * src/am29xxxxx_aux.c (am29_hw_query): Allow devices to return + device IDs of larger than a byte. + +2007-07-08 Bart Veer + + * src/am29xxxxx.c, src/am29xxxxx_aux.c: add 2ram entry and exit + hooks to give HALs some extra control over the low-level flash + operations. + +2007-06-21 Bart Veer + + * src/am29xxxxx_aux.c: issue a memory barrier after every command + that gets sent to the flash, to avoid problems with pipelines that + mess about with write ordering. + +2007-05-31 Bart Veer + + * src/am29xxxxx.c: skip data cache manipulation on architectures + which do not have a data cache. + +2007-05-29 Jonathan Larmour + + * cdl/flash_am29xxxxx_v2.cdl + (CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME): New option. + Allow Flash devices to indicate that they should be sent a "resume" + command on startup. + * src/am29xxxxx_aux.c (am29_hw_force_all_suspended_resume): New + function to implement above CDL. + (cyg_am29xxxxx_init_check_dev): Call the new function. + (cyg_am29xxxxx_init_cfi): Ditto. + (am29_hw_cfi): manufacturer_id and vendor private areas need only + lowest byte taken, to handle multiple parallel devices. + +2007-03-29 Bart Veer + + * src/am29xxxxx_aux.c (am29_hw_erase): use DQ7 to detect + termination instead of DQ6. This avoids hardware problems where + the toggling of DQ6 is not detected. + +2007-03-05 Nick Garnett + + * src/am29xxxxx_aux.c (am29_hw_cfi): Added missing AM29_SWAP() to + fetch of offset of vendor specific data. + +2006-09-27 Nick Garnett + + * src/am29xxxxx_aux.c (am29_hw_cfi): Added generic code to deal + with flash parts that may specify their erase regions in reverse + order to reality. Removed older code that was too specific to + particular devices. + A few changes to the diagnostics in various places. + + * cdl/flash_am29xxxxx_v2.cdl: Replaced + CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_AT49_CFI with + CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CFI_BOGOSITY. This option + performs much the same purpose, but is generic to many more flash + parts. + +2006-08-31 Nick Garnett + + * src/am29xxxxx_aux.c: Added support for ST flash parts which have + similar, but not identical, peculiarities in their CFI data to the + Atmel parts supported earlier. + +2006-08-24 Jonathan Larmour + + * src/am29xxxxx.c: Provide a default HAL_MEMORY_BARRIER() + define if the HAL hasn't provided one. + * src/am29xxxxx_aux.c: Use HAL_MEMORY_BARRIER() any time the + flash is reset back to read array mode. Some processors + need to have their write buffers flushed. + +2006-06-29 Nick Garnett + + * cdl/flash_am29xxxxx_v2.cdl: + * src/am29xxxxx_aux.c: + Added configurable delay between each word during + programming. Some targets seem to need this. + Also added some simple diagnostics. + +2006-05-15 John Dallaway + + * doc/am29xxxxx.sgml: Fix missing tag. + * cdl/flash_am29xxxx_v2.cdl: Reference package documentation. + +2005-09-26 Bart Veer + + * src/am29xxxxx.c, src/am29xxxxx_aux.c: add AM29_SWAP() support for + platforms where the bus connection is wired up strangely. + + * src/am29xxxxx_aux.c, cdl/flash_am29xxxxx_v2.cdl: add optional + support for certain Atmel chips which have a peculiar + implementation of CFI. + +2005-08-17 Jonathan Larmour + + * cdl/flash_am29xxxx_v2.cdl: Provide new + CYGIMP_DEVS_FLASH_AMD_AM29XXXXX_V2_LEAVE_INTERRUPTS_ENABLED + and CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CACHED_ONLY options + to provide better control of when to enable/disable interrupts + or caches. Taken from the Strata flash example. + * include/am29xxxxx_dev.h: Declare cyg_at49xxxx_softlock, _hardlock + and _unlock variants. + * src/am29xxxxx.c: Define AT49 locking commands. + Provide AM29_UNCACHE_ADDRESS in line with the Strata driver, + to be controlled by the above new CDL options, in place of + AM29_P2V. + * src/am29xxxxx_aux.c: Provide AT49 lock status definition. + Provide AT49 softlock/hardlock/unlock API functions and + underlying implementation functions. + Adopt AM29_UNCACHED_ADDRESS rename. + * doc/am29xxxxx.sgml: Update documentation to cover AT49xxxx + locking operations and above new CDL options. + +2005-06-12 Bart Veer + + * src/am29xxxxx_aux.c (cyg_am29xxxxx_program): remove unnecessary + loops variable + * cdl/flash_am29xxxxx_v2.cdl: minor reorganization to put the + important config option first + * doc/am29xxxxx.sgml: document the important config options + +2005-02-23 Bart Veer + + * doc/am29xxxxx.sgml: bring up to date. + +2005-01-19 Jonathan Larmour + + * src/am29xxxxx_aux.c (am29_hw_erase): Handle interleaved + (parallel) flash correctly when one device finishes before another. + (am29_hw_program): Similar. + (cyg_am29xxxxx_program): Use assert correctly. + + * src/am29xxxxx.c (AM29_NEXT_DATUM_32): Use cyg_uint32, not + cyg_uint16. + +2004-12-02 Bart Veer + + * src/am29xxxxx.c, include/am29xxxxx_dev.h: + no longer exists, use instead. + +2004-11-29 Bart Veer + + * include/am29xxxxx_dev.h, src/am29xxxxx.c: eliminate + hwr_map_error() support, no longer needed + + * include/am29xxxxx_dev.h, src/am29xxxxx.c, src/am29xxxxx_aux.c: + The dummy init/query/lock/unlock functions have been moved to the + generic flash package. That also now exports an anonymizer + function. + +2004-11-25 Bart Veer + + * cdl/flash_am29xxxxx_v2.cdl, src/am29xxxxx.c, + src/am29xxxxx_aux.c: this driver can manage the flash and + interrupts itself, without support from the generic flash code. + +2004-11-22 Bart Veer + + * include/am29xxxxx_dev.h, src/am29xxxxx.c, src/am29xxxxx_aux.c, + doc/am29xxxxx.sgml: implement changes to the interface between the + generic flash code and the device drivers. + * include/am29xxxxx_dev.h: rename cyg_block_info to + cyg_flash_block_info + +2004-11-21 Bart Veer + + * doc/am29xxxxx.sgml: describe CDL implications for custom locking + functions and additional devices. + + * cdl/flash_am29xxxxx_v2.cdl: CYGHWR_IO_FLASH_DEVICE_V2 is now + implicit + +2004-11-05 Bart Veer + + * New AM29xxxxx flash driver created + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004, 2005, 2006, 2007, 2008 eCosCentric Limited +// +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// 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#### +//=========================================================================== diff --git a/packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl b/packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl @@ -0,0 +1,220 @@ +# ==================================================================== +# +# flash_am29xxxxx_v2.cdl +# +# Device driver for AMD am29xxxxx flash chips and compatibles +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2004, 2005, 2006, 2007 eCosCentric Ltd +## +## 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., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +## +## 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: 2004-11-05 +# +#####DESCRIPTIONEND#### +# ==================================================================== + +cdl_package CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2 { + display "AMD am29xxxxx flash memory support" + doc ecospro-ref/devs-flash-am29xxxxx.html + parent CYGPKG_IO_FLASH + active_if CYGPKG_IO_FLASH + implements CYGHWR_IO_FLASH_DEVICE + include_dir cyg/io + compile am29xxxxx.c + + description " + Flash memory support for AMD AM29xxxxx devices and compatibles. + This driver implements the V2 flash driver API" + + cdl_option CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT { + display "Maximum number of iterations during a write" + flavor data + default_value 100000000 + legal_values 1024 to 0x7fffffff + description " + Flash program operations may take a long time, and the driver + needs to poll the device to detect when the operation has + completed. This option controls the maximum number of iterations + of the polling loop, before the driver will give up. The timeout + should never actually trigger, as long as the hardware is + functioning correctly. If a timeout does occur the flash device + may be left in an inconsistent state. If very slow flash devices + are used then the platform HAL may require a larger timeout." + } + + cdl_option CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_DELAY { + display "Delay between words while programming" + flavor data + default_value 0 + legal_values 0 to 0x7fffffff + description "The timings of certain processors and flash devices mean that + a short delay may be required between each word as it is programmed. + This option defines that delay in terms of iterations of a delay + loop." + } + + cdl_option CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_TIMEOUT { + display "Maximum number of iterations during a block erase" + flavor data + default_value 100000000 + legal_values 1024 to 0x7fffffff + description " + The driver needs to poll the flash device during a block erase + to detect when the operation has completed. This option controls + the maximum number of iterations of the polling loop, before the + driver will give up. The timeout should never actually trigger, + as long as the hardware is functioning correctly. If a timeout + does occur the flash device may be left in an inconsistent state. + If very slow flash devices are used then the platform HAL may + require a larger timeout." + } + + cdl_option CYGIMP_DEVS_FLASH_AMD_AM29XXXXX_V2_LEAVE_INTERRUPTS_ENABLED { + display "Leave interrupts enabled during flash operations" + active_if { ! CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CACHED_ONLY } + default_value 0 + description " + On typical hardware erasing or programming a flash block requires + disabling interrupts and possibly the cache. During these operations + some or all of the flash hardware will be unusable, and disabling + interrupts is the only reliable way of ensuring that no interrupt + handler or other thread will try to access the flash in the middle + of the operation. This can have a major impact on the real-time + responsiveness of typical applications. + + In some circumstances it is possible to leave interrupts enabled. + The application must run in RAM, not in flash. There must be some + way of accessing the flash which bypasses the cache. The application + must only access the flash using the proper API, for example + cyg_flash_read(), which ensures that only one thread at a time can + access a flash device. Finally there must be no possibility of + entering a ROM monitor running in flash. This can happen if RedBoot + is used as the ROM monitor and virtual vectors are enabled. It can + also happen when debugging the application via RedBoot or gdb stubs. + + If the application can absolutely guarantee that the flash will not be + accessed during a flash operation then it is possible to enable this option, + improving interrupt latency. Any unexpected flash accesses are likely + to cause a system crash. If in doubt leave this option disabled." + } + + cdl_interface CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CACHED_ONLY { + display "Flash memory accesses are always cached" + flavor bool + description " + Flash memory devices are usually accessed via the cache to achieve + acceptable performance. However erase and program operations need + to access the flash directly, bypassing the cache. On some targets + it is possible to access the flash in an uncached part of the + address space, for example by suitable MMU settings. On other + targets it is necessary to disable the cache while erasing or + programming blocks of flash. In the latter case the platform HAL + will implement this interface." + } + + cdl_interface CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CFI_BOGOSITY { + display "Include support for unusual CFI implementation" + flavor bool + description " + CFI, Common Flash Interface, is a standard allowing device drivers + to query the hardware for characteristics such as the erase region + layout. Some flash chips have a somewhat strange implementation of CFI, + requiring extra code within the device driver. If CFI is used for device + initialization and if the platform may come with one of these flash chips + then the platform HAL will implement this interface." + } + + cdl_interface CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME { + display "Erase/program resume is needed after reset" + flavor bool + description " + With true AMD-compatible flash parts, a reset command will also + abort any suspended erase or program operations. But on some + parts which are nearly but not quite compatible, such as AT49xxxxx, + this does not happen, and so an erase/program resume command is + needed after a soft reset in order to be able to use the chip." + } + + cdl_option CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS { + display "Number of different erase regions" + flavor data + default_value 4 + legal_values 1 to 64 + description " + Flash devices vary widely in the way the various flash blocks are + laid out. In uniform devices all flash blocks are the same size, + for example 64 blocks of 64K each. Other devices have a boot block, + where one of the big blocks is subdivided into a number of smaller + ones. For example there could be a 16K block, followed by two 8K blocks, + then a 32K block, and finally 63 64K blocks. Each sequence of blocks + of a given size is known as an erase region, so a uniform device has + a single erase region and the above boot block device has four + erase regions. The driver needs to know the maximum number of erase + regions that may be present, especially if CFI is used to determine + the block details at run-time. Typically this option is controlled + by a requires property in the platform HAL, so users do not need + to edit it." + } + + cdl_component CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2_OPTIONS { + display "AMD AM29xxxxx driver build options" + flavor none + description " + Package specific build options including control over + compiler flags used only in building the AMD am29xxxxx + flash driver, and details of which tests are built." + + cdl_option CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the AMD am29xxxxx flash driver. These flags + are used in addition to the set of global flags." + } + + cdl_option CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2_CFLAGS_REMOVE { + display "Suppressed compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the AMD am29xxxxx flash driver. These flags + are removed from the set of global flags if present." + } + } +} diff --git a/packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml b/packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml @@ -0,0 +1,791 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +AMD AM29xxxxx Flash Device Driver + + + + Overview + + + Overview + eCos Support for AMD AM29xxxxx Flash Devices and Compatibles + + + Description + +The CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2 AMD +AM29xxxxx V2 flash driver package implements support for the AM29xxxxx +family of flash devices and compatibles. Normally the driver is not +accessed directly. Instead application code will use the API provided +by the generic flash driver package +CYGPKG_IO_FLASH, for example by calling functions +like cyg_flash_program. + + +The driver imposes one restriction on application code which +developers should be aware of: when programming the flash the +destination addresses must be aligned to a bus boundary. For example +if the target hardware has a single flash device attached to a 16-bit +bus then program operations must involve a multiple of 16-bit values +aligned to a 16-bit boundary. Note that it is the bus width that +matters, not the device width. If the target hardware has two 16-bit +devices attached to a 32-bit bus then program operations must still be +aligned to a 32-bit boundary, even though in theory a 16-bit boundary +would suffice. In practice this is rarely an issue, and requiring the +larger boundary greatly simplifies the code and improves performance. + + +Many eCos targets with AM29xxxxx or compatible flash devices will +still use the older driver package +CYGPKG_DEVS_FLASH_AMD_AM29XXXXX. Only newer ports +and some older ports that have been converted will use the V2 driver. +This documentation only applies to the V2 driver. + + + + Configuration Options + +The AM29xxxxx flash driver package will be loaded automatically when +configuring eCos for a target with suitable hardware. However the +driver will be inactive unless the generic flash package +CYGPKG_IO_FLASH is loaded. It may be necessary to +add this generic package to the configuration explicitly before the +driver functionality becomes available. There should never be any need +to load or unload the AM29xxxx driver package. + + +There are a number of configuration options, relating mostly to hardware +characteristics. It is very rare that application developers need to +change any of these. For example the option +CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS +may need a non-default value if the flash devices used on the target +have an unusual boot block layout. If so the platform HAL will impose +a requires constraint on this option and the configuration system will +resolve the constraint. The only time it might be necessary to change +the value manually is if the actual board being used is a variant of +the one supported by the platform HAL and uses a different flash chip. + + + + + + + Instantiating an AM29xxxxx Device + + + Instantiating + including the driver in an eCos target + + + + +#include <cyg/io/am29xxxxx_dev.h> + + + int cyg_am29xxxxx_init_check_devid_XX + struct cyg_flash_dev* device + + + int cyg_am29xxxxx_init_cfi_XX + struct cyg_flash_dev* device + + + int cyg_am29xxxxx_erase_XX + struct cyg_flash_dev* device + cyg_flashaddr_t addr + + + int cyg_am29xxxxx_program_XX + struct cyg_flash_dev* device + cyg_flashaddr_t addr + const void* data + size_t len + + + int cyg_at49xxxx_softlock + struct cyg_flash_dev* device + const cyg_flashaddr_t addr + + + int cyg_at49xxxx_hardlock + struct cyg_flash_dev* device + const cyg_flashaddr_t addr + + + int cyg_at49xxxx_unlock + struct cyg_flash_dev* device + const cyg_flashaddr_t addr + + + int cyg_am29xxxxx_read_devid_XX + struct cyg_flash_dev* device + + + + + Description + +The AM29xxxxx family contains some hundreds of different flash +devices, all supporting the same basic set of operations but with +various common or uncommon extensions. The devices vary in capacity, +performance, boot block layout, and width. There are also +platform-specific issues such as how many devices are actually present +on the board and where they are mapped in the address space. The +AM29xxxxx driver package cannot know the details of every chip and +every platform. Instead it is the responsibility of another package, +usually the platform HAL, to supply the necessary information by +instantiating some data structures. Two pieces of information are +especially important: the bus configuration and the boot block layout. + + +Flash devices are typically 8-bits, 16-bits, or 32-bits wide (64-bit +devices are not yet in common use). Most 16-bit devices will also +support 8-bit accesses, but not all. Similarly 32-bit devices can be +accessed 16-bits at a time or 8-bits at a time. A board will have one +or more of these devices on the bus. For example there may be a single +16-bit device on a 16-bit bus, or two 16-bit devices on a 32-bit bus. +The processor's bus logic determines which combinations are possible, +and there will be a trade off between cost and performance: two 16-bit +devices in parallel can provide twice the memory bandwidth of a single +device. The driver supports the following combinations: + + + + 8 + +A single 8-bit flash device on an 8-bit bus. + + + + 16 + +A single 16-bit flash device on a 16-bit bus. + + + + 32 + +A single 32-bit flash device on an 32-bit bus. + + + + 88 + +Two parallel 8-bit devices on an 16-bit bus. + + + + 8888 + +Four parallel 8-bit devices on a 32-bit bus. + + + + 1616 + +Two parallel 16-bit devices on a 32-bit bus, with one device providing +the bottom two bytes of each 32-bit datum and the other device +providing the top two bytes. + + + + 16as8 + +A single 16-bit flash device connected to an 8-bit bus. + + + + +These configuration all require slightly different code to manipulate +the hardware. The AM29xxxxx driver package provides separate functions +for each configuration, for example +cyg_am29xxxxx_erase_16 and +cyg_am29xxxxx_program_1616. + + +At the time of writing not all the configurations have been tested. + + +The second piece of information is the boot block layout. Flash +devices are subdivided into blocks (also known as sectors - both terms +are in common use). Some operations such as erase work on a whole +block at a time, and for most applications a block is the smallest +unit that gets updated. A typical block size is 64K. It is inefficient +to use an entire 64K block for small bits of configuration data and +similar information, so many flash devices also support a number of +smaller boot blocks. A typical 2MB flash device could have a single +16K block, followed by two 8K blocks, then a 32K block, and finally 31 +full-size 64K blocks. The boot blocks may appear at the bottom or the +top of the device. So-called uniform devices do not have boot blocks, +just full-size ones. The driver needs to know the boot block layout. +With modern devices it can work this out at run-time, but often it is +better to provide the information statically. + + + + Example + +In most cases flash support is specific to a platform. Even if two +platforms happen to use the same flash device there are likely to be +differences such as the location in the address map. Hence there is +little possibility of re-using the platform-specific code, and this +code should be placed in the platform HAL rather than in a separate +package. Typically this involves a separate file and a corresponding +compile property in the platform HAL's CDL: + + +cdl_package CYGPKG_HAL_M68K_ALAIA { + … + compile -library=libextras.a alaia_flash.c + … +} + + +The contents of this file will not be accessed directly, only +indirectly via the generic flash API, so normally it would be removed +by link-time garbage collection. To avoid this the object file has to +go into libextras.a. + + +The actual file alaia_flash.c will look something like: + + +#include <pkgconf/system.h> +#ifdef CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2 + +#include <cyg/io/flash.h> +#include <cyg/io/flash_dev.h> +#include <cyg/io/am29xxxxx_dev.h> + +static const CYG_FLASH_FUNS(hal_alaia_flash_amd_funs, + &cyg_am29xxxxx_init_check_devid_16, + &cyg_flash_devfn_query_nop, + &cyg_am29xxxxx_erase_16, + &cyg_am29xxxxx_program_16, + (int (*)(struct cyg_flash_dev*, const cyg_flashaddr_t, void*, size_t))0, + &cyg_flash_devfn_lock_nop, + &cyg_flash_devfn_unlock_nop); + +static const cyg_am29xxxxx_dev hal_alaia_flash_priv = { + .devid = 0x45, + .block_info = { + { 0x00004000, 1 }, + { 0x00002000, 2 }, + { 0x00008000, 1 }, + { 0x00010000, 63 } + } +}; + +CYG_FLASH_DRIVER(hal_alaia_flash, + &hal_alaia_flash_amd_funs, + 0, + 0xFFC00000, + 0xFFFFFFFF, + 4, + hal_alaia_flash_priv.block_info, + &hal_alaia_flash_priv +); +#endif + + +The bulk of the file is protected by an #ifdef for +the AM29xxxxx flash driver. That driver will only be active if the +generic flash support is enabled. Without that support there will be +no way of accessing the device so instantiating the data structures +would serve no purpose. The rest of the file is split into three +structure definitions. The first supplies the functions which will be +used to perform the actual flash accesses, using a macro provided by +the generic flash code in cyg/io/flash_dev.h. The relevant ones +have an _16 suffix, indicating that on this board +there is a single 16-bit flash device on a 16-bit bus. The second +provides information specific to AM29xxxxx flash devices. +The third provides the cyg_flash_dev +structure needed by the generic flash code, which contains pointers to +the previous two. + + + + Functions + +All eCos flash device drivers must implement a standard interface, +defined by the generic flash code CYGPKG_IO_FLASH. +This interface includes a table of seven function pointers for various +operations: initialization, query, erase, program, read, locking and +unlocking. The query operation is optional and the generic flash +support provides a dummy implementation +cyg_flash_devfn_query_nop. AM29xxxxx flash +devices are always directly accessible so there is no need for a +separate read function. The remaining functions are more complicated. + + +Usually the table can be declared const. In a ROM +startup application this avoids both ROM and RAM copies of the table, +saving a small amount of memory. const should not +be used if the table may be modified by a platform-specific +initialization routine. + + + Initialization + +There is a choice of three main initialization functions. The simplest +is cyg_flash_devfn_init_nop, which does nothing. +It can be used if the cyg_am29xxxxx_dev and +cyg_flash_dev structures are fully +initialized statically and the flash will just work without special +effort. This is useful if it is guaranteed that the board will always +be manufactured using the same flash chip, since the nop function +involves the smallest code size and run-time overheads. + + +The next step up is +cyg_am29xxxxx_init_check_devid_XX, where +XX will be replaced by the suffix appropriate for +the bus configuration. It is still necessary to provide all the device +information statically, including the devid +field in the cyg_am29xxxxx_dev structure. +This initialization function will attempt to query the flash device +and check that the provided device id matches the actual hardware. If +there is a mismatch the device will be marked uninitialized and +subsequent attempts to manipulate the flash will fail. + + +If the board may end up being manufactured with any of a number of +different flash chips then the driver can perform run-time +initialization, using a cyg_am29xxxxx_init_cfi_XX +function. This queries the flash device as per the Common Flash Memory +Interface Specification, supported by all current devices (although +not necessarily by older devices). The +block_info field in the +cyg_am29xxxxx_dev structure and the +end and +num_block_infos fields in the +cyg_flash_dev structure will be filled in. +It is still necessary to supply the start +field statically since otherwise the driver will not know how to +access the flash device. The main disadvantage of using CFI is that it +increases the code size. + + +If CFI is used then the cyg_am29xxxxx_dev +structure must not be declared const. The CFI code +will attempt to update the structure and will fail if the structure is +held in read-only memory. This would leave the flash driver +non-functional. + + +A final option is to use a platform-specific initialization function. +This may be useful if the board may be manufactured with one of a +small number of different flash devices and the platform HAL needs to +adapt to this. The AM29xxxxx driver provides a utility function to +read the device id, cyg_am29xxxxx_read_devid_XX: + + +static int +alaia_flash_init(struct cyg_flash_dev* dev) +{ + int devid = cyg_am29xxxxx_read_devid_1616(dev); + switch(devid) { + case 0x0042 : + … + case 0x0084 : + … + default: + return CYG_FLASH_ERR_DRV_WRONG_PART; + } +} + + +There are many other possible uses for a platform-specific +initialization function. For example initial prototype boards might +have only supported 8-bit access to a 16-bit flash device rather than +16-bit access, but this problem was fixed in the next revision. The +platform-specific initialization function can figure out which model +board it is running on and replace the default +16as8 functions with faster 16 +ones. + + + + Erase and Program + +The AM29xxxxx driver provides erase and program functions appropriate +for the various bus configurations. On most targets these can be used +directly. On some targets it may be necessary to do some extra work +before and after the erase and program operations. For example if the +hardware has an MMU then the part of the address map containing the +flash may have been set to read-only, in an attempt to catch spurious +memory accesses. Erasing or programming the flash requires +write-access, so the MMU settings have to be changed temporarily. As +another example some flash device may require a higher voltage to be +applied during an erase or program operation. or a higher voltage may +be desirable to make the operation proceed faster. A typical +platform-specific erase function would look like this: + + +static int +alaia_flash_erase(struct cyg_flash_dev* dev, cyg_flashaddr_t addr) +{ + int result; + … // Set up the hardware for an erase + result = cyg_am29xxxxx_erase_32(dev, addr); + … // Revert the hardware change + return result; +} + + +There are two configurations which affect the erase and program +functions, and which a platform HAL may wish to change: +CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_TIMEOUT +and +CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT. +The erase and program operations both involve polling for completion, +and these timeout impose an upper bound on the polling loop. Normally +these operations should never take anywhere close to the timeout +period, so a timeout indicates a catastrophic failure that should +really be handled by a watchdog reset. A reset is particularly +appropriate because there will be no clean way of aborting the flash +operation. The main reason for the timeouts is to help with debugging +when porting to new hardware. If there is a valid reason why a +particular platform needs different timeouts then the platform HAL's +CDL can require appropriate values for these options. + + + + Locking + +There is no single way of implementing the block lock and unlock +operations on all AM29xxxxx devices. If these operations are supported at +all then usually they involve manipulating the voltages on certain +pins. This would not be able to be handled by generic driver code since it requires +knowing how these pins can be manipulated via the processor's GPIO +lines. Therefore the AM29xxxxx driver does not usually provide lock and unlock +functions, and instead the generic dummy functions +cyg_flash_devfn_lock_nop and +cyg_flash_devfn_unlock_nop should be used. An exception exists for +the AT49xxxx family of devices which are sufficiently AMD +compatible in other respects. Otherwise, if a +platform does provide a way of implementing the locking then this can +be handled by platform-specific functions. + + +static int +alaia_lock(struct cyg_flash_dev* dev, const cyg_flashaddr_t addr) +{ + … +} + +static int +alaia_unlock(struct cyg_flash_dev* dev, const cyg_flashaddr_t addr) +{ + … +} + + +If real locking functions are implemented then the platform HAL's CDL +script should implement the CDL interface +CYGHWR_IO_FLASH_BLOCK_LOCKING. Otherwise the +generic flash package may believe that none of the flash drivers in the +system provide locking functionality and disable the interface functions. + + + AT49xxxx locking + +As locking is standardised across the AT49xxxx family of AMD AM29xxxxx +compatible Flash parts, a method supporting this is included within this +driver. cyg_at49xxxx_softlock_XX provides a means of +locking a Flash sector such that it may be subsequently unlocked. +cyg_at49xxxx_hardlock_XX locks a sector such that +it cannot be unlocked until after reset or a power cycle. +cyg_at49xxxx_unlock_XX unlocks a sector that has +previously been softlocked. At power on or Flash device reset, all sectors +default to being softlocked. + + + + + Other + +The driver provides a set of functions +cyg_am29xxxxx_read_devid_XX, one per supported +bus configuration. These functions take a single argument, a pointer +to the cyg_flash_dev structure, and return +the chip's device id. For older devices this id is a single byte. For +more recent devices the id is a 3-byte value, 0x7E followed by a +further two bytes that actually identify the device. +cyg_am29xxxxx_read_devid_XX is usually called +only from inside a platform-specific driver initialization routine, +allowing the platform HAL to adapt to the actual device present on the +board. + + + + + Device-Specific Structure + +The cyg_am29xxxxx_dev structure provides +information specific to AM29xxxxx flash devices, as opposed to the +more generic flash information which goes into the +cyg_flash_dev structure. There are only two +fields: devid and +block_info. + + +devid is only needed if the driver's +initialization function is set to +cyg_am29xxxxx_init_check_devid_XX. That function +will extract the actual device info from the flash chip and compare it +with the devid field. If there is a +mismatch then subsequent operations on the device will fail. + + +The block_info field consists of one or +more pairs of the block size in bytes and the number of blocks of that +size. The order must match the actual hardware device since the flash +code will use the table to determine the start and end locations of +each block. The table can be initialized in one of three ways: + + + +If the driver initialization function is set to +cyg_flash_devfn_init_nop or +cyg_am29xxxxx_init_check_devid_XX then the block +information should be provided statically. This is appropriate if the +board will also be manufactured using the same flash chip. + + +If cyg_am29xxxxx_init_cfi_XX is used then this +will fill in the block info table. Hence there is no need for static +initialization. + + +If a platform-specific initialization function is used then either +this should fill in the block info table, or the info should be +provided statically. + + + +The size of the block_info table is +determined by the configuration option +CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS. +This has a default value of 4, which should suffice for nearly all +AM29xxxxx flash devices. If more entries are needed then the platform +HAL's CDL script should require a larger value. + + +If the cyg_am29xxxxx_dev structure is +statically initialized then it can be const. This +saves a small amount of memory in ROM startup applications. If the +structure is updated at run-time, either by +cyg_am29xxxxx_init_cfi_XX or by a +platform-specific initialization routine, then it cannot be +const. + + + + Flash Structure + +Internally the generic flash code works in terms of +cyg_flash_dev structures, and the platform +HAL should define one of these. The structure should be placed in the +cyg_flashdev table. The following fields need to be +provided: + + + + funs + +This should point at the table of functions. + + + + start + +The base address of the flash in the address map. On +some board the flash may be mapped into memory several times, for +example it may appear in both cached and uncached parts of the address +space. The start field should correspond to +the cached address. + + + + end + +The address of the last byte in the flash. It can +either be statically initialized, or +cyg_am29xxxxx_init_cfi_XX will calculate +its value at run-time. + + + + num_block_infos + +This should be the number of entries in the +block_info table. It can either be +statically initialized or it will be filled in by +cyg_am29xxxxx_init_cfi_XX. + + + + block_info + +The table with the block information is held in the +cyg_am29xxxxx_dev structure, so this field +should just point into that structure. + + + + priv + +This field is reserved for use by the device driver. For the AM29xxxxx +driver it should point at the appropriate +cyg_am29xxxxx_dev structure. + + + + +The cyg_flash_dev structure contains a number +of other fields which are manipulated only by the generic flash code. +Some of these fields will be updated at run-time so the structure +cannot be declared const. + + + + Multiple Devices + +A board may have several flash devices in parallel, for example two +16-bit devices on a 32-bit bus. It may also have several such banks +to increase the total amount of flash. If each device provides 2MB, +there could be one bank of 2 parallel flash devices at 0xFF800000 and +another bank at 0xFFC00000, giving a total of 8MB. This setup can be +described in several ways. One approach is to define two +cyg_flash_dev structures. The table of +function pointers can usually be shared, as can the +cyg_am29xxxxx_dev structure. Another approach +is to define a single cyg_flash_dev +structure but with a larger block_info +table, covering the blocks in both banks of devices. The second +approach makes more efficient use of memory. + + +Many variations are possible, for example a small slow flash device +may be used for initial bootstrap and holding the configuration data, +while there is also a much larger and faster device to hold a file +system. Such variations are usually best described by separate +cyg_flash_dev structures. + + +If more than one cyg_flash_dev structure is +instantiated then the platform HAL's CDL script should implement the +CDL interface CYGHWR_IO_FLASH_DEVICE once for every +device past the first. Otherwise the generic code may default to the +case of a single flash device and optimize for that. + + + + Platform-Specific Macros + +The AM29xxxxx driver source code includes the header files +cyg/hal/hal_arch.h and +cyg/hal/hal_io.h, and hence +indirectly the corresponding platform header files (if defined). +Optionally these headers can define macros which are used inside the +driver, thus giving the HAL limited control over how the driver works. + + + + Cache Management + +By default the AM29xxxxx driver assumes that the flash can be accessed +uncached, and it will use the HAL +CYGARC_UNCACHED_ADDRESS macro to map the cached +address in the start field of the +cyg_flash_dev structure into an uncached +address. If for any reason this HAL macro is inappropriate for the +flash then an alternative macro +HAL_AM29XXXXX_UNCACHED_ADDRESS can be defined +instead. However fixing the +CYGARC_UNCACHED_ADDRESS macro is normally the +better solution. + + +If there is no way of bypassing the cache then the platform HAL should +implement the CDL interface +CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CACHED_ONLY. The flash +driver will now disable and re-enable the cache as required. For +example a program operation will involve the following: + + + AM29_INTSCACHE_STATE; + AM29_INTSCACHE_BEGIN(); + while ( ! finished ) { + program data + } + AM29_INTSCACHE_END(); + + +The default implementations of these INTSCACHE macros are as follows: +STATE defines any local variables that may be +needed, e.g. to save the current interrupt state; +BEGIN disables interrupts, synchronizes the data +caches, disables it, and invalidates the current contents; +END re-enables the cache and then +interrupts. The cache is only disabled when interrupts are disabled, +so there is no possibility of an interrupt handler running or a +context switch occurring while the cache is disabled, potentially +leaving the system running very slowly. The data cache synchronization +ensures that there are no dirty cache lines, so when the cache is +disabled the low-level flash write code will not see stale data in +memory. The invalidate ensures that at the end of the operation +higher-level code will not pick up stale cache contents instead of the +newly written flash data. + + +Some implementations of the HAL cache macros may not provide the exact +semantics required by the flash driver. For example +HAL_DCACHE_DISABLE may have an unwanted side +effect, or it may do more work than is needed here. The driver will +check for alternative macros +HAL_AM29XXXXX_INTSCACHE_STATE, +HAL_AM29XXXXX_INTSCACHE_BEGIN and +HAL_AM29XXXXX_INTSCACHE_END, using these instead of +the defaults. + + + + diff --git a/packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h b/packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h @@ -0,0 +1,136 @@ +#ifndef CYGONCE_DEVS_FLASH_AM29xxxxx_dev_V2_H +# define CYGONCE_DEVS_FLASH_AM29xxxxx_dev_V2_H +//========================================================================== +// +// am29xxxxx_dev.h +// +// Flash driver for the AMD family - driver details +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004, 2005 eCosCentric Ltd +// +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// 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: 2004-11-05 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include + +externC int cyg_am29xxxxx_read_devid_8( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_read_devid_16( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_read_devid_32( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_read_devid_88( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_read_devid_8888( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_read_devid_1616( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_read_devid_16as8( struct cyg_flash_dev*); + +externC int cyg_am29xxxxx_init_check_devid_8( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_check_devid_16( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_check_devid_32( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_check_devid_88( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_check_devid_8888( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_check_devid_1616( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_check_devid_16as8( struct cyg_flash_dev*); + +externC int cyg_am29xxxxx_init_cfi_8( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_cfi_16( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_cfi_32( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_cfi_88( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_cfi_8888( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_cfi_1616( struct cyg_flash_dev*); +externC int cyg_am29xxxxx_init_cfi_16as8( struct cyg_flash_dev*); + +externC int cyg_am29xxxxx_erase_8( struct cyg_flash_dev*, cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_16( struct cyg_flash_dev*, cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_32( struct cyg_flash_dev*, cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_88( struct cyg_flash_dev*, cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_8888( struct cyg_flash_dev*, cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_1616( struct cyg_flash_dev*, cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_16as8( struct cyg_flash_dev*, cyg_flashaddr_t); + +externC int cyg_am29xxxxx_program_8( struct cyg_flash_dev*, cyg_flashaddr_t, const void*, size_t); +externC int cyg_am29xxxxx_program_16( struct cyg_flash_dev*, cyg_flashaddr_t, const void*, size_t); +externC int cyg_am29xxxxx_program_32( struct cyg_flash_dev*, cyg_flashaddr_t, const void*, size_t); +externC int cyg_am29xxxxx_program_88( struct cyg_flash_dev*, cyg_flashaddr_t, const void*, size_t); +externC int cyg_am29xxxxx_program_8888( struct cyg_flash_dev*, cyg_flashaddr_t, const void*, size_t); +externC int cyg_am29xxxxx_program_1616( struct cyg_flash_dev*, cyg_flashaddr_t, const void*, size_t); +externC int cyg_am29xxxxx_program_16as8( struct cyg_flash_dev*, cyg_flashaddr_t, const void*, size_t); + +externC int cyg_at49xxxx_softlock_8( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_softlock_16( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_softlock_32( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_softlock_88( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_softlock_8888( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_softlock_1616( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_softlock_16as8( struct cyg_flash_dev*, const cyg_flashaddr_t ); + +externC int cyg_at49xxxx_hardlock_8( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_hardlock_16( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_hardlock_32( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_hardlock_88( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_hardlock_8888( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_hardlock_1616( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_hardlock_16as8( struct cyg_flash_dev*, const cyg_flashaddr_t ); + +externC int cyg_at49xxxx_unlock_8( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_unlock_16( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_unlock_32( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_unlock_88( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_unlock_8888( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_unlock_1616( struct cyg_flash_dev*, const cyg_flashaddr_t ); +externC int cyg_at49xxxx_unlock_16as8( struct cyg_flash_dev*, const cyg_flashaddr_t ); + +// FIXME: add program_buffered() support as per e.g. the AM29LV128 +// FIXME: add software lock/unlock support as per e.g. the AM29BDS640 + +// The driver-specific data, pointed at by the priv field in a +// a cyg_flash_dev structure. +typedef struct cyg_am29xxxxx_dev { + // The device id, mainly for use by the init_check_devid() routines + cyg_uint32 devid; + // Space for the block_info fields needed for the cyg_flash_dev. + // These can be statically initialized, or dynamically via + // init_cfi(). + cyg_flash_block_info_t block_info[CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS]; +} cyg_am29xxxxx_dev; + +// TODO: An AM29 specific macro could optimise the common case of CYG_FLASH_FUNS. +// Bart and Jifl discussed this in mid July/start Aug 2005. + +#endif // CYGONCE_DEVS_FLASH_AM29xxxxx_dev_V2_H diff --git a/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c b/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c @@ -0,0 +1,512 @@ +//========================================================================== +// +// am29xxxxx.c +// +// Flash driver for the AMD family +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004, 2005, 2006, 2007 eCosCentric Ltd +// +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// 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: 2004-11-05 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// This driver supports multiple banks of AMD am29xxxxx 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 (*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. +// +// Erase, program, and the locking functions need real +// implementations, although locking is not always available. + +// ---------------------------------------------------------------------------- +// The protocol understood by AMD flash chips and compatibles. +// The AM29_PARALLEL() macro is used in bus configurations with multiple +// devices in parallel, to issue commands to all the devices in a single +// write. In theory some of the operations, e.g. READ_DEVID, only need +// to access a single chip but then you get into complications for the +// SETUP commands. The AM29_SWAP() macro deals with endianness issues +// on some targets and can also help with h/w where things are just not +// wired right. +#define AM29_COMMAND_SETUP1 AM29_SWAP(AM29_PARALLEL(0x00AA)) +#define AM29_COMMAND_SETUP2 AM29_SWAP(AM29_PARALLEL(0x0055)) +#define AM29_COMMAND_RESET AM29_SWAP(AM29_PARALLEL(0x00F0)) +#define AM29_COMMAND_AUTOSELECT AM29_SWAP(AM29_PARALLEL(0x0090)) +#define AM29_COMMAND_ERASE AM29_SWAP(AM29_PARALLEL(0x0080)) +#define AM29_COMMAND_ERASE_SECTOR AM29_SWAP(AM29_PARALLEL(0x0030)) +#define AM29_COMMAND_ERASE_RESUME AM29_SWAP(AM29_PARALLEL(0x0030)) +#define AM29_COMMAND_CFI AM29_SWAP(AM29_PARALLEL(0x0098)) +#define AM29_COMMAND_PROGRAM AM29_SWAP(AM29_PARALLEL(0x00A0)) +// Following are specific to AT49 derivatives +#define AM29_COMMAND_AT49_SOFTLOCK_BLOCK_0 AM29_SWAP(AM29_PARALLEL(0x0080)) +#define AM29_COMMAND_AT49_SOFTLOCK_BLOCK_1 AM29_SWAP(AM29_PARALLEL(0x0040)) +#define AM29_COMMAND_AT49_HARDLOCK_BLOCK_0 AM29_SWAP(AM29_PARALLEL(0x0080)) +#define AM29_COMMAND_AT49_HARDLOCK_BLOCK_1 AM29_SWAP(AM29_PARALLEL(0x0060)) +#define AM29_COMMAND_AT49_UNLOCK_BLOCK AM29_SWAP(AM29_PARALLEL(0x0070)) + +// 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 AM29_OFFSET_CFI_Q AM29_OFFSET_CFI_DATA(0x0010) +#define AM29_OFFSET_CFI_SIZE AM29_OFFSET_CFI_DATA(0x0027) +#define AM29_OFFSET_CFI_BLOCK_REGIONS AM29_OFFSET_CFI_DATA(0x002C) +#define AM29_OFFSET_CFI_BLOCK_COUNT_LSB(_i_) AM29_OFFSET_CFI_DATA(0x002D + (4 * _i_)) +#define AM29_OFFSET_CFI_BLOCK_COUNT_MSB(_i_) AM29_OFFSET_CFI_DATA(0x002E + (4 * _i_)) +#define AM29_OFFSET_CFI_BLOCK_SIZE_LSB(_i_) AM29_OFFSET_CFI_DATA(0x002F + (4 * _i_)) +#define AM29_OFFSET_CFI_BLOCK_SIZE_MSB(_i_) AM29_OFFSET_CFI_DATA(0x0030 + (4 * _i_)) + +#define AM29_STATUS_DQ7 AM29_SWAP(AM29_PARALLEL(0x0080)) +#define AM29_STATUS_DQ6 AM29_SWAP(AM29_PARALLEL(0x0040)) +#define AM29_STATUS_DQ5 AM29_SWAP(AM29_PARALLEL(0x0020)) +#define AM29_STATUS_DQ4 AM29_SWAP(AM29_PARALLEL(0x0010)) +#define AM29_STATUS_DQ3 AM29_SWAP(AM29_PARALLEL(0x0008)) +#define AM29_STATUS_DQ2 AM29_SWAP(AM29_PARALLEL(0x0004)) +#define AM29_STATUS_DQ1 AM29_SWAP(AM29_PARALLEL(0x0002)) +#define AM29_STATUS_DQ0 AM29_SWAP(AM29_PARALLEL(0x0001)) +#define AM29_ID_LOCKED AM29_SWAP(AM29_PARALLEL(0x03)) + +// 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 AM29_NEXT_DATUM_8(_ptr_) (*_ptr_++) +#if CYG_BYTEORDER == CYG_LSBFIRST +# define AM29_NEXT_DATUM_16(_ptr_) \ + ({ \ + cyg_uint16 _result_; \ + _result_ = (_ptr_[1] << 8) | _ptr_[0]; \ + _ptr_ += 2; \ + _result_; }) + +# define AM29_NEXT_DATUM_32(_ptr_) \ + ({ \ + cyg_uint32 _result_; \ + _result_ = (_ptr_[3] << 24) | (_ptr_[2] << 16) | (_ptr_[1] << 8) | _ptr_[0]; \ + _ptr_ += 4; \ + _result_; }) +#else +# define AM29_NEXT_DATUM_16(_ptr_) \ + ({ \ + cyg_uint16 _result_; \ + _result_ = (_ptr_[0] << 8) | _ptr_[1]; \ + _ptr_ += 2; \ + _result_; }) + +# define AM29_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_AM29XXXXX_UNCACHED_ADDRESS() can be supplied by one of the HAL +// packages. Otherwise if CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_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_AM29XXXXX_UNCACHED_ADDRESS) +# define AM29_UNCACHED_ADDRESS(_addr_) ((volatile AM29_TYPE*)HAL_AM29XXXXX_UNCACHED_ADDRESS(_addr_)) +#elif !defined(CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CACHED_ONLY) +# ifndef CYGARC_UNCACHED_ADDRESS +# error Cache should be bypassed but CYGARC_UNCACHED_ADDRESS is not defined. +# endif +# define AM29_UNCACHED_ADDRESS(_addr_) ((volatile AM29_TYPE*)CYGARC_UNCACHED_ADDRESS(_addr_)) +#elif defined(HAL_AM29XXXXX_P2V) +// HAL_AM29XXXXX_P2V is a deprecated macro that is only retained for +// backward compatibility. +# define AM29_UNCACHED_ADDRESS(_addr_) ((volatile AM29_TYPE*)HAL_AM29XXXXX_P2V(_addr_)) +#else +# define AM29_UNCACHED_ADDRESS(_addr_) ((volatile AM29_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 AM29_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_AM29XXXXX_SWAP) +# define AM29_SWAP(_data_) HAL_AM29XXXXX_SWAP(_data_) +#else +# define AM29_SWAP(_data_) (_data_) +#endif +#if defined(HAL_AM29XXXXX_UNSWAP) +# define AM29_UNSWAP(_data_) HAL_AM29XXXXX_UNSWAP(_data_) +#else +# define AM29_UNSWAP(_data_) (_data_) +#endif + +// On some platforms there may be almost inexplicable failures, caused +// by very subtle effects such as instruction cache lines still being +// filled from flash memory which the _hw routines in .2ram sections are +// already running and have taken the flash out of read-array mode. +// These are very rare effects and not amenable to a generic solution, +// so instead the platform HAL (usually) can define additional hook +// macros that get invoked by the .2ram functions. These can e.g. +// add a short delay or invalidate a couple of instruction cache lines, +// but only if the code is executing from flash. Any such hooks will +// affect interrupt latency so should only be used when absolutely +// necessary. They must also be simple code, e.g. no calls to other +// functions that may be in flash. + +#ifdef HAL_AM29XXXXX_2RAM_ENTRY_HOOK +# define AM29_2RAM_ENTRY_HOOK() HAL_AM29XXXXX_2RAM_ENTRY_HOOK() +#else +# define AM29_2RAM_ENTRY_HOOK() CYG_EMPTY_STATEMENT +#endif +#ifdef HAL_AM29XXXXX_2RAM_EXIT_HOOK +# define AM29_2RAM_EXIT_HOOK() HAL_AM29XXXXX_2RAM_EXIT_HOOK() +#else +# define AM29_2RAM_EXIT_HOOK() CYG_EMPTY_STATEMENT +#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. This is necessary if +// CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CACHED_ONLY is implemented, +// i.e. if the cache cannot be bypassed. The cache must be temporarily +// disabled for flash operations, and interrupts have to be disabled +// while the cache is disabled to prevent interrupts and context switches. +// 2) control interrupts only, the default if the cache can be bypassed +// when accessing the flash. The flash is still in an unusable +// state during flash operations so interrupts and context switches +// should be avoided. +// 3) only invalidate at the end, 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_AMD_AM29XXXXX_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 AM29_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. The 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 AM29_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 AM29_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_AMD_AM29XXXXX_V2_LEAVE_INTERRUPTS_ENABLED) + +# define AM29_INTSCACHE_DEFAULT_STATE int _saved_ints_ +# define AM29_INTSCACHE_DEFAULT_BEGIN() HAL_DISABLE_INTERRUPTS(_saved_ints_) + +# if defined(HAL_DCACHE_SYNC) && defined(HAL_DCACHE_INVALIDATE_ALL) +// 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 AM29_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 AM29_INTSCACHE_DEFAULT_END() HAL_RESTORE_INTERRUPTS(_saved_ints_) +# endif +#else + +# define AM29_INTSCACHE_DEFAULT_STATE CYG_EMPTY_STATEMENT +# define AM29_INTSCACHE_DEFAULT_BEGIN() CYG_EMPTY_STATEMENT +# if defined(HAL_DCACHE_SYNC) && defined(HAL_DCACHE_INVALIDATE_ALL) +# define AM29_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 +# else +# define AM29_INTSCACHE_DEFAULT_END() CYG_EMPTY_STATEMENT +# endif +#endif + +#ifdef HAL_AM29XXXXX_INTSCACHE_STATE +# define AM29_INTSCACHE_STATE HAL_AM29XXXXX_INTSCACHE_STATE +#else +# define AM29_INTSCACHE_STATE AM29_INTSCACHE_DEFAULT_STATE +#endif +#ifdef HAL_AM29XXXXX_INTSCACHE_BEGIN +# define AM29_INTSCACHE_BEGIN HAL_AM29XXXXX_INTSCACHE_BEGIN +#else +# define AM29_INTSCACHE_BEGIN AM29_INTSCACHE_DEFAULT_BEGIN +#endif +#ifdef HAL_AM29XXXXX_INTSCACHE_END +# define AM29_INTSCACHE_END HAL_AM29XXXXX_INTSCACHE_END +#else +# define AM29_INTSCACHE_END AM29_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 +am29_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 AM29_SUFFIX 8 +#define AM29_TYPE cyg_uint8 +#define AM29_DEVCOUNT 1 +#define AM29_NEXT_DATUM(_ptr_) AM29_NEXT_DATUM_8(_ptr_) + +#include "am29xxxxx_aux.c" + +#undef AM29_SUFFIX +#undef AM29_TYPE +#undef AM29_DEVCOUNT +#undef AM29_NEXT_DATUM + +// A single 16-bit device. +#define AM29_SUFFIX 16 +#define AM29_TYPE cyg_uint16 +#define AM29_DEVCOUNT 1 +#define AM29_NEXT_DATUM(_ptr_) AM29_NEXT_DATUM_16(_ptr_) + +#include "am29xxxxx_aux.c" + +#undef AM29_SUFFIX +#undef AM29_TYPE +#undef AM29_DEVCOUNT +#undef AM29_NEXT_DATUM + +// A single 32-bit device. +#define AM29_SUFFIX 32 +#define AM29_TYPE cyg_uint32 +#define AM29_DEVCOUNT 1 +#define AM29_NEXT_DATUM(_ptr_) AM29_NEXT_DATUM_32(_ptr_) + +#include "am29xxxxx_aux.c" + +#undef AM29_SUFFIX +#undef AM29_TYPE +#undef AM29_DEVCOUNT +#undef AM29_NEXT_DATUM + +// Two 8-bit devices, giving a 16-bit bus. +#define AM29_SUFFIX 88 +#define AM29_TYPE cyg_uint16 +#define AM29_DEVCOUNT 2 +#define AM29_NEXT_DATUM(_ptr_) AM29_NEXT_DATUM_16(_ptr_) +#define AM29_PARALLEL(_cmd_) ((_cmd_ << 8) | _cmd_) + +#include "am29xxxxx_aux.c" + +#undef AM29_SUFFIX +#undef AM29_TYPE +#undef AM29_DEVCOUNT +#undef AM29_NEXT_DATUM + +// Four 8-bit devices, giving a 32-bit bus. +#define AM29_SUFFIX 8888 +#define AM29_TYPE cyg_uint32 +#define AM29_DEVCOUNT 4 +#define AM29_NEXT_DATUM(_ptr_) AM29_NEXT_DATUM_32(_ptr_) +#define AM29_PARALLEL(_cmd_) ((_cmd_ << 24) | (_cmd_ << 16) | (_cmd_ << 8) | _cmd_) + +#include "am29xxxxx_aux.c" + +#undef AM29_SUFFIX +#undef AM29_TYPE +#undef AM29_DEVCOUNT +#undef AM29_NEXT_DATUM + +// Two 16-bit devices, giving a 32-bit bus. +#define AM29_SUFFIX 1616 +#define AM29_TYPE cyg_uint32 +#define AM29_DEVCOUNT 2 +#define AM29_NEXT_DATUM(_ptr_) AM29_NEXT_DATUM_32(_ptr_) +#define AM29_PARALLEL(_cmd_) ((_cmd_ << 16) | _cmd_) + +#include "am29xxxxx_aux.c" + +#undef AM29_SUFFIX +#undef AM29_TYPE +#undef AM29_DEVCOUNT +#undef AM29_NEXT_DATUM + +// 16AS8. A 16-bit device hooked up so that only byte accesses are +// allowed. This requires unusual offsets +#define AM29_SUFFIX 16as8 +#define AM29_TYPE cyg_uint8 +#define AM29_DEVCOUNT 1 +#define AM29_NEXT_DATUM(_ptr_) AM29_NEXT_DATUM_8(_ptr_) +#define AM29_OFFSET_COMMAND 0x0AAA +#define AM29_OFFSET_COMMAND2 0x0555 +#define AM29_OFFSET_MANUFACTURER_ID 0x0000 +#define AM29_OFFSET_DEVID 0x0002 +#define AM29_OFFSET_DEVID2 0x001C +#define AM29_OFFSET_DEVID3 0x001E +#define AM29_OFFSET_AT49_LOCK_STATUS 04 +#define AM29_OFFSET_CFI 0x00AA +#define AM29_OFFSET_CFI_DATA(_idx_) (2 * _idx_) + +#include "am29xxxxx_aux.c" diff --git a/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c b/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c @@ -0,0 +1,983 @@ +//========================================================================== +// +// am29xxxxx_aux.c +// +// Flash driver for the AMD family - implementation. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004, 2005, 2006, 2007, 2008 eCosCentric Ltd +// +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// 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: 2004-11-05 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// This file is #include'd multiple times from the main am29xxxxx.c file, +// It serves to instantiate the various hardware operations in ways +// appropriate for all the bus configurations. + +// The following macros are used to construct suitable function names +// for the current bus configuration. AM29_SUFFIX is #define'd before +// each #include of am29xxxxx_aux.c + +#ifndef AM29_STR +# define AM29_STR1(_a_) # _a_ +# define AM29_STR(_a_) AM29_STR1(_a_) +# define AM29_CONCAT3_AUX(_a_, _b_, _c_) _a_##_b_##_c_ +# define AM29_CONCAT3(_a_, _b_, _c_) AM29_CONCAT3_AUX(_a_, _b_, _c_) +#endif + +#define AM29_FNNAME(_base_) AM29_CONCAT3(_base_, _, AM29_SUFFIX) + +// Similarly construct a forward declaration, placing the function in +// the .2ram section. Each function must still be in a separate section +// for linker garbage collection. + +# define AM29_RAMFNDECL(_base_, _args_) \ + AM29_FNNAME(_base_) _args_ __attribute__((section (".2ram." AM29_STR(_base_) "_" AM29_STR(AM29_SUFFIX)))) + +// Calculate the various offsets, based on the device count. +// The main code may override these settings for specific +// configurations, e.g. 16as8 +#ifndef AM29_OFFSET_COMMAND +# define AM29_OFFSET_COMMAND 0x0555 +#endif +#ifndef AM29_OFFSET_COMMAND2 +# define AM29_OFFSET_COMMAND2 0x02AA +#endif +#ifndef AM29_OFFSET_MANUFACTURER_ID +# define AM29_OFFSET_MANUFACTURER_ID 0x0000 +#endif +#ifndef AM29_OFFSET_DEVID +# define AM29_OFFSET_DEVID 0x0001 +#endif +#ifndef AM29_OFFSET_DEVID2 +# define AM29_OFFSET_DEVID2 0x000E +#endif +#ifndef AM29_OFFSET_DEVID3 +# define AM29_OFFSET_DEVID3 0x000F +#endif +#ifndef AM29_OFFSET_CFI +# define AM29_OFFSET_CFI 0x0055 +#endif +#ifndef AM29_OFFSET_CFI_DATA +# define AM29_OFFSET_CFI_DATA(_idx_) _idx_ +#endif +#ifndef AM29_OFFSET_AT49_LOCK_STATUS +# define AM29_OFFSET_AT49_LOCK_STATUS 0x02 +#endif + +// For parallel operation commands are issued in parallel and status +// bits are checked in parallel. +#ifndef AM29_PARALLEL +# define AM29_PARALLEL(_cmd_) (_cmd_) +#endif + +// ---------------------------------------------------------------------------- +// Diagnostic routines. + +#if 0 +#define amd_diag( __fmt, ... ) diag_printf("AMD: %s[%d]: " __fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__ ); +#define amd_dump_buf( __addr, __size ) diag_dump_buf( __addr, __size ) +#else +#define amd_diag( __fmt, ... ) +#define amd_dump_buf( __addr, __size ) +#endif + +// ---------------------------------------------------------------------------- +// When performing the various low-level operations like erase the flash +// chip can no longer support ordinary data reads. Obviously this is a +// problem if the current code is executing out of flash. The solution is +// to store the key functions in RAM rather than flash, via a special +// linker section .2ram which usually gets placed in the same area as +// .data. +// +// In a ROM startup application anything in .2ram will consume space +// in both the flash and RAM. Hence it is desirable to keep the .2ram +// functions as small as possible, responsible only for the actual +// hardware manipulation. +// +// All these .2ram functions should be invoked with interrupts +// disabled. Depending on the hardware it may also be necessary to +// have the data cache disabled. The .2ram functions must be +// self-contained, even macro invocations like HAL_DELAY_US() are +// banned because on some platforms those could be implemented as +// function calls. + +// gcc requires forward declarations with the attributes, then the actual +// definitions. +static int AM29_RAMFNDECL(am29_hw_query, (volatile AM29_TYPE*)); +static int AM29_RAMFNDECL(am29_hw_cfi, (struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*)); +static int AM29_RAMFNDECL(am29_hw_erase, (volatile AM29_TYPE*)); +static int AM29_RAMFNDECL(am29_hw_program, (volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32 count, int retries)); +static int AM29_RAMFNDECL(at49_hw_softlock, (volatile AM29_TYPE*)); +static int AM29_RAMFNDECL(at49_hw_hardlock, (volatile AM29_TYPE*)); +static int AM29_RAMFNDECL(at49_hw_unlock, (volatile AM29_TYPE*)); + + +// ---------------------------------------------------------------------------- + +#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME +// With this flash component (e.g. AT49xxxxx), the reset does not +// cause a suspended erase/program to be aborted. Instead all we +// can do is resume any suspended operations. We do this on each +// block as some parts have different granularity. + +static void +AM29_FNNAME(am29_hw_force_all_suspended_resume)(struct cyg_flash_dev* dev, cyg_am29xxxxx_dev* am29_dev, volatile AM29_TYPE* addr) +{ + cyg_ucount16 i,j; + AM29_TYPE datum1, datum2; + + AM29_2RAM_ENTRY_HOOK(); + + for (i=0; inum_block_infos; i++) + { + for (j=0; jblock_info[i].blocks; j++) + { + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE_RESUME; + HAL_MEMORY_BARRIER(); + // We don't know if the suspended operation was an erase or + // program, so just compare the whole word to spot _any_ toggling. + do { + datum1 = addr[AM29_OFFSET_COMMAND]; + datum2 = addr[AM29_OFFSET_COMMAND]; + } while (datum1 != datum2); + + addr += am29_dev->block_info[i].block_size/sizeof(AM29_TYPE); + } + } + + AM29_2RAM_EXIT_HOOK(); +} +#endif // ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME + +// Read the device id. This involves a straightforward command +// sequence, followed by a reset to get back into array mode. +// All chips are accessed in parallel, but only the response +// from the least significant is used. +static int +AM29_FNNAME(am29_hw_query)(volatile AM29_TYPE* addr) +{ + int devid; + cyg_uint32 onedevmask; + + AM29_2RAM_ENTRY_HOOK(); + + // Fortunately the compiler should optimise the below + // tests such that onedevmask is a constant. + if ( 1 == (sizeof(AM29_TYPE) / AM29_DEVCOUNT) ) + onedevmask = 0xFF; + else if ( 2 == (sizeof(AM29_TYPE) / AM29_DEVCOUNT) ) + onedevmask = 0xFFFF; + else { + CYG_ASSERT( 4 == (sizeof(AM29_TYPE) / AM29_DEVCOUNT), + "Unexpected flash width per device" ); + onedevmask = 0xFFFFFFFF; + } + + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AUTOSELECT; + HAL_MEMORY_BARRIER(); + + devid = AM29_UNSWAP(addr[AM29_OFFSET_DEVID]) & onedevmask; + +// amd_diag("devid %x\n", devid ); +// amd_dump_buf(addr, 64 ); + + // The original AMD chips only used a single-byte device id, but + // all codes have now been used up. Newer devices use a 3-byte + // devid. The above devid read will have returned 0x007E. The + // test allows for boards with a mixture of old and new chips. + // The amount of code involved is too small to warrant a config + // option. + // FIXME by jifl: What happens when a single device is connected 16-bits + // (or 32-bits) wide per device? Is the code still 0x7E, and all the + // other devids are 8-bits only? + if (0x007E == devid) { + devid <<= 16; + devid |= ((AM29_UNSWAP(addr[AM29_OFFSET_DEVID2]) & 0x00FF) << 8); + devid |= (AM29_UNSWAP(addr[AM29_OFFSET_DEVID3]) & 0x00FF); + } + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + +// amd_diag("devid %x\n", devid ); + + AM29_2RAM_EXIT_HOOK(); + return devid; +} + +// Perform a CFI query. This involves placing the device(s) into CFI +// mode, checking that this has really happened, and then reading the +// size and block info. The address corresponds to the start of the +// flash. +static int +AM29_FNNAME(am29_hw_cfi)(struct cyg_flash_dev* dev, cyg_am29xxxxx_dev* am29_dev, volatile AM29_TYPE* addr) +{ + int dev_size; + int i; + int erase_regions; + + AM29_2RAM_ENTRY_HOOK(); + +#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CFI_BOGOSITY + int manufacturer_id; + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AUTOSELECT; + HAL_MEMORY_BARRIER(); + + manufacturer_id = AM29_UNSWAP(addr[AM29_OFFSET_MANUFACTURER_ID]) & 0x00FF; + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); +#endif + + // Just a single write is needed to put the device into CFI mode + addr[AM29_OFFSET_CFI] = AM29_COMMAND_CFI; + HAL_MEMORY_BARRIER(); +// amd_diag("CFI data:\n"); +// amd_dump_buf( addr, 256 ); + // Now check that we really are in CFI mode. There should be a 'Q' + // at a specific address. This test is not 100% reliable, but should + // be good enough. + if ('Q' != (AM29_UNSWAP(addr[AM29_OFFSET_CFI_Q]) & 0x00FF)) { + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + AM29_2RAM_EXIT_HOOK(); + return CYG_FLASH_ERR_PROTOCOL; + } + // Device sizes are always a power of 2, and the shift is encoded + // in a single byte + dev_size = 0x01 << (AM29_UNSWAP(addr[AM29_OFFSET_CFI_SIZE]) & 0x00FF); + dev->end = dev->start + dev_size - 1; + + // The number of erase regions is also encoded in a single byte. + // Usually this is no more than 4. A value of 0 indicates that + // only chip erase is supported, but the driver does not cope + // with that. + erase_regions = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_REGIONS]) & 0x00FF; + if (erase_regions > CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS) { + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + AM29_2RAM_EXIT_HOOK(); + return CYG_FLASH_ERR_PROTOCOL; + } + dev->num_block_infos = erase_regions; + + for (i = 0; i < erase_regions; i++) { + cyg_uint32 count, size; + cyg_uint32 count_lsb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_COUNT_LSB(i)]) & 0x00FF; + cyg_uint32 count_msb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_COUNT_MSB(i)]) & 0x00FF; + cyg_uint32 size_lsb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_SIZE_LSB(i)]) & 0x00FF; + cyg_uint32 size_msb = AM29_UNSWAP(addr[AM29_OFFSET_CFI_BLOCK_SIZE_MSB(i)]) & 0x00FF; + + count = ((count_msb << 8) | count_lsb) + 1; + size = (size_msb << 16) | (size_lsb << 8); + am29_dev->block_info[i].block_size = (size_t) size * AM29_DEVCOUNT; + am29_dev->block_info[i].blocks = count; + } + +#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_CFI_BOGOSITY + + // Some flash parts have a peculiar implementation of CFI. The + // device erase regions may not be in the order specified in the + // main CFI area. Instead the erase regions are given in a + // manufacturer dependent fixed order, regardless of whether this + // is a top or bottom boot block device. A vendor-specific + // extended query block has an entry saying whether the boot + // blocks are at the top or bottom. This code works out whether + // the erase regions appear to be specified in the wrong order, + // and then swaps them over. + + { + enum { bottom, symmetric, top } boot_type = symmetric; + cyg_uint32 vspec = AM29_SWAP(addr[AM29_OFFSET_CFI_DATA(0x15)]) & 0x00FF; + + // Take a look at the vendor specific area for the boot block + // order. + + switch( manufacturer_id ) + { + // Atmel appear to have their own layout for the vendor + // specific area. Offset 0x06 of the vendor specific area + // contains a single bit: 0x00 = top boot, 0x01 = bottom + // boot. There appears to be no way of specifying + // symmetric formats. + case 0x1F: + if( (addr[AM29_OFFSET_CFI_DATA(vspec+0x06)] & AM29_SWAP(0x1)) == AM29_SWAP(0x1) ) + boot_type = bottom; + else boot_type = top; + break; + + // Most other manufacturers seem to follow the same layout + // and encoding. Offset 0xF of the vendor specific area + // contains the boot sector layout: 0x00 = uniform, 0x01 = + // 8x8k top and bottom, 0x02 = bottom boot, 0x03 = top + // boot, 0x04 = both top and bottom. + // + // The following manufacturers support this layout: + // AMD, Spansion, ST, Macronix. + default: + if( (addr[AM29_OFFSET_CFI_DATA(vspec+0xF)] == AM29_SWAP(0x2)) ) + boot_type = bottom; + else if( (addr[AM29_OFFSET_CFI_DATA(vspec+0xF)] == AM29_SWAP(0x3)) ) + boot_type = top; + // All other options are symmetric + break; + } + + // If the device is marked as top boot, but the erase region + // list appears to be in bottom boot order, then reverse the + // list. Also swap, if it is marked as bottom boot but the + // erase regions appear to be in top boot order. This code + // assumes that the first boot block is always smaller than + // regular blocks; it is possible to imagine flash layouts for + // which that is not true. + + if( ((boot_type == top) && + (am29_dev->block_info[0].block_size < am29_dev->block_info[erase_regions-1].block_size)) || + ((boot_type == bottom) && + (am29_dev->block_info[0].block_size > am29_dev->block_info[erase_regions-1].block_size))) + { + int lo, hi; + + for( lo = 0, hi = erase_regions-1 ; lo < hi ; lo++, hi-- ) + { + size_t size = am29_dev->block_info[lo].block_size; + cyg_uint32 count = am29_dev->block_info[lo].blocks; + am29_dev->block_info[lo].block_size = am29_dev->block_info[hi].block_size; + am29_dev->block_info[lo].blocks = am29_dev->block_info[hi].blocks; + am29_dev->block_info[hi].block_size = size; + am29_dev->block_info[hi].blocks = count; + } + } + } +#endif + + // Get out of CFI mode + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + + AM29_2RAM_EXIT_HOOK(); + return CYG_FLASH_ERR_OK; +} + +// Erase a single sector. There is no API support for chip-erase. The +// generic code operates one sector at a time, invoking the driver for +// each sector, so there is no opportunity inside the driver for +// erasing multiple sectors in a single call. The address argument +// points at the start of the sector. +static int +AM29_FNNAME(am29_hw_erase)(volatile AM29_TYPE* addr) +{ + int retries; + AM29_TYPE datum; + + AM29_2RAM_ENTRY_HOOK(); + + // Start the erase operation + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE_SECTOR; + HAL_MEMORY_BARRIER(); + // There is now a 50us window in which we could send additional + // ERASE_SECTOR commands, but the driver API does not allow this + + // All chips are now erasing in parallel. Loop until all have + // completed. This can be detected in a number of ways. The DQ7 + // bit will be 0 until the erase is complete, but there is a + // problem if something went wrong (e.g. the sector is locked), + // the erase has not actually started, and the relevant bit was 0 + // already. More useful is DQ6. This will toggle during the 50us + // window and while the erase is in progress, then stop toggling. + // If the erase does not actually start then the bit won't toggle + // at all so the operation completes rather quickly. + // + // If at any time DQ5 is set (indicating a timeout inside the + // chip) then a reset command must be issued and the erase is + // aborted. It is not clear this can actually happen during an + // erase, but just in case. + for (retries = CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_TIMEOUT; + retries > 0; + retries--) { + + datum = addr[AM29_OFFSET_COMMAND]; + // The operation completes when all DQ7 bits are set. + if ((datum & AM29_STATUS_DQ7) == AM29_STATUS_DQ7) { + break; + } + // Otherwise, for any flash chips where DQ7 is still clear, it is + // necessary to test DQ5. + if (((datum ^ AM29_STATUS_DQ7) >> 2) & datum & AM29_STATUS_DQ5) { + // DQ5 is set, indicating a hardware error. The calling code + // will always verify that the erase really was successful + // so we do not need to distinguish between error conditions. + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + break; + } + } + + // A result of 0 indicates a timeout. + // A non-zero result indicates + // that the erase completed or there has been a fatal error. + AM29_2RAM_EXIT_HOOK(); + return retries; +} + +// Write data to flash. At most one block will be processed at a time, +// but the write may be for a subset of the write. The destination +// address will be aligned in a way suitable for the bus. The source +// address need not be aligned. The count is in AM29_TYPE's, i.e. +// as per the bus alignment, not in bytes. +static int +AM29_FNNAME(am29_hw_program)(volatile AM29_TYPE* block_start, volatile AM29_TYPE* addr, const cyg_uint8* buf, cyg_uint32 count, int retries) +{ + int i; + + AM29_2RAM_ENTRY_HOOK(); + + for (i = 0; (i < count) && (retries > 0); i++) { + AM29_TYPE datum, current, active_dq7s; + + // We can only clear bits, not set them, so any bits that were + // already clear need to be preserved. + current = addr[i]; + datum = AM29_NEXT_DATUM(buf) & current; + if (datum == current) { + // No change, so just move on. + continue; + } + + block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + block_start[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_PROGRAM; + HAL_MEMORY_BARRIER(); + addr[i] = datum; + HAL_MEMORY_BARRIER(); + + // The data is now being written. The official algorithm is + // to poll either DQ7 or DQ6, checking DQ5 along the way for + // error conditions. This gets complicated with parallel + // flash chips because they may finish at different times. + // The alternative approach is to ignore the status bits + // completely and just look for current==datum until the + // retry count is exceeded. However that does not cope + // cleanly with cases where the flash chip reports an error + // early on, e.g. because a flash block is locked. + + while (--retries > 0) { +#if CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_DELAY > 0 + // Some chips want a delay between polling + { int j; for( j = 0; j < CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_DELAY; j++ ); } +#endif + // While the program operation is in progress DQ7 will read + // back inverted from datum. + current = addr[i]; + if ((current & AM29_STATUS_DQ7) == (datum & AM29_STATUS_DQ7)) { + // All DQ7 bits now match datum, so the operation has completed. + // But not necessarily successfully. On some chips DQ7 may + // toggle before DQ0-6 are valid, so we need to read the + // data one more time. + current = addr[i]; + if (current != datum) { + retries = 0; // Abort this burst. + } + break; + } + + // Now we want to check the DQ5 status bits, but only for those + // chips which are still programming. ((current^datum) & DQ7) gives + // ones for chips which are still programming, zeroes for chips when + // the programming is complete. + active_dq7s = (current ^ datum) & AM29_STATUS_DQ7; + + if (0 != (current & (active_dq7s >> 2))) { + // Unfortunately this is not sufficient to prove an error. On + // some chips DQ0-6 switch to the data while DQ7 is still a + // status flag, so the set DQ5 bit detected above may be data + // instead of an error. Check again, this time DQ7 may + // indicate completion. + // + // Next problem. Suppose chip A gave a bogus DQ5 result earlier + // because it was just finishing. For this read chip A gives + // back datum, but now chip B is finishing and has reported a + // bogus DQ5. + // + // Solution: if any of the DQ7 lines have changed since the last + // time, go around the loop again. When an error occurs DQ5 + // remains set and DQ7 remains toggled, so there is no harm + // in one more polling loop. + + current = addr[i]; + if (((current ^ datum) & AM29_STATUS_DQ7) != active_dq7s) { + continue; + } + + // DQ5 has been set in a chip where DQ7 indicates an ongoing + // program operation for two successive reads. That is an error. + // The hardware is in a strange state so must be reset. + block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + retries = 0; + break; + } + // No DQ5 bits set in chips which are still programming. Poll again. + } // Retry for current word + } // Next word + + // At this point retries holds the total number of retries left. + // 0 indicates a timeout or fatal error. + // >0 indicates success. + AM29_2RAM_EXIT_HOOK(); + return retries; +} + +// FIXME: implement a separate program routine for buffered writes. + +#if 0 +// Unused for now, but might be useful later +static int +AM29_FNNAME(at49_hw_is_locked)(volatile AM29_TYPE* addr) +{ + int result; + AM29_TYPE plane; + + AM29_2RAM_ENTRY_HOOK(); + + // Plane is bits A21-A20 for AT49BV6416 + // A more generic formula is needed. + plane = AM29_PARALLEL( ((((CYG_ADDRESS)addr)>>21) & 0x3) ); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND + plane] = AM29_COMMAND_AUTOSELECT; + HAL_MEMORY_BARRIER(); + result = addr[AM29_OFFSET_AT49_LOCK_STATUS]; + addr[0] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + // The bottom two bits hold the lock status, LSB indicates + // soft lock, next bit indicates hard lock. We don't distinguish + // in this function. + AM29_2RAM_EXIT_HOOK(); + return (0 != (result & AM29_ID_LOCKED)); +} +#endif + +static int +AM29_FNNAME(at49_hw_softlock)(volatile AM29_TYPE* addr) +{ + int result = CYG_FLASH_ERR_OK; + + AM29_2RAM_ENTRY_HOOK(); + + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AT49_SOFTLOCK_BLOCK_0; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[0] = AM29_COMMAND_AT49_SOFTLOCK_BLOCK_1; + HAL_MEMORY_BARRIER(); + // not sure if this is required: + addr[0] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + AM29_2RAM_EXIT_HOOK(); + return result; +} + +static int +AM29_FNNAME(at49_hw_hardlock)(volatile AM29_TYPE* addr) +{ + int result = CYG_FLASH_ERR_OK; + + AM29_2RAM_ENTRY_HOOK(); + + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AT49_HARDLOCK_BLOCK_0; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + HAL_MEMORY_BARRIER(); + addr[0] = AM29_COMMAND_AT49_HARDLOCK_BLOCK_1; + HAL_MEMORY_BARRIER(); + // not sure if this is required: + addr[0] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + AM29_2RAM_EXIT_HOOK(); + return result; +} + +static int +AM29_FNNAME(at49_hw_unlock)(volatile AM29_TYPE* addr) +{ + int result = CYG_FLASH_ERR_OK; + + AM29_2RAM_ENTRY_HOOK(); + + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + HAL_MEMORY_BARRIER(); + addr[0] = AM29_COMMAND_AT49_UNLOCK_BLOCK; + HAL_MEMORY_BARRIER(); + // not sure if this is required: + addr[0] = AM29_COMMAND_RESET; + HAL_MEMORY_BARRIER(); + AM29_2RAM_EXIT_HOOK(); + return result; +} + + +// ---------------------------------------------------------------------------- +// Exported code, mostly for placing in a cyg_flash_dev_funs structure. + +// Just read the device id, either for sanity checking that the system +// has been configured for the right device, or for filling in the +// block info by a platform-specific init routine if the platform may +// be manufactured with one of several different chips. +int +AM29_FNNAME(cyg_am29xxxxx_read_devid) (struct cyg_flash_dev* dev) +{ + int (*query_fn)(volatile AM29_TYPE*); + int devid; + volatile AM29_TYPE* addr; + AM29_INTSCACHE_STATE; + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + + amd_diag("\n"); + + addr = AM29_UNCACHED_ADDRESS(dev->start); + query_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_query) ); + AM29_INTSCACHE_BEGIN(); + devid = (*query_fn)(addr); + AM29_INTSCACHE_END(); + return devid; +} + +// Validate that the device statically configured is the one on the +// board. +int +AM29_FNNAME(cyg_am29xxxxx_init_check_devid)(struct cyg_flash_dev* dev) +{ + cyg_am29xxxxx_dev* am29_dev; + int devid; + + amd_diag("\n"); + + am29_dev = (cyg_am29xxxxx_dev*) dev->priv; + devid = AM29_FNNAME(cyg_am29xxxxx_read_devid)(dev); + if (devid != am29_dev->devid) { + return CYG_FLASH_ERR_DRV_WRONG_PART; + } + +#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME + { + volatile AM29_TYPE *addr = AM29_UNCACHED_ADDRESS(dev->start); + void (*resume_fn)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*); + resume_fn = (void (*)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*)) + cyg_flash_anonymizer( &AM29_FNNAME(am29_hw_force_all_suspended_resume) ); + AM29_INTSCACHE_STATE; + + AM29_INTSCACHE_BEGIN(); + (*resume_fn)(dev, am29_dev, addr); + AM29_INTSCACHE_END(); + } +#endif + + // Successfully queried the device, and the id's match. That + // should be a good enough indication that the flash is working. + return CYG_FLASH_ERR_OK; +} + +// Initialize via a CFI query, instead of statically specifying the +// boot block layout. +int +AM29_FNNAME(cyg_am29xxxxx_init_cfi)(struct cyg_flash_dev* dev) +{ + int (*cfi_fn)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*); + volatile AM29_TYPE* addr; + cyg_am29xxxxx_dev* am29_dev; + int result; + AM29_INTSCACHE_STATE; + + amd_diag("\n"); + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + am29_dev = (cyg_am29xxxxx_dev*) dev->priv; // Remove const, only place where this is needed. + addr = AM29_UNCACHED_ADDRESS(dev->start); + cfi_fn = (int (*)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*)) + cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_cfi)); + + AM29_INTSCACHE_BEGIN(); + result = (*cfi_fn)(dev, am29_dev, addr); + AM29_INTSCACHE_END(); + + // Now calculate the device size, and hence the end field. + if (CYG_FLASH_ERR_OK == result) { + int i; + int size = 0; + for (i = 0; i < dev->num_block_infos; i++) { + amd_diag("region %d: 0x%08x * %d\n", i, (int)dev->block_info[i].block_size, dev->block_info[i].blocks ); + size += (dev->block_info[i].block_size * dev->block_info[i].blocks); + } + dev->end = dev->start + size - 1; + +#ifdef CYGHWR_DEVS_FLASH_AMD_AM29XXXXX_V2_RESET_NEEDS_RESUME + { + void (*resume_fn)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*); + resume_fn = (void (*)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*)) + cyg_flash_anonymizer( &AM29_FNNAME(am29_hw_force_all_suspended_resume) ); + + AM29_INTSCACHE_BEGIN(); + (*resume_fn)(dev, am29_dev, addr); + AM29_INTSCACHE_END(); + } +#endif + } + return result; +} + +// Erase a single block. The calling code will have supplied a pointer +// aligned to a block boundary. +int +AM29_FNNAME(cyg_am29xxxxx_erase)(struct cyg_flash_dev* dev, cyg_flashaddr_t addr) +{ + int (*erase_fn)(volatile AM29_TYPE*); + volatile AM29_TYPE* block; + cyg_flashaddr_t block_start; + size_t block_size; + int i; + int result; + AM29_INTSCACHE_STATE; + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + CYG_ASSERT((addr >= dev->start) && (addr <= dev->end), "flash address out of device range"); + + am29_get_block_info(dev, addr, &block_start, &block_size); + CYG_ASSERT(addr == block_start, "erase address should be the start of a flash block"); + + amd_diag("addr %p block %p[%d]\n", (void*)addr, (void*)block_start, (int)block_size ); + + block = AM29_UNCACHED_ADDRESS(addr); + erase_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_erase) ); + + AM29_INTSCACHE_BEGIN(); + result = (*erase_fn)(block); + AM29_INTSCACHE_END(); + + // The erase may have failed for a number of reasons, e.g. because + // of a locked sector. The best thing to do here is to check that the + // erase has succeeded. + block = (AM29_TYPE*) addr; + for (i = 0; i < (block_size / sizeof(AM29_TYPE)); i++) { + if (block[i] != (AM29_TYPE)~0) { + // There is no easy way of detecting the specific error, + // e.g. locked flash block, timeout, ... So return a + // useless catch-all error. + return CYG_FLASH_ERR_ERASE; + } + } + return CYG_FLASH_ERR_OK; +} + +// Write some data to the flash. The destination must be aligned +// appropriately for the bus width (not the device width). +int +AM29_FNNAME(cyg_am29xxxxx_program)(struct cyg_flash_dev* dev, cyg_flashaddr_t dest, const void* src, size_t len) +{ + int (*program_fn)(volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32, int); + volatile AM29_TYPE* block; + volatile AM29_TYPE* addr; + cyg_flashaddr_t block_start; + size_t block_size; + const cyg_uint8* data; + int retries; + int i; + + AM29_INTSCACHE_STATE; + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + + amd_diag("dest %p src %p len %d\n", (void*)dest, (void*)src, (int)len ); + + // Only support writes that are aligned to the bus boundary. This + // may be more restrictive than what the hardware is capable of. + // However it ensures that the hw_program routine can write as + // much data as possible each iteration, and hence significantly + // improves performance. The length had better be a multiple of + // the bus width as well + if ((0 != ((CYG_ADDRWORD)dest & (sizeof(AM29_TYPE) - 1))) || + (0 != (len & (sizeof(AM29_TYPE) - 1)))) { + return CYG_FLASH_ERR_INVALID; + } + + addr = AM29_UNCACHED_ADDRESS(dest); + CYG_ASSERT((dest >= dev->start) && (dest <= dev->end), "flash address out of device range"); + + am29_get_block_info(dev, dest, &block_start, &block_size); + CYG_ASSERT(((dest - block_start) + len) <= block_size, "write cannot cross block boundary"); + + block = AM29_UNCACHED_ADDRESS(block_start); + data = (const cyg_uint8*) src; + retries = CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT; + + program_fn = (int (*)(volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32, int)) + cyg_flash_anonymizer( & AM29_FNNAME(am29_hw_program) ); + + AM29_INTSCACHE_BEGIN(); + (*program_fn)(block, addr, data, len / sizeof(AM29_TYPE), retries); + AM29_INTSCACHE_END(); + + // Too many things can go wrong when manipulating the h/w, so + // verify the operation by actually checking the data. + addr = (volatile AM29_TYPE*) dest; + data = (const cyg_uint8*) src; + for (i = 0; i < (len / sizeof(AM29_TYPE)); i++) { + AM29_TYPE datum = AM29_NEXT_DATUM(data); + AM29_TYPE current = addr[i]; + if ((datum & current) != current) { + amd_diag("data %p addr[i] %p datum %08x current %08x\n", data-sizeof(AM29_TYPE), &addr[i], datum, current ); + return CYG_FLASH_ERR_PROGRAM; + } + } + return CYG_FLASH_ERR_OK; +} + +int +AM29_FNNAME(cyg_at49xxxx_softlock)(struct cyg_flash_dev* dev, const cyg_flashaddr_t dest) +{ + volatile AM29_TYPE* uncached; + int result; + int (*lock_fn)(volatile AM29_TYPE*); + AM29_INTSCACHE_STATE; + + amd_diag("\n"); + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + CYG_ASSERT((dest >= (cyg_flashaddr_t)dev->start) && + ((CYG_ADDRESS)dest <= dev->end), "flash address out of device range"); + + uncached = AM29_UNCACHED_ADDRESS(dest); + lock_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(at49_hw_softlock) ); + AM29_INTSCACHE_BEGIN(); + result = (*lock_fn)(uncached); + AM29_INTSCACHE_END(); + return result; +} + +int +AM29_FNNAME(cyg_at49xxxx_hardlock)(struct cyg_flash_dev* dev, const cyg_flashaddr_t dest) +{ + volatile AM29_TYPE* uncached; + int result; + int (*lock_fn)(volatile AM29_TYPE*); + AM29_INTSCACHE_STATE; + + amd_diag("\n"); + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + CYG_ASSERT((dest >= (cyg_flashaddr_t)dev->start) && + ((CYG_ADDRESS)dest <= dev->end), "flash address out of device range"); + + uncached = AM29_UNCACHED_ADDRESS(dest); + lock_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(at49_hw_hardlock) ); + AM29_INTSCACHE_BEGIN(); + result = (*lock_fn)(uncached); + AM29_INTSCACHE_END(); + return result; +} + +int +AM29_FNNAME(cyg_at49xxxx_unlock)(struct cyg_flash_dev* dev, const cyg_flashaddr_t dest) +{ + volatile AM29_TYPE* uncached; + int result; + int (*unlock_fn)(volatile AM29_TYPE*); + AM29_INTSCACHE_STATE; + + amd_diag("\n"); + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + CYG_ASSERT((dest >= (cyg_flashaddr_t)dev->start) && + ((CYG_ADDRESS)dest <= dev->end), "flash address out of device range"); + + uncached = AM29_UNCACHED_ADDRESS(dest); + unlock_fn = (int (*)(volatile AM29_TYPE*)) cyg_flash_anonymizer( & AM29_FNNAME(at49_hw_unlock) ); + + AM29_INTSCACHE_BEGIN(); + result = (*unlock_fn)(uncached); + AM29_INTSCACHE_END(); + return result; +} + +// ---------------------------------------------------------------------------- +// Clean up the various #define's so this file can be #include'd again +#undef AM29_FNNAME +#undef AM29_RAMFNDECL +#undef AM29_OFFSET_COMMAND +#undef AM29_OFFSET_COMMAND2 +#undef AM29_OFFSET_DEVID +#undef AM29_OFFSET_DEVID2 +#undef AM29_OFFSET_DEVID3 +#undef AM29_OFFSET_CFI +#undef AM29_OFFSET_CFI_DATA +#undef AM29_OFFSET_AT49_LOCK_STATUS +#undef AM29_PARALLEL diff --git a/packages/io/flash/current/ChangeLog b/packages/io/flash/current/ChangeLog --- a/packages/io/flash/current/ChangeLog +++ b/packages/io/flash/current/ChangeLog @@ -1,18 +1,312 @@ -2006-02-21 Oliver Munz - Andrew Lunn +2008-08-29 Nick Garnett + + * src/flashiodev.c (flashiodev_init): Assign + CYG_DEVTAB_STATUS_BLOCK to status field of device table + entries. This causes devfs to call the right read/write + operations. + +2008-06-10 Jonathan Larmour + + * tests/flashdev.c (FLASH_TEST_LENGTH): Reduce to a + multiple of 0x20000 as some flash parts (especially if + they are in fact multiple chips in parallel) have that + block size. + Try opening jffs2test FIS partition if flashtest doesn't exist. + +2007-05-14 Jonathan Larmour + + * src/flashiodev.c (flashiodev_get_config): Support new + CYG_IO_GET_CONFIG_FLASH_DEVADDR key to get flash device + base address. + * include/flash.h: Associated structure for key. + * doc/flash.sgml: Document CYG_IO_GET_CONFIG_FLASH_DEVADDR. + +2006-12-18 Jonathan Larmour + + * tests/flash1.c: Replace FLASH_ERR_OK with CYG_FLASH_ERR_OK. + * tests/flashdev.c: Ditto. Also flash_errmsg -> + cyg_flash_errmsg. And some warning cleanups. + + * src/flashiodev.c (flashiodev_lookup): Correct misplaced else + with respect to #ifdefs. Fix for bug 1000355. + +2006-12-18 Nick Garnett + + * src/flash_legacy.h (FLASH_Enable, FLASH_Disable): Add casts to + these macros since the calls in the source don't present the right + types. This is mainly an exercise in suppressing compiler + warnings. + +2006-12-07 Nick Garnett + + * tests/flashdev.c (cyg_start): Fix bug, b.offset should be + initialized to 0. Until now it was some random value on the stack, + why this has never bitten us before is a mystery. + +2006-10-31 Jonathan Larmour + + * src/legacy_dev.c (legacy_flash_program): Define block_size. + +2006-10-03 Jonathan Larmour + + * doc/flash.sgml: Bring into line with actual implementation. + Also some trivial typos. - * src/flash.c (flash_init): Allow repeat calls change the function - used for printing. There are times you don't any output, eg you - are downloading am image over the serial port. +2006-08-01 Jonathan Larmour + + * src/flash.c (cyg_flash_devfn_lock_nop): Don't return error + if this is used with one of multiple flash devices in a + system where a different one does support locking. + (cyg_flash_devfn_unlock_nop): Ditto. + +2006-06-06 John Dallaway + + * cdl/io_flash.cdl: Fix reference to package documentation. + +2006-05-23 Jonathan Larmour + + * src/flash.c (cyg_flash_erase): Report errors and error + addresses correctly. + (cyg_flash_program): Ditto. + +2005-12-21 Jonathan Larmour + + * doc/flash.sgml: Document flash block device lock/unlock config keys. + +2005-10-26 Nick Garnett + + * include/flash.h (cyg_io_flash_getconfig_lock_t) + (cyg_io_flash_getconfig_unlock_t): Added these aliases for + cyg_io_flash_getconfig_erase_t. + + * cdl/io_flash.cdl: Added configury to control compilation of + flash1 test. Added flashdev test. + + * src/legacy_api.c (flash_lock, flash_unlock): Added these functions. + + * src/flashiodev.c (flashiodev_get_config): Added support for lock + and unlock operations. + + * tests/flashdev.c: Added this test to exercise flash /dev/flash + device. Functionally it is similar to the flash1 test. + + * tests/flash1.c : Changed test for legacy API. + (cyg_start): Added tests for lock and unlock functions. + +2005-09-28 Jonathan Larmour + + * src/flash.c (flash_sort_and_check): Allow for Flash + device initialisation to fail, and thus not put this device + on the Flash device list. + +2005-09-26 Bart Veer + + * src/flash.c: put init flag into .bss rather than .data, avoids + some problems when performing soft resets. + +2005-08-16 Jonathan Larmour + + * src/flash.c (cyg_flash_read): Comment out chatter. It's not + very useful for this function. 2005-08-02 Andrew Lunn * tests/flash1.c (cyg_start): Compiler warning fixes. +2005-05-27 Jonathan Larmour + + * tests/flash1.c (cyg_start): Call CYG_TEST_INIT(). + 2005-03-27 Andrew Lunn * tests/flash1.c: Fixed compiler warning with diag_printf. +2005-02-28 Jonathan Larmour + + * include/flash.h: Getconfig/setconfig definitions are relevant for + both new and legacy block device support. + +2005-01-28 Nick Garnett + + * src/legacy_dev.c (legacy_flash_init): Assign flash_info.pf from + flash device field. Otherwise drivers try to call an invalid + address. + (legacy_flash_program): The last argument to flash_program_buf() + is not the block size but the programming buffer size. + +2005-01-21 Jonathan Larmour + + * doc/flash.sgml: Document Flash block devices. + +2005-01-19 Jonathan Larmour + + * src/flashiodev.c: Redesign to use more flexible flash names. + * src/flashiodevlegacy.c: Previous version of flashiodev.c renamed + to this. + * cdl/io_flash.cdl: Support new flashiodev design. + * include/flash.h: don't double indirect err_address address + pointer in cyg_io_flash_getconfig_erase_t. + * src/flash_legacy.h: Undef HAL_FLASH_CACHES_* macros if the HAL + supplied them and they're not needed. + + * src/legacy_api.c (flash_errmsg): Was missing. Add. + + * tests/flash1.c (cyg_start): Silence warning. + +2004-12-02 Bart Veer + + * include/flash_dev.h: now provides everything needed by flash + device drivers. + + * include/flash_priv.h: removed, subsumed by flash_dev.h + + * include/flash.h, src/flash.c, src/flashiodev.c, + src/legacy_api.c, src/legacy_dev.c: + Replace implicit #include's of with explicit + #include's of + +2004-11-29 Bart Veer + + * include/flash_priv.h, src/flash.c, src/legacy_dev.c: remove + hwr_map_error() from V2 drivers + + * include/flash_priv.h, src/flash.c, src/legacy_dev.c: provide + dummy init/query/lock/unlock functions for use by device drivers + which do not support/need this functionality. Export the + anonymizer function. + +2004-11-28 John Dallaway + + * doc/flash.sgml: Fix minor SGML tag problems. + +2004-11-28 Bart Veer + + * src/flash.c (flash_sort_and_check): previous patch would have + resulted in init failure if only one device initialized. + (cyg_flash_get_info): handle per-device init flag + +2004-11-25 Andrew Lunn + + * src/flash.c (flash_sort_and_check): Don't add devices which + failed to initialise onto the list. Don't bother sorting the + list if its empty or only has one entry. + * src/flash.c (find_dev): All devices on the list are initialised so + don't both checking the init flag. + +2004-11-24 Bart Veer + + * cdl/io_flash.cdl, src/flash.c, src/flash_legacy.h, + src/legacy_dev.c: add support for V2 drivers which can take care + of the cache and interrupts themselves. + + * tests/flash1.c (cyg_start): mark N/A for now if a V2 driver is + being used. + * Merge from flash V2 branch + + 2004-11-22 Bart Veer + + * src/legacy_dev.c: remove .2ram attributes. These functions do + not manipulate the hardware, there is no need for them to live in + ram rather than flash + * include/flash_priv.h, src/legacy_dev.c: clean up the interface + between the generic flash code and the device drivers + * include/flash_priv, src/flash_legacy.h, src/flash.c, + src/legacy_dev.c: move cache stuff andother legacy macros + to an internal header. + * include/flash.h, include/flash_priv.h, doc/flash.sgml, + src/flash.c, src/legacy_dev.c: put const in the right places, + and rename cyg_block_info to cyg_flash_block_info + + 2004-11-21 Bart Veer + + * src/flash.c, include/flash_priv.h, cdl/io_flash.h: optimize + for the case of a single flash device. + * include/flash.h, src/flash.c, doc/flash.sgml: remove + cyg_flash_code_overlaps() + * include/flash_priv.h, include/flash.h: + CYGSEM_IO_FLASH_LEGACY_DEVICE_API has been removed, test + CYGHWR_IO_FLASH_DEVICE_LEGACY instead + * cdl/io_flash.cdl: lots of clean-ups + * src/legacy_dev.c (legacy_flash_read): only needed if the + underlying legacy driver required indirect reads. + * src/flash.c: encapsulate various optional bits of code in + macros, to cut down on the #ifdef's in the main code + + 2004-11-20 Bart Veer + + * cdl/io_flash.cdl, src/flash.c(cyg_flash_read): add an interface + for hardware which requires indirect reads, and suppress + unnecessary code if direct reads are always available. + * src/flash.c: rearrange loops to avoid address comparisons, which + tend to go wrong if the flash is at the end of the address space + + 2004-10-06 Andrew Lunn + + * src/legacy_dev.c (legacy_flash_init): The end is the size plus + the start. + + 2004-09-30 Savin Zlobec + + * src/flash.c: Offset into the block is not calculated correctly + when the flash start is not aligned on a flash block. + * src/legacy_dev.c (legacy_flash_read): Fixed typo. + + 2004-09-14 Andrew Lunn + + * src/flash.c (cyg_flash_init): Add assert checking that the + end address matches the block information. + * src/legacy_dev.c (legacy_flash_init): Ensure that the end + address is the last valid address, not the first invalid address. + + 2004-09-09 Andrew Lunn + + * src/flash.c: Support flash blocks of arbitary size. The + DataFlash devices for example have block of 528 bytes. + + 2004-08-21 Andrew Lunn + + * removed the functions cyg_flash_get_limits and + cyg_flash_get_block_info. Reimplemented the legacy functions + that depended on them and flashiodev.c + + 2004-08-19 Andrew Lunn + + * src/flashiodev.c Ordering problem again. If an invalid name is + configured the lookup would fail and it then was impossible to do + a cyg_io_config_set. Lookup cannot fail because the name does not + exist in FIS. + + 2004-08-13 Andrew Lunn + + * src/flash.c: Make the flock lock/unlock code compile without + warnings. + * src/flashiodev.c (flashiodev_lookup): Only initialize the device + if we have not already been initialized via cyg_set_config(). + This stopped redboots mount -f working. + + 2004-08-09 Andrew Lunn + + * src/flashiodev.c (flashiodev_lookup): Moved most of the + flashiodev_init into this new function. This fixed the ordering + issue with redboot startup. When doing a FIS name lookup in + flashiodev_init, redboot was not yet nitialised so lookup + failed. Moving this into flashiodev_lookup solves this problem. + Its now possible for redboot to mount the jffs2 filesystem with + -d /dev/flash1. + + 2004-08-06 Andrew Lunn + + * src/flashiodev.c: Fix typo in macro and configuration options + + 2004-08-05 Andrew Lunn + + * 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. + 2004-09-05 Mark Salter * doc/flash.sgml: Fix unbalanced s. @@ -49,7 +343,7 @@ 2003-11-24 Jani Monoses * cdl/io_flash.cdl: * include/flash.h: Remove CYGNUM_FLASH_WORKSPACE_SIZE cdl option. -2003-11-24 Roland Caßebohm +2003-11-24 Roland Caßebohm * src/flashiodev.c (flashiodev_bwrite): Use flash_read rather than directly doing a memcpy. @@ -331,6 +625,7 @@ 2000-07-29 Gary Thomas " - puts $::cdl_header "#ifdef CYGDAT_IO_FLASH_DEVICE_HEADER" - puts $::cdl_header "# include CYGDAT_IO_FLASH_DEVICE_HEADER" - puts $::cdl_header "#endif " - } - cdl_interface CYGHWR_IO_FLASH_DEVICE { - display "Hardware FLASH device drivers" - description " - This option enables the hardware device drivers - for the current platform." - } - - cdl_interface CYGHWR_IO_FLASH_DEVICE_NOT_IN_RAM { - display "Hardware FLASH device drivers are not in RAM" - flavor booldata - description " - Use of this interface is deprecated. - Drivers should make sure that the functions are - linked to RAM by putting them in .2ram sections." - requires !CYGHWR_IO_FLASH_DEVICE_NOT_IN_RAM + display "Hardware FLASH device drivers" + requires { CYGHWR_IO_FLASH_DEVICE >= 1 } + description " + This calculated option gives the number of flash devices + on the current platform. The generic flash support requires + at least one device." } cdl_interface CYGHWR_IO_FLASH_BLOCK_LOCKING { - display "Hardware can support block locking" - flavor booldata - description " + display "Hardware can support block locking" + flavor booldata + description " This option will be enabled by devices which can support - locking (write-protection) of individual blocks." + locking (write-protection) of individual flash blocks. If + none of the devices support locking then the relevant code + in the generic flash package can be eliminated." + } + + cdl_interface CYGHWR_IO_FLASH_INDIRECT_READS { + display "Hardware requires indirect reads" + flavor booldata + description " + Some flash devices can be read directly like any other + memory. Others can only be accessed indirectly, which + involves extra code. If none of the flash devices on the + target hardware use indirect reads then the extra code + can be eliminated." + } + + cdl_interface CYGHWR_IO_FLASH_DEVICE_NEEDS_CACHE_HANDLED { + display "Device driver needs cache handled centrally" + flavor booldata + description " + Managing flash often requires interacting with the cache. + Some device drivers do not require the generic flash code to + manipulate the cache, either because they do it themselves or + because the hardware does the right thing. Other drivers + require the generic code to disable/enable the cache before + calling into the driver." + } + + cdl_interface CYGHWR_IO_FLASH_DEVICE_LEGACY { + display "Hardware driver uses the legacy interface" + flavor booldata + compile -library=libextras.a legacy_dev.c + description " + The generic flash code can work with either a legacy device + driver or with V2 drivers. If a legacy driver is used on the + current platform then this option will be implemented." + requires { CYGHWR_IO_FLASH_DEVICE_LEGACY <= 1 } + + # For now assume all legacy devices need the cache handled by + # the central code + implements CYGHWR_IO_FLASH_DEVICE_NEEDS_CACHE_HANDLED } cdl_option CYGSEM_IO_FLASH_READ_INDIRECT { - display "Hardware cannot support direct access to FLASH memory" - flavor bool - default_value 0 - requires { !CYGSEM_IO_FLASH_VERIFY_PROGRAM } - description " + display "Legacy device driver uses indirect reads." + active_if CYGHWR_IO_FLASH_DEVICE_LEGACY + flavor bool + default_value 0 + requires { !CYGSEM_IO_FLASH_VERIFY_PROGRAM } + description " This option will be asserted by devices which cannot support direct access to the FLASH memory contents (e.g. EEPROM or NAND devices). In these cases, the driver must provide an appropriate @@ -107,19 +135,20 @@ cdl_package CYGPKG_IO_FLASH { } cdl_option CYGSEM_IO_FLASH_CHATTER { - display "Display status messages during flash operations" - flavor bool - default_value 1 - description " - Selecting this option will cause the drivers to print status + display "Display status messages during flash operations" + flavor bool + default_value 1 + description " + Selecting this option will cause the flash code to print status messages as various flash operations are undertaken." } cdl_option CYGSEM_IO_FLASH_VERIFY_PROGRAM { - display "Verify data programmed to flash" - flavor bool - default_value 1 - description " + display "Verify data programmed to flash" + flavor bool + default_value 1 + active_if { CYGHWR_IO_FLASH_INDIRECT_READS < CYGHWR_IO_FLASH_DEVICE } + description " Selecting this option will cause verification of data programmed to flash." } @@ -135,65 +164,198 @@ cdl_package CYGPKG_IO_FLASH { } cdl_component CYGPKG_IO_FLASH_BLOCK_DEVICE { - display "Instantiate in I/O block device API" + display "Provide /dev block devices" flavor bool active_if CYGPKG_IO default_value 0 compile -library=libextras.a flashiodev.c + requires { CYGINT_IO_FLASH_BLOCK_DEVICE_METHODS > 0} description " - Provides a block device accessible using the standard I/O - API ( cyg_io_read() etc. )" + Provide one or more block devices below /dev which can be + accessed using standard I/O functions such as eCos cyg_io_read() + and POSIX open()." + + cdl_interface CYGINT_IO_FLASH_BLOCK_DEVICE_METHODS { + display "Number of methods to reference Flash" + flavor bool + no_define + description "This interface counts the number of ways Flash can + be referenced in order to be opened. This is so that + the configuration can ensure that at least one method + is available." + } + + cdl_option CYGNUM_IO_FLASH_BLOCK_DEVICES { + display "Number of /dev/flash/ device slots" + flavor data + default_value 2 + description "The number of simultaneously open flash devices + permitted. This is statically configured, rather than + dynamically allocated at run-time as it is likely the + number required will be small." + } - cdl_component CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1 { - display "Name of flash device 1 block device" - flavor data - default_value { "\"/dev/flash1\"" } - - cdl_interface CYGINT_IO_FLASH_BLOCK_CFG_1 { - requires 1 == CYGINT_IO_FLASH_BLOCK_CFG_1 - no_define - } + cdl_option CYGFUN_IO_FLASH_BLOCK_FROM_FIS { + display "Access using named FIS areas" + requires CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT + default_value CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT + implements CYGINT_IO_FLASH_BLOCK_DEVICE_METHODS + description "This method of access to Flash regions uses the names + of FIS regions to identify the Flash device, or portion + of the device. For example /dev/flash/fis/jffs2." + } + cdl_option CYGFUN_IO_FLASH_BLOCK_FROM_DEVOFFSET { + display "Access using device numbers,offsets" + default_value 1 + implements CYGINT_IO_FLASH_BLOCK_DEVICE_METHODS + description "This method of access to Flash regions uses the Flash + device number, along with an offset (which may be + 0 to indicate the start of the device) and an optional length + which will otherwise be assumed to be the whole device. + For example /dev/flash/0/0x1000 or + /dev/flash/1/0,65536." + } + } + + cdl_option CYGSEM_IO_FLASH_LEGACY_API { + display "Provide the legacy user API" + flavor bool + default_value 1 + compile legacy_api.c + description " + Provide an implementation of the lagacy user API. This is mapped + onto the new API via a small layer of code" + } + + cdl_component CYGPKG_IO_FLASH_BLOCK_DEVICE_LEGACY { + display "Provide /dev block devices (legacy format)" + flavor bool + active_if CYGPKG_IO + default_value 0 + compile -library=libextras.a flashiodevlegacy.c + description " + Provide one or more block devices below /dev which can be + accessed using standard I/O functions such as eCos cyg_io_read() + and POSIX open(). This format of configuration is deprecated, + but is included for compatibility with older configurations." + + for { set ::dev 1 } { $::dev <= 2 } { incr ::dev } { + + cdl_component CYGPKG_IO_FLASH_BLOCK_DEVICE_$::dev { + display "Provide block device $::dev" + if { 1 == $::dev } { + default_value 1 + } else { + default_value 0 + } + description "Provide block device $::dev for use with standard I/O" - cdl_component CYGNUM_IO_FLASH_BLOCK_CFG_STATIC_1 { - display "Static configuration" - default_value 1 - implements CYGINT_IO_FLASH_BLOCK_CFG_1 - description " - This configures the flash device 1 block device - with static base and length" - - cdl_option CYGNUM_IO_FLASH_BLOCK_OFFSET_1 { - display "Start offset from flash base" - flavor data - default_value 0x100000 - description " - This gives the offset from the base of flash which this - block device corresponds to." - } - cdl_option CYGNUM_IO_FLASH_BLOCK_LENGTH_1 { - display "Length" - flavor data - default_value 0x100000 - description " - This gives the length of the region of flash given over - to this block device." - } - } - cdl_component CYGNUM_IO_FLASH_BLOCK_CFG_FIS_1 { - display "Configuration from FIS" - default_value 0 - implements CYGINT_IO_FLASH_BLOCK_CFG_1 - description " - This configures the flash device 1 block device - from Redboot FIS" - - cdl_component CYGDAT_IO_FLASH_BLOCK_FIS_NAME_1 { - display "Name of FIS entry" - flavor data - default_value { "\"jffs2\"" } - } - } - } + cdl_option CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_$::dev { + display "Name of flash block device $::dev" + flavor data + default_value "\"\\\"/dev/flash$::dev\\\"\"" + description " + This option determines the name by which the block + device can be accessed." + } + + cdl_interface CYGINT_IO_FLASH_BLOCK_CFG_$::dev { + requires 1 == CYGINT_IO_FLASH_BLOCK_CFG_$::dev + no_define + } + + cdl_component CYGNUM_IO_FLASH_BLOCK_CFG_STATIC_$::dev { + display "Static configuration via offset" + default_value 1 + implements CYGINT_IO_FLASH_BLOCK_CFG_$::dev + description " + This configures the flash block device $::dev + with static offset from the base of the first + flash device and length" + + cdl_option CYGNUM_IO_FLASH_BLOCK_OFFSET_$::dev { + display "Start offset from flash base" + flavor data + default_value 0xFFFFFFFF + requires 0xFFFFFFFF != CYGNUM_IO_FLASH_BLOCK_OFFSET_$::dev + description " + This gives the offset from the base of flash + of the first flash device which this block device + corresponds to." + } + cdl_option CYGNUM_IO_FLASH_BLOCK_LENGTH_$::dev { + display "Length" + flavor data + default_value 0 + requires 0 != CYGNUM_IO_FLASH_BLOCK_OFFSET_$::dev + description " + This gives the length of the region of flash given over + to this block device." + } + } + + cdl_component CYGNUM_IO_FLASH_BLOCK_CFG_STATIC_ABSOLUTE_$::dev { + display "Static configuration via absolute address" + default_value 0 + implements CYGINT_IO_FLASH_BLOCK_CFG_$::dev + description " + This configures the flash block device $::dev + with absolute base address and a length" + + cdl_option CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_START_$::dev { + display "Start offset from flash base" + flavor data + default_value 0xFFFFFFFF + requires 0xFFFFFFFF != CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_START_$::dev + description " + This gives the absolute address in flash which this + block device corresponds to." + } + cdl_option CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_LENGTH_$::dev { + display "Length" + flavor data + default_value 0 + requires 0 != CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_LENGTH_$::dev + description " + This gives the length of the region of flash given over + to this block device." + } + } + + cdl_component CYGNUM_IO_FLASH_BLOCK_CFG_FIS_$::dev { + display "Configuration from FIS" + default_value 0 + implements CYGINT_IO_FLASH_BLOCK_CFG_$::dev + description " + This configures the flash block device $::dev + from Redboot FIS" + + cdl_component CYGDAT_IO_FLASH_BLOCK_FIS_NAME_$::dev { + display "Name of FIS entry" + flavor data + requires "\"\"" != CYGDAT_IO_FLASH_BLOCK_FIS_NAME_$::dev + if { 1 == $::dev } { + default_value { "\"jffs2\"" } + } elseif { 2 == $::dev } { + default_value { "\"jffs2-2\"" } + } else { + default_value { "\"\"" } + } + } + } + } + } + } + + # FIXME: remove in next release + cdl_interface CYGHWR_IO_FLASH_DEVICE_NOT_IN_RAM { + display "Hardware FLASH device drivers are not in RAM" + flavor booldata + description " + Use of this interface is deprecated. + Drivers should make sure that the functions are + linked to RAM by putting them in .2ram sections." + requires !CYGHWR_IO_FLASH_DEVICE_NOT_IN_RAM } cdl_component CYGPKG_IO_FLASH_OPTIONS { @@ -231,28 +393,58 @@ cdl_package CYGPKG_IO_FLASH { display "Flash device driver tests" flavor data no_define - calculated { "tests/flash1" } +# requires { CYGINT_IO_FLASH_TESTS_ADDR_METHOD == 1 } + calculated { (CYGSEM_IO_FLASH_LEGACY_API ? "tests/flash1" : "") . + (CYGPKG_IO_FLASH_BLOCK_DEVICE ? " tests/flashdev" : "") } description " This option specifies the set of tests for the flash device drivers." - cdl_option CYGNUM_IO_FLASH_TEST_OFFSET { - display "Start offset from flash base" - flavor data - default_value 0x100000 - description " - This gives the offset from the base of flash where tests - can be run. It is important to set this correctly, as an - incorrect value could allow the tests to write over critical - portions of the FLASH device and possibly render the target - board totally non-functional." - } - cdl_option CYGNUM_IO_FLASH_TEST_LENGTH { - display "Length" - flavor data - default_value 0x100000 - description " - This gives the length of the region of flash used for testing." - } +# cdl_interface CYGINT_IO_FLASH_TESTS_ADDR_METHOD { +# display "Set single Flash access method" +# } +# +# +# cdl_option CYGTST_IO_FLASH_TESTS_ADDR_FIS { +# display "Use \"flashtest\" FIS entry" +# default_value CYGFUN_IO_FLASH_BLOCK_FROM_FIS +# implements CYGINT_IO_FLASH_TESTS_ADDR_METHOD +# requires CYGFUN_IO_FLASH_BLOCK_FROM_FIS +# requires !CYGTST_IO_FLASH_TESTS_ADDR_HARDCODE +# description " +# This option means that tests will use the Flash region +# identified by the \"flashtest\" FIS entry if it is found." +# } +# +# cdl_component CYGTST_IO_FLASH_TESTS_ADDR_HARDCODE { +# display "Use hardcoded offset from Flash base" +# default_value !CYGTST_IO_FLASH_TESTS_ADDR_FIS +# implements CYGINT_IO_FLASH_TESTS_ADDR_METHOD +# requires !CYGTST_IO_FLASH_TESTS_ADDR_FIS +# description " +# This option means that tests will use the flash region +# identified by the \"flashtest\" FIS entry if it is found." +# + cdl_option CYGNUM_IO_FLASH_TEST_OFFSET { + display "Start offset from flash base" + flavor data + default_value 0x100000 + description " + This gives the offset from the base of flash where tests + can be run. It is important to set this correctly, as an + incorrect value could allow the tests to write over critical + portions of the FLASH device and possibly render the target + board totally non-functional." + } + cdl_option CYGNUM_IO_FLASH_TEST_LENGTH { + display "Length" + flavor data + default_value 0x100000 + description " + This gives the length of the region of flash used for testing." + } +# } } } } + +# EOF io_flash.cdl diff --git a/packages/io/flash/current/doc/flash.sgml b/packages/io/flash/current/doc/flash.sgml --- a/packages/io/flash/current/doc/flash.sgml +++ b/packages/io/flash/current/doc/flash.sgml @@ -1,3 +1,5 @@ + + @@ -11,6 +13,7 @@ + @@ -35,9 +38,6 @@ The eCos FLASH Library The FLASH library is an optional part of eCos, and is only applicable to some platforms. - -FLASH Library - The eCos FLASH library provides the following functionality: @@ -50,6 +50,326 @@ + There are two APIs with the flash library. The old API is +retained for backwards compatibility reasons, but should slowly be +replaced with the new API which is much more flexible and does not +pollute the name space as much. + + + +Notes on using the FLASH library + +FLASH devices cannot be read from when an erase or write +operation is active. This means it is not possible to execute code +from flash while an erase or write operation is active. It is possible +to use the library when the executable image is resident in FLASH. The +low level drivers are written such that the linker places the +functions that actually manipulate the flash into RAM. However the +library may not be interrupt safe. An interrupt must not cause +execution of code that is resident in FLASH. This may be the image +itself, or RedBoot. In some configurations of eCos, ^C on the serial +port or debugging via Ethernet may cause an interrupt handler to call +RedBoot. If RedBoot is resident in FLASH this will cause a crash. +Similarly, if another thread invokes a virtual vector function to +access RedBoot, eg to perform a diag_printf() a +crash could result. + + + Thus with a ROM based image or a ROM based Redboot it is +recommended to disable interrupts while erasing or programming +flash. Using both a ROMRAM or RAM images and a ROMRAM or RAM RedBoot +are safe and there is no need to disable interrupts. Similarly, + + + + +Danger, Will Robinson! Danger! + +Unlike nearly every other aspect of embedded system programming, +getting it wrong with FLASH devices can render your target system +useless. Most targets have a boot loader in the FLASH. Without this +boot loader the target will obviously not boot. So before starting to +play with this library its worth investigating a few things. How do +you recover your target if you delete the boot loader? Do you have the +necessary JTAG cable? Or is specialist hardware needed? Is it even +possible to recover the target boards or must it be thrown into the +rubbish bin? How does killing the board affect your project schedule? + + + + + + +The Version 2 eCos FLASH API + + There are two APIs described here. The first is the application +API which programs should use. The second API is that between the +FLASH IO library and the device drivers. + + +FLASH user API + +All of the functions described below are declared in the header +file <cyg/io/flash.h.h> which all users of +the FLASH library should include. + + +Initializing the FLASH library + +The FLASH library needs to be initialized before other FLASH +operations can be performed. This only needs to be done once. The +following function will only do the initialization once so it's safe +to call multiple times: + +__externC int cyg_flash_init(const cyg_flash_printf *pf); +typedef int cyg_flash_printf(const char *fmt, ...); + + +The parameter pf is a pointer to a function +which is to be used for diagnostic output. Typically the function +diag_printf() will be passed. Normally this +function is not used by the higher layer of the library unless +CYGSEM_IO_FLASH_CHATTER is enabled. Passing a +NULL is not recommended, even when +CYGSEM_IO_FLASH_CHATTER is disabled. The lower layers of the library +may unconditionally call this function, especially when errors occur, +probably resulting in a more serious error/crash!. + + + +Retrieving information about FLASH devices + + +The following five functions return information about the FLASH. + + + +__externC int cyg_flash_get_info(cyg_uint32 devno, cyg_flash_info_t * info); +__externC int cyg_flash_get_info_addr(cyg_flashaddr_t flash_base, cyg_flash_info_t * info); +__externC int cyg_flash_verify_addr(const flashaddr_t address); +__extern size_t cyg_flash_block_size(const cyg_flashaddr_t flash_base); + +typedef struct cyg_flash_block_info + size_t block_size; + cyg_uint32 blocks; +} cyg_flash_block_info_t; + +typedef struct { + cyg_flashaddr_t start; // First address + cyg_flashaddr_t end; // Last address + cyg_uint32 num_block_infos // Number of entries + const cyg_flash_block_info_t *blocks_info; // Info about one block size +} cyg_flash_info_t; + + +cyg_flash_get_info() is the main function +to get information about installed flash devices. Parameter +devno is used to iterate over the available +flash devices, starting from 0. If the devno'th device exists, the +structure pointed to by info is filled in and +CYG_FLASH_ERR_OK is returned, otherwise +CYG_FLASH_ERR_INVALID. +cyg_flash_get_info() is similar, but returns the +information about the flash device at the given address. +cyg_flash_block_size() returns the size of the +block at the given address. cyg_flash_verify_addr() + tests if the target addresses is within one of the FLASH +devices, returning CYG_FLASH_ERR_OK if so. + + + + +Reading from FLASH + + +There are two methods for reading from FLASH. The first is to use the +following function. + + +__externC int cyg_flash_read(cyg_flashaddr_t flash_base, void *ram_base, size_t len, cyg_flashaddr_t *err_address); + + + +flash_base is where in the flash to read +from. ram_base indicates where the data read +from flash should be placed into RAM. len is +the number of bytes to be read from the FLASH and +err_address is used to return the location in +FLASH that any error occurred while reading. + + + +The second method is to simply memcpy() directly +from the FLASH. This is not recommended since some types of device +cannot be read in this way, eg NAND FLASH. Using the FLASH library +function to read the FLASH will always work so making it easy to port +code from one FLASH device to another. + + + + + +Erasing areas of FLASH + + +Blocks of FLASH can be erased using the following function: + + +__externC int cyg_flash_erase(cyg_flashaddr_t flash_base, size_t len, cyg_flashaddr_t *err_address); + + + +flash_base is where in the flash to erase +from. len is the minimum number of bytes to +erase in the FLASH and err_address is used to +return the location in FLASH that any error occurred while erasing. It +should be noted that FLASH devices are block oriented when erasing. It +is not possible to erase a few bytes within a block, the whole block +will be erased. flash_base may be anywhere +within the first block to be erased and flash_base+len + may be anywhere in the last block to be erased. + + + + +Programming the FLASH + + Programming of the flash is achieved using the following +function. + +__externC int cyg_flash_program(cyg_flashaddr_t flash_base, void *ram_base, size_t len, cyg_flashaddr_t *err_address); + + + +flash_base is where in the flash to program +from. ram_base indicates where the data to be +programmed into FLASH should be read from in RAM. len + is the number of bytes to be program into the FLASH and +err_address is used to return the location in +FLASH that any error occurred while programming. + + + + +Locking and unlocking blocks + + +Some flash devices have the ability to lock and unlock blocks. A +locked block cannot be erased or programmed without it first being +unlocked. For devices which support this feature and when +CYGHWR_IO_FLASH_BLOCK_LOCKING is enabled then the following +two functions are available: + + +__externC int cyg_flash_lock(const cyg_flashaddr_t flash_base, size_t len, cyg_flashaddr_t *err_address); +__externC int cyg_flash_unlock(const cyg_flashaddr_t flash_base, size_t len, cyg_flashaddr_t *err_address); + + + + + +Locking FLASH Mutex's + +When the eCos kernel package is included in the eCos +configuration, the FLASH IO library will perform mutex locking on +FLASH operations. This makes the API defined here thread safe. However +applications may wish to directly access the contents of the FLASH. In +order for this to be thread safe it is necessary for the application +to use the following two functions to inform the FLASH IO library that +the FLASH devices are being used and other API calls should be +blocked. + + +__externC int cyg_flash_mutex_lock(const cyg_flashaddr_t from, size_t len); +__externC int cyg_flash_mutex_unlock(const cyg_flashaddr_t from, size_t len); + + + + +Return values and errors + +All the functions above return one of the following return +values. + + +CYG_FLASH_ERR_OK No error - operation complete +CYG_FLASH_ERR_INVALID Invalid FLASH address +CYG_FLASH_ERR_ERASE Error trying to erase +CYG_FLASH_ERR_LOCK Error trying to lock/unlock +CYG_FLASH_ERR_PROGRAM Error trying to program +CYG_FLASH_ERR_PROTOCOL Generic error +CYG_FLASH_ERR_PROTECT Device/region is write-protected +CYG_FLASH_ERR_NOT_INIT FLASH info not yet initialized +CYG_FLASH_ERR_HWR Hardware (configuration?) problem +CYG_FLASH_ERR_ERASE_SUSPEND Device is in erase suspend mode +CYG_FLASH_ERR_PROGRAM_SUSPEND Device is in program suspend mode +CYG_FLASH_ERR_DRV_VERIFY Driver failed to verify data +CYG_FLASH_ERR_DRV_TIMEOUT Driver timed out waiting for device +CYG_FLASH_ERR_DRV_WRONG_PART Driver does not support device +CYG_FLASH_ERR_LOW_VOLTAGE Not enough juice to complete job + + +To turn an error code into a human readable string the following +function can be used: + +__externC const char *cyg_flash_errmsg(const int err); + + + + + + +FLASH device API This section describes the API +between the FLASH IO library the FLASH device drivers. + + +The FLASH device Structure + +This structure keeps all the information about a single driver. + +struct cyg_flash_dev { + const struct cyg_flash_dev_funs *funs; // Function pointers + cyg_uint32 flags; // Device characteristics + cyg_flashaddr_t start; // First address + cyg_flashaddr_t end; // Last address + cyg_uint32 num_block_infos; // Number of entries + const cyg_flash_block_info_t *block_info; // Info about one block size + + const void *priv; // Devices private data + + // The following are only written to by the FLASH IO layer. + cyg_flash_printf *pf; // Pointer to diagnostic printf + bool init; // Device has been initialised +#ifdef CYGPKG_KERNEL + cyg_mutex_t mutex; // Mutex for thread safeness +#endif +#if (CYGHWR_IO_FLASH_DEVICE > 1) + struct cyg_flash_dev *next; // Pointer to next device +#endif +}; + +struct cyg_flash_dev_funs { + int (*flash_init) (struct cyg_flash_dev *dev); + size_t (*flash_query) (struct cyg_flash_dev *dev, void * data, size_t len); + int (*flash_erase_block) (struct cyg_flash_dev *dev, cyg_flashaddr_t block_base); + int (*flash_program) (struct cyg_flash_dev *dev, cyg_flashaddr_t base, const void* data, size_t len); + int (*flash_read) (struct cyg_flash_dev *dev, const cyg_flashaddr_t base, void* data, size_t len); +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + int (*flash_block_lock) (struct cyg_flash_dev *dev, const cyg_flashaddr_t block_base); + int (*flash_block_unlock) (struct cyg_flash_dev *dev, const cyg_flashaddr_t block_base); +#endif +}; + + +The FLASH IO layer will only pass requests for operations on a single block. + + + + + +The legacy Version 1 eCos FLASH API The library has a number of limitations: @@ -72,10 +392,18 @@ The library has a number of limitations: + There are two APIs described here. The first is the application +API which programs should use. The second API is that between the +FLASH io library and the device drivers. + + +FLASH user API + All of the functions described below are declared in the header file <cyg/io/flash.h.h> which all users of the FLASH library should include. + Initializing the FLASH library @@ -84,8 +412,8 @@ operations can be performed. This only n following function will only do the initialization once so it's safe to call multiple times: -externC int flash_init( _printf *pf ); typedef int -_printf(const char *fmt, ...); +externC int flash_init( _printf *pf ); +typedef int _printf(const char *fmt, ...); The parameter pf is a pointer to a function @@ -181,7 +509,7 @@ should be noted that FLASH devices are b is not possible to erase a few bytes within a block, the whole block will be erased. flash_base may be anywhere within the first block to be erased and flash_base+len - maybe anywhere in the last block to be erased. + may be anywhere in the last block to be erased. @@ -266,45 +594,459 @@ its library functions will likely fail a the callers responsibility to use the necessary mutex's if needed. -FLASH devices cannot be read from when an erase or write -operation is active. This means it is not possible to execute code -from flash while an erase or write operation is active. It is possible -to use the library when the executable image is resident in FLASH. The -low level drivers are written such that the linker places the -functions that actually manipulate the flash into RAM. However the -library may not be interrupt safe. An interrupt must not cause -execution of code that is resident in FLASH. This may be the image -itself, or RedBoot. In some configurations of eCos, ^C on the serial -port or debugging via Ethernet may cause an interrupt handler to call -RedBoot. If RedBoot is resident in FLASH this will cause a crash. -Similarly, if another thread invokes a virtual vector function to -access RedBoot, eg to perform a diag_printf() a -crash could result. + + + + +FLASH device API This section describes the API +between the FLASH IO library the FLASH device drivers. + + +The flash_info structure + + The flash_infostructure is used by both +the FLASH IO library and the device driver. +struct flash_info { + int block_size; // Assuming fixed size "blocks" + int blocks; // Number of blocks + int buffer_size; // Size of write buffer (only defined for some devices) + unsigned long block_mask; + void *start, *end; // Address range + int init; // FLASH API initialised + _printf *pf; // printf like function for diagnostics +}; + + +block_mask is used internally in the FLASH IO library. It +contains a mask which can be used to turn an arbitrary address in +flash to the base address of the block which contains the +address. + +There exists one global instance of this structure with the name +flash_info. All calls into the device driver +makes use of this global structure to maintain state. + + + + +Initializing the device driver + +The FLASH IO library will call the following function to +initialize the device driver: + +externC int flash_hwr_init(void); + + +The device driver should probe the hardware to see if the FLASH +devices exist. If it does it should fill in start, end, +blocks and block_size.If the FLASH contains a write buffer +the size of this should be placed in buffer_size +. On successful probing the function should return +FLASH_ERR_OK. When things go wrong it can be +assumed that pf points to a printf like +function for outputting error messages. + - Thus with a ROM based image or a ROM based Redboot it is -recommended to disable interrupts while erasing or programming -flash. Using both a ROMRAM or RAM images and a ROMRAM or RAM RedBoot -are safe and there is no need to disable interrupts. Similarly, - + +Querying the FLASH + +FLASH devices can be queried to return there manufacture ID, +size etc. This function allows this information to be returned. + +int flash_query(unsigned char *data); + +The caller must know the size of data to be returned and provide +an appropriately sized buffer pointed to be parameter +data. This function is generally used by +flash_hwr_init(). + + + +Erasing a block of FLASH + +So that the FLASH IO layer can erase a block of FLASH the +following function should be provided. + +int flash_erase_block(volatile flash_t *block, unsigned int block_size); + +Programming a region of FLASH -Danger, Will Robinson! Danger! +The following function must be provided so that data can be +written into the FLASH. + +int flash_program_buf(volatile flash_t *addr, flash_t *data, int len, + unsigned long block_mask, int buffer_size); + +The device will only be asked to program data in one block of +the flash. The FLASH IO layer will break longer user requests into a +smaller writes. + + + + +Reading a region from FLASH + +Some FLASH devices are not memory mapped so it is not possible +to read there contents directly. The following function read a region +of FLASH. + +int flash_read_buf(volatile flash_t* addr, flash_t* data, int len); + + +As with writing to the flash, the FLASH IO layer will break +longer user requests for data into a number of reads which are at +maximum one block in size. + +A device which cannot be read directy should set +CYGSEM_IO_FLASH_READ_INDIRECT so that the IO layer +makes use of the flash_read_buf()function. + + + + +Locking and unlocking FLASH blocks + +Some flash devices allow blocks to be locked so that they cannot +be written to. The device driver should provide the following +functions to manipulate these locks. + +int flash_lock_block(volatile flash_t *block); +int flash_unlock_block(volatile flash_t *block, int block_size, int blocks); + + +These functions are only used if +CYGHWR_IO_FLASH_BLOCK_LOCKING + + + + +Mapping FLASH error codes to FLASH IO error codes -Unlike nearly every other aspect of embedded system programming, -getting it wrong with FLASH devices can render your target system -useless. Most targets have a boot loader in the FLASH. Without this -boot loader the target will obviously not boot. So before starting to -play with this library its worth investigating a few things. How do -you recover your target if you delete the boot loader? Do you have the -necessary JTAG cable? Or is specialist hardware needed? Is it even -possible to recover the target boards or must it be thrown into the -rubbish bin? How does killing the board affect your project schedule? +The functions flash_erase_block(), +flash_program_buf(), flash_read_buf(), flash_lock_block() and +flash_unlock_block() return an error code which is specific +to the flash device. To map this into a FLASH IO error code, the +driver should provide the following function: + +int flash_hwr_map_error(int err); + + + + +Determining if code is in FLASH + +Although a general function, the device driver is expected to +provide the implementation of the function +flash_code_overlaps(). + + + + +Implementation Notes + +The FLASH IO layer will manipulate the caches as required. The +device drivers do not need to enable/disable caches when performing +operations of the FLASH. + +Device drivers should keep all chatter to a minimum when +CYGSEM_IO_FLASH_CHATTER is not defined. All output +should use the print function in the pf in +flash_info and not +diag_printf() + +Device driver functions which manipulate the state of the flash +so that it cannot be read from for program execute need to ensure +there code is placed into RAM. The linker will do this if the +appropriate attribute is added to the function. e.g: + +int flash_program_buf(volatile flash_t *addr, flash_t *data, int len, + unsigned long block_mask, int buffer_size) + __attribute__ ((section (".2ram.flash_program_buf"))); + + + + + + +FLASH I/O devices + + +It can be useful to be able to access FLASH devices using the generic +I/O infrastructure found in CYGPKG_IO, and the generic +FLASH layer provides an optional ability to do so. This allows +the use of functions like cyg_io_lookup(), +cyg_io_read(), +cyg_io_write() etc. + + Additionally it means that, courtesy of the +“devfs” pseudo-filesystem in the file I/O layer +(CYGPKG_IO_FILEIO), functions like +open(), read(), +write() etc. can even be used directly +on the FLASH devices. - - - + +Overview and CDL Configuration + + +This package implements support for FLASH as an I/O device by exporting +it as if it is a block device. To enable this support, the CDL option +titled “Provide /dev block devices”, also known as +CYGPKG_IO_FLASH_BLOCK_DEVICE, must be enabled. +(There is also a legacy format alternative which is now deprecated). + + +There are two methods of addressing FLASH as a block device: + + +Using the FLASH Information System (FIS) - this is a +method of defining and naming FLASH partitions, usually in RedBoot. +This option is only valid if RedBoot is resident and was used to +boot the application. To reference FLASH partitions in this way, +you would use a device name of the form +/dev/flash/fis/partition-name, +for example /dev/flash/fis/jffs2 to reference a +FIS partition named JFFS2. + +The CDL option CYGFUN_IO_FLASH_BLOCK_FROM_FIS +must be enabled for this support. + + +Referencing by device number, offset and length - this +method extracts addressing information from the name itself. The form +of the device would be +/dev/flash/device-number/offset[,length] + + +device-number + +This is a fixed number allocated to identify each FLASH +region in the system. The first region is numbered 0, the second 1, +and so on. If you have only one FLASH device, it will be numbered 0. + + +offset +This is the index into the FLASH region in bytes to use. It +may be specified as decimal, or if prefixed with +0x, then hexadecimal. + +length +This field is optional and defaults to the remainder +of the FLASH region. Again it may be specified in decimal or +hexadecimal. + + + +Some examples: + +/dev/flash/0/0 +This defines a block device that uses the entirety of +FLASH region 0. + +/dev/flash/1/0x20000,65536 +This defines a block device which points inside FLASH region 1, +starting at offset 0x20000 (128Kb) and extending for 64Kb. + +/dev/flash/0/65536 +This defines a block device which points inside FLASH region 0, +starting at offset 64Kb and continuing up to the end of the device. + + + + +Obviously great care is required when constructing the device names as +using the wrong specification may subsequently overwrite important areas +of FLASH, such as RedBoot. Using the alternative via FIS names is +preferable as these are less error-prone to configure, and also allows for +the FLASH region to be relocated without requiring program recompilation. + + + + + +Using FLASH I/O devices + +The FLASH I/O block devices can be accessed, read and written using +the standard interface supplied by the generic I/O +(CYGPKG_IO) package. These include the functions: +cyg_io_lookup() to access the device and get a +handle, cyg_io_read() and +cyg_io_write() for sequential read and write +operations, cyg_io_bread() and +cyg_io_bwrite() for random access read and write +operations, and cyg_io_get_config() and +cyg_io_setconfig() for run-time configuration +inspection and control. + + +However there are two aspects that differ from some other I/O devices +accessed this way: + + +The first is that the lookup operation uses up +resources which must be subsequently freed when the last user of the +I/O handle is finished. The number of FLASH I/O devices that may be +simultaneously opened is configured with the +CYGNUM_IO_FLASH_BLOCK_DEVICES CDL option. After the +last user is finished, the device may be closed using +cyg_io_setconfig() with the +CYG_IO_SET_CONFIG_CLOSE key. Reference counting to +ensure that it is only the last user that causes a close, is left to +higher layers. + +The second is that write operations assume that the +flash is already erased. Attempting to write to Flash that has +already been written to may result in errors. Instead FLASH must +be erased before it may be written. + + +FLASH block devices can also be read and written using the +standard POSIX primitives, open(), +close(), read(), +write(), lseek(), and so on +if the POSIX file I/O package (CYGPKG_FILEIO) is +included in the configuration. As with the eCos generic I/O interface +you must call close() to ensure resources +are freed when the device is no longer used. + +Other configuration keys are provided to perform FLASH erase +operations, and to retrieve device sizes, and FLASH block sizes at +a particular address. These operations are accessed with +cyg_io_get_config() (or if using the POSIX +file I/O API, cyg_fs_getinfo()) with the +following keys: + + +CYG_IO_GET_CONFIG_FLASH_ERASE +This erases a region of FLASH. +cyg_io_get_config() must be passed a +structure defined as per the following, which is also supplied +in <cyg/io/flash.h>: + +typedef struct { + CYG_ADDRESS offset; + size_t len; + int flasherr; + cyg_flashaddr_t err_address; +} cyg_io_flash_getconfig_erase_t; + + + In this structure, offset specifies +the offset within the block device to erase, len +specifies the amount to address, flasherr is +set on return to specify an error with the FLASH erase operation itself, +and err_address is used if there was an error +to specify at which address the error happened. + + +CYG_IO_GET_CONFIG_FLASH_LOCK +This protects a region of FLASH using the locking facilities +available on the card, if provided by the underlying driver. +cyg_io_get_config() must be passed a +structure defined as per the following: + +typedef struct { + CYG_ADDRESS offset; + size_t len; + int flasherr; + cyg_flashaddr_t err_address; +} cyg_io_flash_getconfig_lock_t; + + + In this structure, offset specifies +the offset within the block device to lock, len +specifies the amount to address, flasherr is +set on return to specify an error with the FLASH lock operation itself, +and err_address is used if there was an error +to specify at which address the error happened. If locking +support is not available -EINVAL will be returned from +cyg_io_get_config(). + + +CYG_IO_GET_CONFIG_FLASH_UNLOCK +This disables protection for a region of FLASH using the +unlocking facilities available on the card, if provided by the underlying driver. +cyg_io_get_config() must be passed a +structure defined as per the following: + +typedef struct { + CYG_ADDRESS offset; + size_t len; + int flasherr; + cyg_flashaddr_t err_address; +} cyg_io_flash_getconfig_unlock_t; + + + In this structure, offset specifies +the offset within the block device to unlock, len +specifies the amount to address, flasherr is +set on return to specify an error with the FLASH unlock operation itself, +and err_address is used if there was an error +to specify at which address the error happened. If unlocking +support is not available -EINVAL will be returned from +cyg_io_get_config(). + + +CYG_IO_GET_CONFIG_FLASH_DEVSIZE +This returns the size of the FLASH block device. The +cyg_io_get_config() function must be passed a +structure defined as per the following, which is also supplied +in <cyg/io/flash.h>: + +typedef struct { + size_t dev_size; +} cyg_io_flash_getconfig_devsize_t; + + + In this structure, dev_size is used to +return the size of the FLASH device. + + +CYG_IO_GET_CONFIG_FLASH_DEVADDR +This returns the address in the virtual memory map that the +generic flash layer has been informed that this FLASH device is mapped to. Note +that some flash devices such as dataflash are not truly memory mapped, +and so this function only returns useful information when used with a true +memory mapped FLASH device. The +cyg_io_get_config() function must be passed a +structure defined as per the following, which is also supplied +in <cyg/io/flash.h>: + +typedef struct { + cyg_flashaddr_t dev_addr; +} cyg_io_flash_getconfig_devaddr_t; + + + In this structure, dev_addr is used to +return the address corresponding to the base of the FLASH device in the +virtual memory map. + + +CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE +This returns the size of a FLASH block at a +supplied offset in the FLASH block device. The +cyg_io_get_config() function must be passed a +structure defined as per the following, which is also supplied +in <cyg/io/flash.h>: + +typedef struct { + CYG_ADDRESS offset; + size_t block_size; +} cyg_io_flash_getconfig_blocksize_t; + + + In this structure, offset specifies the +address within the block device of which the FLASH block size is +required - a single FLASH device may contain blocks of differing +sizes. The block_size field is used to +return the block size at the specified offset. + + + + diff --git a/packages/io/flash/current/include/flash.h b/packages/io/flash/current/include/flash.h --- a/packages/io/flash/current/include/flash.h +++ b/packages/io/flash/current/include/flash.h @@ -8,8 +8,10 @@ //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. +// copyright (C) 2004 Andrew Lunn +// Copyright (C) 2004, 2005 eCosCentric Ltd. +// Copyright (C) 2003 Gary Thomas // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. -// Copyright (C) 2003 Gary Thomas // // 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 +44,7 @@ //#####DESCRIPTIONBEGIN#### // // Author(s): gthomas -// Contributors: gthomas +// Contributors: gthomas, Andrew Lunn, bartv // Date: 2000-07-14 // Purpose: // Description: @@ -54,15 +56,91 @@ #ifndef _IO_FLASH_H_ #define _IO_FLASH_H_ +#include #include -#include +#include +#include +#ifdef CYGPKG_KERNEL +#include +#endif + +// Currently a 32-bit quantity. In future this may be 64-bits on some +// platforms, e.g. to support very large nand flashes which can only +// be accessed indirectly. +typedef CYG_ADDRESS cyg_flashaddr_t; + +typedef struct cyg_flash_block_info +{ + size_t block_size; + cyg_uint32 blocks; +} cyg_flash_block_info_t; + +// Information about what one device driver drives +typedef struct { + cyg_flashaddr_t start; // First address + cyg_flashaddr_t end; // Last address + cyg_uint32 num_block_infos; // Number of entries + const cyg_flash_block_info_t* block_info; // Info about block sizes +} cyg_flash_info_t; +typedef int cyg_flash_printf(const char *fmt, ...); +__externC int cyg_flash_init( cyg_flash_printf *pf ); +__externC int cyg_flash_get_info(cyg_uint32 devno, + cyg_flash_info_t * info); +__externC int cyg_flash_get_info_addr(const cyg_flashaddr_t flash_base, + cyg_flash_info_t * info); +__externC int cyg_flash_verify_addr(const cyg_flashaddr_t address); +__externC size_t cyg_flash_block_size(const cyg_flashaddr_t flash_base); +__externC int cyg_flash_read(const cyg_flashaddr_t flash_base, + void *ram_base, + size_t len, + cyg_flashaddr_t *err_address); +__externC int cyg_flash_erase(cyg_flashaddr_t flash_base, + size_t len, + cyg_flashaddr_t *err_address); +__externC int cyg_flash_program(cyg_flashaddr_t flash_base, + const void *ram_base, + size_t len, + cyg_flashaddr_t *err_address); +__externC int cyg_flash_lock(const cyg_flashaddr_t flash_base, + size_t len, + cyg_flashaddr_t *err_address); +__externC int cyg_flash_unlock(const cyg_flashaddr_t flash_base, + size_t len, + cyg_flashaddr_t *err_address); +__externC const char *cyg_flash_errmsg(const int err); +#ifdef CYGPKG_KERNEL +__externC int cyg_flash_mutex_lock(const cyg_flashaddr_t from, + size_t len); +__externC int cyg_flash_mutex_unlock(const cyg_flashaddr_t from, + size_t len); +#endif + +#define CYG_FLASH_ERR_OK 0x00 // No error - operation complete +#define CYG_FLASH_ERR_INVALID 0x01 // Invalid FLASH address +#define CYG_FLASH_ERR_ERASE 0x02 // Error trying to erase +#define CYG_FLASH_ERR_LOCK 0x03 // Error trying to lock/unlock +#define CYG_FLASH_ERR_PROGRAM 0x04 // Error trying to program +#define CYG_FLASH_ERR_PROTOCOL 0x05 // Generic error +#define CYG_FLASH_ERR_PROTECT 0x06 // Device/region is write-protected +#define CYG_FLASH_ERR_NOT_INIT 0x07 // FLASH info not yet initialized +#define CYG_FLASH_ERR_HWR 0x08 // Hardware (configuration?) problem +#define CYG_FLASH_ERR_ERASE_SUSPEND 0x09 // Device is in erase suspend mode +#define CYG_FLASH_ERR_PROGRAM_SUSPEND 0x0a // Device is in program suspend mode +#define CYG_FLASH_ERR_DRV_VERIFY 0x0b // Driver failed to verify data +#define CYG_FLASH_ERR_DRV_TIMEOUT 0x0c // Driver timed out +#define CYG_FLASH_ERR_DRV_WRONG_PART 0x0d // Driver does not support device +#define CYG_FLASH_ERR_LOW_VOLTAGE 0x0e // Not enough juice to complete job + +#ifdef CYGSEM_IO_FLASH_LEGACY_API typedef int _printf(const char *fmt, ...); externC int flash_init(_printf *pf); externC int flash_erase(void *base, int len, void **err_address); -externC int flash_program(void *flash_base, void *ram_base, int len, void **err_address); -externC int flash_read(void *flash_base, void *ram_base, int len, void **err_address); +externC int flash_program(void *flash_base, void *ram_base, int len, + void **err_address); +externC int flash_read(void *flash_base, void *ram_base, int len, + void **err_address); externC void flash_dev_query(void *data); #ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING externC int flash_lock(void *base, int len, void **err_address); @@ -73,169 +151,50 @@ externC int flash_get_limits(void *base, externC int flash_get_block_info(int *block_size, int *blocks); externC bool flash_code_overlaps(void *start, void *end); externC char *flash_errmsg(int err); +#endif // CYGSEM_IO_FLASH_LEGACY_API -#define FLASH_ERR_OK 0x00 // No error - operation complete -#define FLASH_ERR_INVALID 0x01 // Invalid FLASH address -#define FLASH_ERR_ERASE 0x02 // Error trying to erase -#define FLASH_ERR_LOCK 0x03 // Error trying to lock/unlock -#define FLASH_ERR_PROGRAM 0x04 // Error trying to program -#define FLASH_ERR_PROTOCOL 0x05 // Generic error -#define FLASH_ERR_PROTECT 0x06 // Device/region is write-protected -#define FLASH_ERR_NOT_INIT 0x07 // FLASH info not yet initialized -#define FLASH_ERR_HWR 0x08 // Hardware (configuration?) problem -#define FLASH_ERR_ERASE_SUSPEND 0x09 // Device is in erase suspend mode -#define FLASH_ERR_PROGRAM_SUSPEND 0x0a // Device is in in program suspend mode -#define FLASH_ERR_DRV_VERIFY 0x0b // Driver failed to verify data -#define FLASH_ERR_DRV_TIMEOUT 0x0c // Driver timed out waiting for device -#define FLASH_ERR_DRV_WRONG_PART 0x0d // Driver does not support device -#define FLASH_ERR_LOW_VOLTAGE 0x0e // Not enough juice to complete job +#if defined(CYGSEM_IO_FLASH_LEGACY_API) || defined(CYGHWR_IO_FLASH_DEVICE_LEGACY) +#define FLASH_ERR_OK CYG_FLASH_ERR_OK +#define FLASH_ERR_INVALID CYG_FLASH_ERR_INVALID +#define FLASH_ERR_ERASE CYG_FLASH_ERR_ERASE +#define FLASH_ERR_LOCK CYG_FLASH_ERR_LOCK +#define FLASH_ERR_PROGRAM CYG_FLASH_ERR_PROGRAM +#define FLASH_ERR_PROTOCOL CYG_FLASH_ERR_PROTOCOL +#define FLASH_ERR_PROTECT CYG_FLASH_ERR_PROTECT +#define FLASH_ERR_NOT_INIT CYG_FLASH_ERR_NOT_INIT +#define FLASH_ERR_HWR CYG_FLASH_ERR_HWR +#define FLASH_ERR_ERASE_SUSPEND CYG_FLASH_ERR_ERASE_SUSPEND +#define FLASH_ERR_PROGRAM_SUSPEND CYG_FLASH_ERR_PROGRAM_SUSPEND +#define FLASH_ERR_DRV_VERIFY CYG_FLASH_ERR_DRV_VERIFY +#define FLASH_ERR_DRV_TIMEOUT CYG_FLASH_ERR_DRV_TIMEOUT +#define FLASH_ERR_DRV_WRONG_PART CYG_FLASH_ERR_DRV_WRONG_PART +#define FLASH_ERR_LOW_VOLTAGE CYG_FLASH_ERR_LOW_VOLTAGE +#endif -#ifdef CYGPKG_IO_FLASH_BLOCK_DEVICE +#if defined(CYGPKG_IO_FLASH_BLOCK_DEVICE) || \ + defined(CYGPKG_IO_FLASH_BLOCK_DEVICE_LEGACY) typedef struct { CYG_ADDRESS offset; - int len; + size_t len; int flasherr; - void **err_address; + cyg_flashaddr_t err_address; } cyg_io_flash_getconfig_erase_t; +typedef cyg_io_flash_getconfig_erase_t cyg_io_flash_getconfig_lock_t; +typedef cyg_io_flash_getconfig_erase_t cyg_io_flash_getconfig_unlock_t; + typedef struct { - int dev_size; + size_t dev_size; } cyg_io_flash_getconfig_devsize_t; typedef struct { + cyg_flashaddr_t dev_addr; +} cyg_io_flash_getconfig_devaddr_t; + +typedef struct { CYG_ADDRESS offset; - int block_size; + size_t block_size; } cyg_io_flash_getconfig_blocksize_t; #endif -#ifdef _FLASH_PRIVATE_ - -struct flash_info { - int block_size; // Assuming fixed size "blocks" - int blocks; // Number of blocks - int buffer_size; // Size of write buffer (only defined for some devices) - unsigned long block_mask; - void *start, *end; // Address range - int init; - _printf *pf; -}; - -externC struct flash_info flash_info; -externC int flash_hwr_init(void); -externC int flash_hwr_map_error(int err); - -// -// Some FLASH devices may require additional support, e.g. to turn on -// appropriate voltage drivers, before any operation. -// -#ifdef CYGIMP_FLASH_ENABLE -#define FLASH_Enable CYGIMP_FLASH_ENABLE -extern void CYGIMP_FLASH_ENABLE(void *, void *); -#else -#define FLASH_Enable(_start_, _end_) -#endif -#ifdef CYGIMP_FLASH_DISABLE -#define FLASH_Disable CYGIMP_FLASH_DISABLE -extern void CYGIMP_FLASH_DISABLE(void *, void *); -#else -#define FLASH_Disable(_start_, _end_) -#endif - -// -// Some platforms have a DIP switch or jumper that tells the software that -// the flash is write protected. -// -#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT -externC cyg_bool plf_flash_query_soft_wp(void *addr, int len); -#endif - -//--------------------------------------------------------------------------- -// Execution of flash code must be done inside a -// HAL_FLASH_CACHES_OFF/HAL_FLASH_CACHES_ON region - disabling the -// cache on unified cache systems is necessary to prevent burst access -// to the flash area being programmed. With Harvard style caches, only -// the data cache needs to be disabled, but the instruction cache is -// disabled for consistency. - -// Targets may provide alternative implementations for these macros in -// the hal_cache.h (or var/plf) files. - -// The first part below is a generic, optimal implementation. The -// second part is the old implementation that has been tested to work -// on some targets - but it is not be suitable for targets that would -// do burst access to the flash (it does not disable the data cache). - -// Both implementations must be called with interrupts disabled. - -// NOTE: Do _not_ change any of the below macros without checking that -// the changed code still works on _all_ platforms that rely on these -// macros. There is no such thing as logical and correct when dealing -// with different cache and IO models, so _do not_ mess with this code -// unless you test it properly afterwards. - -#ifndef HAL_FLASH_CACHES_OFF - -// Some drivers have only been tested with the old macros below. -#ifndef HAL_FLASH_CACHES_OLD_MACROS - -#ifdef HAL_CACHE_UNIFIED - -// Note: the ucache code has not been tested yet on any target. -#define HAL_FLASH_CACHES_OFF(_d_, _i_) \ - CYG_MACRO_START \ - _i_ = 0; /* avoids warning */ \ - HAL_UCACHE_IS_ENABLED(_d_); \ - HAL_UCACHE_SYNC(); \ - HAL_UCACHE_INVALIDATE_ALL(); \ - HAL_UCACHE_DISABLE(); \ - CYG_MACRO_END - -#define HAL_FLASH_CACHES_ON(_d_, _i_) \ - CYG_MACRO_START \ - if (_d_) HAL_UCACHE_ENABLE(); \ - CYG_MACRO_END - -#else // HAL_CACHE_UNIFIED - -#define HAL_FLASH_CACHES_OFF(_d_, _i_) \ - CYG_MACRO_START \ - _i_ = 0; /* avoids warning */ \ - HAL_DCACHE_IS_ENABLED(_d_); \ - HAL_DCACHE_SYNC(); \ - HAL_DCACHE_INVALIDATE_ALL(); \ - HAL_DCACHE_DISABLE(); \ - HAL_ICACHE_INVALIDATE_ALL(); \ - CYG_MACRO_END - -#define HAL_FLASH_CACHES_ON(_d_, _i_) \ - CYG_MACRO_START \ - if (_d_) HAL_DCACHE_ENABLE(); \ - CYG_MACRO_END - -#endif // HAL_CACHE_UNIFIED - -#else // HAL_FLASH_CACHES_OLD_MACROS - -// Note: This implementation is broken as it will always enable the i-cache -// even if it was not enabled before. It also doesn't work if the -// target uses burst access to flash since the d-cache is left enabled. -// However, this does not mean you can change this code! Leave it as -// is - if you want a different implementation, provide it in the -// arch/var/platform cache header file. - -#define HAL_FLASH_CACHES_OFF(_d_, _i_) \ - _d_ = 0; /* avoids warning */ \ - _i_ = 0; /* avoids warning */ \ - HAL_DCACHE_SYNC(); \ - HAL_ICACHE_DISABLE(); - -#define HAL_FLASH_CACHES_ON(_d_, _i_) \ - HAL_ICACHE_ENABLE(); - -#endif // HAL_FLASH_CACHES_OLD_MACROS - -#endif // HAL_FLASH_CACHES_OFF - -#endif // _FLASH_PRIVATE_ - #endif // _IO_FLASH_H_ diff --git a/packages/io/flash/current/include/flash_dev.h b/packages/io/flash/current/include/flash_dev.h --- a/packages/io/flash/current/include/flash_dev.h +++ b/packages/io/flash/current/include/flash_dev.h @@ -10,6 +10,9 @@ //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 eCosCentric Ltd. +// Copyright (C) 2004 Andrew Lunn +// Copyright (C) 2003 Gary Thomas // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // // eCos is free software; you can redistribute it and/or modify it under @@ -42,6 +45,144 @@ //========================================================================== //#####DESCRIPTIONBEGIN#### // +// Author(s): gthomas +// Contributors: gthomas, Andrew Lunn, bartv +// Date: 2000-07-14 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include +#include + +// Forward reference of the device structure +struct cyg_flash_dev; + +// Structure of pointers to functions in the device driver +struct cyg_flash_dev_funs { + int (*flash_init) (struct cyg_flash_dev *dev); + size_t (*flash_query) (struct cyg_flash_dev *dev, + void * data, size_t len); + int (*flash_erase_block) (struct cyg_flash_dev *dev, + cyg_flashaddr_t block_base); + int (*flash_program) (struct cyg_flash_dev *dev, + cyg_flashaddr_t base, + const void* data, size_t len); + int (*flash_read) (struct cyg_flash_dev *dev, + const cyg_flashaddr_t base, + void* data, size_t len); +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + int (*flash_block_lock) (struct cyg_flash_dev *dev, + const cyg_flashaddr_t block_base); + int (*flash_block_unlock) (struct cyg_flash_dev *dev, + const cyg_flashaddr_t block_base); +#endif +}; + +// Dummy functions for some of the above operations, if a device does +// not support e.g. locking. +externC int cyg_flash_devfn_init_nop(struct cyg_flash_dev*); +externC size_t cyg_flash_devfn_query_nop(struct cyg_flash_dev*, void*, const size_t); +externC int cyg_flash_devfn_lock_nop(struct cyg_flash_dev*, const cyg_flashaddr_t); +externC int cyg_flash_devfn_unlock_nop(struct cyg_flash_dev*, const cyg_flashaddr_t); + +// Facilitate function calls between flash-resident code and .2ram +// functions. +externC void* cyg_flash_anonymizer(void*); + +// Structure each device places in the HAL table +struct cyg_flash_dev { + const struct cyg_flash_dev_funs *funs; // Function pointers + cyg_uint32 flags; // Device characteristics + cyg_flashaddr_t start; // First address + cyg_flashaddr_t end; // Last address + cyg_uint32 num_block_infos; // Number of entries + const cyg_flash_block_info_t *block_info; // Info about one block size + + const void *priv; // Devices private data + + // The following are only written to by the FLASH IO layer. + cyg_flash_printf *pf; // Pointer to diagnostic printf + bool init; // Device has been initialised +#ifdef CYGPKG_KERNEL + cyg_mutex_t mutex; // Mutex for thread safeness +#endif +#if (CYGHWR_IO_FLASH_DEVICE > 1) + struct cyg_flash_dev *next; // Pointer to next device +#endif +} CYG_HAL_TABLE_TYPE; + +// Macros for instantiating the above structures. +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING +# define CYG_FLASH_FUNS(_funs_, _init_, _query_ , _erase_, _prog_ , _read_, _lock_, _unlock_) \ +struct cyg_flash_dev_funs _funs_ = \ +{ \ + .flash_init = _init_, \ + .flash_query = _query_, \ + .flash_erase_block = _erase_, \ + .flash_program = _prog_, \ + .flash_read = _read_, \ + .flash_block_lock = _lock_, \ + .flash_block_unlock = _unlock_ \ +} +#else +# define CYG_FLASH_FUNS(_funs_, _init_, _query_ , _erase_, _prog_ , _read_, _lock_, _unlock_) \ +struct cyg_flash_dev_funs _funs_ = \ +{ \ + .flash_init = _init_, \ + .flash_query = _query_, \ + .flash_erase_block = _erase_, \ + .flash_program = _prog_, \ + .flash_read = _read_ \ +} +#endif + +// We assume HAL tables are placed into RAM. +#define CYG_FLASH_DRIVER(_name_, _funs_, _flags_, _start_, _end_, _num_block_infos_, _block_info_, _priv_) \ +struct cyg_flash_dev _name_ CYG_HAL_TABLE_ENTRY(cyg_flashdev) = \ +{ \ + .funs = _funs_, \ + .flags = _flags_, \ + .start = _start_, \ + .end = _end_, \ + .num_block_infos = _num_block_infos_, \ + .block_info = _block_info_, \ + .priv = _priv_ \ +} + +// Additional support for legacy device drivers. +#ifdef CYGHWR_IO_FLASH_DEVICE_LEGACY +struct flash_info { + int block_size; // Assuming fixed size "blocks" + int blocks; // Number of blocks + int buffer_size; // Size of write buffer (only defined for some devices) + unsigned long block_mask; + void *start, *end; // Address range + int init; + cyg_flash_printf *pf; +}; + +externC struct flash_info flash_info; +externC int flash_hwr_init(void); +externC int flash_hwr_map_error(int err); +externC void flash_dev_query(void *data); +#endif // CYGHWR_IO_FLASH_DEVICE_LEGACY + +// ---------------------------------------------------------------------------- +// This section provides utility macros used by some flash drivers, especially +// the legacy ones. Such flash drivers need to #define _FLASH_PRIVATE before +// including this file. + +#ifdef _FLASH_PRIVATE_ + +//========================================================================== // Author(s): hmt // Contributors: hmt, jskov, Jose Pascual // Date: 2001-02-22 @@ -53,16 +194,8 @@ // The FLASH_P2V macro can be used to fix up non-linear // mappings of flash blocks (defaults to a linear // implementation). -// -//####DESCRIPTIONEND#### -// //========================================================================== -#ifdef _FLASH_PRIVATE_ -#include - -// ------------------------------------------------------------------------ -// // No mapping on this target - but these casts would be needed if some // manipulation did occur. An example of this might be: // // First 4K page of flash at physical address zero is @@ -165,3 +298,4 @@ typedef cyg_uint64 flash_data_t; #endif // CYGONCE_IO_FLASH_FLASH_DEV_H //---------------------------------------------------------------------------- // end of flash_dev.h + diff --git a/packages/io/flash/current/src/flash.c b/packages/io/flash/current/src/flash.c --- a/packages/io/flash/current/src/flash.c +++ b/packages/io/flash/current/src/flash.c @@ -8,8 +8,10 @@ //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 Andrew Lunn +// Copyright (C) 2004, 2005, 2006 eCosCentric Ltd. +// Copyright (C) 2003 Gary Thomas // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. -// Copyright (C) 2003 Gary Thomas // // 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 @@ -33,16 +35,13 @@ // // This exception does not invalidate any other reasons why a work based on // this file might be covered by the GNU General Public License. -// -// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. -// at http://sources.redhat.com/ecos/ecos-license/ // ------------------------------------------- //####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): gthomas -// Contributors: gthomas +// Contributors: gthomas, Andrew Lunn, Bart Veer // Date: 2000-07-26 // Purpose: // Description: @@ -53,508 +52,880 @@ #include #include - +#ifdef CYGPKG_KERNEL +#include +#endif #include #include #include +#include +#include #include -#define _FLASH_PRIVATE_ #include +#include +#include "flash_legacy.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*); +// Optional verbosity. Using a macro here avoids lots of ifdefs in the +// rest of the code. +#ifdef CYGSEM_IO_FLASH_CHATTER +# define CHATTER(_dev_, _fmt_, ...) (*(_dev_)->pf)((_fmt_), ## __VA_ARGS__) +#else +# define CHATTER(_dev_, _fmt_, ...) CYG_EMPTY_STATEMENT +#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; +// Per-thread locking. Again using macros avoids lots of ifdefs +#ifdef CYGPKG_KERNEL +# define LOCK_INIT(_dev_) cyg_mutex_init(&((_dev_)->mutex)) +# define LOCK(_dev_) cyg_mutex_lock(&((_dev_)->mutex)) +# define UNLOCK(_dev_) cyg_mutex_unlock(&((_dev_)->mutex)) +#else +# define LOCK_INIT(_dev_) CYG_EMPTY_STATEMENT +# define LOCK(_dev_) CYG_EMPTY_STATEMENT +# define UNLOCK(_dev_) CYG_EMPTY_STATEMENT +#endif + +// Software write-protect. Very rarely used. +#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT +# define CHECK_SOFT_WRITE_PROTECT(_addr_, _len_) \ + CYG_MACRO_START \ + if (plf_flash_query_soft_wp((_addr_), (_len_))) \ + return CYG_FLASH_ERR_PROTECT; \ + CYG_MACRO_END +#else +#define CHECK_SOFT_WRITE_PROTECT(_addr_, _len_) CYG_EMPTY_STATEMENT +#endif -int -flash_init(_printf *pf) -{ - int err; +// Has the FLASH IO library been initialised? +static bool init; + +// 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); + +#if (1 == CYGHWR_IO_FLASH_DEVICE) - flash_info.pf = pf; // Do this before calling into the driver - if (flash_info.init) return FLASH_ERR_OK; - - 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; +// Optimize the code for a single flash device, which is the common case. +// The flash subsystem must have been initialized, the single device must +// contain the specified address, and the device itself must have +// initialized successfully. +static struct cyg_flash_dev* +find_dev(cyg_flashaddr_t addr, int* stat) +{ + if (!init) { + *stat = CYG_FLASH_ERR_NOT_INIT; + return NULL; + } + if (! ((addr >= cyg_flashdevtab[0].start) && (addr <= cyg_flashdevtab[0].end))) { + *stat = CYG_FLASH_ERR_INVALID; + return NULL; + } + if (! cyg_flashdevtab[0].init) { + *stat = CYG_FLASH_ERR_NOT_INIT; + return NULL; + } + return &cyg_flashdevtab[0]; } -// 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) +#else + +// There are multiple devices. For convenience these are kept in a +// linked list, sorted by address. This is the head of the list +static struct cyg_flash_dev *flash_head = NULL; + +static bool flash_sort_and_check(void) { - return p; + bool moved; + struct cyg_flash_dev *dev, **previous_next; + + // Place all devices that initialised on the list, unsorted for now. + for (dev = &cyg_flashdevtab[0]; dev != &cyg_flashdevtab_end; dev++) { + if (dev->init) { + dev->next = flash_head; + flash_head = dev; + } + } + + // If there are no valid devices, abort. This might happen if + // all drivers failed to initialize. + if (flash_head == NULL) { + return false; + } + + // 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. This loop may run safely with just one + // entry on the list. + 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); + + // Now walk the linked list and see if there are any overlaps in the + // addresses the devices claim to use using. + for (dev=flash_head; dev->next; dev=dev->next){ + if (dev->end >= dev->next->start) + return false; + } + return true; } -// 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) +// Find the device at the specified address, if any. +static struct cyg_flash_dev* +find_dev(cyg_flashaddr_t addr, int* stat) { - 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); -} - -int -flash_verify_addr(void *target) -{ - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; + struct cyg_flash_dev* dev; + if (!init) { + *stat = CYG_FLASH_ERR_NOT_INIT; + return NULL; + } + for (dev = flash_head; dev; dev = dev->next) { + if ((dev->start <= addr) && (addr <= dev->end)) { + return dev; } - 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; - } + } + *stat = CYG_FLASH_ERR_INVALID; + return NULL; } -int -flash_get_limits(void *target, void **start, void **end) +#endif + +// 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) { - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; + int err; + struct cyg_flash_dev * dev; + + if (init) return CYG_FLASH_ERR_OK; + + CYG_ASSERT(&(cyg_flashdevtab[CYGHWR_IO_FLASH_DEVICE]) == &cyg_flashdevtab_end, "incorrect number of flash devices"); + + for (dev = &cyg_flashdevtab[0]; dev != &cyg_flashdevtab_end; dev++) { + dev->pf = pf; + LOCK_INIT(dev); + + err = dev->funs->flash_init(dev); + if (err != CYG_FLASH_ERR_OK) { + continue; } - *start = flash_info.start; - *end = flash_info.end; - return FLASH_ERR_OK; + CYG_ASSERT(dev->funs, "No flash functions"); + CYG_ASSERT(dev->num_block_infos, "No number of block infos"); + 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"); +#ifdef CYGDBG_USE_ASSERTS + { + int i; + cyg_flashaddr_t addr = dev->start; + for (i = 0; i < dev->num_block_infos; i++) { + addr += dev->block_info[i].block_size * dev->block_info[i].blocks; + } + CYG_ASSERT(dev->end == addr-1, "Invalid end address"); + } +#endif + dev->init = true; + } + +#if (1 == CYGHWR_IO_FLASH_DEVICE) + // Make sure there is one device, otherwise we could end up + // accessing a non-existent cyg_flash_dev structure. + if (&(cyg_flashdevtab[0]) == &cyg_flashdevtab_end) { + return CYG_FLASH_ERR_INVALID; + } +#else + // Place the devices on a sorted linked list and check that there + // are no overlaps in the address space. + if (! flash_sort_and_check() ) { + return CYG_FLASH_ERR_INVALID; + } +#endif + + // Only mark the flash subsystem as initialized if the world is + // consistent. + init = true; + return CYG_FLASH_ERR_OK; } -int -flash_get_block_info(int *block_size, int *blocks) +// Is the address within one of the flash drivers? +__externC int +cyg_flash_verify_addr(const cyg_flashaddr_t address) { - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } - *block_size = flash_info.block_size; - *blocks = flash_info.blocks; - return FLASH_ERR_OK; + int stat = CYG_FLASH_ERR_OK; + (void) find_dev(address, &stat); + return stat; } -int -flash_erase(void *addr, int len, void **err_addr) +// Return information about the Nth driver +__externC int +cyg_flash_get_info(cyg_uint32 Nth, 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; + struct cyg_flash_dev * dev; + + if (!init) return CYG_FLASH_ERR_NOT_INIT; - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } +#if (1 == CYGHWR_IO_FLASH_DEVICE) + if ((0 == Nth) && cyg_flashdevtab[0].init) { + dev = &(cyg_flashdevtab[0]); + } else { + return CYG_FLASH_ERR_INVALID; + } +#else + // Only initialized devices are on the list. + for (dev = flash_head; dev && Nth; dev=dev->next, Nth--) + ; + if (!dev) { + return CYG_FLASH_ERR_INVALID; + } +#endif + 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 +// Return information about the flash at the given address +__externC int +cyg_flash_get_info_addr(const cyg_flashaddr_t flash_base, cyg_flash_info_t * info) +{ + struct cyg_flash_dev *dev; + int stat = CYG_FLASH_ERR_OK; - _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); + dev = find_dev(flash_base, &stat); + if (dev) { + info->start = dev->start; + info->end = dev->end; + info->num_block_infos = dev->num_block_infos; + info->block_info = dev->block_info; + } + return stat; +} - /* 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, size_t len) +{ + struct cyg_flash_dev * dev; + int stat = CYG_FLASH_ERR_OK; + + dev = find_dev(from, &stat); + if (dev) { + LOCK(dev); + if (len > (dev->end + 1 - from)) { + stat = cyg_flash_mutex_lock(dev->end + 1, len - (dev->end + 1 - from)); + if (CYG_FLASH_ERR_OK != stat) { + // Something went wrong, unlock what we just locked + UNLOCK(dev); + } } - -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Erase from %p-%p: ", (void*)block, (void*)end_addr); -#endif + } + return stat; +} - 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; +// Unlock the mutex's for a range of addresses +__externC int +cyg_flash_mutex_unlock(const cyg_flashaddr_t from, size_t len) +{ + struct cyg_flash_dev * dev; + int stat = CYG_FLASH_ERR_OK; - 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; + dev = find_dev(from, &stat); + if (dev) { + UNLOCK(dev); + if (len > (dev->end + 1 - from)) { + stat = cyg_flash_mutex_lock(dev->end + 1, len - (dev->end + 1 - from)); + if (CYG_FLASH_ERR_OK != stat) { + // Something went wrong, relock what we just unlocked. This may not + // be worth it since things must be pretty messed up, and could + // conceivably end in deadlock if there is a concurrent call to + // cyg_flash_mutex_lock(); + LOCK(dev); + } + } + } + return stat; +} #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; +} - // 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; - } -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); -#endif - } - FLASH_Disable(block, end_addr); - HAL_FLASH_CACHES_ON(d_cache, i_cache); -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); -#endif - return (stat); +// 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; + int stat; + + dev = find_dev(flash_base, &stat); + if (!dev) return stat; + return flash_block_size(dev, flash_base); } -int -flash_program(void *_addr, void *_data, int len, void **err_addr) +// Return the first address of a block. The flash might not be aligned +// in terms of its block size. So we have to be careful and use +// offsets. +static inline cyg_flashaddr_t +flash_block_begin(cyg_flashaddr_t addr, struct cyg_flash_dev *dev) +{ + size_t block_size; + cyg_flashaddr_t offset; + + block_size = flash_block_size(dev, addr); + + offset = addr - dev->start; + offset = (offset / block_size) * block_size; + return offset + dev->start; +} + + +__externC int +cyg_flash_erase(cyg_flashaddr_t flash_base, + 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; + cyg_flashaddr_t block, end_addr; + struct cyg_flash_dev * dev; + size_t erase_count; + int stat = CYG_FLASH_ERR_OK; + HAL_FLASH_CACHES_STATE(d_cache, i_cache); + + dev = find_dev(flash_base, &stat); + if (!dev) return stat; + + CHECK_SOFT_WRITE_PROTECT(flash_base, len); + + LOCK(dev); - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; + // Check whether or not we are going past the end of this device, on + // to the next one. If so the next device will be handled by a + // recursive call later on. + if (len > (dev->end + 1 - flash_base)) { + end_addr = dev->end; + } else { + end_addr = flash_base + len - 1; + } + // erase can only happen on a block boundary, so adjust for this + block = flash_block_begin(flash_base, dev); + erase_count = (end_addr + 1) - block; + + CHATTER(dev, "... Erase from %p-%p: ", (void*)block, (void*)end_addr); + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(flash_base, end_addr); + while (erase_count > 0) { + int i; + unsigned char *dp; + bool erased = false; + size_t block_size = flash_block_size(dev, block); + + // Pad to the block boundary, if necessary + if (erase_count < block_size) { + erase_count = block_size; } -#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT - if (plf_flash_query_soft_wp(addr,len)) - return FLASH_ERR_PROTECT; -#endif - - _flash_program_buf = (code_fun*) __anonymizer(&flash_program_buf); - -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Program 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; - + // If there is a read function it probably means the flash + // cannot be read directly. + if (!dev->funs->flash_read) { + erased = true; + dp = (unsigned char *)block; + for (i = 0; i < block_size; i++) { + if (*dp++ != (unsigned char)0xFF) { + erased = false; + break; } + } + } + if (!erased) { + stat = dev->funs->flash_erase_block(dev,block); + } + if (CYG_FLASH_ERR_OK != stat) { + if (err_address) + *err_address = block; + break; + } + block += block_size; + erase_count -= block_size; + CHATTER(dev, "."); + } + FLASH_Disable(flash_base, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); + CHATTER(dev, "\n"); + UNLOCK(dev); + if (stat != CYG_FLASH_ERR_OK) { + return stat; + } - stat = (*_flash_program_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 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)("."); -#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); -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); -#endif - return (stat); + // If there are multiple flash devices in series the erase operation + // may touch successive devices. This can be handled by recursion. + // The stack overheads should be minimal because the number of + // devices will be small. + if (len > (dev->end + 1 - flash_base)) { + return cyg_flash_erase(dev->end+1, + len - (dev->end + 1 - flash_base), + err_address); + } + return CYG_FLASH_ERR_OK; } -int -flash_read(void *_addr, void *_data, int len, void **err_addr) +__externC int +cyg_flash_program(cyg_flashaddr_t flash_base, + const void *ram_base, + 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; + struct cyg_flash_dev * dev; + cyg_flashaddr_t addr, end_addr, block; + const unsigned char * ram = ram_base; + size_t write_count, offset; + int stat = CYG_FLASH_ERR_OK; + HAL_FLASH_CACHES_STATE(d_cache, i_cache); + + dev = find_dev(flash_base, &stat); + if (!dev) return stat; - if (!flash_info.init) { - return FLASH_ERR_NOT_INIT; - } + CHECK_SOFT_WRITE_PROTECT(flash_base, len); + + LOCK(dev); + addr = flash_base; + if (len > (dev->end + 1 - flash_base)) { + end_addr = dev->end; + } else { + end_addr = flash_base + len - 1; + } + write_count = (end_addr + 1) - flash_base; - _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); + // The first write may be in the middle of a block. Do the necessary + // adjustment here rather than inside the loop. + block = flash_block_begin(flash_base, dev); + if (addr == block) { + offset = 0; + } else { + offset = addr - block; + } + + CHATTER(dev, "... Program from %p-%p to %p: ", ram_base, ((CYG_ADDRESS)ram_base)+write_count, addr); + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(flash_base, end_addr); + while (write_count > 0) { + size_t block_size = flash_block_size(dev, addr); + size_t this_write; + if (write_count > (block_size - offset)) { + this_write = block_size - offset; + } else { + this_write = write_count; + } + // Only the first block may need the offset. + offset = 0; + + stat = dev->funs->flash_program(dev, addr, ram, this_write); +#ifdef CYGSEM_IO_FLASH_VERIFY_PROGRAM + if (CYG_FLASH_ERR_OK == stat) // Claims to be OK + if (!dev->funs->flash_read && memcmp((void *)addr, ram, this_write) != 0) { + stat = CYG_FLASH_ERR_DRV_VERIFY; + CHATTER(dev, "V"); + } #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; - - } + if (CYG_FLASH_ERR_OK != stat) { + if (err_address) + *err_address = addr; + break; + } + CHATTER(dev, "."); + write_count -= this_write; + addr += this_write; + ram += this_write; + } + FLASH_Disable(flash_base, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); + CHATTER(dev, "\n"); + UNLOCK(dev); + if (stat != CYG_FLASH_ERR_OK) { + return (stat); + } + if (len > (dev->end + 1 - flash_base)) { + return cyg_flash_program(dev->end+1, ram, + len - (dev->end + 1 - flash_base), + err_address); + } + return CYG_FLASH_ERR_OK; +} - 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 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)("."); -#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); -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); -#endif +__externC int +cyg_flash_read(const cyg_flashaddr_t flash_base, + void *ram_base, + size_t len, + cyg_flashaddr_t *err_address) +{ + struct cyg_flash_dev * dev; + cyg_flashaddr_t addr, end_addr; + unsigned char * ram = (unsigned char *)ram_base; + size_t read_count; + int stat = CYG_FLASH_ERR_OK; + + dev = find_dev(flash_base, &stat); + if (!dev) return stat; + + LOCK(dev); + addr = flash_base; + if (len > (dev->end + 1 - flash_base)) { + end_addr = dev->end; + } else { + end_addr = flash_base + len - 1; + } + read_count = (end_addr + 1) - flash_base; + + // CHATTER(dev, "... Read from %p-%p to %p: ", addr, end_addr, ram_base); + + // If the flash is directly accessible, just read it in one go. This + // still happens with the mutex locked to protect against concurrent + // programs/erases. + if (! dev->funs->flash_read) { + memcpy(ram, (void*)addr, read_count); + } else { +#ifndef CYGHWR_IO_FLASH_INDIRECT_READS + CYG_FAIL("read function supplied but indirect reads not enabled"); + stat = CYG_FLASH_ERR_PROTOCOL; + if (err_address) { + *err_address = addr; + } +#else + // We have to indirect through the device driver. + // The first read may be in the middle of a block. Do the necessary + // adjustment here rather than inside the loop. + size_t offset; + cyg_flashaddr_t block = flash_block_begin(flash_base, dev); + HAL_FLASH_CACHES_STATE(d_cache, i_cache); + if (addr == block) { + offset = 0; + } else { + offset = addr - block; + } + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(flash_base, end_addr); + while (read_count > 0) { + size_t block_size = flash_block_size(dev, addr); + size_t this_read; + if (read_count > (block_size - offset)) { + this_read = block_size - offset; + } else { + this_read = read_count; + } + // Only the first block may need the offset + offset = 0; + + stat = dev->funs->flash_read(dev, addr, ram, this_read); + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = addr; + break; + } + // CHATTER(dev, "."); + read_count -= this_read; + addr += this_read; + ram += this_read; + } + FLASH_Disable(flash_base, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); +#endif + } + // CHATTER(dev, "\n"); + UNLOCK(dev); + 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 (len > (dev->end + 1 - flash_base)) { + return cyg_flash_read(dev->end+1, ram, + len - (dev->end + 1 - flash_base), + 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, + 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; + struct cyg_flash_dev * dev; + size_t lock_count; + int stat = CYG_FLASH_ERR_OK; + HAL_FLASH_CACHES_STATE(d_cache, i_cache); -#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT - if (plf_flash_query_soft_wp(addr,len)) - return FLASH_ERR_PROTECT; -#endif - - _flash_lock_block = (code_fun*) __anonymizer(&flash_lock_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); - } + dev = find_dev(flash_base, &stat); + if (!dev) return stat; + if (!dev->funs->flash_block_lock) return CYG_FLASH_ERR_INVALID; -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Lock from %p-%p: ", block, end_addr); -#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_SOFT_WRITE_PROTECT(flash_base, len); + + LOCK(dev); + if (len > (dev->end + 1 - flash_base)) { + end_addr = dev->end; + } else { + end_addr = flash_base + len - 1; + } + block = flash_block_begin(flash_base, dev); + lock_count = (end_addr + 1) - block; + + CHATTER(dev, "... Locking from %p-%p: ", (void*)block, (void*)end_addr); + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(flash_base, end_addr); + while (lock_count > 0) { + size_t block_size = flash_block_size(dev, block); + if (lock_count < block_size) { + lock_count = block_size; + } + stat = dev->funs->flash_block_lock(dev,block); + + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = block; + break; + } + block += block_size; + lock_count -= block_size; + CHATTER(dev, "."); + } + FLASH_Disable(flash_base, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); + CHATTER(dev, "\n"); + UNLOCK(dev); + if (stat != CYG_FLASH_ERR_OK) { + return stat; + } - // 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; - } -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); -#endif - } - FLASH_Disable(block, end_addr); - HAL_FLASH_CACHES_ON(d_cache, i_cache); -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); -#endif - return (stat); + // Recurse if necessary for the next device + if (len > (dev->end + 1 - flash_base)) { + return cyg_flash_lock(dev->end+1, + len - (dev->end + 1 - flash_base), + 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, + 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; + struct cyg_flash_dev * dev; + size_t unlock_count; + int stat = CYG_FLASH_ERR_OK; + HAL_FLASH_CACHES_STATE(d_cache, i_cache); -#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT - if (plf_flash_query_soft_wp(addr,len)) - return 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); - } + dev = find_dev(flash_base, &stat); + if (!dev) return stat; + if (!dev->funs->flash_block_unlock) return CYG_FLASH_ERR_INVALID; -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("... Unlock from %p-%p: ", block, end_addr); -#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; - } -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("."); -#endif + CHECK_SOFT_WRITE_PROTECT(flash_base, len); + + LOCK(dev); + if (len > (dev->end + 1 - flash_base)) { + end_addr = dev->end; + } else { + end_addr = flash_base + len - 1; + } + block = flash_block_begin(flash_base, dev); + unlock_count = (end_addr + 1) - block; + + CHATTER(dev, "... Unlocking from %p-%p: ", (void*)block, (void*)end_addr); + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + FLASH_Enable(flash_base, end_addr); + while (unlock_count > 0) { + size_t block_size = flash_block_size(dev, block); + if (unlock_count < block_size) { + unlock_count = block_size; } - FLASH_Disable(block, end_addr); - HAL_FLASH_CACHES_ON(d_cache, i_cache); -#ifdef CYGSEM_IO_FLASH_CHATTER - (*flash_info.pf)("\n"); -#endif - return (stat); + stat = dev->funs->flash_block_unlock(dev,block); + + if (CYG_FLASH_ERR_OK != stat && err_address) { + *err_address = block; + break; + } + block += block_size; + unlock_count -= block_size; + + CHATTER(dev, "."); + } + FLASH_Disable(flash_base, end_addr); + HAL_FLASH_CACHES_ON(d_cache, i_cache); + CHATTER(dev, "\n"); + UNLOCK(dev); + if (stat != CYG_FLASH_ERR_OK) { + return stat; + } + + // Recurse if necessary for the next device + if (len > (dev->end + 1 - flash_base)) { + return cyg_flash_lock(dev->end+1, + len - (dev->end + 1 - flash_base), + 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"; } } +// Dummy routines to put into the device function tables, to handle +// unsupported/unnecessary functionality. For example not all devices +// support block locking. +// +// A dummy initialization routine, for platforms where everything is +// done statically and there is no need to check device ids or +// anything similar. +int +cyg_flash_devfn_init_nop(struct cyg_flash_dev* dev) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + return CYG_FLASH_ERR_OK; +} + +// A dummy query routine. The implementation of this is specific to +// each device driver, so some device drivers may choose to do +// nothing. +size_t +cyg_flash_devfn_query_nop(struct cyg_flash_dev* dev, void* data, size_t len) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + CYG_UNUSED_PARAM(void*, data); + CYG_UNUSED_PARAM(size_t, len); + return 0; +} + +// Dummy lock/unlock routines +int +cyg_flash_devfn_lock_nop(struct cyg_flash_dev* dev, const cyg_flashaddr_t addr) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + CYG_UNUSED_PARAM(cyg_flashaddr_t, addr); +#if defined(CYGHWR_IO_FLASH_BLOCK_LOCKING) && (1 < CYGHWR_IO_FLASH_DEVICE) +// If we've been built with locking, and there's more than one flash +// device in the system, then this is probably only being called because +// we can't tell what devices do and don't support locking, and for a _nop +// function this is the device that doesn't support locking. So we don't +// complain if we're asked to. + return CYG_FLASH_ERR_OK; +#else + return CYG_FLASH_ERR_DRV_WRONG_PART; +#endif +} + +int +cyg_flash_devfn_unlock_nop(struct cyg_flash_dev* dev, const cyg_flashaddr_t addr) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + CYG_UNUSED_PARAM(cyg_flashaddr_t, addr); +#if defined(CYGHWR_IO_FLASH_BLOCK_LOCKING) && (1 < CYGHWR_IO_FLASH_DEVICE) +// If we've been built with locking, and there's more than one flash +// device in the system, then this is probably only being called because +// we can't tell what devices do and don't support locking, and for a _nop +// function this is the device that doesn't support locking. So we don't +// complain if we're asked to. + return CYG_FLASH_ERR_OK; +#else + return CYG_FLASH_ERR_DRV_WRONG_PART; +#endif +} + +// On some architectures there are problems calling the .2ram +// functions from the main ones. Specifically the compiler may issue a +// short call, even though the flash and ram are too far apart. The +// solution is to indirect via a function pointer, but the simplistic +// approach is vulnerable to compiler optimization. Hence the function +// pointer is passed through an anonymizer. +void* +cyg_flash_anonymizer(void* fn) +{ + return fn; +} + // EOF io/flash/..../flash.c diff --git a/packages/io/flash/current/src/flash_legacy.h b/packages/io/flash/current/src/flash_legacy.h new file mode 100644 --- /dev/null +++ b/packages/io/flash/current/src/flash_legacy.h @@ -0,0 +1,186 @@ +//========================================================================== +// +// flash_legacy.h +// +// Flash programming - some internal implementation details. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// copyright (C) 2004 Andrew Lunn +// Copyright (C) 2003 Gary Thomas +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2004 eCosCentric Limited +// +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas, Andrew Lunn +// Date: 2000-07-14 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include + + +// Some FLASH devices may require additional support, e.g. to turn on +// appropriate voltage drivers, before any operation. +#ifdef CYGIMP_FLASH_ENABLE +# define FLASH_Enable(__start, __end) CYGIMP_FLASH_ENABLE((void *)__start, (void *)__end) +extern void CYGIMP_FLASH_ENABLE(void *, void *); +#else +# define FLASH_Enable(_start_, _end_) CYG_EMPTY_STATEMENT +#endif +#ifdef CYGIMP_FLASH_DISABLE +# define FLASH_Disable(__start, __end) CYGIMP_FLASH_DISABLE((void *)__start, (void *)__end) +extern void CYGIMP_FLASH_DISABLE(void *, void *); +#else +# define FLASH_Disable(_start_, _end_) CYG_EMPTY_STATEMENT +#endif + +// +// Some platforms have a DIP switch or jumper that tells the software that +// the flash is write protected. +// +#ifdef CYGSEM_IO_FLASH_SOFT_WRITE_PROTECT +externC cyg_bool plf_flash_query_soft_wp(void *addr, int len); +#endif + +//--------------------------------------------------------------------------- +// If all of the flash devices handle cache themselves, or do not need any +// special cache treatment, then the flash macros can be no-ops. +#ifndef CYGHWR_IO_FLASH_DEVICE_NEEDS_CACHE_HANDLED +# undef HAL_FLASH_CACHES_OFF +# undef HAL_FLASH_CACHES_ON +# undef HAL_FLASH_CACHES_STATE +# define HAL_FLASH_CACHES_OFF(_d_, _i_) CYG_EMPTY_STATEMENT +# define HAL_FLASH_CACHES_ON(_d_, _i_) CYG_EMPTY_STATEMENT +# define HAL_FLASH_CACHES_STATE(_d_, _i_) CYG_EMPTY_STATEMENT +#endif + +// Execution of flash code must be done inside a +// HAL_FLASH_CACHES_OFF/HAL_FLASH_CACHES_ON region - disabling the +// cache on unified cache systems is necessary to prevent burst access +// to the flash area being programmed. With Harvard style caches, only +// the data cache needs to be disabled, but the instruction cache is +// disabled for consistency. + +// Targets may provide alternative implementations for these macros in +// the hal_cache.h (or var/plf) files. + +// The first part below is a generic, optimal implementation. The +// second part is the old implementation that has been tested to work +// on some targets - but it is not be suitable for targets that would +// do burst access to the flash (it does not disable the data cache). + +// Both implementations must be called with interrupts disabled. + +// NOTE: Do _not_ change any of the below macros without checking that +// the changed code still works on _all_ platforms that rely on these +// macros. There is no such thing as logical and correct when dealing +// with different cache and IO models, so _do not_ mess with this code +// unless you test it properly afterwards. + +#ifndef HAL_FLASH_CACHES_OFF + +// Some drivers have only been tested with the old macros below. +#ifndef HAL_FLASH_CACHES_OLD_MACROS + +#ifdef HAL_CACHE_UNIFIED + +// Note: the ucache code has not been tested yet on any target. +#define HAL_FLASH_CACHES_OFF(_d_, _i_) \ + CYG_MACRO_START \ + _i_ = 0; /* avoids warning */ \ + HAL_UCACHE_IS_ENABLED(_d_); \ + HAL_UCACHE_SYNC(); \ + HAL_UCACHE_INVALIDATE_ALL(); \ + HAL_UCACHE_DISABLE(); \ + CYG_MACRO_END + +#define HAL_FLASH_CACHES_ON(_d_, _i_) \ + CYG_MACRO_START \ + if (_d_) HAL_UCACHE_ENABLE(); \ + CYG_MACRO_END + +#else // HAL_CACHE_UNIFIED + +#define HAL_FLASH_CACHES_OFF(_d_, _i_) \ + CYG_MACRO_START \ + _i_ = 0; /* avoids warning */ \ + HAL_DCACHE_IS_ENABLED(_d_); \ + HAL_DCACHE_SYNC(); \ + HAL_DCACHE_INVALIDATE_ALL(); \ + HAL_DCACHE_DISABLE(); \ + HAL_ICACHE_INVALIDATE_ALL(); \ + CYG_MACRO_END + +#define HAL_FLASH_CACHES_ON(_d_, _i_) \ + CYG_MACRO_START \ + if (_d_) HAL_DCACHE_ENABLE(); \ + CYG_MACRO_END + +#endif // HAL_CACHE_UNIFIED + +#else // HAL_FLASH_CACHES_OLD_MACROS + +// Note: This implementation is broken as it will always enable the i-cache +// even if it was not enabled before. It also doesn't work if the +// target uses burst access to flash since the d-cache is left enabled. +// However, this does not mean you can change this code! Leave it as +// is - if you want a different implementation, provide it in the +// arch/var/platform cache header file. + +#define HAL_FLASH_CACHES_OFF(_d_, _i_) \ + _d_ = 0; /* avoids warning */ \ + _i_ = 0; /* avoids warning */ \ + HAL_DCACHE_SYNC(); \ + HAL_ICACHE_DISABLE(); + +#define HAL_FLASH_CACHES_ON(_d_, _i_) \ + HAL_ICACHE_ENABLE(); + +#endif // HAL_FLASH_CACHES_OLD_MACROS + +#endif // HAL_FLASH_CACHES_OFF + +#ifndef HAL_FLASH_CACHES_STATE +# define HAL_FLASH_CACHES_STATE(_d_, _i_) int _d_, _i_ +#endif diff --git a/packages/io/flash/current/src/flashiodev.c b/packages/io/flash/current/src/flashiodev.c --- a/packages/io/flash/current/src/flashiodev.c +++ b/packages/io/flash/current/src/flashiodev.c @@ -9,6 +9,8 @@ // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2004 Andrew Lunn +// Copyright (C) 2004, 2007 eCosCentric Ltd. // // 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 @@ -32,17 +34,14 @@ // // This exception does not invalidate any other reasons why a work based on // this file might be covered by the GNU General Public License. -// -// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. -// at http://sources.redhat.com/ecos/ecos-license/ // ------------------------------------------- //####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): jlarmour -// Contributors: woehler -// Date: 2002-01-16 +// Contributors: woehler, Andrew Lunn +// Date: 2004-12-03 // Purpose: // Description: // @@ -50,122 +49,335 @@ // //========================================================================== -#define _FLASH_PRIVATE_ +// INCLUDES + #include - #include #include +#include #include #include #include #include // memcpy #include +#include // mutexes + +// MACROS #define MIN(x,y) ((x)<(y) ? (x) : (y)) +#define MAX(x,y) ((x)>(y) ? (x) : (y)) + +// TYPES struct flashiodev_priv_t{ - char *start; - char *end; + cyg_devtab_entry_t handle; + cyg_flashaddr_t start; + cyg_flashaddr_t end; + cyg_bool valid; }; +// FUNCTION PROTOTYPES -static struct flashiodev_priv_t flashiodev_priv[1]; +static bool flashiodev_init( struct cyg_devtab_entry *tab ); +static Cyg_ErrNo +flashiodev_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) ; +static Cyg_ErrNo +flashiodev_bread( cyg_io_handle_t handle, void *buf, cyg_uint32 *len, + cyg_uint32 pos); +static Cyg_ErrNo +flashiodev_bwrite( cyg_io_handle_t handle, const void *buf, cyg_uint32 *len, + cyg_uint32 pos ); +static Cyg_ErrNo +flashiodev_get_config( cyg_io_handle_t handle, + cyg_uint32 key, + void* buf, + cyg_uint32* len); +static Cyg_ErrNo +flashiodev_set_config( cyg_io_handle_t handle, + cyg_uint32 key, + const void* buf, + cyg_uint32* len); + +// STATICS/GLOBALS + +static struct flashiodev_priv_t flashiodev_table[CYGNUM_IO_FLASH_BLOCK_DEVICES]; +static cyg_drv_mutex_t flashiodev_table_lock; + +BLOCK_DEVIO_TABLE( cyg_io_flashdev_ops, + &flashiodev_bwrite, + &flashiodev_bread, + NULL, // no select + &flashiodev_get_config, + &flashiodev_set_config + ); + + +BLOCK_DEVTAB_ENTRY( cyg_io_flashdev, + "/dev/flash/", + NULL, + &cyg_io_flashdev_ops, + &flashiodev_init, + &flashiodev_lookup, + NULL ); + +// FUNCTIONS static int dummy_printf( const char *fmt, ... ) {return 0;} static bool flashiodev_init( struct cyg_devtab_entry *tab ) { - struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; - int stat = flash_init( &dummy_printf ); - if ( stat == 0 ) { -#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_FIS_1 - CYG_ADDRESS flash_base; - unsigned long size; + int stat = cyg_flash_init( &dummy_printf ); + cyg_ucount32 i; - if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_FLASH_BASE, - CYGDAT_IO_FLASH_BLOCK_FIS_NAME_1, - &flash_base)) - return false; - if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_SIZE, - CYGDAT_IO_FLASH_BLOCK_FIS_NAME_1, - &size)) - return false; - - dev->start = (char *)flash_base; - dev->end = (char *)flash_base + size; -#else - dev->start = (char *)flash_info.start + CYGNUM_IO_FLASH_BLOCK_OFFSET_1; - dev->end = (char *)flash_info.start + CYGNUM_IO_FLASH_BLOCK_OFFSET_1 + - CYGNUM_IO_FLASH_BLOCK_LENGTH_1; -#endif - return true; - } else - return false; + if (stat == CYG_FLASH_ERR_OK) + { + for (i=0; ihandlers == &cyg_io_flashdev_ops, "Unexpected handlers - not flashdev_ops" ); + flashiodev_table[i].handle.handlers = &cyg_io_flashdev_ops; + flashiodev_table[i].handle.status = CYG_DEVTAB_STATUS_BLOCK; + flashiodev_table[i].handle.priv = &flashiodev_table[i]; // link back + } // for + cyg_drv_mutex_init( &flashiodev_table_lock ); + } // if + + return (stat == CYG_FLASH_ERR_OK); } // flashiodev_init() -#if 0 +#ifdef CYGFUN_IO_FLASH_BLOCK_FROM_DEVOFFSET +static cyg_uint32 +parsenum( const char **str ) +{ + cyg_uint32 num = 0; + cyg_uint8 base = 10; + + // trim leading 0s + while (**str == '0') + (*str)++; + // crudely detect hex by assuming that something with a leading 0x + // can just match the 'x' with a leading 0 being trimmed. + if ( (**str == 'x') || (**str == 'X') ) + { + (*str)++; + base = 16; + } + + while (**str) { + switch (**str) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + num = (num * base) + (**str-'0'); + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + if ( base != 16 ) + return num; + num = (num * base) + (**str-'a' + 10); + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + if ( base != 16 ) + return num; + num = (num << 4) + (**str-'A' + 10); + break; + default: + return num; + } // switch + (*str)++; + } // while + + return num; +} // parsenum() +#endif // ifdef CYGFUN_IO_FLASH_BLOCK_FROM_DEVOFFSET + static Cyg_ErrNo -flashiodev_lookup( struct cyg_devtab_entry **tab, - struct cyg_devtab_entry *sub_tab, - const char *name) -{ +flashiodev_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + // We enter with **tab being the entry for /dev/flash only + // We will leave with **tab instead pointing to a newly configured flash device instance + // We could allocate the space for recording these dynamically, but that's overkill, + // so we instead choose the eCos ethos of "let the user decide at configure time". + // After all there's a good chance there'll be only one or two needed at one time. + + // There are two styles of path: + // /dev/flash/fis/ + // and + // /dev/flash//[,] + + cyg_flashaddr_t start=0; + cyg_flashaddr_t end=0; + cyg_bool valid = false; + +#ifdef CYGFUN_IO_FLASH_BLOCK_FROM_FIS + // First, support FIS + if ((name[0] == 'f') && (name[1] == 'i') && + (name[2] == 's') && (name[3] == '/')) + { + CYG_ADDRESS flash_base; + unsigned long size; + + if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_FLASH_BASE, + (char *)&name[4], + &flash_base)) + return -ENOENT; + + if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_SIZE, + (char *)&name[4], + &size)) + return -ENODEV; // If the previous call worked, then this failing would be very wrong. + start = flash_base; + end = flash_base + size - 1; + valid = true; + } else +#endif // ifdef CYGFUN_IO_FLASH_BLOCK_FROM_FIS +#ifdef CYGFUN_IO_FLASH_BLOCK_FROM_DEVOFFSET + // Next, support device numbers with offsets encoded in path name + // Note that for now we assume < 10 flash devices. I think this is reasonable + // to avoid unnecessary code. It can be changed later if needed. + if ( (name[0] >= '0') && (name[0] <= '9') ) + { + cyg_uint32 flashdevno = name[0] - '0'; + int res; + cyg_flash_info_t info; + cyg_uint32 offset; + const char *tempstr; + + if (name[1] != '/') + return -ENOTSUP; + res = cyg_flash_get_info( flashdevno, &info ); + if (CYG_FLASH_ERR_OK != res) + return -ENOENT; + + // Now modify with offset and optional length + tempstr = &name[2]; + offset = parsenum( &tempstr ); + + start = info.start + offset; + end = info.end; + + if (*tempstr == ',') // optional length + { + cyg_uint32 length; + + tempstr++; + length = parsenum( &tempstr ); + + // Check we don't exceed end of device. + // Note the flash end address is the last byte of addressible flash *not* the base+size + if ( (start + length - 1) > end ) + return -EINVAL; + end = start + length - 1; + } + valid = true; + } // else if +#endif + + if (valid) + { + // Find a slot and use it + cyg_ucount32 i; + + cyg_drv_mutex_lock( &flashiodev_table_lock ); + for (i=0; ipriv; + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; - char *startpos = dev->start + pos; - void *erraddr; - Cyg_ErrNo err = ENOERR; - - + cyg_flashaddr_t startpos = dev->start + pos; + Cyg_ErrNo err = ENOERR; + int flasherr; + + CYG_ASSERT( dev->handle.handlers == &cyg_io_flashdev_ops, "bad flash operation link" ); + CYG_ASSERT( ((struct flashiodev_priv_t*)(dev->handle.priv))->valid, "operation on not valid flash device instance" ); + #ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time - char *endpos = startpos + *len - 1; - char *flashend = MIN( (char *)flash_info.end - 1, dev->end - 1); - if ( startpos < dev->start ) - return -EINVAL; - if ( endpos > flashend ) - return -EINVAL; + cyg_flashaddr_t endpos = startpos + *len - 1; + if ( startpos < dev->start ) + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; #endif - - err = flash_read( startpos, - (void *)buf, *len, &erraddr ); - - if ( err ) - err = -EIO; // just something sane - return err; + + flasherr = cyg_flash_read( startpos,(void *)buf, *len, NULL ); + + if ( flasherr != CYG_FLASH_ERR_OK ) + err = -EIO; // just something sane + return err; } // flashiodev_bread() static Cyg_ErrNo flashiodev_bwrite( cyg_io_handle_t handle, const void *buf, cyg_uint32 *len, cyg_uint32 pos ) -{ - struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; - struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; - - Cyg_ErrNo err = ENOERR; - void *erraddr; - char *startpos = dev->start + pos; +{ + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; + + Cyg_ErrNo err = ENOERR; + int flasherr; + cyg_flashaddr_t startpos = dev->start + pos; + + CYG_ASSERT( dev->handle.handlers == &cyg_io_flashdev_ops, "bad flash operation link" ); + CYG_ASSERT( ((struct flashiodev_priv_t*)(dev->handle.priv))->valid, "operation on not valid flash device instance" ); + + // Unlike some other cases we _do_ do bounds checking on this all the time, because + // the consequences of writing over the wrong bit of flash are so nasty. + cyg_flashaddr_t endpos = startpos + *len - 1; + if ( startpos < dev->start ) + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; -#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time - char *endpos = startpos + *len - 1; - char *flashend = MIN( (char *)flash_info.end - 1, dev->end - 1); - if ( startpos < dev->start ) - return -EINVAL; - if ( endpos > flashend ) - return -EINVAL; -#endif - err = flash_program( startpos, - (void *)buf, *len, &erraddr ); - - if ( err ) - err = -EIO; // just something sane - return err; + flasherr = cyg_flash_program( startpos, (void *)buf, *len, NULL ); + + if ( flasherr != CYG_FLASH_ERR_OK ) + err = -EIO; // just something sane + return err; } // flashiodev_bwrite() static Cyg_ErrNo @@ -174,68 +386,135 @@ flashiodev_get_config( cyg_io_handle_t h void* buf, cyg_uint32* len) { - struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; - struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; - switch (key) { - case CYG_IO_GET_CONFIG_FLASH_ERASE: + CYG_ASSERT( dev->handle.handlers == &cyg_io_flashdev_ops, "bad flash operation link" ); + CYG_ASSERT( ((struct flashiodev_priv_t*)(dev->handle.priv))->valid, "operation on not valid flash device instance" ); + + switch (key) { + case CYG_IO_GET_CONFIG_FLASH_ERASE: { - if ( *len != sizeof( cyg_io_flash_getconfig_erase_t ) ) - return -EINVAL; - { - cyg_io_flash_getconfig_erase_t *e = (cyg_io_flash_getconfig_erase_t *)buf; - char *startpos = dev->start + e->offset; - #ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time - char *endpos = startpos + e->len - 1; - char *flashend = MIN( (char *)flash_info.end - 1, dev->end - 1); - if ( startpos < dev->start ) - return -EINVAL; - if ( endpos > flashend ) - return -EINVAL; + if (*len != sizeof( cyg_io_flash_getconfig_erase_t ) ) + return -EINVAL; #endif - e->flasherr = flash_erase( startpos, e->len, e->err_address ); - } - return ENOERR; + { + cyg_io_flash_getconfig_erase_t *e = (cyg_io_flash_getconfig_erase_t *)buf; + cyg_flashaddr_t startpos = dev->start + e->offset; + + // Unlike some other cases we _do_ do bounds checking on this all the time, because + // the consequences of erasing the wrong bit of flash are so nasty. + cyg_flashaddr_t endpos = startpos + e->len - 1; + if ( startpos < dev->start ) + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; + + e->flasherr = cyg_flash_erase( startpos, e->len, &e->err_address ); + } + return ENOERR; } - case CYG_IO_GET_CONFIG_FLASH_DEVSIZE: + +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + case CYG_IO_GET_CONFIG_FLASH_LOCK: { - if ( *len != sizeof( cyg_io_flash_getconfig_devsize_t ) ) - return -EINVAL; - { - cyg_io_flash_getconfig_devsize_t *d = - (cyg_io_flash_getconfig_devsize_t *)buf; +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + if (*len != sizeof( cyg_io_flash_getconfig_lock_t ) ) + return -EINVAL; +#endif + { + cyg_io_flash_getconfig_lock_t *d = (cyg_io_flash_getconfig_lock_t *)buf; + cyg_flashaddr_t startpos = dev->start + d->offset; - //d->dev_size = flash_info.blocks * flash_info.block_size; - d->dev_size = dev->end - dev->start; - } - return ENOERR; +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + cyg_flashaddr_t endpos = startpos + d->len - 1; + if ( startpos < dev->start ) + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; +#endif + + d->flasherr = cyg_flash_lock( startpos, d->len, &d->err_address ); + } + return ENOERR; } - case CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE: + case CYG_IO_GET_CONFIG_FLASH_UNLOCK: { - cyg_io_flash_getconfig_blocksize_t *b = - (cyg_io_flash_getconfig_blocksize_t *)buf; #ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time - char *startpos = dev->start + b->offset; - char *flashend = MIN( (char *)flash_info.end - 1, dev->end - 1); + if (*len != sizeof( cyg_io_flash_getconfig_unlock_t ) ) + return -EINVAL; +#endif + { + cyg_io_flash_getconfig_unlock_t *d = (cyg_io_flash_getconfig_unlock_t *)buf; + cyg_flashaddr_t startpos = dev->start + d->offset; +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + cyg_flashaddr_t endpos = startpos + d->len - 1; if ( startpos < dev->start ) - return -EINVAL; - if ( startpos > flashend ) - return -EINVAL; -#endif - if ( *len != sizeof( cyg_io_flash_getconfig_blocksize_t ) ) - return -EINVAL; - - // offset unused for now - b->block_size = flash_info.block_size; - return ENOERR; + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; +#endif + d->flasherr = cyg_flash_unlock( startpos, d->len, &d->err_address ); + } + return ENOERR; + } +#endif + + case CYG_IO_GET_CONFIG_FLASH_DEVSIZE: + { +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + if ( *len != sizeof( cyg_io_flash_getconfig_devsize_t ) ) + return -EINVAL; +#endif + { + cyg_io_flash_getconfig_devsize_t *d = + (cyg_io_flash_getconfig_devsize_t *)buf; + + d->dev_size = dev->end - dev->start + 1; + } + return ENOERR; } - default: + case CYG_IO_GET_CONFIG_FLASH_DEVADDR: + { +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + if ( *len != sizeof( cyg_io_flash_getconfig_devaddr_t ) ) return -EINVAL; +#endif + { + cyg_io_flash_getconfig_devaddr_t *d = + (cyg_io_flash_getconfig_devaddr_t *)buf; + + d->dev_addr = dev->start; + } + return ENOERR; } + + case CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE: + { + cyg_io_flash_getconfig_blocksize_t *b = + (cyg_io_flash_getconfig_blocksize_t *)buf; + cyg_flashaddr_t pos = dev->start + b->offset; + +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + if ( pos < dev->start ) + return -EINVAL; + if ( pos > dev->end ) + return -EINVAL; + if ( *len != sizeof( cyg_io_flash_getconfig_blocksize_t ) ) + return -EINVAL; +#endif + + b->block_size = cyg_flash_block_size( pos ); + return ENOERR; + } + + default: + return -EINVAL; + } } // flashiodev_get_config() static Cyg_ErrNo @@ -244,53 +523,29 @@ flashiodev_set_config( cyg_io_handle_t h const void* buf, cyg_uint32* len) { -#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_FIS_1 - struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; - struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; -#endif - - switch (key) { -#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_FIS_1 - case CYG_IO_SET_CONFIG_FLASH_FIS_NAME: + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; + + CYG_ASSERT( dev->handle.handlers == &cyg_io_flashdev_ops, "bad flash operation link" ); + CYG_ASSERT( ((struct flashiodev_priv_t*)(dev->handle.priv))->valid, "operation on not valid flash device instance" ); + + switch (key) { + case CYG_IO_SET_CONFIG_CLOSE: { - CYG_ADDRESS flash_base; - unsigned long size; - - if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_FLASH_BASE, - (char *)buf, &flash_base)) - return -ENOENT; - if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_SIZE, - (char *)buf, &size)) - return -ENOENT; - - dev->start = (char *)flash_base; - dev->end = (char *)flash_base + size; - return ENOERR; + Cyg_ErrNo err = ENOERR; + cyg_drv_mutex_lock( &flashiodev_table_lock ); + if (!dev->valid) + { + err = -EBADF; + } else { + dev->valid = false; + } + cyg_drv_mutex_unlock( &flashiodev_table_lock ); + return err; } -#endif - default: - return -EINVAL; - } + default: + return -EINVAL; + } } // flashiodev_set_config() -// get_config/set_config should be added later to provide the other flash -// operations possible, like erase etc. - -BLOCK_DEVIO_TABLE( cyg_io_flashdev1_ops, - &flashiodev_bwrite, - &flashiodev_bread, - 0, // no select - &flashiodev_get_config, - &flashiodev_set_config - ); - - -BLOCK_DEVTAB_ENTRY( cyg_io_flashdev1, - CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1, - 0, - &cyg_io_flashdev1_ops, - &flashiodev_init, - 0, // No lookup required - &flashiodev_priv[0] ); - // EOF flashiodev.c diff --git a/packages/io/flash/current/src/flashiodevlegacy.c b/packages/io/flash/current/src/flashiodevlegacy.c new file mode 100644 --- /dev/null +++ b/packages/io/flash/current/src/flashiodevlegacy.c @@ -0,0 +1,415 @@ +//========================================================================== +// +// flashiodev.c +// +// Old flash device interface to I/O subsystem +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// 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 +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// 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): jlarmour +// Contributors: woehler, Andrew Lunn +// Date: 2002-01-16 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#define _FLASH_PRIVATE_ +#include + +#include +#include +#include +#include +#include +#include // memcpy +#include + +#define MIN(x,y) ((x)<(y) ? (x) : (y)) +#define MAX(x,y) ((x)>(y) ? (x) : (y)) + +struct flashiodev_priv_t{ + cyg_flashaddr_t start; + cyg_flashaddr_t end; + cyg_bool use_fis; + cyg_bool use_absolute; + cyg_bool use_offset; + char * fis_name; + cyg_uint32 block_size; + cyg_bool init; +}; + +static int dummy_printf( const char *fmt, ... ) {return 0;} + +static bool +flashiodev_init( struct cyg_devtab_entry *tab ) +{ + int stat = cyg_flash_init( &dummy_printf ); + + return (stat == CYG_FLASH_ERR_OK); +} // flashiodev_init() + +static Cyg_ErrNo +flashiodev_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)(*tab)->priv; + cyg_flash_info_t info; + cyg_uint32 i; + int stat; + + if (dev->init) + return ENOERR; + + if (dev->use_fis) { + CYG_ADDRESS flash_base; + unsigned long size; + + if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_FLASH_BASE, + dev->fis_name, + &flash_base)) + return ENOERR; + // Strange, yes, but needed since we have to do a lookup in order to + // set the name using cyg_io_config_set. If we fail here you cannot + // do a set. Since dev->init will still be false and attempts to + // actually use the device will fail, so it is safe. + + if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_SIZE, + dev->fis_name, + &size)) + return ENOERR; // Ditto. + dev->start = flash_base; + dev->end = flash_base + size; + } + if (dev->use_offset) { + // dev->start contains the offset to the beginning of the block + // dev->end is the length of the block + cyg_flash_info_t info; + + cyg_flash_get_info(0, &info); + dev->start = dev->start + info.start; + dev->end = dev->start + dev->end; + } + if (dev->use_absolute) { + // dev->start is the absolute address of the start + // dev->end is the length; + dev->end = dev->start + dev->end; + } + + stat = cyg_flash_get_info_addr(dev->start, &info); + if (stat != CYG_FLASH_ERR_OK) { + return ENODEV; + } + dev->block_size = 0; + for (i=0; i < info.num_block_infos; i++){ + dev->block_size = MAX(dev->block_size, info.block_info[i].block_size); + } + + dev->init = 1; + return ENOERR; +} // flashiodev_lookup() + +static Cyg_ErrNo +flashiodev_bread( cyg_io_handle_t handle, void *buf, cyg_uint32 *len, + cyg_uint32 pos) +{ + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; + + cyg_flashaddr_t startpos = dev->start + pos; + Cyg_ErrNo err; + + if (!dev->init) { + return -EINVAL; + } + +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + cyg_flashaddr_t endpos = startpos + *len - 1; + if ( startpos < dev->start ) + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; +#endif + + err = cyg_flash_read( startpos,(void *)buf, *len, NULL ); + + if ( err != CYG_FLASH_ERR_OK ) + err = -EIO; // just something sane + return err; +} // flashiodev_bread() + +static Cyg_ErrNo +flashiodev_bwrite( cyg_io_handle_t handle, const void *buf, cyg_uint32 *len, + cyg_uint32 pos ) +{ + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; + + Cyg_ErrNo err; + cyg_flashaddr_t startpos = dev->start + pos; + + if (!dev->init) { + return -EINVAL; + } + +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + cyg_flashaddr_t endpos = startpos + *len - 1; + if ( startpos < dev->start ) + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; +#endif + err = cyg_flash_program( startpos, (void *)buf, *len, NULL ); + + if ( err ) + err = -EIO; // just something sane + return err; +} // flashiodev_bwrite() + +static Cyg_ErrNo +flashiodev_get_config( cyg_io_handle_t handle, + cyg_uint32 key, + void* buf, + cyg_uint32* len) +{ + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; + + if (!dev->init) { + return -EINVAL; + } + + switch (key) { + case CYG_IO_GET_CONFIG_FLASH_ERASE: + { + if (*len != sizeof( cyg_io_flash_getconfig_erase_t ) ) + return -EINVAL; + { + cyg_io_flash_getconfig_erase_t *e = (cyg_io_flash_getconfig_erase_t *)buf; + cyg_flashaddr_t startpos = dev->start + e->offset; + +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + cyg_flashaddr_t endpos = startpos + e->len - 1; + if ( startpos < dev->start ) + return -EINVAL; + if ( endpos > dev->end ) + return -EINVAL; +#endif + e->flasherr = cyg_flash_erase( startpos, e->len, e->err_address ); + } + return ENOERR; + } + case CYG_IO_GET_CONFIG_FLASH_DEVSIZE: + { + if ( *len != sizeof( cyg_io_flash_getconfig_devsize_t ) ) + return -EINVAL; + { + cyg_io_flash_getconfig_devsize_t *d = + (cyg_io_flash_getconfig_devsize_t *)buf; + + d->dev_size = dev->end - dev->start; + } + return ENOERR; + } + + case CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE: + { + cyg_io_flash_getconfig_blocksize_t *b = + (cyg_io_flash_getconfig_blocksize_t *)buf; +#ifdef CYGPKG_INFRA_DEBUG // don't bother checking this all the time + cyg_flashaddr_t startpos = dev->start + b->offset; + + if ( startpos < dev->start ) + return -EINVAL; + if ( startpos > dev->end ) + return -EINVAL; +#endif + if ( *len != sizeof( cyg_io_flash_getconfig_blocksize_t ) ) + return -EINVAL; + + // offset unused for now + b->block_size = dev->block_size; + return ENOERR; + } + + default: + return -EINVAL; + } +} // flashiodev_get_config() + +static Cyg_ErrNo +flashiodev_set_config( cyg_io_handle_t handle, + cyg_uint32 key, + const void* buf, + cyg_uint32* len) +{ + struct cyg_devtab_entry *tab = (struct cyg_devtab_entry *)handle; + struct flashiodev_priv_t *dev = (struct flashiodev_priv_t *)tab->priv; + + switch (key) { + case CYG_IO_SET_CONFIG_FLASH_FIS_NAME: + { + cyg_flashaddr_t flash_base; + unsigned long size; + cyg_flash_info_t info; + cyg_uint32 i; + int stat; + + if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_FLASH_BASE, + (char *)buf, &flash_base)) + return -ENOENT; + + if(!CYGACC_CALL_IF_FLASH_FIS_OP(CYGNUM_CALL_IF_FLASH_FIS_GET_SIZE, + (char *)buf, &size)) + return -ENOENT; + + dev->start = flash_base; + dev->end = flash_base + size; + + stat = cyg_flash_get_info_addr(dev->start, &info); + + if (stat != CYG_FLASH_ERR_OK) { + return -ENOENT; + } + dev->block_size = 0; + for (i=0; i < info.num_block_infos; i++){ + dev->block_size = MAX(dev->block_size, info.block_info[i].block_size); + } + dev->init = 1; + + return ENOERR; + } + default: + return -EINVAL; + } +} // flashiodev_set_config() + +// get_config/set_config should be added later to provide the other flash +// operations possible, like erase etc. + +#define CYG_FLASHIODEV_DEV(start, end, fis, static, offset, name) \ + { start, end, fis, static, offset, name, 0, 0 } + +#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_FIS_1 +static struct flashiodev_priv_t priv1 = CYG_FLASHIODEV_DEV( + 0, // start + 0, // end + 1, // use_fis + 0, // use_static + 0, // use_offset + CYGDAT_IO_FLASH_BLOCK_FIS_NAME_1); +#endif + +#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_STATIC_ABSOLUTE_1 +static struct flashiodev_priv_t priv1 = CYG_FLASHIODEV_DEV( + CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_START_1, // start + CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_START_1 + CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_LENGTH_1, + 0, // use_fis + 1, // use_static + 0, // use_offset + ""); // fis_name +#endif + +#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_STATIC_1 +static struct flashiodev_priv_t priv1 = CYG_FLASHIODEV_DEV( + CYGNUM_IO_FLASH_BLOCK_OFFSET_1, // start + CYGNUM_IO_FLASH_BLOCK_LENGTH_1, // end + 0, // use_fis + 0, // use_static + 1, // use_offset + ""); // fis_name +#endif + +BLOCK_DEVIO_TABLE( cyg_io_flashdev1_ops, + &flashiodev_bwrite, + &flashiodev_bread, + 0, // no select + &flashiodev_get_config, + &flashiodev_set_config + ); + + +BLOCK_DEVTAB_ENTRY( cyg_io_flashdev1, + CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_1, + 0, + &cyg_io_flashdev1_ops, + &flashiodev_init, + &flashiodev_lookup, + &priv1 ); + +#ifdef CYGSEM_IO_FLASH_BLOCK_DEVICE_2 +#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_FIS_2 +static struct flashiodev_priv_t priv2 = CYG_FLASHIODEV_DEV( + 0, // start + 0, // end + 1, // use_fis + 0, // use_static + 0, // use_offset + CYGDAT_IO_FLASH_BLOCK_FIS_NAME_2); +#endif + +#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_STATIC_ABSOLUTE_2 +static struct flashiodev_priv_t priv2 = CYG_FLASHIODEV_DEV( + CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_START_2, // start + CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_START_2 + CYGNUM_IO_FLASH_BLOCK_ABSOLUTE_LENGTH_2, + 0, // use_fis + 1, // use_static + 0, // use_offset + ""); // fis_name +#endif + +#ifdef CYGNUM_IO_FLASH_BLOCK_CFG_STATIC_2 +static struct flashiodev_priv_t priv2 = CYG_FLASHIODEV_DEV( + CYGNUM_IO_FLASH_BLOCK_OFFSET_2, // start + CYGNUM_IO_FLASH_BLOCK_LENGTH_2, // end + 0, // use_fis + 0, // use_static + 1, // use_offset + ""); // fis_name +#endif + +BLOCK_DEVTAB_ENTRY( cyg_io_flashdev2, + CYGDAT_IO_FLASH_BLOCK_DEVICE_NAME_2, + 0, + &cyg_io_flashdev1_ops, + &flashiodev_init, + &flashiodev_lookup, + &priv2 ); +#endif + +// EOF flashiodev.c diff --git a/packages/io/flash/current/src/legacy_api.c b/packages/io/flash/current/src/legacy_api.c new file mode 100644 --- /dev/null +++ b/packages/io/flash/current/src/legacy_api.c @@ -0,0 +1,162 @@ +//========================================================================== +// +// legacy_api.c +// +// Legacy API implementation on top of the new API +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// 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 +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Andrew Lunn +// Contributors: Andrew Lunn +// Date: 2004-07-02 +// Purpose: +// Description: Implement an interface to the legacy device drivers +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include + +#include + +int +flash_init(_printf *pf) +{ + return cyg_flash_init(pf); +} + +int +flash_verify_addr(void *target) +{ + return cyg_flash_verify_addr((cyg_flashaddr_t) target); +} + +int +flash_get_limits(void *target, void **start, void **end) +{ + int err; + cyg_flash_info_t info; + + err = cyg_flash_get_info(0, &info); + if (err != CYG_FLASH_ERR_OK) { + return err; + } + + *start = (void *)info.start; + *end = (void *)info.end; + + return CYG_FLASH_ERR_OK; +} + + +int +flash_get_block_info(int *block_size, int *blocks) +{ + size_t biggest_size=0; + cyg_uint32 i; + cyg_flash_info_t info; + int err; + + err = cyg_flash_get_info(0, &info); + if (err != CYG_FLASH_ERR_OK) { + return err; + } + + // Find the biggest size of blocks + for (i=0; i < info.num_block_infos; i++) { + if (info.block_info[i].block_size > biggest_size) { + biggest_size = info.block_info[i].block_size; + } + } + + // Calculate the number of biggest size blocks + *block_size = biggest_size; + *blocks = 0; + for (i=0; i < info.num_block_infos; i++) { + *blocks += (info.block_info[i].block_size * + info.block_info[i].blocks) / + biggest_size; + } + return CYG_FLASH_ERR_OK; +} + +int +flash_erase(void *addr, int len, void **err_addr) +{ + return cyg_flash_erase((cyg_flashaddr_t)addr, len, + (cyg_flashaddr_t *)err_addr); +} + +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + +int +flash_lock(void *addr, int len, void **err_addr) +{ + return cyg_flash_lock((cyg_flashaddr_t)addr, len, + (cyg_flashaddr_t *)err_addr); +} + +int +flash_unlock(void *addr, int len, void **err_addr) +{ + return cyg_flash_unlock((cyg_flashaddr_t)addr, len, + (cyg_flashaddr_t *)err_addr); +} + +#endif + +int flash_program(void *flash_base, void *ram_base, int len, + void **err_address) +{ + return cyg_flash_program((cyg_flashaddr_t)flash_base, ram_base, + len, (cyg_flashaddr_t *)err_address); +} + +int flash_read(void *flash_base, void *ram_base, int len, void **err_address) +{ + return cyg_flash_read((cyg_flashaddr_t)flash_base, ram_base, + len, (cyg_flashaddr_t *)err_address); +} + +char *flash_errmsg(int err) +{ + return (char *)cyg_flash_errmsg(err); +} + +// EOF legacy_api.c diff --git a/packages/io/flash/current/src/legacy_dev.c b/packages/io/flash/current/src/legacy_dev.c new file mode 100644 --- /dev/null +++ b/packages/io/flash/current/src/legacy_dev.c @@ -0,0 +1,240 @@ +//========================================================================== +// +// legacy_dev.c +// +// Interface to the legacy device drivers +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 Andrew Lunn +// Copyright (C) 2004, 2006 eCosCentric Limited +// +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Andrew Lunn +// Contributors: Andrew Lunn +// Date: 2004-07-02 +// Purpose: +// Description: Implement an interface to the legacy device drivers +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include "flash_legacy.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. +#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*); + +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; + +// Initialize the device +static int +legacy_flash_init (struct cyg_flash_dev *dev) +{ + int err; + static cyg_flash_block_info_t block_info[1]; + + flash_info.pf = dev->pf; + + err=flash_hwr_init(); + + if (!err) { + dev->start = (cyg_flashaddr_t)flash_info.start; + dev->end = dev->start + flash_info.block_size * flash_info.blocks - 1; + dev->num_block_infos = 1; + dev->block_info = block_info; + block_info[0].block_size = flash_info.block_size; + block_info[0].blocks = flash_info.blocks; + } + return err; +} + +static size_t +legacy_flash_query (struct cyg_flash_dev *dev, + void * data, + size_t len) +{ + typedef void code_fun(void*); + code_fun *_flash_query; + + _flash_query = (code_fun*) cyg_flash_anonymizer(&flash_query); + + (*_flash_query)(data); + + return len; +} + +static int +legacy_flash_erase_block (struct cyg_flash_dev *dev, + cyg_flashaddr_t block_base) +{ + typedef int code_fun(cyg_flashaddr_t, unsigned int); + code_fun *_flash_erase_block; + size_t block_size = dev->block_info[0].block_size; + int stat; + + _flash_erase_block = (code_fun*) cyg_flash_anonymizer(&flash_erase_block); + + stat = (*_flash_erase_block)(block_base, block_size); + return flash_hwr_map_error(stat); +} + +static int +legacy_flash_program(struct cyg_flash_dev *dev, + cyg_flashaddr_t base, + const void* data, size_t len) +{ + typedef int code_fun(cyg_flashaddr_t, const void *, int, unsigned long, int); + code_fun *_flash_program_buf; + size_t block_size = dev->block_info[0].block_size; + size_t block_mask = ~(block_size -1); + int stat; + + _flash_program_buf = (code_fun*) cyg_flash_anonymizer(&flash_program_buf); + + stat = (*_flash_program_buf)(base, data, len, block_mask, flash_info.buffer_size); + return flash_hwr_map_error(stat); +} + +#ifdef CYGSEM_IO_FLASH_READ_INDIRECT +static int +legacy_flash_read (struct cyg_flash_dev *dev, + const cyg_flashaddr_t base, + void* data, size_t len) +{ + typedef int code_fun(const cyg_flashaddr_t, void *, int, unsigned long, int); + code_fun *_flash_read_buf; + size_t block_size = dev->block_info[0].block_size; + size_t block_mask = ~(block_size -1); + int stat; + _flash_read_buf = (code_fun*) cyg_flash_anonymizer(&flash_read_buf); + + stat = (*_flash_read_buf)(base, data, len, block_mask, block_size); + return flash_hwr_map_error(stat); +} + +# define LEGACY_FLASH_READ legacy_flash_read +#else +# define LEGACY_FLASH_READ ((int (*)(struct cyg_flash_dev*, const cyg_flashaddr_t, void*, const size_t))0) +#endif + + +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING +static int +legacy_flash_block_lock (struct cyg_flash_dev *dev, + const cyg_flashaddr_t block_base) +{ + typedef int code_fun(cyg_flashaddr_t); + code_fun *_flash_lock_block; + int stat; + _flash_lock_block = (code_fun*) cyg_flash_anonymizer(&flash_lock_block); + + stat = (*_flash_lock_block)(block_base); + return flash_hwr_map_error(stat); +} + +static int +legacy_flash_block_unlock (struct cyg_flash_dev *dev, + const cyg_flashaddr_t block_base) +{ + typedef int code_fun(cyg_flashaddr_t, int, int); + code_fun *_flash_unlock_block; + size_t block_size = dev->block_info[0].block_size; + cyg_uint32 blocks = dev->block_info[0].blocks; + int stat; + _flash_unlock_block = (code_fun*) cyg_flash_anonymizer(&flash_unlock_block); + + stat = (*_flash_unlock_block)(block_base, block_size, blocks); + return flash_hwr_map_error(stat); +} +#endif + +void +flash_dev_query(void* data) +{ + typedef void code_fun(void*); + code_fun *_flash_query; + HAL_FLASH_CACHES_STATE(d_cache, i_cache); + + _flash_query = (code_fun*) cyg_flash_anonymizer(&flash_query); + + HAL_FLASH_CACHES_OFF(d_cache, i_cache); + (*_flash_query)(data); + HAL_FLASH_CACHES_ON(d_cache, i_cache); +} + +static const CYG_FLASH_FUNS(cyg_legacy_funs, + legacy_flash_init, + legacy_flash_query, + legacy_flash_erase_block, + legacy_flash_program, + LEGACY_FLASH_READ, + legacy_flash_block_lock, + legacy_flash_block_unlock + ); + +CYG_FLASH_DRIVER(cyg_zzlegacy_flashdev, + &cyg_legacy_funs, + 0, // Flags + 0, // Start address, filled in by init + 0, // End address, filled in by init + 0, // Number of block infos, filled in by init + NULL, // Block infos, again filled in by init + NULL // Driver private data, none needed + ); diff --git a/packages/io/flash/current/tests/flash1.c b/packages/io/flash/current/tests/flash1.c --- a/packages/io/flash/current/tests/flash1.c +++ b/packages/io/flash/current/tests/flash1.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) 2004 Gary Thomas +// Copyright (C) 2004, 2005 eCosCentric Limited // // 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 @@ -61,6 +62,14 @@ #include #include +#ifndef CYGSEM_IO_FLASH_LEGACY_API +externC void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA("Only usable with legacy API"); +} +#else externC void cyg_start( void ) { @@ -76,6 +85,8 @@ cyg_start( void ) int i, len; cyg_bool passed, ok; + CYG_TEST_INIT(); + #if 0 int j; @@ -124,16 +135,27 @@ cyg_start( void ) diag_printf("... Using test buffers at %p and %p\n", (void *)test_buf1, (void *)test_buf2); flash_test_start = (void *)(flash_start + CYGNUM_IO_FLASH_TEST_OFFSET); +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + // Unlock test + diag_printf("... Unlock test\n"); + ok = true; + if ((stat = flash_unlock(flash_test_start, + CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != CYG_FLASH_ERR_OK) { + diag_printf("FLASH: unlock failed: %s\n", flash_errmsg(stat)); + ok = false; + } +#endif + // Erase test diag_printf("... Erase test\n"); ok = true; if ((stat = flash_erase(flash_test_start, - CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != FLASH_ERR_OK) { + CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != CYG_FLASH_ERR_OK) { diag_printf("FLASH: erase failed: %s\n", flash_errmsg(stat)); ok = false; } if (ok && (stat = flash_read(flash_test_start, (void *)test_buf1, - CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != FLASH_ERR_OK) { + CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != CYG_FLASH_ERR_OK) { diag_printf("FLASH: read/verify after erase failed: %s\n", flash_errmsg(stat)); ok = false; } @@ -150,7 +172,7 @@ cyg_start( void ) len = CYGNUM_IO_FLASH_TEST_LENGTH; flash_addr = flash_test_start; while (len > 0) { - if ((stat = flash_read(flash_addr, (void *)test_buf1, 0x200, &err_addr)) != FLASH_ERR_OK) { + if ((stat = flash_read(flash_addr, (void *)test_buf1, 0x200, &err_addr)) != CYG_FLASH_ERR_OK) { diag_printf("FLASH: read[short]/verify after erase failed: %s\n", flash_errmsg(stat)); ok = false; break; @@ -184,12 +206,12 @@ cyg_start( void ) } ok = true; if (ok && (stat = flash_program(flash_test_start, (void *)test_buf1, - CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != FLASH_ERR_OK) { + CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != CYG_FLASH_ERR_OK) { diag_printf("FLASH: write failed: %s\n", flash_errmsg(stat)); ok = false; } if (ok && (stat = flash_read(flash_test_start, (void *)test_buf2, - CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != FLASH_ERR_OK) { + CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != CYG_FLASH_ERR_OK) { diag_printf("FLASH: read/verify after write failed: %s\n", flash_errmsg(stat)); ok = false; } @@ -209,7 +231,7 @@ cyg_start( void ) lp1 = (cyg_uint32 *)test_buf1; lp2 = (cyg_uint32 *)test_buf2; while (len > 0) { - if ((stat = flash_read(flash_addr, lp2, 0x200, &err_addr)) != FLASH_ERR_OK) { + if ((stat = flash_read(flash_addr, lp2, 0x200, &err_addr)) != CYG_FLASH_ERR_OK) { diag_printf("FLASH: read[short]/verify after erase failed: %s\n", flash_errmsg(stat)); ok = false; break; @@ -227,16 +249,28 @@ cyg_start( void ) } } } - + if (!ok) { CYG_TEST_INFO("FLASH write/verify failed"); } +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + // Lock test + diag_printf("... Lock test\n"); + ok = true; + if ((stat = flash_lock(flash_test_start, + CYGNUM_IO_FLASH_TEST_LENGTH, &err_addr)) != CYG_FLASH_ERR_OK) { + diag_printf("FLASH: unlock failed: %s\n", flash_errmsg(stat)); + ok = false; + } +#endif + if (passed) { CYG_TEST_PASS_FINISH("FLASH test1"); } else { CYG_TEST_FAIL_FINISH("FLASH test1"); } } +#endif // EOF flash1.c diff --git a/packages/io/flash/current/tests/flashdev.c b/packages/io/flash/current/tests/flashdev.c new file mode 100644 --- /dev/null +++ b/packages/io/flash/current/tests/flashdev.c @@ -0,0 +1,328 @@ +//================================================================= +// +// flashdev.c +// +// Simple tests for FLASHdev driver +// +//================================================================= +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2004 Gary Thomas +// Copyright (C) 2004, 2005, 2006, 2008 eCosCentric Limited +// +// 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., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg, gthomas +// Contributors: +// Date: 2005-10-25 +// Description: Simple test of FLASH I/O device. This is really +// just a copy of the flash1 test, but using the +// /dev/flash interface instead of the flash library. +// +// Options: +//####DESCRIPTIONEND#### +//================================================================= + +// #define DEBUG_PRINTFS + +#include +#include +#include CYGHWR_MEMORY_LAYOUT_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +//================================================================= +// Config options. + +#define FLASH_TEST_OFFSET 0 +#define FLASH_TEST_LENGTH 0x40000 + +//================================================================= +// A FIS entry named "flashtest" of at least FLASH_TEST_LENGTH bytes +// must exist for this test to work. It can be created with the +// following command: +// +// RedBoot> fis cre -b %{freememlo} -l 0x100000 flashtest + +#define FLASH_TEST_DEVICE "/dev/flash/fis/flashtest" + +// If it does not exist, a FIS entry named "jffs2test" will be used +// if present, as this may have been set up for jffs2 testing. +#define FLASH_TEST_DEVICE2 "/dev/flash/fis/jffs2test" + +//================================================================= + +#if !defined(CYGPKG_IO_FLASH_BLOCK_DEVICE) +externC void +cyg_start( void ) +{ + CYG_TEST_INIT(); + CYG_TEST_NA("Only usable with flash block device driver"); +} +#else + +//================================================================= + +externC void +cyg_start( void ) +{ + Cyg_ErrNo stat; + cyg_io_handle_t flash_handle; + cyg_io_flash_getconfig_erase_t e; + cyg_io_flash_getconfig_devsize_t d; + cyg_io_flash_getconfig_blocksize_t b; + cyg_io_flash_getconfig_lock_t l; + cyg_io_flash_getconfig_unlock_t u; + CYG_ADDRWORD flash_start, flash_end; + CYG_ADDRWORD flash_test_start, flash_addr; + cyg_uint32 flash_block_size, flash_num_blocks; + CYG_ADDRWORD test_buf1, test_buf2; + cyg_uint32 *lp1, *lp2; + int i, len; + cyg_bool passed, ok; + + CYG_TEST_INIT(); + + passed = true; + + if ((stat = cyg_io_lookup(FLASH_TEST_DEVICE, &flash_handle)) == -ENOENT) { + stat = cyg_io_lookup(FLASH_TEST_DEVICE2, &flash_handle); + } + + if (stat != 0) { + diag_printf("FLASH: driver init failed: %s\n", strerror(-stat)); + CYG_TEST_FAIL_FINISH("FLASH driver init failed"); + } + + len = sizeof(d); + stat = cyg_io_get_config( flash_handle, CYG_IO_GET_CONFIG_FLASH_DEVSIZE, &d, &len ); + flash_start = 0; + // Keep 'end' address as last valid location, to avoid wrap around problems + flash_end = d.dev_size - 1; + + len = sizeof(b); + b.offset = 0; + stat = cyg_io_get_config( flash_handle, CYG_IO_GET_CONFIG_FLASH_BLOCKSIZE, &b, &len ); + flash_block_size = b.block_size; + flash_num_blocks = d.dev_size/flash_block_size; + + diag_printf("FLASH: %p - %p, %d blocks of 0x%x bytes each.\n", + (void*)flash_start, (void*)(flash_end + 1), flash_num_blocks, flash_block_size); + + // Verify that the testing limits are within the bounds of the + // physical device. Also verify that the size matches with + // the erase block size on the device + if ((FLASH_TEST_OFFSET > (flash_end - flash_start)) || + ((FLASH_TEST_OFFSET + FLASH_TEST_LENGTH) > (flash_end - flash_start))) { + CYG_TEST_FAIL_FINISH("FLASH test region outside physical limits"); + } + if ((FLASH_TEST_LENGTH % flash_block_size) != 0) { + CYG_TEST_FAIL_FINISH("FLASH test region must be integral multiple of erase block size"); + } + + // Allocate two buffers large enough for the test + test_buf1 = (CYG_ADDRWORD)CYGMEM_SECTION_heap1; + test_buf2 = test_buf1 + FLASH_TEST_LENGTH; + if (CYGMEM_SECTION_heap1_SIZE < (FLASH_TEST_LENGTH * 2)) { + CYG_TEST_FAIL_FINISH("FLASH not enough heap space - reduce size of test region"); + } + diag_printf("... Using test buffers at %p and %p\n", (void *)test_buf1, (void *)test_buf2); + flash_test_start = flash_start + FLASH_TEST_OFFSET; + + +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + // Unlock test + diag_printf("... Unlock test\n"); + ok = true; + u.offset = flash_test_start; + u.len = FLASH_TEST_LENGTH; + len = sizeof(u); + if ((stat = cyg_io_get_config(flash_handle, CYG_IO_GET_CONFIG_FLASH_UNLOCK, &u, &len ) ) != 0 || u.flasherr != 0) + { + diag_printf("FLASH: unlock failed: %s %s\n", strerror(stat), cyg_flash_errmsg(u.flasherr)); + ok = false; + } +#endif + + // Erase test + diag_printf("... Erase test\n"); + ok = true; + + e.offset = flash_test_start; + e.len = FLASH_TEST_LENGTH; + len = sizeof(e); + if ((stat = cyg_io_get_config(flash_handle, CYG_IO_GET_CONFIG_FLASH_ERASE, &e, &len ) ) != 0 || e.flasherr != 0) + { + diag_printf("FLASH: erase failed: %s %s\n", cyg_flash_errmsg(stat),cyg_flash_errmsg(e.flasherr)); + ok = false; + } + len = FLASH_TEST_LENGTH; + if (ok && (stat = cyg_io_bread(flash_handle, (void *)test_buf1, &len, flash_test_start)) != 0) + { + diag_printf("FLASH: read/verify after erase failed: %s\n", cyg_flash_errmsg(stat)); + ok = false; + } + lp1 = (cyg_uint32 *)test_buf1; + for (i = 0; i < FLASH_TEST_LENGTH; i += sizeof(cyg_uint32)) { + if (*lp1++ != 0xFFFFFFFF) { + diag_printf("FLASH: non-erased data found at offset %p\n", (void*)((CYG_ADDRWORD)(lp1-1) - test_buf1)); + diag_dump_buf((void *)(lp1-1), 32); + ok = false; + break; + } + } + + // Try reading in little pieces + len = FLASH_TEST_LENGTH; + flash_addr = flash_test_start; + while (len > 0) { + cyg_uint32 l = 0x200; + if ((stat = cyg_io_bread(flash_handle, (void *)test_buf1, &l, flash_addr)) != CYG_FLASH_ERR_OK) { + diag_printf("FLASH: read[short]/verify after erase failed: %s\n", strerror(stat)); + ok = false; + break; + } + flash_addr = flash_addr + l; + len -= l; + lp1 = (cyg_uint32 *)test_buf1; + for (i = 0; i < 0x200; i += sizeof(cyg_uint32)) { + if (*lp1++ != 0xFFFFFFFF) { + diag_printf("FLASH: non-erased data found at offset %p\n", + (cyg_uint8 *)flash_addr + (CYG_ADDRWORD)((lp1-1) - test_buf1)); + diag_dump_buf((void *)(lp1-1), 32); + ok = false; + len = 0; + break; + } + } + } + + if (!ok) { + CYG_TEST_INFO("FLASH erase failed"); + passed = false; + } + + // Simple write/verify test + diag_printf("... Write/verify test\n"); + lp1 = (cyg_uint32 *)test_buf1; + for (i = 0; i < FLASH_TEST_LENGTH; i += sizeof(cyg_uint32)) { + *lp1 = (cyg_uint32)lp1; + lp1++; + } + ok = true; + len = FLASH_TEST_LENGTH; + if (ok && (stat = cyg_io_bwrite(flash_handle, (void *)test_buf1, + &len, flash_test_start)) != 0) { + diag_printf("FLASH: write failed: %s\n", strerror(stat)); + ok = false; + } + + + len = FLASH_TEST_LENGTH; + if (ok && (stat = cyg_io_bread(flash_handle, (void *)test_buf2, &len, flash_test_start)) != CYG_FLASH_ERR_OK) { + diag_printf("FLASH: read/verify after write failed: %s\n", strerror(stat)); + ok = false; + } + lp1 = (cyg_uint32 *)test_buf1; + lp2 = (cyg_uint32 *)test_buf2; + for (i = 0; i < FLASH_TEST_LENGTH; i += sizeof(cyg_uint32)) { + if (*lp2++ != *lp1++) { + diag_printf("FLASH: incorrect data found at offset %p\n", (void *)((CYG_ADDRWORD)(lp2-1) - test_buf2)); + diag_dump_buf((void *)(lp2-1), 32); + ok = false; + break; + } + } + + // Try reading in little pieces + len = FLASH_TEST_LENGTH; + flash_addr = flash_test_start; + lp1 = (cyg_uint32 *)test_buf1; + lp2 = (cyg_uint32 *)test_buf2; + while (len > 0) { + cyg_uint32 l = 0x200; + if ((stat = cyg_io_bread(flash_handle, (void *)lp2, &l, flash_addr)) != 0) { + diag_printf("FLASH: read[short]/verify after erase failed: %s\n", strerror(stat)); + ok = false; + break; + } + flash_addr = flash_addr + l; + len -= l; + for (i = 0; i < l; i += sizeof(cyg_uint32)) { + if (*lp2++ != *lp1++) { + diag_printf("FLASH: incorrect data found at offset %p\n", + (cyg_uint8 *)flash_addr + (CYG_ADDRWORD)((lp2-1) - test_buf2)); + diag_dump_buf((void *)(lp2-1), 32); + ok = false; + len = 0; + break; + } + } + } + +#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING + // Lock test + diag_printf("... Lock test\n"); + ok = true; + l.offset = flash_test_start; + l.len = FLASH_TEST_LENGTH; + len = sizeof(l); + if ((stat = cyg_io_get_config(flash_handle, CYG_IO_GET_CONFIG_FLASH_LOCK, &l, &len ) ) != 0 || l.flasherr != 0 ) + { + diag_printf("FLASH: unlock failed: %s %s\n", strerror(stat), cyg_flash_errmsg(l.flasherr)); + ok = false; + } +#endif + + if (!ok) { + CYG_TEST_INFO("FLASH write/verify failed"); + } + + if (passed) { + CYG_TEST_PASS_FINISH("FLASH test1"); + } else { + CYG_TEST_FAIL_FINISH("FLASH test1"); + } +} +#endif + +//================================================================= +// EOF flashdev.c diff --git a/packages/redboot/current/ChangeLog b/packages/redboot/current/ChangeLog --- a/packages/redboot/current/ChangeLog +++ b/packages/redboot/current/ChangeLog @@ -1,3 +1,23 @@ +2008-11-16 Jonathan Larmour + + * cdl/redboot.cdl, src/flash.c, src/fconfig.c: + Flash-related eCosCentric changes to these files (including a + merge of the flashv2 work) have been merged. Changes have been + directly interleaved below on the correct dates. + +2008-11-14 Jonathan Larmour + + * src/flash.c (fis_lock): Revert patch of 2005-10-05 which disabled + locking entirely if FIS was disabled. Was better fixed by patch of + 2006-02-17. + (fis_list): Revert patch of 2007-04-03 to avoid skipping entries + at 0x0. Replaced by anoncvs patch of 2007-06-02 for consistency. + +2008-09-11 Nick Garnett + + * src/flash.c (fis_create, fis_write, fis_erase): Change alignment + test to cope with flash blocks that are not a power of 2 in size. + 2008-06-18 Bart Veer * src/fconfig.c, src/flash.c, src/io.c, src/load.c, src/main.c, @@ -22,6 +42,11 @@ 2008-05-20 Jonathan Larmour + + * src/flash.c (fis_create): Allow length to be inferred if same + mem address given as prior load address. + 2007-08-28 Gary Thomas * src/flash.c (do_flash_init): Memory allocation was slightly @@ -58,11 +83,28 @@ 2007-04-11 Jonathan Larmour + + * src/flash.c (fis_list): don't skip entries at location 0x0 + 2007-01-22 Peter Korsgaard * src/load.c (do_load): Reset entry address before load so go/exec commands will fail after an incomplete upload. +2006-12-18 John Dallaway + + * src/flash.c, src/fconfig.c: Eliminate some compiler warnings. + +2006-12-06 Jonathan Larmour + + * src/flash.c (find_free): When eliminating collapsed free chunks, + don't decrement num_chunks as a side effect every loop! + +2006-12-04 Bart Veer + + * src/flash.c, src/main.c: eliminate some compiler warnings. + 2006-11-28 David Fernandez * cdl/redboot.cdl: Modified to change the option @@ -84,6 +126,16 @@ 2006-11-19 Andrew Lunn + + * src/flash.c (fis_create): Ensure flash_addr is always inferred + to come from unused space if not set. + +2006-10-19 Jonathan Larmour + + * src/flash.c (fis_create): If not copying, no reason to insist + on a flash address being set. + 2006-09-06 Andrew Lunn * cdl/redboot.cdl: Fix description of CYGSEM_REDBOOT_DISK_IDE. @@ -98,6 +150,19 @@ 2006-07-21 David Ho + + * src/flash.c (flash_reserved): Value for the macro used when this + feature is disabled should be false not true. + +2006-06-09 Nick Garnett + + * cdl/redboot.cdl: + * src/flash.c (flash_reserved): Added config option, + CYGNUM_REDBOOT_FLASH_RESERVED_DEVICES, and code to check for a + list of reserved flash devices that should not be used for FIS + file allocation. + 2006-05-24 Gary Thomas * src/net/net_io.c (do_ip_addr): Bail out if no networking. @@ -129,6 +194,24 @@ 2006-03-27 Gary Thomas + + * src/flash.c (find_free): Fix typo in last change. + +2006-03-16 Jonathan Larmour + + * src/flash.c (find_free): Fix indentation. + Trivial optimisation when free chunk collapsed. + When splitting chunks, be sure to insert not overwrite - there may be + a subsequent free chunk. + Even if out of free chunks when splitting, be sure to mark end of this + chunk as used, to avoid it inappropriately being reported as free. + +2006-03-10 Bart Veer + + * src/flash.c (find_free): fix boundary condition when an fis img + is right at the end of memory. + 2006-02-25 Oliver Munz Andrew Lunn @@ -153,6 +236,32 @@ 2006-02-17 Andrew Lunn + + * src/flash.c (_flash_info): Tidy presentation of flash block info. + +2006-01-17 Jonathan Larmour + + * src/flash.c (fis_init): Default data_length = FIS partition + size for special partitions. + +2005-12-22 Jonathan Larmour + + * src/flash.c (find_free): adjust base only for appropriate device. + (fis_free): Fix up case for !CYGDAT_REDBOOT_FIS_MAX_FREE_CHUNKS + to correctly scan for blank regions. + Remove obsolete version of same. + (fis_find_free): Adapt fis_free changes for this. + +2005-12-12 Jonathan Larmour + + * src/flash.c (fis_free): Rewrite to improve support + for multiple flash devices. Still not really right - needs + wider changes. + (fis_find_free): Better support multiple flash devices. + Far from perfect yet though. Minor improvement on what + was there before though. + 2005-11-23 Peter Korsgaard * src/gunzip.c (do_gunzip): Fixed diag_printf format string warnings. @@ -161,6 +270,24 @@ 2005-10-17 Gary Thomas + + * src/flash.c: Changes to various ifdefs to allow FCONFIG to be + used independently of FIS. It was always intended that this be + possible, but over time it suffered a little bitrot. + +2005-09-30 Nick Garnett + + * cdl/redboot.cdl: Move CYGNUM_REDBOOT_FLASH_BASE out of + CYGPKG_REDBOOT_FLASH component so that it can be used from RBL + when CYGPKG_REDBOOT_FLASH is disabled. + +2005-09-26 Bart Veer + + * src/flash.c: put flag into .bss rather than .data, avoids + problems if soft reset involves branching to the entry point and + .data does not get reinitialized. + 2005-09-13 Jonathan Larmour * src/parse.c (redboot_exec): Conditionalise use of @@ -311,21 +438,35 @@ 2005-04-17 Andrew Lunn + + * src/flash.c (find_free): Correctly split chunks in two when + not final chunk. + 2005-04-07 Peter Korsgaard * cdl/redboot.cdl, doc/redboot_cmds.sgml, src/gunzip.c: Added gunzip command to uncompress GZIP compressed data. -2005-04-11 Peter Korsgaard - - * src/flash.c (find_free): Correctly split chunks in two when - not final chunk. - 2005-03-07 Alexander Neundorf * src/net/arp.c: use correct sizeof(rt->enet_addr) in __arp_lookup() +2005-02-17  Bart Veer   + + * src/flash.c (fis_create): if -r is not specified, set the ram + base address in the fis directory to the current load address. + This assumes the data is currently in the right place, as should + be the case after e.g. loading an elf executable. + +2005-02-11 Jonathan Larmour + + * src/flash.c (find_free): Correct fis free calculations to account + for chunk regions ending 1 byte before fis fis regions. + (fis_find_free): Allow for difference of end from start being one + less than size of region. + 2005-01-26 Nick Garnett * src/fs/fileio.c (do_mkdir, do_deldir, do_del, do_write): Added @@ -382,6 +523,56 @@ 2004-11-26 Nick Garnett + + * Merge from flash V2 branch + + 2004-11-21 Bart Veer + + * src/flash.c (check_code_overlaps): cyg_flash_code_overlaps() has + been removed, so use a RedBoot-specific function instead + + 2004-11-20 Bart Veer + + * src/flash.c (do_flash_init): info.end already holds the last + flash byte, no need to adjust. + + 2004-10-07 Andrew Lunn + + * src/flash.c (do_flash_init): Fix compiler warning about an + unused variable. + + 2004-10-06 Andrew Lunn + + * src/flash.c (do_flash_init): We need info independent of + CYGNUM_REDBOOT_FLASH_BASE being set or not. + + 2004-09-14 Andrew Lunn + + * cdl/redboot.cdl: Change CYGNUM_REDBOOT_FLASH_BASE to a booldata + so we don't always look at address 0 for the flash! + + 2004-08-21 Andrew Lunn + + * src/flash.c (do_flash_init): cyg_flash_get_limits has been removed. + Reimplement this functionality using other calls. + + 2004-08-13 Andrew Lunn + + * cdl/redboot.cdl: Fixed the type of CYGNUM_REDBOOT_FLASH_BASE + * src/flash.c (fis_[un]lock): Fix compiler warnings + + 2004-08-06 Andrew Lunn + + * src/flash.c (do_flash_init): Removed the MIPS br0ken condition + which i wrongly added. My problem was actually a / 0. This roundup + is needed otherwise the workspace goes off the end of the RAM. + + 2004-08-05 Andrew Lunn + + * src/flash.c: + * src/fconfig.c: Make use of the new flash API. + 2004-11-24 Nick Garnett * src/fs/fileio.c: Significantly reorganized to present an @@ -404,6 +595,11 @@ 2004-11-24 Nick Garnett + + * src/flash.c (find_free): allow for flash blocks larger than the + RedBoot MIN_IMAGE_SIZE + 2004-11-09 Ian Campbell * cdl/redboot.cdl, doc/redboot_cmds.sgml, src/iomem.c: Add support @@ -418,6 +614,14 @@ 2004-11-09 David Vrabel + + * src/flash.c (fis_create): Eliminate MIPS compiler workaround + which causes problems with multiple use of flash blocks and appears + to be unnecessary with GCC 3.2.1. + + * src/flash.c (fis_write): Eliminate MIPS compiler workaround. + 2004-11-04 Peter Korsgaard * src/fconfig.c (flash_write_config): Removed compiler warning @@ -526,6 +730,11 @@ 2004-06-25 Andrew Lunn + + * src/flash.c (fis_create): Made unlock/lock operations dependent + on CYGHWR_IO_FLASH_BLOCK_LOCKING. + 2004-05-31 Jani Monoses * src/main.c: @@ -541,6 +750,13 @@ 2004-05-21 Ian Campbell + + * src/flash.c (fis_create): Added unlock and lock operations + around programming of flash region. Some flash parts power up with + all locks on, so we have to unlock, and we might as well play safe + and keep everything locked. + 2004-05-05 Gary Thomas * src/flash.c (fis_load): CRC check on load was broken (the CDL @@ -3920,6 +4136,7 @@ 2000-07-12 Gary Thomas