changeset 3037:e6355066c1b5

New package - SPI flash driver for Silicon Storage Technology, SST25xx devices.
author sergeig
date Wed, 25 May 2011 20:23:23 +0000
parents d27b9c79090a
children c02cbbc2a972
files packages/devs/flash/spi/sst25xx/current/ChangeLog packages/devs/flash/spi/sst25xx/current/cdl/flash_sst25xx.cdl packages/devs/flash/spi/sst25xx/current/include/sst25xx.h packages/devs/flash/spi/sst25xx/current/src/sst25xx.c
diffstat 4 files changed, 945 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/sst25xx/current/ChangeLog
@@ -0,0 +1,31 @@
+2011-04-13  Christophe Coutand  <ecos@hotmail.co.uk>
+
+	* cdl/sst25xx.cdl:
+	* include/sst25xx.h:
+	* src/sst25xx.c:
+	New package - SPI flash driver for Silicon Storage Technology,
+	SST25xx devices.
+
+//===========================================================================
+// ####GPLCOPYRIGHTBEGIN####                                                
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2011 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/sst25xx/current/cdl/flash_sst25xx.cdl
@@ -0,0 +1,126 @@
+##=============================================================================
+##
+##      flash_sst25xx.cdl
+##
+##      SST25xx SPI flash driver configuration options.
+##
+##=============================================================================
+## ####ECOSGPLCOPYRIGHTBEGIN####                                            
+## -------------------------------------------                              
+## This file is part of eCos, the Embedded Configurable Operating System.   
+## Copyright (C) 2011 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):   ccoutand, updated for Silicon Storage Technology
+##              SST25xx flash
+## Original(s): Chris Holgate
+## Date:        2011-04-25
+## Purpose:     Configure SST25xx SPI flash driver.
+##
+######DESCRIPTIONEND####
+##
+##=============================================================================
+
+cdl_package CYGPKG_DEVS_FLASH_SPI_SST25XX {
+    display      "Silicon Storage Technology SST25xx flash memory support"
+    parent        CYGPKG_IO_FLASH
+    active_if     { CYGPKG_IO_FLASH && CYGPKG_IO_SPI }
+    implements    CYGHWR_IO_FLASH_DEVICE
+    implements    CYGHWR_IO_FLASH_BLOCK_LOCKING
+    implements    CYGHWR_IO_FLASH_INDIRECT_READS
+
+    include_dir   cyg/io
+    compile       sst25xx.c
+
+    description   "
+        Flash memory support for the Silicon Storage Technology SST25xx
+        SPI flash devices and compatibles.  This driver implements the
+        V2 flash driver API"
+
+    cdl_option CYGPKG_DEVS_FLASH_SPI_SST25XX_SUPPORT_JEDECID {
+        display       "Device supports JEDEC ID"
+        flavor        bool
+        default_value 0
+
+        description   "
+            Allow probing of the SPI flash(s) on the bus using the
+            JededID Opcode. Some device do not support this command
+            (SST25VF010A, SST25VF512A etc.)."
+    }
+
+    cdl_option CYGPKG_DEVS_FLASH_SPI_SST25XX_BLOCK_SIZE {
+        display       "Flash block size in bytes"
+        flavor        data
+        legal_values  { 4096 32768 65536 }
+        default_value { 4096 }
+
+        description   "
+            Most SST25xx SPI flash devices can use block size of 4KB,
+            32K or 64KB. This option allow users to select which flash
+            geometry to use. For device such as AT25F512 not supporting
+            64K, selecting a wrong geometry will force the driver to
+            abort the initialization process."
+    }
+
+    cdl_option CYGPKG_DEVS_FLASH_SPI_SST25XX_READ_MODE {
+        display       "Flash read transaction"
+        flavor        data
+        legal_values  { "FAST" "SLOW" }
+        default_value { "FAST" }
+
+        description   "
+            The SST25xx SPI flash devices differentiate read access
+            according to the throughput required for this operation. The
+            driver allows both fast and slow reading operation. The
+            option requires the physical layer to use the appropriate
+            SPI bus clock frequency."
+    }
+
+    cdl_option CYGNUM_DEVS_FLASH_SPI_SST25XX_READ_BLOCK_SIZE {
+        display       "Maximum read block size"
+        flavor        data
+        default_value 0
+
+        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."
+    }
+}
+
+# EOF flash_sst25xx.cdl
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/sst25xx/current/include/sst25xx.h
@@ -0,0 +1,83 @@
+#ifndef CYGONCE_DEVS_FLASH_SPI_SST25XX_H
+#define CYGONCE_DEVS_FLASH_SPI_SST25XX_H
+
+//=============================================================================
+//
+//      sst25xx.h
+//
+//      SPI flash driver for Silicon Storage Technology SST25xx devices
+//      and compatibles.
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009, 2011 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):   ccoutand, updated for Silicon Storage Technology SST25xx
+//              flash
+// Original(s): Chris Holgate
+// Purpose:     Silicon Storage Technology SST25xxx 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_sst25xx_funs;
+
+//-----------------------------------------------------------------------------
+// Macro used to generate a flash device object with the default SST25XX
+// settings.  Even though the block info data structure is declared here, the
+// details are not filled in until the device type is inferred during
+// initialization.  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_SST25XX_DRIVER(_name_, _start_, _spidev_)    \
+struct cyg_flash_block_info _name_ ##_block_info;                       \
+CYG_FLASH_DRIVER(_name_, &cyg_devs_flash_spi_sst25xx_funs, 0,           \
+    _start_, _start_, 1, & _name_ ##_block_info, (void*) _spidev_)
+
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+#endif // CYGONCE_DEVS_FLASH_SPI_SST25XX_H
+// EOF sst25xx.h
new file mode 100644
--- /dev/null
+++ b/packages/devs/flash/spi/sst25xx/current/src/sst25xx.c
@@ -0,0 +1,705 @@
+//=============================================================================
+//
+//      sst25xx.c
+//
+//      SPI flash driver for Silicon Storage Technology SST25xx devices
+//      and compatibles.
+//
+//=============================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####                                            
+// -------------------------------------------                              
+// This file is part of eCos, the Embedded Configurable Operating System.   
+// Copyright (C) 2008, 2009, 2011 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):   ccoutand, updated for Silicon Storage Technology SST25xx
+//              flash
+// Original(s): Chris Holgate
+// Date:        2011-04-25
+// Purpose:     Silicon Storage Technology SST25xx 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_sst25xx.h>
+
+#include <string.h>
+
+//-----------------------------------------------------------------------------
+// Enable polled SPI operation for non-kernel builds.
+
+#ifdef CYGPKG_KERNEL
+# define SST25XX_POLLED false
+#else
+# define SST25XX_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 SST25XX_DELAY_MS(_msdelay_) cyg_thread_delay \
+    (1 + ((1000 * _msdelay_ * CYGNUM_HAL_RTC_DENOMINATOR) / (CYGNUM_HAL_RTC_NUMERATOR / 1000)))
+
+#else
+#define SST25XX_DELAY_MS(_msdelay_) CYGACC_CALL_IF_DELAY_US (_msdelay_ * 1000)
+#endif
+
+// Byte / word programming takes 10 us max
+#define SST25XX_WAIT_CHIP_READY( dev )                  \
+{                                                       \
+    cyg_uint8 dev_status;                               \
+    do {                                                \
+        HAL_DELAY_US (10);                              \
+        dev_status = sst25xx_spi_rdsr ( dev );          \
+    } while ( dev_status & SST25XX_STATUS_BSY );        \
+}
+
+//-----------------------------------------------------------------------------
+// Maintenance and debug macros.
+
+#define ASSERT_SST25XX(_test_, _msg_) CYG_ASSERT(_test_, "FAIL (SST25XX) : " _msg_)
+#define TRACE_SST25XX(_msg_, _args_...) if (dev->pf) dev->pf ("SST25XX : " _msg_, ##_args_)
+
+//=============================================================================
+// Define SST25VFxxx SPI protocol.
+//=============================================================================
+
+typedef enum sst25xx_cmd {
+    SST25XX_CMD_WREN   = 0x06,     // Write enable.
+    SST25XX_CMD_WDRI   = 0x04,     // Write disable.
+    SST25XX_CMD_RDJID  = 0x9F,     // Read JEDEC identification.
+    SST25XX_CMD_RDID   = 0x90,     // Read Manufacturer / Device identification.
+    SST25XX_CMD_RDSR   = 0x05,     // Read status register.
+    SST25XX_CMD_WRSR   = 0x01,     // Write status register.
+    SST25XX_CMD_READ   = 0x03,     // Read data
+    SST25XX_CMD_FREAD  = 0x0B,     // Read data (fast).
+    SST25XX_CMD_BP     = 0x02,     // Byte program.
+    SST25XX_CMD_AAI    = 0xAD,     // Word program (with address increment).
+    SST25XX_CMD_SE_4K  = 0x20,     // 4K sector erase.
+    SST25XX_CMD_SE_32K = 0x52,     // 32K sector erase.
+    SST25XX_CMD_SE_64K = 0xD8,     // 64K sector erase.
+    SST25XX_CMD_BE     = 0xC7,     // Chip erase.
+} sst25xx_cmd;
+
+// Status register bitfields.
+#define SST25XX_STATUS_BSY  0x01   /* Operation in progress. */
+#define SST25XX_STATUS_WEL  0x02   /* Write enable latch. */
+#define SST25XX_STATUS_BP0  0x04   /* Block Write Protect 0. */
+#define SST25XX_STATUS_BP1  0x08   /* Block Write Protect 1. */
+#define SST25XX_STATUS_BP2  0x10   /* Block Write Protect 2. */
+#define SST25XX_STATUS_BP3  0x20   /* Block Write Protect 3. */
+#define SST25XX_STATUS_AAI  0x40   /* Auto Address Increment Programming Status */
+#define SST25XX_STATUS_SPRL 0x80   /* Sector Protect Register Bit Lock. */
+
+#define SST25XX_LOCK_BITS   ( SST25XX_STATUS_BP0 | SST25XX_STATUS_BP1 | \
+                              SST25XX_STATUS_BP2 | SST25XX_STATUS_BP3 )
+
+#define SST25XX_MEMORY_ID   0x25
+
+// A few helper constants
+#ifndef SZ_1K
+# define SZ_1K           0x00000400
+# define SZ_2K           0x00000800
+# define SZ_4K           0x00001000
+# define SZ_8K           0x00002000
+# define SZ_16K          0x00004000
+# define SZ_32K          0x00008000
+# define SZ_64K          0x00010000
+#endif
+
+// Select read OPCODE
+#if defined(CYGPKG_DEVS_FLASH_SPI_SST25XX_READ_MODE_SLOW)
+# define SST25XX_CMD_READ_LEN 4
+# define SST25XX_CMD_READ_OPCODE SST25XX_CMD_READ
+#elif defined(CYGPKG_DEVS_FLASH_SPI_SST25XX_READ_MODE_FAST)
+# define SST25XX_CMD_READ_LEN 5
+# define SST25XX_CMD_READ_OPCODE SST25XX_CMD_FREAD
+#endif
+
+// Select sector size
+#if CYGPKG_DEVS_FLASH_SPI_SST25XX_BLOCK_SIZE == SZ_64K
+# define SST25XX_CMD_SE SST25XX_CMD_SE_64K
+#elif CYGPKG_DEVS_FLASH_SPI_SST25XX_BLOCK_SIZE == SZ_32K
+# define SST25XX_CMD_SE SST25XX_CMD_SE_32K
+#elif CYGPKG_DEVS_FLASH_SPI_SST25XX_BLOCK_SIZE == SZ_4K
+# define SST25XX_CMD_SE SST25XX_CMD_SE_4K
+#endif
+
+//=============================================================================
+// Array containing a list of supported devices.  This allows the device
+// parameters to be dynamically detected on initialization.
+//=============================================================================
+
+typedef struct sst25xx_params {
+    cyg_uint16 sector_count[4];       // Number of sectors on device.
+    cyg_uint32 sector_size[4];        // Supported sector size(s) in Byte
+    cyg_uint32 jedec_id;              // 3 byte JEDEC identifier for this device.
+} sst25xx_params;
+
+
+static const sst25xx_params sst25xx_supported_devices [] = {
+    // Some Silicon Storage Technology parts.
+    // Note1: Some part do not support the Read Jedec ID Opcode, instead,
+    // the Read ID Opcode is used which is backward compatible with all
+    // parts
+    // Note2: Not all parts can support sector size of 4, 32 or 64 KBytes
+    { // Support for SST 64 MBit devices (SST25VF064C).
+        sector_count        : {2048, 256, 128, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf254B
+    },
+    { // Support for SST 32 MBit devices (SST25VF032B).
+        sector_count        : {1024, 128, 64, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf254a
+    },
+    { // Support for SST 16 MBit devices (SST25VF016B).
+        sector_count        : {512, 64, 32, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf2541
+    },
+    { // Support for SST 8 MBit devices (SST25VF080B).
+        sector_count        : {256, 32, 16, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf258E
+    },
+    { // Support for SST 4 MBit devices (SST25VF040B).
+        sector_count        : {128, 16, 8, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf258D
+    },
+    { // Support for SST 2 MBit devices (SST25VF020B).
+        sector_count        : {64, 8, 4, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf258C
+    },
+    { // Support for SST 2 MBit devices (SST25LF020A).
+        sector_count        : {64, 8, 0},
+        sector_size         : {SZ_4K, SZ_32K, 0},
+        // Constructed Jedec ID from ID (0xBF43)
+        jedec_id            : 0x00bf2543
+    },
+    { // Support for SST 1 MBit devices (SST25VF010A).
+        sector_count        : {32, 4, 0},
+        sector_size         : {SZ_4K, SZ_32K, 0},
+        // Constructed Jedec ID from ID (0xBF49)
+        jedec_id            : 0x00bf2549
+    },
+    { // Support for SST 512 KBit devices (SST25VF512A).
+        sector_count        : {16, 2, 0},
+        sector_size         : {SZ_4K, SZ_32K, 0},
+        // Constructed Jedec ID from ID (0xBF48)
+        jedec_id            : 0x00bf2548
+    },
+    { // Support for SST 512 KBit devices (SST25WF512).
+        sector_count        : {16, 2, 0},
+        sector_size         : {SZ_4K, SZ_32K, 0},
+        jedec_id            : 0x00bf2501
+    },
+    { // Support for SST 1 MBit devices (SST25WF010).
+        sector_count        : {32, 4, 0},
+        sector_size         : {SZ_4K, SZ_32K, 0},
+        jedec_id            : 0x00bf2502
+    },
+    { // Support for SST 2 MBit devices (SST25WF020).
+        sector_count        : {64, 8, 4},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf2503
+    },
+    { // Support for SST 4 MBit devices (SST25WF040).
+        sector_count        : {128, 16, 8, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf2504
+    },
+    { // Support for SST 8 MBit devices (SST25WF080).
+        sector_count        : {256, 32, 16, 0},
+        sector_size         : {SZ_4K, SZ_32K, SZ_64K, 0},
+        jedec_id            : 0x00bf2505
+    },
+    { // Null terminating entry.
+        sector_count        : {0},
+        sector_size         : {0},
+        jedec_id            : 0
+    }
+};
+
+//=============================================================================
+// Utility functions for address calculations.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Strips out any device address offset to give address within device.
+
+static cyg_bool
+sst25xx_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 initialization, 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.
+
+#if defined(CYGPKG_DEVS_FLASH_SPI_SST25XX_SUPPORT_JEDECID)
+
+static inline cyg_uint32
+sst25xx_spi_rdid(struct cyg_flash_dev *dev)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    const cyg_uint8 tx_buf[4] = { SST25XX_CMD_RDJID, 0x0, 0x0, 0x0 };
+    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;
+}
+
+#else
+
+static inline cyg_uint32
+sst25xx_spi_rdid(struct cyg_flash_dev *dev)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    const cyg_uint8 tx_buf[6] = { SST25XX_CMD_RDID, 0x0, 0x0, 0x0, 0x0, 0x0 };
+    cyg_uint8       rx_buf[6];
+    cyg_uint32      retval = 0;
+
+    // Carry out SPI transfer.
+    cyg_spi_transfer(spi_device, true, 6, tx_buf, rx_buf);
+
+    // Convert 2-byte ID to 32-bit Jedec ID.
+    retval |= ((cyg_uint32)rx_buf[4]) << 16;
+    retval |= (SST25XX_MEMORY_ID << 8);
+    retval |= ((cyg_uint32)rx_buf[5]);
+
+    return retval;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Send write enable command.
+
+static inline void
+sst25xx_spi_wren(struct cyg_flash_dev *dev)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    const cyg_uint8 tx_buf[1] = { SST25XX_CMD_WREN };
+    cyg_spi_transfer(spi_device, SST25XX_POLLED, 1, tx_buf, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Send write disable command.
+
+static inline void
+sst25xx_spi_wrdis(struct cyg_flash_dev *dev)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    const cyg_uint8 tx_buf[1] = { SST25XX_CMD_WDRI };
+    cyg_spi_transfer(spi_device, SST25XX_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
+sst25xx_spi_se(struct cyg_flash_dev *dev, cyg_flashaddr_t addr)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    cyg_uint8       tx_buf[4] = { SST25XX_CMD_SE,
+        (cyg_uint8)(addr >> 16), (cyg_uint8)(addr >> 8), (cyg_uint8)(addr)
+    };
+    cyg_spi_transfer(spi_device, SST25XX_POLLED, 4, tx_buf, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Read and return the 8-bit device status register.
+
+static inline cyg_uint8
+sst25xx_spi_rdsr(struct cyg_flash_dev *dev)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    const cyg_uint8 tx_buf[2] = { SST25XX_CMD_RDSR, 0 };
+    cyg_uint8       rx_buf[2];
+
+    // Carry out SPI transfer and return the status byte.
+    cyg_spi_transfer(spi_device, SST25XX_POLLED, 2, tx_buf, rx_buf);
+
+    return rx_buf[1];
+}
+
+//-----------------------------------------------------------------------------
+// Program a single byte.
+
+static inline void
+sst25xx_spi_pbyte(struct cyg_flash_dev *dev, cyg_flashaddr_t addr,
+                  cyg_uint8 *wbuf)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    const cyg_uint8 tx_buf[4] = { SST25XX_CMD_BP,
+        (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, SST25XX_POLLED, 4, tx_buf, NULL,
+                                 false);
+    cyg_spi_transaction_transfer(spi_device, SST25XX_POLLED, 1, wbuf, NULL,
+                                 false);
+    cyg_spi_transaction_end(spi_device);
+}
+
+//-----------------------------------------------------------------------------
+// Program word with automatic address increment
+
+static inline void
+sst25xx_spi_pword(struct cyg_flash_dev *dev, cyg_flashaddr_t addr,
+                  cyg_uint8 *wbuf, cyg_bool first)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    const cyg_uint8 tx_buf[4] = { SST25XX_CMD_AAI,
+        (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);
+    // Only convey the address with the first transaction. In the following
+    // transfers, address will be incremented internally by the SPI flash
+    if (first == true) {
+        cyg_spi_transaction_transfer(spi_device, SST25XX_POLLED, 4, tx_buf,
+                                     NULL, false);
+    } else {
+        cyg_spi_transaction_transfer(spi_device, SST25XX_POLLED, 1, tx_buf,
+                                     NULL, false);
+    }
+    cyg_spi_transaction_transfer(spi_device, SST25XX_POLLED, 2, wbuf, NULL,
+                                 false);
+    cyg_spi_transaction_end(spi_device);
+}
+
+//-----------------------------------------------------------------------------
+// Implement reads to the specified buffer.
+
+static inline void
+sst25xx_spi_read(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] = { SST25XX_CMD_READ_OPCODE,
+        (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, SST25XX_POLLED,
+                                 SST25XX_CMD_READ_LEN, tx_buf, NULL, false);
+    cyg_spi_transaction_transfer(spi_device, SST25XX_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.
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Initialize the SPI flash, reading back the flash parameters.
+
+static int
+sst25xx_init(struct cyg_flash_dev *dev)
+{
+    sst25xx_params *dev_params = (sst25xx_params *) sst25xx_supported_devices;
+    cyg_uint32      device_id;
+    cyg_uint8       i = 0;
+    int             retval = FLASH_ERR_INVALID;
+
+    // Find the device in the supported devices list.
+    device_id = sst25xx_spi_rdid(dev);
+
+    while ((dev_params->jedec_id != 0) && (dev_params->jedec_id != device_id)) {
+        dev_params++;
+    }
+
+    // Found supported device - update device parameters.  SST25XX devices have
+    // a uniform sector distribution, so only 1 block info record is required.
+    if (dev_params->jedec_id != 0) {
+
+        // Find out is the wanted sector size is supported by the device
+        while ((dev_params->sector_size[i] !=
+                CYGPKG_DEVS_FLASH_SPI_SST25XX_BLOCK_SIZE)
+               && (dev_params->sector_size[i] != 0))
+            i++;
+
+        if (dev_params->sector_size[i] !=
+            CYGPKG_DEVS_FLASH_SPI_SST25XX_BLOCK_SIZE) {
+            TRACE_SST25XX("Init device with JEDEC ID 0x%06X\n",
+                          dev_params->jedec_id);
+            TRACE_SST25XX("SPI Flash Error, not supporting %dK sector size\n",
+                          CYGPKG_DEVS_FLASH_SPI_SST25XX_BLOCK_SIZE);
+            return retval;
+        }
+
+        ASSERT_SST25XX(dev->num_block_infos == 1,
+                       "Only 1 block info record required.");
+        ASSERT_SST25XX(dev->block_info != NULL,
+                       "Null pointer to block info record.");
+
+        if ((dev->num_block_infos == 1) && (dev->block_info != NULL)) {
+            TRACE_SST25XX("Init device with JEDEC ID 0x%06X.\n", device_id);
+            dev->end =
+                dev->start +
+                ((cyg_flashaddr_t) dev_params->sector_size[i] *
+                 (cyg_flashaddr_t) (dev_params->sector_count[i])) - 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 =
+                (size_t)(dev_params->sector_size[i]);
+            ((cyg_flash_block_info_t *) dev->block_info)->blocks =
+                (cyg_uint32)(dev_params->sector_count[i]);
+
+            retval = FLASH_ERR_OK;
+        }
+
+    }
+    return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Erase a single sector of the flash.
+
+static int
+sst25xx_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 (sst25xx_to_local_addr(dev, &local_base)) {
+        sst25xx_spi_wren(dev);
+        sst25xx_spi_se(dev, local_base);
+
+        // Spin waiting for the erase to complete. Erasing takes around
+        // 25 ms
+        do {
+            SST25XX_DELAY_MS(10);
+            dev_status = sst25xx_spi_rdsr(dev);
+        } while (dev_status & SST25XX_STATUS_BSY);
+
+        retval = FLASH_ERR_OK;
+    }
+    return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Program an arbitrary number of pages into flash and verify written data.
+
+static int
+sst25xx_program(struct cyg_flash_dev *dev, cyg_flashaddr_t base,
+                const void *data, size_t len)
+{
+    cyg_uint8      *tx_ptr = (cyg_uint8 *)data;
+    cyg_flashaddr_t local_base = base;
+    cyg_flashaddr_t end;
+    size_t          tx_len = len;
+    cyg_bool        start_wxfer = true;
+    int             retval = FLASH_ERR_OK;
+
+    // Fix up the block address.
+    if (!sst25xx_to_local_addr(dev, &local_base)) {
+        retval = FLASH_ERR_INVALID;
+        goto out;
+    }
+    end = local_base + len - 1;
+
+    // Start with a byte programming if the destination is not
+    // 16 bits aligned
+    if (local_base & (cyg_flashaddr_t) 0x1) {
+        sst25xx_spi_wren(dev);
+        sst25xx_spi_pbyte(dev, local_base, tx_ptr);
+        SST25XX_WAIT_CHIP_READY(dev);
+        sst25xx_spi_wrdis(dev);
+        tx_ptr += 1;
+        tx_len -= 1;
+        local_base = local_base + 1;
+    }
+    // Write word(s)
+    if (tx_len > 1) {
+        sst25xx_spi_wren(dev);
+        while (tx_len > 1) {
+            sst25xx_spi_pword(dev, local_base, tx_ptr, start_wxfer);
+            tx_ptr += 2;
+            tx_len -= 2;
+            start_wxfer = false;
+            SST25XX_WAIT_CHIP_READY(dev);
+        }
+        sst25xx_spi_wrdis(dev);
+    }
+    // Write last byte
+    if (tx_len) {
+        sst25xx_spi_wren(dev);
+        sst25xx_spi_pbyte(dev, end, tx_ptr);
+        SST25XX_WAIT_CHIP_READY(dev);
+        sst25xx_spi_wrdis(dev);
+    }
+
+out:
+    return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Read back an arbitrary amount of data from flash.
+
+static int
+sst25xx_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_SST25XX_READ_BLOCK_SIZE ==
+         0) ? 0xFFFFFFFF : CYGNUM_DEVS_FLASH_SPI_SST25XX_READ_BLOCK_SIZE;
+
+    // Fix up the block address and fill the read buffer.
+    if (sst25xx_to_local_addr(dev, &local_base)) {
+        while (rx_bytes_left) {
+            rx_bytes =
+                (rx_bytes_left <
+                 rx_block_size) ? rx_bytes_left : rx_block_size;
+            sst25xx_spi_read(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;
+}
+
+//-----------------------------------------------------------------------------
+// Lock device
+
+static int
+sst25xx_lock(struct cyg_flash_dev *dev, cyg_flashaddr_t base)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    cyg_flashaddr_t local_base = base;
+    int             retval = FLASH_ERR_INVALID;
+    const cyg_uint8 tx_buf[2] = { SST25XX_CMD_WRSR, SST25XX_LOCK_BITS };
+
+    if (sst25xx_to_local_addr(dev, &local_base)) {
+        sst25xx_spi_wren(dev);
+        cyg_spi_transfer(spi_device, SST25XX_POLLED, 2, tx_buf, NULL);
+        retval = FLASH_ERR_OK;
+    }
+    return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Unlock device
+
+static int
+sst25xx_unlock(struct cyg_flash_dev *dev, cyg_flashaddr_t base)
+{
+    cyg_spi_device *spi_device = (cyg_spi_device *) dev->priv;
+    cyg_flashaddr_t local_base = base;
+    int             retval = FLASH_ERR_INVALID;
+    const cyg_uint8 tx_buf[2] = { SST25XX_CMD_WRSR, 0x0 };
+
+    if (sst25xx_to_local_addr(dev, &local_base)) {
+        sst25xx_spi_wren(dev);
+        cyg_spi_transfer(spi_device, SST25XX_POLLED, 2, tx_buf, NULL);
+        retval = FLASH_ERR_OK;
+    }
+    return retval;
+}
+
+//=============================================================================
+// Fill in the driver data structures.
+//=============================================================================
+
+CYG_FLASH_FUNS (
+    cyg_devs_flash_spi_sst25xx_funs, // Exported name of function pointers.
+    sst25xx_init,                    // Flash initialization.
+    cyg_flash_devfn_query_nop,       // Query operations not supported.
+    sst25xx_erase_block,             // Sector erase.
+    sst25xx_program,                 // Program multiple pages.
+    sst25xx_read,                    // Read arbitrary amount of data.
+    sst25xx_lock,                    // Locking (lock the whole device).
+    sst25xx_unlock
+);
+
+//-----------------------------------------------------------------------------
+// EOF sst25xx.c