Chinaunix首页 | 论坛 | 博客
  • 博客访问: 674276
  • 博文数量: 404
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1237
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-03 10:45
文章分类

全部博文(404)

文章存档

2017年(1)

2016年(27)

2015年(39)

2014年(55)

2013年(66)

2012年(216)

分类:

2012-09-26 13:40:31

原文地址:I2c-s3c2440.c 分析 作者:Deem_passion

此文件是I2C适配器的驱动加载文件,设备加载文件为bsp文件,如果使用的是mini2440,则为mach-mini2440.c


程序运行流程:



1、module_init(i2c_adap_s3c_init);    模块初始化函数登记。


2、驱动启动后自动执行:
    static int __init      i2c_adap_s3c_init(void)
{
       int ret;
       //平台驱动注册
       ret = platform_driver_register(&s3c2410_i2c_driver);
       if (ret == 0) {
              ret = platform_driver_register(&s3c2440_i2c_driver);
              if (ret)
                     platform_driver_unregister(&s3c2410_i2c_driver);
       }
       return ret;
}
这里使用的是平台驱动,也就是使用platform_driver_register注册一个平台driver,这个driver的结构体注册为s3c2410_i2c_driver。


3、平台驱动结构体s3c2410_i2c_driver


static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);


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,
},
};
其中包含了平台驱动用到的几个函数,probe、remove、resume,其中probe是加载driver后调用以初始化设备的,结构体中的id_table中的name是用来和device也就是适配器设备配对的,两者名字一样即配对成功,在有的版本的内核中,driver结构体中没有.id_table的赋值,与device的配对是通过driver中的name来进行配对的。


4、i2c总线初始化函数s3c24xx_i2c_probe(就是之前平台驱动结构体中定义的probe函数)


这个函数的主要目的是对I2C总线进行初始化,方法是:1、先声明一个s3c24xx_i2c结构体 然后对其初始化一些基本信息,这些信息在&s3c24xx_i2c里面,第一条语句就是干这个的,我们可以看到&s3c24xx_i2c里面就包含信息 .name = "s3c2410-i2c", 正好和之前驱动结构体里面的name吻合(这个是i2c_adper结构中的成员,也就是说一个适配器与一个平台设备相匹配)。2、将传入的平台结构体(其他程序调用驱动的时候应该会付给一个平台结构体)中的一些不用修改的信息付给i2c(也就是s3c24xx_i2c结构体),这样就构造出一个我们想要的i2c结构体,其中包含的是完整信息,然后再将i2c付给平台结构体,使用platform_set_drvdata(pdev, i2c);这条语句,这样平台结构就关联上了I2C数据。


static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
       //获取设备数据,该数据在本文件的一开始定义
       struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
       struct resource *res;
       int  ret; 
       //I2C设备关联到平台设备
       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);                           //时钟有效
       //获取平台的IO内存资源
       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, (res->end-res->start)+1,
                                    pdev->name);
       if (i2c->ioarea == NULL) {          //申请失败
              dev_err(&pdev->dev, "cannot request IO\n");
              ret = -ENXIO;
              goto err_clk;
       }
       //映射IO内存
       i2c->regs = ioremap(res->start, (res->end-res->start)+1);
       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;    //适配器的父设备为平台设备
       //初始化I2C(IICON寄存器)
       ret = s3c24xx_i2c_init(i2c);
       if (ret != 0)
              goto       err_iomap;
       //获取平台的IRQ资源
       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
       if (res == NULL) {
              dev_err(&pdev->dev, "cannot find IRQ\n");
              ret = -ENOENT;
              goto err_iomap;
       }
       //申请IRQ中断
       ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
                       pdev->name, i2c);
       if (ret != 0) {
             dev_err(&pdev->dev, "cannot claim IRQ\n");
              goto err_iomap; 
       }
       //关联上设备的IRQ数据
       i2c->irq = res;
       dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res,
              (unsigned long)res->start);
       //添加适配器
       //难怪之前分析的代码里都没有看到适配器的添加,原来是在这里添加的,由此可见,一根I2C总线上应该只有一个适配器
       ret = i2c_add_adapter(&i2c->adap);
       if (ret < 0) {
              dev_err(&pdev->dev, "failed to add bus to i2c core\n");
              goto err_irq;
       }
       //pdev->dev->driver_data = i2c
       //即平台设备关联的驱动数据为I2C数据
       platform_set_drvdata(pdev, i2c);
       dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
       return 0;
 err_irq:
       free_irq(i2c->irq->start, i2c);              //释放中断请求
//初始化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);                       //本时钟的使用者减1
 err_noclk:
       return     ret;
}
5、中断


之前的probe函数注册了中断ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED pdev->name, i2c);从中可以看到中断的处理函数为s3c24xx_i2c_irq,,所以在进行过初始化工作以后,开始等待I2C中断的发生(查看芯片手册,中断一般发生在IICDS寄存器写入完成,以及发送完成时)。


//I2C的中断函数,这个函数在执行s3c24xx_i2c_irq函数时用request_irq注册
//irqno – 请求号
//dev_id – 设备ID
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);          //读状态
       //总线仲裁位(0-成功, 1-失败)
       if (status & S3C2410_IICSTAT_ARBITR) {
              dev_err(i2c->dev, "deal with arbitration loss\n");
       }
       if (i2c->state == STATE_IDLE) {
              //当前的I2C状态为空闲状态,出错,不该进入中断
              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;
}


这里基本就是判断中断是不是可用,是不是出错。


