(7)LinuxI2C驱动--I2C总线驱动

来源:互联网 发布:天空表白墙源码v4.1 编辑:程序博客网 时间:2024/06/05 03:44

前面分析了i2c设备驱动如何实现通过sysfs文件系统访问eeprom,对于读写eeprom,最后都是调用了i2c_transfer(),此函数的实现在i2c核心中。

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) {     unsigned long orig_jiffies;     int ret, try;     /* REVISIT the fault reporting model here is weak:      *      *  - When we get an error after receiving N bytes from a slave,      *    there is no way to report "N".      *      *  - When we get a NAK after transmitting N bytes to a slave,      *    there is no way to report "N" ... or to let the master      *    continue executing the rest of this combined message, if      *    that's the appropriate response.      *      *  - When for example "num" is two and we successfully complete      *    the first message but get an error part way through the      *    second, it's unclear whether that should be reported as      *    one (discarding status on the second message) or errno      *    (discarding status on the first one).      */     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%s\n", ret, (msgs[ret].flags & I2C_M_RD)                 ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,                 (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");         } #endif               if (in_atomic() || irqs_disabled()) {             ret = rt_mutex_trylock(&adap->bus_lock);              if (!ret)                 /* I2C activity is ongoing. */                 return -EAGAIN;         } else {             rt_mutex_lock(&adap->bus_lock);         }            /* Retry automatically on arbitration loss */         orig_jiffies = jiffies;         for (ret = 0, try = 0; try <= adap->retries; try++) {             ret = adap->algo->master_xfer(adap, msgs, num);//i2c总线驱动的入口             if (ret != -EAGAIN)                 break;             if (time_after(jiffies, orig_jiffies + adap->timeout))                 break;         }         rt_mutex_unlock(&adap->bus_lock);         return ret;     } else {         dev_dbg(&adap->dev, "I2C level transfers not supported\n");         return -EOPNOTSUPP;     } }

可以看到,语句ret = adap->algo->master_xfer(adap, msgs, num)就是i2c总线驱动的入口,此语句是寻找i2c_adapter对应的i2c_algorithm后,使用master_xfer()驱动硬件流程来进行实际的传输。

那么i2c_adapter是在哪里绑定了i2c_algorithm呢?master_xfer()又是如何来启动i2c传输的呢?在i2c总线驱动中我们就可以找到答案。

1. 三星S5PV210 i2c适配器的硬件描述

s5pv210处理器内部集成了一个i2c控制器,通过4个主要的寄存器就可以对其进行控制。
在arch/arm/plat-samsung/include/plat/regs-iic.h中列出了这几个寄存器。

#define S3C2410_IICREG(x) (x)#define S3C2410_IICCON    S3C2410_IICREG(0x00)//i2c控制寄存器#define S3C2410_IICSTAT   S3C2410_IICREG(0x04)//i2c状态寄存器#define S3C2410_IICADD    S3C2410_IICREG(0x08)//i2c地址寄存器#define S3C2410_IICDS     S3C2410_IICREG(0x0C)//i2c收发数据移位寄存器

i2c寄存器支持收发两种模式,我们主要使用主模式,通过对IICCON、IICDS和IICADD寄存器的操作,可以在i2c总线上产生开始位,停止位,数据和地址,而传输的状态则是通过IICSTAT寄存器获取。

在三星的i2c总线说明文档中给出了i2c总线进行传输的整个流程。
这里写图片描述

以通过i2c总线写eeprom为例,具体的流程如下:

(1)设置GPIO的相关引脚为IIC输出;(2)设置IIC(打开ACK,打开IIC中断,设置CLK等);(3)设备地址赋给IICDS ,并设置IICSTAT,启动IIC发送设备地址出去;从而找到相应的设备即IIC总线上的设备。(4)第一个Byte的设备地址发送后,从EEPROM得到ACK信号,此信号触发中断;(5)在中断处理函数中把第二个Byte(设备内地址)发送出去;发送之后,接收到ACK又触发中断;(6)中断处理函数把第三个Byte(真正的数据)发送到设备中。(7)发送之后同样接收到ACK并触发中断,中断处理函数判断,发现数据传送完毕。(8)IIC Stop信号,关IIC中断,置位各寄存器。

在下面的小节中将结合代码来分析i2c总线对上面流程的具体实现。

