logo资料库

linux下的i2c驱动以及与时钟芯片pcf8563通信(二).pdf

第1页 / 共15页
第2页 / 共15页
第3页 / 共15页
第4页 / 共15页
第5页 / 共15页
第6页 / 共15页
第7页 / 共15页
第8页 / 共15页
资料共15页,剩余部分请下载后查看
2012/7/9 linux 下的 i2c 与时钟芯片 pcf8563 by: 韩大卫 @吉林师范大学 通信 为更深入的了解 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 以找到执行的层层路径。 知道了数据的发送路径,再观察出 hwclock -w 实现了哪些数据的包装和发送,那么我们的程序就可以在以用 户层模仿这些操作。 层。 在这之间的执行函数分别加 printk,在版卡上观察 dmesg, 这样就可 我们版卡的 cpu 是 Cavium Networks OCTEON CN52XX ********************************************** ******************************* 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 () 下面是 octeon_i2c_xfer 的代码: 后,要进入 if(num==1)中。
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); ret = octeon_i2c_ia_write(i2c, msgs); } octeon_i2c_stop(i2c); out: return (ret != 0) ? ret : num; } else 通过在 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=11 2 [ 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 通过 octeon_i2c_simple_write 来写 pcf8563 时钟芯片, 我们看到: hwclock -w 其中: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 行结果。 另外 , 还要通过 hwclock (),再传入 octeon_i2c_simple_write,看看能不能得到期望的执 来观察 ,为什么没有使用 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 这个 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 内容包括了全部的写的信息。 最后一个参数是 1 , 就是写的时候每次只写一个 msg 而在 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 不同, l en 不同,buf 内容也不同。 正是由于 pcf8563_get_datetime 与 pcf8563_set_datetime 每次操作 msg 的个数不同, 所以进入 octeon_i2c_xfer() n_i2c_xfer 而 pcf8563_set_datetime 每次操作了 octeon_i2c_xfer 这就是为什么使用 hwclock -w 后观察不到 octeon_i2c_xfer()进入 octeon_i2c_simple_read()的原因。 后 执行的函数不同,这就是为什么每次 pcf8563_get_datetime 操作了 octeo 的 octeon_i2c_simple_write () 与 octeon_i2c_read() 的 octeon_i2c_write () Hwclock -w 后: here is pcf8563_get_datetime ,next func is i2c_transfer! [ 1102.616179] octeon_i2c_xfer: num = 2
分享到:
收藏