从零开始之驱动发开、linux驱动(五十一、linux下的IIC总线的通讯方法[5])

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/88927357

我们需要为特定的I2C适配器实现通信方法, 主要是实现i2c_algorithm的functionality() 函数和master_xfer() 函数。
functionality() 函数非常简单, 用于返回algorithm所支持的通信协议。

linux中定义了一下方法


/* To determine what functionality is present */

#define I2C_FUNC_I2C			0x00000001
#define I2C_FUNC_10BIT_ADDR		0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_NOSTART etc. */
#define I2C_FUNC_SMBUS_PEC		0x00000008
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK		0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */

#define I2C_FUNC_SMBUS_BYTE		(I2C_FUNC_SMBUS_READ_BYTE | \
					 I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA	(I2C_FUNC_SMBUS_READ_BYTE_DATA | \
					 I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA	(I2C_FUNC_SMBUS_READ_WORD_DATA | \
					 I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA	(I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
					 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK	(I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
					 I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

#define I2C_FUNC_SMBUS_EMUL		(I2C_FUNC_SMBUS_QUICK | \
					 I2C_FUNC_SMBUS_BYTE | \
					 I2C_FUNC_SMBUS_BYTE_DATA | \
					 I2C_FUNC_SMBUS_WORD_DATA | \
					 I2C_FUNC_SMBUS_PROC_CALL | \
					 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
					 I2C_FUNC_SMBUS_I2C_BLOCK | \
					 I2C_FUNC_SMBUS_PEC)

master_xfer() 函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息,
 

/* i2c bus registration info */

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
	.master_xfer		= s3c24xx_i2c_xfer,
	.functionality		= s3c24xx_i2c_func,
};

可以看一下三星支持的通讯协议。

/* declare our i2c functionality */
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}

iic中,消息的发送只是一个开始引导,具体详细的发送步骤都在中断函数中处理。


/* s3c24xx_i2c_xfer
 *
 * first port of call from the i2c bus code when an message needs
 * transferring across the i2c bus.
*/

static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
			struct i2c_msg *msgs, int num)
{
    /* 拿到具体的那个i2c控制器 */
	struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
	int retry;
	int ret;

	clk_enable(i2c->clk);    /* 开i2c时钟 */

    /* retries表示重新发送次数,前面初始化为2的,
     * 这里是如果num个msg最多可以发生两次 */
	for (retry = 0; retry < adap->retries; retry++) {

        /* 发送num个msgs */
		ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

		if (ret != -EAGAIN)
			goto out;

		dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

		udelay(200);
	}
	ret = -EREMOTEIO;
out:
	clk_disable(i2c->clk);    /* 关i2c时钟 */

	return ret;
}

/* s3c24xx_i2c_doxfer
 *
 * this starts an i2c transfer
*/

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;

    /* 发生前要先等待iic控制器处于空闲状态 */
	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);

    /* 设置要发送的消息 */
	i2c->msg     = msgs;
	i2c->msg_num = num;
	i2c->msg_ptr = 0;
	i2c->msg_idx = 0;
	i2c->state   = STATE_START;    /* 初始化时是起始状态 */

	s3c24xx_i2c_enable_irq(i2c);    /* 数据发生完成后,会有发送完成中断,数据发送或接受前中断必须打开 */
	s3c24xx_i2c_message_start(i2c, msgs);    /* 填写数据到i2c数据寄存器,并启动发送 */
	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;
}

等待空闲很简单,查看寄存器,检查对应位就可以了

static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
{
	unsigned long iicstat;
	int timeout = 400;

    /* 这里查看状态寄存器,保证发生前是空闲状态,如果是忙状态,这里最多等400ms */
	while (timeout-- > 0) {
		iicstat = readl(i2c->regs + S3C2410_IICSTAT);

		if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))
			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;
}

/* s3c24xx_i2c_message_start
 *
 * put the start of a message onto the bus
*/

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;
		addr |= 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);    /* 开启时钟 */

    /* 获取控制寄存器值 */
	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);
    /* 设置要发送的从机地址和数据方向 */
	writeb(addr, i2c->regs + S3C2410_IICDS);

	/* 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);
}

这里我们可以看到,s3c24xx_i2c_message_start函数功能很简单,设置从机地址和数据方向,启动传输。

当然地址发生出去后,从机会有一个ACK信号。i2c主机接收到这个ack信号之后,就会触发中断处理函数,接下来的读写,停止之类就都在中断处理函数中执行了。

开始中断处理函数分析之前,我们看一下三星的这个i2c定义了一些发生状态,有空闲状态,有开始发送状态,有读数据状态,有写数据状态,有发生完成的停止状态。

enum s3c24xx_i2c_state {
	STATE_IDLE,
	STATE_START,
	STATE_READ,
	STATE_WRITE,
	STATE_STOP
};

接下来就看真正的中断处理函数。


/* s3c24xx_i2c_irq
 *
 * top level IRQ servicing routine
*/

