s5pv210 i2c总线驱动s3c2410.c 完全解析2

来源:互联网 发布:数据保密协议范本 编辑:程序博客网 时间:2024/05/17 15:59

3.  数据传输函数s3c24xx_i2c_xfer:

static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)  // msgs 是 设备层比如at24cxx , tp 等设备发出读写数据传进来的{struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;int retry;int ret;clk_enable(i2c->clk);for (retry = 0; retry < adap->retries; retry++) {ret = s3c24xx_i2c_doxfer(i2c, msgs, num); // 调用这个函数if (ret != -EAGAIN) {clk_disable(i2c->clk);return ret;}dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);udelay(100);}clk_disable(i2c->clk);return -EREMOTEIO;} 
这个函数呢只是做了个中转的作用,最终目的还是调用了s3c24xx_i2c_doxfer这个函数,这里呢要理解struct i2c_msg *msgs这个参数,它是设备传进来的,我们需要把这个msg(或者叫包)接受或者传输出去。因此我们有必要看看这个结构体的内容。

struct i2c_msg {__u16 addr;/* slave address*/   // iic设备地址__u16 flags;    // 读写标志#define I2C_M_TEN0x0010/* this is a ten bit chip address */#define I2C_M_RD0x0001/* read data, from slave to master */#define I2C_M_NOSTART0x4000/* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_REV_DIR_ADDR0x2000/* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_IGNORE_NAK0x1000/* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_NO_RD_ACK0x0800/* if I2C_FUNC_PROTOCOL_MANGLING */#define I2C_M_RECV_LEN0x0400/* length will be first received byte */__u16 len;/* msg length*/  // 包里面数据长度__u8 *buf;/* pointer to msg data*/  // buf 接收时作为目的  发送时作为源}; 
传输三要素, 源 ,目的,长度,都在这个包里。 因此下面看看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); // 这里设置传输使能if (ret != 0) {dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);ret = -EAGAIN;goto out;}spin_lock_irq(&i2c->lock);/* 应用程序会调用算法函数,并且会发来几个msg,驱动需要将这些msg读进来 或者发出去*/           /* 初始化 i2c->msg */          i2c->msg = msgs;          i2c->msg_num = num;           i2c->msg_ptr = 0; // msg 中第几个数据          i2c->msg_idx = 0; // 第几个msg          i2c->state = STATE_START; // 状态          s3c24xx_i2c_enable_irq(i2c); // 使能irq          s3c24xx_i2c_message_start(i2c, msgs); // iic 发送开始信号          spin_unlock_irq(&i2c->lock);          timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); // 休眠          ret = i2c->msg_idx;/* having these next two as dev_err() makes life very * noisy when doing an i2cdetect */          if (timeout == 0)dev_dbg(i2c->dev, "timeout\n");          else if (ret != num)          dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);/* ensure the stop has been through the bus */          udelay(10); out:          return ret;}

函数一开始会对记录下这些包的数据,并且设置传输状态为start , 然后发出IIC start信号 (这标志着正式进入IIC传输那一套)。看看IIC start 是如何发出的

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,      struct i2c_msg *msg){unsigned int addr = (msg->addr & 0x7f) << 1; // 获得地址unsigned long stat;unsigned long iiccon;stat = 0;stat |=  S3C2410_IICSTAT_TXRXEN;if (msg->flags & I2C_M_RD) {stat |= S3C2410_IICSTAT_MASTER_RX; // Master receive modeaddr |= 1;} elsestat |= S3C2410_IICSTAT_MASTER_TX; //  Master transmit modeif (msg->flags & I2C_M_REV_DIR_ADDR)addr ^= 1;/* todo - check for wether ack wanted or not */s3c24xx_i2c_enable_ack(i2c); // 再次使能ackiiccon = 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); //000000d0writeb(addr, i2c->regs + S3C2410_IICDS); // 写入地址 slaveaddr/* 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); //000000aawritel(iiccon, i2c->regs + S3C2410_IICCON);stat |= S3C2410_IICSTAT_START; // START signal generation.writel(stat, i2c->regs + S3C2410_IICSTAT);}
发出start信号只要设置一些寄存器就行了,这边写的有点啰嗦了,根据s5pv210的流程图呢就是设置iicstat 寄存器 和 iicds 寄存器就可以了。



s3c24xx_i2c_message_start结束后,从iic时序图上来看就是这个情况,从机地址已经写入设备了,而此时设备接收到数据后给一个ack,并且系统此时会产生一个中断。其实呢,只要iic主机发送或者接收任何一个数据都会产生一个中断,而此时呢主程序执行wait_event_timeout就会休眠,等待中断程序处理完这些包之后才会唤醒,此时就把主动权交给中断处理函数了。





4 中断处理函数s3c24xx_i2c_irq

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);        /* 做一些判断 */if (status & S3C2410_IICSTAT_ARBITR) {   // 0 = Bus arbitration successful/* 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); // 下个字节的传输 out:return IRQ_HANDLED;}

中断处理函数s3c24xx_i2c_irq一开始呢会判断iic信号是否正确,接着就会调用i2c_s3c_irq_nextbyte。 前面说道对于iic传输,只要发送或者接收任何一个字节就会发生一次中断,因此i2c_s3c_irq_nextbyte函数就是处理下一中断,循环往复,直到所有字节处理完成,下面详细看看函数i2c_s3c_irq_nextbyte:

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) {case STATE_IDLE:dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);goto out;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:  // 一开始肯定为start 状态/* last thing we did was send a start condition on the * bus, or started a new i2c message */                /* 检查有没有ack 信号 */if (iicstat & S3C2410_IICSTAT_LASTBIT &&    // 0 = Last-received bit is 0 (ACK was received).     !(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);goto out_ack;}                /* 根据标志位设置此时iic通信状态  */if (i2c->msg->flags & I2C_M_RD)i2c->state = STATE_READ;elsei2c->state = STATE_WRITE;/* terminate the transfer if there is nothing to do * as this is used by the i2c probe to find devices. */               /* just probe the device */if (is_lastmsg(i2c) && i2c->msg->len == 0) {s3c24xx_i2c_stop(i2c, 0);goto out_ack;}


