linux下I2C驅動

默認情況下,I2C以從模式工作。接口在生成起始位後,會自動由從模式切換到主模式,並在仲裁丟失或生成停止位時從主模式切換爲主模式。

在主模式下,I2C接口會啓動數據傳輸並生成時鐘信號。

任何能夠進行發送和接收的設備都可以成爲主設備。一個主控制器能控制信號的傳輸和時鐘頻率,當然在任何時間點上只能有一個主控制器。

空閒狀態:I2C總線的SDA和SCL兩條信號線同時處於高電平。

進行數據傳輸時,在SCL呈現高電平期間,SDA上電平必須保持穩定,低電平爲數據0,高電平爲數據1.

I2C總線上所有數據都是以8字節傳送的,發送器每發送一個字節,就在時鐘脈衝期間釋放數據線,由接收器反饋一個應答信號。

I2C核心:I2C提供了I2C總線驅動和設備驅動的註冊和註銷方法,I2C通信方法(algorithm) 上層的,與具體適配器無關的代碼以及探測設備,檢測設備地址的上層代碼

I2C總線:I2C總線驅動是對I2C硬件體系結構中適配器端的實現,適配器可由CPU控制,其可直接集成在CPU內部。

一個I2C適配器需要i2c_algorithm中提供的通信函數來控制適配器上產生的特定的訪問週期。

master_xfer();用於產生i2c訪問週期需要的start,stop,ack信號.

i2c_scan_static_borad_info()爲整個I2C子系統的核心,會去遍歷一個由I2C設備組成的雙向循環鏈表,並完成所有I2C從設備的i2c_client的註冊。

i2c從設備主要完成三大任務

系統初始化時添加i2c_board_info爲結構的i2c從設備的信息

在i2c從設備驅動裏使用i2c_adapter提供的算法實現i2c通信

將i2c從設備特有數據結構掛載到i2c_client->driver_data


i2c_adapter對應i2c總線控制器

 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along
  * with the access algorithms necessary to access it.
  */
 struct i2c_adapter {
         struct module *owner;
         unsigned int class;               /* classes to allow probing for */
         const struct i2c_algorithm *algo; /* the algorithm to access the bus */
         void *algo_data;
 
         /* data fields that are valid for all devices   */
         struct rt_mutex bus_lock;
 
         int timeout;                    /* in jiffies */
         int retries;
         struct device dev;              /* the adapter device */
 
         int nr;
         char name[48];
         struct completion dev_released;
 
         struct mutex userspace_clients_lock;
         struct list_head userspace_clients;
 };
i2c_client對應真實的i2c物理設備 

 /**
  * struct i2c_client - represent an I2C slave device
  * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
  *      I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
  * @addr: Address used on the I2C bus connected to the parent adapter.
  * @name: Indicates the type of the device, usually a chip name that's
  *      generic enough to hide second-sourcing and compatible revisions.
  * @adapter: manages the bus segment hosting this I2C device
  * @driver: device's driver, hence pointer to access routines
  * @dev: Driver model device node for the slave.
  * @irq: indicates the IRQ generated by this device (if any)
  * @detected: member of an i2c_driver.clients list or i2c-core's
  *      userspace_devices list
  *
  * An i2c_client identifies a single device (i.e. chip) connected to an
  * i2c bus. The behaviour exposed to Linux is defined by the driver
  * managing the device.
  */
 struct i2c_client {
         unsigned short flags;           /* div., see below              */
         unsigned short addr;            /* chip address - NOTE: 7bit    */
                                         /* addresses are stored in the  */
                                         /* _LOWER_ 7 bits               */
         char name[I2C_NAME_SIZE];
         struct i2c_adapter *adapter;    /* the adapter we sit on        */
         struct i2c_driver *driver;      /* and our access routines      */
         struct device dev;              /* the device structure         */
         int irq;                        /* irq issued by device         */
         struct list_head detected;
 };

i2c_driver和i2c_client發生關聯的時刻在i2c_driver的i2c_attach_adapter()函數運行時。

當一個具體的client被偵測到並被關聯的時候,設備和sysfs文件將被註冊。

說起Linux下的子系統,I2C子系統已經算是比較簡單的框架了。

  首先相關代碼在driver/i2c下

