- 论坛徽章:
- 0
|
开发CAN总线程序,以下这个函数my_spidev_rd_wr在中断中调用就会出错,但是在定时器轮询中就可以正常使用。
帮我看看为什么,谢谢!
static char pSpiReadBuf[100];
int my_spidev_rd_wr(int dev, char*buf, int count)
{
unsigned int spi_device = m_SpiDevNum;
unsigned int ofs, pagelen;
int res, i;
int j;
struct spi_transfer_list* list;
list = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL);
if (!list)
return -ENOMEM;
for(j = 0; j < count; ++j)
{
pSpiReadBuf[j] = buf[j];
}
res = count;
ofs = (unsigned long)pSpiReadBuf & (~PAGE_MASK);
pagelen = PAGE_SIZE - ofs;
list->tx[0] = list->rx[0] = virt_to_page(pSpiReadBuf) + ofs;
list->txlen[0] = list->rxlen[0] = count;
list->nr_transfers = 1;
spi_access_bus(spi_device);
spi_transfer(list);
spi_release_bus(spi_device);
kfree(list);
for(j = 0; j < res; j++)
{
buf[j] = pSpiReadBuf[j];
}
return res;
}
其中的spi_access_bus,transfer,spi_release_bus是linux自带的函数。
static struct spi_local spi_dev[NR_SPI_DEVICES]; /* state of the SPI devices */
static int spi_enabled = 0;
static struct semaphore spi_lock; /* protect access to SPI bus */
static int current_device = -1; /* currently selected SPI device */
DECLARE_COMPLETION(transfer_complete);
/* SPI controller device */
AT91PS_SPI controller = (AT91PS_SPI) AT91C_VA_BASE_SPI;
/* ......................................................................... */
/*
* Access and enable the SPI bus.
* This MUST be called before any transfers are performed.
*/
void spi_access_bus(short device)
{
/* Ensure that requested device is valid */
if ((device < 0) || (device >= NR_SPI_DEVICES))
panic("at91_spi: spi_access_bus called with invalid device");
if (spi_enabled == 0) {
AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI; /* Enable Peripheral clock */
controller->SPI_CR = AT91C_SPI_SPIEN; /* Enable SPI */
#ifdef DEBUG_SPI
printk("SPI on\n");
#endif
}
MOD_INC_USE_COUNT;
spi_enabled++;
/* Lock the SPI bus */
down(&spi_lock);
current_device = device;
/* Enable PIO */
if (!spi_dev[device].pio_enabled) {
switch (device) {
case 0: AT91_CfgPIO_SPI_CS0();
case 1: AT91_CfgPIO_SPI_CS1();
case 2: AT91_CfgPIO_SPI_CS2();
case 3: AT91_CfgPIO_SPI_CS3();
}
spi_dev[device].pio_enabled = 1;
#ifdef DEBUG_SPI
printk("SPI CS%i enabled\n", device);
#endif
}
/* Configure SPI bus for device */
controller->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | (spi_dev[device].pcs << 16);
}
/*
* Relinquish control of the SPI bus.
*/
void spi_release_bus(short device)
{
if (device != current_device)
panic("at91_spi: spi_release called with invalid device");
/* Release the SPI bus */
current_device = -1;
up(&spi_lock);
spi_enabled--;
MOD_DEC_USE_COUNT;
if (spi_enabled == 0) {
controller->SPI_CR = AT91C_SPI_SPIDIS; /* Disable SPI */
AT91_SYS->PMC_PCER = 1 << AT91C_ID_SPI; /* Disable Peripheral clock */
#ifdef DEBUG_SPI
printk("SPI off\n");
#endif
}
}
/*
* Perform a data transfer over the SPI bus
*/
int spi_transfer(struct spi_transfer_list* list)
{
struct spi_local *device = (struct spi_local *) &spi_dev[current_device];
if (!list)
panic("at91_spi: spi_transfer called with NULL transfer list");
if (current_device == -1)
panic("at91_spi: spi_transfer called without acquiring bus");
#ifdef DEBUG_SPI
printk("SPI transfer start [%i]\n", list->nr_transfers);
#endif
/* Store transfer list */
device->xfers = list;
list->curr = 0;
/* Assume there must be at least one transfer */
device->tx = pci_map_single(NULL, list->tx[0], list->txlen[0], PCI_DMA_TODEVICE);
device->rx = pci_map_single(NULL, list->rx[0], list->rxlen[0], PCI_DMA_FROMDEVICE);
/* Program PDC registers */
controller->SPI_TPR = device->tx;
controller->SPI_RPR = device->rx;
controller->SPI_TCR = list->txlen[0];
controller->SPI_RCR = list->rxlen[0];
/* Is there a second transfer? */
if (list->nr_transfers > 1) {
device->txnext = pci_map_single(NULL, list->tx[1], list->txlen[1], PCI_DMA_TODEVICE);
device->rxnext = pci_map_single(NULL, list->rx[1], list->rxlen[1], PCI_DMA_FROMDEVICE);
/* Program Next PDC registers */
controller->SPI_TNPR = device->txnext;
controller->SPI_RNPR = device->rxnext;
controller->SPI_TNCR = list->txlen[1];
controller->SPI_RNCR = list->rxlen[1];
}
else {
device->txnext = 0;
device->rxnext = 0;
controller->SPI_TNCR = 0;
controller->SPI_RNCR = 0;
}
// TODO: If we are doing consecutive transfers (at high speed, or
// small buffers), then it might be worth modifying the 'Delay between
// Consecutive Transfers' in the CSR registers.
// This is an issue if we cannot chain the next buffer fast enough
// in the interrupt handler.
/* Enable transmitter and receiver */
controller->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
controller->SPI_IER = AT91C_SPI_SPENDRX; /* enable buffer complete interrupt */
wait_for_completion(&transfer_complete);
#ifdef DEBUG_SPI
printk("SPI transfer end\n");
#endif
return 0;
}
/* ......................................................................... */
/*
* Handle interrupts from the SPI controller.
*/
void spi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned int status;
struct spi_local *device = (struct spi_local *) &spi_dev[current_device];
struct spi_transfer_list *list = device->xfers;
#ifdef DEBUG_SPI
printk("SPI interrupt %i\n", current_device);
#endif
if (!list)
panic("at91_spi: spi_interrupt with a NULL transfer list");
status = controller->SPI_SR & controller->SPI_IMR; /* read status */
pci_unmap_single(NULL, device->tx, list->txlen[list->curr], PCI_DMA_TODEVICE);
pci_unmap_single(NULL, device->rx, list->rxlen[list->curr], PCI_DMA_FROMDEVICE);
device->tx = device->txnext; /* move next transfer to current transfer */
device->rx = device->rxnext;
list->curr = list->curr + 1;
if (list->curr == list->nr_transfers) { /* all transfers complete */
controller->SPI_IDR = AT91C_SPI_SPENDRX; /* disable interrupt */
/* Disable transmitter and receiver */
controller->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
device->xfers = NULL;
complete(&transfer_complete);
}
else if (list->curr+1 == list->nr_transfers) { /* no more next transfers */
device->txnext = 0;
device->rxnext = 0;
controller->SPI_TNCR = 0;
controller->SPI_RNCR = 0;
}
else {
int i = (list->curr)+1;
device->txnext = pci_map_single(NULL, list->tx, list->txlen, PCI_DMA_TODEVICE);
device->rxnext = pci_map_single(NULL, list->rx, list->rxlen, PCI_DMA_FROMDEVICE);
controller->SPI_TNPR = device->txnext;
controller->SPI_RNPR = device->rxnext;
controller->SPI_TNCR = list->txlen;
controller->SPI_RNCR = list->rxlen;
}
}
[ 本帖最后由 c_kun 于 2009-7-27 14:00 编辑 ] |
|