函数一开始会对不同状态做相应处理, iic 在发完start 信号和 设备地址后,此时状态必定为start ,因此会执行 start 分支,此时呢,设备会发送一个ack信号,表示回应。 如果没有ack信号,则说明iic传输失败。在收到ack信号后,此时开始通过数据包的标志为改变状态,可能是读状态,可能是写状态,当下一次再次传输完成新的一个字节的数据时就会根据新的状态进入新的分支。另外需要一提的是if (is_lastmsg(i2c) && i2c->msg->len == 0)这个判断语句并没有真的收发数据,而是发送设备地址查看是否有无此设备,当设备回应ack之后就结束了。如果你学过iic设备创建client的话,也许会知道这个函数i2c_new_probed_device, 有兴趣的朋友可以去看看这函i2c_new_device的区别。
此时等待下一个字节发送或接收,并进入下一次中断,假设此时是写数据。

case STATE_WRITE:/* 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)) { // 如果这个消息中还有数据,那么就把该数据写入iic 总线byte = i2c->msg->buf[i2c->msg_ptr++];   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)) { // 不是最后一个消息/* 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++;  // 下个 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;

当状态为写的时候,一开始会做些判断,此时大致可分为三种情况: 1. 有数据, 把数据发给iic的iicds寄存器,实现传输。 2. 没有数据了,但是此时还有包没发完,就重新更新下发送的包的状态,并重新发出start信号,准备实现对下一个包的处理。 3 没有数据也没有包了,那么就代表数据发送完成,此时发送stop信号就ok了。为了形象的表示这是数据传输情况,送上一幅图。





  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: // 第一次start时 发完地址后并没有数据        if (is_msglast(i2c)) { // 这个包的最后一个字节            /* 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);            } else { //还有包 更新下                /* go to the next transfer */                dev_dbg(i2c->dev, "READ: Next Transfer\n");                i2c->msg_ptr = 0;                i2c->msg_idx++;                i2c->msg++;            }        }        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;

当状态为读的时候,会把iicds里的数据保存到buf中去,由于第一次start信号只是写了一个地址,此时还iicds中并没有数据可读,所以就跳过。 读的情况下大致可以分为三种情况:1 最后一个包,最后一个包中的最后一字节,此时不发ack2. 最后一个包,且 最后最后一字节数据处理完成,此时停止传输   3 .最后一数据完成,此时还有包,重新更新下包的状态,发出start信号,进行下个包的传输。 还是看图更形象:最后看下stop信号的产生,以及对应的函数:

当然最后中断函数结束末尾要清下中断位。


最后看下stop信号的产生,以及对应的函数:

static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret){unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);  dev_dbg(i2c->dev, "STOP\n");/* stop the transfer */iicstat &= ~S3C2410_IICSTAT_START;writel(iicstat, i2c->regs + S3C2410_IICSTAT);  // 停止传输i2c->state = STATE_STOP;s3c24xx_i2c_master_complete(i2c, ret);   // 更新包的状态,并唤醒主程序。s3c24xx_i2c_disable_irq(i2c); // 禁止irq}/* helper functions to determine the current state in the set of * messages we are sending *//* is_lastmsg() * * returns TRUE if the current message is the last in the set*/static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret){    dev_dbg(i2c->dev, "master_complete %d\n", ret);    i2c->msg_ptr = 0;    i2c->msg = NULL;    i2c->msg_idx++;    i2c->msg_num = 0;    if (ret)        i2c->msg_idx = ret;    wake_up(&i2c->wait); // 唤醒主程序            }

最后呢,中断程序会唤醒应用程序,从而实现整个数据包的传输。









0 0