接着分析i2c的数据传输流程。
首先是打开i2c设备,比如open("/dev/i2c/0"),在内核调用下面函数
static int i2cdev_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int ret = 0;
lock_kernel();
i2c_dev = i2c_dev_get_by_minor(minor);
if (!i2c_dev) {
ret = -ENODEV;
goto out;
}
adap = i2c_get_adapter(i2c_dev->adap->nr);
if (!adap) {
ret = -ENODEV;
goto out;
}
/* This creates an anonymous i2c_client, which may later be
* pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
*
* This client is ** NEVER REGISTERED ** with the driver model
* or I2C core code!! It just holds private copies of addressing
* information and maybe a PEC flag.
*/
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
ret = -ENOMEM;
goto out;
}
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
client->driver = &i2cdev_driver;
client->adapter = adap;
file->private_data = client;
out:
unlock_kernel();
return ret;
}
这里就是根据设备的minor号,生成了i2c_client结构体。以后在操作文件的时候,就可以把对i2c_client进行操作了。在i2c_client中有device, 也有device_driver。因此,i2c_client是更高层次的抽象。
先看设备的写函数:
static ssize_t i2cdev_write (struct file *file, const char __user *buf, size_t count,
loff_t *offset)
{
int ret;
char *tmp;
struct i2c_client *client = (struct i2c_client *)file->private_data;
if (count > 8192)
count = 8192;
tmp = kmalloc(count,GFP_KERNEL);
if (tmp==NULL)
return -ENOMEM;
if (copy_from_user(tmp,buf,count)) {
kfree(tmp);
return -EFAULT;
}
pr_debug("i2c-dev: i2c-%d writing %zu bytes.n",
iminor(file->f_path.dentry->d_inode), count);
ret = i2c_master_send(client,tmp,count);
kfree(tmp);
return ret;
}
在这里调用i2c_master_send函数。
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = client->flags & I2C_M_TEN;
msg.len = count;
msg.buf = (char *)buf;
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
这样又调用了i2c_transfer函数。不论读写,最后都是通过这个函数进行操作的。
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
unsigned long orig_jiffies;
int ret, try;
if (adap->algo->master_xfer) {
if (in_atomic() || irqs_disabled()) {
ret = mutex_trylock(&adap->bus_lock);
if (!ret)
/* I2C activity is ongoing. */
return -EAGAIN;
} else {
mutex_lock_nested(&adap->bus_lock, adap->level);
}
/* Retry automatically on arbitration loss */
orig_jiffies = jiffies;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
if (time_after(jiffies, orig_jiffies + adap->timeout))
break;
}
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supportedn");
return -EOPNOTSUPP;
}
}
利用adap->algo->master_xfer进行传输。这个函数是在我们自己的文件中实现的。
最终调用了s3c24xx_i2c_doxfer函数。在这个函数中,只是传输i2c协议中的第一个byte。每传输完一个byte之后,会产生一个中断,其余的数据都是在中断函数中进行传输的。具体的关于i2c的内容,可以在arm的芯片手册和eeprom的芯片手册中看到。
static int i2s_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_IDLEn", __func__);
goto out;
break;
case STATE_STOP:
dev_err(i2c->dev, "%s: called in STATE_STOPn", __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
*/
if (iicstat & S3C2410_IICSTAT_LASTBIT &&
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
/* ack was not received... */
dev_dbg(i2c->dev, "ack was not receivedn");
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 Ackn");
s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
goto out_ack;
}
}
retry_write:
if (!is_msgend(i2c)) {
byte = i2c->msg->buf[i2c->msg_ptr++];
printk( "%02x ", byte);
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 Messagen");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->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;
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);
printk( "%02x ", byte);
i2c->msg->buf[i2c->msg_ptr++] = byte;
prepare_read:
if (is_msglast(i2c)) {
/* last byte of buffer */
if (is_lastmsg(i2c))
s3c24xx_i2c_disable_ack(i2c);
/* NO ACK means the ernd of read. Is it possible here wusq */
} 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 Stopn");
s3c24xx_i2c_stop(i2c, 0);
} else {
/* go to the next transfer */
dev_dbg(i2c->dev, "READ: Next Transfern");
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;
}
/* 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;
status = readl(i2c->regs + S3C2410_IICSTAT);
if (status & S3C2410_IICSTAT_ARBITR) {
/* deal with arbitration loss */
dev_err(i2c->dev, "deal with arbitration lossn");
}
if (i2c->state == STATE_IDLE) {
dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLEn");
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 */
i2s_s3c_irq_nextbyte(i2c, status);
out:
return IRQ_HANDLED;
}
上一篇:Linux I2C驱动详解
下一篇:MINI2440i2c驱动学习一
推荐阅读最新更新时间:2024-11-11 16:49
设计资源 培训 开发板 精华推荐
- TB62737FUG 升压 DC-DC 转换器用于 4 个白光 LED 驱动器的典型应用
- LM2931AD33R 具有抑制功能的超低压降稳压器的典型应用电路
- DC2874A-B,使用 LTC3310S 5V、30A 同步降压 Silent Switcher 2 的演示板
- 基于TLV493DA1B6HTSA2的3D磁性传感模块
- AN1144 - 基于AP3428 1A降压DC-DC转换器的应用电路
- ADK-8400,用于具有 ±800V 隔离度的 HI-8400 隔离式分立传感器的评估板
- OP184FSZ-REEL 电阻与输入串联的典型应用将过压电流限制在安全值
- LTC3811 的典型应用 - 高速双通道、多相降压型 DC/DC 控制器
- 具有输出排序功能的 LT1940L 3.3V 和 5V 双路输出降压转换器的典型应用电路
- 使用 MikroElektronika 的 ESP-WROOM-32 的参考设计