changeset 2756:80f88e64f2ff

* cdl/spi_stm32.cdl: * include/spi_stm32.h: * src/spi_stm32.c: * src/spi_stm32_init.cxx: New package, STM32 SPI driver contributed by Chris Holgate.
author nickg
date Wed, 04 Feb 2009 11:42:34 +0000
parents f935377c1228
children da63b717cc17
files packages/devs/spi/cortexm/stm32/current/ChangeLog packages/devs/spi/cortexm/stm32/current/cdl/spi_stm32.cdl packages/devs/spi/cortexm/stm32/current/include/spi_stm32.h packages/devs/spi/cortexm/stm32/current/src/spi_stm32.c packages/devs/spi/cortexm/stm32/current/src/spi_stm32_init.cxx packages/devs/spi/cortexm/stm32/current/tests/loopback.c
diffstat 6 files changed, 1419 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/devs/spi/cortexm/stm32/current/ChangeLog
@@ -0,0 +1,31 @@
+2009-01-30  Nick Garnett  <nickg@ecoscentric.com>
+
+	* cdl/spi_stm32.cdl: 
+	* include/spi_stm32.h: 
+	* src/spi_stm32.c: 
+	* src/spi_stm32_init.cxx:
+	New package, STM32 SPI driver contributed by Chris Holgate.
+
+//===========================================================================
+// ####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/spi/cortexm/stm32/current/cdl/spi_stm32.cdl
@@ -0,0 +1,211 @@
+##=============================================================================
+##
+##      spi_stm32.cdl
+##
+##      STM32 SPI 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-11-27
+## Purpose:     Configure STM32 SPI driver.
+##
+######DESCRIPTIONEND####
+##
+##=============================================================================
+
+cdl_package CYGPKG_DEVS_SPI_CORTEXM_STM32 {
+    display       "ST STM32 SPI driver"
+    description   "
+        This package provides SPI driver support for the ST STM32 series
+        of microcontrollers.
+    "
+    parent        CYGPKG_IO_SPI
+    active_if     CYGPKG_IO_SPI
+    requires      CYGPKG_HAL_CORTEXM_STM32 
+    hardware
+    include_dir   cyg/io
+    compile       spi_stm32.c
+    compile       -library=libextras.a spi_stm32_init.cxx
+
+cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_PIN_TOGGLE_RATE {
+    display       "Pin toggle rate"
+    description   "
+        Selects the pin toggle rate in MHz to be used for the SPI interfaces.  Higher toggle
+        rates allow increased baud rates at the expense of power and EMI.
+    "
+    flavor        data
+    default_value { 2 }
+    legal_values  { 2 10 50 }
+}
+
+cdl_component CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1 {
+    display       "ST STM32 SPI bus 1"
+    description   "
+        Enable SPI bus 1 on the STM32 device.
+    "
+    flavor        bool
+    default_value false
+
+    cdl_option CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1_CS_GPIOS {
+        display       "SPI chip selects"
+        description   "
+            This is a comma separated list of GPIOs which are to be used as chip
+            select lines for the SPI bus.  For the purposes of specifying
+            which GPIOs to use, they are numbered consecutively from 0 (A0)
+            through 16 (B0) to 111 (G15).
+        "
+        flavor        data
+        default_value { "4" }
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE {
+        display       "Bounce buffer size"
+        description   "
+            DMA bounce buffers are required when running out of external
+            memory.  Set this to the maximum SPI packet size which will be
+            used to enable the DMA bounce buffers.  Set to 0 to disable
+            bounce buffers when running from on-chip memory.
+        "
+        flavor        data 
+        default_value 0
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_TXINTR_PRI {
+        display       "Transmit DMA interrupt priority"
+        flavor        data
+        default_value 5
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_RXINTR_PRI {
+        display       "Receive DMA interrupt priority"
+        flavor        data
+        default_value 6
+    }
+}
+
+cdl_component CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2 {
+    display       "ST STM32 SPI bus 2"
+    description   "
+        Enable SPI bus 2 on the STM32 device.
+    "
+    flavor        bool
+    default_value false
+
+    cdl_option CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2_CS_GPIOS {
+        display       "SPI chip selects"
+        description   "
+            This is a comma separated list of GPIOs which are to be used as chip
+            select lines for the SPI bus.  For the purposes of specifying
+            which GPIOs to use, they are numbered consecutively from 0 (A0)
+            through 16 (B0) to 111 (G15).
+        "
+        flavor        data
+        default_value { "28" }
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE {
+        display       "Bounce buffer size"
+        description   "
+            DMA bounce buffers are required when running out of external
+            memory.  Set this to the maximum SPI packet size which will be
+            used to enable the DMA bounce buffers.  Set to 0 to disable
+            bounce buffers when running from on-chip memory.
+        "
+        flavor        data 
+        default_value 0
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_TXINTR_PRI {
+        display       "Transmit DMA interrupt priority"
+        flavor        data
+        default_value 7
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_RXINTR_PRI {
+        display       "Receive DMA interrupt priority"
+        flavor        data
+        default_value 8
+    }
+}
+
+cdl_component CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3 {
+    display       "ST STM32 SPI bus 3"
+    description   "
+        Enable SPI bus 3 on the STM32 device.
+    "
+    flavor        bool
+    default_value false
+
+    cdl_option CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3_CS_GPIOS {
+        display       "SPI chip selects"
+        description   "
+            This is a comma separated list of GPIOs which are to be used as chip
+            select lines for the SPI bus.  For the purposes of specifying
+            which GPIOs to use, they are numbered consecutively from 0 (A0)
+            through 16 (B0) to 111 (G15).
+        "
+        flavor        data
+        default_value { "15" }
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE {
+        display       "Bounce buffer size"
+        description   "
+            DMA bounce buffers are required when running out of external
+            memory.  Set this to the maximum SPI packet size which will be
+            used to enable the DMA bounce buffers.  Set to 0 to disable
+            bounce buffers when running from on-chip memory.
+        "
+        flavor        data 
+        default_value 0
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_TXINTR_PRI {
+        display       "Transmit DMA interrupt priority"
+        flavor        data
+        default_value 9
+    }
+
+    cdl_option CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_RXINTR_PRI {
+        display       "Receive DMA interrupt priority"
+        flavor        data
+        default_value 10
+    }
+}
+
+}
+# EOF spi_stm32.cdl
new file mode 100644
--- /dev/null
+++ b/packages/devs/spi/cortexm/stm32/current/include/spi_stm32.h
@@ -0,0 +1,145 @@
+#ifndef CYGONCE_DEVS_SPI_CORTEXM_STM32_H
+#define CYGONCE_DEVS_SPI_CORTEXM_STM32_H
+//=============================================================================
+//
+//      spi_stm32.h
+//
+//      Header definitions for STM32 SPI driver.
+//
+//=============================================================================
+// ####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-11-27
+// Purpose:     STM32 SPI driver definitions.
+// Description: 
+// Usage:       #include <cyg/io/spi_stm32.h>
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+#include <pkgconf/hal.h>
+#include <pkgconf/io_spi.h>
+#include <pkgconf/devs_spi_cortexm_stm32.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/hal/drv_api.h>
+#include <cyg/io/spi.h>
+
+//-----------------------------------------------------------------------------
+// STM32 SPI bus configuration and state.
+
+typedef struct cyg_spi_cortexm_stm32_bus_setup_s
+{
+    cyg_uint32        apb_freq;        // Peripheral bus frequency (fp).
+    cyg_haladdress    spi_reg_base;    // Base address of SPI register block.
+    cyg_haladdress    dma_reg_base;    // Base address of DMA register block.
+    cyg_uint8         dma_tx_channel;  // TX DMA channel for this bus.
+    cyg_uint8         dma_rx_channel;  // RX DMA channel for this bus.
+    cyg_uint8         cs_gpio_num;     // Number of chip selects for this bus.
+    const cyg_uint8*  cs_gpio_list;    // List of GPIOs used as chip selects.
+    const cyg_uint8*  spi_gpio_list;   // List of GPIOs used by the SPI interface.
+    cyg_vector_t      dma_tx_intr;     // Interrupt used for DMA transmit.
+    cyg_vector_t      dma_rx_intr;     // Interrupt used for DMA receive.
+    cyg_priority_t    dma_tx_intr_pri; // Interrupt priority for DMA transmit.
+    cyg_priority_t    dma_rx_intr_pri; // Interrupt priority for DMA receive.
+    cyg_uint32        bbuf_size;       // Size of bounce buffers.
+    cyg_uint8*        bbuf_tx;         // Pointer to transmit bounce buffer.
+    cyg_uint8*        bbuf_rx;         // Pointer to receive bounce buffer.
+
+} cyg_spi_cortexm_stm32_bus_setup_t;
+
+typedef struct cyg_spi_cortexm_stm32_bus_s
+{
+    // ---- Upper layer data ----
+    cyg_spi_bus       spi_bus;         // Upper layer SPI bus data.
+
+    // ---- Bus configuration constants ----
+    const cyg_spi_cortexm_stm32_bus_setup_t* setup;
+
+    // ---- Driver state (private) ----
+    cyg_interrupt     tx_intr_data;    // DMA interrupt data (TX).
+    cyg_interrupt     rx_intr_data;    // DMA interrupt data (RX).
+    cyg_handle_t      tx_intr_handle;  // DMA interrupt handle (TX).
+    cyg_handle_t      rx_intr_handle;  // DMA interrupt handle (RX).
+    cyg_drv_mutex_t   mutex;           // Transfer mutex.
+    cyg_drv_cond_t    condvar;         // Transfer condition variable.
+    cyg_bool          tx_dma_done;     // Flags used to signal completion.
+    cyg_bool          rx_dma_done;     // Flags used to signal completion.
+    cyg_bool          cs_up;           // Chip select asserted flag.
+
+} cyg_spi_cortexm_stm32_bus_t;
+
+//-----------------------------------------------------------------------------
+// STM32 SPI device.
+
+typedef struct cyg_spi_cortexm_stm32_device_s
+{
+    // ---- Upper layer data ----
+    cyg_spi_device spi_device;  // Upper layer SPI device data.
+
+    // ---- Device setup (user configurable) ----
+    cyg_uint8  dev_num;         // Device number.
+    cyg_uint8  bus_16bit;       // Use 16 bit (1) or 8 bit (0) transfers.
+    cyg_uint8  cl_pol;          // Clock polarity (0 or 1).
+    cyg_uint8  cl_pha;          // Clock phase    (0 or 1).
+    cyg_uint32 cl_brate;        // Clock baud rate.
+    cyg_uint16 cs_up_udly;      // Minimum delay in us between CS up and transfer start.
+    cyg_uint16 cs_dw_udly;      // Minimum delay in us between transfer end and CS down.
+    cyg_uint16 tr_bt_udly;      // Minimum delay in us between two transfers.
+
+    // ---- Device state (private) ----
+    cyg_uint32 spi_cr1_val;     // SPI configuration register (initialised to 0). 
+
+} cyg_spi_cortexm_stm32_device_t;
+
+//-----------------------------------------------------------------------------
+// Exported bus data structures.
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1
+externC cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus1;
+#endif
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2
+externC cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus2;
+#endif
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3
+externC cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus3;
+#endif
+
+//=============================================================================
+#endif // CYGONCE_DEVS_SPI_CORTEXM_STM32_H
new file mode 100644
--- /dev/null
+++ b/packages/devs/spi/cortexm/stm32/current/src/spi_stm32.c
@@ -0,0 +1,784 @@
+//=============================================================================
+//
+//      spi_stm32.c
+//
+//      SPI driver implementation for STM32
+//
+//=============================================================================
+// ####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-11-27
+// Purpose:     STM32 SPI driver implementation
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+#include <cyg/hal/hal_io.h>
+#include <cyg/hal/hal_if.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/drv_api.h>
+
+#include <cyg/infra/cyg_type.h>
+#include <cyg/infra/cyg_ass.h>
+#include <cyg/infra/diag.h>
+
+#include <cyg/io/spi.h>
+#include <cyg/io/spi_stm32.h>
+
+#include <pkgconf/devs_spi_cortexm_stm32.h>
+
+#include <string.h>
+
+//-----------------------------------------------------------------------------
+// Work out the bus clock frequencies.
+
+#define APB1_FREQ ((CYGARC_HAL_CORTEXM_STM32_INPUT_CLOCK * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PLL_MUL) / \
+  (CYGHWR_HAL_CORTEXM_STM32_CLOCK_HCLK_DIV * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PCLK1_DIV)) 
+#define APB2_FREQ ((CYGARC_HAL_CORTEXM_STM32_INPUT_CLOCK * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PLL_MUL) / \
+  (CYGHWR_HAL_CORTEXM_STM32_CLOCK_HCLK_DIV * CYGHWR_HAL_CORTEXM_STM32_CLOCK_PCLK2_DIV)) 
+
+//-----------------------------------------------------------------------------
+// API function call forward references.
+
+static void stm32_transaction_begin    (cyg_spi_device*);
+static void stm32_transaction_transfer (cyg_spi_device*, cyg_bool, cyg_uint32, const cyg_uint8*, cyg_uint8*, cyg_bool);
+static void stm32_transaction_tick     (cyg_spi_device*, cyg_bool, cyg_uint32);
+static void stm32_transaction_end      (cyg_spi_device*);
+static int  stm32_get_config           (cyg_spi_device*, cyg_uint32, void*, cyg_uint32*);
+static int  stm32_set_config           (cyg_spi_device*, cyg_uint32, const void*, cyg_uint32*);
+
+//-----------------------------------------------------------------------------
+// Null data source and sink must be placed in the on-chip SRAM.  This is
+// either done explicitly (bounce buffers instantiated) or implicitly (no
+// bounce buffers implies that the data area is already on SRAM).
+
+#if (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1) && (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE > 0)) || \
+  (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2) && (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE > 0)) || \
+  (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3) && (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE > 0))
+static cyg_uint16 dma_tx_null __attribute__((section (".sram"))) = 0xFFFF;
+static cyg_uint16 dma_rx_null __attribute__((section (".sram"))) = 0xFFFF;
+
+#elif (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1)) || \
+  (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2)) || \
+  (defined (CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3))
+static cyg_uint16 dma_tx_null = 0xFFFF;
+static cyg_uint16 dma_rx_null = 0xFFFF;
+#endif
+
+//-----------------------------------------------------------------------------
+// Instantiate the bus state data structures.
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1
+static const cyg_uint8 bus1_cs_gpio_list[] = { CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1_CS_GPIOS };
+static const cyg_uint8 bus1_spi_gpio_list[] = { 0x05, 0x06, 0x07 };
+
+#if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE > 0)
+static cyg_uint8 bus1_tx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE] 
+  __attribute__((aligned (2), section (".sram"))) = { 0 };
+static cyg_uint8 bus1_rx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE] 
+  __attribute__((aligned (2), section (".sram"))) = { 0 };
+#endif
+
+static const cyg_spi_cortexm_stm32_bus_setup_t bus1_setup = {
+  .apb_freq                         = APB2_FREQ,
+  .spi_reg_base                     = CYGHWR_HAL_STM32_SPI1,
+  .dma_reg_base                     = CYGHWR_HAL_STM32_DMA1,
+  .dma_tx_channel                   = 3,
+  .dma_rx_channel                   = 2,
+  .cs_gpio_num                      = sizeof (bus1_cs_gpio_list),
+  .cs_gpio_list                     = bus1_cs_gpio_list,
+  .spi_gpio_list                    = bus1_spi_gpio_list,
+  .dma_tx_intr                      = CYGNUM_HAL_INTERRUPT_DMA1_CH3,
+  .dma_rx_intr                      = CYGNUM_HAL_INTERRUPT_DMA1_CH2,
+  .dma_tx_intr_pri                  = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_TXINTR_PRI,
+  .dma_rx_intr_pri                  = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_RXINTR_PRI,
+#if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE > 0)
+  .bbuf_size                        = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS1_BBUF_SIZE,
+  .bbuf_tx                          = bus1_tx_bbuf,
+  .bbuf_rx                          = bus1_rx_bbuf,
+#else
+  .bbuf_size                        = 0,
+#endif
+};
+
+cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus1 = {
+  .spi_bus.spi_transaction_begin    = stm32_transaction_begin,
+  .spi_bus.spi_transaction_transfer = stm32_transaction_transfer,
+  .spi_bus.spi_transaction_tick     = stm32_transaction_tick,
+  .spi_bus.spi_transaction_end      = stm32_transaction_end,
+  .spi_bus.spi_get_config           = stm32_get_config,
+  .spi_bus.spi_set_config           = stm32_set_config,
+  .setup                            = &bus1_setup,
+  .cs_up                            = false
+};
+#endif
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2
+static const cyg_uint8 bus2_cs_gpio_list[] = { CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2_CS_GPIOS };
+static const cyg_uint8 bus2_spi_gpio_list[] = { 0x1D, 0x1E, 0x1F };
+
+#if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE > 0)
+static cyg_uint8 bus2_tx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE] 
+  __attribute__((aligned (2), section (".sram"))) = { 0 };
+static cyg_uint8 bus2_rx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE] 
+  __attribute__((aligned (2), section (".sram"))) = { 0 };
+#endif
+
+static const cyg_spi_cortexm_stm32_bus_setup_t bus2_setup = {
+  .apb_freq                         = APB1_FREQ,
+  .spi_reg_base                     = CYGHWR_HAL_STM32_SPI2,
+  .dma_reg_base                     = CYGHWR_HAL_STM32_DMA1,
+  .dma_tx_channel                   = 5,
+  .dma_rx_channel                   = 4,
+  .cs_gpio_num                      = sizeof (bus2_cs_gpio_list),
+  .cs_gpio_list                     = bus2_cs_gpio_list,
+  .spi_gpio_list                    = bus2_spi_gpio_list,
+  .dma_tx_intr                      = CYGNUM_HAL_INTERRUPT_DMA1_CH5,
+  .dma_rx_intr                      = CYGNUM_HAL_INTERRUPT_DMA1_CH4,
+  .dma_tx_intr_pri                  = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_TXINTR_PRI,
+  .dma_rx_intr_pri                  = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_RXINTR_PRI,
+#if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE > 0)
+  .bbuf_size                        = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS2_BBUF_SIZE,
+  .bbuf_tx                          = bus2_tx_bbuf,
+  .bbuf_rx                          = bus2_rx_bbuf,
+#else
+  .bbuf_size                        = 0,
+#endif
+};
+
+cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus2 = {
+  .spi_bus.spi_transaction_begin    = stm32_transaction_begin,
+  .spi_bus.spi_transaction_transfer = stm32_transaction_transfer,
+  .spi_bus.spi_transaction_tick     = stm32_transaction_tick,
+  .spi_bus.spi_transaction_end      = stm32_transaction_end,
+  .spi_bus.spi_get_config           = stm32_get_config,
+  .spi_bus.spi_set_config           = stm32_set_config,
+  .setup                            = &bus2_setup,
+  .cs_up                            = false
+};
+#endif
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3
+static const cyg_uint8 bus3_cs_gpio_list[] = { CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3_CS_GPIOS };
+static const cyg_uint8 bus3_spi_gpio_list[] = { 0x13, 0x14, 0x15 };
+
+#if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE > 0)
+static cyg_uint8 bus3_tx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE] 
+  __attribute__((aligned (2), section (".sram"))) = { 0 };
+static cyg_uint8 bus3_rx_bbuf [CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE] 
+  __attribute__((alugned (2), section (".sram"))) = { 0 };
+#endif
+
+static const cyg_spi_cortexm_stm32_bus_setup_t bus3_setup = {
+  .apb_freq                         = APB1_FREQ,
+  .spi_reg_base                     = CYGHWR_HAL_STM32_SPI3,
+  .dma_reg_base                     = CYGHWR_HAL_STM32_DMA2,
+  .dma_tx_channel                   = 2,
+  .dma_rx_channel                   = 1
+  .cs_gpio_num                      = sizeof (bus3_cs_gpio_list),
+  .cs_gpio_list                     = bus3_cs_gpio_list,
+  .spi_gpio_list                    = bus3_spi_gpio_list,
+  .dma_tx_intr                      = CYGNUM_HAL_INTERRUPT_DMA2_CH2,
+  .dma_rx_intr                      = CYGNUM_HAL_INTERRUPT_DMA2_CH1,
+  .dma_tx_intr_pri                  = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_TXINTR_PRI,
+  .dma_rx_intr_pri                  = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_RXINTR_PRI,
+#if (CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE > 0)
+  .bbuf_size                        = CYGNUM_DEVS_SPI_CORTEXM_STM32_BUS3_BBUF_SIZE,
+  .bbuf_tx                          = bus3_tx_bbuf,
+  .bbuf_rx                          = bus3_rx_bbuf,
+#else
+  .bbuf_size                        = 0,
+#endif
+};
+
+cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus3 = {
+  .spi_bus.spi_transaction_begin    = stm32_transaction_begin,
+  .spi_bus.spi_transaction_transfer = stm32_transaction_transfer,
+  .spi_bus.spi_transaction_tick     = stm32_transaction_tick,
+  .spi_bus.spi_transaction_end      = stm32_transaction_end,
+  .spi_bus.spi_get_config           = stm32_get_config,
+  .spi_bus.spi_set_config           = stm32_set_config,
+  .setup                            = &bus3_setup,
+  .cs_up                            = false
+};
+#endif
+
+//-----------------------------------------------------------------------------
+// Useful GPIO macros for 'dynamic' pin setup.
+
+static const cyg_uint32 stm32_gpio_port_offsets[] = {
+  CYGHWR_HAL_STM32_GPIOA - CYGHWR_HAL_STM32_GPIOA,
+  CYGHWR_HAL_STM32_GPIOB - CYGHWR_HAL_STM32_GPIOA,
+  CYGHWR_HAL_STM32_GPIOC - CYGHWR_HAL_STM32_GPIOA,
+  CYGHWR_HAL_STM32_GPIOD - CYGHWR_HAL_STM32_GPIOA,
+  CYGHWR_HAL_STM32_GPIOE - CYGHWR_HAL_STM32_GPIOA,
+  CYGHWR_HAL_STM32_GPIOF - CYGHWR_HAL_STM32_GPIOA,
+  CYGHWR_HAL_STM32_GPIOG - CYGHWR_HAL_STM32_GPIOA
+};
+
+#define STM32_GPIO_PINSPEC(__port, __bit, __mode, __cnf )          \
+  (stm32_gpio_port_offsets[__port] | (__bit<<16) |                 \
+  (CYGHWR_HAL_STM32_GPIO_MODE_##__mode) |                          \
+  (CYGHWR_HAL_STM32_GPIO_CNF_##__cnf))
+
+#if (CYGNUM_DEVS_SPI_CORTEXM_STM32_PIN_TOGGLE_RATE == 2)
+#define CYGHWR_HAL_STM32_GPIO_MODE_OUT_SPI CYGHWR_HAL_STM32_GPIO_MODE_OUT_2MHZ
+
+#elif (CYGNUM_DEVS_SPI_CORTEXM_STM32_PIN_TOGGLE_RATE == 10)
+#define CYGHWR_HAL_STM32_GPIO_MODE_OUT_SPI CYGHWR_HAL_STM32_GPIO_MODE_OUT_10MHZ
+
+#elif (CYGNUM_DEVS_SPI_CORTEXM_STM32_PIN_TOGGLE_RATE == 50)
+#define CYGHWR_HAL_STM32_GPIO_MODE_OUT_SPI CYGHWR_HAL_STM32_GPIO_MODE_OUT_50MHZ
+
+#else
+#error "Invalid SPI bus toggle rate."
+#endif
+
+//-----------------------------------------------------------------------------
+// Configure a GPIO pin as a SPI chip select line.
+
+static inline void stm32_spi_gpio_cs_setup
+  (cyg_uint32 gpio_num)
+{
+  cyg_uint32 port, pin, pinspec;
+
+  // Check that the pin number is in range (16 pins per port).
+  pin = gpio_num & 0xF;
+  port = gpio_num >> 4;
+  CYG_ASSERT (port < 7, "STM32 SPI : Invalid GPIO number in chip select list."); 
+
+  // Generate the pin setup specification and configure it.
+  pinspec = STM32_GPIO_PINSPEC (port, pin, OUT_SPI, OUT_PUSHPULL);
+  CYGHWR_HAL_STM32_GPIO_SET (pinspec);
+  CYGHWR_HAL_STM32_GPIO_OUT (pinspec, 1);
+}
+
+//-----------------------------------------------------------------------------
+// Drive a GPIO pin as a SPI chip select line.
+
+static inline void stm32_spi_chip_select
+  (cyg_uint32 gpio_num, cyg_bool assert)
+{
+  cyg_uint32 port, pin, pinspec;
+
+  // Check that the pin number is in range (16 pins per port).
+  pin = gpio_num & 0xF;
+  port = gpio_num >> 4;
+  CYG_ASSERT (port < 7, "STM32 SPI : Invalid GPIO number in chip select list."); 
+
+  // Generate the pin setup specification and drive it.
+  pinspec = STM32_GPIO_PINSPEC (port, pin, OUT_SPI, OUT_PUSHPULL);
+  CYGHWR_HAL_STM32_GPIO_OUT (pinspec, assert ? 0 : 1);
+}
+
+//-----------------------------------------------------------------------------
+// Implement DMA ISRs.  These disable the DMA channel, mask the interrupt 
+// condition and schedule their respective DSRs.
+
+static cyg_uint32 stm32_tx_ISR 
+  (cyg_vector_t vector, cyg_addrword_t data)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data;
+  cyg_uint32 chan = stm32_bus->setup->dma_tx_channel;
+  cyg_haladdress reg_addr;
+
+  // Disable the DMA channel.
+  cyg_drv_isr_lock ();
+  reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan);
+  HAL_WRITE_UINT32 (reg_addr, 0);
+
+  // Clear down the interrupts.
+  reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_IFCR;
+  HAL_WRITE_UINT32 (reg_addr, CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan));
+
+  cyg_drv_interrupt_acknowledge (vector);
+  cyg_drv_interrupt_mask (vector);
+  cyg_drv_isr_unlock ();
+
+  return (CYG_ISR_CALL_DSR | CYG_ISR_HANDLED);
+}
+
+static cyg_uint32 stm32_rx_ISR 
+  (cyg_vector_t vector, cyg_addrword_t data)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data;
+  cyg_uint32 chan = stm32_bus->setup->dma_rx_channel;
+  cyg_haladdress reg_addr;
+
+  // Disable the DMA channel.
+  cyg_drv_isr_lock ();
+  reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan);
+  HAL_WRITE_UINT32 (reg_addr, 0);
+
+  // Clear down the interrupts.
+  reg_addr = stm32_bus->setup->dma_reg_base + CYGHWR_HAL_STM32_DMA_IFCR;
+  HAL_WRITE_UINT32 (reg_addr, CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan));
+
+  cyg_drv_interrupt_acknowledge (vector);
+  cyg_drv_interrupt_mask (vector);
+  cyg_drv_isr_unlock ();
+
+  return (CYG_ISR_CALL_DSR | CYG_ISR_HANDLED);
+}
+
+//-----------------------------------------------------------------------------
+// Implement DMA DSRs.  These clear down the interrupt conditions and assert 
+// their respective 'transaction complete' flags.
+
+static void stm32_tx_DSR 
+  (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data;
+
+  cyg_drv_dsr_lock ();
+  stm32_bus->tx_dma_done = true;
+  cyg_drv_cond_signal (&stm32_bus->condvar);
+  cyg_drv_dsr_unlock ();
+}
+
+static void stm32_rx_DSR 
+  (cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) data;
+ 
+  cyg_drv_dsr_lock ();
+  stm32_bus->rx_dma_done = true;
+  cyg_drv_cond_signal (&stm32_bus->condvar);
+  cyg_drv_dsr_unlock ();
+}
+
+//-----------------------------------------------------------------------------
+// Set up a new SPI bus on initialisation.
+
+static void stm32_spi_bus_setup 
+  (cyg_spi_cortexm_stm32_bus_t* stm32_bus)
+{
+  int i;
+  cyg_haladdress reg_addr;
+  cyg_uint32 pin, pinspec, reg_data;
+
+  // Set up the GPIOs for use as chip select lines.
+  for (i = 0; i < stm32_bus->setup->cs_gpio_num; i ++) {
+    stm32_spi_gpio_cs_setup (stm32_bus->setup->cs_gpio_list[i]);
+  }
+
+  // Configure the SPI clock output pin.
+  pin = stm32_bus->setup->spi_gpio_list[0];
+  pinspec = STM32_GPIO_PINSPEC ((pin >> 4), (pin & 0xF), OUT_SPI, ALT_PUSHPULL);
+  CYGHWR_HAL_STM32_GPIO_SET (pinspec);
+
+  // Configure the SPI MISO input.
+  pin = stm32_bus->setup->spi_gpio_list[1];
+  pinspec = STM32_GPIO_PINSPEC ((pin >> 4), (pin & 0xF), IN, PULLUP);
+  CYGHWR_HAL_STM32_GPIO_SET (pinspec);
+
+  // Configure the SPI MOSI output.  
+  pin = stm32_bus->setup->spi_gpio_list[2];
+  pinspec = STM32_GPIO_PINSPEC ((pin >> 4), (pin & 0xF), OUT_SPI, ALT_PUSHPULL);
+  CYGHWR_HAL_STM32_GPIO_SET (pinspec);
+
+  // Set up SPI default configuration.
+  reg_addr = stm32_bus->setup->spi_reg_base + CYGHWR_HAL_STM32_SPI_CR2;
+  reg_data = CYGHWR_HAL_STM32_SPI_CR2_TXDMAEN | CYGHWR_HAL_STM32_SPI_CR2_RXDMAEN;
+  HAL_WRITE_UINT32 (reg_addr, reg_data);
+
+  // Ensure that the DMA clocks are enabled.
+  reg_addr = CYGHWR_HAL_STM32_RCC + CYGHWR_HAL_STM32_RCC_AHBENR;
+  HAL_READ_UINT32 (reg_addr, reg_data);
+  if (stm32_bus->setup->dma_reg_base == CYGHWR_HAL_STM32_DMA1)
+    reg_data |= CYGHWR_HAL_STM32_RCC_AHBENR_DMA1;
+  else
+    reg_data |= CYGHWR_HAL_STM32_RCC_AHBENR_DMA2;
+  HAL_WRITE_UINT32 (reg_addr, reg_data);
+
+  // Initialise the synchronisation primitivies.
+  cyg_drv_mutex_init (&stm32_bus->mutex);
+  cyg_drv_cond_init (&stm32_bus->condvar, &stm32_bus->mutex);
+
+  // Hook up the ISRs and DSRs.
+  cyg_drv_interrupt_create (stm32_bus->setup->dma_tx_intr, stm32_bus->setup->dma_tx_intr_pri, 
+    (cyg_addrword_t) stm32_bus, stm32_tx_ISR, stm32_tx_DSR, &stm32_bus->tx_intr_handle, 
+    &stm32_bus->tx_intr_data);
+  cyg_drv_interrupt_attach (stm32_bus->tx_intr_handle);
+
+  cyg_drv_interrupt_create (stm32_bus->setup->dma_rx_intr, stm32_bus->setup->dma_rx_intr_pri, 
+    (cyg_addrword_t) stm32_bus, stm32_rx_ISR, stm32_rx_DSR, &stm32_bus->rx_intr_handle, 
+    &stm32_bus->rx_intr_data);
+  cyg_drv_interrupt_attach (stm32_bus->rx_intr_handle);
+
+  // Call upper layer bus init.
+  CYG_SPI_BUS_COMMON_INIT(&stm32_bus->spi_bus);
+}
+
+//-----------------------------------------------------------------------------
+// Set up a DMA channel.
+
+static void dma_channel_setup
+  (cyg_spi_device* device, cyg_uint8* data_buf, cyg_uint32 count, cyg_bool is_tx, cyg_bool polled)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus;
+  cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device;
+
+  cyg_uint32 chan, reg_data;
+  cyg_haladdress dma_reg_base, spi_reg_base, dma_addr, reg_addr;
+
+  // Extract address and channel information.
+  dma_reg_base = stm32_bus->setup->dma_reg_base;
+  spi_reg_base = stm32_bus->setup->spi_reg_base;
+  chan = is_tx ? stm32_bus->setup->dma_tx_channel : stm32_bus->setup->dma_rx_channel;
+
+  // Default options for the DMA channel.
+  reg_data = CYGHWR_HAL_STM32_DMA_CCR_EN;
+
+  // Do not enable interrupts in polled mode.
+  if (!polled)
+    reg_data |= CYGHWR_HAL_STM32_DMA_CCR_TCIE | CYGHWR_HAL_STM32_DMA_CCR_TEIE;
+
+  // Set DMA channel direction and priority level.  The receive channel has higher
+  // priority so that the inbound buffer is always cleared first.
+  if (is_tx) 
+    reg_data |= CYGHWR_HAL_STM32_DMA_CCR_DIR | CYGHWR_HAL_STM32_DMA_CCR_PLMEDIUM;
+  else 
+    reg_data |= CYGHWR_HAL_STM32_DMA_CCR_PLHIGH;
+ 
+  // Set the correct transfer size.
+  if (stm32_device->bus_16bit)
+    reg_data |= CYGHWR_HAL_STM32_DMA_CCR_PSIZE16 | CYGHWR_HAL_STM32_DMA_CCR_MSIZE16;
+  else 
+    reg_data |= CYGHWR_HAL_STM32_DMA_CCR_PSIZE8 | CYGHWR_HAL_STM32_DMA_CCR_MSIZE8;
+
+  // Do not use memory address incrementing for dummy data.
+  if (data_buf != NULL) {
+    reg_data |= CYGHWR_HAL_STM32_DMA_CCR_MINC;
+    dma_addr = (cyg_haladdress) data_buf;
+  }
+  else 
+    dma_addr = (cyg_haladdress) (is_tx ? &dma_tx_null : &dma_rx_null);
+
+  // Program up the DMA memory address.
+  reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CMAR (chan);
+  HAL_WRITE_UINT32 (reg_addr, dma_addr);
+  
+  // Program up the peripheral memory address.
+  dma_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_DR;
+  reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CPAR (chan);
+  HAL_WRITE_UINT32 (reg_addr, dma_addr);
+
+  // Program up the data buffer size.
+  reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CNDTR (chan);
+  HAL_WRITE_UINT32 (reg_addr, count);
+
+  // Enable the DMA via the configuration register.
+  reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan);
+  HAL_WRITE_UINT32 (reg_addr, reg_data);
+}
+
+//-----------------------------------------------------------------------------
+// Initiate a DMA transfer over the SPI interface.
+
+static void spi_transaction_dma 
+  (cyg_spi_device* device, cyg_bool tick_only, cyg_bool polled, cyg_uint32 count, 
+  const cyg_uint8* tx_data, cyg_uint8* rx_data, cyg_bool drop_cs)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus;
+  cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device;
+
+  cyg_haladdress reg_addr;
+  cyg_uint32 chan, reg_data;
+
+  cyg_haladdress dma_reg_base = stm32_bus->setup->dma_reg_base;
+  cyg_haladdress spi_reg_base = stm32_bus->setup->spi_reg_base;
+
+  // Ensure the chip select is asserted, inserting inter-transaction guard 
+  // time if required.  Note that when ticking the device we do not touch the CS.
+  if (!stm32_bus->cs_up && !tick_only) {
+    CYGACC_CALL_IF_DELAY_US (stm32_device->tr_bt_udly);
+    stm32_spi_chip_select (stm32_bus->setup->cs_gpio_list[stm32_device->dev_num], true);        
+    stm32_bus->cs_up = true;
+    CYGACC_CALL_IF_DELAY_US (stm32_device->cs_up_udly);
+  }
+
+  // Set up the DMA channels.
+  dma_channel_setup (device, (cyg_uint8*) tx_data, count, true, polled);
+  dma_channel_setup (device, rx_data, count, false, polled);
+
+  // Run the DMA (polling for completion).
+  if (polled) {
+    cyg_bool transfer_done = false;
+
+    // Enable the SPI controller.
+    reg_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1;
+    HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val | CYGHWR_HAL_STM32_SPI_CR1_SPE);
+
+    // Spin waiting on both DMA status flags.  Trap bus errors and assert in
+    // debug builds or return garbage in production builds.
+    reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_ISR;
+    do {
+      HAL_READ_UINT32 (reg_addr, reg_data);
+      if ((reg_data & CYGHWR_HAL_STM32_DMA_ISR_TEIF (stm32_bus->setup->dma_tx_channel)) ||
+          (reg_data & CYGHWR_HAL_STM32_DMA_ISR_TEIF (stm32_bus->setup->dma_rx_channel))) {
+        CYG_ASSERT (false, "STM32 SPI : DMA bus fault - enable bounce buffers.");
+        transfer_done = true;
+      }
+      if ((reg_data & CYGHWR_HAL_STM32_DMA_ISR_TCIF (stm32_bus->setup->dma_tx_channel)) &&
+          (reg_data & CYGHWR_HAL_STM32_DMA_ISR_TCIF (stm32_bus->setup->dma_rx_channel))) {
+        transfer_done = true;
+      }
+    } while (!transfer_done);
+
+    // Disable the DMA channels on completion and clear the status flags.
+    chan = stm32_bus->setup->dma_tx_channel;
+    reg_data = CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan);
+    reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan);
+    HAL_WRITE_UINT32 (reg_addr, 0);
+
+    chan = stm32_bus->setup->dma_rx_channel;
+    reg_data |= CYGHWR_HAL_STM32_DMA_IFCR_MASK (chan);
+    reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_CCR (chan);
+    HAL_WRITE_UINT32 (reg_addr, 0);
+
+    reg_addr = dma_reg_base + CYGHWR_HAL_STM32_DMA_IFCR;
+    HAL_WRITE_UINT32 (reg_addr, reg_data);
+  }
+
+  // Run the DMA (interrupt driven).
+  else {
+    cyg_drv_mutex_lock (&stm32_bus->mutex);
+    cyg_drv_dsr_lock ();
+    stm32_bus->tx_dma_done = false;
+    stm32_bus->rx_dma_done = false;
+
+    cyg_drv_interrupt_unmask (stm32_bus->setup->dma_tx_intr);
+    cyg_drv_interrupt_unmask (stm32_bus->setup->dma_rx_intr);
+
+    // Enable the SPI controller.
+    reg_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1;
+    HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val | CYGHWR_HAL_STM32_SPI_CR1_SPE);
+
+    // Sit back and wait for the ISR/DSRs to signal completion.
+    do {
+      cyg_drv_cond_wait (&stm32_bus->condvar);
+    } while (!(stm32_bus->tx_dma_done && stm32_bus->rx_dma_done));
+
+    cyg_drv_dsr_unlock ();
+    cyg_drv_mutex_unlock (&stm32_bus->mutex);
+  }
+
+  // Disable the SPI controller.
+  reg_addr = spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1;
+  HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val);
+        
+  // Deassert the chip select.
+  if (drop_cs && !tick_only) {
+    CYGACC_CALL_IF_DELAY_US (stm32_device->cs_dw_udly);
+    stm32_spi_chip_select (stm32_bus->setup->cs_gpio_list[stm32_device->dev_num], false);        
+    stm32_bus->cs_up = false;
+  }     
+}
+
+//-----------------------------------------------------------------------------
+// Initialise SPI interfaces on startup.
+
+void cyg_spi_cortexm_stm32_init 
+  (void)
+{
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS1
+  stm32_spi_bus_setup (&cyg_spi_stm32_bus1);
+#endif
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS2
+  stm32_spi_bus_setup (&cyg_spi_stm32_bus2);
+#endif
+
+#ifdef CYGHWR_DEVS_SPI_CORTEXM_STM32_BUS3
+  stm32_spi_bus_setup (&cyg_spi_stm32_bus3);
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Start a SPI transaction.
+
+static void stm32_transaction_begin    
+  (cyg_spi_device* device)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus;
+  cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device;
+
+  cyg_haladdress reg_addr;
+  cyg_uint32 reg_data, divided_clk, br;
+
+  // On the first transaction, generate the values to be programmed into the
+  // SPI configuration registers for this device and cache them.  This avoids
+  // having to recalculate the prescaler for every transaction.
+  if (!stm32_device->spi_cr1_val) {
+    reg_data = CYGHWR_HAL_STM32_SPI_CR1_MSTR | 
+      CYGHWR_HAL_STM32_SPI_CR1_SSI | CYGHWR_HAL_STM32_SPI_CR1_SSM;
+    if (stm32_device->cl_pol)
+      reg_data |= CYGHWR_HAL_STM32_SPI_CR1_CPOL;
+    if (stm32_device->cl_pha)
+      reg_data |= CYGHWR_HAL_STM32_SPI_CR1_CPHA;
+    if (stm32_device->bus_16bit)
+      reg_data |= CYGHWR_HAL_STM32_SPI_CR1_DFF;
+
+    // Calculate the maximum viable bus speed.
+    divided_clk = stm32_bus->setup->apb_freq / 2;
+    for (br = 0; (br < 7) && (divided_clk > stm32_device->cl_brate); br++)
+      divided_clk >>= 1;
+    CYG_ASSERT (divided_clk <= stm32_device->cl_brate, 
+      "STM32 SPI : Cannot run bus slowly enough for peripheral.");
+    reg_data |= CYGHWR_HAL_STM32_SPI_CR1_BR (br);
+
+    // Cache the configuration register settings.
+    stm32_device->spi_cr1_val = reg_data;
+  }
+
+  // Set up the SPI controller.
+  reg_addr = stm32_bus->setup->spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1;
+  HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val);
+}
+
+//-----------------------------------------------------------------------------
+// Run a transaction transfer.
+
+static void stm32_transaction_transfer 
+  (cyg_spi_device* device, cyg_bool polled, cyg_uint32 count, 
+  const cyg_uint8* tx_data, cyg_uint8* rx_data, cyg_bool drop_cs)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus;
+  cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device;
+
+  // Check for unsupported transactions.
+  CYG_ASSERT (count > 0, "STM32 SPI : Null transfer requested.");
+
+  // We check that the buffers are half-word aligned and that count is a 
+  // multiple of two in order to carry out the 16-bit transfer.
+  if (stm32_device->bus_16bit) {
+    CYG_ASSERT (!(count & 1) && !((cyg_uint32) tx_data & 1) && !((cyg_uint32) rx_data & 1),   
+      "STM32 SPI : Misaligned data in 16-bit transfer.");
+  }
+
+  // Perform transfer via the bounce buffers.  
+  if (stm32_bus->setup->bbuf_size != 0) {
+    cyg_uint8* tx_local = NULL;
+    cyg_uint8* rx_local = NULL;
+
+    // If the requested transfer is too large for the bounce buffer we assert 
+    // in debug builds and truncate in production builds.
+    if (count > stm32_bus->setup->bbuf_size) {
+      CYG_ASSERT (false, "STM32 SPI : Transfer exceeds bounce buffer size.");
+      count = stm32_bus->setup->bbuf_size;
+    }
+    if (tx_data != NULL) {
+      tx_local = stm32_bus->setup->bbuf_tx;        
+      memcpy (tx_local, tx_data, count);
+    }
+    if (rx_data != NULL) {
+      rx_local = stm32_bus->setup->bbuf_rx;        
+    }
+    spi_transaction_dma (device, false, polled, count, tx_local, rx_local, drop_cs);
+    if (rx_data != NULL) {
+      memcpy (rx_data, rx_local, count);
+    }
+  }
+
+  // Perform conventional transfer.
+  else {
+    spi_transaction_dma (device, false, polled, count, tx_data, rx_data, drop_cs);
+  }
+}
+
+//-----------------------------------------------------------------------------
+// Carry out a bus tick operation - this just pushes the required number of
+// zeros onto the bus, leaving the chip select in its current state.
+
+static void stm32_transaction_tick 
+  (cyg_spi_device* device, cyg_bool polled, cyg_uint32 count)
+{
+  cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device;
+
+  // Check for unsupported transactions.
+  CYG_ASSERT (count > 0, "STM32 SPI : Null transfer requested.");
+
+  // We check that count is a multiple of two in order to carry out the 16-bit transfer.
+  if (stm32_device->bus_16bit) {
+    CYG_ASSERT (!(count & 1),   
+      "STM32 SPI : Misaligned data in 16-bit transfer.");
+  }
+
+  // Perform null transfer.
+  spi_transaction_dma (device, true, polled, count, NULL, NULL, false);
+}
+
+//-----------------------------------------------------------------------------
+// Terminate a SPI transaction, disabling the SPI controller.
+
+static void stm32_transaction_end 
+  (cyg_spi_device* device)
+{
+  cyg_spi_cortexm_stm32_bus_t* stm32_bus = (cyg_spi_cortexm_stm32_bus_t*) device->spi_bus;
+  cyg_spi_cortexm_stm32_device_t* stm32_device = (cyg_spi_cortexm_stm32_device_t*) device;
+
+  cyg_haladdress reg_addr;
+
+  // Ensure that the chip select is deasserted.
+  if (stm32_bus->cs_up) {
+    CYGACC_CALL_IF_DELAY_US (stm32_device->cs_dw_udly);
+    stm32_spi_chip_select (stm32_bus->setup->cs_gpio_list[stm32_device->dev_num], false);   
+    stm32_bus->cs_up = false;
+  }     
+
+  // Ensure the SPI controller is disabled.
+  reg_addr = stm32_bus->setup->spi_reg_base + CYGHWR_HAL_STM32_SPI_CR1;
+  HAL_WRITE_UINT32 (reg_addr, stm32_device->spi_cr1_val);
+}
+
+//-----------------------------------------------------------------------------
+// Note that no dynamic configuration options are currently defined.
+
+static int stm32_get_config 
+  (cyg_spi_device* dev, cyg_uint32 key, void* buf, cyg_uint32* len)
+{
+    return -1;
+}
+
+static int stm32_set_config 
+  (cyg_spi_device* dev, cyg_uint32 key, const void* buf, cyg_uint32* len)
+{
+    return -1;
+}
+
+//=============================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/spi/cortexm/stm32/current/src/spi_stm32_init.cxx
@@ -0,0 +1,63 @@
+//=============================================================================
+//
+//      spi_stm32_init.c
+//
+//      SPI driver startup for STM32
+//
+//=============================================================================
+// ####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-11-27
+// Purpose:     STM32 SPI driver startup
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+#include <cyg/infra/cyg_type.h>
+
+externC void cyg_spi_cortexm_stm32_init (void);
+
+class cyg_spi_cortexm_stm32_init_class {
+public:
+  cyg_spi_cortexm_stm32_init_class (void) {
+    cyg_spi_cortexm_stm32_init ();
+  }
+};
+
+static cyg_spi_cortexm_stm32_init_class spi_cortexm_stm32_init CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO);
+
+//=============================================================================
new file mode 100644
--- /dev/null
+++ b/packages/devs/spi/cortexm/stm32/current/tests/loopback.c
@@ -0,0 +1,185 @@
+//=============================================================================
+//
+//      loopback.c
+//
+//      Standalone SPI loopback test.
+//
+//=============================================================================
+// ####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-11-27
+// Purpose:     STM32 SPI loopback test
+// Description: Standalone SPI loopback test.
+// Usage:       Compile as a standalone application.
+//
+//####DESCRIPTIONEND####
+//
+//=============================================================================
+
+//=============================================================================
+// This is a quick loopback test for the STM32 SPI driver.  It only checks
+// the data transfer functionality - chip select handling will require
+// testing with external devices.  In order to run the test, the MOSI and
+// MISO pins for the test port need to be shorted together to provide an
+// external loopback.  Don't do this on a bus which has external devices
+// attached unless you first make sure that none of them are connected to
+// the chip select used by the test harness.
+// The default port and chip select used for this test are SPI bus 1,
+// chip select 0.  These can be changed by editing the loopback_device
+// data structure directly.
+// Note that this is intended to be run as a standalone test and not as part
+// of the standard board tests since it requires a hardware modification.
+//=============================================================================
+
+#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/kernel/kapi.h>
+
+#include <cyg/io/spi.h>                 // Common SPI API
+#include <cyg/io/spi_stm32.h>           // STM32 data structures
+
+#include <string.h>
+
+//---------------------------------------------------------------------------
+// Thread data structures.
+
+cyg_uint8 stack [CYGNUM_HAL_STACK_SIZE_TYPICAL];
+cyg_thread thread_data;
+cyg_handle_t thread_handle;
+
+externC cyg_spi_cortexm_stm32_bus_t cyg_spi_stm32_bus2;
+
+//---------------------------------------------------------------------------
+// SPI loopback device driver data structures.
+
+cyg_spi_cortexm_stm32_device_t loopback_device = {
+    .spi_device.spi_bus = &cyg_spi_stm32_bus1.spi_bus,
+    .dev_num = 0 ,                      // Only 1 device. 
+    .cl_pol = 1,
+    .cl_pha = 1,
+    .cl_brate = 8000000,                // Nominal 8Mhz.
+    .cs_up_udly = 1,
+    .cs_dw_udly = 1,
+    .tr_bt_udly = 1,
+    .bus_16bit = false,
+};
+
+//---------------------------------------------------------------------------
+
+const char tx_data[] = "Testing, testing, 12, 123.";
+const char tx_data1[] = "Testing extended API...";
+const char tx_data2[] = "Testing extended API for a second transaction.";
+
+char rx_data [sizeof(tx_data)];
+char rx_data1 [sizeof(tx_data1)];
+char rx_data2 [sizeof(tx_data2)];
+
+//---------------------------------------------------------------------------
+// Run single loopback transaction using simple transfer API call.
+
+void run_test_1 (cyg_bool polled)
+{
+    diag_printf ("Test 1 : Simple transfer test (polled = %d).\n", polled ? 1 : 0);
+    cyg_spi_transfer (&loopback_device.spi_device, polled, sizeof (tx_data), 
+        (const cyg_uint8*) &tx_data[0], (cyg_uint8*) &rx_data[0]);
+
+    diag_printf ("    Tx data : %s\n", tx_data);
+    diag_printf ("    Rx data : %s\n", rx_data);
+    CYG_ASSERT (memcmp (tx_data, rx_data, sizeof (tx_data)) == 0,
+        "Simple transfer loopback failed - mismatched data.\n");
+}
+
+//---------------------------------------------------------------------------
+// Run two loopback transactions using extended transfer API.
+
+void run_test_2 (cyg_bool polled)
+{
+    diag_printf ("Test 2 : Extended API test (polled = %d).\n", polled ? 1 : 0);
+    cyg_spi_transaction_begin (&loopback_device.spi_device);
+    cyg_spi_transaction_transfer (&loopback_device.spi_device, polled, sizeof (tx_data1), 
+        (const cyg_uint8*) &tx_data1[0], (cyg_uint8*) &rx_data1[0], false);
+    cyg_spi_transaction_transfer (&loopback_device.spi_device, polled, sizeof (tx_data2), 
+        (const cyg_uint8*) &tx_data2[0], (cyg_uint8*) &rx_data2[0], false);
+    cyg_spi_transaction_end (&loopback_device.spi_device);
+
+    diag_printf ("    Tx data 1 : %s\n", tx_data1);
+    diag_printf ("    Rx data 1 : %s\n", rx_data1);
+    diag_printf ("    Tx data 2 : %s\n", tx_data2);
+    diag_printf ("    Rx data 2 : %s\n", rx_data2);
+    CYG_ASSERT (memcmp (tx_data1, rx_data1, sizeof (tx_data1)) == 0,
+        "Simple transfer loopback failed - mismatched data (transfer 1).\n");
+    CYG_ASSERT (memcmp (tx_data2, rx_data2, sizeof (tx_data2)) == 0,
+        "Simple transfer loopback failed - mismatched data (transfer 2).\n");
+}
+
+//---------------------------------------------------------------------------
+// Run all PL022 SPI interface loopback tests.
+
+void run_tests (void)
+{
+    diag_printf ("Running STM32 SPI driver loopback tests.\n");
+    run_test_1 (true); 
+    run_test_1 (false); 
+    run_test_2 (true); 
+    run_test_2 (false); 
+    CYG_TEST_PASS_FINISH ("Loopback tests ran OK");
+}
+
+//---------------------------------------------------------------------------
+// User startup - tests are run in their own thread.
+
+void cyg_user_start(void)
+{
+    CYG_TEST_INIT();
+    cyg_thread_create(
+        10,                                   // Arbitrary priority
+        (cyg_thread_entry_t*) run_tests,      // Thread entry point
+        0,                                    // 
+        "test_thread",                        // Thread name
+        &stack[0],                            // 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();
+}
+
+//=============================================================================