msm7227平台linux I2C驅動分析(2.6.29)

來源:互聯網
上載者:User

Revision History
 
Date Issue Description Author   
<08/07/2010> <1.0> Msm7227平台I2C驅動分析 滕景東   
      
    
目錄
1. 摘要 3
2. 簡介 3
3. I2C架構 3
4. I2C匯流排初始化 4
5. I2C適配器驅動 5
6. I2C裝置驅動 9
7. 使用者空間驅動支援 12
8. 資料轉送架構 16
9. References 16

 

1. 摘要
主要介紹Msm7227平台上I2C驅動原理,多數部分是29核心標準架構。
2. 簡介
I2C只有兩條線,一條串列資料線:SDA,一條是時鐘線SCL。I2C是一種多主機控制匯流排,同一匯流排上可允許多個master.
i2c匯流排適配器(adapter)就是一條i2c匯流排的控制器,在物理串連上若干i2c裝置。在linux驅動中,每種處理器平台有自己的適配器驅動。
3. I2C架構

核心中i2c相關代碼可以分為三個層次:
i2c架構層:i2c.h和i2c-core.c為其主體架構代碼,提供了核心資料結構的定義、i2c適配器驅動和裝置驅動的註冊、登出管理等;i2c-dev.c用於建立i2c適配器的/dev/i2c-%d裝置節點,提供i2c裝置的使用者空間存取方法等。
i2c匯流排適配器驅動:i2c/busses/目錄下,如i2c-msm.c。定義描述具體i2c匯流排適配器的i2c_adapter資料結構、實現在具體i2c適配器上的i2c匯流排通訊的具體實現,並由i2c_algorithm資料結構描述與i2c裝置通訊的方法。
i2c裝置驅動:定義描述具體裝置的i2c_client和可能的私人資料結構。

                    
 
 展示了核心I2C結構大整體架構,以下根據核心載入順序介紹I2C匯流排初始化,I2C匯流排適配器驅動,I2C裝置驅動和使用者空間驅動支援及資料轉送架構五部分介紹。
4. I2C匯流排初始化
 
該過程主要完成了sysfs匯流排結構,最終形成如下結構:
/sys/bus/i2c/
|-- devices
|-- drivers
|   |-- dummy
|      |-- bind
|      |-- uevent
|      `-- unbind
|-- drivers_autoprobe
|-- drivers_probe
`-- uevent

/sys/class/i2c-adapter/
dummy_driver僅僅是註冊了一個空的裝置驅動,註冊驅動時會遍曆載入/sys/class/i2c-adapter/中的所有裝置,該過程在初始話匯流排過程中完成,/sys/class/i2c-adapter/基本為空白,所以我認為這裡的驅動註冊只是驗證i2c匯流排結構的完整性考慮的。
5. I2C適配器驅動


Linux核心的所有適配器驅動程式都在driver/i2c/busses/目錄下,當前高通的驅動是i2c-msm.c,適配器驅動的註冊過程如下:

 
在kernel中提供了兩個adapter註冊介面,分別為i2c_add_adapter()和i2c_add_numbered_adapter().由於在系統中可能存在多個adapter,因為將每一條I2C匯流排對應一個編號,下文中稱為I2C匯流排號。對於i2c_add_adapter()而言,它使用的是動態匯流排號,即由系統給其分配一個匯流排號,而i2c_add_numbered_adapter()則是自己指定匯流排號,如果這個匯流排號非法或者是被佔用,就會註冊失敗。高通的adapter驅動使用了i2c_add_numbered_adapter()註冊,匯流排號最初儲存在platform_data中。
I2C adapter以platform_device方式註冊進系統,在proble函數中初始化了struct i2c_adapter結構:

