Linux I2C驱动分析(一)----I2C架构和总线驱动

一、I2C总线原理

I2C是一种常用的串行总线,由串行数据线SDA 和串线时钟线SCL组成。I2C是一种多主机控制总线,它和USB总线不同,USB是基于master-slave机制,任何设备的通信必须由主机发起才可以,而 I2C 是基于multi master机制,一条总线上可允许多个master。

系统的I2C模块分为I2C总线控制器和I2C设备。I2C 总线控制器是CPU提供的控制I2C总线接口,它控制I2C总线的协议、仲裁、时序。I2C设备是指通过I2C总线与CPU相连的设备,如EEPROM。使用I2C通信时必须指定主从设备。一般来说,I2C总线控制器被配置成主设备,与总线相连的I2C设备如AT24C02作为从设备。

1.1IIC读写原理

IIC总线的开始/停止信号如图1所示。开始信号为:时钟信号线SCL为高电平,数据线SDA从高变低。停止信号为:时钟信号线SCL为高电平,数据线SDA从低变高。

图1 IIC Start-Stop Signal

1.2IIC总线Byte Write

IIC总线写数据分几种格式,如字节写和页写。

字节写传送格式如图2所示。开始信号之后,总线开始发数据,第一个Byte是IIC的设备地址,第二个Byte是设备内的地址(如EEPROM中具体的某个物理地址),然后就是要传送的真正的数据DATA。

NOTE:IIC 总线在传送每个Byte后,都会从IIC总线上的接收设备得到一个ACK信号来确认接收到了数据。其中,第一个Byte的设备地址中,前7位是地址码,第8位是方向位(“0”为发送,“1”为接收)。IIC的中断信号有:ACK,Start,Stop。

图2 IIC Byte Write

Write功能的实际实现原理如图3所示:

(1)设置GPIO的相关引脚为IIC输出;

(2)设置IIC(打开ACK,打开IIC中断,设置CLK等);

(3)设备地址赋给IICDS ,并设置IICSTAT,启动IIC发送设备地址出去;从而找到相应的设备即IIC总线上的设备。

(4)第一个Byte的设备地址发送后,从EEPROM得到ACK信号,此信号触发中断;

(5)在中断处理函数中把第二个Byte(设备内地址)发送出去;发送之后,接收到ACK又触发中断;

(6)中断处理函数把第三个Byte(真正的数据)发送到设备中。

(7)发送之后同样接收到ACK并触发中断,中断处理函数判断,发现数据传送完毕。

(8)IIC Stop信号,关IIC中断,置位各寄存器。

图3 IIC Write Operation

NOTE:对于EEPROM,IICDS寄存器发送的数据会先放在Ring buffer中,当其收到stop信号时,开始实际写入EEPROM中。在实际写的过程中,EEPROM不响应从CPU来的信号,直到写完才会响应,因而有一段延迟代码。在page write时,注意一定要有延时!

NOTE:数据先写到EEPROM的ring buffer中,收到Stop信号时,开始实际地把数据写入EEPROM,这时不响应任何输入。即这时Write函数中后面的延时中,向其发slvaddr时,不会得到ACK,直到数据写完时,才会收到ACK。

1.3IIC总线Random Read

IIC总线读数据为Current Address Read,Random Read,Sequential Read

IIC 总线Random Read传送格式如图4所示。开始信号后,CPU开始写第一个Byte(IIC的设备地址),第二个Byte是设备内的地址(此地址保存在设备中)。然后开始读过程:发送设备地址找到IIC设备,然后就开始读数据。类似写过程,CPU读一个byte的实际数据后,CPU向IIC的EEPROM 发ACK,ACK触发中断。读数据也在中断程序中进行。

图4 IIC Random Read Operation

二、I2C架构概述

在linux中,I2C驱动架构如下所示:


图5 I2C驱动架构1

Linux中I2C体系结构如下图所示(图片来源于网络)。图中用分割线分成了三个层次:用户空间(也就是应用程序),内核(也就是驱动部分)和硬件(也就是实际物理设备)。我们现在就是要研究中间那一层。

2.1I2C驱动概述

Linux的I2C驱动结构可分为3个部分:

a、 I2C核心

I2C

核心提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法(即“algorithm”),与具体适配器无关的代码以及探测设备、检测设备地址等。i2c-core.c中的核心驱动程序可管理多个I2C总线适配器(控制器)和多个I2C从设备。每个I2C从设备驱动都能找到和它相连的I2C总线适配器。

