Index: packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog =================================================================== RCS file: packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog diff -N packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog 18 Nov 2008 00:57:48 -0000 1.2 @@ -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#### +//=========================================================================== Index: packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl =================================================================== RCS file: packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl diff -N packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl 18 Nov 2008 00:57:48 -0000 1.2 @@ -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." + } + } +} Index: packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml =================================================================== RCS file: packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml diff -N packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml 18 Nov 2008 00:57:48 -0000 1.2 @@ -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. + + + + Index: packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h =================================================================== RCS file: packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h diff -N packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h 18 Nov 2008 00:57:48 -0000 1.2 @@ -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 Index: packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c =================================================================== RCS file: packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c diff -N packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c 18 Nov 2008 00:57:49 -0000 1.2 @@ -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" Index: packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c =================================================================== RCS file: packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c diff -N packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c 18 Nov 2008 00:57:49 -0000 1.2 @@ -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