a. 硬件部分 在此只提供mini2440的I2C接口通信协议,S3C2440的I2C控制器主要由4个寄存器完成所有的I2C操作的,这4个寄存器是IICON、IICSTAT、IICADD、IICCDS。(请参见Mini2440手册) b. I2C总线驱动 首先我们要明白总线层驱动编写好是放在/drivers/i2c/buses目录下的。那下面让我们一起分析下I2c_s3c2410.c这个总线驱动吧。前面说过,编写I2C总线驱动层主要是填充i2c_adapter和i2c_algorithm结构体,那么可以开始了,让我们先来填充i2c_algorithm结构体吧,代码如下 static conststruct i2c_algorithms3c24xx_i2c_algorithm = { .master_xfer=s3c24xx_i2c_xfer, .functionality= s3c24xx_i2c_func, }; 我们先看i2c_algorithm中的master_xfer成员,刚才说过,s3c24xx_i2c_xfer是用来确定适配器支持的类型,用于返回总线支持的协议,具体到代码如下 static u32s3c24xx_i2c_func(structi2c_adapter *adap) { returnI2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |I2C_FUNC_PROTOCOL_MANGLING; } 好了,接下来我们把重点放在i2c_algorithm中的functionality成员上,s3c24xx_i2c_func函数用于实现I2C通信协议,将i2c_msg消息传给I2C设备。 static ints3c24xx_i2c_xfer(structi2c_adapter *adap, struct i2c_msg *msgs,int num) { struct s3c24xx_i2c *i2c = (structs3c24xx_i2c *)adap->algo_data; int retry; int ret; structs3c2410_platform_i2c *pdata= i2c->dev->platform_data; if(pdata->cfg_gpio) pdata->cfg_gpio(to_platform_device(i2c->dev)); for (retry = 0;retry retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs,num); //传输到I2C设备的具体函数 if (ret != -EAGAIN) return ret; dev_dbg(i2c->dev,"Retryingtransmission (%d)\n", retry); udelay(100); } return-EREMOTEIO; } 其实,s3c24xx_i2c_xfer主要就是调用s3c24xx_i2c_doxfer完成具体数据的传输任务。 static ints3c24xx_i2c_doxfer(structs3c24xx_i2c *i2c, struct i2c_msg *msgs,int num) { unsigned long timeout; //传输超时 intret; //返回传输的消息个数 if(i2c->suspended) //如果适配器处于挂起状态,则返回 return -EIO; 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);//启动适配器的消息传输 spin_unlock_irq(&i2c->lock); timeout=wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); //设置等待队列,直到i2c->msg_num==0为真或5ms到来才被唤醒 ret =i2c->msg_idx; if (timeout ==0) dev_dbg(i2c->dev,"timeout\n"); else if (ret !=num) dev_dbg(i2c->dev, "incompletexfer(%d)\n", ret); msleep(1); out: return ret; } 总结一下,s3c24xx_i2c_doxfer主要做了如下几件事:第一,将适配器设置为主机发送状态。第二,设置为中断传输方式。第三,发送启动信号,传输第一个字节。第四,等待超时或者其他函数在i2c->msg_num == 0时唤醒这里的等待队列。 到此为止我们会带来几个疑问:第一,s3c24xx_i2c_enable_irq和s3c24xx_i2c_message_start具体怎么实现的?第二,等待队列在何时被唤醒呢? 首先我们先来研究第一个问题,s3c24xx_i2c_enable_irq实现开中断如下 static inline voids3c24xx_i2c_enable_irq(structs3c24xx_i2c *i2c) { unsigned long tmp; tmp = readl(i2c->regs +S3C2410_IICCON); writel(tmp |S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); //将IICCON的D5位置1表示总线在接收或发送一个字节数据后会产生一个中断。 } 下面看看s3c24xx_i2c_message_start实现启动适配器的消息传输的实现吧。 static voids3c24xx_i2c_message_start(structs3c24xx_i2c *i2c, struct i2c_msg *msg) { unsigned int addr = (msg->addr& 0x7f) << 1; //取从设备地址低7位,并前移1位 unsigned long stat; //缓存IICSTAT unsigned long iiccon; //缓存IICCON stat = 0; stat |= S3C2410_IICSTAT_TXRXEN;//使能发送接收功能,为写地址到IICDS if (msg->flags & I2C_M_RD){ //如果读,则主机接收,地址位D0=1 stat |=S3C2410_IICSTAT_MASTER_RX; addr |= 1; }else //如果写,则主机发送,地址位D0=0 stat |=S3C2410_IICSTAT_MASTER_TX; if (msg->flags &I2C_M_REV_DIR_ADDR) addr ^= 1; s3c24xx_i2c_enable_ack(i2c); //使能ACK响应信号 iiccon = readl(i2c->regs +S3C2410_IICCON); writel(stat, i2c->regs +S3C2410_IICSTAT); dev_dbg(i2c->dev, "START:%08lx to IICSTAT, %02x toDS\n", stat, addr); writeb(addr, i2c->regs +S3C2410_IICDS); //写地址到IICDS寄存器 ndelay(i2c->tx_setup); dev_dbg(i2c->dev,"iiccon, %08lx\n", iiccon); writel(iiccon, i2c->regs +S3C2410_IICCON); // stat |= S3C2410_IICSTAT_START; //发送S信号,IICDS寄存器中数据自动发出 writel(stat, i2c->regs +S3C2410_IICSTAT); } 总结下我们这个3c24xx_i2c_message_start函数吧,这个函数主要做了两件事,第一使能ACK信号。第二,将从机地址和读写方式控制字写入待IICDS中,由IICSTAT发送S信号,启动发送从机地址。 嗯,到现在为止我们已经把前面提出的第一个问题解决了,该轮到解决第二个问题了,s3c24xx_i2c_doxfer中的等待队列何时被唤醒呢?其实分析到现在我们发现s3c24xx_i2c_doxfer调用3c24xx_i2c_message_start只是发送了一个从机的地址。真正的数据传输在哪里呢?其实真正的数据传输我们放在了中断处理函数中实现的。当数据准备好发送时,将产生中断,并调用事先注册的中断处理函数s3c24xx_i2c_irq进行数据传输。中断的产生其实有3种情况,第一,总线仲裁失败。第二,当总线还处于空闲状态,因为一些错误操作等原因,意外进入了中断处理函数。第三,收发完一个字节的数据,或者当收发到的I2C设备地址信息吻合。行,那我们先来看看s3c24xx_i2c_irq到底怎么来传输数据的吧。 static irqreturn_ts3c24xx_i2c_irq(intirqno, 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) { //仲裁失败下的处理 dev_err(i2c->dev, "dealwith 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; } i2s_s3c_irq_nextbyte(i2c,status); //传输或接收下一个字节 out: return IRQ_HANDLED; } 我们发现其实中断处理函数s3c24xx_i2c_irq中真正传输数据的函数是i2s_s3c_irq_nextbyte。走了这么久,其实i2s_s3c_irq_nextbyte才是真正的传输数据的核心函数,那我们赶紧来看看 i2s_s3c_irq_nextbyte吧。 static inti2s_s3c_irq_nextbyte(structs3c24xx_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: //发出停止信号P dev_err(i2c->dev,"%s:called in STATE_STOP\n", __func__); s3c24xx_i2c_disable_irq(i2c); //关中断 goto out_ack; case STATE_START: //发出开始信号S //当没有接收到ACK应答信号,说明I2C设备不存在,应停止总线工作 if (iicstat&S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags &I2C_M_IGNORE_NAK)) { dev_dbg(i2c->dev, "ackwas not received\n"); s3c24xx_i2c_stop(i2c, -ENXIO); //完成发送P信号,唤醒,关中断三个事情 goto out_ack; } if (i2c->msg->flags&I2C_M_RD) i2c->state = STATE_READ; //一个读消息 else i2c->state = STATE_WRITE; //一个写消息 // is_lastmsg()判断是当前处理的消息是否是最后一个消息,如果是返回1 if (is_lastmsg(i2c)&&i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); goto out_ack; } if (i2c->state ==STATE_READ)//如果是读那进行跳转,注此case无break! goto prepare_read; case STATE_WRITE: //当没有接收到ACK应答信号,说明I2C设备不存在,应停止总线工作 if (!(i2c->msg->flags &I2C_M_IGNORE_NAK)) { if (iicstat &S3C2410_IICSTAT_LASTBIT) { dev_dbg(i2c->dev, "WRITE:NoAck\n"); s3c24xx_i2c_stop(i2c,-ECONNREFUSED); gotoout_ack; } } retry_write: // is_msgend(0判断当前消息是否已经传输完所有字节,如果是返回1 if (!is_msgend(i2c)) { byte =i2c->msg->buf[i2c->msg_ptr++]; //读取待传输数据 writeb(byte,i2c->regs +S3C2410_IICDS); //将待传输数据写入IICDS ndelay(i2c->tx_setup); //延时50ms,等待发送到总线上 // is_lastmsg()判断是当前处理的消息是否是最后一个消息,如果是返回1 } else if (!is_lastmsg(i2c)) { //当前信息传输完,还有信息要传输情况下 dev_dbg(i2c->dev,"WRITE:Next Message\n"); i2c->msg_ptr= 0; //下一条消息字符串的首地址置0 i2c->msg_idx++; //表示已经传输完1条消息 i2c->msg++; //表示准备传输下一条消息 if(i2c->msg->flags &I2C_M_NOSTART) { //不处理此新类型消息,停止 if (i2c->msg->flags &I2C_M_RD) { s3c24xx_i2c_stop(i2c, -EINVAL); } goto retry_write; //当本消息因类型不被处理则继续查看下面是否有消息 } else { //开始传输消息,将IICDS里的数据发送到总线上 s3c24xx_i2c_message_start(i2c,i2c->msg); i2c->state= STATE_START; } } else { //当前信息传输完,没有信息要传输情况下,停止总线工作 s3c24xx_i2c_stop(i2c, 0); } break; case STATE_READ: byte = readb(i2c->regs +S3C2410_IICDS); //从IICDS读取数据 //将读取到的数据放入缓存区, msg_ptr++直到当前消息传输完毕 i2c->msg->buf[i2c->msg_ptr++]=byte; prepare_read: // is_msglast()判断如果是消息的最后一个字节,如果是返回1 if (is_msglast(i2c)) { // is_lastmsg()判断是当前处理的消息是否是最后一个消息,如果是返回1 if (is_lastmsg(i2c)) s3c24xx_i2c_disable_ack(i2c); //关闭ACK应答信号 // is_msgend(0判断当前消息是否已经传输完所有字节,如果是返回1 }else if (is_msgend(i2c)) { // is_lastmsg()判断是当前处理的消息是否是最后一个消息,如果是返回1 if(is_lastmsg(i2c)) { dev_dbg(i2c->dev, "READ:SendStop\n"); s3c24xx_i2c_stop(i2c, 0); //发P信号,唤醒等待队列 }else { //当前消息传输完毕,但还有其他消息,则将相关指针指向下一条消息 dev_dbg(i2c->dev, "READ:Next Transfer\n"); i2c->msg_ptr = 0; //下一条消息字符串的首地址置0 i2c->msg_idx++; //表示已经传输完1条消息 i2c->msg++; //表示准备传输下一条消息 } } break; } out_ack: tmp = readl(i2c->regs +S3C2410_IICCON); tmp &=~S3C2410_IICCON_IRQPEND; //清除中断,否则会重复执行中断处理函数 writel(tmp, i2c->regs +S3C2410_IICCON); out: return ret; } 我们终于把这个庞大的i2s_s3c_irq_nextbyte搞定了,在这里需要说明几点,第一,消息分为第一条消息,第二条消息,第三条消息等,总共有msg_num条消息,每发送完一个消息,会msg_idx++。每条消息发送完还需要调用s3c24xx_i2c_message_start进行发送新的起始信号S。第二,第i条消息是一个字符串,按照一个字节一个字节的形式发送,由一个指针msg_ptr指向这个字符串的待发送字节的地址。 在i2s_s3c_irq_nextbyte这个函数中,我们发现有很多s3c24xx_i2c_stop终止函数, 那么让我们来看看这个函数到底怎么终止的吧。 static inline voids3c24xx_i2c_stop(structs3c24xx_i2c *i2c, int ret) { unsigned long iicstat =readl(i2c->regs + S3C2410_IICSTAT); dev_dbg(i2c->dev,"STOP\n"); iicstat &=~S3C2410_IICSTAT_START; //发送P信号 writel(iicstat, i2c->regs +S3C2410_IICSTAT); i2c->state = STATE_STOP; //设置适配器状态为停止 s3c24xx_i2c_master_complete(i2c,ret); //唤醒传输等待队列中的进程 s3c24xx_i2c_disable_irq(i2c); //禁止中断 } 这个s3c24xx_i2c_stop函数还是很简单的,但里面调用了s3c24xx_i2c_master_complete这个函数来唤醒传输等待队列中的进程,那我们就来看看s3c24xx_i2c_master_complete啦。 static inlinevoids3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret) { dev_dbg(i2c->dev,"master_complete %d\n", ret); i2c->msg_ptr = 0; //下一条消息字符串的首地址置0 i2c->msg = NULL; //表示没有可传输的消息 i2c->msg_idx++; //表示已经传输完1条消息 i2c->msg_num = 0; //表示没有可传输的消息 if (ret) i2c->msg_idx =ret; //记录已经传输完的信息个数 wake_up(&i2c->wait); //唤醒等待队列中的进程 } 到此为止,我们已经完成了在I2C总线驱动层填充了i2c_adapter和i2c_algorithm结构体,剩下来我们需要把这两个结构体外包一下,来注册这个适配器,这怎么实现呢?当然我们在上面已经分析了中断处理函数s3c24xx_i2c_irq,那么这个函数什么时候被注册的呢?带着这两个问题我们需要继续往下走,go! 下面两个函数就完成了I2C总线层驱动模块的注册和注销。 static int __init i2c_adap_s3c_init(void) { //注册平台设备 return platform_driver_register(&s3c24xx_i2c_driver); } static void __exit i2c_adap_s3c_exit(void) { //注销平台设备 platform_driver_unregister(&s3c24xx_i2c_driver); } 那我们来看看这个平台设备吧。 static structplatform_drivers3c24xx_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, }, }; 平台驱动的这个探测函数s3c24xx_i2c_probe就完成了整个适配器的注册和中断处理函数的注册工作了。我们再来看看这个s3c24xx_i2c_probe函数: static int s3c24xx_i2c_probe(structplatform_device*pdev) { struct s3c24xx_i2c *i2c; //适配器指针 struct s3c2410_platform_i2c*pdata; //平台数据 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(structs3c24xx_i2c), GFP_KERNEL); //分配适配器空间 if (!i2c) { dev_err(&pdev->dev,"no memory for state\n"); return -ENOMEM; } //给适配器赋予名字s3c2410-i2c,这个名字会由cat /sys/class/i2c_dev/0/name看到。 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; //数据从适配器到总线的时间为50ms spin_lock_init(&i2c->lock); init_waitqueue_head(&i2c->wait);//初始化等待队列 i2c->dev = &pdev->dev; i2c->clk =clk_get(&pdev->dev,"i2c"); //获取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); //使能i2c时钟 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; } i2c->ioarea =request_mem_region(res->start, resource_size(res), //申请I/O内存 pdev->name); if (i2c->ioarea == NULL) { dev_err(&pdev->dev,"cannot request IO\n"); ret = -ENXIO; goto err_clk; } 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); i2c->adap.algo_data =i2c; //将私有数据指向适配器结构体 i2c->adap.dev.parent=&pdev->dev; //组织适配器的设备模型 ret =s3c24xx_i2c_init(i2c); //初始化I2C控制器 if (ret != 0) goto err_iomap; i2c->irq = ret =platform_get_irq(pdev, 0); //获取平台设备的中断号 if (ret <= 0) { dev_err(&pdev->dev,"cannotfind IRQ\n"); goto err_iomap; } 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; } i2c->adap.nr =pdata->bus_num; 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);//将I2C适配器设置为平台设备的私有数据 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; } 我们来回顾下这个探测函数s3c24xx_i2c_probe吧,这个函数主要干了六件事。第一,申请一个I2C适配器结构体,并对其赋值。第二,获取I2C时钟资源,并注册时钟。第三,获取资源并最终映射到物理地址。第四,申请中断处理函数。第五,初始化I2C控制器。第六,将I2C适配器添加到内核中。对于resume函数由于做的是跟探测函数相反的操作,在此就无需浪费时间了。 接下来,我们来看看上面第五步初始化I2C控制器所使用的函数s3c24xx_i2c_init。 static ints3c24xx_i2c_init(struct s3c24xx_i2c *i2c) { unsigned long iicon =S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN; struct s3c2410_platform_i2c*pdata; unsigned int freq; pdata =i2c->dev->platform_data; if (pdata->cfg_gpio) //初始化GPIO口 pdata->cfg_gpio(to_platform_device(i2c->dev)); writeb(pdata->slave_addr,i2c->regs + S3C2410_IICADD); //写入从机地址 dev_info(i2c->dev, "slaveaddress 0x%02x\n",pdata->slave_addr); writel(iicon, i2c->regs +S3C2410_IICCON); //开中断,ACK信号使能 if (s3c24xx_i2c_clockrate(i2c,&freq) != 0) { //设置时钟源和时钟频率 writel(0, i2c->regs+S3C2410_IICCON); //失败则设置为0 dev_err(i2c->dev,"cannotmeet bus frequency required\n"); return -EINVAL; } dev_info(i2c->dev, "busfrequency set to %d KHz\n",freq); dev_dbg(i2c->dev,"S3C2410_IICCON=0x%02lx\n", iicon); return 0; } 在s3c24xx_i2c_init中,我们调用s3c24xx_i2c_clockrate设置了时钟源和时钟频率,继续看下去。 static ints3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsignedint *got) { struct s3c2410_platform_i2c*pdata = i2c->dev->platform_data; unsigned long clkin =clk_get_rate(i2c->clk); //获取PLCK时钟,单位为HZ unsigned int divs, div1; unsigned long target_frequency; u32 iiccon; int freq; i2c->clkrate = clkin; clkin /= 1000; //时钟频率单位转为KHZ dev_dbg(i2c->dev, "pdatadesired frequency %lu\n", pdata->frequency); target_frequency =pdata->frequency ? pdata->frequency :100000; target_frequency /= 1000; //目标频率,单位KHZ freq =s3c24xx_i2c_calcdivisor(clkin, target_frequency, &div1,&divs); //获取分频值 if (freq > target_frequency) { dev_err(i2c->dev, "Unable to achieve desiredfrequency %luKHz." \ " Lowest achievable%dKHz\n", target_frequency, freq); return -EINVAL; } *got = freq; //将分频值写入IICCON相应位 iiccon = readl(i2c->regs +S3C2410_IICCON); iiccon &=~(S3C2410_IICCON_SCALEMASK |S3C2410_IICCON_TXDIV_512); iiccon |= (divs-1); if (div1 == 512) iiccon|=S3C2410_IICCON_TXDIV_512; writel(iiccon, i2c->regs +S3C2410_IICCON); //如果设备是2440,则执行下面代码处理 if (s3c24xx_i2c_is2440(i2c)) { unsigned long sda_delay; if (pdata->sda_delay){ sda_delay = (freq / 1000) *pdata->sda_delay; sda_delay /= 1000000; sda_delay =DIV_ROUND_UP(sda_delay, 5); if (sda_delay > 3) sda_delay = 3; sda_delay |=S3C2410_IICLC_FILTER_ON; } else sda_delay = 0; dev_dbg(i2c->dev,"IICLC=%08lx\n",sda_delay); writel(sda_delay,i2c->regs +S3C2440_IICLC); } return 0; } 在s3c24xx_i2c_clockratet中,我们调用s3c24xx_i2c_calcdivisor根据已知PCLK和目标频率,获取了两个分频系数,我们继续看下去。 static ints3c24xx_i2c_calcdivisor(unsignedlong clkin, unsigned int wanted, unsigned int *div1, unsigned int*divs) { unsigned int calc_divs = clkin /wanted; unsigned int calc_div1; if (calc_divs > (16*16)) calc_div1 =512; //IICLK=PCLK/512 else calc_div1 =16; //IICLK=PCLK/16 calc_divs += calc_div1-1; calc_divs /= calc_div1; if (calc_divs == 0) //控制分频量程范围 calc_divs = 1; if (calc_divs > 17) //控制分频量程范围 calc_divs = 17; *divs = calc_divs; //分频系数 *div1 = calc_div1; //时钟源的选择 return clkin / (calc_divs *calc_div1); } 好了,到现在为止,我们的I2C总线层驱动就已经全部搞定了,我们总结下吧!在基于mini2440的I2C总线层驱动中,我们首先加载了一个平台设备,在平台设备的探测函数中,我们主要注册了适配器和中断处理函数。适配器结构体主要是实现通信方法的函数s3c24xx_i2c_xfer,我们在这里是使用的中断方式进行通信的,这也是大多数的情况下我们的选择,当然我们也可以采用查询的方式进行编写s3c24xx_i2c_xfer函数,只需要判断是读还是写操作就可以了。I2C总线层驱动模块加载后会在sys文件系统下产生一个适配器节点,可以供I2C设备驱动层来进行探测匹配。
|