Platform device driver architecture - concept, platform bus, platform driver, platform device

Reference: I.MX6U embedded Linux Driver Development Guide of Zhengyuan Electronics

1. platform device driver architecture

         Starting from Linux 2.6, Linux has added a set of driver management and registration mechanism - platform bus driver model. Platform bus is a virtual bus (only one), which has no corresponding hardware structure. platform_device is the corresponding device, platform_driver is the corresponding driver. Compared with the traditional bus/device/driver mechanism, the platform is uniformly managed by the kernel, which improves the portability and security of the code. The so-called platform_device is not a parallel concept with character device, block device and network device, but an additional means provided by Linux system. The framework of Linux bus device driver model is shown in the following figure:

         From the figure, we can clearly see the overall architecture of the Linux platform bus device driver model. In the bus device driver model, the three entities of bus, device and driver need to be concerned. The bus binds the device and driver. When the system registers a driver with the kernel, it calls platform_ driver_ The register function registers the driver to the bus and puts it into the drv linked list of the bus. When registering the driver, it will call the match function of the bus to find each matching device on the bus. If a matching device is found, it will call the corresponding probe function to bind the corresponding device and driver; Similarly, when the system registers a device with the kernel, it calls platform_ device_ The register function registers the device to the bus and puts it into the dev linked list of the bus. When registering the device, it will also call the match function of the bus to find each driver matching the bus. If a matching driver is found, it will call the corresponding probe function to bind the corresponding device and driver; This matching process is automatically completed by the bus.

reference resources: linux bus, device, driver model_ babyzhaoshu521 blog - CSDN blog_ linux bus driver model

         When learning platform architecture, we should use the idea of object-oriented, pay attention to some important structures, separate attributes and behaviors, and then connect with each other.

2. Bus

         Linux kernel uses bus_ The type structure represents the bus.

         This structure is defined in the file include/linux/device.h, bus_ The type structure is as follows:

1 struct bus_type { 
2     const char *name; /* Bus name */ 
3     const char *dev_name; 
4     struct device *dev_root; 
5     struct device_attribute *dev_attrs; 
6     const struct attribute_group **bus_groups; /* Bus properties */ 
7     const struct attribute_group **dev_groups; /* Device properties */ 
8     const struct attribute_group **drv_groups; /* Drive properties */ 
9 
10    int (*match)(struct device *dev, struct device_driver *drv); 
11    int (*uevent)(struct device *dev, struct kobj_uevent_env *env); 
12    int (*probe)(struct device *dev); 
13    int (*remove)(struct device *dev); 
14    void (*shutdown)(struct device *dev); 
15 
16    int (*online)(struct device *dev); 
17    int (*offline)(struct device *dev);
18    int (*suspend)(struct device *dev, pm_message_t state); 
19    int (*resume)(struct device *dev); 
20    const struct dev_pm_ops *pm; 
21    const struct iommu_ops *iommu_ops; 
22    struct subsys_private *p; 
23    struct lock_class_key lock_key; 
24 }; 

         Line 10, match function, which is very important to complete the matching between the device and the driver. The bus uses the match function to find the corresponding driver according to the registered device, or to find the corresponding device according to the registered driver. Therefore, each bus must implement this function. The match function has two parameters: dev and drv, which are device and device respectively_ Driver type, that is, device and driver.

3. platform bus

         Platform bus is bus_ A specific instance of type is defined in the file drivers/base/platform.c, and the platform bus is defined as follows:

1 struct bus_type platform_bus_type = {
2     .name = "platform",
3     .dev_groups = platform_dev_groups,
4     .match = platform_match,
5     .uevent = platform_uevent,
6     .pm = &platform_dev_pm_ops,
7 };

         platform_bus_type is the platform bus, where platform_match is the matching function. Let's take a look at how drivers and devices match, platform_ The match function is defined in the file drivers/base/platform.c. The contents of the function are as follows:

