linux下的i2c与时钟芯片pcf8563通信(引言篇)

2012/7/9
					
linux下的i2c与时钟芯片pcf8563通信(引言篇)  			

by: 韩大卫 @吉林师范大学@08信息

	 
为更深入的了解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 实现了哪些数据的包装和发送,那么我们的程序就可以在以用户层模仿这
些操作。这样就可以看到linux 下有关与i2c总线的架构,层与层之间怎么发生的联系,使用了哪些关键的函数,通过哪些操
作读写了i2c从设备的相应地址的寄存器。 如果观察到了这些,那么我们就不仅仅读懂了i2c的驱动,也知道如何来driver了。

另外,本文章为引言篇,目的只是让大家看到思路,代码中还有一些不成熟的地方,比如没有实现写pcf8563星期寄存器,

还有我的代码将假设是时钟芯片挂在/dev/i2c-1 号总线上,如果在其他的嵌入式系统中pcf8563挂在i2c-0号总线上,

那么我们的命令将不能实现等等。

最终的发布命令可以参考《linux下的i2c与时钟芯片pcf8563通信》这篇文章中的代码,上述问题都得到了很好的解决。


 ************ *****************************************

关于用户层的ioctl(),与内核源代码 driver/i2c/i2c-dev.c 中的i2cdev_ioctl()——>i2cdev_ioctl_rdrw

()----->i2c_transfer()------->master_xfer() -------------> xxx_i2c_xfer() 之间的联系如果不清楚的话,

那么请查询linux下有关于 i2c驱动,i2c架构的相关资料。 也可联系我获取资料,

我的联系方式是:handawei@jusontech.com 

我们版卡的cpu是Cavium Networks OCTEON CN52XX,所以使用的内核代码是: drivers/i2c/busses/i2c-octeon.c 
								
 
********************************************** *******************************

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)中。

下面是 octeon_i2c_xfer的代码:

static int octeon_i2c_xfer(struct i2c_adapter *adap,
                           struct i2c_msg *msgs,
                           int num)
{
        printk(KERN_DEBUG "here is octeon_i2c_xfer,num = %d\n",num);
        struct i2c_msg *pmsg;
        int i;
        int ret = 0; 
        struct octeon_i2c *i2c = i2c_get_adapdata(adap);

        if (num == 1) { 
                if (msgs[0].len > 0 && msgs[0].len <= 8) { 
                        if (msgs[0].flags & I2C_M_RD)
                                ret = octeon_i2c_simple_read(i2c, msgs);
                        else 
                                ret = octeon_i2c_simple_write(i2c, msgs);
                        goto out; 
                }    
        } else if (num == 2) { 
                if ((msgs[0].flags & I2C_M_RD) == 0 && 
                    msgs[0].len > 0 && msgs[0].len <= 2 && 
                    msgs[1].len > 0 && msgs[1].len <= 8 && 
                    msgs[0].addr == msgs[1].addr) {
                        if (msgs[1].flags & I2C_M_RD)
                                ret = octeon_i2c_ia_read(i2c, msgs);
                        else 
                                ret = octeon_i2c_ia_write(i2c, msgs);
                        goto out; 
                }    
        }    

        for (i = 0; ret == 0 && i < num; i++) {
                pmsg = &msgs[i];
                dev_dbg(i2c->dev,
                        "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n",
                         pmsg->flags & I2C_M_RD ? "read" : "write",
                         pmsg->len, pmsg->addr, i + 1, num);
                if (pmsg->flags & I2C_M_RD)
                        ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf,
                                              pmsg->len, i);
			 else
                                ret = octeon_i2c_ia_write(i2c, msgs);
                                                                         }
        octeon_i2c_stop(i2c);
out:
        return (ret != 0) ? ret : num;
}



通过在 i2c-core.c: i2c_master_send()中添加printk, 
 
        printk(KERN_DEBUG "%s: msg.addr = %x,msg.flags = %x,msg.len = %d,msg.buf(data) = %s\n",
                __func__,msg.addr,msg.flags,msg.len,msg.buf);


