1. 程式人生 > >SPI核心驅動模型-dm8127

SPI核心驅動模型-dm8127

利用TI提供的已有SPI核心驅動模型,該方案需要我們瞭解核心的SPI驅動模型是如何實現的。

3.1.    SPI核心驅動模型
在2.6的linux核心中,SPI的驅動架構可以分為如下三個層次:SPI 核心層、SPI控制器驅動層和SPI裝置驅動層。Linux 中SPI驅動程式碼位於drivers/spi目錄。

3.1.1.    SPI核心層
SPI核心層是Linux的SPI核心部分,提供了核心資料結構的定義、SPI控制器驅動和裝置驅動的註冊、登出管理等API。其為硬體平臺無關層,向下遮蔽了物理匯流排控制器的差異,定義了統一的訪問策略和介面;其向上提供了統一的介面,以便SPI裝置驅動通過匯流排控制器進行資料收發。Linux中,SPI核心層的程式碼位於driver/spi/ spi.c。

3.1.2.    SPI控制器驅動層
SPI控制器驅動層,每種處理器平臺都有自己的控制器驅動,屬於平臺移植相關層。它的職責是為系統中每條SPI匯流排實現相應的讀寫方法。在物理上,每個SPI控制器可以連線若干個SPI從裝置。
在系統開機時,SPI控制器驅動被首先裝載。一個控制器驅動用於支援一條特定的SPI匯流排的讀寫。一個控制器驅動可以用資料結構struct spi_master來描述,定義在include/liunx/spi/spi.h檔案中。
針對DM8127,控制器驅動程式為omap2_mcspi.c,SPI 控制器驅動的註冊採用Platform device和Platform driver機制。

3.1.2.1.    SPI控制器的Platform device
Platform device的定義和註冊程式碼位於arch/arm/mach-omap2/devices.c中。
static struct platform_device omap2_mcspi1 = {
    .name        = "omap2_mcspi",
    .id        = 1,
    .num_resources    = ARRAY_SIZE(omap2_mcspi1_resources),
    .resource    = omap2_mcspi1_resources,
    .dev        = {
        .platform_data = &omap2_mcspi1_config,
    },
};

static struct platform_device omap2_mcspi2 = {
    .name        = "omap2_mcspi",
    .id        = 2,
    .num_resources    = ARRAY_SIZE(omap2_mcspi2_resources),
    .resource    = omap2_mcspi2_resources,
    .dev        = {
        .platform_data = &omap2_mcspi2_config,
    },
};

static struct platform_device omap2_mcspi3 = {
    .name        = "omap2_mcspi",
    .id        = 3,
    .num_resources    = ARRAY_SIZE(omap2_mcspi3_resources),
    .resource    = omap2_mcspi3_resources,
    .dev        = {
        .platform_data = &omap2_mcspi3_config,
    },
};

static struct platform_device omap2_mcspi4 = {
    .name        = "omap2_mcspi",
    .id        = 4,
    .num_resources    = ARRAY_SIZE(omap2_mcspi4_resources),
    .resource    = omap2_mcspi4_resources,
    .dev        = {
        .platform_data = &omap2_mcspi4_config,
    },
};
可以看到,這邊定義了4個SPI控制器的Platform device,id分別為“1,2,3, 4”,name都為“omap2_mcspi”,變數resource中定義了介面卡的暫存器基地址。

3.1.2.2.    Platform device的註冊如下
platform_device_register(&omap2_mcspi1);
platform_device_register(&omap2_mcspi2);
platform_device_register(&omap2_mcspi3);
platform_device_register(&omap2_mcspi4);
以上四個函式在核心啟動時完成。將Platform device註冊到Platform總線上,完成註冊後,暫存器的基地址等資訊會在裝置樹中描述,此後只需利用platform_get_resource等標準介面自動獲取即可,實現了驅動和資源的分離。

3.1.2.3.    SPI控制器的Platform driver
Platform driver的註冊程式碼位於核心的drivers/spi/omap2_mcspi.c中,該驅動的註冊目的是初始化DM8127的SPI 控制器,提供SPI匯流排資料傳輸的具體實現,並且向PSI核心層註冊SPI 控制器。

3.1.2.4.    Platform driver的定義如下
static struct platform_driver omap2_mcspi_driver = {
    .driver = {
        .name =        "omap2_mcspi",
        .owner =    THIS_MODULE,
        .pm =        &omap2_mcspi_pm_ops
    },
    .remove =    __exit_p(omap2_mcspi_remove),
};
核心啟動時會呼叫函式platform_driver_probe()來註冊Platform driver,該函式是帶有probe函式的平臺驅動註冊函式,註冊時,會掃描platform bus上的所有裝置,由於匹配因子name為" omap2_mcspi ",與之前註冊的Platform device匹配成功,於是函式omap2_mcspi_probe()將被呼叫,控制器device和driver將被繫結起來。
在檔案drivers/spi/omap2_mcspi.c中會涉及到一個數據結構omap2_mcspi,這個結構專門針對DM8127的SPI控制器,程式碼如下:
struct omap2_mcspi {
    struct work_struct    work;
    
