Mercurial > flash_v2
changeset 1739:1773ef378c6f
Add new AM29xxxxx V2 flash driver
| author | bartv |
|---|---|
| date | Sat, 20 Nov 2004 18:09:53 +0000 |
| parents | 0801d20c7256 |
| children | 699be933fd5e |
| files | packages/ChangeLog packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c packages/ecos.db |
| diffstat | 8 files changed, 1909 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,7 @@ +2004-11-20 Bart Veer <bartv@ecoscentric.com> + + * ecos.db: Add new AM29xxxxxx driver which supports the V2 API + 2004-08-06 Andrew Lunn <andrew.lunn@ascom.ch> * ecos.db: Added the new strata driver which uses the V2 API.
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/ChangeLog @@ -0,0 +1,35 @@ +2004-11-05 Bart Veer <bartv@ecoscentric.com> + + * New AM29xxxxx flash driver created + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/cdl/flash_am29xxxxx_v2.cdl @@ -0,0 +1,141 @@ +# ==================================================================== +# +# 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 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" + parent CYGPKG_IO_FLASH + active_if CYGPKG_IO_FLASH + implements CYGHWR_IO_FLASH_DEVICE + implements CYGHWR_IO_FLASH_DEVICE_V2 + 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_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_option CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_TIMEOUT { + display "Maximum number of iterations during a block erase" + flavor data + default_value 100000000 + 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 CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT { + display "Maximum number of iterations during a write" + flavor data + default_value 10000000 + description " + The driver needs to poll the flash device when writing data + 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_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." + } + } +}
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/doc/am29xxxxx.sgml @@ -0,0 +1,723 @@ +<!-- DOCTYPE part PUBLIC "-//OASIS//DTD DocBook V3.1//EN" --> + +<!-- {{{ Banner --> + +<!-- =============================================================== --> +<!-- --> +<!-- am29xxxxx.sgml --> +<!-- --> +<!-- Documentation for the am29xxxxx flash device driver. --> +<!-- --> +<!-- =============================================================== --> +<!-- ####ECOSPRODOCCOPYRIGHTBEGIN#### --> +<!-- --> +<!-- This file is part of eCosPro(tm) --> +<!-- Copyright (C) 2004 eCosCentric Limited --> +<!-- Distribution of the work or derivative of the work in any --> +<!-- form is prohibited unless prior permission obtained from the --> +<!-- copyright holder --> +<!-- --> +<!-- ####ECOSPRODOCCOPYRIGHTEND#### --> +<!-- =============================================================== --> +<!-- #####DESCRIPTIONBEGIN#### --> +<!-- --> +<!-- Author(s): bartv --> +<!-- Date: 2004/11/05 --> +<!-- --> +<!-- ####DESCRIPTIONEND#### --> +<!-- =============================================================== --> + +<!-- }}} --> + +<part id="devs-flash-am29xxxxx"><title>AMD AM29xxxxx Flash Device Driver</title> + +<refentry id="am29xxxxx"> + <refmeta> + <refentrytitle>Overview</refentrytitle> + </refmeta> + <refnamediv> + <refname>Overview</refname> + <refpurpose>eCos Support for AMD AM29xxxxx Flash Devices and Compatibles</refpurpose> + </refnamediv> + + <refsect1 id="am29xxxxx-description"><title>Description</title> + <para> +The <varname>CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2</varname> AMD +AM29xxxxx V2 flash driver package implements support for the AM29xxxxx +family of flash devices and compatibles. The driver is not normally +accessed directly. Instead application code will use the API provided +by the generic flash driver package +<varname>CYGPKG_IO_FLASH</varname>, for example by calling functions +like <function>cyg_flash_program</function>. + </para> + <para> +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. + </para> + <note><para> +Many eCos targets with AM29xxxxx or compatible flash devices will +still use the older driver package +<varname>CYGPKG_DEVS_FLASH_AMD_AM29XXXXX</varname>. Only newer ports +and some older ports that have been converted will use the V2 driver. +This documentation only applies to the V2 driver. + </para></note> + </refsect1> + + <refsect1 id="am29xxxxx-config"><title>Configuration Options</title> + <para> +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 +<varname>CYGPKG_IO_FLASH</varname> 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. + </para> + <para> +The AM29xxxxx flash driver package contains a small 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 +<varname>CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS</varname> +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. + </para> + </refsect1> +</refentry> + +<refentry id="am29xxxxx-instance"> + <refmeta> + <refentrytitle>Instantiating an AM29xxxxx Device</refentrytitle> + </refmeta> + <refnamediv> + <refname>Instantiating</refname> + <refpurpose>including the driver in an eCos target</refpurpose> + </refnamediv> + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo> +#include <cyg/io/am29xxxxx_dev.h> + </funcsynopsisinfo> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_init_nop</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_init_check_devid_XX</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_init_cfi_XX</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_erase_XX</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + <paramdef>const cyg_flashaddr_t <parameter>addr</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_program_XX</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + <paramdef>const cyg_flashaddr_t <parameter>addr</parameter></paramdef> + <paramdef>const void* <parameter>data</parameter></paramdef> + <paramdef>const size_t <parameter>len</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_query_nop</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + <paramdef>void* <parameter>data</parameter></paramdef> + <paramdef>const size_t <parameter>len</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_hwr_map_error_nop</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + <paramdef>int <parameter>err</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_lock_nop</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + <paramdef>const cyg_flashaddr_t <parameter>addr</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_unlock_nop</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + <paramdef>const cyg_flashaddr_t <parameter>addr</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_am29xxxxx_read_devid_XX</function></funcdef> + <paramdef>struct cyg_flash_dev* <parameter>device</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1 id="am29xxxxx-instance-description"><title>Description</title> + <para> +The AM29xxxxx family contains some hundreds of different flash +devices, all supporting the same basic set of operations, 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 all this information. Instead it +is the responsibility of another package, usually the platform HAL, to +instantiate some flash device structures. Two pieces of information +are especially important: the bus configuration and the boot block +layout. + </para> + <para> +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 usually there will be a trade off between cost and performance. +For example two 16-bit devices in parallel can provide twice the +memory bandwidth of a single device. The driver supports the following +combinations: + </para> + <variablelist> + <varlistentry> + <term>8</term> + <listitem><para> +A single 8-bit flash device on an 8-bit bus. + </para></listitem> + </varlistentry> + <varlistentry> + <term>16</term> + <listitem><para> +A single 16-bit flash device on a 16-bit bus. + </para></listitem> + </varlistentry> + <varlistentry> + <term>32</term> + <listitem><para> +A single 32-bit flash device on an 32-bit bus. + </para></listitem> + </varlistentry> + <varlistentry> + <term>88</term> + <listitem><para> +Two parallel 8-bit devices on an 16-bit bus. + </para></listitem> + </varlistentry> + <varlistentry> + <term>8888</term> + <listitem><para> +Four parallel 8-bit devices on a 32-bit bus. + </para></listitem> + </varlistentry> + <varlistentry> + <term>1616</term> + <listitem><para> +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 upper two bytes. + </para></listitem> + </varlistentry> + <varlistentry> + <term>16as8</term> + <listitem><para> +A single 16-bit flash device connected to an 8-bit bus. + </para></listitem> + </varlistentry> + </variablelist> + <para> +These configuration all require slightly different code to manipulate +the hardware. The AM29xxxxx driver package provides separate functions +for each configuration, for example +<function>cyg_am29xxxxx_erase_16</function> and +<function>cyg_am29xxxxx_program_1616</function>. + </para> + <caution><para> +At the time of writing not all the configurations have been tested. + </para></caution> + <para> +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. + </para> + </refsect1> + + <refsect1 id="am29xxxxx-instance-example"><title>Example</title> + <para> +Flash support is usually specific to each 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 is generally 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: + </para> + <programlisting width=72> +cdl_package CYGPKG_HAL_M68K_ALAIA { + … + compile -library=libextras.a alaia_flash.c + … +} + </programlisting> + <para> +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 <filename>libextras.a</filename>. + </para> + <para> +The actual file <filename>alaia_flash.c</filename> will look something like: + </para> + <programlisting> +#include <pkgconf/system.h> +#ifdef CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2 + +#include <cyg/io/flash.h> +#include <cyg/io/flash_priv.h> +#include <cyg/io/am29xxxxx_dev.h> + +static const CYG_FLASH_FUNS(hal_alaia_flash_amd_funs, + &cyg_am29xxxxx_init_check_devid_16, + &cyg_am29xxxxx_query_nop, + &cyg_am29xxxxx_erase_16, + &cyg_am29xxxxx_program_16, + (int (*)(struct cyg_flash_dev*, const cyg_flashaddr_t, void*, const size_t))0, + &cyg_am29xxxxx_hwr_map_error_nop, + &cyg_am29xxxxx_lock_nop, + &cyg_am29xxxxx_unlock_nop); + +static const cyg_am29xxxxx_dev hal_alaia_flash_priv = { + .devid = 0x45, + .block_info = { + { 0x00004000, 1 }, + { 0x00002000, 2 }, + { 0x00008000, 1 }, + { 0x00010000, 63 } + } +}; + +struct cyg_flash_dev hal_alaia_flash CYG_HAL_TABLE_ENTRY(cyg_flashdev) = { + .funs = &hal_alaia_flash_amd_funs, + .start = 0xFFE00000, + .end = 0xFFFFFFFF, + .num_block_infos = 4, + .block_info = hal_alaia_flash_priv.block_info, + .priv = &hal_alaia_flash_priv +}; +#endif + </programlisting> + <para> +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 there is no point in instantiating the device. The rest +of the file is split into three 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 <filename +class="headerfile">cyg/io/flash_priv.h</filename>. The +relevant ones have an <literal>_16</literal> suffix, indicating that +on this board there is a single 16-bit flash device on a 16-bit +bus. The second definition provides information specific to AM29xxxxx +flash devices. The third provides the +<structname>cyg_flash_dev</structname> structure needed by the generic +flash code, which contains pointers to the previous two. + </para> + </refsect1> + + <refsect1 id="am29xxxxx-instance-functions"><title>Functions</title> + <para> +All eCos flash device drivers must implement a standard interface, +defined by the generic flash code <varname>CYGPKG_IO_FLASH</varname>. +This interface includes a table of 8 function pointers for various +operations: initialization, query, erase, program, read, error code +handling, locking and unlocking. The query operation is optional and +the AM29xxxxx driver only provides a dummy implementation +<function>cyg_am29xxxxx_query_nop</function>. AM29xxxxx flash devices +are always directly accessible so there is no need for a separate read +function. Standard flash error codes are used so only a dummy +<function>cyg_am29xxxxx_hwr_map_error_nop</function> function is +needed. The remaining functions are more complicated. + </para> + <para> +Usually the table can be declared <literal>const</literal>. In a ROM +startup application this avoids both ROM and RAM copies of the table, +saving a small amount of memory. <literal>const</literal> should not +be used if the table may be modified by a platform-specific +initialization routine. + </para> + + <refsect2 id="am29xxxxx-instance-functions-init"><title>Initialization</title> + <para> +There is a choice of three main initialization functions. The simplest +is <function>cyg_am29xxxxx_init_nop</function>, which does nothing. It +can be used if the <structname>cyg_am29xxxxx_dev</structname> and +<structname>cyg_flash_dev</structname> 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. + </para> + <para> +The next step up is +<function>cyg_am29xxxxx_init_check_devid_XX</function>, where +<literal>XX</literal> will be replaced by the suffix appropriate for the +bus configuration. It is still necessary to provide all the device +information statically, including the <structfield>devid</structfield> +field in the <structname>cyg_am29xxxxx_dev</structname> structure. +However 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. + </para> + <para> +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 <function>cyg_am29xxxxx_init_cfi_XX</function> +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 +<structfield>block_info</structfield> field in the +<structname>cyg_am29xxxxx_dev</structname> structure and the +<structfield>end</structfield> and +<structfield>num_block_infos</structfield> fields in the +<structname>cyg_flash_dev</structname> structure will be filled in. +It is still necessary to supply the <structfield>start</structfield> +field statically since otherwise the driver will not know how to +access the flash device. The main disadvantage of using CFI is that it +will increase the code size. + </para> + <para> +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, <function>cyg_am29xxxxx_read_devid_XX</function>: + </para> + <programlisting width=72> +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; + } +} + </programlisting> + <para> +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 was fixed in the next revision. The +platform-specific initialization function could figure out which model +board it is running on and replace the default +<literal>16as8</literal> functions with <literal>16</literal> ones. + </para> + </refsect2> + + <refsect2 id="am29xxxxx-instance-functions-erase-program"><title>Erase and Program</title> + <para> +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. For +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: + </para> + <programlisting width=72> +static int +alaia_flash_erase(struct cyg_flash_dev* dev, const 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; +} + </programlisting> + <para> +There are two configurations which affect the erase and program +functions, and which a platform HAL may wish to change: +<varname>CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_TIMEOUT</varname> +and +<varname>CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT</varname>. +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, and hence a timeout probably 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. + </para> + </refsect2> + + <refsect2 id="am29xxxxx-instance-functions-locking"><title>Locking</title> + <para> +There is no single way of implementing the block lock and unlock +operations on AM29xxxxx devices. If these operations are supported at +all then usually they involve manipulating the voltages on certain +pins. This cannot be handled by generic driver code since it requires +knowing exactly how these pins can be manipulated via the processor's +GPIO pins. Therefore the AM29xxxxx driver does not provide functional +lock and unlock functions, only dummy functions +<function>cyg_am29xxxxx_lock_nop</function> and +<function>cyg_am29xxxxx_unlock_nop</function>. If there is a way of +implementing the locking then this can be handled by platform-specific +functions. + </para> + <programlisting width=72> +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) +{ + … +} + </programlisting> + </refsect2> + + <refsect2 id="am29xxxxx-instance-functions-other"><title>Other</title> + <para> +The driver provides a set of functions +<function>cyg_am29xxxxx_read_devid_XX</function>, one per supported +bus configuration. These functions take a single argument, a pointer +to the <structname>cyg_flash_dev</structname> 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. +<function>cyg_am29xxxxx_read_devid_XX</function> 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. + </para> + </refsect2> + </refsect1> + + <refsect1 id="am29xxxxx-instance-devpriv"><title>Device-Specific Structure</title> + <para> +The <structname>cyg_am29xxxxx_dev</structname> structure provides +information specific to AM29xxxxx flash devices, as opposed to the +more generic flash information which goes into the +<structname>cyg_flash_dev</structname> structure. There are only two +fields: <structfield>devid</structfield> and +<structfield>block_info</structfield>. + </para> + <para> +<structfield>devid</structfield> is only needed if the driver's +initialization function is set to +<function>cyg_am29xxxxx_init_check_devid_XX</function>. That function +will extract the actual device info from the flash chip and compare it +with the <structfield>devid</structfield> field. If there is a +mismatch then subsequent operations on the device will fail. + </para> + <para> +The <structfield>block_info</structfield> 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: + </para> + <orderedlist> + <listitem><para> +If the driver initialization function is set to +<function>cyg_am29xxxxx_init_nop</function> or +<function>cyg_am29xxxxx_init_check_devid_XX</function> then the block +information should be provided statically. This is appropriate if the +board will also be manufactured using the same flash chip. + </para></listitem> + <listitem><para> +If <function>cyg_am29xxxxx_init_cfi_XX</function> is used then this +will fill in the block info table. Hence there is no need for static +initialization. + </para></listitem> + <listitem><para> +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. + </para></listitem> + </orderedlist> + <para> +The size of the <structfield>block_info</structfield> table is +determined by the configuration option +<varname>CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS</varname>. +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. + </para> + <para> +If the <structname>cyg_am29xxxxx_dev</structname> structure is +statically initialized then it can be <literal>const</literal>. This +saves a small amount of memory in ROM startup applications. If the +structure may be updated at run-time, either by +<function>cyg_am29xxxxx_init_cfi_XX</function> or by a +platform-specific initialization routine, then it cannot be +<literal>const</literal>. + </para> + </refsect1> + + <refsect1 id="am29xxxxx-instance-flash"><title>Flash Structure</title> + <para> +Internally the flash code works in terms of +<structname>cyg_flash_dev</structname> structures, and the platform +HAL should define one of these. The structure should be placed in the +<literal>cyg_flashdev</literal> table. The following fields need to be +provided: + </para> + <variablelist> + <varlistentry> + <term><structfield>funs</structfield></term> + <listitem><para> +This should point at the table of functions. + </para></listitem> + </varlistentry> + <varlistentry> + <term><structfield>start</structfield></term> + <listitem><para> +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 <structfield>start</structfield> field should correspond to +the cached address. + </para></listitem> + </varlistentry> + <varlistentry> + <term><structfield>end</structfield></term> + <listitem><para> +The address of the last byte in the flash. It can +either be statically initialized, or +<function>cyg_am29xxxxx_init_cfi_XX</function> will calculate +its value at run-time. + </para></listitem> + </varlistentry> + <varlistentry> + <term><structfield>num_block_infos</structfield></term> + <listitem><para> +This should be the number of entries in the +<structfield>block_info</structfield> table. It can either be +statically initialized or it will be filled in by +<function>cyg_am29xxxxx_init_cfi_XX</function>. + </para></listitem> + </varlistentry> + <varlistentry> + <term><structfield>block_info</structfield></term> + <listitem><para> +The table with the block information is held in the +<structname>cyg_am29xxxxx_dev</structname> structure, so this field +should just point into that structure. + </para></listitem> + </varlistentry> + <varlistentry> + <term><structfield>priv</structfield></term> + <listitem><para> +This field is reserved for use by the device driver. For the AM29xxxxx +driver it should point at the appropriate +<structname>cyg_am29xxxxx</structname> structure. + </para></listitem> + </varlistentry> + </variablelist> + <para> +The <structname>cyg_flash_dev</structname> 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 <literal>const</literal>. + </para> + </refsect1> + + <refsect1 id="am29xxxxx-instance-serial"><title>Multiple Devices</title> + <para> +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 +<structname>cyg_flash_dev</structname> structures. The table of +function pointers can usually be shared, as can the +<structname>cyg_am29xxxxx_dev</structname> structure. Another approach +is to define a single <structname>cyg_flash_dev</structname> +structure but with a larger <structfield>block_info</structfield> +table, covering the blocks in both banks of devices. The second +approach makes more efficient use of memory. + </para> + <para> +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 +<structname>cyg_flash_dev</structname> structures. + </para> + </refsect1> + + <refsect1 id="am29xxxxx-instance-platform"><title>Platform-Specific Macros</title> + <para> +The AM29xxxxx driver source code includes the header files +<filename class="headerfile">cyg/hal/hal_arch.h</filename> and +<filename class="headerfile">cyg/hal/hal_io.h</filename>, 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. + </para> + <para> +<function>HAL_AM29XXXXX_P2V</function> provides control over the +addresses used to manipulate the flash. For example the flash device +may be mirrored in the address space: if the top bit of the address is +clear then the memory access will go via the cache; if the top bit is +set then the access will bypass the cache. Ordinary read accesses +should go via the cache so the <structfield>start</structfield> field +in the <structname>cyg_flash_dev</structname> structure should have +the top bit clear. <function>HAL_AM29XXXXX_P2V</function> can then or +in the top bit, ensuring that the cache does not get in the way of +flash updates. + </para> + <programlisting width=72> +#define HAL_AM29XXXXX_P2V(_addr_) \ + ({ CYG_ADDRWORD new_addr = (CYG_ADDRWORD)(_addr_); \ + new_addr |= 0x80000000; \ + new_addr; }) + </programlisting> + </refsect1> + +</refentry> + +</part>
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/include/am29xxxxx_dev.h @@ -0,0 +1,115 @@ +#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 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 <pkgconf/devs_flash_amd_am29xxxxx_v2.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/io/flash.h> +#include <cyg/io/flash_priv.h> + +externC int cyg_am29xxxxx_init_nop(struct cyg_flash_dev*); +externC size_t cyg_am29xxxxx_query_nop(struct cyg_flash_dev*, void*, const size_t); +externC int cyg_am29xxxxx_hwr_map_error_nop(struct cyg_flash_dev*, int); +externC int cyg_am29xxxxx_lock_nop(struct cyg_flash_dev*, const cyg_flashaddr_t); +externC int cyg_am29xxxxx_unlock_nop(struct cyg_flash_dev*, const cyg_flashaddr_t); + +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*, const cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_16( struct cyg_flash_dev*, const cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_32( struct cyg_flash_dev*, const cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_88( struct cyg_flash_dev*, const cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_8888( struct cyg_flash_dev*, const cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_1616( struct cyg_flash_dev*, const cyg_flashaddr_t); +externC int cyg_am29xxxxx_erase_16as8( struct cyg_flash_dev*, const cyg_flashaddr_t); + +externC int cyg_am29xxxxx_program_8( struct cyg_flash_dev*, const cyg_flashaddr_t, const void*, const size_t); +externC int cyg_am29xxxxx_program_16( struct cyg_flash_dev*, const cyg_flashaddr_t, const void*, const size_t); +externC int cyg_am29xxxxx_program_32( struct cyg_flash_dev*, const cyg_flashaddr_t, const void*, const size_t); +externC int cyg_am29xxxxx_program_88( struct cyg_flash_dev*, const cyg_flashaddr_t, const void*, const size_t); +externC int cyg_am29xxxxx_program_8888( struct cyg_flash_dev*, const cyg_flashaddr_t, const void*, const size_t); +externC int cyg_am29xxxxx_program_1616( struct cyg_flash_dev*, const cyg_flashaddr_t, const void*, const size_t); +externC int cyg_am29xxxxx_program_16as8( struct cyg_flash_dev*, const cyg_flashaddr_t, const void*, const size_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_block_info_t block_info[CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_REGIONS]; +} cyg_am29xxxxx_dev; + +#endif // CYGONCE_DEVS_FLASH_AM29xxxxx_dev_V2_H
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx.c @@ -0,0 +1,384 @@ +//========================================================================== +// +// am29xxxxx.c +// +// Flash driver for the AMD family +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 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 <pkgconf/devs_flash_amd_am29xxxxx_v2.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/diag.h> +#include <cyg/io/flash.h> +#include <cyg/io/flash_priv.h> +#include <cyg/io/am29xxxxx_dev.h> +#include <cyg/hal/hal_arch.h> +#include <cyg/hal/hal_cache.h> +#include <cyg/hal/hal_io.h> + +// 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 (*hwr_map_error)(...) +// int (*block_lock)(...) +// int (*block_unlock)(...) +// +// The devices do not need any special initialization. However a given +// board may be manufactured with any one of several devices, which +// complicates things. The main complication is that there may be +// different bootsector layouts. The primary job of the init function +// is to check the device id, possibly fill in the bootsector info, +// or even to use the CFI support to get the bootsector info from the +// device itself. There may be other complications, e.g. minor variations +// of a given board design. These can be handled by h/w specific init +// functions in the platform HAL. +// +// The query function need not do anything useful, it is +// driver-defined. +// +// No read function need be supplied because the flash memory is +// always directly accessible to the cpu. +// +// The hwr_map_error is a no-op. +// +// Erase, program, and the locking functions need real +// implementations. + +// ---------------------------------------------------------------------------- +// The protocol understood by 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. +#define AM29_COMMAND_SETUP1 AM29_PARALLEL(0x00AA) +#define AM29_COMMAND_SETUP2 AM29_PARALLEL(0x0055) +#define AM29_COMMAND_RESET AM29_PARALLEL(0x00F0) +#define AM29_COMMAND_AUTOSELECT AM29_PARALLEL(0x0090) +#define AM29_COMMAND_ERASE AM29_PARALLEL(0x0080) +#define AM29_COMMAND_ERASE_SECTOR AM29_PARALLEL(0x0030) +#define AM29_COMMAND_CFI AM29_PARALLEL(0x0098) +#define AM29_COMMAND_PROGRAM AM29_PARALLEL(0x00A0) + +// 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_PARALLEL(0x0080) +#define AM29_STATUS_DQ6 AM29_PARALLEL(0x0040) +#define AM29_STATUS_DQ5 AM29_PARALLEL(0x0020) +#define AM29_STATUS_DQ4 AM29_PARALLEL(0x0010) +#define AM29_STATUS_DQ3 AM29_PARALLEL(0x0008) +#define AM29_STATUS_DQ2 AM29_PARALLEL(0x0004) +#define AM29_STATUS_DQ1 AM29_PARALLEL(0x0002) +#define AM29_STATUS_DQ0 AM29_PARALLEL(0x0001) + +// The addresses used for programming the flash may be different from +// the ones used to read the flash. For example the former may be in +// uncached memory while the latter are in cached memory. The platform +// HAL can provide a macro HAL_AM29XXXXX_P2V() to convert an address +// relative to the base in the flash dev structure to the address that +// should actually be manipulated. +# ifdef HAL_AM29XXXXX_P2V +# define AM29_P2V(_addr_) (volatile AM29_TYPE*)HAL_AM29XXXXX_P2V(_addr_) +# else +# define AM29_P2V(_addr_) (volatile AM29_TYPE*)(_addr_) +# endif + +// 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_uint16 _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_uint16 _result_; \ + _result_ = (_ptr_[0] << 24) | (_ptr_[1] << 16) | (_ptr_[2] << 8) | _ptr_[3]; \ + _ptr_ += 4; \ + _result_; }) + +#endif + +// ---------------------------------------------------------------------------- +// Generic code. + +// A dummy initialization routine, for platforms where everything is +// done statically and there is no need to check device ids or anything similar. +int +cyg_am29xxxxx_init_nop(struct cyg_flash_dev* dev) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + return CYG_FLASH_ERR_OK; +} + +// A dummy query routine. The implementation of this is specific to +// each device driver, and I choose to do as little as possible. +size_t +cyg_am29xxxxx_query_nop(struct cyg_flash_dev* dev, void* data, const size_t len) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + CYG_UNUSED_PARAM(void*, data); + CYG_UNUSED_PARAM(size_t, len); + return 0; +} + +// A dummy hwr_map_error routine. +int +cyg_am29xxxxx_hwr_map_error_nop(struct cyg_flash_dev* dev, int err) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + return err; +} + +// Dummy lock/unlock routines +int +cyg_am29xxxxx_lock_nop(struct cyg_flash_dev* dev, const cyg_flashaddr_t addr) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + CYG_UNUSED_PARAM(cyg_flashaddr_t, addr); + return CYG_FLASH_ERR_DRV_WRONG_PART; +} + +int +cyg_am29xxxxx_unlock_nop(struct cyg_flash_dev* dev, const cyg_flashaddr_t addr) +{ + CYG_UNUSED_PARAM(struct cyg_flash_dev*, dev); + CYG_UNUSED_PARAM(cyg_flashaddr_t, addr); + return CYG_FLASH_ERR_DRV_WRONG_PART; +} + +// On some architectures there are problems calling the .2ram +// functions from the main ones. Specifically the compiler may issue a +// short call, even though the flash and ram are too far apart. The +// solution is to indirect via a function pointer, but the simplistic +// approach is vulnerable to compiler optimization. Hence the function +// pointer is passed through an anonymizer. Even this may fail in +// future if the compiler starts doing global optimization. +static void* +am29_anonymizer(void* fn) +{ + return fn; +} + +// 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_DEVID 0x0002 +#define AM29_OFFSET_DEVID2 0x001C +#define AM29_OFFSET_DEVID3 0x001E +#define AM29_OFFSET_CFI 0x00AA +#define AM29_OFFSET_CFI_DATA(_idx_) (2 * _idx_) + +#include "am29xxxxx_aux.c"
new file mode 100644 --- /dev/null +++ b/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c @@ -0,0 +1,497 @@ +//========================================================================== +// +// 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 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_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 + +// For parallel operation commands are issued in parallel and status +// bits are checked in parallel. +#ifndef AM29_PARALLEL +# define AM29_PARALLEL(_cmd_) (_cmd_) +#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 void AM29_RAMFNDECL(am29_hw_erase, (volatile AM29_TYPE*)); +static void AM29_RAMFNDECL(am29_hw_program, (volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32 count)); + +// 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; + + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_AUTOSELECT; + devid = addr[AM29_OFFSET_DEVID] & 0x00FF; + + // 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. + if (0x007E == devid) { + devid <<= 16; + devid |= ((addr[AM29_OFFSET_DEVID2] & 0x00FF) << 8); + devid |= (addr[AM29_OFFSET_DEVID3] & 0x00FF); + } + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + 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; + + // Just a single write is needed to put the device into CFI mode + addr[AM29_OFFSET_CFI] = AM29_COMMAND_CFI; + // 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' != (addr[AM29_OFFSET_CFI_Q] & 0x00FF)) { + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + 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 << (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 = 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; + 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 = addr[AM29_OFFSET_CFI_BLOCK_COUNT_LSB(i)] & 0x00FF; + cyg_uint32 count_msb = addr[AM29_OFFSET_CFI_BLOCK_COUNT_MSB(i)] & 0x00FF; + cyg_uint32 size_lsb = addr[AM29_OFFSET_CFI_BLOCK_SIZE_LSB(i)] & 0x00FF; + cyg_uint32 size_msb = 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; + } + + // Get out of CFI mode + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + + 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 void +AM29_FNNAME(am29_hw_erase)(volatile AM29_TYPE* addr) +{ + int retries = CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_ERASE_TIMEOUT; + + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE; + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_SETUP1; + addr[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_ERASE_SECTOR; + // 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. + do { + AM29_TYPE datum1, datum2; + datum1 = addr[AM29_OFFSET_COMMAND]; + datum2 = addr[AM29_OFFSET_COMMAND]; + if ((datum1 & AM29_STATUS_DQ6) == (datum2 & AM29_STATUS_DQ6)) { + // The bits have stopped toggling, so finished. + break; + } + if (0 != ((datum1 | datum2) & AM29_STATUS_DQ5)) { + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + break; + } + } while (retries-- > 0); + + // The calling code will verify that the erase was successful, + // and generate an error code. +} + +// 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 void +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; + + for (i = 0; i < count; i++) { + AM29_TYPE datum; + AM29_TYPE current, masked_datum; + + // 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; + block_start[AM29_OFFSET_COMMAND2] = AM29_COMMAND_SETUP2; + block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_PROGRAM; + addr[i] = datum; + + // The data is now being written. While the write is in progress + // DQ7 will have an inverted value from what was written, so we + // can poll, comparing just this bit. Again, if DQ5 is set then + // an error has occurred. + masked_datum = datum & AM29_STATUS_DQ7; + retries = CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT; + do { + current = addr[i]; + if ((current & AM29_STATUS_DQ7) == masked_datum) { + break; + } + if (0 != (current & AM29_STATUS_DQ5)) { + current = addr[i]; + if (current == datum) { + // Race condition, but the operation did succeed. + break; + } else { + // A timeout has occurred inside the hardware and + // the system is in a strange state. Reset but don't + // try to write any more of the data. + block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + return; + } + } + } while (retries-- > 0); + + if (0 == retries) { + // Failed to write this word, no point in trying to write the rest. + return; + } + } +} + +// FIXME: implement a separate program routine for buffered writes. + +// ---------------------------------------------------------------------------- +// 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; + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + + addr = AM29_P2V(dev->start); + query_fn = (int (*)(volatile AM29_TYPE*)) am29_anonymizer( & AM29_FNNAME(am29_hw_query) ); + devid = (*query_fn)(addr); + 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; + + 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; + } + // 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; + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + am29_dev = (cyg_am29xxxxx_dev*) dev->priv; + addr = AM29_P2V(dev->start); + cfi_fn = (int (*)(struct cyg_flash_dev*, cyg_am29xxxxx_dev*, volatile AM29_TYPE*)) + am29_anonymizer( & AM29_FNNAME(am29_hw_cfi)); + + result = (*cfi_fn)(dev, am29_dev, addr); + + // 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++) { + size += (dev->block_info[i].block_size * dev->block_info[i].blocks); + } + dev->end = dev->start + size - 1; + } + 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, const cyg_flashaddr_t addr) +{ + void (*erase_fn)(volatile AM29_TYPE*); + volatile AM29_TYPE* block; + cyg_flashaddr_t block_start; + size_t block_size; + int i; + + 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"); + + block = AM29_P2V(addr); + erase_fn = (void (*)(volatile AM29_TYPE*)) am29_anonymizer( & AM29_FNNAME(am29_hw_erase) ); + (*erase_fn)(block); + + // 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, const cyg_flashaddr_t dest, const void* src, const size_t len) +{ + void (*program_fn)(volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32); + volatile AM29_TYPE* block; + volatile AM29_TYPE* addr; + cyg_flashaddr_t block_start; + size_t block_size; + const cyg_uint8* data; + int i; + + CYG_CHECK_DATA_PTR(dev, "valid flash device pointer required"); + CYG_ASSERT((dest >= dev->start) && (addr <= dev->end), "flash address out of device range"); + + // 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; + } + + 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_P2V(block_start); + addr = AM29_P2V(dest); + data = (const cyg_uint8*) src; + + program_fn = (void (*)(volatile AM29_TYPE*, volatile AM29_TYPE*, const cyg_uint8*, cyg_uint32)) + am29_anonymizer( & AM29_FNNAME(am29_hw_program) ); + (*program_fn)(block, addr, (const cyg_uint8*)src, len / sizeof(AM29_TYPE)); + + // 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; + 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) { + return CYG_FLASH_ERR_PROGRAM; + } + } + return CYG_FLASH_ERR_OK; +} + +// ---------------------------------------------------------------------------- +// 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_PARALLEL
--- a/packages/ecos.db +++ b/packages/ecos.db @@ -181,6 +181,16 @@ package CYGPKG_DEVS_FLASH_AMD_AM29XXXXX flash memory devices." } +package CYGPKG_DEVS_FLASH_AMD_AM29XXXXX_V2 { + alias { "Support for AMD AM29xxxxx flash memory" flash_amd_am29xxxxx_v2 } + directory devs/flash/amd/am29xxxxxv2 + script flash_am29xxxxx_v2.cdl + hardware + description " + This package contains hardware support for AMD AM29xxxxx + flash memory devices." +} + package CYGPKG_DEVS_FLASH_ATMEL_AT29CXXXX { alias { "Support for Atmel AT29Cxxxx flash memory" flash_atmel_at29cxxxx } directory devs/flash/atmel/at29cxxxx
