I2C驱动(IIC驱动)

在启动系统时,需要对IIC子系统进行初始化。驱动开发人员只需要用它,而不需要修改它。
下面对这些公用代码的主要部分进行介绍。
IIC子系统是作为模块加载到系统中的。在系统启动中的模块加载阶段,会调用i2c_init()函数初始化IIC子系统。该函数的代码如下:

static int _init i2c_init (void)
{
    int retval;    /*返回值,成功返回0,错误返回负值*/
    retval = bus_register (&i2e_bus_type);    /*注册一条 IIC的BUS总线*/
    if (retval)
        return retval;
    retval = class_register (&i2c_adapter_class);
    				/*注册适配器类,用于实现sys文件系统的部分功能*/
    if (retval)
        goto bus_err;
    retval = i2c_add_driver (&dummy_driver);
   					 /*将一个空驱动注册到IIC总线中*/
    if (retval)
        goto class_err;
        return 0;
class_err:
    class_unregister(&i2c_adapter_class);    /*类注销*/
bus_err:
        bus_unregister(&i2c_bus_type);    /*总线注销*/
    return retval :
}

与i2c_init()函数对应的退出函数是i2c_exit()。 该函数完成i2c_init()函数相反的功能

static void _exit i2c_exit(void)
{
    i2c_del_driver ( &dummy_driver) ;
    class_unregister (&i2c_adapter_class) ;
    bus_unregister (&i2c_bus_type) ;
}

适配器驱动程序是lIC设备驱动程序需要实现的主要驱动程序,这个驱动程序需要根据具体的适配器硬件来编写。
i2c_adapter 结构体为描述各种IIC适配器提供了通用“模板”,它定义了注册总线上所有设备的clients链表、指向具体IIC适配器的总线通信方法i2c_algorithm 的algo指针、实现i2c总线操作原子性的lock 信号量。但i2c_adapter 结构体只是所有适配器的共有属性,并不能代表所有类型的适配器。

struct s3c24xx_i2c {
    spinlock_t lock;
    wait_queue_head_t  wait;
    unsigned int  suspended:1;    /*表示设备是否挂起,只用一位确定*/
    struct i2c_msg  *msg;
    unsigned int  msg_num;
    unsigned int  msg_idx;
    unsigned int  msg_ptr;
    unsigned int  tx_setup;
    unsigned int irq;    /*适配器申请的中断号*/
    enum s3c24xx_i2c_state state;
    unsigned long  clkrate;
    void _iomem  *regs;
    struct clk  *clk;    /*对应的时钟*/
    struct device  *dev;    /*适配器对应的设备结构体*/
    struct resource  ioarea;    /*适配器的资源*/
    struct i2c_adapter_adapter adap;     /*适配器主体结构体*/
};

s3c24xx_i2c 适配器结构体中有一个i2c_msg的消息指针。该结构体是从适配器到IIC设备传输数据的基本单位,代码如下:

struct i2c_msg{ 
    _u16 addr;					/*IIC设备的地址*/
    _u16 flags; 				/*消息类型标志*/
#define I2C_M_TEN 0x0010		/*这是有10位地址芯片*/
#define I2C_M_RD 0x0001			/*表示从从机到主机读数据*/
#define I2C_M_NOSTART 0x4000
    /*EUNC_PROTOCOL_MANGLING 协议的相关标志*/
#define I2C_M_REV_DIR_ADDR 0x2000
    /*FUNC_PROTOCOL_MANGLING 协议的相关标志*/
#define I2C M_IGNORE_NAK 0x1000
    /*FUNC_PROTOCOL_MANGLING协议的相关标志*/
#define I2C_M_NO_RD_ACK 0x0800
    /*FUNC_PROTOCOL_MANGLING协议的相关标志*/
#define I2C_M_RECV_LEN 0x0400 	/*第一次接收的字节长度*/
    _u16 len;					/*消息字节长度*/
    _u8 buf;					/*指向消息数据的缓冲区*/
};

