linux spi驅動分析

2021-06-10 18:34:28 字數 4753 閱讀 9717

關於spi的學習,我覺得最好的方法還是看linux的源**,主要是driver/spi/spi.c(h),spidev.c(h)。spi dev的示例可以看看at25.c,spi匯流排的示例可以看omap_uwire或者spi_s3c24xx.c和spi_s3c24xx_gpio.c。在看這些**之前,需要對linux的裝置模型有一定的了解。

下面是我整理的關於spi的一些心得,核心版本2.6.29。

spi子系統

spi子系統中,spi裝置用struct spi_dev描述,它的驅動程式用struct spi_driver描述。spi匯流排裝置用struct spi_master描述。另外,還有兩個重要的全域性變數:

struct bus_typespi_bus_type = ;

static structclass spi_master_class = ;

spi_bus_type對應sys中的spi bus匯流排,linux裝置模型對這個結構體有詳細介紹。

所有spi_master對應的spi匯流排都屬於spi_master_class,也就是說是乙個虛擬裝置,它的父裝置可能是物理裝置,比如platform_device等等,s3c2410就是這種情況。

spi裝置

spi裝置的驅動程式通過spi_register_driver註冊進spi子系統,驅動型別為struct spi_driver。典型例子如at25.c。

static structspi_driver at25_driver = ,

.probe            =at25_probe,

.remove          =__devexit_p(at25_remove),

};因為spi匯流排不支援spi裝置的自動檢測,所以一般在spi的probe函式中不會檢測裝置是否存在,而是做一些spi裝置的初始化工作。

spi驅動中可以呼叫下列函式進行spi的傳輸操作:

static inline intspi_write(struct spi_device *spi, const u8 *buf, size_t len);

static inline intspi_read(struct spi_device *spi, u8 *buf, size_t len);

extern intspi_write_then_read(struct spi_device*spi,        const u8 *txbuf, unsignedn_tx,

u8*rxbuf, unsigned n_rx);

static inlinessize_t spi_w8r8(struct spi_device *spi, u8 cmd);

static inlinessize_t spi_w8r16(struct spi_device *spi, u8 cmd);

由於spi裝置不能被spi匯流排動態掃瞄,所以spi子系統使用了另一種方法,就是通過spi_register_board_info函式將spi裝置靜態得登記到系統中。

int __initspi_register_board_info(struct spi_board_info const *info, unsigned n);

structspi_board_info ;

在具體平台的檔案中,可以定義structspi_board_info的結構體,然後通過spi_register_board_info函式儲存這些結構體,最後在scan_boardinfo函式中根據這些儲存的結構體建立spi裝置(spi_new_device)。

spi_new_device用於登記spi裝置,這裡面又分兩步,首先是spi_alloc_device,然後是spi_add_device。

struct spi_device*spi_new_device(struct spi_master *master, struct spi_board_info *chip)

spi_dev*pdev = spi_alloc(master);

proxy->chip_select= chip->chip_select;

proxy->max_speed_hz= chip->max_speed_hz;

proxy->mode= chip->mode;

proxy->irq= chip->irq;

strlcpy(proxy->modalias,chip->modalias, sizeof(proxy->modalias));

proxy->dev.platform_data= (void *) chip->platform_data;

proxy->controller_data= chip->controller_data;

proxy->controller_state= null;

spi_add_device(proxy);

struct spi_device*spi_alloc_device(struct spi_master *master)

struct device * dev= master->dev.parent;

structspi_dev * spi = kzalloc(sizeof *spi, gfp_kernel);

spi->master= master;

spi->dev.parent =dev;

spi->dev.bus= &spi_bus_type;

spi->dev.release= spidev_release;

device_initialize(&spi->dev);

這裡spi_dev的父裝置被指定為master的父裝置,而master是spi匯流排裝置,擁有class,是乙個虛擬裝置。也就是說,spi裝置和與之對應的匯流排裝置擁有同乙個父裝置,這個父裝置一般來說是乙個物理裝置。

intspi_add_device(struct spi_device *spi)

snprintf(spi->dev.bus_id,sizeof spi->dev.bus_id, "%s.%u", spi->master->dev.bus_id,

spi->chip_select);

