当前位置:嗨网首页>书籍在线阅读

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++推进到下一条消息。