1 static int platform_match(struct device *dev, struct device_driver *drv)
2 {
3      struct platform_device *pdev = to_platform_device(dev);
4      struct platform_driver *pdrv = to_platform_driver(drv);
5 
6      /*When driver_override is set,only bind to the matching driver*/
7      if (pdev->driver_override)
8          return !strcmp(pdev->driver_override, drv->name);
9 
10     /* Attempt an OF style match first */
11     if (of_driver_match_device(dev, drv))
12          return 1;
13
14     /* Then try ACPI style match */
15     if (acpi_driver_match_device(dev, drv))
16          return 1;
17
18     /* Then try to match against the id table */
19     if (pdrv->id_table)
20          return platform_match_id(pdrv->id_table, pdev) != NULL;
21
22     /* fall-back to driver name match */
23         return (strcmp(pdev->name, drv->name) == 0);
24 }

         There are four methods for matching driver and equipment. Let's take a look at them in turn:

         Lines 11 to 12, the first matching method, OF type matching, that is, the matching method adopted by the device tree, OF_ driver_ match_ The device function is defined in the file include/linux/of_device.h. device_ There is an OF in the driver structure (representing the device driver)_ match_ Table. This member variable holds the compatible matching table OF the driver. The compatible attribute OF each device node in the device tree will be associated with OF_ match_ Compare all members in the table table to see if there are the same entries. If there are, it means that the device matches the driver. After the device and driver match successfully, the probe function will be executed.

         Lines 15 ~ 16, the second matching method, ACPI matching method.

         Lines 19 ~ 20, the third matching method, id_table matching, each platform_ The driver structure has an id_ The table member variable, as its name suggests, holds a lot of id information. This id information stores the driver types supported by the platform D driver.

         Line 23, the fourth matching method, if the ID of the third matching method_ If the table does not exist, directly compare the name fields of the driver and the device to see if they are equal. If they are equal, the matching is successful. For the Linux version number that supports the device tree, the general device driver supports two matching methods: device tree and no device tree for compatibility. That is, the first matching method generally exists, and only one of the third and fourth matching methods exists. Generally, the fourth matching method is used most, that is, directly comparing the name field of the driver and the device. After all, this method is the simplest.

4. platform driver

platform_ The driver structure represents the platform driver, which is defined in the file include / Linux / platform_ In device. H, the contents are as follows:

1  struct platform_driver {
2      int (*probe)(struct platform_device *);
3      int (*remove)(struct platform_device *);
4      void (*shutdown)(struct platform_device *);
5      int (*suspend)(struct platform_device *, pm_message_t state);
6      int (*resume)(struct platform_device *);
7      struct device_driver driver;
8      const struct platform_device_id *id_table;
9      bool prevent_deferred_probe;
10 };

         In line 2, the probe function is executed after the driver and the device are successfully matched. It is a very important function. Generally, the driver provider will write it. If he wants to write a new driver, the probe needs to implement it by himself.

         Line 7, driver member, device_ The driver structure variable is widely used in the Linux kernel, such as object-oriented thinking and device_driver is equivalent to the base class and provides the most basic driving framework. plaform_driver inherits this base class, and then adds some unique member variables on this basis.

         Line 8, id_table, which is the third method we used when explaining the platform bus matching driver and device in the previous section, id_table is a table (that is, an array), and the type of each element is platform_device_id,platform_ device_ The ID structure is as follows:

1 struct platform_device_id { 
2     char name[PLATFORM_NAME_SIZE]; 
3     kernel_ulong_t driver_data; 
4 }; 

         device_ The driver structure is defined in include/linux/device.h, device_ The driver structure is as follows:

1 struct device_driver { 
2     const char *name; 
3     struct bus_type *bus; 
4 
5     struct module *owner; 
6     const char *mod_name; /* used for built-in modules */ 
7 
8     bool suppress_bind_attrs; /* disables bind/unbind via sysfs */ 
9 
10     const struct of_device_id *of_match_table; 
11     const struct acpi_device_id *acpi_match_table; 
12 
13     int (*probe) (struct device *dev); 
14     int (*remove) (struct device *dev); 
15     void (*shutdown) (struct device *dev); 
16     int (*suspend) (struct device *dev, pm_message_t state); 
17     int (*resume) (struct device *dev); 
18     const struct attribute_group **groups; 
19 
20     const struct dev_pm_ops *pm; 
21 
22     struct driver_private *p; 
23 }; 

         Line 10, of_match_table is the matching table used by the driver when using the device tree. It is also an array, and each matching item is of_device_id structure type, which is defined in the file include / Linux / mod_ In devicetable. H, the contents are as follows:

