Linux driver development study notes [11]: platform device driver

table of Contents

1. Linux driver separation and layering

1.1. Drive separation and separation

1.2, driven layering

2. Driver-Bus-Device Model

2.1 Bus

2.2 Drive

2.3 Equipment

Three, platform platform driver model

3.1 platform bus

3.2 platform driver

3.3 platform equipment

3.4 platform matching process

Four, platform platform driver experimental program writing

4.1 No device tree

4.2 With device tree


1. Linux driver separation and layering

In order to facilitate the writing of drivers, improve software reuse and cross-platform performance, Linux proposes the layering and separation of drivers

1.1. Drive separation and separation

It is divided into host driver and device driver, with a unified API in the middle for connection. Host controller drivers are generally written by semiconductor manufacturers, and device drivers are written under the linux driver framework. This is the bus, driver, and device models in Linux

1.2, driven layering

 

2. Driver-Bus-Device Model

According to the separation and layering of drivers, a bus-driver-device driver framework is derived. The bus code generally does not need to be written by us, provided by the linux kernel. We only need to write drivers and devices. When registering a driver to the bus, the bus will look up all existing devices to see which device matches this driver. Similarly, when registering a device to the bus, the bus will be in the existing Check whether it matches the driver in the driver.

Driver : specific device driver, read, modify, erase, write

Device : device properties, for example, IIC device has IIC device address, speed, etc.

2.1 Bus

Bus data type is the bus_type , using bus_register up to the kernel bus, the main role is to complete match with the drive apparatus. There are many types of buses in Linux, which can be viewed in the /sys/bus directory of the root file system

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;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};

2.2 Drive

The bus data type is device_driver , use driver_register to register the driver with the kernel. After the driver matches the device, the probe function in the driver will be executed . Can be viewed in the /sys/bus/specific bus/drivers directory of the root file system

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;
};

2.3 Equipment

The bus data type is device , use device_register to register the device with the kernel. After the driver matches the device, the probe function in the driver will be executed . Can be viewed in the /sys/bus/specific bus/devices directory of the root file system

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 */
    ...
	/* arch specific additions */
	struct dev_archdata	archdata;

	struct device_node	*of_node; /* associated device tree node */
	struct fwnode_handle	*fwnode; /* firmware device node */

	dev_t			devt;	/* dev_t, creates the sysfs "dev" */
};

Three, platform platform driver model

According to the driver-bus-device driver model, real buses such as IIC, SPI, and USB are completely matched, but some peripherals cannot be attributed to specific buses, such as timers, RTC, and LCD etc. For this reason, the linux kernel has created a virtual platform driver model: platform driver - bus - platform device

3.1 platform bus

For the platform platform, the platform bus is also a bus_type type. platform_match is responsible for the matching of the platform driver and the device. Register the platform bus with the kernel in platform_bus_init ---> bus_register

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_groups	= platform_dev_groups,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

int __init platform_bus_init(void)
{
	int error;

	early_platform_cleanup();

	error = device_register(&platform_bus);
	if (error)
		return error;
	error =  bus_register(&platform_bus_type);
	if (error)
		device_unregister(&platform_bus);
	of_platform_register_reconfig_notifier();
	return error;
}

3.2 platform driver

The platform driver is derived on the basis of struct device_driver, adding some platform own attributes, using platform_driver_register to register the platform driver with the kernel, if the platform driver matches the platform device successfully, the probe in platform_driver will be executed .

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 of_device_id *of_match_table;
    // ---> const char *name;
	const struct platform_device_id *id_table;
	bool prevent_deferred_probe;
};

3.3 platform equipment

The platform driver is derived on the basis of struct device, adding some platform properties. When there is no device tree, you need to use platform_device_register to register platform devices with the kernel. When there is a device tree, you only need to modify the device tree. If the platform driver matches the platform device successfully, the probe in platform_driver will be executed.

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;
	char *driver_override; /* Driver name to force a match */

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};

3.4 platform matching process

Driver and device matching is done through the bus->match function, the match function under the platform bus is platform_match

/**
 * platform_match - bind platform device to platform driver.
 * @dev: device.
 * @drv: driver.
 *
 * Platform device IDs are assumed to be encoded like this:
 * "<name><instance>", where <name> is a short description of the type of
 * device, like "pci" or "floppy", and <instance> is the enumerated
 * instance of the device, like '0' or '42'.  Driver IDs are simply
 * "<name>".  So, extract the <name> from the platform_device structure,
 * and compare it against the name of the driver. Return whether they match
 * or not.
 */
static int platform_match(struct device *dev, struct device_driver *drv)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct platform_driver *pdrv = to_platform_driver(drv);

	/* When driver_override is set, only bind to the matching driver */
	if (pdev->driver_override)
        return !strcmp(pdev->driver_override, drv->name);

	/* Attempt an OF style match first(首先尝试通过设备树来匹配) */
	if (of_driver_match_device(dev, drv))
        return 1;

	/* Then try ACPI style match(再尝试通过acpi来匹配) */
	if (acpi_driver_match_device(dev, drv))
        return 1;

	/* Then try to match against the id table(根据driver下的id_table来匹配dev) */
	if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;

	/* fall-back to driver name match */ 
   /*最终的就是比较字符串,也就是比较 platform_device->name 和 platform_driver->driver->name
    *这是大多数无设备树情况下的匹配使用方式*/
	return (strcmp(pdev->name, drv->name) == 0);
}

When there is a device tree, the matching process is:

of_driver_match_device(dev, drv)
    ---> of_match_device //of_match_table 为 platform驱动下的 device_driver ---> of_match_table

static inline int of_driver_match_device(struct device *dev,const struct device_driver *drv)
{
	return of_match_device(drv->of_match_table, dev) != NULL;
}

/*
 * Struct used for matching a device
 */
struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};

Four, platform platform driver experimental program writing

4.1 No device tree

The experimental routine takes led lighting as an example. It needs to be written in two parts: led device and led driver. The led device contains all the resources of the led, that is, the address information of the IO used. These address information will be provided to the led driver. . That is, in the led driver, the function platform_get_resource will be used to obtain these resources (address information) from the led device

struct resource *platform_get_resource(struct platform_device *dev, 
                                        unsigned int type, //资源类型 
                                        unsigned int num)//资源索引

Program source code:

https://github.com/denghengli/linux_driver/tree/master/18_led_platform

Usage example:

4.2 With device tree

When there is a device tree, the device is described by the device tree, so there is no need to register the device with the bus, but directly modify the device tree, and then write the driver. After the driver and the device are successfully matched, the device information will be converted from the device tree node to the platform_device structure. The experimental routine uses led lighting as an example:

Program source code:

https://github.com/denghengli/linux_driver/tree/master/19_led_dtsplatform

Usage example:

 

Guess you like

Origin blog.csdn.net/m0_37845735/article/details/107307579