b、I2C总线驱动

I2C总线驱动主要包括I2C适配器结构i2c_adapter和I2C适配器的algorithm数据结构。

通过I2C总线驱动的代码,可控制I2C适配器以主控方式产生开始位、停止位、读写周期,以及以从设备方式被读写、产生ACK等。

c、I2C设备驱动

I2C设备驱动是对I2C设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。I2C设备驱动主要包括数据结构i2c_driver和i2c_client。

图6 I2C驱动架构2

如上图所示,每一条I2C总线对应一个adapter。在kernel中,每一个adapter提供了一个描述的结构(struct i2c_adapter),也定义了adapter支持的操作。再通过i2c core层将i2c设备与i2c adapter关联起来。

三、I2C代码在内核中的结构

3.1  I2C驱动调用关系

内核中对于I2C定义了4种结构:

1)i2c_adapter—I2C总线适配器。即为CPU中的I2C总线控制器。

2)i2c_algorithm—I2C总线通信传输算法,管理I2C总线控制器,实现I2C总线上数据的发送和接收等操作。

3)i2c_client—挂载在I2C总线上的I2C设备的驱动程序。

4)i2c_driver—用于管理I2C的驱动程序,它对应I2C的设备节点。

这4种结构的定义见include/linux/i2c.h文件。

对于i2c_driver和i2c_client,i2c_driver对应一套驱动方法,是纯粹的用于辅助作用的数据结构,它不对应于任何的物理实体。

i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_client
一般被包含在i2c字符设备的私有信息结构体中。i2c_driver
与i2c_client发生关联的时刻在i2c_driver的attach_adapter()函数被运行时。attach_adapter()会探测物理设备,当确定一个client存在时,把该client使用的i2c_client数据结构的adapter指针指向对应的i2c_adapter,driver指针指向该i2c_driver,并会调用i2c_adapter的client_register()函数。相反的过程发生在i2c_driver
的detach_client()函数被调用的时候。

对于i2c_adpater 与i2c_client,与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附于i2c_adpater。由于一个适配器上可以连接多个I2C设备,所以一个i2c_adpater也可以被多个i2c_client依附,i2c_adpater中包括依附于它的i2c_client的链表。

i2c.h文件中除定义上述4个重要结构之外,还定义了一个非常重要的结构体:i2c_msg,其定义如下:

1.   struct i2c_msg {

2.          __u16 addr; /* slave address*/

3.          __u16 flags;

4.   #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */

5.   #define I2C_M_RD 0x0001 /* read data, from slave to master */

6.   #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */

7.   #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */

8.   #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */

9.   #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */

10.  #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */

11.          __u16 len; /* msg length */

12.          __u8 *buf; /* pointer to msg data */

13.  };

它是实际传输的数据,其中包括了slave address、数据长度和实际的数据。

3.2  内核中的I2C驱动

Linux内核源码的drivers目录下有个i2c目录,其中包含如下文件和文件夹:

a、i2c-core.c

这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。

b、i2c-dev.c

实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访问设备时的主设备号都为89,次设备号为0~255。应用程序通过“i2c-%d”
(i2c-0, i2c-1, ..., i2c-10,
...)文件名并使用文件操作接口open()、write()、read()、ioctl()和close()等来访问这个设备。

i2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read()、write()和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。

c、chips文件夹

此目录中包含了一些特定的I2C设备驱动,如RTC实时钟芯片驱动和I2C接口的EEPROM驱动等。

d、busses文件夹

此目录中包含了一些I2C总线的驱动,如S3C2410的I2C控制器驱动为i2c-s3c2410.c。

e、algos文件夹

实现了一些I2C总线适配器的algorithm。

i2c-core.c文件不需要修改,其主要实现的函数有:

1)adapter和client相关操作

1.   int i2c_add_adapter(struct i2c_adapter *adap); //增加adapter

2.   int i2c_del_adapter(struct i2c_adapter *adap);

3.   int i2c_register_driver(struct module *, struct i2c_driver *); //增加驱动 (i2c_add_driver)

4.   int i2c_del_driver(struct i2c_driver *driver);

5.   int i2c_attach_client(struct i2c_client *client); //增加client

6.   int i2c_detach_client(struct i2c_client *client);

2)I2C传输,发送和接收

1.   int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);

2.   int i2c_master_send(struct i2c_client *client,const char *buf ,int count);

