2007年7月19日 星期四

Linux I2C driver

目的:
紀錄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的文件

沒有留言: