1. i2c_transfer()
函数本身不具备驱动适配器物理硬件完成消息交互的能力,它只是寻找到i2c_adapter对应的i2c_algorithm,并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程. 成功返回0
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
"len=%d\n", ret, msgs[ret].flags & I2C_M_RD ?
'R' : 'W', msgs[ret].addr, msgs[ret].len);
}
#endif
down(&adap->bus_lock);
/* 主要就是这一句,调用master_xfer() */
ret = adap->algo->master_xfer(adap,msgs,num);
up(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
2. master_xfer()
下面的代码是基于s3c2410,代码在driver/i2c/busses/i2c_s3c2410.c
static struct i2c_algorithm s3c24xx_i2c_algorithm = {
.name = "S3C2410-I2C-Algorithm",
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
}
上面的是i2c总线的注册信息,master_xfer被设置成
s3c24xx_i2c_xfer.当信息通道i2c总线传输,这是第一个被调用的端口
该函数实现了重试功能, 数据传输的实现是调用s3c24xx_i2c_doxfer().
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
//从适配器的私有数据中获得适配器s3c24xx_i2c结构体
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;int retry;int ret; /* 重试的次数,adap->retries */
for (retry = 0; retry < adap->retries; retry++) {
/* 主要就这一句,调用s3c24xx_i2c_doxfer()*/
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
if (ret != -EAGAIN)
return ret;
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
udelay(100);
}
return -EREMOTEIO;
}
该函数开始传输数据,成功返回0,否则返回-EAGAIN
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
{
unsigned long timeout;
int ret;
/*设置成主设备发送状态,判断是否属于busy,不忙返回0,否则返回-ETIMEOUT*/
ret = s3c24xx_i2c_set_master(i2c);
if (ret != 0) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
ret = -EAGAIN;
goto out;
}
spin_lock_irq(&i2c->lock);
/* 设置参数 将状态设置成开始*/
i2c->msg = msgs;
i2c->msg_num = num;
i2c->msg_ptr = 0;
i2c->msg_idx = 0;
i2c->state = STATE_START;
s3c24xx_i2c_enable_irq(i2c); /* 使能中断 */
s3c24xx_i2c_message_start(i2c, msgs); /* 重要 主设备将信息发送到总线上*/
spin_unlock_irq(&i2c->lock);
/* 进入睡眠,直到 i2c->msg_num == 0 或 HZ * 5 时间后;正常返回meg_num*/
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
ret = i2c->msg_idx;
if (timeout == 0)
dev_dbg(i2c->dev, "timeout\n");
else if (ret != num)
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
msleep(1);
out:
return ret;
}
4.s3c24xx_i2c_message_start()
该函数配置寄存器,完成在I2C总线上广播第一个slave address + write/read
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,struct i2c_msg *msg){
/* 设置从设备地址 */
unsigned int addr = (msg->addr & 0x7f) << 1;
unsigned long stat; //slave device address
unsigned long iiccon;
/*配置register设置i2c总线start,并完成前面广播从设备地址的功能*/
stat = 0;
stat |= S3C2410_IICSTAT_TXRXEN;
if (msg->flags & I2C_M_RD) {
/* 设置写操作 */
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1; //last bit = 1
} else
/* 设置读操作 */
stat |= S3C2410_IICSTAT_MASTER_TX;
//this should has "addr |=0;"
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
/* 使能ACK */
s3c24xx_i2c_enable_ack(i2c);
iiccon = readl(i2c->regs + S3C2410_IICCON);
writel(stat, i2c->regs + S3C2410_IICSTAT);
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
writeb(addr, i2c->regs + S3C2410_IICDS);
ndelay(i2c->tx_setup);
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
writel(iiccon, i2c->regs + S3C2410_IICCON);
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
}