struct i2c_adapter {<br /> struct module *owner;<br /> unsigned int id;<br /> unsigned int class; /* classes to allow probing for */<br /> const struct i2c_algorithm *algo; /* the algorithm to access the bus */<br /> void *algo_data;</p><p> /* --- administration stuff. */<br /> int (*client_register)(struct i2c_client *);<br /> int (*client_unregister)(struct i2c_client *);</p><p> /* data fields that are valid for all devices */<br /> u8 level; /* nesting level for lockdep */<br /> struct mutex bus_lock;<br /> struct mutex clist_lock;</p><p> int timeout; /* in jiffies */<br /> int retries;<br /> struct device dev; /* the adapter device */</p><p> int nr; /*該成員描述了匯流排號*/<br /> struct list_head clients; /* i2c_client結構鏈表,該結構包含device,driver和<br />adapter結構*/<br /> char name[48];<br /> struct completion dev_released;<br />};

其中nr的值是在arch/arm/mach-msm/devices.c中定義的:
struct platform_device msm_device_i2c = {<br /> .name = "msm_i2c",<br /> .id = 0,<br /> .num_resources = ARRAY_SIZE(resources_i2c),<br /> .resource = resources_i2c,<br />};<br />struct platform_device msm_device_i2c_2 = {<br /> .name = "msm_i2c",<br /> .id = 2,<br /> .num_resources = ARRAY_SIZE(resources_i2c_2),<br /> .resource = resources_i2c_2,<br />};

該結構以參數形式傳進i2c_add_numbered_adapter(),下一步將進入

static int i2c_register_adapter(struct i2c_adapter *adap)<br />{<br /> int res = 0, dummy;</p><p> /* Can't register until after driver model init */<br /> if (unlikely(WARN_ON(!i2c_bus_type.p)))<br /> return -EAGAIN;</p><p> mutex_init(&adap->bus_lock);<br /> mutex_init(&adap->clist_lock);<br /> INIT_LIST_HEAD(&adap->clients);/*初始化裝置鏈表*/</p><p> mutex_lock(&core_lock);</p><p> /* Add the adapter to the driver core.<br /> * If the parent pointer is not set up,<br /> * we add this adapter to the host bus.<br /> */<br /> if (adap->dev.parent == NULL) {<br /> adap->dev.parent = &platform_bus;/*父裝置是platform_bus*/<br /> pr_debug("I2C adapter driver [%s] forgot to specify "<br /> "physical device/n", adap->name);<br /> }<br /> dev_set_name(&adap->dev, "i2c-%d", adap->nr);/*裝置節點名字*/<br /> adap->dev.release = &i2c_adapter_dev_release;<br /> adap->dev.class = &i2c_adapter_class;<br /> res = device_register(&adap->dev); /*註冊adapter這個裝置本身*/<br /> if (res)<br /> goto out_list;</p><p> dev_dbg(&adap->dev, "adapter [%s] registered/n", adap->name);</p><p> /*以下部分完成i2c裝置和驅動的註冊*/<br /> if (adap->nr < __i2c_first_dynamic_bus_num)/*主板初始化時的動態匯流排號,該值已匯出符號表*/<br /> i2c_scan_static_board_info(adap);/*完成新類型i2c裝置的註冊,一般只在主板初始化時*/</p><p> /* Notify drivers */<br /> dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,<br /> i2c_do_add_adapter); /*探測匯流排上的所有i2c裝置驅動,同時完成client、driver、device、adapter的綁定,但driver->address_data非空的情況下有用,而這又意味著只對舊的i2c機制有效*/</p><p>out_unlock:<br /> mutex_unlock(&core_lock);<br /> return res;</p><p>out_list:<br /> idr_remove(&i2c_adapter_idr, adap->nr);<br /> goto out_unlock;<br />}</p><p>

i2c_scan_static_board_info對應的初始化過程在board-msm7x27.c中完成,
i2c_register_board_info(0, i2c_devices, ARRAY_SIZE(i2c_devices));
6. I2C裝置7. 驅動
驅動的編寫方法已在《msm7227-I2C裝置驅動實現要點.doc》中介紹,此節分析驅動和裝置的註冊過程。
 
