2011.11.18 星期五
I2c-dev.c 参考代码 TP驱动分析(五)
1、
昨天分析的代码中,在I2C_RDWR中,解释了部分功能,但核心的通讯函数是
res = i2c_transfer(client->adapter,//适配器
rdwr_pa,//msg
rdwr_arg.nmsgs);//msg数量
其中
int i2c_transfer(
struct i2c_adapter * adap,
struct i2c_msg *msgs,
int num)
{
int ret;
if (adap->algo->master_xfer) {
mutex_lock_nested(&adap->bus_lock, adap->level);
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
这其中master_xfer 定义如下
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
static int s3c24xx_i2c_xfer(
struct i2c_adapter *adap, //适配器
struct i2c_msg *msgs,
int num)
{
//从这里可见,I2C数据是从适配器的algo_data中来的
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
int retry;
int ret;
for (retry = 0; retry < adap->retries; retry++) { //重复尝试的次数
ret = s3c24xx_i2c_doxfer(i2c, msgs, num); //进行I2C通讯
if (ret != -EAGAIN) //正常结束,返回
return ret;
//超时结束,等待100us再尝试
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
udelay(100);
}
return -EREMOTEIO;
}
static int s3c24xx_i2c_doxfer(
struct s3c24xx_i2c *i2c,
struct i2c_msg *msgs, //消息包
int num ) //消息包的数量
{
unsigned long timeout;
int ret;
ret = s3c24xx_i2c_set_master(i2c); //判断400ms内IIC总线是否空闲
if (ret != 0) { //连续400ms IIC总线不空闲
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; //当前I2C状态机
s3c24xx_i2c_enable_irq(i2c); //开I2C中断
s3c24xx_i2c_message_start(i2c, msgs); //发开始位
spin_unlock_irq(&i2c->lock); //恢复中断
//进程进入睡眠,等待队列唤醒,返回的是剩余时间。
//唤醒条件:
// 1、i2c->msg_num == 0
// 2、HZ * 5,等待时间达到
// 3、wake_up函数(发STOP位后调用的s3c24xx_i2c_master_complete函数中将调用wake_up)
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
ret = i2c->msg_idx; //当前操作的msg包号
if (timeout == 0) //超时
dev_dbg(i2c->dev, "timeout\n");
else if (ret != num) //当前要操作的包没有操作完
dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
//睡眠1ms(保证两次操作至少间隔1ms)
msleep(1);
out:
return ret; //返回操作了几个包
}
static void s3c24xx_i2c_message_start(
struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1; //取地址
unsigned long stat; //这个是准备设置CPU的IICSTAT寄存器的
unsigned long iiccon;
stat = 0;
stat |= S3C2410_IICSTAT_TXRXEN; //I2C的RXTX有效
if (msg->flags & I2C_M_RD) { //主机读操作
stat |= S3C2410_IICSTAT_MASTER_RX; //主机接收
addr |= 1; //地址最低位为0
} else //主机写操作
stat |= S3C2410_IICSTAT_MASTER_TX; //主机发送
//这个标记没有看到定义
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
//打开ACK硬件功能
s3c24xx_i2c_enable_ack(i2c);
//读IICCON寄存器
iiccon = readl(i2c->regs + S3C2410_IICCON);
//写IICSTAT寄存器
writel(stat, i2c->regs + S3C2410_IICSTAT);
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
//将地址作为数据写入(I2C第一字节为发送设备地址)
writeb(addr, i2c->regs + S3C2410_IICDS);
//短延迟,保证刚才的数据写入到寄存器中
//也就是设置时序的建立时间
ndelay(i2c->tx_setup);
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
writel(iiccon, i2c->regs + S3C2410_IICCON);
//这位写1,表示开始,发送IICDS中的数据
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
}
这里的i2c_transfer只是发送了i2c的起始信号,后面就交给中断来完成了,这里就联系到了i2c-s3c2440.c了。