运行后可以看到 msg.flags 一直等于0 ,那么  在  octeon_i2c_xfer ()中
			
	if (num == 1) { 
                if (msgs[0].len > 0 && msgs[0].len <= 8) { 
                        if (msgs[0].flags & I2C_M_RD)
                                ret = octeon_i2c_simple_read(i2c, msgs);
                        else 
                                ret = octeon_i2c_simple_write(i2c, msgs);
                        goto out; 


    会一直进入octeon_i2c_simple_write()执行写操作。 下面是octeon_i2c_simple_write()的代码:


static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
{
        u64 cmd;
        int i, j;
        int ret = 0;

        octeon_i2c_enable_hlc(i2c);

        printk(KERN_DEBUG "%s:\n",__func__);

retry:
        cmd = SW_TWSI_V | SW_TWSI_SOVR;
        /* SIZE */
        cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT;
        /* A */
        cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_A_SHIFT;

        if (msgs[0].flags & I2C_M_TEN)
                cmd |= SW_TWSI_OP_10;
        else
                cmd |= SW_TWSI_OP_7;

                printk(KERN_DEBUG "%s:cmd = %llx\n",__func__,cmd);

        for (i = 0, j = msgs[0].len - 1; i  < msgs[0].len && i < 4; i++, j--){
                cmd |= (u64)msgs[0].buf[j] << (8 * i);
         printk(KERN_DEBUG "%s:msgs[0].buf[%d] = %x,cmd = %llx\n",__func__,j,msgs[0].buf[j],cmd);
        }
        if (msgs[0].len >= 4) {
                u64 ext = 0;
                for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--)
                        ext |= (u64)msgs[0].buf[j] << (8 * i);

                        printk(KERN_DEBUG "%s:ext = %llx\n",__func__,ext);

                __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT);
        }

        octeon_i2c_hlc_int_clear(i2c);
        __raw_writeq(cmd, i2c->twsi_base + SW_TWSI);

   ret = octeon_i2c_hlc_wait(i2c);

        if (ret)
                goto err;

        cmd = __raw_readq(i2c->twsi_base + SW_TWSI);

        if ((cmd & SW_TWSI_R) == 0) {
                if (octeon_i2c_lost_arb(cmd))
                        goto retry;
                ret = -EIO;
                goto err;
        }

err:
        return ret;
}


                                                                                                       


  下面是版卡执行 hwclock -w 后的dmesg 中的一部分:




