Linux spi system

題圖:gratisography

Linux spi system

SPI是由Motorola提出的一種全雙工同步串行通信接口基茵,通信波特率可以高達5Mbps盖袭,但具體速度大小取決于SPI硬件,SPI接口具有全雙工操作岖沛,操作簡單贺辰,數(shù)據(jù)傳輸速率較高的優(yōu)點虱岂,但也存在沒有指定的流控制竹捉,沒有應(yīng)答機制確認是否接收到數(shù)據(jù)的缺點筷屡。

1.Linux下SPI的驅(qū)動架構(gòu)


如下:

SPI框架

從圖中可以觀察到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_registerplatform_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_deviceplatform_driver;設(shè)備的probe使用相關(guān)的總線扛稽,如i2c總線吁峻,則對應(yīng)i2c_devicei2c_driver,i2c總線在张,則對應(yīng)spi_devicespi_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)教程基显,感謝您的查閱蘸吓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诸衔,一起剝皮案震驚了整個濱河市赶么,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鹃锈,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件宪萄,死亡現(xiàn)場離奇詭異艺谆,居然都是意外死亡,警方通過查閱死者的電腦和手機雨膨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門擂涛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人聊记,你說我怎么就攤上這事撒妈。” “怎么了排监?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵狰右,是天一觀的道長。 經(jīng)常有香客問我舆床,道長棋蚌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任挨队,我火速辦了婚禮谷暮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盛垦。我一直安慰自己湿弦,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布腾夯。 她就那樣靜靜地躺著颊埃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝶俱。 梳的紋絲不亂的頭發(fā)上班利,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音榨呆,去河邊找鬼罗标。 笑死,一個胖子當(dāng)著我的面吹牛愕提,可吹牛的內(nèi)容都是我干的馒稍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼浅侨,長吁一口氣:“原來是場噩夢啊……” “哼纽谒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起如输,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鼓黔,失蹤者是張志新(化名)和其女友劉穎央勒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澳化,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡崔步,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了缎谷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片井濒。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖列林,靈堂內(nèi)的尸體忽然破棺而出瑞你,到底是詐尸還是另有隱情,我是刑警寧澤希痴,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布者甲,位于F島的核電站,受9級特大地震影響砌创,放射性物質(zhì)發(fā)生泄漏虏缸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一嫩实、第九天 我趴在偏房一處隱蔽的房頂上張望刽辙。 院中可真熱鬧,春花似錦甲献、人聲如沸扫倡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至疚鲤,卻和暖如春锥累,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背集歇。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工桶略, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诲宇。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓际歼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親姑蓝。 傳聞我的和親對象是個殘疾皇子鹅心,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容