.built-in.o.cmd       .i2c-dev.o.cmd        algos/                i2c-boardinfo.c       i2c-core.h            i2c-dev.o     muxes/.i2c-boardinfo.o.cmd  Kconfig               built-in.o            i2c-boardinfo.o       i2c-core.o            i2c-mux.c           .i2c-core.o.cmd       Makefile              busses/               i2c-core.c            i2c-dev.c             i2c-smbus.c    

i2c-dev是用戶態直接相關聯的,裏面有相關文件(linux中設備即文件)操作的函數,最後依然會調用到i2c-core(是硬件層與系統調用層的中間緩衝層)的函數(i2c_master_send,i2c_master_recv最終還是調用i2c_transfer),i2c默認的主設備號爲89.

 static int __init i2c_dev_init(void)
 {
         int res;
 
         printk(KERN_INFO "i2c /dev entries driver\n");
 
         res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
         if (res)
                 goto out;

         i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
         if (IS_ERR(i2c_dev_class)) {
                 res = PTR_ERR(i2c_dev_class);
                 goto out_unreg_chrdev;
         }
 
         /* Keep track of adapters which will be added or removed later */
         res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
         if (res)
                 goto out_unreg_class;
 
         /* Bind to already existing adapters right away */
         i2c_for_each_dev(NULL, i2cdev_attach_adapter);
 
         return 0;
 
 out_unreg_class:
         class_destroy(i2c_dev_class);
 out_unreg_chrdev:
         unregister_chrdev(I2C_MAJOR, "i2c");
 out:
         printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
         return res;
 }
在i2c-core中最後還是調用我們在busses/下寫的i2c控制器模塊中的傳輸函數(回調) ,在xxx_i2c_probe中,初始化一些硬件環境(中斷,內存空間,時鐘),i2c控制器抽象成i2c_adapter,最後調用i2c_add_numbered_adapter().註冊到i2c子系統中。在該函數中調用i2c_register_adapter(),最終還是調用device_register().創建設備節點。其中之前一直很讓我困惑的是節點設備的名字,原來在子系統中已經將設備節點的名字封裝好,不需要我們在去命名,注意這裏的節點設備名字和platform_device的名字是可以不相同的。

如果register成功就調用另一個核心的函數:

          /* create pre-declared device nodes */
          if (adap->nr < __i2c_first_dynamic_bus_num)
                  i2c_scan_static_board_info(adap);

掃描之前靜態註冊的I2C控制器設備,代碼在(arch/xxx_cpu/xxx_sys/i2c_board_info.c)中:

關鍵函數在:

  static int __init xxx_i2c_board_init(void)
  {
          i2c_register_board_info(0,xxx_i2c_1_board_info,ARRAY_SIZE(xxx_i2c_1_board_info));
          i2c_register_board_info(1,xxx_i2c_2_board_info,ARRAY_SIZE(xxx_i2c_2_board_info));
          i2c_register_board_info(2,xxx_i2c_3_board_info,ARRAY_SIZE(xxx_i2c_3_board_info));
          i2c_register_board_info(3,xxx_i2c_4_board_info,ARRAY_SIZE(xxx_i2c_4_board_info));
          return 0;
  }
如果註冊總線號和i2c控制器的number相同則又要創建一個註冊新的i2c設備i2c_new_device()//??也不知道爲什麼


最關鍵的是是i2c通信過程。主要在xxx_i2c_probe()中dev->adap->algo = &i2c_xxx_algo;

 static struct i2c_algorithm i2c_xxx_algo = {
         .master_xfer    = i2c_xxx_xfer,
         .functionality  = i2c_xxx_func,
 };
最終i2c-core中的i2c_transfer()會調用i2c_xxx_xfer();裏面主要對I2C控制器芯片的寄存器操作。

信息的內容被封裝成i2c_message;

 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                  */
 };
接下來就是I2C測試程序的編寫了,我們使用的是eeprom,因爲是支持i2c接口,所以向i2c從設備地址寫入若干數值,再從該地址讀取數值,如果寫入和讀出是相同的,那麼就代表i2c驅動是對的。


編寫具體的i2c驅動

提供i2c適配器的硬件驅動,探測,初始化i2c適配器(申請i2c,I/O地址和中斷號),驅動cpu控制的i2c適配器從硬件上產生。

提供i2c控制的algorithm(xxx_tranfer)

實現i2c設備驅動中的i2c_driver接口,只是實現設備與總線的連接

實現i2c設備對應類型的具體驅動

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章