3.   int i2c_master_recv(struct i2c_client *client, char *buf ,int count);

i2c_transfer
函数用于进行I2C适配器和I2C设备之间的一组消息交互。i2c_master_send函数和i2c_master_recv函数调用i2c_transfer函数分别完成一条写消息和一条读消息。而i2c_transfer函数实现中使用这句话adap->algo->master_xfer(adap,msgs,num);来调用i2c_algorithm中注册的master_xfer函数。

i2c_algorithm如下定义:

1.   struct i2c_algorithm {

2.       int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,

3.                               int num);

4.       int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,

5.                               unsigned short flags, char read_write,

6.                               u8 command, int size, union i2c_smbus_data *data);

7.       u32 (*functionality) (struct i2c_adapter *);

8.       }

根据定义主要要实现i2c_algorithm的master_xfer()函数和functionality()函数。

四、Algorithm中的传输函数master_xfer

图6只是提供了一个大概的框架。在下面的代码分析中,从Algorithm中的传输函数master_xfer来开始分析整个结构。以下的代码分析是基于linux
3.0.4。分析的代码基本位于: linux-3.0.4/drivers/i2c/位置。

博文以一款CPU的I2C模块作为例子。

分析一个Linux驱动代码,一般都是从module_init()开始,分析一个不带操作系统的程序,一般从main函数开始,此处我们分析I2C的总线驱动,从设备调用I2C总线驱动的入口处开始分析。在i2c-core.c中的i2c_transfer函数中,会有语句:ret
= adap->algo->master_xfer(adap, msgs,
num);来实现数据传递,实际此处就是I2C总线驱动执行的入口,相应算法结构体函数的赋值会在总线驱动的探测函数中执行,后面会讲述。

算法结构体赋值如下:

1.  static struct i2c_algorithm i2c_gsc_algo = {

2.      .master_xfer = i2c_gsc_xfer,

3.      .functionality = i2c_gsc_func,

4.  };

i2c_gsc_func()函数实现的就是总线驱动支持的操作,程序如下:

1.  static u32 i2c_gsc_func(struct i2c_adapter *adap)

2.  {

3.

4.      return I2C_FUNC_I2C |

5.          I2C_FUNC_10BIT_ADDR |

6.          I2C_FUNC_SMBUS_BYTE |

7.          I2C_FUNC_SMBUS_BYTE_DATA |

8.          I2C_FUNC_SMBUS_WORD_DATA |

9.          I2C_FUNC_SMBUS_I2C_BLOCK;

10.                                      }

i2c_gsc_xfer()函数实现开始传输I2C数据,程序如下:

1.  static int i2c_gsc_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

2.  {

3.      struct gsc_i2c_dev *dev = i2c_get_adapdata(adap); //获取总线设备结构体,设置在probe函数中

4.      int ret;

5.      dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);

6.      //开始初始化变量,准备开始传输

7.      mutex_lock(&dev->lock);

8.      INIT_COMPLETION(dev->cmd_complete);

9.      dev->msgs = msgs;

10.            dev->msgs_num = num;

11.            dev->cmd_err = 0;

12.            dev->msg_write_idx = 0; //此变量用来标识传输到第几个dev->msgs,dev->msgs_num标识总共有几个msgs

13.            dev->msg_read_idx = 0;

14.            dev->msg_err = 0;

15.            dev->status = STATUS_IDLE;

16.            dev->abort_source = 0;

17.            ret = i2c_gsc_wait_bus_not_busy(dev); //查询总线是否空闲,只有空闲才开始传输

18.            if (ret < 0)

19.                goto done;

20.            /* start the transfers */

21.            i2c_gsc_xfer_init(dev); //设置传输模式,开启中断

22.            /* wait for tx to complete */

23.            ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ); //等待传输完成,中断中会设置

24.            if (ret == 0) {

25.                dev_err(dev->dev, "controller timed out\n");

26.                i2c_gsc_init(dev);

27.                ret = -ETIMEDOUT;

28.                goto done;

29.            } else if (ret < 0)

30.                goto done;

31.            if (dev->msg_err) {

32.                ret = dev->msg_err;

33.                goto done;

34.            }

35.            /* no error */

36.            if (likely(!dev->cmd_err)) {

37.                /* Disable the adapter */

38.                writel(0, dev->base + GSC_IC_ENABLE);

39.                ret = num;

40.                goto done;

41.            }

42.            /* We have an error */