1 struct of_device_id {
2     char name[32];
3     char type[32];
4     char compatible[128];
5     const void *data;
6 };

         The compatible in line 4 is very important because for the device tree, it is through the compatible attribute value and of of of the device node_ match_ Compare the compatible member variables of each item in the table. If they are equal, it means that the device and this driver are successfully matched.

         When writing a platform driver, first define a platform_driver structure variable, and then implement each member variable in the structure, focusing on the implementation of matching method and probe function. When the driver and device match successfully, the probe function will be executed. The specific driver is written in the probe function, such as character device driver, etc.

         When we define and initialize the platform_ After the driver structure variable, you need to call platform in the driver entry function_ driver_ The register function registers a platform driver with the Linux kernel_ driver_ The prototype of register function is as follows:

        int platform_driver_register (struct platform_driver *driver)

         Function parameters and return values have the following meanings:

         Driver: the platform driver to register.

         Return value: negative number, failed; 0, successful.

         You also need to use platform in the driver unloading function_ driver_ Unregister function unloads the platform driver_ driver_ The prototype of unregister function is as follows:

        void platform_driver_unregister(struct platform_driver *drv)

         Function parameters and return values have the following meanings:

         drv: platform driver to uninstall.

         Return value: none.

         The platform driver framework is as follows:

/* Equipment structure */
1 struct xxx_dev{
2     struct cdev cdev;
3     /* Other specific contents of equipment structure */
4 };
5 
6 struct xxx_dev xxxdev; /* Define a device structure variable */
7 
8 static int xxx_open(struct inode *inode, struct file *filp)
9 { 
10     /* Function details */
11      return 0;
12 }
13
14 static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
15 {
16     /* Function details */
17     return 0;
18 }
19
20 /*
21 * Character device driver operation set
22 */
23 static struct file_operations xxx_fops = {
24     .owner = THIS_MODULE,
25     .open = xxx_open,
26     .write = xxx_write,
27 };
28
29 /*
30 * platform Driven probe function
31 * This function will be executed after the driver is successfully matched with the device
32 */
33 static int xxx_probe(struct platform_device *dev)
34 { 
35     ......
36     cdev_init(&xxxdev.cdev, &xxx_fops); /* Register character device driver */
37     /* Function details */
38     return 0;
39 }
40
41 static int xxx_remove(struct platform_device *dev)
42 {
43     ......
44     cdev_del(&xxxdev.cdev);/* Delete cdev */
45     /* Function details */
46     return 0;
47 }
48
49 /* Match list */
50 static const struct of_device_id xxx_of_match[] = {
51     { .compatible = "xxx-gpio" },
52     { /* Sentinel */ }
53 };
54
55 /*
56 * platform Platform driven structure
57 */
58 static struct platform_driver xxx_driver = {
59     .driver = {
60     .name = "xxx",
61     .of_match_table = xxx_of_match,
62     },
63     .probe = xxx_probe,
64     .remove = xxx_remove,
65 };
66 
67 /* Driver module loading */
68 static int __init xxxdriver_init(void)
69 {
70     return platform_driver_register(&xxx_driver);
71 }
72
73 /* Driver module unloading */
74 static void __exit xxxdriver_exit(void)
75 {
76     platform_driver_unregister(&xxx_driver);
77 }
78
79 module_init(xxxdriver_init);
80 module_exit(xxxdriver_exit);
81 MODULE_LICENSE("GPL");
82 MODULE_AUTHOR("zuozhongkai");

         Lines 1 to 27, the traditional character device driver, the so-called platform driver, is not independent of other types of drivers other than character device driver, block device driver and network device driver. Platform is only a framework for the separation and layering of drivers. The specific implementation of its drivers still needs character device driver, block device driver or network device driver.

         Lines 33-39, xxx_probe function. This function will be executed after the driver and device are matched successfully. All the character device drivers previously written in the init function of the driver entry will be placed in this probe function. For example, register character device drivers, add cdev, create classes, and so on.

         Lines 41-47, xxx_remove function, platform_ The remove member variable in the driver structure. This function will be executed when the platform device driver is closed. The previous work to be done in the driver uninstall exit function will be put into this function. For example, use iounmap to free memory, delete cdev, log off device number, and so on.

         Lines 50-53, xxx_of_match matching table. If the device tree is used, the driver and device will be matched through this matching table. Line 51 sets a matching item. The compatible value of this matching item is "XXX GPIO". Therefore, when the compatible attribute value of the device node in the device tree is "XXX GPIO", this device will match this driver.

         Line 52 is a tag, of_ device_ The last match in the ID table must be empty.

         Lines 58 to 65 define a platform_driver structure variable xxx_driver, which means platform driven, and the paltform is set in lines 59 to 62_ Device in driver_ Name and of of driver member variables_ match_ Table these two properties. The name attribute is used to match the traditional driver and device, that is, to check whether the name fields of the driver and device are the same. of_ match_ The table attribute is used for driver and device checking under the device tree. For a complete driver, two matching methods must be provided: device tree and no device tree. The last two lines 63 and 64 set the two member variables probe and remove.

         Lines 68 to 71, drive the entry function and call platform_ driver_ The register function registers a platform driver with the Linux kernel, that is, XXX defined above_ Driver structure variable.

         Lines 74 to 77, drive the exit function and call platform_ driver_ The unregister function unloads the previously registered platform driver.