[   38.989069] pcf8563_get_datetime:  secs=4, mins=41, hours=18,ecs=5, mins=6, hours=112
[   40.292306] pcf8563_set_datetime:  secs=5, mins=41, hours=18,ecs=5, mins=6, hours=112
[   40.292321] pcf8563_set_datetime:data[0] = 2,data[1] = 5
[   40.292333] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 2,msg.buf[1] = 5
[   40.292346] here is octeon_i2c_xfer, num = 1
[   40.292355] octeon_i2c_simple_write:
[   40.292363] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.292374] octeon_i2c_simple_write:msgs[0].buf[1] = 5,cmd = 8090510000000005
[   40.292386] octeon_i2c_simple_write:msgs[0].buf[0] = 2,cmd = 8090510000000205
[   40.292687] pcf8563_set_datetime:data[0] = 3,data[1] = 41
[   40.292699] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 3,msg.buf[1] = 41
[   40.292712] here is octeon_i2c_xfer, num = 1
[   40.292719] octeon_i2c_simple_write:
[   40.292727] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.292738] octeon_i2c_simple_write:msgs[0].buf[1] = 41,cmd = 8090510000000041
[   40.292750] octeon_i2c_simple_write:msgs[0].buf[0] = 3,cmd = 8090510000000341
[   40.293049] pcf8563_set_datetime:data[0] = 4,data[1] = 18
[   40.293061] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 4,msg.
buf[1] = 18
[   40.293074] here is octeon_i2c_xfer, num = 1
[   40.293082] octeon_i2c_simple_write:
[   40.293089] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.293100] octeon_i2c_simple_write:msgs[0].buf[1] = 18,cmd = 8090510000000018
[   40.293112] octeon_i2c_simple_write:msgs[0].buf[0] = 4,cmd = 8090510000000418
[   40.293411] pcf8563_set_datetime:data[0] = 5,data[1] = 5
[   40.293423] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 5,msg.
buf[1] = 5
[   40.293435] here is octeon_i2c_xfer, num = 1
[   40.293443] octeon_i2c_simple_write:
[   40.293451] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.293462] octeon_i2c_simple_write:msgs[0].buf[1] = 5,cmd = 8090510000000005
[   40.293474] octeon_i2c_simple_write:msgs[0].buf[0] = 5,cmd = 8090510000000505
[   40.293772] pcf8563_set_datetime:data[0] = 6,data[1] = 4
[   40.293784] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 6,msg.buf[1] = 4
[   40.293796] here is octeon_i2c_xfer, num = 1
[   40.293804] octeon_i2c_simple_write:
[   40.293812] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.293822] octeon_i2c_simple_write:msgs[0].buf[1] = 4,cmd = 8090510000000004
[   40.293835] octeon_i2c_simple_write:msgs[0].buf[0] = 6,cmd = 8090510000000604
[   40.294133] pcf8563_set_datetime:data[0] = 7,data[1] = 7
[   40.294145] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 7,msg.
buf[1] = 7
[   40.294157] here is octeon_i2c_xfer, num = 1
[   40.294165] octeon_i2c_simple_write:
[   40.294173] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.294184] octeon_i2c_simple_write:msgs[0].buf[1] = 7,cmd = 8090510000000007
[   40.294196] octeon_i2c_simple_write:msgs[0].buf[0] = 7,cmd = 8090510000000707
[   40.294494] pcf8563_set_datetime:data[0] = 8,data[1] = 12
[   40.294506] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 8,msg.
buf[1] = 12
[   40.294519] here is octeon_i2c_xfer, num = 1
[   40.294526] octeon_i2c_simple_write:
[   40.294534] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.294545] octeon_i2c_simple_write:msgs[0].buf[1] = 12,cmd = 8090510000000012
[   40.294557] octeon_i2c_simple_write:msgs[0].buf[0] = 8,cmd = 8090510000000812



pcf8563_get_datetime:  是读到的时间
pcf8563_set_datetime:   是要写的时间

	在pcf8563_set_datetime 中需要执行7次循环:

