1. 程式人生 > >I2C 總線原理與架構

I2C 總線原理與架構

alt har probe 調用 module 核心 enter 寄存器 target

一、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.1、IIC讀寫原理

IIC總線的開始/停止信號如圖1所示。開始信號為:時鐘信號線SCL為高電平,數據線SDA從高變低。停止信號為:時鐘信號線SCL為高電平,數據線SDA從低變高。

1.2、IIC總線Byte Write

IIC總線寫數據分幾種格式,如字節寫和頁寫。

字節寫傳送格式如圖2所示。開始信號之後,總線開始發數據,第一個Byte是IIC的設備地址,第二個Byte是設備內的地址(如EEPROM中具體的某個物理地址),然後就是要傳送的真正的數據DATA。

NOTE:IIC總線在傳送每個Byte後,都會從IIC總線上的接收設備得到一個ACK信號來確認接收到了數據。其中,第一個Byte的設備地址中,前7位是地址碼,第8位是方向位(“0”為發送,“1”為接收)。IIC的中斷信號有:ACK,Start,Stop。

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中斷,置位各寄存器。

NOTE:對於EEPROM,IICDS寄存器發送的數據會先放在Ring buffer中,當其收到stop信號時,開始實際寫入EEPROM中。在實際寫的過程中,EEPROM不響應從CPU來的信號,直到寫完才會響應,因而有一段延遲代碼。在page write時,註意一定要有延時!

NOTE:數據先寫到EEPROM的ring buffer中,收到Stop信號時,開始實際地把數據寫入EEPROM,這時不響應任何輸入。即這時Write函數中後面的延時中,向其發slvaddr時,不會得到ACK,直到數據寫完時,才會收到ACK。

1.3、IIC總線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.1、I2C驅動概述

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. return I2C_FUNC_I2C |
  4. I2C_FUNC_10BIT_ADDR |
  5. I2C_FUNC_SMBUS_BYTE |
  6. I2C_FUNC_SMBUS_BYTE_DATA |
  7. I2C_FUNC_SMBUS_WORD_DATA |
  8. I2C_FUNC_SMBUS_I2C_BLOCK;
  9. }

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. struct gsc_i2c_dev *dev = dev_id;
  4. u32 stat;
  5. stat = i2c_gsc_read_clear_intrbits(dev); //清除中斷標誌位
  6. dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat);
  7. if (stat & GSC_IC_INTR_TX_ABRT) {
  8. dev->cmd_err |= GSC_IC_ERR_TX_ABRT;
  9. dev->status = STATUS_IDLE;
  10. /*
  11. * Anytime TX_ABRT is set, the contents of the tx/rx
  12. * buffers are flushed. Make sure to skip them.
  13. */
  14. writel(0, dev->base + GSC_IC_INTR_MASK); //如果是傳輸終止則清除所有中斷
  15. goto tx_aborted;
  16. }
  17. if (stat & GSC_IC_INTR_RX_FULL)
  18. i2c_gsc_read(dev); //接收fifo滿中斷,讀取數據
  19. if (stat & GSC_IC_INTR_TX_EMPTY)
  20. i2c_gsc_xfer_msg(dev); //發送fifo空中斷,發送數據
  21. /*
  22. * No need to modify or disable the interrupt mask here.
  23. * i2c_gsc_xfer_msg() will take care of it according to
  24. * the current transmit status.
  25. */
  26. tx_aborted:
  27. if ((stat & (GSC_IC_INTR_TX_ABRT | GSC_IC_INTR_STOP_DET)) || dev->msg_err)
  28. complete(&dev->cmd_complete); //發送錯誤或者發送終止,完成事件,對應上面的wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
  29. return IRQ_HANDLED;
  30. }

接下來看下:接收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. adapter->nr = id;
  18. return i2c_register_adapter(adapter);
  19. }

在這裏涉及到一個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 + 總線號。

文章轉自:輝輝308 https://blog.csdn.net/apple_guet/article/details/21379425

I2C 總線原理與架構