? ? ?
flags可以控制数据、协议格式等 len代表消息产股的
buf是指向所传输数据的指针
下面介绍AM3359 I2C适配器的传输机制:
static int omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct omap_i2c_dev *dev = i2c_get_adapdata(adap); int i; int r; r = pm_runtime_get_sync(dev->dev); if (IS_ERR_VALUE(r)) goto out; r = omap_i2c_wait_for_bb(dev); //通过读取寄存器I2C_IRQSTATUS的12位BB查询总线状态,等待总线空闲 if (r < 0) goto out; if (dev->set_mpu_wkup_lat != NULL) dev->set_mpu_wkup_lat(dev->dev, dev->latency); for (i = 0; i < num; i++) { r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1))); //传输消息,最后一条消息接STOP位 if (r != 0) break; } if (r == 0) r = num; omap_i2c_wait_for_bb(dev); out: pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); return r; } omap_i2c_xfer_msg比较长,让我们慢慢分析:
static int omap_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop) { struct omap_i2c_dev *dev = i2c_get_adapdata(adap); unsigned long timeout; u16 w; dev_dbg(dev->dev, \ msg->addr, msg->len, msg->flags, stop); if (msg->len == 0) //无效长度检测 return -EINVAL; dev->receiver = !!(msg->flags & I2C_M_RD); //判断是否为读取数据,若是则为receiver模式 omap_i2c_resize_fifo(dev, msg->len, dev->receiver); //根据所需发送/接收数据调整并清空对应FIFO,操作I2C_BUF寄存器0x94 //14位,清除接收FIFO,13~8位设置接收FIFO大小,最大64字节 //6位,清除发送FIFO,0~5位设置发送FIFO大小,最大64字节 omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr); //写入从地址 /* REVISIT: Could the STB bit of I2C_CON be used with probing? */ dev->buf = msg->buf; //组装消息 dev->buf_len = msg->len; /* make sure writes to dev->buf_len are ordered */ barrier(); omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len); //写入消息数量 /* Clear the FIFO Buffers */ w = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG); w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR; omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, w); //依然是清除FIFO,在omap_i2c_resize_fifo中只清除了RX/TX之一,由dev->receiver决定 INIT_COMPLETION(dev->cmd_complete); //初始化等待量,是为中断处理线程准备的 dev->cmd_err = 0; //清空错误码 w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; //使能I2C适配器,并设置master模式,产生开始位。即S-A-D /* S开始位,A从地址,D数据,P停止位。在I2C适配器发送数据时的序列为: * S-A-D-(n)-P * 而即便是I2C适配器从从设备中读取数据,其协议头也是一样的,之后后续发生改变: * S-A-D-S-A-D-P 关于读写方向,一包含在A中。所以无论是读还是写,第一个S-A-D都会有的。 */ /* High speed configuration */ if (dev->speed > 400) w |= OMAP_I2C_CON_OPMODE_HS; if (msg->flags & I2C_M_STOP) stop = 1; if (msg->flags & I2C_M_TEN) //10bit从地址扩展 w |= OMAP_I2C_CON_XA; if (!(msg->flags & I2C_M_RD)) w |= OMAP_I2C_CON_TRX; //设置是发送、接收模式 if (!dev->b_hw && stop) //在传输最后生成一个STOP位,若flags设置了I2C_M_STOP则每一个消息后都要跟一个STOP位(真的有这样的从设备需求) w |= OMAP_I2C_CON_STP; omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); //通过设置I2C_CON寄存器初始化一次传输,此处后进入中断程序 /* * Don't write stt and stp together on some hardware. */ if (dev->b_hw && stop) { unsigned long delay = jiffies + OMAP_I2C_TIMEOUT; u16 con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); while (con & OMAP_I2C_CON_STT) { con = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); /* Let the user know if i2c is in a bad state */ if (time_after(jiffies, delay)) { dev_err(dev->dev, \ \ return -ETIMEDOUT; } cpu_relax(); } w |= OMAP_I2C_CON_STP; w &= ~OMAP_I2C_CON_STT; omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); //写停止位 } /* * REVISIT: We should abort the transfer on signals, but the bus goes * into arbitration and we're currently unable to recover from it. */ timeout = wait_for_completion_timeout(&dev->cmd_complete, OMAP_I2C_TIMEOUT); //等待中断处理完成 if (timeout == 0) { dev_err(dev->dev, \ omap_i2c_reset(dev); __omap_i2c_init(dev); return -ETIMEDOUT; } if (likely(!dev->cmd_err)) //下边是一些错误处理,错误码会在中断处理中出错的时候配置上 return 0; /* We have an error */ if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_XUDF)) { omap_i2c_reset(dev); __omap_i2c_init(dev); return -EIO; } if (dev->cmd_err & OMAP_I2C_STAT_NACK) { if (msg->flags & I2C_M_IGNORE_NAK) return 0; if (stop) { w = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); w |= OMAP_I2C_CON_STP; omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); } return -EREMOTEIO; } return -EIO; } 可见,这里只是对消息的发送、接收做了前期的初始化以及扫尾工作,关键在于中断如何处理:
static irqreturn_t omap_i2c_isr(int irq, void *dev_id) { struct omap_i2c_dev *dev = dev_id; irqreturn_t ret = IRQ_HANDLED; u16 mask; u16 stat; spin_lock(&dev->lock); mask = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); if (stat & mask) //检验中断是否有效,若有效则开启中断线程 ret = IRQ_WAKE_THREAD; spin_unlock(&dev->lock); return ret; } 接下来进入I2C适配器的中断处理线程:
static irqreturn_t omap_i2c_isr_thread(int this_irq, void *dev_id) { struct omap_i2c_dev *dev = dev_id; unsigned long flags; u16 bits; u16 stat; int err = 0, count = 0; spin_lock_irqsave(&dev->lock, flags); do { bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG); stat &= bits; //IRQ status和使能寄存器基本是一一对应的(除部分保留位) /* If we're in receiver mode, ignore XDR/XRDY */ //根据不同模式自动忽略对应寄存器 if (dev->receiver) stat &= ~(OMAP_I2C_STAT_XDR | OMAP_I2C_STAT_XRDY); else stat &= ~(OMAP_I2C_STAT_RDR | OMAP_I2C_STAT_RRDY); if (!stat) { /* my work here is done */ goto out;