6、真正的中断处理,也就是IIC通讯处理是i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long    iicstat),这里包含了IIC通讯的全部处理


//I2C中断,根据状态机判断下一个操作
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_IDLE\n", __FUNCTION__);
              goto       out;
              break;
       case STATE_STOP:              //停止位发出状态
              dev_err(i2c->dev, "%s: called in STATE_STOP\n", __FUNCTION__);
              s3c24xx_i2c_disable_irq(i2c);              //禁止I2C中断    
              goto       out_ack;
       case STATE_START:             //I2C开始状态
              if (iicstat  & S3C2410_IICSTAT_LASTBIT   //I2C最后收到的位是1(NOACK)
                     &&  !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {//消息包可以忽略ACK
                     dev_dbg(i2c->dev, "ack was not received\n");
                     s3c24xx_i2c_stop(i2c, -EREMOTEIO);              //发停止位
                     goto       out_ack;
              }
              //运行到这里,说明收到了AC
              //判断开始位之后的操作是读还是写?
              if (i2c->msg->flags & I2C_M_RD)
                     i2c->state = STATE_READ;                       //读状态
              else
                     i2c->state = STATE_WRITE;                      //写状态
              //当前是最后一个包 且 包的长度为0
              if (is_lastmsg(i2c) && i2c->msg->len == 0) {
                     s3c24xx_i2c_stop(i2c, 0);                      //发停止位
                     goto        out_ack;
              }
              if (i2c->state == STATE_READ)             //读操作,到“准备读”操作
                     goto       prepare_read;
       case STATE_WRITE:                   //I2C写状态
       retry_write:
              if (!is_msgend(i2c)) {           //不是msg包的最后一个字节
                     byte = i2c->msg->buf[i2c->msg_ptr++];     //读出要写的字节
                     writeb(byte, i2c->regs + S3C2410_IICDS); //写入IICDS
                     ndelay(i2c->tx_setup);   //等待建立时间
              } else if (!is_lastmsg(i2c)) {  //不是最后一个msg包
                     dev_dbg(i2c->dev, "WRITE: Next Message\n");
                     i2c->msg_ptr = 0;         //当前操作的字节为首地址
                     i2c->msg_idx ++;          //当前操作的msg包为下一个
                     i2c->msg++;                 //到下一个msg包
                     if (i2c->msg->flags & I2C_M_NOSTART) {//本msg包不需要发开始位
                            if (i2c->msg->flags & I2C_M_RD) {      //本包是读操作
                                   s3c24xx_i2c_stop(i2c, -EINVAL);  //发停止位
                            }
       goto       retry_write;                                //写操作,连续写数据
}
   else {                                                    //本msg包需要发开始位
                            s3c24xx_i2c_message_start(i2c, i2c->msg);  //发开始位
                            i2c->state = STATE_START;            //修改状态为开始
                     }
              } else {                               //最后一个msg包的最后一个字节
                     s3c24xx_i2c_stop(i2c, 0);      //发停止位
              }
              break;
       case STATE_READ:                    //I2C读操作
       //I2C读操作的最后一个字节是可以收NO ACK的,不明白的话就先看一下IIC协议
              if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)      //不能忽略ACK
                     && !(is_msglast(i2c) && is_lastmsg(i2c))) { //不是最后一个包的最后一个字节
                     if (iicstat & S3C2410_IICSTAT_LASTBIT) {      //I2C接收到的最后一位为1
                            dev_dbg(i2c->dev, "READ: No Ack\n");
                            s3c24xx_i2c_stop(i2c, -ECONNREFUSED);      //没有收到ACK,发停止位
                            goto       out_ack;


                     }
              }
              byte = readb(i2c->regs + S3C2410_IICDS);        //读取接到的字节
              i2c->msg->buf[i2c->msg_ptr++] = byte;           
       //准备读下一字节
       prepare_read:
              if (is_msglast(i2c)) {                 //准备操作msg包的最后一个字节
                     if (is_lastmsg(i2c))          //当前操作的是最后一个msg包
                            s3c24xx_i2c_disable_ack(i2c);   //禁止ACK(发NO ACK)
              } else if (is_msgend(i2c)) {       //当前msg包的最后一个字节操作结束
                     if (is_lastmsg(i2c)) {        //当前操作的是最后一个msg包
                            dev_dbg(i2c->dev, "READ: Send Stop\n");
                            s3c24xx_i2c_stop(i2c, 0);             //发停止位
                     } else {                           //当前操作的不是最后一个包
                            dev_dbg(i2c->dev, "READ: Next Transfer\n");
                            i2c->msg_ptr = 0;  //操作的数据为第一个数据
                            i2c->msg_idx++;    //下一个数据包
                            i2c->msg++;
                     }
              }
              break;
       }
//当前状态为停止位发出
//没有收到ACK
//当前发的是空包
 out_ack:
       //清除挂起条件 并 恢复操作
       tmp = readl(i2c->regs + S3C2410_IICCON);     
       tmp &= ~S3C2410_IICCON_IRQPEND;
       writel(tmp, i2c->regs + S3C2410_IICCON);
//空闲状态下进中断,直接到这里
 out:
       return     ret;
}

总结:这个驱动程序应该是最底层的驱动程序了,但是并没有注册设备,也就是只是注册了驱动程序,所以设备注册应该是在别的驱动程序里的,只有注册了驱动设备,才能在/dev/中有设备文件,上层程序才能通过打开/dev/中的设备文件来关联到驱动程序进而对具体设备进行操作。

阅读(448) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~