static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
	struct s3c24xx_i2c *i2c = dev_id;
	unsigned long status;
	unsigned long tmp;

    /* 获取iic的状态寄存器 */
	status = readl(i2c->regs + S3C2410_IICSTAT);

    /* 仲裁失败的特殊情况 */
	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");

        /* 这里把iic控制器寄存器给清掉 */
		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;
}

在开始之前我们说一下iic的数据发送和接收。

假设我们在设备地址为0x02的设备中,要在0x03地址写一个字节数据,要写的数据为0x04。

正常顺序应该是这样的。

  • 主机填写设备地址0x02,并设置方向为写,启动发送。
  • 接下来从机接收的设备地址,返回ACK。
  • 主机收到ACK,产生中断。
  • 主机在中断处理函数中先判断是否收到应答信号,如果收到则填写0x03到数据寄存器,并清除中断挂起。
  • 接下来从机收到0x03的地址信息,返回一个ACK信号。
  • 主机接收到ACK信号,产生中断。
  • 主机在中断处理函数中先判断是否收到应答信号,如果收到则填写0x04到数据寄存器,并清除中断挂起。
  • 接下来从机收到0x04的数据信息,返回一个ACK信号。
  • 主机接收到ACK信号,产生中断。
  • 接下来整个写过程已经完成,主机发送一个停止信号,表示此次通信结束。

当然对于读操作,会相对麻烦一些。

因为读操作之前,要先设置设备内要读的寄存器(存储器)地址,之后再启动读操作。

中断函数的处理过程,是一个状态机,一步跟着一步。


/* is_lastmsg()
 *
 * returns TRUE if the current message is the last in the set
*/
/* 判断是不是最后一个消息 */
static inline int is_lastmsg(struct s3c24xx_i2c *i2c)
{
	return i2c->msg_idx >= (i2c->msg_num - 1);
}

/* is_msglast
 *
 * returns TRUE if we this is the last byte in the current message
*/
/* 判断是不是当前消息的最后一个字节 */
static inline int is_msglast(struct s3c24xx_i2c *i2c)
{
	return i2c->msg_ptr == i2c->msg->len-1;
}

/* is_msgend
 *
 * returns TRUE if we reached the end of the current message
*/
/* 目前处于当前消息的结束位置 */
static inline int is_msgend(struct s3c24xx_i2c *i2c)
{
	return i2c->msg_ptr >= i2c->msg->len;
}

/* i2c_s3c_irq_nextbyte
 *
 * process an interrupt and work out what to do
 */

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

	case STATE_STOP:
        /* 发生完停止信号,会关闭这个iic中断 */
		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
		 */

        /* 正常第一次进来,是xfer函数发送完地址,正常,从机是有应答信号要返回的
         * 这里判断,接收到应答才继续执行,否则直接发生停止信号
         */
		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);
			goto out_ack;
		}
        
        /* 对于读操作,设置下一步状态是读,对于写操作,设置下一步状态是写 */
		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. */

        /* 判断是不是有数据,没数据就停止 */
		if (is_lastmsg(i2c) && i2c->msg->len == 0) {
			s3c24xx_i2c_stop(i2c, 0);
			goto out_ack;
		}
        /* 对于读操作,这里就跳转到读 */
		if (i2c->state == STATE_READ)
			goto prepare_read;

		/* fall through to the write state, as we will need to
		 * send a byte as well */
        /* 对于写,直接继续执行就可以 */

	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)) {
            /* 当前msg还有数据,则把数据写进数据寄存器 */
			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)) {     /* 是否还有下一个msg要发送 */
			/* we need to go to the next i2c message */
            /* 上一个msg发送完了,取出新的msg */
			dev_dbg(i2c->dev, "WRITE: Next Message\n");

			i2c->msg_ptr = 0;
			i2c->msg_idx++;    //发生的msg数量
			i2c->msg++;

			/* check to see if we need to do another message */
            /* 这里要检查设备是不是设置了I2C_M_NOSTART标志,这个标志表示,对于同一个设备
             * 要发送不同的msg,要不要重新发生起始信号,发生设备地址
             */
			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 */
                    /* 读操作前就是一个写操作,肯定不用重新发生start之类,读操作要是有这条的话,肯定是有问题的,表明参数无效 */
					s3c24xx_i2c_stop(i2c, -EINVAL);
				} else {
                    /* 这里要发送停止信号,是对这里隐含的else里面 */
                }

				goto retry_write;
			} else {
				/* send the new start */
                /* 有些msg是设置设备的不同寄存器,所以要重新发起一次传输的 */
				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
		 */
        /* 对于读数据,通常是设置为两个msg,一个是写包,用来告诉从机要读的地址
         * 用第二个包来进行真正的读操作,而第二个msg,第一次进来并没数据
         */
		byte = readb(i2c->regs + S3C2410_IICDS);
		i2c->msg->buf[i2c->msg_ptr++] = byte;

 prepare_read:

        /* 对于正常的读过程,下面的几个if或者else if都是进不去的 */

		if (is_msglast(i2c)) {
			/* last byte of buffer */
                /* 对于最后一个读的数据,主机是可以不发送ack信号给从机的 */
			if (is_lastmsg(i2c))
				s3c24xx_i2c_disable_ack(i2c);

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

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/88927357