43.            if (dev->cmd_err == GSC_IC_ERR_TX_ABRT) {

44.                ret = i2c_gsc_handle_tx_abort(dev);

45.                goto done;

46.            }

47.            ret = -EIO;

48.        done:

49.            mutex_unlock(&dev->lock);

50.            return ret;

51.        }

从以上函数看出,当执行完此函数后,中断打开,实际的传输在中断中完成。

中断号和申请中断函数在总线驱动的probe函数中完成,最后会讲述。接下来就看下中断函数i2c_gsc_isr:

1.  static irqreturn_t i2c_gsc_isr(int this_irq, void *dev_id)

2.  {

3.

4.      struct gsc_i2c_dev *dev = dev_id;

5.      u32 stat;

6.      stat = i2c_gsc_read_clear_intrbits(dev); //清除中断标志位

7.      dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);

8.      if (stat & GSC_IC_INTR_TX_ABRT) {

9.          dev->cmd_err |= GSC_IC_ERR_TX_ABRT;

10.            dev->status = STATUS_IDLE;

11.            /*

12.

13.             * Anytime TX_ABRT is set, the contents of the tx/rx

14.

15.             * buffers are flushed. Make sure to skip them.

16.

17.             */

18.            writel(0, dev->base + GSC_IC_INTR_MASK); //如果是传输终止则清除所有中断

19.            goto tx_aborted;

20.        }

21.        if (stat & GSC_IC_INTR_RX_FULL)

22.            i2c_gsc_read(dev); //接收fifo满中断,读取数据

23.        if (stat & GSC_IC_INTR_TX_EMPTY)

24.            i2c_gsc_xfer_msg(dev); //发送fifo空中断,发送数据

25.        /*

26.         * No need to modify or disable the interrupt mask here.

27.         * i2c_gsc_xfer_msg() will take care of it according to

28.         * the current transmit status.

29.         */

30.    tx_aborted:

31.        if ((stat & (GSC_IC_INTR_TX_ABRT | GSC_IC_INTR_STOP_DET)) || dev->msg_err)

32.   
        complete(&dev->cmd_complete); //发送错误或者发送终止,完成事件,对应上面的wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);

33.        return IRQ_HANDLED;

34.    }

接下来看下:接收fifo满中断,读取数据函数:i2c_gsc_read()

1.     static void i2c_gsc_read(struct gsc_i2c_dev *dev)

2.     {

3.         struct i2c_msg *msgs = dev->msgs;

4.         int rx_valid;

5.         for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {

6.             u32 len;

7.             u8 *buf;

8.             if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))

9.                 continue;

10.            if (!(dev->status & STATUS_READ_IN_PROGRESS)) {

11.                //第一次开始读,设置长度和存储数组地址

12.                len = msgs[dev->msg_read_idx].len;

13.                buf = msgs[dev->msg_read_idx].buf;

14.            } else {

15.                /* 注意此处,如果是第一次开始读,读的长度和存储数组都放在结构体dev->msgs中,如果不是

16.                第一次读,长度和存储数组放在dev->rx_buf_len和dev->rx_buf中,在本函数最后会判断一次是否能够

17.                读完全,如果不完全,则更新dev->rx_buf_len和dev->rx_buf。*/

18.                len = dev->rx_buf_len;

19.                buf = dev->rx_buf;

20.            }

21.            rx_valid = readl(dev->base + GSC_IC_RXFLR); //读取接收fifo里数据长度

22.            for (; len > 0 && rx_valid > 0; len--, rx_valid--)

23.                *buf++ = readl(dev->base + GSC_IC_DATA_CMD); //读取数据

24.            if (len > 0) {

25.                //如果没有读取完成,设置状态位,更新变量,和上面红色的呼应

26.                dev->status |= STATUS _READ_IN_PROGRESS;

27.                dev->rx_buf_len = len;

28.                dev->rx_buf = buf;

29.                return;

30.            } else

31.                dev->status &= ~STATUS_READ_IN_PROGRESS; //一次读取完成

32.        }

33.    }

发送fifo空中断,发送数据函数i2c_gsc_xfer_msg:

1.  static void i2c_gsc_xfer_msg(struct gsc_i2c_dev *dev)

