紀錄Linux I2C driver架構, porting步驟與心得
大綱:
1. 簡介I2C protocol(包含SMBus)
2. Linux I2C driver架構
3. Porting步驟與心得
內文:
1.簡介I2C protocol(包含SMBus)
<缺...>
2. Linux I2C driver架構
可分成三個層次來看
(1). I2C core框架:
提供核心的data structure的define和操作這些data structure的相關介面function, 用來實現i2c adapter和device的註冊/註銷管理,以及I2C通信方法上層與H/W adapter無關的代碼, 為系統中每個I2C bus增加相應的read/write method
(2). I2C bus adapter driver:
定義描述具體I2C bus adapter的i2c_adapter structure. 實現在實體I2C adapter上的I2C bus通信方法, 並由i2c_algorithm來描述
為系統每個I2C bus增加相對應的讀寫方法, 等待device driver的呼叫
在系統開機時, 首先裝載的是I2C bus driver, 一個bus driver support一特定的I2C bus的讀寫, 一個bus driver通常需要2個module- struct i2c_adapter, struct i2c_algorithm
static struct i2c_adapter pb1550_board_adapter = {
name: "pb1550 adapter",
id: I2C_HW_AU1550_PSC,
algo: NULL,
algo_data: &pb1550_i2c_info,
inc_use: pb1550_inc_use,
dec_use: pb1550_dec_use,
client_register: pb1550_reg,
client_unregister: pb1550_unreg,
client_count: 0,
};
这个样例挂接了一个叫做“pb1550 adapter”的驱动。但这个模块并未提供读写函数,具体的读写方法由第二个模块,struct i2c_algorithm提供。
static struct i2c_algorithm au1550_algo = {
.name = "Au1550 algorithm",
.id = I2C_ALGO_AU1550,
.master_xfer = au1550_xfer,
.functionality = au1550_func,
};
i2c_adap->algo = &au1550_algo;
这个样例给上述总线驱动增加了读写“算法”。通常情况下每个I2C总线驱动都定义一个自己的读写算法,但鉴于有些总线使用相同的算法,因而可以共用同一套读写函数。本例中的驱动定义了自己的读写算法模块,起名叫“Au1550 algorithm”。
全部填妥后,通过调用:
i2c_add_adapter(i2c_adap);
(3). I2C device driver:
定義描述描述具體設備i2c_client(ex: 某一個thernal sensor)和可能的私有data structure, 使用I2C core提供的function interface在kernel 中 register, 並實現具體功能(read, write, ioctl....)
device driver則是與掛在I2C bus上的實體的設備通訊的driver
bus driver只是提供對一條bus的讀寫機制, 本身不做通信, 通信是由I2C device driver來做, device driver透過i2c bus實體的設備進行通訊Device driver有兩個module- struct i2c_driver, struct i2c_client
當系統開機 i2c bus driver裝載完畢, 就可以裝載device driver
舉個例子再說明一次static struct i2c_driver driver = {
.name = "i2c TV tuner driver",
.id = I2C_DRIVERID_TUNER,
.flags = I2C_DF_NOTIFY,
.attach_adapter = tuner_probe,
.detach_client = tuner_detach,
.command = tuner_command,
};
i2c_add_driver(&driver);
这个i2c_driver一旦装入完成,其中的attach_adapter函数就会被调用。在其中可以遍历系统中的每个i2c总线驱动,探测想要访问的设备:
static int tuner_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, tuner_attach);
}
注意探测可能会找到多个设备,因而不仅一个I2C总线可以挂多个不同类型的设备,一个设备驱动也可以同时为挂在多个不同I2C总线上的设备服务。
每当设备驱动探测到了一个它能支持的设备,它就创建一个struct i2c_client来标识这个设备:
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &driver;
/* Tell the I2C layer a new client has arrived */
err = i2c_attach_client(new_client);
if (err)
goto error;
可见,一个i2c_client代表着位于adapter总线上,地址为address,使用driver来驱动的一个设备。它将总线驱动与设备驱动,以及设备地址绑定在了一起。一个i2c_client就代表着一个I2C设备。
当得到I2C设备后,就可以直接对此设备进行读写:
/*
* The master routines are the ones normally used to transmit data to devices
* on a bus (or read from them). Apart from two basic transfer functions to
* transmit one message at a time, a more complex version can be used to
* transmit an arbitrary number of messages without interruption.
*/
extern int i2c_master_send(struct i2c_client *,const char* ,int);
extern int i2c_master_recv(struct i2c_client *,char* ,int);
example:AT91RM9200的I2C driver
首先初始AT91RM9200的I2C工作模式, 然後裝載I2C bus driver
static struct i2c_adapter at91rm9200_adapter = {
.name = "AT91RM9200",
.id = I2C_ALGO_SMBUS,
.algo = &at91_algorithm,
.algo_data = NULL
...
}
真正讀寫
static struct i2c_algorithm at91_algorithm = {
.name = "at91i2c",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = at91_smbus_xfer
.master_xfer = at91_xfer
.functionality = at91_func
...
}
通過呼叫I2C core中的介面函數 i2c_add_adapter將這兩個module註冊到作業系統理bus driver就算裝上, i2c_algorithm實現i2c通信的具體方法, 針對本文at91_xfer最為關鍵, I2C core框架中提供給host使用的data transfer interface- i2c_master_send, i2c_master_recv, i2c_tarnsfer最終都是呼叫at91_xfer
device driver (x1227 一個 RTC IC)
struct i2c_driver x1227_driver = {
.name = "x1227driver"
.id = "I2C_DRIVERID_X1227,
.flags = I2C_DF_NOTIFY,
.attach_adapter = x1227_probe,
.detech_client = x1227_detech,
.command = x1227_command,
}
其中attach_adapter利用adapter driver提供的I2C probe方法, 利用device driver module中提供的address, 檢測可能存在的設備及其地址, 如果發現設備則create struct i2c_client來標示這個device並向該adapter的data structure register. detect_client用於bus上得unregister, 並release i2c_client及相對應的私有data structure. command是user interface中的ioctl功能的底層實現
i2c device driver需要實現兩各方面的介面, 一個是對I2C core框架的介面, 設備初始化時通過function i2c_add_driver來實現driver的register, 這個i2c_driver一旦裝入完成, 其中attach_adapter function就會開始被呼叫
另一個是對user application的介面, 提供user program probe I2C device的interface, 包括實現open, release, read, write, ioctl每個device driver都有一個稱為file_operation的data structure
static struct file_operations rtc_fops = {
.owner = THIS_MODULE,
.ioctl = x1227_rtc_ioctl,
.open = x1227_rtc_open,
.release = x1227_rtc_release,
}
Reference:
1. Lm_sensors - Linux hardware monitoring, 網站目的是讓機器可以讀取thermal sensor等來監控系統, 但因為主要的sensor IC介面不外乎是I2C or SMBus介面, 所以Kernel很多有關I2C的程式來至於這邊的貢獻
2. Linux的I2C驱动架构 以分析source code data structure方面來解釋Linux I2C driver架構
3. 嵌入式Linux系統中I2C總線設備的驅動設計 分析Linux I2C driver架構, 同時提供了一個很好的架構圖
4. [misc驱动]linux下I2C驱动(一)~(七) 解釋了i2c-dev的概念, 並解釋了一些lm_sensors project的文件
沒有留言:
張貼留言