MTK6580(Android6.0)-使用DTS注册平台设备、匹配平台驱动

news/2024/11/29 9:37:11/


一、初始化device tree
file:kernel-3.18/init/main.c
asmlinkage __visible void __init start_kernel(void)
{...setup_arch(&command_line);...
}
file:kernel-3.18/arch/arm64/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{	...unflatten_device_tree();...
}
文件:kernel-3.18/drivers/of/fdt.c
/*** unflatten_device_tree - create tree of device_nodes from flat blob** unflattens the device-tree passed by the firmware, creating the* tree of struct device_node. It also fills the "name" and "type"* pointers of the nodes so the normal device-tree walking functions* can be used.*/
void __init unflatten_device_tree(void)
{__unflatten_device_tree(initial_boot_params, &of_allnodes,early_init_dt_alloc_memory_arch);/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */of_alias_scan(early_init_dt_alloc_memory_arch);
}
其中of_allnodes的类型为struct device_node ,各成员解析如下:
struct device_node {const char *name;----------------------device node nameconst char *type;-----------------------对应device_type的属性phandle phandle;-----------------------对应该节点的phandle属性const char *full_name; ----------------从“/”开始的,表示该node的full pathstruct property *properties;-------------该节点的属性列表struct property *deadprops; ----------如果需要,删除某些属性,并挂入到deadprops的列表struct device_node *parent;------parent、child以及sibling将所有的device node连接起来struct device_node *child;struct device_node *sibling;struct device_node *next; --------通过该指针可以获取相同类型的下一个nodestruct device_node *allnext;-------通过该指针可以获取node global list下一个nodestruct proc_dir_entry *pde;--------开放到userspace的proc接口信息struct kref kref;-------------该node的reference countunsigned long _flags;void *data;
};
file:kernel-3.18/drivers/of/fdt.c
/*** __unflatten_device_tree - create tree of device_nodes from flat blob** unflattens a device-tree, creating the* tree of struct device_node. It also fills the "name" and "type"* pointers of the nodes so the normal device-tree walking functions* can be used.* @blob: The blob to expand* @mynodes: The device_node tree created by the call* @dt_alloc: An allocator that provides a virtual address to memory* for the resulting tree*/
static void __unflatten_device_tree(void *blob,struct device_node **mynodes, //mynodes 为全局链表void * (*dt_alloc)(u64 size, u64 align))
{unsigned long size;int start;void *mem;struct device_node **allnextp = mynodes;pr_debug(" -> unflatten_device_tree()\n");if (!blob) {pr_debug("No device tree pointer\n");return;}pr_debug("Unflattening device tree:\n");pr_debug("magic: %08x\n", fdt_magic(blob));pr_debug("size: %08x\n", fdt_totalsize(blob));pr_debug("version: %08x\n", fdt_version(blob));if (fdt_check_header(blob)) {pr_err("Invalid device tree blob header\n");return;}/* First pass, scan for size */start = 0;size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0);size = ALIGN(size, 4);pr_debug("  size is %lx, allocating...\n", size);/* Allocate memory for the expanded device tree */mem = dt_alloc(size + 4, __alignof__(struct device_node));memset(mem, 0, size);*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);pr_debug("  unflattening %p...\n", mem);/* Second pass, do actual unflattening */start = 0;unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);if (be32_to_cpup(mem + size) != 0xdeadbeef)pr_warning("End of tree marker overwritten: %08x\n",be32_to_cpup(mem + size));*allnextp = NULL;pr_debug(" <- unflatten_device_tree()\n");
}
/*** unflatten_dt_node - Alloc and populate a device_node from the flat tree* @blob: The parent device tree blob* @mem: Memory chunk to use for allocating device nodes and properties* @p: pointer to node in flat tree* @dad: Parent struct device_node* @allnextpp: pointer to ->allnext from last allocated device_node* @fpsize: Size of the node path up at the current depth.*/
static void * unflatten_dt_node(void *blob,void *mem,int *poffset,struct device_node *dad,struct device_node ***allnextpp,unsigned long fpsize)
{const __be32 *p;struct device_node *np;struct property *pp, **prev_pp = NULL;const char *pathp;unsigned int l, allocl;static int depth = 0;int old_depth;int offset;int has_name = 0;int new_format = 0;pathp = fdt_get_name(blob, *poffset, &l);if (!pathp)return mem;allocl = l++;/* version 0x10 has a more compact unit name here instead of the full* path. we accumulate the full path size using "fpsize", we'll rebuild* it later. We detect this because the first character of the name is* not '/'.*/if ((*pathp) != '/') {new_format = 1;if (fpsize == 0) {/* root node: special case. fpsize accounts for path* plus terminating zero. root node only has '/', so* fpsize should be 2, but we want to avoid the first* level nodes to have two '/' so we use fpsize 1 here*/fpsize = 1;allocl = 2;l = 1;pathp = "";} else {/* account for '/' and path size minus terminal 0* already in 'l'*/fpsize += l;allocl = fpsize;}}np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node));if (allnextpp) {char *fn;of_node_init(np);np->full_name = fn = ((char *)np) + sizeof(*np);if (new_format) {/* rebuild full path for new format */if (dad && dad->parent) {strcpy(fn, dad->full_name);
#ifdef DEBUGif ((strlen(fn) + l + 1) != allocl) {pr_debug("%s: p: %d, l: %d, a: %d\n",pathp, (int)strlen(fn),l, allocl);}
#endiffn += strlen(fn);}*(fn++) = '/';}memcpy(fn, pathp, l);prev_pp = &np->properties;**allnextpp = np;*allnextpp = &np->allnext;if (dad != NULL) {np->parent = dad;/* we temporarily use the next field as `last_child'*/if (dad->next == NULL)dad->child = np;elsedad->next->sibling = np;dad->next = np;}}/* process properties */for (offset = fdt_first_property_offset(blob, *poffset);(offset >= 0);(offset = fdt_next_property_offset(blob, offset))) {const char *pname;u32 sz;if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {offset = -FDT_ERR_INTERNAL;break;}if (pname == NULL) {pr_info("Can't find property name in list !\n");break;}if (strcmp(pname, "name") == 0)has_name = 1;pp = unflatten_dt_alloc(&mem, sizeof(struct property),__alignof__(struct property));if (allnextpp) {/* We accept flattened tree phandles either in* ePAPR-style "phandle" properties, or the* legacy "linux,phandle" properties.  If both* appear and have different values, things* will get weird.  Don't do that. */if ((strcmp(pname, "phandle") == 0) ||(strcmp(pname, "linux,phandle") == 0)) {if (np->phandle == 0)np->phandle = be32_to_cpup(p);}/* And we process the "ibm,phandle" property* used in pSeries dynamic device tree* stuff */if (strcmp(pname, "ibm,phandle") == 0)np->phandle = be32_to_cpup(p);pp->name = (char *)pname;pp->length = sz;pp->value = (__be32 *)p;*prev_pp = pp;prev_pp = &pp->next;}}/* with version 0x10 we may not have the name property, recreate* it here from the unit name if absent*/if (!has_name) {const char *p1 = pathp, *ps = pathp, *pa = NULL;int sz;while (*p1) {if ((*p1) == '@')pa = p1;if ((*p1) == '/')ps = p1 + 1;p1++;}if (pa < ps)pa = p1;sz = (pa - ps) + 1;pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,__alignof__(struct property));if (allnextpp) {pp->name = "name";pp->length = sz;pp->value = pp + 1;*prev_pp = pp;prev_pp = &pp->next;memcpy(pp->value, ps, sz - 1);((char *)pp->value)[sz - 1] = 0;pr_debug("fixed up name for %s -> %s\n", pathp,(char *)pp->value);}}if (allnextpp) {*prev_pp = NULL;np->name = of_get_property(np, "name", NULL);np->type = of_get_property(np, "device_type", NULL);if (!np->name)np->name = "<NULL>";if (!np->type)np->type = "<NULL>";}old_depth = depth;*poffset = fdt_next_node(blob, *poffset, &depth);if (depth < 0)depth = 0;while (*poffset > 0 && depth > old_depth)mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,fpsize);if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)pr_err("unflatten: error %d processing FDT\n", *poffset);return mem;
}
二、具体创建platform device的过程

file:kernel-3.18/arch/arm64/kernel/setup.c

//系统调用of_platform_populate

static int __init arm64_device_init(void)
{of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);return 0;
}
arch_initcall_sync(arm64_device_init); //arch_initcall_sync为宏函数,这里可以理解为module的加载(和module_init类似)
file:kernel-3.18/drivers/of/platform.c
/*** 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)break;}of_node_put(root);return rc;
}
file:kernel-3.18/drivers/of/base.c
static struct device_node *__of_find_node_by_path(struct device_node *parent,const char *path)
{struct device_node *child;int len = strchrnul(path, '/') - path;if (!len)return NULL;__for_each_child_of_node(parent, child) {const char *name = strrchr(child->full_name, '/');if (WARN(!name, "malformed device_node %s\n", child->full_name))continue;name++;if (strncmp(path, name, len) == 0 && (strlen(name) == len))return child;}return NULL;
}

    在这个函数中有一个很关键的全局变量:allnodes,它的定义是在 drivers/of/base.c 里面,struct device_node
*allnodes;这应该所就是那个所谓的“device tree data”了。它应该指向了 device tree 的根节点。我们知道 device
 tree 是由 DTC(Device Tree Compiler)编译成二进制文件DTB(Ddevice Tree Blob)的,然后在系统上电之后由
bootloader加载到内存中去,这个时候还没有device tree,而在内存中只有一个所谓的 DTB地址,这只是一个以某个内
存地开始的一堆原始的dt数据,没有树结构。kernel的任务需要把这些数据转换成一个树结构然后再把这棵树的根节点的
地址赋值给allnodes就行了。这个过程一定是非常重要,因为没有这个 device tree 那所有的设备就没办法初始化,
所以这个 dt 树的形成一定在kernel 刚刚启动的时候就完成了。

/*** of_platform_bus_create() - Create a device for a node and its children.* @bus: device node of the bus to instantiate* @matches: match table for bus nodes* @lookup: auxdata table for matching id and platform_data with device nodes* @parent: parent for new device, or NULL for top level.* @strict: require compatible property** Creates a platform_device for the provided device_node, and optionally* recursively create devices for all the child nodes.*/
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))) { //没有compatible直接返回pr_debug("%s() - skipping %s, no compatible prop\n", __func__, bus->full_name);return 0;}auxdata = of_dev_lookup(lookup, bus); //在传入lookup table寻找和该device node匹配的附加数据 if (auxdata) {bus_id = auxdata->name;platform_data = auxdata->platform_data;}if (of_device_is_compatible(bus, "arm,primecell")) {/** Don't return an error here to keep compatibility with older* device tree files.*/of_amba_device_create(bus, bus_id, platform_data, parent);return 0;}/*这个函数是真正生成struct device的地方*/dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);if (!dev || !of_match_node(matches, bus))return 0;/*如果compatible属性不是"simple-bus"和"arm,amba-bus"则在返回,不继续遍历子节点。这里我的理解是"simple-bus"和"arm,amba-bus"这两种设备不具备热插拔能力,因此在这里就先创建了struct device*/for_each_child_of_node(bus, child) {pr_debug("   create child: %s\n", child->full_name);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;
}/*** of_platform_device_create_pdata - Alloc, initialize and register an of_device* @np: pointer to node to create device for* @bus_id: name to assign device* @platform_data: pointer to populate platform_data pointer with* @parent: Linux device model parent device.** Returns pointer to created platform device, or NULL if a device was not* registered.  Unavailable devices will not get registered.*/
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;if (!of_device_is_available(np) ||of_node_test_and_set_flag(np, OF_POPULATED))return NULL;
/* of_device_alloc除了分配struct platform_device的内存,还分配了该platform device需要的resource的内存(参考struct platform_device 中的resource成员)。当然,这就需要解析该device node的interrupt资源以及memory address资源,这些资源的原始数据都来自dtb中。*/dev = of_device_alloc(np, bus_id, parent);if (!dev)goto err_clear_flag;of_dma_configure(&dev->dev);dev->dev.bus = &platform_bus_type;  //设置匹配方式dev->dev.platform_data = platform_data;/* We do not fill the DMA ops for platform devices by default.* This is currently the responsibility of the platform code* to do such, possibly using a device notifier*/if (of_device_add(dev) != 0) {  把这个device加入到设备模型中,后续驱动注册的时候就可以匹配到了platform_device_put(dev);   goto err_clear_flag;}return dev;err_clear_flag:of_node_clear_flag(np, OF_POPULATED);return NULL;
}
file:kernel-3.18/drivers/of/device.c
int of_device_add(struct platform_device *ofdev)
{BUG_ON(ofdev->dev.of_node == NULL);/* name and id have to be set so that the platform bus doesn't get* confused on matching */ofdev->name = dev_name(&ofdev->dev);ofdev->id = -1;/* device_add will assume that this device is on the same node as* the parent. If there is no parent defined, set the node* explicitly */if (!ofdev->dev.parent)set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));return device_add(&ofdev->dev);
}
file:kernel-3.18/drivers/base/core.c
/*** device_add - add device to device hierarchy.* @dev: device.** This is part 2 of device_register(), though may be called* separately _iff_ device_initialize() has been called separately.** This adds @dev to the kobject hierarchy via kobject_add(), adds it* to the global and sibling lists for the device, then* adds it to the other relevant subsystems of the driver model.** Do not call this routine or device_register() more than once for* any device structure.  The driver model core is not designed to work* with devices that get unregistered and then spring back to life.* (Among other things, it's very hard to guarantee that all references* to the previous incarnation of @dev have been dropped.)  Allocate* and register a fresh new struct device instead.** NOTE: _Never_ directly free @dev after calling this function, even* if it returned an error! Always use put_device() to give up your* reference instead.*/
int device_add(struct device *dev)
{struct device *parent = NULL;struct kobject *kobj;struct class_interface *class_intf;int error = -EINVAL;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 (kobj)dev->kobj.parent = kobj;/* use parent numa_node */if (parent)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)goto Error;/* notify platform of device entry */if (platform_notify)platform_notify(dev);error = device_create_file(dev, &dev_attr_uevent);if (error)goto attrError;if (MAJOR(dev->devt)) {error = device_create_file(dev, &dev_attr_dev);if (error)goto ueventattrError;error = device_create_sys_dev_entry(dev);if (error)goto devtattrError;devtmpfs_create_node(dev);}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);/* 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); //------------very important----------------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->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;DPMError:bus_remove_device(dev);BusError:device_remove_attrs(dev);AttrsError:device_remove_class_symlinks(dev);SymlinkError:if (MAJOR(dev->devt))devtmpfs_delete_node(dev);if (MAJOR(dev->devt))device_remove_sys_dev_entry(dev);devtattrError:if (MAJOR(dev->devt))device_remove_file(dev, &dev_attr_dev);ueventattrError:device_remove_file(dev, &dev_attr_uevent);attrError:kobject_uevent(&dev->kobj, KOBJ_REMOVE);kobject_del(&dev->kobj);Error:cleanup_device_parent(dev);if (parent)put_device(parent);
name_error:kfree(dev->p);dev->p = NULL;goto done;
}
EXPORT_SYMBOL_GPL(device_add);
file:kernel-3.18/drivers/base/bus.c
/*** bus_add_device - add device to bus* @dev: device being added** - Add device's bus attributes.* - Create links to device's bus.* - Add the device to its bus's list of devices.*/
int bus_add_device(struct device *dev)
{struct bus_type *bus = bus_get(dev->bus);int error = 0;if (bus) {pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));error = device_add_attrs(bus, dev);if (error)goto out_put;error = device_add_groups(dev, bus->dev_groups);if (error)goto out_id;error = sysfs_create_link(&bus->p->devices_kset->kobj,&dev->kobj, dev_name(dev));if (error)goto out_groups;error = sysfs_create_link(&dev->kobj,&dev->bus->p->subsys.kobj, "subsystem");if (error)goto out_subsys;klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);}return 0;out_subsys:sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_groups:device_remove_groups(dev, bus->dev_groups);
out_id:device_remove_attrs(bus, dev);
out_put:bus_put(dev->bus);return error;
}
file:kernel-3.18/drivers/base/core.c
int device_add_groups(struct device *dev, const struct attribute_group **groups)
{return sysfs_create_groups(&dev->kobj, groups);
}
三、匹配平台驱动
file:kernel-3.18/drivers/base/bus.c
/*** bus_probe_device - probe drivers for a new device* @dev: device to probe** - Automatically probe for a driver if the bus allows it.*/
void bus_probe_device(struct device *dev)
{struct bus_type *bus = dev->bus;struct subsys_interface *sif;int ret;if (!bus)return;if (bus->p->drivers_autoprobe) {ret = device_attach(dev);WARN_ON(ret < 0);}mutex_lock(&bus->p->mutex);list_for_each_entry(sif, &bus->p->interfaces, node)if (sif->add_dev)sif->add_dev(dev, sif);mutex_unlock(&bus->p->mutex);
}
file:kernel-3.18/drivers/base/dd.c
/*** device_attach - try to attach device to a driver.* @dev: device.** Walk the list of drivers that the bus has and call* driver_probe_device() for each pair. If a compatible* pair is found, break out and return.** Returns 1 if the device was bound to a driver;* 0 if no matching driver was found;* -ENODEV if the device is not registered.** When called for a USB interface, @dev->parent lock must be held.*/
int device_attach(struct device *dev)
{int ret = 0;device_lock(dev);if (dev->driver) {if (klist_node_attached(&dev->p->knode_driver)) {ret = 1;goto out_unlock;}ret = device_bind_driver(dev);if (ret == 0)ret = 1;else {dev->driver = NULL;ret = 0;}} else {ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);pm_request_idle(dev);}
out_unlock:device_unlock(dev);return ret;
}
<span style="font-size:14px;">file<span style="color:#009900;">:kernel-3.18/drivers/base/dd.c</span></span>
static int __device_attach(struct device_driver *drv, void *data)
{struct device *dev = data;if (!driver_match_device(drv, dev))return 0;return driver_probe_device(drv, dev);
}
file:kernel-3.18/drivers/base/base.h
static inline int driver_match_device(struct device_driver *drv,struct device *dev)
{return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
file:kernel-3.18/drivers/base/dd.c
/*** driver_probe_device - attempt to bind device & driver together* @drv: driver to bind a device to* @dev: device to try to bind to the driver** This function returns -ENODEV if the device is not registered,* 1 if the device is bound successfully and 0 otherwise.** This function must be called with @dev lock held.  When called for a* USB interface, @dev->parent lock must be held as well.*/
int driver_probe_device(struct device_driver *drv, struct device *dev)
{int ret = 0;if (!device_is_registered(dev))return -ENODEV;pr_debug("bus: '%s': %s: matched device %s with driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);pm_runtime_barrier(dev);ret = really_probe(dev, drv);pm_request_idle(dev);return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{int ret = 0;int local_trigger_count = atomic_read(&deferred_trigger_count);atomic_inc(&probe_count);pr_debug("bus: '%s': %s: probing driver %s with device %s\n",drv->bus->name, __func__, drv->name, dev_name(dev));WARN_ON(!list_empty(&dev->devres_head));dev->driver = drv;/* If using pinctrl, bind pins now before probing */ret = pinctrl_bind_pins(dev);if (ret)goto probe_failed;if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}if (dev->bus->probe) {ret = dev->bus->probe(dev);if (ret)goto probe_failed;} else if (drv->probe) {ret = drv->probe(dev);if (ret)goto probe_failed;}driver_bound(dev);ret = 1;pr_debug("bus: '%s': %s: bound device %s to driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);goto done;probe_failed:devres_release_all(dev);driver_sysfs_remove(dev);dev->driver = NULL;dev_set_drvdata(dev, NULL);if (ret == -EPROBE_DEFER) {/* Driver requested deferred probing */dev_info(dev, "Driver %s requests probe deferral\n", drv->name);driver_deferred_probe_add(dev);/* Did a trigger occur while probing? Need to re-trigger if yes */if (local_trigger_count != atomic_read(&deferred_trigger_count))driver_deferred_probe_trigger();} else if (ret != -ENODEV && ret != -ENXIO) {/* driver matched but the probe failed */printk(KERN_WARNING"%s: probe of %s failed with error %d\n",drv->name, dev_name(dev), ret);} else {pr_debug("%s: probe of %s rejects match %d\n",drv->name, dev_name(dev), ret);}/** Ignore errors returned by ->probe so that the next driver can try* its luck.*/ret = 0;
done:atomic_dec(&probe_count);wake_up(&probe_waitqueue);return ret;
}
在platform_driver 的注册中,将drv->driver.probe赋值为platform_drv_probe
file:kernel-3.18/drivers/base/platform.c
/*** __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;if (drv->probe)drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;return driver_register(&drv->driver);
}
在platform_drv_probe 中调用drv->probe(dev)也就是platform_driver的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) {ret = drv->probe(dev);if (ret)dev_pm_domain_detach(_dev, true);}if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {dev_warn(_dev, "probe deferral not supported\n");ret = -ENXIO;}return ret;
}
四、总结
   其实引入dts机制后,总线设备如platform bus,i2c bus 的注册,直接从从dts中解析设备资源,之后注册设备流程和
先前设备注册大同小异,而驱动注册和加载以及设备、驱动的匹配和先前没有区别。也就是说,Linux核心思想设备、驱动
、总线关系没有改变,改变只是一小部分实现形式而已。dts在新版内核已经完全支持,对于我们普通的内核驱动开发人员
来说,只需要了解其简单的dts节点规则, 设备驱动匹配规则,能够熟练的从dts中解析数据等(当然这不是本章内容)。
ps:dts 的注册平台设备和匹配平台驱动的流程和很简单,具体实现细节十分麻烦,例如它用C语言进行的许多解析操作,
涉及到繁琐算法,本篇不作分析


http://www.ppmy.cn/news/494634.html

相关文章

智能硬件产品系列 之 MTK6580方案篇(一)硬件选型

智能硬件产品系列 之 MTK6580开发篇&#xff08;一&#xff09;硬件选型 开篇小序主板设计&#xff1a;自己设计主板&#xff0c;还是购买核心板&#xff1f;拿来主义&#xff1a;万能的淘宝功能规划&#xff1a;量力而为 开篇小序 还不知道要在家待多久&#xff0c;这时候能接…

FWD:互联网重塑中国商业

《金融时报》中文网特约撰稿人程苓峰 观一叶落知天下秋。先从一个场景说起。 2009年9月10号晚&#xff0c;阿里巴巴集团在2万人聚集的杭州黄龙体育场搞十周年庆典。场上表演&#xff0c;是装扮古怪的马云和来自五大洲的员工。嘉宾台上看戏的高朋满 座&#xff0c;有比如万通冯仑…

程序员面试之道(《程序员面试笔试宝典》)之求职的时候该不该只看钱?

所谓好公司&#xff1a;一是收入&#xff0c;二是环境&#xff0c;三是未来&#xff0c;还有就是无形的福利&#xff0c;比如&#xff1a;和你一起工作的同事都是素质高又专业的人&#xff0c;会让你在工作中更有愉悦感和成就感。 ——《杜拉拉之似水年华》 求职的时候&#…

程序员求职之道(《程序员面试笔试宝典》)之求职的时候该不该只看钱?

所谓好公司&#xff1a;一是收入&#xff0c;二是环境&#xff0c;三是未来&#xff0c;还有就是无形的福利&#xff0c;比如&#xff1a;和你一起工作的同事都是素质高又专业的人&#xff0c;会让你在工作中更有愉悦感和成就感。 ——《杜拉拉之似水年华》 求职的时候&#x…

腾讯阿里混战云服务,七牛云营收增速300%以上,凭什么?

七牛云创始人兼CEO许式伟 巨头云集的云计算领域&#xff0c;创业者如何从细分领域切进&#xff0c;撕开市场入口&#xff1f; 亚马逊云计算在中国正式投入商用后&#xff0c;中国云计算产业再添巨头&#xff1a;腾讯、阿里巴巴、百度……巨头包围之下&#xff0c;七牛云这家创…

计算机网络笔记:电子邮件概述

电子邮件概述&#xff1a;电子邮件把邮件发送到收件人使用的邮件服务器&#xff0c;并放在其中的收件人邮箱中&#xff0c;收件人可以在自己方便时上网到自己使用的邮件服务器进行读取。 电子邮件最重要的两个草案标准&#xff1a;简单邮件传送协议SMTP和互联网文本报文格式。…

MATLAB命令之求矩阵长度

在写MATLAB代码时经常会用到求某个矩阵长度&#xff0c;我之前通常用size&#xff0c;后来发现别人喜欢用length。这篇文章旨在统计分析各大求长度指令的异同点。 size(a) -- 表矩阵每个维度的长度 a [1 2 3 4;5 6 7 8;9 10 11 12]&#xff1b; size(a)&#xff1b; 结果是[3&…

微型计算机可以处理的二进制长度,可以处理的二进制数据长度是多少位

微型计算机中CPU进行算术运算和逻辑运算时&#xff0c;可以处理的二进制数据长度是32位、64位。 CPU一次能并行处理的二进制位数为字长&#xff0c;字长总是8的整数倍&#xff0c;常用的字长为8位(早期)、16位(早期)、32位和64位。 本教程操作环境&#xff1a;windows7系统、De…