2.  {

3.      struct i2c_msg *msgs = dev->msgs;

4.      u32 intr_mask;

5.      int tx_limit, rx_limit;

6.      u32 addr = msgs[dev->msg_write_idx].addr;

7.      u32 buf_len = dev->tx_buf_len;

8.      u8 *buf = dev->tx_buf;

9.      intr_mask = GSC_IC_INTR_DEFAULT_MASK; //设置默认屏蔽位

10.          //使用dev->msg_write_idx标识传输第几个msgs

11.          for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {

12.              /*

13.               * if target address has changed, we need to

14.               * reprogram the target address in the i2c

15.               * adapter when we are done with this transfer

16.               */

17.              //两次传输地址不一样,退出

18.              if (msgs[dev->msg_write_idx].addr != addr) {

19.                  dev_err(dev->dev,

20.                      "%s: invalid target address\n", __func__);

21.                  dev->msg_err = -EINVAL;

22.                  break;

23.              }

24.              //传输长度为0,退出

25.              if (msgs[dev->msg_write_idx].len == 0) {

26.                  dev_err(dev->dev,

27.                      "%s: invalid message length\n", __func__);

28.                  dev->msg_err = -EINVAL;

29.                  break;

30.              }

31.              //如果是第一次传输,设置传输长度和数组地址

32.              if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {

33.                  /* new i2c_msg */

34.                  buf = msgs[dev->msg_write_idx].buf;

35.                  buf_len = msgs[dev->msg_write_idx].len;

36.              }

37.              tx_limit = dev->tx_fifo_depth - readl(dev->base + GSC_IC_TXFLR); //计算可以往寄存器里写几个数据

38.              rx_limit = dev->rx_fifo_depth - readl(dev->base + GSC_IC_RXFLR); //计算可以从寄存器里读几个数据

39.              while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {

40.                  u32 cmd = 0;

41.                  if((dev->msg_write_idx == dev->msgs_num-1) && buf_len == 1)

42.                      cmd |= 0x200; //最后一次传输,设置寄存器发送stop信号

43.                  if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {

44.                      writel(cmd|0x100, dev->base + GSC_IC_DATA_CMD); //写命令,此处为读

45.                      rx_limit--;

46.                  } else

47.                      writel(cmd|*buf++, dev->base + GSC_IC_DATA_CMD); //写数据

48.                  tx_limit--; buf_len--;

49.              }

50.              //更新变量

51.              dev->tx_buf = buf;

52.              dev->tx_buf_len = buf_len;

53.              if (buf_len > 0) {

54.                  /* more bytes to be written */

55.                  dev->status |= STATUS_WRITE_IN_PROGRESS;

56.                  break;

57.              } else

58.                  dev->status &= ~STATUS_WRITE_IN_PROGRESS; //读写完成

59.          }

60.          /*

61.           * If i2c_msg index search is completed, we don't need TX_EMPTY

62.           * interrupt any more.

63.           */

64.          if (dev->msg_write_idx == dev->msgs_num)

65.              intr_mask &= ~GSC_IC_INTR_TX_EMPTY; //如果写完成,屏蔽发送中断

66.          if (dev->msg_err)

67.              intr_mask = 0; //如果出现错误,屏蔽所有中断

68.          writel(intr_mask, dev->base + GSC_IC_INTR_MASK); //写屏蔽寄存器

69.      }

到这里就讲述完成了I2C数据传输中总线驱动部分,接下来讲述总线驱动中的注册和探测函数。

五、总线驱动注册和探测函数

和其他总线驱动类似,I2C总线驱动注册成平台设备,所以首先需要定义平台设备,包括寄存器的起始地址和大小,中断信息等。

接下来就是总线驱动模块的注册和移除了,如下:

1.  static int __init gsc_i2c_init_driver(void)

2.  {

3.      return platform_driver_probe(&gsc_i2c_driver, gsc_i2c_probe);

4.  }

5.  static void __exit gsc_i2c_exit_driver(void)

6.  {

7.      platform_driver_unregister(&gsc_i2c_driver);

8.  }

9.  module_init(gsc_i2c_init_driver);

10.    module_exit(gsc_i2c_exit_driver);

平台设备驱动的结构体如下:

1.  static struct platform_driver gsc_i2c_driver = {

2.      .remove = __devexit_p(gsc_i2c_remove),

3.      .driver = {

4.          .name = "XXXX-i2c",

5.          .owner = THIS_MODULE,

6.      },

7.  };

接下来就看下I2C总线驱动的探测函数gsc_i2c_probe:

1.  static int __devinit gsc_i2c_probe(struct platform_device *pdev)

