Chinaunix首页 | 论坛 | 博客
  • 博客访问: 268007
  • 博文数量: 60
  • 博客积分: 2010
  • 博客等级: 大尉
  • 技术积分: 820
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-18 00:28
文章分类

全部博文(60)

文章存档

2010年(60)

我的朋友

分类:

2010-04-26 17:41:36

I2C适配器驱动被作为一个单独模块被加载进内核,在模块加载和卸载函数中,只需注册和注销一个platform_driver结构体,如代码清单15.25。
代码清单15.25 S3C2410 I2C总线驱动模块加载与卸载
1 static int __init i2c_adap_s3c_init(void)
2 {
3 int ret;
4
5 ret = platform_driver_register(&s3c2410_i2c_driver);
6 if (ret == 0) {
7    ret = platform_driver_register(&s3c2440_i2c_driver);
8    if (ret)
9     platform_driver_unregister(&s3c2410_i2c_driver);
10 }
11
12 return ret;
13 }
14
15 static void __exit i2c_adap_s3c_exit(void)
16 {
17 platform_driver_unregister(&s3c2410_i2c_driver);
18 platform_driver_unregister(&s3c2440_i2c_driver);
19 }
20 module_init(i2c_adap_s3c_init);
21 module_exit(i2c_adap_s3c_exit);
platform_driver结构体包含了具体适配器probe()函数、remove()函数、resume()函数指针等信息,它需要被定义和赋值,如代码清单15.26。
代码清单15.26 platform_driver结构体
1 static struct platform_driver s3c2410_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.27 S3C2410 I2C总线驱动中s3c24xx_i2c_probe函数
1 static int s3c24xx_i2c_probe(struct platform_device *pdev)
2 {
3 struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
4 struct resource *res;
5 int ret;
6
7 /* 使能I2C时钟 */
8 i2c->dev = &pdev->dev;
9 i2c->clk = clk_get(&pdev->dev, "i2c");
10 if (IS_ERR(i2c->clk)) {
11    dev_err(&pdev->dev, "cannot get clock\n");
12    ret = -ENOENT;
13    goto out;
14 }
15 clk_enable(i2c->clk);
16
17 /* 映射寄存器 */
18 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
19 if (res == NULL) {
20    dev_err(&pdev->dev, "cannot find IO resource\n");
21    ret = -ENOENT;
22    goto out;
23 }
24 i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
25       pdev->name);
26 if (i2c->ioarea == NULL) {
27    dev_err(&pdev->dev, "cannot request IO\n");
28    ret = -ENXIO;
29    goto out;
30 }
31
32 i2c->regs = ioremap(res->start, (res->end-res->start)+1);
33 if (i2c->regs == NULL) {
34    dev_err(&pdev->dev, "cannot map IO\n");
35    ret = -ENXIO;
36    goto out;
37 }
38
39 /* 设置I2C信息块 */
40 i2c->adap.algo_data = i2c;
41 i2c->adap.dev.parent = &pdev->dev;
42
43 /* 初始化I2C控制器 */
44 ret = s3c24xx_i2c_init(i2c);
45 if (ret != 0)
46    goto out;
47
48 /* 申请中断 */
49 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
50 if (res == NULL) {
51    ret = -ENOENT;
52    goto out;
53 }
54 ret = request_irq(res->start, s3c24xx_i2c_irq, SA_INTERRUPT,
55      pdev->name, i2c);
56 if (ret != 0) {
57    goto out;
58 }
59 i2c->irq = res;
60   
61 ret = i2c_add_adapter(&i2c->adap); /*添加i2c_adapter*/
62 if (ret < 0) {
63    goto out;
64 }
65
66 platform_set_drvdata(pdev, i2c);
67
68 out:
69 if (ret < 0)
70    s3c24xx_i2c_free(i2c);
71 return ret;
72 }
上 述代码中主体工作是使能硬件并申请I2C适配器使用I/O地址、中断号等,在这些工作都完成无误后,通过I2C核心提供 i2c_add_adapter()函数添加这个适配器。因为S3C2410内部集成I2C控制器,可以确定I2C适配器一定存在, s3c24xx_i2c_probe()函数虽然命名“探测”,但实际没有也不必进行任何探测工作,之所以这样命名完全是一种设计习惯。
与s3c24xx_i2c_probe ()函数完成相反功能函数是s3c24xx_i2c_remove()函数,它在适配器模块卸载函数调用 platform_driver_unregister()函数时被通过platform_driverremove指针方式调用。 xxx_i2c_remove()设计模板见代码清单15.28。
代码清单15.28 S3C2410 I2C总线驱动中s3c24xx_i2c_remove函数
1 static int s3c24xx_i2c_remove(struct platform_device *pdev)
2 {
3 struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
4
5 if (i2c != NULL) {
6    s3c24xx_i2c_free(i2c);
7    platform_set_drvdata(pdev, NULL);
8 }
9
10 return 0;
11 }
代 码清单15.27和15.28中用到s3c24xx_i2c结构体进行适配器所有信息封装,类似于私有信息结构体,它与代码清单15.12给出 xxx_i2c结构体模板对应。代码清单15.29给出了s3c24xx_i2c结构体定义,以及驱动模块定义一个s3c24xx_i2c结构体全局 实例。
代码清单15.29 s3c24xx_i2c结构体
1 struct s3c24xx_i2c {
2 spinlock_t   lock;
3 wait_queue_head_t wait;
4 struct i2c_msg   *msg;
5 unsigned int   msg_num;
6 unsigned int   msg_idx;
7 unsigned int   msg_ptr;
8 enum s3c24xx_i2c_state state;
9 void __iomem   *regs;
10 struct clk   *clk;
11 struct device   *dev;
12 struct resource   *irq;
13 struct resource   *ioarea;
14 struct i2c_adapter adap;
15 };
16
17 static struct s3c24xx_i2c s3c24xx_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.4 S3C2410 I2C总线通信方法
由代码清单15.29第23行可以看出,I2C适配器对应i2c_algorithm结构体实例为s3c24xx_i2c_algorithm,代码清单15.30给出了s3c24xx_i2c_algorithm定义。
代码清单15.30 S3C2410i2c_algorithm结构体
1 static struct i2c_algorithm s3c24xx_i2c_algorithm = {
2 .master_xfer   = s3c24xx_i2c_xfer,
3 .functionality   = s3c24xx_i2c_func,
4 };
上 述代码第1行指定了S3C2410 I2C总线通信传输函数s3c24xx_i2c_xfer(),这个函数是如此关键,所有I2C总线上对设备访问最终应该由它来完成,代码清单 15.31给出了这个重要函数以及其依赖s3c24xx_i2c_doxfer()函数和s3c24xx_i2c_message_start()函数 源代码。
代码清单15.31 S3C2410 I2C总线驱动master_xfer函数
1 static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
2     struct i2c_msg *msgs, int num)
3 {
4 struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
5 int retry;
6 int ret;
7
8 for (retry = 0; retry < adap->retries; retry++) {
9    ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
10    if (ret != -EAGAIN)
11     return ret;
12    udelay(100);
13 }
14
15 return -EREMOTEIO;
16 }
17
18 static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
19 {
20 unsigned long timeout;
21 int ret;
22
23 ret = s3c24xx_i2c_set_master(i2c);
24 if (ret != 0) {
25    ret = -EAGAIN;
26    goto out;
27 }
28
29 spin_lock_irq(&i2c->lock);
30 i2c->msg     = msgs;
31 i2c->msg_num = num;
32 i2c->msg_ptr = 0;
33 i2c->msg_idx = 0;
34 i2c->state   = STATE_START;
35 s3c24xx_i2c_enable_irq(i2c);
36 s3c24xx_i2c_message_start(i2c, msgs);
37 spin_unlock_irq(&i2c->lock);
38
39 timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
40
41 ret = i2c->msg_idx;
42
43 if (timeout == 0)
44    dev_dbg(i2c->dev, "timeout\n");
45 else if (ret != num)
46    dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
47
48 msleep(1);/* 确保停止位已经被传递 */
49
50 out:
51 return ret;
52 }
53
54 static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
55           struct i2c_msg *msg)
56 {
57 unsigned int addr = (msg->addr & 0x7f) << 1;
58 unsigned long stat;
59 unsigned long iiccon;
60
61 stat = 0;
62 stat |= S3C2410_IICSTAT_TXRXEN;
63
64 if (msg->flags & I2C_M_RD) {
65    stat |= S3C2410_IICSTAT_MASTER_RX;
66    addr |= 1;
67 } else
68    stat |= S3C2410_IICSTAT_MASTER_TX;
69 if (msg->flags & I2C_M_REV_DIR_ADDR)
70    addr ^= 1;
71
72 s3c24xx_i2c_enable_ack(i2c);/*如果要使能ACK,则使能*/
73
74 iiccon = readl(i2c->regs + S3C2410_IICCON);
75 writel(stat, i2c->regs + S3C2410_IICSTAT);
76 writeb(addr, i2c->regs + S3C2410_IICDS);
77   
78 udelay(1);/*在发送新开始位前延迟1位*/
79 writel(iiccon, i2c->regs + S3C2410_IICCON);
80 stat |= S3C2410_IICSTAT_START;
81 writel(stat, i2c->regs + S3C2410_IICSTAT);
82 }
s3c24xx_i2c_xfer()函数调用s3c24xx_i2c_doxfer()函数传输I2C消息,第8行循环意味着最多可以重试adap->retries次。
s3c24xx_i2c_doxfer()首先将S3C2410I2C适配器设置为I2C主设备,其后初始化s3c24xx_i2c结构体,使能I2C中断,并调用s3c24xx_i2c_message_start()函数启动I2C消息传输。
s3c24xx_i2c_message_start()函数写S3C2410适配器对应控制寄存器向I2C从设备传递开始位和从设备地址。
上 述代码只是启动了I2C消息数组传输周期,并没有完整实现图15.3中给出algorithm master_xfer流程。这个流程完整实现需要借助I2C适配器上中断来步步推进。代码清单15.32给出了S3C2410 I2C适配器中断处理函数以及其依赖i2s_s3c_irq_nextbyte()函数源代码。
代码清单15.32 S3C2410 I2C适配器中断处理函数
1   static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id,
2          struct pt_regs *regs)
3   {
4   struct s3c24xx_i2c *i2c = dev_id;
5   unsigned long status;
6   unsigned long tmp;
7  
8   status = readl(i2c->regs + S3C2410_IICSTAT);
9   if (status & S3C2410_IICSTAT_ARBITR) {
10    ...  
11 }
12
13 if (i2c->state == STATE_IDLE) {
14    tmp = readl(i2c->regs + S3C2410_IICCON);
15    tmp &= ~S3C2410_IICCON_IRQPEND;
16    writel(tmp, i2c->regs + S3C2410_IICCON);
17    goto out;
18 }
19   
20 i2s_s3c_irq_nextbyte(i2c, status);/* 把传输工作进一步推进 */
21
22   out:
23 return IRQ_HANDLED;
24 }
25
26 static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
27 {
28 unsigned long tmp;
29 unsigned char byte;
30 int ret = 0;
31
32 switch (i2c->state) {
33 case STATE_IDLE:
34    goto out;
35    break;
36 case STATE_STOP:
37    s3c24xx_i2c_disable_irq(i2c);  
38    goto out_ack;
39 case STATE_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     goto out_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     goto out_ack;
57    }
58
59    if (i2c->state == STATE_READ)
60     goto prepare_read;
61    /* 进入写状态 */
62 case STATE_WRITE:
63 retry_write:
64    if (!is_msgend(i2c)) {
65     byte = i2c->msg->buf[i2c->msg_ptr++];
66     writeb(byte, i2c->regs + S3C2410_IICDS);
67    
68    } else if (!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      goto retry_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);/* send stop */
87    }
88    break;
89 case STATE_READ:
90    /* 有一个字节可读,看是否还有消息要处理 */
91    if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) &&
92       !(is_msglast(i2c) && is_lastmsg(i2c))) {
93
94     if (iicstat & S3C2410_IICSTAT_LASTBIT) {
95      dev_dbg(i2c->dev, "READ: No Ack\n");
96
97      s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
98      goto out_ack;
99     }
100    }
101    byte = readb(i2c->regs + S3C2410_IICDS);
102    i2c->msg->buf[i2c->msg_ptr++] = byte;
103
104 prepare_read:
105    if (is_msglast(i2c)) {/* last byte of buffer */
106     if (is_lastmsg(i2c))
107      s3c24xx_i2c_disable_ack(i2c);
108    
109    } else if (is_msgend(i2c)) {
110     /* 还有消息要处理吗? */
111     if (is_lastmsg(i2c)) {    
112      s3c24xx_i2c_stop(i2c, 0);/* last message, send stop and complete */
113     } else {
114      /* 推进到下一条消息 */
115      i2c->msg_ptr = 0;
116      i2c->msg_idx++;
117      i2c->msg++;
118     }
119    }
120    break;
121 }
122
123 /* irq清除 */
124 out_ack:
125 tmp = readl(i2c->regs + S3C2410_IICCON);
126 tmp &= ~S3C2410_IICCON_IRQPEND;
127 writel(tmp, i2c->regs + S3C2410_IICCON);
128 out:
129 return ret;
130 }
中 断处理函数s3c24xx_i2c_irq()主要通过调用i2s_s3c_irq_nextbyte()函数进行传输工作进一步推进。 i2s_s3c_irq_nextbyte()函数通过switch(i2c->state)语句分成i2c->state不同状态进行处 理,在每种状态下,先检查i2c->state状态与硬件寄存器应该处于状态是否一致,如果不一致,则证明有误,直接返回。当I2C处于读状态 STATE_READ或写状态STATE_WRITE时,通过is_lastmsg()函数判断是否传输是最后一条I2C消息,如果是,则产生停止位, 否则通过i2c->msg_idx++、i2c->msg++推进到下一条消息。
15.6 实例:SAA7113H视频AD芯片I2C设备驱动
15.6.1 SAA7113H视频AD芯片硬件描述
如图15.9,SAA7113H是飞利浦半导体推出9位视频AD芯片,它可以选择4路视频输入中1路,并采样为9位数字信号。

