欢迎来到虫虫下载站 | 资源下载 资源专辑 关于我们
虫虫下载站

spi_at91.c

开放源码实时操作系统源码.
C
第 1 页 / 共 2 页
字号:
static void
spi_at91_start_transfer(cyg_spi_at91_device_t *dev)
{
    cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus;
  
    if (spi_bus->cs_up)
        return;
 
    // 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. 
    CYGACC_CALL_IF_DELAY_US(dev->tr_bt_udly);
    
    // Raise CS

#ifdef CYGHWR_DEVS_SPI_ARM_AT91_PCSDEC
    spi_at91_set_npcs(spi_bus,~dev->dev_num);
#else
    spi_at91_set_npcs(spi_bus,~(1<<dev->dev_num));
#endif
    CYGACC_CALL_IF_DELAY_US(dev->cs_up_udly);
   
    spi_bus->cs_up = true;
}

static void
spi_at91_drop_cs(cyg_spi_at91_device_t *dev)
{
    cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus;
    
    if (!spi_bus->cs_up)
       return;
           
    // Drop CS

    CYGACC_CALL_IF_DELAY_US(dev->cs_dw_udly);
    spi_at91_set_npcs(spi_bus,0x0F); 
    spi_bus->cs_up = false;
}

static void
spi_at91_transfer(cyg_spi_at91_device_t *dev,
                  cyg_uint32             count, 
                  const cyg_uint8       *tx_data,
                  cyg_uint8             *rx_data)
{
    cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus;

    // Since PDC transfer buffer counters are 16 bit long, 
    // we have to split longer transfers into chunks. 
    while (count > 0)
    {
        cyg_uint16 tr_count = count > 0xFFFF ? 0xFFFF : count;

        // Set rx buf pointer and counter 
        if (NULL != rx_data)
        {
            HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RPR, (cyg_uint32)rx_data);
            HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_RCR, (cyg_uint32)tr_count);
        }

        // Set tx buf pointer and counter  
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TPR, (cyg_uint32)tx_data);
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TCR, (cyg_uint32)tr_count);
   
#ifdef AT91_SPI_PTCR
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_PTCR, 
                         AT91_SPI_PTCR_RXTEN | AT91_SPI_PTCR_TXTEN);
#endif
        // Enable the SPI int events we are interested in
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_IER, 
                         AT91_SPI_SR_ENDRX | AT91_SPI_SR_ENDTX);

        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 completition
            cyg_drv_dsr_lock();
            {
                while (!spi_bus->transfer_end)
                    cyg_drv_cond_wait(&spi_bus->transfer_cond);
            }
            cyg_drv_dsr_unlock();
        }    
        cyg_drv_mutex_unlock(&spi_bus->transfer_mx);

        if (NULL == 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;
            CYGACC_CALL_IF_DELAY_US(val > 1 ? val : 1);

            // Clear the rx data reg
            HAL_READ_UINT32(spi_bus->base+AT91_SPI_RDR, val);
        }

        // Adjust running variables
        
        if (NULL != rx_data)
            rx_data += tr_count;
        tx_data += tr_count;
        count   -= tr_count;
    }
}

static void
spi_at91_transfer_polled(cyg_spi_at91_device_t *dev, 
                         cyg_uint32             count,
                         const cyg_uint8       *tx_data,
                         cyg_uint8             *rx_data)
{
    cyg_uint32 val;
    cyg_spi_at91_bus_t *spi_bus = (cyg_spi_at91_bus_t *)dev->spi_device.spi_bus;

    // Transmit and receive byte by byte
    while (count-- > 0)
    {
        // Wait for transmit data register empty
        do
        {
            HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, val);
        } while ( !(val & AT91_SPI_SR_TDRE) );
       
        // Send next byte over the wire 
        val = *tx_data++;
        HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_TDR, val);
        
        // Wait for reveive data register full 
        do
        {
            HAL_READ_UINT32(spi_bus->base+AT91_SPI_SR, val);
        } while ( !(val & AT91_SPI_SR_RDRF) );
        
        // Store received byte 
        HAL_READ_UINT32(spi_bus->base+AT91_SPI_RDR, val);
        if (NULL != rx_data)
            *rx_data++ = val; 
    }
}

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