Summary of platform driver implementation process:

         1) Define a platform_driver structure variable.

        2) Implement the probe function.

        3) Implement the remove function.

        4) Implementation of_match_table.

        5) Call platform_ driver_ The register function registers a platform driver with the Linux kernel.

        6) Call platform_driver_unregister   Function to unload the platform driver.

5. platform equipment

         platform_ The device structure represents the platform device. Here, we should note that if the kernel supports the device tree, we will not use the platform again_ Device is used to describe the device, because the device tree is used to describe it. Of course, if you have to use platform_device can also be used to describe device information. platform_ The device structure is defined in the file include / Linux / platform_ In device. H, the structure is as follows:

22 struct platform_device {
23     const char *name; 
24     int id; 
25     bool id_auto;
26     struct device dev;
27     u32 num_resources; 
28     struct resource *resource;
29
30     const struct platform_device_id *id_entry;
31     char *driver_override; /* Driver name to force a match */
32
33     /* MFD cell pointer */
34     struct mfd_cell *mfd_cell;
35
36     /* arch specific additions */
37     struct pdev_archdata archdata;
38 };

         In line 23, name indicates the device name, which should be the same as the name field of the platform driver used. Otherwise, the device cannot match the corresponding driver. For example, if the name field of the corresponding platform driver is "XXX GPIO", the name field should also be set to "XXX GPIO".

         Line 27, num_resources indicates the number of resources, which is generally the size of the resource in line 28.

         In line 28, resource represents resources, that is, device information, such as peripheral registers. The Linux kernel uses resource

         The structure represents a resource, and the content of the resource structure is as follows:

18 struct resource {
19     resource_size_t start;
20     resource_size_t end;
21     const char *name;
22     unsigned long flags;
23     struct resource *parent, *sibling, *child;
24 };

         Start and end represent the start and end information of resources respectively. For memory resources, they represent the start and end addresses of memory, name represents the resource name, flags represents the resource type, and the optional resource types are defined in the file include/linux/ioport.h, as shown below:

