This is the mail archive of the ecos-discuss@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

at91 SPI in CS mode with 'PDC'


Hello, In the ecos at91 spi device driver,the chip select pin of SPI is configured in GPIO mode.
While I want to use the SPI in 'CS' mode with 'PDC' .Does anyone have done this before?
I have modified the at91 SPI in 'CS' mode ,and use the current counter and next counter for
the 'PDC'.And use the SPI to read and write a FRAM.
However the the modified driver couldn't work well.Some times the read and write operation go
well .While other times not,only the first part of the datas which read from the FRAM are right.

Bellow is the modified transfer function of the at91 SPI device driver,
spi_at91_transfer(cyg_spi_at91_device_t *dev,
                  cyg_uint32             count, 
                  cyg_uint32             n_count,
                  const cyg_uint8       *tx_data,
                  const cyg_uint8       *n_tx_data,
                  cyg_uint8             *rx_data,
                  cyg_uint8             *n_rx_data)
{
    cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t
*)dev->spi_device.spi_bus;
    CYG_INTERRUPT_STATE intr_state;
#ifdef SPI_AT91_DIAG
    cyg_uint8 *rx_data_orig = rx_data;
    cyg_uint32 count_orig = count;
#endif
    spi_diag("dev %d count %d txd %p rxd %p\n", dev->dev_num, count, tx_data,
rx_data );
    spi_dump_buf( tx_data, count );
    if ((NULL != rx_data)|(NULL!=n_rx_data))
    {
        // We must delay here, in case the last operation was polled. Why?
        // Because empirically there seems to be a silicon bug with some AT91s
        // which prevents correct operation of the PDC at the point the RPR is
        // set below *if* the last operation was polled *and* certain ranges of
        // address are used *and* insufficient time has passed since the last
        // (polled) transfer.
        SPI_AT91_DELAY_US(1);
    }
    // Since PDC transfer buffer counters are 16 bit long, 
    // we have to split longer transfers into chunks. 
    while ((count> 0)|(n_count>0))
    {
        cyg_uint16 tr_count = count> 0xFFFF ? 0xFFFF : count;
        cyg_uint16 n_tr_count =n_count> 0xFFFF?0xFFFF:n_count;
        cyg_drv_dsr_lock();
        // Set rx buf pointer and counter 
        if ((NULL != rx_data)|(NULL != n_rx_data))
        {
            HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RPR,
CYGARC_PHYSICAL_ADDRESS(rx_data));
            HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RCR, (cyg_uint32)tr_count);
         //   AT91_PDC_RX_ENABLE( spi_bus->base );
        }
        // Set tx buf pointer and counter
        HAL_DCACHE_SYNC();
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TPR,
CYGARC_PHYSICAL_ADDRESS(tx_data));
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TCR, (cyg_uint32)tr_count);
       // AT91_PDC_TX_ENABLE( spi_bus->base );
       // Set n_rx buf pointer and counter 
       if(NULL != n_rx_data)
       {
          //  HAL_DCACHE_SYNC();
            HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RNPR,
CYGARC_PHYSICAL_ADDRESS(n_rx_data));
            HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RNCR,
(cyg_uint32)n_tr_count);           
       }
     if((NULL != rx_data)|(NULL != n_rx_data))
         AT91_PDC_RX_ENABLE( spi_bus->base );
        // Set n_tx buf pointer and counter
    //    HAL_DCACHE_SYNC();
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TNPR,
CYGARC_PHYSICAL_ADDRESS(n_tx_data));
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TNCR, (cyg_uint32)n_tr_count);
        AT91_PDC_TX_ENABLE( spi_bus->base );
        // Enable the SPI int events we are interested in
    HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_IER, AT91_SPI_SR_RXBUFF | 
                                                AT91_SPI_SR_TXBUFE);
        cyg_drv_mutex_lock(&spi_bus->transfer_mx);
        {
            spi_bus->transfer_end = false;
            // Unmask the SPI int
            cyg_drv_interrupt_unmask(spi_bus->interrupt_number);
            // Wait for its completion
            {
                while (!spi_bus->transfer_end)
                    cyg_drv_cond_wait(&spi_bus->transfer_cond);
            }
        }    
        cyg_drv_mutex_unlock(&spi_bus->transfer_mx);
        if ((NULL == rx_data)&&(NULL == n_rx_data))
        {
            cyg_uint32 val;
            // If rx buffer was NULL, then the PDC receiver data transfer
            // was not started and we didn't wait for ENDRX, but only for 
            // ENDTX. Meaning that right now the last byte is being serialized 
            // over the line and when finished input data will appear in 
            // rx data reg. We have to wait for this to happen here, if we
            // don't we'll get the last received byte as the first one in the
            // next transfer!
            // FIXME: is there any better way to do this? 
            // If not, then precalculate this value.
            val = 8000000/dev->cl_brate;
            SPI_AT91_DELAY_US(val> 1 ? val : 1);
            // Clear the rx data reg
            HAL_READ_UINT32(spi_bus->base+AT91_SPI_RDR, val);
        }
        else
        {
            AT91_PDC_RX_DISABLE( spi_bus->base );
        }
        AT91_PDC_TX_DISABLE( spi_bus->base );
        cyg_drv_dsr_unlock();
        // Adjust running variables
        if (NULL != rx_data)
            rx_data += tr_count;
        tx_data += tr_count;
        count   -= tr_count;
      if(NULL != n_rx_data)
          n_rx_data += n_tr_count;
      n_tx_data +=n_tr_count;
      n_count -= n_tr_count;
    }
    HAL_DISABLE_INTERRUPTS(intr_state);
    HAL_DCACHE_SYNC();
    HAL_DCACHE_INVALIDATE_ALL();
    HAL_RESTORE_INTERRUPTS(intr_state);

    if( rx_data != NULL )
    {
        spi_diag("rx_data:\n");
        spi_dump_buf( rx_data_orig, count_orig );
    }
}

spi_at91_DSR(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *) data;
    cyg_uint32 stat;
    // Read the status register and 
    // check for transfer completion
    HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, stat);
    spi_diag("stat %08x\n", stat );
    if((stat & AT91_SPI_SR_RXBUFF) && (stat & AT91_SPI_SR_TXBUFE))
    {
        // Transfer ended  
        spi_bus->transfer_end = true;
        cyg_drv_cond_signal(&spi_bus->transfer_cond);
    }
    else
    {
        // Transfer still in progress - unmask the SPI 
        // int so we can get more SPI int events
        cyg_drv_interrupt_unmask(vector);
    }


Does anyone help me about this problem?
Thanks a lot! 		 	   		  

-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]