最近在看uboot源码,写个博客分享一下自己的学习例程,同时也是做个笔记,方便后面的复习。
硬件平台:RK3188
u-boot版本:2014-01
RK平台I2C源码在drivers/i2c目录中的rk_i2c.c和rk_i2c.h文件中,面向用户调用的函数有两个
/*
* i2c_read - Read from i2c memory
* @chip: target i2c address
* @addr: address to read from
* @alen:
* @buffer: buffer for read data
* @len: num of bytes to be read
*
* Read from i2c memory.
*/
int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len)
{
struct rk30_i2c *i2c = (struct rk30_i2c *)get_base();
if (i2c == NULL || buf == NULL) {
printf("i2c_read error: i2c = 0x%08x, buf = 0x%08x\n", i2c, buf);
return -1;
}
return rk_i2c_read(i2c, chip, addr, alen, buf, len);
}
/*
* i2c_write - Write to i2c memory
* @chip: target i2c address
* @addr: address to read from
* @alen:
* @buffer: buffer for read data
* @len: num of bytes to be read
*
* Write to i2c memory.
*/
int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len)
{
struct rk30_i2c *i2c = (struct rk30_i2c *)get_base();
if (i2c == NULL || buf == NULL) {
printf("i2c_read error: i2c = 0x%08x, buf = 0x%08x\n", i2c, buf);
return -1;
}
return rk_i2c_write(i2c, chip, addr, alen, buf, len);
}
从上面源码的注释中可以看出,传入参数分别为设备地址(chip)、寄存器地址(addr)、读或写的数据地址(buffer)和需要读写的字节数(len),其中alen未作注释。这两个函数只是对i2c的状态做一个判断,同时得到当前需要操作的i2c基地址,具体作用的函数在
rk_i2c_write(i2c, chip, addr, alen, buf, len)函数中,下面以写函数进行分析。
往下追一层函数得到下面的代码:
static int rk_i2c_write(struct rk30_i2c *i2c, uchar chip, uint reg, uint r_len, uchar *buf, uint b_len)
{
if (r_len + b_len + 1 > 32) {
return -1;
}
rk30_i2c_enable(i2c, 0, 1);
rk_i2c_get_ipd_event(i2c, I2C_STARTIPD);
rk_i2c_clean_start(i2c);
rk_i2c_write_prepare(i2c, chip, reg, r_len, buf, b_len);
rk_i2c_get_ipd_event(i2c, I2C_MBTFIPD);
rk_i2c_send_stop(i2c);
rk_i2c_get_ipd_event(i2c, I2C_STOPIPD);
rk_i2c_clean_stop(i2c);
rk_i2c_disable(i2c);
return 0;
}
函数的传入参数增加了调用函数中得到的i2c基地址,同时做了一些I2C基本的操作,数据的写入在
rk_i2c_write_prepare(i2c, chip, reg, r_len, buf, b_len)函数中,函数内容如下:
static void rk_i2c_write_prepare(struct rk30_i2c *i2c, uchar chip, uint reg, uint r_len, uchar *buf, uint b_len)
{
unsigned int data = 0, cnt = 0, len = 0, i, j;
unsigned char byte;
i2c_writel(I2C_MBTFIEN, i2c->regs + I2C_IEN);
for(i = 0; i < 8; i++) {
data = 0;
for(j = 0; j < 4; j++) {
if (is_msgend(cnt, r_len, b_len))
break;
if (cnt == 0) {
byte = (chip & 0x7f) << 1;
} else if ((cnt == 1) && (r_len != 0)) {
byte = (unsigned char)(reg & 0xff);
} else if ((cnt == 2) && (r_len == 2)) {
byte = (unsigned char)((reg >> 8) & 0xff);
} else {
byte = buf[len++];
}
cnt++;
data |= (byte << (j * 8));
}
i2c_writel(data, i2c->regs + I2C_TXDATA_BASE + 4 * i);
if (is_msgend(cnt, r_len, b_len))
break;
}
i2c_writel(cnt, i2c->regs + I2C_MTXCNT);
}
对于传入数据的操作全部在for循环中完成,总共发送8次,在这里可以看出,r_len决定了寄存器地址的长度,下面从r_len为2和不是2的两种情况来分析。
在r_len = 2的时候,将data分为4块,首次进入后将设备地址(chip)放在data的低8位(0字节),将寄存器地址分为高低字节,将低地址放在data的第1字节,高地址放在data的第2字节,data第3字节由写入数据的收地址buf[0]填充;
在r_len != 2的时候,首次进入后将设备地址(chip)放在data的低8位(0字节),将寄存器地址放在data的第1字节,data的第2和3字节分别有buf[0]和buf[1]填充;
从上面的分析可以看出,正常使用时,如果传送的寄存器地址为8位,则r_len=1,寄存器地址为16位,则r_len=2。
在循环中有两个用于判断是否数据传输完成的语句if (is_msgend(cnt, r_len, b_len))break,其函数原型如下:
/* returns TRUE if we reached the end of the current message */
static inline int is_msgend(uint cnt, uint r_len, uint b_len)
{
return cnt >= 1 + r_len + b_len;
}
其实就是根据寄存器地址长度和数据长度来判断i2c的发送是否已经完成,如果发送完成则跳出发送循环。