29  #define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */
30 
31  #define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */
32  #define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */
33  #define IORESOURCE_MEM 0x00000200
34  #define IORESOURCE_REG 0x00000300 /* Register offsets */
35  #define IORESOURCE_IRQ 0x00000400
36  #define IORESOURCE_DMA 0x00000800
37  #define IORESOURCE_BUS 0x00001000
    ......
104 /* PCI control bits. Shares IORESOURCE_BITS with above PCI ROM. */
105 #define IORESOURCE_PCI_FIXED (1<<4) /* Do not move resource */

         In previous Linux versions that did not support device trees, users need to write a platform_ The device variable is used to describe the device information, and then the platform is used_ device_ The register function registers the device information in the Linux kernel. The prototype of this function is as follows:

        int platform_device_register(struct platform_device *pdev)

         Function parameters and return values have the following meanings:

         pdev: the platform device to register.

         Return value: negative number, failed; 0, successful.

         If you no longer use platform, you can use platform_device_unregister function unregisters the corresponding platform device_ device_ The prototype of unregister function is as follows:

        void platform_device_unregister(struct platform_device *pdev)

         Function parameters and return values have the following meanings:

         pdev: platform device to log off.

         Return value: none.

         The platform device information framework is as follows:

1 /* Register address definition*/
2 #define PERIPH1_REGISTER_BASE (0X20000000) / * first register address of peripheral 1*/  
3 #define PERIPH2_REGISTER_BASE (0X020E0068) / * first address of peripheral 2 register*/
4 #define REGISTER_LENGTH 4
5 
6 /* resources */
7 static struct resource xxx_resources[] = {
8     [0] = {
9         .start = PERIPH1_REGISTER_BASE,
10        .end = (PERIPH1_REGISTER_BASE + REGISTER_LENGTH - 1),
11        .flags = IORESOURCE_MEM,
12    },  
13    [1] = {
14        .start = PERIPH2_REGISTER_BASE,
15        .end = (PERIPH2_REGISTER_BASE + REGISTER_LENGTH - 1),
16        .flags = IORESOURCE_MEM,
17    },
18 }; 
    ......
19
20 /* platform Equipment structure */
21 static struct platform_device xxxdevice = {
22     .name = "xxx-gpio",
23     .id = -1,
24     .num_resources = ARRAY_SIZE(xxx_resources),
25     .resource = xxx_resources,
26 };
27 
28 /* Device module loading */
29 static int __init xxxdevice_init(void)
30 {
31     return platform_device_register(&xxxdevice);
32 }
33
34 /* Device module logout */
35 static void __exit xxx_resourcesdevice_exit(void)
36 {
37     platform_device_unregister(&xxxdevice);
38 }
39
40 module_init(xxxdevice_init);
41 module_exit(xxxdevice_exit);
42 MODULE_LICENSE("GPL");
43 MODULE_AUTHOR("zuozhongkai");

Lines 7-18, array xxx_resources refers to device resources. There are two resources in total, which are the register information of device peripheral 1 and peripheral 2. Therefore, all flags are IORESOURCE_MEM, indicating that the resource is of memory type.

Lines 21-26: platform device structure variable. Note that the name field should be consistent with the name field in the driver used, otherwise the driver and device cannot be matched successfully. num_resources indicates the resource size, which is actually the array XXX_ The number of elements of resources. Here, array is used_ Size to measure the number of elements of an array.

Line 29~32, the device module loading function, calling platform_ in this function. device_ Register registers the platform device with the Linux kernel.

Line 35~38, the device module unloads function, and calls platform_ in this function. device_ Unregister unloads the platform device from the Linux kernel.

The sample code is mainly used in the Linux version that does not support the device tree. When the Linux kernel supports the device tree, the user does not need to register the platform device manually. Because the device information is described in the device tree, when the Linux kernel starts, it will read the device information from the device tree and organize it into a platform_device form, as for the device tree platform_ The specific process of device will not be investigated in detail. Those interested can have a look. There are many blogs on the Internet that explain the whole process in detail.  

Tags: Linux IoT stm32

Posted on Fri, 17 Sep 2021 11:25:46 -0400 by kevin1