i2c registration process

i2c driver notes under linux

https://www.cnblogs.com/shmily-linux/archive/2013/10/07/3356306.html

1. Several basic concepts

1.1. Device model

It is composed of bus (bus_type) + device (device) + driver (device_driver). Under this model, all devices are connected through the bus. Even if some devices are not connected to a physical bus, linux sets an internal , Virtual platform bus, used to maintain the relationship between bus, driver, and equipment.

Therefore, the realization of a device driver under Linux can be divided into two major steps:

1. Equipment registration;

2. Driver registration.

 

Of course, there are some details:

1. Driven probe function

2. How are the drivers and devices bound?

1.2. Several data structures of i2c device driver

 

i2c_adapter:

Each i2c_adapter corresponds to a physical i2c controller, which is dynamically created in the probe function of the i2c bus driver. Register to i2c_core through i2c_add_adapter.

 

i2c_algorithm:

The key function master_xfer() in i2c_algorithm generates signals required for i2c access in units of i2c_msg. The master_xfer() corresponding to different platforms is different. You need to implement your own xxx_xfer() method according to the hardware characteristics of the platform used to fill the master_xfer pointer of i2c_algorithm; on A31, it is the sun6i_i2c_algorithm function.

 

i2c_client:

Represents an i2c slave device mounted on the i2c bus, containing the data required by the device:

The i2c controller to which the i2c slave device is attached struct i2c_adapter *adapter

The driver of the i2c slave device struct i2c_driver *driver

The access address of the i2c slave device addr, name

The name of the i2c slave device.

2. i2c bus driver

2.1. Functional division

From the hardware function, it can be divided into: i2c controller and i2c peripheral (slave device). Multiple i2c peripherals can be mounted on each i2c controller bus. Separate management of i2c controllers and peripherals in Linux: i2c-sun6i.c file completes i2c controller device registration and driver registration; i2c-core.c provides a unified device registration interface for specific i2c peripherals And driver registration interface, it separates the implementation details of device driver and hardware control (such as operating i2c registers).

2.2. i2c-sun6i.c

This file is related to the specific hardware platform and corresponds to the A3x series of chips. This file is actually the realization of the i2c bus driver, essentially registering the i2c bus device with the kernel, registering the bus driver, and realizing the timing control algorithm for bus transmission. The i2c controller is registered as a Platform device as follows:

Copy code

    if (twi_used_mask & TWI0_USED_MASK)
        platform_device_register(&sun6i_twi0_device);
 
    if (twi_used_mask & TWI1_USED_MASK)
        platform_device_register(&sun6i_twi1_device);
 
    if (twi_used_mask & TWI2_USED_MASK)
        platform_device_register(&sun6i_twi2_device);
 
    if (twi_used_mask & TWI3_USED_MASK)
        platform_device_register(&sun6i_twi3_device);
 
    if (twi_used_mask)
        return platform_driver_register(&sun6i_i2c_driver);

Copy code

 

It should be noted that the correspondence between devices and drivers is many-to-one; that is, if the device types are the same, they will share the same set of drivers, so the above code only registers the driver platform_driver_register(&sun6i_i2c_driver) once. 

 

Device registration:

Register the i2c controller device as a platform device, define a struct platform_device data structure for each controller, and set the .name to "sun6i-i2c" (the driver will be matched by the name later), and then call platform_device_register( ) Register the device on the platform bus.

 

After the device registration is completed, its intuitive performance is to appear under the file system: /sys/bus/platform/devices/sun6i-i2c.0

 

The registration process through platform_device_register() is, in the final analysis, a change to the data structure of struct platform_device, gradually completing the assignment of .dev.parent, .dev.kobj, and .dev.bus, and then adding .dev.kobj to platform_bus- > On the linked list of kobj.

 

Driver registration:

The steps are similar to those of device registration, but also define a data structure for the driver:

struct platform_driver sun6i_i2c_driver;

 

Because one driver can correspond to multiple devices, and the three controllers in the system are basically the same (the difference is that the address of the register is different), so the three devices registered above share the same set of drivers.

 

Initialize the .probe and .remove functions, and then call platform_driver_register to register the driver. The main function call process:

platform_driver_register --> driver_register --> bus_add_driver --> driver_attach

 

It should be noted that driver_attach, this function traverses all devices on the bus (platform_bus_type), finds the device that matches the driver, and points the driver pointer on the device structure that meets the conditions to the driver, thus completing the match between the driver and the device ( The __driver_attach function is completed).

 

If the device is matched, the probe function of platform_bus_type needs to be executed, and finally the probe function of the device driver (sun6i_i2c_probe) is called.

2.2.1  sun6i_i2c_probe

A lot of work is done in the sun6i_i2c_probe function, including hardware initialization, interrupt registration, and creation of i2c_adapter for each i2c controller.

Copy code

