在启动系统时,需要对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()中完成的。
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;
}