LinuxI2C设备驱动文件操作接口.docx
- 文档编号:11869462
- 上传时间:2023-04-06
- 格式:DOCX
- 页数:12
- 大小:21.48KB
LinuxI2C设备驱动文件操作接口.docx
《LinuxI2C设备驱动文件操作接口.docx》由会员分享,可在线阅读,更多相关《LinuxI2C设备驱动文件操作接口.docx(12页珍藏版)》请在冰豆网上搜索。
LinuxI2C设备驱动文件操作接口
I2C适配器驱动被作为一个单独的模块被加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结构体,如代码清单15.25。
代码清单15.25S3C2410I2C总线驱动模块加载与卸载
1staticint__initi2c_adap_s3c_init(void)
2{
3intret;
4
5ret=platform_driver_register(&s3c2410_i2c_driver);
6if(ret==0){
7 ret=platform_driver_register(&s3c2440_i2c_driver);
8 if(ret)
9 platform_driver_unregister(&s3c2410_i2c_driver);
10}
11
12returnret;
13}
14
15staticvoid__exiti2c_adap_s3c_exit(void)
16{
17platform_driver_unregister(&s3c2410_i2c_driver);
18platform_driver_unregister(&s3c2440_i2c_driver);
19}
20module_init(i2c_adap_s3c_init);
21module_exit(i2c_adap_s3c_exit);
platform_driver结构体包含了具体适配器的probe()函数、remove()函数、resume()函数指针等信息,它需要被定义和赋值,如代码清单15.26。
代码清单15.26platform_driver结构体
1staticstructplatform_drivers3c2410_i2c_driver={
2.probe =s3c24xx_i2c_probe,
3.remove =s3c24xx_i2c_remove,
4.resume =s3c24xx_i2c_resume,
5.driver ={
6 .owner=THIS_MODULE,
7 .name="s3c2410-i2c",
8},
9};
当通过Linux内核源代码/drivers/base/platform.c文件中定义platform_driver_unregister()函数注册platform_driver结构体时,其中probe指针指向的s3c24xx_i2c_probe()函数将被调用以初始化适配器硬件,如代码清单15.27。
代码清单15.27S3C2410I2C总线驱动中的s3c24xx_i2c_probe函数
1staticints3c24xx_i2c_probe(structplatform_device*pdev)
2{
3structs3c24xx_i2c*i2c=&s3c24xx_i2c;
4structresource*res;
5intret;
6
7/*使能I2C的时钟*/
8i2c->dev=&pdev->dev;
9i2c->clk=clk_get(&pdev->dev,"i2c");
10if(IS_ERR(i2c->clk)){
11 dev_err(&pdev->dev,"cannotgetclock\n");
12 ret=-ENOENT;
13 gotoout;
14}
15clk_enable(i2c->clk);
16
17/*映射寄存器*/
18res=platform_get_resource(pdev,IORESOURCE_MEM,0);
19if(res==NULL){
20 dev_err(&pdev->dev,"cannotfindIOresource\n");
21 ret=-ENOENT;
22 gotoout;
23}
24i2c->ioarea=request_mem_region(res->start,(res->end-res->start)+1,
25 pdev->name);
26if(i2c->ioarea==NULL){
27 dev_err(&pdev->dev,"cannotrequestIO\n");
28 ret=-ENXIO;
29 gotoout;
30}
31
32i2c->regs=ioremap(res->start,(res->end-res->start)+1);
33if(i2c->regs==NULL){
34 dev_err(&pdev->dev,"cannotmapIO\n");
35 ret=-ENXIO;
36 gotoout;
37}
38
39/*设置I2C的信息块*/
40i2c->adap.algo_data=i2c;
41i2c->adap.dev.parent=&pdev->dev;
42
43/*初始化I2C控制器*/
44ret=s3c24xx_i2c_init(i2c);
45if(ret!
=0)
46 gotoout;
47
48/*申请中断*/
49res=platform_get_resource(pdev,IORESOURCE_IRQ,0);
50if(res==NULL){
51 ret=-ENOENT;
52 gotoout;
53}
54ret=request_irq(res->start,s3c24xx_i2c_irq,SA_INTERRUPT,
55 pdev->name,i2c);
56if(ret!
=0){
57 gotoout;
58}
59i2c->irq=res;
60
61ret=i2c_add_adapter(&i2c->adap);/*添加i2c_adapter*/
62if(ret<0){
63 gotoout;
64}
65
66platform_set_drvdata(pdev,i2c);
67
68out:
69if(ret<0)
70 s3c24xx_i2c_free(i2c);
71returnret;
72}
上述代码中的主体工作是使能硬件并申请I2C适配器使用的I/O地址、中断号等,在这些工作都完成无误后,通过I2C核心提供的i2c_add_adapter()函数添加这个适配器。
因为S3C2410内部集成I2C控制器,可以确定I2C适配器一定存在,s3c24xx_i2c_probe()函数虽然命名“探测”,但实际没有也不必进行任何探测工作,之所以这样命名完全是一种设计习惯。
与s3c24xx_i2c_probe()函数完成相反功能的函数是s3c24xx_i2c_remove()函数,它在适配器模块卸载函数调用platform_driver_unregister()函数时被通过platform_driver的remove指针方式调用。
xxx_i2c_remove()的设计模板见代码清单15.28。
代码清单15.28S3C2410I2C总线驱动中的s3c24xx_i2c_remove函数
1staticints3c24xx_i2c_remove(structplatform_device*pdev)
2{
3structs3c24xx_i2c*i2c=platform_get_drvdata(pdev);
4
5if(i2c!
=NULL){
6 s3c24xx_i2c_free(i2c);
7 platform_set_drvdata(pdev,NULL);
8}
9
10return0;
11}
代码清单15.27和15.28中用到的s3c24xx_i2c结构体进行适配器所有信息的封装,类似于私有信息结构体,它与代码清单15.12给出的xxx_i2c结构体模板对应。
代码清单15.29给出了s3c24xx_i2c结构体的定义,以及驱动模块定义的一个s3c24xx_i2c结构体全局实例。
代码清单15.29s3c24xx_i2c结构体
1structs3c24xx_i2c{
2spinlock_t lock;
3wait_queue_head_twait;
4structi2c_msg *msg;
5unsignedint msg_num;
6unsignedint msg_idx;
7unsignedint msg_ptr;
8enums3c24xx_i2c_statestate;
9void__iomem *regs;
10structclk *clk;
11structdevice *dev;
12structresource *irq;
13structresource *ioarea;
14structi2c_adapteradap;
15};
16
17staticstructs3c24xx_i2cs3c24xx_i2c={
18.lock=SPIN_LOCK_UNLOCKED,/*自旋锁未锁定*/
19.wait=__WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),/*等待队列初始化*/
20.adap={
21 .name ="s3c2410-i2c",
22 .owner =THIS_MODULE,
23 .algo =&s3c24xx_i2c_algorithm,
24 .retries =2,
25 .class =I2C_CLASS_HWMON,
26},
27};
15.5.4S3C2410I2C总线通信方法
由代码清单15.29第23行可以看出,I2C适配器对应的i2c_algorithm结构体实例为s3c24xx_i2c_algorithm,代码清单15.30给出了s3c24xx_i2c_algorithm的定义。
代码清单15.30S3C2410的i2c_algorithm结构体
1staticstructi2c_algorithms3c24xx_i2c_algorithm={
2.master_xfer =s3c24xx_i2c_xfer,
3.functionality =s3c24xx_i2c_func,
4};
上述代码第1行指定了S3C2410I2C总线通信传输函数s3c24xx_i2c_xfer(),这个函数是如此的关键,所有I2C总线上对设备的访问最终应该由它来完成,代码清单15.31给出了这个重要函数以及其依赖的s3c24xx_i2c_doxfer()函数和s3c24xx_i2c_message_start()函数的源代码。
代码清单15.31S3C2410I2C总线驱动的master_xfer函数
1staticints3c24xx_i2c_xfer(structi2c_adapter*adap,
2 structi2c_msg*msgs,intnum)
3{
4structs3c24xx_i2c*i2c=(structs3c24xx_i2c*)adap->algo_data;
5intretry;
6intret;
7
8for(retry=0;retry
9 ret=s3c24xx_i2c_doxfer(i2c,msgs,num);
10 if(ret!
=-EAGAIN)
11 returnret;
12 udelay(100);
13}
14
15return-EREMOTEIO;
16}
17
18staticints3c24xx_i2c_doxfer(structs3c24xx_i2c*i2c,structi2c_msg*msgs,intnum)
19{
20unsignedlongtimeout;
21intret;
22
23ret=s3c24xx_i2c_set_master(i2c);
24if(ret!
=0){
25 ret=-EAGAIN;
26 gotoout;
27}
28
29spin_lock_irq(&i2c->lock);
30i2c->msg =msgs;
31i2c->msg_num=num;
32i2c->msg_ptr=0;
33i2c->msg_idx=0;
34i2c->state =STATE_START;
35s3c24xx_i2c_enable_irq(i2c);
36s3c24xx_i2c_message_start(i2c,msgs);
37spin_unlock_irq(&i2c->lock);
38
39timeout=wait_event_timeout(i2c->wait,i2c->msg_num==0,HZ*5);
40
41ret=i2c->msg_idx;
42
43if(timeout==0)
44 dev_dbg(i2c->dev,"timeout\n");
45elseif(ret!
=num)
46 dev_dbg(i2c->dev,"incompletexfer(%d)\n",ret);
47
48msleep
(1);/*确保停止位已经被传递*/
49
50out:
51returnret;
52}
53
54staticvoids3c24xx_i2c_message_start(structs3c24xx_i2c*i2c,
55 structi2c_msg*msg)
56{
57unsignedintaddr=(msg->addr&0x7f)<<1;
58unsignedlongstat;
59unsignedlongiiccon;
60
61stat=0;
62stat|=S3C2410_IICSTAT_TXRXEN;
63
64if(msg->flags&I2C_M_RD){
65 stat|=S3C2410_IICSTAT_MASTER_RX;
66 addr|=1;
67}else
68 stat|=S3C2410_IICSTAT_MASTER_TX;
69if(msg->flags&I2C_M_REV_DIR_ADDR)
70 addr^=1;
71
72s3c24xx_i2c_enable_ack(i2c);/*如果要使能ACK,则使能*/
73
74iiccon=readl(i2c->regs+S3C2410_IICCON);
75writel(stat,i2c->regs+S3C2410_IICSTAT);
76writeb(addr,i2c->regs+S3C2410_IICDS);
77
78udelay
(1);/*在发送新的开始位前延迟1位*/
79writel(iiccon,i2c->regs+S3C2410_IICCON);
80stat|=S3C2410_IICSTAT_START;
81writel(stat,i2c->regs+S3C2410_IICSTAT);
82}
s3c24xx_i2c_xfer()函数调用s3c24xx_i2c_doxfer()函数传输I2C消息,第8行的循环意味着最多可以重试adap->retries次。
s3c24xx_i2c_doxfer()首先将S3C2410的I2C适配器设置为I2C主设备,其后初始化s3c24xx_i2c结构体,使能I2C中断,并调用s3c24xx_i2c_message_start()函数启动I2C消息的传输。
s3c24xx_i2c_message_start()函数写S3C2410适配器对应的控制寄存器向I2C从设备传递开始位和从设备地址。
上述代码只是启动了I2C消息数组的传输周期,并没有完整实现图15.3中给出的algorithmmaster_xfer流程。
这个流程的完整实现需要借助I2C适配器上的中断来步步推进。
代码清单15.32给出了S3C2410I2C适配器中断处理函数以及其依赖的i2s_s3c_irq_nextbyte()函数的源代码。
代码清单15.32S3C2410I2C适配器中断处理函数
1 staticirqreturn_ts3c24xx_i2c_irq(intirqno,void*dev_id,
2 structpt_regs*regs)
3 {
4 structs3c24xx_i2c*i2c=dev_id;
5 unsignedlongstatus;
6 unsignedlongtmp;
7
8 status=readl(i2c->regs+S3C2410_IICSTAT);
9 if(status&S3C2410_IICSTAT_ARBITR){
10 ...
11}
12
13if(i2c->state==STATE_IDLE){
14 tmp=readl(i2c->regs+S3C2410_IICCON);
15 tmp&=~S3C2410_IICCON_IRQPEND;
16 writel(tmp,i2c->regs+S3C2410_IICCON);
17 gotoout;
18}
19
20i2s_s3c_irq_nextbyte(i2c,status);/*把传输工作进一步推进*/
21
22 out:
23returnIRQ_HANDLED;
24}
25
26staticinti2s_s3c_irq_nextbyte(structs3c24xx_i2c*i2c,unsignedlongiicstat)
27{
28unsignedlongtmp;
29unsignedcharbyte;
30intret=0;
31
32switch(i2c->state){
33caseSTATE_IDLE:
34 gotoout;
35 break;
36caseSTATE_STOP:
37 s3c24xx_i2c_disable_irq(i2c);
38 gotoout_ack;
39caseSTATE_START:
40 /*我们最近做的一件事是启动一个新I2C消息*/
41 if(iicstat&S3C2410_IICSTAT_LASTBIT&&
42 !
(i2c->msg->flags&I2C_M_IGNORE_NAK)){
43 /*没有收到ACK*/
44 s3c24xx_i2c_stop(i2c,-EREMOTEIO);
45 gotoout_ack;
46 }
47
48 if(i2c->msg->flags&I2C_M_RD)
49 i2c->state=STATE_READ;
50 else
51 i2c->state=STATE_WRITE;
52
53 /*仅一条消息,而且长度为0(主要用于适配器探测),发送停止位*/
54 if(is_lastmsg(i2c)&&i2c->msg->len==0){
55 s3c24xx_i2c_stop(i2c,0);
56 gotoout_ack;
57 }
58
59 if(i2c->state==STATE_READ)
60 gotoprepare_read;
61 /*进入写状态*/
62caseSTATE_WRITE:
63retry_write:
64 if(!
is_msgend(i2c)){
65 byte=i2c->msg->buf[i2c->msg_ptr++];
66 writeb(byte,i2c->regs+S3C2410_IICDS);
67
68 }elseif(!
is_lastmsg(i2c)){
69 /*推进到下一条消息*/
70 i2c->msg_ptr=0;
71 i2c->msg_idx++;
72 i2c->msg++;
73
74 /*检查是否要为该消息产生开始位*/
75 if(i2c->msg->flags&I2C_M_NOSTART){
76 if(i2c->msg->flags&I2C_M_RD){
77 s3c24xx_i2c_stop(i2c,-EINVAL);
78 }
79 gotoretry_write;
80 }else{
81 /*发送新的开始位*/
82 s3c24xx_i2c_message_start(i2c,i2c->msg);
83 i2c->state=STATE_START;
84 }
85 }else{
86 s3c24xx_i2c_stop(i2c,0);/*sendstop*/
87 }
88 break;
89caseSTATE_READ:
90 /*有一个字节可读,看是否还有消息要处理*/
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- LinuxI2C 设备 驱动 文件 操作 接口