Linux spi system
SPI是由Motorola提出的一種全雙工同步串行通信接口基茵,通信波特率可以高達5Mbps盖袭,但具體速度大小取決于SPI硬件,SPI接口具有全雙工操作岖沛,操作簡單贺辰,數(shù)據(jù)傳輸速率較高的優(yōu)點虱岂,但也存在沒有指定的流控制竹捉,沒有應(yīng)答機制確認是否接收到數(shù)據(jù)的缺點筷屡。
1.Linux下SPI的驅(qū)動架構(gòu)
如下:
從圖中可以觀察到SPI系統(tǒng)的整個框架涧偷,發(fā)現(xiàn)跟I2C的框架很十分相似;
硬件和用戶空間就不多說了毙死,直接看內(nèi)核空間部分燎潮。
內(nèi)核空間主要分為三個模塊,控制器驅(qū)動扼倘,設(shè)備器驅(qū)動确封,和連接兩個驅(qū)動的SPI核心;
spi與i2c內(nèi)核空間的對比
- master就相當(dāng)于i2c中的apapter
- spi_device即i2c中的i2c_client
- spi core 即i2c core
不同于i2c的目錄框架比較忙明確再菊,i2c的控制器驅(qū)動在/drivers/i2c/busses/下面爪喘,設(shè)備器驅(qū)動在/drivers/i2c/chips/下面。而spi的控制器就直接放在spi/目錄下纠拔,設(shè)備器驅(qū)動根據(jù)功能分開在各個地方秉剑,如spi flash的一般就在/drivers/mtd/device/下,siwtch的spi接口就放在/drivers/net/ethernet/下稠诲。
2.SPI master驅(qū)動
SPI控制器的驅(qū)動分為兩部分侦鹏,device和driver,使用platform總線進行連接臀叙。
platform_device
的注冊一般在arch下面實現(xiàn)略水,使用platform_add_devices(arch_devices, ARRAY_SIZE(arch_devices));
進行添加,由于內(nèi)核中有很多使用platform注冊的總線匹耕,所以一般會統(tǒng)一使用一次platform_add_devices
函數(shù)進行添加聚请,如下:
static struct platform_device *arch_devices[] __initdata = {
&arch_nor,
&arch_i2c,
&arch_wdt,
&arch_spi,
&arch_tdm_device,
&arch_pfe_device,
}
一個arch_devices
里面包含很多platform_device
,每個device各自實現(xiàn)后,再一塊兒添加驶赏。
static struct platform_device comcerto_spi = {
.name = "comcerto_spi",
.id = 0,
.num_resources = ARRAY_SIZE(comcerto_spi_resource),
.resource = comcerto_spi_resource,
.dev = {
.platform_data = &ls_spi_pdata,
},
};
platform_driver
的實現(xiàn)在/driver/spi/中實現(xiàn)炸卑,使用platform_driver_register
和platform_driver_unregister
進行加載下載。
static struct platform_driver designware_spi_driver = {
.probe = designware_spi_probe,
.remove = __devexit_p(designware_spi_remove),
#if CONFIG_PM
.suspend = designware_spi_suspend,
.resume = designware_spi_resume,
#endif
.driver = {
.name = DESIGNWARE_SPI_NAME,
.owner = THIS_MODULE,
},
};
static int __init designware_spi_init(void)
{
return platform_driver_register(&designware_spi_driver);
}
static void __exit designware_spi_exit(void)
{
platform_driver_unregister(&designware_spi_driver);
}
module_init(designware_spi_init);
module_exit(designware_spi_exit);
designware_spi_probe
函數(shù)里面就是master的實現(xiàn)部分煤傍,里面會對spi進行很多處理盖文,如中斷/dma/等,最后會使用spi_register_master
函數(shù)進行注冊spi_master蚯姆,該函數(shù)就是spi的核心層了五续,位于/driver/spi/spi.c中,這一部分的內(nèi)容還不知道如何描述龄恋,后面有思路時在更新疙驾。
3.SPI device驅(qū)動
SPI設(shè)備的驅(qū)動也是分為兩部分,device和driver郭毕,使用spi總線進行連接它碎。
spi_device
的注冊一般在arch下面實現(xiàn),使用spi_register_board_info(arch_spi_board_info, ARRAY_SIZE(arch_spi_board_info))
函數(shù)進行添加显押,函數(shù)里面的arch_spi_board_info
是
spi_board_info
結(jié)構(gòu)體扳肛,需要我們對里面的參數(shù)進行配置,如下例子:
static struct spi_board_info comcerto_spi_board_info[] = {
{
/* FIXME: for chipselect-0 */
.modalias = "s25fl256s0",
.chip_select = 0,
.max_speed_hz = 4*1000*1000,
.bus_num = 1,
.irq = -1,
.mode = SPI_MODE_3,
.platform_data = &spi_pdata,
.controller_data = &spi_ctrl_data,
},
}
spi_driver
的實現(xiàn)也比較簡單乘碑,一般就是通過spi_register_driver
來進行添加挖息,spi_unregister_driver
進行卸載。
如下例子:
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.id_table = m25p_ids,
.probe = m25p_probe,
.remove = __devexit_p(m25p_remove),
};
static int __init m25p80_init(void)
{
return spi_register_driver(&m25p80_driver);
}
static void __exit m25p80_exit(void)
{
spi_unregister_driver(&m25p80_driver);
}
module_init(m25p80_init);
module_exit(m25p80_exit);
當(dāng)spi_driver.id_table
信息與spi_board_info
所指向的設(shè)備(或者設(shè)備樹中的節(jié)點)匹配成功兽肤,則執(zhí)行spi_driver.probe()
套腹,對應(yīng)probe函數(shù)里面實現(xiàn)的內(nèi)容就要根據(jù)spi所使用的場景進行對于的實現(xiàn)了。
如spi flash一般是作為存儲介質(zhì)位于mtd下面的轿衔,所以probe函數(shù)里面就要使用mtd_device_parse_register
來實現(xiàn)mtd的添加沉迹;switch的spi接口是用來跟switch進行數(shù)據(jù)通信,probe函數(shù)里面就可以使用register_chrdev_region
來添加個字符設(shè)備進行操作害驹。
調(diào)試驅(qū)動的時候最常用的方法就是使用printk來進行交互鞭呕,進行定位、驗證宛官,但是要在哪邊進行printk呢葫松,個人覺得調(diào)試spi驅(qū)動有一個地方一定要進行printk,那就是位于spi.c
下的spi_match_id()
函數(shù),如下:
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
const struct spi_device *sdev)
{
while (id->name[0]) {
if (!strcmp(sdev->modalias, id->name))
return id;
id++;
}
return NULL;
}
總結(jié):分析調(diào)試I2C/SPI的驅(qū)動后底洗,有以下幾點感悟:
- 不管什么驅(qū)動腋么,大致都可以分為兩個動作響應(yīng),或者說兩個probe接口亥揖,一個是控制器的probe(根據(jù)cpu的不同進行改變)珊擂,一個是設(shè)備的probe(根據(jù)設(shè)備的不同進行改變)圣勒。
- 每一個probe接口有與之對于的device和driver,并且需要一種總線相連摧扇。
- 控制器的probe一般使用platform總線圣贸,對應(yīng)
platform_device
和platform_driver
;設(shè)備的probe使用相關(guān)的總線扛稽,如i2c總線吁峻,則對應(yīng)i2c_device
和i2c_driver
,i2c總線在张,則對應(yīng)spi_device
和spi_driver
用含。 - probe函數(shù)的觸發(fā)都是對應(yīng)的
match_id
函數(shù),如platform_match_id
帮匾、i2c_match_id
啄骇、spi_match_id
。 - platform的device的注冊在arch下面辟狈,使用
platform_add_devices
添加肠缔,platform對應(yīng)的driver,位于各目錄下哼转,如i2c位于i2c/busses/里面,使用i2c_register_adapter
來添加槽华。 - 驅(qū)動如i2c的device的注冊位于arch下壹蔓,使用
i2c_register_board_info
或dts添加,對應(yīng)i2c的driver位于i2c/chips/下面猫态,使用i2c_add_driver
來添加佣蓉。
Linux spi system的分析就到這邊,有感悟時會持續(xù)會更新亲雪。
注:以上內(nèi)容都是本人在學(xué)習(xí)過程積累的一些心得勇凭,難免會有參考到其他文章的一些知識,如有侵權(quán)义辕,請及時通知我虾标,我將及時刪除或標(biāo)注內(nèi)容出處,如有錯誤之處也請指出灌砖,進行探討學(xué)習(xí)璧函。文章只是起一個引導(dǎo)作用,詳細的數(shù)據(jù)解析內(nèi)容還請查看Linux相關(guān)教程基显,感謝您的查閱蘸吓。