2008年3月24日 星期一

Linux SPI subsystem (2)

我們在上一篇文章中提到,master driver 負責初始化硬體控制器,寫入暫存器以及中斷處理。換句話說,它必須要能夠做到低階的硬體控制;在以 ARM 為基礎的 SoC 當中,也就是要實際上對 I/O memory 做讀寫的動作。因此撰寫 master driver 前,必須要先熟讀 SoC 的使用手冊,充分了解如何操作這些暫存器以支援相關的功能。

此外,master driver 還必須要將從上層傳送來的訊息封包加以解析,並將傳送該封包所需要的硬體參數設定事先寫入暫存器,最後才起始資料傳送。如果控制器支援中斷,那麼 master driver 也必須先註冊中斷常式以便處理例外狀況。接下來我們就來看看一些細節部分的資料結構與函式。

一般在 SoC 系統下,SPI 控制器會以 platform device 的形式存在,是故我們會將 master driver 註冊為一種 platform driver,接著在 probe 函式中呼叫 spi_alloc_master,該函式宣告如下:

struct spi_master *spi_alloc_master(struct device *host, unsigned size);

該函式會分配一塊記憶體並回傳指向 spi_master 結構的指標,參數 size 則可以指定額外的空間大小,該空間可以透過 spi_master_get_devdata 來取得,開發者可用以存放額外的資訊。接著我們需要設定一些關於該控制器的資訊,如:

master->bus_num = 0;
master->num_chipselect = 1;
master->setup = mysetup;
master->transfer = mytransfer;
master->cleanup = mycleanup;

其中 bus_num 是代表控制器的識別號碼,若指定 -1 則表示由系統動態分配。num_chipselect 是這個控制器所能連接的裝置編號的最大值,1 代表最多可連接 2 個裝置 (0, 1)。最後呼叫 spi_register_master 來向系統註冊即可。

當開始要進行傳輸時,在上面程式片段中的 setup 會在傳送前被呼叫,讓 master driver 根據周邊所能接受的傳輸模式參數來設定控制器;接著 transfer 會被呼叫,其參數包括要傳送的周邊以及指向 spi_message 結構的指標。其中 spi_message 結構還包含了一個或多個要傳輸的 buffer,定義在 spi_transfer 結構中。要注意的是,由於 transfer 函式中不可呼叫任何會進入睡眠的函式,是故實際的傳輸會由另外產生的 worker thread 來達成;並且在傳輸完成後,呼叫 spi_message 結構中的 complete 函式以通知 bus driver。最後則是 cleanup 函式被呼叫以釋放進行傳輸時所使用的資源。

另外當要卸載核心模組時,需要呼叫 spi_unregister_master 以解除註冊。

下一篇我們將會來討論 protocol driver 的部分。

2008年3月20日 星期四

Linux SPI subsystem (1)

目前進行中的項目是在 DaVinci 平台上開發 SPI 控制器驅動程式,不過由於手邊缺乏硬體平台可供測試的關係,現在仍然進度緩慢。不過這期間也研究了一下 Linux SPI 子系統,稍微有一些心得,來和各位分享一下。

SPI 是 Serial Peripheral Interface 的縮寫,它是一種串列式的 IO 介面,時脈約 1~ 70MHz,詳細資料可見 [1]。 DaVinci DM6446 上配備了一個 SPI 控制器,支援二個 chip select,時脈可達 33MHz。

Linux SPI 子系統將驅動程式分為三種類型:
  • SPI bus driver:
    主要目的為提供 API 給下層的 master driver 及上層的 protocol driver (稍後會提)來註冊,並傳送封包至下層的 driver。
  • SPI master driver:
    主要工作為初始化控制器,並負責對暫存器寫入以及中斷處理。
  • SPI protocol driver:
    主要為發送 SPI 訊息封包至所選擇的裝置。
從這樣的架構來看,bus driver 扮演了一個抽象層的角色,對上隱藏了硬體的細節,對下則隱藏了對特定裝置的通訊協定。是故 master driver 並不需要知道控制器接了什麼周邊,它只專注在如何操作控制器來將訊息封包送出;而 protocol driver 只需專心於和特定週邊的溝通,而不需了解要如何傳送訊息封包。

在子系統中,訊息封包的格式定義在 spi.h 的 spi_message 結構中。protocol driver 要傳送資料,只需填上 spi_message 結構相對應的欄位,並呼叫 spi_sync 以起始傳送。或者也可直接呼叫 spi_read/spi_write,參數則填入你的 buffer pointer 以及長度即可。

一般在嵌入式系統中,SPI 周邊多是直接固定在板子上,且硬體並無特別的方式可供偵測,因此我們需要在平台初始化時預先定義系統包含那些 SPI 周邊,而子系統也定義一個結構 spi_board_info 提供給開發者填入相對應的資訊,例如:

struct spi_board_info devices[] = {
     {
         .modalias = "mydevice",
         .max_speed_hz = 6000000,
         .bus_num = 0,
         .chip_select = 1,
         .mode = SPI_MODE_1,
     },
     {
         ......
     },
};

接著呼叫 spi_register_board_info 來註冊這些裝置。需要注意的是,modalias 欄位的值必須與 protocol driver 註冊的名字相同;bus driver 會以該值作為比對的依據。若需在執行時期註冊新的裝置,則可呼叫 spi_new_device 來加入。

關於 master driver 的部分留待下一篇來討論。

相關連結:
[1] Serial Peripheral Interface Bus
[2] TMS320DM644x DMSoC Serial Peripheral Interface (SPI) User's Guide

2008年3月18日 星期二

PINMUX patch

前幾天我送了一個 patch 到 mailing list 上,主要為加入檢查其他周邊是否被啟動的函式。檢查的方法即是看 PINMUX 這個暫存器中,代表各個周邊的 bit 是否被設定。由於在 DVEVM 中,很多周邊都有共用 pin 腳的情況發生,因此這個函式可供裝置驅動程式檢查之用。

2008年3月17日 星期一

新建第一篇

在百般思考之下,還是決定要建一個部落格來和網友們分享一下自已的心得。
基本上,本格會定位為一個嵌入式系統開發的筆記,目前主要著重在 DaVinci 平台下 Linux 系統的開發。未來會多放些和 DaVinci Linux 相關的研究筆記和嘗試在其上開發的程式碼。這些程式碼除了放在這外,也會 post 至 mailing list 上,希望大家不吝指教。

相關連結:
TI DaVinci
DaVinci Linux mailing list