其中addr为IIC设备的地址。这个字段说明一个适配器在获得总线控制权后,可以与多个IIC设备进行交互。buf指向与IIC设备交互的数据缓冲区,其长度为len。 flags中的标志位描述该消息的属性,这些属性由一系列I2C_M_*宏表示。
使用i2c_add_adapter()函数向 IIC子系统添加适配器结构体i2c_adapter。

int i2c_add_adapter (struct i2c_adapter *adapter)
{
    int id,res=0
            retry:
            if (idr_pre_get(&i2c_adapter_idr,GFP_KERNEL) == 0)
            return -ENOMEM;             /*内存分配失败*/
    mutex_lock(&core_lock);             /*锁定内核锁*/
    res = idr_get_new_above(&i2c_adapter_idr, adapter,
                            __i2c_first_dynamic_bus_num, &id) ;
    mutex_unlock(&core_lock) ;          /*释放内核锁*/
    if (res< 0) {
        if (res ==-EAGAIN)
            goto retry;                 /*分配没有成功,重试*/
        return res ;
    }
    adapter->nr = id;
    return i2c_register_adapter (adapter); /*注册适配器设备*/
}

IDR机制适用在那些需要把某个整数和特定指针关联在一起的地方。例如,在IIC总线中,每个设备都有自己的地址,要想在总线上找到特定的设备,就必须要先发送该设备的地址。当适配器要访问总线上的IIC设备时,首先要知道它们的ID号,同时要在内核中建立一个用于描述该设备的结构体和驱动程序。
通过数组进行索引才能将该设备的ID号和它的设备结构体联系起来,采用IDR机制,该机制内部采用红黑树(radix, 类似于二分数)实现,可以很方便地将整数和指针关联起来,并且具有很高的搜索效率。
IDR结构体的定义如下

struct idr {
	struct idr_layer __rcu *top;
	struct idr_layer *id_free;
	int		  layers; /* only valid without concurrent changes */
	int		  id_free_cnt;
	spinlock_t	  lock;
};

使用idr_init()函数对IDR结构体进行初始化

void idr_init(struct idr *idp)
{
memset (idp, 0, sizeof (struct idr) ) ;      /*初始化为0*/
spin_1ock_init (&idp->lock) ;                /*初始化自旋锁*/
}

使用idr_get_new()函数和idr_get_new_above()函数,可以使一个ID号和一个指针关联。

int idr_get_new(struct idr *idp, void *ptr, int *id);
int idr_get_new_above(struct idr *idp, void *ptr, int starting_id, int *id);

使用i2c_get_adapter()函数可以通过ID号获得适配器指针,该函数的代码如下

struct i2c_adapter* i2c_get_adapter(int id)
{
    struct i2c_adapter *adapter;/*适配器指针*/
    mutex_lock(&core_lock);/*锁定内核锁*/
    adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id);
    /*通过ID号,查询适配器指针*/
    if (adapter && !try_module_get (adapter->owner) )
        /*适配器模块引用计数加1*/
        adapter = NULL;
    mutex_unlock (&core_lock) ;/*释放内核锁*/
    return adapter;
}

s3c24xx_i2c_xfer()函数用于实现IIC通信协议,将i2c_msg消息传给IIC设备。

statie int s3c24xx_i2c_xfer (struct i2c_adapter *adapt
                             struct i2c_msg *msgs, int num)
{
    /*从适配器的私有数据获得适配器s3c24xx i2c结构体*/
    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 (120, msgs,  num) ;         /*传输到IIC设备的具体函数*/
        if(ret != -EAGAIN)
            return ret ;
        dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
        /*重试信息*/
        udelay(100) ;       /*延时100微秒*/
        return - EREMOTEIO;/*I/0错误*/
    }

适配器的传输函数s3c24xx_ ji2c_ 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);
    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);
    ret=i2c->msg_idx;
    if (timeout == 0)           /*在规定的时间没有成功写入数据*/
        dev_dbg (i2c->dev, "timeout\n");
    else if (ret != num)        /*未写完规定的消息个数,则失败*/
        dev_dbg (i2c->dev, "incomplete xfer (%d) \n", ret);
    msleep(1);                  /*睡眠1毫秒,使总线停止*/