static void 
spi_at91_transaction_begin(cyg_spi_device *dev)
{
    cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev;    
    cyg_spi_at91_bus_t *spi_bus = 
      (cyg_spi_at91_bus_t *)at91_spi_dev->spi_device.spi_bus;
    cyg_uint32 val;
    
    if (!at91_spi_dev->init)
    {
        at91_spi_dev->init = true;
        spi_at91_calc_scbr(at91_spi_dev);
    }
    
    // Configure SPI channel 0 - this is the only channel we 
    // use for all devices since we drive chip selects manually
    
    val = AT91_SPI_CSR_BITS8;

    if (1 == at91_spi_dev->cl_pol)
        val |= AT91_SPI_CSR_CPOL;

    if (1 == at91_spi_dev->cl_pha)
        val |= AT91_SPI_CSR_NCPHA;

    val |= AT91_SPI_CSR_SCBR(at91_spi_dev->cl_scbr);

    HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CSR0, val); 

    // Enable SPI clock
    HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER, 1<<spi_bus->interrupt_number);

    // Enable the SPI controller
    HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CR, AT91_SPI_CR_SPIEN);
    
    /* As we are using this driver only in master mode with NPCS0 
       configured as GPIO instead of a peripheral pin, it is neccessary 
       for the Mode Failure detection to be switched off as this will
       cause havoc with the driver */ 

    // Put SPI bus into master mode
    if (1 == at91_spi_dev->cl_div32) {
      val = AT91_SPI_MR_MSTR | AT91_SPI_MR_DIV32;
#ifdef AT91_SPI_MR_MODFDIS
      val |= AT91_SPI_MR_MODFDIS;
#endif
      HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_MR, val);
    } else {
      val = AT91_SPI_MR_MSTR;
#ifdef AT91_SPI_MR_MODFDIS
      val |= AT91_SPI_MR_MODFDIS;
#endif
      HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_MR, val);
    }
}

static void 
spi_at91_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_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev;

    // Select the device if not already selected
    spi_at91_start_transfer(at91_spi_dev);
 
    // Perform the transfer
    if (polled)
        spi_at91_transfer_polled(at91_spi_dev, count, tx_data, rx_data);
    else
        spi_at91_transfer(at91_spi_dev, count, tx_data, rx_data);

    // Deselect the device if requested
    if (drop_cs)
        spi_at91_drop_cs(at91_spi_dev);
}

static void 
spi_at91_transaction_tick(cyg_spi_device *dev, 
                          cyg_bool        polled,  
                          cyg_uint32      count)
{
    const cyg_uint32 zeros[10] = { 0,0,0,0,0,0,0,0,0,0 };

    cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev;
    
    // Transfer count zeros 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 wich state the
    // device will be ticked.
    
    while (count > 0)
    {
        int tcnt = count > 40 ? 40 : count;
        
        if (polled)
            spi_at91_transfer_polled(at91_spi_dev, tcnt, 
                                     (const cyg_uint8 *) zeros, NULL);
        else
            spi_at91_transfer(at91_spi_dev, tcnt, 
                              (const cyg_uint8 *) zeros, NULL);

        count -= tcnt;
    }
}

static void                    
spi_at91_transaction_end(cyg_spi_device* dev)
{
    cyg_spi_at91_device_t * at91_spi_dev = (cyg_spi_at91_device_t *)dev; 
    cyg_spi_at91_bus_t *spi_bus = 
      (cyg_spi_at91_bus_t *)at91_spi_dev->spi_device.spi_bus;

    // Disable the SPI controller
    HAL_WRITE_UINT32(spi_bus->base+AT91_SPI_CR, AT91_SPI_CR_SPIDIS);
   
    // Disable SPI clock
    HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCDR,1<<spi_bus->interrupt_number);
   
    spi_at91_drop_cs((cyg_spi_at91_device_t *) dev);
}

static int                     
spi_at91_get_config(cyg_spi_device *dev, 
                    cyg_uint32      key, 
                    void           *buf,
                    cyg_uint32     *len)
{
    cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_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 = at91_spi_dev->cl_brate; 
            }
        }
        break;
        default:
            return -EINVAL;
    }
    return ENOERR;
}

static int                     
spi_at91_set_config(cyg_spi_device *dev, 
                    cyg_uint32      key, 
                    const void     *buf, 
                    cyg_uint32     *len)
{
    cyg_spi_at91_device_t *at91_spi_dev = (cyg_spi_at91_device_t *) dev;
   
    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 = at91_spi_dev->cl_brate;
           
                at91_spi_dev->cl_brate = cl_brate;
            
                if (!spi_at91_calc_scbr(at91_spi_dev))
                {
                    at91_spi_dev->cl_brate = old_cl_brate;
                    spi_at91_calc_scbr(at91_spi_dev);
                    return -EINVAL;
                }
            }
        }
        break;
        default:
            return -EINVAL;
    }
    return ENOERR;
}

// -------------------------------------------------------------------------
// EOF spi_at91.c

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -