I2C Subsystem
      In
 Linux 2.6.24

          Author:
          Varun Mahajan
          <varunmahajan06@gmail.com>
Contents
 ●
   Data structures representing I2C bus, device, driver, etc
 ●
   How to add an I2C device to the kernel
 ●
   What happens when a new instance of I2C bus is recognized by the
 kernel
 ●
   How to add an I2C device driver to the kernel
 ●
   Device ↔ Driver binding
Data Structures
                                                                                I2C



                                     Bus                                                                               Device



Driver                                                   Adapter                               Driver                                         Client


                                           struct i2c_adapter                         struct i2c_driver                         struct i2c_client


struct bus_type                            nr /*bus no*/                              (*probe) (i2c_client *)                   addr /*device address*/
                                                                                      (*remove) (i2c_client *)
                                           /*algorithm to access the bus*/                                                      *adapter
*name = “i2c”                              *algo                                      (*shutdown) (i2c_client *)                *driver
                                                                                      (*suspend) (i2c_client*, mesg)            driver_name
(*match) (device*, device_driver*)         (*client_register) (i2c_client*)           (*resume) (i2c_client *)                  irq
                                           (*client_unregister) (i2c_client*)
(*probe) (device *)
(*remove) (device *)                       /*list of clients*/
                                                                                                                                   struct device
                                           list_head clients                             struct device_driver
(*shutdown) (device *)
(*suspend) (device *, mesg)
(*resume) (device *)                                                                                                               *parent
                                                                                         *name                                     *driver
                                              struct device                              *bus                                      *bus
klist_devices
klist_drivers
                                                                                         kobj                                      /*driver/platform specific*/
                                                                                         klist_devices                             *driver_data
                                                                                         knode_bus                                 *platform_data

                                                                                                                                   kobj
                                                                                                                                   knode_bus
                                                                                                                                   knode_driver
Data Structures


             i2c_driver_1                  i2c_driver_2


             i2c_client_1                  i2c_client_2



                  D1                         D2



 I2C Bus 1                                                i2c_adapter_1

                                                                          I2C Bus Driver   bus_type

 I2C Bus 2                                                i2c_adapter_2



                                 D3



                            i2c_driver_3


                            i2c_client_3
How to add an I2C device to the Kernel
 Populate the __i2c_board_list in the board specific initialization code




          __i2c_board_list     List of struct i2c_devinfo



                             struct i2c_devinfo

                             busnum                         BUS_NO

                               struct
                                                            “KXSD9_driver”
                               i2c_board_info

                               driver_name
                                                            KXSD9_I2C_ADDR
                               addr

                               irq
                                                            KXSD9_IRQ
                               *platform_data

                                                             PLATFORM_DATA
When a new I2C Bus instance is recognized by Kernel

  1. A structure i2c_adapter is instantiated for the new bus instance




            struct i2c_adapter


            nr /*bus no*/                        BUS_NO

            /*algorithm to access the bus*/
            *algo

            (*client_register) (i2c_client*)
            (*client_unregister) (i2c_client*)

            /*list of clients*/
            list_head clients




               struct device
When a new I2C Bus instance is recognized by Kernel

  2. For this new adapter, the __i2c_board_list is scanned to check for
  devices whose bus nos match with the adapter’s bus no. If there is a
  match then a new i2c_client structure is created for that device



             struct i2c_client
                                                             struct i2c_devinfo
                                                BUS_NO
             addr /*device address*/
                                                             busnum
             *adapter
             *driver                   KXSD9_I2C_ADDR
             driver_name                                       struct
             irq                                               i2c_board_info
               struct device
                                                               driver_name
                                            “KXSD9_driver”

               *parent                                         addr
               *driver
               *bus                                            irq
                                            KXSD9_IRQ
               /*driver/platform                               *platform_data
               specific*/
               *driver_data
               *platform_data
                                            PLATFORM_DATA
               kobj
               knode_bus
               knode_driver
When a new I2C Bus instance is recognized by Kernel

  3. The Data structures are linked as shown below

         struct i2c_client                               struct i2c_adapter


         addr /*device address*/                         nr /*bus no*/

         *adapter                                        /*algorithm to access the bus*/
         *driver                   KXSD9_I2C_ADDR        *algo
         driver_name
         irq                                             (*client_register) (i2c_client*)
                                              BUS_NO     (*client_unregister) (i2c_client*)
           struct device
                                                         /*list of clients*/
                                        “KXSD9_driver”
                                                         list_head clients
           *parent
           *driver                                         struct device
           *bus
                                        KXSD9_IRQ
           /*driver/platform
           specific*/
           *driver_data                                    struct bus_type
           *platform_data

           kobj                                            *name = “i2c”

           knode_bus                                       (*match) (device*,
                                                           device_driver*)
           knode_driver
                                                           (*probe) (device *)
                                                           (*remove) (device *)

                                                           (*shutdown) (device *)
                                                           (*suspend) (device *, mesg)
                                                           (*resume) (device *)

                                                           klist_devices

                                                           klist_drivers
How to add an I2C device driver to the Kernel
  Populate the i2c_driver structure and define the relevant functions.
  Add this driver to the kernel through i2c_add_driver()




                struct i2c_driver
                                                   KXSD9_probe ( i2c_client *)
                (*probe) (i2c_client *)
                                                 KXSD9_remove ( i2c_client *)
                (*remove) (i2c_client *)
                                                 KXSD9_shutdown ( i2c_client *)
                (*shutdown) (i2c_client *)
                (*suspend) (i2c_client*, mesg)   KXSD9_suspend ( i2c_client *)
                (*resume) (i2c_client *)
                                                 KXSD9_resume ( i2c_client *)


                  struct device_driver


                  *name                            “I2C_driver”
                  *bus

                  kobj
                  klist_devices
                  knode_bus
Code Flow after i2c_add_driver()
Device ↔ Driver Binding
 • For this new i2c_driver the kernel scans the klist_devices of the
 i2c_bus_type structure. If an existing device’s i2c_client’s
 driver_name matches with the i2c_driver.driver’s name, then it calls
 i2c_bus_type.probe() for this matched device. I2c_bus_type.probe()
 internally calls the i2c_driver.probe(). If i2c_driver.probe() succeeds,
 the i2c_driver.driver is added to the klist_drivers of i2c_bus_type and
 the device<->driver are bound


 • i2c_driver.probe ( i2c_client * )
     – Store a reference to i2c_client* for future use
     – Initialize the device
struct i2c_client

                                KXSD9_I2C_ADDR
addr /*device address*/

*adapter
*driver                                                struct i2c_adapter
driver_name
irq                                 BUS_NO
                                                       nr /*bus no*/
  struct device
                                                       /*algorithm to access the bus*/
                                  “KXSD9_driver”       *algo
  *parent
  *driver                                              (*client_register) (i2c_client*)
  *bus                                                 (*client_unregister) (i2c_client*)

                                  KXSD9_IRQ            /*list of clients*/
  /*driver/platform
  specific*/                                           list_head clients
  *driver_data
  *platform_data                                            struct device

  kobj

  knode_bus
                                MATCH

  knode_driver                                     struct i2c_driver


                                                   (*probe) (i2c_client *)
                                                   (*remove) (i2c_client *)
  struct bus_type
                                                   (*shutdown) (i2c_client *)
                                                   (*suspend) (i2c_client*, mesg)
  *name = “i2c”                                    (*resume) (i2c_client *)

  (*match) (device*,
  device_driver*)                                    struct device_driver

  (*probe) (device *)
  (*remove) (device *)          “KXSD9_driver”       *name
                                                     *bus
  (*shutdown) (device *)
  (*suspend) (device *, mesg)                        kobj
  (*resume) (device *)
                                                     klist_devices
  klist_devices
                                                     knode_bus
  klist_drivers
struct i2c_client

                                KXSD9_I2C_ADDR
addr /*device address*/

*adapter
*driver                                               struct i2c_adapter
driver_name
irq                                BUS_NO
                                                      nr /*bus no*/
  struct device
                                                      /*algorithm to access the bus*/
                                 “KXSD9_driver”       *algo
  *parent
  *driver                                             (*client_register) (i2c_client*)
  *bus                                                (*client_unregister) (i2c_client*)

                                 KXSD9_IRQ            /*list of clients*/
  /*driver/platform
  specific*/                                          list_head clients
  *driver_data
  *platform_data                                           struct device

  kobj

  knode_bus

  knode_driver                                    struct i2c_driver


                                                  (*probe) (i2c_client *)
                                                  (*remove) (i2c_client *)
  struct bus_type
                                                  (*shutdown) (i2c_client *)
                                                  (*suspend) (i2c_client*, mesg)
  *name = “i2c”                                   (*resume) (i2c_client *)

  (*match) (device*,
  device_driver*)                                   struct device_driver

  (*probe) (device *)
  (*remove) (device *)                              *name                               “KXSD9_driver”
                                                    *bus
  (*shutdown) (device *)
  (*suspend) (device *, mesg)                       kobj
  (*resume) (device *)
                                                    klist_devices
  klist_devices
                                                    knode_bus
  klist_drivers
References
 ●
     Linux Kernel 2.6.24 Source Code
 ●
     I2C Bus Specification version 2.1 January 2000
END…

I2C Subsystem In Linux-2.6.24

  • 1.
    I2C Subsystem In Linux 2.6.24 Author: Varun Mahajan <varunmahajan06@gmail.com>
  • 2.
    Contents ● Data structures representing I2C bus, device, driver, etc ● How to add an I2C device to the kernel ● What happens when a new instance of I2C bus is recognized by the kernel ● How to add an I2C device driver to the kernel ● Device ↔ Driver binding
  • 3.
    Data Structures I2C Bus Device Driver Adapter Driver Client struct i2c_adapter struct i2c_driver struct i2c_client struct bus_type nr /*bus no*/ (*probe) (i2c_client *) addr /*device address*/ (*remove) (i2c_client *) /*algorithm to access the bus*/ *adapter *name = “i2c” *algo (*shutdown) (i2c_client *) *driver (*suspend) (i2c_client*, mesg) driver_name (*match) (device*, device_driver*) (*client_register) (i2c_client*) (*resume) (i2c_client *) irq (*client_unregister) (i2c_client*) (*probe) (device *) (*remove) (device *) /*list of clients*/ struct device list_head clients struct device_driver (*shutdown) (device *) (*suspend) (device *, mesg) (*resume) (device *) *parent *name *driver struct device *bus *bus klist_devices klist_drivers kobj /*driver/platform specific*/ klist_devices *driver_data knode_bus *platform_data kobj knode_bus knode_driver
  • 4.
    Data Structures i2c_driver_1 i2c_driver_2 i2c_client_1 i2c_client_2 D1 D2 I2C Bus 1 i2c_adapter_1 I2C Bus Driver bus_type I2C Bus 2 i2c_adapter_2 D3 i2c_driver_3 i2c_client_3
  • 5.
    How to addan I2C device to the Kernel Populate the __i2c_board_list in the board specific initialization code __i2c_board_list List of struct i2c_devinfo struct i2c_devinfo busnum BUS_NO struct “KXSD9_driver” i2c_board_info driver_name KXSD9_I2C_ADDR addr irq KXSD9_IRQ *platform_data PLATFORM_DATA
  • 6.
    When a newI2C Bus instance is recognized by Kernel 1. A structure i2c_adapter is instantiated for the new bus instance struct i2c_adapter nr /*bus no*/ BUS_NO /*algorithm to access the bus*/ *algo (*client_register) (i2c_client*) (*client_unregister) (i2c_client*) /*list of clients*/ list_head clients struct device
  • 7.
    When a newI2C Bus instance is recognized by Kernel 2. For this new adapter, the __i2c_board_list is scanned to check for devices whose bus nos match with the adapter’s bus no. If there is a match then a new i2c_client structure is created for that device struct i2c_client struct i2c_devinfo BUS_NO addr /*device address*/ busnum *adapter *driver KXSD9_I2C_ADDR driver_name struct irq i2c_board_info struct device driver_name “KXSD9_driver” *parent addr *driver *bus irq KXSD9_IRQ /*driver/platform *platform_data specific*/ *driver_data *platform_data PLATFORM_DATA kobj knode_bus knode_driver
  • 8.
    When a newI2C Bus instance is recognized by Kernel 3. The Data structures are linked as shown below struct i2c_client struct i2c_adapter addr /*device address*/ nr /*bus no*/ *adapter /*algorithm to access the bus*/ *driver KXSD9_I2C_ADDR *algo driver_name irq (*client_register) (i2c_client*) BUS_NO (*client_unregister) (i2c_client*) struct device /*list of clients*/ “KXSD9_driver” list_head clients *parent *driver struct device *bus KXSD9_IRQ /*driver/platform specific*/ *driver_data struct bus_type *platform_data kobj *name = “i2c” knode_bus (*match) (device*, device_driver*) knode_driver (*probe) (device *) (*remove) (device *) (*shutdown) (device *) (*suspend) (device *, mesg) (*resume) (device *) klist_devices klist_drivers
  • 9.
    How to addan I2C device driver to the Kernel Populate the i2c_driver structure and define the relevant functions. Add this driver to the kernel through i2c_add_driver() struct i2c_driver KXSD9_probe ( i2c_client *) (*probe) (i2c_client *) KXSD9_remove ( i2c_client *) (*remove) (i2c_client *) KXSD9_shutdown ( i2c_client *) (*shutdown) (i2c_client *) (*suspend) (i2c_client*, mesg) KXSD9_suspend ( i2c_client *) (*resume) (i2c_client *) KXSD9_resume ( i2c_client *) struct device_driver *name “I2C_driver” *bus kobj klist_devices knode_bus
  • 10.
    Code Flow afteri2c_add_driver()
  • 11.
    Device ↔ DriverBinding • For this new i2c_driver the kernel scans the klist_devices of the i2c_bus_type structure. If an existing device’s i2c_client’s driver_name matches with the i2c_driver.driver’s name, then it calls i2c_bus_type.probe() for this matched device. I2c_bus_type.probe() internally calls the i2c_driver.probe(). If i2c_driver.probe() succeeds, the i2c_driver.driver is added to the klist_drivers of i2c_bus_type and the device<->driver are bound • i2c_driver.probe ( i2c_client * ) – Store a reference to i2c_client* for future use – Initialize the device
  • 12.
    struct i2c_client KXSD9_I2C_ADDR addr /*device address*/ *adapter *driver struct i2c_adapter driver_name irq BUS_NO nr /*bus no*/ struct device /*algorithm to access the bus*/ “KXSD9_driver” *algo *parent *driver (*client_register) (i2c_client*) *bus (*client_unregister) (i2c_client*) KXSD9_IRQ /*list of clients*/ /*driver/platform specific*/ list_head clients *driver_data *platform_data struct device kobj knode_bus MATCH knode_driver struct i2c_driver (*probe) (i2c_client *) (*remove) (i2c_client *) struct bus_type (*shutdown) (i2c_client *) (*suspend) (i2c_client*, mesg) *name = “i2c” (*resume) (i2c_client *) (*match) (device*, device_driver*) struct device_driver (*probe) (device *) (*remove) (device *) “KXSD9_driver” *name *bus (*shutdown) (device *) (*suspend) (device *, mesg) kobj (*resume) (device *) klist_devices klist_devices knode_bus klist_drivers
  • 13.
    struct i2c_client KXSD9_I2C_ADDR addr /*device address*/ *adapter *driver struct i2c_adapter driver_name irq BUS_NO nr /*bus no*/ struct device /*algorithm to access the bus*/ “KXSD9_driver” *algo *parent *driver (*client_register) (i2c_client*) *bus (*client_unregister) (i2c_client*) KXSD9_IRQ /*list of clients*/ /*driver/platform specific*/ list_head clients *driver_data *platform_data struct device kobj knode_bus knode_driver struct i2c_driver (*probe) (i2c_client *) (*remove) (i2c_client *) struct bus_type (*shutdown) (i2c_client *) (*suspend) (i2c_client*, mesg) *name = “i2c” (*resume) (i2c_client *) (*match) (device*, device_driver*) struct device_driver (*probe) (device *) (*remove) (device *) *name “KXSD9_driver” *bus (*shutdown) (device *) (*suspend) (device *, mesg) kobj (*resume) (device *) klist_devices klist_devices knode_bus klist_drivers
  • 14.
    References ● Linux Kernel 2.6.24 Source Code ● I2C Bus Specification version 2.1 January 2000
  • 15.