changeset 2768:49a028abd944

* cdl/m25pxx.cdl: * include/m25pxx.h: * src/m25pxx.c: * test/m25pxx_test.c: New package - SPI flash driver for M25Pxx devices and compatibles. Driver contributed by Chris Holgate. Includes a soak test which is not part of the default tests as it requires a specific configuration to be used - see test/m25pxx_test.c for details.
author nickg
date Thu, 05 Feb 2009 17:25:24 +0000
parents 1d8577f3a1b1
children ef659b3b6980
files packages/devs/flash/spi/m25pxx/current/ChangeLog packages/devs/flash/spi/m25pxx/current/cdl/flash_m25pxx.cdl packages/devs/flash/spi/m25pxx/current/include/m25pxx.h packages/devs/flash/spi/m25pxx/current/src/m25pxx.c packages/devs/flash/spi/m25pxx/current/test/m25pxx_test.c
diffstat 5 files changed, 1057 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/m25pxx/current/ChangeLog
@@ -0,0 +1,34 @@
+2009-02-05  Chris Holgate  <chris@zynaptic.com>
+
+	* cdl/m25pxx.cdl: 
+	* include/m25pxx.h: 
+	* src/m25pxx.c: 
+	* test/m25pxx_test.c:
+	New package - SPI flash driver for M25Pxx devices and compatibles. 
+	Driver contributed by Chris Holgate.  Includes a soak test which is
+	not part of the default tests as it requires a specific configuration
+	to be used - see test/m25pxx_test.c for details.
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####                                                
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+//
+// This program 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.                                                           
+//
+// This program 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 this program; if not, write to the                            
+// Free Software Foundation, Inc., 51 Franklin Street,                      
+// Fifth Floor, Boston, MA  02110-1301, USA.                                
+// -------------------------------------------                              
+// ####GPLCOPYRIGHTEND####                                                  
+//===========================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/m25pxx/current/cdl/flash_m25pxx.cdl
@@ -0,0 +1,77 @@
+##=============================================================================
+##
+##      flash_m25pxx.cdl
+##
+##      M25Pxx SPI flash driver configuration options.
+##
+##=============================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####                                            
+## -------------------------------------------                              
+## This file is part of eCos, the Embedded Configurable Operating System.   
+## Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+##
+## eCos is free software; you can redistribute it and/or modify it under    
+## the terms of the GNU General Public License as published by the Free     
+## Software Foundation; either version 2 or (at your option) any later      
+## version.                                                                 
+##
+## eCos is distributed in the hope that it will be useful, but WITHOUT      
+## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
+## FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
+## for more details.                                                        
+##
+## You should have received a copy of the GNU General Public License        
+## along with eCos; if not, write to the Free Software Foundation, Inc.,    
+## 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+##
+## As a special exception, if other files instantiate templates or use      
+## macros or inline functions from this file, or you compile this file      
+## and link it with other works to produce a work based on this file,       
+## this file does not by itself cause the resulting work to be covered by   
+## the GNU General Public License. However the source code for this file    
+## must still be made available in accordance with section (3) of the GNU   
+## General Public License v2.                                               
+##
+## This exception does not invalidate any other reasons why a work based    
+## on this file might be covered by the GNU General Public License.         
+## -------------------------------------------                              
+## ####ECOSGPLCOPYRIGHTEND####                                              
+##=============================================================================
+#######DESCRIPTIONBEGIN####
+##
+## Author(s):   Chris Holgate
+## Date:        2008-12-22
+## Purpose:     Configure M25Pxx SPI flash driver.
+##
+######DESCRIPTIONEND####
+##
+##=============================================================================
+
+cdl_package CYGPKG_DEVS_FLASH_SPI_M25PXX {
+    display	"M25PXX flash memory support"
+    parent	CYGPKG_IO_FLASH
+    active_if	CYGPKG_IO_FLASH CYGPKG_IO_SPI
+    implements	CYGHWR_IO_FLASH_DEVICE
+    implements  CYGHWR_IO_FLASH_INDIRECT_READS 
+    include_dir	cyg/io
+    compile	m25pxx.c
+
+    description "
+        Flash memory support for Numonyx M25Pxx SPI flash devices and 
+        compatibles.  This driver implements the V2 flash driver API"
+
+cdl_option CYGNUM_DEVS_FLASH_SPI_M25PXX_READ_BLOCK_SIZE {
+    display       "Maximum read block size"
+    description   "
+        In theory it is possible to read back the entire flash contents using
+        a single SPI transaction.  However, some SPI bus drivers have a maximum 
+        transaction size - for example transactions may be limited to the 
+        length of a DMA bounce buffer.  Setting this option to a non-zero value 
+        specifies the maximum SPI bus transfer size which will be used when 
+        reading back data.  Read requests for areas larger than this block size 
+        will automatically be split into a series of smaller SPI bus transactions.
+    "
+    flavor        data 
+    default_value 0
+}
+}
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/m25pxx/current/include/m25pxx.h
@@ -0,0 +1,78 @@
+#ifndef CYGONCE_DEVS_FLASH_SPI_M25PXX_H
+#define CYGONCE_DEVS_FLASH_SPI_M25PXX_H
+
+//=============================================================================
+//
+//      m25pxx.h
+//
+//      SPI flash driver for Numonyx M25Pxx devices and compatibles.
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under    
+// the terms of the GNU General Public License as published by the Free     
+// Software Foundation; either version 2 or (at your option) any later      
+// version.                                                                 
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT      
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
+// for more details.                                                        
+//
+// You should have received a copy of the GNU General Public License        
+// along with eCos; if not, write to the Free Software Foundation, Inc.,    
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+//
+// As a special exception, if other files instantiate templates or use      
+// macros or inline functions from this file, or you compile this file      
+// and link it with other works to produce a work based on this file,       
+// this file does not by itself cause the resulting work to be covered by   
+// the GNU General Public License. However the source code for this file    
+// must still be made available in accordance with section (3) of the GNU   
+// General Public License v2.                                               
+//
+// This exception does not invalidate any other reasons why a work based    
+// on this file might be covered by the GNU General Public License.         
+// -------------------------------------------                              
+// ####ECOSGPLCOPYRIGHTEND####                                              
+//=============================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   Chris Holgate
+// Date:        2008-12-22
+// Purpose:     Numonyx M25Pxx SPI flash driver implementation
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+// Required data structures.
+#include <cyg/io/flash_dev.h>
+
+// Exported handle on the driver function table.
+externC struct cyg_flash_dev_funs cyg_devs_flash_spi_m25pxx_funs;
+
+//-----------------------------------------------------------------------------
+// Macro used to generate a flash device object with the default M25PXX 
+// settings.  Even though the block info data structure is declared here, the 
+// details are not filled in until the device type is inferred during 
+// initialisation.  This also applies to the 'end' field which is calculated 
+// using the _start_ address and the inferred size of the device.
+// _name_   is the root name of the instantiated data structures.
+// _start_  is the base address of the device - for SPI based devices this can
+//          have an arbitrary value, since the device is not memory mapped.
+// _spidev_ is a pointer to a SPI device object of type cyg_spi_device.  This
+//          is not typechecked during compilation so be careful!
+
+#define CYG_DEVS_FLASH_SPI_M25PXX_DRIVER(_name_, _start_, _spidev_) \
+struct cyg_flash_block_info _name_ ##_block_info; \
+CYG_FLASH_DRIVER(_name_, &cyg_devs_flash_spi_m25pxx_funs, 0, \
+  _start_, _start_, 1, & _name_ ##_block_info, (void*) _spidev_)
+
+//=============================================================================
+
+#endif // CYGONCE_DEVS_FLASH_SPI_M25PXX_H
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/m25pxx/current/src/m25pxx.c
@@ -0,0 +1,473 @@
+//=============================================================================
+//
+//      m25pxx.c
+//
+//      SPI flash driver for Numonyx M25Pxx devices and compatibles.
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under    
+// the terms of the GNU General Public License as published by the Free     
+// Software Foundation; either version 2 or (at your option) any later      
+// version.                                                                 
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT      
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
+// for more details.                                                        
+//
+// You should have received a copy of the GNU General Public License        
+// along with eCos; if not, write to the Free Software Foundation, Inc.,    
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+//
+// As a special exception, if other files instantiate templates or use      
+// macros or inline functions from this file, or you compile this file      
+// and link it with other works to produce a work based on this file,       
+// this file does not by itself cause the resulting work to be covered by   
+// the GNU General Public License. However the source code for this file    
+// must still be made available in accordance with section (3) of the GNU   
+// General Public License v2.                                               
+//
+// This exception does not invalidate any other reasons why a work based    
+// on this file might be covered by the GNU General Public License.         
+// -------------------------------------------                              
+// ####ECOSGPLCOPYRIGHTEND####                                              
+//=============================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   Chris Holgate
+// Date:        2008-12-22
+// Purpose:     Numonyx M25Pxx SPI flash driver implementation
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+#include <cyg/io/spi.h>
+#include <cyg/io/flash.h>
+#include <cyg/io/flash_dev.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+
+#include <pkgconf/devs_flash_spi_m25pxx.h>
+
+#include <string.h>
+
+//-----------------------------------------------------------------------------
+// Enable polled SPI operation for non-kernel builds.
+
+#ifdef CYGPKG_KERNEL
+#define M25PXX_POLLED false
+
+#else
+#define M25PXX_POLLED true
+#endif
+
+//-----------------------------------------------------------------------------
+// Implement delay functions for kernel and non-kernel builds.  The kernel
+// build assumes that the API calls are made in the thread context.
+
+#ifdef CYGPKG_KERNEL
+#define M25PXX_DELAY_MS(_msdelay_) cyg_thread_delay (\
+  1 + ((1000 * _msdelay_ * CYGNUM_HAL_RTC_DENOMINATOR) / (CYGNUM_HAL_RTC_NUMERATOR / 1000)))
+
+#else
+#define M25PXX_DELAY_MS(_msdelay_) CYGACC_CALL_IF_DELAY_US (_msdelay_ * 1000)
+#endif
+
+//-----------------------------------------------------------------------------
+// Maintenance and debug macros.
+
+#define TODO_M25P(_msg_) CYG_ASSERT(false, "TODO (M25P) : " _msg_)
+#define FAIL_M25P(_msg_) CYG_ASSERT(false, "FAIL (M25P) : " _msg_)
+#define ASSERT_M25P(_test_, _msg_) CYG_ASSERT(_test_, "FAIL (M25P) : " _msg_)
+#define TRACE_M25P(_msg_, _args_...) if (dev->pf) dev->pf ("M25PXX : " _msg_, ##_args_)
+
+//=============================================================================
+// Define M25Pxx SPI protocol.
+//=============================================================================
+
+typedef enum m25pxx_cmd { 
+  M25PXX_CMD_WREN  = 0x06,  // Write enable.
+  M25PXX_CMD_WDRI  = 0x04,  // Write disable.
+  M25PXX_CMD_RDID  = 0x9F,  // Read identification.
+  M25PXX_CMD_RDSR  = 0x05,  // Read status register.
+  M25PXX_CMD_WRSR  = 0x01,  // Write status register.
+  M25PXX_CMD_READ  = 0x03,  // Read data.
+  M25PXX_CMD_FREAD = 0x0B,  // Read data (fast transaction).
+  M25PXX_CMD_PP    = 0x02,  // Page program.
+  M25PXX_CMD_SE    = 0xD8,  // Sector erase.
+  M25PXX_CMD_BE    = 0xC7,  // Bulk erase.
+  M25PXX_CMD_RES   = 0xAB,  // Read electronic signature.
+} m25pxx_cmd;
+
+// Status register bitfields.
+#define M25PXX_STATUS_WIP  0x01  /* Write in progress. */
+#define M25PXX_STATUS_WEL  0x02  /* Write enable latch. */
+#define M25PXX_STATUS_BP0  0x04  /* Block protect 0. */
+#define M25PXX_STATUS_BP1  0x08  /* Block protect 1. */
+#define M25PXX_STATUS_BP2  0x10  /* Block protect 2. */
+#define M25PXX_STATUS_SRWD 0x80  /* Status register write protect. */
+
+// Page size of 256 bytes appears to be common for all devices.
+#define M25PXX_PAGE_SIZE 256
+
+//=============================================================================
+// Array containing a list of supported devices.  This allows the device
+// parameters to be dynamically detected on initialisation.
+//=============================================================================
+
+typedef struct m25pxx_params {
+  cyg_uint16 sector_size;   // Number of pages in a sector.
+  cyg_uint16 sector_count;  // Number of sectors on device.  
+  cyg_uint32 jedec_id;      // 3 byte JEDEC identifier for this device.
+} m25pxx_params;
+
+static const m25pxx_params m25pxx_supported_devices [] = {
+  { // Support for Numonyx 128 MBit devices.
+    sector_size  : 1024,
+    sector_count : 64,
+    jedec_id     : 0x00202018
+  },
+  { // Support for Numonyx 64 MBit devices.
+    sector_size  : 256,
+    sector_count : 128,
+    jedec_id     : 0x00202017
+  },
+  { // Support for Numonyx 16 MBit devices.
+    sector_size  : 256,
+    sector_count : 64,
+    jedec_id     : 0x00202016
+  },
+  { // Support for Numonyx 16 MBit devices.
+    sector_size  : 256,
+    sector_count : 32,
+    jedec_id     : 0x00202015
+  },
+  { // Support for Numonyx 8 MBit devices.
+    sector_size  : 256,
+    sector_count : 16,
+    jedec_id     : 0x00202014
+  },
+  { // Support for Numonyx 4 MBit devices.
+    sector_size  : 256,
+    sector_count : 8,
+    jedec_id     : 0x00202013
+  },
+  { // Support for Numonyx 2 MBit devices.
+    sector_size  : 256,
+    sector_count : 4,
+    jedec_id     : 0x00202012
+  },
+  { // Support for Numonyx 1 MBit devices.
+    sector_size  : 128,
+    sector_count : 4,
+    jedec_id     : 0x00202011
+  },
+  { // Support for Numonyx 512 KBit devices.
+    sector_size  : 128,
+    sector_count : 2,
+    jedec_id     : 0x00202010
+  },
+  { // Null terminating entry.
+    sector_size  : 0,
+    sector_count : 0,
+    jedec_id     : 0
+  }
+};
+
+//=============================================================================
+// Utility functions for address calculations.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Strips out any device address offset to give address within device.
+
+static cyg_bool m25pxx_to_local_addr
+  (struct cyg_flash_dev* dev, cyg_flashaddr_t* addr)
+{
+  cyg_bool retval = false;
+
+  // Range check address before modifying it.
+  if ((*addr >= dev->start) && (*addr <= dev->end)) {
+    *addr -= dev->start;
+    retval = true;
+  }
+  return retval;
+}
+
+//=============================================================================
+// Wrapper functions for various SPI transactions.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Read back the 3-byte JEDEC ID, returning it as a 32-bit integer.
+// This function is called during flash initialisation, which can often be
+// called from the startup/idle thread.  This means that we should always use
+// SPI polled mode in order to prevent the thread from attempting to sleep.
+
+static inline cyg_uint32 m25pxx_spi_rdid
+  (struct cyg_flash_dev *dev)
+{
+  cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv;
+  const cyg_uint8 tx_buf [4] = { M25PXX_CMD_RDID, 0, 0, 0 };
+  cyg_uint8 rx_buf [4];
+  cyg_uint32 retval = 0;
+
+  // Carry out SPI transfer.
+  cyg_spi_transfer (spi_device, true, 4, tx_buf, rx_buf);
+
+  // Convert 3-byte ID to 32-bit integer.
+  retval |= ((cyg_uint32) rx_buf[1]) << 16;   
+  retval |= ((cyg_uint32) rx_buf[2]) << 8;   
+  retval |= ((cyg_uint32) rx_buf[3]);   
+
+  return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Send write enable command.
+
+static inline void m25pxx_spi_wren
+  (struct cyg_flash_dev *dev)
+{
+  cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv;
+  const cyg_uint8 tx_buf [1] = { M25PXX_CMD_WREN };
+  cyg_spi_transfer (spi_device, M25PXX_POLLED, 1, tx_buf, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Send sector erase command.  The address parameter is a device local address 
+// within the sector to be erased.
+
+static inline void m25pxx_spi_se
+  (struct cyg_flash_dev *dev, cyg_flashaddr_t addr)
+{
+  cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv;
+  const cyg_uint8 tx_buf [4] = { M25PXX_CMD_SE,
+    (cyg_uint8) (addr >> 16), (cyg_uint8) (addr >> 8), (cyg_uint8) (addr) };
+  cyg_spi_transfer (spi_device, M25PXX_POLLED, 4, tx_buf, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Read and return the 8-bit device status register.
+
+static inline cyg_uint8 m25pxx_spi_rdsr
+  (struct cyg_flash_dev *dev)
+{
+  cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv;
+  const cyg_uint8 tx_buf [2] = { M25PXX_CMD_RDSR, 0 };
+  cyg_uint8 rx_buf [2];
+
+  // Carry out SPI transfer and return the status byte.
+  cyg_spi_transfer (spi_device, M25PXX_POLLED, 2, tx_buf, rx_buf);
+  return rx_buf [1];
+}
+
+//-----------------------------------------------------------------------------
+// Program a single page.
+
+static inline void m25pxx_spi_pp
+  (struct cyg_flash_dev *dev, cyg_flashaddr_t addr, cyg_uint8* wbuf, cyg_uint32 wbuf_len)
+{
+  cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv;
+  const cyg_uint8 tx_buf [4] = { M25PXX_CMD_PP,
+    (cyg_uint8) (addr >> 16), (cyg_uint8) (addr >> 8), (cyg_uint8) (addr) };
+
+  // Implement the program operation as a multistage SPI transaction.
+  cyg_spi_transaction_begin (spi_device);
+  cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, 4, tx_buf, NULL, false);
+  cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, wbuf_len, wbuf, NULL, false);
+  cyg_spi_transaction_end (spi_device);
+}
+
+//-----------------------------------------------------------------------------
+// Implement fast reads to the specified buffer.
+
+static inline void m25pxx_spi_fread
+  (struct cyg_flash_dev *dev, cyg_flashaddr_t addr, cyg_uint8* rbuf, cyg_uint32 rbuf_len)
+{
+  cyg_spi_device* spi_device = (cyg_spi_device*) dev->priv;
+  const cyg_uint8 tx_buf [5] = { M25PXX_CMD_FREAD,
+    (cyg_uint8) (addr >> 16), (cyg_uint8) (addr >> 8), (cyg_uint8) (addr), 0 };
+
+  // Implement the read operation as a multistage SPI transaction.
+  cyg_spi_transaction_begin (spi_device);
+  cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, 5, tx_buf, NULL, false);
+  cyg_spi_transaction_transfer (spi_device, M25PXX_POLLED, rbuf_len, NULL, rbuf, false);
+  cyg_spi_transaction_end (spi_device);
+}
+
+//=============================================================================
+// Standard Flash device API.  All the following functions assume that a valid
+// SPI device handle is passed in the 'priv' reference of the flash device
+// data structure.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Initialise the SPI flash, reading back the flash parameters.
+
+static int m25pxx_init 
+  (struct cyg_flash_dev *dev)
+{
+  m25pxx_params* dev_params = (m25pxx_params*) m25pxx_supported_devices;
+  cyg_uint32 device_id;
+  int retval = FLASH_ERR_INVALID;
+
+  // Find the device in the supported devices list.
+  device_id = m25pxx_spi_rdid (dev);
+  while ((dev_params->jedec_id != 0) && (dev_params->jedec_id != device_id)) {
+    dev_params ++;
+  }
+
+  // Found supported device - update device parameters.  M25PXX devices have a 
+  // uniform sector distribution, so only 1 block info record is required.
+  if (dev_params->jedec_id != 0) {
+    ASSERT_M25P (dev->num_block_infos == 1, "Only 1 block info record required.");
+    ASSERT_M25P (dev->block_info != NULL, "Null pointer to block info record.");
+    if ((dev->num_block_infos == 1) && (dev->block_info != NULL)) { 
+      TRACE_M25P ("Init device with JEDEC ID 0x%06X.\n", device_id);
+      dev->end = dev->start + (M25PXX_PAGE_SIZE * (cyg_flashaddr_t) dev_params->sector_size * 
+        (cyg_flashaddr_t) dev_params->sector_count) - 1;
+
+      // Strictly speaking the block info fields are 'read only'.  However, we
+      // have a legitimate reason for updating the contents here and can cast
+      // away the const.
+      ((cyg_flash_block_info_t*) dev->block_info)->block_size = 
+        M25PXX_PAGE_SIZE * (size_t) dev_params->sector_size; 
+      ((cyg_flash_block_info_t*) dev->block_info)->blocks = 
+        (cyg_uint32) dev_params->sector_count;
+      retval = FLASH_ERR_OK;
+    }
+  }
+  return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Erase a single sector of the flash.
+
+static int m25pxx_erase_block 
+  (struct cyg_flash_dev *dev, cyg_flashaddr_t block_base)
+{
+  cyg_flashaddr_t local_base = block_base;
+  int retval = FLASH_ERR_INVALID;
+  cyg_uint8 dev_status;
+
+  // Fix up the block address and send the sector erase command.
+  if (m25pxx_to_local_addr (dev, &local_base)) {
+    m25pxx_spi_wren (dev);
+    m25pxx_spi_se (dev, local_base);
+
+    // Spin waiting for the erase to complete.  This can take between 1 and 3
+    // seconds, so we use a polling interval of 1/2 sec.
+    do {
+      M25PXX_DELAY_MS (500);
+      dev_status = m25pxx_spi_rdsr (dev);
+    } while (dev_status & M25PXX_STATUS_WIP);
+
+    retval = FLASH_ERR_OK;    
+  }
+  return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Program an arbitrary number of pages into flash and verify written data.
+
+static int m25pxx_program 
+  (struct cyg_flash_dev *dev, cyg_flashaddr_t base, const void* data, size_t len)
+{
+  cyg_flashaddr_t local_base = base;
+  int retval = FLASH_ERR_OK;
+  cyg_uint8* tx_ptr = (cyg_uint8*) data;
+  cyg_uint32 tx_bytes_left = (cyg_uint32) len;
+  cyg_uint32 tx_bytes;
+  cyg_uint8  dev_status;
+
+  // Fix up the block address.
+  if (!m25pxx_to_local_addr (dev, &local_base)) {
+    retval = FLASH_ERR_INVALID;
+    goto out;
+  }
+
+  // The start of the transaction may not be page aligned, so we need to work
+  // out how many bytes to transmit before we hit the first page boundary.
+  tx_bytes = M25PXX_PAGE_SIZE - (((cyg_uint32) local_base) & (M25PXX_PAGE_SIZE - 1));
+  if (tx_bytes > tx_bytes_left) tx_bytes = tx_bytes_left;
+
+  // Perform page program operations.
+  while (tx_bytes_left) {
+    m25pxx_spi_wren (dev);
+    m25pxx_spi_pp (dev, local_base, tx_ptr, tx_bytes);
+
+    // Spin waiting for write to complete.  This can take up to 5ms, so
+    // we use a polling interval of 1ms - which may get rounded up to the
+    // RTC tick granularity.
+    do {
+      M25PXX_DELAY_MS (1);
+      dev_status = m25pxx_spi_rdsr (dev);
+    } while (dev_status & M25PXX_STATUS_WIP);
+    
+    // Update counters and data pointers for the next page.
+    tx_bytes_left -= tx_bytes;
+    tx_ptr += tx_bytes;
+    local_base += tx_bytes; 
+    tx_bytes = (tx_bytes_left > M25PXX_PAGE_SIZE) ? M25PXX_PAGE_SIZE : tx_bytes_left;
+  }
+
+out:
+  return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Read back an arbitrary amount of data from flash.
+
+static int m25pxx_read 
+  (struct cyg_flash_dev *dev, const cyg_flashaddr_t base, void* data, size_t len)
+{
+  cyg_flashaddr_t local_base = base;
+  int retval = FLASH_ERR_INVALID;
+  cyg_uint8* rx_ptr = (cyg_uint8*) data;
+  cyg_uint32 rx_bytes_left = (cyg_uint32) len;
+  cyg_uint32 rx_bytes;
+
+  // Determine the maximum transfer size to use.
+  cyg_uint32 rx_block_size = (CYGNUM_DEVS_FLASH_SPI_M25PXX_READ_BLOCK_SIZE == 0) ? 
+    0xFFFFFFFF : CYGNUM_DEVS_FLASH_SPI_M25PXX_READ_BLOCK_SIZE;
+
+  // Fix up the block address and fill the read buffer.
+  if (m25pxx_to_local_addr (dev, &local_base)) {
+    while (rx_bytes_left) {
+      rx_bytes = (rx_bytes_left < rx_block_size) ? rx_bytes_left : rx_block_size;
+      m25pxx_spi_fread (dev, local_base, rx_ptr, rx_bytes);
+
+      // Update counters and data pointers for next read block.
+      rx_bytes_left -= rx_bytes;
+      rx_ptr += rx_bytes;
+      local_base += rx_bytes; 
+    }
+    retval = FLASH_ERR_OK;
+  }
+  return retval;
+}
+
+//=============================================================================
+// Fill in the driver data structures.
+//=============================================================================
+
+CYG_FLASH_FUNS (
+  cyg_devs_flash_spi_m25pxx_funs, // Exported name of function pointers.
+  m25pxx_init,                    // Flash initialisation.
+  cyg_flash_devfn_query_nop,      // Query operations not supported.
+  m25pxx_erase_block,             // Sector erase.
+  m25pxx_program,                 // Program multiple pages.
+  m25pxx_read,                    // Read arbitrary amount of data.
+  cyg_flash_devfn_lock_nop,       // Locking not supported (no per-sector locks).
+  cyg_flash_devfn_unlock_nop
+);
+
+//=============================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/m25pxx/current/test/m25pxx_test.c
@@ -0,0 +1,395 @@
+//=============================================================================
+//
+//      m25pxx_test.c
+//
+//      SPI flash driver tests for Numonyx M25Pxx devices and compatibles.
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under    
+// the terms of the GNU General Public License as published by the Free     
+// Software Foundation; either version 2 or (at your option) any later      
+// version.                                                                 
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT      
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
+// for more details.                                                        
+//
+// You should have received a copy of the GNU General Public License        
+// along with eCos; if not, write to the Free Software Foundation, Inc.,    
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+//
+// As a special exception, if other files instantiate templates or use      
+// macros or inline functions from this file, or you compile this file      
+// and link it with other works to produce a work based on this file,       
+// this file does not by itself cause the resulting work to be covered by   
+// the GNU General Public License. However the source code for this file    
+// must still be made available in accordance with section (3) of the GNU   
+// General Public License v2.                                               
+//
+// This exception does not invalidate any other reasons why a work based    
+// on this file might be covered by the GNU General Public License.         
+// -------------------------------------------                              
+// ####ECOSGPLCOPYRIGHTEND####                                              
+//=============================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):   Chris Holgate
+// Date:        2008-12-22
+// Purpose:     Numonyx M25Pxx SPI flash driver stress tests.
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+#include <pkgconf/system.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/testcase.h>           // Test macros
+#include <cyg/infra/cyg_ass.h>            // Assertion macros
+#include <cyg/infra/diag.h>               // Diagnostic output
+
+#include <cyg/hal/hal_arch.h>             // CYGNUM_HAL_STACK_SIZE_TYPICAL
+#include <cyg/hal/hal_if.h>
+#include <cyg/kernel/kapi.h>
+
+#include <cyg/io/spi.h>
+#include <cyg/io/flash.h>
+#include <cyg/io/m25pxx.h>
+
+#include <string.h>
+
+#include CYGHWR_MEMORY_LAYOUT_H
+
+//-----------------------------------------------------------------------------
+// Set up the SPI intertface on the ST STM3210E eval board for the STM32.  The 
+// M25PXX device is connected to SPI bus 1.  To configure the board for this 
+// test, jumper JP3 must be open and SPI1 should be set up in configtool so
+// that chip select 1 corresponds to GPIO number 18 (PB2) and 50MHz I/O is
+// enabled.  If the data area is in external RAM, SPI1 must be configured with 
+// bounce buffers of at least 256 bytes and the M25PXX read data block size 
+// should be set to the same size as the bounce buffers.
+
+#ifdef CYGPKG_HAL_CORTEXM_STM32_STM3210E_EVAL
+#include <cyg/io/spi_stm32.h>
+
+CYG_DEVS_SPI_CORTEXM_STM32_DEVICE (
+    m25pxx_spi_device, 1, 0, false, 0, 0, 25000000, 1, 1, 1
+);
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Instantiate the M25Pxx device driver.
+
+CYG_DEVS_FLASH_SPI_M25PXX_DRIVER (
+    m25pxx_flash_device, 0, &m25pxx_spi_device
+);
+
+//-----------------------------------------------------------------------------
+// Thread data structures.
+
+static cyg_uint32 stack [CYGNUM_HAL_STACK_SIZE_TYPICAL / 4];
+static cyg_thread thread_data;
+static cyg_handle_t thread_handle;
+
+//-----------------------------------------------------------------------------
+// Data transfer buffers.
+
+#define BUF_SIZE 1024
+
+static cyg_uint8 wbuf [BUF_SIZE];
+static cyg_uint8 rbuf [BUF_SIZE];
+
+//-----------------------------------------------------------------------------
+// Print out version information.
+
+void do_version (void)
+{
+    char *version = CYGACC_CALL_IF_MONITOR_VERSION();
+    if (version != NULL) diag_printf("%s", version);
+#ifdef HAL_PLATFORM_CPU
+    diag_printf("Platform: %s (%s) %s\n", HAL_PLATFORM_BOARD, HAL_PLATFORM_CPU, HAL_PLATFORM_EXTRA);
+#endif
+    diag_printf("RAM: %p-%p, ", (void*)(CYGMEM_REGION_ram), (void*)(CYGMEM_REGION_ram + CYGMEM_REGION_ram_SIZE - 1));
+    diag_printf("(HEAP: %p-%p)\n", (void*)(CYGMEM_SECTION_heap1), (void*)(CYGMEM_SECTION_heap1 + CYGMEM_SECTION_heap1_SIZE - 1));
+}
+
+//-----------------------------------------------------------------------------
+// Run checkerboard and inverse checkerboard writes over each sector in turn.
+
+cyg_uint32 run_test_1
+  (void)
+{
+    int status;
+    cyg_uint32 i, j;
+    cyg_uint32 errors = 0;
+    cyg_flashaddr_t base_addr, err_addr;
+
+    diag_printf ("Test 1 - write and verify checkerboard and inverse checkerboard.\n");
+    
+    // Iterate over all flash sectors.
+    for (i = 0; i < m25pxx_flash_device.block_info->blocks; i++) {
+        base_addr =  m25pxx_flash_device.start + (i * m25pxx_flash_device.block_info->block_size);
+
+        // Erase block.
+        status = cyg_flash_erase (base_addr, 1, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+
+        // Set up buffer with checkerboard.
+        for (j = 0; j < BUF_SIZE;) {
+           wbuf [j++] = 0x55;
+           wbuf [j++] = 0xAA;
+        }
+
+        // Write the checkerboard to the entire sector.
+        for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) {
+            status = cyg_flash_program (base_addr + j, wbuf, BUF_SIZE, &err_addr);
+            if (status != FLASH_ERR_OK) {
+                diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status));
+                errors ++;
+            }
+        }
+
+        // Read back the checkerboard and verify.
+        for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) {
+            status = cyg_flash_read (base_addr + j, rbuf, BUF_SIZE, &err_addr);
+            if (status != FLASH_ERR_OK) {
+                diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status));
+                errors ++;
+            }
+            else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) {
+                diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr + j, base_addr + j + BUF_SIZE - 1);
+                errors ++;
+            }
+        }
+
+        // Erase block.
+        status = cyg_flash_erase (base_addr, 1, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+
+        // Set up buffer with inverse checkerboard.
+        for (j = 0; j < BUF_SIZE;) {
+           wbuf [j++] = 0xAA;
+           wbuf [j++] = 0x55;
+        }
+
+        // Write the checkerboard to the entire sector.
+        for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) {
+            status = cyg_flash_program (base_addr + j, wbuf, BUF_SIZE, &err_addr);
+            if (status != FLASH_ERR_OK) {
+                diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status));
+                errors ++;
+            }
+        }
+
+        // Read back the checkerboard and verify.
+        for (j = 0; j < m25pxx_flash_device.block_info->block_size; j += BUF_SIZE) {
+            status = cyg_flash_read (base_addr + j, rbuf, BUF_SIZE, &err_addr);
+            if (status != FLASH_ERR_OK) {
+                diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status));
+                errors ++;
+            }
+            else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) {
+                diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr + j, base_addr + j + BUF_SIZE - 1);
+                errors ++;
+            }
+        }
+    }
+    return errors;
+}
+
+//-----------------------------------------------------------------------------
+// Write and verify counting sequence over all device.
+
+cyg_uint32 run_test_2
+  (void)
+{
+    int status;
+    cyg_uint32 i, j;
+    cyg_uint32 errors = 0;
+    cyg_flashaddr_t base_addr, err_addr;
+
+    diag_printf ("Test 2 - write and verify counting sequence.\n");
+    
+    // Erase all flash sectors.
+    for (i = 0; i < m25pxx_flash_device.block_info->blocks; i++) {
+        base_addr =  m25pxx_flash_device.start + (i * m25pxx_flash_device.block_info->block_size);
+
+        // Erase block.
+        status = cyg_flash_erase (base_addr, 1, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+    }
+
+    // Write counting sequence.
+    base_addr =  m25pxx_flash_device.start;
+    for (i = 0; i < (m25pxx_flash_device.end - m25pxx_flash_device.start) / 4;) {
+        for (j = 0; j < BUF_SIZE;) {
+            wbuf [j++] = (cyg_uint8) ((i >> 0) & 0xFF);
+            wbuf [j++] = (cyg_uint8) ((i >> 8) & 0xFF);
+            wbuf [j++] = (cyg_uint8) ((i >> 16) & 0xFF);
+            wbuf [j++] = (cyg_uint8) ((i >> 24) & 0xFF);            
+            i++;
+        }
+        status = cyg_flash_program (base_addr, wbuf, BUF_SIZE, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+        base_addr += BUF_SIZE;
+    }
+
+    // Verify counting sequence.
+    base_addr = m25pxx_flash_device.start;
+    for (i = 0; i < (m25pxx_flash_device.end - m25pxx_flash_device.start) / 4;) {
+        for (j = 0; j < BUF_SIZE;) {
+            wbuf [j++] = (cyg_uint8) ((i >> 0) & 0xFF);
+            wbuf [j++] = (cyg_uint8) ((i >> 8) & 0xFF);
+            wbuf [j++] = (cyg_uint8) ((i >> 16) & 0xFF);
+            wbuf [j++] = (cyg_uint8) ((i >> 24) & 0xFF);            
+            i++;
+        }
+        status = cyg_flash_read (base_addr, rbuf, BUF_SIZE, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+        else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) {
+            diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr, base_addr + BUF_SIZE - 1);
+            errors ++;
+        }
+        base_addr += BUF_SIZE;
+    }
+    return errors;
+}
+
+//-----------------------------------------------------------------------------
+// Perform non-aligned buffer read/write tests, spanning sector boundaries.
+
+cyg_uint32 run_test_3
+  (void)
+{
+    int status;
+    cyg_uint32 i, count;
+    cyg_uint32 errors = 0;
+    cyg_flashaddr_t base_addr, err_addr;
+
+    diag_printf ("Test 3 - test non-aligned writes and reads.\n");
+    
+    // Fill the write buffer with a basic counting sequence.
+    count = 0;
+    for (i = 0; i < BUF_SIZE;) {
+        wbuf [i++] = (cyg_uint8) ((count >> 0) & 0xFF);
+        wbuf [i++] = (cyg_uint8) ((count >> 8) & 0xFF);
+        count++;
+    }
+
+    // Assuming 256 byte pages gives 256 possible alignments.
+    base_addr = m25pxx_flash_device.start + m25pxx_flash_device.block_info->block_size;
+    for (i = 1; i <= 256; i++) {
+
+        // Erase sectors either side of sector boundary.
+        status = cyg_flash_erase (base_addr - 1, 1, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+        status = cyg_flash_erase (base_addr, 1, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash erase error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+
+        // Write data spanning sector boundary.        
+        status = cyg_flash_program (base_addr - i, wbuf, BUF_SIZE, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash write error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+
+        // Verify data spanning sector boundary.
+        status = cyg_flash_read (base_addr - i, rbuf, BUF_SIZE, &err_addr);
+        if (status != FLASH_ERR_OK) {
+            diag_printf ("Flash read error : %s\n", cyg_flash_errmsg (status));
+            errors ++;
+        }
+        else if (memcmp (rbuf, wbuf, BUF_SIZE) != 0) {
+            diag_printf ("Flash read data corruption (0x%08X - 0x%08X).\n", base_addr - i, base_addr - i + BUF_SIZE - 1);
+            errors ++;
+        }
+
+        // Use next sector boundary for next test.
+        base_addr += m25pxx_flash_device.block_info->block_size;
+        if (base_addr >= m25pxx_flash_device.end)
+            base_addr = m25pxx_flash_device.start + m25pxx_flash_device.block_info->block_size;
+    }
+    return errors;
+}
+
+//-----------------------------------------------------------------------------
+// Run all M25Pxx SPI interface loopback tests.
+
+void run_tests 
+    (void)
+{
+    cyg_uint32 errors = 0;
+
+    // Check that the device is intialised.
+    if (m25pxx_flash_device.start == m25pxx_flash_device.end) {
+        diag_printf ("M25Pxx device not initialised.\n");
+        errors ++;
+        goto out;
+    }
+
+    // Run the tests.
+    errors += run_test_1 (); 
+    errors += run_test_2 (); 
+    errors += run_test_3 ();
+
+out:
+    diag_printf ("----\nTests complete - detected %d errors in total.\n", errors);
+    if (errors == 0)
+        CYG_TEST_PASS_FINISH ("M25Pxx driver tests ran OK.");
+    else 
+        CYG_TEST_FAIL_FINISH ("M25Pxx driver test FAILED.");
+}
+
+//-----------------------------------------------------------------------------
+// User startup - tests are run in their own thread.
+
+void cyg_user_start
+    (void)
+{
+    do_version();
+    diag_printf ("----\nChecking for M25Pxx compatible devices.\n");
+    cyg_flash_init (&diag_printf);
+    CYG_TEST_INIT();
+    cyg_thread_create(
+        10,                                   // Arbitrary priority
+        (cyg_thread_entry_t*) run_tests,      // Thread entry point
+        0,                                    // 
+        "test_thread",                        // Thread name
+        (cyg_uint8*) stack,                   // Stack
+        CYGNUM_HAL_STACK_SIZE_TYPICAL,        // Stack size
+        &thread_handle,                       // Thread handle
+        &thread_data                          // Thread data structure
+    );
+    cyg_thread_resume(thread_handle);
+    cyg_scheduler_start();
+}
+
+//=============================================================================