Zynqlinux的I2C驅(qū)動(dòng)學(xué)習(xí)筆記(一)

最近在用米爾的Z-TURN BOARD單板做小項(xiàng)目。順便也加強(qiáng)學(xué)習(xí)I2C驅(qū)動(dòng),記一篇做記錄。
I2C總線知識(shí)非常簡(jiǎn)單,SDA,SCL,他們的時(shí)序規(guī)則是:I2C總線是由數(shù)據(jù)線SDA和時(shí)鐘SCL構(gòu)成的串行總線,可發(fā)送和接收數(shù)據(jù)。在CPU與被控IC之間、IC與IC之間進(jìn)行雙向傳送,最高傳送速率100kbps。各種被控制電路均并聯(lián)在這條總線上,但就像電話機(jī)一樣只有撥通各自的號(hào)碼才能工作,所以每個(gè)電路和模塊都有唯一的地址,在信息的傳輸過(guò)程中,I2C總線上并接的每一模塊電路既是主控器(或被控器),又是發(fā)送器(或接收器),這取決于它所要完成的功能。CPU發(fā)出的控制信號(hào)分為地址碼和控制量?jī)刹糠?,地址碼用來(lái)選址,即接通需要控制的電路,確定控制的種類;控制量決定該調(diào)整的類別(如對(duì)比度、亮度等)及需要調(diào)整的量。這樣,各控制電路雖然掛在同一條總線上,卻彼此獨(dú)立,互不相關(guān)。

創(chuàng)新互聯(lián)公司是一家專業(yè)的成都網(wǎng)站建設(shè)公司,我們專注網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè)、網(wǎng)絡(luò)營(yíng)銷、企業(yè)網(wǎng)站建設(shè),買友情鏈接,廣告投放為企業(yè)客戶提供一站式建站解決方案,能帶給客戶新的互聯(lián)網(wǎng)理念。從網(wǎng)站結(jié)構(gòu)的規(guī)劃UI設(shè)計(jì)到用戶體驗(yàn)提高,創(chuàng)新互聯(lián)力求做到盡善盡美。

I2C總線在傳送數(shù)據(jù)過(guò)程中共有三種類型信號(hào), 它們分別是:開始信號(hào)、結(jié)束信號(hào)和應(yīng)答信號(hào)。

開始信號(hào):SCL為高電平時(shí),SDA由高電平向低電平跳變,開始傳送數(shù)據(jù)。
結(jié)束信號(hào):SCL為高電平時(shí),SDA由低電平向高電平跳變,結(jié)束傳送數(shù)據(jù)。
應(yīng)答信號(hào):接收數(shù)據(jù)的IC在接收到8bit數(shù)據(jù)后,向發(fā)送數(shù)據(jù)的IC發(fā)出特定的低電平脈沖,表示已收到數(shù)據(jù)。CPU向受控單元發(fā)出一個(gè)信號(hào)后,等待受控單元發(fā)出一個(gè)應(yīng)答信號(hào),CPU接收到應(yīng)答信號(hào)后,根據(jù)實(shí)際情況作出是否繼續(xù)傳遞信號(hào)的判斷。若未收到應(yīng)答信號(hào),由判斷為受控單元出現(xiàn)故障。

在LINUX系統(tǒng)初始化的過(guò)程中,通過(guò) i2c_register_board_info,將所需要的I2C從設(shè)備加入一個(gè)名為_i2c_board_list雙向循環(huán)鏈表,系統(tǒng)在成功加載I2C主設(shè)備adapt后,就會(huì)對(duì)這張鏈表里所有I2C從設(shè)備逐一地完成 i2c_client的注冊(cè)。

也就是說(shuō),i2c_client和i2c_adapter都是由i2c_core來(lái)維護(hù)的。

在xilinx-linux中,i2c從設(shè)備是通過(guò)dts文件傳遞給內(nèi)核的,內(nèi)核通過(guò)zynq_init_machine函數(shù)注冊(cè)所有的i2c從設(shè)備,i2c_client.

