I2c-dev.c 参考代码 TP驱动分析(五)

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了。

你可能感兴趣的:(I2c-dev.c 参考代码 TP驱动分析(五))