/* 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));


第一个循环部分dmesg:
[   40.292321] pcf8563_set_datetime:data[0] = 2,data[1] = 5
[   40.292333] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 2,msg.
buf[1] = 5
[   40.292346] here is octeon_i2c_xfer, num = 1
[   40.292355] octeon_i2c_simple_write:
[   40.292363] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.292374] octeon_i2c_simple_write:msgs[0].buf[1] = 5,cmd = 8090510000000005
[   40.292386] octeon_i2c_simple_write:msgs[0].buf[0] = 2,cmd = 8090510000000205

在pcf8563_set_datetime ()中:


 for (i = 0; i < 7; i++) {
                unsigned char data[2] = { PCF8563_REG_SC + i,

		//注意: #define PCF8563_REG_SC          0x02

                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]);

}


通过i2c_master_send()进入octeon_i2c_xfer(),再进入 octeon_i2c_simple_write,

在这个函数中:
{

cmd = SW_TWSI_V | SW_TWSI_SOVR;

cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT;
//   Address field.:<49:40>
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_A_SHIFT;

if (msgs[0].flags & I2C_M_TEN)
                cmd |= SW_TWSI_OP_10;
	
for (i = 0, j = msgs[0].len - 1; i  < msgs[0].len && i < 4; i++, j--){
                cmd |= (u64)msgs[0].buf[j] << (8 * i);

...
}

根据msgs中的len,addr,flags, buf[]中的内容填充cmd,执行cmd 并 返回执行结果

  同样的, 在进入第二次循环时,  这时操作的是pcf8563 的第三个寄存器,03h: minutes 分钟寄存器。 

[   40.292687] pcf8563_set_datetime:data[0] = 3,data[1] = 41
[   40.292699] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 3,msg.
buf[1] = 41
[   40.292712] here is octeon_i2c_xfer, num = 1
[   40.292719] octeon_i2c_simple_write:
[   40.292727] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.292738] octeon_i2c_simple_write:msgs[0].buf[1] = 41,cmd = 8090510000000041
[   40.292750] octeon_i2c_simple_write:msgs[0].buf[0] = 3,cmd = 8090510000000341


第三次,操作pcf8563的第四个寄存器:04h hours 
[   40.293049] pcf8563_set_datetime:data[0] = 4,data[1] = 18
[   40.293061] i2c_master_send: msg.addr = 51,msg.flags = 0,msg.len = 2,msg.buf[0] = 4,msg.
buf[1] = 18
[   40.293074] here is octeon_i2c_xfer, num = 1
[   40.293082] octeon_i2c_simple_write:
[   40.293089] octeon_i2c_simple_write:cmd = 8090510000000000
[   40.293100] octeon_i2c_simple_write:msgs[0].buf[1] = 18,cmd = 8090510000000018
[   40.293112] octeon_i2c_simple_write:msgs[0].buf[0] = 4,cmd = 8090510000000418




我们看到:

hwclock -w 通过 octeon_i2c_simple_write 来写pcf8563时钟芯片,

其中:msg.addr = 0x51 ,  msg.len = 2 ,msg.flags = 0, 

msg.buf[0] = 相应地址偏移
msg.buf[1] = 给相应寄存器的值

cmd 中最后8bit为数据域: 0418  ,04 代表offset, 18代表data。 


那么我们的i2c-test, 也这样操作试试:

包装一个msg,  传入octeon_i2c_xfer(),再传入 octeon_i2c_simple_write,看看能不能得到期望的执行结果。
另外 , 还要通过hwclock 来观察  ,为什么没有使用octeon_i2c_simple_read  来读寄存器的现象。

														

注意到:

在关于写的操作中: pcf8563_set_datetime()
 i2c_master_send() 
{
..
   msg.addr = client->addr;
       msg.flags = client->flags & I2C_M_TEN;
        msg.len = count;
        msg.buf = (char *)buf;
 
        ret = i2c_transfer(adap, &msg, 1);

..}
 
i2c_transfer 最后一个参数是 1, 就是写的时候每次只写一个msg

这个msg中的成员buf,在pcf8563_set_datetime()中做了相应的填充:

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));

}

最后,这一个msg中buf 内容包括了全部的写的信息。


而在pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
        struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
        unsigned char buf[13] = { PCF8563_REG_ST1 };

        struct i2c_msg msgs[] = { 
                { client->addr, 0, 1, buf },    /* setup read ptr */
                { client->addr, I2C_M_RD, 13, buf },    /* read status + date */
        };  


        if ((i2c_transfer(client->adapter, msgs, 2)) != 2) 
...

}


i2c_transfer 第三个参数是2,即每次读操作时,每次操作2个msg。 两个msg,addr相同,flags不同, len不同,buf内容也不同。

正是由于pcf8563_get_datetime与pcf8563_set_datetime  每次操作msg的个数不同,

所以进入octeon_i2c_xfer()后  执行的函数不同,这就是为什么每次pcf8563_get_datetime操作了octeon_i2c_xfer的  octeon_i2c_write() 与 octeon_i2c_read()

而pcf8563_set_datetime 每次操作了octeon_i2c_xfer的  octeon_i2c_simple_write() 

这就是为什么使用hwclock -w 后观察不到octeon_i2c_xfer()进入octeon_i2c_simple_read()的原因。


