OFN鼠标驱动(六) -- drivers\i2c\busses\i2c-s3c2410.c的分析

 

之前我们分析的都是I2C的一些结构问题,现在我们来看看I2C的具体物理实现。

drivers\i2c\busses里面有很多文件,每一个文件都对应着一个平台的I2C物理实现函数,因为我们的平台是S3C2440,所以我们只需要看i2c_s3c2410.c文件就可以了,其他的文件的实现方法应该都是类似的。

 

分析完这个文件的代码之后,我们对I2C驱动的设备模型理解又更深了一层。之前我们有个错误的认识,认为一条I2C总线上可以有多个适配器,其实一条I2C总线上只能有一个适配器,而之前一直苦寻不到的适配器的添加操作,则是在这个文件中实现的,也就是在I2C总线探测的时候添加该总线的适配器,同时关联上的algo算法,这样,就又解决了前面留下的两个问题

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//I2C的状态

enum      s3c24xx_i2c_state {

       STATE_IDLE,                     //空闲

       STATE_START,            //开始位

       STATE_READ,             //读

       STATE_WRITE,           //写

       STATE_STOP               //停止位

};

 

//定义I2C的一个结构体

struct s3c24xx_i2c {

       spinlock_t                     lock;                     //自旋锁

       wait_queue_head_t        wait;                     //等待队列

 

       struct i2c_msg               *msg;             //消息包(I2C内核代码中经常看到这个结构)

       unsigned int                  msg_num;      //消息包数量

       unsigned int                  msg_idx;        //当前操作的是第几个包

       unsigned int                  msg_ptr;         //当前操作的msg包中的字节地址

 

       unsigned int                  tx_setup;        //数据发送前的建立时间

 

       enum s3c24xx_i2c_state        state;       //I2C状态

 

       void __iomem               *regs;             //ARM 寄存器指针(首地址)

       struct clk                      *clk;

       struct device                 *dev;

       struct resource               *irq;

       struct resource               *ioarea;

       struct i2c_adapter          adap;              //适配器

};

 

//设置一个默认的I2C平台数据结构

static struct    s3c2410_platform_i2c          s3c24xx_i2c_default_platform = {

       .flags                    = 0,

       .slave_addr            = 0x10,                 //控制器从机地址

       .bus_freq               = 100*1000,          //标准的总线频率

       .max_freq              = 400*1000,          //最大的总线频率(400K)

       .sda_delay             = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,

       //SDA线上的过滤操作,去干扰用

};

 

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//return true is this is an s3c2440

//这个注解已经说明了这个函数的作用了

static inline int      s3c24xx_i2c_is2440(struct s3c24xx_i2c      *i2c)

{

       struct platform_device   *pdev = to_platform_device(i2c->dev);

       return     !strcmp(pdev->name, "s3c2440-i2c");

}

 

//获取平台的I2C结构体,如果dev有挂载就返回dev挂载的数据,否则就返回默认的数据

static inline struct s3c2410_platform_i2c     *s3c24xx_i2c_get_platformdata(struct device *dev)

{

       if (dev->platform_data != NULL)

              return     (struct s3c2410_platform_i2c *)dev->platform_data;

 

       return     &s3c24xx_i2c_default_platform;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//发送STOP位后调用的函数

static inline void    s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c,     int ret)