图15.9 SAA7113H输入、输出与I2C接口
对SAA7113H 输入通道选择以及采样方式设置都需通过其I2C接口进行,以0x4A地址可读SAA7113H寄存器,以0x4B可写SAA7113H寄存器。 SAA7113HI2C接口连接在S3C2410I2C控制器上,作为S3C2410 I2C控制器从设备。SAA7113H写时序与图15.7给出写时序是一致,但是读时序则需通过2条I2C消息解决,在第1条消息中应该给 SAA7113H写入寄存器子地址,如图15.10。

图15.10 SAA7113H读时序
15.6.2 SAA7113H视频AD芯片驱动模块加载与卸载
由于不需要给SAA7113H实现文件操作接口,在设备驱动模块加载和卸载函数中均不再需要注册和注销字符设备操作,代码清单15.33给出了SAA7113H设备驱动模块加载和卸载源代码。
代码清单15.33 SAA7113H设备驱动模块加载和卸载函数
1 static int __init saa711x_init (void)
2 {
3    return i2c_add_driver(&i2c_driver_saa711x); //注册i2c_driver
4 }
5
6 static void __exit saa711x_exit (void)
7 {
8    i2c_del_driver(&i2c_driver_saa711x); //注销i2c_driver
9 }
10
11 module_init(saa711x_init);
12 module_exit(saa711x_exit);
15.6.3 SAA7113H设备驱动i2c_driver成员函数
如 代码清单15.34,SAA7113H设备驱动模块加载和卸载中添加和删除i2c_driver_saa711x结构体attach_adapter 指针被赋值为saa711x_attach_adapter,detach_client指针被赋值为saa711x_detach_client,而 command指针被赋值为saa711x_command,代码清单15.35给出了这3个函数实现。
代码清单15.34 SAA7113H设备驱动i2c_driver结构
1 static struct i2c_driver i2c_driver_saa711x = {
2 .driver = {
3 .name = "saa711x",
4 },
5 .id = I2C_DRIVERID_SAA711X,
6 /* 成员函数 */
7 .attach_adapter = saa711x_attach_adapter,
8 .detach_client = saa711x_detach_client,
9 .command = saa711x_command,
10 };
代码清单15.35 SAA7113H设备驱动i2c_driver成员函数
1 saa711x_attach_adapter (struct i2c_adapter *adapter)
2 {
3 dprintk(1, KERN_INFO "saa711x.c: starting probe for adapter %s (0x%x)\n",
4    I2C_NAME(adapter), adapter->id);
5   
6 return i2c_probe(adapter, &addr_data, &saa711x_detect_client);
7 //saa711x_detect_client会调用i2c_set_clientdata()、i2c_attach_client()
8 }
9
10 static int saa711x_detach_client (struct i2c_client *client)
11 {
12 struct saa711x *decoder = i2c_get_clientdata(client);
13 int err;
14
15 err = i2c_detach_client(client);
16 if (err) {
17    return err;
18 }
19
20 kfree(decoder);
21 kfree(client);
22
23 return 0;
24 }
25
26 static int saa711x_command(struct i2c_client *client, unsigned int cmd, void
27   *arg)
28 {
29   struct saa711x *decoder = i2c_get_clientdata(client);
30
31   switch (cmd) //处理不同命令
32   {
33     case 0:
34     case DECODER_INIT:
35       ...
36     case DECODER_DUMP:
37       ...
38     case DECODER_GET_CAPABILITIES:
39       ...
40     case DECODER_GET_STATUS:
41       ...
42     case DECODER_SET_VBI_BYPASS:
43       ...
44     case DECODER_SET_NORM:
45       ...
46     case DECODER_SET_INPUT:
47       {
48         int *iarg = arg;
49         if (*iarg < 0 || *iarg > 9)
50         {
51           return - EINVAL;
52         }
53         if (decoder->input != *iarg)
54         {
55           decoder->input = *iarg;
56           saa711x_write(client, 0x02, (decoder->reg[0x02] &0xf0) | decoder
57             ->input);
58           saa711x_write(client, 0x09, (decoder->reg[0x09] &0x7f) | ((decoder
59             ->input > 3) ? 0x80 : 0));
60         }
61       }
62       break;
63
64     case DECODER_SET_OUTPUT:
65       ...
66     case DECODER_ENABLE_OUTPUT:
67       ...
68     case DECODER_SET_PICTURE:
69       ...
70     default:
71       return - EINVAL;
72   }
73
74   return 0;
75 }
15.8本章小结
Linux I2C驱动体系结构有相当复杂度,它主要由3部分组成,即I2C核心、I2C总线驱动和I2C设备驱动。I2C核心是I2C总线驱动和I2C设备驱动 中间枢纽,它以通用、与平台无关接口实现了I2C中设备与适配器沟通。I2C总线驱动填充i2c_adapter和i2c_algorithm结构 体,I2C设备驱动填充i2c_driver和i2c_client结构体。
另外,系统中i2c-dev.c文件定义主设备号为89设备可以方便地给应用程序提供读写I2C设备寄存器能力,使得工程师大多数时候并不需要为具体I2C设备驱动定义文件操作接口。
最后,工程师在设计I2C设备驱动程序,并不是一定要遵守Linux I2C驱动体系结构,完全可以把它当作一个普通字符设备处理,如果不考虑融入Linux现有I2C框架话。
阅读(1293) | 评论(0) | 转发(0) |
0

上一篇:I2C设备驱动

下一篇:软网卡

给主人留下些什么吧!~~