Hwclock -w  后:

here is pcf8563_get_datetime ,next func is i2c_transfer!
[ 1102.616179] octeon_i2c_xfer: num = 2
[ 1102.616188] octeon_i2c_xfer:msgs[0].addr = 51, msgs[0].flags = 0, msgs[0].len = 1
[ 1102.616200] octeon_i2c_xfer:msgs[0].buf[0] = 0
[ 1102.616211] octeon_i2c_xfer:msgs[1].addr = 51, msgs[1].flags = 1, msgs[1].len = 13
[ 1102.616223] octeon_i2c_xfer:msgs[1].buf[1] = 0
[ 1102.616233] octeon_i2c_write:data[0](msgs->buf[]) = 0, target(addr) = 51,  length = 1, phase = 0
[ 1102.616360] octeon_i2c_write: data[0] = 0 
[ 1102.616468] octeon_i2c_read:data[0](msgs->buf[0]) = 0,target(addr) = 51, length = 13,phase = 1
[ 1102.616697] octeon_i2c_read: data[0] =  8 
[ 1102.616804] octeon_i2c_read: data[1] =  0 
[ 1102.616911] octeon_i2c_read: data[2] =  28 
[ 1102.617019] octeon_i2c_read: data[3] =  35 
[ 1102.617127] octeon_i2c_read: data[4] =  14 
[ 1102.617235] octeon_i2c_read: data[5] =  6 
[ 1102.617342] octeon_i2c_read: data[6] =  2d 
[ 1102.617450] octeon_i2c_read: data[7] =  27 
[ 1102.617557] octeon_i2c_read: data[8] =  12 
[ 1102.617665] octeon_i2c_read: data[9] =  a0 
[ 1102.617773] octeon_i2c_read: data[10] =  84 
[ 1102.617881] octeon_i2c_read: data[11] =  b2 
[ 1102.617989] octeon_i2c_read: data[12] =  b5 
[ 1102.618003] here come back pcf8563_get_datetime ,the received data :  secs=28, mins=35, 
hours=14,mday=6, mon=6, year=112,wday=5



由于num = 2,


进入octeon_i2c_xfer后 先要判断:

 else if (num == 2) { 
                if ((msgs[0].flags & I2C_M_RD) == 0 && 
                    msgs[0].len > 0 && msgs[0].len <= 2 && 
                    msgs[1].len > 0 && msgs[1].len <= 8 && 
                    msgs[0].addr == msgs[1].addr) {
                        if (msgs[1].flags & I2C_M_RD)
                                ret = octeon_i2c_ia_read(i2c, msgs);
                        else 
                                ret = octeon_i2c_ia_write(i2c, msgs);
                        goto out; 

  for (i = 0; ret == 0 && i < num; i++) {
                pmsg = &msgs[i];
                dev_dbg(i2c->dev,
                        "Doing %s %d byte(s) to/from 0x%02x - %d of %d messages\n",
                         pmsg->flags & I2C_M_RD ? "read" : "write",
                         pmsg->len, pmsg->addr, i + 1, num);
                if (pmsg->flags & I2C_M_RD)
                        ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf,
                                              pmsg->len, i);
                else
                        ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf,
                                               pmsg->len, i);
        }



由于  msgs[1].len = 13,不满足条件,因此即使num=2,pcf8563_get_datetime永远不会进入
octeon_i2c_ia_read/write  ,不会进入内部地址的读写操作。

之后程序进入for循环,这样,经过判断,第一次要进入octeon_i2c_write()函数,第二次循环进入 octeon_i2c_read(),

在  Octeon_i2c_write()中:



