//==========================================================================
//
//      spi_omap.c
//
//      TI OMAP SPI driver 
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 2005, 2006, 2007, 2008 Free Software Foundation, Inc.      
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 eCosCentric Limited                 
//
// eCos is free software; you can redistribute it and/or modify it under    
// the terms of the GNU General Public License as published by the Free     
// Software Foundation; either version 2 or (at your option) any later      
// version.                                                                 
//
// eCos is distributed in the hope that it will be useful, but WITHOUT      
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
// for more details.                                                        
//
// You should have received a copy of the GNU General Public License        
// along with eCos; if not, write to the Free Software Foundation, Inc.,    
// 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):     nickg
// Date:          2009-12-09
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/hal.h>
#include <pkgconf/io_spi.h>
#include <pkgconf/devs_spi_arm_omap.h>

#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_if.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/hal/drv_api.h>
#include <cyg/io/spi.h>
#include <cyg/io/spi_omap.h>
#include <cyg/error/codes.h>

// -------------------------------------------------------------------------

//#define SPI_OMAP_DIAG

#ifdef SPI_OMAP_DIAG
#define spi_diag( __fmt, ... ) diag_printf("SPI: %35s[%3d]: " __fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__ );
#define spi_dump_buf(__addr, __size ) diag_dump_buf( __addr, __size )
#else
#define spi_diag( __fmt, ... )
#define spi_dump_buf(__addr, __size )
#endif

#ifdef CYGACC_CALL_IF_DELAY_US
# define SPI_OMAP_DELAY_US(us) CYGACC_CALL_IF_DELAY_US(us)
#else
# include <cyg/hal/hal_diag.h>
# define SPI_OMAP_DELAY_US(us) HAL_DELAY_US(us)
#endif

// -------------------------------------------------------------------------

static void spi_omap_init_bus(cyg_spi_omap_bus_t * spi_bus);

#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA

static cyg_uint32 spi_omap_ISR(cyg_vector_t vector, cyg_addrword_t data);

static void spi_omap_DSR(cyg_vector_t   vector, 
                          cyg_ucount32   count, 
                          cyg_addrword_t data);

static void spi_omap_dma_callback( hal_edma_channel *edma_chan, cyg_uint32 event, CYG_ADDRWORD data );

#endif // CYGPKG_DEVS_SPI_ARM_OMAP_DMA

// -------------------------------------------------------------------------

static void spi_omap_init_bus(cyg_spi_omap_bus_t * spi_bus)
{
    cyg_uint32 gcr1;
    
    spi_diag("bus base %08x\n",spi_bus->base);

#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA    
    // Create and attach SPI interrupt object
    cyg_drv_interrupt_create(spi_bus->interrupt_number,
                             spi_bus->interrupt_priority,
                             (cyg_addrword_t)spi_bus,   
                             spi_omap_ISR,
                             spi_omap_DSR,
                             &spi_bus->spi_interrupt_handle,
                             &spi_bus->spi_interrupt);

    cyg_drv_interrupt_attach(spi_bus->spi_interrupt_handle);
    cyg_drv_interrupt_unmask(spi_bus->interrupt_number);
#endif

    // Init transfer mutex and condition
    cyg_drv_mutex_init(&spi_bus->transfer_mx);
    cyg_drv_cond_init(&spi_bus->transfer_cond, 
                      &spi_bus->transfer_mx);
    
    // Init flags
    spi_bus->transfer_end = true;
    spi_bus->cs_up        = false;

    // Configure device, power up and set pins to SPI device
    CYGHWR_HAL_L1XX_POWER_ENABLE( spi_bus->enable );
    CYGHWR_HAL_L1XX_PINMUX_SET( spi_bus->sclk_pin );
    CYGHWR_HAL_L1XX_PINMUX_SET( spi_bus->miso_pin );
    CYGHWR_HAL_L1XX_PINMUX_SET( spi_bus->mosi_pin );

    // Leave the CS pins to be enabled when we do an actual
    // transfer. That way we don't steal them from other uses if we
    // don't use them.
    

    // Reset the device
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_GCR0, 0 );
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_GCR0, CYGHWR_HAL_OMAP_SPI_GCR0_RESET );

    // Set master mode
    gcr1 = CYGHWR_HAL_OMAP_SPI_GCR1_MASTER | CYGHWR_HAL_OMAP_SPI_GCR1_CLKMOD;
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_GCR1, gcr1 );

    // Configure for 3pin mode, we control the CS pins ourself via the
    // GPIO controls in the ls 8 bits of the PCx registers.
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC0,
                      CYGHWR_HAL_OMAP_SPI_PCX_CLK|CYGHWR_HAL_OMAP_SPI_PCX_MOSI|CYGHWR_HAL_OMAP_SPI_PCX_MISO);
    
    // Set up FMT0 register later

    // Set level register for the interrupts we want
    // (This seems to be a somewhat pointless piece of functionality)
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_LVL,
                      CYGHWR_HAL_OMAP_SPI_INT_OVRN    |
                      CYGHWR_HAL_OMAP_SPI_INT_BITERR  |
                      CYGHWR_HAL_OMAP_SPI_INT_DESYNC  |
                      CYGHWR_HAL_OMAP_SPI_INT_PARERR  |
                      CYGHWR_HAL_OMAP_SPI_INT_TIMEOUT |
                      CYGHWR_HAL_OMAP_SPI_INT_DLENERR );
    
    // Enable error interrupts
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_INT,
                      CYGHWR_HAL_OMAP_SPI_INT_OVRN    |
                      CYGHWR_HAL_OMAP_SPI_INT_BITERR  |
                      CYGHWR_HAL_OMAP_SPI_INT_DESYNC  |
                      CYGHWR_HAL_OMAP_SPI_INT_PARERR  |
                      CYGHWR_HAL_OMAP_SPI_INT_TIMEOUT |
                      CYGHWR_HAL_OMAP_SPI_INT_DLENERR );

    // Perform any platform specific setup