{

       dev_dbg(i2c->dev, "master_complete %d\n", ret);

 

       i2c->msg_ptr = 0;                //数据指针

       i2c->msg = NULL;               //msg包

       i2c->msg_idx ++;                 //当前操作的包的索引

       i2c->msg_num = 0;                     //msg包的数量

       if (ret)

              i2c->msg_idx = ret;             

 

       wake_up(&i2c->wait);          //唤醒等待队列

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//不使用ACK

//实现方法是将IICCON寄存器的bit7写0

static inline void    s3c24xx_i2c_disable_ack(struct s3c24xx_i2c      *i2c)

{

       unsigned long        tmp;

      

       tmp = readl(i2c->regs + S3C2410_IICCON);             //读寄存器IICCON

       writel(tmp & ~S3C2410_IICCON_ACKEN,      i2c->regs + S3C2410_IICCON);

}

 

//使用ACK

//实现方法是将IICCON寄存器的bit7写1

static inline void    s3c24xx_i2c_enable_ack(struct s3c24xx_i2c       *i2c)

{

       unsigned long        tmp;

      

       tmp = readl(i2c->regs + S3C2410_IICCON);

       writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//IICCON.5负责控制RX/TX是否产生中断

static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)

{

       unsigned long tmp;

      

       tmp = readl(i2c->regs + S3C2410_IICCON);

       writel(tmp & ~S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);

}

 

static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)

{

       unsigned long tmp;

      

       tmp = readl(i2c->regs + S3C2410_IICCON);

       writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//发送MSG包的第一个字节(设备地址)

static void      s3c24xx_i2c_message_start(

       struct s3c24xx_i2c        *i2c,                                 

       struct i2c_msg              *msg)

{

       unsigned int addr = (msg->addr & 0x7f) << 1;    //取地址

       unsigned long        stat;        //这个是准备设置CPU的IICSTAT寄存器的

       unsigned long        iiccon;

 

       stat = 0;

       stat |=  S3C2410_IICSTAT_TXRXEN;              //I2C的RXTX有效

 

       if (msg->flags & I2C_M_RD) {                         //主机读操作

              stat |= S3C2410_IICSTAT_MASTER_RX;  //主机接收

              addr |= 1;              //地址最低位为0

       } else                                                              //主机写操作

              stat |= S3C2410_IICSTAT_MASTER_TX;   //主机发送

 

       //这个标记没有看到定义

       if (msg->flags & I2C_M_REV_DIR_ADDR)

              addr ^= 1;

 

       //打开ACK硬件功能

       s3c24xx_i2c_enable_ack(i2c);

 

       //读IICCON寄存器

       iiccon = readl(i2c->regs + S3C2410_IICCON);

       //写IICSTAT寄存器

       writel(stat, i2c->regs + S3C2410_IICSTAT);             

      

       dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);

       //将地址作为数据写入(I2C第一字节为发送设备地址)

       writeb(addr, i2c->regs + S3C2410_IICDS);

      

       //短延迟,保证刚才的数据写入到寄存器中

       //也就是设置时序的建立时间

       ndelay(i2c->tx_setup);

 

       dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);

       writel(iiccon, i2c->regs + S3C2410_IICCON);

      

       //这位写1,表示开始,发送IICDS中的数据

       stat |=  S3C2410_IICSTAT_START;

       writel(stat, i2c->regs + S3C2410_IICSTAT);

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//发送停止信号

static inline void    s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)