I2C的linux必須知道4個(gè)結(jié)構(gòu)體:i2c_adapter,i2c_algorithm,i2c_client,i2c_driver
struct i2c_adapter {
struct module owner;
unsigned int class; / classes to allow probing for /
const struct i2c_algorithm algo; / the algorithm to access the bus
/
void *algo_data;

/ data fields that are valid for all devices /
struct rt_mutex bus_lock;

int timeout; / in jIFfies /
int retries;
struct device dev; / the adapter device
/

int nr;
char name[48];
struct completion dev_released;

struct mutex userspace_clients_lock;
struct list_head userspace_clients;

struct i2c_bus_recovery_info *bus_recovery_info;
};

i2c總線控制器數(shù)據(jù)依附于algo_data,比如xi2cps,s3c24xx_i2c。

struct device dev;成員表明i2c_adapter是一個(gè)硬件,對(duì)應(yīng)SoC上的I2C控制器。而i2c_algorithm則是這個(gè)I2C控制器的底層驅(qū)動(dòng)程序。

同理:
struct i2c_client {
unsigned short flags; / div., see below /
unsigned short addr; / chip address - NOTE: 7bit /
/ addresses are stored in the /
/ LOWER 7 bits
/
char name[I2C_NAME_SIZE];
struct i2c_adapter adapter; / the adapter we sit on /
struct i2c_driver driver; / and our access routines /
struct device dev; / the device structure /
int irq; / irq issued by device
/
struct list_head detected;
};

struct i2c_client代表一個(gè)掛載到i2c總線上的i2c從設(shè)備,該設(shè)備所需要的數(shù)據(jù)結(jié)構(gòu),其中包括
該i2c從設(shè)備所依附的i2c主設(shè)備 struct i2c_adapter adapter
該i2c從設(shè)備的驅(qū)動(dòng)程序struct i2c_driver
driver
作為i2c從設(shè)備所通用的成員變量,比如addr, name等
該i2c從設(shè)備驅(qū)動(dòng)所特有的數(shù)據(jù),依附于dev->driver_data下,在i2c_driver中的probe函數(shù)中設(shè)置這個(gè)結(jié)構(gòu)體成員。比如eeprom的eeprom_data。
所有i2c從設(shè)備組成的雙向鏈表:detected

struct device dev表明struct i2c_client代表的是一個(gè)硬件,比如eeprom芯片,或則rtc芯片,通過(guò)i2c總線連接到i2c_adapter硬件上。
而i2c_driver則是這個(gè)i2c_client芯片硬件的驅(qū)動(dòng)程序。

我們一般會(huì)對(duì)每個(gè)I2C字符設(shè)備定義一個(gè)私有信息結(jié)構(gòu)體,而i2c_client一般被包含在這個(gè)私有信息結(jié)構(gòu)體中??催^(guò)LDR3源代碼的hacker應(yīng)該比較清楚。

i2c_client依附于i2c_adapter,也就是I2C設(shè)備和I2C總線控制器的對(duì)應(yīng)關(guān)系,一個(gè)i2c_adapter可以掛接多個(gè)i2c_client,i2c_adapter的struct list_head userspace_clients;結(jié)構(gòu)成員就是所有client的鏈表。

linux的最新版本基本上支持目前所有的I2C適配器硬件和I2C從設(shè)備,但是對(duì)于工程師來(lái)說(shuō),可能要面臨各種情況:為i2c_adapter和i2c_client編寫驅(qū)動(dòng)程序。

二、I2C核心
I2C核心是源碼位于drivers/i2c/i2c-core.c,它并不依賴于硬件平臺(tái)的接口函數(shù),是I2C總線驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)的紐帶。

增加/刪除i2c_adapter
int i2c_add_adapter(struct i2c_adapter adapter) //調(diào)用i2c_register_adapter()
int i2c_del_adapter(struct i2c_adapter
adapter)

增加/刪除i2c_driver
int i2c_register_driver(struct module owner, struct i2c_driver driver)
int i2c_add_driver(struct i2c_driver driver) //調(diào)用i2c_register_driver
void i2c_del_driver(struct i2c_driver
driver)

增加/刪除i2c_client
struct i2c_client i2c_new_device(struct i2c_adapter adap, struct i2c_board_info const info)
void i2c_unregister_device(struct i2c_client
client)

