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
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 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
6. 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
7. 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
8. 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
9. 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
11. 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