platform总线的匹配和探测流程

注册总线

bus_register:bus_type_private的初始化,在这条总线目录下创建/bus/busname,/device, /driver 目录,初始化这条总线上的设备链表:struct klist klist_devices;初始化这条总线上的驱动链表:struct klist klist_drivers

注册设备

不管是平台总线还是其他总线,最终通过device_register来注册设备

device_register
    device_add
        bus_add_device/bus_probe_device
            device_initial_probe
                __device_attach->bus_for_each_drv
                    __device_attach_driver
                        driver_match_device
                            drv->bus->match
                        driver_probe_device
                            really_probe
                                dev->bus->probe | drv->probe
                            

devcie_add

1. bus_add_device将设备添加到总线上,当然总线类型,在生成设备的时候会去初始化

2. device_create调用device_add会传递设备号进去,device_register不会传递设备号,只是单纯注册设备;有设备号就会去创建设备节点文件;紧接着也会发送KOBJ_ADD通知用户层创建设备(内核创建了肯定就不会再创建了),但是这个add的行为还是要上报给用户层做其他的状态同步操作

3. 最后通过bus_probe_device探测驱动

int device_add(struct device *dev)
{
	struct device *parent;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;
	struct kobject *glue_dir = NULL;

	dev = get_device(dev);
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);

	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (IS_ERR(kobj)) {
		error = PTR_ERR(kobj);
		goto parent_error;
	}
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error) {
		glue_dir = get_glue_dir(dev);
		goto Error;
	}

	/* notify platform of device entry */
	error = device_platform_notify(dev, KOBJ_ADD);
	if (error)
		goto platform_error;

	error = device_create_file(dev, &dev_attr_uevent);
	if (error)
		goto attrError;

	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);
	if (error)
		goto DPMError;
	device_pm_add(dev);

	if (MAJOR(dev->devt)) {
		error = device_create_file(dev, &dev_attr_dev);
		if (error)
			goto DevAttrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto SysEntryError;

		devtmpfs_create_node(dev);
	}

	/* Notify clients of device addition.  This call must come
	 * after dpm_sysfs_add() and before kobject_uevent().
	 */
	if (dev->bus)
		blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
					     BUS_NOTIFY_ADD_DEVICE, dev);

	kobject_uevent(&dev->kobj, KOBJ_ADD);
	bus_probe_device(dev);
	if (parent)
		klist_add_tail(&dev->p->knode_parent,
			       &parent->p->klist_children);

	if (dev->class) {
		mutex_lock(&dev->class->p->mutex);
		/* tie the class to the device */
		klist_add_tail(&dev->p->knode_class,
			       &dev->class->p->klist_devices);

		/* notify any interfaces that the device is here */
		list_for_each_entry(class_intf,
				    &dev->class->p->interfaces, node)
			if (class_intf->add_dev)
				class_intf->add_dev(dev, class_intf);
		mutex_unlock(&dev->class->p->mutex);
	}
done:
	put_device(dev);
	return error;
 SysEntryError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &dev_attr_dev);
 DevAttrError:
	device_pm_remove(dev);
	dpm_sysfs_remove(dev);
 DPMError:
	bus_remove_device(dev);
 BusError:
	device_remove_attrs(dev);
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	device_remove_file(dev, &dev_attr_uevent);
 attrError:
	device_platform_notify(dev, KOBJ_REMOVE);
platform_error:
	kobject_uevent(&dev->kobj, KOBJ_REMOVE);
	glue_dir = get_glue_dir(dev);
	kobject_del(&dev->kobj);
 Error:
	cleanup_glue_dir(dev, glue_dir);
parent_error:
	put_device(parent);
name_error:
	kfree(dev->p);
	dev->p = NULL;
	goto done;
}
EXPORT_SYMBOL_GPL(device_add);