{

       unsigned long        iicstat = readl(i2c->regs + S3C2410_IICSTAT);   //读状态

       dev_dbg(i2c->dev, "STOP\n");

 

       //该位写0,停止信号(读是BUSY)

       iicstat     &= ~ S3C2410_IICSTAT_START;

       writel(iicstat, i2c->regs + S3C2410_IICSTAT);

      

       i2c->state = STATE_STOP;                        //状态机为停

      

       s3c24xx_i2c_master_complete(i2c, ret);       //主机I2C动作完成

       s3c24xx_i2c_disable_irq(i2c);                     //禁止I2C中断

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//返回1 – 当前操作的包是最后一个包

static inline int      is_lastmsg(struct s3c24xx_i2c *i2c)

{

       return     i2c->msg_idx >= (i2c->msg_num - 1);

}

 

//返回1 – 当前操作的字节是MSG包的最后一个字节

static inline int      is_msglast(struct s3c24xx_i2c *i2c)

{

       return     i2c->msg_ptr == i2c->msg->len-1;

}

 

//返回1 – 当前msg包的数据指针已经到包尾

static inline int      is_msgend(struct s3c24xx_i2c *i2c)

{

       return     i2c->msg_ptr >= i2c->msg->len;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//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;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//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(NO ACK)

                     &&  !(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;

              }

 

              //运行到这里,说明收到了ACK

              //判断开始位之后的操作是读还是写?

              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;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//等待400ms看IIC总线是否空闲

//空闲返回0

static int        s3c24xx_i2c_set_master(struct s3c24xx_i2c        *i2c)

{

       unsigned long        iicstat;

       int                        timeout = 400;              //等待400ms的ACK

 

       while (timeout-- > 0) {

              iicstat = readl(i2c->regs + S3C2410_IICSTAT);   //读状态

              if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))

                     return 0;         //I2C空闲,读结束

 

              msleep(1);             //睡眠1ms(让出CPU给其他进程)

       }

 

       //超时出错

       dev_dbg(i2c->dev, "timeout: GPEDAT is %08x\n",

              __raw_readl(S3C2410_GPEDAT));

 

       return     -ETIMEDOUT;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//将消息包发送出去,用wait_event_timeout等待全部的包发送完,或则超时

//注意:doxfer是do  xfer的意思,xfer是数据通讯

static int        s3c24xx_i2c_doxfer(

       struct s3c24xx_i2c        *i2c,

       struct i2c_msg              *msgs,           //消息包

       int                               num)              //消息包的数量

{

       unsigned long        timeout;

       int ret;

 

       ret = s3c24xx_i2c_set_master(i2c);              //判断400ms内IIC总线是否空闲

       if (ret != 0) {         //连续400ms IIC总线不空闲

              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;    //当前I2C状态机

 

       s3c24xx_i2c_enable_irq(i2c);              //开I2C中断

       s3c24xx_i2c_message_start(i2c, msgs); //发开始位

       spin_unlock_irq(&i2c->lock);       //恢复中断

      

       //进程进入睡眠,等待队列唤醒,返回的是剩余时间。

       //唤醒条件:

       //     1、i2c->msg_num == 0

       //     2、HZ * 5,等待时间达到

       //     3、wake_up函数(发STOP位后调用的s3c24xx_i2c_master_complete函数中将调用wake_up)

       timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);

 

       ret = i2c->msg_idx;              //当前操作的msg包号

 

       if (timeout == 0)           //超时

              dev_dbg(i2c->dev, "timeout\n");

       else if (ret != num)        //当前要操作的包没有操作完

              dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);

 

       //睡眠1ms(保证两次操作至少间隔1ms)

       msleep(1);

 

 out:

       return     ret;                //返回操作了几个包

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//尝试调用s3c24xx_i2c_doxfer进行数据传输

static int        s3c24xx_i2c_xfer(

       struct i2c_adapter *adap,            //适配器

       struct i2c_msg       *msgs,

       int                        num)

{

       //从这里可见,I2C数据是从适配器的algo_data中来的

       struct s3c24xx_i2c        *i2c = (struct s3c24xx_i2c *)adap->algo_data;

       int retry;

       int ret;

 

       for (retry = 0; retry < adap->retries; retry++) {          //重复尝试的次数

              ret =       s3c24xx_i2c_doxfer(i2c, msgs, num);   //进行I2C通讯

              if (ret != -EAGAIN)      //正常结束,返回

                     return ret;

 

              //超时结束,等待100us再尝试

              dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

              udelay(100);

       }

       return     -EREMOTEIO;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

static u32       s3c24xx_i2c_func(struct i2c_adapter *adap)

{

       return     I2C_FUNC_I2C

              | I2C_FUNC_SMBUS_EMUL

              | I2C_FUNC_PROTOCOL_MANGLING;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//I2C总线的注册信息

//适配器关联的algo(算法)操作

static const struct i2c_algorithm   s3c24xx_i2c_algorithm = {

       .master_xfer          = s3c24xx_i2c_xfer,

       .functionality         = s3c24xx_i2c_func,

};

 

//I2C总线的数据结构

static struct s3c24xx_i2c       s3c24xx_i2c = {

       .lock              = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),                        //中断锁

       .wait              = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),        //等待队列

       .tx_setup = 50,                     //建立时间

       .adap              = {                //适配器

              .name                    = "s3c2410-i2c",                  //适配器名

              .owner                  = THIS_MODULE,

              .algo                            = &s3c24xx_i2c_algorithm,  //关联的算法操作

              .retries                  = 2,                                    //尝试次数

              .class                     = I2C_CLASS_HWMON,     //所属类

       },

};

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

下面代码是设置IIC总线频率的,在我们看下面代码之前,我们先看看S3C2440规格书中对计算I2C总线频率的描述,否则下面的代码会看得一头雾水:

IIC总线频率是由IICCON寄存器设置的:

Tx clock = IICCLK / (IICCON[3:0] + 1)

其中IICCLK是IICCON的bit6, 该位为0则IICCLK = Fpclk/16,为1为Fpclk/512

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//计算要设置的分频参数,返回的是clk/分频(即计算出来的分频后的频率)

static int        s3c24xx_i2c_calcdivisor(

       unsigned long        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;                  //需要的分频>256

       else

              calc_div1 = 16;                    //需要的分频<=256

 

       calc_divs += calc_div1-1;

       calc_divs /= calc_div1;

 

       //由这里可见,calc_divs是设置进IICCON[3:0]中的

       //加1是因为计算公式中有个加1

       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);

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//比较实际频率和目标频率的误差,+-2khz内可以接受

static inline int      freq_acceptable(unsigned int freq,       unsigned int wanted)

{

       int   diff = freq - wanted;

       return     (diff >= -2 && diff <= 2);

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//设置I2C总线的频率,先设置bus_freq中心频率,如果该频率下没有合适的参数,则扫描总线的最大最小频率范围内有无合适的参数

static int        s3c24xx_i2c_getdivisor(

       struct s3c24xx_i2c               *i2c,

       struct s3c2410_platform_i2c        *pdata,

       unsigned long                      *iicon,

       unsigned int                        *got)

{

       unsigned long clkin = clk_get_rate(i2c->clk);      //获取I2C的时钟频率(HZ)

      

       unsigned int   divs, div1;

       int                 freq;

       int                 start, end;

 

       clkin /= 1000;        //将单位转为KHz

    

       dev_dbg(i2c->dev,  "pdata %p, freq %lu %lu..%lu\n",

               pdata, pdata->bus_freq, pdata->min_freq, pdata->max_freq);

 

       if (pdata->bus_freq != 0) {   

              //如果I2C总线频率不为0,计算频率并比较误差

              freq = s3c24xx_i2c_calcdivisor(clkin, pdata->bus_freq/1000, &div1, &divs);

              if (freq_acceptable(freq, pdata->bus_freq/1000))

                     goto found;            //找到频率的设置参数

       }

 

       //获取总线的最大和最小频率

       start = (pdata->max_freq == 0) ? pdata->bus_freq : pdata->max_freq;

       end = pdata->min_freq;

 

       start /= 1000;

       end /= 1000;                 //转换单位为KHz

 

       //扫描频率范围

       for (; start > end; start--) {

              freq = s3c24xx_i2c_calcdivisor(clkin, start, &div1, &divs);

              if (freq_acceptable(freq, start))

                     goto found;

       }

 

       //没有找到合适的参数

       return -EINVAL;

 

//找到I2C频率的参数

 found:

       *got = freq;                  //获得的频率

       *iicon |= (divs-1);         //divs-1为设置进寄存器的值(参看公式)

       *iicon |= (div1 == 512) ? S3C2410_IICCON_TXDIV_512 : 0;

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//初始化I2C(主要是设置IICON)

static int        s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)

{

       //IICON设置为中断有效,并使能ACK

       unsigned long        iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;

       struct s3c2410_platform_i2c        *pdata;           //平台数据

       unsigned int                        freq;

 

       //获取平台数据(I2C总线->适配器->设备->父设备)

       //如父设备没有平台数据,则关联上本文件一开始设置的默认的平台数据

       pdata = s3c24xx_i2c_get_platformdata(i2c->adap.dev.parent);

 

       //初始化GPIO口的配置,功能为IIC功能

       s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);

       s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);

 

       //写从机地址(怀疑是CPU作为IIC从机时的设备地址)

       writeb(pdata->slave_addr,     i2c->regs + S3C2410_IICADD);

       dev_info(i2c->dev, "slave address 0x%02x\n",    pdata->slave_addr);

 

       //根据总线频率设置IICON寄存器

       if (s3c24xx_i2c_getdivisor(i2c, pdata, &iicon, &freq) != 0) {

              dev_err(i2c->dev, "cannot meet bus frequency required\n");

              return     -EINVAL;

       }

 

       //打印信息

       dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);

       dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);

      

       writel(iicon, i2c->regs + S3C2410_IICCON);     //实际设置到ARM的IICON寄存器中

 

       //检查CPU类型是否是S3C2440

       if (s3c24xx_i2c_is2440(i2c)) {

              dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);

              writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);     //过滤器(去干扰毛刺)

       }

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//I2C总线的探测函数,这个函数里面关联了适配器,基本上I2C的初始化都在这个函数中

