15-S3C6410 I2C总线通信方法
15.5.4 S3C6410 I2C总线通信方法
由代码清单15.20的第22行可以看出,I2C适配器对应的i2calgorithm结构体实例为s3c24xx i2c_algorithm,代码清单15.23所示为s3c24xx_i2c_algorithm的定义。
代码清单15.23 S3C6410的i2c_algorithm结构体
1 static struct i2c_algorithm s3c24xx_i2c_algorithm = {
2 .master_xfer = s3c24xx_i2c_xfer,
3 .functionality = s3c24xx_i2c_func,
4 };
上述代码第一行指定了S3C6410 I2C总线通信传输函数s3c24xx_i2c_xfer(),这个函数非常关键,所有I2C总线上对设备的访问最终应该由它来完成,代码清单15.24所示为这个重要函数以及其依赖的s3c24xx_i2c_doxfer()函数和s3c24xx_i2c_message_start()函数的源代码。
代码清单15.24 S3C6410 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
10 ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
11
12 if (ret != -EAGAIN)
13 return ret;
14
15 dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
16
17 udelay(100);
18 }
19
20 return -EREMOTEIO;
21 }
22
23 static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
24 struct i2c_msg *msgs, int num)
25 {
26 unsigned long timeout;
27 int ret;
28 int iicstat;
29
30 if (i2c->suspended)
31 return -EIO;
32
33 ret = s3c24xx_i2c_set_master(i2c);
34 if (ret != 0) {
35 dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
36 s3c24xx_i2c_stop(i2c, -ENXIO);
37
38 iicstat = readl(i2c->regs + S3C2410_IICSTAT);
39
40 if ((iicstat & S3C2410_IICSTAT_BUSBUSY)){
41 iicstat &= ~(S3C2410_IICSTAT_TXRXEN | S3C2410_IICSTAT_BUSBUSY);
42 writel(iicstat,i2c->regs + S3C2410_IICSTAT);
43 }
44
45 msleep(1);
46
47 ret = -EAGAIN;
48 goto out;
49 }
50
51 spin_lock_irq(&i2c->lock);
52
53 i2c->msg = msgs;
54 i2c->msg_num = num;
55 i2c->msg_ptr = 0;
56 i2c->msg_idx = 0;
57 i2c->state = STATE_START;
58
59 s3c24xx_i2c_enable_irq(i2c);
60 s3c24xx_i2c_message_start(i2c, msgs);
61 spin_unlock_irq(&i2c->lock);
62
63 timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, Hz * 5);
64
65 ret = i2c->msg_idx;
66
67 if (timeout == 0)
68 dev_dbg(i2c->dev, "timeout\n");
69 else if (ret != num)
70 dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
71
72 msleep(1); / 确保停止位已经被传递 /
73
74 out:
75 return ret;
76 }
77
78 static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
79 struct i2c_msg *msg)
80 {
81 unsigned int addr = (msg->addr & 0x7f) << 1;
82 unsigned long stat;
83 unsigned long iiccon;
84
85 stat = 0;
86 stat |= S3C2410_IICSTAT_TXRXEN;
87
88 if (msg->flags & I2C_M_RD) {
89 stat |= S3C2410_IICSTAT_MASTER_RX;
90 addr |= 1;
91 } else
92 stat |= S3C2410_IICSTAT_MASTER_TX;
93
94 if (msg->flags & I2C_M_REV_DIR_ADDR)
95 addr ^= 1;
96
97 s3c24xx_i2c_enable_ack(i2c);
98
99 iiccon = readl(i2c->regs + S3C2410_IICCON);
100 writel(stat, i2c->regs + S3C2410_IICSTAT);
101
102 dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
103 writeb(addr, i2c->regs + S3C2410_IICDS);
104
105 ndelay(i2c->tx_setup);
106
107 dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
108 writel(iiccon, i2c->regs + S3C2410_IICCON);
109
110 stat |= S3C2410_IICSTAT_START;
111 writel(stat, i2c->regs + S3C2410_IICSTAT);
112 }
s3c24xx_i2c_xfer()函数调用s3c24xx_i2c_doxfer()函数传输I2C消息,第8行的循环意味着最多可以重试adap->retries次。
s3c24xx_i2c_doxfer()首先将S3C6410的I2C适配器设置为I2C主设备,其后初始化s3c24xx_i2c结构体,使能I2C中断,并调用s3c24xx_i2c_message_start()函数启动I2C消息的传输。
s3c24xx_i2c_message_start()函数写S3C6410适配器对应的控制寄存器,向I2C从设备传递开始位和从设备地址。
上述代码只是启动了I2C消息数组的传输周期,并没有完整实现图15.3中给出的algorithm master_xfer流程。这个流程的完整实现需要借助I2C适配器上的中断来步步推进。代码清单15.25所示为S3C6410 I2C适配器中断处理函数以及其依赖的i2s_s3c_irq_nextbyte()函数的源代码。
代码清单15.25 S3C6410 I2C适配器中断处理函数
1 static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
2 {
3 struct s3c24xx_i2c *i2c = dev_id;
4 unsigned long status;
5 unsigned long tmp;
6
7 status = readl(i2c->regs + S3C2410_IICSTAT);
8 if (status & S3C2410_IICSTAT_ARBITR) {
9 ...
10 }
11
12 if (i2c->state == STATE_IDLE) {
13 tmp = readl(i2c->regs + S3C2410_IICCON);
14 tmp &= ~S3C2410_IICCON_IRQPEND;
15 writel(tmp, i2c->regs + S3C2410_IICCON);
16 goto out;
17 }
18
19 i2s_s3c_irq_nextbyte(i2c, status);/ 把传输工作进一步推进 /
20
21 out:
22 return IRQ_HANDLED;
23 }
24
25 static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
26 {
27 unsigned long tmp;
28 unsigned char byte;
29 int ret = 0;
30
31 switch (i2c->state) {
32 case STATE_IDLE:
33 goto out;
34 break;
35 case STATE_STOP:
36 s3c24xx_i2c_disable_irq(i2c);
37 goto out_ack;
38 case STATE_START:
39 / 我们最近做的一件事是启动一个新I 2C消息/
40 if (iicstat & S3C2410_IICSTAT_LASTBIT &&
41 !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
42 / 没有收到ACK /
43 s3c24xx_i2c_stop(i2c, -EREMOTEIO);
44 goto out_ack;
45 }
46
47 if (i2c->msg->flags & I2C_M_RD)
48 i2c->state = STATE_READ;
49 else
50 i2c->state = STATE_WRITE;
51
52 / 仅一条消息,而且长度为0(主要用于适配器探测),发送停止位/
53 if (is_lastmsg(i2c) && i2c->msg->len == 0) {
54 s3c24xx_i2c_stop(i2c, 0);
55 goto out_ack;
56 }
57
58 if (i2c->state == STATE_READ)
59 goto prepare_read;
60 / 进入写状态 /
61 case STATE_WRITE:
62 ...
63 retry_write:
64 if (!is_msgend(i2c)) {
65 byte = i2c->msg->buf[i2c->msg_ptr++];
66 writeb(byte, i2c->regs + S3C2410_IICDS);
67 ndelay(i2c->tx_sep);
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++推进到下一条消息。