2.  {

3.      struct gsc_i2c_dev *dev;

4.      struct i2c_adapter *adap;

5.      struct resource *mem, *ioarea;

6.      int irq, r;

7.      //申请设备资源

8.      /* NOTE: driver uses the static register mapping */

9.      mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);

10.      if (!mem) {

11.          dev_err(&pdev->dev, "no mem resource?\n");

12.          return -EINVAL;

13.      }

14.      irq = platform_get_irq(pdev, 0);

15.      if (irq < 0) {

16.          dev_err(&pdev->dev, "no irq resource?\n");

17.          return irq; /* -ENXIO */

18.      }

19.      ioarea = request_mem_region(mem->start, resource_size(mem),

20.              pdev->name);

21.      if (!ioarea) {

22.          dev_err(&pdev->dev, "I2C region already claimed\n");

23.          return -EBUSY;

24.      }

25.      //申请总线结构体变量

26.      dev = kzalloc(sizeof(struct gsc_i2c_dev), GFP_KERNEL);

27.      if (!dev) {

28.          r = -ENOMEM;

29.          goto err_release_region;

30.      }

31.      //初始化变量

32.      init_completion(&dev->cmd_complete);

33.      mutex_init(&dev->lock);

34.      dev->dev = get_device(&pdev->dev);

35.      dev->irq = irq;

36.      platform_set_drvdata(pdev, dev);

37.      dev->clk = clk_get(&pdev->dev, "i2c");

38.      if (IS_ERR(dev->clk)) {

39.          r = -ENODEV;

40.          goto err_free_mem;

41.      }

42.      clk_enable(dev->clk);

43.      dev->base = ioremap(mem->start, resource_size(mem));

44.      if (dev->base == NULL) {

45.          dev_err(&pdev->dev, "failure mapping io resources\n");

46.          r = -EBUSY;

47.          goto err_unuse_clocks;

48.      }

49.      //设置发送和接收fifo深度

50.      dev->tx_fifo_depth = 8;

51.      dev->rx_fifo_depth = 8;

52.      i2c_gsc_init(dev); //初始化I2C总线时钟

53.      writel(0, dev->base + GSC_IC_INTR_MASK); /* disable IRQ */

54.      r = request_irq(dev->irq, i2c_gsc_isr, IRQF_DISABLED, pdev->name, dev); //申请中断函数,上面已经讲述

55.      if (r) {

56.          dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);

57.          goto err_iounmap;

58.      }

59.      //设置I2C的adap

60.      adap = &dev->adapter;

61.      i2c_set_adapdata(adap, dev);

62.      adap->owner = THIS_MODULE;

63.      adap->class = I2C_CLASS_HWMON;

64.      strlcpy(adap->name, "BLX GSC3280 I2C adapter",

65.              sizeof(adap->name));

66.      adap->algo = &i2c_gsc_algo; //设置adap的算法,包括传输函数和支持的操作函数,本文开始已经讲述

67.      adap->dev.parent = &pdev->dev;

68.      adap->nr = pdev->id;

69.      r = i2c_add_numbered_adapter(adap); //增加适配器计数,后面讲述

70.      if (r) {

71.          dev_err(&pdev->dev, "failure adding adapter\n");

72.          goto err_free_irq;

73.      }

74.      return 0;

75.      //中途退出分支

76.  err_free_irq:

77.      free_irq(dev->irq, dev);

78.  err_iounmap:

79.      iounmap(dev->base);

80.  err_unuse_clocks:

81.      clk_disable(dev->clk);

82.      clk_put(dev->clk);

83.      dev->clk = NULL;

84.  err_free_mem:

85.      platform_set_drvdata(pdev, NULL);

86.      put_device(&pdev->dev);

87.      kfree(dev);

88.  err_release_region:

89.      release_mem_region(mem->start, resource_size(mem));

90.      return r;

91.  }

在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter()。由于在系统中可能存在多个adapter,因此将每一条I2C总线对应一个编号,下文中称为I2C总线号。这个总线号与PCI中的总线号不同。它和硬件无关,只是软件上便于区分而已。对于实际的设备,一条I2C总线就意味着CPU的一个I2C控制器,也对应着一个adapter结构体。

对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分配一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败。

1.  int i2c_add_adapter(struct i2c_adapter *adapter)

