版权声明:本文为博主原创文章,未经博主允许不得转载。 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);
}