⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at91_spi.c

📁 英贝德ebd9200-I开发板linux下CAN测试程序(MCP2510)
💻 C
字号:
/* * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200 (Thunder) * * (c) SAN People (Pty) Ltd * * This program 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 of the License, or (at your option) any later version. */#include <linux/init.h>#include <linux/module.h>#include <asm/semaphore.h>#include <linux/pci.h>#include <linux/sched.h>#include <linux/completion.h>#include <asm/arch/AT91RM9200_SPI.h>#include <asm/arch/pio.h>#include "at91_spi.h"#undef DEBUG_SPI//#define DEBUG_SPI	1static 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 */static 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();  break;			case 1: AT91_CfgPIO_SPI_CS1();  break;			case 2: AT91_CfgPIO_SPI_CS2();  break;			case 3: AT91_CfgPIO_SPI_CS3();  break;		}		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. */static 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[i], list->txlen[i], PCI_DMA_TODEVICE);		device->rxnext = pci_map_single(NULL, list->rx[i], list->rxlen[i], PCI_DMA_FROMDEVICE);		controller->SPI_TNPR = device->txnext;		controller->SPI_RNPR = device->rxnext;		controller->SPI_TNCR = list->txlen[i];		controller->SPI_RNCR = list->rxlen[i];	}}/* ......................................................................... *//* * Initialize the SPI controller */static int __init at91_spi_init(void){	init_MUTEX(&spi_lock);	AT91_CfgPIO_SPI();	controller->SPI_CR = AT91C_SPI_SWRST;	/* software reset of SPI controller */	/* Set Chip Select registers to good defaults */	controller->SPI_CSR0 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8);	controller->SPI_CSR1 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_SPI_BAUD << 8);//	controller->SPI_CSR2 = (0x100000) | ((MASTER_CLOCK / (2*MCP2510_CLK)) << 8);	controller->SPI_CSR2 =  AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_MCP2510_BAUD << 8);	controller->SPI_CSR3 = AT91C_SPI_CPOL | AT91C_SPI_BITS_8 | (16 << 16) | (DEFAULT_MCP2510_BAUD << 8);	controller->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;	memset(&spi_dev, 0, sizeof(spi_dev));	spi_dev[0].pcs = 0xE;	spi_dev[1].pcs = 0xD;	spi_dev[2].pcs = 0xB;	spi_dev[3].pcs = 0x7;	if (request_irq(AT91C_ID_SPI, spi_interrupt, 0, "spi", NULL))		return -EBUSY;	controller->SPI_CR = AT91C_SPI_SPIEN;		/* Enable SPI */	return 0;}static void at91_spi_exit(void){	controller->SPI_CR = AT91C_SPI_SPIDIS;		/* Disable SPI */	free_irq(AT91C_ID_SPI, 0);}EXPORT_SYMBOL(spi_access_bus);EXPORT_SYMBOL(spi_release_bus);EXPORT_SYMBOL(spi_transfer);module_init(at91_spi_init);module_exit(at91_spi_exit);MODULE_LICENSE("GPL")MODULE_AUTHOR("Andrew Victor")MODULE_DESCRIPTION("SPI driver for Atmel AT91RM9200")

⌨️ 快捷键说明

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