2.  {

3.      int id, res = 0;

4.  retry:

5.      if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

6.          return -ENOMEM;

7.      mutex_lock(&core_lock);

8.      /* "above" here means "above or equal to", sigh */

9.      res = idr_get_new_above(&i2c_adapter_idr, adapter,

10.                      __i2c_first_dynamic_bus_num, &id);

11.          mutex_unlock(&core_lock);

12.          if (res < 0) {

13.              if (res == -EAGAIN)

14.                  goto retry;

15.              return res;

16.

17.          }

18.          adapter->nr = id;

19.          return i2c_register_adapter(adapter);

20.      }

在这里涉及到一个idr结构。idr结构本来是为了配合page cache中的radix
tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存。避免在内存不够的时候出现问题。所以,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id。以后凭这个id就可以在idr中找到相对应的结构了。

注意一下idr_get_new_above(&i2c_adapter_idr,
adapter,__i2c_first_dynamic_bus_num,
&id)参数的含义,它是将adapter结构插入到i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num,然后将对应的id号存放在adapter->nr中。调用i2c_register_adapter(adapter)对这个adapter进一步注册。

1.   int i2c_add_numbered_adapter(struct i2c_adapter *adap)

2.   {

3.       int id;

4.       int status;

5.       if (adap->nr & ~MAX_ID_MASK)

6.           return -EINVAL;

7.   retry:

8.       if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)

9.           return -ENOMEM;

10.      mutex_lock(&core_lock);

11.      /* "above" here means "above or equal to", sigh;

12.       * we need the "equal to" result to force the result

13.       */

14.      status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);

15.      if (status == 0 && id != adap->nr) {

16.          status = -EBUSY;

17.          idr_remove(&i2c_adapter_idr, id);

18.      }

19.      mutex_unlock(&core_lock);

20.      if (status == -EAGAIN)

21.          goto retry;

22.      if (status == 0)

23.          status = i2c_register_adapter(adap);

24.      return status;

25.  }

对比一下就知道差别了,在这里它已经指定好了adapter->nr了。如果分配的id不和指定的相等,便返回错误。本文使用的注册函数即为i2c_add_numbered_adapter。

i2c_register_adapter()代码如下:

1.     static int i2c_register_adapter(struct i2c_adapter *adap)

2.     {

3.         int res = 0, dummy;

4.         mutex_init(&adap->bus_lock);

5.         mutex_init(&adap->clist_lock);

6.         INIT_LIST_HEAD(&adap->clients);

7.         mutex_lock(&core_lock);

8.         /* Add the adapter to the driver core.

9.         * If the parent pointer is not set up,

10.        * we add this adapter to the host bus.

11.        */

12.        if (adap->dev.parent == NULL) {

13.            adap->dev.parent = &platform_bus;

14.            pr_debug("I2C adapter driver [%s] forgot to specify "

15.                "physical device/n", adap->name);

16.        }

17.        sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);

18.        adap->dev.release = &i2c_adapter_dev_release;

19.        adap->dev.class = &i2c_adapter_class;

20.        res = device_register(&adap->dev);

21.        if (res)

22.            goto out_list;

23.        dev_dbg(&adap->dev, "adapter [%s] registered/n", adap->name);

24.        /* create pre-declared device nodes for new-style drivers */

25.        if (adap->nr < __i2c_first_dynamic_bus_num)

26.            i2c_scan_static_board_info(adap); //板级设备静态扫描,第二部分会讲述

27.        /* let legacy drivers scan this bus for matching devices */

28.        dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,

29.                    i2c_do_add_adapter);

30.    out_unlock:

31.        mutex_unlock(&core_lock);

32.        return res;

33.    out_list:

34.        idr_remove(&i2c_adapter_idr, adap->nr);

35.        goto out_unlock;

36.    }

首先对adapter和adapter中内嵌的struct
device结构进行必须的初始化,之后注册adapter内嵌的struct
device。在这里注意一下adapter->dev的初始化,它的类别为i2c_adapter_class,如果没有父结点,则将其父结点设为platform_bus.adapter->dev的名字,为i2c
+ 总线号。

原文请参见: http://blog.chinaunix.net/uid-25445243-id-3609731.html