本還想詳細分析代碼,但發現,這張圖已經足夠說明i2c驅動的註冊過程了,下面對我看代碼時碰到的一些問題簡要分析。
裝置和驅動的關聯
大家知道,對於一個驅動程式有兩個元素不可或缺,即裝置和驅動,一般驅動都是通過裝置名稱和驅動名的匹配建立關係的,我從i2c/chips/裡看到的範例程式碼了只能發現驅動的註冊,卻不見裝置註冊的蹤影,令人疑惑,跟蹤發現,在i2c adapter註冊時會遍曆i2c_board_info這樣一個結構,而這個結構在29以前或更早的核心裡是不存在的,該資料結構在board-msm7x27.c中初始化了i2c裝置名稱及裝置地址,這便解決了驅動與裝置的匹配問題,同時器件地址的提供也有所改變,舊的核心是在驅動中使用一個normal_i2c數組儲存地址的。
名字匹配
一個i2c驅動是可以有多個名字的,即一個驅動程式可以支援多個裝置,該機制是通過 struct i2c_device_id實現的,驅動中建立這麼一個結構體數組,i2c架構層便會掃描該數組,與裝置名稱去匹配,匹配成功的都會進入相應probe函數。
進入probe
該過程困惑了我一段時間,其實要進入自己驅動的probe首先需要進入匯流排的probe,而進入匯流排probe的前提是與匯流排的match成功,具體實現大家可以根據上面的圖看一下相應代碼便知。
裝置模型
I2C的架構充分利用的裝置模型的原理及sysfs的實現,我認為理解i2C架構前先瞭解一下裝置模型是很有必要的。這裡將我的個人理解總結一下:
 Kobject是裝置模型的最小單位,kset是對kobject的集合,struct driver_private、struct device等結構都內嵌了kobject,kset也內嵌kobject用於表徵自己。相同特性的kset的合集又構成了subsys,舉個不太恰當的類比:
kobject之於裝置或驅動;kset之於某一類裝置,如i2c;subsys之於子系統,如輸入子系統。其實在29核心中subsys就是一個kset結構,貼兩張圖理解一下:
 
8. 使用者空間驅動支援
這部分在i2c-dev.c中實現,這部分內容簡單的說就是通過內嵌一個具有file_operations的標準字元裝置驅動來虛擬i2c裝置,這樣,就可以在使用者空間直接操作i2c裝置了。
流程如:
 
餘下的就是常規file_operation了,open操作:
static int i2cdev_open(struct inode *inode, struct file *file)<br />{<br /> unsigned int minor = iminor(inode);<br /> struct i2c_client *client;<br /> struct i2c_adapter *adap;<br /> struct i2c_dev *i2c_dev;<br /> int ret = 0;</p><p> lock_kernel();/*核心上鎖,一般只在多cpu是有用*/<br /> i2c_dev = i2c_dev_get_by_minor(minor);/*因為有兩個adapter,同一個主裝置號*/<br /> if (!i2c_dev) {<br /> ret = -ENODEV;<br /> goto out;<br /> }</p><p> adap = i2c_get_adapter(i2c_dev->adap->nr);<br /> if (!adap) {<br /> ret = -ENODEV;<br /> goto out;<br /> }</p><p> /* This creates an anonymous i2c_client, which may later be<br /> * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.<br /> *<br /> * This client is ** NEVER REGISTERED ** with the driver model<br /> * or I2C core code!! It just holds private copies of addressing<br /> * information and maybe a PEC flag.<br /> */<br /> client = kzalloc(sizeof(*client), GFP_KERNEL);<br /> if (!client) {<br /> i2c_put_adapter(adap);<br /> ret = -ENOMEM;<br /> goto out;<br /> }<br /> snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);<br /> client->driver = &i2cdev_driver;/*綁定字元裝置驅動*/</p><p> client->adapter = adap;<br /> file->private_data = client;</p><p>out:<br /> unlock_kernel();<br /> return ret;<br />}<br />
注意這裡分配並初始化了一個struct i2c_client結構.但是沒有註冊這個clinet.此外,這個函數中還有一個比較奇怪的操作.不是在前面已經將i2c_dev->adap指向要操作的adapter麼?為什麼還要以adapter->nr為關鍵字從i2c_adapter_idr去找這個操作的adapter呢?注意了,調用i2c_get_adapter()從匯流排號nr找到操作的adapter的時候,還會增加module的引用計數.這樣可以防止模組意外被釋放掉.也許有人會有這樣的疑問,那 i2c_dev->adap->nr操作,如果i2c_dev->adap被釋放掉的話,不是一樣會引起系統崩潰麼?這裡因為,在i2cdev_attach_adapter()間接的增加了一次adapter的一次引用計數.如下:
static int i2cdev_attach_adapter(struct i2c_adapter *adap)<br />{<br />......<br />i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,<br /> MKDEV(I2C_MAJOR, adap->nr),<br /> "i2c-%d", adap->nr);<br />......<br />}
看到了麼,i2c_dev內嵌的device是以adap->dev為父結點,在device_create()中會增次adap->dev的一次引用計數.
好了,open()操作到此就完成了.
使用方法:(參考kernel-test/i2c-msm-test.c)
1、構造struct i2c_msg
[讀] struct i2c_msg msgs[] = {<br /> [0] = {<br /> .addr = slave_address,<br /> .flags = 0,<br /> .buf = (void *)offset_data,<br /> .len = ARRAY_SIZE(offset_data),<br /> },<br /> [1] = {<br /> .addr = slave_address,<br /> .flags = I2C_M_RD,<br /> .buf = (void *)buf,<br /> .len = count,<br /> },<br /> };<br />[寫] struct i2c_msg msgs[] = {<br /> [0] = {<br /> .addr = slave_address,<br /> .flags = 0,<br /> .buf = (void *)data,<br /> .len = (2 + len) * sizeof(*data),<br /> },<br /> }
2、通過ioctl操作裝置

