SPI(Serial Peripheral Interface)?是一個(gè)同步的四線(xiàn)制串行線(xiàn),用于連接微控制器和傳感器、存儲(chǔ)器及外圍設(shè)備。三條信號(hào)線(xiàn)持有時(shí)鐘信號(hào)(SCLK,經(jīng)常在10MHz左右)和并行數(shù)據(jù)線(xiàn)帶有“主出,從進(jìn)(MOSI)”或是“主進(jìn),從出(MISO)”信號(hào)。數(shù)據(jù)交換的時(shí)候有四種時(shí)鐘模式,模式0和模式3是最經(jīng)常使用的。每個(gè)時(shí)鐘周期將會(huì)傳遞數(shù)據(jù)進(jìn)和出。如果沒(méi)有數(shù)據(jù)傳遞的話(huà),時(shí)鐘將不會(huì)循環(huán)。
SPI驅(qū)動(dòng)分為兩類(lèi):
控制器驅(qū)動(dòng):它們通常內(nèi)嵌于片上系統(tǒng)處理器,通常既支持主設(shè)備,又支持從設(shè)備。這些驅(qū)動(dòng)涉及硬件寄存器,可能使用DMA?;蛩鼈兪褂肎PIO引腳成為PIO bitbangers。這部分通常會(huì)由特定的開(kāi)發(fā)板提供商提供,不用自己寫(xiě)。
協(xié)議驅(qū)動(dòng):它們通過(guò)控制器驅(qū)動(dòng),以SPI連接的方式在主從設(shè)備之間傳遞信息。這部分涉及具體的SPI從設(shè)備,通常需要自己編寫(xiě)。
那么特定的目標(biāo)板如何讓Linux?操控SPI設(shè)備?下面以AT91SAM9260系列CAN設(shè)備驅(qū)動(dòng)為例,Linux內(nèi)核版本為2.6.19。本文不涉及控制器驅(qū)動(dòng)分析。
board_info提供足夠的信息使得系統(tǒng)正常工作而不需要芯片驅(qū)動(dò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初始化時(shí)候就可以加載SPI CAN驅(qū)動(dòng)。
下面來(lái)看MCP2515 CAN驅(qū)動(dòng)的結(jié)構(gòu),協(xié)議驅(qū)動(dòng)有點(diǎn)類(lèi)似平臺(tái)設(shè)備驅(qū)動(dòng),本文只列出框架,不涉及具體實(shí)現(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??
};??
驅(qū)動(dòng)將自動(dòng)試圖綁定驅(qū)動(dòng)到任何board_info給定別名為"?mcp2515"的SPI設(shè)備。??
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;??
}??
一旦進(jìn)入probe(),驅(qū)動(dòng)使用"struct spi_message"向SPI設(shè)備要求I/O。當(dāng)remove()返回時(shí),驅(qū)動(dòng)保證將不會(huì)提交任何這種信息。
一個(gè)spi_message是協(xié)議操作序列,以一個(gè)原子序列執(zhí)行。SPI驅(qū)動(dòng)控制包括:
????(1)當(dāng)雙向讀寫(xiě)開(kāi)始,是根據(jù)spi_transfer要求序列是怎樣安排的。
????(2)隨意設(shè)定傳遞后的短延時(shí),使用spi_transfer.delay_usecs設(shè)定。
????(3)在一次傳遞和任何延時(shí)之后,無(wú)論片選是否活躍,使用spi_transfer.cs_change標(biāo)志,????暗示下條信息是否進(jìn)入這個(gè)同樣的設(shè)備,使用原子組中最后一次傳輸上的spi_transfer.cs_change標(biāo)志位,可能節(jié)省芯片選擇取消操作的成本。
?
評(píng)論