那么在 GITCHAT
的驱动里需要定义 GITCHAT
的基地址、中断号等信息。假设 GITCHAT
的地址为0x0001
,中断号是 2
,那么:
#define GITCHAT_BASE 0x0001
#define GITCHAT_INTERRUPT 2
int gitchat_send()
{
writel(GITCHAT_BASE + REG, 1);
...
}
int gitchat_init()
{
request_init(GITCHAT_INTERRUPT, ...);
...
}
比如一个板子上有一个 GITCHAT
,首先向总线注册:
static struct resource gitchat_resource[] = {
{
.start = ...,
.end = ...,
.flags = IORESOURCE_MEM
}...
};
static struct platform_device gitchat_device = {
.name = "gitchat";
.id = 0;
.num_resources = ARRAY_SIZE(gitchat_resource);
.resource = gitchat_resource,
};
static struct platform_device *ip0x_device __initdata = {
&gitchat_device,
...
};
static ini __init ip0x_init(void)
{
platform_add_devices(ip0x_device, ARRAY_SIZE(ip0x_device));
}
下面看下驱动注册总线的代码示例:
static int gitchat_probe(struct platform_device *pdev)
{
...
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
...
}
总线里有很多匹配方式,比如:
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 */
if (acpi_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
从上面可知 platform
总线下的设备和驱动是通过名字进行匹配的,先去匹配 platform_driver
中的 id_table
表中的各个名字与 platform_device->name
名字是否相同,如果相同则匹配。
__platform_driver_register
/**
* __platform_driver_register - register a driver for platform-level devices
* @drv: platform driver structure
* @owner: owning module/driver
*/
int __platform_driver_register(struct platform_driver *drv,
struct module *owner)
{
drv->driver.owner = owner;
drv->driver.bus = &platform_bus_type;
drv->driver.probe = platform_drv_probe;
drv->driver.remove = platform_drv_remove;
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
platform_drv_probe
static int platform_drv_probe(struct device *_dev)
{
struct platform_driver *drv = to_platform_driver(_dev->driver);
struct platform_device *dev = to_platform_device(_dev);
int ret;
ret = of_clk_set_defaults(_dev->of_node, false);
if (ret < 0)
return ret;
ret = dev_pm_domain_attach(_dev, true);
if (ret != -EPROBE_DEFER) {
if (drv->probe) {
ret = drv->probe(dev);
if (ret)
dev_pm_domain_detach(_dev, true);
} else {
/* don't fail if just dev_pm_domain_attach failed */
ret = 0;
}
}
if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
dev_warn(_dev, "probe deferral not supported\n");
ret = -ENXIO;
}
return ret;
}
platform_get_resource
/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type
* @num: resource index
*/
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
{
int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (type == resource_type(r) && num-- == 0)
return r;
}
return NULL;
}
smsc_lan9217_device
static struct platform_device smsc_lan9217_device = {
.name = "smsc911x",
.id = -1,
.dev = {
.platform_data = &smsc911x_config,
},
.num_resources = ARRAY_SIZE(smsc911x_resources),
.resource = smsc911x_resources,
};
smsc911x_resources
static struct resource smsc911x_resources[] = {
[0] = {
.start = 0x28000000,
.end = 0x280000ff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 65,
.end = 65,
.flags = IORESOURCE_IRQ,
},
};
__platform_driver_register
drv->driver.probe = platform_drv_probe;
ret = drv->probe(dev);
.probe = gitchat_probe
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
for (i = 0; i < dev->num_resources; i++)
struct resource *r = &dev->resource[i];
static struct platform_device smsc_lan9217_device = {
.name = "smsc911x",
.id = -1,
.dev = {
.platform_data = &smsc911x_config,
},
.num_resources = ARRAY_SIZE(smsc911x_resources),
.resource = smsc911x_resources,
};
static struct resource smsc911x_resources[] = {
[0] = {
.start = 0x28000000,
.end = 0x280000ff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 65,
.end = 65,
.flags = IORESOURCE_IRQ,
},
};
platform_match_id
static const struct platform_device_id *platform_match_id(
const struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++;
}
return NULL;
}
a. 注册 platform_driver
的过程:
platform_driver_register
__platform_driver_register
drv->driver.probe = platform_drv_probe;
driver_register
bus_add_driver
// 把 platform_driver
放入 platform_bus_type
的driver
链表中
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); // 把 platform_driver 放入 platform_bus_type 的driver链表中
// 对于plarform_bus_type
下的每一个设备, 调用__driver_attach
driver_attach
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); // 对于plarform_bus_type下的每一个设备, 调用__driver_attach
__driver_attach
ret = driver_match_device(drv, dev); // 判断dev和drv是否匹配成功
return drv->bus->match ? drv->bus->match(dev, drv) : 1; // 调用 platform_bus_type.match
driver_probe_device(drv, dev);
really_probe
drv->probe // platform_drv_probe
platform_drv_probe
struct platform_driver *drv = to_platform_driver(_dev->driver);
drv->probe
b. 注册 platform_device
的过程:
platform_device_register
platform_device_add
device_add
bus_add_device
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); // 把 platform_device 放入 platform_bus_type的device链表中
bus_probe_device(dev);
device_initial_probe
__device_attach
ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); // // 对于plarform_bus_type下的每一个driver, 调用 __device_attach_driver
__device_attach_driver
ret = driver_match_device(drv, dev);
return drv->bus->match ? drv->bus->match(dev, drv) : 1; // 调用platform_bus_type.match
driver_probe_device
匹配函数是platform_bus_type.match
, 即platform_match
,
匹配过程按优先顺序罗列如下:
a. 比较platform_dev.driver_override
和 platform_driver.drv->name
b. 比较 platform_dev.dev.of_node
的compatible
属性 和platform_driver.drv->of_match_table
c. 比较 platform_dev.name
和 platform_driver.id_table
d. 比较platform_dev.name
和 platform_driver.drv->name