static int octeon_i2c_write(struct octeon_i2c *i2c, int target,
                            const u8 *data, int length, int phase)
{
      
        int i, result;
        u8 tmp; 

restart:
        result = octeon_i2c_start(i2c);
        if (result)
                return result;

        octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1);
        octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);

        result = octeon_i2c_wait(i2c);
        if (result)
                return result;

        for (i = 0; i < length; i++) {
                tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
                if (phase == 0 && octeon_i2c_lost_arb(tmp))
                        goto restart;

                if ((tmp != STAT_TXADDR_ACK) && (tmp != STAT_TXDATA_ACK)) {
                        dev_err(i2c->dev,
                                "%s: bad status before write (0x%x)\n",
                                __func__, tmp);
                        return -EIO;
                }

                printk(KERN_DEBUG "%s: data[%d] =  %x\n",__func__,i,data[i]);

                octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]);
                octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);

                result = octeon_i2c_wait(i2c);
                if (result)
                        return result;
        }

        return 0;
}


其中:

	octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]);

作用就是将data[i] 数据写进入octeon  的数据寄存器,即实现了写的操作。




static int octeon_i2c_read(struct octeon_i2c *i2c, int target,
                           u8 *data, int length, int phase)
{
        //added by handawei.2012.7.2
    printk(KERN_DEBUG "%s:data[0](msgs->buf[0]) = %xtarget(addr) = %x, length = %d,phase = %d\n",
                                __func__,data[0],target,length,phase);

        int i, result;
        u8 tmp;

        if (length < 1)
                return -EINVAL;

restart:
        result = octeon_i2c_start(i2c);
        if (result)
                return result;

        octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, (target<<1) | 1);
        octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB);

        result = octeon_i2c_wait(i2c);
        if (result)
                return result;

        for (i = 0; i < length; i++) {
                tmp = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT);
                if (phase == 0 && octeon_i2c_lost_arb(tmp))
                        goto restart;

                if ((tmp != STAT_RXDATA_ACK) && (tmp != STAT_RXADDR_ACK)) {
                        dev_err(i2c->dev,
                                "%s: bad status before read (0x%x)\n",
                                __func__, tmp);
                        return -EIO;
                }

                if (i+1 < length)
                        octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
                                                TWSI_CTL_ENAB | TWSI_CTL_AAK);
                else
                        octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL,
                                                TWSI_CTL_ENAB);

                result = octeon_i2c_wait(i2c);
                if (result)
                        return result;

                data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA);
                printk(KERN_DEBUG "%s: data[%d] =  %x\n",__func__,i,data[i]);

        }
        return 0;
                             
}


   data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA);


data[i]   就保存了cpu 读数据寄存器后的返回结果,即保存了读的操作。

here is pcf8563_get_datetime ,next func is i2c_transfer!
[ 1102.616179] octeon_i2c_xfer: num = 2
[ 1102.616188] octeon_i2c_xfer:msgs[0].addr = 51, msgs[0].flags = 0, msgs[0].len = 1
[ 1102.616200] octeon_i2c_xfer:msgs[0].buf[0] = 0
[ 1102.616211] octeon_i2c_xfer:msgs[1].addr = 51, msgs[1].flags = 1, msgs[1].len = 13
[ 1102.616223] octeon_i2c_xfer:msgs[1].buf[1] = 0
[ 1102.616233] octeon_i2c_write:data[0](msgs->buf[]) = 0, target(addr) = 51,  length = 1, phase = 0
[ 1102.616360] octeon_i2c_write: data[0] = 0 
[ 1102.616468] octeon_i2c_read:data[0](msgs->buf[0]) = 0,target(addr) = 51, length = 13,phase = 1
[ 1102.616697] octeon_i2c_read: data[0] =  8 
[ 1102.616804] octeon_i2c_read: data[1] =  0 
[ 1102.616911] octeon_i2c_read: data[2] =  28 
[ 1102.617773] octeon_i2c_read: data[10] =  84 
[ 1102.618003] here come back pcf8563_get_datetime ,the received data :  secs=28, mins=35, 
hours=14,mday=6, mon=6, year=112,wday=5


可以看到,len = 13,经过了13次循环后,通过读pcf8563的00-0c号寄存器,buf[]得到了填充。