    spinlock_t        lock;
    struct list_head    msg_queue;
    struct spi_master    *master;
    struct clk        *ick;
    struct clk        *fck;
    
    void __iomem        *base;
    unsigned long        phys;
    
    struct omap2_mcspi_dma    *dma_channels;
};
msg_queue對應訊息佇列。
master對應通用的spi控制器結構。
ick和fck分別對應介面時鐘和功能時鐘。
base對應SPI控制器暫存器的虛擬地址。
phys對應SPI控制器暫存器的實體地址。
dma_channels對應SPI控制器的DMA通道。  
函式omap2_mcspi_probe()的執行流程如下圖:
 
3.1.3.    SPI裝置驅動層
SPI裝置驅動層為使用者介面層,其為使用者提供了通過SPI匯流排訪問具體裝置的介面。
SPI裝置驅動層可以用兩個模組來描述,struct spi_driver和struct spi_device。
針對DM8127上的裝置,我們利用核心提供的SPI使用者驅動程式spidev.c

3.1.2.1.    DM8127上的設備註冊
裝置的建立和註冊分為兩步。
第一步:將裝置資訊加入到裝置連結串列
在板級初始化時將SPI裝置的名稱,地址和相關的資訊加入到連結串列board_list中,該連結串列定義在driver/spi/spi.c中,記錄了具體開發板上的SPI裝置資訊。
在board-ti8148ipnc.c中,裝置資訊定義如下:(這裡是我們主要修改的程式碼,新增spi裝置所對應的資訊,包括spi總訊號,也就是具體對應dm8127的哪個spi控制器;spi時鐘平率;片選,也就是對應dm8127的哪個片選訊號;工作模式即極性和相位)
struct spi_board_info __initdata ti8148_spi_slave_info[] = {
    {
        .modalias    = "dm8127_spidev",
        .irq        = -1,
        .max_speed_hz    = 100000,
        .bus_num    = 1,//AD9970掛在SPI0上,片選為cs0
        .chip_select    = 0,
        .mode = SPI_MODE_3,
    },
    {
        .modalias    = "dm8127_spidev",
        .irq        = -1,
        .max_speed_hz    = 100000,
        .bus_num    = 1, //AD9970掛在SPI0上,片選為cs1
        .chip_select    = 1,
        .mode = SPI_MODE_3,
    },
    {
        .modalias    = "dm8127_spidev",
        .irq        = -1,
        .max_speed_hz    = 100000,
        .bus_num    = 1, //eeprom掛在SPI0上,片選為cs2
        .chip_select    = 2,
        .mode = SPI_MODE_0,
    },
    {
        .modalias    = "dm8127_spidev",
        .irq        = -1,
        .max_speed_hz    = 100000,
        .bus_num    = 2, //fpga掛在SPI1上,片選為cs0
        .chip_select    = 0,
        .mode = SPI_MODE_0,
    },
}
裝置資訊通過函式spi_register_board_info()加入到連結串列board_list中,程式碼如下:
void __init ti8148_spi_init(void)
{
    spi_register_board_info(ti8148_spi_slave_info,
                ARRAY_SIZE(ti8148_spi_slave_info));
}
裝置加入到裝置連結串列board_list的流程圖如下:
 
第二步:裝置spi_device的建立並新增
spi_device的建立並新增是在SPI控制器驅動註冊過程中完成的,spi_register_master()函式在註冊SPI控制器驅動的過程會掃描SPI裝置連結串列board_list,如果該總線上有對應的SPI裝置,則建立相應的spi_device,並將其新增到SPI的裝置連結串列中。流程圖如下所示:
 
3.1.2.2.    DM8127上的裝置驅動註冊
在drivers/spi/spidev.c中,定義了裝置驅動,程式碼如下:
static struct spi_driver spidev_spi_driver = {
    .driver = {
        .name =        "dm8127_spidev",
        .owner =    THIS_MODULE,
    },
    .probe =    spidev_probe,
    .remove =    __devexit_p(spidev_remove),
};

static int __init spidev_init(void)
{
status = spi_register_driver(&spidev_spi_driver);    
}
註冊的簡要示意圖如下:
 
在模組載入的時候首先呼叫spidev_init(),然後spidev_init()呼叫函式spi_register_driver()註冊spider_spi_driver結構體。
函式spi_register_driver()初始化該驅動的匯流排為spi_bus_type,然後使用函式driver_register(&sdrv->driver)註冊該驅動,因此核心會在SPI總線上遍歷所有SPI裝置,由於該裝置驅動的匹配因子name變數為“dm8127_spidev”,因此正好和之前建立的chip->modalias為“dm8127_spidev”的SPI 裝置匹配。因此SPI裝置驅動的probe函式spidev_probe()將會被呼叫,完成一些具體裝置相關的初始化等操作,同時我們要針對不同spi裝置去配置一下spi傳輸的bits_per_word,程式碼如下:
if(spi->master->bus_num == 2)//spi1
{
    spi->bits_per_word = 32;
}
else if(spi->master->bus_num == 1)//spi0
{
    spi->bits_per_word = 8;
}
spi_setup(spi);