Revision history
Date issue description author
<08/07/2010> <1.0> msm7227 platform I2C driver analysis Teng Jingdong
Directory
1. Summary 3
2. Introduction 3
3. I2C architecture 3
4. I2C bus initialization 4
5. I2C Adapter Driver 5
6. I2C Device Driver 9
7. user space drive support 12
8. Data Transmission framework 16
9. References 16
1. Summary
This article mainly introduces the I2C driver principle on the msm7227 platform, most of which is the 29-core standard architecture.
2. Introduction
I2C only has two lines, one serial data line: SDA, and the other clock line, and the other is the SCL. I2C is a multi-host control bus that allows multiple masters on the same bus.
The I2C bus adapter is a controller of the I2C bus, which is physically connected to several I2C devices. In Linux drivers, each processor platform has its own Adapter Driver.
3. I2C Architecture
The I2C code in the kernel can be divided into three layers:
I2C framework layer: I2C. H and i2c-core.c for its main framework code, provides core data structure definition, I2C Adapter Driver and device driver registration, logout management and so on; the i2c-dev.c is used to create the/dev/I2C-% d device node for the I2C adapter, providing user space access methods for I2C devices, etc.
I2C bus Adapter Driver: I2C/busses/directory, such as i2c-msm.c. Define the i2c_adapter data structure of the I2C bus adapter and the implementation of I2C bus communication on the I2C adapter. The i2c_algorithm Data Structure describes how to communicate with the I2C device.
I2C Device Driver: defines the i2c_client and possible private data structure that describes the specific device.
The overall framework of the kernel I2C structure is presented. The following section describes the I2C bus initialization, I2C bus Adapter Driver, I2C Device Driver, user space driver support, and data transmission framework based on the kernel loading sequence.
4. I2C bus Initialization
This process mainly completes the sysfs bus structure and finally forms the following structure:
/Sys/bus/I2C/
| -- Devices
| -- Drivers
| -- Dummy
| -- Bind
| -- Uevent
| '-- Unbind
| -- Drivers_autoprobe
| -- Drivers_probe
'-- Uevent
And
/Sys/class/i2c-adapter/
Dummy_driver only registers an empty device driver, which traverses all the devices in the loaded/sys/class/i2c-adapter/when registering the driver. This process is completed in the initial BUS process, /sys/class/i2c-adapter/basic is empty, so I think the driver registration here is just a concern for verifying the integrity of the I2C bus structure.
5. I2C Adapter Driver
All the Linux kernel adapter drivers are under the driver/I2C/busses/directory, the current Qualcomm driver is a i2c-msm.c, The Adapter Driver registration process is as follows:
Two adapter registration interfaces are provided in the kernel: i2c_add_adapter () and i2c_add_numbered_adapter (). there may be multiple adapters in the system, because each I2C bus corresponds to a number, which is called the I2C bus number. For i2c_add_adapter (), it uses a dynamic bus number, that is, the system allocates a bus number to it, while i2c_add_numbered_adapter () is the bus number specified by itself, if the bus number is invalid or occupied, the registration fails. The Qualcomm Adapter Driver is registered using i2c_add_numbered_adapter (), and the bus number is originally stored in platform_data.
The I2C adapter is registered to the system as platform_device, And the struct i2c_adapter structure is initialized in the proble function:
Struct i2c_adapter {<br/> struct module * owner; <br/> unsigned int ID; <br/> unsigned int class; /* classes to allow probing for */<br/> const struct i2c_algorithm * algo;/* the algorithm to access the bus */<br/> void * algo_data; </P> <p>/* --- administration stuff. */<br/> int (* client_register) (struct i2c_client *); <br/> int (* client_unregister) (struct i2c_client *); </P> <p>/* data fields that are valid for all devices */<br/> u8 level; /* nesting level for lockdep */<br/> struct mutex bus_lock; <br/> struct mutex clist_lock; </P> <p> int timeout; /* In jiffies */<br/> int retries; <br/> struct device dev;/* the adapter device */</P> <p> int NR; /* This Member describes the bus number */<br/> struct list_head clients;/* i2c_client Structure linked list, which contains device, driver and <br/> adapter structure */<br/> char name [48]; <br/> struct completion dev_released; <br/> };
The NR value is defined in arch/ARM/Mach-MSM/devices. C:
Struct platform_device msm_device_i2c ={< br/>. name = "msm_i2c", <br/>. id = 0, <br/>. num_resources = array_size (resources_i2c), <br/>. resource = resources_i2c, <br/>}; <br/> struct platform_device msm_device_i2c_2 ={< br/>. name = "msm_i2c", <br/>. id = 2, <br/>. num_resources = array_size (resources_i2c_2), <br/>. resource = resources_i2c_2, <br/> };
This structure is passed as a parameter to i2c_add_numbered_adapter ().
Static int i2c_register_adapter (struct i2c_adapter * ADAP) <br/>{< br/> int res = 0, dummy; </P> <p>/* can't register until after Driver Model init */<br/> If (unlikely (warn_on (! I2c_bus_type.p) <br/> return-eagain; </P> <p> mutex_init (& ADAP-> bus_lock ); <br/> mutex_init (& ADAP-> clist_lock); <br/> init_list_head (& ADAP-> clients ); /* initialize the device linked list */</P> <p> mutex_lock (& core_lock); </P> <p>/* Add the adapter to the driver core. <br/> * If the parent pointer is not set up, <br/> * we add this adapter to the host bus. <br/> */<br/> If (ADAP-> Dev. parent = NULL) {<br/> ADAP-> Dev. parent = & platform_bus; /* the parent device is platform_bus */<br/> pr_debug ("I2C Adapter Driver [% s] Forgot to specify" <br/> "physical device/N ", ADAP-> name); <br/>}< br/> dev_set_name (& ADAP-> Dev, "I2C-% d", ADAP-> nr ); /* Device node name */<br/> ADAP-> Dev. release = & i2c_adapter_dev_release; <br/> ADAP-> Dev. class = & i2c_adapter_class; <br/> res = device_register (& ADAP-> Dev);/* register the adapter itself */<br/> If (RES) <br/> goto out_list; </P> <p> dev_dbg (& ADAP-> Dev, "adapter [% s] registered/N", ADAP-> name ); </P> <p>/* the following sections complete registration of I2C devices and drivers */<br/> If (ADAP-> Nr <_ i2c_first_dynamic_bus_num) /* the dynamic bus number when the motherboard is initialized. This value has been exported to the symbol table */<br/> i2c_scan_static_board_info (ADAP);/* registers the new type of I2C device, generally, only */</P> <p>/* notify drivers */<br/> dummy = bus_for_each_drv (& i2c_bus_type, null, ADAP, <br/> i2c_do_add_adapter);/* detects drivers of all I2C devices on the bus and binds the client, driver, device, and adapter, however, driver-> address_data is useful when it is not null, which means it is only valid for the old I2C Mechanism */</P> <p> out_unlock: <br/> mutex_unlock (& core_lock); <br/> return res; </P> <p> out_list: <br/> idr_remove (& i2c_adapter_idr, ADAP-> nr); <br/> goto out_unlock; <br/>}</P> <p>
The initialization process for i2c_scan_static_board_info is completed in the board-msm7x27.c,
I2c_register_board_info (0, i2c_devices, array_size (i2c_devices ));
6. I2C device 7. Driver
The driver Compilation Guide is introduced in the memory msm7227-i2cdevice driver Real-Time Point .doc. This section analyzes the driver and device registration process.
I also want to analyze the code in detail, but I found that this figure is enough to describe the registration process of the I2C driver. I will briefly analyze some problems I encountered when reading the code.
Association between devices and drivers
As you know, a driver has two elements that are indispensable: Device and driver. Generally, drivers are created by matching the device name and driver name, the sample code I have seen from I2C/chips/can only find the registration of the driver, but does not see the registration of the device. It is confusing, tracking and discovery, the i2c_board_info structure will be traversed during I2C adapter registration, and this structure does not exist in the kernel before or earlier 29, the data structure initializes the I2C device name and device address in the board-msm7x27.c, which solves the problem of matching the driver and the device, and the device address is also changed, the old kernel uses a normal_i2c array in the driver to save the address.
Name match
An I2C driver can have multiple names, that is, a driver can support multiple devices. This mechanism is implemented through struct i2c_device_id, and such a struct array is created in the driver, the I2C architecture layer scans the array and matches the device name. If the device name matches successfully, the corresponding probe function is entered.
Enter Probe
This process puzzles me for a while. In fact, to enter the probe driven by myself, you first need to enter the probe of the bus. The premise of entering the probe of the bus is that the match is successful with the bus, for specific implementation, you can refer to the figure above to see the corresponding code.
Device Model
The I2C architecture fully utilizes the principle of the device model and the implementation of sysfs. I think it is necessary to first understand the device model before understanding the I2C architecture. Here is a summary of my personal understanding:
Kobject is the smallest unit of the device model, and kset is the set of kobject. The structures such as struct driver_private and struct device are embedded with kobject, and kset is embedded with kobject to represent itself. The collection of ksets with the same features constitutes subsys. an inappropriate analogy is given:
Kobject is for a device or driver; kset is for a certain type of device, such as I2C; subsys is for a subsystem, such as an input subsystem. In fact, in kernel 29, subsys is a kset structure, which is illustrated in two figures:
8. user space drive support
This part is implemented in the i2c-dev.c, simply by embedding a standard character device driver with file_operations into a virtual I2C device, you can directly operate the I2C device in the user space.
The process is as follows:
The remaining is regular file_operation. open operation:
Static int i2cdev_open (struct inode * inode, struct file * file) <br/>{< br/> unsigned int minor = iminor (inode ); <br/> struct i2c_client * client; <br/> struct i2c_adapter * ADAP; <br/> struct i2c_dev * i2c_dev; <br/> int ret = 0; </P> <p> lock_kernel ();/* the kernel is locked, which is generally only useful for multiple CPUs */<br/> i2c_dev = i2c_dev_get_by_minor (minor ); /* because there are two adapters, the same master device Number */<br/> If (! I2c_dev) {<br/> ret =-enodev; <br/> goto out; <br/>}</P> <p> ADAP = i2c_get_adapter (i2c_dev-> ADAP-> nr); <br/> If (! ADAP) {<br/> ret =-enodev; <br/> goto out; <br/>}</P> <p>/* This creates an anonymous i2c_client, which may later be <br/> * pointed to some address using i2c_slave or i2c_slave_force. <br/> * this client is ** never registered ** with the driver model <br/> * or I2C core code !! It just holds private copies of addressing <br/> * Information and maybe a PEC flag. <br/> */<br/> client = kzarloc (sizeof (* client), gfp_kernel); <br/> If (! Client) {<br/> i2c_put_adapter (ADAP); <br/> ret =-enomem; <br/> goto out; <br/>}< br/> snprintf (client-> name, i2c_name_size, "i2c-dev % d", ADAP-> nr ); <br/> client-> driver = & i2cdev_driver;/* bind a character device driver */</P> <p> client-> adapter = ADAP; <br/> file-> private_data = client; </P> <p> out: <br/> unlock_kernel (); <br/> return ret; <br/>}< br/>
Note that a struct i2c_client structure is allocated and initialized here. but this clinet is not registered. in addition, this function has a strange operation. didn't I point i2c_dev-> ADAP to the adapter to be operated before? Why do I need to find the adapter for this operation from i2c_adapter_idr using the adapter-> Nr keyword? Note that when calling i2c_get_adapter () to find the adapter operated on from the bus No. Nr, the reference count of the module will also be increased. this prevents the module from being accidentally released. some people may have such questions: i2c_dev-> ADAP-> Nr operation. If i2c_dev-> ADAP is released, will it cause system crash? This is because, in i2cdev_attach_adapter (), the reference count of an adapter is indirectly increased:
Static int i2cdev_attach_adapter (struct i2c_adapter * ADAP) <br/>{< br/> ...... <br/> i2c_dev-> Dev = device_create (i2c_dev_class, & ADAP-> Dev, <br/> mkdev (i2c_major, ADAP-> nr ), <br/> "I2C-% d", ADAP-> nr); <br/> ...... <br/>}
As you can see, the device embedded in i2c_dev uses ADAP-> Dev as the parent node and adds a reference count of ADAP-> Dev in device_create.
Now, the open () operation is complete.
Usage: (refer to kernel-test/i2c-msm-test.c)
1. Construct struct i2c_msg
[Read] struct i2c_msg msgs [] ={< br/> [0] ={< br/>. ADDR = slave_address, <br/>. flags = 0, <br/>. buf = (void *) offset_data, <br/>. len = array_size (offset_data), <br/>}, <br/> [1] = {<br/>. ADDR = slave_address, <br/>. flags = i2c_m_rd, <br/>. buf = (void *) BUF, <br/>. len = count, <br/>}, <br/> }; <br/> [write] struct i2c_msg msgs [] ={< br/> [0] ={< br/>. ADDR = slave_address, <br/>. flags = 0, <br/>. buf = (void *) data, <br/>. len = (2 + Len) * sizeof (* data), <br/>}, <br/>}
2. Operate the device through IOCTL
Static int do_rdwr (int fd, struct i2c_msg * msgs, int nmsgs) <br/>{< br/> struct i2c_rdwr_ioctl_data msgset ={< br/>. msgs = msgs, <br/>. nmsgs = nmsgs,/* msgs count */<br/>}; </P> <p> If (MSGs = NULL | nmsgs <= 0) <br/> return-1; </P> <p> If (IOCTL (FD, i2c_rdwr, & msgset) <0) <br/> return-1; </P> <p> return 0; <br/>}< br/>
3. IOCTL command words:
# Define i2c_smbus_read 1 <br/> # define i2c_smbus_write 0 </P> <p> # define i2c_smbus_quick 0 <br/> # define i2c_smbus_byte 1 <br/> # define limit 2 br/> # define i2c_smbus_word_data 3 <br/> # define i2c_smbus_proc_call 4 <br/> # define i2c_smbus_block_data 5 <br/> # define limit 6 <br/> # define limit 7 </P> <p> # define i2c_retries 0x0701 <br/> # define i2c_timeout 0x0702 <br/> # define i2c_slave 0x0703 <br/> # define i2c_slave_force 0X0706 <br/> # define i2c_tenbit 0x0704 <br/> # define i2c_funcs 0x0705 <br/> # define i2c_rdwr 0x0707 <br/> # define i2c_pec 0X0708 <br/> # define i2c_smbus 0x0720 </P> <p>
9. Data Transmission framework
The I2C architecture supports two types of reading and writing. The default operation is the SMBus protocol, which is similar to the I2C protocol. If the Controller does not support SMBus, the framework layer can use i2c_transfer to simulate the implementation of SMBus, the system's default I2C transmission functions are generally transmitted Based on the I2C analog SMBus method, such as i2c_smbus_write_byte_data and i2c_smbus_read_byte_data.
The bus Implementation of I2C protocol should be I2C controller rather than SMBus controller. I2C protocol and SMBus Protocol are not equivalent, SMBus is a subset of I2C, and SMBus is derived from I2C. The data transmitted on the SMBus must be in I2C format, but the data transmitted on the SMBus may not meet the communication requirements (data sequence) of a specific I2C slave device ).
Use i2c_smbus_write_byte_data to introduce the data process:
10. References
Http://blog.chinaunix.net/u1/51562/showart_1403925.html.
[2]. 《msm7227-i2c Device Driver important spot .doc Teng Jingdong