而返回后Buf[]  的值赋给了  struct rtc_time *tm    的相应成员。

在pcf8563_get_datatime()中,完成了对struct rtc_time *tm 的填充,完成了读了操作。


static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
        struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
        unsigned char buf[13] = { PCF8563_REG_ST1 };

        struct i2c_msg msgs[] = { 
                { client->addr, 0, 1, buf },    /* setup read ptr */
                { client->addr, I2C_M_RD, 13, buf },    /* read status + date */
        };  

        printk(KERN_DEBUG "here is %s ,next func is i2c_transfer!\n",__func__);

        /* read registers */
        if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
                dev_err(&client->dev, "%s: read error\n", __func__);
                return -EIO;
        }   

        if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
                dev_info(&client->dev,
                        "low voltage detected, date/time is not reliable.\n");

        // the orginal data is dev_dbg . modified by handawei.2012.6.4
        dev_dbg(&client->dev,
                "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
                "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
                __func__,
                buf[0], buf[1], buf[2], buf[3],
                buf[4], buf[5], buf[6], buf[7],
                buf[8]);


        tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F);
        tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F);
        tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
        tm->tm_mday = bcd2bin(buf[PCF8563_REG_DM] & 0x3F);
        tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
        tm->tm_mon = bcd2bin(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ /* -1 :tm_mon:  
[0-11]  */
        tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]);
        if (tm->tm_year < 70) 
                tm->tm_year += 100;     /* assume we are in 1970...2069 */

	 pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
                (tm->tm_year >= 100) : (tm->tm_year < 100);



        /* detect the polarity heuristically. see note above. */
       if (rtc_valid_tm(tm) < 0)
                dev_err(&client->dev, "retrieved date/time is not valid.\n");

        return 0;
}


 执行 hwclock -r 后 也可以看到:


[ 2821.618046] here is pcf8563_get_datetime ,next func is i2c_transfer!
[ 2821.618058] octeon_i2c_xfer: num = 2
[ 2821.618067] octeon_i2c_xfer:msgs[0].addr = 51, msgs[0].flags = 0, msgs[0].len = 1
[ 2821.618079] octeon_i2c_xfer:msgs[0].buf[0] = 0
[ 2821.618090] octeon_i2c_xfer:msgs[1].addr = 51, msgs[1].flags = 1, msgs[1].len = 13
[ 2821.618102] octeon_i2c_xfer:msgs[1].buf[1] = 0
[ 2821.618112] octeon_i2c_write:data[0](msgs->buf[]) = 0, target(addr) = 51,  length = 1, phase = 0
[ 2821.618240] octeon_i2c_write: data[0] = 0 
[ 2821.618348] octeon_i2c_read:data[0](msgs->buf[0]) = 0,target(addr) = 51, length = 13,phase = 1
[ 2821.618578] octeon_i2c_read: data[0] =  8 
[ 2821.618685] octeon_i2c_read: data[1] =  0 
[ 2821.618792] octeon_i2c_read: data[2] =  8 
[ 2821.619329] octeon_i2c_read: data[7] =  7 
[ 2821.619440] octeon_i2c_read: data[8] =  12 
[ 2821.619547] octeon_i2c_read: data[9] =  a0 
[ 2821.619655] octeon_i2c_read: data[10] =  84 
[ 2821.619763] octeon_i2c_read: data[11] =  b2 
[ 2821.619871] octeon_i2c_read: data[12] =  b5 
[ 2821.619885] here come back pcf8563_get_datetime ,the received data :  secs=8, mins=4, hours=15,
mday=6, mon=6, year=112,wday=5


宗上,可以定下结论:

在执行关于时间的读操作时 ,使用了 pcf8563_get_datetime --> octeon_i2c_read  

执行关于时间啊的写操作是,使用了pcf8563_set_datetime   ---> octeon_i2c_simple_write


在octeon_i2c_xfer() 中 进入不同读写函数的条件是就利用 msg中的 flags,len.