#ifdef CYGHWR_HAL_OMAP_SPI_INIT
    CYGHWR_HAL_OMAP_SPI_INIT( spi_bus );
#endif

#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA    
    // Init DMA channels

    hal_edma_channel_init( &spi_bus->tx_chan, spi_bus->tx_dma, spi_omap_dma_callback, (CYG_ADDRWORD)spi_bus );
    hal_edma_channel_init( &spi_bus->rx_chan, spi_bus->rx_dma, spi_omap_dma_callback, (CYG_ADDRWORD)spi_bus );
#endif
    
    gcr1 |= CYGHWR_HAL_OMAP_SPI_GCR1_ENABLE;
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_GCR1, gcr1 );
    
    // Call upper layer bus init
    CYG_SPI_BUS_COMMON_INIT(&spi_bus->spi_bus);
}

// -------------------------------------------------------------------------
// SPI driver constructor.

void CYGBLD_ATTRIB_C_INIT_PRI(CYG_INIT_BUS_SPI) spi_omap_init( void )
{
#if CYGINT_DEVS_SPI_ARM_OMAP_SPI0>0
    {
        __externC cyg_spi_omap_bus_t cyg_spi_omap_bus0;
        spi_omap_init_bus( &cyg_spi_omap_bus0 );
    }
#endif
#if CYGINT_DEVS_SPI_ARM_OMAP_SPI1>0
    {
        __externC cyg_spi_omap_bus_t cyg_spi_omap_bus1;
        spi_omap_init_bus( &cyg_spi_omap_bus1 );
    }
#endif
}

// -------------------------------------------------------------------------

#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA
static cyg_uint32 
spi_omap_ISR(cyg_vector_t vector, cyg_addrword_t data)
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *) data;    
    cyg_uint32 ret = CYG_ISR_HANDLED;
    cyg_uint32 flg;

    // We only come here when we get an error.

    // Read and clear any bits set
    HAL_READ_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_FLG, flg );
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_FLG, flg );
    
    spi_diag("flg %08x\n", flg );

    
    cyg_drv_interrupt_acknowledge(vector);
    
    cyg_drv_interrupt_mask(vector);

    ret |= CYG_ISR_CALL_DSR;

    return ret;
}

// -------------------------------------------------------------------------

static void 
spi_omap_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *) data;

    spi_diag("\n");

    hal_edma_channel_stop( &spi_bus->tx_chan );
    hal_edma_channel_stop( &spi_bus->rx_chan );
    
    // Transfer ended  
    spi_bus->transfer_end = true;
    cyg_drv_cond_signal(&spi_bus->transfer_cond);
}

#endif // CYGPKG_DEVS_SPI_ARM_OMAP_DMA

// -------------------------------------------------------------------------

static void
spi_omap_start_transfer(cyg_spi_omap_device_t *dev)
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *)dev->spi_device.spi_bus;
    cyg_uint32 cs_pio;
    
    if (spi_bus->cs_up)
        return;

    spi_diag("dev %d\n", dev->dev_num );
    
    // Force minimal delay between two transfers - in case two transfers
    // follow each other w/o delay, then we have to wait here in order for
    // the peripheral device to detect cs transition from inactive to active. 

    SPI_OMAP_DELAY_US(dev->tr_bt_udly);
    
    // Assert CS

    if( dev->dev_num < spi_bus->cs_count )
    {
        cs_pio = 1<<spi_bus->cs_pin[dev->dev_num];

        // Force CS pin low
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC5, cs_pio );
    }
    
    SPI_OMAP_DELAY_US(dev->cs_up_udly);
   
    spi_bus->cs_up = true;
}