out:
    return ret;
}

在适配器发送数据以前,需要判断总线的忙闲状态。使用s3c24xx i2c set _master()函数读取IICSTAT寄存器的[5]位,可以判断总线的忙闲状态。当为0时,总线空闲;当为1时总线繁忙。该函数的代码如下:

static int s3c24xx_2c_set_ master (struct s3c24xx_i2c *i2c)
{
    unsigned long iicstat;
    /*用于存储IICSTAT的状态*/
    int timeout = 400;
    /*用于存储IICSTAT的状态*/
    while (timeout-- > 0) {
        /*进行400次尝试,获得总线*/
        iicstat=read1 (i2c->regs + S3C2410_IICSTAT) ;
        /*读取状态寄存器IICSTAT的值*/
        if (!(iicstat& S3C2410_IICSTAT_BUSBUSY)) /*检查第5位是否为0*/
            return 0;
        /*返回0表示空闲*/
        msleep(1) ;
        /*等待1毫秒*/
        return 一ETIMEDOUT:
        }

s3c24xx_ i2c_ message_start()函数写s3c2440适配器对应的寄存器,向IIC设备传递开始位和IIC设备地址。其主要完成两个功能:
写s3c2440的适配器对应的IICON和IICSTAT寄存器。
写从设备地址,并发出开始信号S。
该函数的代码如下:

static void s3c24xx _i2c_message_start (struct s3c24xx_i2c *i2c, struct
                                        i2c_msg *msg)
{
    unsigned int addr=(msg->addr & 0x7f) << 1;
    /*取从设备的低7位地址,并向前移动位*/
    unsigned long stat;
    /*缓存IICSTAT寄存器的值*/
    unsignedlong iiccon;
    /*缓存IICCON寄存器的值*/
    stat=0;
    /*状态初始化为0*/
    stat |= S3C2410_IICSTAT_TXRXEN;
    /*使能接收和发送功能,使适配器能发送数据*/
    if (msg->flags & I2C_M_RD) {/*如果消息类型是从IIC设备到适配器读数据*/
        stat |= S3C2410_ IICSTAT_MASTER_RX; /*将适配器设置为主机接收器*/
        addr |=1;
        /*将地址的最低位置1表示读操作*/
    } else
        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) ;
    /*读出IICCON寄存器的值/
/*设置IICSTAT的值,使其为主机发送器,接收使能*/
    writel (stat,i2c->regs + S3C2410 IICSTAT) :
        dev_dbg(i2c->dev, "START: %081x to IICSTAT, %02x to DS\n", stat,
                addr);
    /*打印调试信息*/
    writeb (addr,i2c->regs + S3C2410_ IICDS); /*写地址寄存器的值*/
    ndelay(i2c->tx_setup); /*延时,以使数据写入寄存器中*/
    writel (iiccon,i2c->regs + S3C2410 IICCON):/*写IICCON寄存器的值*/
        stat|= S3C2410 IICSTAT START;
    /*设置为启动状态*/
    writel(stat,i2c->regs + S3C2410 IICSTAT); /*发出s开始信号/
}

数据的通信过程如下:
(1)传输数据时,调用s3c24xx_ i2c_ algorithm 结构体中的数据传输函数s3c24xx_i2c_ xfer()。
(2) s3c24xx_ i2c_ xfer()中会调用s3c24xx_ i2c_ doxfer()进行数据的传输。
(3) s3c24xx_ i2c_ doxfer()中 向总线发送IIC设备地址和开始信号S后,便会调用wait event timeout()函数进入等待状态。
(4)将数据准备好发送时,将产生中断,并调用事先注册的中断处理函数
s3c24xx_ i2c_ irq()。
(5) s3c24xx_ i2c_ irq()调用下一个字节传输函数i2s_ s3c_ irq nextbyte()来传输数据。
(6)当数据传输完成后,会调用s3c24xx_ i2c_ stop()。
(7)最后调用wake_up()唤醒等待队列,完成数据的传输过程。
当s3c2440的IIC适配器处于主机模式时,IIC操作的第一步总是向IIC总线写入设备的地址及开始信号,这步由上面介绍的函数s3c24xx_ i2c_ set_ master() 和s3c24xx_ i2c_ message_ start()完 成。而收发数据的后继操作都是在IIC中断处理函数ts3c24xx_ i2c_irq()中完成的。
I2C驱动(IIC驱动)_第1张图片
i2s_ s3c_irq_nextbyte()函 数用来传送下一个字节,其代码如下:

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:
        /*总线上没有数据传输,则立即返回0*/
        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*/
        /*当没有接收到IIC设备的应答ACK信号,说明对应地址的IIC设备不存在,停
止总线工作*/
        if (iicstat & S3C2410_IICSTAT_LASTBIT&&
                ! (i2c->msg->flags & I2C_ M IGNORE NAK) ) {
            dev_dbg (i2c->dev, "ack was 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 ()判断是否只有一条消息,如果这条消息为0字节,那么发送
停止信号P。0长度的消息用于设备探测probe()时检测设备*/
        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:
        /*没有接收到IIC设备的ACK信号,则表示出错,停止总线传输*/
        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)) {
            byte= i2c->msg->buf[i2c->msg_ ptr++];
            /*读出缓存区中的数据,并增加偏移*/
            writeb(byte,i2c->regs + S3C2410_IICDS):
                /*将一个字节的数据写到IICDS中*/
                ndelay(i2c->tx_setup); /*等待数据发送到总线上*/
        } else if (!is_lastmsg (i2c)) {
            /*如果不是最后一个消息, 则移向下一个消息*/
            dev_dbg (i2c->dev, WRITE: Next Message\n");
                     i2c->msg_ptr= 0;
                    i2c->msg_idx++;
            i2c->msg++;
            /*不处理这种新类型的消息,直接停止*/
            if (i2c->msg->flags & I2C_M_NOSTART) (
                if (i2c->msg->flags & I2C_M_RD) {
                    s3c24xxi2c stop(i2c, -EINVAL) ;
                }
            goto retry write;
        } else{
            /*开始传输消息,将IICDS的数据发到总线上*/
            s3c24xxi2c_message_start(i2c, i2c->msg) ;
            i2c->state = STATE START;
            /*置开始状态*/
        }
    }else
    s3c24xxi2c_stop(i2c, 0);
    /*所有消息传输结束,停止总线*/
    break;
    case STATE_READ:
        /*读数据*/
        byte= readb(i2c->regs + S3C2410_IICDS); /*从数据寄存器读出数据*/
        i2c->msg->buf [i2c->msg_ptr++]= byte; /*放到缓存区中*/
prepare_read:
        if (is_msglast(i2c)) {/*一个消息的最后一个字节*/
            if (is_lastmsg (i2c))
                /*最后一个消息*/
                s3c24xx_i2c_disable_ack(i2c) ;
            /*禁止ACK信号*/
        } else if (is_msgend(i2c)) {
            /*读完一个消息*/
            if(is_lastmsg(i2c)){
                /*最后一个消息*/
                dev_dbg (i2c->dev, "READ: Send Stop\n") ;
                s3c24xx_ i2c stop(i2c,0); /*发出停止信号P,并唤醒队列*/
            } else{
                /*传输下一个消息*/
                dev_dbg (i2c->dev, "READ: Next Transfer\n") ;
                i2c->msg_ptr= 0;
                i2c->msg_idx++;
                /*移动到下一个消息索引值*/
                i2c->msg++;
                /*移动到下一个消息*/
            }
        }
        break;
}
out_ack:
/*清除中断,不然将重复执行该中断处理函数*/
tmp = readl (i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel (tmp, i2c->regs + S3C2410_IICCON) ;
out :
return ret ;
}

设备驱动模块的加载函数如下

static int_init i2c_ adap_ s3c init (void)
{
    int ret;
    /*返回值*/
    ret - platform driver_register (&s3c2410 i2c driver) ;
    /*注册驱动程序*/
    if(ret==0){
        ret= platform driver register (6s3c2440 i2c driver) ;
        /*再次注册*/
        if (ret)
            platform driver unregister (&s3c2410 i2c driver);
        /*注销驱动程序*/
    }
    return ret;
}

平台设备注册函数platform driver register()中会调用探测函数3c24xx_ i2c_ probe()。 在该函数中将初始化适配器、IIC 等硬件设备。其主要完成如下几个功能:
(1)申请一个适配器结构体i2c, 并对其赋初值。
(2)获得i2c时钟资源。
(3)将适配器的寄存器资源映射到虛拟内存中。
(4)申请中断处理函数。
(5)初始化IIC控制器。
(6)添加适配器i2c到内核中。
完成这些功能的代码如下:

static int s3c24xx i2c probe (struct platform device *pdev)
{
    struct s3c24xx_i2c *i2c;
    /*适配器指针*/
    struct s3c2410 platform i2c *pdata; /*IIC平台设备相关的数据*/
    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) ;
        if (!i2c) {
            /*内存不足,失败*/
            dev_err (&pdev->dev, "no memory | for state\n") ;
            return - ENOMEM;
        }
        strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof (i2c->adap.name));
        /*给适配器赋名为s3c2410-i2c */
        i2c->adap.owner=THIS MODULE;                /*模块指针*/
        i2c->adap.a1go=Gs3c24xx i2c algorithm;      /*给适配器一个通信方法*/
        i2c->adap.retries = 2;                      /*2次总线仲裁尝试*/
        i2c->adap.class= I2C CLASS HWMON |I2C CLASS SPD;
        /*定义适配器类*/
        /*数据从适配器传输到总线的时间为50纳秒*/
        i2c->tx_setup= 50;
        spin_1ock_init (&i2c->lock) ;
        /*初始化自旋锁*/
        init waitqueue head (&i2c->wait) ;
        /*初始化等待队列头部*/
        /*以下代码找到i2c的时钟,并且调用clk_ enable ()函数启动它*/
        i2c->dev=&pdev->dev;
        i2c->c1k=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 ip\n", i2c->clk);
        clk_enable (i2c->clk) ;
        /*启动时钟*/
        res =platform_get_resource (pdev, IORESOURCE MEM, 0);
        /*获得适配器的寄存器资源*/
        if (res==NULL) {
            /*资源获取失败则退出*/
            dev=err (&pdev->dev, "cannot find I0 resource\n") ;
            ret = -ENOENT;
            goto err_clk;
            /*申请一块I/0内存,对应适配器的几个寄存器*/
            i2c->ioarea = request_mem_region (res->start, (res->end-res->
                                                           start)+1,pdev->name) ;
        }if (i2c->ioarea == NULL) {
            /*I/0内存获取失败则退出*/
            dev_err (&pdev->dev, "cannot request IO\n") ;
            ret=-ENXIO;
            goto err_clk;
        }
        /*将设备内存映射到虚拟地址空间,这样可以使用函数访问*/
        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 loarea;
    }
    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);
    /*初始化IIC控制器*/
    1f (ret!- 0)
            /*初始化失败*/
            goto err_ iomap; 
    i2c->irq -ret- platform get. irq (pdev, 0) ;
    /*获得平台设备的第一个中断号*/
    if(ret<= 0){
        dev_ err (&pdev->dev, "cannot find IRQ\n") ;
        goto err_ 1 omap;
        /*申请一个中断处理函数,前面已经介绍过该中断函数*/
        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 8d\n", i2c->irq) ;
        goto err_iomap;
        /*在内核中注册一个适配器使用的时钟*/
        ret=s3c24xx i2c register cpufreq(i2c) ;
        dev_ err (&pdev->dev, "faliled 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 ;
    }
    /*摄制平台设备的私有数据为i2c适配器*/
    platform set drvdata(pdev, i2c) ;
    dev_ info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name (&i2c->adap.
                                                             dev)) ;
    return 0;                       //成功返回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) ;
    /*释放i2c适配器结构体资源*/
    return ret;
}

你可能感兴趣的:(I2C驱动(IIC驱动))