我们仿照pcf8563_get_datetime()中的代码:

 struct i2c_msg msgs[] = { 
                { client->addr, 0, 1, buf },    /* setup read ptr */
                { client->addr, I2C_M_RD, 13, buf },    /* read status + date */
        };  


写设计一个我们自定义的数据结构,这个数据结构为struct i2c_rdwr_ioctl_data, 代码如下:

struct i2c_rdwr_ioctl_data {
        struct i2c_msg __user *msgs;    /* pointers to i2c_msgs */
        __u32 nmsgs;                    /* number of i2c_msgs */
};

struct i2c_msg代码如下:


struct i2c_msg {
        __u16 addr;     /* slave address                        */
        __u16 flags;
#define I2C_M_TEN               0x0010  /* this is a ten bit chip address */
#define I2C_M_RD                0x0001  /* read data, from slave to master */
#define I2C_M_NOSTART           0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR      0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK        0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK         0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN          0x0400  /* length will be first received byte */
        __u16 len;              /* msg length                           */
        __u8 *buf;              /* pointer to msg data                  */
};



我们定义一个i2c_read_data(),参考pcf8563_get_datetime,核心内容如下:
{
	
	struct i2c_rdwr_ioctl_data *data;
	data->nmsgs = 2;

	data->msgs[0].addr = addr;
	data->msgs[0].flags = 0;
	data->msgs[0].len = 1;
	data->msgs[0].buf[0] = offset;

	data->msgs[1].addr = addr;
	data->msgs[1].flags = I2C_M_RD;
	data->msgs[1].len = 13;			
	data->msgs[1].buf[0] = 0;

	ioctl(fd, I2C_RDWR, (unsigned long)data) ;

}


pcf8563_set_datetime中使用的i2c_master_send(){
..
	msg.addr = client->addr;
        msg.flags = client->flags & I2C_M_TEN;
        msg.len = count;
        msg.buf = (char *)buf;

	ret = i2c_transfer(adap, &msg, 1);
..
	}

参考以上,我们定义一个i2c_write_data(),核心代码如下:
{
	struct i2c_rdwr_ioctl_data *data;

	data->nmsgs = 1;


        data->msgs[0].addr = addr;
        data->msgs[0].flags = 0;
        data->msgs[0].len = 2;
        data->msgs[0].buf[0] = offset;
        data->msgs[0].buf[1] = val;


	ioctl(fd, I2C_RDWR, (unsigned long)data) ;



} 


通过i2cctl函数将我们包装好的数据结构发送出去。



******************************************************************************************

运行结果如下:



root@(none):/han# ./i2c-test /dev/i2c-20
 Hello i2c-test![ 5536.661954] i2c-mux.c: msgs = 51 , num = 2

enter i2c_open OK!
 i2c.c: i2c_open: fd = 3
enter __i2c_set [ 5536.669209] i2c-mux.c: msgs = 51 , num = 1
OK!
__i2c_set done!
here is i2c_read_data function in main.c
[ 5536.677154] i2c-mux.c: msgs = 51 , num = 2
 __i2c_send: data->msgs[0].addr = 51,data->msgs[0].buf[0] = 4
 i2c_read_data success, val = 15

 here is i2c_write_data function in main.c 
the data to write val = 8
 __i2c_send: data->msgs[0].addr = 51,data->msgs[0].buf[0] = 4
i2c_write_data success,val1 = 8

here is i2c_read_data function in main.c
 __i2c_send: data->msgs[0].addr = 51,data->msgs[0].buf[0] = 4
 i2c_read_data success, val = 8

root@(none):/han# date 
Fri Jul  6 15:49:25 UTC 2012
root@(none):/han# hwclock -r
Fri Jul  6 08:49:28 2012  -0.628861 seconds


				成功了!!!					

i2c-test 成功地读写到了地址为0x51,  偏移地址为0x04的 寄存器内容!!!

转载于:https://my.oschina.net/handawei/blog/68102

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值