/**
 * device_register - register a device with the system.
 * @dev: pointer to the device structure
 *
 * This happens in two clean steps - initialize the device
 * and add it to the system. The two steps can be called
 * separately, but this is the easiest and most common.
 * I.e. you should only call the two helpers separately if
 * have a clearly defined need to use and refcount the device
 * before it is added to the hierarchy.
 *
 * For more information, see the kerneldoc for device_initialize()
 * and device_add().
 *
 * NOTE: _Never_ directly free @dev after calling this function, even
 * if it returned an error! Always use put_device() to give up the
 * reference initialized in this function instead.
 */
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
EXPORT_SYMBOL_GPL(device_register);

注册驱动

不管是平台总线还是其他总线,最终通过driver_register来注册驱动。流程基本跟注册设备一致

driver_register
    bus_add_driver
        driver_attach
            bus_for_each_dev
                __driver_attach
                       driver_match_device
                       driver_probe_device

platform

总线

kernel_init->do_basic_setup->driver_init(先于do_initcalls执行)->platform_bus_init(device_register(&platform_bus);bus_register(&platform_bus_type))

设备

arch_initcall(customize_machine)会把根的子节点都转换成platform_device节点;下图的两种方式都是从根节点遍历子节点,分配struct platform_device的内存,还分配了该platform device需要的resource的内存,加入统一设备模型系统中

static int __init customize_machine(void)
{
	/*
	 * customizes platform devices, or adds new ones
	 * On DT based machines, we fall back to populating the
	 * machine from the device tree, if no callback is provided,
	 * otherwise we would always need an init_machine callback.
	 */
	of_iommu_init();
	if (machine_desc->init_machine)
		machine_desc->init_machine();
#ifdef CONFIG_OF
	else
		of_platform_populate(NULL, of_default_bus_match_table,
					NULL, NULL);
#endif
	return 0;
}

of_default_bus_match_table匹配表如下,根节点下的这些子节点的子节点也会生成platform_device