i2c总线,核心,驱动详解的更多相关文章

  1. linux usb 驱动详解

    linux usb 驱动详解 USB 设备驱动代码通过urb和所有的 USB 设备通讯.urb用 struct urb 结构描述(include/linux/usb.h ). urb 以一种异步的方式 ...

  2. 25.Linux-Nor Flash驱动(详解)

    1.nor硬件介绍: 从原理图中我们能看到NOR FLASH有地址线,有数据线,它和我们的SDRAM接口相似,能直接读取数据,但是不能像SDRAM直接写入数据,需要有命令才行 1.1其中我们2440的 ...

  3. 16.Linux-LCD驱动(详解)

    在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构体: framebuffer_alloc(); 2) 设置fb_info 3) 设置硬件相关的操作 ...

  4. PHP的核心配置详解

    1.PHP核心配置详解 代码在不同的环境下执行的结果也会大有不同,可能就因为一个配置问题,导致一个非常高危的漏洞能够利用:也可能你已经找到的一个漏洞就因为你的配置问题,导致你鼓捣很久都无法构造成功的漏 ...

  5. Lucene系列六:Lucene搜索详解(Lucene搜索流程详解、搜索核心API详解、基本查询详解、QueryParser详解)

    一.搜索流程详解 1. 先看一下Lucene的架构图 由图可知搜索的过程如下: 用户输入搜索的关键字.对关键字进行分词.根据分词结果去索引库里面找到对应的文章id.根据文章id找到对应的文章 2. L ...

  6. MyBatis核心配置文件详解

    ------------------------siwuxie095                                     MyBatis 核心配置文件详解         1.核心 ...

  7. 使用VS2010编译MongoDB C++驱动详解

    最近为了解决IM消息记录的高速度写入.多文档类型支持的需求,决定使用MongoDB来解决. 考虑到MongoDB对VS版本要求较高,与我现有的VS版本不兼容,在leveldb.ssdb.redis.h ...

  8. 16.Linux-LCD驱动(详解)【转】

    转自:https://www.cnblogs.com/lifexy/p/7604011.html 在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构 ...

  9. Nginx 核心配置详解

    目录 Nginx 核心配置详解 Nginx 四层访问控制: Nginx账户认证功能: 自定义错误页面: 自定义访问日志: 检测文件是否存在: 长连接配置: 作为下载服务器配置: 作为上传服务器: 其他 ...

随机推荐

  1. linux服务器搭建

    centos7 java web项目环境搭配 2018年07月19日 17:20:21 阅读数:25 首先进行系统安装,此处不进行详细介绍,自行百度安装 一.配置ip地址信息 1.进入/etc/sys ...

  2. Linux系统挂载只读改成读写

    1.mount命令可用于查看哪个模块输入只读,一般显示为: [root@localhost ~]# mount /dev/cciss/c0d0p2 on / type ext3 (rw) proc o ...

  3. Mysql在Linux的基本操作文档

    总结了Mysql在Linux下的应用,以下是Linux操作系统操作MySQL常用命令小结,需要的朋友参考下: 1.Mysql服务 # chkconfig --list 列出所有系统服务 # chkco ...

  4. php使用sftp上传文件

    搞这个SFTP文件传输搞了一整天真是醉了,从sftp安装,到php的ssh2扩展安装,最后到php应用ssh2来上传文件:最后就没有最后了 Failure creating remote file: ...

  5. Linux下Python2升级Python3

    Linux下Python2的升级方法: 一.下载Python3安装包: 1.在线下载 wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2 ...

  6. mysql 事务中如果有sql语句出错,会导致自动回滚吗?

    事务,我们都知道具有原子性,操作要么全部成功,要么全部失败.但是有可能会造成误解. 我们先准备一张表,来进行测试 CREATE TABLE `name` ( `id` int(11) unsigned ...

  7. php使用pthreads v3多线程的抓取新浪新闻信息

    我们使用pthreads,来写一个多线程的抓取页面小程序,把结果存到数据库里. 数据表结构如下: CREATE TABLE `tb_sina` ( `id` int(11) unsigned NOT ...

  8. C#使用MiniDump捕获异常

    c/c++语言里MiniDump是一个重要的调试手段,他们没有C#/java这样语言有很多异常输出信息( JVM异常导出bug日志功能,通常在jdk目录,文件格式hs_err_%pid%.log,pi ...

  9. Linux 编译时内存不足

    1.编译内核出现问题:No space left on device AS      .tmp_kallsyms1.o .tmp_kallsyms1.S:2: fatal error: when wr ...

  10. mysql utf8mb4

    遇到的问题 有一个项目需要存储 emoji 表情.另外极少数中文在存储的时候也遇到 utf8 编码格式的数据库在储存时报错. Rails creating schema_migrations - My ...