Qualcomm平臺的SPI驅動框架分析
-
一、SPI匯流排概述
-
SPI是英語Serial Peripheral interface的縮寫,顧名思義就是序列外圍裝置介面,是Motorola首先在其MC68HCXX系列處理器上定義的。SPI介面主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字訊號處理器和數字訊號解碼器之間。SPI是一種高速的,全雙工,同步的通訊匯流排,並且在晶片的管腳上只佔用四根線,節約了晶片的管腳,同時為PCB的佈局上節省空間,提供方便。
-
SPI的通訊原理很簡單,它以主從方式工作,這種模式通常有一個主裝置和一個或多個從裝置,需要4根線,事實上3根也可以。也是所有基於SPI的裝置共有的,它們是SDI(
-
MOSI(SDO):主器件資料輸出,從器件資料輸入。
-
MISO(SDI):主器件資料輸入,從器件資料輸出。
-
SCLK :時鐘訊號,由主器件產生。
-
CS:從器件使能訊號,由主器件控制。
-
其中CS是控制晶片是否被選中的,也就是說只有片選訊號為預先規定的使能訊號時(高電位或低電位),對此晶片的操作才有效,這就允許在同一總線上連線多個SPI裝置成為可能。需要注意的是,在具體的應用中,當一條SPI總線上連線有多個裝置時,SPI本身的CS有可能被其他的GPIO腳代替,即每個裝置的CS腳被連線到處理器端不同的GPIO,通過操作不同的GPIO口來控制具體的需要操作的SPI裝置,減少各個SPI裝置間的干擾。
-
SPI是序列通訊協議,也就是說資料是一位一位從MSB或者LSB開始傳輸的,這就是SCK時鐘線存在的原因,由SCK提供時鐘脈衝,MISO、MOSI則基於此脈衝完成資料傳輸。 SPI支援4-32bits的序列資料傳輸,支援MSB和LSB,每次資料傳輸時當從裝置的大小端發生變化時需要重新設定SPI Master的大小端。
-
二、Linux SPI驅動總體架構
-
在2.6的linux核心中,SPI的驅動架構可以分為如下三個層次:SPI 核心層、SPI控制器驅動層和SPI裝置驅動層。Linux 中SPI驅動程式碼位於drivers/spi目錄。
-
1.SPI核心層
-
SPI核心層是Linux的SPI核心部分,提供了核心資料結構的定義、SPI控制器驅動和裝置驅動的註冊、登出管理等API。其為硬體平臺無關層,向下遮蔽了物理匯流排控制器的差異,定義了統一的訪問策略和介面;其向上提供了統一的介面,以便SPI裝置驅動通過匯流排控制器進行資料收發。
-
Linux中,SPI核心層的程式碼位於driver/spi/spi.c。
-
2.SPI控制器驅動層
-
SPI控制器驅動層,每種處理器平臺都有自己的控制器驅動,屬於平臺移植相關層。它的職責是為系統中每條SPI匯流排實現相應的讀寫方法。在物理上,每個SPI控制器可以連線若干個SPI從裝置。
-
在系統開機時,SPI控制器驅動被首先裝載。一個控制器驅動用於支援一條特定的SPI匯流排的讀寫。一個控制器驅動可以用資料結構struct spi_master來描述。
-
3.SPI裝置驅動層
-
SPI裝置驅動層為使用者介面層,其為使用者提供了通過SPI匯流排訪問具體裝置的介面。
-
SPI裝置驅動層可以用兩個模組來描述,struct spi_driver和struct spi_device。
-
Driver是為device服務的,spi_driver註冊時會掃描SPI bus上的裝置,進行驅動和裝置的繫結,probe函式用於驅動和裝置匹配時被呼叫。從上面的結構體註釋中我們可以知道,SPI的通訊是通過訊息佇列機制,而不是像I2C那樣通過與從裝置進行對話的方式。
-
三、spi子系統主要資料結構
-
1.在Linux中,使用spi_master結構來描述一個SPI主機控制器的驅動。
-
struct spi_master {
-
struct device dev;
-
s16 bus_num; //為該控制器對應的SPI匯流排號
-
u16 num_chipselect; //控制器支援的片選數量,即能支援多少個spi裝置,從裝置的片選號不能大於這個數量
-
u16 dma_alignment;
-
int (*setup)(struct
spi_device *spi);//函式是設定SPI匯流排的模式,時鐘等的初始化函式,
針對裝置設定SPI的工作時鐘及資料傳輸模式等。在spi_add_device函式中呼叫
-
int (*transfer)(struct
spi_device *spi,struct spi_message *mesg);//實現SPI匯流排讀寫方法的函式。實現資料的雙向傳輸,可能會睡眠
-
void (*cleanup)(struct spi_device *spi); //登出時候呼叫
-
};
-
//分配,註冊和登出的SPI主機的API由SPI核心提供:
-
struct spi_master *spi_alloc_master(struct device *host, unsigned
size);
-
int spi_register_master(struct spi_master *master);
-
void spi_unregister_master(struct spi_master *master);
-
2.在Linux中用spi_driver來描述一個SPI外設驅動。
-
struct spi_driver {
-
const struct spi_device_id *id_table;//匹配的裝置表
-
int (*probe)(struct
spi_device *spi);
-
int (*remove)(struct
spi_device *spi);
-
void (*shutdown)(struct spi_device *spi);
-
int (*suspend)(struct
spi_device *spi, pm_message_t mesg);
-
int (*resume)(struct
spi_device *spi);
-
struct device_driver driver;
-
};
-
3.Linux用spi_device來描述一個SPI外設裝置。
-
struct spi_device {
-
struct device dev;
-
struct spi_master *master;//對應的控制器指標
-
u32 max_speed_hz;//spi通訊的時鐘
-
u8 chip_select; //片選,用於區分同一總線上的不同裝置
-
u8 mode;
-
#define SPI_CPHA 0x01 /* clock phase */
-
#define SPI_CPOL 0x02 /* clock polarity */
-
#define SPI_MODE_0 (0|0) /* (original
MicroWire) */
-
#define SPI_MODE_1 (0|SPI_CPHA)
-
#define SPI_MODE_2 (SPI_CPOL|0)
-
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
-
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
-
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
-
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
-
#define SPI_LOOP 0x20 /* loopback mode */
-
#define SPI_NO_CS 0x40 /* 1 dev/bus, no
chipselect */
-
#define SPI_READY 0x80 /* slave pulls low to pause */
-
u8 bits_per_word;//每個字長的位元數
-
int irq;//使用的中斷號
-
void *controller_state;
-
void *controller_data;
-
char modalias[SPI_NAME_SIZE];//名字
-
};
-
看這三個結構的關係,這裡spi_device與spi_master是同一個父裝置,這是在spi_new_device函式中設定的,一般這個裝置是一個物理裝置。
-
-
4.為了解決多個不同的SPI裝置共享SPI控制器而帶來的訪問衝突,spi_bitbang使用核心提供的工作佇列(workqueue)。 workqueue是Linux核心中定義的一種回撥處理方式。採用這種方式需要傳輸資料時,不直接完成資料的傳輸,而是將要傳輸的工作分裝成相應的訊息 (spi_message),傳送給對應的workqueue,由與workqueue關聯的核心守護執行緒(daemon)負責具體的執行。由於
workqueue會將收到的訊息按時間先後順序排列,這樣就是對裝置的訪問嚴格序列化,解決了衝突。
-
struct spi_bitbang {
-
struct workqueue_struct *workqueue;//工作佇列頭
-
struct work_struct work;//每一次傳輸都傳遞下來一個spi_message,都向工作佇列頭新增一個
-
workspinlock_t lock;
-
struct list_head queue;//掛接spi_message,如果上一次的spi_message還沒有處理完,接下來的spi_message就掛接在queue上等待處理
-
u8 busy;//忙碌標誌
-
u8 use_dma;
-
u8 flags;
-
struct spi_master *master;//指向spi控制器
-
int (*setup_transfer)(struct
spi_device *spi,struct spi_transfer *t);//設定傳輸模式
-
void (*chipselect)(struct spi_device *spi, int is_on);//片選
-
#define BITBANG_CS_ACTIVE 1 /* normally nCS, active low */
-
#define BITBANG_CS_INACTIVE 0
-
int (*txrx_bufs)(struct
spi_device *spi, struct spi_transfer *t);//傳輸函式
-
u32 (*txrx_word[4])(struct
spi_device *spi,unsigned nsecs,u32 word, u8
bits);
-
};
-
5.下面來看看spi_message:
-
struct spi_message {
-
struct list_head transfers; //此次訊息的傳輸佇列,一個訊息可以包含多個傳輸段
-
struct spi_device *spi; //傳輸的目的裝置
-
unsigned is_dma_mapped:1; //如果為真,此次呼叫提供dma和cpu虛擬地址
-
void (*complete)(void *context); //非同步呼叫完成後的回撥函式
-
void *context; //回撥函式的引數
-
unsigned actual_length; //此次傳輸的實際長度
-
int status; //執行的結果,成功被置0,否則是一個負的錯誤碼
-
struct list_head queue;
-
void *state;
-
};
-
在有訊息需要傳遞的時候,會將spi_transfer通過自己的transfer_list欄位掛到spi_message的transfers連結串列頭上。spi_message用來原子的執行spi_transfer表示的一串陣列傳輸請求。這個傳輸佇列是原子的,這意味著在這個訊息完成之前不會有其 他訊息佔用匯流排。訊息的執行總是按照FIFO的順序。
-
6.下面看一看spi_transfer:
-
struct spi_transfer {
-
const void *tx_buf; //要寫入裝置的資料(必須是dma_safe),或者為NULL
-
void *rx_buf; //要讀取的資料緩衝(必須是dma_safe),或者為NULL
-
unsigned len; //tx和rx的大小(位元組數),這裡不是指它的和,而是各自的長度,他們總是相等的
-
dma_addr_t tx_dma; //如果spi_message.is_dma_mapped是真,這個是tx的dma地址
-
dma_addr_t rx_dma; //如果spi_message.is_dma_mapped是真,這個是rx的dma地址
-
unsigned cs_change:1; //影響此次傳輸之後的片選,指示本次tranfer結束之後是否要重新片選並呼叫setup改變設定,這個標誌可以較少系統開銷u8
-
bits_per_word; //每個字長的位元數,如果是0,使用預設值
-
u16 delay_usecs; //此次傳輸結束和片選改變之間的延時,之後就會啟動另一個傳輸或者結束整個訊息
-
u32 speed_hz; //通訊時鐘。如果是0,使用預設值
-
struct list_head transfer_list; //用來連線的雙向連結串列節點
-
};
-
7.spi控制器驅動的私有資料
-
struct davinci_spi {
-
struct spi_bitbang bitbang;
-
struct clk *clk;//時鐘
-
u8 version;
-
resource_size_t pbase;//spi暫存器實體地址
-
void __iomem *base;//spi暫存器虛擬地址
-
u32 irq;//spi中斷號
-
struct completion done;
-
const void *tx;//傳送buffer
-
void *rx;//讀buffer
-
#define SPI_TMP_BUFSZ (SMP_CACHE_BYTES + 1)
-
u8 rx_tmp_buf[SPI_TMP_BUFSZ];
-
int rcount;//讀資料個數
-
int wcount;//寫資料個數
-
struct davinci_spi_dma dma;
-
struct davinci_spi_platform_data *pdata;
-
void (*get_rx)(u32 rx_data, struct
davinci_spi *);//讀資料函式
-
u32 (*get_tx)(struct davinci_spi *);//傳送資料函式
-
u8 bytes_per_word[SPI_MAX_CHIPSELECT];
-
u32 speed;
-
u32 cs_num;
-
bool in_use;
-
#ifdef CONFIG_CPU_FREQ
-
struct notifier_block freq_transition;
-
#endif
-
};
-
四、spi驅動初始化
-
1.spi匯流排註冊
-
//spi匯流排結構
-
struct bus_type spi_bus_type = {
-
.name = "spi",
-
.dev_attrs = spi_dev_attrs,
-
.match = spi_match_device,
-
.uevent = spi_uevent,
-
.suspend = spi_suspend,
-
.resume = spi_resume,
-
};
-
//spi_master類,會在sysfs檔案系統建立該類
-
static struct class spi_master_class = {
-
.name = "spi_master",
-
.owner = THIS_MODULE,
-
.dev_release = spi_master_release,
-
};
-
static int __init spi_init(void)
-
{
-
int status;
-
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
-
相關推薦
Qualcomm平臺的SPI驅動框架分析
一、SPI匯流排概述 SPI是英語Serial Peripheral interface的縮寫,顧名思義就是序列外圍裝置介面,是Motorola首先在其MC68HCXX系列處理器上定義的。SPI介面主要應用在 EEPROM,FLASH,實時時鐘,AD轉換器,還有數字訊號處理器和數字訊號解碼器之間
Linux驅動修煉之道-SPI驅動框架原始碼分析(中-續)
然後看這裡是怎樣註冊spi主機控制器驅動的: int spi_register_master(struct spi_master *master) { 。。。。。。。。。。。。。。。。 /*將spi新增到核心,這
Linux驅動修煉之道-SPI驅動框架原始碼分析(中)
來自:http://blog.csdn.net/woshixingaaa/article/details/6574220 這篇來分析spi子系統的建立過程。 嵌入式微處理器訪問SPI裝置有兩種方式:使用GPIO模擬SPI介面的工作時序或者使用SPI控制
Linux驅動修煉之道-SPI驅動框架原始碼分析(上)
SPI驅動架構,以前用過,不過沒這個詳細,跟各位一起分享: 來自:http://blog.csdn.net/woshixingaaa/article/details/6574215 SPI協議是一種同步的序列資料連線標準,由摩托羅拉公司命名,可工作於全雙工模式。相
Linux驅動修煉之道-SPI驅動框架原始碼分析(下-續)
spi_async在spi.h中定義的: <span style="font-size:18px;">staticinlineint spi_async(struct spi_device *spi, struct spi_mess
SPI驅動框架-1(DM8127 Linux2.6.37為例)
orm span remove mac 設備 single 隊列 drive for 一、驅動程序結構 1、platform_device 文件:/arch/arm/mach-omap2/device.c static struct omap2_mcspi_platfor
5.7.6.framebuffer驅動框架分析1
iop 之間 write ioctl 程序 硬件 struct 於平 完成 http://www.mamicode.com/info-detail-1209620.html 5.7.6.1、fbmem_init函數[driver/video/fbmem.c] (1)#ifd
v4l2驅動框架分析-1
需要思考的問題: (1) cimutils應用程式維護了哪些結構體,v4l2驅動框架維護了哪些結構體 (2)/dev/video0 這個節點怎麼建立的 (3)應用層open 裝置節點/dev/video0 的時候,核心中的呼叫關係和具體乾的工作 (4)應用層ioctl 操作後,核心中
tty驅動框架分析
tty框架如下圖所示: 整個 uart 框架大概的樣子如上圖所示,大致可以分為四層,一層是下層我們的串列埠驅動層,它直接與硬體相接觸,我們需要填充一個 struct uart_ops 的結構體,再向上是tty核心層,在向上是線路規程,再向上是是直接和使用者空間對接的,它們每一層都有一個
MTK平臺camera驅動架構分析
MTK6580 AndroidO(android8.1)版本camera 驅動分析 CAMERA驅動整個框架分為:三個部分hal部分邏輯呼叫,kernel層的通用驅動sensorlist.c 和具體IC的驅動xxxx_mipi_raw.c 這裡主要介紹kernel部分和HAL層部分。
USB攝像頭驅動框架分析
怎樣構造一個攝像頭驅動程式: 1. 分配video_device:video_device_alloc 2. 設定 .fops .ioctl_ops (裡面需要設定11項) 如果要用核心提供的緩衝區操作函式,還需要構造一個videobuf
linux驅動由淺入深系列:usb子系統之四(android平臺滑鼠驅動程式碼分析)
android上的usb口是支援OTG(on the go)的,USB OTG既可以作為Host又可以作為Device,我們本文來看一下android手機作為Host連線滑鼠的情況。OTG是如何做到既可以做Host又可以作為Device的呢 標準usb接頭中有四根線:vbu
tty初探—uart驅動框架分析(二)uart_add_one_port
在前面的一篇文章中,我們分析了一個 uart_driver 的向上註冊過程,主要是 tty 的一些東西,知道了 tty 註冊了一個字元裝置驅動,我們在使用者空間 open 時將呼叫到 uart_port.ops.startup ,在使用者空間 write 則呼叫 u
usb gadget驅動框架分析
以renesas BSP為例: 1.composite_driver層註冊流程 usb_composite_probe usb_gadget_probe_driver /*driver->udc_name沒有指定為e659000.usb那麼就預設是使用第一個UDC,找裡面的第一個還
嵌入式Linux——IIC驅動(2):i2c驅動框架分析
簡介: 本文主要介紹i2c匯流排框架,即對i2c的各個層次(i2c匯流排,i2c核心,i2c裝置)進行分析。同時我也會結合程式對框架進行說明。所以本文將分為兩部分,第一部分對i2c的框架進行介紹,而第二部分就是結合程式碼分析。 核心:linux-2.6.2
Qualcomm 平臺觸控式螢幕驅動移植 筆記
TP觸控式螢幕,應該是驅動開發中比較簡單並且適合新手入手的模組。不過雖然簡單,但涉及到的內容還是比較多的,其中Linux相關主要的機制: 1. input 機制 2. 中斷、定時器 3. I2C 1
學習筆記 --- LINUX網絡卡驅動框架分析
網絡卡的驅動很簡單,就是填充net_device結構體,其應用層到網路協議層核心已經完成了,我們的工作就是填寫這個net_device,然後註冊就可以了。 修正一下:上面第三步應該是:register_netdev 下面程式碼實現一個虛擬網絡卡,這裡沒有實際的網絡卡,只是
Linux系統spi驅動程式分析---(一)
說明:本文將分析Linux-2.6.17原始碼中的spi驅動程式,其內容為本人閱讀原始碼後的一些理解。由於本人水平有限,所以內容可能比較雜亂零散,權當個人筆記記錄之用。而以下內容均以powerpc架構為例說明。 在Linux系統中,spi驅動的設計採用了分層設計模式的思想
linux SPI驅動框架(二) -- 裝置驅動
裝置驅動關注的結構體主要有兩個,struct spi_device描述spi從裝置,struct spi_driver是從裝置的裝置驅動。 struct spi_device { struct device dev; str
SylixOS中AHCI驅動框架分析
1、概述 本文件介紹SATA和AHCI相關協議,以IMX6Q實驗平臺為基礎,分析SylixOS中AHCI驅動框架的具體實現。 2、SATA簡介 2.1 SATA硬碟 串列埠硬碟SATA(Serial ATA)與以往的並口硬碟PATA(Parallel ATA)相比,資料傳