一、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,其定義如下:
點擊(此處)折疊或打開
struct i2c_msg?{???????__u16 addr;?/*?slave address*/???????__u16 flags;#define I2C_M_TEN 0x0010?/*?this?is?a ten bit chip address?*/#define I2C_M_RD 0x0001?/*?read data,?from slave?to?master?*/#define I2C_M_NOSTART 0x4000?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/#define I2C_M_REV_DIR_ADDR 0x2000?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/#define I2C_M_IGNORE_NAK 0x1000?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/#define I2C_M_NO_RD_ACK 0x0800?/*?if?I2C_FUNC_PROTOCOL_MANGLING?*/#define I2C_M_RECV_LEN 0x0400?/*?length will be first received byte?*/???????__u16?len;?/*?msg length?*/???????__u8?*buf;?/*?pointer?to?msg data?*/}; ??????? 它是實際傳輸的數據,其中包括了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相關操作
?
點擊(此處)折疊或打開
int?i2c_add_adapter(struct i2c_adapter?*adap);?//增加adapterint?i2c_del_adapter(struct i2c_adapter?*adap);int?i2c_register_driver(struct module?*,?struct i2c_driver?*);?//增加驅動?(i2c_add_driver)int?i2c_del_driver(struct i2c_driver?*driver);int?i2c_attach_client(struct i2c_client?*client);?//增加clientint?i2c_detach_client(struct i2c_client?*client); ????????2)I2C傳輸,發送和接收
點擊(此處)折疊或打開
int?i2c_transfer(struct i2c_adapter?*?adap,?struct i2c_msg?*msgs,?int?num);int?i2c_master_send(struct i2c_client?*client,const?char?*buf?,int?count);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如下定義:
點擊(此處)折疊或打開
struct i2c_algorithm?{????int?(*master_xfer)(struct i2c_adapter?*adap,?struct i2c_msg?*msgs,????????????????????????????int?num);????int?(*smbus_xfer)?(struct i2c_adapter?*adap,?u16 addr,????????????????????????????unsigned short flags,?char read_write,????????????????????????????u8 command,?int?size,?union i2c_smbus_data?*data);????u32?(*functionality)?(struct i2c_adapter?*);????} ? ?? ? 根據定義主要要實現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總線驅動執行的入口,相應算法結構體函數的賦值會在總線驅動的探測函數中執行,后面會講述。
??????? 算法結構體賦值如下:
點擊(此處)折疊或打開
static struct i2c_algorithm i2c_gsc_algo?=?{????.master_xfer?=?i2c_gsc_xfer,????.functionality?=?i2c_gsc_func,}; ????????i2c_gsc_func()函數實現的就是總線驅動支持的操作,程序如下:
點擊(此處)折疊或打開
static u32 i2c_gsc_func(struct i2c_adapter?*adap){ ????return I2C_FUNC_I2C?|????????I2C_FUNC_10BIT_ADDR?|????????I2C_FUNC_SMBUS_BYTE?|????????I2C_FUNC_SMBUS_BYTE_DATA?|????????I2C_FUNC_SMBUS_WORD_DATA?|????????I2C_FUNC_SMBUS_I2C_BLOCK;} ????????i2c_gsc_xfer()函數實現開始傳輸I2C數據,程序如下:
點擊(此處)折疊或打開
static?int?i2c_gsc_xfer(struct i2c_adapter?*adap,?struct i2c_msg msgs[],?int?num){????struct gsc_i2c_dev?*dev?=?i2c_get_adapdata(adap);?//獲取總線設備結構體,設置在probe函數中????int?ret;????dev_dbg(dev->dev,?"%s: msgs: %d\n",?__func__,?num);????//開始初始化變量,準備開始傳輸????mutex_lock(&dev->lock);????INIT_COMPLETION(dev->cmd_complete);????dev->msgs?=?msgs;????dev->msgs_num?=?num;????dev->cmd_err?=?0;????dev->msg_write_idx?=?0;?//此變量用來標識傳輸到第幾個dev->msgs,dev->msgs_num標識總共有幾個msgs????dev->msg_read_idx?=?0;????dev->msg_err?=?0;????dev->status?=?STATUS_IDLE;????dev->abort_source?=?0;????ret?=?i2c_gsc_wait_bus_not_busy(dev);?//查詢總線是否空閑,只有空閑才開始傳輸?????if?(ret?<?0)????????goto done;????/*?start the transfers?*/????i2c_gsc_xfer_init(dev);?//設置傳輸模式,開啟中斷?????/*?wait?for?tx?to?complete?*/????ret?=?wait_for_completion_interruptible_timeout(&dev->cmd_complete,?HZ);?//等待傳輸完成,中斷中會設置????if?(ret?==?0)?{????????dev_err(dev->dev,?"controller timed out\n");????????i2c_gsc_init(dev);????????ret?=?-ETIMEDOUT;????????goto done;????}?else?if?(ret?<?0)????????goto done;????if?(dev->msg_err)?{????????ret?=?dev->msg_err;????????goto done;????}????/*?no?error?*/????if?(likely(!dev->cmd_err))?{????????/*?Disable the adapter?*/????????writel(0,?dev->base?+?GSC_IC_ENABLE);????????ret?=?num;????????goto done;????}????/*?We have an?error?*/????if?(dev->cmd_err?==?GSC_IC_ERR_TX_ABRT)?{????????ret?=?i2c_gsc_handle_tx_abort(dev);????????goto done;????}????ret?=?-EIO;done:????mutex_unlock(&dev->lock);????return ret;} ????????從以上函數看出,當執行完此函數后,中斷打開,實際的傳輸在中斷中完成。
??????? 中斷號和申請中斷函數在總線驅動的probe函數中完成,最后會講述。接下來就看下中斷函數i2c_gsc_isr:
點擊(此處)折疊或打開
static irqreturn_t i2c_gsc_isr(int?this_irq,?void?*dev_id){ ????struct gsc_i2c_dev?*dev?=?dev_id;??? u32 stat;????stat?=?i2c_gsc_read_clear_intrbits(dev);?//清除中斷標志位?????dev_dbg(dev->dev,?"%s: stat=0x%x\n",?__func__,?stat);????if?(stat?&?GSC_IC_INTR_TX_ABRT)?{????????dev->cmd_err?|=?GSC_IC_ERR_TX_ABRT;????????dev->status?=?STATUS_IDLE;????????/* ?????????*?Anytime TX_ABRT?is?set,?the contents of the tx/rx ?????????*?buffers are flushed.?Make sure?to?skip them. ?????????*/????????writel(0,?dev->base?+?GSC_IC_INTR_MASK);?//如果是傳輸終止則清除所有中斷?????????goto tx_aborted;????}????if?(stat?&?GSC_IC_INTR_RX_FULL)????????i2c_gsc_read(dev);?//接收fifo滿中斷,讀取數據????if?(stat?&?GSC_IC_INTR_TX_EMPTY)????????i2c_gsc_xfer_msg(dev);?//發送fifo空中斷,發送數據?????/*?????*?No need?to?modify?or?disable the interrupt mask here.?????*?i2c_gsc_xfer_msg()?will take care of it according?to?????*?the current transmit status.?????*/tx_aborted:????if?((stat?&?(GSC_IC_INTR_TX_ABRT?|?GSC_IC_INTR_STOP_DET))?||?dev->msg_err)????????complete(&dev->cmd_complete);?//發送錯誤或者發送終止,完成事件,對應上面的wait_for_completion_interruptible_timeout(&dev->cmd_complete,?HZ);????return IRQ_HANDLED;} ??????? 接下來看下:接收fifo滿中斷,讀取數據函數:i2c_gsc_read()
點擊(此處)折疊或打開
static void i2c_gsc_read(struct gsc_i2c_dev?*dev){????struct i2c_msg?*msgs?=?dev->msgs;????int?rx_valid;????for?(;?dev->msg_read_idx?<?dev->msgs_num;?dev->msg_read_idx++)?{????????u32?len;????????u8?*buf;????????if?(!(msgs[dev->msg_read_idx].flags?&?I2C_M_RD))????????????continue;????????if?(!(dev->status?&?STATUS_READ_IN_PROGRESS))?{????????????//第一次開始讀,設置長度和存儲數組地址????????????len?=?msgs[dev->msg_read_idx].len;????????????buf?=?msgs[dev->msg_read_idx].buf;????????}?else?{????????????/*?注意此處,如果是第一次開始讀,讀的長度和存儲數組都放在結構體dev->msgs中,如果不是????????????第一次讀,長度和存儲數組放在dev->rx_buf_len和dev->rx_buf中,在本函數最后會判斷一次是否能夠????????????讀完全,如果不完全,則更新dev->rx_buf_len和dev->rx_buf。*/????????????len?=?dev->rx_buf_len;????????????buf?=?dev->rx_buf;????????}????????rx_valid?=?readl(dev->base?+?GSC_IC_RXFLR);?//讀取接收fifo里數據長度?????????for?(;?len?>?0?&&?rx_valid?>?0;?len--,?rx_valid--)????????????*buf++?=?readl(dev->base?+?GSC_IC_DATA_CMD);?//讀取數據????????if?(len?>?0)?{????????????//如果沒有讀取完成,設置狀態位,更新變量,和上面紅色的呼應????????????dev->status?|=?STATUS _READ_IN_PROGRESS;????????????dev->rx_buf_len?=?len;????????????dev->rx_buf?=?buf;????????????return;????????}?else????????????dev->status?&=?~STATUS_READ_IN_PROGRESS;?//一次讀取完成????}} ????????發送fifo空中斷,發送數據函數i2c_gsc_xfer_msg:
點擊(此處)折疊或打開
static void i2c_gsc_xfer_msg(struct gsc_i2c_dev?*dev){????struct i2c_msg?*msgs?=?dev->msgs;????u32 intr_mask;????int?tx_limit,?rx_limit;????u32 addr?=?msgs[dev->msg_write_idx].addr;????u32 buf_len?=?dev->tx_buf_len;????u8?*buf?=?dev->tx_buf;????intr_mask?=?GSC_IC_INTR_DEFAULT_MASK;?//設置默認屏蔽位????//使用dev->msg_write_idx標識傳輸第幾個msgs????for?(;?dev->msg_write_idx?<?dev->msgs_num;?dev->msg_write_idx++)?{????????/*?????????*?if?target address has changed,?we need?to?????????*?reprogram the target address?in?the i2c?????????*?adapter when we are done with this transfer?????????*/????????//兩次傳輸地址不一樣,退出????????if?(msgs[dev->msg_write_idx].addr?!=?addr)?{????????????dev_err(dev->dev,????????????????"%s: invalid target address\n",?__func__);????????????dev->msg_err?=?-EINVAL;????????????break;????????}????????//傳輸長度為0,退出????????if?(msgs[dev->msg_write_idx].len?==?0)?{????????????dev_err(dev->dev,????????????????"%s: invalid message length\n",?__func__);????????????dev->msg_err?=?-EINVAL;????????????break;????????}????????//如果是第一次傳輸,設置傳輸長度和數組地址????????if?(!(dev->status?&?STATUS_WRITE_IN_PROGRESS))?{????????????/*?new i2c_msg?*/????????????buf?=?msgs[dev->msg_write_idx].buf;????????????buf_len?=?msgs[dev->msg_write_idx].len;????????}????????tx_limit?=?dev->tx_fifo_depth?-?readl(dev->base?+?GSC_IC_TXFLR);?//計算可以往寄存器里寫幾個數據?????????rx_limit?=?dev->rx_fifo_depth?-?readl(dev->base?+?GSC_IC_RXFLR);?//計算可以從寄存器里讀幾個數據????????while?(buf_len?>?0?&&?tx_limit?>?0?&&?rx_limit?>?0)?{????????????u32 cmd?=?0;????????????if((dev->msg_write_idx?==?dev->msgs_num-1)?&&?buf_len?==?1)????????????????cmd?|=?0x200;?//最后一次傳輸,設置寄存器發送stop信號????????????if?(msgs[dev->msg_write_idx].flags?&?I2C_M_RD)?{????????????????writel(cmd|0x100,?dev->base?+?GSC_IC_DATA_CMD);?//寫命令,此處為讀????????????????rx_limit--;????????????}?else????????????????writel(cmd|*buf++,?dev->base?+?GSC_IC_DATA_CMD);?//寫數據????????????tx_limit--;?buf_len--;????????}????????//更新變量????????dev->tx_buf?=?buf;????????dev->tx_buf_len?=?buf_len;????????if?(buf_len?>?0)?{????????????/*?more bytes?to?be written?*/????????????dev->status?|=?STATUS_WRITE_IN_PROGRESS;????????????break;????????}?else????????????dev->status?&=?~STATUS_WRITE_IN_PROGRESS;?//讀寫完成?????}????/*?????*?If?i2c_msg index search?is?completed,?we don't need TX_EMPTY?????*?interrupt any more.?????*/????if?(dev->msg_write_idx?==?dev->msgs_num)????????intr_mask?&=?~GSC_IC_INTR_TX_EMPTY;?//如果寫完成,屏蔽發送中斷????if?(dev->msg_err)????????intr_mask?=?0;?//如果出現錯誤,屏蔽所有中斷????writel(intr_mask,?dev->base?+?GSC_IC_INTR_MASK);?//寫屏蔽寄存器} ??????? 到這里就講述完成了I2C數據傳輸中總線驅動部分,接下來講述總線驅動中的注冊和探測函數。
五、總線驅動注冊和探測函數
??????? 和其他總線驅動類似,I2C總線驅動注冊成平臺設備,所以首先需要定義平臺設備,包括寄存器的起始地址和大小,中斷信息等。
??????? 接下來就是總線驅動模塊的注冊和移除了,如下:
點擊(此處)折疊或打開
static?int?__init gsc_i2c_init_driver(void){????return platform_driver_probe(&gsc_i2c_driver,?gsc_i2c_probe);}static void __exit gsc_i2c_exit_driver(void){????platform_driver_unregister(&gsc_i2c_driver);}module_init(gsc_i2c_init_driver);module_exit(gsc_i2c_exit_driver); ??????? 平臺設備驅動的結構體如下:
點擊(此處)折疊或打開
static struct platform_driver gsc_i2c_driver?=?{????.remove?=?__devexit_p(gsc_i2c_remove),????.driver?=?{????????.name?=?"XXXX-i2c",????????.owner?=?THIS_MODULE,????},}; ??????? 接下來就看下I2C總線驅動的探測函數gsc_i2c_probe:
點擊(此處)折疊或打開
static?int?__devinit gsc_i2c_probe(struct platform_device?*pdev){????struct gsc_i2c_dev?*dev;????struct i2c_adapter?*adap;????struct resource?*mem,?*ioarea;????int?irq,?r;????//申請設備資源????/*?NOTE:?driver uses the static register mapping?*/????mem?=?platform_get_resource(pdev,?IORESOURCE_MEM,?0);????if?(!mem)?{????????dev_err(&pdev->dev,?"no mem resource?\n");????????return?-EINVAL;????}????irq?=?platform_get_irq(pdev,?0);????if?(irq?<?0)?{????????dev_err(&pdev->dev,?"no irq resource?\n");????????return irq;?/*?-ENXIO?*/????}????ioarea?=?request_mem_region(mem->start,?resource_size(mem),????????????pdev->name);????if?(!ioarea)?{????????dev_err(&pdev->dev,?"I2C region already claimed\n");????????return?-EBUSY;????}????//申請總線結構體變量????dev?=?kzalloc(sizeof(struct gsc_i2c_dev),?GFP_KERNEL);????if?(!dev)?{????????r?=?-ENOMEM;????????goto err_release_region;????}????//初始化變量????init_completion(&dev->cmd_complete);????mutex_init(&dev->lock);????dev->dev?=?get_device(&pdev->dev);????dev->irq?=?irq;????platform_set_drvdata(pdev,?dev);????dev->clk?=?clk_get(&pdev->dev,?"i2c");????if?(IS_ERR(dev->clk))?{????????r?=?-ENODEV;????????goto err_free_mem;????}????clk_enable(dev->clk);????dev->base?=?ioremap(mem->start,?resource_size(mem));????if?(dev->base?==?NULL)?{????????dev_err(&pdev->dev,?"failure mapping io resources\n");????????r?=?-EBUSY;????????goto err_unuse_clocks;????}????//設置發送和接收fifo深度????dev->tx_fifo_depth?=?8;????dev->rx_fifo_depth?=?8;????i2c_gsc_init(dev);?//初始化I2C總線時鐘????writel(0,?dev->base?+?GSC_IC_INTR_MASK);?/*?disable IRQ?*/????r?=?request_irq(dev->irq,?i2c_gsc_isr,?IRQF_DISABLED,?pdev->name,?dev);?//申請中斷函數,上面已經講述????if?(r)?{????????dev_err(&pdev->dev,?"failure requesting irq %i\n",?dev->irq);????????goto err_iounmap;????}????//設置I2C的adap????adap?=?&dev->adapter;????i2c_set_adapdata(adap,?dev);????adap->owner?=?THIS_MODULE;????adap->class?=?I2C_CLASS_HWMON;????strlcpy(adap->name,?"BLX GSC3280 I2C adapter",????????????sizeof(adap->name));????adap->algo?=?&i2c_gsc_algo;?//設置adap的算法,包括傳輸函數和支持的操作函數,本文 開始已經講述????adap->dev.parent?=?&pdev->dev;????adap->nr?=?pdev->id;????r?=?i2c_add_numbered_adapter(adap);?//增加適配器計數,后面講述????if?(r)?{????????dev_err(&pdev->dev,?"failure adding adapter\n");????????goto err_free_irq;????}????return 0;????//中途退出分支err_free_irq:????free_irq(dev->irq,?dev);err_iounmap:????iounmap(dev->base);err_unuse_clocks:????clk_disable(dev->clk);????clk_put(dev->clk);????dev->clk?=?NULL;err_free_mem:????platform_set_drvdata(pdev,?NULL);????put_device(&pdev->dev);????kfree(dev);err_release_region:????release_mem_region(mem->start,?resource_size(mem));????return r;} ????????在kernel中提供了兩個adapter注冊接口,分別為i2c_add_adapter()和 i2c_add_numbered_adapter()。由于在系統中可能存在多個adapter,因此將每一條I2C總線對應一個編號,下文中稱為 I2C總線號。這個總線號與PCI中的總線號不同。它和硬件無關,只是軟件上便于區分而已。對于實際的設備,一條I2C總線就意味著CPU的一個I2C控制器,也對應著一個adapter結構體。
????????對于i2c_add_adapter()而言,它使用的是動態總線號,即由系統給其分配一個總線號,而i2c_add_numbered_adapter()則是自己指定總線號,如果這個總線號非法或者是被占用,就會注冊失敗。
點擊(此處)折疊或打開
int?i2c_add_adapter(struct i2c_adapter?*adapter){????int?id,?res?=?0;retry:????if?(idr_pre_get(&i2c_adapter_idr,?GFP_KERNEL)?==?0)????????return?-ENOMEM;????mutex_lock(&core_lock);????/*?"above"?here means?"above or equal to",?sigh?*/????res?=?idr_get_new_above(&i2c_adapter_idr,?adapter,????????????????__i2c_first_dynamic_bus_num,?&id);????mutex_unlock(&core_lock);????if?(res?<?0)?{????????if?(res?==?-EAGAIN)????????????goto retry;????????return res; ????}????adapter->nr?=?id;????return i2c_register_adapter(adapter);} ?????????在這里涉及到一個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進一步注冊。
點擊(此處)折疊或打開
int?i2c_add_numbered_adapter(struct i2c_adapter?*adap){????int?id;????int?status;????if?(adap->nr?&?~MAX_ID_MASK)????????return?-EINVAL;retry:????if?(idr_pre_get(&i2c_adapter_idr,?GFP_KERNEL)?==?0)????????return?-ENOMEM;????mutex_lock(&core_lock);????/*?"above"?here means?"above or equal to",?sigh;?????*?we need the?"equal to"?result?to?force the result?????*/????status?=?idr_get_new_above(&i2c_adapter_idr,?adap,?adap->nr,?&id);????if?(status?==?0?&&?id?!=?adap->nr)?{????????status?=?-EBUSY;????????idr_remove(&i2c_adapter_idr,?id);????}????mutex_unlock(&core_lock);????if?(status?==?-EAGAIN)????????goto retry;????if?(status?==?0)????????status?=?i2c_register_adapter(adap);????return status;} ????????對比一下就知道差別了,在這里它已經指定好了adapter->nr了。如果分配的id不和指定的相等,便返回錯誤。本文使用的注冊函數即為i2c_add_numbered_adapter。
????????i2c_register_adapter()代碼如下:
點擊(此處)折疊或打開
static?int?i2c_register_adapter(struct i2c_adapter?*adap){????int?res?=?0,?dummy;????mutex_init(&adap->bus_lock);????mutex_init(&adap->clist_lock);????INIT_LIST_HEAD(&adap->clients);????mutex_lock(&core_lock);????/*?Add the adapter?to?the driver core.????*?If?the parent pointer?is?not?set?up,????*?we add this adapter?to?the host bus.????*/????if?(adap->dev.parent?==?NULL)?{????????adap->dev.parent?=?&platform_bus;????????pr_debug("I2C adapter driver [%s] forgot to specify "????????????"physical device/n",?adap->name);????}????sprintf(adap->dev.bus_id,?"i2c-%d",?adap->nr);????adap->dev.release?=?&i2c_adapter_dev_release;????adap->dev.class?=?&i2c_adapter_class;????res?=?device_register(&adap->dev);????if?(res)????????goto out_list;????dev_dbg(&adap->dev,?"adapter [%s] registered/n",?adap->name);????/*?create pre-declared device nodes?for?new-style drivers?*/????if?(adap->nr?<?__i2c_first_dynamic_bus_num)????????i2c_scan_static_board_info(adap);?//板級設備靜態掃描,第二部分會講述????/*?let?legacy drivers scan this bus?for?matching devices?*/????dummy?=?bus_for_each_drv(&i2c_bus_type,?NULL,?adap,????????????????i2c_do_add_adapter);out_unlock:????mutex_unlock(&core_lock);????return res;out_list:????idr_remove(&i2c_adapter_idr,?adap->nr);????goto out_unlock;} ????????首先對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
轉載于:https://www.cnblogs.com/isAndyWu/p/10292649.html
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的I2C 总线原理与架构的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。