static int do_rdwr(int fd, struct i2c_msg *msgs, int nmsgs)<br />{<br /> struct i2c_rdwr_ioctl_data msgset = {<br /> .msgs = msgs,<br /> .nmsgs = nmsgs, /* msgs 個數*/<br /> };</p><p> if (msgs == NULL || nmsgs <= 0)<br /> return -1;</p><p> if (ioctl(fd, I2C_RDWR, &msgset) < 0)<br /> return -1;</p><p> return 0;<br />}<br />

3、ioctl命令字:

#define I2C_SMBUS_READ 1<br />#define I2C_SMBUS_WRITE 0</p><p>#define I2C_SMBUS_QUICK 0<br />#define I2C_SMBUS_BYTE 1<br />#define I2C_SMBUS_BYTE_DATA 2<br />#define I2C_SMBUS_WORD_DATA 3<br />#define I2C_SMBUS_PROC_CALL 4<br />#define I2C_SMBUS_BLOCK_DATA 5<br />#define I2C_SMBUS_I2C_BLOCK_DATA 6<br />#define I2C_SMBUS_BLOCK_PROC_CALL 7 </p><p>#define I2C_RETRIES 0x0701<br />#define I2C_TIMEOUT 0x0702<br />#define I2C_SLAVE 0x0703<br />#define I2C_SLAVE_FORCE 0x0706<br />#define I2C_TENBIT 0x0704<br />#define I2C_FUNCS 0x0705<br />#define I2C_RDWR 0x0707<br />#define I2C_PEC 0x0708<br />#define I2C_SMBUS 0x0720 </p><p>

9. 資料轉送架構
I2C架構的讀寫支援兩種類型,預設實現的操作是smbus協議,該協議與i2c協議類似,如果控制器不支援smbus,架構層可以用i2c_transfer類比smbus的實現,系統預設的i2c傳輸函數一般都是基於i2c類比的smbus方法傳輸的,如i2c_smbus_write_byte_data,i2c_smbus_read_byte_data等。
I2C協議的匯流排實現應該是I2C控制器,而不是SMBUS控制器, I2C協議和SMBUS協議不完成等同,SMBUS是I2C的子集,smbus由I2C衍生而來。smbus匯流排上傳輸的資料一定是I2C的格式的,但是SMBUS上傳輸的資料不一定能滿足具體某個I2C從裝置的通訊要求(資料序列)。
以i2c_smbus_write_byte_data介紹資料流程:
 
10. References
[1]. http://blog.chinaunix.net/u1/51562/showart_1403925.html
[2]. 《msm7227-I2C裝置驅動實現要點.doc》 滕景東

相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。