Linux下的i2c驅動以及與時鐘芯片PCF8563通信過程。
為更深入的了解linux下的i2c總線驅動以及通信原理,可以用一個用戶程序模擬,這個程序,可以使用一個addr, 一個offset,對i2c的從設備地址為addr,寄存器地址為offset的寄存器讀寫操作。
在我們的版卡上時鐘芯片pcf8563的i2c地址為0x51 , pcf8563有00—0f個寄存器,通過讀寫秒,分鐘,小時等的寄存器,可以驗證我們的程序是否執行成功。
一,這個測試程序怎麼寫?
思路是: hwclock -w /hwclock -s 這些命令都是對始終芯片pcf8563執行了讀寫的操作命令,那麼我們的程序,就模仿hwclock -w 的執行過程,最後實現通過cpu(octeon) 與i2c從設備的數據通信。 這樣就看到了i2c總線在處理器octeon的控制下的通信過程。
二,怎麼觀察hwclock -w 的執行過程?
hwclock -w 讀寫了時鐘芯片pcf8563,那麼從pcf8563的驅動程序入手,在pcf8563中的read,write 函數中進入i2c層。再有i2c層進入octeon。
即從rtc層進入i2c層, 再進入cpu層。 在這之間的執行函數分別加printk,在版卡上觀察dmesg, 這樣就可以找到執行的層層路徑。
知道了數據的發送路徑,再觀察出hwclock -w 實現了哪些數據的包裝和發送,那麼我們的程序就可以在以用戶層模仿這些操作。
注意:
我們版卡的cpu是CaviumNetworks OCTEON CN52XX
********************************************************************
by 韓大衛 @卓訊技術@吉林師范大學
轉載務必表明出處!
********************************************** **********************
hwclock -w 命令需要使用到的rtc芯片pcf8563中的讀寫函數如下:
在driver/rtc/rtc-pcf8563.c 中
static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
int i, err;
unsigned char buf[9];
printk(KERN_DEBUG "%s: secs=%d, mins=%d, hours=%d,ecs=%d, mins=%d, hours=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
/* hours, minutes and seconds */
buf[PCF8563_REG_SC] = bin2bcd(tm->tm_sec);
buf[PCF8563_REG_MN] = bin2bcd(tm->tm_min);
buf[PCF8563_REG_HR] = bin2bcd(tm->tm_hour);
buf[PCF8563_REG_DM] = bin2bcd(tm->tm_mday);
/* month, 1 - 12 */
buf[PCF8563_REG_MO] = bin2bcd(tm->tm_mon + 1);
/* year and century */
buf[PCF8563_REG_YR] = bin2bcd(tm->tm_year % 100);
if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
buf[PCF8563_REG_MO] |= PCF8563_MO_C;
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
/* write register's data */
for (i = 0; i < 7; i++) {
unsigned char data[2] = { PCF8563_REG_SC + i,
buf[PCF8563_REG_SC + i] };
err = i2c_master_send(client, data, sizeof(data));
if (err != sizeof(data)) {
dev_err(&client->dev,
"%s: err=%d addr=%02x, data=%02x\n",
__func__, err, data[0], data[1]);
return -EIO;
}
在 driver/i2c/i2c-core.c 中:
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
//added by handwei.2012.7.5
printk(KERN_DEBUG "%s: msg.addr = %x,msg.flags = %x,msg.len = %d,msg.buf[0] = %x,msg.buf[1] = %x\n",__func__,msg.addr,msg.flags,msg.len,msg.buf[0],msg.buf[1]);
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
注意: i2c_transfer(adap, &msg, 1);
中的 1 決定了 進入 octeon_i2c_xfer ()後,要進入 if(num==1)中。