const struct of_device_id of_default_bus_match_table[] = {
	{ .compatible = "simple-bus", },
	{ .compatible = "simple-mfd", },
	{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
	{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
	{} /* Empty terminated list */
};

of_platform_populate 

初始化设备树的时候,把所有设备树节点都抽象成了device_node实例;而of_platform_bus_create里会找到根节点的所有子节点来填充一个个platform_device实例,可以看到of_platform_populate里的就是找到/节点的device_node开始填充的

/**
 * of_platform_populate() - Populate platform_devices from device tree data
 * @root: parent of the first level to probe or NULL for the root of the tree
 * @matches: match table, NULL to use the default
 * @lookup: auxdata table for matching id and platform_data with device nodes
 * @parent: parent to hook devices from, NULL for toplevel
 *
 * Similar to of_platform_bus_probe(), this function walks the device tree
 * and creates devices from nodes.  It differs in that it follows the modern
 * convention of requiring all device nodes to have a 'compatible' property,
 * and it is suitable for creating devices which are children of the root
 * node (of_platform_bus_probe will only create children of the root which
 * are selected by the @matches argument).
 *
 * New board support should be using this function instead of
 * of_platform_bus_probe().
 *
 * Returns 0 on success, < 0 on failure.
 */
int of_platform_populate(struct device_node *root,
			const struct of_device_id *matches,
			const struct of_dev_auxdata *lookup,
			struct device *parent)
{
	struct device_node *child;
	int rc = 0;

	root = root ? of_node_get(root) : of_find_node_by_path("/");
	if (!root)
		return -EINVAL;

	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(root, OF_POPULATED_BUS);

	of_node_put(root);
	return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);

of_platform_bus_create

of_platform_bus_create函数:创建根节点下的某个子节点以及该子节点下的所有子节点对应的平台设备;

(1).而且是带有平台数据platform_data的;下面的of_platform_device_create_pdata会通过dev->dev.platform_data = platform_data;将数据填充到struct platform_device ->struct device -> platform_data成员中;这个操作也可以设置platform

(2).OF_DEV_AUXDATA的_name成员会作为bus_id 传递到of_device_alloc并通过 dev_set_name(&dev->dev, "%s", bus_id);设置device的name

static int of_platform_bus_create(struct device_node *bus,
				  const struct of_device_id *matches,
				  const struct of_dev_auxdata *lookup,
				  struct device *parent, bool strict)
{
	const struct of_dev_auxdata *auxdata;
	struct device_node *child;
	struct platform_device *dev;
	const char *bus_id = NULL;
	void *platform_data = NULL;
	int rc = 0;
 
	/* Make sure it has a compatible property */
	if (strict && (!of_get_property(bus, "compatible", NULL))) {
		return 0;
	}
    //确保该设备节点之前没有被填充过
	if (of_node_check_flag(bus, OF_POPULATED_BUS)) {
		return 0;
	}
 
    //查看lookup数组里有没有数组元素与该设备节点的compatile和内存空间首地址匹配,
    //如果有匹配,返回该数组元素
	auxdata = of_dev_lookup(lookup, bus);
	if (auxdata) {
		bus_id = auxdata->name;
        //如果设备节点是在lookup数组里,是带有平台数据的,
        //否则没有平台数据
		platform_data = auxdata->platform_data;
	}
 
	dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
 
    //递归创建bus节点的子节点对应的平台设备
	for_each_child_of_node(bus, child) {
		rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
 
    //设置标志,表示该节点填充完
	of_node_set_flag(bus, OF_POPULATED_BUS);
	return rc;
}

lookup示例

static struct mv_usb_platform_data asr1802s_usb_pdata = {
        .mode           = MV_USB_MODE_OTG,
        .otg_force_a_bus_req = 1,
        .disable_otg_clock_gating = 0,
};

static const struct of_dev_auxdata asr1802s_auxdata_lookup[] __initconst = {
....    
    OF_DEV_AUXDATA("asr,mv-otg", 0xd4208100, "mv-otg",
                            &asr1802s_usb_pdata),
....
}



struct of_dev_auxdata {
	char *compatible;
	resource_size_t phys_addr;
	char *name;
	void *platform_data;
};

/* Macro to simplify populating a lookup table */
#define OF_DEV_AUXDATA(_compat,_phys,_name,_pdata) \
	{ .compatible = _compat, .phys_addr = _phys, .name = _name, \
	  .platform_data = _pdata }

///

static void __init asr1802s_dt_init_machine(void)
{
        asr1802s_clk_init();
        pxa_init_dma(IRQ_PXA1822_DMA_INT0, 32);
        mmp_entry_vector_init();

        of_platform_populate(NULL, of_default_bus_match_table,
                             asr1802s_auxdata_lookup, NULL);

        pxa_init_pmua_regdump();
        pxa_init_pmum_regdump();
        pxa_init_dvc_regdump();
        pxa_init_apbs_regdump();
}



DT_MACHINE_START(ASR1802S_DT, "ASR ASR1802S (Device Tree Support)")
        .map_io         = mmp_map_io,
        .init_irq       = irqchip_init,
        .init_time      = asr1802s_timer_init,
        .reserve        = asr1802s_reserve,
        .init_machine   = asr1802s_dt_init_machine,
        .dt_compat      = asr1802s_dt_board_compat,
        .restart        = mmp_arch_restart,
MACHINE_END

of_platform_device_create_pdata

of_platform_device_create_pdata就是真正创建platform_device的地方;设备树中的reg和interrupts资源将会被转换成platform_device内的struct resources资源;其他属性:platform_device->struct device dev->struct device_node *of_node直接指向设备树转换来的device_node

1. of_device_alloc返回一个platform_device

2. of_dev_add 添加一个platform_device

/* 
 * 创建一个 struct platform_device 节点
 */
static struct platform_device *of_platform_device_create_pdata(
                      struct device_node *np,
                      const char *bus_id,
                      void *platform_data,
                      struct device *parent)
{
    struct platform_device *dev;

    /* 判断 status 状态 */
    if (!of_device_is_available(np) ||
        of_node_test_and_set_flag(np, OF_POPULATED))
        return NULL;

    /* 分配并初始化*/
    dev = of_device_alloc(np, bus_id, parent);
    if (!dev)
        goto err_clear_flag;

    dev->dev.bus = &platform_bus_type;
    dev->dev.platform_data = platform_data;		/* 这个 platform_data 应该为NULL*/
    /* 
    * 扫描node的 dma-ranges 属性,并初始化 dev->coherent_dma_mask,dma_mask
    * 如果节点有 iommus 属性
    * 		dev->archdata.mapping = sunxi_mapping (arm_setup_iommu_dma_ops 和具体架构有关)
    * 		dev->archdata.dma_ops = coherent ? &iommu_coherent_ops : &iommu_ops
    * 否则:
    * 		dev->archdata.dma_ops = coherent ? &arm_coherent_dma_ops : &arm_dma_ops;
    */
    of_dma_configure(&dev->dev, dev->dev.of_node);
    /* 没配置 CONFIG_GENERIC_MSI_IRQ_DOMAIN,此函数为空 */
    of_msi_configure(&dev->dev, dev->dev.of_node);

    if (of_device_add(dev) != 0) {
        of_dma_deconfigure(&dev->dev);
        platform_device_put(dev);
        goto err_clear_flag;
    }

    return dev;

err_clear_flag:
    of_node_clear_flag(np, OF_POPULATED);
    return NULL;
}

 struct platform_device *of_device_alloc(struct device_node *np,
                  const char *bus_id,
                  struct device *parent)
{
    struct platform_device *dev;
    int rc, i, num_reg = 0, num_irq;
    struct resource *res, temp_res;

	/* 
	 * 分配并初始化一个 platform_device
	 * 会初始化以下成员:
	 *		pdev.name
	 *		pdev.id
	 *		pdev.dev(device_initialize(&pdev.dev))
	 *		pdev.dev.release = platform_device_release;
	 *		调用 arch_setup_pdev_archdata(&pdev); 该函数在ARM上为空
	 */
    dev = platform_device_alloc("", -1);
    if (!dev)
        return NULL;

    /* 统计io资源:主要是检索 'reg'和 'reg-names' 属性 */
    while (of_address_to_resource(np, num_reg, &temp_res) == 0)
        num_reg++;
    /* 统计中断资源: 主要是检索 ‘interrupts’ 属性 */
    num_irq = of_irq_count(np);

    /* 
     * 填充上述资源到 pdev->resource
     * reg属性指定的地址 res的 flags = IORESOURCE_MEM
     * interrupts属性指定的地址 res 的flags = IORESOURCE_IRQ | irqd_get_trigger_type(irq_get_irq_data(irq));
     */
    if (num_irq || num_reg) {
        res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
        if (!res) {
            platform_device_put(dev);
            return NULL;
        }

        dev->num_resources = num_reg + num_irq;
        dev->resource = res;
        for (i = 0; i < num_reg; i++, res++) {
            rc = of_address_to_resource(np, i, res);
            WARN_ON(rc);
        }
        if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
            pr_debug("not all legacy IRQ resources mapped for %s\n",
                 np->name);
    }
	/* 
	* 初始化 dev 的一些属性
	*/
    dev->dev.of_node = of_node_get(np);
    dev->dev.fwnode = &np->fwnode;
    dev->dev.parent = parent ? : &platform_bus;

    if (bus_id)
        dev_set_name(&dev->dev, "%s", bus_id);
    else
        of_device_make_bus_id(&dev->dev);

    return dev;
}

void of_device_make_bus_id(struct device *dev)
{
    struct device_node *node = dev->of_node;
    const __be32 *reg;
    u64 addr;
    const char __maybe_unused *name;

    /* Construct the name, using parent nodes if necessary to ensure uniqueness */
    while (node->parent) {
        /*
         * If the address can be translated, then that is as much
         * uniqueness as we need. Make it the first component and return
         */
        reg = of_get_property(node, "reg", NULL);
        if (reg && (addr = of_translate_address(node, reg)) != OF_BAD_ADDR) {
            /* 
             * 一般情况都是走这里
             * 所以可看看出 Name = 'reg_addr.node_name.dev_name'
             * 此时 dev_name = ""
             */
            dev_set_name(dev, dev_name(dev) ? "%llx.%s:%s" : "%llx.%s",
                     (unsigned long long)addr, node->name,
                     dev_name(dev));
            return;
        }

        /* format arguments only used if dev_name() resolves to NULL */
        dev_set_name(dev, dev_name(dev) ? "%s:%s" : "%s",
                 strrchr(node->full_name, '/') + 1, dev_name(dev));
        node = node->parent;
    }
}

驱动与设备匹配

匹配流程如下

platform_driver_register(struct platform_driver *drv)
    driver_register(struct device_driver * drv)
        driver_match_device
static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}


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

 匹配过程

platform总线的匹配函数如下:

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

(1) When driver_override is set, only bind to the matching drive

如果设备有driver_override成员,强行只匹配跟device->driver_override的名称相同的驱动

(2) 用设备树中的 compatible 属性和platform_driver中的driver中的 of_match_table 来匹配;of_match_table ;一般用于系统启动时,就开启的设备

pmx: pinmux@d401e000 {
                                compatible = "pinconf-single";
                                reg = <0xd401e000 0x330>;
                                #address-cells = <1>;
                                #size-cells = <1>;
                                #gpio-range-cells = <3>;
                                ranges;

                                pinctrl-single,register-width = <32>;
                                pinctrl-single,function-mask = <7>;

                                range: gpio-range {
                                        #pinctrl-single,gpio-range-cells = <3>;
                                };
                        };

。。。。

static struct of_device_id pcs_of_match[] = {
        { .compatible = "pinctrl-single", .data = (void *)false },
        { .compatible = "pinconf-single", .data = (void *)true },
        { },
};
MODULE_DEVICE_TABLE(of, pcs_of_match);

static struct platform_driver pcs_driver = {
        .probe          = pcs_probe,
        .remove         = pcs_remove,
        .driver = {
                .owner          = THIS_MODULE,
                .name           = DRIVER_NAME,
                .of_match_table = pcs_of_match,
        },
};

(3) 用 platform_driver 中的 id_table 中的和 platform_device 中的 name来匹配;; id_table一般也用于模块化的驱动,比如pci,usb等设备

struct platform_device pdev = {
	.name = "hello1",
	.id = PLATFORM_DEVID_AUTO, //自动分配
	.dev = {
		.release =  pdev_release,
	},
	.resource = res,
	.num_resources = ARRAY_SIZE(res),
};

。。。。。

struct platform_device_id idtable[] = {
	{"hello1",0},
	{"hello2",1},
	{"hello3",2},
	{/*end*/}
};
 
struct platform_driver pdrv = {
	.probe = pdrv_probe,
	.remove = pdrv_remove,
	.driver = {
		.name = "aabbccdd", 
	},
	.id_table = idtable,
};

(4) 用platform_device中的name和 platform_driver 中的 driver 中的 name来匹配;一般用于非设备树创建的platform_device

struct platform_device pdev = {
	.name = "aabbccdd",
	.id = PLATFORM_DEVID_AUTO, //自动分配
	.dev = {
		.release =  pdev_release,
	},
	.resource = res,
	.num_resources = ARRAY_SIZE(res),
};

。。。。

struct platform_driver pdrv = {
    .probe = pdrv_probe,
    .remove = pdrv_remove,
    .driver = {
        .name = "aabbccdd",
    },
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值