status= spi->master->setup(spi);

status =device_add(&spi->dev);

spi匯流排

struct spi_master;

登記spi匯流排

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

intspi_register_master(struct spi_master *master);

scan_boardinfo(master);

spi_register_master中會呼叫scan_boardinfo。scan_boardinfo中,會掃瞄前面儲存的boardinfo,看新註冊的master中的bus_num是否與boardinfo中bus_num匹配,如果匹配,那就呼叫spi_new_device建立spi裝置,並登記到spi子系統中。

setup函式

setup函式會做一些初始化工作。比如,根據spi裝置的速率,裝置paster的位傳輸定時器;設定spi傳輸型別;等等。

spi_add_device函式中,會先呼叫setup函式,然後再呼叫device_add。這是因為device_add中會呼叫到driver的probe函式,而probe函式中可能會對spi裝置做io操作。所以spi子系統就先呼叫setup為可能的io操作做好準備。

但是,在**中,setup函式似乎也就只在這乙個地方被呼叫。具體傳輸過程中切換spi裝置時也要做配置工作,但這裡的配置工作就由具體傳輸的實現**決定了,可以看看spi_bitbang.c中的函式bitbang_work。

cleanup函式

cleanup函式會在spidev_release函式中被呼叫,spidev_release被登記為spi dev的release函式。

transfer函式

transfer函式用於spi的io傳輸。但是,transfer函式一般不會執行真正的傳輸操作,而是把要傳輸的內容放到乙個佇列裡,然後呼叫一種類似底半部的機制進行真正的傳輸。這是因為,spi匯流排一般會連多個spi裝置,而spi裝置間的訪問可能會併發。如果直接在transfer函式中實現傳輸,那麼會產生競態,spi裝置互相間會干擾。

所以,真正的spi傳輸與具體的spi控制器的實現有關,spi的框架**中沒有涉及。像spi裝置的片選、根據具體裝置進行時鐘調整等等都在實現傳輸的**中被呼叫。

spi的傳輸命令都是通過結構體spi_message定義。裝置程式呼叫transfer函式將spi_message交給spi匯流排驅動,匯流排驅動再將message傳到底半部排隊,實現序列化傳輸。

struct spi_message;

spi_message中,有乙個transfers佇列,spi_transfer結構體通過這個佇列掛到spi_message中。乙個spi_message代表一次傳輸會話,spi_transfer代表一次單獨的io操作。比如,有些spi裝置需要先讀後寫,那麼這個讀寫過程就是一次spi會話,裡面包括兩個transfer,乙個定義寫操作的引數,另乙個定義讀操作的引數。

spidev.c

如果不想為自己的spi裝置寫驅動,那麼可以用linux自帶的spidev.c提供的驅動程式。要使用spidev.c的驅動,只要在登記裝置時,把裝置名設定成spidev就可以。spidev.c會在device目錄下自動為每乙個匹配的spi裝置建立裝置節點,節點名」spi%d」。之後,使用者程式可以通過字元型裝置的通用介面控制spi裝置。

需要注意的是,spidev建立的裝置在裝置模型中屬於虛擬裝置,它的class是spidev_class。它的父裝置是在boardinfo中定義的spi裝置。

linux spi驅動分析 基於STM32

linux kernel 版本2.6.30,spi驅動基於platform device,platform driver驅動模型來編寫.spi分為主從裝置,乙個主裝置下可心掛接多個從裝置,linux驅動中使用struct spi master結構來表示乙個主裝置,使用struct spi devic...

LINUX SPI驅動筆記

spi匯流排由miso 序列資料輸入 mosi 序列資料輸出 sck 序列移位時鐘 cs 使能訊號 4個訊號線組成 linux下spi驅動開發 首先明確spi驅動層次,如下圖 我們以上面的這個圖為思路 spi bus spi匯流排對應的匯流排型別為spi bus type,在核心的drivers s...

嵌入式Linux SPI驅動

1.1 spi主機驅動 linux使用spi master結構體表示spi主機驅動,定義在 drivers linux spi spi.h transfer 資料傳輸函式 transfer one message spi傳送護具函式,傳送乙個spi message資料 1.2 spi裝置驅動 lin...