SPI(Serial Peripheral Interface)?是一個同步的四線制串行線,用于連接微控制器和傳感器、存儲器及外圍設備。三條信號線持有時鐘信號(SCLK,經(jīng)常在10MHz左右)和并行數(shù)據(jù)線帶有“主出,從進(MOSI)”或是“主進,從出(MISO)”信號。數(shù)據(jù)交換的時候有四種時鐘模式,模式0和模式3是最經(jīng)常使用的。每個時鐘周期將會傳遞數(shù)據(jù)進和出。如果沒有數(shù)據(jù)傳遞的話,時鐘將不會循環(huán)。
SPI驅動分為兩類:
控制器驅動:它們通常內(nèi)嵌于片上系統(tǒng)處理器,通常既支持主設備,又支持從設備。這些驅動涉及硬件寄存器,可能使用DMA?;蛩鼈兪褂肎PIO引腳成為PIO bitbangers。這部分通常會由特定的開發(fā)板提供商提供,不用自己寫。
協(xié)議驅動:它們通過控制器驅動,以SPI連接的方式在主從設備之間傳遞信息。這部分涉及具體的SPI從設備,通常需要自己編寫。
那么特定的目標板如何讓Linux?操控SPI設備?下面以AT91SAM9260系列CAN設備驅動為例,Linux內(nèi)核版本為2.6.19。本文不涉及控制器驅動分析。
board_info提供足夠的信息使得系統(tǒng)正常工作而不需要芯片驅動加載
[cpp]?view plain?copy
在arch/arm/mach-at91rm9200/board-at91sam9260.c中有如下代碼:??
#include???
#include???
…….??
static?struct?spi_board_info?ek_spi_devices[]?=?{??
/*?spi?can?,add?by?mrz?*/??
#if?defined(CONFIG_CAN_MCP2515)????
{??
.modalias?=?"mcp2515",??
.chip_select?=?0,??
//??????.controller_data?=?AT91_PIN_PB3,??
.irq?=?AT91_PIN_PC6,?//AT91SAM9260_ID_IRQ0,??
.platform_data?=?&mcp251x_data,??
.max_speed_hz?=?10?*?1000?*?1000,??
.bus_num?=?1,??
.mode?=?0,??
}??
#endif??
};.??
………??
static?void?__init?ek_board_init(void)??
{??
……??
/*?SPI?*/??
at91_add_device_spi(ek_spi_devices,?ARRAY_SIZE(ek_spi_devices));??
}??
這樣在Linux初始化時候就可以加載SPI CAN驅動。
下面來看MCP2515 CAN驅動的結構,協(xié)議驅動有點類似平臺設備驅動,本文只列出框架,不涉及具體實現(xiàn)代碼,在/driver/CAN/MCP2515.c中:
[c-sharp]?view plain?copy
static?struct?spi_driver?mcp251x_driver?=?{??
.driver?=?{??
.name???=?mcp2515,??
.bus????=?&spi_bus_type,??
.owner??=?THIS_MODULE,??
},??
.probe??=?mcp251x_probe,??
.remove?=?__devexit_p(mcp251x_remove),??
#ifdef?CONFIG_PM??
.suspend????=?mcp251x_suspend,??
.resume?=?mcp251x_resume,??
#endif??
};??
驅動將自動試圖綁定驅動到任何board_info給定別名為"?mcp2515"的SPI設備。??
static?int?__devinit?mcp251x_probe(struct?spi_device?*spi)??
{??
struct?mcp251x?*chip;??
int?ret?=?0;??
dev_dbg(&spi->dev,?"%s:?start/n",??__FUNCTION__);??
chip?=?kmalloc(sizeof(struct?mcp251x),?GFP_KERNEL);??
if?(!chip)?{??
ret?=?-ENOMEM;??
goto?error_alloc;??
}??
dev_set_drvdata(&spi->dev,?chip);??
chip->txbin?=?chip->txbout?=?0;??
chip->rxbin?=?chip->rxbout?=?0;??
chip->count?=?0;??
chip->spi?=?spi;??
init_MUTEX(&chip->lock);??
init_MUTEX(&chip->txblock);??
init_MUTEX(&chip->rxblock);??
init_waitqueue_head(&chip->wq);??
#if?(LINUX_VERSION_CODE?>=?KERNEL_VERSION(2,6,20))??
INIT_WORK(&chip->irq_work,?mcp251x_irq_handler);??
#else??
INIT_WORK(&chip->irq_work,?mcp251x_irq_handler,?spi);??
#endif??
chip->spi_transfer_buf?=?kmalloc(SPI_TRANSFER_BUF_LEN,?GFP_KERNEL);??
if?(!chip->spi_transfer_buf)?{??
ret?=?-ENOMEM;??
goto?error_buf;??
}??
ret?=?request_irq(spi->irq,?mcp251x_irq,?SA_SAMPLE_RANDOM,?DRIVER_NAME,?spi);??
if?(ret?0)?{??
dev_err(&spi->dev,?"request?irq?%d?failed?(ret?=?%d)/n",?spi->irq,?ret);??
goto?error_irq;??
}??
cdev_init(&chip->cdev,?&mcp251x_fops);??
chip->cdev.owner?=?THIS_MODULE;??
ret?=?cdev_add(&chip->cdev,?MKDEV(MAJOR(can_devt),?can_minor),?1);??
if?(ret?0)?{??
dev_err(&spi->dev,?"register?char?device?failed?(ret?=?%d)/n",?ret);??
goto?error_register;??
}??
chip->class_dev?=?class_device_create(can_class,?NULL,??
MKDEV(MAJOR(can_devt),?can_minor),??
&spi->dev,?"can%d",?can_minor);??
if?(IS_ERR(chip->class_dev))?{??
dev_err(&spi->dev,?"cannot?create?CAN?class?device/n");??
ret?=?PTR_ERR(chip->class_dev);??
goto?error_class_reg;??
}??
dev_info(&spi->dev,?"device?register?at?dev(%d:%d)/n",??
MAJOR(can_devt),?can_minor);??
mcp251x_hw_init(spi);??
mcp251x_set_bit_rate(spi,?125000);?/*?A?reasonable?default?*/??
mcp251x_hw_sleep(spi);??
can_minor++;??
return?0;??
error_class_reg:??
cdev_del(&chip->cdev);??
error_register:??
free_irq(spi->irq,?spi);??
error_irq:??
kfree(chip->spi_transfer_buf);??
error_buf:??
kfree(chip);??
error_alloc:??
return?ret;??
}??
一旦進入probe(),驅動使用"struct spi_message"向SPI設備要求I/O。當remove()返回時,驅動保證將不會提交任何這種信息。
一個spi_message是協(xié)議操作序列,以一個原子序列執(zhí)行。SPI驅動控制包括:
????(1)當雙向讀寫開始,是根據(jù)spi_transfer要求序列是怎樣安排的。
????(2)隨意設定傳遞后的短延時,使用spi_transfer.delay_usecs設定。
????(3)在一次傳遞和任何延時之后,無論片選是否活躍,使用spi_transfer.cs_change標志,????暗示下條信息是否進入這個同樣的設備,使用原子組中最后一次傳輸上的spi_transfer.cs_change標志位,可能節(jié)省芯片選擇取消操作的成本。
?
評論
查看更多