spi_at91.c
字号:
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 + -