// -------------------------------------------------------------------------

static void
spi_omap_drop_cs(cyg_spi_omap_device_t *dev)
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *)dev->spi_device.spi_bus;
    cyg_uint32 cs_pio;
    
    if (!spi_bus->cs_up)
       return;

    spi_diag("dev %d\n", dev->dev_num );    
    
    // Drop CS

    SPI_OMAP_DELAY_US(dev->cs_dw_udly);

    if( dev->dev_num < spi_bus->cs_count )
    {
        cs_pio = 1<<spi_bus->cs_pin[dev->dev_num];

        // Force CS pin high
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC4, cs_pio );
    }
    
    spi_bus->cs_up = false;
}

// -------------------------------------------------------------------------

#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA

// -------------------------------------------------------------------------

static void
spi_omap_dma_callback( hal_edma_channel *edma_chan, cyg_uint32 event, CYG_ADDRWORD data )
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *) data;

    hal_edma_channel_stop( &spi_bus->tx_chan );
    hal_edma_channel_stop( &spi_bus->rx_chan );

    // Transfer ended  
    spi_bus->transfer_end = true;
    cyg_drv_cond_signal(&spi_bus->transfer_cond);    
}

// -------------------------------------------------------------------------

static void
spi_omap_transfer(cyg_spi_omap_device_t *dev,
                  cyg_uint32             count, 
                  const cyg_uint8       *tx_data,
                  cyg_uint8             *rx_data)
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *)dev->spi_device.spi_bus;
    cyg_uint32 int0;

    spi_diag("dev %d count %d txd %p rxd %p\n", dev->dev_num, count, tx_data, rx_data );
    if( 0 && NULL != tx_data )
    {
        spi_diag("tx_data:\n");        
        spi_dump_buf( tx_data, count );
    }

    // Claim the lock
    cyg_drv_mutex_lock(&spi_bus->transfer_mx);
    {
        // Set up transfer parameters
        spi_bus->transfer_end = false;
        spi_bus->tx_data = tx_data;
        spi_bus->rx_data = rx_data;
        spi_bus->tx_count = count;
        spi_bus->rx_count = count;

        // If no tx_data, send the contents of the rx buffer.
        if( NULL == tx_data )
            tx_data = rx_data;

        HAL_DCACHE_SYNC();

        // Clear all pending flags
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_FLG, 0xFFFFFFFF );        
        
        // Set up DMA channels

        hal_edma_channel_source( &spi_bus->tx_chan, tx_data, 1, 1 );
        hal_edma_channel_dest( &spi_bus->tx_chan, (void *)(spi_bus->base+CYGHWR_HAL_OMAP_SPI_DAT1), 0, 0 );
        hal_edma_channel_size( &spi_bus->tx_chan, count );
        
        hal_edma_channel_source( &spi_bus->rx_chan, (void *)(spi_bus->base+CYGHWR_HAL_OMAP_SPI_BUF), 0, 0 );
        hal_edma_channel_dest( &spi_bus->rx_chan, rx_data, 1, 1 );
        hal_edma_channel_size( &spi_bus->rx_chan, count );

        hal_edma_channel_start( &spi_bus->rx_chan, 0 );
        hal_edma_channel_start( &spi_bus->tx_chan, 0 );

        
        // Start the DMA transfer.
        HAL_READ_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_INT, int0 );
        int0 |= CYGHWR_HAL_OMAP_SPI_INT_DMAREQ;
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_INT, int0 );
        
        // Wait for completion
        while (!spi_bus->transfer_end)
            cyg_drv_cond_wait(&spi_bus->transfer_cond);

        // Stop the transfer
        HAL_READ_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_INT, int0 );
        int0 &= ~CYGHWR_HAL_OMAP_SPI_INT_DMAREQ;
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_INT, int0 );
        
    }    
    cyg_drv_mutex_unlock(&spi_bus->transfer_mx);
    
    if( 0 && rx_data != NULL )
    {
        spi_diag("rx_data:\n");
        spi_dump_buf( rx_data, count );
    }
}

#endif // CYGPKG_DEVS_SPI_ARM_OMAP_DMA

// -------------------------------------------------------------------------

static void
spi_omap_transfer_polled(cyg_spi_omap_device_t *dev, 
                         cyg_uint32             count,
                         const cyg_uint8       *tx_data,
                         cyg_uint8             *rx_data)
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *)dev->spi_device.spi_bus;
    cyg_uint32 base = spi_bus->base;
    cyg_uint32 tx_count = count;
    cyg_uint32 rx_count = count;
    cyg_uint32 buf;
//    cyg_uint8 val;
#ifdef SPI_OMAP_DIAG
    cyg_uint32 count0 = count;
    cyg_uint8 *rx_data0 = rx_data;
#endif

    spi_diag("dev %d count %d txd %p rxd %p\n", dev->dev_num, count, tx_data, rx_data );

    // Transmit and receive byte by byte
    while (tx_count > 0 || rx_count > 0)
    {
        cyg_uint32 txd;
        
        // Wait for Tx buffer to be empty
        do
        {
            HAL_READ_UINT32( base+CYGHWR_HAL_OMAP_SPI_BUF, buf );
        } while( buf & CYGHWR_HAL_OMAP_SPI_BUF_TXFULL );

        // If no tx data buffer supplied, send FFs.
        if( tx_data != NULL )
            txd = *tx_data++;
        else
            txd = 0xFF;

        // Transmit next byte
        HAL_WRITE_UINT32( base+CYGHWR_HAL_OMAP_SPI_DAT0, txd );
        
        // Wait for RX data to arrive
        do
        {
            HAL_READ_UINT32( base+CYGHWR_HAL_OMAP_SPI_BUF, buf );
        } while( buf & CYGHWR_HAL_OMAP_SPI_BUF_RXEMPTY );

        if( rx_data != NULL )
        {
            *rx_data = CYGHWR_HAL_OMAP_SPI_BUF_RXDATA(buf);
            rx_data++;
        }

        tx_count--;
        rx_count--;
    }

#if 0
    if( rx_data != NULL )
    {
        spi_diag("rx_data:\n");
        spi_dump_buf( rx_data0, count0 );
    }
#endif
}

// -------------------------------------------------------------------------

void cyg_spi_omap_transaction_begin(cyg_spi_device *dev)
{
    cyg_spi_omap_device_t *omap_spi_dev = (cyg_spi_omap_device_t *) dev;
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *)dev->spi_bus;
    cyg_uint32 fmt = 0;
    cyg_int32 prescale;

    // Configure CS pin for output, ensure it starts high.
    if( omap_spi_dev->dev_num < spi_bus->cs_count )
    {
        cyg_uint32 cs_pio = 1<<spi_bus->cs_pin[omap_spi_dev->dev_num];
        cyg_uint32 reg;

        // Switch pinmux for CS pin to SPI device
        CYGHWR_HAL_L1XX_PINMUX_SET( spi_bus->cs_pinmux[spi_bus->cs_pin[omap_spi_dev->dev_num]] );
        
        // Set CS pin to GPIO mode
        HAL_READ_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC0, reg );
        reg &= ~cs_pio;
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC0, reg );

        // Set pin state high
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC4, cs_pio );
        
        // Set pin to output mode
        HAL_READ_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC1, reg );
        reg |= cs_pio;
        HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_PC1, reg );
    }
    
    // Configure FMT0 with phase and polarity
    if( omap_spi_dev->cl_pol )
        fmt |= CYGHWR_HAL_OMAP_SPI_FMT_POLARITY;

    if( omap_spi_dev->cl_pha )
        fmt |= CYGHWR_HAL_OMAP_SPI_FMT_PHASE;

    // Disable CS timers, we do that ourself
    fmt |= CYGHWR_HAL_OMAP_SPI_FMT_DISCSTIMERS;

    // Set 8 bit character length
    fmt |= CYGHWR_HAL_OMAP_SPI_FMT_CHARLEN( 8 );

    // Set prescale to select required baud rate
    prescale = CYGHWR_HAL_ARM_ARM9_OMAP_L1XX_CLOCK_PLL_SYSCLK2 / omap_spi_dev->cl_brate;
    
    fmt |= CYGHWR_HAL_OMAP_SPI_FMT_PRESCALE( prescale-1 );

    // Write the format register
    HAL_WRITE_UINT32( spi_bus->base+CYGHWR_HAL_OMAP_SPI_FMT0, fmt );
}

// -------------------------------------------------------------------------

