s5pv210 i2c總線驅動s3c2410.c 完全解析2

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_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* 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 mode
		addr |= 1;
	} else
		stat |= S3C2410_IICSTAT_MASTER_TX; //  Master transmit mode

	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);

	dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); //000000d0
	writeb(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); //000000aa
	writel(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;
		else
			i2c->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); // 喚醒主程序
    
    
    
}

最後呢,中斷程序會喚醒應用程序,從而實現整個數據包的傳輸。









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章