1268         pdata = pdev->dev.platform_data;
1269         if (pdata == NULL) {
1270                 return -ENODEV;
1271         }
1272
1273         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1274         irq = platform_get_irq(pdev, 0);
1275         if (res == NULL || irq < 0) {
1276                 return -ENODEV;
1277         }
1278
1279        if (!request_mem_region(res->start, resource_size(res), res->name)) {
1280                 return -ENOMEM;
1281         }

Copy code

 

  • First get the private data pointer of the current device and keep it in pdata; then get the memory resources occupied by the device through platform_get_resource, and apply for: request_mem_region. At the same time, irq resources are also retained. 

Copy code

1288
1289         strlcpy(i2c->adap.name, "sun6i-i2c", sizeof(i2c->adap.name));
1290         i2c->adap.owner   = THIS_MODULE;
1291         i2c->adap.nr      = pdata->bus_num;
1292         i2c->adap.retries = 3;
1293         i2c->adap.timeout = 5*HZ;
1294         i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
1295         i2c->bus_freq     = pdata->frequency;
1296         i2c->irq                  = irq;
1297         i2c->bus_num      = pdata->bus_num;
1298         i2c->status       = I2C_XFER_IDLE;
1299         i2c->suspended = 0;
1300         spin_lock_init(&i2c->lock);
1301         init_waitqueue_head(&i2c->wait);

Copy code

  • Initialize i2c_adapter, and initialize a work queue init_waitqueue_head. 
  • Apply for IO resources through ioremap;
  • To apply for irq resources through request_irq, the interrupt processing service function is: sun6i_i2c_handler;
  • sun6i_i2c_hw_init, hardware initialization for i2c control;
  • i2c->adap.algo = &sun6i_i2c_algorithm, initialize the bus transmission algorithm of the controller and call the device driver;
  • Register the initialized i2c_adapter to i2c_core: i2c_add_numbered_adapter.

At this point, the probe function is complete.

2.2.2  sun6i_i2c_core_process

The interrupt service program sun6i_i2c_handler of the i2c controller calls sun6i_i2c_core_process, and the actual transmission control of the i2c bus is also completed in this function.

Main process:

  1. Read the current status of the i2c controller, twi_query_irq_status, and keep it in the state;
  2. Branch and jump according to the value of state to control the working state of i2c;
  3. After the transfer is complete, call sun6i_i2c_xfer_complete to wake up the work queue.

 

2.2.3  sun6i_i2c_xfer

Each i2c controller device will create an i2c_adapter after the driver is bound to describe the controller. The establishment and initialization of i2c_adapter is established when the probe is driven. Each i2c_adapter contains a pointer to the i2c_algorithm structure. i2c_algorithm is used to provide a functional interface for operating the i2c controller, mainly the master_xfer function, which corresponds to i2c-sun6i.c, which is actually:

 

static int sun6i_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

The function of this function is to notify i2c_adapter that it needs to exchange data with peripherals, and the information to be exchanged is passed in through struct i2c_msg *msgs. sun6i_i2c_xfer actually calls sun6i_i2c_do_xfer for transmission.

Because the read and write rate of the i2c bus is limited, sun6i_i2c_do_xfer starts the i2c transmission and enters sleep through wait_event_timeout until the interrupt wakes up or times out; the interrupt wake up is completed by sun6i_i2c_xfer_complete.

3. i2c device driver

3.1. Driver Registration

The driver registration of i2c slave device uses the interface provided by i2c-core.c: i2c_register_driver; its call is as follows:

 

i2c_register_driver --> driver_register --> bus_add_driver;

 

Analyze bus_add_driver:

  • About struct driver_private *p of device_driver data structure

The device driver model is hierarchical management of device drivers through kobject, so device_driver should contain kobject members, linux includes kobject in struct driver_private, and device_driver includes struct driver_private; we can understand that driver_private is the private data of device_driver, by The kernel operates.

struct driver_private is dynamically applied for and initialized at the beginning of driver registration.

  • klist_init(&priv->klist_devices, NULL, NULL);

Initialize the device linked list, and every device that matches the driver will be added to the linked list.

  • priv->kobj.kset = bus->p->drivers_kset;

Specify the kset to which the driver belongs;

  • kobject_init_and_add

Initialize the kobject, and add the kobject to its corresponding kset set (ie bus->p->drivers_kset).

This function finally calls kobject_add_internal to add the kobject to the corresponding kset; the main thing is that if the parent of the kobject is NULL, its parent will be set to the kobject of the kset collection:

parent = kobject_get(&kobj->kset->kobj);

 

Next is to create a folder for kobject: create_dir(kobj); so that it can be displayed from the /sys/ directory.

  • driver_attach, bind the driver and the device

It will traverse the device linked list on the bus, find the matching devices, and bind them.

driver_attach -->  bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);

Pass the function pointer __driver_attach into bus_for_each_dev, and drive and match each device found.

 

bus_for_each_dev:

