Detailed explanation of Linux driver 10-platform bus
The platform bus is a knowledge point that must be mastered to learn Linux drivers.
Reference for this article has been published: Linux 3.14 kernel
1. Concept
There are many physical buses in embedded systems: I2c, SPI, USB, uart, PCIE, APB, AHB
Linux has added a new driver management and registration mechanism since 2.6. The platform bus is a virtual bus, not a physical bus.
Compared with PCI and USB, it is mainly used to describe on-chip resources on SOC. The resources described by platform have one thing in common: they are directly addressed on the CPU bus.
Platform devices are assigned a name (used in driver bindings) and a set of resources such as addresses and interrupt request numbers (IRQs).
The device is represented by platform_device, and the driver is registered with platform_driver.
Compared with the traditional bus/device/driver mechanism, the platform is uniformly managed by the kernel and uses resources in the driver, which improves the security and portability of the code.
2. platform
1. The two most important structures of the platform bus
All drivers maintained by the platform must be defined with this structure:
platform_driver
struct platform_driver {
int (*probe)(struct platform_device *); //
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
};
This structure is used to register the driver to the platform bus.
member | meaning |
---|---|
probe | When the driver and hardware information are successfully matched, the probe function will be called, and the registration and initialization of all resources of the driver are placed in the probe function. |
remove | The hardware information has been removed, or the driver has been uninstalled. All must be released. The operation of releasing resources is placed in this function. |
struct device_driver driver | All drivers maintained by the kernel must contain this member. Usually driver->name is used to match the device. |
const struct platform_device_id *id_table | Often a driver may be able to support multiple hardware at the same time, and the names of these hardware are placed in the structure array. |
When we write a driver, we often need to fill in the above members.
platform_device
The platform bus is a structure used to describe device hardware information, including all resources of the hardware (io, memory, interrupts, DMA, etc.).
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
member | meaning |
---|---|
const char *name | The name of the device, used to match the driver |
struct device dev | All devices maintained in the kernel must contain this member. |
u32 num_resources | Number of resources |
struct resource *resource | Describe resources |
struct device dev->release() must be implemented,
The member struct resource that describes the hardware information
0x139d0000
struct resource {
resource_size_t start; //表示资源的起始值,
resource_size_t end; //表示资源的最后一个字节的地址, 如果是中断,end和satrt相同
const char *name; // 可不写
unsigned long flags; //资源的类型
struct resource *parent, *sibling, *child;
};
flags的类型说明
#define IORESOURCE_MEM 0x00000200 //内存
#define IORESOURCE_IRQ 0x00000400 //中断
All drivers managed by the kernel must contain a member called struct device_driver . //The hardware described by men must contain members of the struct device structure. //female
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
};
in:
const char *name;
Used to match hardware.
The kernel describes the hardware and must contain members of the struct device structure:
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this
device */
void *platform_data; /* Platform specific data, device
core doesn't touch it */
struct dev_pm_info power;
struct dev_pm_domain *pm_domain;
#ifdef CONFIG_PINCTRL
struct dev_pin_info *pins;
#endif
#ifdef CONFIG_NUMA
int numa_node; /* NUMA node this device is close to */
#endif
u64 *dma_mask; /* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
alloc_coherent mappings as
not all hardware supports
64 bit addresses for consistent
allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_head dma_pools; /* dma pools (if dma'ble) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
#ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma
allocations */
#endif
/* arch specific additions */
struct dev_archdata archdata;
struct device_node *of_node; /* associated device tree node */
struct acpi_dev_node acpi_node; /* associated ACPI device node */
dev_t devt; /* dev_t, creates the sysfs "dev" */
u32 id; /* device instance */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **groups; /* optional groups */
void (*release)(struct device *dev);
struct iommu_group *iommu_group;
bool offline_disabled:1;
bool offline:1;
};
in:
void (*release)(struct device *dev);
Can not be empty.
2. How to register
Steps to register a platform driver
1) Register driver platform_device_register
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
2) Register device platform_driver_register
#define platform_driver_register(drv) \
__platform_driver_register(drv, THIS_MODULE)
3. Examples
1. Development steps
The development steps of the platform bus driver are:
equipment
The structure that needs to be implemented is: platform_device.
1) Initialize resource structure variables
2) Initialize platform_device structure variable
3) Register the device with the system: platform_device_register.
The above three steps must be completed before the device driver is loaded, that is, before platform_driver_register() is executed, because the driver registration needs to match all registered device names in the kernel.
Adding device to the kernel in platform_driver_register() ultimately calls the device_add function.
The main difference between Platform_device_add and device_add is the additional step of insert_resource (p, r), which adds the platform resource (resource) into the kernel and is managed uniformly by the kernel.
drive
In driver registration, the structure that needs to be implemented is: platform_driver.
In the driver's initialization function, platform_driver_register() is called to register platform_driver.
It should be noted that the value of the name variable in platform_driver and platform_device must be the same [ without considering the device tree , a new article will be written in detail about the device tree later].
In this way, when registering in platform_driver_register(), the value of the name variable in the currently registered platform_driver will be compared with the value of the name variable in all registered platform_devices. Only a platform_device with the same name can be successfully registered.
When the registration is successful, the platform_driver structure element probe function pointer will be called.
Example 1
This example is relatively simple and is only used to test whether platform_driver and platform_device can match successfully.
The left side is the code registered by the platform_device structure, and the right side is the code registered by the platform_driver structure.
platform_driver definition and registration:
1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/platform_device.h>
4 #include <linux/ioport.h>
5
6 static int hello_probe(struct platform_device *pdev)
7 {
8 printk("match ok \n");
9 return 0;
10 }
11 static int hello_remove(struct platform_device *pdev)
12 {
13 printk("hello_remove \n");
14 return 0;
15 }
16 static struct platform_driver hello_driver =
17 {
18 .probe = hello_probe,
19 .driver.name = "duang",
20 .remove = hello_remove,
21 };
22 static int hello_init(void)
23 {
24 printk("hello_init \n");
25 return platform_driver_register(&hello_driver);
26 }
27 static void hello_exit(void)
28 {
29 printk("hello_exit \n");
30 platform_driver_unregister(&hello_driver);
31 return;
32 }
33 MODULE_LICENSE("GPL");
34 module_init(hello_init);
35 module_exit(hello_exit);
platform_device definition and registration:
1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/platform_device.h>
4 #include <linux/ioport.h>
5
6 static void hello_release(struct device *dev)
7 {
8 return;
9 }
10 static struct platform_device hello_device =
11 {
12 .name = "duang",
13 .id = -1,
14 .dev.release = hello_release,
15 };
16
17
18 static int hello_init(void)
19 {
20 printk("hello_init \n");
21 return platform_device_register(&hello_device);
22
23 }
24 static void hello_exit(void)
25 {
26 printk("hello_exit \n");
27 platform_device_unregister(&hello_device);
28 return;
29 }
30 MODULE_LICENSE("GPL");
31 module_init(hello_init);
32 module_exit(hello_exit);
This program is only used to test whether the platform framework can be successfully matched. struct platform_device hello_device does not set any hardware information.
Makfile
1 ifneq ($(KERNELRELEASE),)
2 obj-m:=device.o driver.o
3 else
4 KDIR :=/lib/modules/$(shell uname -r)/build
5 PWD :=$(shell pwd)
6 all:
7 make -C $(KDIR) M=$(PWD) modules
8 clean:
9 rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order
10 endif
This makefile can compile two C files into ko files at the same time.
Compile:
Compile the generated files:
Load module
清空log信息
sudo dmesg -c
Example 2
Add hardware information to the structure platform_device and make it readable in the kernel. This example adds the following information to the hello_device structure:
-
The base register address is 0x139d0000, the space of this address is 0x4
-
Interrupt number 199 [Note] The actual kernel will calculate a new interrupt number based on the HW id of the peripheral device (usually the SOC manufacturer will define a unique ID for each interrupt source when the device is SOC). The interrupt number is The number will be recognized by the CPU.
device.c
struct resource res[]={
[0] ={
.start = 0x139d0000,
.end = 0x139d0000 + 0x3,
.flags = IORESOURCE_MEM,
},
[1] ={
.start = 199,
.end = 199,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device hello_device =
{
.name = "duang",
.id = -1,
.dev.release = hello_release,
.num_resources = ARRAY_SIZE(res),
.resource = res,
};
driver.c
static int hello_probe(struct platform_device *pdev)
{
printk("match ok \n");
printk("mem = %x \n",pdev->resource[0].start);
printk("irq = %d \n",pdev->resource[1].start);
//注册中断、申请内存
return 0;
}
Recompile, uninstall the module of the first example, and clear the log:
make
sudo rmmod device
sudo rmmod driver
sudo dmesg -c
implement
It can be seen from the results that the probe function correctly read the hardware information.
4. How is platform_device managed?
1. No device tree
When there is no device tree, taking Samsung Cortex-A8 s5pc100 as an example, the hardware information is placed in the following location
arch\arm\mach-s5pc100\Mach-smdkc100.c
arch\arm\plat-samsung\
This array stores information about the hardware that needs to be initialized for kernel startup.
2. If there is a device tree
The kernel will have complete code for device initialization. It will parse and initialize the device tree information when the kernel starts, and initialize the hardware information into the corresponding linked list. After the bus matching is successful, the hardware information will be passed to the probe() function.
4. Other knowledge points related to the bus
1. Kernel bus related structure variables
All buses maintained by the kernel need to register a variable with the following structure.
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct device_attribute *dev_attrs; /* use dev_groups instead */
const struct attribute_group **bus_groups;
const struct attribute_group **dev_groups;
const struct attribute_group **drv_groups;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*online)(struct device *dev);
int (*offline)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct iommu_ops *iommu_ops;
struct subsys_private *p;
struct lock_class_key lock_key;
};
The definition of platform bus variable struct bus_type platform_bus_type is defined as follows:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
The most important member is **.match**.
When the hardware information of a device is registered to the platform_bus_type bus, all drivers maintained by the platform bus will be traversed and matched by name. If they are the same, it means that the hardware information and the driver match, and the driver's platform_driver ->probe function will be called to initialize. All resources of the driver to make the driver effective.
When a device driver is registered to the platform_bus_type bus, all hardware information maintained by the platform bus will be traversed and matched by name. If they are the same, it means that the hardware information and the driver match, and the driver's platform_driver ->probe function will be called to initialize All resources of the driver to make the driver effective.
Register location
drivers\base\Platform.c
5. Detailed explanation of registration code process
The advantage of stroking the architecture is that it can help us locate problems
1. When is the match function called?
2. When is the probe function called?
The following is the calling process of the above two problem codes:
We will introduce the device tree in detail later.
To join the group, please add Koujun’s personal WeChat account to take you to the advanced level of embedded.
Reply " 1024 " in the official account to get free learning materials. We look forward to your attention~