2. i2c总线驱动的加载/卸载

i2c总线驱动被作为一个单独的模块加载,下面首先分析它的加载/卸载函数。

static int __init i2c_adap_s3c_init(void) {     return platform_driver_register(&s3c24xx_i2c_driver);//注册为平台驱动 } subsys_initcall(i2c_adap_s3c_init); static void __exit i2c_adap_s3c_exit(void) {     platform_driver_unregister(&s3c24xx_i2c_driver); } module_exit(i2c_adap_s3c_exit);

三星s5pv210的i2c总线驱动是作为平台驱动来实现的,其中传入的结构体s3c24xx_i2c_driver就是platform_driver。

 static struct platform_driver s3c24xx_i2c_driver = {     .probe      = s3c24xx_i2c_probe,     .remove     = s3c24xx_i2c_remove,     .id_table   = s3c24xx_driver_ids,     .driver     = {         .owner  = THIS_MODULE,         .name   = "s3c-i2c",         .pm = S3C24XX_DEV_PM_OPS,     }, };

3. i2c总线驱动的probe

i2c总线驱动的probe函数会在一个合适的设备被发现的时候由总线驱动调用。

/* s3c24xx_i2c_probe * * called by the bus driver when a suitable device is found*/static int s3c24xx_i2c_probe(struct platform_device *pdev){    struct s3c24xx_i2c *i2c;//封装i2c适配器的信息    struct s3c2410_platform_i2c *pdata;//i2c平台数据    struct resource *res;//平台资源    int ret;    pdata = pdev->dev.platform_data;//找到平台数据    if (!pdata) {        dev_err(&pdev->dev, "no platform data\n");        return -EINVAL;    }    i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);//为i2c适配器私有数据结构体分配内存空间,并且初始化为0    if (!i2c) {        dev_err(&pdev->dev, "no memory for state\n");        return -ENOMEM;    }    //填充i2c适配器私有数据结构体    strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));//名字    i2c->adap.owner   = THIS_MODULE;//模块拥有者    i2c->adap.algo    = &s3c24xx_i2c_algorithm;//总线通讯方法    i2c->adap.retries = 2;//重试次数    i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;    i2c->tx_setup     = 50;    spin_lock_init(&i2c->lock);//i2c适配器私有数据的锁进行初始化    init_waitqueue_head(&i2c->wait);//初始化等待队列    /* find the clock and enable it */    //找到时钟,并且使能    i2c->dev = &pdev->dev;    i2c->clk = clk_get(&pdev->dev, "i2c");//找到时钟    if (IS_ERR(i2c->clk)) {        dev_err(&pdev->dev, "cannot get clock\n");        ret = -ENOENT;        goto err_noclk;    }    dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);    clk_enable(i2c->clk);//使能    /* map the registers */    //映射寄存器    //获取平台设备资源,对于IORESOURSE_MEM类型的资源,start,end表示platform_device占据的内存的开始地址和结束地址    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    if (res == NULL) {        dev_err(&pdev->dev, "cannot find IO resource\n");        ret = -ENOENT;        goto err_clk;    }    //申请io内存资源    i2c->ioarea = request_mem_region(res->start, resource_size(res),                     pdev->name);    if (i2c->ioarea == NULL) {        dev_err(&pdev->dev, "cannot request IO\n");        ret = -ENXIO;        goto err_clk;    }    //映射io    //I/O端口空间映射到内存的虚拟地址    i2c->regs = ioremap(res->start, resource_size(res));    if (i2c->regs == NULL) {        dev_err(&pdev->dev, "cannot map IO\n");        ret = -ENXIO;        goto err_ioarea;    }    dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",        i2c->regs, i2c->ioarea, res);    /* setup info block for the i2c core */    //设置i2c核心所需数据    i2c->adap.algo_data = i2c;    i2c->adap.dev.parent = &pdev->dev;    /* initialise the i2c controller */    //i2c适配器私有数据结构提填充完了,就初始化i2c控制器    ret = s3c24xx_i2c_init(i2c);    if (ret != 0)        goto err_iomap;    /* find the IRQ for this unit (note, this relies on the init call to     * ensure no current IRQs pending     */    //找到要申请的中断号    i2c->irq = ret = platform_get_irq(pdev, 0);    if (ret <= 0) {        dev_err(&pdev->dev, "cannot find IRQ\n");        goto err_iomap;    }    //申请中断,指定了中断处理函数s3c24xx_i2c_irq    ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,              dev_name(&pdev->dev), i2c);    if (ret != 0) {        dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);        goto err_iomap;    }    //动态变频,忽略    ret = s3c24xx_i2c_register_cpufreq(i2c);    if (ret < 0) {        dev_err(&pdev->dev, "failed to register cpufreq notifier\n");        goto err_irq;    }    /* Note, previous versions of the driver used i2c_add_adapter()     * to add the bus at any number. We now pass the bus number via     * the platform data, so if unset it will now default to always     * being bus 0.     */    i2c->adap.nr = pdata->bus_num;    //添加i2c适配器(cpu内部集成)    ret = i2c_add_numbered_adapter(&i2c->adap);    if (ret < 0) {        dev_err(&pdev->dev, "failed to add bus to i2c core\n");        goto err_cpufreq;    }    platform_set_drvdata(pdev, i2c);    clk_disable(i2c->clk);    dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));    return 0; err_cpufreq:    s3c24xx_i2c_deregister_cpufreq(i2c); err_irq:    free_irq(i2c->irq, i2c); err_iomap:    iounmap(i2c->regs); err_ioarea:    release_resource(i2c->ioarea);    kfree(i2c->ioarea); err_clk:    clk_disable(i2c->clk);    clk_put(i2c->clk); err_noclk:    kfree(i2c);    return ret;}

可以看到,i2c24xx_i2c_probe()的主要工作有:使能硬件,申请i2c适配器使用的io地址、中断号,然后向i2c核心添加了这个适配器。

s3c24xx_i2c是i2c适配器的私有数据结构体,封装了适配器的所有信息。

struct s3c24xx_i2c {    spinlock_t      lock;//用于防止并发访问的锁    wait_queue_head_t   wait;//等待队列    unsigned int        suspended:1;    struct i2c_msg      *msg;//i2c消息    unsigned int        msg_num;//i2c消息的数量    unsigned int        msg_idx;//当前消息中的一个指针    unsigned int        msg_ptr;//消息索引    unsigned int        tx_setup;//等待数据发送到总线上的一个建立时间    unsigned int        irq;//中断    enum s3c24xx_i2c_state  state;//i2c状态    unsigned long       clkrate;    void __iomem        *regs;    struct clk      *clk;    struct device       *dev;    struct resource     *ioarea;    struct i2c_adapter  adap;//i2c_adapter#ifdef CONFIG_CPU_FREQ    struct notifier_block   freq_transition;#endif};

初始化i2c控制器函数s3c24xx_i2c_init()如下

 static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) {     unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;//中断使能,ACK使能     struct s3c2410_platform_i2c *pdata;     unsigned int freq;     /* get the plafrom data */     pdata = i2c->dev->platform_data;//获取平台数据     /* inititalise the gpio */     if (pdata->cfg_gpio)//初始化gpio ,流程(1)         pdata->cfg_gpio(to_platform_device(i2c->dev));     /* write slave address */     writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);//写从设备地址     dev_dbg(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);     writel(iicon, i2c->regs + S3C2410_IICCON);//写控制寄存器,也就是使能中断和使能ACK,流程(2)     /* we need to work out the divisors for the clock... */     if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {//计算时钟分频         writel(0, i2c->regs + S3C2410_IICCON);         dev_err(i2c->dev, "cannot meet bus frequency required\n");         return -EINVAL;     }     /* todo - check that the i2c lines aren't being dragged anywhere */     dev_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq);     dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);     dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);     writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);     return 0; }

s3c24xx_i2c_init()中完成了前面所说的通过i2c总线写eeprom流程的(1)(2)两步。


在浅谈LinuxI2C驱动架构这一小节中提到了,i2c总线驱动是对I2C硬件体系结构中适配器端的实现,主要是实现了两个结构i2c_adapter和i2c_algorithm,从而控制i2c适配器产生通讯信号。

在i2c24xx_i2c_probe()中就填充了i2c_adapter,并且通过i2c->adap.algo = &s3c24xx_i2c_algorithm给i2c_adapter绑定了i2c_algorithm。

其中s3c24xx_i2c_algorithm为
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
其中s3c24xx_i2c_xfer()用来启动i2c传输,s3c24xx_i2c_func()返回所支持的通讯协议。

所以说,i2c设备通过i2c_transfer()进行实际传输,在i2c核心中我们已经看到,i2c_transfer实际是调用了i2c_adapter对应的master_xfer(),此处,在i2c总线驱动中,把master_xfer()指定为了s3c24xx_i2c_xfer(),所以说此时,传输任务交给了s3c24xx_i2c_xfer()。

通过后面分析我们会看到,s3c24xx_i2c_xfer()只是启动了i2c传输,把i2c传输这个任务进行推进并且完成还需要靠我们在probe中注册的中断来完成,对应的中断处理函数是s3c24xx_i2c_irq(),后面都会详细分析。

3. 启动i2c传输

接下来就是分析负责启动i2c传输任务的s3c24xx_i2c_xfer()。

 static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,             struct i2c_msg *msgs, int num) {     struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;//获得i2c适配器私有数据结构     int retry;     int ret;     clk_enable(i2c->clk);//使能时钟     for (retry = 0; retry < adap->retries; retry++) {//传输不成功,则重试,retries为重试次数。         ret = s3c24xx_i2c_doxfer(i2c, msgs, num);//启动一次i2c传输         if (ret != -EAGAIN)             goto out;         dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);         udelay(100);     }     ret = -EREMOTEIO; out:     clk_disable(i2c->clk);     return ret; }

可以看到s3c24xx_i2c_xfer()是调用了s3c24xx_i2c_doxfer()来启动传输的。

static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,                   struct i2c_msg *msgs, int num) {     unsigned long timeout;     int ret;     if (i2c->suspended)         return -EIO;     ret = s3c24xx_i2c_set_master(i2c);//检查i2c总线状态,总线不忙返回0     if (ret != 0) {         dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);         ret = -EAGAIN;         goto out;     }     spin_lock_irq(&i2c->lock);     //把消息写入i2c适配器的私有数据结构体中     i2c->msg     = msgs;//i2c消息     i2c->msg_num = num;//消息数量     i2c->msg_ptr = 0;//消息指针,指向当前消息未发送部分的开始     i2c->msg_idx = 0;//消息索引     i2c->state   = STATE_START;//将状态改为STATE_START     s3c24xx_i2c_enable_irq(i2c);//使能中断     s3c24xx_i2c_message_start(i2c, msgs);//发送第一个byte,获得ACK后触发中断。     spin_unlock_irq(&i2c->lock);     timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);//等待消息传输完成,否则超时

s3c24xx_i2c_doxfer()首先调用s3c24xx_i2c_set_master()来检查总线状态,s3c24xx_i2c_set_master()的实现如下

 static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) {     unsigned long iicstat;     int timeout = 400;     while (timeout-- > 0) {         iicstat = readl(i2c->regs + S3C2410_IICSTAT);//读i2c状态寄存器         if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))//总线不忙,则返回0;否则直到超时             return 0;         msleep(1);     }     writel(iicstat & ~S3C2410_IICSTAT_TXRXEN, i2c->regs + S3C2410_IICSTAT);     if (!(readl(i2c->regs + S3C2410_IICSTAT) & S3C2410_IICSTAT_BUSBUSY))         return 0;     return -ETIMEDOUT; }

在获知总线不忙后,把要消息写入i2c适配器私有数据结构,并且把状态改为STATE_START。
然后使能中断,通过s3c24xx_i2c_message_start()发送第一个byte,这样在获取ACK后就会触发中断来推进i2c的传输。

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,                       struct i2c_msg *msg) {     unsigned int addr = (msg->addr & 0x7f) << 1;//从设备地址,7位地址,最低位用来表示读或者写,1为读,0为写。     unsigned long stat;     unsigned long iiccon;     stat = 0;     stat |=  S3C2410_IICSTAT_TXRXEN;//使能RxTx     if (msg->flags & I2C_M_RD) {//从i2c消息判断,如果是读         stat |= S3C2410_IICSTAT_MASTER_RX;//把状态设为主模式读         addr |= 1;//别且设置第一byte最低位为1,表示读     } else//否则是写         stat |= S3C2410_IICSTAT_MASTER_TX;//把状态设为主模式写     if (msg->flags & I2C_M_REV_DIR_ADDR)//如果是读写反转         addr ^= 1;//读写交换     /* todo - check for wether ack wanted or not */     s3c24xx_i2c_enable_ack(i2c);//使能ACK     iiccon = readl(i2c->regs + S3C2410_IICCON);     writel(stat, i2c->regs + S3C2410_IICSTAT);//根据前面的设置来配置控制寄存器,流程(3)     dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);     writeb(addr, i2c->regs + S3C2410_IICDS);//把第一个byte写入i2c收发数据移位寄存器,流程(3)     /* delay here to ensure the data byte has gotten onto the bus      * before the transaction is started */     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);//修改状态,流程(3) }

s3c24xx_i2c_message_start()在i2c总线上发送了一个开始信号,即完成了通过i2c总线写eeprom中的流程(3)的工作,设备地址赋给IICDS ,并设置IICSTAT,启动IIC发送设备地址出去,当从设备收到此数据并且回复ACK后,i2c适配器收到ACK后就会触发中断来推进i2c的传输。

4. 通过中断来推进i2c的传输

发送完第一个byte,收到ACK信号后就会进入中断,并且以后只要收到ACK信号就都会进入中断。中断在probe中已经注册,它的实现
如下

static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id) {     struct s3c24xx_i2c *i2c = dev_id;     unsigned long status;     unsigned long tmp;     status = readl(i2c->regs + S3C2410_IICSTAT);//获得i2c状态寄存器的值     if (status & S3C2410_IICSTAT_ARBITR) {//需要仲裁         /* deal with arbitration loss */         dev_err(i2c->dev, "deal with arbitration loss\n");     }     if (i2c->state == STATE_IDLE) {//空闲状态         dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");         tmp = readl(i2c->regs + S3C2410_IICCON);         tmp &= ~S3C2410_IICCON_IRQPEND;         writel(tmp, i2c->regs +  S3C2410_IICCON);         goto out;     }     /* pretty much this leaves us with the fact that we've      * transmitted or received whatever byte we last sent */     i2c_s3c_irq_nextbyte(i2c, status);//推进传输,传输下一个byte  out:     return IRQ_HANDLED; }

i2c总线驱动的中断处理函数s3c24xx_i2c_irq()是调用i2c_s3c_irq_nextbyte()来推进i2c的传输的。

static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat){    unsigned long tmp;    unsigned char byte;    int ret = 0;    switch (i2c->state) {//根据i2c的状态选择    case STATE_IDLE://空闲        dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);        goto out;        break;    case STATE_STOP://停止        dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);        s3c24xx_i2c_disable_irq(i2c);//禁止中断        goto out_ack;    case STATE_START://开始        /* last thing we did was send a start condition on the         * bus, or started a new i2c message         */        //切换为开始状态之前,刚发送了第一个byte,也就是设备地址        //首先检查下state时候与硬件寄存器的状态一致        if (iicstat & S3C2410_IICSTAT_LASTBIT &&            !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {            /* ack was not received... */            dev_dbg(i2c->dev, "ack was not received\n");            s3c24xx_i2c_stop(i2c, -ENXIO);//停止i2c传输            goto out_ack;        }        if (i2c->msg->flags & I2C_M_RD)//如果当前i2c消息的标志为i2c读            i2c->state = STATE_READ;//则修改状态为i2c读        else            i2c->state = STATE_WRITE;//否则修改为i2c写        /* terminate the transfer if there is nothing to do         * as this is used by the i2c probe to find devices. */        if (is_lastmsg(i2c) && i2c->msg->len == 0) {//如果是最后一条消息则停止i2c传输。            s3c24xx_i2c_stop(i2c, 0);            goto out_ack;        }        if (i2c->state == STATE_READ)//如果i2c状态为读,就跳到读,否则,,就会跳到写,,,因为没有break            goto prepare_read;        /* fall through to the write state, as we will need to         * send a byte as well */    case STATE_WRITE://第一次开始写i2c        /* we are writing data to the device... check for the         * end of the message, and if so, work out what to do         */        if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {            if (iicstat & S3C2410_IICSTAT_LASTBIT) {                dev_dbg(i2c->dev, "WRITE: No Ack\n");                s3c24xx_i2c_stop(i2c, -ECONNREFUSED);                goto out_ack;            }        } retry_write://继续写        if (!is_msgend(i2c)) {//不是一条消息的最后1Byte            byte = i2c->msg->buf[i2c->msg_ptr++];//取出此消息的下一个byte            writeb(byte, i2c->regs + S3C2410_IICDS);//写入收发数据移位寄存器。            /* delay after writing the byte to allow the             * data setup time on the bus, as writing the             * data to the register causes the first bit             * to appear on SDA, and SCL will change as             * soon as the interrupt is acknowledged */            ndelay(i2c->tx_setup);//延迟,等待数据发送        } else if (!is_lastmsg(i2c)) {//是一条消息的最后一个byte,不是最后一条消息            /* we need to go to the next i2c message */            dev_dbg(i2c->dev, "WRITE: Next Message\n");            i2c->msg_ptr = 0;//当前消息未发数据开始指针复位            i2c->msg_idx++;//消息索引++            i2c->msg++;//下一条消息            /* check to see if we need to do another message */            if (i2c->msg->flags & I2C_M_NOSTART) {//在发送下个消息之前,检查是否需要一个新的开始信号,如果不需要                if (i2c->msg->flags & I2C_M_RD) {//如果是读                    /* cannot do this, the controller                     * forces us to send a new START                     * when we change direction */                    s3c24xx_i2c_stop(i2c, -EINVAL);//错误,返回                }                goto retry_write;//继续写            } else {//如果需要一个新的开始信号                /* send the new start */                s3c24xx_i2c_message_start(i2c, i2c->msg);//发送一个新的开始信号                i2c->state = STATE_START;//并且修改状态            }        } else {//是一条消息的最后            /* send stop */            s3c24xx_i2c_stop(i2c, 0);//停止发送        }        break;    case STATE_READ://开始读        /* we have a byte of data in the data register, do         * something with it, and then work out wether we are         * going to do any more read/write         */        byte = readb(i2c->regs + S3C2410_IICDS);//先获取读到的消息,后面再决定时候有用        i2c->msg->buf[i2c->msg_ptr++] = byte;//把消息存入读缓冲 prepare_read://如果第一个byte是读,则跳到此处。        if (is_msglast(i2c)) {//是当前消息的最后一byte,也就是当前消息只剩1Byte的空余            /* last byte of buffer */            if (is_lastmsg(i2c))//如果也是最后一条消息                s3c24xx_i2c_disable_ack(i2c);//那么就禁止ACK        } else if (is_msgend(i2c)) {//否则如果是当前消息已经用完读缓冲            /* ok, we've read the entire buffer, see if there             * is anything else we need to do */            if (is_lastmsg(i2c)) {//如果是最后一条消息了                /* last message, send stop and complete */                dev_dbg(i2c->dev, "READ: Send Stop\n");                s3c24xx_i2c_stop(i2c, 0);//停止i2c传输            } else {//否则进入下一条i2c传输                /* go to the next transfer */                dev_dbg(i2c->dev, "READ: Next Transfer\n");                i2c->msg_ptr = 0;                i2c->msg_idx++;                i2c->msg++;//下一条i2c消息            }        }        break;    }    /* acknowlegde the IRQ and get back on with the work */ out_ack:    tmp = readl(i2c->regs + S3C2410_IICCON);    tmp &= ~S3C2410_IICCON_IRQPEND;//清中断标志位    writel(tmp, i2c->regs + S3C2410_IICCON); out:    return ret;}

i2c_s3c_irq_nextbyte()推进了i2c的传输,以写eeprom为例,第一个Byte的设备地址发送后,从EEPROM得到ACK信号,此信号触发中断,在中断处理函数中把第二个Byte(设备内地址)发送出去;发送之后,接收到ACK又触发中断,中断处理函数把第三个Byte(真正的数据)发送到设备中,发送之后同样接收到ACK并触发中断,中断处理函数判断,发现数据传送完毕,就发送IIC Stop信号,关IIC中断,置位各寄存器。这样就把通过i2c总线写eeprom的整个流程都实现了。

5. 总结

i2c总线驱动控制i2c适配器产生通信信号,通过master_xfer()启动一个i2c传输,然后通过中断推进i2c传输。

0 0