//*pdev为平台设备

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;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//移除设备

static int        s3c24xx_i2c_remove(struct platform_device *pdev)

{

       //从平台设备中获取driver_data(上个函数探测的时候才关联上I2C)

       struct s3c24xx_i2c        *i2c = platform_get_drvdata(pdev);

 

       //删除适配器,释放IRQ

       i2c_del_adapter(&i2c->adap);

       free_irq(i2c->irq->start, i2c);

 

       //禁止时钟,时钟的使用者减1

       clk_disable(i2c->clk);

       clk_put(i2c->clk);

 

       //释放内存映射

       iounmap(i2c->regs);

 

       //释放IO资源的关联

       //释放IO资源占用的内存

       release_resource(i2c->ioarea);

       kfree(i2c->ioarea);

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//I2C设备的恢复

static int               s3c24xx_i2c_resume(struct platform_device *dev)

{

       struct s3c24xx_i2c        *i2c = platform_get_drvdata(dev);

 

       if (i2c != NULL)

              s3c24xx_i2c_init(i2c);          //重新初始化即可

 

       return 0;

}

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//平台设备的驱动,关联上的一些操作函数,看数据的结构,其实也是遵循了字符型驱动设备的结构了。

static struct platform_driver   s3c2410_i2c_driver = {

       .probe            = s3c24xx_i2c_probe,

       .remove          = s3c24xx_i2c_remove,

       .resume          = s3c24xx_i2c_resume,

       .driver            = {

              .owner    = THIS_MODULE,

              .name      = "s3c2410-i2c",

       },

};

 

//同上,只是.name换了一下

static struct platform_driver s3c2440_i2c_driver = {

       .probe            = s3c24xx_i2c_probe,

       .remove          = s3c24xx_i2c_remove,

       .resume          = s3c24xx_i2c_resume,

       .driver            = {

              .owner    = THIS_MODULE,

              .name      = "s3c2440-i2c",

       },

};

 

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//代码在init段,也就是启动时会自动执行代码

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;

}

 

static void __exit i2c_adap_s3c_exit(void)

{

       //平台驱动卸载

       platform_driver_unregister(&s3c2410_i2c_driver);

       platform_driver_unregister(&s3c2440_i2c_driver);

}

 

module_init(i2c_adap_s3c_init);

module_exit(i2c_adap_s3c_exit);

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值