注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函數(shù)。之后attach被merge到了i2c_new_device中,而detach直接被unregister取代。實(shí)際上這兩個(gè)函數(shù)內(nèi)部都是調(diào)用了device_register()和device_unregister()

I2C傳輸、發(fā)送接收
int i2c_transfer(struct i2c_adapter adap, struct i2c_msg msgs, int num)
int i2c_master_send(struct i2c_client client,const char buf ,int count)
int i2c_master_recv(struct i2c_client client, char buf ,int count)
i2c_transfer()函數(shù)用于進(jìn)行I2C 適配器和I2C 設(shè)備之間的一組消息交互,i2c_master_send()函數(shù)和i2c_master_recv()函數(shù)內(nèi)部會(huì)調(diào)用i2c_transfer()函數(shù)分別完成一條寫消息和一條讀消息。
i2c_transfer()本身不能和硬件完成消息交互,它尋找i2c_adapter對(duì)應(yīng)的i2c_algorithm,要實(shí)現(xiàn)數(shù)據(jù)傳送就要實(shí)現(xiàn)i2c_algorithm的master_xfer(),這個(gè)函數(shù)與具體的硬件有關(guān),大部分時(shí)間由廠商完成。
i2c_transfer()通過(guò)調(diào)用__i2c_transfer()完成I2C通訊:
int __i2c_transfer(struct i2c_adapter adap, struct i2c_msg msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;

/ Retry automatically on arbitration loss /
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}

return ret;

}

可見(jiàn)retries為重傳嘗試次數(shù),timeout為超時(shí)時(shí)間。

三、Linux I2C總線驅(qū)動(dòng)
1、I2C適配器的加載和卸除
加載:申請(qǐng)硬件資源,比如IO地址,中斷號(hào),調(diào)用i2c_add_adapter加載適配器
i2c_add_adapter中會(huì)調(diào)用i2c_register_adapter函數(shù)
static int i2c_register_adapter(struct i2c_adapter *adap)
{
device_register(&adap->dev); //完成I2C主設(shè)備adapter的注冊(cè),即注冊(cè)object和發(fā)送uevent等
i2c_scan_static_board_info(adap); //注冊(cè)i2c_clienlt
}

static void i2c_scan_static_board_info(struct i2c_adapter adapter)
{
struct i2c_devinfo
devinfo;

down_read(&__i2c_board_lock);
list_for_each_entry(devinfo, &i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
“Can’t create device at 0x%02x\n”,
devinfo->board_info.addr);
}
up_read(&
i2c_board_lock);
}

i2c_new_device調(diào)用device_register注冊(cè)i2c從設(shè)備。
那么,這個(gè)I2C從設(shè)備組成的雙向循環(huán)鏈表,是什么時(shí)候通過(guò)什么方式建立起來(lái)的呢?
以 /arch/ARM/mach-pxa/saar.c 為例
static void __init saar_init(void)
{
saar_init_i2c();
}

static void __init saar_init_i2c(void)
{
pxa_set_i2c_info(NULL);
i2c_register_board_info(0, ARRAY_AND_SIZE(saar_i2c_info));
}

static struct i2c_board_info saar_i2c_info[] = {
[0] = {
.type = “da9034”,
.addr = 0x34,
.platform_data = &saar_da9034_info,
.irq = PXA_GPIO_TO_IRQ(mfp_to_gpio(MFP_PIN_GPIO83)),
},
};

/ drivers/i2c/i2c-boardinfo.c /
int __init i2c_register_board_info(int busnum, structi2c_board_info const info, unsigned len)
{
struct i2c_devinfo
devinfo;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); //將I2C從設(shè)備加入該鏈表中
}

標(biāo)題名稱:Zynqlinux的I2C驅(qū)動(dòng)學(xué)習(xí)筆記(一)
當(dāng)前路徑:http://www.muchs.cn/article16/ihpcgg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站策劃、網(wǎng)站排名、品牌網(wǎng)站制作、網(wǎng)站營(yíng)銷建站公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都做網(wǎng)站