(7)LinuxI2C驱动--I2C总线驱动
来源:互联网 发布:天空表白墙源码v4.1 编辑:程序博客网 时间:2024/06/05 03:44
前面分析了i2c设备驱动如何实现通过sysfs文件系统访问eeprom,对于读写eeprom,最后都是调用了i2c_transfer(),此函数的实现在i2c核心中。
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { unsigned long orig_jiffies; int ret, try; /* REVISIT the fault reporting model here is weak: * * - When we get an error after receiving N bytes from a slave, * there is no way to report "N". * * - When we get a NAK after transmitting N bytes to a slave, * there is no way to report "N" ... or to let the master * continue executing the rest of this combined message, if * that's the appropriate response. * * - When for example "num" is two and we successfully complete * the first message but get an error part way through the * second, it's unclear whether that should be reported as * one (discarding status on the second message) or errno * (discarding status on the first one). */ if (adap->algo->master_xfer) { #ifdef DEBUG for (ret = 0; ret < num; ret++) { dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD) ? 'R' : 'W', msgs[ret].addr, msgs[ret].len, (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : ""); } #endif if (in_atomic() || irqs_disabled()) { ret = rt_mutex_trylock(&adap->bus_lock); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; } else { rt_mutex_lock(&adap->bus_lock); } /* 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);//i2c总线驱动的入口 if (ret != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adap->timeout)) break; } rt_mutex_unlock(&adap->bus_lock); return ret; } else { dev_dbg(&adap->dev, "I2C level transfers not supported\n"); return -EOPNOTSUPP; } }
可以看到,语句ret = adap->algo->master_xfer(adap, msgs, num)就是i2c总线驱动的入口,此语句是寻找i2c_adapter对应的i2c_algorithm后,使用master_xfer()驱动硬件流程来进行实际的传输。
那么i2c_adapter是在哪里绑定了i2c_algorithm呢?master_xfer()又是如何来启动i2c传输的呢?在i2c总线驱动中我们就可以找到答案。
1. 三星S5PV210 i2c适配器的硬件描述
s5pv210处理器内部集成了一个i2c控制器,通过4个主要的寄存器就可以对其进行控制。
在arch/arm/plat-samsung/include/plat/regs-iic.h中列出了这几个寄存器。
#define S3C2410_IICREG(x) (x)#define S3C2410_IICCON S3C2410_IICREG(0x00)//i2c控制寄存器#define S3C2410_IICSTAT S3C2410_IICREG(0x04)//i2c状态寄存器#define S3C2410_IICADD S3C2410_IICREG(0x08)//i2c地址寄存器#define S3C2410_IICDS S3C2410_IICREG(0x0C)//i2c收发数据移位寄存器
i2c寄存器支持收发两种模式,我们主要使用主模式,通过对IICCON、IICDS和IICADD寄存器的操作,可以在i2c总线上产生开始位,停止位,数据和地址,而传输的状态则是通过IICSTAT寄存器获取。
在三星的i2c总线说明文档中给出了i2c总线进行传输的整个流程。
以通过i2c总线写eeprom为例,具体的流程如下:
(1)设置GPIO的相关引脚为IIC输出;(2)设置IIC(打开ACK,打开IIC中断,设置CLK等);(3)设备地址赋给IICDS ,并设置IICSTAT,启动IIC发送设备地址出去;从而找到相应的设备即IIC总线上的设备。(4)第一个Byte的设备地址发送后,从EEPROM得到ACK信号,此信号触发中断;(5)在中断处理函数中把第二个Byte(设备内地址)发送出去;发送之后,接收到ACK又触发中断;(6)中断处理函数把第三个Byte(真正的数据)发送到设备中。(7)发送之后同样接收到ACK并触发中断,中断处理函数判断,发现数据传送完毕。(8)IIC Stop信号,关IIC中断,置位各寄存器。
在下面的小节中将结合代码来分析i2c总线对上面流程的具体实现。
2. i2c总线驱动的加载/卸载
i2c总线驱动被作为一个单独的模块加载,下面首先分析它的加载/卸载函数。
static int __init i2c_adap_s3c_init(void) { return platform_driver_register(&s3c24xx_i2c_driver);//注册为平台驱动 } subsys_initcall(i2c_adap_s3c_init); static void __exit i2c_adap_s3c_exit(void) { platform_driver_unregister(&s3c24xx_i2c_driver); } module_exit(i2c_adap_s3c_exit);
三星s5pv210的i2c总线驱动是作为平台驱动来实现的,其中传入的结构体s3c24xx_i2c_driver就是platform_driver。
static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, }, };
3. i2c总线驱动的probe
i2c总线驱动的probe函数会在一个合适的设备被发现的时候由总线驱动调用。
/* s3c24xx_i2c_probe * * called by the bus driver when a suitable device is found*/static int s3c24xx_i2c_probe(struct platform_device *pdev){ struct s3c24xx_i2c *i2c;//封装i2c适配器的信息 struct s3c2410_platform_i2c *pdata;//i2c平台数据 struct resource *res;//平台资源 int ret; pdata = pdev->dev.platform_data;//找到平台数据 if (!pdata) { dev_err(&pdev->dev, "no platform data\n"); return -EINVAL; } i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);//为i2c适配器私有数据结构体分配内存空间,并且初始化为0 if (!i2c) { dev_err(&pdev->dev, "no memory for state\n"); return -ENOMEM; } //填充i2c适配器私有数据结构体 strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));//名字 i2c->adap.owner = THIS_MODULE;//模块拥有者 i2c->adap.algo = &s3c24xx_i2c_algorithm;//总线通讯方法 i2c->adap.retries = 2;//重试次数 i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup = 50; spin_lock_init(&i2c->lock);//i2c适配器私有数据的锁进行初始化 init_waitqueue_head(&i2c->wait);//初始化等待队列 /* find the clock and enable it */ //找到时钟,并且使能 i2c->dev = &pdev->dev; i2c->clk = clk_get(&pdev->dev, "i2c");//找到时钟 if (IS_ERR(i2c->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); ret = -ENOENT; goto err_noclk; } dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk); clk_enable(i2c->clk);//使能 /* map the registers */ //映射寄存器 //获取平台设备资源,对于IORESOURSE_MEM类型的资源,start,end表示platform_device占据的内存的开始地址和结束地址 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { dev_err(&pdev->dev, "cannot find IO resource\n"); ret = -ENOENT; goto err_clk; } //申请io内存资源 i2c->ioarea = request_mem_region(res->start, resource_size(res), pdev->name); if (i2c->ioarea == NULL) { dev_err(&pdev->dev, "cannot request IO\n"); ret = -ENXIO; goto err_clk; } //映射io //I/O端口空间映射到内存的虚拟地址 i2c->regs = ioremap(res->start, resource_size(res)); if (i2c->regs == NULL) { dev_err(&pdev->dev, "cannot map IO\n"); ret = -ENXIO; goto err_ioarea; } dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res); /* setup info block for the i2c core */ //设置i2c核心所需数据 i2c->adap.algo_data = i2c; i2c->adap.dev.parent = &pdev->dev; /* initialise the i2c controller */ //i2c适配器私有数据结构提填充完了,就初始化i2c控制器 ret = s3c24xx_i2c_init(i2c); if (ret != 0) goto err_iomap; /* find the IRQ for this unit (note, this relies on the init call to * ensure no current IRQs pending */ //找到要申请的中断号 i2c->irq = ret = platform_get_irq(pdev, 0); if (ret <= 0) { dev_err(&pdev->dev, "cannot find IRQ\n"); goto err_iomap; } //申请中断,指定了中断处理函数s3c24xx_i2c_irq ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c); if (ret != 0) { dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); goto err_iomap; } //动态变频,忽略 ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); goto err_irq; } /* Note, previous versions of the driver used i2c_add_adapter() * to add the bus at any number. We now pass the bus number via * the platform data, so if unset it will now default to always * being bus 0. */ i2c->adap.nr = pdata->bus_num; //添加i2c适配器(cpu内部集成) ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { dev_err(&pdev->dev, "failed to add bus to i2c core\n"); goto err_cpufreq; } platform_set_drvdata(pdev, i2c); clk_disable(i2c->clk); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); return 0; err_cpufreq: s3c24xx_i2c_deregister_cpufreq(i2c); err_irq: free_irq(i2c->irq, i2c); err_iomap: iounmap(i2c->regs); err_ioarea: release_resource(i2c->ioarea); kfree(i2c->ioarea); err_clk: clk_disable(i2c->clk); clk_put(i2c->clk); err_noclk: kfree(i2c); return ret;}
可以看到,i2c24xx_i2c_probe()的主要工作有:使能硬件,申请i2c适配器使用的io地址、中断号,然后向i2c核心添加了这个适配器。
s3c24xx_i2c是i2c适配器的私有数据结构体,封装了适配器的所有信息。
struct s3c24xx_i2c { spinlock_t lock;//用于防止并发访问的锁 wait_queue_head_t wait;//等待队列 unsigned int suspended:1; struct i2c_msg *msg;//i2c消息 unsigned int msg_num;//i2c消息的数量 unsigned int msg_idx;//当前消息中的一个指针 unsigned int msg_ptr;//消息索引 unsigned int tx_setup;//等待数据发送到总线上的一个建立时间 unsigned int irq;//中断 enum s3c24xx_i2c_state state;//i2c状态 unsigned long clkrate; void __iomem *regs; struct clk *clk; struct device *dev; struct resource *ioarea; struct i2c_adapter adap;//i2c_adapter#ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition;#endif};
初始化i2c控制器函数s3c24xx_i2c_init()如下
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) { unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;//中断使能,ACK使能 struct s3c2410_platform_i2c *pdata; unsigned int freq; /* get the plafrom data */ pdata = i2c->dev->platform_data;//获取平台数据 /* inititalise the gpio */ if (pdata->cfg_gpio)//初始化gpio ,流程(1) pdata->cfg_gpio(to_platform_device(i2c->dev)); /* write slave address */ writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);//写从设备地址 dev_dbg(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); writel(iicon, i2c->regs + S3C2410_IICCON);//写控制寄存器,也就是使能中断和使能ACK,流程(2) /* we need to work out the divisors for the clock... */ if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {//计算时钟分频 writel(0, i2c->regs + S3C2410_IICCON); dev_err(i2c->dev, "cannot meet bus frequency required\n"); return -EINVAL; } /* todo - check that the i2c lines aren't being dragged anywhere */ dev_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq); dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay); writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC); return 0; }
s3c24xx_i2c_init()中完成了前面所说的通过i2c总线写eeprom流程的(1)(2)两步。
在浅谈LinuxI2C驱动架构这一小节中提到了,i2c总线驱动是对I2C硬件体系结构中适配器端的实现,主要是实现了两个结构i2c_adapter和i2c_algorithm,从而控制i2c适配器产生通讯信号。
在i2c24xx_i2c_probe()中就填充了i2c_adapter,并且通过i2c->adap.algo = &s3c24xx_i2c_algorithm给i2c_adapter绑定了i2c_algorithm。
其中s3c24xx_i2c_algorithm为
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
其中s3c24xx_i2c_xfer()用来启动i2c传输,s3c24xx_i2c_func()返回所支持的通讯协议。
所以说,i2c设备通过i2c_transfer()进行实际传输,在i2c核心中我们已经看到,i2c_transfer实际是调用了i2c_adapter对应的master_xfer(),此处,在i2c总线驱动中,把master_xfer()指定为了s3c24xx_i2c_xfer(),所以说此时,传输任务交给了s3c24xx_i2c_xfer()。
通过后面分析我们会看到,s3c24xx_i2c_xfer()只是启动了i2c传输,把i2c传输这个任务进行推进并且完成还需要靠我们在probe中注册的中断来完成,对应的中断处理函数是s3c24xx_i2c_irq(),后面都会详细分析。
3. 启动i2c传输
接下来就是分析负责启动i2c传输任务的s3c24xx_i2c_xfer()。
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;//获得i2c适配器私有数据结构 int retry; int ret; clk_enable(i2c->clk);//使能时钟 for (retry = 0; retry < adap->retries; retry++) {//传输不成功,则重试,retries为重试次数。 ret = s3c24xx_i2c_doxfer(i2c, msgs, num);//启动一次i2c传输 if (ret != -EAGAIN) goto out; dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry); udelay(100); } ret = -EREMOTEIO; out: clk_disable(i2c->clk); return ret; }
可以看到s3c24xx_i2c_xfer()是调用了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);//检查i2c总线状态,总线不忙返回0 if (ret != 0) { dev_err(i2c->dev, "cannot get bus (error %d)\n", ret); ret = -EAGAIN; goto out; } spin_lock_irq(&i2c->lock); //把消息写入i2c适配器的私有数据结构体中 i2c->msg = msgs;//i2c消息 i2c->msg_num = num;//消息数量 i2c->msg_ptr = 0;//消息指针,指向当前消息未发送部分的开始 i2c->msg_idx = 0;//消息索引 i2c->state = STATE_START;//将状态改为STATE_START s3c24xx_i2c_enable_irq(i2c);//使能中断 s3c24xx_i2c_message_start(i2c, msgs);//发送第一个byte,获得ACK后触发中断。 spin_unlock_irq(&i2c->lock); timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);//等待消息传输完成,否则超时
s3c24xx_i2c_doxfer()首先调用s3c24xx_i2c_set_master()来检查总线状态,s3c24xx_i2c_set_master()的实现如下
static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c) { unsigned long iicstat; int timeout = 400; while (timeout-- > 0) { iicstat = readl(i2c->regs + S3C2410_IICSTAT);//读i2c状态寄存器 if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))//总线不忙,则返回0;否则直到超时 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; }
在获知总线不忙后,把要消息写入i2c适配器私有数据结构,并且把状态改为STATE_START。
然后使能中断,通过s3c24xx_i2c_message_start()发送第一个byte,这样在获取ACK后就会触发中断来推进i2c的传输。
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg) { unsigned int addr = (msg->addr & 0x7f) << 1;//从设备地址,7位地址,最低位用来表示读或者写,1为读,0为写。 unsigned long stat; unsigned long iiccon; stat = 0; stat |= S3C2410_IICSTAT_TXRXEN;//使能RxTx if (msg->flags & I2C_M_RD) {//从i2c消息判断,如果是读 stat |= S3C2410_IICSTAT_MASTER_RX;//把状态设为主模式读 addr |= 1;//别且设置第一byte最低位为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);//使能ACK iiccon = readl(i2c->regs + S3C2410_IICCON); writel(stat, i2c->regs + S3C2410_IICSTAT);//根据前面的设置来配置控制寄存器,流程(3) dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr); writeb(addr, i2c->regs + S3C2410_IICDS);//把第一个byte写入i2c收发数据移位寄存器,流程(3) /* 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);//修改状态,流程(3) }
s3c24xx_i2c_message_start()在i2c总线上发送了一个开始信号,即完成了通过i2c总线写eeprom中的流程(3)的工作,设备地址赋给IICDS ,并设置IICSTAT,启动IIC发送设备地址出去,当从设备收到此数据并且回复ACK后,i2c适配器收到ACK后就会触发中断来推进i2c的传输。
4. 通过中断来推进i2c的传输
发送完第一个byte,收到ACK信号后就会进入中断,并且以后只要收到ACK信号就都会进入中断。中断在probe中已经注册,它的实现
如下
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);//获得i2c状态寄存器的值 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"); 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);//推进传输,传输下一个byte out: return IRQ_HANDLED; }
i2c总线驱动的中断处理函数s3c24xx_i2c_irq()是调用i2c_s3c_irq_nextbyte()来推进i2c的传输的。
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) {//根据i2c的状态选择 case STATE_IDLE://空闲 dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__); goto out; break; 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://开始 /* last thing we did was send a start condition on the * bus, or started a new i2c message */ //切换为开始状态之前,刚发送了第一个byte,也就是设备地址 //首先检查下state时候与硬件寄存器的状态一致 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);//停止i2c传输 goto out_ack; } if (i2c->msg->flags & I2C_M_RD)//如果当前i2c消息的标志为i2c读 i2c->state = STATE_READ;//则修改状态为i2c读 else i2c->state = STATE_WRITE;//否则修改为i2c写 /* 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) {//如果是最后一条消息则停止i2c传输。 s3c24xx_i2c_stop(i2c, 0); goto out_ack; } if (i2c->state == STATE_READ)//如果i2c状态为读,就跳到读,否则,,就会跳到写,,,因为没有break goto prepare_read; /* fall through to the write state, as we will need to * send a byte as well */ case STATE_WRITE://第一次开始写i2c /* 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)) {//不是一条消息的最后1Byte byte = i2c->msg->buf[i2c->msg_ptr++];//取出此消息的下一个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)) {//是一条消息的最后一个byte,不是最后一条消息 /* 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++;//下一条消息 /* 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);//先获取读到的消息,后面再决定时候有用 i2c->msg->buf[i2c->msg_ptr++] = byte;//把消息存入读缓冲 prepare_read://如果第一个byte是读,则跳到此处。 if (is_msglast(i2c)) {//是当前消息的最后一byte,也就是当前消息只剩1Byte的空余 /* 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);//停止i2c传输 } else {//否则进入下一条i2c传输 /* go to the next transfer */ dev_dbg(i2c->dev, "READ: Next Transfer\n"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++;//下一条i2c消息 } } 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;}
i2c_s3c_irq_nextbyte()推进了i2c的传输,以写eeprom为例,第一个Byte的设备地址发送后,从EEPROM得到ACK信号,此信号触发中断,在中断处理函数中把第二个Byte(设备内地址)发送出去;发送之后,接收到ACK又触发中断,中断处理函数把第三个Byte(真正的数据)发送到设备中,发送之后同样接收到ACK并触发中断,中断处理函数判断,发现数据传送完毕,就发送IIC Stop信号,关IIC中断,置位各寄存器。这样就把通过i2c总线写eeprom的整个流程都实现了。
5. 总结
i2c总线驱动控制i2c适配器产生通信信号,通过master_xfer()启动一个i2c传输,然后通过中断推进i2c传输。
- (7)LinuxI2C驱动--I2C总线驱动
- (2)LinuxI2C驱动--I2C总线
- (6)LinuxI2C驱动--I2C设备驱动
- LinuxI2C总线驱动深入分析
- LinuxI2C总线驱动大致框架
- LinuxI2C总线驱动深入分析
- (5)LinuxI2C驱动--浅谈LinuxI2C驱动架构
- (1)LinuxI2C驱动--概述
- i2c适配器驱动源码分析(i2c总线驱动)
- I2c总线驱动
- I2C总线驱动
- Linux I2C 总线驱动
- linux i2c总线驱动
- I2C总线驱动代码
- I2C总线驱动
- kernel I2C总线驱动
- 浅析I2C总线驱动
- 7-i2C总线_mpu6050驱动编程
- 剑指offer 12 -打印1到最大的n位数
- 《C++Primer》读书笔记--命名空间
- java 框架基础知识(2)----动态代理-->Spring AOP
- 【牛腩】界面整合(36-61讲)
- ERROR 2002 (HY000)
- (7)LinuxI2C驱动--I2C总线驱动
- redis集群报错:clusterdown the cluster is down
- 如何选择域名和主机
- NSTimer 计时器的创建
- poj 3764 字典树求异或最大值
- 深入理解java垃圾回收机制----
- Android 双缓冲技术
- 一起talk C栗子吧(第十七回:C语言实例--栈二)
- 对onreadystatechange属性的理解