Traverse all the devices on the bus, because the devices on the bus are all nodes on the bus->p->klist_devices linked list, so this function is actually a traversal of the linked list. For details, please refer to klist.

 

__driver_attach (source code location drivers/base/dd.c):

Match the device and the driver. If the match is successful, try to bind.

 

1. First confirm the match: driver_match_device(drv, dev);

Calling relationship: --> drv->bus->match --> i2c_device_match 

-->  of_driver_match_device

  i2c_match_id

 

It can be seen that there are finally two ways to drive matching queries:

Method 1: Compare of_device_id by of_driver_match_device;

Method 2: Compare id_table by i2c_match_id;

Method two is actually comparison

i2c_driver->id_table->name and client->name are the same.

 

2. If the match is confirmed, bind the driver and the device: driver_probe_device;

Calling relationship: driver_probe_device --> really_probe

 --> dev->bus->probe

    driver_bound

 

In really_probe, first point the device's driver pointer to the driver: dev->driver = drv.

Corresponding to i2c_bus_type, dev->bus->probe is: i2c_device_probe, which finally calls the driver's probe function.

 

The last is driver_bound, which binds the driver and the device:

In fact, it is to call klist_add_tail: add the device node to the klist_devices of the driver;

 

  • Call klist_add_tail to add the registered driver to the klist_drivers of the bus;

klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

 

  •  module_add_driver(drv->owner,  drv)

Create drivers directory in sysfs

3.2. Device Registration

Method 1: i2c device dynamic discovery and registration

At the end of i2c_register_driver:

        INIT_LIST_HEAD(&driver->clients);
        /* Walk the adapters that are already present */
        i2c_for_each_dev(driver, __process_new_driver);

 

Observe i2c_for_each_dev: 

Copy code

int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
        int res;
 
        mutex_lock(&core_lock);
        res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
        mutex_unlock(&core_lock);
 
        return res;
}

Copy code

 

In fact, it is to traverse the klist_devices linked list on the i2c bus, and execute __process_new_driver for each device obtained. 

跟踪 __process_new_driver --> i2c_do_add_adapter --> i2c_detect

i2c_detect realizes i2c device discovery: after registering the driver, i2c_detect detects whether a suitable device is connected to the bus. The implementation of i2c_detect is as follows:

  • Traverse the address list (address_list) given by the driver on each adapter, which is completed by the i2c_detect_address function; finally, driver->detect (the device discovery function provided by the device driver) will be called;
  • If a device that meets the conditions is found, execute i2c_new_device to establish i2c_client for the device; and add the device to the i2c_bus_type->p->klist_devices linked list (device_register), complete through the bus_add_device function, and finally call bus_probe_device to try to bind the driver.
  • Add client to the driver's device list: list_add_tail(&client->detected, &driver->clients)

 

Method 2: Static registration of i2c devices

Linux 3.3 provides a static definition method to register the device, the interface prototype: linux-3.3/drivers/i2c/i2c-boardinfo.c

int __init
i2c_register_board_info(int busnum,
        struct i2c_board_info const *info, unsigned len)

core content: 

  • Apply for struct i2c_devinfo to describe an i2c peripheral;
  • list_add_tail(&devinfo->list, &__i2c_board_list), add devinfo to the linked list __i2c_board_list for subsequent search;

Scan __i2c_board_list, create client

i2c_register_board_info just added the device descriptor to __i2c_board_list, but did not create the client. When calling i2c_register_adapter to register the adapter, it will scan __i2c_board_list and create the client; the specific call:

i2c_register_adapter 

--> i2c_scan_static_board_info 

--> i2c_new_device 

--> device_register

 

Completed client creation in i2c_new_device, and device registration device_register.

 

PS:

According to the above registration process, i2c_register_board_info should be completed before i2c_register_adapter, otherwise the nodes in __i2c_board_list will not be scanned.

 

to sum up:

  • From the above analysis, it can be seen that the i2c device driver is registered through i2c_register_driver, and the i2c device is registered through i2c_new_device. In the end, these two functions try to drive and device binding (driver_attach and bus_probe_device); therefore, no matter whether you register the driver first or Register the device first, and finally bind the appropriate driver and device.
  • There are two ways to register the device:

1. Through i2c_register_board_info, i2c device registration is performed statically at the beginning of the system startup (axp power driver does this);

2. Implement the detect function of the i2c device driver, dynamically detect the creation of the device when the driver is loaded, the touch screen driver gt82x.ko of the aw platform uses this method.

  • Linux completes the hierarchical management of device drivers by embedding kobject and kset in the drive data structure. Understanding kobject and kset is very important for understanding the device driver model.

4. i2c driver architecture diagram

 

1、i2c_add_adapter

2、i2c_new_device/i2c_register_board_info

3、i2c_add_driver

4. Call the match function registered in the i2c bus to match

5. Call the match function registered in the platform bus to match

6、i2cdev_attach_adapter

Guess you like

Origin blog.csdn.net/u014426028/article/details/108833217