void cyg_spi_omap_transaction_transfer(cyg_spi_device  *dev, 
                                       cyg_bool         polled,  
                                       cyg_uint32       count, 
                                       const cyg_uint8 *tx_data, 
                                       cyg_uint8       *rx_data, 
                                       cyg_bool         drop_cs) 
{
    cyg_spi_omap_device_t *omap_spi_dev = (cyg_spi_omap_device_t *) dev;

    spi_diag("dev %d count %d txd %p rxd %p%s%s\n", omap_spi_dev->dev_num, count, tx_data, rx_data,
             polled?" polled":"", drop_cs?" drop_cs":"");

    // Select the device if not already selected
    spi_omap_start_transfer(omap_spi_dev);

    // Perform the transfer
#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA

    // If the transfer is for less than 32 bytes, do it via polling,
    // the DMA setup overhead is otherwise too great.
    if( count < 32 ) polled = true;
    
    if (!polled )
        spi_omap_transfer(omap_spi_dev, count, tx_data, rx_data);        
    else
#endif
        spi_omap_transfer_polled(omap_spi_dev, count, tx_data, rx_data);        
    
    // Deselect the device if requested
    if (drop_cs)
        spi_omap_drop_cs(omap_spi_dev);
}

// -------------------------------------------------------------------------

void cyg_spi_omap_transaction_tick(cyg_spi_device *dev, 
                                   cyg_bool        polled,  
                                   cyg_uint32      count)
{
    // We write FFs, and not zeroes. MMC cards at least require that.
    const cyg_uint32 FFs[10] = { 0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,
                                 0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff };

    cyg_spi_omap_device_t *omap_spi_dev = (cyg_spi_omap_device_t *) dev;

    spi_diag("dev %d count %d%s\n", omap_spi_dev->dev_num, count, polled?" polled":"");
    
    // Transfer count FFs to the device - we don't touch the
    // chip select, the device could be selected or deselected.
    // It is up to the device driver to decide in which state the
    // device will be ticked.
    
    while (count > 0)
    {
        int tcnt = count > sizeof(FFs) ? sizeof(FFs) : count;
        
#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA    
        if (!polled)
            spi_omap_transfer(omap_spi_dev, tcnt, 
                              (const cyg_uint8 *) FFs, NULL);
        else
#endif
            spi_omap_transfer_polled(omap_spi_dev, tcnt,
                                     (const cyg_uint8 *) FFs, NULL);
        count -= tcnt;
    }
}

// -------------------------------------------------------------------------

void cyg_spi_omap_transaction_end(cyg_spi_device* dev)
{
    cyg_spi_omap_bus_t *spi_bus = (cyg_spi_omap_bus_t *)dev->spi_bus;

    spi_diag("dev %d\n", ((cyg_spi_omap_device_t *)dev)->dev_num );

#ifdef CYGPKG_DEVS_SPI_ARM_OMAP_DMA
    
    hal_edma_channel_stop( &spi_bus->tx_chan );
    hal_edma_channel_stop( &spi_bus->rx_chan );
    
#endif
    
    spi_omap_drop_cs((cyg_spi_omap_device_t *) dev);
}

// -------------------------------------------------------------------------

int cyg_spi_omap_get_config(cyg_spi_device *dev, 
                             cyg_uint32      key, 
                             void           *buf,
                             cyg_uint32     *len)
{
    cyg_spi_omap_device_t *omap_spi_dev = (cyg_spi_omap_device_t *) dev;
    
    switch (key) 
    {
        case CYG_IO_GET_CONFIG_SPI_CLOCKRATE:
        {
            if (*len != sizeof(cyg_uint32))
                return -EINVAL;
            else
            {
                cyg_uint32 *cl_brate = (cyg_uint32 *)buf;
                *cl_brate = omap_spi_dev->cl_brate; 
            }
        }
        break;
        default:
            return -EINVAL;
    }
    return ENOERR;
}

// -------------------------------------------------------------------------

int cyg_spi_omap_set_config(cyg_spi_device *dev, 
                            cyg_uint32      key, 
                            const void     *buf, 
                            cyg_uint32     *len)
{
    cyg_spi_omap_device_t *omap_spi_dev = (cyg_spi_omap_device_t *) dev;

    spi_diag("dev %d key %08x\n", omap_spi_dev->dev_num, key );
    
    switch (key) 
    {
        case CYG_IO_SET_CONFIG_SPI_CLOCKRATE:
        {
            if (*len != sizeof(cyg_uint32))
                return -EINVAL;
            else
            {
                cyg_uint32 cl_brate     = *((cyg_uint32 *)buf);
//                cyg_uint32 old_cl_brate = omap_spi_dev->cl_brate;
           
                omap_spi_dev->cl_brate = cl_brate;

                // Baud rate will be set on next transfer
            }
        }
        break;
        default:
            return -EINVAL;
    }
    return ENOERR;
}

// -------------------------------------------------------------------------
// EOF spi_omap.c
